/**
* @file actuator.c
- * @purpose Implementation of Actuator related functionality
+ * @brief Implementation of Actuator related functionality
*/
#include "actuator.h"
-#include "control.h"
#include "options.h"
/** Array of Actuators (global to this file) initialised by Actuator_Init **/
}
/**
- * Start an Actuator
- * @param a - The Actuator to start
- * @param experiment_name - Prepended to DataFile filename
+ * Sets the actuator to the desired mode. No checks are
+ * done to see if setting to the desired mode will conflict with
+ * the current mode - the caller must guarantee this itself.
+ * @param a The actuator whose mode is to be changed
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
+ * e.g for CONTROL_START it represents the experiment name.
*/
-void Actuator_Start(Actuator * a, const char * experiment_name)
+void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg)
{
- // Set filename
- char filename[BUFSIZ];
- if (sprintf(filename, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
+ switch (mode)
{
- Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
- }
-
- Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
- // Open DataFile
- Data_Open(&(a->data_file), filename);
-
- a->activated = true; // Don't forget this
-
- a->control_changed = false;
-
- // Create the thread
- 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));
+ case CONTROL_START:
+ {
+ char filename[BUFSIZ];
+ const char *experiment_name = (const char*) arg;
+ int ret;
+
+ if (snprintf(filename, BUFSIZ, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
+ {
+ Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
+ }
+
+ Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
+ // Open DataFile
+ Data_Open(&(a->data_file), filename);
+
+ 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);
+ }
+ }
+ break;
+
+ case CONTROL_EMERGENCY: //TODO add proper case for emergency
+ case CONTROL_PAUSE:
+ a->allow_actuation = false;
+ 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
+ Data_Close(&(a->data_file)); // Close DataFile
+ break;
+ default:
+ Fatal("Unknown control mode: %d", mode);
}
}
/**
- * Stop an Actuator
- * @param s - The Actuator to stop
- */
-void Actuator_Stop(Actuator * a)
-{
- // Stop
- 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
+ * Sets all actuators to the desired mode.
+ * @see Actuator_SetMode for more information.
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
*/
-void Actuator_StopAll()
+void Actuator_SetModeAll(ControlModes mode, void * arg)
{
- for (int i = 0; i < NUMACTUATORS; ++i)
- Actuator_Stop(g_actuators+i);
-}
-
-/**
- * Start all Actuators
- */
-void Actuator_StartAll(const char * experiment_name)
-{
- for (int i = 0; i < NUMACTUATORS; ++i)
- Actuator_Start(g_actuators+i, experiment_name);
+ for (int i = 0; i < NUMACTUATORS; i++)
+ Actuator_SetMode(&g_actuators[i], mode, arg);
}
/**
pthread_mutex_unlock(&(a->mutex));
if (!a->activated)
break;
+ else if (!a->allow_actuation)
+ continue;
Actuator_SetValue(a, a->control.value);
}
/**
* @file actuator.h
- * @purpose Declarations for actuator control
+ * @brief Declarations for actuator control
*/
#ifndef _ACTUATOR_H
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;
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
-
+extern void Actuator_SetModeAll(ControlModes mode, void *arg);
+extern void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg);
extern void * Actuator_Loop(void * args); // Main loop for a thread that handles an Actuator
extern void Actuator_SetValue(Actuator * a, double value); // Set an actuator by value
#include "log.h"
#include "fastcgi.h"
+#include "control.h"
/**Converts a timeval to a double**/
#define TIMEVAL_TO_DOUBLE(tv) ((tv).tv_sec + 1e-6 * ((tv).tv_usec))
#include "sensor.h"
#include "actuator.h"
-typedef enum ControlState {
- STATE_STOPPED,
- STATE_PAUSED,
- STATE_RUNNING
-} ControlState;
-
-typedef enum Mode {
- START,
- PAUSE,
- RESUME,
- STOP
-} Mode;
-
typedef struct ControlData {
- ControlState state;
+ ControlModes current_mode;
pthread_mutex_t mutex;
struct timeval start_time;
} ControlData;
-ControlData g_controls = {STATE_STOPPED, PTHREAD_MUTEX_INITIALIZER, {0}};
+ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
static bool ExperimentExists(const char *experiment_name) {
FILE *fp = fopen(experiment_name, "r");
void Control_Handler(FCGIContext *context, char *params) {
const char *action, *key = "", *name = "";
bool force = false;
- Mode mode;
+ ControlModes desired_mode;
FCGIValue values[4] = {
{"action", &action, FCGI_REQUIRED(FCGI_STRING_T)},
if (!strcmp(action, "lock")) {
FCGI_LockControl(context, force);
return;
+ } else if (!strcmp(action, "emergency")) {
+ desired_mode = CONTROL_EMERGENCY;
} else if (FCGI_HasControl(context, key)) {
if (!strcmp(action, "release")) {
FCGI_ReleaseControl(context);
} else if (!strcmp(action, "start")) {
- mode = START;
+ desired_mode = CONTROL_START;
} else if (!strcmp(action, "pause")) {
- mode = PAUSE;
+ desired_mode = CONTROL_PAUSE;
} else if (!strcmp(action, "resume")) {
- mode = RESUME;
+ desired_mode = CONTROL_RESUME;
} else if (!strcmp(action, "stop")) {
- mode = STOP;
+ desired_mode = CONTROL_STOP;
} else {
FCGI_RejectJSON(context, "Unknown action specified.");
return;
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;
+ 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 (ExperimentExists(name) && !force) {
+ FCGI_RejectJSON(context, "An experiment with that name already exists.");
+ return;
+ }
+ arg = (void*)name;
}
-}
-
-bool Control_Start(const char *experiment_name) {
- bool ret = false;
- pthread_mutex_lock(&(g_controls.mutex));
- if (g_controls.state == STATE_STOPPED) {
- 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;
- }
+ const char *ret;
+ if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
+ FCGI_RejectJSON(context, ret);
+ } else {
+ FCGI_BeginJSON(context, STATUS_OK);
+ FCGI_JSONPair("description", "ok");
+ FCGI_EndJSON();
}
- pthread_mutex_unlock(&(g_controls.mutex));
- return ret;
}
+/**
+ * Sets the mode to the desired mode, if possible.
+ * @param desired_mode The mode to be set to
+ * @param arg An argument specific to the mode to be set.
+ * @return NULL on success, an error message on failure.
+ */
+const char* Control_SetMode(ControlModes desired_mode, void * arg)
+{
+ const char *ret = NULL;
-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;
+ if (g_controls.current_mode == CONTROL_EMERGENCY) {
+ ret = "In emergency mode. Restart software.";
+ } 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) {
+ FILE *fp = fopen((const char*) arg, "a");
+ if (fp) {
+ fclose(fp);
+ gettimeofday(&(g_controls.start_time), NULL);
+ } else {
+ ret = "Failed to create experiment placeholder";
+ }
+ } 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.";
}
- 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;
+
+ if (ret == NULL) {
+ Actuator_SetModeAll(desired_mode, arg);
+ Sensor_SetModeAll(desired_mode, arg);
+ g_controls.current_mode = desired_mode;
}
pthread_mutex_unlock(&(g_controls.mutex));
return ret;
}
-bool Control_Stop() {
- bool ret = false;
-
+/**
+ * Gets the current mode.
+ * @return The current mode
+ */
+ControlModes Control_GetMode() {
+ ControlModes ret;
pthread_mutex_lock(&(g_controls.mutex));
- if (g_controls.state != STATE_STOPPED) {
- Actuator_StopAll();
- Sensor_StopAll();
- g_controls.state = STATE_STOPPED;
- ret = true;
- }
- pthread_mutex_unlock(&(g_controls.mutex));
+ ret = g_controls.current_mode;
+ pthread_mutex_unlock(&(g_controls.mutex));
return ret;
}
+/*
bool Control_Lock() {
pthread_mutex_lock(&(g_controls.mutex));
if (g_controls.state == STATE_RUNNING || g_controls.state == STATE_PAUSED)
void Control_Unlock() {
pthread_mutex_unlock(&(g_controls.mutex));
-}
+}*/
+/**
+ * Gets the start time for the current experiment
+ * @return the start time
+ */
const struct timeval* Control_GetStartTime() {
return &g_controls.start_time;
}
\ No newline at end of file
#ifndef _CONTROL_H
#define _CONTROL_H
+typedef enum ControlModes {
+ CONTROL_START,
+ CONTROL_PAUSE,
+ CONTROL_RESUME,
+ CONTROL_STOP,
+ CONTROL_EMERGENCY
+} ControlModes;
+
/** ID codes for all the actuators **/
extern void Control_Handler(FCGIContext *context, char *params);
-extern bool Control_Start(const char *experiment_name);
-extern bool Control_Pause();
-extern bool Control_Resume();
-extern bool Control_Stop();
-extern bool Control_Lock();
-extern void Control_Unlock();
+extern const char* Control_SetMode(ControlModes desired_mode, void * arg);
+extern ControlModes Control_GetMode();
+//extern bool Control_Lock();
+//extern void Control_Unlock();
extern const struct timeval* Control_GetStartTime();
#endif
/**
* @file data.c
- * @purpose Implementation of data handling functions; saving, loading, displaying, selecting.
+ * @brief Implementation of data handling functions; saving, loading, displaying, selecting.
*/
#include "data.h"
/**
* @file data.h
- * @purpose Declaration of data handling functions; saving, loading, displaying, selecting.
+ * @brief Declaration of data handling functions; saving, loading, displaying, selecting.
*/
#ifndef _DATAPOINT_H
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"));
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) {
- if (lock_required && !Control_Lock()) {
- FCGI_RejectJSONEx(&context, STATUS_NOTRUNNING, "Experiment is not running.");
- } else {
- module_handler(&context, params);
- if (lock_required)
- Control_Unlock();
- }
+ module_handler(&context, params);
} else {
FCGI_RejectJSON(&context, "Unhandled module");
}
Actuator_Init();
//Sensor_StartAll("test");
//Actuator_StartAll("test");
- Control_Start("test");
+ const char *ret;
+ if ((ret = Control_SetMode(CONTROL_START, "test")) != NULL)
+ Fatal("Control_SetMode failed with '%s'", ret);
// run request thread in the main thread
FCGI_RequestLoop(NULL);
- Control_Stop();
+ if ((ret = Control_SetMode(CONTROL_STOP, "test")) != NULL)
+ Fatal("Control_SetMode failed with '%s'", ret);
//Sensor_StopAll();
//Actuator_StopAll();
*/
#include "common.h"
-#include "control.h"
#include "sensor.h"
#include "options.h"
#include <math.h>
}
/**
- * Start a Sensor recording DataPoints
- * @param s - The Sensor to start
- * @param experiment_name - Prepended to DataFile filename
+ * Sets the sensor to the desired control mode. No checks are
+ * done to see if setting to the desired mode will conflict with
+ * the current mode - the caller must guarantee this itself.
+ * @param s The sensor whose mode is to be changed
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
+ * e.g for CONTROL_START it represents the experiment name.
*/
-void Sensor_Start(Sensor * s, const char * experiment_name)
+void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg)
{
- // Set filename
- char filename[BUFSIZ];
- if (sprintf(filename, "%s_s%d", experiment_name, s->id) >= BUFSIZ)
+ switch(mode)
{
- Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
- }
-
- 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));
-}
-
-/**
- * Pause a sensor from recording DataPoints. Blocks until it is paused.
- * @param s - The Sensor to pause
- */
-void Sensor_Pause(Sensor *s)
-{
- if (s->record_data)
- {
- s->record_data = false;
- pthread_join(s->thread, NULL);
+ case CONTROL_START:
+ {
+ // 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)
+ {
+ Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
+ }
+
+ 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!
+
+ // Create the thread
+ ret = pthread_create(&(s->thread), NULL, Sensor_Loop, (void*)(s));
+ if (ret != 0)
+ {
+ Fatal("Failed to create Sensor_Loop for Sensor %d", s->id);
+ }
+ }
+ 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);
+
+ Data_Close(&(s->data_file)); // Close DataFile
+ s->newest_data.time_stamp = 0;
+ s->newest_data.value = 0;
+ break;
+ default:
+ Fatal("Unknown control mode: %d", mode);
}
}
/**
- * Resumes a paused sensor.
- * @param s - The Sensor to resume
+ * Sets all sensors to the desired mode.
+ * @see Sensor_SetMode for more information.
+ * @param mode The mode to be changed to
+ * @param arg An argument specific to the mode to be set.
*/
-void Sensor_Resume(Sensor *s)
+void Sensor_SetModeAll(ControlModes mode, void * arg)
{
- 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
- */
-void Sensor_StopAll()
-{
- for (int i = 0; i < NUMSENSORS; ++i)
- 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
- */
-void Sensor_StartAll(const char * experiment_name)
-{
- for (int i = 0; i < NUMSENSORS; ++i)
- Sensor_Start(g_sensors+i, experiment_name);
+ for (int i = 0; i < NUMSENSORS; i++)
+ Sensor_SetMode(&g_sensors[i], mode, arg);
}
Log(LOGDEBUG, "Sensor %d starts", s->id);
// Until the sensor is stopped, record data points
- while (s->record_data)
+ while (s->activated)
{
- 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:
+ 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
{
- //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value);
- Data_Save(&(s->data_file), &d, 1); // Record it
+ //Do something? wait?
+ usleep(100000);
}
}
SensorId id;
/** DataFile to store sensor values in **/
DataFile data_file;
+ /** Indicates whether the Sensor is not stopped **/
+ bool activated;
/** Indicates whether the Sensor should record data **/
bool record_data;
/** Thread the Sensor is running in **/
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_Pause(Sensor *s);
-extern void Sensor_Resume(Sensor *s);
-extern void Sensor_PauseAll();
-extern void Sensor_ResumeAll();
+extern void Sensor_SetModeAll(ControlModes mode, void * arg);
+extern void Sensor_SetMode(Sensor * s, ControlModes mode, void * arg);
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
#!/bin/bash
-valgrind --leak-check=full --track-origins=yes --show-reachable=yes ./server
+valgrind --leak-check=full --show-reachable=yes ./server