1 // generic.cxx -- generic protocol class
3 // Written by Curtis Olson, started November 1999.
5 // Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27 #include <string.h> // strstr()
28 #include <stdlib.h> // strtod(), atoi()
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/io/iochannel.hxx>
33 #include <simgear/structure/exception.hxx>
34 #include <simgear/misc/sg_path.hxx>
35 #include <simgear/misc/stdint.hxx>
36 #include <simgear/misc/strutils.hxx>
37 #include <simgear/props/props.hxx>
38 #include <simgear/props/props_io.hxx>
39 #include <simgear/math/SGMath.hxx>
41 #include <Main/globals.hxx>
42 #include <Main/fg_props.hxx>
43 #include <Main/fg_os.hxx>
44 #include <Main/util.hxx>
45 #include "generic.hxx"
47 using simgear::strutils::unescape;
49 class FGProtocolWrapper {
51 virtual ~FGProtocolWrapper() {}
52 virtual int wrap( size_t n, uint8_t * buf ) = 0;
53 virtual int unwrap( size_t n, uint8_t * buf ) = 0;
57 * http://www.ka9q.net/papers/kiss.html
59 class FGKissWrapper : public FGProtocolWrapper {
61 virtual int wrap( size_t n, uint8_t * buf );
62 virtual int unwrap( size_t n, uint8_t * buf );
64 static const uint8_t FEND;
65 static const uint8_t FESC;
66 static const uint8_t TFEND;
67 static const uint8_t TFESC;
70 const uint8_t FGKissWrapper::FEND = 0xC0;
71 const uint8_t FGKissWrapper::FESC = 0xDB;
72 const uint8_t FGKissWrapper::TFEND = 0xDC;
73 const uint8_t FGKissWrapper::TFESC = 0xDD;
75 int FGKissWrapper::wrap( size_t n, uint8_t * buf )
77 std::vector<uint8_t> dest;
81 dest.push_back(0); // command/channel always zero
82 for( size_t i = 0; i < n; i++ ) {
87 dest.push_back(TFESC);
92 dest.push_back(TFEND);
100 dest.push_back(FEND);
102 memcpy( buf, dest.data(), dest.size() );
106 int FGKissWrapper::unwrap( size_t n, uint8_t * buf )
111 while( 0 < n && FEND != *sp ) {
116 // ignore all leading FEND
117 while( 0 < n && FEND == *sp ) {
122 if( 0 == n ) return 0;
124 std::vector<uint8_t> dest;
126 bool escaped = false;
137 dest.push_back( FESC );
141 dest.push_back( FEND );
144 default: // this is an error - ignore and continue
159 SG_LOG(SG_IO, SG_WARN,
160 "KISS frame detected FEND before end of frame. Trailing data dropped." );
173 memcpy( buf, dest.data(), dest.size() );
178 class FGSTXETXWrapper : public FGProtocolWrapper {
180 virtual int wrap( size_t n, uint8_t * buf );
181 virtual int unwrap( size_t n, uint8_t * buf );
183 static const uint8_t STX;
184 static const uint8_t ETX;
185 static const uint8_t DLE;
188 const uint8_t FGSTXETXWrapper::STX = 0x02;
189 const uint8_t FGSTXETXWrapper::ETX = 0x03;
190 const uint8_t FGSTXETXWrapper::DLE = 0x00;
192 int FGSTXETXWrapper::wrap( size_t n, uint8_t * buf )
195 // <dle><stx>payload<dle><etx>
196 // if payload contains <dle>, stuff <dle> as <dle><dle>
197 std::vector<uint8_t> dest;
209 dest.push_back(*sp++);
215 memcpy( buf, dest.data(), dest.size() );
219 int FGSTXETXWrapper::unwrap( size_t n, uint8_t * buf )
224 FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false), initOk(false), wrapper(NULL)
227 if (tokens[1] == "socket") {
229 } else if (tokens[1] == "file") {
235 if ((configToken >= tokens.size())||(tokens[ configToken ] == "")) {
236 SG_LOG(SG_NETWORK, SG_ALERT,
237 "Not enough tokens passed for generic '" << tokens[1] << "' protocol. ");
241 string config = tokens[ configToken ];
242 file_name = config+".xml";
243 direction = tokens[2];
245 if (direction != "in" && direction != "out" && direction != "bi") {
246 SG_LOG(SG_NETWORK, SG_ALERT, "Unsuported protocol direction: "
254 FGGeneric::~FGGeneric() {
268 // generate the message
269 bool FGGeneric::gen_message_binary() {
270 string generic_sentence;
274 for (unsigned int i = 0; i < _out_message.size(); i++) {
276 switch (_out_message[i].type) {
279 val = _out_message[i].offset +
280 _out_message[i].prop->getIntValue() * _out_message[i].factor;
281 int32_t intVal = val;
282 if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
283 intVal = (int32_t) sg_bswap_32((uint32_t)intVal);
285 memcpy(&buf[length], &intVal, sizeof(int32_t));
286 length += sizeof(int32_t);
291 buf[length] = (char) (_out_message[i].prop->getBoolValue() ? true : false);
297 val = _out_message[i].offset +
298 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
300 int32_t fixed = (int)(val * 65536.0f);
301 if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
302 fixed = (int32_t) sg_bswap_32((uint32_t)fixed);
304 memcpy(&buf[length], &fixed, sizeof(int32_t));
305 length += sizeof(int32_t);
311 val = _out_message[i].offset +
312 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
314 tmpun32.floatVal = static_cast<float>(val);
316 if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
317 tmpun32.intVal = sg_bswap_32(tmpun32.intVal);
319 memcpy(&buf[length], &tmpun32.intVal, sizeof(uint32_t));
320 length += sizeof(uint32_t);
326 val = _out_message[i].offset +
327 _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
329 tmpun64.doubleVal = val;
331 if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
332 tmpun64.longVal = sg_bswap_64(tmpun64.longVal);
334 memcpy(&buf[length], &tmpun64.longVal, sizeof(uint64_t));
335 length += sizeof(uint64_t);
341 val = _out_message[i].offset +
342 _out_message[i].prop->getIntValue() * _out_message[i].factor;
343 int8_t byteVal = val;
344 memcpy(&buf[length], &byteVal, sizeof(int8_t));
345 length += sizeof(int8_t);
349 default: // SG_STRING
350 const char *strdata = _out_message[i].prop->getStringValue();
351 int32_t strlength = strlen(strdata);
353 if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
354 SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
355 "FG_STRING will be written in host byte order.");
357 /* Format for strings is
358 * [length as int, 4 bytes][ASCII data, length bytes]
360 if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
361 strlength = sg_bswap_32(strlength);
363 memcpy(&buf[length], &strlength, sizeof(int32_t));
364 length += sizeof(int32_t);
365 strncpy(&buf[length], strdata, strlength);
367 /* FIXME padding for alignment? Something like:
368 * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0;
375 // add the footer to the packet ("line")
376 switch (binary_footer_type) {
378 binary_footer_value = length;
386 if (binary_footer_type != FOOTER_NONE) {
387 int32_t intValue = binary_footer_value;
388 if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
389 intValue = sg_bswap_32(binary_footer_value);
391 memcpy(&buf[length], &intValue, sizeof(int32_t));
392 length += sizeof(int32_t);
395 if( wrapper ) length = wrapper->wrap( length, reinterpret_cast<uint8_t*>(buf) );
400 bool FGGeneric::gen_message_ascii() {
401 string generic_sentence;
406 for (unsigned int i = 0; i < _out_message.size(); i++) {
409 generic_sentence += var_separator;
412 string format = simgear::strutils::sanitizePrintfFormat(_out_message[i].format);
414 switch (_out_message[i].type) {
417 val = _out_message[i].offset +
418 _out_message[i].prop->getIntValue() * _out_message[i].factor;
419 snprintf(tmp, 255, format.c_str(), (int)val);
423 snprintf(tmp, 255, format.c_str(),
424 _out_message[i].prop->getBoolValue());
428 val = _out_message[i].offset +
429 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
430 snprintf(tmp, 255, format.c_str(), (float)val);
434 val = _out_message[i].offset +
435 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
436 snprintf(tmp, 255, format.c_str(), (float)val);
440 val = _out_message[i].offset +
441 _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
442 snprintf(tmp, 255, format.c_str(), (double)val);
445 default: // SG_STRING
446 snprintf(tmp, 255, format.c_str(),
447 _out_message[i].prop->getStringValue());
450 generic_sentence += tmp;
453 /* After each lot of variables has been added, put the line separator
456 generic_sentence += line_separator;
458 length = generic_sentence.length();
459 strncpy( buf, generic_sentence.c_str(), length );
464 bool FGGeneric::gen_message() {
466 return gen_message_binary();
468 return gen_message_ascii();
472 bool FGGeneric::parse_message_binary(int length) {
478 while ((++i < (int)_in_message.size()) && (p1 < p2)) {
480 switch (_in_message[i].type) {
482 if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
483 tmp32 = sg_bswap_32(*(int32_t *)p1);
485 tmp32 = *(int32_t *)p1;
487 updateValue(_in_message[i], (int)tmp32);
488 p1 += sizeof(int32_t);
492 updateValue(_in_message[i], p1[0] != 0);
497 if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
498 tmp32 = sg_bswap_32(*(int32_t *)p1);
500 tmp32 = *(int32_t *)p1;
502 updateValue(_in_message[i], (float)tmp32 / 65536.0f);
503 p1 += sizeof(int32_t);
508 if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
509 tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1);
511 tmpun32.floatVal = *(float *)p1;
513 updateValue(_in_message[i], tmpun32.floatVal);
514 p1 += sizeof(int32_t);
519 if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
520 tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1);
522 tmpun64.doubleVal = *(double *)p1;
524 updateValue(_in_message[i], tmpun64.doubleVal);
525 p1 += sizeof(int64_t);
529 tmp32 = *(int8_t *)p1;
530 updateValue(_in_message[i], (int)tmp32);
531 p1 += sizeof(int8_t);
534 default: // SG_STRING
535 SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
536 "Ignoring unsupported binary input chunk type.");
544 bool FGGeneric::parse_message_ascii(int length) {
547 int chunks = _in_message.size();
548 int line_separator_size = line_separator.size();
550 if (length < line_separator_size ||
551 line_separator.compare(buf + length - line_separator_size) != 0) {
553 SG_LOG(SG_IO, SG_WARN,
554 "Input line does not end with expected line separator." );
556 buf[length - line_separator_size] = 0;
559 size_t varsep_len = var_separator.length();
560 while ((++i < chunks) && p1) {
565 p2 = strstr(p1, var_separator.c_str());
572 switch (_in_message[i].type) {
575 updateValue(_in_message[i], atoi(p1));
579 updateValue(_in_message[i], atof(p1) != 0.0);
584 updateValue(_in_message[i], (float)strtod(p1, 0));
588 updateValue(_in_message[i], (double)strtod(p1, 0));
591 default: // SG_STRING
592 _in_message[i].prop->setStringValue(p1);
602 bool FGGeneric::parse_message_len(int length) {
604 return parse_message_binary(length);
606 return parse_message_ascii(length);
611 // open hailing frequencies
612 bool FGGeneric::open() {
613 if ( is_enabled() ) {
614 SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
615 << "is already in use, ignoring" );
619 SGIOChannel *io = get_io_channel();
621 if ( ! io->open( get_direction() ) ) {
622 SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
628 if ( ((get_direction() == SG_IO_OUT )||
629 (get_direction() == SG_IO_BI))
630 && ! preamble.empty() ) {
631 if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
632 SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
641 // process work for this port
642 bool FGGeneric::process() {
643 SGIOChannel *io = get_io_channel();
645 if ( (get_direction() == SG_IO_OUT) ||
646 (get_direction() == SG_IO_BI) ) {
648 if ( ! io->write( buf, length ) ) {
649 SG_LOG( SG_IO, SG_WARN, "Error writing data." );
654 if (( get_direction() == SG_IO_IN ) ||
655 (get_direction() == SG_IO_BI) ) {
656 if ( io->get_type() == sgFileType ) {
658 length = io->readline( buf, FG_MAX_MSG_SIZE );
660 parse_message_len( length );
662 SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
666 length = io->read( buf, binary_record_length );
667 if ( length == binary_record_length ) {
668 parse_message_len( length );
670 SG_LOG( SG_IO, SG_ALERT,
671 "Generic protocol: Received binary "
672 "record of unexpected size, expected: "
673 << binary_record_length << " but received: "
679 while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
680 parse_message_len( length );
683 while ((length = io->read( buf, binary_record_length ))
684 == binary_record_length ) {
685 parse_message_len( length );
689 SG_LOG( SG_IO, SG_ALERT,
690 "Generic protocol: Received binary "
691 "record of unexpected size, expected: "
692 << binary_record_length << " but received: "
702 return true; // should not get there, but please the compiler
709 bool FGGeneric::close() {
710 SGIOChannel *io = get_io_channel();
712 if ( ((get_direction() == SG_IO_OUT)||
713 (get_direction() == SG_IO_BI))
714 && ! postamble.empty() ) {
715 if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
716 SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
721 set_enabled( false );
723 if ( ! io->close() ) {
734 SGPath path( globals->get_fg_root() );
735 path.append("Protocol");
736 path.append(file_name.c_str());
738 SG_LOG(SG_NETWORK, SG_INFO, "Reading communication protocol from "
743 readProperties(path.str(), &root);
744 } catch (const sg_exception & ex) {
745 SG_LOG(SG_NETWORK, SG_ALERT,
746 "Unable to load the protocol configuration file: " << ex.getFormattedMessage() );
750 if (direction == "out") {
751 SGPropertyNode *output = root.getNode("generic/output");
753 _out_message.clear();
754 if (!read_config(output, _out_message))
760 } else if (direction == "in") {
761 SGPropertyNode *input = root.getNode("generic/input");
764 if (!read_config(input, _in_message))
769 if (!binary_mode && (line_separator.empty() ||
770 *line_separator.rbegin() != '\n')) {
772 SG_LOG(SG_IO, SG_WARN,
773 "Warning: Appending newline to line separator in generic input.");
774 line_separator.push_back('\n');
784 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
786 binary_mode = root->getBoolValue("binary_mode");
789 /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml
790 * file for each format
792 * var_sep_string = the string/charachter to place between variables
793 * line_sep_string = the string/charachter to place at the end of each
796 preamble = unescape(root->getStringValue("preamble"));
797 postamble = unescape(root->getStringValue("postamble"));
798 var_sep_string = unescape(root->getStringValue("var_separator"));
799 line_sep_string = unescape(root->getStringValue("line_separator"));
801 if ( var_sep_string == "newline" ) {
802 var_separator = '\n';
803 } else if ( var_sep_string == "tab" ) {
804 var_separator = '\t';
805 } else if ( var_sep_string == "space" ) {
807 } else if ( var_sep_string == "formfeed" ) {
808 var_separator = '\f';
809 } else if ( var_sep_string == "carriagereturn" ) {
810 var_sep_string = '\r';
811 } else if ( var_sep_string == "verticaltab" ) {
812 var_separator = '\v';
814 var_separator = var_sep_string;
817 if ( line_sep_string == "newline" ) {
818 line_separator = '\n';
819 } else if ( line_sep_string == "tab" ) {
820 line_separator = '\t';
821 } else if ( line_sep_string == "space" ) {
822 line_separator = ' ';
823 } else if ( line_sep_string == "formfeed" ) {
824 line_separator = '\f';
825 } else if ( line_sep_string == "carriagereturn" ) {
826 line_separator = '\r';
827 } else if ( line_sep_string == "verticaltab" ) {
828 line_separator = '\v';
830 line_separator = line_sep_string;
833 // default values: no footer and record_length = sizeof(representation)
834 binary_footer_type = FOOTER_NONE;
835 binary_record_length = -1;
837 // default choice is network byte order (big endian)
838 if (sgIsLittleEndian()) {
839 binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
841 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
844 if ( root->hasValue("binary_footer") ) {
845 string footer_type = root->getStringValue("binary_footer");
846 if ( footer_type == "length" ) {
847 binary_footer_type = FOOTER_LENGTH;
848 } else if ( footer_type.substr(0, 5) == "magic" ) {
849 binary_footer_type = FOOTER_MAGIC;
850 binary_footer_value = strtol(footer_type.substr(6,
851 footer_type.length() - 6).c_str(), (char**)0, 0);
852 } else if ( footer_type != "none" ) {
853 SG_LOG(SG_IO, SG_ALERT,
854 "generic protocol: Unknown generic binary protocol "
855 "footer '" << footer_type << "', using no footer.");
859 if ( root->hasValue("record_length") ) {
860 binary_record_length = root->getIntValue("record_length");
863 if ( root->hasValue("byte_order") ) {
864 string byte_order = root->getStringValue("byte_order");
865 if (byte_order == "network" ) {
866 if ( sgIsLittleEndian() ) {
867 binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
869 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
871 } else if ( byte_order == "host" ) {
872 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
874 SG_LOG(SG_IO, SG_ALERT,
875 "generic protocol: Undefined generic binary protocol"
876 "byte order, using HOST byte order.");
880 if( root->hasValue( "wrapper" ) ) {
881 string w = root->getStringValue( "wrapper" );
882 if( w == "kiss" ) wrapper = new FGKissWrapper();
883 else if( w == "stxetx" ) wrapper = new FGSTXETXWrapper();
884 else SG_LOG(SG_IO, SG_ALERT,
885 "generic protocol: Undefined binary protocol wrapper '" + w + "' ignored" );
889 int record_length = 0; // Only used for binary protocols.
890 vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
892 for (unsigned int i = 0; i < chunks.size(); i++) {
896 // chunk.name = chunks[i]->getStringValue("name");
897 chunk.format = unescape(chunks[i]->getStringValue("format", "%d"));
898 chunk.offset = chunks[i]->getDoubleValue("offset");
899 chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
900 chunk.min = chunks[i]->getDoubleValue("min");
901 chunk.max = chunks[i]->getDoubleValue("max");
902 chunk.wrap = chunks[i]->getBoolValue("wrap");
903 chunk.rel = chunks[i]->getBoolValue("relative");
905 if( chunks[i]->hasChild("const") ) {
906 chunk.prop = new SGPropertyNode();
907 chunk.prop->setStringValue( chunks[i]->getStringValue("const", "" ) );
909 string node = chunks[i]->getStringValue("node", "/null");
910 chunk.prop = fgGetNode(node.c_str(), true);
913 string type = chunks[i]->getStringValue("type");
915 // Note: officially the type is called 'bool' but for backward
916 // compatibility 'boolean' will also be supported.
917 if (type == "bool" || type == "boolean") {
918 chunk.type = FG_BOOL;
920 } else if (type == "float") {
921 chunk.type = FG_FLOAT;
922 record_length += sizeof(int32_t);
923 } else if (type == "double") {
924 chunk.type = FG_DOUBLE;
925 record_length += sizeof(int64_t);
926 } else if (type == "fixed") {
927 chunk.type = FG_FIXED;
928 record_length += sizeof(int32_t);
929 } else if (type == "string") {
930 chunk.type = FG_STRING;
931 } else if (type == "byte") {
932 chunk.type = FG_BYTE;
933 record_length += sizeof(int8_t);
936 record_length += sizeof(int32_t);
938 msg.push_back(chunk);
944 if ((chunks.size() > 1)&&(var_sep_string.length() == 0))
946 // ASCII protocols really need a separator when there is more than one chunk per line
947 SG_LOG(SG_IO, SG_ALERT,
948 "generic protocol: Invalid configuration. "
949 "'var_separator' must not be empty for protocols which have more than one chunk per line.");
955 if (binary_record_length == -1) {
956 binary_record_length = record_length;
957 } else if (binary_record_length < record_length) {
958 SG_LOG(SG_IO, SG_ALERT,
959 "generic protocol: Requested binary record length shorter than "
960 " requested record representation.");
961 binary_record_length = record_length;
968 void FGGeneric::updateValue(FGGeneric::_serial_prot& prot, bool val)
972 // value inverted if received true, otherwise leave unchanged
974 setValue(prot.prop, !getValue<bool>(prot.prop));
978 setValue(prot.prop, val);