From 47e4777df0db2a5514bc7b1cf0ca30b8443a556d Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 25 Feb 2020 16:49:54 +0100 Subject: [PATCH] Ops --- .../Moose/Functional/Warehouse.lua | 2 +- Moose Development/Moose/Ops/AWACSGroup.lua | 95 --- Moose Development/Moose/Ops/AirWing.lua | 277 +++++--- Moose Development/Moose/Ops/Auftrag.lua | 569 ++++++++++++++++ .../Moose/Ops/CarrierAirWing.lua | 629 ------------------ Moose Development/Moose/Ops/FlightGroup.lua | 524 +-------------- Moose Development/Moose/Ops/TankerGroup.lua | 412 ------------ Moose Development/Moose/Ops/WingCommander.lua | 3 +- 8 files changed, 768 insertions(+), 1743 deletions(-) delete mode 100644 Moose Development/Moose/Ops/AWACSGroup.lua create mode 100644 Moose Development/Moose/Ops/Auftrag.lua delete mode 100644 Moose Development/Moose/Ops/CarrierAirWing.lua delete mode 100644 Moose Development/Moose/Ops/TankerGroup.lua diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e4b424079..564db8261 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -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 diff --git a/Moose Development/Moose/Ops/AWACSGroup.lua b/Moose Development/Moose/Ops/AWACSGroup.lua deleted file mode 100644 index 6bceeee35..000000000 --- a/Moose Development/Moose/Ops/AWACSGroup.lua +++ /dev/null @@ -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 \ No newline at end of file diff --git a/Moose Development/Moose/Ops/AirWing.lua b/Moose Development/Moose/Ops/AirWing.lua index 564303a2a..20c5a0f7b 100644 --- a/Moose Development/Moose/Ops/AirWing.lua +++ b/Moose Development/Moose/Ops/AirWing.lua @@ -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=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 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//... - 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 - - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 1876199c3..71ed0c00e 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -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) diff --git a/Moose Development/Moose/Ops/TankerGroup.lua b/Moose Development/Moose/Ops/TankerGroup.lua deleted file mode 100644 index b8b1d1839..000000000 --- a/Moose Development/Moose/Ops/TankerGroup.lua +++ /dev/null @@ -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=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 - diff --git a/Moose Development/Moose/Ops/WingCommander.lua b/Moose Development/Moose/Ops/WingCommander.lua index fa0cfec45..f184e981e 100644 --- a/Moose Development/Moose/Ops/WingCommander.lua +++ b/Moose Development/Moose/Ops/WingCommander.lua @@ -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