This commit is contained in:
Frank
2020-03-06 17:29:43 +01:00
parent fd730206a6
commit 2eb4e87d32
7 changed files with 538 additions and 236 deletions
@@ -1894,19 +1894,26 @@ function WAREHOUSE:New(warehouse, alias)
-- From State --> Event --> To State -- From State --> Event --> To State
self:AddTransition("NotReadyYet", "Load", "Loaded") -- Load the warehouse state from scatch. self:AddTransition("NotReadyYet", "Load", "Loaded") -- Load the warehouse state from scatch.
self:AddTransition("Stopped", "Load", "Loaded") -- Load the warehouse state stopped state. self:AddTransition("Stopped", "Load", "Loaded") -- Load the warehouse state stopped state.
self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse from scratch. self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse from scratch.
self:AddTransition("Loaded", "Start", "Running") -- Start the warehouse when loaded from disk. self:AddTransition("Loaded", "Start", "Running") -- Start the warehouse when loaded from disk.
self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "Status", "*") -- Status update.
self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock.
self:AddTransition("*", "NewAsset", "*") -- New asset was added to warehouse stock. self:AddTransition("*", "NewAsset", "*") -- New asset was added to warehouse stock.
self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse.
self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode.
self:AddTransition("Running", "RequestSpawned", "*") -- Assets of request were spawned. self:AddTransition("Running", "RequestSpawned", "*") -- Assets of request were spawned.
self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. 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("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier (unused ==> unnecessary?).
self:AddTransition("*", "AssetSpawned", "*") -- Asset has been spawned into the world. self:AddTransition("*", "AssetSpawned", "*") -- Asset has been spawned into the world.
self:AddTransition("*", "AssetLowFuel", "*") -- Asset is low on fuel. self:AddTransition("*", "AssetLowFuel", "*") -- Asset is low on fuel.
self:AddTransition("*", "Arrived", "*") -- Cargo or transport group has arrived. 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("*", "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("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! 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. --- Get a warehouse request from its unique id.
-- @param #WAREHOUSE self -- @param #WAREHOUSE self
-- @param #number id Asset ID. -- @param #number id Request 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. -- @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) function WAREHOUSE:GetRequestByID(id)
if id then if id then
@@ -8092,7 +8100,6 @@ end
-- @param #number qitemID ID of queue item to be removed. -- @param #number qitemID ID of queue item to be removed.
-- @param #table queue The queue from which the item should be deleted. -- @param #table queue The queue from which the item should be deleted.
function WAREHOUSE:_DeleteQueueItemByID(qitemID, queue) function WAREHOUSE:_DeleteQueueItemByID(qitemID, queue)
self:F({qitem=qitem, queue=queue})
for i=1,#queue do for i=1,#queue do
local _item=queue[i] --#WAREHOUSE.Queueitem local _item=queue[i] --#WAREHOUSE.Queueitem
+250 -87
View File
@@ -27,6 +27,8 @@
-- @field #number nflightsCAP Number of CAP flights constantly in the air. -- @field #number nflightsCAP Number of CAP flights constantly in the air.
-- @field #number nflightsTANKER Number of TANKER 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 #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 -- @extends Functional.Warehouse#WAREHOUSE
--- Be surprised! --- Be surprised!
@@ -49,6 +51,7 @@ AIRWING = {
missionqueue = {}, missionqueue = {},
payloads = {}, payloads = {},
cappoints = {}, cappoints = {},
wingcommander = nil,
} }
--- Squadron data. --- Squadron data.
@@ -366,6 +369,25 @@ function AIRWING:AddMission(Mission)
return self return self
end 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. --- Set CAP zones.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #number n Number of flights. Default 1. -- @param #number n Number of flights. Default 1.
@@ -375,11 +397,11 @@ function AIRWING:SetCAPflights(n)
return self return self
end end
--- Add a CAP zone. --- Add a patrol Point for CAP missions.
-- @param #AIRWING self -- @param #AIRWING self
-- @param Core.Zone#ZONE AcceptZone Add a zone to the CAP zone set. -- @param Core.Zone#ZONE AcceptZone Add a zone to the CAP zone set.
-- @return #AIRWING self -- @return #AIRWING self
function AIRWING:AddCAPPoint(Coordinate, Heading, Leg, Speed) function AIRWING:AddPatrolPointCAP(Coordinate, Heading, Leg, Speed)
local cappoint={} --#AIRWING.PatrolData local cappoint={} --#AIRWING.PatrolData
cappoint.coord=Coordinate cappoint.coord=Coordinate
@@ -430,6 +452,13 @@ function AIRWING:onafterStatus(From, Event, To)
local fsmstate=self:GetState() local fsmstate=self:GetState()
-- Check CAP missions.
self:CheckCAP()
self:CheckTANKER()
self:CheckAWACS()
------------------ ------------------
-- Mission Info -- -- Mission Info --
------------------ ------------------
@@ -487,6 +516,58 @@ function AIRWING:onafterStatus(From, Event, To)
end 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.noccuied<b.noccuied
end
if PatrolPoints then
-- Sort data wrt number of flights at that point.
table.sort(PatrolPoints, sort)
return PatrolPoints[1]
else
local point={} --#AIRWING.PatrolData
point.coord=self:GetCoordinate()
point.speed=math.random(250, 350)
point.heading=math.random(360)
point.occupied=false
end
end
--- Get next mission.
-- @param #AIRWING self
-- @return Ops.Auftrag#AUFTRAG Next mission or *nil*.
function AIRWING:_CheckCAP()
local Ncap=self:CountAssetsOnMission(AUFTRAG.Type.PATROL)
for i=1,self.nflightsCAP-Ncap do
local patrol=self:_GetPatrolData(self.cappoints)
local missionCAP=AUFTRAG:NewPATROL(patrol.coord, patrol.speed, patrol.heading, patrol.leg)
missionCAP.patroldata=patrol
self:AddMission(missionCAP)
end
end
--- Get next mission. --- Get next mission.
-- @param #AIRWING self -- @param #AIRWING self
-- @return Ops.Auftrag#AUFTRAG Next mission or *nil*. -- @return Ops.Auftrag#AUFTRAG Next mission or *nil*.
@@ -528,9 +609,13 @@ function AIRWING:_GetNextMission()
if can then if can then
-- Optimize the asset selection. Most useful assets will come first. -- Optimize the asset selection. Most useful assets will come first.
-- TODO: This could be moved to AUFTRAG, right?
--self:_OptimizeAssetSelection(assets, mission) --self:_OptimizeAssetSelection(assets, mission)
-- TODO: check that mission.assets table is clean. -- Check that mission.assets table is clean.
if mission.assets and #mission.assets>0 then
self:E(self.lid..string.format("ERROR: mission %s of type %s has already assets attached!", mission.name, mission.type))
end
mission.assets={} mission.assets={}
-- Assign assets to mission. -- Assign assets to mission.
@@ -544,7 +629,7 @@ function AIRWING:_GetNextMission()
self:E("No payload for asset! This should not happen!") self:E("No payload for asset! This should not happen!")
end end
table.insert(mission.assets, asset) mission:AddAsset(asset)
end end
return mission return mission
@@ -608,61 +693,6 @@ end
-- FSM Events -- 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. --- On after "MissionRequest" event. Performs a self request to the warehouse for the mission assets. Sets mission status to REQUESTED.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #string From From state. -- @param #string From From state.
@@ -713,6 +743,132 @@ function AIRWING:onafterMissionRequest(From, Event, To, Mission)
end 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. --- On after "Request" event.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #string From From state. -- @param #string From From state.
@@ -741,34 +897,6 @@ function AIRWING:onafterRequest(From, Event, To, Request)
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
end
--- On after "SelfRequest" event. --- On after "SelfRequest" event.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #string From From state. -- @param #string From From state.
@@ -972,6 +1100,41 @@ function AIRWING:GetAssetCurrentMission(asset)
end 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. --- Check if assets for a given mission type are available.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #string MissionType Type of mission. -- @param #string MissionType Type of mission.
+103 -7
View File
@@ -97,6 +97,7 @@ AUFTRAG = {
lid = nil, lid = nil,
auftragsnummer = nil, auftragsnummer = nil,
flightdata = {}, flightdata = {},
assets = {},
} }
--- Global mission counter. --- Global mission counter.
@@ -116,6 +117,7 @@ _AUFTRAGSNR=0
-- @param #string FERRY Ferry flight mission. -- @param #string FERRY Ferry flight mission.
-- @param #string INTERCEPT Intercept mission. -- @param #string INTERCEPT Intercept mission.
-- @param #string ORBIT Orbit mission. -- @param #string ORBIT Orbit mission.
-- @param #string PATROL Similar to CAP but no auto engage targets.
-- @param #string RECON Recon mission. -- @param #string RECON Recon mission.
-- @param #string SEAD Suppression/destruction of enemy air defences. -- @param #string SEAD Suppression/destruction of enemy air defences.
-- @param #string STRIKE Strike mission. -- @param #string STRIKE Strike mission.
@@ -133,6 +135,7 @@ AUFTRAG.Type={
FERRY="Ferry Flight", FERRY="Ferry Flight",
INTERCEPT="Intercept", INTERCEPT="Intercept",
ORBIT="Orbit", ORBIT="Orbit",
PATROL="Patrol",
RECON="Recon", RECON="Recon",
SEAD="SEAD", SEAD="SEAD",
STRIKE="Strike", STRIKE="Strike",
@@ -291,6 +294,29 @@ function AUFTRAG:NewORBIT(Coordinate, Speed, Heading, Leg)
return auftrag return auftrag
end 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. --- Create a CAP mission.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate. -- @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. --- Check if mission is done.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @return #boolean If true, mission is done. -- @return #boolean If true, mission is done.
function AUFTRAG:IsCancelled() function AUFTRAG:IsDone()
return self.status==AUFTRAG.Status.DONE return self.status==AUFTRAG.Status.DONE
end end
@@ -640,6 +666,16 @@ function AUFTRAG:onafterStatus(From, Event, To)
end end
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. --- Set flightgroup mission status.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group. -- @param Ops.FlightGroup#FLIGHTGROUP flightgroup The flight group.
@@ -735,7 +771,7 @@ end
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
function AUFTRAG:onafterRequested(From, Event, TO) function AUFTRAG:onafterRequested(From, Event, To)
self.status=AUFTRAG.Status.REQUESTED self.status=AUFTRAG.Status.REQUESTED
self:I(self.lid..string.format("New mission status=%s", self.status)) self:I(self.lid..string.format("New mission status=%s", self.status))
end end
@@ -802,7 +838,8 @@ function AUFTRAG:onafterCancel(From, Event, To)
self:I(self.lid..string.format("New mission status=%s", self.status)) self:I(self.lid..string.format("New mission status=%s", self.status))
if self.airwing then if self.airwing then
-- Airwing will cancel all flight missions and remove queued request from warehouse queue.
self.airwing:MissionCancel(self) self.airwing:MissionCancel(self)
else else
@@ -811,11 +848,12 @@ function AUFTRAG:onafterCancel(From, Event, To)
local flightdata=_flightdata --#AUFTRAG.FlightData local flightdata=_flightdata --#AUFTRAG.FlightData
flightdata.flightgroup:MissionCancel(self) flightdata.flightgroup:MissionCancel(self)
end end
end end
end end
--- On after "Stop" event. --- On after "Stop" event. Remove mission from AIRWING and FLIGHTGROUP mission queues.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
@@ -823,6 +861,19 @@ end
function AUFTRAG:onafterStop(From, Event, To) function AUFTRAG:onafterStop(From, Event, To)
-- TODO: remove missions from queues in WINGCOMMANDER, AIRWING and FLIGHGROUPS! -- 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() self.CallScheduler:Clear()
end end
@@ -831,15 +882,60 @@ end
-- Misc Functions -- 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. --- Count alive mission targets.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @return #number Number of alive targets. -- @return #number Number of alive target units.
function AUFTRAG:CountMissionTargets() function AUFTRAG:CountMissionTargets()
local N=0 local N=0
if self.engageTargetGroupset then if self.engageTargetGroupset then
local n=self.engageTargetGroupset:CountAlive() --local n=self.engageTargetGroupset:CountAlive()
N=N+n
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 end
if self.engageTargetUnitset then if self.engageTargetUnitset then
@@ -679,6 +679,25 @@ function FLIGHTGROUP:AddMission(Mission)
return self return self
end 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. --- Set AIRWING the flight group belongs to.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
-- @param Ops.AirWing#AIRWING airwing The AIRWING object. -- @param Ops.AirWing#AIRWING airwing The AIRWING object.
+32 -13
View File
@@ -71,10 +71,11 @@ INTEL.version="0.0.3"
-- ToDo list -- ToDo list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Accept and reject zones. -- DONE: Accept zones.
-- TODO: SetAttributeZone --> return groups of generalized attributes in a zone. -- TODO: Reject zones.
-- 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?! -- NOGO: SetAttributeZone --> return groups of generalized attributes in a zone.
-- TODO: Combine units to groups for all, new and lost. -- 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. -- TODO: process detected set asynchroniously for better performance.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -243,7 +244,7 @@ function INTEL:onafterStatus(From, Event, To)
local Ncontacts=#self.Contacts local Ncontacts=#self.Contacts
-- Short info. -- 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) self:I(self.lid..text)
-- Detailed info. -- Detailed info.
@@ -277,22 +278,39 @@ function INTEL:UpdateIntel()
-- Get set of detected units. -- Get set of detected units.
local detectedunitset=recce:GetDetectedUnitSet() local detectedunitset=recce:GetDetectedUnitSet()
-- Add detected units to all set. -- Add detected units to all set.
DetectedSet=DetectedSet:GetSetUnion(detectedunitset) DetectedSet=DetectedSet:GetSetUnion(detectedunitset)
end end
end end
end end
-- TODO: Filter units from accept/reject zones. -- TODO: Filter units from reject zones.
-- TODO: Filter unit types. -- TODO: Filter unit types.
-- TODO: Filter detection methods? -- TODO: Filter detection methods?
local remove={}
for _,_zone in pairs(self.acceptzoneset.Set) do for _,_unit in pairs(DetectedSet.Set) do
local zone=_zone --Core.Zone#ZONE 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 end
-- Remove filtered units.
DetectedSet:RemoveUnitsByName(remove)
-- Create detected contacts. -- Create detected contacts.
self:CreateDetectedItems(DetectedSet) self:CreateDetectedItems(DetectedSet)
@@ -305,6 +323,7 @@ function INTEL:CreateDetectedItems(detectedunitset)
local detectedgroupset=SET_GROUP:New() local detectedgroupset=SET_GROUP:New()
-- Convert detected UNIT set to detected GROUP set.
for _,_unit in pairs(detectedunitset:GetSet()) do for _,_unit in pairs(detectedunitset:GetSet()) do
local unit=_unit --Wrapper.Unit#UNIT local unit=_unit --Wrapper.Unit#UNIT
@@ -374,9 +393,9 @@ function INTEL:CreateDetectedItems(detectedunitset)
local group=detectedgroupset:FindGroup(item.groupname) 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 if self:CheckContactLost(item) then
-- Trigger LostContact event. -- Trigger LostContact event. This also adds the contact to the self.ContactsLost table.
self:LostContact(item) self:LostContact(item)
-- Remove contact from table. -- Remove contact from table.
self:RemoveContact(item) self:RemoveContact(item)
@@ -412,7 +431,7 @@ function INTEL:onafterLostContact(From, Event, To, Contact)
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Fuctions -- Misc Functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create detected items. --- Create detected items.
+26 -67
View File
@@ -1,4 +1,4 @@
--- **Ops** - (R2.5) - AI Squadron for Ops. --- **Ops** - Airwing Squadron.
-- --
-- **Main Features:** -- **Main Features:**
-- --
@@ -15,9 +15,9 @@
-- @type SQUADRON -- @type SQUADRON
-- @field #string ClassName Name of the class. -- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status. -- @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 #string livery Livery of the squadron.
-- @field #table flights Table of flight groups. -- @field #table assets Table of assets.
-- @extends Core.Fsm#FSM -- @extends Core.Fsm#FSM
--- Be surprised! --- Be surprised!
@@ -34,10 +34,13 @@
SQUADRON = { SQUADRON = {
ClassName = "SQUADRON", ClassName = "SQUADRON",
Debug = nil, Debug = nil,
sid = nil, lid = nil,
flights = {}, name = nil,
tasks = {}, templatename = nil,
livery = nil, assets = {},
livery = nil,
skill = nil,
airwing = nil,
} }
--- Flight group element. --- Flight group element.
@@ -64,13 +67,13 @@ SQUADRON.version="0.0.1"
-- @param #string SquadName Name of the squadron, e.g. "VFA-37". -- @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}`. -- @param #table tasks Table of squadron tasks, e.g. `{SQUADRON.Task.INTERCEPT, SQUADRON.Task.SEAD}`.
-- @return #SQUADRON self -- @return #SQUADRON self
function SQUADRON:New(squadronname) function SQUADRON:New(SquadronName, TemplateGroupName)
-- Inherit everything from FSM class. -- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #SQUADRON local self=BASE:Inherit(self, FSM:New()) -- #SQUADRON
--self.flightgroup=AIGroup --self.flightgroup=AIGroup
self.squadronname=tostring(squadronname) self.name=tostring(SquadronName)
-- Set home airbase. -- Set home airbase.
self.homebase=airbase self.homebase=airbase
@@ -90,7 +93,6 @@ function SQUADRON:New(squadronname)
-- Add FSM transitions. -- Add FSM transitions.
-- From State --> Event --> To State -- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start FSM. self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
self:AddTransition("*", "Status", "*") -- SQUADRON status update self:AddTransition("*", "Status", "*") -- SQUADRON status update
@@ -150,57 +152,15 @@ function SQUADRON:SetLivery(liveryname)
self.livery=liveryname self.livery=liveryname
end end
--- Set home airbase. --- Set airwing.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param Wrapper.Airbase#AIRBASE airbase Home airbase object of the squadron. -- @param Ops.AirWing#AIRWING Airwing The airwing.
-- @return #SQUADRON self -- @return #SQUADRON self
function SQUADRON:SetHomeBase(airbase) function SQUADRON:SetAirwing(Airwing)
self.homebase=airbase self.airwing=Airwing
end 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 -- Start & Status
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -214,41 +174,40 @@ end
function SQUADRON:onafterStart(From, Event, To) function SQUADRON:onafterStart(From, Event, To)
-- Short info. -- Short info.
local text=string.format("Starting flight group %s.", self.groupname) local text=string.format("Starting SQUADRON %s.", self.name)
self:I(self.sid..text) self:I(self.lid..text)
-- Start the status monitoring. -- Start the status monitoring.
self:__SquadronStatus(-1) self:__Status(-1)
end end
--- On after "FlightStatus" event. --- On after "Status" event.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param Wrapper.Group#GROUP Group Flight group. -- @param Wrapper.Group#GROUP Group Flight group.
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
-- @param #string To To state. -- @param #string To To state.
function SQUADRON:onafterSquadronStatus(From, Event, To) function SQUADRON:onafterStatus(From, Event, To)
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
-- Check if group has detected any units. -- Check if group has detected any units.
self:_CheckFlightStatus() self:_CheckAssetStatus()
-- Short info. -- 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()) 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) self:I(self.sid..text)
end end
--- On after "FlightStatus" event. --- Check asset status.
-- @param #SQUADRON self -- @param #SQUADRON self
function SQUADRON:_CheckFlightstatus() function SQUADRON:_CheckAssetStatus()
for _,_flight in pairs(self.flights) do for _,_assets in pairs(self.assets) do
local flight=_flight --#SQUADRON.Flight local asset=_asset --#SQUADRON.Flight
flight.flightgroup:IsSpawned() flight.flightgroup:IsSpawned()
+98 -59
View File
@@ -54,14 +54,17 @@ WINGCOMMANDER = {
--- WINGCOMMANDER class version. --- WINGCOMMANDER class version.
-- @field #string version -- @field #string version
WINGCOMMANDER.version="0.0.2" WINGCOMMANDER.version="0.0.3"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add tasks. -- DONE: Add/remove spawned flightgroups to detection set.
-- TODO: -- 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 -- Constructor
@@ -144,6 +147,35 @@ function WINGCOMMANDER:AddAirwing(Airwing)
return self return self
end 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 -- Start & Status
@@ -174,7 +206,7 @@ function WINGCOMMANDER:onafterStart(From, Event, To)
end end
--- On after "Sitrep" event. --- On after "Status" event.
-- @param #WINGCOMMANDER self -- @param #WINGCOMMANDER self
-- @param Wrapper.Group#GROUP Group Flight group. -- @param Wrapper.Group#GROUP Group Flight group.
-- @param #string From From state. -- @param #string From From state.
@@ -193,7 +225,11 @@ function WINGCOMMANDER:onafterStatus(From, Event, To)
for _,_contact in pairs(self.ContactsLost) do for _,_contact in pairs(self.ContactsLost) do
local contact=_contact --#WINGCOMMANDER.Contact 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. -- Cancel this mission.
contact.mission:Cancel() contact.mission:Cancel()
@@ -248,7 +284,7 @@ function WINGCOMMANDER:onafterStatus(From, Event, To)
-- Add mission to queue. -- Add mission to queue.
if mission then if mission then
mission.nassets=1 mission.nassets=1
table.insert(self.missionqueue, mission) self:AddMission(mission)
end end
end end
@@ -296,48 +332,10 @@ function WINGCOMMANDER:CheckMissionQueue()
-- PLANNNED Mission -- PLANNNED Mission
--- ---
-- Table of airwings that can do the mission. local airwing=self:GetAirwingForMission(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. if airwing then
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<b.dist
end
table.sort(airwings, sortdist)
-- This is the closest airwing to the target.
local airwing=airwings[1].airwing --Ops.AirWing#AIRWING
-- Add mission to airwing. -- Add mission to airwing.
self:MissionAssign(airwing, mission) self:MissionAssign(airwing, mission)
@@ -356,6 +354,59 @@ function WINGCOMMANDER:CheckMissionQueue()
end end
--- Check all airwings if they are able to do a specific mission type at a certain location with a given number of assets.
-- @param #WINGCOMMANDER self
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- @return Ops.AirWing#AIRWING The airwing best for this mission.
function WINGCOMMANDER:GetAirwingForMission(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<b.dist
end
table.sort(airwings, sortdist)
-- This is the closest airwing to the target.
local airwing=airwings[1].airwing --Ops.AirWing#AIRWING
return airwing
end
return nil
end
--- Check resources. --- Check resources.
-- @param #WINGCOMMANDER self -- @param #WINGCOMMANDER self
@@ -380,16 +431,4 @@ function WINGCOMMANDER:CheckResources()
end end
return capabilities return capabilities
end end
--- Check all airwings if they are able to do a specific mission type at a certain location with a given number of assets.
-- @param #WINGCOMMANDER self
-- @return Ops.AirWing#AIRWING The airwing object best for this mission.
function WINGCOMMANDER:GetAirwingForMission(MissionType, Coordinate, Nassets)
--TODO: run over all airwings. sort by distance and available assets.
end