1
0
Fork 0

Expose FlightHistory as a http service

usage:
GET http://localhost:8080/flighthistory/track.json
retrieves track as JSON data
{
  flightHistory: [
    {
      latitude: (number),
      longitude: (number),
      altitude: (number)
    }
  ]
}

GET http://localhost:8080/flighthistory/track.kml
retrieves track as KML path
optional request parameter:
LineColor=(hex encoded rgba color)
LineWidth=(line width in pixel)
PolyColor=(hex encoded rgba color)
interval=(number of seconds to auto-refresh)
This commit is contained in:
Torsten Dreyer 2015-02-23 16:33:20 +01:00
parent 68c9adb489
commit cc6178a9f3
7 changed files with 444 additions and 78 deletions

View file

@ -5,12 +5,14 @@ set(SOURCES
ScreenshotUriHandler.cxx
PropertyUriHandler.cxx
JsonUriHandler.cxx
FlightHistoryUriHandler.cxx
PkgUriHandler.cxx
RunUriHandler.cxx
NavdbUriHandler.cxx
PropertyChangeWebsocket.cxx
PropertyChangeObserver.cxx
jsonprops.cxx
SimpleDOM.cxx
)
set(HEADERS
@ -19,6 +21,7 @@ set(HEADERS
ScreenshotUriHandler.hxx
PropertyUriHandler.hxx
JsonUriHandler.hxx
FlightHistoryUriHandler.hxx
PkgUriHandler.hxx
RunUriHandler.hxx
NavdbUriHandler.hxx
@ -27,6 +30,7 @@ set(HEADERS
PropertyChangeWebsocket.hxx
PropertyChangeObserver.hxx
jsonprops.hxx
SimpleDOM.hxx
)
flightgear_component(Http "${SOURCES}" "${HEADERS}")

View file

@ -0,0 +1,275 @@
// 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 <3rdparty/cjson/cJSON.h>
#include <Aircraft/FlightHistory.hxx>
#include <Main/fg_props.hxx>
#include <sstream>
using std::string;
using std::stringstream;
namespace flightgear {
namespace http {
static cJSON * GeodToJson(const SGGeod & geod) {
cJSON * json = cJSON_CreateObject();
cJSON_AddItemToObject(json, "latitude",
cJSON_CreateNumber(geod.getLatitudeDeg()));
cJSON_AddItemToObject(json, "longitude",
cJSON_CreateNumber(geod.getLongitudeDeg()));
cJSON_AddItemToObject(json, "altitude",
cJSON_CreateNumber(geod.getElevationFt()));
return json;
}
static string FlightHistoryToJson(const SGGeodVec & history) {
cJSON * json = cJSON_CreateObject();
cJSON * jsonArray = cJSON_CreateArray();
for (SGGeodVec::const_iterator it = history.begin(); it != history.end();
++it) {
cJSON_AddItemToArray(jsonArray, GeodToJson(*it));
}
cJSON_AddItemToObject(json, "flightHistory", jsonArray);
char * jsonString = cJSON_PrintUnformatted(json);
string reply(jsonString);
free(jsonString);
cJSON_Delete(json);
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 (false == 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") {
response.Header["Content-Type"] = "application/json; charset=UTF-8";
response.Content = FlightHistoryToJson(
history->pathForHistory(minEdgeLengthM));
} else {
response.StatusCode = 404;
response.Content = "{}";
}
return true;
}
} // namespace http
} // namespace flightgear

View 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 char * 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

View file

@ -20,6 +20,7 @@
#include "PropertyUriHandler.hxx"
#include "SimpleDOM.hxx"
#include <Main/fg_props.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
@ -63,84 +64,6 @@ static inline std::string htmlSpecialChars( const std::string & s )
return reply;
}
class DOMElement {
public:
virtual ~DOMElement() {}
virtual std::string render() const = 0;
};
class DOMTextElement : public DOMElement {
public:
DOMTextElement( const string & text ) : _text(text) {}
virtual std::string render() const { return _text; }
private:
string _text;
};
class DOMNode : public DOMElement {
public:
DOMNode( const string & name ) : _name(name) {}
virtual ~DOMNode();
virtual std::string render() const;
virtual DOMNode * addChild( DOMElement * child );
virtual DOMNode * setAttribute( const string & name, const string & value );
protected:
string _name;
typedef vector<const DOMElement*> Children_t;
typedef map<string,string> Attributes_t;
Children_t _children;
Attributes_t _attributes;
};
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;
}
class SortedChilds : public simgear::PropertyList {
public:
SortedChilds( SGPropertyNode_ptr node ) {

View 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;
}
}
}

View 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

View file

@ -24,6 +24,7 @@
#include "ScreenshotUriHandler.hxx"
#include "PropertyUriHandler.hxx"
#include "JsonUriHandler.hxx"
#include "FlightHistoryUriHandler.hxx"
#include "PkgUriHandler.hxx"
#include "RunUriHandler.hxx"
#include "NavdbUriHandler.hxx"
@ -459,6 +460,11 @@ void MongooseHttpd::init()
}
//#endif
if ((uri = n->getStringValue("flighthistory"))[0] != 0) {
SG_LOG(SG_NETWORK, SG_INFO, "httpd: adding flighthistory uri handler at " << uri);
_uriHandler.push_back(new flightgear::http::FlightHistoryUriHandler(uri));
}
if ((uri = n->getStringValue("run"))[0] != 0) {
SG_LOG(SG_NETWORK, SG_INFO, "httpd: adding run uri handler at " << uri);
_uriHandler.push_back(new flightgear::http::RunUriHandler(uri));