source: xplra/src/client/c/hu/varadiistvan/xplra/XPlane.cc@ 110:9ac6386fe9ff

Last change on this file since 110:9ac6386fe9ff was 110:9ac6386fe9ff, checked in by István Váradi <ivaradi@…>, 17 months ago

The C++ client API can connect over TCP

File size: 17.6 KB
RevLine 
[13]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
[26]33#include "MultiGetter.h"
34#include "MultiSetter.h"
35
[14]36#include <hu/varadiistvan/scpl/io/LocalClientSocket.h>
[110]37#include <hu/varadiistvan/scpl/io/TCPClientSocket.h>
[14]38#include <hu/varadiistvan/scpl/io/DataStream.h>
39
40#include <xplra/Protocol.h>
41
42#include <memory>
43
[30]44#ifndef _WIN32
45#include <signal.h>
46#endif
47
[13]48//------------------------------------------------------------------------------
49
50using hu::varadiistvan::xplra::XPlane;
[26]51using hu::varadiistvan::xplra::MultiGetter;
52using hu::varadiistvan::xplra::MultiSetter;
[13]53
[14]54using hu::varadiistvan::scpl::io::LocalClientSocket;
[110]55using hu::varadiistvan::scpl::io::TCPClientSocket;
[14]56using hu::varadiistvan::scpl::io::DataStream;
57
58using xplra::Protocol;
59
[92]60using std::unique_ptr;
[16]61using std::string;
[54]62using std::min;
[14]63
[13]64//------------------------------------------------------------------------------
[14]65
[110]66const unsigned short XPlane::defaultTCPPort;
67
68//------------------------------------------------------------------------------
69
70template <class ClientSocket>
71void 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
[107]102void XPlane::checkStream()
[14]103{
104 if (stream==0) {
105 throw NotConnectedException();
106 } else if (stream->failed()) {
107 throw IOException(stream->getErrorCode());
108 }
109}
110
111//------------------------------------------------------------------------------
112
[107]113void XPlane::checkResult(uint8_t result, bool hasParameter, long parameter)
[14]114{
115 switch(result) {
116 case Protocol::RESULT_OK:
117 return;
118 case Protocol::RESULT_INVALID_COMMAND:
[40]119 throw ProtocolException(ProtocolException::INVALID_COMMAND,
120 hasParameter, parameter);
[14]121 case Protocol::RESULT_UNKNOWN_DATAREF:
[40]122 throw ProtocolException(ProtocolException::UNKNOWN_DATAREF,
123 hasParameter, parameter);
[14]124 case Protocol::RESULT_INVALID_TYPE:
[40]125 throw ProtocolException(ProtocolException::INVALID_TYPE,
126 hasParameter, parameter);
[14]127 case Protocol::RESULT_INVALID_LENGTH:
[40]128 throw ProtocolException(ProtocolException::INVALID_LENGTH,
129 hasParameter, parameter);
[14]130 case Protocol::RESULT_INVALID_OFFSET:
[40]131 throw ProtocolException(ProtocolException::INVALID_OFFSET,
132 hasParameter, parameter);
[14]133 case Protocol::RESULT_INVALID_COUNT:
[40]134 throw ProtocolException(ProtocolException::INVALID_COUNT,
135 hasParameter, parameter);
[14]136 case Protocol::RESULT_INVALID_ID:
[40]137 throw ProtocolException(ProtocolException::INVALID_ID,
138 hasParameter, parameter);
139 case Protocol::RESULT_INVALID_DURATION:
140 throw ProtocolException(ProtocolException::INVALID_DURATION,
141 hasParameter, parameter);
[14]142 case Protocol::RESULT_OTHER_ERROR:
143 default:
[40]144 throw ProtocolException(ProtocolException::OTHER,
145 hasParameter, parameter);
[14]146 }
147}
148
149//------------------------------------------------------------------------------
[17]150
[107]151void XPlane::checkResult(bool multi)
[17]152{
153 uint8_t result = stream->readU8();
[40]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 }
[17]162 checkStream();
[40]163 checkResult(result, hasParameter, parameter);
[17]164}
165
166//------------------------------------------------------------------------------
[14]167//------------------------------------------------------------------------------
168
[107]169XPlane::~XPlane() noexcept
[14]170{
171 disconnect();
[26]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 }
[14]179}
180
181//------------------------------------------------------------------------------
182
[107]183void XPlane::connect()
[14]184{
185 if (socket!=0) return;
186
[92]187 unique_ptr<LocalClientSocket> clientSocket(new LocalClientSocket("xplra",
188 &waiter));
[110]189 connect(std::move(clientSocket));
190}
[14]191
[110]192//------------------------------------------------------------------------------
[14]193
[110]194void XPlane::connectTCP(const std::string& address, unsigned short port)
195{
196 if (socket!=0) return;
[57]197
[110]198 unique_ptr<TCPClientSocket> clientSocket(new TCPClientSocket(address.c_str(),
199 port,
200 &waiter));
201 connect(std::move(clientSocket));
[14]202}
203
204//------------------------------------------------------------------------------
205
[107]206void XPlane::disconnect() noexcept
[14]207{
208 if (socket==0) return;
209
210 delete stream; stream = 0;
211 delete socket; socket = 0;
212}
213
214//------------------------------------------------------------------------------
215
[107]216MultiGetter& XPlane::createMultiGetter() noexcept
[26]217{
218 MultiGetter* getter = new MultiGetter(*this);
219 multiBuffers.insert(getter);
220 return *getter;
221}
222
223//------------------------------------------------------------------------------
224
[107]225MultiSetter& XPlane::createMultiSetter() noexcept
[26]226{
227 MultiSetter* setter = new MultiSetter(*this);
228 multiBuffers.insert(setter);
229 return *setter;
230}
231
232//------------------------------------------------------------------------------
233
[107]234bool XPlane::destroyMultiBuffer(MultiBuffer& buffer)
[26]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
[107]247void XPlane::getVersions(int& xplaneVersion, int& xplmVersion, int& xplraVersion)
[36]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
[107]262void XPlane::reloadPlugins()
[40]263{
264 stream->writeU8(Protocol::COMMAND_RELOAD_PLUGINS);
265 stream->flush();
266 checkResult();
267}
268
269//------------------------------------------------------------------------------
270
[107]271void XPlane::saveSituation(const char* path)
[87]272{
273 stream->writeU8(Protocol::COMMAND_SAVE_SITUATION);
274 stream->writeString(path);
275 stream->flush();
276 checkResult();
277}
278
279//------------------------------------------------------------------------------
280
[107]281void XPlane::getScalar(const char* name, uint8_t type)
[14]282{
283 checkStream();
284
285 stream->writeU8(Protocol::COMMAND_GET_SINGLE);
286 stream->writeString(name, strlen(name));
[15]287 stream->writeU8(type);
[14]288 if (!stream->flush()) checkStream();
289
[17]290 checkResult();
[15]291}
292
293//------------------------------------------------------------------------------
294
[107]295int XPlane::getInt(const char* name)
[15]296{
297 getScalar(name, Protocol::TYPE_INT);
[14]298
299 int value = stream->readS32();
300 checkStream();
301 return value;
302}
303
[13]304//------------------------------------------------------------------------------
305
[107]306float XPlane::getFloat(const char* name)
[15]307{
308 getScalar(name, Protocol::TYPE_FLOAT);
309
310 float value = stream->readFloat();
311 checkStream();
312 return value;
313}
314
315//------------------------------------------------------------------------------
316
[107]317double XPlane::getDouble(const char* name)
[15]318{
319 getScalar(name, Protocol::TYPE_DOUBLE);
320
321 double value = stream->readDouble();
322 checkStream();
323 return value;
324}
325
326//------------------------------------------------------------------------------
327
[16]328size_t XPlane::getArray(const char* name, uint8_t type,
[107]329 ssize_t length, size_t offset)
[16]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
350size_t XPlane::getFloatArray(const char* name, float* dest,
[107]351 size_t length, size_t offset)
[16]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
362float* XPlane::getFloatArray(const char* name, size_t& length,
[107]363 size_t offset)
[16]364{
365 length = getArray(name, Protocol::TYPE_FLOAT_ARRAY, -1, offset);
366
[92]367 unique_ptr<float[]> data(new float[length]);
[16]368 if (!stream->read(data.get(), length*sizeof(float))) checkStream();
369 return data.release();
370}
371
372//------------------------------------------------------------------------------
373
374size_t XPlane::getIntArray(const char* name, int32_t* dest,
[107]375 size_t length, size_t offset)
[16]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
386int32_t* XPlane::getIntArray(const char* name, size_t& length,
[107]387 size_t offset)
[16]388{
389 length = getArray(name, Protocol::TYPE_INT_ARRAY, -1, offset);
390
[92]391 unique_ptr<int32_t[]> data(new int32_t[length]);
[16]392 if (!stream->read(data.get(), length*sizeof(int32_t))) checkStream();
393 return data.release();
394}
395
396//------------------------------------------------------------------------------
397
398size_t XPlane::getByteArray(const char* name, uint8_t* dest,
[107]399 size_t length, size_t offset)
[16]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
410uint8_t* XPlane::getByteArray(const char* name, size_t& length,
[107]411 size_t offset)
[16]412{
413 length = getArray(name, Protocol::TYPE_BYTE_ARRAY, -1, offset);
414
[92]415 unique_ptr<uint8_t[]> data(new uint8_t[length]);
[16]416 if (!stream->read(data.get(), length*sizeof(uint8_t))) checkStream();
417 return data.release();
418}
419
420//------------------------------------------------------------------------------
421
[107]422string XPlane::getString(const char* name, size_t offset)
[16]423{
424 size_t length = 0;
[92]425 unique_ptr<uint8_t[]> data(getByteArray(name, length, offset));
[16]426 return string(reinterpret_cast<char*>(data.get()));
427}
428
429//------------------------------------------------------------------------------
430
[107]431void XPlane::setScalar(const char* name, uint8_t type)
[17]432{
433 stream->writeU8(Protocol::COMMAND_SET_SINGLE);
434 stream->writeString(name, strlen(name));
435 stream->writeU8(type);
436}
437
438//------------------------------------------------------------------------------
439
[107]440void XPlane::setInt(const char* name, int value)
[17]441{
442 setScalar(name, Protocol::TYPE_INT);
443 stream->writeS32(value);
444 stream->flush();
445 checkResult();
446}
447
448//------------------------------------------------------------------------------
449
[107]450void XPlane::setFloat(const char* name, float value)
[17]451{
452 setScalar(name, Protocol::TYPE_FLOAT);
453 stream->writeFloat(value);
454 stream->flush();
455 checkResult();
456}
457
458//------------------------------------------------------------------------------
459
[107]460void XPlane::setDouble(const char* name, double value)
[17]461{
462 setScalar(name, Protocol::TYPE_DOUBLE);
463 stream->writeDouble(value);
464 stream->flush();
465 checkResult();
466}
467
468//------------------------------------------------------------------------------
469
[18]470void XPlane::setArray(const char* name, uint8_t type, size_t length,
[107]471 size_t offset)
[18]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
482void XPlane::setFloatArray(const char* name, const float* values, size_t length,
[107]483 size_t offset)
[18]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
493void XPlane::setIntArray(const char* name, const int32_t* values, size_t length,
[107]494 size_t offset)
[18]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
504void XPlane::setByteArray(const char* name, const uint8_t* values, size_t length,
[107]505 size_t offset)
[18]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
515void XPlane::setString(const char* name, const char* value, size_t length,
[107]516 size_t offset)
[18]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 {
[92]523 unique_ptr<uint8_t[]> buffer(new uint8_t[length]);
[18]524 memcpy(buffer.get(), value, valueLength);
525 memset(buffer.get() + valueLength, 0, length - valueLength);
526 setByteArray(name, buffer.get(), length, offset);
527 }
528}
[40]529
530//------------------------------------------------------------------------------
531
[107]532void XPlane::showMessage(const char* message, float duration)
[40]533{
534 stream->writeU8(Protocol::COMMAND_SHOW_MESSAGE);
535 stream->writeString(message);
536 stream->writeFloat(duration);
537 stream->flush();
538 checkResult();
539}
540
[18]541//------------------------------------------------------------------------------
542
[54]543void XPlane::registerHotkeys(const uint16_t* codes, size_t length)
[107]544
[54]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
[107]555void XPlane::queryHotkeys(uint8_t* states, size_t length)
[54]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
577void XPlane::unregisterHotkeys()
578{
579 stream->writeU8(Protocol::COMMAND_UNREGISTER_HOTKEYS);
580 stream->flush();
581 checkResult();
582}
583
584//------------------------------------------------------------------------------
585
[13]586// Local Variables:
587// mode: C++
588// c-basic-offset: 4
589// indent-tabs-mode: nil
590// End:
Note: See TracBrowser for help on using the repository browser.