This commit is contained in:
Frank
2020-02-25 16:49:54 +01:00
parent e1c602583c
commit 47e4777df0
8 changed files with 768 additions and 1743 deletions
@@ -5601,7 +5601,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol
end
if asset.payload then
unit.payload=asset.payload
unit.payload.pylons=asset.payload.pylons
end
end
@@ -1,95 +0,0 @@
--- **Ops** - (R2.5) - Aerial refueling tanker.
--
-- **Main Features:**
--
-- * Monitor flight status of elements or entire group.
-- * Create a mission queue.
-- * Inherits FLIGHTGROUP class.
--
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.AwacsGroup
-- @image OPS_AwacsGroup.png
--- AWACSGROUP class.
-- @type AWACSGROUP
-- @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.
-- @extends Ops.FlightGroup#FLIGHTGROUP
--- *To invent an airplane is nothing. To build one is something. To fly is everything.* -- Otto Lilienthal
--
-- ===
--
-- ![Banner Image](..\Presentations\CarrierAirWing\AWACSGROUP_Main.jpg)
--
-- # The AWACSGROUP Concept
--
-- # Events
--
--
-- # Tasking
--
--
-- # Examples
--
--
--
--
--
-- @field #AWACSGROUP
AWACSGROUP = {
ClassName = "AWACSGROUP",
Debug = false,
lid = nil,
awacszones = nil,
missionqueue = {},
currentmission = nil,
missioncounter = nil,
}
--- Tanker mission table.
-- @type AWACSGROUP.Mission
-- @field #string name Name of the mission.
-- @field #number mid ID of the mission.
-- @field #number tid ID of the assigned FLIGHTGROUP task.
-- @field Core.Zone#ZONE zone Mission zone.
-- @field #number duration Duration of mission.
-- @field #number altitude Altitude of orbit in meters ASL.
-- @field #number distance Length of orbit leg in meters.
-- @field #number heading Heading of orbit in degrees.
-- @field #number speed Speed in m/s.
-- @field #number Tadded Time the mission was added.
-- @field #number Tstart Start time in seconds.
-- @field #number Tstarted Time the mission was started.
-- @field #number Tstop Time the mission is stopped.
-- @field #number Tsopped Time the mission was stopped.
--- AWACSGROUP class version.
-- @field #string version
AWACSGROUP.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add orbit task.
-- TODO: Add client queue.
-- TODO: Add menu?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new AWACSGROUP object and start the FSM.
-- @param #AWACSGROUP self
-- @param #string groupname Name of the group.
-- @return #AWACSGROUP self
function AWACSGROUP:New(groupname)
end
+170 -107
View File
@@ -20,7 +20,6 @@
-- @field #table menu Table of menu items.
-- @field #table squadrons Table of squadrons.
-- @field #table missionqueue Mission queue table.
-- @field #table missioncounter Running index counting the added missions.
-- @field #table payloads Playloads for specific aircraft and mission types.
-- @extends Functional.Warehouse#WAREHOUSE
@@ -42,7 +41,6 @@ AIRWING = {
menu = nil,
squadrons = nil,
missionqueue = {},
missioncounter = nil,
payloads = {},
}
@@ -57,24 +55,18 @@ AIRWING = {
--- Squadron asset.
-- @type AIRWING.SquadronAsset
-- @field #AIRWING.Missiondata mission The assigned mission.
-- @field #AIRWING.Payload payload The payload of the asset.
-- @field Ops.Auftrag#AUFTRAG mission The assigned mission.
-- @field Ops.FlightGroup#FLIGHTGROUP flightgroup The flightgroup object.
-- @extends Functional.Warehouse#WAREHOUSE.Assetitem
--- Mission data.
-- @type AIRWING.Missiondata
-- @extends Ops.FlightGroup#FLIGHTGROUP.Mission
-- @field #number MID Mission ID.
-- @field #string squadname Name of the assigned squadron.
-- @field #table assets Assets assigned for this mission.
-- @field #number nassets Number of required assets.
--- Payload data.
-- @type AIRWING.Payload
-- @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.
--- AIRWING class version.
@@ -112,15 +104,12 @@ function AIRWING:New(warehousename, airwingname)
return nil
end
self.warehousename=warehousename
-- Squadrons.
self.squadrons={}
self.missioncounter=0
-- Set some string id for output to DCS.log file.
self.lid=string.format("AIRWING %s | ", airwingname)
self.lid=string.format("AIRWING %s | ", self.alias)
-- Add FSM transitions.
-- From State --> Event --> To State
@@ -153,9 +142,9 @@ function AIRWING:New(warehousename, airwingname)
-- Debug trace.
if false then
self.Debug=true
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
self:TraceOnOff(true)
self:TraceClass(self.ClassName)
self:TraceLevel(1)
end
return self
@@ -189,42 +178,46 @@ end
--- Add a payload to air wing resources.
-- @param #AIRWING self
-- @param #string UnitName Name of the (template) unit from which the payload is extracted.
-- @param #number Npayloads Number of payloads to add to the airwing resources. Default 99 (which should be enough for most scenarios).
-- @param #string AircraftType Type of aircraft which can use this payload.
-- @param #table Pylons The pylons table from UNIT:FindByName("Unitname"):GetTemplatePylons().
-- @param #table MissionTypes Mission types this payload can be used for.
function AIRWING:AddPayload(UnitName, Npayloads, MissionTypes)
-- @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)
local payload=self:GetPayloadByName(UnitName)
if payload then
-- Payload already exists. Increase the number.
payload.navail=payload.navail+Npayloads
--TODO: maybe check if mission types given now are different from before.
else
local unit=UNIT:FindByName(UnitName)
if unit then
payload={} --#AIRWING.Payload
payload.navail=Npayloads
payload.missiontypes=MissionTypes
payload.aircrafttype=unit:GetTypeName()
payload.pylons=unit:GetTemplatePylons()
--TODO: maybe add fuel, chaff and gun?
--table.insert(self.payloads, payload)
self.payloads[UnitName]=payload
local payload=nil --#AIRWING.Payload
if self.payloads[AircraftType] then
payload=self.payloads[AircraftType]
else
payload={}
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
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.
@@ -235,6 +228,15 @@ function AIRWING:GetPayloadByName(UnitName)
return self.payloads[UnitName]
end
--- Add a payload to air wing resources.
-- @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]
end
--- Add flight group(s) to squadron.
-- @param #AIRWING self
@@ -279,45 +281,29 @@ function AIRWING:GetSquadron(SquadronName)
end
--- Create a CAP mission.
-- @param #AIRWING self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param Core.Zone#ZONE_RADIUS ZoneCAP Circular CAP zone. Detected targets in this zone will be engaged.
-- @param #table TargetTypes Table of target types. Default {"Air"}.
-- @return Ops.FlightGroup#FLIGHTGROUP.MissionCAP The CAP mission table.
function AIRWING:CreateMissionCAP(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCap, TargetTypes)
local mission=FLIGHTGROUP.CreateMissionCAP(self, OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCap, TargetTypes)
return mission
end
--- Add mission to queue.
-- @param #AIRWING self
-- @param #AIRWING.Missiondata Mission for this group.
-- @param Ops.Auftrag#AUFTRAG Mission for this group.
-- @param #number Nassets Number of required assets for this mission. Default 1.
-- @param Core.Point#COORDINATE WaypointCoordinate Coordinate of the mission waypoint.
-- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added.
-- @param #string ClockStop Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time.
-- @param #number Prio Priority of the mission, i.e. a number between 1 and 100. Default 50.
-- @param #string Name Mission name. Default "Aerial Refueling #00X", where "#00X" is a running mission counter index starting at "#001".
-- @return #AIRWING.Missiondata The mission table.
function AIRWING:AddMission(Mission, Nassets, WaypointCoordinate, ClockStart, ClockStop, Prio, Name)
-- TODO: need to check that this call increases the correct mission counter and adds it to the mission queue.
local mission=FLIGHTGROUP.AddMission(self, Mission, WaypointCoordinate, nil, ClockStart, ClockStop, Prio, Name)
-- @return #AIRWING self
function AIRWING:AddMission(Mission, Nassets, WaypointCoordinate)
mission.nassets=Nassets or 1
-- Mission needs the correct MID.
mission.mid=nil
mission.MID=self.missioncounter
-- Number of assets.
Mission.nassets=Nassets or 1
return mission
Mission.waypointcoordinate=WaypointCoordinate
Mission.waypointindex=nil
-- Add mission to queue.
table.insert(self.missionqueue, Mission)
-- Info text.
local text=string.format("Added %s mission %s at waypoint #%s. Starting at %s. Stopping at %s",
tostring(Mission.type), tostring(Mission.name), tostring(Mission.waypointindex), UTILS.SecondsToClock(Mission.Tstart, true), Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop, true) or "never")
self:I(self.lid..text)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -332,7 +318,7 @@ function AIRWING:onafterStart(From, Event, To)
self:GetParent(self).onafterStart(self, From, Event, To)
-- Info.
self:I(self.lid..string.format("Starting AIRWING v%s %s (%s)", AIRWING.version, self.alias, self.warehousename))
self:I(self.lid..string.format("Starting AIRWING v%s", AIRWING.version))
-- Add F10 radio menu.
self:_SetMenuCoalition()
@@ -429,8 +415,8 @@ function AIRWING:_GetNextMission()
-- Sort results table wrt times they have already been engaged.
local function _sort(a, b)
local taskA=a --#AIRWING.Missiondata
local taskB=b --#AIRWING.Missiondata
local taskA=a --Ops.Auftrag#AUFTRAG
local taskB=b --Ops.Auftrag#AUFTRAG
--TODO: probably sort by prio first and then by time as only missions for T>Tstart are executed. That would ensure that the highest prio mission is carried out first!
return (taskA.Tstart<taskB.Tstart) or (taskA.Tstart==taskB.Tstart and taskA.prio<taskB.prio)
end
@@ -441,23 +427,30 @@ function AIRWING:_GetNextMission()
-- Look for first task that is not accomplished.
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --#AIRWING.Missiondata
local mission=_mission --Ops.Auftrag#AUFTRAG
-- 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==FLIGHTGROUP.MissionStatus.SCHEDULED, TstartPassed=time>=mission.Tstart, CanMission=can, Nassets=#assets})
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==FLIGHTGROUP.MissionStatus.SCHEDULED and time>=mission.Tstart and can then
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
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!")
end
table.insert(mission.assets, asset)
asset.mission=mission
end
@@ -469,6 +462,41 @@ function AIRWING:_GetNextMission()
return nil
end
--- Get next mission.
-- @param #AIRWING self
-- @param #table assets Table of (unoptimized) assets.
-- @return #AIRWING.Missiondata Next mission or *nil*.
function AIRWING:_OptimizeAssetSelection(assets, Mission)
local TargetCoordinate=Mission:GetTargetCoordinate()
local dStock=self:GetCoordinate():Get2DDistance(TargetCoordinate)
-- Sort results table wrt distacance.
local function _sort(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
return (dA<dB) or (dA==dB and taskA.prio<taskB.prio)
end
table.sort(assets, _sort)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -519,14 +547,14 @@ end
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AIRWING.Missiondata Mission The requested mission.
-- @param Ops.Auftrag#AUFTRAG Mission The requested mission.
function AIRWING:onafterMissionRequest(From, Event, To, Mission)
-- Set mission status to ASSIGNED.
Mission.status=FLIGHTGROUP.MissionStatus.ASSIGNED
Mission.status=AUFTRAG.Status.ASSIGNED
--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.MID))
self:AddRequest(self, WAREHOUSE.Descriptor.ASSETLIST, Mission.assets, Mission.nassets, nil, nil, Mission.prio, tostring(Mission.auftragsnummer))
end
@@ -548,7 +576,7 @@ function AIRWING:onafterRequest(From, Event, To, Request)
for _,_asset in pairs(assets) do
local asset=_asset --#AIRWING.SquadronAsset
asset.payload=Mission.payload
--asset.payload=Mission.payload
end
@@ -679,13 +707,9 @@ function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets)
local cando=true
local assets={}
local gotit=false
for _,canmission in pairs(Squadron.missiontypes) do
if canmission==MissionType then
gotit=true
break
end
end
-- WARNING: This assumes that all assets of the squad can do the same mission types!
-- TODO: we could make the mission type as a parameter of the assets! Then we can have heterogenious squads as well!
local gotit=self:CheckMissionType(MissionType, Squadron.missiontypes)
if not gotit then
-- This squad cannot do this mission.
@@ -697,7 +721,25 @@ function AIRWING:SquadronCanMission(Squadron, MissionType, Nassets)
-- Check if has already a mission assigned.
if asset.mission==nil then
table.insert(assets, asset)
if asset.spawned then
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
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.
if self:CheckMissionType(MissionType, asset.payload.missiontypes) then
-- TODO: check if asset actually has weapons left!
table.insert(assets, asset)
end
end
end
@@ -745,28 +787,49 @@ function AIRWING:CanMission(MissionType, Nassets)
end
end
-- Now clear all reserved payloads.
for _,_asset in pairs(Assets) do
local asset=_asset --#AIRWING.SquadronAsset
self:UnReservePayloadForAsset(asset)
end
return Can, Assets
end
--- Returns the mission for a given mission ID (MID).
--- Returns the mission for a given mission ID (Autragsnummer).
-- @param #AIRWING self
-- @param #number mid Mission ID.
-- @return #AIRWING.Missiondata Mission table.
-- @param #string MissionType The requested mission type.
-- @param #table PossibleTypes A table with possible mission types.
-- @return #boolean If true, the requested mission type is part of the possible mission types.
function AIRWING:CheckMissionType(MissionType, PossibleTypes)
for _,canmission in pairs(PossibleTypes) do
if canmission==MissionType then
return true
end
end
return false
end
--- Returns the mission for a given mission ID (Autragsnummer).
-- @param #AIRWING self
-- @param #number mid Mission ID (Auftragsnummer).
-- @return Ops.Auftrag#AUFTRAG Mission table.
function AIRWING:GetMissionByID(mid)
for _,_mission in pairs(self.missionqueue) do
local mission=_mission --#AIRWING.Missiondata
local mission=_mission --Ops.Auftrag#AUFTRAG
if mission.MID==tonumber(mid) then
if mission.auftragsnummer==tonumber(mid) then
return mission
end
end
--return self.missionqueue[mid]
return nil
end
+569
View File
@@ -0,0 +1,569 @@
--- **Ops** - Auftrag (mission) for Ops.
--
-- **Main Features:**
--
-- * Create mission for ops.
--
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.Auftrag
-- @image OPS_Auftrag.png
--- AUFTRAG class.
-- @type AUFTRAG
-- @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 #number auftragsnummer Auftragsnummer.
-- @field #string type Mission type.
-- @field #string status Mission status.
-- @field #string name Mission name.
-- @field #number mid Mission ID.
-- @field #number prio Mission priority.
-- @field #number Tstart Mission start time in seconds.
-- @field #number Tstop Mission stop time in seconds.
-- @field #number duration Mission duration in seconds.
-- @field #table DCStask DCS task structure.
-- @field Core.Point#COORDINATE waypointcoord Coordinate of the waypoint task.
-- @field Ops.FlightGroup#AUFTRAG.Task waypointtask Waypoint task.
-- @field #number waypointindex Waypoint number at which the task is executed.
-- @field #number marker F10 map marker ID.
-- @field Core.Point#COORDINATE coordOrbit Coordinate where to orbit.
-- @field #number speedOrbit Orbit speed in m/s.
-- @field #number heading Heading in degrees.
-- @field #number leg Length of leg in meters.
-- @field Core.Zone#ZONE_RADIUS zoneEngage *Circular* engagement zone.
-- @field #table typeTargets Table of target types that are engaged in the engagement zone.
-- @field Core.Point#COORDINATE coordTarget Coordinate of target location.
-- @field Core.Set#SET_GROUP groupsetTargets Set of target groups to attack.
-- @field #string squadname Name of the assigned squadron.
-- @field #table assets Assets assigned for this mission.
-- @field #number nassets Number of required assets.
-- @extends Ops.FlightGroup#AUFTRAG
--- *To invent an airplane is nothing. To build one is something. To fly is everything.* -- Otto Lilienthal
--
-- ===
--
-- ![Banner Image](..\Presentations\CarrierAirWing\AUFTRAG_Main.jpg)
--
-- # The AUFTRAG Concept
--
-- # Events
--
--
-- # Tasking
--
--
-- # Examples
--
--
--
--
--
-- @field #AUFTRAG
AUFTRAG = {
ClassName = "AUFTRAG",
Debug = false,
lid = nil,
auftragsnummer = nil,
}
--- Global mission counter.
_AUFTRAGSNR=0
--- Mission types.
-- @type AUFTRAG.Type
-- @param #string ANTISHIP Anti-ship mission.
-- @param #string AWACS AWACS mission.
-- @param #string BAI Battlefield Air Interdiction.
-- @param #string BOMBING Bombing mission.
-- @param #string CAP Combat Air Patrol.
-- @param #string CAS Close Air Support.
-- @param #string ESCORT Escort mission.
-- @param #string FACA Forward AirController airborne mission.
-- @param #string FERRY Ferry flight mission.
-- @param #string INTERCEPT Intercept mission.
-- @param #string ORBIT Orbit mission.
-- @param #string RECON Recon mission.
-- @param #string SEAD Suppression/destruction of enemy air defences.
-- @param #string STRIKE Strike mission.
-- @param #string TANKER Tanker mission.
-- @param #string TRANSPORT Transport mission.
AUFTRAG.Type={
ANTISHIP="Anti Ship",
AWACS="AWACS",
BAI="BAI",
BOMBING="Bombing",
CAP="CAP",
CAS="CAS",
ESCORT="Escort",
FACA="FAC-A",
FERRY="Ferry Flight",
INTERCEPT="Intercept",
ORBIT="Orbit",
RECON="Recon",
SEAD="SEAD",
STRIKE="Strike",
TANKER="Tanker",
TRANSPORT="Transport",
}
--- Mission status.
-- @type AUFTRAG.Status
-- @field #string SCHEDULED Mission is scheduled.
-- @field #string ASSIGNED Mission was assigned.
-- @field #string EXECUTING Mission is being executed.
-- @field #string ACCOMPLISHED Mission is accomplished.
AUFTRAG.Status={
SCHEDULED="scheduled",
ASSIGNED="assigned",
EXECUTING="executing",
ACCOMPLISHED="accomplished",
FAILED="failed",
}
--- AUFTRAG class version.
-- @field #string version
AUFTRAG.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot
-- TODO: mission roe, rot
-- TODO: mission formation
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new AUFTRAG object and start the FSM.
-- @param #AUFTRAG self
-- @param #string Type Mission type.
-- @return #AUFTRAG self
function AUFTRAG:New(Type)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #AUFTRAG
-- Increase global counter.
_AUFTRAGSNR=_AUFTRAGSNR+1
self.type=Type
self.auftragsnummer=_AUFTRAGSNR
self:AddTransition("*", "Start", "*") -- Mission has started.
self:AddTransition("*", "Update", "*") -- Mission is updated with latest data.
self:AddTransition("*", "Done", "*") -- Mission is over.
self:AddTransition("*", "Abort", "*") -- Mission is aborted.
return self
end
--- Create a new AUFTRAG object and start the FSM.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @return #AUFTRAG self
function AUFTRAG:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg)
local auftrag=AUFTRAG:New(AUFTRAG.Type.ORBIT)
auftrag.coordOrbit=OrbitCoordinate
auftrag.speedOrbit=UTILS.KnotsToMps(OrbitSpeed or 350)
auftrag.heading=Heading or 270
auftrag.leg=UTILS.NMToMeters(Leg or 10)
mission.DCStask=self:GetDCSMissionTask()
return auftrag
end
--- Create a CAP mission.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param Core.Zone#ZONE_RADIUS ZoneCAP Circular CAP zone. Detected targets in this zone will be engaged.
-- @param #table TargetTypes Table of target types. Default {"Air"}.
-- @return #AUFTRAG self
function AUFTRAG:NewCAP(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAP, TargetTypes)
-- Ensure given TargetTypes parameter is a table.
if TargetTypes then
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
end
-- Create ORBIT first.
local mission=self:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg)
-- CAP paramters.
mission.type=AUFTRAG.Type.CAP
mission.zoneEngage=ZoneCAP or ZONE_RADIUS:New("CAP Zone", OrbitCoordinate:GetVec2(), mission.leg)
mission.typeTargets=TargetTypes or {"Air"}
mission.DCStask=self:GetDCSMissionTask()
return mission
end
--- Create a CAS mission.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged.
-- @param #table TargetTypes Table of target types. Default {"Helicopters", "Ground Units", "Light armed ships"}.
-- @return #AUFTRAG self
function AUFTRAG:NewCAS(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAS, TargetTypes)
-- Ensure given TargetTypes parameter is a table.
if TargetTypes then
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
end
-- Create ORBIT first.
local mission=self:NewORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg)
-- CAS paramters.
mission.type=AUFTRAG.Type.CAS
mission.zoneEngage=ZoneCAS or ZONE_RADIUS:New("CAS Zone", OrbitCoordinate:GetVec2(), Leg)
mission.typeTargets=TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"}
mission.DCStask=self:GetDCSMissionTask()
return mission
end
--- Create a STRIKE mission. Flight will attack a specified coordinate.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE Coordinate Target Coordinate.
-- @param #number Altitude Attack altitude in feet. Default 1000.
-- @return #AUFTRAG self
function AUFTRAG:NewSTRIKE(TargetCoordinate, Altitude)
local mission=AUFTRAG:New(AUFTRAG.Type.STRIKE)
mission.coordTarget=TargetCoordinate
mission.altitude=UTILS.FeetToMeters(Altitude or 1000)
mission.DCStask=self:GetDCSMissionTask()
return mission
end
--- Create an INTERCEPT mission.
-- @param #AUFTRAG self
-- @param Core.Set#SET_GROUP TargetGroupSet The set of target groups to intercept.
-- @return #AUFTRAG self
function AUFTRAG:NewINTERCEPT(TargetGroupSet)
if TargetGroupSet:IsInstanceOf("GROUP") then
env.info("Converting group to set!")
TargetGroupSet=SET_GROUP:New():AddGroup(TargetGroupSet)
end
local mission=AUFTRAG:New(AUFTRAG.Type.INTERCEPT)
mission.groupsetTargets=TargetGroupSet
mission.groupsetTargets:FilterDeads():FilterCrashes()
mission.DCStask=self:GetDCSMissionTask()
return mission
end
--- Create a BAI mission.
-- @param #AUFTRAG self
-- @param Core.Set#SET_GROUP TargetGroupSet The set of target groups to attack. Instead of a SET an ordinary GROUP object can also be given if only a single group needs to be attacked.
-- @return #AUFTRAG self
function AUFTRAG:NewBAI(TargetGroupSet)
if TargetGroupSet:IsInstanceOf("GROUP") then
env.info("Converting group to set!")
TargetGroupSet=SET_GROUP:New():AddGroup(TargetGroupSet)
end
local mission=AUFTRAG:New(AUFTRAG.Type.BAI)
mission.groupsetTargets=TargetGroupSet
mission.groupsetTargets:FilterDeads():FilterCrashes()
mission.DCStask=self:GetDCSMissionTask()
return mission
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User API Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set mission start and stop time.
-- @param #AUFTRAG self
-- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added.
-- @param #string ClockStop (Optional) Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time.
-- @param #AUFTRAG self
function AUFTRAG:SetMissionTime(ClockStart, ClockStop)
-- Current mission time.
local Tnow=timer.getAbsTime()
-- Set start time. Default in 5 sec.
local Tstart=Tnow+5
if ClockStart and type(ClockStart)=="number" then
Tstart=Tnow+ClockStart
elseif ClockStart and type(ClockStart)=="string" then
Tstart=UTILS.ClockToSeconds(ClockStart)
end
-- Set stop time. Default nil.
local Tstop=nil
if ClockStop and type(ClockStop)=="number" then
Tstop=Tnow+ClockStop
elseif ClockStop and type(ClockStop)=="string" then
Tstop=UTILS.ClockToSeconds(ClockStop)
end
self.Tstart=Tstart
self.Tstop=Tstop
if Tstop then
self.duration=self.Tstop-self.Tstart
end
return self
end
--- Set mission priority.
-- @param #AUFTRAG self
-- @param #number Prio Priority 1=high, 100=low. Default 50
-- @param #AUFTRAG self
function AUFTRAG:SetPriority(Prio)
self.prio=Prio or 50
return self
end
--- Set mission name.
-- @param #AUFTRAG self
-- @param #string Name Name of the mission.
-- @param #AUFTRAG self
function AUFTRAG:SetName(Name)
self.name=Name or string.format("Auftragsnummer %d", self.auftragsnummer)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Count alive mission targets.
-- @param #AUFTRAG self
-- @param #number Number of alive targets.
function AUFTRAG:CountMissionTargets()
local N=0
if self.groupsetTargets then
local n=self.groupsetTargets:CountAlive()
N=N+n
end
if self.unitsetTargets then
local n=self.unitsetTargets:CountAlive()
N=N+n
end
return N
end
--- Count alive mission targets.
-- @param #AUFTRAG self
-- @param Core.Point#COORDINATE The target coordinate or nil.
function AUFTRAG:GetTargetCoordinate()
if self.groupsetTargets then
local group=self.groupsetTargets:GetFirst() --Wrapper.Group#GROUP
return group:GetCoordinate()
elseif self.unitsetTargets then
local unit=self.unitsetTargets:GetFirst() --Wraper.Unit#UNIT
return unit:GetCoordinate()
end
return nil
end
--- Get DCS task table for the given mission.
-- @param #AUFTRAG self
-- @return DCS#Task The DCS task table. If multiple tasks are necessary, this is returned as a combo task.
function AUFTRAG:GetDCSMissionTask()
local DCStasks={}
-- Create DCS task based on current self.
if self.type==AUFTRAG.Type.ANTISHIP then
----------------------
-- ANTISHIP Mission --
----------------------
local DCStask=CONTROLLABLE.TaskAttackUnit(nil, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType)
elseif self.type==AUFTRAG.Type.AWACS then
-------------------
-- AWACS Mission --
-------------------
elseif self.type==AUFTRAG.Type.BAI then
-----------------
-- BAI Mission --
-----------------
for _,_group in pairs(self.groupsetTargets:GetSet()) do
local TargetGroup=_group
local DCStask=CONTROLLABLE.TaskAttackGroup(nil, TargetGroup, ENUMS.WeaponFlag.Auto, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit)
table.insert(DCStasks, DCStask)
end
elseif self.type==AUFTRAG.Type.BOMBING then
---------------------
-- BOMBING Mission --
---------------------
elseif self.type==AUFTRAG.Type.CAP then
-----------------
-- CAP Mission --
-----------------
local CoordRaceTrack=self.coordOrbit:Translate(self.leg, self.heading, true)
local DCStask=CONTROLLABLE.TaskOrbit(nil, self.coordOrbit, self.coordOrbit.y, self.speedOrbit, CoordRaceTrack)
-- TODO! Could be added in the same task!
self:AddTaskEnrouteEngageTargetsInZone(self.zoneEngage, self.typeTargets, self.prio)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.CAS then
-----------------
-- CAS Mission --
-----------------
local CoordRaceTrack=self.coordOrbit:Translate(self.leg, self.heading, true)
local DCStask=CONTROLLABLE.TaskOrbit(nil, self.coordOrbit, self.coordOrbit.y, self.speedOrbit, CoordRaceTrack)
self:AddTaskEnrouteEngageTargetsInZone(self.zoneEngage, self.typeTargets, self.prio)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.ESCORT then
--------------------
-- ESCORT Mission --
--------------------
local DCStask=CONTROLLABLE.TaskEscort(nil, FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes)
elseif self.type==AUFTRAG.Type.FACA then
-----------------
-- FAC Mission --
-----------------
local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(nil, AttackGroup, WeaponType, Designation, Datalink)
elseif self.type==AUFTRAG.Type.FERRY then
-------------------
-- FERRY Mission --
-------------------
elseif self.type==AUFTRAG.Type.INTERCEPT then
-----------------------
-- INTERCEPT Mission --
-----------------------
for _,_group in pairs(self.groupsetTargets:GetSet()) do
local TargetGroup=_group
local DCStask=CONTROLLABLE.TaskAttackGroup(nil, TargetGroup, ENUMS.WeaponFlag.Auto, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit)
table.insert(DCStasks, DCStask)
end
elseif self.type==AUFTRAG.Type.ORBIT then
-------------------
-- ORBIT Mission --
-------------------
local CoordRaceTrack=self.coordOrbit:Translate(self.leg, self.heading, true)
local DCStask=CONTROLLABLE.TaskOrbit(nil, self.coordOrbit, self.coordOrbit.y, self.speedOrbit, CoordRaceTrack)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.RECON then
-------------------
-- RECON Mission --
-------------------
elseif self.type==AUFTRAG.Type.STRIKE then
-------------------
-- STIKE Mission --
-------------------
local Vec2=self.coordtarget:GetVec2()
local DCStask=CONTROLLABLE.TaskAttackMapObject(nil, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, self.altitude, WeaponType)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.TANKER then
--------------------
-- TANKER Mission --
--------------------
elseif self.type==AUFTRAG.Type.TRANSPORT then
else
self:E(self.lid..string.format("ERROR: Unknown mission task!"))
return nil
end
if #DCStasks==1 then
return DCStasks[1]
else
return CONTROLLABLE.TaskCombo(nil, DCStasks)
end
end
@@ -1,629 +0,0 @@
--- **Ops** - (R2.5) - Carrier Air Wing (CVW) Warehouse.
--
-- **Main Features:**
--
-- * Manage squadrons.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.CVW
-- @image OPS_CarrierAirWing.png
--- CVW class.
-- @type CVW
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string sid Class id string for output to DCS log file.
-- @field #string carriername The name of the carrier unit.
-- @field #table menu Table of menu items.
-- @field #table squadrons Table of squadrons.
-- @extends Functional.Warehouse#WAREHOUSE
--- Be surprised!
--
-- ===
--
-- ![Banner Image](..\Presentations\CarrierAirWing\CVW_Main.jpg)
--
-- # The CVW Concept
--
--
--
-- @field #CVW
CVW = {
ClassName = "CVW",
sid = nil,
carriername = nil,
menu = nil,
squadrons = nil,
taskqueue = {},
}
--- Squadron
-- @type CVW.Squadron
-- @field #string name Name of the squadron.
-- @field #table assets Assets of the squadron.
-- @field #table tasks Task(s) of the squadron.
-- @field #string livery Livery of the squadron.
-- @field DCS#Coalition.Side coalition Coalition side.
-- @field #number threadlevel Thread level of intruder.
-- @field #string threadtext Thread text.
-- @field #number category Group category.
-- @field #string categoryname Group category name.
-- @field #string typename Type name of group.
-- @field #table menu The squadron menu entries.
--- Squadron tasks.
-- @type CVW.Task
-- @param #string INTERCEPT Intercept task.
-- @param #string CAP Combat Air Patrol task.s
-- @param #string BAI Battlefield Air Interdiction task.
-- @param #string SEAD Suppression/destruction of enemy air defences.
-- @param #string STRIKE Strike task.
-- @param #string AWACS AWACS task.
-- @param #string TANKER Tanker task.
CVW.Task={
INTERCEPT="Intercept",
CAP="CAP",
BAI="BAI",
SEAD="SEAD",
STRIKE="Strike",
CAS="CAS",
AWACS="AWACS",
TANKER="Tanker",
}
--- CVW class version.
-- @field #string version
CVW.version="0.0.5"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot!
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new CVW class object for a specific aircraft carrier unit.
-- @param #CVW self
-- @param #string carriername Name of the carrier unit.
-- @param #string cvwname Name of the carrier air wing, e.g. "CVW-8".
-- @return #CVW self
function CVW:New(carriername, cvwname)
-- Inherit everything from WAREHOUSE class.
local self=BASE:Inherit(self, WAREHOUSE:New(carriername, cvwname)) -- #CVW
if not self then
BASE:E(string.format("ERROR: Could not find carrier %s!", carriername))
return nil
end
self.carriername=carriername
self.squadrons={}
-- Set some string id for output to DCS.log file.
self.sid=string.format("CVW %s |", self.carriername)
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "AirwingStatus", "*") -- CVW status update.
self:AddTransition("*", "RequestCAP", "*") -- Request CAP flight.
self:AddTransition("*", "RequestIntercept", "*") -- Request Intercept.
self:AddTransition("*", "RequestCAS", "*") -- Request CAS.
self:AddTransition("*", "RequestSEAD", "*") -- Request SEAD.
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the CVW. Initializes parameters and starts event handlers.
-- @function [parent=#CVW] Start
-- @param #CVW self
--- Triggers the FSM event "Start" after a delay. Starts the CVW. Initializes parameters and starts event handlers.
-- @function [parent=#CVW] __Start
-- @param #CVW self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the CVW and all its event handlers.
-- @param #CVW self
--- Triggers the FSM event "Stop" after a delay. Stops the CVW and all its event handlers.
-- @function [parent=#CVW] __Stop
-- @param #CVW self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "SkipperStatus".
-- @function [parent=#CVW] AirwingStatus
-- @param #CVW self
--- Triggers the FSM event "SkipperStatus" after a delay.
-- @function [parent=#CVW] __AirwingStatus
-- @param #CVW self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "RequestCAP".
-- @function [parent=#CVW] RequestCAP
-- @param #CVW self
-- @param Core.Point#COORDINATE coordinate
-- @param #number altitude Altitude
-- @param #number leg Race track length.
-- @param #number heading Heading in degrees.
-- @param #number speed Speed in knots.
-- @param #CVW.Squadron squadron Explicitly request a specific squadron.
-- Debug trace.
if true then
self.Debug=true
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Optimized F10 radio menu for a single carrier. The menu entries will be stored directly under F10 Other/Skipper/ and not F10 Other/Skipper/"Carrier Alias"/.
-- **WARNING**: If you use this with two CVW objects/carriers, the radio menu will be screwed up!
-- @param #CVW self
-- @param #boolean switch If true or nil single menu is enabled. If false, menu is for multiple carriers in the mission.
-- @return #CVW self
function CVW:SetMenuSingleCarrier(switch)
if switch==true or switch==nil then
self.menusingle=true
else
self.menusingle=false
end
return self
end
--- Add a squadron to the carrier air wing.
-- @param #CVW self
-- @param #string name Name of the squadron, e.g. "VFA-37".
-- @param #table tasks Table of tasks the squadron is supposed to do.
-- @param #string livery The livery for all added flight group. Default is the livery of the template group.
-- @return #CVW.Squadron The squadron object.
function CVW:AddSquadron(name, tasks, livery)
local squadron={} --#CVW.Squadron
squadron.name=name
squadron.assets={}
squadron.tasks=tasks or {}
squadron.livery=livery
table.insert(self.squadrons, squadron)
return squadron
end
--- Add flight group(s) to squadron.
-- @param #CVW self
-- @param #CVW.Squadron squadron The squadron object.
-- @param Wrapper.Group#GROUP flightgroup The flight group object.
-- @param #number ngroups Number of groups to add.
-- @return #CVW self
function CVW:AddFlightToSquadron(squadron, flightgroup, ngroups)
local text=string.format("FF Adding asset %s to squadron %s", flightgroup:GetName(), squadron.name)
env.info(text)
function self:OnAfterNewAsset(From, Event, To, asset, assignment)
local text=string.format("FF assignment=%s, squadron=%s", assignment, squadron.name)
env.info(text)
if assignment==squadron.name then
table.insert(squadron.assets, asset)
end
end
self:AddAsset(flightgroup, ngroups, nil, nil, nil, nil, nil, {squadron.livery}, squadron.name)
return self
end
--- Get squadron by name
-- @param #CVW self
-- @param #string name Name of the squadron, e.g. "VFA-37".
-- @return #CVW self or nil.
function CVW:GetSquadron(name)
for _,_squadron in pairs(self.squadrons) do
if _squadron.name==name then
return _squadron
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start CVW FSM.
-- @param #CVW self
function CVW:onafterStart(From, Event, To)
-- Start parent Warehouse.
self:GetParent(self).onafterStart(self, From, Event, To)
-- Info.
self:I(self.sid..string.format("Starting CVW v%s for carrier %s", CVW.version, self.carriername))
-- Add F10 radio menu.
self:_SetMenuCoalition()
for _,_squadron in pairs(self.squadrons) do
local squadron=_squadron --#CVW.Squadron
self:_AddSquadonMenu(squadron)
end
-- Init status updates.
self:__AirwingStatus(-1)
end
--- Update status.
-- @param #CVW self
function CVW:onafterAirwingStatus(From, Event, To)
local fsmstate=self:GetState()
-- Info text.
local text=string.format("State %s", fsmstate)
self:I(self.sid..text)
local text="Squadrons:"
for i,_squadron in pairs(self.squadrons) do
local squadron=_squadron --#CVW.Squadron
text=text..string.format("\n %s", squadron.name)
for j,_asset in pairs(squadron.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
local assignment=asset.assignment or "none"
local name=asset.templatename
local task=asset.CVWtask or "none"
local spawned=tostring(asset.spawned)
local groupname=asset.spawngroupname
local group=nil --Wrapper.Group#GROUP
local typename=asset.unittype
local fuel=100
if groupname then
group=GROUP:FindByName(groupname)
fuel=group:GetFuelMin()
end
text=text..string.format("\n-[%d] %s*%d: spawned=%s, task=%s, fuel=%d", j, typename, asset.nunits, task)
end
end
self:__AirwingStatus(-30)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Events
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Request CAP.
-- @param #CVW self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Core.Point#COORDINATE coordinate
-- @param #number altitude Altitude
-- @param #number leg Race track length.
-- @param #number heading Heading in degrees.
-- @param #number speed Speed in knots.
-- @param #CVW.Squadron squadron Explicitly request a specific squadron.
function CVW:onafterRequestCAP(From, Event, To, coordinate, altitude, leg, heading, speed, squadron)
local n=self:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, squadron.name, false)
if n>0 then
self:__AddRequest(1, self, WAREHOUSE.Descriptor.ASSIGNMENT, squadron.name, 1, nil, nil, nil, CVW.Task.CAP)
function self:OnAfterSelfRequest(From,Event,To,Groupset,Request)
local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem
local groupset=Groupset --Core.Set#SET_GROUP
for _,_asset in pairs(request.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
local a=asset.cargobay
end
if Request.assignment==CVW.Task.CAP then
for _,_group in pairs(groupset:GetSet()) do
local group=_group --Wrapper.Group#GROUP
self:LaunchCAP(group, coordinate, altitude, leg, speed, heading)
end
end
end
else
MESSAGE:New("No CAP planes currently available", 5, "CVW"):ToCoalition(self:GetCoalition())
end
end
--- Launch a CAP flight.
-- @param #CVW self
-- @param Wrapper.Group#GROUP group Flight group.
-- @param Core.Point#COORDINATE capcoord Coordinate of CAP.
-- @param #number alt Altitude in feet. Default 20000 ft.
-- @param #number leg Length of race track pattern leg. Default 15 NM.
-- @param #number heading Heading of race track in degrees. Default 180° i.e. from North to South.
-- @param #number speed Speed in knots.
function CVW:LaunchCAP(group, capcoord, alt, leg, heading, speed)
alt=UTILS.FeetToMeters(alt or 20000)
speed=speed or 350
leg=leg or 15
-- Task orbit.
local taskOrbit=group:TaskOrbit(capcoord, alt, UTILS.KnotsToMps(speed+50), capcoord:Translate(UTILS.NMToMeters(leg), heading))
local wp={}
wp[1]=self:GetCoordinate():WaypointAirTakeOffParking()
wp[2]=self:GetCoordinate():SetAltitude(alt):WaypointAirTurningPoint(COORDINATE.WaypointAltType.BARO, UTILS.KnotsToKmph(speed), {taskOrbit}, "CAP")
-- Start uncontrolled group.
group:StartUncontrolled()
--airboss:SetExcludeAI()
-- Route group.
group:Route(wp)
end
--- Create a new task name.
-- @param #CVW self
-- @param #string task Task of type @{#CVW.Task}.
-- @string Task name, e.g. Task0001_CAP, Task0002_BAI, Task0003_INTERCEPT, ...
function CVW:_NewTaskName(task)
self.ntasks=self.ntasks+1
local taskname=string.format("Task#%04d_%s", self.ntasks, task)
return taskname
end
--- Request Intercept.
-- @param #CVW self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param Wrapper.Group#GROUP bandits Group of bandits to intercept.
-- @param #CVW.Squadron squadron Explicitly request a specific squadron.
function CVW:onafterRequestIntercept(From, Event, To, bandits, squadron)
local n=self:GetNumberOfAssets(WAREHOUSE.Descriptor.ASSIGNMENT, squadron.name, false)
if n>0 then
self:__AddRequest(1, self, WAREHOUSE.Descriptor.ASSIGNMENT, squadron.name, 1, nil, nil, nil, CVW.Task.INTERCEPT)
function self:OnAfterSelfRequest(From,Event,To,Groupset,Request)
local request=Request --Functional.Warehouse#WAREHOUSE.Pendingitem
local groupset=Groupset --Core.Set#SET_GROUP
if Request.assignment==CVW.Task.INTERCEPT then
for _,_group in pairs(groupset:GetSet()) do
local group=_group --Wrapper.Group#GROUP
self:LaunchIntercept(group)
end
end
end
else
MESSAGE:New("No INTERCEPT fighters currently available", 5, "CVW"):ToCoalition(self:GetCoalition())
end
end
function CVW:OnAfterSelfRequest(From,Event,To,groupset,request)
end
--- Launch a flight group to intercept an intruder.
-- @param #CVW self
-- @param Wrapper.Group#GROUP group Interceptor flight group.
-- @param Wrapper.Group#GROUP bandits Bandit group.
function CVW:LaunchIntercept(group, bandit)
-- Task orbit.
local tasks={}
for _,unit in pairs(bandit:GetUnits()) do
tasks[#tasks+1]=group:TaskAttackUnit(unit)
end
local speed=group:GetSpeedMax()
local altitude=bandit:GetAltitude()
-- Create waypoints.
local wp={}
wp[1]=self:GetCoordinate():WaypointAirTakeOffParking()
wp[2]=self:GetCoordinate():SetAltitude(altitude):WaypointAirTurningPoint(COORDINATE.WaypointAltType.BARO, speed, {tasks}, "Intercept")
-- Start uncontrolled group.
group:StartUncontrolled()
--airboss:SetExcludeAI()
-- Route group
group:Route(wp)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Menu Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Patrol carrier.
-- @param #CVW self
-- @return #CVW self
function CVW:_SetMenuCoalition()
-- Get coalition.
local Coalition=self:GetCoalition()
-- Init menu table.
self.menu=self.menu or {}
-- Init menu coalition table.
self.menu[Coalition]=self.menu[Coalition] or {}
-- Shortcut.
local menu=self.menu[Coalition]
if self.menusingle then
-- F10/Skipper/...
if not menu.CVW then
menu.CVW=MENU_COALITION:New(Coalition, "CVW")
end
else
-- F10/Skipper/<Carrier Alias>/...
if not menu.Root then
menu.Root=MENU_COALITION:New(Coalition, "CVW")
end
menu.CVW=MENU_COALITION:New(Coalition, self.alias, menu.Root)
end
-------------------
-- Squadron Menu --
-------------------
menu.Squadron={}
menu.Squadron.Main= MENU_COALITION:New(Coalition, "Squadrons", menu.CVW)
menu.Warehouse={}
menu.Warehouse.Main = MENU_COALITION:New(Coalition, "Warehouse", menu.CVW)
menu.Warehouse.Reports = MENU_COALITION_COMMAND:New(Coalition, "Reports On/Off", menu.Warehouse.Main, self.WarehouseReportsToggle, self)
menu.Warehouse.Assets = MENU_COALITION_COMMAND:New(Coalition, "Report Assets", menu.Warehouse.Main, self.ReportWarehouseStock, self)
menu.ReportSquadrons = MENU_COALITION_COMMAND:New(Coalition, "Report Squadrons", menu.CVW, self.ReportSquadrons, self)
end
--- Report squadron status.
-- @param #CVW self
function CVW:ReportSquadrons()
local text="Squadron Report:"
for i,_squadron in pairs(self.squadrons) do
local squadron=_squadron --#CVW.Squadron
local name=squadron.name
local nspawned=0
local nstock=0
for _,_asset in pairs(squadron.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
--env.info(string.format("Asset name=%s", asset.spawngroupname))
local n=asset.nunits
if asset.spawned then
nspawned=nspawned+n
else
nstock=nstock+n
end
end
text=string.format("\n%s: AC on duty=%d, in stock=%d", name, nspawned, nstock)
end
self:I(self.sid..text)
MESSAGE:New(text, 10, "CVW", true):ToCoalition(self:GetCoalition())
end
--- Add sub menu for this intruder.
-- @param #CVW self
-- @param #CVW.Squadron squadron The squadron data.
function CVW:_AddSquadonMenu(squadron)
local Coalition=self:GetCoalition()
local root=self.menu[Coalition].Squadron.Main
local menu=MENU_COALITION:New(Coalition, squadron.name, root)
MENU_COALITION_COMMAND:New(Coalition, "Report", menu, self._ReportSq, self, squadron)
MENU_COALITION_COMMAND:New(Coalition, "Launch CAP", menu, self._LaunchCAP, self, squadron)
-- Set menu.
squadron.menu=menu
end
--- Report squadron status.
-- @param #CVW self
-- @param #CVW.Squadron squadron The squadron object.
function CVW:_ReportSq(squadron)
local text=string.format("%s: %s assets:", squadron.name, tostring(squadron.categoryname))
for i,_asset in pairs(squadron.assets) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
text=text..string.format("%d.) ")
end
end
--- Warehouse reports on/off.
-- @param #CVW self
function CVW:WarehouseReportsToggle()
self.Report=not self.Report
MESSAGE:New(string.format("Warehouse reports are now %s", tostring(self.Report)), 10, "CVW", true):ToCoalition(self:GetCoalition())
end
--- Report warehouse stock.
-- @param #CVW self
function CVW:ReportWarehouseStock()
local text=self:_GetStockAssetsText(false)
MESSAGE:New(text, 10, "CVW", true):ToCoalition(self:GetCoalition())
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+27 -497
View File
@@ -316,109 +316,6 @@ FLIGHTGROUP.TaskType={
-- @field #number WaypointIndex Waypoint number at which the enroute task is added.
--- Mission types.
-- @type FLIGHTGROUP.MissionType
-- @param #string ANTISHIP Anti-ship mission.
-- @param #string AWACS AWACS mission.
-- @param #string BAI Battlefield Air Interdiction.
-- @param #string BOMBING Bombing mission.
-- @param #string CAP Combat Air Patrol.
-- @param #string CAS Close Air Support.
-- @param #string ESCORT Escort mission.
-- @param #string FACA Forward AirController airborne mission.
-- @param #string FERRY Ferry flight mission.
-- @param #string INTERCEPT Intercept mission.
-- @param #string ORBIT Orbit mission.
-- @param #string RECON Recon mission.
-- @param #string SEAD Suppression/destruction of enemy air defences.
-- @param #string STRIKE Strike mission.
-- @param #string TANKER Tanker mission.
-- @param #string TRANSPORT Transport mission.
FLIGHTGROUP.MissionType={
ANTISHIP="Anti Ship",
AWACS="AWACS",
BAI="BAI",
BOMBING="Bombing",
CAP="CAP",
CAS="CAS",
ESCORT="Escort",
FACA="FAC-A",
FERRY="Ferry Flight",
INTERCEPT="Intercept",
ORBIT="Orbit",
RECON="Recon",
SEAD="SEAD",
STRIKE="Strike",
TANKER="Tanker",
TRANSPORT="Transport",
}
--- Mission status.
-- @type FLIGHTGROUP.MissionStatus
-- @field #string SCHEDULED Mission is scheduled.
-- @field #string ASSIGNED Mission was assigned.
-- @field #string EXECUTING Mission is being executed.
-- @field #string ACCOMPLISHED Mission is accomplished.
FLIGHTGROUP.MissionStatus={
SCHEDULED="scheduled",
ASSIGNED="assigned",
EXECUTING="executing",
ACCOMPLISHED="accomplished",
FAILED="failed",
}
--- Generic mission.
-- @type FLIGHTGROUP.Mission
-- @field #string type Mission Type.
-- @field #string status Mission status.
-- @field #string name Mission name.
-- @field #number mid Mission ID.
-- @field #number prio Mission priority.
-- @field #number Tstart Mission start time in seconds.
-- @field #number Tstop Mission stop time in seconds.
-- @field #number duration Mission duration in seconds.
-- @field #table DCStask DCS task structure.
-- @field Core.Point#COORDINATE waypointcoord Coordinate of the waypoint task.
-- @field #FLIGHTGROUP.Task waypointtask Waypoint task.
-- @field #number waypointindex Waypoint number at which the task is executed.
-- @field #number marker F10 map marker ID.
--- ORBIT mission. Basis for other missions like TANKER, AWACS, CAP.
-- @type FLIGHTGROUP.MissionORBIT
-- @field Core.Point#COORDINATE coordOrbit Coordinate where to orbit.
-- @field #number speedOrbit Orbit speed in m/s.
-- @field #number heading Heading in degrees.
-- @field #number leg Length of leg in meters.
-- @extends #FLIGHTGROUP.Mission
--- CAP mission.
-- @type FLIGHTGROUP.MissionCAP
-- @field Core.Zone#ZONE_RADIUS zoneEngage *Circular* engagement zone.
-- @field #table typeTargets Table of target types that are engaged in the engagement zone. Default is {"Air"}.
-- @extends #FLIGHTGROUP.MissionORBIT
--- CAS mission.
-- @type FLIGHTGROUP.MissionCAS
-- @field Core.Zone#ZONE_RADIUS zoneEngage *Circular* engagement zone.
-- @field #table typeTargets Table of target types that are engaged in the engagement zone. Default is {"LightArmoredUnits"}.
-- @extends #FLIGHTGROUP.MissionORBIT
--- STRIKE mission.
-- @type FLIGHTGROUP.MissionSTRIKE
-- @field Core.Point#COORDINATE coordTarget Coordinate of target location.
-- @field #number altitude Attack altitude in meters.
-- @extends #FLIGHTGROUP.Mission
--- BAI mission.
-- @type FLIGHTGROUP.MissionBAI
-- @field Core.Set#SET_GROUP groupsetTargets Set of target groups to attack.
-- @extends #FLIGHTGROUP.Mission
--- INTERCEPT mission.
-- @type FLIGHTGROUP.MissionINTERCEPT
-- @field Core.Set#SET_GROUP groupsetTargets Set of target groups to intercept.
-- @extends #FLIGHTGROUP.Mission
--- FLIGHTGROUP class version.
-- @field #string version
FLIGHTGROUP.version="0.3.2"
@@ -429,7 +326,6 @@ FLIGHTGROUP.version="0.3.2"
-- DONE: Add tasks.
-- DONE: Waypoints, read, add, insert, detour.
-- DONE: Get ammo.
-- DONE: Get pylons.
-- DONE: Fuel threshhold ==> RTB.
@@ -532,7 +428,9 @@ function FLIGHTGROUP:New(groupname, autostart)
self:AddTransition("*", "MissionUpdate", "*") -- Mission is updated with latest data.
self:AddTransition("*", "MissionDone", "*") -- Mission is over.
self:AddTransition("*", "MissionAbort", "*") -- Mission is aborted.
self:AddTransition("Airborne", "EngageTarget", "Engaging") -- Pause current task.
self:AddTransition("Engaging", "Disengage", "Airborne") -- Pause current task.
self:AddTransition("*", "ElementSpawned", "*") -- An element was spawned.
self:AddTransition("*", "ElementParking", "*") -- An element is parking.
@@ -761,6 +659,28 @@ function FLIGHTGROUP:RemoveTask(taskid)
end
--- Add mission to queue.
-- @param #FLIUGHTGROUP self
-- @param Ops.Auftrag#AUFTRAG.Mission 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
function FLIGHTGROUP:AddMission(Mission, WaypointCoordinate, WaypointIndex)
Mission.waypointcoordinate=WaypointCoordinate
Mission.waypointindex=WaypointIndex
-- Add mission to queue.
table.insert(self.missionqueue, Mission)
-- Info text.
local text=string.format("Added %s mission %s at waypoint #%s. Starting at %s. Stopping at %s",
tostring(Mission.type), tostring(Mission.name), tostring(Mission.waypointindex), UTILS.SecondsToClock(Mission.Tstart, true), Mission.Tstop and UTILS.SecondsToClock(Mission.Tstop, true) or "never")
self:I(self.lid..text)
return self
end
--- Set squadron the flight group belongs to.
-- @param #FLIGHTGROUP self
-- @param #boolean If true or nil, group get enroute task tanker. If false, not.
@@ -1180,384 +1100,7 @@ function FLIGHTGROUP:Route(waypoints)
return self
end
--- Create an ORBIT mission. This is also used as template for other mission types, e.g. TANKER, AWACS, CAP.
-- @param #FLIGHTGROUP self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @return #FLIGHTGROUP.MissionORBIT The ORBIT mission table.
function FLIGHTGROUP:CreateMissionORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg)
local mission={} --#FLIGHTGROUP.MissionORBIT
mission.type=FLIGHTGROUP.MissionType.ORBIT
mission.coordOrbit=OrbitCoordinate
mission.speedOrbit=UTILS.KnotsToMps(OrbitSpeed or 350)
mission.heading=Heading or 270
mission.leg=UTILS.NMToMeters(Leg or 10)
return mission
end
--- Create a CAP mission.
-- @param #FLIGHTGROUP self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param Core.Zone#ZONE_RADIUS ZoneCAP Circular CAP zone. Detected targets in this zone will be engaged.
-- @param #table TargetTypes Table of target types. Default {"Air"}.
-- @return #FLIGHTGROUP.MissionCAP The CAP mission table.
function FLIGHTGROUP:CreateMissionCAP(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAP, TargetTypes)
local mission=self:CreateMissionORBIT(OrbitCoordinate, OrbitSpeed, Heading, Leg) --#FLIGHTGROUP.MissionCAP
mission.type=FLIGHTGROUP.MissionType.CAP
mission.zoneEngage=ZoneCAP or ZONE_RADIUS:New("CAP Zone", OrbitCoordinate:GetVec2(), mission.leg)
mission.typeTargets=TargetTypes or {"Air"}
return mission
end
--- Create a CAS mission.
-- @param #FLIGHTGROUP self
-- @param Core.Point#COORDINATE OrbitCoordinate Where to orbit. Altitude is also taken from the coordinate.
-- @param #number OrbitSpeed Orbit speed in knots. Default 350 kts.
-- @param #number Heading Heading of race-track pattern in degrees. Default 270 (East to West).
-- @param #number Leg Length of race-track in NM. Default 10 NM.
-- @param Core.Zone#ZONE_RADIUS ZoneCAS Circular CAS zone. Detected targets in this zone will be engaged.
-- @param #table TargetTypes Table of target types. Default {"Helicopters", "Ground Units", "Light armed ships"}.
-- @return #FLIGHTGROUP.MissionCAP The CAP mission table.
function FLIGHTGROUP:CreateMissionCAS(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAS, TargetTypes)
-- Ensure given TargetTypes parameter is a table.
if TargetTypes then
if type(TargetTypes)~="table" then
TargetTypes={TargetTypes}
end
end
TargetTypes=TargetTypes or {"Helicopters", "Ground Units", "Light armed ships"}
ZoneCAS=ZoneCAS or ZONE_RADIUS:New("CAS Zone", OrbitCoordinate:GetVec2(), Leg)
local mission=self:CreateMissionCAP(OrbitCoordinate, OrbitSpeed, Heading, Leg, ZoneCAS, TargetTypes) --#FLIGHTGROUP.MissionCAP
return mission
end
--- Create a STRIKE mission. Flight will attack a specified coordinate.
-- @param #FLIGHTGROUP self
-- @param Core.Point#COORDINATE Coordinate Target Coordinate.
-- @param #number Altitude Attack altitude in feet. Default 1000.
-- @return #FLIGHTGROUP.MissionSTRIKE The STRIKE mission table.
function FLIGHTGROUP:CreateMissionSTRIKE(TargetCoordinate, Altitude)
local mission={} --#FLIGHTGROUP.MissionSTRIKE
mission.type=FLIGHTGROUP.MissionType.STIKE
mission.coordTarget=TargetCoordinate
mission.altitude=UTILS.FeetToMeters(Altitude or 1000)
return mission
end
--- Create an INTERCEPT mission.
-- @param #FLIGHTGROUP self
-- @param Core.Set#SET_GROUP TargetGroupSet The set of target groups to intercept.
function FLIGHTGROUP:CreateMissionINTERCEPT(TargetGroupSet)
local mission={} --#FLIGHTGROUP.MissionINTERCEPT
if TargetGroupSet:IsInstanceOf("GROUP") then
env.info("Converting group to set!")
TargetGroupSet=SET_GROUP:New():AddGroup(TargetGroupSet)
end
mission.type=FLIGHTGROUP.MissionType.INTERCEPT
mission.groupsetTargets=TargetGroupSet
mission.groupsetTargets:FilterDeads():FilterCrashes()
return mission
end
--- Create a BAI mission.
-- @param #FLIGHTGROUP self
-- @param Core.Set#SET_GROUP TargetGroupSet The set of target groups to attack. Instead of a SET an ordinary GROUP object can also be given if only a single group needs to be attacked.
function FLIGHTGROUP:CreateMissionBAI(TargetGroupSet)
local mission={} --#FLIGHTGROUP.MissionBAI
if TargetGroupSet:IsInstanceOf("GROUP") then
env.info("Converting group to set!")
TargetGroupSet=SET_GROUP:New():AddGroup(TargetGroupSet)
end
mission.type=FLIGHTGROUP.MissionType.BAI
mission.groupsetTargets=TargetGroupSet
mission.groupsetTargets:FilterDeads():FilterCrashes()
return mission
end
--- Create a BAI mission.
-- @param #FLIGHTGROUP self
-- @param #FLIGHTGROUP.Mission mission The mission.
-- @param #number Number of alive targets.
function FLIGHTGROUP:CountMissionTargets(mission)
local N=0
if mission.groupsetTargets then
local n=mission.groupsetTargets:CountAlive()
N=N+n
end
if mission.unitsetTargets then
local n=mission.unitsetTargets:CountAlive()
N=N+n
end
return N
end
--- Add mission to queue.
-- @param #FLIGHTGROUP self
-- @param #FLIGHTGROUP.Mission 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.
-- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time. Default is 5 seconds after mission was added.
-- @param #string ClockStop Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time.
-- @param #number Prio Priority of the mission, i.e. a number between 1 and 100. Default 50.
-- @param #string Name Mission name. Default "Aerial Refueling #00X", where "#00X" is a running mission counter index starting at "#001".
-- @return #FLIGHTGROUP.Mission The mission table. This is a deep copy of the input mission with adjusted parameters for the mission type.
function FLIGHTGROUP:AddMission(Mission, WaypointCoordinate, WaypointIndex, ClockStart, ClockStop, Prio, Name)
-- Increase mission counter.
self.missioncounter=self.missioncounter+1
-- Create copy of Mission template.
local mission=UTILS.DeepCopy(Mission) --#FLIGHTGROUP.Mission
-- Current mission time.
local Tnow=timer.getAbsTime()
-- Set start time. Default in 5 sec.
local Tstart=Tnow+5
if ClockStart and type(ClockStart)=="number" then
Tstart=Tnow+ClockStart
elseif ClockStart and type(ClockStart)=="string" then
Tstart=UTILS.ClockToSeconds(ClockStart)
end
-- Set stop time. Default nil.
local Tstop=nil
if ClockStop and type(ClockStop)=="number" then
Tstop=Tnow+ClockStop
elseif ClockStop and type(ClockStop)=="string" then
Tstop=UTILS.ClockToSeconds(ClockStop)
end
-- Make mission table.
mission.mid=self.missioncounter
mission.waypointcoord=WaypointCoordinate
mission.waypointindex=WaypointIndex
mission.waypointtask=nil
mission.Tstart=Tstart
mission.Tstop=Tstop
mission.status=FLIGHTGROUP.MissionStatus.SCHEDULED
if Tstop then
mission.duration=mission.Tstop-mission.Tstart
end
mission.prio=Prio or 50
mission.name=Name or string.format("%s mission ID%03d", mission.type, mission.mid)
mission.DCStask=self:GetDCSMissionTask(mission)
-- Add mission to queue.
table.insert(self.missionqueue, mission)
-- Info text.
local text=string.format("Added %s mission %s at waypoint #%s. Starting at %s. Stopping at %s",
tostring(mission.type), tostring(mission.name), tostring(mission.waypointindex), UTILS.SecondsToClock(mission.Tstart, true), mission.Tstop and UTILS.SecondsToClock(mission.Tstop, true) or "never")
self:I(self.lid..text)
return mission
end
--- Get DCS task table for the given mission.
-- @param #FLIGHTGROUP self
-- @param #FLIGHTGROUP.Mission Mission Mission for this group.
-- @return DCS#Task The DCS task table. If multiple tasks are necessary, this is returned as a combo task.
function FLIGHTGROUP:GetDCSMissionTask(Mission)
local DCStasks={}
-- Create DCS task based on current mission.
if Mission.type==FLIGHTGROUP.MissionType.ANTISHIP then
----------------------
-- ANTISHIP Mission --
----------------------
local DCStask=CONTROLLABLE.TaskAttackUnit(self.group, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType)
elseif Mission.type==FLIGHTGROUP.MissionType.AWACS then
-------------------
-- AWACS Mission --
-------------------
elseif Mission.type==FLIGHTGROUP.MissionType.BAI then
-----------------
-- BAI Mission --
-----------------
local mission=Mission --#FLIGHTGROUP.MissionBAI
for _,_group in pairs(mission.groupsetTargets:GetSet()) do
local TargetGroup=_group
local DCStask=CONTROLLABLE.TaskAttackGroup(self.group, TargetGroup, ENUMS.WeaponFlag.Auto, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit)
table.insert(DCStasks, DCStask)
end
elseif Mission.type==FLIGHTGROUP.MissionType.BOMBING then
---------------------
-- BOMBING Mission --
---------------------
elseif Mission.type==FLIGHTGROUP.MissionType.CAP then
-----------------
-- CAP Mission --
-----------------
local mission=Mission --#FLIGHTGROUP.MissionCAP
local CoordRaceTrack=mission.coordOrbit:Translate(mission.leg, mission.heading, true)
local DCStask=CONTROLLABLE.TaskOrbit(self.group, mission.coordOrbit, mission.coordOrbit.y, mission.speedOrbit, CoordRaceTrack)
self:AddTaskEnrouteEngageTargetsInZone(mission.zoneEngage, mission.typeTargets, mission.prio)
table.insert(DCStasks, DCStask)
elseif Mission.type==FLIGHTGROUP.MissionType.CAS then
-----------------
-- CAS Mission --
-----------------
local mission=Mission --#FLIGHTGROUP.MissionCAS
local CoordRaceTrack=mission.coordOrbit:Translate(mission.leg, mission.heading, true)
local DCStask=CONTROLLABLE.TaskOrbit(self.group, mission.coordOrbit, mission.coordOrbit.y, mission.speedOrbit, CoordRaceTrack)
self:AddTaskEnrouteEngageTargetsInZone(mission.zoneEngage, mission.typeTargets, mission.prio)
table.insert(DCStasks, DCStask)
elseif Mission.type==FLIGHTGROUP.MissionType.ESCORT then
--------------------
-- ESCORT Mission --
--------------------
local DCStask=CONTROLLABLE.TaskEscort(self.group, FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes)
elseif Mission.type==FLIGHTGROUP.MissionType.FACA then
-----------------
-- FAC Mission --
-----------------
local DCStask=CONTROLLABLE.TaskFAC_AttackGroup(self.group, AttackGroup, WeaponType, Designation, Datalink)
elseif Mission.type==FLIGHTGROUP.MissionType.FERRY then
-------------------
-- FERRY Mission --
-------------------
elseif Mission.type==FLIGHTGROUP.MissionType.INTERCEPT then
-----------------------
-- INTERCEPT Mission --
-----------------------
local mission=Mission --#FLIGHTGROUP.MissionINTERCEPT
for _,_group in pairs(mission.groupsetTargets:GetSet()) do
local TargetGroup=_group
local DCStask=CONTROLLABLE.TaskAttackGroup(self.group, TargetGroup, ENUMS.WeaponFlag.Auto, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit)
table.insert(DCStasks, DCStask)
end
elseif Mission.type==FLIGHTGROUP.MissionType.ORBIT then
-------------------
-- ORBIT Mission --
-------------------
local mission=Mission --#FLIGHTGROUP.MissionORBIT
local CoordRaceTrack=mission.coordOrbit:Translate(mission.leg, mission.heading, true)
local DCStask=CONTROLLABLE.TaskOrbit(self.group, mission.coordOrbit, mission.coordOrbit.y, mission.speedOrbit, CoordRaceTrack)
table.insert(DCStasks, DCStask)
elseif Mission.type==FLIGHTGROUP.MissionType.RECON then
-------------------
-- RECON Mission --
-------------------
elseif Mission.type==FLIGHTGROUP.MissionType.STRIKE then
-------------------
-- STIKE Mission --
-------------------
local mission=Mission --#FLIGHTGROUP.MissionSTRIKE
local Vec2=mission.coordtarget:GetVec2()
local DCStask=CONTROLLABLE.TaskAttackMapObject(self.group, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, mission.altitude, WeaponType)
table.insert(DCStasks, DCStask)
elseif Mission.type==FLIGHTGROUP.MissionType.TANKER then
--------------------
-- TANKER Mission --
--------------------
elseif Mission.type==FLIGHTGROUP.MissionType.TRANSPORT then
else
self:E(self.lid..string.format("ERROR: Unknown mission task!"))
return nil
end
if #DCStasks==1 then
return DCStasks[1]
else
return CONTROLLABLE.TaskCombo(self.group, DCStasks)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
@@ -3125,7 +2668,7 @@ function FLIGHTGROUP:onafterTaskExecute(From, Event, To, Task)
-- Cancel current task if there is any.
if self.taskcurrent>0 then
--self:TaskCancel()
self:TaskCancel()
end
-- Set current task.
@@ -3139,10 +2682,6 @@ function FLIGHTGROUP:onafterTaskExecute(From, Event, To, Task)
-- If task is scheduled (not waypoint) set task.
if Task.type==FLIGHTGROUP.TaskType.SCHEDULED then
-- Clear all tasks.
--self.group:ClearTasks()
local DCStasks={}
if Task.dcstask.id=='ComboTask' then
@@ -3158,24 +2697,15 @@ function FLIGHTGROUP:onafterTaskExecute(From, Event, To, Task)
local TaskDone=self.group:TaskFunction("FLIGHTGROUP._TaskDone", self, Task)
table.insert(DCStasks, TaskDone)
env.info("FF DCS task combo")
for i,DCStask in pairs(DCStasks) do
self:I{{DCStask=DCStask}}
end
-- Combo task.
local TaskCombo=self.group:TaskCombo(DCStasks)
-- Stop condition!
local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1)
-- Controlled task.
local TaskControlled=self.group:TaskControlled(TaskCombo, TaskCondition)
env.info("FF executing controlled Task:")
self:I({task=TaskControlled})
-- Set task for group.
self:SetTask(TaskControlled, 1)
-412
View File
@@ -1,412 +0,0 @@
--- **Ops** - (R2.5) - Aerial refueling tanker.
--
-- **Main Features:**
--
-- * Monitor flight status of elements or entire group.
-- * Create a mission queue.
-- * Inherits FLIGHTGROUP class.
--
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.TankerGroup
-- @image OPS_TankerGroup.png
--- TANKERGROUP class.
-- @type TANKERGROUP
-- @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 #table Qmissions Mission queue.
-- @field #table Qclients Client queue.
-- @field #TANKERGROUP.Mission currentmission Currently assigned mission.
-- @field #number missioncounter Counter of total missions.
-- @field Core.Scheduler#SCHEDULER Statusupdater Scheduler to update tanker status.
-- @extends Ops.FlightGroup#FLIGHTGROUP
--- *To invent an airplane is nothing. To build one is something. To fly is everything.* -- Otto Lilienthal
--
-- ===
--
-- ![Banner Image](..\Presentations\CarrierAirWing\TANKERGROUP_Main.jpg)
--
-- # The TANKERGROUP Concept
--
-- # Events
--
--
-- # Tasking
--
--
-- # Examples
--
--
--
--
--
-- @field #TANKERGROUP
TANKERGROUP = {
ClassName = "TANKERGROUP",
Debug = false,
lid = nil,
Qmissions = {},
Qclients = {},
currentmission = nil,
missioncounter = nil,
Statusupdater = nil,
}
--- Tanker mission table.
-- @type TANKERGROUP.Mission
-- @field #string name Name of the mission.
-- @field #number mid ID of the mission.
-- @field #string status Mission status.
-- @field Ops.FlightGroup#FLIGHTGROUP.Task task Task of mission.
-- @field Core.Zone#ZONE zone Mission zone.
-- @field #number duration Duration of mission.
-- @field #number altitude Altitude of orbit in meters ASL.
-- @field #number distance Length of orbit leg in meters.
-- @field #number heading Heading of orbit in degrees.
-- @field #number speed Speed in m/s.
-- @field #number Tadded Time the mission was added.
-- @field #number Tstart Start time in seconds.
-- @field #number Tstarted Time the mission was started.
-- @field #number Tstop Time the mission is stopped.
-- @field #number Tsopped Time the mission was stopped.
-- @field #number prio Priority of the mission.
--- Mission status.
-- @type TANKERGROUP.MissionStatus
-- @field #string SCHEDULED Mission is scheduled.
-- @field #string EXECUTING Mission is being executed.
-- @field #string ASSIGNED Mission was assigned.
-- @field #string ACCOMPLISHED Mission is accomplished.
TANKERGROUP.MissionStatus={
SCHEDULED="scheduled",
EXECUTING="executing",
ASSIGNED="assigned",
ACCOMPLISHED="accomplished",
FAILED="failed",
}
--- TANKERGROUP class version.
-- @field #string version
TANKERGROUP.version="0.0.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add orbit task.
-- TODO: Add client queue.
-- TODO: Add menu?
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new TANKERGROUP object and start the FSM.
-- @param #TANKERGROUP self
-- @param #string groupname Name of the group.
-- @return #TANKERGROUP self
function TANKERGROUP:New(groupname)
-- Create flight group.
local fg=FLIGHTGROUP:New(groupname, false)
-- Inherit everything from TANKERGROUP class.
local self=BASE:Inherit(self, fg) -- #TANKERGROUP
-- Init mission counter.
self.missioncounter=0
-- Log id.
self.lid=string.format("FLIGHTGROUP %s (TANKER) | ", groupname)
if false then
BASE:TraceOn()
BASE:TraceLevel(3)
BASE:TraceClass(self.ClassName)
BASE:TraceClass("FLIGHTGROUP")
end
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "TankerState", "*") -- Tanker is on station and ready to refuel.
self:AddTransition("*", "OnStation", "Ready2Refuel") -- Tanker is on station and ready to refuel.
self:AddTransition("*", "MissionStart", "*") -- Tanker is on station and ready to refuel.
self:AddTransition("*", "MissionAccomplished", "*") -- Tanker is on station and ready to refuel.
-- Call status update.
self:__TankerState(-5)
-- Start FSM.
self:Start()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User API functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add mission for tanker.
-- @param #TANKERGROUP self
-- @param Core.Zone#ZONE Zone The mission zone. Orbit is picked at a random location.
-- @param #number Altitude Orbit altitude in feet. Default is 10000 ft.
-- @param #number Distance Length of the race-track pattern leg in NM.
-- @param #number Heading Heading of the race-track pattern in degrees. Default is 90, i.e. from West to East.
-- @param #number SpeedOrbit Orbit speed in knots. Default is 280 knots.
-- @param #string ClockStart Time the mission is started, e.g. "05:00" for 5 am. If specified as a #number, it will be relative (in seconds) to the current mission time.
-- @param #string ClockStop Time the mission is stopped, e.g. "13:00" for 1 pm. If mission could not be started at that time, it will be removed from the queue. If specified as a #number it will be relative (in seconds) to the current mission time.
-- @param #number Prio Priority of the mission, i.e. a number between 1 and 100. Default 50.
-- @param #string Name Mission name. Default "Aerial Refueling #00X", where "#00X" is a running mission counter index starting at "#001".
-- @return #TANKERGROUP.Mission The mission table.
function TANKERGROUP:AddMission(Zone, Altitude, Distance, Heading, SpeedOrbit, ClockStart, ClockStop, Prio, Name)
-- Increase mission counter.
self.missioncounter=self.missioncounter+1
-- Current mission time.
local Tnow=timer.getAbsTime()
-- Set start time. Default in 5 sec.
local Tstart=Tnow+5
if ClockStart and type(ClockStart)=="number" then
Tstart=Tnow+ClockStart
elseif ClockStart and type(ClockStart)=="string" then
Tstart=UTILS.ClockToSeconds(ClockStart)
end
-- Set stop time. Default nil.
local Tstop=nil
if ClockStop and type(ClockStop)=="number" then
Tstop=Tnow+ClockStop
elseif ClockStop and type(ClockStop)=="string" then
Tstop=UTILS.ClockToSeconds(ClockStop)
end
-- Make mission table.
local mission={} --#TANKERGROUP.Mission
mission.zone=Zone
mission.mid=self.missioncounter
mission.altitude=UTILS.FeetToMeters(Altitude or 10000)
mission.distance=UTILS.NMToMeters(Distance or 25)
mission.name=Name or string.format("Aerial Refueling #%03d", mission.mid)
mission.speed=UTILS.KnotsToMps(SpeedOrbit or 280)
mission.heading=Heading or 270
mission.Tadded=Tnow
mission.Tstart=Tstart
mission.Tstop=Tstop
mission.status=TANKERGROUP.MissionStatus.SCHEDULED
if Tstop then
mission.duration=mission.Tstop-mission.Tstart
end
mission.prio=Prio or 50
mission.task=nil
-- Add mission to queue.
table.insert(self.Qmissions, mission)
local text=string.format("Added mission %s at zone %s. Starting at %s. Stopping at %s. Altitude=%d ft, Leg=%s NM, Heading=%03d, Speed=%d kts",
mission.zone:GetName(), mission.name, UTILS.SecondsToClock(mission.Tstart, true), mission.Tstop and UTILS.SecondsToClock(mission.Tstop, true) or "never",
UTILS.MetersToFeet(mission.altitude), UTILS.MetersToNM(mission.distance), mission.heading, UTILS.MpsToKnots(mission.speed))
self:I(self.lid..text)
return mission
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "TankerStatus" event.
-- @param #TANKERGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function TANKERGROUP:onafterTankerState(From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
-- First check if group is alive?
if self:IsAlive()~=nil and not self.currentmission then
local mission=self:_GetNextMission()
if mission then
env.info("FF starting mission")
self:MissionStart(mission)
end
end
-- Current mission name.
local mymission=self.currentmission and self.currentmission.name or "N/A"
-- Current status.
local text=string.format("Tanker Status %s: Mission=%s (%d)", fsmstate, mymission, #self.Qmissions)
for i,_mission in pairs(self.Qmissions) do
local mission=_mission --#TANKERGROUP.Mission
text=text..string.format("\n[%d] %s status=%s", i, tostring(mission.name), tostring(mission.status))
end
self:I(self.lid..text)
-- Nest status update in 30 sec.
self:__TankerState(-30)
end
--- On after "MissionStart" event.
-- @param #TANKERGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #TANKERGROUP.Mission Mission The mission table.
function TANKERGROUP:onafterMissionStart(From, Event, To, Mission)
-- TODO: need to handle case that group is spawned at a later point in time!
-- Delay for route to mission. Group needs to be activated and controlled.
local delay=nil
-- Check if group is spawned.
if self:IsInUtero() then
-- Activate group if it is late activated.
if self:IsLateActivated() then
self:Activate()
delay=1
end
-- Activate group if it is uncontrolled.
if self:IsUncontrolled() then
self:StartUncontrolled(5)
delay=6
end
end
-- Set current mission.
self.currentmission=Mission
-- Set Tstarted time stamp.
self.currentmission.Tstarted=timer.getAbsTime()
-- Set mission status.
self.currentmission.status=TANKERGROUP.MissionStatus.EXECUTING
-- Route flight to mission zone.
self:RouteToMission(Mission, delay)
end
--- On after "TaskDone" event.
-- @param #TANKERGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.FlightGroup#FLIGHTGROUP.Task Task.
function TANKERGROUP:onafterTaskDone(From, Event, To, Task)
-- First call flight status.
self:GetParent(self).onafterTaskDone(self, From, Event, To, Task)
if self.currentmission then
local missiontask=self.currentmission.task
if missiontask.id==Task.id then
self:MissionAccomplished(self.currentmission)
end
end
end
--- On after "MissionAccomplished" event.
-- @param #TANKERGROUP self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #TANKERGROUP.Mission Mission
function TANKERGROUP:onafterMissionAccomplished(From, Event, To, Mission)
Mission.status=TANKERGROUP.MissionStatus.ACCOMPLISHED
self.currentmission=nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- MISC functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get next mission.
-- @param #TANKERGROUP self
-- @return #TANKERGROUP.Mission Next mission or *nil*.
function TANKERGROUP:_GetNextMission()
-- Number of missions.
local Nmissions=#self.Qmissions
-- Treat special cases.
if Nmissions==0 then
return nil
end
-- Sort results table wrt times they have already been engaged.
local function _sort(a, b)
local taskA=a --#TANKERGROUP.Mission
local taskB=b --#TANKERGROUP.Mission
return (taskA.Tstart<taskB.Tstart) or (taskA.Tstart==taskB.Tstart and taskA.prio<taskB.prio)
end
table.sort(self.Qmissions, _sort)
-- Current time.
local time=timer.getAbsTime()
-- Look for first task that is not accomplished.
for _,_mission in pairs(self.Qmissions) do
local mission=_mission --#TANKERGROUP.Mission
if mission.status==TANKERGROUP.MissionStatus.SCHEDULED and time>=mission.Tstart then
return mission
end
end
return nil
end
--- Route group to mission. Also sets the
-- @param #TANKERGROUP self
-- @param #TANKERGROUP.Mission mission The mission table.
-- @param #number delay Delay in seconds.
function TANKERGROUP:RouteToMission(mission, delay)
if delay and delay>0 then
-- Delay call.
self:ScheduleOnce(delay, TANKERGROUP.RouteToMission, self, mission)
else
-- Get random coordinate in mission zone.
local Coordinate=mission.zone:GetRandomCoordinate():SetAltitude(mission.altitude, true)
-- Set second coordinate for race track pattern.
local CoordRaceTrack=Coordinate:Translate(mission.distance, mission.heading, true)
Coordinate:MarkToAll("Orbit 1")
CoordRaceTrack:MarkToAll("Orbit 2")
-- Add waypoint.
self:AddWaypointAir(Coordinate, nil, self.speedmax*0.8)
-- Create task to orbit.
local taskorbit=self.group:TaskOrbit(Coordinate, mission.altitude, mission.speed, CoordRaceTrack)
-- Add waypoint task.
-- TODO: find last AIR waypoint! We dont want this at landing waypoints.
mission.task=self:AddTaskWaypoint(taskorbit, #self.waypoints, mission.name, 10, mission.duration)
end
end
@@ -52,7 +52,6 @@ WINGCOMMANDER = {
-- @field #number Nonmission Number of assets currently on mission.
--- WINGCOMMANDER class version.
-- @field #string version
WINGCOMMANDER.version="0.0.1"
@@ -257,7 +256,7 @@ function WINGCOMMANDER:CheckResources()
local capabilities={}
for _,MissionType in pairs(FLIGHTGROUP.MissionType) do
for _,MissionType in pairs(AUFTRAG.Type) do
capabilities[MissionType]=0
for _,_airwing in pairs(self.airwings) do