Make it work on the BBB
[matches/MCTX3420.git] / server / actuator.c
1 /**
2  * @file actuator.c
3  * @purpose 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  * Start an Actuator
32  * @param a - The Actuator to start
33  * @param experiment_name - Prepended to DataFile filename
34  */
35 void Actuator_Start(Actuator * a, const char * experiment_name)
36 {
37         // Set filename
38         char filename[BUFSIZ];
39         if (sprintf(filename, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
40         {
41                 Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
42         }
43
44         Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
45         // Open DataFile
46         Data_Open(&(a->data_file), filename);
47
48         a->activated = true; // Don't forget this
49         
50         a->control_changed = false;
51
52         // Create the thread
53         pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
54 }
55
56 /**
57  * Stop an Actuator
58  * @param s - The Actuator to stop
59  */
60 void Actuator_Stop(Actuator * a)
61 {
62         // Stop
63         a->activated = false;
64         Actuator_SetControl(a, NULL);
65         pthread_join(a->thread, NULL); // Wait for thread to exit
66         Data_Close(&(a->data_file)); // Close DataFile
67
68 }
69
70 /**
71  * Stop all Actuators
72  */
73 void Actuator_StopAll()
74 {
75         for (int i = 0; i < NUMACTUATORS; ++i)
76                 Actuator_Stop(g_actuators+i);
77 }
78
79 /**
80  * Start all Actuators
81  */
82 void Actuator_StartAll(const char * experiment_name)
83 {
84         for (int i = 0; i < NUMACTUATORS; ++i)
85                 Actuator_Start(g_actuators+i, experiment_name);
86 }
87
88 /**
89  * Actuator control thread
90  * @param arg - Cast to an Actuator*
91  * @returns NULL to keep pthreads happy
92  */
93 void * Actuator_Loop(void * arg)
94 {
95         Actuator * a = (Actuator*)(arg);
96         
97         // Loop until stopped
98         while (a->activated)
99         {
100                 pthread_mutex_lock(&(a->mutex));
101                 while (!a->control_changed)
102                 {
103                         pthread_cond_wait(&(a->cond), &(a->mutex));
104                 }
105                 a->control_changed = false;
106                 pthread_mutex_unlock(&(a->mutex));
107                 if (!a->activated)
108                         break;
109                 Log(LOGDEBUG, "About to Setvalue");
110                 Actuator_SetValue(a, a->control.value);
111         }
112
113         //TODO: Cleanup?
114         
115         // Keep pthreads happy
116         return NULL;
117 }
118
119 /**
120  * Set an Actuators control variable
121  * @param a - Actuator to control 
122  * @param c - Control to set to
123  */
124 void Actuator_SetControl(Actuator * a, ActuatorControl * c)
125 {
126         pthread_mutex_lock(&(a->mutex));
127         if (c != NULL)
128                 a->control = *c;
129         a->control_changed = true;
130         Log(LOGDEBUG, "About to broadcast");
131         pthread_cond_broadcast(&(a->cond));
132         pthread_mutex_unlock(&(a->mutex));
133         
134 }
135
136 /**
137  * Set an Actuator value
138  * @param a - The Actuator
139  * @param value - The value to set
140  */
141 void Actuator_SetValue(Actuator * a, double value)
142 {
143         // Set time stamp
144         struct timeval t;
145         gettimeofday(&t, NULL);
146
147         DataPoint d = {TIMEVAL_DIFF(t, g_options.start_time), value};
148         Log(LOGDEBUG, "id: %d", a->id);
149         //TODO: Set actuator
150         switch (a->id)
151         {
152                 case ACTUATOR_TEST0:    
153                         {//LED actuator test code, should blink onboard LED next to Ethernet port
154                                 FILE *LEDHandle = NULL;         //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
155                                 char *LEDBrightness = "/sys/class/leds/beaglebone:green:usr3/brightness";
156                                 int val = (!!(int)value);
157                                 Log(LOGDEBUG, "Val: %d", val);
158                                 if(val == 1) {
159                                         if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
160                                                 fwrite("1", sizeof(char), 1, LEDHandle);
161                                                 fclose(LEDHandle);
162                                         } else perror("fail");
163                                 }
164                                 else if(val == 0) {
165                                         if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
166                                                 fwrite("0", sizeof(char), 1, LEDHandle);
167                                                 fclose(LEDHandle);
168                                         }
169                                 }
170                                 else perror("Pin value should be 1 or 0");
171                         }
172                         break;
173                 case ACTUATOR_TEST1:
174                         break;
175         }
176
177         Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
178
179         // Record the value
180         Data_Save(&(a->data_file), &d, 1);
181 }
182
183
184 /**
185  * Helper: Begin Actuator response in a given format
186  * @param context - the FCGIContext
187  * @param format - Format
188  * @param id - ID of Actuator
189  */
190 void Actuator_BeginResponse(FCGIContext * context, ActuatorId id, DataFormat format)
191 {
192         // Begin response
193         switch (format)
194         {
195                 case JSON:
196                         FCGI_BeginJSON(context, STATUS_OK);
197                         FCGI_JSONLong("id", id);
198                         break;
199                 default:
200                         FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
201                         break;
202         }
203 }
204
205 /**
206  * Helper: End Actuator response in a given format
207  * @param context - the FCGIContext
208  * @param id - ID of the Actuator
209  * @param format - Format
210  */
211 void Actuator_EndResponse(FCGIContext * context, ActuatorId id, DataFormat format)
212 {
213         // End response
214         switch (format)
215         {
216                 case JSON:
217                         FCGI_EndJSON();
218                         break;
219                 default:
220                         break;
221         }
222 }
223
224
225
226
227 /**
228  * Handle a request for an Actuator
229  * @param context - FCGI context
230  * @param params - Parameters passed
231  */
232 void Actuator_Handler(FCGIContext * context, char * params)
233 {
234         struct timeval now;
235         gettimeofday(&now, NULL);
236         double current_time = TIMEVAL_DIFF(now, g_options.start_time);
237         int id = 0;
238         double set = 0;
239         double start_time = 0;
240         double end_time = current_time;
241         char * fmt_str;
242
243         // key/value pairs
244         FCGIValue values[] = {
245                 {"id", &id, FCGI_REQUIRED(FCGI_INT_T)}, 
246                 {"set", &set, FCGI_DOUBLE_T},
247                 {"start_time", &start_time, FCGI_DOUBLE_T},
248                 {"end_time", &end_time, FCGI_DOUBLE_T},
249                 {"format", &fmt_str, FCGI_STRING_T}
250         };
251
252         // enum to avoid the use of magic numbers
253         typedef enum {
254                 ID,
255                 SET,
256                 START_TIME,
257                 END_TIME,
258                 FORMAT
259         } ActuatorParams;
260         
261         // Fill values appropriately
262         if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
263         {
264                 // Error occured; FCGI_RejectJSON already called
265                 return;
266         }       
267
268         // Get the Actuator identified
269         Actuator * a = NULL;
270         if (id < 0 || id >= NUMACTUATORS)
271         {
272                 FCGI_RejectJSON(context, "Invalid Actuator id");
273                 return;
274         }
275         
276         a = g_actuators+id;
277
278         DataFormat format = Data_GetFormat(&(values[FORMAT]));
279
280         // Begin response
281         Actuator_BeginResponse(context, id, format);
282
283         // Set?
284         if (FCGI_RECEIVED(values[SET].flags))
285         {
286                 if (format == JSON)
287                         FCGI_JSONDouble("set", set);
288         
289                 ActuatorControl c;
290                 c.value = set;
291
292                 Actuator_SetControl(a, &c);
293         }
294
295         // Print Data
296         Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
297         
298         // Finish response
299         Actuator_EndResponse(context, id, format);
300 }

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