source: src/mlx/flight.py@ 346:78e378a97bdf

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

It is now possible to log faults so that the last one is updated instead of writing a new one (#143)

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