From 9fa09e0e92b957d63a120fc888417f83e31d919e Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Wed, 10 Dec 2014 17:05:22 +0000 Subject: [PATCH] receiver.json support, internal webserver cleanup. Add data/receiver.json (generated once) and support for it in script.js. Internal webserver rearrangement to support multiple json files. --- debian/changelog | 2 + dump1090.c | 19 +++++-- dump1090.h | 10 ++-- net_io.c | 122 +++++++++++++++++++++++++++++------------- public_html/script.js | 43 ++++++++++----- 5 files changed, 138 insertions(+), 58 deletions(-) diff --git a/debian/changelog b/debian/changelog index 60ac286..5da9885 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,8 @@ dump1090-mutability (1.08.2302.14+1mu-3) UNRELEASED; urgency=medium * Be much more cautious about missing config settings so we don't explode so badly if something is omitted. * Use the package version as the version number compiled into the binary. + * Add data/receiver.json (generated once) and support for it in script.js. + * Internal webserver rearrangement to support multiple json files. -- Oliver Jowett Wed, 10 Dec 2014 02:01:27 +0000 diff --git a/dump1090.c b/dump1090.c index 46a7cdb..619423a 100644 --- a/dump1090.c +++ b/dump1090.c @@ -634,14 +634,15 @@ void backgroundTasks(void) { } } - if (Modes.json_path && Modes.json_interval > 0) { + if (Modes.json_aircraft_path && Modes.json_interval > 0) { time_t now = time(NULL); if (now >= next_json) { - modesWriteJson(Modes.json_path); + writeJsonToFile(Modes.json_aircraft_path, generateAircraftJson); next_json = now + Modes.json_interval; } } } + // //========================================================================= // @@ -837,9 +838,13 @@ int main(int argc, char **argv) { #ifndef _WIN32 } else if (!strcmp(argv[j], "--write-json") && more) { ++j; - Modes.json_path = malloc(strlen(argv[j]) + 15); - strcpy(Modes.json_path, argv[j]); - strcat(Modes.json_path, "/aircraft.json"); + Modes.json_aircraft_path = malloc(strlen(argv[j]) + 15); + strcpy(Modes.json_aircraft_path, argv[j]); + strcat(Modes.json_aircraft_path, "/aircraft.json"); + + Modes.json_metadata_path = malloc(strlen(argv[j]) + 15); + strcpy(Modes.json_metadata_path, argv[j]); + strcat(Modes.json_metadata_path, "/receiver.json"); } else if (!strcmp(argv[j], "--write-json-every") && more) { Modes.json_interval = atoi(argv[++j]); #endif @@ -908,6 +913,10 @@ int main(int argc, char **argv) { } if (Modes.net) modesInitNet(); + if (Modes.json_metadata_path && Modes.json_interval > 0) { + writeJsonToFile(Modes.json_metadata_path, generateReceiverJson); // once only on startup + } + // If the user specifies --net-only, just run in order to serve network // clients without reading data from the RTL device while (Modes.net_only) { diff --git a/dump1090.h b/dump1090.h index 4dac2bb..909cf68 100644 --- a/dump1090.h +++ b/dump1090.h @@ -369,8 +369,9 @@ struct { // Internal state int mlat; // Use Beast ascii format for raw data output, i.e. @...; iso *...; int interactive_rtl1090; // flight table in interactive mode is formatted like RTL1090 int no_decode; // Disable decoding and aircraft tracking - char *json_path; // Path to json data file to write, or NULL not to. - int json_interval; // Interval between rewriting the json data file + char *json_aircraft_path; // Path to json aircraft file to write, or NULL not to. + char *json_metadata_path; // Path to json metadata file to write, or NULL not to. + int json_interval; // Interval between rewriting the json aircraft file // User details double fUserLat; // Users receiver/antenna lat/lon needed for initial surface location @@ -495,7 +496,10 @@ void modesInitNet (void); void modesQueueOutput (struct modesMessage *mm); void modesReadFromClient(struct client *c, char *sep, int(*handler)(struct client *, char *)); void modesNetPeriodicWork (void); -void modesWriteJson (const char *path); + +void writeJsonToFile(const char *path, char * (*generator) (int*)); +char *generateAircraftJson(int *len); +char *generateReceiverJson(int *len); #ifdef __cplusplus } diff --git a/net_io.c b/net_io.c index c1b7b53..a55b0e2 100644 --- a/net_io.c +++ b/net_io.c @@ -667,7 +667,7 @@ int decodeHexMessage(struct client *c, char *hex) { // // Return a description of planes in json. No metric conversion // -char *aircraftsToJson(int *len) { +char *generateAircraftJson(int *len) { time_t now = time(NULL); struct aircraft *a = Modes.aircrafts; int buflen = 1024; // The initial buffer is incremented as needed @@ -727,15 +727,40 @@ char *aircraftsToJson(int *len) { return buf; } -// Write JSON state to json_path -void modesWriteJson(const char *path) +// +// Return a description of the receiver in json. +// +char *generateReceiverJson(int *len) +{ + char *buf = (char *) malloc(1024), *p = buf; + + p += sprintf(p, "{ " \ + "\"version\" : \"%s\", " + "\"refresh\" : %d", + MODES_DUMP1090_VERSION, Modes.json_interval * 1000); + + if (Modes.fUserLat != 0.0 || Modes.fUserLon != 0.0) { + p += sprintf(p, ", " \ + "\"lat\" : %.2f, " + "\"lon\" : %.2f", + Modes.fUserLat, Modes.fUserLon); // round to 2dp - about 0.5-1km accuracy - for privacy reasons + } + + p += sprintf(p, " }\n"); + + *len = (p - buf); + return buf; +} + +// Write JSON to file +void writeJsonToFile(const char *path, char * (*generator) (int*)) { #ifndef _WIN32 char tmppath[PATH_MAX]; int fd; int len = 0; - char *content; mode_t mask; + char *content; snprintf(tmppath, PATH_MAX, "%s.XXXXXX", path); tmppath[PATH_MAX-1] = 0; @@ -747,27 +772,29 @@ void modesWriteJson(const char *path) umask(mask); fchmod(fd, 0644 & ~mask); - content = aircraftsToJson(&len); + content = generator(&len); + if (write(fd, content, len) != len) goto error_1; if (close(fd) < 0) goto error_2; - free(content); rename(tmppath, path); + free(content); return; error_1: close(fd); error_2: - free(content); unlink(tmppath); + free(content); return; - #endif } + + // //========================================================================= // @@ -775,6 +802,17 @@ void modesWriteJson(const char *path) #define MODES_CONTENT_TYPE_CSS "text/css;charset=utf-8" #define MODES_CONTENT_TYPE_JSON "application/json;charset=utf-8" #define MODES_CONTENT_TYPE_JS "application/javascript;charset=utf-8" + +static struct { + char *path; + char * (*handler)(int*); + char *content_type; +} url_handlers[] = { + { "/data/aircraft.json", generateAircraftJson, MODES_CONTENT_TYPE_JSON }, + { "/data/receiver.json", generateReceiverJson, MODES_CONTENT_TYPE_JSON }, + { NULL, NULL, NULL } +}; + // // Get an HTTP request header and write the response to the client. // gain here we assume that the socket buffer is enough without doing @@ -788,10 +826,10 @@ int handleHTTPRequest(struct client *c, char *p) { int clen, hdrlen; int httpver, keepalive; int statuscode = 500; - char *url, *content; - char ctype[48]; - char getFile[1024]; + char *url, *content = NULL; char *ext; + char *content_type; + int i; if (Modes.debug & MODES_DEBUG_NET) printf("\nHTTP request: %s\n", c->buf); @@ -820,23 +858,30 @@ int handleHTTPRequest(struct client *c, char *p) { printf("HTTP requested URL: %s\n\n", url); } - if (strlen(url) < 2) { - snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file - } else { - snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); + statuscode = 404; + for (i = 0; url_handlers[i].path; ++i) { + if (!strcmp(url, url_handlers[i].path)) { + content_type = url_handlers[i].content_type; + content = url_handlers[i].handler(&clen); + statuscode = 200; + if (Modes.debug & MODES_DEBUG_NET) { + printf("HTTP: 200: %s -> internal (%d bytes, %s)\n", url, clen, content_type); + } + break; + } } - - // Select the content to send, we have just two so far: - // "/" -> Our google map application. - // "/aircraft.json" -> Our ajax request to update planes. - if (strstr(url, "/data/aircraft.json")) { - statuscode = 200; - content = aircraftsToJson(&clen); - //snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); - } else { + + if (!content) { struct stat sbuf; int fd = -1; char rp[PATH_MAX], hrp[PATH_MAX]; + char getFile[1024]; + + if (strlen(url) < 2) { + snprintf(getFile, sizeof getFile, "%s/gmap.html", HTMLPATH); // Default file + } else { + snprintf(getFile, sizeof getFile, "%s/%s", HTMLPATH, url); + } if (!realpath(getFile, rp)) rp[0] = 0; @@ -866,22 +911,27 @@ int handleHTTPRequest(struct client *c, char *p) { if (fd != -1) { close(fd); } - } - // Get file extension and content type - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_HTML); // Default content type - ext = strrchr(getFile, '.'); + // Get file extension and content type + content_type = MODES_CONTENT_TYPE_HTML; // Default content type + ext = strrchr(getFile, '.'); + + if (strlen(ext) > 0) { + if (strstr(ext, ".json")) { + content_type = MODES_CONTENT_TYPE_JSON; + } else if (strstr(ext, ".css")) { + content_type = MODES_CONTENT_TYPE_CSS; + } else if (strstr(ext, ".js")) { + content_type = MODES_CONTENT_TYPE_JS; + } + } - if (strlen(ext) > 0) { - if (strstr(ext, ".json")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JSON); - } else if (strstr(ext, ".css")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_CSS); - } else if (strstr(ext, ".js")) { - snprintf(ctype, sizeof ctype, MODES_CONTENT_TYPE_JS); + if (Modes.debug & MODES_DEBUG_NET) { + printf("HTTP: %d: %s -> %s (%d bytes, %s)\n", statuscode, url, rp, clen, content_type); } } + // Create the header and send the reply hdrlen = snprintf(hdr, sizeof(hdr), "HTTP/1.1 %d \r\n" @@ -893,7 +943,7 @@ int handleHTTPRequest(struct client *c, char *p) { "Expires: Sat, 26 Jul 1997 05:00:00 GMT\r\n" "\r\n", statuscode, - ctype, + content_type, keepalive ? "keep-alive" : "close", clen); diff --git a/public_html/script.js b/public_html/script.js index cf12e41..83aa95e 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -17,6 +17,9 @@ CenterLat = Number(localStorage['CenterLat']) || CONST_CENTERLAT; CenterLon = Number(localStorage['CenterLon']) || CONST_CENTERLON; ZoomLvl = Number(localStorage['ZoomLvl']) || CONST_ZOOMLVL; +Dump1090Version = "unknown version"; +RefreshInterval = 1000; + function fetchData() { $.getJSON('data/aircraft.json', function(data) { PlanesOnMap = 0 @@ -32,29 +35,41 @@ function fetchData() { var plane = jQuery.extend(true, {}, planeObject); } - /* For special squawk tests - if (data[j].hex == '48413x') { - data[j].squawk = '7700'; - } //*/ - - // Set SpecialSquawk-value - if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') { - SpecialSquawk = true; - } - + // Set SpecialSquawk-value + if (data[j].squawk == '7500' || data[j].squawk == '7600' || data[j].squawk == '7700') { + SpecialSquawk = true; + } + // Call the function update plane.funcUpdateData(data[j]); // Copy the plane into Planes Planes[plane.icao] = plane; } - + PlanesOnTable = data.length; }); } -// Initalizes the map and starts up our timers to call various functions function initialize() { + // Get receiver metadata, reconfigure using it, then continue + // with initialization + $.getJSON('data/receiver.json') + .done(function(data) { + if (typeof data.receiverlat !== "undefined") { + SiteShow = true; + SiteLat = data.receiverlat; + SiteLon = data.receiverlon; + } + + Dump1090Version = data.version; + RefreshInterval = data.refresh; + }) + .always(initialize_after_config); +} + +// Initalizes the map and starts up our timers to call various functions +function initialize_after_config() { // Make a list of all the available map IDs var mapTypeIds = []; for(var type in google.maps.MapTypeId) { @@ -209,7 +224,7 @@ function initialize() { refreshSelected(); reaper(); extendedPulse(); - }, 1000); + }, RefreshInterval); } // This looks for planes to reap out of the master Planes variable @@ -257,7 +272,7 @@ function refreshSelected() { html += '' + selected.flight + ''; } else { - html += 'DUMP1090'; + html += 'DUMP1090 ' + Dump1090Version + ''; } if (selected && selected.squawk == 7500) { // Lets hope we never see this... Aircraft Hijacking