source: src/mlx/acft.py@ 1136:fdb94a9baec1

python3
Last change on this file since 1136:fdb94a9baec1 was 1116:7eaafcf70da9, checked in by István Váradi <ivaradi@…>, 6 months ago

SimBrief support update for various aircraft types (re #376)

File size: 51.1 KB
Line 
1
2from . import const
3from . import gates
4from . import checks
5from . import fs
6from .i18n import xstr
7from . import util
8
9import sys
10import time
11import traceback
12
13from collections import deque
14
15#---------------------------------------------------------------------------------------
16
17## @package mlx.acft
18#
19# The simulator-independent aircraft classes.
20#
21# This module contains the aircraft classes that contain some general data
22# about each aircraft type in the MAVA Fleet. The base class, \ref Aircraft
23# also implements some parts of the logging and traces some data. The classes
24# are also responsible for creating the \ref mlx.checks "checkers". Most of
25# them are created by the \ref Aircraft class' constructor, but there can be
26# type-specific differences. For example the lights are different for different
27# types, so there is a \ref Aircraft._appendLightsLoggers "function" which can
28# be reimplemented in child classes, if needed. This class maintains also the
29# \ref SmoothedValue "smoothed values" of the IAS and the VS and set these
30# values in the \ref mlx.fs.AircraftState "aircraft state" when it is received
31# from the simulator.
32
33#---------------------------------------------------------------------------------------
34
35# Derate type: no derate possible
36DERATE_NONE = 0
37
38# Derate type: Boeing, i.e. a percentage value.
39# For logging, the percentage value is expected as a string (i.e. whatever the
40# pilot enters into the text field).
41DERATE_BOEING = 1
42
43# Derate type: EPR, i.e. an EPR value.
44# For logging, the EPR value is expected as a string (i.e. whatever the pilot
45# enters into the text field).
46DERATE_EPR = 2
47
48# Derate type: Tupolev, i.e. nominal or takeoff
49# For logging, one of the DERATE_TUPOLEV_xxx values are expected.
50DERATE_TUPOLEV = 3
51
52# Tupolev derate value: nominal
53DERATE_TUPOLEV_NOMINAL = 1
54
55# Tupolev derate value: takeoff
56DERATE_TUPOLEV_TAKEOFF = 2
57
58# Derate type: BAe-146, i.e. enabled or not
59# For logging, a boolean is expected.
60DERATE_B462 = 4
61
62#---------------------------------------------------------------------------------------
63
64class SmoothedValue(object):
65 """A smoothed value."""
66 def __init__(self):
67 """Construct the value."""
68 self._deque = deque()
69 self._sum = 0
70
71 def add(self, length, value):
72 """Add the given value and smooth with the given length."""
73 dequeLength = len(self._deque)
74 while dequeLength>=length:
75 self._sum -= self._deque.popleft()
76 dequeLength -= 1
77
78 self._sum += value
79 self._deque.append(value)
80
81 def get(self):
82 """Get the average."""
83 return self._sum / len(self._deque)
84
85#---------------------------------------------------------------------------------------
86
87class SimBriefData(object):
88 """Data to be used when creating SimBrief briefings."""
89 def __init__(self, climbProfiles, cruiseProfiles, descentProfiles,
90 cruiseParameters = {}):
91 """Construct the SimBrief data with the given profiles.
92
93 cruiseParameters is a dictionary keyed by the cruise profile index. It
94 contains a tuple of:
95 - a boolean indicating if the parameter is mandatory,
96 - the name of the parameter for the SimBrief API
97 """
98 self.climbProfiles = climbProfiles
99 self.cruiseProfiles = cruiseProfiles
100 self.descentProfiles = descentProfiles
101 self.cruiseParameters = cruiseParameters
102
103#---------------------------------------------------------------------------------------
104
105class Aircraft(object):
106 """Base class for aircraft."""
107 @staticmethod
108 def create(flight, bookedFlight):
109 """Create an aircraft instance for the type in the given flight."""
110 acft = _classes[flight.aircraftType](flight)
111 acft.setBookedFlight(bookedFlight)
112 return acft
113
114 def __init__(self, flight, minLandingFuel = None,
115 recommendedLandingFuel = None):
116 """Construct the aircraft for the given type."""
117 self._flight = flight
118 self._minLandingFuel = minLandingFuel
119 self._recommendedLandingFuel = recommendedLandingFuel
120
121 self._name = None
122 self._modelName = None
123
124 self._aircraftState = None
125
126 self._maxVS = -10000.0
127 self._minVS = 10000.0
128
129 self._v1r2LineIndex = None
130 self._derateLineID = None
131 self._takeoffAntiIceLineID = None
132 self._vrefLineIndex = None
133 self._landingAntiIceLineID = None
134
135 self.humanWeight = 82.0
136
137 self.initialClimbSpeedAltitude = 1500
138 self.reverseMinSpeed = 50
139
140 self.hasStrobeLight = True
141
142 self.maxTakeOffPitch = 15.0
143 self.maxTouchDownPitch = 15.0
144 self.brakeCoolTime = 10.0
145
146 self.simBriefData = None
147
148 self._checkers = []
149
150 config = flight.config
151 # Loggers
152
153 self._checkers.append(checks.StageChecker())
154 self._checkers.append(checks.TakeOffLogger())
155
156 self._checkers.append(checks.AltimeterLogger())
157
158 self._ilsLogger = checks.ILSLogger()
159 self._checkers.append(self._ilsLogger)
160 self._nav1Logger = checks.NAV1Logger()
161 self._checkers.append(self._nav1Logger)
162 self._nav2Logger = checks.NAV2Logger()
163 self._checkers.append(self._nav2Logger)
164
165 self._adf1Logger = checks.ADF1Logger()
166 self._checkers.append(self._adf1Logger)
167 self._adf2Logger = checks.ADF2Logger()
168 self._checkers.append(self._adf2Logger)
169
170 self._checkers.append(checks.SquawkLogger())
171
172 self._appendLightsLoggers()
173
174 self._checkers.append(checks.FlapsLogger())
175
176 self._checkers.append(checks.GearsLogger())
177 self._checkers.append(checks.InitialClimbSpeedLogger())
178 self._checkers.append(checks.CruiseSpeedLogger())
179 self._checkers.append(checks.SpoilerLogger())
180 self._checkers.append(checks.APLogger())
181
182 if config.isMessageTypeFS(const.MESSAGETYPE_VISIBILITY):
183 self._checkers.append(checks.VisibilityChecker())
184
185 # FIXME: we should have a central data model object, and not collect
186 # the data from the GUI. However, some pieces of data (e.g. V-speeds,
187 # etc. that is entered into the GUI) *should* be a part of the GUI and
188 # queried from it, so the model should have a reference to the GUI as
189 # well and access such data via the GUI!
190 if config.onlineACARS and flight.loggedIn and not flight.entranceExam:
191 self._checkers.append(checks.ACARSSender(flight._gui))
192
193 # Fault checkers
194
195 self._appendLightsCheckers()
196
197 self._checkers.append(checks.TransponderChecker())
198
199 self._checkers.append(checks.BankChecker())
200
201 self._checkers.append(checks.FlapsRetractChecker())
202 self._checkers.append(checks.FlapsSpeedLimitChecker())
203
204 self._checkers.append(checks.GearsDownChecker())
205 self._checkers.append(checks.GearSpeedLimitChecker())
206
207 self._checkers.append(checks.GLoadChecker())
208
209 self._checkers.append(checks.MLWChecker())
210 self._checkers.append(checks.MTOWChecker())
211 self._checkers.append(checks.MZFWChecker())
212 self._checkers.append(checks.PayloadChecker())
213
214 self._checkers.append(checks.SpeedChecker())
215 self._checkers.append(checks.VSChecker())
216
217 timeout = 30.0 + config.realIASSmoothingLength - 1
218 self._checkers.append(checks.OverspeedChecker(faultTimeout = timeout))
219
220 self._checkers.append(checks.StallChecker())
221
222 self._checkers.append(checks.PitotChecker())
223
224 self._checkers.append(checks.ReverserLogger())
225 self._checkers.append(checks.ReverserChecker())
226
227 if flight.aircraftType is not None and config.enableApproachCallouts:
228 approachCallouts = flight.config.getApproachCallouts(flight.aircraftType)
229 if approachCallouts:
230 self._checkers.append(checks.ApproachCalloutsPlayer(approachCallouts))
231
232 self._smoothedIAS = SmoothedValue()
233 self._smoothedVS = SmoothedValue()
234
235 @property
236 def type(self):
237 """Get the type of the aircraft."""
238 return self._flight.aircraftType
239
240 @property
241 def flight(self):
242 """Get the flight the aircraft belongs to."""
243 return self._flight
244
245 @property
246 def minLandingFuel(self):
247 """Get the minimum acceptable amount of the landing fuel."""
248 return self._minLandingFuel
249
250 @property
251 def recommendedLandingFuel(self):
252 """Get the recommended amount of the landing fuel."""
253 return self._recommendedLandingFuel
254
255 @property
256 def logger(self):
257 """Get the logger to use for the aircraft."""
258 return self._flight.logger
259
260 @property
261 def state(self):
262 """Get the current aircraft state."""
263 return self._aircraftState
264
265 @property
266 def speedInKnots(self):
267 """Indicate if the speed is in knots.
268
269 This default implementation returns True."""
270 return True
271
272 @property
273 def aglInFeet(self):
274 """Indicate if AGL altitudes are to be logged in feet.
275
276 This default implementation returns True."""
277 return True
278
279 @property
280 def timestamp(self):
281 """Get the timestamp of the current state."""
282 return None if self._aircraftState is None \
283 else self._aircraftState.timestamp
284
285 @property
286 def derateType(self):
287 """Get the derate type for this aircraft.
288
289 This default implementation returns DERATE_NONE."""
290 return DERATE_NONE
291
292 def setBookedFlight(self, bookedFlight):
293 """Update the aircraft based on the booked flight data (e.g. tail number).
294
295 This default implementation does nothing."""
296
297 def getDerateLine(self, value):
298 """Get the log line for the given derate value.
299
300 It uses the the derate type and produces the standard message for
301 each. This children need not override it, although they can."""
302 dt = self.derateType
303
304 if dt==DERATE_BOEING:
305 return "Derate calculated by the pilot: %s %%" % \
306 ("-" if value is None else value,)
307 elif dt==DERATE_EPR:
308 return "EPR calculated by the pilot: %s" % \
309 ("-" if value is None else value,)
310 elif dt==DERATE_TUPOLEV:
311 return "Thrust setting calculated by the pilot: %s" % \
312 ("-" if value is None else
313 "nominal" if value==DERATE_TUPOLEV_NOMINAL else "takeoff",)
314 elif dt==DERATE_B462:
315 return "Derate setting: %s" % \
316 ("-" if value is None else "enabled" if value else "disabled",)
317 elif dt!=DERATE_NONE:
318 print("mlx.acft.getDerateLine: invalid derate type: " + dt)
319
320 return None
321
322 def getFlapsSpeedLimit(self, flaps):
323 """Get the speed limit for the given flaps setting."""
324 return self.flapSpeedLimits[flaps] if flaps in self.flapSpeedLimits \
325 else None
326
327 def modelChanged(self, timestamp, aircraftName, modelName):
328 """Called when the simulator's aircraft changes."""
329 self._name = aircraftName
330 self._modelName = modelName
331 if self._flight.stage is not None:
332 self._logNameAndModel(timestamp)
333
334 def handleState(self, aircraftState):
335 """Called when the state of the aircraft changes.
336
337 This is the function that the simulator calls directly with the new
338 state."""
339 try:
340 config = self._flight.config
341
342 self._smoothedIAS.add(config.realIASSmoothingLength, aircraftState.ias)
343 aircraftState.smoothedIAS = self._smoothedIAS.get()
344
345 self._smoothedVS.add(config.realVSSmoothingLength, aircraftState.vs)
346 aircraftState.smoothedVS = self._smoothedVS.get()
347
348 for checker in self._checkers:
349 try:
350 checker.check(self._flight, self, self._flight.logger,
351 self._aircraftState, aircraftState)
352 except:
353 print("Checker", checker, "failed", file=sys.stderr)
354 traceback.print_exc()
355
356 self._flight.handleState(self._aircraftState, aircraftState)
357
358 self._maxVS = max(self._maxVS, aircraftState.vs)
359 self._minVS = min(self._minVS, aircraftState.vs)
360 except:
361 print("Failed to handle the state", file=sys.stderr)
362 traceback.print_exc()
363 finally:
364 self._aircraftState = aircraftState
365
366 def setStage(self, aircraftState, newStage):
367 """Set the given stage as the new one and do whatever should be
368 done."""
369 if newStage==const.STAGE_BOARDING:
370 self._logNameAndModel(aircraftState.timestamp)
371
372 oldStage = self._flight.stage
373
374 if self._flight.setStage(aircraftState.timestamp, newStage):
375 if newStage==const.STAGE_PUSHANDTAXI:
376 self.logger.message(aircraftState.timestamp, "Block time start")
377 self._flight.logFuel(aircraftState)
378 self.logger.message(aircraftState.timestamp,
379 "ZFW: %.2f kg" % (aircraftState.zfw))
380 flight = self._flight
381 if flight.v1 is None or flight.vr is None or flight.v2 is None:
382 fs.sendMessage(const.MESSAGETYPE_HELP,
383 "Don't forget to set the takeoff V-speeds!",
384 5)
385 elif newStage==const.STAGE_TAKEOFF:
386 if oldStage == const.STAGE_RTO and self._flight.hasRTO:
387 rtoState = self._flight.rtoState
388 if (aircraftState.timestamp - rtoState.timestamp) < \
389 (self.brakeCoolTime * 60.0):
390 self.logger.fault("brakeCoolTime",
391 aircraftState.timestamp,
392 "Did not cool the brakes for at least %.f minutes after the RTO" % (self.brakeCoolTime,),
393 15.0)
394 self.logger.message(aircraftState.timestamp,
395 "Flight time start")
396 self.logger.message(aircraftState.timestamp,
397 "Takeoff weight: %.0f kg, MTOW: %.0f kg" % \
398 (aircraftState.grossWeight, self.mtow))
399 self._logQNH(aircraftState)
400 self.logger.message(aircraftState.timestamp,
401 "Wind %03.0f/%.0f" % \
402 (aircraftState.windDirection,
403 aircraftState.windSpeed))
404 self._logRadios(aircraftState)
405 self._logV1R2(aircraftState)
406 self._logDerate(aircraftState)
407 self._logTakeoffAntiIce(aircraftState)
408 elif newStage==const.STAGE_DESCENT or newStage==const.STAGE_LANDING:
409 self._logRadios(aircraftState)
410 if newStage==const.STAGE_LANDING:
411 self._logQNH(aircraftState)
412 elif newStage==const.STAGE_GOAROUND:
413 from .logger import Logger
414 self._flight.handleFault("goaround",
415 aircraftState.timestamp,
416 "Go-around detected, please, explain!",
417 Logger.NO_SCORE)
418 elif newStage==const.STAGE_TAXIAFTERLAND:
419 flight = self._flight
420 bookedFlight = flight.bookedFlight
421 config = flight.config
422 if config.onlineGateSystem and \
423 flight.loggedIn and \
424 not flight.entranceExam and \
425 bookedFlight.arrivalICAO=="LHBP" and \
426 config.isMessageTypeFS(const.MESSAGETYPE_GATE_SYSTEM):
427 self._flight.getFleet(callback = self._fleetRetrieved,
428 force = True)
429 self.logger.message(aircraftState.timestamp, "Flight time end")
430 self._flight.logFuel(aircraftState)
431 if self._minLandingFuel is not None and \
432 aircraftState.totalFuel<self._minLandingFuel:
433 self._flight.handleFault(self.__class__,
434 aircraftState.timestamp,
435 "The amount of the landing fuel is less than the minimum for this type: %ukgs (possible NO GO!)" %
436 (self._minLandingFuel,), 0)
437 self.logger.message(aircraftState.timestamp,
438 "Landing weight: %.0f kg, MLW: %.0f" % \
439 (aircraftState.grossWeight, self.mlw))
440 self.logger.message(aircraftState.timestamp,
441 "Vertical speed range: %.0f..%.0f feet/min" % \
442 (self._minVS, self._maxVS))
443 # elif newStage==const.STAGE_PARKING:
444 # self.logger.message(aircraftState.timestamp, "Block time end")
445 elif newStage==const.STAGE_END:
446 flightLength = self._flight.flightTimeEnd - self._flight.flightTimeStart
447 self.logger.message(aircraftState.timestamp, "Block time end")
448 self.logger.message(aircraftState.timestamp,
449 "Flight time: " +
450 util.getTimeIntervalString(flightLength))
451 self.logger.message(aircraftState.timestamp,
452 "Flown distance: %.2f NM" % \
453 (self._flight.flownDistance,))
454 blockLength = self._flight.blockTimeEnd - self._flight.blockTimeStart
455 self.logger.message(aircraftState.timestamp,
456 "Block time: " +
457 util.getTimeIntervalString(blockLength))
458
459 def prepareFlare(self):
460 """Called when it is detected that we will soon flare.
461
462 On the first call, it should start monitoring some parameters more
463 closely to determine flare time."""
464 self.flight.simulator.startFlare()
465
466 def flareStarted(self, windSpeed, windDirection, visibility,
467 flareStart, flareStartFS):
468 """Called when the flare has started."""
469 self.logger.message(self._aircraftState.timestamp, "The flare has begun")
470 self.logger.message(self._aircraftState.timestamp,
471 "Wind %03.0f/%.0f" % \
472 (windDirection, windSpeed))
473 self.logger.message(self._aircraftState.timestamp,
474 "Visibility: %.0f metres" % (visibility,))
475 self._logQNH(self._aircraftState)
476 self._logVRef()
477 self._logLandingAntiIce(self._aircraftState)
478 self.flight.flareStarted(flareStart, flareStartFS)
479 fs.sendMessage(const.MESSAGETYPE_INFORMATION, "Flare-time", 3)
480
481 def flareFinished(self, flareEnd, flareEndFS, tdRate, tdRateCalculatedByFS,
482 ias, pitch, bank, heading):
483 """Called when the flare has finished."""
484 (flareTimeFromFS, flareTime) = self.flight.flareFinished(flareEnd,
485 flareEndFS,
486 tdRate)
487 self.logger.message(self._aircraftState.timestamp,
488 "Flaretime: %.3f (from %s)" % \
489 (flareTime,
490 "the simulator" if flareTimeFromFS else "real time",))
491 self.logger.message(self._aircraftState.timestamp,
492 "Touchdown rate: %.0f feet/min" % (tdRate,))
493 self.logger.message(self._aircraftState.timestamp,
494 "Touchdown rate was calculated by the %s" % \
495 ("simulator" if tdRateCalculatedByFS else "logger",))
496 flight = self._flight
497 self.logger.message(self._aircraftState.timestamp,
498 "Touchdown speed: %.0f %s" % \
499 (flight.speedFromKnots(ias),
500 flight.getEnglishSpeedUnit()))
501 self.logger.message(self._aircraftState.timestamp,
502 "Touchdown pitch: %.1f degrees" % (pitch,))
503 self.logger.message(self._aircraftState.timestamp,
504 "Touchdown bank: %.1f degrees" % (bank,))
505 self.logger.message(self._aircraftState.timestamp,
506 "Touchdown heading: %03.0f degrees" % (heading,))
507 self.logger.message(self._aircraftState.timestamp,
508 "CG: %.1f%%" % \
509 (self._aircraftState.cog*100.0,))
510
511 if abs(pitch)>self.maxTouchDownPitch:
512 self._flight.handleNoGo("TDPitch", self._aircraftState.timestamp,
513 "Touchdown pitch higher than aircraft maximum (%.2f)" % \
514 (self.maxTouchDownPitch,),
515 "TD TAILSTRIKE NO GO")
516
517 def cancelFlare(self):
518 """Cancel flare, if it has started."""
519 self.flight.simulator.cancelFlare()
520
521 def checkFlightEnd(self, aircraftState):
522 """Check if the end of the flight has arrived.
523
524 This default implementation checks the N1 values, but for
525 piston-powered aircraft you need to check the RPMs."""
526 if aircraftState.n1 is not None:
527 for n1 in aircraftState.n1:
528 if n1 is not None and n1>=0.5: return False
529 return True
530
531 def updateV1R2(self):
532 """Update the V1, Vr and V2 values from the flight, if the these values
533 have already been logged."""
534 if self._v1r2LineIndex is not None:
535 self._logV1R2()
536
537 def updateDerate(self):
538 """Update the derate value from the flight, if the these values
539 have already been logged."""
540 if self._derateLineID is not None:
541 self._logDerate()
542
543 def updateTakeoffAntiIce(self):
544 """Update the take-off anti-ice setting."""
545 if self._takeoffAntiIceLineID is not None:
546 self._logTakeoffAntiIce()
547
548 def _appendLightsLoggers(self):
549 """Append the loggers needed for the lights.
550
551 This default implementation adds the loggers for the anti-collision
552 lights, the landing lights, the strobe lights and the NAV lights."""
553 self._checkers.append(checks.AnticollisionLightsLogger())
554 self._checkers.append(checks.LandingLightsLogger())
555 self._checkers.append(checks.StrobeLightsLogger())
556 self._checkers.append(checks.NavLightsLogger())
557
558 def _appendLightsCheckers(self):
559 """Append the checkers needed for the lights.
560
561 This default implementation adds the checkers for the anti-collision
562 lights, the landing lights, the strobe lights and the NAV lights."""
563 self._checkers.append(checks.AntiCollisionLightsChecker())
564 self._checkers.append(checks.LandingLightsChecker())
565 self._checkers.append(checks.NavLightsChecker())
566 self._checkers.append(checks.StrobeLightsChecker())
567
568 def _speedToLog(self, speed):
569 """Convert the given speed (being either None or expressed in the
570 flight's speed unit into a string."""
571 if speed is None:
572 return "-"
573 else:
574 return str(speed) + " " + self._flight.getEnglishSpeedUnit()
575
576 def _logV1R2(self, state = None):
577 """Log the V1, Vr and V2 value either newly, or by updating the
578 corresponding line."""
579 message = "Calc. TO speeds: V1: %s, VR: %s, V2: %s" % \
580 (self._speedToLog(self._flight.v1),
581 self._speedToLog(self._flight.vr),
582 self._speedToLog(self._flight.v2))
583
584 if self._v1r2LineIndex is None:
585 if state is None:
586 state = self._aircraftState
587 self._v1r2LineIndex = \
588 self.logger.message(state.timestamp, message)
589 else:
590 self.logger.updateLine(self._v1r2LineIndex, message)
591
592 def _logDerate(self, state = None):
593 """Log the derate values either newly or by updating the corresponding
594 line."""
595 dt = self.derateType
596 if dt==DERATE_NONE:
597 return
598
599 message = self.getDerateLine(self._flight.derate)
600 if message is not None:
601 if self._derateLineID is None:
602 if state is None:
603 state = self._aircraftState
604 self._derateLineID = \
605 self.logger.message(state.timestamp, message)
606 else:
607 self.logger.updateLine(self._derateLineID, message)
608
609 def _logTakeoffAntiIce(self, state = None):
610 """Log the take-off anti-ice setting either newly or by updating the
611 corresponding line."""
612 antiIceOn = self._flight.takeoffAntiIceOn
613 if state is not None:
614 antiIceOn = antiIceOn or state.antiIceOn is True
615 self._flight.takeoffAntiIceOn = antiIceOn
616
617 message = "Anti-ice was turned %s" % \
618 ("ON" if antiIceOn else "OFF")
619
620 if self._takeoffAntiIceLineID is None:
621 if state is None:
622 state = self._aircraftState
623 self._takeoffAntiIceLineID = \
624 self.logger.message(state.timestamp, message)
625 else:
626 self.logger.updateLine(self._takeoffAntiIceLineID, message)
627
628 def updateVRef(self):
629 """Update the Vref value from the flight, if the Vref value has already
630 been logged."""
631 if self._vrefLineIndex is not None:
632 self._logVRef()
633
634 def _logVRef(self):
635 """Log the Vref value either newly, or by updating the corresponding
636 line."""
637 message = "VRef speed calculated by the pilot: %s" % \
638 (self._speedToLog(self._flight.vref),)
639 if self._vrefLineIndex is None:
640 self._vrefLineIndex = \
641 self.logger.message(self._aircraftState.timestamp, message)
642 else:
643 self.logger.updateLine(self._vrefLineIndex, message)
644
645 def updateLandingAntiIce(self):
646 """Update the landing anti-ice setting."""
647 if self._landingAntiIceLineID is not None:
648 self._logLandingAntiIce()
649
650 def _logLandingAntiIce(self, state = None):
651 """Log the landing anti-ice setting either newly or by updating the
652 corresponding line."""
653 antiIceOn = self._flight.landingAntiIceOn
654 if state is not None:
655 antiIceOn = antiIceOn or state.antiIceOn is True
656 self._flight.landingAntiIceOn = antiIceOn
657
658 message = "Anti-ice was turned %s" % \
659 ("ON" if antiIceOn else "OFF")
660
661 if self._landingAntiIceLineID is None:
662 if state is None:
663 state = self._aircraftState
664 self._landingAntiIceLineID = \
665 self.logger.message(state.timestamp, message)
666 else:
667 self.logger.updateLine(self._landingAntiIceLineID, message)
668
669 def _fleetRetrieved(self, fleet):
670 """Callback for the fleet retrieval result."""
671 if fleet is not None:
672 gateList = ""
673 occupiedGateNumbers = fleet.getOccupiedGateNumbers()
674 for gate in gates.lhbpGates.gates:
675 if gate.isAvailable(gates.lhbpGates, occupiedGateNumbers):
676 if gateList: gateList += ", "
677 gateList += gate.number
678 fs.sendMessage(const.MESSAGETYPE_GATE_SYSTEM,
679 "Free gates: " + gateList, 20)
680
681
682 def _logRadios(self, aircraftState):
683 """Log the radios from the given aircraft state."""
684 flight = self._flight
685 logger = flight.logger
686
687 self._ilsLogger.forceLog(flight, logger, aircraftState)
688 self._nav1Logger.forceLog(flight, logger, aircraftState)
689 self._nav2Logger.forceLog(flight, logger, aircraftState)
690 self._adf1Logger.forceLog(flight, logger, aircraftState)
691 self._adf2Logger.forceLog(flight, logger, aircraftState)
692
693 def _logQNH(self, aircraftState):
694 """Log the current QNH along with the altimeter setting."""
695 self.logger.message(aircraftState.timestamp,
696 "QNH: %.2f hPa, altimeter: %.2f hPa" % \
697 (aircraftState.qnh, aircraftState.altimeter))
698
699 def _logNameAndModel(self, timestamp):
700 """Log the aircraft's name and model with taking the timestamp from the
701 given state."""
702 self._flight.logger.message(timestamp,
703 "Aircraft: name='%s', model='%s'" % \
704 (self._name, self._modelName))
705
706#---------------------------------------------------------------------------------------
707
708class Boeing737(Aircraft):
709 """Base class for the various aircraft in the Boeing 737 family.
710
711 The aircraft type-specific values in the aircraft state have the following
712 structure:
713 - fuel: left, centre, right
714 - n1: left, right
715 - reverser: left, right"""
716 def __init__(self, flight, minLandingFuel = 2500,
717 recommendedLandingFuel = 3500):
718 super(Boeing737, self).__init__(flight,
719 minLandingFuel = minLandingFuel,
720 recommendedLandingFuel =
721 recommendedLandingFuel)
722
723 self.gearSpeedLimit = 270
724 self.flapSpeedLimits = { 1 : 260,
725 2 : 260,
726 5 : 250,
727 10 : 210,
728 15 : 200,
729 25 : 190,
730 30 : 175,
731 40 : 162 }
732
733 @property
734 def derateType(self):
735 """Get the derate type for this type."""
736 return DERATE_BOEING
737
738#---------------------------------------------------------------------------------------
739
740class B736(Boeing737):
741 """Boeing 737-600 aircraft."""
742 dow = 38307
743
744 def __init__(self, flight):
745 super(B736, self).__init__(flight)
746 self.mtow = 58328
747 self.mlw = 54657
748 self.mzfw = 51482
749 self.maxTakeOffPitch = 16.2
750 self.maxTouchDownPitch = 14.7
751 self.simBriefData = SimBriefData(["250/280/78"],
752 ["CI", "M75", "M78", "M79", "M80", "LRC"],
753 ["78/280/250"],
754 cruiseParameters = {0: (False, "civalue")})
755
756#---------------------------------------------------------------------------------------
757
758class B737(Boeing737):
759 """Boeing 737-700 aircraft."""
760 dow = 39250
761
762 def __init__(self, flight):
763 super(B737, self).__init__(flight)
764 self.mtow = 61410
765 self.mlw = 58059
766 self.mzfw = 54657
767 self.maxTakeOffPitch = 14.7
768 self.maxTouchDownPitch = 13.2
769 self.simBriefData = SimBriefData(["250/280/78"],
770 ["CI", "M75", "M78", "M79", "M80", "LRC"],
771 ["78/280/250", "78/250/250"],
772 cruiseParameters = {0: (False, "civalue")})
773
774#---------------------------------------------------------------------------------------
775
776class B738(Boeing737):
777 """Boeing 737-800 aircraft."""
778 dow = 42690
779
780 def __init__(self, flight):
781 super(B738, self).__init__(flight)
782 self.mtow = 71791
783 self.mlw = 65317
784 self.mzfw = 61688
785 self.maxTakeOffPitch = 11
786 self.maxTouchDownPitch = 9.5
787 self.simBriefData = SimBriefData(["250/280/78"],
788 ["CI", "M76", "M78", "M79", "M80", "LRC"],
789 ["78/280/250", "78/250/250"],
790 cruiseParameters = {0: (False, "civalue")})
791
792#---------------------------------------------------------------------------------------
793
794class B738Charter(B738):
795 """Boeing 737-800 aircraft used for charters."""
796 def __init__(self, flight):
797 super(B738Charter, self).__init__(flight)
798 self.mtow = 77791
799 self.simBriefData = SimBriefData(["AUTO"],
800 ["280/M74"],
801 ["AUTO"])
802
803#---------------------------------------------------------------------------------------
804
805class Boeing737CL(Boeing737):
806 """Base class for the various aircraft in the Boeing 737 Classic family."""
807 def __init__(self, flight):
808 super(Boeing737CL, self).__init__(flight, minLandingFuel = 3500,
809 recommendedLandingFuel = None)
810
811#---------------------------------------------------------------------------------------
812
813class B732(Boeing737CL):
814 """Boeing 737-200 aircraft."""
815 dow = 27646
816
817 def __init__(self, flight):
818 super(B732, self).__init__(flight)
819 self.mtow = 52390
820 self.mlw = 46720
821 self.mzfw = 43091
822 self.maxTakeOffPitch = 15.5
823 self.maxTouchDownPitch = 15.5
824 self.simBriefData = SimBriefData(["250/280/70"],
825 ["LRC", "M72", "M73", "M74"],
826 ["74/320/250"])
827
828#---------------------------------------------------------------------------------------
829
830class B733(Boeing737CL):
831 """Boeing 737-300 aircraft."""
832 dow = 32900
833
834 def __init__(self, flight):
835 super(B733, self).__init__(flight)
836 self.mtow = 56472
837 self.mlw = 51710
838 self.mzfw = 47625
839 self.maxTakeOffPitch = 13.4
840 self.maxTouchDownPitch = 12.0
841 self.simBriefData = SimBriefData(["250/280/74"],
842 ["CI", "M74", "M76", "M78", "LRC"],
843 ["74/280/250"],
844 cruiseParameters = {0: (False, "civalue")})
845
846#---------------------------------------------------------------------------------------
847
848class B734(Boeing737CL):
849 """Boeing 737-400 aircraft."""
850 def __init__(self, flight):
851 super(B734, self).__init__(flight)
852 self.dow = 35100
853 self.mtow = 62822
854 self.mlw = 54885
855 self.mzfw = 51256
856 self.maxTakeOffPitch = 11.4
857 self.maxTouchDownPitch = 10
858 self.simBriefData = SimBriefData(["250/280/74"],
859 ["CI", "M74", "M76", "M78", "LRC"],
860 ["74/280/250"],
861 cruiseParameters = {0: (False, "civalue")})
862
863#---------------------------------------------------------------------------------------
864
865class B735(Boeing737CL):
866 """Boeing 737-500 aircraft."""
867 dow = 31900
868
869 def __init__(self, flight):
870 super(B735, self).__init__(flight)
871 self.mtow = 62823
872 self.mlw = 49895
873 self.mzfw = 46720
874 self.maxTakeOffPitch = 14.7
875 self.maxTouchDownPitch = 13.2
876 self.simBriefData = SimBriefData(["250/280/74"],
877 ["CI", "M74", "M76", "M78", "LRC"],
878 ["74/280/250"],
879 cruiseParameters = {0: (False, "civalue")})
880
881#---------------------------------------------------------------------------------------
882
883class DH8D(Aircraft):
884 """Bombardier Dash-8 Q400 aircraft.
885
886 The aircraft type-specific values in the aircraft state have the following
887 structure:
888 - fuel: left, right
889 - n1: left, right
890 - reverser: left, right."""
891 dow = 18508
892
893 def __init__(self, flight):
894 super(DH8D, self).__init__(flight, minLandingFuel = 2000)
895 self.mtow = 29574
896 self.mlw = 28123
897 self.mzfw = 26308
898 self.gearSpeedLimit = 215
899 self.flapSpeedLimits = { 5 : 200,
900 10 : 181,
901 15 : 172,
902 35 : 158 }
903 self.maxTakeOffPitch = 8.0
904 self.maxTouchDownPitch = 7.0
905 self.simBriefData = SimBriefData(["I-900", "II-900", "III-900",
906 "I-850", "II-850", "III-850"],
907 ["MCR", "ISC", "LRC", "HSC"],
908 ["I-850", "II-850", "III-850"])
909
910#---------------------------------------------------------------------------------------
911
912class Boeing767(Aircraft):
913 """Base class for the various aircraft in the Boeing 767 family.
914
915 The aircraft type-specific values in the aircraft state have the following
916 structure:
917 - fuel: left, centre, right
918 - n1: left, right
919 - reverser: left, right"""
920
921 def __init__(self, flight, minLandingFuel = 7000):
922 super(Boeing767, self).__init__(flight, minLandingFuel = minLandingFuel)
923 self.gearSpeedLimit = 270
924 self.flapSpeedLimits = { 1 : 255,
925 5 : 235,
926 10 : 215,
927 20 : 215,
928 25 : 185,
929 30 : 175 }
930
931 @property
932 def derateType(self):
933 """Get the derate type for this type."""
934 return DERATE_BOEING
935
936#---------------------------------------------------------------------------------------
937
938class B762(Boeing767):
939 """Boeing 767-200 aircraft."""
940 dow = 84507
941
942 def __init__(self, flight):
943 super(B762, self).__init__(flight)
944 self.mtow = 159210
945 self.mlw = 126098
946 self.mzfw = 114758
947 self.maxTakeOffPitch = 13.1
948 self.maxTouchDownPitch = 11.6
949 self.simBriefData = SimBriefData(["250/290/78"],
950 ["CI", "M76", "M78", "M80", "M82",
951 "M84", "M85", "LRC"],
952 ["78/290/250"],
953 cruiseParameters = {0: (False, "civalue")})
954
955#---------------------------------------------------------------------------------------
956
957class B763(Boeing767):
958 """Boeing 767-300 aircraft."""
959 dow = 91311
960
961 def __init__(self, flight):
962 super(B763, self).__init__(flight)
963 self.mtow = 181436
964 self.mlw = 137892
965 self.mzfw = 114758
966 self.maxTakeOffPitch = 9.6
967 self.maxTouchDownPitch = 8.1
968 self.simBriefData = SimBriefData(["250/290/78"],
969 ["CI", "M76", "M78", "M80", "M82", "M84", "LRC"],
970 ["78/290/250"],
971 cruiseParameters = {0: (False, "civalue")})
972
973 def setBookedFlight(self, bookedFlight):
974 """Update the aircraft based on the booked flight data (e.g. tail number)."""
975 if bookedFlight.tailNumber=="HA-LHD":
976 self.mtow = 159210
977 self.mlw = 126098
978
979#---------------------------------------------------------------------------------------
980
981class CRJ2(Aircraft):
982 """Bombardier CRJ-200 aircraft.
983
984 The aircraft type-specific values in the aircraft state have the following
985 structure:
986 - fuel: left, centre, right
987 - n1: left, right
988 - reverser: left, right."""
989 dow = 14549
990
991 def __init__(self, flight):
992 super(CRJ2, self).__init__(flight, minLandingFuel = 1000)
993 self.mtow = 22995
994 self.mlw = 21319
995 self.mzfw = 19958
996 self.gearSpeedLimit = 240
997 self.flapSpeedLimits = { 8 : 260,
998 20 : 220,
999 30 : 190,
1000 45 : 175 }
1001 self.maxTakeOffPitch = 18.0
1002 self.maxTouchDownPitch = 18.0
1003 self.simBriefData = SimBriefData(["250/70", "290/74"],
1004 ["CI", "LRC", "M70", "M72", "M74", "M77", "M80"],
1005 ["74/290/250", "77/320/250"],
1006 cruiseParameters = {0: (False, "civalue")})
1007
1008#---------------------------------------------------------------------------------------
1009
1010class F70(Aircraft):
1011 """Fokker 70 aircraft.
1012
1013 The aircraft type-specific values in the aircraft state have the following
1014 structure:
1015 - fuel: left, centre, right
1016 - n1: left, right
1017 - reverser: left, right."""
1018 dow = 24283
1019
1020 def __init__(self, flight):
1021 super(F70, self).__init__(flight, minLandingFuel = 1900)
1022 self.mtow = 38100 # FIXME: differentiate by registration number,
1023 # MTOW of HA-LMF: 41955
1024 self.mlw = 36740
1025 self.mzfw = 32655
1026 self.gearSpeedLimit = 200
1027 self.flapSpeedLimits = { 8 : 250,
1028 15 : 220,
1029 25 : 220,
1030 42 : 180 }
1031 self.reverseMinSpeed = 50
1032 self.maxTakeOffPitch = 16.0
1033 self.maxTouchDownPitch = 16.0
1034 self.simBriefData = SimBriefData(["250/280/70"],
1035 ["M70", "LRC"],
1036 ["70/280/250"])
1037
1038 @property
1039 def derateType(self):
1040 """Get the derate type for this type."""
1041 return DERATE_EPR
1042
1043#---------------------------------------------------------------------------------------
1044
1045class DC3(Aircraft):
1046 """Lisunov Li-2 (DC-3) aircraft.
1047
1048 The aircraft type-specific values in the aircraft state have the following
1049 structure:
1050 - fuel: left aux, left, right, right aux
1051 - rpm: left, right
1052 - reverser: left, right."""
1053 dow = 8627
1054
1055 def __init__(self, flight):
1056 super(DC3, self).__init__(flight)
1057 self.mtow = 11884
1058 self.mlw = 11793
1059 self.mzfw = 11780
1060 self.gearSpeedLimit = 148
1061 self.flapSpeedLimits = { 15 : 135,
1062 30 : 99,
1063 45 : 97 }
1064
1065 def _checkFlightEnd(self, aircraftState):
1066 """Check if the end of the flight has arrived.
1067
1068 This implementation checks the RPM values to be 0."""
1069 for rpm in aircraftState.rpm:
1070 if rpm>0: return False
1071 return True
1072
1073#---------------------------------------------------------------------------------------
1074
1075class T134(Aircraft):
1076 """Tupolev Tu-134 aircraft.
1077
1078 The aircraft type-specific values in the aircraft state have the following
1079 structure:
1080 - fuel: left tip, left aux, centre, right aux, right tip, external 1,
1081 external 2
1082 - n1: left, right
1083 - reverser: left, right."""
1084 dow = 29500
1085
1086 def __init__(self, flight):
1087 super(T134, self).__init__(flight, minLandingFuel = 3000)
1088 self.mtow = 49000
1089 self.mlw = 43000
1090 self.mzfw = 38500
1091 self.gearSpeedLimit = 216
1092 self.flapSpeedLimits = { 10 : 450,
1093 20 : 400,
1094 30 : 300 }
1095 self.reverseMinSpeed = 50
1096
1097 self.hasStrobeLight = False
1098
1099 self.maxTakeOffPitch = 16.0
1100 self.maxTouchDownPitch = 16.0
1101
1102 @property
1103 def derateType(self):
1104 """Get the derate type for this type."""
1105 return DERATE_TUPOLEV
1106
1107 @property
1108 def speedInKnots(self):
1109 """Indicate if the speed is in knots."""
1110 return False
1111
1112 @property
1113 def aglInFeet(self):
1114 """Indicate if AGL altituedes are in feet."""
1115 return False
1116
1117 def _appendLightsLoggers(self):
1118 """Append the loggers needed for the lights."""
1119 self._checkers.append(checks.AnticollisionLightsLogger())
1120 self._checkers.append(checks.LandingLightsLogger())
1121 self._checkers.append(checks.NavLightsLogger())
1122
1123 def _appendLightsCheckers(self):
1124 """Append the checkers needed for the lights."""
1125 self._checkers.append(checks.TupolevAntiCollisionLightsChecker())
1126 self._checkers.append(checks.TupolevLandingLightsChecker())
1127 self._checkers.append(checks.LandingLightsChecker())
1128 self._checkers.append(checks.NavLightsChecker())
1129
1130#---------------------------------------------------------------------------------------
1131
1132class T154(Aircraft):
1133 """Tupolev Tu-154 aircraft.
1134
1135 The aircraft type-specific values in the aircraft state have the following
1136 structure:
1137 - fuel: left aux, left, centre, centre 2, right, right aux
1138 - n1: left, centre, right
1139 - reverser: left, right"""
1140 dow = 53259
1141
1142 def __init__(self, flight):
1143 super(T154, self).__init__(flight, minLandingFuel = 5000)
1144 self.mtow = 98000
1145 self.mlw = 78000
1146 self.mzfw = 72000
1147 self.gearSpeedLimit = 216
1148 self.flapSpeedLimits = { 15 : 227,
1149 28 : 194,
1150 45 : 162 }
1151 self.reverseMinSpeed = 50
1152
1153 self.hasStrobeLight = False
1154
1155 self.maxTakeOffPitch = 16.0
1156 self.maxTouchDownPitch = 16.0
1157 self.simBriefData = SimBriefData(["AUTO"],
1158 ["AUTO"],
1159 ["AUTO"])
1160
1161 @property
1162 def speedInKnots(self):
1163 """Indicate if the speed is in knots."""
1164 return False
1165
1166 @property
1167 def aglInFeet(self):
1168 """Indicate if AGL altituedes are in feet."""
1169 return False
1170
1171 @property
1172 def derateType(self):
1173 """Get the derate type for this type."""
1174 return DERATE_TUPOLEV
1175
1176 def setBookedFlight(self, bookedFlight):
1177 """Update the aircraft based on the booked flight data (e.g. tail number)."""
1178 if bookedFlight.tailNumber in ["HA-LCM", "HA-LCN", "HA-LCO", "HA-LCP",
1179 "HA-LCR", "HA-LCU", "HA-LCV"]:
1180 self.mtow = 100000
1181 self.mlw = 80000
1182 elif bookedFlight.tailNumber=="HA-LCX":
1183 self.mtow = 100000
1184 self.mlw = 80000
1185 self.mzfw = 74000
1186
1187 self.flapSpeedLimits = { 15 : 227,
1188 28 : 194,
1189 36 : 178,
1190 45 : 162 }
1191
1192 def _appendLightsLoggers(self):
1193 """Append the loggers needed for the lights."""
1194 self._checkers.append(checks.AnticollisionLightsLogger())
1195 self._checkers.append(checks.LandingLightsLogger())
1196 self._checkers.append(checks.NavLightsLogger())
1197
1198 def _appendLightsCheckers(self):
1199 """Append the checkers needed for the lights."""
1200 self._checkers.append(checks.AntiCollisionLightsChecker())
1201 self._checkers.append(checks.TupolevLandingLightsChecker())
1202 self._checkers.append(checks.LandingLightsChecker())
1203 self._checkers.append(checks.NavLightsChecker())
1204
1205#---------------------------------------------------------------------------------------
1206
1207class YK40(Aircraft):
1208 """Yakovlev Yak-40 aircraft.
1209
1210 The aircraft type-specific values in the aircraft state have the following
1211 structure:
1212 - fuel: left, right
1213 - n1: left, right
1214 - reverser: left, right"""
1215 dow = 9400
1216
1217 def __init__(self, flight):
1218 super(YK40, self).__init__(flight)
1219 self.mtow = 17200
1220 self.mlw = 16800
1221 self.mzfw = 12100
1222 self.gearSpeedLimit = 165
1223 self.flapSpeedLimits = { 20 : 165,
1224 35 : 135 }
1225
1226 self.hasStrobeLight = False
1227
1228 @property
1229 def speedInKnots(self):
1230 """Indicate if the speed is in knots."""
1231 return False
1232
1233 @property
1234 def aglInFeet(self):
1235 """Indicate if AGL altituedes are in feet."""
1236 return False
1237
1238 @property
1239 def derateType(self):
1240 """Get the derate type for this type."""
1241 return DERATE_TUPOLEV
1242
1243 def _appendLightsLoggers(self):
1244 """Append the loggers needed for the lights."""
1245 self._checkers.append(checks.AnticollisionLightsLogger())
1246 self._checkers.append(checks.LandingLightsLogger())
1247 self._checkers.append(checks.NavLightsLogger())
1248
1249 def _appendLightsCheckers(self):
1250 """Append the checkers needed for the lights."""
1251 self._checkers.append(checks.AntiCollisionLightsChecker())
1252 self._checkers.append(checks.LandingLightsChecker())
1253 self._checkers.append(checks.NavLightsChecker())
1254
1255#---------------------------------------------------------------------------------------
1256
1257class B462(Aircraft):
1258 """British Aerospace BAe-146 aircraft.
1259
1260 The aircraft type-specific values in the aircraft state have the following
1261 structure:
1262 - fuel: left, centre, right
1263 - n1: left outer, left inner, right inner, right outer
1264 - reverser: empty (the plane has no reversers)"""
1265 dow = 25706
1266
1267 def __init__(self, flight):
1268 super(B462, self).__init__(flight)
1269 self.mtow = 43998
1270 self.mlw = 38599
1271 self.mzfw = 33792
1272 self.gearSpeedLimit = 210
1273 self.flapSpeedLimits = { 18 : 217,
1274 24 : 180,
1275 30 : 170,
1276 33 : 150 }
1277 self.simBriefData = SimBriefData(["HighSpeed", "LongRange"],
1278 ["M70", "MCR", "LRC"],
1279 ["HighSpeed", "LongRange"])
1280 @property
1281 def derateType(self):
1282 """Get the derate type for this type."""
1283 return DERATE_B462
1284
1285#---------------------------------------------------------------------------------------
1286
1287mostFuelTanks = [const.FUELTANK_LEFT_TIP, const.FUELTANK_EXTERNAL1,
1288 const.FUELTANK_LEFT_AUX,
1289 const.FUELTANK_CENTRE,
1290 const.FUELTANK_RIGHT_AUX,
1291 const.FUELTANK_EXTERNAL2, const.FUELTANK_RIGHT_TIP]
1292
1293#---------------------------------------------------------------------------------------
1294
1295_classes = { const.AIRCRAFT_B736 : B736,
1296 const.AIRCRAFT_B737 : B737,
1297 const.AIRCRAFT_B738 : B738,
1298 const.AIRCRAFT_B738C : B738Charter,
1299 const.AIRCRAFT_B732 : B732,
1300 const.AIRCRAFT_B733 : B733,
1301 const.AIRCRAFT_B734 : B734,
1302 const.AIRCRAFT_B735 : B735,
1303 const.AIRCRAFT_DH8D : DH8D,
1304 const.AIRCRAFT_B762 : B762,
1305 const.AIRCRAFT_B763 : B763,
1306 const.AIRCRAFT_CRJ2 : CRJ2,
1307 const.AIRCRAFT_F70 : F70,
1308 const.AIRCRAFT_DC3 : DC3,
1309 const.AIRCRAFT_T134 : T134,
1310 const.AIRCRAFT_T154 : T154,
1311 const.AIRCRAFT_YK40 : YK40,
1312 const.AIRCRAFT_B462 : B462 }
1313
1314#---------------------------------------------------------------------------------------
1315
1316def getClass(aircraftType):
1317 """Get the class representing the given aircraft types"""
1318 return _classes[aircraftType]
1319
1320#---------------------------------------------------------------------------------------
1321
1322if __name__ == "__main__":
1323 value = SmoothedValue()
1324
1325 print("Adding 1, 12.0")
1326 value.add(1, 12.0)
1327 print(value.get())
1328
1329 print("Adding 1, 15.0")
1330 value.add(1, 15.0)
1331 print(value.get())
1332
1333 print("Adding 2, 18.0")
1334 value.add(2, 18.0)
1335 print(value.get())
1336
1337 print("Adding 2, 20.0")
1338 value.add(2, 20.0)
1339 print(value.get())
1340
1341 print("Adding 5, 22.0")
1342 value.add(5, 22.0)
1343 print(value.get())
1344
1345 print("Adding 5, 25.0")
1346 value.add(5, 25.0)
1347 print(value.get())
1348
1349 print("Adding 5, 29.0")
1350 value.add(5, 29.0)
1351 print(value.get())
1352
1353 print("Adding 5, 21.0")
1354 value.add(5, 21.0)
1355 print(value.get())
1356
1357 print("Adding 5, 26.0")
1358 value.add(5, 26.0)
1359 print(value.get())
1360
1361 print("Adding 2, 30.0")
1362 value.add(2, 30.0)
1363 print(value.get())
1364
1365 print("Adding 2, 55.0")
1366 value.add(2, 55.0)
1367 print(value.get())
1368
1369#---------------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.