Improved J661 support: Difference between revisions

Jump to navigation Jump to search
Trim leading space to fix syntaxhighlight
(Moving up a suitable first section; Cleanup; +-cat)
(Trim leading space to fix syntaxhighlight)
Line 40: Line 40:


<syntaxhighlight lang="diff">
<syntaxhighlight lang="diff">
    diff --git a/src/Network/props.cxx b/src/Network/props.cxx
diff --git a/src/Network/props.cxx b/src/Network/props.cxx
    index 7240d5c..bd014db 100644
index 7240d5c..bd014db 100644
    --- a/src/Network/props.cxx
--- a/src/Network/props.cxx
    +++ b/src/Network/props.cxx
+++ b/src/Network/props.cxx
    @@ -33,6 +33,7 @@
@@ -33,6 +33,7 @@
    #include <simgear/misc/strutils.hxx>
#include <simgear/misc/strutils.hxx>
    #include <simgear/props/props.hxx>
#include <simgear/props/props.hxx>
    #include <simgear/props/props_io.hxx>
#include <simgear/props/props_io.hxx>
    +#include <simgear/structure/exception.hxx>
+#include <simgear/structure/exception.hxx>


    #include <sstream>
#include <sstream>
    #include <iostream>
#include <iostream>
    @@ -45,6 +46,12 @@
@@ -45,6 +46,12 @@


    #include "props.hxx"
#include "props.hxx"


    +#include <map>
+#include <map>
    +#include <vector>
+#include <vector>
    +#include <string>
+#include <string>
    +
+
    +#include <boost/functional/hash.hpp> // for property hashing (should probably be replaced eventually)
+#include <boost/functional/hash.hpp> // for property hashing (should probably be replaced eventually)
    +
+
    using std::stringstream;
using std::stringstream;
    using std::ends;
using std::ends;


    @@ -55,7 +62,7 @@ using std::endl;
@@ -55,7 +62,7 @@ using std::endl;
      * Props connection class.
* Props connection class.
      * This class represents a connection to props client.
* This class represents a connection to props client.
      */
*/
    -class PropsChannel : public simgear::NetChat
-class PropsChannel : public simgear::NetChat
    +class PropsChannel : public simgear::NetChat, public SGPropertyChangeListener
+class PropsChannel : public simgear::NetChat, public SGPropertyChangeListener
    {
{
        simgear::NetBuffer buffer;
simgear::NetBuffer buffer;


    @@ -75,6 +82,7 @@ public:
@@ -75,6 +82,7 @@ public:
          * Constructor.
* Constructor.
          */
*/
        PropsChannel();
PropsChannel();
    +    ~PropsChannel();
+    ~PropsChannel();


        /**
/**
          * Append incoming data to our request buffer.
* Append incoming data to our request buffer.
    @@ -89,10 +97,38 @@ public:
@@ -89,10 +97,38 @@ public:
          */
*/
        void foundTerminator();
void foundTerminator();


    +    // callback for registered listeners (subscriptions)
+    // callback for registered listeners (subscriptions)
    +    void valueChanged(SGPropertyNode *node);
+    void valueChanged(SGPropertyNode *node);
    private:
private:
    +
+
    +    typedef std::vector<std::string> ParameterList;
+    typedef std::vector<std::string> ParameterList;
    +
+
    +
+
        inline void node_not_found_error( const string& s ) const {
inline void node_not_found_error( const string& s ) const {
            throw "node '" + s + "' not found";
throw "node '" + s + "' not found";
        }
}
    +
+
    +    void error(std::string msg) {  // wrapper: prints errors to STDERR and to the telnet client
+    void error(std::string msg) {  // wrapper: prints errors to STDERR and to the telnet client
    +      push( msg.c_str() ); push( getTerminator() );
+      push( msg.c_str() ); push( getTerminator() );
    +      SG_LOG(SG_GENERAL, SG_ALERT, __FILE__<<"@" << __LINE__ <<" in " << __FUNCTION__ <<":"<< msg.c_str() << std::endl);
+      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) {
+    bool check_args(ParameterList tok, unsigned int num, const char* func) {
    +      if (tok.size()-1 < num) {
+      if (tok.size()-1 < num) {
    +          error(string("Wrong argument count for:")+string(func) );
+          error(string("Wrong argument count for:")+string(func) );
    +          return false;
+          return false;
    +      }
+      }
    +      return true;
+      return true;
    +    }
+    }
    +
+
    +    std::vector<SGPropertyNode_ptr> _listeners;
+    std::vector<SGPropertyNode_ptr> _listeners;
    +    typedef void (PropsChannel::*TelnetCallback) (const ParameterList&);
+    typedef void (PropsChannel::*TelnetCallback) (const ParameterList&);
    +    std::map<std::string, TelnetCallback> callback_map;
+    std::map<std::string, TelnetCallback> callback_map;
    +
+
    +    // callback implementations:
+    // callback implementations:
    +    void subscribe(const ParameterList &p);
+    void subscribe(const ParameterList &p);
    +    void unsubscribe(const ParameterList &p);
+    void unsubscribe(const ParameterList &p);
    };
};


    /**
/**
    @@ -104,6 +140,67 @@ PropsChannel::PropsChannel()
@@ -104,6 +140,67 @@ PropsChannel::PropsChannel()
          mode(PROMPT)
mode(PROMPT)
    {
{
        setTerminator( "\r\n" );
setTerminator( "\r\n" );
    +    callback_map["subscribe"]    =    &PropsChannel::subscribe;
+    callback_map["subscribe"]    =    &PropsChannel::subscribe;
    +    callback_map["unsubscribe"]  =  &PropsChannel::unsubscribe;
+    callback_map["unsubscribe"]  =  &PropsChannel::unsubscribe;
    +}
+}
    +
+
    +PropsChannel::~PropsChannel() {
+PropsChannel::~PropsChannel() {
    +  // clean up all registered listeners   
+  // clean up all registered listeners   
    +  for (unsigned int i=0;i< _listeners.size(); i++)
+  for (unsigned int i=0;i< _listeners.size(); i++)
    +    _listeners[i]->removeChangeListener( this  );
+    _listeners[i]->removeChangeListener( this  );
    +
+
    +}
+}
    +
+
    +void PropsChannel::subscribe(const ParameterList& param) {
+void PropsChannel::subscribe(const ParameterList& param) {
    +  if (! check_args(param,1,"subscribe")) return;
+  if (! check_args(param,1,"subscribe")) return;
    +
+
    +  std::string command = param[0];
+  std::string command = param[0];
    +  const char* p = param[1].c_str();
+  const char* p = param[1].c_str();
    +  if (!p) return;
+  if (!p) return;
    +
+
    +        SG_LOG(SG_GENERAL, SG_ALERT, p << std::endl);
+        SG_LOG(SG_GENERAL, SG_ALERT, p << std::endl);
    +        push( command.c_str() ); push ( " " );
+        push( command.c_str() ); push ( " " );
    +        push( p );
+        push( p );
    +        push( getTerminator() );
+        push( getTerminator() );
    +
+
    +        SGPropertyNode *n = globals->get_props()->getNode( p,true );
+        SGPropertyNode *n = globals->get_props()->getNode( p,true );
    +  if ( n->isTied() ) {
+  if ( n->isTied() ) {
    +      error("Error:Tied properties cannot register listeners");
+      error("Error:Tied properties cannot register listeners");
    +      return;
+      return;
    +  }
+  }
    +    if (n) {
+    if (n) {
    +        n->addChangeListener( this );
+        n->addChangeListener( this );
    +    std::stringstream hashcode;
+    std::stringstream hashcode;
    +    boost::hash<std::string> string_hash;
+    boost::hash<std::string> string_hash;
    +    hashcode << "id:" << string_hash(n->getPath()) << getTerminator();
+    hashcode << "id:" << string_hash(n->getPath()) << getTerminator();
    +    // push( hashcode.str().c_str() );  // not yet very useful
+    // push( hashcode.str().c_str() );  // not yet very useful
    +    _listeners.push_back( n ); // housekeeping, save for deletion in dtor later on
+    _listeners.push_back( n ); // housekeeping, save for deletion in dtor later on
    +        }
+        }
    +        else {
+        else {
    +      error("listener could not be added");
+      error("listener could not be added");
    +        }
+        }
    +
+
    +
+
    +}
+}
    +
+
    +void PropsChannel::unsubscribe(const ParameterList &param) {
+void PropsChannel::unsubscribe(const ParameterList &param) {
    +  if (!check_args(param,1,"unsubscribe")) return;
+  if (!check_args(param,1,"unsubscribe")) return;
    +
+
    +  try {
+  try {
    +  SGPropertyNode *n = globals->get_props()->getNode( param[1].c_str() );
+  SGPropertyNode *n = globals->get_props()->getNode( param[1].c_str() );
    +  n->removeChangeListener( this );
+  n->removeChangeListener( this );
    +  } catch (sg_exception &e) {
+  } catch (sg_exception &e) {
    +    error("Error:Listener could not be removed");
+    error("Error:Listener could not be removed");
    +  }
+  }
    +}
+}
    +
+
    +
+
    +//TODO: provide support for different types of subscriptions MODES ? (child added/removed, thesholds, min/max)
+//TODO: provide support for different types of subscriptions MODES ? (child added/removed, thesholds, min/max)
    +void PropsChannel::valueChanged(SGPropertyNode* ptr) {
+void PropsChannel::valueChanged(SGPropertyNode* ptr) {
    +  SG_LOG(SG_GENERAL, SG_ALERT, __FILE__<< "@"<<__LINE__ << ":" << __FUNCTION__ << std::endl);  
+  SG_LOG(SG_GENERAL, SG_ALERT, __FILE__<< "@"<<__LINE__ << ":" << __FUNCTION__ << std::endl);  
    +  std::stringstream response;
+  std::stringstream response;
    +  response << ptr->getPath(true) << "=" <<  ptr->getStringValue() << "\r\n"; //TODO: use hashes, echo several properties at once, use proper terminator
+  response << ptr->getPath(true) << "=" <<  ptr->getStringValue() << "\r\n"; //TODO: use hashes, echo several properties at once, use proper terminator
    +  push( response.str().c_str() );
+  push( response.str().c_str() );
    }
}


    /**
/**
    @@ -160,7 +257,7 @@ PropsChannel::foundTerminator()
@@ -160,7 +257,7 @@ PropsChannel::foundTerminator()
        const char* cmd = buffer.getData();
const char* cmd = buffer.getData();
        SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );


    -    vector<string> tokens = simgear::strutils::split( cmd );
-    vector<string> tokens = simgear::strutils::split( cmd );
    +    ParameterList tokens = simgear::strutils::split( cmd );
+    ParameterList tokens = simgear::strutils::split( cmd );


        SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );


    @@ -292,7 +389,7 @@ PropsChannel::foundTerminator()
@@ -292,7 +389,7 @@ PropsChannel::foundTerminator()
                        if ( !globals->get_commands()
if ( !globals->get_commands()
                                  ->execute( "reinit", &args) )
->execute( "reinit", &args) )
                        {
{
    -                        SG_LOG( SG_NETWORK, SG_ALERT,
-                        SG_LOG( SG_NETWORK, SG_ALERT,
    +                        SG_LOG( SG_GENERAL, SG_ALERT,
+                        SG_LOG( SG_GENERAL, SG_ALERT,
                                    "Command " << tokens[1] << " failed.");
"Command " << tokens[1] << " failed.");
                            if ( mode == PROMPT ) {
if ( mode == PROMPT ) {
                                tmp += "*failed*";
tmp += "*failed*";
    @@ -357,7 +454,7 @@ PropsChannel::foundTerminator()
@@ -357,7 +454,7 @@ PropsChannel::foundTerminator()
                        if ( !globals->get_commands()
if ( !globals->get_commands()
                                  ->execute(tokens[1].c_str(), &args) )
->execute(tokens[1].c_str(), &args) )
                        {
{
    -                        SG_LOG( SG_NETWORK, SG_ALERT,
-                        SG_LOG( SG_NETWORK, SG_ALERT,
    +                        SG_LOG( SG_GENERAL, SG_ALERT,
+                        SG_LOG( SG_GENERAL, SG_ALERT,
                                    "Command " << tokens[1] << " failed.");
"Command " << tokens[1] << " failed.");
                            if ( mode == PROMPT ) {
if ( mode == PROMPT ) {
                                tmp += "*failed*";
tmp += "*failed*";
    @@ -386,7 +483,14 @@ PropsChannel::foundTerminator()
@@ -386,7 +483,14 @@ PropsChannel::foundTerminator()
                    mode = DATA;
mode = DATA;
                } else if ( command == "prompt" ) {
} else if ( command == "prompt" ) {
                    mode = PROMPT;
mode = PROMPT;
    -            } else {
-            } else {
    +            } else if (command  == "subscribe" || command == "unsubscribe") {
+            } else if (command  == "subscribe" || command == "unsubscribe") {
    +        TelnetCallback t = callback_map[ command.c_str() ]; //FIXME: use map lookup for condition
+        TelnetCallback t = callback_map[ command.c_str() ]; //FIXME: use map lookup for condition
    +        if (t)
+        if (t)
    +                (this->*t) (tokens);
+                (this->*t) (tokens);
    +        else
+        else
    +            error("No matching callback found for command:"+command);
+            error("No matching callback found for command:"+command);
    +      }
+      }
    +      else {
+      else {
                    const char* msg = "\
const char* msg = "\
    Valid commands are:\r\n\
Valid commands are:\r\n\
    \r\n\
\r\n\
    @@ -400,7 +504,9 @@ prompt            switch to interactive mode (default)\r\n\
@@ -400,7 +504,9 @@ prompt            switch to interactive mode (default)\r\n\
    pwd                display your current path\r\n\
pwd                display your current path\r\n\
    quit              terminate connection\r\n\
quit              terminate connection\r\n\
    run <command>      run built in command\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";
    +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\
+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";
+unscubscribe <id>  unscubscribe from property changes (id must be the property hash as returned from subscribe command)\r\n";
                    push( msg );
push( msg );
                }
}
            }
}
    diff --git a/src/Network/props.hxx b/src/Network/props.hxx
diff --git a/src/Network/props.hxx b/src/Network/props.hxx
    index e55f7c1..09b5a09 100644
index e55f7c1..09b5a09 100644
    --- a/src/Network/props.hxx
--- a/src/Network/props.hxx
    +++ b/src/Network/props.hxx
+++ b/src/Network/props.hxx
    @@ -40,7 +40,8 @@
@@ -40,7 +40,8 @@
      * FlightGear properties.
* FlightGear properties.
      */
*/
    class FGProps : public FGProtocol,
class FGProps : public FGProtocol,
    -      public simgear::NetChannel
-      public simgear::NetChannel
    +      public simgear::NetChannel,
+      public simgear::NetChannel,
    +      public SGPropertyChangeListener // for subscriptions
+      public SGPropertyChangeListener // for subscriptions
    {
{
    private:
private:
</syntaxhighlight>
</syntaxhighlight>


Navigation menu