X-Git-Url: http://git.itanic.dy.fi/?p=log-plotter;a=blobdiff_plain;f=data.c;h=56a6b72faeca6cd445135421bbc5910851c32dd3;hp=ef11dfa0550f6dbe926a5cf3dd960e62604517b2;hb=HEAD;hpb=44f9c67945693b34dc03b602d33670b87863ddef diff --git a/data.c b/data.c index ef11dfa..56a6b72 100644 --- a/data.c +++ b/data.c @@ -2,11 +2,205 @@ #include #include #include +#include +#include +#include #include #include #include +#include +#include + +#include "data.h" +#include "trace.h" +#include "event.h" +#include "utils.h" +#include "event.h" +#include "plotter_status.h" + +#define MAX_CHANNELS 4 + +struct dataparser_struct { + struct eventhandler_entry evhandler; + struct plotter_config *cfg; + int infd; + int outfd[MAX_CHANNELS]; + int offset; + time_t start_time; + char buf[256]; +}; + +static int separate_entries(char *buf, char *entries[], int max_entries) +{ + int i = 0; + + entries[i] = buf; + i++; + + while (*buf && i< max_entries) { + if (*buf != ';') { + buf++; + continue; + } + + *buf = 0; + buf++; + entries[i] = buf; + i++; + } + + return i; +} + +static void init_data(struct charger_data *data) +{ + int i; + + bzero(data, sizeof(*data)); + for (i = 0; i < MAX_CELLS; i++) + data->cell_voltage[i] = NAN; + + data->timestamp = NAN; + data->input_voltage = NAN; + data->charging_voltage = NAN; + data->charging_current = NAN; + data->total_charge = NAN; + data->int_temp = NAN; + data->ext_temp = NAN; +} + +/* + * Convert one log line into charger_data structure. Supports at least + * iCharger 6 and 10 cell chargers. Others are not tested. + * + * Returns negative on incorrect data, zero on success + */ +static int parse_logline(const char *buf, struct charger_data *data) +{ + int i, j, entry_count; + int ret = -1; + int max_cells; + int d; + char *str = strdup(buf); + char *entries[64]; + + entry_count = separate_entries(str, entries, ARRAY_SIZE(entries)); + + init_data(data); + + if (entries[0][0] != '$') { + pr_debug("Discarding malformed data entry\n"); + goto out; + } + + i = 0; + entries[i]++; /* discard the dollar sign */ + data->channel = atoi(entries[i++]); + if (data->channel > MAX_CHANNELS) + data->channel = 0; + + data->state = atoi(entries[i++]); + + /* Timestamp is optional */ + if (strlen(entries[1]) > 0) + data->timestamp = atof(entries[i++]); + + data->input_voltage = atof(entries[i++]) / 1000.0; + data->charging_voltage = atof(entries[i++]) / 1000.0; + data->charging_current = atof(entries[i++]) / 100.0; + +#define ASSIGN_OR_NAN(data, val) \ + do { \ + if ((val) == 0) \ + (data) = NAN; \ + else \ + (data) = val; \ + } while(0); + + max_cells = entry_count - 10; + pr_debug("max_cells: %d\n", max_cells); + + for (j = 0; j < max_cells; j++, i++) { + d = atoi(entries[i]); + ASSIGN_OR_NAN(data->cell_voltage[j], d / 1000.0); + + if (d) + data->cell_count++; + } + + d = atoi(entries[i++]); + ASSIGN_OR_NAN(data->int_temp, d / 10.0); + + d = atoi(entries[i++]); + ASSIGN_OR_NAN(data->ext_temp, d / 10.0); + + data->total_charge = atof(entries[i++]); + + ret = 0; +out: + free(str); + return ret; +} + +static void dump_data(struct charger_data *data) +{ + int i; + + pr_debug("channel %d\n", data->channel); + pr_debug("state %d\n", data->state); + pr_debug("timestamp %.1f\n", data->timestamp); + pr_debug("input_voltage %.3f\n", data->input_voltage); + pr_debug("charging_voltage %.3f\n", data->charging_voltage); + pr_debug("charging_current %.3f\n", data->charging_current); + + for (i = 0; i < MAX_CELLS; i++) { + if (isnan(data->cell_voltage[i])) + continue; + + pr_debug("cell_voltage[%d] %f\n", i, + data->cell_voltage[i]); + } + + pr_debug("total_charge %.0f\n", data->total_charge); + pr_debug("int_temp %.1f\n", data->int_temp); + pr_debug("ext_temp %.1f\n", data->ext_temp); +} + +static void print_status_line(struct charger_data *data) +{ + int i, active_cells = 0; + double cell_avg = 0; + char time_str[16]; + + if (data->timestamp > 3600) { + snprintf(time_str, sizeof(time_str), "%d:%02d:%02d", + (int)(data->timestamp / 3600), + (int)((int)data->timestamp % 3600) / 60, + (int)data->timestamp % 60); + } else { + snprintf(time_str, sizeof(time_str), "%2d:%02d", + ((int)data->timestamp % 3600) / 60, + (int)data->timestamp % 60); + } + + for (i = 0; i < MAX_CELLS; i++) { + if (!isnan(data->cell_voltage[i])) { + cell_avg += data->cell_voltage[i]; + active_cells++; + } + } + cell_avg /= (double)active_cells; + + pr_info("\r\033[K%s [%s] Ubat: %.3fV Ucell avg: %.3fV " + "Current: %.2fA Charge %.0fmAh ", + time_str, + state_to_str(data->state), + data->charging_voltage, cell_avg, + data->charging_current, data->total_charge); -#include "debug.h" + fflush(stdout); + +} /** * Read data from a slow device @@ -31,6 +225,8 @@ static int read_log_line(int infd, char *buf, size_t bufsize, int *offset) if (ret == 0) { pr_err("Read EOF, stopping\n"); + + set_plotter_system_status(SYSTEM_STATUS_NO_USB); return -1; } buf[*offset + ret] = 0; @@ -62,74 +258,145 @@ static int read_log_line(int infd, char *buf, size_t bufsize, int *offset) return 0; } -int read_data(int infd, int outfd) +static int open_new_logfile(struct dataparser_struct *dt, int channel) { - struct epoll_event ev; - time_t start_time = 0, cur_time; - int epoll_fd; - int ret; - char buf[256]; - int offset = 0; + char path[2048]; + struct tm *tm; + time_t cur_time = time(NULL); - epoll_fd = epoll_create(1); - if (epoll_fd < 0) { - pr_err("Failed to create epoll socket: %m\n"); + tm = localtime(&cur_time); + strftime(path, sizeof(path), dt->cfg->log_path, tm); + + store_int_variable_value_to_array("channel", channel, dt->cfg); + replace_variables_with_values(path, sizeof(path), dt->cfg); + + pr_debug("Opening %s for writing the log file\n", path); + + dt->outfd[channel] = open(path, O_CREAT | O_APPEND | O_WRONLY, 0664); + if (dt->outfd[channel] < 0) { + pr_err("Failed to open file %s for writing: %m\n", + path); return -1; } - ev.events = EPOLLIN; - ev.data.fd = infd; - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, infd, &ev) == -1) { - pr_err("epoll_ctl: %m\n"); - return 1; + return 0; +} + +static int read_data(struct eventhandler_entry *h) +{ + struct dataparser_struct *dt; + time_t cur_time; + int ret; + struct charger_data data; + char str[320]; + int len; + + dt = container_of(h, struct dataparser_struct, evhandler); + + ret = read_log_line(dt->infd, dt->buf, sizeof(dt->buf), &dt->offset); + if (ret <= 0) + return ret; + + if (ret == 0) + pr_err("%s %s: EOF\n", __FILE__, __func__); + + if (strlen(dt->buf) < 5) { + pr_debug("discarding truncated log entry\n"); + dt->offset = 0; + return 0; } - while (1) { - char str[320]; - int len; + if (!dt->start_time) + dt->start_time = time(NULL); - ret = epoll_wait(epoll_fd, &ev, 1, -1); - if (ret == 0) - continue; + cur_time = time(NULL); - if (ret < 0) { - pr_err("epoll: %m\n"); - return -1; - } + parse_logline(dt->buf, &data); - ret = read_log_line(infd, buf, sizeof(buf), &offset); - if (ret < 0) - return ret; + set_plotter_system_status(data.state); - if (ret == 0) - continue; + /* Fill in possibly missing timestamp */ + if (isnan(data.timestamp) || data.timestamp == 0) + data.timestamp = cur_time - dt->start_time; - if (strlen(buf) < 5) { - pr_debug("discarding truncated log entry\n"); - offset = 0; - continue; - } + print_status_line(&data); - if (!start_time) - start_time = time(NULL); + if (0) + dump_data(&data); - cur_time = time(NULL); + if (!dt->cfg->log_path) + return 0; - pr_info("%s\n", buf); + if (state_has_changed()) { + dt->start_time = time(NULL); + data.timestamp = 0; + store_str_variable_value_to_array("status", + state_to_str(plotter_state.system_status), dt->cfg); - if (!outfd) - continue; + store_int_variable_value_to_array("cell_count", + data.cell_count, dt->cfg); + + ret = open_new_logfile(dt, data.channel); + if (ret < 0) + return ret; + } - len = snprintf(str, sizeof(str), - "%ld;%s\n", cur_time - start_time, buf); + len = snprintf(str, sizeof(str), + "%d;%d;%.1f;" + "%.3f;%.3f;%.3f;" + "%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;%.3f;" + "%.f;%.1f;%.1f\n", /* mAh, and temp */ + data.channel, + data.state, + data.timestamp, - ret = write(outfd, str, len); - if (ret < 0) { - pr_err("write: %m\n"); - break; - } + data.input_voltage, + data.charging_voltage, + data.charging_current, + + data.cell_voltage[0], + data.cell_voltage[1], + data.cell_voltage[2], + data.cell_voltage[3], + data.cell_voltage[4], + data.cell_voltage[5], + data.cell_voltage[6], + data.cell_voltage[7], + data.cell_voltage[8], + data.cell_voltage[9], + data.int_temp, + data.ext_temp, + data.total_charge); + + if (dt->outfd[data.channel] <= 0) + open_new_logfile(dt, data.channel); + + ret = write(dt->outfd[data.channel], str, len); + if (ret < 0) { + pr_err("write: %m\n"); + return -1;; } return 0; } +static struct dataparser_struct dataparser = { + .evhandler.name = "OpenLog Parser", + .evhandler.events = EPOLLIN, + .evhandler.handle_event = read_data, +}; + +int init_data_parser(int infd, struct plotter_config *cfg) +{ + int ret; + + dataparser.evhandler.fd = infd; + dataparser.infd = infd; + dataparser.cfg = cfg; + + ret = register_event_handler(&dataparser.evhandler); + if (ret < 0) + return 1; + + return 0; +}