3 * @purpose Implementation of sensor thread
4 * TODO: Finalise implementation
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
17 DataPoint GetData(int sensor_id)
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
24 //TODO: Deal with time stamps properly
33 d.value = (float)(rand() % 100) / 100;
36 Fatal("Unknown sensor id: %d", sensor_id);
39 usleep(100000); // simulate delay in sensor polling
46 * @param s - Sensor to destroy
48 void Destroy(Sensor * s)
50 // Maybe move the binary file into long term file storage?
58 * @param s - Sensor to initialise
60 void Sensor_Init(Sensor * s, int id)
66 #define FILENAMESIZE BUFSIZ
67 char filename[FILENAMESIZE];
68 //if (s->id >= pow(10, FILENAMESIZE))
71 Fatal("Too many sensors! FILENAMESIZE is %d; increase it and recompile.", FILENAMESIZE);
74 pthread_mutex_init(&(s->mutex), NULL);
76 sprintf(filename, "%d", s->id);
77 unlink(filename); //TODO: Move old files somewhere
79 s->file = fopen(filename, "a+b"); // open binary file
80 Log(LOGDEBUG, "Initialised sensor %d; binary file is \"%s\"", id, filename);
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)
89 void * Sensor_Main(void * arg)
91 Sensor * s = (Sensor*)(arg);
93 while (true) //TODO: Exit condition
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.
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
103 while (s->write_index < SENSOR_DATABUFSIZ)
105 s->buffer[s->write_index] = GetData(s->id);
109 //Log(LOGDEBUG, "Filled buffer");
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)
117 Fatal("Wrote %d data points and expected to write %d to \"%s\" - %s", amount_written, SENSOR_DATABUFSIZ, strerror(errno));
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
123 s->write_index = 0; // reset position in buffer
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
136 int Sensor_Query(Sensor * s, DataPoint * buffer, int bufsiz)
139 //CRITICAL SECTION (Don't access file while sensor thread is writing to it!)
140 pthread_mutex_lock(&(s->mutex));
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));
150 * Handle a request to the sensor module
151 * @param context - The context to work in
152 * @param params - Parameters passed
154 void Sensor_Handler(FCGIContext *context, char * params)
156 DataPoint buffer[SENSOR_QUERYBUFSIZ];
157 StatusCodes status = STATUS_OK;
158 const char * key; const char * value;
160 int sensor_id = SENSOR_NONE;
162 while ((params = FCGI_KeyPair(params, &key, &value)) != NULL)
164 Log(LOGDEBUG, "Got key=%s and value=%s", key, value);
165 if (strcmp(key, "id") == 0)
168 if (sensor_id != SENSOR_NONE)
170 Log(LOGERR, "Only one sensor id should be specified");
171 status = STATUS_ERROR;
176 Log(LOGERR, "No id specified.");
177 status = STATUS_ERROR;
180 //TODO: Use human readable sensor identifier string for API?
181 sensor_id = strtol(value, &end, 10);
184 Log(LOGERR, "Sensor id not an integer; %s", value);
185 status = STATUS_ERROR;
191 Log(LOGERR, "Unknown key \"%s\" (value = %s)", key, value);
192 status = STATUS_ERROR;
197 if (sensor_id == SENSOR_NONE)
199 Log(LOGERR, "No sensor id specified");
200 status = STATUS_ERROR;
202 else if (sensor_id >= NUMSENSORS || sensor_id < 0)
204 Log(LOGERR, "Invalid sensor id %d", sensor_id);
205 status = STATUS_ERROR;
208 if (status == STATUS_ERROR)
210 FCGI_RejectJSON(context);
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");
222 for (int i = 0; i < amount_read; ++i)
224 FCGI_JSONValue("[%f, %f]", buffer[i].time, buffer[i].value);
225 if (i+1 < amount_read)
229 //Log(LOGDEBUG, "Done producing JSON response");