source: xplra/src/client/python/xplra.py@ 32:22525b9438b5

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

Implemented the setting of single datarefs

File size: 11.2 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
30TYPE_INT = 0x01
31
32TYPE_FLOAT = 0x02
33
34TYPE_DOUBLE = 0x03
35
36TYPE_FLOAT_ARRAY = 0x11
37
38TYPE_INT_ARRAY = 0x12
39
40TYPE_BYTE_ARRAY = 0x13
41
42RESULT_OK = 0x00
43
44RESULT_INVALID_COMMAND = 0x01
45
46RESULT_UNKNOWN_DATAREF = 0x02
47
48RESULT_INVALID_TYPE = 0x03
49
50RESULT_INVALID_LENGTH = 0x04
51
52RESULT_INVALID_OFFSET = 0x05
53
54RESULT_INVALID_COUNT = 0x06
55
56RESULT_INVALID_ID = 0x07
57
58RESULT_OTHER_ERROR = 0xff
59
60#-------------------------------------------------------------------------------
61
62class ProtocolException(Exception):
63 """Exception to signify protocol errors."""
64 _message = { RESULT_INVALID_COMMAND : "invalid command",
65 RESULT_UNKNOWN_DATAREF : "unknown dataref",
66 RESULT_INVALID_TYPE : "invalid type",
67 RESULT_INVALID_LENGTH : "invalid length",
68 RESULT_INVALID_OFFSET : "invalid offset",
69 RESULT_INVALID_COUNT : "invalid count",
70 RESULT_INVALID_ID : "invalid ID",
71 RESULT_OTHER_ERROR : "other error" }
72
73 @staticmethod
74 def getMessage(resultCode):
75 """Get the message for the given result code."""
76 if resultCode in ProtocolException._message:
77 return ProtocolException._message[resultCode]
78 else:
79 return "unknown error code " + `resultCode`
80
81 def __init__(self, resultCode):
82 super(ProtocolException, self).__init__("xplra.ProtocolException: " +
83 self.getMessage(resultCode))
84 self.resultCode = resultCode
85
86#-------------------------------------------------------------------------------
87
88class XPlane(object):
89 """The main class representing the connection to X-Plane."""
90
91 @staticmethod
92 def _packLength(x):
93 """Pack the given integer as a variable-length value."""
94 s = ""
95 while True:
96 y = x&0x7f
97 x >>= 7
98 if x>0: y |= 0x80
99 s += struct.pack("B", y)
100 if x==0:
101 return s
102
103 @staticmethod
104 def _packString(s):
105 """Pack the given string."""
106 return XPlane._packLength(len(s)) + s
107
108 def __init__(self):
109 """Construct the object."""
110 self._stream = None
111
112 def connect(self):
113 """Try to connect to X-Plane."""
114 if self._stream is not None:
115 return
116
117 if os.name=="nt":
118 self._stream = open(r'\\.\pipe\\xplra', "b")
119 else:
120 import socket
121 s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
122 s.connect("/tmp/xplra-" + os.environ["LOGNAME"])
123 self._stream = s.makefile()
124
125 @property
126 def isConnected(self):
127 """Determine if we are connected to the simulator."""
128 return self._stream is not None
129
130 def getInt(self, name):
131 """Get the value of the integer dataref with the given name."""
132 return self._getSingle(name, TYPE_INT)
133
134 def getFloat(self, name):
135 """Get the value of the float dataref with the given name."""
136 return self._getSingle(name, TYPE_FLOAT)
137
138 def getDouble(self, name):
139 """Get the value of the double dataref with the given name."""
140 return self._getSingle(name, TYPE_DOUBLE)
141
142 def getFloatArray(self, name, length = -1, offset = 0):
143 """Get the value of the float array dataref with the given name."""
144 return self._getSingle(name, TYPE_FLOAT_ARRAY, length, offset)
145
146 def getIntArray(self, name, length = -1, offset = 0):
147 """Get the value of the integer array dataref with the given name."""
148 return self._getSingle(name, TYPE_INT_ARRAY, length, offset)
149
150 def getByteArray(self, name, length = -1, offset = 0):
151 """Get the value of the byte array dataref with the given name."""
152 return self._getSingle(name, TYPE_BYTE_ARRAY, length, offset)
153
154 def getString(self, name, offset = 0):
155 """Get the value of the byte array dataref with the given name as a
156 string."""
157 s = ""
158 for b in self.getByteArray(name, offset = offset):
159 if b==0: break
160 s += chr(b)
161 return s
162
163 def setInt(self, name, value):
164 """Set the value of the integer dataref with the given name."""
165 self._setSingle(name, TYPE_INT, value)
166
167 def setFloat(self, name, value):
168 """Set the value of the float dataref with the given name."""
169 self._setSingle(name, TYPE_FLOAT, value)
170
171 def setDouble(self, name, value):
172 """Set the value of the double dataref with the given name."""
173 self._setSingle(name, TYPE_DOUBLE, value)
174
175 def setFloatArray(self, name, value, offset = 0):
176 """Set the value of the float array dataref with the given name."""
177 self._setSingle(name, TYPE_FLOAT_ARRAY, value, offset = offset)
178
179 def setIntArray(self, name, value, offset = 0):
180 """Set the value of the integer array dataref with the given name."""
181 self._setSingle(name, TYPE_INT_ARRAY, value, offset = offset)
182
183 def setByteArray(self, name, value, offset = 0):
184 """Set the value of the byte array dataref with the given name."""
185 self._setSingle(name, TYPE_BYTE_ARRAY, value, offset = offset)
186
187 def setString(self, name, value, length, offset = 0):
188 """Set the value of the byte array dataref with the given name from a
189 string.
190
191 The string will be padded with 0's so that its length is the given
192 one."""
193 value = [ord(c) for c in value[:length]]
194 value += [0] * (length - len(value))
195 self.setByteArray(name, value, offset)
196
197 def disconnect(self):
198 """Disconnect from X-Plane."""
199 if self._stream is not None:
200 self._stream.close()
201 self._stream = None
202
203 def _checkResult(self, resultCode = None):
204 """Check the given result code.
205
206 If it is None, it will be read first.
207
208 If it is not RESULT_OK, a ProtocolException is thrown."""
209
210 if resultCode is None:
211 resultCode = self._readU8()
212 if resultCode!=RESULT_OK:
213 raise ProtocolException(resultCode)
214
215 def _getSingle(self, name, type, length = None, offset = None):
216 """Get the single value of the given name and type."""
217 self._writeU8(COMMAND_GET_SINGLE)
218 self._writeString(name)
219 self._writeU8(type)
220 if length is not None and offset is not None:
221 self._writeS32(length)
222 self._writeS32(offset)
223
224 self._flush()
225 self._checkResult()
226 return self._readValue(type)
227
228 def _readValue(self, type):
229 """Read the value of the given type from the stream."""
230 if type==TYPE_INT:
231 return self._readS32()
232 elif type==TYPE_FLOAT:
233 return self._readFloat()
234 elif type==TYPE_DOUBLE:
235 return self._readDouble()
236 else:
237 length = self._readS32()
238 arr = None
239 elementSize = 4
240 if type==TYPE_FLOAT_ARRAY:
241 arr = array.array("f")
242 elif type==TYPE_INT_ARRAY:
243 arr = array.array("i")
244 elif type==TYPE_BYTE_ARRAY:
245 arr = array.array("B")
246 elementSize = 1
247 if arr is not None and length>0:
248 arr.fromstring(self._stream.read(length*elementSize))
249 return arr
250
251 def _setSingle(self, name, type, value, offset = None):
252 """Set the single value of the given name and type."""
253 self._writeU8(COMMAND_SET_SINGLE)
254 self._writeString(name)
255 self._writeU8(type)
256 if offset is not None:
257 self._writeS32(len(value))
258 self._writeS32(offset)
259
260 self._writeValue(type, value)
261 self._flush()
262 self._checkResult()
263
264 def _writeValue(self, type, value):
265 """Write a value of the given type."""
266 if type==TYPE_INT:
267 self._writeS32(int(value))
268 elif type==TYPE_FLOAT:
269 self._writeFloat(float(value))
270 elif type==TYPE_DOUBLE:
271 self._writeDouble(float(value))
272 else:
273 arr = None
274 if type==TYPE_FLOAT_ARRAY:
275 arr = array.array("f")
276 elif type==TYPE_INT_ARRAY:
277 arr = array.array("i")
278 elif type==TYPE_BYTE_ARRAY:
279 arr = array.array("B")
280 arr.fromlist(value)
281 self._stream.write(arr.tostring())
282
283 def _writeU8(self, x):
284 """Write the given value as an unsigned, 8-bit value."""
285 self._stream.write(struct.pack("B", x))
286
287 def _writeS32(self, x):
288 """Write the given value as a signed, 32-bit value."""
289 self._stream.write(struct.pack("i", x))
290
291 def _writeU32(self, x):
292 """Write the given value as an unsigned, 32-bit value."""
293 self._stream.write(struct.pack("I", x))
294
295 def _writeFloat(self, x):
296 """Write the given value as a single-precision floating point."""
297 self._stream.write(struct.pack("f", x))
298
299 def _writeDouble(self, x):
300 """Write the given value as a double-precision floating point."""
301 self._stream.write(struct.pack("d", x))
302
303 def _writeLength(self, length):
304 """Write the given value is a variable-length value into our stream."""
305 self._stream.write(self._packLength(length))
306
307 def _writeString(self, str):
308 """Write the given string into the stream."""
309 self._stream.write(self._packString(str))
310
311 def _flush(self):
312 """Flush our stream."""
313 self._stream.flush()
314
315 def _readU8(self):
316 """Read an unsigned, 8-bit value from the stream."""
317 (value,) = struct.unpack("B", self._stream.read(1))
318 return value
319
320 def _readS32(self):
321 """Read a signed, 32-bit value from the stream."""
322 (value,) = struct.unpack("i", self._stream.read(4))
323 return value
324
325 def _readU32(self):
326 """Read an unsigned, 32-bit value from the stream."""
327 (value,) = struct.unpack("I", self._stream.read(4))
328 return value
329
330 def _readFloat(self):
331 """Read a single-precision floating point value from the stream."""
332 (value,) = struct.unpack("f", self._stream.read(4))
333 return value
334
335 def _readDouble(self):
336 """Read a double-precision floating point value from the stream."""
337 (value,) = struct.unpack("d", self._stream.read(8))
338 return value
339
340 def _readLength(self):
341 """Read a variable-length value from our stream."""
342 length = 0
343 while True:
344 (x,) = struct.unpack("B", self._stream.read(1))
345 length <<= 7
346 length |= x&0x7f
347 if x&0x80==0:
348 return length
349
350 def _readString(self):
351 """Read a string from our stream."""
352 length = self._readLength()
353 return self._stream.read(length)
354
355#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.