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