]> git.mxchange.org Git - flightgear.git/blobdiff - src/Network/generic.cxx
NavDisplay - custom symbol support enabled.
[flightgear.git] / src / Network / generic.cxx
index 4b94eacc1573215ff1fd3ec2e9d0a2cf68858b6e..dca36308739490aa765cb33c9a828e73fd7fb840 100644 (file)
@@ -1,4 +1,4 @@
-// generic.cxx -- generic protocal class
+// generic.cxx -- generic protocol class
 //
 // Written by Curtis Olson, started November 1999.
 //
 #include <simgear/misc/stdint.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/props/props_io.hxx>
+#include <simgear/math/SGMath.hxx>
 
 #include <Main/globals.hxx>
 #include <Main/fg_props.hxx>
+#include <Main/fg_os.hxx>
 #include <Main/util.hxx>
-
 #include "generic.hxx"
 
-
-
-FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false)
+FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false), initOk(false)
 {
-    int configToken;
+    size_t configToken;
     if (tokens[1] == "socket") {
         configToken = 7;
     } else if (tokens[1] == "file") {
@@ -54,54 +53,41 @@ FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false)
         configToken = 6; 
     }
 
-    if (configToken >= tokens.size()) {
-       SG_LOG(SG_GENERAL, SG_ALERT,
-              "Not enough tokens passed for generic protocol");
+    if ((configToken >= tokens.size())||(tokens[ configToken ] == "")) {
+       SG_LOG(SG_NETWORK, SG_ALERT,
+              "Not enough tokens passed for generic '" << tokens[1] << "' protocol. ");
        return;
     }
 
     string config = tokens[ configToken ];
-    string file = config+".xml";
-
-    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());
+    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" && direction != "bi") {
+        SG_LOG(SG_NETWORK, SG_ALERT, "Unsuported protocol direction: "
+               << direction);
+        return;
     }
 
-    if (tokens[2] == "out") {
-        SGPropertyNode *output = root.getNode("generic/output");
-        if (output) {
-            read_config(output, _out_message);
-        }
-    } else if (tokens[2] == "in") {
-        SGPropertyNode *input = root.getNode("generic/input");
-        if (input) {
-            read_config(input, _in_message);
-        }
-    } else {
-        SG_LOG(SG_GENERAL, SG_ALERT, "Unsuported protocol direction: "
-               << tokens[2]);
-    }
+    reinit();
 }
 
 FGGeneric::~FGGeneric() {
 }
 
+union u32 {
+    uint32_t intVal;
+    float floatVal;
+};
+
+union u64 {
+    uint64_t longVal;
+    double doubleVal;
+};
 
 // generate the message
 bool FGGeneric::gen_message_binary() {
     string generic_sentence;
-    char tmp[255];
     length = 0;
 
     double val;
@@ -109,20 +95,20 @@ bool FGGeneric::gen_message_binary() {
 
         switch (_out_message[i].type) {
         case FG_INT:
+        {
             val = _out_message[i].offset +
                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
-
-            if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) {
-                *((int32_t*)&buf[length]) = (int32_t)val;
-            } else {
-                *((uint32_t*)&buf[length]) = sg_bswap_32((uint32_t)val);
+            int32_t intVal = val;
+            if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
+                intVal = (int32_t) sg_bswap_32((uint32_t)intVal);
             }
+            memcpy(&buf[length], &intVal, sizeof(int32_t));
             length += sizeof(int32_t);
             break;
+        }
 
         case FG_BOOL:
-            *((int8_t*)&buf[length])
-                      = _out_message[i].prop->getBoolValue() ? true : false;
+            buf[length] = (char) (_out_message[i].prop->getBoolValue() ? true : false);
             length += 1;
             break;
 
@@ -131,42 +117,48 @@ bool FGGeneric::gen_message_binary() {
             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 {
-                *((uint32_t*)&buf[length]) = sg_bswap_32((uint32_t)val);
+            int32_t fixed = (int)(val * 65536.0f);
+            if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
+                fixed = (int32_t) sg_bswap_32((uint32_t)fixed);
             } 
+            memcpy(&buf[length], &fixed, sizeof(int32_t));
             length += sizeof(int32_t);
             break;
         }
+
         case FG_FLOAT:
+        {
             val = _out_message[i].offset +
                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
+            u32 tmpun32;
+            tmpun32.floatVal = static_cast<float>(val);
 
-            if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) {
-                *((float*)&buf[length]) = val;
-            } else {
-                *((float*)&buf[length]) = sg_bswap_32(*(uint32_t*)&val);
+            if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
+                tmpun32.intVal = sg_bswap_32(tmpun32.intVal);
             }
-            length += sizeof(int32_t);
+            memcpy(&buf[length], &tmpun32.intVal, sizeof(uint32_t));
+            length += sizeof(uint32_t);
             break;
+        }
 
         case FG_DOUBLE:
+        {
             val = _out_message[i].offset +
                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
+            u64 tmpun64;
+            tmpun64.doubleVal = val;
 
-            if (binary_byte_order == BYTE_ORDER_MATCHES_NETWORK_ORDER) {
-                *((double*)&buf[length]) = val;
-            } else {
-                *((double*)&buf[length]) = sg_bswap_64(*(uint64_t*)&val);
+            if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
+                tmpun64.longVal = sg_bswap_64(tmpun64.longVal);
             }
-            length += sizeof(int64_t);
+            memcpy(&buf[length], &tmpun64.longVal, sizeof(uint64_t));
+            length += sizeof(uint64_t);
             break;
+        }
 
         default: // SG_STRING
             const char *strdata = _out_message[i].prop->getStringValue();
-            int strlength = strlen(strdata);
+            int32_t strlength = strlen(strdata);
 
             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
                 SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
@@ -175,17 +167,17 @@ bool FGGeneric::gen_message_binary() {
             /* 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;
-            } else {
-                *((int32_t*)&buf[length]) = sg_bswap_32(strlength);
+            if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
+                strlength = sg_bswap_32(strlength);
             }
+            memcpy(&buf[length], &strlength, sizeof(int32_t));
             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;
              */
+            break;
         }
     }
 
@@ -196,15 +188,16 @@ bool FGGeneric::gen_message_binary() {
             break;
 
         case FOOTER_MAGIC:
+        case FOOTER_NONE:
             break;
     }
 
     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);
+        int32_t intValue = binary_footer_value;
+        if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
+            intValue = sg_bswap_32(binary_footer_value);
         }
+        memcpy(&buf[length], &intValue, sizeof(int32_t));
         length += sizeof(int32_t);
     }
 
@@ -249,8 +242,8 @@ bool FGGeneric::gen_message_ascii() {
 
         case FG_DOUBLE:
             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);
+                _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
+            snprintf(tmp, 255, _out_message[i].format.c_str(), (double)val);
             break;
 
         default: // SG_STRING
@@ -280,117 +273,121 @@ bool FGGeneric::gen_message() {
     }
 }
 
-bool FGGeneric::parse_message_binary() {
+bool FGGeneric::parse_message_binary(int length) {
     char *p2, *p1 = buf;
-    int64_t tmp;
-    double val;
+    int32_t tmp32;
     int i = -1;
 
-    p2 = p1 + FG_MAX_MSG_SIZE;
+    p2 = p1 + length;
     while ((++i < (int)_in_message.size()) && (p1  < p2)) {
 
         switch (_in_message[i].type) {
         case FG_INT:
             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
-                tmp = sg_bswap_32(*(int32_t *)p1);
+                tmp32 = sg_bswap_32(*(int32_t *)p1);
             } else {
-                tmp = *(int32_t *)p1;
+                tmp32 = *(int32_t *)p1;
             }
-
-            val = _in_message[i].offset + (double)tmp * _in_message[i].factor;
-
-            _in_message[i].prop->setIntValue((int)val);
+            updateValue(_in_message[i], (int)tmp32);
             p1 += sizeof(int32_t);
             break;
 
         case FG_BOOL:
-            _in_message[i].prop->setBoolValue( p1[0] != 0 );
+            updateValue(_in_message[i], p1[0] != 0);
             p1 += 1;
             break;
 
         case FG_FIXED:
             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
-                tmp = sg_bswap_32(*(int32_t *)p1);
+                tmp32 = sg_bswap_32(*(int32_t *)p1);
             } else {
-                tmp = *(int32_t *)p1;
+                tmp32 = *(int32_t *)p1;
             }
-
-            val = _in_message[i].offset +
-                  ((double)tmp / 65536.0f) * _in_message[i].factor;
-
-            _in_message[i].prop->setFloatValue(val);
+            updateValue(_in_message[i], (float)tmp32 / 65536.0f);
             p1 += sizeof(int32_t);
             break;
 
         case FG_FLOAT:
+            u32 tmpun32;
             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
-                tmp = sg_bswap_32(*(int32_t *)p1);
+                tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1);
             } else {
-                tmp = *(int32_t *)p1;
+                tmpun32.floatVal = *(float *)p1;
             }
-
-            val = _in_message[i].offset +
-                  *(float *)&tmp * _in_message[i].factor;
-
-            _in_message[i].prop->setFloatValue(val);
+            updateValue(_in_message[i], tmpun32.floatVal);
             p1 += sizeof(int32_t);
             break;
 
         case FG_DOUBLE:
+            u64 tmpun64;
             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
-                tmp = sg_bswap_64(*(int64_t *)p1);
+                tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1);
             } else {
-                tmp = *(int64_t *)p1;
+                tmpun64.doubleVal = *(double *)p1;
             }
-
-            val = _in_message[i].offset +
-                   *(double *)&tmp * _in_message[i].factor;
-
-            _in_message[i].prop->setDoubleValue(val);
+            updateValue(_in_message[i], tmpun64.doubleVal);
             p1 += sizeof(int64_t);
             break;
 
         default: // SG_STRING
             SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
                     "Ignoring unsupported binary input chunk type.");
+            break;
         }
     }
     
     return true;
 }
 
-bool FGGeneric::parse_message_ascii() {
-    char *p2, *p1 = buf;
-    double val;
+bool FGGeneric::parse_message_ascii(int length) {
+    char *p1 = buf;
     int i = -1;
+    int chunks = _in_message.size();
+    int line_separator_size = line_separator.size();
+
+    if (length < line_separator_size ||
+        line_separator.compare(buf + length - line_separator_size) != 0) {
 
-    while ((++i < (int)_in_message.size()) &&
-           p1 && strcmp(p1, line_separator.c_str())) {
+        SG_LOG(SG_IO, SG_WARN,
+               "Input line does not end with expected line separator." );
+    } else {
+        buf[length - line_separator_size] = 0;
+    }
 
-        p2 = strstr(p1, var_separator.c_str());
-        if (p2) {
-            *p2 = 0;
-            p2 += var_separator.length();
+    size_t varsep_len = var_separator.length();
+    while ((++i < chunks) && p1) {
+        char* p2 = NULL;
+
+        if (varsep_len > 0)
+        {
+            p2 = strstr(p1, var_separator.c_str());
+            if (p2) {
+                *p2 = 0;
+                p2 += varsep_len;
+            }
         }
 
         switch (_in_message[i].type) {
         case FG_INT:
-            val = _in_message[i].offset + atoi(p1) * _in_message[i].factor;
-            _in_message[i].prop->setIntValue((int)val);
+            updateValue(_in_message[i], atoi(p1));
             break;
 
         case FG_BOOL:
-            _in_message[i].prop->setBoolValue( atof(p1) != 0.0 );
+            updateValue(_in_message[i], atof(p1) != 0.0);
             break;
 
         case FG_FIXED:
+        case FG_FLOAT:
+            updateValue(_in_message[i], (float)strtod(p1, 0));
+            break;
+
         case FG_DOUBLE:
-            val = _in_message[i].offset + strtod(p1, 0) * _in_message[i].factor;
-            _in_message[i].prop->setFloatValue((float)val);
+            updateValue(_in_message[i], (double)strtod(p1, 0));
             break;
 
         default: // SG_STRING
             _in_message[i].prop->setStringValue(p1);
+            break;
         }
 
         p1 = p2;
@@ -399,11 +396,11 @@ bool FGGeneric::parse_message_ascii() {
     return true;
 }
 
-bool FGGeneric::parse_message() {
+bool FGGeneric::parse_message_len(int length) {
     if (binary_mode) {
-        return parse_message_binary(); 
+        return parse_message_binary(length);
     } else {
-        return parse_message_ascii();
+        return parse_message_ascii(length);
     }
 }
 
@@ -425,7 +422,9 @@ bool FGGeneric::open() {
 
     set_enabled( true );
 
-    if ( get_direction() == SG_IO_OUT && ! preamble.empty() ) {
+    if ( ((get_direction() == SG_IO_OUT )||
+          (get_direction() == SG_IO_BI))
+          && ! preamble.empty() ) {
         if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
             SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
             return false;
@@ -440,46 +439,65 @@ bool FGGeneric::open() {
 bool FGGeneric::process() {
     SGIOChannel *io = get_io_channel();
 
-    if ( get_direction() == SG_IO_OUT ) {
+    if ( (get_direction() == SG_IO_OUT) ||
+         (get_direction() == SG_IO_BI) ) {
         gen_message();
         if ( ! io->write( buf, length ) ) {
             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
             goto error_out;
         }
-    } else if ( get_direction() == SG_IO_IN ) {
-        if (!binary_mode) {
-            if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
-                parse_message();
+    }
+
+    if (( get_direction() == SG_IO_IN ) ||
+        (get_direction() == SG_IO_BI) ) {
+        if ( io->get_type() == sgFileType ) {
+            if (!binary_mode) {
+                length = io->readline( buf, FG_MAX_MSG_SIZE );
+                if ( length > 0 ) {
+                    parse_message_len( length );
+                } else {
+                    SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
+                    return false;
+                }
             } else {
-                SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
-                return false;
-            }
-        } else {
-            if ( (length = io->read( buf, binary_record_length )) > 0 ) {
-                if (length != binary_record_length) {
+                length = io->read( buf, binary_record_length );
+                if ( length == binary_record_length ) {
+                    parse_message_len( length );
+                } else {
                     SG_LOG( SG_IO, SG_ALERT,
                             "Generic protocol: Received binary "
                             "record of unexpected size, expected: "
-                            << binary_record_length << "received: "
+                            << binary_record_length << " but received: "
                             << length);
-                } else {
-                    SG_LOG( SG_IO, SG_DEBUG,
-                           "Generic protocol: received record of " << length <<
-                           " bytes.");
-                    parse_message();
+                }
+            }
+        } else {
+            if (!binary_mode) {
+                while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
+                    parse_message_len( length );
                 }
             } else {
-                SG_LOG( SG_IO, SG_INFO,
-                        "Generic protocol: Error reading data." );
-                return false;
+                while ((length = io->read( buf, binary_record_length )) 
+                          == binary_record_length ) {
+                    parse_message_len( length );
+                }
+
+                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);
-    else
+    if (exitOnError) {
+        fgOSExit(1);
+        return true; // should not get there, but please the compiler
+    } else
         return false;
 }
 
@@ -488,7 +506,9 @@ error_out:
 bool FGGeneric::close() {
     SGIOChannel *io = get_io_channel();
 
-    if ( get_direction() == SG_IO_OUT && ! postamble.empty() ) {
+    if ( ((get_direction() == SG_IO_OUT)||
+          (get_direction() == SG_IO_BI))
+          && ! postamble.empty() ) {
         if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
             SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
             return false;
@@ -506,6 +526,58 @@ bool FGGeneric::close() {
 
 
 void
+FGGeneric::reinit()
+{
+    SGPath path( globals->get_fg_root() );
+    path.append("Protocol");
+    path.append(file_name.c_str());
+
+    SG_LOG(SG_NETWORK, SG_INFO, "Reading communication protocol from "
+                                << path.str());
+
+    SGPropertyNode root;
+    try {
+        readProperties(path.str(), &root);
+    } catch (const sg_exception & ex) {
+        SG_LOG(SG_NETWORK, SG_ALERT,
+         "Unable to load the protocol configuration file: " << ex.getFormattedMessage() );
+         return;
+    }
+
+    if (direction == "out") {
+        SGPropertyNode *output = root.getNode("generic/output");
+        if (output) {
+            _out_message.clear();
+            if (!read_config(output, _out_message))
+            {
+                // bad configuration
+                return;
+            }
+        }
+    } else if (direction == "in") {
+        SGPropertyNode *input = root.getNode("generic/input");
+        if (input) {
+            _in_message.clear();
+            if (!read_config(input, _in_message))
+            {
+                // bad configuration
+                return;
+            }
+            if (!binary_mode && (line_separator.size() == 0 ||
+                *line_separator.rbegin() != '\n')) {
+
+                SG_LOG(SG_IO, SG_WARN,
+                    "Warning: Appending newline to line separator in generic input.");
+                line_separator.push_back('\n');
+            }
+        }
+    }
+
+    initOk = true;
+}
+
+
+bool
 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
 {
     binary_mode = root->getBoolValue("binary_mode");
@@ -605,6 +677,7 @@ FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
 
     int record_length = 0; // Only used for binary protocols.
     vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
+
     for (unsigned int i = 0; i < chunks.size(); i++) {
 
         _serial_prot chunk;
@@ -613,12 +686,19 @@ FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
         chunk.format = fgUnescape(chunks[i]->getStringValue("format", "%d"));
         chunk.offset = chunks[i]->getDoubleValue("offset");
         chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
+        chunk.min = chunks[i]->getDoubleValue("min");
+        chunk.max = chunks[i]->getDoubleValue("max");
+        chunk.wrap = chunks[i]->getBoolValue("wrap");
+        chunk.rel = chunks[i]->getBoolValue("relative");
 
         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;
             record_length += 1;
         } else if (type == "float") {
@@ -640,12 +720,42 @@ FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
 
     }
 
-    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;
+    if( !binary_mode )
+    {
+        if ((chunks.size() > 1)&&(var_sep_string.length() == 0))
+        {
+            // ASCII protocols really need a separator when there is more than one chunk per line
+            SG_LOG(SG_IO, SG_ALERT,
+                   "generic protocol: Invalid configuration. "
+                   "'var_separator' must not be empty for protocols which have more than one chunk per line.");
+            return false;
+        }
+    }
+    else
+    {
+        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;
+        }
     }
+
+    return true;
+}
+
+void FGGeneric::updateValue(FGGeneric::_serial_prot& prot, bool val)
+{
+  if( prot.rel )
+  {
+    // value inverted if received true, otherwise leave unchanged
+    if( val )
+      setValue(prot.prop, !getValue<bool>(prot.prop));
+  }
+  else
+  {
+    setValue(prot.prop, val);
+  }
 }