source: xplra/src/client/python/xplra.py@ 56:1d491f271eaf

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

Implemented a more proper handling of a broken connection in the Python client

File size: 25.8 KB
Line 
1# X-Plane Remote Access client module
2#-------------------------------------------------------------------------------
3
4import os
5import struct
6import array
7
8#-------------------------------------------------------------------------------
9
10COMMAND_GET_SINGLE = 0x01
11
12COMMAND_SET_SINGLE = 0x02
13
14COMMAND_GET_MULTI = 0x03
15
16COMMAND_SET_MULTI = 0x04
17
18COMMAND_REGISTER_GET_MULTI = 0x11
19
20COMMAND_UNREGISTER_GET_MULTI = 0x12
21
22COMMAND_EXECUTE_GET_MULTI = 0x13
23
24COMMAND_REGISTER_SET_MULTI = 0x21
25
26COMMAND_UNREGISTER_SET_MULTI = 0x22
27
28COMMAND_EXECUTE_SET_MULTI = 0x23
29
30COMMAND_GET_VERSIONS = 0x31
31
32COMMAND_RELOAD_PLUGINS = 0x32
33
34COMMAND_SHOW_MESSAGE = 0x41
35
36COMMAND_REGISTER_HOTKEYS = 0x51
37
38COMMAND_QUERY_HOTKEYS = 0x52
39
40COMMAND_UNREGISTER_HOTKEYS = 0x53
41
42TYPE_INT = 0x01
43
44TYPE_FLOAT = 0x02
45
46TYPE_DOUBLE = 0x03
47
48TYPE_FLOAT_ARRAY = 0x11
49
50TYPE_INT_ARRAY = 0x12
51
52TYPE_BYTE_ARRAY = 0x13
53
54RESULT_OK = 0x00
55
56RESULT_INVALID_COMMAND = 0x01
57
58RESULT_UNKNOWN_DATAREF = 0x02
59
60RESULT_INVALID_TYPE = 0x03
61
62RESULT_INVALID_LENGTH = 0x04
63
64RESULT_INVALID_OFFSET = 0x05
65
66RESULT_INVALID_COUNT = 0x06
67
68RESULT_INVALID_ID = 0x07
69
70RESULT_INVALID_DURATION = 0x08
71
72RESULT_OTHER_ERROR = 0xff
73
74HOTKEY_MODIFIER_SHIFT = 0x0100
75
76HOTKEY_MODIFIER_CONTROL = 0x0200
77
78#-------------------------------------------------------------------------------
79
80class ProtocolException(Exception):
81 """Exception to signify protocol errors."""
82 _message = { RESULT_INVALID_COMMAND : "invalid command",
83 RESULT_UNKNOWN_DATAREF : "unknown dataref",
84 RESULT_INVALID_TYPE : "invalid type",
85 RESULT_INVALID_LENGTH : "invalid length",
86 RESULT_INVALID_OFFSET : "invalid offset",
87 RESULT_INVALID_COUNT : "invalid count",
88 RESULT_INVALID_ID : "invalid ID",
89 RESULT_INVALID_DURATION : "invalid duration",
90 RESULT_OTHER_ERROR : "other error" }
91
92 @staticmethod
93 def getMessage(resultCode):
94 """Get the message for the given result code."""
95 if resultCode in ProtocolException._message:
96 return ProtocolException._message[resultCode]
97 else:
98 return "unknown error code " + `resultCode`
99
100 def __init__(self, resultCode, parameter = None):
101 message = "xplra.ProtocolException: " + self.getMessage(resultCode)
102 if parameter is not None:
103 if resultCode==RESULT_UNKNOWN_DATAREF:
104 message += " (# %d)" % (parameter,)
105
106 super(ProtocolException, self).__init__(message)
107 self.resultCode = resultCode
108 self.parameter = parameter
109
110#-------------------------------------------------------------------------------
111
112class XPlane(object):
113 """The main class representing the connection to X-Plane."""
114
115 @staticmethod
116 def _packLength(x):
117 """Pack the given integer as a variable-length value."""
118 s = ""
119 while True:
120 y = x&0x7f
121 x >>= 7
122 if x>0: y |= 0x80
123 s += struct.pack("B", y)
124 if x==0:
125 return s
126
127 @staticmethod
128 def _packString(s):
129 """Pack the given string."""
130 return XPlane._packLength(len(s)) + s
131
132 def __init__(self):
133 """Construct the object."""
134 self._stream = None
135 self._multiBuffers = []
136
137 def connect(self):
138 """Try to connect to X-Plane."""
139 if self._stream is not None:
140 return
141
142 if os.name=="nt":
143 self._stream = open(r'\\.\pipe\\xplra', "b")
144 else:
145 import socket
146 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
147 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
148 self._stream = s.makefile()
149
150 for multiBuffer in self._multiBuffers:
151 multiBuffer._reregister()
152
153 @property
154 def isConnected(self):
155 """Determine if we are connected to the simulator."""
156 return self._stream is not None
157
158 def createMultiGetter(self):
159 """Create a new multi-dataref getter for this X-Plane object."""
160 multiGetter = MultiGetter(self)
161 self._multiBuffers.append(multiGetter)
162 return multiGetter
163
164 def createMultiSetter(self):
165 """Create a new multi-dataref setter for this X-Plane object."""
166 multiSetter = MultiSetter(self)
167 self._multiBuffers.append(multiSetter)
168 return multiSetter
169
170 def destroyMultiBuffer(self, multiBuffer):
171 """Destroy the given multi-dataref buffer.
172
173 It actually removes it from the list of multiple buffers and
174 unregisters it safely.
175
176 Returns the result of the safe unregistration if the buffer was found,
177 None otherwise.
178 """
179 if multiBuffer in self._multiBuffers:
180 self._multiBuffers.remove(multiBuffer)
181 return multiBuffer.unregisterSafely()
182 else:
183 return None
184
185 def getVersions(self):
186 """Get the versions of X-Plane, XPLM and XPLRA as a tuple."""
187 self._writeU8(COMMAND_GET_VERSIONS)
188 self._flush()
189 self._checkResult()
190 return (self._readS32(), self._readS32(), self._readS32())
191
192 def reloadPlugins(self):
193 """Reload the plugins in X-Plane.
194
195 After this, this connection becomes invalid."""
196 self._writeU8(COMMAND_RELOAD_PLUGINS)
197 self._flush()
198 self._checkResult();
199
200 def getInt(self, name):
201 """Get the value of the integer dataref with the given name."""
202 return self._getSingle(name, TYPE_INT)
203
204 def getFloat(self, name):
205 """Get the value of the float dataref with the given name."""
206 return self._getSingle(name, TYPE_FLOAT)
207
208 def getDouble(self, name):
209 """Get the value of the double dataref with the given name."""
210 return self._getSingle(name, TYPE_DOUBLE)
211
212 def getFloatArray(self, name, length = -1, offset = 0):
213 """Get the value of the float array dataref with the given name."""
214 return self._getSingle(name, TYPE_FLOAT_ARRAY, length, offset)
215
216 def getIntArray(self, name, length = -1, offset = 0):
217 """Get the value of the integer array dataref with the given name."""
218 return self._getSingle(name, TYPE_INT_ARRAY, length, offset)
219
220 def getByteArray(self, name, length = -1, offset = 0):
221 """Get the value of the byte array dataref with the given name."""
222 return self._getSingle(name, TYPE_BYTE_ARRAY, length, offset)
223
224 def getString(self, name, offset = 0):
225 """Get the value of the byte array dataref with the given name as a
226 string."""
227 s = ""
228 for b in self.getByteArray(name, offset = offset):
229 if b==0: break
230 s += chr(b)
231 return s
232
233 def setInt(self, name, value):
234 """Set the value of the integer dataref with the given name."""
235 self._setSingle(name, TYPE_INT, value)
236
237 def setFloat(self, name, value):
238 """Set the value of the float dataref with the given name."""
239 self._setSingle(name, TYPE_FLOAT, value)
240
241 def setDouble(self, name, value):
242 """Set the value of the double dataref with the given name."""
243 self._setSingle(name, TYPE_DOUBLE, value)
244
245 def setFloatArray(self, name, value, offset = 0):
246 """Set the value of the float array dataref with the given name."""
247 self._setSingle(name, TYPE_FLOAT_ARRAY, value, offset = offset)
248
249 def setIntArray(self, name, value, offset = 0):
250 """Set the value of the integer array dataref with the given name."""
251 self._setSingle(name, TYPE_INT_ARRAY, value, offset = offset)
252
253 def setByteArray(self, name, value, offset = 0):
254 """Set the value of the byte array dataref with the given name."""
255 self._setSingle(name, TYPE_BYTE_ARRAY, value, offset = offset)
256
257 def setString(self, name, value, length, offset = 0):
258 """Set the value of the byte array dataref with the given name from a
259 string.
260
261 The string will be padded with 0's so that its length is the given
262 one."""
263 value = [ord(c) for c in value[:length]]
264 value += [0] * (length - len(value))
265 self.setByteArray(name, value, offset)
266
267 def showMessage(self, message, duration):
268 """Show a message in the simulator window for the given duration.
269
270 The duration is a floating-point number denoting seconds."""
271 self._writeU8(COMMAND_SHOW_MESSAGE)
272 self._writeString(message)
273 self._writeFloat(duration)
274 self._flush()
275 self._checkResult()
276
277 def registerHotkeys(self, hotkeyCodes):
278 """Register the given hotkey codes for watching."""
279 self._writeU8(COMMAND_REGISTER_HOTKEYS)
280 self._writeU32(len(hotkeyCodes))
281 for hotkeyCode in hotkeyCodes:
282 self._writeU16(hotkeyCode)
283 self._flush()
284 self._checkResult()
285
286 def queryHotkeys(self):
287 """Query the state of the hotkeys registered previously"""
288 self._writeU8(COMMAND_QUERY_HOTKEYS)
289 self._flush()
290 self._checkResult()
291
292 length = self._readU32()
293 states = []
294 for i in range(0, length):
295 states.append(self._readU8()!=0)
296
297 return states
298
299 def unregisterHotkeys(self):
300 """Unregister the previously registered hotkeys."""
301 self._writeU8(COMMAND_UNREGISTER_HOTKEYS)
302 self._flush()
303 self._checkResult()
304
305 def disconnect(self):
306 """Disconnect from X-Plane."""
307 if self._stream is not None:
308 try:
309 self._stream.close()
310 finally:
311 self._stream = None
312
313 def _checkResult(self, resultCode = None, parameter = None, multi = False):
314 """Check the given result code.
315
316 If it is None, it will be read first.
317
318 If it is not RESULT_OK, a ProtocolException is thrown."""
319
320 if resultCode is None:
321 resultCode = self._readU8()
322 if multi and resultCode==RESULT_UNKNOWN_DATAREF:
323 parameter = self._readU32()
324
325 if resultCode!=RESULT_OK:
326 raise ProtocolException(resultCode, parameter)
327
328 def _getSingle(self, name, type, length = None, offset = None):
329 """Get the single value of the given name and type."""
330 self._writeU8(COMMAND_GET_SINGLE)
331 self._writeString(name)
332 self._writeU8(type)
333 if length is not None and offset is not None:
334 self._writeS32(length)
335 self._writeS32(offset)
336
337 self._flush()
338 self._checkResult()
339 return self._readValue(type)
340
341 def _readValue(self, type):
342 """Read the value of the given type from the stream."""
343 if type==TYPE_INT:
344 return self._readS32()
345 elif type==TYPE_FLOAT:
346 return self._readFloat()
347 elif type==TYPE_DOUBLE:
348 return self._readDouble()
349 else:
350 length = self._readS32()
351 arr = None
352 elementSize = 4
353 if type==TYPE_FLOAT_ARRAY:
354 arr = array.array("f")
355 elif type==TYPE_INT_ARRAY:
356 arr = array.array("i")
357 elif type==TYPE_BYTE_ARRAY:
358 arr = array.array("B")
359 elementSize = 1
360 if arr is not None and length>0:
361 arr.fromstring(self._stream.read(length*elementSize))
362 return None if arr is None else arr.tolist()
363
364 def _setSingle(self, name, type, value, offset = None):
365 """Set the single value of the given name and type."""
366 self._writeU8(COMMAND_SET_SINGLE)
367 self._writeString(name)
368 self._writeU8(type)
369 if offset is not None:
370 self._writeS32(len(value))
371 self._writeS32(offset)
372
373 self._writeValue(type, value)
374 self._flush()
375 self._checkResult()
376
377 def _writeValue(self, type, value):
378 """Write a value of the given type."""
379 if type==TYPE_INT:
380 self._writeS32(int(value))
381 elif type==TYPE_FLOAT:
382 self._writeFloat(float(value))
383 elif type==TYPE_DOUBLE:
384 self._writeDouble(float(value))
385 else:
386 arr = None
387 if type==TYPE_FLOAT_ARRAY:
388 arr = array.array("f")
389 elif type==TYPE_INT_ARRAY:
390 arr = array.array("i")
391 elif type==TYPE_BYTE_ARRAY:
392 arr = array.array("B")
393 arr.fromlist(value)
394 self._stream.write(arr.tostring())
395
396 def _writeU8(self, x):
397 """Write the given value as an unsigned, 8-bit value."""
398 self._stream.write(struct.pack("B", x))
399
400 def _writeU16(self, x):
401 """Write the given value as an unsigned, 16-bit value."""
402 self._stream.write(struct.pack("H", x))
403
404 def _writeS32(self, x):
405 """Write the given value as a signed, 32-bit value."""
406 self._stream.write(struct.pack("i", x))
407
408 def _writeU32(self, x):
409 """Write the given value as an unsigned, 32-bit value."""
410 self._stream.write(struct.pack("I", x))
411
412 def _writeFloat(self, x):
413 """Write the given value as a single-precision floating point."""
414 self._stream.write(struct.pack("f", x))
415
416 def _writeDouble(self, x):
417 """Write the given value as a double-precision floating point."""
418 self._stream.write(struct.pack("d", x))
419
420 def _writeLength(self, length):
421 """Write the given value is a variable-length value into our stream."""
422 self._stream.write(self._packLength(length))
423
424 def _writeString(self, str):
425 """Write the given string into the stream."""
426 self._stream.write(self._packString(str))
427
428 def _flush(self):
429 """Flush our stream."""
430 self._stream.flush()
431
432 def _readU8(self):
433 """Read an unsigned, 8-bit value from the stream."""
434 (value,) = struct.unpack("B", self._stream.read(1))
435 return value
436
437 def _readS32(self):
438 """Read a signed, 32-bit value from the stream."""
439 (value,) = struct.unpack("i", self._stream.read(4))
440 return value
441
442 def _readU32(self):
443 """Read an unsigned, 32-bit value from the stream."""
444 (value,) = struct.unpack("I", self._stream.read(4))
445 return value
446
447 def _readFloat(self):
448 """Read a single-precision floating point value from the stream."""
449 (value,) = struct.unpack("f", self._stream.read(4))
450 return value
451
452 def _readDouble(self):
453 """Read a double-precision floating point value from the stream."""
454 (value,) = struct.unpack("d", self._stream.read(8))
455 return value
456
457 def _readLength(self):
458 """Read a variable-length value from our stream."""
459 length = 0
460 while True:
461 (x,) = struct.unpack("B", self._stream.read(1))
462 length <<= 7
463 length |= x&0x7f
464 if x&0x80==0:
465 return length
466
467 def _readString(self):
468 """Read a string from our stream."""
469 length = self._readLength()
470 return self._stream.read(length)
471
472#-------------------------------------------------------------------------------
473
474class MultiBuffer(object):
475 """Buffer for querying or setting multi-dataref values."""
476 @staticmethod
477 def _getDefault(type, length):
478 """Get the default value for the given type."""
479 if type==TYPE_INT:
480 return 0
481 elif type==TYPE_FLOAT:
482 return float(0.0)
483 elif type==TYPE_DOUBLE:
484 return 0.0
485 elif length<=0:
486 return []
487 elif type==TYPE_FLOAT_ARRAY:
488 return [float(0.0)] * length
489 elif type==TYPE_INT_ARRAY or type==TYPE_BYTE_ARRAY:
490 return [0] * length
491
492 def __init__(self, xplane, registerCommand, unregisterCommand):
493 """Construct the buffer for the given XPlane instance and with the
494 given register/unregister command values."""
495 self._xplane = xplane
496 self._registerCommand = registerCommand
497 self._unregisterCommand = unregisterCommand
498
499 self._dataRefs = []
500 self._values = None
501
502 self._registeredID = None
503
504 @property
505 def values(self):
506 """Query the values as a list."""
507 if self._values is None:
508 self.finalize()
509 return self._values
510
511 @property
512 def wasRegistered(self):
513 """Determine if this buffer was registered."""
514 return self._wasRegistered
515
516 def addInt(self, name):
517 """Add an integer to the buffer with the given name
518
519 Returns an ID (or index) of the dataref."""
520 return self._add(name, TYPE_INT)
521
522 def addFloat(self, name):
523 """Add a float to the buffer with the given name
524
525 Returns an ID (or index) of the dataref."""
526 return self._add(name, TYPE_FLOAT)
527
528 def addDouble(self, name):
529 """Add a double to the buffer with the given name
530
531 Returns an ID (or index) of the dataref."""
532 return self._add(name, TYPE_DOUBLE)
533
534 def addFloatArray(self, name, length = -1, offset = 0):
535 """Add a floating point array to the buffer with the given name.
536
537 Returns an ID (or index) of the dataref."""
538 return self._add(name, TYPE_FLOAT_ARRAY, length, offset)
539
540 def addIntArray(self, name, length = -1, offset = 0):
541 """Add an integer array to the buffer with the given name.
542
543 Returns an ID (or index) of the dataref."""
544 return self._add(name, TYPE_INT_ARRAY, length, offset)
545
546 def addByteArray(self, name, length = -1, offset = 0):
547 """Add a byte array to the buffer with the given name.
548
549 Returns an ID (or index) of the dataref."""
550 return self._add(name, TYPE_BYTE_ARRAY, length, offset)
551
552 def finalize(self):
553 """Finalize the buffer, if not finalized yet.
554
555 It initializes the array of values with some defaults.
556
557 Returns whether there is any data in the buffer."""
558 if self._values is None:
559 self._values = [self._getDefault(type, length)
560 for (_, type, length, _) in self._dataRefs]
561 return self._values!=[]
562
563 def register(self):
564 """Register the buffer in X-Plane."""
565 if self.finalize() and self._registeredID is None:
566 self._writeSpec(self._registerCommand)
567 self._registeredID = self._xplane._readU32()
568
569 def unregister(self):
570 """Unregister the buffer from X-Plane."""
571 if self._registeredID is not None:
572 self._xplane._writeU8(self._unregisterCommand)
573 self._xplane._writeU32(self._registeredID)
574 self._xplane._flush()
575 self._xplane._checkResult()
576 self._registeredID = None
577
578 def unregisterSafely(self):
579 """Unregister the buffer from X-Plane, ignoring exceptions.
580
581 Returns True if the unregistration succeeded, False otherwise.
582 """
583 try:
584 self.unregister()
585 return True
586 except:
587 return False
588 finally:
589 self._registeredID = None
590
591 def execute(self):
592 """Perform the querying or the setting of the values.
593
594 It first checks if the buffer is finalized. If not, it will be
595 finalized. However, if it is not finalized but also registered, it will
596 first be unregistered and the re-registered after finalizing."""
597 if self._values is None and self._registeredID is not None:
598 self.unregister()
599 self.register()
600 else:
601 self.finalize()
602
603 if self._registeredID is None:
604 self._executeUnregistered()
605 else:
606 self._executeRegistered()
607
608 def getString(self, id):
609 """Get the value of the dataref with the given ID as a string.
610
611 The dataref should be of type byte array."""
612 if self._dataRefs[id][1]!=TYPE_BYTE_ARRAY:
613 raise TypeError("xplra.MultiBuffer.getString: only byte arrays can be converted to strings")
614 if self._values is None:
615 self.finalize()
616 s = ""
617 for c in self._values[id]:
618 if c==0: break
619 s += chr(c)
620 return s
621
622 def _reregister(self):
623 """Re-register the buffer in X-Plane, if it has been registered earlier
624
625 If it has not been registered, nothing is done. Otherwise the buffer
626 gets registered, and the old ID is forgotten. This function is meant to
627 be used by the XPlane object when it creates a new connection. If the
628 registration fails, the original ID is restored, so the _reregister()
629 could be called again
630 """
631 if self._registeredID is not None:
632 origRegisteredID = self._registeredID
633 try:
634 self._registeredID = None
635 self.register()
636 except:
637 self._registeredID = origRegisteredID
638 raise
639
640 def _add(self, name, type, length = None, offset = None):
641 """Add a scalar to the buffer with the given name and type"""
642 index = len(self._dataRefs)
643 self._values = None
644 self._dataRefs.append( (name, type, length, offset) )
645 return index
646
647 def _writeSpec(self, command):
648 """Write the specification preceded by the given command and check for
649 the result.
650
651 The specification is basically the list of the datarefs."""
652 self._xplane._writeU8(command)
653 self._xplane._writeU32(len(self._dataRefs))
654
655 for (name, type, length, offset) in self._dataRefs:
656 self._xplane._writeString(name)
657 self._xplane._writeU8(type)
658 if length is not None and offset is not None:
659 self._xplane._writeS32(length)
660 self._xplane._writeS32(offset)
661
662 self._xplane._flush()
663 self._xplane._checkResult(multi = True)
664
665 def __len__(self):
666 """Get the number of value items in the buffer."""
667 if self._values is None:
668 self.finalize()
669 return len(self._values)
670
671 def __getitem__(self, id):
672 """Get the item with the given ID."""
673 if self._values is None:
674 self.finalize()
675 return self._values[id]
676
677 def __setitem__(self, id, value):
678 """Set the item with the given ID."""
679 if self._values is None:
680 self.finalize()
681 type = self._dataRefs[id][1]
682 length = self._dataRefs[id][2]
683 if type==TYPE_INT:
684 self._values[id] = int(value)
685 elif type==TYPE_FLOAT or type==TYPE_DOUBLE:
686 self._values[id] = float(value)
687 elif type==TYPE_FLOAT_ARRAY:
688 if len(value)!=length:
689 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
690 self._values[id] = [float(x) for x in value]
691 elif type==TYPE_INT_ARRAY:
692 if len(value)!=length:
693 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
694 self._values[id] = [int(x) for x in value]
695 elif type==TYPE_BYTE_ARRAY:
696 if isinstance(value, str):
697 lst = [ord(x) for x in value[:length]]
698 if len(lst)<length:
699 lst += [0] * (length-len(lst))
700 self._values[id] = lst
701 else:
702 if len(value)!=length:
703 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
704 self._values[id] = [int(x) for x in value]
705
706 def __iter__(self):
707 """Get an iterator over the values of this buffer."""
708 if self._values is None:
709 self.finalize()
710 return iter(self._values)
711
712
713#-------------------------------------------------------------------------------
714
715class MultiGetter(MultiBuffer):
716 """Multi-dataref buffer for querying."""
717 def __init__(self, xplane):
718 """Construct the getter."""
719 super(MultiGetter, self).__init__(xplane,
720 COMMAND_REGISTER_GET_MULTI,
721 COMMAND_UNREGISTER_GET_MULTI)
722
723 def _executeRegistered(self):
724 """Execute the query if the buffer is registered."""
725 self._xplane._writeU8(COMMAND_EXECUTE_GET_MULTI)
726 self._xplane._writeU32(self._registeredID)
727 self._xplane._flush()
728
729 self._xplane._checkResult(multi = True)
730
731 self._readValues()
732
733 def _executeUnregistered(self):
734 """Execute the query if the buffer is not registered."""
735 self._writeSpec(COMMAND_GET_MULTI)
736 self._readValues()
737
738 def _readValues(self):
739 """Read the values into the values array."""
740 for i in range(0, len(self._dataRefs)):
741 self._values[i] = self._xplane._readValue(self._dataRefs[i][1])
742
743#-------------------------------------------------------------------------------
744
745class MultiSetter(MultiBuffer):
746 """Multi-dataref buffer for setting."""
747 def __init__(self, xplane):
748 """Construct the getter."""
749 super(MultiSetter, self).__init__(xplane,
750 COMMAND_REGISTER_SET_MULTI,
751 COMMAND_UNREGISTER_SET_MULTI)
752
753 def _executeRegistered(self):
754 """Execute the query if the buffer is registered."""
755 self._xplane._writeU8(COMMAND_EXECUTE_SET_MULTI)
756 self._xplane._writeU32(self._registeredID)
757 for i in range(0, len(self._dataRefs)):
758 self._xplane._writeValue(self._dataRefs[i][1], self._values[i])
759 self._xplane._flush()
760
761 self._xplane._checkResult(multi = True)
762
763 def _executeUnregistered(self):
764 """Execute the query if the buffer is not registered."""
765 self._xplane._writeU8(COMMAND_SET_MULTI)
766
767 numDataRefs = len(self._dataRefs)
768 self._xplane._writeU32(numDataRefs)
769
770 for i in range(0, numDataRefs):
771 (name, type, length, offset) = self._dataRefs[i]
772 value = self._values[i]
773
774 self._xplane._writeString(name)
775 self._xplane._writeU8(type)
776 if length is not None and offset is not None:
777 self._xplane._writeS32(len(value))
778 self._xplane._writeS32(offset)
779 self._xplane._writeValue(type, value)
780
781 self._xplane._flush()
782 self._xplane._checkResult(multi = True)
783
784#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.