This commit is contained in:
Frank
2020-02-26 17:03:23 +01:00
parent 173e68676c
commit 75216793f9
4 changed files with 228 additions and 141 deletions
+178 -103
View File
@@ -16,11 +16,11 @@
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string warehousename The name of the warehouse unit/static.
-- @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.
-- @extends Functional.Warehouse#WAREHOUSE
--- Be surprised!
@@ -36,12 +36,13 @@
-- @field #AIRWING
AIRWING = {
ClassName = "AIRWING",
Debug = false,
lid = nil,
warehousename = nil,
menu = nil,
squadrons = nil,
missionqueue = {},
payloads = {},
payloadcounter = nil,
}
--- Squadron data.
@@ -62,16 +63,17 @@ AIRWING = {
--- Payload data.
-- @type AIRWING.Payload
-- @field #string unitname Name of the unit this pylon was extracted from.
-- @field #string aircrafttype Type of aircraft, which can use this payload.
-- @field #table missiontypes Mission types for which this payload can be used.
-- @field #table pylons Pylon data extracted for the unit template.
-- @field #number navail Number of available payloads of this type.
-- @field #number nreserved Number of payloads reserved.
-- @field #boolean unlimited If true, this payload is unlimited and does not get consumed.
--- AIRWING class version.
-- @field #string version
AIRWING.version="0.1.1"
AIRWING.version="0.1.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
@@ -79,11 +81,11 @@ AIRWING.version="0.1.1"
-- DONE: Add squadrons to warehouse.
-- TODO: Make special request to transfer squadrons to anther airwing (or warehouse).
-- TODO: Build mission queue.
-- TODO: Find way to start missions.
-- TODO: Check if missions are accomplished.
-- DONE: Build mission queue.
-- DONE: Find way to start missions.
-- TODO: Check if missions are accomplished.
-- TODO: Paylods as assets.
-- TODO: Cargo as assets.
-- TODO: Spawn in air or hot.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -107,6 +109,7 @@ function AIRWING:New(warehousename, airwingname)
-- Squadrons.
self.squadrons={}
self.payloadcounter=0
-- Set some string id for output to DCS.log file.
self.lid=string.format("AIRWING %s | ", self.alias)
@@ -176,65 +179,82 @@ function AIRWING:AddSquadron(SquadronName, MissionTypes, Livery, Skill)
return squadron
end
--- Add a payload to air wing resources.
--- Add a **new** payload to air wing resources.
-- @param #AIRWING self
-- @param #string AircraftType Type of aircraft which can use this payload.
-- @param #table Pylons The pylons table from UNIT:FindByName("Unitname"):GetTemplatePylons().
-- @param #string UnitName Name of the unit, the payload is extracted from.
-- @param #table MissionTypes Mission types this payload can be used for.
-- @param #number Npayloads Number of payloads to add to the airwing resources. Default 99 (which should be enough for most scenarios).
function AIRWING:AddPayload(AircraftType, Pylons, MissionTypes, Npayloads)
function AIRWING:NewPayload(UnitName, MissionTypes, Npayloads)
local payload=nil --#AIRWING.Payload
if self.payloads[AircraftType] then
payload=self.payloads[AircraftType]
else
payload={}
local unit=UNIT:FindByName(UnitName)
if unit then
local unitpayload=unit:GetTemplatePayload()
local payload={} --#AIRWING.Payload
payload.unitname=UnitName
payload.aircrafttype=unit:GetTypeName()
payload.missiontypes=MissionTypes or {}
payload.pylons=unitpayload
payload.navail=Npayloads or 99
payload.navail=Npayloads or 99
payload.missiontypes=MissionTypes
payload.aircrafttype=AircraftType
payload.pylons=Pylons
payload.nreserved=0
self.payloads[AircraftType]=payload
payload.unlimited=false
table.insert(self.payloads, payload)
end
--[[
payload={} --#AIRWING.Payload
payload.navail=Npayloads or 99
payload.missiontypes=MissionTypes
payload.aircrafttype=unit:GetTypeName()
payload.pylons=unit:GetTemplatePylons()
payload.nreserved=0
--TODO: maybe add fuel, chaff and gun?
self.payloads[UnitName]=payload
end
end
]]
end
--- Add a payload to air wing resources.
--- Fetch a payload from the airwing resources for a given unit and mission type.
-- @param #AIRWING self
-- @param #string UnitName Name of the unit from which the payload was extracted.
-- @return #AIRWING.Payload
function AIRWING:GetPayloadByName(UnitName)
return self.payloads[UnitName]
-- @param #string UnitType The type of the unit.
-- @param #string MissionType The mission type.
-- @return #AIRWING.Payload Payload table or *nil*.
function AIRWING:FetchPayloadFromStock(UnitType, MissionType)
for _,_payload in pairs(self.payloads) do
local payload=_payload --#AIRWING.Payload
-- Check right type, mission and available.
if payload.aircrafttype==UnitType and payload.navail>0 and self:CheckMissionType(MissionType, payload.missiontypes) then
-- Consume if not unlimited.
if not payload.unlimited then
payload.navail=payload.navail-1
end
-- Return a copy of the table.
return payload
end
end
return nil
end
--- Add a payload to air wing resources.
--- Return payload from asset back to stock.
-- @param #AIRWING self
-- @param #AIRWING.SquadronAsset asset The squadron asset.
-- @param #string MissionType The mission type.
-- @return #AIRWING.Payload
function AIRWING:GetPayloadForAsset(asset, MissionType)
return self.payloads[UnitName]
function AIRWING:ReturnPayloadFromAsset(asset)
local payload=asset.payload
if payload then
-- Increase count if not unlimited.
if not payload.unlimited then
payload.navail=payload.navail+1
end
-- Remove asset payload.
asset.payload=nil
else
self:E(self.lid.."ERROR: asset had no payload attached!")
end
end
@@ -346,8 +366,8 @@ function AIRWING:onafterStatus(From, Event, To)
------------------
local text=string.format("Missions Total=%d:", #self.missionqueue)
for i,_mission in pairs(self.missionqueue) do
local mission=_mission --#AIRWING.Missiondata
text=text..string.format("\n[%d] %s: Status=%s, Nassets=%d, Prio=%d, ID=%d (%s)", i, mission.type, mission.status, mission.nassets, mission.prio, mission.MID, mission.name)
local mission=_mission --Ops.Auftrag#AUFTRAG
text=text..string.format("\n[%d] %s: Status=%s, Nassets=%d, Prio=%d, ID=%d (%s)", i, mission.type, mission.status, mission.nassets, mission.prio, mission.auftragsnummer, mission.name)
end
self:I(self.lid..text)
@@ -402,7 +422,7 @@ end
--- Get next mission.
-- @param #AIRWING self
-- @return #AIRWING.Missiondata Next mission or *nil*.
-- @return Ops.Auftrag#AUFTRAG Next mission or *nil*.
function AIRWING:_GetNextMission()
-- Number of missions.
@@ -428,72 +448,94 @@ function AIRWING:_GetNextMission()
-- Look for first task that is not accomplished.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
-- Firstly, check if mission is due?
if mission.status==AUFTRAG.Status.SCHEDULED and time>=mission.Tstart then
-- Check if airwing can do the mission and gather required assets.
local can, assets=self:CanMission(mission.type, mission.nassets)
-- Debug output.
self:T3({self.lid.."Mission check:", MissionStatusScheduled=mission.status==AUFTRAG.Status.SCHEDULED, TstartPassed=time>=mission.Tstart, CanMission=can, Nassets=#assets})
-- Check that mission is still scheduled, time has passed and enough assets are available.
if mission.status==AUFTRAG.Status.SCHEDULED and time>=mission.Tstart and can then
-- TODO: check that mission.assets table is clean.
mission.assets=mission.assets or {}
-- Assign assets to mission.
for i=1,mission.nassets do
-- Check if airwing can do the mission and gather required assets.
local can, assets=self:CanMission(mission.type, mission.nassets)
local asset=assets[i] --#AIRWING.SquadronAsset
asset.payload=self:GetPayloadForAsset(asset, mission.type)
if not asset.payload then
self:E("No payload for asset! This should not happen!")
-- Debug output.
self:T3({self.lid.."Mission check:", MissionStatusScheduled=mission.status==AUFTRAG.Status.SCHEDULED, TstartPassed=time>=mission.Tstart, CanMission=can, Nassets=#assets})
-- Check that mission is still scheduled, time has passed and enough assets are available.
if can then
-- Optimize the asset selection. Most useful assets will come first.
self:_OptimizeAssetSelection(assets, mission)
-- TODO: check that mission.assets table is clean.
mission.assets=mission.assets or {}
-- Assign assets to mission.
for i=1,mission.nassets do
local asset=assets[i] --#AIRWING.SquadronAsset
asset.payload=self:FetchPayloadFromStock(asset.unittype, mission.type)
if not asset.payload then
self:E("No payload for asset! This should not happen!")
end
table.insert(mission.assets, asset)
asset.mission=mission
end
table.insert(mission.assets, asset)
asset.mission=mission
return mission
end
return mission
end
end
end -- mission due?
end -- mission loop
return nil
end
--- Get next mission.
--- Optimize chosen assets for the mission at hand.
-- @param #AIRWING self
-- @param #table assets Table of (unoptimized) assets.
-- @return #AIRWING.Missiondata Next mission or *nil*.
-- @param Ops.Auftrag#AUFTRAG Next mission or *nil*.
function AIRWING:_OptimizeAssetSelection(assets, Mission)
local TargetCoordinate=Mission:GetTargetCoordinate()
local dStock=self:GetCoordinate():Get2DDistance(TargetCoordinate)
for _,_asset in pairs(assets) do
local asset=_asset --#AIRWING.SquadronAsset
if asset.spawned then
local group=GROUP:FindByName(asset.spawngroupname)
asset.dist=group:GetCoordinate():Get2DDistance(TargetCoordinate)
else
asset.dist=dStock
end
end
-- Sort results table wrt distacance.
local function _sort(a, b)
local function optimize(a, b)
local assetA=a --#AIRWING.SquadronAsset
local assetB=b --#AIRWING.SquadronAsset
local dA=dStock
if assetA.spawned then
local group=GROUP:FindByName(assetA.spawngroupname)
dA=group:GetCoordinate():Get2DDistance(TargetCoordinate)
end
local dB=dStock
if assetB.spawned then
local group=GROUP:FindByName(assetA.spawngroupname)
dB=group:GetCoordinate():Get2DDistance(TargetCoordinate)
end
--TODO: This could be vastly improved. Need to gather ideas during testing
-- Calculate ETA? Assets on orbit missions should arrive faster even if they are further away.
-- Max speed of assets.
-- Fuel amount?
-- Range of assets?
return (dA<dB) or (dA==dB and taskA.prio<taskB.prio)
--TODO: Need to define a scoring function, which gives a weighted result of more parameters.
return (assetA.dist<assetB.dist) --or (dA==dB and taskA.prio<taskB.prio)
end
table.sort(assets, _sort)
-- Optimize order of assets.
table.sort(assets, optimize)
-- Remove distance parameter.
for _,asset in pairs(assets) do
asset.dist=nil
end
end
@@ -535,7 +577,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AIRWING.Missiondata Mission
-- @param Ops.Auftrag#AUFTRAG Mission
function AIRWING:onafterMissionNew(From, Event, To, Mission)
self:I(self.lid..string.format("New mission %s", Mission.name))
@@ -552,9 +594,27 @@ function AIRWING:onafterMissionRequest(From, Event, To, Mission)
-- Set mission status to ASSIGNED.
Mission.status=AUFTRAG.Status.ASSIGNED
--TODO: Some assets might already be spawned and even on a different mission (orbit).
-- Need to dived to set into spawned and instock assets and handle the other
-- Assets to be requested
local areq={}
for _,_asset in pairs(Mission.assets) do
local asset=_asset --#AIRWING.SquadronAsset
if asset.spawned then
else
table.insert(areq, asset)
end
end
--TODO: Check that mission prio is same as warehouse prio (small=high or the other way around).
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Mission.assets, Mission.nassets, nil, nil, Mission.prio, tostring(Mission.auftragsnummer))
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, areq, #areq, nil, nil, Mission.prio, tostring(Mission.auftragsnummer))
end
@@ -721,18 +781,26 @@ function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets)
-- Check if has already a mission assigned.
if asset.mission==nil then
---
-- This asset as no current mission
---
if asset.spawned then
-- This asset is already spawned. Let's check if it has the right payload.
if self:CheckMissionType(MissionType, asset.payload.missiontypes) then
table.insert(assets, asset)
end
else
-- Check if we got a payload and reserve it for this asset.
local reserved=self:ReservePayloadForAsset(asset, MissionType)
if reserved then
local payload=self:FetchPayloadFromStock(asset.unittype, MissionType)
if payload then
asset.payload=payload
table.insert(assets, asset)
end
end
elseif asset.mission==AUFTRAG.Type.ORBIT then
-- Check if the payload of this asset is compatible with the mission.
@@ -754,6 +822,13 @@ function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets)
return cando, assets
end
--- Check if assets for a given mission type are available.
-- @param #AIRWING self
-- @return #boolean If true, enough assets are available.
function AIRWING:IsAssetOnMission()
end
--- Check if assets for a given mission type are available.
-- @param #AIRWING self
-- @param #string MissionType Type of mission.
@@ -792,7 +867,7 @@ function AIRWING:CanMission(MissionType, Nassets)
-- Now clear all reserved payloads.
for _,_asset in pairs(Assets) do
local asset=_asset --#AIRWING.SquadronAsset
self:UnReservePayloadForAsset(asset)
self:ReturnPayloadFromAsset(asset,payload)
end
return Can, Assets
+8 -4
View File
@@ -137,8 +137,9 @@ AUFTRAG.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot
-- TODO: mission roe, rot
-- TODO: mission formation
-- TODO: Mission ROE and ROT
-- TODO: Mission formation, etc.
-- TODO: FSM events.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
@@ -157,8 +158,11 @@ function AUFTRAG:New(Type)
_AUFTRAGSNR=_AUFTRAGSNR+1
self.type=Type
self.auftragsnummer=_AUFTRAGSNR
self.status=AUFTRAG.Status.SCHEDULED
self.name=string.format("Auftrag #%d", self.auftragsnummer)
self.lid=string.format("Auftrag #%d %s | ", self.auftragsnummer, self.type)
@@ -511,8 +515,8 @@ function AUFTRAG:GetDCSMissionTask()
-- INTERCEPT Mission --
-----------------------
for _,_group in pairs(self.groupsetTargets:GetSet()) do
local TargetGroup=_group
for _,group in pairs(self.groupsetTargets:GetSet()) do
local TargetGroup=group --Wrapper.Group#GROUP
local DCStask=CONTROLLABLE.TaskAttackGroup(nil, TargetGroup, ENUMS.WeaponFlag.Auto, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit)
+41 -33
View File
@@ -30,12 +30,9 @@
-- @field #number taskcounter Running number of task ids.
-- @field #number taskcurrent ID of current task. If 0, there is no current task assigned.
-- @field #table taskenroute Enroute task of the group.
-- @field #table taskpaused Paused tasks.
-- @field #table missionqueue Queue of missions.
-- @field #number missioncounter Running number of task ids.
-- @field #FLIGHTGROUP.Mission currentmission The mission currently assigned or executed.
-- @field #boolean istanker If true, group gets enroute task tanker.
-- @field #boolean isawacs if True, group get enroute task AWACS.
-- @field #boolean eplrs If true, group will activate it's datalink.
-- @field Ops.Auftrag#AUFTRAG currentmission The mission currently assigned or executed.
-- @field Core.Set#SET_UNIT detectedunits Set of detected units.
-- @field Wrapper.Airbase#AIRBASE homebase The home base of the flight group.
-- @field Wrapper.Airbase#AIRBASE destbase The destination base of the flight group.
@@ -67,8 +64,8 @@
-- @field #number TACANchannel TACAN channel.
-- @field #string TACANmode TACAN mode, i.e. "Y" (or "X").
-- @field #string TACANmorse TACAN morse code.
-- @field #number RadioFreq Radio frequency in MHz.
-- @field #number RadioModu Radio modulation `radio.modulation.AM` or `radio.modulation.FM`.
-- @field #number RadioFreq Default Radio frequency in MHz.
-- @field #number RadioModu Default Radio modulation `radio.modulation.AM` or `radio.modulation.FM`.
-- @field #number CallsignName Call sign name.
-- @field #number CallsignNumber Call sign number.
-- @field #boolean EPLRS If true, turn EPLRS data link on.
@@ -174,15 +171,14 @@ FLIGHTGROUP = {
taskcounter = nil,
taskcurrent = nil,
taskenroute = nil,
taskpaused = {},
missionqueue = {},
missioncounter = nil,
missioncurrent = nil,
istanker = nil,
isawacs = nil,
eplrs = nil,
currentmission = nil,
detectedunits = {},
homebase = nil,
destination = nil,
destbase = nil,
homezone = nil,
destzone = nil,
attribute = nil,
actype = nil,
speedmax = nil,
@@ -500,8 +496,6 @@ function FLIGHTGROUP:New(groupname, autostart)
-- Init task counter.
self.taskcurrent=0
self.taskcounter=0
self.missioncounter=0
-- Holding flag.
self.flaghold=USERFLAG:New(string.format("%s_FlagHold", self.groupname))
@@ -660,8 +654,8 @@ function FLIGHTGROUP:RemoveTask(taskid)
end
--- Add mission to queue.
-- @param #FLIUGHTGROUP self
-- @param Ops.Auftrag#AUFTRAG.Mission Mission Mission for this group.
-- @param #FLIGHTGROUP self
-- @param Ops.Auftrag#AUFTRAG Mission Mission for this group.
-- @param Core.Point#COORDINATE WaypointCoordinate Coordinate of the mission waypoint
-- @param #number WaypointIndex The waypoint number. Default is after all current air waypoints.
-- @return #FLIGHTGROUP self
@@ -2378,6 +2372,8 @@ function FLIGHTGROUP:onafterGotoWaypoint(From, Event, To, n)
self.currentwp=n
self:UpdateRoute()
--TODO: option to re-enable waypoint tasks.
end
@@ -2714,26 +2710,37 @@ function FLIGHTGROUP:onafterTaskExecute(From, Event, To, Task)
end
--- On after TaskPause event.
--- On after "TaskPause" event. Pauses the current task
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #FLIGHTGROUP.Task Task The task.
function FLIGHTGROUP:onafterTaskPause(From, Event, To, Task)
function FLIGHTGROUP:onafterTaskPause(From, Event, To)
if self.taskcurrent>0 then
local Task=self:GetTaskCurrent()
if Task then
-- Clear all tasks.
self.group:ClearTasks()
-- Task status executing.
Task.status=1 --FLIGHTGROUP.TaskStatus.PAUSED
Task.status=FLIGHTGROUP.TaskStatus.PAUSED
end
end
--- On after "TaskUnPause" event.
-- @param #FLIGHTGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function FLIGHTGROUP:onafterTaskUnpause(From, Event, To)
--self.task
end
--- On after "TaskCancel" event. Cancels the current task.
-- @param #FLIGHTGROUP self
-- @param #string From From state.
@@ -2745,14 +2752,17 @@ function FLIGHTGROUP:onafterTaskCancel(From, Event, To)
local task=self:GetTaskCurrent()
if task then
-- Debug info.
local text=string.format("Task %s ID=%d cancelled.", task.description, task.id)
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
self:I(self.lid..text)
-- Clear tasks.
--self.group:ClearTasks()
--self.group:PopCurrentTask()
-- Set stop flag.
task.stopflag:Set(1)
self:TaskDone(task)
-- Call task done.
self:TaskDone(task)
else
local text=string.format("WARNING: No current task to cancel!")
MESSAGE:New(text, 10, "DEBUG"):ToAllIf(self.Debug)
@@ -2943,7 +2953,6 @@ end
--- Function called when a task is executed.
--@param Wrapper.Group#GROUP group Group that reached the holding zone.
--@param #FLIGHTGROUP.Mission
--@param #FLIGHTGROUP flightgroup Flight group.
--@param #FLIGHTGROUP.Task task Task.
function FLIGHTGROUP._TaskExecute(group, flightgroup, task)
@@ -2960,7 +2969,6 @@ end
--- Function called when a task is done.
--@param Wrapper.Group#GROUP group Group that reached the holding zone.
--@param #FLIGHTGROUP.Mission
--@param #FLIGHTGROUP flightgroup Flight group.
--@param #FLIGHTGROUP.Task task Task.
function FLIGHTGROUP._TaskDone(group, flightgroup, task)
@@ -2984,7 +2992,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #FLIGHTGROUP.Mission Mission The mission table.
-- @param Ops.Auftrag#AUFTRAG Mission The mission table.
function FLIGHTGROUP:onbeforeMissionStart(From, Event, To, Mission)
self:I(self.lid..string.format("FF Starting mission %s, FSM=%s, LateActivated=%s, UnControlled=%s", tostring(Mission.name), self:GetState(), tostring(self:IsLateActivated()), tostring(self:IsUncontrolled())))
@@ -3016,7 +3024,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #FLIGHTGROUP.Mission Mission The mission table.
-- @param Ops.Auftrag#AUFTRAG Mission The mission table.
function FLIGHTGROUP:onafterMissionStart(From, Event, To, Mission)
local text=string.format("Starting Mission %s", tostring(Mission.name))
@@ -3041,7 +3049,7 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #FLIGHTGROUP.Mission Mission
-- @param Ops.Auftrag#AUFTRAG Mission
function FLIGHTGROUP:onafterMissionDone(From, Event, To, Mission)
-- TODO: evaluate mission success or failure! complicated!
@@ -3751,8 +3759,8 @@ function FLIGHTGROUP:AddWaypointAir(coordinate, wpnumber, speed, updateroute)
-- Shift all mission waypoints after the inserted waypoint.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --#FLIGHTGROUP.Mission
if mission.status==AUFTRAG.Status.SCHEDULED and mission.waypoint>=wpnumber then
local mission=_mission --Ops.Auftrag#AUFTRAG
if mission.status==AUFTRAG.Status.SCHEDULED and mission.waypointindex and mission.waypointindex>=wpnumber then
mission.waypointindex=mission.waypointindex+1
end
end