From 9e7cfbbc6137056bba8ed8644fc6ecfe398553fe Mon Sep 17 00:00:00 2001 From: Jeremy Tan Date: Sat, 14 Sep 2013 15:44:33 +0800 Subject: [PATCH] Add semi working control code --- server/actuator.c | 78 +++++++++++++++++++------------ server/actuator.h | 3 ++ server/control.c | 114 +++++++++++++++++++++++++++++++++++++++------- server/control.h | 3 +- server/fastcgi.c | 11 ++++- server/fastcgi.h | 3 +- server/sensor.c | 72 +++++++++++++++++++---------- server/sensor.h | 5 +- 8 files changed, 215 insertions(+), 74 deletions(-) diff --git a/server/actuator.c b/server/actuator.c index 425449c..2caa03c 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -54,6 +54,25 @@ void Actuator_Start(Actuator * a, const char * experiment_name) pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a)); } +void Actuator_Pause(Actuator *a) +{ + if (a->activated) + { + a->activated = false; + Actuator_SetControl(a, NULL); + pthread_join(a->thread, NULL); // Wait for thread to exit + } +} + +void Actuator_Resume(Actuator *a) +{ + if (!a->activated) + { + a->activated = true; + pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a)); + } +} + /** * Stop an Actuator * @param s - The Actuator to stop @@ -61,13 +80,23 @@ void Actuator_Start(Actuator * a, const char * experiment_name) void Actuator_Stop(Actuator * a) { // Stop - a->activated = false; - Actuator_SetControl(a, NULL); - pthread_join(a->thread, NULL); // Wait for thread to exit + Actuator_Pause(a); Data_Close(&(a->data_file)); // Close DataFile } +void Actuator_PauseAll() +{ + for (int i = 0; i < NUMACTUATORS; ++i) + Actuator_Pause(g_actuators+i); +} + +void Actuator_ResumeAll() +{ + for (int i = 0; i < NUMACTUATORS; ++i) + Actuator_Resume(g_actuators+i); +} + /** * Stop all Actuators */ @@ -256,33 +285,24 @@ void Actuator_Handler(FCGIContext * context, char * params) DataFormat format = Data_GetFormat(&(values[FORMAT])); - if (Control_Lock()) - { - // Begin response - Actuator_BeginResponse(context, id, format); - - // Set? - if (FCGI_RECEIVED(values[SET].flags)) - { - if (format == JSON) - FCGI_JSONDouble("set", set); - - ActuatorControl c; - c.value = set; - - Actuator_SetControl(a, &c); - } - - // Print Data - Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time); - - // Finish response - Actuator_EndResponse(context, id, format); + // Begin response + Actuator_BeginResponse(context, id, format); - Control_Unlock(); - } - else + // Set? + if (FCGI_RECEIVED(values[SET].flags)) { - FCGI_RejectJSON(context, "Experiment is not running."); + if (format == JSON) + FCGI_JSONDouble("set", set); + + ActuatorControl c; + c.value = set; + + Actuator_SetControl(a, &c); } + + // Print Data + Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time); + + // Finish response + Actuator_EndResponse(context, id, format); } diff --git a/server/actuator.h b/server/actuator.h index df946a0..8a6b644 100644 --- a/server/actuator.h +++ b/server/actuator.h @@ -61,6 +61,9 @@ extern void Actuator_Init(); // One off initialisation of *all* Actuators extern void Actuator_StartAll(const char * experiment_name); // Start all Actuators extern void Actuator_StopAll(); // Stop all Actuators +extern void Actuator_PauseAll(); +extern void Actuator_ResumeAll(); + extern void Actuator_Start(Actuator * a, const char * experiment_name); // Start a Actuator extern void Actuator_Stop(Actuator * a); // Stop an Actuator diff --git a/server/control.c b/server/control.c index 4c64f02..29d9ef6 100644 --- a/server/control.c +++ b/server/control.c @@ -13,6 +13,13 @@ typedef enum ControlState { STATE_RUNNING } ControlState; +typedef enum Mode { + START, + PAUSE, + RESUME, + STOP +} Mode; + typedef struct ControlData { ControlState state; pthread_mutex_t mutex; @@ -21,6 +28,14 @@ typedef struct ControlData { ControlData g_controls = {STATE_STOPPED, PTHREAD_MUTEX_INITIALIZER, {0}}; +static bool ExperimentExists(const char *experiment_name) { + FILE *fp = fopen(experiment_name, "r"); + if (!fp) + return false; + fclose(fp); + return true; +} + /** * System control handler. This covers high-level control, including * admin related functions and starting/stopping experiments.. @@ -30,6 +45,7 @@ ControlData g_controls = {STATE_STOPPED, PTHREAD_MUTEX_INITIALIZER, {0}}; void Control_Handler(FCGIContext *context, char *params) { const char *action, *key = "", *name = ""; bool force = false; + Mode mode; FCGIValue values[4] = { {"action", &action, FCGI_REQUIRED(FCGI_STRING_T)}, @@ -43,27 +59,66 @@ void Control_Handler(FCGIContext *context, char *params) { if (!strcmp(action, "lock")) { FCGI_LockControl(context, force); + return; } else if (FCGI_HasControl(context, key)) { if (!strcmp(action, "release")) { FCGI_ReleaseControl(context); } else if (!strcmp(action, "start")) { - FCGI_BeginJSON(context, STATUS_OK); - FCGI_JSONPair("description", "start"); - FCGI_EndJSON(); + mode = START; } else if (!strcmp(action, "pause")) { - FCGI_BeginJSON(context, STATUS_OK); - FCGI_JSONPair("description", "stop"); - FCGI_EndJSON(); - } else if (!strcmp(action, "end")) { - FCGI_BeginJSON(context, STATUS_OK); - FCGI_JSONPair("description", "stop"); - FCGI_EndJSON(); + mode = PAUSE; + } else if (!strcmp(action, "resume")) { + mode = RESUME; + } else if (!strcmp(action, "stop")) { + mode = STOP; } else { FCGI_RejectJSON(context, "Unknown action specified."); + return; } } else { FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Invalid control key specified."); + return; + } + + switch(mode) { + case START: + if (!*name) { + FCGI_RejectJSON(context, "An experiment name must be provided"); + } else if (ExperimentExists(name) && !force) { + FCGI_RejectJSONEx(context, STATUS_ALREADYEXISTS, + "An experiment with the specified name already exists."); + } else if (!Control_Start(name)) { + FCGI_RejectJSON(context, "An experiment is already running."); + } else { + FCGI_BeginJSON(context, STATUS_OK); + FCGI_EndJSON(); + } + break; + case PAUSE: + if (!Control_Pause()) { + FCGI_RejectJSON(context, "No experiment to pause."); + } else { + FCGI_BeginJSON(context, STATUS_OK); + FCGI_EndJSON(); + } + break; + case RESUME: + if (!Control_Resume()) { + FCGI_RejectJSON(context, "No experiment to resume."); + } else { + FCGI_BeginJSON(context, STATUS_OK); + FCGI_EndJSON(); + } + break; + case STOP: + if (!Control_Stop()) { + FCGI_RejectJSON(context, "No experiment to stop."); + } else { + FCGI_BeginJSON(context, STATUS_OK); + FCGI_EndJSON(); + } + break; } } @@ -72,20 +127,45 @@ bool Control_Start(const char *experiment_name) { pthread_mutex_lock(&(g_controls.mutex)); if (g_controls.state == STATE_STOPPED) { - gettimeofday(&(g_controls.start_time), NULL); - Sensor_StartAll(experiment_name); - Actuator_StartAll(experiment_name); - g_controls.state = STATE_RUNNING; - ret = true; + FILE *fp = fopen(experiment_name, "a"); + if (fp) { + fclose(fp); + gettimeofday(&(g_controls.start_time), NULL); + Sensor_StartAll(experiment_name); + Actuator_StartAll(experiment_name); + g_controls.state = STATE_RUNNING; + ret = true; + } } pthread_mutex_unlock(&(g_controls.mutex)); return ret; } -void Control_Pause() { +bool Control_Pause() { + bool ret = false; pthread_mutex_lock(&(g_controls.mutex)); + if (g_controls.state == STATE_RUNNING) { + Actuator_PauseAll(); + Sensor_PauseAll(); + g_controls.state = STATE_PAUSED; + ret = true; + } pthread_mutex_unlock(&(g_controls.mutex)); + return ret; +} + +bool Control_Resume() { + bool ret = false; + pthread_mutex_lock(&(g_controls.mutex)); + if (g_controls.state == STATE_PAUSED) { + Actuator_ResumeAll(); + Sensor_ResumeAll(); + g_controls.state = STATE_RUNNING; + ret = true; + } + pthread_mutex_unlock(&(g_controls.mutex)); + return ret; } bool Control_Stop() { @@ -104,7 +184,7 @@ bool Control_Stop() { bool Control_Lock() { pthread_mutex_lock(&(g_controls.mutex)); - if (g_controls.state == STATE_RUNNING) + if (g_controls.state == STATE_RUNNING || g_controls.state == STATE_PAUSED) return true; pthread_mutex_unlock(&(g_controls.mutex)); return false; diff --git a/server/control.h b/server/control.h index f1b95ea..188b13a 100644 --- a/server/control.h +++ b/server/control.h @@ -8,7 +8,8 @@ /** ID codes for all the actuators **/ extern void Control_Handler(FCGIContext *context, char *params); extern bool Control_Start(const char *experiment_name); -extern void Control_Pause(); +extern bool Control_Pause(); +extern bool Control_Resume(); extern bool Control_Stop(); extern bool Control_Lock(); extern void Control_Unlock(); diff --git a/server/fastcgi.c b/server/fastcgi.c index 05dae34..6b2957e 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -439,6 +439,7 @@ void * FCGI_RequestLoop (void *data) Log(LOGDEBUG, "Got request #%d", context.response_number); ModuleHandler module_handler = NULL; char module[BUFSIZ], params[BUFSIZ]; + bool lock_required = false; //strncpy doesn't zero-truncate properly snprintf(module, BUFSIZ, "%s", getenv("DOCUMENT_URI_LOCAL")); @@ -462,13 +463,21 @@ void * FCGI_RequestLoop (void *data) module_handler = Control_Handler; } else if (!strcmp("sensors", module)) { module_handler = Sensor_Handler; + lock_required = true; } else if (!strcmp("actuators", module)) { module_handler = Actuator_Handler; + lock_required = true; } context.current_module = module; if (module_handler) { - module_handler(&context, params); + if (lock_required && !Control_Lock()) { + FCGI_RejectJSONEx(&context, STATUS_NOTRUNNING, "Experiment is not running."); + } else { + module_handler(&context, params); + if (lock_required) + Control_Unlock(); + } } else { FCGI_RejectJSON(&context, "Unhandled module"); } diff --git a/server/fastcgi.h b/server/fastcgi.h index c1e0b3e..af56301 100644 --- a/server/fastcgi.h +++ b/server/fastcgi.h @@ -17,7 +17,8 @@ typedef enum StatusCodes { STATUS_OK = 1, STATUS_ERROR = -1, STATUS_UNAUTHORIZED = -2, - STATUS_OUTOFRANGE = -3 + STATUS_NOTRUNNING = -3, + STATUS_ALREADYEXISTS = -4 } StatusCodes; #define FCGI_PARAM_REQUIRED (1 << 0) diff --git a/server/sensor.c b/server/sensor.c index a151312..867f7d4 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -69,22 +69,43 @@ void Sensor_Start(Sensor * s, const char * experiment_name) } /** - * Stop a Sensor from recording DataPoints. Blocks until it has stopped. - * @param s - The Sensor to stop + * Pause a sensor from recording DataPoints. Blocks until it is paused. + * @param s - The Sensor to pause */ -void Sensor_Stop(Sensor * s) +void Sensor_Pause(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; + pthread_join(s->thread, NULL); } } +/** + * Resumes a paused sensor. + * @param s - The Sensor to resume + */ +void Sensor_Resume(Sensor *s) +{ + if (!s->record_data) + { + s->record_data = true; + 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) +{ + Sensor_Pause(s); + Data_Close(&(s->data_file)); // Close DataFile + s->newest_data.time_stamp = 0; + s->newest_data.value = 0; +} + /** * Stop all Sensors */ @@ -94,6 +115,18 @@ void Sensor_StopAll() Sensor_Stop(g_sensors+i); } +void Sensor_PauseAll() +{ + for (int i = 0; i < NUMSENSORS; ++i) + Sensor_Pause(g_sensors+i); +} + +void Sensor_ResumeAll() +{ + for (int i = 0; i < NUMSENSORS; ++i) + Sensor_Resume(g_sensors+i); +} + /** * Start all Sensors */ @@ -325,23 +358,14 @@ void Sensor_Handler(FCGIContext *context, char * params) DataFormat format = Data_GetFormat(&(values[FORMAT])); - if (Control_Lock()) - { - // Begin response - Sensor_BeginResponse(context, id, format); - - // Print Data - Data_Handler(&(s->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time); - - // Finish response - Sensor_EndResponse(context, id, format); + // Begin response + Sensor_BeginResponse(context, id, format); - Control_Unlock(); - } - else - { - FCGI_RejectJSON(context, "Experiment is not running."); - } + // Print Data + Data_Handler(&(s->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time); + + // Finish response + Sensor_EndResponse(context, id, format); } diff --git a/server/sensor.h b/server/sensor.h index 1b44673..6c51ac1 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -55,7 +55,10 @@ extern void Sensor_StartAll(const char * experiment_name); // Start all Sensors 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_Pause(Sensor *s); +extern void Sensor_Resume(Sensor *s); +extern void Sensor_PauseAll(); +extern void Sensor_ResumeAll(); 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 -- 2.20.1