From 2eb4e87d32b76c307a3b578f89d5ef982eceac8f Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 6 Mar 2020 17:29:43 +0100 Subject: [PATCH] Ops --- .../Moose/Functional/Warehouse.lua | 13 +- Moose Development/Moose/Ops/AirWing.lua | 337 +++++++++++++----- Moose Development/Moose/Ops/Auftrag.lua | 110 +++++- Moose Development/Moose/Ops/FlightGroup.lua | 19 + Moose Development/Moose/Ops/Intelligence.lua | 45 ++- Moose Development/Moose/Ops/Squadron.lua | 93 ++--- Moose Development/Moose/Ops/WingCommander.lua | 157 +++++--- 7 files changed, 538 insertions(+), 236 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 5fb896a71..0a6522bcf 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1894,19 +1894,26 @@ function WAREHOUSE:New(warehouse, alias) -- From State --> Event --> To State self:AddTransition("NotReadyYet", "Load", "Loaded") -- Load the warehouse state from scatch. self:AddTransition("Stopped", "Load", "Loaded") -- Load the warehouse state stopped state. + self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse from scratch. self:AddTransition("Loaded", "Start", "Running") -- Start the warehouse when loaded from disk. + self:AddTransition("*", "Status", "*") -- Status update. + self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. self:AddTransition("*", "NewAsset", "*") -- New asset was added to warehouse stock. + self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. self:AddTransition("Running", "RequestSpawned", "*") -- Assets of request were spawned. self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier (unused ==> unnecessary?). self:AddTransition("*", "AssetSpawned", "*") -- Asset has been spawned into the world. self:AddTransition("*", "AssetLowFuel", "*") -- Asset is low on fuel. + self:AddTransition("*", "Arrived", "*") -- Cargo or transport group has arrived. + self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! @@ -5933,8 +5940,9 @@ end --- Get a warehouse request from its unique id. -- @param #WAREHOUSE self --- @param #number id Asset ID. --- @return #WAREHOUSE.Pendingitem The warehouse requested - either queued or pending. #boolean If *true*, request is queued, if *false*, request is pending, if *nil*, request could not be found. +-- @param #number id Request ID. +-- @return #WAREHOUSE.Pendingitem The warehouse requested - either queued or pending. +-- @return #boolean If *true*, request is queued, if *false*, request is pending, if *nil*, request could not be found. function WAREHOUSE:GetRequestByID(id) if id then @@ -8092,7 +8100,6 @@ end -- @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 diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 74f2735cc..ce75e09df 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -27,6 +27,8 @@ -- @field #number nflightsCAP Number of CAP flights constantly in the air. -- @field #number nflightsTANKER Number of TANKER flights constantly in the air. -- @field #number nflightsAWACS Number of AWACS flights constantly in the air. +-- @field #table cappoints Table of CAP points. +-- @field Ops.WingCommander#WINGCOMMANDER wingcommander The wing commander responsible for this airwing. -- @extends Functional.Warehouse#WAREHOUSE --- Be surprised! @@ -49,6 +51,7 @@ AIRWING = { missionqueue = {}, payloads = {}, cappoints = {}, + wingcommander = nil, } --- Squadron data. @@ -366,6 +369,25 @@ function AIRWING:AddMission(Mission) return self end +--- Remove mission from queue. +-- @param #AIRWING self +-- @param Ops.Auftrag#AUFTRAG Mission Mission to be removed. +-- @return #AIRWING self +function AIRWING:RemoveMission(Mission) + + for i,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission.auftragsnummer==Mission.auftragsnummer then + table.remove(self.missionqueue, i) + break + end + + end + + return self +end + --- Set CAP zones. -- @param #AIRWING self -- @param #number n Number of flights. Default 1. @@ -375,11 +397,11 @@ function AIRWING:SetCAPflights(n) return self end ---- Add a CAP zone. +--- Add a patrol Point for CAP missions. -- @param #AIRWING self -- @param Core.Zone#ZONE AcceptZone Add a zone to the CAP zone set. -- @return #AIRWING self -function AIRWING:AddCAPPoint(Coordinate, Heading, Leg, Speed) +function AIRWING:AddPatrolPointCAP(Coordinate, Heading, Leg, Speed) local cappoint={} --#AIRWING.PatrolData cappoint.coord=Coordinate @@ -430,6 +452,13 @@ function AIRWING:onafterStatus(From, Event, To) local fsmstate=self:GetState() + -- Check CAP missions. + self:CheckCAP() + + self:CheckTANKER() + + self:CheckAWACS() + ------------------ -- Mission Info -- ------------------ @@ -487,6 +516,58 @@ function AIRWING:onafterStatus(From, Event, To) end +--- Get patrol data +-- @param #AIRWING self +-- @param #table PatrolPoints Patrol data points. +-- @return #AIRWING.PatrolData +function AIRWING:_GetPatrolData(PatrolPoints) + + local function sort(a,b) + return a.noccuied0 then + self:E(self.lid..string.format("ERROR: mission %s of type %s has already assets attached!", mission.name, mission.type)) + end mission.assets={} -- Assign assets to mission. @@ -544,7 +629,7 @@ function AIRWING:_GetNextMission() self:E("No payload for asset! This should not happen!") end - table.insert(mission.assets, asset) + mission:AddAsset(asset) end return mission @@ -608,61 +693,6 @@ end -- FSM Events ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On after "NewAsset" event. Asset is added to the given squadron (asset assignment). --- @param #AIRWING self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset that has just been added. --- @param #string assignment The (optional) assignment for the asset. -function AIRWING:onafterNewAsset(From, Event, To, asset, assignment) - - -- Call parent warehouse function first. - self:GetParent(self).onafterNewAsset(self, From, Event, To, asset, assignment) - - -- Get squadron. - local squad=self:GetSquadron(asset.assignment) - - -- Check if asset is already part of the squadron. If an asset returns, it will be added again! We check that asset.assignment is also assignment. - if squad and asset.assignment==assignment then - - -- Debug text. - local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s", squad.name, assignment, asset.unittype, asset.attribute) - self:I(self.lid..text) - - -- Add asset to squadron. - table.insert(squad.assets, asset) - - end -end - ---- On after "MissionCancel" event. --- @param #AIRWING self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Ops.Auftrag#AUFTRAG Mission The mission to be cancelled. -function AIRWING:onafterMissionCancel(From, Event, To, Mission) - - self:I(self.lid..string.format("Cancel mission %s", Mission.name)) - - for _,_asset in pairs(Mission.assets) do - local asset=_asset --#AIRWING.SquadronAsset - - local flightgroup=asset.flightgroup - - if flightgroup then - flightgroup:MissionCancel(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. -- @param #AIRWING self -- @param #string From From state. @@ -713,6 +743,132 @@ function AIRWING:onafterMissionRequest(From, Event, To, Mission) end +--- On after "MissionCancel" event. Cancels the missions of all flightgroups. Deletes request from warehouse queue. +-- @param #AIRWING self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Ops.Auftrag#AUFTRAG Mission The mission to be cancelled. +function AIRWING:onafterMissionCancel(From, Event, To, Mission) + + self:I(self.lid..string.format("Cancel mission %s", Mission.name)) + + for _,_asset in pairs(Mission.assets) do + local asset=_asset --#AIRWING.SquadronAsset + + local flightgroup=asset.flightgroup + + if flightgroup then + flightgroup:MissionCancel(Mission) + end + end + + -- Remove queued request (if any). + if Mission.requestID then + self:_DeleteQueueItemByID(Mission.requestID, self.queue) + end + +end + +--- On after "NewAsset" event. Asset is added to the given squadron (asset assignment). +-- @param #AIRWING self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Functional.Warehouse#WAREHOUSE.Assetitem asset The asset that has just been added. +-- @param #string assignment The (optional) assignment for the asset. +function AIRWING:onafterNewAsset(From, Event, To, asset, assignment) + + -- Call parent warehouse function first. + self:GetParent(self).onafterNewAsset(self, From, Event, To, asset, assignment) + + -- Get squadron. + local squad=self:GetSquadron(asset.assignment) + + -- Check if asset is already part of the squadron. If an asset returns, it will be added again! We check that asset.assignment is also assignment. + if squad then + + if asset.assignment==assignment then + + -- Debug text. + local text=string.format("Adding asset to squadron %s: assignment=%s, type=%s, attribute=%s", squad.name, assignment, asset.unittype, asset.attribute) + self:I(self.lid..text) + + -- Add asset to squadron. + table.insert(squad.assets, asset) + + else + + local mission=self:GetMissionByID(assignment) + + if mission then + local text=string.format("Asset %s returned from %s mission %s", asset.spawngroupname, mission.type, mission.name) + self:I(self.lid..text) + else + self:E(self.lid.."ERROR: new asset not beloning to any s") + end + + end + + end +end + + + + +--- On after "AssetSpawned" event triggered when an asset group is spawned into the cruel world. +-- Creates a new flightgroup element and adds the mission to the flightgroup queue. +-- @param #AIRWING self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP group The group spawned. +-- @param #AIRWING.SquadronAsset asset The asset that was spawned. +-- @param Functional.Warehouse#WAREHOUSE.Pendingitem request The request of the dead asset. +function AIRWING:onafterAssetSpawned(From, Event, To, group, asset, request) + + -- Call parent warehouse function first. + self:GetParent(self).onafterAssetSpawned(self, From, Event, To, group, asset, request) + + -- Create a flight group. + asset.flightgroup=self:_CreateFlightGroup(asset) + + -- Get Mission (if any). + local mission=self:GetMissionByID(request.assignment) + + -- Add mission to flightgroup queue. + if mission then + + -- Add mission to flightgroup queue. + asset.flightgroup:AddMission(mission) + end + + -- Add group to the detection set of the WINGCOMMANDER. + if self.wingcommander then + self.wingcommander.detectionset:AddGroup(asset.flightgroup.group) + end + +end + +--- On after "AssetDead" event triggered when an asset group died. +-- @param #AIRWING self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #AIRWING.SquadronAsset asset The asset that is dead. +-- @param Functional.Warehouse#WAREHOUSE.Pendingitem request The request of the dead asset. +function AIRWING:onafterAssetDead(From, Event, To, asset, request) + + -- Call parent warehouse function first. + self:GetParent(self).onafterAssetDead(From, Event, To, asset, request) + + -- Add group to the detection set of the WINGCOMMANDER. + if self.wingcommander then + self.wingcommander.detectionset:RemoveGroupsByName({asset.spawngroupname}) + end +end + + --- On after "Request" event. -- @param #AIRWING self -- @param #string From From state. @@ -741,34 +897,6 @@ function AIRWING:onafterRequest(From, Event, To, Request) end ---- On after "AssetSpawned" event triggered when an asset group is spawned into the cruel world. Creates a new flightgroup element and adds the mission to the flightgroup queue. --- @param #AIRWING self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Wrapper.Group#GROUP group The group spawned. --- @param #AIRWING.SquadronAsset asset The asset that was spawned. --- @param Functional.Warehouse#WAREHOUSE.Pendingitem request The request of the dead asset. -function AIRWING:onafterAssetSpawned(From, Event, To, group, asset, request) - - -- Call parent warehouse function first. - self:GetParent(self).onafterAssetSpawned(self, From, Event, To, group, asset, request) - - -- Create a flight group. - asset.flightgroup=self:_CreateFlightGroup(asset) - - -- Get Mission (if any). - local mission=self:GetMissionByID(request.assignment) - - -- Add mission to flightgroup queue. - if mission then - - -- Add mission to flightgroup queue. - asset.flightgroup:AddMission(mission) - end - -end - --- On after "SelfRequest" event. -- @param #AIRWING self -- @param #string From From state. @@ -972,6 +1100,41 @@ function AIRWING:GetAssetCurrentMission(asset) end +--- Count assets on mission. +-- @param #AIRWING self +-- @param #table MissionTypes Types on mission to be checked. Default all. +-- @return #number Number of pending and queued assets. +-- @return #number Number of pending assets. +-- @return #number Number of queued assets. +function AIRWING:CountAssetsOnMission(MissionTypes) + + local Nq=0 + local Np=0 + + for _,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + -- Check if this mission type is requested. + if self:CheckMissionType(mission.type, MissionTypes) then + + for _,_asset in pairs(mission.assets or {}) do + local asset=_asset --#AIRWING.SquadronAsset + + local request, isqueued=self:GetRequestByID(mission.requestID) + + if isqueued then + Nq=Nq+1 + else + Np=Np+1 + end + + end + end + end + + return Np+Nq, Np, Nq +end + --- Check if assets for a given mission type are available. -- @param #AIRWING self -- @param #string MissionType Type of mission. diff --git a/Moose Development/Moose/Ops/Auftrag.lua b/Moose Development/Moose/Ops/Auftrag.lua index bc3b09a98..052354daa 100644 --- a/Moose Development/Moose/Ops/Auftrag.lua +++ b/Moose Development/Moose/Ops/Auftrag.lua @@ -97,6 +97,7 @@ AUFTRAG = { lid = nil, auftragsnummer = nil, flightdata = {}, + assets = {}, } --- Global mission counter. @@ -116,6 +117,7 @@ _AUFTRAGSNR=0 -- @param #string FERRY Ferry flight mission. -- @param #string INTERCEPT Intercept mission. -- @param #string ORBIT Orbit mission. +-- @param #string PATROL Similar to CAP but no auto engage targets. -- @param #string RECON Recon mission. -- @param #string SEAD Suppression/destruction of enemy air defences. -- @param #string STRIKE Strike mission. @@ -133,6 +135,7 @@ AUFTRAG.Type={ FERRY="Ferry Flight", INTERCEPT="Intercept", ORBIT="Orbit", + PATROL="Patrol", RECON="Recon", SEAD="SEAD", STRIKE="Strike", @@ -291,6 +294,29 @@ function AUFTRAG:NewORBIT(Coordinate, Speed, Heading, Leg) return auftrag end +--- Create a PATROL mission. +-- @param #AUFTRAG self +-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate. +-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts. +-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West). +-- @param #number Leg Length of race-track in NM. Default 10 NM. +-- @param #number Altitude Orbit altitude in feet. +-- @return #AUFTRAG self +function AUFTRAG:NewPATROL(OrbitCoordinate, OrbitSpeed, Heading, Leg, Altitude) + + -- Create ORBIT first. + local mission=self:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg) + + if Altitude then + mission.orbitCoord.y=UTILS.FeetToMeters(Altitude) + end + + -- CAP paramters. + mission.type=AUFTRAG.Type.PATROL + + return mission +end + --- Create a CAP mission. -- @param #AUFTRAG self -- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate. @@ -562,7 +588,7 @@ end --- Check if mission is done. -- @param #AUFTRAG self -- @return #boolean If true, mission is done. -function AUFTRAG:IsCancelled() +function AUFTRAG:IsDone() return self.status==AUFTRAG.Status.DONE end @@ -640,6 +666,16 @@ function AUFTRAG:onafterStatus(From, Event, To) end end +--- Set flightgroup mission status. +-- @param #AUFTRAG self +-- @return #string status New status. +function AUFTRAG:Evaluate() + + local Ntargets=self:CountMissionTargets() + +end + + --- Set flightgroup mission status. -- @param #AUFTRAG self -- @param Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group. @@ -735,7 +771,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AUFTRAG:onafterRequested(From, Event, TO) +function AUFTRAG:onafterRequested(From, Event, To) self.status=AUFTRAG.Status.REQUESTED self:I(self.lid..string.format("New mission status=%s", self.status)) end @@ -802,7 +838,8 @@ function AUFTRAG:onafterCancel(From, Event, To) self:I(self.lid..string.format("New mission status=%s", self.status)) if self.airwing then - + + -- Airwing will cancel all flight missions and remove queued request from warehouse queue. self.airwing:MissionCancel(self) else @@ -811,11 +848,12 @@ function AUFTRAG:onafterCancel(From, Event, To) local flightdata=_flightdata --#AUFTRAG.FlightData flightdata.flightgroup:MissionCancel(self) end + end end ---- On after "Stop" event. +--- On after "Stop" event. Remove mission from AIRWING and FLIGHTGROUP mission queues. -- @param #AUFTRAG self -- @param #string From From state. -- @param #string Event Event. @@ -823,6 +861,19 @@ end function AUFTRAG:onafterStop(From, Event, To) -- TODO: remove missions from queues in WINGCOMMANDER, AIRWING and FLIGHGROUPS! + + if self.wingcommander then + + end + + if self.airwing then + self.airwing:RemoveMission(self) + end + + for _,_flightdata in pairs(self.flightdata) do + local flightdata=_flightdata --#AUFTRAG.FlightData + flightdata.flightgroup:RemoveMission(self) + end self.CallScheduler:Clear() end @@ -831,15 +882,60 @@ end -- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Add asset to mission. +-- @param #AUFTRAG self +-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset to be added to the mission. +-- @return #AIRWING self +function AUFTRAG:AddAsset(Asset) + + self.assets=self.assets or {} + + table.insert(self.assets, Asset) + + return self +end + +--- Delete asset from mission. +-- @param #AUFTRAG self +-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The asset to be removed. +-- @return #AIRWING self +function AUFTRAG:DelAsset(Asset) + + for i,_asset in pairs(self.assets or {}) do + local asset=_asset --Ops.AirWing#AIRWING.SquadronAsset + + if asset.uid==Asset.uid then + table.remove(self.assets, i) + end + + end + +end + + --- Count alive mission targets. -- @param #AUFTRAG self --- @return #number Number of alive targets. +-- @return #number Number of alive target units. function AUFTRAG:CountMissionTargets() local N=0 if self.engageTargetGroupset then - local n=self.engageTargetGroupset:CountAlive() - N=N+n + --local n=self.engageTargetGroupset:CountAlive() + + for _,_group in pairs(self.engageTargetGroupset.Set) do + local group=_group --Wrapper.Group#GROUP + + if group and group:IsAlive() then + + for _,_unit in pairs(group:GetUnits()) do + local unit=_unit --Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + N=N+1 + end + end + + end + end end if self.engageTargetUnitset then diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 0d0244814..701ed27d1 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -679,6 +679,25 @@ function FLIGHTGROUP:AddMission(Mission) return self end +--- Remove mission from queue. +-- @param #FLIGHTGROUP self +-- @param Ops.Auftrag#AUFTRAG Mission Mission to be removed. +-- @return #FLIGHTGROUP self +function FLIGHTGROUP:RemoveMission(Mission) + + for i,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission.auftragsnummer==Mission.auftragsnummer then + table.remove(self.missionqueue, i) + break + end + + end + + return self +end + --- Set AIRWING the flight group belongs to. -- @param #FLIGHTGROUP self -- @param Ops.AirWing#AIRWING airwing The AIRWING object. diff --git a/Moose Development/Moose/Ops/Intelligence.lua b/Moose Development/Moose/Ops/Intelligence.lua index 10ae18dba..b657cb511 100644 --- a/Moose Development/Moose/Ops/Intelligence.lua +++ b/Moose Development/Moose/Ops/Intelligence.lua @@ -71,10 +71,11 @@ INTEL.version="0.0.3" -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Accept and reject zones. --- TODO: SetAttributeZone --> return groups of generalized attributes in a zone. --- 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. +-- DONE: Accept zones. +-- TODO: Reject zones. +-- NOGO: SetAttributeZone --> return groups of generalized attributes in a zone. +-- DONE: 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?! +-- DONE: Combine units to groups for all, new and lost. -- TODO: process detected set asynchroniously for better performance. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -243,7 +244,7 @@ function INTEL:onafterStatus(From, Event, To) local Ncontacts=#self.Contacts -- Short info. - local text=string.format("Status %s: Agents=%s, Contacts=%d, New=%d, Lost=%d", fsmstate, self.detectionset:CountAlive(), Ncontacts, #self.ContactsUnknown, #self.ContactsLost) + local text=string.format("Status %s [Agents=%s]: Contacts=%d, New=%d, Lost=%d", fsmstate, self.detectionset:CountAlive(), Ncontacts, #self.ContactsUnknown, #self.ContactsLost) self:I(self.lid..text) -- Detailed info. @@ -277,22 +278,39 @@ function INTEL:UpdateIntel() -- Get set of detected units. local detectedunitset=recce:GetDetectedUnitSet() - + -- Add detected units to all set. DetectedSet=DetectedSet:GetSetUnion(detectedunitset) end end end - -- TODO: Filter units from accept/reject zones. + -- TODO: Filter units from reject zones. -- TODO: Filter unit types. -- TODO: Filter detection methods? - - for _,_zone in pairs(self.acceptzoneset.Set) do - local zone=_zone --Core.Zone#ZONE + local remove={} + for _,_unit in pairs(DetectedSet.Set) do + local unit=_unit --Wrapper.Unit#UNIT + -- Check if unit is in any of the accept zones. + local inzone=false + for _,_zone in pairs(self.acceptzoneset.Set) do + local zone=_zone --Core.Zone#ZONE + if unit:IsInZone(zone) then + inzone=true + break + end + end + + -- Unit is not in accept zone ==> remove! + if not inzone then + table.insert(remove, unit:GetName()) + end end + -- Remove filtered units. + DetectedSet:RemoveUnitsByName(remove) + -- Create detected contacts. self:CreateDetectedItems(DetectedSet) @@ -305,6 +323,7 @@ function INTEL:CreateDetectedItems(detectedunitset) local detectedgroupset=SET_GROUP:New() + -- Convert detected UNIT set to detected GROUP set. for _,_unit in pairs(detectedunitset:GetSet()) do local unit=_unit --Wrapper.Unit#UNIT @@ -374,9 +393,9 @@ function INTEL:CreateDetectedItems(detectedunitset) local group=detectedgroupset:FindGroup(item.groupname) - -- Check if deltaT>Tforget. We dont want quick oszillations between detected and undetected states. + -- Check if deltaT>Tforget. We dont want quick oscillations between detected and undetected states. if self:CheckContactLost(item) then - -- Trigger LostContact event. + -- Trigger LostContact event. This also adds the contact to the self.ContactsLost table. self:LostContact(item) -- Remove contact from table. self:RemoveContact(item) @@ -412,7 +431,7 @@ function INTEL:onafterLostContact(From, Event, To, Contact) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Misc Fuctions +-- Misc Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create detected items. diff --git a/Moose Development/Moose/Ops/Squadron.lua b/Moose Development/Moose/Ops/Squadron.lua index cf0f770c9..d4973471b 100644 --- a/Moose Development/Moose/Ops/Squadron.lua +++ b/Moose Development/Moose/Ops/Squadron.lua @@ -1,4 +1,4 @@ ---- **Ops** - (R2.5) - AI Squadron for Ops. +--- **Ops** - Airwing Squadron. -- -- **Main Features:** -- @@ -15,9 +15,9 @@ -- @type SQUADRON -- @field #string ClassName Name of the class. -- @field #boolean Debug Debug mode. Messages to all about status. --- @field #string sid Class id string for output to DCS log file. +-- @field #string lid Class id string for output to DCS log file. -- @field #string livery Livery of the squadron. --- @field #table flights Table of flight groups. +-- @field #table assets Table of assets. -- @extends Core.Fsm#FSM --- Be surprised! @@ -34,10 +34,13 @@ SQUADRON = { ClassName = "SQUADRON", Debug = nil, - sid = nil, - flights = {}, - tasks = {}, - livery = nil, + lid = nil, + name = nil, + templatename = nil, + assets = {}, + livery = nil, + skill = nil, + airwing = nil, } --- Flight group element. @@ -64,13 +67,13 @@ SQUADRON.version="0.0.1" -- @param #string SquadName Name of the squadron, e.g. "VFA-37". -- @param #table tasks Table of squadron tasks, e.g. `{SQUADRON.Task.INTERCEPT, SQUADRON.Task.SEAD}`. -- @return #SQUADRON self -function SQUADRON:New(squadronname) +function SQUADRON:New(SquadronName, TemplateGroupName) -- Inherit everything from FSM class. local self=BASE:Inherit(self, FSM:New()) -- #SQUADRON --self.flightgroup=AIGroup - self.squadronname=tostring(squadronname) + self.name=tostring(SquadronName) -- Set home airbase. self.homebase=airbase @@ -90,7 +93,6 @@ function SQUADRON:New(squadronname) -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("Stopped", "Start", "Running") -- Start FSM. - self:AddTransition("*", "Status", "*") -- SQUADRON status update @@ -150,57 +152,15 @@ function SQUADRON:SetLivery(liveryname) self.livery=liveryname end ---- Set home airbase. +--- Set airwing. -- @param #SQUADRON self --- @param Wrapper.Airbase#AIRBASE airbase Home airbase object of the squadron. +-- @param Ops.AirWing#AIRWING Airwing The airwing. -- @return #SQUADRON self -function SQUADRON:SetHomeBase(airbase) - self.homebase=airbase +function SQUADRON:SetAirwing(Airwing) + self.airwing=Airwing end ---- Add a group to the squadron. --- @param #SQUADRON self --- @param #string groupname Name of the group as defined in the mission editor. --- @param #number n Number of groups that is added. --- @return #SQUADRON self -function SQUADRON:AddGroup(groupname, n) - - - - -end - - ---- Add flight group(s) to squadron. --- @param #SQUADRON self --- @param Ops.FlightGroup#FLIGHTGROUP flightgroup Flight group. --- @return #SQUADRON self -function SQUADRON:AddFlightGroup(flightgroup) - - local text=string.format("Adding flight group %s to squadron", flightgroup:GetName()) - self:I(self.sid..text) - - -- Set squadron. - flightgroup:SetSquadron(self) - - table.insert(self.flightgroups, flightgroup) - - function flightgroup:OnAfterDetectedUnit(From,Event,To,Unit) - self:GetSquadron():DetectedUnit(Unit) - end - - function flightgroup:OnAfterFlightAirborne(From,Event,To) - self:GetSquadron():FlightAirborne(self) - end - - function flightgroup:OnAfterFlightDead(From,Event,To) - self:GetSquadron():FlightDead(self) - end - - return self -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -214,41 +174,40 @@ end function SQUADRON:onafterStart(From, Event, To) -- Short info. - local text=string.format("Starting flight group %s.", self.groupname) - self:I(self.sid..text) + local text=string.format("Starting SQUADRON %s.", self.name) + self:I(self.lid..text) -- Start the status monitoring. - self:__SquadronStatus(-1) + self:__Status(-1) end ---- On after "FlightStatus" event. +--- On after "Status" event. -- @param #SQUADRON self -- @param Wrapper.Group#GROUP Group Flight group. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function SQUADRON:onafterSquadronStatus(From, Event, To) +function SQUADRON:onafterStatus(From, Event, To) -- FSM state. local fsmstate=self:GetState() -- Check if group has detected any units. - self:_CheckFlightStatus() + self:_CheckAssetStatus() -- 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) - end ---- On after "FlightStatus" event. +--- Check asset status. -- @param #SQUADRON self -function SQUADRON:_CheckFlightstatus() +function SQUADRON:_CheckAssetStatus() - for _,_flight in pairs(self.flights) do - local flight=_flight --#SQUADRON.Flight + for _,_assets in pairs(self.assets) do + local asset=_asset --#SQUADRON.Flight flight.flightgroup:IsSpawned() diff --git a/Moose Development/Moose/Ops/WingCommander.lua b/Moose Development/Moose/Ops/WingCommander.lua index 67280c111..2d3e02fb8 100644 --- a/Moose Development/Moose/Ops/WingCommander.lua +++ b/Moose Development/Moose/Ops/WingCommander.lua @@ -54,14 +54,17 @@ WINGCOMMANDER = { --- WINGCOMMANDER class version. -- @field #string version -WINGCOMMANDER.version="0.0.2" +WINGCOMMANDER.version="0.0.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Add tasks. --- TODO: +-- DONE: Add/remove spawned flightgroups to detection set. +-- TODO: Define A2A and A2G parameters. Engagedistance, etc. +-- TODO: Borderzones. +-- TODO: Improve airwing selection. Look at CAP flights near by etc. +-- TODO: Maybe it's possible to preselect the assets for the mission. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor @@ -144,6 +147,35 @@ function WINGCOMMANDER:AddAirwing(Airwing) return self end +--- Add mission to mission queue. +-- @param #WINGCOMMANDER self +-- @param Ops.Auftrag#AUFTRAG Mission Mission to be added. +-- @return #WINGCOMMANDER self +function WINGCOMMANDER:AddMission(Mission) + + table.insert(self.missionqueue, Mission) + + return self +end + +--- Remove mission from queue. +-- @param #WINGCOMMANDER self +-- @param Ops.Auftrag#AUFTRAG Mission Mission to be removed. +-- @return #WINGCOMMANDER self +function WINGCOMMANDER:RemoveMission(Mission) + + for i,_mission in pairs(self.missionqueue) do + local mission=_mission --Ops.Auftrag#AUFTRAG + + if mission.auftragsnummer==Mission.auftragsnummer then + table.remove(self.missionqueue, i) + break + end + + end + + return self +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status @@ -174,7 +206,7 @@ function WINGCOMMANDER:onafterStart(From, Event, To) end ---- On after "Sitrep" event. +--- On after "Status" event. -- @param #WINGCOMMANDER self -- @param Wrapper.Group#GROUP Group Flight group. -- @param #string From From state. @@ -193,7 +225,11 @@ function WINGCOMMANDER:onafterStatus(From, Event, To) for _,_contact in pairs(self.ContactsLost) do local contact=_contact --#WINGCOMMANDER.Contact - if contact.mission and contact.mission.airwing then + if contact.mission then + + local text=string.format("Lost contact to target! %s mission %s will be cancelled.", contact.mission.type:upper(), contact.mission.name) + MESSAGE:New(text, 120, "WINGCOMMANDER"):ToAll() + self:I(self.lid..text) -- Cancel this mission. contact.mission:Cancel() @@ -248,7 +284,7 @@ function WINGCOMMANDER:onafterStatus(From, Event, To) -- Add mission to queue. if mission then mission.nassets=1 - table.insert(self.missionqueue, mission) + self:AddMission(mission) end end @@ -296,48 +332,10 @@ function WINGCOMMANDER:CheckMissionQueue() -- 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 + local airwing=self:GetAirwingForMission(mission) - -- 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 + if airwing then - -- Can anyone? - if #airwings>0 then - - -- Sort table wrt distace - local function sortdist(a,b) - return a.dist0 then + + -- Sort table wrt distace + local function sortdist(a,b) + return a.dist