diff --git a/aman/sys/RecedingHorizonControl.py b/aman/sys/RecedingHorizonControl.py index d1f98b7..298bbbd 100644 --- a/aman/sys/RecedingHorizonControl.py +++ b/aman/sys/RecedingHorizonControl.py @@ -7,7 +7,10 @@ from datetime import timedelta import pytz +from aman.config.Airport import Airport +from aman.config.AirportSequencing import AirportSequencing from aman.config.RHC import RHC +from aman.sys.aco.Node import Node from aman.sys.RecedingHorizonWindow import RecedingHorizonWindow from aman.types.Inbound import Inbound @@ -106,19 +109,56 @@ class RecedingHorizonControl: inbound.FixedSequence = index < self.FreezedIndex self.Windows[index].Inbounds.sort(key = lambda x: x.PlannedArrivalTime if None != x.PlannedArrivalTime else x.EnrouteArrivalTime) - def lastFixedInboundOnRunway(self, runway : str): - # no inbounds available + def latestFixedInbounds(self, configuration : Airport, sequenceConfiguration : AirportSequencing): if 0 == len(self.Windows): - return None + return None, None - # search from the back to the front to find the last inbound + # create the runway tree + runwayInbounds = {} + for runway in sequenceConfiguration.ActiveArrivalRunways: + runwayInbounds[runway.Runway.Name] = None + + # create the IAF tree + iafInbounds = {} + for star in configuration.ArrivalRouteConstraints: + altitude = configuration.ArrivalRouteConstraints[star][0].Altitude + iaf = configuration.ArrivalRouteConstraints[star][0].Name + + if iaf not in iafInbounds: + iafInbounds[iaf] = { altitude : None } + elif altitude not in iafInbounds[iaf]: + iafInbounds[iaf][altitude] = None + + # associate the inbounds to the runways and the IAFs for i in range(min(self.FreezedIndex, len(self.Windows)), -1, -1): for inbound in self.Windows[i].Inbounds: - if None != inbound.PlannedRunway and runway == inbound.PlannedRunway.Name: - return inbound + if None == inbound.PlannedRunway or None == inbound.PlannedArrivalRoute: + continue - # no inbound found - return None + node = Node(inbound, None, None, None, None) + node.EstimatedTouchdownTime = inbound.PlannedArrivalTime + node.EstimatedIafAltitude = inbound.PlannedArrivalRoute[0].Altitude + node.EstimatedIafTime = inbound.PlannedArrivalRoute[0].PTA + + if inbound.PlannedRunway.Name in runwayInbounds: + if None == runwayInbounds[inbound.PlannedRunway.Name] or runwayInbounds[inbound.PlannedRunway.Name].EstimatedTouchdownTime < node.EstimatedTouchdownTime: + runwayInbounds[inbound.PlannedRunway.Name] = node + + if inbound.PlannedArrivalRoute[0].Name in iafInbounds: + delta = 100000.0 + targetLevel = None + for level in iafInbounds[inbound.PlannedArrivalRoute[0].Name]: + difference = abs(level - inbound.PlannedArrivalRoute[0].Altitude) + if difference < delta: + delta = difference + targetLevel = level + + if None == iafInbounds[inbound.PlannedArrivalRoute[0].Name][targetLevel]: + iafInbounds[inbound.PlannedArrivalRoute[0].Name][targetLevel] = Node + elif iafInbounds[inbound.PlannedArrivalRoute[0].Name][targetLevel].EstimatedIafTime < node.EstimatedIafTime: + iafInbounds[inbound.PlannedArrivalRoute[0].Name][targetLevel] = Node + + return runwayInbounds, iafInbounds def optimizationRelevantInbounds(self): # no new inbounds diff --git a/aman/sys/Worker.py b/aman/sys/Worker.py index d568d70..f52757e 100644 --- a/aman/sys/Worker.py +++ b/aman/sys/Worker.py @@ -102,16 +102,12 @@ class Worker(Thread): # get the last landing aircrafts per runway before the RHC stage to check for constraints # this is required to handle the overlap between windows - preceedingInbounds = {} - for runway in self.SequencingConfiguration.ActiveArrivalRunways: - inbound = self.RecedingHorizonControl.lastFixedInboundOnRunway(runway.Runway.Name) - if None != inbound: - preceedingInbounds[runway.Runway.Name] = Node(inbound, None, None, None, None) + runways, iafs = self.RecedingHorizonControl.latestFixedInbounds(self.Configuration, self.SequencingConfiguration) # configure the ACO run acoConfig = Configuration(constraints = self.SequencingConfiguration, config = self.Configuration, earliest = earliestArrivalTime, weather = self.WeatherModel, - preceeding = None if 0 == len(preceedingInbounds) else preceedingInbounds, + preceedingRunways = runways, preceedingIafs = iafs, ants = 5 * len(relevantInbounds), generations = 5 * len(relevantInbounds)) # perform the ACO run diff --git a/aman/sys/aco/Configuration.py b/aman/sys/aco/Configuration.py index 07d6d48..95e5e03 100644 --- a/aman/sys/aco/Configuration.py +++ b/aman/sys/aco/Configuration.py @@ -6,7 +6,8 @@ class Configuration: def __init__(self, **kwargs): # the AMAN specific information self.RunwayConstraints = kwargs.get('constraints', None) - self.PreceedingInbounds = kwargs.get('preceeding', None) + self.PreceedingRunwayInbounds = kwargs.get('preceedingRunways', None) + self.PreceedingIafInbounds = kwargs.get('preceedingIafs', None) self.EarliestArrivalTime = kwargs.get('earliest', None) self.WeatherModel = kwargs.get('weather', None) self.AirportConfiguration = kwargs.get('config', None) diff --git a/aman/sys/aco/RunwayManager.py b/aman/sys/aco/RunwayManager.py index 590debb..0426d5d 100644 --- a/aman/sys/aco/RunwayManager.py +++ b/aman/sys/aco/RunwayManager.py @@ -1,5 +1,7 @@ #!/usr/bin/env python +import copy + from datetime import datetime, timedelta from aman.config.RunwaySequencing import RunwayAssignmentType @@ -11,15 +13,9 @@ class RunwayManager: def __init__(self, configuration : Configuration): self.Spacings = SpacingConstraints() self.Configuration = configuration + self.RunwayInbounds = copy.deepcopy(configuration.PreceedingRunwayInbounds) + self.IafInbounds = copy.deepcopy(configuration.PreceedingIafInbounds) - # initialize the tracker which inbound arrives at which runway - self.RunwayInbounds = {} - if None != configuration.PreceedingInbounds: - for runway in configuration.PreceedingInbounds: - self.RunwayInbounds[runway] = configuration.PreceedingInbounds[runway] - for runway in configuration.RunwayConstraints.ActiveArrivalRunways: - if not runway.Runway.Name in self.RunwayInbounds: - self.RunwayInbounds[runway.Runway.Name] = None def calculateEarliestArrivalTime(self, runway : str, node : Node, earliestArrivalTime : datetime): constrainedETA = None