source: xplra/misc/client.py@ 8:acc105036a41

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

Added the registry of multiple-data queries

  • Property exe set to *
File size: 11.8 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
103 self.use_rawinput = True
104 self.intro = "\nX-Plane Remote Access plugin command prompt\n"
105 self.prompt = "XPLRA> "
106
107 self.daemon = True
108
109 def default(self, line):
110 """Handle uhandled commands"""
111 if line=="EOF":
112 print
113 return True
114 else:
115 return super(CLI, self).default(line)
116
117 def do_get(self, args):
118 """Handle the 'get' command"""
119 words = self._splitArgs(args)
120 result = self._parseGetSpec(words)
121 if result is None:
122 return False
123
124 (name, type, length, offset) = result[0]
125
126 self._writeU8(0x01)
127 self._writeString(name)
128 self._writeU8(self._types[type])
129 if length is not None:
130 self._writeS32(length)
131 self._writeS32(offset)
132 self._flush()
133
134 result = self._readU8()
135 if result==0:
136 print self._readValue(type)
137 else:
138 print >> sys.stderr, "Error code:", result
139
140 def do_set(self, args):
141 """Handle the 'set' command"""
142 words = self._splitArgs(args)
143 if len(words)<3:
144 print >> sys.stderr, "Missing parameters"
145 return False
146
147 name = words[0]
148
149 type = words[1]
150 if type not in self._types:
151 print >> sys.stderr, "Invalid type"
152 return False
153
154 value = words[2]
155
156 length = None
157 offset = None
158 if len(words)>4:
159 length = int(words[3])
160 offset = int(words[4])
161
162 self._writeU8(0x02)
163 self._writeString(name)
164 self._writeU8(self._types[type])
165 if length is not None:
166 self._writeS32(length)
167 self._writeS32(offset)
168 self._writeValue(type, value, length)
169
170 self._flush()
171
172 result = self._readU8()
173 if result!=0:
174 print >> sys.stderr, "Error code:", result
175
176 def do_reg_multiget(self, args):
177 """Handle the 'reg_multiget' command"""
178 words = self._splitArgs(args)
179 specs = []
180 while words:
181 result = self._parseGetSpec(words)
182 if result is None:
183 return False
184 else:
185 specs.append(result[0])
186 words = result[1]
187
188 if not specs:
189 return False
190
191 self._writeU8(0x11)
192 self._writeU32(len(specs))
193 for (name, type, length, offset) in specs:
194 print name, type
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 id = self._readU32()
205 self._multiGets[id] = \
206 [type for (name, type, length, offset) in specs]
207 print "ID:", id
208 else:
209 print >> sys.stderr, "Error code:", result
210
211 def do_unreg_multiget(self, args):
212 """Handle the 'unreg_multiget' command"""
213 words = self._splitArgs(args)
214 if len(words)<1:
215 print >> sys.stderr, "Missing parameter"
216 return False
217
218 id = int(words[0])
219
220 self._writeU8(0x12)
221 self._writeU32(id)
222 self._flush()
223
224 result = self._readU8()
225 if result!=0:
226 print >> sys.stderr, "Error code:", result
227
228 if id in self._multiGets:
229 del self._multiGets[id]
230
231 def do_exec_multiget(self, args):
232 """Handle the 'unreg_multiget' command"""
233 words = self._splitArgs(args)
234 if len(words)<1:
235 print >> sys.stderr, "Missing parameter"
236 return False
237
238 id = int(words[0])
239 if id not in self._multiGets:
240 print >> sys.stderr, "Invalid ID"
241 return False
242
243 self._writeU8(0x13)
244 self._writeU32(id)
245 self._flush()
246
247 result = self._readU8()
248 if result==0:
249 for type in self._multiGets[id]:
250 value = self._readValue(type)
251 print value
252 elif result==0x02:
253 index = self._readU32()
254 print >> sys.stderr, "Invalid dataref at #%d" % (index,)
255 else:
256 print >> sys.stderr, "Error code:", result
257
258 def _writeU8(self, x):
259 """Write the given value as an unsigned, 8-bit value."""
260 self._stream.write(struct.pack("B", x))
261
262 def _writeS32(self, x):
263 """Write the given value as a signed, 32-bit value."""
264 self._stream.write(struct.pack("i", x))
265
266 def _writeU32(self, x):
267 """Write the given value as an unsigned, 32-bit value."""
268 self._stream.write(struct.pack("I", x))
269
270 def _writeFloat(self, x):
271 """Write the given value as a single-precision floating point."""
272 self._stream.write(struct.pack("f", x))
273
274 def _writeDouble(self, x):
275 """Write the given value as a double-precision floating point."""
276 self._stream.write(struct.pack("d", x))
277
278 def _writeLength(self, length):
279 """Write the given value is a variable-length value into our stream."""
280 self._stream.write(self._packLength(length))
281
282 def _writeString(self, str):
283 """Write the given string into the stream."""
284 self._stream.write(self._packString(str))
285
286 def _flush(self):
287 """Flush our stream."""
288 self._stream.flush()
289
290 def _readU8(self):
291 """Read an unsigned, 8-bit value from the stream."""
292 (value,) = struct.unpack("B", self._stream.read(1))
293 return value
294
295 def _readS32(self):
296 """Read a signed, 32-bit value from the stream."""
297 (value,) = struct.unpack("i", self._stream.read(4))
298 return value
299
300 def _readU32(self):
301 """Read an unsigned, 32-bit value from the stream."""
302 (value,) = struct.unpack("I", self._stream.read(4))
303 return value
304
305 def _readFloat(self):
306 """Read a single-precision floating point value from the stream."""
307 (value,) = struct.unpack("f", self._stream.read(4))
308 return value
309
310 def _readDouble(self):
311 """Read a double-precision floating point value from the stream."""
312 (value,) = struct.unpack("d", self._stream.read(8))
313 return value
314
315 def _readLength(self):
316 """Read a variable-length value from our stream."""
317 length = 0
318 while True:
319 (x,) = struct.unpack("B", self._stream.read(1))
320 length <<= 7
321 length |= x&0x7f
322 if x&0x80==0:
323 return length
324
325 def _readString(self):
326 """Read a string from our stream."""
327 length = self._readLength()
328 return self._stream.read(length)
329
330 def _readValue(self, type):
331 """Read the value of the given type from the stream."""
332 if type=="i":
333 return self._readS32()
334 elif type=="f":
335 return self._readFloat()
336 elif type=="d":
337 return self._readDouble()
338 elif type in ["fa", "ia", "ba", "s"]:
339 length = self._readS32()
340 if length>8192:
341 print "Very big length:", length
342 return None
343 elif length>0:
344 if type=="fa":
345 return [self._readFloat() for i in range(0, length)]
346 elif type=="ia":
347 return [self._readS32() for i in range(0, length)]
348 elif type=="ba" or type=="s":
349 bytes = [self._readU8() for i in range(0, length)]
350 if type=="ba":
351 return bytes
352 else:
353 string = ""
354 for b in bytes:
355 if b==0: break
356 string += chr(b)
357 return string
358 elif length==0:
359 return []
360 else:
361 return None
362
363 def _writeValue(self, type, value, length = None):
364 """Write the given value of the given type to the stream."""
365 if type=="i":
366 self._writeS32(int(value))
367 elif type=="f":
368 self._writeFloat(float(value))
369 elif type=="d":
370 self._writeDouble(float(value))
371 elif type in ["fa", "ia", "ba", "s"]:
372 if type=="s":
373 values = [ord(c) for c in value]
374 else:
375 words = [word.strip() for word in value.split(",")]
376 values = [float(word) if type=="fa" else int(word)
377 for word in words]
378 if len(values)<length:
379 values += [0.0 if type=="fa" else 0] * (length-len(values))
380 elif len(values)>length:
381 values = values[:length]
382 for value in values:
383 if type=="fa":
384 self._writeFloat(value)
385 elif type=="ia":
386 self._writeS32(value)
387 else:
388 self._writeU8(value)
389
390#------------------------------------------------------------------------------
391
392if __name__ == "__main__":
393 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
394 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
395 CLI(s.makefile()).cmdloop()
Note: See TracBrowser for help on using the repository browser.