feat: add HIL diagnostics and meter health handling

This commit is contained in:
2026-08-17 22:33:04 +02:00
parent bc2ac6e8f6
commit aa25aa2c12
30 changed files with 2108 additions and 236 deletions
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include <Arduino.h>
#include "config.h"
#ifndef ENABLE_HIL_TRACE
#define ENABLE_HIL_TRACE 0
#endif
enum class HilFault : uint8_t {
DropChunk = 0,
DuplicateChunk,
CorruptChunk,
SuppressAck,
DelayAck,
WrongAckBatch,
Count
};
#if ENABLE_HIL_TRACE
void hil_trace_init(DeviceRole role, uint16_t short_id, int reset_reason);
void hil_trace_event(const char *event, const char *fields_json = nullptr);
void hil_trace_eventf(const char *event, const char *fields_fmt, ...);
void hil_trace_poll_commands();
bool hil_fault_take(HilFault fault);
uint32_t hil_fault_delay_ms();
uint32_t hil_crc32(const uint8_t *data, size_t len);
void hil_trace_health(uint8_t queue_depth, uint8_t build_depth, uint8_t retry_count);
#else
inline void hil_trace_init(DeviceRole, uint16_t, int) {}
inline void hil_trace_event(const char *, const char * = nullptr) {}
inline void hil_trace_eventf(const char *, const char *, ...) {}
inline void hil_trace_poll_commands() {}
inline bool hil_fault_take(HilFault) { return false; }
inline uint32_t hil_fault_delay_ms() { return 0; }
inline uint32_t hil_crc32(const uint8_t *, size_t) { return 0; }
inline void hil_trace_health(uint8_t, uint8_t, uint8_t) {}
#endif
+15
View File
@@ -3,7 +3,22 @@
#include <Arduino.h>
#include "data_model.h"
struct MeterDriverStats {
uint32_t frames_ok;
uint32_t frames_parse_fail;
uint32_t rx_overflow;
uint32_t rx_timeout;
uint32_t bytes_rx;
uint32_t last_rx_ms;
uint32_t last_good_frame_ms;
uint32_t uart_buffer_full;
uint32_t uart_fifo_overflow;
uint32_t uart_frame_error;
uint32_t uart_parity_error;
};
void meter_init();
bool meter_read(MeterData &data);
bool meter_poll_frame(const char *&frame, size_t &len);
bool meter_parse_frame(const char *frame, size_t len, MeterData &data);
void meter_get_stats(MeterDriverStats &out);
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#include <stdint.h>
enum class MeterHealthClassification : uint8_t {
Fresh = 0,
NoData,
Stale
};
struct MeterHealthState {
bool fault_active;
};
struct MeterHealthEvaluation {
bool ok;
bool fault_started;
bool recovered;
MeterHealthClassification classification;
};
MeterHealthEvaluation meter_health_evaluate(MeterHealthState &state,
bool has_snapshot,
uint32_t age_ms,
uint32_t max_age_ms);
const char *meter_health_classification_text(MeterHealthClassification classification);
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
constexpr size_t METER_FRAME_MAX = 512;
constexpr uint8_t METER_REQUIRED_FIELDS_MASK = 0x1f;
enum class MeterFrameEvent : uint8_t {
None = 0,
Complete,
Timeout,
Overflow,
Resync
};
struct MeterFrameCollector {
char buffer[METER_FRAME_MAX + 1];
size_t length;
uint32_t last_rx_ms;
bool in_frame;
};
void meter_frame_collector_init(MeterFrameCollector &collector);
MeterFrameEvent meter_frame_collector_check_timeout(MeterFrameCollector &collector,
uint32_t now_ms,
uint32_t timeout_ms);
MeterFrameEvent meter_frame_collector_feed(MeterFrameCollector &collector,
char byte,
uint32_t now_ms);
enum class MeterParseStatus : uint8_t {
Valid = 0,
Empty,
MissingTerminator,
LineTooLong,
MissingRequired,
InvalidMeterSeconds,
MalformedValue
};
struct MeterParseResult {
float energy_total_kwh;
float total_power_w;
float phase_power_w[3];
uint32_t meter_seconds;
uint8_t fields_mask;
bool meter_seconds_valid;
bool valid;
MeterParseStatus status;
};
bool meter_parser_parse(const char *frame, size_t len, MeterParseResult &result);
const char *meter_parse_status_text(MeterParseStatus status);