source: src/mlx/pyuipc_sim.py@ 166:e4ba22b7a13b

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

Added support for specifying the host name

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 host = sys.argv[1] if len(sys.argv)>1 else "localhost"
935 self._client = Client(host)
936
937 self._valueHandlers = {}
938 self._valueHandlers["year"] = (0x0240, "H", lambda value: value,
939 lambda word: int(word))
940 self._valueHandlers["yday"] = (0x023e, "H", lambda value: value,
941 lambda word: int(word))
942 self._valueHandlers["hour"] = (0x023b, "b", lambda value: value,
943 lambda word: int(word))
944 self._valueHandlers["min"] = (0x023c, "b", lambda value: value,
945 lambda word: int(word))
946 self._valueHandlers["sec"] = (0x023a, "b", lambda value: value,
947 lambda word: int(word))
948 self._valueHandlers["acftName"] = (0x3d00, -256, lambda value: value,
949 lambda word: word)
950 self._valueHandlers["airPath"] = (0x3c00, -256, lambda value: value,
951 lambda word: word)
952 self._valueHandlers["latitude"] = (0x0560, "l",
953 lambda value: value * 90.0 /
954 10001750.0 / 65536.0 / 65536.0,
955 lambda word: long(float(word) *
956 10001750.0 *
957 65536.0 * 65536.0 / 90.0))
958 self._valueHandlers["longitude"] = (0x0568, "l",
959 lambda value: value * 360.0 /
960 65536.0 / 65536.0 / 65536.0 / 65536.0,
961 lambda word: long(float(word) *
962 65536.0 * 65536.0 *
963 65536.0 * 65536.0 /
964 360.0))
965 self._valueHandlers["paused"] = (0x0264, "H", CLI.bool2str, CLI.str2bool)
966 self._valueHandlers["frozen"] = (0x3364, "H", CLI.bool2str, CLI.str2bool)
967 self._valueHandlers["replay"] = (0x0628, "d", CLI.bool2str, CLI.str2bool)
968 self._valueHandlers["slew"] = (0x05dc, "H", CLI.bool2str, CLI.str2bool)
969 self._valueHandlers["overspeed"] = (0x036d, "b", CLI.bool2str, CLI.str2bool)
970 self._valueHandlers["stalled"] = (0x036c, "b", CLI.bool2str, CLI.str2bool)
971 self._valueHandlers["onTheGround"] = (0x0366, "H", CLI.bool2str, CLI.str2bool)
972 self._valueHandlers["zfw"] = (0x3bfc, "d",
973 lambda value: value * const.LBSTOKG / 256.0,
974 lambda word: int(float(word) * 256.0 *
975 const.KGSTOLB))
976 self._valueHandlers["grossWeight"] = (0x30c0, "f",
977 lambda value: value * const.LBSTOKG,
978 lambda word: None)
979 self._valueHandlers["heading"] = (0x0580, "d",
980 CLI.pyuipc2degree, CLI.degree2pyuipc)
981 self._valueHandlers["pitch"] = (0x0578, "d",
982 CLI.pyuipc2degree, CLI.degree2pyuipc)
983 self._valueHandlers["bank"] = (0x057c, "d",
984 CLI.pyuipc2degree, CLI.degree2pyuipc)
985 self._valueHandlers["ias"] = (0x02bc, "d",
986 lambda value: value / 128.0,
987 lambda word: int(float(word) * 128.0))
988 self._valueHandlers["mach"] = (0x11c6, "H",
989 lambda value: value / 20480.0,
990 lambda word: int(float(word) * 20480.0))
991 self._valueHandlers["gs"] = (0x02b4, "d",
992 lambda value: value * 3600.0 / 65536.0 / 1852.0,
993 lambda word: int(float(word) * 65536.0 *
994 1852.0 / 3600))
995 self._valueHandlers["tas"] = (0x02b8, "d",
996 lambda value: value / 128.0,
997 lambda word: None)
998 self._valueHandlers["vs"] = (0x02c8, "d",
999 lambda value: value * 60 /
1000 const.FEETTOMETRES / 256.0,
1001 lambda word: int(float(word) *
1002 const.FEETTOMETRES *
1003 256.0 / 60.0))
1004 self._valueHandlers["tdRate"] = (0x030c, "d",
1005 lambda value: value * 60 /
1006 const.FEETTOMETRES / 256.0,
1007 lambda word: int(float(word) *
1008 const.FEETTOMETRES *
1009 256.0 / 60.0))
1010 self._valueHandlers["radioAltitude"] = (0x31e4, "d",
1011 lambda value: value /
1012 const.FEETTOMETRES /
1013 65536.0,
1014 lambda word: int(float(word) *
1015 const.FEETTOMETRES *
1016 65536.0))
1017 self._valueHandlers["altitude"] = (0x0570, "l",
1018 lambda value: value /
1019 const.FEETTOMETRES / 65536.0 /
1020 65536.0,
1021 lambda word: long(float(word) *
1022 const.FEETTOMETRES *
1023 65536.0 * 65536.0))
1024 self._valueHandlers["gLoad"] = (0x11ba, "H",
1025 lambda value: value / 625.0,
1026 lambda word: int(float(word) * 625.0))
1027
1028 self._valueHandlers["flapsControl"] = (0x0bdc, "d",
1029 lambda value: value * 100.0 / 16383.0,
1030 lambda word: int(float(word) *
1031 16383.0 / 100.0))
1032 self._valueHandlers["flaps"] = (0x0be0, "d",
1033 lambda value: value * 100.0 / 16383.0,
1034 lambda word: int(float(word) *
1035 16383.0 / 100.0))
1036 self._valueHandlers["lights"] = (0x0d0c, "H",
1037 lambda value: value,
1038 lambda word: int(word))
1039 self._valueHandlers["pitot"] = (0x029c, "b", CLI.bool2str, CLI.str2bool)
1040 self._valueHandlers["parking"] = (0x0bc8, "H", CLI.bool2str, CLI.str2bool)
1041 self._valueHandlers["noseGear"] = (0x0bec, "d",
1042 lambda value: value * 100.0 / 16383.0,
1043 lambda word: int(float(word) *
1044 16383.0 / 100.0))
1045 self._valueHandlers["spoilersArmed"] = (0x0bcc, "d",
1046 CLI.bool2str, CLI.str2bool)
1047 self._valueHandlers["spoilers"] = (0x0bd0, "d",
1048 lambda value: value,
1049 lambda word: int(word))
1050 self._valueHandlers["qnh"] = (0x0330, "H",
1051 lambda value: value / 16.0,
1052 lambda word: int(float(word)*16.0))
1053 self._valueHandlers["nav1"] = (0x0350, "H",
1054 Values._writeFrequency,
1055 lambda word: Values._readFrequency(float(word)))
1056 self._valueHandlers["nav2"] = (0x0352, "H",
1057 Values._writeFrequency,
1058 lambda word: Values._readFrequency(float(word)))
1059 self._valueHandlers["squawk"] = (0x0354, "H",
1060 Values._writeBCD,
1061 lambda word: Values._readBCD(int(word)))
1062 self._valueHandlers["windSpeed"] = (0x0e90, "H",
1063 lambda value: value,
1064 lambda word: int(word))
1065 self._valueHandlers["windDirection"] = (0x0e92, "H",
1066 lambda value: value * 360.0 / 65536.0,
1067 lambda word: int(int(word) *
1068 65536.0 / 360.0))
1069 self._valueHandlers["fuelWeight"] = (0x0af4, "H",
1070 lambda value: value / 256.0,
1071 lambda word: int(float(word)*256.0))
1072
1073
1074 self._valueHandlers["centreLevel"] = (0x0b74, "d", CLI.pyuipc2fuelLevel,
1075 CLI.fuelLevel2pyuipc)
1076 self._valueHandlers["centreCapacity"] = (0x0b78, "d",
1077 CLI.pyuipc2fuelCapacity,
1078 CLI.fuelCapacity2pyuipc)
1079 self._valueHandlers["leftMainLevel"] = (0x0b7c, "d", CLI.pyuipc2fuelLevel,
1080 CLI.fuelLevel2pyuipc)
1081 self._valueHandlers["leftMainCapacity"] = (0x0b80, "d",
1082 CLI.pyuipc2fuelCapacity,
1083 CLI.fuelCapacity2pyuipc)
1084 self._valueHandlers["leftAuxLevel"] = (0x0b84, "d", CLI.pyuipc2fuelLevel,
1085 CLI.fuelLevel2pyuipc)
1086 self._valueHandlers["leftAuxCapacity"] = (0x0b88, "d",
1087 CLI.pyuipc2fuelCapacity,
1088 CLI.fuelCapacity2pyuipc)
1089 self._valueHandlers["leftTipLevel"] = (0x0b8c, "d", CLI.pyuipc2fuelLevel,
1090 CLI.fuelLevel2pyuipc)
1091 self._valueHandlers["leftTipCapacity"] = (0x0b90, "d",
1092 CLI.pyuipc2fuelCapacity,
1093 CLI.fuelCapacity2pyuipc)
1094 self._valueHandlers["rightMainLevel"] = (0x0b94, "d", CLI.pyuipc2fuelLevel,
1095 CLI.fuelLevel2pyuipc)
1096 self._valueHandlers["rightMainCapacity"] = (0x0b98, "d",
1097 CLI.pyuipc2fuelCapacity,
1098 CLI.fuelCapacity2pyuipc)
1099 self._valueHandlers["rightAuxLevel"] = (0x0b9c, "d", CLI.pyuipc2fuelLevel,
1100 CLI.fuelLevel2pyuipc)
1101 self._valueHandlers["rightAuxCapacity"] = (0x0ba0, "d",
1102 CLI.pyuipc2fuelCapacity,
1103 CLI.fuelCapacity2pyuipc)
1104 self._valueHandlers["rightTipLevel"] = (0x0ba4, "d", CLI.pyuipc2fuelLevel,
1105 CLI.fuelLevel2pyuipc)
1106 self._valueHandlers["rightTipCapacity"] = (0x0ba8, "d",
1107 CLI.pyuipc2fuelCapacity,
1108 CLI.fuelCapacity2pyuipc)
1109 self._valueHandlers["centre2Level"] = (0x1244, "d", CLI.pyuipc2fuelLevel,
1110 CLI.fuelLevel2pyuipc)
1111 self._valueHandlers["centre2Capacity"] = (0x1248, "d",
1112 CLI.pyuipc2fuelCapacity,
1113 CLI.fuelCapacity2pyuipc)
1114 self._valueHandlers["external1Level"] = (0x1254, "d", CLI.pyuipc2fuelLevel,
1115 CLI.fuelLevel2pyuipc)
1116 self._valueHandlers["external1Capacity"] = (0x1258, "d",
1117 CLI.pyuipc2fuelCapacity,
1118 CLI.fuelCapacity2pyuipc)
1119 self._valueHandlers["external2Level"] = (0x125c, "d", CLI.pyuipc2fuelLevel,
1120 CLI.fuelLevel2pyuipc)
1121 self._valueHandlers["external2Capacity"] = (0x1260, "d",
1122 CLI.pyuipc2fuelCapacity,
1123 CLI.fuelCapacity2pyuipc)
1124
1125 self._valueHandlers["n1_1"] = (0x2000, "f", lambda value: value,
1126 lambda word: float(word))
1127 self._valueHandlers["n1_2"] = (0x2100, "f", lambda value: value,
1128 lambda word: float(word))
1129 self._valueHandlers["n1_3"] = (0x2200, "f", lambda value: value,
1130 lambda word: float(word))
1131
1132 self._valueHandlers["throttle_1"] = (0x088c, "H",
1133 CLI.pyuipc2throttle,
1134 CLI.throttle2pyuipc)
1135 self._valueHandlers["throttle_2"] = (0x0924, "H",
1136 CLI.pyuipc2throttle,
1137 CLI.throttle2pyuipc)
1138 self._valueHandlers["throttle_3"] = (0x09bc, "H",
1139 CLI.pyuipc2throttle,
1140 CLI.throttle2pyuipc)
1141
1142 self._valueHandlers["visibility"] = (0x0e8a, "H",
1143 lambda value: value*1609.344/100.0,
1144 lambda word: int(float(word)*
1145 100.0/1609.344))
1146
1147 self._valueHandlers["payloadCount"] = (0x13fc, "d",
1148 lambda value: value,
1149 lambda word: int(word))
1150 for i in range(0, 61):
1151 self._valueHandlers["payload%d" % (i,)] = (0x1400 + i * 48, "f",
1152 lambda value:
1153 value * const.LBSTOKG,
1154 lambda word:
1155 float(word)*const.KGSTOLB)
1156 self._valueHandlers["textScrolling"] = (0x1274, "h",
1157 CLI.bool2str, CLI.str2bool)
1158
1159 self._valueHandlers["messageDuration"] = (0x32fa, "h",
1160 lambda value: value,
1161 lambda word: int(word))
1162 self._valueHandlers["message"] = (0x3380, -128,
1163 lambda value: value,
1164 lambda word: word)
1165 def default(self, line):
1166 """Handle unhandle commands."""
1167 if line=="EOF":
1168 print
1169 return self.do_quit("")
1170 else:
1171 return super(CLI, self).default(line)
1172
1173 def do_get(self, args):
1174 """Handle the get command."""
1175 names = args.split()
1176 data = []
1177 for name in names:
1178 if name not in self._valueHandlers:
1179 print >> sys.stderr, "Unknown variable: " + name
1180 return False
1181 valueHandler = self._valueHandlers[name]
1182 data.append((valueHandler[0], valueHandler[1]))
1183
1184 try:
1185 result = self._client.read(data)
1186 for i in range(0, len(result)):
1187 name = names[i]
1188 valueHandler = self._valueHandlers[name]
1189 print name + "=" + str(valueHandler[2](result[i]))
1190 except Exception, e:
1191 print >> sys.stderr, "Failed to read data: " + str(e)
1192
1193 return False
1194
1195 def help_get(self):
1196 """Print help for the get command."""
1197 print "get <variable> [<variable>...]"
1198
1199 def complete_get(self, text, line, begidx, endidx):
1200 """Try to complete the get command."""
1201 return [key for key in self._valueHandlers if key.startswith(text)]
1202
1203 def do_set(self, args):
1204 """Handle the set command."""
1205 arguments = args.split()
1206 names = []
1207 data = []
1208 for argument in arguments:
1209 words = argument.split("=")
1210 if len(words)!=2:
1211 print >> sys.stderr, "Invalid argument: " + argument
1212 return False
1213
1214 (name, value) = words
1215 if name not in self._valueHandlers:
1216 print >> sys.stderr, "Unknown variable: " + name
1217 return False
1218
1219 valueHandler = self._valueHandlers[name]
1220 try:
1221 value = valueHandler[3](value)
1222 data.append((valueHandler[0], valueHandler[1], value))
1223 except Exception, e:
1224 print >> sys.stderr, "Invalid value '%s' for variable %s: %s" % \
1225 (value, name, str(e))
1226 return False
1227
1228 try:
1229 self._client.write(data)
1230 print "Data written"
1231 except Exception, e:
1232 print >> sys.stderr, "Failed to write data: " + str(e)
1233
1234 return False
1235
1236 def help_set(self):
1237 """Print help for the set command."""
1238 print "set <variable>=<value> [<variable>=<value>...]"
1239
1240 def complete_set(self, text, line, begidx, endidx):
1241 """Try to complete the set command."""
1242 if not text and begidx>0 and line[begidx-1]=="=":
1243 return []
1244 else:
1245 return [key + "=" for key in self._valueHandlers if key.startswith(text)]
1246
1247 def do_close(self, args):
1248 """Close an existing connection so that FS will fail."""
1249 try:
1250 self._client.close()
1251 print "Connection closed"
1252 except Exception, e:
1253 print >> sys.stderr, "Failed to close the connection: " + str(e)
1254
1255 def do_failopen(self, args):
1256 """Enable/disable the failing of opens."""
1257 try:
1258 value = self.str2bool(args)
1259 self._client.failOpen(value)
1260 print "Opening will%s fail" % ("" if value else " not",)
1261 except Exception, e:
1262 print >> sys.stderr, "Failed to set open failure: " + str(e)
1263
1264 def help_failopen(self, usage = False):
1265 """Help for the failopen close"""
1266 if usage: print "Usage:",
1267 print "failopen yes|no"
1268
1269 def complete_failopen(self, text, line, begidx, endidx):
1270 if text:
1271 if "yes".startswith(text): return ["yes"]
1272 elif "no".startswith(text): return ["no"]
1273 else: return []
1274 else:
1275 return ["yes", "no"]
1276
1277 def do_quit(self, args):
1278 """Handle the quit command."""
1279 self._client.quit()
1280 return True
1281
1282#------------------------------------------------------------------------------
1283
1284if __name__ == "__main__":
1285 CLI().cmdloop()
1286else:
1287 server = Server()
1288 server.start()
1289
1290#------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.