source: src/mlx/config.py@ 1079:bfa2f7d95934

python3
Last change on this file since 1079:bfa2f7d95934 was 1067:9ce88d0b881d, checked in by István Váradi <ivaradi@…>, 17 months ago

Support for connecting to X-Plane remotely

File size: 40.1 KB
RevLine 
[113]1# -*- encoding: utf-8 -*-
[36]2
[919]3from . import const
4from .util import secondaryInstallation, utf2unicode
[132]5
[40]6import os
[43]7import sys
[373]8import traceback
[919]9import configparser
[40]10
[298]11## @package mlx.config
[373]12#
[298]13# The handling of the configuration.
14#
15# The \ref Config class contains the main configuration and is capable of
16# loading and saving the configuration. It contains getters and setters for the
17# configuration options.
18#
19# Some parts of the configuration are not simple data items, like strings or
20# booleans, but more complicated data. These have their own class, like \ref
21# ApproachCallouts or \ref Checklist.
22
[40]23#-------------------------------------------------------------------------------
24
25configPath = os.path.join(os.path.expanduser("~"),
[373]26 "mlx.config" if os.name=="nt" else ".mlxrc") + \
27 ("-secondary" if secondaryInstallation else "")
[40]28
29#-------------------------------------------------------------------------------
30
[124]31if os.name=="nt":
32 _languageMap = { "en_GB" : "eng",
33 "hu_HU" : "hun" }
34
35#-------------------------------------------------------------------------------
36
[166]37class Hotkey(object):
38 """A hotkey."""
39 def __init__(self, ctrl = False, shift = False, key = "0"):
40 """Construct the hotkey."""
41 self.ctrl = ctrl
42 self.shift = shift
43 self.key = key
44
45 def set(self, s):
46 """Set the hotkey from the given string."""
47 self.ctrl = "C" in s[:-1]
48 self.shift = "S" in s[:-1]
49 self.key = s[-1]
50
51 def __eq__(self, other):
52 """Check if the given hotkey is equal to the other one."""
53 return self.ctrl == other.ctrl and self.shift == other.shift and \
54 self.key == other.key
55
[264]56 def __ne__(self, other):
57 """Check if the given hotkey is not equal to the other one."""
58 return not self==other
[373]59
[166]60 def __str__(self):
61 """Construct the hotkey to a string."""
62 s = ""
63 if self.ctrl: s += "C"
64 if self.shift: s += "S"
65 s += self.key
66 return s
67
68#-------------------------------------------------------------------------------
69
[175]70class Checklist(object):
71 """A checklist for a certain aircraft type."""
72 # The name of the section of the checklists
73 SECTION="checklists"
[373]74
[175]75 @staticmethod
76 def fromConfig(config, aircraftType):
77 """Create a checklist for the given aircraft type from the given
78 config."""
79 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
80 fileList = []
81 while True:
82 option = baseName + str(len(fileList))
83 if config.has_option(Checklist.SECTION, option):
84 fileList.append(config.get(Checklist.SECTION, option))
85 else:
86 break
87
88 return Checklist(fileList)
89
90 def __init__(self, fileList = None):
91 """Construct the check list with the given file list."""
92 self._fileList = [] if fileList is None else fileList[:]
93
94 def clone(self):
95 """Clone the checklist."""
96 return Checklist(self._fileList)
97
98 def toConfig(self, config, aircraftType):
99 """Add this checklist to the given config."""
100 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
101 for index in range(0, len(self._fileList)):
102 option = baseName + str(index)
103 config.set(Checklist.SECTION, option,
104 self._fileList[index])
105
106 def __eq__(self, other):
107 """Determine if the checklist is equal to the given other one."""
108 return self._fileList == other._fileList
109
[264]110 def __ne__(self, other):
111 """Determine if the checklist is not equal to the given other one."""
112 return not self==other
[373]113
[175]114 def __len__(self):
115 """Get the length of the file list."""
116 return len(self._fileList)
117
118 def __getitem__(self, index):
119 """Get the file with the given index."""
120 return self._fileList[index]
121
122 def __iter__(self):
123 """Iterate over the files."""
124 return iter(self._fileList)
125
126#-------------------------------------------------------------------------------
127
[264]128class ApproachCallouts(object):
129 """The approach callouts for a certain aircraft type."""
130 # The name of the section of the approach callouts
131 SECTION="callouts"
[373]132
[264]133 @staticmethod
134 def fromConfig(config, aircraftType):
135 """Create a checklist for the given aircraft type from the given
136 config."""
137 baseName = "callouts." + const.icaoCodes[aircraftType] + "."
138 mapping = {}
139 while True:
140 option = baseName + str(len(mapping))
141 if config.has_option(ApproachCallouts.SECTION, option):
142 value = config.get(ApproachCallouts.SECTION, option)
143 (altitude, path) = value.split(",")
144 altitude = int(altitude.strip())
145 path = path.strip()
146 mapping[altitude] = path
147 else:
148 break
149
[373]150 return ApproachCallouts(mapping)
[264]151
152 def __init__(self, mapping = None):
153 """Construct the check list with the given mapping of altitudes to
154 files."""
155 self._mapping = {} if mapping is None else mapping.copy()
156
157 def clone(self):
158 """Clone the callout information."""
159 return ApproachCallouts(self._mapping)
160
161 def toConfig(self, config, aircraftType):
162 """Add this checklist to the given config."""
163 baseName = "callouts." + const.icaoCodes[aircraftType] + "."
164 index = 0
[919]165 for (altitude, path) in self._mapping.items():
[264]166 option = baseName + str(index)
167 config.set(ApproachCallouts.SECTION, option,
168 "%d, %s" % (altitude, path))
169 index += 1
170
[273]171 def getAltitudes(self, descending = True):
172 """Get the altitudes in decreasing order by default."""
[919]173 altitudes = list(self._mapping.keys())
[273]174 altitudes.sort(reverse = descending)
175 return altitudes
176
[919]177 def __bool__(self):
[273]178 """Return if there is anything in the mapping."""
179 return not not self._mapping
180
[264]181 def __eq__(self, other):
182 """Determine if the approach callout mapping is equal to the given
183 other one."""
184 return self._mapping == other._mapping
185
186 def __ne__(self, other):
187 """Determine if the approach callout mapping is not equal to the given
188 other one."""
189 return not self==other
190
191 def __len__(self):
192 """Get the number of elements in the mapping."""
193 return len(self._mapping)
194
195 def __getitem__(self, altitude):
[273]196 """Get the file that is associated with the given altitude.
[264]197
198 If no such file found, return None."""
[273]199 return self._mapping[altitude] if altitude in self._mapping else None
[264]200
201 def __iter__(self):
202 """Iterate over the pairs of altitudes and paths in decreasing order of
203 the altitude."""
[273]204 altitudes = self.getAltitudes()
[264]205
206 for altitude in altitudes:
207 yield (altitude, self._mapping[altitude])
208
209#-------------------------------------------------------------------------------
210
[36]211class Config(object):
212 """Our configuration."""
[1040]213 DEFAULT_UPDATE_URL = "https://mlx.varadiistvan.hu/update/new"
[132]214
215 _messageTypesSection = "messageTypes"
[373]216
[36]217 def __init__(self):
218 """Construct the configuration with default values."""
[40]219
[42]220 self._pilotID = ""
221 self._password = ""
[45]222 self._rememberPassword = False
[42]223
[132]224 self._language = ""
[147]225 self._hideMinimizedWindow = True
[249]226 self._quitOnClose = False
[373]227 self._onlineGateSystem = not secondaryInstallation
228 self._onlineACARS = not secondaryInstallation
[132]229 self._flareTimeFromFS = False
[148]230 self._syncFSTime = False
[183]231 self._usingFS2Crew = False
[197]232 self._iasSmoothingLength = -2
233 self._vsSmoothingLength = -2
[149]234
[684]235 self._useSimBrief = False
[689]236 self._simBriefUserName = ""
237 self._simBriefPassword = ""
238 self._rememberSimBriefPassword = False
[684]239
[149]240 self._pirepDirectory = None
[392]241 self._pirepAutoSave = False
[166]242
[503]243 self._defaultMSFS = os.name=="nt"
244
[373]245 self._enableSounds = not secondaryInstallation
[166]246
247 self._pilotControlsSounds = True
248 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
249
[264]250 self._enableApproachCallouts = False
[166]251 self._speedbrakeAtTD = True
252
253 self._enableChecklists = False
254 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
[373]255
256 self._autoUpdate = True
[40]257 self._updateURL = Config.DEFAULT_UPDATE_URL
[1040]258 #if secondaryInstallation:
259 # self._updateURL += "/exp"
[1011]260 self._updateURLUpdated = True
[132]261
[1067]262 self._xplaneRemote = False
263 self._xplaneAddress = ""
264
[132]265 self._messageTypeLevels = {}
[175]266
267 self._checklists = {}
[264]268 self._approachCallouts = {}
[175]269 for aircraftType in const.aircraftTypes:
270 self._checklists[aircraftType] = Checklist()
[264]271 self._approachCallouts[aircraftType] = ApproachCallouts()
[373]272
[123]273 self._modified = False
[107]274
[40]275 @property
[42]276 def pilotID(self):
277 """Get the pilot ID."""
278 return self._pilotID
279
280 @pilotID.setter
281 def pilotID(self, pilotID):
282 """Set the pilot ID."""
283 if pilotID!=self._pilotID:
284 self._pilotID = pilotID
285 self._modified = True
286
287 @property
288 def password(self):
289 """Get the password."""
290 return self._password
291
292 @password.setter
293 def password(self, password):
294 """Set the password."""
295 if password!=self._password:
296 self._password = password
297 self._modified = True
298
299 @property
[45]300 def rememberPassword(self):
301 """Get if we should remember the password."""
302 return self._rememberPassword
303
304 @rememberPassword.setter
305 def rememberPassword(self, rememberPassword):
306 """Set if we should remember the password."""
307 if rememberPassword!=self._rememberPassword:
308 self._rememberPassword = rememberPassword
309 self._modified = True
310
311 @property
[123]312 def language(self):
313 """Get the language to use."""
314 return self._language
315
316 @language.setter
317 def language(self, language):
318 """Set the language to use."""
319 if language!=self._language:
320 self._language = language
321 self._modified = True
322
323 @property
[147]324 def hideMinimizedWindow(self):
325 """Get whether a minimized window should be hidden."""
326 return self._hideMinimizedWindow
327
328 @hideMinimizedWindow.setter
329 def hideMinimizedWindow(self, hideMinimizedWindow):
330 """Set whether a minimized window should be hidden."""
331 if hideMinimizedWindow!=self._hideMinimizedWindow:
332 self._hideMinimizedWindow = hideMinimizedWindow
333 self._modified = True
[373]334
[147]335 @property
[249]336 def quitOnClose(self):
337 """Get whether the application should quit when the close button is
338 clicked."""
339 return self._quitOnClose
340
341 @quitOnClose.setter
342 def quitOnClose(self, quitOnClose):
343 """Set whether the application should quit when the close button is
344 clicked."""
345 if quitOnClose!=self._quitOnClose:
346 self._quitOnClose = quitOnClose
347 self._modified = True
[373]348
[249]349 @property
[136]350 def onlineGateSystem(self):
351 """Get whether the online gate system should be used."""
352 return self._onlineGateSystem
353
354 @onlineGateSystem.setter
355 def onlineGateSystem(self, onlineGateSystem):
356 """Set whether the online gate system should be used."""
357 if onlineGateSystem!=self._onlineGateSystem:
358 self._onlineGateSystem = onlineGateSystem
359 self._modified = True
360
361 @property
[139]362 def onlineACARS(self):
363 """Get whether the online ACARS system should be used."""
364 return self._onlineACARS
365
366 @onlineACARS.setter
367 def onlineACARS(self, onlineACARS):
368 """Set whether the online ACARS system should be used."""
369 if onlineACARS!=self._onlineACARS:
370 self._onlineACARS = onlineACARS
371 self._modified = True
372
373 @property
[131]374 def flareTimeFromFS(self):
375 """Get whether the flare time should be calculated from the time values
376 returned by the simulator."""
377 return self._flareTimeFromFS
378
379 @flareTimeFromFS.setter
380 def flareTimeFromFS(self, flareTimeFromFS):
381 """Set whether the flare time should be calculated from the time values
382 returned by the simulator."""
383 if flareTimeFromFS!=self._flareTimeFromFS:
384 self._flareTimeFromFS = flareTimeFromFS
385 self._modified = True
386
[148]387 @property
388 def syncFSTime(self):
389 """Get whether the simulator's time should be synchronized with the
390 machine's clock."""
391 return self._syncFSTime
392
393 @syncFSTime.setter
394 def syncFSTime(self, syncFSTime):
395 """Set whether the simulator's time should be synchronized with the
396 machine's clock."""
397 if syncFSTime!=self._syncFSTime:
398 self._syncFSTime = syncFSTime
399 self._modified = True
400
[149]401 @property
[183]402 def usingFS2Crew(self):
403 """Get whether the FS2Crew addon is being used."""
404 return self._usingFS2Crew
405
406 @usingFS2Crew.setter
407 def usingFS2Crew(self, usingFS2Crew):
408 """Set whether the FS2Crew addon is being used."""
409 if usingFS2Crew!=self._usingFS2Crew:
410 self._usingFS2Crew = usingFS2Crew
411 self._modified = True
412
413 @property
[197]414 def iasSmoothingLength(self):
415 """Get the number of samples over which the IAS is averaged for the
416 smoothed IAS calculation. It may be negative, in which case smoothing
417 is disabled, but we nevertheless store the number of seconds in case it
418 may become useful later."""
419 return self._iasSmoothingLength
420
421 @property
422 def realIASSmoothingLength(self):
423 """Get the real smoothing length of IAS."""
424 return max(self._iasSmoothingLength, 1)
425
426 @iasSmoothingLength.setter
427 def iasSmoothingLength(self, iasSmoothingLength):
428 """Set the number of samples over which the IAS is averaged for the
429 smoothed IAS calculation."""
430 if iasSmoothingLength!=self._iasSmoothingLength:
431 self._iasSmoothingLength = iasSmoothingLength
432 self._modified = True
433
434 @property
435 def vsSmoothingLength(self):
436 """Get the number of samples over which the VS is averaged for the
437 smoothed VS calculation. It may be negative, in which case smoothing
438 is disabled, but we nevertheless store the number of seconds in case it
439 may become useful later."""
440 return self._vsSmoothingLength
441
442 @property
443 def realVSSmoothingLength(self):
444 """Get the real smoothing length of VS."""
445 return max(self._vsSmoothingLength, 1)
446
447 @vsSmoothingLength.setter
448 def vsSmoothingLength(self, vsSmoothingLength):
449 """Set the number of samples over which the VS is averaged for the
450 smoothed VS calculation."""
451 if vsSmoothingLength!=self._vsSmoothingLength:
452 self._vsSmoothingLength = vsSmoothingLength
453 self._modified = True
454
455 @property
[684]456 def useSimBrief(self):
457 """Check if SimBrief should be used."""
458 return self._useSimBrief
459
460 @useSimBrief.setter
461 def useSimBrief(self, useSimBrief):
462 """Check if SimBrief should be used."""
463 if self._useSimBrief != useSimBrief:
464 self._useSimBrief = useSimBrief
465 self._modified = True
466
467 @property
[689]468 def simBriefUserName(self):
469 """Get the SimBrief user name last used"""
470 return self._simBriefUserName
471
472 @simBriefUserName.setter
473 def simBriefUserName(self, simBriefUserName):
474 """Set the SimBrief user name to be used next."""
475 if self._simBriefUserName != simBriefUserName:
476 self._simBriefUserName = simBriefUserName
477 self._modified = True
478
479 @property
480 def simBriefPassword(self):
481 """Get the SimBrief password last used"""
482 return self._simBriefPassword
483
484 @simBriefPassword.setter
485 def simBriefPassword(self, simBriefPassword):
486 """Set the SimBrief password to be used next."""
487 if self._simBriefPassword != simBriefPassword:
488 self._simBriefPassword = simBriefPassword
489 self._modified = True
490
491 @property
492 def rememberSimBriefPassword(self):
493 """Get if we should remember the SimBrief password."""
494 return self._rememberSimBriefPassword
495
496 @rememberSimBriefPassword.setter
497 def rememberSimBriefPassword(self, rememberSimBriefPassword):
498 """Set if we should remember the SimBrief password."""
499 if rememberSimBriefPassword!=self._rememberSimBriefPassword:
500 self._rememberSimBriefPassword = rememberSimBriefPassword
501 self._modified = True
502
503 @property
[149]504 def pirepDirectory(self):
505 """Get the directory offered by default when saving a PIREP."""
506 return self._pirepDirectory
507
508 @pirepDirectory.setter
509 def pirepDirectory(self, pirepDirectory):
510 """Get the directory offered by default when saving a PIREP."""
511 if pirepDirectory!=self._pirepDirectory and \
512 (pirepDirectory!="" or self._pirepDirectory is not None):
513 self._pirepDirectory = None if pirepDirectory=="" \
514 else pirepDirectory
[392]515 if self._pirepDirectory is None:
516 self._pirepAutoSave = False
517 self._modified = True
518
519 @property
520 def pirepAutoSave(self):
521 """Get whether the PIREP should be saved automatically when it becomes
522 saveable."""
523 return self._pirepAutoSave
524
525 @pirepAutoSave.setter
526 def pirepAutoSave(self, pirepAutoSave):
527 """Set whether the PIREP should be saved automatically when it becomes
528 saveable."""
529 pirepAutoSave = pirepAutoSave and self._pirepDirectory is not None
530 if pirepAutoSave!=self._pirepAutoSave:
531 self._pirepAutoSave = pirepAutoSave
[149]532 self._modified = True
533
[503]534 @property
535 def defaultMSFS(self):
536 """Get if the default simulator type is MS FS."""
537 return self._defaultMSFS
538
539 @defaultMSFS.setter
540 def defaultMSFS(self, defaultMSFS):
541 """Set if the default simulator type is MS FS."""
542 if defaultMSFS!=self._defaultMSFS:
543 self._defaultMSFS = defaultMSFS
544 self._modified = True
545
[132]546 def getMessageTypeLevel(self, messageType):
547 """Get the level for the given message type."""
548 return self._messageTypeLevels[messageType] \
549 if messageType in self._messageTypeLevels \
550 else const.MESSAGELEVEL_NONE
551
[134]552 def isMessageTypeFS(self, messageType):
553 """Determine if the given message type is displayed in the
554 simulator."""
555 level = self.getMessageTypeLevel(messageType)
556 return level==const.MESSAGELEVEL_FS or \
557 level==const.MESSAGELEVEL_BOTH
[373]558
[132]559 def setMessageTypeLevel(self, messageType, level):
560 """Set the level of the given message type."""
561 if messageType not in self._messageTypeLevels or \
562 self._messageTypeLevels[messageType]!=level:
563 self._messageTypeLevels[messageType] = level
564 self._modified = True
565
[131]566 @property
[166]567 def enableSounds(self):
568 """Get whether background sounds are enabled."""
569 return self._enableSounds
570
571 @enableSounds.setter
572 def enableSounds(self, enableSounds):
573 """Set whether background sounds are enabled."""
574 if enableSounds!=self._enableSounds:
575 self._enableSounds = enableSounds
576 self._modified = True
577
[373]578 @property
[166]579 def pilotControlsSounds(self):
580 """Get whether the pilot controls the background sounds."""
581 return self._pilotControlsSounds
582
583 @pilotControlsSounds.setter
584 def pilotControlsSounds(self, pilotControlsSounds):
585 """Set whether the pilot controls the background sounds."""
586 if pilotControlsSounds!=self._pilotControlsSounds:
587 self._pilotControlsSounds = pilotControlsSounds
588 self._modified = True
589
590 @property
591 def pilotHotkey(self):
592 """Get the pilot's hotkey."""
593 return self._pilotHotkey
594
595 @pilotHotkey.setter
596 def pilotHotkey(self, pilotHotkey):
597 """Set the pilot's hotkey."""
598 if pilotHotkey!=self._pilotHotkey:
599 self._pilotHotkey = pilotHotkey
600 self._modified = True
601
[264]602 @property
603 def enableApproachCallouts(self):
604 """Get whether the approach callouts should be played."""
605 return self._enableApproachCallouts
[166]606
[264]607 @enableApproachCallouts.setter
608 def enableApproachCallouts(self, enableApproachCallouts):
609 """Set whether the approach callouts should be played."""
610 if enableApproachCallouts!=self._enableApproachCallouts:
611 self._enableApproachCallouts = enableApproachCallouts
612 self._modified = True
[166]613
614 @property
615 def speedbrakeAtTD(self):
616 """Get whether the speedbrake sounds should be played at touchdown."""
617 return self._speedbrakeAtTD
618
619 @speedbrakeAtTD.setter
620 def speedbrakeAtTD(self, speedbrakeAtTD):
621 """Set whether the speedbrake sounds should be played at touchdown."""
622 if speedbrakeAtTD!=self._speedbrakeAtTD:
623 self._speedbrakeAtTD = speedbrakeAtTD
624 self._modified = True
[373]625
[166]626 @property
627 def enableChecklists(self):
628 """Get whether aircraft-specific checklists should be played."""
629 return self._enableChecklists
630
631 @enableChecklists.setter
632 def enableChecklists(self, enableChecklists):
633 """Get whether aircraft-specific checklists should be played."""
634 if enableChecklists!=self._enableChecklists:
635 self._enableChecklists = enableChecklists
636 self._modified = True
637
638 @property
639 def checklistHotkey(self):
640 """Get the checklist hotkey."""
641 return self._checklistHotkey
642
643 @checklistHotkey.setter
644 def checklistHotkey(self, checklistHotkey):
645 """Set the checklist hotkey."""
646 if checklistHotkey!=self._checklistHotkey:
647 self._checklistHotkey = checklistHotkey
648 self._modified = True
649
650 @property
[40]651 def autoUpdate(self):
652 """Get if an automatic update is needed."""
653 return self._autoUpdate
654
655 @autoUpdate.setter
656 def autoUpdate(self, autoUpdate):
657 """Set if an automatic update is needed."""
658 if autoUpdate!=self._autoUpdate:
659 self._autoUpdate = autoUpdate
660 self._modified = True
661
662 @property
663 def updateURL(self):
664 """Get the update URL."""
665 return self._updateURL
666
667 @updateURL.setter
668 def updateURL(self, updateURL):
669 """Set the update URL."""
670 if updateURL!=self._updateURL:
671 self._updateURL = updateURL
672 self._modified = True
673
[1067]674 @property
675 def xplaneRemote(self):
676 """Indicate if X-Plane should be accessed remotely."""
677 return self._xplaneRemote
678
679 @xplaneRemote.setter
680 def xplaneRemote(self, xplaneRemote):
681 """Set if X-Plane should be accessed remotely."""
682 if xplaneRemote!=self._xplaneRemote:
683 self._xplaneRemote = xplaneRemote
684 self._modified = True
685
686 @property
687 def xplaneAddress(self):
688 """Get the address of the machine running X-Plane"""
689 return self._xplaneAddress
690
691 @xplaneAddress.setter
692 def xplaneAddress(self, xplaneAddress):
693 """Set the address of the machine running X-Plane."""
694 if xplaneAddress!=self._xplaneAddress:
695 self._xplaneAddress = xplaneAddress
696 self._modified = True
697
[175]698 def getChecklist(self, aircraftType):
699 """Get the checklist for the given aircraft type."""
700 return self._checklists[aircraftType]
701
702 def setChecklist(self, aircraftType, checklist):
703 """Set the checklist for the given aircraft type."""
704 if checklist!=self._checklists[aircraftType]:
705 self._checklists[aircraftType] = checklist.clone()
706 self._modified = True
707
[264]708 def getApproachCallouts(self, aircraftType):
709 """Get the approach callouts for the given aircraft type."""
710 return self._approachCallouts[aircraftType]
711
712 def setApproachCallouts(self, aircraftType, approachCallouts):
713 """Set the approach callouts for the given aircraft type."""
714 if not approachCallouts==self._approachCallouts[aircraftType]:
715 self._approachCallouts[aircraftType] = approachCallouts.clone()
716 self._modified = True
717
[40]718 def load(self):
719 """Load the configuration from its default location."""
[1011]720 self._updateURLUpdated = False
721
[373]722 try:
[919]723 config = configparser.RawConfigParser()
[373]724 config.read(configPath)
725 except:
726 traceback.print_exc()
727 return
[40]728
[42]729 self._pilotID = self._get(config, "login", "id", "")
730 self._password = self._get(config, "login", "password", "")
[45]731 self._rememberPassword = self._getBoolean(config, "login",
732 "rememberPassword", False)
[42]733
[132]734 self._language = self._get(config, "general", "language", "")
[249]735
[147]736 self._hideMinimizedWindow = self._getBoolean(config, "general",
737 "hideMinimizedWindow",
738 True)
[249]739 self._quitOnClose = self._getBoolean(config, "general",
740 "quitOnClose", False)
[373]741
[136]742 self._onlineGateSystem = self._getBoolean(config, "general",
743 "onlineGateSystem",
[373]744 not secondaryInstallation)
[139]745 self._onlineACARS = self._getBoolean(config, "general",
[373]746 "onlineACARS",
747 not secondaryInstallation)
[132]748 self._flareTimeFromFS = self._getBoolean(config, "general",
749 "flareTimeFromFS",
750 False)
[148]751 self._syncFSTime = self._getBoolean(config, "general",
752 "syncFSTime",
753 False)
[183]754 self._usingFS2Crew = self._getBoolean(config, "general",
755 "usingFS2Crew",
756 False)
[197]757 self._iasSmoothingLength = int(self._get(config, "general",
758 "iasSmoothingLength",
759 -2))
760 self._vsSmoothingLength = int(self._get(config, "general",
761 "vsSmoothingLength",
762 -2))
[684]763
[688]764 self._useSimBrief = self._getBoolean(config, "simbrief",
765 "use", False)
[689]766 self._simBriefUserName = self._get(config, "simbrief",
767 "username", "")
768 self._simBriefPassword = self._get(config, "simbrief",
769 "password", "")
770 self._rememberSimBriefPassword = self._getBoolean(config, "simbrief",
771 "rememberPassword",
772 False)
[684]773
[149]774 self._pirepDirectory = self._get(config, "general",
775 "pirepDirectory", None)
[132]776
[485]777 self._pirepAutoSave = self._getBoolean(config, "general",
778 "pirepAutoSave", False)
[486]779 if self._pirepDirectory is None:
780 self._pirepAutoSave = False
[392]781
[132]782 self._messageTypeLevels = {}
783 for messageType in const.messageTypes:
784 self._messageTypeLevels[messageType] = \
785 self._getMessageTypeLevel(config, messageType)
[166]786
[373]787 self._enableSounds = self._getBoolean(config, "sounds", "enable",
788 not secondaryInstallation)
[166]789 self._pilotControlsSounds = self._getBoolean(config, "sounds",
790 "pilotControls", True)
791 self._pilotHotkey.set(self._get(config, "sounds",
792 "pilotHotkey", "C0"))
[270]793 self._enableApproachCallouts = \
794 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
[166]795 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
796 "speedbrakeAtTD", True)
797
798 self._enableChecklists = self._getBoolean(config, "sounds",
799 "enableChecklists", False)
800 self._checklistHotkey.set(self._get(config, "sounds",
801 "checklistHotkey", "CS0"))
[373]802
[40]803 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
[1011]804 self._updateURLUpdated = self._getBoolean(config, "update",
805 "urlUpdated", False)
[1040]806 if self._updateURLUpdated:
807 self._updateURL = self._get(config, "update", "url",
808 Config.DEFAULT_UPDATE_URL) # +
809 #("/exp" if secondaryInstallation else ""))
[1045]810
811 if self._updateURL.find("update/new")>0 or \
812 self._updateURL.find("update/py3")>0:
813 self._updateURL = "https://mlx.varadiistvan.hu/update"
814 self._updateURLUpdated = False
[1011]815 if self._updateURL.startswith("http://") and not self._updateURLUpdated:
816 self._updateURL = "https://" + self._updateURL[7:]
817 self._updateURLUpdated = True
818
[175]819 for aircraftType in const.aircraftTypes:
820 self._checklists[aircraftType] = \
821 Checklist.fromConfig(config, aircraftType)
[264]822 self._approachCallouts[aircraftType] = \
823 ApproachCallouts.fromConfig(config, aircraftType)
[175]824
[503]825 self._defaultMSFS = self._getBoolean(config, "general",
826 "defaultMSFS", os.name=="nt")
827
[1067]828 self._xplaneRemote = self._getBoolean(config, "general",
829 "xplaneRemote", False)
830 self._xplaneAddress = self._get(config, "general",
831 "xplaneAddress", "")
832
[40]833 self._modified = False
834
835 def save(self):
836 """Save the configuration file if it has been modified."""
837 if not self._modified:
838 return
839
[919]840 config = configparser.RawConfigParser()
[40]841
[42]842 config.add_section("login")
843 config.set("login", "id", self._pilotID)
844 config.set("login", "password", self._password)
[45]845 config.set("login", "rememberPassword",
846 "yes" if self._rememberPassword else "no")
[42]847
[132]848 config.add_section("general")
849 if self._language:
850 config.set("general", "language", self._language)
[147]851 config.set("general", "hideMinimizedWindow",
852 "yes" if self._hideMinimizedWindow else "no")
[249]853 config.set("general", "quitOnClose",
854 "yes" if self._quitOnClose else "no")
[136]855 config.set("general", "onlineGateSystem",
856 "yes" if self._onlineGateSystem else "no")
[139]857 config.set("general", "onlineACARS",
858 "yes" if self._onlineACARS else "no")
[132]859 config.set("general", "flareTimeFromFS",
860 "yes" if self._flareTimeFromFS else "no")
[148]861 config.set("general", "syncFSTime",
862 "yes" if self._syncFSTime else "no")
[183]863 config.set("general", "usingFS2Crew",
864 "yes" if self._usingFS2Crew else "no")
[197]865 config.set("general", "iasSmoothingLength",
866 str(self._iasSmoothingLength))
867 config.set("general", "vsSmoothingLength",
868 str(self._vsSmoothingLength))
[132]869
[688]870 config.add_section("simbrief")
871 config.set("simbrief", "use",
[684]872 "yes" if self._useSimBrief else "no")
[689]873 config.set("simbrief", "username", self._simBriefUserName)
874 config.set("simbrief", "password", self._simBriefPassword)
875 config.set("simbrief", "rememberPassword",
876 "yes" if self._rememberSimBriefPassword else "no")
[684]877
[149]878 if self._pirepDirectory is not None:
879 config.set("general", "pirepDirectory", self._pirepDirectory)
[392]880 config.set("general", "pirepAutoSave",
881 "yes" if self._pirepAutoSave else "no")
[149]882
[503]883 config.set("general", "defaultMSFS",
884 "yes" if self._defaultMSFS else "no")
885
[1067]886 config.set("general", "xplaneRemote",
887 "yes" if self._xplaneRemote else "no")
888 config.set("general", "xplaneAddress", self._xplaneAddress)
889
[132]890 config.add_section(Config._messageTypesSection)
891 for messageType in const.messageTypes:
892 if messageType in self._messageTypeLevels:
893 option = self._getMessageTypeLevelOptionName(messageType)
[373]894 level = self._messageTypeLevels[messageType]
[132]895 config.set(Config._messageTypesSection, option,
896 const.messageLevel2string(level))
[166]897
898 config.add_section("sounds")
899 config.set("sounds", "enable",
900 "yes" if self._enableSounds else "no")
901 config.set("sounds", "pilotControls",
902 "yes" if self._pilotControlsSounds else "no")
903 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
[270]904 config.set("sounds", "enableApproachCallouts",
905 "yes" if self._enableApproachCallouts else "no")
[166]906 config.set("sounds", "speedbrakeAtTD",
907 "yes" if self._speedbrakeAtTD else "no")
908
909 config.set("sounds", "enableChecklists",
910 "yes" if self._enableChecklists else "no")
911 config.set("sounds", "checklistHotkey",
912 str(self._checklistHotkey))
[373]913
[40]914 config.add_section("update")
[45]915 config.set("update", "auto",
916 "yes" if self._autoUpdate else "no")
[40]917 config.set("update", "url", self._updateURL)
[1011]918 config.set("update", "urlUpdated", self._updateURLUpdated)
[40]919
[175]920 config.add_section(Checklist.SECTION)
[264]921 config.add_section(ApproachCallouts.SECTION)
[175]922 for aircraftType in const.aircraftTypes:
923 self._checklists[aircraftType].toConfig(config, aircraftType)
[264]924 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
[175]925
[40]926 try:
[43]927 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
[919]928 0o600)
[43]929 with os.fdopen(fd, "wt") as f:
[40]930 config.write(f)
931 self._modified = False
[402]932
[919]933 print("Configuration saved:")
[402]934 self.log()
935
[919]936 except Exception as e:
937 print("Failed to update config: " + \
938 utf2unicode(str(e)), file=sys.stderr)
[40]939
940 def _getBoolean(self, config, section, option, default):
941 """Get the given option as a boolean, if found in the given config,
942 otherwise the default."""
943 return config.getboolean(section, option) \
944 if config.has_option(section, option) \
945 else default
[373]946
[40]947 def _get(self, config, section, option, default):
948 """Get the given option as a string, if found in the given config,
949 otherwise the default."""
950 return config.get(section, option) \
951 if config.has_option(section, option) \
952 else default
[36]953
[132]954 def _getMessageTypeLevel(self, config, messageType):
955 """Get the message type level for the given message type."""
956 option = self._getMessageTypeLevelOptionName(messageType)
957 if config.has_option(Config._messageTypesSection, option):
958 value = config.get(Config._messageTypesSection, option)
959 return const.string2messageLevel(value)
[373]960 elif secondaryInstallation:
961 return const.MESSAGELEVEL_NONE
[133]962 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
963 const.MESSAGETYPE_FAULT,
964 const.MESSAGETYPE_NOGO,
965 const.MESSAGETYPE_GATE_SYSTEM,
[373]966 const.MESSAGETYPE_HELP]:
[133]967 return const.MESSAGELEVEL_BOTH
[132]968 else:
[133]969 return const.MESSAGELEVEL_FS
[132]970
971 def _getMessageTypeLevelOptionName(self, messageType):
972 """Get the option name for the given message type level."""
973 return const.messageType2string(messageType)
[155]974
975 def setupLocale(self):
976 """Setup the locale based on the language set.
977
978 Return True if a specific language was set, False otherwise."""
979 import locale
980 if self._language:
[919]981 print("Setting up locale for", self._language)
[155]982 os.environ["LANGUAGE"] = self._language
983 langAndEncoding = self._language + "." + locale.getpreferredencoding()
984 os.environ["LANG"] = langAndEncoding
985 os.environ["LC_MESSAGES"] = langAndEncoding
986 os.environ["LC_COLLATE"] = langAndEncoding
987 os.environ["LC_CTYPE"] = langAndEncoding
[156]988 os.environ["LC_MONETARY"] = langAndEncoding
989 os.environ["LC_NUMERIC"] = langAndEncoding
990 os.environ["LC_TIME"] = langAndEncoding
[155]991 return True
992 else:
993 return False
994
[107]995 def getLanguage(self):
996 """Get the language to be used."""
[113]997 import locale
[107]998 if self._language:
[124]999 if os.name=="nt":
1000 if self._language in _languageMap:
1001 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
1002 else:
1003 locale.setlocale(locale.LC_ALL, "")
1004 else:
1005 locale.setlocale(locale.LC_ALL, (self._language,
1006 locale.getpreferredencoding()))
[107]1007 return self._language
1008 else:
[113]1009 locale.setlocale(locale.LC_ALL, "")
[107]1010 return locale.getdefaultlocale()[0]
1011
[402]1012 def log(self):
1013 """Log the configuration by printing the values"""
[919]1014 print(" pilot ID:", self._pilotID)
1015 print(" rememberPassword:", self._rememberPassword)
[402]1016
[919]1017 print(" language:", self._language)
[402]1018
[919]1019 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
1020 print(" quitOnClose:", self._quitOnClose)
[402]1021
[919]1022 print(" onlineGateSystem:", self._onlineGateSystem)
1023 print(" onlineACARS:", self._onlineACARS)
[402]1024
[919]1025 print(" flareTimeFromFS:", self._flareTimeFromFS)
1026 print(" syncFSTime:", self._syncFSTime)
1027 print(" usingFS2Crew:", self._usingFS2Crew)
[402]1028
[919]1029 print(" iasSmoothingLength:", self._iasSmoothingLength)
1030 print(" vsSmoothingLength:", self._vsSmoothingLength)
[402]1031
[919]1032 print(" useSimBrief:", self._useSimBrief)
1033 print(" simBriefUserName:", self._simBriefUserName)
1034 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
[684]1035
[919]1036 print(" pirepDirectory:", self._pirepDirectory)
1037 print(" pirepAutoSave:", self._pirepAutoSave)
[402]1038
[919]1039 print(" defaultMSFS:", self._defaultMSFS)
[1067]1040 print(" xplaneRemote:", self._xplaneRemote)
1041 print(" xplaneAddress:", self._xplaneAddress)
[503]1042
[919]1043 print(" enableSounds:", self._enableSounds)
[402]1044
[919]1045 print(" pilotControlsSounds:", self._pilotControlsSounds)
1046 print(" pilotHotkey:", str(self._pilotHotkey))
[402]1047
[919]1048 print(" enableApproachCallouts:", self._enableApproachCallouts)
1049 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
[402]1050
[919]1051 print(" enableChecklists:", self._enableChecklists)
1052 print(" checklistHotkey:", str(self._checklistHotkey))
[402]1053
[919]1054 print(" autoUpdate:", self._autoUpdate)
1055 print(" updateURL:", self._updateURL)
[1011]1056 print(" updateURLUpdated:", self._updateURLUpdated)
[402]1057
[919]1058 print(" messageTypeLevels:")
1059 for (type, level) in self._messageTypeLevels.items():
1060 print(" %s: %s" % (const.messageType2string(type),
1061 const.messageLevel2string(level)))
[402]1062
[919]1063 print(" checklists:")
1064 for (type, checklist) in self._checklists.items():
1065 print(" %s:" % (const.icaoCodes[type],))
[402]1066 for path in checklist:
[919]1067 print(" " + path)
[402]1068
[919]1069 print(" approachCallouts:")
1070 for (type, approachCallouts) in self._approachCallouts.items():
1071 print(" %s:" % (const.icaoCodes[type],))
[402]1072 for (altitude, path) in approachCallouts:
[919]1073 print(" %d: %s" % (altitude, path))
[402]1074
[36]1075#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.