source: xplra/src/client/python/xplra.py@ 31:a37c4ec54e84

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

Reading of single values works with the Python client

File size: 8.7 KB
RevLine 
[31]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 return self.getByteArray(name, offset = offset).tostring()
158
159 def disconnect(self):
160 """Disconnect from X-Plane."""
161 if self._stream is not None:
162 self._stream.close()
163 self._stream = None
164
165 def _checkResult(self, resultCode = None):
166 """Check the given result code.
167
168 If it is None, it will be read first.
169
170 If it is not RESULT_OK, a ProtocolException is thrown."""
171
172 if resultCode is None:
173 resultCode = self._readU8()
174 if resultCode!=RESULT_OK:
175 raise ProtocolException(resultCode)
176
177 def _getSingle(self, name, type, length = None, offset = None):
178 """Get the single value of the given name and type."""
179 self._writeU8(COMMAND_GET_SINGLE)
180 self._writeString(name)
181 self._writeU8(type)
182 if length is not None and offset is not None:
183 self._writeS32(length)
184 self._writeS32(offset)
185
186 self._flush()
187 self._checkResult()
188 return self._readValue(type)
189
190 def _readValue(self, type):
191 """Read the value of the given type from the stream."""
192 if type==TYPE_INT:
193 return self._readS32()
194 elif type==TYPE_FLOAT:
195 return self._readFloat()
196 elif type==TYPE_DOUBLE:
197 return self._readDouble()
198 else:
199 length = self._readS32()
200 arr = None
201 elementSize = 4
202 if type==TYPE_FLOAT_ARRAY:
203 arr = array.array("f")
204 elif type==TYPE_INT_ARRAY:
205 arr = array.array("i")
206 elif type==TYPE_BYTE_ARRAY:
207 arr = array.array("B")
208 elementSize = 1
209 if arr is not None and length>0:
210 arr.fromstring(self._stream.read(length*elementSize))
211 return arr
212
213 def _writeU8(self, x):
214 """Write the given value as an unsigned, 8-bit value."""
215 self._stream.write(struct.pack("B", x))
216
217 def _writeS32(self, x):
218 """Write the given value as a signed, 32-bit value."""
219 self._stream.write(struct.pack("i", x))
220
221 def _writeU32(self, x):
222 """Write the given value as an unsigned, 32-bit value."""
223 self._stream.write(struct.pack("I", x))
224
225 def _writeFloat(self, x):
226 """Write the given value as a single-precision floating point."""
227 self._stream.write(struct.pack("f", x))
228
229 def _writeDouble(self, x):
230 """Write the given value as a double-precision floating point."""
231 self._stream.write(struct.pack("d", x))
232
233 def _writeLength(self, length):
234 """Write the given value is a variable-length value into our stream."""
235 self._stream.write(self._packLength(length))
236
237 def _writeString(self, str):
238 """Write the given string into the stream."""
239 self._stream.write(self._packString(str))
240
241 def _flush(self):
242 """Flush our stream."""
243 self._stream.flush()
244
245 def _readU8(self):
246 """Read an unsigned, 8-bit value from the stream."""
247 (value,) = struct.unpack("B", self._stream.read(1))
248 return value
249
250 def _readS32(self):
251 """Read a signed, 32-bit value from the stream."""
252 (value,) = struct.unpack("i", self._stream.read(4))
253 return value
254
255 def _readU32(self):
256 """Read an unsigned, 32-bit value from the stream."""
257 (value,) = struct.unpack("I", self._stream.read(4))
258 return value
259
260 def _readFloat(self):
261 """Read a single-precision floating point value from the stream."""
262 (value,) = struct.unpack("f", self._stream.read(4))
263 return value
264
265 def _readDouble(self):
266 """Read a double-precision floating point value from the stream."""
267 (value,) = struct.unpack("d", self._stream.read(8))
268 return value
269
270 def _readLength(self):
271 """Read a variable-length value from our stream."""
272 length = 0
273 while True:
274 (x,) = struct.unpack("B", self._stream.read(1))
275 length <<= 7
276 length |= x&0x7f
277 if x&0x80==0:
278 return length
279
280 def _readString(self):
281 """Read a string from our stream."""
282 length = self._readLength()
283 return self._stream.read(length)
284
285#-------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.