]> git.mxchange.org Git - flightgear.git/blob - src/Network/props.cxx
Compute teh altitude-agl if the remote end isn't sending it.
[flightgear.git] / src / Network / props.cxx
1 // \file props.cxx
2 // Property server class.
3 //
4 // Written by Curtis Olson, started September 2000.
5 // Modified by Bernie Bright, May 2002.
6 //
7 // Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 //
23 // $Id$
24
25
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29
30 #include <simgear/compiler.h>
31 #include <simgear/debug/logstream.hxx>
32 #include <simgear/misc/commands.hxx>
33 #include <simgear/misc/strutils.hxx>
34 #include <simgear/props/props.hxx>
35 #include <simgear/props/props_io.hxx>
36
37 #include STL_STRSTREAM
38
39 #include <Main/globals.hxx>
40 #include <Main/viewmgr.hxx>
41
42 #include <plib/netChat.h>
43
44 #include "props.hxx"
45
46 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
47 SG_USING_STD(strstream);
48 SG_USING_STD(ends);
49 #endif
50
51 /**
52  * Props connection class.
53  * This class represents a connection to props client.
54  */
55 class PropsChannel : public netChat
56 {
57     netBuffer buffer;
58
59     /**
60      * Current property node name.
61      */
62     string path;
63
64     enum Mode {
65         PROMPT,
66         DATA
67     };
68     Mode mode;
69
70 public:
71     /**
72      * Constructor.
73      */
74     PropsChannel();
75     
76     /**
77      * Append incoming data to our request buffer.
78      *
79      * @param s Character string to append to buffer
80      * @param n Number of characters to append.
81      */
82     void collectIncomingData( const char* s, int n );
83
84     /**
85      * Process a complete request from the props client.
86      */
87     void foundTerminator();
88
89 private:
90     /**
91      * Return a "Node no found" error message to the client.
92      */
93     void node_not_found_error( const string& node_name );
94 };
95
96 /**
97  * 
98  */
99 PropsChannel::PropsChannel()
100     : buffer(512),
101       path("/"),
102       mode(PROMPT)
103 {
104     setTerminator( "\r\n" );
105 }
106
107 /**
108  * 
109  */
110 void
111 PropsChannel::collectIncomingData( const char* s, int n )
112 {
113     buffer.append( s, n );
114 }
115
116 /**
117  * 
118  */
119 void
120 PropsChannel::node_not_found_error( const string& node_name )
121 {
122     string error = "-ERR Node \"";
123     error += node_name;
124     error += "\" not found.";
125     push( error.c_str() );
126     push( getTerminator() );
127 }
128
129 // return a human readable form of the value "type"
130 static string
131 getValueTypeString( const SGPropertyNode *node )
132 {
133     string result;
134
135     if ( node == NULL )
136     {
137         return "unknown";
138     }
139
140     SGPropertyNode::Type type = node->getType();
141     if ( type == SGPropertyNode::UNSPECIFIED ) {
142         result = "unspecified";
143     } else if ( type == SGPropertyNode::NONE ) {
144         result = "none";
145     } else if ( type == SGPropertyNode::BOOL ) {
146         result = "bool";
147     } else if ( type == SGPropertyNode::INT ) {
148         result = "int";
149     } else if ( type == SGPropertyNode::LONG ) {
150         result = "long";
151     } else if ( type == SGPropertyNode::FLOAT ) {
152         result = "float";
153     } else if ( type == SGPropertyNode::DOUBLE ) {
154         result = "double";
155     } else if ( type == SGPropertyNode::STRING ) {
156         result = "string";
157     }
158
159     return result;
160 }
161
162 /**
163  * We have a command.
164  * 
165  */
166 void
167 PropsChannel::foundTerminator()
168 {
169     const char* cmd = buffer.getData();
170     SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
171
172     vector<string> tokens = simgear::strutils::split( cmd );
173
174     SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
175
176     if (!tokens.empty()) {
177         string command = tokens[0];
178
179         if (command == "ls") {
180             SGPropertyNode* dir = node;
181             if (tokens.size() == 2) {
182                 if (tokens[1][0] == '/') {
183                     dir = globals->get_props()->getNode( tokens[1].c_str() );
184                 } else {
185                     string s = path;
186                     s += "/";
187                     s += tokens[1];
188                     dir = globals->get_props()->getNode( s.c_str() );
189                 }
190
191                 if (dir == 0) {
192                     node_not_found_error( tokens[1] );
193                     goto prompt;
194                 }
195             }
196
197             for (int i = 0; i < dir->nChildren(); i++) {
198                 SGPropertyNode * child = dir->getChild(i);
199                 string line = child->getDisplayName(true);
200
201                 if ( child->nChildren() > 0 ) {
202                     line += "/";
203                 } else {
204                     if (mode == PROMPT) {
205                         string value = child->getStringValue();
206                         line += " =\t'" + value + "'\t(";
207                         line += getValueTypeString( child );
208                         line += ")";
209                     }
210                 }
211
212                 line += getTerminator();
213                 push( line.c_str() );
214             }
215         } else if ( command == "dump" ) {
216             strstream buf;
217             if ( tokens.size() <= 1 ) {
218                 writeProperties( buf, node );
219                 buf << ends; // null terminate the string
220                 push( buf.str() );
221                 push( getTerminator() );
222             } else {
223                 SGPropertyNode *child = node->getNode( tokens[1].c_str() );
224                 if ( child ) {
225                     writeProperties ( buf, child );
226                     buf << ends; // null terminate the string
227                     push( buf.str() );
228                     push( getTerminator() );
229                 } else {
230                     node_not_found_error( tokens[1] );
231                 }
232             }
233         }
234         else if ( command == "cd" ) {
235             if (tokens.size() == 2) {
236                 try {
237                     SGPropertyNode* child = node->getNode( tokens[1].c_str() );
238                     if ( child ) {
239                         node = child;
240                         path = node->getPath();
241                     } else {
242                         node_not_found_error( tokens[1] );
243                     }
244                 } catch (...) {
245                     // Ignore attempt to move past root node with ".."
246                 }
247             }
248         } else if ( command == "pwd" ) {
249             string ttt = node->getPath();
250             if (ttt.empty()) {
251                 ttt = "/";
252             }
253
254             push( ttt.c_str() );
255             push( getTerminator() );
256         } else if ( command == "get" || command == "show" ) {
257             if ( tokens.size() == 2 ) {
258                 string tmp;     
259                 string value = node->getStringValue ( tokens[1].c_str(), "" );
260                 if ( mode == PROMPT ) {
261                     tmp = tokens[1];
262                     tmp += " = '";
263                     tmp += value;
264                     tmp += "' (";
265                     tmp += getValueTypeString(
266                                      node->getNode( tokens[1].c_str() ) );
267                     tmp += ")";
268                 } else {
269                     tmp = value;
270                 }
271                 push( tmp.c_str() );
272                 push( getTerminator() );
273             }
274         } else if ( command == "set" ) {
275             if ( tokens.size() >= 2 ) {
276                 string value, tmp;
277                 if ( tokens.size() == 3 ) {
278                     value = tokens[2];
279                 } else {
280                     value = "";
281                 }
282                 node->getNode( tokens[1].c_str(), true )
283                     ->setStringValue(value.c_str());
284
285                 if ( mode == PROMPT ) {
286                     // now fetch and write out the new value as confirmation
287                     // of the change
288                     value = node->getStringValue ( tokens[1].c_str(), "" );
289                     tmp = tokens[1] + " = '" + value + "' (";
290                     tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
291                     tmp += ")";
292                     push( tmp.c_str() );
293                     push( getTerminator() );
294                 }
295             } 
296         } else if ( command == "reinit" ) {
297             if ( tokens.size() == 2 ) {
298                 string tmp;     
299                 SGPropertyNode args;
300                 for ( unsigned int i = 1; i < tokens.size(); ++i ) {
301                     cout << "props: adding subsystem = " << tokens[i] << endl;
302                     SGPropertyNode *node = args.getNode("subsystem", i-1, true);
303                     node->setStringValue( tokens[i].c_str() );
304                 }
305                 if ( !globals->get_commands()
306                          ->execute( "reinit", &args) )
307                 {
308                     SG_LOG( SG_GENERAL, SG_ALERT,
309                             "Command " << tokens[1] << " failed.");
310                     if ( mode == PROMPT ) {
311                         tmp += "*failed*";
312                         push( tmp.c_str() );
313                         push( getTerminator() );
314                     }
315                 } else {
316                     if ( mode == PROMPT ) {
317                         tmp += "<completed>";
318                         push( tmp.c_str() );
319                         push( getTerminator() );
320                     }
321                 }
322             }
323         } else if ( command == "run" ) {
324             string tmp; 
325             if ( tokens.size() >= 2 ) {
326                 SGPropertyNode args;
327                 if ( tokens[1] == "reinit" ) {
328                     for ( unsigned int i = 2; i < tokens.size(); ++i ) {
329                         cout << "props: adding subsystem = " << tokens[i]
330                              << endl;
331                         SGPropertyNode *node
332                             = args.getNode("subsystem", i-2, true);
333                         node->setStringValue( tokens[i].c_str() );
334                     }
335                 }
336                 if ( !globals->get_commands()
337                          ->execute(tokens[1].c_str(), &args) )
338                 {
339                     SG_LOG( SG_GENERAL, SG_ALERT,
340                             "Command " << tokens[1] << " failed.");
341                     if ( mode == PROMPT ) {
342                         tmp += "*failed*";
343                         push( tmp.c_str() );
344                         push( getTerminator() );
345                     }
346                 } else {
347                     if ( mode == PROMPT ) {
348                         tmp += "<completed>";
349                         push( tmp.c_str() );
350                         push( getTerminator() );
351                     }
352                 }
353             } else {
354                 if ( mode == PROMPT ) {
355                     tmp += "no command specified";
356                     push( tmp.c_str() );
357                     push( getTerminator() );
358                 }
359             }
360         } else if (command == "quit") {
361             close();
362             shouldDelete();
363             return;
364         } else if ( command == "data" ) {
365             mode = DATA;
366         } else if ( command == "prompt" ) {
367             mode = PROMPT;
368         } else {
369             const char* msg = "\
370 Valid commands are:\r\n\
371 \r\n\
372 cd <dir>           cd to a directory, '..' to move back\r\n\
373 data               switch to raw data mode\r\n\
374 dump               dump current state (in xml)\r\n\
375 get <var>          show the value of a parameter\r\n\
376 help               show this help message\r\n\
377 ls [<dir>]         list directory\r\n\
378 prompt             switch to interactive mode (default)\r\n\
379 pwd                display your current path\r\n\
380 quit               terminate connection\r\n\
381 run <command>      run built in command\r\n\
382 set <var> <val>    set <var> to a new <val>\r\n\
383 show <var>         synonym for get\r\n";
384             push( msg );
385         }
386     }
387
388  prompt:
389     if (mode == PROMPT) {
390         string prompt = node->getPath();
391         if (prompt.empty()) {
392             prompt = "/";
393         }
394         prompt += "> ";
395         push( prompt.c_str() );
396     }
397
398     buffer.remove();
399 }
400
401 /**
402  * 
403  */
404 FGProps::FGProps( const vector<string>& tokens )
405 {
406     // tokens:
407     //   props,port#
408     //   props,medium,direction,hz,hostname,port#,style
409     if (tokens.size() == 2) {
410         port = atoi( tokens[1].c_str() );
411         set_hz( 5 );                // default to processing requests @ 5Hz
412     } else if (tokens.size() == 7) {
413         char* endptr;
414         errno = 0;
415         int hz = strtol( tokens[3].c_str(), &endptr, 10 );
416         if (errno != 0) {
417            SG_LOG( SG_IO, SG_ALERT, "I/O poll frequency out of range" );
418            set_hz( 5 );           // default to processing requests @ 5Hz
419         } else {
420             SG_LOG( SG_IO, SG_INFO, "Setting I/O poll frequency to "
421                     << hz << " Hz");
422             set_hz( hz );
423         }
424         port = atoi( tokens[5].c_str() );
425     } else {
426         throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
427     }
428 }
429
430 /**
431  * 
432  */
433 FGProps::~FGProps()
434 {
435 }
436
437 /**
438  * 
439  */
440 bool
441 FGProps::open()
442 {
443     if ( is_enabled() )
444     {
445         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
446                 << "is already in use, ignoring" );
447         return false;
448     }
449
450     netChannel::open();
451     netChannel::bind( "", port );
452     netChannel::listen( 5 );
453     SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
454
455     set_enabled( true );
456     return true;
457 }
458
459 /**
460  * 
461  */
462 bool
463 FGProps::close()
464 {
465     return true;
466 }
467
468 /**
469  * 
470  */
471 bool
472 FGProps::process()
473 {
474     netChannel::poll();
475     return true;
476 }
477
478 /**
479  * 
480  */
481 void
482 FGProps::handleAccept()
483 {
484     netAddress addr;
485     int handle = accept( &addr );
486     SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
487             << addr.getHost() << ":" << addr.getPort() );
488     PropsChannel* channel = new PropsChannel();
489     channel->setHandle( handle );
490 }