From de2f7148ee9af90ff13ae569bf4ac42dd5eb18dd Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 17 Sep 2019 22:09:56 +0200 Subject: [PATCH] FG/FC --- .../Moose/Functional/Warehouse.lua | 24 +- Moose Development/Moose/Ops/FlightControl.lua | 610 ++++++++---------- Moose Development/Moose/Ops/FlightGroup.lua | 79 ++- 3 files changed, 352 insertions(+), 361 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index f10e7a1fc..14a50fdeb 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -79,6 +79,7 @@ -- @field #number lowfuelthresh Low fuel threshold. Triggers the event AssetLowFuel if for any unit fuel goes below this number. -- @field #boolean respawnafterdestroyed If true, warehouse is respawned after it was destroyed. Assets are kept. -- @field #number respawndelay Delay before respawn in seconds. +-- @field Ops.FlightControl#FLIGHTCONTROL flightcontrol The flightcontrol for this airbase. -- @extends Core.Fsm#FSM --- Have your assets at the right place at the right time - or not! @@ -1573,6 +1574,7 @@ WAREHOUSE = { lowfuelthresh = 0.15, respawnafterdestroyed=false, respawndelay = nil, + flightcontrol = nil, } --- Item of the warehouse stock table. @@ -1752,7 +1754,7 @@ _WAREHOUSEDB = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.9.7" +WAREHOUSE.version="0.9.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -2507,6 +2509,16 @@ function WAREHOUSE:SetSafeParkingOff() return self end +--- Set flight control. +-- @param #WAREHOUSE self +-- @param Ops.FlightControl#FLIGHTCONTROL flightcontrol Flight control object. +-- @return #WAREHOUSE self +function WAREHOUSE:SetFlightControl(flightcontrol) + self.flightcontrol=flightcontrol + return self +end + + --- Set low fuel threshold. If one unit of an asset has less fuel than this number, the event AssetLowFuel will be fired. -- @param #WAREHOUSE self -- @param #number threshold Relative low fuel threshold, i.e. a number in [0,1]. Default 0.15 (15%). @@ -5592,6 +5604,8 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Debug info. self:T2({airtemplate=template}) + + FLIGHTGROUP:New(template.name) -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP @@ -5805,7 +5819,13 @@ function WAREHOUSE:_RouteAir(aircraft) -- Give start command to activate uncontrolled aircraft within the next 60 seconds. local starttime=math.random(60) - aircraft:StartUncontrolled(starttime) + + local fc=_DATABASE:GetFlightControl(self.airbasename) + if fc then + --local flightgroup=FLIGHTGROUP:New(aircraft:GetName()) + else + aircraft:StartUncontrolled(starttime) + end -- Debug info. self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s (after start command)", aircraft:GetName(), tostring(aircraft:IsAlive()))) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 0d55d914b..c67ae1b4e 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -1,4 +1,4 @@ ---- **OPS** - (R2.5) - Manage recovery of aircraft at airdromes and FARPS. +--- **OPS** - (R2.5) - Manage recovery of aircraft at airdromes. -- -- -- @@ -94,7 +94,7 @@ FLIGHTCONTROL = { --- FlightControl class version. -- @field #string version -FLIGHTCONTROL.version="0.0.5" +FLIGHTCONTROL.version="0.0.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list @@ -262,165 +262,6 @@ function FLIGHTCONTROL:onafterStatus() self:__Status(-30) end ---- Initialize data of runways. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_InitRunwayData() - - -- Get spawn points on runway. - local runwaycoords=self.airbase:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway) - - self:E(self.lid..string.format("Runway coords # = %d", #runwaycoords)) - - for i=1,#runwaycoords,2 do - - -- Assuming each runway has two points. - local j=(i+1)/2 - - -- Coordinates of the two runway points. - local c1=runwaycoords[i] --Core.Point#COORDINATES - local c2=runwaycoords[i+1] --Core.Point#COORDINATES - - -- Debug mark - c1:MarkToAll("Runway Point 1") - c2:MarkToAll("Runway Point 2") - - -- Heading of runway. - local hdg=c1:HeadingTo(c2) - - -- Debug info. - self:T(self.lid..string.format("Runway %d heading=%03d", j, hdg)) - - -- Runway table. - local runway={} --#FLIGHTCONTROL.Runway - runway.direction=hdg - runway.length=c1:Get2DDistance(c2) - runway.position=c1 - - -- Add runway. - table.insert(self.runways, runway) - - -- Inverse runway. - local runway={} --#FLIGHTCONTROL.Runway - local hdg=hdg-180 - if hdg<0 then - hdg=hdg+360 - end - runway.direction=hdg - runway.length=c1:Get2DDistance(c2) - runway.position=c2 - - -- Add inverse runway. - table.insert(self.runways, runway) - end - -end - ---- Get the active runway based on current wind direction. --- @param #FLIGHTCONTROL self --- @return #FLIGHTCONTROL.Runway Active runway. -function FLIGHTCONTROL:_GetActiveRunway() - -- TODO: get runway. - local i=math.max(self.activerwyno, #self.runways) - return self.runways[i] -end - ---- Init parking spots. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_InitParkingSpots() - - -- Parking spots of airbase. - local parkingdata=self.airbase:GetParkingSpotsTable() - - self.parking={} - - for _,_spot in pairs(parkingdata) do - local spot=_spot --Wrapper.Airbase#AIRBASE.ParkingData - - local parking={} --#FLIGHTCONTROL.ParkingSpot - - parking.position=spot.Coordinate - parking.drunway=spot.DistToRwy - parking.terminal=spot.TerminalType - parking.id=spot.TerminalID - parking.free=spot.Free - parking.reserved=spot.TOAC - - -- Mark position. - local text=string.format("ID=%d, Terminal=%d, Free=%s, Reserved=%s, Dist=%.1f", parking.id, parking.terminal, tostring(parking.free), tostring(parking.reserved), parking.drunway) - parking.markerid=parking.position:MarkToAll(text) - - -- Add to table. - table.insert(self.parking, parking) - end - -end - ---- Get free parking spots. --- @param #FLIGHTCONTROL self --- @param #number terminal Terminal type or nil. --- @return #number Number of free spots. Total if terminal=nil or of the requested terminal type. --- @return #table Table of free parking spots of data type #FLIGHCONTROL.ParkingSpot. -function FLIGHTCONTROL:_GetFreeParkingSpots(terminal) - - local freespots={} - - local n=0 - for _,_parking in pairs(self.parking) do - local parking=_parking --#FLIGHTCONTROL.ParkingSpot - - if parking.free then - if terminal==nil or terminal==parking.terminal then - n=n+1 - table.insert(freespots, parking) - end - end - end - - return n,freespots -end - ---- Init parking spots. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_UpdateParkingSpots() - - -- Parking spots of airbase. - local parkingdata=self.airbase:GetParkingSpotsTable() - - local message="Parking Spots:" - for _,_parkingspot in pairs(self.parking) do - local parking=_parkingspot --#FLIGHTCONTROL.ParkingSpot - - for _,_spot in pairs(parkingdata) do - local spot=_spot --Wrapper.Airbase#AIRBASE.ParkingSpot - - if parking.id==spot.TerminalID then - - parking.position=spot.Coordinate - parking.drunway=spot.DistToRwy - parking.terminal=spot.TerminalType - parking.id=spot.TerminalID - parking.free=spot.Free - parking.reserved=spot.TOAC - - -- Mark position. - if parking.markerid then - parking.position:RemoveMark(parking.markerid) - end - - local text=string.format("ID=%d, Terminal=%d, Free=%s, Reserved=%s, Dist=%.1f", parking.id, parking.terminal, tostring(parking.free), tostring(parking.reserved), parking.drunway) - message=message.."\n"..text - parking.markerid=parking.position:MarkToAll(text) - - break - end - - end - end - - self:E(self.lid..message) -end - - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Event Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -528,26 +369,36 @@ end function FLIGHTCONTROL:_CheckQueues() -- Print queues - self:_PrintQueue(self.flights, "All flights") + --self:_PrintQueue(self.flights, "All flights") self:_PrintQueue(self.Qparking, "Parking") self:_PrintQueue(self.Qtakeoff, "Takeoff") self:_PrintQueue(self.Qwaiting, "Holding") self:_PrintQueue(self.Qlanding, "Landing") + + -- Number of groups landing. + local nlanding=#self.Qlanding + + -- Number of groups taking off. + local ntakeoff=#self.Qtakeoff + + -- Number of holding groups. + local nholding=#self.Qwaiting + + -- Number of parking groups. + local nparking=#self.Qparking - -- Get next wairing flight. - local flight=self:_GetNextFightWaiting() + -- Get next flight in holding queue. + local flight=self:_GetNextFightHolding() if flight then - - -- Number of groups landing. - local nlanding=#self.Qlanding - - -- Number of groups taking off. - local ntakeoff=#self.Qtakeoff if nlanding==0 and ntakeoff==0 then + + -- Message. local text=string.format("Flight %s, you are cleared to taxi to runway.", flight.groupname) - MESSAGE:New(text, 5, "FLIGHTCONTROL"):ToAll() + MESSAGE:New(text, 5, "FLIGHTCONTROL"):ToAll() + + flight:FlightLanding() self:_LandAI(flight) end @@ -556,7 +407,9 @@ function FLIGHTCONTROL:_CheckQueues() -- Get next wairing flight. local flight=self:_GetNextFightParking() - if flight then + if flight and ntakeoff==0 and nlanding==0 then + + -- Message. local text=string.format("Flight %s, you are cleared to taxi to runway.", flight.groupname) MESSAGE:New(text, 5, "FLIGHTCONTROL"):ToAll() @@ -571,60 +424,12 @@ function FLIGHTCONTROL:_CheckQueues() end ---- Check parking spots. --- @param #FLIGHTCONTROL self -function FLIGHTCONTROL:_CheckParking() - -- Init all elements as NOT parking anywhere. - for _,_flight in pairs(self.flights) do - -- Loop over all elements. - for _,_element in pairs(_flight.elements) do - local element=_element --#FLIGHTCONTROL.FlightElement - element.parking=false - end - end - - -- Loop over all parking spots. - for i,_spot in pairs(self.parking) do - local spot=_spot --#FLIGHTCONTROL.ParkingSpot - - -- Assume spot is free. - spot.free=true - - -- Loop over all flights. - for _,_flight in pairs(self.flights) do - local flight=_flight --Ops.FlightGroup#FLIGHTGROUP - - -- Loop over all elements. - for _,_element in pairs(_flight.elements) do - local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element - - if element.unit and element.unit:IsAlive() then - - -- Distance to parking spot. - local dist=element.unit:GetCoordinate():Get3DDistance(spot.position) - - -- Element is parking on this spot - if dist<5 and not element.unit:InAir() then - element.parking=true - spot.free=false - end - - else - self:E(self.lid..string.format("ERROR: Element %s is not alive any more!", element.name)) - end - - end - - - end - end -end --- Get next flight waiting for landing clearance. -- @param #FLIGHTCONTROL self -- @return Ops.FlightGroup#FLIGHTGROUP Marshal flight next in line and ready to enter the pattern. Or nil if no flight is ready. -function FLIGHTCONTROL:_GetNextFightWaiting() +function FLIGHTCONTROL:_GetNextFightHolding() -- Loop over all marshal flights. for _,_flight in pairs(self.Qwaiting) do @@ -730,51 +535,42 @@ end --- Add flight to landing queue and set recovered to false for all elements of the flight and its section members. -- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group of element. +-- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. +-- @return #boolean If true, flight was added. False otherwise. function FLIGHTCONTROL:_AddFlightToLandingQueue(flight) + -- Check if already in queue. + if self:_InQueue(self.Qlanding, flight.group) then + return false + end + -- Add flight to table. table.insert(self.Qlanding, flight) - -- Set flag to -1. - flight.flag=-1 - - -- New time stamp for time in pattern. - flight.time=timer.getAbsTime() - - -- Init recovered switch. - flight.recovered=false - for _,elem in pairs(flight.elements) do - elem.recoverd=false - end + -- Flight is not holding any more. + flight.Tholding=nil + return true end --- Add flight to takeoff queue. -- @param #FLIGHTCONTROL self --- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group of element. +-- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. +-- @return #boolean If true, flight was added. False otherwise. function FLIGHTCONTROL:_AddFlightToTakeoffQueue(flight) -- Check if already in queue. - if self:_InQueue(self.Qtakeoff,flight.group) then - return + if self:_InQueue(self.Qtakeoff, flight.group) then + return false end -- Add flight to table. table.insert(self.Qtakeoff, flight) - -- Set flag to -1. - flight.flag=-1 - -- New time stamp for time in pattern. - flight.time=timer.getAbsTime() - - -- Init recovered switch. - flight.tookoff=false - for _,elem in pairs(flight.elements) do - elem.tookoff=false - end + flight.Tholding=timer.getAbsTime() + return true end --- Check if a group is in a queue. @@ -784,15 +580,245 @@ end -- @return #boolean If true, group is in the queue. False otherwise. function FLIGHTCONTROL:_InQueue(queue, group) local name=group:GetName() + for _,_flight in pairs(queue) do local flight=_flight --Ops.FlightGroup#FLIGHTGROUP if name==flight.groupname then return true end end + return false end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Runway Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Initialize data of runways. +-- @param #FLIGHTCONTROL self +function FLIGHTCONTROL:_InitRunwayData() + + -- Get spawn points on runway. + local runwaycoords=self.airbase:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway) + + self:E(self.lid..string.format("Runway coords # = %d", #runwaycoords)) + + for i=1,#runwaycoords,2 do + + -- Assuming each runway has two points. + local j=(i+1)/2 + + -- Coordinates of the two runway points. + local c1=runwaycoords[i] --Core.Point#COORDINATES + local c2=runwaycoords[i+1] --Core.Point#COORDINATES + + -- Debug mark + c1:MarkToAll("Runway Point 1") + c2:MarkToAll("Runway Point 2") + + -- Heading of runway. + local hdg=c1:HeadingTo(c2) + + -- Debug info. + self:T(self.lid..string.format("Runway %d heading=%03d", j, hdg)) + + -- Runway table. + local runway={} --#FLIGHTCONTROL.Runway + runway.direction=hdg + runway.length=c1:Get2DDistance(c2) + runway.position=c1 + + self:I(self.lid..string.format("Adding runway #%d: heading=%03d, lendth=%d m", #self.runways+1, runway.direction, runway.length)) + + -- Add runway. + table.insert(self.runways, runway) + + -- Inverse runway. + local runway={} --#FLIGHTCONTROL.Runway + local hdg=hdg-180 + if hdg<0 then + hdg=hdg+360 + end + runway.direction=hdg + runway.length=c1:Get2DDistance(c2) + runway.position=c2 + + self:I(self.lid..string.format("Adding runway #%d: heading=%03d, lendth=%d m", #self.runways+1, runway.direction, runway.length)) + + -- Add inverse runway. + table.insert(self.runways, runway) + end + +end + +--- Get the active runway based on current wind direction. +-- @param #FLIGHTCONTROL self +-- @return #FLIGHTCONTROL.Runway Active runway. +function FLIGHTCONTROL:_GetActiveRunway() + -- TODO: get runway. + local i=math.max(self.activerwyno, #self.runways) + return self.runways[i] +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Parking Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Init parking spots. +-- @param #FLIGHTCONTROL self +function FLIGHTCONTROL:_InitParkingSpots() + + -- Parking spots of airbase. + local parkingdata=self.airbase:GetParkingSpotsTable() + + self.parking={} + + for _,_spot in pairs(parkingdata) do + local spot=_spot --Wrapper.Airbase#AIRBASE.ParkingData + + local parking={} --#FLIGHTCONTROL.ParkingSpot + + parking.position=spot.Coordinate + parking.drunway=spot.DistToRwy + parking.terminal=spot.TerminalType + parking.id=spot.TerminalID + parking.free=spot.Free + parking.reserved=spot.TOAC + + -- Mark position. + local text=string.format("ID=%d, Terminal=%d, Free=%s, Reserved=%s, Dist=%.1f", parking.id, parking.terminal, tostring(parking.free), tostring(parking.reserved), parking.drunway) + parking.markerid=parking.position:MarkToAll(text) + + -- Add to table. + table.insert(self.parking, parking) + end + +end + +--- Check parking spots. +-- @param #FLIGHTCONTROL self +function FLIGHTCONTROL:_CheckParking() + + -- Init all elements as NOT parking anywhere. + for _,_flight in pairs(self.flights) do + -- Loop over all elements. + for _,_element in pairs(_flight.elements) do + local element=_element --#FLIGHTCONTROL.FlightElement + element.parking=false + end + end + + -- Loop over all parking spots. + for i,_spot in pairs(self.parking) do + local spot=_spot --#FLIGHTCONTROL.ParkingSpot + + -- Assume spot is free. + spot.free=true + + -- Loop over all flights. + for _,_flight in pairs(self.flights) do + local flight=_flight --Ops.FlightGroup#FLIGHTGROUP + + -- Loop over all elements. + for _,_element in pairs(_flight.elements) do + local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element + + if element.unit and element.unit:IsAlive() then + + -- Distance to parking spot. + local dist=element.unit:GetCoordinate():Get3DDistance(spot.position) + + -- Element is parking on this spot + if dist<5 and not element.unit:InAir() then + element.parking=true + spot.free=false + end + + else + self:E(self.lid..string.format("ERROR: Element %s is not alive any more!", element.name)) + end + + end + + + end + end +end + +--- Get free parking spots. +-- @param #FLIGHTCONTROL self +-- @param #number terminal Terminal type or nil. +-- @return #number Number of free spots. Total if terminal=nil or of the requested terminal type. +-- @return #table Table of free parking spots of data type #FLIGHCONTROL.ParkingSpot. +function FLIGHTCONTROL:_GetFreeParkingSpots(terminal) + + local freespots={} + + local n=0 + for _,_parking in pairs(self.parking) do + local parking=_parking --#FLIGHTCONTROL.ParkingSpot + + if parking.free then + if terminal==nil or terminal==parking.terminal then + n=n+1 + table.insert(freespots, parking) + end + end + end + + return n,freespots +end + +--- Init parking spots. +-- @param #FLIGHTCONTROL self +function FLIGHTCONTROL:_UpdateParkingSpots() + + -- Parking spots of airbase. + local parkingdata=self.airbase:GetParkingSpotsTable() + + local message="Parking Spots:" + for _,_parkingspot in pairs(self.parking) do + local parking=_parkingspot --#FLIGHTCONTROL.ParkingSpot + + for _,_spot in pairs(parkingdata) do + local spot=_spot --Wrapper.Airbase#AIRBASE.ParkingSpot + + if parking.id==spot.TerminalID then + + parking.position=spot.Coordinate + parking.drunway=spot.DistToRwy + parking.terminal=spot.TerminalType + parking.id=spot.TerminalID + parking.free=spot.Free + parking.reserved=spot.TOAC + + -- Mark position. + if parking.markerid then + parking.position:RemoveMark(parking.markerid) + end + + local text=string.format("ID=%d, Terminal=%d, Free=%s, Reserved=%s, Dist=%.1f", parking.id, parking.terminal, tostring(parking.free), tostring(parking.reserved), parking.drunway) + message=message.."\n"..text + parking.markerid=parking.position:MarkToAll(text) + + break + end + + end + end + + self:E(self.lid..message) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ATIS Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- ATC Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Flight and Element Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1070,75 +1096,6 @@ function FLIGHTCONTROL:_WaitAI(flight, stack, respawn) flight:Hold(self.airbase, holding.pos0) - if true then - return - end - - -- Flight group name. - local group=flight.group - local groupname=flight.groupname - - ---------------- - -- Set Speeds -- - ---------------- - - -- Aircraft speed 274 knots TAS ~= 250 KIAS when orbiting the pattern. (Orbit expects m/s.) - local speedOrbitMps=UTILS.KnotsToMps(274) - - -- Orbit speed in km/h for waypoints. - local speedOrbitKmh=UTILS.KnotsToKmph(274) - - -- Aircraft speed 400 knots when transiting to holding zone. (Waypoint expects km/h.) - local speedTransit=UTILS.KnotsToKmph(370) - - --------------- - -- Waypoints -- - --------------- - - -- Waypoints array to be filled depending on case etc. - local wp={} - - -- Current position. Always good for as the first waypoint. - wp[1]=group:GetCoordinate():WaypointAirTurningPoint(nil, speedTransit, {}, "Current Position") - - -- Task function when arriving at the holding zone. This will set flight.holding=true. - local TaskHolding=flight.group:TaskFunction("FLIGHTCONTROL._ReachedHoldingZone", self, flight) - - - - -- Set orbit task. - local TaskOrbit=group:TaskOrbit(holding.pos0, altitude, speedOrbitMps, holding.pos1) - - -- Orbit at waypoint. - wp[#wp+1]=holding.pos0:WaypointAirTurningPoint(nil, speedOrbitKmh, {TaskHolding, TaskOrbit}, string.format("Holding at Angels %d", angels)) - - -- Debug markers. - if self.Debug then - holding.pos0:MarkToAll(string.format("Waiting Orbit of flight %s at Angels %s", groupname, angels)) - end - - if respawn then - - -- This should clear the landing waypoints. - -- Note: This resets the weapons and the fuel state. But not the units fortunately. - - -- Get group template. - local Template=group:GetTemplate() - - -- Set route points. - Template.route.points=wp - - -- Respawn the group. - group=group:Respawn(Template, true) - - end - - -- Reinit waypoints. - group:WayPointInitialize(wp) - - -- Route group. - group:Route(wp, 1) - end --- Tell AI to land at the airbase. Flight is added to the landing queue. @@ -1159,29 +1116,6 @@ function FLIGHTCONTROL:_LandAI(flight) self:_RemoveFlightFromQueue(self.Qwaiting, flight, "holding") end ---- Function called when a group has reached the holding zone. ---@param Wrapper.Group#GROUP group Group that reached the holding zone. ---@param #FLIGHTCONTROL flightcontrol Flightcontrol object. ---@param Ops.FlightGroup#FLIGHTGROUP flight Flight group that has reached the holding zone. -function FLIGHTCONTROL._ReachedHoldingZone(group, flightcontrol, flight) - - -- Debug message. - local text=string.format("Flight %s reached holding zone.", group:GetName()) - MESSAGE:New(text,10):ToAllIf(flightcontrol.Debug) - flightcontrol:T(flightcontrol.lid..text) - - -- Debug mark. - if flightcontrol.Debug then - group:GetCoordinate():MarkToAll(text) - end - - -- Set holding flag true and set timestamp for marshal time check. - if flight then - flight.holding=true - flight.time=timer.getAbsTime() - end -end - --- Get holding point. -- @param #FLIGHTCONTROL self -- @param Ops.FlightGroup#FLIGHTGROUP flight Flight group. diff --git a/Moose Development/Moose/Ops/FlightGroup.lua b/Moose Development/Moose/Ops/FlightGroup.lua index 814660dff..57d3d49c2 100644 --- a/Moose Development/Moose/Ops/FlightGroup.lua +++ b/Moose Development/Moose/Ops/FlightGroup.lua @@ -94,6 +94,7 @@ FLIGHTGROUP = { -- @field #string TAXIING Element is taxiing after engine startup. -- @field #string TAKEOFF Element took of after takeoff event. -- @field #string AIRBORNE Element is airborne. Either after takeoff or after air start. +-- @field #string LANDING Element is landing. -- @field #string LANDED Element landed and is taxiing to its parking spot. -- @field #string ARRIVED Element arrived at its parking spot and shut down its engines. -- @field #string DEAD Element is dead after it crashed, pilot ejected or pilot dead events. @@ -104,6 +105,7 @@ FLIGHTGROUP.ElementStatus={ TAXIING="taxiing", TAKEOFF="takeoff", AIRBORNE="airborne", + LANDING="landing", LANDED="landed", ARRIVED="arrived", DEAD="dead", @@ -277,6 +279,7 @@ function FLIGHTGROUP:New(groupname) self:AddTransition("*", "FlightTaxiing", "Taxiing") -- The whole flight group is taxiing. self:AddTransition("*", "FlightTakeoff", "Airborne") -- The whole flight group is airborne. self:AddTransition("*", "FlightAirborne", "Airborne") -- The whole flight group is airborne. + self:AddTransition("*", "FlightLanding", "Landing") -- The whole flight group is landing. self:AddTransition("*", "FlightLanded", "Landed") -- The whole flight group has landed. self:AddTransition("*", "FlightArrived", "Arrived") -- The whole flight group has arrived. self:AddTransition("*", "FlightDead", "Dead") -- The whole flight group is dead. @@ -555,6 +558,13 @@ function FLIGHTGROUP:IsAirborne() return self:Is("Airborne") end +--- Check if flight is landing. +-- @param #FLIGHTGROUP self +-- @return #boolean +function FLIGHTGROUP:IsLanded() + return self:Is("Landing") +end + --- Check if flight has landed and is now taxiing to its parking spot. -- @param #FLIGHTGROUP self -- @return #boolean @@ -1026,6 +1036,9 @@ function FLIGHTGROUP:onafterElementTakeoff(From, Event, To, Element, airbase) -- Set element status. self:_UpdateStatus(Element, FLIGHTGROUP.ElementStatus.TAKEOFF, airbase) + + -- Trigger element airborne event. + self:__ElementAirborne(1, Element) end --- On after "ElementAirborne" event. @@ -1049,7 +1062,7 @@ end -- @param #FLIGHTGROUP.Element Element The flight group element. -- @param Wrapper.Airbase#AIRBASE airbase The airbase if applicable or nil. function FLIGHTGROUP:onafterElementLanded(From, Event, To, Element, airbase) - self:I(self.sid..string.format("Element landed %s.", Element.name)) + self:I(self.sid..string.format("Element landed %s at %s", Element.name, airbase and airbase:GetName() or "unknown")) -- Set element status. self:_UpdateStatus(Element, FLIGHTGROUP.ElementStatus.LANDED, airbase) @@ -1161,7 +1174,7 @@ function FLIGHTGROUP:onafterFlightTakeoff(From, Event, To, airbase) end -- Trigger airborne event. - self:FlightAirborne(airbase) + self:__FlightAirborne(1, airbase) end @@ -1184,6 +1197,18 @@ function FLIGHTGROUP:onafterFlightAirborne(From, Event, To, airbase) end +--- On after "FlightLanded" event. +-- @param #FLIGHTGROUP self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function FLIGHTGROUP:onafterFlightLanding(From, Event, To) + self:I(self.sid..string.format("Flight landing %s", self.groupname)) + + self:_SetElementStatusAll(FLIGHTGROUP.ElementStatus.LANDING) + +end + --- On after "FlightLanded" event. -- @param #FLIGHTGROUP self -- @param #string From From state. @@ -1198,9 +1223,6 @@ function FLIGHTGROUP:onafterFlightLanded(From, Event, To, airbase) end end - - - --- On after "PassingWaypoint" event. -- @param #FLIGHTGROUP self -- @param #string From From state. @@ -1211,8 +1233,7 @@ end function FLIGHTGROUP:onafterPassingWaypoint(From, Event, To, n, N) local text=string.format("Group %s passed waypoint %d/%d", self.groupname, n, N) self:I(self.sid..text) - MESSAGE:New(text, 30, "DEBUG"):ToAllIf(self.Debug) - + MESSAGE:New(text, 20, "DEBUG"):ToAllIf(self.Debug) end --- On after "FuelLow" event. @@ -1337,6 +1358,7 @@ function FLIGHTGROUP:onafterHold(From, Event, To, airbase, SpeedTo, SpeedHold) -- Defaults: SpeedTo=SpeedTo or 350 SpeedHold=SpeedHold or 250 + local SpeedLand=190 -- Debug message. local text=string.format("Flight group set to hold at airbase %s", airbase:GetName()) @@ -1366,11 +1388,12 @@ function FLIGHTGROUP:onafterHold(From, Event, To, airbase, SpeedTo, SpeedHold) -- TODO: make dependend on AC type helos etc. -- Approach point: 10 NN in direction of runway. - local papproach=runway.position:Translate(UTILS.NMToMeters(15), runway.direction):SetAltitude(UTILS.FeetToMeters(1700)) - papproach:MarkToAll("Approach Point") + local papproach=airbase:GetCoordinate():Translate(UTILS.NMToMeters(10), runway.direction-180):SetAltitude(UTILS.FeetToMeters(3000)) + + papproach:MarkToAll("Final Approach") -- Approach waypoint. - wpap=papproach:WaypointAirTurningPoint(nil, UTILS.KnotsToKmph(170), {}, "Final Approach") + wpap=papproach:WaypointAirTurningPoint(nil, UTILS.KnotsToKmph(SpeedLand), {}, "Final Approach") -- Set flightcontrol for this flight. self:SetFlightControl(fc) @@ -1395,7 +1418,7 @@ function FLIGHTGROUP:onafterHold(From, Event, To, airbase, SpeedTo, SpeedHold) if wpap then wp[#wp+1]=wpap end - wp[#wp+1]=airbase:GetCoordinate():WaypointAirLanding(SpeedHold, airbase, {}, "Landing") + wp[#wp+1]=airbase:GetCoordinate():SetAltitude(UTILS.FeetToMeters(500)):WaypointAirLanding(UTILS.KnotsToKmph(SpeedLand), airbase, {}, "Landing") local respawn=true @@ -2140,13 +2163,13 @@ function FLIGHTGROUP._PassingWaypoint(group, flightgroup, i) -- Debug smoke and marker. if flightgroup.Debug then local pos=group:GetCoordinate() - pos:SmokeRed() + --pos:SmokeRed() local MarkerID=pos:MarkToAll(string.format("Group %s reached waypoint %d", group:GetName(), i)) end -- Debug message. - MESSAGE:New(text,10):ToAllIf(flightgroup.Debug) - flightgroup:I(flightgroup.sid..text) + --MESSAGE:New(text,10):ToAllIf(flightgroup.Debug) + flightgroup:T2(flightgroup.sid..text) -- Set current waypoint. flightgroup.currentwp=i @@ -2241,7 +2264,7 @@ function FLIGHTGROUP:_AllSimilarStatus(status) elseif status==FLIGHTGROUP.ElementStatus.TAKEOFF then - -- Element AIRBORNE: Check that the other are not stil SPAWNED, PARKING or TAXIING + -- Element TAKEOFF: Check that the other are not stil SPAWNED, PARKING or TAXIING if element.status~=status and element.status==FLIGHTGROUP.ElementStatus.SPAWNED or element.status==FLIGHTGROUP.ElementStatus.PARKING or @@ -2251,7 +2274,7 @@ function FLIGHTGROUP:_AllSimilarStatus(status) elseif status==FLIGHTGROUP.ElementStatus.AIRBORNE then - -- Element AIRBORNE: Check that the other are not stil SPAWNED, PARKING or TAXIING + -- Element AIRBORNE: Check that the other are not stil SPAWNED, PARKING, TAXIING or TAKEOFF if element.status~=status and element.status==FLIGHTGROUP.ElementStatus.SPAWNED or element.status==FLIGHTGROUP.ElementStatus.PARKING or @@ -2349,8 +2372,8 @@ function FLIGHTGROUP:_UpdateStatus(element, newstatus, airbase) --- if self:_AllSimilarStatus(newstatus) then + -- Trigger takeoff event. Also triggers airborne event. self:FlightTakeoff(airbase) - --TODO: also trigger Airborne() ? end elseif newstatus==FLIGHTGROUP.ElementStatus.AIRBORNE then @@ -2363,11 +2386,11 @@ function FLIGHTGROUP:_UpdateStatus(element, newstatus, airbase) if self:IsTaxiing() then self:FlightAirborne() elseif self:IsParking() then - --self:FlightTaxiing(group) + --self:FlightTaxiing() self:FlightAirborne() elseif self:IsSpawned() then - --self:FlightParking(group) - --self:FlightTaxiing(group) + --self:FlightParking() + --self:FlightTaxiing() self:FlightAirborne() end @@ -2379,7 +2402,7 @@ function FLIGHTGROUP:_UpdateStatus(element, newstatus, airbase) --- if self:_AllSimilarStatus(newstatus) then - self:FlightLanded(group) + self:FlightLanded(airbase) end elseif newstatus==FLIGHTGROUP.ElementStatus.ARRIVED then @@ -2410,6 +2433,20 @@ function FLIGHTGROUP:_UpdateStatus(element, newstatus, airbase) end end +--- Set status for all elements (except dead ones). +-- @param #FLIGHTGROUP self +-- @param #string status Element status. +function FLIGHTGROUP:_SetElementStatusAll(status) + + for _,_element in pairs(self.elements) do + local element=_element --#FLIGHTGROUP.Element + if element.status~=FLIGHTGROUP.ElementStatus.DEAD then + element.status=status + end + end + +end + --- Check detected units. -- @param #FLIGHTGROUP self function FLIGHTGROUP:_CheckDetectedUnits()