source: src/mlx/config.py

python3
Last change on this file was 1067:9ce88d0b881d, checked in by István Váradi <ivaradi@…>, 6 weeks ago

Support for connecting to X-Plane remotely

File size: 40.1 KB
Line 
1# -*- encoding: utf-8 -*-
2
3from . import const
4from .util import secondaryInstallation, utf2unicode
5
6import os
7import sys
8import traceback
9import configparser
10
11## @package mlx.config
12#
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
23#-------------------------------------------------------------------------------
24
25configPath = os.path.join(os.path.expanduser("~"),
26 "mlx.config" if os.name=="nt" else ".mlxrc") + \
27 ("-secondary" if secondaryInstallation else "")
28
29#-------------------------------------------------------------------------------
30
31if os.name=="nt":
32 _languageMap = { "en_GB" : "eng",
33 "hu_HU" : "hun" }
34
35#-------------------------------------------------------------------------------
36
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
56 def __ne__(self, other):
57 """Check if the given hotkey is not equal to the other one."""
58 return not self==other
59
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
70class Checklist(object):
71 """A checklist for a certain aircraft type."""
72 # The name of the section of the checklists
73 SECTION="checklists"
74
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
110 def __ne__(self, other):
111 """Determine if the checklist is not equal to the given other one."""
112 return not self==other
113
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
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"
132
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
150 return ApproachCallouts(mapping)
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
165 for (altitude, path) in self._mapping.items():
166 option = baseName + str(index)
167 config.set(ApproachCallouts.SECTION, option,
168 "%d, %s" % (altitude, path))
169 index += 1
170
171 def getAltitudes(self, descending = True):
172 """Get the altitudes in decreasing order by default."""
173 altitudes = list(self._mapping.keys())
174 altitudes.sort(reverse = descending)
175 return altitudes
176
177 def __bool__(self):
178 """Return if there is anything in the mapping."""
179 return not not self._mapping
180
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):
196 """Get the file that is associated with the given altitude.
197
198 If no such file found, return None."""
199 return self._mapping[altitude] if altitude in self._mapping else None
200
201 def __iter__(self):
202 """Iterate over the pairs of altitudes and paths in decreasing order of
203 the altitude."""
204 altitudes = self.getAltitudes()
205
206 for altitude in altitudes:
207 yield (altitude, self._mapping[altitude])
208
209#-------------------------------------------------------------------------------
210
211class Config(object):
212 """Our configuration."""
213 DEFAULT_UPDATE_URL = "https://mlx.varadiistvan.hu/update/new"
214
215 _messageTypesSection = "messageTypes"
216
217 def __init__(self):
218 """Construct the configuration with default values."""
219
220 self._pilotID = ""
221 self._password = ""
222 self._rememberPassword = False
223
224 self._language = ""
225 self._hideMinimizedWindow = True
226 self._quitOnClose = False
227 self._onlineGateSystem = not secondaryInstallation
228 self._onlineACARS = not secondaryInstallation
229 self._flareTimeFromFS = False
230 self._syncFSTime = False
231 self._usingFS2Crew = False
232 self._iasSmoothingLength = -2
233 self._vsSmoothingLength = -2
234
235 self._useSimBrief = False
236 self._simBriefUserName = ""
237 self._simBriefPassword = ""
238 self._rememberSimBriefPassword = False
239
240 self._pirepDirectory = None
241 self._pirepAutoSave = False
242
243 self._defaultMSFS = os.name=="nt"
244
245 self._enableSounds = not secondaryInstallation
246
247 self._pilotControlsSounds = True
248 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
249
250 self._enableApproachCallouts = False
251 self._speedbrakeAtTD = True
252
253 self._enableChecklists = False
254 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
255
256 self._autoUpdate = True
257 self._updateURL = Config.DEFAULT_UPDATE_URL
258 #if secondaryInstallation:
259 # self._updateURL += "/exp"
260 self._updateURLUpdated = True
261
262 self._xplaneRemote = False
263 self._xplaneAddress = ""
264
265 self._messageTypeLevels = {}
266
267 self._checklists = {}
268 self._approachCallouts = {}
269 for aircraftType in const.aircraftTypes:
270 self._checklists[aircraftType] = Checklist()
271 self._approachCallouts[aircraftType] = ApproachCallouts()
272
273 self._modified = False
274
275 @property
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
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
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
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
334
335 @property
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
348
349 @property
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
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
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
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
401 @property
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
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
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
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
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
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
532 self._modified = True
533
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
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
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
558
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
566 @property
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
578 @property
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
602 @property
603 def enableApproachCallouts(self):
604 """Get whether the approach callouts should be played."""
605 return self._enableApproachCallouts
606
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
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
625
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
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
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
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
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
718 def load(self):
719 """Load the configuration from its default location."""
720 self._updateURLUpdated = False
721
722 try:
723 config = configparser.RawConfigParser()
724 config.read(configPath)
725 except:
726 traceback.print_exc()
727 return
728
729 self._pilotID = self._get(config, "login", "id", "")
730 self._password = self._get(config, "login", "password", "")
731 self._rememberPassword = self._getBoolean(config, "login",
732 "rememberPassword", False)
733
734 self._language = self._get(config, "general", "language", "")
735
736 self._hideMinimizedWindow = self._getBoolean(config, "general",
737 "hideMinimizedWindow",
738 True)
739 self._quitOnClose = self._getBoolean(config, "general",
740 "quitOnClose", False)
741
742 self._onlineGateSystem = self._getBoolean(config, "general",
743 "onlineGateSystem",
744 not secondaryInstallation)
745 self._onlineACARS = self._getBoolean(config, "general",
746 "onlineACARS",
747 not secondaryInstallation)
748 self._flareTimeFromFS = self._getBoolean(config, "general",
749 "flareTimeFromFS",
750 False)
751 self._syncFSTime = self._getBoolean(config, "general",
752 "syncFSTime",
753 False)
754 self._usingFS2Crew = self._getBoolean(config, "general",
755 "usingFS2Crew",
756 False)
757 self._iasSmoothingLength = int(self._get(config, "general",
758 "iasSmoothingLength",
759 -2))
760 self._vsSmoothingLength = int(self._get(config, "general",
761 "vsSmoothingLength",
762 -2))
763
764 self._useSimBrief = self._getBoolean(config, "simbrief",
765 "use", False)
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)
773
774 self._pirepDirectory = self._get(config, "general",
775 "pirepDirectory", None)
776
777 self._pirepAutoSave = self._getBoolean(config, "general",
778 "pirepAutoSave", False)
779 if self._pirepDirectory is None:
780 self._pirepAutoSave = False
781
782 self._messageTypeLevels = {}
783 for messageType in const.messageTypes:
784 self._messageTypeLevels[messageType] = \
785 self._getMessageTypeLevel(config, messageType)
786
787 self._enableSounds = self._getBoolean(config, "sounds", "enable",
788 not secondaryInstallation)
789 self._pilotControlsSounds = self._getBoolean(config, "sounds",
790 "pilotControls", True)
791 self._pilotHotkey.set(self._get(config, "sounds",
792 "pilotHotkey", "C0"))
793 self._enableApproachCallouts = \
794 self._getBoolean(config, "sounds", "enableApproachCallouts", False)
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"))
802
803 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
804 self._updateURLUpdated = self._getBoolean(config, "update",
805 "urlUpdated", False)
806 if self._updateURLUpdated:
807 self._updateURL = self._get(config, "update", "url",
808 Config.DEFAULT_UPDATE_URL) # +
809 #("/exp" if secondaryInstallation else ""))
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
815 if self._updateURL.startswith("http://") and not self._updateURLUpdated:
816 self._updateURL = "https://" + self._updateURL[7:]
817 self._updateURLUpdated = True
818
819 for aircraftType in const.aircraftTypes:
820 self._checklists[aircraftType] = \
821 Checklist.fromConfig(config, aircraftType)
822 self._approachCallouts[aircraftType] = \
823 ApproachCallouts.fromConfig(config, aircraftType)
824
825 self._defaultMSFS = self._getBoolean(config, "general",
826 "defaultMSFS", os.name=="nt")
827
828 self._xplaneRemote = self._getBoolean(config, "general",
829 "xplaneRemote", False)
830 self._xplaneAddress = self._get(config, "general",
831 "xplaneAddress", "")
832
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
840 config = configparser.RawConfigParser()
841
842 config.add_section("login")
843 config.set("login", "id", self._pilotID)
844 config.set("login", "password", self._password)
845 config.set("login", "rememberPassword",
846 "yes" if self._rememberPassword else "no")
847
848 config.add_section("general")
849 if self._language:
850 config.set("general", "language", self._language)
851 config.set("general", "hideMinimizedWindow",
852 "yes" if self._hideMinimizedWindow else "no")
853 config.set("general", "quitOnClose",
854 "yes" if self._quitOnClose else "no")
855 config.set("general", "onlineGateSystem",
856 "yes" if self._onlineGateSystem else "no")
857 config.set("general", "onlineACARS",
858 "yes" if self._onlineACARS else "no")
859 config.set("general", "flareTimeFromFS",
860 "yes" if self._flareTimeFromFS else "no")
861 config.set("general", "syncFSTime",
862 "yes" if self._syncFSTime else "no")
863 config.set("general", "usingFS2Crew",
864 "yes" if self._usingFS2Crew else "no")
865 config.set("general", "iasSmoothingLength",
866 str(self._iasSmoothingLength))
867 config.set("general", "vsSmoothingLength",
868 str(self._vsSmoothingLength))
869
870 config.add_section("simbrief")
871 config.set("simbrief", "use",
872 "yes" if self._useSimBrief else "no")
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")
877
878 if self._pirepDirectory is not None:
879 config.set("general", "pirepDirectory", self._pirepDirectory)
880 config.set("general", "pirepAutoSave",
881 "yes" if self._pirepAutoSave else "no")
882
883 config.set("general", "defaultMSFS",
884 "yes" if self._defaultMSFS else "no")
885
886 config.set("general", "xplaneRemote",
887 "yes" if self._xplaneRemote else "no")
888 config.set("general", "xplaneAddress", self._xplaneAddress)
889
890 config.add_section(Config._messageTypesSection)
891 for messageType in const.messageTypes:
892 if messageType in self._messageTypeLevels:
893 option = self._getMessageTypeLevelOptionName(messageType)
894 level = self._messageTypeLevels[messageType]
895 config.set(Config._messageTypesSection, option,
896 const.messageLevel2string(level))
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))
904 config.set("sounds", "enableApproachCallouts",
905 "yes" if self._enableApproachCallouts else "no")
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))
913
914 config.add_section("update")
915 config.set("update", "auto",
916 "yes" if self._autoUpdate else "no")
917 config.set("update", "url", self._updateURL)
918 config.set("update", "urlUpdated", self._updateURLUpdated)
919
920 config.add_section(Checklist.SECTION)
921 config.add_section(ApproachCallouts.SECTION)
922 for aircraftType in const.aircraftTypes:
923 self._checklists[aircraftType].toConfig(config, aircraftType)
924 self._approachCallouts[aircraftType].toConfig(config, aircraftType)
925
926 try:
927 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
928 0o600)
929 with os.fdopen(fd, "wt") as f:
930 config.write(f)
931 self._modified = False
932
933 print("Configuration saved:")
934 self.log()
935
936 except Exception as e:
937 print("Failed to update config: " + \
938 utf2unicode(str(e)), file=sys.stderr)
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
946
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
953
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)
960 elif secondaryInstallation:
961 return const.MESSAGELEVEL_NONE
962 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
963 const.MESSAGETYPE_FAULT,
964 const.MESSAGETYPE_NOGO,
965 const.MESSAGETYPE_GATE_SYSTEM,
966 const.MESSAGETYPE_HELP]:
967 return const.MESSAGELEVEL_BOTH
968 else:
969 return const.MESSAGELEVEL_FS
970
971 def _getMessageTypeLevelOptionName(self, messageType):
972 """Get the option name for the given message type level."""
973 return const.messageType2string(messageType)
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:
981 print("Setting up locale for", self._language)
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
988 os.environ["LC_MONETARY"] = langAndEncoding
989 os.environ["LC_NUMERIC"] = langAndEncoding
990 os.environ["LC_TIME"] = langAndEncoding
991 return True
992 else:
993 return False
994
995 def getLanguage(self):
996 """Get the language to be used."""
997 import locale
998 if self._language:
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()))
1007 return self._language
1008 else:
1009 locale.setlocale(locale.LC_ALL, "")
1010 return locale.getdefaultlocale()[0]
1011
1012 def log(self):
1013 """Log the configuration by printing the values"""
1014 print(" pilot ID:", self._pilotID)
1015 print(" rememberPassword:", self._rememberPassword)
1016
1017 print(" language:", self._language)
1018
1019 print(" hideMinimizedWindow:", self._hideMinimizedWindow)
1020 print(" quitOnClose:", self._quitOnClose)
1021
1022 print(" onlineGateSystem:", self._onlineGateSystem)
1023 print(" onlineACARS:", self._onlineACARS)
1024
1025 print(" flareTimeFromFS:", self._flareTimeFromFS)
1026 print(" syncFSTime:", self._syncFSTime)
1027 print(" usingFS2Crew:", self._usingFS2Crew)
1028
1029 print(" iasSmoothingLength:", self._iasSmoothingLength)
1030 print(" vsSmoothingLength:", self._vsSmoothingLength)
1031
1032 print(" useSimBrief:", self._useSimBrief)
1033 print(" simBriefUserName:", self._simBriefUserName)
1034 print(" rememberSimBriefPassword:", self._rememberSimBriefPassword)
1035
1036 print(" pirepDirectory:", self._pirepDirectory)
1037 print(" pirepAutoSave:", self._pirepAutoSave)
1038
1039 print(" defaultMSFS:", self._defaultMSFS)
1040 print(" xplaneRemote:", self._xplaneRemote)
1041 print(" xplaneAddress:", self._xplaneAddress)
1042
1043 print(" enableSounds:", self._enableSounds)
1044
1045 print(" pilotControlsSounds:", self._pilotControlsSounds)
1046 print(" pilotHotkey:", str(self._pilotHotkey))
1047
1048 print(" enableApproachCallouts:", self._enableApproachCallouts)
1049 print(" speedbrakeAtTD:", self._speedbrakeAtTD)
1050
1051 print(" enableChecklists:", self._enableChecklists)
1052 print(" checklistHotkey:", str(self._checklistHotkey))
1053
1054 print(" autoUpdate:", self._autoUpdate)
1055 print(" updateURL:", self._updateURL)
1056 print(" updateURLUpdated:", self._updateURLUpdated)
1057
1058 print(" messageTypeLevels:")
1059 for (type, level) in self._messageTypeLevels.items():
1060 print(" %s: %s" % (const.messageType2string(type),
1061 const.messageLevel2string(level)))
1062
1063 print(" checklists:")
1064 for (type, checklist) in self._checklists.items():
1065 print(" %s:" % (const.icaoCodes[type],))
1066 for path in checklist:
1067 print(" " + path)
1068
1069 print(" approachCallouts:")
1070 for (type, approachCallouts) in self._approachCallouts.items():
1071 print(" %s:" % (const.icaoCodes[type],))
1072 for (altitude, path) in approachCallouts:
1073 print(" %d: %s" % (altitude, path))
1074
1075#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.