]> git.mxchange.org Git - flightgear.git/blob - src/Network/generic.cxx
generic binary protocol: add 'word' datatype
[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 class FGProtocolWrapper {
50 public:
51   virtual ~FGProtocolWrapper() {}
52   virtual int wrap( size_t n, uint8_t * buf ) = 0;
53   virtual int unwrap( size_t n, uint8_t * buf ) = 0;
54 };
55
56 /**
57  * http://www.ka9q.net/papers/kiss.html
58  */
59 class FGKissWrapper : public FGProtocolWrapper {
60 public:
61   virtual int wrap( size_t n, uint8_t * buf );
62   virtual int unwrap( size_t n, uint8_t * buf );
63 private:
64   static const uint8_t  FEND;
65   static const uint8_t  FESC;
66   static const uint8_t  TFEND;
67   static const uint8_t  TFESC;
68 };
69
70 const uint8_t  FGKissWrapper::FEND  = 0xC0;
71 const uint8_t  FGKissWrapper::FESC  = 0xDB;
72 const uint8_t  FGKissWrapper::TFEND = 0xDC;
73 const uint8_t  FGKissWrapper::TFESC = 0xDD;
74
75 int FGKissWrapper::wrap( size_t n, uint8_t * buf )
76 {
77   std::vector<uint8_t> dest;
78   uint8_t *sp = buf;
79
80   dest.push_back(FEND);
81   dest.push_back(0); // command/channel always zero
82   for( size_t i = 0; i < n; i++ ) {
83     uint8_t c = *sp++;
84     switch( c ) {
85       case FESC:
86         dest.push_back(FESC);
87         dest.push_back(TFESC);
88         break;
89
90       case FEND:
91         dest.push_back(FESC);
92         dest.push_back(TFEND);
93         break;
94
95       default:
96         dest.push_back(c);
97         break;
98     }
99   }
100   dest.push_back(FEND);
101
102   memcpy( buf, dest.data(), dest.size() );
103   return dest.size();
104 }
105
106 int FGKissWrapper::unwrap( size_t n, uint8_t * buf )
107 {
108   uint8_t * sp = buf;
109
110   // look for FEND
111   while( 0 < n && FEND != *sp ) {
112     sp++;
113     n--;
114   }
115
116   // ignore all leading FEND
117   while( 0 < n && FEND == *sp ) {
118     sp++;
119     n--;
120   }
121
122   if( 0 == n ) return 0;
123
124   std::vector<uint8_t> dest;
125   {
126     bool escaped = false;
127
128     while( 0 < n ) {
129
130       n--;
131       uint8_t c = *sp++;
132
133       if( escaped ) {
134         switch( c ) {
135
136           case TFESC:
137             dest.push_back( FESC );
138             break;
139
140           case TFEND:
141             dest.push_back( FEND );
142             break;
143
144           default: // this is an error - ignore and continue
145             break;
146         }
147
148         escaped = false;
149
150       } else {
151
152         switch( c ) {
153           case FESC:
154             escaped = true;
155             break;
156
157           case FEND:
158             if( 0 != n ) {
159               SG_LOG(SG_IO, SG_WARN,
160                "KISS frame detected FEND before end of frame. Trailing data dropped." );
161             }
162             n = 0; 
163             break;
164
165           default:
166             dest.push_back( c );
167             break;
168         }
169       }
170     }
171   }
172
173   memcpy( buf, dest.data(), dest.size() );
174   return dest.size();
175 }
176
177
178 class FGSTXETXWrapper : public FGProtocolWrapper {
179 public:
180   virtual int wrap( size_t n, uint8_t * buf );
181   virtual int unwrap( size_t n, uint8_t * buf );
182
183   static const uint8_t  STX;
184   static const uint8_t  ETX;
185   static const uint8_t  DLE;
186 };
187
188 const uint8_t  FGSTXETXWrapper::STX  = 0x02;
189 const uint8_t  FGSTXETXWrapper::ETX  = 0x03;
190 const uint8_t  FGSTXETXWrapper::DLE  = 0x00;
191
192 int FGSTXETXWrapper::wrap( size_t n, uint8_t * buf )
193 {
194   // stuff payload as
195   // <dle><stx>payload<dle><etx>
196   // if payload contains <dle>, stuff <dle> as <dle><dle>
197   std::vector<uint8_t> dest;
198   uint8_t *sp = buf;
199
200   dest.push_back(DLE);
201   dest.push_back(STX);
202
203   while( n > 0 ) {
204     n--;
205
206     if( DLE == *sp )
207       dest.push_back(DLE);
208
209     dest.push_back(*sp++);
210   }
211
212   dest.push_back(DLE);
213   dest.push_back(ETX);
214
215   memcpy( buf, dest.data(), dest.size() ); 
216   return dest.size();
217 }
218
219 int FGSTXETXWrapper::unwrap( size_t n, uint8_t * buf )
220 {
221   return n;
222 }
223
224 FGGeneric::FGGeneric(vector<string> tokens) : exitOnError(false), initOk(false), wrapper(NULL)
225 {
226     size_t configToken;
227     if (tokens[1] == "socket") {
228         configToken = 7;
229     } else if (tokens[1] == "file") {
230         configToken = 5;
231     } else {
232         configToken = 6; 
233     }
234
235     if ((configToken >= tokens.size())||(tokens[ configToken ] == "")) {
236        SG_LOG(SG_NETWORK, SG_ALERT,
237               "Not enough tokens passed for generic '" << tokens[1] << "' protocol. ");
238        return;
239     }
240
241     string config = tokens[ configToken ];
242     file_name = config+".xml";
243     direction = tokens[2];
244
245     if (direction != "in" && direction != "out" && direction != "bi") {
246         SG_LOG(SG_NETWORK, SG_ALERT, "Unsuported protocol direction: "
247                << direction);
248         return;
249     }
250
251     reinit();
252 }
253
254 FGGeneric::~FGGeneric() {
255   delete wrapper;
256 }
257
258 union u32 {
259     uint32_t intVal;
260     float floatVal;
261 };
262
263 union u64 {
264     uint64_t longVal;
265     double doubleVal;
266 };
267
268 // generate the message
269 bool FGGeneric::gen_message_binary() {
270     string generic_sentence;
271     length = 0;
272
273     double val;
274     for (unsigned int i = 0; i < _out_message.size(); i++) {
275
276         switch (_out_message[i].type) {
277         case FG_INT:
278         {
279             val = _out_message[i].offset +
280                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
281             int32_t intVal = val;
282             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
283                 intVal = (int32_t) sg_bswap_32((uint32_t)intVal);
284             }
285             memcpy(&buf[length], &intVal, sizeof(int32_t));
286             length += sizeof(int32_t);
287             break;
288         }
289
290         case FG_BOOL:
291             buf[length] = (char) (_out_message[i].prop->getBoolValue() ? true : false);
292             length += 1;
293             break;
294
295         case FG_FIXED:
296         {
297             val = _out_message[i].offset +
298                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
299
300             int32_t fixed = (int)(val * 65536.0f);
301             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
302                 fixed = (int32_t) sg_bswap_32((uint32_t)fixed);
303             } 
304             memcpy(&buf[length], &fixed, sizeof(int32_t));
305             length += sizeof(int32_t);
306             break;
307         }
308
309         case FG_FLOAT:
310         {
311             val = _out_message[i].offset +
312                  _out_message[i].prop->getFloatValue() * _out_message[i].factor;
313             u32 tmpun32;
314             tmpun32.floatVal = static_cast<float>(val);
315
316             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
317                 tmpun32.intVal = sg_bswap_32(tmpun32.intVal);
318             }
319             memcpy(&buf[length], &tmpun32.intVal, sizeof(uint32_t));
320             length += sizeof(uint32_t);
321             break;
322         }
323
324         case FG_DOUBLE:
325         {
326             val = _out_message[i].offset +
327                  _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
328             u64 tmpun64;
329             tmpun64.doubleVal = val;
330
331             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
332                 tmpun64.longVal = sg_bswap_64(tmpun64.longVal);
333             }
334             memcpy(&buf[length], &tmpun64.longVal, sizeof(uint64_t));
335             length += sizeof(uint64_t);
336             break;
337         }
338
339         case FG_BYTE:
340         {
341             val = _out_message[i].offset +
342                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
343             int8_t byteVal = val;
344             memcpy(&buf[length], &byteVal, sizeof(int8_t));
345             length += sizeof(int8_t);
346             break;
347         }
348
349         case FG_WORD:
350         {
351             val = _out_message[i].offset +
352                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
353             int16_t wordVal = val;
354             memcpy(&buf[length], &wordVal, sizeof(int16_t));
355             length += sizeof(int16_t);
356             break;
357         }
358
359         default: // SG_STRING
360             const char *strdata = _out_message[i].prop->getStringValue();
361             int32_t strlength = strlen(strdata);
362
363             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
364                 SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
365                         "FG_STRING will be written in host byte order.");
366             }
367             /* Format for strings is 
368              * [length as int, 4 bytes][ASCII data, length bytes]
369              */
370             if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
371                 strlength = sg_bswap_32(strlength);
372             }
373             memcpy(&buf[length], &strlength, sizeof(int32_t));
374             length += sizeof(int32_t);
375             strncpy(&buf[length], strdata, strlength);
376             length += strlength; 
377             /* FIXME padding for alignment? Something like: 
378              * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0;
379              */
380             break;
381
382         }
383     }
384
385     // add the footer to the packet ("line")
386     switch (binary_footer_type) {
387         case FOOTER_LENGTH:
388             binary_footer_value = length;
389             break;
390
391         case FOOTER_MAGIC:
392         case FOOTER_NONE:
393             break;
394     }
395
396     if (binary_footer_type != FOOTER_NONE) {
397         int32_t intValue = binary_footer_value;
398         if (binary_byte_order != BYTE_ORDER_MATCHES_NETWORK_ORDER) {
399             intValue = sg_bswap_32(binary_footer_value);
400         }
401         memcpy(&buf[length], &intValue, sizeof(int32_t));
402         length += sizeof(int32_t);
403     }
404
405     if( wrapper ) length = wrapper->wrap( length, reinterpret_cast<uint8_t*>(buf) );
406
407     return true;
408 }
409
410 bool FGGeneric::gen_message_ascii() {
411     string generic_sentence;
412     char tmp[255];
413     length = 0;
414
415     double val;
416     for (unsigned int i = 0; i < _out_message.size(); i++) {
417
418         if (i > 0) {
419             generic_sentence += var_separator;
420         }
421         
422         string format = simgear::strutils::sanitizePrintfFormat(_out_message[i].format);
423
424         switch (_out_message[i].type) {
425         case FG_BYTE:
426         case FG_WORD:
427         case FG_INT:
428             val = _out_message[i].offset +
429                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
430             snprintf(tmp, 255, format.c_str(), (int)val);
431             break;
432
433         case FG_BOOL:
434             snprintf(tmp, 255, format.c_str(),
435                                _out_message[i].prop->getBoolValue());
436             break;
437
438         case FG_FIXED:
439             val = _out_message[i].offset +
440                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
441             snprintf(tmp, 255, format.c_str(), (float)val);
442             break;
443
444         case FG_FLOAT:
445             val = _out_message[i].offset +
446                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
447             snprintf(tmp, 255, format.c_str(), (float)val);
448             break;
449
450         case FG_DOUBLE:
451             val = _out_message[i].offset +
452                 _out_message[i].prop->getDoubleValue() * _out_message[i].factor;
453             snprintf(tmp, 255, format.c_str(), (double)val);
454             break;
455
456         default: // SG_STRING
457             snprintf(tmp, 255, format.c_str(),
458                                    _out_message[i].prop->getStringValue());
459         }
460
461         generic_sentence += tmp;
462     }
463
464     /* After each lot of variables has been added, put the line separator
465      * char/string
466      */
467     generic_sentence += line_separator;
468
469     length =  generic_sentence.length();
470     strncpy( buf, generic_sentence.c_str(), length );
471
472     return true;
473 }
474
475 bool FGGeneric::gen_message() {
476     if (binary_mode) {
477         return gen_message_binary();
478     } else {
479         return gen_message_ascii();
480     }
481 }
482
483 bool FGGeneric::parse_message_binary(int length) {
484     char *p2, *p1 = buf;
485     int32_t tmp32;
486     int i = -1;
487
488     p2 = p1 + length;
489     while ((++i < (int)_in_message.size()) && (p1  < p2)) {
490
491         switch (_in_message[i].type) {
492         case FG_INT:
493             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
494                 tmp32 = sg_bswap_32(*(int32_t *)p1);
495             } else {
496                 tmp32 = *(int32_t *)p1;
497             }
498             updateValue(_in_message[i], (int)tmp32);
499             p1 += sizeof(int32_t);
500             break;
501
502         case FG_BOOL:
503             updateValue(_in_message[i], p1[0] != 0);
504             p1 += 1;
505             break;
506
507         case FG_FIXED:
508             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
509                 tmp32 = sg_bswap_32(*(int32_t *)p1);
510             } else {
511                 tmp32 = *(int32_t *)p1;
512             }
513             updateValue(_in_message[i], (float)tmp32 / 65536.0f);
514             p1 += sizeof(int32_t);
515             break;
516
517         case FG_FLOAT:
518             u32 tmpun32;
519             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
520                 tmpun32.intVal = sg_bswap_32(*(uint32_t *)p1);
521             } else {
522                 tmpun32.floatVal = *(float *)p1;
523             }
524             updateValue(_in_message[i], tmpun32.floatVal);
525             p1 += sizeof(int32_t);
526             break;
527
528         case FG_DOUBLE:
529             u64 tmpun64;
530             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
531                 tmpun64.longVal = sg_bswap_64(*(uint64_t *)p1);
532             } else {
533                 tmpun64.doubleVal = *(double *)p1;
534             }
535             updateValue(_in_message[i], tmpun64.doubleVal);
536             p1 += sizeof(int64_t);
537             break;
538
539         case FG_BYTE:
540             tmp32 = *(int8_t *)p1;
541             updateValue(_in_message[i], (int)tmp32);
542             p1 += sizeof(int8_t);
543             break;
544
545         case FG_WORD:
546             if (binary_byte_order == BYTE_ORDER_NEEDS_CONVERSION) {
547                 tmp32 = sg_bswap_16(*(int16_t *)p1);
548             } else {
549                 tmp32 = *(int16_t *)p1;
550             }
551             updateValue(_in_message[i], (int)tmp32);
552             p1 += sizeof(int16_t);
553             break;
554
555         default: // SG_STRING
556             SG_LOG( SG_IO, SG_ALERT, "Generic protocol: "
557                     "Ignoring unsupported binary input chunk type.");
558             break;
559         }
560     }
561     
562     return true;
563 }
564
565 bool FGGeneric::parse_message_ascii(int length) {
566     char *p1 = buf;
567     int i = -1;
568     int chunks = _in_message.size();
569     int line_separator_size = line_separator.size();
570
571     if (length < line_separator_size ||
572         line_separator.compare(buf + length - line_separator_size) != 0) {
573
574         SG_LOG(SG_IO, SG_WARN,
575                "Input line does not end with expected line separator." );
576     } else {
577         buf[length - line_separator_size] = 0;
578     }
579
580     size_t varsep_len = var_separator.length();
581     while ((++i < chunks) && p1) {
582         char* p2 = NULL;
583
584         if (varsep_len > 0)
585         {
586             p2 = strstr(p1, var_separator.c_str());
587             if (p2) {
588                 *p2 = 0;
589                 p2 += varsep_len;
590             }
591         }
592
593         switch (_in_message[i].type) {
594         case FG_BYTE:
595         case FG_WORD:
596         case FG_INT:
597             updateValue(_in_message[i], atoi(p1));
598             break;
599
600         case FG_BOOL:
601             updateValue(_in_message[i], atof(p1) != 0.0);
602             break;
603
604         case FG_FIXED:
605         case FG_FLOAT:
606             updateValue(_in_message[i], (float)strtod(p1, 0));
607             break;
608
609         case FG_DOUBLE:
610             updateValue(_in_message[i], (double)strtod(p1, 0));
611             break;
612
613         default: // SG_STRING
614             _in_message[i].prop->setStringValue(p1);
615             break;
616         }
617
618         p1 = p2;
619     }
620
621     return true;
622 }
623
624 bool FGGeneric::parse_message_len(int length) {
625     if (binary_mode) {
626         return parse_message_binary(length);
627     } else {
628         return parse_message_ascii(length);
629     }
630 }
631
632
633 // open hailing frequencies
634 bool FGGeneric::open() {
635     if ( is_enabled() ) {
636         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
637                 << "is already in use, ignoring" );
638         return false;
639     }
640
641     SGIOChannel *io = get_io_channel();
642
643     if ( ! io->open( get_direction() ) ) {
644         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
645         return false;
646     }
647
648     set_enabled( true );
649
650     if ( ((get_direction() == SG_IO_OUT )||
651           (get_direction() == SG_IO_BI))
652           && ! preamble.empty() ) {
653         if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
654             SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
655             return false;
656         }
657     }
658
659     return true;
660 }
661
662
663 // process work for this port
664 bool FGGeneric::process() {
665     SGIOChannel *io = get_io_channel();
666
667     if ( (get_direction() == SG_IO_OUT) ||
668          (get_direction() == SG_IO_BI) ) {
669         gen_message();
670         if ( ! io->write( buf, length ) ) {
671             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
672             goto error_out;
673         }
674     }
675
676     if (( get_direction() == SG_IO_IN ) ||
677         (get_direction() == SG_IO_BI) ) {
678         if ( io->get_type() == sgFileType ) {
679             if (!binary_mode) {
680                 length = io->readline( buf, FG_MAX_MSG_SIZE );
681                 if ( length > 0 ) {
682                     parse_message_len( length );
683                 } else {
684                     SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
685                     return false;
686                 }
687             } else {
688                 length = io->read( buf, binary_record_length );
689                 if ( length == binary_record_length ) {
690                     parse_message_len( length );
691                 } else {
692                     SG_LOG( SG_IO, SG_ALERT,
693                             "Generic protocol: Received binary "
694                             "record of unexpected size, expected: "
695                             << binary_record_length << " but received: "
696                             << length);
697                 }
698             }
699         } else {
700             if (!binary_mode) {
701                 while ((length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
702                     parse_message_len( length );
703                 }
704             } else {
705                 while ((length = io->read( buf, binary_record_length )) 
706                           == binary_record_length ) {
707                     parse_message_len( length );
708                 }
709
710                 if ( length > 0 ) {
711                     SG_LOG( SG_IO, SG_ALERT,
712                         "Generic protocol: Received binary "
713                         "record of unexpected size, expected: "
714                         << binary_record_length << " but received: "
715                         << length);
716                 }
717             }
718         }
719     }
720     return true;
721 error_out:
722     if (exitOnError) {
723         fgOSExit(1);
724         return true; // should not get there, but please the compiler
725     } else
726         return false;
727 }
728
729
730 // close the channel
731 bool FGGeneric::close() {
732     SGIOChannel *io = get_io_channel();
733
734     if ( ((get_direction() == SG_IO_OUT)||
735           (get_direction() == SG_IO_BI))
736           && ! postamble.empty() ) {
737         if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
738             SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
739             return false;
740         }
741     }
742
743     set_enabled( false );
744
745     if ( ! io->close() ) {
746         return false;
747     }
748
749     return true;
750 }
751
752
753 void
754 FGGeneric::reinit()
755 {
756     SGPath path( globals->get_fg_root() );
757     path.append("Protocol");
758     path.append(file_name.c_str());
759
760     SG_LOG(SG_NETWORK, SG_INFO, "Reading communication protocol from "
761                                 << path.str());
762
763     SGPropertyNode root;
764     try {
765         readProperties(path.str(), &root);
766     } catch (const sg_exception & ex) {
767         SG_LOG(SG_NETWORK, SG_ALERT,
768          "Unable to load the protocol configuration file: " << ex.getFormattedMessage() );
769          return;
770     }
771
772     if (direction == "out") {
773         SGPropertyNode *output = root.getNode("generic/output");
774         if (output) {
775             _out_message.clear();
776             if (!read_config(output, _out_message))
777             {
778                 // bad configuration
779                 return;
780             }
781         }
782     } else if (direction == "in") {
783         SGPropertyNode *input = root.getNode("generic/input");
784         if (input) {
785             _in_message.clear();
786             if (!read_config(input, _in_message))
787             {
788                 // bad configuration
789                 return;
790             }
791             if (!binary_mode && (line_separator.empty() ||
792                 *line_separator.rbegin() != '\n')) {
793
794                 SG_LOG(SG_IO, SG_WARN,
795                     "Warning: Appending newline to line separator in generic input.");
796                 line_separator.push_back('\n');
797             }
798         }
799     }
800
801     initOk = true;
802 }
803
804
805 bool
806 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
807 {
808     binary_mode = root->getBoolValue("binary_mode");
809
810     if (!binary_mode) {
811         /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml
812          * file for each format
813          *
814          * var_sep_string  = the string/charachter to place between variables
815          * line_sep_string = the string/charachter to place at the end of each
816          *                   lot of variables
817          */
818         preamble = unescape(root->getStringValue("preamble"));
819         postamble = unescape(root->getStringValue("postamble"));
820         var_sep_string = unescape(root->getStringValue("var_separator"));
821         line_sep_string = unescape(root->getStringValue("line_separator"));
822
823         if ( var_sep_string == "newline" ) {
824             var_separator = '\n';
825         } else if ( var_sep_string == "tab" ) {
826             var_separator = '\t';
827         } else if ( var_sep_string == "space" ) {
828             var_separator = ' ';
829         } else if ( var_sep_string == "formfeed" ) {
830             var_separator = '\f';
831         } else if ( var_sep_string == "carriagereturn" ) {
832             var_sep_string = '\r';
833         } else if ( var_sep_string == "verticaltab" ) {
834             var_separator = '\v';
835         } else {
836             var_separator = var_sep_string;
837         }
838
839         if ( line_sep_string == "newline" ) {
840             line_separator = '\n';
841         } else if ( line_sep_string == "tab" ) {
842             line_separator = '\t';
843         } else if ( line_sep_string == "space" ) {
844             line_separator = ' ';
845         } else if ( line_sep_string == "formfeed" ) {
846             line_separator = '\f';
847         } else if ( line_sep_string == "carriagereturn" ) {
848             line_separator = '\r';
849         } else if ( line_sep_string == "verticaltab" ) {
850             line_separator = '\v';
851         } else {
852             line_separator = line_sep_string;
853         }
854     } else {
855         // default values: no footer and record_length = sizeof(representation)
856         binary_footer_type = FOOTER_NONE;
857         binary_record_length = -1;
858
859         // default choice is network byte order (big endian)
860         if (sgIsLittleEndian()) {
861            binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
862         } else {
863            binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
864         }
865
866         if ( root->hasValue("binary_footer") ) {
867             string footer_type = root->getStringValue("binary_footer");
868             if ( footer_type == "length" ) {
869                 binary_footer_type = FOOTER_LENGTH;
870             } else if ( footer_type.substr(0, 5) == "magic" ) {
871                 binary_footer_type = FOOTER_MAGIC;
872                 binary_footer_value = strtol(footer_type.substr(6, 
873                             footer_type.length() - 6).c_str(), (char**)0, 0);
874             } else if ( footer_type != "none" ) {
875                 SG_LOG(SG_IO, SG_ALERT,
876                        "generic protocol: Unknown generic binary protocol "
877                        "footer '" << footer_type << "', using no footer.");
878             }
879         }
880
881         if ( root->hasValue("record_length") ) {
882             binary_record_length = root->getIntValue("record_length");
883         }
884
885         if ( root->hasValue("byte_order") ) {
886             string byte_order = root->getStringValue("byte_order");
887             if (byte_order == "network" ) {
888                 if ( sgIsLittleEndian() ) {
889                     binary_byte_order = BYTE_ORDER_NEEDS_CONVERSION;
890                 } else {
891                     binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
892                 }
893             } else if ( byte_order == "host" ) {
894                 binary_byte_order = BYTE_ORDER_MATCHES_NETWORK_ORDER;
895             } else {
896                 SG_LOG(SG_IO, SG_ALERT,
897                        "generic protocol: Undefined generic binary protocol"
898                        "byte order, using HOST byte order.");
899             }
900         }
901
902         if( root->hasValue( "wrapper" ) ) {
903             string w = root->getStringValue( "wrapper" );
904             if( w == "kiss" )  wrapper = new FGKissWrapper();
905             else if( w == "stxetx" )  wrapper = new FGSTXETXWrapper();
906             else SG_LOG(SG_IO, SG_ALERT,
907                        "generic protocol: Undefined binary protocol wrapper '" + w + "' ignored" );
908         }
909     }
910
911     int record_length = 0; // Only used for binary protocols.
912     vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
913
914     for (unsigned int i = 0; i < chunks.size(); i++) {
915
916         _serial_prot chunk;
917
918         // chunk.name = chunks[i]->getStringValue("name");
919         chunk.format = unescape(chunks[i]->getStringValue("format", "%d"));
920         chunk.offset = chunks[i]->getDoubleValue("offset");
921         chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
922         chunk.min = chunks[i]->getDoubleValue("min");
923         chunk.max = chunks[i]->getDoubleValue("max");
924         chunk.wrap = chunks[i]->getBoolValue("wrap");
925         chunk.rel = chunks[i]->getBoolValue("relative");
926
927         if( chunks[i]->hasChild("const") ) {
928             chunk.prop = new SGPropertyNode();
929             chunk.prop->setStringValue( chunks[i]->getStringValue("const", "" ) );
930         } else {
931             string node = chunks[i]->getStringValue("node", "/null");
932             chunk.prop = fgGetNode(node.c_str(), true);
933         }
934
935         string type = chunks[i]->getStringValue("type");
936
937         // Note: officially the type is called 'bool' but for backward
938         //       compatibility 'boolean' will also be supported.
939         if (type == "bool" || type == "boolean") {
940             chunk.type = FG_BOOL;
941             record_length += 1;
942         } else if (type == "float") {
943             chunk.type = FG_FLOAT;
944             record_length += sizeof(int32_t);
945         } else if (type == "double") {
946             chunk.type = FG_DOUBLE;
947             record_length += sizeof(int64_t);
948         } else if (type == "fixed") {
949             chunk.type = FG_FIXED;
950             record_length += sizeof(int32_t);
951         } else if (type == "string") {
952             chunk.type = FG_STRING;
953         } else if (type == "byte") {
954             chunk.type = FG_BYTE;
955             record_length += sizeof(int8_t);
956         } else if (type == "word") {
957             chunk.type = FG_WORD;
958             record_length += sizeof(int16_t);
959         } else {
960             chunk.type = FG_INT;
961             record_length += sizeof(int32_t);
962         }
963         msg.push_back(chunk);
964
965     }
966
967     if( !binary_mode )
968     {
969         if ((chunks.size() > 1)&&(var_sep_string.length() == 0))
970         {
971             // ASCII protocols really need a separator when there is more than one chunk per line
972             SG_LOG(SG_IO, SG_ALERT,
973                    "generic protocol: Invalid configuration. "
974                    "'var_separator' must not be empty for protocols which have more than one chunk per line.");
975             return false;
976         }
977     }
978     else
979     {
980         if (binary_record_length == -1) {
981             binary_record_length = record_length;
982         } else if (binary_record_length < record_length) {
983             SG_LOG(SG_IO, SG_ALERT,
984                    "generic protocol: Requested binary record length shorter than "
985                    " requested record representation.");
986             binary_record_length = record_length;
987         }
988     }
989
990     return true;
991 }
992
993 void FGGeneric::updateValue(FGGeneric::_serial_prot& prot, bool val)
994 {
995   if( prot.rel )
996   {
997     // value inverted if received true, otherwise leave unchanged
998     if( val )
999       setValue(prot.prop, !getValue<bool>(prot.prop));
1000   }
1001   else
1002   {
1003     setValue(prot.prop, val);
1004   }
1005 }