]> git.mxchange.org Git - flightgear.git/blob - src/Network/generic.cxx
Use helper to validate printf formats.
[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         string format = simgear::strutils::sanitizePrintfFormat(_out_message[i].format);
224
225         switch (_out_message[i].type) {
226         case FG_INT:
227             val = _out_message[i].offset +
228                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
229             snprintf(tmp, 255, format.c_str(), (int)val);
230             break;
231
232         case FG_BOOL:
233             snprintf(tmp, 255, format.c_str(),
234                                _out_message[i].prop->getBoolValue());
235             break;
236
237         case FG_FIXED:
238             val = _out_message[i].offset +
239                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
240             snprintf(tmp, 255, format.c_str(), (float)val);
241             break;
242
243         case FG_FLOAT:
244             val = _out_message[i].offset +
245                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
246             snprintf(tmp, 255, format.c_str(), (float)val);
247             break;
248
249         case FG_DOUBLE:
250             val = _out_message[i].offset +
251                 _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
252             snprintf(tmp, 255, format.c_str(), (double)val);
253             break;
254
255         default: // SG_STRING
256             snprintf(tmp, 255, format.c_str(),
257                                    _out_message[i].prop->getStringValue());
258         }
259
260         generic_sentence += tmp;
261     }
262
263     /* After each lot of variables has been added, put the line separator
264      * char/string
265      */
266     generic_sentence += line_separator;
267
268     length =  generic_sentence.length();
269     strncpy( buf, generic_sentence.c_str(), length );
270
271     return true;
272 }
273
274 bool FGGeneric::gen_message() {
275     if (binary_mode) {
276         return gen_message_binary();
277     } else {
278         return gen_message_ascii();
279     }
280 }
281
282 bool FGGeneric::parse_message_binary(int length) {
283     char *p2, *p1 = buf;
284     int32_t tmp32;
285     int i = -1;
286
287     p2 = p1 + length;
288     while ((++i < (int)_in_message.size()) && (p1  < p2)) {
289
290         switch (_in_message[i].type) {
291         case FG_INT:
292             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
293                 tmp32 = sg_bswap_32(*(int32_t *)p1);
294             } else {
295                 tmp32 = *(int32_t *)p1;
296             }
297             updateValue(_in_message[i], (int)tmp32);
298             p1 += sizeof(int32_t);
299             break;
300
301         case FG_BOOL:
302             updateValue(_in_message[i], p1[0] != 0);
303             p1 += 1;
304             break;
305
306         case FG_FIXED:
307             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
308                 tmp32 = sg_bswap_32(*(int32_t *)p1);
309             } else {
310                 tmp32 = *(int32_t *)p1;
311             }
312             updateValue(_in_message[i], (float)tmp32 / 65536.0f);
313             p1 += sizeof(int32_t);
314             break;
315
316         case FG_FLOAT:
317             u32 tmpun32;
318             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
319                 tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1);
320             } else {
321                 tmpun32.floatVal = *(float *)p1;
322             }
323             updateValue(_in_message[i], tmpun32.floatVal);
324             p1 += sizeof(int32_t);
325             break;
326
327         case FG_DOUBLE:
328             u64 tmpun64;
329             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
330                 tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1);
331             } else {
332                 tmpun64.doubleVal = *(double *)p1;
333             }
334             updateValue(_in_message[i], tmpun64.doubleVal);
335             p1 += sizeof(int64_t);
336             break;
337
338         default: // SG_STRING
339             SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
340                     "Ignoring unsupported binary input chunk type.");
341             break;
342         }
343     }
344     
345     return true;
346 }
347
348 bool FGGeneric::parse_message_ascii(int length) {
349     char *p1 = buf;
350     int i = -1;
351     int chunks = _in_message.size();
352     int line_separator_size = line_separator.size();
353
354     if (length < line_separator_size ||
355         line_separator.compare(buf + length - line_separator_size) != 0) {
356
357         SG_LOG(SG_IO, SG_WARN,
358                "Input line does not end with expected line separator." );
359     } else {
360         buf[length - line_separator_size] = 0;
361     }
362
363     size_t varsep_len = var_separator.length();
364     while ((++i < chunks) && p1) {
365         char* p2 = NULL;
366
367         if (varsep_len > 0)
368         {
369             p2 = strstr(p1, var_separator.c_str());
370             if (p2) {
371                 *p2 = 0;
372                 p2 += varsep_len;
373             }
374         }
375
376         switch (_in_message[i].type) {
377         case FG_INT:
378             updateValue(_in_message[i], atoi(p1));
379             break;
380
381         case FG_BOOL:
382             updateValue(_in_message[i], atof(p1) != 0.0);
383             break;
384
385         case FG_FIXED:
386         case FG_FLOAT:
387             updateValue(_in_message[i], (float)strtod(p1, 0));
388             break;
389
390         case FG_DOUBLE:
391             updateValue(_in_message[i], (double)strtod(p1, 0));
392             break;
393
394         default: // SG_STRING
395             _in_message[i].prop->setStringValue(p1);
396             break;
397         }
398
399         p1 = p2;
400     }
401
402     return true;
403 }
404
405 bool FGGeneric::parse_message_len(int length) {
406     if (binary_mode) {
407         return parse_message_binary(length);
408     } else {
409         return parse_message_ascii(length);
410     }
411 }
412
413
414 // open hailing frequencies
415 bool FGGeneric::open() {
416     if ( is_enabled() ) {
417         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
418                 << "is already in use, ignoring" );
419         return false;
420     }
421
422     SGIOChannel *io = get_io_channel();
423
424     if ( ! io->open( get_direction() ) ) {
425         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
426         return false;
427     }
428
429     set_enabled( true );
430
431     if ( ((get_direction() == SG_IO_OUT )||
432           (get_direction() == SG_IO_BI))
433           && ! preamble.empty() ) {
434         if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
435             SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
436             return false;
437         }
438     }
439
440     return true;
441 }
442
443
444 // process work for this port
445 bool FGGeneric::process() {
446     SGIOChannel *io = get_io_channel();
447
448     if ( (get_direction() == SG_IO_OUT) ||
449          (get_direction() == SG_IO_BI) ) {
450         gen_message();
451         if ( ! io->write( buf, length ) ) {
452             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
453             goto error_out;
454         }
455     }
456
457     if (( get_direction() == SG_IO_IN ) ||
458         (get_direction() == SG_IO_BI) ) {
459         if ( io->get_type() == sgFileType ) {
460             if (!binary_mode) {
461                 length = io->readline( buf, FG_MAX_MSG_SIZE );
462                 if ( length > 0 ) {
463                     parse_message_len( length );
464                 } else {
465                     SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
466                     return false;
467                 }
468             } else {
469                 length = io->read( buf, binary_record_length );
470                 if ( length == binary_record_length ) {
471                     parse_message_len( length );
472                 } else {
473                     SG_LOG( SG_IO, SG_ALERT,
474                             "Generic protocol: Received binary "
475                             "record of unexpected size, expected: "
476                             << binary_record_length << " but received: "
477                             << length);
478                 }
479             }
480         } else {
481             if (!binary_mode) {
482                 while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
483                     parse_message_len( length );
484                 }
485             } else {
486                 while ((length = io->read( buf, binary_record_length )) 
487                           == binary_record_length ) {
488                     parse_message_len( length );
489                 }
490
491                 if ( length > 0 ) {
492                     SG_LOG( SG_IO, SG_ALERT,
493                         "Generic protocol: Received binary "
494                         "record of unexpected size, expected: "
495                         << binary_record_length << " but received: "
496                         << length);
497                 }
498             }
499         }
500     }
501     return true;
502 error_out:
503     if (exitOnError) {
504         fgOSExit(1);
505         return true; // should not get there, but please the compiler
506     } else
507         return false;
508 }
509
510
511 // close the channel
512 bool FGGeneric::close() {
513     SGIOChannel *io = get_io_channel();
514
515     if ( ((get_direction() == SG_IO_OUT)||
516           (get_direction() == SG_IO_BI))
517           && ! postamble.empty() ) {
518         if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
519             SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
520             return false;
521         }
522     }
523
524     set_enabled( false );
525
526     if ( ! io->close() ) {
527         return false;
528     }
529
530     return true;
531 }
532
533
534 void
535 FGGeneric::reinit()
536 {
537     SGPath path( globals->get_fg_root() );
538     path.append("Protocol");
539     path.append(file_name.c_str());
540
541     SG_LOG(SG_NETWORK, SG_INFO, "Reading communication protocol from "
542                                 << path.str());
543
544     SGPropertyNode root;
545     try {
546         readProperties(path.str(), &root);
547     } catch (const sg_exception & ex) {
548         SG_LOG(SG_NETWORK, SG_ALERT,
549          "Unable to load the protocol configuration file: " << ex.getFormattedMessage() );
550          return;
551     }
552
553     if (direction == "out") {
554         SGPropertyNode *output = root.getNode("generic/output");
555         if (output) {
556             _out_message.clear();
557             if (!read_config(output, _out_message))
558             {
559                 // bad configuration
560                 return;
561             }
562         }
563     } else if (direction == "in") {
564         SGPropertyNode *input = root.getNode("generic/input");
565         if (input) {
566             _in_message.clear();
567             if (!read_config(input, _in_message))
568             {
569                 // bad configuration
570                 return;
571             }
572             if (!binary_mode && (line_separator.empty() ||
573                 *line_separator.rbegin() != '\n')) {
574
575                 SG_LOG(SG_IO, SG_WARN,
576                     "Warning: Appending newline to line separator in generic input.");
577                 line_separator.push_back('\n');
578             }
579         }
580     }
581
582     initOk = true;
583 }
584
585
586 bool
587 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
588 {
589     binary_mode = root->getBoolValue("binary_mode");
590
591     if (!binary_mode) {
592         /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml
593          * file for each format
594          *
595          * var_sep_string  = the string/charachter to place between variables
596          * line_sep_string = the string/charachter to place at the end of each
597          *                   lot of variables
598          */
599         preamble = unescape(root->getStringValue("preamble"));
600         postamble = unescape(root->getStringValue("postamble"));
601         var_sep_string = unescape(root->getStringValue("var_separator"));
602         line_sep_string = unescape(root->getStringValue("line_separator"));
603
604         if ( var_sep_string == "newline" ) {
605             var_separator = '\n';
606         } else if ( var_sep_string == "tab" ) {
607             var_separator = '\t';
608         } else if ( var_sep_string == "space" ) {
609             var_separator = ' ';
610         } else if ( var_sep_string == "formfeed" ) {
611             var_separator = '\f';
612         } else if ( var_sep_string == "carriagereturn" ) {
613             var_sep_string = '\r';
614         } else if ( var_sep_string == "verticaltab" ) {
615             var_separator = '\v';
616         } else {
617             var_separator = var_sep_string;
618         }
619
620         if ( line_sep_string == "newline" ) {
621             line_separator = '\n';
622         } else if ( line_sep_string == "tab" ) {
623             line_separator = '\t';
624         } else if ( line_sep_string == "space" ) {
625             line_separator = ' ';
626         } else if ( line_sep_string == "formfeed" ) {
627             line_separator = '\f';
628         } else if ( line_sep_string == "carriagereturn" ) {
629             line_separator = '\r';
630         } else if ( line_sep_string == "verticaltab" ) {
631             line_separator = '\v';
632         } else {
633             line_separator = line_sep_string;
634         }
635     } else {
636         // default values: no footer and record_length = sizeof(representation)
637         binary_footer_type = FOOTER_NONE;
638         binary_record_length = -1;
639
640         // default choice is network byte order (big endian)
641         if (sgIsLittleEndian()) {
642            binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
643         } else {
644            binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
645         }
646
647         if ( root->hasValue("binary_footer") ) {
648             string footer_type = root->getStringValue("binary_footer");
649             if ( footer_type == "length" ) {
650                 binary_footer_type = FOOTER_LENGTH;
651             } else if ( footer_type.substr(0, 5) == "magic" ) {
652                 binary_footer_type = FOOTER_MAGIC;
653                 binary_footer_value = strtol(footer_type.substr(6, 
654                             footer_type.length() - 6).c_str(), (char**)0, 0);
655             } else if ( footer_type != "none" ) {
656                 SG_LOG(SG_IO, SG_ALERT,
657                        "generic protocol: Unknown generic binary protocol "
658                        "footer '" << footer_type << "', using no footer.");
659             }
660         }
661
662         if ( root->hasValue("record_length") ) {
663             binary_record_length = root->getIntValue("record_length");
664         }
665
666         if ( root->hasValue("byte_order") ) {
667             string byte_order = root->getStringValue("byte_order");
668             if (byte_order == "network" ) {
669                 if ( sgIsLittleEndian() ) {
670                     binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
671                 } else {
672                     binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
673                 }
674             } else if ( byte_order == "host" ) {
675                 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
676             } else {
677                 SG_LOG(SG_IO, SG_ALERT,
678                        "generic protocol: Undefined generic binary protocol"
679                        "byte order, using HOST byte order.");
680             }
681         }
682     }
683
684     int record_length = 0; // Only used for binary protocols.
685     vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
686
687     for (unsigned int i = 0; i < chunks.size(); i++) {
688
689         _serial_prot chunk;
690
691         // chunk.name = chunks[i]->getStringValue("name");
692         chunk.format = unescape(chunks[i]->getStringValue("format", "%d"));
693         chunk.offset = chunks[i]->getDoubleValue("offset");
694         chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
695         chunk.min = chunks[i]->getDoubleValue("min");
696         chunk.max = chunks[i]->getDoubleValue("max");
697         chunk.wrap = chunks[i]->getBoolValue("wrap");
698         chunk.rel = chunks[i]->getBoolValue("relative");
699
700         string node = chunks[i]->getStringValue("node", "/null");
701         chunk.prop = fgGetNode(node.c_str(), true);
702
703         string type = chunks[i]->getStringValue("type");
704
705         // Note: officially the type is called 'bool' but for backward
706         //       compatibility 'boolean' will also be supported.
707         if (type == "bool" || type == "boolean") {
708             chunk.type = FG_BOOL;
709             record_length += 1;
710         } else if (type == "float") {
711             chunk.type = FG_FLOAT;
712             record_length += sizeof(int32_t);
713         } else if (type == "double") {
714             chunk.type = FG_DOUBLE;
715             record_length += sizeof(int64_t);
716         } else if (type == "fixed") {
717             chunk.type = FG_FIXED;
718             record_length += sizeof(int32_t);
719         } else if (type == "string")
720             chunk.type = FG_STRING;
721         else {
722             chunk.type = FG_INT;
723             record_length += sizeof(int32_t);
724         }
725         msg.push_back(chunk);
726
727     }
728
729     if( !binary_mode )
730     {
731         if ((chunks.size() > 1)&&(var_sep_string.length() == 0))
732         {
733             // ASCII protocols really need a separator when there is more than one chunk per line
734             SG_LOG(SG_IO, SG_ALERT,
735                    "generic protocol: Invalid configuration. "
736                    "'var_separator' must not be empty for protocols which have more than one chunk per line.");
737             return false;
738         }
739     }
740     else
741     {
742         if (binary_record_length == -1) {
743             binary_record_length = record_length;
744         } else if (binary_record_length < record_length) {
745             SG_LOG(SG_IO, SG_ALERT,
746                    "generic protocol: Requested binary record length shorter than "
747                    " requested record representation.");
748             binary_record_length = record_length;
749         }
750     }
751
752     return true;
753 }
754
755 void FGGeneric::updateValue(FGGeneric::_serial_prot& prot, bool val)
756 {
757   if( prot.rel )
758   {
759     // value inverted if received true, otherwise leave unchanged
760     if( val )
761       setValue(prot.prop, !getValue<bool>(prot.prop));
762   }
763   else
764   {
765     setValue(prot.prop, val);
766   }
767 }