-// props.hxx -- FGFS property manager interaction class
+// \file props.cxx
+// Property server class.
//
// Written by Curtis Olson, started September 2000.
+// Modified by Bernie Bright, May 2002.
+// Modified by Jean-Paul Anceaux, Dec 2015.
//
-// 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
//
// 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$
-#include <Main/globals.hxx>
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
-#include <simgear/io/iochannel.hxx>
-#include <simgear/math/sg_types.hxx>
-#include <simgear/misc/props.hxx>
+#include <simgear/structure/commands.hxx>
+#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>
+#include <errno.h>
+
+#include <Main/globals.hxx>
+#include <Viewer/viewmgr.hxx>
-#include <stdlib.h> // atoi() atof()
+#include <simgear/io/sg_netChat.hxx>
-#include STL_STRSTREAM
+#include <simgear/misc/strutils.hxx>
#include "props.hxx"
-#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
-SG_USING_STD(cout);
-SG_USING_STD(istrstream);
-#endif
+#include <map>
+#include <vector>
+#include <string>
+
+#include <boost/foreach.hpp>
+
+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 simgear::NetChat, public SGPropertyChangeListener
+{
+ simgear::NetBuffer buffer;
+
+ /**
+ * Current property node name.
+ */
+ string path;
+
+ enum Mode {
+ PROMPT,
+ DATA
+ };
+ Mode mode;
+
+public:
+ /**
+ * Constructor.
+ */
+ PropsChannel();
+ ~PropsChannel();
+
+ /**
+ * Append incoming data to our request buffer.
+ *
+ * @param s Character string to append to buffer
+ * @param n Number of characters to append.
+ */
+ void collectIncomingData( const char* s, int n );
+
+ /**
+ * Process a complete request from the props client.
+ */
+ 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";
+ }
-FGProps::FGProps() {
+ 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<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);
+};
+
+/**
+ *
+ */
+PropsChannel::PropsChannel()
+ : buffer(512),
+ path("/"),
+ mode(PROMPT)
+{
+ setTerminator( "\r\n" );
+ callback_map["subscribe"] = &PropsChannel::subscribe;
+ callback_map["unsubscribe"] = &PropsChannel::unsubscribe;
}
-FGProps::~FGProps() {
+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;
-// open hailing frequencies
-bool FGProps::open() {
- path = "/";
+ std::string command = param[0];
+ const char* p = param[1].c_str();
+ if (!p) return;
- 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_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;
+ }
- SGIOChannel *io = get_io_channel();
+ if (n) {
+ n->addChangeListener( this );
+ _listeners.push_back( n ); // housekeeping, save for deletion in dtor later on
+ } else {
+ error("listener could not be added");
+ }
+}
- if ( ! io->open( get_direction() ) ) {
- SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
- return false;
- }
+void PropsChannel::unsubscribe(const ParameterList ¶m) {
+ if (!check_args(param,1,"unsubscribe")) return;
- set_enabled( true );
- SG_LOG( SG_IO, SG_INFO, "Opening properties channel communication layer." );
+ 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");
+ }
+}
- return true;
+
+//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::collectIncomingData( const char* s, int n )
+{
+ buffer.append( s, n );
+}
// return a human readable form of the value "type"
-static string getValueTypeString( const SGValue *v ) {
+static string
+getValueTypeString( const SGPropertyNode *node )
+{
+ using namespace simgear;
+
string result;
- if ( v == NULL ) {
- return "unknown";
+ if ( node == NULL )
+ {
+ return "unknown";
}
- SGValue::Type type = v->getType();
- if ( type == SGValue::UNKNOWN ) {
- result = "unknown";
- } else if ( type == SGValue::BOOL ) {
- result = "bool";
- } else if ( type == SGValue::INT ) {
- result = "int";
- } else if ( type == SGValue::FLOAT ) {
- result = "float";
- } else if ( type == SGValue::DOUBLE ) {
- result = "double";
- } else if ( type == SGValue::STRING ) {
- result = "string";
+ props::Type type = node->getType();
+ if ( type == props::UNSPECIFIED ) {
+ result = "unspecified";
+ } else if ( type == props::NONE ) {
+ result = "none";
+ } 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;
}
-
-bool FGProps::process_command( const char *cmd ) {
- SGIOChannel *io = get_io_channel();
-
- cout << "processing command = " << cmd;
- string_list tokens;
- tokens.clear();
-
- istrstream in(cmd);
-
- while ( !in.eof() ) {
- string token;
- in >> token;
- tokens.push_back( token );
- }
-
- string command = tokens[0];
-
- SGPropertyNode * node = globals->get_props()->getNode(path);
-
- if ( command == "ls" ) {
- for (int i = 0; i < (int)node->nChildren(); i++) {
- SGPropertyNode * child = node->getChild(i);
- string name = child->getName();
- string line = name;
- if ( child->nChildren() > 0 ) {
- line += "/\n";
- } else {
- string value = node->getStringValue ( name, "" );
- line += " =\t'" + value + "'\t(";
- line += getValueTypeString( node->getValue( name ) );
- line += ")\n";
+/**
+ * We have a command.
+ *
+ */
+void
+PropsChannel::foundTerminator()
+{
+ const char* cmd = buffer.getData();
+ SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
+
+ ParameterList tokens = simgear::strutils::split( cmd );
+
+ SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
+
+ 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() );
+ }
+
+ if (dir == 0) {
+ node_not_found_error( tokens[1] );
+ }
+ }
+
+ 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) {
+ 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 = "/";
+ }
+
+ 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 = 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;
+ 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 ) {
+ // 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() );
+ }
+ 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 += "<completed>";
+ 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 += "<completed>";
+ push( tmp.c_str() );
+ push( getTerminator() );
+ }
+ }
+ } else {
+ if ( mode == PROMPT ) {
+ tmp += "no command specified";
+ push( tmp.c_str() );
+ push( getTerminator() );
+ }
+ }
+ } 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);
}
- io->writestring( line.c_str() );
- }
- } else if ( command == "dump" ) {
- strstream buf;
- if ( tokens.size() <= 1 ) {
- writeProperties ( buf, node);
- io->writestring( buf.str() );
- }
- else {
- SGPropertyNode *child = node->getNode(tokens[1]);
- if ( child ) {
- writeProperties ( buf, child );
- io->writestring( buf.str() );
- } else {
- tokens[1] += " Not Found\n";
- io->writestring( tokens[1].c_str() );
- }
- }
- } else if ( command == "cd" ) {
- // string tmp = "current path = " + node.getPath() + "\n";
- // io->writestring( tmp.c_str() );
-
- if ( tokens.size() <= 1 ) {
- // do nothing
- } else {
- SGPropertyNode *child = node->getNode(tokens[1]);
- if ( child ) {
- node = child;
- path = node->getPath();
- } else {
- tokens[1] += " Not Found\n";
- io->writestring( tokens[1].c_str() );
- }
- }
- } else if ( command == "pwd" ) {
- string ttt = node->getPath();
- if ( ttt == "" ) {
- ttt = "/";
- }
- ttt += "\n";
- io->writestring( ttt.c_str() );
- } else if ( command == "get" || command == "show" ) {
- if ( tokens.size() <= 1 ) {
- // do nothing
- } else {
- string ttt = "debug = '" + tokens[1] + "'\n";
- io->writestring( ttt.c_str() );
-
- string value = node->getStringValue ( tokens[1], "" );
- string tmp = tokens[1] + " = '" + value + "' (";
- tmp += getValueTypeString( node->getValue( tokens[1] ) );
- tmp += ")\n";
-
- io->writestring( tmp.c_str() );
- }
- } else if ( command == "set" ) {
- if ( tokens.size() <= 2 ) {
- // do nothing
- } else {
- node->getValue( tokens[1], true )->setStringValue(tokens[2]);
-
- // now fetch and write out the new value as confirmation
- // of the change
- string value = node->getStringValue ( tokens[1], "" );
- string tmp = tokens[1] + " = '" + value + "' (";
- tmp += getValueTypeString( node->getValue( tokens[1] ) );
- tmp += ")\n";
-
- io->writestring( tmp.c_str() );
- }
- } else if ( command == "quit" ) {
- close();
- } else {
- io->writestring( "\n" );
- io->writestring( "Valid commands are:\n" );
- io->writestring( "\n" );
- io->writestring( "help show help message\n" );
- io->writestring( "ls list current directory\n" );
- io->writestring( "dump dump current state (in xml)\n" );
- io->writestring( "cd <dir> cd to a directory, '..' to move back\n" );
- io->writestring( "pwd display your current path\n" );
- io->writestring( "get <var> show the value of a parameter\n" );
- io->writestring( "show <var> synonym for get\n" );
- io->writestring( "set <var> <val> set <var> to a new <val>\n" );
- io->writestring( "quit terminate connection\n" );
- io->writestring( "\n" );
+ else if ( command == "seti" ) {
+ string value, tmp;
+ if (tokens.size() == 3) {
+ node->getNode( tokens[1].c_str(), true )
+ ->setIntValue(atoi(tokens[2].c_str()));
+ }
+ if ( mode == PROMPT ) {
+ tmp = tokens[1].c_str();
+ tmp += " " + tokens[2];
+ tmp += " (";
+ tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
+ tmp += ")";
+ push( tmp.c_str() );
+ push( getTerminator() );
+ }
+ }
+ else if ( command == "setd" || command == "setf") {
+ string value, tmp;
+ if (tokens.size() == 3) {
+ node->getNode( tokens[1].c_str(), true )
+ ->setDoubleValue(atof(tokens[2].c_str()));
+ }
+ if ( mode == PROMPT ) {
+ tmp = tokens[1].c_str();
+ tmp += " ";
+ tmp += tokens[2].c_str();
+ tmp += " (";
+ tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
+ tmp += ")";
+ push( tmp.c_str() );
+ push( getTerminator() );
+ }
+ }
+ else if ( command == "setb" ) {
+ string tmp, value;
+ if (tokens.size() == 3) {
+ if (tokens[2] == "false" || tokens[2] == "0") {
+ node->getNode( tokens[1].c_str(), true )
+ ->setBoolValue(false);
+ value = " False ";
+ }
+ if (tokens[2] == "true" || tokens[2] == "1"){
+ node->getNode( tokens[1].c_str(), true )
+ ->setBoolValue(true);
+ value = " True ";
+ }
+ if ( mode == PROMPT ) {
+ tmp = tokens[1].c_str();
+ tmp += value;
+ tmp += " (";
+ tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
+ tmp += ")";
+ push( tmp.c_str() );
+ push( getTerminator() );
+ }
+
+ }
+ }
+ else if ( command == "del" ) {
+ string tmp;
+ if (tokens.size() == 3){
+ node->getNode( tokens[1].c_str(), true )->removeChild(tokens[2].c_str(),0);
+ }
+ if ( mode == PROMPT ) {
+ tmp = "Delete ";
+ tmp += tokens[1].c_str();
+ tmp += tokens[2];
+ push( tmp.c_str() );
+ push( getTerminator() );
+ }
+ }
+ else {
+ const char* msg = "\
+Valid commands are:\r\n\
+\r\n\
+cd <dir> cd to a directory, '..' to move back\r\n\
+data switch to raw data mode\r\n\
+dump dump current state (in xml)\r\n\
+get <var> show the value of a parameter\r\n\
+help show this help message\r\n\
+ls [<dir>] list directory\r\n\
+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 String <var> to a new <val>\r\n\
+setb <var> <val> set Bool <var> to a new <val> only work with the foling value 0, 1, true, false\r\n\
+setd <var> <val> set Double <var> to a new <val>\r\n\
+setf <var> <val> alias for setd\r\n\
+seti <var> <val> set Int <var> to a new <val>\r\n\
+del <var> <nod> delete <nod> in <var>\r\n\
+subscribe <var> subscribe to property changes \r\n\
+unscubscribe <var> 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() );
}
- string prompt = node->getPath();
- if ( prompt == "" ) {
- prompt = "/";
+ if ( mode == PROMPT ) {
+ string prompt = node->getPath();
+ if (prompt.empty()) {
+ prompt = "/";
+ }
+ prompt += "> ";
+ push( prompt.c_str() );
}
- prompt += "> ";
- io->writestring( prompt.c_str() );
- return true;
+ buffer.remove();
}
-
-// process work for this port
-bool FGProps::process() {
- SGIOChannel *io = get_io_channel();
-
- // cout << "processing incoming props command" << endl;
-
- if ( get_direction() == SG_IO_BI ) {
- // cout << " (bi directional)" << endl;
- while ( io->readline( buf, max_cmd_len ) > 0 ) {
- SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
- process_command( buf );
- }
+/**
+ *
+ */
+FGProps::FGProps( const vector<string>& tokens )
+{
+ // tokens:
+ // props,port#
+ // props,medium,direction,hz,hostname,port#,style
+ if (tokens.size() == 2) {
+ port = atoi( tokens[1].c_str() );
+ set_hz( 5 ); // default to processing requests @ 5Hz
+ } else if (tokens.size() == 7) {
+ char* endptr;
+ errno = 0;
+ int hz = strtol( tokens[3].c_str(), &endptr, 10 );
+ if (errno != 0) {
+ SG_LOG( SG_IO, SG_ALERT, "I/O poll frequency out of range" );
+ set_hz( 5 ); // default to processing requests @ 5Hz
+ } else {
+ SG_LOG( SG_IO, SG_INFO, "Setting I/O poll frequency to "
+ << hz << " Hz");
+ set_hz( hz );
+ }
+ port = atoi( tokens[5].c_str() );
} else {
- SG_LOG( SG_IO, SG_ALERT,
- "in or out direction not supported for FGProps." );
+ throw FGProtocolConfigError( "FGProps: incorrect number of configuration arguments" );
}
+}
- return true;
+/**
+ *
+ */
+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;
+ }
-// close the channel
-bool FGProps::close() {
- SGIOChannel *io = get_io_channel();
+ if (!simgear::NetChannel::open())
+ {
+ SG_LOG( SG_IO, SG_ALERT, "FGProps: Failed to open network socket.");
+ return false;
+ }
- set_enabled( 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;
+ }
- if ( ! io->close() ) {
- 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;
}
- cout << "successfully closed channel\n";
+ poller.addChannel(this);
+
+ SG_LOG( SG_IO, SG_INFO, "Props server started on port " << port );
+
+ set_enabled( true );
+ return true;
+}
+
+/**
+ *
+ */
+bool
+FGProps::close()
+{
+ SG_LOG( SG_IO, SG_INFO, "closing FGProps" );
+ return true;
+}
+/**
+ *
+ */
+bool
+FGProps::process()
+{
+ poller.poll();
return true;
}
+
+/**
+ *
+ */
+void
+FGProps::handleAccept()
+{
+ simgear::IPAddress addr;
+ int handle = accept( &addr );
+ SG_LOG( SG_IO, SG_INFO, "Props server accepted connection from "
+ << addr.getHost() << ":" << addr.getPort() );
+ PropsChannel* channel = new PropsChannel();
+ channel->setHandle( handle );
+ poller.addChannel( channel );
+}