From: Jeremy Tan Date: Sat, 21 Sep 2013 08:27:35 +0000 (+0800) Subject: Change control code - some bugfixes, plus stop threads in paused state X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=5799ff15b85e78018f2e1800dde82c9aec43f382;p=matches%2FMCTX3420.git Change control code - some bugfixes, plus stop threads in paused state --- diff --git a/server/actuator.c b/server/actuator.c index 9d2ad13..c71b05a 100644 --- a/server/actuator.c +++ b/server/actuator.c @@ -44,7 +44,6 @@ void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg) { char filename[BUFSIZ]; const char *experiment_name = (const char*) arg; - int ret; if (snprintf(filename, BUFSIZ, "%s_a%d", experiment_name, a->id) >= BUFSIZ) { @@ -54,34 +53,43 @@ void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg) Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename); // Open DataFile Data_Open(&(a->data_file), filename); - + } + case CONTROL_RESUME: //Case fallthrough; no break before + { + int ret; a->activated = true; // Don't forget this - a->allow_actuation = true; - a->control_changed = false; - // Create the thread ret = pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a)); if (ret != 0) { Fatal("Failed to create Actuator_Loop for Actuator %d", a->id); } + + Log(LOGDEBUG, "Resuming actuator %d", a->id); } break; case CONTROL_EMERGENCY: //TODO add proper case for emergency case CONTROL_PAUSE: - a->allow_actuation = false; + a->activated = false; + Actuator_SetControl(a, NULL); + pthread_join(a->thread, NULL); // Wait for thread to exit + + Log(LOGDEBUG, "Paused actuator %d", a->id); break; - case CONTROL_RESUME: - a->allow_actuation = true; + break; case CONTROL_STOP: - a->allow_actuation = false; - a->activated = false; - Actuator_SetControl(a, NULL); - pthread_join(a->thread, NULL); // Wait for thread to exit + if (a->activated) //May have been paused before + { + a->activated = false; + Actuator_SetControl(a, NULL); + pthread_join(a->thread, NULL); // Wait for thread to exit + } Data_Close(&(a->data_file)); // Close DataFile + + Log(LOGDEBUG, "Stopped actuator %d", a->id); break; default: Fatal("Unknown control mode: %d", mode); @@ -121,8 +129,6 @@ void * Actuator_Loop(void * arg) pthread_mutex_unlock(&(a->mutex)); if (!a->activated) break; - else if (!a->allow_actuation) - continue; Actuator_SetValue(a, a->control.value); } @@ -238,8 +244,6 @@ void Actuator_EndResponse(FCGIContext * context, ActuatorId id, DataFormat forma } - - /** * Handle a request for an Actuator * @param context - FCGI context diff --git a/server/actuator.h b/server/actuator.h index 81150e8..ee0ed32 100644 --- a/server/actuator.h +++ b/server/actuator.h @@ -52,8 +52,6 @@ typedef struct pthread_cond_t cond; /** Indicates whether the Actuator is running **/ bool activated; - /** Indicates whether the Actuator can be actuated or not **/ - bool allow_actuation; } Actuator; diff --git a/server/control.c b/server/control.c index 2c0fd79..5c8a410 100644 --- a/server/control.c +++ b/server/control.c @@ -50,11 +50,6 @@ void Control_Handler(FCGIContext *context, char *params) { return; } else if (!strcmp(action, "emergency")) { desired_mode = CONTROL_EMERGENCY; - } else if (!strcmp(action, "query")) { - FCGI_BeginJSON(context, STATUS_OK); - FCGI_JSONPair("state", Control_GetModeName(Control_GetMode())); - FCGI_EndJSON(); - return; } else if (FCGI_HasControl(context, key)) { if (!strcmp(action, "release")) { FCGI_ReleaseControl(context); @@ -78,11 +73,7 @@ void Control_Handler(FCGIContext *context, char *params) { void *arg = NULL; if (desired_mode == CONTROL_START) { - int len = strlen(name); - if (len <= 0) { - FCGI_RejectJSON(context, "An experiment name must be specified."); - return; - } else if (PathExists(name) && !force) { + if (PathExists(name) && !force) { FCGI_RejectJSON(context, "An experiment with that name already exists."); return; } @@ -111,26 +102,43 @@ const char* Control_SetMode(ControlModes desired_mode, void * arg) const char *ret = NULL; pthread_mutex_lock(&(g_controls.mutex)); - if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP) { - ret = "In emergency mode. Stop before doing anything else."; - } else if (g_controls.current_mode == desired_mode) { - ret = "Already in desired mode."; - } else if (desired_mode == CONTROL_START) { - if (g_controls.current_mode == CONTROL_STOP) { - //TODO Sanitise name (ensure it contains no special chars eg \ / .. . - FILE *fp = fopen((const char*) arg, "a"); - if (fp) { - fclose(fp); - gettimeofday(&(g_controls.start_time), NULL); - } else { - ret = "Cannot open experiment name marker"; - } - } else { - ret = "Cannot start when not in a stopped state."; - } - } else if (desired_mode == CONTROL_RESUME) { - if (g_controls.current_mode != CONTROL_PAUSE) - ret = "Cannot resume when not in a paused state."; + if (g_controls.current_mode == desired_mode) + ret = "Already in the desired mode."; + else if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP) + ret = "In emergency mode. You must stop before continuing."; + else switch (desired_mode) { + case CONTROL_START: + if (g_controls.current_mode == CONTROL_STOP) { + const char * name = arg; + if (!*name) + ret = "An experiment name must be specified"; + else if (strpbrk(name, INVALID_CHARACTERS)) + ret = "The experiment name must not contain: " INVALID_CHARACTERS_JSON; + else { + FILE *fp = fopen((const char*) arg, "a"); + if (fp) { + fclose(fp); + gettimeofday(&(g_controls.start_time), NULL); + } else + ret = "Cannot open experiment name marker"; + } + } else + ret = "Cannot start when not in a stopped state."; + break; + case CONTROL_PAUSE: + if (g_controls.current_mode != CONTROL_START) + ret = "Cannot pause when not in a running state."; + break; + case CONTROL_RESUME: + if (g_controls.current_mode != CONTROL_PAUSE) + ret = "Cannot resume when not in a paused state."; + break; + case CONTROL_EMERGENCY: + if (g_controls.current_mode != CONTROL_START) //pfft + ret = "Not running so how can there be an emergency."; + break; + default: + break; } if (ret == NULL) { @@ -146,22 +154,14 @@ const char* Control_SetMode(ControlModes desired_mode, void * arg) } /** - * Gets the current mode. - * @return The current mode - */ -ControlModes Control_GetMode() { - return g_controls.current_mode; -} - -/** - * Gets a string representation of a mode + * Gets a string representation of the current mode * @param mode The mode to get a string representation of * @return The string representation of the mode */ -const char * Control_GetModeName(ControlModes mode) { +const char * Control_GetModeName() { const char * ret = "Unknown"; - switch (mode) { + switch (g_controls.current_mode) { case CONTROL_START: ret = "Running"; break; case CONTROL_PAUSE: ret = "Paused"; break; case CONTROL_RESUME: ret = "Resumed"; break; @@ -171,19 +171,6 @@ const char * Control_GetModeName(ControlModes mode) { return ret; } -/* -bool Control_Lock() { - pthread_mutex_lock(&(g_controls.mutex)); - if (g_controls.state == STATE_RUNNING || g_controls.state == STATE_PAUSED) - return true; - pthread_mutex_unlock(&(g_controls.mutex)); - return false; -} - -void Control_Unlock() { - pthread_mutex_unlock(&(g_controls.mutex)); -}*/ - /** * Gets the start time for the current experiment * @return the start time diff --git a/server/control.h b/server/control.h index 5b51e95..7b3511a 100644 --- a/server/control.h +++ b/server/control.h @@ -13,11 +13,15 @@ typedef enum ControlModes { CONTROL_EMERGENCY } ControlModes; -/** ID codes for all the actuators **/ +/** Invalid filename characters **/ +#define INVALID_CHARACTERS "\"*/:<>?\\|" +/** The same as INVALID_CHARACTERS, except escaped for use in JSON strings **/ +#define INVALID_CHARACTERS_JSON "\\\"*/:<>?\\\\|" + extern void Control_Handler(FCGIContext *context, char *params); extern const char* Control_SetMode(ControlModes desired_mode, void * arg); extern ControlModes Control_GetMode(); -extern const char * Control_GetModeName(ControlModes mode); +extern const char * Control_GetModeName(); //extern bool Control_Lock(); //extern void Control_Unlock(); extern const struct timeval* Control_GetStartTime(); diff --git a/server/fastcgi.c b/server/fastcgi.c index b7a3e6b..a648ae8 100644 --- a/server/fastcgi.c +++ b/server/fastcgi.c @@ -294,6 +294,7 @@ void FCGI_BeginJSON(FCGIContext *context, StatusCodes status_code) FCGI_JSONDouble("start_time", TIMEVAL_TO_DOUBLE(g_options.start_time)); FCGI_JSONDouble("current_time", TIMEVAL_TO_DOUBLE(now)); FCGI_JSONDouble("running_time", TIMEVAL_DIFF(now, g_options.start_time)); + FCGI_JSONPair("control_state", Control_GetModeName()); } /** diff --git a/server/sensor.c b/server/sensor.c index 5115f99..64c7aa9 100644 --- a/server/sensor.c +++ b/server/sensor.c @@ -39,7 +39,6 @@ void Sensor_Init() { g_sensors[i].id = i; Data_Init(&(g_sensors[i].data_file)); - g_sensors[i].record_data = false; } } @@ -61,7 +60,6 @@ void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg) // Set filename char filename[BUFSIZ]; const char *experiment_name = (const char*) arg; - int ret; if (snprintf(filename, BUFSIZ, "%s_s%d", experiment_name, s->id) >= BUFSIZ) { @@ -71,9 +69,11 @@ void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg) Log(LOGDEBUG, "Sensor %d with DataFile \"%s\"", s->id, filename); // Open DataFile Data_Open(&(s->data_file), filename); - - s->activated = true; - s->record_data = true; // Don't forget this! + } + case CONTROL_RESUME: //Case fallthrough, no break before + { + int ret; + s->activated = true; // Don't forget this! // Create the thread ret = pthread_create(&(s->thread), NULL, Sensor_Loop, (void*)(s)); @@ -81,23 +81,29 @@ void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg) { Fatal("Failed to create Sensor_Loop for Sensor %d", s->id); } + + Log(LOGDEBUG, "Resuming sensor %d", s->id); } - break; + break; + case CONTROL_EMERGENCY: case CONTROL_PAUSE: - s->record_data = false; - break; - case CONTROL_RESUME: - s->record_data = true; - break; - case CONTROL_STOP: s->activated = false; - s->record_data = false; pthread_join(s->thread, NULL); + Log(LOGDEBUG, "Paused sensor %d", s->id); + break; + + case CONTROL_STOP: + if (s->activated) //May have been paused before + { + s->activated = false; + pthread_join(s->thread, NULL); + } Data_Close(&(s->data_file)); // Close DataFile s->newest_data.time_stamp = 0; s->newest_data.value = 0; + Log(LOGDEBUG, "Stopped sensor %d", s->id); break; default: Fatal("Unknown control mode: %d", mode); @@ -128,6 +134,7 @@ void Sensor_CheckData(SensorId id, double value) { Log(LOGERR, "Sensor %s is above or below its safety value of %f or %f\n", g_sensor_names[id],thresholds[id].max_error, thresholds[id].min_error); //new function that stops actuators? + //Control_SetMode(CONTROL_EMERGENCY, NULL) } else if( value > thresholds[id].max_warn || value < thresholds[id].min_warn) { @@ -211,20 +218,12 @@ void * Sensor_Loop(void * arg) // Until the sensor is stopped, record data points while (s->activated) { - if (s->record_data) - { - 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: - { - //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value); - Data_Save(&(s->data_file), &d, 1); // Record it - } - } - else + 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: { - //Do something? wait? - usleep(100000); + //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value); + Data_Save(&(s->data_file), &d, 1); // Record it } } diff --git a/server/sensor.h b/server/sensor.h index bdab0f9..6c7abaa 100644 --- a/server/sensor.h +++ b/server/sensor.h @@ -32,10 +32,8 @@ typedef struct SensorId id; /** DataFile to store sensor values in **/ DataFile data_file; - /** Indicates whether the Sensor is not stopped **/ + /** Indicates whether the Sensor is active or not **/ bool activated; - /** Indicates whether the Sensor should record data **/ - bool record_data; /** Thread the Sensor is running in **/ pthread_t thread; /** Most recently recorded data **/