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