Merge branch 'master' of github:szmoore/MCTX3420
[matches/MCTX3420.git] / server / data.c
1 /**
2  * @file data.c
3  * @purpose Implementation of data handling functions; saving, loading, displaying, selecting.
4  */
5
6 #include "data.h"
7 #include <assert.h> //TODO: Remove asserts
8
9 /**
10  * One off initialisation of DataFile
11  * @param df - The DataFile
12  */
13 void Data_Init(DataFile * df)
14 {
15         // Everything is NULL
16         df->filename = NULL;
17         df->read_file = NULL;
18         df->write_file = NULL;
19 }
20
21 /**
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
25  */
26 void Data_Open(DataFile * df, const char * filename)
27 {
28         assert(filename != NULL);
29         assert(df != NULL);
30
31         // Set the filename
32         df->filename = strdup(filename);
33
34         // Set number of DataPoints
35         df->num_points = 0; 
36
37         // Set write FILE*
38         df->write_file = fopen(filename, "w+");
39         if (df->write_file == NULL)
40         {
41                 Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
42         }
43         
44         // Set read FILE*
45         df->read_file = df->write_file;
46
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"
49         /*
50         fopen(filename, "r");
51         if (df->read_file == NULL)
52         {
53                 Fatal("Error opening DataFile %s - %s", filename, strerror(errno));
54         }
55         */
56
57 }
58
59 /**
60  * Close a DataFile
61  * @param df - The DataFile to close
62  */
63 void Data_Close(DataFile * df)
64 {
65         assert(df != NULL);
66
67         //TODO: Write data to TSV?
68
69         // Clear the FILE*s
70         df->read_file = NULL;
71         df->write_file = NULL;
72
73         fclose(df->write_file);
74
75         // Clear the filename
76         free(df->filename);
77         df->filename = NULL;
78 }
79
80 /**
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
85  */
86 void Data_Save(DataFile * df, DataPoint * buffer, int amount)
87 {
88         pthread_mutex_unlock(&(df->mutex));
89         assert(df != NULL);
90         assert(buffer != NULL);
91         assert(amount >= 0);
92
93         // Go to the end of the file
94         if (fseek(df->write_file, 0, SEEK_END) < 0)
95         {
96                 Fatal("Error seeking to end of DataFile %s - %s", df->filename, strerror(errno));
97         }
98
99         // Attempt to write the DataPoints
100         int amount_written = fwrite(buffer, sizeof(DataPoint), amount, df->write_file);
101         
102         // Check if the correct number of points were written
103         if (amount_written != amount)
104         {
105                 Fatal("Wrote %d points instead of %d to DataFile %s - %s", amount_written, amount, df->filename, strerror(errno));
106         }
107
108         // Update number of DataPoints
109         df->num_points += amount_written;
110
111         pthread_mutex_unlock(&(df->mutex));
112         
113 }
114
115 /**
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)
122  */
123 int Data_Read(DataFile * df, DataPoint * buffer, int index, int amount)
124 {
125         pthread_mutex_lock(&(df->mutex));
126
127         assert(df != NULL);
128         assert(buffer != NULL);
129         assert(index >= 0);
130         assert(amount > 0);
131         
132         // If we would read past the end of the file, reduce the amount of points to read
133         
134                 if (index + amount > df->num_points)
135                 {
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;
138                 }
139         
140
141         // Go to position in file
142         if (fseek(df->read_file, index*sizeof(DataPoint), SEEK_SET))
143         {
144                 Fatal("Error seeking to position %d in DataFile %s - %s", index, df->filename, strerror(errno));
145         }
146
147         // Attempt to read the DataPoints
148         int amount_read = fread(buffer, sizeof(DataPoint), amount, df->read_file);
149
150         // Check if correct number of points were read
151         if (amount_read != amount)
152         {
153                 Log(LOGERR,"Read %d points instead of %d from DataFile %s - %s", amount_read, amount, df->filename, strerror(errno));
154         }
155
156         pthread_mutex_unlock(&(df->mutex));
157         return amount_read;
158 }
159
160 /**
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
166  */
167 void Data_PrintByIndexes(DataFile * df, int start_index, int end_index, DataFormat format)
168 {
169         assert(df != NULL);
170         assert(start_index >= 0);
171         assert(end_index >= 0);
172         assert(end_index <= df->num_points-1 || df->num_points == 0);
173
174         const char * fmt_string; // Format for each data point
175         char seperator; // Character used to seperate successive data points
176         
177         // Determine what format string and seperator character to use
178         switch (format)
179         {
180                 case JSON:
181                         fmt_string = "[%f,%f]";
182                         seperator = ',';
183                         // For JSON we need an opening bracket
184                         FCGI_PrintRaw("["); 
185                         break;
186                 case TSV:
187                         fmt_string = "%f\t%f";
188                         seperator = '\n';
189                         break;
190         }
191
192         DataPoint buffer[DATA_BUFSIZ]; // Buffer
193         // initialise buffer to stop stuff complaining
194         memset(buffer, 0, sizeof(DataPoint)*DATA_BUFSIZ);
195         
196         if (start_index < end_index)
197         {
198
199                 int index = start_index;
200                 // Repeat until all DataPoints are printed
201                 while (index <= end_index)
202                 {
203                         // Fill the buffer from the DataFile
204                         int amount_read = Data_Read(df, buffer, index, DATA_BUFSIZ);
205         
206                         // Print all points in the buffer
207                         for (int i = 0; i < amount_read && index <= end_index; ++i)
208                         {
209                                 // Print individual DataPoint
210                                 FCGI_PrintRaw(fmt_string, buffer[i].time_stamp, buffer[i].value);
211                                 
212                                 // Last seperator is not required
213                                 if (index+1 <= end_index)
214                                         FCGI_PrintRaw("%c", seperator);
215         
216                                 // Advance the position in the DataFile
217                                 ++index;
218                         }
219                 }
220         }
221         
222         switch (format)
223         {
224                 case JSON:
225                         // For JSON we need a closing bracket
226                         FCGI_PrintRaw("]"); 
227                         break;
228                 default:
229                         break;
230         }
231 }
232
233 /**
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
240  */
241 void Data_PrintByTimes(DataFile * df, double start_time, double end_time, DataFormat format)
242 {
243         assert(df != NULL);
244         assert(start_time >= 0);
245         assert(end_time >= 0);
246         assert(end_time >= start_time);
247         
248         DataPoint closest;
249
250         // Get starting index
251         int start_index = Data_FindByTime(df, start_time, &closest);
252
253         // Start time is greater than most recent time stamp
254         if (start_index >= df->num_points-1)
255         {
256                 if (start_index == 0 || closest.time_stamp < start_time)
257                 {
258                         Data_PrintByIndexes(df, 0, 0, format); // Will print "empty" dataset
259                         return;
260                 }
261         }
262
263         // Get finishing index
264         int end_index = Data_FindByTime(df, end_time, &closest);
265
266         // Print data between the indexes
267         Data_PrintByIndexes(df, start_index, end_index, format);
268 }
269
270 /**
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
276  */
277 int Data_FindByTime(DataFile * df, double time_stamp, DataPoint * closest)
278 {
279         assert(df != NULL);
280         assert(time_stamp >= 0);
281         assert(closest != NULL);
282
283         DataPoint tmp; // Current DataPoint in binary search
284
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
290
291         // Commence binary search:
292         while (upper - lower > 1)
293         {
294                 // Pick midpoint
295                 index = lower + ((upper - lower)/2);
296
297                 // Look at DataPoint
298                 if (Data_Read(df, &tmp, index, 1) != 1)
299                 {
300                         Fatal("Couldn't read DataFile %s at index %d", df->filename, index);
301                 }
302
303                 // Change search interval to either half appropriately
304                 if (tmp.time_stamp > time_stamp)
305                 {
306                         upper = index;
307                 }
308                 else if (tmp.time_stamp < time_stamp)
309                 {
310                         lower = index;
311                 }
312         }
313
314         // Store closest DataPoint
315         if (closest != NULL)
316                 *closest = tmp;
317         
318         return index;
319         
320 }

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