source: src/mlx/pyuipc_sim.py@ 107:35310bf5309c

Last change on this file since 107:35310bf5309c was 89:ef4711a984fe, checked in by István Váradi <ivaradi@…>, 13 years ago

Added the collection of some further statistics and the finish page

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