]> git.itanic.dy.fi Git - log-plotter/blob - data.c
Convert data reading into event based system
[log-plotter] / data.c
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <time.h>
5 #include <stdlib.h>
6 #include <sys/epoll.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <string.h>
10 #include <math.h>
11
12 #include "data.h"
13 #include "trace.h"
14 #include "event.h"
15 #include "utils.h"
16 #include "event.h"
17
18 struct dataparser_struct {
19         struct eventhandler_entry evhandler;
20         int infd;
21         int outfd;
22         int offset;
23         time_t start_time;
24         char buf[256];
25 };
26
27 static int separate_entries(char *buf, char *entries[], int max_entries)
28 {
29         int i = 0;
30
31         entries[i] = buf;
32         i++;
33
34         while (*buf && i< max_entries) {
35                 if (*buf != ';') {
36                         buf++;
37                         continue;
38                 }
39
40                 *buf = 0;
41                 buf++;
42                 entries[i] = buf;
43                 i++;
44         }
45
46         return i;
47 }
48
49 static void init_data(struct charger_data *data)
50 {
51         int i;
52
53         bzero(data, sizeof(*data));
54         for (i = 0; i < MAX_CELLS; i++)
55                 data->cell_voltage[i] = NAN;
56
57         data->timestamp = NAN;
58         data->input_voltage = NAN;
59         data->charging_voltage = NAN;
60         data->charging_current = NAN;
61         data->total_charge = NAN;
62         data->int_temp = NAN;
63         data->ext_temp = NAN;
64 }
65
66 /*
67  * Convert one log line into charger_data structure. Supports at least
68  * iCharger 6 and 10 cell chargers. Others are not tested.
69  *
70  * Returns negative on incorrect data, zero on success
71  */
72 static int parse_logline(const char *buf, struct charger_data *data)
73 {
74         int i, j, entry_count;
75         int ret = -1;
76         int max_cells;
77         int d;
78         char *str = strdup(buf);
79         char *entries[64];
80
81         entry_count = separate_entries(str, entries, ARRAY_SIZE(entries));
82
83         pr_debug("Entry count: %d\n", entry_count);
84
85         init_data(data);
86
87         if (entries[0][0] != '$') {
88                 pr_debug("Discarding malformed data entry\n");
89                 goto out;
90         }
91
92         for (i = 0; i < entry_count; i++)
93                 pr_debug("Entry %d: data: %s\n", i, entries[i]);
94
95         i = 0;
96         entries[i]++; /* discard the dollar sign */
97         data->channel = atoi(entries[i++]);
98
99         data->state = atoi(entries[i++]);
100
101         /* Timestamp is optional */
102         if (strlen(entries[1]) > 0)
103                 data->timestamp = atof(entries[i++]);
104
105         data->input_voltage = atof(entries[i++]) / 1000.0;
106         data->charging_voltage = atof(entries[i++]) / 1000.0;
107         data->charging_current = atof(entries[i++]) / 100.0;
108
109 #define ASSIGN_OR_NAN(data, val)                \
110         do {                                    \
111                 if ((val) == 0)                 \
112                         (data) = NAN;           \
113                 else                            \
114                         (data) = val;           \
115         } while(0);
116
117         max_cells = entry_count - 10;
118         pr_debug("max_cells: %d\n", max_cells);
119
120         for (j = 0; j < max_cells; j++, i++) {
121                 d = atoi(entries[i]);
122                 ASSIGN_OR_NAN(data->cell_voltage[j], d / 1000.0);
123         }
124
125         d = atoi(entries[i++]);
126         ASSIGN_OR_NAN(data->int_temp, d / 10.0);
127
128         d = atoi(entries[i++]);
129         ASSIGN_OR_NAN(data->ext_temp, d / 10.0);
130
131         data->total_charge = atof(entries[i++]);
132
133         ret = 0;
134 out:
135         free(str);
136         return ret;
137 }
138
139 static void dump_data(struct charger_data *data)
140 {
141         int i;
142
143         pr_debug("channel %d\n", data->channel);
144         pr_debug("state %d\n", data->state);
145         pr_debug("timestamp %.1f\n", data->timestamp);
146         pr_debug("input_voltage %.3f\n", data->input_voltage);
147         pr_debug("charging_voltage %.3f\n", data->charging_voltage);
148         pr_debug("charging_current %.3f\n", data->charging_current);
149
150         for (i = 0; i < MAX_CELLS; i++) {
151                 if (isnan(data->cell_voltage[i]))
152                         continue;
153
154                 pr_debug("cell_voltage[%d] %f\n", i,
155                         data->cell_voltage[i]);
156         }
157
158         pr_debug("total_charge %.0f\n", data->total_charge);
159         pr_debug("int_temp %.1f\n", data->int_temp);
160         pr_debug("ext_temp %.1f\n", data->ext_temp);
161 }
162
163 static void print_status_line(struct charger_data *data)
164 {
165         int i, active_cells = 0;
166         double cell_avg = 0;
167         char time_str[16];
168
169         if (data->timestamp > 3600) {
170                 snprintf(time_str, sizeof(time_str), "%d:%02d:%02d",
171                         (int)(data->timestamp / 3600),
172                         (int)((int)data->timestamp % 3600) / 60,
173                         (int)data->timestamp % 60);
174         } else {
175                 snprintf(time_str, sizeof(time_str), "%2d:%02d",
176                         ((int)data->timestamp % 3600) / 60,
177                         (int)data->timestamp % 60);
178         }
179
180         for (i = 0; i < MAX_CELLS; i++) {
181                 if (!isnan(data->cell_voltage[i])) {
182                         cell_avg += data->cell_voltage[i];
183                         active_cells++;
184                 }
185         }
186         cell_avg /= (double)active_cells;
187
188         pr_info("\r\033[K%8s Ubat: %.3fV Ucell avg: %.3fV "
189                 "Current: %.2fA Charge %.0fmAh ",
190                 time_str, data->charging_voltage, cell_avg,
191                 data->charging_current, data->total_charge);
192
193         fflush(stdout);
194
195 }
196
197 /**
198  * Read data from a slow device
199  *
200  * return 1 when a complete NULL terminated line has been read
201  *
202  * return 0 when a partial line has been read and appended to the
203  *               buffer at @offset
204  *
205  * return negative on error
206  */
207 static int read_log_line(int infd, char *buf, size_t bufsize, int *offset)
208 {
209         int ret;
210         int i;
211
212         ret = read(infd, buf + *offset, bufsize - *offset - 1);
213         if (ret < 0) {
214                 pr_err("read: %m\n");
215                 return -1;
216         }
217
218         if (ret == 0) {
219                 pr_err("Read EOF, stopping\n");
220                 return -1;
221         }
222         buf[*offset + ret] = 0;
223
224         for (i = 0; i < ret; i++) {
225                 if (buf[i + *offset] == '\n' ||
226                     buf[i + *offset] == '\r') {
227                         /*
228                          * Got a complete line when there is a newline
229                          * at the end. Remove the newline and possible
230                          * other junk, such as '\r'
231                          */
232                         buf[i + *offset] = 0;
233                         *offset = 0;
234
235                         return 1;
236                 }
237
238                 /*
239                  * Fixme! Nothing guarantees that there isn't actually
240                  * more data (a part of a new log entry perhaps) after
241                  * the newline. So in rare cases (we are prevented
242                  * from reading the serial line in very long time) we
243                  * might lose data from the stream..
244                  */
245         }
246
247         *offset += ret;
248         return 0;
249 }
250
251 static int read_data(struct eventhandler_entry *h)
252 {
253         struct dataparser_struct *dt;
254         time_t cur_time;
255         int ret;
256         struct charger_data data;
257         char str[320];
258         int len;
259
260         dt = container_of(h, struct dataparser_struct, evhandler);
261
262         ret = read_log_line(dt->infd, dt->buf, sizeof(dt->buf), &dt->offset);
263         pr_debug("Line now %d: %s\n", dt->offset, dt->buf);
264         if (ret <= 0)
265                 return ret;
266
267         if (ret == 0)
268                 pr_err("%s %s: EOF\n", __FILE__, __func__);
269
270         if (strlen(dt->buf) < 5) {
271                 pr_debug("discarding truncated log entry\n");
272                 dt->offset = 0;
273                 return 0;
274         }
275
276         if (!dt->start_time)
277                 dt->start_time = time(NULL);
278
279         cur_time = time(NULL);
280
281         parse_logline(dt->buf, &data);
282
283         /* Fill in possibly missing timestamp */
284         if (isnan(data.timestamp) || data.timestamp == 0)
285                 data.timestamp = cur_time - dt->start_time;
286
287         print_status_line(&data);
288
289         dump_data(&data);
290
291         if (!dt->outfd)
292                 return 0;
293
294         len = snprintf(str, sizeof(str),
295                 "%d;%d;%.1f;"
296                 "%.3f;%.3f;%.3f;"
297                 "%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;"
298                 "%.f;%.1f;%.1f\n", /* mAh, and temp */
299                 data.channel,
300                 data.state,
301                 data.timestamp,
302
303                 data.input_voltage,
304                 data.charging_voltage,
305                 data.charging_current,
306
307                 data.cell_voltage[0],
308                 data.cell_voltage[1],
309                 data.cell_voltage[2],
310                 data.cell_voltage[3],
311                 data.cell_voltage[4],
312                 data.cell_voltage[5],
313                 data.cell_voltage[6],
314                 data.cell_voltage[7],
315                 data.cell_voltage[8],
316                 data.cell_voltage[9],
317                 data.int_temp,
318                 data.ext_temp,
319                 data.total_charge);
320
321         ret = write(dt->outfd, str, len);
322         if (ret < 0) {
323                 pr_err("write: %m\n");
324                 return -1;;
325         }
326
327         return 0;
328 }
329
330 static struct dataparser_struct dataparser = {
331         .evhandler.name = "OpenLog Parser",
332         .evhandler.events = EPOLLIN,
333         .evhandler.handle_event = read_data,
334 };
335
336 int init_data_parser(int infd, int outfd)
337 {
338         int ret;
339
340         dataparser.evhandler.fd = infd;
341         dataparser.infd = infd;
342         dataparser.outfd = outfd;
343
344         ret = register_event_handler(&dataparser.evhandler);
345         if (ret < 0)
346                 return 1;
347
348         return 0;
349 }