Use configured local timezone in web UI and drop legacy history CSV parsing
This commit is contained in:
@@ -57,26 +57,23 @@ static HistoryJob g_history = {};
|
|||||||
static constexpr size_t SD_LIST_MAX_FILES = 200;
|
static constexpr size_t SD_LIST_MAX_FILES = 200;
|
||||||
static constexpr size_t SD_DOWNLOAD_MAX_PATH = 160;
|
static constexpr size_t SD_DOWNLOAD_MAX_PATH = 160;
|
||||||
|
|
||||||
static String format_utc_hms(uint32_t ts_utc) {
|
static String format_local_hms(uint32_t ts_utc) {
|
||||||
if (ts_utc == 0) {
|
if (ts_utc == 0) {
|
||||||
return "n/a";
|
return "n/a";
|
||||||
}
|
}
|
||||||
time_t t = static_cast<time_t>(ts_utc);
|
time_t t = static_cast<time_t>(ts_utc);
|
||||||
struct tm tm_utc;
|
struct tm tm_local;
|
||||||
gmtime_r(&t, &tm_utc);
|
localtime_r(&t, &tm_local);
|
||||||
char buf[16];
|
char buf[24];
|
||||||
snprintf(buf, sizeof(buf), "%02d:%02d:%02d UTC",
|
strftime(buf, sizeof(buf), "%H:%M:%S %Z", &tm_local);
|
||||||
tm_utc.tm_hour,
|
|
||||||
tm_utc.tm_min,
|
|
||||||
tm_utc.tm_sec);
|
|
||||||
return String(buf);
|
return String(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String format_epoch_hms(uint32_t ts_utc) {
|
static String format_epoch_local_hms(uint32_t ts_utc) {
|
||||||
if (ts_utc == 0) {
|
if (ts_utc == 0) {
|
||||||
return "n/a";
|
return "n/a";
|
||||||
}
|
}
|
||||||
return String(ts_utc) + " (" + format_utc_hms(ts_utc) + ")";
|
return String(ts_utc) + " (" + format_local_hms(ts_utc) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t timestamp_age_seconds(uint32_t ts_utc) {
|
static uint32_t timestamp_age_seconds(uint32_t ts_utc) {
|
||||||
@@ -277,12 +274,12 @@ static bool history_parse_line(const char *line, uint32_t &ts_out, float &p_out)
|
|||||||
if (!line || line[0] < '0' || line[0] > '9') {
|
if (!line || line[0] < '0' || line[0] > '9') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const char *comma = strchr(line, ',');
|
const char *comma1 = strchr(line, ',');
|
||||||
if (!comma) {
|
if (!comma1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
char ts_buf[16];
|
char ts_buf[16];
|
||||||
size_t ts_len = static_cast<size_t>(comma - line);
|
size_t ts_len = static_cast<size_t>(comma1 - line);
|
||||||
if (ts_len >= sizeof(ts_buf)) {
|
if (ts_len >= sizeof(ts_buf)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -293,32 +290,22 @@ static bool history_parse_line(const char *line, uint32_t &ts_out, float &p_out)
|
|||||||
if (end == ts_buf) {
|
if (end == ts_buf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto parse_float_field = [](const char *start, const char *end, float &out) -> bool {
|
const char *comma2 = strchr(comma1 + 1, ',');
|
||||||
if (!start) {
|
if (!comma2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
char p_buf[16];
|
|
||||||
size_t p_len = end ? static_cast<size_t>(end - start) : strlen(start);
|
|
||||||
if (p_len == 0 || p_len >= sizeof(p_buf)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy(p_buf, start, p_len);
|
|
||||||
p_buf[p_len] = '\0';
|
|
||||||
char *endp = nullptr;
|
|
||||||
out = strtof(p_buf, &endp);
|
|
||||||
return endp != p_buf;
|
|
||||||
};
|
|
||||||
|
|
||||||
const char *field2_start = comma + 1;
|
|
||||||
const char *field2_end = strchr(field2_start, ',');
|
|
||||||
float p = 0.0f;
|
|
||||||
bool parsed_power = parse_float_field(field2_start, field2_end, p);
|
|
||||||
if (!parsed_power && field2_end) {
|
|
||||||
const char *field3_start = field2_end + 1;
|
|
||||||
const char *field3_end = strchr(field3_start, ',');
|
|
||||||
parsed_power = parse_float_field(field3_start, field3_end, p);
|
|
||||||
}
|
}
|
||||||
if (!parsed_power) {
|
const char *p_start = comma2 + 1;
|
||||||
|
const char *p_end = strchr(p_start, ',');
|
||||||
|
char p_buf[16];
|
||||||
|
size_t p_len = p_end ? static_cast<size_t>(p_end - p_start) : strlen(p_start);
|
||||||
|
if (p_len == 0 || p_len >= sizeof(p_buf)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
memcpy(p_buf, p_start, p_len);
|
||||||
|
p_buf[p_len] = '\0';
|
||||||
|
char *endp = nullptr;
|
||||||
|
float p = strtof(p_buf, &endp);
|
||||||
|
if (endp == p_buf) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ts_out = ts;
|
ts_out = ts;
|
||||||
@@ -418,7 +405,7 @@ static String render_sender_block(const SenderStatus &status) {
|
|||||||
if (!status.has_data) {
|
if (!status.has_data) {
|
||||||
s += "No data";
|
s += "No data";
|
||||||
} else {
|
} else {
|
||||||
s += "Last update: " + format_epoch_hms(status.last_update_ts_utc);
|
s += "Last update: " + format_epoch_local_hms(status.last_update_ts_utc);
|
||||||
if (time_is_synced()) {
|
if (time_is_synced()) {
|
||||||
s += " (" + String(timestamp_age_seconds(status.last_update_ts_utc)) + "s ago)";
|
s += " (" + String(timestamp_age_seconds(status.last_update_ts_utc)) + "s ago)";
|
||||||
}
|
}
|
||||||
@@ -437,7 +424,7 @@ static String render_sender_block(const SenderStatus &status) {
|
|||||||
duplicate_pct = (static_cast<float>(duplicate_batches) * 100.0f) / static_cast<float>(total_batches);
|
duplicate_pct = (static_cast<float>(duplicate_batches) * 100.0f) / static_cast<float>(total_batches);
|
||||||
}
|
}
|
||||||
s += "<br>Dup batches: " + String(duplicate_batches) + "/" + String(total_batches) + " (" + String(duplicate_pct, 1) + "%)";
|
s += "<br>Dup batches: " + String(duplicate_batches) + "/" + String(total_batches) + " (" + String(duplicate_pct, 1) + "%)";
|
||||||
s += " last: " + format_epoch_hms(status.rx_last_duplicate_ts_utc);
|
s += " last: " + format_epoch_local_hms(status.rx_last_duplicate_ts_utc);
|
||||||
if (time_is_synced() && status.rx_last_duplicate_ts_utc > 0) {
|
if (time_is_synced() && status.rx_last_duplicate_ts_utc > 0) {
|
||||||
s += " (" + String(timestamp_age_seconds(status.rx_last_duplicate_ts_utc)) + "s ago)";
|
s += " (" + String(timestamp_age_seconds(status.rx_last_duplicate_ts_utc)) + "s ago)";
|
||||||
}
|
}
|
||||||
@@ -665,14 +652,14 @@ static void handle_sender() {
|
|||||||
if (g_last_batch_count[i] > 0) {
|
if (g_last_batch_count[i] > 0) {
|
||||||
html += "<h3>Last batch (" + String(g_last_batch_count[i]) + " samples)</h3>";
|
html += "<h3>Last batch (" + String(g_last_batch_count[i]) + " samples)</h3>";
|
||||||
html += "<table border='1' cellspacing='0' cellpadding='3'>";
|
html += "<table border='1' cellspacing='0' cellpadding='3'>";
|
||||||
html += "<tr><th>#</th><th>ts_utc</th><th>ts_hms_utc</th><th>e_kwh</th><th>p_w</th><th>p1_w</th><th>p2_w</th><th>p3_w</th>";
|
html += "<tr><th>#</th><th>ts_utc</th><th>ts_hms_local</th><th>e_kwh</th><th>p_w</th><th>p1_w</th><th>p2_w</th><th>p3_w</th>";
|
||||||
html += "<th>bat_v</th><th>bat_pct</th><th>rssi</th><th>snr</th><th>err_tx</th><th>err_last</th><th>rx_reject</th></tr>";
|
html += "<th>bat_v</th><th>bat_pct</th><th>rssi</th><th>snr</th><th>err_tx</th><th>err_last</th><th>rx_reject</th></tr>";
|
||||||
for (uint8_t r = 0; r < g_last_batch_count[i]; ++r) {
|
for (uint8_t r = 0; r < g_last_batch_count[i]; ++r) {
|
||||||
const MeterData &d = g_last_batch[i][r];
|
const MeterData &d = g_last_batch[i][r];
|
||||||
html += "<tr>";
|
html += "<tr>";
|
||||||
html += "<td>" + String(r) + "</td>";
|
html += "<td>" + String(r) + "</td>";
|
||||||
html += "<td>" + String(d.ts_utc) + "</td>";
|
html += "<td>" + String(d.ts_utc) + "</td>";
|
||||||
html += "<td>" + format_utc_hms(d.ts_utc) + "</td>";
|
html += "<td>" + format_local_hms(d.ts_utc) + "</td>";
|
||||||
html += "<td>" + String(d.energy_total_kwh, 2) + "</td>";
|
html += "<td>" + String(d.energy_total_kwh, 2) + "</td>";
|
||||||
html += "<td>" + String(round_power_w(d.total_power_w)) + "</td>";
|
html += "<td>" + String(round_power_w(d.total_power_w)) + "</td>";
|
||||||
html += "<td>" + String(round_power_w(d.phase_power_w[0])) + "</td>";
|
html += "<td>" + String(round_power_w(d.phase_power_w[0])) + "</td>";
|
||||||
|
|||||||
Reference in New Issue
Block a user