]> git.mxchange.org Git - flightgear.git/blob - src/Network/generic.cxx
Kill off platformDesktopPath entirely
[flightgear.git] / src / Network / generic.cxx
1 // generic.cxx -- generic protocol class
2 //
3 // Written by Curtis Olson, started November 1999.
4 //
5 // Copyright (C) 1999  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <string.h>                // strstr()
28 #include <stdlib.h>                // strtod(), atoi()
29 #include <cstdio>
30
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>
40
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"
46
47 using simgear::strutils::unescape;
48
49 FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false), initOk(false)
50 {
51     size_t configToken;
52     if (tokens[1] == "socket") {
53         configToken = 7;
54     } else if (tokens[1] == "file") {
55         configToken = 5;
56     } else {
57         configToken = 6; 
58     }
59
60     if ((configToken >= tokens.size())||(tokens[ configToken ] == "")) {
61        SG_LOG(SG_NETWORK, SG_ALERT,
62               "Not enough tokens passed for generic '" << tokens[1] << "' protocol. ");
63        return;
64     }
65
66     string config = tokens[ configToken ];
67     file_name = config+".xml";
68     direction = tokens[2];
69
70     if (direction != "in" && direction != "out" && direction != "bi") {
71         SG_LOG(SG_NETWORK, SG_ALERT, "Unsuported protocol direction: "
72                << direction);
73         return;
74     }
75
76     reinit();
77 }
78
79 FGGeneric::~FGGeneric() {
80 }
81
82 union u32 {
83     uint32_t intVal;
84     float floatVal;
85 };
86
87 union u64 {
88     uint64_t longVal;
89     double doubleVal;
90 };
91
92 // generate the message
93 bool FGGeneric::gen_message_binary() {
94     string generic_sentence;
95     length = 0;
96
97     double val;
98     for (unsigned int i = 0; i < _out_message.size(); i++) {
99
100         switch (_out_message[i].type) {
101         case FG_INT:
102         {
103             val = _out_message[i].offset +
104                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
105             int32_t intVal = val;
106             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
107                 intVal = (int32_t) sg_bswap_32((uint32_t)intVal);
108             }
109             memcpy(&buf[length], &intVal, sizeof(int32_t));
110             length += sizeof(int32_t);
111             break;
112         }
113
114         case FG_BOOL:
115             buf[length] = (char) (_out_message[i].prop->getBoolValue() ? true : false);
116             length += 1;
117             break;
118
119         case FG_FIXED:
120         {
121             val = _out_message[i].offset +
122                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
123
124             int32_t fixed = (int)(val * 65536.0f);
125             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
126                 fixed = (int32_t) sg_bswap_32((uint32_t)fixed);
127             } 
128             memcpy(&buf[length], &fixed, sizeof(int32_t));
129             length += sizeof(int32_t);
130             break;
131         }
132
133         case FG_FLOAT:
134         {
135             val = _out_message[i].offset +
136                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
137             u32 tmpun32;
138             tmpun32.floatVal = static_cast<float>(val);
139
140             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
141                 tmpun32.intVal = sg_bswap_32(tmpun32.intVal);
142             }
143             memcpy(&buf[length], &tmpun32.intVal, sizeof(uint32_t));
144             length += sizeof(uint32_t);
145             break;
146         }
147
148         case FG_DOUBLE:
149         {
150             val = _out_message[i].offset +
151                  _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
152             u64 tmpun64;
153             tmpun64.doubleVal = val;
154
155             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
156                 tmpun64.longVal = sg_bswap_64(tmpun64.longVal);
157             }
158             memcpy(&buf[length], &tmpun64.longVal, sizeof(uint64_t));
159             length += sizeof(uint64_t);
160             break;
161         }
162
163         default: // SG_STRING
164             const char *strdata = _out_message[i].prop->getStringValue();
165             int32_t strlength = strlen(strdata);
166
167             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
168                 SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
169                         "FG_STRING will be written in host byte order.");
170             }
171             /* Format for strings is 
172              * [length as int, 4 bytes][ASCII data, length bytes]
173              */
174             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
175                 strlength = sg_bswap_32(strlength);
176             }
177             memcpy(&buf[length], &strlength, sizeof(int32_t));
178             length += sizeof(int32_t);
179             strncpy(&buf[length], strdata, strlength);
180             length += strlength; 
181             /* FIXME padding for alignment? Something like: 
182              * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0;
183              */
184             break;
185         }
186     }
187
188     // add the footer to the packet ("line")
189     switch (binary_footer_type) {
190         case FOOTER_LENGTH:
191             binary_footer_value = length;
192             break;
193
194         case FOOTER_MAGIC:
195         case FOOTER_NONE:
196             break;
197     }
198
199     if (binary_footer_type != FOOTER_NONE) {
200         int32_t intValue = binary_footer_value;
201         if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
202             intValue = sg_bswap_32(binary_footer_value);
203         }
204         memcpy(&buf[length], &intValue, sizeof(int32_t));
205         length += sizeof(int32_t);
206     }
207
208     return true;
209 }
210
211 bool FGGeneric::gen_message_ascii() {
212     string generic_sentence;
213     char tmp[255];
214     length = 0;
215
216     double val;
217     for (unsigned int i = 0; i < _out_message.size(); i++) {
218
219         if (i > 0) {
220             generic_sentence += var_separator;
221         }
222
223         switch (_out_message[i].type) {
224         case FG_INT:
225             val = _out_message[i].offset +
226                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
227             snprintf(tmp, 255, _out_message[i].format.c_str(), (int)val);
228             break;
229
230         case FG_BOOL:
231             snprintf(tmp, 255, _out_message[i].format.c_str(),
232                                _out_message[i].prop->getBoolValue());
233             break;
234
235         case FG_FIXED:
236             val = _out_message[i].offset +
237                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
238             snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val);
239             break;
240
241         case FG_FLOAT:
242             val = _out_message[i].offset +
243                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
244             snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val);
245             break;
246
247         case FG_DOUBLE:
248             val = _out_message[i].offset +
249                 _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
250             snprintf(tmp, 255, _out_message[i].format.c_str(), (double)val);
251             break;
252
253         default: // SG_STRING
254             snprintf(tmp, 255, _out_message[i].format.c_str(),
255                                    _out_message[i].prop->getStringValue());
256         }
257
258         generic_sentence += tmp;
259     }
260
261     /* After each lot of variables has been added, put the line separator
262      * char/string
263      */
264     generic_sentence += line_separator;
265
266     length =  generic_sentence.length();
267     strncpy( buf, generic_sentence.c_str(), length );
268
269     return true;
270 }
271
272 bool FGGeneric::gen_message() {
273     if (binary_mode) {
274         return gen_message_binary();
275     } else {
276         return gen_message_ascii();
277     }
278 }
279
280 bool FGGeneric::parse_message_binary(int length) {
281     char *p2, *p1 = buf;
282     int32_t tmp32;
283     int i = -1;
284
285     p2 = p1 + length;
286     while ((++i < (int)_in_message.size()) && (p1  < p2)) {
287
288         switch (_in_message[i].type) {
289         case FG_INT:
290             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
291                 tmp32 = sg_bswap_32(*(int32_t *)p1);
292             } else {
293                 tmp32 = *(int32_t *)p1;
294             }
295             updateValue(_in_message[i], (int)tmp32);
296             p1 += sizeof(int32_t);
297             break;
298
299         case FG_BOOL:
300             updateValue(_in_message[i], p1[0] != 0);
301             p1 += 1;
302             break;
303
304         case FG_FIXED:
305             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
306                 tmp32 = sg_bswap_32(*(int32_t *)p1);
307             } else {
308                 tmp32 = *(int32_t *)p1;
309             }
310             updateValue(_in_message[i], (float)tmp32 / 65536.0f);
311             p1 += sizeof(int32_t);
312             break;
313
314         case FG_FLOAT:
315             u32 tmpun32;
316             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
317                 tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1);
318             } else {
319                 tmpun32.floatVal = *(float *)p1;
320             }
321             updateValue(_in_message[i], tmpun32.floatVal);
322             p1 += sizeof(int32_t);
323             break;
324
325         case FG_DOUBLE:
326             u64 tmpun64;
327             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
328                 tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1);
329             } else {
330                 tmpun64.doubleVal = *(double *)p1;
331             }
332             updateValue(_in_message[i], tmpun64.doubleVal);
333             p1 += sizeof(int64_t);
334             break;
335
336         default: // SG_STRING
337             SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
338                     "Ignoring unsupported binary input chunk type.");
339             break;
340         }
341     }
342     
343     return true;
344 }
345
346 bool FGGeneric::parse_message_ascii(int length) {
347     char *p1 = buf;
348     int i = -1;
349     int chunks = _in_message.size();
350     int line_separator_size = line_separator.size();
351
352     if (length < line_separator_size ||
353         line_separator.compare(buf + length - line_separator_size) != 0) {
354
355         SG_LOG(SG_IO, SG_WARN,
356                "Input line does not end with expected line separator." );
357     } else {
358         buf[length - line_separator_size] = 0;
359     }
360
361     size_t varsep_len = var_separator.length();
362     while ((++i < chunks) && p1) {
363         char* p2 = NULL;
364
365         if (varsep_len > 0)
366         {
367             p2 = strstr(p1, var_separator.c_str());
368             if (p2) {
369                 *p2 = 0;
370                 p2 += varsep_len;
371             }
372         }
373
374         switch (_in_message[i].type) {
375         case FG_INT:
376             updateValue(_in_message[i], atoi(p1));
377             break;
378
379         case FG_BOOL:
380             updateValue(_in_message[i], atof(p1) != 0.0);
381             break;
382
383         case FG_FIXED:
384         case FG_FLOAT:
385             updateValue(_in_message[i], (float)strtod(p1, 0));
386             break;
387
388         case FG_DOUBLE:
389             updateValue(_in_message[i], (double)strtod(p1, 0));
390             break;
391
392         default: // SG_STRING
393             _in_message[i].prop->setStringValue(p1);
394             break;
395         }
396
397         p1 = p2;
398     }
399
400     return true;
401 }
402
403 bool FGGeneric::parse_message_len(int length) {
404     if (binary_mode) {
405         return parse_message_binary(length);
406     } else {
407         return parse_message_ascii(length);
408     }
409 }
410
411
412 // open hailing frequencies
413 bool FGGeneric::open() {
414     if ( is_enabled() ) {
415         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
416                 << "is already in use, ignoring" );
417         return false;
418     }
419
420     SGIOChannel *io = get_io_channel();
421
422     if ( ! io->open( get_direction() ) ) {
423         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
424         return false;
425     }
426
427     set_enabled( true );
428
429     if ( ((get_direction() == SG_IO_OUT )||
430           (get_direction() == SG_IO_BI))
431           && ! preamble.empty() ) {
432         if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
433             SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
434             return false;
435         }
436     }
437
438     return true;
439 }
440
441
442 // process work for this port
443 bool FGGeneric::process() {
444     SGIOChannel *io = get_io_channel();
445
446     if ( (get_direction() == SG_IO_OUT) ||
447          (get_direction() == SG_IO_BI) ) {
448         gen_message();
449         if ( ! io->write( buf, length ) ) {
450             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
451             goto error_out;
452         }
453     }
454
455     if (( get_direction() == SG_IO_IN ) ||
456         (get_direction() == SG_IO_BI) ) {
457         if ( io->get_type() == sgFileType ) {
458             if (!binary_mode) {
459                 length = io->readline( buf, FG_MAX_MSG_SIZE );
460                 if ( length > 0 ) {
461                     parse_message_len( length );
462                 } else {
463                     SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
464                     return false;
465                 }
466             } else {
467                 length = io->read( buf, binary_record_length );
468                 if ( length == binary_record_length ) {
469                     parse_message_len( length );
470                 } else {
471                     SG_LOG( SG_IO, SG_ALERT,
472                             "Generic protocol: Received binary "
473                             "record of unexpected size, expected: "
474                             << binary_record_length << " but received: "
475                             << length);
476                 }
477             }
478         } else {
479             if (!binary_mode) {
480                 while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
481                     parse_message_len( length );
482                 }
483             } else {
484                 while ((length = io->read( buf, binary_record_length )) 
485                           == binary_record_length ) {
486                     parse_message_len( length );
487                 }
488
489                 if ( length > 0 ) {
490                     SG_LOG( SG_IO, SG_ALERT,
491                         "Generic protocol: Received binary "
492                         "record of unexpected size, expected: "
493                         << binary_record_length << " but received: "
494                         << length);
495                 }
496             }
497         }
498     }
499     return true;
500 error_out:
501     if (exitOnError) {
502         fgOSExit(1);
503         return true; // should not get there, but please the compiler
504     } else
505         return false;
506 }
507
508
509 // close the channel
510 bool FGGeneric::close() {
511     SGIOChannel *io = get_io_channel();
512
513     if ( ((get_direction() == SG_IO_OUT)||
514           (get_direction() == SG_IO_BI))
515           && ! postamble.empty() ) {
516         if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
517             SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
518             return false;
519         }
520     }
521
522     set_enabled( false );
523
524     if ( ! io->close() ) {
525         return false;
526     }
527
528     return true;
529 }
530
531
532 void
533 FGGeneric::reinit()
534 {
535     SGPath path( globals->get_fg_root() );
536     path.append("Protocol");
537     path.append(file_name.c_str());
538
539     SG_LOG(SG_NETWORK, SG_INFO, "Reading communication protocol from "
540                                 << path.str());
541
542     SGPropertyNode root;
543     try {
544         readProperties(path.str(), &root);
545     } catch (const sg_exception & ex) {
546         SG_LOG(SG_NETWORK, SG_ALERT,
547          "Unable to load the protocol configuration file: " << ex.getFormattedMessage() );
548          return;
549     }
550
551     if (direction == "out") {
552         SGPropertyNode *output = root.getNode("generic/output");
553         if (output) {
554             _out_message.clear();
555             if (!read_config(output, _out_message))
556             {
557                 // bad configuration
558                 return;
559             }
560         }
561     } else if (direction == "in") {
562         SGPropertyNode *input = root.getNode("generic/input");
563         if (input) {
564             _in_message.clear();
565             if (!read_config(input, _in_message))
566             {
567                 // bad configuration
568                 return;
569             }
570             if (!binary_mode && (line_separator.size() == 0 ||
571                 *line_separator.rbegin() != '\n')) {
572
573                 SG_LOG(SG_IO, SG_WARN,
574                     "Warning: Appending newline to line separator in generic input.");
575                 line_separator.push_back('\n');
576             }
577         }
578     }
579
580     initOk = true;
581 }
582
583
584 bool
585 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
586 {
587     binary_mode = root->getBoolValue("binary_mode");
588
589     if (!binary_mode) {
590         /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml
591          * file for each format
592          *
593          * var_sep_string  = the string/charachter to place between variables
594          * line_sep_string = the string/charachter to place at the end of each
595          *                   lot of variables
596          */
597         preamble = unescape(root->getStringValue("preamble"));
598         postamble = unescape(root->getStringValue("postamble"));
599         var_sep_string = unescape(root->getStringValue("var_separator"));
600         line_sep_string = unescape(root->getStringValue("line_separator"));
601
602         if ( var_sep_string == "newline" ) {
603             var_separator = '\n';
604         } else if ( var_sep_string == "tab" ) {
605             var_separator = '\t';
606         } else if ( var_sep_string == "space" ) {
607             var_separator = ' ';
608         } else if ( var_sep_string == "formfeed" ) {
609             var_separator = '\f';
610         } else if ( var_sep_string == "carriagereturn" ) {
611             var_sep_string = '\r';
612         } else if ( var_sep_string == "verticaltab" ) {
613             var_separator = '\v';
614         } else {
615             var_separator = var_sep_string;
616         }
617
618         if ( line_sep_string == "newline" ) {
619             line_separator = '\n';
620         } else if ( line_sep_string == "tab" ) {
621             line_separator = '\t';
622         } else if ( line_sep_string == "space" ) {
623             line_separator = ' ';
624         } else if ( line_sep_string == "formfeed" ) {
625             line_separator = '\f';
626         } else if ( line_sep_string == "carriagereturn" ) {
627             line_separator = '\r';
628         } else if ( line_sep_string == "verticaltab" ) {
629             line_separator = '\v';
630         } else {
631             line_separator = line_sep_string;
632         }
633     } else {
634         // default values: no footer and record_length = sizeof(representation)
635         binary_footer_type = FOOTER_NONE;
636         binary_record_length = -1;
637
638         // default choice is network byte order (big endian)
639         if (sgIsLittleEndian()) {
640            binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
641         } else {
642            binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
643         }
644
645         if ( root->hasValue("binary_footer") ) {
646             string footer_type = root->getStringValue("binary_footer");
647             if ( footer_type == "length" ) {
648                 binary_footer_type = FOOTER_LENGTH;
649             } else if ( footer_type.substr(0, 5) == "magic" ) {
650                 binary_footer_type = FOOTER_MAGIC;
651                 binary_footer_value = strtol(footer_type.substr(6, 
652                             footer_type.length() - 6).c_str(), (char**)0, 0);
653             } else if ( footer_type != "none" ) {
654                 SG_LOG(SG_IO, SG_ALERT,
655                        "generic protocol: Unknown generic binary protocol "
656                        "footer '" << footer_type << "', using no footer.");
657             }
658         }
659
660         if ( root->hasValue("record_length") ) {
661             binary_record_length = root->getIntValue("record_length");
662         }
663
664         if ( root->hasValue("byte_order") ) {
665             string byte_order = root->getStringValue("byte_order");
666             if (byte_order == "network" ) {
667                 if ( sgIsLittleEndian() ) {
668                     binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
669                 } else {
670                     binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
671                 }
672             } else if ( byte_order == "host" ) {
673                 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
674             } else {
675                 SG_LOG(SG_IO, SG_ALERT,
676                        "generic protocol: Undefined generic binary protocol"
677                        "byte order, using HOST byte order.");
678             }
679         }
680     }
681
682     int record_length = 0; // Only used for binary protocols.
683     vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
684
685     for (unsigned int i = 0; i < chunks.size(); i++) {
686
687         _serial_prot chunk;
688
689         // chunk.name = chunks[i]->getStringValue("name");
690         chunk.format = unescape(chunks[i]->getStringValue("format", "%d"));
691         chunk.offset = chunks[i]->getDoubleValue("offset");
692         chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
693         chunk.min = chunks[i]->getDoubleValue("min");
694         chunk.max = chunks[i]->getDoubleValue("max");
695         chunk.wrap = chunks[i]->getBoolValue("wrap");
696         chunk.rel = chunks[i]->getBoolValue("relative");
697
698         string node = chunks[i]->getStringValue("node", "/null");
699         chunk.prop = fgGetNode(node.c_str(), true);
700
701         string type = chunks[i]->getStringValue("type");
702
703         // Note: officially the type is called 'bool' but for backward
704         //       compatibility 'boolean' will also be supported.
705         if (type == "bool" || type == "boolean") {
706             chunk.type = FG_BOOL;
707             record_length += 1;
708         } else if (type == "float") {
709             chunk.type = FG_FLOAT;
710             record_length += sizeof(int32_t);
711         } else if (type == "double") {
712             chunk.type = FG_DOUBLE;
713             record_length += sizeof(int64_t);
714         } else if (type == "fixed") {
715             chunk.type = FG_FIXED;
716             record_length += sizeof(int32_t);
717         } else if (type == "string")
718             chunk.type = FG_STRING;
719         else {
720             chunk.type = FG_INT;
721             record_length += sizeof(int32_t);
722         }
723         msg.push_back(chunk);
724
725     }
726
727     if( !binary_mode )
728     {
729         if ((chunks.size() > 1)&&(var_sep_string.length() == 0))
730         {
731             // ASCII protocols really need a separator when there is more than one chunk per line
732             SG_LOG(SG_IO, SG_ALERT,
733                    "generic protocol: Invalid configuration. "
734                    "'var_separator' must not be empty for protocols which have more than one chunk per line.");
735             return false;
736         }
737     }
738     else
739     {
740         if (binary_record_length == -1) {
741             binary_record_length = record_length;
742         } else if (binary_record_length < record_length) {
743             SG_LOG(SG_IO, SG_ALERT,
744                    "generic protocol: Requested binary record length shorter than "
745                    " requested record representation.");
746             binary_record_length = record_length;
747         }
748     }
749
750     return true;
751 }
752
753 void FGGeneric::updateValue(FGGeneric::_serial_prot& prot, bool val)
754 {
755   if( prot.rel )
756   {
757     // value inverted if received true, otherwise leave unchanged
758     if( val )
759       setValue(prot.prop, !getValue<bool>(prot.prop));
760   }
761   else
762   {
763     setValue(prot.prop, val);
764   }
765 }