From: Callum Date: Fri, 13 Sep 2013 04:37:04 +0000 (+0800) Subject: Merge remote-tracking branch 'upstream/master' X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=5ab6641e1e2c28d85440f4cbe4c4091d0c54d1ab;p=matches%2FMCTX3420.git Merge remote-tracking branch 'upstream/master' Conflicts: server/sensor.c server/sensor.h --- 5ab6641e1e2c28d85440f4cbe4c4091d0c54d1ab diff --cc server/sensor.c index 2a0e762,227ea42..0000000 deleted file mode 100644,100644 --- a/server/sensor.c +++ /dev/null @@@ -1,623 -1,372 +1,0 @@@ --/** -- * @file sensor.c -- * @brief Implementation of sensor thread -- * TODO: Finalise implementation -- */ -- --#include "common.h" --#include "sensor.h" --#include "options.h" --#include -- --/** Array of sensors, initialised by Sensor_Init **/ --static Sensor g_sensors[NUMSENSORS]; //global to this file - -/** Human readable names for the sensors **/ --const char * g_sensor_names[NUMSENSORS] = { -- "analog_test0", "analog_test1", - "analog_fail0", "digital_test0", - "digital_test1", "digital_fail0" - "digital_test0", "digital_test1" --}; -- --/** - * Checks the sensor data for unsafe or unexpected results - * @param sensor_id - The ID of the sensor - * @param value - The value of the sensor to check - * One off initialisation of *all* sensors -- */ - void CheckSensor( SensorId sensor_id, double value) -void Sensor_Init() --{ - switch (sensor_id) - for (int i = 0; i < NUMSENSORS; ++i) -- { - case ANALOG_FAIL0: - { - if( value > ANALOG_FAIL0_SAFETY || value < ANALOG_FAIL0_MIN_SAFETY) - { - Log(LOGERR, "Sensor analog_fail0 is above or below its safety value of %d or %d\n", ANALOG_FAIL0_SAFETY, ANALOG_FAIL0_MIN_SAFETY); - //new function that stops actuators? - } - else if( value > ANALOG_FAIL0_WARN || value < ANALOG_FAIL0_MIN_WARN) - { - Log(LOGWARN, "Sensor analog_test0 is above or below its warning value of %d or %d\n", ANALOG_FAIL0_WARN, ANALOG_FAIL0_MIN_WARN); - } - break; - } - case DIGITAL_FAIL0: - { - if( value != 0 && value != 1) - { - Log(LOGERR, "Sensor digital_fail0 is not 0 or 1\n"); - } - break; - } - default: - { - //So it doesn't complain about the missing cases - in practice we will need all sensors to be checked as above, no need to include a default as we should only pass valid sensor_id's; unless for some reason we have a sensor we don't need to check (but then why would you pass to this function in the first place :P) - } - g_sensors[i].id = i; - Data_Init(&(g_sensors[i].data_file)); - g_sensors[i].record_data = false; -- } --} -- --/** - * Read a data value from a sensor; block until value is read - * @param sensor_id - The ID of the sensor - * @param d - DataPoint to set - * @returns NULL for digital sensors when data is unchanged, otherwise d - * Start a Sensor recording DataPoints - * @param s - The Sensor to start - * @param experiment_name - Prepended to DataFile filename -- */ - DataPoint * GetData(SensorId sensor_id, DataPoint * d) -void Sensor_Start(Sensor * s, const char * experiment_name) --{ - // switch based on the sensor_id at the moment for testing; - // might be able to just directly access ADC from sensor_id? - //TODO: Implement for real sensors - // Set filename - char filename[BUFSIZ]; - if (sprintf(filename, "%s_%d", experiment_name, s->id) >= BUFSIZ) - { - Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ); - } -- - - //TODO: We should ensure the time is *never* allowed to change on the server if we use gettimeofday - // Another way people might think of getting the time is to count CPU cycles with clock() - // But this will not work because a) CPU clock speed may change on some devices (RPi?) and b) It counts cycles used by all threads - Log(LOGDEBUG, "Sensor %d with DataFile \"%s\"", s->id, filename); - // Open DataFile - Data_Open(&(s->data_file), filename); - - s->record_data = true; // Don't forget this! - - // Create the thread - pthread_create(&(s->thread), NULL, Sensor_Loop, (void*)(s)); -} - -/** - * Stop a Sensor from recording DataPoints. Blocks until it has stopped. - * @param s - The Sensor to stop - */ -void Sensor_Stop(Sensor * s) -{ - // Stop - if (s->record_data) - { - s->record_data = false; - pthread_join(s->thread, NULL); // Wait for thread to exit - Data_Close(&(s->data_file)); // Close DataFile - s->newest_data.time_stamp = 0; - s->newest_data.value = 0; - } -} - -/** - * Stop all Sensors - */ -void Sensor_StopAll() -{ - for (int i = 0; i < NUMSENSORS; ++i) - Sensor_Stop(g_sensors+i); -} - -/** - * Start all Sensors - */ -void Sensor_StartAll(const char * experiment_name) -{ - for (int i = 0; i < NUMSENSORS; ++i) - Sensor_Start(g_sensors+i, experiment_name); -} - -/** - * Read a DataPoint from a Sensor; block until value is read - * @param id - The ID of the sensor - * @param d - DataPoint to set - * @returns True if the DataPoint was different from the most recently recorded. - */ -bool Sensor_Read(Sensor * s, DataPoint * d) -{ -- - // Set time stamp -- struct timeval t; -- gettimeofday(&t, NULL); - d->time_stamp = (t.tv_sec - g_options.start_time.tv_sec) + 1e-6*(t.tv_usec - g_options.start_time.tv_usec); - d->time_stamp = TIMEVAL_DIFF(t, g_options.start_time); -- - // Make time relative - //d->time_stamp.tv_sec -= g_options.start_time.tv_sec; - //d->time_stamp.tv_usec -= g_options.start_time.tv_usec; - - switch (sensor_id) - // Read value based on Sensor Id - switch (s->id) -- { -- case ANALOG_TEST0: - d->value = (double)(rand() % 100) / 100; - break; - - case ANALOG_TEST1: -- { -- static int count = 0; -- d->value = count++; -- break; -- } - case ANALOG_TEST1: - d->value = (double)(rand() % 100) / 100; - break; - case ANALOG_FAIL0: - d->value = (double)(rand() % 6) * -( rand() % 2) / ( rand() % 100 + 1); - //Gives a value between -5 and 5 - CheckSensor(sensor_id, d->value); - break; - //TODO: For digital sensors, consider only updating when sensor is actually changed -- case DIGITAL_TEST0: -- d->value = t.tv_sec % 2; -- break; -- case DIGITAL_TEST1: -- d->value = (t.tv_sec+1)%2; - break; - case DIGITAL_FAIL0: - if( rand() % 100 > 98) - d->value = 2; - d->value = rand() % 2; - //Gives 0 or 1 or a 2 every 1/100 times - CheckSensor(sensor_id, d->value); -- break; -- default: - Fatal("Unknown sensor id: %d", sensor_id); - Fatal("Unknown sensor id: %d", s->id); -- break; -- } -- usleep(100000); // simulate delay in sensor polling - - return d; - } - - /** - * Destroy a sensor - * @param s - Sensor to destroy - */ - void Destroy(Sensor * s) - { - // Maybe move the binary file into long term file storage? - fclose(s->file); - } - - -- - /** - * Initialise a sensor - * @param s - Sensor to initialise - */ - void Init(Sensor * s, int id) - { - s->write_index = 0; - s->id = id; - s->points_written = 0; - s->points_read = 0; - // Perform sanity check based on Sensor's ID and the DataPoint - Sensor_CheckData(s->id, d); -- - #define FILENAMESIZE 3 - char filename[FILENAMESIZE]; - if (s->id >= pow(10, FILENAMESIZE)) - // Update latest DataPoint if necessary - bool result = (d->value != s->newest_data.value); - if (result) -- { - Fatal("Too many sensors! FILENAMESIZE is %d; increase it and recompile.", FILENAMESIZE); - s->newest_data.time_stamp = d->time_stamp; - s->newest_data.value = d->value; -- } - - pthread_mutex_init(&(s->mutex), NULL); - - sprintf(filename, "%d", s->id); - unlink(filename); //TODO: Move old files somewhere - - s->file = fopen(filename, "a+b"); // open binary file - Log(LOGDEBUG, "Initialised sensor %d; binary file is \"%s\"", id, filename); - return result; --} - -- --/** - * Run the main sensor polling loop - * @param arg - Cast to Sensor* - Sensor that the thread will handle - * @returns NULL (void* required to use the function with pthreads) - * Checks the sensor data for unsafe or unexpected results - * @param sensor_id - The ID of the sensor - * @param d - DataPoint to check -- */ - void * Sensor_Main(void * arg) -void Sensor_CheckData(SensorId id, DataPoint * d) --{ - Sensor * s = (Sensor*)(arg); - - while (Thread_Runstate() == RUNNING) //TODO: Exit condition - //TODO: Implement - /* - switch (sensor_id) -- { - // The sensor will write data to a buffer until it is full - // Then it will open a file and dump the buffer to the end of it. - // Rinse and repeat - - // The reason I've added the buffer is because locks are expensive - // But maybe it's better to just write data straight to the file - // I'd like to do some tests by changing SENSOR_DATABUFSIZ - - while (s->write_index < SENSOR_DATABUFSIZ) - case ANALOG_TEST0: -- { - DataPoint * d = &(s->buffer[s->write_index]); - if (GetData(s->id, d) == NULL) - if( *sensor value* > ANALOG_TEST0_SAFETY) -- { - Fatal("Error collecting data"); - LogEx(LOGERR, GetData, Sensor analog_test0 is above the safe value); - //new log function that stops actuators? -- } - s->write_index += 1; - } - - //Log(LOGDEBUG, "Filled buffer"); - - // CRITICAL SECTION (no threads should be able to read/write the file at the same time) - pthread_mutex_lock(&(s->mutex)); - //TODO: Valgrind complains about this fseek: "Syscall param write(buf) points to uninitialised byte(s)" - // Not sure why, but we should find out and fix it. - fseek(s->file, 0, SEEK_END); - int amount_written = fwrite(s->buffer, sizeof(DataPoint), SENSOR_DATABUFSIZ, s->file); - if (amount_written != SENSOR_DATABUFSIZ) - //Also include a warning level? - else if( *sensor value* > ANALOG_TEST0_WARN) -- { - Fatal("Wrote %d data points and expected to write %d to \"%s\" - %s", amount_written, SENSOR_DATABUFSIZ, strerror(errno)); - LogEx(LOGWARN, GetData, Sensor analog_test0); -- } - s->points_written += amount_written; - //Log(LOGDEBUG, "Wrote %d data points for sensor %d", amount_written, s->id); - pthread_mutex_unlock(&(s->mutex)); - // End of critical section - - s->write_index = 0; // reset position in buffer - - } - Log(LOGDEBUG, "Thread for sensor %d exits", s->id); - return NULL; - } - - /** - * Get position in a binary sensor file with a timestamp using a binary search - * @param s - Sensor to use - * @param time_stamp - Timestamp - * @param count - If not NULL, used to provide number of searches required - * @param found - If not NULL, set to the closest DataPoint - * @returns Integer giving the *closest* index in the file - * TODO: Refactor or replace? - */ - int FindTime(Sensor * s, double time_stamp, int * count, DataPoint * found) - { - DataPoint d; - - int lower = 0; - int upper = s->points_written - 1; - int index = 0; - if (count != NULL) - *count = 0; - - while (upper - lower > 1) - { - index = lower + ((upper - lower)/2); - - // Basically anything with fseek is critical; if we don't make it critical the sensor thread may alter data at a random point in the file! - // CRITICAL SECTION (May need to rethink how this is done, but I can't see how to do it without fseek :S) - // Regarding the suggestion that we have 2 file pointers; one for reading and one for writing: - // That seems like it will work... but we will have to be very careful and test it first - pthread_mutex_lock(&s->mutex); - fseek(s->file, index*sizeof(DataPoint), SEEK_SET); - int amount_read = fread(&d, sizeof(DataPoint), 1, s->file); - pthread_mutex_unlock(&s->mutex); - - if (amount_read != 1) - { - Fatal("Couldn't read single data point from sensor %d", s->id); - } - - if (d.time_stamp > time_stamp) - { - upper = index; - } - else if (d.time_stamp < time_stamp) - { - lower = index; -- } - if (count != NULL) - *count += 1; -- } - - if (found != NULL) - *found = d; - - return index; - - */ --} - -- --/** - * Print sensor data between two indexes in the file, using a given format - * @param s - Sensor to use - * @param start - Start index - * @param end - End index - * @param output_type - JSON, CSV or TSV output format - * Record data from a single Sensor; to be run in a seperate thread - * @param arg - Cast to Sensor* - Sensor that the thread will handle - * @returns NULL (void* required to use the function with pthreads) -- */ - void PrintData(Sensor * s, int start, int end, OutputType output_type) -void * Sensor_Loop(void * arg) --{ - DataPoint buffer[SENSOR_QUERYBUFSIZ]; - int index = start; - - if (output_type == JSON) - { - FCGI_JSONValue("["); - } - - Sensor * s = (Sensor*)(arg); - Log(LOGDEBUG, "Sensor %d starts", s->id); -- - while (index < end) - // Until the sensor is stopped, record data points - while (s->record_data) -- { - int to_read = end - index; - if (to_read > SENSOR_QUERYBUFSIZ) - { - to_read = SENSOR_QUERYBUFSIZ; - } - - int amount_read = 0; - // CRITICAL SECTION - pthread_mutex_lock(&(s->mutex)); - - fseek(s->file, index*sizeof(DataPoint), SEEK_SET); - amount_read = fread(buffer, sizeof(DataPoint), to_read, s->file); - - pthread_mutex_unlock(&(s->mutex)); - // End critical section - - if (amount_read != to_read) - { - Fatal("Failed to read %d DataPoints from sensor %d; read %d instead", to_read, s->id, amount_read); - } - - // Print the data - for (int i = 0; i < amount_read; ++i) - DataPoint d; - //Log(LOGDEBUG, "Sensor %d reads data [%f,%f]", s->id, d.time_stamp, d.value); - if (Sensor_Read(s, &d)) // If new DataPoint is read: -- { - //TODO: Reformat? - switch (output_type) - { - case JSON: - FCGI_JSONValue("[%f, %f]", buffer[i].time_stamp, buffer[i].value); - if (i+1 < amount_read) - FCGI_JSONValue(","); - break; - case CSV: - FCGI_PrintRaw("%f,%f\n", buffer[i].time_stamp, buffer[i].value); - break; - case TSV: - default: - FCGI_PrintRaw("%f\t%f\n", buffer[i].time_stamp, buffer[i].value); - break; - } - //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value); - Data_Save(&(s->data_file), &d, 1); // Record it -- } - index += amount_read; - } - - if (output_type == JSON) - { - FCGI_JSONValue("]"); -- } - } - - // Needed to keep pthreads happy -- - /** - * Fill buffer with most recent sensor data - * TODO: This may be obselete; remove? - * @param s - Sensor to use - * @param buffer - Buffer to fill - * @param bufsiz - Size of buffer to fill - * @returns The number of DataPoints actually read - */ - int Sensor_Query(Sensor * s, DataPoint * buffer, int bufsiz) - { - int amount_read = 0; - //CRITICAL SECTION (Don't access file while sensor thread is writing to it!) - pthread_mutex_lock(&(s->mutex)); - - fseek(s->file, -bufsiz*sizeof(DataPoint), SEEK_END); - amount_read = fread(buffer, sizeof(DataPoint), bufsiz, s->file); - //Log(LOGDEBUG, "Read %d data points", amount_read); - pthread_mutex_unlock(&(s->mutex)); - return amount_read; - Log(LOGDEBUG, "Sensor %d finished", s->id); - return NULL; --} -- --/** -- * Get a Sensor given an ID string -- * @param id_str ID string -- * @returns Sensor* identified by the string; NULL on error -- */ --Sensor * Sensor_Identify(const char * id_str) --{ -- char * end; -- // Parse string as integer -- int id = strtol(id_str, &end, 10); -- if (*end != '\0') -- { -- return NULL; -- } -- // Bounds check -- if (id < 0 || id >= NUMSENSORS) -- return NULL; -- -- -- Log(LOGDEBUG, "Sensor \"%s\" identified", g_sensor_names[id]); -- return g_sensors+id; -} - -/** - * Helper: Begin sensor response in a given format - * @param context - the FCGIContext - * @param format - Format - * @param id - ID of sensor - */ -void Sensor_BeginResponse(FCGIContext * context, SensorId id, DataFormat format) -{ - // Begin response - switch (format) - { - case JSON: - FCGI_BeginJSON(context, STATUS_OK); - FCGI_JSONLong("id", id); - FCGI_JSONKey("data"); - break; - default: - FCGI_PrintRaw("Content-type: text/plain\r\n\r\n"); - break; - } -} - -/** - * Helper: End sensor response in a given format - * @param context - the FCGIContext - * @param id - ID of the sensor - * @param format - Format - */ -void Sensor_EndResponse(FCGIContext * context, SensorId id, DataFormat format) -{ - // End response - switch (format) - { - case JSON: - FCGI_EndJSON(); - break; - default: - break; - } --} -- --/** -- * Handle a request to the sensor module -- * @param context - The context to work in -- * @param params - Parameters passed - * TODO: Seriously need to write more helper functions and decrease the size of this function! -- */ --void Sensor_Handler(FCGIContext *context, char * params) --{ - StatusCodes status = STATUS_OK; - - OutputType output_type = JSON; - - - - const char * key; const char * value; - - Sensor * sensor = NULL; - -- struct timeval now; -- gettimeofday(&now, NULL); - - double start_time = -1; - double end_time = -1; - double current_time = (now.tv_sec - g_options.start_time.tv_sec) + 1e-6*(now.tv_usec - g_options.start_time.tv_usec); - bool seek_time = false; - bool points_specified = false; - int query_size = SENSOR_QUERYBUFSIZ; - int start_index = -1; - int end_index = -1; - - - while ((params = FCGI_KeyPair(params, &key, &value)) != NULL) - { - Log(LOGDEBUG, "Got key=%s and value=%s", key, value); - if (strcmp(key, "id") == 0) - { - if (sensor != NULL) - { - Log(LOGERR, "Only one sensor id should be specified"); - status = STATUS_ERROR; - break; - } - if (*value == '\0') - { - Log(LOGERR, "No id specified."); - status = STATUS_ERROR; - break; - } - - sensor = Sensor_Identify(value); - if (sensor == NULL) - { - Log(LOGERR, "Invalid sensor id: %s", value); - status = STATUS_ERROR; - break; - } - } - else if (strcmp(key, "format") == 0) - { - if (strcmp(value, "json") == 0) - output_type = JSON; - else if (strcmp(value, "csv") == 0) - output_type = CSV; - else if (strcmp(value, "tsv") == 0) - output_type = TSV; - } - else if (strcmp(key, "points") == 0) - { - points_specified = true; - if (strcmp(value, "all") == 0) - { - query_size = sensor->points_written; - } - else - { - char * end; - query_size = strtol(value, &end, 10); - if (*end != '\0') - { - Log(LOGERR, "Require \"all\" or an integer value: %s = %s", key, value); - status = STATUS_ERROR; - break; - } - } - - } - else if (strcmp(key, "start_time") == 0) - { - seek_time = true; - char * end; - start_time = strtod(value, &end); - if (*end != '\0') - { - Log(LOGERR, "Require a double: %s = %s", key, value); - status = STATUS_ERROR; - break; - } - - // Treat negative values as being relative to the current time - if (start_time < 0) - { - start_time = current_time + start_time; - } - start_time = floor(start_time); - } - else if (strcmp(key, "end_time") == 0) - { - seek_time = true; - char * end; - end_time = strtod(value, &end); - if (*end != '\0') - { - Log(LOGERR, "Require a double: %s = %s", key, value); - status = STATUS_ERROR; - break; - } - double current_time = TIMEVAL_DIFF(now, g_options.start_time); -- - // Treat negative values as being relative to the current time - if (end_time < 0) - { - end_time = current_time + end_time; - } - end_time = ceil(end_time); - } - // For backward compatability: - else if (strcmp(key, "dump") == 0) - { - output_type = TSV; - query_size = sensor->points_written+1; - } - else - { - Log(LOGERR, "Unknown key \"%s\" (value = %s)", key, value); - status = STATUS_ERROR; - break; - } - } - int id = 0; - double start_time = 0; - double end_time = current_time; - char * fmt_str; -- - if (status != STATUS_ERROR && sensor == NULL) - { - Log(LOGERR, "No valid sensor id given"); - status = STATUS_ERROR; - } - // key/value pairs - FCGIValue values[] = { - {"id", &id, FCGI_REQUIRED(FCGI_LONG_T)}, - {"format", &fmt_str, FCGI_STRING_T}, - {"start_time", &start_time, FCGI_DOUBLE_T}, - {"end_time", &end_time, FCGI_DOUBLE_T}, - }; -- - if (status == STATUS_ERROR) - // enum to avoid the use of magic numbers - typedef enum { - ID, - FORMAT, - START_TIME, - END_TIME, - } SensorParams; - - // Fill values appropriately - if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue))) -- { - FCGI_RejectJSON(context, "Invalid input parameters"); - // Error occured; FCGI_RejectJSON already called -- return; -- } -- - // Get Sensor - Sensor * s = NULL; -- - if (seek_time) - // Error checking on sensor id - if (id < 0 || id >= NUMSENSORS) -- { - if (end_time < 0 && !points_specified) - end_index = sensor->points_written; - else - { - int count = 0; DataPoint d; - end_index = FindTime(sensor, end_time, &count, &d); - Log(LOGDEBUG, "FindTime - Looked for %f; found [%f,%f] after %d iterations; sensor %d, position %d", end_time, d.time_stamp, d.value, count, sensor->id, end_index); - } - if (start_time < 0) - start_time = 0; - else - { - int count = 0; DataPoint d; - start_index = FindTime(sensor, start_time, &count, &d); - Log(LOGDEBUG, "FindTime - Looked for %f; found [%f,%f] after %d iterations; sensor %d, position %d", start_time, d.time_stamp, d.value, count, sensor->id, start_index); - } - - if (points_specified) - end_index = start_index + query_size; - Log(LOGERR, "Invalid id %d", id); -- } -- else -- { - start_index = sensor->points_written - query_size; - - end_index = sensor->points_written; - s = g_sensors+id; -- } -- - if (start_index < 0) - { - Log(LOGNOTE, "start_index = %d => Clamped to 0", start_index); - start_index = 0; - } - if (end_index > sensor->points_written) - DataFormat format = JSON; - - // Check if format type was specified - if (FCGI_RECEIVED(values[FORMAT].flags)) -- { - Log(LOGNOTE, "end_index = %d => Clamped to %d", end_index, sensor->points_written); - end_index = sensor->points_written; - if (strcmp(fmt_str, "json") == 0) - format = JSON; - else if (strcmp(fmt_str, "tsv") == 0) - format = TSV; - else - Log(LOGERR, "Unknown format type \"%s\"", fmt_str); -- } - -- - switch (output_type) - { - case JSON: - FCGI_BeginJSON(context, status); - FCGI_JSONLong("id", sensor->id); - FCGI_JSONKey("data"); - PrintData(sensor, start_index, end_index, output_type); - FCGI_EndJSON(); - break; - default: - FCGI_PrintRaw("Content-type: text/plain\r\n\r\n"); - PrintData(sensor, start_index, end_index, output_type); - //Force download with content-disposition - // Sam: This is cool, but I don't think we should do it - // - letting the user view it in the browser and then save with their own filename is more flexible - //"Content-disposition: attachment;filename=%d.csv\r\n\r\n", sensor->id); - break; - } -- - } - - /** - * Setup Sensors, start Sensor polling thread(s) - */ - void Sensor_Spawn() - { - // start sensor threads - for (int i = 0; i < NUMSENSORS; ++i) - // Begin response - Sensor_BeginResponse(context, id, format); - - // If a time was specified - if ((s != NULL) && (FCGI_RECEIVED(values[START_TIME].flags) || FCGI_RECEIVED(values[END_TIME].flags))) -- { - Init(g_sensors+i, i); - pthread_create(&(g_sensors[i].thread), NULL, Sensor_Main, (void*)(g_sensors+i)); - } - } - // Wrap times relative to the current time - if (start_time < 0) - start_time += current_time; - if (end_time < 0) - end_time += current_time; -- - /** - * Quit Sensor loops - */ - void Sensor_Join() - { - if (!Thread_Runstate()) - { - Fatal("This function should not be called before Thread_QuitProgram"); - // Print points by time range - Data_PrintByTimes(&(s->data_file), start_time, end_time, format); - -- } - for (int i = 0; i < NUMSENSORS; ++i) - else if (s != NULL) // No time was specified; just return a recent set of points -- { - pthread_join(g_sensors[i].thread, NULL); - Destroy(g_sensors+i); - pthread_mutex_lock(&(s->data_file.mutex)); - int start_index = s->data_file.num_points-DATA_BUFSIZ; - int end_index = s->data_file.num_points-1; - pthread_mutex_unlock(&(s->data_file.mutex)); - - // Bounds check - if (start_index < 0) - start_index = 0; - if (end_index < 0) - end_index = 0; - - // Print points by indexes - Log(LOGDEBUG, "Sensor %d file \"%s\" indexes %d->%d", s->id, s->data_file.filename, start_index, end_index); - Data_PrintByIndexes(&(s->data_file), start_index, end_index, format); -- } - - // Finish response - Sensor_EndResponse(context, id, format); - --} - - diff --cc server/sensor.h index f834f7a,55b7d89..0000000 deleted file mode 100644,100644 --- a/server/sensor.h +++ /dev/null @@@ -1,86 -1,71 +1,0 @@@ --/** -- * @file sensor.h -- * @brief Declarations for sensor thread related stuff -- */ -- --#ifndef _SENSOR_H --#define _SENSOR_H -- - /** Number of data points to keep in sensor buffers **/ - #define SENSOR_DATABUFSIZ 10 - /** Size of the query buffer. @see Sensor_Handler **/ - #define SENSOR_QUERYBUFSIZ 10 -#include "data.h" -- --/** Number of sensors **/ - #define NUMSENSORS 6 -#define NUMSENSORS 4 -- --/** Safety Values for sensors **/ - #define ANALOG_FAIL0_WARN 4 - #define ANALOG_FAIL0_SAFETY 5 - #define ANALOG_FAIL0_MIN_WARN -4 - #define ANALOG_FAIL0_MIN_SAFETY -5 -//TODO: Probably better to use an array instead -#define ANALOG_TEST0_SAFETY 1000 -#define ANALOG_TEST1_SAFETY 1000 -#define DIGITAL_TEST0_SAFETY 1 -#define DIGITAL_TEST1_SAFETY 1 -- - typedef enum SensorId { - -typedef enum SensorId -{ -- ANALOG_TEST0, -- ANALOG_TEST1, - ANALOG_FAIL0, -- DIGITAL_TEST0, - DIGITAL_TEST1, - DIGITAL_FAIL0 - DIGITAL_TEST1 --} SensorId; -- - typedef enum - { - JSON, // JSON data - CSV, // Comma seperated vector - TSV // Tab seperated vector - } OutputType; - -- --/** Human readable names for the sensors **/ --extern const char * g_sensor_names[NUMSENSORS]; -- - /** Structure to represent data recorded by a sensor at an instant in time **/ - typedef struct - { - /** Time at which data was taken **/ - double time_stamp; - /** Value of data **/ - double value; - } DataPoint; -- --/** Structure to represent a sensor **/ --typedef struct --{ -- /** ID number of the sensor **/ -- SensorId id; - /** Buffer to store data from the sensor **/ - DataPoint buffer[SENSOR_DATABUFSIZ]; - /** Index of last point written in the data buffer **/ - int write_index; - /** Number of points read **/ - long points_read; - /** Number of points written to file **/ - long points_written; - /** Binary file to write data into when buffer is full **/ - FILE * file; - /** Thread running the sensor **/ - /** DataFile to store sensor values in **/ - DataFile data_file; - /** Indicates whether the Sensor should record data **/ - bool record_data; - /** Thread the Sensor is running in **/ -- pthread_t thread; - /** Mutex to protect access to stuff **/ - pthread_mutex_t mutex; - /** Most recently recorded data **/ - DataPoint newest_data; -- - --} Sensor; -- -- -extern void Sensor_Init(); // One off initialisation of *all* sensors -- -extern void Sensor_StartAll(const char * experiment_name); // Start all Sensors recording data -extern void Sensor_StopAll(); // Stop all Sensors recording data -extern void Sensor_Start(Sensor * s, const char * experiment_name); // Start a sensor recording datas -extern void Sensor_Stop(Sensor * s); // Stop a Sensor from recording data -- - extern void Sensor_Spawn(); // Initialise sensor - extern void Sensor_Join(); //Join sensor threads - extern void * Sensor_Main(void * args); // main loop for sensor thread; pass a Sensor* cast to void* -- - extern int Sensor_Query(Sensor * s, DataPoint * buffer, int bufsiz); // fill buffer with sensor data -extern void * Sensor_Loop(void * args); // Main loop for a thread that handles a Sensor -extern bool Sensor_Read(Sensor * s, DataPoint * d); // Read a single DataPoint, indicating if it has changed since the last one -extern void Sensor_CheckData(SensorId id, DataPoint * d); // Check a DataPoint -extern Sensor * Sensor_Identify(const char * str); // Identify a Sensor from a string Id -- - extern void Sensor_Handler(FCGIContext *context, char * params); -extern void Sensor_Handler(FCGIContext *context, char * params); // Handle a FCGI request for Sensor data - - -- --#endif //_SENSOR_H --