3 * @brief Implementation of data handling functions; saving, loading, displaying, selecting.
7 #include <assert.h> //TODO: Remove asserts
10 * One off initialisation of DataFile
11 * @param df - The DataFile
13 void Data_Init(DataFile * df)
16 memset(df, 0, sizeof(DataFile));
17 pthread_mutex_init(&(df->mutex), NULL);
21 * Initialise a DataFile from a filename; opens read/write FILE*
22 * @param df - DataFile to initialise
23 * @param filename - Name of file; overwritten if it exists
25 void Data_Open(DataFile * df, const char * filename)
27 assert(filename != NULL);
31 df->filename = strdup(filename);
33 // Set number of DataPoints
37 df->file = fopen(filename, "wb+");
40 Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
46 * @param df - The DataFile to close
48 void Data_Close(DataFile * df)
52 //TODO: Write data to TSV?
65 * Save DataPoints to a DataFile
66 * @param df - The DataFile to save to
67 * @param buffer - Array of DataPoint(s) to save
68 * @param amount - Number of DataPoints in the buffer
70 void Data_Save(DataFile * df, DataPoint * buffer, int amount)
72 pthread_mutex_lock(&(df->mutex));
74 assert(buffer != NULL);
77 // Go to the end of the file
78 if (fseek(df->file, 0, SEEK_END) < 0)
80 Fatal("Error seeking to end of DataFile %s - %s", df->filename, strerror(errno));
83 // Attempt to write the DataPoints
84 int amount_written = fwrite(buffer, sizeof(DataPoint), amount, df->file);
86 // Check if the correct number of points were written
87 if (amount_written != amount)
89 Fatal("Wrote %d points instead of %d to DataFile %s - %s", amount_written, amount, df->filename, strerror(errno));
92 // Update number of DataPoints
93 df->num_points += amount_written;
95 pthread_mutex_unlock(&(df->mutex));
99 * Read DataPoints from a DataFile
100 * @param df - The DataFile to read from
101 * @param buffer - Array to fill with DataPoints
102 * @param index - Index to start reading at (inclusive)
103 * @param amount - Maximum number of DataPoints to read
104 * @returns - Actual number of points read (If smaller than amount, the end of the file was reached)
106 int Data_Read(DataFile * df, DataPoint * buffer, int index, int amount)
108 pthread_mutex_lock(&(df->mutex));
111 assert(buffer != NULL);
115 // If we would read past the end of the file, reduce the amount of points to read
117 if (index + amount > df->num_points)
119 Log(LOGDEBUG, "Requested %d points but will only read %d to get to EOF (%d)", amount, df->num_points - index, df->num_points);
120 amount = df->num_points - index;
124 // Go to position in file
125 if (fseek(df->file, index*sizeof(DataPoint), SEEK_SET))
127 Fatal("Error seeking to position %d in DataFile %s - %s", index, df->filename, strerror(errno));
130 // Attempt to read the DataPoints
131 int amount_read = fread(buffer, sizeof(DataPoint), amount, df->file);
133 // Check if correct number of points were read
134 if (amount_read != amount)
136 Log(LOGERR,"Read %d points instead of %d from DataFile %s - %s", amount_read, amount, df->filename, strerror(errno));
139 pthread_mutex_unlock(&(df->mutex));
144 * Print data points between two indexes using a given format
145 * @param df - DataFile to print
146 * @param start_index - Index to start at (inclusive)
147 * @param end_index - Index to end at (exclusive)
148 * @param format - The format to use
150 void Data_PrintByIndexes(DataFile * df, int start_index, int end_index, DataFormat format)
153 assert(start_index >= 0);
154 assert(end_index >= 0);
155 assert(end_index <= df->num_points || df->num_points == 0);
157 const char * fmt_string; // Format for each data point
158 char separator; // Character used to seperate successive data points
160 // Determine what format string and separator character to use
164 fmt_string = "[%.9f,%f]";
166 // For JSON we need an opening bracket
170 fmt_string = "%.9f\t%f";
175 DataPoint buffer[DATA_BUFSIZ] = {{0}}; // Buffer
176 int index = start_index;
178 // Repeat until all DataPoints are printed
179 while (index < end_index)
181 // Fill the buffer from the DataFile
182 int amount_read = Data_Read(df, buffer, index, DATA_BUFSIZ);
184 // Print all points in the buffer
185 for (int i = 0; i < amount_read && index < end_index; ++i)
187 // Print individual DataPoint
188 FCGI_PrintRaw(fmt_string, buffer[i].time_stamp, buffer[i].value);
190 // Last separator is not required
191 if (index+1 < end_index)
192 FCGI_PrintRaw("%c", separator);
194 // Advance the position in the DataFile
202 // For JSON we need a closing bracket
211 * Print data points between two time stamps using a given format.
212 * Prints nothing if the time stamp
213 * @param df - DataFile to print
214 * @param start_time - Time to start from (inclusive)
215 * @param end_time - Time to end at (exclusive)
216 * @param format - The format to use
218 void Data_PrintByTimes(DataFile * df, double start_time, double end_time, DataFormat format)
227 int start_index = 0, end_index = 0;
228 if (start_time < end_time)
230 start_index = Data_FindByTime(df, start_time, NULL);
231 end_index = Data_FindByTime(df, end_time, NULL);
234 Data_PrintByIndexes(df, start_index, end_index, format);
238 * Get the index of the DataPoint closest to a given time stamp
239 * @param df - DataFile to search
240 * @param time_stamp - The time stamp to search for
241 * @param closest - If not NULL, will be filled with the DataPoint chosen
242 * @returns index of DataPoint with the *closest* time stamp to that given
244 int Data_FindByTime(DataFile * df, double time_stamp, DataPoint * closest)
247 assert(time_stamp >= 0);
248 //assert(closest != NULL);
250 DataPoint tmp; // Current DataPoint in binary search
252 int lower = 0; // lower index in binary search
253 pthread_mutex_lock(&(df->mutex));
254 int upper = df->num_points - 1; // upper index in binary search
255 pthread_mutex_unlock(&(df->mutex));
256 int index = 0; // current index in binary search
258 // Commence binary search:
259 while (upper - lower > 1)
262 index = lower + ((upper - lower)/2);
265 if (Data_Read(df, &tmp, index, 1) != 1)
267 Fatal("Couldn't read DataFile %s at index %d", df->filename, index);
270 // Change search interval to either half appropriately
271 if (tmp.time_stamp > time_stamp)
275 else if (tmp.time_stamp < time_stamp)
281 // Store closest DataPoint
290 * Helper; handle FCGI response that requires data
291 * Should be called first.
292 * @param df - DataFile to access
293 * @param start - Info about start_time param
294 * @param end - Info about end_time param
295 * @param fmt - Info about format param
296 * @param current_time - Current time
298 void Data_Handler(DataFile * df, FCGIValue * start, FCGIValue * end, DataFormat format, double current_time)
300 double start_time = *(double*)(start->value);
301 double end_time = *(double*)(end->value);
305 FCGI_JSONKey("data");
308 // If a time was specified
309 if (FCGI_RECEIVED(start->flags) || FCGI_RECEIVED(end->flags))
311 // Wrap times relative to the current time
313 start_time += current_time;
315 end_time += current_time;
317 // Print points by time range
318 Data_PrintByTimes(df, start_time, end_time, format);
321 else // No time was specified; just return a recent set of points
323 pthread_mutex_lock(&(df->mutex));
324 int start_index = df->num_points-DATA_BUFSIZ;
325 int end_index = df->num_points-1;
326 pthread_mutex_unlock(&(df->mutex));
334 // Print points by indexes
335 Data_PrintByIndexes(df, start_index, end_index, format);
341 * Helper - Convert human readable format string to DataFormat
342 * @param fmt - FCGIValue to use
344 DataFormat Data_GetFormat(FCGIValue * fmt)
346 const char * fmt_str = *(const char**)(fmt->value);
347 // Check if format type was specified
348 if (FCGI_RECEIVED(fmt->flags))
350 if (strcmp(fmt_str, "json") == 0)
352 else if (strcmp(fmt_str, "tsv") == 0)
355 Log(LOGERR, "Unknown format type \"%s\"", fmt_str);
361 * Binary search for index of a double in an array
362 * @param value - The value
363 * @param x - The array
364 * @param size - Sizeof the array
366 int FindClosest(double value, double x[], int size)
371 while (upper - lower > 1)
373 index = lower + ((upper - lower)/2);
374 double look = x[index];
377 else if (look < value)
383 if (x[index] > value && index > 0)
390 * Get calibrated value by interpolation in array y
391 * @param value - Raw measured value
392 * @param x - x values (raw values) of the data
393 * @param y - calibrated values
394 * @param size - Number of values in the arrays
395 * @returns interpolated calibrated value
397 double Data_Calibrate(double value, double x[], double y[], int size)
399 int i = FindClosest(value, x, size);
404 double dist = (value - x[i])/(x[i+1] - x[i]);
405 return y[i] + dist*(y[i+1]-y[i]);