]> git.itanic.dy.fi Git - log-plotter/blob - data.c
data: Move the total charge field at the end of the data
[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 "debug.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 /**
154  * Read data from a slow device
155  *
156  * return 1 when a complete NULL terminated line has been read
157  *
158  * return 0 when a partial line has been read and appended to the
159  *               buffer at @offset
160  *
161  * return negative on error
162  */
163 static int read_log_line(int infd, char *buf, size_t bufsize, int *offset)
164 {
165         int ret;
166         int i;
167
168         ret = read(infd, buf + *offset, bufsize - *offset - 1);
169         if (ret < 0) {
170                 pr_err("read: %m\n");
171                 return -1;
172         }
173
174         if (ret == 0) {
175                 pr_err("Read EOF, stopping\n");
176                 return -1;
177         }
178         buf[*offset + ret] = 0;
179
180         for (i = 0; i < ret; i++) {
181                 if (buf[i + *offset] == '\n' ||
182                     buf[i + *offset] == '\r') {
183                         /*
184                          * Got a complete line when there is a newline
185                          * at the end. Remove the newline and possible
186                          * other junk, such as '\r'
187                          */
188                         buf[i + *offset] = 0;
189                         *offset = 0;
190
191                         return 1;
192                 }
193
194                 /*
195                  * Fixme! Nothing guarantees that there isn't actually
196                  * more data (a part of a new log entry perhaps) after
197                  * the newline. So in rare cases (we are prevented
198                  * from reading the serial line in very long time) we
199                  * might lose data from the stream..
200                  */
201         }
202
203         *offset += ret;
204         return 0;
205 }
206
207 int read_data(int infd, int outfd)
208 {
209         struct epoll_event ev;
210         time_t start_time = 0, cur_time;
211         int epoll_fd;
212         int ret;
213         char buf[256];
214         int offset = 0;
215
216         epoll_fd = epoll_create(1);
217         if (epoll_fd < 0) {
218                 pr_err("Failed to create epoll socket: %m\n");
219                 return -1;
220         }
221
222         ev.events = EPOLLIN;
223         ev.data.fd = infd;
224         if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, infd, &ev) == -1) {
225                 pr_err("epoll_ctl: %m\n");
226                 return 1;
227         }
228
229         while (1) {
230                 struct charger_data data;
231                 char str[320];
232                 int len;
233
234                 ret = epoll_wait(epoll_fd, &ev, 1, -1);
235                 if (ret == 0)
236                         continue;
237
238                 if (ret < 0) {
239                         pr_err("epoll: %m\n");
240                         return -1;
241                 }
242
243                 ret = read_log_line(infd, buf, sizeof(buf), &offset);
244                 if (ret < 0)
245                         return ret;
246
247                 if (ret == 0)
248                         continue;
249
250                 if (strlen(buf) < 5) {
251                         pr_debug("discarding truncated log entry\n");
252                         offset = 0;
253                         continue;
254                 }
255
256                 if (!start_time)
257                         start_time = time(NULL);
258
259                 cur_time = time(NULL);
260
261                 pr_info("%s\n", buf);
262
263                 parse_logline(buf, &data);
264
265                 /* Fill in possibly missing timestamp */
266                 if (isnan(data.timestamp) || data.timestamp == 0);
267                         data.timestamp = cur_time - start_time;
268
269                 dump_data(&data);
270
271                 if (!outfd)
272                         continue;
273
274                 len = snprintf(str, sizeof(str),
275                         "%d;%d;%.1f;"
276                         "%.3f;%.3f;%.3f;"
277                         "%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;"
278                         "%.f;%.1f;%.1f\n", /* mAh, and temp */
279                         data.channel,
280                         data.state,
281                         data.timestamp,
282
283                         data.input_voltage,
284                         data.charging_voltage,
285                         data.charging_current,
286
287                         data.cell_voltage[0],
288                         data.cell_voltage[1],
289                         data.cell_voltage[2],
290                         data.cell_voltage[3],
291                         data.cell_voltage[4],
292                         data.cell_voltage[5],
293                         data.cell_voltage[6],
294                         data.cell_voltage[7],
295                         data.cell_voltage[8],
296                         data.cell_voltage[9],
297                         data.int_temp,
298                         data.ext_temp,
299                         data.total_charge);
300
301                 ret = write(outfd, str, len);
302                 if (ret < 0) {
303                         pr_err("write: %m\n");
304                         break;
305                 }
306         }
307
308         return 0;
309 }
310