first commit
This commit is contained in:
1019
src/Network/ATC-Inputs.cxx
Normal file
1019
src/Network/ATC-Inputs.cxx
Normal file
File diff suppressed because it is too large
Load Diff
92
src/Network/ATC-Inputs.hxx
Normal file
92
src/Network/ATC-Inputs.hxx
Normal file
@@ -0,0 +1,92 @@
|
||||
// ATC-Inputs.hxx -- Translate ATC hardware inputs to FGFS properties
|
||||
//
|
||||
// Written by Curtis Olson, started November 2004.
|
||||
//
|
||||
// Copyright (C) 2004 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _FG_ATC_INPUTS_HXX
|
||||
#define _FG_ATC_INPUTS_HXX
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#define ATC_ANAL_IN_VALUES 32
|
||||
#define ATC_ANAL_IN_BYTES (2 * ATC_ANAL_IN_VALUES)
|
||||
#define ATC_RADIO_SWITCH_BYTES 32
|
||||
#define ATC_SWITCH_BYTES 16
|
||||
#define ATC_NUM_COLS 8
|
||||
|
||||
|
||||
class FGATCInput {
|
||||
|
||||
int is_open;
|
||||
|
||||
int board;
|
||||
SGPath config;
|
||||
|
||||
int analog_in_fd;
|
||||
int radios_fd;
|
||||
int switches_fd;
|
||||
|
||||
char analog_in_file[256];
|
||||
char radios_file[256];
|
||||
char switches_file[256];
|
||||
|
||||
unsigned char analog_in_bytes[ATC_ANAL_IN_BYTES];
|
||||
int analog_in_data[ATC_ANAL_IN_VALUES];
|
||||
unsigned char radio_switch_data[ATC_RADIO_SWITCH_BYTES];
|
||||
unsigned char switch_data[ATC_SWITCH_BYTES];
|
||||
|
||||
SGPropertyNode_ptr ignore_flight_controls;
|
||||
SGPropertyNode_ptr ignore_pedal_controls;
|
||||
|
||||
SGPropertyNode_ptr analog_in_node;
|
||||
SGPropertyNode_ptr radio_in_node;
|
||||
SGPropertyNode_ptr switches_node;
|
||||
|
||||
void init_config();
|
||||
bool do_analog_in();
|
||||
bool do_radio_switches();
|
||||
bool do_switches();
|
||||
|
||||
public:
|
||||
|
||||
// Constructor: The _board parameter specifies which board to
|
||||
// reference. Possible values are 0 or 1. The _config_file
|
||||
// parameter specifies the location of the input config file (xml)
|
||||
FGATCInput( const int _board, const SGPath &_config_file );
|
||||
|
||||
// Destructor
|
||||
~FGATCInput() { }
|
||||
|
||||
bool open();
|
||||
|
||||
// process the hardware inputs. This code assumes the calling
|
||||
// layer will lock the hardware.
|
||||
bool process();
|
||||
|
||||
bool close();
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_ATC_INPUTS_HXX
|
||||
284
src/Network/ATC-Main.cxx
Normal file
284
src/Network/ATC-Main.cxx
Normal file
@@ -0,0 +1,284 @@
|
||||
// ATC-Main.cxx -- FGFS interface to ATC hardware
|
||||
//
|
||||
// Written by Curtis Olson, started January 2002
|
||||
//
|
||||
// Copyright (C) 2002 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <stdlib.h> // atoi() atof() abs()
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdio.h> //snprintf
|
||||
#ifdef _WIN32
|
||||
# include <io.h> //lseek, read, write
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/math/sg_types.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "ATC-Main.hxx"
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::vector;
|
||||
|
||||
// Lock the ATC hardware
|
||||
static int fgATCMainLock( int fd ) {
|
||||
// rewind
|
||||
lseek( fd, 0, SEEK_SET );
|
||||
|
||||
char tmp[2];
|
||||
int result = read( fd, tmp, 1 );
|
||||
if ( result != 1 ) {
|
||||
SG_LOG( SG_IO, SG_DEBUG, "Lock failed" );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// Write a radios command
|
||||
static int fgATCMainRelease( int fd ) {
|
||||
// rewind
|
||||
lseek( fd, 0, SEEK_SET );
|
||||
|
||||
char tmp[2];
|
||||
tmp[0] = tmp[1] = 0;
|
||||
int result = write( fd, tmp, 1 );
|
||||
|
||||
if ( result != 1 ) {
|
||||
SG_LOG( SG_IO, SG_DEBUG, "Release failed" );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void FGATCMain::init_config() {
|
||||
#if defined( unix ) || defined( __CYGWIN__ )
|
||||
// Next check home directory for .fgfsrc.hostname file
|
||||
SGPath atcsim_config = SGPath::home();
|
||||
atcsim_config.append( ".fgfs-atc610x.xml" );
|
||||
try {
|
||||
SG_LOG(SG_NETWORK, SG_ALERT,
|
||||
"Warning: loading deprecated config file: " << atcsim_config);
|
||||
readProperties( atcsim_config, globals->get_props() );
|
||||
} catch (const sg_exception &e) {
|
||||
// fail silently, this is an old style config file I want to continue
|
||||
// to support if it exists.
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Open and initialize ATC hardware
|
||||
bool FGATCMain::open() {
|
||||
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_IO, SG_ALERT,
|
||||
"Initializing ATC hardware, please wait ..." );
|
||||
|
||||
// This loads the config parameters generated by "simcal"
|
||||
init_config();
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Open the /proc files
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
string lock0_file = "/proc/atcflightsim/board0/lock";
|
||||
string lock1_file = "/proc/atcflightsim/board1/lock";
|
||||
|
||||
lock0_fd = ::open( lock0_file.c_str(), O_RDWR );
|
||||
if ( lock0_fd == -1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
|
||||
char msg[256];
|
||||
snprintf( msg, 256, "Error opening %s", lock0_file.c_str() );
|
||||
perror( msg );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
lock1_fd = ::open( lock1_file.c_str(), O_RDWR );
|
||||
if ( lock1_fd == -1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
|
||||
char msg[256];
|
||||
snprintf( msg, 256, "Error opening %s", lock1_file.c_str() );
|
||||
perror( msg );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
if ( !input0_path.isNull() ) {
|
||||
input0 = new FGATCInput( 0, input0_path );
|
||||
input0->open();
|
||||
}
|
||||
if ( !input1_path.isNull() ) {
|
||||
input1 = new FGATCInput( 1, input1_path );
|
||||
input1->open();
|
||||
}
|
||||
if ( !output0_path.isNull() ) {
|
||||
output0 = new FGATCOutput( 0, output0_path );
|
||||
output0->open( lock0_fd );
|
||||
}
|
||||
if ( !output1_path.isNull() ) {
|
||||
output1 = new FGATCOutput( 1, output1_path );
|
||||
output1->open( lock1_fd );
|
||||
}
|
||||
|
||||
set_hz( 30 ); // default to processing requests @ 30Hz
|
||||
set_enabled( true );
|
||||
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
// Finished initing hardware
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
SG_LOG( SG_IO, SG_ALERT,
|
||||
"Done initializing ATC hardware." );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FGATCMain::process() {
|
||||
// cout << "Main::process()\n";
|
||||
|
||||
bool board0_locked = false;
|
||||
bool board1_locked = false;
|
||||
|
||||
if ( input0 != NULL || output0 != NULL ) {
|
||||
// Lock board0 if we have a configuration for it
|
||||
if ( fgATCMainLock( lock0_fd ) > 0 ) {
|
||||
board0_locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( input1 != NULL || output1 != NULL ) {
|
||||
// Lock board1 if we have a configuration for it
|
||||
if ( fgATCMainLock( lock1_fd ) > 0 ) {
|
||||
board1_locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// cout << " locks: ";
|
||||
// if ( board0_locked ) { cout << "board0 "; }
|
||||
// if ( board1_locked ) { cout << "board1 "; }
|
||||
// cout << endl;
|
||||
|
||||
// process the ATC inputs
|
||||
if ( input0 != NULL && board0_locked ) {
|
||||
input0->process();
|
||||
}
|
||||
if ( input1 != NULL && board1_locked ) {
|
||||
input1->process();
|
||||
}
|
||||
|
||||
// run our custom nasal script. This is a layer above the raw
|
||||
// hardware inputs. It handles situations where there isn't a
|
||||
// direct 1-1 linear mapping between ATC functionality and FG
|
||||
// functionality, and handles situations where FG expects more
|
||||
// functionality from the interface than the ATC hardware can
|
||||
// directly provide.
|
||||
|
||||
FGNasalSys *n = (FGNasalSys*)globals->get_subsystem("nasal");
|
||||
bool result = n->parseAndRun( "atcsim.update()" );
|
||||
if ( !result ) {
|
||||
SG_LOG( SG_NETWORK, SG_ALERT, "Nasal: atcsim.update() failed!" );
|
||||
}
|
||||
|
||||
// process the ATC outputs
|
||||
if ( output0 != NULL && board0_locked ) {
|
||||
output0->process();
|
||||
}
|
||||
if ( output1 != NULL && board1_locked ) {
|
||||
output1->process();
|
||||
}
|
||||
|
||||
if ( board0_locked ) {
|
||||
fgATCMainRelease( lock0_fd );
|
||||
}
|
||||
if ( board1_locked ) {
|
||||
fgATCMainRelease( lock1_fd );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FGATCMain::close() {
|
||||
cout << "FGATCMain::close()" << endl;
|
||||
|
||||
int result;
|
||||
|
||||
if ( input0 != NULL ) {
|
||||
input0->close();
|
||||
}
|
||||
if ( input1 != NULL ) {
|
||||
input1->close();
|
||||
}
|
||||
if ( output0 != NULL ) {
|
||||
output0->close();
|
||||
}
|
||||
if ( output1 != NULL ) {
|
||||
output1->close();
|
||||
}
|
||||
|
||||
result = ::close( lock0_fd );
|
||||
if ( result == -1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
|
||||
char msg[256];
|
||||
snprintf( msg, 256, "Error closing lock0_fd" );
|
||||
perror( msg );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
result = ::close( lock1_fd );
|
||||
if ( result == -1 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "errno = " << errno );
|
||||
char msg[256];
|
||||
snprintf( msg, 256, "Error closing lock1_fd" );
|
||||
perror( msg );
|
||||
exit( -1 );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
96
src/Network/ATC-Main.hxx
Normal file
96
src/Network/ATC-Main.hxx
Normal file
@@ -0,0 +1,96 @@
|
||||
// ATC-Main.hxx -- FGFS interface to ATC 610x hardware
|
||||
//
|
||||
// Written by Curtis Olson, started January 2002.
|
||||
//
|
||||
// Copyright (C) 2002 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _FG_ATC_MAIN_HXX
|
||||
#define _FG_ATC_MAIN_HXX
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include "protocol.hxx"
|
||||
|
||||
#include "ATC-Inputs.hxx"
|
||||
#include "ATC-Outputs.hxx"
|
||||
|
||||
|
||||
class FGATCMain : public FGProtocol {
|
||||
|
||||
FGATCInput *input0; // board0 input interface class
|
||||
FGATCInput *input1; // board1 input interface class
|
||||
FGATCOutput *output0; // board0 output interface class
|
||||
FGATCOutput *output1; // board1 output interface class
|
||||
|
||||
SGPath input0_path;
|
||||
SGPath input1_path;
|
||||
SGPath output0_path;
|
||||
SGPath output1_path;
|
||||
|
||||
int lock0_fd;
|
||||
int lock1_fd;
|
||||
|
||||
public:
|
||||
|
||||
FGATCMain() :
|
||||
input0(NULL),
|
||||
input1(NULL),
|
||||
output0(NULL),
|
||||
output1(NULL),
|
||||
input0_path(""),
|
||||
input1_path(""),
|
||||
output0_path(""),
|
||||
output1_path("")
|
||||
{ }
|
||||
|
||||
~FGATCMain() {
|
||||
delete input0;
|
||||
delete input1;
|
||||
delete output0;
|
||||
delete output1;
|
||||
}
|
||||
|
||||
// Open and initialize ATC 610x hardware
|
||||
bool open();
|
||||
|
||||
void init_config();
|
||||
|
||||
bool process();
|
||||
|
||||
bool close();
|
||||
|
||||
inline void set_path_names( const SGPath &in0, const SGPath &in1,
|
||||
const SGPath &out0, const SGPath &out1 )
|
||||
{
|
||||
input0_path = in0;
|
||||
input1_path = in1;
|
||||
output0_path = out0;
|
||||
output1_path = out1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_ATC_MAIN_HXX
|
||||
1106
src/Network/ATC-Outputs.cxx
Normal file
1106
src/Network/ATC-Outputs.cxx
Normal file
File diff suppressed because it is too large
Load Diff
95
src/Network/ATC-Outputs.hxx
Normal file
95
src/Network/ATC-Outputs.hxx
Normal file
@@ -0,0 +1,95 @@
|
||||
// ATC-Outputs.hxx -- Translate FGFS properties into ATC hardware outputs
|
||||
//
|
||||
// Written by Curtis Olson, started November 2004.
|
||||
//
|
||||
// Copyright (C) 2004 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _FG_ATC_OUTPUTS_HXX
|
||||
#define _FG_ATC_OUTPUTS_HXX
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#define ATC_RADIO_DISPLAY_BYTES 48
|
||||
#define ATC_ANALOG_OUT_CHANNELS 48
|
||||
#define ATC_COMPASS_CH 5
|
||||
#define ATC_STEPPER_HOME 0xC0
|
||||
|
||||
|
||||
class FGATCOutput {
|
||||
|
||||
int is_open;
|
||||
|
||||
int board;
|
||||
SGPath config;
|
||||
|
||||
int analog_out_fd;
|
||||
int lamps_fd;
|
||||
int radio_display_fd;
|
||||
int stepper_fd;
|
||||
|
||||
char analog_out_file[256];
|
||||
char lamps_file[256];
|
||||
char radio_display_file[256];
|
||||
char stepper_file[256];
|
||||
|
||||
unsigned char analog_out_data[ATC_ANALOG_OUT_CHANNELS*2];
|
||||
unsigned char radio_display_data[ATC_RADIO_DISPLAY_BYTES];
|
||||
|
||||
SGPropertyNode_ptr analog_out_node;
|
||||
SGPropertyNode_ptr lamps_out_node;
|
||||
SGPropertyNode_ptr radio_display_node;
|
||||
SGPropertyNode_ptr steppers_node;
|
||||
|
||||
void init_config();
|
||||
bool do_analog_out();
|
||||
bool do_lamps();
|
||||
bool do_radio_display();
|
||||
bool do_steppers();
|
||||
|
||||
// hardwired stepper motor code
|
||||
float compass_position;
|
||||
|
||||
public:
|
||||
|
||||
// Constructor: The _board parameter specifies which board to
|
||||
// reference. Possible values are 0 or 1. The _config_file
|
||||
// parameter specifies the location of the output config file (xml)
|
||||
FGATCOutput( const int _board, const SGPath &_config_file );
|
||||
|
||||
// Destructor
|
||||
~FGATCOutput() { }
|
||||
|
||||
// need to pass in atc hardware lock_fd so that the radios and
|
||||
// lamps can be blanked and so that the compass can be homed.
|
||||
bool open( int lock_fd );
|
||||
|
||||
// process the hardware outputs. This code assumes the calling
|
||||
// layer will lock the hardware.
|
||||
bool process();
|
||||
|
||||
bool close();
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_ATC_OUTPUTS_HXX
|
||||
425
src/Network/AV400.cxx
Normal file
425
src/Network/AV400.cxx
Normal file
@@ -0,0 +1,425 @@
|
||||
// AV400.cxx -- Garmin 400 series protocal class
|
||||
//
|
||||
// Written by Curtis Olson, started August 2006.
|
||||
//
|
||||
// Copyright (C) 2006 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "AV400.hxx"
|
||||
|
||||
FGAV400::FGAV400() {
|
||||
}
|
||||
|
||||
FGAV400::~FGAV400() {
|
||||
}
|
||||
|
||||
|
||||
// generate AV400 message
|
||||
bool FGAV400::gen_message() {
|
||||
// cout << "generating garmin message" << endl;
|
||||
|
||||
char msg_z[32], msg_A[32], msg_B[32], msg_C[32], msg_D[32];
|
||||
char msg_Q[32], msg_T[32], msg_type2[256];
|
||||
// the following could be implemented, but currently are unused
|
||||
// char msg_E[32], msg_G[32], msg_I[32], msg_K[32], msg_L[32], msg_S[32];
|
||||
// char msg_l[32];
|
||||
|
||||
char dir;
|
||||
int deg;
|
||||
double min;
|
||||
|
||||
// create msg_z
|
||||
sprintf( msg_z, "z%05.0f\r\n", fdm.get_Altitude() );
|
||||
|
||||
// create msg_A
|
||||
sprintf( msg_A, "A");
|
||||
|
||||
double latd = fdm.get_Latitude() * SGD_RADIANS_TO_DEGREES;
|
||||
if ( latd < 0.0 ) {
|
||||
latd = -latd;
|
||||
dir = 'S';
|
||||
} else {
|
||||
dir = 'N';
|
||||
}
|
||||
deg = (int)latd;
|
||||
min = (latd - (double)deg) * 60.0 * 100.0;
|
||||
sprintf( msg_A, "A%c %02d %04.0f\r\n", dir, deg, min);
|
||||
|
||||
// create msg_B
|
||||
double lond = fdm.get_Longitude() * SGD_RADIANS_TO_DEGREES;
|
||||
if ( lond < 0.0 ) {
|
||||
lond = -lond;
|
||||
dir = 'W';
|
||||
} else {
|
||||
dir = 'E';
|
||||
}
|
||||
deg = (int)lond;
|
||||
min = (lond - (double)deg) * 60.0 * 100.0;
|
||||
sprintf( msg_B, "B%c %03d %04.0f\r\n", dir, deg, min);
|
||||
|
||||
// create msg_C
|
||||
float magdeg = fgGetDouble( "/environment/magnetic-variation-deg" );
|
||||
double vn = fgGetDouble( "/velocities/speed-north-fps" );
|
||||
double ve = fgGetDouble( "/velocities/speed-east-fps" );
|
||||
double gnd_trk_true = atan2( ve, vn ) * SGD_RADIANS_TO_DEGREES;
|
||||
double gnd_trk_mag = gnd_trk_true - magdeg;
|
||||
if ( gnd_trk_mag < 0.0 ) { gnd_trk_mag += 360.0; }
|
||||
if ( gnd_trk_mag >= 360.0 ) { gnd_trk_mag -= 360.0; }
|
||||
sprintf( msg_C, "C%03.0f\r\n", gnd_trk_mag);
|
||||
|
||||
// create msg_D
|
||||
double speed_kt = sqrt( vn*vn + ve*ve ) * SG_FPS_TO_KT;
|
||||
if ( speed_kt > 999.0 ) {
|
||||
speed_kt = 999.0;
|
||||
}
|
||||
sprintf( msg_D, "D%03.0f\r\n", speed_kt);
|
||||
|
||||
// create msg_E (not implemented)
|
||||
// create msg_G (not implemented)
|
||||
// create msg_I (not implemented)
|
||||
// create msg_K (not implemented)
|
||||
// create msg_L (not implemented)
|
||||
|
||||
// create msg_Q
|
||||
if ( magdeg < 0.0 ) {
|
||||
magdeg = -magdeg;
|
||||
dir = 'W';
|
||||
} else {
|
||||
dir = 'E';
|
||||
}
|
||||
sprintf( msg_Q, "Q%c%03.0f\r\n", dir, magdeg * 10.0 );
|
||||
|
||||
// create msg_S (not implemented)
|
||||
|
||||
// create msg_T
|
||||
sprintf( msg_T, "T---------\r\n" );
|
||||
|
||||
// create msg_l (not implemented)
|
||||
|
||||
// sentence type 2
|
||||
sprintf( msg_type2, "w01%c\r\n", (char)65 );
|
||||
|
||||
// assemble message
|
||||
string sentence;
|
||||
sentence += '\002'; // STX
|
||||
sentence += msg_z; // altitude
|
||||
sentence += msg_A; // latitude
|
||||
sentence += msg_B; // longitude
|
||||
sentence += msg_C; // ground track
|
||||
sentence += msg_D; // ground speed (kt)
|
||||
// sentence += "E-----\r\n";
|
||||
// sentence += "G-----\r\n";
|
||||
// sentence += "I----\r\n";
|
||||
// sentence += "K-----\r\n";
|
||||
// sentence += "L----\r\n";
|
||||
sentence += msg_Q; // magvar
|
||||
// sentence += "S-----\r\n";
|
||||
sentence += msg_T; // end of type 1 messages (must be sent)
|
||||
sentence += msg_type2; // type2 message
|
||||
// sentence += "l------\r\n";
|
||||
sentence += '\003'; // ETX
|
||||
|
||||
// cout << sentence;
|
||||
length = sentence.length();
|
||||
// cout << endl << "length = " << length << endl;
|
||||
strncpy( buf, sentence.c_str(), length );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// parse AV400 message
|
||||
bool FGAV400::parse_message() {
|
||||
SG_LOG( SG_IO, SG_INFO, "parse garmin message" );
|
||||
|
||||
string msg = buf;
|
||||
msg = msg.substr( 0, length );
|
||||
SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
|
||||
|
||||
string::size_type begin_line, end_line, begin, end;
|
||||
begin_line = begin = 0;
|
||||
|
||||
// extract out each line
|
||||
end_line = msg.find("\n", begin_line);
|
||||
while ( end_line != string::npos ) {
|
||||
string line = msg.substr(begin_line, end_line - begin_line);
|
||||
begin_line = end_line + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " input line = " << line );
|
||||
|
||||
// leading character
|
||||
string start = msg.substr(begin, 1);
|
||||
++begin;
|
||||
SG_LOG( SG_IO, SG_INFO, " start = " << start );
|
||||
|
||||
// sentence
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sentence = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " sentence = " << sentence );
|
||||
|
||||
double lon_deg, lon_min, lat_deg, lat_min;
|
||||
double lon, lat, speed, heading, altitude;
|
||||
|
||||
if ( sentence == "GPRMC" ) {
|
||||
// time
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string utc = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " utc = " << utc );
|
||||
|
||||
// junk
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string junk = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " junk = " << junk );
|
||||
|
||||
// lat val
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lat_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lat_deg = atof( lat_str.substr(0, 2).c_str() );
|
||||
lat_min = atof( lat_str.substr(2).c_str() );
|
||||
|
||||
// lat dir
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lat_dir = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lat = lat_deg + ( lat_min / 60.0 );
|
||||
if ( lat_dir == "S" ) {
|
||||
lat *= -1;
|
||||
}
|
||||
|
||||
fdm.set_Latitude( lat * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " lat = " << lat );
|
||||
|
||||
// lon val
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lon_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lon_deg = atof( lon_str.substr(0, 3).c_str() );
|
||||
lon_min = atof( lon_str.substr(3).c_str() );
|
||||
|
||||
// lon dir
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lon_dir = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lon = lon_deg + ( lon_min / 60.0 );
|
||||
if ( lon_dir == "W" ) {
|
||||
lon *= -1;
|
||||
}
|
||||
|
||||
fdm.set_Longitude( lon * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " lon = " << lon );
|
||||
|
||||
#if 0
|
||||
double sl_radius, lat_geoc;
|
||||
sgGeodToGeoc( fdm.get_Latitude(),
|
||||
fdm.get_Altitude(),
|
||||
&sl_radius, &lat_geoc );
|
||||
fdm.set_Geocentric_Position( lat_geoc,
|
||||
fdm.get_Longitude(),
|
||||
sl_radius + fdm.get_Altitude() );
|
||||
#endif
|
||||
|
||||
// speed
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string speed_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
speed = atof( speed_str.c_str() );
|
||||
fdm.set_V_calibrated_kts( speed );
|
||||
// fdm.set_V_ground_speed( speed );
|
||||
SG_LOG( SG_IO, SG_INFO, " speed = " << speed );
|
||||
|
||||
// heading
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string hdg_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
heading = atof( hdg_str.c_str() );
|
||||
fdm.set_Euler_Angles( fdm.get_Phi(),
|
||||
fdm.get_Theta(),
|
||||
heading * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " heading = " << heading );
|
||||
} else if ( sentence == "PGRMZ" ) {
|
||||
// altitude
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string alt_str = msg.substr(begin, end - begin);
|
||||
altitude = atof( alt_str.c_str() );
|
||||
begin = end + 1;
|
||||
|
||||
// altitude units
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string alt_units = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
if ( alt_units != "F" && alt_units != "f" ) {
|
||||
altitude *= SG_METER_TO_FEET;
|
||||
}
|
||||
|
||||
fdm.set_Altitude( altitude );
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, " altitude = " << altitude );
|
||||
|
||||
}
|
||||
|
||||
// printf("%.8f %.8f\n", lon, lat);
|
||||
|
||||
begin = begin_line;
|
||||
end_line = msg.find("\n", begin_line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// open hailing frequencies
|
||||
bool FGAV400::open() {
|
||||
if ( is_enabled() ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||
<< "is already in use, ignoring" );
|
||||
return false;
|
||||
}
|
||||
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
if ( ! io->open( get_direction() ) ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
|
||||
return false;
|
||||
}
|
||||
|
||||
set_enabled( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// process work for this port
|
||||
bool FGAV400::process() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
if ( get_direction() == SG_IO_OUT ) {
|
||||
gen_message();
|
||||
if ( ! io->write( buf, length ) ) {
|
||||
SG_LOG( SG_IO, SG_WARN, "Error writing data." );
|
||||
return false;
|
||||
}
|
||||
} else if ( get_direction() == SG_IO_IN ) {
|
||||
if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
|
||||
if ( parse_message() ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
|
||||
}
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
|
||||
return false;
|
||||
}
|
||||
if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
|
||||
if ( parse_message() ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
|
||||
}
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error reading data." );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// close the channel
|
||||
bool FGAV400::close() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
set_enabled( false );
|
||||
|
||||
if ( ! io->close() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
61
src/Network/AV400.hxx
Normal file
61
src/Network/AV400.hxx
Normal file
@@ -0,0 +1,61 @@
|
||||
// AV400.hxx -- Garmin 400 series protocal class
|
||||
//
|
||||
// Written by Curtis Olson, started August 2006.
|
||||
//
|
||||
// Copyright (C) 2006 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _FG_AV400_HXX
|
||||
#define _FG_AV400_HXX
|
||||
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "protocol.hxx"
|
||||
#include <FDM/flightProperties.hxx>
|
||||
|
||||
class FGAV400 : public FGProtocol {
|
||||
|
||||
char buf[ FG_MAX_MSG_SIZE ];
|
||||
int length;
|
||||
|
||||
public:
|
||||
|
||||
FGAV400();
|
||||
~FGAV400();
|
||||
|
||||
bool gen_message();
|
||||
bool parse_message();
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
|
||||
FlightProperties fdm;
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_AV400_HXX
|
||||
298
src/Network/AV400Sim.cxx
Normal file
298
src/Network/AV400Sim.cxx
Normal file
@@ -0,0 +1,298 @@
|
||||
// AV400Sim.cxx -- Garmin 400 series protocal class. This AV400Sim
|
||||
// protocol generates the set of "simulator" commands a garmin 400
|
||||
// series gps would expect as input in simulator mode. The AV400
|
||||
// protocol generates the set of commands that a garmin 400 series gps
|
||||
// would emit.
|
||||
//
|
||||
// Written by Curtis Olson, started Janauary 2009.
|
||||
//
|
||||
// Copyright (C) 2009 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
||||
#include <FDM/flightProperties.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "AV400Sim.hxx"
|
||||
|
||||
FGAV400Sim::FGAV400Sim() {
|
||||
fdm = new FlightProperties;
|
||||
}
|
||||
|
||||
FGAV400Sim::~FGAV400Sim() {
|
||||
delete fdm;
|
||||
}
|
||||
|
||||
|
||||
// generate AV400Sim message
|
||||
bool FGAV400Sim::gen_message() {
|
||||
// cout << "generating garmin message" << endl;
|
||||
|
||||
char msg_a[32], msg_b[32], msg_c[32], msg_d[32], msg_e[32];
|
||||
char msg_f[32], msg_h[32], msg_i[32], msg_j[32], msg_k[32], msg_l[32], msg_r[32];
|
||||
char msg_type2[256];
|
||||
|
||||
char dir;
|
||||
int deg;
|
||||
double min;
|
||||
|
||||
// create msg_a
|
||||
double latd = fdm->get_Latitude() * SGD_RADIANS_TO_DEGREES;
|
||||
if ( latd < 0.0 ) {
|
||||
latd = -latd;
|
||||
dir = 'S';
|
||||
} else {
|
||||
dir = 'N';
|
||||
}
|
||||
deg = (int)latd;
|
||||
min = (latd - (double)deg) * 60.0 * 100.0;
|
||||
sprintf( msg_a, "a%c %03d %04.0f\r\n", dir, deg, min);
|
||||
|
||||
// create msg_b
|
||||
double lond = fdm->get_Longitude() * SGD_RADIANS_TO_DEGREES;
|
||||
if ( lond < 0.0 ) {
|
||||
lond = -lond;
|
||||
dir = 'W';
|
||||
} else {
|
||||
dir = 'E';
|
||||
}
|
||||
deg = (int)lond;
|
||||
min = (lond - (double)deg) * 60.0 * 100.0;
|
||||
sprintf( msg_b, "b%c %03d %04.0f\r\n", dir, deg, min);
|
||||
|
||||
// create msg_c
|
||||
double alt = fdm->get_Altitude();
|
||||
if ( alt > 99999.0 ) { alt = 99999.0; }
|
||||
sprintf( msg_c, "c%05.0f\r\n", alt );
|
||||
|
||||
// create msg_d
|
||||
double ve_kts = fgGetDouble( "/velocities/speed-east-fps" ) * SG_FPS_TO_KT;
|
||||
if ( ve_kts < 0.0 ) {
|
||||
ve_kts = -ve_kts;
|
||||
dir = 'W';
|
||||
} else {
|
||||
dir = 'E';
|
||||
}
|
||||
if ( ve_kts > 999.0 ) { ve_kts = 999.0; }
|
||||
sprintf( msg_d, "d%c%03.0f\r\n", dir, ve_kts );
|
||||
|
||||
// create msg_e
|
||||
double vn_kts = fgGetDouble( "/velocities/speed-north-fps" ) * SG_FPS_TO_KT;
|
||||
if ( vn_kts < 0.0 ) {
|
||||
vn_kts = -vn_kts;
|
||||
dir = 'S';
|
||||
} else {
|
||||
dir = 'N';
|
||||
}
|
||||
if ( vn_kts > 999.0 ) { vn_kts = 999.0; }
|
||||
sprintf( msg_e, "e%c%03.0f\r\n", dir, vn_kts );
|
||||
|
||||
// create msg_f
|
||||
double climb_fpm = fgGetDouble( "/velocities/vertical-speed-fps" ) * 60;
|
||||
if ( climb_fpm < 0.0 ) {
|
||||
climb_fpm = -climb_fpm;
|
||||
dir = 'D';
|
||||
} else {
|
||||
dir = 'U';
|
||||
}
|
||||
if ( climb_fpm > 9999.0 ) { climb_fpm = 9999.0; }
|
||||
sprintf( msg_f, "f%c%04.0f\r\n", dir, climb_fpm );
|
||||
|
||||
// create msg_h
|
||||
double obs = fgGetDouble( "/instrumentation/nav[0]/radials/selected-deg" );
|
||||
sprintf( msg_h, "h%04d\r\n", (int)(obs*10) );
|
||||
|
||||
// create msg_i
|
||||
double fuel = fgGetDouble( "/consumables/fuel/total-fuel-gals" );
|
||||
if ( fuel > 999.9 ) { fuel = 999.9; }
|
||||
sprintf( msg_i, "i%04.0f\r\n", fuel*10.0 );
|
||||
|
||||
// create msg_j
|
||||
double gph = fgGetDouble( "/engines/engine[0]/fuel-flow-gph" );
|
||||
gph += fgGetDouble( "/engines/engine[1]/fuel-flow-gph" );
|
||||
gph += fgGetDouble( "/engines/engine[2]/fuel-flow-gph" );
|
||||
gph += fgGetDouble( "/engines/engine[3]/fuel-flow-gph" );
|
||||
if ( gph > 999.9 ) { gph = 999.9; }
|
||||
sprintf( msg_j, "j%04.0f\r\n", gph*10.0 );
|
||||
|
||||
// create msg_k
|
||||
sprintf( msg_k, "k%04d%02d%02d%02d%02d%02d\r\n",
|
||||
fgGetInt( "/sim/time/utc/year"),
|
||||
fgGetInt( "/sim/time/utc/month"),
|
||||
fgGetInt( "/sim/time/utc/day"),
|
||||
fgGetInt( "/sim/time/utc/hour"),
|
||||
fgGetInt( "/sim/time/utc/minute"),
|
||||
fgGetInt( "/sim/time/utc/second") );
|
||||
|
||||
// create msg_l
|
||||
alt = fgGetDouble( "/instrumentation/pressure-alt-ft" );
|
||||
if ( alt > 99999.0 ) { alt = 99999.0; }
|
||||
sprintf( msg_l, "l%05.0f\r\n", alt );
|
||||
|
||||
// create msg_r
|
||||
sprintf( msg_r, "rA\r\n" );
|
||||
|
||||
// sentence type 2
|
||||
sprintf( msg_type2, "w01%c\r\n", (char)65 );
|
||||
|
||||
// assemble message
|
||||
string sentence;
|
||||
sentence += '\002'; // STX
|
||||
sentence += msg_a; // latitude
|
||||
sentence += msg_b; // longitude
|
||||
sentence += msg_c; // gps altitude
|
||||
sentence += msg_d; // ve kts
|
||||
sentence += msg_e; // vn kts
|
||||
sentence += msg_f; // climb fpm
|
||||
sentence += msg_h; // obs heading in deg (*10)
|
||||
sentence += msg_i; // total fuel in gal (*10)
|
||||
sentence += msg_j; // fuel flow gph (*10)
|
||||
sentence += msg_k; // date/time (UTC)
|
||||
sentence += msg_l; // pressure altitude
|
||||
sentence += msg_r; // RAIM available
|
||||
sentence += msg_type2; // type2 message
|
||||
sentence += '\003'; // ETX
|
||||
|
||||
// cout << sentence;
|
||||
length = sentence.length();
|
||||
// cout << endl << "length = " << length << endl;
|
||||
strncpy( buf, sentence.c_str(), length );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// parse AV400Sim message
|
||||
bool FGAV400Sim::parse_message() {
|
||||
SG_LOG( SG_IO, SG_INFO, "parse AV400Sim message" );
|
||||
|
||||
string msg = buf;
|
||||
msg = msg.substr( 0, length );
|
||||
SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
|
||||
|
||||
string ident = msg.substr(0, 1);
|
||||
if ( ident == "i" ) {
|
||||
string side = msg.substr(1,1);
|
||||
string num = msg.substr(2,3);
|
||||
if ( side == "-" ) {
|
||||
fgSetDouble("/instrumentation/gps/cdi-deflection", 0.0);
|
||||
} else {
|
||||
int pos = atoi(num.c_str());
|
||||
if ( side == "L" ) {
|
||||
pos *= -1;
|
||||
}
|
||||
fgSetDouble("/instrumentation/gps/cdi-deflection",
|
||||
(double)pos / 8.0);
|
||||
fgSetBool("/instrumentation/gps/has-gs", false);
|
||||
}
|
||||
} else if ( ident == "k" ) {
|
||||
string ind = msg.substr(1,1);
|
||||
if ( ind == "T" ) {
|
||||
fgSetBool("/instrumentation/gps/to-flag", true);
|
||||
fgSetBool("/instrumentation/gps/from-flag", false);
|
||||
} else if ( ind == "F" ) {
|
||||
fgSetBool("/instrumentation/gps/to-flag", false);
|
||||
fgSetBool("/instrumentation/gps/from-flag", true);
|
||||
} else {
|
||||
fgSetBool("/instrumentation/gps/to-flag", false);
|
||||
fgSetBool("/instrumentation/gps/from-flag", false);
|
||||
}
|
||||
} else {
|
||||
// SG_LOG( SG_IO, SG_ALERT, "unknown AV400Sim message = " << msg );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// open hailing frequencies
|
||||
bool FGAV400Sim::open() {
|
||||
if ( is_enabled() ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||
<< "is already in use, ignoring" );
|
||||
return false;
|
||||
}
|
||||
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
if ( ! io->open( get_direction() ) ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
|
||||
return false;
|
||||
}
|
||||
|
||||
set_enabled( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// process work for this port
|
||||
bool FGAV400Sim::process() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
// until we have parsers/generators for the reverse direction,
|
||||
// this is hardwired to expect that the physical GPS is slaving
|
||||
// from FlightGear.
|
||||
|
||||
// Send FlightGear data to the external device
|
||||
gen_message();
|
||||
if ( ! io->write( buf, length ) ) {
|
||||
SG_LOG( SG_IO, SG_WARN, "Error writing data." );
|
||||
return false;
|
||||
}
|
||||
|
||||
// read the device messages back
|
||||
while ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
|
||||
// SG_LOG( SG_IO, SG_ALERT, "Success reading data." );
|
||||
if ( parse_message() ) {
|
||||
// SG_LOG( SG_IO, SG_ALERT, "Success parsing data." );
|
||||
} else {
|
||||
// SG_LOG( SG_IO, SG_ALERT, "Error parsing data." );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// close the channel
|
||||
bool FGAV400Sim::close() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
set_enabled( false );
|
||||
|
||||
if ( ! io->close() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
65
src/Network/AV400Sim.hxx
Normal file
65
src/Network/AV400Sim.hxx
Normal file
@@ -0,0 +1,65 @@
|
||||
// AV400Sim.hxx -- Garmin 400 series protocal class. This AV400Sim
|
||||
// protocol generates the set of "simulator" commands a garmin 400
|
||||
// series gps would expect as input in simulator mode. The AV400
|
||||
// protocol generates the set of commands that a garmin 400 series gps
|
||||
// would emit.
|
||||
//
|
||||
// Written by Curtis Olson, started Januar 2009.
|
||||
//
|
||||
// Copyright (C) 2009 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _FG_AV400SIM_HXX
|
||||
#define _FG_AV400SIM_HXX
|
||||
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "protocol.hxx"
|
||||
|
||||
class FlightProperties;
|
||||
|
||||
class FGAV400Sim : public FGProtocol {
|
||||
|
||||
char buf[ FG_MAX_MSG_SIZE ];
|
||||
int length;
|
||||
FlightProperties* fdm;
|
||||
|
||||
public:
|
||||
|
||||
FGAV400Sim();
|
||||
~FGAV400Sim();
|
||||
|
||||
bool gen_message();
|
||||
bool parse_message();
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_AV400SIM_HXX
|
||||
1049
src/Network/AV400WSim.cxx
Normal file
1049
src/Network/AV400WSim.cxx
Normal file
File diff suppressed because it is too large
Load Diff
134
src/Network/AV400WSim.hxx
Normal file
134
src/Network/AV400WSim.hxx
Normal file
@@ -0,0 +1,134 @@
|
||||
// AV400Sim.hxx -- Garmin 400 series protocal class. This AV400Sim
|
||||
// protocol generates the set of "simulator" commands a garmin 400
|
||||
// series gps would expect as input in simulator mode. The AV400
|
||||
// protocol generates the set of commands that a garmin 400 series gps
|
||||
// would emit.
|
||||
//
|
||||
// Written by Curtis Olson, started Januar 2009.
|
||||
//
|
||||
// Copyright (C) 2009 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "protocol.hxx"
|
||||
|
||||
class FlightProperties;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Class FGAV400WSimA handles the input/output over the first serial port.
|
||||
// This is very similar to the way previous Garmin non-WAAS models communicated
|
||||
// but some items have been stripped out and just a minimal amount of
|
||||
// info is necessary to be transmitted over this port.
|
||||
|
||||
class FGAV400WSimA : public FGProtocol {
|
||||
|
||||
char buf[ FG_MAX_MSG_SIZE ];
|
||||
int length;
|
||||
|
||||
public:
|
||||
|
||||
FGAV400WSimA();
|
||||
~FGAV400WSimA();
|
||||
|
||||
bool gen_message();
|
||||
bool parse_message();
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Class FGAV400WSimB handles the input/output over the second serial port
|
||||
// which Garmin refers to as the "GPS Port". Some messages are handled on
|
||||
// fixed cycle (usually 1 and 5 Hz) and some immediate responses are needed
|
||||
// to certain messages upon requet by the GPS unit
|
||||
|
||||
class FGAV400WSimB : public FGProtocol {
|
||||
|
||||
char buf[ FG_MAX_MSG_SIZE ];
|
||||
int length;
|
||||
double hz2;
|
||||
int hz2count;
|
||||
int hz2cycles;
|
||||
char flight_phase;
|
||||
std::string hal;
|
||||
std::string val;
|
||||
std::string sbas_sel;
|
||||
bool req_hostid;
|
||||
bool req_sbas;
|
||||
int outputctr;
|
||||
|
||||
FlightProperties* fdm;
|
||||
|
||||
static const int SOM_SIZE = 2;
|
||||
static const int DEG_TO_MILLIARCSECS = ( 60 * 60 * 1000 );
|
||||
|
||||
// Flight Phases
|
||||
static const int PHASE_OCEANIC = 4;
|
||||
static const int PHASE_ENROUTE = 5;
|
||||
static const int PHASE_TERM = 6;
|
||||
static const int PHASE_NONPREC = 7;
|
||||
static const int PHASE_LNAVVNAV = 8;
|
||||
static const int PHASE_LPVLP = 9;
|
||||
|
||||
public:
|
||||
|
||||
FGAV400WSimB();
|
||||
~FGAV400WSimB();
|
||||
|
||||
bool gen_hostid_message();
|
||||
bool gen_sbas_message();
|
||||
|
||||
bool gen_Wh_message();
|
||||
bool gen_Wx_message();
|
||||
|
||||
bool gen_Wt_message();
|
||||
bool gen_Wm_message();
|
||||
bool gen_Wv_message();
|
||||
|
||||
bool verify_checksum( std::string message, int datachars );
|
||||
std::string asciitize_message( std::string message );
|
||||
std::string buffer_to_string();
|
||||
bool parse_message();
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
|
||||
inline double get_hz2() const { return hz2; }
|
||||
inline void set_hz2( double t ) { hz2 = t, hz2cycles = get_hz() / hz2; }
|
||||
|
||||
};
|
||||
94
src/Network/CMakeLists.txt
Normal file
94
src/Network/CMakeLists.txt
Normal file
@@ -0,0 +1,94 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
ATC-Inputs.cxx
|
||||
ATC-Main.cxx
|
||||
ATC-Outputs.cxx
|
||||
AV400.cxx
|
||||
AV400Sim.cxx
|
||||
AV400WSim.cxx
|
||||
atlas.cxx
|
||||
garmin.cxx
|
||||
generic.cxx
|
||||
HTTPClient.cxx
|
||||
DNSClient.cxx
|
||||
flarm.cxx
|
||||
igc.cxx
|
||||
joyclient.cxx
|
||||
jsclient.cxx
|
||||
lfsglass.cxx
|
||||
native.cxx
|
||||
native_structs.cxx
|
||||
native_ctrls.cxx
|
||||
native_fdm.cxx
|
||||
native_gui.cxx
|
||||
nmea.cxx
|
||||
opengc.cxx
|
||||
props.cxx
|
||||
protocol.cxx
|
||||
pve.cxx
|
||||
ray.cxx
|
||||
rul.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
ATC-Inputs.hxx
|
||||
ATC-Main.hxx
|
||||
ATC-Outputs.hxx
|
||||
AV400.hxx
|
||||
AV400Sim.hxx
|
||||
AV400WSim.hxx
|
||||
atlas.hxx
|
||||
garmin.hxx
|
||||
generic.hxx
|
||||
HTTPClient.hxx
|
||||
DNSClient.hxx
|
||||
flarm.hxx
|
||||
igc.hxx
|
||||
joyclient.hxx
|
||||
jsclient.hxx
|
||||
lfsglass.hxx
|
||||
native.hxx
|
||||
native_ctrls.hxx
|
||||
native_ctrls.hxx
|
||||
native_fdm.hxx
|
||||
native_gui.hxx
|
||||
nmea.hxx
|
||||
opengc.hxx
|
||||
props.hxx
|
||||
protocol.hxx
|
||||
pve.hxx
|
||||
ray.hxx
|
||||
rul.hxx
|
||||
)
|
||||
|
||||
if (CycloneDDS_FOUND)
|
||||
list(APPEND SOURCES
|
||||
dds_props.cxx
|
||||
)
|
||||
|
||||
list(APPEND HEADERS
|
||||
dds_props.hxx
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ENABLE_IAX)
|
||||
list(APPEND SOURCES fgcom.cxx)
|
||||
list(APPEND HEADERS fgcom.hxx)
|
||||
endif()
|
||||
|
||||
flightgear_component(Network "${SOURCES}" "${HEADERS}")
|
||||
|
||||
if (CycloneDDS_FOUND)
|
||||
add_subdirectory(DDS)
|
||||
endif()
|
||||
|
||||
if(RTI_FOUND)
|
||||
add_subdirectory(HLA)
|
||||
endif()
|
||||
|
||||
add_subdirectory(http)
|
||||
|
||||
if(ENABLE_SWIFT)
|
||||
add_subdirectory(Swift)
|
||||
endif()
|
||||
34
src/Network/DDS/CMakeLists.txt
Normal file
34
src/Network/DDS/CMakeLists.txt
Normal file
@@ -0,0 +1,34 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
dds_ctrls.c
|
||||
dds_gui.c
|
||||
dds_fdm.c
|
||||
dds_props.c
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
dds_ctrls.h
|
||||
dds_fdm.h
|
||||
dds_gui.h
|
||||
dds_props.h
|
||||
)
|
||||
|
||||
add_executable(fg_dds_log
|
||||
WIN32
|
||||
MACOSX_BUNDLE
|
||||
fg_dds_log.cpp
|
||||
${SOURCES}
|
||||
)
|
||||
setup_fgfs_libraries(fg_dds_log)
|
||||
|
||||
add_executable(fg_dds_prop
|
||||
WIN32
|
||||
MACOSX_BUNDLE
|
||||
fg_dds_prop.cpp
|
||||
${SOURCES}
|
||||
)
|
||||
setup_fgfs_libraries(fg_dds_prop)
|
||||
|
||||
|
||||
flightgear_component(Network "${SOURCES}" "${HEADERS}")
|
||||
12
src/Network/DDS/README
Normal file
12
src/Network/DDS/README
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
Converting the OMG-IDL compatible interface definition files to C compatible
|
||||
header and code files can be done using the package provided by CycloneDDS,
|
||||
or any other OMG-IDL compatible IDL to C converter.
|
||||
|
||||
java -classpath "/usr/lib/cmake/CycloneDDS/idlc/idlc-jar-with-dependencies.jar"\
|
||||
org.eclipse.cyclonedds.compilers.Idlc dds_<type>.idl
|
||||
|
||||
The .idl files as well as the genereated dds_ctrls, dds_fdm and dds_gui header
|
||||
and souce files in this dirctory are in the Public Domain, and come with no
|
||||
warranty.
|
||||
|
||||
88
src/Network/DDS/dds_ctrls.c
Normal file
88
src/Network/DDS/dds_ctrls.c
Normal file
@@ -0,0 +1,88 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_ctrls.c
|
||||
Source: dds_ctrls.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
#include "dds_ctrls.h"
|
||||
|
||||
|
||||
static const dds_key_descriptor_t FG_DDS_Ctrls_keys[1] =
|
||||
{
|
||||
{ "id", 0 }
|
||||
};
|
||||
|
||||
static const uint32_t FG_DDS_Ctrls_ops [] =
|
||||
{
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN | DDS_OP_FLAG_KEY, offsetof (FG_DDS_Ctrls, id),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_Ctrls, version),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, aileron),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, elevator),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, rudder),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, aileron_trim),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, elevator_trim),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, rudder_trim),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, flaps),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, spoilers),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, speedbrake),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_Ctrls, flaps_power),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_Ctrls, flap_motor_ok),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_Ctrls, num_engines),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, master_bat), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, master_alt), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (FG_DDS_Ctrls, magnetos), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (FG_DDS_Ctrls, starter_power), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, throttle), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, mixture), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, condition), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY, offsetof (FG_DDS_Ctrls, fuel_pump_power), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, prop_advance), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (FG_DDS_Ctrls, feed_tank_to), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, reverse), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, engine_ok), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, mag_left_ok), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, mag_right_ok), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, spark_plugs_ok), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (FG_DDS_Ctrls, oil_press_status), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_Ctrls, fuel_pump_ok), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_Ctrls, num_tanks),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (FG_DDS_Ctrls, fuel_selector), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_Ctrls, cross_feed),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, brake_left),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, brake_right),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, copilot_brake_left),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, copilot_brake_right),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, brake_parking),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_Ctrls, gear_handle),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_Ctrls, master_avionics),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, comm_1),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, comm_2),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, nav_1),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, nav_2),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, wind_speed_kt),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, wind_dir_deg),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, turbulence_norm),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, temp_c),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, press_inhg),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, hground),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_Ctrls, magvar),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_Ctrls, icing),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY, offsetof (FG_DDS_Ctrls, speedup),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_Ctrls, freeze),
|
||||
DDS_OP_RTS
|
||||
};
|
||||
|
||||
const dds_topic_descriptor_t FG_DDS_Ctrls_desc =
|
||||
{
|
||||
sizeof (FG_DDS_Ctrls),
|
||||
4u,
|
||||
DDS_TOPIC_FIXED_KEY,
|
||||
1u,
|
||||
"FG::DDS_Ctrls",
|
||||
FG_DDS_Ctrls_keys,
|
||||
56,
|
||||
FG_DDS_Ctrls_ops,
|
||||
"<MetaData version=\"1.0.0\"><Module name=\"FG\"><Struct name=\"DDS_Ctrls\"><Member name=\"id\"><Short/></Member><Member name=\"version\"><Short/></Member><Member name=\"aileron\"><Float/></Member><Member name=\"elevator\"><Float/></Member><Member name=\"rudder\"><Float/></Member><Member name=\"aileron_trim\"><Float/></Member><Member name=\"elevator_trim\"><Float/></Member><Member name=\"rudder_trim\"><Float/></Member><Member name=\"flaps\"><Float/></Member><Member name=\"spoilers\"><Float/></Member><Member name=\"speedbrake\"><Float/></Member><Member name=\"flaps_power\"><Boolean/></Member><Member name=\"flap_motor_ok\"><Boolean/></Member><Member name=\"num_engines\"><UShort/></Member><Member name=\"master_bat\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"master_alt\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"magnetos\"><Array size=\"4\"><ULong/></Array></Member><Member name=\"starter_power\"><Array size=\"4\"><ULong/></Array></Member><Member name=\"throttle\"><Array size=\"4\"><Float/></Array></Member><Member name=\"mixture\"><Array size=\"4\"><Float/></Array></Member><Member name=\"condition\"><Array size=\"4\"><Float/></Array></Member><Member name=\"fuel_pump_power\"><Array size=\"4\"><ULong/></Array></Member><Member name=\"prop_advance\"><Array size=\"4\"><Float/></Array></Member><Member name=\"feed_tank_to\"><Array size=\"4\"><UShort/></Array></Member><Member name=\"reverse\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"engine_ok\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"mag_left_ok\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"mag_right_ok\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"spark_plugs_ok\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"oil_press_status\"><Array size=\"4\"><UShort/></Array></Member><Member name=\"fuel_pump_ok\"><Array size=\"4\"><Boolean/></Array></Member><Member name=\"num_tanks\"><UShort/></Member><Member name=\"fuel_selector\"><Array size=\"8\"><UShort/></Array></Member><Member name=\"cross_feed\"><Boolean/></Member><Member name=\"brake_left\"><Float/></Member><Member name=\"brake_right\"><Float/></Member><Member name=\"copilot_brake_left\"><Float/></Member><Member name=\"copilot_brake_right\"><Float/></Member><Member name=\"brake_parking\"><Float/></Member><Member name=\"gear_handle\"><Boolean/></Member><Member name=\"master_avionics\"><Boolean/></Member><Member name=\"comm_1\"><Float/></Member><Member name=\"comm_2\"><Float/></Member><Member name=\"nav_1\"><Float/></Member><Member name=\"nav_2\"><Float/></Member><Member name=\"wind_speed_kt\"><Float/></Member><Member name=\"wind_dir_deg\"><Float/></Member><Member name=\"turbulence_norm\"><Float/></Member><Member name=\"temp_c\"><Float/></Member><Member name=\"press_inhg\"><Float/></Member><Member name=\"hground\"><Float/></Member><Member name=\"magvar\"><Float/></Member><Member name=\"icing\"><Boolean/></Member><Member name=\"speedup\"><ULong/></Member><Member name=\"freeze\"><UShort/></Member></Struct></Module></MetaData>"
|
||||
};
|
||||
96
src/Network/DDS/dds_ctrls.h
Normal file
96
src/Network/DDS/dds_ctrls.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_ctrls.h
|
||||
Source: dds_ctrls.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
|
||||
#include "dds/ddsc/dds_public_impl.h"
|
||||
|
||||
#ifndef _DDSL_DDS_CTRLS_H_
|
||||
#define _DDSL_DDS_CTRLS_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FG_DDS_CTRLS_VERSION 1
|
||||
#define FG_DDS_ENGINES 4
|
||||
#define FG_DDS_WHEELS 16
|
||||
#define FG_DDS_TANKS 8
|
||||
|
||||
|
||||
typedef struct FG_DDS_Ctrls
|
||||
{
|
||||
int16_t id;
|
||||
int16_t version;
|
||||
float aileron;
|
||||
float elevator;
|
||||
float rudder;
|
||||
float aileron_trim;
|
||||
float elevator_trim;
|
||||
float rudder_trim;
|
||||
float flaps;
|
||||
float spoilers;
|
||||
float speedbrake;
|
||||
bool flaps_power;
|
||||
bool flap_motor_ok;
|
||||
uint16_t num_engines;
|
||||
bool master_bat[4];
|
||||
bool master_alt[4];
|
||||
uint32_t magnetos[4];
|
||||
uint32_t starter_power[4];
|
||||
float throttle[4];
|
||||
float mixture[4];
|
||||
float condition[4];
|
||||
uint32_t fuel_pump_power[4];
|
||||
float prop_advance[4];
|
||||
uint16_t feed_tank_to[4];
|
||||
bool reverse[4];
|
||||
bool engine_ok[4];
|
||||
bool mag_left_ok[4];
|
||||
bool mag_right_ok[4];
|
||||
bool spark_plugs_ok[4];
|
||||
uint16_t oil_press_status[4];
|
||||
bool fuel_pump_ok[4];
|
||||
uint16_t num_tanks;
|
||||
uint16_t fuel_selector[8];
|
||||
bool cross_feed;
|
||||
float brake_left;
|
||||
float brake_right;
|
||||
float copilot_brake_left;
|
||||
float copilot_brake_right;
|
||||
float brake_parking;
|
||||
bool gear_handle;
|
||||
bool master_avionics;
|
||||
float comm_1;
|
||||
float comm_2;
|
||||
float nav_1;
|
||||
float nav_2;
|
||||
float wind_speed_kt;
|
||||
float wind_dir_deg;
|
||||
float turbulence_norm;
|
||||
float temp_c;
|
||||
float press_inhg;
|
||||
float hground;
|
||||
float magvar;
|
||||
bool icing;
|
||||
uint32_t speedup;
|
||||
uint16_t freeze;
|
||||
} FG_DDS_Ctrls;
|
||||
|
||||
extern const dds_topic_descriptor_t FG_DDS_Ctrls_desc;
|
||||
|
||||
#define FG_DDS_Ctrls__alloc() \
|
||||
((FG_DDS_Ctrls*) dds_alloc (sizeof (FG_DDS_Ctrls)));
|
||||
|
||||
#define FG_DDS_Ctrls_free(d,o) \
|
||||
dds_sample_free ((d), &FG_DDS_Ctrls_desc, (o))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _DDSL_DDS_CTRLS_H_ */
|
||||
113
src/Network/DDS/dds_ctrls.idl
Normal file
113
src/Network/DDS/dds_ctrls.idl
Normal file
@@ -0,0 +1,113 @@
|
||||
// format dfescription: https://www.omg.org/spec/IDL/4.2/PDF
|
||||
|
||||
// Adapted from net_ctrls.hxx
|
||||
module FG
|
||||
{
|
||||
|
||||
// defining it this way also generates accompanying #defines in the header file.
|
||||
|
||||
const short DDS_CTRLS_VERSION = 1;
|
||||
|
||||
const short DDS_ENGINES = 4;
|
||||
const short DDS_WHEELS = 16;
|
||||
const short DDS_TANKS = 8;
|
||||
|
||||
struct DDS_Ctrls
|
||||
{
|
||||
short id; // DDS id
|
||||
short version; // packet version
|
||||
|
||||
// Aero controls
|
||||
// net_ctrl.hxx defines doubles but flouts do have enough precission
|
||||
float aileron; // -1 ... 1
|
||||
float elevator; // -1 ... 1
|
||||
float rudder; // -1 ... 1
|
||||
float aileron_trim; // -1 ... 1
|
||||
float elevator_trim; // -1 ... 1
|
||||
float rudder_trim; // -1 ... 1
|
||||
float flaps; // 0 ... 1
|
||||
float spoilers;
|
||||
float speedbrake;
|
||||
|
||||
// Aero control faults
|
||||
boolean flaps_power; // true = power available
|
||||
boolean flap_motor_ok;
|
||||
|
||||
// Engine controls
|
||||
unsigned short num_engines; // number of valid engines
|
||||
boolean master_bat[DDS_ENGINES];
|
||||
boolean master_alt[DDS_ENGINES];
|
||||
unsigned long magnetos[DDS_ENGINES];
|
||||
unsigned long starter_power[DDS_ENGINES]; // true = starter power
|
||||
float throttle[DDS_ENGINES]; // 0 ... 1
|
||||
float mixture[DDS_ENGINES]; // 0 ... 1
|
||||
float condition[DDS_ENGINES]; // 0 ... 1
|
||||
unsigned long fuel_pump_power[DDS_ENGINES]; // true = on
|
||||
float prop_advance[DDS_ENGINES]; // 0 ... 1
|
||||
unsigned short feed_tank_to[4];
|
||||
boolean reverse[4];
|
||||
|
||||
|
||||
// Engine faults
|
||||
boolean engine_ok[DDS_ENGINES];
|
||||
boolean mag_left_ok[DDS_ENGINES];
|
||||
boolean mag_right_ok[DDS_ENGINES];
|
||||
boolean spark_plugs_ok[DDS_ENGINES]; // false = fouled plugs
|
||||
unsigned short oil_press_status[DDS_ENGINES];// 0 = normal, 1 = low, 2 = full fail
|
||||
boolean fuel_pump_ok[DDS_ENGINES];
|
||||
|
||||
// Fuel management
|
||||
unsigned short num_tanks; // number of valid tanks
|
||||
unsigned short fuel_selector[DDS_TANKS]; // false = off, true = on
|
||||
// unsigned short xfer_pump[5]; // specifies transfer from array
|
||||
// value tank to tank specified
|
||||
// by int value
|
||||
boolean cross_feed; // false = off, true = on
|
||||
|
||||
// Brake controls
|
||||
float brake_left;
|
||||
float brake_right;
|
||||
float copilot_brake_left;
|
||||
float copilot_brake_right;
|
||||
float brake_parking;
|
||||
|
||||
// Landing Gear
|
||||
boolean gear_handle; // true=gear handle down; false= gear handle up
|
||||
|
||||
// Switches
|
||||
boolean master_avionics;
|
||||
|
||||
// nav and Comm
|
||||
float comm_1;
|
||||
float comm_2;
|
||||
float nav_1;
|
||||
float nav_2;
|
||||
|
||||
// wind and turbulance
|
||||
float wind_speed_kt;
|
||||
float wind_dir_deg;
|
||||
float turbulence_norm;
|
||||
|
||||
// temp and pressure
|
||||
float temp_c;
|
||||
float press_inhg;
|
||||
|
||||
// other information about environment
|
||||
float hground; // ground elevation (meters)
|
||||
float magvar; // local magnetic variation in degs.
|
||||
|
||||
// hazards
|
||||
boolean icing; // icing status could me much
|
||||
// more complex but I'm
|
||||
// starting simple here.
|
||||
|
||||
// simulation control
|
||||
unsigned long speedup; // integer speedup multiplier
|
||||
unsigned short freeze; // 0=normal
|
||||
// 0x01=master
|
||||
// 0x02=position
|
||||
// 0x04=fuel
|
||||
};
|
||||
#pragma keylist DDS_Ctrls id
|
||||
|
||||
}; // module FG
|
||||
96
src/Network/DDS/dds_fdm.c
Normal file
96
src/Network/DDS/dds_fdm.c
Normal file
@@ -0,0 +1,96 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_fdm.c
|
||||
Source: dds_fdm.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
#include "dds_fdm.h"
|
||||
|
||||
|
||||
static const dds_key_descriptor_t FG_DDS_FDM_keys[1] =
|
||||
{
|
||||
{ "id", 0 }
|
||||
};
|
||||
|
||||
static const uint32_t FG_DDS_FDM_ops [] =
|
||||
{
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN | DDS_OP_FLAG_KEY, offsetof (FG_DDS_FDM, id),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_FDM, version),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, longitude),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, latitude),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, altitude),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, agl),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, phi),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, theta),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, psi),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, alpha),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, beta),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, phidot),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, thetadot),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, psidot),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, vcas),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, climb_rate),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, v_north),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, v_east),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, v_down),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, v_body_u),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, v_body_v),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, v_body_w),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, A_X_pilot),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, A_Y_pilot),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, A_Z_pilot),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, stall_warning),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, slip_deg),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_FDM, num_engines),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (FG_DDS_FDM, eng_state), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, rpm), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, fuel_flow), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, fuel_px), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, egt), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, cht), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, mp_osi), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, tit), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, oil_temp), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, oil_px), 4,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_FDM, num_tanks),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, fuel_quantity), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_BOO, offsetof (FG_DDS_FDM, tank_selected), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, capacity_m3), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, unusable_m3), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, density_kgpm3), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, level_m3), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_FDM, num_wheels),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_2BY, offsetof (FG_DDS_FDM, wow), 16,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, gear_pos), 16,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, gear_steer), 16,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, gear_compression), 16,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY, offsetof (FG_DDS_FDM, cur_time),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY, offsetof (FG_DDS_FDM, warp),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, visibility),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, elevator),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, elevator_trim_tab),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, left_flap),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, right_flap),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, left_aileron),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, right_aileron),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, rudder),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, nose_wheel),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, speedbrake),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_FDM, spoilers),
|
||||
DDS_OP_RTS
|
||||
};
|
||||
|
||||
const dds_topic_descriptor_t FG_DDS_FDM_desc =
|
||||
{
|
||||
sizeof (FG_DDS_FDM),
|
||||
8u,
|
||||
DDS_TOPIC_FIXED_KEY,
|
||||
1u,
|
||||
"FG::DDS_FDM",
|
||||
FG_DDS_FDM_keys,
|
||||
64,
|
||||
FG_DDS_FDM_ops,
|
||||
"<MetaData version=\"1.0.0\"><Module name=\"FG\"><Struct name=\"DDS_FDM\"><Member name=\"id\"><Short/></Member><Member name=\"version\"><Short/></Member><Member name=\"longitude\"><Double/></Member><Member name=\"latitude\"><Double/></Member><Member name=\"altitude\"><Double/></Member><Member name=\"agl\"><Float/></Member><Member name=\"phi\"><Float/></Member><Member name=\"theta\"><Float/></Member><Member name=\"psi\"><Float/></Member><Member name=\"alpha\"><Float/></Member><Member name=\"beta\"><Float/></Member><Member name=\"phidot\"><Float/></Member><Member name=\"thetadot\"><Float/></Member><Member name=\"psidot\"><Float/></Member><Member name=\"vcas\"><Float/></Member><Member name=\"climb_rate\"><Float/></Member><Member name=\"v_north\"><Float/></Member><Member name=\"v_east\"><Float/></Member><Member name=\"v_down\"><Float/></Member><Member name=\"v_body_u\"><Float/></Member><Member name=\"v_body_v\"><Float/></Member><Member name=\"v_body_w\"><Float/></Member><Member name=\"A_X_pilot\"><Float/></Member><Member name=\"A_Y_pilot\"><Float/></Member><Member name=\"A_Z_pilot\"><Float/></Member><Member name=\"stall_warning\"><Float/></Member><Member name=\"slip_deg\"><Float/></Member><Member name=\"num_engines\"><UShort/></Member><Member name=\"eng_state\"><Array size=\"4\"><UShort/></Array></Member><Member name=\"rpm\"><Array size=\"4\"><Float/></Array></Member><Member name=\"fuel_flow\"><Array size=\"4\"><Float/></Array></Member><Member name=\"fuel_px\"><Array size=\"4\"><Float/></Array></Member><Member name=\"egt\"><Array size=\"4\"><Float/></Array></Member><Member name=\"cht\"><Array size=\"4\"><Float/></Array></Member><Member name=\"mp_osi\"><Array size=\"4\"><Float/></Array></Member><Member name=\"tit\"><Array size=\"4\"><Float/></Array></Member><Member name=\"oil_temp\"><Array size=\"4\"><Float/></Array></Member><Member name=\"oil_px\"><Array size=\"4\"><Float/></Array></Member><Member name=\"num_tanks\"><UShort/></Member><Member name=\"fuel_quantity\"><Array size=\"8\"><Float/></Array></Member><Member name=\"tank_selected\"><Array size=\"8\"><Boolean/></Array></Member><Member name=\"capacity_m3\"><Array size=\"8\"><Float/></Array></Member><Member name=\"unusable_m3\"><Array size=\"8\"><Float/></Array></Member><Member name=\"density_kgpm3\"><Array size=\"8\"><Float/></Array></Member><Member name=\"level_m3\"><Array size=\"8\"><Float/></Array></Member><Member name=\"num_wheels\"><UShort/></Member><Member name=\"wow\"><Array size=\"16\"><UShort/></Array></Member><Member name=\"gear_pos\"><Array size=\"16\"><Float/></Array></Member><Member name=\"gear_steer\"><Array size=\"16\"><Float/></Array></Member><Member name=\"gear_compression\"><Array size=\"16\"><Float/></Array></Member><Member name=\"cur_time\"><ULongLong/></Member><Member name=\"warp\"><ULongLong/></Member><Member name=\"visibility\"><Float/></Member><Member name=\"elevator\"><Float/></Member><Member name=\"elevator_trim_tab\"><Float/></Member><Member name=\"left_flap\"><Float/></Member><Member name=\"right_flap\"><Float/></Member><Member name=\"left_aileron\"><Float/></Member><Member name=\"right_aileron\"><Float/></Member><Member name=\"rudder\"><Float/></Member><Member name=\"nose_wheel\"><Float/></Member><Member name=\"speedbrake\"><Float/></Member><Member name=\"spoilers\"><Float/></Member></Struct></Module></MetaData>"
|
||||
};
|
||||
104
src/Network/DDS/dds_fdm.h
Normal file
104
src/Network/DDS/dds_fdm.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_fdm.h
|
||||
Source: dds_fdm.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
|
||||
#include "dds/ddsc/dds_public_impl.h"
|
||||
|
||||
#ifndef _DDSL_DDS_FDM_H_
|
||||
#define _DDSL_DDS_FDM_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FG_DDS_FDM_VERSION 1
|
||||
#define FG_DDS_ENGINES 4
|
||||
#define FG_DDS_WHEELS 16
|
||||
#define FG_DDS_TANKS 8
|
||||
|
||||
|
||||
typedef struct FG_DDS_FDM
|
||||
{
|
||||
int16_t id;
|
||||
int16_t version;
|
||||
double longitude;
|
||||
double latitude;
|
||||
double altitude;
|
||||
float agl;
|
||||
float phi;
|
||||
float theta;
|
||||
float psi;
|
||||
float alpha;
|
||||
float beta;
|
||||
float phidot;
|
||||
float thetadot;
|
||||
float psidot;
|
||||
float vcas;
|
||||
float climb_rate;
|
||||
float v_north;
|
||||
float v_east;
|
||||
float v_down;
|
||||
float v_body_u;
|
||||
float v_body_v;
|
||||
float v_body_w;
|
||||
float A_X_pilot;
|
||||
float A_Y_pilot;
|
||||
float A_Z_pilot;
|
||||
float stall_warning;
|
||||
float slip_deg;
|
||||
uint16_t num_engines;
|
||||
uint16_t eng_state[4];
|
||||
float rpm[4];
|
||||
float fuel_flow[4];
|
||||
float fuel_px[4];
|
||||
float egt[4];
|
||||
float cht[4];
|
||||
float mp_osi[4];
|
||||
float tit[4];
|
||||
float oil_temp[4];
|
||||
float oil_px[4];
|
||||
uint16_t num_tanks;
|
||||
float fuel_quantity[8];
|
||||
bool tank_selected[8];
|
||||
float capacity_m3[8];
|
||||
float unusable_m3[8];
|
||||
float density_kgpm3[8];
|
||||
float level_m3[8];
|
||||
uint16_t num_wheels;
|
||||
uint16_t wow[16];
|
||||
float gear_pos[16];
|
||||
float gear_steer[16];
|
||||
float gear_compression[16];
|
||||
uint64_t cur_time;
|
||||
uint64_t warp;
|
||||
float visibility;
|
||||
float elevator;
|
||||
float elevator_trim_tab;
|
||||
float left_flap;
|
||||
float right_flap;
|
||||
float left_aileron;
|
||||
float right_aileron;
|
||||
float rudder;
|
||||
float nose_wheel;
|
||||
float speedbrake;
|
||||
float spoilers;
|
||||
} FG_DDS_FDM;
|
||||
|
||||
extern const dds_topic_descriptor_t FG_DDS_FDM_desc;
|
||||
|
||||
#define FG_DDS_FDM__alloc() \
|
||||
((FG_DDS_FDM*) dds_alloc (sizeof (FG_DDS_FDM)));
|
||||
|
||||
#define FG_DDS_FDM_free(d,o) \
|
||||
dds_sample_free ((d), &FG_DDS_FDM_desc, (o))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _DDSL_DDS_FDM_H_ */
|
||||
106
src/Network/DDS/dds_fdm.idl
Normal file
106
src/Network/DDS/dds_fdm.idl
Normal file
@@ -0,0 +1,106 @@
|
||||
// format dfescription: https://www.omg.org/spec/IDL/4.2/PDF
|
||||
|
||||
// Adapted from net_fdm.hxx
|
||||
module FG
|
||||
{
|
||||
|
||||
// defining it this way also generates accompanying #defines in the header file.
|
||||
|
||||
const short DDS_FDM_VERSION = 1;
|
||||
|
||||
// same as defined in net_ctrls.hxx
|
||||
// this is different from net_fdm.hxx
|
||||
const short DDS_ENGINES = 4;
|
||||
const short DDS_WHEELS = 16;
|
||||
const short DDS_TANKS = 8;
|
||||
|
||||
struct DDS_FDM
|
||||
{
|
||||
short id; // DDS id
|
||||
short version; // packet version
|
||||
|
||||
// Positions
|
||||
double longitude; // geodetic (radians)
|
||||
double latitude; // geodetic (radians)
|
||||
double altitude; // above sea level (meters)
|
||||
float agl; // above ground level (meters)
|
||||
float phi; // roll (radians)
|
||||
float theta; // pitch (radians)
|
||||
float psi; // yaw or true heading (radians)
|
||||
float alpha; // angle of attack (radians)
|
||||
float beta; // side slip angle (radians)
|
||||
|
||||
// Velocities
|
||||
float phidot; // roll rate (radians/sec)
|
||||
float thetadot; // pitch rate (radians/sec)
|
||||
float psidot; // yaw rate (radians/sec)
|
||||
float vcas; // calibrated airspeed
|
||||
float climb_rate; // feet per second
|
||||
float v_north; // north velocity in local/body frame, fps
|
||||
float v_east; // east velocity in local/body frame, fps
|
||||
float v_down; // down/vertical velocity in local/body frame, fps
|
||||
float v_body_u; // ECEF velocity in body frame
|
||||
float v_body_v; // ECEF velocity in body frame
|
||||
float v_body_w; // ECEF velocity in body frame
|
||||
|
||||
// Accelerations
|
||||
float A_X_pilot; // X accel in body frame ft/sec^2
|
||||
float A_Y_pilot; // Y accel in body frame ft/sec^2
|
||||
float A_Z_pilot; // Z accel in body frame ft/sec^2
|
||||
|
||||
// Stall
|
||||
float stall_warning;// 0.0 - 1.0 indicating the amount of stall
|
||||
float slip_deg; // slip ball deflection
|
||||
|
||||
// Pressure
|
||||
|
||||
// Engine status
|
||||
unsigned short num_engines; // Number of valid engines
|
||||
unsigned short eng_state[DDS_ENGINES];// Engine state (off, cranking, running)
|
||||
float rpm[DDS_ENGINES]; // Engine RPM rev/min
|
||||
float fuel_flow[DDS_ENGINES]; // Fuel flow gallons/hr
|
||||
float fuel_px[DDS_ENGINES]; // Fuel pressure psi
|
||||
float egt[DDS_ENGINES]; // Exhuast gas temp deg F
|
||||
float cht[DDS_ENGINES]; // Cylinder head temp deg F
|
||||
float mp_osi[DDS_ENGINES]; // Manifold pressure
|
||||
float tit[DDS_ENGINES]; // Turbine Inlet Temperature
|
||||
float oil_temp[DDS_ENGINES]; // Oil temp deg F
|
||||
float oil_px[DDS_ENGINES]; // Oil pressure psi
|
||||
|
||||
// Consumables
|
||||
unsigned short num_tanks; // Max number of fuel tanks
|
||||
float fuel_quantity[DDS_TANKS]; // used by GPSsmooth and possibly others
|
||||
boolean tank_selected[DDS_TANKS]; // selected, capacity, usable, density and level required for multiple-pc setups to work
|
||||
float capacity_m3[DDS_TANKS];
|
||||
float unusable_m3[DDS_TANKS];
|
||||
float density_kgpm3[DDS_TANKS];
|
||||
float level_m3[DDS_TANKS];
|
||||
|
||||
|
||||
// Gear status
|
||||
unsigned short num_wheels;
|
||||
unsigned short wow[DDS_WHEELS];
|
||||
float gear_pos[DDS_WHEELS];
|
||||
float gear_steer[DDS_WHEELS];
|
||||
float gear_compression[DDS_WHEELS];
|
||||
|
||||
// Environment
|
||||
unsigned long long cur_time;// current unix time, 64-bit
|
||||
unsigned long long warp; // offset in seconds to unix time
|
||||
float visibility; // visibility in meters (for env. effects)
|
||||
|
||||
// Control surface positions (normalized values)
|
||||
float elevator;
|
||||
float elevator_trim_tab;
|
||||
float left_flap;
|
||||
float right_flap;
|
||||
float left_aileron;
|
||||
float right_aileron;
|
||||
float rudder;
|
||||
float nose_wheel;
|
||||
float speedbrake;
|
||||
float spoilers;
|
||||
};
|
||||
#pragma keylist DDS_FDM id
|
||||
|
||||
}; // module FG
|
||||
10
src/Network/DDS/dds_fwd.hxx
Normal file
10
src/Network/DDS/dds_fwd.hxx
Normal file
@@ -0,0 +1,10 @@
|
||||
// dds_fwd.hxx
|
||||
//
|
||||
// This file is in the Public Domain, and comes with no warranty.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "dds_gui.h"
|
||||
#include "dds_fdm.h"
|
||||
#include "dds_ctrls.h"
|
||||
#include "dds_props.h"
|
||||
55
src/Network/DDS/dds_gui.c
Normal file
55
src/Network/DDS/dds_gui.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_gui.c
|
||||
Source: dds_gui.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
#include "dds_gui.h"
|
||||
|
||||
|
||||
static const dds_key_descriptor_t FG_DDS_GUI_keys[1] =
|
||||
{
|
||||
{ "id", 0 }
|
||||
};
|
||||
|
||||
static const uint32_t FG_DDS_GUI_ops [] =
|
||||
{
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN | DDS_OP_FLAG_KEY, offsetof (FG_DDS_GUI, id),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_GUI, version),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, longitude),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, latitude),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, altitude),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, agl),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, phi),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, theta),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, psi),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, vcas),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, climb_rate),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_2BY, offsetof (FG_DDS_GUI, num_tanks),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, fuel_quantity), 8,
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY, offsetof (FG_DDS_GUI, cur_time),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_8BY, offsetof (FG_DDS_GUI, warp),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, ground_elev),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, tuned_freq),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, nav_radial),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_GUI, in_range),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, dist_nm),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, course_deviation_deg),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP, offsetof (FG_DDS_GUI, gs_deviation_deg),
|
||||
DDS_OP_RTS
|
||||
};
|
||||
|
||||
const dds_topic_descriptor_t FG_DDS_GUI_desc =
|
||||
{
|
||||
sizeof (FG_DDS_GUI),
|
||||
8u,
|
||||
DDS_TOPIC_FIXED_KEY,
|
||||
1u,
|
||||
"FG::DDS_GUI",
|
||||
FG_DDS_GUI_keys,
|
||||
23,
|
||||
FG_DDS_GUI_ops,
|
||||
"<MetaData version=\"1.0.0\"><Module name=\"FG\"><Struct name=\"DDS_GUI\"><Member name=\"id\"><Short/></Member><Member name=\"version\"><Short/></Member><Member name=\"longitude\"><Double/></Member><Member name=\"latitude\"><Double/></Member><Member name=\"altitude\"><Float/></Member><Member name=\"agl\"><Float/></Member><Member name=\"phi\"><Float/></Member><Member name=\"theta\"><Float/></Member><Member name=\"psi\"><Float/></Member><Member name=\"vcas\"><Float/></Member><Member name=\"climb_rate\"><Float/></Member><Member name=\"num_tanks\"><UShort/></Member><Member name=\"fuel_quantity\"><Array size=\"8\"><Float/></Array></Member><Member name=\"cur_time\"><ULongLong/></Member><Member name=\"warp\"><ULongLong/></Member><Member name=\"ground_elev\"><Float/></Member><Member name=\"tuned_freq\"><Float/></Member><Member name=\"nav_radial\"><Float/></Member><Member name=\"in_range\"><Boolean/></Member><Member name=\"dist_nm\"><Float/></Member><Member name=\"course_deviation_deg\"><Float/></Member><Member name=\"gs_deviation_deg\"><Float/></Member></Struct></Module></MetaData>"
|
||||
};
|
||||
61
src/Network/DDS/dds_gui.h
Normal file
61
src/Network/DDS/dds_gui.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_gui.h
|
||||
Source: dds_gui.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
|
||||
#include "dds/ddsc/dds_public_impl.h"
|
||||
|
||||
#ifndef _DDSL_DDS_GUI_H_
|
||||
#define _DDSL_DDS_GUI_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FG_DDS_GUI_VERSION 1
|
||||
#define FG_DDS_TANKS 8
|
||||
|
||||
|
||||
typedef struct FG_DDS_GUI
|
||||
{
|
||||
int16_t id;
|
||||
int16_t version;
|
||||
double longitude;
|
||||
double latitude;
|
||||
float altitude;
|
||||
float agl;
|
||||
float phi;
|
||||
float theta;
|
||||
float psi;
|
||||
float vcas;
|
||||
float climb_rate;
|
||||
uint16_t num_tanks;
|
||||
float fuel_quantity[8];
|
||||
uint64_t cur_time;
|
||||
uint64_t warp;
|
||||
float ground_elev;
|
||||
float tuned_freq;
|
||||
float nav_radial;
|
||||
bool in_range;
|
||||
float dist_nm;
|
||||
float course_deviation_deg;
|
||||
float gs_deviation_deg;
|
||||
} FG_DDS_GUI;
|
||||
|
||||
extern const dds_topic_descriptor_t FG_DDS_GUI_desc;
|
||||
|
||||
#define FG_DDS_GUI__alloc() \
|
||||
((FG_DDS_GUI*) dds_alloc (sizeof (FG_DDS_GUI)));
|
||||
|
||||
#define FG_DDS_GUI_free(d,o) \
|
||||
dds_sample_free ((d), &FG_DDS_GUI_desc, (o))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _DDSL_DDS_GUI_H_ */
|
||||
55
src/Network/DDS/dds_gui.idl
Normal file
55
src/Network/DDS/dds_gui.idl
Normal file
@@ -0,0 +1,55 @@
|
||||
// format dfescription: https://www.omg.org/spec/IDL/4.2/PDF
|
||||
|
||||
// Taken from net_gui.hxx
|
||||
|
||||
module FG
|
||||
{
|
||||
|
||||
// defining it this way also generates accompanying #defines in the header file.
|
||||
|
||||
const short DDS_GUI_VERSION = 1;
|
||||
|
||||
// same as defined in net_ctrls.hxx
|
||||
// this is different from net_gui.hxx
|
||||
const short DDS_TANKS = 8;
|
||||
|
||||
struct DDS_GUI
|
||||
{
|
||||
short id;
|
||||
short version;
|
||||
|
||||
// Positions
|
||||
double longitude; // geodetic (radians)
|
||||
double latitude; // geodetic (radians)
|
||||
|
||||
float altitude; // above sea level (meters)
|
||||
float agl; // above ground level (meters)
|
||||
float phi; // roll (radians)
|
||||
float theta; // pitch (radians)
|
||||
float psi; // yaw or true heading (radians)
|
||||
|
||||
// Velocities
|
||||
float vcas;
|
||||
float climb_rate; // feet per second
|
||||
|
||||
// Consumables
|
||||
unsigned short num_tanks; // Max number of fuel tanks
|
||||
float fuel_quantity[DDS_TANKS];
|
||||
|
||||
// Environment
|
||||
unsigned long long cur_time;// current unix time
|
||||
unsigned long long warp; // offset in seconds to unix time
|
||||
float ground_elev; // ground elev (meters)
|
||||
|
||||
// Approach
|
||||
float tuned_freq; // currently tuned frequency
|
||||
float nav_radial; // target nav radial
|
||||
boolean in_range; // tuned navaid is in range?
|
||||
float dist_nm; // distance to tuned navaid in nautical miles
|
||||
float course_deviation_deg; // degrees off target course
|
||||
float gs_deviation_deg; // degrees off target glide slope
|
||||
};
|
||||
#pragma keylist DDS_GUI id
|
||||
|
||||
}; // module FG
|
||||
|
||||
47
src/Network/DDS/dds_props.c
Normal file
47
src/Network/DDS/dds_props.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_props.c
|
||||
Source: dds_props.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
#include "dds_props.h"
|
||||
|
||||
|
||||
static const dds_key_descriptor_t FG_DDS_prop_keys[1] =
|
||||
{
|
||||
{ "id", 0 }
|
||||
};
|
||||
|
||||
static const uint32_t FG_DDS_prop_ops [] =
|
||||
{
|
||||
DDS_OP_ADR | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | DDS_OP_FLAG_KEY, offsetof (FG_DDS_prop, id),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_1BY, offsetof (FG_DDS_prop, version),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_BOO, offsetof (FG_DDS_prop, mode),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_UNI | DDS_OP_SUBTYPE_4BY | DDS_OP_FLAG_SGN, offsetof (FG_DDS_prop, val._d), 9u, (31u << 16) + 4u,
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_BOO | 0, FG_DDS_BOOL, offsetof (FG_DDS_prop, val._u.Bool),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | 0, FG_DDS_NONE, offsetof (FG_DDS_prop, val._u.Int32),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_SGN | 0, FG_DDS_INT, offsetof (FG_DDS_prop, val._u.Int32),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_8BY | DDS_OP_FLAG_SGN | 0, FG_DDS_LONG, offsetof (FG_DDS_prop, val._u.Int64),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_4BY | DDS_OP_FLAG_FP | 0, FG_DDS_FLOAT, offsetof (FG_DDS_prop, val._u.Float32),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_8BY | DDS_OP_FLAG_FP | 0, FG_DDS_DOUBLE, offsetof (FG_DDS_prop, val._u.Float64),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_ALIAS, offsetof (FG_DDS_prop, val._u.String),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_STRING, offsetof (FG_DDS_prop, val._u.String),
|
||||
DDS_OP_JEQ | DDS_OP_TYPE_STR | 0, FG_DDS_UNSPECIFIED, offsetof (FG_DDS_prop, val._u.String),
|
||||
DDS_OP_ADR | DDS_OP_TYPE_ARR | DDS_OP_SUBTYPE_1BY, offsetof (FG_DDS_prop, guid), 16,
|
||||
DDS_OP_RTS
|
||||
};
|
||||
|
||||
const dds_topic_descriptor_t FG_DDS_prop_desc =
|
||||
{
|
||||
sizeof (FG_DDS_prop),
|
||||
8u,
|
||||
DDS_TOPIC_FIXED_KEY | DDS_TOPIC_NO_OPTIMIZE | DDS_TOPIC_CONTAINS_UNION,
|
||||
1u,
|
||||
"FG::DDS_prop",
|
||||
FG_DDS_prop_keys,
|
||||
15,
|
||||
FG_DDS_prop_ops,
|
||||
"<MetaData version=\"1.0.0\"><Module name=\"FG\"><Enum name=\"propType\"><Element name=\"DDS_NONE\" value=\"0\"/><Element name=\"DDS_ALIAS\" value=\"1\"/><Element name=\"DDS_BOOL\" value=\"2\"/><Element name=\"DDS_INT\" value=\"3\"/><Element name=\"DDS_LONG\" value=\"4\"/><Element name=\"DDS_FLOAT\" value=\"5\"/><Element name=\"DDS_DOUBLE\" value=\"6\"/><Element name=\"DDS_STRING\" value=\"7\"/><Element name=\"DDS_UNSPECIFIED\" value=\"8\"/></Enum><Union name=\"propValue\"><SwitchType><Type name=\"propType\"/></SwitchType><Case name=\"Bool\"><Boolean/><Label value=\"DDS_BOOL\"/></Case><Case name=\"Int32\"><Long/><Label value=\"DDS_NONE\"/><Label value=\"DDS_INT\"/></Case><Case name=\"Int64\"><LongLong/><Label value=\"DDS_LONG\"/></Case><Case name=\"Float32\"><Float/><Label value=\"DDS_FLOAT\"/></Case><Case name=\"Float64\"><Double/><Label value=\"DDS_DOUBLE\"/></Case><Case name=\"String\"><String/><Label value=\"DDS_ALIAS\"/><Label value=\"DDS_STRING\"/><Label value=\"DDS_UNSPECIFIED\"/></Case></Union><Struct name=\"DDS_prop\"><Member name=\"id\"><Long/></Member><Member name=\"version\"><Octet/></Member><Member name=\"mode\"><Boolean/></Member><Member name=\"val\"><Type name=\"propValue\"/></Member><Member name=\"guid\"><Array size=\"16\"><Octet/></Array></Member></Struct></Module></MetaData>"
|
||||
};
|
||||
79
src/Network/DDS/dds_props.h
Normal file
79
src/Network/DDS/dds_props.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/****************************************************************
|
||||
|
||||
Generated by Eclipse Cyclone DDS IDL to C Translator
|
||||
File name: dds_props.h
|
||||
Source: dds_props.idl
|
||||
Cyclone DDS: V0.7.0
|
||||
|
||||
*****************************************************************/
|
||||
|
||||
#include "dds/ddsc/dds_public_impl.h"
|
||||
|
||||
#ifndef _DDSL_DDS_PROPS_H_
|
||||
#define _DDSL_DDS_PROPS_H_
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define FG_DDS_PROP_VERSION 0
|
||||
#define FG_DDS_PROP_REQUEST -1
|
||||
#define FG_DDS_MODE_READ (0)
|
||||
#define FG_DDS_MODE_WRITE (1)
|
||||
typedef enum FG_propType
|
||||
{
|
||||
FG_DDS_NONE,
|
||||
FG_DDS_ALIAS,
|
||||
FG_DDS_BOOL,
|
||||
FG_DDS_INT,
|
||||
FG_DDS_LONG,
|
||||
FG_DDS_FLOAT,
|
||||
FG_DDS_DOUBLE,
|
||||
FG_DDS_STRING,
|
||||
FG_DDS_UNSPECIFIED
|
||||
} FG_propType;
|
||||
|
||||
#define FG_propType__alloc() \
|
||||
((FG_propType*) dds_alloc (sizeof (FG_propType)));
|
||||
|
||||
|
||||
typedef struct FG_propValue
|
||||
{
|
||||
FG_propType _d;
|
||||
union
|
||||
{
|
||||
bool Bool;
|
||||
int32_t Int32;
|
||||
int64_t Int64;
|
||||
float Float32;
|
||||
double Float64;
|
||||
char * String;
|
||||
} _u;
|
||||
} FG_propValue;
|
||||
|
||||
#define FG_propValue__alloc() \
|
||||
((FG_propValue*) dds_alloc (sizeof (FG_propValue)));
|
||||
|
||||
|
||||
typedef struct FG_DDS_prop
|
||||
{
|
||||
int32_t id;
|
||||
uint8_t version;
|
||||
bool mode;
|
||||
FG_propValue val;
|
||||
uint8_t guid[16];
|
||||
} FG_DDS_prop;
|
||||
|
||||
extern const dds_topic_descriptor_t FG_DDS_prop_desc;
|
||||
|
||||
#define FG_DDS_prop__alloc() \
|
||||
((FG_DDS_prop*) dds_alloc (sizeof (FG_DDS_prop)));
|
||||
|
||||
#define FG_DDS_prop_free(d,o) \
|
||||
dds_sample_free ((d), &FG_DDS_prop_desc, (o))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* _DDSL_DDS_PROPS_H_ */
|
||||
90
src/Network/DDS/dds_props.idl
Normal file
90
src/Network/DDS/dds_props.idl
Normal file
@@ -0,0 +1,90 @@
|
||||
// format dfescription: https://www.omg.org/spec/IDL/4.2/PDF
|
||||
|
||||
// Adapted from net_fdm.hxx
|
||||
module FG
|
||||
{
|
||||
|
||||
// defining it this way also generates accompanying #defines in the header file.
|
||||
const octet DDS_PROP_VERSION = 0;
|
||||
const short DDS_PROP_REQUEST = -1;
|
||||
const boolean DDS_MODE_READ = 0;
|
||||
const boolean DDS_MODE_WRITE = 1;
|
||||
|
||||
enum propType
|
||||
{
|
||||
DDS_NONE, // The node hasn't been assigned a value yet
|
||||
DDS_ALIAS, // The node "points" to another node
|
||||
DDS_BOOL,
|
||||
DDS_INT, // 32-bit integer
|
||||
DDS_LONG, // 64-bit integer
|
||||
DDS_FLOAT, // 32-bit floating point number
|
||||
DDS_DOUBLE, // 64-bit floating point number
|
||||
DDS_STRING, // UTF-8 string
|
||||
DDS_UNSPECIFIED // Resolves to STRING
|
||||
};
|
||||
|
||||
union propValue switch ( propType )
|
||||
{
|
||||
case DDS_BOOL:
|
||||
boolean Bool;
|
||||
|
||||
case DDS_NONE:
|
||||
case DDS_INT:
|
||||
long Int32;
|
||||
|
||||
case DDS_LONG:
|
||||
long long Int64;
|
||||
|
||||
case DDS_FLOAT:
|
||||
float Float32;
|
||||
|
||||
case DDS_DOUBLE:
|
||||
double Float64;
|
||||
|
||||
case DDS_ALIAS:
|
||||
case DDS_STRING:
|
||||
case DDS_UNSPECIFIED:
|
||||
string String;
|
||||
};
|
||||
|
||||
// Initial property request sequence
|
||||
// for properties where the id is not yet known:
|
||||
// 1. Set id to FG_DDS_PROP_REQUEST
|
||||
// 2. Set version to FG_DDS_PROP_VERSION
|
||||
// 3. Set mode to FG_DDS_MODE_READ
|
||||
// 4. set guid to the 16-byte participants GUID
|
||||
// 5. Set val type to FG_DDS_STRING
|
||||
// 6. Set val String to the propery path
|
||||
// 7. Send the package.
|
||||
//
|
||||
// 8. Wait for an answer
|
||||
// * Check whether guid matches the participants GUID.
|
||||
// * The index of the requested propery path is stored in the id variable
|
||||
// which should be used by successive request as the id for that property.
|
||||
// * The val union holds the value of the propery.
|
||||
//
|
||||
// Successive property request sequence:
|
||||
// 1. Make sure id is to the id of the requested property
|
||||
// 2. Make sure version is to FG_DDS_PROP_VERSION
|
||||
// 3. Set mode to FG_DDS_MODE_READ or FG_DDS_MODE_WRITE
|
||||
// 3a. Optionally set the val union to match the property to overwrite.
|
||||
// 4. Send the package.
|
||||
//
|
||||
// 5. Wait for an answer
|
||||
// * Check whether id matches the requested property id.
|
||||
// * The val union holds the value of the propery.
|
||||
struct DDS_prop
|
||||
{
|
||||
long id; // 32-bit property index and DDS id.
|
||||
octet version; // 8-bit sample-type version number.
|
||||
|
||||
boolean mode; // FG_DDS_MODE_READ or FG_DDS_MODE_WRITE
|
||||
propValue val; // property value or path.
|
||||
|
||||
// A sequence could be used here but unfortunatelly it's initializer
|
||||
// already is 10-bytes in size.
|
||||
octet guid[16];
|
||||
};
|
||||
#pragma keylist DDS_prop id
|
||||
|
||||
}; // module FG
|
||||
170
src/Network/DDS/fg_dds_log.cpp
Normal file
170
src/Network/DDS/fg_dds_log.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <simgear/io/SGDataDistributionService.hxx>
|
||||
|
||||
#include "dds_fwd.hxx"
|
||||
|
||||
/* An array of one message(aka sample in dds terms) will be used. */
|
||||
#define MAX_SAMPLES 1
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <termios.h>
|
||||
|
||||
void set_mode(int want_key)
|
||||
{
|
||||
static struct termios tios_old, tios_new;
|
||||
if (!want_key) {
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &tios_old);
|
||||
return;
|
||||
}
|
||||
|
||||
tcgetattr(STDIN_FILENO, &tios_old);
|
||||
tios_new = tios_old;
|
||||
tios_new.c_lflag &= ~(ICANON | ECHO);
|
||||
tcsetattr(STDIN_FILENO, TCSANOW, &tios_new);
|
||||
}
|
||||
|
||||
int get_key()
|
||||
{
|
||||
int c = 0;
|
||||
struct timeval tv;
|
||||
fd_set fs;
|
||||
tv.tv_usec = tv.tv_sec = 0;
|
||||
|
||||
FD_ZERO(&fs);
|
||||
FD_SET(STDIN_FILENO, &fs);
|
||||
select(STDIN_FILENO + 1, &fs, 0, 0, &tv);
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &fs)) {
|
||||
c = getchar();
|
||||
}
|
||||
return c;
|
||||
}
|
||||
#else
|
||||
# include <conio.h>
|
||||
|
||||
int get_key()
|
||||
{
|
||||
if (kbhit()) {
|
||||
return getch();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_mode(int want_key)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
std::map<std::string, SG_DDS_Topic*> topics;
|
||||
SG_DDS participant;
|
||||
|
||||
FG_DDS_GUI gui;
|
||||
topics["gui"] = new SG_DDS_Topic(gui, &FG_DDS_GUI_desc);
|
||||
participant.add(topics["gui"], SG_IO_IN);
|
||||
|
||||
FG_DDS_FDM fdm;
|
||||
topics["fdm"] = new SG_DDS_Topic(fdm, &FG_DDS_FDM_desc);
|
||||
participant.add(topics["fdm"], SG_IO_IN);
|
||||
|
||||
FG_DDS_Ctrls ctrls;
|
||||
topics["ctrls"] = new SG_DDS_Topic(ctrls, &FG_DDS_Ctrls_desc);
|
||||
participant.add(topics["ctrls"], SG_IO_IN);
|
||||
|
||||
FG_DDS_prop prop;
|
||||
topics["prop"] = new SG_DDS_Topic(prop, &FG_DDS_prop_desc);
|
||||
participant.add(topics["prop"], SG_IO_IN);
|
||||
|
||||
set_mode(1);
|
||||
while(participant.wait(0.1f))
|
||||
{
|
||||
if (topics["gui"]->read()) {
|
||||
printf("=== [fg_dds_log] Received : ");
|
||||
printf("GUI Message:\n");
|
||||
printf(" version: %i\n", gui.version);
|
||||
printf(" tuned_freq: %lf\n", gui.longitude);
|
||||
printf(" nav_radial: %lf\n", gui.latitude);
|
||||
printf(" dist_nm: %lf\n", gui.altitude);
|
||||
}
|
||||
|
||||
if (topics["fdm"]->read()) {
|
||||
printf("=== [fg_dds_log] Received : ");
|
||||
printf("FDM Message:\n");
|
||||
printf(" version: %i\n", fdm.version);
|
||||
printf(" longitude: %lf\n", fdm.longitude);
|
||||
printf(" latitude: %lf\n", fdm.latitude);
|
||||
printf(" altitude: %lf\n", fdm.altitude);
|
||||
}
|
||||
|
||||
if (topics["ctrls"]->read()) {
|
||||
printf("=== [fg_dds_log] Received : ");
|
||||
printf("Ctrls Message:\n");
|
||||
printf(" version: %i\n", ctrls.version);
|
||||
printf(" aileron: %lf\n", ctrls.aileron);
|
||||
printf(" elevator: %lf\n", ctrls.elevator);
|
||||
printf(" rudder: %lf\n", ctrls.rudder);
|
||||
}
|
||||
|
||||
if (topics["prop"]->read() &&
|
||||
prop.version == FG_DDS_PROP_VERSION )
|
||||
{
|
||||
printf("=== [fg_dds_log] Received : ");
|
||||
printf("Prop Message:\n");
|
||||
printf(" version: %i\n", prop.version);
|
||||
printf(" mode: %s\n", prop.mode ? "write" : "read");
|
||||
printf(" id: %i\n", prop.id);
|
||||
if (prop.id == FG_DDS_PROP_REQUEST) {
|
||||
printf(" path: %s\n", prop.val._u.String);
|
||||
printf("GUID: ");
|
||||
for(int i=0; i<16; ++i)
|
||||
printf("%X ", prop.guid[i]);
|
||||
printf("\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(prop.val._d)
|
||||
{
|
||||
case FG_DDS_BOOL:
|
||||
printf(" type: bool");
|
||||
printf(" value: %i\n", prop.val._u.Bool);
|
||||
break;
|
||||
case FG_DDS_INT:
|
||||
printf(" type: int");
|
||||
printf(" value: %i\n", prop.val._u.Int32);
|
||||
break;
|
||||
case FG_DDS_LONG:
|
||||
printf(" type: long");
|
||||
printf(" value: %li\n", prop.val._u.Int64);
|
||||
break;
|
||||
case FG_DDS_FLOAT:
|
||||
printf(" type: float");
|
||||
printf(" value: %f\n", prop.val._u.Float32);
|
||||
break;
|
||||
case FG_DDS_DOUBLE:
|
||||
printf(" type: double");
|
||||
printf(" value: %lf\n", prop.val._u.Float64);
|
||||
break;
|
||||
case FG_DDS_STRING:
|
||||
printf(" type: string");
|
||||
printf(" value: %s\n", prop.val._u.String);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
if (get_key()) break;
|
||||
}
|
||||
set_mode(0);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
99
src/Network/DDS/fg_dds_prop.cpp
Normal file
99
src/Network/DDS/fg_dds_prop.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <simgear/io/SGDataDistributionService.hxx>
|
||||
|
||||
#include "dds_props.h"
|
||||
|
||||
/* An array of one message(aka sample in dds terms) will be used. */
|
||||
#define MAX_SAMPLES 1
|
||||
|
||||
int main()
|
||||
{
|
||||
SG_DDS participant;
|
||||
|
||||
FG_DDS_prop prop;
|
||||
SG_DDS_Topic *topic = new SG_DDS_Topic(prop, &FG_DDS_prop_desc);
|
||||
|
||||
participant.add(topic, SG_IO_BI);
|
||||
|
||||
dds_guid_t guid = topic->get_guid();
|
||||
memcpy(prop.guid, guid.v, 16);
|
||||
printf("GUID: ");
|
||||
for(int i=0; i<16; ++i)
|
||||
printf("%X ", prop.guid[i]);
|
||||
printf("\n");
|
||||
|
||||
char path[256];
|
||||
printf("\nType 'q' to quit\n");
|
||||
do
|
||||
{
|
||||
printf("Property path or id: ");
|
||||
int len = scanf("%255s", path);
|
||||
if (len == EOF) continue;
|
||||
|
||||
if (*path == 'q') break;
|
||||
|
||||
char *end;
|
||||
int id = strtol(path, &end, 10);
|
||||
|
||||
prop.id = (end == path) ? FG_DDS_PROP_REQUEST : id;
|
||||
prop.version = FG_DDS_PROP_VERSION;
|
||||
prop.mode = FG_DDS_MODE_READ;
|
||||
prop.val._d = FG_DDS_STRING;
|
||||
prop.val._u.String = path;
|
||||
|
||||
topic->write();
|
||||
|
||||
participant.wait();
|
||||
if (topic->read() &&
|
||||
prop.version == FG_DDS_PROP_VERSION &&
|
||||
prop.id != FG_DDS_PROP_REQUEST)
|
||||
{
|
||||
printf("\nReceived:\n");
|
||||
switch(prop.val._d)
|
||||
{
|
||||
case FG_DDS_NONE:
|
||||
printf(" type: none");
|
||||
break;
|
||||
case FG_DDS_ALIAS:
|
||||
printf(" type: alias");
|
||||
printf(" value: %s\n", prop.val._u.String);
|
||||
break;
|
||||
case FG_DDS_BOOL:
|
||||
printf(" type: bool");
|
||||
printf(" value: %i\n", prop.val._u.Bool);
|
||||
break;
|
||||
case FG_DDS_INT:
|
||||
printf(" type: int");
|
||||
printf(" value: %i\n", prop.val._u.Int32);
|
||||
break;
|
||||
case FG_DDS_LONG:
|
||||
printf(" type: long");
|
||||
printf(" value: %li\n", prop.val._u.Int64);
|
||||
break;
|
||||
case FG_DDS_FLOAT:
|
||||
printf(" type: float");
|
||||
printf(" value: %f\n", prop.val._u.Float32);
|
||||
break;
|
||||
case FG_DDS_DOUBLE:
|
||||
printf(" type: double");
|
||||
printf(" value: %lf\n", prop.val._u.Float64);
|
||||
break;
|
||||
case FG_DDS_STRING:
|
||||
printf(" type: string");
|
||||
printf(" value: %s\n", prop.val._u.String);
|
||||
break;
|
||||
case FG_DDS_UNSPECIFIED:
|
||||
printf(" type: unspecified");
|
||||
printf(" value: %s\n", prop.val._u.String);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while(true);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
76
src/Network/DNSClient.cxx
Normal file
76
src/Network/DNSClient.cxx
Normal file
@@ -0,0 +1,76 @@
|
||||
// HDNSClient.cxx -- Singleton DNS client object
|
||||
//
|
||||
// Written by James Turner, started April 2012.
|
||||
// Based on HTTPClient from James Turner
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "DNSClient.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
FGDNSClient::FGDNSClient() :
|
||||
_inited(false)
|
||||
{
|
||||
}
|
||||
|
||||
FGDNSClient::~FGDNSClient()
|
||||
{
|
||||
}
|
||||
|
||||
void FGDNSClient::init()
|
||||
{
|
||||
if (_inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
_dns.reset( new simgear::DNS::Client() );
|
||||
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
void FGDNSClient::postinit()
|
||||
{
|
||||
}
|
||||
|
||||
void FGDNSClient::shutdown()
|
||||
{
|
||||
_dns.reset();
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
void FGDNSClient::update(double)
|
||||
{
|
||||
_dns->update();
|
||||
}
|
||||
|
||||
void FGDNSClient::makeRequest(const simgear::DNS::Request_ptr& req)
|
||||
{
|
||||
_dns->makeRequest(req);
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGDNSClient> registrantFGDNSClient;
|
||||
55
src/Network/DNSClient.hxx
Normal file
55
src/Network/DNSClient.hxx
Normal file
@@ -0,0 +1,55 @@
|
||||
// DNSClient.hxx -- Singleton DNS client object
|
||||
//
|
||||
// Written by Torsten Dreyer, started November 2016.
|
||||
// Based on HTTPClient from James Turner
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_DNS_CLIENT_HXX
|
||||
#define FG_DNS_CLIENT_HXX
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/io/DNSClient.hxx>
|
||||
#include <memory>
|
||||
|
||||
class FGDNSClient : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
FGDNSClient();
|
||||
virtual ~FGDNSClient();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void postinit() override;
|
||||
void shutdown() override;
|
||||
void update(double) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "dns"; }
|
||||
|
||||
void makeRequest(const simgear::DNS::Request_ptr& req);
|
||||
|
||||
// simgear::HTTP::Client* client() { return _http.get(); }
|
||||
// simgear::HTTP::Client const* client() const { return _http.get(); }
|
||||
|
||||
private:
|
||||
bool _inited;
|
||||
std::unique_ptr<simgear::DNS::Client> _dns;
|
||||
};
|
||||
|
||||
#endif // FG_DNS_CLIENT_HXX
|
||||
|
||||
|
||||
7
src/Network/HLA/CMakeLists.txt
Normal file
7
src/Network/HLA/CMakeLists.txt
Normal file
@@ -0,0 +1,7 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
hla.cxx
|
||||
)
|
||||
|
||||
flightgear_component(HLA "${SOURCES}")
|
||||
1327
src/Network/HLA/hla.cxx
Normal file
1327
src/Network/HLA/hla.cxx
Normal file
File diff suppressed because it is too large
Load Diff
56
src/Network/HLA/hla.hxx
Normal file
56
src/Network/HLA/hla.hxx
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright (C) 2009 - 2010 Mathias Fröhlich <Mathias.Froehlich@web.de>
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
|
||||
#ifndef _FG_HLA_HXX
|
||||
#define _FG_HLA_HXX
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/structure/SGSharedPtr.hxx>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <Network/protocol.hxx>
|
||||
|
||||
class FGHLA : public FGProtocol {
|
||||
public:
|
||||
FGHLA(const std::vector<std::string>& tokens);
|
||||
virtual ~FGHLA();
|
||||
|
||||
virtual bool open();
|
||||
virtual bool process();
|
||||
virtual bool close();
|
||||
|
||||
private:
|
||||
/// All the utility classes we need currently
|
||||
class XMLConfigReader;
|
||||
class MPUpdateCallback;
|
||||
class MPReflectCallback;
|
||||
class MultiplayerObjectInstance;
|
||||
class MultiplayerObjectClass;
|
||||
class Federate;
|
||||
|
||||
/// The configuration parameters extracted from the tokens in the constructor
|
||||
std::string _objectModelConfig;
|
||||
std::string _federation;
|
||||
std::string _federate;
|
||||
|
||||
/// The toplevel rti class
|
||||
SGSharedPtr<Federate> _hlaFederate;
|
||||
};
|
||||
|
||||
#endif // _FG_HLA_HXX
|
||||
337
src/Network/HTTPClient.cxx
Normal file
337
src/Network/HTTPClient.cxx
Normal file
@@ -0,0 +1,337 @@
|
||||
// HTTPClient.cxx -- Singleton HTTP client object
|
||||
//
|
||||
// Written by James Turner, started April 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "HTTPClient.hxx"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
#include <simgear/package/Root.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
#include <simgear/package/Install.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
#include <simgear/nasal/cppbind/from_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/to_nasal.hxx>
|
||||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/cppbind/Ghost.hxx>
|
||||
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
using namespace simgear;
|
||||
|
||||
typedef nasal::Ghost<pkg::RootRef> NasalPackageRoot;
|
||||
typedef nasal::Ghost<pkg::PackageRef> NasalPackage;
|
||||
typedef nasal::Ghost<pkg::CatalogRef> NasalCatalog;
|
||||
typedef nasal::Ghost<pkg::InstallRef> NasalInstall;
|
||||
|
||||
static const char* OFFICIAL_CATALOG_ID = "org.flightgear.fgaddon.trunk";
|
||||
|
||||
// fallback URL is used when looking up a version-specific catalog fails
|
||||
static const char* FALLBACK_CATALOG_URL = "http://mirrors.ibiblio.org/flightgear/ftp/Aircraft-trunk/catalog.xml";
|
||||
|
||||
namespace {
|
||||
|
||||
std::string _getDefaultCatalogId()
|
||||
{
|
||||
return fgGetString("/sim/package-system/default-catalog/id",
|
||||
OFFICIAL_CATALOG_ID);
|
||||
}
|
||||
|
||||
pkg::CatalogRef getDefaultCatalog()
|
||||
{
|
||||
if (!globals->packageRoot())
|
||||
return pkg::CatalogRef();
|
||||
|
||||
return globals->packageRoot()->getCatalogById(_getDefaultCatalogId());
|
||||
}
|
||||
|
||||
std::string _getDefaultCatalogUrl()
|
||||
{
|
||||
return fgGetString("/sim/package-system/default-catalog/url",
|
||||
"http://mirrors.ibiblio.org/flightgear/ftp/" FLIGHTGEAR_VERSION "/catalog.xml");
|
||||
}
|
||||
} // of anonymous namespace
|
||||
|
||||
|
||||
FGHTTPClient::FGHTTPClient() :
|
||||
_inited(false)
|
||||
{
|
||||
}
|
||||
|
||||
FGHTTPClient::~FGHTTPClient()
|
||||
{
|
||||
}
|
||||
|
||||
FGHTTPClient* FGHTTPClient::getOrCreate()
|
||||
{
|
||||
auto ext = globals->get_subsystem<FGHTTPClient>();
|
||||
if (ext) {
|
||||
return ext;
|
||||
}
|
||||
|
||||
ext = globals->add_new_subsystem<FGHTTPClient>(SGSubsystemMgr::GENERAL);
|
||||
ext->init();
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
||||
void FGHTTPClient::init()
|
||||
{
|
||||
// launcher may need to setup HTTP access abnormally early, so
|
||||
// guard against duplicate inits
|
||||
if (_inited) {
|
||||
return;
|
||||
}
|
||||
|
||||
_http.reset(new simgear::HTTP::Client);
|
||||
|
||||
std::string proxyHost(fgGetString("/sim/presets/proxy/host"));
|
||||
int proxyPort(fgGetInt("/sim/presets/proxy/port"));
|
||||
std::string proxyAuth(fgGetString("/sim/presets/proxy/auth"));
|
||||
|
||||
if (!proxyHost.empty()) {
|
||||
_http->setProxy(proxyHost, proxyPort, proxyAuth);
|
||||
}
|
||||
|
||||
pkg::Root* packageRoot = globals->packageRoot();
|
||||
if (packageRoot) {
|
||||
// package system needs access to the HTTP engine too
|
||||
packageRoot->setHTTPClient(_http.get());
|
||||
|
||||
// start a refresh now
|
||||
// setting 'force' true to work around the problem where a slightly stale
|
||||
// catalog exists, but aircraft are modified - this causes an MD5 sum
|
||||
// mismatch
|
||||
packageRoot->refresh(true);
|
||||
}
|
||||
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
bool FGHTTPClient::isDefaultCatalogInstalled() const
|
||||
{
|
||||
return getDefaultCatalog().valid();
|
||||
}
|
||||
|
||||
pkg::CatalogRef FGHTTPClient::addDefaultCatalog()
|
||||
{
|
||||
pkg::CatalogRef defaultCatalog = getDefaultCatalog();
|
||||
if (!defaultCatalog.valid()) {
|
||||
auto cat = pkg::Catalog::createFromUrl(globals->packageRoot(), getDefaultCatalogUrl());
|
||||
return cat;
|
||||
}
|
||||
|
||||
return defaultCatalog;
|
||||
}
|
||||
|
||||
std::string FGHTTPClient::getDefaultCatalogId() const
|
||||
{
|
||||
return _getDefaultCatalogId();
|
||||
}
|
||||
|
||||
std::string FGHTTPClient::getDefaultCatalogUrl() const
|
||||
{
|
||||
return _getDefaultCatalogUrl();
|
||||
}
|
||||
|
||||
std::string FGHTTPClient::getDefaultCatalogFallbackUrl() const
|
||||
{
|
||||
return std::string(FALLBACK_CATALOG_URL);
|
||||
}
|
||||
|
||||
static naRef f_package_existingInstall( pkg::Package& pkg,
|
||||
const nasal::CallContext& ctx )
|
||||
{
|
||||
return ctx.to_nasal(
|
||||
pkg.existingInstall( ctx.getArg<pkg::Package::InstallCallback>(0) )
|
||||
);
|
||||
}
|
||||
|
||||
static naRef f_package_uninstall(pkg::Package& pkg, const nasal::CallContext& ctx)
|
||||
{
|
||||
pkg::InstallRef ins = pkg.existingInstall();
|
||||
if (ins) {
|
||||
ins->uninstall();
|
||||
}
|
||||
|
||||
return naNil();
|
||||
}
|
||||
|
||||
static SGPropertyNode_ptr queryPropsFromHash(const nasal::Hash& h)
|
||||
{
|
||||
SGPropertyNode_ptr props(new SGPropertyNode);
|
||||
|
||||
for (nasal::Hash::const_iterator it = h.begin(); it != h.end(); ++it) {
|
||||
std::string const key = it->getKey();
|
||||
if ((key == "name") || (key == "description")) {
|
||||
props->setStringValue(key, it->getValue<std::string>());
|
||||
} else if (strutils::starts_with(key, "rating-")) {
|
||||
props->setIntValue(key, it->getValue<int>());
|
||||
} else if (key == "tags") {
|
||||
string_list tags = it->getValue<string_list>();
|
||||
string_list::const_iterator tagIt;
|
||||
int tagCount = 0;
|
||||
for (tagIt = tags.begin(); tagIt != tags.end(); ++tagIt) {
|
||||
SGPropertyNode_ptr tag = props->getChild("tag", tagCount++, true);
|
||||
tag->setStringValue(*tagIt);
|
||||
}
|
||||
} else if (key == "installed") {
|
||||
props->setBoolValue(key, it->getValue<bool>());
|
||||
} else {
|
||||
SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term in hash:" << key);
|
||||
}
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
static naRef f_root_search(pkg::Root& root, const nasal::CallContext& ctx)
|
||||
{
|
||||
SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
|
||||
pkg::PackageList result = root.packagesMatching(query);
|
||||
return ctx.to_nasal(result);
|
||||
}
|
||||
|
||||
static naRef f_catalog_search(pkg::Catalog& cat, const nasal::CallContext& ctx)
|
||||
{
|
||||
SGPropertyNode_ptr query = queryPropsFromHash(ctx.requireArg<nasal::Hash>(0));
|
||||
pkg::PackageList result = cat.packagesMatching(query);
|
||||
return ctx.to_nasal(result);
|
||||
}
|
||||
|
||||
static naRef f_catalog_packages(pkg::Catalog& cat, const nasal::CallContext& ctx)
|
||||
{
|
||||
// TODO: support package types
|
||||
pkg::PackageList result = cat.packages();
|
||||
return ctx.to_nasal(result);
|
||||
}
|
||||
|
||||
|
||||
static naRef f_catalog_installedPackages(pkg::Catalog& cat, naContext c)
|
||||
{
|
||||
// TODO: support package types
|
||||
pkg::PackageList result = cat.installedPackages();
|
||||
return nasal::to_nasal(c, result);
|
||||
}
|
||||
|
||||
static naRef f_package_variants(pkg::Package& pack, naContext c)
|
||||
{
|
||||
nasal::Hash h(c);
|
||||
string_list vars(pack.variants());
|
||||
for (string_list_iterator it = vars.begin(); it != vars.end(); ++it) {
|
||||
h.set(*it, pack.nameForVariant(*it));
|
||||
}
|
||||
|
||||
return h.get_naRef();
|
||||
}
|
||||
|
||||
void FGHTTPClient::postinit()
|
||||
{
|
||||
NasalPackageRoot::init("PackageRoot")
|
||||
.member("path", &pkg::Root::path)
|
||||
.member("version", &pkg::Root::catalogVersion)
|
||||
.method("refresh", &pkg::Root::refresh)
|
||||
.method("catalogs", &pkg::Root::catalogs)
|
||||
.method("packageById", &pkg::Root::getPackageById)
|
||||
.method("catalogById", &pkg::Root::getCatalogById)
|
||||
.method("search", &f_root_search);
|
||||
|
||||
NasalCatalog::init("Catalog")
|
||||
.member("installRoot", &pkg::Catalog::installRoot)
|
||||
.member("id", &pkg::Catalog::id)
|
||||
.member("url", &pkg::Catalog::url)
|
||||
.member("description", &pkg::Catalog::description)
|
||||
.method("packages", &f_catalog_packages)
|
||||
.method("packageById", &pkg::Catalog::getPackageById)
|
||||
.method("refresh", &pkg::Catalog::refresh)
|
||||
.method("needingUpdate", &pkg::Catalog::packagesNeedingUpdate)
|
||||
.member("installed", &f_catalog_installedPackages)
|
||||
.method("search", &f_catalog_search)
|
||||
.member("enabled", &pkg::Catalog::isEnabled);
|
||||
|
||||
NasalPackage::init("Package")
|
||||
.member("id", &pkg::Package::id)
|
||||
.member("name", &pkg::Package::name)
|
||||
.member("description", &pkg::Package::description)
|
||||
.member("installed", &pkg::Package::isInstalled)
|
||||
.member("thumbnails", &pkg::Package::thumbnailUrls)
|
||||
.member("variants", &f_package_variants)
|
||||
.member("revision", &pkg::Package::revision)
|
||||
.member("catalog", &pkg::Package::catalog)
|
||||
.method("install", &pkg::Package::install)
|
||||
.method("uninstall", &f_package_uninstall)
|
||||
.method("existingInstall", &f_package_existingInstall)
|
||||
.method("lprop", &pkg::Package::getLocalisedProp)
|
||||
.member("fileSize", &pkg::Package::fileSizeBytes);
|
||||
|
||||
typedef pkg::Install* (pkg::Install::*InstallCallback)
|
||||
(const pkg::Install::Callback&);
|
||||
typedef pkg::Install* (pkg::Install::*ProgressCallback)
|
||||
(const pkg::Install::ProgressCallback&);
|
||||
NasalInstall::init("Install")
|
||||
.member("revision", &pkg::Install::revsion)
|
||||
.member("pkg", &pkg::Install::package)
|
||||
.member("path", &pkg::Install::path)
|
||||
.member("hasUpdate", &pkg::Install::hasUpdate)
|
||||
.method("startUpdate", &pkg::Install::startUpdate)
|
||||
.method("uninstall", &pkg::Install::uninstall)
|
||||
.method("done", static_cast<InstallCallback>(&pkg::Install::done))
|
||||
.method("fail", static_cast<InstallCallback>(&pkg::Install::fail))
|
||||
.method("always", static_cast<InstallCallback>(&pkg::Install::always))
|
||||
.method("progress", static_cast<ProgressCallback>(&pkg::Install::progress));
|
||||
|
||||
pkg::Root* packageRoot = globals->packageRoot();
|
||||
if (packageRoot) {
|
||||
FGNasalSys* nasalSys = globals->get_subsystem<FGNasalSys>();
|
||||
nasal::Hash nasalGlobals = nasalSys->getGlobals();
|
||||
nasal::Hash nasalPkg = nasalGlobals.createHash("pkg"); // module
|
||||
nasalPkg.set("root", packageRoot);
|
||||
}
|
||||
}
|
||||
|
||||
void FGHTTPClient::shutdown()
|
||||
{
|
||||
_http.reset();
|
||||
|
||||
_inited = false;
|
||||
}
|
||||
|
||||
void FGHTTPClient::update(double)
|
||||
{
|
||||
_http->update();
|
||||
}
|
||||
|
||||
void FGHTTPClient::makeRequest(const simgear::HTTP::Request_ptr& req)
|
||||
{
|
||||
_http->makeRequest(req);
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGHTTPClient> registrantFGHTTPClient(
|
||||
SGSubsystemMgr::GENERAL,
|
||||
{{"nasal", SGSubsystemMgr::Dependency::HARD}});
|
||||
67
src/Network/HTTPClient.hxx
Normal file
67
src/Network/HTTPClient.hxx
Normal file
@@ -0,0 +1,67 @@
|
||||
// HTTPClient.hxx -- Singleton HTTP client object
|
||||
//
|
||||
// Written by James Turner, started April 2012.
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_HTTP_CLIENT_HXX
|
||||
#define FG_HTTP_CLIENT_HXX
|
||||
|
||||
#include <simgear/io/HTTPClient.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class FGHTTPClient : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
FGHTTPClient();
|
||||
virtual ~FGHTTPClient();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void postinit() override;
|
||||
void shutdown() override;
|
||||
void update(double) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "http"; }
|
||||
|
||||
|
||||
static FGHTTPClient* getOrCreate();
|
||||
|
||||
void makeRequest(const simgear::HTTP::Request_ptr& req);
|
||||
|
||||
simgear::HTTP::Client* client() { return _http.get(); }
|
||||
simgear::HTTP::Client const* client() const { return _http.get(); }
|
||||
|
||||
bool isDefaultCatalogInstalled() const;
|
||||
simgear::pkg::CatalogRef addDefaultCatalog();
|
||||
|
||||
std::string getDefaultCatalogId() const;
|
||||
std::string getDefaultCatalogUrl() const;
|
||||
std::string getDefaultCatalogFallbackUrl() const;
|
||||
|
||||
private:
|
||||
bool _inited;
|
||||
std::unique_ptr<simgear::HTTP::Client> _http;
|
||||
};
|
||||
|
||||
#endif // FG_HTTP_CLIENT_HXX
|
||||
|
||||
|
||||
92
src/Network/RemoteXMLRequest.hxx
Normal file
92
src/Network/RemoteXMLRequest.hxx
Normal file
@@ -0,0 +1,92 @@
|
||||
// RemoteXMLRequest.hxx -- xml http requests
|
||||
//
|
||||
// Written by James Turner
|
||||
//
|
||||
// Copyright (C) 2012 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef REMOTEXMLREQUEST_HXXH
|
||||
#define REMOTEXMLREQUEST_HXXH
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/io/HTTPMemoryRequest.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
class RemoteXMLRequest:
|
||||
public simgear::HTTP::MemoryRequest
|
||||
{
|
||||
public:
|
||||
SGPropertyNode_ptr _complete;
|
||||
SGPropertyNode_ptr _status;
|
||||
SGPropertyNode_ptr _failed;
|
||||
SGPropertyNode_ptr _target;
|
||||
|
||||
RemoteXMLRequest(const std::string& url, SGPropertyNode* targetNode) :
|
||||
simgear::HTTP::MemoryRequest(url),
|
||||
_target(targetNode)
|
||||
{
|
||||
}
|
||||
|
||||
void setCompletionProp(SGPropertyNode_ptr p)
|
||||
{
|
||||
_complete = p;
|
||||
}
|
||||
|
||||
void setStatusProp(SGPropertyNode_ptr p)
|
||||
{
|
||||
_status = p;
|
||||
}
|
||||
|
||||
void setFailedProp(SGPropertyNode_ptr p)
|
||||
{
|
||||
_failed = p;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
virtual void onFail()
|
||||
{
|
||||
SG_LOG(SG_IO, SG_INFO, "network level failure in RemoteXMLRequest");
|
||||
if (_failed) {
|
||||
_failed->setBoolValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void onDone()
|
||||
{
|
||||
int response = responseCode();
|
||||
bool failed = false;
|
||||
if (response == 200) {
|
||||
try {
|
||||
const char* buffer = responseBody().c_str();
|
||||
readProperties(buffer, responseBody().size(), _target, true);
|
||||
} catch (const sg_exception &e) {
|
||||
SG_LOG(SG_IO, SG_WARN, "parsing XML from remote, failed: " << e.getFormattedMessage());
|
||||
failed = true;
|
||||
response = 406; // 'not acceptable', anything better?
|
||||
}
|
||||
} else {
|
||||
failed = true;
|
||||
}
|
||||
// now the response data is output, signal Nasal / listeners
|
||||
if (_complete) _complete->setBoolValue(true);
|
||||
if (_status) _status->setIntValue(response);
|
||||
if (_failed && failed) _failed->setBoolValue(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //REMOTEXMLREQUEST_HXXH
|
||||
32
src/Network/Swift/CMakeLists.txt
Normal file
32
src/Network/Swift/CMakeLists.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
swift_connection.cxx
|
||||
dbusconnection.cpp
|
||||
dbusobject.cpp
|
||||
dbusmessage.cpp
|
||||
dbusdispatcher.cpp
|
||||
dbuserror.cpp
|
||||
dbusserver.cpp
|
||||
plugin.cpp
|
||||
service.cpp
|
||||
traffic.cpp
|
||||
SwiftAircraftManager.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
swift_connection.hxx
|
||||
dbusconnection.h
|
||||
dbusobject.h
|
||||
dbusmessage.h
|
||||
dbuscallbacks.h
|
||||
dbusdispatcher.h
|
||||
dbuserror.h
|
||||
dbusserver.h
|
||||
plugin.h
|
||||
service.h
|
||||
traffic.h
|
||||
SwiftAircraftManager.h
|
||||
)
|
||||
|
||||
flightgear_component(Swift "${SOURCES}" "${HEADERS}")
|
||||
129
src/Network/Swift/SwiftAircraftManager.cpp
Normal file
129
src/Network/Swift/SwiftAircraftManager.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Manger class for aircraft generated by swift
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "SwiftAircraftManager.h"
|
||||
#include <Main/globals.hxx>
|
||||
#include <utility>
|
||||
|
||||
FGSwiftAircraftManager::FGSwiftAircraftManager()
|
||||
{
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
FGSwiftAircraftManager::~FGSwiftAircraftManager()
|
||||
{
|
||||
this->removeAllPlanes();
|
||||
m_initialized = false;
|
||||
}
|
||||
|
||||
bool FGSwiftAircraftManager::isInitialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
bool FGSwiftAircraftManager::addPlane(const std::string& callsign, const std::string& modelString)
|
||||
{
|
||||
this->removePlane(callsign); // Remove plane if already exists e.g. when rematching is done.
|
||||
auto curAircraft = FGAISwiftAircraftPtr(new FGAISwiftAircraft(callsign, modelString));
|
||||
globals->get_subsystem<FGAIManager>()->attach(curAircraft);
|
||||
// Init props after prop-root is assigned
|
||||
curAircraft->initProps();
|
||||
|
||||
aircraftByCallsign.insert(std::pair<std::string, FGAISwiftAircraftPtr>(callsign, curAircraft));
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGSwiftAircraftManager::updatePlanes(const std::vector<SwiftPlaneUpdate>& updates)
|
||||
{
|
||||
for (auto& update : updates) {
|
||||
auto it = aircraftByCallsign.find(update.callsign);
|
||||
if (it != aircraftByCallsign.end()) {
|
||||
it->second->updatePosition(update.position, update.orientation, update.groundspeed, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGSwiftAircraftManager::getRemoteAircraftData(std::vector<std::string>& callsigns, std::vector<double>& latitudesDeg, std::vector<double>& longitudesDeg, std::vector<double>& elevationsM, std::vector<double>& verticalOffsets) const
|
||||
{
|
||||
if (callsigns.empty() || aircraftByCallsign.empty()) { return; }
|
||||
|
||||
const auto requestedCallsigns = callsigns;
|
||||
callsigns.clear();
|
||||
latitudesDeg.clear();
|
||||
longitudesDeg.clear();
|
||||
elevationsM.clear();
|
||||
verticalOffsets.clear();
|
||||
|
||||
for (const auto& requestedCallsign : requestedCallsigns) {
|
||||
const auto it = aircraftByCallsign.find(requestedCallsign);
|
||||
if (it == aircraftByCallsign.end()) { continue; }
|
||||
|
||||
const FGAISwiftAircraft* aircraft = it->second;
|
||||
assert(aircraft);
|
||||
|
||||
|
||||
SGGeod pos;
|
||||
pos.setLatitudeDeg(aircraft->_getLatitude());
|
||||
pos.setLongitudeDeg(aircraft->_getLongitude());
|
||||
const double latDeg = pos.getLatitudeDeg();
|
||||
const double lonDeg = pos.getLongitudeDeg();
|
||||
double groundElevation = aircraft->getGroundElevation(pos);
|
||||
|
||||
callsigns.push_back(requestedCallsign);
|
||||
latitudesDeg.push_back(latDeg);
|
||||
longitudesDeg.push_back(lonDeg);
|
||||
elevationsM.push_back(groundElevation);
|
||||
verticalOffsets.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
void FGSwiftAircraftManager::removePlane(const std::string& callsign)
|
||||
{
|
||||
auto it = aircraftByCallsign.find(callsign);
|
||||
if (it != aircraftByCallsign.end()) {
|
||||
it->second->setDie(true);
|
||||
aircraftByCallsign.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void FGSwiftAircraftManager::removeAllPlanes()
|
||||
{
|
||||
for (auto it = aircraftByCallsign.begin(); it != aircraftByCallsign.end();) {
|
||||
it->second->setDie(true);
|
||||
it = aircraftByCallsign.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
double FGSwiftAircraftManager::getElevationAtPosition(const std::string& callsign, const SGGeod& pos) const
|
||||
{
|
||||
auto it = aircraftByCallsign.find(callsign);
|
||||
if (it != aircraftByCallsign.end()) {
|
||||
return it->second->getGroundElevation(pos);
|
||||
}
|
||||
// Aircraft not found in list
|
||||
return std::numeric_limits<double>::quiet_NaN();
|
||||
}
|
||||
|
||||
void FGSwiftAircraftManager::setPlanesTransponders(const std::vector<AircraftTransponder>& transponders)
|
||||
{
|
||||
for (const auto& transponder : transponders) {
|
||||
auto it = aircraftByCallsign.find(transponder.callsign);
|
||||
if (it != aircraftByCallsign.end()) {
|
||||
it->second->setPlaneTransponder(transponder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGSwiftAircraftManager::setPlanesSurfaces(const std::vector<AircraftSurfaces>& surfaces)
|
||||
{
|
||||
for (const auto& surface : surfaces) {
|
||||
auto it = aircraftByCallsign.find(surface.callsign);
|
||||
if (it != aircraftByCallsign.end()) {
|
||||
it->second->setPlaneSurface(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/Network/Swift/SwiftAircraftManager.h
Normal file
48
src/Network/Swift/SwiftAircraftManager.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Manger class for aircraft generated by swift
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <AIModel/AIManager.hxx>
|
||||
#include <AIModel/AISwiftAircraft.h>
|
||||
#include <Scenery/scenery.hxx>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifndef FGSWIFTAIRCRAFTMANAGER_H
|
||||
#define FGSWIFTAIRCRAFTMANAGER_H
|
||||
|
||||
struct SwiftPlaneUpdate {
|
||||
std::string callsign;
|
||||
SGGeod position;
|
||||
SGVec3d orientation;
|
||||
double groundspeed;
|
||||
bool onGround;
|
||||
};
|
||||
|
||||
class FGSwiftAircraftManager
|
||||
{
|
||||
using FGAISwiftAircraftPtr = SGSharedPtr<FGAISwiftAircraft>;
|
||||
|
||||
public:
|
||||
FGSwiftAircraftManager();
|
||||
~FGSwiftAircraftManager();
|
||||
bool addPlane(const std::string& callsign, const std::string& modelString);
|
||||
void updatePlanes(const std::vector<SwiftPlaneUpdate>& updates);
|
||||
void getRemoteAircraftData(std::vector<std::string>& callsigns, std::vector<double>& latitudesDeg, std::vector<double>& longitudesDeg,
|
||||
std::vector<double>& elevationsM, std::vector<double>& verticalOffsets) const;
|
||||
void removePlane(const std::string& callsign);
|
||||
void removeAllPlanes();
|
||||
void setPlanesTransponders(const std::vector<AircraftTransponder>& transponders);
|
||||
double getElevationAtPosition(const std::string& callsign, const SGGeod& pos) const;
|
||||
bool isInitialized() const;
|
||||
void setPlanesSurfaces(const std::vector<AircraftSurfaces>& surfaces);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, FGAISwiftAircraftPtr> aircraftByCallsign;
|
||||
bool m_initialized = false;
|
||||
};
|
||||
#endif
|
||||
51
src/Network/Swift/dbuscallbacks.h
Normal file
51
src/Network/Swift/dbuscallbacks.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSASYNCCALLBACKS_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSASYNCCALLBACKS_H
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <functional>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
//! \cond PRIVATE
|
||||
template <typename T>
|
||||
class DBusAsyncCallbacks
|
||||
{
|
||||
public:
|
||||
DBusAsyncCallbacks() = default;
|
||||
DBusAsyncCallbacks(const std::function<dbus_bool_t(T*)>& add,
|
||||
const std::function<void(T*)>& remove,
|
||||
const std::function<void(T*)>& toggled)
|
||||
: m_addHandler(add), m_removeHandler(remove), m_toggledHandler(toggled)
|
||||
{
|
||||
}
|
||||
|
||||
static dbus_bool_t add(T* watch, void* refcon)
|
||||
{
|
||||
return static_cast<DBusAsyncCallbacks*>(refcon)->m_addHandler(watch);
|
||||
}
|
||||
|
||||
static void remove(T* watch, void* refcon)
|
||||
{
|
||||
return static_cast<DBusAsyncCallbacks*>(refcon)->m_removeHandler(watch);
|
||||
}
|
||||
|
||||
static void toggled(T* watch, void* refcon)
|
||||
{
|
||||
return static_cast<DBusAsyncCallbacks*>(refcon)->m_toggledHandler(watch);
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<dbus_bool_t(T*)> m_addHandler;
|
||||
std::function<void(T*)> m_removeHandler;
|
||||
std::function<void(T*)> m_toggledHandler;
|
||||
};
|
||||
//! \endcond
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
174
src/Network/Swift/dbusconnection.cpp
Normal file
174
src/Network/Swift/dbusconnection.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "dbusconnection.h"
|
||||
#include "dbusobject.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
CDBusConnection::CDBusConnection()
|
||||
{
|
||||
dbus_threads_init_default();
|
||||
}
|
||||
|
||||
CDBusConnection::CDBusConnection(DBusConnection* connection)
|
||||
{
|
||||
m_connection.reset(connection);
|
||||
dbus_connection_ref(connection);
|
||||
// Don't exit application, if the connection is disconnected
|
||||
dbus_connection_set_exit_on_disconnect(connection, false);
|
||||
dbus_connection_add_filter(connection, filterDisconnectedFunction, this, nullptr);
|
||||
}
|
||||
|
||||
CDBusConnection::~CDBusConnection()
|
||||
{
|
||||
close();
|
||||
if (m_connection) { dispatch(); } // dispatch is virtual, but safe to call in dtor, as it's declared final
|
||||
if (m_dispatcher) { m_dispatcher->remove(this); }
|
||||
}
|
||||
|
||||
bool CDBusConnection::connect(BusType type)
|
||||
{
|
||||
assert(type == SessionBus);
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
|
||||
DBusBusType dbusBusType;
|
||||
switch (type) {
|
||||
case SessionBus: dbusBusType = DBUS_BUS_SESSION; break;
|
||||
}
|
||||
|
||||
m_connection.reset(dbus_bus_get_private(dbusBusType, &error));
|
||||
if (dbus_error_is_set(&error)) {
|
||||
m_lastError = CDBusError(&error);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't exit application, if the connection is disconnected
|
||||
dbus_connection_set_exit_on_disconnect(m_connection.get(), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDBusConnection::setDispatcher(CDBusDispatcher* dispatcher)
|
||||
{
|
||||
assert(dispatcher);
|
||||
|
||||
m_dispatcher = dispatcher;
|
||||
|
||||
m_dispatcher->add(this);
|
||||
|
||||
dbus_connection_set_watch_functions(
|
||||
m_connection.get(),
|
||||
dispatcher->m_watchCallbacks.add,
|
||||
dispatcher->m_watchCallbacks.remove,
|
||||
dispatcher->m_watchCallbacks.toggled,
|
||||
&dispatcher->m_watchCallbacks, nullptr);
|
||||
|
||||
dbus_connection_set_timeout_functions(
|
||||
m_connection.get(),
|
||||
dispatcher->m_timeoutCallbacks.add,
|
||||
dispatcher->m_timeoutCallbacks.remove,
|
||||
dispatcher->m_timeoutCallbacks.toggled,
|
||||
&dispatcher->m_timeoutCallbacks, nullptr);
|
||||
}
|
||||
|
||||
void CDBusConnection::requestName(const std::string& name)
|
||||
{
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
dbus_bus_request_name(m_connection.get(), name.c_str(), 0, &error);
|
||||
}
|
||||
|
||||
bool CDBusConnection::isConnected() const
|
||||
{
|
||||
return m_connection && dbus_connection_get_is_connected(m_connection.get());
|
||||
}
|
||||
|
||||
void CDBusConnection::registerDisconnectedCallback(CDBusObject* obj, DisconnectedCallback func)
|
||||
{
|
||||
m_disconnectedCallbacks[obj] = func;
|
||||
}
|
||||
|
||||
void CDBusConnection::unregisterDisconnectedCallback(CDBusObject* obj)
|
||||
{
|
||||
auto it = m_disconnectedCallbacks.find(obj);
|
||||
if (it == m_disconnectedCallbacks.end()) { return; }
|
||||
m_disconnectedCallbacks.erase(it);
|
||||
}
|
||||
|
||||
void CDBusConnection::registerObjectPath(CDBusObject* object, const std::string& interfaceName, const std::string& objectPath, const DBusObjectPathVTable& dbusObjectPathVTable)
|
||||
{
|
||||
(void)interfaceName;
|
||||
if (!m_connection) { return; }
|
||||
|
||||
dbus_connection_try_register_object_path(m_connection.get(), objectPath.c_str(), &dbusObjectPathVTable, object, nullptr);
|
||||
}
|
||||
|
||||
void CDBusConnection::sendMessage(const CDBusMessage& message)
|
||||
{
|
||||
if (!isConnected()) { return; }
|
||||
dbus_uint32_t serial = message.getSerial();
|
||||
dbus_connection_send(m_connection.get(), message.m_message, &serial);
|
||||
}
|
||||
|
||||
void CDBusConnection::close()
|
||||
{
|
||||
if (m_connection) { dbus_connection_close(m_connection.get()); }
|
||||
}
|
||||
|
||||
void CDBusConnection::dispatch()
|
||||
{
|
||||
dbus_connection_ref(m_connection.get());
|
||||
if (dbus_connection_get_dispatch_status(m_connection.get()) == DBUS_DISPATCH_DATA_REMAINS) {
|
||||
while (dbus_connection_dispatch(m_connection.get()) == DBUS_DISPATCH_DATA_REMAINS)
|
||||
;
|
||||
}
|
||||
dbus_connection_unref(m_connection.get());
|
||||
}
|
||||
|
||||
void CDBusConnection::setDispatchStatus(DBusConnection* connection, DBusDispatchStatus status)
|
||||
{
|
||||
if (dbus_connection_get_is_connected(connection) == FALSE) { return; }
|
||||
|
||||
switch (status) {
|
||||
case DBUS_DISPATCH_DATA_REMAINS:
|
||||
//m_dispatcher->add(this);
|
||||
break;
|
||||
case DBUS_DISPATCH_COMPLETE:
|
||||
case DBUS_DISPATCH_NEED_MEMORY:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CDBusConnection::setDispatchStatus(DBusConnection* connection, DBusDispatchStatus status, void* data)
|
||||
{
|
||||
auto* obj = static_cast<CDBusConnection*>(data);
|
||||
obj->setDispatchStatus(connection, status);
|
||||
}
|
||||
|
||||
DBusHandlerResult CDBusConnection::filterDisconnectedFunction(DBusConnection* connection, DBusMessage* message, void* data)
|
||||
{
|
||||
(void)connection; // unused
|
||||
|
||||
auto* obj = static_cast<CDBusConnection*>(data);
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
|
||||
if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
|
||||
for (auto it = obj->m_disconnectedCallbacks.begin(); it != obj->m_disconnectedCallbacks.end(); ++it) {
|
||||
it->second();
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
104
src/Network/Swift/dbusconnection.h
Normal file
104
src/Network/Swift/dbusconnection.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSCONNECTION_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSCONNECTION_H
|
||||
|
||||
#include "dbuscallbacks.h"
|
||||
#include "dbusdispatcher.h"
|
||||
#include "dbuserror.h"
|
||||
#include "dbusmessage.h"
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <event2/event.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
class CDBusObject;
|
||||
|
||||
//! DBus connection
|
||||
class CDBusConnection : public IDispatchable
|
||||
{
|
||||
public:
|
||||
//! Bus type
|
||||
enum BusType { SessionBus };
|
||||
|
||||
//! Disconnect Callback
|
||||
using DisconnectedCallback = std::function<void()>;
|
||||
|
||||
//! Default constructor
|
||||
CDBusConnection();
|
||||
|
||||
//! Constructor
|
||||
CDBusConnection(DBusConnection* connection);
|
||||
|
||||
//! Destructor
|
||||
~CDBusConnection() override;
|
||||
|
||||
// The ones below are not implemented yet.
|
||||
// If you need them, make sure that connection reference count is correct
|
||||
CDBusConnection(const CDBusConnection&) = delete;
|
||||
CDBusConnection& operator=(const CDBusConnection&) = delete;
|
||||
|
||||
//! Connect to bus
|
||||
bool connect(BusType type);
|
||||
|
||||
//! Set dispatcher
|
||||
void setDispatcher(CDBusDispatcher* dispatcher);
|
||||
|
||||
//! Request name to the bus
|
||||
void requestName(const std::string& name);
|
||||
|
||||
//! Is connected?
|
||||
bool isConnected() const;
|
||||
|
||||
//! Register a disconnected callback
|
||||
void registerDisconnectedCallback(CDBusObject* obj, DisconnectedCallback func);
|
||||
|
||||
//! Register a disconnected callback
|
||||
void unregisterDisconnectedCallback(CDBusObject* obj);
|
||||
|
||||
//! Register DBus object with interfaceName and objectPath.
|
||||
//! \param object
|
||||
//! \param interfaceName
|
||||
//! \param objectPath
|
||||
//! \param dbusObjectPathVTable Virtual table handling DBus messages
|
||||
void registerObjectPath(CDBusObject* object, const std::string& interfaceName, const std::string& objectPath, const DBusObjectPathVTable& dbusObjectPathVTable);
|
||||
|
||||
//! Send message to bus
|
||||
void sendMessage(const CDBusMessage& message);
|
||||
|
||||
//! Close connection
|
||||
void close();
|
||||
|
||||
//! Get the last error
|
||||
CDBusError lastError() const { return m_lastError; }
|
||||
|
||||
protected:
|
||||
// cppcheck-suppress virtualCallInConstructor
|
||||
virtual void dispatch() override final;
|
||||
|
||||
private:
|
||||
void setDispatchStatus(DBusConnection* connection, DBusDispatchStatus status);
|
||||
static void setDispatchStatus(DBusConnection* connection, DBusDispatchStatus status, void* data);
|
||||
static DBusHandlerResult filterDisconnectedFunction(DBusConnection* connection, DBusMessage* message, void* data);
|
||||
|
||||
struct DBusConnectionDeleter {
|
||||
void operator()(DBusConnection* obj) const { dbus_connection_unref(obj); }
|
||||
};
|
||||
|
||||
CDBusDispatcher* m_dispatcher = nullptr;
|
||||
std::unique_ptr<DBusConnection, DBusConnectionDeleter> m_connection;
|
||||
CDBusError m_lastError;
|
||||
std::unordered_map<CDBusObject*, DisconnectedCallback> m_disconnectedCallbacks;
|
||||
};
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
242
src/Network/Swift/dbusdispatcher.cpp
Normal file
242
src/Network/Swift/dbusdispatcher.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "dbusdispatcher.h"
|
||||
#include "dbusconnection.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace { // anonymosu namespace
|
||||
|
||||
template <typename T, typename... Args>
|
||||
std::unique_ptr<T> our_make_unique(Args&&... args)
|
||||
{
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
//! Functor struct deleteing an event
|
||||
struct EventDeleter {
|
||||
//! Delete functor
|
||||
void operator()(event* obj) const
|
||||
{
|
||||
event_del(obj);
|
||||
event_free(obj);
|
||||
}
|
||||
};
|
||||
|
||||
//! DBus watch handler
|
||||
class WatchHandler
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
WatchHandler(event_base* base, DBusWatch* watch)
|
||||
: m_base(base), m_watch(watch)
|
||||
{
|
||||
const unsigned int flags = dbus_watch_get_flags(watch);
|
||||
short monitoredEvents = EV_PERSIST;
|
||||
|
||||
if (flags & DBUS_WATCH_READABLE) { monitoredEvents |= EV_READ; }
|
||||
if (flags & DBUS_WATCH_WRITABLE) { monitoredEvents |= EV_WRITE; }
|
||||
|
||||
const int fd = dbus_watch_get_unix_fd(watch);
|
||||
m_event.reset(event_new(m_base, fd, monitoredEvents, callback, this));
|
||||
event_add(m_event.get(), nullptr);
|
||||
}
|
||||
|
||||
//! Get DBus watch
|
||||
DBusWatch* getWatch() { return m_watch; }
|
||||
|
||||
//! Get DBus watch
|
||||
const DBusWatch* getWatch() const { return m_watch; }
|
||||
|
||||
private:
|
||||
//! Event callback
|
||||
static void callback(evutil_socket_t fd, short event, void* data)
|
||||
{
|
||||
(void)fd; // Not really unused, but GCC/Clang still complain about it.
|
||||
auto* watchHandler = static_cast<WatchHandler*>(data);
|
||||
|
||||
unsigned int flags = 0;
|
||||
if (event & EV_READ) { flags |= DBUS_WATCH_READABLE; }
|
||||
if (event & EV_WRITE) { flags |= DBUS_WATCH_WRITABLE; }
|
||||
dbus_watch_handle(watchHandler->m_watch, flags);
|
||||
}
|
||||
|
||||
event_base* m_base = nullptr;
|
||||
std::unique_ptr<event, EventDeleter> m_event;
|
||||
DBusWatch* m_watch = nullptr;
|
||||
};
|
||||
|
||||
//! DBus timeout handler
|
||||
class TimeoutHandler
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
TimeoutHandler(event_base* base, DBusTimeout* timeout)
|
||||
: m_base(base), m_timeout(timeout)
|
||||
{
|
||||
timeval timer;
|
||||
const int interval = dbus_timeout_get_interval(timeout);
|
||||
timer.tv_sec = interval / 1000;
|
||||
timer.tv_usec = (interval % 1000) * 1000;
|
||||
|
||||
m_event.reset(evtimer_new(m_base, callback, this));
|
||||
evtimer_add(m_event.get(), &timer);
|
||||
}
|
||||
|
||||
//! Get DBus timeout
|
||||
const DBusTimeout* getTimeout() const { return m_timeout; }
|
||||
|
||||
private:
|
||||
//! Event callback
|
||||
static void callback(evutil_socket_t fd, short event, void* data)
|
||||
{
|
||||
(void)fd; // unused
|
||||
(void)event; // unused
|
||||
auto* timeoutHandler = static_cast<TimeoutHandler*>(data);
|
||||
dbus_timeout_handle(timeoutHandler->m_timeout);
|
||||
}
|
||||
|
||||
event_base* m_base = nullptr;
|
||||
std::unique_ptr<event, EventDeleter> m_event;
|
||||
DBusTimeout* m_timeout = nullptr;
|
||||
};
|
||||
|
||||
//! Generic Timer
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
Timer() = default;
|
||||
//! Constructor
|
||||
Timer(event_base* base, const timeval& timeout, const std::function<void()>& func)
|
||||
: m_base(base), m_func(func)
|
||||
{
|
||||
m_event.reset(evtimer_new(m_base, callback, this));
|
||||
evtimer_add(m_event.get(), &timeout);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Event callback
|
||||
static void callback(evutil_socket_t fd, short event, void* data)
|
||||
{
|
||||
(void)fd; // unused
|
||||
(void)event; // unused
|
||||
auto* timer = static_cast<Timer*>(data);
|
||||
timer->m_func();
|
||||
delete timer;
|
||||
}
|
||||
|
||||
event_base* m_base = nullptr;
|
||||
std::unique_ptr<event, EventDeleter> m_event;
|
||||
std::function<void()> m_func;
|
||||
};
|
||||
|
||||
CDBusDispatcher::CDBusDispatcher() : m_eventBase(event_base_new())
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
m_watchCallbacks = WatchCallbacks(std::bind(&CDBusDispatcher::dbusAddWatch, this, _1),
|
||||
std::bind(&CDBusDispatcher::dbusRemoveWatch, this, _1),
|
||||
std::bind(&CDBusDispatcher::dbusWatchToggled, this, _1));
|
||||
|
||||
m_timeoutCallbacks = TimeoutCallbacks(std::bind(&CDBusDispatcher::dbusAddTimeout, this, _1),
|
||||
std::bind(&CDBusDispatcher::dbusRemoveTimeout, this, _1),
|
||||
std::bind(&CDBusDispatcher::dbusTimeoutToggled, this, _1));
|
||||
}
|
||||
|
||||
CDBusDispatcher::~CDBusDispatcher()
|
||||
{
|
||||
}
|
||||
|
||||
void CDBusDispatcher::add(IDispatchable* dispatchable)
|
||||
{
|
||||
m_dispatchList.push_back(dispatchable);
|
||||
}
|
||||
|
||||
void CDBusDispatcher::remove(IDispatchable* dispatchable)
|
||||
{
|
||||
auto it = std::find(m_dispatchList.begin(), m_dispatchList.end(), dispatchable);
|
||||
if (it != m_dispatchList.end()) { m_dispatchList.erase(it); }
|
||||
}
|
||||
|
||||
void CDBusDispatcher::waitAndRun()
|
||||
{
|
||||
if (!m_eventBase) { return; }
|
||||
event_base_dispatch(m_eventBase.get());
|
||||
}
|
||||
|
||||
void CDBusDispatcher::runOnce()
|
||||
{
|
||||
if (!m_eventBase) { return; }
|
||||
event_base_loop(m_eventBase.get(), EVLOOP_NONBLOCK);
|
||||
dispatch();
|
||||
}
|
||||
|
||||
void CDBusDispatcher::dispatch()
|
||||
{
|
||||
if (m_dispatchList.empty()) { return; }
|
||||
|
||||
for (IDispatchable* dispatchable : m_dispatchList) {
|
||||
dispatchable->dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
dbus_bool_t CDBusDispatcher::dbusAddWatch(DBusWatch* watch)
|
||||
{
|
||||
if (dbus_watch_get_enabled(watch) == FALSE) { return true; }
|
||||
|
||||
int fd = dbus_watch_get_unix_fd(watch);
|
||||
m_watchers.emplace(fd, our_make_unique<WatchHandler>(m_eventBase.get(), watch));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDBusDispatcher::dbusRemoveWatch(DBusWatch* watch)
|
||||
{
|
||||
for (auto it = m_watchers.begin(); it != m_watchers.end();) {
|
||||
if (it->second->getWatch() == watch) {
|
||||
it = m_watchers.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CDBusDispatcher::dbusWatchToggled(DBusWatch* watch)
|
||||
{
|
||||
if (dbus_watch_get_enabled(watch) == TRUE) {
|
||||
dbusAddWatch(watch);
|
||||
} else {
|
||||
dbusRemoveWatch(watch);
|
||||
}
|
||||
}
|
||||
|
||||
dbus_bool_t CDBusDispatcher::dbusAddTimeout(DBusTimeout* timeout)
|
||||
{
|
||||
if (dbus_timeout_get_enabled(timeout) == FALSE) { return TRUE; }
|
||||
m_timeouts.emplace_back(new TimeoutHandler(m_eventBase.get(), timeout));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDBusDispatcher::dbusRemoveTimeout(DBusTimeout* timeout)
|
||||
{
|
||||
auto predicate = [timeout](const std::unique_ptr<TimeoutHandler>& ptr) {
|
||||
return ptr->getTimeout() == timeout;
|
||||
};
|
||||
|
||||
m_timeouts.erase(std::remove_if(m_timeouts.begin(), m_timeouts.end(), predicate), m_timeouts.end());
|
||||
}
|
||||
|
||||
void CDBusDispatcher::dbusTimeoutToggled(DBusTimeout* timeout)
|
||||
{
|
||||
if (dbus_timeout_get_enabled(timeout) == TRUE)
|
||||
dbusAddTimeout(timeout);
|
||||
else
|
||||
dbusRemoveTimeout(timeout);
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
99
src/Network/Swift/dbusdispatcher.h
Normal file
99
src/Network/Swift/dbusdispatcher.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSDISPATCHER_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSDISPATCHER_H
|
||||
|
||||
#include "dbuscallbacks.h"
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
class WatchHandler;
|
||||
class TimeoutHandler;
|
||||
class CDBusConnection;
|
||||
class CDBusDispatcher;
|
||||
|
||||
//! Dispatchable Interface
|
||||
class IDispatchable
|
||||
{
|
||||
public:
|
||||
//! Default constructor
|
||||
IDispatchable() = default;
|
||||
|
||||
//! Default destructor
|
||||
virtual ~IDispatchable() = default;
|
||||
|
||||
//! Dispatch execution method
|
||||
virtual void dispatch() = 0;
|
||||
|
||||
private:
|
||||
friend CDBusDispatcher;
|
||||
};
|
||||
|
||||
//! DBus Dispatcher
|
||||
class CDBusDispatcher
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CDBusDispatcher();
|
||||
|
||||
//! Destructor
|
||||
virtual ~CDBusDispatcher();
|
||||
|
||||
//! Add dispatchable object
|
||||
void add(IDispatchable* dispatchable);
|
||||
|
||||
//! Remove dispatchable object
|
||||
void remove(IDispatchable* dispatchable);
|
||||
|
||||
//! Waits for events to be dispatched and handles them
|
||||
void waitAndRun();
|
||||
|
||||
//! Dispatches ready handlers and returns without waiting
|
||||
void runOnce();
|
||||
|
||||
private:
|
||||
friend class WatchHandler;
|
||||
friend class TimeoutHandler;
|
||||
friend class Timer;
|
||||
friend class CDBusConnection;
|
||||
friend class CDBusServer;
|
||||
|
||||
struct EventBaseDeleter {
|
||||
void operator()(event_base* obj) const { event_base_free(obj); }
|
||||
};
|
||||
|
||||
using WatchCallbacks = DBusAsyncCallbacks<DBusWatch>;
|
||||
using TimeoutCallbacks = DBusAsyncCallbacks<DBusTimeout>;
|
||||
|
||||
void dispatch();
|
||||
|
||||
dbus_bool_t dbusAddWatch(DBusWatch* watch);
|
||||
void dbusRemoveWatch(DBusWatch* watch);
|
||||
void dbusWatchToggled(DBusWatch* watch);
|
||||
|
||||
dbus_bool_t dbusAddTimeout(DBusTimeout* timeout);
|
||||
void dbusRemoveTimeout(DBusTimeout* timeout);
|
||||
void dbusTimeoutToggled(DBusTimeout* timeout);
|
||||
|
||||
WatchCallbacks m_watchCallbacks;
|
||||
TimeoutCallbacks m_timeoutCallbacks;
|
||||
std::unordered_multimap<evutil_socket_t, std::unique_ptr<WatchHandler>> m_watchers;
|
||||
std::vector<std::unique_ptr<TimeoutHandler>> m_timeouts;
|
||||
std::unique_ptr<event_base, EventBaseDeleter> m_eventBase;
|
||||
|
||||
std::vector<IDispatchable*> m_dispatchList;
|
||||
};
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif
|
||||
16
src/Network/Swift/dbuserror.cpp
Normal file
16
src/Network/Swift/dbuserror.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "dbuserror.h"
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
CDBusError::CDBusError(const DBusError* error)
|
||||
: m_name(error->name), m_message(error->message)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
42
src/Network/Swift/dbuserror.h
Normal file
42
src/Network/Swift/dbuserror.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSERROR_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSERROR_H
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <string>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
//! DBus error
|
||||
class CDBusError
|
||||
{
|
||||
public:
|
||||
//! Error type
|
||||
enum ErrorType {
|
||||
NoError,
|
||||
Other
|
||||
};
|
||||
|
||||
//! Default constructur
|
||||
CDBusError() = default;
|
||||
|
||||
//! Constructor
|
||||
explicit CDBusError(const DBusError* error);
|
||||
|
||||
//! Get error type
|
||||
ErrorType getType() const { return m_errorType; }
|
||||
|
||||
private:
|
||||
ErrorType m_errorType = NoError;
|
||||
std::string m_name;
|
||||
std::string m_message;
|
||||
};
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
246
src/Network/Swift/dbusmessage.cpp
Normal file
246
src/Network/Swift/dbusmessage.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "dbusmessage.h"
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
CDBusMessage::CDBusMessage(DBusMessage* message)
|
||||
{
|
||||
m_message = dbus_message_ref(message);
|
||||
}
|
||||
|
||||
CDBusMessage::CDBusMessage(const CDBusMessage& other)
|
||||
{
|
||||
m_message = dbus_message_ref(other.m_message);
|
||||
m_serial = other.m_serial;
|
||||
}
|
||||
|
||||
CDBusMessage::CDBusMessage(DBusMessage* message, dbus_uint32_t serial)
|
||||
{
|
||||
m_message = dbus_message_ref(message);
|
||||
m_serial = serial;
|
||||
}
|
||||
|
||||
CDBusMessage::~CDBusMessage()
|
||||
{
|
||||
dbus_message_unref(m_message);
|
||||
}
|
||||
|
||||
CDBusMessage& CDBusMessage::operator=(CDBusMessage other)
|
||||
{
|
||||
std::swap(m_serial, other.m_serial);
|
||||
m_message = dbus_message_ref(other.m_message);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool CDBusMessage::isMethodCall() const
|
||||
{
|
||||
return dbus_message_get_type(m_message) == DBUS_MESSAGE_TYPE_METHOD_CALL;
|
||||
}
|
||||
|
||||
bool CDBusMessage::wantsReply() const
|
||||
{
|
||||
return !dbus_message_get_no_reply(m_message);
|
||||
}
|
||||
|
||||
std::string CDBusMessage::getSender() const
|
||||
{
|
||||
const char* sender = dbus_message_get_sender(m_message);
|
||||
return sender ? std::string(sender) : std::string();
|
||||
}
|
||||
|
||||
dbus_uint32_t CDBusMessage::getSerial() const
|
||||
{
|
||||
return dbus_message_get_serial(m_message);
|
||||
}
|
||||
|
||||
std::string CDBusMessage::getInterfaceName() const
|
||||
{
|
||||
return dbus_message_get_interface(m_message);
|
||||
}
|
||||
|
||||
std::string CDBusMessage::getObjectPath() const
|
||||
{
|
||||
return dbus_message_get_path(m_message);
|
||||
}
|
||||
|
||||
std::string CDBusMessage::getMethodName() const
|
||||
{
|
||||
return dbus_message_get_member(m_message);
|
||||
}
|
||||
|
||||
void CDBusMessage::beginArgumentWrite()
|
||||
{
|
||||
dbus_message_iter_init_append(m_message, &m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(bool value)
|
||||
{
|
||||
dbus_bool_t boolean = value ? 1 : 0;
|
||||
dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_BOOLEAN, &boolean);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(const char* value)
|
||||
{
|
||||
dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_STRING, &value);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(const std::string& value)
|
||||
{
|
||||
const char* ptr = value.c_str();
|
||||
dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_STRING, &ptr);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(int value)
|
||||
{
|
||||
dbus_int32_t i = value;
|
||||
dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_INT32, &i);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(double value)
|
||||
{
|
||||
dbus_message_iter_append_basic(&m_messageIterator, DBUS_TYPE_DOUBLE, &value);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(const std::vector<double>& array)
|
||||
{
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus_message_iter_open_container(&m_messageIterator, DBUS_TYPE_ARRAY, DBUS_TYPE_DOUBLE_AS_STRING, &arrayIterator);
|
||||
const double* ptr = array.data();
|
||||
dbus_message_iter_append_fixed_array(&arrayIterator, DBUS_TYPE_DOUBLE, &ptr, static_cast<int>(array.size()));
|
||||
dbus_message_iter_close_container(&m_messageIterator, &arrayIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::appendArgument(const std::vector<std::string>& array)
|
||||
{
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus_message_iter_open_container(&m_messageIterator, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &arrayIterator);
|
||||
for (const auto& i : array) {
|
||||
const char* ptr = i.c_str();
|
||||
dbus_message_iter_append_basic(&arrayIterator, DBUS_TYPE_STRING, &ptr);
|
||||
}
|
||||
dbus_message_iter_close_container(&m_messageIterator, &arrayIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::beginArgumentRead()
|
||||
{
|
||||
dbus_message_iter_init(m_message, &m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(int& value)
|
||||
{
|
||||
if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_INT32) { return; }
|
||||
dbus_int32_t i;
|
||||
dbus_message_iter_get_basic(&m_messageIterator, &i);
|
||||
value = i;
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(bool& value)
|
||||
{
|
||||
if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_BOOLEAN) { return; }
|
||||
dbus_bool_t v;
|
||||
dbus_message_iter_get_basic(&m_messageIterator, &v);
|
||||
if (v == TRUE) {
|
||||
value = true;
|
||||
} else {
|
||||
value = false;
|
||||
}
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(double& value)
|
||||
{
|
||||
if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_DOUBLE) { return; }
|
||||
dbus_message_iter_get_basic(&m_messageIterator, &value);
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(std::string& value)
|
||||
{
|
||||
const char* str = nullptr;
|
||||
if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_STRING) { return; }
|
||||
dbus_message_iter_get_basic(&m_messageIterator, &str);
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
value = std::string(str);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(std::vector<int>& value)
|
||||
{
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus_message_iter_recurse(&m_messageIterator, &arrayIterator);
|
||||
do {
|
||||
if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_INT32) { return; }
|
||||
dbus_int32_t i;
|
||||
dbus_message_iter_get_basic(&arrayIterator, &i);
|
||||
value.push_back(i);
|
||||
} while (dbus_message_iter_next(&arrayIterator));
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(std::vector<bool>& value)
|
||||
{
|
||||
if (dbus_message_iter_get_arg_type(&m_messageIterator) != DBUS_TYPE_ARRAY) { return; }
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus_message_iter_recurse(&m_messageIterator, &arrayIterator);
|
||||
do {
|
||||
if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_BOOLEAN) { return; }
|
||||
dbus_bool_t b;
|
||||
dbus_message_iter_get_basic(&arrayIterator, &b);
|
||||
if (b == TRUE) {
|
||||
value.push_back(true);
|
||||
} else {
|
||||
value.push_back(false);
|
||||
}
|
||||
} while (dbus_message_iter_next(&arrayIterator));
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(std::vector<double>& value)
|
||||
{
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus_message_iter_recurse(&m_messageIterator, &arrayIterator);
|
||||
do {
|
||||
if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_DOUBLE) { return; }
|
||||
double d;
|
||||
dbus_message_iter_get_basic(&arrayIterator, &d);
|
||||
value.push_back(d);
|
||||
} while (dbus_message_iter_next(&arrayIterator));
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
void CDBusMessage::getArgument(std::vector<std::string>& value)
|
||||
{
|
||||
DBusMessageIter arrayIterator;
|
||||
dbus_message_iter_recurse(&m_messageIterator, &arrayIterator);
|
||||
do {
|
||||
if (dbus_message_iter_get_arg_type(&arrayIterator) != DBUS_TYPE_STRING) { return; }
|
||||
const char* str = nullptr;
|
||||
dbus_message_iter_get_basic(&arrayIterator, &str);
|
||||
value.push_back(std::string(str));
|
||||
} while (dbus_message_iter_next(&arrayIterator));
|
||||
dbus_message_iter_next(&m_messageIterator);
|
||||
}
|
||||
|
||||
CDBusMessage CDBusMessage::createSignal(const std::string& path, const std::string& interfaceName, const std::string& signalName)
|
||||
{
|
||||
DBusMessage* signal = dbus_message_new_signal(path.c_str(), interfaceName.c_str(), signalName.c_str());
|
||||
return CDBusMessage(signal);
|
||||
}
|
||||
|
||||
CDBusMessage CDBusMessage::createReply(const std::string& destination, dbus_uint32_t serial)
|
||||
{
|
||||
DBusMessage* reply = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_RETURN);
|
||||
dbus_message_set_no_reply(reply, TRUE);
|
||||
if (!destination.empty()) { dbus_message_set_destination(reply, destination.c_str()); }
|
||||
dbus_message_set_reply_serial(reply, serial);
|
||||
CDBusMessage msg(reply);
|
||||
dbus_message_unref(reply);
|
||||
return msg;
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
99
src/Network/Swift/dbusmessage.h
Normal file
99
src/Network/Swift/dbusmessage.h
Normal file
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSMESSAGE_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSMESSAGE_H
|
||||
|
||||
#include "dbus/dbus.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
//! DBus Message
|
||||
class CDBusMessage
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
//! @{
|
||||
CDBusMessage(DBusMessage* message);
|
||||
CDBusMessage(const CDBusMessage& other);
|
||||
//! @}
|
||||
|
||||
//! Destructor
|
||||
~CDBusMessage();
|
||||
|
||||
//! Assignment operator
|
||||
CDBusMessage& operator=(CDBusMessage other);
|
||||
|
||||
//! Is this message a method call?
|
||||
bool isMethodCall() const;
|
||||
|
||||
//! Does this message want a reply?
|
||||
bool wantsReply() const;
|
||||
|
||||
//! Get the message sender
|
||||
std::string getSender() const;
|
||||
|
||||
//! Get the message serial. This is usally required for reply message.
|
||||
dbus_uint32_t getSerial() const;
|
||||
|
||||
//! Get the called interface name
|
||||
std::string getInterfaceName() const;
|
||||
|
||||
//! Get the called object path
|
||||
std::string getObjectPath() const;
|
||||
|
||||
//! Get the called method name
|
||||
std::string getMethodName() const;
|
||||
|
||||
//! Begin writing argument
|
||||
void beginArgumentWrite();
|
||||
|
||||
//! Append argument. Make sure to call \sa beginArgumentWrite() before.
|
||||
//! @{
|
||||
void appendArgument(bool value);
|
||||
void appendArgument(const char* value);
|
||||
void appendArgument(const std::string& value);
|
||||
void appendArgument(int value);
|
||||
void appendArgument(double value);
|
||||
void appendArgument(const std::vector<double>& array);
|
||||
void appendArgument(const std::vector<std::string>& array);
|
||||
//! @}
|
||||
|
||||
//! Begin reading arguments
|
||||
void beginArgumentRead();
|
||||
|
||||
//! Read single argument. Make sure to call \sa beginArgumentRead() before.
|
||||
//! @{
|
||||
void getArgument(int& value);
|
||||
void getArgument(bool& value);
|
||||
void getArgument(double& value);
|
||||
void getArgument(std::string& value);
|
||||
void getArgument(std::vector<int>& value);
|
||||
void getArgument(std::vector<bool>& value);
|
||||
void getArgument(std::vector<double>& value);
|
||||
void getArgument(std::vector<std::string>& value);
|
||||
//! @}
|
||||
|
||||
//! Creates a DBus message containing a DBus signal
|
||||
static CDBusMessage createSignal(const std::string& path, const std::string& interfaceName, const std::string& signalName);
|
||||
|
||||
//! Creates a DBus message containing a DBus reply
|
||||
static CDBusMessage createReply(const std::string& destination, dbus_uint32_t serial);
|
||||
|
||||
private:
|
||||
friend class CDBusConnection;
|
||||
|
||||
DBusMessage* m_message = nullptr;
|
||||
DBusMessageIter m_messageIterator;
|
||||
CDBusMessage(DBusMessage* message, dbus_uint32_t serial);
|
||||
dbus_uint32_t m_serial = 0;
|
||||
};
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
91
src/Network/Swift/dbusobject.cpp
Normal file
91
src/Network/Swift/dbusobject.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "dbusobject.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
CDBusObject::CDBusObject()
|
||||
{
|
||||
}
|
||||
|
||||
CDBusObject::~CDBusObject()
|
||||
{
|
||||
if (m_dbusConnection) { m_dbusConnection->unregisterDisconnectedCallback(this); }
|
||||
};
|
||||
|
||||
void CDBusObject::setDBusConnection(const std::shared_ptr<CDBusConnection>& dbusConnection)
|
||||
{
|
||||
m_dbusConnection = dbusConnection;
|
||||
dbusConnectedHandler();
|
||||
CDBusConnection::DisconnectedCallback disconnectedHandler = std::bind(&CDBusObject::dbusDisconnectedHandler, this);
|
||||
m_dbusConnection->registerDisconnectedCallback(this, disconnectedHandler);
|
||||
}
|
||||
|
||||
void CDBusObject::registerDBusObjectPath(const std::string& interfaceName, const std::string& objectPath)
|
||||
{
|
||||
assert(m_dbusConnection);
|
||||
m_interfaceName = interfaceName;
|
||||
m_objectPath = objectPath;
|
||||
m_dbusConnection->registerObjectPath(this, interfaceName, objectPath, m_dbusObjectPathVTable);
|
||||
}
|
||||
|
||||
void CDBusObject::sendDBusSignal(const std::string& name)
|
||||
{
|
||||
if (!m_dbusConnection) { return; }
|
||||
CDBusMessage signal = CDBusMessage::createSignal(m_objectPath, m_interfaceName, name);
|
||||
m_dbusConnection->sendMessage(signal);
|
||||
}
|
||||
|
||||
void CDBusObject::sendDBusMessage(const CDBusMessage& message)
|
||||
{
|
||||
if (!m_dbusConnection) { return; }
|
||||
m_dbusConnection->sendMessage(message);
|
||||
}
|
||||
|
||||
void CDBusObject::maybeSendEmptyDBusReply(bool wantsReply, const std::string& destination, dbus_uint32_t serial)
|
||||
{
|
||||
if (wantsReply) {
|
||||
CDBusMessage reply = CDBusMessage::createReply(destination, serial);
|
||||
m_dbusConnection->sendMessage(reply);
|
||||
}
|
||||
}
|
||||
|
||||
void CDBusObject::queueDBusCall(const std::function<void()>& func)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
m_qeuedDBusCalls.push_back(func);
|
||||
}
|
||||
|
||||
void CDBusObject::invokeQueuedDBusCalls()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
while (m_qeuedDBusCalls.size() > 0) {
|
||||
m_qeuedDBusCalls.front()();
|
||||
m_qeuedDBusCalls.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void CDBusObject::dbusObjectPathUnregisterFunction(DBusConnection* connection, void* data)
|
||||
{
|
||||
(void)connection; // unused
|
||||
(void)data; // unused
|
||||
}
|
||||
|
||||
DBusHandlerResult CDBusObject::dbusObjectPathMessageFunction(DBusConnection* connection, DBusMessage* message, void* data)
|
||||
{
|
||||
(void)connection; // unused
|
||||
|
||||
auto* obj = static_cast<CDBusObject*>(data);
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
|
||||
CDBusMessage dbusMessage(message);
|
||||
return obj->dbusMessageHandler(dbusMessage);
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
94
src/Network/Swift/dbusobject.h
Normal file
94
src/Network/Swift/dbusobject.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSOBJECT_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSOBJECT_H
|
||||
|
||||
#include "dbusconnection.h"
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
//! DBus base object
|
||||
class CDBusObject
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CDBusObject();
|
||||
|
||||
//! Destructor
|
||||
virtual ~CDBusObject();
|
||||
|
||||
//! Set the assigned DBus connection.
|
||||
//! \remark Currently one object can only manage one connection at a time
|
||||
void setDBusConnection(const std::shared_ptr<CDBusConnection>& dbusConnection);
|
||||
|
||||
//! Register itself with interfaceName and objectPath
|
||||
//! \warning Before calling this method, make sure that a valid DBus connection was set.
|
||||
void registerDBusObjectPath(const std::string& interfaceName, const std::string& objectPath);
|
||||
|
||||
protected:
|
||||
//! Handler which is called when DBusCconnection is established
|
||||
virtual void dbusConnectedHandler() {}
|
||||
|
||||
//! DBus message handler
|
||||
virtual DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) = 0;
|
||||
|
||||
//! Handler which is called when DBusConnection disconnected
|
||||
virtual void dbusDisconnectedHandler() {}
|
||||
|
||||
//! Send DBus signal
|
||||
void sendDBusSignal(const std::string& name);
|
||||
|
||||
//! Send DBus message
|
||||
void sendDBusMessage(const CDBusMessage& message);
|
||||
|
||||
//! Maybe sends an empty DBus reply (acknowledgement)
|
||||
void maybeSendEmptyDBusReply(bool wantsReply, const std::string& destination, dbus_uint32_t serial);
|
||||
|
||||
//! Send DBus reply
|
||||
template <typename T>
|
||||
void sendDBusReply(const std::string& destination, dbus_uint32_t serial, const T& argument)
|
||||
{
|
||||
CDBusMessage reply = CDBusMessage::createReply(destination, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(argument);
|
||||
m_dbusConnection->sendMessage(reply);
|
||||
}
|
||||
|
||||
//! Send DBus reply
|
||||
template <typename T>
|
||||
void sendDBusReply(const std::string& destination, dbus_uint32_t serial, const std::vector<T>& array)
|
||||
{
|
||||
CDBusMessage reply = CDBusMessage::createReply(destination, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(array);
|
||||
m_dbusConnection->sendMessage(reply);
|
||||
}
|
||||
|
||||
//! Queue a DBus call to be executed in a different thread
|
||||
void queueDBusCall(const std::function<void()>& func);
|
||||
|
||||
//! Invoke all pending DBus calls. They will be executed in the calling thread.
|
||||
void invokeQueuedDBusCalls();
|
||||
|
||||
private:
|
||||
static void dbusObjectPathUnregisterFunction(DBusConnection* connection, void* data);
|
||||
static DBusHandlerResult dbusObjectPathMessageFunction(DBusConnection* connection, DBusMessage* message, void* data);
|
||||
|
||||
std::shared_ptr<CDBusConnection> m_dbusConnection;
|
||||
std::string m_interfaceName;
|
||||
std::string m_objectPath;
|
||||
|
||||
std::mutex m_mutex;
|
||||
std::deque<std::function<void()>> m_qeuedDBusCalls;
|
||||
|
||||
const DBusObjectPathVTable m_dbusObjectPathVTable = {dbusObjectPathUnregisterFunction, dbusObjectPathMessageFunction, nullptr, nullptr, nullptr, nullptr};
|
||||
};
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
84
src/Network/Swift/dbusserver.cpp
Normal file
84
src/Network/Swift/dbusserver.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "dbusobject.h"
|
||||
#include "dbusserver.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
CDBusServer::CDBusServer()
|
||||
{
|
||||
dbus_threads_init_default();
|
||||
}
|
||||
|
||||
CDBusServer::~CDBusServer()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
bool CDBusServer::listen(const std::string& address)
|
||||
{
|
||||
DBusError error;
|
||||
dbus_error_init(&error);
|
||||
m_server.reset(dbus_server_listen(address.c_str(), &error));
|
||||
|
||||
if (!m_server) {
|
||||
return false;
|
||||
}
|
||||
|
||||
dbus_server_set_new_connection_function(m_server.get(), onNewConnection, this, nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CDBusServer::isConnected() const
|
||||
{
|
||||
return m_server ? dbus_server_get_is_connected(m_server.get()) : false;
|
||||
}
|
||||
|
||||
void CDBusServer::close()
|
||||
{
|
||||
if (m_server) { dbus_server_disconnect(m_server.get()); }
|
||||
}
|
||||
|
||||
void CDBusServer::setDispatcher(CDBusDispatcher* dispatcher)
|
||||
{
|
||||
assert(dispatcher);
|
||||
assert(m_server);
|
||||
|
||||
m_dispatcher = dispatcher;
|
||||
|
||||
dbus_server_set_watch_functions(
|
||||
m_server.get(),
|
||||
dispatcher->m_watchCallbacks.add,
|
||||
dispatcher->m_watchCallbacks.remove,
|
||||
dispatcher->m_watchCallbacks.toggled,
|
||||
&dispatcher->m_watchCallbacks, nullptr);
|
||||
|
||||
dbus_server_set_timeout_functions(
|
||||
m_server.get(),
|
||||
dispatcher->m_timeoutCallbacks.add,
|
||||
dispatcher->m_timeoutCallbacks.remove,
|
||||
dispatcher->m_timeoutCallbacks.toggled,
|
||||
&dispatcher->m_timeoutCallbacks, nullptr);
|
||||
}
|
||||
|
||||
void CDBusServer::onNewConnection(DBusServer*, DBusConnection* conn)
|
||||
{
|
||||
auto dbusConnection = std::make_shared<CDBusConnection>(conn);
|
||||
m_newConnectionFunc(dbusConnection);
|
||||
}
|
||||
|
||||
void CDBusServer::onNewConnection(DBusServer* server, DBusConnection* conn, void* data)
|
||||
{
|
||||
auto* obj = static_cast<CDBusServer*>(data);
|
||||
obj->onNewConnection(server, conn);
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
79
src/Network/Swift/dbusserver.h
Normal file
79
src/Network/Swift/dbusserver.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_DBUSSERVER_H
|
||||
#define BLACKSIM_FGSWIFTBUS_DBUSSERVER_H
|
||||
|
||||
#include "dbuscallbacks.h"
|
||||
#include "dbusdispatcher.h"
|
||||
#include "dbuserror.h"
|
||||
#include "dbusmessage.h"
|
||||
|
||||
#include <dbus/dbus.h>
|
||||
#include <event2/event.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
class CDBusObject;
|
||||
|
||||
//! DBus connection
|
||||
class CDBusServer : public IDispatchable
|
||||
{
|
||||
public:
|
||||
//! New connection handler function
|
||||
using NewConnectionFunc = std::function<void(std::shared_ptr<CDBusConnection>)>;
|
||||
|
||||
//! Constructor
|
||||
CDBusServer();
|
||||
|
||||
//! Destructor
|
||||
~CDBusServer();
|
||||
|
||||
//! Set the dispatcher
|
||||
void setDispatcher(CDBusDispatcher* dispatcher);
|
||||
|
||||
//! Connect to bus
|
||||
bool listen(const std::string& address);
|
||||
|
||||
//! Is connected?
|
||||
bool isConnected() const;
|
||||
|
||||
void dispatch() override {}
|
||||
|
||||
//! Close connection
|
||||
void close();
|
||||
|
||||
//! Get the last error
|
||||
CDBusError lastError() const { return m_lastError; }
|
||||
|
||||
//! Set the function to be used for handling new connections.
|
||||
void setNewConnectionFunc(const NewConnectionFunc& func)
|
||||
{
|
||||
m_newConnectionFunc = func;
|
||||
}
|
||||
|
||||
private:
|
||||
void onNewConnection(DBusServer* server, DBusConnection* conn);
|
||||
static void onNewConnection(DBusServer* server, DBusConnection* conn, void* data);
|
||||
|
||||
struct DBusServerDeleter {
|
||||
void operator()(DBusServer* obj) const { dbus_server_unref(obj); }
|
||||
};
|
||||
|
||||
CDBusDispatcher* m_dispatcher = nullptr;
|
||||
std::unique_ptr<DBusServer, DBusServerDeleter> m_server;
|
||||
CDBusError m_lastError;
|
||||
NewConnectionFunc m_newConnectionFunc;
|
||||
};
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
74
src/Network/Swift/plugin.cpp
Normal file
74
src/Network/Swift/plugin.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "traffic.h"
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <thread>
|
||||
|
||||
namespace {
|
||||
inline std::string fgswiftbusServiceName()
|
||||
{
|
||||
return "org.swift-project.fgswiftbus";
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace FGSwiftBus {
|
||||
CPlugin::CPlugin()
|
||||
{
|
||||
startServer();
|
||||
}
|
||||
|
||||
CPlugin::~CPlugin()
|
||||
{
|
||||
if (m_dbusConnection) {
|
||||
m_dbusConnection->close();
|
||||
}
|
||||
m_shouldStop = true;
|
||||
if (m_dbusThread.joinable()) { m_dbusThread.join(); }
|
||||
}
|
||||
|
||||
void CPlugin::startServer()
|
||||
{
|
||||
m_service.reset(new CService());
|
||||
m_traffic.reset(new CTraffic());
|
||||
m_dbusP2PServer.reset(new CDBusServer());
|
||||
|
||||
std::string ip = fgGetString("/sim/swift/address", "127.0.0.1");
|
||||
std::string port = fgGetString("/sim/swift/port", "45003");
|
||||
std::string listenAddress = "tcp:host=" + ip + ",port=" + port;
|
||||
if (!m_dbusP2PServer->listen(listenAddress)) {
|
||||
m_service->addTextMessage("FGSwiftBus startup failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
m_dbusP2PServer->setDispatcher(&m_dbusDispatcher);
|
||||
m_dbusP2PServer->setNewConnectionFunc([this](const std::shared_ptr<CDBusConnection>& conn) {
|
||||
m_dbusConnection = conn;
|
||||
m_dbusConnection->setDispatcher(&m_dbusDispatcher);
|
||||
m_service->setDBusConnection(m_dbusConnection);
|
||||
m_service->registerDBusObjectPath(m_service->InterfaceName(), m_service->ObjectPath());
|
||||
m_traffic->setDBusConnection(m_dbusConnection);
|
||||
m_traffic->registerDBusObjectPath(m_traffic->InterfaceName(), m_traffic->ObjectPath());
|
||||
});
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus started");
|
||||
}
|
||||
|
||||
void CPlugin::fastLoop()
|
||||
{
|
||||
this->m_dbusDispatcher.runOnce();
|
||||
this->m_service->process();
|
||||
this->m_traffic->process();
|
||||
this->m_traffic->emitSimFrame();
|
||||
}
|
||||
} // namespace FGSwiftBus
|
||||
58
src/Network/Swift/plugin.h
Normal file
58
src/Network/Swift/plugin.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_PLUGIN_H
|
||||
#define BLACKSIM_FGSWIFTBUS_PLUGIN_H
|
||||
|
||||
//! \file
|
||||
|
||||
/*!
|
||||
* \namespace FGSwiftBus
|
||||
* Plugin loaded by Flightgear which publishes a DBus service
|
||||
*/
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "dbusconnection.h"
|
||||
#include "dbusdispatcher.h"
|
||||
#include "dbusserver.h"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
namespace FGSwiftBus {
|
||||
class CService;
|
||||
class CTraffic;
|
||||
|
||||
/*!
|
||||
* Main plugin class
|
||||
*/
|
||||
class CPlugin
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CPlugin();
|
||||
void startServer();
|
||||
//! Destructor
|
||||
~CPlugin();
|
||||
void fastLoop();
|
||||
|
||||
private:
|
||||
CDBusDispatcher m_dbusDispatcher;
|
||||
std::unique_ptr<CDBusServer> m_dbusP2PServer;
|
||||
std::shared_ptr<CDBusConnection> m_dbusConnection;
|
||||
std::unique_ptr<CService> m_service;
|
||||
std::unique_ptr<CTraffic> m_traffic;
|
||||
|
||||
std::thread m_dbusThread;
|
||||
bool m_isRunning = false;
|
||||
bool m_shouldStop = false;
|
||||
};
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // BLACKSIM_FGSWIFTBUS_PLUGIN_H
|
||||
633
src/Network/Swift/service.cpp
Normal file
633
src/Network/Swift/service.cpp
Normal file
@@ -0,0 +1,633 @@
|
||||
/*
|
||||
* Service module for swift<->FG connection
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "service.h"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <iostream>
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#define FGSWIFTBUS_API_VERSION 3;
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
CService::CService()
|
||||
{
|
||||
// Initialize node pointers
|
||||
m_textMessageNode = fgGetNode("/sim/messages/copilot", true);
|
||||
m_aircraftModelPathNode = fgGetNode("/sim/aircraft-dir", true);
|
||||
m_aircraftDescriptionNode = fgGetNode("/sim/description", true);
|
||||
m_isPausedNode = fgGetNode("/sim/freeze/master", true);
|
||||
m_latitudeNode = fgGetNode("/position/latitude-deg", true);
|
||||
m_longitudeNode = fgGetNode("/position/longitude-deg", true);
|
||||
m_altitudeMSLNode = fgGetNode("/position/altitude-ft", true);
|
||||
m_heightAGLNode = fgGetNode("/position/altitude-agl-ft", true);
|
||||
m_groundSpeedNode = fgGetNode("/velocities/groundspeed-kt", true);
|
||||
m_pitchNode = fgGetNode("/orientation/pitch-deg", true);
|
||||
m_rollNode = fgGetNode("/orientation/roll-deg", true);
|
||||
m_trueHeadingNode = fgGetNode("/orientation/heading-deg", true);
|
||||
m_wheelsOnGroundNode = fgGetNode("/gear/gear/wow", true);
|
||||
m_com1ActiveNode = fgGetNode("/instrumentation/comm/frequencies/selected-mhz", true);
|
||||
m_com1StandbyNode = fgGetNode("/instrumentation/comm/frequencies/standby-mhz", true);
|
||||
m_com2ActiveNode = fgGetNode("/instrumentation/comm[1]/frequencies/selected-mhz", true);
|
||||
m_com2StandbyNode = fgGetNode("/instrumentation/comm[1]/frequencies/standby-mhz", true);
|
||||
m_transponderCodeNode = fgGetNode("/instrumentation/transponder/id-code", true);
|
||||
m_transponderModeNode = fgGetNode("/instrumentation/transponder/inputs/knob-mode", true);
|
||||
m_transponderIdentNode = fgGetNode("/instrumentation/transponder/ident", true);
|
||||
m_beaconLightsNode = fgGetNode("/controls/lighting/beacon", true);
|
||||
m_landingLightsNode = fgGetNode("/controls/lighting/landing-lights", true);
|
||||
m_navLightsNode = fgGetNode("/controls/lighting/nav-lights", true);
|
||||
m_strobeLightsNode = fgGetNode("/controls/lighting/strobe", true);
|
||||
m_taxiLightsNode = fgGetNode("/controls/lighting/taxi-light", true);
|
||||
m_altimeterServiceableNode = fgGetNode("/instrumentation/altimeter/serviceable", true);
|
||||
m_pressAltitudeFtNode = fgGetNode("/instrumentation/altimeter/pressure-alt-ft", true);
|
||||
m_flapsDeployRatioNode = fgGetNode("/surface-positions/flap-pos-norm", true);
|
||||
m_gearDeployRatioNode = fgGetNode("/gear/gear/position-norm", true);
|
||||
m_speedBrakeDeployRatioNode = fgGetNode("/surface-positions/speedbrake-pos-norm", true);
|
||||
m_aircraftNameNode = fgGetNode("/sim/aircraft", true);
|
||||
m_groundElevationNode = fgGetNode("/position/ground-elev-m", true);
|
||||
m_velocityXNode = fgGetNode("/velocities/speed-east-fps", true);
|
||||
m_velocityYNode = fgGetNode("/velocities/speed-down-fps", true);
|
||||
m_velocityZNode = fgGetNode("/velocities/speed-north-fps", true);
|
||||
m_rollRateNode = fgGetNode("/orientation/roll-rate-degps", true);
|
||||
m_pichRateNode = fgGetNode("/orientation/pitch-rate-degps", true);
|
||||
m_yawRateNode = fgGetNode("/orientation/yaw-rate-degps", true);
|
||||
m_com1VolumeNode = fgGetNode("/instrumentation/comm/volume", true);
|
||||
m_com2VolumeNode = fgGetNode("/instrumentation/comm[1]/volume", true);
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Service initialized");
|
||||
}
|
||||
|
||||
const std::string& CService::InterfaceName()
|
||||
{
|
||||
static const std::string s(FGSWIFTBUS_SERVICE_INTERFACENAME);
|
||||
return s;
|
||||
}
|
||||
|
||||
const std::string& CService::ObjectPath()
|
||||
{
|
||||
static const std::string s(FGSWIFTBUS_SERVICE_OBJECTPATH);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Static method
|
||||
int CService::getVersionNumber()
|
||||
{
|
||||
return FGSWIFTBUS_API_VERSION;
|
||||
}
|
||||
|
||||
void CService::addTextMessage(const std::string& text)
|
||||
{
|
||||
if (text.empty()) { return; }
|
||||
m_textMessageNode->setStringValue(text);
|
||||
}
|
||||
|
||||
std::string CService::getAircraftModelPath() const
|
||||
{
|
||||
return m_aircraftModelPathNode->getStringValue();
|
||||
}
|
||||
|
||||
std::string CService::getAircraftLivery() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string CService::getAircraftIcaoCode() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string CService::getAircraftDescription() const
|
||||
{
|
||||
return m_aircraftDescriptionNode->getStringValue();
|
||||
}
|
||||
|
||||
bool CService::isPaused() const
|
||||
{
|
||||
return m_isPausedNode->getBoolValue();
|
||||
}
|
||||
|
||||
double CService::getLatitude() const
|
||||
{
|
||||
return m_latitudeNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getLongitude() const
|
||||
{
|
||||
return m_longitudeNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getAltitudeMSL() const
|
||||
{
|
||||
return m_altitudeMSLNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getHeightAGL() const
|
||||
{
|
||||
return m_heightAGLNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getGroundSpeed() const
|
||||
{
|
||||
return m_groundSpeedNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getPitch() const
|
||||
{
|
||||
return m_pitchNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getRoll() const
|
||||
{
|
||||
return m_rollNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getTrueHeading() const
|
||||
{
|
||||
return m_trueHeadingNode->getDoubleValue();
|
||||
}
|
||||
|
||||
bool CService::getAllWheelsOnGround() const
|
||||
{
|
||||
return m_wheelsOnGroundNode->getBoolValue();
|
||||
}
|
||||
|
||||
int CService::getCom1Active() const
|
||||
{
|
||||
return (int)(m_com1ActiveNode->getDoubleValue() * 1000);
|
||||
}
|
||||
|
||||
int CService::getCom1Standby() const
|
||||
{
|
||||
return (int)(m_com1StandbyNode->getDoubleValue() * 1000);
|
||||
}
|
||||
|
||||
int CService::getCom2Active() const
|
||||
{
|
||||
return (int)(m_com2ActiveNode->getDoubleValue() * 1000);
|
||||
}
|
||||
|
||||
int CService::getCom2Standby() const
|
||||
{
|
||||
return (int)(m_com2StandbyNode->getDoubleValue() * 1000);
|
||||
}
|
||||
|
||||
int CService::getTransponderCode() const
|
||||
{
|
||||
return m_transponderCodeNode->getIntValue();
|
||||
}
|
||||
|
||||
int CService::getTransponderMode() const
|
||||
{
|
||||
return m_transponderModeNode->getIntValue();
|
||||
}
|
||||
|
||||
bool CService::getTransponderIdent() const
|
||||
{
|
||||
return m_transponderIdentNode->getBoolValue();
|
||||
}
|
||||
|
||||
bool CService::getBeaconLightsOn() const
|
||||
{
|
||||
return m_beaconLightsNode->getBoolValue();
|
||||
}
|
||||
|
||||
bool CService::getLandingLightsOn() const
|
||||
{
|
||||
return m_landingLightsNode->getBoolValue();
|
||||
}
|
||||
|
||||
bool CService::getNavLightsOn() const
|
||||
{
|
||||
return m_navLightsNode->getBoolValue();
|
||||
}
|
||||
|
||||
|
||||
bool CService::getStrobeLightsOn() const
|
||||
{
|
||||
return m_strobeLightsNode->getBoolValue();
|
||||
}
|
||||
|
||||
bool CService::getTaxiLightsOn() const
|
||||
{
|
||||
return m_taxiLightsNode->getBoolValue();
|
||||
}
|
||||
|
||||
double CService::getPressAlt() const
|
||||
{
|
||||
if (m_altimeterServiceableNode->getBoolValue()) {
|
||||
return m_pressAltitudeFtNode->getDoubleValue();
|
||||
} else {
|
||||
return m_altitudeMSLNode->getDoubleValue();
|
||||
}
|
||||
}
|
||||
|
||||
void CService::setCom1Active(int freq)
|
||||
{
|
||||
m_com1ActiveNode->setDoubleValue(freq / (double)1000);
|
||||
}
|
||||
|
||||
void CService::setCom1Standby(int freq)
|
||||
{
|
||||
m_com1StandbyNode->setDoubleValue(freq / (double)1000);
|
||||
}
|
||||
|
||||
void CService::setCom2Active(int freq)
|
||||
{
|
||||
m_com2ActiveNode->setDoubleValue(freq / (double)1000);
|
||||
}
|
||||
|
||||
void CService::setCom2Standby(int freq)
|
||||
{
|
||||
m_com2StandbyNode->setDoubleValue(freq / (double)1000);
|
||||
}
|
||||
|
||||
void CService::setTransponderCode(int code)
|
||||
{
|
||||
m_transponderCodeNode->setIntValue(code);
|
||||
}
|
||||
|
||||
void CService::setTransponderMode(int mode)
|
||||
{
|
||||
m_transponderModeNode->setIntValue(mode);
|
||||
}
|
||||
|
||||
double CService::getFlapsDeployRatio() const
|
||||
{
|
||||
return m_flapsDeployRatioNode->getFloatValue();
|
||||
}
|
||||
|
||||
double CService::getGearDeployRatio() const
|
||||
{
|
||||
return m_gearDeployRatioNode->getFloatValue();
|
||||
}
|
||||
|
||||
int CService::getNumberOfEngines() const
|
||||
{
|
||||
// TODO Use correct property
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::vector<double> CService::getEngineN1Percentage() const
|
||||
{
|
||||
// TODO use correct engine numbers
|
||||
std::vector<double> list;
|
||||
const auto number = static_cast<unsigned int>(getNumberOfEngines());
|
||||
list.reserve(number);
|
||||
for (unsigned int engineNumber = 0; engineNumber < number; ++engineNumber) {
|
||||
list.push_back(fgGetDouble("/engine/engine/n1"));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
double CService::getSpeedBrakeRatio() const
|
||||
{
|
||||
return m_speedBrakeDeployRatioNode->getFloatValue();
|
||||
}
|
||||
|
||||
double CService::getGroundElevation() const
|
||||
{
|
||||
return m_groundElevationNode->getDoubleValue();
|
||||
}
|
||||
|
||||
std::string CService::getAircraftModelFilename() const
|
||||
{
|
||||
std::string modelFileName = getAircraftName();
|
||||
modelFileName.append("-set.xml");
|
||||
return modelFileName;
|
||||
}
|
||||
|
||||
std::string CService::getAircraftModelString() const
|
||||
{
|
||||
std::string modelName = getAircraftName();
|
||||
std::string modelString = "FG " + modelName;
|
||||
return modelString;
|
||||
}
|
||||
|
||||
std::string CService::getAircraftName() const
|
||||
{
|
||||
return m_aircraftNameNode->getStringValue();
|
||||
}
|
||||
|
||||
double CService::getVelocityX() const
|
||||
{
|
||||
return m_velocityXNode->getDoubleValue() * SG_FEET_TO_METER;
|
||||
}
|
||||
|
||||
double CService::getVelocityY() const
|
||||
{
|
||||
return m_velocityYNode->getDoubleValue() * SG_FEET_TO_METER * -1; // + (up), - (down)
|
||||
}
|
||||
|
||||
double CService::getVelocityZ() const
|
||||
{
|
||||
return m_velocityZNode->getDoubleValue() * SG_FEET_TO_METER;
|
||||
}
|
||||
|
||||
double CService::getRollRate() const
|
||||
{
|
||||
return m_rollRateNode->getDoubleValue() * SG_DEGREES_TO_RADIANS;
|
||||
}
|
||||
double CService::getPitchRate() const
|
||||
{
|
||||
return m_pichRateNode->getDoubleValue() * SG_DEGREES_TO_RADIANS;
|
||||
}
|
||||
|
||||
double CService::getYawRate() const
|
||||
{
|
||||
return m_yawRateNode->getDoubleValue() * SG_DEGREES_TO_RADIANS;
|
||||
}
|
||||
|
||||
double CService::getCom1Volume() const
|
||||
{
|
||||
return m_com1VolumeNode->getDoubleValue();
|
||||
}
|
||||
|
||||
double CService::getCom2Volume() const
|
||||
{
|
||||
return m_com2VolumeNode->getDoubleValue();
|
||||
}
|
||||
|
||||
|
||||
static const char* introspection_service = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE;
|
||||
|
||||
DBusHandlerResult CService::dbusMessageHandler(const CDBusMessage& message_)
|
||||
{
|
||||
CDBusMessage message(message_);
|
||||
const std::string sender = message.getSender();
|
||||
const dbus_uint32_t serial = message.getSerial();
|
||||
const bool wantsReply = message.wantsReply();
|
||||
|
||||
if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE) {
|
||||
if (message.getMethodName() == "Introspect") {
|
||||
sendDBusReply(sender, serial, introspection_service);
|
||||
}
|
||||
} else if (message.getInterfaceName() == FGSWIFTBUS_SERVICE_INTERFACENAME) {
|
||||
if (message.getMethodName() == "addTextMessage") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
std::string text;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(text);
|
||||
|
||||
queueDBusCall([=]() {
|
||||
addTextMessage(text);
|
||||
});
|
||||
} else if (message.getMethodName() == "getOwnAircraftSituationData") {
|
||||
queueDBusCall([=]() {
|
||||
double lat = getLatitude();
|
||||
double lon = getLongitude();
|
||||
double alt = getAltitudeMSL();
|
||||
double gs = getGroundSpeed();
|
||||
double pitch = getPitch();
|
||||
double roll = getRoll();
|
||||
double trueHeading = getTrueHeading();
|
||||
double pressAlt = getPressAlt();
|
||||
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(lat);
|
||||
reply.appendArgument(lon);
|
||||
reply.appendArgument(alt);
|
||||
reply.appendArgument(gs);
|
||||
reply.appendArgument(pitch);
|
||||
reply.appendArgument(roll);
|
||||
reply.appendArgument(trueHeading);
|
||||
reply.appendArgument(pressAlt);
|
||||
sendDBusMessage(reply);
|
||||
});
|
||||
} else if (message.getMethodName() == "getOwnAircraftVelocityData") {
|
||||
queueDBusCall([=]() {
|
||||
double velocityX = getVelocityX();
|
||||
double velocityY = getVelocityY();
|
||||
double velocityZ = getVelocityZ();
|
||||
double pitchVelocity = getPitchRate();
|
||||
double rollVelocity = getRollRate();
|
||||
double yawVelocity = getYawRate();
|
||||
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(velocityX);
|
||||
reply.appendArgument(velocityY);
|
||||
reply.appendArgument(velocityZ);
|
||||
reply.appendArgument(pitchVelocity);
|
||||
reply.appendArgument(rollVelocity);
|
||||
reply.appendArgument(yawVelocity);
|
||||
sendDBusMessage(reply);
|
||||
});
|
||||
} else if (message.getMethodName() == "getVersionNumber") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getVersionNumber());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftModelPath") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftModelPath());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftModelFilename") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftModelFilename());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftModelString") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftModelString());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftName") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftName());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftLivery") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftLivery());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftIcaoCode") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftIcaoCode());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAircraftDescription") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAircraftDescription());
|
||||
});
|
||||
} else if (message.getMethodName() == "isPaused") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, isPaused());
|
||||
});
|
||||
} else if (message.getMethodName() == "getLatitudeDeg") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getLatitude());
|
||||
});
|
||||
} else if (message.getMethodName() == "getLongitudeDeg") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getLongitude());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAltitudeMslFt") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAltitudeMSL());
|
||||
});
|
||||
} else if (message.getMethodName() == "getHeightAglFt") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getHeightAGL());
|
||||
});
|
||||
} else if (message.getMethodName() == "getGroundSpeedKts") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getGroundSpeed());
|
||||
});
|
||||
} else if (message.getMethodName() == "getPitchDeg") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getPitch());
|
||||
});
|
||||
} else if (message.getMethodName() == "getRollDeg") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getRoll());
|
||||
});
|
||||
} else if (message.getMethodName() == "getAllWheelsOnGround") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getAllWheelsOnGround());
|
||||
});
|
||||
} else if (message.getMethodName() == "getCom1ActiveKhz") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getCom1Active());
|
||||
});
|
||||
} else if (message.getMethodName() == "getCom1StandbyKhz") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getCom1Standby());
|
||||
});
|
||||
} else if (message.getMethodName() == "getCom2ActiveKhz") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getCom2Active());
|
||||
});
|
||||
} else if (message.getMethodName() == "getCom2StandbyKhz") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getCom2Standby());
|
||||
});
|
||||
} else if (message.getMethodName() == "getTransponderCode") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getTransponderCode());
|
||||
});
|
||||
} else if (message.getMethodName() == "getTransponderMode") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getTransponderMode());
|
||||
});
|
||||
} else if (message.getMethodName() == "getTransponderIdent") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getTransponderIdent());
|
||||
});
|
||||
} else if (message.getMethodName() == "getBeaconLightsOn") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getBeaconLightsOn());
|
||||
});
|
||||
} else if (message.getMethodName() == "getLandingLightsOn") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getLandingLightsOn());
|
||||
});
|
||||
} else if (message.getMethodName() == "getNavLightsOn") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getNavLightsOn());
|
||||
});
|
||||
} else if (message.getMethodName() == "getStrobeLightsOn") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getStrobeLightsOn());
|
||||
});
|
||||
} else if (message.getMethodName() == "getTaxiLightsOn") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getTaxiLightsOn());
|
||||
});
|
||||
} else if (message.getMethodName() == "getPressAlt") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getPressAlt());
|
||||
});
|
||||
} else if (message.getMethodName() == "getGroundElevation") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getGroundElevation());
|
||||
});
|
||||
} else if (message.getMethodName() == "setCom1ActiveKhz") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
int frequency = 0;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(frequency);
|
||||
queueDBusCall([=]() {
|
||||
setCom1Active(frequency);
|
||||
});
|
||||
} else if (message.getMethodName() == "setCom1StandbyKhz") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
int frequency = 0;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(frequency);
|
||||
queueDBusCall([=]() {
|
||||
setCom1Standby(frequency);
|
||||
});
|
||||
} else if (message.getMethodName() == "setCom2ActiveKhz") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
int frequency = 0;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(frequency);
|
||||
queueDBusCall([=]() {
|
||||
setCom2Active(frequency);
|
||||
});
|
||||
} else if (message.getMethodName() == "setCom2StandbyKhz") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
int frequency = 0;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(frequency);
|
||||
queueDBusCall([=]() {
|
||||
setCom2Standby(frequency);
|
||||
});
|
||||
} else if (message.getMethodName() == "setTransponderCode") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
int code = 0;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(code);
|
||||
queueDBusCall([=]() {
|
||||
setTransponderCode(code);
|
||||
});
|
||||
} else if (message.getMethodName() == "setTransponderMode") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
int mode = 0;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(mode);
|
||||
queueDBusCall([=]() {
|
||||
setTransponderMode(mode);
|
||||
});
|
||||
} else if (message.getMethodName() == "getFlapsDeployRatio") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getFlapsDeployRatio());
|
||||
});
|
||||
} else if (message.getMethodName() == "getGearDeployRatio") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getGearDeployRatio());
|
||||
});
|
||||
} else if (message.getMethodName() == "getEngineN1Percentage") {
|
||||
queueDBusCall([=]() {
|
||||
std::vector<double> array = getEngineN1Percentage();
|
||||
sendDBusReply(sender, serial, array);
|
||||
});
|
||||
} else if (message.getMethodName() == "getSpeedBrakeRatio") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getSpeedBrakeRatio());
|
||||
});
|
||||
} else if (message.getMethodName() == "getCom1Volume") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getCom1Volume());
|
||||
});
|
||||
} else if (message.getMethodName() == "getCom2Volume") {
|
||||
queueDBusCall([=]() {
|
||||
sendDBusReply(sender, serial, getCom2Volume());
|
||||
});
|
||||
} else {
|
||||
// Unknown message. Tell DBus that we cannot handle it
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
int CService::process()
|
||||
{
|
||||
invokeQueuedDBusCalls();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
262
src/Network/Swift/service.h
Normal file
262
src/Network/Swift/service.h
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Service module for swift<->FG connection
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_SERVICE_H
|
||||
#define BLACKSIM_FGSWIFTBUS_SERVICE_H
|
||||
|
||||
//! \file
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
|
||||
#include "dbusobject.h"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <chrono>
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/raw_socket.hxx>
|
||||
#include <simgear/misc/stdint.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/structure/event_mgr.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
#include <string>
|
||||
|
||||
|
||||
//! \cond PRIVATE
|
||||
#define FGSWIFTBUS_SERVICE_INTERFACENAME "org.swift_project.fgswiftbus.service"
|
||||
#define FGSWIFTBUS_SERVICE_OBJECTPATH "/fgswiftbus/service"
|
||||
//! \endcond
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
/*!
|
||||
* FGSwiftBus service object which is accessible through DBus
|
||||
*/
|
||||
class CService : public CDBusObject
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CService();
|
||||
|
||||
//! DBus interface name
|
||||
static const std::string& InterfaceName();
|
||||
|
||||
//! DBus object path
|
||||
static const std::string& ObjectPath();
|
||||
|
||||
//! Getting flightgear version
|
||||
static int getVersionNumber();
|
||||
|
||||
////! Add a text message to the on-screen display, with RGB components in the range [0,1]
|
||||
void addTextMessage(const std::string& text);
|
||||
|
||||
////! Get full path to current aircraft model
|
||||
std::string getAircraftModelPath() const;
|
||||
|
||||
////! Get base filename of current aircraft model
|
||||
std::string getAircraftModelFilename() const;
|
||||
|
||||
////! Get canonical swift model string of current aircraft model
|
||||
std::string getAircraftModelString() const;
|
||||
|
||||
////! Get name of current aircraft model
|
||||
std::string getAircraftName() const;
|
||||
|
||||
////! Get path to current aircraft livery
|
||||
std::string getAircraftLivery() const;
|
||||
|
||||
//! Get the ICAO code of the current aircraft model
|
||||
std::string getAircraftIcaoCode() const;
|
||||
|
||||
////! Get the description of the current aircraft model
|
||||
std::string getAircraftDescription() const;
|
||||
|
||||
//! True if sim is paused
|
||||
bool isPaused() const;
|
||||
|
||||
//! Get aircraft latitude in degrees
|
||||
double getLatitude() const;
|
||||
|
||||
//! Get aircraft longitude in degrees
|
||||
double getLongitude() const;
|
||||
|
||||
//! Get aircraft altitude in feet
|
||||
double getAltitudeMSL() const;
|
||||
|
||||
//! Get aircraft height in feet
|
||||
double getHeightAGL() const;
|
||||
|
||||
//! Get aircraft groundspeed in knots
|
||||
double getGroundSpeed() const;
|
||||
|
||||
//! Get aircraft pitch in degrees above horizon
|
||||
double getPitch() const;
|
||||
|
||||
//! Get aircraft roll in degrees
|
||||
double getRoll() const;
|
||||
|
||||
//! Get aircraft true heading in degrees
|
||||
double getTrueHeading() const;
|
||||
|
||||
//! Get whether all wheels are on the ground
|
||||
bool getAllWheelsOnGround() const;
|
||||
|
||||
//! Get the current COM1 active frequency in kHz
|
||||
int getCom1Active() const;
|
||||
|
||||
//! Get the current COM1 standby frequency in kHz
|
||||
int getCom1Standby() const;
|
||||
|
||||
//! Get the current COM2 active frequency in kHz
|
||||
int getCom2Active() const;
|
||||
|
||||
//! Get the current COM2 standby frequency in kHz
|
||||
int getCom2Standby() const;
|
||||
|
||||
//! Get the current transponder code in decimal
|
||||
int getTransponderCode() const;
|
||||
|
||||
//! Get the current transponder mode (depends on the aircraft, 0-2 usually mean standby, >2 active)
|
||||
int getTransponderMode() const;
|
||||
|
||||
//! Get whether we are currently squawking ident
|
||||
bool getTransponderIdent() const;
|
||||
|
||||
//! Get whether beacon lights are on
|
||||
bool getBeaconLightsOn() const;
|
||||
|
||||
//! Get whether landing lights are on
|
||||
bool getLandingLightsOn() const;
|
||||
|
||||
//! Get whether nav lights are on
|
||||
bool getNavLightsOn() const;
|
||||
|
||||
//! Get whether strobe lights are on
|
||||
bool getStrobeLightsOn() const;
|
||||
|
||||
//! Get whether taxi lights are on
|
||||
bool getTaxiLightsOn() const;
|
||||
|
||||
//! Get pressure altitude in ft
|
||||
double getPressAlt() const;
|
||||
|
||||
//! Set the current COM1 active frequency in kHz
|
||||
void setCom1Active(int freq);
|
||||
|
||||
//! Set the current COM1 standby frequency in kHz
|
||||
void setCom1Standby(int freq);
|
||||
|
||||
//! Set the current COM2 active frequency in kHz
|
||||
void setCom2Active(int freq);
|
||||
|
||||
//! Set the current COM2 standby frequency in kHz
|
||||
void setCom2Standby(int freq);
|
||||
|
||||
////! Set the current transponder code in decimal
|
||||
void setTransponderCode(int code);
|
||||
|
||||
////! Set the current transponder mode (depends on the aircraft, 0 and 1 usually mean standby, >1 active)
|
||||
void setTransponderMode(int mode);
|
||||
|
||||
//! Get flaps deploy ratio, where 0.0 is flaps fully retracted, and 1.0 is flaps fully extended.
|
||||
double getFlapsDeployRatio() const;
|
||||
|
||||
//! Get gear deploy ratio, where 0 is up and 1 is down
|
||||
double getGearDeployRatio() const;
|
||||
|
||||
//! Get the number of engines of current aircraft
|
||||
int getNumberOfEngines() const;
|
||||
|
||||
//! Get the N1 speed as percent of max (per engine)
|
||||
std::vector<double> getEngineN1Percentage() const;
|
||||
|
||||
//! Get the ratio how much the speedbrakes surfaces are extended (0.0 is fully retracted, and 1.0 is fully extended)
|
||||
double getSpeedBrakeRatio() const;
|
||||
|
||||
//! Get ground elevation at aircraft current position
|
||||
double getGroundElevation() const;
|
||||
|
||||
//! Get x velocity in m/s
|
||||
double getVelocityX() const;
|
||||
|
||||
//! Get y velocity in m/s
|
||||
double getVelocityY() const;
|
||||
|
||||
//! Get z velocity in m/s
|
||||
double getVelocityZ() const;
|
||||
|
||||
//! Get roll rate in rad/sec
|
||||
double getRollRate() const;
|
||||
|
||||
//! Get pitch rate in rad/sec
|
||||
double getPitchRate() const;
|
||||
|
||||
//! Get yaw rate in rad/sec
|
||||
double getYawRate() const;
|
||||
|
||||
double getCom1Volume() const;
|
||||
double getCom2Volume() const;
|
||||
|
||||
//! Perform generic processing
|
||||
int process();
|
||||
|
||||
protected:
|
||||
DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override;
|
||||
|
||||
private:
|
||||
SGPropertyNode_ptr m_textMessageNode;
|
||||
SGPropertyNode_ptr m_aircraftModelPathNode;
|
||||
//SGPropertyNode_ptr aircraftLiveryNode;
|
||||
//SGPropertyNode_ptr aircraftIcaoCodeNode;
|
||||
SGPropertyNode_ptr m_aircraftDescriptionNode;
|
||||
SGPropertyNode_ptr m_isPausedNode;
|
||||
SGPropertyNode_ptr m_latitudeNode;
|
||||
SGPropertyNode_ptr m_longitudeNode;
|
||||
SGPropertyNode_ptr m_altitudeMSLNode;
|
||||
SGPropertyNode_ptr m_heightAGLNode;
|
||||
SGPropertyNode_ptr m_groundSpeedNode;
|
||||
SGPropertyNode_ptr m_pitchNode;
|
||||
SGPropertyNode_ptr m_rollNode;
|
||||
SGPropertyNode_ptr m_trueHeadingNode;
|
||||
SGPropertyNode_ptr m_wheelsOnGroundNode;
|
||||
SGPropertyNode_ptr m_com1ActiveNode;
|
||||
SGPropertyNode_ptr m_com1StandbyNode;
|
||||
SGPropertyNode_ptr m_com2ActiveNode;
|
||||
SGPropertyNode_ptr m_com2StandbyNode;
|
||||
SGPropertyNode_ptr m_transponderCodeNode;
|
||||
SGPropertyNode_ptr m_transponderModeNode;
|
||||
SGPropertyNode_ptr m_transponderIdentNode;
|
||||
SGPropertyNode_ptr m_beaconLightsNode;
|
||||
SGPropertyNode_ptr m_landingLightsNode;
|
||||
SGPropertyNode_ptr m_navLightsNode;
|
||||
SGPropertyNode_ptr m_strobeLightsNode;
|
||||
SGPropertyNode_ptr m_taxiLightsNode;
|
||||
SGPropertyNode_ptr m_altimeterServiceableNode;
|
||||
SGPropertyNode_ptr m_pressAltitudeFtNode;
|
||||
SGPropertyNode_ptr m_flapsDeployRatioNode;
|
||||
SGPropertyNode_ptr m_gearDeployRatioNode;
|
||||
SGPropertyNode_ptr m_speedBrakeDeployRatioNode;
|
||||
SGPropertyNode_ptr m_groundElevationNode;
|
||||
//SGPropertyNode_ptr numberEnginesNode;
|
||||
//SGPropertyNode_ptr engineN1PercentageNode;
|
||||
SGPropertyNode_ptr m_aircraftNameNode;
|
||||
SGPropertyNode_ptr m_velocityXNode;
|
||||
SGPropertyNode_ptr m_velocityYNode;
|
||||
SGPropertyNode_ptr m_velocityZNode;
|
||||
SGPropertyNode_ptr m_rollRateNode;
|
||||
SGPropertyNode_ptr m_pichRateNode;
|
||||
SGPropertyNode_ptr m_yawRateNode;
|
||||
SGPropertyNode_ptr m_com1VolumeNode;
|
||||
SGPropertyNode_ptr m_com2VolumeNode;
|
||||
};
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
97
src/Network/Swift/swift_connection.cxx
Normal file
97
src/Network/Swift/swift_connection.cxx
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "plugin.h"
|
||||
#include "swift_connection.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <simgear/structure/event_mgr.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
namespace {
|
||||
inline std::string fgswiftbusServiceName()
|
||||
{
|
||||
return std::string("org.swift-project.fgswiftbus");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool SwiftConnection::startServer(const SGPropertyNode* arg, SGPropertyNode* root)
|
||||
{
|
||||
SwiftConnection::plug = std::make_unique<FGSwiftBus::CPlugin>();
|
||||
|
||||
serverRunning = true;
|
||||
fgSetBool("/sim/swift/serverRunning", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SwiftConnection::stopServer(const SGPropertyNode* arg, SGPropertyNode* root)
|
||||
{
|
||||
fgSetBool("/sim/swift/serverRunning", false);
|
||||
serverRunning = false;
|
||||
|
||||
SwiftConnection::plug.reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SwiftConnection::SwiftConnection()
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
SwiftConnection::~SwiftConnection()
|
||||
{
|
||||
shutdown();
|
||||
|
||||
if (serverRunning) {
|
||||
SwiftConnection::plug.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void SwiftConnection::init()
|
||||
{
|
||||
if (!initialized) {
|
||||
globals->get_commands()->addCommand("swiftStart", this, &SwiftConnection::startServer);
|
||||
globals->get_commands()->addCommand("swiftStop", this, &SwiftConnection::stopServer);
|
||||
|
||||
fgSetBool("/sim/swift/available", true);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SwiftConnection::update(double delta_time_sec)
|
||||
{
|
||||
if (serverRunning) {
|
||||
SwiftConnection::plug->fastLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void SwiftConnection::shutdown()
|
||||
{
|
||||
if (initialized) {
|
||||
fgSetBool("/sim/swift/available", false);
|
||||
initialized = false;
|
||||
|
||||
globals->get_commands()->removeCommand("swiftStart");
|
||||
globals->get_commands()->removeCommand("swiftStop");
|
||||
}
|
||||
}
|
||||
|
||||
void SwiftConnection::reinit()
|
||||
{
|
||||
shutdown();
|
||||
init();
|
||||
}
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<SwiftConnection> registrantSwiftConnection(
|
||||
SGSubsystemMgr::POST_FDM);
|
||||
49
src/Network/Swift/swift_connection.hxx
Normal file
49
src/Network/Swift/swift_connection.hxx
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/io/raw_socket.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include "dbusconnection.h"
|
||||
#include "dbusdispatcher.h"
|
||||
#include "dbusserver.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
class SwiftConnection : public SGSubsystem
|
||||
{
|
||||
public:
|
||||
SwiftConnection();
|
||||
~SwiftConnection();
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
void reinit() override;
|
||||
void shutdown() override;
|
||||
void update(double delta_time_sec) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "swift"; }
|
||||
|
||||
bool startServer(const SGPropertyNode* arg, SGPropertyNode* root);
|
||||
bool stopServer(const SGPropertyNode* arg, SGPropertyNode* root);
|
||||
|
||||
std::unique_ptr<FGSwiftBus::CPlugin> plug{};
|
||||
|
||||
private:
|
||||
bool serverRunning = false;
|
||||
bool initialized = false;
|
||||
};
|
||||
299
src/Network/Swift/traffic.cpp
Normal file
299
src/Network/Swift/traffic.cpp
Normal file
@@ -0,0 +1,299 @@
|
||||
/*
|
||||
* Traffic module for swift<->FG connection
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
//! \cond PRIVATE
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include "traffic.h"
|
||||
#include "SwiftAircraftManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
// clazy:excludeall=reserve-candidates
|
||||
|
||||
namespace FGSwiftBus {
|
||||
|
||||
CTraffic::CTraffic()
|
||||
{
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic started");
|
||||
}
|
||||
|
||||
CTraffic::~CTraffic()
|
||||
{
|
||||
cleanup();
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "FGSwiftBus Traffic stopped");
|
||||
}
|
||||
|
||||
const std::string& CTraffic::InterfaceName()
|
||||
{
|
||||
static std::string s(FGSWIFTBUS_TRAFFIC_INTERFACENAME);
|
||||
return s;
|
||||
}
|
||||
|
||||
const std::string& CTraffic::ObjectPath()
|
||||
{
|
||||
static std::string s(FGSWIFTBUS_TRAFFIC_OBJECTPATH);
|
||||
return s;
|
||||
}
|
||||
|
||||
bool CTraffic::initialize()
|
||||
{
|
||||
acm.reset(new FGSwiftAircraftManager());
|
||||
return acm->isInitialized();
|
||||
}
|
||||
|
||||
void CTraffic::emitSimFrame()
|
||||
{
|
||||
if (m_emitSimFrame) { sendDBusSignal("simFrame"); }
|
||||
m_emitSimFrame = !m_emitSimFrame;
|
||||
}
|
||||
|
||||
void CTraffic::emitPlaneAdded(const std::string& callsign)
|
||||
{
|
||||
CDBusMessage signalPlaneAdded = CDBusMessage::createSignal(FGSWIFTBUS_TRAFFIC_OBJECTPATH, FGSWIFTBUS_TRAFFIC_INTERFACENAME, "remoteAircraftAdded");
|
||||
signalPlaneAdded.beginArgumentWrite();
|
||||
signalPlaneAdded.appendArgument(callsign);
|
||||
sendDBusMessage(signalPlaneAdded);
|
||||
}
|
||||
|
||||
void CTraffic::cleanup()
|
||||
{
|
||||
acm.reset();
|
||||
}
|
||||
|
||||
void CTraffic::dbusDisconnectedHandler()
|
||||
{
|
||||
if (acm)
|
||||
acm->removeAllPlanes();
|
||||
}
|
||||
|
||||
const char* introspection_traffic = DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE;
|
||||
|
||||
DBusHandlerResult CTraffic::dbusMessageHandler(const CDBusMessage& message_)
|
||||
{
|
||||
CDBusMessage message(message_);
|
||||
const std::string sender = message.getSender();
|
||||
const dbus_uint32_t serial = message.getSerial();
|
||||
const bool wantsReply = message.wantsReply();
|
||||
|
||||
if (message.getInterfaceName() == DBUS_INTERFACE_INTROSPECTABLE) {
|
||||
if (message.getMethodName() == "Introspect") {
|
||||
sendDBusReply(sender, serial, introspection_traffic);
|
||||
}
|
||||
} else if (message.getInterfaceName() == FGSWIFTBUS_TRAFFIC_INTERFACENAME) {
|
||||
if (message.getMethodName() == "acquireMultiplayerPlanes") {
|
||||
queueDBusCall([=]() {
|
||||
std::string owner;
|
||||
bool acquired = true;
|
||||
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(acquired);
|
||||
reply.appendArgument(owner);
|
||||
sendDBusMessage(reply);
|
||||
});
|
||||
} else if (message.getMethodName() == "initialize") {
|
||||
sendDBusReply(sender, serial, initialize());
|
||||
} else if (message.getMethodName() == "cleanup") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
queueDBusCall([=]() {
|
||||
cleanup();
|
||||
});
|
||||
} else if (message.getMethodName() == "addPlane") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
std::string callsign;
|
||||
std::string modelName;
|
||||
std::string aircraftIcao;
|
||||
std::string airlineIcao;
|
||||
std::string livery;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(callsign);
|
||||
message.getArgument(modelName);
|
||||
message.getArgument(aircraftIcao);
|
||||
message.getArgument(airlineIcao);
|
||||
message.getArgument(livery);
|
||||
|
||||
queueDBusCall([=]() {
|
||||
if (acm->addPlane(callsign, modelName)) {
|
||||
emitPlaneAdded(callsign);
|
||||
}
|
||||
});
|
||||
} else if (message.getMethodName() == "removePlane") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
std::string callsign;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(callsign);
|
||||
queueDBusCall([=]() {
|
||||
acm->removePlane(callsign);
|
||||
});
|
||||
} else if (message.getMethodName() == "removeAllPlanes") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
queueDBusCall([=]() {
|
||||
acm->removeAllPlanes();
|
||||
});
|
||||
} else if (message.getMethodName() == "setPlanesPositions") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
std::vector<std::string> callsigns;
|
||||
std::vector<double> latitudes;
|
||||
std::vector<double> longitudes;
|
||||
std::vector<double> altitudes;
|
||||
std::vector<double> pitches;
|
||||
std::vector<double> rolls;
|
||||
std::vector<double> headings;
|
||||
std::vector<double> groundspeeds;
|
||||
std::vector<bool> onGrounds;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(callsigns);
|
||||
message.getArgument(latitudes);
|
||||
message.getArgument(longitudes);
|
||||
message.getArgument(altitudes);
|
||||
message.getArgument(pitches);
|
||||
message.getArgument(rolls);
|
||||
message.getArgument(headings);
|
||||
message.getArgument(groundspeeds);
|
||||
message.getArgument(onGrounds);
|
||||
queueDBusCall([=]() {
|
||||
std::vector<SwiftPlaneUpdate> updates;
|
||||
for (long unsigned int i = 0; i < latitudes.size(); i++) {
|
||||
SGGeod pos;
|
||||
pos.setLatitudeDeg(latitudes.at(i));
|
||||
pos.setLongitudeDeg(longitudes.at(i));
|
||||
pos.setElevationFt(altitudes.at(i));
|
||||
SGVec3d orientation(pitches.at(i), rolls.at(i), headings.at(i));
|
||||
updates.push_back({callsigns.at(i), pos, orientation, groundspeeds.at(i), onGrounds.at(i)});
|
||||
}
|
||||
acm->updatePlanes(updates);
|
||||
});
|
||||
} else if (message.getMethodName() == "getRemoteAircraftData") {
|
||||
std::vector<std::string> requestedcallsigns;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(requestedcallsigns);
|
||||
queueDBusCall([=]() {
|
||||
std::vector<std::string> callsigns = requestedcallsigns;
|
||||
std::vector<double> latitudesDeg;
|
||||
std::vector<double> longitudesDeg;
|
||||
std::vector<double> elevationsM;
|
||||
std::vector<double> verticalOffsets;
|
||||
acm->getRemoteAircraftData(callsigns, latitudesDeg, longitudesDeg, elevationsM, verticalOffsets);
|
||||
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(callsigns);
|
||||
reply.appendArgument(latitudesDeg);
|
||||
reply.appendArgument(longitudesDeg);
|
||||
reply.appendArgument(elevationsM);
|
||||
reply.appendArgument(verticalOffsets);
|
||||
sendDBusMessage(reply);
|
||||
});
|
||||
} else if (message.getMethodName() == "getElevationAtPosition") {
|
||||
std::string callsign;
|
||||
double latitudeDeg;
|
||||
double longitudeDeg;
|
||||
double altitudeMeters;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(callsign);
|
||||
message.getArgument(latitudeDeg);
|
||||
message.getArgument(longitudeDeg);
|
||||
message.getArgument(altitudeMeters);
|
||||
queueDBusCall([=]() {
|
||||
SGGeod pos;
|
||||
pos.setLatitudeDeg(latitudeDeg);
|
||||
pos.setLongitudeDeg(longitudeDeg);
|
||||
pos.setElevationM(altitudeMeters);
|
||||
double elevation = acm->getElevationAtPosition(callsign, pos);
|
||||
CDBusMessage reply = CDBusMessage::createReply(sender, serial);
|
||||
reply.beginArgumentWrite();
|
||||
reply.appendArgument(callsign);
|
||||
reply.appendArgument(elevation);
|
||||
sendDBusMessage(reply);
|
||||
});
|
||||
} else if (message.getMethodName() == "setPlanesTransponders") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
std::vector<std::string> callsigns;
|
||||
std::vector<int> codes;
|
||||
std::vector<bool> modeCs;
|
||||
std::vector<bool> idents;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(callsigns);
|
||||
message.getArgument(codes);
|
||||
message.getArgument(modeCs);
|
||||
message.getArgument(idents);
|
||||
std::vector<AircraftTransponder> transponders;
|
||||
transponders.reserve(callsigns.size());
|
||||
for (long unsigned int i = 0; i < callsigns.size(); i++) {
|
||||
transponders.emplace_back(callsigns.at(i), codes.at(i), modeCs.at(i), idents.at(i));
|
||||
}
|
||||
queueDBusCall([=]() {
|
||||
acm->setPlanesTransponders(transponders);
|
||||
});
|
||||
} else if (message.getMethodName() == "setPlanesSurfaces") {
|
||||
maybeSendEmptyDBusReply(wantsReply, sender, serial);
|
||||
std::vector<std::string> callsigns;
|
||||
std::vector<double> gears;
|
||||
std::vector<double> flaps;
|
||||
std::vector<double> spoilers;
|
||||
std::vector<double> speedBrakes;
|
||||
std::vector<double> slats;
|
||||
std::vector<double> wingSweeps;
|
||||
std::vector<double> thrusts;
|
||||
std::vector<double> elevators;
|
||||
std::vector<double> rudders;
|
||||
std::vector<double> ailerons;
|
||||
std::vector<bool> landLights;
|
||||
std::vector<bool> taxiLights;
|
||||
std::vector<bool> beaconLights;
|
||||
std::vector<bool> strobeLights;
|
||||
std::vector<bool> navLights;
|
||||
std::vector<int> lightPatterns;
|
||||
message.beginArgumentRead();
|
||||
message.getArgument(callsigns);
|
||||
message.getArgument(gears);
|
||||
message.getArgument(flaps);
|
||||
message.getArgument(spoilers);
|
||||
message.getArgument(speedBrakes);
|
||||
message.getArgument(slats);
|
||||
message.getArgument(wingSweeps);
|
||||
message.getArgument(thrusts);
|
||||
message.getArgument(elevators);
|
||||
message.getArgument(rudders);
|
||||
message.getArgument(ailerons);
|
||||
message.getArgument(landLights);
|
||||
message.getArgument(taxiLights);
|
||||
message.getArgument(beaconLights);
|
||||
message.getArgument(strobeLights);
|
||||
message.getArgument(navLights);
|
||||
message.getArgument(lightPatterns);
|
||||
std::vector<AircraftSurfaces> surfaces;
|
||||
surfaces.reserve(callsigns.size());
|
||||
for (long unsigned int i = 0; i < callsigns.size(); i++) {
|
||||
surfaces.emplace_back(callsigns.at(i), gears.at(i), flaps.at(i), spoilers.at(i), speedBrakes.at(i), slats.at(i),
|
||||
wingSweeps.at(i), thrusts.at(i), elevators.at(i), rudders.at(i), ailerons.at(i),
|
||||
landLights.at(i), taxiLights.at(i), beaconLights.at(i), strobeLights.at(i), navLights.at(i), lightPatterns.at(i));
|
||||
}
|
||||
queueDBusCall([=]() {
|
||||
acm->setPlanesSurfaces(surfaces);
|
||||
});
|
||||
} else {
|
||||
// Unknown message. Tell DBus that we cannot handle it
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
|
||||
int CTraffic::process()
|
||||
{
|
||||
invokeQueuedDBusCalls();
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
//! \endcond
|
||||
71
src/Network/Swift/traffic.h
Normal file
71
src/Network/Swift/traffic.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Traffic module for swift<->FG connection
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 swift Project Community / Contributors (https://swift-project.org/)
|
||||
* SPDX-FileCopyrightText: (C) 2019-2022 Lars Toenning <dev@ltoenning.de>
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef BLACKSIM_FGSWIFTBUS_TRAFFIC_H
|
||||
#define BLACKSIM_FGSWIFTBUS_TRAFFIC_H
|
||||
|
||||
//! \file
|
||||
|
||||
#include "SwiftAircraftManager.h"
|
||||
#include "dbusobject.h"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
//! \cond PRIVATE
|
||||
#define FGSWIFTBUS_TRAFFIC_INTERFACENAME "org.swift_project.fgswiftbus.traffic"
|
||||
#define FGSWIFTBUS_TRAFFIC_OBJECTPATH "/fgswiftbus/traffic"
|
||||
//! \endcond
|
||||
|
||||
namespace FGSwiftBus {
|
||||
/*!
|
||||
* FGSwiftBus service object for traffic aircraft which is accessible through DBus
|
||||
*/
|
||||
class CTraffic : public CDBusObject
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
CTraffic();
|
||||
|
||||
//! Destructor
|
||||
~CTraffic() override;
|
||||
|
||||
//! DBus interface name
|
||||
static const std::string& InterfaceName();
|
||||
|
||||
//! DBus object path
|
||||
static const std::string& ObjectPath();
|
||||
|
||||
//! Initialize the multiplayer planes rendering and return true if successful
|
||||
bool initialize();
|
||||
|
||||
//! Perform generic processing
|
||||
int process();
|
||||
|
||||
void emitSimFrame();
|
||||
|
||||
protected:
|
||||
virtual void dbusDisconnectedHandler() override;
|
||||
|
||||
DBusHandlerResult dbusMessageHandler(const CDBusMessage& message) override;
|
||||
|
||||
private:
|
||||
void emitPlaneAdded(const std::string& callsign);
|
||||
void cleanup();
|
||||
|
||||
struct Plane {
|
||||
void* id = nullptr;
|
||||
std::string callsign;
|
||||
char label[32]{};
|
||||
};
|
||||
|
||||
bool m_emitSimFrame = true;
|
||||
std::unique_ptr<FGSwiftAircraftManager> acm;
|
||||
};
|
||||
} // namespace FGSwiftBus
|
||||
|
||||
#endif // guard
|
||||
596
src/Network/atlas.cxx
Normal file
596
src/Network/atlas.cxx
Normal file
@@ -0,0 +1,596 @@
|
||||
// atlas.cxx -- Atlas protocal class
|
||||
//
|
||||
// Written by Curtis Olson, started November 1999.
|
||||
//
|
||||
// Copyright (C) 1999 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/timing/sg_time.hxx>
|
||||
|
||||
#include <FDM/flightProperties.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/fg_init.hxx>
|
||||
|
||||
#include "atlas.hxx"
|
||||
|
||||
|
||||
FGAtlas::FGAtlas() :
|
||||
length(0),
|
||||
fdm(new FlightProperties)
|
||||
{
|
||||
_adf_freq = fgGetNode("/instrumentation/adf/frequencies/selected-khz", true);
|
||||
_nav1_freq = fgGetNode("/instrumentation/nav/frequencies/selected-mhz", true);
|
||||
_nav1_sel_radial = fgGetNode("/instrumentation/nav/radials/selected-deg", true);
|
||||
_nav2_freq = fgGetNode("/instrumentation/nav[1]/frequencies/selected-mhz", true);
|
||||
_nav2_sel_radial = fgGetNode("/instrumentation/nav[1]/radials/selected-deg", true);
|
||||
}
|
||||
|
||||
FGAtlas::~FGAtlas() {
|
||||
delete fdm;
|
||||
}
|
||||
|
||||
|
||||
// calculate the atlas check sum
|
||||
static char calc_atlas_cksum(char *sentence) {
|
||||
unsigned char sum = 0;
|
||||
int i, len;
|
||||
|
||||
// cout << sentence << endl;
|
||||
|
||||
len = strlen(sentence);
|
||||
sum = sentence[0];
|
||||
for ( i = 1; i < len; i++ ) {
|
||||
// cout << sentence[i];
|
||||
sum ^= sentence[i];
|
||||
}
|
||||
// cout << endl;
|
||||
|
||||
// printf("sum = %02x\n", sum);
|
||||
return sum;
|
||||
}
|
||||
|
||||
// generate Atlas message
|
||||
bool FGAtlas::gen_message() {
|
||||
// cout << "generating atlas message" << endl;
|
||||
char rmc[256], gga[256], patla[256];
|
||||
char rmc_sum[10], gga_sum[10], patla_sum[10];
|
||||
char dir;
|
||||
int deg;
|
||||
double min;
|
||||
|
||||
SGTime *t = globals->get_time_params();
|
||||
|
||||
char utc[10];
|
||||
snprintf( utc, sizeof(utc), "%02d%02d%02d",
|
||||
t->getGmt()->tm_hour, t->getGmt()->tm_min, t->getGmt()->tm_sec );
|
||||
|
||||
char lat[20];
|
||||
double latd = fdm->get_Latitude() * SGD_RADIANS_TO_DEGREES;
|
||||
if ( latd < 0.0 ) {
|
||||
latd *= -1.0;
|
||||
dir = 'S';
|
||||
} else {
|
||||
dir = 'N';
|
||||
}
|
||||
deg = (int)(latd);
|
||||
min = (latd - (double)deg) * 60.0;
|
||||
snprintf( lat, sizeof(lat), "%02d%06.3f,%c", abs(deg), min, dir);
|
||||
|
||||
char lon[20];
|
||||
double lond = fdm->get_Longitude() * SGD_RADIANS_TO_DEGREES;
|
||||
if ( lond < 0.0 ) {
|
||||
lond *= -1.0;
|
||||
dir = 'W';
|
||||
} else {
|
||||
dir = 'E';
|
||||
}
|
||||
deg = (int)(lond);
|
||||
min = (lond - (double)deg) * 60.0;
|
||||
snprintf( lon, sizeof(lon), "%03d%06.3f,%c", abs(deg), min, dir);
|
||||
|
||||
char speed[10];
|
||||
snprintf( speed, sizeof(speed), "%05.1f", fdm->get_V_equiv_kts() );
|
||||
|
||||
char heading[10];
|
||||
snprintf( heading, sizeof(heading), "%05.1f", fdm->get_Psi() * SGD_RADIANS_TO_DEGREES );
|
||||
|
||||
char altitude_m[10];
|
||||
snprintf( altitude_m, sizeof(altitude_m), "%02d",
|
||||
(int)(fdm->get_Altitude() * SG_FEET_TO_METER) );
|
||||
|
||||
char altitude_ft[10];
|
||||
snprintf( altitude_ft, sizeof(altitude_ft), "%02d", (int)fdm->get_Altitude() );
|
||||
|
||||
char date[16];
|
||||
unsigned short tm_mday = t->getGmt()->tm_mday;
|
||||
unsigned short tm_mon = t->getGmt()->tm_mon + 1;
|
||||
unsigned short tm_year = t->getGmt()->tm_year;
|
||||
snprintf( date, sizeof(date), "%02u%02u%02u", tm_mday, tm_mon, tm_year);
|
||||
|
||||
// $GPRMC,HHMMSS,A,DDMM.MMM,N,DDDMM.MMM,W,XXX.X,XXX.X,DDMMYY,XXX.X,E*XX
|
||||
snprintf( rmc, sizeof(rmc), "GPRMC,%s,A,%s,%s,%s,%s,%s,0.000,E",
|
||||
utc, lat, lon, speed, heading, date );
|
||||
snprintf( rmc_sum, sizeof(rmc_sum), "%02X", calc_atlas_cksum(rmc) );
|
||||
|
||||
snprintf( gga, sizeof(gga), "GPGGA,%s,%s,%s,1,,,%s,F,,,,",
|
||||
utc, lat, lon, altitude_ft );
|
||||
snprintf( gga_sum, sizeof(gga_sum), "%02X", calc_atlas_cksum(gga) );
|
||||
|
||||
snprintf( patla, sizeof(patla), "PATLA,%.2f,%.1f,%.2f,%.1f,%.0f",
|
||||
_nav1_freq->getDoubleValue(),
|
||||
_nav1_sel_radial->getDoubleValue(),
|
||||
_nav2_freq->getDoubleValue(),
|
||||
_nav2_sel_radial->getDoubleValue(),
|
||||
_adf_freq->getDoubleValue() );
|
||||
snprintf( patla_sum, sizeof(patla_sum), "%02X", calc_atlas_cksum(patla) );
|
||||
|
||||
SG_LOG( SG_IO, SG_DEBUG, rmc );
|
||||
SG_LOG( SG_IO, SG_DEBUG, gga );
|
||||
SG_LOG( SG_IO, SG_DEBUG, patla );
|
||||
|
||||
string atlas_sentence;
|
||||
|
||||
// RMC sentence
|
||||
atlas_sentence = "$";
|
||||
atlas_sentence += rmc;
|
||||
atlas_sentence += "*";
|
||||
atlas_sentence += rmc_sum;
|
||||
atlas_sentence += "\n";
|
||||
|
||||
// GGA sentence
|
||||
atlas_sentence += "$";
|
||||
atlas_sentence += gga;
|
||||
atlas_sentence += "*";
|
||||
atlas_sentence += gga_sum;
|
||||
atlas_sentence += "\n";
|
||||
|
||||
// PATLA sentence
|
||||
atlas_sentence += "$";
|
||||
atlas_sentence += patla;
|
||||
atlas_sentence += "*";
|
||||
atlas_sentence += patla_sum;
|
||||
atlas_sentence += "\n";
|
||||
|
||||
// cout << atlas_sentence;
|
||||
|
||||
length = atlas_sentence.length();
|
||||
strncpy( buf, atlas_sentence.c_str(), length );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// parse Atlas message. messages will look something like the
|
||||
// following:
|
||||
//
|
||||
// $GPRMC,163227,A,3321.173,N,11039.855,W,000.1,270.0,171199,0.000,E*61
|
||||
// $GPGGA,163227,3321.173,N,11039.855,W,1,,,3333,F,,,,*0F
|
||||
|
||||
bool FGAtlas::parse_message() {
|
||||
SG_LOG( SG_IO, SG_INFO, "parse atlas message" );
|
||||
|
||||
string msg = buf;
|
||||
msg = msg.substr( 0, length );
|
||||
SG_LOG( SG_IO, SG_INFO, "entire message = " << msg );
|
||||
|
||||
string::size_type begin_line, end_line, begin, end;
|
||||
begin_line = begin = 0;
|
||||
|
||||
// extract out each line
|
||||
end_line = msg.find("\n", begin_line);
|
||||
while ( end_line != string::npos ) {
|
||||
string line = msg.substr(begin_line, end_line - begin_line);
|
||||
begin_line = end_line + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " input line = " << line );
|
||||
|
||||
// leading character
|
||||
string start = msg.substr(begin, 1);
|
||||
++begin;
|
||||
SG_LOG( SG_IO, SG_INFO, " start = " << start );
|
||||
|
||||
// sentence
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string sentence = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " sentence = " << sentence );
|
||||
|
||||
double lon_deg, lon_min, lat_deg, lat_min;
|
||||
double lon, lat, speed, heading, altitude;
|
||||
|
||||
if ( sentence == "GPRMC" ) {
|
||||
// time
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string utc = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " utc = " << utc );
|
||||
|
||||
// junk
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string junk = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " junk = " << junk );
|
||||
|
||||
// lat val
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lat_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lat_deg = atof( lat_str.substr(0, 2).c_str() );
|
||||
lat_min = atof( lat_str.substr(2).c_str() );
|
||||
|
||||
// lat dir
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lat_dir = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lat = lat_deg + ( lat_min / 60.0 );
|
||||
if ( lat_dir == "S" ) {
|
||||
lat *= -1;
|
||||
}
|
||||
|
||||
fdm->set_Latitude( lat * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " lat = " << lat );
|
||||
|
||||
// lon val
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lon_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lon_deg = atof( lon_str.substr(0, 3).c_str() );
|
||||
lon_min = atof( lon_str.substr(3).c_str() );
|
||||
|
||||
// lon dir
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lon_dir = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lon = lon_deg + ( lon_min / 60.0 );
|
||||
if ( lon_dir == "W" ) {
|
||||
lon *= -1;
|
||||
}
|
||||
|
||||
fdm->set_Longitude( lon * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " lon = " << lon );
|
||||
|
||||
#if 0
|
||||
double sl_radius, lat_geoc;
|
||||
sgGeodToGeoc( fdm->get_Latitude(),
|
||||
fdm->get_Altitude(),
|
||||
&sl_radius, &lat_geoc );
|
||||
fdm->set_Geocentric_Position( lat_geoc,
|
||||
fdm->get_Longitude(),
|
||||
sl_radius + fdm->get_Altitude() );
|
||||
#endif
|
||||
|
||||
// speed
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string speed_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
speed = atof( speed_str.c_str() );
|
||||
fdm->set_V_calibrated_kts( speed );
|
||||
// fdm->set_V_ground_speed( speed );
|
||||
SG_LOG( SG_IO, SG_INFO, " speed = " << speed );
|
||||
|
||||
// heading
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string hdg_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
heading = atof( hdg_str.c_str() );
|
||||
fdm->set_Euler_Angles( fdm->get_Phi(),
|
||||
fdm->get_Theta(),
|
||||
heading * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " heading = " << heading );
|
||||
} else if ( sentence == "GPGGA" ) {
|
||||
// time
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string utc = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " utc = " << utc );
|
||||
|
||||
// lat val
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lat_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lat_deg = atof( lat_str.substr(0, 2).c_str() );
|
||||
lat_min = atof( lat_str.substr(2).c_str() );
|
||||
|
||||
// lat dir
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lat_dir = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lat = lat_deg + ( lat_min / 60.0 );
|
||||
if ( lat_dir == "S" ) {
|
||||
lat *= -1;
|
||||
}
|
||||
|
||||
// fdm->set_Latitude( lat * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " lat = " << lat );
|
||||
|
||||
// lon val
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lon_str = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lon_deg = atof( lon_str.substr(0, 3).c_str() );
|
||||
lon_min = atof( lon_str.substr(3).c_str() );
|
||||
|
||||
// lon dir
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string lon_dir = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
lon = lon_deg + ( lon_min / 60.0 );
|
||||
if ( lon_dir == "W" ) {
|
||||
lon *= -1;
|
||||
}
|
||||
|
||||
// fdm->set_Longitude( lon * SGD_DEGREES_TO_RADIANS );
|
||||
SG_LOG( SG_IO, SG_INFO, " lon = " << lon );
|
||||
|
||||
// junk
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string junk = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " junk = " << junk );
|
||||
|
||||
// junk
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
junk = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " junk = " << junk );
|
||||
|
||||
// junk
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
junk = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " junk = " << junk );
|
||||
|
||||
// altitude
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string alt_str = msg.substr(begin, end - begin);
|
||||
altitude = atof( alt_str.c_str() );
|
||||
begin = end + 1;
|
||||
|
||||
// altitude units
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string alt_units = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
|
||||
if ( alt_units != "F" ) {
|
||||
altitude *= SG_METER_TO_FEET;
|
||||
}
|
||||
|
||||
fdm->set_Altitude( altitude );
|
||||
|
||||
SG_LOG( SG_IO, SG_INFO, " altitude = " << altitude );
|
||||
|
||||
} else if ( sentence == "PATLA" ) {
|
||||
// nav1 freq
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string nav1_freq = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " nav1_freq = " << nav1_freq );
|
||||
|
||||
// nav1 selected radial
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string nav1_rad = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " nav1_rad = " << nav1_rad );
|
||||
|
||||
// nav2 freq
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string nav2_freq = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " nav2_freq = " << nav2_freq );
|
||||
|
||||
// nav2 selected radial
|
||||
end = msg.find(",", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string nav2_rad = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " nav2_rad = " << nav2_rad );
|
||||
|
||||
// adf freq
|
||||
end = msg.find("*", begin);
|
||||
if ( end == string::npos ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string adf_freq = msg.substr(begin, end - begin);
|
||||
begin = end + 1;
|
||||
SG_LOG( SG_IO, SG_INFO, " adf_freq = " << adf_freq );
|
||||
}
|
||||
|
||||
// printf("%.8f %.8f\n", lon, lat);
|
||||
|
||||
begin = begin_line;
|
||||
end_line = msg.find("\n", begin_line);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// open hailing frequencies
|
||||
bool FGAtlas::open() {
|
||||
if ( is_enabled() ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||
<< "is already in use, ignoring" );
|
||||
return false;
|
||||
}
|
||||
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
if ( ! io->open( get_direction() ) ) {
|
||||
SG_LOG( SG_IO, SG_ALERT, "Error opening channel communication layer." );
|
||||
return false;
|
||||
}
|
||||
|
||||
set_enabled( true );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// process work for this port
|
||||
bool FGAtlas::process() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
if ( get_direction() == SG_IO_OUT ) {
|
||||
gen_message();
|
||||
if ( ! io->write( buf, length ) ) {
|
||||
SG_LOG( SG_IO, SG_WARN, "Error writing data." );
|
||||
return false;
|
||||
}
|
||||
} else if ( get_direction() == SG_IO_IN ) {
|
||||
if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
|
||||
parse_message();
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_WARN, "Error reading data." );
|
||||
return false;
|
||||
}
|
||||
if ( (length = io->readline( buf, FG_MAX_MSG_SIZE )) > 0 ) {
|
||||
parse_message();
|
||||
} else {
|
||||
SG_LOG( SG_IO, SG_WARN, "Error reading data." );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// close the channel
|
||||
bool FGAtlas::close() {
|
||||
SG_LOG( SG_IO, SG_INFO, "closing FGAtlas" );
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
set_enabled( false );
|
||||
|
||||
if ( ! io->close() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
65
src/Network/atlas.hxx
Normal file
65
src/Network/atlas.hxx
Normal file
@@ -0,0 +1,65 @@
|
||||
// atlas.hxx -- Atlas protocal class
|
||||
//
|
||||
// Written by Curtis Olson, started November 1999.
|
||||
//
|
||||
// Copyright (C) 1999 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifndef _FG_ATLAS_HXX
|
||||
#define _FG_ATLAS_HXX
|
||||
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "protocol.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
class FlightProperties;
|
||||
|
||||
class FGAtlas : public FGProtocol {
|
||||
|
||||
char buf[ FG_MAX_MSG_SIZE ];
|
||||
int length;
|
||||
FlightProperties* fdm;
|
||||
SGPropertyNode_ptr _adf_freq, _nav1_freq,_nav1_sel_radial, _nav2_freq, _nav2_sel_radial;
|
||||
|
||||
public:
|
||||
|
||||
FGAtlas();
|
||||
~FGAtlas();
|
||||
|
||||
bool gen_message();
|
||||
bool parse_message();
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
};
|
||||
|
||||
|
||||
#endif // _FG_ATLAS_HXX
|
||||
|
||||
|
||||
191
src/Network/dds_props.cxx
Normal file
191
src/Network/dds_props.cxx
Normal file
@@ -0,0 +1,191 @@
|
||||
// dds_props.cxx -- FGFS "DDS" properties protocal class
|
||||
//
|
||||
// Written by Erik Hofman, started April 2021
|
||||
//
|
||||
// Copyright (C) 2021 by Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <simgear/io/SGDataDistributionService.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "dds_props.hxx"
|
||||
|
||||
// open hailing frequencies
|
||||
bool FGDDSProps::open() {
|
||||
if (is_enabled()) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "This shouldn't happen, but the channel "
|
||||
<< "is already in use, ignoring");
|
||||
return false;
|
||||
}
|
||||
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
SG_DDS_Topic *dds = static_cast<SG_DDS_Topic*>(io);
|
||||
dds->setup(prop, &FG_DDS_prop_desc);
|
||||
|
||||
// always send and recieve.
|
||||
if (!io->open(SG_IO_BI)) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Error opening channel communication layer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
set_enabled(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// process work for this port
|
||||
bool FGDDSProps::process() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
int length = sizeof(prop);
|
||||
char *buf = reinterpret_cast<char*>(&prop);
|
||||
|
||||
if (get_direction() == SG_IO_IN)
|
||||
{
|
||||
// act as a client: send a request and wait for an answer.
|
||||
|
||||
}
|
||||
else if (get_direction() == SG_IO_OUT || get_direction() == SG_IO_BI)
|
||||
{
|
||||
// act as a server: read requests and send the results.
|
||||
while (io->read(buf, length) &&
|
||||
prop.version == FG_DDS_PROP_VERSION &&
|
||||
prop.mode == FG_DDS_MODE_READ)
|
||||
{
|
||||
// s is used to keep a copy of the string returend by
|
||||
// p->getStringValue() in setProp until it is sent to the DDS layer.
|
||||
std::string s;
|
||||
|
||||
if (prop.id == FG_DDS_PROP_REQUEST)
|
||||
{
|
||||
if (prop.val._d == FG_DDS_STRING)
|
||||
{
|
||||
const char *path = prop.val._u.String;
|
||||
auto it = path_list.find(path);
|
||||
if (it == path_list.end())
|
||||
{
|
||||
SGPropertyNode_ptr props = globals->get_props();
|
||||
SGPropertyNode_ptr p = props->getNode(path);
|
||||
if (p)
|
||||
{
|
||||
prop.id = prop_list.size();
|
||||
try {
|
||||
prop_list.push_back(p);
|
||||
path_list[prop.val._u.String] = prop.id;
|
||||
|
||||
} catch (sg_exception&) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "out of memory");
|
||||
}
|
||||
}
|
||||
setProp(prop, p, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
prop.id = std::distance(path_list.begin(), it);
|
||||
setProp(prop, prop_list[prop.id], s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SG_LOG(SG_IO, SG_DEBUG, "Recieved a mangled DDS sample.");
|
||||
setProp(prop, nullptr, s);
|
||||
}
|
||||
}
|
||||
else {
|
||||
setProp(prop, prop_list[prop.id], s);
|
||||
}
|
||||
|
||||
// send the response.
|
||||
if (!io->write(buf, length)) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "Error writing data.");
|
||||
}
|
||||
} // while
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// close the channel
|
||||
bool FGDDSProps::close() {
|
||||
SGIOChannel *io = get_io_channel();
|
||||
|
||||
set_enabled(false);
|
||||
|
||||
if (! io->close()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FGDDSProps::setProp(FG_DDS_prop& prop, SGPropertyNode_ptr p, std::string& s)
|
||||
{
|
||||
// prop.id = FG_DDS_PROP_REQUEST;
|
||||
prop.version = FG_DDS_PROP_VERSION;
|
||||
prop.mode = FG_DDS_MODE_WRITE;
|
||||
if (p)
|
||||
{
|
||||
simgear::props::Type type = p->getType();
|
||||
if (type == simgear::props::BOOL) {
|
||||
prop.val._d = FG_DDS_BOOL;
|
||||
prop.val._u.Bool = p->getBoolValue();
|
||||
} else if (type == simgear::props::INT) {
|
||||
prop.val._d = FG_DDS_INT;
|
||||
prop.val._u.Int32 = p->getIntValue();
|
||||
} else if (type == simgear::props::LONG) {
|
||||
prop.val._d = FG_DDS_LONG;
|
||||
prop.val._u.Int64 = p->getLongValue();
|
||||
} else if (type == simgear::props::FLOAT) {
|
||||
prop.val._d = FG_DDS_FLOAT;
|
||||
prop.val._u.Float32 = p->getFloatValue();
|
||||
} else if (type == simgear::props::DOUBLE) {
|
||||
prop.val._d = FG_DDS_DOUBLE;
|
||||
prop.val._u.Float64 = p->getDoubleValue();
|
||||
} else if (type == simgear::props::ALIAS) {
|
||||
prop.val._d = FG_DDS_ALIAS;
|
||||
s = p->getStringValue();
|
||||
prop.val._u.String = const_cast<char*>(s.c_str());
|
||||
} else if (type == simgear::props::STRING) {
|
||||
prop.val._d = FG_DDS_STRING;
|
||||
s = p->getStringValue();
|
||||
prop.val._u.String = const_cast<char*>(s.c_str());
|
||||
} else if (type == simgear::props::UNSPECIFIED) {
|
||||
prop.val._d = FG_DDS_UNSPECIFIED;
|
||||
s = p->getStringValue();
|
||||
prop.val._u.String = const_cast<char*>(s.c_str());
|
||||
} else {
|
||||
prop.val._d = FG_DDS_NONE;
|
||||
prop.val._u.Int32 = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
59
src/Network/dds_props.hxx
Normal file
59
src/Network/dds_props.hxx
Normal file
@@ -0,0 +1,59 @@
|
||||
// dds_props.hxx -- FGFS "DDS" properties protocal class
|
||||
//
|
||||
// Written by Erik Hofman, started April 2021
|
||||
//
|
||||
// Copyright (C) 2021 by Erik Hofman <erik@ehofman.com>
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <simgear/props/propsfwd.hxx>
|
||||
|
||||
#include "protocol.hxx"
|
||||
#include "DDS/dds_props.h"
|
||||
|
||||
|
||||
class FGDDSProps : public FGProtocol {
|
||||
|
||||
FG_DDS_prop prop;
|
||||
|
||||
simgear::PropertyList prop_list;
|
||||
std::map<std::string,uint32_t> path_list;
|
||||
|
||||
static void setProp(FG_DDS_prop&, SGPropertyNode_ptr, std::string &);
|
||||
|
||||
public:
|
||||
|
||||
FGDDSProps() = default;
|
||||
~FGDDSProps() = default;
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
};
|
||||
748
src/Network/fgcom.cxx
Normal file
748
src/Network/fgcom.cxx
Normal file
@@ -0,0 +1,748 @@
|
||||
// fgcom.cxx -- FGCom: Voice communication
|
||||
//
|
||||
// Written by Clement de l'Hamaide, started May 2013.
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# include <config.h>
|
||||
|
||||
#include "fgcom.hxx"
|
||||
|
||||
// standard library includes
|
||||
#include <cstdio>
|
||||
|
||||
// simgear includes
|
||||
#include <simgear/compiler.h>
|
||||
#include <simgear/sg_inlines.h>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
// flightgear includes
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <ATC/CommStation.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <Navaids/navlist.hxx>
|
||||
#include <Main/sentryIntegration.hxx>
|
||||
|
||||
#include <iaxclient.h>
|
||||
|
||||
|
||||
#define NUM_CALLS 4
|
||||
#define MAX_GND_RANGE 10.0
|
||||
#define MAX_TWR_RANGE 50.0
|
||||
#define MAX_RANGE 100.0
|
||||
#define MIN_RANGE 20.0
|
||||
#define MIN_GNDTWR_RANGE 0.0
|
||||
#define DEFAULT_SERVER "fgcom.flightgear.org"
|
||||
#define IAX_DELAY 300 // delay between calls in milliseconds
|
||||
#define TEST_FREQ 910.00
|
||||
#define NULL_ICAO "ZZZZ"
|
||||
|
||||
const int special_freq[] = { // Define some freq who need to be used with NULL_ICAO
|
||||
910000,
|
||||
911000,
|
||||
700000,
|
||||
123450,
|
||||
122750,
|
||||
121500,
|
||||
123500 };
|
||||
|
||||
static FGCom* static_instance = NULL;
|
||||
|
||||
|
||||
|
||||
static int iaxc_callback( iaxc_event e )
|
||||
{
|
||||
switch( e.type )
|
||||
{
|
||||
case IAXC_EVENT_TEXT:
|
||||
static_instance->iaxTextEvent(e.ev.text);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::iaxTextEvent(struct iaxc_ev_text text)
|
||||
{
|
||||
if( (text.type == IAXC_TEXT_TYPE_STATUS ||
|
||||
text.type == IAXC_TEXT_TYPE_IAX) &&
|
||||
_showMessages_node->getBoolValue() )
|
||||
{
|
||||
_text_node->setStringValue(text.message);
|
||||
}
|
||||
|
||||
auto level = SG_INFO;
|
||||
if ((text.type == IAXC_TEXT_TYPE_ERROR) || (text.type == IAXC_TEXT_TYPE_FATALERROR)) {
|
||||
level = SG_ALERT;
|
||||
}
|
||||
|
||||
SG_LOG(SG_SOUND, level, std::string{"FCCom IAX:"} + text.message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
FGCom::FGCom()
|
||||
{
|
||||
_maxRange = MAX_RANGE;
|
||||
_minRange = MIN_RANGE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FGCom::~FGCom()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::bind()
|
||||
{
|
||||
SGPropertyNode *node = fgGetNode("/sim/fgcom", 0, true);
|
||||
_test_node = node->getChild( "test", 0, true );
|
||||
_server_node = node->getChild( "server", 0, true );
|
||||
_enabled_node = node->getChild( "enabled", 0, true );
|
||||
_micBoost_node = node->getChild( "mic-boost", 0, true );
|
||||
_micLevel_node = node->getChild( "mic-level", 0, true );
|
||||
_silenceThd_node = node->getChild( "silence-threshold", 0, true );
|
||||
_speakerLevel_node = node->getChild( "speaker-level", 0, true );
|
||||
_selectedInput_node = node->getChild( "device-input", 0, true );
|
||||
_selectedOutput_node = node->getChild( "device-output", 0, true );
|
||||
_showMessages_node = node->getChild( "show-messages", 0, true );
|
||||
|
||||
SGPropertyNode *reg_node = node->getChild("register", 0, true);
|
||||
_register_node = reg_node->getChild( "enabled", 0, true );
|
||||
_username_node = reg_node->getChild( "username", 0, true );
|
||||
_password_node = reg_node->getChild( "password", 0, true );
|
||||
|
||||
_ptt_node = fgGetNode("/controls/radios/comm-ptt", true);
|
||||
_selected_comm_node = fgGetNode("/controls/radios/comm-radio-selected", true);
|
||||
_callsign_node = fgGetNode("/sim/multiplay/callsign", true);
|
||||
_text_node = fgGetNode("/sim/messages/atc", true );
|
||||
_version_node = fgGetNode("/sim/version/flightgear", true );
|
||||
|
||||
// Set default values if not provided
|
||||
if ( !_enabled_node->hasValue() )
|
||||
_enabled_node->setBoolValue(true);
|
||||
|
||||
if ( !_test_node->hasValue() )
|
||||
_test_node->setBoolValue(false);
|
||||
|
||||
if ( !_micBoost_node->hasValue() )
|
||||
_micBoost_node->setIntValue(1);
|
||||
|
||||
if ( !_server_node->hasValue() )
|
||||
_server_node->setStringValue(DEFAULT_SERVER);
|
||||
|
||||
if ( !_speakerLevel_node->hasValue() )
|
||||
_speakerLevel_node->setFloatValue(1.0);
|
||||
|
||||
if ( !_micLevel_node->hasValue() )
|
||||
_micLevel_node->setFloatValue(1.0);
|
||||
|
||||
if ( !_silenceThd_node->hasValue() )
|
||||
_silenceThd_node->setFloatValue(-35.0);
|
||||
|
||||
if ( !_register_node->hasValue() )
|
||||
_register_node->setBoolValue(false);
|
||||
|
||||
if ( !_username_node->hasValue() )
|
||||
_username_node->setStringValue("guest");
|
||||
|
||||
if ( !_password_node->hasValue() )
|
||||
_password_node->setStringValue("guest");
|
||||
|
||||
if ( !_showMessages_node->hasValue() )
|
||||
_showMessages_node->setBoolValue(false);
|
||||
|
||||
_mpTransmitFrequencyNode = fgGetNode("sim/multiplay/comm-transmit-frequency-hz", 0, true);
|
||||
_mpTransmitPowerNode = fgGetNode("sim/multiplay/comm-transmit-power-norm", 0, true);
|
||||
|
||||
_selectedOutput_node->addChangeListener(this);
|
||||
_selectedInput_node->addChangeListener(this);
|
||||
_speakerLevel_node->addChangeListener(this);
|
||||
_silenceThd_node->addChangeListener(this);
|
||||
_micBoost_node->addChangeListener(this);
|
||||
_micLevel_node->addChangeListener(this);
|
||||
_enabled_node->addChangeListener(this);
|
||||
_ptt_node->addChangeListener(this);
|
||||
_selected_comm_node->addChangeListener(this);
|
||||
_test_node->addChangeListener(this);
|
||||
// if you listen to more properties, be sure to remove
|
||||
// the listener in unbind() as well
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::unbind()
|
||||
{
|
||||
_selectedOutput_node->removeChangeListener(this);
|
||||
_selectedInput_node->removeChangeListener(this);
|
||||
_speakerLevel_node->removeChangeListener(this);
|
||||
_silenceThd_node->removeChangeListener(this);
|
||||
_micBoost_node->removeChangeListener(this);
|
||||
_micLevel_node->removeChangeListener(this);
|
||||
_enabled_node->removeChangeListener(this);
|
||||
_ptt_node->removeChangeListener(this);
|
||||
_selected_comm_node->removeChangeListener(this);
|
||||
_test_node->removeChangeListener(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::init()
|
||||
{
|
||||
_enabled = _enabled_node->getBoolValue();
|
||||
_server = _server_node->getStringValue();
|
||||
_register = _register_node->getBoolValue();
|
||||
_username = _username_node->getStringValue();
|
||||
_password = _password_node->getStringValue();
|
||||
|
||||
_currentCommFrequency = 0.0;
|
||||
|
||||
_maxRange = MAX_RANGE;
|
||||
_minRange = MIN_RANGE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::postinit()
|
||||
{
|
||||
if( !_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
//WARNING: this _must_ be executed after sound system is totally initialized !
|
||||
if( iaxc_initialize(NUM_CALLS) ) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot initialize iaxclient");
|
||||
_enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
assert( static_instance == NULL );
|
||||
static_instance = this;
|
||||
iaxc_set_event_callback( iaxc_callback );
|
||||
|
||||
// FIXME: To be implemented in IAX audio driver
|
||||
//iaxc_mic_boost_set( _micBoost_node->getIntValue() );
|
||||
std::string app = "FGFS-";
|
||||
app += _version_node->getStringValue();
|
||||
|
||||
iaxc_set_callerid( _callsign_node->getStringValue().c_str(), app.c_str() );
|
||||
iaxc_set_formats (IAXC_FORMAT_SPEEX, IAXC_FORMAT_ULAW|IAXC_FORMAT_SPEEX);
|
||||
iaxc_set_speex_settings(1, 5, 0, 1, 0, 3);
|
||||
iaxc_set_filters(IAXC_FILTER_AGC | IAXC_FILTER_DENOISE);
|
||||
iaxc_set_silence_threshold(_silenceThd_node->getFloatValue());
|
||||
iaxc_start_processing_thread ();
|
||||
|
||||
// Now IAXClient is initialized
|
||||
_initialized = true;
|
||||
|
||||
if ( _register ) {
|
||||
_regId = iaxc_register( const_cast<char*>(_username.c_str()),
|
||||
const_cast<char*>(_password.c_str()),
|
||||
const_cast<char*>(_server.c_str()) );
|
||||
if( _regId == -1 ) {
|
||||
SG_LOG(SG_IO, SG_ALERT, "FGCom: cannot register iaxclient");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Here we will create the list of available audio devices
|
||||
Each audio device has a name, an ID, and a list of capabilities
|
||||
If an audio device can output sound, available-output=true
|
||||
If an audio device can input sound, available-input=true
|
||||
|
||||
/sim/fgcom/selected-input (int)
|
||||
/sim/fgcom/selected-output (int)
|
||||
|
||||
/sim/fgcom/device[n]/id (int)
|
||||
/sim/fgcom/device[n]/name (string)
|
||||
/sim/fgcom/device[n]/available-input (bool)
|
||||
/sim/fgcom/device[n]/available-output (bool)
|
||||
*/
|
||||
|
||||
//FIXME: OpenAL driver use an hard-coded device
|
||||
// so all following is unused finally until someone
|
||||
// implement "multi-device" support in IAX audio driver
|
||||
SGPropertyNode *node = fgGetNode("/sim/fgcom", 0, true);
|
||||
|
||||
struct iaxc_audio_device *devs;
|
||||
int nDevs, input, output, ring;
|
||||
|
||||
iaxc_audio_devices_get(&devs,&nDevs, &input, &output, &ring);
|
||||
|
||||
for(int i=0; i<nDevs; i++ ) {
|
||||
SGPropertyNode *in_node = node->getChild("device", i, true);
|
||||
|
||||
// devID
|
||||
_deviceID_node[i] = in_node->getChild("id", 0, true);
|
||||
_deviceID_node[i]->setIntValue(devs[i].devID);
|
||||
|
||||
// name
|
||||
_deviceName_node[i] = in_node->getChild("name", 0, true);
|
||||
_deviceName_node[i]->setStringValue(devs[i].name);
|
||||
|
||||
// input capability
|
||||
_deviceInput_node[i] = in_node->getChild("available-input", 0, true);
|
||||
if( devs[i].capabilities & IAXC_AD_INPUT )
|
||||
_deviceInput_node[i]->setBoolValue(true);
|
||||
else
|
||||
_deviceInput_node[i]->setBoolValue(false);
|
||||
|
||||
// output capability
|
||||
_deviceOutput_node[i] = in_node->getChild("available-output", 0, true);
|
||||
if( devs[i].capabilities & IAXC_AD_OUTPUT )
|
||||
_deviceOutput_node[i]->setBoolValue(true);
|
||||
else
|
||||
_deviceOutput_node[i]->setBoolValue(false);
|
||||
|
||||
// use default device at start
|
||||
if( devs[i].capabilities & IAXC_AD_INPUT_DEFAULT )
|
||||
_selectedInput_node->setIntValue(devs[i].devID);
|
||||
if( devs[i].capabilities & IAXC_AD_OUTPUT_DEFAULT )
|
||||
_selectedOutput_node->setIntValue(devs[i].devID);
|
||||
}
|
||||
|
||||
// Mute the mic and set speaker at start
|
||||
iaxc_input_level_set( 0.0 );
|
||||
iaxc_output_level_set(getCurrentCommVolume());
|
||||
|
||||
iaxc_millisleep(50);
|
||||
|
||||
// Do the first call at start
|
||||
setupCommFrequency();
|
||||
connectToCommFrequency();
|
||||
}
|
||||
|
||||
double FGCom::getCurrentCommVolume() const {
|
||||
double rv = 1.0;
|
||||
|
||||
if (_speakerLevel_node)
|
||||
rv = _speakerLevel_node->getFloatValue();
|
||||
|
||||
if (_commVolumeNode)
|
||||
rv = rv * _commVolumeNode->getFloatValue();
|
||||
|
||||
return rv;
|
||||
}
|
||||
double FGCom::getCurrentFrequencyKhz() const {
|
||||
return 10 * static_cast<int>(_currentCommFrequency * 100 + 0.25);
|
||||
}
|
||||
|
||||
void FGCom::setupCommFrequency(int channel) {
|
||||
if (channel < 1) {
|
||||
if (_selected_comm_node != nullptr) {
|
||||
channel = _selected_comm_node->getIntValue();
|
||||
}
|
||||
}
|
||||
// disconnect if channel set to 0
|
||||
if (channel < 1) {
|
||||
if (_currentCallIdent != -1) {
|
||||
iaxc_dump_call_number(_currentCallIdent);
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: disconnect as channel 0 " << _currentCallIdent);
|
||||
_currentCallIdent = -1;
|
||||
}
|
||||
_currentCommFrequency = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel > 0)
|
||||
{
|
||||
channel--; // adjust back to zero based index.
|
||||
SGPropertyNode *commRadioNode = fgGetNode("/instrumentation/")->getChild("comm", channel, false);
|
||||
if (commRadioNode) {
|
||||
SGPropertyNode *frequencyNode = commRadioNode->getChild("frequencies");
|
||||
if (_commVolumeNode)
|
||||
_commVolumeNode->removeChangeListener(this);
|
||||
_commVolumeNode = commRadioNode->getChild("volume");
|
||||
if (frequencyNode) {
|
||||
frequencyNode = frequencyNode->getChild("selected-mhz");
|
||||
if (frequencyNode) {
|
||||
if (_commFrequencyNode != frequencyNode) {
|
||||
if (_commFrequencyNode)
|
||||
_commFrequencyNode->removeChangeListener(this);
|
||||
_commFrequencyNode = frequencyNode;
|
||||
_commFrequencyNode->addChangeListener(this);
|
||||
_commVolumeNode->addChangeListener(this);
|
||||
}
|
||||
_currentCommFrequency = frequencyNode->getDoubleValue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: setupCommFrequency node listener failed: channel " << channel);
|
||||
}
|
||||
|
||||
if (_commFrequencyNode)
|
||||
_commFrequencyNode->removeChangeListener(this);
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: setupCommFrequency invalid channel " << channel);
|
||||
|
||||
_currentCommFrequency = 0.0;
|
||||
}
|
||||
|
||||
void FGCom::connectToCommFrequency() {
|
||||
// ensure that the current comm is still in range
|
||||
if ((_currentCallFrequency > 0.0) && !isInRange(_currentCallFrequency)) {
|
||||
SG_LOG(SG_IO, SG_WARN, "FGCom: call out of range of: " << _currentCallFrequency);
|
||||
_currentCallFrequency = 0.0;
|
||||
}
|
||||
|
||||
// don't connected (and disconnect if already connected) when tuned freq is 0
|
||||
if (_currentCommFrequency < 1.0) {
|
||||
if (_currentCallIdent != -1) {
|
||||
iaxc_dump_call_number(_currentCallIdent);
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: disconnect as freq 0: current call " << _currentCallIdent);
|
||||
_currentCallIdent = -1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentCallFrequency != _currentCommFrequency || _currentCallIdent == -1) {
|
||||
if (_currentCallIdent != -1) {
|
||||
iaxc_dump_call_number(_currentCallIdent);
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: dump_call_number " << _currentCallIdent);
|
||||
_currentCallIdent = -1;
|
||||
}
|
||||
|
||||
if (_currentCallIdent == -1)
|
||||
{
|
||||
std::string num = computePhoneNumber(_currentCommFrequency, getAirportCode(_currentCommFrequency));
|
||||
_processingTimer.stamp();
|
||||
if (!isInRange(_currentCommFrequency)) {
|
||||
if (_currentCallIdent != -1) {
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: disconnect call as not in range " << _currentCallIdent);
|
||||
if (_currentCallIdent != -1) {
|
||||
iaxc_dump_call_number(_currentCallIdent);
|
||||
_currentCallIdent = -1;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!num.empty()) {
|
||||
_currentCallIdent = iaxc_call(num.c_str());
|
||||
if (_currentCallIdent == -1)
|
||||
SG_LOG(SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str());
|
||||
else {
|
||||
SG_LOG(SG_IO, SG_DEBUG, "FGCom: call established " << num.c_str() << " Freq: " << _currentCommFrequency);
|
||||
_currentCallFrequency = _currentCommFrequency;
|
||||
}
|
||||
}
|
||||
else
|
||||
SG_LOG(SG_IO, SG_WARN, "FGCom: frequency " << _currentCommFrequency << " does not map to valid IAX address");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FGCom::updateCall()
|
||||
{
|
||||
if (_processingTimer.elapsedMSec() > IAX_DELAY) {
|
||||
_processingTimer.stamp();
|
||||
connectToCommFrequency();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::update(double dt)
|
||||
{
|
||||
if (!_enabled || !_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateCall();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::shutdown()
|
||||
{
|
||||
if( !_enabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
_initialized = false;
|
||||
_enabled = false;
|
||||
|
||||
iaxc_set_event_callback(NULL);
|
||||
iaxc_unregister(_regId);
|
||||
iaxc_stop_processing_thread();
|
||||
iaxc_shutdown();
|
||||
|
||||
// added to help debugging lingering IAX thread after fgOSCloseWindow
|
||||
flightgear::addSentryBreadcrumb("Did shutdown FGCom", "info");
|
||||
|
||||
assert( static_instance == this );
|
||||
static_instance = NULL;
|
||||
}
|
||||
|
||||
void FGCom::valueChanged(SGPropertyNode *prop)
|
||||
{
|
||||
if (prop == _enabled_node) {
|
||||
bool isEnabled = prop->getBoolValue();
|
||||
if (_enabled == isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( isEnabled ) {
|
||||
init();
|
||||
postinit();
|
||||
} else {
|
||||
shutdown();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// avoid crash when properties change before FGCom::postinit
|
||||
// https://sourceforge.net/p/flightgear/codetickets/2574/
|
||||
if (!_initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop == _commVolumeNode && _enabled) {
|
||||
if (_ptt_node->getIntValue()) {
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: ignoring change comm volume as PTT pressed");
|
||||
}
|
||||
else
|
||||
{
|
||||
iaxc_input_level_set(0.0);
|
||||
iaxc_output_level_set(getCurrentCommVolume());
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: change comm volume=" << _commVolumeNode->getFloatValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (prop == _selected_comm_node && _enabled) {
|
||||
setupCommFrequency();
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: change comm frequency (selected node): set to " << _currentCommFrequency);
|
||||
}
|
||||
|
||||
if (prop == _commFrequencyNode && _enabled) {
|
||||
setupCommFrequency();
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: change comm frequency (property updated): set to " << _currentCommFrequency);
|
||||
}
|
||||
|
||||
if (prop == _ptt_node && _enabled) {
|
||||
if (_ptt_node->getIntValue()) {
|
||||
// ensure that we are on the right channel by calling setupCommFrequency
|
||||
setupCommFrequency();
|
||||
iaxc_output_level_set(0.0);
|
||||
iaxc_input_level_set(_micLevel_node->getFloatValue()); //0.0 = min , 1.0 = max
|
||||
_mpTransmitFrequencyNode->setValue(_currentCallFrequency * 1000000);
|
||||
_mpTransmitPowerNode->setValue(1.0);
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: PTT active: " << _currentCallFrequency);
|
||||
}
|
||||
else {
|
||||
iaxc_output_level_set(getCurrentCommVolume());
|
||||
iaxc_input_level_set(0.0);
|
||||
SG_LOG(SG_IO, SG_INFO, "FGCom: PTT release: " << _currentCallFrequency << " vol=" << getCurrentCommVolume());
|
||||
_mpTransmitFrequencyNode->setValue(0);
|
||||
_mpTransmitPowerNode->setValue(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (prop == _test_node) {
|
||||
testMode( prop->getBoolValue() );
|
||||
return;
|
||||
}
|
||||
|
||||
if (prop == _silenceThd_node && _initialized) {
|
||||
float silenceThd = prop->getFloatValue();
|
||||
SG_CLAMP_RANGE<float>( silenceThd, -60, 0 );
|
||||
iaxc_set_silence_threshold( silenceThd );
|
||||
return;
|
||||
}
|
||||
|
||||
//FIXME: not implemented in IAX audio driver (audio_openal.c)
|
||||
if (prop == _micBoost_node && _initialized) {
|
||||
int micBoost = prop->getIntValue();
|
||||
SG_CLAMP_RANGE<int>( micBoost, 0, 1 );
|
||||
iaxc_mic_boost_set( micBoost ) ; // 0 = enabled , 1 = disabled
|
||||
return;
|
||||
}
|
||||
|
||||
//FIXME: not implemented in IAX audio driver (audio_openal.c)
|
||||
if ((prop == _selectedInput_node || prop == _selectedOutput_node) && _initialized) {
|
||||
int selectedInput = _selectedInput_node->getIntValue();
|
||||
int selectedOutput = _selectedOutput_node->getIntValue();
|
||||
iaxc_audio_devices_set(selectedInput, selectedOutput, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_listener_active)
|
||||
return;
|
||||
|
||||
_listener_active++;
|
||||
|
||||
if (prop == _speakerLevel_node && _enabled) {
|
||||
float speakerLevel = prop->getFloatValue();
|
||||
SG_CLAMP_RANGE<float>( speakerLevel, 0.0, 1.0 );
|
||||
_speakerLevel_node->setFloatValue(speakerLevel);
|
||||
iaxc_output_level_set(speakerLevel);
|
||||
}
|
||||
|
||||
if (prop == _micLevel_node && _enabled) {
|
||||
float micLevel = prop->getFloatValue();
|
||||
SG_CLAMP_RANGE<float>( micLevel, 0.0, 1.0 );
|
||||
_micLevel_node->setFloatValue(micLevel);
|
||||
//iaxc_input_level_set(micLevel);
|
||||
}
|
||||
|
||||
_listener_active--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FGCom::testMode(bool testMode)
|
||||
{
|
||||
if(testMode && _initialized) {
|
||||
_enabled = false;
|
||||
iaxc_dump_all_calls();
|
||||
iaxc_input_level_set( 1.0 );
|
||||
iaxc_output_level_set( _speakerLevel_node->getFloatValue() );
|
||||
std::string num = computePhoneNumber(TEST_FREQ, NULL_ICAO);
|
||||
if( num.size() > 0 ) {
|
||||
iaxc_millisleep(IAX_DELAY);
|
||||
_currentCallIdent = iaxc_call(num.c_str());
|
||||
}
|
||||
if( _currentCallIdent == -1 )
|
||||
SG_LOG( SG_IO, SG_DEBUG, "FGCom: cannot call " << num.c_str() );
|
||||
} else {
|
||||
if( _initialized ) {
|
||||
iaxc_dump_all_calls();
|
||||
iaxc_millisleep(IAX_DELAY);
|
||||
iaxc_input_level_set( 0.0 );
|
||||
iaxc_output_level_set(getCurrentCommVolume());
|
||||
_currentCallIdent = -1;
|
||||
_enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
\param freq The requested frequency e.g 120.825
|
||||
\return The ICAO code as string e.g LFMV
|
||||
*/
|
||||
|
||||
std::string FGCom::getAirportCode(const double& freq)
|
||||
{
|
||||
SGGeod aircraftPos = globals->get_aircraft_position();
|
||||
|
||||
for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
|
||||
if(special_freq[i] == getCurrentFrequencyKhz()) {
|
||||
return NULL_ICAO;
|
||||
}
|
||||
}
|
||||
|
||||
flightgear::CommStation* apt = flightgear::CommStation::findByFreq(getCurrentFrequencyKhz(), aircraftPos);
|
||||
if( !apt ) {
|
||||
apt = flightgear::CommStation::findByFreq(getCurrentFrequencyKhz() -10, aircraftPos); // Check for 8.33KHz
|
||||
if( !apt ) {
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
if( apt->type() == FGPositioned::FREQ_TOWER ) {
|
||||
_maxRange = MAX_TWR_RANGE;
|
||||
_minRange = MIN_GNDTWR_RANGE;
|
||||
} else if( apt->type() == FGPositioned::FREQ_GROUND ) {
|
||||
_maxRange = MAX_GND_RANGE;
|
||||
_minRange = MIN_GNDTWR_RANGE;
|
||||
} else {
|
||||
_maxRange = MAX_RANGE;
|
||||
_minRange = MIN_RANGE;
|
||||
}
|
||||
|
||||
_aptPos = apt->geod();
|
||||
return apt->airport()->ident();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
\param freq The requested frequency e.g 120.825
|
||||
\param iaco The associated ICAO code e.g LFMV
|
||||
\return The phone number as string i.e username:password@fgcom.flightgear.org/0176707786120825
|
||||
*/
|
||||
|
||||
std::string FGCom::computePhoneNumber(const double& freq, const std::string& icao) const
|
||||
{
|
||||
if( icao.empty() )
|
||||
return std::string();
|
||||
|
||||
char phoneNumber[256];
|
||||
char exten[32];
|
||||
char tmp[5];
|
||||
|
||||
/*Convert ICAO to ASCII */
|
||||
sprintf( tmp, "%4s", icao.c_str() );
|
||||
|
||||
/*Built the phone number */
|
||||
sprintf( exten,
|
||||
"%02d%02d%02d%02d%02d%06d",
|
||||
01,
|
||||
tmp[0],
|
||||
tmp[1],
|
||||
tmp[2],
|
||||
tmp[3],
|
||||
(int) (freq * 1000 + 0.5) );
|
||||
exten[16] = '\0';
|
||||
|
||||
snprintf( phoneNumber,
|
||||
sizeof (phoneNumber),
|
||||
"%s:%s@%s/%s",
|
||||
_username.c_str(),
|
||||
_password.c_str(),
|
||||
_server.c_str(),
|
||||
exten);
|
||||
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
\return A boolean value, 1=in range, 0=out of range
|
||||
*/
|
||||
|
||||
bool FGCom::isInRange(const double &freq) const
|
||||
{
|
||||
for(size_t i=0; i<sizeof(special_freq)/sizeof(special_freq[0]); i++) { // Check if it's a special freq
|
||||
if( (special_freq[i]) == getCurrentFrequencyKhz()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
SGGeod acftPos = globals->get_aircraft_position();
|
||||
double distNm = SGGeodesy::distanceNm(_aptPos, acftPos);
|
||||
double delta_elevation_ft = fabs(acftPos.getElevationFt() - _aptPos.getElevationFt());
|
||||
double rangeNm = 1.23 * sqrt(delta_elevation_ft);
|
||||
|
||||
if (rangeNm > _maxRange) rangeNm = _maxRange;
|
||||
if (rangeNm < _minRange) rangeNm = _minRange;
|
||||
if( distNm > rangeNm ) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FGCom> registrantFGCom;
|
||||
108
src/Network/fgcom.hxx
Normal file
108
src/Network/fgcom.hxx
Normal file
@@ -0,0 +1,108 @@
|
||||
#ifndef FG_FGCOM_HXX
|
||||
#define FG_FGCOM_HXX
|
||||
|
||||
// fgcom.hxx -- FGCom: Voice communication
|
||||
//
|
||||
// Written by Clement de l'Hamaide, started May 2013.
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
|
||||
class FGCom : public SGSubsystem,
|
||||
public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
FGCom();
|
||||
virtual ~FGCom();
|
||||
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void init() override;
|
||||
void postinit() override;
|
||||
void shutdown() override;
|
||||
void unbind() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "fgcom"; }
|
||||
|
||||
virtual void valueChanged(SGPropertyNode *prop);
|
||||
void iaxTextEvent(struct iaxc_ev_text text);
|
||||
|
||||
private:
|
||||
SGPropertyNode_ptr _ptt_node; // PTT; nonzero int indicating channel number (instrumentation/comm/[channel-1])
|
||||
SGPropertyNode_ptr _selected_comm_node; // selected channel (fgcom); nonzero channel int indicating channel number (instrumentation/comm/[channel-1])
|
||||
SGPropertyNode_ptr _commFrequencyNode; // current comm node in use; e.g. /instrumentation/comm[0]
|
||||
SGPropertyNode_ptr _commVolumeNode; // current volume node in use; e.g. /instrumentation/comm[0]/volume
|
||||
SGPropertyNode_ptr _test_node; // sim/fgcom/test
|
||||
SGPropertyNode_ptr _text_node; // sim/fgcom/text
|
||||
SGPropertyNode_ptr _server_node; // sim/fgcom/server
|
||||
SGPropertyNode_ptr _enabled_node; // sim/fgcom/enabled
|
||||
SGPropertyNode_ptr _version_node; // sim/version/flightgear
|
||||
SGPropertyNode_ptr _micBoost_node; // sim/fgcom/mic-boost
|
||||
SGPropertyNode_ptr _callsign_node; // sim/multiplay/callsign
|
||||
SGPropertyNode_ptr _register_node; // sim/fgcom/register/enabled
|
||||
SGPropertyNode_ptr _username_node; // sim/fgcom/register/username
|
||||
SGPropertyNode_ptr _password_node; // sim/fgcom/register/password
|
||||
SGPropertyNode_ptr _micLevel_node; // sim/fgcom/mic-level
|
||||
SGPropertyNode_ptr _silenceThd_node; // sim/fgcom/silence-threshold
|
||||
SGPropertyNode_ptr _speakerLevel_node; // sim/fgcom/speaker-level
|
||||
SGPropertyNode_ptr _deviceID_node[4]; // sim/fgcom/device[n]/id
|
||||
SGPropertyNode_ptr _deviceName_node[4]; // sim/fgcom/device[n]/name
|
||||
SGPropertyNode_ptr _deviceInput_node[4]; // sim/fgcom/device[n]/available-input
|
||||
SGPropertyNode_ptr _deviceOutput_node[4]; // sim/fgcom/device[n]/available-output
|
||||
SGPropertyNode_ptr _selectedInput_node; // sim/fgcom/device-input
|
||||
SGPropertyNode_ptr _selectedOutput_node; // sim/fgcom/device-output
|
||||
SGPropertyNode_ptr _showMessages_node; // sim/fgcom/show-messages
|
||||
SGPropertyNode_ptr _mpTransmitFrequencyNode; // sim/multiplay/comm-transmit-frequency-mhz
|
||||
SGPropertyNode_ptr _mpTransmitPowerNode; // sim/multiplay/comm-transmit-power-norm
|
||||
|
||||
double _maxRange = 0.0;
|
||||
double _minRange = 0.0;
|
||||
double _currentCommFrequency = 0.0;
|
||||
double _currentCallFrequency = 0.0;
|
||||
bool _register = true;
|
||||
bool _enabled = false;
|
||||
bool _initialized = false;
|
||||
int _regId = 0;
|
||||
int _currentCallIdent = -1;
|
||||
//int _callComm1;
|
||||
int _listener_active = 0;
|
||||
std::string _server;
|
||||
std::string _callsign;
|
||||
std::string _username;
|
||||
std::string _password;
|
||||
SGTimeStamp _processingTimer;
|
||||
SGGeod _aptPos;
|
||||
|
||||
std::string computePhoneNumber(const double& freq, const std::string& icao) const;
|
||||
std::string getAirportCode(const double& freq);
|
||||
// SGGeod getAirportPos(const double& freq) const;
|
||||
void setupCommFrequency(int channel = -1);
|
||||
double getCurrentFrequencyKhz() const;
|
||||
double getCurrentCommVolume() const;
|
||||
bool isInRange(const double& freq) const;
|
||||
|
||||
void updateCall();
|
||||
void connectToCommFrequency();
|
||||
void testMode(bool testMode);
|
||||
};
|
||||
|
||||
#endif // of FG_FGCOM_HXX
|
||||
|
||||
|
||||
469
src/Network/flarm.cxx
Normal file
469
src/Network/flarm.cxx
Normal file
@@ -0,0 +1,469 @@
|
||||
// flarm.cxx -- Flarm protocol class
|
||||
//
|
||||
// Written by Thorsten Brehm, started November 2017.
|
||||
//
|
||||
// Copyright (C) 2017 Thorsten Brehm - brehmt (at) gmail com
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/constants.h>
|
||||
#include <simgear/io/iochannel.hxx>
|
||||
#include <simgear/math/sg_geodesy.hxx>
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
#include <FDM/flightProperties.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <flightgearBuildId.h>
|
||||
|
||||
#include "flarm.hxx"
|
||||
|
||||
using namespace NMEA;
|
||||
|
||||
// #define FLARM_DEBUGGING
|
||||
|
||||
#ifdef FLARM_DEBUGGING
|
||||
#warning Flarm debugging is enabled!
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The Flarm protocol emulation reports multi-player and AI aircraft within the
|
||||
* configured range using NMEA-style messages. The module supports bidirectional
|
||||
* communication, i.e. its capable of sending messages, and also of receiving
|
||||
* (and replying to) configuration commands. The emulation should be good enough
|
||||
* to convince standard NAV/moving map clients (tablet APPs like skydemon, skymap,
|
||||
* xcsoar etc) that they're connected to a Flarm device. The clients receive the
|
||||
* GPS position and traffic information from the flight simulator.
|
||||
*
|
||||
* By default, the emulation reports all moving aircraft as targets of the lowest
|
||||
* alert level ("traffic info only"). Parked aircraft are not reported.
|
||||
*
|
||||
* Higher alert levels (low-level/import/urgent alert) are only triggered
|
||||
* when the FlightGear TCAS instrument is installed in the aircraft model (yes,
|
||||
* Flarm is not TCAS, but reusing the TCAS threat-level for the emulation
|
||||
* should be good enough :-) ). The TCAS instrument classifies all AI and MP
|
||||
* aircraft into 4 threat levels (from invisible to high alert) - similar to
|
||||
* the actual alert levels normally provided by a Flarm device.
|
||||
*
|
||||
* Supported NMEA messages:
|
||||
* $GPRMC: own position information
|
||||
*
|
||||
* Supported Garmin proprietary messages:
|
||||
* $PGRMZ: own barometric altitude information in feet
|
||||
*
|
||||
* Supported Flarm proprietary messages:
|
||||
* $PFLAU: status and intruder data
|
||||
* $PFLAA: data on targets within range
|
||||
* $PFLAV: version information
|
||||
* $PFLAE: device status information
|
||||
* $PFLAC: configuration request
|
||||
* $PFLAS: debug information
|
||||
*
|
||||
* Module properties:
|
||||
* All Flarm configuration properties are mirrored to /sim/flarm/config.
|
||||
* Useful properties:
|
||||
* /sim/flarm/config/RANGE Range in meters when considering traffic targets
|
||||
* /sim/flarm/config/NMEAOUT NMEA message mode (0=off, 1=all, 2=Garmin/NMEA messages only, 3=Flarm messages only)
|
||||
* /sim/flarm/config/ACFT Aircraft type (1=glider, 3=helicopter, 8=motor aircraft, 9=jet)
|
||||
*
|
||||
*/
|
||||
|
||||
FGFlarm::FGFlarm() :
|
||||
FGGarmin(),
|
||||
mFlarmMessages(FLARM::SET),
|
||||
mFlarmConfig(fgGetNode("/sim/flarm/config", true)),
|
||||
mLastUpdate(0)
|
||||
{
|
||||
// Flarm (and Garmin) devices normally report barometric altitude in feet
|
||||
mMetric = false;
|
||||
// disable all Garmin messages, except PGRMZ
|
||||
mGarminMessages = GARMIN::PGRMZ;
|
||||
// Allow processing more message lines per cycle than with FG's standard NMEA protocol,
|
||||
// otherwise we won't reply quickly when a remote sends multiple configuration requests.
|
||||
mMaxReceiveLines = 20;
|
||||
// allow bidirectional communication (we're sending data and accepting requests)
|
||||
mBiDirectionalSupport = true;
|
||||
|
||||
#ifdef FLARM_DEBUGGING
|
||||
// show I/O debug messages
|
||||
sglog().set_log_classes(SG_IO);
|
||||
sglog().set_log_priority(SG_DEBUG);
|
||||
#endif
|
||||
|
||||
// some default configuration data, to please XCSoar and other apps
|
||||
const unsigned int zero=0;
|
||||
setDefaultConfigValue("ACFT", 9);
|
||||
setDefaultConfigValue("ADDWP", "");
|
||||
setDefaultConfigValue("BAUD", 5);
|
||||
setDefaultConfigValue("CFLAGS", zero);
|
||||
setDefaultConfigValue("COMPID", "");
|
||||
setDefaultConfigValue("COMPCLASS", "");
|
||||
setDefaultConfigValue("COPIL", "");
|
||||
setDefaultConfigValue("GLIDERID", "");
|
||||
setDefaultConfigValue("GLIDERTYPE", "");
|
||||
setDefaultConfigValue("ID", "0");
|
||||
setDefaultConfigValue("LOGINT", 2);
|
||||
setDefaultConfigValue("NEWTASK", "");
|
||||
setDefaultConfigValue("NMEAOUT", 1); // all messages enabled
|
||||
setDefaultConfigValue("NOTRACK", zero); // disabled
|
||||
setDefaultConfigValue("PILOT", "Curt"); // :-)))
|
||||
setDefaultConfigValue("PRIV", zero);
|
||||
setDefaultConfigValue("RANGE", 25500);
|
||||
setDefaultConfigValue("THRE", 2);
|
||||
setDefaultConfigValue("UI", zero);
|
||||
}
|
||||
|
||||
|
||||
FGFlarm::~FGFlarm() {
|
||||
}
|
||||
|
||||
|
||||
void FGFlarm::setDefaultConfigValue(const char* ConfigKey, const char* Value)
|
||||
{
|
||||
if (!mFlarmConfig->hasValue(ConfigKey))
|
||||
mFlarmConfig->setStringValue(ConfigKey, Value);
|
||||
}
|
||||
|
||||
|
||||
void FGFlarm::setDefaultConfigValue(const char* ConfigKey, unsigned int Value)
|
||||
{
|
||||
if (!mFlarmConfig->hasValue(ConfigKey))
|
||||
mFlarmConfig->setIntValue(ConfigKey, Value);
|
||||
}
|
||||
|
||||
|
||||
// generate Flarm NMEA messages
|
||||
bool FGFlarm::gen_message()
|
||||
{
|
||||
// generate generic messages first
|
||||
FGGarmin::gen_message();
|
||||
|
||||
// traffic updates once per second only, independent of the normal protocol frequency
|
||||
if ((get_count()-mLastUpdate)/get_hz() < 1.0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
mLastUpdate = get_count();
|
||||
|
||||
char nmea[256];
|
||||
int TargetCount=0;
|
||||
double ClosestDistanceM2 = 99e9;
|
||||
double ClosestLond=0.0, ClosestLatd=0.0;
|
||||
int ClosestRelVerticalM=0;
|
||||
int ClosestThreatLevel=-1;
|
||||
|
||||
// obtain own position
|
||||
double latd = mFdm.get_Latitude() * SGD_RADIANS_TO_DEGREES;
|
||||
double lond = mFdm.get_Longitude() * SGD_RADIANS_TO_DEGREES;
|
||||
|
||||
// PFLAA (Flarm proprietary)
|
||||
if (mFlarmMessages & FLARM::PFLAA)
|
||||
{
|
||||
double altitude_ft = mFdm.get_Altitude();
|
||||
|
||||
// check all AI/MP aircraft
|
||||
SGPropertyNode* pAi = fgGetNode("/ai/models", true);
|
||||
simgear::PropertyList aircraftList = pAi->getChildren("aircraft");
|
||||
for (simgear::PropertyList::iterator i = aircraftList.begin(); i != aircraftList.end(); ++i)
|
||||
{
|
||||
SGPropertyNode* pModel = *i;
|
||||
if ((pModel)&&(pModel->nChildren()))
|
||||
{
|
||||
double GroundSpeedKt = pModel->getDoubleValue("velocities/true-airspeed-kt", 0.0);
|
||||
int threatLevel = pModel->getIntValue("tcas/threat-level", -99);
|
||||
// threatLevel is undefined (-99) when no TCAS is installed
|
||||
if (threatLevel == -99)
|
||||
{
|
||||
// set threat level to 0 (traffic info) when a/c is moving. Otherwise -1 (invisible).
|
||||
threatLevel = (GroundSpeedKt>1) ? 0 : -1;
|
||||
}
|
||||
|
||||
// report traffic, unless considered "invisible"
|
||||
if (threatLevel >= 0)
|
||||
{
|
||||
// position data of current intruder
|
||||
double targetLatd = pModel->getDoubleValue("position/latitude-deg", 0.0);
|
||||
double targetLond = pModel->getDoubleValue("position/longitude-deg", 0.0);
|
||||
|
||||
// calculate the relative North and relative East distances in meters, as
|
||||
// required by the Flarm protocol
|
||||
double RelNorthAngleDeg = targetLatd - latd;
|
||||
double RelNorth = ((2*SG_PI*SG_POLAR_RADIUS_M) / 360.0) * RelNorthAngleDeg;
|
||||
|
||||
double RelEastAngleDeg = targetLond - lond;
|
||||
double RelEast = ((2*SG_PI*SG_EQUATORIAL_RADIUS_M) / 360.0) *
|
||||
abs(cos(latd*SGD_DEGREES_TO_RADIANS)) * RelEastAngleDeg;
|
||||
|
||||
#ifdef FLARM_DEBUGGING
|
||||
{
|
||||
double distanceM = sqrt(RelNorth*RelNorth+RelEast*RelEast);
|
||||
pModel->setDoubleValue("flarm/distance", distanceM);
|
||||
pModel->setIntValue("flarm/alive", pModel->getIntValue("flarm/alive",0)+1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// do not consider targets beyond 100km (1e5 meters)
|
||||
double FlarmRangeM = mFlarmConfig->getIntValue("RANGE", 25500);
|
||||
double DistanceM2 = RelNorth*RelNorth+RelEast*RelEast;
|
||||
if (DistanceM2 < FlarmRangeM*FlarmRangeM)
|
||||
{
|
||||
TargetCount++;
|
||||
#if 0//def FLARM_DEBUGGING
|
||||
{
|
||||
double distanceM = sqrt(RelNorth*RelNorth+RelEast*RelEast);
|
||||
printf("%3u: id %3u, %s, distance: %.1fkm, North: %.1f, East: %.1f, speed: %.1f kt\n",
|
||||
pModel->getIndex(),
|
||||
pModel->getIntValue("id"),
|
||||
pModel->getStringValue("callsign", "<no name>"),
|
||||
distanceM/1000.0, RelNorth/1e3, RelEast/1e3, GroundSpeedKt);
|
||||
}
|
||||
#endif
|
||||
|
||||
int RelVerticalM = (pModel->getDoubleValue("position/altitude-ft", 0.0)-altitude_ft)* SG_FEET_TO_METER;
|
||||
int Track = pModel->getDoubleValue("orientation/true-heading-deg", 0.0);
|
||||
int ClimbRateMs = pModel->getDoubleValue("velocities/vertical-speed-fps", 0.0) * (SG_FPS_TO_KT * SG_KT_TO_MPS);
|
||||
int AcftType = 9; // report as jet aircraft for now
|
||||
// generate some fake 6-digit hex code
|
||||
unsigned int ID = pModel->getIntValue("id") & 0x00FFFFFF;
|
||||
//$PFLAA,AlarmLevel,RelNorth,RelEast,RelVertical,IDType,ID,Track,TurnRate,GroundSpeed,ClimbRate,AcftType
|
||||
snprintf( nmea, 256, "$PFLAA,%u,%i,%i,%i,2,%06X,%u,,%i,%i,%u",
|
||||
threatLevel, (int)RelNorth, (int)RelEast, RelVerticalM, ID,
|
||||
Track, (int) (GroundSpeedKt*SG_KT_TO_MPS), ClimbRateMs, AcftType);
|
||||
add_with_checksum(nmea, 256);
|
||||
|
||||
if (DistanceM2 < ClosestDistanceM2)
|
||||
{
|
||||
ClosestDistanceM2 = DistanceM2;
|
||||
ClosestThreatLevel = threatLevel;
|
||||
ClosestRelVerticalM = RelVerticalM;
|
||||
ClosestLatd = targetLatd;
|
||||
ClosestLond = targetLond;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PFLAU (Flarm proprietary)
|
||||
if (mFlarmMessages & FLARM::PFLAU)
|
||||
{
|
||||
if (ClosestThreatLevel < 0)
|
||||
{
|
||||
// no threats, but maybe some targets
|
||||
snprintf( nmea, 256, "$PFLAU,%u,1,1,1,0,,0,,", TargetCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate the bearing and range of the closest target
|
||||
double az2, bearing, distanceM;
|
||||
geo_inverse_wgs_84(latd, lond, ClosestLatd, ClosestLond, &bearing, &az2, &distanceM);
|
||||
|
||||
// calculate relative bearing
|
||||
double heading = mFdm.get_Psi_deg();
|
||||
bearing -= heading;
|
||||
SG_NORMALIZE_RANGE(bearing, -180.0, 180.0);
|
||||
|
||||
// set alert mode, depending on TCAS threat level
|
||||
int AlertMode = 0; // no alert
|
||||
if (ClosestThreatLevel >= 2) // TCAS RA alert
|
||||
AlertMode = 2; // alarm!
|
||||
else
|
||||
if (ClosestThreatLevel == 1) // TCAS proximity alert
|
||||
AlertMode = 1; // warning
|
||||
snprintf( nmea, 256, "$PFLAU,%u,1,1,1,%u,%.0f,%u,%u,%u",
|
||||
TargetCount, ClosestThreatLevel, bearing, AlertMode, ClosestRelVerticalM, (unsigned int) distanceM);
|
||||
}
|
||||
add_with_checksum(nmea, 256);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// process a Flarm sentence
|
||||
void FGFlarm::parse_message(const std::vector<std::string>& tokens)
|
||||
{
|
||||
char nmea[256];
|
||||
|
||||
if ( tokens[0] == "PFLAE" )
|
||||
{
|
||||
if (tokens.size()<2)
|
||||
return;
|
||||
|
||||
// #1: request
|
||||
const string& request = tokens[1];
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAE request = " << request );
|
||||
if (request == "R")
|
||||
{
|
||||
// report "no errors"
|
||||
snprintf( nmea, 256, "$PFLAE,A,0,0");
|
||||
add_with_checksum(nmea, 256);
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAE reply= " << nmea );
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( tokens[0] == "PFLAV" )
|
||||
{
|
||||
if (tokens.size()<2)
|
||||
return;
|
||||
|
||||
// #1: request
|
||||
const string& request = tokens[1];
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAV version request = " << request );
|
||||
if (request == "R")
|
||||
{
|
||||
// report some fixed version to please the requesting device
|
||||
snprintf( nmea, 256, "$PFLAV,A,2.00,6.00,");
|
||||
add_with_checksum(nmea, 256);
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAV reply= " << nmea );
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( tokens[0] == "PFLAS" )
|
||||
{
|
||||
// status/debug information
|
||||
|
||||
if (tokens.size()<2)
|
||||
return;
|
||||
|
||||
// #1: request
|
||||
const string& request = tokens[1];
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAS status/debug request = " << request );
|
||||
if (request == "R")
|
||||
{
|
||||
// Report some debug data to please the requesting device.
|
||||
// Apparently debug replies are not in NMEA format and contain plain text.
|
||||
const char* FlrmDebugReply = (
|
||||
"------------------------------------------\r\n"
|
||||
"FlightGear " FLIGHTGEAR_VERSION "\r\n"
|
||||
"Revision " REVISION "\r\n"
|
||||
"------------------------------------------\r\n");
|
||||
mNmeaSentence += FlrmDebugReply;
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAS reply = " << FlrmDebugReply );
|
||||
}
|
||||
}
|
||||
else
|
||||
if ( tokens[0] == "PFLAC" )
|
||||
{
|
||||
// configuration command
|
||||
|
||||
if (tokens.size()<3)
|
||||
return;
|
||||
|
||||
bool Error = true;
|
||||
|
||||
// #1: request
|
||||
const string& request = tokens[1];
|
||||
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAC config request = " << request );
|
||||
|
||||
// #2: keyword
|
||||
const string& keyword = tokens[2];
|
||||
SG_LOG( SG_IO, SG_DEBUG, " PFLAC config request = " << keyword );
|
||||
|
||||
// check if the config element is supported
|
||||
SGPropertyNode* configNode = mFlarmConfig->getChild(keyword,0,false);
|
||||
if (!configNode)
|
||||
{
|
||||
// unsupported configuration element
|
||||
}
|
||||
else
|
||||
if (request == "R")
|
||||
{
|
||||
// reply with config data
|
||||
snprintf( nmea, 256, "$PFLAC,A,%s,%s",
|
||||
keyword.c_str(), configNode->getStringValue().c_str());
|
||||
add_with_checksum(nmea, 256);
|
||||
Error = false;
|
||||
}
|
||||
else
|
||||
if (request == "S")
|
||||
{
|
||||
if (tokens.size()<4)
|
||||
return;
|
||||
Error = false;
|
||||
|
||||
// special handling for some parameters
|
||||
if (keyword == "NMEAOUT")
|
||||
{
|
||||
// #3: value
|
||||
int value = (int) atof(tokens[3].c_str());
|
||||
switch(value % 10)
|
||||
{
|
||||
case 0:
|
||||
// disable all periodic messages
|
||||
mNmeaMessages = 0;
|
||||
mGarminMessages = 0;
|
||||
mFlarmMessages = 0;
|
||||
break;
|
||||
case 1:
|
||||
// enable all periodic messages
|
||||
mNmeaMessages = NMEA::SET;
|
||||
mGarminMessages = GARMIN::PGRMZ;
|
||||
mFlarmMessages = FLARM::SET;
|
||||
break;
|
||||
case 2:
|
||||
// enable all, except periodic Flarm messages
|
||||
mNmeaMessages = NMEA::SET;
|
||||
mGarminMessages = GARMIN::PGRMZ;
|
||||
mFlarmMessages = 0;
|
||||
break;
|
||||
case 3:
|
||||
// enable all, except periodic NMEA messages
|
||||
mNmeaMessages = 0;
|
||||
mGarminMessages = GARMIN::PGRMZ;
|
||||
mFlarmMessages = FLARM::SET;
|
||||
break;
|
||||
default:
|
||||
Error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Error)
|
||||
{
|
||||
// just store the config data as it is
|
||||
configNode->setStringValue(tokens[3]);
|
||||
|
||||
// reply
|
||||
snprintf( nmea, 256, "$PFLAC,A,%s,%s",
|
||||
keyword.c_str(), configNode->getStringValue().c_str());
|
||||
add_with_checksum(nmea, 256);
|
||||
}
|
||||
}
|
||||
|
||||
if (Error)
|
||||
{
|
||||
// report error for unsupported requests
|
||||
snprintf( nmea, 256, "$PFLAC,A,ERROR");
|
||||
add_with_checksum(nmea, 256);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid or unsupported message.
|
||||
// In flarm mode, we only accept flarm messages as input, but no other messages, i.e. no position updates.
|
||||
// Use the Garmin or NMEA basic protocols to feed position data into FG.
|
||||
SG_LOG( SG_IO, SG_DEBUG, " Unsupported message = " << tokens[0] );
|
||||
}
|
||||
}
|
||||
58
src/Network/flarm.hxx
Normal file
58
src/Network/flarm.hxx
Normal file
@@ -0,0 +1,58 @@
|
||||
// flarm.hxx -- Flarm protocol class
|
||||
//
|
||||
// Written by Thorsten Brehm, started November 2017.
|
||||
//
|
||||
// Copyright (C) 2017 Thorsten Brehm - brehmt (at) gmail com
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef _FG_FLARM_HXX
|
||||
#define _FG_FLARM_HXX
|
||||
|
||||
#include "garmin.hxx"
|
||||
|
||||
namespace NMEA
|
||||
{
|
||||
// Flarm proprietary messages
|
||||
namespace FLARM
|
||||
{
|
||||
const unsigned int PFLAU = (1<<0);
|
||||
const unsigned int PFLAA = (1<<1);
|
||||
const unsigned int SET = (PFLAU|PFLAA);
|
||||
}
|
||||
}
|
||||
|
||||
class FGFlarm : public FGGarmin {
|
||||
protected:
|
||||
unsigned int mFlarmMessages;
|
||||
SGPropertyNode_ptr mFlarmConfig;
|
||||
unsigned long mLastUpdate;
|
||||
|
||||
// process a Flarm sentence
|
||||
virtual void parse_message(const std::vector<std::string>& tokens);
|
||||
|
||||
void setDefaultConfigValue(const char* ConfigKey, const char* Value);
|
||||
void setDefaultConfigValue(const char* ConfigKey, unsigned int Value);
|
||||
|
||||
public:
|
||||
FGFlarm();
|
||||
~FGFlarm();
|
||||
|
||||
virtual bool gen_message();
|
||||
};
|
||||
|
||||
#endif // _FG_FLARM_HXX
|
||||
103
src/Network/garmin.cxx
Normal file
103
src/Network/garmin.cxx
Normal file
@@ -0,0 +1,103 @@
|
||||
// garmin.cxx -- Garmin protocol class
|
||||
//
|
||||
// Written by Curtis Olson, started November 1999.
|
||||
//
|
||||
// Copyright (C) 1999 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <FDM/flightProperties.hxx>
|
||||
#include <simgear/constants.h>
|
||||
#include "garmin.hxx"
|
||||
|
||||
using namespace NMEA;
|
||||
|
||||
FGGarmin::FGGarmin() :
|
||||
FGNMEA(),
|
||||
mGarminMessages(GARMIN::PGRMZ),
|
||||
mMetric(true) // use metric altitude reports
|
||||
// In fact Garmin devices normally seem report barometric altitude in feet (not meters), but
|
||||
// the FG implementation has always used metric reports for years (and we keep it this way,
|
||||
// to avoid complaints about changed implementation). The unit is also part of the NMEA message,
|
||||
// so smart devices are probably ok with both anyway.
|
||||
{
|
||||
// only enable the GPRMC standard NMEA message
|
||||
mNmeaMessages = NMEA::GPRMC;
|
||||
// Garmin uses CR-LF line feeds.
|
||||
mLineFeed = "\r\n";
|
||||
}
|
||||
|
||||
|
||||
FGGarmin::~FGGarmin()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// generate Garmin NMEA messages
|
||||
bool FGGarmin::gen_message()
|
||||
{
|
||||
(void) FGNMEA::gen_message();
|
||||
|
||||
char nmea[256];
|
||||
|
||||
// RMZ sentence (Garmin proprietary)
|
||||
if (mGarminMessages & GARMIN::PGRMZ)
|
||||
{
|
||||
double altitude_ft = mFdm.get_Altitude();
|
||||
|
||||
// $PGRMZ,AAAA.A,F,T*XX
|
||||
if (mMetric)
|
||||
sprintf( nmea, "$PGRMZ,%.1f,M,3", altitude_ft * SG_FEET_TO_METER );
|
||||
else
|
||||
sprintf( nmea, "$PGRMZ,%.1f,F,3", altitude_ft );
|
||||
add_with_checksum(nmea, 256);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// process a Garmin sentence
|
||||
void FGGarmin::parse_message(const std::vector<std::string>& tokens)
|
||||
{
|
||||
if (tokens[0] == "PGRMZ")
|
||||
{
|
||||
if (tokens.size()<3)
|
||||
return;
|
||||
|
||||
// #1: altitude
|
||||
double altitude = atof( tokens[1].c_str() );
|
||||
|
||||
// #2: altitude units
|
||||
const std::string& alt_units = tokens[2];
|
||||
if ( alt_units != "F" && alt_units != "f" )
|
||||
altitude *= SG_METER_TO_FEET;
|
||||
|
||||
mFdm.set_Altitude( altitude );
|
||||
|
||||
SG_LOG( SG_IO, SG_DEBUG, " altitude = " << altitude );
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a Garmin message. Maybe standard NMEA message.
|
||||
FGNMEA::parse_message(tokens);
|
||||
}
|
||||
}
|
||||
55
src/Network/garmin.hxx
Normal file
55
src/Network/garmin.hxx
Normal file
@@ -0,0 +1,55 @@
|
||||
// garmin.hxx -- Garmin protocol class
|
||||
//
|
||||
// Written by Curtis Olson, started November 1999.
|
||||
//
|
||||
// Copyright (C) 1999 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#ifndef _FG_GARMIN_HXX
|
||||
#define _FG_GARMIN_HXX
|
||||
|
||||
#include "nmea.hxx"
|
||||
|
||||
namespace NMEA
|
||||
{
|
||||
// Garmin proprietary messages
|
||||
namespace GARMIN
|
||||
{
|
||||
const unsigned int PGRMZ = (1<<0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FGGarmin : public FGNMEA {
|
||||
|
||||
|
||||
protected:
|
||||
unsigned int mGarminMessages;
|
||||
bool mMetric;
|
||||
|
||||
// process a Garmin sentence
|
||||
virtual void parse_message(const std::vector<std::string>& tokens);
|
||||
|
||||
public:
|
||||
FGGarmin();
|
||||
~FGGarmin();
|
||||
|
||||
virtual bool gen_message();
|
||||
};
|
||||
|
||||
#endif // _FG_GARMIN_HXX
|
||||
1009
src/Network/generic.cxx
Normal file
1009
src/Network/generic.cxx
Normal file
File diff suppressed because it is too large
Load Diff
124
src/Network/generic.hxx
Normal file
124
src/Network/generic.hxx
Normal file
@@ -0,0 +1,124 @@
|
||||
// generic.hxx -- generic protocol class
|
||||
//
|
||||
// Written by Curtis Olson, started November 1999.
|
||||
//
|
||||
// Copyright (C) 1999 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
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "protocol.hxx"
|
||||
|
||||
|
||||
class FGGeneric : public FGProtocol {
|
||||
|
||||
public:
|
||||
|
||||
FGGeneric(vector<std::string>);
|
||||
~FGGeneric();
|
||||
|
||||
bool gen_message();
|
||||
bool parse_message_len(int length);
|
||||
|
||||
// open hailing frequencies
|
||||
bool open();
|
||||
|
||||
void reinit();
|
||||
|
||||
// process work for this port
|
||||
bool process();
|
||||
|
||||
// close the channel
|
||||
bool close();
|
||||
|
||||
void setExitOnError(bool val) { exitOnError = val; }
|
||||
bool getExitOnError() { return exitOnError; }
|
||||
bool getInitOk(void) { return initOk; }
|
||||
protected:
|
||||
|
||||
enum e_type { FG_BOOL=0, FG_INT, FG_FLOAT, FG_DOUBLE, FG_STRING, FG_FIXED, FG_BYTE, FG_WORD };
|
||||
|
||||
typedef struct {
|
||||
// std::string name;
|
||||
std::string format;
|
||||
e_type type;
|
||||
double offset;
|
||||
double factor;
|
||||
double min, max;
|
||||
bool wrap;
|
||||
bool rel;
|
||||
SGPropertyNode_ptr prop;
|
||||
} _serial_prot;
|
||||
|
||||
private:
|
||||
|
||||
std::string file_name;
|
||||
|
||||
int length;
|
||||
char buf[ FG_MAX_MSG_SIZE ];
|
||||
|
||||
std::string preamble;
|
||||
std::string postamble;
|
||||
std::string var_separator;
|
||||
std::string line_separator;
|
||||
std::string var_sep_string;
|
||||
std::string line_sep_string;
|
||||
vector<_serial_prot> _out_message;
|
||||
vector<_serial_prot> _in_message;
|
||||
|
||||
bool binary_mode;
|
||||
enum {FOOTER_NONE, FOOTER_LENGTH, FOOTER_MAGIC} binary_footer_type;
|
||||
int binary_footer_value;
|
||||
int binary_record_length;
|
||||
enum {BYTE_ORDER_NEEDS_CONVERSION, BYTE_ORDER_MATCHES_NETWORK_ORDER} binary_byte_order;
|
||||
|
||||
bool gen_message_ascii();
|
||||
bool gen_message_binary();
|
||||
bool parse_message_ascii(int length);
|
||||
bool parse_message_binary(int length);
|
||||
bool read_config(SGPropertyNode *root, vector<_serial_prot> &msg);
|
||||
bool exitOnError;
|
||||
bool initOk;
|
||||
|
||||
class FGProtocolWrapper * wrapper;
|
||||
|
||||
template<class T>
|
||||
static void updateValue(_serial_prot& prot, const T& val)
|
||||
{
|
||||
T new_val = (prot.rel ? getValue<T>(prot.prop) : 0)
|
||||
+ prot.offset
|
||||
+ prot.factor * val;
|
||||
|
||||
if( prot.max > prot.min )
|
||||
{
|
||||
if( prot.wrap )
|
||||
new_val = SGMisc<double>::normalizePeriodic(prot.min, prot.max, new_val);
|
||||
else
|
||||
new_val = SGMisc<T>::clip(new_val, prot.min, prot.max);
|
||||
}
|
||||
|
||||
setValue(prot.prop, new_val);
|
||||
}
|
||||
|
||||
// Special handling for bool (relative change = toggle, no min/max, no wrap)
|
||||
static void updateValue(_serial_prot& prot, bool val);
|
||||
};
|
||||
38
src/Network/http/CMakeLists.txt
Normal file
38
src/Network/http/CMakeLists.txt
Normal file
@@ -0,0 +1,38 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
httpd.cxx
|
||||
ScreenshotUriHandler.cxx
|
||||
PropertyUriHandler.cxx
|
||||
JsonUriHandler.cxx
|
||||
FlightHistoryUriHandler.cxx
|
||||
PkgUriHandler.cxx
|
||||
RunUriHandler.cxx
|
||||
MirrorPropertyTreeWebsocket.cxx
|
||||
NavdbUriHandler.cxx
|
||||
PropertyChangeWebsocket.cxx
|
||||
PropertyChangeObserver.cxx
|
||||
jsonprops.cxx
|
||||
SimpleDOM.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
urihandler.hxx
|
||||
httpd.hxx
|
||||
ScreenshotUriHandler.hxx
|
||||
PropertyUriHandler.hxx
|
||||
JsonUriHandler.hxx
|
||||
FlightHistoryUriHandler.hxx
|
||||
PkgUriHandler.hxx
|
||||
RunUriHandler.hxx
|
||||
NavdbUriHandler.hxx
|
||||
HTTPRequest.hxx
|
||||
Websocket.hxx
|
||||
PropertyChangeWebsocket.hxx
|
||||
PropertyChangeObserver.hxx
|
||||
MirrorPropertyTreeWebsocket.hxx
|
||||
jsonprops.hxx
|
||||
SimpleDOM.hxx
|
||||
)
|
||||
|
||||
flightgear_component(Http "${SOURCES}" "${HEADERS}")
|
||||
314
src/Network/http/FlightHistoryUriHandler.cxx
Normal file
314
src/Network/http/FlightHistoryUriHandler.cxx
Normal file
@@ -0,0 +1,314 @@
|
||||
// FlightHistoryUriHandler.cxx -- FlightHistory service
|
||||
//
|
||||
// Written by Torsten Dreyer, started February 2015.
|
||||
//
|
||||
// Copyright (C) 2015 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "FlightHistoryUriHandler.hxx"
|
||||
#include "SimpleDOM.hxx"
|
||||
#include <cJSON.h>
|
||||
|
||||
#include <Aircraft/FlightHistory.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <sstream>
|
||||
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
/*
|
||||
{
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
coordinates: [lon,lat,alt],..]
|
||||
},
|
||||
properties: {
|
||||
type: "FlightHistory"
|
||||
},
|
||||
}
|
||||
*/
|
||||
|
||||
static const char * errorPage =
|
||||
"<html><head><title>Flight History</title></head><body>"
|
||||
"<h1>Flight History</h1>"
|
||||
"Supported formats:"
|
||||
"<ul>"
|
||||
"<li><a href='track.json'>JSON</a></li>"
|
||||
"<li><a href='track.kml'>KML</a></li>"
|
||||
"</ul>"
|
||||
"</body></html>";
|
||||
|
||||
static string FlightHistoryToJson(const SGGeodVec & history, size_t last_seen ) {
|
||||
cJSON * feature = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(feature, "type", cJSON_CreateString("Feature"));
|
||||
|
||||
cJSON * lineString = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(feature, "geometry", lineString );
|
||||
|
||||
cJSON * properties = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(feature, "properties", properties );
|
||||
cJSON_AddItemToObject(properties, "type", cJSON_CreateString("FlightHistory"));
|
||||
cJSON_AddItemToObject(properties, "last", cJSON_CreateNumber(last_seen));
|
||||
|
||||
cJSON_AddItemToObject(lineString, "type", cJSON_CreateString("LineString"));
|
||||
cJSON * coordinates = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(lineString, "coordinates", coordinates);
|
||||
for (SGGeodVec::const_iterator it = history.begin(); it != history.end();
|
||||
++it) {
|
||||
cJSON * coordinate = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(coordinates, coordinate);
|
||||
|
||||
cJSON_AddItemToArray(coordinate, cJSON_CreateNumber(it->getLongitudeDeg()));
|
||||
cJSON_AddItemToArray(coordinate, cJSON_CreateNumber(it->getLatitudeDeg()));
|
||||
cJSON_AddItemToArray(coordinate, cJSON_CreateNumber(it->getElevationM()));
|
||||
|
||||
}
|
||||
|
||||
char * jsonString = cJSON_PrintUnformatted(feature);
|
||||
string reply(jsonString);
|
||||
free(jsonString);
|
||||
cJSON_Delete(lineString);
|
||||
return reply;
|
||||
}
|
||||
|
||||
static string AutoUpdateResponse(const HTTPRequest & request,
|
||||
const string & base, const string & interval) {
|
||||
|
||||
string url = "http://";
|
||||
url.append(request.HeaderVariables.get("Host")).append(base);
|
||||
|
||||
std::string reply("<?xml version='1.0' encoding='UTF-8'?>");
|
||||
DOMNode * kml = new DOMNode("kml");
|
||||
kml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
|
||||
|
||||
DOMNode * document = kml->addChild(new DOMNode("Document"));
|
||||
|
||||
DOMNode * networkLink = document->addChild(new DOMNode("NetworkLink"));
|
||||
DOMNode * link = networkLink->addChild(new DOMNode("Link"));
|
||||
link->addChild(new DOMNode("href"))->addChild(new DOMTextElement(url));
|
||||
link->addChild(new DOMNode("refreshMode"))->addChild(
|
||||
new DOMTextElement("onInterval"));
|
||||
link->addChild(new DOMNode("refreshInterval"))->addChild(
|
||||
new DOMTextElement(interval.empty() ? "10" : interval));
|
||||
|
||||
reply.append(kml->render());
|
||||
delete kml;
|
||||
return reply;
|
||||
}
|
||||
|
||||
static string FlightHistoryToKml(const SGGeodVec & history,
|
||||
const HTTPRequest & request) {
|
||||
string interval = request.RequestVariables.get("interval");
|
||||
if (!interval.empty()) {
|
||||
return AutoUpdateResponse(request, "/flighthistory/track.kml", interval);
|
||||
}
|
||||
|
||||
std::string reply("<?xml version='1.0' encoding='UTF-8'?>");
|
||||
DOMNode * kml = new DOMNode("kml");
|
||||
kml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
|
||||
|
||||
DOMNode * document = kml->addChild(new DOMNode("Document"));
|
||||
|
||||
document->addChild(new DOMNode("name"))->addChild(
|
||||
new DOMTextElement("FlightGear"));
|
||||
document->addChild(new DOMNode("description"))->addChild(
|
||||
new DOMTextElement("FlightGear Flight History"));
|
||||
|
||||
DOMNode * style = document->addChild(new DOMNode("Style"))->setAttribute(
|
||||
"id", "flight-history");
|
||||
DOMNode * lineStyle = style->addChild(new DOMNode("LineStyle"));
|
||||
|
||||
string lineColor = request.RequestVariables.get("LineColor");
|
||||
string lineWidth = request.RequestVariables.get("LineWidth");
|
||||
string polyColor = request.RequestVariables.get("PolyColor");
|
||||
|
||||
lineStyle->addChild(new DOMNode("color"))->addChild(
|
||||
new DOMTextElement(lineColor.empty() ? "427ebfff" : lineColor));
|
||||
lineStyle->addChild(new DOMNode("width"))->addChild(
|
||||
new DOMTextElement(lineWidth.empty() ? "4" : lineWidth));
|
||||
|
||||
lineStyle = style->addChild(new DOMNode("PolyStyle"));
|
||||
lineStyle->addChild(new DOMNode("color"))->addChild(
|
||||
new DOMTextElement(polyColor.empty() ? "fbfc4600" : polyColor));
|
||||
|
||||
DOMNode * placemark = document->addChild(new DOMNode("Placemark"));
|
||||
placemark->addChild(new DOMNode("name"))->addChild(
|
||||
new DOMTextElement("Flight Path"));
|
||||
placemark->addChild(new DOMNode("styleUrl"))->addChild(
|
||||
new DOMTextElement("#flight-history"));
|
||||
|
||||
DOMNode * linestring = placemark->addChild(new DOMNode("LineString"));
|
||||
linestring->addChild(new DOMNode("extrude"))->addChild(
|
||||
new DOMTextElement("1"));
|
||||
linestring->addChild(new DOMNode("tessalate"))->addChild(
|
||||
new DOMTextElement("1"));
|
||||
linestring->addChild(new DOMNode("altitudeMode"))->addChild(
|
||||
new DOMTextElement("absolute"));
|
||||
|
||||
stringstream ss;
|
||||
|
||||
for (SGGeodVec::const_iterator it = history.begin(); it != history.end();
|
||||
++it) {
|
||||
ss << (*it).getLongitudeDeg() << "," << (*it).getLatitudeDeg() << ","
|
||||
<< it->getElevationM() << " ";
|
||||
}
|
||||
|
||||
linestring->addChild(new DOMNode("coordinates"))->addChild(
|
||||
new DOMTextElement(ss.str()));
|
||||
|
||||
reply.append(kml->render());
|
||||
delete kml;
|
||||
return reply;
|
||||
}
|
||||
|
||||
static bool GetJsonDouble(cJSON * json, const char * item, double & out) {
|
||||
cJSON * cj = cJSON_GetObjectItem(json, item);
|
||||
if (NULL == cj)
|
||||
return false;
|
||||
|
||||
if (cj->type != cJSON_Number)
|
||||
return false;
|
||||
|
||||
out = cj->valuedouble;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool GetJsonBool(cJSON * json, const char * item, bool & out) {
|
||||
cJSON * cj = cJSON_GetObjectItem(json, item);
|
||||
if (NULL == cj)
|
||||
return false;
|
||||
|
||||
if (cj->type == cJSON_True) {
|
||||
out = true;
|
||||
return true;
|
||||
}
|
||||
if (cj->type == cJSON_False) {
|
||||
out = true;
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FlightHistoryUriHandler::handleRequest(const HTTPRequest & request,
|
||||
HTTPResponse & response, Connection * connection) {
|
||||
response.Header["Access-Control-Allow-Origin"] = "*";
|
||||
response.Header["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST";
|
||||
response.Header["Access-Control-Allow-Headers"] =
|
||||
"Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token";
|
||||
|
||||
if (request.Method == "OPTIONS") {
|
||||
return true; // OPTIONS only needs the headers
|
||||
}
|
||||
|
||||
FGFlightHistory* history =
|
||||
static_cast<FGFlightHistory*>(globals->get_subsystem("history"));
|
||||
|
||||
double minEdgeLengthM = 50;
|
||||
string requestPath = request.Uri.substr(getUri().length());
|
||||
|
||||
if (request.Method == "GET") {
|
||||
} else if (request.Method == "POST") {
|
||||
/*
|
||||
* {
|
||||
* sampleIntervalSec: (number),
|
||||
* maxMemoryUseBytes: (number),
|
||||
* clearOnTakeoff: (bool),
|
||||
* enabled: (bool),
|
||||
* }
|
||||
*/
|
||||
cJSON * json = cJSON_Parse(request.Content.c_str());
|
||||
if ( NULL != json) {
|
||||
double d = .0;
|
||||
bool b = false;
|
||||
bool doReinit = false;
|
||||
if (GetJsonDouble(json, "sampleIntervalSec", d)) {
|
||||
fgSetDouble("/sim/history/sample-interval-sec", d);
|
||||
doReinit = true;
|
||||
}
|
||||
if (GetJsonDouble(json, "maxMemoryUseBytes", d)) {
|
||||
fgSetDouble("/sim/history/max-memory-use-bytes", d);
|
||||
doReinit = true;
|
||||
}
|
||||
|
||||
if (GetJsonBool(json, "clearOnTakeoff", b)) {
|
||||
fgSetBool("/sim/history/clear-on-takeoff", b);
|
||||
doReinit = true;
|
||||
}
|
||||
if (GetJsonBool(json, "enabled", b)) {
|
||||
fgSetBool("/sim/history/enabled", b);
|
||||
}
|
||||
|
||||
if (doReinit) {
|
||||
history->reinit();
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
|
||||
} else {
|
||||
SG_LOG(SG_NETWORK, SG_INFO,
|
||||
"PkgUriHandler: invalid request method '" << request.Method << "'");
|
||||
response.Header["Allow"] = "OPTIONS, GET, POST";
|
||||
response.StatusCode = 405;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (requestPath == "track.kml") {
|
||||
response.Header["Content-Type"] =
|
||||
"application/vnd.google-earth.kml+xml; charset=UTF-8";
|
||||
|
||||
response.Content = FlightHistoryToKml(
|
||||
history->pathForHistory(minEdgeLengthM), request);
|
||||
|
||||
} else if (requestPath == "track.json") {
|
||||
size_t count = -1;
|
||||
try {
|
||||
count = std::stoul(request.RequestVariables.get("count"));
|
||||
}
|
||||
catch( ... ) {
|
||||
}
|
||||
size_t last = 0;
|
||||
try {
|
||||
last = std::stoul(request.RequestVariables.get("last"));
|
||||
}
|
||||
catch( ... ) {
|
||||
}
|
||||
|
||||
response.Header["Content-Type"] = "application/json; charset=UTF-8";
|
||||
PagedPathForHistory_ptr h = history->pagedPathForHistory( count, last );
|
||||
response.Content = FlightHistoryToJson( h->path, h->last_seen );
|
||||
|
||||
} else {
|
||||
response.Header["Content-Type"] = "text/html";
|
||||
response.Content = errorPage;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
40
src/Network/http/FlightHistoryUriHandler.hxx
Normal file
40
src/Network/http/FlightHistoryUriHandler.hxx
Normal file
@@ -0,0 +1,40 @@
|
||||
// FlightHistoryUriHandler.hxx -- FlightHistory service
|
||||
//
|
||||
// Written by Torsten Dreyer, started February 2015.
|
||||
//
|
||||
// Copyright (C) 2015 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_FLIGHTHISTORY_URI_HANDLER_HXX
|
||||
#define __FG_FLIGHTHISTORY_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class FlightHistoryUriHandler : public URIHandler {
|
||||
public:
|
||||
FlightHistoryUriHandler( const std::string& uri = "/flighthistory/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_FLIGHTHISTORY_URI_HANDLER_HXX
|
||||
65
src/Network/http/HTTPRequest.hxx
Normal file
65
src/Network/http/HTTPRequest.hxx
Normal file
@@ -0,0 +1,65 @@
|
||||
// HTTPRequest.hxx -- Wraps a http Request
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_HTTPREQUEST_HXX
|
||||
#define FG_HTTPREQUEST_HXX
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class HTTPRequest
|
||||
{
|
||||
public:
|
||||
HTTPRequest() {}
|
||||
virtual ~HTTPRequest() {}
|
||||
|
||||
std::string Method;
|
||||
std::string Uri;
|
||||
std::string HttpVersion;
|
||||
std::string QueryString;
|
||||
|
||||
std::string remoteAddress;
|
||||
int remotePort;
|
||||
std::string localAddress;
|
||||
int localPort;
|
||||
|
||||
std::string Content;
|
||||
|
||||
class StringMap : public std::map<std::string,std::string> {
|
||||
public:
|
||||
std::string get( const std::string & key ) const {
|
||||
const_iterator it = find( key );
|
||||
return it == end() ? "" : it->second;
|
||||
}
|
||||
};
|
||||
|
||||
StringMap RequestVariables;
|
||||
|
||||
StringMap HeaderVariables;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace flightgear
|
||||
|
||||
#endif // FG_HTTPREQUEST_HXX
|
||||
51
src/Network/http/HTTPResponse.hxx
Normal file
51
src/Network/http/HTTPResponse.hxx
Normal file
@@ -0,0 +1,51 @@
|
||||
// HTTPResponse.hxx -- Wraps a http Response
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef FG_HTTPRESPONSE_HXX
|
||||
#define FG_HTTPRESPONSE_HXX
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class HTTPResponse
|
||||
{
|
||||
public:
|
||||
HTTPResponse();
|
||||
virtual ~HTTPResponse() {}
|
||||
|
||||
int StatusCode;
|
||||
std::string Content;
|
||||
|
||||
typedef std::map<std::string,std::string> Header_t;
|
||||
Header_t Header;
|
||||
};
|
||||
|
||||
inline HTTPResponse::HTTPResponse() :
|
||||
StatusCode(200)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace flightgear
|
||||
|
||||
#endif // FG_HTTPRESPONSE_HXX
|
||||
111
src/Network/http/JsonUriHandler.cxx
Normal file
111
src/Network/http/JsonUriHandler.cxx
Normal file
@@ -0,0 +1,111 @@
|
||||
// JsonUriHandler.cxx -- json interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "JsonUriHandler.hxx"
|
||||
#include "jsonprops.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
bool JsonUriHandler::handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection )
|
||||
{
|
||||
response.Header["Content-Type"] = "application/json; charset=UTF-8";
|
||||
response.Header["Access-Control-Allow-Origin"] = "*";
|
||||
response.Header["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST";
|
||||
response.Header["Access-Control-Allow-Headers"] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token";
|
||||
|
||||
if( request.Method == "OPTIONS" ){
|
||||
return true; // OPTIONS only needs the headers
|
||||
}
|
||||
|
||||
if( request.Method == "GET" ){
|
||||
|
||||
// max recursion depth
|
||||
int depth = atoi(request.RequestVariables.get("d").c_str());
|
||||
if( depth < 1 ) depth = 1; // at least one level
|
||||
|
||||
// pretty print (y) or compact print (default)
|
||||
bool indent = request.RequestVariables.get("i") == "y";
|
||||
bool timestamp = request.RequestVariables.get("t") == "y";
|
||||
|
||||
SGPropertyNode_ptr node = getRequestedNode(request );
|
||||
if( !node.valid() ) {
|
||||
response.StatusCode = 404;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
response.Content = JSON::toJsonString( indent, node, depth, timestamp ? fgGetDouble("/sim/time/elapsed-sec") : -1.0 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( request.Method == "POST" ) {
|
||||
SGPropertyNode_ptr node = getRequestedNode(request );
|
||||
if( !node.valid() ) {
|
||||
response.StatusCode = 404;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "JsonUriHandler: setting property from'" << request.Content << "'" );
|
||||
cJSON * json = cJSON_Parse( request.Content.c_str() );
|
||||
if( NULL != json ) {
|
||||
JSON::toProp( json, node );
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "JsonUriHandler: invalid request method '" << request.Method << "'" );
|
||||
response.Header["Allow"] = "OPTIONS, GET, POST";
|
||||
response.StatusCode = 405;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr JsonUriHandler::getRequestedNode(const HTTPRequest & request)
|
||||
{
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "JsonUriHandler: request is '" << request.Uri << "'" );
|
||||
string propertyPath = request.Uri;
|
||||
propertyPath = propertyPath.substr( getUri().size() );
|
||||
|
||||
// skip trailing '/' - not very efficient but shouldn't happen too often
|
||||
while( !propertyPath.empty() && propertyPath[ propertyPath.length()-1 ] == '/' )
|
||||
propertyPath = propertyPath.substr(0,propertyPath.length()-1);
|
||||
|
||||
SGPropertyNode_ptr reply = fgGetNode( string("/") + propertyPath );
|
||||
if( !reply.valid() ) {
|
||||
SG_LOG(SG_NETWORK,SG_WARN, "JsonUriHandler: requested node not found: '" << propertyPath << "'");
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
41
src/Network/http/JsonUriHandler.hxx
Normal file
41
src/Network/http/JsonUriHandler.hxx
Normal file
@@ -0,0 +1,41 @@
|
||||
// JsonUriHandler.hxx -- json interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_JSON_URI_HANDLER_HXX
|
||||
#define __FG_JSON_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class JsonUriHandler : public URIHandler {
|
||||
public:
|
||||
JsonUriHandler( const std::string& uri = "/json/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
private:
|
||||
SGPropertyNode_ptr getRequestedNode(const HTTPRequest & request);
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_SCREENSHOT_URI_HANDLER_HXX
|
||||
542
src/Network/http/MirrorPropertyTreeWebsocket.cxx
Normal file
542
src/Network/http/MirrorPropertyTreeWebsocket.cxx
Normal file
@@ -0,0 +1,542 @@
|
||||
// MirrorPropertyTreeWebsocket.cxx -- A websocket for mirroring a property sub-tree
|
||||
//
|
||||
// Written by James Turner, started November 2016.
|
||||
//
|
||||
// Copyright (C) 2016 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "MirrorPropertyTreeWebsocket.hxx"
|
||||
#include "jsonprops.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <cJSON.h>
|
||||
|
||||
//#define MIRROR_DEBUG 1
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
using std::string;
|
||||
|
||||
typedef unsigned int PropertyId; // connection local property id
|
||||
|
||||
struct PropertyValue
|
||||
{
|
||||
PropertyValue(SGPropertyNode* cur = nullptr) :
|
||||
type(simgear::props::NONE)
|
||||
{
|
||||
if (!cur) {
|
||||
return;
|
||||
}
|
||||
|
||||
type = cur->getType();
|
||||
switch (type) {
|
||||
case simgear::props::INT:
|
||||
intValue = cur->getIntValue();
|
||||
break;
|
||||
|
||||
case simgear::props::BOOL:
|
||||
intValue = cur->getBoolValue();
|
||||
break;
|
||||
|
||||
case simgear::props::FLOAT:
|
||||
case simgear::props::DOUBLE:
|
||||
doubleValue = cur->getDoubleValue();
|
||||
break;
|
||||
|
||||
case simgear::props::STRING:
|
||||
case simgear::props::UNSPECIFIED:
|
||||
stringValue = cur->getStringValue();
|
||||
break;
|
||||
|
||||
case simgear::props::NONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
SG_LOG(SG_NETWORK, SG_DEV_ALERT, "MirrorPropTree PropertyValue : implement me!" << type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool equals(SGPropertyNode* node, const PropertyValue& other) const
|
||||
{
|
||||
if (other.type != type) return false;
|
||||
switch (type) {
|
||||
case simgear::props::INT:
|
||||
case simgear::props::BOOL:
|
||||
return intValue == other.intValue;
|
||||
|
||||
case simgear::props::FLOAT:
|
||||
case simgear::props::DOUBLE:
|
||||
return std::fabs(doubleValue - other.doubleValue) < 1e-4;
|
||||
|
||||
case simgear::props::STRING:
|
||||
case simgear::props::UNSPECIFIED:
|
||||
return stringValue == other.stringValue;
|
||||
|
||||
case simgear::props::NONE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
simgear::props::Type type;
|
||||
union {
|
||||
int intValue;
|
||||
double doubleValue;
|
||||
};
|
||||
std::string stringValue;
|
||||
};
|
||||
|
||||
|
||||
struct RemovedNode
|
||||
{
|
||||
RemovedNode(SGPropertyNode* node, unsigned int aId) :
|
||||
path(node->getPath()),
|
||||
id(aId)
|
||||
{}
|
||||
|
||||
std::string path;
|
||||
unsigned int id = 0;
|
||||
|
||||
bool operator==(const RemovedNode& other) const
|
||||
{
|
||||
return (path == other.path);
|
||||
}
|
||||
};
|
||||
|
||||
class MirrorTreeListener : public SGPropertyChangeListener
|
||||
{
|
||||
public:
|
||||
MirrorTreeListener() : SGPropertyChangeListener(true /* recursive */)
|
||||
{
|
||||
previousValues.resize(2);
|
||||
}
|
||||
|
||||
virtual ~MirrorTreeListener()
|
||||
{
|
||||
}
|
||||
|
||||
void valueChanged(SGPropertyNode* node) override
|
||||
{
|
||||
auto it = idHash.find(node);
|
||||
if (it == idHash.end()) {
|
||||
// not new to the server, but new to the client
|
||||
newNodes.insert(node);
|
||||
} else {
|
||||
assert(previousValues.size() > it->second);
|
||||
PropertyValue newVal(node);
|
||||
if (!previousValues[it->second].equals(node, newVal)) {
|
||||
previousValues[it->second] = newVal;
|
||||
changedNodes.insert(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void childAdded(SGPropertyNode* parent, SGPropertyNode* child) override
|
||||
{
|
||||
SG_UNUSED(parent);
|
||||
recursiveAdd(child);
|
||||
}
|
||||
|
||||
void recursiveAdd(SGPropertyNode* node)
|
||||
{
|
||||
RemovedNode r(node, 0 /* id not actually used */);
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "looking for RR:" << r.path);
|
||||
#endif
|
||||
auto rrIt = std::find(recentlyRemoved.begin(), recentlyRemoved.end(), r);
|
||||
if (rrIt != recentlyRemoved.end()) {
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "recycling node:" << node->getPath());
|
||||
#endif
|
||||
const auto id = rrIt->id;
|
||||
// recycle nodes which get thrashed from Nasal (deleted + re-created
|
||||
// each time a Nasal timer fires)
|
||||
removedNodes.erase(id); // don't remove it!
|
||||
idHash.insert(std::make_pair(node, id));
|
||||
|
||||
// we can still do change compression here, but this also
|
||||
// deals with type mutation when removing + re-adding with a
|
||||
// different type
|
||||
PropertyValue newVal(node);
|
||||
if (!previousValues[id].equals(node, newVal)) {
|
||||
previousValues[id] = newVal;
|
||||
changedNodes.insert(node);
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "\tand will actually change" << node->getPath());
|
||||
#endif
|
||||
}
|
||||
|
||||
recentlyRemoved.erase(rrIt);
|
||||
return;
|
||||
}
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "new node:" << node->getPath());
|
||||
#endif
|
||||
newNodes.insert(node);
|
||||
int child = 0;
|
||||
for (; child < node->nChildren(); ++child) {
|
||||
recursiveAdd(node->getChild(child));
|
||||
}
|
||||
}
|
||||
|
||||
void childRemoved(SGPropertyNode* parent, SGPropertyNode* child) override
|
||||
{
|
||||
changedNodes.erase(child); // have to do this here with the pointer valid
|
||||
newNodes.erase(child);
|
||||
|
||||
auto it = idHash.find(child);
|
||||
if (it != idHash.end()) {
|
||||
removedNodes.insert(it->second);
|
||||
idHash.erase(it);
|
||||
// record so we can map removed+add of the same property into
|
||||
// a simple value change (this happens commonly with the canvas
|
||||
// due to lazy Nasal scripting)
|
||||
recentlyRemoved.emplace_back(child, it->second);
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "adding RR:" << recentlyRemoved.back().path);
|
||||
#endif
|
||||
}
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "saw remove of:" << child->getPath());
|
||||
#endif
|
||||
}
|
||||
|
||||
void registerSubtree(SGPropertyNode* node)
|
||||
{
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "register subtree:" << node->getPath());
|
||||
#endif
|
||||
valueChanged(node);
|
||||
|
||||
// and recurse
|
||||
int child = 0;
|
||||
for (; child < node->nChildren(); ++child) {
|
||||
registerSubtree(node->getChild(child));
|
||||
}
|
||||
}
|
||||
|
||||
std::set<SGPropertyNode*> newNodes;
|
||||
std::set<SGPropertyNode*> changedNodes;
|
||||
std::set<PropertyId> removedNodes;
|
||||
|
||||
PropertyId idForProperty(SGPropertyNode* prop)
|
||||
{
|
||||
auto it = idHash.find(prop);
|
||||
if (it == idHash.end()) {
|
||||
it = idHash.insert(it, std::make_pair(prop, nextPropertyId++));
|
||||
previousValues.push_back(PropertyValue(prop));
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
cJSON* makeJSONData()
|
||||
{
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SGTimeStamp st;
|
||||
st.stamp();
|
||||
|
||||
int newSize = newNodes.size();
|
||||
int changedSize = changedNodes.size();
|
||||
int removedSize = removedNodes.size();
|
||||
#endif
|
||||
cJSON* result = cJSON_CreateObject();
|
||||
if (!newNodes.empty()) {
|
||||
cJSON * newNodesArray = cJSON_CreateArray();
|
||||
|
||||
// cJSON_AddItemToArray performance is O(N) due to use of a linked
|
||||
// list, which dominates the performance here. To fix this we maintan
|
||||
// a point to the tail of the array, keeping appends O(1)
|
||||
cJSON* arrayTail = nullptr;
|
||||
|
||||
for (auto prop : newNodes) {
|
||||
changedNodes.erase(prop); // avoid duplicate send
|
||||
cJSON* newPropData = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(newPropData, "path", cJSON_CreateString(prop->getPath(true).c_str()));
|
||||
cJSON_AddItemToObject(newPropData, "type", cJSON_CreateString(JSON::getPropertyTypeString(prop->getType())));
|
||||
cJSON_AddItemToObject(newPropData, "index", cJSON_CreateNumber(prop->getIndex()));
|
||||
cJSON_AddItemToObject(newPropData, "position", cJSON_CreateNumber(prop->getPosition()));
|
||||
cJSON_AddItemToObject(newPropData, "id", cJSON_CreateNumber(idForProperty(prop)));
|
||||
if (prop->getType() != simgear::props::NONE) {
|
||||
cJSON_AddItemToObject(newPropData, "value", JSON::valueToJson(prop));
|
||||
}
|
||||
|
||||
if (arrayTail) {
|
||||
arrayTail->next = newPropData;
|
||||
newPropData->prev = arrayTail;
|
||||
arrayTail = newPropData;
|
||||
} else {
|
||||
cJSON_AddItemToArray(newNodesArray, newPropData);
|
||||
arrayTail = newPropData;
|
||||
}
|
||||
}
|
||||
|
||||
newNodes.clear();
|
||||
cJSON_AddItemToObject(result, "created", newNodesArray);
|
||||
}
|
||||
|
||||
|
||||
if (!removedNodes.empty()) {
|
||||
cJSON * deletedNodesArray = cJSON_CreateArray();
|
||||
for (auto propId : removedNodes) {
|
||||
cJSON_AddItemToArray(deletedNodesArray, cJSON_CreateNumber(propId));
|
||||
}
|
||||
cJSON_AddItemToObject(result, "removed", deletedNodesArray);
|
||||
removedNodes.clear();
|
||||
}
|
||||
|
||||
if (!changedNodes.empty()) {
|
||||
cJSON * changedNodesArray = cJSON_CreateArray();
|
||||
|
||||
// see comment above about cJSON_AddItemToArray
|
||||
cJSON* tail = nullptr;
|
||||
|
||||
for (auto prop : changedNodes) {
|
||||
cJSON* propData = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(propData, cJSON_CreateNumber(idForProperty(prop)));
|
||||
cJSON_AddItemToArray(propData, JSON::valueToJson(prop));
|
||||
|
||||
|
||||
if (tail) {
|
||||
tail->next = propData;
|
||||
propData->prev = tail;
|
||||
tail = propData;
|
||||
} else {
|
||||
cJSON_AddItemToArray(changedNodesArray, propData);
|
||||
tail = propData;
|
||||
}
|
||||
}
|
||||
|
||||
changedNodes.clear();
|
||||
cJSON_AddItemToObject(result, "changed", changedNodesArray);
|
||||
}
|
||||
#if defined (MIRROR_DEBUG)
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "making JSON data took:" << st.elapsedMSec() << " for " << newSize << "/" << changedSize << "/" << removedSize);
|
||||
#endif
|
||||
recentlyRemoved.clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool haveChangesToSend() const
|
||||
{
|
||||
return !newNodes.empty() || !changedNodes.empty() || !removedNodes.empty();
|
||||
}
|
||||
private:
|
||||
PropertyId nextPropertyId = 1;
|
||||
std::unordered_map<SGPropertyNode*, PropertyId> idHash;
|
||||
std::vector<PropertyValue> previousValues;
|
||||
|
||||
/// track recently removed nodes in case they are re-created imemdiately
|
||||
/// after with the same type, since we can make this much more efficient
|
||||
/// when sending over the wire.
|
||||
std::vector<RemovedNode> recentlyRemoved;
|
||||
};
|
||||
|
||||
#if 0
|
||||
|
||||
static void handleSetCommand(const string_list& nodes, cJSON* json, WebsocketWriter &writer)
|
||||
{
|
||||
cJSON * value = cJSON_GetObjectItem(json, "value");
|
||||
if ( NULL != value ) {
|
||||
if (nodes.size() > 1) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: WS set: insufficent values for nodes:" << nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr n = fgGetNode(nodes.front());
|
||||
if (!n) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: set '" << nodes.front() << "' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
setPropertyFromJson(n, value);
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON * values = cJSON_GetObjectItem(json, "values");
|
||||
if ( ( NULL == values ) || ( static_cast<size_t>(cJSON_GetArraySize(values)) != nodes.size()) ) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: WS set: mismatched nodes/values sizes:" << nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
string_list::const_iterator it;
|
||||
int i=0;
|
||||
for (it = nodes.begin(); it != nodes.end(); ++it, ++i) {
|
||||
SGPropertyNode_ptr n = fgGetNode(*it);
|
||||
if (!n) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
setPropertyFromJson(n, cJSON_GetArrayItem(values, i));
|
||||
} // of nodes iteration
|
||||
}
|
||||
|
||||
static void handleExecCommand(cJSON* json)
|
||||
{
|
||||
cJSON* name = cJSON_GetObjectItem(json, "fgcommand");
|
||||
if ((NULL == name )|| (NULL == name->valuestring)) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: exec: no fgcommand name");
|
||||
return;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr arg(new SGPropertyNode);
|
||||
JSON::addChildrenToProp( json, arg );
|
||||
|
||||
globals->get_commands()->execute(name->valuestring, arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
MirrorPropertyTreeWebsocket::MirrorPropertyTreeWebsocket(const std::string& path) :
|
||||
_rootPath(path),
|
||||
_listener(new MirrorTreeListener),
|
||||
_minSendInterval(100)
|
||||
{
|
||||
checkNodeExists();
|
||||
}
|
||||
|
||||
MirrorPropertyTreeWebsocket::~MirrorPropertyTreeWebsocket()
|
||||
{
|
||||
}
|
||||
|
||||
void MirrorPropertyTreeWebsocket::close()
|
||||
{
|
||||
if (_subtreeRoot) {
|
||||
_subtreeRoot->removeChangeListener(_listener.get());
|
||||
}
|
||||
}
|
||||
|
||||
void MirrorPropertyTreeWebsocket::checkNodeExists()
|
||||
{
|
||||
_subtreeRoot = globals->get_props()->getNode(_rootPath, false);
|
||||
if (_subtreeRoot) {
|
||||
_subtreeRoot->addChangeListener(_listener.get());
|
||||
_listener->registerSubtree(_subtreeRoot);
|
||||
_lastSendTime = SGTimeStamp::now();
|
||||
}
|
||||
}
|
||||
|
||||
void MirrorPropertyTreeWebsocket::handleRequest(const HTTPRequest & request, WebsocketWriter &writer)
|
||||
{
|
||||
if (!_subtreeRoot) {
|
||||
checkNodeExists();
|
||||
if (!_subtreeRoot) {
|
||||
return; // still no node exists, we can't process this
|
||||
}
|
||||
}
|
||||
|
||||
if (request.Content.empty()) return;
|
||||
#if 0
|
||||
/*
|
||||
* allowed JSON is
|
||||
{
|
||||
command : 'addListener',
|
||||
nodes : [
|
||||
'/bar/baz',
|
||||
'/foo/bar'
|
||||
],
|
||||
node: '/bax/foo'
|
||||
}
|
||||
*/
|
||||
cJSON * json = cJSON_Parse(request.Content.c_str());
|
||||
if ( NULL != json) {
|
||||
string command;
|
||||
cJSON * j = cJSON_GetObjectItem(json, "command");
|
||||
if ( NULL != j && NULL != j->valuestring) {
|
||||
command = j->valuestring;
|
||||
}
|
||||
|
||||
// handle a single node name, or an array of them
|
||||
string_list nodeNames;
|
||||
j = cJSON_GetObjectItem(json, "node");
|
||||
if ( NULL != j && NULL != j->valuestring) {
|
||||
nodeNames.push_back(simgear::strutils::strip(string(j->valuestring)));
|
||||
}
|
||||
|
||||
cJSON * nodes = cJSON_GetObjectItem(json, "nodes");
|
||||
if ( NULL != nodes) {
|
||||
for (int i = 0; i < cJSON_GetArraySize(nodes); i++) {
|
||||
cJSON * node = cJSON_GetArrayItem(nodes, i);
|
||||
if ( NULL == node) continue;
|
||||
if ( NULL == node->valuestring) continue;
|
||||
nodeNames.push_back(simgear::strutils::strip(string(node->valuestring)));
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "get") {
|
||||
handleGetCommand(nodeNames, writer);
|
||||
} else if (command == "set") {
|
||||
handleSetCommand(nodeNames, json, writer);
|
||||
} else if (command == "exec") {
|
||||
handleExecCommand(json);
|
||||
} else {
|
||||
string_list::const_iterator it;
|
||||
for (it = nodeNames.begin(); it != nodeNames.end(); ++it) {
|
||||
_watchedNodes.handleCommand(command, *it, _propertyChangeObserver);
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void MirrorPropertyTreeWebsocket::poll(WebsocketWriter & writer)
|
||||
{
|
||||
if (!_subtreeRoot) {
|
||||
checkNodeExists();
|
||||
if (!_subtreeRoot) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_listener->haveChangesToSend()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_lastSendTime.elapsedMSec() < _minSendInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
// okay, we will send now, update the send stamp
|
||||
_lastSendTime.stamp();
|
||||
|
||||
cJSON * json = _listener->makeJSONData();
|
||||
char * jsonString = cJSON_PrintUnformatted( json );
|
||||
writer.writeText( jsonString );
|
||||
free( jsonString );
|
||||
cJSON_Delete( json );
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
62
src/Network/http/MirrorPropertyTreeWebsocket.hxx
Normal file
62
src/Network/http/MirrorPropertyTreeWebsocket.hxx
Normal file
@@ -0,0 +1,62 @@
|
||||
// MirrorPropertyTreeWebsocket.hxx -- A websocket for mirroring a property sub-tree
|
||||
//
|
||||
// Written by James Turner, started November 2016.
|
||||
//
|
||||
// Copyright (C) 2016 James Turner
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef MIRROR_PROP_TREE_WEBSOCKET_HXX_
|
||||
#define MIRROR_PROP_TREE_WEBSOCKET_HXX_
|
||||
|
||||
#include "Websocket.hxx"
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/timing/timestamp.hxx>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class MirrorTreeListener;
|
||||
|
||||
class MirrorPropertyTreeWebsocket : public Websocket
|
||||
{
|
||||
public:
|
||||
MirrorPropertyTreeWebsocket(const std::string& path);
|
||||
~MirrorPropertyTreeWebsocket() override;
|
||||
|
||||
void close() override;
|
||||
void handleRequest(const HTTPRequest & request, WebsocketWriter & writer) override;
|
||||
void poll(WebsocketWriter & writer) override;
|
||||
|
||||
private:
|
||||
void checkNodeExists();
|
||||
|
||||
friend class MirrorTreeListener;
|
||||
|
||||
std::string _rootPath;
|
||||
SGPropertyNode_ptr _subtreeRoot;
|
||||
std::unique_ptr<MirrorTreeListener> _listener;
|
||||
int _minSendInterval;
|
||||
SGTimeStamp _lastSendTime;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* MIRROR_PROP_TREE_WEBSOCKET_HXX_ */
|
||||
382
src/Network/http/NavdbUriHandler.cxx
Normal file
382
src/Network/http/NavdbUriHandler.cxx
Normal file
@@ -0,0 +1,382 @@
|
||||
// NavdbUriHandler.cxx -- Access the nav database
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "NavdbUriHandler.hxx"
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <Navaids/navrecord.hxx>
|
||||
#include <Airports/airport.hxx>
|
||||
#include <ATC/CommStation.hxx>
|
||||
#include <cJSON.h>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
static cJSON * createPositionArray(double x, double y, double z)
|
||||
{
|
||||
cJSON * p = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(p, cJSON_CreateNumber(x));
|
||||
cJSON_AddItemToArray(p, cJSON_CreateNumber(y));
|
||||
cJSON_AddItemToArray(p, cJSON_CreateNumber(z));
|
||||
return p;
|
||||
}
|
||||
|
||||
static cJSON * createPositionArray(double x, double y)
|
||||
{
|
||||
cJSON * p = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(p, cJSON_CreateNumber(x));
|
||||
cJSON_AddItemToArray(p, cJSON_CreateNumber(y));
|
||||
return p;
|
||||
}
|
||||
|
||||
static cJSON * createLOCGeometry(FGNavRecord * navRecord)
|
||||
{
|
||||
assert( navRecord != NULL );
|
||||
|
||||
cJSON * geometry = cJSON_CreateObject();
|
||||
int range = navRecord->get_range();
|
||||
|
||||
double width = navRecord->localizerWidth();
|
||||
double course = navRecord->get_multiuse();
|
||||
|
||||
double px[4];
|
||||
double py[4];
|
||||
|
||||
px[0] = navRecord->longitude();
|
||||
py[0] = navRecord->latitude();
|
||||
|
||||
for (int i = -1; i <= +1; i++) {
|
||||
double c = SGMiscd::normalizeAngle((course + 180 + i * width / 2) * SG_DEGREES_TO_RADIANS);
|
||||
SGGeoc geoc = SGGeoc::fromGeod(navRecord->geod());
|
||||
SGGeod p2 = SGGeod::fromGeoc(geoc.advanceRadM(c, range * SG_NM_TO_METER));
|
||||
px[i + 2] = p2.getLongitudeDeg();
|
||||
py[i + 2] = p2.getLatitudeDeg();
|
||||
}
|
||||
// Add three lines: centerline, left and right edge
|
||||
cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("MultiLineString"));
|
||||
cJSON * coordinates = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(geometry, "coordinates", coordinates);
|
||||
for (int i = 1; i < 4; i++) {
|
||||
cJSON * line = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray(coordinates, line);
|
||||
cJSON_AddItemToArray(line, createPositionArray(px[0], py[0]));
|
||||
cJSON_AddItemToArray(line, createPositionArray(px[i], py[i]));
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
static cJSON * createPointGeometry(FGPositioned * positioned )
|
||||
{
|
||||
cJSON * geometry = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("Point"));
|
||||
cJSON_AddItemToObject(geometry, "coordinates",
|
||||
createPositionArray(positioned ->longitude(), positioned->latitude(), positioned->elevationM()));
|
||||
return geometry;
|
||||
}
|
||||
|
||||
static cJSON * createRunwayPolygon( FGRunwayBase * rwy )
|
||||
{
|
||||
cJSON * polygon = cJSON_CreateObject();
|
||||
cJSON_AddItemToObject(polygon, "type", cJSON_CreateString("Polygon"));
|
||||
cJSON * coordinates = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(polygon, "coordinates", coordinates );
|
||||
cJSON * linearRing = cJSON_CreateArray();
|
||||
cJSON_AddItemToArray( coordinates, linearRing );
|
||||
|
||||
// compute the four corners of the runway
|
||||
SGGeod p1 = rwy->pointOffCenterline( 0.0, rwy->widthM()/2 );
|
||||
SGGeod p2 = rwy->pointOffCenterline( 0.0, -rwy->widthM()/2 );
|
||||
SGGeod p3 = rwy->pointOffCenterline( rwy->lengthM(), -rwy->widthM()/2 );
|
||||
SGGeod p4 = rwy->pointOffCenterline( rwy->lengthM(), rwy->widthM()/2 );
|
||||
cJSON_AddItemToArray( linearRing, createPositionArray(p1.getLongitudeDeg(), p1.getLatitudeDeg()) );
|
||||
cJSON_AddItemToArray( linearRing, createPositionArray(p2.getLongitudeDeg(), p2.getLatitudeDeg()) );
|
||||
cJSON_AddItemToArray( linearRing, createPositionArray(p3.getLongitudeDeg(), p3.getLatitudeDeg()) );
|
||||
cJSON_AddItemToArray( linearRing, createPositionArray(p4.getLongitudeDeg(), p4.getLatitudeDeg()) );
|
||||
// close the ring
|
||||
cJSON_AddItemToArray( linearRing, createPositionArray(p1.getLongitudeDeg(), p1.getLatitudeDeg()) );
|
||||
return polygon;
|
||||
}
|
||||
|
||||
static cJSON * createAirportGeometry(FGAirport * airport )
|
||||
{
|
||||
assert( airport != NULL );
|
||||
FGRunwayList runways = airport->getRunwaysWithoutReciprocals();
|
||||
|
||||
if( runways.empty() ) {
|
||||
// no runways? Create a Point geometry
|
||||
return createPointGeometry( airport );
|
||||
}
|
||||
|
||||
cJSON * geometry = cJSON_CreateObject();
|
||||
|
||||
// if there are runways, create a geometry collection
|
||||
cJSON_AddItemToObject(geometry, "type", cJSON_CreateString("GeometryCollection"));
|
||||
cJSON * geometryCollection = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(geometry, "geometries", geometryCollection);
|
||||
|
||||
// the first item is the aerodrome reference point
|
||||
cJSON_AddItemToArray( geometryCollection, createPointGeometry(airport) );
|
||||
|
||||
// followed by the runway polygons
|
||||
for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) {
|
||||
cJSON_AddItemToArray( geometryCollection, createRunwayPolygon(*it) );
|
||||
}
|
||||
|
||||
FGTaxiwayList taxiways = airport->getTaxiways();
|
||||
// followed by the taxiway polygons
|
||||
for( FGTaxiwayList::iterator it = taxiways.begin(); it != taxiways.end(); ++it ) {
|
||||
cJSON_AddItemToArray( geometryCollection, createRunwayPolygon(*it) );
|
||||
}
|
||||
|
||||
return geometry;
|
||||
}
|
||||
|
||||
static cJSON * createGeometryFor(FGPositioned * positioned)
|
||||
{
|
||||
switch( positioned->type() ) {
|
||||
case FGPositioned::LOC:
|
||||
case FGPositioned::ILS:
|
||||
return createLOCGeometry( dynamic_cast<FGNavRecord*>(positioned) );
|
||||
|
||||
case FGPositioned::AIRPORT:
|
||||
return createAirportGeometry( dynamic_cast<FGAirport*>(positioned) );
|
||||
|
||||
default:
|
||||
return createPointGeometry( positioned );
|
||||
}
|
||||
}
|
||||
|
||||
static void addAirportProperties(cJSON * json, FGAirport * airport )
|
||||
{
|
||||
if( NULL == airport ) return;
|
||||
double longestRunwayLength = 0.0;
|
||||
double longestRunwayHeading = 0.0;
|
||||
const char * longestRunwaySurface = "";
|
||||
|
||||
cJSON_AddItemToObject(json, "name", cJSON_CreateString(airport->getName().c_str()));
|
||||
cJSON * runwaysJson = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(json, "runways", runwaysJson);
|
||||
|
||||
FGRunwayList runways = airport->getRunways();
|
||||
for( FGRunwayList::iterator it = runways.begin(); it != runways.end(); ++it ) {
|
||||
FGRunway * runway = *it;
|
||||
cJSON * runwayJson = cJSON_CreateObject();
|
||||
cJSON_AddItemToArray( runwaysJson, runwayJson );
|
||||
cJSON_AddItemToObject(runwayJson, "id", cJSON_CreateString(runway->ident().c_str()));
|
||||
cJSON_AddItemToObject(runwayJson, "length_m", cJSON_CreateNumber(runway->lengthM()));
|
||||
cJSON_AddItemToObject(runwayJson, "width_m", cJSON_CreateNumber(runway->widthM()));
|
||||
cJSON_AddItemToObject(runwayJson, "surface", cJSON_CreateString(runway->surfaceName()));
|
||||
cJSON_AddItemToObject(runwayJson, "heading_deg", cJSON_CreateNumber(runway->headingDeg()));
|
||||
double d = runway->displacedThresholdM();
|
||||
if( d > .0 )
|
||||
cJSON_AddItemToObject(runwayJson, "dispacedThreshold_m", cJSON_CreateNumber(d));
|
||||
|
||||
d = runway->stopwayM();
|
||||
if( d > .0 )
|
||||
cJSON_AddItemToObject(runwayJson, "stopway_m", cJSON_CreateNumber(d));
|
||||
|
||||
if( runway->lengthM() > longestRunwayLength ) {
|
||||
longestRunwayLength = runway->lengthM();
|
||||
longestRunwayHeading = runway->headingDeg();
|
||||
longestRunwaySurface = runway->surfaceName();
|
||||
}
|
||||
}
|
||||
cJSON_AddItemToObject(json, "longestRwyLength_m", cJSON_CreateNumber(longestRunwayLength));
|
||||
cJSON_AddItemToObject(json, "longestRwyHeading_deg", cJSON_CreateNumber(longestRunwayHeading));
|
||||
cJSON_AddItemToObject(json, "longestRwySurface", cJSON_CreateString(longestRunwaySurface));
|
||||
if( airport->getMetar() ) {
|
||||
cJSON_AddItemToObject(json, "metar", cJSON_CreateTrue());
|
||||
}
|
||||
|
||||
cJSON * commsJson = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(json, "comm", commsJson);
|
||||
flightgear::CommStationList comms = airport->commStations();
|
||||
for( flightgear::CommStationList::iterator it = comms.begin(); it != comms.end(); ++it ) {
|
||||
flightgear::CommStation * comm = *it;
|
||||
cJSON * commJson = cJSON_CreateObject();
|
||||
cJSON_AddItemToArray( commsJson, commJson );
|
||||
cJSON_AddItemToObject(commJson, "id", cJSON_CreateString(comm->ident().c_str()));
|
||||
cJSON_AddItemToObject(commJson, "mhz", cJSON_CreateNumber(comm->freqMHz()));
|
||||
}
|
||||
}
|
||||
|
||||
static void addNAVProperties(cJSON * json, FGNavRecord * navRecord )
|
||||
{
|
||||
if( NULL == navRecord ) return;
|
||||
cJSON_AddItemToObject(json, "range_nm", cJSON_CreateNumber(navRecord->get_range()));
|
||||
cJSON_AddItemToObject(json, "frequency", cJSON_CreateNumber((double) navRecord->get_freq() / 100.0));
|
||||
switch (navRecord->type()) {
|
||||
case FGPositioned::ILS:
|
||||
case FGPositioned::LOC:
|
||||
cJSON_AddItemToObject(json, "localizer-course", cJSON_CreateNumber(navRecord->get_multiuse()));
|
||||
break;
|
||||
|
||||
case FGPositioned::VOR:
|
||||
cJSON_AddItemToObject(json, "variation", cJSON_CreateNumber(navRecord->get_multiuse()));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static cJSON * createPropertiesFor(FGPositioned * positioned)
|
||||
{
|
||||
cJSON * properties = cJSON_CreateObject();
|
||||
|
||||
cJSON_AddItemToObject(properties, "name", cJSON_CreateString(positioned->name().c_str()));
|
||||
// also add id to properties
|
||||
cJSON_AddItemToObject(properties, "id", cJSON_CreateString(positioned->ident().c_str()));
|
||||
cJSON_AddItemToObject(properties, "type", cJSON_CreateString(positioned->typeString()));
|
||||
cJSON_AddItemToObject(properties, "elevation-m", cJSON_CreateNumber(positioned->elevationM()));
|
||||
addNAVProperties( properties, dynamic_cast<FGNavRecord*>(positioned) );
|
||||
addAirportProperties( properties, dynamic_cast<FGAirport*>(positioned) );
|
||||
return properties;
|
||||
}
|
||||
|
||||
static cJSON * createFeatureFor(FGPositioned * positioned)
|
||||
{
|
||||
cJSON * feature = cJSON_CreateObject();
|
||||
|
||||
// A GeoJSON object with the type "Feature" is a feature object.
|
||||
cJSON_AddItemToObject(feature, "type", cJSON_CreateString("Feature"));
|
||||
|
||||
// A feature object must have a member with the name "geometry".
|
||||
// The value of the geometry member is a geometry object as defined above or a JSON null value.
|
||||
cJSON_AddItemToObject(feature, "geometry", createGeometryFor(positioned));
|
||||
|
||||
// A feature object must have a member with the name "properties".
|
||||
// The value of the properties member is an object (any JSON object or a JSON null value).
|
||||
cJSON_AddItemToObject(feature, "properties", createPropertiesFor(positioned));
|
||||
|
||||
// If a feature has a commonly used identifier, that identifier should be included
|
||||
// as a member of the feature object with the name "id".
|
||||
cJSON_AddItemToObject(feature, "id", cJSON_CreateString(positioned->ident().c_str()));
|
||||
|
||||
return feature;
|
||||
}
|
||||
|
||||
bool NavdbUriHandler::handleRequest(const HTTPRequest & request, HTTPResponse & response, Connection * connection)
|
||||
{
|
||||
|
||||
response.Header["Content-Type"] = "application/json; charset=UTF-8";
|
||||
response.Header["Access-Control-Allow-Origin"] = "*";
|
||||
response.Header["Access-Control-Allow-Methods"] = "OPTIONS, GET";
|
||||
response.Header["Access-Control-Allow-Headers"] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token";
|
||||
|
||||
if( request.Method == "OPTIONS" ){
|
||||
return true; // OPTIONS only needs the headers
|
||||
}
|
||||
|
||||
if( request.Method != "GET" ){
|
||||
response.Header["Allow"] = "OPTIONS, GET";
|
||||
response.StatusCode = 405;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool indent = request.RequestVariables.get("i") == "y";
|
||||
|
||||
string query = request.RequestVariables.get("q");
|
||||
FGPositionedList result;
|
||||
|
||||
if (query == "findWithinRange") {
|
||||
// ?q=findWithinRange&lat=53.5&lon=10.0&range=100&type=vor,ils
|
||||
|
||||
double lat, lon, range = -1;
|
||||
try {
|
||||
lat = std::stod(request.RequestVariables.get("lat"));
|
||||
lon = std::stod(request.RequestVariables.get("lon"));
|
||||
range = std::stod(request.RequestVariables.get("range"));
|
||||
}
|
||||
catch (...) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (range <= 1.0) goto fail;
|
||||
// In remembrance of a famous bug
|
||||
|
||||
SGGeod pos = SGGeod::fromDeg(lon, lat);
|
||||
FGPositioned::TypeFilter filter;
|
||||
try {
|
||||
filter = FGPositioned::TypeFilter::fromString(request.RequestVariables.get("type"));
|
||||
}
|
||||
catch (...) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
result = FGPositioned::findWithinRange(pos, range, &filter);
|
||||
} else if (query == "airports") {
|
||||
cJSON * json = cJSON_CreateArray();
|
||||
for( char ** airports = FGAirport::searchNamesAndIdents(""); *airports; airports++ ) {
|
||||
cJSON_AddItemToArray(json, cJSON_CreateString(*airports));
|
||||
}
|
||||
char * jsonString = indent ? cJSON_Print(json) : cJSON_PrintUnformatted(json);
|
||||
cJSON_Delete(json);
|
||||
response.Content = jsonString;
|
||||
free(jsonString);
|
||||
return true;
|
||||
|
||||
} else if (query == "airport") {
|
||||
FGAirportRef airport = FGAirport::findByIdent(request.RequestVariables.get("id"));
|
||||
if( airport.valid() )
|
||||
result.push_back( airport );
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
{ // create some GeoJSON from the result list
|
||||
// GeoJSON always consists of a single object.
|
||||
cJSON * geoJSON = cJSON_CreateObject();
|
||||
|
||||
// The GeoJSON object must have a member with the name "type".
|
||||
// This member's value is a string that determines the type of the GeoJSON object.
|
||||
cJSON_AddItemToObject(geoJSON, "type", cJSON_CreateString("FeatureCollection"));
|
||||
|
||||
// we send zero to many features - let's make it a FeatureCollection
|
||||
// A GeoJSON object with the type "FeatureCollection" is a feature collection object.
|
||||
// An object of type "FeatureCollection" must have a member with the name "features".
|
||||
// The value corresponding to "features" is an array.
|
||||
cJSON * featureCollection = cJSON_CreateArray();
|
||||
cJSON_AddItemToObject(geoJSON, "features", featureCollection);
|
||||
|
||||
for (FGPositionedList::iterator it = result.begin(); it != result.end(); ++it) {
|
||||
// Each element in the array is a feature object as defined above.
|
||||
cJSON_AddItemToArray(featureCollection, createFeatureFor(*it));
|
||||
}
|
||||
|
||||
char * jsonString = indent ? cJSON_Print(geoJSON) : cJSON_PrintUnformatted(geoJSON);
|
||||
cJSON_Delete(geoJSON);
|
||||
response.Content = jsonString;
|
||||
free(jsonString);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
fail: response.StatusCode = 400;
|
||||
response.Content = "{ 'error': 'bad request' }";
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
38
src/Network/http/NavdbUriHandler.hxx
Normal file
38
src/Network/http/NavdbUriHandler.hxx
Normal file
@@ -0,0 +1,38 @@
|
||||
// NavdbUriHandler.hxx -- Access the nav database
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_NAVDB_URI_HANDLER_HXX
|
||||
#define __FG_NAVDB_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class NavdbUriHandler : public URIHandler {
|
||||
public:
|
||||
NavdbUriHandler( const std::string& uri = "/navdb" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_NAVDB_URI_HANDLER_HXX
|
||||
228
src/Network/http/PkgUriHandler.cxx
Normal file
228
src/Network/http/PkgUriHandler.cxx
Normal file
@@ -0,0 +1,228 @@
|
||||
// PkgUriHandler.cxx -- service for the package system
|
||||
//
|
||||
// Written by Torsten Dreyer, started February 2015.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "PkgUriHandler.hxx"
|
||||
#include <cJSON.h>
|
||||
|
||||
#include <simgear/package/Root.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Delegate.hxx>
|
||||
#include <simgear/package/Install.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
/*
|
||||
url: /pkg/command/args
|
||||
|
||||
Examples:
|
||||
/pkg/path
|
||||
|
||||
Input:
|
||||
{
|
||||
command: "command",
|
||||
args: {
|
||||
}
|
||||
}
|
||||
|
||||
Output:
|
||||
{
|
||||
}
|
||||
*/
|
||||
|
||||
static cJSON * StringListToJson( const string_list & l )
|
||||
{
|
||||
cJSON * jsonArray = cJSON_CreateArray();
|
||||
for( string_list::const_iterator it = l.begin(); it != l.end(); ++it )
|
||||
cJSON_AddItemToArray(jsonArray, cJSON_CreateString((*it).c_str()) );
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
static cJSON * PackageToJson( simgear::pkg::Package * p )
|
||||
{
|
||||
cJSON * json = cJSON_CreateObject();
|
||||
if( p ) {
|
||||
cJSON_AddItemToObject(json, "id", cJSON_CreateString( p->id().c_str() ));
|
||||
cJSON_AddItemToObject(json, "name", cJSON_CreateString( p->name().c_str() ));
|
||||
cJSON_AddItemToObject(json, "description", cJSON_CreateString( p->description().c_str() ));
|
||||
cJSON_AddItemToObject(json, "installed", cJSON_CreateBool( p->isInstalled() ));
|
||||
cJSON_AddItemToObject(json, "thumbnails", StringListToJson( p->thumbnailUrls() ));
|
||||
cJSON_AddItemToObject(json, "variants", StringListToJson( p->variants() ));
|
||||
cJSON_AddItemToObject(json, "revision", cJSON_CreateNumber( p->revision() ));
|
||||
cJSON_AddItemToObject(json, "fileSize", cJSON_CreateNumber( p->fileSizeBytes() ));
|
||||
cJSON_AddItemToObject(json, "author", cJSON_CreateString( p->getLocalisedProp("author").c_str() ));
|
||||
cJSON_AddItemToObject(json, "ratingFdm", cJSON_CreateString( p->getLocalisedProp("rating/FDM").c_str() ));
|
||||
cJSON_AddItemToObject(json, "ratingCockpit", cJSON_CreateString( p->getLocalisedProp("rating/cockpit").c_str() ));
|
||||
cJSON_AddItemToObject(json, "ratingModel", cJSON_CreateString( p->getLocalisedProp("rating/model").c_str() ));
|
||||
cJSON_AddItemToObject(json, "ratingSystems", cJSON_CreateString( p->getLocalisedProp("rating/systems").c_str() ));
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
static cJSON * PackageListToJson( const simgear::pkg::PackageList & l )
|
||||
{
|
||||
cJSON * jsonArray = cJSON_CreateArray();
|
||||
for( simgear::pkg::PackageList::const_iterator it = l.begin(); it != l.end(); ++it ) {
|
||||
cJSON_AddItemToArray(jsonArray, PackageToJson(*it) );
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
|
||||
static cJSON * CatalogToJson( simgear::pkg::Catalog * c )
|
||||
{
|
||||
cJSON * json = cJSON_CreateObject();
|
||||
if( c ) {
|
||||
cJSON_AddItemToObject(json, "id", cJSON_CreateString( c->id().c_str() ));
|
||||
std::string s = c->installRoot().utf8Str();
|
||||
cJSON_AddItemToObject(json, "installRoot", cJSON_CreateString( s.c_str() ));
|
||||
cJSON_AddItemToObject(json, "url", cJSON_CreateString( c->url().c_str() ));
|
||||
cJSON_AddItemToObject(json, "description", cJSON_CreateString( c->description().c_str() ));
|
||||
cJSON_AddItemToObject(json, "packages", PackageListToJson(c->packages()) );
|
||||
cJSON_AddItemToObject(json, "needingUpdate", PackageListToJson(c->packagesNeedingUpdate()) );
|
||||
cJSON_AddItemToObject(json, "installed", PackageListToJson(c->installedPackages()) );
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
static string PackageRootCommand( simgear::pkg::Root* packageRoot, const string & command, const string & args )
|
||||
{
|
||||
cJSON * json = cJSON_CreateObject();
|
||||
|
||||
if( command == "path" ) {
|
||||
std::string p = packageRoot->path().utf8Str();
|
||||
cJSON_AddItemToObject(json, "path", cJSON_CreateString( p.c_str() ));
|
||||
|
||||
} else if( command == "version" ) {
|
||||
|
||||
cJSON_AddItemToObject(json, "version", cJSON_CreateString( packageRoot->applicationVersion().c_str() ));
|
||||
|
||||
} else if( command == "refresh" ) {
|
||||
packageRoot->refresh(true);
|
||||
cJSON_AddItemToObject(json, "refresh", cJSON_CreateString( "OK" ));
|
||||
|
||||
} else if( command == "catalogs" ) {
|
||||
|
||||
cJSON * jsonArray = cJSON_CreateArray();
|
||||
simgear::pkg::CatalogList catalogList = packageRoot->catalogs();
|
||||
for( simgear::pkg::CatalogList::iterator it = catalogList.begin(); it != catalogList.end(); ++it ) {
|
||||
cJSON_AddItemToArray(jsonArray, CatalogToJson(*it) );
|
||||
}
|
||||
cJSON_AddItemToObject(json, "catalogs", jsonArray );
|
||||
|
||||
} else if( command == "packageById" ) {
|
||||
|
||||
simgear::pkg::PackageRef p = packageRoot->getPackageById(args);
|
||||
cJSON_AddItemToObject(json, "package", PackageToJson( p ));
|
||||
|
||||
} else if( command == "catalogById" ) {
|
||||
|
||||
simgear::pkg::CatalogRef p = packageRoot->getCatalogById(args);
|
||||
cJSON_AddItemToObject(json, "catalog", CatalogToJson( p ));
|
||||
|
||||
} else if( command == "search" ) {
|
||||
|
||||
SGPropertyNode_ptr query(new SGPropertyNode);
|
||||
simgear::pkg::PackageList packageList = packageRoot->packagesMatching(query);
|
||||
cJSON_AddItemToObject(json, "packages", PackageListToJson(packageList) );
|
||||
|
||||
} else if( command == "install" ) {
|
||||
|
||||
simgear::pkg::PackageRef package = packageRoot->getPackageById(args);
|
||||
if( NULL == package ) {
|
||||
SG_LOG(SG_NETWORK,SG_WARN,"Can't install package '" << args << "', package not found" );
|
||||
cJSON_Delete( json );
|
||||
return string("");
|
||||
}
|
||||
package->existingInstall();
|
||||
|
||||
} else {
|
||||
SG_LOG( SG_NETWORK,SG_WARN, "Unhandled pkg command : '" << command << "'" );
|
||||
cJSON_Delete( json );
|
||||
return string("");
|
||||
}
|
||||
|
||||
char * jsonString = cJSON_PrintUnformatted( json );
|
||||
string reply(jsonString);
|
||||
free( jsonString );
|
||||
cJSON_Delete( json );
|
||||
return reply;
|
||||
}
|
||||
|
||||
static string findCommand( const string & uri, string & outArgs )
|
||||
{
|
||||
size_t n = uri.find_first_of('/');
|
||||
if( n == string::npos ) outArgs = string("");
|
||||
else outArgs = uri.substr( n+1 );
|
||||
return uri.substr( 0, n );
|
||||
}
|
||||
|
||||
bool PkgUriHandler::handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection )
|
||||
{
|
||||
response.Header["Content-Type"] = "application/json; charset=UTF-8";
|
||||
response.Header["Access-Control-Allow-Origin"] = "*";
|
||||
response.Header["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST";
|
||||
response.Header["Access-Control-Allow-Headers"] = "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token";
|
||||
|
||||
if( request.Method == "OPTIONS" ){
|
||||
return true; // OPTIONS only needs the headers
|
||||
}
|
||||
|
||||
simgear::pkg::Root* packageRoot = globals->packageRoot();
|
||||
if( NULL == packageRoot ) {
|
||||
SG_LOG( SG_NETWORK,SG_WARN, "NO PackageRoot" );
|
||||
response.StatusCode = 500;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
string argString;
|
||||
string command = findCommand( string(request.Uri).substr(getUri().size()), argString );
|
||||
|
||||
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "Request is for command '" << command << "' with arg='" << argString << "'" );
|
||||
|
||||
if( request.Method == "GET" ){
|
||||
} else if( request.Method == "POST" ) {
|
||||
} else {
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "PkgUriHandler: invalid request method '" << request.Method << "'" );
|
||||
response.Header["Allow"] = "OPTIONS, GET, POST";
|
||||
response.StatusCode = 405;
|
||||
response.Content = "{}";
|
||||
return true;
|
||||
}
|
||||
|
||||
response.Content = PackageRootCommand( packageRoot, command, argString );
|
||||
if( response.Content.empty() ) {
|
||||
response.StatusCode = 404;
|
||||
response.Content = "{}";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
40
src/Network/http/PkgUriHandler.hxx
Normal file
40
src/Network/http/PkgUriHandler.hxx
Normal file
@@ -0,0 +1,40 @@
|
||||
// PkgUriHandler.hxx -- service for the package system
|
||||
//
|
||||
// Written by Torsten Dreyer, started February 2015.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_PKG_URI_HANDLER_HXX
|
||||
#define __FG_PKG_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class PkgUriHandler : public URIHandler {
|
||||
public:
|
||||
PkgUriHandler( const std::string& uri = "/pkg/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
private:
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_PKG_URI_HANDLER_HXX
|
||||
105
src/Network/http/PropertyChangeObserver.cxx
Normal file
105
src/Network/http/PropertyChangeObserver.cxx
Normal file
@@ -0,0 +1,105 @@
|
||||
// PropertyChangeObserver.cxx -- Watch properties for changes
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "PropertyChangeObserver.hxx"
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
using std::string;
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
|
||||
|
||||
PropertyChangeObserver::PropertyChangeObserver()
|
||||
{
|
||||
}
|
||||
|
||||
PropertyChangeObserver::~PropertyChangeObserver()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
void PropertyChangeObserver::check()
|
||||
{
|
||||
|
||||
for (Entries_t::iterator it = _entries.begin(); it != _entries.end(); ++it) {
|
||||
if (!(*it)->_node.isShared()) {
|
||||
// node is no longer used but by us - remove the entry
|
||||
it = _entries.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!(*it)->_changed ) {
|
||||
(*it)->_changed = (*it)->_prevValue != (*it)->_node->getStringValue();
|
||||
if ((*it)->_changed)
|
||||
(*it)->_prevValue = (*it)->_node->getStringValue();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyChangeObserver::uncheck()
|
||||
{
|
||||
for (Entries_t::iterator it = _entries.begin(); it != _entries.end(); ++it) {
|
||||
(*it)->_changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
const SGPropertyNode_ptr PropertyChangeObserver::addObservation( const string propertyName)
|
||||
{
|
||||
for (Entries_t::iterator it = _entries.begin(); it != _entries.end(); ++it) {
|
||||
if (propertyName == (*it)->_node->getPath(true) ) {
|
||||
// if a new observer is added to a property, mark it as changed to ensure the observer
|
||||
// gets notified on initial call. This also causes a notification for all other observers of this
|
||||
// property.
|
||||
(*it)->_changed = true;
|
||||
return (*it)->_node;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
PropertyChangeObserverEntryRef entry = new PropertyChangeObserverEntry();
|
||||
entry->_node = fgGetNode( propertyName, true );
|
||||
_entries.push_back( entry );
|
||||
return entry->_node;
|
||||
}
|
||||
catch( string & s ) {
|
||||
SG_LOG(SG_NETWORK,SG_WARN,"httpd: can't observer '" << propertyName << "'. Invalid name." );
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
bool PropertyChangeObserver::isChangedValue(const SGPropertyNode_ptr node)
|
||||
{
|
||||
for (Entries_t::iterator it = _entries.begin(); it != _entries.end(); ++it) {
|
||||
PropertyChangeObserverEntryRef entry = *it;
|
||||
|
||||
if( entry->_node == node && entry->_changed ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
64
src/Network/http/PropertyChangeObserver.hxx
Normal file
64
src/Network/http/PropertyChangeObserver.hxx
Normal file
@@ -0,0 +1,64 @@
|
||||
// PropertyChangeObserver.hxx -- Watch properties for changes
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef PROPERTYCHANGEOBSERVER_HXX_
|
||||
#define PROPERTYCHANGEOBSERVER_HXX_
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
struct PropertyChangeObserverEntry : public SGReferenced {
|
||||
PropertyChangeObserverEntry()
|
||||
: _changed(true)
|
||||
{
|
||||
}
|
||||
SGPropertyNode_ptr _node;
|
||||
std::string _prevValue;
|
||||
bool _changed;
|
||||
};
|
||||
|
||||
typedef SGSharedPtr<PropertyChangeObserverEntry> PropertyChangeObserverEntryRef;
|
||||
|
||||
class PropertyChangeObserver {
|
||||
public:
|
||||
PropertyChangeObserver();
|
||||
virtual ~PropertyChangeObserver();
|
||||
|
||||
const SGPropertyNode_ptr addObservation( const std::string propertyName);
|
||||
bool isChangedValue(const SGPropertyNode_ptr node);
|
||||
|
||||
void check();
|
||||
void uncheck();
|
||||
|
||||
void clear() { _entries.clear(); }
|
||||
|
||||
private:
|
||||
typedef std::vector<PropertyChangeObserverEntryRef> Entries_t;
|
||||
Entries_t _entries;
|
||||
|
||||
};
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif /* PROPERTYCHANGEOBSERVER_HXX_ */
|
||||
260
src/Network/http/PropertyChangeWebsocket.cxx
Normal file
260
src/Network/http/PropertyChangeWebsocket.cxx
Normal file
@@ -0,0 +1,260 @@
|
||||
// PropertyChangeWebsocket.cxx -- A websocket for propertychangelisteners
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "PropertyChangeWebsocket.hxx"
|
||||
#include "PropertyChangeObserver.hxx"
|
||||
#include "jsonprops.hxx"
|
||||
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <cJSON.h>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
using std::string;
|
||||
|
||||
static unsigned nextid = 0;
|
||||
|
||||
static void setPropertyFromJson(SGPropertyNode_ptr prop, cJSON * json)
|
||||
{
|
||||
if (!prop) return;
|
||||
if ( NULL == json ) return;
|
||||
switch ( json->type ) {
|
||||
case cJSON_String:
|
||||
prop->setStringValue(json->valuestring);
|
||||
break;
|
||||
|
||||
case cJSON_Number:
|
||||
prop->setDoubleValue(json->valuedouble);
|
||||
break;
|
||||
|
||||
case cJSON_True:
|
||||
prop->setBoolValue(true);
|
||||
break;
|
||||
|
||||
case cJSON_False:
|
||||
prop->setBoolValue(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handleSetCommand(const string_list& nodes, cJSON* json, WebsocketWriter &writer)
|
||||
{
|
||||
cJSON * value = cJSON_GetObjectItem(json, "value");
|
||||
if ( NULL != value ) {
|
||||
if (nodes.size() > 1) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: WS set: insufficent values for nodes:" << nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr n = fgGetNode(nodes.front());
|
||||
if (!n) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: set '" << nodes.front() << "' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
setPropertyFromJson(n, value);
|
||||
return;
|
||||
}
|
||||
|
||||
cJSON * values = cJSON_GetObjectItem(json, "values");
|
||||
if ( ( NULL == values ) || ( static_cast<size_t>(cJSON_GetArraySize(values)) != nodes.size()) ) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: WS set: mismatched nodes/values sizes:" << nodes.size());
|
||||
return;
|
||||
}
|
||||
|
||||
string_list::const_iterator it;
|
||||
int i=0;
|
||||
for (it = nodes.begin(); it != nodes.end(); ++it, ++i) {
|
||||
SGPropertyNode_ptr n = fgGetNode(*it);
|
||||
if (!n) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
setPropertyFromJson(n, cJSON_GetArrayItem(values, i));
|
||||
} // of nodes iteration
|
||||
}
|
||||
|
||||
static void handleExecCommand(cJSON* json)
|
||||
{
|
||||
cJSON* name = cJSON_GetObjectItem(json, "fgcommand");
|
||||
if ((NULL == name )|| (NULL == name->valuestring)) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: exec: no fgcommand name");
|
||||
return;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr arg(new SGPropertyNode);
|
||||
JSON::addChildrenToProp( json, arg );
|
||||
|
||||
globals->get_commands()->execute(name->valuestring, arg, nullptr);
|
||||
}
|
||||
|
||||
PropertyChangeWebsocket::PropertyChangeWebsocket(PropertyChangeObserver * propertyChangeObserver)
|
||||
: id(++nextid),
|
||||
_propertyChangeObserver(propertyChangeObserver),
|
||||
_minTriggerInterval(fgGetDouble("/sim/http/property-websocket/update-interval-secs", 0.05)), // default 20Hz
|
||||
_lastTrigger(-1000)
|
||||
{
|
||||
}
|
||||
|
||||
PropertyChangeWebsocket::~PropertyChangeWebsocket()
|
||||
{
|
||||
}
|
||||
|
||||
void PropertyChangeWebsocket::close()
|
||||
{
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "closing PropertyChangeWebsocket #" << id);
|
||||
_watchedNodes.clear();
|
||||
}
|
||||
|
||||
void PropertyChangeWebsocket::handleGetCommand(const string_list& nodes, WebsocketWriter &writer)
|
||||
{
|
||||
double t = fgGetDouble("/sim/time/elapsed-sec");
|
||||
string_list::const_iterator it;
|
||||
for (it = nodes.begin(); it != nodes.end(); ++it) {
|
||||
SGPropertyNode_ptr n = fgGetNode(*it);
|
||||
if (!n) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: get '" << *it << "' not found");
|
||||
return;
|
||||
}
|
||||
|
||||
writer.writeText( JSON::toJsonString( false, n, 0, t ) );
|
||||
} // of nodes iteration
|
||||
}
|
||||
|
||||
void PropertyChangeWebsocket::handleRequest(const HTTPRequest & request, WebsocketWriter &writer)
|
||||
{
|
||||
if (request.Content.empty()) return;
|
||||
|
||||
/*
|
||||
* allowed JSON is
|
||||
{
|
||||
command : 'addListener',
|
||||
nodes : [
|
||||
'/bar/baz',
|
||||
'/foo/bar'
|
||||
],
|
||||
node: '/bax/foo'
|
||||
}
|
||||
*/
|
||||
cJSON * json = cJSON_Parse(request.Content.c_str());
|
||||
if ( NULL != json) {
|
||||
string command;
|
||||
cJSON * j = cJSON_GetObjectItem(json, "command");
|
||||
if ( NULL != j && NULL != j->valuestring) {
|
||||
command = j->valuestring;
|
||||
}
|
||||
|
||||
// handle a single node name, or an array of them
|
||||
string_list nodeNames;
|
||||
j = cJSON_GetObjectItem(json, "node");
|
||||
if ( NULL != j && NULL != j->valuestring) {
|
||||
nodeNames.push_back(simgear::strutils::strip(string(j->valuestring)));
|
||||
}
|
||||
|
||||
cJSON * nodes = cJSON_GetObjectItem(json, "nodes");
|
||||
if ( NULL != nodes) {
|
||||
for (int i = 0; i < cJSON_GetArraySize(nodes); i++) {
|
||||
cJSON * node = cJSON_GetArrayItem(nodes, i);
|
||||
if ( NULL == node) continue;
|
||||
if ( NULL == node->valuestring) continue;
|
||||
nodeNames.push_back(simgear::strutils::strip(string(node->valuestring)));
|
||||
}
|
||||
}
|
||||
|
||||
if (command == "get") {
|
||||
handleGetCommand(nodeNames, writer);
|
||||
} else if (command == "set") {
|
||||
handleSetCommand(nodeNames, json, writer);
|
||||
} else if (command == "exec") {
|
||||
handleExecCommand(json);
|
||||
} else {
|
||||
string_list::const_iterator it;
|
||||
for (it = nodeNames.begin(); it != nodeNames.end(); ++it) {
|
||||
_watchedNodes.handleCommand(command, *it, _propertyChangeObserver);
|
||||
}
|
||||
}
|
||||
|
||||
cJSON_Delete(json);
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyChangeWebsocket::poll(WebsocketWriter & writer)
|
||||
{
|
||||
double now = fgGetDouble("/sim/time/elapsed-sec");
|
||||
|
||||
if( _minTriggerInterval > .0 ) {
|
||||
if( now - _lastTrigger <= _minTriggerInterval )
|
||||
return;
|
||||
|
||||
_lastTrigger = now;
|
||||
}
|
||||
|
||||
for (WatchedNodesList::iterator it = _watchedNodes.begin(); it != _watchedNodes.end(); ++it) {
|
||||
SGPropertyNode_ptr node = *it;
|
||||
|
||||
string newValue;
|
||||
if (_propertyChangeObserver->isChangedValue(node)) {
|
||||
string out = JSON::toJsonString( false, node, 0, now );
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "PropertyChangeWebsocket::poll() new Value for " << node->getPath(true) << " '" << node->getStringValue() << "' #" << id << ": " << out );
|
||||
writer.writeText( out );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PropertyChangeWebsocket::WatchedNodesList::handleCommand(const string & command, const string & node,
|
||||
PropertyChangeObserver * propertyChangeObserver)
|
||||
{
|
||||
if (command == "addListener") {
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (node == (*it)->getPath(true)) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: " << command << " '" << node << "' ignored (duplicate)");
|
||||
return; // dupliate
|
||||
}
|
||||
}
|
||||
SGPropertyNode_ptr n = propertyChangeObserver->addObservation(node);
|
||||
if (n.valid()) push_back(n);
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "httpd: " << command << " '" << node << "' success");
|
||||
|
||||
} else if (command == "removeListener") {
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
if (node == (*it)->getPath(true)) {
|
||||
this->erase(it);
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "httpd: " << command << " '" << node << "' success");
|
||||
return;
|
||||
}
|
||||
}
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "httpd: " << command << " '" << node << "' ignored (not found)");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
61
src/Network/http/PropertyChangeWebsocket.hxx
Normal file
61
src/Network/http/PropertyChangeWebsocket.hxx
Normal file
@@ -0,0 +1,61 @@
|
||||
// PropertyChangeWebsocket.hxx -- A websocket for propertychangelisteners
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef PROPERTYCHANGEWEBSOCKET_HXX_
|
||||
#define PROPERTYCHANGEWEBSOCKET_HXX_
|
||||
|
||||
#include "Websocket.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class PropertyChangeObserver;
|
||||
|
||||
class PropertyChangeWebsocket: public Websocket {
|
||||
public:
|
||||
PropertyChangeWebsocket(PropertyChangeObserver * propertyChangeObserver);
|
||||
virtual ~PropertyChangeWebsocket();
|
||||
virtual void close();
|
||||
virtual void handleRequest(const HTTPRequest & request, WebsocketWriter & writer);
|
||||
virtual void poll(WebsocketWriter & writer);
|
||||
|
||||
private:
|
||||
unsigned id;
|
||||
PropertyChangeObserver * _propertyChangeObserver;
|
||||
|
||||
void handleGetCommand(const string_list& nodes, WebsocketWriter &writer);
|
||||
|
||||
class WatchedNodesList: public std::vector<SGPropertyNode_ptr> {
|
||||
public:
|
||||
void handleCommand(const std::string & command, const std::string & node, PropertyChangeObserver * propertyChangeObserver);
|
||||
};
|
||||
|
||||
WatchedNodesList _watchedNodes;
|
||||
double _minTriggerInterval;
|
||||
double _lastTrigger;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* PROPERTYCHANGEWEBSOCKET_HXX_ */
|
||||
411
src/Network/http/PropertyUriHandler.cxx
Normal file
411
src/Network/http/PropertyUriHandler.cxx
Normal file
@@ -0,0 +1,411 @@
|
||||
// PropertyUriHandler.cxx -- a web form interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "PropertyUriHandler.hxx"
|
||||
#include "SimpleDOM.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
// copied from http://stackoverflow.com/a/24315631
|
||||
static void ReplaceAll(std::string & str, const std::string & from, const std::string & to)
|
||||
{
|
||||
size_t start_pos = 0;
|
||||
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
|
||||
}
|
||||
}
|
||||
|
||||
static const std::string specialChars[][2] = {
|
||||
{ "&", "&" },
|
||||
{ "\"", """ },
|
||||
{ "'", "'" },
|
||||
{ "<", "<" },
|
||||
{ ">", ">" },
|
||||
};
|
||||
|
||||
static inline std::string htmlSpecialChars( const std::string & s )
|
||||
{
|
||||
string reply = s;
|
||||
for( size_t i = 0; i < sizeof(specialChars)/sizeof(specialChars[0]); ++i )
|
||||
ReplaceAll( reply, specialChars[i][0], specialChars[i][1] );
|
||||
return reply;
|
||||
}
|
||||
|
||||
class SortedChilds : public simgear::PropertyList {
|
||||
public:
|
||||
SortedChilds( SGPropertyNode_ptr node ) {
|
||||
for (int i = 0; i < node->nChildren(); i++)
|
||||
push_back(node->getChild(i));
|
||||
std::sort(begin(), end(), CompareNodes());
|
||||
}
|
||||
private:
|
||||
class CompareNodes {
|
||||
public:
|
||||
bool operator() (const SGPropertyNode *a, const SGPropertyNode *b) const {
|
||||
int r = strcmp(a->getNameString().c_str(), b->getNameString().c_str());
|
||||
return r ? r < 0 : a->getIndex() < b->getIndex();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
static const char * getPropertyTypeString( simgear::props::Type type )
|
||||
{
|
||||
switch( type ) {
|
||||
case simgear::props::NONE: return "";
|
||||
case simgear::props::ALIAS: return "alias";
|
||||
case simgear::props::BOOL: return "bool";
|
||||
case simgear::props::INT: return "int";
|
||||
case simgear::props::LONG: return "long";
|
||||
case simgear::props::FLOAT: return "float";
|
||||
case simgear::props::DOUBLE: return "double";
|
||||
case simgear::props::STRING: return "string";
|
||||
case simgear::props::UNSPECIFIED: return "unspecified";
|
||||
case simgear::props::EXTENDED: return "extended";
|
||||
case simgear::props::VEC3D: return "vec3d";
|
||||
case simgear::props::VEC4D: return "vec4d";
|
||||
default: return "?";
|
||||
}
|
||||
}
|
||||
|
||||
DOMElement * createHeader( const string & prefix, const string & propertyPath )
|
||||
{
|
||||
using namespace simgear::strutils;
|
||||
|
||||
string path = prefix;
|
||||
|
||||
DOMNode * root = new DOMNode( "div" );
|
||||
root->setAttribute( "id", "breadcrumb" );
|
||||
|
||||
DOMNode * headline = new DOMNode( "h3" );
|
||||
root->addChild( headline );
|
||||
headline->addChild( new DOMTextElement("FlightGear Property Browser") );
|
||||
|
||||
{
|
||||
DOMNode * div = new DOMNode("div");
|
||||
root->addChild(div);
|
||||
div->addChild( new DOMNode("span"))->addChild( new DOMTextElement("Path:"));
|
||||
div->addChild( new DOMNode("span"))->addChild( new DOMTextElement( propertyPath ) );
|
||||
div->addChild( new DOMNode("a"))->
|
||||
setAttribute("href",string("/json/")+propertyPath+"?i=y")->
|
||||
addChild( new DOMTextElement( "As JSON" ) );
|
||||
}
|
||||
|
||||
DOMNode * breadcrumb = new DOMNode("ul");
|
||||
root->addChild( breadcrumb );
|
||||
|
||||
DOMNode * li = new DOMNode("li");
|
||||
breadcrumb->addChild( li );
|
||||
DOMNode * a = new DOMNode("a");
|
||||
li->addChild( a );
|
||||
a->setAttribute( "href", path );
|
||||
a->addChild( new DOMTextElement( "[root]" ) );
|
||||
|
||||
string_list items = split( propertyPath, "/" );
|
||||
for( string_list::iterator it = items.begin(); it != items.end(); ++it ) {
|
||||
if( (*it).empty() ) continue;
|
||||
path.append( *it ).append( "/" );
|
||||
|
||||
li = new DOMNode("li");
|
||||
breadcrumb->addChild( li );
|
||||
a = new DOMNode("a");
|
||||
li->addChild( a );
|
||||
a->setAttribute( "href", path );
|
||||
a->addChild( new DOMTextElement( (*it) ) );
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static DOMNode * createLabeledRadioButton( const char * label, const std::string & name, bool checked )
|
||||
{
|
||||
DOMNode * root = new DOMNode("span");
|
||||
root->setAttribute( "class", "radiobutton-container" );
|
||||
|
||||
root->addChild( new DOMNode("span"))->addChild( new DOMTextElement(label) );
|
||||
DOMNode * radio = root->addChild(new DOMNode( "input" ))
|
||||
->setAttribute( "type", "radio" )
|
||||
->setAttribute( "name", name )
|
||||
->setAttribute( "value", label );
|
||||
|
||||
if( checked )
|
||||
radio->setAttribute( "checked", "checked" );
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
static DOMElement * renderPropertyValueElement( SGPropertyNode_ptr node )
|
||||
{
|
||||
string value = node->getStringValue();
|
||||
int len = value.length();
|
||||
|
||||
if( len < 15 ) len = 15;
|
||||
|
||||
DOMNode * root;
|
||||
|
||||
if( node->getType() == simgear::props::BOOL ) {
|
||||
root = new DOMNode( "span" );
|
||||
|
||||
root->addChild( createLabeledRadioButton( "true", node->getDisplayName(), node->getBoolValue() ));
|
||||
root->addChild( createLabeledRadioButton( "false", node->getDisplayName(), !node->getBoolValue() ));
|
||||
|
||||
} else if( len < 60 ) {
|
||||
root = new DOMNode( "input" );
|
||||
root->setAttribute( "type", "text" );
|
||||
root->setAttribute( "name", node->getDisplayName() );
|
||||
root->setAttribute( "value", htmlSpecialChars(value) );
|
||||
root->setAttribute( "size", std::to_string(len) );
|
||||
root->setAttribute( "maxlength", "2047" );
|
||||
} else {
|
||||
int rows = (len / 60)+1;
|
||||
int cols = 60;
|
||||
root = new DOMNode( "textarea" );
|
||||
root->setAttribute( "name", node->getDisplayName() );
|
||||
root->setAttribute( "cols", std::to_string( cols ) );
|
||||
root->setAttribute( "rows", std::to_string( rows ) );
|
||||
root->setAttribute( "maxlength", "2047" );
|
||||
root->addChild( new DOMTextElement( htmlSpecialChars(value) ) );
|
||||
}
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
bool PropertyUriHandler::handleGetRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection )
|
||||
{
|
||||
|
||||
string propertyPath = request.Uri;
|
||||
|
||||
// strip the uri prefix of our handler
|
||||
propertyPath = propertyPath.substr( getUri().size() );
|
||||
|
||||
// strip the querystring
|
||||
size_t pos = propertyPath.find( '?' );
|
||||
if( pos != string::npos ) {
|
||||
propertyPath = propertyPath.substr( 0, pos-1 );
|
||||
}
|
||||
|
||||
// skip trailing '/' - not very efficient but shouldn't happen too often
|
||||
while( !propertyPath.empty() && propertyPath[ propertyPath.length()-1 ] == '/' )
|
||||
propertyPath = propertyPath.substr(0,propertyPath.length()-1);
|
||||
|
||||
if( request.RequestVariables.get("submit") == "update" ) {
|
||||
// update leaf
|
||||
string value = request.RequestVariables.get("value");
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "httpd: setting " << propertyPath << " to '" << value << "'" );
|
||||
try {
|
||||
fgSetString( propertyPath.c_str(), value );
|
||||
}
|
||||
catch( string & s ) {
|
||||
SG_LOG(SG_NETWORK,SG_WARN, "httpd: setting " << propertyPath << " to '" << value << "' failed: " << s );
|
||||
}
|
||||
}
|
||||
|
||||
if( request.RequestVariables.get("submit") == "set" ) {
|
||||
for( HTTPRequest::StringMap::const_iterator it = request.RequestVariables.begin(); it != request.RequestVariables.end(); ++it ) {
|
||||
if( it->first == "submit" ) continue;
|
||||
string pp = propertyPath + "/" + it->first;
|
||||
SG_LOG(SG_NETWORK,SG_INFO, "httpd: setting " << pp << " to '" << it->second << "'" );
|
||||
try {
|
||||
fgSetString( pp, it->second );
|
||||
}
|
||||
catch( string & s ) {
|
||||
SG_LOG(SG_NETWORK,SG_WARN, "httpd: setting " << pp << " to '" << it->second << "' failed: " << s );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// build the response
|
||||
DOMNode * html = new DOMNode( "html" );
|
||||
html->setAttribute( "lang", "en" );
|
||||
|
||||
DOMNode * head = new DOMNode( "head" );
|
||||
html->addChild( head );
|
||||
|
||||
DOMNode * e;
|
||||
e = new DOMNode( "title" );
|
||||
head->addChild( e );
|
||||
e->addChild( new DOMTextElement( string("FlightGear Property Browser - ") + propertyPath ) );
|
||||
|
||||
e = new DOMNode( "link" );
|
||||
head->addChild( e );
|
||||
e->setAttribute( "href", "/css/props.css" );
|
||||
e->setAttribute( "rel", "stylesheet" );
|
||||
e->setAttribute( "type", "text/css" );
|
||||
|
||||
DOMNode * body = new DOMNode( "body" );
|
||||
html->addChild( body );
|
||||
|
||||
SGPropertyNode_ptr node;
|
||||
try {
|
||||
node = fgGetNode( string("/") + propertyPath );
|
||||
}
|
||||
catch( string & s ) {
|
||||
SG_LOG(SG_NETWORK,SG_WARN, "httpd: reading '" << propertyPath << "' failed: " << s );
|
||||
}
|
||||
if( !node.valid() ) {
|
||||
DOMNode * headline = new DOMNode( "h3" );
|
||||
body->addChild( headline );
|
||||
headline->addChild( new DOMTextElement( "Non-existent node requested!" ) );
|
||||
e = new DOMNode( "b" );
|
||||
e->addChild( new DOMTextElement( propertyPath ) );
|
||||
// does not exist
|
||||
body->addChild( e );
|
||||
response.StatusCode = 404;
|
||||
|
||||
} else if( node->nChildren() > 0 ) {
|
||||
// Render the list of children
|
||||
body->addChild( createHeader( getUri(), propertyPath ));
|
||||
|
||||
DOMNode * table = new DOMNode("table");
|
||||
body->addChild( table );
|
||||
|
||||
DOMNode * tr = new DOMNode( "tr" );
|
||||
table->addChild( tr );
|
||||
|
||||
DOMNode * th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( " " ) );
|
||||
|
||||
th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( "Property" ) );
|
||||
th->setAttribute( "id", "property" );
|
||||
|
||||
th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( "Value" ) );
|
||||
th->setAttribute( "id", "value" );
|
||||
|
||||
th = new DOMNode( "th" );
|
||||
tr->addChild( th );
|
||||
th->addChild( new DOMTextElement( "Type" ) );
|
||||
th->setAttribute( "id", "type" );
|
||||
|
||||
SortedChilds sortedChilds( node );
|
||||
for(SortedChilds::iterator it = sortedChilds.begin(); it != sortedChilds.end(); ++it ) {
|
||||
tr = new DOMNode( "tr" );
|
||||
table->addChild( tr );
|
||||
|
||||
SGPropertyNode_ptr child = *it;
|
||||
string name = child->getDisplayName(true);
|
||||
|
||||
DOMNode * td;
|
||||
|
||||
// Expand Link
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "expand" );
|
||||
if ( child->nChildren() > 0 ) {
|
||||
DOMNode * a = new DOMNode("a");
|
||||
td->addChild( a );
|
||||
a->setAttribute( "href", getUri() + propertyPath + "/" + name );
|
||||
a->addChild( new DOMTextElement( "(+)" ));
|
||||
}
|
||||
|
||||
// Property Name
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "property" );
|
||||
DOMNode * a = new DOMNode("a");
|
||||
td->addChild( a );
|
||||
a->setAttribute( "href", getUri() + propertyPath + "/" + name );
|
||||
a->addChild( new DOMTextElement( name ) );
|
||||
|
||||
// Value
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "value" );
|
||||
if ( child->nChildren() == 0 ) {
|
||||
DOMNode * form = new DOMNode("form");
|
||||
td->addChild( form );
|
||||
form->setAttribute( "method", "GET" );
|
||||
form->setAttribute( "action", getUri() + propertyPath );
|
||||
|
||||
e = new DOMNode( "input" );
|
||||
form->addChild( e );
|
||||
e->setAttribute( "type", "submit" );
|
||||
e->setAttribute( "value", "set" );
|
||||
e->setAttribute( "name", "submit" );
|
||||
|
||||
form->addChild( renderPropertyValueElement( node->getNode( name ) ) );
|
||||
|
||||
} else {
|
||||
td->addChild( new DOMTextElement( " " ) );
|
||||
}
|
||||
|
||||
// Property Type
|
||||
td = new DOMNode("td");
|
||||
tr->addChild( td );
|
||||
td->setAttribute( "id", "type" );
|
||||
td->addChild(
|
||||
new DOMTextElement( getPropertyTypeString(node->getNode( name )->getType()) ) );
|
||||
|
||||
}
|
||||
} else {
|
||||
// Render a single property
|
||||
body->addChild( createHeader( getUri(), propertyPath ));
|
||||
e = new DOMNode( "div" );
|
||||
body->addChild( e );
|
||||
|
||||
e->setAttribute( "id", "currentvalue" );
|
||||
e->addChild( new DOMTextElement( "Current Value: " ) );
|
||||
e->addChild( new DOMTextElement( htmlSpecialChars(node->getStringValue()) ) );
|
||||
|
||||
DOMNode * form = new DOMNode("form");
|
||||
body->addChild( form );
|
||||
form->setAttribute( "method", "GET" );
|
||||
form->setAttribute( "action", getUri() + propertyPath );
|
||||
|
||||
e = new DOMNode( "input" );
|
||||
form->addChild( e );
|
||||
e->setAttribute( "type", "submit" );
|
||||
e->setAttribute( "value", "update" );
|
||||
e->setAttribute( "name", "submit" );
|
||||
|
||||
form->addChild( renderPropertyValueElement( node ) );
|
||||
}
|
||||
|
||||
// Send the response
|
||||
response.Content = "<!DOCTYPE html>";
|
||||
response.Content.append( html->render() );
|
||||
delete html;
|
||||
response.Header["Content-Type"] = "text/html; charset=UTF-8";
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
38
src/Network/http/PropertyUriHandler.hxx
Normal file
38
src/Network/http/PropertyUriHandler.hxx
Normal file
@@ -0,0 +1,38 @@
|
||||
// PropertyUriHandler.cxx -- a web form interface to the property tree
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_PROPERTY_URI_HANDLER_HXX
|
||||
#define __FG_PROPERTY_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class PropertyUriHandler : public URIHandler {
|
||||
public:
|
||||
PropertyUriHandler( const std::string& uri = "/prop/" ) : URIHandler( uri ) {}
|
||||
virtual bool handleGetRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_PROPERTY_URI_HANDLER_HXX
|
||||
66
src/Network/http/RunUriHandler.cxx
Normal file
66
src/Network/http/RunUriHandler.cxx
Normal file
@@ -0,0 +1,66 @@
|
||||
// RunUriHandler.cxx -- Run a flightgear command
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
|
||||
#include "RunUriHandler.hxx"
|
||||
#include "jsonprops.hxx"
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/commands.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <cJSON.h>
|
||||
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
|
||||
bool RunUriHandler::handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection )
|
||||
{
|
||||
response.Header["Content-Type"] = "text/plain";
|
||||
string command = request.RequestVariables.get("value");
|
||||
if( command.empty() ) {
|
||||
response.StatusCode = 400;
|
||||
response.Content = "command not specified";
|
||||
return true;
|
||||
}
|
||||
|
||||
SGPropertyNode_ptr args = new SGPropertyNode();
|
||||
cJSON * json = cJSON_Parse( request.Content.c_str() );
|
||||
JSON::toProp( json, args );
|
||||
|
||||
SG_LOG( SG_NETWORK, SG_INFO, "RunUriHandler("<< request.Content << "): command='" << command << "', arg='" << JSON::toJsonString(false,args,5) << "'");
|
||||
|
||||
cJSON_Delete( json );
|
||||
if ( globals->get_commands()->execute(command.c_str(), args, nullptr) ) {
|
||||
response.Content = "ok.";
|
||||
return true;
|
||||
}
|
||||
|
||||
response.Content = "command '" + command + "' failed.";
|
||||
response.StatusCode = 501; // Not implemented probably suits best
|
||||
SG_LOG( SG_NETWORK, SG_WARN, response.Content );
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
38
src/Network/http/RunUriHandler.hxx
Normal file
38
src/Network/http/RunUriHandler.hxx
Normal file
@@ -0,0 +1,38 @@
|
||||
// RunUriHandler.hxx -- Provide screenshots via http
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_RUN_URI_HANDLER_HXX
|
||||
#define __FG_RUN_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class RunUriHandler : public URIHandler {
|
||||
public:
|
||||
RunUriHandler( const std::string& uri = "/run.cgi" ) : URIHandler( uri ) {}
|
||||
virtual bool handleRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_RUN_URI_HANDLER_HXX
|
||||
588
src/Network/http/ScreenshotUriHandler.cxx
Normal file
588
src/Network/http/ScreenshotUriHandler.cxx
Normal file
@@ -0,0 +1,588 @@
|
||||
// ScreenshotUriHandler.cxx -- Provide screenshots via http
|
||||
//
|
||||
// Started by Curtis Olson, started June 2001.
|
||||
// osg support written by James Turner
|
||||
// Ported to new httpd infrastructure by Torsten Dreyer
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "ScreenshotUriHandler.hxx"
|
||||
|
||||
#include <osgDB/Registry>
|
||||
#include <osgDB/ReaderWriter>
|
||||
#include <osgUtil/SceneView>
|
||||
#include <osgViewer/Viewer>
|
||||
|
||||
#include <Canvas/canvas_mgr.hxx>
|
||||
#include <simgear/canvas/Canvas.hxx>
|
||||
|
||||
#include <simgear/threads/SGQueue.hxx>
|
||||
#include <simgear/structure/Singleton.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Viewer/renderer.hxx>
|
||||
|
||||
#include <queue>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::list;
|
||||
|
||||
namespace sc = simgear::canvas;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ImageReadyListener {
|
||||
public:
|
||||
virtual void imageReady(osg::ref_ptr<osg::Image>) = 0;
|
||||
virtual ~ImageReadyListener()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class StringReadyListener {
|
||||
public:
|
||||
virtual void stringReady(const std::string &) = 0;
|
||||
virtual ~StringReadyListener()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct ImageCompressionTask {
|
||||
StringReadyListener * stringReadyListener;
|
||||
string format;
|
||||
osg::ref_ptr<osg::Image> image;
|
||||
|
||||
ImageCompressionTask()
|
||||
{
|
||||
stringReadyListener = NULL;
|
||||
}
|
||||
|
||||
ImageCompressionTask(const ImageCompressionTask & other)
|
||||
{
|
||||
stringReadyListener = other.stringReadyListener;
|
||||
format = other.format;
|
||||
image = other.image;
|
||||
}
|
||||
|
||||
ImageCompressionTask & operator =(const ImageCompressionTask & other)
|
||||
{
|
||||
stringReadyListener = other.stringReadyListener;
|
||||
format = other.format;
|
||||
image = other.image;
|
||||
return *this;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class ImageCompressor: public OpenThreads::Thread {
|
||||
public:
|
||||
ImageCompressor()
|
||||
{
|
||||
}
|
||||
virtual void run();
|
||||
void addTask(ImageCompressionTask & task);
|
||||
private:
|
||||
typedef SGBlockingQueue<ImageCompressionTask> TaskList;
|
||||
TaskList _tasks;
|
||||
};
|
||||
|
||||
typedef simgear::Singleton<ImageCompressor> ImageCompressorSingleton;
|
||||
|
||||
void ImageCompressor::run()
|
||||
{
|
||||
osg::ref_ptr<osgDB::ReaderWriter::Options> options = new osgDB::ReaderWriter::Options("JPEG_QUALITY 80 PNG_COMPRESSION 9");
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor is running");
|
||||
for (;;) {
|
||||
ImageCompressionTask task = _tasks.pop();
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor has an image");
|
||||
if ( NULL != task.stringReadyListener) {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor checking for writer for " << task.format);
|
||||
osgDB::ReaderWriter* writer = osgDB::Registry::instance()->getReaderWriterForExtension(task.format);
|
||||
if (!writer)
|
||||
continue;
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor compressing to " << task.format);
|
||||
std::stringstream outputStream;
|
||||
osgDB::ReaderWriter::WriteResult wr;
|
||||
wr = writer->writeImage(*task.image, outputStream, options);
|
||||
|
||||
if (wr.success()) {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor compressed to " << task.format);
|
||||
task.stringReadyListener->stringReady(outputStream.str());
|
||||
}
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor done for this image" << task.format);
|
||||
}
|
||||
}
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "ImageCompressor exiting");
|
||||
}
|
||||
|
||||
void ImageCompressor::addTask(ImageCompressionTask & task)
|
||||
{
|
||||
_tasks.push(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on <a href="http://code.google.com/p/osgworks">osgworks</a> ScreenCapture.cpp
|
||||
*
|
||||
*/
|
||||
class ScreenshotCallback: public osg::Camera::DrawCallback {
|
||||
public:
|
||||
ScreenshotCallback()
|
||||
: _min_delta_tick(1.0/8.0)
|
||||
{
|
||||
_previousFrameTick = osg::Timer::instance()->tick();
|
||||
}
|
||||
|
||||
virtual void operator ()(osg::RenderInfo& renderInfo) const
|
||||
{
|
||||
osg::Timer_t n = osg::Timer::instance()->tick();
|
||||
double dt = osg::Timer::instance()->delta_s(_previousFrameTick,n);
|
||||
if (dt < _min_delta_tick)
|
||||
return;
|
||||
_previousFrameTick = n;
|
||||
|
||||
bool hasSubscribers = false;
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
hasSubscribers = !_subscribers.empty();
|
||||
|
||||
}
|
||||
if (hasSubscribers) {
|
||||
osg::ref_ptr<osg::Image> image = new osg::Image;
|
||||
const osg::Viewport* vp = renderInfo.getState()->getCurrentViewport();
|
||||
image->readPixels(vp->x(), vp->y(), vp->width(), vp->height(), GL_RGB, GL_UNSIGNED_BYTE);
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
while (!_subscribers.empty()) {
|
||||
try {
|
||||
_subscribers.back()->imageReady(image);
|
||||
}
|
||||
catch (...) {
|
||||
}
|
||||
_subscribers.pop_back();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void subscribe(ImageReadyListener * subscriber)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_subscribers.push_back(subscriber);
|
||||
}
|
||||
|
||||
void unsubscribe(ImageReadyListener * subscriber)
|
||||
{
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_subscribers.remove( subscriber );
|
||||
}
|
||||
|
||||
private:
|
||||
mutable list<ImageReadyListener*> _subscribers;
|
||||
mutable OpenThreads::Mutex _lock;
|
||||
mutable double _previousFrameTick;
|
||||
double _min_delta_tick;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ScreenshotRequest: public ConnectionData, public ImageReadyListener, StringReadyListener {
|
||||
public:
|
||||
ScreenshotRequest(const string & window, const string & type, bool stream)
|
||||
: _type(type), _stream(stream)
|
||||
{
|
||||
if ( NULL == osgDB::Registry::instance()->getReaderWriterForExtension(_type))
|
||||
throw sg_format_exception("Unsupported image type: " + type, type);
|
||||
|
||||
osg::Camera * camera = findLastCamera(globals->get_renderer()->getViewerBase(), window);
|
||||
if ( NULL == camera)
|
||||
throw sg_error("Can't find a camera for window '" + window + "'");
|
||||
|
||||
// add our ScreenshotCallback to the camera
|
||||
if ( NULL == camera->getFinalDrawCallback()) {
|
||||
//TODO: are we leaking the Callback on reinit?
|
||||
camera->setFinalDrawCallback(new ScreenshotCallback());
|
||||
}
|
||||
|
||||
_screenshotCallback = dynamic_cast<ScreenshotCallback*>(camera->getFinalDrawCallback());
|
||||
if ( NULL == _screenshotCallback)
|
||||
throw sg_error("Can't find ScreenshotCallback");
|
||||
|
||||
requestScreenshot();
|
||||
}
|
||||
|
||||
virtual ~ScreenshotRequest()
|
||||
{
|
||||
_screenshotCallback->unsubscribe(this);
|
||||
}
|
||||
|
||||
virtual void imageReady(osg::ref_ptr<osg::Image> rawImage)
|
||||
{
|
||||
// called from a rendering thread, not from the main loop
|
||||
ImageCompressionTask task;
|
||||
task.image = rawImage;
|
||||
task.format = _type;
|
||||
task.stringReadyListener = this;
|
||||
ImageCompressorSingleton::instance()->addTask(task);
|
||||
}
|
||||
|
||||
void requestScreenshot()
|
||||
{
|
||||
_screenshotCallback->subscribe(this);
|
||||
}
|
||||
|
||||
mutable OpenThreads::Mutex _lock;
|
||||
|
||||
virtual void stringReady(const string & s)
|
||||
{
|
||||
// called from the compressor thread
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_compressedData = s;
|
||||
}
|
||||
|
||||
string getScreenshot()
|
||||
{
|
||||
string reply;
|
||||
{
|
||||
// called from the main loop
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
reply = _compressedData;
|
||||
_compressedData.clear();
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
osg::Camera* findLastCamera(osgViewer::ViewerBase * viewer, const string & windowName)
|
||||
{
|
||||
osgViewer::ViewerBase::Windows windows;
|
||||
viewer->getWindows(windows);
|
||||
|
||||
osgViewer::GraphicsWindow* window = NULL;
|
||||
|
||||
if (!windowName.empty()) {
|
||||
for (osgViewer::ViewerBase::Windows::iterator itr = windows.begin(); itr != windows.end(); ++itr) {
|
||||
if ((*itr)->getTraits()->windowName == windowName) {
|
||||
window = *itr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( NULL == window) {
|
||||
if (!windowName.empty()) {
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "requested window " << windowName << " not found, using first window");
|
||||
}
|
||||
window = *windows.begin();
|
||||
}
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "Looking for last Camera of window '" << window->getTraits()->windowName << "'");
|
||||
|
||||
osg::GraphicsContext::Cameras& cameras = window->getCameras();
|
||||
osg::Camera* lastCamera = 0;
|
||||
for (osg::GraphicsContext::Cameras::iterator cam_itr = cameras.begin(); cam_itr != cameras.end(); ++cam_itr) {
|
||||
if (lastCamera) {
|
||||
if ((*cam_itr)->getRenderOrder() > lastCamera->getRenderOrder()) {
|
||||
lastCamera = (*cam_itr);
|
||||
}
|
||||
if ((*cam_itr)->getRenderOrder() == lastCamera->getRenderOrder()
|
||||
&& (*cam_itr)->getRenderOrderNum() >= lastCamera->getRenderOrderNum()) {
|
||||
lastCamera = (*cam_itr);
|
||||
}
|
||||
} else {
|
||||
lastCamera = *cam_itr;
|
||||
}
|
||||
}
|
||||
|
||||
return lastCamera;
|
||||
}
|
||||
|
||||
bool isStream() const
|
||||
{
|
||||
return _stream;
|
||||
}
|
||||
|
||||
const string & getType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
private:
|
||||
string _type;
|
||||
bool _stream;
|
||||
string _compressedData;
|
||||
ScreenshotCallback * _screenshotCallback;
|
||||
};
|
||||
|
||||
/**
|
||||
*/
|
||||
class CanvasImageRequest : public ConnectionData, public simgear::canvas::CanvasImageReadyListener, StringReadyListener {
|
||||
public:
|
||||
ImageCompressionTask *currenttask=NULL;
|
||||
sc::CanvasPtr canvas;
|
||||
int connected = 0;
|
||||
|
||||
CanvasImageRequest(const string & window, const string & type, int canvasindex, bool stream)
|
||||
: _type(type), _stream(stream) {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImageRequest:");
|
||||
|
||||
if (NULL == osgDB::Registry::instance()->getReaderWriterForExtension(_type))
|
||||
throw sg_format_exception("Unsupported image type: " + type, type);
|
||||
|
||||
CanvasMgr* canvas_mgr = static_cast<CanvasMgr*> (globals->get_subsystem("Canvas"));
|
||||
if (!canvas_mgr) {
|
||||
SG_LOG(SG_NETWORK, SG_WARN, "CanvasImage:CanvasMgr not found");
|
||||
} else {
|
||||
canvas = canvas_mgr->getCanvas(canvasindex);
|
||||
if (!canvas) {
|
||||
throw sg_error("CanvasImage:Canvas not found for index " + std::to_string(canvasindex));
|
||||
} else {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage:Canvas found for index " << canvasindex);
|
||||
//SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImageRequest: found camera " << camera << ", width=" << canvas->getSizeX() << ", height=%d\n" << canvas->getSizeY());
|
||||
|
||||
SGConstPropertyNode_ptr canvasnode = canvas->getProps();
|
||||
if (canvasnode) {
|
||||
string canvasname = canvasnode->getStringValue("name");
|
||||
if (!canvasname.empty()) {
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "CanvasImageRequest: node=" << canvasnode->getDisplayName().c_str() << ", canvasname =" << canvasname);
|
||||
}
|
||||
}
|
||||
//Looping until success is no option
|
||||
connected = canvas->subscribe(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assumption: when unsubscribe returns,there might just be a compressor thread running,
|
||||
// causing a crash when the deconstructor finishes. Rare, but might happen. Just wait to be sure.
|
||||
virtual ~CanvasImageRequest() {
|
||||
if (currenttask){
|
||||
SG_LOG(SG_NETWORK, SG_ALERT, "CanvasImage: task running, pausing for 15 seconds");
|
||||
SGTimeStamp::sleepFor(SGTimeStamp::fromSec(15));
|
||||
}
|
||||
|
||||
if (canvas && connected){
|
||||
canvas->unsubscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void imageReady(osg::ref_ptr<osg::Image> rawImage) {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage:imageReady");
|
||||
// called from a rendering thread, not from the main loop
|
||||
ImageCompressionTask task;
|
||||
currenttask = &task;
|
||||
task.image = rawImage;
|
||||
task.format = _type;
|
||||
task.stringReadyListener = this;
|
||||
ImageCompressorSingleton::instance()->addTask(task);
|
||||
}
|
||||
|
||||
void requestCanvasImage() {
|
||||
connected = canvas->subscribe(this);
|
||||
}
|
||||
|
||||
mutable OpenThreads::Mutex _lock;
|
||||
|
||||
virtual void stringReady(const string & s) {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage:stringReady");
|
||||
|
||||
// called from the compressor thread
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
_compressedData = s;
|
||||
// allow destructor
|
||||
currenttask = NULL;
|
||||
}
|
||||
|
||||
string getCanvasImage() {
|
||||
string reply;
|
||||
{
|
||||
// called from the main loop
|
||||
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
|
||||
reply = _compressedData;
|
||||
_compressedData.clear();
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool isStream() const {
|
||||
return _stream;
|
||||
}
|
||||
|
||||
const string & getType() const {
|
||||
return _type;
|
||||
}
|
||||
|
||||
private:
|
||||
string _type;
|
||||
bool _stream;
|
||||
string _compressedData;
|
||||
};
|
||||
|
||||
ScreenshotUriHandler::ScreenshotUriHandler(const std::string& uri)
|
||||
: URIHandler(uri)
|
||||
{
|
||||
}
|
||||
|
||||
ScreenshotUriHandler::~ScreenshotUriHandler()
|
||||
{
|
||||
ImageCompressorSingleton::instance()->cancel();
|
||||
//ImageCompressorSingleton::instance()->join();
|
||||
}
|
||||
|
||||
const static string KEY_SCREENSHOT("ScreenshotUriHandler::ScreenshotRequest");
|
||||
const static string KEY_CANVASIMAGE("ScreenshotUriHandler::CanvasImageRequest");
|
||||
#define BOUNDARY "--fgfs-screenshot-boundary"
|
||||
|
||||
bool ScreenshotUriHandler::handleGetRequest(const HTTPRequest & request, HTTPResponse & response, Connection * connection)
|
||||
{
|
||||
if (!ImageCompressorSingleton::instance()->isRunning())
|
||||
ImageCompressorSingleton::instance()->start();
|
||||
|
||||
string type = request.RequestVariables.get("type");
|
||||
if (type.empty()) type = "jpg";
|
||||
|
||||
// string camera = request.RequestVariables.get("camera");
|
||||
string window = request.RequestVariables.get("window");
|
||||
|
||||
bool stream = (!request.RequestVariables.get("stream").empty());
|
||||
|
||||
int canvasindex = -1;
|
||||
string s_canvasindex = request.RequestVariables.get("canvasindex");
|
||||
if (!s_canvasindex.empty()) canvasindex = atoi(s_canvasindex.c_str());
|
||||
|
||||
SGSharedPtr<ScreenshotRequest> screenshotRequest;
|
||||
SGSharedPtr<CanvasImageRequest> canvasimageRequest;
|
||||
try {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "new ScreenshotRequest("<<window<<","<<type<<"," << stream << "," << canvasindex <<")");
|
||||
if (canvasindex == -1)
|
||||
screenshotRequest = new ScreenshotRequest(window, type, stream);
|
||||
else
|
||||
canvasimageRequest = new CanvasImageRequest(window, type, canvasindex, stream);
|
||||
}
|
||||
catch (sg_format_exception & ex)
|
||||
{
|
||||
SG_LOG(SG_NETWORK, SG_INFO, ex.getFormattedMessage());
|
||||
response.Header["Content-Type"] = "text/plain";
|
||||
response.StatusCode = 410;
|
||||
response.Content = ex.getFormattedMessage();
|
||||
return true;
|
||||
}
|
||||
catch (sg_error & ex)
|
||||
{
|
||||
SG_LOG(SG_NETWORK, SG_INFO, ex.getFormattedMessage());
|
||||
response.Header["Content-Type"] = "text/plain";
|
||||
response.StatusCode = 500;
|
||||
response.Content = ex.getFormattedMessage();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!stream) {
|
||||
response.Header["Content-Type"] = string("image/").append(type);
|
||||
response.Header["Content-Disposition"] = string("inline; filename=\"fgfs-screen.").append(type).append("\"");
|
||||
} else {
|
||||
response.Header["Content-Type"] = string("multipart/x-mixed-replace; boundary=" BOUNDARY);
|
||||
|
||||
}
|
||||
|
||||
if (canvasindex == -1)
|
||||
connection->put(KEY_SCREENSHOT, screenshotRequest);
|
||||
else
|
||||
connection->put(KEY_CANVASIMAGE, canvasimageRequest);
|
||||
return false; // call me again thru poll
|
||||
}
|
||||
|
||||
bool ScreenshotUriHandler::poll(Connection * connection)
|
||||
{
|
||||
SGSharedPtr<ConnectionData> data = connection->get(KEY_SCREENSHOT);
|
||||
if (data) {
|
||||
ScreenshotRequest * screenshotRequest = dynamic_cast<ScreenshotRequest*>(data.get());
|
||||
if ( NULL == screenshotRequest) return true; // Should not happen, kill the connection
|
||||
|
||||
const string & screenshot = screenshotRequest->getScreenshot();
|
||||
if (screenshot.empty()) {
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "No screenshot available.");
|
||||
return false; // not ready yet, call again.
|
||||
}
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "Screenshot is ready, size=" << screenshot.size());
|
||||
|
||||
if (screenshotRequest->isStream()) {
|
||||
std::ostringstream ss;
|
||||
ss << BOUNDARY << "\r\nContent-Type: image/";
|
||||
ss << screenshotRequest->getType() << "\r\nContent-Length:";
|
||||
ss << screenshot.size() << "\r\n\r\n";
|
||||
connection->write(ss.str().c_str(), ss.str().length());
|
||||
}
|
||||
|
||||
connection->write(screenshot.data(), screenshot.size());
|
||||
|
||||
if (screenshotRequest->isStream()) {
|
||||
screenshotRequest->requestScreenshot();
|
||||
// continue until user closes connection
|
||||
return false;
|
||||
}
|
||||
|
||||
// single screenshot, send terminating chunk
|
||||
connection->remove(KEY_SCREENSHOT);
|
||||
connection->write("", 0);
|
||||
return true; // done.
|
||||
} // Screenshot
|
||||
|
||||
// CanvasImage
|
||||
data = connection->get(KEY_CANVASIMAGE);
|
||||
CanvasImageRequest * canvasimageRequest = dynamic_cast<CanvasImageRequest*> (data.get());
|
||||
if (NULL == canvasimageRequest) return true; // Should not happen, kill the connection
|
||||
|
||||
if (!canvasimageRequest->connected) {
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "CanvasImageRequest: not connected. Resubscribing");
|
||||
canvasimageRequest->requestCanvasImage();
|
||||
}
|
||||
|
||||
const string & canvasimage = canvasimageRequest->getCanvasImage();
|
||||
if (canvasimage.empty()) {
|
||||
SG_LOG(SG_NETWORK, SG_INFO, "No canvasimage available.");
|
||||
return false; // not ready yet, call again.
|
||||
}
|
||||
|
||||
SG_LOG(SG_NETWORK, SG_DEBUG, "CanvasImage is ready, size=" << canvasimage.size());
|
||||
|
||||
if (canvasimageRequest->isStream()) {
|
||||
std::ostringstream ss;
|
||||
ss << BOUNDARY << "\r\nContent-Type: image/";
|
||||
ss << canvasimageRequest->getType() << "\r\nContent-Length:";
|
||||
ss << canvasimage.size() << "\r\n\r\n";
|
||||
connection->write(ss.str().c_str(), ss.str().length());
|
||||
}
|
||||
connection->write(canvasimage.data(), canvasimage.size());
|
||||
if (canvasimageRequest->isStream()) {
|
||||
canvasimageRequest->requestCanvasImage();
|
||||
// continue until user closes connection
|
||||
return false;
|
||||
}
|
||||
|
||||
// single canvasimage, send terminating chunk
|
||||
connection->remove(KEY_CANVASIMAGE);
|
||||
connection->write("", 0);
|
||||
return true; // done.
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
40
src/Network/http/ScreenshotUriHandler.hxx
Normal file
40
src/Network/http/ScreenshotUriHandler.hxx
Normal file
@@ -0,0 +1,40 @@
|
||||
// ScreenshotUriHandler.hxx -- Provide screenshots via http
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_SCREENSHOT_URI_HANDLER_HXX
|
||||
#define __FG_SCREENSHOT_URI_HANDLER_HXX
|
||||
|
||||
#include "urihandler.hxx"
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class ScreenshotUriHandler : public URIHandler {
|
||||
public:
|
||||
ScreenshotUriHandler( const std::string& uri = "/screenshot/" );
|
||||
~ScreenshotUriHandler();
|
||||
virtual bool handleGetRequest( const HTTPRequest & request, HTTPResponse & response, Connection * connection );
|
||||
virtual bool poll( Connection * connection );
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif //#define __FG_SCREENSHOT_URI_HANDLER_HXX
|
||||
54
src/Network/http/SimpleDOM.cxx
Normal file
54
src/Network/http/SimpleDOM.cxx
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "SimpleDOM.hxx"
|
||||
using std::string;
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
DOMNode::~DOMNode()
|
||||
{
|
||||
for( Children_t::const_iterator it = _children.begin(); it != _children.end(); ++it )
|
||||
delete *it;
|
||||
}
|
||||
|
||||
string DOMNode::render() const
|
||||
{
|
||||
string reply;
|
||||
reply.append( "<" ).append( _name );
|
||||
for( Attributes_t::const_iterator it = _attributes.begin(); it != _attributes.end(); ++it ) {
|
||||
reply.append( " " );
|
||||
reply.append( it->first );
|
||||
reply.append( "=\"" );
|
||||
reply.append( it->second );
|
||||
reply.append( "\"" );
|
||||
}
|
||||
|
||||
if( _children.empty() ) {
|
||||
reply.append( " />\r" );
|
||||
return reply;
|
||||
} else {
|
||||
reply.append( ">" );
|
||||
}
|
||||
|
||||
for( Children_t::const_iterator it = _children.begin(); it != _children.end(); ++it ) {
|
||||
reply.append( (*it)->render() );
|
||||
}
|
||||
|
||||
reply.append( "</" ).append( _name ).append( ">\r" );
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
DOMNode * DOMNode::addChild( DOMElement * child )
|
||||
{
|
||||
_children.push_back( child );
|
||||
return dynamic_cast<DOMNode*>(child);
|
||||
}
|
||||
|
||||
DOMNode * DOMNode::setAttribute( const string & name, const string & value )
|
||||
{
|
||||
_attributes[name] = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
64
src/Network/http/SimpleDOM.hxx
Normal file
64
src/Network/http/SimpleDOM.hxx
Normal file
@@ -0,0 +1,64 @@
|
||||
// SimpleDOM.hxx -- poor man's DOM
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef __FG_SIMPLE_DOM_HXX
|
||||
#define __FG_SIMPLE_DOM_HXX
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class DOMElement {
|
||||
public:
|
||||
virtual ~DOMElement() {}
|
||||
virtual std::string render() const = 0;
|
||||
};
|
||||
|
||||
class DOMTextElement : public DOMElement {
|
||||
public:
|
||||
DOMTextElement( const std::string & text ) : _text(text) {}
|
||||
virtual std::string render() const { return _text; }
|
||||
|
||||
private:
|
||||
std::string _text;
|
||||
};
|
||||
|
||||
class DOMNode : public DOMElement {
|
||||
public:
|
||||
DOMNode( const std::string & name ) : _name(name) {}
|
||||
virtual ~DOMNode();
|
||||
|
||||
virtual std::string render() const;
|
||||
virtual DOMNode * addChild( DOMElement * child );
|
||||
virtual DOMNode * setAttribute( const std::string & name, const std::string & value );
|
||||
protected:
|
||||
std::string _name;
|
||||
typedef std::vector<const DOMElement*> Children_t;
|
||||
typedef std::map<std::string,std::string> Attributes_t;
|
||||
Children_t _children;
|
||||
Attributes_t _attributes;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif // __FG_SIMPLE_DOM_HXX
|
||||
80
src/Network/http/Websocket.hxx
Normal file
80
src/Network/http/Websocket.hxx
Normal file
@@ -0,0 +1,80 @@
|
||||
// Websocket.cxx -- a base class for websockets
|
||||
//
|
||||
// Written by Torsten Dreyer, started April 2014.
|
||||
//
|
||||
// Copyright (C) 2014 Torsten Dreyer
|
||||
//
|
||||
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#ifndef WEBSOCKET_HXX_
|
||||
#define WEBSOCKET_HXX_
|
||||
|
||||
#include "HTTPRequest.hxx"
|
||||
#include <string>
|
||||
|
||||
namespace flightgear {
|
||||
namespace http {
|
||||
|
||||
class WebsocketWriter {
|
||||
public:
|
||||
virtual ~WebsocketWriter()
|
||||
{
|
||||
}
|
||||
|
||||
virtual int writeToWebsocket(int opcode, const char * data, size_t len) = 0;
|
||||
|
||||
// ref: http://tools.ietf.org/html/rfc6455#section-5.2
|
||||
int writeContinuation(const char * data, size_t len); // { return writeToWebsocket( 0, data, len ); }
|
||||
int writeText(const char * data, size_t len)
|
||||
{
|
||||
return writeToWebsocket(1, data, len);
|
||||
}
|
||||
inline int writeText(const std::string & text)
|
||||
{
|
||||
return writeText(text.c_str(), text.length());
|
||||
}
|
||||
inline int writeBinary(const char * data, size_t len)
|
||||
{
|
||||
return writeToWebsocket(2, data, len);
|
||||
}
|
||||
inline int writeConnectionClose(const char * data, size_t len)
|
||||
{
|
||||
return writeToWebsocket(8, data, len);
|
||||
}
|
||||
inline int writePing(const char * data, size_t len)
|
||||
{
|
||||
return writeToWebsocket(9, data, len);
|
||||
}
|
||||
inline int writePong(const char * data, size_t len)
|
||||
{
|
||||
return writeToWebsocket(0xa, data, len);
|
||||
}
|
||||
};
|
||||
|
||||
class Websocket {
|
||||
public:
|
||||
virtual ~Websocket()
|
||||
{
|
||||
}
|
||||
virtual void close() = 0;
|
||||
virtual void handleRequest(const HTTPRequest & request, WebsocketWriter & writer) = 0;
|
||||
virtual void poll(WebsocketWriter & writer) = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
} // namespace flightgear
|
||||
|
||||
#endif /* WEBSOCKET_HXX_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user