]> git.mxchange.org Git - flightgear.git/commitdiff
Expose FlightHistory as a http service
authorTorsten Dreyer <torsten@ŧ3r.de>
Mon, 23 Feb 2015 15:33:20 +0000 (16:33 +0100)
committerTorsten Dreyer <torsten@ŧ3r.de>
Mon, 23 Feb 2015 15:33:58 +0000 (16:33 +0100)
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)

src/Network/http/CMakeLists.txt
src/Network/http/FlightHistoryUriHandler.cxx [new file with mode: 0644]
src/Network/http/FlightHistoryUriHandler.hxx [new file with mode: 0644]
src/Network/http/PropertyUriHandler.cxx
src/Network/http/SimpleDOM.cxx [new file with mode: 0644]
src/Network/http/SimpleDOM.hxx [new file with mode: 0644]
src/Network/http/httpd.cxx

index 44078faf10739524777165bb2d21f34f5a4a7ce1..9e913d0ccb4710d1ff73f574e4bc1213767b818a 100644 (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}")
diff --git a/src/Network/http/FlightHistoryUriHandler.cxx b/src/Network/http/FlightHistoryUriHandler.cxx
new file mode 100644 (file)
index 0000000..b92b90b
--- /dev/null
@@ -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
+
diff --git a/src/Network/http/FlightHistoryUriHandler.hxx b/src/Network/http/FlightHistoryUriHandler.hxx
new file mode 100644 (file)
index 0000000..9c17829
--- /dev/null
@@ -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
index 3fe9c33b9286436697f3751cb827e5d3b39d9925..01b28c0f2a6f7548bee8ee07c455a92d7f441e0e 100644 (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 ) {
diff --git a/src/Network/http/SimpleDOM.cxx b/src/Network/http/SimpleDOM.cxx
new file mode 100644 (file)
index 0000000..bee2e29
--- /dev/null
@@ -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;
+}
+
+}
+}
diff --git a/src/Network/http/SimpleDOM.hxx b/src/Network/http/SimpleDOM.hxx
new file mode 100644 (file)
index 0000000..fc5126a
--- /dev/null
@@ -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
index 829acf69a8399da018b9752489fd10a985b5956c..3f38be83248f4f78281d2c1bcf2979465f01daf7 100644 (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));