Refactor meter parser to single-pass OBIS dispatch
This commit is contained in:
@@ -33,12 +33,47 @@ void meter_init() {
|
||||
Serial2.begin(9600, SERIAL_7E1, PIN_METER_RX, -1);
|
||||
}
|
||||
|
||||
static bool parse_obis_ascii_value(const char *line, const char *obis, float &out_value) {
|
||||
const char *p = strstr(line, obis);
|
||||
if (!p) {
|
||||
return false;
|
||||
enum class ObisField : uint8_t {
|
||||
None = 0,
|
||||
Energy = 1,
|
||||
TotalPower = 2,
|
||||
Phase1 = 3,
|
||||
Phase2 = 4,
|
||||
Phase3 = 5,
|
||||
MeterSeconds = 6
|
||||
};
|
||||
|
||||
static ObisField detect_obis_field(const char *line) {
|
||||
if (!line) {
|
||||
return ObisField::None;
|
||||
}
|
||||
const char *lparen = strchr(p, '(');
|
||||
const char *p = line;
|
||||
while (*p == ' ' || *p == '\t') {
|
||||
++p;
|
||||
}
|
||||
if (strncmp(p, "1-0:1.8.0", 9) == 0) {
|
||||
return ObisField::Energy;
|
||||
}
|
||||
if (strncmp(p, "1-0:16.7.0", 10) == 0) {
|
||||
return ObisField::TotalPower;
|
||||
}
|
||||
if (strncmp(p, "1-0:36.7.0", 10) == 0) {
|
||||
return ObisField::Phase1;
|
||||
}
|
||||
if (strncmp(p, "1-0:56.7.0", 10) == 0) {
|
||||
return ObisField::Phase2;
|
||||
}
|
||||
if (strncmp(p, "1-0:76.7.0", 10) == 0) {
|
||||
return ObisField::Phase3;
|
||||
}
|
||||
if (strncmp(p, "0-0:96.8.0*255", 14) == 0) {
|
||||
return ObisField::MeterSeconds;
|
||||
}
|
||||
return ObisField::None;
|
||||
}
|
||||
|
||||
static bool parse_obis_ascii_payload_value(const char *line, float &out_value) {
|
||||
const char *lparen = strchr(line, '(');
|
||||
if (!lparen) {
|
||||
return false;
|
||||
}
|
||||
@@ -68,12 +103,12 @@ static bool parse_obis_ascii_value(const char *line, const char *obis, float &ou
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_obis_ascii_unit_scale(const char *line, const char *obis, float &value) {
|
||||
const char *p = strstr(line, obis);
|
||||
if (!p) {
|
||||
static bool parse_obis_ascii_unit_scale(const char *line, float &value) {
|
||||
const char *lparen = strchr(line, '(');
|
||||
if (!lparen) {
|
||||
return false;
|
||||
}
|
||||
const char *asterisk = strchr(p, '*');
|
||||
const char *asterisk = strchr(lparen, '*');
|
||||
if (!asterisk) {
|
||||
return false;
|
||||
}
|
||||
@@ -113,12 +148,8 @@ static int8_t hex_nibble(char c) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool parse_obis_hex_u32(const char *line, const char *obis, uint32_t &out_value) {
|
||||
const char *p = strstr(line, obis);
|
||||
if (!p) {
|
||||
return false;
|
||||
}
|
||||
const char *lparen = strchr(p, '(');
|
||||
static bool parse_obis_hex_payload_u32(const char *line, uint32_t &out_value) {
|
||||
const char *lparen = strchr(line, '(');
|
||||
if (!lparen) {
|
||||
return false;
|
||||
}
|
||||
@@ -261,37 +292,54 @@ bool meter_parse_frame(const char *frame, size_t len, MeterData &data) {
|
||||
return data.valid;
|
||||
}
|
||||
|
||||
ObisField field = detect_obis_field(line);
|
||||
float value = NAN;
|
||||
if (parse_obis_ascii_value(line, "1-0:1.8.0", value)) {
|
||||
parse_obis_ascii_unit_scale(line, "1-0:1.8.0", value);
|
||||
data.energy_total_kwh = value;
|
||||
energy_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
if (parse_obis_ascii_value(line, "1-0:16.7.0", value)) {
|
||||
data.total_power_w = value;
|
||||
total_p_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
if (parse_obis_ascii_value(line, "1-0:36.7.0", value)) {
|
||||
data.phase_power_w[0] = value;
|
||||
p1_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
if (parse_obis_ascii_value(line, "1-0:56.7.0", value)) {
|
||||
data.phase_power_w[1] = value;
|
||||
p2_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
if (parse_obis_ascii_value(line, "1-0:76.7.0", value)) {
|
||||
data.phase_power_w[2] = value;
|
||||
p3_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
uint32_t meter_seconds = 0;
|
||||
if (parse_obis_hex_u32(line, "0-0:96.8.0*255", meter_seconds)) {
|
||||
data.meter_seconds = meter_seconds;
|
||||
data.meter_seconds_valid = true;
|
||||
switch (field) {
|
||||
case ObisField::Energy:
|
||||
if (parse_obis_ascii_payload_value(line, value)) {
|
||||
parse_obis_ascii_unit_scale(line, value);
|
||||
data.energy_total_kwh = value;
|
||||
energy_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
break;
|
||||
case ObisField::TotalPower:
|
||||
if (parse_obis_ascii_payload_value(line, value)) {
|
||||
data.total_power_w = value;
|
||||
total_p_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
break;
|
||||
case ObisField::Phase1:
|
||||
if (parse_obis_ascii_payload_value(line, value)) {
|
||||
data.phase_power_w[0] = value;
|
||||
p1_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
break;
|
||||
case ObisField::Phase2:
|
||||
if (parse_obis_ascii_payload_value(line, value)) {
|
||||
data.phase_power_w[1] = value;
|
||||
p2_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
break;
|
||||
case ObisField::Phase3:
|
||||
if (parse_obis_ascii_payload_value(line, value)) {
|
||||
data.phase_power_w[2] = value;
|
||||
p3_ok = true;
|
||||
got_any = true;
|
||||
}
|
||||
break;
|
||||
case ObisField::MeterSeconds:
|
||||
if (parse_obis_hex_payload_u32(line, meter_seconds)) {
|
||||
data.meter_seconds = meter_seconds;
|
||||
data.meter_seconds_valid = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
line_len = 0;
|
||||
|
||||
Reference in New Issue
Block a user