OPSTRANSPORT

- Improved assignment to multiple legions.
This commit is contained in:
Frank 2021-09-10 00:32:15 +02:00
parent 6f126e6cd4
commit 884c51a69a
4 changed files with 290 additions and 59 deletions

View File

@ -34,7 +34,7 @@
-- @field #string type Mission type.
-- @field #string status Mission status.
-- @field #table legions Assigned legions.
-- @field #table statusLegion Mission status of all assigned LEGIONSs.
-- @field #table statusLegion Mission status of all assigned LEGIONs.
-- @field #string statusCommander Mission status of the COMMANDER.
-- @field #string statusChief Mission status of the CHIF.
-- @field #table groupdata Group specific data.
@ -538,19 +538,22 @@ function AUFTRAG:New(Type)
-- State is planned.
self.status=AUFTRAG.Status.PLANNED
-- Defaults
-- Defaults .
self:SetName()
self:SetPriority()
self:SetTime()
self:SetRequiredAssets()
self:SetRequiredCarriers()
self.engageAsGroup=true
self.dTevaluate=5
-- Init counters and stuff.
self.repeated=0
self.repeatedSuccess=0
self.repeatedFailure=0
self.Nrepeat=0
self.NrepeatFailure=0
self.NrepeatSuccess=0
self.nassets=1
self.dTevaluate=5
self.Ncasualties=0
self.Nkills=0
self.Nelements=0
@ -1854,10 +1857,38 @@ function AUFTRAG:SetTransportForAssets(DeployZone, DisembarkZone, Carriers)
end
-- Set min/max number of carriers to be assigned.
self.opstransport.nCarriersMin=self.nCarriersMin
self.opstransport.nCarriersMax=self.nCarriersMax
return self
end
--- Add a transport Legion. This requires an OPSTRANSPORT to be set via `AUFTRAG:SetTransportForAssets`.
--- Set number of required carrier groups if an OPSTRANSPORT assignment is required.
-- @param #AUFTRAG self
-- @param #number NcarriersMin Number of carriers *at least* required. Default 1.
-- @param #number NcarriersMax Number of carriers *at most* used for transportation. Default is same as `NcarriersMin`.
-- @return #AUFTRAG self
function AUFTRAG:SetRequiredCarriers(NcarriersMin, NcarriersMax)
self.nCarriersMin=NcarriersMin or 1
self.nCarriersMax=NcarriersMax or self.nCarriersMin
-- Ensure that max is at least equal to min.
if self.nCarriersMax<self.nCarriersMin then
self.nCarriersMax=self.nCarriersMin
end
-- Pass this on to the ops transport.
if self.opstransport then
self.opstransport:SetRequiredCarriers(NcarriersMin, NcarriersMax)
end
return self
end
--- Add a transport Legion.
-- @param #AUFTRAG self
-- @param Ops.Legion#LEGION Legion The legion.
function AUFTRAG:AddTransportLegion(Legion)
@ -3633,8 +3664,11 @@ end
-- @return #AUFTRAG self
function AUFTRAG:AddAsset(Asset)
self.assets=self.assets or {}
-- Debug info
self:T(self.lid..string.format("Adding asset \"%s\" to mission", tostring(Asset.spawngroupname)))
-- Add to table.
self.assets=self.assets or {}
table.insert(self.assets, Asset)
return self
@ -3650,7 +3684,7 @@ function AUFTRAG:DelAsset(Asset)
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if asset.uid==Asset.uid then
self:T(self.lid..string.format("Removing asset \"%s\" from mission", tostring(asset.spawngroupname)))
self:T(self.lid..string.format("Removing asset \"%s\" from mission", tostring(Asset.spawngroupname)))
table.remove(self.assets, i)
return self
end

View File

@ -324,6 +324,22 @@ function BRIGADE:onafterStatus(From, Event, To)
self:I(self.lid..text)
end
--------------------
-- Transport Info --
--------------------
if self.verbose>=2 then
local text=string.format("Transports Total=%d:", #self.transportqueue)
for i,_transport in pairs(self.transportqueue) do
local transport=_transport --Ops.OpsTransport#OPSTRANSPORT
local prio=string.format("%d/%s", transport.prio, tostring(transport.importance)) ; if transport.urgent then prio=prio.." (!)" end
local carriers=string.format("Ncargo=%d/%d, Ncarriers=%d", transport.Ncargo, transport.Ndelivered, transport.Ncarrier)
text=text..string.format("\n[%d] UID=%d: Status=%s, Prio=%s, Cargo: %s", i, transport.uid, transport:GetState(), prio, carriers)
end
self:I(self.lid..text)
end
-------------------
-- Platoon Info --
-------------------

View File

@ -256,7 +256,6 @@ function LEGION:AddMission(Mission)
-- Add ops transport to transport Legions.
if Mission.opstransport then
-- Add a new TZC: from pickup here to the deploy zone.
local tzc=Mission.opstransport:AddTransportZoneCombo(self.spawnzone, Mission.opstransport.tzcDefault.DeployZone)
@ -316,9 +315,15 @@ function LEGION:AddOpsTransport(OpsTransport)
-- Is not queued at a legion.
OpsTransport:Queued()
-- Set legion status.
OpsTransport:SetLegionStatus(self, AUFTRAG.Status.QUEUED)
-- Add mission to queue.
table.insert(self.transportqueue, OpsTransport)
-- Add this legion to the transport.
OpsTransport:AddLegion(self)
-- 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")
@ -427,7 +432,7 @@ function LEGION:_GetNextMission()
end
table.sort(self.missionqueue, _sort)
-- Look for first mission that is SCHEDULED.
-- Search min importance.
local vip=math.huge
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
@ -436,9 +441,6 @@ function LEGION:_GetNextMission()
end
end
-- Current time.
local time=timer.getAbsTime()
-- Look for first task that is not accomplished.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
@ -453,7 +455,6 @@ function LEGION:_GetNextMission()
return mission
end
end -- mission due?
end -- mission loop
@ -474,7 +475,7 @@ function LEGION:_GetNextTransport()
end
--- Function to get carrier assets from all cohorts.
local function getAssets(n, weightGroup)
local function getAssets(n, N , weightGroup)
-- Selected assets.
local assets={}
@ -496,7 +497,12 @@ function LEGION:_GetNextTransport()
-- Add to assets.
table.insert(assets, asset)
if #assets==n then
--TODO: Optimize Asset Selection!
--TODO: Check if deploy and (any) pickup zone is an airbase, so airplanes can be used.
-- Max number of assets reached.
if #assets==N then
return assets
end
end
@ -506,16 +512,22 @@ function LEGION:_GetNextTransport()
end
end
-- At least min number reached?
if #assets>=n then
return assets
else
return nil
end
end
--TODO: Sort transports wrt to prio and importance. See mission sorting!
-- 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
if transport:IsQueued(self) and transport:IsReadyToGo() then
-- Get all undelivered cargo ops groups.
local cargoOpsGroups=transport:GetCargoOpsGroups(false)
@ -534,10 +546,14 @@ function LEGION:_GetNextTransport()
end
-- Get assets. If not enough assets can be found, nil is returned.
local assets=getAssets(1, weightGroup)
local assets=getAssets(transport.nCarriersMin, transport.nCarriersMax, weightGroup)
if assets then
transport.assets=assets
for _,_asset in pairs(assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
asset.isReserved=true
transport:AddAsset(asset)
end
return transport
end
@ -547,7 +563,7 @@ function LEGION:_GetNextTransport()
end
-- No transport found.
return nil
end
@ -735,6 +751,7 @@ function LEGION:onafterMissionRequest(From, Event, To, Mission)
asset.requested=true
asset.isReserved=false
-- Set missin task so that the group is spawned with the right one.
if Mission.missionTask then
asset.missionTask=Mission.missionTask
end
@ -761,37 +778,45 @@ end
-- @param Ops.OpsTransport#OPSTRANSPORT Opstransport The requested mission.
function LEGION:onafterTransportRequest(From, Event, To, OpsTransport)
-- Set mission status from QUEUED to REQUESTED.
OpsTransport:Requested()
-- List of assets that will be requested.
local AssetList={}
-- 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
--TODO: Find spawned assets on ALERT 5 mission OPSTRANSPORT.
--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
-- Check that this asset belongs to this Legion warehouse.
if asset.wid==self.uid then
-- Set asset to requested! Important so that new requests do not use this asset!
asset.requested=true
asset.isReserved=false
-- Check max required transports.
if i==1 then
break
-- Set transport mission task.
asset.missionTask=ENUMS.MissionTask.TRANSPORT
-- Add asset to list.
table.insert(AssetList, asset)
end
end
end
if #AssetList>0 then
-- Set mission status from QUEUED to REQUESTED.
OpsTransport:Requested()
-- Set legion status. Ensures that it is not considered in the next selection.
OpsTransport:SetLegionStatus(self, OPSTRANSPORT.Status.REQUESTED)
-- 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)
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, AssetList, #AssetList, 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
@ -1619,11 +1644,14 @@ function LEGION:GetAircraftTypes(onlyactive, cohorts)
end
--- Check if assets for a given mission type are available.
--
-- OBSOLETE and renamed to _CanMission (to see if it is still used somewhere)
--
-- @param #LEGION self
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
-- @return #boolean If true, enough assets are available.
-- @return #table Assets that can do the required mission.
function LEGION:CanMission(Mission)
function LEGION:_CanMission(Mission)
-- Assume we CAN and NO assets are available.
local Can=true

View File

@ -2,10 +2,10 @@
--
-- ## Main Features:
--
-- * Transport troops from A to B.
-- * Transport troops from A to B
-- * Supports ground, naval and airborne (airplanes and helicopters) units as carriers
-- * Use combined forces (ground, naval, air) to transport the troops.
-- * Additional FSM events to hook into and customize your mission design.
-- * Use combined forces (ground, naval, air) to transport the troops
-- * Additional FSM events to hook into and customize your mission design
--
-- ===
--
@ -51,6 +51,9 @@
--
-- @field Ops.Auftrag#AUFTRAG mission The mission attached to this transport.
-- @field #table assets Warehouse assets assigned for this transport.
-- @field #table legions Assigned legions.
-- @field #table statusLegion Transport status of all assigned LEGIONs.
-- @field #table requestID The ID of the queued warehouse request. Necessary to cancel the request if the transport was cancelled before the request is processed.
--
-- @extends Core.Fsm#FSM
@ -58,8 +61,6 @@
--
-- ===
--
-- ![Banner Image](..\Presentations\OPS\Transport\_Main.png)
--
-- # The OPSTRANSPORT Concept
--
-- This class simulates troop transport using carriers such as APCs, ships, helicopters or airplanes. The carriers and transported groups need to be OPSGROUPS (see ARMYGROUP, NAVYGROUP and FLIGHTGROUP classes).
@ -119,6 +120,9 @@ OPSTRANSPORT = {
tzcCounter = 0,
conditionStart = {},
assets = {},
legions = {},
statusLegion = {},
requestID = {},
}
--- Cargo transport status.
@ -209,16 +213,17 @@ function OPSTRANSPORT:New(CargoGroups, PickupZone, DeployZone)
self.uid=_OPSTRANSPORTID
-- Defaults.
self:SetPriority()
self:SetTime()
self:SetRequiredCarriers()
-- Init arrays and counters.
self.cargos={}
self.carriers={}
self.Ncargo=0
self.Ncarrier=0
self.Ndelivered=0
self:SetPriority()
self:SetTime()
-- Set default TZC.
self.tzcDefault=self:AddTransportZoneCombo(PickupZone, DeployZone, CargoGroups)
@ -772,6 +777,33 @@ function OPSTRANSPORT:GetRequiredCargos(TransportZoneCombo)
return TransportZoneCombo.RequiredCargos
end
--- Set number of required carrier groups for an OPSTRANSPORT assignment. Only used if transport is assigned at **LEGION** or higher level.
-- @param #OPSTRANSPORT self
-- @param #number NcarriersMin Number of carriers *at least* required. Default 1.
-- @param #number NcarriersMax Number of carriers *at most* used for transportation. Default is same as `NcarriersMin`.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetRequiredCarriers(NcarriersMin, NcarriersMax)
self.nCarriersMin=NcarriersMin or 1
self.nCarriersMax=NcarriersMax or self.nCarriersMin
-- Ensure that max is at least equal to min.
if self.nCarriersMax<self.nCarriersMin then
self.nCarriersMax=self.nCarriersMin
end
return self
end
--- Get the number of required carrier groups for an OPSTRANSPORT assignment. Only used if transport is assigned at **LEGION** or higher level.
-- @param #OPSTRANSPORT self
-- @return #number Number of carriers *at least* required.
-- @return #number Number of carriers *at most* used for transportation.
function OPSTRANSPORT:GetRequiredCarriers()
return self.nCarriersMin, self.nCarriersMax
end
--- Add a carrier assigned for this transport.
-- @param #OPSTRANSPORT self
@ -1174,6 +1206,78 @@ function OPSTRANSPORT:GetNcarrier()
return self.Ncarrier
end
--- Add asset to transport.
-- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be added.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddAsset(Asset)
-- Debug info
self:T(self.lid..string.format("Adding asset \"%s\" to transport", tostring(Asset.spawngroupname)))
-- Add asset to table.
self.assets=self.assets or {}
table.insert(self.assets, Asset)
return self
end
--- Delete asset from mission.
-- @param #OPSTRANSPORT self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The asset to be removed.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:DelAsset(Asset)
for i,_asset in pairs(self.assets or {}) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
if asset.uid==Asset.uid then
self:T(self.lid..string.format("Removing asset \"%s\" from transport", tostring(Asset.spawngroupname)))
table.remove(self.assets, i)
return self
end
end
return self
end
--- Add LEGION to the transport.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion The legion.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:AddLegion(Legion)
-- Debug info.
self:I(self.lid..string.format("Adding legion %s", Legion.alias))
-- Add legion to table.
table.insert(self.legions, Legion)
return self
end
--- Remove LEGION from transport.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion The legion.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:RemoveLegion(Legion)
-- Loop over legions
for i=#self.legions,1,-1 do
local legion=self.legions[i] --Ops.Legion#LEGION
if legion.alias==Legion.alias then
-- Debug info.
self:I(self.lid..string.format("Removing legion %s", Legion.alias))
table.remove(self.legions, i)
return self
end
end
self:E(self.lid..string.format("ERROR: Legion %s not found and could not be removed!", Legion.alias))
return self
end
--- Check if an OPS group is assigned as carrier for this transport.
-- @param #OPSTRANSPORT self
-- @param Ops.OpsGroup#OPSGROUP CarrierGroup Potential carrier OPSGROUP.
@ -1215,6 +1319,7 @@ function OPSTRANSPORT:IsReadyToGo()
end
if not gotzones then
text=text.."No, pickup/deploy zone combo not yet defined!"
return false
end
-- Start time did not pass yet.
@ -1246,39 +1351,83 @@ function OPSTRANSPORT:IsReadyToGo()
return true
end
--- Set LEGION transport status.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion The legion.
-- @param #string Status New status.
-- @return #OPSTRANSPORT self
function OPSTRANSPORT:SetLegionStatus(Legion, Status)
-- Old status
local status=self:GetLegionStatus(Legion)
-- Debug info.
self:I(self.lid..string.format("Setting LEGION %s to status %s-->%s", Legion.alias, tostring(status), tostring(Status)))
-- New status.
self.statusLegion[Legion.alias]=Status
return self
end
--- Get LEGION transport status.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion The legion.
-- @return #string status Current status.
function OPSTRANSPORT:GetLegionStatus(Legion)
-- Current status.
local status=self.statusLegion[Legion.alias] or "unknown"
return status
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)
local is=self:is(OPSTRANSPORT.Status.PLANNED)
return is
end
--- Check if state is QUEUED.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion (Optional) Check if transport is queued at this legion.
-- @return #boolean If true, status is QUEUED.
function OPSTRANSPORT:IsQueued()
return self:is(OPSTRANSPORT.Status.QUEUED)
function OPSTRANSPORT:IsQueued(Legion)
local is=self:is(OPSTRANSPORT.Status.QUEUED)
if Legion then
is=self:GetLegionStatus(Legion)==OPSTRANSPORT.Status.QUEUED
end
return is
end
--- Check if state is REQUESTED.
-- @param #OPSTRANSPORT self
-- @param Ops.Legion#LEGION Legion (Optional) Check if transport is queued at this legion.
-- @return #boolean If true, status is REQUESTED.
function OPSTRANSPORT:IsRequested()
return self:is(OPSTRANSPORT.Status.REQUESTED)
function OPSTRANSPORT:IsRequested(Legion)
local is=self:is(OPSTRANSPORT.Status.REQUESTED)
if Legion then
is=self:GetLegionStatus(Legion)==OPSTRANSPORT.Status.REQUESTED
end
return is
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)
local is=self:is(OPSTRANSPORT.Status.SCHEDULED)
return is
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)
local is=self:is(OPSTRANSPORT.Status.EXECUTING)
return is
end
--- Check if all cargo was delivered (or is dead).
@ -1314,7 +1463,7 @@ function OPSTRANSPORT:onafterStatus(From, Event, To)
if self.verbose>=1 then
-- Info text.
local text=string.format("%s: Ncargo=%d/%d, Ncarrier=%d/%d", fsmstate:upper(), self.Ncargo, self.Ndelivered, #self.carriers, self.Ncarrier)
local text=string.format("%s: Ncargo=%d/%d, Ncarrier=%d/%d, Nlegions=%d", fsmstate:upper(), self.Ncargo, self.Ndelivered, #self.carriers, self.Ncarrier, #self.legions)
-- Info about cargo and carrier.
if self.verbose>=2 then
@ -1701,13 +1850,17 @@ function OPSTRANSPORT:_GetTransportZoneCombo(Carrier)
-- Check that pickup and deploy zones were defined.
if tz.PickupZone and tz.DeployZone and tz.EmbarkZone then
--TODO: Check if Carrier is an aircraft and if so, check that pickup AND deploy zones are airbases (not ships, not farps).
-- Count undelivered cargos in embark(!) zone that fit into the carrier.
local ncargo=self:_CountCargosInZone(tz.EmbarkZone, false, Carrier, tz)
--env.info(string.format("FF GetPickupZone i=%d, ncargo=%d", i, ncargo))
if ncargo>0 then
-- At least one group in the zone.
if ncargo>=1 then
-- Distance to the carrier in meters.
local dist=tz.PickupZone:Get2DDistance(vec2)
if distmin==nil or dist<distmin then