Improved J661 support: Difference between revisions

Jump to navigation Jump to search
m
→‎Patch prototype: http://flightgear.org/forums/viewtopic.php?f=18&t=12467&p=149039&hilit=j661#p149039
mNo edit summary
m (→‎Patch prototype: http://flightgear.org/forums/viewtopic.php?f=18&t=12467&p=149039&hilit=j661#p149039)
Line 8: Line 8:


= Patch prototype =
= Patch prototype =
<syntaxhighlight lang="cpp">
<syntaxhighlight lang="diff">
//
    diff --git a/src/Network/props.cxx b/src/Network/props.cxx
    index 7240d5c..bd014db 100644
    --- a/src/Network/props.cxx
    +++ b/src/Network/props.cxx
    @@ -33,6 +33,7 @@
    #include <simgear/misc/strutils.hxx>
    #include <simgear/props/props.hxx>
    #include <simgear/props/props_io.hxx>
    +#include <simgear/structure/exception.hxx>
 
    #include <sstream>
    #include <iostream>
    @@ -45,6 +46,12 @@
 
    #include "props.hxx"
 
    +#include <map>
    +#include <vector>
    +#include <string>
    +
    +#include <boost/functional/hash.hpp> // for property hashing (should probably be replaced eventually)
    +
    using std::stringstream;
    using std::ends;
 
    @@ -55,7 +62,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 +82,7 @@ public:
          * Constructor.
          */
        PropsChannel();
    +    ~PropsChannel();
 
        /**
          * Append incoming data to our request buffer.
    @@ -89,10 +97,38 @@ public:
          */
        void foundTerminator();
 
    +    // callback for registered listeners (subscriptions)
    +    void valueChanged(SGPropertyNode *node);
    private:
    +
    +    typedef std::vector<std::string> 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_GENERAL, SG_ALERT, __FILE__<<"@" << __LINE__ <<" in " << __FUNCTION__ <<":"<< msg.c_str() << std::endl);
    +    }
    +
    +
    +    bool check_args(ParameterList tok, unsigned int num, const char* func) {
    +      if (tok.size()-1 < num) {
    +          error(string("Wrong argument count for:")+string(func) );
    +          return false;
    +      }
    +      return true;
    +    }
    +
    +    std::vector<SGPropertyNode_ptr> _listeners;
    +    typedef void (PropsChannel::*TelnetCallback) (const ParameterList&);
    +    std::map<std::string, TelnetCallback> callback_map;
    +
    +    // callback implementations:
    +    void subscribe(const ParameterList &p);
    +    void unsubscribe(const ParameterList &p);
    };
 
    /**
    @@ -104,6 +140,67 @@ PropsChannel::PropsChannel()
          mode(PROMPT)
    {
        setTerminator( "\r\n" );
    +    callback_map["subscribe"]    =    &PropsChannel::subscribe;
    +    callback_map["unsubscribe"]  =  &PropsChannel::unsubscribe;
    +}
    +
    +PropsChannel::~PropsChannel() {
    +  // clean up all registered listeners 
    +  for (unsigned int i=0;i< _listeners.size(); i++)
    +    _listeners[i]->removeChangeListener( this  );
    +
    +}
    +
    +void PropsChannel::subscribe(const ParameterList& param) {
    +  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 );
    +    std::stringstream hashcode;
    +    boost::hash<std::string> string_hash;
    +    hashcode << "id:" << string_hash(n->getPath()) << getTerminator();
    +    // push( hashcode.str().c_str() );  // not yet very useful
    +    _listeners.push_back( n ); // housekeeping, save for deletion in dtor later on
    +        }
    +        else {
    +      error("listener could not be added");
    +        }
    +
    +
    +}
    +
    +void PropsChannel::unsubscribe(const ParameterList &param) {
    +  if (!check_args(param,1,"unsubscribe")) return;
    +
    +  try {
    +  SGPropertyNode *n = globals->get_props()->getNode( param[1].c_str() );
    +  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() << "\r\n"; //TODO: use hashes, echo several properties at once, use proper terminator
    +  push( response.str().c_str() );
    }
 
    /**
    @@ -160,7 +257,7 @@ PropsChannel::foundTerminator()
        const char* cmd = buffer.getData();
        SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
 
    -    vector<string> tokens = simgear::strutils::split( cmd );
    +    ParameterList tokens = simgear::strutils::split( cmd );
 
        SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
 
    @@ -292,7 +389,7 @@ PropsChannel::foundTerminator()
                        if ( !globals->get_commands()
                                  ->execute( "reinit", &args) )
                        {
    -                        SG_LOG( SG_NETWORK, SG_ALERT,
    +                        SG_LOG( SG_GENERAL, SG_ALERT,
                                    "Command " << tokens[1] << " failed.");
                            if ( mode == PROMPT ) {
                                tmp += "*failed*";
    @@ -357,7 +454,7 @@ PropsChannel::foundTerminator()
                        if ( !globals->get_commands()
                                  ->execute(tokens[1].c_str(), &args) )
                        {
    -                        SG_LOG( SG_NETWORK, SG_ALERT,
    +                        SG_LOG( SG_GENERAL, SG_ALERT,
                                    "Command " << tokens[1] << " failed.");
                            if ( mode == PROMPT ) {
                                tmp += "*failed*";
    @@ -386,7 +483,14 @@ PropsChannel::foundTerminator()
                    mode = DATA;
                } else if ( command == "prompt" ) {
                    mode = PROMPT;
    -            } else {
    +            } else if (command  == "subscribe" || command == "unsubscribe") {
    +        TelnetCallback t = callback_map[ command.c_str() ]; //FIXME: use map lookup for condition
    +        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 +504,9 @@ 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";
    +set <var> <val>    set <var> to a new <val>\r\n\
    +subscribe <var>      subscribe to property changes (returns hash code for property)\r\n\
    +unscubscribe <id>  unscubscribe from property changes (id must be the property hash as returned from subscribe command)\r\n";
                    push( msg );
                }
            }
    diff --git a/src/Network/props.hxx b/src/Network/props.hxx
    index e55f7c1..09b5a09 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:
 
 
</syntaxhighlight>
</syntaxhighlight>


= Prototype patch =
= Prototype patch =

Navigation menu