Use fixed-point meter parsing and early-exit on complete frame
This commit is contained in:
@@ -24,6 +24,7 @@ static uint32_t g_frames_parse_fail = 0;
|
||||
static uint32_t g_rx_overflow = 0;
|
||||
static uint32_t g_rx_timeout = 0;
|
||||
static uint32_t g_last_log_ms = 0;
|
||||
static constexpr uint32_t METER_FIXED_FRAC_MAX_DIV = 10000;
|
||||
|
||||
void meter_init() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
@@ -72,35 +73,91 @@ static ObisField detect_obis_field(const char *line) {
|
||||
return ObisField::None;
|
||||
}
|
||||
|
||||
static bool parse_decimal_fixed(const char *start, const char *end, float &out_value) {
|
||||
if (!start || !end || end <= start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *cur = start;
|
||||
bool started = false;
|
||||
bool negative = false;
|
||||
bool in_fraction = false;
|
||||
bool saw_digit = false;
|
||||
uint64_t int_part = 0;
|
||||
uint32_t frac_part = 0;
|
||||
uint32_t frac_div = 1;
|
||||
|
||||
while (cur < end) {
|
||||
char c = *cur++;
|
||||
if (!started) {
|
||||
if (c == '+' || c == '-') {
|
||||
started = true;
|
||||
negative = (c == '-');
|
||||
continue;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
started = true;
|
||||
saw_digit = true;
|
||||
int_part = static_cast<uint64_t>(c - '0');
|
||||
continue;
|
||||
}
|
||||
if (c == '.' || c == ',') {
|
||||
started = true;
|
||||
in_fraction = true;
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c >= '0' && c <= '9') {
|
||||
saw_digit = true;
|
||||
uint32_t digit = static_cast<uint32_t>(c - '0');
|
||||
if (!in_fraction) {
|
||||
if (int_part <= (UINT64_MAX - digit) / 10ULL) {
|
||||
int_part = int_part * 10ULL + digit;
|
||||
}
|
||||
} else if (frac_div < METER_FIXED_FRAC_MAX_DIV) {
|
||||
frac_part = frac_part * 10U + digit;
|
||||
frac_div *= 10U;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((c == '.' || c == ',') && !in_fraction) {
|
||||
in_fraction = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!saw_digit) {
|
||||
return false;
|
||||
}
|
||||
double value = static_cast<double>(int_part);
|
||||
if (frac_div > 1U) {
|
||||
value += static_cast<double>(frac_part) / static_cast<double>(frac_div);
|
||||
}
|
||||
if (negative) {
|
||||
value = -value;
|
||||
}
|
||||
out_value = static_cast<float>(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_obis_ascii_payload_value(const char *line, float &out_value) {
|
||||
const char *lparen = strchr(line, '(');
|
||||
if (!lparen) {
|
||||
return false;
|
||||
}
|
||||
const char *cur = lparen + 1;
|
||||
char num_buf[24];
|
||||
size_t n = 0;
|
||||
while (*cur && *cur != ')' && *cur != '*') {
|
||||
char c = *cur++;
|
||||
if ((c >= '0' && c <= '9') || c == '-' || c == '+' || c == '.' || c == ',') {
|
||||
if (c == ',') {
|
||||
c = '.';
|
||||
const char *end = lparen + 1;
|
||||
while (*end && *end != ')' && *end != '*') {
|
||||
++end;
|
||||
}
|
||||
if (n + 1 < sizeof(num_buf)) {
|
||||
num_buf[n++] = c;
|
||||
}
|
||||
} else if (n == 0) {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (n == 0) {
|
||||
if (end <= lparen + 1) {
|
||||
return false;
|
||||
}
|
||||
num_buf[n] = '\0';
|
||||
out_value = static_cast<float>(atof(num_buf));
|
||||
return true;
|
||||
return parse_decimal_fixed(lparen + 1, end, out_value);
|
||||
}
|
||||
|
||||
static bool parse_obis_ascii_unit_scale(const char *line, float &value) {
|
||||
@@ -342,6 +399,12 @@ bool meter_parse_frame(const char *frame, size_t len, MeterData &data) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (energy_ok && total_p_ok && p1_ok && p2_ok && p3_ok && data.meter_seconds_valid) {
|
||||
data.valid = true;
|
||||
g_frames_ok++;
|
||||
return true;
|
||||
}
|
||||
|
||||
line_len = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user