feat(vd960DBN): offlog 协议导出命令分发 — log_stat/log_query/log_clear 落地

协议先行 (08893d5, MQTT V1.06 + TCP JSON V1.02) 的固件侧实现:
- offlog 新增导出 API: enabled/seq_last/type_str/evt_to_json
  (seq_first=seq_last-count+1, idx=start_seq-seq_first, count≤4)
- MQTT iot_handle_publish 三分支: log_stat/log_query/log_clear
  (log_query 响应 static buf 防大栈; log_clear 阻塞~2.8s 主循环可接受)
- TCP JSON 三 handler + cmd 表 (需鉴权)
- 单测新增 test_export_json (8 例全过): boot rst 大端/coil sub/evt_retry/data:null/seq 公式
- devlog V3.9
This commit is contained in:
wangfq
2026-08-05 08:53:18 +08:00
parent 08893d5134
commit 1a01316c7f
6 changed files with 331 additions and 1 deletions
@@ -18,6 +18,7 @@
#include "loop_uart_proto.h"
#include "simple_json.h"
#include "storage.h"
#include "offlog.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
@@ -636,7 +637,62 @@ static void handle_report_config(uint8_t socket, uint32_t msg_id, const char *js
g_report_cfg.enable, g_report_cfg.interval, g_report_cfg.sensor_type);
}
/* 4.16 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */
/* 4.16 log_stat — 查询脱机事件日志统计 */
static void handle_log_stat(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[256];
uint32_t count = offlog_count();
uint32_t seq_last = offlog_seq_last();
uint32_t seq_first = (count > 0) ? (seq_last - count + 1) : 0;
snprintf(data_json, sizeof(data_json),
"{\"stream\":\"event\",\"enabled\":%s,\"boot_seq\":%lu,"
"\"count\":%lu,\"capacity\":%u,\"seq_first\":%lu,\"seq_last\":%lu}",
offlog_enabled() ? "true" : "false",
(unsigned long)offlog_boot_seq(),
(unsigned long)count, (unsigned)OFFLOG_MAX_RECORDS,
(unsigned long)seq_first, (unsigned long)seq_last);
json_send_ok(socket, msg_id, "log_stat", data_json);
PRINT("JSON: log_stat count=%lu seq_first=%lu seq_last=%lu\n",
(unsigned long)count, (unsigned long)seq_first, (unsigned long)seq_last);
}
/* 4.17 log_query — 分页拉取脱机事件日志 (按全局序号, count≤4) */
static void handle_log_query(uint8_t socket, uint32_t msg_id, const char *json) {
char data_json[TCP_JSON_DATA_BUF_LEN];
uint32_t start_seq = json_get_uint_field(json, "\"start_seq\"");
uint32_t req_count = json_get_uint_field(json, "\"count\"");
uint32_t total = offlog_count();
uint32_t seq_last = offlog_seq_last();
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
if (req_count > OFFLOG_MAX_QUERY_RECORDS) req_count = OFFLOG_MAX_QUERY_RECORDS;
int pos = snprintf(data_json, sizeof(data_json),
"{\"start_seq\":%lu,\"records\":[",
(unsigned long)start_seq);
uint32_t fetched = 0;
if (total > 0 && start_seq >= seq_first && start_seq <= seq_last) {
uint32_t idx = start_seq - seq_first; /* 逻辑索引 = 全局序号 - seq_first */
while (fetched < req_count && (idx + fetched) < total) {
OfflogEvt evt;
if (offlog_read_idx((uint16_t)(idx + fetched), &evt) != 0) break;
if (fetched > 0) pos += snprintf(data_json + pos, sizeof(data_json) - pos, ",");
pos += offlog_evt_to_json(&evt, data_json + pos, sizeof(data_json) - pos);
fetched++;
}
}
snprintf(data_json + pos, sizeof(data_json) - pos, "]}");
json_send_ok(socket, msg_id, "log_query", data_json);
PRINT("JSON: log_query start_seq=%lu req=%lu fetched=%lu\n",
(unsigned long)start_seq, (unsigned long)req_count, (unsigned long)fetched);
}
/* 4.18 log_clear — 清除脱机事件日志 (审计留痕, 阻塞 ~2.8s) */
static void handle_log_clear(uint8_t socket, uint32_t msg_id, const char *json) {
offlog_clear();
json_send_ok(socket, msg_id, "log_clear", NULL);
PRINT("JSON: log_clear done\n");
}
/* 4.19 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */
static void handle_loop_version_query(uint8_t socket, uint32_t msg_id, const char *json) {
lup_send_get_version();
g_json_pending.active = 1;
@@ -754,6 +810,9 @@ static const JsonCmdEntry g_cmd_table[] = {
{"loop_sens_read", handle_loop_sens_read, 1},
{"loop_sens_write", handle_loop_sens_write, 1},
{"report_config", handle_report_config, 1},
{"log_stat", handle_log_stat, 1},
{"log_query", handle_log_query, 1},
{"log_clear", handle_log_clear, 1},
};
#define JSON_CMD_COUNT (sizeof(g_cmd_table) / sizeof(g_cmd_table[0]))