source: src/mlx/config.py@ 233:c0ab38ca1546

Last change on this file since 233:c0ab38ca1546 was 197:93f89e9049be, checked in by István Váradi <ivaradi@…>, 12 years ago

Added support for smoothed IAS and VS values

File size: 25.5 KB
Line 
1# Configuration and related stuff
2# -*- encoding: utf-8 -*-
3
4#-------------------------------------------------------------------------------
5
6import const
7
8import os
9import sys
10import ConfigParser
11
12#-------------------------------------------------------------------------------
13
14configPath = os.path.join(os.path.expanduser("~"),
15 "mlx.config" if os.name=="nt" else ".mlxrc")
16
17#-------------------------------------------------------------------------------
18
19if os.name=="nt":
20 _languageMap = { "en_GB" : "eng",
21 "hu_HU" : "hun" }
22
23#-------------------------------------------------------------------------------
24
25class Hotkey(object):
26 """A hotkey."""
27 def __init__(self, ctrl = False, shift = False, key = "0"):
28 """Construct the hotkey."""
29 self.ctrl = ctrl
30 self.shift = shift
31 self.key = key
32
33 def set(self, s):
34 """Set the hotkey from the given string."""
35 self.ctrl = "C" in s[:-1]
36 self.shift = "S" in s[:-1]
37 self.key = s[-1]
38
39 def __eq__(self, other):
40 """Check if the given hotkey is equal to the other one."""
41 return self.ctrl == other.ctrl and self.shift == other.shift and \
42 self.key == other.key
43
44 def __str__(self):
45 """Construct the hotkey to a string."""
46 s = ""
47 if self.ctrl: s += "C"
48 if self.shift: s += "S"
49 s += self.key
50 return s
51
52#-------------------------------------------------------------------------------
53
54class Checklist(object):
55 """A checklist for a certain aircraft type."""
56 # The name of the section of the checklists
57 SECTION="checklists"
58
59 @staticmethod
60 def fromConfig(config, aircraftType):
61 """Create a checklist for the given aircraft type from the given
62 config."""
63 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
64 fileList = []
65 while True:
66 option = baseName + str(len(fileList))
67 if config.has_option(Checklist.SECTION, option):
68 fileList.append(config.get(Checklist.SECTION, option))
69 else:
70 break
71
72 return Checklist(fileList)
73
74 def __init__(self, fileList = None):
75 """Construct the check list with the given file list."""
76 self._fileList = [] if fileList is None else fileList[:]
77
78 def clone(self):
79 """Clone the checklist."""
80 return Checklist(self._fileList)
81
82 def toConfig(self, config, aircraftType):
83 """Add this checklist to the given config."""
84 baseName = "checklist." + const.icaoCodes[aircraftType] + "."
85 for index in range(0, len(self._fileList)):
86 option = baseName + str(index)
87 config.set(Checklist.SECTION, option,
88 self._fileList[index])
89
90 def __eq__(self, other):
91 """Determine if the checklist is equal to the given other one."""
92 return self._fileList == other._fileList
93
94 def __len__(self):
95 """Get the length of the file list."""
96 return len(self._fileList)
97
98 def __getitem__(self, index):
99 """Get the file with the given index."""
100 return self._fileList[index]
101
102 def __iter__(self):
103 """Iterate over the files."""
104 return iter(self._fileList)
105
106#-------------------------------------------------------------------------------
107
108class Config(object):
109 """Our configuration."""
110 DEFAULT_UPDATE_URL = "http://mlx.varadiistvan.hu/update"
111
112 _messageTypesSection = "messageTypes"
113
114 def __init__(self):
115 """Construct the configuration with default values."""
116
117 self._pilotID = ""
118 self._password = ""
119 self._rememberPassword = False
120
121 self._language = ""
122 self._hideMinimizedWindow = True
123 self._onlineGateSystem = True
124 self._onlineACARS = True
125 self._flareTimeFromFS = False
126 self._syncFSTime = False
127 self._usingFS2Crew = False
128 self._iasSmoothingLength = -2
129 self._vsSmoothingLength = -2
130
131 self._pirepDirectory = None
132
133 self._enableSounds = True
134
135 self._pilotControlsSounds = True
136 self._pilotHotkey = Hotkey(ctrl = True, shift = False, key = "0")
137
138 #self._approachCallOuts = False
139 self._speedbrakeAtTD = True
140
141 self._enableChecklists = False
142 self._checklistHotkey = Hotkey(ctrl = True, shift = True, key = "0")
143
144 self._autoUpdate = True
145 self._updateURL = Config.DEFAULT_UPDATE_URL
146
147 self._messageTypeLevels = {}
148
149 self._checklists = {}
150 for aircraftType in const.aircraftTypes:
151 self._checklists[aircraftType] = Checklist()
152
153 self._modified = False
154
155 @property
156 def pilotID(self):
157 """Get the pilot ID."""
158 return self._pilotID
159
160 @pilotID.setter
161 def pilotID(self, pilotID):
162 """Set the pilot ID."""
163 if pilotID!=self._pilotID:
164 self._pilotID = pilotID
165 self._modified = True
166
167 @property
168 def password(self):
169 """Get the password."""
170 return self._password
171
172 @password.setter
173 def password(self, password):
174 """Set the password."""
175 if password!=self._password:
176 self._password = password
177 self._modified = True
178
179 @property
180 def rememberPassword(self):
181 """Get if we should remember the password."""
182 return self._rememberPassword
183
184 @rememberPassword.setter
185 def rememberPassword(self, rememberPassword):
186 """Set if we should remember the password."""
187 if rememberPassword!=self._rememberPassword:
188 self._rememberPassword = rememberPassword
189 self._modified = True
190
191 @property
192 def language(self):
193 """Get the language to use."""
194 return self._language
195
196 @language.setter
197 def language(self, language):
198 """Set the language to use."""
199 if language!=self._language:
200 self._language = language
201 self._modified = True
202
203 @property
204 def hideMinimizedWindow(self):
205 """Get whether a minimized window should be hidden."""
206 return self._hideMinimizedWindow
207
208 @hideMinimizedWindow.setter
209 def hideMinimizedWindow(self, hideMinimizedWindow):
210 """Set whether a minimized window should be hidden."""
211 if hideMinimizedWindow!=self._hideMinimizedWindow:
212 self._hideMinimizedWindow = hideMinimizedWindow
213 self._modified = True
214
215 @property
216 def onlineGateSystem(self):
217 """Get whether the online gate system should be used."""
218 return self._onlineGateSystem
219
220 @onlineGateSystem.setter
221 def onlineGateSystem(self, onlineGateSystem):
222 """Set whether the online gate system should be used."""
223 if onlineGateSystem!=self._onlineGateSystem:
224 self._onlineGateSystem = onlineGateSystem
225 self._modified = True
226
227 @property
228 def onlineACARS(self):
229 """Get whether the online ACARS system should be used."""
230 return self._onlineACARS
231
232 @onlineACARS.setter
233 def onlineACARS(self, onlineACARS):
234 """Set whether the online ACARS system should be used."""
235 if onlineACARS!=self._onlineACARS:
236 self._onlineACARS = onlineACARS
237 self._modified = True
238
239 @property
240 def flareTimeFromFS(self):
241 """Get whether the flare time should be calculated from the time values
242 returned by the simulator."""
243 return self._flareTimeFromFS
244
245 @flareTimeFromFS.setter
246 def flareTimeFromFS(self, flareTimeFromFS):
247 """Set whether the flare time should be calculated from the time values
248 returned by the simulator."""
249 if flareTimeFromFS!=self._flareTimeFromFS:
250 self._flareTimeFromFS = flareTimeFromFS
251 self._modified = True
252
253 @property
254 def syncFSTime(self):
255 """Get whether the simulator's time should be synchronized with the
256 machine's clock."""
257 return self._syncFSTime
258
259 @syncFSTime.setter
260 def syncFSTime(self, syncFSTime):
261 """Set whether the simulator's time should be synchronized with the
262 machine's clock."""
263 if syncFSTime!=self._syncFSTime:
264 self._syncFSTime = syncFSTime
265 self._modified = True
266
267 @property
268 def usingFS2Crew(self):
269 """Get whether the FS2Crew addon is being used."""
270 return self._usingFS2Crew
271
272 @usingFS2Crew.setter
273 def usingFS2Crew(self, usingFS2Crew):
274 """Set whether the FS2Crew addon is being used."""
275 if usingFS2Crew!=self._usingFS2Crew:
276 self._usingFS2Crew = usingFS2Crew
277 self._modified = True
278
279 @property
280 def iasSmoothingLength(self):
281 """Get the number of samples over which the IAS is averaged for the
282 smoothed IAS calculation. It may be negative, in which case smoothing
283 is disabled, but we nevertheless store the number of seconds in case it
284 may become useful later."""
285 return self._iasSmoothingLength
286
287 @property
288 def realIASSmoothingLength(self):
289 """Get the real smoothing length of IAS."""
290 return max(self._iasSmoothingLength, 1)
291
292 @iasSmoothingLength.setter
293 def iasSmoothingLength(self, iasSmoothingLength):
294 """Set the number of samples over which the IAS is averaged for the
295 smoothed IAS calculation."""
296 if iasSmoothingLength!=self._iasSmoothingLength:
297 self._iasSmoothingLength = iasSmoothingLength
298 self._modified = True
299
300 @property
301 def vsSmoothingLength(self):
302 """Get the number of samples over which the VS is averaged for the
303 smoothed VS calculation. It may be negative, in which case smoothing
304 is disabled, but we nevertheless store the number of seconds in case it
305 may become useful later."""
306 return self._vsSmoothingLength
307
308 @property
309 def realVSSmoothingLength(self):
310 """Get the real smoothing length of VS."""
311 return max(self._vsSmoothingLength, 1)
312
313 @vsSmoothingLength.setter
314 def vsSmoothingLength(self, vsSmoothingLength):
315 """Set the number of samples over which the VS is averaged for the
316 smoothed VS calculation."""
317 if vsSmoothingLength!=self._vsSmoothingLength:
318 self._vsSmoothingLength = vsSmoothingLength
319 self._modified = True
320
321 @property
322 def pirepDirectory(self):
323 """Get the directory offered by default when saving a PIREP."""
324 return self._pirepDirectory
325
326 @pirepDirectory.setter
327 def pirepDirectory(self, pirepDirectory):
328 """Get the directory offered by default when saving a PIREP."""
329 if pirepDirectory!=self._pirepDirectory and \
330 (pirepDirectory!="" or self._pirepDirectory is not None):
331 self._pirepDirectory = None if pirepDirectory=="" \
332 else pirepDirectory
333 self._modified = True
334
335 def getMessageTypeLevel(self, messageType):
336 """Get the level for the given message type."""
337 return self._messageTypeLevels[messageType] \
338 if messageType in self._messageTypeLevels \
339 else const.MESSAGELEVEL_NONE
340
341 def isMessageTypeFS(self, messageType):
342 """Determine if the given message type is displayed in the
343 simulator."""
344 level = self.getMessageTypeLevel(messageType)
345 return level==const.MESSAGELEVEL_FS or \
346 level==const.MESSAGELEVEL_BOTH
347
348 def setMessageTypeLevel(self, messageType, level):
349 """Set the level of the given message type."""
350 if messageType not in self._messageTypeLevels or \
351 self._messageTypeLevels[messageType]!=level:
352 self._messageTypeLevels[messageType] = level
353 self._modified = True
354
355 @property
356 def enableSounds(self):
357 """Get whether background sounds are enabled."""
358 return self._enableSounds
359
360 @enableSounds.setter
361 def enableSounds(self, enableSounds):
362 """Set whether background sounds are enabled."""
363 if enableSounds!=self._enableSounds:
364 self._enableSounds = enableSounds
365 self._modified = True
366
367 @property
368 def pilotControlsSounds(self):
369 """Get whether the pilot controls the background sounds."""
370 return self._pilotControlsSounds
371
372 @pilotControlsSounds.setter
373 def pilotControlsSounds(self, pilotControlsSounds):
374 """Set whether the pilot controls the background sounds."""
375 if pilotControlsSounds!=self._pilotControlsSounds:
376 self._pilotControlsSounds = pilotControlsSounds
377 self._modified = True
378
379 @property
380 def pilotHotkey(self):
381 """Get the pilot's hotkey."""
382 return self._pilotHotkey
383
384 @pilotHotkey.setter
385 def pilotHotkey(self, pilotHotkey):
386 """Set the pilot's hotkey."""
387 if pilotHotkey!=self._pilotHotkey:
388 self._pilotHotkey = pilotHotkey
389 self._modified = True
390
391 # @property
392 # def approachCallOuts(self):
393 # """Get whether the approach callouts should be played."""
394 # return self._approachCallOuts
395
396 # @approachCallOuts.setter
397 # def approachCallOuts(self, approachCallOuts):
398 # """Set whether the approach callouts should be played."""
399 # if approachCallOuts!=self._approachCallOuts:
400 # self._approachCallOuts = approachCallOuts
401 # self._modified = True
402
403 @property
404 def speedbrakeAtTD(self):
405 """Get whether the speedbrake sounds should be played at touchdown."""
406 return self._speedbrakeAtTD
407
408 @speedbrakeAtTD.setter
409 def speedbrakeAtTD(self, speedbrakeAtTD):
410 """Set whether the speedbrake sounds should be played at touchdown."""
411 if speedbrakeAtTD!=self._speedbrakeAtTD:
412 self._speedbrakeAtTD = speedbrakeAtTD
413 self._modified = True
414
415 @property
416 def enableChecklists(self):
417 """Get whether aircraft-specific checklists should be played."""
418 return self._enableChecklists
419
420 @enableChecklists.setter
421 def enableChecklists(self, enableChecklists):
422 """Get whether aircraft-specific checklists should be played."""
423 if enableChecklists!=self._enableChecklists:
424 self._enableChecklists = enableChecklists
425 self._modified = True
426
427 @property
428 def checklistHotkey(self):
429 """Get the checklist hotkey."""
430 return self._checklistHotkey
431
432 @checklistHotkey.setter
433 def checklistHotkey(self, checklistHotkey):
434 """Set the checklist hotkey."""
435 if checklistHotkey!=self._checklistHotkey:
436 self._checklistHotkey = checklistHotkey
437 self._modified = True
438
439 @property
440 def autoUpdate(self):
441 """Get if an automatic update is needed."""
442 return self._autoUpdate
443
444 @autoUpdate.setter
445 def autoUpdate(self, autoUpdate):
446 """Set if an automatic update is needed."""
447 if autoUpdate!=self._autoUpdate:
448 self._autoUpdate = autoUpdate
449 self._modified = True
450
451 @property
452 def updateURL(self):
453 """Get the update URL."""
454 return self._updateURL
455
456 @updateURL.setter
457 def updateURL(self, updateURL):
458 """Set the update URL."""
459 if updateURL!=self._updateURL:
460 self._updateURL = updateURL
461 self._modified = True
462
463 def getChecklist(self, aircraftType):
464 """Get the checklist for the given aircraft type."""
465 return self._checklists[aircraftType]
466
467 def setChecklist(self, aircraftType, checklist):
468 """Set the checklist for the given aircraft type."""
469 if checklist!=self._checklists[aircraftType]:
470 self._checklists[aircraftType] = checklist.clone()
471 self._modified = True
472
473 def load(self):
474 """Load the configuration from its default location."""
475 config = ConfigParser.RawConfigParser()
476 config.read(configPath)
477
478 self._pilotID = self._get(config, "login", "id", "")
479 self._password = self._get(config, "login", "password", "")
480 self._rememberPassword = self._getBoolean(config, "login",
481 "rememberPassword", False)
482
483 self._language = self._get(config, "general", "language", "")
484 self._hideMinimizedWindow = self._getBoolean(config, "general",
485 "hideMinimizedWindow",
486 True)
487 self._onlineGateSystem = self._getBoolean(config, "general",
488 "onlineGateSystem",
489 True)
490 self._onlineACARS = self._getBoolean(config, "general",
491 "onlineACARS", True)
492 self._flareTimeFromFS = self._getBoolean(config, "general",
493 "flareTimeFromFS",
494 False)
495 self._syncFSTime = self._getBoolean(config, "general",
496 "syncFSTime",
497 False)
498 self._usingFS2Crew = self._getBoolean(config, "general",
499 "usingFS2Crew",
500 False)
501 self._iasSmoothingLength = int(self._get(config, "general",
502 "iasSmoothingLength",
503 -2))
504 self._vsSmoothingLength = int(self._get(config, "general",
505 "vsSmoothingLength",
506 -2))
507 self._pirepDirectory = self._get(config, "general",
508 "pirepDirectory", None)
509
510 self._messageTypeLevels = {}
511 for messageType in const.messageTypes:
512 self._messageTypeLevels[messageType] = \
513 self._getMessageTypeLevel(config, messageType)
514
515 self._enableSounds = self._getBoolean(config, "sounds",
516 "enable", True)
517 self._pilotControlsSounds = self._getBoolean(config, "sounds",
518 "pilotControls", True)
519 self._pilotHotkey.set(self._get(config, "sounds",
520 "pilotHotkey", "C0"))
521 #self._approachCallOuts = self._getBoolean(config, "sounds",
522 # "approachCallOuts", False)
523 self._speedbrakeAtTD = self._getBoolean(config, "sounds",
524 "speedbrakeAtTD", True)
525
526 self._enableChecklists = self._getBoolean(config, "sounds",
527 "enableChecklists", False)
528 self._checklistHotkey.set(self._get(config, "sounds",
529 "checklistHotkey", "CS0"))
530
531 self._autoUpdate = self._getBoolean(config, "update", "auto", True)
532 self._updateURL = self._get(config, "update", "url",
533 Config.DEFAULT_UPDATE_URL)
534
535 for aircraftType in const.aircraftTypes:
536 self._checklists[aircraftType] = \
537 Checklist.fromConfig(config, aircraftType)
538
539 self._modified = False
540
541 def save(self):
542 """Save the configuration file if it has been modified."""
543 if not self._modified:
544 return
545
546 config = ConfigParser.RawConfigParser()
547
548 config.add_section("login")
549 config.set("login", "id", self._pilotID)
550 config.set("login", "password", self._password)
551 config.set("login", "rememberPassword",
552 "yes" if self._rememberPassword else "no")
553
554 config.add_section("general")
555 if self._language:
556 config.set("general", "language", self._language)
557 config.set("general", "hideMinimizedWindow",
558 "yes" if self._hideMinimizedWindow else "no")
559 config.set("general", "onlineGateSystem",
560 "yes" if self._onlineGateSystem else "no")
561 config.set("general", "onlineACARS",
562 "yes" if self._onlineACARS else "no")
563 config.set("general", "flareTimeFromFS",
564 "yes" if self._flareTimeFromFS else "no")
565 config.set("general", "syncFSTime",
566 "yes" if self._syncFSTime else "no")
567 config.set("general", "usingFS2Crew",
568 "yes" if self._usingFS2Crew else "no")
569 config.set("general", "iasSmoothingLength",
570 str(self._iasSmoothingLength))
571 config.set("general", "vsSmoothingLength",
572 str(self._vsSmoothingLength))
573
574 if self._pirepDirectory is not None:
575 config.set("general", "pirepDirectory", self._pirepDirectory)
576
577 config.add_section(Config._messageTypesSection)
578 for messageType in const.messageTypes:
579 if messageType in self._messageTypeLevels:
580 option = self._getMessageTypeLevelOptionName(messageType)
581 level = self._messageTypeLevels[messageType]
582 config.set(Config._messageTypesSection, option,
583 const.messageLevel2string(level))
584
585 config.add_section("sounds")
586 config.set("sounds", "enable",
587 "yes" if self._enableSounds else "no")
588 config.set("sounds", "pilotControls",
589 "yes" if self._pilotControlsSounds else "no")
590 config.set("sounds", "pilotHotkey", str(self._pilotHotkey))
591 #config.set("sounds", "approachCallOuts",
592 # "yes" if self._approachCallOuts else "no")
593 config.set("sounds", "speedbrakeAtTD",
594 "yes" if self._speedbrakeAtTD else "no")
595
596 config.set("sounds", "enableChecklists",
597 "yes" if self._enableChecklists else "no")
598 config.set("sounds", "checklistHotkey",
599 str(self._checklistHotkey))
600
601 config.add_section("update")
602 config.set("update", "auto",
603 "yes" if self._autoUpdate else "no")
604 config.set("update", "url", self._updateURL)
605
606 config.add_section(Checklist.SECTION)
607 for aircraftType in const.aircraftTypes:
608 self._checklists[aircraftType].toConfig(config, aircraftType)
609
610 try:
611 fd = os.open(configPath, os.O_CREAT|os.O_TRUNC|os.O_WRONLY,
612 0600)
613 with os.fdopen(fd, "wt") as f:
614 config.write(f)
615 self._modified = False
616 except Exception, e:
617 print >> sys.stderr, "Failed to update config: " + str(e)
618
619 def _getBoolean(self, config, section, option, default):
620 """Get the given option as a boolean, if found in the given config,
621 otherwise the default."""
622 return config.getboolean(section, option) \
623 if config.has_option(section, option) \
624 else default
625
626 def _get(self, config, section, option, default):
627 """Get the given option as a string, if found in the given config,
628 otherwise the default."""
629 return config.get(section, option) \
630 if config.has_option(section, option) \
631 else default
632
633 def _getMessageTypeLevel(self, config, messageType):
634 """Get the message type level for the given message type."""
635 option = self._getMessageTypeLevelOptionName(messageType)
636 if config.has_option(Config._messageTypesSection, option):
637 value = config.get(Config._messageTypesSection, option)
638 return const.string2messageLevel(value)
639 elif messageType in [const.MESSAGETYPE_LOGGER_ERROR,
640 const.MESSAGETYPE_FAULT,
641 const.MESSAGETYPE_NOGO,
642 const.MESSAGETYPE_GATE_SYSTEM,
643 const.MESSAGETYPE_HELP]:
644 return const.MESSAGELEVEL_BOTH
645 else:
646 return const.MESSAGELEVEL_FS
647
648 def _getMessageTypeLevelOptionName(self, messageType):
649 """Get the option name for the given message type level."""
650 return const.messageType2string(messageType)
651
652 def setupLocale(self):
653 """Setup the locale based on the language set.
654
655 Return True if a specific language was set, False otherwise."""
656 import locale
657 if self._language:
658 print "Setting up locale for", self._language
659 os.environ["LANGUAGE"] = self._language
660 langAndEncoding = self._language + "." + locale.getpreferredencoding()
661 os.environ["LANG"] = langAndEncoding
662 os.environ["LC_MESSAGES"] = langAndEncoding
663 os.environ["LC_COLLATE"] = langAndEncoding
664 os.environ["LC_CTYPE"] = langAndEncoding
665 os.environ["LC_MONETARY"] = langAndEncoding
666 os.environ["LC_NUMERIC"] = langAndEncoding
667 os.environ["LC_TIME"] = langAndEncoding
668 return True
669 else:
670 return False
671
672 def getLanguage(self):
673 """Get the language to be used."""
674 import locale
675 if self._language:
676 if os.name=="nt":
677 if self._language in _languageMap:
678 locale.setlocale(locale.LC_ALL, _languageMap[self._language])
679 else:
680 locale.setlocale(locale.LC_ALL, "")
681 else:
682 locale.setlocale(locale.LC_ALL, (self._language,
683 locale.getpreferredencoding()))
684 return self._language
685 else:
686 locale.setlocale(locale.LC_ALL, "")
687 return locale.getdefaultlocale()[0]
688
689#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.