source: xplra/misc/client.py@ 9:953cd606427c

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

Registering multi-data update requests works

  • Property exe set to *
File size: 14.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_reg_multiget(self, args):
178 """Handle the 'reg_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(0x11)
193 self._writeU32(len(specs))
194 for (name, type, length, offset) in specs:
195 print name, type
196 self._writeString(name)
197 self._writeU8(self._types[type])
198 if length is not None:
199 self._writeS32(length)
200 self._writeS32(offset)
201 self._flush()
202
203 result = self._readU8()
204 if result==0:
205 id = self._readU32()
206 self._multiGets[id] = \
207 [type for (name, type, length, offset) in specs]
208 print "ID:", id
209 else:
210 print >> sys.stderr, "Error code:", result
211
212 def do_unreg_multiget(self, args):
213 """Handle the 'unreg_multiget' command"""
214 words = self._splitArgs(args)
215 if len(words)<1:
216 print >> sys.stderr, "Missing parameter"
217 return False
218
219 id = int(words[0])
220
221 self._writeU8(0x12)
222 self._writeU32(id)
223 self._flush()
224
225 result = self._readU8()
226 if result!=0:
227 print >> sys.stderr, "Error code:", result
228
229 if id in self._multiGets:
230 del self._multiGets[id]
231
232 def do_exec_multiget(self, args):
233 """Handle the 'exec_multiget' command"""
234 words = self._splitArgs(args)
235 if len(words)<1:
236 print >> sys.stderr, "Missing parameter"
237 return False
238
239 id = int(words[0])
240 if id not in self._multiGets:
241 print >> sys.stderr, "Invalid ID"
242 return False
243
244 self._writeU8(0x13)
245 self._writeU32(id)
246 self._flush()
247
248 result = self._readU8()
249 if result==0:
250 for type in self._multiGets[id]:
251 value = self._readValue(type)
252 print value
253 elif result==0x02:
254 index = self._readU32()
255 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
256 else:
257 print >> sys.stderr, "Error code:", result
258
259 def do_reg_multiset(self, args):
260 """Handle the 'reg_multiset' command"""
261 words = self._splitArgs(args)
262 specs = []
263 while words:
264 result = self._parseGetSpec(words)
265 if result is None:
266 return False
267 else:
268 specs.append(result[0])
269 words = result[1]
270
271 if not specs:
272 return False
273
274 self._writeU8(0x21)
275 self._writeU32(len(specs))
276 for (name, type, length, offset) in specs:
277 print name, type
278 self._writeString(name)
279 self._writeU8(self._types[type])
280 if length is not None:
281 self._writeS32(length)
282 self._writeS32(offset)
283 self._flush()
284
285 result = self._readU8()
286 if result==0:
287 id = self._readU32()
288 self._multiSets[id] = \
289 [(type, length) for (name, type, length, offset) in specs]
290 print "ID:", id
291 else:
292 print >> sys.stderr, "Error code:", result
293
294 def do_unreg_multiset(self, args):
295 """Handle the 'unreg_multiset' command"""
296 words = self._splitArgs(args)
297 if len(words)<1:
298 print >> sys.stderr, "Missing parameter"
299 return False
300
301 id = int(words[0])
302
303 self._writeU8(0x22)
304 self._writeU32(id)
305 self._flush()
306
307 result = self._readU8()
308 if result!=0:
309 print >> sys.stderr, "Error code:", result
310
311 if id in self._multiSets:
312 del self._multiSets[id]
313
314 def do_exec_multiset(self, args):
315 """Handle the 'exec_multiget' command"""
316 words = self._splitArgs(args)
317 if len(words)<1:
318 print >> sys.stderr, "Missing parameter"
319 return False
320
321 id = int(words[0])
322 if id not in self._multiSets:
323 print >> sys.stderr, "Invalid ID"
324 return False
325
326 words = words[1:]
327 types = self._multiSets[id]
328 if len(words)<len(types):
329 print >> sys.stderr, "Missing parameter"
330 return False
331
332
333 self._writeU8(0x23)
334 self._writeU32(id)
335 for ((type, length), word) in zip(types, words):
336 self._writeValue(type, word, length)
337 self._flush()
338
339 result = self._readU8()
340 if result==0x02:
341 index = self._readU32()
342 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
343 elif result!=0:
344 print >> sys.stderr, "Error code:", result
345
346 def _writeU8(self, x):
347 """Write the given value as an unsigned, 8-bit value."""
348 self._stream.write(struct.pack("B", x))
349
350 def _writeS32(self, x):
351 """Write the given value as a signed, 32-bit value."""
352 self._stream.write(struct.pack("i", x))
353
354 def _writeU32(self, x):
355 """Write the given value as an unsigned, 32-bit value."""
356 self._stream.write(struct.pack("I", x))
357
358 def _writeFloat(self, x):
359 """Write the given value as a single-precision floating point."""
360 self._stream.write(struct.pack("f", x))
361
362 def _writeDouble(self, x):
363 """Write the given value as a double-precision floating point."""
364 self._stream.write(struct.pack("d", x))
365
366 def _writeLength(self, length):
367 """Write the given value is a variable-length value into our stream."""
368 self._stream.write(self._packLength(length))
369
370 def _writeString(self, str):
371 """Write the given string into the stream."""
372 self._stream.write(self._packString(str))
373
374 def _flush(self):
375 """Flush our stream."""
376 self._stream.flush()
377
378 def _readU8(self):
379 """Read an unsigned, 8-bit value from the stream."""
380 (value,) = struct.unpack("B", self._stream.read(1))
381 return value
382
383 def _readS32(self):
384 """Read a signed, 32-bit value from the stream."""
385 (value,) = struct.unpack("i", self._stream.read(4))
386 return value
387
388 def _readU32(self):
389 """Read an unsigned, 32-bit value from the stream."""
390 (value,) = struct.unpack("I", self._stream.read(4))
391 return value
392
393 def _readFloat(self):
394 """Read a single-precision floating point value from the stream."""
395 (value,) = struct.unpack("f", self._stream.read(4))
396 return value
397
398 def _readDouble(self):
399 """Read a double-precision floating point value from the stream."""
400 (value,) = struct.unpack("d", self._stream.read(8))
401 return value
402
403 def _readLength(self):
404 """Read a variable-length value from our stream."""
405 length = 0
406 while True:
407 (x,) = struct.unpack("B", self._stream.read(1))
408 length <<= 7
409 length |= x&0x7f
410 if x&0x80==0:
411 return length
412
413 def _readString(self):
414 """Read a string from our stream."""
415 length = self._readLength()
416 return self._stream.read(length)
417
418 def _readValue(self, type):
419 """Read the value of the given type from the stream."""
420 if type=="i":
421 return self._readS32()
422 elif type=="f":
423 return self._readFloat()
424 elif type=="d":
425 return self._readDouble()
426 elif type in ["fa", "ia", "ba", "s"]:
427 length = self._readS32()
428 if length>8192:
429 print "Very big length:", length
430 return None
431 elif length>0:
432 if type=="fa":
433 return [self._readFloat() for i in range(0, length)]
434 elif type=="ia":
435 return [self._readS32() for i in range(0, length)]
436 elif type=="ba" or type=="s":
437 bytes = [self._readU8() for i in range(0, length)]
438 if type=="ba":
439 return bytes
440 else:
441 string = ""
442 for b in bytes:
443 if b==0: break
444 string += chr(b)
445 return string
446 elif length==0:
447 return []
448 else:
449 return None
450
451 def _writeValue(self, type, value, length = None):
452 """Write the given value of the given type to the stream."""
453 if type=="i":
454 self._writeS32(int(value))
455 elif type=="f":
456 self._writeFloat(float(value))
457 elif type=="d":
458 self._writeDouble(float(value))
459 elif type in ["fa", "ia", "ba", "s"]:
460 if type=="s":
461 values = [ord(c) for c in value]
462 else:
463 words = [word.strip() for word in value.split(",")]
464 values = [float(word) if type=="fa" else int(word)
465 for word in words]
466 if len(values)<length:
467 values += [0.0 if type=="fa" else 0] * (length-len(values))
468 elif len(values)>length:
469 values = values[:length]
470 for value in values:
471 if type=="fa":
472 self._writeFloat(value)
473 elif type=="ia":
474 self._writeS32(value)
475 else:
476 self._writeU8(value)
477
478#------------------------------------------------------------------------------
479
480if __name__ == "__main__":
481 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
482 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
483 CLI(s.makefile()).cmdloop()
Note: See TracBrowser for help on using the repository browser.