diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index b8554ef96..5fb896a71 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -8087,6 +8087,23 @@ function WAREHOUSE:_DeleteQueueItem(qitem, queue) end end +--- Delete item from queue. +-- @param #WAREHOUSE self +-- @param #number qitemID ID of queue item to be removed. +-- @param #table queue The queue from which the item should be deleted. +function WAREHOUSE:_DeleteQueueItemByID(qitemID, queue) + self:F({qitem=qitem, queue=queue}) + + for i=1,#queue do + local _item=queue[i] --#WAREHOUSE.Queueitem + if _item.uid==qitemID then + self:T(self.lid..string.format("Deleting queue item id=%d.", qitemID)) + table.remove(queue,i) + break + end + end +end + --- Sort requests queue wrt prio and request uid. -- @param #WAREHOUSE self function WAREHOUSE:_SortQueue() diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 55f8cf555..74f2735cc 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -177,6 +177,7 @@ end -- @return #AIRWING.Squadron The squadron object. function AIRWING:AddSquadron(SquadronName, MissionTypes, Livery, Skill) + -- Ensure Missiontypes is a table. if MissionTypes and type(MissionTypes)~="table" then MissionTypes={MissionTypes} end @@ -186,8 +187,8 @@ function AIRWING:AddSquadron(SquadronName, MissionTypes, Livery, Skill) table.insert(MissionTypes, AUFTRAG.Type.ORBIT) end + -- Set up new squadron data. local squadron={} --#AIRWING.Squadron - squadron.name=SquadronName squadron.assets={} squadron.missiontypes=MissionTypes @@ -210,6 +211,14 @@ function AIRWING:NewPayload(Unit, MissionTypes, Npayloads, Unlimited) if type(Unit)=="string" then Unit=UNIT:FindByName(Unit) + if not Unit then + Unit=GROUP:FindByName(Unit) + end + end + + -- If a GROUP object was given, get the first unit. + if Unit:IsInstanceOf("GROUP") then + Unit=Unit:GetUnit(1) end if Unit then @@ -351,7 +360,7 @@ function AIRWING:AddMission(Mission) -- Info text. local text=string.format("Added %s mission %s. Starting at %s. Stopping at %s", - tostring(Mission.type), tostring(Mission.name), UTILS.SecondsToClock(Mission.Tstart, true), Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop, true) or "never") + tostring(Mission.type), tostring(Mission.name), UTILS.SecondsToClock(Mission.Tstart, true), Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop, true) or "INF") self:I(self.lid..text) return self @@ -647,6 +656,11 @@ function AIRWING:onafterMissionCancel(From, Event, To, Mission) end end + -- Remove queued request (if any). + if Mission.requestID then + self:_DeleteQueueItemByID(Mission.requestID, self.queue) + end + end --- On after "MissionRequest" event. Performs a self request to the warehouse for the mission assets. Sets mission status to REQUESTED. @@ -930,8 +944,10 @@ function AIRWING:IsAssetOnMission(asset, MissionTypes) for _,_mission in pairs(asset.flightgroup.missionqueue or {}) do local mission=_mission --Ops.Auftrag#AUFTRAG + local status=mission:GetFlightStatus(asset.flightgroup) + -- Only if mission is not already over. - if mission.status~=AUFTRAG.Status.DONE and self:CheckMissionType(mission.type, MissionTypes) then + if status~=AUFTRAG.FlightStatus.DONE and status~=AUFTRAG.FlightStatus.CANCELLED and self:CheckMissionType(mission.type, MissionTypes) then return true end diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index 098f6a7ea..bc3b09a98 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -29,6 +29,7 @@ -- @field #number duration Mission duration in seconds. -- @field #number marker F10 map marker ID. -- @field #table DCStask DCS task structure. +-- @field #number Ntargets Number of mission targets. -- -- @field Core.Point#COORDINATE waypointcoord Coordinate of the waypoint task. -- @@ -192,7 +193,7 @@ AUFTRAG.FlightStatus={ --- AUFTRAG class version. -- @field #string version -AUFTRAG.version="0.0.4" +AUFTRAG.version="0.0.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -258,6 +259,7 @@ function AUFTRAG:New(Type) self:AddTransition("*", "Failed", AUFTRAG.Status.FAILED) self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Stop", "Stopped") self:__Status(-1) @@ -572,6 +574,13 @@ function AUFTRAG:IsOver() return over end +--- Check if mission is NOT over. +-- @param #AUFTRAG self +-- @return #boolean If true, mission is NOT over yet. +function AUFTRAG:IsNotOver() + return not self:IsOver() +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Mission Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -584,17 +593,51 @@ end -- @param Ops.AirWing#AIRWING Airwing The airwing. function AUFTRAG:onafterStatus(From, Event, To) - local fsmstate=self:GetState() - + -- Number of alive mission targets. local Ntargets=self:CountMissionTargets() + -- Number of alive flights attached to this mission. + local Nflights=self:CountFlightGroups() + + -- Cancel mission if stop time passed. + if self.Tstop and timer.getAbsTime()>self.Tstop then + self:Cancel() + end + + -- Cancel mission if number of targest is 0 (and was >0 before as some missions, e.g. orbit, don't have any target). + if self:IsNotOver() and self.Ntargets>0 and Ntargets==0 then + self:Cancel() + end + + -- Check if ALL flights are done with their mission. + if self:IsNotOver() and self:CheckFlightsDone() then + self:Done() + end + + -- Current FSM state. + local fsmstate=self:GetState() + + -- Mission start stop time. local Cstart=UTILS.SecondsToClock(self.Tstart, true) local Cstop=self.Tstop and UTILS.SecondsToClock(self.Tstop, true) or "INF" -- Info message. - self:I(self.lid..string.format("FSM state %s (Mstatus=%s): T=%s-%s flights=%d, targets=%d", fsmstate, self.status, Cstart, Cstop, #self.flightdata, Ntargets)) + self:I(self.lid..string.format("Status \"%s\": T=%s-%s flights=%d, targets=%d", self.status, Cstart, Cstop, Nflights, Ntargets)) - self:__Status(-30) + -- Check for error. + if fsmstate~=self.status then + self:E(self.lid..string.format("ERROR: FSM state %s != %s mission status!", fsmstate, self.status)) + end + + + -- Check if mission is OVER. + if self:IsOver() then + -- TODO: evaluate mission result. self.Ntargets>0 and Ntargets=? + -- TODO: if failed, repeat mission, i.e. set status to PLANNED? if success, stop and remove from ALL queues. + self:Stop() + else + self:__Status(-30) + end end --- Set flightgroup mission status. @@ -657,11 +700,13 @@ function AUFTRAG:CheckFlightsDone() return false end + -- Assume we are done. local done=true for groupname,data in pairs(self.flightdata) do local flightdata=data --#AUFTRAG.FlightData if flightdata.status~=AUFTRAG.FlightStatus.DONE and flightdata.status~=AUFTRAG.FlightStatus.CANCELLED then + -- At least one flight group is not DONE or CANCELLED yet! done=false end end @@ -770,6 +815,18 @@ function AUFTRAG:onafterCancel(From, Event, To) end +--- On after "Stop" event. +-- @param #AUFTRAG self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AUFTRAG:onafterStop(From, Event, To) + + -- TODO: remove missions from queues in WINGCOMMANDER, AIRWING and FLIGHGROUPS! + + self.CallScheduler:Clear() +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -793,6 +850,20 @@ function AUFTRAG:CountMissionTargets() return N end +--- Count alive flight groups assigned for this mission. +-- @param #AUFTRAG self +-- @return #number Number of alive flight groups. +function AUFTRAG:CountFlightGroups() + local N=0 + for _,_flightdata in pairs(self.flightdata) do + local flightdata=_flightdata --#AUFTRAG.FlightData + if flightdata and flightdata.flightgroup and flightdata.flightgroup:IsAlive() then + N=N+1 + end + end + return N +end + --- Get coordinate of target. First unit/group of the set is used. -- @param #AUFTRAG self -- @return Core.Point#COORDINATE The target coordinate or nil. @@ -800,10 +871,14 @@ function AUFTRAG:GetTargetCoordinate() if self.engageTargetGroupset then local group=self.engageTargetGroupset:GetFirst() --Wrapper.Group#GROUP - return group:GetCoordinate() + if group and group:IsAlive() then + return group:GetCoordinate() + end elseif self.engageTargetUnitset then local unit=self.engageTargetUnitset:GetFirst() --Wrapper.Unit#UNIT - return unit:GetCoordinate() + if unit and unit:IsAlive() then + return unit:GetCoordinate() + end elseif self.engageCoord then return self.engageCoord elseif self.orbitCoord then @@ -993,6 +1068,8 @@ function AUFTRAG:GetDCSMissionTask() end + -- Count mission targets. + self.Ntargets=self:CountMissionTargets() -- Return the task. if #DCStasks==1 then diff --git a/Moose Development/Moose/Ops/WingCommander.lua b/Moose Development/Moose/Ops/WingCommander.lua index e062c9139..67280c111 100644 --- a/Moose Development/Moose/Ops/WingCommander.lua +++ b/Moose Development/Moose/Ops/WingCommander.lua @@ -79,6 +79,9 @@ function WINGCOMMANDER:New(AgentSet) -- Set some string id for output to DCS.log file. self.lid=string.format("WINGCOMMANDER | ") + -- Add FSM transitions. + -- From State --> Event --> To State + self:AddTransition("*", "MissionAssign", "*") -- Mission was assigned to an AIRWING. ------------------------ --- Pseudo Functions --- @@ -160,6 +163,14 @@ function WINGCOMMANDER:onafterStart(From, Event, To) -- Start parent INTEL. self:GetParent(self).onafterStart(self, From, Event, To) + + -- Start attached airwings. + for _,_airwing in pairs(self.airwings) do + local airwing=_airwing --Ops.AirWing#AIRWING + if airwing:GetState()=="NotReadyYet" then + airwing:Start() + end + end end @@ -250,6 +261,23 @@ function WINGCOMMANDER:onafterStatus(From, Event, To) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM Events +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- On after "MissionAssign" event. Mission is added to the AIRWING mission queue. +-- @param #WINGCOMMANDER self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Ops.AirWing#AIRWING Airwing The AIRWING. +-- @param Ops.Auftrag#AUFTRAG Mission The mission. +function WINGCOMMANDER:onafterMissionAssign(From, Event, To, Airwing, Mission) + + Airwing:AddMission(Mission) + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Resources ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -311,7 +339,7 @@ function WINGCOMMANDER:CheckMissionQueue() local airwing=airwings[1].airwing --Ops.AirWing#AIRWING -- Add mission to airwing. - airwing:AddMission(mission) + self:MissionAssign(airwing, mission) return end