3 * @purpose 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)
18 df->write_file = NULL;
22 * Initialise a DataFile from a filename; opens read/write FILE*
23 * @param df - DataFile to initialise
24 * @param filename - Name of file; overwritten if it exists
26 void Data_Open(DataFile * df, const char * filename)
28 assert(filename != NULL);
32 df->filename = strdup(filename);
34 // Set number of DataPoints
38 df->write_file = fopen(filename, "w+");
39 if (df->write_file == NULL)
41 Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
45 df->read_file = df->write_file;
47 //NOTE: Opening the same file in read mode gives funny results; fread generally reads less than expected
48 // The strerror is: "Transport endpoint is not connected"
51 if (df->read_file == NULL)
53 Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
61 * @param df - The DataFile to close
63 void Data_Close(DataFile * df)
67 //TODO: Write data to TSV?
71 df->write_file = NULL;
73 fclose(df->write_file);
81 * Save DataPoints to a DataFile
82 * @param df - The DataFile to save to
83 * @param buffer - Array of DataPoint(s) to save
84 * @param amount - Number of DataPoints in the buffer
86 void Data_Save(DataFile * df, DataPoint * buffer, int amount)
88 pthread_mutex_unlock(&(df->mutex));
90 assert(buffer != NULL);
93 // Go to the end of the file
94 if (fseek(df->write_file, 0, SEEK_END) < 0)
96 Fatal("Error seeking to end of DataFile %s - %s", df->filename, strerror(errno));
99 // Attempt to write the DataPoints
100 int amount_written = fwrite(buffer, sizeof(DataPoint), amount, df->write_file);
102 // Check if the correct number of points were written
103 if (amount_written != amount)
105 Fatal("Wrote %d points instead of %d to DataFile %s - %s", amount_written, amount, df->filename, strerror(errno));
108 // Update number of DataPoints
109 df->num_points += amount_written;
111 pthread_mutex_unlock(&(df->mutex));
116 * Read DataPoints from a DataFile
117 * @param df - The DataFile to read from
118 * @param buffer - Array to fill with DataPoints
119 * @param index - Index to start reading at (inclusive)
120 * @param amount - Maximum number of DataPoints to read
121 * @returns - Actual number of points read (If smaller than amount, the end of the file was reached)
123 int Data_Read(DataFile * df, DataPoint * buffer, int index, int amount)
125 pthread_mutex_lock(&(df->mutex));
128 assert(buffer != NULL);
132 // If we would read past the end of the file, reduce the amount of points to read
134 if (index + amount > df->num_points)
136 Log(LOGDEBUG, "Requested %d points but will only read %d to get to EOF (%d)", amount, df->num_points - index, df->num_points);
137 amount = df->num_points - index;
141 // Go to position in file
142 if (fseek(df->read_file, index*sizeof(DataPoint), SEEK_SET))
144 Fatal("Error seeking to position %d in DataFile %s - %s", index, df->filename, strerror(errno));
147 // Attempt to read the DataPoints
148 int amount_read = fread(buffer, sizeof(DataPoint), amount, df->read_file);
150 // Check if correct number of points were read
151 if (amount_read != amount)
153 Log(LOGERR,"Read %d points instead of %d from DataFile %s - %s", amount_read, amount, df->filename, strerror(errno));
156 pthread_mutex_unlock(&(df->mutex));
161 * Print data points between two indexes using a given format
162 * @param df - DataFile to print
163 * @param start_index - Index to start at (inclusive)
164 * @param end_index - Index to end at (inclusive)
165 * @param format - The format to use
167 void Data_PrintByIndexes(DataFile * df, int start_index, int end_index, DataFormat format)
170 assert(start_index >= 0);
171 assert(end_index >= 0);
172 assert(end_index <= df->num_points-1 || df->num_points == 0);
174 const char * fmt_string; // Format for each data point
175 char seperator; // Character used to seperate successive data points
177 // Determine what format string and seperator character to use
181 fmt_string = "[%f,%f]";
183 // For JSON we need an opening bracket
187 fmt_string = "%f\t%f";
192 DataPoint buffer[DATA_BUFSIZ]; // Buffer
193 // initialise buffer to stop stuff complaining
194 memset(buffer, 0, sizeof(DataPoint)*DATA_BUFSIZ);
196 if (start_index < end_index)
199 int index = start_index;
200 // Repeat until all DataPoints are printed
201 while (index <= end_index)
203 // Fill the buffer from the DataFile
204 int amount_read = Data_Read(df, buffer, index, DATA_BUFSIZ);
206 // Print all points in the buffer
207 for (int i = 0; i < amount_read && index <= end_index; ++i)
209 // Print individual DataPoint
210 FCGI_PrintRaw(fmt_string, buffer[i].time_stamp, buffer[i].value);
212 // Last seperator is not required
213 if (index+1 <= end_index)
214 FCGI_PrintRaw("%c", seperator);
216 // Advance the position in the DataFile
225 // For JSON we need a closing bracket
234 * Print data points between two time stamps using a given format.
235 * Prints nothing if the time stamp
236 * @param df - DataFile to print
237 * @param start_time - Time to start from (inclusive)
238 * @param end_time - Time to end at (inclusive)
239 * @param format - The format to use
241 void Data_PrintByTimes(DataFile * df, double start_time, double end_time, DataFormat format)
244 assert(start_time >= 0);
245 assert(end_time >= 0);
246 assert(end_time >= start_time);
250 // Get starting index
251 int start_index = Data_FindByTime(df, start_time, &closest);
253 // Start time is greater than most recent time stamp
254 if (start_index >= df->num_points-1)
256 if (start_index == 0 || closest.time_stamp < start_time)
258 Data_PrintByIndexes(df, 0, 0, format); // Will print "empty" dataset
263 // Get finishing index
264 int end_index = Data_FindByTime(df, end_time, &closest);
266 // Print data between the indexes
267 Data_PrintByIndexes(df, start_index, end_index, format);
271 * Get the index of the DataPoint closest to a given time stamp
272 * @param df - DataFile to search
273 * @param time_stamp - The time stamp to search for
274 * @param closest - If not NULL, will be filled with the DataPoint chosen
275 * @returns index of DataPoint with the *closest* time stamp to that given
277 int Data_FindByTime(DataFile * df, double time_stamp, DataPoint * closest)
280 assert(time_stamp >= 0);
281 assert(closest != NULL);
283 DataPoint tmp; // Current DataPoint in binary search
285 int lower = 0; // lower index in binary search
286 pthread_mutex_lock(&(df->mutex));
287 int upper = df->num_points - 1; // upper index in binary search
288 pthread_mutex_unlock(&(df->mutex));
289 int index = 0; // current index in binary search
291 // Commence binary search:
292 while (upper - lower > 1)
295 index = lower + ((upper - lower)/2);
298 if (Data_Read(df, &tmp, index, 1) != 1)
300 Fatal("Couldn't read DataFile %s at index %d", df->filename, index);
303 // Change search interval to either half appropriately
304 if (tmp.time_stamp > time_stamp)
308 else if (tmp.time_stamp < time_stamp)
314 // Store closest DataPoint