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