X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FNetwork%2Fprops.cxx;h=58905e2f30a8a1c953cf05e017cac6d0239e0de4;hb=b0dcb657e77579ecc79798ff365737095f96f9e2;hp=ac1b6ee9702633f1d166d45e19ab3b05f9a6b6b7;hpb=51e307a74296fa30464ff02df695451e79de5e5a;p=flightgear.git diff --git a/src/Network/props.cxx b/src/Network/props.cxx index ac1b6ee97..58905e2f3 100644 --- a/src/Network/props.cxx +++ b/src/Network/props.cxx @@ -33,18 +33,27 @@ #include #include #include +#include #include #include #include #include
-#include
+#include #include +#include + #include "props.hxx" +#include +#include +#include + +#include + using std::stringstream; using std::ends; @@ -55,7 +64,7 @@ using std::endl; * Props connection class. * This class represents a connection to props client. */ -class PropsChannel : public simgear::NetChat +class PropsChannel : public simgear::NetChat, public SGPropertyChangeListener { simgear::NetBuffer buffer; @@ -75,6 +84,7 @@ public: * Constructor. */ PropsChannel(); + ~PropsChannel(); /** * Append incoming data to our request buffer. @@ -89,10 +99,37 @@ public: */ void foundTerminator(); + // callback for registered listeners (subscriptions) + void valueChanged(SGPropertyNode *node); private: + + 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); }; /** @@ -104,6 +141,62 @@ PropsChannel::PropsChannel() mode(PROMPT) { setTerminator( "\r\n" ); + callback_map["subscribe"] = &PropsChannel::subscribe; + callback_map["unsubscribe"] = &PropsChannel::unsubscribe; +} + +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() ); } /** @@ -160,7 +253,7 @@ 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() ); @@ -292,7 +385,7 @@ PropsChannel::foundTerminator() if ( !globals->get_commands() ->execute( "reinit", &args) ) { - SG_LOG( SG_GENERAL, SG_ALERT, + SG_LOG( SG_NETWORK, SG_ALERT, "Command " << tokens[1] << " failed."); if ( mode == PROMPT ) { tmp += "*failed*"; @@ -357,7 +450,7 @@ PropsChannel::foundTerminator() if ( !globals->get_commands() ->execute(tokens[1].c_str(), &args) ) { - SG_LOG( SG_GENERAL, SG_ALERT, + SG_LOG( SG_NETWORK, SG_ALERT, "Command " << tokens[1] << " failed."); if ( mode == PROMPT ) { tmp += "*failed*"; @@ -386,7 +479,14 @@ PropsChannel::foundTerminator() mode = DATA; } else if ( command == "prompt" ) { mode = PROMPT; - } else { + } 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\ @@ -400,7 +500,9 @@ prompt switch to interactive mode (default)\r\n\ 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"; +set set to a new \r\n\ +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 ); } } @@ -450,7 +552,6 @@ FGProps::FGProps( const vector& tokens ) } else { throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" ); } - printf( "Property server started on port %d\n", port ); } /** @@ -473,9 +574,28 @@ FGProps::open() return false; } - simgear::NetChannel::open(); - simgear::NetChannel::bind( "", port ); - simgear::NetChannel::listen( 5 ); + 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; + } + + poller.addChannel(this); + SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port ); set_enabled( true ); @@ -498,7 +618,7 @@ FGProps::close() bool FGProps::process() { - simgear::NetChannel::poll(); + poller.poll(); return true; } @@ -514,4 +634,5 @@ FGProps::handleAccept() << addr.getHost() << ":" << addr.getPort() ); PropsChannel* channel = new PropsChannel(); channel->setHandle( handle ); + poller.addChannel( channel ); }