Change control code - some bugfixes, plus stop threads in paused state
[matches/MCTX3420.git] / server / actuator.c
1 /**
2  * @file actuator.c
3  * @brief Implementation of Actuator related functionality
4  */
5
6 #include "actuator.h"
7 #include "options.h"
8
9 /** Array of Actuators (global to this file) initialised by Actuator_Init **/
10 static Actuator g_actuators[NUMACTUATORS];
11
12 /** Human readable names for the Actuators **/
13 const char * g_actuator_names[NUMACTUATORS] = { 
14         "actuator_test0", "actuator_test1"
15 };
16
17 /**
18  * One off initialisation of *all* Actuators
19  */
20 void Actuator_Init()
21 {
22         for (int i = 0; i < NUMACTUATORS; ++i)
23         {
24                 g_actuators[i].id = i;
25                 Data_Init(&(g_actuators[i].data_file));
26                 pthread_mutex_init(&(g_actuators[i].mutex), NULL);
27         }
28 }
29
30 /**
31  * Sets the actuator to the desired mode. No checks are
32  * done to see if setting to the desired mode will conflict with
33  * the current mode - the caller must guarantee this itself.
34  * @param a The actuator whose mode is to be changed
35  * @param mode The mode to be changed to
36  * @param arg An argument specific to the mode to be set. 
37  *            e.g for CONTROL_START it represents the experiment name.
38  */
39 void Actuator_SetMode(Actuator * a, ControlModes mode, void *arg)
40 {
41         switch (mode)
42         {
43                 case CONTROL_START:
44                         {
45                                 char filename[BUFSIZ];
46                                 const char *experiment_name = (const char*) arg;
47
48                                 if (snprintf(filename, BUFSIZ, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
49                                 {
50                                         Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
51                                 }
52
53                                 Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
54                                 // Open DataFile
55                                 Data_Open(&(a->data_file), filename);
56                         } 
57                 case CONTROL_RESUME:  //Case fallthrough; no break before
58                         {
59                                 int ret;
60                                 a->activated = true; // Don't forget this
61                                 a->control_changed = false;
62
63                                 ret = pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
64                                 if (ret != 0)
65                                 {
66                                         Fatal("Failed to create Actuator_Loop for Actuator %d", a->id);
67                                 }
68
69                                 Log(LOGDEBUG, "Resuming actuator %d", a->id);
70                         }
71                 break;
72
73                 case CONTROL_EMERGENCY: //TODO add proper case for emergency
74                 case CONTROL_PAUSE:
75                         a->activated = false;
76                         Actuator_SetControl(a, NULL);
77                         pthread_join(a->thread, NULL); // Wait for thread to exit
78
79                         Log(LOGDEBUG, "Paused actuator %d", a->id);
80                 break;
81
82                 break;
83                 case CONTROL_STOP:
84                         if (a->activated) //May have been paused before
85                         {
86                                 a->activated = false;
87                                 Actuator_SetControl(a, NULL);
88                                 pthread_join(a->thread, NULL); // Wait for thread to exit       
89                         }
90                         Data_Close(&(a->data_file)); // Close DataFile
91                         
92                         Log(LOGDEBUG, "Stopped actuator %d", a->id);
93                 break;
94                 default:
95                         Fatal("Unknown control mode: %d", mode);
96         }
97 }
98
99 /**
100  * Sets all actuators to the desired mode. 
101  * @see Actuator_SetMode for more information.
102  * @param mode The mode to be changed to
103  * @param arg An argument specific to the mode to be set.
104  */
105 void Actuator_SetModeAll(ControlModes mode, void * arg)
106 {
107         for (int i = 0; i < NUMACTUATORS; i++)
108                 Actuator_SetMode(&g_actuators[i], mode, arg);
109 }
110
111 /**
112  * Actuator control thread
113  * @param arg - Cast to an Actuator*
114  * @returns NULL to keep pthreads happy
115  */
116 void * Actuator_Loop(void * arg)
117 {
118         Actuator * a = (Actuator*)(arg);
119         
120         // Loop until stopped
121         while (a->activated)
122         {
123                 pthread_mutex_lock(&(a->mutex));
124                 while (!a->control_changed)
125                 {
126                         pthread_cond_wait(&(a->cond), &(a->mutex));
127                 }
128                 a->control_changed = false;
129                 pthread_mutex_unlock(&(a->mutex));
130                 if (!a->activated)
131                         break;
132
133                 Actuator_SetValue(a, a->control.value);
134         }
135
136         //TODO: Cleanup?
137         
138         // Keep pthreads happy
139         return NULL;
140 }
141
142 /**
143  * Set an Actuators control variable
144  * @param a - Actuator to control 
145  * @param c - Control to set to
146  */
147 void Actuator_SetControl(Actuator * a, ActuatorControl * c)
148 {
149         pthread_mutex_lock(&(a->mutex));
150         if (c != NULL)
151                 a->control = *c;
152         a->control_changed = true;
153         pthread_cond_broadcast(&(a->cond));
154         pthread_mutex_unlock(&(a->mutex));
155         
156 }
157
158 /**
159  * Set an Actuator value
160  * @param a - The Actuator
161  * @param value - The value to set
162  */
163 void Actuator_SetValue(Actuator * a, double value)
164 {
165         // Set time stamp
166         struct timeval t;
167         gettimeofday(&t, NULL);
168
169         DataPoint d = {TIMEVAL_DIFF(t, *Control_GetStartTime()), value};
170         //TODO: Set actuator
171         switch (a->id)
172         {
173                 case ACTUATOR_TEST0: 
174                         {
175                                 FILE *led_handle = NULL;        //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
176                                 const char *led_format = "/sys/class/leds/beaglebone:green:usr%d/brightness";
177                                 char buf[50];
178                                 bool turn_on = value;
179
180                                 for (int i = 0; i < 4; i++) 
181                                 {
182                                         snprintf(buf, 50, led_format, i);
183                                         if ((led_handle = fopen(buf, "w")) != NULL)
184                                         {
185                                                 if (turn_on)
186                                                         fwrite("1", sizeof(char), 1, led_handle);
187                                                 else
188                                                         fwrite("0", sizeof(char), 1, led_handle);
189                                                 fclose(led_handle);
190                                         }
191                                         else
192                                                 Log(LOGDEBUG, "LED fopen failed: %s", strerror(errno)); 
193                                 }
194                         }
195                         break;
196                 case ACTUATOR_TEST1:
197                         break;
198         }
199
200         Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
201
202         // Record the value
203         Data_Save(&(a->data_file), &d, 1);
204 }
205
206 /**
207  * Helper: Begin Actuator response in a given format
208  * @param context - the FCGIContext
209  * @param format - Format
210  * @param id - ID of Actuator
211  */
212 void Actuator_BeginResponse(FCGIContext * context, ActuatorId id, DataFormat format)
213 {
214         // Begin response
215         switch (format)
216         {
217                 case JSON:
218                         FCGI_BeginJSON(context, STATUS_OK);
219                         FCGI_JSONLong("id", id);
220                         break;
221                 default:
222                         FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
223                         break;
224         }
225 }
226
227 /**
228  * Helper: End Actuator response in a given format
229  * @param context - the FCGIContext
230  * @param id - ID of the Actuator
231  * @param format - Format
232  */
233 void Actuator_EndResponse(FCGIContext * context, ActuatorId id, DataFormat format)
234 {
235         // End response
236         switch (format)
237         {
238                 case JSON:
239                         FCGI_EndJSON();
240                         break;
241                 default:
242                         break;
243         }
244 }
245
246
247 /**
248  * Handle a request for an Actuator
249  * @param context - FCGI context
250  * @param params - Parameters passed
251  */
252 void Actuator_Handler(FCGIContext * context, char * params)
253 {
254         struct timeval now;
255         gettimeofday(&now, NULL);
256         double current_time = TIMEVAL_DIFF(now, *Control_GetStartTime());
257         int id = 0;
258         double set = 0;
259         double start_time = 0;
260         double end_time = current_time;
261         char * fmt_str;
262
263         // key/value pairs
264         FCGIValue values[] = {
265                 {"id", &id, FCGI_REQUIRED(FCGI_INT_T)}, 
266                 {"set", &set, FCGI_DOUBLE_T},
267                 {"start_time", &start_time, FCGI_DOUBLE_T},
268                 {"end_time", &end_time, FCGI_DOUBLE_T},
269                 {"format", &fmt_str, FCGI_STRING_T}
270         };
271
272         // enum to avoid the use of magic numbers
273         typedef enum {
274                 ID,
275                 SET,
276                 START_TIME,
277                 END_TIME,
278                 FORMAT
279         } ActuatorParams;
280         
281         // Fill values appropriately
282         if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
283         {
284                 // Error occured; FCGI_RejectJSON already called
285                 return;
286         }       
287
288         // Get the Actuator identified
289         Actuator * a = NULL;
290         if (id < 0 || id >= NUMACTUATORS)
291         {
292                 FCGI_RejectJSON(context, "Invalid Actuator id");
293                 return;
294         }
295         
296         a = g_actuators+id;
297
298         DataFormat format = Data_GetFormat(&(values[FORMAT]));
299
300         // Begin response
301         Actuator_BeginResponse(context, id, format);
302
303         // Set?
304         if (FCGI_RECEIVED(values[SET].flags))
305         {
306                 if (format == JSON)
307                         FCGI_JSONDouble("set", set);
308         
309                 ActuatorControl c;
310                 c.value = set;
311
312                 Actuator_SetControl(a, &c);
313         }
314
315         // Print Data
316         Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
317         
318         // Finish response
319         Actuator_EndResponse(context, id, format);
320 }

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