source: xplra/misc/client.py@ 10:b0048ba6f3ca

Last change on this file since 10:b0048ba6f3ca was 10:b0048ba6f3ca, checked in by István Váradi <ivaradi@…>, 11 years ago

Implemented the commands querying or setting multiple datarefs directly

  • Property exe set to *
File size: 16.6 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 _writeU8(self, x):
420 """Write the given value as an unsigned, 8-bit value."""
421 self._stream.write(struct.pack("B", x))
422
423 def _writeS32(self, x):
424 """Write the given value as a signed, 32-bit value."""
425 self._stream.write(struct.pack("i", x))
426
427 def _writeU32(self, x):
428 """Write the given value as an unsigned, 32-bit value."""
429 self._stream.write(struct.pack("I", x))
430
431 def _writeFloat(self, x):
432 """Write the given value as a single-precision floating point."""
433 self._stream.write(struct.pack("f", x))
434
435 def _writeDouble(self, x):
436 """Write the given value as a double-precision floating point."""
437 self._stream.write(struct.pack("d", x))
438
439 def _writeLength(self, length):
440 """Write the given value is a variable-length value into our stream."""
441 self._stream.write(self._packLength(length))
442
443 def _writeString(self, str):
444 """Write the given string into the stream."""
445 self._stream.write(self._packString(str))
446
447 def _flush(self):
448 """Flush our stream."""
449 self._stream.flush()
450
451 def _readU8(self):
452 """Read an unsigned, 8-bit value from the stream."""
453 (value,) = struct.unpack("B", self._stream.read(1))
454 return value
455
456 def _readS32(self):
457 """Read a signed, 32-bit value from the stream."""
458 (value,) = struct.unpack("i", self._stream.read(4))
459 return value
460
461 def _readU32(self):
462 """Read an unsigned, 32-bit value from the stream."""
463 (value,) = struct.unpack("I", self._stream.read(4))
464 return value
465
466 def _readFloat(self):
467 """Read a single-precision floating point value from the stream."""
468 (value,) = struct.unpack("f", self._stream.read(4))
469 return value
470
471 def _readDouble(self):
472 """Read a double-precision floating point value from the stream."""
473 (value,) = struct.unpack("d", self._stream.read(8))
474 return value
475
476 def _readLength(self):
477 """Read a variable-length value from our stream."""
478 length = 0
479 while True:
480 (x,) = struct.unpack("B", self._stream.read(1))
481 length <<= 7
482 length |= x&0x7f
483 if x&0x80==0:
484 return length
485
486 def _readString(self):
487 """Read a string from our stream."""
488 length = self._readLength()
489 return self._stream.read(length)
490
491 def _readValue(self, type):
492 """Read the value of the given type from the stream."""
493 if type=="i":
494 return self._readS32()
495 elif type=="f":
496 return self._readFloat()
497 elif type=="d":
498 return self._readDouble()
499 elif type in ["fa", "ia", "ba", "s"]:
500 length = self._readS32()
501 if length>8192:
502 print "Very big length:", length
503 return None
504 elif length>0:
505 if type=="fa":
506 return [self._readFloat() for i in range(0, length)]
507 elif type=="ia":
508 return [self._readS32() for i in range(0, length)]
509 elif type=="ba" or type=="s":
510 bytes = [self._readU8() for i in range(0, length)]
511 if type=="ba":
512 return bytes
513 else:
514 string = ""
515 for b in bytes:
516 if b==0: break
517 string += chr(b)
518 return string
519 elif length==0:
520 return []
521 else:
522 return None
523
524 def _writeValue(self, type, value, length = None):
525 """Write the given value of the given type to the stream."""
526 if type=="i":
527 self._writeS32(int(value))
528 elif type=="f":
529 self._writeFloat(float(value))
530 elif type=="d":
531 self._writeDouble(float(value))
532 elif type in ["fa", "ia", "ba", "s"]:
533 if type=="s":
534 values = [ord(c) for c in value]
535 else:
536 words = [word.strip() for word in value.split(",")]
537 values = [float(word) if type=="fa" else int(word)
538 for word in words]
539 if len(values)<length:
540 values += [0.0 if type=="fa" else 0] * (length-len(values))
541 elif len(values)>length:
542 values = values[:length]
543 for value in values:
544 if type=="fa":
545 self._writeFloat(value)
546 elif type=="ia":
547 self._writeS32(value)
548 else:
549 self._writeU8(value)
550
551#------------------------------------------------------------------------------
552
553if __name__ == "__main__":
554 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
555 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
556 CLI(s.makefile()).cmdloop()
Note: See TracBrowser for help on using the repository browser.