source: src/mlx/pyuipc_sim.py@ 137:f7489252b322

Last change on this file since 137:f7489252b322 was 133:dcbe33497899, checked in by István Váradi <ivaradi@…>, 13 years ago

The general message sending works and the most important messages are sent

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