From 539dfd6a38e6dc371f582f58297b012c5953012a Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 7 Dec 2022 18:32:50 +0100 Subject: [PATCH] OPS Operation - Condition - Armygroup - Auftrag - OPS --- Moose Development/Moose/Core/Condition.lua | 145 ++++++++++--- Moose Development/Moose/Ops/ArmyGroup.lua | 40 +++- Moose Development/Moose/Ops/Auftrag.lua | 4 +- Moose Development/Moose/Ops/Operation.lua | 225 +++++++++++++++------ Moose Development/Moose/Ops/OpsGroup.lua | 41 +++- 5 files changed, 353 insertions(+), 102 deletions(-) diff --git a/Moose Development/Moose/Core/Condition.lua b/Moose Development/Moose/Core/Condition.lua index 80f80cf52..7e9842a7b 100644 --- a/Moose Development/Moose/Core/Condition.lua +++ b/Moose Development/Moose/Core/Condition.lua @@ -25,10 +25,13 @@ -- @field #string lid Class id string for output to DCS log file. -- @field #string name Name of the condition. -- @field #boolean isAny General functions are evaluated as any condition. --- @field #boolean negateResult Negeate result of evaluation. +-- @field #boolean negateResult Negate result of evaluation. +-- @field #boolean noneResult Boolean that is returned if no condition functions at all were specified. -- @field #table functionsGen General condition functions. -- @field #table functionsAny Any condition functions. -- @field #table functionsAll All condition functions. +-- @field #number functionCounter Running number to determine the unique ID of condition functions. +-- @field #boolean defaultPersist Default persistence of condition functions. -- -- @extends Core.Base#BASE @@ -42,27 +45,34 @@ -- -- @field #CONDITION CONDITION = { - ClassName = "CONDITION", - lid = nil, - functionsGen = {}, - functionsAny = {}, - functionsAll = {}, + ClassName = "CONDITION", + lid = nil, + functionsGen = {}, + functionsAny = {}, + functionsAll = {}, + functionCounter = 0, + defaultPersist = false, } --- Condition function. -- @type CONDITION.Function --- @field #function func Callback function to check for a condition. Should return a `#boolean`. +-- @field #number uid Unique ID of the condition function. +-- @field #string type Type of the condition function: "gen", "any", "all". +-- @field #boolean persistence If `true`, this is persistent. +-- @field #function func Callback function to check for a condition. Must return a `#boolean`. -- @field #table arg (Optional) Arguments passed to the condition callback function if any. --- CONDITION class version. -- @field #string version -CONDITION.version="0.2.0" +CONDITION.version="0.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Make FSM. +-- TODO: Make FSM. No sure if really necessary. +-- DONE: Option to remove condition functions. +-- DONE: Persistence option for condition functions. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -79,6 +89,8 @@ function CONDITION:New(Name) self.name=Name or "Condition X" + self:SetNoneResult(false) + self.lid=string.format("%s | ", self.name) return self @@ -102,6 +114,28 @@ function CONDITION:SetNegateResult(Negate) return self end +--- Set whether `true` or `false` is returned, if no conditions at all were specified. By default `false` is returned. +-- @param #CONDITION self +-- @param #boolean ReturnValue Returns this boolean. +-- @return #CONDITION self +function CONDITION:SetNoneResult(ReturnValue) + if not ReturnValue then + self.noneResult=false + else + self.noneResult=true + end + return self +end + +--- Set whether condition functions are persistent, *i.e.* are removed. +-- @param #CONDITION self +-- @param #boolean IsPersistent If `true`, condition functions are persistent. +-- @return #CONDITION self +function CONDITION:SetDefaultPersistence(IsPersistent) + self.defaultPersist=IsPersistent + return self +end + --- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`). -- @param #CONDITION self -- @param #function Function The function to call. @@ -114,47 +148,109 @@ end -- -- myCondition:AddFunction(isAequalB, a, b) -- --- @return #CONDITION self +-- @return #CONDITION.Function Condition function table. function CONDITION:AddFunction(Function, ...) -- Condition function. - local condition=self:_CreateCondition(Function, ...) + local condition=self:_CreateCondition(0, Function, ...) -- Add to table. table.insert(self.functionsGen, condition) - return self + return condition end --- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`). -- @param #CONDITION self -- @param #function Function The function to call. -- @param ... (Optional) Parameters passed to the function (if any). --- @return #CONDITION self +-- @return #CONDITION.Function Condition function table. function CONDITION:AddFunctionAny(Function, ...) -- Condition function. - local condition=self:_CreateCondition(Function, ...) + local condition=self:_CreateCondition(1, Function, ...) -- Add to table. table.insert(self.functionsAny, condition) - return self + return condition end --- Add a function that is evaluated. It must return a `#boolean` value, *i.e.* either `true` or `false` (or `nil`). -- @param #CONDITION self -- @param #function Function The function to call. -- @param ... (Optional) Parameters passed to the function (if any). --- @return #CONDITION self +-- @return #CONDITION.Function Condition function table. function CONDITION:AddFunctionAll(Function, ...) -- Condition function. - local condition=self:_CreateCondition(Function, ...) + local condition=self:_CreateCondition(2, Function, ...) -- Add to table. table.insert(self.functionsAll, condition) + return condition +end + +--- Remove a condition function. +-- @param #CONDITION self +-- @param #CONDITION.Function ConditionFunction The condition function to be removed. +-- @return #CONDITION self +function CONDITION:RemoveFunction(ConditionFunction) + + if ConditionFunction then + + local data=nil + if ConditionFunction.type==0 then + data=self.functionsGen + elseif ConditionFunction.type==1 then + data=self.functionsAny + elseif ConditionFunction.type==2 then + data=self.functionsAll + end + + if data then + for i=#data,1,-1 do + local cf=data[i] --#CONDITION.Function + if cf.uid==ConditionFunction.uid then + self:T(self.lid..string.format("Removed ConditionFunction UID=%d", cf.uid)) + table.remove(data, i) + return self + end + end + end + + end + + return self +end + +--- Remove all non-persistant condition functions. +-- @param #CONDITION self +-- @return #CONDITION self +function CONDITION:RemoveNonPersistant() + + for i=#self.functionsGen,1,-1 do + local cf=self.functionsGen[i] --#CONDITION.Function + if not cf.persistence then + table.remove(self.functionsGen, i) + end + end + + for i=#self.functionsAll,1,-1 do + local cf=self.functionsAll[i] --#CONDITION.Function + if not cf.persistence then + table.remove(self.functionsAll, i) + end + end + + for i=#self.functionsAny,1,-1 do + local cf=self.functionsAny[i] --#CONDITION.Function + if not cf.persistence then + table.remove(self.functionsAny, i) + end + end + return self end @@ -167,11 +263,7 @@ function CONDITION:Evaluate(AnyTrue) -- Check if at least one function was given. if #self.functionsAll + #self.functionsAny + #self.functionsAll == 0 then - if self.negateResult then - return true - else - return false - end + return self.noneResult end -- Any condition for gen. @@ -280,13 +372,20 @@ end --- Create conditon function object. -- @param #CONDITION self +-- @param #number Ftype Function type: 0=Gen, 1=All, 2=Any. -- @param #function Function The function to call. -- @param ... (Optional) Parameters passed to the function (if any). -- @return #CONDITION.Function Condition function. -function CONDITION:_CreateCondition(Function, ...) +function CONDITION:_CreateCondition(Ftype, Function, ...) + + -- Increase counter. + self.functionCounter=self.functionCounter+1 local condition={} --#CONDITION.Function + condition.uid=self.functionCounter + condition.type=Ftype or 0 + condition.persistence=self.defaultPersist condition.func=Function condition.arg={} if arg then diff --git a/Moose Development/Moose/Ops/ArmyGroup.lua b/Moose Development/Moose/Ops/ArmyGroup.lua index d6acf2797..14c701d61 100644 --- a/Moose Development/Moose/Ops/ArmyGroup.lua +++ b/Moose Development/Moose/Ops/ArmyGroup.lua @@ -142,6 +142,7 @@ function ARMYGROUP:New(group) ------------------------ --- Pseudo Functions --- ------------------------ + --- Triggers the FSM event "Cruise". -- @function [parent=#ARMYGROUP] Cruise -- @param #ARMYGROUP self @@ -253,10 +254,14 @@ function ARMYGROUP:New(group) --- Triggers the FSM event "Retreat". -- @function [parent=#ARMYGROUP] Retreat -- @param #ARMYGROUP self + -- @param Core.Zone#ZONE_BASE Zone (Optional) Zone where to retreat. Default is the closest retreat zone. + -- @param #number Formation (Optional) Formation of the group. --- Triggers the FSM event "Retreat" after a delay. -- @function [parent=#ARMYGROUP] __Retreat -- @param #ARMYGROUP self + -- @param Core.Zone#ZONE_BASE Zone (Optional) Zone where to retreat. Default is the closest retreat zone. + -- @param #number Formation (Optional) Formation of the group. -- @param #number delay Delay in seconds. --- On after "Retreat" event. @@ -265,7 +270,8 @@ function ARMYGROUP:New(group) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - + -- @param Core.Zone#ZONE_BASE Zone Zone where to retreat. + -- @param #number Formation Formation of the group. Can be #nil. --- Triggers the FSM event "Retreated". -- @function [parent=#ARMYGROUP] Retreated @@ -1010,6 +1016,9 @@ function ARMYGROUP:onbeforeUpdateRoute(From, Event, To, n, N, Speed, Formation) elseif task.dcstask.id==AUFTRAG.SpecialTask.RELOCATECOHORT then -- For relocate self:T2(self.lid.."Allowing update route for Task: Relocate Cohort") + elseif task.dcstask.id==AUFTRAG.SpecialTask.REARMING then + -- For relocate + self:T2(self.lid.."Allowing update route for Task: Rearming") else local taskname=task and task.description or "No description" self:T(self.lid..string.format("WARNING: Update route denied because taskcurrent=%d>0! Task description = %s", self.taskcurrent, tostring(taskname))) @@ -1411,10 +1420,15 @@ function ARMYGROUP:onbeforeRearm(From, Event, To, Coordinate, Formation) -- Pause current mission. if self:IsOnMission() then - self:T(self.lid.."Rearm command but have current mission ==> Pausing mission!") - self:PauseMission() - dt=-0.1 - allowed=false + local mission=self:GetMissionCurrent() + if mission and mission.type~=AUFTRAG.Type.REARMING then + self:T(self.lid.."Rearm command but have current mission ==> Pausing mission!") + self:PauseMission() + dt=-0.1 + allowed=false + else + self:T(self.lid.."Rearm command and current mission is REARMING ==> Transition ALLOWED!") + end end -- Disengage. @@ -1480,6 +1494,7 @@ function ARMYGROUP:onafterRearmed(From, Event, To) -- Check if this is a rearming mission. if mission and mission.type==AUFTRAG.Type.REARMING then + -- Rearmed ==> Mission Done! This also checks if the group is done. self:MissionDone(mission) @@ -1651,8 +1666,12 @@ function ARMYGROUP:onafterRetreat(From, Event, To, Zone, Formation) -- ID of current waypoint. local uid=self:GetWaypointCurrent().uid + -- Get random coordinate of the zone. local Coordinate=Zone:GetRandomCoordinate() + -- Debug info. + self:T(self.lid..string.format("Retreating to zone %s", Zone:GetName())) + -- Add waypoint after current. local wp=self:AddWaypoint(Coordinate, nil, uid, Formation, true) @@ -1952,8 +1971,10 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation if not Formation then if self.formationPerma then Formation = self.formationPerma + elseif self.optionDefault.Formation then + Formation = self.optionDefault.Formation elseif self.option.Formation then - Formation = self.option.Formation + Formation = self.option.Formation else -- Default formation is on road. Formation = ENUMS.Formation.Vehicle.OnRoad @@ -2037,8 +2058,11 @@ function ARMYGROUP:_InitGroup(Template) -- Set default radio. self:SetDefaultRadio(self.radio.Freq, self.radio.Modu, self.radio.On) - -- Set default formation from first waypoint. - self.optionDefault.Formation=template.route.points[1].action --self:GetWaypoint(1).action + -- Get current formation from first waypoint. + self.option.Formation=template.route.points[1].action + + -- Set default formation to "on road". + self.optionDefault.Formation=ENUMS.Formation.Vehicle.OnRoad -- Default TACAN off. self:SetDefaultTACAN(nil, nil, nil, nil, true) diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index d5e62aab1..7458a5bfd 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -628,7 +628,7 @@ AUFTRAG.Category={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.9.7" +AUFTRAG.version="0.9.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -5810,7 +5810,7 @@ function AUFTRAG:GetDCSMissionTask() table.insert(DCStasks, DCStask) - elseif self.type==AUFTRAG.Type.AMMOSUPPLY then + elseif self.type==AUFTRAG.Type.REARMING then ---------------------- -- REARMING Mission -- diff --git a/Moose Development/Moose/Ops/Operation.lua b/Moose Development/Moose/Ops/Operation.lua index 5ff31e850..c6598acc2 100644 --- a/Moose Development/Moose/Ops/Operation.lua +++ b/Moose Development/Moose/Ops/Operation.lua @@ -33,7 +33,7 @@ -- @field #number Tstop Stop time in seconds of abs mission time. -- @field #number duration Duration of the operation in seconds. -- @field Core.Condition#CONDITION conditionStart Start condition. --- @field Core.Condition#CONDITION conditionStop Stop condition. +-- @field Core.Condition#CONDITION conditionOver Over condition. -- @field #table branches Branches. -- @field #OPERATION.Branch branchMaster Master branch. -- @field #OPERATION.Branch branchActive Active branch. @@ -47,7 +47,7 @@ -- @field #table missions Missions. -- @extends Core.Fsm#FSM ---- *Before this time tomorrow I shall have gained a peerage, or Westminster Abbey* -- Horatio Nelson +--- *Before this time tomorrow I shall have gained a peerage, or Westminster Abbey.* -- Horatio Nelson -- -- === -- @@ -95,6 +95,7 @@ _OPERATIONID=0 -- @field Core.Condition#CONDITION conditionOver Conditions when the phase is over. -- @field #string status Phase status. -- @field #number Tstart Abs. mission time when the phase was started. +-- @field #number nActive Number of times the phase was active. -- @field #number duration Duration in seconds how long the phase should be active after it started. -- @field #OPERATION.Branch branch The branch this phase belongs to. @@ -134,6 +135,7 @@ OPERATION.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: "Over" conditions. +-- TODO: Repeat phases: after over ==> planned (not over) -- DONE: Branches. -- DONE: Phases. @@ -168,6 +170,15 @@ function OPERATION:New(Name) -- Master branch. self.branchMaster=self:AddBranch("Master") + self.conditionStart=CONDITION:New("Operation %s start", self.name) + self.conditionStart:SetNoneResult(false) --If no condition function is specified, the ops will NOT be over. + self.conditionStart:SetDefaultPersistence(false) + + self.conditionOver=CONDITION:New("Operation %s over", self.name) + self.conditionOver:SetNoneResult(false) + self.conditionOver:SetDefaultPersistence(false) + + -- Set master as active branch. self.branchActive=self.branchMaster @@ -204,6 +215,12 @@ function OPERATION:New(Name) -- @param #OPERATION self -- @param #number delay Delay in seconds. + --- On after "Start" event. + -- @function [parent=#OPERATION] OnAfterStart + -- @param #OPERATION self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. --- Triggers the FSM event "Stop". -- @function [parent=#OPERATION] Stop @@ -375,6 +392,27 @@ function OPERATION:SetTime(ClockStart, ClockStop) return self end +--- Add (all) condition function when the whole operation is over. Must return a `#boolean`. +-- @param #OPERATION self +-- @param #function Function Function that needs to be `true` before the operation is over. +-- @param ... Condition function arguments if any. +-- @return Core.Condition#CONDITION.Function Condition function table. +function OPERATION:AddConditonOverAll(Function, ...) + local cf=self.conditionOver:AddFunctionAll(Function, ...) + return cf +end + +--- Add (any) condition function when the whole operation is over. Must return a `#boolean`. +-- @param #OPERATION self +-- @param #function Function Function that needs to be `true` before the operation is over. +-- @param ... Condition function arguments if any. +-- @return Core.Condition#CONDITION.Function Condition function table. +function OPERATION:AddConditonOverAny(Phase, Function, ...) + local cf=self.conditionOver:AddFunctionAny(Function, ...) + return cf +end + + --- Add a new phase to the operation. This is added add the end of all previously added phases (if any). -- @param #OPERATION self -- @param #string Name Name of the phase. Default "Phase-01" where the last number is a running number. @@ -392,9 +430,9 @@ function OPERATION:AddPhase(Name, Branch, Duration) -- Branch of phase phase.branch=Branch + -- Set duraction of pahse (if any). phase.duration=Duration - -- Debug output. self:T(self.lid..string.format("Adding phase %s to branch %s", phase.name, Branch.name)) @@ -469,7 +507,7 @@ function OPERATION:SetPhaseStatus(Phase, Status) -- Set time stamp when phase becase active. if Phase.status==OPERATION.PhaseStatus.ACTIVE then Phase.Tstart=timer.getAbsTime() - env.info("FF Setting phase start time stamp") + Phase.nActive=Phase.nActive+1 elseif Phase.status==OPERATION.PhaseStatus.OVER then -- Trigger PhaseOver event. self:PhaseOver(Phase) @@ -504,14 +542,15 @@ end --- Add condition function when the given phase is over. Must return a `#boolean`. -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase. --- @param #function Function Function that needs to be `true`before the phase is over. +-- @param #function Function Function that needs to be `true` before the phase is over. -- @param ... Condition function arguments if any. --- @return #OPERATION self +-- @return Core.Condition#CONDITION.Function Condition function table. function OPERATION:AddPhaseConditonOverAll(Phase, Function, ...) if Phase then - Phase.conditionOver:AddFunctionAll(Function, ...) + local cf=Phase.conditionOver:AddFunctionAll(Function, ...) + return cf end - return self + return nil end --- Add condition function when the given phase is over. Must return a `#boolean`. @@ -519,16 +558,41 @@ end -- @param #OPERATION.Phase Phase The phase. -- @param #function Function Function that needs to be `true` before the phase is over. -- @param ... Condition function arguments if any. --- @return #OPERATION self +-- @return Core.Condition#CONDITION.Function Condition function table. function OPERATION:AddPhaseConditonOverAny(Phase, Function, ...) if Phase then - Phase.conditionOver:AddFunctionAny(Function, ...) + local cf=Phase.conditionOver:AddFunctionAny(Function, ...) + return cf + end + return nil +end + +--- Set persistence of condition function. By default, condition functions are removed after a phase is over. +-- @param #OPERATION self +-- @param Core.Condition#CONDITION.Function ConditionFunction Condition function table. +-- @param #boolean IsPersistent If `true` or `nil`, condition function is persistent. +-- @return #OPERATION self +function OPERATION:SetConditionFunctionPersistence(ConditionFunction, IsPersistent) + ConditionFunction.persistence=IsPersistent + return self +end + +--- Add condition function when the given phase is to be repeated. The provided function must return a `#boolean`. +-- If the condition evaluation returns `true`, the phase is set to state `Planned` instead of `Over` and can be repeated. +-- @param #OPERATION self +-- @param #OPERATION.Phase Phase The phase. +-- @param #function Function Function that needs to be `true` before the phase is over. +-- @param ... Condition function arguments if any. +-- @return #OPERATION self +function OPERATION:AddPhaseConditonRepeatAll(Phase, Function, ...) + if Phase then + Phase.conditionRepeat:AddFunctionAll(Function, ...) end return self end ---- Get codition when the given phase is over. +--- Get condition when the given phase is over. -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase. -- @return Core.Condition#CONDITION Condition when the phase is over (if any). @@ -536,11 +600,12 @@ function OPERATION:GetPhaseConditonOver(Phase, Condition) return Phase.conditionOver end ---- Get currrently active phase. +--- Get how many times a phase has been active. -- @param #OPERATION self --- @return #OPERATION.Phase Current phase or `nil` if no current phase is active. -function OPERATION:GetPhaseActive() - return self.phase +-- @param #OPERATION.Phase Phase The phase. +-- @return #number Number of times the phase has been active. +function OPERATION:GetPhaseNactive(Phase) + return Phase.nActive end --- Get name of a phase. @@ -558,18 +623,11 @@ function OPERATION:GetPhaseName(Phase) return "None" end ---- Check if a phase is the currently active one. +--- Get currrently active phase. -- @param #OPERATION self --- @param #OPERATION.Phase Phase The phase to check. --- @return #boolean If `true`, this phase is currently active. -function OPERATION:IsPhaseActive(Phase) - local phase=self:GetPhaseActive() - if phase and phase.uid==Phase.uid then - return true - else - return false - end - return nil +-- @return #OPERATION.Phase Current phase or `nil` if no current phase is active. +function OPERATION:GetPhaseActive() + return self.phase end --- Get index of phase. @@ -709,19 +767,23 @@ end -- @param #OPERATION.Phase PhaseFrom The phase of the *from* branch *after* which to switch. -- @param #OPERATION.Phase PhaseTo The phase of the *to* branch *to* which to switch. -- @param Core.Condition#CONDITION ConditionSwitch (Optional) Condition(s) when to switch the branches. --- @return #OPERATION.Branch Branch table object. +-- @return #OPERATION.Edge Edge table object. function OPERATION:AddEdge(PhaseFrom, PhaseTo, ConditionSwitch) local edge={} --#OPERATION.Edge - edge.phaseFrom=PhaseFrom edge.phaseTo=PhaseTo edge.branchFrom=PhaseFrom.branch edge.branchTo=PhaseTo.branch - edge.conditionSwitch=ConditionSwitch or CONDITION:New("Edge") + if ConditionSwitch then + edge.conditionSwitch=ConditionSwitch + else + edge.conditionSwitch=CONDITION:New("Edge") + edge.conditionSwitch:SetNoneResult(true) + end table.insert(edge.branchFrom.edges, edge) @@ -729,16 +791,18 @@ function OPERATION:AddEdge(PhaseFrom, PhaseTo, ConditionSwitch) end --- Add condition function to an edge when branches are switched. The function must return a `#boolean`. +-- If multiple condition functions are added, all of these must return true for the branch switch to occur. -- @param #OPERATION self -- @param #OPERATION.Edge Edge The edge connecting the two branches. -- @param #function Function Function that needs to be `true` for switching between the branches. -- @param ... Condition function arguments if any. --- @return #OPERATION self +-- @return Core.Condition#CONDITION.Function Condition function table. function OPERATION:AddEdgeConditonSwitchAll(Edge, Function, ...) if Edge then - Edge.conditionSwitch:AddFunctionAll(Function, ...) + local cf=Edge.conditionSwitch:AddFunctionAll(Function, ...) + return cf end - return self + return nil end --- Add mission to operation. @@ -769,9 +833,9 @@ function OPERATION:AddTarget(Target, Phase) return self end ---- Add Targets from operation. +--- Get targets of operation. -- @param #OPERATION self --- @param #OPERATION.Phase Phase +-- @param #OPERATION.Phase Phase (Optional) Only return targets set for this phase. Default is targets of all phases. -- @return #table Targets Table of #TARGET objects function OPERATION:GetTargets(Phase) local N = {} @@ -926,6 +990,14 @@ function OPERATION:IsStopped() return is end +--- Check if operation is **not** "Over" or "Stopped". +-- @param #OPERATION self +-- @return #boolean If `true`, operation is not "Over" or "Stopped". +function OPERATION:IsNotOver() + local is=not (self:IsOver() or self:IsStopped()) + return is +end + --- Check if phase is in status "Active". -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase. @@ -937,6 +1009,20 @@ function OPERATION:IsPhaseActive(Phase) return false end +--- Check if a phase is the currently active one. +-- @param #OPERATION self +-- @param #OPERATION.Phase Phase The phase to check. +-- @return #boolean If `true`, this phase is currently active. +function OPERATION:IsPhaseActive(Phase) + local phase=self:GetPhaseActive() + if phase and phase.uid==Phase.uid then + return true + else + return false + end + return nil +end + --- Check if phase is in status "Planned". -- @param #OPERATION self -- @param #OPERATION.Phase Phase The phase. @@ -990,27 +1076,30 @@ function OPERATION:onafterStatusUpdate(From, Event, To) -- Current FSM state. local fsmstate=self:GetState() + -- Start operation. if self:IsPlanned() then - if self.Tstart and Tnow>self.Tstart then + + -- Start operation if start time has passed (if any) and start condition(s) are met (if any). + if (self.Tstart and Tnow>self.Tstart or self.Tstart==nil) and (self.conditionStart==nil or self.conditionStart:Evaluate()) then self:Start() end - end - if (self.Tstop and Tnow>self.Tstop) and not (self:IsOver() or self:IsStopped()) then - self:Over() + + elseif self:IsNotOver() then + + -- Operation is over if stop time has passed (if any) and over condition(s) are met (if any). + if (self.Tstop and Tnow>self.Tstop or self.Tstop==nil) and (self.conditionOver==nil or self.conditionOver:Evaluate()) then + self:Over() + end + end - if (not self:IsRunning()) and (self.conditionStart and self.conditionStart:Evaluate()) then - self:Start() - end - if self:IsRunning() and (self.conditionStop and self.conditionStop:Evaluate()) then - self:Over() - end -- Check phases. if self:IsRunning() then self:_CheckPhases() end + -- Debug output. if self.verbose>=1 then @@ -1035,7 +1124,7 @@ function OPERATION:onafterStatusUpdate(From, Event, To) local text="Phases:" for i,_phase in pairs(self.branchActive.phases) do local phase=_phase --#OPERATION.Phase - text=text..string.format("\n[%d] %s: status=%s", i, phase.name, tostring(phase.status)) + text=text..string.format("\n[%d] %s [uid=%d]: status=%s Nact=%d", i, phase.name, phase.uid, tostring(phase.status), phase.nActive) end if text=="Phases:" then text=text.." None" end self:I(self.lid..text) @@ -1107,6 +1196,19 @@ function OPERATION:onafterPhaseChange(From, Event, To, Phase) return self end +--- On after "PhaseOver" event. +-- @param #OPERATION self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #OPERATION.Phase Phase The phase that is over. +function OPERATION:onafterPhaseOver(From, Event, To, Phase) + + -- Remove all non-persistant condition functions. + Phase.conditionOver:RemoveNonPersistant() + +end + --- On after "BranchSwitch" event. -- @param #OPERATION self -- @param #string From From state. @@ -1168,14 +1270,18 @@ function OPERATION:_CheckPhases() -- Check if active phase is over if conditon over is defined. if phase and phase.conditionOver then + + -- Evaluate if phase is over. local isOver=phase.conditionOver:Evaluate() local Tnow=timer.getAbsTime() + -- Check if duration of phase if over. if phase.duration and phase.Tstart and Tnow-phase.Tstart>phase.duration then isOver=true end + -- Set phase status to over. This also triggers the PhaseOver() event. if isOver then self:SetPhaseStatus(phase, OPERATION.PhaseStatus.OVER) end @@ -1187,6 +1293,11 @@ function OPERATION:_CheckPhases() for _,_edge in pairs(self.branchActive.edges) do local edge=_edge --#OPERATION.Edge + if phase then + --env.info(string.format("phase active uid=%d", phase.uid)) + --env.info(string.format("Phase from uid=%d", edge.phaseFrom.uid)) + end + if (edge.phaseFrom==nil) or (phase and edge.phaseFrom.uid==phase.uid) then -- Evaluate switch condition. @@ -1197,37 +1308,25 @@ function OPERATION:_CheckPhases() -- Get next phase of the branch local phaseTo=edge.phaseTo or self:GetPhaseNext(edge.branchTo, nil) - if phaseTo then - + if phaseTo then + -- Switch to new branch. self:BranchSwitch(edge.branchTo, phaseTo) else + -- No next phase ==> Ops is over! self:Over() end -- Done here! - return - --- -- If we want to switch to a specific phase of the branch. --- if edge.phaseTo then --- --- -- Change phase. --- self:PhaseChange(edge.phaseTo) --- --- -- Done here! --- return --- end --- --- -- Break the loop. --- break + return end end end - + -- Next phase. self:PhaseNext() @@ -1248,7 +1347,9 @@ function OPERATION:_CreatePhase(Name) phase.uid=self.counterPhase phase.name=Name or string.format("Phase-%02d", self.counterPhase) phase.conditionOver=CONDITION:New(Name.." Over") + phase.conditionOver:SetDefaultPersistence(false) phase.status=OPERATION.PhaseStatus.PLANNED + phase.nActive=0 return phase end diff --git a/Moose Development/Moose/Ops/OpsGroup.lua b/Moose Development/Moose/Ops/OpsGroup.lua index 7656d769b..3dd305a27 100644 --- a/Moose Development/Moose/Ops/OpsGroup.lua +++ b/Moose Development/Moose/Ops/OpsGroup.lua @@ -4043,11 +4043,16 @@ end -- @param #string To To state. -- @param Ops.OpsGroup#OPSGROUP.Task Task The task. function OPSGROUP:onafterTaskExecute(From, Event, To, Task) - self:T({Task}) + -- Debug message. local text=string.format("Task %s ID=%d execute", tostring(Task.description), Task.id) + + -- Debug info. self:T(self.lid..text) - self:T({Task}) + + -- Debug info. + self:T2({Task}) + -- Cancel current task if there is any. if self.taskcurrent>0 then self:TaskCancel() @@ -4070,7 +4075,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) -- Get mission of this task (if any). local Mission=self:GetMissionByTaskID(self.taskcurrent) - + -- Update push DCS task. self:_UpdateTask(Task, Mission) -- Set AUFTRAG status. @@ -4080,7 +4085,7 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task) end ---- Push task +--- Push task. -- @param #OPSGROUP self -- @param Ops.OpsGroup#OPSGROUP.Task Task The task. function OPSGROUP:_UpdateTask(Task, Mission) @@ -4218,6 +4223,14 @@ function OPSGROUP:_UpdateTask(Task, Mission) -- Check if ammo is full. local rearmed=self:_CheckAmmoFull() + + if rearmed then + self:T2(self.lid.."Ammo already full ==> reaming task done!") + self:TaskDone(Task) + else + self:T2(self.lid.."Ammo not full ==> Rearm()") + self:Rearm() + end elseif Task.dcstask.id==AUFTRAG.SpecialTask.ALERT5 then @@ -6070,6 +6083,18 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint) -- Final zone reached ==> task done. self:TaskDone(task) + elseif task and task.dcstask.id==AUFTRAG.SpecialTask.REARMING then + + --- + -- SPECIAL TASK: Rearming Mission + --- + + -- Debug info. + self:T(self.lid..string.format("FF Rearming Mission ==> Rearm()")) + + -- Call rearm event. + self:Rearm() + else --- @@ -7361,8 +7386,10 @@ function OPSGROUP:CancelAllMissions() -- Cancel all missions. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG - self:T(self.lid.."Cancelling mission "..tostring(mission:GetName())) - self:MissionCancel(mission) + if mission:IsNotOver() then + self:T(self.lid.."Cancelling mission "..tostring(mission:GetName())) + self:MissionCancel(mission) + end end end @@ -10179,7 +10206,7 @@ function OPSGROUP:_CheckAmmoStatus() -- Check if rearming is completed. if self:IsRearming() then - if ammo.Total==self.ammo.Total then + if ammo.Total>=self.ammo.Total then self:Rearmed() end end