source: src/mlx/pyuipc_sim.py@ 186:a808eda90d15

Last change on this file since 186:a808eda90d15 was 177:356d15d88060, checked in by István Váradi <ivaradi@…>, 13 years ago

The PyUIPC simulator can now handle the hotkeys

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