source: src/mlx/pyuipc_sim.py@ 128:e14fcd9d9215

Last change on this file since 128:e14fcd9d9215 was 117:af3d52b9adc4, checked in by István Váradi <ivaradi@…>, 13 years ago

Implemented the weight help tab

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