Add Actuator related code
[matches/MCTX3420.git] / server / sensor.c
1 /**
2  * @file sensor.c
3  * @brief Implementation of sensor thread
4  * TODO: Finalise implementation
5  */
6
7 #include "common.h"
8 #include "sensor.h"
9 #include "options.h"
10 #include <math.h>
11
12 /** Array of sensors, initialised by Sensor_Init **/
13 static Sensor g_sensors[NUMSENSORS]; //global to this file
14
15 /** Human readable names for the sensors **/
16 const char * g_sensor_names[NUMSENSORS] = {     
17         "analog_test0", "analog_test1", 
18         "digital_test0", "digital_test1"
19 };
20
21 /**
22  * One off initialisation of *all* sensors
23  */
24 void Sensor_Init()
25 {
26         for (int i = 0; i < NUMSENSORS; ++i)
27         {
28                 g_sensors[i].id = i;
29                 Data_Init(&(g_sensors[i].data_file));
30                 g_sensors[i].record_data = false;
31         }
32 }
33
34 /**
35  * Start a Sensor recording DataPoints
36  * @param s - The Sensor to start
37  * @param experiment_name - Prepended to DataFile filename
38  */
39 void Sensor_Start(Sensor * s, const char * experiment_name)
40 {
41         // Set filename
42         char filename[BUFSIZ];
43         if (sprintf(filename, "%s_s%d", experiment_name, s->id) >= BUFSIZ)
44         {
45                 Fatal("Experiment name \"%s\" too long (>%d)", experiment_name, BUFSIZ);
46         }
47
48         Log(LOGDEBUG, "Sensor %d with DataFile \"%s\"", s->id, filename);
49         // Open DataFile
50         Data_Open(&(s->data_file), filename);
51
52         s->record_data = true; // Don't forget this!
53
54         // Create the thread
55         pthread_create(&(s->thread), NULL, Sensor_Loop, (void*)(s));
56 }
57
58 /**
59  * Stop a Sensor from recording DataPoints. Blocks until it has stopped.
60  * @param s - The Sensor to stop
61  */
62 void Sensor_Stop(Sensor * s)
63 {
64         // Stop
65         if (s->record_data)
66         {
67                 s->record_data = false;
68                 pthread_join(s->thread, NULL); // Wait for thread to exit
69                 Data_Close(&(s->data_file)); // Close DataFile
70                 s->newest_data.time_stamp = 0;
71                 s->newest_data.value = 0;
72         }
73 }
74
75 /**
76  * Stop all Sensors
77  */
78 void Sensor_StopAll()
79 {
80         for (int i = 0; i < NUMSENSORS; ++i)
81                 Sensor_Stop(g_sensors+i);
82 }
83
84 /**
85  * Start all Sensors
86  */
87 void Sensor_StartAll(const char * experiment_name)
88 {
89         for (int i = 0; i < NUMSENSORS; ++i)
90                 Sensor_Start(g_sensors+i, experiment_name);
91 }
92
93 /**
94  * Read a DataPoint from a Sensor; block until value is read
95  * @param id - The ID of the sensor
96  * @param d - DataPoint to set
97  * @returns True if the DataPoint was different from the most recently recorded.
98  */
99 bool Sensor_Read(Sensor * s, DataPoint * d)
100 {
101         
102         // Set time stamp
103         struct timeval t;
104         gettimeofday(&t, NULL);
105         d->time_stamp = TIMEVAL_DIFF(t, g_options.start_time);
106
107         // Read value based on Sensor Id
108         switch (s->id)
109         {
110                 case ANALOG_TEST0:
111                         d->value = (double)(rand() % 100) / 100;
112                         break;
113
114                 case ANALOG_TEST1:
115                 {
116                         static int count = 0;
117                         d->value = count++;
118                         break;
119                 }
120                 case DIGITAL_TEST0:
121                         d->value = t.tv_sec % 2;
122                         break;
123                 case DIGITAL_TEST1:
124                         d->value = (t.tv_sec+1)%2;
125                         break;
126                 default:
127                         Fatal("Unknown sensor id: %d", s->id);
128                         break;
129         }       
130         usleep(100000); // simulate delay in sensor polling
131
132         // Perform sanity check based on Sensor's ID and the DataPoint
133         Sensor_CheckData(s->id, d);
134
135         // Update latest DataPoint if necessary
136         bool result = (d->value != s->newest_data.value);
137         if (result)
138         {
139                 s->newest_data.time_stamp = d->time_stamp;
140                 s->newest_data.value = d->value;
141         }
142         return result;
143 }
144
145 /**
146  * Checks the sensor data for unsafe or unexpected results 
147  * @param sensor_id - The ID of the sensor
148  * @param d - DataPoint to check
149  */
150 void Sensor_CheckData(SensorId id, DataPoint * d)
151 {
152         //TODO: Implement
153         /*
154         switch (sensor_id)
155         {
156                 case ANALOG_TEST0:
157                 {
158                         if( *sensor value* > ANALOG_TEST0_SAFETY)
159                         {
160                                 LogEx(LOGERR, GetData, Sensor analog_test0 is above the safe value);
161                         //new log function that stops actuators?
162                         }
163                         //Also include a warning level?
164                         else if( *sensor value* > ANALOG_TEST0_WARN)
165                         {
166                                 LogEx(LOGWARN, GetData, Sensor analog_test0);   
167                         }
168                 }
169         }
170         */
171 }
172                 
173
174 /**
175  * Record data from a single Sensor; to be run in a seperate thread
176  * @param arg - Cast to Sensor* - Sensor that the thread will handle
177  * @returns NULL (void* required to use the function with pthreads)
178  */
179 void * Sensor_Loop(void * arg)
180 {
181         Sensor * s = (Sensor*)(arg);
182         Log(LOGDEBUG, "Sensor %d starts", s->id);
183
184         // Until the sensor is stopped, record data points
185         while (s->record_data)
186         {
187                 DataPoint d;
188                 //Log(LOGDEBUG, "Sensor %d reads data [%f,%f]", s->id, d.time_stamp, d.value);
189                 if (Sensor_Read(s, &d)) // If new DataPoint is read:
190                 {
191                         //Log(LOGDEBUG, "Sensor %d saves data [%f,%f]", s->id, d.time_stamp, d.value);
192                         Data_Save(&(s->data_file), &d, 1); // Record it
193                 }
194         }
195         
196         // Needed to keep pthreads happy
197
198         Log(LOGDEBUG, "Sensor %d finished", s->id);
199         return NULL;
200 }
201
202 /**
203  * Get a Sensor given an ID string
204  * @param id_str ID string
205  * @returns Sensor* identified by the string; NULL on error
206  */
207 Sensor * Sensor_Identify(const char * id_str)
208 {
209         char * end;
210         // Parse string as integer
211         int id = strtol(id_str, &end, 10);
212         if (*end != '\0')
213         {
214                 return NULL;
215         }
216         // Bounds check
217         if (id < 0 || id >= NUMSENSORS)
218                 return NULL;
219
220
221         Log(LOGDEBUG, "Sensor \"%s\" identified", g_sensor_names[id]);
222         return g_sensors+id;
223 }
224
225 /**
226  * Helper: Begin sensor response in a given format
227  * @param context - the FCGIContext
228  * @param format - Format
229  * @param id - ID of sensor
230  */
231 void Sensor_BeginResponse(FCGIContext * context, SensorId id, DataFormat format)
232 {
233         // Begin response
234         switch (format)
235         {
236                 case JSON:
237                         FCGI_BeginJSON(context, STATUS_OK);
238                         FCGI_JSONLong("id", id);
239                         break;
240                 default:
241                         FCGI_PrintRaw("Content-type: text/plain\r\n\r\n");
242                         break;
243         }
244 }
245
246 /**
247  * Helper: End sensor response in a given format
248  * @param context - the FCGIContext
249  * @param id - ID of the sensor
250  * @param format - Format
251  */
252 void Sensor_EndResponse(FCGIContext * context, SensorId id, DataFormat format)
253 {
254         // End response
255         switch (format)
256         {
257                 case JSON:
258                         FCGI_EndJSON();
259                         break;
260                 default:
261                         break;
262         }
263 }
264
265 /**
266  * Handle a request to the sensor module
267  * @param context - The context to work in
268  * @param params - Parameters passed
269  */
270 void Sensor_Handler(FCGIContext *context, char * params)
271 {
272         struct timeval now;
273         gettimeofday(&now, NULL);
274         double current_time = TIMEVAL_DIFF(now, g_options.start_time);
275
276         int id = 0;
277         double start_time = 0;
278         double end_time = current_time;
279         char * fmt_str;
280
281         // key/value pairs
282         FCGIValue values[] = {
283                 {"id", &id, FCGI_REQUIRED(FCGI_LONG_T)}, 
284                 {"format", &fmt_str, FCGI_STRING_T}, 
285                 {"start_time", &start_time, FCGI_DOUBLE_T}, 
286                 {"end_time", &end_time, FCGI_DOUBLE_T},
287         };
288
289         // enum to avoid the use of magic numbers
290         typedef enum {
291                 ID,
292                 FORMAT,
293                 START_TIME,
294                 END_TIME,
295         } SensorParams;
296         
297         // Fill values appropriately
298         if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
299         {
300                 // Error occured; FCGI_RejectJSON already called
301                 return;
302         }
303
304
305         // Error checking on sensor id
306         if (id < 0 || id >= NUMSENSORS)
307         {
308                 FCGI_RejectJSON(context, "Invalid sensor id");
309                 return;
310         }
311         Sensor * s = g_sensors+id;
312         
313         DataFormat format = Data_GetFormat(&(values[FORMAT]));
314
315         // Begin response
316         Sensor_BeginResponse(context, id, format);
317
318         // Print Data
319         Data_Handler(&(s->data_file), &(values[START_TIME]), &(values[END_TIME]), format, current_time);
320         
321         // Finish response
322         Sensor_EndResponse(context, id, format);
323         
324 }
325
326

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