source: src/mlx/pyuipc_sim.py@ 243:1a42c5aa468b

Last change on this file since 243:1a42c5aa468b was 243:1a42c5aa468b, checked in by István Váradi <ivaradi@…>, 12 years ago

Added the logging of the CoG value

File size: 59.8 KB
RevLine 
[51]1# Simulator for the pyuipc module
2#------------------------------------------------------------------------------
3
[52]4import const
5
[53]6import cmd
7import threading
8import socket
[51]9import time
[54]10import calendar
[53]11import sys
12import struct
13import cPickle
[57]14import math
[51]15
16#------------------------------------------------------------------------------
17
18# Version constants
19SIM_ANY=0
20SIM_FS98=1
21SIM_FS2K=2
22SIM_CFS2=3
23SIM_CFS1=4
24SIM_FLY=5
25SIM_FS2K2=6
26SIM_FS2K4=7
[212]27SIM_FSX=8
[51]28
29#------------------------------------------------------------------------------
30
31# Error constants
32ERR_OK=0
33ERR_OPEN=1
34ERR_NOFS=2
35ERR_REGMSG=3
36ERR_ATOM=4
37ERR_MAP=5
38ERR_VIEW=6
39ERR_VERSION=7
40ERR_WRONGFS=8
41ERR_NOTOPEN=9
42ERR_NODATA=10
43ERR_TIMEOUT=11
44ERR_SENDMSG=12
45ERR_DATA=13
46ERR_RUNNING=14
47ERR_SIZE=15
48
49#------------------------------------------------------------------------------
50
51# The version of FSUIPC
52fsuipc_version=0x0401
53lib_version=0x0302
54fs_version=SIM_FS2K4
55
56#------------------------------------------------------------------------------
57
58class FSUIPCException(Exception):
59 """FSUIPC exception class.
60
61 It contains a member variable named errorCode. The string is a text
62 describing the error."""
63
64 errors=["OK",
65 "Attempt to Open when already Open",
66 "Cannot link to FSUIPC or WideClient",
67 "Failed to Register common message with Windows",
68 "Failed to create Atom for mapping filename",
69 "Failed to create a file mapping object",
70 "Failed to open a view to the file map",
71 "Incorrect version of FSUIPC, or not FSUIPC",
72 "Sim is not version requested",
73 "Call cannot execute, link not Open",
74 "Call cannot execute: no requests accumulated",
75 "IPC timed out all retries",
76 "IPC sendmessage failed all retries",
77 "IPC request contains bad data",
78 "Maybe running on WideClient, but FS not running on Server, or wrong FSUIPC",
79 "Read or Write request cannot be added, memory for Process is full"]
80
81 def __init__(self, errorCode):
82 """
83 Construct the exception
84 """
85 if errorCode<len(self.errors):
86 self.errorString = self.errors[errorCode]
87 else:
88 self.errorString = "Unknown error"
89 Exception.__init__(self, self.errorString)
90 self.errorCode = errorCode
91
92 def __str__(self):
93 """
94 Convert the excption to string
95 """
96 return "FSUIPC error: %d (%s)" % (self.errorCode, self.errorString)
97
98#------------------------------------------------------------------------------
99
[52]100class Values(object):
101 """The values that can be read from 'FSUIPC'."""
102 # Fuel data index: centre tank
103 FUEL_CENTRE = 0
104
105 # Fuel data index: left main tank
106 FUEL_LEFT = 1
107
108 # Fuel data index: right main tank
109 FUEL_RIGHT = 2
110
111 # Fuel data index: left aux tank
112 FUEL_LEFT_AUX = 3
113
114 # Fuel data index: right aux tank
115 FUEL_RIGHT_AUX = 4
116
117 # Fuel data index: left tip tank
118 FUEL_LEFT_TIP = 5
119
120 # Fuel data index: right tip tank
[211]121 FUEL_RIGHT_TIP = 6
[52]122
123 # Fuel data index: external 1 tank
124 FUEL_EXTERNAL_1 = 7
125
126 # Fuel data index: external 2 tank
127 FUEL_EXTERNAL_2 = 8
128
129 # Fuel data index: centre 2 tank
130 FUEL_CENTRE_2 = 9
131
132 # The number of fuel tank entries
133 NUM_FUEL = FUEL_CENTRE_2 + 1
134
135 # Engine index: engine #1
136 ENGINE_1 = 0
137
138 # Engine index: engine #2
139 ENGINE_2 = 1
140
141 # Engine index: engine #3
142 ENGINE_3 = 2
143
[177]144 # The number of hotkey entries
145 HOTKEY_SIZE = 56
146
[52]147 @staticmethod
[54]148 def _readFrequency(frequency):
[52]149 """Convert the given frequency into BCD."""
[54]150 return Values._readBCD(int(frequency*100.0))
[52]151
152 @staticmethod
[54]153 def _readBCD(value):
[52]154 """Convert the given value into BCD format."""
155 bcd = (value/1000) % 10
156 bcd <<= 4
[53]157 bcd |= (value/100) % 10
[52]158 bcd <<= 4
159 bcd |= (value/10) % 10
160 bcd <<= 4
161 bcd |= value % 10
162 return bcd
163
[54]164 @staticmethod
165 def _writeFrequency(value):
166 """Convert the given value into a frequency."""
167 return (Values._writeBCD(value) + 10000) / 100.0
168
169 @staticmethod
170 def _writeBCD(value):
171 """Convert the given BCD value into a real value."""
[55]172 bcd = (value>>12) & 0x0f
[54]173 bcd *= 10
[55]174 bcd += (value>>8) & 0x0f
[54]175 bcd *= 10
[55]176 bcd += (value>>4) & 0x0f
[54]177 bcd *= 10
[55]178 bcd += (value>>0) & 0x0f
[54]179
180 return bcd
181
[52]182 def __init__(self):
183 """Construct the values with defaults."""
184 self._timeOffset = 0
185 self.airPath = "C:\\Program Files\\Microsoft Games\\" \
186 "FS9\\Aircraft\\Cessna\\cessna172.air"
187 self.aircraftName = "Cessna 172SP"
188 self.flapsNotches = [0, 1, 2, 5, 10, 15, 25, 30, 40]
[211]189 self.fuelCapacities = [10000.0, 5000.0, 5000.0, 5000.0, 5000.0,
190 5000.0, 5000.0, 5000.0, 5000.0, 5000.0]
[89]191
192 self.latitude = 47.5
193 self.longitude = 19.05
[52]194
195 self.paused = False
196 self.frozen = False
197 self.replay = False
198 self.slew = False
199 self.overspeed = False
200 self.stalled = False
201 self.onTheGround = True
202
203 self.zfw = 50000.0
204
205 self.fuelWeights = [0.0, 3000.0, 3000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
[56]206 # Wikipedia: "Jet fuel", Jet A-1 density at 15*C .804kg/l -> 6.7 pounds/gallon
207 self.fuelWeight = 6.70970518
[52]208
209 self.heading = 220.0
210 self.pitch = 0.0
211 self.bank = 0.0
212
213 self.ias = 0.0
214 self.vs = 0.0
[85]215 self.tdRate = 0.0
[52]216
217 self.radioAltitude = None
218 self.altitude = 513.0
219
220 self.gLoad = 1.0
221
222 self.flapsControl = 0.0
223 self.flaps = 0.0
224
225 self.navLightsOn = True
226 self.antiCollisionLightsOn = False
227 self.landingLightsOn = False
228 self.strobeLightsOn = False
229
230 self.pitot = False
231 self.parking = True
232
[209]233 self.gearControl = 1.0
[52]234 self.noseGear = 1.0
235
236 self.spoilersArmed = False
237 self.spoilers = 0.0
238
239 self.altimeter = 1013.0
240
241 self.nav1 = 117.3
242 self.nav2 = 109.5
243 self.squawk = 2200
244
245 self.windSpeed = 8.0
246 self.windDirection = 300.0
[85]247 self.visibility = 10000
[52]248
249 self.n1 = [0.0, 0.0, 0.0]
250 self.throttles = [0.0, 0.0, 0.0]
251
[117]252 self.payloadCount = 1
253 self.payload = []
254 for i in range(0, 61): self.payload.append(0.0)
255
[133]256 self.textScrolling = False
257 self.message = ""
258 self.messageDuration = 0
259
[177]260 self.hotkeyTable = []
261 for i in range(0, Values.HOTKEY_SIZE):
262 self.hotkeyTable.append([0, 0, 0, 0])
263
[243]264 self.cog = 0.27
265
[212]266 self.pmdg_737ng_switches = 0
267 self.pmdg_737ngx_lts_positionsw = 0
268
[177]269 def read(self, offset, type):
[52]270 """Read the value at the given offset."""
271 try:
[177]272 return self._read(offset, type)
[52]273 except Exception, e:
274 print "failed to read offset %04x: %s" % (offset, str(e))
275 raise FSUIPCException(ERR_DATA)
276
[177]277 def _read(self, offset, type):
[52]278 """Read the value at the given offset."""
279 if offset==0x023a: # Second of time
280 return self._readUTC().tm_sec
281 elif offset==0x023b: # Hour of Zulu time
282 return self._readUTC().tm_hour
283 elif offset==0x023c: # Minute of Zulu time
284 return self._readUTC().tm_min
285 elif offset==0x023e: # Day number on year
286 return self._readUTC().tm_yday
287 elif offset==0x0240: # Year in FS
288 return self._readUTC().tm_year
289 elif offset==0x0264: # Paused
290 return 1 if self.paused else 0
291 elif offset==0x029c: # Pitot
292 return 1 if self.pitot else 0
293 elif offset==0x02b4: # Ground speed
294 # FIXME: calculate TAS first, then from the heading and
295 # wind the GS
296 return int(self.ias * 65536.0 * 1852.0 / 3600.0)
[57]297 elif offset==0x02b8: # TAS
298 return int(self._getTAS() * 128.0)
[52]299 elif offset==0x02bc: # IAS
300 return int(self.ias * 128.0)
301 elif offset==0x02c8: # VS
302 return int(self.vs * const.FEETTOMETRES * 256.0 / 60.0)
[85]303 elif offset==0x030c: # TD rate
304 return int(self.tdRate * const.FEETTOMETRES * 256.0 / 60.0)
[52]305 elif offset==0x0330: # Altimeter
306 return int(self.altimeter * 16.0)
307 elif offset==0x0350: # NAV1
[54]308 return Values._readFrequency(self.nav1)
[52]309 elif offset==0x0352: # NAV2
[54]310 return Values._readFrequency(self.nav2)
[52]311 elif offset==0x0354: # Squawk
[54]312 return Values._readBCD(self.squawk)
313 elif offset==0x0366: # On the ground
314 return 1 if self.onTheGround else 0
[52]315 elif offset==0x036c: # Stalled
316 return 1 if self.stalled else 0
317 elif offset==0x036d: # Overspeed
318 return 1 if self.overspeed else 0
[89]319 elif offset==0x0560: # Latitude
320 return long(self.latitude * 10001750.0 * 65536.0 * 65536.0 / 90.0)
321 elif offset==0x0568: # Longitude
322 return long(self.longitude * 65536.0 * 65536.0 * 65536.0 * 65536.0 / 360.0)
[52]323 elif offset==0x0570: # Altitude
324 return long(self.altitude * const.FEETTOMETRES * 65536.0 * 65536.0)
325 elif offset==0x0578: # Pitch
326 return int(self.pitch * 65536.0 * 65536.0 / 360.0)
327 elif offset==0x057c: # Bank
328 return int(self.bank * 65536.0 * 65536.0 / 360.0)
329 elif offset==0x0580: # Heading
330 return int(self.heading * 65536.0 * 65536.0 / 360.0)
331 elif offset==0x05dc: # Slew
332 return 1 if self.slew else 0
333 elif offset==0x0628: # Replay
334 return 1 if self.replay else 0
335 elif offset==0x088c: # Engine #1 throttle
336 return self._getThrottle(self.ENGINE_1)
337 elif offset==0x0924: # Engine #2 throttle
338 return self._getThrottle(self.ENGINE_2)
339 elif offset==0x09bc: # Engine #3 throttle
340 return self._getThrottle(self.ENGINE_3)
341 elif offset==0x0af4: # Fuel weight
342 return int(self.fuelWeight * 256.0)
343 elif offset==0x0b74: # Centre tank level
344 return self._getFuelLevel(self.FUEL_CENTRE)
345 elif offset==0x0b78: # Centre tank capacity
346 return self._getFuelCapacity(self.FUEL_CENTRE)
347 elif offset==0x0b7c: # Left tank level
348 return self._getFuelLevel(self.FUEL_LEFT)
349 elif offset==0x0b80: # Left tank capacity
350 return self._getFuelCapacity(self.FUEL_LEFT)
351 elif offset==0x0b84: # Left aux tank level
352 return self._getFuelLevel(self.FUEL_LEFT_AUX)
353 elif offset==0x0b88: # Left aux tank capacity
354 return self._getFuelCapacity(self.FUEL_LEFT_AUX)
355 elif offset==0x0b8c: # Left tip tank level
356 return self._getFuelLevel(self.FUEL_LEFT_TIP)
357 elif offset==0x0b90: # Left tip tank capacity
358 return self._getFuelCapacity(self.FUEL_LEFT_TIP)
359 elif offset==0x0b94: # Right aux tank level
[53]360 return self._getFuelLevel(self.FUEL_RIGHT)
[52]361 elif offset==0x0b98: # Right aux tank capacity
[53]362 return self._getFuelCapacity(self.FUEL_RIGHT)
[52]363 elif offset==0x0b9c: # Right tank level
[53]364 return self._getFuelLevel(self.FUEL_RIGHT_AUX)
[52]365 elif offset==0x0ba0: # Right tank capacity
[53]366 return self._getFuelCapacity(self.FUEL_RIGHT_AUX)
[52]367 elif offset==0x0ba4: # Right tip tank level
368 return self._getFuelLevel(self.FUEL_RIGHT_TIP)
369 elif offset==0x0ba8: # Right tip tank capacity
370 return self._getFuelCapacity(self.FUEL_RIGHT_TIP)
371 elif offset==0x0bc8: # Parking
372 return 1 if self.parking else 0
373 elif offset==0x0bcc: # Spoilers armed
374 return 1 if self.spoilersArmed else 0
375 elif offset==0x0bd0: # Spoilers
376 return 0 if self.spoilers == 0 \
377 else int(self.spoilers * (16383 - 4800) + 4800)
378 elif offset==0x0bdc: # Flaps control
379 numNotchesM1 = len(self.flapsNotches) - 1
380 flapsIncrement = 16383.0 / numNotchesM1
381 index = 0
382 while index<numNotchesM1 and \
[88]383 self.flapsControl>self.flapsNotches[index]:
[52]384 index += 1
[88]385
[52]386 if index==numNotchesM1:
387 return 16383
388 else:
[88]389 return int(index * flapsIncrement +
390 (self.flapsControl-self.flapsNotches[index]) *
391 flapsIncrement /
[52]392 (self.flapsNotches[index+1] - self.flapsNotches[index]))
393 elif offset==0x0be0 or offset==0x0be4: # Flaps left and right
394 return self.flaps * 16383.0 / self.flapsNotches[-1]
[209]395 elif offset==0x0be8: # Gear control
396 return int(self.gearControl * 16383.0)
[52]397 elif offset==0x0bec: # Nose gear
398 return int(self.noseGear * 16383.0)
399 elif offset==0x0d0c: # Lights
400 lights = 0
401 if self.navLightsOn: lights |= 0x01
402 if self.antiCollisionLightsOn: lights |= 0x02
403 if self.landingLightsOn: lights |= 0x04
404 if self.strobeLightsOn: lights |= 0x10
405 return lights
[85]406 elif offset==0x0e8a: # Visibility
407 return int(self.visibility * 100.0 / 1609.344)
[52]408 elif offset==0x0e90: # Wind speed
409 return int(self.windSpeed)
410 elif offset==0x0e92: # Wind direction
411 return int(self.windDirection * 65536.0 / 360.0)
412 elif offset==0x11ba: # G-Load
413 return int(self.gLoad * 625.0)
414 elif offset==0x11c6: # Mach
415 # FIXME: calculate from IAS, altitude and QNH
416 return int(self.ias * 0.05 * 20480.)
417 elif offset==0x1244: # Centre 2 tank level
418 return self._getFuelLevel(self.FUEL_CENTRE_2)
419 elif offset==0x1248: # Centre 2 tank capacity
420 return self._getFuelCapacity(self.FUEL_CENTRE_2)
421 elif offset==0x1254: # External 1 tank level
422 return self._getFuelLevel(self.FUEL_EXTERNAL_1)
423 elif offset==0x1258: # External 1 tank capacity
424 return self._getFuelCapacity(self.FUEL_EXTERNAL_1)
425 elif offset==0x125c: # External 2 tank level
426 return self._getFuelLevel(self.FUEL_EXTERNAL_2)
427 elif offset==0x1260: # External 2 tank capacity
428 return self._getFuelCapacity(self.FUEL_EXTERNAL_2)
[133]429 elif offset==0x1274: # Text display mode
430 return 1 if self.textScrolling else 0
[117]431 elif offset==0x13fc: # The number of the payload stations
432 return self.payloadCount
433 elif offset>=0x1400 and offset<=0x1f40 and \
434 ((offset-0x1400)%48)==0: # Payload
435 return self.payload[ (offset - 0x1400) / 48 ]
[52]436 elif offset==0x2000: # Engine #1 N1
437 return self.n1[self.ENGINE_1]
438 elif offset==0x2100: # Engine #2 N1
439 return self.n1[self.ENGINE_2]
440 elif offset==0x2200: # Engine #3 N1
441 return self.n1[self.ENGINE_3]
[243]442 elif offset==0x2ef8: # Centre of Gravity
443 return self.cog
[55]444 elif offset==0x30c0: # Gross weight
445 return (self.zfw + sum(self.fuelWeights)) * const.KGSTOLB
[52]446 elif offset==0x31e4: # Radio altitude
447 # FIXME: if self.radioAltitude is None, calculate from the
448 # altitude with some, perhaps random, ground altitude
449 # value
450 radioAltitude = (self.altitude - 517) \
451 if self.radioAltitude is None else self.radioAltitude
452 return (radioAltitude * const.FEETTOMETRES * 65536.0)
[177]453 elif offset==0x320c:
454 return Values.HOTKEY_SIZE
455 elif offset>=0x3210 and offset<0x3210+Values.HOTKEY_SIZE*4:
456 tableOffset = offset - 0x3210
457 hotkeyIndex = tableOffset / 4
458 index = tableOffset % 4
459 if type=="b" or type=="c":
460 return self.hotkeyTable[hotkeyIndex][index]
461 elif type=="d" or type=="u":
462 if index==0:
463 hotkey = self.hotkeyTable[hotkeyIndex]
464 value = hotkey[3]
465 value <<= 8
466 value |= hotkey[2]
467 value <<= 8
468 value |= hotkey[1]
469 value <<= 8
470 value |= hotkey[0]
471 return value
472 else:
473 print "Unhandled offset: %04x" % (offset,)
474 raise FSUIPCException(ERR_DATA)
475 else:
476 print "Unhandled offset: %04x" % (offset,)
477 raise FSUIPCException(ERR_DATA)
[133]478 elif offset==0x32fa: # Message duration
479 return self.messageDuration
480 elif offset==0x3380: # Message
481 return self.message
[52]482 elif offset==0x3364: # Frozen
483 return 1 if self.frozen else 0
484 elif offset==0x3bfc: # ZFW
[53]485 return int(self.zfw * 256.0 * const.KGSTOLB)
[52]486 elif offset==0x3c00: # Path of the current AIR file
487 return self.airPath
488 elif offset==0x3d00: # Name of the current aircraft
489 return self.aircraftName
[212]490 elif offset==0x6202: # PMDG 737NG switches
491 return self.pmdg_737ng_switches
492 elif offset==0x6500: # PMDG 737NGX lights position SW
493 return self.pmdg_737ngx_lts_positionsw
[52]494 else:
495 print "Unhandled offset: %04x" % (offset,)
496 raise FSUIPCException(ERR_DATA)
[54]497
[177]498 def write(self, offset, value, type):
[54]499 """Write the value at the given offset."""
500 try:
[177]501 return self._write(offset, value, type)
[54]502 except Exception, e:
503 print "failed to write offset %04x: %s" % (offset, str(e))
504 raise FSUIPCException(ERR_DATA)
505
[177]506 def _write(self, offset, value, type):
[54]507 """Write the given value at the given offset."""
508 if offset==0x023a: # Second of time
509 self._updateTimeOffset(5, value)
510 elif offset==0x023b: # Hour of Zulu time
511 self._updateTimeOffset(3, value)
512 elif offset==0x023c: # Minute of Zulu time
513 self._updateTimeOffset(4, value)
514 elif offset==0x023e: # Day number in the year
515 self._updateTimeOffset(7, value)
516 elif offset==0x0240: # Year in FS
517 self._updateTimeOffset(0, value)
518 elif offset==0x0264: # Paused
519 self.paused = value!=0
520 elif offset==0x029c: # Pitot
521 self.pitot = value!=0
522 elif offset==0x02b4: # Ground speed
523 # FIXME: calculate TAS using the heading and the wind, and
524 # then IAS based on the altitude
525 self.ias = value * 3600.0 / 65536.0 / 1852.0
526 elif offset==0x02bc: # IAS
527 self.ias = value / 128.0
528 elif offset==0x02c8: # VS
529 self.vs = value * 60.0 / const.FEETTOMETRES / 256.0
[85]530 if not self.onTheGround:
531 self.tdRate = self.vs
[54]532 elif offset==0x0330: # Altimeter
533 self.altimeter = value / 16.0
534 elif offset==0x0350: # NAV1
535 self.nav1 = Values._writeFrequency(value)
536 elif offset==0x0352: # NAV2
537 self.nav2 = Values._writeFrequency(value)
538 elif offset==0x0354: # Squawk
539 self.squawk = Values._writeBCD(value)
[85]540 elif offset==0x0366: # On the groud
[54]541 self.onTheGround = value!=0
[85]542 if not self.onTheGround:
543 self.tdRate = self.vs
[54]544 elif offset==0x036c: # Stalled
545 self.stalled = value!=0
546 elif offset==0x036d: # Overspeed
547 self.overspeed = value!=0
[89]548 elif offset==0x0560: # Latitude
549 self.latitude = value * 90.0 / 10001750.0 / 65536.0 / 65536.0
550 elif offset==0x0568: # Longitude
551 self.longitude = value * 360.0 / 65536.0 / 65536.0 / 65536.0 / 65536.0
[54]552 elif offset==0x0570: # Altitude
553 self.altitude = value / const.FEETTOMETRES / 65536.0 / 65536.0
554 elif offset==0x0578: # Pitch
555 self.pitch = value * 360.0 / 65536.0 / 65536.0
556 elif offset==0x057c: # Bank
557 self.bank = value * 360.0 / 65536.0 / 65536.0
558 elif offset==0x0580: # Heading
559 self.heading = value * 360.0 / 65536.0 / 65536.0
560 elif offset==0x05dc: # Slew
561 self.slew = value!=0
562 elif offset==0x0628: # Replay
563 self.replay = value!=0
564 elif offset==0x088c: # Engine #1 throttle
565 self._setThrottle(self.ENGINE_1, value)
566 elif offset==0x0924: # Engine #2 throttle
567 self._setThrottle(self.ENGINE_2, value)
568 elif offset==0x09bc: # Engine #3 throttle
569 self._setThrottle(self.ENGINE_3, value)
570 elif offset==0x0af4: # Fuel weight
571 self.fuelWeight = value / 256.0
572 elif offset==0x0b74: # Centre tank level
573 self._setFuelLevel(self.FUEL_CENTRE, value)
574 elif offset==0x0b78: # Centre tank capacity
575 self._setFuelCapacity(self.FUEL_CENTRE, value)
576 elif offset==0x0b7c: # Left tank level
577 self._setFuelLevel(self.FUEL_LEFT, value)
578 elif offset==0x0b80: # Left tank capacity
579 self._setFuelCapacity(self.FUEL_LEFT, value)
580 elif offset==0x0b84: # Left aux tank level
581 self._setFuelLevel(self.FUEL_LEFT_AUX, value)
582 elif offset==0x0b88: # Left aux tank capacity
583 self._setFuelCapacity(self.FUEL_LEFT_AUX, value)
584 elif offset==0x0b8c: # Left tip tank level
585 self._setFuelLevel(self.FUEL_LEFT_TIP, value)
586 elif offset==0x0b90: # Left tip tank capacity
587 self._setFuelCapacity(self.FUEL_LEFT_TIP, value)
588 elif offset==0x0b94: # Right aux tank level
589 self._setFuelLevel(self.FUEL_RIGHT, value)
590 elif offset==0x0b98: # Right aux tank capacity
591 self._setFuelCapacity(self.FUEL_RIGHT, value)
592 elif offset==0x0b9c: # Right tank level
593 self._setFuelLevel(self.FUEL_RIGHT_AUX, value)
594 elif offset==0x0ba0: # Right tank capacity
595 self._setFuelCapacity(self.FUEL_RIGHT_AUX, value)
596 elif offset==0x0ba4: # Right tip tank level
597 self._setFuelLevel(self.FUEL_RIGHT_TIP, value)
598 elif offset==0x0ba8: # Right tip tank capacity
599 self._setFuelCapacity(self.FUEL_RIGHT_TIP, value)
600 elif offset==0x0bc8: # Parking
601 self.parking = value!=0
602 elif offset==0x0bcc: # Spoilers armed
603 self.spoilersArmed = value!=0
604 elif offset==0x0bd0: # Spoilers
605 self.spoilters = 0 if value==0 \
606 else (value - 4800) / (16383 - 4800)
607 elif offset==0x0bdc: # Flaps control
608 numNotchesM1 = len(self.flapsNotches) - 1
609 flapsIncrement = 16383.0 / numNotchesM1
[88]610 index = int(value / flapsIncrement)
[54]611 if index>=numNotchesM1:
612 self.flapsControl = self.flapsNotches[-1]
613 else:
614 self.flapsControl = self.flapsNotches[index]
[88]615 self.flapsControl += (value - index * flapsIncrement) * \
[54]616 (self.flapsNotches[index+1] - self.flapsNotches[index]) / \
617 flapsIncrement
618 elif offset==0x0be0 or offset==0x0be4: # Flaps left and right
619 self.flaps = value * self.flapsNotches[-1] / 16383.0
[209]620 elif offset==0x0be8: # Gear control
621 self.gearControl = value / 16383.0
[54]622 elif offset==0x0bec: # Nose gear
623 self.noseGear = value / 16383.0
624 elif offset==0x0d0c: # Lights
[83]625 self.navLightsOn = (value&0x01)!=0
626 self.antiCollisionLightsOn = (value&0x02)!=0
627 self.landingLightsOn = (value&0x04)!=0
628 self.strobeLightsOn = (value&0x10)!=0
[85]629 elif offset==0x0e8a: # Visibility
630 self.visibility = value * 1609.344 / 100.0
[54]631 elif offset==0x0e90: # Wind speed
632 self.windSpeed = value
633 elif offset==0x0e92: # Wind direction
634 self.windDirection = value * 360.0 / 65536.0
635 elif offset==0x11ba: # G-Load
636 self.gLoad = value / 625.0
637 elif offset==0x11c6: # Mach
638 # FIXME: calculate IAS using the altitude and QNH
639 self.ias = value / 0.05 / 20480
640 elif offset==0x1244: # Centre 2 tank level
641 self._setFuelLevel(self.FUEL_CENTRE_2, value)
642 elif offset==0x1248: # Centre 2 tank capacity
643 self._setFuelCapacity(self.FUEL_CENTRE_2, value)
644 elif offset==0x1254: # External 1 tank level
645 self._setFuelLevel(self.FUEL_EXTERNAL_1, value)
646 elif offset==0x1258: # External 1 tank capacity
647 self._setFuelCapacity(self.FUEL_EXTERNAL_1, value)
648 elif offset==0x125c: # External 2 tank level
649 self._setFuelLevel(self.FUEL_EXTERNAL_2, value)
650 elif offset==0x1260: # External 2 tank capacity
651 self._setFuelCapacity(self.FUEL_EXTERNAL_2, value)
[133]652 elif offset==0x1274: # Text display mode
653 textScrolling = value!=0
[117]654 elif offset==0x13fc: # The number of the payload stations
655 self.payloadCount = int(value)
656 elif offset>=0x1400 and offset<=0x1f40 and \
657 ((offset-0x1400)%48)==0: # Payload
658 self.payload[ (offset - 0x1400) / 48 ] = value
[54]659 elif offset==0x2000: # Engine #1 N1
660 self.n1[self.ENGINE_1] = value
661 elif offset==0x2100: # Engine #2 N1
662 self.n1[self.ENGINE_2] = value
663 elif offset==0x2200: # Engine #3 N1
664 self.n1[self.ENGINE_3] = value
[243]665 elif offset==0x2ef8: # Centre of Gravity
666 self.cog = value
[55]667 elif offset==0x30c0: # Gross weight
[54]668 raise FSUIPCException(ERR_DATA)
669 elif offset==0x31e4: # Radio altitude
670 raise FSUIPCException(ERR_DATA)
[177]671 elif offset==0x320c:
672 return Values.HOTKEY_SIZE
673 elif offset>=0x3210 and offset<0x3210+Values.HOTKEY_SIZE*4:
674 tableOffset = offset - 0x3210
675 hotkeyIndex = tableOffset / 4
676 index = tableOffset % 4
677 if type=="b" or type=="c":
678 self.hotkeyTable[hotkeyIndex][index] = value
679 elif type=="d" or type=="u":
680 if index==0:
681 hotkey = self.hotkeyTable[hotkeyIndex]
682 hotkey[0] = value & 0xff
683 hotkey[1] = (value>>8) & 0xff
684 hotkey[2] = (value>>16) & 0xff
685 hotkey[3] = (value>>24) & 0xff
686 else:
687 print "Unhandled offset: %04x for type '%s'" % (offset, type)
688 raise FSUIPCException(ERR_DATA)
689 else:
690 print "Unhandled offset: %04x for type '%s'" % (offset, type)
691 raise FSUIPCException(ERR_DATA)
[133]692 elif offset==0x32fa: # Message duration
693 self.messageDuration = value
694 elif offset==0x3380: # Message
695 self.message = value
[54]696 elif offset==0x3364: # Frozen
697 self.frozen = value!=0
698 elif offset==0x3bfc: # ZFW
699 self.zfw = value * const.LBSTOKG / 256.0
700 elif offset==0x3c00: # Path of the current AIR file
701 self.airPath = value
702 elif offset==0x3d00: # Name of the current aircraft
703 self.aircraftName = value
[212]704 elif offset==0x6202: # PMDG 737NG switches
705 self.pmdg_737ng_switches = value
706 elif offset==0x6500: # PMDG 737NGX lights position SW
707 self.pmdg_737ngx_lts_positionsw = value
[54]708 else:
709 print "Unhandled offset: %04x" % (offset,)
710 raise FSUIPCException(ERR_DATA)
[52]711
712 def _readUTC(self):
713 """Read the UTC time.
714
715 The current offset is added to it."""
716 return time.gmtime(time.time() + self._timeOffset)
717
718 def _getFuelLevel(self, index):
719 """Get the fuel level for the fuel tank with the given
720 index."""
721 return 0 if self.fuelCapacities[index]==0.0 else \
[54]722 int(self.fuelWeights[index] * 65536.0 * 128.0 / self.fuelCapacities[index])
[52]723
724 def _getFuelCapacity(self, index):
725 """Get the capacity of the fuel tank with the given index."""
[54]726 return int(self.fuelCapacities[index] * const.KGSTOLB / self.fuelWeight)
[52]727
728 def _getThrottle(self, index):
729 """Get the throttle value for the given index."""
730 return int(self.throttles[index] * 16383.0)
[54]731
732 def _updateTimeOffset(self, index, value):
733 """Update the time offset if the value in the tm structure is replaced
734 by the given value."""
735 tm = self._readUTC()
736 tm1 = tm[:index] + (value,) + tm[(index+1):]
737 self._timeOffset += calendar.timegm(tm1) - calendar.timegm(tm)
[52]738
[54]739 def _setThrottle(self, index, value):
740 """Set the throttle value for the given index."""
741 self.throttles[index] = value / 16383.0
742
743 def _setFuelLevel(self, index, value):
744 """Set the fuel level for the fuel tank with the given index."""
745 self.fuelWeights[index] = self.fuelCapacities[index] * float(value) / \
746 65536.0 / 128.0
747
748 def _setFuelCapacity(self, index, value):
749 """Set the capacity of the fuel tank with the given index."""
750 self.fuelCapacities[index] = value * self.fuelWeight * const.LBSTOKG
751
[57]752 def _getTAS(self):
753 """Calculate the true airspeed."""
754 pressure = 101325 * math.pow(1 - 2.25577e-5 * self.altitude *
755 const.FEETTOMETRES,
756 5.25588)
757 temperature = 15 - self.altitude * 6.5 * const.FEETTOMETRES / 1000.0
758 temperature += 273.15 # Celsius -> Kelvin
759 airDensity = pressure / (temperature * 287.05)
[88]760 #print "pressure:", pressure, "temperature:", temperature, "airDensity:", airDensity
[57]761 return self.ias * math.sqrt(1.225 / airDensity)
[54]762
[52]763#------------------------------------------------------------------------------
764
765values = Values()
766
767#------------------------------------------------------------------------------
768
[59]769failOpen = False
770
771opened = False
772
773#------------------------------------------------------------------------------
774
[51]775def open(request):
776 """Open the connection."""
[59]777 global opened
778 if failOpen:
779 raise FSUIPCException(ERR_NOFS)
780 elif opened:
781 raise FSUIPCException(ERR_OPEN)
782 else:
783 time.sleep(0.5)
784 opened = True
785 return True
[51]786
787#------------------------------------------------------------------------------
788
[213]789def prepare_data(pattern, forRead = True, checkOpened = True):
[51]790 """Prepare the given pattern for reading and/or writing."""
[213]791 if not checkOpened or opened:
[59]792 return pattern
793 else:
[213]794 raise FSUIPCException(ERR_NOTOPEN)
[59]795
[51]796#------------------------------------------------------------------------------
797
[213]798def read(data, checkOpened = True):
[51]799 """Read the given data."""
[213]800 if not checkOpened or opened:
[177]801 return [values.read(offset, type) for (offset, type) in data]
[59]802 else:
[213]803 raise FSUIPCException(ERR_NOTOPEN)
[51]804
805#------------------------------------------------------------------------------
806
[213]807def write(data, checkOpened = True):
[54]808 """Write the given data."""
[213]809 if not checkOpened or opened:
[59]810 for (offset, type, value) in data:
[177]811 values.write(offset, value, type)
[59]812 else:
[213]813 raise FSUIPCException(ERR_NOTOPEN)
[54]814
815#------------------------------------------------------------------------------
816
[51]817def close():
818 """Close the connection."""
[59]819 global opened
820 opened = False
[51]821
822#------------------------------------------------------------------------------
[53]823
824PORT=15015
825
826CALL_READ=1
827CALL_WRITE=2
[212]828CALL_SETVERSION=3
829CALL_CLOSE=4
830CALL_FAILOPEN=5
[59]831CALL_QUIT = 99
[53]832
833RESULT_RETURNED=1
834RESULT_EXCEPTION=2
835
836#------------------------------------------------------------------------------
837
838class Server(threading.Thread):
839 """The server thread."""
840 def __init__(self):
841 """Construct the thread."""
842 super(Server, self).__init__()
843 self.daemon = True
844
845 def run(self):
846 """Perform the server's operation."""
847 serverSocket = socket.socket()
848
849 serverSocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
850 serverSocket.bind(("", PORT))
851
852 serverSocket.listen(5)
853
854 while True:
855 (clientSocket, clientAddress) = serverSocket.accept()
856 thread = threading.Thread(target = self._process, args=(clientSocket,))
857 thread.start()
858
859 def _process(self, clientSocket):
860 """Process the commands arriving on the given socket."""
861 socketFile = clientSocket.makefile()
862 try:
863 while True:
864 (length,) = struct.unpack("I", clientSocket.recv(4))
865 data = clientSocket.recv(length)
866 (call, args) = cPickle.loads(data)
867 exception = None
868
869 try:
870 if call==CALL_READ:
[213]871 result = read(args[0], checkOpened = False)
[53]872 elif call==CALL_WRITE:
[213]873 result = write(args[0], checkOpened = False)
[212]874 elif call==CALL_SETVERSION:
875 global fs_version
876 fs_version = args[0]
877 result = None
[59]878 elif call==CALL_CLOSE:
879 global opened
880 opened = False
881 result = None
882 elif call==CALL_FAILOPEN:
883 global failOpen
884 failOpen = args[0]
885 result = None
[53]886 else:
887 break
888 except Exception, e:
889 exception = e
890
891 if exception is None:
892 data = cPickle.dumps((RESULT_RETURNED, result))
893 else:
[213]894 data = cPickle.dumps((RESULT_EXCEPTION, str(exception)))
[53]895 clientSocket.send(struct.pack("I", len(data)) + data)
896 except Exception, e:
897 print >> sys.stderr, "pyuipc_sim.Server._process: failed with exception:", str(e)
898 finally:
899 try:
900 socketFile.close()
901 except:
902 pass
903 clientSocket.close()
904
905#------------------------------------------------------------------------------
906
907class Client(object):
908 """Client to the server."""
909 def __init__(self, serverHost):
910 """Construct the client and connect to the given server
911 host."""
912 self._socket = socket.socket()
913 self._socket.connect((serverHost, PORT))
914
915 self._socketFile = self._socket.makefile()
916
917 def read(self, data):
918 """Read the given data."""
[54]919 return self._call(CALL_READ, data)
920
921 def write(self, data):
922 """Write the given data."""
923 return self._call(CALL_WRITE, data)
924
[212]925 def setVersion(self, version):
926 """Set the FS version to emulate."""
927 return self._call(CALL_SETVERSION, int(version))
928
[59]929 def close(self):
930 """Close the connection currently opened in the simulator."""
931 return self._call(CALL_CLOSE, None)
932
933 def failOpen(self, really):
934 """Enable/disable open failure in the simulator."""
935 return self._call(CALL_FAILOPEN, really)
936
937 def quit(self):
938 """Quit from the simulator."""
939 data = cPickle.dumps((CALL_QUIT, None))
940 self._socket.send(struct.pack("I", len(data)) + data)
941
[54]942 def _call(self, command, data):
943 """Perform a call with the given command and data."""
944 data = cPickle.dumps((command, [data]))
[53]945 self._socket.send(struct.pack("I", len(data)) + data)
946 (length,) = struct.unpack("I", self._socket.recv(4))
947 data = self._socket.recv(length)
948 (resultCode, result) = cPickle.loads(data)
949 if resultCode==RESULT_RETURNED:
950 return result
951 else:
[213]952 raise Exception(result)
[53]953
954#------------------------------------------------------------------------------
955#------------------------------------------------------------------------------
956
957# FIXME: implement proper completion and history
[55]958class CLI(cmd.Cmd):
[53]959 """The command-line interpreter."""
[55]960 @staticmethod
961 def str2bool(s):
962 """Convert the given string to a PyUIPC boolean value (i.e. 0 or 1)."""
963 return 1 if s in ["yes", "true", "on"] else 0
964
965 @staticmethod
966 def bool2str(value):
967 """Convert the PyUIPC boolean value (i.e. 0 or 1) into a string."""
968 return "no" if value==0 else "yes"
969
970 @staticmethod
971 def degree2pyuipc(degree):
972 """Convert the given degree (as a string) into a PyUIPC value."""
973 return int(float(degree) * 65536.0 * 65536.0 / 360.0)
974
975 @staticmethod
976 def pyuipc2degree(value):
977 """Convert the given PyUIPC value into a degree."""
978 return valie * 360.0 / 65536.0 / 65536.0
[56]979
980 @staticmethod
981 def fuelLevel2pyuipc(level):
982 """Convert the given percentage value (as a string) into a PyUIPC value."""
983 return int(float(level) * 128.0 * 65536.0 / 100.0)
984
985 @staticmethod
986 def pyuipc2fuelLevel(value):
987 """Convert the PyUIPC value into a percentage value."""
988 return value * 100.0 / 128.0 / 65536.0
989
990 @staticmethod
991 def fuelCapacity2pyuipc(capacity):
992 """Convert the given capacity value (as a string) into a PyUIPC value."""
993 return int(capacity)
994
995 @staticmethod
996 def pyuipc2fuelCapacity(value):
997 """Convert the given capacity value into a PyUIPC value."""
998 return value
999
1000 @staticmethod
1001 def throttle2pyuipc(throttle):
1002 """Convert the given throttle value (as a string) into a PyUIPC value."""
1003 return int(float(throttle) * 16384.0 / 100.0)
1004
1005 @staticmethod
1006 def pyuipc2throttle(value):
1007 """Convert the given PyUIPC value into a throttle value."""
1008 return value * 100.0 / 16384.0
[55]1009
1010 def __init__(self):
[53]1011 """Construct the CLI."""
[55]1012 cmd.Cmd.__init__(self)
[53]1013
[55]1014 self.use_rawinput = True
[53]1015 self.intro = "\nPyUIPC simulator command prompt\n"
1016 self.prompt = "PyUIPC> "
1017
1018 self.daemon = True
1019
[163]1020 host = sys.argv[1] if len(sys.argv)>1 else "localhost"
1021 self._client = Client(host)
[55]1022
1023 self._valueHandlers = {}
1024 self._valueHandlers["year"] = (0x0240, "H", lambda value: value,
1025 lambda word: int(word))
1026 self._valueHandlers["yday"] = (0x023e, "H", lambda value: value,
1027 lambda word: int(word))
1028 self._valueHandlers["hour"] = (0x023b, "b", lambda value: value,
1029 lambda word: int(word))
1030 self._valueHandlers["min"] = (0x023c, "b", lambda value: value,
1031 lambda word: int(word))
1032 self._valueHandlers["sec"] = (0x023a, "b", lambda value: value,
1033 lambda word: int(word))
1034 self._valueHandlers["acftName"] = (0x3d00, -256, lambda value: value,
1035 lambda word: word)
1036 self._valueHandlers["airPath"] = (0x3c00, -256, lambda value: value,
1037 lambda word: word)
[89]1038 self._valueHandlers["latitude"] = (0x0560, "l",
1039 lambda value: value * 90.0 /
1040 10001750.0 / 65536.0 / 65536.0,
1041 lambda word: long(float(word) *
1042 10001750.0 *
1043 65536.0 * 65536.0 / 90.0))
1044 self._valueHandlers["longitude"] = (0x0568, "l",
1045 lambda value: value * 360.0 /
1046 65536.0 / 65536.0 / 65536.0 / 65536.0,
1047 lambda word: long(float(word) *
1048 65536.0 * 65536.0 *
1049 65536.0 * 65536.0 /
1050 360.0))
[55]1051 self._valueHandlers["paused"] = (0x0264, "H", CLI.bool2str, CLI.str2bool)
1052 self._valueHandlers["frozen"] = (0x3364, "H", CLI.bool2str, CLI.str2bool)
1053 self._valueHandlers["replay"] = (0x0628, "d", CLI.bool2str, CLI.str2bool)
1054 self._valueHandlers["slew"] = (0x05dc, "H", CLI.bool2str, CLI.str2bool)
1055 self._valueHandlers["overspeed"] = (0x036d, "b", CLI.bool2str, CLI.str2bool)
1056 self._valueHandlers["stalled"] = (0x036c, "b", CLI.bool2str, CLI.str2bool)
1057 self._valueHandlers["onTheGround"] = (0x0366, "H", CLI.bool2str, CLI.str2bool)
1058 self._valueHandlers["zfw"] = (0x3bfc, "d",
1059 lambda value: value * const.LBSTOKG / 256.0,
1060 lambda word: int(float(word) * 256.0 *
1061 const.KGSTOLB))
1062 self._valueHandlers["grossWeight"] = (0x30c0, "f",
1063 lambda value: value * const.LBSTOKG,
1064 lambda word: None)
1065 self._valueHandlers["heading"] = (0x0580, "d",
1066 CLI.pyuipc2degree, CLI.degree2pyuipc)
1067 self._valueHandlers["pitch"] = (0x0578, "d",
1068 CLI.pyuipc2degree, CLI.degree2pyuipc)
1069 self._valueHandlers["bank"] = (0x057c, "d",
1070 CLI.pyuipc2degree, CLI.degree2pyuipc)
1071 self._valueHandlers["ias"] = (0x02bc, "d",
1072 lambda value: value / 128.0,
1073 lambda word: int(float(word) * 128.0))
1074 self._valueHandlers["mach"] = (0x11c6, "H",
1075 lambda value: value / 20480.0,
1076 lambda word: int(float(word) * 20480.0))
1077 self._valueHandlers["gs"] = (0x02b4, "d",
1078 lambda value: value * 3600.0 / 65536.0 / 1852.0,
1079 lambda word: int(float(word) * 65536.0 *
1080 1852.0 / 3600))
[57]1081 self._valueHandlers["tas"] = (0x02b8, "d",
1082 lambda value: value / 128.0,
1083 lambda word: None)
[55]1084 self._valueHandlers["vs"] = (0x02c8, "d",
1085 lambda value: value * 60 /
1086 const.FEETTOMETRES / 256.0,
1087 lambda word: int(float(word) *
1088 const.FEETTOMETRES *
1089 256.0 / 60.0))
[85]1090 self._valueHandlers["tdRate"] = (0x030c, "d",
1091 lambda value: value * 60 /
1092 const.FEETTOMETRES / 256.0,
1093 lambda word: int(float(word) *
1094 const.FEETTOMETRES *
1095 256.0 / 60.0))
[55]1096 self._valueHandlers["radioAltitude"] = (0x31e4, "d",
1097 lambda value: value /
1098 const.FEETTOMETRES /
1099 65536.0,
1100 lambda word: int(float(word) *
1101 const.FEETTOMETRES *
1102 65536.0))
1103 self._valueHandlers["altitude"] = (0x0570, "l",
1104 lambda value: value /
1105 const.FEETTOMETRES / 65536.0 /
1106 65536.0,
1107 lambda word: long(float(word) *
1108 const.FEETTOMETRES *
1109 65536.0 * 65536.0))
1110 self._valueHandlers["gLoad"] = (0x11ba, "H",
1111 lambda value: value / 625.0,
1112 lambda word: int(float(word) * 625.0))
1113
1114 self._valueHandlers["flapsControl"] = (0x0bdc, "d",
1115 lambda value: value * 100.0 / 16383.0,
1116 lambda word: int(float(word) *
1117 16383.0 / 100.0))
1118 self._valueHandlers["flaps"] = (0x0be0, "d",
1119 lambda value: value * 100.0 / 16383.0,
1120 lambda word: int(float(word) *
1121 16383.0 / 100.0))
1122 self._valueHandlers["lights"] = (0x0d0c, "H",
[83]1123 lambda value: value,
1124 lambda word: int(word))
[55]1125 self._valueHandlers["pitot"] = (0x029c, "b", CLI.bool2str, CLI.str2bool)
1126 self._valueHandlers["parking"] = (0x0bc8, "H", CLI.bool2str, CLI.str2bool)
[209]1127 self._valueHandlers["gearControl"] = (0x0be8, "d",
1128 lambda value: value * 100.0 / 16383.0,
1129 lambda word: int(float(word) *
1130 16383.0 / 100.0))
[55]1131 self._valueHandlers["noseGear"] = (0x0bec, "d",
1132 lambda value: value * 100.0 / 16383.0,
1133 lambda word: int(float(word) *
1134 16383.0 / 100.0))
1135 self._valueHandlers["spoilersArmed"] = (0x0bcc, "d",
1136 CLI.bool2str, CLI.str2bool)
1137 self._valueHandlers["spoilers"] = (0x0bd0, "d",
1138 lambda value: value,
1139 lambda word: int(word))
1140 self._valueHandlers["qnh"] = (0x0330, "H",
1141 lambda value: value / 16.0,
1142 lambda word: int(float(word)*16.0))
1143 self._valueHandlers["nav1"] = (0x0350, "H",
1144 Values._writeFrequency,
1145 lambda word: Values._readFrequency(float(word)))
1146 self._valueHandlers["nav2"] = (0x0352, "H",
1147 Values._writeFrequency,
1148 lambda word: Values._readFrequency(float(word)))
1149 self._valueHandlers["squawk"] = (0x0354, "H",
1150 Values._writeBCD,
1151 lambda word: Values._readBCD(int(word)))
1152 self._valueHandlers["windSpeed"] = (0x0e90, "H",
1153 lambda value: value,
1154 lambda word: int(word))
1155 self._valueHandlers["windDirection"] = (0x0e92, "H",
1156 lambda value: value * 360.0 / 65536.0,
1157 lambda word: int(int(word) *
1158 65536.0 / 360.0))
[56]1159 self._valueHandlers["fuelWeight"] = (0x0af4, "H",
1160 lambda value: value / 256.0,
1161 lambda word: int(float(word)*256.0))
1162
1163
1164 self._valueHandlers["centreLevel"] = (0x0b74, "d", CLI.pyuipc2fuelLevel,
1165 CLI.fuelLevel2pyuipc)
1166 self._valueHandlers["centreCapacity"] = (0x0b78, "d",
1167 CLI.pyuipc2fuelCapacity,
1168 CLI.fuelCapacity2pyuipc)
1169 self._valueHandlers["leftMainLevel"] = (0x0b7c, "d", CLI.pyuipc2fuelLevel,
1170 CLI.fuelLevel2pyuipc)
1171 self._valueHandlers["leftMainCapacity"] = (0x0b80, "d",
1172 CLI.pyuipc2fuelCapacity,
1173 CLI.fuelCapacity2pyuipc)
1174 self._valueHandlers["leftAuxLevel"] = (0x0b84, "d", CLI.pyuipc2fuelLevel,
1175 CLI.fuelLevel2pyuipc)
1176 self._valueHandlers["leftAuxCapacity"] = (0x0b88, "d",
1177 CLI.pyuipc2fuelCapacity,
1178 CLI.fuelCapacity2pyuipc)
1179 self._valueHandlers["leftTipLevel"] = (0x0b8c, "d", CLI.pyuipc2fuelLevel,
1180 CLI.fuelLevel2pyuipc)
1181 self._valueHandlers["leftTipCapacity"] = (0x0b90, "d",
1182 CLI.pyuipc2fuelCapacity,
1183 CLI.fuelCapacity2pyuipc)
1184 self._valueHandlers["rightMainLevel"] = (0x0b94, "d", CLI.pyuipc2fuelLevel,
1185 CLI.fuelLevel2pyuipc)
1186 self._valueHandlers["rightMainCapacity"] = (0x0b98, "d",
1187 CLI.pyuipc2fuelCapacity,
1188 CLI.fuelCapacity2pyuipc)
1189 self._valueHandlers["rightAuxLevel"] = (0x0b9c, "d", CLI.pyuipc2fuelLevel,
1190 CLI.fuelLevel2pyuipc)
1191 self._valueHandlers["rightAuxCapacity"] = (0x0ba0, "d",
1192 CLI.pyuipc2fuelCapacity,
1193 CLI.fuelCapacity2pyuipc)
1194 self._valueHandlers["rightTipLevel"] = (0x0ba4, "d", CLI.pyuipc2fuelLevel,
1195 CLI.fuelLevel2pyuipc)
1196 self._valueHandlers["rightTipCapacity"] = (0x0ba8, "d",
1197 CLI.pyuipc2fuelCapacity,
1198 CLI.fuelCapacity2pyuipc)
1199 self._valueHandlers["centre2Level"] = (0x1244, "d", CLI.pyuipc2fuelLevel,
1200 CLI.fuelLevel2pyuipc)
1201 self._valueHandlers["centre2Capacity"] = (0x1248, "d",
1202 CLI.pyuipc2fuelCapacity,
1203 CLI.fuelCapacity2pyuipc)
1204 self._valueHandlers["external1Level"] = (0x1254, "d", CLI.pyuipc2fuelLevel,
1205 CLI.fuelLevel2pyuipc)
1206 self._valueHandlers["external1Capacity"] = (0x1258, "d",
1207 CLI.pyuipc2fuelCapacity,
1208 CLI.fuelCapacity2pyuipc)
1209 self._valueHandlers["external2Level"] = (0x125c, "d", CLI.pyuipc2fuelLevel,
1210 CLI.fuelLevel2pyuipc)
1211 self._valueHandlers["external2Capacity"] = (0x1260, "d",
1212 CLI.pyuipc2fuelCapacity,
1213 CLI.fuelCapacity2pyuipc)
1214
1215 self._valueHandlers["n1_1"] = (0x2000, "f", lambda value: value,
1216 lambda word: float(word))
1217 self._valueHandlers["n1_2"] = (0x2100, "f", lambda value: value,
1218 lambda word: float(word))
1219 self._valueHandlers["n1_3"] = (0x2200, "f", lambda value: value,
1220 lambda word: float(word))
1221
1222 self._valueHandlers["throttle_1"] = (0x088c, "H",
1223 CLI.pyuipc2throttle,
1224 CLI.throttle2pyuipc)
1225 self._valueHandlers["throttle_2"] = (0x0924, "H",
1226 CLI.pyuipc2throttle,
1227 CLI.throttle2pyuipc)
1228 self._valueHandlers["throttle_3"] = (0x09bc, "H",
1229 CLI.pyuipc2throttle,
1230 CLI.throttle2pyuipc)
[55]1231
[85]1232 self._valueHandlers["visibility"] = (0x0e8a, "H",
1233 lambda value: value*1609.344/100.0,
1234 lambda word: int(float(word)*
1235 100.0/1609.344))
1236
[117]1237 self._valueHandlers["payloadCount"] = (0x13fc, "d",
1238 lambda value: value,
1239 lambda word: int(word))
1240 for i in range(0, 61):
1241 self._valueHandlers["payload%d" % (i,)] = (0x1400 + i * 48, "f",
1242 lambda value:
1243 value * const.LBSTOKG,
1244 lambda word:
1245 float(word)*const.KGSTOLB)
[133]1246 self._valueHandlers["textScrolling"] = (0x1274, "h",
1247 CLI.bool2str, CLI.str2bool)
[117]1248
[133]1249 self._valueHandlers["messageDuration"] = (0x32fa, "h",
1250 lambda value: value,
1251 lambda word: int(word))
1252 self._valueHandlers["message"] = (0x3380, -128,
1253 lambda value: value,
1254 lambda word: word)
[177]1255
1256 for i in range(0, Values.HOTKEY_SIZE):
1257 self._valueHandlers["hotkey%d" % (i,)] = (0x3210 + i*4, "u",
1258 lambda value: "0x%08x" % (value,),
1259 lambda word: long(word, 16))
[212]1260
[243]1261 self._valueHandlers["cog"] = (0x2ef8, "f", lambda value: value,
1262 lambda word: float(word))
1263
[212]1264 self._valueHandlers["pmdg_737ng_switches"] = (0x6202, "b",
1265 lambda value: value,
1266 lambda word: int(word))
1267
1268 self._valueHandlers["pmdg_737ngx_lts_positionsw"] = (0x6500, "b",
1269 lambda value: value,
1270 lambda word: int(word))
1271
[55]1272 def default(self, line):
1273 """Handle unhandle commands."""
1274 if line=="EOF":
1275 print
[59]1276 return self.do_quit("")
[55]1277 else:
1278 return super(CLI, self).default(line)
1279
1280 def do_get(self, args):
1281 """Handle the get command."""
1282 names = args.split()
1283 data = []
1284 for name in names:
1285 if name not in self._valueHandlers:
1286 print >> sys.stderr, "Unknown variable: " + name
1287 return False
1288 valueHandler = self._valueHandlers[name]
1289 data.append((valueHandler[0], valueHandler[1]))
1290
[53]1291 try:
[55]1292 result = self._client.read(data)
1293 for i in range(0, len(result)):
1294 name = names[i]
1295 valueHandler = self._valueHandlers[name]
1296 print name + "=" + str(valueHandler[2](result[i]))
[53]1297 except Exception, e:
[55]1298 print >> sys.stderr, "Failed to read data: " + str(e)
1299
1300 return False
1301
1302 def help_get(self):
1303 """Print help for the get command."""
1304 print "get <variable> [<variable>...]"
1305
1306 def complete_get(self, text, line, begidx, endidx):
1307 """Try to complete the get command."""
1308 return [key for key in self._valueHandlers if key.startswith(text)]
[53]1309
1310 def do_set(self, args):
1311 """Handle the set command."""
[55]1312 arguments = args.split()
1313 names = []
1314 data = []
1315 for argument in arguments:
1316 words = argument.split("=")
1317 if len(words)!=2:
1318 print >> sys.stderr, "Invalid argument: " + argument
1319 return False
1320
1321 (name, value) = words
1322 if name not in self._valueHandlers:
1323 print >> sys.stderr, "Unknown variable: " + name
1324 return False
1325
1326 valueHandler = self._valueHandlers[name]
1327 try:
1328 value = valueHandler[3](value)
1329 data.append((valueHandler[0], valueHandler[1], value))
1330 except Exception, e:
1331 print >> sys.stderr, "Invalid value '%s' for variable %s: %s" % \
1332 (value, name, str(e))
1333 return False
1334
1335 try:
1336 self._client.write(data)
1337 print "Data written"
1338 except Exception, e:
1339 print >> sys.stderr, "Failed to write data: " + str(e)
1340
[53]1341 return False
1342
1343 def help_set(self):
1344 """Print help for the set command."""
[55]1345 print "set <variable>=<value> [<variable>=<value>...]"
1346
1347 def complete_set(self, text, line, begidx, endidx):
1348 """Try to complete the set command."""
1349 if not text and begidx>0 and line[begidx-1]=="=":
1350 return []
1351 else:
1352 return [key + "=" for key in self._valueHandlers if key.startswith(text)]
[53]1353
[212]1354 def do_setversion(self, args):
1355 """Set the version number to simulate"""
1356 try:
1357 value = int(args)
1358 self._client.setVersion(value)
1359 print "Emulating version %d" % (value,)
1360 except Exception, e:
1361 print >> sys.stderr, "Failed to set the version: " + str(e)
1362
1363 def help_setversion(self, usage = False):
1364 """Help for the setversion command"""
1365 if usage: print "Usage:",
1366 print "setversion <number>"
1367
[59]1368 def do_close(self, args):
1369 """Close an existing connection so that FS will fail."""
1370 try:
1371 self._client.close()
1372 print "Connection closed"
1373 except Exception, e:
[212]1374 print >> sys.stderr, "Failed to close the connection: " + str(e)
[59]1375
1376 def do_failopen(self, args):
1377 """Enable/disable the failing of opens."""
1378 try:
1379 value = self.str2bool(args)
1380 self._client.failOpen(value)
1381 print "Opening will%s fail" % ("" if value else " not",)
1382 except Exception, e:
1383 print >> sys.stderr, "Failed to set open failure: " + str(e)
1384
1385 def help_failopen(self, usage = False):
[212]1386 """Help for the failopen command"""
[59]1387 if usage: print "Usage:",
1388 print "failopen yes|no"
1389
1390 def complete_failopen(self, text, line, begidx, endidx):
1391 if text:
1392 if "yes".startswith(text): return ["yes"]
1393 elif "no".startswith(text): return ["no"]
1394 else: return []
1395 else:
1396 return ["yes", "no"]
1397
[53]1398 def do_quit(self, args):
1399 """Handle the quit command."""
[59]1400 self._client.quit()
[53]1401 return True
1402
1403#------------------------------------------------------------------------------
1404
1405if __name__ == "__main__":
[55]1406 CLI().cmdloop()
[53]1407else:
1408 server = Server()
1409 server.start()
1410
1411#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.