Cleaned up Beaglebone sensors/actuators/PWM code
[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
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         pthread_cond_broadcast(&(a->cond));
131         pthread_mutex_unlock(&(a->mutex));
132         
133 }
134
135 /**
136  * Set an Actuator value
137  * @param a - The Actuator
138  * @param value - The value to set
139  */
140 void Actuator_SetValue(Actuator * a, double value)
141 {
142         // Set time stamp
143         struct timeval t;
144         gettimeofday(&t, NULL);
145
146         DataPoint d = {TIMEVAL_DIFF(t, g_options.start_time), value};
147         //TODO: Set actuator
148         switch (a->id)
149         {
150                 case ACTUATOR_TEST0:                    //LED actuator test code, should blink onboard LED next to Ethernet port
151                         FILE *LEDHandle = NULL;         //code reference: http://learnbuildshare.wordpress.com/2013/05/19/beaglebone-black-controlling-user-leds-using-c/
152                         char *LEDBrightness = "/sys/class/leds/beaglebone\:green\:usr0/brightness";
153                         if(value == 1) {
154                                 if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
155                                         fwrite("1", sizeof(char), 1, LEDHandle);
156                                         fclose(LEDHandle);
157                                 }
158                         else if(value == 0) {
159                                 if((LEDHandle = fopen(LEDBrightness, "r+")) != NULL) {
160                                         fwrite("0", sizeof(char), 1, LEDHandle);
161                                         fclose(LEDHandle);
162                         }
163                         else perror("Pin value should be 1 or 0");
164                         break;
165                 case ACTUATOR_TEST1:
166                         break;
167         }
168
169         Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
170
171         // Record the value
172         Data_Save(&(a->data_file), &d, 1);
173 }
174
175 /**
176  * Helper: Begin Actuator response in a given format
177  * @param context - the FCGIContext
178  * @param format - Format
179  * @param id - ID of Actuator
180  */
181 void Actuator_BeginResponse(FCGIContext * context, ActuatorId id, DataFormat format)
182 {
183         // Begin response
184         switch (format)
185         {
186                 case JSON:
187                         FCGI_BeginJSON(context, STATUS_OK);
188                         FCGI_JSONLong("id", id);
189                         break;
190                 default:
191                         FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
192                         break;
193         }
194 }
195
196 /**
197  * Helper: End Actuator response in a given format
198  * @param context - the FCGIContext
199  * @param id - ID of the Actuator
200  * @param format - Format
201  */
202 void Actuator_EndResponse(FCGIContext * context, ActuatorId id, DataFormat format)
203 {
204         // End response
205         switch (format)
206         {
207                 case JSON:
208                         FCGI_EndJSON();
209                         break;
210                 default:
211                         break;
212         }
213 }
214
215
216
217
218 /**
219  * Handle a request for an Actuator
220  * @param context - FCGI context
221  * @param params - Parameters passed
222  */
223 void Actuator_Handler(FCGIContext * context, char * params)
224 {
225         struct timeval now;
226         gettimeofday(&now, NULL);
227         double current_time = TIMEVAL_DIFF(now, g_options.start_time);
228         int id = 0;
229         double set = 0;
230         double start_time = 0;
231         double end_time = current_time;
232         char * fmt_str;
233
234         // key/value pairs
235         FCGIValue values[] = {
236                 {"id", &id, FCGI_REQUIRED(FCGI_LONG_T)}, 
237                 {"set", &set, FCGI_DOUBLE_T},
238                 {"start_time", &start_time, FCGI_DOUBLE_T},
239                 {"end_time", &end_time, FCGI_DOUBLE_T},
240                 {"format", &fmt_str, FCGI_STRING_T}
241         };
242
243         // enum to avoid the use of magic numbers
244         typedef enum {
245                 ID,
246                 SET,
247                 START_TIME,
248                 END_TIME,
249                 FORMAT
250         } ActuatorParams;
251         
252         // Fill values appropriately
253         if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
254         {
255                 // Error occured; FCGI_RejectJSON already called
256                 return;
257         }       
258
259         // Get the Actuator identified
260         Actuator * a = NULL;
261         if (id < 0 || id >= NUMACTUATORS)
262         {
263                 FCGI_RejectJSON(context, "Invalid Actuator id");
264                 return;
265         }
266         
267         a = g_actuators+id;
268
269         DataFormat format = Data_GetFormat(&(values[FORMAT]));
270
271         // Begin response
272         Actuator_BeginResponse(context, id, format);
273
274         // Set?
275         if (FCGI_RECEIVED(values[SET].flags))
276         {
277                 if (format == JSON)
278                         FCGI_JSONDouble("set", set);
279         
280                 ActuatorControl c;
281                 c.value = set;
282
283                 Actuator_SetControl(a, &c);
284         }
285
286         // Print Data
287         Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
288         
289         // Finish response
290         Actuator_EndResponse(context, id, format);
291 }

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