source: src/mlx/pyuipc_sim.py@ 211:fbd8dad0b6be

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

Reorganized the lights loggin and checks a bit

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