]> git.mxchange.org Git - flightgear.git/blob - src/Network/props.cxx
Merge branch 'work4' into next
[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 - http://www.flightgear.org/~curt
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 <sstream>
38 #include <iostream>
39
40 #include <Main/globals.hxx>
41 #include <Main/viewmgr.hxx>
42
43 #include <plib/netChat.h>
44
45 #include "props.hxx"
46
47 using std::stringstream;
48 using std::ends;
49
50 using std::cout;
51 using std::endl;
52
53 /**
54  * Props connection class.
55  * This class represents a connection to props client.
56  */
57 class PropsChannel : public netChat
58 {
59     netBuffer buffer;
60
61     /**
62      * Current property node name.
63      */
64     string path;
65
66     enum Mode {
67         PROMPT,
68         DATA
69     };
70     Mode mode;
71
72 public:
73     /**
74      * Constructor.
75      */
76     PropsChannel();
77
78     /**
79      * Append incoming data to our request buffer.
80      *
81      * @param s Character string to append to buffer
82      * @param n Number of characters to append.
83      */
84     void collectIncomingData( const char* s, int n );
85
86     /**
87      * Process a complete request from the props client.
88      */
89     void foundTerminator();
90
91 private:
92     inline void node_not_found_error( const string& s ) const {
93         throw "node '" + s + "' not found";
94     }
95 };
96
97 /**
98  *
99  */
100 PropsChannel::PropsChannel()
101     : buffer(512),
102       path("/"),
103       mode(PROMPT)
104 {
105     setTerminator( "\r\n" );
106 }
107
108 /**
109  *
110  */
111 void
112 PropsChannel::collectIncomingData( const char* s, int n )
113 {
114     buffer.append( s, n );
115 }
116
117 // return a human readable form of the value "type"
118 static string
119 getValueTypeString( const SGPropertyNode *node )
120 {
121     using namespace simgear;
122
123     string result;
124
125     if ( node == NULL )
126     {
127         return "unknown";
128     }
129
130     props::Type type = node->getType();
131     if ( type == props::UNSPECIFIED ) {
132         result = "unspecified";
133     } else if ( type == props::NONE ) {
134         result = "none";
135     } else if ( type == props::BOOL ) {
136         result = "bool";
137     } else if ( type == props::INT ) {
138         result = "int";
139     } else if ( type == props::LONG ) {
140         result = "long";
141     } else if ( type == props::FLOAT ) {
142         result = "float";
143     } else if ( type == props::DOUBLE ) {
144         result = "double";
145     } else if ( type == props::STRING ) {
146         result = "string";
147     }
148
149     return result;
150 }
151
152 /**
153  * We have a command.
154  *
155  */
156 void
157 PropsChannel::foundTerminator()
158 {
159     const char* cmd = buffer.getData();
160     SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
161
162     vector<string> tokens = simgear::strutils::split( cmd );
163
164     SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
165
166     try {
167         if (!tokens.empty()) {
168             string command = tokens[0];
169
170             if (command == "ls") {
171                 SGPropertyNode* dir = node;
172                 if (tokens.size() == 2) {
173                     if (tokens[1][0] == '/') {
174                         dir = globals->get_props()->getNode( tokens[1].c_str() );
175                     } else {
176                         string s = path;
177                         s += "/";
178                         s += tokens[1];
179                         dir = globals->get_props()->getNode( s.c_str() );
180                     }
181
182                     if (dir == 0) {
183                         node_not_found_error( tokens[1] );
184                     }
185                 }
186
187                 for (int i = 0; i < dir->nChildren(); i++) {
188                     SGPropertyNode * child = dir->getChild(i);
189                     string line = child->getDisplayName(true);
190
191                     if ( child->nChildren() > 0 ) {
192                         line += "/";
193                     } else {
194                         if (mode == PROMPT) {
195                             string value = child->getStringValue();
196                             line += " =\t'" + value + "'\t(";
197                             line += getValueTypeString( child );
198                             line += ")";
199                         }
200                     }
201
202                     line += getTerminator();
203                     push( line.c_str() );
204                 }
205             } else if ( command == "dump" ) {
206                 stringstream buf;
207                 if ( tokens.size() <= 1 ) {
208                     writeProperties( buf, node );
209                     buf << ends; // null terminate the string
210                     push( buf.str().c_str() );
211                     push( getTerminator() );
212                 } else {
213                     SGPropertyNode *child = node->getNode( tokens[1].c_str() );
214                     if ( child ) {
215                         writeProperties ( buf, child );
216                         buf << ends; // null terminate the string
217                         push( buf.str().c_str() );
218                         push( getTerminator() );
219                     } else {
220                         node_not_found_error( tokens[1] );
221                     }
222                 }
223             }
224             else if ( command == "cd" ) {
225                 if (tokens.size() == 2) {
226                     SGPropertyNode* child = node->getNode( tokens[1].c_str() );
227                     if ( child ) {
228                         node = child;
229                         path = node->getPath();
230                     } else {
231                         node_not_found_error( tokens[1] );
232                     }
233                 }
234             } else if ( command == "pwd" ) {
235                 string pwd = node->getPath();
236                 if (pwd.empty()) {
237                     pwd = "/";
238                 }
239
240                 push( pwd.c_str() );
241                 push( getTerminator() );
242             } else if ( command == "get" || command == "show" ) {
243                 if ( tokens.size() == 2 ) {
244                     string tmp;
245                     string value = node->getStringValue ( tokens[1].c_str(), "" );
246                     if ( mode == PROMPT ) {
247                         tmp = tokens[1];
248                         tmp += " = '";
249                         tmp += value;
250                         tmp += "' (";
251                         tmp += getValueTypeString(
252                                          node->getNode( tokens[1].c_str() ) );
253                         tmp += ")";
254                     } else {
255                         tmp = value;
256                     }
257                     push( tmp.c_str() );
258                     push( getTerminator() );
259                 }
260             } else if ( command == "set" ) {
261                 if ( tokens.size() >= 2 ) {
262                     string value, tmp;
263                     for (unsigned int i = 2; i < tokens.size(); i++) {
264                         if (i > 2)
265                             value += " ";
266                         value += tokens[i];
267                     }
268                     node->getNode( tokens[1].c_str(), true )
269                         ->setStringValue(value.c_str());
270
271                     if ( mode == PROMPT ) {
272                         // now fetch and write out the new value as confirmation
273                         // of the change
274                         value = node->getStringValue ( tokens[1].c_str(), "" );
275                         tmp = tokens[1] + " = '" + value + "' (";
276                         tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
277                         tmp += ")";
278                         push( tmp.c_str() );
279                         push( getTerminator() );
280                     }
281                 }
282             } else if ( command == "reinit" ) {
283                 if ( tokens.size() == 2 ) {
284                     string tmp;
285                     SGPropertyNode args;
286                     for ( unsigned int i = 1; i < tokens.size(); ++i ) {
287                         cout << "props: adding subsystem = " << tokens[i] << endl;
288                         SGPropertyNode *node = args.getNode("subsystem", i-1, true);
289                         node->setStringValue( tokens[i].c_str() );
290                     }
291                     if ( !globals->get_commands()
292                              ->execute( "reinit", &args) )
293                     {
294                         SG_LOG( SG_GENERAL, SG_ALERT,
295                                 "Command " << tokens[1] << " failed.");
296                         if ( mode == PROMPT ) {
297                             tmp += "*failed*";
298                             push( tmp.c_str() );
299                             push( getTerminator() );
300                         }
301                     } else {
302                         if ( mode == PROMPT ) {
303                             tmp += "<completed>";
304                             push( tmp.c_str() );
305                             push( getTerminator() );
306                         }
307                     }
308                 }
309             } else if ( command == "run" ) {
310                 string tmp;
311                 if ( tokens.size() >= 2 ) {
312                     SGPropertyNode args;
313                     if ( tokens[1] == "reinit" ) {
314                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
315                             cout << "props: adding subsystem = " << tokens[i]
316                                  << endl;
317                             SGPropertyNode *node
318                                 = args.getNode("subsystem", i-2, true);
319                             node->setStringValue( tokens[i].c_str() );
320                         }
321                     } else if ( tokens[1] == "set-sea-level-air-temp-degc" ) {
322                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
323                             cout << "props: set-sl command = " << tokens[i]
324                                  << endl;
325                             SGPropertyNode *node
326                                 = args.getNode("temp-degc", i-2, true);
327                             node->setStringValue( tokens[i].c_str() );
328                         }
329                     } else if ( tokens[1] == "set-outside-air-temp-degc" ) {
330                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
331                             cout << "props: set-oat command = " << tokens[i]
332                                  << endl;
333                             SGPropertyNode *node
334                                 = args.getNode("temp-degc", i-2, true);
335                             node->setStringValue( tokens[i].c_str() );
336                         }
337                     } else if ( tokens[1] == "timeofday" ) {
338                         for ( unsigned int i = 2; i < tokens.size(); ++i ) {
339                             cout << "props: time of day command = " << tokens[i]
340                                  << endl;
341                             SGPropertyNode *node
342                                 = args.getNode("timeofday", i-2, true);
343                             node->setStringValue( tokens[i].c_str() );
344                         }
345                     } else if ( tokens[1] == "play-audio-message" ) {
346                         if ( tokens.size() == 4 ) {
347                             cout << "props: play audio message = " << tokens[2]
348                                  << " " << tokens[3] << endl;
349                             SGPropertyNode *node;
350                             node = args.getNode("path", 0, true);
351                             node->setStringValue( tokens[2].c_str() );
352                             node = args.getNode("file", 0, true);
353                             node->setStringValue( tokens[3].c_str() );
354                        }
355                     }
356                     if ( !globals->get_commands()
357                              ->execute(tokens[1].c_str(), &args) )
358                     {
359                         SG_LOG( SG_GENERAL, SG_ALERT,
360                                 "Command " << tokens[1] << " failed.");
361                         if ( mode == PROMPT ) {
362                             tmp += "*failed*";
363                             push( tmp.c_str() );
364                             push( getTerminator() );
365                         }
366                     } else {
367                         if ( mode == PROMPT ) {
368                             tmp += "<completed>";
369                             push( tmp.c_str() );
370                             push( getTerminator() );
371                         }
372                     }
373                 } else {
374                     if ( mode == PROMPT ) {
375                         tmp += "no command specified";
376                         push( tmp.c_str() );
377                         push( getTerminator() );
378                     }
379                 }
380             } else if ( command == "quit" || command == "exit" ) {
381                 close();
382                 shouldDelete();
383                 return;
384             } else if ( command == "data" ) {
385                 mode = DATA;
386             } else if ( command == "prompt" ) {
387                 mode = PROMPT;
388             } else {
389                 const char* msg = "\
390 Valid commands are:\r\n\
391 \r\n\
392 cd <dir>           cd to a directory, '..' to move back\r\n\
393 data               switch to raw data mode\r\n\
394 dump               dump current state (in xml)\r\n\
395 get <var>          show the value of a parameter\r\n\
396 help               show this help message\r\n\
397 ls [<dir>]         list directory\r\n\
398 prompt             switch to interactive mode (default)\r\n\
399 pwd                display your current path\r\n\
400 quit               terminate connection\r\n\
401 run <command>      run built in command\r\n\
402 set <var> <val>    set <var> to a new <val>\r\n";
403                 push( msg );
404             }
405         }
406
407     } catch ( const string& msg ) {
408         string error = "-ERR \"" + msg + "\"";
409         push( error.c_str() );
410         push( getTerminator() );
411     }
412
413     if ( mode == PROMPT ) {
414         string prompt = node->getPath();
415         if (prompt.empty()) {
416             prompt = "/";
417         }
418         prompt += "> ";
419         push( prompt.c_str() );
420     }
421
422     buffer.remove();
423 }
424
425 /**
426  *
427  */
428 FGProps::FGProps( const vector<string>& tokens )
429 {
430     // tokens:
431     //   props,port#
432     //   props,medium,direction,hz,hostname,port#,style
433     if (tokens.size() == 2) {
434         port = atoi( tokens[1].c_str() );
435         set_hz( 5 );                // default to processing requests @ 5Hz
436     } else if (tokens.size() == 7) {
437         char* endptr;
438         errno = 0;
439         int hz = strtol( tokens[3].c_str(), &endptr, 10 );
440         if (errno != 0) {
441            SG_LOG( SG_IO, SG_ALERT, "I/O poll frequency out of range" );
442            set_hz( 5 );           // default to processing requests @ 5Hz
443         } else {
444             SG_LOG( SG_IO, SG_INFO, "Setting I/O poll frequency to "
445                     << hz << " Hz");
446             set_hz( hz );
447         }
448         port = atoi( tokens[5].c_str() );
449     } else {
450         throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
451     }
452 }
453
454 /**
455  *
456  */
457 FGProps::~FGProps()
458 {
459 }
460
461 /**
462  *
463  */
464 bool
465 FGProps::open()
466 {
467     if ( is_enabled() )
468     {
469         SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
470                 << "is already in use, ignoring" );
471         return false;
472     }
473
474     netChannel::open();
475     netChannel::bind( "", port );
476     netChannel::listen( 5 );
477     SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
478
479     set_enabled( true );
480     return true;
481 }
482
483 /**
484  *
485  */
486 bool
487 FGProps::close()
488 {
489     SG_LOG( SG_IO, SG_INFO, "closing FGProps" );
490     return true;
491 }
492
493 /**
494  *
495  */
496 bool
497 FGProps::process()
498 {
499     netChannel::poll();
500     return true;
501 }
502
503 /**
504  *
505  */
506 void
507 FGProps::handleAccept()
508 {
509     netAddress addr;
510     int handle = accept( &addr );
511     SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
512             << addr.getHost() << ":" << addr.getPort() );
513     PropsChannel* channel = new PropsChannel();
514     channel->setHandle( handle );
515 }