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