]> git.mxchange.org Git - flightgear.git/blob - src/Network/generic.cxx
Sync. w. JSBSim CVS
[flightgear.git] / src / Network / generic.cxx
1 // generic.cxx -- generic protocal 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
30 #include <simgear/debug/logstream.hxx>
31 #include <simgear/io/iochannel.hxx>
32 #include <simgear/structure/exception.hxx>
33 #include <simgear/misc/sg_path.hxx>
34 #include <simgear/misc/stdint.hxx>
35 #include <simgear/props/props.hxx>
36 #include <simgear/props/props_io.hxx>
37
38 #include <Main/globals.hxx>
39 #include <Main/fg_props.hxx>
40 #include <Main/util.hxx>
41
42 #include "generic.hxx"
43
44
45
46 FGGeneric::FGGeneric(string& config) : exitOnError(false)
47 {
48
49     string file = config+".xml";
50
51     SGPath path( globals->get_fg_root() );
52     path.append("Protocol");
53     path.append(file.c_str());
54     SG_LOG(SG_GENERAL, SG_INFO, "Reading communication protocol from "
55                                 << path.str());
56
57     SGPropertyNode root;
58     try {
59         readProperties(path.str(), &root);
60      } catch (const sg_exception &e) {
61         SG_LOG(SG_GENERAL, SG_ALERT,
62          "Unable to load the protocol configuration file");
63          return;
64     }
65
66     SGPropertyNode *output = root.getNode("generic/output");
67     if (output)
68         read_config(output, _out_message);
69
70     SGPropertyNode *input = root.getNode("generic/input");
71     if (input)
72         read_config(input, _in_message);
73 }
74
75 FGGeneric::~FGGeneric() {
76 }
77
78
79 // generate the message
80 bool FGGeneric::gen_message() {
81     string generic_sentence;
82     char tmp[255];
83     length = 0;
84
85     double val;
86
87     for (unsigned int i = 0; i < _out_message.size(); i++) {
88
89         if (i > 0 && !binary_mode)
90             generic_sentence += var_separator;
91
92         switch (_out_message[i].type) {
93         case FG_INT:
94             val = _out_message[i].offset +
95                   _out_message[i].prop->getIntValue() * _out_message[i].factor;
96             if (binary_mode) {
97                 *((int32_t*)&buf[length]) = (int32_t)val;
98                 length += sizeof(int32_t);
99             } else {
100                 snprintf(tmp, 255, _out_message[i].format.c_str(), (int)val);
101             }
102             break;
103
104         case FG_BOOL:
105             if (binary_mode) {
106                 *((int8_t*)&buf[length])
107                           = _out_message[i].prop->getBoolValue() ? true : false;
108                 length += sizeof(int8_t);
109             } else {
110                 snprintf(tmp, 255, _out_message[i].format.c_str(),
111                                    _out_message[i].prop->getBoolValue());
112             }
113             break;
114
115         case FG_DOUBLE:
116             val = _out_message[i].offset +
117                 _out_message[i].prop->getFloatValue() * _out_message[i].factor;
118             if (binary_mode) {
119                 *((double*)&buf[length]) = val;
120                 length += sizeof(double);
121             } else {
122                 snprintf(tmp, 255, _out_message[i].format.c_str(), (float)val);
123             }
124             break;
125
126         default: // SG_STRING
127             if (binary_mode) {
128                 const char *strdata = _out_message[i].prop->getStringValue();
129                 int strlength = strlen(strdata);
130
131                 /* Format for strings is 
132                  * [length as int, 4 bytes][ASCII data, length bytes]
133                  */
134                 *((int32_t*)&buf[length]) = strlength;
135                                 length += sizeof(int32_t);
136                                 strncpy(&buf[length], strdata, strlength);
137                                 length += strlength; 
138                 /* FIXME padding for alignment? Something like: 
139                  * length += (strlength % 4 > 0 ? sizeof(int32_t) - strlength % 4 : 0;
140                  */
141             } else {
142                 snprintf(tmp, 255, _out_message[i].format.c_str(),
143                                    _out_message[i].prop->getStringValue());
144             }
145         }
146
147         if (!binary_mode) {
148             generic_sentence += tmp;
149         }
150     }
151
152     if (!binary_mode) {
153         /* After each lot of variables has been added, put the line separator
154          * char/string
155          */
156         generic_sentence += line_separator;
157
158         length =  generic_sentence.length();
159         strncpy( buf, generic_sentence.c_str(), length );
160     } else {
161         // add the footer to the packet ("line")
162         switch (binary_footer_type) {
163             case FOOTER_LENGTH:
164                 binary_footer_value = length;
165                 break;
166
167             case FOOTER_MAGIC:
168                 break;
169         }
170         if (binary_footer_type != FOOTER_NONE) {
171             *((int32_t*)&buf[length]) = binary_footer_value;
172             length += sizeof(int32_t);
173         }
174     }
175
176     return true;
177 }
178
179 bool FGGeneric::parse_message() {
180     char *p2, *p1 = buf;
181     double val;
182     int i = -1;
183
184     if (binary_mode)
185         SG_LOG( SG_IO, SG_ALERT,
186                 "generic protocol: binary mode input is not yet implemented.");
187         
188     while ((++i < (int)_in_message.size()) &&
189             p1 && strcmp(p1, line_separator.c_str())) {
190
191         p2 = strstr(p1, var_separator.c_str());
192         if (p2) {
193             *p2 = 0;
194             p2 += var_separator.length();
195         }
196
197         switch (_in_message[i].type) {
198         case FG_INT:
199             val = _in_message[i].offset + atoi(p1) * _in_message[i].factor;
200             _in_message[i].prop->setIntValue((int)val);
201             break;
202
203         case FG_BOOL:
204             _in_message[i].prop->setBoolValue( atof(p1) != 0.0 );
205             break;
206
207         case FG_DOUBLE:
208             val = _in_message[i].offset + strtod(p1, 0) * _in_message[i].factor;
209             _in_message[i].prop->setFloatValue((float)val);
210             break;
211
212         default: // SG_STRING
213              _in_message[i].prop->setStringValue(p1);
214         }
215
216         p1 = p2;
217     }
218     
219     return true;
220 }
221
222
223
224 // open hailing frequencies
225 bool FGGeneric::open() {
226     if ( is_enabled() ) {
227         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
228                 << "is already in use, ignoring" );
229         return false;
230     }
231
232     SGIOChannel *io = get_io_channel();
233
234     if ( ! io->open( get_direction() ) ) {
235         SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
236         return false;
237     }
238
239     set_enabled( true );
240
241     if ( get_direction() == SG_IO_OUT && ! preamble.empty() ) {
242         if ( ! io->write( preamble.c_str(), preamble.size() ) ) {
243             SG_LOG( SG_IO, SG_WARN, "Error writing preamble." );
244             return false;
245         }
246     }
247
248     return true;
249 }
250
251
252 // process work for this port
253 bool FGGeneric::process() {
254     SGIOChannel *io = get_io_channel();
255
256     if ( get_direction() == SG_IO_OUT ) {
257         gen_message();
258         if ( ! io->write( buf, length ) ) {
259             SG_LOG( SG_IO, SG_WARN, "Error writing data." );
260             goto error_out;
261         }
262     } else if ( get_direction() == SG_IO_IN ) {
263         if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
264             parse_message();
265         } else {
266             SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
267             goto error_out;
268         }
269     }
270     return true;
271 error_out:
272     if (exitOnError)
273         fgExit(1);
274     else
275         return false;
276 }
277
278
279 // close the channel
280 bool FGGeneric::close() {
281     SGIOChannel *io = get_io_channel();
282
283     if ( get_direction() == SG_IO_OUT && ! postamble.empty() ) {
284         if ( ! io->write( postamble.c_str(), postamble.size() ) ) {
285             SG_LOG( SG_IO, SG_ALERT, "Error writing postamble." );
286             return false;
287         }
288     }
289
290     set_enabled( false );
291
292     if ( ! io->close() ) {
293         return false;
294     }
295
296     return true;
297 }
298
299
300 void
301 FGGeneric::read_config(SGPropertyNode *root, vector<_serial_prot> &msg)
302 {
303     binary_mode = root->getBoolValue("binary_mode");
304
305     if (!binary_mode) {
306         /* These variables specified in the $FG_ROOT/data/Protocol/xxx.xml
307          * file for each format
308          *
309          * var_sep_string  = the string/charachter to place between variables
310          * line_sep_string = the string/charachter to place at the end of each
311          *                   lot of variables
312          */
313         preamble = fgUnescape(root->getStringValue("preamble"));
314         postamble = fgUnescape(root->getStringValue("postamble"));
315         var_sep_string = fgUnescape(root->getStringValue("var_separator"));
316         line_sep_string = fgUnescape(root->getStringValue("line_separator"));
317
318         if ( var_sep_string == "newline" )
319                 var_separator = '\n';
320         else if ( var_sep_string == "tab" )
321                 var_separator = '\t';
322         else if ( var_sep_string == "space" )
323                 var_separator = ' ';
324         else if ( var_sep_string == "formfeed" )
325                 var_separator = '\f';
326         else if ( var_sep_string == "carriagereturn" )
327                 var_sep_string = '\r';
328         else if ( var_sep_string == "verticaltab" )
329                 var_separator = '\v';
330         else
331                 var_separator = var_sep_string;
332
333         if ( line_sep_string == "newline" )
334                 line_separator = '\n';
335         else if ( line_sep_string == "tab" )
336                 line_separator = '\t';
337         else if ( line_sep_string == "space" )
338                 line_separator = ' ';
339         else if ( line_sep_string == "formfeed" )
340                 line_separator = '\f';
341         else if ( line_sep_string == "carriagereturn" )
342                 line_separator = '\r';
343         else if ( line_sep_string == "verticaltab" )
344                 line_separator = '\v';
345         else
346                 line_separator = line_sep_string;
347     } else {
348         binary_footer_type = FOOTER_NONE; // default choice
349         if ( root->hasValue("binary_footer") ) {
350             string footer_type = root->getStringValue("binary_footer");
351             if ( footer_type == "length" )
352                 binary_footer_type = FOOTER_LENGTH;
353             else if ( footer_type.substr(0, 5) == "magic" ) {
354                 binary_footer_type = FOOTER_MAGIC;
355                 binary_footer_value = strtol(footer_type.substr(6, 
356                             footer_type.length() - 6).c_str(), (char**)0, 0);
357             } else if ( footer_type != "none" )
358                 SG_LOG(SG_IO, SG_ALERT,
359                        "generic protocol: Undefined generic binary protocol"
360                                            "footer, using no footer.");
361         }
362     }
363
364     vector<SGPropertyNode_ptr> chunks = root->getChildren("chunk");
365     for (unsigned int i = 0; i < chunks.size(); i++) {
366
367         _serial_prot chunk;
368
369         // chunk.name = chunks[i]->getStringValue("name");
370         chunk.format = fgUnescape(chunks[i]->getStringValue("format", "%d"));
371         chunk.offset = chunks[i]->getDoubleValue("offset");
372         chunk.factor = chunks[i]->getDoubleValue("factor", 1.0);
373
374         string node = chunks[i]->getStringValue("node", "/null");
375         chunk.prop = fgGetNode(node.c_str(), true);
376
377         string type = chunks[i]->getStringValue("type");
378         if (type == "bool")
379             chunk.type = FG_BOOL;
380         else if (type == "float")
381             chunk.type = FG_DOUBLE;
382         else if (type == "string")
383             chunk.type = FG_STRING;
384         else
385             chunk.type = FG_INT;
386
387         msg.push_back(chunk);
388
389     }
390
391 }
392