source: xplra/src/client/python/xplra.py@ 64:29a745311929

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

Added a proper implementation of the stream for Win32

File size: 29.4 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
112if os.name=="nt":
113 import win32file
114 import io
115
116 class Win32NamedPipe(io.RawIOBase):
117 """A stream object to represent a Win32 named pipe."""
118 def __init__(self, name):
119 """Construct the pipe with the given name."""
120 self._handle = win32file.CreateFile(name,
121 win32file.GENERIC_READ |
122 win32file.GENERIC_WRITE,
123 0, None,
124 win32file.OPEN_EXISTING, 0, None)
125
126 def close(self):
127 """Close the pipe, if not closed already."""
128 if self._handle is not None:
129 win32file.CloseHandle(self._handle)
130 self._handle = None
131
132 @property
133 def closed(self):
134 """Determine if the pipe is closed or not."""
135 return self._handle is None
136
137 def fileno(self):
138 """Get the file number, which is impossible."""
139 raise IOError("Win32NamedPipe.fileno: not supported")
140
141 def flush(self):
142 """Flush the stream."""
143 pass
144
145 def isatty(self):
146 """Determine if the stream is a terminal, which it is not."""
147 return False
148
149 def read(self, n = -1):
150 """Read at most the given number of bytes from the stream."""
151 if n==-1:
152 return self.readall()
153 else:
154 (error, data) = win32file.ReadFile(self._handle, n)
155 return data
156
157 def readable(self):
158 """Determine if the stream is readable, which it is."""
159 return self._handle is not None
160
161 def readall(self):
162 """Read all bytes from the stream, which is not supported."""
163 raise IOError("Win32NamedPipe.readall: not supported")
164
165 def readinto(self, buffer):
166 """Read into the given buffer."""
167 (error, data) = win32file.ReadFile(self._handle, len(buffer))
168 length = len(data)
169 buffer[:length] = data
170 return length
171
172 def readline(self, limit = -1):
173 """Read a line, which is currently not supported."""
174 raise IOError("Win32NamedPipe.readline: not supported")
175
176 def readlines(self, hint = -1):
177 """Read lines, which is currently not supported."""
178 raise IOError("Win32NamedPipe.readlines: not supported")
179
180 def seek(self, offset, whence = io.SEEK_SET):
181 """Seek in the stream, which is not supported."""
182 raise IOError("Win32NamedPipe.seek: not supported")
183
184 def seekable(self):
185 """Determine if the stream is seekable, which it is not."""
186 return False
187
188 def tell(self):
189 """Get the current position, which is not supported."""
190 raise IOError("Win32NamedPipe.tell: not supported")
191
192 def truncate(self, size = None):
193 """Truncate the stream, which is not supported."""
194 raise IOError("Win32NamedPipe.truncate: not supported")
195
196 def writable(self):
197 """Determine if the stream is writable, which it is."""
198 return self._handle is not None
199
200 def write(self, buffer):
201 """Write the given buffer into the stream."""
202 (error, written) = win32file.WriteFile(self._handle, buffer.tobytes())
203 return written
204
205 def writelines(self, lines):
206 """Write the given lines, which is not supported."""
207 raise IOError("Win32NamedPipe.writelines: not supported")
208
209#-------------------------------------------------------------------------------
210
211class XPlane(object):
212 """The main class representing the connection to X-Plane."""
213
214 @staticmethod
215 def _packLength(x):
216 """Pack the given integer as a variable-length value."""
217 s = ""
218 while True:
219 y = x&0x7f
220 x >>= 7
221 if x>0: y |= 0x80
222 s += struct.pack("B", y)
223 if x==0:
224 return s
225
226 @staticmethod
227 def _packString(s):
228 """Pack the given string."""
229 return XPlane._packLength(len(s)) + s
230
231 def __init__(self):
232 """Construct the object."""
233 self._stream = None
234 self._multiBuffers = []
235
236 def connect(self):
237 """Try to connect to X-Plane."""
238 if self._stream is not None:
239 return
240
241 if os.name=="nt":
242 pipe = Win32NamedPipe(r'\\.\pipe\\xplra')
243 self._stream = io.BufferedRWPair(pipe, pipe)
244 else:
245 import socket
246 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
247 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
248 self._stream = s.makefile()
249
250 for multiBuffer in self._multiBuffers:
251 multiBuffer._reregister()
252
253 @property
254 def isConnected(self):
255 """Determine if we are connected to the simulator."""
256 return self._stream is not None
257
258 def createMultiGetter(self):
259 """Create a new multi-dataref getter for this X-Plane object."""
260 multiGetter = MultiGetter(self)
261 self._multiBuffers.append(multiGetter)
262 return multiGetter
263
264 def createMultiSetter(self):
265 """Create a new multi-dataref setter for this X-Plane object."""
266 multiSetter = MultiSetter(self)
267 self._multiBuffers.append(multiSetter)
268 return multiSetter
269
270 def destroyMultiBuffer(self, multiBuffer):
271 """Destroy the given multi-dataref buffer.
272
273 It actually removes it from the list of multiple buffers and
274 unregisters it safely.
275
276 Returns the result of the safe unregistration if the buffer was found,
277 None otherwise.
278 """
279 if multiBuffer in self._multiBuffers:
280 self._multiBuffers.remove(multiBuffer)
281 return multiBuffer.unregisterSafely()
282 else:
283 return None
284
285 def getVersions(self):
286 """Get the versions of X-Plane, XPLM and XPLRA as a tuple."""
287 self._writeU8(COMMAND_GET_VERSIONS)
288 self._flush()
289 self._checkResult()
290 return (self._readS32(), self._readS32(), self._readS32())
291
292 def reloadPlugins(self):
293 """Reload the plugins in X-Plane.
294
295 After this, this connection becomes invalid."""
296 self._writeU8(COMMAND_RELOAD_PLUGINS)
297 self._flush()
298 self._checkResult();
299
300 def getInt(self, name):
301 """Get the value of the integer dataref with the given name."""
302 return self._getSingle(name, TYPE_INT)
303
304 def getFloat(self, name):
305 """Get the value of the float dataref with the given name."""
306 return self._getSingle(name, TYPE_FLOAT)
307
308 def getDouble(self, name):
309 """Get the value of the double dataref with the given name."""
310 return self._getSingle(name, TYPE_DOUBLE)
311
312 def getFloatArray(self, name, length = -1, offset = 0):
313 """Get the value of the float array dataref with the given name."""
314 return self._getSingle(name, TYPE_FLOAT_ARRAY, length, offset)
315
316 def getIntArray(self, name, length = -1, offset = 0):
317 """Get the value of the integer array dataref with the given name."""
318 return self._getSingle(name, TYPE_INT_ARRAY, length, offset)
319
320 def getByteArray(self, name, length = -1, offset = 0):
321 """Get the value of the byte array dataref with the given name."""
322 return self._getSingle(name, TYPE_BYTE_ARRAY, length, offset)
323
324 def getString(self, name, offset = 0):
325 """Get the value of the byte array dataref with the given name as a
326 string."""
327 s = ""
328 for b in self.getByteArray(name, offset = offset):
329 if b==0: break
330 s += chr(b)
331 return s
332
333 def setInt(self, name, value):
334 """Set the value of the integer dataref with the given name."""
335 self._setSingle(name, TYPE_INT, value)
336
337 def setFloat(self, name, value):
338 """Set the value of the float dataref with the given name."""
339 self._setSingle(name, TYPE_FLOAT, value)
340
341 def setDouble(self, name, value):
342 """Set the value of the double dataref with the given name."""
343 self._setSingle(name, TYPE_DOUBLE, value)
344
345 def setFloatArray(self, name, value, offset = 0):
346 """Set the value of the float array dataref with the given name."""
347 self._setSingle(name, TYPE_FLOAT_ARRAY, value, offset = offset)
348
349 def setIntArray(self, name, value, offset = 0):
350 """Set the value of the integer array dataref with the given name."""
351 self._setSingle(name, TYPE_INT_ARRAY, value, offset = offset)
352
353 def setByteArray(self, name, value, offset = 0):
354 """Set the value of the byte array dataref with the given name."""
355 self._setSingle(name, TYPE_BYTE_ARRAY, value, offset = offset)
356
357 def setString(self, name, value, length, offset = 0):
358 """Set the value of the byte array dataref with the given name from a
359 string.
360
361 The string will be padded with 0's so that its length is the given
362 one."""
363 value = [ord(c) for c in value[:length]]
364 value += [0] * (length - len(value))
365 self.setByteArray(name, value, offset)
366
367 def showMessage(self, message, duration):
368 """Show a message in the simulator window for the given duration.
369
370 The duration is a floating-point number denoting seconds."""
371 self._writeU8(COMMAND_SHOW_MESSAGE)
372 self._writeString(message)
373 self._writeFloat(duration)
374 self._flush()
375 self._checkResult()
376
377 def registerHotkeys(self, hotkeyCodes):
378 """Register the given hotkey codes for watching."""
379 self._writeU8(COMMAND_REGISTER_HOTKEYS)
380 self._writeU32(len(hotkeyCodes))
381 for hotkeyCode in hotkeyCodes:
382 self._writeU16(hotkeyCode)
383 self._flush()
384 self._checkResult()
385
386 def queryHotkeys(self):
387 """Query the state of the hotkeys registered previously"""
388 self._writeU8(COMMAND_QUERY_HOTKEYS)
389 self._flush()
390 self._checkResult()
391
392 length = self._readU32()
393 states = []
394 for i in range(0, length):
395 states.append(self._readU8()!=0)
396
397 return states
398
399 def unregisterHotkeys(self):
400 """Unregister the previously registered hotkeys."""
401 self._writeU8(COMMAND_UNREGISTER_HOTKEYS)
402 self._flush()
403 self._checkResult()
404
405 def disconnect(self):
406 """Disconnect from X-Plane."""
407 if self._stream is not None:
408 try:
409 self._stream.close()
410 finally:
411 self._stream = None
412
413 def _checkResult(self, resultCode = None, parameter = None, multi = False):
414 """Check the given result code.
415
416 If it is None, it will be read first.
417
418 If it is not RESULT_OK, a ProtocolException is thrown."""
419
420 if resultCode is None:
421 resultCode = self._readU8()
422 if multi and resultCode==RESULT_UNKNOWN_DATAREF:
423 parameter = self._readU32()
424
425 if resultCode!=RESULT_OK:
426 raise ProtocolException(resultCode, parameter)
427
428 def _getSingle(self, name, type, length = None, offset = None):
429 """Get the single value of the given name and type."""
430 self._writeU8(COMMAND_GET_SINGLE)
431 self._writeString(name)
432 self._writeU8(type)
433 if length is not None and offset is not None:
434 self._writeS32(length)
435 self._writeS32(offset)
436
437 self._flush()
438 self._checkResult()
439 return self._readValue(type)
440
441 def _readValue(self, type):
442 """Read the value of the given type from the stream."""
443 if type==TYPE_INT:
444 return self._readS32()
445 elif type==TYPE_FLOAT:
446 return self._readFloat()
447 elif type==TYPE_DOUBLE:
448 return self._readDouble()
449 else:
450 length = self._readS32()
451 arr = None
452 elementSize = 4
453 if type==TYPE_FLOAT_ARRAY:
454 arr = array.array("f")
455 elif type==TYPE_INT_ARRAY:
456 arr = array.array("i")
457 elif type==TYPE_BYTE_ARRAY:
458 arr = array.array("B")
459 elementSize = 1
460 if arr is not None and length>0:
461 arr.fromstring(self._stream.read(length*elementSize))
462 return None if arr is None else arr.tolist()
463
464 def _setSingle(self, name, type, value, offset = None):
465 """Set the single value of the given name and type."""
466 self._writeU8(COMMAND_SET_SINGLE)
467 self._writeString(name)
468 self._writeU8(type)
469 if offset is not None:
470 self._writeS32(len(value))
471 self._writeS32(offset)
472
473 self._writeValue(type, value)
474 self._flush()
475 self._checkResult()
476
477 def _writeValue(self, type, value):
478 """Write a value of the given type."""
479 if type==TYPE_INT:
480 self._writeS32(int(value))
481 elif type==TYPE_FLOAT:
482 self._writeFloat(float(value))
483 elif type==TYPE_DOUBLE:
484 self._writeDouble(float(value))
485 else:
486 arr = None
487 if type==TYPE_FLOAT_ARRAY:
488 arr = array.array("f")
489 elif type==TYPE_INT_ARRAY:
490 arr = array.array("i")
491 elif type==TYPE_BYTE_ARRAY:
492 arr = array.array("B")
493 arr.fromlist(value)
494 self._stream.write(arr.tostring())
495
496 def _writeU8(self, x):
497 """Write the given value as an unsigned, 8-bit value."""
498 self._stream.write(struct.pack("B", x))
499
500 def _writeU16(self, x):
501 """Write the given value as an unsigned, 16-bit value."""
502 self._stream.write(struct.pack("H", x))
503
504 def _writeS32(self, x):
505 """Write the given value as a signed, 32-bit value."""
506 self._stream.write(struct.pack("i", x))
507
508 def _writeU32(self, x):
509 """Write the given value as an unsigned, 32-bit value."""
510 self._stream.write(struct.pack("I", x))
511
512 def _writeFloat(self, x):
513 """Write the given value as a single-precision floating point."""
514 self._stream.write(struct.pack("f", x))
515
516 def _writeDouble(self, x):
517 """Write the given value as a double-precision floating point."""
518 self._stream.write(struct.pack("d", x))
519
520 def _writeLength(self, length):
521 """Write the given value is a variable-length value into our stream."""
522 self._stream.write(self._packLength(length))
523
524 def _writeString(self, str):
525 """Write the given string into the stream."""
526 self._stream.write(self._packString(str))
527
528 def _flush(self):
529 """Flush our stream."""
530 self._stream.flush()
531
532 def _readU8(self):
533 """Read an unsigned, 8-bit value from the stream."""
534 (value,) = struct.unpack("B", self._stream.read(1))
535 return value
536
537 def _readS32(self):
538 """Read a signed, 32-bit value from the stream."""
539 (value,) = struct.unpack("i", self._stream.read(4))
540 return value
541
542 def _readU32(self):
543 """Read an unsigned, 32-bit value from the stream."""
544 (value,) = struct.unpack("I", self._stream.read(4))
545 return value
546
547 def _readFloat(self):
548 """Read a single-precision floating point value from the stream."""
549 (value,) = struct.unpack("f", self._stream.read(4))
550 return value
551
552 def _readDouble(self):
553 """Read a double-precision floating point value from the stream."""
554 (value,) = struct.unpack("d", self._stream.read(8))
555 return value
556
557 def _readLength(self):
558 """Read a variable-length value from our stream."""
559 length = 0
560 while True:
561 (x,) = struct.unpack("B", self._stream.read(1))
562 length <<= 7
563 length |= x&0x7f
564 if x&0x80==0:
565 return length
566
567 def _readString(self):
568 """Read a string from our stream."""
569 length = self._readLength()
570 return self._stream.read(length)
571
572#-------------------------------------------------------------------------------
573
574class MultiBuffer(object):
575 """Buffer for querying or setting multi-dataref values."""
576 @staticmethod
577 def _getDefault(type, length):
578 """Get the default value for the given type."""
579 if type==TYPE_INT:
580 return 0
581 elif type==TYPE_FLOAT:
582 return float(0.0)
583 elif type==TYPE_DOUBLE:
584 return 0.0
585 elif length<=0:
586 return []
587 elif type==TYPE_FLOAT_ARRAY:
588 return [float(0.0)] * length
589 elif type==TYPE_INT_ARRAY or type==TYPE_BYTE_ARRAY:
590 return [0] * length
591
592 def __init__(self, xplane, registerCommand, unregisterCommand):
593 """Construct the buffer for the given XPlane instance and with the
594 given register/unregister command values."""
595 self._xplane = xplane
596 self._registerCommand = registerCommand
597 self._unregisterCommand = unregisterCommand
598
599 self._dataRefs = []
600 self._values = None
601
602 self._registeredID = None
603
604 @property
605 def values(self):
606 """Query the values as a list."""
607 if self._values is None:
608 self.finalize()
609 return self._values
610
611 def addInt(self, name):
612 """Add an integer to the buffer with the given name
613
614 Returns an ID (or index) of the dataref."""
615 return self._add(name, TYPE_INT)
616
617 def addFloat(self, name):
618 """Add a float to the buffer with the given name
619
620 Returns an ID (or index) of the dataref."""
621 return self._add(name, TYPE_FLOAT)
622
623 def addDouble(self, name):
624 """Add a double to the buffer with the given name
625
626 Returns an ID (or index) of the dataref."""
627 return self._add(name, TYPE_DOUBLE)
628
629 def addFloatArray(self, name, length = -1, offset = 0):
630 """Add a floating point array to the buffer with the given name.
631
632 Returns an ID (or index) of the dataref."""
633 return self._add(name, TYPE_FLOAT_ARRAY, length, offset)
634
635 def addIntArray(self, name, length = -1, offset = 0):
636 """Add an integer array to the buffer with the given name.
637
638 Returns an ID (or index) of the dataref."""
639 return self._add(name, TYPE_INT_ARRAY, length, offset)
640
641 def addByteArray(self, name, length = -1, offset = 0):
642 """Add a byte array to the buffer with the given name.
643
644 Returns an ID (or index) of the dataref."""
645 return self._add(name, TYPE_BYTE_ARRAY, length, offset)
646
647 def finalize(self):
648 """Finalize the buffer, if not finalized yet.
649
650 It initializes the array of values with some defaults.
651
652 Returns whether there is any data in the buffer."""
653 if self._values is None:
654 self._values = [self._getDefault(type, length)
655 for (_, type, length, _) in self._dataRefs]
656 return self._values!=[]
657
658 def register(self):
659 """Register the buffer in X-Plane."""
660 if self.finalize() and self._registeredID is None:
661 self._writeSpec(self._registerCommand)
662 self._registeredID = self._xplane._readU32()
663
664 def unregister(self):
665 """Unregister the buffer from X-Plane."""
666 if self._registeredID is not None:
667 self._xplane._writeU8(self._unregisterCommand)
668 self._xplane._writeU32(self._registeredID)
669 self._xplane._flush()
670 self._xplane._checkResult()
671 self._registeredID = None
672
673 def unregisterSafely(self):
674 """Unregister the buffer from X-Plane, ignoring exceptions.
675
676 Returns True if the unregistration succeeded, False otherwise.
677 """
678 try:
679 self.unregister()
680 return True
681 except:
682 return False
683 finally:
684 self._registeredID = None
685
686 def execute(self):
687 """Perform the querying or the setting of the values.
688
689 It first checks if the buffer is finalized. If not, it will be
690 finalized. However, if it is not finalized but also registered, it will
691 first be unregistered and the re-registered after finalizing."""
692 if self._values is None and self._registeredID is not None:
693 self.unregister()
694 self.register()
695 else:
696 self.finalize()
697
698 if self._registeredID is None:
699 self._executeUnregistered()
700 else:
701 self._executeRegistered()
702
703 def getString(self, id):
704 """Get the value of the dataref with the given ID as a string.
705
706 The dataref should be of type byte array."""
707 if self._dataRefs[id][1]!=TYPE_BYTE_ARRAY:
708 raise TypeError("xplra.MultiBuffer.getString: only byte arrays can be converted to strings")
709 if self._values is None:
710 self.finalize()
711 s = ""
712 for c in self._values[id]:
713 if c==0: break
714 s += chr(c)
715 return s
716
717 def _reregister(self):
718 """Re-register the buffer in X-Plane, if it has been registered earlier
719
720 If it has not been registered, nothing is done. Otherwise the buffer
721 gets registered, and the old ID is forgotten. This function is meant to
722 be used by the XPlane object when it creates a new connection. If the
723 registration fails, the original ID is restored, so the _reregister()
724 could be called again
725 """
726 if self._registeredID is not None:
727 origRegisteredID = self._registeredID
728 try:
729 self._registeredID = None
730 self.register()
731 except:
732 self._registeredID = origRegisteredID
733 raise
734
735 def _add(self, name, type, length = None, offset = None):
736 """Add a scalar to the buffer with the given name and type"""
737 index = len(self._dataRefs)
738 self._values = None
739 self._dataRefs.append( (name, type, length, offset) )
740 return index
741
742 def _writeSpec(self, command):
743 """Write the specification preceded by the given command and check for
744 the result.
745
746 The specification is basically the list of the datarefs."""
747 self._xplane._writeU8(command)
748 self._xplane._writeU32(len(self._dataRefs))
749
750 for (name, type, length, offset) in self._dataRefs:
751 self._xplane._writeString(name)
752 self._xplane._writeU8(type)
753 if length is not None and offset is not None:
754 self._xplane._writeS32(length)
755 self._xplane._writeS32(offset)
756
757 self._xplane._flush()
758 self._xplane._checkResult(multi = True)
759
760 def __len__(self):
761 """Get the number of value items in the buffer."""
762 if self._values is None:
763 self.finalize()
764 return len(self._values)
765
766 def __getitem__(self, id):
767 """Get the item with the given ID."""
768 if self._values is None:
769 self.finalize()
770 return self._values[id]
771
772 def __setitem__(self, id, value):
773 """Set the item with the given ID."""
774 if self._values is None:
775 self.finalize()
776 type = self._dataRefs[id][1]
777 length = self._dataRefs[id][2]
778 if type==TYPE_INT:
779 self._values[id] = int(value)
780 elif type==TYPE_FLOAT or type==TYPE_DOUBLE:
781 self._values[id] = float(value)
782 elif type==TYPE_FLOAT_ARRAY:
783 if len(value)!=length:
784 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
785 self._values[id] = [float(x) for x in value]
786 elif type==TYPE_INT_ARRAY:
787 if len(value)!=length:
788 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
789 self._values[id] = [int(x) for x in value]
790 elif type==TYPE_BYTE_ARRAY:
791 if isinstance(value, str):
792 lst = [ord(x) for x in value[:length]]
793 if len(lst)<length:
794 lst += [0] * (length-len(lst))
795 self._values[id] = lst
796 else:
797 if len(value)!=length:
798 raise ValueError("xplra.MultiBuffer: expected a list of length %d" % (length,))
799 self._values[id] = [int(x) for x in value]
800
801 def __iter__(self):
802 """Get an iterator over the values of this buffer."""
803 if self._values is None:
804 self.finalize()
805 return iter(self._values)
806
807
808#-------------------------------------------------------------------------------
809
810class MultiGetter(MultiBuffer):
811 """Multi-dataref buffer for querying."""
812 def __init__(self, xplane):
813 """Construct the getter."""
814 super(MultiGetter, self).__init__(xplane,
815 COMMAND_REGISTER_GET_MULTI,
816 COMMAND_UNREGISTER_GET_MULTI)
817
818 def _executeRegistered(self):
819 """Execute the query if the buffer is registered."""
820 self._xplane._writeU8(COMMAND_EXECUTE_GET_MULTI)
821 self._xplane._writeU32(self._registeredID)
822 self._xplane._flush()
823
824 self._xplane._checkResult(multi = True)
825
826 self._readValues()
827
828 def _executeUnregistered(self):
829 """Execute the query if the buffer is not registered."""
830 self._writeSpec(COMMAND_GET_MULTI)
831 self._readValues()
832
833 def _readValues(self):
834 """Read the values into the values array."""
835 for i in range(0, len(self._dataRefs)):
836 self._values[i] = self._xplane._readValue(self._dataRefs[i][1])
837
838#-------------------------------------------------------------------------------
839
840class MultiSetter(MultiBuffer):
841 """Multi-dataref buffer for setting."""
842 def __init__(self, xplane):
843 """Construct the getter."""
844 super(MultiSetter, self).__init__(xplane,
845 COMMAND_REGISTER_SET_MULTI,
846 COMMAND_UNREGISTER_SET_MULTI)
847
848 def _executeRegistered(self):
849 """Execute the query if the buffer is registered."""
850 self._xplane._writeU8(COMMAND_EXECUTE_SET_MULTI)
851 self._xplane._writeU32(self._registeredID)
852 for i in range(0, len(self._dataRefs)):
853 self._xplane._writeValue(self._dataRefs[i][1], self._values[i])
854 self._xplane._flush()
855
856 self._xplane._checkResult(multi = True)
857
858 def _executeUnregistered(self):
859 """Execute the query if the buffer is not registered."""
860 self._xplane._writeU8(COMMAND_SET_MULTI)
861
862 numDataRefs = len(self._dataRefs)
863 self._xplane._writeU32(numDataRefs)
864
865 for i in range(0, numDataRefs):
866 (name, type, length, offset) = self._dataRefs[i]
867 value = self._values[i]
868
869 self._xplane._writeString(name)
870 self._xplane._writeU8(type)
871 if length is not None and offset is not None:
872 self._xplane._writeS32(len(value))
873 self._xplane._writeS32(offset)
874 self._xplane._writeValue(type, value)
875
876 self._xplane._flush()
877 self._xplane._checkResult(multi = True)
878
879#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.