141 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			141 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| 
 | |
| from threading import Thread, Lock
 | |
| import sys
 | |
| import time
 | |
| 
 | |
| from aman.com import Weather
 | |
| from aman.config.Airport import Airport
 | |
| from aman.sys.aco.Colony import Colony
 | |
| from aman.sys.aco.Configuration import Configuration
 | |
| from aman.sys.WeatherModel import WeatherModel
 | |
| from aman.sys.RecedingHorizonControl import RecedingHorizonControl
 | |
| from aman.types.Inbound import Inbound
 | |
| from aman.types.PerformanceData import PerformanceData
 | |
| 
 | |
| class Worker(Thread):
 | |
|     def __init__(self):
 | |
|         Thread.__init__(self)
 | |
|         self.StopThread = None
 | |
|         self.Icao = None
 | |
|         self.Configuration = None
 | |
|         self.PerformanceData = None
 | |
|         self.UpdateLock = None
 | |
|         self.ReportQueue = {}
 | |
|         self.WeatherModel = None
 | |
|         self.RecedingHorizonControl = None
 | |
| 
 | |
|     def __del__(self):
 | |
|         self.release()
 | |
| 
 | |
|     def acquire(self, icao : str, configuration : Airport, weather : Weather, performance : PerformanceData):
 | |
|         self.StopThread = None
 | |
|         self.Icao = icao
 | |
|         self.Configuration = configuration
 | |
|         self.sequencingConfiguration = configuration.DefaultSequencing
 | |
|         self.PerformanceData = performance
 | |
|         self.UpdateLock = Lock()
 | |
|         self.ReportQueue = {}
 | |
|         self.WeatherModel = WeatherModel(configuration.GaforId, weather)
 | |
|         self.RecedingHorizonControl = RecedingHorizonControl(configuration.RecedingHorizonControl)
 | |
| 
 | |
|         # merge the constraint information with the GNG information
 | |
|         for runway in self.Configuration.GngData.ArrivalRoutes:
 | |
|             for star in self.Configuration.GngData.ArrivalRoutes[runway]:
 | |
|                 for name in self.Configuration.ArrivalRouteConstraints:
 | |
|                     if name == star.Name:
 | |
|                         for constraint in self.Configuration.ArrivalRouteConstraints[name]:
 | |
|                             foundWaypoint = False
 | |
| 
 | |
|                             for waypoint in star.Route:
 | |
|                                 if constraint.Name == waypoint.Name:
 | |
|                                     waypoint.Altitude = constraint.Altitude
 | |
|                                     waypoint.Speed = constraint.Speed
 | |
|                                     waypoint.BaseTurn = constraint.BaseTurn
 | |
|                                     waypoint.FinalTurn = constraint.FinalTurn
 | |
|                                     foundWaypoint = True
 | |
|                                     break
 | |
| 
 | |
|                             if False == foundWaypoint:
 | |
|                                 sys.stderr.write('Unable to find ' + constraint.Name + ' in ' + name)
 | |
|                                 sys.exit(-1)
 | |
|                         break
 | |
| 
 | |
|         self.start()
 | |
| 
 | |
|     def acquireLock(self):
 | |
|         if None != self.UpdateLock:
 | |
|             self.UpdateLock.acquire()
 | |
| 
 | |
|     def release(self):
 | |
|         self.StopThread = True
 | |
|         self.join()
 | |
| 
 | |
|     def releaseLock(self):
 | |
|         if None != self.UpdateLock:
 | |
|             self.UpdateLock.release()
 | |
| 
 | |
|     def run(self):
 | |
|         counter = 0
 | |
| 
 | |
|         while None == self.StopThread:
 | |
|             time.sleep(1)
 | |
|             counter += 1
 | |
|             # TODO change the counter to some useful values
 | |
|             if 0 != (counter % 10):
 | |
|                 continue
 | |
| 
 | |
|             self.acquireLock()
 | |
| 
 | |
|             # perform some book-keeping
 | |
|             self.RecedingHorizonControl.cleanupWindows()
 | |
| 
 | |
|             # update the aircraft information in RHC
 | |
|             for callsign in self.ReportQueue:
 | |
|                 report = self.ReportQueue[callsign]
 | |
| 
 | |
|                 if 0 != report.distanceToIAF and '' != report.initialApproachFix:
 | |
|                     inbound = Inbound(report, self.sequencingConfiguration, self.Configuration.GngData, self.PerformanceData, self.WeatherModel)
 | |
|                     if None != inbound.PlannedRunway and None != inbound.PlannedStar:
 | |
|                         self.RecedingHorizonControl.update(inbound)
 | |
|                     else:
 | |
|                         print('Unable to find all data of ' + report.aircraft.callsign)
 | |
| 
 | |
|             self.ReportQueue.clear()
 | |
| 
 | |
|             # search the ACO relevant aircrafts
 | |
|             relevantInbounds, earliestArrivalTime = self.RecedingHorizonControl.optimizationRelevantInbounds()
 | |
|             if None != relevantInbounds:
 | |
|                 start = time.process_time()
 | |
| 
 | |
|                 # 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] = inbound
 | |
| 
 | |
|                 # configure the ACO run
 | |
|                 acoConfig = Configuration(constraints = self.sequencingConfiguration, inbounds = relevantInbounds,
 | |
|                                           earliest = earliestArrivalTime, weather = self.WeatherModel,
 | |
|                                           preceeding = None if 0 == len(preceedingInbounds) else preceedingInbounds,
 | |
|                                           ants = 5 * len(relevantInbounds), generations = 5 * len(relevantInbounds))
 | |
| 
 | |
|                 # perform the ACO run
 | |
|                 aco = Colony(acoConfig)
 | |
|                 aco.optimize()
 | |
|                 if None != aco.Result:
 | |
|                     print('ACO-Sequence:')
 | |
|                     for inbound in aco.Result:
 | |
|                         print('    ' + inbound.Report.aircraft.callsign + ': ' + inbound.PlannedRunway.Name + ' @ ' + str(inbound.PlannedArrivalTime) +
 | |
|                               ' dt=' + str((inbound.PlannedArrivalTime - inbound.InitialArrivalTime).total_seconds()))
 | |
|                     print('Delays: FCFS=' + str(aco.FcfsDelay.total_seconds()) + ', ACO=' + str(aco.ResultDelay.total_seconds()))
 | |
| 
 | |
|                 # measure the exuction time of the overall optimization process
 | |
|                 print('Execution time: ' + str(time.process_time() - start) + ' seconds')
 | |
|             else:
 | |
|                 print('No relevant inbounds found for the optimization in ' + self.Icao)
 | |
| 
 | |
|             self.releaseLock()
 |