RunwayManager.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. #!/usr/bin/env python
  2. import copy
  3. from datetime import datetime, timedelta
  4. from aman.config.RunwaySequencing import RunwayAssignmentType
  5. from aman.sys.aco.Configuration import Configuration
  6. from aman.sys.aco.Constraints import SpacingConstraints
  7. from aman.sys.aco.Node import Node
  8. class RunwayManager:
  9. def __init__(self, configuration : Configuration):
  10. self.Spacings = SpacingConstraints()
  11. self.Configuration = configuration
  12. self.RunwayInbounds = copy.deepcopy(configuration.PreceedingRunwayInbounds)
  13. self.IafInbounds = copy.deepcopy(configuration.PreceedingIafInbounds)
  14. def calculateEarliestArrivalTime(self, runway : str, node : Node, earliestArrivalTime : datetime):
  15. constrainedETA = None
  16. if None != self.RunwayInbounds[runway]:
  17. # get the WTC based ETA
  18. if None == self.RunwayInbounds[runway].Inbound.WTC or None == node.Inbound.WTC:
  19. spacingWTC = 3
  20. else:
  21. if self.RunwayInbounds[runway].Inbound.WTC not in self.Spacings.WtcSpacing:
  22. spacingWTC = 3
  23. elif node.Inbound.WTC not in self.Spacings.WtcSpacing[self.RunwayInbounds[runway].Inbound.WTC]:
  24. spacingWTC = self.Spacings.WtcSpacing[self.RunwayInbounds[runway].Inbound.WTC]['L']
  25. else:
  26. spacingWTC = self.Spacings.WtcSpacing[self.RunwayInbounds[runway].Inbound.WTC][node.Inbound.WTC]
  27. # get the runway time spacing
  28. spacingRunway = self.Configuration.RunwayConstraints.findRunway(runway).Spacing
  29. constrainedETA = self.RunwayInbounds[runway].Inbound.PlannedArrivalTime + timedelta(minutes = max(spacingWTC, spacingRunway) / (node.Inbound.PerformanceData.SpeedApproach / 60))
  30. # calculate the arrival times for the dependent inbounds
  31. for dependentRunway in self.Configuration.RunwayConstraints.findDependentRunways(runway):
  32. if None != self.RunwayInbounds[dependentRunway.Runway.Name]:
  33. candidate = self.RunwayInbounds[dependentRunway.Runway.Name].Inbound.PlannedArrivalTime + timedelta(minutes = 3 / (node.Inbound.PerformanceData.SpeedApproach / 60))
  34. if None == constrainedETA or candidate > constrainedETA:
  35. constrainedETA = candidate
  36. if None == constrainedETA:
  37. eta = max(node.ArrivalCandidates[runway].InitialArrivalTime, earliestArrivalTime)
  38. else:
  39. eta = max(node.ArrivalCandidates[runway].InitialArrivalTime, max(constrainedETA, earliestArrivalTime))
  40. return eta, eta - node.ArrivalCandidates[runway].InitialArrivalTime
  41. def selectShallShouldMayArrivalRunway(self, node : Node, runways, earliestArrivalTime : datetime):
  42. candidate = None
  43. delay = None
  44. for runway in runways:
  45. eta, _ = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  46. if None == delay:
  47. delay = eta - node.ArrivalCandidates[runway.Runway.Name].InitialArrivalTime
  48. candidate = runway
  49. elif delay > (eta - node.ArrivalCandidates[runway.Runway.Name].InitialArrivalTime):
  50. delay = eta- node.ArrivalCandidates[runway.Runway.Name].InitialArrivalTime
  51. candidate = runway
  52. return candidate
  53. def executeShallShouldMayAssignment(self, node : Node, earliestArrivalTime : datetime):
  54. shallRunways = []
  55. shouldRunways = []
  56. mayRunways = []
  57. for runway in self.Configuration.RunwayConstraints.ActiveArrivalRunways:
  58. # test the shall assignments
  59. if RunwayAssignmentType.AircraftType in runway.ShallAssignments:
  60. if node.Inbound.Report.aircraft.type in runway.ShallAssignments[RunwayAssignmentType.AircraftType]:
  61. shallRunways.append(runway)
  62. if RunwayAssignmentType.GateAssignment in runway.ShallAssignments:
  63. if node.Inbound.Report.plannedGate in runway.ShallAssignments[RunwayAssignmentType.GateAssignment]:
  64. shallRunways.append(runway)
  65. # test the should assignments
  66. if RunwayAssignmentType.AircraftType in runway.ShouldAssignments:
  67. if node.Inbound.Report.aircraft.type in runway.ShouldAssignments[RunwayAssignmentType.AircraftType]:
  68. shouldRunways.append(runway)
  69. if RunwayAssignmentType.GateAssignment in runway.ShouldAssignments:
  70. if node.Inbound.Report.plannedGate in runway.ShouldAssignments[RunwayAssignmentType.GateAssignment]:
  71. shouldRunways.append(runway)
  72. # test the may assignments
  73. if RunwayAssignmentType.AircraftType in runway.MayAssignments:
  74. if node.Inbound.Report.aircraft.type in runway.MayAssignments[RunwayAssignmentType.AircraftType]:
  75. eta, _ = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  76. if (eta - node.ArrivalCandidates[runway.Runway.Name].InitialArrivalTime) <= self.Configuration.AirportConfiguration.MaxDelayMay:
  77. mayRunways.append(runway)
  78. if RunwayAssignmentType.GateAssignment in runway.MayAssignments:
  79. if node.Inbound.Report.plannedGate in runway.MayAssignments[RunwayAssignmentType.GateAssignment]:
  80. eta, _ = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  81. if (eta - node.ArrivalCandidates[runway.Runway.Name].InitialArrivalTime) <= self.Configuration.AirportConfiguration.MaxDelayMay:
  82. mayRunways.append(runway)
  83. runway = self.selectShallShouldMayArrivalRunway(node, shallRunways, earliestArrivalTime)
  84. if None != runway:
  85. return [ runway ]
  86. runway = self.selectShallShouldMayArrivalRunway(node, shouldRunways, earliestArrivalTime)
  87. if None != runway:
  88. return [ runway ]
  89. runway = self.selectShallShouldMayArrivalRunway(node, mayRunways, earliestArrivalTime)
  90. if None != runway:
  91. return [ runway ]
  92. return self.Configuration.RunwayConstraints.ActiveArrivalRunways
  93. def selectArrivalRunway(self, node : Node, earliestArrivalTime : datetime):
  94. availableRunways = self.Configuration.RunwayConstraints.ActiveArrivalRunways
  95. if 0 == len(availableRunways):
  96. return None, None, None
  97. if True == self.Configuration.RunwayConstraints.UseShallShouldMay and None == node.Inbound.RequestedRunway:
  98. availableRunways = self.executeShallShouldMayAssignment(node, earliestArrivalTime)
  99. elif None != node.Inbound.RequestedRunway:
  100. for runway in availableRunways:
  101. if node.Inbound.RequestedRunway == runway.Runway.Name:
  102. availableRunways = [ runway ]
  103. break
  104. if 0 == len(availableRunways):
  105. runway = self.Configuration.RunwayConstraints.ActiveArrivalRunways[0]
  106. eta, delta = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  107. return runway, eta, delta
  108. # start with the beginning
  109. selectedRunway = None
  110. lostTime = None
  111. eta = None
  112. # get the runway with the earliest ETA
  113. for runway in availableRunways:
  114. candidate, delta = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  115. if None == eta or eta > candidate:
  116. selectedRunway = runway.Runway
  117. lostTime = delta
  118. eta = candidate
  119. # find the corresponding IAF
  120. iaf = node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].Waypoint.Name
  121. if iaf in self.IafInbounds:
  122. delta = 100000.0
  123. targetLevel = None
  124. # find the planned level
  125. for level in self.IafInbounds[iaf]:
  126. difference = abs(level - node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].Altitude)
  127. if difference < delta:
  128. delta = difference
  129. targetLevel = level
  130. if targetLevel in self.IafInbounds[iaf]:
  131. # check if we have to lose time to ensure the IAF spacing
  132. # the function assumes that model allows only TTG during flight to IAF
  133. if None != self.IafInbounds[iaf][targetLevel]:
  134. if None != self.IafInbounds[iaf][targetLevel].Inbound.PlannedArrivalRoute:
  135. # ETA at IAF of preceeding traffic
  136. plannedDelta = self.IafInbounds[iaf][targetLevel].Inbound.PlannedArrivalTime - self.IafInbounds[iaf][targetLevel].Inbound.EnrouteArrivalTime
  137. iafETAPreceeding = self.IafInbounds[iaf][targetLevel].Inbound.PlannedArrivalRoute[0].ETA + plannedDelta
  138. # ETA at IAF of current inbound
  139. plannedDelta = eta - node.Inbound.EnrouteArrivalTime
  140. iafETACurrent = node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].ETA
  141. # required time delte to ensure IAF spacing
  142. timeSpacing = timedelta(hours = self.Configuration.AirportConfiguration.IafSpacing / node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].GroundSpeed)
  143. # we are too close to preceeding traffic
  144. currentTimeSpacing = iafETACurrent - iafETAPreceeding
  145. if timeSpacing > currentTimeSpacing:
  146. eta = eta + (timeSpacing - currentTimeSpacing)
  147. lostTime += (timeSpacing - currentTimeSpacing)
  148. return selectedRunway, eta, lostTime
  149. def registerNode(self, node : Node, runway : str):
  150. self.RunwayInbounds[runway] = node
  151. # find the corresponding IAF
  152. iaf = node.ArrivalCandidates[runway].ArrivalRoute[0].Waypoint.Name
  153. if iaf in self.IafInbounds:
  154. delta = 100000.0
  155. targetLevel = None
  156. # find the planned level
  157. for level in self.IafInbounds[iaf]:
  158. difference = abs(level - node.ArrivalCandidates[runway].ArrivalRoute[0].Altitude)
  159. if difference < delta:
  160. delta = difference
  161. targetLevel = level
  162. if targetLevel in self.IafInbounds[iaf]:
  163. self.IafInbounds[iaf][targetLevel] = node