From 1116b19b00a952b1e8a25a4ac1caa472defa49ed Mon Sep 17 00:00:00 2001 From: Frank Date: Tue, 19 Sep 2023 17:04:37 +0200 Subject: [PATCH] FC --- Moose Development/Moose/Ops/FlightControl.lua | 282 ++++++++++++------ Moose Development/Moose/Utilities/Utils.lua | 6 +- 2 files changed, 194 insertions(+), 94 deletions(-) diff --git a/Moose Development/Moose/Ops/FlightControl.lua b/Moose Development/Moose/Ops/FlightControl.lua index 26db608ab..15ce0524c 100644 --- a/Moose Development/Moose/Ops/FlightControl.lua +++ b/Moose Development/Moose/Ops/FlightControl.lua @@ -337,7 +337,6 @@ FLIGHTCONTROL.Violation={ Speeding="Speeding", AltitudeDeviation="Altitude Deviation", --300 feet from assigned alt. RunwayIncursion="Runway Incursion", - } --- Warning. @@ -353,6 +352,7 @@ FLIGHTCONTROL.version="0.7.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list +-- TODO: Controller is a nice guy or not. How he/she reacts on warnings and violations! -- TODO: Progressive Taxi -- TODO: Ground control for takeoff and taxi. -- TODO: Improve approach pattern with new NAVIGATION classes. @@ -1426,7 +1426,7 @@ function FLIGHTCONTROL:_CheckQueues() -- Give AI the landing signal. if flight.isAI then - -- Message. + -- Readback instructions. local text=string.format("Runway %s, cleared to land, %s", runway, callsign) -- Transmit message. @@ -1434,6 +1434,8 @@ function FLIGHTCONTROL:_CheckQueues() -- Land AI. self:_LandAI(flight, parking) + + --TODO: Why do I not set the status to LANDING for AI? else -- We set this flight to landing. With this he is allowed to leave the pattern. @@ -1461,97 +1463,103 @@ function FLIGHTCONTROL:_CheckQueues() -- Get callsign. local callsign=self:_GetCallsignName(flight) - -- Runway. + -- Runway text. local runway=self:GetActiveRunwayText(true) + -- Runway for take-off. local rwy=self:GetActiveRunwayTakeoff() local coord=flight:GetCoordinate() - local taxipath, taxiways=self.airbase:FindTaxiwaysFromAtoB(coord, rwy.position) - - taxipath:Draw(true) - - -- Message. - local text=string.format("%s, %s, taxi to runway %s", callsign, self.alias, runway) - - local taxiroute="" - if taxipath and taxiways then - taxiroute=taxiroute.." via " - for _,_pathline in pairs(taxiways) do - local pathline=_pathline --Core.Pathline#PATHLINE - local name=UTILS.Split(pathline.name, " ") - local name=name[#name] - local tw=name - if string.len(name)==1 then - name=ENUMS.Phonetic[name] - end - taxiroute=taxiroute..string.format("%s, ", name) - end - end - text=text..taxiroute - - text=text..string.format("hold short of runway %s.", runway) - - flight.taxipath=taxipath - + if self:GetFlightStatus(flight)==FLIGHTCONTROL.FlightStatus.READYTO then - text=string.format("%s, %s, cleared for take-off, runway %s", callsign, self.alias, runway) - end - - -- Transmit message. - self:TransmissionTower(text, flight) - - -- Check if flight is AI. Humans have to request taxi via F10 menu. - if flight.isAI then --- - -- AI + -- Ready for TAKEOFF --- - - -- Message. - local text="Wilco, " - - -- Start uncontrolled aircraft. - if flight:IsUncontrolled() then + + local text=string.format("%s, %s, cleared for take-off, runway %s", callsign, self.alias, runway) - -- Message. - text=text..string.format("starting engines, ") - - -- Start uncontrolled aircraft. - flight:StartUncontrolled() - end - - -- Message. - text=text..string.format("runway %s, %s", runway, callsign) - - -- Transmit message. - self:TransmissionPilot(text, flight, 10) - - -- Remove parking guards. - for _,_element in pairs(flight.elements) do - local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element - if element and element.parking then - local spot=self:GetParkingSpotByID(element.parking.TerminalID) - self:RemoveParkingGuard(spot) - end - end - - -- Set flight to takeoff. No way we can stop the AI now. + -- Player is ready for takeoff self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.TAKEOFF) else - + --- - -- PLAYER + -- Ready to TAXI --- - - if self:GetFlightStatus(flight)==FLIGHTCONTROL.FlightStatus.READYTO then - - -- Player is ready for takeoff + + -- Message. + local text=string.format("%s, %s, taxi to runway %s", callsign, self.alias, runway) + + -- Get taxi ways if they have been specified by the mission designer. + local taxipath, taxiways=self.airbase:FindTaxiwaysFromAtoB(coord, rwy.position) + + if taxipath then + + taxipath:Draw(true) + + -- Get taxi way text. + local taxiroute=self:_GetTaxiwayText(taxipath, false) + + text=text.." via "..taxiroute + end + + -- Hold short instructions. + -- TODO: Does this need to be said? + text=text..string.format(", hold short of runway %s.", runway) + + -- Set taxiway to flight so he can read back. + flight.taxipath=taxipath + + -- Transmit message. + self:TransmissionTower(text, flight) + + -- Check if flight is AI. Humans have to request taxi via F10 menu. + if flight.isAI then + + --- + -- AI + --- + + -- Read back runway. + local text=string.format("Runway %s, ", runway) + + -- Start uncontrolled aircraft. + if flight:IsUncontrolled() then + + -- Message. + text=text..string.format("starting engines, ") + + -- Start uncontrolled aircraft. + flight:StartUncontrolled() + end + + -- Append call sign. + text=text..callsign + + -- Transmit message. + self:TransmissionPilot(text, flight, 10) + + -- Remove parking guards. + for _,_element in pairs(flight.elements) do + local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element + if element and element.parking then + local spot=self:GetParkingSpotByID(element.parking.TerminalID) + self:RemoveParkingGuard(spot) + end + end + + -- Set flight to takeoff. No way we can stop the AI now. + -- TODO: To improve this, we would need to monitor if the AI gets to the holding position or on the runway. + -- Probably need some holding guard similar to the parking guard. self:SetFlightStatus(flight, FLIGHTCONTROL.FlightStatus.TAKEOFF) else - + + --- + -- PLAYER + --- + -- Remove parking guards. for _,_element in pairs(flight.elements) do local element=_element --Ops.FlightGroup#FLIGHTGROUP.Element @@ -1565,7 +1573,7 @@ function FLIGHTCONTROL:_CheckQueues() end end - end + end --AI/PLAYER end @@ -3206,6 +3214,7 @@ function FLIGHTCONTROL:_PlayerHolding(groupname) -- Distance. local dist=stack.pos0:Get2DDistance(Coordinate) + -- Currently take a large valu for testing! local dmax=UTILS.NMToMeters(500) if dist This means no clearance for landing! + -- + --- -- Pilot leaves pattern for landing local text=string.format("%s, %s, leaving pattern for landing.", self.alias, callsign) @@ -3372,7 +3391,7 @@ function FLIGHTCONTROL:_PlayerConfirmLanding(groupname) local text=string.format("%s, negative! Hold position until you get clearance.", callsign) -- Send message. - self:TransmissionTower(text, flight, 10) + self:TransmissionTower(text, flight, 10) end else @@ -4224,6 +4243,48 @@ function FLIGHTCONTROL:_CheckFlights() if (flightstatus==FLIGHTCONTROL.FlightStatus.HOLDING) then --Check altitude and position + + local stack=flight.stack + + if stack then + + local altitude=stack.angels + + local alt=flight:GetAltitude() + + local diff=alt-altitude + + -- TODO: issue warning and keep track. otherwise this will repeat unncessarily + -- TODO: make a good warning structure and save it in the flightgroup or here. + -- needs the kind of warning and the time stamp at least + -- + + if diff<300 then + + local timestamp=self:_GetLastWarning(WARNING.Type.AltitudeLow) + if timestamp==nil or timer.getAbsTime()-timestamp>5*60 then + + end + + -- Get number of warnings issued in the last 30 min. + local N=self:_GetWarnings(WARNING.Type.AltitudeLow, Tstart, Tend) + if N>1 then + elseif N>5 then + --Possible pilot deviation. + end + + + local text=string.format("%s, you are too low. Climb to the assigned altitude!") + self:TransmissionTower(text, flight) + + elseif diff>300 then + + local text=string.format("%s, you are too high. Descent to the assigned altitude!") + self:TransmissionTower(text, flight) + + end + end + end if (flightstatus==FLIGHTCONTROL.FlightStatus.ARRIVED) then @@ -4607,23 +4668,62 @@ function FLIGHTCONTROL:_CleanText(Text) return text end ---- Clean text. Remove control sequences. +--- Get taxiway, e.g. "Alpha, Echo or Bravo". +-- We assume that the last part of the input string is separated by a blank " ", e.g. "Kutaisi Taxiway Alpha". +-- @param #FLIGHTCONTROL self +-- @param #string Name Name that contains the taxiway. +-- @param #boolean Short Only return letter, e.g. "A" for "Alpha". +-- @return #string Taxiway name, e.g. "Alpha". +function FLIGHTCONTROL:_GetTaxiwayName(Name, Short) + + -- Split by blanks. + local name=UTILS.Split(Name, " ") + + -- Get last part. + local name=name[#name] + + if string.len(name)==1 then + -- Taxiway given as "A" + if not Short then + -- Get full name "Alpha" + name=ENUMS.Phonetic[name] + end + else + -- Taxiway given as "Alpha" + if Short then + -- Get only first character. + name=string.sub(name, 1, 1) + end + end + + return name +end + +--- Get taxiways as text, e.g. "Alpha, Echo, Bravo", from a given PATHLINE taxiway. -- @param #FLIGHTCONTROL self -- @param Core.Pathline#PATHLINE taxipath Taxi path. --- @param #string Cleaned text. -function FLIGHTCONTROL:_GetTaxiwayText(taxipath) +-- @param #boolean short Only short names, e.g. "A" instead of "Alpha". +-- @return #string Taxiway names. +function FLIGHTCONTROL:_GetTaxiwayText(taxipath, short) + + -- Last taxiway name. + local last=self:_GetTaxiwayName(taxipath.points[1].name, short) + + -- Init taxiways. + local taxiroute=last + + for i=2,#taxipath.points do + local p=taxipath.points[i] --Core.Pathline#PATHLINE.Point + + local name=self:_GetTaxiwayName(p.name, short) - local taxiroute="" - for i,_point in pairs(taxipath.points) do - local p=_point --Core.Pathline#PATHLINE.Point - local name=UTILS.Split(p.name, " ") - local name=name[#name] - local tw=name - if string.len(name)==1 then - name=ENUMS.Phonetic[name] - end - taxiroute=taxiroute..string.format("%s, ", name) + if last~=name then + taxiroute=taxiroute..string.format(", %s", name) + last=name + end end + + return taxiroute end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 388a216b7..469fabeea 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -301,7 +301,7 @@ end -- @return #string Table as a string. UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function - lookup_table = {} + local lookup_table = {} local function _Serialize( tbl ) @@ -3073,7 +3073,7 @@ end -- @param #table Table The table. -- @param #table Object The object to check. -- @param #string Key (Optional) Key to check. By default, the object itself is checked. --- @return #booolen Returns `true` if object is in table. +-- @return #boolean Returns `true` if object is in table. function UTILS.IsInTable(Table, Object, Key) for key, object in pairs(Table) do @@ -3095,7 +3095,7 @@ end -- @param #table Table The table. -- @param #table Objects The objects to check. -- @param #string Key (Optional) Key to check. --- @return #booolen Returns `true` if object is in table. +-- @return #boolean Returns `true` if object is in table. function UTILS.IsAnyInTable(Table, Objects, Key) for _,Object in pairs(UTILS.EnsureTable(Objects)) do