3 * @brief Handles all client control requests (admin related)
10 typedef struct ControlData {
11 ControlModes current_mode;
12 pthread_mutex_t mutex;
13 struct timeval start_time;
16 ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
18 static bool PathExists(const char *path) {
19 FILE *fp = fopen(path, "r");
28 * System control handler. This covers high-level control, including
29 * admin related functions and starting/stopping experiments..
30 * @param context The context to work in
31 * @param params The input parameters
33 void Control_Handler(FCGIContext *context, char *params) {
34 const char *action, *key = "", *name = "";
36 ControlModes desired_mode;
38 FCGIValue values[4] = {
39 {"action", &action, FCGI_REQUIRED(FCGI_STRING_T)},
40 {"key", &key, FCGI_STRING_T},
41 {"force", &force, FCGI_BOOL_T},
42 {"name", &name, FCGI_STRING_T}
45 if (!FCGI_ParseRequest(context, params, values, 4))
48 if (!strcmp(action, "lock")) {
49 FCGI_LockControl(context, force);
51 } else if (!strcmp(action, "emergency")) {
52 desired_mode = CONTROL_EMERGENCY;
53 } else if (!strcmp(action, "query")) {
54 FCGI_BeginJSON(context, STATUS_OK);
55 FCGI_JSONPair("state", Control_GetModeName(Control_GetMode()));
58 } else if (FCGI_HasControl(context, key)) {
59 if (!strcmp(action, "release")) {
60 FCGI_ReleaseControl(context);
61 } else if (!strcmp(action, "start")) {
62 desired_mode = CONTROL_START;
63 } else if (!strcmp(action, "pause")) {
64 desired_mode = CONTROL_PAUSE;
65 } else if (!strcmp(action, "resume")) {
66 desired_mode = CONTROL_RESUME;
67 } else if (!strcmp(action, "stop")) {
68 desired_mode = CONTROL_STOP;
70 FCGI_RejectJSON(context, "Unknown action specified.");
74 FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED,
75 "Invalid control key specified.");
80 if (desired_mode == CONTROL_START) {
81 int len = strlen(name);
83 FCGI_RejectJSON(context, "An experiment name must be specified.");
85 } else if (PathExists(name) && !force) {
86 FCGI_RejectJSON(context, "An experiment with that name already exists.");
94 if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
95 FCGI_RejectJSON(context, ret);
97 FCGI_BeginJSON(context, STATUS_OK);
98 FCGI_JSONPair("description", "ok");
104 * Sets the mode to the desired mode, if possible.
105 * @param desired_mode The mode to be set to
106 * @param arg An argument specific to the mode to be set.
107 * @return NULL on success, an error message on failure.
109 const char* Control_SetMode(ControlModes desired_mode, void * arg)
111 const char *ret = NULL;
113 pthread_mutex_lock(&(g_controls.mutex));
114 if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP) {
115 ret = "In emergency mode. Stop before doing anything else.";
116 } else if (g_controls.current_mode == desired_mode) {
117 ret = "Already in desired mode.";
118 } else if (desired_mode == CONTROL_START) {
119 if (g_controls.current_mode == CONTROL_STOP) {
120 //TODO Sanitise name (ensure it contains no special chars eg \ / .. .
121 FILE *fp = fopen((const char*) arg, "a");
124 gettimeofday(&(g_controls.start_time), NULL);
126 ret = "Cannot open experiment name marker";
129 ret = "Cannot start when not in a stopped state.";
131 } else if (desired_mode == CONTROL_RESUME) {
132 if (g_controls.current_mode != CONTROL_PAUSE)
133 ret = "Cannot resume when not in a paused state.";
137 Actuator_SetModeAll(desired_mode, arg);
138 Sensor_SetModeAll(desired_mode, arg);
139 if (desired_mode != CONTROL_RESUME)
140 g_controls.current_mode = desired_mode;
142 g_controls.current_mode = CONTROL_START;
144 pthread_mutex_unlock(&(g_controls.mutex));
149 * Gets the current mode.
150 * @return The current mode
152 ControlModes Control_GetMode() {
153 return g_controls.current_mode;
157 * Gets a string representation of a mode
158 * @param mode The mode to get a string representation of
159 * @return The string representation of the mode
161 const char * Control_GetModeName(ControlModes mode) {
162 const char * ret = "Unknown";
165 case CONTROL_START: ret = "Running"; break;
166 case CONTROL_PAUSE: ret = "Paused"; break;
167 case CONTROL_RESUME: ret = "Resumed"; break;
168 case CONTROL_STOP: ret = "Stopped"; break;
169 case CONTROL_EMERGENCY: ret = "Emergency mode"; break;
175 bool Control_Lock() {
176 pthread_mutex_lock(&(g_controls.mutex));
177 if (g_controls.state == STATE_RUNNING || g_controls.state == STATE_PAUSED)
179 pthread_mutex_unlock(&(g_controls.mutex));
183 void Control_Unlock() {
184 pthread_mutex_unlock(&(g_controls.mutex));
188 * Gets the start time for the current experiment
189 * @return the start time
191 const struct timeval* Control_GetStartTime() {
192 return &g_controls.start_time;