]> git.mxchange.org Git - flightgear.git/blobdiff - src/Network/props.cxx
MIPSpro 7.4 fixes
[flightgear.git] / src / Network / props.cxx
index 25c304fd51221814af1aec9d18b53c6a7fb4a1c3..b0526c8ab254a9ec179b2e5cc3446530105e4916 100644 (file)
@@ -1,6 +1,8 @@
-// props.hxx -- FGFS property manager interaction class
+// \file props.cxx
+// Property server class.
 //
 // Written by Curtis Olson, started September 2000.
+// Modified by Bernie Bright, May 2002.
 //
 // Copyright (C) 2000  Curtis L. Olson - curt@flightgear.org
 //
 // $Id$
 
 
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
 #include <simgear/compiler.h>
 #include <simgear/debug/logstream.hxx>
-#include <simgear/io/iochannel.hxx>
-#include <simgear/math/sg_types.hxx>
+#include <simgear/structure/commands.hxx>
+#include <simgear/misc/strutils.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx>
+
+#include STL_STRSTREAM
+
+#include <Main/globals.hxx>
+#include <Main/viewmgr.hxx>
 
-#include <strstream.h>
+#include <plib/netChat.h>
 
 #include "props.hxx"
 
+SG_USING_STD(strstream);
+SG_USING_STD(ends);
 
-FGProps::FGProps() {
-}
+/**
+ * Props connection class.
+ * This class represents a connection to props client.
+ */
+class PropsChannel : public netChat
+{
+    netBuffer buffer;
 
-FGProps::~FGProps() {
-}
+    /**
+     * Current property node name.
+     */
+    string path;
 
+    enum Mode {
+       PROMPT,
+       DATA
+    };
+    Mode mode;
 
-// open hailing frequencies
-bool FGProps::open() {
-    if ( is_enabled() ) {
-       FG_LOG( FG_IO, FG_ALERT, "This shouldn't happen, but the channel " 
-               << "is already in use, ignoring" );
-       return false;
-    }
+public:
+    /**
+     * Constructor.
+     */
+    PropsChannel();
+    
+    /**
+     * Append incoming data to our request buffer.
+     *
+     * @param s Character string to append to buffer
+     * @param n Number of characters to append.
+     */
+    void collectIncomingData( const char* s, int n );
 
-    SGIOChannel *io = get_io_channel();
+    /**
+     * Process a complete request from the props client.
+     */
+    void foundTerminator();
 
-    if ( ! io->open( get_direction() ) ) {
-       FG_LOG( FG_IO, FG_ALERT, "Error opening channel communication layer." );
-       return false;
-    }
+private:
+    /**
+     * Return a "Node no found" error message to the client.
+     */
+    void node_not_found_error( const string& node_name );
+};
 
-    set_enabled( true );
+/**
+ * 
+ */
+PropsChannel::PropsChannel()
+    : buffer(512),
+      path("/"),
+      mode(PROMPT)
+{
+    setTerminator( "\r\n" );
+}
 
-    return true;
+/**
+ * 
+ */
+void
+PropsChannel::collectIncomingData( const char* s, int n )
+{
+    buffer.append( s, n );
 }
 
+/**
+ * 
+ */
+void
+PropsChannel::node_not_found_error( const string& node_name )
+{
+    string error = "-ERR Node \"";
+    error += node_name;
+    error += "\" not found.";
+    push( error.c_str() );
+    push( getTerminator() );
+}
 
-bool FGProps::process_command( const char *cmd ) {
-    cout << "processing command = " << cmd << endl;
-    string_list tokens;
-    tokens.clear();
+// return a human readable form of the value "type"
+static string
+getValueTypeString( const SGPropertyNode *node )
+{
+    string result;
 
-    istrstream in(cmd);
-    
-    while ( !in.eof() ) {
-       string token;
-       in >> token;
-       tokens.push_back( token );
+    if ( node == NULL )
+    {
+       return "unknown";
     }
 
-    string command = tokens[0];
-
-    if ( command == "ls" ) {
-       
+    SGPropertyNode::Type type = node->getType();
+    if ( type == SGPropertyNode::UNSPECIFIED ) {
+       result = "unspecified";
+    } else if ( type == SGPropertyNode::NONE ) {
+        result = "none";
+    } else if ( type == SGPropertyNode::BOOL ) {
+       result = "bool";
+    } else if ( type == SGPropertyNode::INT ) {
+       result = "int";
+    } else if ( type == SGPropertyNode::LONG ) {
+       result = "long";
+    } else if ( type == SGPropertyNode::FLOAT ) {
+       result = "float";
+    } else if ( type == SGPropertyNode::DOUBLE ) {
+       result = "double";
+    } else if ( type == SGPropertyNode::STRING ) {
+       result = "string";
     }
 
-    return true;
+    return result;
 }
 
+/**
+ * We have a command.
+ * 
+ */
+void
+PropsChannel::foundTerminator()
+{
+    const char* cmd = buffer.getData();
+    SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
 
-// process work for this port
-bool FGProps::process() {
-    SGIOChannel *io = get_io_channel();
+    vector<string> tokens = simgear::strutils::split( cmd );
 
-    cout << "processing incoming props command" << endl;
+    SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
 
-    if ( get_direction() == SG_IO_BI ) {
-       cout << "  (bi directional)" << endl;
-       while ( io->read( buf, max_cmd_len ) > 0 ) {
-           FG_LOG( FG_IO, FG_ALERT, "Success reading data." );
-           process_command( buf );
+    if (!tokens.empty()) {
+       string command = tokens[0];
+
+       if (command == "ls") {
+           SGPropertyNode* dir = node;
+           if (tokens.size() == 2) {
+               if (tokens[1][0] == '/') {
+                   dir = globals->get_props()->getNode( tokens[1].c_str() );
+               } else {
+                   string s = path;
+                   s += "/";
+                   s += tokens[1];
+                   dir = globals->get_props()->getNode( s.c_str() );
+               }
+
+               if (dir == 0) {
+                   node_not_found_error( tokens[1] );
+                   goto prompt;
+               }
+           }
+
+           for (int i = 0; i < dir->nChildren(); i++) {
+               SGPropertyNode * child = dir->getChild(i);
+               string line = child->getDisplayName(true);
+
+               if ( child->nChildren() > 0 ) {
+                   line += "/";
+               } else {
+                   if (mode == PROMPT) {
+                       string value = child->getStringValue();
+                       line += " =\t'" + value + "'\t(";
+                       line += getValueTypeString( child );
+                       line += ")";
+                   }
+               }
+
+               line += getTerminator();
+               push( line.c_str() );
+           }
+       } else if ( command == "dump" ) {
+           strstream buf;
+           if ( tokens.size() <= 1 ) {
+               writeProperties( buf, node );
+               buf << ends; // null terminate the string
+               push( buf.str() );
+               push( getTerminator() );
+           } else {
+               SGPropertyNode *child = node->getNode( tokens[1].c_str() );
+               if ( child ) {
+                   writeProperties ( buf, child );
+                   buf << ends; // null terminate the string
+                   push( buf.str() );
+                   push( getTerminator() );
+               } else {
+                   node_not_found_error( tokens[1] );
+               }
+           }
+       }
+       else if ( command == "cd" ) {
+           if (tokens.size() == 2) {
+               try {
+                   SGPropertyNode* child = node->getNode( tokens[1].c_str() );
+                   if ( child ) {
+                       node = child;
+                       path = node->getPath();
+                   } else {
+                       node_not_found_error( tokens[1] );
+                   }
+               } catch (...) {
+                   // Ignore attempt to move past root node with ".."
+               }
+           }
+       } else if ( command == "pwd" ) {
+           string ttt = node->getPath();
+           if (ttt.empty()) {
+               ttt = "/";
+           }
+
+           push( ttt.c_str() );
+           push( getTerminator() );
+       } else if ( command == "get" || command == "show" ) {
+           if ( tokens.size() == 2 ) {
+               string tmp;     
+               string value = node->getStringValue ( tokens[1].c_str(), "" );
+               if ( mode == PROMPT ) {
+                   tmp = tokens[1];
+                   tmp += " = '";
+                   tmp += value;
+                   tmp += "' (";
+                   tmp += getValueTypeString(
+                                    node->getNode( tokens[1].c_str() ) );
+                   tmp += ")";
+               } else {
+                   tmp = value;
+               }
+               push( tmp.c_str() );
+               push( getTerminator() );
+           }
+       } else if ( command == "set" ) {
+           if ( tokens.size() >= 2 ) {
+                string value, tmp;
+                if ( tokens.size() == 3 ) {
+                    value = tokens[2];
+                } else {
+                    value = "";
+                }
+                node->getNode( tokens[1].c_str(), true )
+                    ->setStringValue(value.c_str());
+
+               if ( mode == PROMPT ) {
+                   // now fetch and write out the new value as confirmation
+                   // of the change
+                   value = node->getStringValue ( tokens[1].c_str(), "" );
+                   tmp = tokens[1] + " = '" + value + "' (";
+                   tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
+                   tmp += ")";
+                   push( tmp.c_str() );
+                   push( getTerminator() );
+               }
+           } 
+       } else if ( command == "reinit" ) {
+           if ( tokens.size() == 2 ) {
+               string tmp;     
+                SGPropertyNode args;
+                for ( unsigned int i = 1; i < tokens.size(); ++i ) {
+                    cout << "props: adding subsystem = " << tokens[i] << endl;
+                    SGPropertyNode *node = args.getNode("subsystem", i-1, true);
+                    node->setStringValue( tokens[i].c_str() );
+                }
+                if ( !globals->get_commands()
+                         ->execute( "reinit", &args) )
+                {
+                    SG_LOG( SG_GENERAL, SG_ALERT,
+                            "Command " << tokens[1] << " failed.");
+                    if ( mode == PROMPT ) {
+                        tmp += "*failed*";
+                        push( tmp.c_str() );
+                        push( getTerminator() );
+                    }
+                } else {
+                    if ( mode == PROMPT ) {
+                        tmp += "<completed>";
+                        push( tmp.c_str() );
+                        push( getTerminator() );
+                    }
+                }
+           }
+       } else if ( command == "run" ) {
+            string tmp;        
+            if ( tokens.size() >= 2 ) {
+                SGPropertyNode args;
+                if ( tokens[1] == "reinit" ) {
+                    for ( unsigned int i = 2; i < tokens.size(); ++i ) {
+                        cout << "props: adding subsystem = " << tokens[i]
+                             << endl;
+                        SGPropertyNode *node
+                            = args.getNode("subsystem", i-2, true);
+                        node->setStringValue( tokens[i].c_str() );
+                    }
+                } else if ( tokens[1] == "set-outside-air-temp-degc" ) {
+                    for ( unsigned int i = 2; i < tokens.size(); ++i ) {
+                        cout << "props: set-oat command = " << tokens[i]
+                             << endl;
+                        SGPropertyNode *node
+                            = args.getNode("temp-degc", i-2, true);
+                        node->setStringValue( tokens[i].c_str() );
+                    }
+                } else if ( tokens[1] == "timeofday" ) {
+                    for ( unsigned int i = 2; i < tokens.size(); ++i ) {
+                        cout << "props: time of day command = " << tokens[i]
+                             << endl;
+                        SGPropertyNode *node
+                            = args.getNode("timeofday", i-2, true);
+                        node->setStringValue( tokens[i].c_str() );
+                    }
+                }
+                if ( !globals->get_commands()
+                         ->execute(tokens[1].c_str(), &args) )
+                {
+                    SG_LOG( SG_GENERAL, SG_ALERT,
+                            "Command " << tokens[1] << " failed.");
+                    if ( mode == PROMPT ) {
+                        tmp += "*failed*";
+                        push( tmp.c_str() );
+                        push( getTerminator() );
+                    }
+                } else {
+                    if ( mode == PROMPT ) {
+                        tmp += "<completed>";
+                        push( tmp.c_str() );
+                        push( getTerminator() );
+                    }
+                }
+           } else {
+                if ( mode == PROMPT ) {
+                    tmp += "no command specified";
+                    push( tmp.c_str() );
+                    push( getTerminator() );
+                }
+            }
+       } else if (command == "quit") {
+           close();
+           shouldDelete();
+           return;
+       } else if ( command == "data" ) {
+           mode = DATA;
+       } else if ( command == "prompt" ) {
+           mode = PROMPT;
+       } else {
+           const char* msg = "\
+Valid commands are:\r\n\
+\r\n\
+cd <dir>           cd to a directory, '..' to move back\r\n\
+data               switch to raw data mode\r\n\
+dump               dump current state (in xml)\r\n\
+get <var>          show the value of a parameter\r\n\
+help               show this help message\r\n\
+ls [<dir>]         list directory\r\n\
+prompt             switch to interactive mode (default)\r\n\
+pwd                display your current path\r\n\
+quit               terminate connection\r\n\
+run <command>      run built in command\r\n\
+set <var> <val>    set <var> to a new <val>\r\n\
+show <var>         synonym for get\r\n";
+           push( msg );
        }
-    } else {
-       FG_LOG( FG_IO, FG_ALERT, 
-               "in or out direction not supported for FGProps." );
     }
 
-    return true;
-}
+ prompt:
+    if (mode == PROMPT) {
+       string prompt = node->getPath();
+       if (prompt.empty()) {
+           prompt = "/";
+       }
+       prompt += "> ";
+       push( prompt.c_str() );
+    }
 
+    buffer.remove();
+}
 
-// close the channel
-bool FGProps::close() {
-    SGIOChannel *io = get_io_channel();
+/**
+ * 
+ */
+FGProps::FGProps( const vector<string>& tokens )
+{
+    // tokens:
+    //   props,port#
+    //   props,medium,direction,hz,hostname,port#,style
+    if (tokens.size() == 2) {
+       port = atoi( tokens[1].c_str() );
+        set_hz( 5 );                // default to processing requests @ 5Hz
+    } else if (tokens.size() == 7) {
+        char* endptr;
+        errno = 0;
+        int hz = strtol( tokens[3].c_str(), &endptr, 10 );
+        if (errno != 0) {
+           SG_LOG( SG_IO, SG_ALERT, "I/O poll frequency out of range" );
+           set_hz( 5 );           // default to processing requests @ 5Hz
+        } else {
+            SG_LOG( SG_IO, SG_INFO, "Setting I/O poll frequency to "
+                    << hz << " Hz");
+            set_hz( hz );
+        }
+       port = atoi( tokens[5].c_str() );
+    } else {
+       throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
+    }
+}
 
-    set_enabled( false );
+/**
+ * 
+ */
+FGProps::~FGProps()
+{
+}
 
-    if ( ! io->close() ) {
+/**
+ * 
+ */
+bool
+FGProps::open()
+{
+    if ( is_enabled() )
+    {
+       SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " 
+               << "is already in use, ignoring" );
        return false;
     }
 
+    netChannel::open();
+    netChannel::bind( "", port );
+    netChannel::listen( 5 );
+    SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
+
+    set_enabled( true );
+    return true;
+}
+
+/**
+ * 
+ */
+bool
+FGProps::close()
+{
     return true;
 }
+
+/**
+ * 
+ */
+bool
+FGProps::process()
+{
+    netChannel::poll();
+    return true;
+}
+
+/**
+ * 
+ */
+void
+FGProps::handleAccept()
+{
+    netAddress addr;
+    int handle = accept( &addr );
+    SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
+           << addr.getHost() << ":" << addr.getPort() );
+    PropsChannel* channel = new PropsChannel();
+    channel->setHandle( handle );
+}