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