source: xplra/src/client/python/xplra.py@ 57:f52efb9ba3d8

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

Implemented the C++ client part of the re-registration support

File size: 25.7 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 def addInt(self, name):
512 """Add an integer to the buffer with the given name
513
514 Returns an ID (or index) of the dataref."""
515 return self._add(name, TYPE_INT)
516
517 def addFloat(self, name):
518 """Add a float to the buffer with the given name
519
520 Returns an ID (or index) of the dataref."""
521 return self._add(name, TYPE_FLOAT)
522
523 def addDouble(self, name):
524 """Add a double to the buffer with the given name
525
526 Returns an ID (or index) of the dataref."""
527 return self._add(name, TYPE_DOUBLE)
528
529 def addFloatArray(self, name, length = -1, offset = 0):
530 """Add a floating point array to the buffer with the given name.
531
532 Returns an ID (or index) of the dataref."""
533 return self._add(name, TYPE_FLOAT_ARRAY, length, offset)
534
535 def addIntArray(self, name, length = -1, offset = 0):
536 """Add an integer array to the buffer with the given name.
537
538 Returns an ID (or index) of the dataref."""
539 return self._add(name, TYPE_INT_ARRAY, length, offset)
540
541 def addByteArray(self, name, length = -1, offset = 0):
542 """Add a byte array to the buffer with the given name.
543
544 Returns an ID (or index) of the dataref."""
545 return self._add(name, TYPE_BYTE_ARRAY, length, offset)
546
547 def finalize(self):
548 """Finalize the buffer, if not finalized yet.
549
550 It initializes the array of values with some defaults.
551
552 Returns whether there is any data in the buffer."""
553 if self._values is None:
554 self._values = [self._getDefault(type, length)
555 for (_, type, length, _) in self._dataRefs]
556 return self._values!=[]
557
558 def register(self):
559 """Register the buffer in X-Plane."""
560 if self.finalize() and self._registeredID is None:
561 self._writeSpec(self._registerCommand)
562 self._registeredID = self._xplane._readU32()
563
564 def unregister(self):
565 """Unregister the buffer from X-Plane."""
566 if self._registeredID is not None:
567 self._xplane._writeU8(self._unregisterCommand)
568 self._xplane._writeU32(self._registeredID)
569 self._xplane._flush()
570 self._xplane._checkResult()
571 self._registeredID = None
572
573 def unregisterSafely(self):
574 """Unregister the buffer from X-Plane, ignoring exceptions.
575
576 Returns True if the unregistration succeeded, False otherwise.
577 """
578 try:
579 self.unregister()
580 return True
581 except:
582 return False
583 finally:
584 self._registeredID = None
585
586 def execute(self):
587 """Perform the querying or the setting of the values.
588
589 It first checks if the buffer is finalized. If not, it will be
590 finalized. However, if it is not finalized but also registered, it will
591 first be unregistered and the re-registered after finalizing."""
592 if self._values is None and self._registeredID is not None:
593 self.unregister()
594 self.register()
595 else:
596 self.finalize()
597
598 if self._registeredID is None:
599 self._executeUnregistered()
600 else:
601 self._executeRegistered()
602
603 def getString(self, id):
604 """Get the value of the dataref with the given ID as a string.
605
606 The dataref should be of type byte array."""
607 if self._dataRefs[id][1]!=TYPE_BYTE_ARRAY:
608 raise TypeError("xplra.MultiBuffer.getString: only byte arrays can be converted to strings")
609 if self._values is None:
610 self.finalize()
611 s = ""
612 for c in self._values[id]:
613 if c==0: break
614 s += chr(c)
615 return s
616
617 def _reregister(self):
618 """Re-register the buffer in X-Plane, if it has been registered earlier
619
620 If it has not been registered, nothing is done. Otherwise the buffer
621 gets registered, and the old ID is forgotten. This function is meant to
622 be used by the XPlane object when it creates a new connection. If the
623 registration fails, the original ID is restored, so the _reregister()
624 could be called again
625 """
626 if self._registeredID is not None:
627 origRegisteredID = self._registeredID
628 try:
629 self._registeredID = None
630 self.register()
631 except:
632 self._registeredID = origRegisteredID
633 raise
634
635 def _add(self, name, type, length = None, offset = None):
636 """Add a scalar to the buffer with the given name and type"""
637 index = len(self._dataRefs)
638 self._values = None
639 self._dataRefs.append( (name, type, length, offset) )
640 return index
641
642 def _writeSpec(self, command):
643 """Write the specification preceded by the given command and check for
644 the result.
645
646 The specification is basically the list of the datarefs."""
647 self._xplane._writeU8(command)
648 self._xplane._writeU32(len(self._dataRefs))
649
650 for (name, type, length, offset) in self._dataRefs:
651 self._xplane._writeString(name)
652 self._xplane._writeU8(type)
653 if length is not None and offset is not None:
654 self._xplane._writeS32(length)
655 self._xplane._writeS32(offset)
656
657 self._xplane._flush()
658 self._xplane._checkResult(multi = True)
659
660 def __len__(self):
661 """Get the number of value items in the buffer."""
662 if self._values is None:
663 self.finalize()
664 return len(self._values)
665
666 def __getitem__(self, id):
667 """Get the item with the given ID."""
668 if self._values is None:
669 self.finalize()
670 return self._values[id]
671
672 def __setitem__(self, id, value):
673 """Set the item with the given ID."""
674 if self._values is None:
675 self.finalize()
676 type = self._dataRefs[id][1]
677 length = self._dataRefs[id][2]
678 if type==TYPE_INT:
679 self._values[id] = int(value)
680 elif type==TYPE_FLOAT or type==TYPE_DOUBLE:
681 self._values[id] = float(value)
682 elif type==TYPE_FLOAT_ARRAY:
683 if len(value)!=length:
684 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
685 self._values[id] = [float(x) for x in value]
686 elif type==TYPE_INT_ARRAY:
687 if len(value)!=length:
688 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
689 self._values[id] = [int(x) for x in value]
690 elif type==TYPE_BYTE_ARRAY:
691 if isinstance(value, str):
692 lst = [ord(x) for x in value[:length]]
693 if len(lst)<length:
694 lst += [0] * (length-len(lst))
695 self._values[id] = lst
696 else:
697 if len(value)!=length:
698 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
699 self._values[id] = [int(x) for x in value]
700
701 def __iter__(self):
702 """Get an iterator over the values of this buffer."""
703 if self._values is None:
704 self.finalize()
705 return iter(self._values)
706
707
708#-------------------------------------------------------------------------------
709
710class MultiGetter(MultiBuffer):
711 """Multi-dataref buffer for querying."""
712 def __init__(self, xplane):
713 """Construct the getter."""
714 super(MultiGetter, self).__init__(xplane,
715 COMMAND_REGISTER_GET_MULTI,
716 COMMAND_UNREGISTER_GET_MULTI)
717
718 def _executeRegistered(self):
719 """Execute the query if the buffer is registered."""
720 self._xplane._writeU8(COMMAND_EXECUTE_GET_MULTI)
721 self._xplane._writeU32(self._registeredID)
722 self._xplane._flush()
723
724 self._xplane._checkResult(multi = True)
725
726 self._readValues()
727
728 def _executeUnregistered(self):
729 """Execute the query if the buffer is not registered."""
730 self._writeSpec(COMMAND_GET_MULTI)
731 self._readValues()
732
733 def _readValues(self):
734 """Read the values into the values array."""
735 for i in range(0, len(self._dataRefs)):
736 self._values[i] = self._xplane._readValue(self._dataRefs[i][1])
737
738#-------------------------------------------------------------------------------
739
740class MultiSetter(MultiBuffer):
741 """Multi-dataref buffer for setting."""
742 def __init__(self, xplane):
743 """Construct the getter."""
744 super(MultiSetter, self).__init__(xplane,
745 COMMAND_REGISTER_SET_MULTI,
746 COMMAND_UNREGISTER_SET_MULTI)
747
748 def _executeRegistered(self):
749 """Execute the query if the buffer is registered."""
750 self._xplane._writeU8(COMMAND_EXECUTE_SET_MULTI)
751 self._xplane._writeU32(self._registeredID)
752 for i in range(0, len(self._dataRefs)):
753 self._xplane._writeValue(self._dataRefs[i][1], self._values[i])
754 self._xplane._flush()
755
756 self._xplane._checkResult(multi = True)
757
758 def _executeUnregistered(self):
759 """Execute the query if the buffer is not registered."""
760 self._xplane._writeU8(COMMAND_SET_MULTI)
761
762 numDataRefs = len(self._dataRefs)
763 self._xplane._writeU32(numDataRefs)
764
765 for i in range(0, numDataRefs):
766 (name, type, length, offset) = self._dataRefs[i]
767 value = self._values[i]
768
769 self._xplane._writeString(name)
770 self._xplane._writeU8(type)
771 if length is not None and offset is not None:
772 self._xplane._writeS32(len(value))
773 self._xplane._writeS32(offset)
774 self._xplane._writeValue(type, value)
775
776 self._xplane._flush()
777 self._xplane._checkResult(multi = True)
778
779#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.