--- **Ops** - Army Warehouse. -- -- **Main Features:** -- -- * Manage platoons -- -- === -- -- ### Author: **funkyfranky** -- @module Ops.Army -- @image OPS_AirWing.png --- ARMY class. -- @type ARMY -- @field #string ClassName Name of the class. -- @field #number verbose Verbosity of output. -- @field #string lid Class id string for output to DCS log file. -- @field #table menu Table of menu items. -- @field #table squadrons Table of squadrons. -- @field #table missionqueue Mission queue table. -- @field #table payloads Playloads for specific aircraft and mission types. -- @field #number payloadcounter Running index of payloads. -- @field Core.Set#SET_ZONE zonesetCAP Set of CAP zones. -- @field Core.Set#SET_ZONE zonesetTANKER Set of TANKER zones. -- @field Core.Set#SET_ZONE zonesetAWACS Set of AWACS zones. -- @field #number nflightsCAP Number of CAP flights constantly in the air. -- @field #number nflightsAWACS Number of AWACS flights constantly in the air. -- @field #number nflightsTANKERboom Number of TANKER flights with BOOM constantly in the air. -- @field #number nflightsTANKERprobe Number of TANKER flights with PROBE constantly in the air. -- @field #number nflightsRescueHelo Number of Rescue helo flights constantly in the air. -- @field #table pointsCAP Table of CAP points. -- @field #table pointsTANKER Table of Tanker points. -- @field #table pointsAWACS Table of AWACS points. -- @field Ops.WingCommander#WINGCOMMANDER wingcommander The wing commander responsible for this airwing. -- -- @field Ops.RescueHelo#RESCUEHELO rescuehelo The rescue helo. -- @field Ops.RecoveryTanker#RECOVERYTANKER recoverytanker The recoverytanker. -- -- @extends Functional.Warehouse#WAREHOUSE --- Be surprised! -- -- === -- -- ![Banner Image](..\Presentations\OPS\AirWing\_Main.png) -- -- # The ARMY Concept -- -- An ARMY consists of multiple SQUADRONS. These squadrons "live" in a WAREHOUSE, i.e. a physical structure that is connected to an airbase (airdrome, FRAP or ship). -- For an airwing to be operational, it needs airframes, weapons/fuel and an airbase. -- -- # Create an Army -- -- ## Constructing the Army -- -- airwing=ARMY:New("Warehouse Batumi", "8th Fighter Wing") -- airwing:Start() -- -- The first parameter specified the warehouse, i.e. the static building housing the airwing (or the name of the aircraft carrier). The second parameter is optional -- and sets an alias. -- -- ## Adding Squadrons -- -- At this point the airwing does not have any assets (aircraft). In order to add these, one needs to first define SQUADRONS. -- -- VFA151=SQUADRON:New("F-14 Group", 8, "VFA-151 (Vigilantes)") -- VFA151:AddMissionCapability({AUFTRAG.Type.GCICAP, AUFTRAG.Type.INTERCEPT}) -- -- airwing:AddSquadron(VFA151) -- -- This adds eight Tomcat groups beloning to VFA-151 to the airwing. This squadron has the ability to perform combat air patrols and intercepts. -- -- ## Adding Payloads -- -- Adding pure airframes is not enough. The aircraft also need weapons (and fuel) for certain missions. These must be given to the airwing from template groups -- defined in the Mission Editor. -- -- -- F-14 payloads for CAP and INTERCEPT. Phoenix are first, sparrows are second choice. -- airwing:NewPayload(GROUP:FindByName("F-14 Payload AIM-54C"), 2, {AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.GCICAP}, 80) -- airwing:NewPayload(GROUP:FindByName("F-14 Payload AIM-7M"), 20, {AUFTRAG.Type.INTERCEPT, AUFTRAG.Type.GCICAP}) -- -- This will add two AIM-54C and 20 AIM-7M payloads. -- -- If the airwing gets an intercept or patrol mission assigned, it will first use the AIM-54s. Once these are consumed, the AIM-7s are attached to the aircraft. -- -- When an airwing does not have a payload for a certain mission type, the mission cannot be carried out. -- -- You can set the number of payloads to "unlimited" by setting its quantity to -1. -- -- # Adding Missions -- -- Various mission types can be added easily via the AUFTRAG class. -- -- Once you created an AUFTRAG you can add it to the ARMY with the :AddMission(mission) function. -- -- This mission will be put into the ARMY queue. Once the mission start time is reached and all resources (airframes and pylons) are available, the mission is started. -- If the mission stop time is over (and the mission is not finished), it will be cancelled and removed from the queue. This applies also to mission that were not even -- started. -- -- # Command an Army -- -- An airwing can receive missions from a WINGCOMMANDER. See docs of that class for details. -- -- However, you are still free to add missions at anytime. -- -- -- @field #ARMY ARMY = { ClassName = "ARMY", verbose = 0, lid = nil, menu = nil, squadrons = {}, missionqueue = {}, payloads = {}, payloadcounter = 0, pointsCAP = {}, pointsTANKER = {}, pointsAWACS = {}, wingcommander = nil, } --- Squadron asset. -- @type ARMY.SquadronAsset -- @field #ARMY.Payload payload The payload of the asset. -- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flightgroup object. -- @field #string squadname Name of the squadron this asset belongs to. -- @field #number Treturned Time stamp when asset returned to the airwing. -- @extends Functional.Warehouse#WAREHOUSE.Assetitem --- ARMY class version. -- @field #string version ARMY.version="0.0.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- ToDo list ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: A lot! ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a new ARMY class object. -- @param #ARMY self -- @param #string warehousename Name of the warehouse static or unit object representing the warehouse. -- @param #string airwingname Name of the air wing, e.g. "ARMY-8". -- @return #ARMY self function ARMY:New(warehousename, airwingname) -- Inherit everything from WAREHOUSE class. local self=BASE:Inherit(self, WAREHOUSE:New(warehousename, airwingname)) -- #ARMY -- Nil check. if not self then BASE:E(string.format("ERROR: Could not find warehouse %s!", warehousename)) return nil end -- Set some string id for output to DCS.log file. self.lid=string.format("ARMY %s | ", self.alias) -- Add FSM transitions. -- From State --> Event --> To State self:AddTransition("*", "MissionRequest", "*") -- Add a (mission) request to the warehouse. self:AddTransition("*", "MissionCancel", "*") -- Cancel mission. ------------------------ --- Pseudo Functions --- ------------------------ --- Triggers the FSM event "Start". Starts the ARMY. Initializes parameters and starts event handlers. -- @function [parent=#ARMY] Start -- @param #ARMY self --- Triggers the FSM event "Start" after a delay. Starts the ARMY. Initializes parameters and starts event handlers. -- @function [parent=#ARMY] __Start -- @param #ARMY self -- @param #number delay Delay in seconds. --- Triggers the FSM event "Stop". Stops the ARMY and all its event handlers. -- @param #ARMY self --- Triggers the FSM event "Stop" after a delay. Stops the ARMY and all its event handlers. -- @function [parent=#ARMY] __Stop -- @param #ARMY self -- @param #number delay Delay in seconds. return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Start & Status ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Start AIRWING FSM. -- @param #AIRWING self function AIRWING:onafterStart(From, Event, To) -- Start parent Warehouse. self:GetParent(self).onafterStart(self, From, Event, To) -- Info. self:I(self.lid..string.format("Starting AIRWING v%s", AIRWING.version)) end --- Update status. -- @param #AIRWING self function AIRWING:onafterStatus(From, Event, To) -- Status of parent Warehouse. self:GetParent(self).onafterStatus(self, From, Event, To) local fsmstate=self:GetState() -- General info: if self.verbose>=1 then -- Count missions not over yet. local Nmissions=self:CountMissionsInQueue() -- Assets tot local Npq, Np, Nq=self:CountAssetsOnMission() local assets=string.format("%d (OnMission: Total=%d, Active=%d, Queued=%d)", self:CountAssets(), Npq, Np, Nq) -- Output. local text=string.format("%s: Missions=%d, Payloads=%d (%d), Squads=%d, Assets=%s", fsmstate, Nmissions, Npayloads, #self.payloads, #self.squadrons, assets) self:I(self.lid..text) end ------------------ -- Mission Info -- ------------------ if self.verbose>=2 then local text=string.format("Missions Total=%d:", #self.missionqueue) for i,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG local prio=string.format("%d/%s", mission.prio, tostring(mission.importance)) ; if mission.urgent then prio=prio.." (!)" end local assets=string.format("%d/%d", mission:CountOpsGroups(), mission.nassets) local target=string.format("%d/%d Damage=%.1f", mission:CountMissionTargets(), mission:GetTargetInitialNumber(), mission:GetTargetDamage()) text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s", i, mission.name, mission.type, mission.status, prio, assets, target) end self:I(self.lid..text) end ------------------- -- Squadron Info -- ------------------- if self.verbose>=3 then local text="Squadrons:" for i,_squadron in pairs(self.squadrons) do local squadron=_squadron --Ops.Squadron#SQUADRON local callsign=squadron.callsignName and UTILS.GetCallsignName(squadron.callsignName) or "N/A" local modex=squadron.modex and squadron.modex or -1 local skill=squadron.skill and tostring(squadron.skill) or "N/A" -- Squadron text text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s", squadron.name, squadron:GetState(), squadron.aircrafttype, squadron:CountAssetsInStock(), #squadron.assets, callsign, modex, skill) end self:I(self.lid..text) end -------------- -- Mission --- -------------- -- Check if any missions should be cancelled. self:_CheckMissions() -- Get next mission. local mission=self:_GetNextMission() -- Request mission execution. if mission then self:MissionRequest(mission) end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Stuff ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Check if mission is not over and ready to cancel. -- @param #AIRWING self function AIRWING:_CheckMissions() -- Loop over missions in queue. for _,_mission in pairs(self.missionqueue) do local mission=_mission --Ops.Auftrag#AUFTRAG if mission:IsNotOver() and mission:IsReadyToCancel() then mission:Cancel() end end end --- Get next mission. -- @param #AIRWING self -- @return Ops.Auftrag#AUFTRAG Next mission or *nil*. function AIRWING:_GetNextMission() -- Number of missions. local Nmissions=#self.missionqueue -- Treat special cases. if Nmissions==0 then return nil end -- Sort results table wrt prio and start time. local function _sort(a, b) local taskA=a --Ops.Auftrag#AUFTRAG local taskB=b --Ops.Auftrag#AUFTRAG return (taskA.prio0 then self:E(self.lid..string.format("ERROR: mission %s of type %s has already assets attached!", mission.name, mission.type)) end mission.assets={} -- Assign assets to mission. for i=1,mission.nassets do local asset=assets[i] --#AIRWING.SquadronAsset -- Should not happen as we just checked! if not asset.payload then self:E(self.lid.."ERROR: No payload for asset! This should not happen!") end -- Add asset to mission. mission:AddAsset(asset) end -- Now return the remaining payloads. for i=mission.nassets+1,#assets do local asset=assets[i] --#AIRWING.SquadronAsset for _,uid in pairs(gotpayload) do if uid==asset.uid then self:ReturnPayloadFromAsset(asset) break end end end return mission end end -- mission due? end -- mission loop return nil end