RunwayManager.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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. expectedRunway = None
  58. for runway in self.Configuration.RunwayConstraints.ActiveArrivalRunways:
  59. # test the shall assignments
  60. if RunwayAssignmentType.AircraftType in runway.ShallAssignments:
  61. if node.Inbound.Report.aircraft.type in runway.ShallAssignments[RunwayAssignmentType.AircraftType]:
  62. shallRunways.append(runway)
  63. expectedRunway = runway.Runway.Name
  64. if RunwayAssignmentType.GateAssignment in runway.ShallAssignments:
  65. if node.Inbound.Report.plannedGate in runway.ShallAssignments[RunwayAssignmentType.GateAssignment]:
  66. shallRunways.append(runway)
  67. expectedRunway = runway.Runway.Name
  68. # test the should assignments
  69. if RunwayAssignmentType.AircraftType in runway.ShouldAssignments:
  70. if node.Inbound.Report.aircraft.type in runway.ShouldAssignments[RunwayAssignmentType.AircraftType]:
  71. shouldRunways.append(runway)
  72. expectedRunway = runway.Runway.Name
  73. if RunwayAssignmentType.GateAssignment in runway.ShouldAssignments:
  74. if node.Inbound.Report.plannedGate in runway.ShouldAssignments[RunwayAssignmentType.GateAssignment]:
  75. shouldRunways.append(runway)
  76. expectedRunway = runway.Runway.Name
  77. # test the may assignments
  78. if RunwayAssignmentType.AircraftType in runway.MayAssignments:
  79. if node.Inbound.Report.aircraft.type in runway.MayAssignments[RunwayAssignmentType.AircraftType]:
  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. expectedRunway = runway.Runway.Name
  84. if RunwayAssignmentType.GateAssignment in runway.MayAssignments:
  85. if node.Inbound.Report.plannedGate in runway.MayAssignments[RunwayAssignmentType.GateAssignment]:
  86. eta, _ = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  87. if (eta - node.ArrivalCandidates[runway.Runway.Name].InitialArrivalTime) <= self.Configuration.AirportConfiguration.MaxDelayMay:
  88. mayRunways.append(runway)
  89. expectedRunway = runway.Runway.Name
  90. runway = self.selectShallShouldMayArrivalRunway(node, shallRunways, earliestArrivalTime)
  91. if None != runway:
  92. return 'shall', expectedRunway, [ runway ]
  93. runway = self.selectShallShouldMayArrivalRunway(node, shouldRunways, earliestArrivalTime)
  94. if None != runway:
  95. return 'should', expectedRunway, [ runway ]
  96. runway = self.selectShallShouldMayArrivalRunway(node, mayRunways, earliestArrivalTime)
  97. if None != runway:
  98. return 'may', expectedRunway, [ runway ]
  99. return 'other', None, self.Configuration.RunwayConstraints.ActiveArrivalRunways
  100. def selectArrivalRunway(self, node : Node, earliestArrivalTime : datetime):
  101. availableRunways = self.Configuration.RunwayConstraints.ActiveArrivalRunways
  102. if 0 == len(availableRunways):
  103. return None, None, None, None, None
  104. expectedRunway = None
  105. if True == self.Configuration.RunwayConstraints.UseShallShouldMay and None == node.Inbound.RequestedRunway:
  106. type, expectedRunway, availableRunways = self.executeShallShouldMayAssignment(node, earliestArrivalTime)
  107. elif None != node.Inbound.RequestedRunway:
  108. for runway in availableRunways:
  109. if node.Inbound.RequestedRunway == runway.Runway.Name:
  110. availableRunways = [ runway ]
  111. type = 'other'
  112. break
  113. if 0 == len(availableRunways):
  114. runway = self.Configuration.RunwayConstraints.ActiveArrivalRunways[0]
  115. eta, delta = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  116. return 'other', None, runway, eta, delta
  117. # start with the beginning
  118. selectedRunway = None
  119. lostTime = None
  120. eta = None
  121. # get the runway with the earliest ETA
  122. for runway in availableRunways:
  123. candidate, delta = self.calculateEarliestArrivalTime(runway.Runway.Name, node, earliestArrivalTime)
  124. if None == eta or eta > candidate:
  125. selectedRunway = runway.Runway
  126. lostTime = delta
  127. eta = candidate
  128. # find the corresponding IAF
  129. iaf = node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].Waypoint.Name
  130. if iaf in self.IafInbounds:
  131. delta = 100000.0
  132. targetLevel = None
  133. # find the planned level
  134. for level in self.IafInbounds[iaf]:
  135. difference = abs(level - node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].Altitude)
  136. if difference < delta:
  137. delta = difference
  138. targetLevel = level
  139. if targetLevel in self.IafInbounds[iaf]:
  140. # check if we have to lose time to ensure the IAF spacing
  141. # the function assumes that model allows only TTG during flight to IAF
  142. if None != self.IafInbounds[iaf][targetLevel]:
  143. if None != self.IafInbounds[iaf][targetLevel].Inbound.PlannedArrivalRoute:
  144. # ETA at IAF of preceeding traffic
  145. plannedDelta = self.IafInbounds[iaf][targetLevel].Inbound.PlannedArrivalTime - self.IafInbounds[iaf][targetLevel].Inbound.EnrouteArrivalTime
  146. iafETAPreceeding = self.IafInbounds[iaf][targetLevel].Inbound.PlannedArrivalRoute[0].ETA + plannedDelta
  147. # ETA at IAF of current inbound
  148. plannedDelta = eta - node.Inbound.EnrouteArrivalTime
  149. iafETACurrent = node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].ETA
  150. # required time delte to ensure IAF spacing
  151. timeSpacing = timedelta(hours = self.Configuration.AirportConfiguration.IafSpacing / node.ArrivalCandidates[selectedRunway.Name].ArrivalRoute[0].GroundSpeed)
  152. # we are too close to preceeding traffic
  153. currentTimeSpacing = iafETACurrent - iafETAPreceeding
  154. if timeSpacing > currentTimeSpacing:
  155. eta = eta + (timeSpacing - currentTimeSpacing)
  156. lostTime += (timeSpacing - currentTimeSpacing)
  157. return type, expectedRunway, selectedRunway, eta, lostTime
  158. def registerNode(self, node : Node, runway : str):
  159. self.RunwayInbounds[runway] = node
  160. # find the corresponding IAF
  161. iaf = node.ArrivalCandidates[runway].ArrivalRoute[0].Waypoint.Name
  162. if iaf in self.IafInbounds:
  163. delta = 100000.0
  164. targetLevel = None
  165. # find the planned level
  166. for level in self.IafInbounds[iaf]:
  167. difference = abs(level - node.ArrivalCandidates[runway].ArrivalRoute[0].Altitude)
  168. if difference < delta:
  169. delta = difference
  170. targetLevel = level
  171. if targetLevel in self.IafInbounds[iaf]:
  172. self.IafInbounds[iaf][targetLevel] = node