feat: add HIL diagnostics and meter health handling
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
#include "meter_parser.h"
|
||||
#include "meter_health.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
static std::string frame(const char *energy_line = "1-0:1.8.0*255(001234.567*kWh)",
|
||||
const char *seconds = "00000064",
|
||||
bool terminator = true) {
|
||||
std::string value = "/DD3HIL\r\n0-0:96.8.0*255(";
|
||||
value += seconds;
|
||||
value += ")\r\n";
|
||||
if (energy_line) {
|
||||
value += energy_line;
|
||||
value += "\r\n";
|
||||
}
|
||||
value += "1-0:16.7.0*255(000950*W)\r\n";
|
||||
value += "1-0:36.7.0*255(000500*W)\r\n";
|
||||
value += "1-0:56.7.0*255(000450*W)\r\n";
|
||||
value += "1-0:76.7.0*255(000000*W)\r\n";
|
||||
if (terminator) value += "!\r\n";
|
||||
return value;
|
||||
}
|
||||
|
||||
static MeterParseResult parse(const std::string &telegram) {
|
||||
MeterParseResult result = {};
|
||||
meter_parser_parse(telegram.data(), telegram.size(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int main() {
|
||||
MeterHealthState health_state = {};
|
||||
MeterHealthEvaluation health = meter_health_evaluate(health_state, false, UINT32_MAX, 15000);
|
||||
assert(!health.ok && health.fault_started && !health.recovered);
|
||||
health = meter_health_evaluate(health_state, false, UINT32_MAX, 15000);
|
||||
assert(!health.ok && !health.fault_started && !health.recovered);
|
||||
health = meter_health_evaluate(health_state, true, 100, 15000);
|
||||
assert(health.ok && !health.fault_started && health.recovered);
|
||||
health = meter_health_evaluate(health_state, true, 15001, 15000);
|
||||
assert(!health.ok && health.fault_started && !health.recovered);
|
||||
|
||||
MeterParseResult valid = parse(frame());
|
||||
assert(valid.valid);
|
||||
assert(valid.status == MeterParseStatus::Valid);
|
||||
assert(valid.fields_mask == METER_REQUIRED_FIELDS_MASK);
|
||||
assert(valid.meter_seconds_valid && valid.meter_seconds == 100);
|
||||
assert(fabsf(valid.energy_total_kwh - 1234.567f) < 0.001f);
|
||||
|
||||
MeterParseResult wh = parse(frame("1-0:1.8.0*255(1234567*Wh)"));
|
||||
assert(wh.valid);
|
||||
assert(fabsf(wh.energy_total_kwh - 1234.567f) < 0.001f);
|
||||
|
||||
MeterParseResult missing = parse(frame(nullptr));
|
||||
assert(!missing.valid && missing.status == MeterParseStatus::MissingRequired);
|
||||
|
||||
MeterParseResult bad_seconds = parse(frame(nullptr, "GGGGGGGG"));
|
||||
assert(!bad_seconds.valid && bad_seconds.status == MeterParseStatus::InvalidMeterSeconds);
|
||||
|
||||
MeterParseResult bad_unit = parse(frame("1-0:1.8.0*255(1234*J)"));
|
||||
assert(!bad_unit.valid && bad_unit.status == MeterParseStatus::MalformedValue);
|
||||
|
||||
MeterParseResult no_end = parse(frame(nullptr, "00000064", false));
|
||||
assert(!no_end.valid && no_end.status == MeterParseStatus::MissingTerminator);
|
||||
|
||||
MeterParseResult false_prefix = parse(frame("X1-0:1.8.0*255(1234*kWh)"));
|
||||
assert(!false_prefix.valid && false_prefix.status == MeterParseStatus::MissingRequired);
|
||||
|
||||
MeterFrameCollector collector = {};
|
||||
meter_frame_collector_init(collector);
|
||||
uint32_t now = 0;
|
||||
for (char c : std::string("/truncated\r\n")) {
|
||||
meter_frame_collector_feed(collector, c, now += 20);
|
||||
}
|
||||
std::string recovery = frame();
|
||||
assert(meter_frame_collector_feed(collector, recovery[0], now += 20) == MeterFrameEvent::Resync);
|
||||
MeterFrameEvent event = MeterFrameEvent::None;
|
||||
for (size_t i = 1; i < recovery.size(); ++i) {
|
||||
event = meter_frame_collector_feed(collector, recovery[i], now += 20);
|
||||
if (event == MeterFrameEvent::Complete) break;
|
||||
}
|
||||
assert(event == MeterFrameEvent::Complete);
|
||||
MeterParseResult recovered = {};
|
||||
assert(meter_parser_parse(collector.buffer, collector.length, recovered));
|
||||
|
||||
meter_frame_collector_init(collector);
|
||||
meter_frame_collector_feed(collector, '/', 10);
|
||||
assert(meter_frame_collector_check_timeout(collector, 1510, 1500) == MeterFrameEvent::None);
|
||||
assert(meter_frame_collector_check_timeout(collector, 1511, 1500) == MeterFrameEvent::Timeout);
|
||||
|
||||
meter_frame_collector_init(collector);
|
||||
meter_frame_collector_feed(collector, '/', 0);
|
||||
event = MeterFrameEvent::None;
|
||||
for (size_t i = 0; i < METER_FRAME_MAX; ++i) {
|
||||
event = meter_frame_collector_feed(collector, 'A', static_cast<uint32_t>(i + 1));
|
||||
if (event == MeterFrameEvent::Overflow) break;
|
||||
}
|
||||
assert(event == MeterFrameEvent::Overflow);
|
||||
|
||||
puts("meter parser and collector tests passed");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user