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 bool PathExists(const char *path)
20 FILE *fp = fopen(path, "r");
29 * System control handler. This covers high-level control, including
30 * admin related functions and starting/stopping experiments..
31 * @param context The context to work in
32 * @param params The input parameters
34 void Control_Handler(FCGIContext *context, char *params) {
35 const char *action = "";
36 const char *name = "";
38 ControlModes desired_mode;
42 // Login/auth now handled entirely in fastcgi.c and login.c
43 //TODO: Need to not have the ability for any user to stop someone else' experiment...
44 // (achieve by storing the username of the person running the current experiment, even when they log out?)
45 // (Our program should only realisitically support a single experiment at a time, so that should be sufficient)
46 FCGIValue values[4] = {
47 {"action", &action, FCGI_REQUIRED(FCGI_STRING_T)},
48 {"force", &force, FCGI_BOOL_T},
49 {"name", &name, FCGI_STRING_T}
52 if (!FCGI_ParseRequest(context, params, values, 4))
55 if (!strcmp(action, "lock")) {
56 FCGI_LockControl(context, force);
58 } else if (!strcmp(action, "emergency")) {
59 desired_mode = CONTROL_EMERGENCY;
61 else if (!strcmp(action, "release")) {
62 FCGI_ReleaseControl(context);
63 } else if (!strcmp(action, "start")) {
64 desired_mode = CONTROL_START;
65 } else if (!strcmp(action, "pause")) {
66 desired_mode = CONTROL_PAUSE;
67 } else if (!strcmp(action, "resume")) {
68 desired_mode = CONTROL_RESUME;
69 } else if (!strcmp(action, "stop")) {
70 desired_mode = CONTROL_STOP;
72 FCGI_RejectJSON(context, "Unknown action specified.");
77 if (desired_mode == CONTROL_START) {
78 if (PathExists(name) && !force) {
79 FCGI_RejectJSON(context, "An experiment with that name already exists.");
87 if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
88 FCGI_RejectJSON(context, ret);
90 FCGI_BeginJSON(context, STATUS_OK);
91 FCGI_JSONPair("description", "ok");
97 * Sets the mode to the desired mode, if possible.
98 * @param desired_mode The mode to be set to
99 * @param arg An argument specific to the mode to be set.
100 * @return NULL on success, an error message on failure.
102 const char* Control_SetMode(ControlModes desired_mode, void * arg)
104 const char *ret = NULL;
106 pthread_mutex_lock(&(g_controls.mutex));
107 if (g_controls.current_mode == desired_mode)
108 ret = "Already in the desired mode.";
109 else if (g_controls.current_mode == CONTROL_EMERGENCY && desired_mode != CONTROL_STOP)
110 ret = "In emergency mode. You must stop before continuing.";
111 else switch (desired_mode) {
113 if (g_controls.current_mode == CONTROL_STOP) {
114 const char * name = arg;
116 ret = "An experiment name must be specified";
117 else if (strpbrk(name, INVALID_CHARACTERS))
118 ret = "The experiment name must not contain: " INVALID_CHARACTERS_JSON;
120 FILE *fp = fopen((const char*) arg, "a");
123 gettimeofday(&(g_controls.start_time), NULL);
125 ret = "Cannot open experiment name marker";
128 ret = "Cannot start when not in a stopped state.";
131 if (g_controls.current_mode != CONTROL_START)
132 ret = "Cannot pause when not in a running state.";
135 if (g_controls.current_mode != CONTROL_PAUSE)
136 ret = "Cannot resume when not in a paused state.";
138 case CONTROL_EMERGENCY:
139 if (g_controls.current_mode != CONTROL_START) //pfft
140 ret = "Not running so how can there be an emergency.";
147 Actuator_SetModeAll(desired_mode, arg);
148 Sensor_SetModeAll(desired_mode, arg);
149 if (desired_mode != CONTROL_RESUME)
150 g_controls.current_mode = desired_mode;
152 g_controls.current_mode = CONTROL_START;
154 pthread_mutex_unlock(&(g_controls.mutex));
159 * Gets a string representation of the current mode
160 * @param mode The mode to get a string representation of
161 * @return The string representation of the mode
163 const char * Control_GetModeName() {
164 const char * ret = "Unknown";
166 switch (g_controls.current_mode) {
167 case CONTROL_START: ret = "Running"; break;
168 case CONTROL_PAUSE: ret = "Paused"; break;
169 case CONTROL_RESUME: ret = "Resumed"; break;
170 case CONTROL_STOP: ret = "Stopped"; break;
171 case CONTROL_EMERGENCY: ret = "Emergency mode"; break;
177 * Gets the start time for the current experiment
178 * @return the start time
180 const struct timeval* Control_GetStartTime() {
181 return &g_controls.start_time;