Bernie Bright:
Here is the new super improved telnet property interface. CVS changelog is at the end of this message. Once this new telnet code is in and compiles every where we can remove Network/props.[ch]xx. I've added a --telnet=<port> command line option to invoke the new server. Later on we could remove the --props option, or least change it to invoke the new server. I'll let you decide. I've added some new commands to the telnet interface: view next Select the next view. view prev Select the previous view. view set <n> Select view 'n' view get Return index of current view I'm not sure if these same effects could be achieved through property operations. The commands provide a convenient shortcut in any case. I'm also planning on adding a panel command to manipulate panels and objects contained therein (eg simulated mouse clicks). There is going to be some commonality with the command objects so we may need to rationalize this in the near future. Finally, I've also included my python stuff. This is still very much a work in progress, basically I've been using it to test the new telnet server. I have tested it with python 2.2. Feel free to add it to the repository if you want. I would suggest a scripts/python directory CVS Changelog Network/telnet.cxx: New property telnet protocol interface. It supports the same user interface provided by the --props server. Additionally it handles multiple simultaneous connections. Added "view" command to manipulate viewmgr. Network/protocol.hxx: Added protocol configuration exception. Main/fg_io.cxx: Added new "telnet" protocol. Added protocol configuration parse exceptions. Simplified protocol configuration parsing. Main/options.cxx: Added --telnet=<port> command line option and help message.
This commit is contained in:
parent
52c1cb2f7d
commit
f9f05aa870
7 changed files with 772 additions and 170 deletions
|
@ -35,6 +35,7 @@
|
|||
#include <simgear/io/sg_socket_udp.hxx>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <Network/protocol.hxx>
|
||||
#include <Network/atc610x.hxx>
|
||||
|
@ -51,6 +52,7 @@
|
|||
#include <Network/opengc.hxx>
|
||||
#include <Network/nmea.hxx>
|
||||
#include <Network/props.hxx>
|
||||
#include <Network/telnet.hxx>
|
||||
#include <Network/pve.hxx>
|
||||
#include <Network/ray.hxx>
|
||||
#include <Network/rul.hxx>
|
||||
|
@ -68,172 +70,126 @@ io_container global_io_list;
|
|||
// configure a port based on the config string
|
||||
static FGProtocol *parse_port_config( const string& config )
|
||||
{
|
||||
bool short_circuit = false;
|
||||
|
||||
string::size_type begin, end;
|
||||
|
||||
begin = 0;
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, "Parse I/O channel request: " << config );
|
||||
|
||||
// determine protocol
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL; // dummy
|
||||
vector<string> tokens = simgear::strutils::split( config, "," );
|
||||
if (tokens.empty())
|
||||
{
|
||||
SG_LOG( SG_IO, SG_ALERT,
|
||||
"Port configuration error: empty config string" );
|
||||
return 0;
|
||||
}
|
||||
|
||||
string protocol = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
string protocol = tokens[0];
|
||||
SG_LOG( SG_IO, SG_INFO, " protocol = " << protocol );
|
||||
|
||||
FGProtocol *io;
|
||||
if ( protocol == "atc610x" ) {
|
||||
FGATC610x *atc610x = new FGATC610x;
|
||||
io = atc610x;
|
||||
short_circuit = true;
|
||||
cout << "here ..." << endl;
|
||||
} else if ( protocol == "atlas" ) {
|
||||
FGAtlas *atlas = new FGAtlas;
|
||||
io = atlas;
|
||||
} else if ( protocol == "opengc" ) {
|
||||
// char wait;
|
||||
// printf("Parsed opengc\n"); cin >> wait;
|
||||
FGOpenGC *opengc = new FGOpenGC;
|
||||
io = opengc;
|
||||
} else if ( protocol == "garmin" ) {
|
||||
FGGarmin *garmin = new FGGarmin;
|
||||
io = garmin;
|
||||
} else if ( protocol == "httpd" ) {
|
||||
// determine port
|
||||
string port = config.substr(begin);
|
||||
FGHttpd *httpd = new FGHttpd( atoi(port.c_str()) );
|
||||
io = httpd;
|
||||
short_circuit = true;
|
||||
FGProtocol *io = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if ( protocol == "atc610x" ) {
|
||||
return new FGATC610x;
|
||||
} else if ( protocol == "atlas" ) {
|
||||
FGAtlas *atlas = new FGAtlas;
|
||||
io = atlas;
|
||||
} else if ( protocol == "opengc" ) {
|
||||
// char wait;
|
||||
// printf("Parsed opengc\n"); cin >> wait;
|
||||
FGOpenGC *opengc = new FGOpenGC;
|
||||
io = opengc;
|
||||
} else if ( protocol == "garmin" ) {
|
||||
FGGarmin *garmin = new FGGarmin;
|
||||
io = garmin;
|
||||
} else if ( protocol == "httpd" ) {
|
||||
// determine port
|
||||
string port = tokens[1];
|
||||
return new FGHttpd( atoi(port.c_str()) );
|
||||
#ifdef FG_JPEG_SERVER
|
||||
} else if ( protocol == "jpg-httpd" ) {
|
||||
// determine port
|
||||
string port = config.substr(begin);
|
||||
FGJpegHttpd *jpeg_httpd = new FGJpegHttpd( atoi(port.c_str()) );
|
||||
io = jpeg_httpd;
|
||||
short_circuit = true;
|
||||
} else if ( protocol == "jpg-httpd" ) {
|
||||
// determine port
|
||||
string port = tokens[1];
|
||||
return new FGJpegHttpd( atoi(port.c_str()) );
|
||||
#endif
|
||||
} else if ( protocol == "joyclient" ) {
|
||||
FGJoyClient *joyclient = new FGJoyClient;
|
||||
io = joyclient;
|
||||
} else if ( protocol == "native" ) {
|
||||
FGNative *native = new FGNative;
|
||||
io = native;
|
||||
} else if ( protocol == "native_ctrls" ) {
|
||||
FGNativeCtrls *native_ctrls = new FGNativeCtrls;
|
||||
io = native_ctrls;
|
||||
} else if ( protocol == "native_fdm" ) {
|
||||
FGNativeFDM *native_fdm = new FGNativeFDM;
|
||||
io = native_fdm;
|
||||
} else if ( protocol == "nmea" ) {
|
||||
FGNMEA *nmea = new FGNMEA;
|
||||
io = nmea;
|
||||
} else if ( protocol == "props" ) {
|
||||
FGProps *props = new FGProps;
|
||||
io = props;
|
||||
} else if ( protocol == "pve" ) {
|
||||
FGPVE *pve = new FGPVE;
|
||||
io = pve;
|
||||
} else if ( protocol == "ray" ) {
|
||||
FGRAY *ray = new FGRAY;
|
||||
io = ray;
|
||||
} else if ( protocol == "rul" ) {
|
||||
FGRUL *rul = new FGRUL;
|
||||
io = rul;
|
||||
} else {
|
||||
return NULL;
|
||||
} else if ( protocol == "joyclient" ) {
|
||||
FGJoyClient *joyclient = new FGJoyClient;
|
||||
io = joyclient;
|
||||
} else if ( protocol == "native" ) {
|
||||
FGNative *native = new FGNative;
|
||||
io = native;
|
||||
} else if ( protocol == "native_ctrls" ) {
|
||||
FGNativeCtrls *native_ctrls = new FGNativeCtrls;
|
||||
io = native_ctrls;
|
||||
} else if ( protocol == "native_fdm" ) {
|
||||
FGNativeFDM *native_fdm = new FGNativeFDM;
|
||||
io = native_fdm;
|
||||
} else if ( protocol == "nmea" ) {
|
||||
FGNMEA *nmea = new FGNMEA;
|
||||
io = nmea;
|
||||
} else if ( protocol == "props" ) {
|
||||
io = new FGProps();
|
||||
} else if ( protocol == "telnet" ) {
|
||||
io = new FGTelnet( tokens );
|
||||
return io;
|
||||
} else if ( protocol == "pve" ) {
|
||||
FGPVE *pve = new FGPVE;
|
||||
io = pve;
|
||||
} else if ( protocol == "ray" ) {
|
||||
FGRAY *ray = new FGRAY;
|
||||
io = ray;
|
||||
} else if ( protocol == "rul" ) {
|
||||
FGRUL *rul = new FGRUL;
|
||||
io = rul;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
catch (FGProtocolConfigError& err)
|
||||
{
|
||||
SG_LOG( SG_IO, SG_ALERT, "Port configuration error: " << err.what() );
|
||||
delete io;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( ! short_circuit ) {
|
||||
// determine medium
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL; // dummy
|
||||
}
|
||||
|
||||
string medium = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " medium = " << medium );
|
||||
string medium = tokens[1];
|
||||
SG_LOG( SG_IO, SG_INFO, " medium = " << medium );
|
||||
|
||||
// determine direction
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL; // dummy
|
||||
}
|
||||
|
||||
string direction = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
io->set_direction( direction );
|
||||
SG_LOG( SG_IO, SG_INFO, " direction = " << direction );
|
||||
string direction = tokens[2];
|
||||
io->set_direction( direction );
|
||||
SG_LOG( SG_IO, SG_INFO, " direction = " << direction );
|
||||
|
||||
// determine hertz
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL; // dummy
|
||||
}
|
||||
|
||||
string hertz_str = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
double hertz = atof( hertz_str.c_str() );
|
||||
io->set_hz( hertz );
|
||||
SG_LOG( SG_IO, SG_INFO, " hertz = " << hertz );
|
||||
string hertz_str = tokens[3];
|
||||
double hertz = atof( hertz_str.c_str() );
|
||||
io->set_hz( hertz );
|
||||
SG_LOG( SG_IO, SG_INFO, " hertz = " << hertz );
|
||||
|
||||
if ( medium == "serial" ) {
|
||||
// device name
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string device = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " device = " << device );
|
||||
if ( medium == "serial" ) {
|
||||
// device name
|
||||
string device = tokens[4];
|
||||
SG_LOG( SG_IO, SG_INFO, " device = " << device );
|
||||
|
||||
// baud
|
||||
string baud = config.substr(begin);
|
||||
SG_LOG( SG_IO, SG_INFO, " baud = " << baud );
|
||||
// baud
|
||||
string baud = tokens[5];
|
||||
SG_LOG( SG_IO, SG_INFO, " baud = " << baud );
|
||||
|
||||
SGSerial *ch = new SGSerial( device, baud );
|
||||
io->set_io_channel( ch );
|
||||
} else if ( medium == "file" ) {
|
||||
// file name
|
||||
string file = config.substr(begin);
|
||||
SG_LOG( SG_IO, SG_INFO, " file name = " << file );
|
||||
SGSerial *ch = new SGSerial( device, baud );
|
||||
io->set_io_channel( ch );
|
||||
} else if ( medium == "file" ) {
|
||||
// file name
|
||||
string file = tokens[4];
|
||||
SG_LOG( SG_IO, SG_INFO, " file name = " << file );
|
||||
|
||||
SGFile *ch = new SGFile( file );
|
||||
io->set_io_channel( ch );
|
||||
} else if ( medium == "socket" ) {
|
||||
// hostname
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string hostname = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " hostname = " << hostname );
|
||||
SGFile *ch = new SGFile( file );
|
||||
io->set_io_channel( ch );
|
||||
} else if ( medium == "socket" ) {
|
||||
string hostname = tokens[4];
|
||||
string port = tokens[5];
|
||||
string style = tokens[6];
|
||||
|
||||
// port string
|
||||
end = config.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string port = config.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " port string = " << port );
|
||||
|
||||
// socket style
|
||||
string style_str = config.substr(begin);
|
||||
SG_LOG( SG_IO, SG_INFO, " style string = " << style_str );
|
||||
SG_LOG( SG_IO, SG_INFO, " hostname = " << hostname );
|
||||
SG_LOG( SG_IO, SG_INFO, " port = " << port );
|
||||
SG_LOG( SG_IO, SG_INFO, " style = " << style );
|
||||
|
||||
SGSocket *ch = new SGSocket( hostname, port, style_str );
|
||||
io->set_io_channel( ch );
|
||||
}
|
||||
io->set_io_channel( new SGSocket( hostname, port, style ) );
|
||||
}
|
||||
|
||||
return io;
|
||||
|
@ -273,8 +229,6 @@ void fgIOInit() {
|
|||
|
||||
// process any serial port work
|
||||
void fgIOProcess() {
|
||||
FGProtocol *p;
|
||||
|
||||
// cout << "processing I/O channels" << endl;
|
||||
|
||||
static int inited = 0;
|
||||
|
@ -292,9 +246,9 @@ void fgIOProcess() {
|
|||
last = current;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < (int)global_io_list.size(); ++i ) {
|
||||
for ( unsigned int i = 0; i < global_io_list.size(); ++i ) {
|
||||
// cout << " channel = " << i << endl;
|
||||
p = global_io_list[i];
|
||||
FGProtocol* p = global_io_list[i];
|
||||
|
||||
if ( p->is_enabled() ) {
|
||||
p->dec_count_down( interval );
|
||||
|
|
|
@ -1332,7 +1332,8 @@ int mainLoop( int argc, char **argv ) {
|
|||
version = "unknown version";
|
||||
#endif
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "FlightGear: Version "
|
||||
<< version << endl );
|
||||
<< version );
|
||||
SG_LOG( SG_GENERAL, SG_INFO, "Built with " << SG_COMPILER_STR );
|
||||
|
||||
// Allocate global data structures. This needs to happen before
|
||||
// we parse command line options
|
||||
|
|
|
@ -24,7 +24,8 @@ libNetwork_a_SOURCES = \
|
|||
pve.cxx pve.hxx \
|
||||
raw_ctrls.hxx \
|
||||
ray.cxx ray.hxx \
|
||||
rul.cxx rul.hxx
|
||||
rul.cxx rul.hxx \
|
||||
telnet.cxx telnet.hxx
|
||||
|
||||
if OLD_AUTOMAKE
|
||||
INCLUDES += -I$(top_srcdir) -I$(top_srcdir)/src
|
||||
|
|
|
@ -95,3 +95,14 @@ bool FGProtocol::parse_message() {
|
|||
}
|
||||
|
||||
|
||||
void FGProtocol::set_direction( const string& d ) {
|
||||
if ( d == "in" ) {
|
||||
dir = SG_IO_IN;
|
||||
} else if ( d == "out" ) {
|
||||
dir = SG_IO_OUT;
|
||||
} else if ( d == "bi" ) {
|
||||
dir = SG_IO_BI;
|
||||
} else {
|
||||
dir = SG_IO_NONE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
|
||||
#include STL_STRING
|
||||
#include <vector>
|
||||
|
@ -36,10 +37,6 @@ SG_USING_STD(vector);
|
|||
|
||||
#define FG_MAX_MSG_SIZE 16384
|
||||
|
||||
// forward declaration
|
||||
class SGIOChannel;
|
||||
|
||||
|
||||
class FGProtocol {
|
||||
|
||||
private:
|
||||
|
@ -68,17 +65,7 @@ public:
|
|||
virtual bool close();
|
||||
|
||||
inline SGProtocolDir get_direction() const { return dir; }
|
||||
inline void set_direction( const string& d ) {
|
||||
if ( d == "in" ) {
|
||||
dir = SG_IO_IN;
|
||||
} else if ( d == "out" ) {
|
||||
dir = SG_IO_OUT;
|
||||
} else if ( d == "bi" ) {
|
||||
dir = SG_IO_BI;
|
||||
} else {
|
||||
dir = SG_IO_NONE;
|
||||
}
|
||||
}
|
||||
void set_direction( const string& d );
|
||||
|
||||
inline double get_hz() const { return hz; }
|
||||
inline void set_hz( double t ) { hz = t; }
|
||||
|
@ -108,6 +95,18 @@ typedef vector < FGProtocol * > io_container;
|
|||
typedef io_container::iterator io_iterator;
|
||||
typedef io_container::const_iterator const_io_iterator;
|
||||
|
||||
#include <stdexcept>
|
||||
SG_USING_STD(invalid_argument);
|
||||
|
||||
//namespace flightgear { namespace network {
|
||||
class FGProtocolConfigError : public invalid_argument
|
||||
{
|
||||
public:
|
||||
FGProtocolConfigError( const string& what_string )
|
||||
: invalid_argument(what_string) {}
|
||||
};
|
||||
//}} // end namespace flightgear::network
|
||||
|
||||
|
||||
#endif // _PROTOCOL_HXX
|
||||
|
||||
|
|
547
src/Network/telnet.cxx
Normal file
547
src/Network/telnet.cxx
Normal file
|
@ -0,0 +1,547 @@
|
|||
// \file telnet.cx
|
||||
// Property telnet server class.
|
||||
//
|
||||
// Written by Bernie Bright, started May 2002.
|
||||
//
|
||||
// Copyright (C) 2002 Bernie Bright - bbright@bigpond.net.au
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
#include <simgear/misc/props.hxx>
|
||||
#include <simgear/misc/props_io.hxx>
|
||||
|
||||
#include STL_STRSTREAM
|
||||
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/viewmgr.hxx>
|
||||
|
||||
#include <plib/netChat.h>
|
||||
|
||||
#include "telnet.hxx"
|
||||
|
||||
#if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
|
||||
SG_USING_STD(strstream);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Telnet connection class.
|
||||
* This class represents a connection to a telnet-style client.
|
||||
*/
|
||||
class TelnetChannel : public netChat
|
||||
{
|
||||
netBuffer buffer;
|
||||
|
||||
/**
|
||||
* Current property node name.
|
||||
*/
|
||||
string path;
|
||||
|
||||
enum Mode {
|
||||
PROMPT,
|
||||
DATA
|
||||
};
|
||||
Mode mode;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
TelnetChannel();
|
||||
|
||||
/**
|
||||
* 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 telnet client.
|
||||
*/
|
||||
void foundTerminator();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Return a "Node no found" error message to the client.
|
||||
*/
|
||||
void node_not_found_error( const string& node_name );
|
||||
|
||||
void view_cmd( const vector<string>& );
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
TelnetChannel::TelnetChannel()
|
||||
: buffer(512),
|
||||
path("/"),
|
||||
mode(PROMPT)
|
||||
{
|
||||
setTerminator( "\r\n" );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
TelnetChannel::collectIncomingData( const char* s, int n )
|
||||
{
|
||||
buffer.append( s, n );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
TelnetChannel::node_not_found_error( const string& node_name )
|
||||
{
|
||||
string error = "ERR Node \"";
|
||||
error += node_name;
|
||||
error += "\" not found.";
|
||||
push( error.c_str() );
|
||||
push( getTerminator() );
|
||||
}
|
||||
|
||||
// return a human readable form of the value "type"
|
||||
static string
|
||||
getValueTypeString( const SGPropertyNode *node )
|
||||
{
|
||||
string result;
|
||||
|
||||
if ( node == NULL )
|
||||
{
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
SGPropertyNode::Type type = node->getType();
|
||||
if ( type == SGPropertyNode::UNSPECIFIED ) {
|
||||
result = "unspecified";
|
||||
} else if ( type == SGPropertyNode::NONE ) {
|
||||
result = "none";
|
||||
} else if ( type == SGPropertyNode::BOOL ) {
|
||||
result = "bool";
|
||||
} else if ( type == SGPropertyNode::INT ) {
|
||||
result = "int";
|
||||
} else if ( type == SGPropertyNode::LONG ) {
|
||||
result = "long";
|
||||
} else if ( type == SGPropertyNode::FLOAT ) {
|
||||
result = "float";
|
||||
} else if ( type == SGPropertyNode::DOUBLE ) {
|
||||
result = "double";
|
||||
} else if ( type == SGPropertyNode::STRING ) {
|
||||
result = "string";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have a command.
|
||||
*
|
||||
* TODO: possible future commands:
|
||||
* panel <subcmd>
|
||||
* panel load [path]
|
||||
* panel mouse <button> up|down|click <x> <y>
|
||||
* panel visible 0|1
|
||||
* panel height -> h, Retrieve panel height
|
||||
* panel width -> w, Retrieve panel width
|
||||
* panel xoffset -> x, Retrieve panel x offset
|
||||
* panel yoffset -> y, Retrieve panel y offset
|
||||
*
|
||||
* property <subcmd>
|
||||
* property toggle <prop>
|
||||
* property adjust <prop> <step> <offset> <factor> <min> <max> <wrap>
|
||||
* property multiply <prop> <factor>
|
||||
* property swap <prop1> <prop2>
|
||||
* property scale <prop> <setting> <offset> <factor>
|
||||
*
|
||||
* view <subcmd>
|
||||
* view next
|
||||
* view prev
|
||||
* view set <n>
|
||||
* view current -> n, Retrieve index of current view
|
||||
*/
|
||||
void
|
||||
TelnetChannel::foundTerminator()
|
||||
{
|
||||
const char* cmd = buffer.getData();
|
||||
SG_LOG( SG_IO, SG_INFO, "processing command = \"" << cmd << "\"" );
|
||||
|
||||
vector<string> tokens = simgear::strutils::split( cmd );
|
||||
|
||||
SGPropertyNode* node = globals->get_props()->getNode( path.c_str() );
|
||||
|
||||
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] );
|
||||
goto prompt;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < dir->nChildren(); i++)
|
||||
{
|
||||
SGPropertyNode * child = dir->getChild(i);
|
||||
string name = child->getName();
|
||||
string line = name;
|
||||
|
||||
if (dir->getChild( name.c_str(), 1 ))
|
||||
{
|
||||
char buf[16];
|
||||
sprintf(buf, "[%d]", child->getIndex());
|
||||
line += buf;
|
||||
}
|
||||
|
||||
if ( child->nChildren() > 0 )
|
||||
{
|
||||
line += "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mode == PROMPT)
|
||||
{
|
||||
string value = dir->getStringValue( name.c_str(), "" );
|
||||
line += " =\t'" + value + "'\t(";
|
||||
line += getValueTypeString(
|
||||
dir->getNode( name.c_str() ) );
|
||||
line += ")";
|
||||
}
|
||||
}
|
||||
|
||||
line += getTerminator();
|
||||
push( line.c_str() );
|
||||
}
|
||||
}
|
||||
else if ( command == "dump" )
|
||||
{
|
||||
strstream buf;
|
||||
if ( tokens.size() <= 1 )
|
||||
{
|
||||
writeProperties( buf, node );
|
||||
push( buf.str() );
|
||||
push( getTerminator() );
|
||||
}
|
||||
else
|
||||
{
|
||||
SGPropertyNode *child = node->getNode( tokens[1].c_str() );
|
||||
if ( child )
|
||||
{
|
||||
writeProperties ( buf, child );
|
||||
push( buf.str() );
|
||||
push( getTerminator() );
|
||||
}
|
||||
else
|
||||
{
|
||||
node_not_found_error( tokens[1] );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( command == "cd" )
|
||||
{
|
||||
if (tokens.size() == 2)
|
||||
{
|
||||
try
|
||||
{
|
||||
SGPropertyNode* child = node->getNode( tokens[1].c_str() );
|
||||
if ( child )
|
||||
{
|
||||
node = child;
|
||||
path = node->getPath();
|
||||
}
|
||||
else
|
||||
{
|
||||
node_not_found_error( tokens[1] );
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Ignore attempt to move past root node with ".."
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( command == "pwd" )
|
||||
{
|
||||
string ttt = node->getPath();
|
||||
if (ttt.empty())
|
||||
{
|
||||
ttt = "/";
|
||||
}
|
||||
|
||||
push( ttt.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 += ")\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = value + "\n";
|
||||
}
|
||||
push( tmp.c_str() );
|
||||
}
|
||||
}
|
||||
else if ( command == "set" )
|
||||
{
|
||||
if ( tokens.size() == 3 )
|
||||
{
|
||||
node->getNode( tokens[1].c_str(), true )->setStringValue(tokens[2].c_str());
|
||||
|
||||
if ( mode == PROMPT )
|
||||
{
|
||||
// now fetch and write out the new value as confirmation
|
||||
// of the change
|
||||
string value = node->getStringValue ( tokens[1].c_str(), "" );
|
||||
string tmp = tokens[1] + " = '" + value + "' (";
|
||||
tmp += getValueTypeString( node->getNode( tokens[1].c_str() ) );
|
||||
tmp += ")\n";
|
||||
push( tmp.c_str() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (command == "quit")
|
||||
{
|
||||
close();
|
||||
shouldDelete();
|
||||
return;
|
||||
}
|
||||
else if ( command == "data" )
|
||||
{
|
||||
mode = DATA;
|
||||
}
|
||||
else if ( command == "prompt" )
|
||||
{
|
||||
mode = PROMPT;
|
||||
}
|
||||
else if ( command == "view" )
|
||||
{
|
||||
view_cmd( tokens );
|
||||
}
|
||||
// else if ( command == "panel" )
|
||||
// {
|
||||
// panel_cmd( tokens );
|
||||
// }
|
||||
// else if ( command == "property" )
|
||||
// {
|
||||
// property_cmd( tokens );
|
||||
// }
|
||||
else
|
||||
{
|
||||
const char* msg = "
|
||||
Valid commands are:
|
||||
|
||||
cd <dir> cd to a directory, '..' to move back
|
||||
data switch to raw data mode
|
||||
dump dump current state (in xml)
|
||||
get <var> show the value of a parameter
|
||||
help show this help message
|
||||
ls [<dir>] list directory
|
||||
prompt switch to interactive mode (default)
|
||||
pwd display your current path
|
||||
quit terminate connection
|
||||
set <var> <val> set <var> to a new <val>
|
||||
show <var> synonym for get
|
||||
view next display next view
|
||||
view prev display prev view
|
||||
view set <n> display view 'n'
|
||||
view get return current view index
|
||||
view current return current view index
|
||||
";
|
||||
push( msg );
|
||||
}
|
||||
}
|
||||
|
||||
prompt:
|
||||
if (mode == PROMPT)
|
||||
{
|
||||
string prompt = node->getPath();
|
||||
if (prompt.empty())
|
||||
{
|
||||
prompt = "/";
|
||||
}
|
||||
prompt += "> ";
|
||||
push( prompt.c_str() );
|
||||
}
|
||||
|
||||
buffer.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
TelnetChannel::view_cmd( const vector<string>& tokens )
|
||||
{
|
||||
if (tokens.size() <= 1)
|
||||
{
|
||||
// ERROR: no sub-command
|
||||
return;
|
||||
}
|
||||
string subcmd = tokens[1];
|
||||
|
||||
if (subcmd == "next")
|
||||
{
|
||||
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
||||
globals->get_viewmgr()->next_view();
|
||||
}
|
||||
else if (subcmd == "prev")
|
||||
{
|
||||
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
||||
globals->get_viewmgr()->prev_view();
|
||||
}
|
||||
else if (subcmd == "set")
|
||||
{
|
||||
if (tokens.size() == 3)
|
||||
{
|
||||
int i = atoi( tokens[2].c_str() );
|
||||
if (0 >= i && i < globals->get_viewmgr()->size())
|
||||
{
|
||||
globals->get_current_view()->setHeadingOffset_deg(0.0);
|
||||
globals->get_viewmgr()->set_view(i);
|
||||
globals->get_viewmgr()->copyToCurrent();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (subcmd == "get" || subcmd == "current")
|
||||
{
|
||||
int i = globals->get_viewmgr()->get_current();
|
||||
char buf[16];
|
||||
snprintf( buf, sizeof(buf), "%d", i );
|
||||
push( buf );
|
||||
push( getTerminator() );
|
||||
}
|
||||
else
|
||||
{
|
||||
// ERROR: invalid subcommand.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
FGTelnet::FGTelnet( const vector<string>& tokens )
|
||||
{
|
||||
if (tokens.size() != 2)
|
||||
{
|
||||
throw FGProtocolConfigError( "FGProps: expected 1 argument, <port>" );
|
||||
}
|
||||
|
||||
port = atoi( tokens[1].c_str() );
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
FGTelnet::~FGTelnet()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool
|
||||
FGTelnet::open()
|
||||
{
|
||||
if ( is_enabled() )
|
||||
{
|
||||
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||
<< "is already in use, ignoring" );
|
||||
return false;
|
||||
}
|
||||
|
||||
netChannel::open();
|
||||
netChannel::bind( "", port );
|
||||
netChannel::listen( 5 );
|
||||
SG_LOG( SG_IO, SG_INFO, "Telnet server started on port " << port );
|
||||
|
||||
set_hz( 5 ); // default to processing requests @ 5Hz
|
||||
set_enabled( true );
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool
|
||||
FGTelnet::close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool
|
||||
FGTelnet::process()
|
||||
{
|
||||
netChannel::poll();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
void
|
||||
FGTelnet::handleAccept()
|
||||
{
|
||||
netAddress addr;
|
||||
int handle = accept( &addr );
|
||||
SG_LOG( SG_IO, SG_INFO, "Telnet server accepted connection from "
|
||||
<< addr.getHost() << ":" << addr.getPort() );
|
||||
TelnetChannel* channel = new TelnetChannel();
|
||||
channel->setHandle( handle );
|
||||
}
|
89
src/Network/telnet.hxx
Normal file
89
src/Network/telnet.hxx
Normal file
|
@ -0,0 +1,89 @@
|
|||
// \file telnet.hxx
|
||||
// Property server class.
|
||||
//
|
||||
// Written by Bernie Bright, started May 2002.
|
||||
//
|
||||
// Copyright (C) 2002 Bernie Bright - bbright@bigpond.net.au
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef TELNET_HXX_INCLUDED
|
||||
#define TELNET_HXX_INCLUDED 1
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include STL_STRING
|
||||
#include <vector>
|
||||
|
||||
SG_USING_STD(string);
|
||||
SG_USING_STD(vector);
|
||||
|
||||
#include <plib/netChannel.h>
|
||||
|
||||
#include "protocol.hxx"
|
||||
|
||||
/**
|
||||
* Property server class.
|
||||
* This class provides a telnet-like server for remote access to
|
||||
* FlightGear properties.
|
||||
*/
|
||||
class FGTelnet : public FGProtocol,
|
||||
public netChannel
|
||||
{
|
||||
private:
|
||||
|
||||
/**
|
||||
* Server port to listen on.
|
||||
*/
|
||||
int port;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new TCP server.
|
||||
*
|
||||
* @param tokens Tokenized configuration parameters
|
||||
*/
|
||||
FGTelnet( const vector<string>& tokens );
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~FGTelnet();
|
||||
|
||||
/**
|
||||
* Start the telnet server.
|
||||
*/
|
||||
bool open();
|
||||
|
||||
/**
|
||||
* Process network activity.
|
||||
*/
|
||||
bool process();
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
bool close();
|
||||
|
||||
/**
|
||||
* Accept a new client connection.
|
||||
*/
|
||||
void handleAccept();
|
||||
|
||||
};
|
||||
|
||||
#endif //TELNET_HXX_INCLUDED
|
||||
|
Loading…
Reference in a new issue