From 5d9c02bb1ca8a99e08800be88af9a9e02caf1041 Mon Sep 17 00:00:00 2001 From: Jeremy Tan Date: Sat, 7 Sep 2013 21:52:29 +0800 Subject: [PATCH] Add experimental support for querying a sensor an arbitrary range of points --- server/fastcgi.c | 2 +- server/fastcgi.h | 3 +- server/sensor.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++ server/sensor.h | 3 ++ 4 files changed, 101 insertions(+), 2 deletions(-) diff --git a/server/fastcgi.c b/server/fastcgi.c index 1bd520b..e41e375 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -355,7 +355,7 @@ void * FCGI_RequestLoop (void *data) } else if (!strcmp("control", module)) { module_handler = Control_Handler; } else if (!strcmp("sensors", module)) { - module_handler = Sensor_Handler; + module_handler = Sensor_Handler2; } context.current_module = module; diff --git a/server/fastcgi.h b/server/fastcgi.h index f52a20a..9a37a6f 100644 --- a/server/fastcgi.h +++ b/server/fastcgi.h @@ -16,7 +16,8 @@ typedef enum StatusCodes { STATUS_OK = 1, STATUS_ERROR = -1, - STATUS_UNAUTHORIZED = -2 + STATUS_UNAUTHORIZED = -2, + STATUS_OUTOFRANGE = -3 } StatusCodes; typedef struct FCGIContext FCGIContext; diff --git a/server/sensor.c b/server/sensor.c index c0c3ab1..3d5aec7 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -135,6 +135,7 @@ void * Sensor_Main(void * arg) Fatal("Error collecting data"); } s->write_index += 1; + //TODO: s->points_read not used? } //Log(LOGDEBUG, "Filled buffer"); @@ -149,6 +150,7 @@ void * Sensor_Main(void * arg) { Fatal("Wrote %d data points and expected to write %d to \"%s\" - %s", amount_written, SENSOR_DATABUFSIZ, strerror(errno)); } + s->points_stored += amount_written; //Log(LOGDEBUG, "Wrote %d data points for sensor %d", amount_written, s->id); pthread_mutex_unlock(&(s->mutex)); // End of critical section @@ -203,6 +205,99 @@ Sensor * Sensor_Identify(const char * id_str) return g_sensors+id; } +/* + * Behaviour: + * Dump true: + * - from < 0: From beginning of file, else from that point onwards (0-indexed) + * - count < 0: All points available, else *at most* that many points + * Dump false: + * - from < 0: From the end of file (last available points), else from that point onwards (0-indexed) + * - count < 0: Default buffer size (SENSOR_QUERYBUFSIZ), else *at most* that many points + */ +void Sensor_Handler2(FCGIContext *context, char *params) +{ + const char *key, *value; + int id = -1, from = -1, count = -1; + bool dump = false; + + //Lazy checking + while ((params = FCGI_KeyPair(params, &key, &value))) { + if (!strcmp(key, "id") && *value) { + char *end; + id = strtol(value, &end, 10); + if (*end != '\0') + id = -1; + } else if (!strcmp(key, "dump")) { + dump = !dump; + } else if (!strcmp(key, "from") && *value) { + from = strtol(value, NULL, 10); + } else if (!strcmp(key, "count") && *value) { + count = strtol(value, NULL, 10); + } + } + + if (id < 0 || id >= NUMSENSORS) { + FCGI_RejectJSON(context, "Invalid sensor id specified."); + return; + } + + Sensor *sensor = &g_sensors[id]; + DataPoint buffer[SENSOR_QUERYBUFSIZ]; + int amount_read = 0, total = 0; + + //Critical section + pthread_mutex_lock(&(sensor->mutex)); + if (from >= sensor->points_stored) { + FCGI_RejectJSONEx(context, STATUS_OUTOFRANGE, "Invalid range specified."); + } else if (dump) { + from = (from < 0) ? 0 : from; + count = (count < 0) ? sensor->points_stored : count; + + FCGI_PrintRaw("Content-type: text/plain\r\n" + "Content-disposition: attachment;filename=%d.csv\r\n\r\n", id); + + fseek(sensor->file, sizeof(DataPoint) * from, SEEK_SET); + //Force download with content-disposition + do { + amount_read = fread(buffer, sizeof(DataPoint), SENSOR_QUERYBUFSIZ, sensor->file); + for (int i = 0; i < amount_read && total < count; i++, total++) { + FCGI_PrintRaw("%f\t%f\n", buffer[i].time_stamp, buffer[i].value); + } + } while (amount_read > 0 && total < count); + } else { + count = (count < 0) ? SENSOR_QUERYBUFSIZ : count; + if (from < 0) { + from = sensor->points_stored - count; + if (from < 0) + from = 0; + } + fseek(sensor->file, sizeof(DataPoint) * from, SEEK_SET); + + FCGI_BeginJSON(context, STATUS_OK); + FCGI_JSONLong("id", id); + FCGI_JSONKey("data"); + FCGI_JSONValue("["); + while (total < count) { + amount_read = fread(buffer, sizeof(DataPoint), SENSOR_QUERYBUFSIZ, sensor->file); + if (amount_read > 0) { + FCGI_JSONValue("[%f, %f]", buffer[0].time_stamp, buffer[0].value); + total++; + for (int i = 1; i < amount_read && total < count; i++, total++) + FCGI_JSONValue(", [%f, %f]", buffer[i].time_stamp, buffer[i].value); + } else { + break; + } + } + FCGI_JSONValue("]"); + + FCGI_JSONLong("total_points", sensor->points_stored); + FCGI_JSONLong("next_point", from + total); + FCGI_EndJSON(); + } + pthread_mutex_unlock(&(sensor->mutex)); + //End critical section +} + /** * Handle a request to the sensor module * @param context - The context to work in diff --git a/server/sensor.h b/server/sensor.h index 8ae5660..9bdefda 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -46,6 +46,8 @@ typedef struct long points_read; /** Binary file to write data into when buffer is full **/ FILE * file; + /** Number of data points stored in file **/ + long points_stored; /** Thread running the sensor **/ pthread_t thread; /** Mutex to protect access to stuff **/ @@ -65,5 +67,6 @@ extern int Sensor_Query(Sensor * s, DataPoint * buffer, int bufsiz); // fill buf extern void Sensor_Handler(FCGIContext *context, char * params); +extern void Sensor_Handler2(FCGIContext *context, char *params); #endif //_SENSOR_H -- 2.20.1