X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FNetwork%2Fgeneric.cxx;h=423d2be189b28ffec2de641415dc99ab3b1bdcfb;hb=ae50c054a9007b98f1a8dafe6d589d0b4cab8873;hp=60c99ee58b3a88c9e8227e16684327dd6a76dadc;hpb=80bada55cd20de9e8bb8fd2557edc08afd534853;p=flightgear.git diff --git a/src/Network/generic.cxx b/src/Network/generic.cxx index 60c99ee58..423d2be18 100644 --- a/src/Network/generic.cxx +++ b/src/Network/generic.cxx @@ -37,154 +37,328 @@ #include
#include
+#include
#include "generic.hxx" -FGGeneric::FGGeneric(string& config) { +FGGeneric::FGGeneric(vector tokens) : exitOnError(false) +{ + size_t configToken; + if (tokens[1] == "socket") { + configToken = 7; + } else if (tokens[1] == "file") { + configToken = 5; + } else { + configToken = 6; + } - string file = config+".xml"; + if (configToken >= tokens.size()) { + SG_LOG(SG_GENERAL, SG_ALERT, + "Not enough tokens passed for generic protocol"); + return; + } - SGPath path( globals->get_fg_root() ); - path.append("Protocol"); - path.append(file.c_str()); - SG_LOG(SG_GENERAL, SG_INFO, "Reading communication protocol from " - << path.str()); + string config = tokens[ configToken ]; + file_name = config+".xml"; + direction = tokens[2]; - SGPropertyNode root; - try { - readProperties(path.str(), &root); - } catch (const sg_exception &e) { - SG_LOG(SG_GENERAL, SG_ALERT, - "Unable to load the protocol configuration file"); - return; + if (direction != "in" && direction != "out") { + SG_LOG(SG_GENERAL, SG_ALERT, "Unsuported protocol direction: " + << direction); } - SGPropertyNode *output = root.getNode("generic/output"); - if (output) - read_config(output, _out_message); - - SGPropertyNode *input = root.getNode("generic/input"); - if (input) - read_config(input, _in_message); + reinit(); } FGGeneric::~FGGeneric() { } +union u32 { + uint32_t intVal; + float floatVal; +}; + +union u64 { + uint64_t longVal; + double doubleVal; +}; // generate the message -bool FGGeneric::gen_message() { +bool FGGeneric::gen_message_binary() { string generic_sentence; - char tmp[255]; length = 0; double val; - for (unsigned int i = 0; i < _out_message.size(); i++) { - if (i > 0 && !binary_mode) - generic_sentence += var_separator; - switch (_out_message[i].type) { case FG_INT: val = _out_message[i].offset + _out_message[i].prop->getIntValue() * _out_message[i].factor; - if (binary_mode) { + + if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) { *((int32_t*)&buf[length]) = (int32_t)val; - length += sizeof(int32_t); } else { - snprintf(tmp, 255, _out_message[i].format.c_str(), (int)val); + *((uint32_t*)&buf[length]) = sg_bswap_32((uint32_t)val); } + length += sizeof(int32_t); break; case FG_BOOL: - if (binary_mode) { - *((int8_t*)&buf[length]) - = _out_message[i].prop->getBoolValue() ? true : false; - length += sizeof(int8_t); + *((int8_t*)&buf[length]) + = _out_message[i].prop->getBoolValue() ? true : false; + length += 1; + break; + + case FG_FIXED: + { + val = _out_message[i].offset + + _out_message[i].prop->getFloatValue() * _out_message[i].factor; + + int fixed = (int)(val * 65536.0f); + if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) { + *((int32_t*)&buf[length]) = (int32_t)fixed; } else { - snprintf(tmp, 255, _out_message[i].format.c_str(), - _out_message[i].prop->getBoolValue()); + *((uint32_t*)&buf[length]) = sg_bswap_32((uint32_t)fixed); + } + length += sizeof(int32_t); + break; + } + case FG_FLOAT: + val = _out_message[i].offset + + _out_message[i].prop->getFloatValue() * _out_message[i].factor; + + if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) { + *((float*)&buf[length]) = val; + } else { + u32 tmpun32; + tmpun32.floatVal = static_cast(val); + *((uint32_t*)&buf[length]) = sg_bswap_32(tmpun32.intVal); } + length += sizeof(uint32_t); break; case FG_DOUBLE: val = _out_message[i].offset + - _out_message[i].prop->getFloatValue() * _out_message[i].factor; - if (binary_mode) { + _out_message[i].prop->getFloatValue() * _out_message[i].factor; + + if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) { *((double*)&buf[length]) = val; - length += sizeof(double); } else { - snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val); + u64 tmpun64; + tmpun64.doubleVal = val; + *((uint64_t*)&buf[length]) = sg_bswap_64(tmpun64.longVal); } + length += sizeof(int64_t); break; default: // SG_STRING - if (binary_mode) { - const char *strdata = _out_message[i].prop->getStringValue(); - int strlength = strlen(strdata); + const char *strdata = _out_message[i].prop->getStringValue(); + int strlength = strlen(strdata); - /* Format for strings is - * [length as int, 4 bytes][ASCII data, length bytes] - */ + if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) { + SG_LOG( SG_IO, SG_ALERT, "Generic protocol: " + "FG_STRING will be written in host byte order."); + } + /* Format for strings is + * [length as int, 4 bytes][ASCII data, length bytes] + */ + if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) { *((int32_t*)&buf[length]) = strlength; - length += sizeof(int32_t); - strncpy(&buf[length], strdata, strlength); - length += strlength; - /* FIXME padding for alignment? Something like: - * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0; - */ } else { - snprintf(tmp, 255, _out_message[i].format.c_str(), - _out_message[i].prop->getStringValue()); + *((int32_t*)&buf[length]) = sg_bswap_32(strlength); } + length += sizeof(int32_t); + strncpy(&buf[length], strdata, strlength); + length += strlength; + /* FIXME padding for alignment? Something like: + * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0; + */ } + } + + // add the footer to the packet ("line") + switch (binary_footer_type) { + case FOOTER_LENGTH: + binary_footer_value = length; + break; + + case FOOTER_MAGIC: + case FOOTER_NONE: + break; + } - if (!binary_mode) { - generic_sentence += tmp; + if (binary_footer_type != FOOTER_NONE) { + if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) { + *((int32_t*)&buf[length]) = binary_footer_value; + } else { + *((int32_t*)&buf[length]) = sg_bswap_32(binary_footer_value); } + length += sizeof(int32_t); } - if (!binary_mode) { - /* After each lot of variables has been added, put the line separator - * char/string - */ - generic_sentence += line_separator; + return true; +} - length = generic_sentence.length(); - strncpy( buf, generic_sentence.c_str(), length ); - } else { - // add the footer to the packet ("line") - switch (binary_footer_type) { - case FOOTER_LENGTH: - binary_footer_value = length; - break; - - case FOOTER_MAGIC: - break; +bool FGGeneric::gen_message_ascii() { + string generic_sentence; + char tmp[255]; + length = 0; + + double val; + for (unsigned int i = 0; i < _out_message.size(); i++) { + + if (i > 0) { + generic_sentence += var_separator; } - if (binary_footer_type != FOOTER_NONE) { - *((int32_t*)&buf[length]) = binary_footer_value; - length += sizeof(int32_t); + + switch (_out_message[i].type) { + case FG_INT: + val = _out_message[i].offset + + _out_message[i].prop->getIntValue() * _out_message[i].factor; + snprintf(tmp, 255, _out_message[i].format.c_str(), (int)val); + break; + + case FG_BOOL: + snprintf(tmp, 255, _out_message[i].format.c_str(), + _out_message[i].prop->getBoolValue()); + break; + + case FG_FIXED: + val = _out_message[i].offset + + _out_message[i].prop->getFloatValue() * _out_message[i].factor; + snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val); + break; + + case FG_FLOAT: + val = _out_message[i].offset + + _out_message[i].prop->getFloatValue() * _out_message[i].factor; + snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val); + break; + + case FG_DOUBLE: + val = _out_message[i].offset + + _out_message[i].prop->getDoubleValue() * _out_message[i].factor; + snprintf(tmp, 255, _out_message[i].format.c_str(), (double)val); + break; + + default: // SG_STRING + snprintf(tmp, 255, _out_message[i].format.c_str(), + _out_message[i].prop->getStringValue()); } + + generic_sentence += tmp; } + /* After each lot of variables has been added, put the line separator + * char/string + */ + generic_sentence += line_separator; + + length = generic_sentence.length(); + strncpy( buf, generic_sentence.c_str(), length ); + return true; } -bool FGGeneric::parse_message() { +bool FGGeneric::gen_message() { + if (binary_mode) { + return gen_message_binary(); + } else { + return gen_message_ascii(); + } +} + +bool FGGeneric::parse_message_binary() { + char *p2, *p1 = buf; + int32_t tmp32; + double val; + int i = -1; + + p2 = p1 + FG_MAX_MSG_SIZE; + while ((++i < (int)_in_message.size()) && (p1 < p2)) { + + switch (_in_message[i].type) { + case FG_INT: + if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) { + tmp32 = sg_bswap_32(*(int32_t *)p1); + } else { + tmp32 = *(int32_t *)p1; + } + + val = _in_message[i].offset + (double)tmp32 * _in_message[i].factor; + + _in_message[i].prop->setIntValue((int)val); + p1 += sizeof(int32_t); + break; + + case FG_BOOL: + _in_message[i].prop->setBoolValue( p1[0] != 0 ); + p1 += 1; + break; + + case FG_FIXED: + if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) { + tmp32 = sg_bswap_32(*(int32_t *)p1); + } else { + tmp32 = *(int32_t *)p1; + } + + val = _in_message[i].offset + + ((double)tmp32 / 65536.0f) * _in_message[i].factor; + + _in_message[i].prop->setFloatValue(val); + p1 += sizeof(int32_t); + break; + + case FG_FLOAT: + u32 tmpun32; + if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) { + tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1); + } else { + tmpun32.floatVal = *(float *)p1; + } + + val = _in_message[i].offset + + tmpun32.floatVal * _in_message[i].factor; + + _in_message[i].prop->setFloatValue(val); + p1 += sizeof(int32_t); + break; + + case FG_DOUBLE: + u64 tmpun64; + if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) { + tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1); + } else { + tmpun64.doubleVal = *(double *)p1; + } + + val = _in_message[i].offset + + tmpun64.doubleVal * _in_message[i].factor; + + _in_message[i].prop->setDoubleValue(val); + p1 += sizeof(int64_t); + break; + + default: // SG_STRING + SG_LOG( SG_IO, SG_ALERT, "Generic protocol: " + "Ignoring unsupported binary input chunk type."); + } + } + + return true; +} + +bool FGGeneric::parse_message_ascii() { char *p2, *p1 = buf; double val; int i = -1; - if (binary_mode) - SG_LOG( SG_IO, SG_ALERT, - "generic protocol: binary mode input is not yet implemented."); - while ((++i < (int)_in_message.size()) && - p1 && strcmp(p1, line_separator.c_str())) { + p1 && strcmp(p1, line_separator.c_str())) { p2 = strstr(p1, var_separator.c_str()); if (p2) { @@ -202,21 +376,34 @@ bool FGGeneric::parse_message() { _in_message[i].prop->setBoolValue( atof(p1) != 0.0 ); break; - case FG_DOUBLE: + case FG_FIXED: + case FG_FLOAT: val = _in_message[i].offset + strtod(p1, 0) * _in_message[i].factor; _in_message[i].prop->setFloatValue((float)val); break; + case FG_DOUBLE: + val = _in_message[i].offset + strtod(p1, 0) * _in_message[i].factor; + _in_message[i].prop->setDoubleValue(val); + break; + default: // SG_STRING - _in_message[i].prop->setStringValue(p1); + _in_message[i].prop->setStringValue(p1); } p1 = p2; } - + return true; } +bool FGGeneric::parse_message() { + if (binary_mode) { + return parse_message_binary(); + } else { + return parse_message_ascii(); + } +} // open hailing frequencies @@ -236,6 +423,13 @@ bool FGGeneric::open() { set_enabled( true ); + if ( get_direction() == SG_IO_OUT && ! preamble.empty() ) { + if ( ! io->write( preamble.c_str(), preamble.size() ) ) { + SG_LOG( SG_IO, SG_WARN, "Error writing preamble." ); + return false; + } + } + return true; } @@ -248,18 +442,58 @@ bool FGGeneric::process() { gen_message(); if ( ! io->write( buf, length ) ) { SG_LOG( SG_IO, SG_WARN, "Error writing data." ); - return false; + goto error_out; } } else if ( get_direction() == SG_IO_IN ) { - if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) { - parse_message(); + if ( io->get_type() == sgFileType ) { + if (!binary_mode) { + length = io->readline( buf, FG_MAX_MSG_SIZE ); + if ( length > 0 ) { + parse_message(); + } else { + SG_LOG( SG_IO, SG_ALERT, "Error reading data." ); + return false; + } + } else { + length = io->read( buf, binary_record_length ); + if ( length == binary_record_length ) { + parse_message(); + } else { + SG_LOG( SG_IO, SG_ALERT, + "Generic protocol: Received binary " + "record of unexpected size, expected: " + << binary_record_length << " but received: " + << length); + } + } } else { - SG_LOG( SG_IO, SG_ALERT, "Error reading data." ); - return false; + if (!binary_mode) { + while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) { + parse_message(); + } + } else { + while ((length = io->read( buf, binary_record_length )) + == binary_record_length ) { + parse_message(); + } + + if ( length > 0 ) { + SG_LOG( SG_IO, SG_ALERT, + "Generic protocol: Received binary " + "record of unexpected size, expected: " + << binary_record_length << " but received: " + << length); + } + } } } - return true; +error_out: + if (exitOnError) { + fgExit(1); + return true; // should not get there, but please the compiler + } else + return false; } @@ -267,6 +501,13 @@ bool FGGeneric::process() { bool FGGeneric::close() { SGIOChannel *io = get_io_channel(); + if ( get_direction() == SG_IO_OUT && ! postamble.empty() ) { + if ( ! io->write( postamble.c_str(), postamble.size() ) ) { + SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." ); + return false; + } + } + set_enabled( false ); if ( ! io->close() ) { @@ -277,13 +518,45 @@ bool FGGeneric::close() { } +void +FGGeneric::reinit() +{ + SGPath path( globals->get_fg_root() ); + path.append("Protocol"); + path.append(file_name.c_str()); + + SG_LOG(SG_GENERAL, SG_INFO, "Reading communication protocol from " + << path.str()); + + SGPropertyNode root; + try { + readProperties(path.str(), &root); + } catch (const sg_exception &) { + SG_LOG(SG_GENERAL, SG_ALERT, + "Unable to load the protocol configuration file"); + return; + } + + if (direction == "out") { + SGPropertyNode *output = root.getNode("generic/output"); + if (output) { + _out_message.clear(); + read_config(output, _out_message); + } + } else if (direction == "in") { + SGPropertyNode *input = root.getNode("generic/input"); + if (input) { + _in_message.clear(); + read_config(input, _in_message); + } + } +} + + void FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg) { - if (root->hasValue("binary_mode")) - binary_mode = root->getBoolValue("binary_mode"); - else - binary_mode = false; + binary_mode = root->getBoolValue("binary_mode"); if (!binary_mode) { /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml @@ -293,81 +566,139 @@ FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg) * line_sep_string = the string/charachter to place at the end of each * lot of variables */ - var_sep_string = root->getStringValue("var_separator"); - line_sep_string = root->getStringValue("line_separator"); - - if ( var_sep_string == "newline" ) - var_separator = '\n'; - else if ( var_sep_string == "tab" ) - var_separator = '\t'; - else if ( var_sep_string == "space" ) - var_separator = ' '; - else if ( var_sep_string == "formfeed" ) - var_separator = '\f'; - else if ( var_sep_string == "carriagereturn" ) - var_sep_string = '\r'; - else if ( var_sep_string == "verticaltab" ) - var_separator = '\v'; - else - var_separator = var_sep_string; - - if ( line_sep_string == "newline" ) - line_separator = '\n'; - else if ( line_sep_string == "tab" ) - line_separator = '\t'; - else if ( line_sep_string == "space" ) - line_separator = ' '; - else if ( line_sep_string == "formfeed" ) - line_separator = '\f'; - else if ( line_sep_string == "carriagereturn" ) - line_separator = '\r'; - else if ( line_sep_string == "verticaltab" ) - line_separator = '\v'; - else - line_separator = line_sep_string; + preamble = fgUnescape(root->getStringValue("preamble")); + postamble = fgUnescape(root->getStringValue("postamble")); + var_sep_string = fgUnescape(root->getStringValue("var_separator")); + line_sep_string = fgUnescape(root->getStringValue("line_separator")); + + if ( var_sep_string == "newline" ) { + var_separator = '\n'; + } else if ( var_sep_string == "tab" ) { + var_separator = '\t'; + } else if ( var_sep_string == "space" ) { + var_separator = ' '; + } else if ( var_sep_string == "formfeed" ) { + var_separator = '\f'; + } else if ( var_sep_string == "carriagereturn" ) { + var_sep_string = '\r'; + } else if ( var_sep_string == "verticaltab" ) { + var_separator = '\v'; + } else { + var_separator = var_sep_string; + } + + if ( line_sep_string == "newline" ) { + line_separator = '\n'; + } else if ( line_sep_string == "tab" ) { + line_separator = '\t'; + } else if ( line_sep_string == "space" ) { + line_separator = ' '; + } else if ( line_sep_string == "formfeed" ) { + line_separator = '\f'; + } else if ( line_sep_string == "carriagereturn" ) { + line_separator = '\r'; + } else if ( line_sep_string == "verticaltab" ) { + line_separator = '\v'; + } else { + line_separator = line_sep_string; + } } else { - binary_footer_type = FOOTER_NONE; // default choice + // default values: no footer and record_length = sizeof(representation) + binary_footer_type = FOOTER_NONE; + binary_record_length = -1; + + // default choice is network byte order (big endian) + if (sgIsLittleEndian()) { + binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION; + } else { + binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER; + } + if ( root->hasValue("binary_footer") ) { string footer_type = root->getStringValue("binary_footer"); - if ( footer_type == "length" ) + if ( footer_type == "length" ) { binary_footer_type = FOOTER_LENGTH; - else if ( footer_type.substr(0, 5) == "magic" ) { + } else if ( footer_type.substr(0, 5) == "magic" ) { binary_footer_type = FOOTER_MAGIC; binary_footer_value = strtol(footer_type.substr(6, footer_type.length() - 6).c_str(), (char**)0, 0); - } else if ( footer_type != "none" ) + } else if ( footer_type != "none" ) { + SG_LOG(SG_IO, SG_ALERT, + "generic protocol: Unknown generic binary protocol " + "footer '" << footer_type << "', using no footer."); + } + } + + if ( root->hasValue("record_length") ) { + binary_record_length = root->getIntValue("record_length"); + } + + if ( root->hasValue("byte_order") ) { + string byte_order = root->getStringValue("byte_order"); + if (byte_order == "network" ) { + if ( sgIsLittleEndian() ) { + binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION; + } else { + binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER; + } + } else if ( byte_order == "host" ) { + binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER; + } else { SG_LOG(SG_IO, SG_ALERT, "generic protocol: Undefined generic binary protocol" - "footer, using no footer."); + "byte order, using HOST byte order."); + } } } + int record_length = 0; // Only used for binary protocols. vector chunks = root->getChildren("chunk"); for (unsigned int i = 0; i < chunks.size(); i++) { _serial_prot chunk; - // chunk.name = chunks[i]->getStringValue("name"); - chunk.format = chunks[i]->getStringValue("format", "%d"); + // chunk.name = chunks[i]->getStringValue("name"); + chunk.format = fgUnescape(chunks[i]->getStringValue("format", "%d")); chunk.offset = chunks[i]->getDoubleValue("offset"); chunk.factor = chunks[i]->getDoubleValue("factor", 1.0); - string node = chunks[i]->getStringValue("node"); + string node = chunks[i]->getStringValue("node", "/null"); chunk.prop = fgGetNode(node.c_str(), true); string type = chunks[i]->getStringValue("type"); - if (type == "bool") + + // Note: officially the type is called 'bool' but for backward + // compatibility 'boolean' will also be supported. + if (type == "bool" || type == "boolean") { chunk.type = FG_BOOL; - else if (type == "float") + record_length += 1; + } else if (type == "float") { + chunk.type = FG_FLOAT; + record_length += sizeof(int32_t); + } else if (type == "double") { chunk.type = FG_DOUBLE; - else if (type == "string") + record_length += sizeof(int64_t); + } else if (type == "fixed") { + chunk.type = FG_FIXED; + record_length += sizeof(int32_t); + } else if (type == "string") chunk.type = FG_STRING; - else + else { chunk.type = FG_INT; - + record_length += sizeof(int32_t); + } msg.push_back(chunk); } + if( binary_mode ) { + if (binary_record_length == -1) { + binary_record_length = record_length; + } else if (binary_record_length < record_length) { + SG_LOG(SG_IO, SG_ALERT, + "generic protocol: Requested binary record length shorter than " + " requested record representation."); + binary_record_length = record_length; + } + } } -