source: src/mlx/flight.py@ 359:fa4e93edd48b

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

Eliminated the parking stage (#150)

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