From ae3d1f6b2ae6c2e09868d09666dd73d4557105d0 Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 3 Mar 2020 16:48:05 +0100 Subject: [PATCH] Ops --- Moose Development/Moose/Ops/AirWing.lua | 2 + Moose Development/Moose/Ops/Auftrag.lua | 10 +- Moose Development/Moose/Ops/FlightGroup.lua | 10 +- Moose Development/Moose/Ops/Intelligence.lua | 131 +++++++++++--- Moose Development/Moose/Ops/WingCommander.lua | 171 ++++++++++++++---- 5 files changed, 252 insertions(+), 72 deletions(-) diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 6d29cb004..e6383db58 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -85,6 +85,8 @@ AIRWING.version="0.1.3" -- TODO: Check if missions are accomplished. -- DONE: Payloads as resources. -- TODO: Spawn in air or hot. +-- TODO: Define CAP zones. +-- TODO: Define TANKER zones for refuelling. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index ff41a82ac..1a8bfdb4c 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -54,6 +54,7 @@ -- @field Wrapper.Group#GROUP escortGroup The group to be escorted. -- @field DCS#Vec3 escortVec3 The 3D offset vector from the escorted group to the escort group. -- +-- @field Ops.AirWing#AIRWING airwing The assigned airwing. -- @field #string squadname Name of the assigned Airwing squadron. -- @field #table assets Airwing Assets assigned for this mission. -- @field #number nassets Number of required assets by the Airwing. @@ -436,6 +437,13 @@ function AUFTRAG:SetWeaponType(WeaponType) return self end +--- Check if mission is executing. +-- @param #AUFTRAG self +-- @return #boolean If true, mission is currently executing. +function AUFTRAG:IsExecuting() + return self.status==AUFTRAG.Status.EXECUTING +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -525,7 +533,7 @@ end --- Get coordinate of target. First unit/group of the set is used. -- @param #AUFTRAG self --- @param Core.Point#COORDINATE The target coordinate or nil. +-- @return Core.Point#COORDINATE The target coordinate or nil. function AUFTRAG:GetTargetCoordinate() if self.engageTargetGroupset then diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index b727ed471..b7e548181 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -3140,7 +3140,6 @@ function FLIGHTGROUP:onafterMissionStart(From, Event, To, Mission) -- Set mission status. Mission:Start() - --Mission.status=AUFTRAG.Status.STARTED -- Route flight to mission zone. self:RouteToMission(Mission, 5) @@ -3159,19 +3158,20 @@ function FLIGHTGROUP:onafterMissionExecute(From, Event, To, Mission) self:I(self.lid..text) MESSAGE:New(text, 120, "DEBUG"):ToAllIf(true) + -- Set mission status. Mission:Execute() - --Mission.status=AUFTRAG.Status.EXECUTING end ---- On after "MissionCancel" event. Cancels the current mission. +--- On after "MissionCancel" event. Cancels the mission. -- @param #FLIGHTGROUP self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function FLIGHTGROUP:onafterMissionCancel(From, Event, To) +-- @param Ops.Auftrag#AUFTRAG Mission The mission to be cancelled. +function FLIGHTGROUP:onafterMissionCancel(From, Event, To, Mission) - if self.currentmission then + if Mission.auftragsnummer==self.currentmission then -- Cancelling the mission is actually cancelling the current task. self:TaskCancel() diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 656a46b8d..52bd42ecf 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -17,7 +17,9 @@ -- @field #boolean Debug Debug mode. Messages to all about status. -- @field #string lid Class id string for output to DCS log file. -- @field #table filter Category filters. --- @field #table DetectedItems Table of detected items. +-- @field #table Contacts Table of detected items. +-- @field #table ContactsLost Table of lost detected items. +-- @field #table ContactsUnknown Table of new detected items. -- @extends Core.Fsm#FSM --- Be surprised! @@ -32,13 +34,14 @@ -- -- @field #INTEL INTEL = { - ClassName = "INTEL", - Debug = nil, - lid = nil, - filter = nil, - detectionset = nil, - DetectedItems = {}, - DetectedGroups = {}, + ClassName = "INTEL", + Debug = nil, + lid = nil, + filter = nil, + detectionset = nil, + Contacts = {}, + ContactsLost = {}, + ContactsUnknown = {}, } --- Detected item info. @@ -56,7 +59,7 @@ INTEL = { --- INTEL class version. -- @field #string version -INTEL.version="0.0.1" +INTEL.version="0.0.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list @@ -87,9 +90,6 @@ function INTEL:New(DetectionSet) -- Start State. self:SetStartState("Stopped") - -- Init set of detected units. - self.detectedunits=SET_UNIT:New() - -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("Stopped", "Start", "Running") -- Start FSM. @@ -97,12 +97,8 @@ function INTEL:New(DetectionSet) self:AddTransition("*", "Detect", "*") -- INTEL status update - self:AddTransition("*", "DetectedUnit", "*") -- - - self:AddTransition("*", "DetectedGroups", "*") -- All groups that are currently detected. - self:AddTransition("*", "DetectedGroupsUnknown", "*") -- Newly detected groups, which were previously unknown. - self:AddTransition("*", "DetectedGroupsDead", "*") -- Previously detected groups that could not be detected any more. - self:AddTransition("*", "DetectedGroupsLost", "*") -- Previously detected groups that could not be detected any more. + self:AddTransition("*", "NewContact", "*") -- + self:AddTransition("*", "LostContact", "*") -- ------------------------ @@ -153,12 +149,21 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set livery. +--- Set accept zones. -- @param #INTEL self --- @param #string liveryname Name of the livery. +-- @param Core.Set#SET_ZONE AcceptZoneSet Set of accept zones -- @return #INTEL self -function INTEL:SetLivery(liveryname) - self.livery=liveryname +function INTEL:SetAcceptZones(AcceptZoneSet) + self.acceptzoneset=AcceptZoneSet or SET_ZONE:New() + return self +end + +--- Set accept zones. +-- @param #INTEL self +-- @param Core.Zone#ZONE AcceptZone Add a zone to the accept zone set. +-- @return #INTEL self +function INTEL:AddAcceptZone(AcceptZone) + self.acceptzoneset=nil end @@ -175,7 +180,7 @@ end function INTEL:onafterStart(From, Event, To) -- Short info. - local text=string.format("Starting flight group %s.", self.groupname) + local text=string.format("Starting INTEL v%s.", self.version) self:I(self.sid..text) -- Start the status monitoring. @@ -197,8 +202,8 @@ function INTEL:onafterStatus(From, Event, To) self:UpdateIntel() -- Short info. - local text=string.format("Flight status %s [%d/%d]. Task=%d/%d. Waypoint=%d/%d. Detected=%d", fsmstate, #self.element, #self.element, self.taskcurrent, #self.taskqueue, self.currentwp or 0, #self.waypoints or 0, self.detectedunits:Count()) - self:I(self.sid..text) + local text=string.format("Status=%s", fsmstate) + self:I(self.lid..text) self:__Status(-30) @@ -224,12 +229,22 @@ function INTEL:UpdateIntel() end + -- TODO: Filter units from accept/reject zones. + -- TODO: Filter unit types. + -- TODO: Filter detection methods? + + self:CreateDetectedItems(DetectedSet) + + --[[ + -- Newly detected units. local detectednew=DetectedSet:GetSetComplement(self.detectedunits) -- Previously detected units which got lost. local detectedlost=self.detectedunits:GetSetComplement(DetectedSet) + ]] + -- TODO: Loose units only if they remain undetected for a given time interval. We want to avoid fast oscillation between detected/lost states. Maybe 1-5 min would be a good time interval?! -- TODO: Combine units to groups for all, new and lost. -- TODO: process detected set asynchroniously for better performance. @@ -259,34 +274,90 @@ function INTEL:CreateDetectedItems(detectedunitset) for _,_group in pairs(detectedgroupset.Set) do local group=_group --Wrapper.Group#GROUP - local detecteditem=self:GetDetectedItemByName(group:GetName()) + local groupname=group:GetName() + + local detecteditem=self:GetContactByName(groupname) if detecteditem then --- -- Detected item already exists ==> Update data. --- - + detecteditem.Tdetected=timer.getAbsTime() + detecteditem.position=group:GetCoordinate() else --- -- Detected item does not exist in our list yet. --- + local item={} --#INTEL.DetectedItem + + item.groupname=groupname + item.Tdetected=timer.getAbsTime() + item.group=group + item.position=group:GetCoordinate() + item.typename=group:GetTypeName() + item.attribute=group:GetAttribute() + item.category=group:GetCategory() + item.categoryname=group:GetCategoryName() + + self:AddContact(item) end end + + -- Now check if there some groups could not be detected any more. + for i=#self.Contacts,1,-1 do + local item=self.Contacts[i] --#INTEL.DetectedItem + + local group=detectedgroupset:FindGroup(item.groupname) + + if not group then + --TODO: check if deltaT>Tforget. we dont want quick oszillations between detected and undetected states. + self:RemoveContact(item) + end + end end --- Create detected items. -- @param #INTEL self --- @param #string name Name of the detected item. +-- @param #string groupname Name of the contact group. -- @return #INTEL.DetectedItem -function INTEL:GetDetectedItemByName(name) +function INTEL:GetContactByName(groupname) - return self.DetectedItems[name] + for i,_contact in pairs(self.Contacts) do + local contact=_contact --#INTEL.DetectedItem + if contact.groupname==groupname then + return contact + end + end + return nil +end + +--- Remove a contact from our list. +-- @param #INTEL self +-- @param #INTEL.DetectedItem Contact The contact to be removed. +function INTEL:RemoveContact(Contact) + + for i,_contact in pairs(self.Contacts) do + local contact=_contact --#INTEL.DetectedItem + + if contact.groupname==Contact.groupname then + table.remove(self.Contacts, i) + end + + end + +end + +--- Remove a contact from our list. +-- @param #INTEL self +-- @param #INTEL.DetectedItem Contact The contact to be removed. +function INTEL:AddContact(Contact) + table.insert(self.Contacts, Contact) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/WingCommander.lua b/Moose Development/Moose/Ops/WingCommander.lua index 2866f89e7..467244318 100644 --- a/Moose Development/Moose/Ops/WingCommander.lua +++ b/Moose Development/Moose/Ops/WingCommander.lua @@ -17,7 +17,8 @@ -- @field #boolean Debug Debug mode. Messages to all about status. -- @field #string lid Class id string for output to DCS log file. -- @field #table airwings Table of airwings. --- @extends Core.Fsm#FSM +-- @field #table missionqueue Mission queue. +-- @extends Ops.Intelligence#INTEL --- Be surprised! -- @@ -35,22 +36,21 @@ WINGCOMMANDER = { Debug = nil, lid = nil, airwings = {}, + missionqueue = {}, } ---- Mission capability. --- @type WINGCOMMANDER.Capability --- @field #string missiontype Mission Type. --- @field #number Ntot Total number of assets for this task. --- @field #number Navail Number of available assets --- @field #number Nonmission Number of assets currently on mission. - --- Mission resources. -- @type WINGCOMMANDER.Recourses +-- -- @field #string missiontype Mission Type. -- @field #number Ntot Total number of assets for this task. -- @field #number Navail Number of available assets -- @field #number Nonmission Number of assets currently on mission. +--- Contact details. +-- @type WINGCOMMANDER.Contact +-- @field Ops.Auftrag#AUFTRAG mission The assigned mission. +-- @extends Ops.Intelligence#INTEL.DetectedItem --- WINGCOMMANDER class version. -- @field #string version @@ -84,9 +84,7 @@ function WINGCOMMANDER:New(AgentSet) -- Add FSM transitions. -- From State --> Event --> To State - self:AddTransition("Stopped", "Start", "Running") -- Start FSM. - - self:AddTransition("*", "Sitrep", "*") -- WINGCOMMANDER status update + --self:AddTransition("Stopped", "Start", "Running") -- Start FSM. ------------------------ @@ -110,12 +108,12 @@ function WINGCOMMANDER:New(AgentSet) -- @param #WINGCOMMANDER self -- @param #number delay Delay in seconds. - --- Triggers the FSM event "FlightStatus". - -- @function [parent=#WINGCOMMANDER] WingCommanderStatus + --- Triggers the FSM event "Status". + -- @function [parent=#WINGCOMMANDER] Status -- @param #WINGCOMMANDER self --- Triggers the FSM event "SkipperStatus" after a delay. - -- @function [parent=#WINGCOMMANDER] __WingCommanderStatus + -- @function [parent=#WINGCOMMANDER] __Status -- @param #WINGCOMMANDER self -- @param #number delay Delay in seconds. @@ -167,8 +165,10 @@ function WINGCOMMANDER:onafterStart(From, Event, To) local text=string.format("Starting Wing Commander") self:I(self.lid..text) - -- Start the status monitoring. - self:__SitRep(-1) + -- Start parent INTEL. + self:GetParent(self).onafterStart(self, From, Event, To) + + end --- On after "Sitrep" event. @@ -177,18 +177,19 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function WINGCOMMANDER:onafterSitrep(From, Event, To) +function WINGCOMMANDER:onafterStatus(From, Event, To) + + -- Start parent INTEL. + self:GetParent(self).onafterStatus(self, From, Event, To) -- FSM state. local fsmstate=self:GetState() - - -- Short info. local text=string.format("No activities") - local capabilities=self:CheckResources() + --local capabilities=self:CheckResources() -- Number of assets total, on mission, available -- Number of assets available of each mission type. @@ -196,10 +197,25 @@ function WINGCOMMANDER:onafterSitrep(From, Event, To) self:I(self.lid..text) - if DetectedGroupsUnknown then - for _,_group in pairs(DetectedGroupsUnknown) do - local group=_group --Wrapper.Group#GROUP + -- Clean up missions where the contact was lost. + for _,_contact in pairs(self.ContactsLost) do + local contact=_contact --#WINGCOMMANDER.Contact + + if contact.mission and contact.mission.airwing then + + -- Cancel this mission. + contact.mission.airwing:MissionCancel(contact.mission) + + end + + end + + + -- Create missions for all new contacts. + for _,_contact in pairs(self.ContactsUnknown) do + local contact=_contact --#WINGCOMMANDER.Contact + local group=contact.group --Wrapper.Group#GROUP if group and group:IsAlive() then @@ -207,14 +223,11 @@ function WINGCOMMANDER:onafterSitrep(From, Event, To) local attribute=group:GetAttribute() local threatlevel=group:GetThreatLevel() - if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then + local mission=nil --Ops.Auftrag#AUFTRAG - if capability.INTERCEPT.Navail>0 then - - --TODO: Something like get closest AIRWING or squadron? - -- Launch even from multiple airwings? Currently Navail is like this. - - end + if category==Group.Category.AIRPLANE or category==Group.Category.HELICOPTER then + + mission=AUFTRAG:NewINTERCEPT(group) elseif category==Group.Category.GROUND then @@ -231,27 +244,113 @@ function WINGCOMMANDER:onafterSitrep(From, Event, To) end + mission=AUFTRAG:NewBAI(group) + elseif category==Group.Category.SHIP then --TODO: ANTISHIP end - - + + + -- Add mission to queue. + if mission then + table.insert(self.missionqueue, mission) + end + end end - end - + -- Check mission queue and assign one PLANNED mission. + self:CheckMissionQueue() + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Resources ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Check mission queue and assign ONE planned mission. +-- @param #WINGCOMMANDER self +function WINGCOMMANDER:CheckMissionQueue() + + for _,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + -- We look for PLANNED missions. + if mission.status==AUFTRAG.Status.PLANNED then + + --- + -- PLANNNED Mission + --- + + -- Table of airwings that can do the mission. + local airwings={} + + -- Loop over all airwings. + for _,_airwing in pairs(self.airwings) do + local airwing=_airwing --Ops.AirWing#AIRWING + + -- Check if airwing can do this mission. + local can,assets=airwing:CanMission(mission.type, mission.nassets) + + -- Can it? + if can then + + -- Get coordinate of the target. + local coord=mission:GetTargetCoordinate() + + if coord then + + -- Distance from airwing to target. + local dist=coord:Get2DDistance(airwing:GetCoordinate()) + + -- Add airwing to table of airwings that can. + table.insert(airwings, {airwing=airwing, dist=dist, targetcoord=coord}) + + end + + end + + end + + -- Can anyone? + if #airwings>0 then + + -- Sort table wrt distace + local function sortdist(a,b) + return a.dist