]> git.mxchange.org Git - flightgear.git/blob - src/Network/props.cxx
Enhancements to AI plane transmissions
[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/structure/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                 } else if ( tokens[1] == "timeofday" ) {
336                     for ( unsigned int i = 2; i < tokens.size(); ++i ) {
337                         cout << "props: adding subsystem = " << tokens[i]
338                              << endl;
339                         SGPropertyNode *node
340                             = args.getNode("timeofday", i-2, true);
341                         node->setStringValue( tokens[i].c_str() );
342                     }
343                 }
344                 if ( !globals->get_commands()
345                          ->execute(tokens[1].c_str(), &args) )
346                 {
347                     SG_LOG( SG_GENERAL, SG_ALERT,
348                             "Command " << tokens[1] << " failed.");
349                     if ( mode == PROMPT ) {
350                         tmp += "*failed*";
351                         push( tmp.c_str() );
352                         push( getTerminator() );
353                     }
354                 } else {
355                     if ( mode == PROMPT ) {
356                         tmp += "<completed>";
357                         push( tmp.c_str() );
358                         push( getTerminator() );
359                     }
360                 }
361             } else {
362                 if ( mode == PROMPT ) {
363                     tmp += "no command specified";
364                     push( tmp.c_str() );
365                     push( getTerminator() );
366                 }
367             }
368         } else if (command == "quit") {
369             close();
370             shouldDelete();
371             return;
372         } else if ( command == "data" ) {
373             mode = DATA;
374         } else if ( command == "prompt" ) {
375             mode = PROMPT;
376         } else {
377             const char* msg = "\
378 Valid commands are:\r\n\
379 \r\n\
380 cd <dir>           cd to a directory, '..' to move back\r\n\
381 data               switch to raw data mode\r\n\
382 dump               dump current state (in xml)\r\n\
383 get <var>          show the value of a parameter\r\n\
384 help               show this help message\r\n\
385 ls [<dir>]         list directory\r\n\
386 prompt             switch to interactive mode (default)\r\n\
387 pwd                display your current path\r\n\
388 quit               terminate connection\r\n\
389 run <command>      run built in command\r\n\
390 set <var> <val>    set <var> to a new <val>\r\n\
391 show <var>         synonym for get\r\n";
392             push( msg );
393         }
394     }
395
396  prompt:
397     if (mode == PROMPT) {
398         string prompt = node->getPath();
399         if (prompt.empty()) {
400             prompt = "/";
401         }
402         prompt += "> ";
403         push( prompt.c_str() );
404     }
405
406     buffer.remove();
407 }
408
409 /**
410  * 
411  */
412 FGProps::FGProps( const vector<string>& tokens )
413 {
414     // tokens:
415     //   props,port#
416     //   props,medium,direction,hz,hostname,port#,style
417     if (tokens.size() == 2) {
418         port = atoi( tokens[1].c_str() );
419         set_hz( 5 );                // default to processing requests @ 5Hz
420     } else if (tokens.size() == 7) {
421         char* endptr;
422         errno = 0;
423         int hz = strtol( tokens[3].c_str(), &endptr, 10 );
424         if (errno != 0) {
425            SG_LOG( SG_IO, SG_ALERT, "I/O poll frequency out of range" );
426            set_hz( 5 );           // default to processing requests @ 5Hz
427         } else {
428             SG_LOG( SG_IO, SG_INFO, "Setting I/O poll frequency to "
429                     << hz << " Hz");
430             set_hz( hz );
431         }
432         port = atoi( tokens[5].c_str() );
433     } else {
434         throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
435     }
436 }
437
438 /**
439  * 
440  */
441 FGProps::~FGProps()
442 {
443 }
444
445 /**
446  * 
447  */
448 bool
449 FGProps::open()
450 {
451     if ( is_enabled() )
452     {
453         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
454                 << "is already in use, ignoring" );
455         return false;
456     }
457
458     netChannel::open();
459     netChannel::bind( "", port );
460     netChannel::listen( 5 );
461     SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
462
463     set_enabled( true );
464     return true;
465 }
466
467 /**
468  * 
469  */
470 bool
471 FGProps::close()
472 {
473     return true;
474 }
475
476 /**
477  * 
478  */
479 bool
480 FGProps::process()
481 {
482     netChannel::poll();
483     return true;
484 }
485
486 /**
487  * 
488  */
489 void
490 FGProps::handleAccept()
491 {
492     netAddress addr;
493     int handle = accept( &addr );
494     SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
495             << addr.getHost() << ":" << addr.getPort() );
496     PropsChannel* channel = new PropsChannel();
497     channel->setHandle( handle );
498 }