diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index bd094ba25..cdcf608d0 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -68,6 +68,7 @@ -- @field #number spawndelay Delay time in seconds before first spawning happens. -- @field #number spawninterval Interval between spawning units/groups. Note that we add a randomization of 50%. -- @field #number coalition Coalition of spawn group template. +-- @field #number country Country of spawn group template. -- @field #string category Category of aircarft: "plane" or "heli". -- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red. -- @field #table ctable Table with the valid coalitons from choice self.friendly. @@ -107,7 +108,7 @@ -- @field #table markerids Array with marker IDs. -- @field #string livery Livery of the aircraft set by user. -- @field #string skill Skill of AI. --- @field #boolean ATC Enable ATC. +-- @field #boolean ATCswitch Enable/disable ATC if set to true/false. -- @extends Functional.Spawn#SPAWN ---# RAT class, extends @{Spawn#SPAWN} @@ -262,6 +263,7 @@ RAT={ spawndelay=5, -- Delay time in seconds before first spawning happens. spawninterval=5, -- Interval between spawning units/groups. Note that we add a randomization of 50%. coalition = nil, -- Coalition of spawn group template. + country = nil, -- Country of the group template. category = nil, -- Category of aircarft: "plane" or "heli". friendly = "same", -- Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red. ctable = {}, -- Table with the valid coalitons from choice self.friendly. @@ -281,6 +283,7 @@ RAT={ departure_zones={}, -- Array containing the names of the departure zones. departure_ports={}, -- Array containing the names of the departure airports. destination_ports={}, -- Array containing the names of the destination airports. + excluded_ports={}, -- Array containing the names of explicitly excluded airports. ratcraft={}, -- Array with the spawned RAT aircraft. Tinactive=300, -- Time in seconds after which inactive units will be destroyed. Default is 300 seconds. reportstatus=false, -- Aircraft report status. @@ -301,7 +304,7 @@ RAT={ markerids={}, -- Array with marker IDs. livery=nil, -- Livery of the aircraft. skill="High", -- Skill of AI. - ATC=true, -- Enable ATC. + ATCswitch=true, -- Enable ATC. } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -335,11 +338,6 @@ RAT.wp={ RAT.coal={ same="same", sameonly="sameonly", - all="all", - blue="blue", - blueonly="blueonly", - red="red", - redonly="redonly", neutral="neutral", } @@ -464,16 +462,6 @@ function RAT:New(groupname, alias) -- Get all airports of current map (Caucasus, NTTR, Normandy, ...). self:_GetAirportsOfMap() - - -- Init RAT ATC if not already done. - if self.ATC and not RAT.ATC.init then - RAT:_ATCInit(self.airports_map) - end - - -- Create F10 main menu if it does not exists yet. - if self.f10menu and not RAT.MenuF10 then - RAT.MenuF10 = MENU_MISSION:New("RAT") - end return self end @@ -489,6 +477,16 @@ function RAT:Spawn(naircraft) -- Number of aircraft to spawn. Default is one. self.ngroups=naircraft or 1 + -- Init RAT ATC if not already done. + if self.ATCswitch and not RAT.ATC.init then + RAT:_ATCInit(self.airports_map) + end + + -- Create F10 main menu if it does not exists yet. + if self.f10menu and not RAT.MenuF10 then + RAT.MenuF10 = MENU_MISSION:New("RAT") + end + -- Set the coalition table based on choice of self.coalition and self.friendly. self:_SetCoalitionTable() @@ -531,6 +529,7 @@ function RAT:Spawn(naircraft) text=text..string.format("Time inactive: %4.1f\n", self.Tinactive) text=text..string.format("Create F10 menu : %s\n", tostring(self.f10menu)) text=text..string.format("F10 submenu name: %s\n", self.SubMenuName) + text=text..string.format("ATC enabled : %s\n", tostring(self.ATCswitch)) text=text..string.format("******************************************************\n") env.info(RAT.id..text) @@ -570,25 +569,15 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set the friendly coalitions from which the airports can be used as departure or destination. +--- Set the friendly coalitions from which the airports can be used as departure and destination. -- @param #RAT self --- @param #string friendly "same"=own coalition+neutral (default), "all"=neutral+red+blue", "sameonly"=own coalition only, "blue"=blue+neutral, "blueonly"=blue, "red"=red+neutral, "redonly"=red, "neutral"=neutral. +-- @param #string friendly "same"=own coalition+neutral (default), "sameonly"=own coalition only, "neutral"=all neutral airports. -- Default is "same", so aircraft will use airports of the coalition their spawn template has plus all neutral airports. --- @usage yak:SetCoalition("all") will spawn aircraft randomly on airports of any coaliton, i.e. red, blue and neutral, regardless of its own coalition. --- @usage yak:SetCoalition("redonly") will spawn aircraft randomly on airports belonging to the red coalition _only_. +-- @usage yak:SetCoalition("neutral") will spawn aircraft randomly on all neutral airports. +-- @usage yak:SetCoalition("sameonly") will spawn aircraft randomly on airports belonging to the same coalition only as the template. function RAT:SetCoalition(friendly) - if friendly:lower()=="all" then - self.friendly=RAT.coal.all - elseif friendly:lower()=="sameonly" then + if friendly:lower()=="sameonly" then self.friendly=RAT.coal.sameonly - elseif friendly:lower()=="blue" then - self.friendly=RAT.coal.blue - elseif friendly:lower()=="blueonly" then - self.friendly=RAT.coal.blueonly - elseif friendly:lower()=="red" then - self.friendly=RAT.coal.red - elseif friendly:lower()=="redonly" then - self.friendly=RAT.coal.redonly elseif friendly:lower()=="neutral" then self.friendly=RAT.coal.neutral else @@ -596,6 +585,32 @@ function RAT:SetCoalition(friendly) end end +--- Set coalition of RAT group. You can make red templates blue and vice versa. +-- @param #RAT self +-- @param #string color Color of coalition, i.e. "red" or blue". +function RAT:SetCoalitionAircraft(color) + if color:lower()=="blue" then + self.coalition=coalition.side.BLUE + if not self.country then + self.country=country.id.USA + end + elseif color:lower()=="red" then + self.coalition=coalition.side.RED + if not self.country then + self.country=country.id.RUSSIA + end + elseif color:lower()=="neutral" then + self.coalition=coalition.side.NEUTRAL + end +end + +--- Set country of RAT group. This overrules the coalition settings. +-- @param #RAT self +-- @param #number id DCS country enumerator ID. For example country.id.USA or country.id.RUSSIA. +function RAT:SetCoalition2(id) + self.country=id +end + --- Set takeoff type. Starting cold at airport, starting hot at airport, starting at runway, starting in the air. -- Default is "takeoff-coldorhot". So there is a 50% chance that the aircraft starts with cold engines and 50% that it starts with hot engines. -- @param #RAT self @@ -704,6 +719,17 @@ function RAT:SetDestination(names) end +--- Airports, FARPs and ships explicitly excluded as departures and destinations. +-- @param #RAT self +-- @param #string airports Name or table of names of excluded airports. +function RAT:Excluded(ports) + if type(ports)=="string" then + self.excluded_ports={ports} + else + self.excluded_ports=ports + end +end + --- Set livery of aircraft. If more than one livery is specified in a table, the actually used one is chosen randomly from the selection. -- @param #RAT self -- @param #string skins Name of livery or table of names of liveries. @@ -715,7 +741,6 @@ function RAT:Livery(skins) end end - --- Aircraft will continue their journey from their destination. This means they are respawned at their destination and get a new random destination. -- @param #RAT self -- @param #boolean switch Turn journey on=true or off=false. If no value is given switch=true. @@ -822,8 +847,7 @@ end -- @param #RAT self -- @param #boolean switch true=enable ATC, false=disable ATC. function RAT:EnableATC(switch) - switch=switch or true - self.ATC=switch + self.ATCswitch=switch end --- Set minimum distance between departure and destination. Default is 5 km. @@ -1033,7 +1057,7 @@ function RAT:_SpawnWithRoute(_departure, _destination) self.alive=self.alive+1 -- ATC is monitoring this flight. - if self.ATC then + if self.ATCswitch then RAT:_ATCAddFlight(group:GetName(), destination:GetName()) end @@ -1245,23 +1269,28 @@ function RAT:_SetRoute(takeoff, _departure, _destination) -- DESTINATION AIRPORT local destination=nil if _destination then + if self:_AirportExists(_destination) then destination=AIRBASE:FindByName(_destination) else local text=string.format("ERROR: Specified destination airport %s does not exist for %s!", _destination, self.alias) env.error(RAT.id..text) end - else - -- Get all destination airports within reach. - local destinations=self:_GetDestinations(Pdeparture, self.mindist, math.min(self.aircraft.Reff, self.maxdist)) - local random_destination=false - if self.continuejourney and _departure then - random_destination=true + else + + -- This handes the case where we have a journey and the first flight is done, i.e. _departure is set. + -- If a user specified more than two destination airport explicitly, then we will stick to this. + -- Otherwise, the route is random from now on. + if self.continuejourney and _departure and #self.destination_ports<3 then + self.random_destination=true end + + -- Get all destination airports within reach. + local destinations=self:_GetDestinations(departure, Pdeparture, self.mindist, math.min(self.aircraft.Reff, self.maxdist)) -- Pick a destination airport. - destination=self:_PickDestination(destinations, random_destination) + destination=self:_PickDestination(destinations) end -- Return nil if no departure could be found. @@ -1477,7 +1506,7 @@ end -- @param #RAT self -- @param #number takeoff Takeoff type. -- @return Wrapper.Airbase#AIRBASE Departure airport if spawning at airport. --- @return Coore.Zone#ZONE Departure zone if spawning in air. +-- @return Core.Zone#ZONE Departure zone if spawning in air. function RAT:_PickDeparture(takeoff) -- Array of possible departure airports or zones. @@ -1488,19 +1517,25 @@ function RAT:_PickDeparture(takeoff) if self.random_departure then -- Air start above a random airport. - for _,airport in pairs(self.airports)do - table.insert(departures, airport:GetZone()) + for _,airport in pairs(self.airports) do + if not self:_Excluded(airport:GetName()) then + table.insert(departures, airport:GetZone()) + end end else -- Put all specified zones in table. for _,name in pairs(self.departure_zones) do - table.insert(departures, ZONE:New(name)) + if not self:_Excluded(name) then + table.insert(departures, ZONE:New(name)) + end end -- Put all specified airport zones in table. for _,name in pairs(self.departure_ports) do - table.insert(departures, AIRBASE:FindByName(name):GetZone()) + if not self:_Excluded(name) then + table.insert(departures, AIRBASE:FindByName(name):GetZone()) + end end end @@ -1511,14 +1546,18 @@ function RAT:_PickDeparture(takeoff) -- All friendly departure airports. for _,airport in pairs(self.airports) do - table.insert(departures, airport) + if not self:_Excluded(airport:GetName()) then + table.insert(departures, airport) + end end else -- All airports specified by user for _,name in pairs(self.departure_ports) do - table.insert(departures, AIRBASE:FindByName(name)) + if not self:_Excluded(name) then + table.insert(departures, AIRBASE:FindByName(name)) + end end end @@ -1553,27 +1592,37 @@ end -- @return Wrapper.Airbase#AIRBASE Destination airport. function RAT:_PickDestination(destinations, _random) + --[[ -- Take destinations from user input. if not (self.random_destination or _random) then + destinations=nil destinations={} + -- All airports specified by user. for _,name in pairs(self.destination_ports) do - table.insert(destinations, AIRBASE:FindByName(name)) + if not self:_Excluded(name) then + table.insert(destinations, AIRBASE:FindByName(name)) + end end end + ]] -- Randomly select one possible destination. local destination=nil if destinations and #destinations>0 then + + -- Random selection. destination=destinations[math.random(#destinations)] -- Wrapper.Airbase#AIRBASE + -- Debug message. local text="Chosen destination airport: "..destination:GetName().." (ID "..destination:GetID()..")" env.info(RAT.id..text) if self.debug then MESSAGE:New(text, 30):ToAll() end + else env.error(RAT.id.."No destination airport found.") end @@ -1581,36 +1630,58 @@ function RAT:_PickDestination(destinations, _random) return destination end -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Get all possible destination airports depending on departure position. -- The list is sorted w.r.t. distance to departure position. -- @param #RAT self +-- @param Wrapper.Airbase#AIRBASE departure Departure airport or zone. -- @param Core.Point#COORDINATE q Coordinate of the departure point. -- @param #number minrange Minimum range to q in meters. -- @param #number maxrange Maximum range to q in meters. -- @return #table Table with possible destination airports. -- @return #nil If no airports could be found. -function RAT:_GetDestinations(q, minrange, maxrange) +function RAT:_GetDestinations(departure, q, minrange, maxrange) + -- Min/max range to destination. minrange=minrange or self.mindist maxrange=maxrange or self.maxdist + + local possible_destinations={} + if self.random_destination then - -- Initialize array. - local destinations={} - - -- loop over all friendly airports - for _,airport in pairs(self.airports) do - local p=airport:GetCoordinate() - local distance=q:Get2DDistance(p) - -- check if distance form departure to destination is within min/max range - if distance>=minrange and distance<=maxrange then - table.insert(destinations, airport) + -- Airports of friendly coalitions. + for _,airport in pairs(self.airports) do + local name=airport:GetName() + if self:_IsFriendly(name) and not self:_Excluded(name) and name~=departure:GetName() then + + -- Distance from departure to possible destination + local distance=q:Get2DDistance(airport:GetCoordinate()) + + -- Check if distance form departure to destination is within min/max range. + if distance>=minrange and distance<=maxrange then + table.insert(possible_destinations, airport) + end + end end + + else + + -- Airports specified by user. + for _,name in pairs(self.destination_ports) do + --if self:_IsFriendly(name) and not self:_Excluded(name) and name~=departure:GetName() then + if name~=departure:GetName() then + local airport=AIRBASE:FindByName(name) + --TODO: Maybe here I should check min/max distance as well? But the user explicitly specified the airports... + table.insert(possible_destinations, airport) + end + end + end - env.info(RAT.id.."Number of possible destination airports = "..#destinations) - if #destinations > 1 then + -- Info message. + env.info(RAT.id.."Number of possible destination airports = "..#possible_destinations) + + if #possible_destinations > 0 then --- Compare distance of destination airports. -- @param Core.Point#COORDINATE a Coordinate of point a. -- @param Core.Point#COORDINATE b Coordinate of point b. @@ -1620,17 +1691,45 @@ function RAT:_GetDestinations(q, minrange, maxrange) local qb=q:Get2DDistance(b:GetCoordinate()) return qa < qb end - table.sort(destinations, compare) + table.sort(possible_destinations, compare) else env.error(RAT.id.."No possible destination airports found!") - destinations=nil + possible_destinations=nil end -- Return table with destination airports. - return destinations + return possible_destinations end +--- Check if airport is excluded from possible departures and destinations. +-- @param #RAT self +-- @param #string port Name of airport, FARP or ship to check. +-- @return #boolean true if airport is excluded and false otherwise. +function RAT:_Excluded(port) + for _,name in pairs(self.excluded_ports) do + if name==port then + return true + end + end + return false +end + +--- Check if airport is friendly, i.e. belongs to the right coalition. +-- @param #RAT self +-- @param #string port Name of airport, FARP or ship to check. +-- @return #boolean true if airport is friendly and false otherwise. +function RAT:_IsFriendly(port) + for _,airport in pairs(self.airports) do + local name=airport:GetName() + if name==port then + return true + end + end + return false +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Get all airports of the current map. -- @param #RAT self @@ -1674,7 +1773,6 @@ function RAT:_GetAirportsOfMap() end end - --- Get all "friendly" airports of the current map. -- @param #RAT self function RAT:_GetAirportsOfCoalition() @@ -1801,8 +1899,19 @@ function RAT:Status(message, forID) local Hp=COORDINATE:New(self.ratcraft[i].waypoints[6].x, self.ratcraft[i].waypoints[6].alt, self.ratcraft[i].waypoints[6].y) local Dholding=Pn:Get2DDistance(Hp) - -- If distance to holding point is less then 10 km we register the plane - if self.ATC and Dholding<10000 and self.ratcraft[i].status~="Holding" then + -- Status shortcut. + local status=self.ratcraft[i].status + + -- Range from holding point + local DRholding + if self.category==RAT.cat.plane then + DRholding=6000 + else + DRholding=500 + end + + -- If distance to holding point is less then 6 km we register the plane. + if self.ATCswitch and Dholding<=DRholding and status=="On journey (after takeoff)" then RAT:_ATCRegisterFlight(group:GetName(), Tnow) self.ratcraft[i].status="Holding" end @@ -1829,7 +1938,7 @@ function RAT:Status(message, forID) --text=text..string.format("Speed = %i km/h\n", vel) text=text..string.format("Distance travelled = %6.1f km\n", self.ratcraft[i]["Distance"]/1000) text=text..string.format("Distance to destination = %6.1f km\n", Ddestination/1000) - if self.ATC then + if self.ATCswitch then text=text..string.format("Distance to holding point = %6.1f km", Dholding/1000) end if not airborne then @@ -1896,7 +2005,7 @@ end -- @param #RAT self function RAT:_SetStatus(group, status) local index=self:GetSpawnIndexFromGroup(group) - env.info(RAT.id.."Index for group "..group:GetName().." "..index.." status: "..status) + env.info(RAT.id.."Status for group "..group:GetName()..": "..status) self.ratcraft[index].status=status end @@ -2032,7 +2141,7 @@ function RAT:_OnLand(EventData) self:_SetStatus(SpawnGroup, "Taxiing (after landing)") -- ATC plane landed. Take it out of the queue and set runway to free. - if self.ATC then + if self.ATCswitch then RAT:_ATCFlightLanded(SpawnGroup:GetName()) end @@ -2137,6 +2246,8 @@ function RAT:_OnCrash(EventData) local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP + env.info(string.format("%sGroup %s crashed!", RAT.id, SpawnGroup:GetName())) + if SpawnGroup then -- Get the template name of the group. This can be nil if this was not a spawned group. @@ -2449,9 +2560,14 @@ function RAT:_TaskHolding(P1, Altitude, Speed, Duration) DCSTask.id="ControlledTask" DCSTask.params={} DCSTask.params.task=Task - local userflagname=string.format("%s#%03d", self.alias, self.SpawnIndex+1) - env.info("ATC setting user flag name to "..userflagname) - DCSTask.params.stopCondition={userFlag=userflagname, userFlagValue=1, duration=6000} + + if self.ATCswitch then + -- Set stop condition for holding. Either flag=1 or after max. 30 min holding. + local userflagname=string.format("%s#%03d", self.alias, self.SpawnIndex+1) + DCSTask.params.stopCondition={userFlag=userflagname, userFlagValue=1, duration=1800} + else + DCSTask.params.stopCondition={duration=Duration} + end return DCSTask end @@ -2551,17 +2667,7 @@ end -- @param #RAT self function RAT:_SetCoalitionTable() -- get all possible departures/destinations depending on coalition - if self.friendly==RAT.coal.all then - self.ctable={coalition.side.BLUE, coalition.side.RED, coalition.side.NEUTRAL} - elseif self.friendly==RAT.coal.blue then - self.ctable={coalition.side.BLUE, coalition.side.NEUTRAL} - elseif self.friendly==RAT.coal.blueonly then - self.ctable={coalition.side.BLUE} - elseif self.friendly==RAT.coal.red then - self.ctable={coalition.side.RED, coalition.side.NEUTRAL} - elseif self.friendly==RAT.coal.redonly then - self.ctable={coalition.side.RED} - elseif self.friendly==RAT.coal.neutral then + if self.friendly==RAT.coal.neutral then self.ctable={coalition.side.NEUTRAL} elseif self.friendly==RAT.coal.same then self.ctable={self.coalition, coalition.side.NEUTRAL} @@ -2733,36 +2839,19 @@ function RAT:_SetMarker(text, wp) -- Convert to coordinate. local vec={x=wp.x, y=wp.alt, z=wp.y} -- Place maker visible for all on the F10 map. - trigger.action.markToAll(RAT.markerid, text, vec) + local text1=string.format("%s:\n%s", self.alias, text) + trigger.action.markToAll(RAT.markerid, text1, vec) end --- Delete all markers on F10 map. -- @param #RAT self function RAT:_DeleteMarkers() - --self:E({"self ids before: ", self.markerids}) for k,v in ipairs(self.markerids) do - --env.info("Deleting marker id v= "..v) trigger.action.removeMark(v) end for k,v in ipairs(self.markerids) do - --env.info("Removing marker k= "..k) self.markerids[k]=nil end - --self:E({"self ids after: ", self.markerids}) -end - ---- Utility function which checks if table contains a specific value. --- @param #RAT self --- @param #table tab Table with elements to check. --- @param #string val The value we are looking for. --- @return #boolean True if element in the list, false otherwise. -function RAT:has_value (tab, val) - for _,value in pairs(tab) do - if value == val then - return true - end - end - return false end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2817,6 +2906,12 @@ function RAT:_ModifySpawnTemplate(waypoints) -- Onboard number. SpawnTemplate.units[UnitID]["onboard_num"] = self.SpawnIndex + -- Modify coaltion and country of template. + SpawnTemplate.CoalitionID=self.coalition + if self.country then + SpawnTemplate.CountryID=self.country + end + -- Parking spot. UnitTemplate.parking = nil UnitTemplate.parking_id = nil @@ -2853,7 +2948,7 @@ end -- @param #table airports_map List of all airports of the map. function RAT:_ATCInit(airports_map) if not RAT.ATC.init then - env.info("Starting RAT ATC") + env.info(RAT.id.."Starting RAT ATC.") RAT.ATC.init=true for _,ap in pairs(airports_map) do local name=ap:GetName() @@ -2861,12 +2956,26 @@ function RAT:_ATCInit(airports_map) RAT.ATC.airport[name].queue={} RAT.ATC.airport[name].busy=false RAT.ATC.airport[name].onfinal=nil + RAT.ATC.airport[name].traffic=0 end - SCHEDULER:New(nil, RAT._ATCCheck, {self}, 5, 60) - SCHEDULER:New(nil, RAT._ATCStatus, {self}, 5, 120) + SCHEDULER:New(nil, RAT._ATCCheck, {self}, 5, 15) + SCHEDULER:New(nil, RAT._ATCStatus, {self}, 5, 60) + RAT.ATC.T0=timer.getTime() end end +--- Adds andd initializes a new flight after it was spawned. +-- @param #RAT self +-- @param #string name Group name of the flight. +-- @param #string dest Name of the destination airport. +function RAT:_ATCAddFlight(name, dest) + env.info(string.format("%s%s ATC: Adding flight %s with destination %s.", RAT.id, dest, name, dest)) + RAT.ATC.flight[name]={} + RAT.ATC.flight[name].destination=dest + RAT.ATC.flight[name].Tarrive=-1 + RAT.ATC.flight[name].holding=-1 + RAT.ATC.flight[name].Tonfinal=-1 +end --- Deletes a flight from ATC lists after it landed. -- @param #RAT self @@ -2880,47 +2989,113 @@ function RAT:_ATCDelFlight(t,entry) end end ---- Adds andd initializes a new flight after it was spawned. --- @param #RAT self --- @param #string name Group name of the flight. --- @param #string dest Name of the destination airport. -function RAT:_ATCAddFlight(name, dest) - env.info(string.format("ATC: Adding flight %s with destination %s.", name, dest)) - RAT.ATC.flight[name]={} - RAT.ATC.flight[name].destination=dest - RAT.ATC.flight[name].holding=-1 -end - --- Registers a flight once it is near its holding point at the final destination. -- @param #RAT self -- @param #string name Group name of the flight. -- @param #number time Time the fight first registered. function RAT:_ATCRegisterFlight(name, time) - env.info("ATC: Reg name "..name) - env.info("ATC: Reg time "..time) RAT.ATC.flight[name].Tarrive=time RAT.ATC.flight[name].holding=0 end ---- Takes care of organisational stuff after a plane has landed. --- @param #RAT self --- @param #string name Group name of flight. -function RAT:_ATCFlightLanded(name) - -- Destination airport. - local dest=RAT.ATC.flight[name].destination +--- ATC status report about flights. +-- @param #RAT self +function RAT:_ATCStatus() + + -- Current time. + local Tnow=timer.getTime() + + for name,_ in pairs(RAT.ATC.flight) do + + local hold=RAT.ATC.flight[name].holding + local dest=RAT.ATC.flight[name].destination + + if hold >= 0 then + + -- Some string whether the runway is busy or not. + local busy="Runway is currently clear" + if RAT.ATC.airport[dest].busy then + if RAT.ATC.airport[dest].onfinal then + busy="Runway is occupied by "..RAT.ATC.airport[dest].onfinal + else + busy="Runway is occupied" + end + end + + -- Aircraft is holding. + env.info(string.format("%s%s ATC: Flight %s is holding for %i:%02d. %s.", RAT.id, dest, name, hold/60, hold%60, busy)) + + elseif hold==RAT.ATC.onfinal then + + -- Aircarft is on final approach for landing. + local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal + env.info(string.format("%s%s ATC: Flight %s was cleared for landing. Waiting %i:%02d for landing event.", RAT.id, dest, name, Tfinal/60, Tfinal%60)) + + --TODO: Trigger landing for another aircraft when Tfinal > x min? + -- After five minutes we set the runway to green. ==> Increase the landing frequency a bit. + if Tfinal>300 then + RAT.ATC.airport[dest].busy=false + end + + elseif hold==RAT.ATC.unregistered then + + -- Aircraft has not arrived at holding point. + --env.info(string.format("%s ATC: Flight %s is not registered yet (hold %d).", dest, name, hold)) + + else + env.error(RAT.id.."Unknown holding time in RAT:_ATCStatus().") + end + end - -- Airport is not busy any more. - RAT.ATC.airport[dest].busy=false +end + +--- Main ATC function. Updates the landing queue of all airports and inceases holding time for all flights. +-- @param #RAT self +function RAT:_ATCCheck() + + -- Init queue of flights at all airports. + RAT:_ATCQueue() - -- No aircraft on final any more. - RAT.ATC.airport[dest].onfinal=nil + -- Current time. + local Tnow=timer.getTime() - -- Remove this flight from list of flights. - RAT:_ATCDelFlight(RAT.ATC.flight, name) + for name,_ in pairs(RAT.ATC.airport) do - -- Debug info - env.info(string.format("ATC: Flight %s landed at %s.", name, dest)) + + -- List of flights cleared for landing. + local qw={} + + for qID,flight in ipairs(RAT.ATC.airport[name].queue) do + + -- Number of aircraft in queue. + local nqueue=#RAT.ATC.airport[name].queue + + if RAT.ATC.airport[name].busy then + + -- Update holding time. + RAT.ATC.flight[flight].holding=Tnow-RAT.ATC.flight[flight].Tarrive + + -- Debug message. + local text=string.format("%s ATC: Flight %s runway is busy. You are #%d of %d in landing queue. Your holding time is %i:%02d.", name, flight,qID, nqueue, RAT.ATC.flight[flight].holding/60, RAT.ATC.flight[flight].holding%60) + env.info(text) + + else + + -- Clear flight for landing. + RAT:_ATCClearForLanding(name, flight) + table.insert(qw, qID) + + end + + end + + -- Remove cleared flights from queue. + for _,i in pairs(qw) do + table.remove(RAT.ATC.airport[name].queue, i) + end + + end end --- Giving landing clearance for aircraft by setting user flag. @@ -2934,85 +3109,60 @@ function RAT:_ATCClearForLanding(airport, flight) RAT.ATC.airport[airport].busy=true -- Flight which is landing. RAT.ATC.airport[airport].onfinal=flight + -- Current time. + RAT.ATC.flight[flight].Tonfinal=timer.getTime() -- Set user flag to 1 ==> stop condition for holding. trigger.action.setUserFlag(flight, 1) - --local flagvalue=trigger.misc.getUserFlag(flight) - env.info(RAT.id.."ATC: Flight "..flight.."cleared for landing.") + local flagvalue=trigger.misc.getUserFlag(flight) + + -- Debug message. + local text1=string.format("%s%s ATC: Flight %s cleared for final approach (flag=%d).", RAT.id, airport, flight, flagvalue) + local text2=string.format("%s ATC: Flight %s you are cleared for landing.", airport, flight) + env.info(text1) + MESSAGE:New(text2, 10):ToAll() end ---- ATC status report about flights. +--- Takes care of organisational stuff after a plane has landed. -- @param #RAT self -function RAT:_ATCStatus() - - for name,_ in pairs(RAT.ATC.flight) do +-- @param #string name Group name of flight. +function RAT:_ATCFlightLanded(name) - local hold=RAT.ATC.flight[name].holding + if RAT.ATC.flight[name] then + + -- Destination airport. local dest=RAT.ATC.flight[name].destination - if hold >= 0 then - -- Some string whether the runway is busy or not. - local busy="free" - if RAT.ATC.airport[dest].busy then - busy="busy" - end - -- Aircraft is holding. - env.info(string.format("%s ATC: Flight %s is holding for %d (runway is %s).",dest, name, hold, busy)) - elseif hold==RAT.ATC.onfinal then - -- Aircarft is on final approach for landing. - env.info(string.format("%s ATC: Flight %s was cleared for landing. Waiting for landing event.", dest, name)) - elseif hold==RAT.ATC.unregistered then - -- Aircraft has not arrived at holding point. - env.info(string.format("%s ATC: Flight %s is not registered yet (hold %d).", dest, name,hold)) - else - env.info("Unknown holding time.") - end + -- Times for holding and final approach. + local Tnow=timer.getTime() + local Tfinal=Tnow-RAT.ATC.flight[name].Tonfinal + local Thold=RAT.ATC.flight[name].Tonfinal-RAT.ATC.flight[name].Tarrive + + -- Airport is not busy any more. + RAT.ATC.airport[dest].busy=false + + -- No aircraft on final any more. + RAT.ATC.airport[dest].onfinal=nil + + -- Remove this flight from list of flights. + RAT:_ATCDelFlight(RAT.ATC.flight, name) + + -- Increase landing counter to monitor traffic. + RAT.ATC.airport[dest].traffic=RAT.ATC.airport[dest].traffic+1 + + -- Debug info + local text1=string.format("%s%s ATC: Flight %s landed. Tholding = %i:%02d, Tfinal = %i:%02d.", RAT.id, dest, name, Thold/60, Thold%60, Tfinal/60, Tfinal%60) + local text2=string.format("%s ATC: Flight %s landed. Welcome to %s.", dest, name, dest) + env.info(text1) + env.info(string.format("%s%s ATC: Number of planes landed in total %d.", RAT.id, dest, RAT.ATC.airport[dest].traffic)) + MESSAGE:New(text2, 10):ToAll() end end ---- Main ATC function. Updates the landing queue of all airports and inceases holding time for all flights. --- @param #RAT self -function RAT:_ATCCheck() - - -- Init queue of flights at all airports. - RAT:_ATCQueue() - - local Tnow=timer.getTime() - - for name,_ in pairs(RAT.ATC.airport) do - - -- List of finished flights - local qw={} - - for qID,flight in ipairs(RAT.ATC.airport[name].queue) do - - -- Number of aircraft in queue. - local nqueue=#RAT.ATC.airport[name].queue - - if RAT.ATC.airport[name].busy then - --env.info(string.format("%s ATC: Flight %s runway is busy. You are #%d of %d in landing queue.", name, flight,qID, nqueue)) - RAT.ATC.flight[flight].holding=Tnow-RAT.ATC.flight[flight].Tarrive - else - --env.info(string.format("%s ATC: Flight %s you are cleared for landing.", name, flight,qID)) - -- Clear flight for landing - RAT:_ATCClearForLanding(name, flight) - table.insert(qw, qID) - end - - end - - -- Remove flight from queue. - for _,i in pairs(qw) do - table.remove(RAT.ATC.airport[name].queue, i) - end - - end -end - --- Creates a landing queue for all flights holding at airports. Aircraft with longest holding time gets first permission to land. -- @param #RAT self function RAT:_ATCQueue() - --env.info("Queue:") + for airport,_ in pairs(RAT.ATC.airport) do -- Local airport queue. @@ -3042,7 +3192,7 @@ function RAT:_ATCQueue() table.insert(RAT.ATC.airport[airport].queue, v[1]) end - --env.info(airport, dump(RAT.ATC.airport[airport].queue)) - end end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/Moose Mission Setup/Moose.lua b/Moose Mission Setup/Moose.lua index 15b8b7b7f..86ab72f2d 100644 --- a/Moose Mission Setup/Moose.lua +++ b/Moose Mission Setup/Moose.lua @@ -1,5 +1,5 @@ env.info( '*** MOOSE DYNAMIC INCLUDE START *** ' ) -env.info( 'Moose Generation Timestamp: 20170920_1942' ) +env.info( 'Moose Generation Timestamp: 20170924_1034' ) local base = _G