Partial implementation of higher level control functions
[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 "control.h"
8 #include "options.h"
9
10 /** Array of Actuators (global to this file) initialised by Actuator_Init **/
11 static Actuator g_actuators[NUMACTUATORS];
12
13 /** Human readable names for the Actuators **/
14 const char * g_actuator_names[NUMACTUATORS] = { 
15         "actuator_test0", "actuator_test1"
16 };
17
18 /**
19  * One off initialisation of *all* Actuators
20  */
21 void Actuator_Init()
22 {
23         for (int i = 0; i < NUMACTUATORS; ++i)
24         {
25                 g_actuators[i].id = i;
26                 Data_Init(&(g_actuators[i].data_file));
27                 pthread_mutex_init(&(g_actuators[i].mutex), NULL);
28         }
29 }
30
31 /**
32  * Start an Actuator
33  * @param a - The Actuator to start
34  * @param experiment_name - Prepended to DataFile filename
35  */
36 void Actuator_Start(Actuator * a, const char * experiment_name)
37 {
38         // Set filename
39         char filename[BUFSIZ];
40         if (sprintf(filename, "%s_a%d", experiment_name, a->id) >= BUFSIZ)
41         {
42                 Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
43         }
44
45         Log(LOGDEBUG, "Actuator %d with DataFile \"%s\"", a->id, filename);
46         // Open DataFile
47         Data_Open(&(a->data_file), filename);
48
49         a->activated = true; // Don't forget this
50         
51         a->control_changed = false;
52
53         // Create the thread
54         pthread_create(&(a->thread), NULL, Actuator_Loop, (void*)(a));
55 }
56
57 /**
58  * Stop an Actuator
59  * @param s - The Actuator to stop
60  */
61 void Actuator_Stop(Actuator * a)
62 {
63         // Stop
64         a->activated = false;
65         Actuator_SetControl(a, NULL);
66         pthread_join(a->thread, NULL); // Wait for thread to exit
67         Data_Close(&(a->data_file)); // Close DataFile
68
69 }
70
71 /**
72  * Stop all Actuators
73  */
74 void Actuator_StopAll()
75 {
76         for (int i = 0; i < NUMACTUATORS; ++i)
77                 Actuator_Stop(g_actuators+i);
78 }
79
80 /**
81  * Start all Actuators
82  */
83 void Actuator_StartAll(const char * experiment_name)
84 {
85         for (int i = 0; i < NUMACTUATORS; ++i)
86                 Actuator_Start(g_actuators+i, experiment_name);
87 }
88
89 /**
90  * Actuator control thread
91  * @param arg - Cast to an Actuator*
92  * @returns NULL to keep pthreads happy
93  */
94 void * Actuator_Loop(void * arg)
95 {
96         Actuator * a = (Actuator*)(arg);
97         
98         // Loop until stopped
99         while (a->activated)
100         {
101                 pthread_mutex_lock(&(a->mutex));
102                 while (!a->control_changed)
103                 {
104                         pthread_cond_wait(&(a->cond), &(a->mutex));
105                 }
106                 a->control_changed = false;
107                 pthread_mutex_unlock(&(a->mutex));
108                 if (!a->activated)
109                         break;
110
111                 Actuator_SetValue(a, a->control.value);
112         }
113
114         //TODO: Cleanup?
115         
116         // Keep pthreads happy
117         return NULL;
118 }
119
120 /**
121  * Set an Actuators control variable
122  * @param a - Actuator to control 
123  * @param c - Control to set to
124  */
125 void Actuator_SetControl(Actuator * a, ActuatorControl * c)
126 {
127         pthread_mutex_lock(&(a->mutex));
128         if (c != NULL)
129                 a->control = *c;
130         a->control_changed = true;
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         //TODO: Set actuator
149         switch (a->id)
150         {
151                 case ACTUATOR_TEST0:
152                         break;
153                 case ACTUATOR_TEST1:
154                         break;
155         }
156
157         Log(LOGDEBUG, "Actuator %s set to %f", g_actuator_names[a->id], value);
158
159         // Record the value
160         Data_Save(&(a->data_file), &d, 1);
161 }
162
163 /**
164  * Helper: Begin Actuator response in a given format
165  * @param context - the FCGIContext
166  * @param format - Format
167  * @param id - ID of Actuator
168  */
169 void Actuator_BeginResponse(FCGIContext * context, ActuatorId id, DataFormat format)
170 {
171         // Begin response
172         switch (format)
173         {
174                 case JSON:
175                         FCGI_BeginJSON(context, STATUS_OK);
176                         FCGI_JSONLong("id", id);
177                         break;
178                 default:
179                         FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
180                         break;
181         }
182 }
183
184 /**
185  * Helper: End Actuator response in a given format
186  * @param context - the FCGIContext
187  * @param id - ID of the Actuator
188  * @param format - Format
189  */
190 void Actuator_EndResponse(FCGIContext * context, ActuatorId id, DataFormat format)
191 {
192         // End response
193         switch (format)
194         {
195                 case JSON:
196                         FCGI_EndJSON();
197                         break;
198                 default:
199                         break;
200         }
201 }
202
203
204
205
206 /**
207  * Handle a request for an Actuator
208  * @param context - FCGI context
209  * @param params - Parameters passed
210  */
211 void Actuator_Handler(FCGIContext * context, char * params)
212 {
213         struct timeval now;
214         gettimeofday(&now, NULL);
215         double current_time = TIMEVAL_DIFF(now, g_options.start_time);
216         int id = 0;
217         double set = 0;
218         double start_time = 0;
219         double end_time = current_time;
220         char * fmt_str;
221
222         // key/value pairs
223         FCGIValue values[] = {
224                 {"id", &id, FCGI_REQUIRED(FCGI_INT_T)}, 
225                 {"set", &set, FCGI_DOUBLE_T},
226                 {"start_time", &start_time, FCGI_DOUBLE_T},
227                 {"end_time", &end_time, FCGI_DOUBLE_T},
228                 {"format", &fmt_str, FCGI_STRING_T}
229         };
230
231         // enum to avoid the use of magic numbers
232         typedef enum {
233                 ID,
234                 SET,
235                 START_TIME,
236                 END_TIME,
237                 FORMAT
238         } ActuatorParams;
239         
240         // Fill values appropriately
241         if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
242         {
243                 // Error occured; FCGI_RejectJSON already called
244                 return;
245         }       
246
247         // Get the Actuator identified
248         Actuator * a = NULL;
249         if (id < 0 || id >= NUMACTUATORS)
250         {
251                 FCGI_RejectJSON(context, "Invalid Actuator id");
252                 return;
253         }
254         
255         a = g_actuators+id;
256
257         DataFormat format = Data_GetFormat(&(values[FORMAT]));
258
259         if (Control_Lock())
260         {
261                 // Begin response
262                 Actuator_BeginResponse(context, id, format);
263
264                 // Set?
265                 if (FCGI_RECEIVED(values[SET].flags))
266                 {
267                         if (format == JSON)
268                                 FCGI_JSONDouble("set", set);
269                 
270                         ActuatorControl c;
271                         c.value = set;
272
273                         Actuator_SetControl(a, &c);
274                 }
275
276                 // Print Data
277                 Data_Handler(&(a->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
278                 
279                 // Finish response
280                 Actuator_EndResponse(context, id, format);
281
282                 Control_Unlock();
283         }
284         else
285         {
286                 FCGI_RejectJSON(context, "Experiment is not running.");
287         }
288 }

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