Merge pull request #16 from jtanx/master
[matches/MCTX3420.git] / server / sensor.c
1 /**
2  * @file sensor.c
3  * @purpose Implementation of sensor thread
4  * TODO: Finalise implementation
5  */
6
7
8 #include "common.h"
9 #include "sensor.h"
10 #include <math.h>
11
12 /**
13  * Read a data value from a sensor; block until value is read
14  * @param sensor_id - The ID of the sensor
15  * @returns The current value of the sensor
16  */
17 DataPoint GetData(int sensor_id)
18 {
19         // switch based on the sensor_id at the moment for testing;
20         // might be able to just directly access ADC from sensor_id?
21         //TODO: Implement for real sensors
22
23         DataPoint d;
24         //TODO: Deal with time stamps properly
25         static int count = 0;
26         d.time = count++;
27         switch (sensor_id)
28         {
29                 case SENSOR_TEST0:
30                         d.value = count;
31                         break;
32                 case SENSOR_TEST1:
33                         d.value = (float)(rand() % 100) / 100;
34                         break;
35                 default:
36                         Fatal("Unknown sensor id: %d", sensor_id);
37                         break;
38         }       
39         usleep(100000); // simulate delay in sensor polling
40         return d;
41 }
42
43
44 /**
45  * Destroy a sensor
46  * @param s - Sensor to destroy
47  */
48 void Destroy(Sensor * s)
49 {
50         // Maybe move the binary file into long term file storage?
51         fclose(s->file);
52 }
53
54
55
56 /**
57  * Initialise a sensor
58  * @param s - Sensor to initialise
59  */
60 void Sensor_Init(Sensor * s, int id)
61 {
62         s->write_index = 0;
63         s->read_offset = 0;
64         s->id = id;
65
66         #define FILENAMESIZE BUFSIZ
67         char filename[FILENAMESIZE];
68         //if (s->id >= pow(10, FILENAMESIZE))
69         if (false)
70         {
71                 Fatal("Too many sensors! FILENAMESIZE is %d; increase it and recompile.", FILENAMESIZE);
72         }
73
74         pthread_mutex_init(&(s->mutex), NULL);
75                 
76         sprintf(filename, "%d", s->id);
77         unlink(filename); //TODO: Move old files somewhere
78
79         s->file = fopen(filename, "a+b"); // open binary file
80         Log(LOGDEBUG, "Initialised sensor %d; binary file is \"%s\"", id, filename);
81 }
82
83
84 /**
85  * Run the main sensor polling loop
86  * @param arg - Cast to Sensor* - Sensor that the thread will handle
87  * @returns NULL (void* required to use the function with pthreads)
88  */
89 void * Sensor_Main(void * arg)
90 {
91         Sensor * s = (Sensor*)(arg);
92
93         while (true) //TODO: Exit condition
94         {
95                 // The sensor will write data to a buffer until it is full
96                 // Then it will open a file and dump the buffer to the end of it.
97                 // Rinse and repeat
98
99                 // The reason I've added the buffer is because locks are expensive
100                 // But maybe it's better to just write data straight to the file
101                 // I'd like to do some tests by changing SENSOR_DATABUFSIZ
102
103                 while (s->write_index < SENSOR_DATABUFSIZ)
104                 {
105                         s->buffer[s->write_index] = GetData(s->id);
106                         s->write_index += 1;
107                 }
108
109                 //Log(LOGDEBUG, "Filled buffer");
110
111                 // CRITICAL SECTION (no threads should be able to read/write the file at the same time)
112                 pthread_mutex_lock(&(s->mutex));
113                         fseek(s->file, 0, SEEK_END);
114                         int amount_written = fwrite(s->buffer, sizeof(DataPoint), SENSOR_DATABUFSIZ, s->file);
115                         if (amount_written != SENSOR_DATABUFSIZ)
116                         {
117                                 Fatal("Wrote %d data points and expected to write %d to \"%s\" - %s", amount_written, SENSOR_DATABUFSIZ, strerror(errno));
118                         }
119                         //Log(LOGDEBUG, "Wrote %d data points for sensor %d", amount_written, s->id);
120                 pthread_mutex_unlock(&(s->mutex));
121                 // End of critical section
122
123                 s->write_index = 0; // reset position in buffer
124                 
125         }
126         return NULL;
127 }
128
129 /**
130  * Fill buffer with most recent sensor data
131  * @param s - Sensor to use
132  * @param buffer - Buffer to fill
133  * @param bufsiz - Size of buffer to fill
134  * @returns The number of DataPoints actually read
135  */
136 int Sensor_Query(Sensor * s, DataPoint * buffer, int bufsiz)
137 {
138         int amount_read = 0;
139         //CRITICAL SECTION (Don't access file while sensor thread is writing to it!)
140         pthread_mutex_lock(&(s->mutex));
141                 
142                 fseek(s->file, -bufsiz*sizeof(DataPoint), SEEK_END);
143                 amount_read = fread(buffer, sizeof(DataPoint), bufsiz, s->file);
144                 //Log(LOGDEBUG, "Read %d data points", amount_read);            
145         pthread_mutex_unlock(&(s->mutex));
146         return amount_read;
147 }
148
149 /**
150  * Handle a request to the sensor module
151  * @param context - The context to work in
152  * @param params - Parameters passed
153  */
154 void Sensor_Handler(FCGIContext *context, char * params)
155 {
156         DataPoint buffer[SENSOR_QUERYBUFSIZ];
157         StatusCodes status = STATUS_OK;
158         const char * key; const char * value;
159
160         int sensor_id = SENSOR_NONE;
161
162         while ((params = FCGI_KeyPair(params, &key, &value)) != NULL)
163         {
164                 Log(LOGDEBUG, "Got key=%s and value=%s", key, value);
165                 if (strcmp(key, "id") == 0)
166                 {
167                         char *end;
168                         if (sensor_id != SENSOR_NONE)
169                         {
170                                 Log(LOGERR, "Only one sensor id should be specified");
171                                 status = STATUS_ERROR;
172                                 break;
173                         }
174                         if (*value == '\0')
175                         {
176                                 Log(LOGERR, "No id specified.");
177                                 status = STATUS_ERROR;
178                                 break;
179                         }
180                         //TODO: Use human readable sensor identifier string for API?
181                         sensor_id = strtol(value, &end, 10);
182                         if (*end != '\0')
183                         {
184                                 Log(LOGERR, "Sensor id not an integer; %s", value);
185                                 status = STATUS_ERROR;
186                                 break;
187                         }
188                 }
189                 else
190                 {
191                         Log(LOGERR, "Unknown key \"%s\" (value = %s)", key, value);
192                         status = STATUS_ERROR;
193                         break;
194                 }               
195         }
196
197         if (sensor_id == SENSOR_NONE)
198         {
199                 Log(LOGERR, "No sensor id specified");
200                 status = STATUS_ERROR;
201         }
202         else if (sensor_id >= NUMSENSORS || sensor_id < 0)
203         {
204                 Log(LOGERR, "Invalid sensor id %d", sensor_id);
205                 status = STATUS_ERROR;
206         }
207
208         if (status == STATUS_ERROR)
209         {
210                 FCGI_RejectJSON(context);
211         }
212         else
213         {
214                 FCGI_BeginJSON(context, status);        
215                 FCGI_JSONPair(key, value); // should spit back sensor ID
216                 //Log(LOGDEBUG, "Call Sensor_Query...");
217                 int amount_read = Sensor_Query(&(g_sensors[sensor_id]), buffer, SENSOR_QUERYBUFSIZ);
218                 //Log(LOGDEBUG, "Read %d DataPoints", amount_read);
219                 //Log(LOGDEBUG, "Produce JSON response");
220                 FCGI_JSONKey("data");
221                 FCGI_JSONValue("[");
222                 for (int i = 0; i < amount_read; ++i)
223                 {
224                         FCGI_JSONValue("[%f, %f]", buffer[i].time, buffer[i].value);
225                         if (i+1 < amount_read)
226                                 FCGI_JSONValue(",");
227                 }
228                 FCGI_JSONValue("]");
229                 //Log(LOGDEBUG, "Done producing JSON response");
230                 FCGI_EndJSON(); 
231         }
232 }

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