Merge branch 'master' of https://github.com/szmoore/MCTX3420.git
[matches/MCTX3420.git] / server / control.c
1 /**
2  * @file control.c
3  * @brief Handles all client control requests (admin related)
4  */
5 #include "common.h"
6 #include "control.h"
7 #include "sensor.h"
8 #include "actuator.h"
9
10 typedef struct ControlData {
11         ControlModes current_mode;
12         pthread_mutex_t mutex;
13         struct timeval start_time;
14 } ControlData;
15
16 ControlData g_controls = {CONTROL_STOP, PTHREAD_MUTEX_INITIALIZER, {0}};
17
18 static bool PathExists(const char *path) {
19         FILE *fp = fopen(path, "r");
20         if (fp) {
21                 fclose(fp);
22                 return true;
23         }
24         return false;
25 }
26
27 /**
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
32  */
33 void Control_Handler(FCGIContext *context, char *params) {
34         const char *action, *key = "", *name = "";
35         bool force = false;
36         ControlModes desired_mode;
37
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}
43         };
44
45         if (!FCGI_ParseRequest(context, params, values, 4))
46                 return;
47         
48         if (!strcmp(action, "lock")) {
49                 FCGI_LockControl(context, force);
50                 return;
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()));
56                 FCGI_EndJSON();
57                 return;
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;
69                 } else {
70                         FCGI_RejectJSON(context, "Unknown action specified.");
71                         return;
72                 }
73         } else {
74                 FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, 
75                         "Invalid control key specified.");
76                 return;
77         }
78
79         void *arg = NULL;
80         if (desired_mode == CONTROL_START) {
81                 int len = strlen(name);
82                 if (len <= 0) {
83                         FCGI_RejectJSON(context, "An experiment name must be specified.");
84                         return;
85                 } else if (PathExists(name) && !force) {
86                         FCGI_RejectJSON(context, "An experiment with that name already exists.");
87                         return;
88                 }
89
90                 arg = (void*)name;
91         }
92
93         const char *ret;
94         if ((ret = Control_SetMode(desired_mode, arg)) != NULL) {
95                 FCGI_RejectJSON(context, ret);
96         } else {
97                 FCGI_BeginJSON(context, STATUS_OK);
98                 FCGI_JSONPair("description", "ok");
99                 FCGI_EndJSON();
100         }
101 }
102
103 /**
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.
108  */
109 const char* Control_SetMode(ControlModes desired_mode, void * arg)
110 {
111         const char *ret = NULL;
112
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");
122                         if (fp) {
123                                 fclose(fp);
124                                 gettimeofday(&(g_controls.start_time), NULL);
125                         } else {
126                                 ret = "Cannot open experiment name marker";
127                         }
128                 } else {
129                         ret = "Cannot start when not in a stopped state.";
130                 }
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.";
134         }
135         
136         if (ret == NULL) {
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;
141                 else
142                         g_controls.current_mode = CONTROL_START;
143         }
144         pthread_mutex_unlock(&(g_controls.mutex));
145         return ret;
146 }
147
148 /**
149  * Gets the current mode.
150  * @return The current mode
151  */
152 ControlModes Control_GetMode() {
153         return g_controls.current_mode;
154 }
155
156 /**
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
160  */
161 const char * Control_GetModeName(ControlModes mode) {
162         const char * ret = "Unknown";
163
164         switch (mode) {
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;
170         }
171         return ret;
172 }
173
174 /*
175 bool Control_Lock() {
176         pthread_mutex_lock(&(g_controls.mutex));
177         if (g_controls.state == STATE_RUNNING || g_controls.state == STATE_PAUSED)
178                 return true;
179         pthread_mutex_unlock(&(g_controls.mutex));
180         return false;
181 }
182
183 void Control_Unlock() {
184         pthread_mutex_unlock(&(g_controls.mutex));
185 }*/
186
187 /**
188  * Gets the start time for the current experiment
189  * @return the start time
190  */
191 const struct timeval* Control_GetStartTime() {
192         return &g_controls.start_time;
193 }

UCC git Repository :: git.ucc.asn.au