source: src/mlx/pyuipc_sim.py@ 209:3172e5cab96e

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

The position of the gear control lever is logged

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