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
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
*/
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);
}
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
STATE_RUNNING
} ControlState;
+typedef enum Mode {
+ START,
+ PAUSE,
+ RESUME,
+ STOP
+} Mode;
+
typedef struct ControlData {
ControlState state;
pthread_mutex_t mutex;
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..
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)},
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;
}
}
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() {
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;
/** 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();
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) {
- 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");
}
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)
}
/**
- * 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
*/
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
*/
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);
}
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