source: xplra/misc/client.py@ 42:719f590e4878

Last change on this file since 42:719f590e4878 was 39:44eda5bbbdfb, checked in by István Váradi <ivaradi@…>, 12 years ago

Implemented a plugin reload command

  • Property exe set to *
File size: 17.3 KB
Line 
1#!/usr/bin/env python
2
3import sys
4import cmd
5import struct
6import socket
7import os
8
9#------------------------------------------------------------------------------
10
11class CLI(cmd.Cmd):
12 """Simple command-line interface for the X-Plane Remote Access plugin."""
13 _types = { "i" : 0x01,
14 "f" : 0x02,
15 "d" : 0x03,
16 "fa" : 0x11,
17 "ia" : 0x12,
18 "ba" : 0x13,
19 "s" : 0x13 }
20
21 @staticmethod
22 def _splitArgs(args):
23 """Split the given argument string into a list of words."""
24 words = []
25 word = ""
26 inQuote = False
27 for c in args:
28 if c.isspace() and not inQuote:
29 if word:
30 words.append(word)
31 word = ""
32 elif c=="\"" and (not word or inQuote):
33 inQuote = not inQuote
34 else:
35 word += c
36 if word:
37 words.append(word)
38 return words
39
40 @staticmethod
41 def _parseGetSpec(words):
42 """Parse a data query specification from the given argument word lisst.
43
44 If there is some failure, it returns None.
45
46 Otherwise it returns a tuple of two items:
47 - the parsed data, which is a tuple of these items:
48 - the name of the dataref to query
49 - the type of the dataref to query (it is a valid type, checked by
50 the function)
51 - the length of the dataref to query
52 - the offset to query the dataref from
53
54 - the remainder of the argument list."""
55 if len(words)<2:
56 print >> sys.stderr, "Missing parameters"
57 return None
58
59 nextIndex = 2
60
61 name = words[0]
62 type = words[1]
63 if type not in CLI._types:
64 print >> sys.stderr, "Invalid type"
65 return None
66
67 length = None
68 offset = None
69 if type in ["ia", "fa", "ba", "s"]:
70 if len(words)<4:
71 print >> sys.stderr, "Missing parameters"
72 return None
73 length = int(words[2])
74 offset = int(words[3])
75 nextIndex = 4
76
77 return ((name, type, length, offset), words[nextIndex:])
78
79 @staticmethod
80 def _packLength(x):
81 """Pack the given integer as a variable-length value."""
82 s = ""
83 while True:
84 y = x&0x7f
85 x >>= 7
86 if x>0: y |= 0x80
87 s += struct.pack("B", y)
88 if x==0:
89 return s
90
91 @staticmethod
92 def _packString(s):
93 """Pack the given string."""
94 return CLI._packLength(len(s)) + s
95
96 def __init__(self, stream):
97 """Construct the CLI."""
98 cmd.Cmd.__init__(self)
99
100 self._stream = stream
101 self._multiGets = {}
102 self._multiSets = {}
103
104 self.use_rawinput = True
105 self.intro = "\nX-Plane Remote Access plugin command prompt\n"
106 self.prompt = "XPLRA> "
107
108 self.daemon = True
109
110 def default(self, line):
111 """Handle uhandled commands"""
112 if line=="EOF":
113 print
114 return True
115 else:
116 return super(CLI, self).default(line)
117
118 def do_get(self, args):
119 """Handle the 'get' command"""
120 words = self._splitArgs(args)
121 result = self._parseGetSpec(words)
122 if result is None:
123 return False
124
125 (name, type, length, offset) = result[0]
126
127 self._writeU8(0x01)
128 self._writeString(name)
129 self._writeU8(self._types[type])
130 if length is not None:
131 self._writeS32(length)
132 self._writeS32(offset)
133 self._flush()
134
135 result = self._readU8()
136 if result==0:
137 print self._readValue(type)
138 else:
139 print >> sys.stderr, "Error code:", result
140
141 def do_set(self, args):
142 """Handle the 'set' command"""
143 words = self._splitArgs(args)
144 if len(words)<3:
145 print >> sys.stderr, "Missing parameters"
146 return False
147
148 name = words[0]
149
150 type = words[1]
151 if type not in self._types:
152 print >> sys.stderr, "Invalid type"
153 return False
154
155 value = words[2]
156
157 length = None
158 offset = None
159 if len(words)>4:
160 length = int(words[3])
161 offset = int(words[4])
162
163 self._writeU8(0x02)
164 self._writeString(name)
165 self._writeU8(self._types[type])
166 if length is not None:
167 self._writeS32(length)
168 self._writeS32(offset)
169 self._writeValue(type, value, length)
170
171 self._flush()
172
173 result = self._readU8()
174 if result!=0:
175 print >> sys.stderr, "Error code:", result
176
177 def do_multiget(self, args):
178 """Handle the 'multiget' command"""
179 words = self._splitArgs(args)
180 specs = []
181 while words:
182 result = self._parseGetSpec(words)
183 if result is None:
184 return False
185 else:
186 specs.append(result[0])
187 words = result[1]
188
189 if not specs:
190 return False
191
192 self._writeU8(0x03)
193 self._writeU32(len(specs))
194 for (name, type, length, offset) in specs:
195 self._writeString(name)
196 self._writeU8(self._types[type])
197 if length is not None:
198 self._writeS32(length)
199 self._writeS32(offset)
200 self._flush()
201
202 result = self._readU8()
203 if result==0:
204 for (_, type, _, _) in specs:
205 value = self._readValue(type)
206 print value
207 elif result==0x02:
208 index = self._readU32()
209 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
210 else:
211 print >> sys.stderr, "Error code:", result
212
213 def do_multiset(self, args):
214 """Handle the 'multiset' command"""
215 words = self._splitArgs(args)
216 specs = []
217 values = []
218 while words:
219 result = self._parseGetSpec(words)
220 if result is None:
221 return False
222 else:
223 words = result[1]
224 if not words:
225 print >> sys.stderr, "Missing argument"
226 return False
227 specs.append(result[0])
228 values.append(words[0])
229 words = words[1:]
230
231 if not specs:
232 return False
233
234 self._writeU8(0x04)
235 self._writeU32(len(specs))
236 for ((name, type, length, offset), value) in zip(specs, values):
237 self._writeString(name)
238 self._writeU8(self._types[type])
239 if length is not None:
240 self._writeS32(length)
241 self._writeS32(offset)
242 self._writeValue(type, value, length)
243 self._flush()
244
245 result = self._readU8()
246 if result==0x02:
247 index = self._readU32()
248 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
249 elif result!=0:
250 print >> sys.stderr, "Error code:", result
251
252 def do_reg_multiget(self, args):
253 """Handle the 'reg_multiget' command"""
254 words = self._splitArgs(args)
255 specs = []
256 while words:
257 result = self._parseGetSpec(words)
258 if result is None:
259 return False
260 else:
261 specs.append(result[0])
262 words = result[1]
263
264 if not specs:
265 return False
266
267 self._writeU8(0x11)
268 self._writeU32(len(specs))
269 for (name, type, length, offset) in specs:
270 self._writeString(name)
271 self._writeU8(self._types[type])
272 if length is not None:
273 self._writeS32(length)
274 self._writeS32(offset)
275 self._flush()
276
277 result = self._readU8()
278 if result==0:
279 id = self._readU32()
280 self._multiGets[id] = \
281 [type for (name, type, length, offset) in specs]
282 print "ID:", id
283 else:
284 print >> sys.stderr, "Error code:", result
285
286 def do_unreg_multiget(self, args):
287 """Handle the 'unreg_multiget' command"""
288 words = self._splitArgs(args)
289 if len(words)<1:
290 print >> sys.stderr, "Missing parameter"
291 return False
292
293 id = int(words[0])
294
295 self._writeU8(0x12)
296 self._writeU32(id)
297 self._flush()
298
299 result = self._readU8()
300 if result!=0:
301 print >> sys.stderr, "Error code:", result
302
303 if id in self._multiGets:
304 del self._multiGets[id]
305
306 def do_exec_multiget(self, args):
307 """Handle the 'exec_multiget' command"""
308 words = self._splitArgs(args)
309 if len(words)<1:
310 print >> sys.stderr, "Missing parameter"
311 return False
312
313 id = int(words[0])
314 if id not in self._multiGets:
315 print >> sys.stderr, "Invalid ID"
316 return False
317
318 self._writeU8(0x13)
319 self._writeU32(id)
320 self._flush()
321
322 result = self._readU8()
323 if result==0:
324 for type in self._multiGets[id]:
325 value = self._readValue(type)
326 print value
327 elif result==0x02:
328 index = self._readU32()
329 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
330 else:
331 print >> sys.stderr, "Error code:", result
332
333 def do_reg_multiset(self, args):
334 """Handle the 'reg_multiset' command"""
335 words = self._splitArgs(args)
336 specs = []
337 while words:
338 result = self._parseGetSpec(words)
339 if result is None:
340 return False
341 else:
342 specs.append(result[0])
343 words = result[1]
344
345 if not specs:
346 return False
347
348 self._writeU8(0x21)
349 self._writeU32(len(specs))
350 for (name, type, length, offset) in specs:
351 self._writeString(name)
352 self._writeU8(self._types[type])
353 if length is not None:
354 self._writeS32(length)
355 self._writeS32(offset)
356 self._flush()
357
358 result = self._readU8()
359 if result==0:
360 id = self._readU32()
361 self._multiSets[id] = \
362 [(type, length) for (name, type, length, offset) in specs]
363 print "ID:", id
364 else:
365 print >> sys.stderr, "Error code:", result
366
367 def do_unreg_multiset(self, args):
368 """Handle the 'unreg_multiset' command"""
369 words = self._splitArgs(args)
370 if len(words)<1:
371 print >> sys.stderr, "Missing parameter"
372 return False
373
374 id = int(words[0])
375
376 self._writeU8(0x22)
377 self._writeU32(id)
378 self._flush()
379
380 result = self._readU8()
381 if result!=0:
382 print >> sys.stderr, "Error code:", result
383
384 if id in self._multiSets:
385 del self._multiSets[id]
386
387 def do_exec_multiset(self, args):
388 """Handle the 'exec_multiget' command"""
389 words = self._splitArgs(args)
390 if len(words)<1:
391 print >> sys.stderr, "Missing parameter"
392 return False
393
394 id = int(words[0])
395 if id not in self._multiSets:
396 print >> sys.stderr, "Invalid ID"
397 return False
398
399 words = words[1:]
400 types = self._multiSets[id]
401 if len(words)<len(types):
402 print >> sys.stderr, "Missing parameter"
403 return False
404
405
406 self._writeU8(0x23)
407 self._writeU32(id)
408 for ((type, length), word) in zip(types, words):
409 self._writeValue(type, word, length)
410 self._flush()
411
412 result = self._readU8()
413 if result==0x02:
414 index = self._readU32()
415 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
416 elif result!=0:
417 print >> sys.stderr, "Error code:", result
418
419 def do_show_message(self, args):
420 words = self._splitArgs(args)
421 if len(words)<2:
422 print >> sys.stderr, "Missing parameters"
423 return False
424
425 message = words[0]
426 duration = float(words[1])
427
428 self._writeU8(0x41)
429 self._writeString(message)
430 self._writeFloat(duration)
431 self._flush()
432
433 result = self._readU8()
434 if result!=0:
435 print >> sys.stderr, "Error code:", result
436
437 def do_reload(self, args):
438 self._writeU8(0x32)
439 self._flush()
440
441 result = self._readU8()
442 if result!=0:
443 print >> sys.stderr, "Error code:", result
444
445 def _writeU8(self, x):
446 """Write the given value as an unsigned, 8-bit value."""
447 self._stream.write(struct.pack("B", x))
448
449 def _writeS32(self, x):
450 """Write the given value as a signed, 32-bit value."""
451 self._stream.write(struct.pack("i", x))
452
453 def _writeU32(self, x):
454 """Write the given value as an unsigned, 32-bit value."""
455 self._stream.write(struct.pack("I", x))
456
457 def _writeFloat(self, x):
458 """Write the given value as a single-precision floating point."""
459 self._stream.write(struct.pack("f", x))
460
461 def _writeDouble(self, x):
462 """Write the given value as a double-precision floating point."""
463 self._stream.write(struct.pack("d", x))
464
465 def _writeLength(self, length):
466 """Write the given value is a variable-length value into our stream."""
467 self._stream.write(self._packLength(length))
468
469 def _writeString(self, str):
470 """Write the given string into the stream."""
471 self._stream.write(self._packString(str))
472
473 def _flush(self):
474 """Flush our stream."""
475 self._stream.flush()
476
477 def _readU8(self):
478 """Read an unsigned, 8-bit value from the stream."""
479 (value,) = struct.unpack("B", self._stream.read(1))
480 return value
481
482 def _readS32(self):
483 """Read a signed, 32-bit value from the stream."""
484 (value,) = struct.unpack("i", self._stream.read(4))
485 return value
486
487 def _readU32(self):
488 """Read an unsigned, 32-bit value from the stream."""
489 (value,) = struct.unpack("I", self._stream.read(4))
490 return value
491
492 def _readFloat(self):
493 """Read a single-precision floating point value from the stream."""
494 (value,) = struct.unpack("f", self._stream.read(4))
495 return value
496
497 def _readDouble(self):
498 """Read a double-precision floating point value from the stream."""
499 (value,) = struct.unpack("d", self._stream.read(8))
500 return value
501
502 def _readLength(self):
503 """Read a variable-length value from our stream."""
504 length = 0
505 while True:
506 (x,) = struct.unpack("B", self._stream.read(1))
507 length <<= 7
508 length |= x&0x7f
509 if x&0x80==0:
510 return length
511
512 def _readString(self):
513 """Read a string from our stream."""
514 length = self._readLength()
515 return self._stream.read(length)
516
517 def _readValue(self, type):
518 """Read the value of the given type from the stream."""
519 if type=="i":
520 return self._readS32()
521 elif type=="f":
522 return self._readFloat()
523 elif type=="d":
524 return self._readDouble()
525 elif type in ["fa", "ia", "ba", "s"]:
526 length = self._readS32()
527 if length>8192:
528 print "Very big length:", length
529 return None
530 elif length>0:
531 if type=="fa":
532 return [self._readFloat() for i in range(0, length)]
533 elif type=="ia":
534 return [self._readS32() for i in range(0, length)]
535 elif type=="ba" or type=="s":
536 bytes = [self._readU8() for i in range(0, length)]
537 if type=="ba":
538 return bytes
539 else:
540 string = ""
541 for b in bytes:
542 if b==0: break
543 string += chr(b)
544 return string
545 elif length==0:
546 return []
547 else:
548 return None
549
550 def _writeValue(self, type, value, length = None):
551 """Write the given value of the given type to the stream."""
552 if type=="i":
553 self._writeS32(int(value))
554 elif type=="f":
555 self._writeFloat(float(value))
556 elif type=="d":
557 self._writeDouble(float(value))
558 elif type in ["fa", "ia", "ba", "s"]:
559 if type=="s":
560 values = [ord(c) for c in value]
561 else:
562 words = [word.strip() for word in value.split(",")]
563 values = [float(word) if type=="fa" else int(word)
564 for word in words]
565 if len(values)<length:
566 values += [0.0 if type=="fa" else 0] * (length-len(values))
567 elif len(values)>length:
568 values = values[:length]
569 for value in values:
570 if type=="fa":
571 self._writeFloat(value)
572 elif type=="ia":
573 self._writeS32(value)
574 else:
575 self._writeU8(value)
576
577#------------------------------------------------------------------------------
578
579if __name__ == "__main__":
580 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
581 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
582 CLI(s.makefile()).cmdloop()
Note: See TracBrowser for help on using the repository browser.