- Lots of stuff
This commit is contained in:
Frank 2021-08-21 00:58:28 +02:00
parent 16964520df
commit d73ebaca76
10 changed files with 731 additions and 301 deletions

View File

@ -156,7 +156,7 @@ function ARMYGROUP:New(group)
--self:HandleEvent(EVENTS.Hit, self.OnEventHit) --self:HandleEvent(EVENTS.Hit, self.OnEventHit)
-- Start the status monitoring. -- Start the status monitoring.
self:__Status(-1) self.timerStatus=TIMER:New(self.Status, self):Start(1, 30)
-- Start queue update timer. -- Start queue update timer.
self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5) self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5)
@ -343,30 +343,17 @@ end
-- Status -- Status
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- Update status.
-- @param #ARMYGROUP self
function ARMYGROUP:onbeforeStatus(From, Event, To)
if self:IsDead() then
self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false
elseif self:IsStopped() then
self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false
end
return true
end
--- Update status. --- Update status.
-- @param #ARMYGROUP self -- @param #ARMYGROUP self
function ARMYGROUP:onafterStatus(From, Event, To) function ARMYGROUP:Status()
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
local alive=self:IsAlive() local alive=self:IsAlive()
env.info(self.lid.."FF status="..fsmstate)
if alive then if alive then
--- ---
@ -490,9 +477,6 @@ function ARMYGROUP:onafterStatus(From, Event, To)
self:_PrintTaskAndMissionStatus() self:_PrintTaskAndMissionStatus()
-- Next status update.
self:__Status(-30)
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -596,9 +580,6 @@ function ARMYGROUP:onafterSpawned(From, Event, To)
self:FullStop() self:FullStop()
end end
-- Update status.
self:__Status(-0.1)
end end
end end
@ -652,7 +633,7 @@ function ARMYGROUP:onafterUpdateRoute(From, Event, To, n, Speed, Formation)
wp.speed=UTILS.KnotsToMps(Speed) wp.speed=UTILS.KnotsToMps(Speed)
else else
-- Take default waypoint speed. But make sure speed>0 if patrol ad infinitum. -- Take default waypoint speed. But make sure speed>0 if patrol ad infinitum.
if self.adinfinitum and wp.speed<0.1 then if wp.speed<0.1 then --self.adinfinitum and
wp.speed=UTILS.KmphToMps(self.speedCruise) wp.speed=UTILS.KmphToMps(self.speedCruise)
end end
end end
@ -1156,7 +1137,7 @@ function ARMYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Formation
-- Check if final waypoint is still passed. -- Check if final waypoint is still passed.
if wpnumber>self.currentwp then if wpnumber>self.currentwp then
self.passedfinalwp=false self:_PassedFinalWaypoint(false, "ARMYGROUP.AddWaypoint: wpnumber>self.currentwp")
end end
-- Speed in knots. -- Speed in knots.

View File

@ -90,6 +90,7 @@
-- @field Core.Set#SET_GROUP transportGroupSet Groups to be transported. -- @field Core.Set#SET_GROUP transportGroupSet Groups to be transported.
-- @field Core.Point#COORDINATE transportPickup Coordinate where to pickup the cargo. -- @field Core.Point#COORDINATE transportPickup Coordinate where to pickup the cargo.
-- @field Core.Point#COORDINATE transportDropoff Coordinate where to drop off the cargo. -- @field Core.Point#COORDINATE transportDropoff Coordinate where to drop off the cargo.
-- @field Ops.OpsTransport#OPSTRANSPORT opstransport OPS transport assignment.
-- --
-- @field #number artyRadius Radius in meters. -- @field #number artyRadius Radius in meters.
-- @field #number artyShots Number of shots fired. -- @field #number artyShots Number of shots fired.
@ -328,6 +329,7 @@ _AUFTRAGSNR=0
-- @field #string TROOPTRANSPORT Troop transport mission. -- @field #string TROOPTRANSPORT Troop transport mission.
-- @field #string ARTY Fire at point. -- @field #string ARTY Fire at point.
-- @field #string PATROLZONE Patrol a zone. -- @field #string PATROLZONE Patrol a zone.
-- @field #string OPSTRANSPORT Ops transport.
AUFTRAG.Type={ AUFTRAG.Type={
ANTISHIP="Anti Ship", ANTISHIP="Anti Ship",
AWACS="AWACS", AWACS="AWACS",
@ -352,6 +354,7 @@ AUFTRAG.Type={
TROOPTRANSPORT="Troop Transport", TROOPTRANSPORT="Troop Transport",
ARTY="Fire At Point", ARTY="Fire At Point",
PATROLZONE="Patrol Zone", PATROLZONE="Patrol Zone",
OPSTRANSPORT="Ops Transport",
} }
--- Mission status. --- Mission status.
@ -1171,6 +1174,48 @@ function AUFTRAG:NewTROOPTRANSPORT(TransportGroupSet, DropoffCoordinate, PickupC
return mission return mission
end end
--- Create a OPS TRANSPORT mission.
-- @param #AUFTRAG self
-- @param Core.Set#SET_GROUP CargoGroupSet The set group(s) to be transported.
-- @param Core.Zone#ZONE PickupZone Pick up zone
-- @param Core.Zone#ZONE DeployZone Deploy zone
-- @return #AUFTRAG self
function AUFTRAG:NewOPSTRANSPORT(CargoGroupSet, PickupZone, DeployZone)
local mission=AUFTRAG:New(AUFTRAG.Type.OPSTRANSPORT)
mission.transportGroupSet=CargoGroupSet
mission:_TargetFromObject(mission.transportGroupSet)
--mission.transportPickup=PickupCoordinate or mission:GetTargetCoordinate()
--mission.transportDropoff=DropoffCoordinate
-- Debug.
--mission.transportPickup:MarkToAll("Pickup")
--mission.transportDropoff:MarkToAll("Drop off")
mission.opstransport=OPSTRANSPORT:New(CargoGroupSet, PickupZone, DeployZone)
function mission.opstransport:OnAfterExecuting(From, Event, To)
mission:Executing()
end
function mission.opstransport:OnAfterDelivered(From, Event, To)
mission:Done()
end
-- TODO: what's the best ROE here?
mission.optionROE=ENUMS.ROE.ReturnFire
mission.optionROT=ENUMS.ROT.PassiveDefense
mission.DCStask=mission:GetDCSMissionTask()
return mission
end
--- Create an ARTY mission. --- Create an ARTY mission.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Target Center of the firing solution. -- @param Core.Point#COORDINATE Target Center of the firing solution.
@ -1211,6 +1256,11 @@ function AUFTRAG:NewPATROLZONE(Zone, Speed, Altitude)
local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE) local mission=AUFTRAG:New(AUFTRAG.Type.PATROLZONE)
-- Ensure we got a ZONE and not just the zone name.
if type(Zone)=="string" then
Zone=ZONE:New(Zone)
end
mission:_TargetFromObject(Zone) mission:_TargetFromObject(Zone)
mission.optionROE=ENUMS.ROE.OpenFire mission.optionROE=ENUMS.ROE.OpenFire
@ -1541,14 +1591,14 @@ end
--- Get number of required assets. --- Get number of required assets.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param Ops.Legion#Legion Legion (Optional) Only get the required assets for a specific legion. -- @param Ops.Legion#Legion Legion (Optional) Only get the required assets for a specific legion. If required assets for this legion are not defined, the total number is returned.
-- @param #number Number of required assets. -- @return #number Number of required assets.
function AUFTRAG:GetRequiredAssets(Legion) function AUFTRAG:GetRequiredAssets(Legion)
local N=self.nassets local N=self.nassets
if Legion then if Legion and self.Nassets[Legion.alias] then
N=self.Nassets[Legion.alias] or 0 N=self.Nassets[Legion.alias]
end end
return N return N
@ -1669,6 +1719,43 @@ function AUFTRAG:SetMissionRange(Range)
return self return self
end end
--- Attach OPS transport to the mission. Mission assets will be transported before the mission is started at the OPSGROUP level.
-- @param #AUFTRAG self
-- @param Ops.OpsTransport#OPSTRANSPORT OpsTransport The OPS transport assignment attached to the mission.
-- @return #AUFTRAG self
function AUFTRAG:SetOpsTransport(OpsTransport)
self.opstransport=OpsTransport
return self
end
--- Attach OPS transport to the mission. Mission assets will be transported before the mission is started at the OPSGROUP level.
-- @param #AUFTRAG self
-- @param Core.Zone#ZONE PickupZone Zone where assets are picked up.
-- @param Core.Zone#ZONE DeployZone Zone where assets are deployed.
-- @param Core.Set#SET_OPSGROUP Carriers Set of carriers. Can also be a single group. Can also be added via the AddTransportCarriers functions.
-- @return #AUFTRAG self
function AUFTRAG:SetTransportForAssets(PickupZone, DeployZone, Carriers)
-- OPS transport from pickup to deploy zone.
self.opstransport=OPSTRANSPORT:New(nil, PickupZone, DeployZone)
if Carriers then
if Carriers:IsInstanceOf("SET_OPSGROUP") then
for _,_carrier in pairs(Carriers.Set) do
local carrier=_carrier --Ops.OpsGroup#OPSGROUP
carrier:AddOpsTransport(self.opstransport)
end
elseif Carriers:IsInstanceOf("OPSGROUP") then
Carriers:AddOpsTransport(self.opstransport)
end
end
return self
end
--- Set Rules of Engagement (ROE) for this mission. --- Set Rules of Engagement (ROE) for this mission.
-- @param #AUFTRAG self -- @param #AUFTRAG self
-- @param #string roe Mission ROE. -- @param #string roe Mission ROE.
@ -1959,6 +2046,11 @@ function AUFTRAG:AddOpsGroup(OpsGroup)
groupdata.waypointtask=nil groupdata.waypointtask=nil
self.groupdata[OpsGroup.groupname]=groupdata self.groupdata[OpsGroup.groupname]=groupdata
-- Add ops transport to new group.
if self.opstransport then
self.opstransport:AddCargoGroups(OpsGroup)
end
return self return self
end end
@ -2237,7 +2329,7 @@ function AUFTRAG:onafterStatus(From, Event, To)
elseif (self.Tstop and Tnow>self.Tstop+10) or (Ntargets0>0 and Ntargets==0) then elseif (self.Tstop and Tnow>self.Tstop+10) or (Ntargets0>0 and Ntargets==0) then
-- Cancel mission if stop time passed. -- Cancel mission if stop time passed.
self:Cancel() --self:Cancel()
end end
@ -3763,6 +3855,23 @@ function AUFTRAG:GetDCSMissionTask(TaskControllable)
table.insert(DCStasks, TaskEmbark) table.insert(DCStasks, TaskEmbark)
table.insert(DCStasks, TaskDisEmbark) table.insert(DCStasks, TaskDisEmbark)
elseif self.type==AUFTRAG.Type.OPSTRANSPORT then
--------------------------
-- OPSTRANSPORT Mission --
--------------------------
local DCStask={}
DCStask.id="OpsTransport"
-- We create a "fake" DCS task and pass the parameters to the FLIGHTGROUP.
local param={}
DCStask.params=param
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.RESCUEHELO then elseif self.type==AUFTRAG.Type.RESCUEHELO then
------------------------- -------------------------

View File

@ -243,6 +243,21 @@ function BRIGADE:onafterStatus(From, Event, To)
self:I(self.lid..text) self:I(self.lid..text)
end end
----------------
-- Transport ---
----------------
-- Check if any transports should be cancelled.
--self:_CheckTransports()
-- Get next mission.
local transport=self:_GetNextTransport()
-- Request mission execution.
if transport then
self:TransportRequest(transport)
end
-------------- --------------
-- Mission --- -- Mission ---
-------------- --------------

View File

@ -842,10 +842,21 @@ function COHORT:RecruitAssets(Mission, Npayloads)
end end
-- Check if in a state where we really do not want to fight any more. -- Check if in a state where we really do not want to fight any more.
if flightgroup:IsHolding() or flightgroup:IsLanding() or flightgroup:IsLanded() or flightgroup:IsArrived() or flightgroup:IsDead() or flightgroup:IsStopped() then if flightgroup:IsFlightgroup() then
if flightgroup:IsHolding() or flightgroup:IsLanding() or flightgroup:IsLanded() or flightgroup:IsArrived() then
combatready=false
end
else
if flightgroup:IsRearming() or flightgroup:IsRetreating() or flightgroup:IsReturning() then
combatready=false
end
end
-- Applies to all opsgroups.
if flightgroup:IsDead() or flightgroup:IsStopped() then
combatready=false combatready=false
end end
--TODO: Check transport for combat readyness! --TODO: Check transport for combat readyness!
-- This asset is "combatready". -- This asset is "combatready".

View File

@ -1,15 +1,15 @@
--- **Ops** - Commander of an Airwing, Brigade or Flotilla. --- **Ops** - Commander of Airwings, Brigades and Flotillas.
-- --
-- **Main Features:** -- **Main Features:**
-- --
-- * Manages AIRWINGS, BRIGADEs and FLOTILLAs -- * Manages AIRWINGS, BRIGADEs and FLOTILLAs
-- * Handles missions (AUFTRAG) and finds the best airwing for the job -- * Handles missions (AUFTRAG) and finds the best man for the job
-- --
-- === -- ===
-- --
-- ### Author: **funkyfranky** -- ### Author: **funkyfranky**
-- @module Ops.WingCommander -- @module Ops.Commander
-- @image OPS_WingCommander.png -- @image OPS_Commander.png
--- COMMANDER class. --- COMMANDER class.
@ -28,7 +28,7 @@
-- --
-- # The COMMANDER Concept -- # The COMMANDER Concept
-- --
-- A wing commander is the head of legions. He will find the best AIRWING to perform an assigned AUFTRAG (mission). -- A commander is the head of legions. He will find the best LEGIONs to perform an assigned AUFTRAG (mission).
-- --
-- --
-- @field #COMMANDER -- @field #COMMANDER
@ -36,7 +36,7 @@ COMMANDER = {
ClassName = "COMMANDER", ClassName = "COMMANDER",
Debug = nil, Debug = nil,
lid = nil, lid = nil,
legions = {}, legions = {},
missionqueue = {}, missionqueue = {},
} }
@ -48,7 +48,8 @@ COMMANDER.version="0.1.0"
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Improve airwing selection. Mostly done! -- TODO: Improve legion selection. Mostly done!
-- TODO: Allow multiple Legions for one mission.
-- NOGO: Maybe it's possible to preselect the assets for the mission. -- NOGO: Maybe it's possible to preselect the assets for the mission.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -145,16 +146,29 @@ end
-- User functions -- User functions
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add an airwing to the wingcommander. --- Add an AIRWING to the commander.
-- @param #COMMANDER self -- @param #COMMANDER self
-- @param Ops.AirWing#AIRWING Airwing The airwing to add. -- @param Ops.AirWing#AIRWING Airwing The airwing to add.
-- @return #COMMANDER self -- @return #COMMANDER self
function COMMANDER:AddAirwing(Airwing) function COMMANDER:AddAirwing(Airwing)
-- This airwing is managed by this wing commander. -- Add legion.
Airwing.commander=self self:AddLegion(Airwing)
return self
end
table.insert(self.legions, Airwing) --- Add a LEGION to the commander.
-- @param #COMMANDER self
-- @param Ops.Legion#LEGION Legion The legion to add.
-- @return #COMMANDER self
function COMMANDER:AddLegion(Legion)
-- This legion is managed by the commander.
Legion.commander=self
-- Add to legions.
table.insert(self.legions, Legion)
return self return self
end end
@ -237,21 +251,21 @@ function COMMANDER:onafterStatus(From, Event, To)
self:CheckMissionQueue() self:CheckMissionQueue()
-- Status. -- Status.
local text=string.format("Status %s: Airwings=%d, Missions=%d", fsmstate, #self.legions, #self.missionqueue) local text=string.format("Status %s: Legions=%d, Missions=%d", fsmstate, #self.legions, #self.missionqueue)
self:I(self.lid..text) self:I(self.lid..text)
-- Airwing Info -- Legion info.
if #self.legions>0 then if #self.legions>0 then
local text="Airwings:" local text="Legions:"
for _,_airwing in pairs(self.legions) do for _,_legion in pairs(self.legions) do
local airwing=_airwing --Ops.AirWing#AIRWING local legion=_legion --Ops.Legion#LEGION
local Nassets=airwing:CountAssets() local Nassets=legion:CountAssets()
local Nastock=airwing:CountAssets(true) local Nastock=legion:CountAssets(true)
text=text..string.format("\n* %s [%s]: Assets=%s stock=%s", airwing.alias, airwing:GetState(), Nassets, Nastock) text=text..string.format("\n* %s [%s]: Assets=%s stock=%s", legion.alias, legion:GetState(), Nassets, Nastock)
for _,aname in pairs(AUFTRAG.Type) do for _,aname in pairs(AUFTRAG.Type) do
local na=airwing:CountAssets(true, {aname}) local na=legion:CountAssets(true, {aname})
local np=airwing:CountPayloadsInStock({aname}) local np=legion:CountPayloadsInStock({aname})
local nm=airwing:CountAssetsOnMission({aname}) local nm=legion:CountAssetsOnMission({aname})
if na>0 or np>0 then if na>0 or np>0 then
text=text..string.format("\n - %s: assets=%d, payloads=%d, on mission=%d", aname, na, np, nm) text=text..string.format("\n - %s: assets=%d, payloads=%d, on mission=%d", aname, na, np, nm)
end end
@ -353,23 +367,26 @@ function COMMANDER:CheckMissionQueue()
local mission=_mission --Ops.Auftrag#AUFTRAG local mission=_mission --Ops.Auftrag#AUFTRAG
-- We look for PLANNED missions. -- We look for PLANNED missions.
if mission.status==AUFTRAG.Status.PLANNED then if mission:IsPlanned() then
--- ---
-- PLANNNED Mission -- PLANNNED Mission
--- ---
local airwings=self:GetLegionsForMission(mission) -- Get legions for mission.
local legions=self:GetLegionsForMission(mission)
if airwings then if legions then
for _,airwing in pairs(airwings) do for _,_legion in pairs(legions) do
local legion=_legion --Ops.Legion#LEGION
-- Add mission to airwing. -- Add mission to legion.
self:MissionAssign(airwing, mission) self:MissionAssign(legion, mission)
end end
-- Only ONE mission is assigned.
return return
end end
@ -395,31 +412,36 @@ function COMMANDER:GetLegionsForMission(Mission)
local legions={} local legions={}
-- Loop over all legions. -- Loop over all legions.
for _,_airwing in pairs(self.legions) do for _,_legion in pairs(self.legions) do
local airwing=_airwing --Ops.AirWing#AIRWING local legion=_legion --Ops.Legion#LEGION
-- Check if airwing can do this mission. -- Count number of assets in stock.
local can,assets=airwing:CanMission(Mission) local Nassets=0
if legion:IsAirwing() then
Nassets=legion:CountAssetsWithPayloadsInStock(Mission.payloads, {Mission.type}, Attributes)
else
Nassets=legion:CountAssets(true, {Mission.type}, Attributes) --Could also specify the attribute if Air or Ground mission.
end
-- Has it assets that can? -- Has it assets that can?
if #assets>0 then if Nassets>0 then
-- Get coordinate of the target. -- Get coordinate of the target.
local coord=Mission:GetTargetCoordinate() local coord=Mission:GetTargetCoordinate()
if coord then if coord then
-- Distance from airwing to target. -- Distance from legion to target.
local distance=UTILS.MetersToNM(coord:Get2DDistance(airwing:GetCoordinate())) local distance=UTILS.MetersToNM(coord:Get2DDistance(legion:GetCoordinate()))
-- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6 -- Round: 55 NM ==> 5.5 ==> 6, 63 NM ==> 6.3 ==> 6
local dist=UTILS.Round(distance/10, 0) local dist=UTILS.Round(distance/10, 0)
-- Debug info. -- Debug info.
self:I(self.lid..string.format("Got legion %s with Nassets=%d and dist=%.1f NM, rounded=%.1f", airwing.alias, #assets, distance, dist)) self:I(self.lid..string.format("Got legion %s with Nassets=%d and dist=%.1f NM, rounded=%.1f", legion.alias, Nassets, distance, dist))
-- Add airwing to table of legions that can. -- Add legion to table of legions that can.
table.insert(legions, {airwing=airwing, distance=distance, dist=dist, targetcoord=coord, nassets=#assets}) table.insert(legions, {airwing=legion, distance=distance, dist=dist, targetcoord=coord, nassets=Nassets})
end end
@ -431,8 +453,8 @@ function COMMANDER:GetLegionsForMission(Mission)
if #legions>0 then if #legions>0 then
--- Something like: --- Something like:
-- * Closest airwing that can should be first prio. -- * Closest legion that can should be first prio.
-- * However, there should be a certain "quantization". if wing is 50 or 60 NM way should not really matter. In that case, the airwing with more resources should get the job. -- * However, there should be a certain "quantization". if wing is 50 or 60 NM way should not really matter. In that case, the legion with more resources should get the job.
local function score(a) local function score(a)
local d=math.round(a.dist/10) local d=math.round(a.dist/10)
end end
@ -440,7 +462,7 @@ function COMMANDER:GetLegionsForMission(Mission)
env.info(self.lid.."FF #legions="..#legions) env.info(self.lid.."FF #legions="..#legions)
-- Sort table wrt distance and number of assets. -- Sort table wrt distance and number of assets.
-- Distances within 10 NM are equal and the airwing with more assets is preferred. -- Distances within 10 NM are equal and the legion with more assets is preferred.
local function sortdist(a,b) local function sortdist(a,b)
local ad=a.dist local ad=a.dist
local bd=b.dist local bd=b.dist
@ -471,7 +493,7 @@ function COMMANDER:GetLegionsForMission(Mission)
self:I(self.lid..string.format("Found %d legions that can do mission %s (%s) requiring %d assets", #selection, Mission:GetName(), Mission:GetType(), Mission.nassets)) self:I(self.lid..string.format("Found %d legions that can do mission %s (%s) requiring %d assets", #selection, Mission:GetName(), Mission:GetType(), Mission.nassets))
return selection return selection
else else
self:T(self.lid..string.format("Not enough LEGIONs found that could do the job :/")) self:T(self.lid..string.format("Not enough LEGIONs found that could do the job :/ Number of assets avail %d < %d required for the mission", N, Mission.nassets))
return nil return nil
end end
@ -482,17 +504,18 @@ function COMMANDER:GetLegionsForMission(Mission)
return nil return nil
end end
--- Check mission queue and assign ONE planned mission. --- Count assets of all assigned legions.
-- @param #COMMANDER self -- @param #COMMANDER self
-- @param #boolean InStock If true, only assets that are in the warehouse stock/inventory are counted. -- @param #boolean InStock If true, only assets that are in the warehouse stock/inventory are counted.
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`. -- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`.
-- @return #number Amount of asset groups in stock. -- @return #number Amount of asset groups.
function COMMANDER:CountAssets(InStock, MissionTypes, Attributes) function COMMANDER:CountAssets(InStock, MissionTypes, Attributes)
local N=0 local N=0
for _,_airwing in pairs(self.legions) do for _,_legion in pairs(self.legions) do
local airwing=_airwing --Ops.AirWing#AIRWING local legion=_legion --Ops.Legion#LEGION
N=N+airwing:CountAssets(InStock, MissionTypes, Attributes) N=N+legion:CountAssets(InStock, MissionTypes, Attributes)
end end
return N return N

View File

@ -230,13 +230,13 @@ function FLIGHTGROUP:New(group)
-- Add FSM transitions. -- Add FSM transitions.
-- From State --> Event --> To State -- From State --> Event --> To State
self:AddTransition("*", "LandAtAirbase", "Inbound") -- Helo group is ordered to land at a specific point. self:AddTransition("*", "LandAtAirbase", "Inbound") -- Group is ordered to land at an airbase.
self:AddTransition("*", "RTB", "Inbound") -- Group is returning to destination base. self:AddTransition("*", "RTB", "Inbound") -- Group is returning to (home/destination) airbase.
self:AddTransition("*", "RTZ", "Inbound") -- Group is returning to destination zone. Not implemented yet! self:AddTransition("*", "RTZ", "Inbound") -- Group is returning to destination zone. Not implemented yet!
self:AddTransition("Inbound", "Holding", "Holding") -- Group is in holding pattern. self:AddTransition("Inbound", "Holding", "Holding") -- Group is in holding pattern.
self:AddTransition("*", "Refuel", "Going4Fuel") -- Group is send to refuel at a tanker. self:AddTransition("*", "Refuel", "Going4Fuel") -- Group is send to refuel at a tanker.
self:AddTransition("Going4Fuel", "Refueled", "Airborne") -- Group finished refueling. self:AddTransition("Going4Fuel", "Refueled", "Cruising") -- Group finished refueling.
self:AddTransition("*", "LandAt", "LandingAt") -- Helo group is ordered to land at a specific point. self:AddTransition("*", "LandAt", "LandingAt") -- Helo group is ordered to land at a specific point.
self:AddTransition("LandingAt", "LandedAt", "LandedAt") -- Helo group landed landed at a specific point. self:AddTransition("LandingAt", "LandedAt", "LandedAt") -- Helo group landed landed at a specific point.
@ -244,12 +244,8 @@ function FLIGHTGROUP:New(group)
self:AddTransition("*", "FuelLow", "*") -- Fuel state of group is low. Default ~25%. self:AddTransition("*", "FuelLow", "*") -- Fuel state of group is low. Default ~25%.
self:AddTransition("*", "FuelCritical", "*") -- Fuel state of group is critical. Default ~10%. self:AddTransition("*", "FuelCritical", "*") -- Fuel state of group is critical. Default ~10%.
self:AddTransition("*", "OutOfMissilesAA", "*") -- Group is out of A2A (air) missiles. self:AddTransition("Cruising", "EngageTarget", "Engaging") -- Engage targets.
self:AddTransition("*", "OutOfMissilesAG", "*") -- Group is out of A2G (ground) missiles. self:AddTransition("Engaging", "Disengage", "Cruising") -- Engagement over.
self:AddTransition("*", "OutOfMissilesAS", "*") -- Group is out of A2S (ship) missiles.
self:AddTransition("Airborne", "EngageTarget", "Engaging") -- Engage targets.
self:AddTransition("Engaging", "Disengage", "Airborne") -- Engagement over.
self:AddTransition("*", "ElementParking", "*") -- An element is parking. self:AddTransition("*", "ElementParking", "*") -- An element is parking.
self:AddTransition("*", "ElementEngineOn", "*") -- An element spooled up the engines. self:AddTransition("*", "ElementEngineOn", "*") -- An element spooled up the engines.
@ -305,7 +301,7 @@ function FLIGHTGROUP:New(group)
self:_InitGroup() self:_InitGroup()
-- Start the status monitoring. -- Start the status monitoring.
self:__Status(-1) self.timerStatus=TIMER:New(self.Status, self):Start(1, 30)
-- Start queue update timer. -- Start queue update timer.
self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5) self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5)
@ -662,6 +658,15 @@ function FLIGHTGROUP:IsFuelCritical()
return self.fuelcritical return self.fuelcritical
end end
--- Check if flight is good on fuel (not below low or even critical state).
-- @param #FLIGHTGROUP self
-- @return #boolean If true, flight is good on fuel.
function FLIGHTGROUP:IsFuelGood()
local isgood=not (self.fuellow or self.fuelcritical)
return isgood
end
--- Check if flight can do air-to-ground tasks. --- Check if flight can do air-to-ground tasks.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
-- @param #boolean ExcludeGuns If true, exclude gun -- @param #boolean ExcludeGuns If true, exclude gun
@ -830,15 +835,14 @@ function FLIGHTGROUP:onbeforeStatus(From, Event, To)
return true return true
end end
--- On after "Status" event. --- Status update.
-- @param #FLIGHTGROUP self -- @param #FLIGHTGROUP self
-- @param #string From From state. function FLIGHTGROUP:Status()
-- @param #string Event Event.
-- @param #string To To state.
function FLIGHTGROUP:onafterStatus(From, Event, To)
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
env.info(self.lid.."FF status="..fsmstate)
-- Update position. -- Update position.
self:_UpdatePosition() self:_UpdatePosition()
@ -894,8 +898,8 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
local fc=self.flightcontrol and self.flightcontrol.airbasename or "N/A" local fc=self.flightcontrol and self.flightcontrol.airbasename or "N/A"
local curr=self.currbase and self.currbase:GetName() or "N/A" local curr=self.currbase and self.currbase:GetName() or "N/A"
local text=string.format("Status %s [%d/%d]: Tasks=%d, Missions=%s, Waypoint=%d/%d, Detected=%d, Home=%s, Destination=%s, Current=%s, FC=%s", local text=string.format("Status %s [%d/%d]: Tasks=%d, Missions=%s, Waypoint=%d/%d [%s], Detected=%d, Home=%s, Destination=%s, Current=%s, FC=%s",
fsmstate, #self.elements, #self.elements, nTaskTot, nMissions, self.currentwp or 0, self.waypoints and #self.waypoints or 0, fsmstate, #self.elements, #self.elements, nTaskTot, nMissions, self.currentwp or 0, self.waypoints and #self.waypoints or 0, tostring(self.passedfinalwp),
self.detectedunits:Count(), home, dest, curr, fc) self.detectedunits:Count(), home, dest, curr, fc)
self:I(self.lid..text) self:I(self.lid..text)
@ -1024,33 +1028,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
self:FuelCritical() self:FuelCritical()
end end
-- This causes severe problems as OutOfMissiles is called over and over again leading to many RTB calls.
if false then
-- Out of AA Missiles? CAP, GCICAP, INTERCEPT
local CurrIsCap = false
-- Out of AG Missiles? BAI, SEAD, CAS, STRIKE
local CurrIsA2G = false
-- Check AUFTRAG Type
local CurrAuftrag = self:GetMissionCurrent()
if CurrAuftrag then
local CurrAuftragType = CurrAuftrag:GetType()
if CurrAuftragType == "CAP" or CurrAuftragType == "GCICAP" or CurrAuftragType == "INTERCEPT" then CurrIsCap = true end
if CurrAuftragType == "BAI" or CurrAuftragType == "CAS" or CurrAuftragType == "SEAD" or CurrAuftragType == "STRIKE" then CurrIsA2G = true end
end
-- Check A2A
if (not self:CanAirToAir(true)) and CurrIsCap then
self:OutOfMissilesAA()
end
-- Check A2G
if (not self:CanAirToGround(false)) and CurrIsA2G then
self:OutOfMissilesAG()
end
end
end end
--- ---
@ -1065,7 +1042,7 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
--- ---
-- Engage Detected Targets -- Engage Detected Targets
--- ---
if self:IsAirborne() and self.detectionOn and self.engagedetectedOn and not (self.fuellow or self.fuelcritical) then if self:IsAirborne() and self:IsFuelGood() and self.detectionOn and self.engagedetectedOn then
-- Target. -- Target.
local targetgroup=nil --Wrapper.Group#GROUP local targetgroup=nil --Wrapper.Group#GROUP
@ -1153,11 +1130,6 @@ function FLIGHTGROUP:onafterStatus(From, Event, To)
self:_CheckCargoTransport() self:_CheckCargoTransport()
-- Next check in ~30 seconds.
if not self:IsStopped() then
self:__Status(-30)
end
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -1658,9 +1630,6 @@ function FLIGHTGROUP:onafterSpawned(From, Event, To)
self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false) self:GetGroup():SetOption(AI.Option.Air.id.RTB_ON_BINGO, false)
--self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH) --self.group:SetOption(AI.Option.Air.id.RADAR_USING, AI.Option.Air.val.RADAR_USING.FOR_CONTINUOUS_SEARCH)
-- Update status.
self:__Status(-0.1)
-- Update route. -- Update route.
self:__UpdateRoute(-0.5) self:__UpdateRoute(-0.5)
@ -2338,7 +2307,7 @@ function FLIGHTGROUP:onbeforeRTB(From, Event, To, airbase, SpeedTo, SpeedHold)
end end
-- Only if fuel is not low or critical. -- Only if fuel is not low or critical.
if not (self:IsFuelLow() or self:IsFuelCritical()) then if self:IsFuelGood() then
-- Check if there are remaining tasks. -- Check if there are remaining tasks.
local Ntot,Nsched, Nwp=self:CountRemainingTasks() local Ntot,Nsched, Nwp=self:CountRemainingTasks()
@ -2442,7 +2411,7 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand)
self.currbase=airbase self.currbase=airbase
-- Passed final waypoint! -- Passed final waypoint!
self.passedfinalwp=true self:_PassedFinalWaypoint(true, "_LandAtAirbase")
-- Not waiting any more. -- Not waiting any more.
self.Twaiting=nil self.Twaiting=nil
@ -2477,7 +2446,7 @@ function FLIGHTGROUP:_LandAtAirbase(airbase, SpeedTo, SpeedHold, SpeedLand)
p1=HoldingPoint.pos1 p1=HoldingPoint.pos1
-- Debug marks. -- Debug marks.
if self.Debug then if false then
p0:MarkToAll("Holding point P0") p0:MarkToAll("Holding point P0")
p1:MarkToAll("Holding point P1") p1:MarkToAll("Holding point P1")
end end
@ -3453,7 +3422,7 @@ function FLIGHTGROUP:InitWaypoints()
-- Check if only 1 wp? -- Check if only 1 wp?
if #self.waypoints==1 then if #self.waypoints==1 then
self.passedfinalwp=true self:_PassedFinalWaypoint(true, "FLIGHTGROUP:InitWaypoints #self.waypoints==1")
end end
end end
@ -3475,7 +3444,7 @@ function FLIGHTGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Altitud
local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID)
if wpnumber>self.currentwp then if wpnumber>self.currentwp then
self.passedfinalwp=false self:_PassedFinalWaypoint(false, "FLIGHTGROUP:AddWaypoint wpnumber>self.currentwp")
end end
-- Speed in knots. -- Speed in knots.
@ -3520,7 +3489,7 @@ function FLIGHTGROUP:AddWaypointLanding(Airbase, Speed, AfterWaypointWithID, Alt
local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID) local wpnumber=self:GetWaypointIndexAfterID(AfterWaypointWithID)
if wpnumber>self.currentwp then if wpnumber>self.currentwp then
self.passedfinalwp=false self:_PassedFinalWaypoint(false, "AddWaypointLanding")
end end
-- Speed in knots. -- Speed in knots.
@ -3929,11 +3898,6 @@ function FLIGHTGROUP:GetParking(airbase)
-- Debug output for occupied spots. -- Debug output for occupied spots.
self:T2(self.lid..string.format("Parking spot %d is occupied or not big enough!", parkingspot.TerminalID)) self:T2(self.lid..string.format("Parking spot %d is occupied or not big enough!", parkingspot.TerminalID))
--if self.Debug then
-- local coord=problem.coord --Core.Point#COORDINATE
-- local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.", _termid, problem.name, problem.type, problem.size, problem.dist)
-- coord:MarkToAll(string.format(text))
--end
end end

View File

@ -15,7 +15,9 @@
-- @field #number verbose Verbosity of output. -- @field #number verbose Verbosity of output.
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #table missionqueue Mission queue table. -- @field #table missionqueue Mission queue table.
-- @field #table transportqueue Transport queue.
-- @field #table cohorts Cohorts of this legion. -- @field #table cohorts Cohorts of this legion.
-- @field Ops.Commander#COMMANDER commander Commander of this legion.
-- @extends Functional.Warehouse#WAREHOUSE -- @extends Functional.Warehouse#WAREHOUSE
--- Be surprised! --- Be surprised!
@ -34,6 +36,7 @@ LEGION = {
verbose = 0, verbose = 0,
lid = nil, lid = nil,
missionqueue = {}, missionqueue = {},
transportqueue = {},
cohorts = {}, cohorts = {},
} }
@ -76,6 +79,8 @@ function LEGION:New(WarehouseName, LegionName)
self:AddTransition("*", "MissionRequest", "*") -- Add a (mission) request to the warehouse. self:AddTransition("*", "MissionRequest", "*") -- Add a (mission) request to the warehouse.
self:AddTransition("*", "MissionCancel", "*") -- Cancel mission. self:AddTransition("*", "MissionCancel", "*") -- Cancel mission.
self:AddTransition("*", "TransportRequest", "*") -- Add a (mission) request to the warehouse.
self:AddTransition("*", "OpsOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG). self:AddTransition("*", "OpsOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG).
self:AddTransition("*", "FlightOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG). self:AddTransition("*", "FlightOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG).
self:AddTransition("*", "ArmyOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG). self:AddTransition("*", "ArmyOnMission", "*") -- An OPSGROUP was send on a Mission (AUFTRAG).
@ -138,9 +143,9 @@ function LEGION:SetVerbosity(VerbosityLevel)
return self return self
end end
--- Add a mission for the airwing. The airwing will pick the best available assets for the mission and lauch it when ready. --- Add a mission for the legion. It will pick the best available assets for the mission and lauch it when ready.
-- @param #LEGION self -- @param #LEGION self
-- @param Ops.Auftrag#AUFTRAG Mission Mission for this airwing. -- @param Ops.Auftrag#AUFTRAG Mission Mission for this legion.
-- @return #LEGION self -- @return #LEGION self
function LEGION:AddMission(Mission) function LEGION:AddMission(Mission)
@ -184,6 +189,27 @@ function LEGION:RemoveMission(Mission)
return self return self
end end
--- Add transport assignment to queue.
-- @param #LEGION self
-- @param Ops.OpsTransport#OPSTRANSPORT OpsTransport Transport assignment.
-- @return #LEGION self
function LEGION:AddOpsTransport(OpsTransport)
-- Is not queued at a legion.
OpsTransport:Queued()
-- Add mission to queue.
table.insert(self.transportqueue, OpsTransport)
-- Info text.
local text=string.format("Added Transport %s. Starting at %s-%s",
tostring(OpsTransport.uid), UTILS.SecondsToClock(OpsTransport.Tstart, true), OpsTransport.Tstop and UTILS.SecondsToClock(OpsTransport.Tstop, true) or "INF")
self:T(self.lid..text)
return self
end
--- Get cohort by name. --- Get cohort by name.
-- @param #LEGION self -- @param #LEGION self
-- @param #string CohortName Name of the platoon. -- @param #string CohortName Name of the platoon.
@ -261,6 +287,7 @@ function LEGION:_CheckMissions()
end end
end end
--- Get next mission. --- Get next mission.
-- @param #LEGION self -- @param #LEGION self
-- @return Ops.Auftrag#AUFTRAG Next mission or `#nil`. -- @return Ops.Auftrag#AUFTRAG Next mission or `#nil`.
@ -301,7 +328,7 @@ function LEGION:_GetNextMission()
-- Firstly, check if mission is due? -- Firstly, check if mission is due?
if mission:IsQueued(self) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then if mission:IsQueued(self) and mission:IsReadyToGo() and (mission.importance==nil or mission.importance<=vip) then
-- Check if airwing can do the mission and gather required assets. -- Check if legion can do the mission and gather required assets.
local can, assets=self:CanMission(mission) local can, assets=self:CanMission(mission)
-- Check that mission is still scheduled, time has passed and enough assets are available. -- Check that mission is still scheduled, time has passed and enough assets are available.
@ -391,6 +418,72 @@ function LEGION:_GetNextMission()
return nil return nil
end end
--- Get next transport.
-- @param #LEGION self
-- @return Ops.OpsTransport#OPSTRANSPORT Next transport or `#nil`.
function LEGION:_GetNextTransport()
-- Number of missions.
local Ntransports=#self.transportqueue
-- Treat special cases.
if Ntransports==0 then
return nil
end
local function getAssets(n)
local assets={}
-- Loop over assets.
for _,_cohort in pairs(self.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
if cohort:CheckMissionCapability({AUFTRAG.Type.OPSTRANSPORT}, cohort.missiontypes) then
for _,_asset in pairs(cohort.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Check if asset is currently on a mission (STARTED or QUEUED).
if not asset.spawned then
-- Add to assets.
table.insert(assets, asset)
if #assets==n then
return assets
end
end
end
end
end
end
-- Look for first task that is not accomplished.
for _,_transport in pairs(self.transportqueue) do
local transport=_transport --Ops.OpsTransport#OPSTRANSPORT
-- Check if transport is still queued and ready.
if transport:IsQueued() and transport:IsReadyToGo() then
local assets=getAssets(1)
if #assets>0 then
transport.assets=assets
return transport
end
end
end
return nil
end
--- Calculate the mission score of an asset. --- Calculate the mission score of an asset.
-- @param #LEGION self -- @param #LEGION self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem asset Asset -- @param Functional.Warehouse#WAREHOUSE.Assetitem asset Asset
@ -518,7 +611,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
Mission:Requested() Mission:Requested()
-- Set legion status. Ensures that it is not considered in the next selection. -- Set legion status. Ensures that it is not considered in the next selection.
Mission:SetLegionStatus(self, AUFTRAG.Status.REQUESTED) Mission:SetLegionStatus(self, AUFTRAG.Status.REQUESTED)
--- ---
-- Some assets might already be spawned and even on a different mission (orbit). -- Some assets might already be spawned and even on a different mission (orbit).
@ -556,7 +649,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
end end
end end
-- Add request to airwing warehouse. -- Add request to legion warehouse.
if #Assetlist>0 then if #Assetlist>0 then
--local text=string.format("Requesting assets for mission %s:", Mission.name) --local text=string.format("Requesting assets for mission %s:", Mission.name)
@ -572,9 +665,11 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
end end
-- Add request to airwing warehouse. -- TODO: Get/set functions for assignment string.
-- TODO: better Assignment string. local assignment=string.format("Mission-%d", Mission.auftragsnummer)
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, tostring(Mission.auftragsnummer))
-- Add request to legion warehouse.
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Assetlist, #Assetlist, nil, nil, Mission.prio, assignment)
-- The queueid has been increased in the onafterAddRequest function. So we can simply use it here. -- The queueid has been increased in the onafterAddRequest function. So we can simply use it here.
Mission.requestID[self.alias]=self.queueid Mission.requestID[self.alias]=self.queueid
@ -582,6 +677,50 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
end end
--- On after "MissionRequest" event. Performs a self request to the warehouse for the mission assets. Sets mission status to REQUESTED.
-- @param #LEGION self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsTransport#OPSTRANSPORT Opstransport The requested mission.
function LEGION:onafterTransportRequest(From, Event, To, OpsTransport)
-- Set mission status from QUEUED to REQUESTED.
OpsTransport:Requested()
-- Set legion status. Ensures that it is not considered in the next selection.
--Mission:SetLegionStatus(self, AUFTRAG.Status.REQUESTED)
-- Add request to legion warehouse.
if #OpsTransport.assets>0 then
--local text=string.format("Requesting assets for mission %s:", Mission.name)
for i,_asset in pairs(OpsTransport.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Set asset to requested! Important so that new requests do not use this asset!
asset.requested=true
-- Check max required transports.
if i==1 then
break
end
end
-- TODO: Get/set functions for assignment string.
local assignment=string.format("Transport-%d", OpsTransport.uid)
-- Add request to legion warehouse.
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, OpsTransport.assets, #OpsTransport.assets, nil, nil, OpsTransport.prio, assignment)
-- The queueid has been increased in the onafterAddRequest function. So we can simply use it here.
OpsTransport.requestID=OpsTransport.requestID or {}
OpsTransport.requestID[self.alias]=self.queueid
end
end
--- On after "MissionCancel" event. Cancels the missions of all flightgroups. Deletes request from warehouse queue. --- On after "MissionCancel" event. Cancels the missions of all flightgroups. Deletes request from warehouse queue.
-- @param #LEGION self -- @param #LEGION self
-- @param #string From From state. -- @param #string From From state.
@ -616,35 +755,6 @@ function LEGION:onafterMissionCancel(From, Event, To, Mission)
end end
end end
--[[
if Mission:IsPlanned() or Mission:IsQueued() or Mission:IsRequested() or Ngroups == 0 then
Mission:Done()
else
for _,_asset in pairs(Mission.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Asset should belong to this legion.
if asset.wid==self.uid then
local opsgroup=asset.flightgroup
if opsgroup then
opsgroup:MissionCancel(Mission)
end
-- Not requested any more (if it was).
asset.requested=nil
end
end
end
]]
-- Remove queued request (if any). -- Remove queued request (if any).
if Mission.requestID[self.alias] then if Mission.requestID[self.alias] then
self:_DeleteQueueItemByID(Mission.requestID[self.alias], self.queue) self:_DeleteQueueItemByID(Mission.requestID[self.alias], self.queue)
@ -683,7 +793,7 @@ end
-- @param #string assignment The (optional) assignment for the asset. -- @param #string assignment The (optional) assignment for the asset.
function LEGION:onafterNewAsset(From, Event, To, asset, assignment) function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
-- Call parent warehouse function first. -- Call parent WAREHOUSE function first.
self:GetParent(self, LEGION).onafterNewAsset(self, From, Event, To, asset, assignment) self:GetParent(self, LEGION).onafterNewAsset(self, From, Event, To, asset, assignment)
-- Debug text. -- Debug text.
@ -697,6 +807,10 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
if cohort then if cohort then
if asset.assignment==assignment then if asset.assignment==assignment then
---
-- Asset is added to the COHORT for the first time
---
local nunits=#asset.template.units local nunits=#asset.template.units
@ -750,7 +864,11 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
--asset.terminalType=AIRBASE.TerminalType.OpenBig --asset.terminalType=AIRBASE.TerminalType.OpenBig
else else
--env.info("FF cohort asset returned") ---
-- Asset is returned to the COHORT
---
-- Trigger event.
self:AssetReturned(cohort, asset) self:AssetReturned(cohort, asset)
end end
@ -758,7 +876,7 @@ function LEGION:onafterNewAsset(From, Event, To, asset, assignment)
end end
end end
--- On after "AssetReturned" event. Triggered when an asset group returned to its airwing. --- On after "AssetReturned" event. Triggered when an asset group returned to its legion.
-- @param #LEGION self -- @param #LEGION self
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
@ -859,41 +977,66 @@ function LEGION:onafterAssetSpawned(From, Event, To, group, asset, request)
flightgroup:SetFuelLowRefuel(cohort.fuellowRefuel) flightgroup:SetFuelLowRefuel(cohort.fuellowRefuel)
end end
--- -- Assignment.
-- Mission local assignment=request.assignment
---
if string.find(assignment, "Mission-") then
-- Get Mission (if any).
local mission=self:GetMissionByID(request.assignment)
-- Add mission to flightgroup queue.
if mission then
if Tacan then
--mission:SetTACAN(Tacan, Morse, UnitName, Band)
end
---
-- Mission
---
local uid=UTILS.Split(assignment, "-")[2]
-- Get Mission (if any).
local mission=self:GetMissionByID(uid)
-- Add mission to flightgroup queue. -- Add mission to flightgroup queue.
asset.flightgroup:AddMission(mission) if mission then
-- Trigger event. if Tacan then
self:__OpsOnMission(5, flightgroup, mission) --mission:SetTACAN(Tacan, Morse, UnitName, Band)
end
else
-- Add mission to flightgroup queue.
if Tacan then flightgroup:AddMission(mission)
--flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band)
-- Trigger event.
self:__OpsOnMission(5, flightgroup, mission)
else
if Tacan then
--flightgroup:SwitchTACAN(Tacan, Morse, UnitName, Band)
end
end end
-- Add group to the detection set of the CHIEF (INTEL).
if self.commander and self.commander.chief then
self.commander.chief.detectionset:AddGroup(asset.flightgroup.group)
end
elseif string.find(assignment, "Transport-") then
---
-- Transport
---
local uid=UTILS.Split(assignment, "-")[2]
-- Get Mission (if any).
local transport=self:GetTransportByID(uid)
-- Add mission to flightgroup queue.
if transport then
flightgroup:AddOpsTransport(transport)
end
end end
-- Add group to the detection set of the WINGCOMMANDER.
if self.wingcommander and self.wingcommander.chief then
self.wingcommander.chief.detectionset:AddGroup(asset.flightgroup.group)
end
end end
end end
--- On after "AssetDead" event triggered when an asset group died. --- On after "AssetDead" event triggered when an asset group died.
@ -907,11 +1050,11 @@ function LEGION:onafterAssetDead(From, Event, To, asset, request)
-- Call parent warehouse function first. -- Call parent warehouse function first.
self:GetParent(self, LEGION).onafterAssetDead(self, From, Event, To, asset, request) self:GetParent(self, LEGION).onafterAssetDead(self, From, Event, To, asset, request)
-- Add group to the detection set of the WINGCOMMANDER. -- Remove group from the detection set of the CHIEF (INTEL).
if self.wingcommander and self.wingcommander.chief then if self.commander and self.commander.chief then
self.wingcommander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname}) self.commander.chief.detectionset:RemoveGroupsByName({asset.spawngroupname})
end end
-- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function -- Remove asset from mission is done via Mission:AssetDead() call from flightgroup onafterFlightDead function
-- Remove asset from squadron same -- Remove asset from squadron same
@ -1203,7 +1346,7 @@ function LEGION:CountMissionsInQueue(MissionTypes)
return N return N
end end
--- Count total number of assets that are in the warehouse stock (not spawned). --- Count total number of assets of the legion.
-- @param #LEGION self -- @param #LEGION self
-- @param #boolean InStock If true, only assets that are in the warehouse stock/inventory are counted. -- @param #boolean InStock If true, only assets that are in the warehouse stock/inventory are counted.
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types. -- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
@ -1221,6 +1364,54 @@ function LEGION:CountAssets(InStock, MissionTypes, Attributes)
return N return N
end end
--- Count total number of assets in LEGION warehouse stock that also have a payload.
-- @param #LEGION self
-- @param #boolean Payloads (Optional) Specifc payloads to consider. Default all.
-- @param #table MissionTypes (Optional) Count only assest that can perform certain mission type(s). Default is all types.
-- @param #table Attributes (Optional) Count only assest that have a certain attribute(s), e.g. `WAREHOUSE.Attribute.AIR_BOMBER`.
-- @return #number Amount of asset groups in stock.
function LEGION:CountAssetsWithPayloadsInStock(Payloads, MissionTypes, Attributes)
-- Total number counted.
local N=0
-- Number of payloads in stock per aircraft type.
local Npayloads={}
-- First get payloads for aircraft types of squadrons.
for _,_cohort in pairs(self.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
if Npayloads[cohort.aircrafttype]==nil then
Npayloads[cohort.aircrafttype]=self:CountPayloadsInStock(MissionTypes, cohort.aircrafttype, Payloads)
env.info(string.format("FF got Npayloads=%d for type=%s",Npayloads[cohort.aircrafttype], cohort.aircrafttype))
end
end
for _,_cohort in pairs(self.cohorts) do
local cohort=_cohort --Ops.Cohort#COHORT
-- Number of assets in stock.
local n=cohort:CountAssets(true, MissionTypes, Attributes)
-- Number of payloads.
local p=Npayloads[cohort.aircrafttype] or 0
-- Only the smaller number of assets or paylods is really available.
local m=math.min(n, p)
env.info("FF n="..n)
env.info("FF p="..p)
-- Add up what we have. Could also be zero.
N=N+m
-- Reduce number of available payloads.
Npayloads[cohort.aircrafttype]=Npayloads[cohort.aircrafttype]-m
end
return N
end
--- Count assets on mission. --- Count assets on mission.
-- @param #LEGION self -- @param #LEGION self
-- @param #table MissionTypes Types on mission to be checked. Default all. -- @param #table MissionTypes Types on mission to be checked. Default all.
@ -1241,19 +1432,23 @@ function LEGION:CountAssetsOnMission(MissionTypes, Cohort)
for _,_asset in pairs(mission.assets or {}) do for _,_asset in pairs(mission.assets or {}) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Ensure asset belongs to this letion.
if asset.wid==self.uid then
if Cohort==nil or Cohort.name==asset.squadname then if Cohort==nil or Cohort.name==asset.squadname then
local request, isqueued=self:GetRequestByID(mission.requestID[self.alias]) local request, isqueued=self:GetRequestByID(mission.requestID[self.alias])
if isqueued then if isqueued then
Nq=Nq+1 Nq=Nq+1
else else
Np=Np+1 Np=Np+1
end
end end
end end
end end
end end
end end
@ -1279,8 +1474,13 @@ function LEGION:GetAssetsOnMission(MissionTypes)
for _,_asset in pairs(mission.assets or {}) do for _,_asset in pairs(mission.assets or {}) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Ensure asset belongs to this legion.
if asset.wid==self.uid then
table.insert(assets, asset) table.insert(assets, asset)
end
end end
end end
@ -1289,7 +1489,7 @@ function LEGION:GetAssetsOnMission(MissionTypes)
return assets return assets
end end
--- Get the aircraft types of this airwing. --- Get the unit types of this legion. These are the unit types of all assigned cohorts.
-- @param #LEGION self -- @param #LEGION self
-- @param #boolean onlyactive Count only the active ones. -- @param #boolean onlyactive Count only the active ones.
-- @param #table cohorts Table of cohorts. Default all. -- @param #table cohorts Table of cohorts. Default all.
@ -1332,24 +1532,24 @@ function LEGION:CanMission(Mission)
-- Assume we CAN and NO assets are available. -- Assume we CAN and NO assets are available.
local Can=true local Can=true
local Assets={} local Assets={}
-- Squadrons for the job. If user assigned to mission or simply all. -- Squadrons for the job. If user assigned to mission or simply all.
local cohorts=Mission.squadrons or self.cohorts local cohorts=Mission.squadrons or self.cohorts
local Nassets=Mission.nassets or 1 -- Number of required assets.
if Mission.Nassets and Mission.Nassets[self.alias] then local Nassets=Mission:GetRequiredAssets(self)
Nassets=Mission.Nassets[self.alias]
end
-- Get aircraft unit types for the job. -- Get aircraft unit types for the job.
local unittypes=self:GetAircraftTypes(true, cohorts) local unittypes=self:GetAircraftTypes(true, cohorts)
-- Count all payloads in stock. -- Count all payloads in stock.
if self:IsAirwing() then if self:IsAirwing() then
-- Number of payloads in stock.
local Npayloads=self:CountPayloadsInStock(Mission.type, unittypes, Mission.payloads) local Npayloads=self:CountPayloadsInStock(Mission.type, unittypes, Mission.payloads)
if Npayloads<Nassets then if Npayloads<Nassets then
self:T(self.lid..string.format("INFO: Not enough PAYLOADS available! Got %d but need at least %d", Npayloads, Mission.nassets)) self:T(self.lid..string.format("INFO: Not enough PAYLOADS available! Got %d but need at least %d", Npayloads, Nassets))
return false, Assets return false, Assets
end end
end end
@ -1493,6 +1693,24 @@ function LEGION:GetMissionByID(mid)
return nil return nil
end end
--- Returns the mission for a given ID.
-- @param #LEGION self
-- @param #number uid Transport UID.
-- @return Ops.OpsTransport#OPSTRANSPORT Transport assignment.
function LEGION:GetTransportByID(uid)
for _,_transport in pairs(self.transportqueue) do
local transport=_transport --Ops.OpsTransport#OPSTRANSPORT
if transport.uid==tonumber(uid) then
return transport
end
end
return nil
end
--- Returns the mission for a given request ID. --- Returns the mission for a given request ID.
-- @param #LEGION self -- @param #LEGION self
-- @param #number RequestID Unique ID of the request. -- @param #number RequestID Unique ID of the request.

View File

@ -88,7 +88,8 @@ NAVYGROUP.version="0.7.0"
-- TODO list -- TODO list
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Extend, shorten turn into wind windows -- TODO: Submaries.
-- TODO: Extend, shorten turn into wind windows.
-- TODO: Skipper menu. -- TODO: Skipper menu.
-- DONE: Collision warning. -- DONE: Collision warning.
-- DONE: Detour, add temporary waypoint and resume route. -- DONE: Detour, add temporary waypoint and resume route.
@ -176,7 +177,7 @@ function NAVYGROUP:New(group)
self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit) self:HandleEvent(EVENTS.RemoveUnit, self.OnEventRemoveUnit)
-- Start the status monitoring. -- Start the status monitoring.
self:__Status(-1) self.timerStatus=TIMER:New(self.Status, self):Start(1, 30)
-- Start queue update timer. -- Start queue update timer.
self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5) self.timerQueueUpdate=TIMER:New(self._QueueUpdate, self):Start(2, 5)
@ -454,24 +455,9 @@ end
-- Status -- Status
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
---- Update status.
-- @param #NAVYGROUP self
function NAVYGROUP:onbeforeStatus(From, Event, To)
if self:IsDead() then
self:T(self.lid..string.format("Onbefore Status DEAD ==> false"))
return false
elseif self:IsStopped() then
self:T(self.lid..string.format("Onbefore Status STOPPED ==> false"))
return false
end
return true
end
--- Update status. --- Update status.
-- @param #NAVYGROUP self -- @param #NAVYGROUP self
function NAVYGROUP:onafterStatus(From, Event, To) function NAVYGROUP:Status(From, Event, To)
-- FSM state. -- FSM state.
local fsmstate=self:GetState() local fsmstate=self:GetState()
@ -628,9 +614,6 @@ function NAVYGROUP:onafterStatus(From, Event, To)
self:_PrintTaskAndMissionStatus() self:_PrintTaskAndMissionStatus()
-- Next status update in 30 seconds.
self:__Status(-30)
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@ -897,7 +880,7 @@ function NAVYGROUP:onafterTurnIntoWind(From, Event, To, IntoWind)
IntoWind.waypoint=wptiw IntoWind.waypoint=wptiw
if IntoWind.Uturn and self.Debug then if IntoWind.Uturn and false then
IntoWind.Coordinate:MarkToAll("Return coord") IntoWind.Coordinate:MarkToAll("Return coord")
end end
@ -1120,7 +1103,7 @@ function NAVYGROUP:AddWaypoint(Coordinate, Speed, AfterWaypointWithID, Depth, Up
-- Check if final waypoint is still passed. -- Check if final waypoint is still passed.
if wpnumber>self.currentwp then if wpnumber>self.currentwp then
self.passedfinalwp=false self:_PassedFinalWaypoint(false, "NAVYGROUP:AddWaypoint wpnumber>self.currentwp")
end end
-- Speed in knots. -- Speed in knots.

View File

@ -14,7 +14,6 @@
--- OPSGROUP class. --- OPSGROUP class.
-- @type OPSGROUP -- @type OPSGROUP
-- @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 #number verbose Verbosity level. 0=silent. -- @field #number verbose Verbosity level. 0=silent.
-- @field #string lid Class id string for output to DCS log file. -- @field #string lid Class id string for output to DCS log file.
-- @field #string groupname Name of the group. -- @field #string groupname Name of the group.
@ -60,9 +59,9 @@
-- @field #number speedWp Speed to the next waypoint in m/s. -- @field #number speedWp Speed to the next waypoint in m/s.
-- @field #boolean passedfinalwp Group has passed the final waypoint. -- @field #boolean passedfinalwp Group has passed the final waypoint.
-- @field #number wpcounter Running number counting waypoints. -- @field #number wpcounter Running number counting waypoints.
-- @field #boolean respawning Group is being respawned.
-- @field Core.Set#SET_ZONE checkzones Set of zones. -- @field Core.Set#SET_ZONE checkzones Set of zones.
-- @field Core.Set#SET_ZONE inzones Set of zones in which the group is currently in. -- @field Core.Set#SET_ZONE inzones Set of zones in which the group is currently in.
-- @field Core.Timer#TIMER timerStatus Timer for status update.
-- @field Core.Timer#TIMER timerCheckZone Timer for check zones. -- @field Core.Timer#TIMER timerCheckZone Timer for check zones.
-- @field Core.Timer#TIMER timerQueueUpdate Timer for queue updates. -- @field Core.Timer#TIMER timerQueueUpdate Timer for queue updates.
-- @field #boolean groupinitialized If true, group parameters were initialized. -- @field #boolean groupinitialized If true, group parameters were initialized.
@ -145,7 +144,6 @@
-- @field #OPSGROUP -- @field #OPSGROUP
OPSGROUP = { OPSGROUP = {
ClassName = "OPSGROUP", ClassName = "OPSGROUP",
Debug = false,
verbose = 0, verbose = 0,
lid = nil, lid = nil,
groupname = nil, groupname = nil,
@ -169,7 +167,6 @@ OPSGROUP = {
checkzones = nil, checkzones = nil,
inzones = nil, inzones = nil,
groupinitialized = nil, groupinitialized = nil,
respawning = nil,
wpcounter = 1, wpcounter = 1,
radio = {}, radio = {},
option = {}, option = {},
@ -553,7 +550,7 @@ function OPSGROUP:New(group)
self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy. self:AddTransition("*", "InUtero", "InUtero") -- Deactivated group goes back to mummy.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM. self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self:AddTransition("*", "Status", "*") -- Status update. --self:AddTransition("*", "Status", "*") -- Status update.
self:AddTransition("*", "Destroyed", "*") -- The whole group is dead. self:AddTransition("*", "Destroyed", "*") -- The whole group is dead.
self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage. self:AddTransition("*", "Damaged", "*") -- Someone in the group took damage.
@ -580,6 +577,11 @@ function OPSGROUP:New(group)
self:AddTransition("*", "OutOfRockets", "*") -- Group is out of rockets. self:AddTransition("*", "OutOfRockets", "*") -- Group is out of rockets.
self:AddTransition("*", "OutOfBombs", "*") -- Group is out of bombs. self:AddTransition("*", "OutOfBombs", "*") -- Group is out of bombs.
self:AddTransition("*", "OutOfMissiles", "*") -- Group is out of missiles. self:AddTransition("*", "OutOfMissiles", "*") -- Group is out of missiles.
self:AddTransition("*", "OutOfTorpedos", "*") -- Group is out of torpedos.
self:AddTransition("*", "OutOfMissilesAA", "*") -- Group is out of A2A (air) missiles.
self:AddTransition("*", "OutOfMissilesAG", "*") -- Group is out of A2G (ground) missiles.
self:AddTransition("*", "OutOfMissilesAS", "*") -- Group is out of A2S (ship) missiles.
self:AddTransition("*", "EnterZone", "*") -- Group entered a certain zone. self:AddTransition("*", "EnterZone", "*") -- Group entered a certain zone.
self:AddTransition("*", "LeaveZone", "*") -- Group leaves a certain zone. self:AddTransition("*", "LeaveZone", "*") -- Group leaves a certain zone.
@ -2127,16 +2129,21 @@ end
-- @return #number Next waypoint index. -- @return #number Next waypoint index.
function OPSGROUP:GetWaypointIndexNext(cyclic, i) function OPSGROUP:GetWaypointIndexNext(cyclic, i)
-- If not specified, we take the adinititum value.
if cyclic==nil then if cyclic==nil then
cyclic=self.adinfinitum cyclic=self.adinfinitum
end end
-- Total number of waypoints.
local N=#self.waypoints local N=#self.waypoints
-- Default is currentwp.
i=i or self.currentwp i=i or self.currentwp
-- If no next waypoint exists, because the final waypoint was reached, we return the last waypoint.
local n=math.min(i+1, N) local n=math.min(i+1, N)
-- If last waypoint was reached, the first waypoint is the next in line.
if cyclic and i==N then if cyclic and i==N then
n=1 n=1
end end
@ -2388,7 +2395,7 @@ function OPSGROUP:RemoveWaypoint(wpindex)
-- TODO: patrol adinfinitum. -- TODO: patrol adinfinitum.
if self.currentwp>=n then if self.currentwp>=n then
self.passedfinalwp=true self:_PassedFinalWaypoint(true, "Removed FUTURE waypoint")
end end
self:_CheckGroupDone(1) self:_CheckGroupDone(1)
@ -3088,9 +3095,9 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- New waypoint. -- New waypoint.
if self.isFlightgroup then if self.isFlightgroup then
FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
elseif self.isNavygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isArmygroup then elseif self.isArmygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isNavygroup then
NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
end end
@ -3119,9 +3126,9 @@ function OPSGROUP:onafterTaskExecute(From, Event, To, Task)
-- New waypoint. -- New waypoint.
if self.isFlightgroup then if self.isFlightgroup then
FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
elseif self.isNavygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isArmygroup then elseif self.isArmygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isNavygroup then
NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
end end
@ -3464,10 +3471,28 @@ function OPSGROUP:_GetNextMission()
-- Look for first mission that is SCHEDULED. -- Look for first mission that is SCHEDULED.
for _,_mission in pairs(self.missionqueue) do for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG local mission=_mission --Ops.Auftrag#AUFTRAG
-- Local transport.
local transport=true
if mission.opstransport then
local cargos=mission.opstransport:GetCargoOpsGroups(false) or {}
for _,_opsgroup in pairs(cargos) do
local opscargo=_opsgroup --Ops.OpsGroup#OPSGROUP
if opscargo.groupname==self.groupname then
transport=false
break
end
end
end
-- TODO: One could think of opsgroup specific start conditions. A legion also checks if "ready" but it can be other criteria for the group to actually start the mission.
-- Good example is the above transport. The legion should start the mission but the group should only start after the transport is finished.
if mission:GetGroupStatus(self)==AUFTRAG.Status.SCHEDULED and (mission:IsReadyToGo() or self.legion) and (mission.importance==nil or mission.importance<=vip) then -- Check necessary conditions.
if mission:GetGroupStatus(self)==AUFTRAG.GroupStatus.SCHEDULED and (mission:IsReadyToGo() or self.legion) and (mission.importance==nil or mission.importance<=vip) and transport then
return mission return mission
end end
end end
return nil return nil
@ -3817,6 +3842,11 @@ function OPSGROUP:RouteToMission(mission, delay)
if self:IsDead() or self:IsStopped() then if self:IsDead() or self:IsStopped() then
return return
end end
if mission.type==AUFTRAG.Type.OPSTRANSPORT then
self:AddOpsTransport(mission.opstransport)
return
end
-- ID of current waypoint. -- ID of current waypoint.
local uid=self:GetWaypointCurrent().uid local uid=self:GetWaypointCurrent().uid
@ -4110,9 +4140,9 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
if self.isFlightgroup then if self.isFlightgroup then
FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
elseif self.isNavygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isArmygroup then elseif self.isArmygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isNavygroup then
NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
end end
@ -4140,9 +4170,9 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
if self.isFlightgroup then if self.isFlightgroup then
FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) FLIGHTGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
elseif self.isNavygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isArmygroup then elseif self.isArmygroup then
ARMYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Formation)
elseif self.isNavygroup then
NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude) NAVYGROUP.AddWaypoint(self, Coordinate, Speed, AfterWaypointWithID, Altitude)
end end
@ -4158,8 +4188,8 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
if wpindex==nil or wpindex==#self.waypoints then if wpindex==nil or wpindex==#self.waypoints then
-- Set switch to true. -- Set switch to true.
if not self.adinfinitum or #self.waypoints<=1 then if not self.adinfinitum or #self.waypoints<=1 then
self.passedfinalwp=true self:_PassedFinalWaypoint(true, "Passing waypoint and NOT adinfinitum and #self.waypoints<=1")
end end
end end
@ -4182,7 +4212,7 @@ function OPSGROUP:onafterPassingWaypoint(From, Event, To, Waypoint)
-- Set switch to true. -- Set switch to true.
if not self.adinfinitum or #self.waypoints<=1 then if not self.adinfinitum or #self.waypoints<=1 then
self.passedfinalwp=true self:_PassedFinalWaypoint(true, "PassingWaypoint: wpindex=nil or wpindex=#self.waypoints")
end end
end end
@ -4846,6 +4876,22 @@ function OPSGROUP:_UpdateLaser()
end end
--- On before "ElementSpawned" event. Check that element is not in status spawned already.
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.OpsGroup#OPSGROUP.Element Element The flight group element.
function OPSGROUP:onbeforeElementSpawned(From, Event, To, Element)
if Element and Element.status==OPSGROUP.ElementStatus.SPAWNED then
self:I(self.lid..string.format("FF element %s is already spawned", Element.name))
return false
end
return true
end
--- On after "ElementInUtero" event. --- On after "ElementInUtero" event.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param #string From From state. -- @param #string From From state.
@ -5254,6 +5300,7 @@ function OPSGROUP:onafterStop(From, Event, To)
-- Stop check timers. -- Stop check timers.
self.timerCheckZone:Stop() self.timerCheckZone:Stop()
self.timerQueueUpdate:Stop() self.timerQueueUpdate:Stop()
self.timerStatus:Stop()
-- Stop FSM scheduler. -- Stop FSM scheduler.
self.CallScheduler:Clear() self.CallScheduler:Clear()
@ -7518,7 +7565,16 @@ function OPSGROUP:_CheckAmmoStatus()
if ammo.MissilesAS and self.ammo.MissilesAS>0 and not self.outofMissilesAS then if ammo.MissilesAS and self.ammo.MissilesAS>0 and not self.outofMissilesAS then
self.outofMissilesAS=true self.outofMissilesAS=true
self:OutOfMissilesAS() self:OutOfMissilesAS()
end end
-- Torpedos.
if self.outofTorpedos and ammo.Torpedos>0 then
self.outofTorpedos=false
end
if ammo.Torpedos==0 and self.ammo.Torpedos>0 and not self.outofTorpedos then
self.outofTorpedos=true
self:OutOfTorpedos()
end
-- Check if group is engaging. -- Check if group is engaging.
@ -7647,7 +7703,7 @@ function OPSGROUP:_AddWaypoint(waypoint, wpnumber)
-- Now we obviously did not pass the final waypoint. -- Now we obviously did not pass the final waypoint.
if self.currentwp and wpnumber>self.currentwp then if self.currentwp and wpnumber>self.currentwp then
self.passedfinalwp=false self:_PassedFinalWaypoint(false, "_AddWaypoint self.currentwp and wpnumber>self.currentwp")
end end
end end
@ -7733,7 +7789,7 @@ function OPSGROUP:_InitWaypoints(WpIndexMin, WpIndexMax)
-- Check if only 1 wp? -- Check if only 1 wp?
if #self.waypoints==1 then if #self.waypoints==1 then
self.passedfinalwp=true self:_PassedFinalWaypoint(true, "_InitWaypoints: #self.waypoints==1")
end end
else else
@ -7825,41 +7881,62 @@ end
--@param #number uid Waypoint UID. --@param #number uid Waypoint UID.
function OPSGROUP._PassingWaypoint(group, opsgroup, uid) function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
-- Debug message.
local text=string.format("Group passing waypoint uid=%d", uid)
opsgroup:T(opsgroup.lid..text)
-- Get waypoint data. -- Get waypoint data.
local waypoint=opsgroup:GetWaypointByID(uid) local waypoint=opsgroup:GetWaypointByID(uid)
if waypoint then if waypoint then
-- Increase passing counter.
waypoint.npassed=waypoint.npassed+1
-- Current wp. -- Current wp.
local currentwp=opsgroup.currentwp local currentwp=opsgroup.currentwp
-- Get the current waypoint index. -- Get the current waypoint index.
opsgroup.currentwp=opsgroup:GetWaypointIndex(uid) opsgroup.currentwp=opsgroup:GetWaypointIndex(uid)
local wpistemp=waypoint.temp or waypoint.detour or waypoint.astar
-- Remove temp waypoints.
if wpistemp then
opsgroup:RemoveWaypointByID(uid)
end
-- Set expected speed and formation from the next WP. -- Get next waypoint. Tricky part is that if
local wpnext=opsgroup:GetWaypointNext() local wpnext=opsgroup:GetWaypointNext()
if wpnext then
if wpnext and (opsgroup.currentwp<#opsgroup.waypoints or opsgroup.adinfinitum or wpistemp) then
opsgroup:I(opsgroup.lid..string.format("Next waypoint UID=%d index=%d", wpnext.uid, opsgroup:GetWaypointIndex(wpnext.uid)))
-- Set formation. -- Set formation.
if opsgroup.isGround then if opsgroup.isGround then
opsgroup.formation=wpnext.action opsgroup.formation=wpnext.action
end end
-- Set speed. -- Set speed to next wp.
opsgroup.speed=wpnext.speed opsgroup.speed=wpnext.speed
if opsgroup.speed<0.01 then
opsgroup.speed=UTILS.KmphToMps(opsgroup.speedCruise)
end
else
env.info(opsgroup.lid.."FF 300")
-- Set passed final waypoint.
opsgroup:_PassedFinalWaypoint(true, "_PassingWaypoint No next Waypoint found")
end end
-- Debug message.
local text=string.format("Group passing waypoint uid=%d", uid)
opsgroup:T(opsgroup.lid..text)
-- Trigger PassingWaypoint event. -- Trigger PassingWaypoint event.
if waypoint.temp then if waypoint.temp then
-- Remove temp waypoint.
opsgroup:RemoveWaypointByID(uid)
if opsgroup:IsNavygroup() or opsgroup:IsArmygroup() then if opsgroup:IsNavygroup() or opsgroup:IsArmygroup() then
--TODO: not sure if this works with FLIGHTGROUPS --TODO: not sure if this works with FLIGHTGROUPS
opsgroup:Cruise() opsgroup:Cruise()
@ -7867,17 +7944,11 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
elseif waypoint.astar then elseif waypoint.astar then
-- Remove Astar waypoint.
opsgroup:RemoveWaypointByID(uid)
-- Cruise. -- Cruise.
opsgroup:Cruise() opsgroup:Cruise()
elseif waypoint.detour then elseif waypoint.detour then
-- Remove detour waypoint.
opsgroup:RemoveWaypointByID(uid)
if opsgroup:IsRearming() then if opsgroup:IsRearming() then
-- Trigger Rearming event. -- Trigger Rearming event.
@ -7890,6 +7961,7 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
elseif opsgroup:IsReturning() then elseif opsgroup:IsReturning() then
-- Trigger Returned event.
opsgroup:Returned() opsgroup:Returned()
elseif opsgroup:IsPickingup() then elseif opsgroup:IsPickingup() then
@ -7965,9 +8037,6 @@ function OPSGROUP._PassingWaypoint(group, opsgroup, uid)
opsgroup.ispathfinding=false opsgroup.ispathfinding=false
end end
-- Increase passing counter.
waypoint.npassed=waypoint.npassed+1
-- Call event function. -- Call event function.
opsgroup:PassingWaypoint(waypoint) opsgroup:PassingWaypoint(waypoint)
end end
@ -9398,7 +9467,7 @@ function OPSGROUP:GetAmmoUnit(unit, display)
nmissilesAS=nmissilesAS+Nammo nmissilesAS=nmissilesAS+Nammo
elseif MissileCategory==Weapon.MissileCategory.BM then elseif MissileCategory==Weapon.MissileCategory.BM then
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
nmissilesAG=nmissilesAG+Nammo nmissilesBM=nmissilesBM+Nammo
elseif MissileCategory==Weapon.MissileCategory.CRUISE then elseif MissileCategory==Weapon.MissileCategory.CRUISE then
nmissiles=nmissiles+Nammo nmissiles=nmissiles+Nammo
nmissilesCR=nmissilesCR+Nammo nmissilesCR=nmissilesCR+Nammo
@ -9477,6 +9546,20 @@ function OPSGROUP:_MissileCategoryName(categorynumber)
return cat return cat
end end
--- Set passed final waypoint value.
-- @param #OPSGROUP self
-- @param #boolean final If `true`, final waypoint was passed.
-- @param #string comment Some comment as to why the final waypoint was passed.
function OPSGROUP:_PassedFinalWaypoint(final, comment)
-- Debug info.
self:I(self.lid..string.format("Passed final waypoint=%s [from %s]: comment \"%s\"", tostring(final), tostring(self.passedfinalwp), tostring(comment)))
-- Set value.
self.passedfinalwp=final
end
--- Get coordinate from an object. --- Get coordinate from an object.
-- @param #OPSGROUP self -- @param #OPSGROUP self
-- @param Wrapper.Object#OBJECT Object The object. -- @param Wrapper.Object#OBJECT Object The object.

View File

@ -119,16 +119,21 @@ OPSTRANSPORT = {
pathsTransport = {}, pathsTransport = {},
pathsPickup = {}, pathsPickup = {},
requiredCargos = {}, requiredCargos = {},
assets = {},
} }
--- Cargo transport status. --- Cargo transport status.
-- @type OPSTRANSPORT.Status -- @type OPSTRANSPORT.Status
-- @field #string PLANNED Planning state. -- @field #string PLANNED Planning state.
-- @field #string QUEUED Queued state.
-- @field #string REQUESTED Requested state.
-- @field #string SCHEDULED Transport is scheduled in the cargo queue. -- @field #string SCHEDULED Transport is scheduled in the cargo queue.
-- @field #string EXECUTING Transport is being executed. -- @field #string EXECUTING Transport is being executed.
-- @field #string DELIVERED Transport was delivered. -- @field #string DELIVERED Transport was delivered.
OPSTRANSPORT.Status={ OPSTRANSPORT.Status={
PLANNED="planned", PLANNED="planned",
QUEUED="queued",
REQUESTED="requested",
SCHEDULED="scheduled", SCHEDULED="scheduled",
EXECUTING="executing", EXECUTING="executing",
DELIVERED="delivered", DELIVERED="delivered",
@ -207,7 +212,10 @@ function OPSTRANSPORT:New(GroupSet, Pickupzone, Deployzone)
-- PLANNED --> SCHEDULED --> EXECUTING --> DELIVERED -- PLANNED --> SCHEDULED --> EXECUTING --> DELIVERED
self:AddTransition("*", "Planned", OPSTRANSPORT.Status.PLANNED) -- Cargo transport was planned. self:AddTransition("*", "Planned", OPSTRANSPORT.Status.PLANNED) -- Cargo transport was planned.
self:AddTransition(OPSTRANSPORT.Status.PLANNED, "Scheduled", OPSTRANSPORT.Status.SCHEDULED) -- Cargo is queued at at least one carrier. self:AddTransition(OPSTRANSPORT.Status.PLANNED, "Queued", OPSTRANSPORT.Status.QUEUED) -- Cargo is queued at at least one carrier.
self:AddTransition(OPSTRANSPORT.Status.QUEUED, "Requested", OPSTRANSPORT.Status.REQUESTED) -- Transport assets have been requested from a warehouse.
self:AddTransition(OPSTRANSPORT.Status.QUEUED, "Scheduled", OPSTRANSPORT.Status.SCHEDULED) -- Cargo is queued at at least one carrier.
self:AddTransition(OPSTRANSPORT.Status.PLANNED, "Scheduled", OPSTRANSPORT.Status.SCHEDULED) -- Cargo is queued at at least one carrier.
self:AddTransition(OPSTRANSPORT.Status.SCHEDULED, "Executing", OPSTRANSPORT.Status.EXECUTING) -- Cargo is being transported. self:AddTransition(OPSTRANSPORT.Status.SCHEDULED, "Executing", OPSTRANSPORT.Status.EXECUTING) -- Cargo is being transported.
self:AddTransition("*", "Delivered", OPSTRANSPORT.Status.DELIVERED) -- Cargo was delivered. self:AddTransition("*", "Delivered", OPSTRANSPORT.Status.DELIVERED) -- Cargo was delivered.
@ -814,6 +822,41 @@ function OPSTRANSPORT:IsReadyToGo()
return true return true
end end
--- Check if state is PLANNED.
-- @param #OPSTRANSPORT self
-- @return #boolean If true, status is PLANNED.
function OPSTRANSPORT:IsPlanned()
return self:is(OPSTRANSPORT.Status.PLANNED)
end
--- Check if state is QUEUED.
-- @param #OPSTRANSPORT self
-- @return #boolean If true, status is QUEUED.
function OPSTRANSPORT:IsQueued()
return self:is(OPSTRANSPORT.Status.QUEUED)
end
--- Check if state is REQUESTED.
-- @param #OPSTRANSPORT self
-- @return #boolean If true, status is REQUESTED.
function OPSTRANSPORT:IsRequested()
return self:is(OPSTRANSPORT.Status.REQUESTED)
end
--- Check if state is SCHEDULED.
-- @param #OPSTRANSPORT self
-- @return #boolean If true, status is SCHEDULED.
function OPSTRANSPORT:IsScheduled()
return self:is(OPSTRANSPORT.Status.SCHEDULED)
end
--- Check if state is EXECUTING.
-- @param #OPSTRANSPORT self
-- @return #boolean If true, status is EXECUTING.
function OPSTRANSPORT:IsExecuting()
return self:is(OPSTRANSPORT.Status.EXECUTING)
end
--- Check if all cargo was delivered (or is dead). --- Check if all cargo was delivered (or is dead).
-- @param #OPSTRANSPORT self -- @param #OPSTRANSPORT self
-- @return #boolean If true, all possible cargo was delivered. -- @return #boolean If true, all possible cargo was delivered.