145 regels
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			145 regels
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env python
 | |
| 
 | |
| import time
 | |
| 
 | |
| from datetime import datetime as dt
 | |
| from datetime import timedelta
 | |
| 
 | |
| import pytz
 | |
| 
 | |
| from aman.config.RHC import RHC
 | |
| from aman.sys.RecedingHorizonWindow import RecedingHorizonWindow
 | |
| from aman.types.Inbound import Inbound
 | |
| 
 | |
| class RecedingHorizonControl:
 | |
|     def __init__(self, config : RHC):
 | |
|         self.Windows = []
 | |
|         # contains the current index and the missed update counter
 | |
|         self.AssignedWindow = {}
 | |
|         self.Configuration = config
 | |
|         self.FreezedIndex = int(self.Configuration.FixedBeforeArrival.seconds / self.Configuration.WindowSize)
 | |
| 
 | |
|     def update(self, inbound : Inbound):
 | |
|         # check if we need to update
 | |
|         if inbound.Report.aircraft.callsign in self.AssignedWindow:
 | |
|             index = self.AssignedWindow[inbound.Report.aircraft.callsign][0]
 | |
|             self.AssignedWindow[inbound.Report.aircraft.callsign][1] = 0
 | |
| 
 | |
|             # check if we assume the scheduling as fixed
 | |
|             if index < self.FreezedIndex:
 | |
|                 return
 | |
| 
 | |
|             plannedInbound = self.Windows[index].inbound(inbound.Report.aircraft.callsign)
 | |
|             plannedInbound.CurrentPosition = inbound.CurrentPosition
 | |
|             plannedInbound.ArrivalCandidates = inbound.ArrivalCandidates
 | |
| 
 | |
|             # check if we need to update the inbound
 | |
|             if plannedInbound.PlannedArrivalTime < inbound.EarliestArrivalTime:
 | |
|                 if inbound.EarliestArrivalTime < self.Windows[index].StartTime or inbound.EarliestArrivalTime >= self.Windows[index].EndTime:
 | |
|                     self.Windows[index].remove(inbound.Report.aircraft.callsign)
 | |
|                     self.AssignedWindow.pop(inbound.Report.aircraft.callsign)
 | |
|                     self.update(inbound)
 | |
|                 else:
 | |
|                     plannedInbound.PlannedStar = inbound.PlannedStar
 | |
|                     plannedInbound.PlannedRunway = inbound.PlannedRunway
 | |
|                     plannedInbound.InitialArrivalTime = inbound.InitialArrivalTime
 | |
|                     plannedInbound.EarliestArrivalTime = inbound.EarliestArrivalTime
 | |
|                     plannedInbound.PlannedArrivalTime = inbound.EarliestArrivalTime
 | |
|         else:
 | |
|             inserted = False
 | |
|             for i in range(0, len(self.Windows)):
 | |
|                 window = self.Windows[i]
 | |
| 
 | |
|                 # find the correct window
 | |
|                 if window.StartTime <= inbound.EarliestArrivalTime and window.EndTime > inbound.EarliestArrivalTime:
 | |
|                     if i > self.FreezedIndex:
 | |
|                         self.AssignedWindow[inbound.Report.aircraft.callsign] = [ i, 0 ]
 | |
|                         inbound.PlannedArrivalTime = inbound.EarliestArrivalTime
 | |
|                         window.insert(inbound)
 | |
|                     inserted = True
 | |
|                     break
 | |
| 
 | |
|             # create the new window
 | |
|             if False == inserted:
 | |
|                 if 0 != len(self.Windows):
 | |
|                     lastWindowTime = self.Windows[-1].EndTime
 | |
|                 else:
 | |
|                     lastWindowTime = dt.utcfromtimestamp(int(time.time())).replace(tzinfo = pytz.UTC)
 | |
|                 timestep = timedelta(seconds = self.Configuration.WindowSize)
 | |
| 
 | |
|                 while True:
 | |
|                     self.Windows.append(RecedingHorizonWindow(lastWindowTime, lastWindowTime + timestep))
 | |
|                     if self.Windows[-1].EndTime > inbound.EarliestArrivalTime:
 | |
|                         self.AssignedWindow[inbound.Report.aircraft.callsign] = [ len(self.Windows) - 1, 0 ]
 | |
|                         self.Windows[-1].insert(inbound)
 | |
|                         inbound.PlannedArrivalTime = max(self.Windows[-1].StartTime, inbound.EarliestArrivalTime)
 | |
|                         self.Windows[-1].Inbounds.sort(key = lambda x: x.PlannedArrivalTime)
 | |
|                         break
 | |
|                     lastWindowTime = self.Windows[-1].EndTime
 | |
| 
 | |
|     def lastFixedInboundOnRunway(self, runway : str):
 | |
|         # no inbounds available
 | |
|         if 0 == len(self.Windows):
 | |
|             return None
 | |
| 
 | |
|         # search from the back to the front to find the last inbound
 | |
|         for i in range(min(self.FreezedIndex, len(self.Windows)), -1, -1):
 | |
|             for inbound in self.Windows[i].Inbounds:
 | |
|                 if runway == inbound.PlannedRunway.Runway.name:
 | |
|                     return inbound
 | |
| 
 | |
|         # no inbound found
 | |
|         return None
 | |
| 
 | |
|     def optimizationRelevantInbounds(self):
 | |
|         # no new inbounds
 | |
|         if len(self.Windows) <= self.FreezedIndex:
 | |
|             return None, None
 | |
| 
 | |
|         inbounds = []
 | |
|         earliestArrivalTime = None
 | |
| 
 | |
|         # check the overlapping windows
 | |
|         #for i in range(self.FreezedIndex + 1, min(len(self.Windows), self.FreezedIndex + 1 + self.Configuration.WindowOverlap)):
 | |
|         for i in range(0, len(self.Windows)):
 | |
|             if None == earliestArrivalTime:
 | |
|                 earliestArrivalTime = self.Windows[i].StartTime
 | |
|             for inbound in self.Windows[i].Inbounds:
 | |
|                 inbounds.append(inbound)
 | |
| 
 | |
|         # check if we found relevant inbounds
 | |
|         if 0 != len(inbounds):
 | |
|             inbounds.sort(key = lambda x: x.InitialArrivalTime)
 | |
|             return inbounds, earliestArrivalTime
 | |
|         else:
 | |
|             return None, None
 | |
| 
 | |
|     def cleanupWindows(self):
 | |
|         currentUtc = dt.utcfromtimestamp(int(time.time())).replace(tzinfo = pytz.UTC)
 | |
|         offsetCorrection = 0
 | |
| 
 | |
|         # delete the non-required windows
 | |
|         while 0 != len(self.Windows) and currentUtc > self.Windows[0].EndTime:
 | |
|             # cleanup the association table
 | |
|             for inbound in self.Windows[0].Inbounds:
 | |
|                 self.AssignedWindow.pop(inbound.Report.aircraft.callsign)
 | |
| 
 | |
|             offsetCorrection += 1
 | |
|             self.Windows.pop(0)
 | |
| 
 | |
|         # correct the association table
 | |
|         if 0 != offsetCorrection:
 | |
|             for callsign in self.AssignedWindow:
 | |
|                 self.AssignedWindow[callsign][0] -= offsetCorrection
 | |
| 
 | |
|         # delete the non-updated aircrafts and increase the missed-counter for later runs
 | |
|         callsigns = []
 | |
|         for callsign in self.AssignedWindow:
 | |
|             if 2 < self.AssignedWindow[callsign][1]:
 | |
|                 self.Windows[self.AssignedWindow[callsign][0]].remove(callsign)
 | |
|                 callsigns.append(callsign)
 | |
|             self.AssignedWindow[callsign][1] += 1
 | |
| 
 | |
|         for callsign in callsigns:
 | |
|             self.AssignedWindow.pop(callsign)
 |