19 #include "plotter_status.h"
21 struct dataparser_struct {
22 struct eventhandler_entry evhandler;
31 static int separate_entries(char *buf, char *entries[], int max_entries)
38 while (*buf && i< max_entries) {
53 static void init_data(struct charger_data *data)
57 bzero(data, sizeof(*data));
58 for (i = 0; i < MAX_CELLS; i++)
59 data->cell_voltage[i] = NAN;
61 data->timestamp = NAN;
62 data->input_voltage = NAN;
63 data->charging_voltage = NAN;
64 data->charging_current = NAN;
65 data->total_charge = NAN;
71 * Convert one log line into charger_data structure. Supports at least
72 * iCharger 6 and 10 cell chargers. Others are not tested.
74 * Returns negative on incorrect data, zero on success
76 static int parse_logline(const char *buf, struct charger_data *data)
78 int i, j, entry_count;
82 char *str = strdup(buf);
85 entry_count = separate_entries(str, entries, ARRAY_SIZE(entries));
89 if (entries[0][0] != '$') {
90 pr_debug("Discarding malformed data entry\n");
95 entries[i]++; /* discard the dollar sign */
96 data->channel = atoi(entries[i++]);
98 data->state = atoi(entries[i++]);
100 /* Timestamp is optional */
101 if (strlen(entries[1]) > 0)
102 data->timestamp = atof(entries[i++]);
104 data->input_voltage = atof(entries[i++]) / 1000.0;
105 data->charging_voltage = atof(entries[i++]) / 1000.0;
106 data->charging_current = atof(entries[i++]) / 100.0;
108 #define ASSIGN_OR_NAN(data, val) \
116 max_cells = entry_count - 10;
117 pr_debug("max_cells: %d\n", max_cells);
119 for (j = 0; j < max_cells; j++, i++) {
120 d = atoi(entries[i]);
121 ASSIGN_OR_NAN(data->cell_voltage[j], d / 1000.0);
124 d = atoi(entries[i++]);
125 ASSIGN_OR_NAN(data->int_temp, d / 10.0);
127 d = atoi(entries[i++]);
128 ASSIGN_OR_NAN(data->ext_temp, d / 10.0);
130 data->total_charge = atof(entries[i++]);
138 static void dump_data(struct charger_data *data)
142 pr_debug("channel %d\n", data->channel);
143 pr_debug("state %d\n", data->state);
144 pr_debug("timestamp %.1f\n", data->timestamp);
145 pr_debug("input_voltage %.3f\n", data->input_voltage);
146 pr_debug("charging_voltage %.3f\n", data->charging_voltage);
147 pr_debug("charging_current %.3f\n", data->charging_current);
149 for (i = 0; i < MAX_CELLS; i++) {
150 if (isnan(data->cell_voltage[i]))
153 pr_debug("cell_voltage[%d] %f\n", i,
154 data->cell_voltage[i]);
157 pr_debug("total_charge %.0f\n", data->total_charge);
158 pr_debug("int_temp %.1f\n", data->int_temp);
159 pr_debug("ext_temp %.1f\n", data->ext_temp);
162 static void print_status_line(struct charger_data *data)
164 int i, active_cells = 0;
168 if (data->timestamp > 3600) {
169 snprintf(time_str, sizeof(time_str), "%d:%02d:%02d",
170 (int)(data->timestamp / 3600),
171 (int)((int)data->timestamp % 3600) / 60,
172 (int)data->timestamp % 60);
174 snprintf(time_str, sizeof(time_str), "%2d:%02d",
175 ((int)data->timestamp % 3600) / 60,
176 (int)data->timestamp % 60);
179 for (i = 0; i < MAX_CELLS; i++) {
180 if (!isnan(data->cell_voltage[i])) {
181 cell_avg += data->cell_voltage[i];
185 cell_avg /= (double)active_cells;
187 pr_info("\r\033[K%8s Ubat: %.3fV Ucell avg: %.3fV "
188 "Current: %.2fA Charge %.0fmAh ",
189 time_str, data->charging_voltage, cell_avg,
190 data->charging_current, data->total_charge);
197 * Read data from a slow device
199 * return 1 when a complete NULL terminated line has been read
201 * return 0 when a partial line has been read and appended to the
204 * return negative on error
206 static int read_log_line(int infd, char *buf, size_t bufsize, int *offset)
211 ret = read(infd, buf + *offset, bufsize - *offset - 1);
213 pr_err("read: %m\n");
218 pr_err("Read EOF, stopping\n");
220 set_plotter_system_status(SYSTEM_STATUS_NO_USB);
223 buf[*offset + ret] = 0;
225 for (i = 0; i < ret; i++) {
226 if (buf[i + *offset] == '\n' ||
227 buf[i + *offset] == '\r') {
229 * Got a complete line when there is a newline
230 * at the end. Remove the newline and possible
231 * other junk, such as '\r'
233 buf[i + *offset] = 0;
240 * Fixme! Nothing guarantees that there isn't actually
241 * more data (a part of a new log entry perhaps) after
242 * the newline. So in rare cases (we are prevented
243 * from reading the serial line in very long time) we
244 * might lose data from the stream..
252 static int open_new_logfile(struct dataparser_struct *dt)
256 time_t cur_time = time(NULL);
258 tm = localtime(&cur_time);
259 strftime(path, sizeof(path), dt->log_path, tm);
261 pr_debug("Opening %s for writing the log file\n", path);
263 dt->outfd = open(path, O_CREAT | O_APPEND | O_WRONLY, 0664);
265 pr_err("Failed to open file %s for writing: %m\n",
273 static int read_data(struct eventhandler_entry *h)
275 struct dataparser_struct *dt;
278 struct charger_data data;
282 dt = container_of(h, struct dataparser_struct, evhandler);
284 ret = read_log_line(dt->infd, dt->buf, sizeof(dt->buf), &dt->offset);
289 pr_err("%s %s: EOF\n", __FILE__, __func__);
291 if (strlen(dt->buf) < 5) {
292 pr_debug("discarding truncated log entry\n");
298 dt->start_time = time(NULL);
300 cur_time = time(NULL);
302 parse_logline(dt->buf, &data);
304 set_plotter_system_status(data.state);
306 /* Fill in possibly missing timestamp */
307 if (isnan(data.timestamp) || data.timestamp == 0)
308 data.timestamp = cur_time - dt->start_time;
310 print_status_line(&data);
318 if (state_has_changed()) {
319 ret = open_new_logfile(dt);
324 len = snprintf(str, sizeof(str),
327 "%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;"
328 "%.f;%.1f;%.1f\n", /* mAh, and temp */
334 data.charging_voltage,
335 data.charging_current,
337 data.cell_voltage[0],
338 data.cell_voltage[1],
339 data.cell_voltage[2],
340 data.cell_voltage[3],
341 data.cell_voltage[4],
342 data.cell_voltage[5],
343 data.cell_voltage[6],
344 data.cell_voltage[7],
345 data.cell_voltage[8],
346 data.cell_voltage[9],
351 ret = write(dt->outfd, str, len);
353 pr_err("write: %m\n");
360 static struct dataparser_struct dataparser = {
361 .evhandler.name = "OpenLog Parser",
362 .evhandler.events = EPOLLIN,
363 .evhandler.handle_event = read_data,
366 int init_data_parser(int infd, struct plotter_config *cfg)
370 dataparser.evhandler.fd = infd;
371 dataparser.infd = infd;
372 dataparser.log_path = cfg->log_path;
374 ret = register_event_handler(&dataparser.evhandler);