source: src/mlx/flight.py@ 383:fcb9932b14ee

Last change on this file since 383:fcb9932b14ee was 383:fcb9932b14ee, checked in by István Váradi <ivaradi@…>, 11 years ago

Added the new Cruise page where the cruise level can be modified (#160)

File size: 15.6 KB
Line 
1
2from soundsched import SoundScheduler, ChecklistScheduler
3from checks import SpeedChecker
4
5import const
6import util
7
8import threading
9
10#---------------------------------------------------------------------------------------
11
12## @package mlx.flight
13#
14# The global flight state.
15#
16# This module defines a single class, \ref Flight, which represents the flight
17# in progress.
18
19#---------------------------------------------------------------------------------------
20
21class Flight(object):
22 """The object with the global flight state.
23
24 It is also the hub for the other main objects participating in the handling of
25 the flight."""
26 @staticmethod
27 def canLogCruiseAltitude(stage):
28 """Determine if the cruise altitude can be logged in the given
29 stage."""
30 return stage in [const.STAGE_CRUISE, const.STAGE_DESCENT,
31 const.STAGE_LANDING]
32
33 def __init__(self, logger, gui):
34 """Construct the flight."""
35 self._stage = None
36 self.logger = logger
37 self._gui = gui
38
39 gui.resetFlightStatus()
40
41 self._pilotHotkeyPressed = False
42 self._checklistHotkeyPressed = False
43
44 self.flareTimeFromFS = False
45
46 self.aircraftType = None
47 self.aircraft = None
48 self.simulator = None
49
50 self.blockTimeStart = None
51 self.flightTimeStart = None
52 self.climbTimeStart = None
53 self.cruiseTimeStart = None
54 self.descentTimeStart = None
55 self.flightTimeEnd = None
56 self.blockTimeEnd = None
57
58 self._rtoState = None
59 self._rtoLogEntryID = None
60
61 self._lastDistanceTime = None
62 self._previousLatitude = None
63 self._previousLongitude = None
64 self.flownDistance = 0.0
65
66 self.startFuel = None
67 self.endFuel = None
68
69 self._endCondition = threading.Condition()
70
71 self._flareStart = None
72 self._flareStartFS = None
73
74 self._tdRate = None
75
76 self._soundScheduler = SoundScheduler(self)
77 self._checklistScheduler = ChecklistScheduler(self)
78
79 @property
80 def config(self):
81 """Get the configuration."""
82 return self._gui.config
83
84 @property
85 def stage(self):
86 """Get the flight stage."""
87 return self._stage
88
89 @property
90 def loggedIn(self):
91 """Indicate if the user has logged in properly."""
92 return self._gui.loggedIn
93
94 @property
95 def entranceExam(self):
96 """Get whether an entrance exam is being performed."""
97 return self._gui.entranceExam
98
99 @property
100 def bookedFlight(self):
101 """Get the booked flight."""
102 return self._gui.bookedFlight
103
104 @property
105 def numCrew(self):
106 """Get the number of crew members on the flight."""
107 return self._gui.numCrew
108
109 @property
110 def numPassengers(self):
111 """Get the number of passengers on the flight."""
112 return self._gui.numPassengers
113
114 @property
115 def bagWeight(self):
116 """Get the baggage weight for the flight."""
117 return self._gui.bagWeight
118
119 @property
120 def cargoWeight(self):
121 """Get the cargo weight for the flight."""
122 return self._gui.cargoWeight
123
124 @property
125 def mailWeight(self):
126 """Get the mail weight for the flight."""
127 return self._gui.mailWeight
128
129 @property
130 def zfw(self):
131 """Get the Zero-Fuel Weight of the flight."""
132 return self._gui.zfw
133
134 @property
135 def filedCruiseAltitude(self):
136 """Get the filed cruise altitude."""
137 return self._gui.filedCruiseAltitude
138
139 @property
140 def cruiseAltitude(self):
141 """Get the cruise altitude of the flight."""
142 return self._gui.cruiseAltitude
143
144 @property
145 def route(self):
146 """Get the route of the flight."""
147 return self._gui.route
148
149 @property
150 def departureMETAR(self):
151 """Get the departure METAR of the flight."""
152 return self._gui.departureMETAR
153
154 @property
155 def arrivalMETAR(self):
156 """Get the arrival METAR of the flight."""
157 return self._gui.arrivalMETAR
158
159 @property
160 def departureRunway(self):
161 """Get the departure runway."""
162 return self._gui.departureRunway
163
164 @property
165 def sid(self):
166 """Get the SID followed."""
167 return self._gui.sid
168
169 @property
170 def v1(self):
171 """Get the V1 speed of the flight."""
172 return self._gui.v1
173
174 @property
175 def vr(self):
176 """Get the Vr speed of the flight."""
177 return self._gui.vr
178
179 @property
180 def v2(self):
181 """Get the V2 speed of the flight."""
182 return self._gui.v2
183
184 @property
185 def star(self):
186 """Get the STAR planned."""
187 return self._gui.star
188
189 @property
190 def transition(self):
191 """Get the transition planned."""
192 return self._gui.transition
193
194 @property
195 def approachType(self):
196 """Get the approach type."""
197 return self._gui.approachType
198
199 @property
200 def arrivalRunway(self):
201 """Get the arrival runway."""
202 return self._gui.arrivalRunway
203
204 @property
205 def vref(self):
206 """Get the VRef speed of the flight."""
207 return self._gui.vref
208
209 @property
210 def tdRate(self):
211 """Get the touchdown rate if known, None otherwise."""
212 return self._tdRate
213
214 @property
215 def flightType(self):
216 """Get the type of the flight."""
217 return self._gui.flightType
218
219 @property
220 def online(self):
221 """Get whether the flight was an online flight."""
222 return self._gui.online
223
224 @property
225 def comments(self):
226 """Get the comments made by the pilot."""
227 return self._gui.comments
228
229 @property
230 def flightDefects(self):
231 """Get the flight defects reported by the pilot."""
232 return self._gui.flightDefects
233
234 @property
235 def delayCodes(self):
236 """Get the delay codes."""
237 return self._gui.delayCodes
238
239 @property
240 def speedInKnots(self):
241 """Determine if the speeds for the flight are to be expressed in
242 knots."""
243 return self.aircraft.speedInKnots if self.aircraft is not None \
244 else True
245
246 @property
247 def hasRTO(self):
248 """Determine if we have an RTO state."""
249 return self._rtoState is not None
250
251 @property
252 def rtoState(self):
253 """Get the RTO state."""
254 return self._rtoState
255
256 def handleState(self, oldState, currentState):
257 """Handle a new state information."""
258 self._updateFlownDistance(currentState)
259
260 self.endFuel = currentState.totalFuel
261 if self.startFuel is None:
262 self.startFuel = self.endFuel
263
264 self._soundScheduler.schedule(currentState,
265 self._pilotHotkeyPressed)
266 self._pilotHotkeyPressed = False
267
268 if self._checklistHotkeyPressed:
269 self._checklistScheduler.hotkeyPressed()
270 self._checklistHotkeyPressed = False
271
272 def setStage(self, timestamp, stage):
273 """Set the flight stage.
274
275 Returns if the stage has really changed."""
276 if stage!=self._stage:
277 self._logStageDuration(timestamp, stage)
278 self._stage = stage
279 self._gui.setStage(stage)
280 self.logger.stage(timestamp, stage)
281 if stage==const.STAGE_PUSHANDTAXI:
282 self.blockTimeStart = timestamp
283 elif stage==const.STAGE_TAKEOFF:
284 self.flightTimeStart = timestamp
285 elif stage==const.STAGE_CLIMB:
286 self.climbTimeStart = timestamp
287 elif stage==const.STAGE_CRUISE:
288 self.cruiseTimeStart = timestamp
289 elif stage==const.STAGE_DESCENT:
290 self.descentTimeStart = timestamp
291 elif stage==const.STAGE_TAXIAFTERLAND:
292 self.flightTimeEnd = timestamp
293 # elif stage==const.STAGE_PARKING:
294 # self.blockTimeEnd = timestamp
295 elif stage==const.STAGE_END:
296 self.blockTimeEnd = timestamp
297 with self._endCondition:
298 self._endCondition.notify()
299 return True
300 else:
301 return False
302
303 def handleFault(self, faultID, timestamp, what, score,
304 updatePrevious = False, updateID = None):
305 """Handle the given fault.
306
307 faultID as a unique ID for the given kind of fault. If another fault of
308 this ID has been reported earlier, it will be reported again only if
309 the score is greater than last time. This ID can be, e.g. the checker
310 the report comes from."""
311 id = self.logger.fault(faultID, timestamp, what, score,
312 updatePrevious = updatePrevious,
313 updateID = updateID)
314 self._gui.setRating(self.logger.getRating())
315 return id
316
317 def handleNoGo(self, faultID, timestamp, what, shortReason):
318 """Handle a No-Go fault."""
319 self.logger.noGo(faultID, timestamp, what)
320 self._gui.setNoGo(shortReason)
321
322 def setRTOState(self, state):
323 """Set the state that might be used as the RTO state.
324
325 If there has been no RTO state, the GUI is notified that from now on
326 the user may select to report an RTO."""
327 hadNoRTOState = self._rtoState is None
328
329 self._rtoState = state
330 self._rtoLogEntryID = \
331 SpeedChecker.logSpeedFault(self, state,
332 stage = const.STAGE_PUSHANDTAXI)
333
334 if hadNoRTOState:
335 self._gui.updateRTO()
336
337 def rtoToggled(self, indicated):
338 """Called when the user has toggled the RTO indication."""
339 if self._rtoState is not None:
340 if indicated:
341 self.logger.clearFault(self._rtoLogEntryID,
342 "RTO at %d knots" %
343 (self._rtoState.groundSpeed,))
344 self._gui.setRating(self.logger.getRating())
345 if self._stage == const.STAGE_PUSHANDTAXI:
346 self.setStage(self.aircraft.state.timestamp,
347 const.STAGE_RTO)
348 else:
349 SpeedChecker.logSpeedFault(self, self._rtoState,
350 stage = const.STAGE_PUSHANDTAXI,
351 updateID = self._rtoLogEntryID)
352 if self._stage == const.STAGE_RTO:
353 self.setStage(self.aircraft.state.timestamp,
354 const.STAGE_PUSHANDTAXI)
355
356 def flareStarted(self, flareStart, flareStartFS):
357 """Called when the flare time has started."""
358 self._flareStart = flareStart
359 self._flareStartFS = flareStartFS
360
361 def flareFinished(self, flareEnd, flareEndFS, tdRate):
362 """Called when the flare time has ended.
363
364 Return a tuple of the following items:
365 - a boolean indicating if FS time is used
366 - the flare time
367 """
368 self._tdRate = tdRate
369 if self.flareTimeFromFS:
370 return (True, flareEndFS - self._flareStartFS)
371 else:
372 return (False, flareEnd - self._flareStart)
373
374 def wait(self):
375 """Wait for the flight to end."""
376 with self._endCondition:
377 while self._stage!=const.STAGE_END:
378 self._endCondition.wait(1)
379
380 def getFleet(self, callback, force = False):
381 """Get the fleet and call the given callback."""
382 self._gui.getFleetAsync(callback = callback, force = force)
383
384 def pilotHotkeyPressed(self):
385 """Called when the pilot hotkey is pressed."""
386 self._pilotHotkeyPressed = True
387
388 def checklistHotkeyPressed(self):
389 """Called when the checklist hotkey is pressed."""
390 self._checklistHotkeyPressed = True
391
392 def speedFromKnots(self, knots):
393 """Convert the given speed value expressed in knots into the flight's
394 speed unit."""
395 return knots if self.speedInKnots else knots * const.KNOTSTOKMPH
396
397 def speedToKnots(self, speed):
398 """Convert the given speed expressed in the flight's speed unit into
399 knots."""
400 return speed if self.speedInKnots else speed * const.KMPHTOKNOTS
401
402 def getEnglishSpeedUnit(self):
403 """Get the English name of the speed unit used by the flight."""
404 return "knots" if self.speedInKnots else "km/h"
405
406 def getI18NSpeedUnit(self):
407 """Get the speed unit suffix for i18n message identifiers."""
408 return "_knots" if self.speedInKnots else "_kmph"
409
410 def logFuel(self, aircraftState):
411 """Log the amount of fuel"""
412 fuelStr = ""
413 for (tank, amount) in aircraftState.fuel:
414 if fuelStr: fuelStr += " - "
415 fuelStr += "%s=%.0f kg" % (const.fuelTank2logString(tank), amount)
416
417 self.logger.message(aircraftState.timestamp, "Fuel: " + fuelStr)
418 self.logger.message(aircraftState.timestamp,
419 "Total fuel: %.0f kg" % (aircraftState.totalFuel,))
420
421 def cruiseLevelChanged(self):
422 """Called when the cruise level hass changed."""
423 if self.canLogCruiseAltitude(self._stage):
424 message = "Cruise altitude modified to %d feet" % \
425 (self._gui.loggableCruiseAltitude,)
426 self.logger.message(self.aircraft.timestamp, message)
427 return True
428 else:
429 return False
430
431 def _updateFlownDistance(self, currentState):
432 """Update the flown distance."""
433 if not currentState.onTheGround:
434 updateData = False
435 if self._lastDistanceTime is None or \
436 self._previousLatitude is None or \
437 self._previousLongitude is None:
438 updateData = True
439 elif currentState.timestamp >= (self._lastDistanceTime + 30.0):
440 updateData = True
441 self.flownDistance += self._getDistance(currentState)
442
443 if updateData:
444 self._previousLatitude = currentState.latitude
445 self._previousLongitude = currentState.longitude
446 self._lastDistanceTime = currentState.timestamp
447 else:
448 if self._lastDistanceTime is not None and \
449 self._previousLatitude is not None and \
450 self._previousLongitude is not None:
451 self.flownDistance += self._getDistance(currentState)
452
453 self._lastDistanceTime = None
454
455 def _getDistance(self, currentState):
456 """Get the distance between the previous and the current state."""
457 return util.getDistCourse(self._previousLatitude, self._previousLongitude,
458 currentState.latitude, currentState.longitude)[0]
459
460 def _logStageDuration(self, timestamp, stage):
461 """Log the duration of the stage preceding the given one."""
462 what = None
463 startTime = None
464
465 if stage==const.STAGE_TAKEOFF:
466 what = "Pushback and taxi"
467 startTime = self.blockTimeStart
468 elif stage==const.STAGE_CRUISE:
469 what = "Climb"
470 startTime = self.climbTimeStart
471 elif stage==const.STAGE_DESCENT:
472 what = "Cruise"
473 startTime = self.cruiseTimeStart
474 elif stage==const.STAGE_LANDING:
475 what = "Descent"
476 startTime = self.descentTimeStart
477 elif stage==const.STAGE_END:
478 what = "Taxi after landing"
479 startTime = self.flightTimeEnd
480
481 if what is not None and startTime is not None:
482 duration = timestamp - startTime
483 self.logger.message(timestamp,
484 "%s time: %s" % \
485 (what, util.getTimeIntervalString(duration)))
486
487#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.