FlightGear next
FlightHistoryUriHandler.cxx
Go to the documentation of this file.
1// FlightHistoryUriHandler.cxx -- FlightHistory service
2//
3// Written by Torsten Dreyer, started February 2015.
4//
5// Copyright (C) 2015 Torsten Dreyer
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
22#include "SimpleDOM.hxx"
23#include <cJSON.h>
24
26#include <Main/fg_props.hxx>
27#include <sstream>
28
29using std::string;
30using std::stringstream;
31
32namespace flightgear {
33namespace http {
34
35/*
36{
37 type: "Feature",
38 geometry: {
39 type: "LineString",
40 coordinates: [lon,lat,alt],..]
41 },
42 properties: {
43 type: "FlightHistory"
44 },
45}
46*/
47
48static const char * errorPage =
49 "<html><head><title>Flight History</title></head><body>"
50 "<h1>Flight History</h1>"
51 "Supported formats:"
52 "<ul>"
53 "<li><a href='track.json'>JSON</a></li>"
54 "<li><a href='track.kml'>KML</a></li>"
55 "</ul>"
56 "</body></html>";
57
58static string FlightHistoryToJson(const SGGeodVec & history, size_t last_seen ) {
59 cJSON * feature = cJSON_CreateObject();
60 cJSON_AddItemToObject(feature, "type", cJSON_CreateString("Feature"));
61
62 cJSON * lineString = cJSON_CreateObject();
63 cJSON_AddItemToObject(feature, "geometry", lineString );
64
65 cJSON * properties = cJSON_CreateObject();
66 cJSON_AddItemToObject(feature, "properties", properties );
67 cJSON_AddItemToObject(properties, "type", cJSON_CreateString("FlightHistory"));
68 cJSON_AddItemToObject(properties, "last", cJSON_CreateNumber(last_seen));
69
70 cJSON_AddItemToObject(lineString, "type", cJSON_CreateString("LineString"));
71 cJSON * coordinates = cJSON_CreateArray();
72 cJSON_AddItemToObject(lineString, "coordinates", coordinates);
73 for (SGGeodVec::const_iterator it = history.begin(); it != history.end();
74 ++it) {
75 cJSON * coordinate = cJSON_CreateArray();
76 cJSON_AddItemToArray(coordinates, coordinate);
77
78 cJSON_AddItemToArray(coordinate, cJSON_CreateNumber(it->getLongitudeDeg()));
79 cJSON_AddItemToArray(coordinate, cJSON_CreateNumber(it->getLatitudeDeg()));
80 cJSON_AddItemToArray(coordinate, cJSON_CreateNumber(it->getElevationM()));
81
82 }
83
84 char * jsonString = cJSON_PrintUnformatted(feature);
85 string reply(jsonString);
86 free(jsonString);
87 cJSON_Delete(lineString);
88 return reply;
89}
90
91static string AutoUpdateResponse(const HTTPRequest & request,
92 const string & base, const string & interval) {
93
94 string url = "http://";
95 url.append(request.HeaderVariables.get("Host")).append(base);
96
97 std::string reply("<?xml version='1.0' encoding='UTF-8'?>");
98 DOMNode * kml = new DOMNode("kml");
99 kml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
100
101 DOMNode * document = kml->addChild(new DOMNode("Document"));
102
103 DOMNode * networkLink = document->addChild(new DOMNode("NetworkLink"));
104 DOMNode * link = networkLink->addChild(new DOMNode("Link"));
105 link->addChild(new DOMNode("href"))->addChild(new DOMTextElement(url));
106 link->addChild(new DOMNode("refreshMode"))->addChild(
107 new DOMTextElement("onInterval"));
108 link->addChild(new DOMNode("refreshInterval"))->addChild(
109 new DOMTextElement(interval.empty() ? "10" : interval));
110
111 reply.append(kml->render());
112 delete kml;
113 return reply;
114}
115
116static string FlightHistoryToKml(const SGGeodVec & history,
117 const HTTPRequest & request) {
118 string interval = request.RequestVariables.get("interval");
119 if (!interval.empty()) {
120 return AutoUpdateResponse(request, "/flighthistory/track.kml", interval);
121 }
122
123 std::string reply("<?xml version='1.0' encoding='UTF-8'?>");
124 DOMNode * kml = new DOMNode("kml");
125 kml->setAttribute("xmlns", "http://www.opengis.net/kml/2.2");
126
127 DOMNode * document = kml->addChild(new DOMNode("Document"));
128
129 document->addChild(new DOMNode("name"))->addChild(
130 new DOMTextElement("FlightGear"));
131 document->addChild(new DOMNode("description"))->addChild(
132 new DOMTextElement("FlightGear Flight History"));
133
134 DOMNode * style = document->addChild(new DOMNode("Style"))->setAttribute(
135 "id", "flight-history");
136 DOMNode * lineStyle = style->addChild(new DOMNode("LineStyle"));
137
138 string lineColor = request.RequestVariables.get("LineColor");
139 string lineWidth = request.RequestVariables.get("LineWidth");
140 string polyColor = request.RequestVariables.get("PolyColor");
141
142 lineStyle->addChild(new DOMNode("color"))->addChild(
143 new DOMTextElement(lineColor.empty() ? "427ebfff" : lineColor));
144 lineStyle->addChild(new DOMNode("width"))->addChild(
145 new DOMTextElement(lineWidth.empty() ? "4" : lineWidth));
146
147 lineStyle = style->addChild(new DOMNode("PolyStyle"));
148 lineStyle->addChild(new DOMNode("color"))->addChild(
149 new DOMTextElement(polyColor.empty() ? "fbfc4600" : polyColor));
150
151 DOMNode * placemark = document->addChild(new DOMNode("Placemark"));
152 placemark->addChild(new DOMNode("name"))->addChild(
153 new DOMTextElement("Flight Path"));
154 placemark->addChild(new DOMNode("styleUrl"))->addChild(
155 new DOMTextElement("#flight-history"));
156
157 DOMNode * linestring = placemark->addChild(new DOMNode("LineString"));
158 linestring->addChild(new DOMNode("extrude"))->addChild(
159 new DOMTextElement("1"));
160 linestring->addChild(new DOMNode("tessalate"))->addChild(
161 new DOMTextElement("1"));
162 linestring->addChild(new DOMNode("altitudeMode"))->addChild(
163 new DOMTextElement("absolute"));
164
165 stringstream ss;
166
167 for (SGGeodVec::const_iterator it = history.begin(); it != history.end();
168 ++it) {
169 ss << (*it).getLongitudeDeg() << "," << (*it).getLatitudeDeg() << ","
170 << it->getElevationM() << " ";
171 }
172
173 linestring->addChild(new DOMNode("coordinates"))->addChild(
174 new DOMTextElement(ss.str()));
175
176 reply.append(kml->render());
177 delete kml;
178 return reply;
179}
180
181static bool GetJsonDouble(cJSON * json, const char * item, double & out) {
182 cJSON * cj = cJSON_GetObjectItem(json, item);
183 if (NULL == cj)
184 return false;
185
186 if (cj->type != cJSON_Number)
187 return false;
188
189 out = cj->valuedouble;
190
191 return true;
192}
193
194static bool GetJsonBool(cJSON * json, const char * item, bool & out) {
195 cJSON * cj = cJSON_GetObjectItem(json, item);
196 if (NULL == cj)
197 return false;
198
199 if (cj->type == cJSON_True) {
200 out = true;
201 return true;
202 }
203 if (cj->type == cJSON_False) {
204 out = true;
205 return true;
206
207 }
208 return false;
209}
210
212 HTTPResponse & response, Connection * connection) {
213 response.Header["Access-Control-Allow-Origin"] = "*";
214 response.Header["Access-Control-Allow-Methods"] = "OPTIONS, GET, POST";
215 response.Header["Access-Control-Allow-Headers"] =
216 "Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token";
217
218 if (request.Method == "OPTIONS") {
219 return true; // OPTIONS only needs the headers
220 }
221
222 auto history = globals->get_subsystem<FGFlightHistory>();
223
224 double minEdgeLengthM = 50;
225 string requestPath = request.Uri.substr(getUri().length());
226
227 if (request.Method == "GET") {
228 } else if (request.Method == "POST") {
229 /*
230 * {
231 * sampleIntervalSec: (number),
232 * maxMemoryUseBytes: (number),
233 * clearOnTakeoff: (bool),
234 * enabled: (bool),
235 * }
236 */
237 cJSON * json = cJSON_Parse(request.Content.c_str());
238 if ( NULL != json) {
239 double d = .0;
240 bool b = false;
241 bool doReinit = false;
242 if (GetJsonDouble(json, "sampleIntervalSec", d)) {
243 fgSetDouble("/sim/history/sample-interval-sec", d);
244 doReinit = true;
245 }
246 if (GetJsonDouble(json, "maxMemoryUseBytes", d)) {
247 fgSetDouble("/sim/history/max-memory-use-bytes", d);
248 doReinit = true;
249 }
250
251 if (GetJsonBool(json, "clearOnTakeoff", b)) {
252 fgSetBool("/sim/history/clear-on-takeoff", b);
253 doReinit = true;
254 }
255 if (GetJsonBool(json, "enabled", b)) {
256 fgSetBool("/sim/history/enabled", b);
257 }
258
259 if (doReinit) {
260 history->reinit();
261 }
262
263 cJSON_Delete(json);
264 }
265
266 response.Content = "{}";
267 return true;
268
269 } else {
270 SG_LOG(SG_NETWORK, SG_INFO,
271 "PkgUriHandler: invalid request method '" << request.Method << "'");
272 response.Header["Allow"] = "OPTIONS, GET, POST";
273 response.StatusCode = 405;
274 response.Content = "{}";
275 return true;
276 }
277
278 if (requestPath == "track.kml") {
279 response.Header["Content-Type"] =
280 "application/vnd.google-earth.kml+xml; charset=UTF-8";
281
282 response.Content = FlightHistoryToKml(
283 history->pathForHistory(minEdgeLengthM), request);
284
285 } else if (requestPath == "track.json") {
286 size_t count = -1;
287 try {
288 count = std::stoul(request.RequestVariables.get("count"));
289 }
290 catch( ... ) {
291 }
292 size_t last = 0;
293 try {
294 last = std::stoul(request.RequestVariables.get("last"));
295 }
296 catch( ... ) {
297 }
298
299 response.Header["Content-Type"] = "application/json; charset=UTF-8";
300 PagedPathForHistory_ptr h = history->pagedPathForHistory( count, last );
301 response.Content = FlightHistoryToJson( h->path, h->last_seen );
302
303 } else {
304 response.Header["Content-Type"] = "text/html";
305 response.Content = errorPage;
306 }
307
308 return true;
309}
310
311} // namespace http
312} // namespace flightgear
313
SGSharedPtr< PagedPathForHistory > PagedPathForHistory_ptr
record the history of the aircraft's movements, making it available as a contiguous block.
virtual std::string render() const
Definition SimpleDOM.cxx:13
virtual DOMNode * setAttribute(const std::string &name, const std::string &value)
Definition SimpleDOM.cxx:47
virtual DOMNode * addChild(DOMElement *child)
Definition SimpleDOM.cxx:41
virtual bool handleRequest(const HTTPRequest &request, HTTPResponse &response, Connection *connection)
This method gets called from the httpd if a request has been detected on the connection.
std::string get(const std::string &key) const
const std::string & getUri() const
Getter for the URI this handler serves.
FGGlobals * globals
Definition globals.cxx:142
static string FlightHistoryToJson(const SGGeodVec &history, size_t last_seen)
static bool GetJsonBool(cJSON *json, const char *item, bool &out)
static const char * errorPage
static string AutoUpdateResponse(const HTTPRequest &request, const string &base, const string &interval)
static string FlightHistoryToKml(const SGGeodVec &history, const HTTPRequest &request)
static bool GetJsonDouble(cJSON *json, const char *item, double &out)
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
std::vector< SGGeod > SGGeodVec
Definition PolyLine.hxx:36
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
Definition proptest.cpp:31
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
Definition proptest.cpp:24