X-Plane Remote Access Plugin and Client Library
XPlane.cc
1 // Copyright (c) 2013 by István Váradi
2 
3 // This file is part of XPLRA, a remote-access plugin for X-Plane
4 
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 
8 // 1. Redistributions of source code must retain the above copyright notice, this
9 // list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 
14 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
18 // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 
25 // The views and conclusions contained in the software and documentation are those
26 // of the authors and should not be interpreted as representing official policies,
27 // either expressed or implied, of the FreeBSD Project.
28 
29 //------------------------------------------------------------------------------
30 
31 #include "XPlane.h"
32 
33 #include "MultiGetter.h"
34 #include "MultiSetter.h"
35 
36 #include <hu/varadiistvan/scpl/io/LocalClientSocket.h>
37 #include <hu/varadiistvan/scpl/io/TCPClientSocket.h>
38 #include <hu/varadiistvan/scpl/io/DataStream.h>
39 
40 #include <xplra/Protocol.h>
41 
42 #include <memory>
43 
44 #ifndef _WIN32
45 #include <signal.h>
46 #endif
47 
48 //------------------------------------------------------------------------------
49 
53 
54 using hu::varadiistvan::scpl::io::LocalClientSocket;
55 using hu::varadiistvan::scpl::io::TCPClientSocket;
56 using hu::varadiistvan::scpl::io::DataStream;
57 
58 using xplra::Protocol;
59 
60 using std::unique_ptr;
61 using std::string;
62 using std::min;
63 
64 //------------------------------------------------------------------------------
65 
66 const unsigned short XPlane::defaultTCPPort;
67 
68 //------------------------------------------------------------------------------
69 
70 template <class ClientSocket>
71 void XPlane::connect(std::unique_ptr<ClientSocket> clientSocket)
72 {
73  auto& connector = clientSocket->getConnector();
74 
75  while (!connector.connect()) {
76  if (connector.failed()) {
77  throw IOException(connector.getErrorCode());
78  }
79  waiter.wait();
80  if (waiter.failed()) {
81  throw IOException(waiter.getErrorCode());
82  }
83  }
84 
85 #ifndef _WIN32
86  signal(SIGPIPE, SIG_IGN);
87 #endif
88 
89  socket = clientSocket.release();
90  stream = new DataStream(*socket);
91 
92  for(multiBuffers_t::iterator i = multiBuffers.begin();
93  i!=multiBuffers.end(); ++i)
94  {
95  MultiBuffer* buffer = *i;
96  buffer->reregisterInXPlane();
97  }
98 }
99 
100 //------------------------------------------------------------------------------
101 
103 {
104  if (stream==0) {
105  throw NotConnectedException();
106  } else if (stream->failed()) {
107  throw IOException(stream->getErrorCode());
108  }
109 }
110 
111 //------------------------------------------------------------------------------
112 
113 void XPlane::checkResult(uint8_t result, bool hasParameter, long parameter)
114 {
115  switch(result) {
116  case Protocol::RESULT_OK:
117  return;
118  case Protocol::RESULT_INVALID_COMMAND:
120  hasParameter, parameter);
121  case Protocol::RESULT_UNKNOWN_DATAREF:
123  hasParameter, parameter);
124  case Protocol::RESULT_INVALID_TYPE:
126  hasParameter, parameter);
127  case Protocol::RESULT_INVALID_LENGTH:
129  hasParameter, parameter);
130  case Protocol::RESULT_INVALID_OFFSET:
132  hasParameter, parameter);
133  case Protocol::RESULT_INVALID_COUNT:
135  hasParameter, parameter);
136  case Protocol::RESULT_INVALID_ID:
138  hasParameter, parameter);
139  case Protocol::RESULT_INVALID_DURATION:
141  hasParameter, parameter);
142  case Protocol::RESULT_OTHER_ERROR:
143  default:
145  hasParameter, parameter);
146  }
147 }
148 
149 //------------------------------------------------------------------------------
150 
151 void XPlane::checkResult(bool multi)
152 {
153  uint8_t result = stream->readU8();
154  bool hasParameter = false;
155  long parameter = 0;
156  if (multi) {
157  if (result==Protocol::RESULT_UNKNOWN_DATAREF) {
158  parameter = stream->readU32();
159  hasParameter = true;
160  }
161  }
162  checkStream();
163  checkResult(result, hasParameter, parameter);
164 }
165 
166 //------------------------------------------------------------------------------
167 //------------------------------------------------------------------------------
168 
169 XPlane::~XPlane() noexcept
170 {
171  disconnect();
172  for(multiBuffers_t::iterator i = multiBuffers.begin();
173  i!=multiBuffers.end(); ++i)
174  {
175  MultiBuffer* buffer = *i;
176  buffer->forgetRegistration();
177  delete buffer;
178  }
179 }
180 
181 //------------------------------------------------------------------------------
182 
184 {
185  if (socket!=0) return;
186 
187  unique_ptr<LocalClientSocket> clientSocket(new LocalClientSocket("xplra",
188  &waiter));
189  connect(std::move(clientSocket));
190 }
191 
192 //------------------------------------------------------------------------------
193 
194 void XPlane::connectTCP(const std::string& address, unsigned short port)
195 {
196  if (socket!=0) return;
197 
198  unique_ptr<TCPClientSocket> clientSocket(new TCPClientSocket(address.c_str(),
199  port,
200  &waiter));
201  connect(std::move(clientSocket));
202 }
203 
204 //------------------------------------------------------------------------------
205 
206 void XPlane::disconnect() noexcept
207 {
208  if (socket==0) return;
209 
210  delete stream; stream = 0;
211  delete socket; socket = 0;
212 }
213 
214 //------------------------------------------------------------------------------
215 
217 {
218  MultiGetter* getter = new MultiGetter(*this);
219  multiBuffers.insert(getter);
220  return *getter;
221 }
222 
223 //------------------------------------------------------------------------------
224 
226 {
227  MultiSetter* setter = new MultiSetter(*this);
228  multiBuffers.insert(setter);
229  return *setter;
230 }
231 
232 //------------------------------------------------------------------------------
233 
235 {
236  multiBuffers_t::iterator i = multiBuffers.find(&buffer);
237  if (i==multiBuffers.end()) return false;
238 
239  multiBuffers.erase(i);
240  delete &buffer;
241 
242  return true;
243 }
244 
245 //------------------------------------------------------------------------------
246 
247 void XPlane::getVersions(int& xplaneVersion, int& xplmVersion, int& xplraVersion)
248 {
249  stream->writeU8(Protocol::COMMAND_GET_VERSIONS);
250  stream->flush();
251  checkResult();
252 
253  xplaneVersion = stream->readS32();
254  xplmVersion = stream->readS32();
255  xplraVersion = stream->readS32();
256 
257  checkStream();
258 }
259 
260 //------------------------------------------------------------------------------
261 
263 {
264  stream->writeU8(Protocol::COMMAND_RELOAD_PLUGINS);
265  stream->flush();
266  checkResult();
267 }
268 
269 //------------------------------------------------------------------------------
270 
271 void XPlane::saveSituation(const char* path)
272 {
273  stream->writeU8(Protocol::COMMAND_SAVE_SITUATION);
274  stream->writeString(path);
275  stream->flush();
276  checkResult();
277 }
278 
279 //------------------------------------------------------------------------------
280 
281 void XPlane::getScalar(const char* name, uint8_t type)
282 {
283  checkStream();
284 
285  stream->writeU8(Protocol::COMMAND_GET_SINGLE);
286  stream->writeString(name, strlen(name));
287  stream->writeU8(type);
288  if (!stream->flush()) checkStream();
289 
290  checkResult();
291 }
292 
293 //------------------------------------------------------------------------------
294 
295 int XPlane::getInt(const char* name)
296 {
297  getScalar(name, Protocol::TYPE_INT);
298 
299  int value = stream->readS32();
300  checkStream();
301  return value;
302 }
303 
304 //------------------------------------------------------------------------------
305 
306 float XPlane::getFloat(const char* name)
307 {
308  getScalar(name, Protocol::TYPE_FLOAT);
309 
310  float value = stream->readFloat();
311  checkStream();
312  return value;
313 }
314 
315 //------------------------------------------------------------------------------
316 
317 double XPlane::getDouble(const char* name)
318 {
319  getScalar(name, Protocol::TYPE_DOUBLE);
320 
321  double value = stream->readDouble();
322  checkStream();
323  return value;
324 }
325 
326 //------------------------------------------------------------------------------
327 
328 size_t XPlane::getArray(const char* name, uint8_t type,
329  ssize_t length, size_t offset)
330 {
331  checkStream();
332 
333  stream->writeU8(Protocol::COMMAND_GET_SINGLE);
334  stream->writeString(name, strlen(name));
335  stream->writeU8(type);
336  stream->writeS32(static_cast<int32_t>(length));
337  stream->writeS32(static_cast<int32_t>(offset));
338  if (!stream->flush()) checkStream();
339 
340  uint8_t result = stream->readU8();
341  length = stream->readS32();
342  checkStream();
343  checkResult(result);
344 
345  return length;
346 }
347 
348 //------------------------------------------------------------------------------
349 
350 size_t XPlane::getFloatArray(const char* name, float* dest,
351  size_t length, size_t offset)
352 {
353  length = getArray(name, Protocol::TYPE_FLOAT_ARRAY, length, offset);
354 
355  if (!stream->read(dest, length*sizeof(float))) checkStream();
356 
357  return length;
358 }
359 
360 //------------------------------------------------------------------------------
361 
362 float* XPlane::getFloatArray(const char* name, size_t& length,
363  size_t offset)
364 {
365  length = getArray(name, Protocol::TYPE_FLOAT_ARRAY, -1, offset);
366 
367  unique_ptr<float[]> data(new float[length]);
368  if (!stream->read(data.get(), length*sizeof(float))) checkStream();
369  return data.release();
370 }
371 
372 //------------------------------------------------------------------------------
373 
374 size_t XPlane::getIntArray(const char* name, int32_t* dest,
375  size_t length, size_t offset)
376 {
377  length = getArray(name, Protocol::TYPE_INT_ARRAY, length, offset);
378 
379  if (!stream->read(dest, length*sizeof(int32_t))) checkStream();
380 
381  return length;
382 }
383 
384 //------------------------------------------------------------------------------
385 
386 int32_t* XPlane::getIntArray(const char* name, size_t& length,
387  size_t offset)
388 {
389  length = getArray(name, Protocol::TYPE_INT_ARRAY, -1, offset);
390 
391  unique_ptr<int32_t[]> data(new int32_t[length]);
392  if (!stream->read(data.get(), length*sizeof(int32_t))) checkStream();
393  return data.release();
394 }
395 
396 //------------------------------------------------------------------------------
397 
398 size_t XPlane::getByteArray(const char* name, uint8_t* dest,
399  size_t length, size_t offset)
400 {
401  length = getArray(name, Protocol::TYPE_BYTE_ARRAY, length, offset);
402 
403  if (!stream->read(dest, length*sizeof(uint8_t))) checkStream();
404 
405  return length;
406 }
407 
408 //------------------------------------------------------------------------------
409 
410 uint8_t* XPlane::getByteArray(const char* name, size_t& length,
411  size_t offset)
412 {
413  length = getArray(name, Protocol::TYPE_BYTE_ARRAY, -1, offset);
414 
415  unique_ptr<uint8_t[]> data(new uint8_t[length]);
416  if (!stream->read(data.get(), length*sizeof(uint8_t))) checkStream();
417  return data.release();
418 }
419 
420 //------------------------------------------------------------------------------
421 
422 string XPlane::getString(const char* name, size_t offset)
423 {
424  size_t length = 0;
425  unique_ptr<uint8_t[]> data(getByteArray(name, length, offset));
426  return string(reinterpret_cast<char*>(data.get()));
427 }
428 
429 //------------------------------------------------------------------------------
430 
431 void XPlane::setScalar(const char* name, uint8_t type)
432 {
433  stream->writeU8(Protocol::COMMAND_SET_SINGLE);
434  stream->writeString(name, strlen(name));
435  stream->writeU8(type);
436 }
437 
438 //------------------------------------------------------------------------------
439 
440 void XPlane::setInt(const char* name, int value)
441 {
442  setScalar(name, Protocol::TYPE_INT);
443  stream->writeS32(value);
444  stream->flush();
445  checkResult();
446 }
447 
448 //------------------------------------------------------------------------------
449 
450 void XPlane::setFloat(const char* name, float value)
451 {
452  setScalar(name, Protocol::TYPE_FLOAT);
453  stream->writeFloat(value);
454  stream->flush();
455  checkResult();
456 }
457 
458 //------------------------------------------------------------------------------
459 
460 void XPlane::setDouble(const char* name, double value)
461 {
462  setScalar(name, Protocol::TYPE_DOUBLE);
463  stream->writeDouble(value);
464  stream->flush();
465  checkResult();
466 }
467 
468 //------------------------------------------------------------------------------
469 
470 void XPlane::setArray(const char* name, uint8_t type, size_t length,
471  size_t offset)
472 {
473  stream->writeU8(Protocol::COMMAND_SET_SINGLE);
474  stream->writeString(name, strlen(name));
475  stream->writeU8(type);
476  stream->writeS32(static_cast<int32_t>(length));
477  stream->writeS32(static_cast<int32_t>(offset));
478 }
479 
480 //------------------------------------------------------------------------------
481 
482 void XPlane::setFloatArray(const char* name, const float* values, size_t length,
483  size_t offset)
484 {
485  setArray(name, Protocol::TYPE_FLOAT_ARRAY, length, offset);
486  stream->write(values, length*sizeof(*values));
487  stream->flush();
488  checkResult();
489 }
490 
491 //------------------------------------------------------------------------------
492 
493 void XPlane::setIntArray(const char* name, const int32_t* values, size_t length,
494  size_t offset)
495 {
496  setArray(name, Protocol::TYPE_INT_ARRAY, length, offset);
497  stream->write(values, length*sizeof(*values));
498  stream->flush();
499  checkResult();
500 }
501 
502 //------------------------------------------------------------------------------
503 
504 void XPlane::setByteArray(const char* name, const uint8_t* values, size_t length,
505  size_t offset)
506 {
507  setArray(name, Protocol::TYPE_BYTE_ARRAY, length, offset);
508  stream->write(values, length*sizeof(*values));
509  stream->flush();
510  checkResult();
511 }
512 
513 //------------------------------------------------------------------------------
514 
515 void XPlane::setString(const char* name, const char* value, size_t length,
516  size_t offset)
517 {
518  size_t valueLength = strlen(value);
519  if ((valueLength+1)>=length) {
520  setByteArray(name, reinterpret_cast<const uint8_t*>(value),
521  length, offset);
522  } else {
523  unique_ptr<uint8_t[]> buffer(new uint8_t[length]);
524  memcpy(buffer.get(), value, valueLength);
525  memset(buffer.get() + valueLength, 0, length - valueLength);
526  setByteArray(name, buffer.get(), length, offset);
527  }
528 }
529 
530 //------------------------------------------------------------------------------
531 
532 void XPlane::showMessage(const char* message, float duration)
533 {
534  stream->writeU8(Protocol::COMMAND_SHOW_MESSAGE);
535  stream->writeString(message);
536  stream->writeFloat(duration);
537  stream->flush();
538  checkResult();
539 }
540 
541 //------------------------------------------------------------------------------
542 
543 void XPlane::registerHotkeys(const uint16_t* codes, size_t length)
544 
545 {
546  stream->writeU8(Protocol::COMMAND_REGISTER_HOTKEYS);
547  stream->writeU32(length);
548  stream->write(codes, sizeof(uint16_t)*length);
549  stream->flush();
550  checkResult();
551 }
552 
553 //------------------------------------------------------------------------------
554 
555 void XPlane::queryHotkeys(uint8_t* states, size_t length)
556 {
557  stream->writeU8(Protocol::COMMAND_QUERY_HOTKEYS);
558  stream->flush();
559  checkResult();
560 
561  size_t count = stream->readU32();
562  checkStream();
563  stream->read(states, min(length, count));
564  if (length<count) {
565  while(length<count) {
566  stream->readU8();
567  ++length;
568  }
569  } else if (length>count) {
570  memset(states + count, 0, length - count);
571  }
572  checkStream();
573 }
574 
575 //------------------------------------------------------------------------------
576 
578 {
579  stream->writeU8(Protocol::COMMAND_UNREGISTER_HOTKEYS);
580  stream->flush();
581  checkResult();
582 }
583 
584 //------------------------------------------------------------------------------
585 
586 // Local Variables:
587 // mode: C++
588 // c-basic-offset: 4
589 // indent-tabs-mode: nil
590 // End:
@ INVALID_COMMAND
Invalid command issued.
Definition: Exception.h:125
void setArray(const char *name, uint8_t type, size_t length, size_t offset)
Definition: XPlane.cc:470
void registerHotkeys(const uint16_t *codes, size_t length)
Definition: XPlane.cc:543
int getInt(const char *name)
Definition: XPlane.cc:295
size_t getIntArray(const char *name, int32_t *dest, size_t length, size_t offset=0)
Definition: XPlane.cc:374
void getScalar(const char *name, uint8_t type)
Definition: XPlane.cc:281
size_t getByteArray(const char *name, uint8_t *dest, size_t length, size_t offset=0)
Definition: XPlane.cc:398
bool destroyMultiBuffer(MultiBuffer &buffer)
Definition: XPlane.cc:234
void setInt(const char *name, int value)
Definition: XPlane.cc:440
size_t getArray(const char *name, uint8_t type, ssize_t length, size_t offset)
Definition: XPlane.cc:328
float getFloat(const char *name)
Definition: XPlane.cc:306
void disconnect() noexcept
Definition: XPlane.cc:206
multiBuffers_t multiBuffers
Definition: XPlane.h:99
void queryHotkeys(uint8_t *states, size_t length)
Definition: XPlane.cc:555
scpl::io::BufferedStream * socket
Definition: XPlane.h:89
MultiGetter & createMultiGetter() noexcept
Definition: XPlane.cc:216
size_t getFloatArray(const char *name, float *dest, size_t length, size_t offset=0)
Definition: XPlane.cc:350
void showMessage(const char *message, float duration)
Definition: XPlane.cc:532
void setDouble(const char *name, double value)
Definition: XPlane.cc:460
void setFloatArray(const char *name, const float *values, size_t length, size_t offset=0)
Definition: XPlane.cc:482
scpl::io::DataStream * stream
Definition: XPlane.h:94
void saveSituation(const char *path)
Definition: XPlane.cc:271
void setScalar(const char *name, uint8_t type)
Definition: XPlane.cc:431
scpl::io::Waiter waiter
Definition: XPlane.h:84
void setIntArray(const char *name, const int32_t *values, size_t length, size_t offset=0)
Definition: XPlane.cc:493
void connectTCP(const std::string &address, unsigned short port=defaultTCPPort)
Definition: XPlane.cc:194
void checkResult(uint8_t result, bool hasParameter=false, long parameter=0)
Definition: XPlane.cc:113
void setString(const char *name, const char *value, size_t length, size_t offset=0)
Definition: XPlane.cc:515
void setByteArray(const char *name, const uint8_t *values, size_t length, size_t offset=0)
Definition: XPlane.cc:504
double getDouble(const char *name)
Definition: XPlane.cc:317
void getVersions(int &xplaneVersion, int &xplmVersion, int &xplraVersion)
Definition: XPlane.cc:247
MultiSetter & createMultiSetter() noexcept
Definition: XPlane.cc:225
std::string getString(const char *name, size_t offset=0)
Definition: XPlane.cc:422
void setFloat(const char *name, float value)
Definition: XPlane.cc:450