test: add json stability and discovery payload coverage
This commit is contained in:
29
lib/dd3_legacy_core/src/data_model.cpp
Normal file
29
lib/dd3_legacy_core/src/data_model.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "data_model.h"
|
||||
#include <esp_mac.h>
|
||||
|
||||
void init_device_ids(uint16_t &short_id, char *device_id, size_t device_id_len) {
|
||||
uint8_t mac[6] = {0};
|
||||
// Read base MAC without needing WiFi to be started.
|
||||
esp_read_mac(mac, ESP_MAC_WIFI_STA);
|
||||
short_id = (static_cast<uint16_t>(mac[4]) << 8) | mac[5];
|
||||
snprintf(device_id, device_id_len, "dd3-%04X", short_id);
|
||||
}
|
||||
|
||||
const char *rx_reject_reason_text(RxRejectReason reason) {
|
||||
switch (reason) {
|
||||
case RxRejectReason::CrcFail:
|
||||
return "crc_fail";
|
||||
case RxRejectReason::InvalidMsgKind:
|
||||
return "invalid_msg_kind";
|
||||
case RxRejectReason::LengthMismatch:
|
||||
return "length_mismatch";
|
||||
case RxRejectReason::DeviceIdMismatch:
|
||||
return "device_id_mismatch";
|
||||
case RxRejectReason::BatchIdMismatch:
|
||||
return "batch_id_mismatch";
|
||||
case RxRejectReason::UnknownSender:
|
||||
return "unknown_sender";
|
||||
default:
|
||||
return "none";
|
||||
}
|
||||
}
|
||||
96
lib/dd3_legacy_core/src/json_codec.cpp
Normal file
96
lib/dd3_legacy_core/src/json_codec.cpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#include "json_codec.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
|
||||
static constexpr size_t STATE_JSON_DOC_CAPACITY = 512;
|
||||
|
||||
static float round2(float value) {
|
||||
if (isnan(value)) {
|
||||
return value;
|
||||
}
|
||||
return roundf(value * 100.0f) / 100.0f;
|
||||
}
|
||||
|
||||
static int32_t round_to_i32(float value) {
|
||||
if (isnan(value)) {
|
||||
return 0;
|
||||
}
|
||||
long rounded = lroundf(value);
|
||||
if (rounded > INT32_MAX) {
|
||||
return INT32_MAX;
|
||||
}
|
||||
if (rounded < INT32_MIN) {
|
||||
return INT32_MIN;
|
||||
}
|
||||
return static_cast<int32_t>(rounded);
|
||||
}
|
||||
|
||||
static const char *short_id_from_device_id(const char *device_id) {
|
||||
if (!device_id) {
|
||||
return "";
|
||||
}
|
||||
size_t len = strlen(device_id);
|
||||
if (len >= 4) {
|
||||
return device_id + (len - 4);
|
||||
}
|
||||
return device_id;
|
||||
}
|
||||
|
||||
static void format_float_2(char *buf, size_t buf_len, float value) {
|
||||
if (!buf || buf_len == 0) {
|
||||
return;
|
||||
}
|
||||
if (isnan(value)) {
|
||||
snprintf(buf, buf_len, "null");
|
||||
return;
|
||||
}
|
||||
snprintf(buf, buf_len, "%.2f", round2(value));
|
||||
}
|
||||
|
||||
static void set_int_or_null(JsonDocument &doc, const char *key, float value) {
|
||||
if (!key || key[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
if (isnan(value)) {
|
||||
doc[key] = nullptr;
|
||||
return;
|
||||
}
|
||||
doc[key] = round_to_i32(value);
|
||||
}
|
||||
|
||||
bool meterDataToJson(const MeterData &data, String &out_json) {
|
||||
StaticJsonDocument<STATE_JSON_DOC_CAPACITY> doc;
|
||||
doc["id"] = short_id_from_device_id(data.device_id);
|
||||
doc["ts"] = data.ts_utc;
|
||||
char buf[16];
|
||||
format_float_2(buf, sizeof(buf), data.energy_total_kwh);
|
||||
doc["e_kwh"] = serialized(buf);
|
||||
set_int_or_null(doc, "p_w", data.total_power_w);
|
||||
set_int_or_null(doc, "p1_w", data.phase_power_w[0]);
|
||||
set_int_or_null(doc, "p2_w", data.phase_power_w[1]);
|
||||
set_int_or_null(doc, "p3_w", data.phase_power_w[2]);
|
||||
format_float_2(buf, sizeof(buf), data.battery_voltage_v);
|
||||
doc["bat_v"] = serialized(buf);
|
||||
doc["bat_pct"] = data.battery_percent;
|
||||
if (data.link_valid) {
|
||||
doc["rssi"] = data.link_rssi_dbm;
|
||||
doc["snr"] = data.link_snr_db;
|
||||
}
|
||||
if (data.err_meter_read > 0) {
|
||||
doc["err_m"] = data.err_meter_read;
|
||||
}
|
||||
if (data.err_decode > 0) {
|
||||
doc["err_d"] = data.err_decode;
|
||||
}
|
||||
if (data.err_lora_tx > 0) {
|
||||
doc["err_tx"] = data.err_lora_tx;
|
||||
}
|
||||
doc["err_last"] = static_cast<uint8_t>(data.last_error);
|
||||
doc["rx_reject"] = data.rx_reject_reason;
|
||||
doc["rx_reject_text"] = rx_reject_reason_text(static_cast<RxRejectReason>(data.rx_reject_reason));
|
||||
|
||||
out_json = "";
|
||||
size_t len = serializeJson(doc, out_json);
|
||||
return len > 0;
|
||||
}
|
||||
Reference in New Issue
Block a user