source: src/mlx/flight.py@ 349:41c486c8a0b4

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

The basic strobe-less RTO handling logic works (#143)

File size: 13.5 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 with self._endCondition:
280 self._endCondition.notify()
281 return True
282 else:
283 return False
284
285 def handleFault(self, faultID, timestamp, what, score,
286 updatePrevious = False, updateID = None):
287 """Handle the given fault.
288
289 faultID as a unique ID for the given kind of fault. If another fault of
290 this ID has been reported earlier, it will be reported again only if
291 the score is greater than last time. This ID can be, e.g. the checker
292 the report comes from."""
293 id = self.logger.fault(faultID, timestamp, what, score,
294 updatePrevious = updatePrevious,
295 updateID = updateID)
296 self._gui.setRating(self.logger.getRating())
297 return id
298
299 def handleNoGo(self, faultID, timestamp, what, shortReason):
300 """Handle a No-Go fault."""
301 self.logger.noGo(faultID, timestamp, what)
302 self._gui.setNoGo(shortReason)
303
304 def setRTOState(self, state):
305 """Set the state that might be used as the RTO state.
306
307 If there has been no RTO state, the GUI is notified that from now on
308 the user may select to report an RTO."""
309 hadNoRTOState = self._rtoState is None
310
311 self._rtoState = state
312 self._rtoLogEntryID = \
313 SpeedChecker.logSpeedFault(self, state,
314 stage = const.STAGE_PUSHANDTAXI)
315
316 if hadNoRTOState:
317 self._gui.updateRTO()
318
319 def rtoToggled(self, indicated):
320 """Called when the user has toggled the RTO indication."""
321 if self._rtoState is not None:
322 if indicated:
323 self.logger.clearFault(self._rtoLogEntryID,
324 "RTO at %d knots" %
325 (self._rtoState.groundSpeed,))
326 self._gui.setRating(self.logger.getRating())
327 else:
328 SpeedChecker.logSpeedFault(self, self._rtoState,
329 stage = const.STAGE_PUSHANDTAXI,
330 updateID = self._rtoLogEntryID)
331
332 def flareStarted(self, flareStart, flareStartFS):
333 """Called when the flare time has started."""
334 self._flareStart = flareStart
335 self._flareStartFS = flareStartFS
336
337 def flareFinished(self, flareEnd, flareEndFS, tdRate):
338 """Called when the flare time has ended.
339
340 Return a tuple of the following items:
341 - a boolean indicating if FS time is used
342 - the flare time
343 """
344 self._tdRate = tdRate
345 if self.flareTimeFromFS:
346 return (True, flareEndFS - self._flareStartFS)
347 else:
348 return (False, flareEnd - self._flareStart)
349
350 def wait(self):
351 """Wait for the flight to end."""
352 with self._endCondition:
353 while self._stage!=const.STAGE_END:
354 self._endCondition.wait(1)
355
356 def getFleet(self, callback, force = False):
357 """Get the fleet and call the given callback."""
358 self._gui.getFleetAsync(callback = callback, force = force)
359
360 def pilotHotkeyPressed(self):
361 """Called when the pilot hotkey is pressed."""
362 self._pilotHotkeyPressed = True
363
364 def checklistHotkeyPressed(self):
365 """Called when the checklist hotkey is pressed."""
366 self._checklistHotkeyPressed = True
367
368 def speedFromKnots(self, knots):
369 """Convert the given speed value expressed in knots into the flight's
370 speed unit."""
371 return knots if self.speedInKnots else knots * const.KNOTSTOKMPH
372
373 def speedToKnots(self, speed):
374 """Convert the given speed expressed in the flight's speed unit into
375 knots."""
376 return speed if self.speedInKnots else speed * const.KMPHTOKNOTS
377
378 def getEnglishSpeedUnit(self):
379 """Get the English name of the speed unit used by the flight."""
380 return "knots" if self.speedInKnots else "km/h"
381
382 def getI18NSpeedUnit(self):
383 """Get the speed unit suffix for i18n message identifiers."""
384 return "_knots" if self.speedInKnots else "_kmph"
385
386 def logFuel(self, aircraftState):
387 """Log the amount of fuel"""
388 fuelStr = ""
389 for (tank, amount) in aircraftState.fuel:
390 if fuelStr: fuelStr += " - "
391 fuelStr += "%s=%.0f kg" % (const.fuelTank2logString(tank), amount)
392
393 self.logger.message(aircraftState.timestamp, "Fuel: " + fuelStr)
394 self.logger.message(aircraftState.timestamp,
395 "Total fuel: %.0f kg" % (aircraftState.totalFuel,))
396
397 def cruiseLevelChanged(self):
398 """Called when the cruise level hass changed."""
399 if self._stage in [const.STAGE_CRUISE, const.STAGE_DESCENT,
400 const.STAGE_LANDING]:
401 message = "Cruise altitude modified to %d feet" % \
402 (self.cruiseAltitude,)
403 self.logger.message(self.aircraft.timestamp, message)
404
405 def _updateFlownDistance(self, currentState):
406 """Update the flown distance."""
407 if not currentState.onTheGround:
408 updateData = False
409 if self._lastDistanceTime is None or \
410 self._previousLatitude is None or \
411 self._previousLongitude is None:
412 updateData = True
413 elif currentState.timestamp >= (self._lastDistanceTime + 30.0):
414 updateData = True
415 self.flownDistance += self._getDistance(currentState)
416
417 if updateData:
418 self._previousLatitude = currentState.latitude
419 self._previousLongitude = currentState.longitude
420 self._lastDistanceTime = currentState.timestamp
421 else:
422 if self._lastDistanceTime is not None and \
423 self._previousLatitude is not None and \
424 self._previousLongitude is not None:
425 self.flownDistance += self._getDistance(currentState)
426
427 self._lastDistanceTime = None
428
429 def _getDistance(self, currentState):
430 """Get the distance between the previous and the current state."""
431 return util.getDistCourse(self._previousLatitude, self._previousLongitude,
432 currentState.latitude, currentState.longitude)[0]
433
434#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.