From 9b4c2def75605908e81becfd0fab0a2ae70a3629 Mon Sep 17 00:00:00 2001 From: James Turner Date: Thu, 17 May 2012 17:31:28 +0100 Subject: [PATCH] Merge commit 'refs/merge-requests/28' of git://gitorious.org/fg/flightgear into merge-requests/28 --- src/Network/props.cxx | 110 ++++++++++++++++++++++++++++++++++++++++-- src/Network/props.hxx | 3 +- 2 files changed, 108 insertions(+), 5 deletions(-) diff --git a/src/Network/props.cxx b/src/Network/props.cxx index 1fd2971eb..8ea9596c5 100644 --- a/src/Network/props.cxx +++ b/src/Network/props.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -43,8 +44,16 @@ #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 &e) { + 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() ); @@ -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 ); } } diff --git a/src/Network/props.hxx b/src/Network/props.hxx index e55f7c133..09b5a09a3 100644 --- a/src/Network/props.hxx +++ b/src/Network/props.hxx @@ -40,7 +40,8 @@ * FlightGear properties. */ class FGProps : public FGProtocol, - public simgear::NetChannel + public simgear::NetChannel, + public SGPropertyChangeListener // for subscriptions { private: