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
Line 
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
50using hu::varadiistvan::xplra::XPlane;
51using hu::varadiistvan::xplra::MultiGetter;
52using hu::varadiistvan::xplra::MultiSetter;
53
54using hu::varadiistvan::scpl::io::LocalClientSocket;
55using hu::varadiistvan::scpl::io::TCPClientSocket;
56using hu::varadiistvan::scpl::io::DataStream;
57
58using xplra::Protocol;
59
60using std::unique_ptr;
61using std::string;
62using std::min;
63
64//------------------------------------------------------------------------------
65
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
102void XPlane::checkStream()
103{
104 if (stream==0) {
105 throw NotConnectedException();
106 } else if (stream->failed()) {
107 throw IOException(stream->getErrorCode());
108 }
109}
110
111//------------------------------------------------------------------------------
112
113void 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:
119 throw ProtocolException(ProtocolException::INVALID_COMMAND,
120 hasParameter, parameter);
121 case Protocol::RESULT_UNKNOWN_DATAREF:
122 throw ProtocolException(ProtocolException::UNKNOWN_DATAREF,
123 hasParameter, parameter);
124 case Protocol::RESULT_INVALID_TYPE:
125 throw ProtocolException(ProtocolException::INVALID_TYPE,
126 hasParameter, parameter);
127 case Protocol::RESULT_INVALID_LENGTH:
128 throw ProtocolException(ProtocolException::INVALID_LENGTH,
129 hasParameter, parameter);
130 case Protocol::RESULT_INVALID_OFFSET:
131 throw ProtocolException(ProtocolException::INVALID_OFFSET,
132 hasParameter, parameter);
133 case Protocol::RESULT_INVALID_COUNT:
134 throw ProtocolException(ProtocolException::INVALID_COUNT,
135 hasParameter, parameter);
136 case Protocol::RESULT_INVALID_ID:
137 throw ProtocolException(ProtocolException::INVALID_ID,
138 hasParameter, parameter);
139 case Protocol::RESULT_INVALID_DURATION:
140 throw ProtocolException(ProtocolException::INVALID_DURATION,
141 hasParameter, parameter);
142 case Protocol::RESULT_OTHER_ERROR:
143 default:
144 throw ProtocolException(ProtocolException::OTHER,
145 hasParameter, parameter);
146 }
147}
148
149//------------------------------------------------------------------------------
150
151void 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
169XPlane::~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
183void XPlane::connect()
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
194void 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
206void XPlane::disconnect() noexcept
207{
208 if (socket==0) return;
209
210 delete stream; stream = 0;
211 delete socket; socket = 0;
212}
213
214//------------------------------------------------------------------------------
215
216MultiGetter& XPlane::createMultiGetter() noexcept
217{
218 MultiGetter* getter = new MultiGetter(*this);
219 multiBuffers.insert(getter);
220 return *getter;
221}
222
223//------------------------------------------------------------------------------
224
225MultiSetter& XPlane::createMultiSetter() noexcept
226{
227 MultiSetter* setter = new MultiSetter(*this);
228 multiBuffers.insert(setter);
229 return *setter;
230}
231
232//------------------------------------------------------------------------------
233
234bool XPlane::destroyMultiBuffer(MultiBuffer& buffer)
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
247void 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
262void XPlane::reloadPlugins()
263{
264 stream->writeU8(Protocol::COMMAND_RELOAD_PLUGINS);
265 stream->flush();
266 checkResult();
267}
268
269//------------------------------------------------------------------------------
270
271void 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
281void 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
295int 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
306float 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
317double 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
328size_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
350size_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
362float* 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
374size_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
386int32_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
398size_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
410uint8_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
422string 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
431void 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
440void 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
450void 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
460void 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
470void 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
482void 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
493void 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
504void 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
515void 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
532void 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
543void 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
555void 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
577void XPlane::unregisterHotkeys()
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:
Note: See TracBrowser for help on using the repository browser.