X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FNetwork%2Fprops.cxx;h=58905e2f30a8a1c953cf05e017cac6d0239e0de4;hb=b0dcb657e77579ecc79798ff365737095f96f9e2;hp=58f7e4466ce3c864cdd9281cd703b122a3523d04;hpb=da5ea10d5db9675e6a5d75db5b33edfc350566b1;p=flightgear.git diff --git a/src/Network/props.cxx b/src/Network/props.cxx index 58f7e4466..58905e2f3 100644 --- a/src/Network/props.cxx +++ b/src/Network/props.cxx @@ -4,7 +4,7 @@ // Written by Curtis Olson, started September 2000. // Modified by Bernie Bright, May 2002. // -// Copyright (C) 2000 Curtis L. Olson - curt@flightgear.org +// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as @@ -18,7 +18,7 @@ // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software -// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -33,26 +33,40 @@ #include #include #include +#include #include +#include +#include #include
-#include
+#include -#include +#include + +#include #include "props.hxx" -SG_USING_STD(stringstream); -SG_USING_STD(ends); +#include +#include +#include + +#include + +using std::stringstream; +using std::ends; + +using std::cout; +using std::endl; /** * Props connection class. * This class represents a connection to props client. */ -class PropsChannel : public netChat +class PropsChannel : public simgear::NetChat, public SGPropertyChangeListener { - netBuffer buffer; + simgear::NetBuffer buffer; /** * Current property node name. @@ -60,8 +74,8 @@ class PropsChannel : public netChat string path; enum Mode { - PROMPT, - DATA + PROMPT, + DATA }; Mode mode; @@ -70,7 +84,8 @@ public: * Constructor. */ PropsChannel(); - + ~PropsChannel(); + /** * Append incoming data to our request buffer. * @@ -84,15 +99,41 @@ public: */ void foundTerminator(); + // callback for registered listeners (subscriptions) + void valueChanged(SGPropertyNode *node); private: - /** - * Return a "Node no found" error message to the client. - */ - void node_not_found_error( const string& node_name ); + + typedef string_list ParameterList; + + inline void node_not_found_error( const string& s ) const { + throw "node '" + s + "' not found"; + } + + void error(std::string msg) { // wrapper: prints errors to STDERR and to the telnet client + push( msg.c_str() ); push( getTerminator() ); + SG_LOG(SG_NETWORK, SG_ALERT, __FILE__<<"@" << __LINE__ <<" in " << __FUNCTION__ <<":"<< msg.c_str() << std::endl); + } + + + bool check_args(const ParameterList &tok, const unsigned int num, const char* func) { + if (tok.size()-1 < num) { + error(string("Error:Wrong argument count for:")+string(func) ); + return false; + } + return true; + } + + std::vector _listeners; + typedef void (PropsChannel::*TelnetCallback) (const ParameterList&); + std::map callback_map; + + // callback implementations: + void subscribe(const ParameterList &p); + void unsubscribe(const ParameterList &p); }; /** - * + * */ PropsChannel::PropsChannel() : buffer(512), @@ -100,58 +141,103 @@ PropsChannel::PropsChannel() mode(PROMPT) { setTerminator( "\r\n" ); + callback_map["subscribe"] = &PropsChannel::subscribe; + callback_map["unsubscribe"] = &PropsChannel::unsubscribe; } -/** - * - */ -void -PropsChannel::collectIncomingData( const char* s, int n ) -{ - buffer.append( s, n ); +PropsChannel::~PropsChannel() { + // clean up all registered listeners + BOOST_FOREACH(SGPropertyNode_ptr l, _listeners) { + l->removeChangeListener( this ); + } +} + +void PropsChannel::subscribe(const ParameterList ¶m) { + if (! check_args(param,1,"subscribe")) return; + + std::string command = param[0]; + const char* p = param[1].c_str(); + if (!p) return; + + //SG_LOG(SG_GENERAL, SG_ALERT, p << std::endl); + push( command.c_str() ); push ( " " ); + push( p ); + push( getTerminator() ); + + SGPropertyNode *n = globals->get_props()->getNode( p,true ); + if ( n->isTied() ) { + error("Error:Tied properties cannot register listeners"); + return; + } + + if (n) { + n->addChangeListener( this ); + _listeners.push_back( n ); // housekeeping, save for deletion in dtor later on + } else { + error("listener could not be added"); + } +} + +void PropsChannel::unsubscribe(const ParameterList ¶m) { + if (!check_args(param,1,"unsubscribe")) return; + + try { + SGPropertyNode *n = globals->get_props()->getNode( param[1].c_str() ); + if (n) + n->removeChangeListener( this ); + } catch (sg_exception&) { + error("Error:Listener could not be removed"); + } +} + + +//TODO: provide support for different types of subscriptions MODES ? (child added/removed, thesholds, min/max) +void PropsChannel::valueChanged(SGPropertyNode* ptr) { + //SG_LOG(SG_GENERAL, SG_ALERT, __FILE__<< "@"<<__LINE__ << ":" << __FUNCTION__ << std::endl); + std::stringstream response; + response << ptr->getPath(true) << "=" << ptr->getStringValue() << getTerminator(); //TODO: use hashes, echo several properties at once + push( response.str().c_str() ); } /** - * + * */ void -PropsChannel::node_not_found_error( const string& node_name ) +PropsChannel::collectIncomingData( const char* s, int n ) { - string error = "-ERR Node \""; - error += node_name; - error += "\" not found."; - push( error.c_str() ); - push( getTerminator() ); + buffer.append( s, n ); } // return a human readable form of the value "type" static string getValueTypeString( const SGPropertyNode *node ) { + using namespace simgear; + string result; if ( node == NULL ) { - return "unknown"; + return "unknown"; } - SGPropertyNode::Type type = node->getType(); - if ( type == SGPropertyNode::UNSPECIFIED ) { - result = "unspecified"; - } else if ( type == SGPropertyNode::NONE ) { + props::Type type = node->getType(); + if ( type == props::UNSPECIFIED ) { + result = "unspecified"; + } else if ( type == props::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"; + } else if ( type == props::BOOL ) { + result = "bool"; + } else if ( type == props::INT ) { + result = "int"; + } else if ( type == props::LONG ) { + result = "long"; + } else if ( type == props::FLOAT ) { + result = "float"; + } else if ( type == props::DOUBLE ) { + result = "double"; + } else if ( type == props::STRING ) { + result = "string"; } return result; @@ -159,7 +245,7 @@ getValueTypeString( const SGPropertyNode *node ) /** * We have a command. - * + * */ void PropsChannel::foundTerminator() @@ -167,228 +253,241 @@ PropsChannel::foundTerminator() const char* cmd = buffer.getData(); SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" ); - vector tokens = simgear::strutils::split( cmd ); + ParameterList tokens = simgear::strutils::split( cmd ); SGPropertyNode* node = globals->get_props()->getNode( path.c_str() ); - 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; - } - } + try { + 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() ); + } - 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" ) { - stringstream buf; - if ( tokens.size() <= 1 ) { - writeProperties( buf, node ); - buf << ends; // null terminate the string - push( buf.str().c_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().c_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 = "/"; - } + if (dir == 0) { + node_not_found_error( tokens[1] ); + } + } - 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]; + 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" ) { + stringstream buf; + if ( tokens.size() <= 1 ) { + writeProperties( buf, node ); + buf << ends; // null terminate the string + push( buf.str().c_str() ); + push( getTerminator() ); } else { - value = ""; + SGPropertyNode *child = node->getNode( tokens[1].c_str() ); + if ( child ) { + writeProperties ( buf, child ); + buf << ends; // null terminate the string + push( buf.str().c_str() ); + push( getTerminator() ); + } else { + node_not_found_error( tokens[1] ); + } } - 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() ); + } + else if ( command == "cd" ) { + if (tokens.size() == 2) { + SGPropertyNode* child = node->getNode( tokens[1].c_str() ); + if ( child ) { + node = child; + path = node->getPath(); + } else { + node_not_found_error( tokens[1] ); + } + } + } else if ( command == "pwd" ) { + string pwd = node->getPath(); + if (pwd.empty()) { + pwd = "/"; } - if ( !globals->get_commands() - ->execute( "reinit", &args) ) - { - SG_LOG( SG_GENERAL, SG_ALERT, - "Command " << tokens[1] << " failed."); + + push( pwd.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 += "*failed*"; - push( tmp.c_str() ); - push( getTerminator() ); + tmp = tokens[1]; + tmp += " = '"; + tmp += value; + tmp += "' ("; + tmp += getValueTypeString( + node->getNode( tokens[1].c_str() ) ); + tmp += ")"; + } else { + tmp = value; } - } else { + push( tmp.c_str() ); + push( getTerminator() ); + } + } else if ( command == "set" ) { + if ( tokens.size() >= 2 ) { + string value, tmp; + for (unsigned int i = 2; i < tokens.size(); i++) { + if (i > 2) + value += " "; + value += tokens[i]; + } + node->getNode( tokens[1].c_str(), true ) + ->setStringValue(value.c_str()); + if ( mode == PROMPT ) { - tmp += ""; + // 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 == "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-sea-level-air-temp-degc" ) { - for ( unsigned int i = 2; i < tokens.size(); ++i ) { - cout << "props: set-sl command = " << tokens[i] - << endl; - SGPropertyNode *node - = args.getNode("temp-degc", i-2, true); + } 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() ); } - } 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( "reinit", &args) ) + { + SG_LOG( SG_NETWORK, SG_ALERT, + "Command " << tokens[1] << " failed."); + if ( mode == PROMPT ) { + tmp += "*failed*"; + push( tmp.c_str() ); + push( getTerminator() ); + } + } else { + if ( mode == PROMPT ) { + tmp += ""; + push( tmp.c_str() ); + push( getTerminator() ); + } } } - 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 ( 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-sea-level-air-temp-degc" ) { + for ( unsigned int i = 2; i < tokens.size(); ++i ) { + cout << "props: set-sl command = " << tokens[i] + << endl; + SGPropertyNode *node + = args.getNode("temp-degc", 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() ); + } + } else if ( tokens[1] == "play-audio-message" ) { + if ( tokens.size() == 4 ) { + cout << "props: play audio message = " << tokens[2] + << " " << tokens[3] << endl; + SGPropertyNode *node; + node = args.getNode("path", 0, true); + node->setStringValue( tokens[2].c_str() ); + node = args.getNode("file", 0, true); + node->setStringValue( tokens[3].c_str() ); + } + } + if ( !globals->get_commands() + ->execute(tokens[1].c_str(), &args) ) + { + SG_LOG( SG_NETWORK, SG_ALERT, + "Command " << tokens[1] << " failed."); + if ( mode == PROMPT ) { + tmp += "*failed*"; + push( tmp.c_str() ); + push( getTerminator() ); + } + } else { + if ( mode == PROMPT ) { + tmp += ""; + push( tmp.c_str() ); + push( getTerminator() ); + } } } else { if ( mode == PROMPT ) { - tmp += ""; + tmp += "no command specified"; 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 = "\ + } else if ( command == "quit" || command == "exit" ) { + close(); + shouldDelete(); + return; + } else if ( command == "data" ) { + mode = DATA; + } else if ( command == "prompt" ) { + mode = PROMPT; + } else if (callback_map.find(command) != callback_map.end() ) { + TelnetCallback t = callback_map[ command ]; + if (t) + (this->*t) (tokens); + else + error("No matching callback found for command:"+command); + } + else { + const char* msg = "\ Valid commands are:\r\n\ \r\n\ cd cd to a directory, '..' to move back\r\n\ @@ -402,26 +501,32 @@ pwd display your current path\r\n\ quit terminate connection\r\n\ run run built in command\r\n\ set set to a new \r\n\ -show synonym for get\r\n"; - push( msg ); - } +subscribe subscribe to property changes \r\n\ +unscubscribe unscubscribe from property changes (var must be the property name/path used by subscribe)\r\n"; + push( msg ); + } + } + + } catch ( const string& msg ) { + string error = "-ERR \"" + msg + "\""; + push( error.c_str() ); + push( getTerminator() ); } - prompt: - if (mode == PROMPT) { - string prompt = node->getPath(); - if (prompt.empty()) { - prompt = "/"; - } - prompt += "> "; - push( prompt.c_str() ); + if ( mode == PROMPT ) { + string prompt = node->getPath(); + if (prompt.empty()) { + prompt = "/"; + } + prompt += "> "; + push( prompt.c_str() ); } buffer.remove(); } /** - * + * */ FGProps::FGProps( const vector& tokens ) { @@ -429,7 +534,7 @@ FGProps::FGProps( const vector& tokens ) // props,port# // props,medium,direction,hz,hostname,port#,style if (tokens.size() == 2) { - port = atoi( tokens[1].c_str() ); + port = atoi( tokens[1].c_str() ); set_hz( 5 ); // default to processing requests @ 5Hz } else if (tokens.size() == 7) { char* endptr; @@ -443,35 +548,54 @@ FGProps::FGProps( const vector& tokens ) << hz << " Hz"); set_hz( hz ); } - port = atoi( tokens[5].c_str() ); + port = atoi( tokens[5].c_str() ); } else { - throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" ); + throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" ); } } /** - * + * */ FGProps::~FGProps() { } /** - * + * */ 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; + SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel " + << "is already in use, ignoring" ); + return false; + } + + if (!simgear::NetChannel::open()) + { + SG_LOG( SG_IO, SG_ALERT, "FGProps: Failed to open network socket."); + return false; + } + + int err = simgear::NetChannel::bind( "", port ); + if (err) + { + SG_LOG( SG_IO, SG_ALERT, "FGProps: Failed to open port #" << port << " - the port is already used (error " << err << ")."); + return false; + } + + err = simgear::NetChannel::listen( 5 ); + if (err) + { + SG_LOG( SG_IO, SG_ALERT, "FGProps: Failed to listen on port #" << port << "(error " << err << ")."); + return false; } - netChannel::open(); - netChannel::bind( "", port ); - netChannel::listen( 5 ); + poller.addChannel(this); + SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port ); set_enabled( true ); @@ -479,34 +603,36 @@ FGProps::open() } /** - * + * */ bool FGProps::close() { + SG_LOG( SG_IO, SG_INFO, "closing FGProps" ); return true; } /** - * + * */ bool FGProps::process() { - netChannel::poll(); + poller.poll(); return true; } /** - * + * */ void FGProps::handleAccept() { - netAddress addr; + simgear::IPAddress addr; int handle = accept( &addr ); SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from " - << addr.getHost() << ":" << addr.getPort() ); + << addr.getHost() << ":" << addr.getPort() ); PropsChannel* channel = new PropsChannel(); channel->setHandle( handle ); + poller.addChannel( channel ); }