From b8ac6687196c80e447efbcda04e07180fd799b10 Mon Sep 17 00:00:00 2001 From: ehofman Date: Tue, 10 Feb 2004 15:35:48 +0000 Subject: [PATCH] Add some example remote controll scripts --- scripts/example/.cvsignore | 2 + scripts/example/Makefile.am | 1 + scripts/example/fgfsclient.c | 151 +++++++++++++++++++++++++++ scripts/example/fgfsclient.cxx | 181 +++++++++++++++++++++++++++++++++ scripts/example/fgfsscript | 95 +++++++++++++++++ scripts/example/remote.html | 133 ++++++++++++++++++++++++ 6 files changed, 563 insertions(+) create mode 100644 scripts/example/.cvsignore create mode 100644 scripts/example/Makefile.am create mode 100644 scripts/example/fgfsclient.c create mode 100644 scripts/example/fgfsclient.cxx create mode 100644 scripts/example/fgfsscript create mode 100644 scripts/example/remote.html diff --git a/scripts/example/.cvsignore b/scripts/example/.cvsignore new file mode 100644 index 000000000..282522db0 --- /dev/null +++ b/scripts/example/.cvsignore @@ -0,0 +1,2 @@ +Makefile +Makefile.in diff --git a/scripts/example/Makefile.am b/scripts/example/Makefile.am new file mode 100644 index 000000000..6f18c49ec --- /dev/null +++ b/scripts/example/Makefile.am @@ -0,0 +1 @@ +EXTRA_DIST = fgfsclient.c fgfsclient.cxx fgfsscript remote.html diff --git a/scripts/example/fgfsclient.c b/scripts/example/fgfsclient.c new file mode 100644 index 000000000..820d28730 --- /dev/null +++ b/scripts/example/fgfsclient.c @@ -0,0 +1,151 @@ +/* $Id$ */ +/* gcc -O2 -g -pedantic -Wall fgfsclient.c -o fgfsclient */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DFLTHOST "localhost" +#define DFLTPORT 5501 +#define MAXMSG 256 +#define fgfsclose close + + +void init_sockaddr(struct sockaddr_in *name, const char *hostname, unsigned port); +int fgfswrite(int sock, char *msg, ...); +const char *fgfsread(int sock, int wait); +void fgfsflush(int sock); + + + +int fgfswrite(int sock, char *msg, ...) +{ + va_list va; + ssize_t len; + char buf[MAXMSG]; + + va_start(va, msg); + vsprintf(buf, msg, va); + va_end(va); + printf("SEND: \t<%s>\n", buf); + strcat(buf, "\015\012"); + + len = write(sock, buf, strlen(buf)); + if (len < 0) { + perror("fgfswrite"); + exit(EXIT_FAILURE); + } + return len; +} + + + +const char *fgfsread(int sock, int timeout) +{ + static char buf[MAXMSG]; + char *p; + fd_set ready; + struct timeval tv; + ssize_t len; + + FD_ZERO(&ready); + FD_SET(sock, &ready); + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (!select(32, &ready, 0, 0, &tv)) + return NULL; + + len = read(sock, buf, MAXMSG - 1); + if (len < 0) { + perror("fgfsread"); + exit(EXIT_FAILURE); + } + if (len == 0) + return NULL; + + for (p = &buf[len - 1]; p >= buf; p--) + if (*p != '\015' && *p != '\012') + break; + *++p = '\0'; + return strlen(buf) ? buf : NULL; +} + + + +void fgfsflush(int sock) +{ + const char *p; + while ((p = fgfsread(sock, 0)) != NULL) { + printf("IGNORE: \t<%s>\n", p); + } +} + + + +int fgfsconnect(const char *hostname, const int port) +{ + struct sockaddr_in serv_addr; + struct hostent *hostinfo; + int sock; + + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) { + perror("fgfsconnect/socket"); + return -1; + } + + hostinfo = gethostbyname(hostname); + if (hostinfo == NULL) { + fprintf(stderr, "fgfsconnect: unknown host: \"%s\"\n", hostname); + close(sock); + return -2; + } + + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + serv_addr.sin_addr = *(struct in_addr *)hostinfo->h_addr; + + if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + perror("fgfsconnect/connect"); + close(sock); + return -3; + } + return sock; +} + + + +int main(int argc, char **argv) +{ + int sock; + unsigned port; + const char *hostname, *p; + + hostname = argc > 1 ? argv[1] : DFLTHOST; + port = argc > 2 ? atoi(argv[2]) : DFLTPORT; + + sock = fgfsconnect(hostname, port); + if (sock < 0) + return (EXIT_FAILURE); + + fgfswrite(sock, "data"); + fgfswrite(sock, "set /controls/engines/engine[%d]/throttle %d", 0, 1); + fgfswrite(sock, "get /sim/aircraft"); + p = fgfsread(sock, 3); + if (p != NULL) + printf("READ: \t<%s>\n", p); + fgfswrite(sock, "quit"); + fgfsclose(sock); + return EXIT_SUCCESS; +} + + diff --git a/scripts/example/fgfsclient.cxx b/scripts/example/fgfsclient.cxx new file mode 100644 index 000000000..fa4012f38 --- /dev/null +++ b/scripts/example/fgfsclient.cxx @@ -0,0 +1,181 @@ +// $Id$ +// g++ -O2 -g -pedantic -Wall fgfsclient.cxx -o fgfsclient -lstdc++ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const int maxlen = 256; + + +class FGFSSocket { + int sock; + bool connected; + unsigned timeout; + public: + FGFSSocket(const char *name, const unsigned port); + ~FGFSSocket() { close(); }; + + int write(const char *msg, ...); + const char *read(void); + inline void flush(void); + void settimeout(unsigned t) { timeout = t; }; + private: + int close(void); +}; + + +FGFSSocket::FGFSSocket(const char *hostname = "localhost", const unsigned port = 5501) + : + sock(-1), + connected(false), + timeout(1) +{ + sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock < 0) + throw("FGFSSocket/socket"); + + struct hostent *hostinfo; + hostinfo = gethostbyname(hostname); + if (!hostinfo) { + close(); + throw("FGFSSocket/gethostbyname: unknown host"); + } + + struct sockaddr_in serv_addr; + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = htons(port); + serv_addr.sin_addr = *(struct in_addr *)hostinfo->h_addr; + + if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { + close(); + throw("FGFSSocket/connect"); + } + connected = true; + try { + write("data"); + } catch (...) { + close(); + throw; + } +} + + +int FGFSSocket::close(void) +{ + if (connected) + write("quit"); + if (sock < 0) + return 0; + int ret = ::close(sock); + sock = -1; + return ret; +} + + +int FGFSSocket::write(const char *msg, ...) +{ + va_list va; + ssize_t len; + char buf[maxlen]; + fd_set fd; + struct timeval tv; + + FD_ZERO(&fd); + FD_SET(sock, &fd); + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (!select(FD_SETSIZE, 0, &fd, 0, &tv)) + throw("FGFSSocket::write/select: timeout exceeded"); + + va_start(va, msg); + vsprintf(buf, msg, va); + va_end(va); + std::cout << "SEND: " << buf << std::endl; + strcat(buf, "\015\012"); + + len = ::write(sock, buf, strlen(buf)); + if (len < 0) + throw("FGFSSocket::write"); + return len; +} + + +const char *FGFSSocket::read(void) +{ + static char buf[maxlen]; + char *p; + fd_set fd; + struct timeval tv; + ssize_t len; + + FD_ZERO(&fd); + FD_SET(sock, &fd); + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (!select(FD_SETSIZE, &fd, 0, 0, &tv)) { + if (timeout == 0) + return 0; + else + throw("FGFSSocket::read/select: timeout exceeded"); + } + + len = ::read(sock, buf, maxlen - 1); + if (len < 0) + throw("FGFSSocket::read/read"); + if (len == 0) + return 0; + + for (p = &buf[len - 1]; p >= buf; p--) + if (*p != '\015' && *p != '\012') + break; + *++p = '\0'; + return strlen(buf) ? buf : 0; +} + + +inline void FGFSSocket::flush(void) +{ + int i = timeout; + timeout = 0; + while (read()) + ; + timeout = i; +} + + +int main(const int argc, const char *argv[]) +try { + const char *hostname = argc > 1 ? argv[1] : "localhost"; + int port = argc > 2 ? atoi(argv[2]) : 5501; + + FGFSSocket f(hostname, port); + f.flush(); + f.write("set /controls/engines/engine[%d]/throttle %lg", 0, 1.0); + f.write("set /controls/engines/engine[%d]/throttle %lg", 1, 1.0); + f.write("get /sim/aircraft"); + const char *p = f.read(); + if (p) + std::cout << "RECV: " << p << std::endl; + return 0; + +} catch (const char s[]) { + std::cerr << "Error: " << s << ": " << strerror(errno) << std::endl; + return -1; + +} catch (...) { + std::cerr << "Error: unknown exception" << std::endl; + return -1; +} + + +// vim:cindent diff --git a/scripts/example/fgfsscript b/scripts/example/fgfsscript new file mode 100644 index 000000000..03572538f --- /dev/null +++ b/scripts/example/fgfsscript @@ -0,0 +1,95 @@ +#!/usr/bin/perl -w +# USAGE: fgfsscript [host [port]] +# Melchior FRANZ, a8603365@unet.univie.ac.at +# $Id$ + +use strict; +use IO::Socket; + +my $host = (shift || 'localhost'); +my $port = (shift || 5501); +my ($fgfs, $i); + + + +# main() +{ + chdir; + $fgfs = &connect($host, $port, 120) || die " can't open socket\n"; + &send("data"); + + # wait for random altitude (0--3000 ft.) to be reached + my $alt = int(rand(3000)); + print "disaster begins at $alt ft. AGL\n"; + while (1) { + sleep(1); + $i = &get("/position/altitude-agl-ft"); + print "\r" . int($i) . " ft."; + print "\n" and last if $i > $alt; + } + + print "start fuel dumping :-)\n"; + for ($i = 0; $i < 4; $i++) { + sleep(rand(60)); + &set("/consumables/fuel/tank[$i]/level-gal_us", 0); + print "tank $i empty\n"; + } + + &send("quit"); + close $fgfs; +} + + + + +sub get() +{ + &send("get " . shift); + eof $fgfs and die "\nconnection closed by host"; + $_ = <$fgfs>; + s/\015?\012$//; + /^-ERR (.*)/ and die "\nfgfs error: $1\n"; + return $_; +} + + +sub set() +{ + my $prop = shift; + my $value = shift; + &send("set $prop $value"); +} + + +sub send() +{ + print $fgfs shift, "\015\012"; +} + + +sub connect() +{ + my $host = shift; + my $port = shift; + my $timeout = (shift || 120); + my $socket; + STDOUT->autoflush(1); + print "connect "; + while ($timeout--) { + if ($socket = IO::Socket::INET->new( + Proto => 'tcp', + PeerAddr => $host, + PeerPort => $port)) { + print ".. done.\n"; + $socket->autoflush(1); + sleep 1; + return $socket; + } + print "."; + sleep(1); + } + return 0; +} + + +# vi:ts=8:sw=8:noet:nowrap:cindent diff --git a/scripts/example/remote.html b/scripts/example/remote.html new file mode 100644 index 000000000..133b31c68 --- /dev/null +++ b/scripts/example/remote.html @@ -0,0 +1,133 @@ + + + + FlightGear: Remote control + + + + + + + + +

FlightGear: Remote control

+ + +

FlightGear has several interfaces that provide access to internal +parameters. The HTTP interface is best suited for human interaction +via a web browser. The telnet interface is the ideal choice for +remotely controlling FlightGear by means of external programs. +

+ +

To activate FlightGear's telnet server capabilities, call it with +a --props specifiaction: + +

+
+$ fgfs --props=socket,bi,5,localhost,5501,tcp
+
+ +
+
+ + + + + + + +
socket:FlightGear protocol
bi:bidirectional
5:polling frequency in Hertz
localhost: server name or IP-address
5501: server port
tcp:internet protocol type
+
+ +
+
+In newer versions of FlightGear just type: +
+
+$ fgfs --telnet=5501
+
+
+

+ +

To learn more about the supported commands, connect to FlightGear +with a telnet program and type in "help<RETURN>". This is what you'll get:

+ +

+
+$ telnet localhost 5501
+Trying ::1...
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+help
+
+Valid commands are:
+
+help             show help message
+ls [<dir>]       list directory
+dump             dump current state (in xml)
+cd <dir>         cd to a directory, '..' to move back
+pwd              display your current path
+get <var>        show the value of a parameter
+show <var>       synonym for get
+set <var> <val>  set <var> to a new <val>
+data             switch to raw data mode
+prompt           switch to interactive mode (default)
+quit             terminate connection
+
+/>
+
+
+ +

Now you can browse in the property system like in a Linux file +system with cd, ls, pwd.

+ + + + +

Here you can download a sample script +written in Perl, that shows how to access and manipulate FlightGear's +internal parameters. It can be started before FlightGear (in which +case it tries up to 2 minutes to connect) or afterwards. Then +it picks a random AGL altitude and checks every 5 seconds +if the aircraft has already climbed at this height. Now it starts +to empty all four tanks, one after the other, until the engines +stop working. Try to find a place where you can land safely. :-)

+ +
+
+$ fgfsscript&
+$ fgfs --props=socket,bi,5,localhost,5501,tcp
+
+
+ +

The script defaults to localhost and port 5501, +but you can let the script control FlightGear on another host and +under another port.

+ +
+
+$ fgfsscript some.host.org 1234&
+
+
+ + +

Demo programs are available in:
+

+

+ + +
+
+
+$Id$
+Melchior FRANZ +(mfranz at aon dot at) +
+ + +