feat(vd960DBN): TCP/MQTT log_* 命令支持快照流 stream=snapshot (2026-08-18)
- snapshot.c/h: 新增 snap_rec_to_json() — SnapRec 64B → JSON (channels 对齐 0xC0, variation 3B 符号扩展, misc_type 全枚举) - tcp_json_srv.c: handle_log_stat/query/clear 加 stream 解析 + snapshot 分支 - iot_mqtt_srv.c: log_stat/query/clear 加 stream 解析 + snapshot 分支 - 事件流 count=0 按上限处理 (与 BLE 对齐); 快照 QUERY count≤2, CLEAR ~45ms - 新增 gcc 隔离单测 tests/test_snap_to_json.c 34 断言全过 - devlog V4.1
This commit is contained in:
@@ -133,5 +133,6 @@ uint32_t snap_seq_last(void); /* 最新一条记录全局序
|
||||
uint32_t snap_boot_seq(void); /* 当前 boot 序号 */
|
||||
int snap_read_idx(uint32_t idx, SnapRec *out); /* 按逻辑序号读, 0=成功 -1=越界 */
|
||||
void snap_clear(void); /* 清空快照区 (审计写入事件流) */
|
||||
int snap_rec_to_json(const SnapRec *r, char *buf, int buf_len); /* 记录 -> JSON (TCP/MQTT log_query) */
|
||||
|
||||
#endif /* _SNAPSHOT_H__ */
|
||||
|
||||
@@ -549,32 +549,52 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
|
||||
}
|
||||
|
||||
} else if (strcmp(cmd_str, "log_stat") == 0) {
|
||||
/* 日志统计/分页定位: seq_first = seq_last - count + 1 */
|
||||
uint32_t total = offlog_count();
|
||||
uint32_t seq_last = offlog_seq_last();
|
||||
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
|
||||
/* 日志统计/分页定位: event/snapshot 流 (协议 V1.07) */
|
||||
char stream_buf[16];
|
||||
memset(stream_buf, 0, sizeof(stream_buf));
|
||||
simple_parse_json(json, "\"stream\"", stream_buf);
|
||||
char resp[300];
|
||||
snprintf(resp, sizeof(resp),
|
||||
"{\"msg_id\":%lu,\"cmd\":\"log_stat\","
|
||||
"\"ts\":%lu,\"code\":0,\"msg\":\"success\","
|
||||
"\"data\":{\"stream\":\"event\",\"enabled\":%s,\"boot_seq\":%lu,"
|
||||
"\"count\":%lu,\"capacity\":%u,\"seq_first\":%lu,\"seq_last\":%lu}}",
|
||||
msg_id, dev_time_now(),
|
||||
offlog_enabled() ? "true" : "false",
|
||||
(unsigned long)offlog_boot_seq(),
|
||||
(unsigned long)total, (unsigned)OFFLOG_MAX_RECORDS,
|
||||
(unsigned long)seq_first, (unsigned long)seq_last);
|
||||
if (strcmp(stream_buf, "snapshot") == 0) {
|
||||
uint32_t total = snap_count();
|
||||
uint32_t seq_last = snap_seq_last();
|
||||
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
|
||||
snprintf(resp, sizeof(resp),
|
||||
"{\"msg_id\":%lu,\"cmd\":\"log_stat\","
|
||||
"\"ts\":%lu,\"code\":0,\"msg\":\"success\","
|
||||
"\"data\":{\"stream\":\"snapshot\",\"enabled\":%s,\"boot_seq\":%lu,"
|
||||
"\"count\":%lu,\"capacity\":%lu,\"seq_first\":%lu,\"seq_last\":%lu}}",
|
||||
msg_id, dev_time_now(),
|
||||
snap_enabled() ? "true" : "false",
|
||||
(unsigned long)snap_boot_seq(),
|
||||
(unsigned long)total, (unsigned long)SNAP_MAX_RECORDS,
|
||||
(unsigned long)seq_first, (unsigned long)seq_last);
|
||||
PRINT("IOT: log_stat(snapshot) count=%lu seq_first=%lu seq_last=%lu\n",
|
||||
(unsigned long)total, (unsigned long)seq_first, (unsigned long)seq_last);
|
||||
} else {
|
||||
uint32_t total = offlog_count();
|
||||
uint32_t seq_last = offlog_seq_last();
|
||||
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
|
||||
snprintf(resp, sizeof(resp),
|
||||
"{\"msg_id\":%lu,\"cmd\":\"log_stat\","
|
||||
"\"ts\":%lu,\"code\":0,\"msg\":\"success\","
|
||||
"\"data\":{\"stream\":\"event\",\"enabled\":%s,\"boot_seq\":%lu,"
|
||||
"\"count\":%lu,\"capacity\":%lu,\"seq_first\":%lu,\"seq_last\":%lu}}",
|
||||
msg_id, dev_time_now(),
|
||||
offlog_enabled() ? "true" : "false",
|
||||
(unsigned long)offlog_boot_seq(),
|
||||
(unsigned long)total, (unsigned long)OFFLOG_MAX_RECORDS,
|
||||
(unsigned long)seq_first, (unsigned long)seq_last);
|
||||
PRINT("IOT: log_stat count=%lu seq_first=%lu seq_last=%lu\n",
|
||||
(unsigned long)total, (unsigned long)seq_first, (unsigned long)seq_last);
|
||||
}
|
||||
iot_mqtt_publish(resp_topic, resp, strlen(resp), 1);
|
||||
PRINT("IOT: log_stat count=%lu seq_first=%lu seq_last=%lu\n",
|
||||
(unsigned long)total, (unsigned long)seq_first, (unsigned long)seq_last);
|
||||
|
||||
} else if (strcmp(cmd_str, "log_query") == 0) {
|
||||
/* 分页拉取脱机日志: 按全局序号, count≤4 (500B 发布限制) */
|
||||
/* 分页拉取脱机日志: 按全局序号, event/snapshot 流 (协议 V1.07) */
|
||||
static char resp[IOT_MQTT_SEND_BUF_LEN]; /* static: 避免大栈开销 */
|
||||
char stream_buf[16];
|
||||
uint32_t start_seq = 0, req_count = 0;
|
||||
uint32_t total = offlog_count();
|
||||
uint32_t seq_last = offlog_seq_last();
|
||||
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
|
||||
int is_snap = 0;
|
||||
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
simple_parse_json(json, "\"start_seq\"", tmp);
|
||||
@@ -582,38 +602,74 @@ static void iot_handle_publish(const char *topic, uint8_t *payload, int payload_
|
||||
memset(tmp, 0, sizeof(tmp));
|
||||
simple_parse_json(json, "\"count\"", tmp);
|
||||
if (strlen(tmp) > 0) req_count = (uint32_t)strtoul(tmp, NULL, 10);
|
||||
if (req_count > OFFLOG_MAX_QUERY_RECORDS) req_count = OFFLOG_MAX_QUERY_RECORDS;
|
||||
memset(stream_buf, 0, sizeof(stream_buf));
|
||||
simple_parse_json(json, "\"stream\"", stream_buf);
|
||||
if (strcmp(stream_buf, "snapshot") == 0) is_snap = 1;
|
||||
|
||||
int pos = snprintf(resp, sizeof(resp),
|
||||
"{\"msg_id\":%lu,\"cmd\":\"log_query\",\"ts\":%lu,\"code\":0,"
|
||||
"\"msg\":\"success\",\"data\":{\"start_seq\":%lu,\"records\":[",
|
||||
msg_id, dev_time_now(), (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(resp + pos, sizeof(resp) - pos, ",");
|
||||
pos += offlog_evt_to_json(&evt, resp + pos, sizeof(resp) - pos);
|
||||
fetched++;
|
||||
|
||||
if (is_snap) {
|
||||
uint32_t total = snap_count();
|
||||
uint32_t seq_last = snap_seq_last();
|
||||
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
|
||||
if (req_count > SNAP_MAX_QUERY_RECORDS) req_count = SNAP_MAX_QUERY_RECORDS;
|
||||
if (req_count == 0) req_count = SNAP_MAX_QUERY_RECORDS;
|
||||
if (snap_enabled() && 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) {
|
||||
SnapRec rec;
|
||||
if (snap_read_idx(idx + fetched, &rec) != 0) break;
|
||||
if (fetched > 0) pos += snprintf(resp + pos, sizeof(resp) - pos, ",");
|
||||
pos += snap_rec_to_json(&rec, resp + pos, sizeof(resp) - pos);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
PRINT("IOT: log_query(snapshot) start_seq=%lu req=%lu fetched=%lu\n",
|
||||
(unsigned long)start_seq, (unsigned long)req_count, (unsigned long)fetched);
|
||||
} else {
|
||||
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;
|
||||
if (req_count == 0) req_count = OFFLOG_MAX_QUERY_RECORDS;
|
||||
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(resp + pos, sizeof(resp) - pos, ",");
|
||||
pos += offlog_evt_to_json(&evt, resp + pos, sizeof(resp) - pos);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
PRINT("IOT: log_query start_seq=%lu req=%lu fetched=%lu\n",
|
||||
(unsigned long)start_seq, (unsigned long)req_count, (unsigned long)fetched);
|
||||
}
|
||||
snprintf(resp + pos, sizeof(resp) - pos, "]}}");
|
||||
PRINT("IOT: log_query start_seq=%lu req=%lu fetched=%lu\n",
|
||||
(unsigned long)start_seq, (unsigned long)req_count, (unsigned long)fetched);
|
||||
iot_mqtt_publish(resp_topic, resp, strlen(resp), 1);
|
||||
|
||||
} else if (strcmp(cmd_str, "log_clear") == 0) {
|
||||
/* 清除日志 (审计留痕). 阻塞 ~2.8s (63 扇区擦除), 主循环上下文可接受 */
|
||||
offlog_clear();
|
||||
/* 清除日志 (审计留痕). event 阻塞 ~2.8s (63 扇区), snapshot 阻塞 ~45ms (逻辑清除+当前扇区) */
|
||||
char stream_buf[16];
|
||||
memset(stream_buf, 0, sizeof(stream_buf));
|
||||
simple_parse_json(json, "\"stream\"", stream_buf);
|
||||
char resp[256];
|
||||
if (strcmp(stream_buf, "snapshot") == 0) {
|
||||
snap_clear();
|
||||
PRINT("IOT: log_clear(snapshot) done\n");
|
||||
} else {
|
||||
offlog_clear();
|
||||
PRINT("IOT: log_clear done\n");
|
||||
}
|
||||
snprintf(resp, sizeof(resp),
|
||||
"{\"msg_id\":%lu,\"cmd\":\"log_clear\","
|
||||
"\"ts\":%lu,\"code\":0,\"msg\":\"success\"}",
|
||||
msg_id, dev_time_now());
|
||||
iot_mqtt_publish(resp_topic, resp, strlen(resp), 1);
|
||||
PRINT("IOT: log_clear done\n");
|
||||
|
||||
} else {
|
||||
// 暂不支持的命令, 返回错误
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "storage.h"
|
||||
#include "cmcng.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* 编译期断言: 记录必须是 64B (定长环形索引依赖) */
|
||||
typedef char snap_rec_size_must_be_64[(sizeof(SnapRec) == 64) ? 1 : -1];
|
||||
@@ -421,3 +422,61 @@ void snap_clear(void)
|
||||
}
|
||||
snap_flush_head();
|
||||
}
|
||||
|
||||
/* SnapRec -> JSON object (channels aligned with 0xC0 wire format).
|
||||
Used by TCP JSON / MQTT log_query stream=snapshot (protocol V1.03/V1.07).
|
||||
Returns snprintf written length; caller must ensure buf_len enough. */
|
||||
int snap_rec_to_json(const SnapRec *r, char *buf, int buf_len)
|
||||
{
|
||||
int coil_count = (r->len > (SNAP_COILS_MAX * SNAP_COIL_BYTES))
|
||||
? SNAP_COILS_MAX : (r->len / SNAP_COIL_BYTES);
|
||||
if (coil_count < 0) coil_count = 0;
|
||||
|
||||
int pos = snprintf(buf, buf_len,
|
||||
"{\"seq\":%lu,\"boot_seq\":%u,\"ts_ms\":%lu,\"coil_count\":%u,\"channels\":[",
|
||||
(unsigned long)r->seq, (unsigned)r->boot_seq,
|
||||
(unsigned long)r->ts_ms, (unsigned)coil_count);
|
||||
|
||||
int c;
|
||||
for (c = 0; c < coil_count; c++) {
|
||||
const uint8_t *p = &r->coils[c * SNAP_COIL_BYTES];
|
||||
uint8_t cfg = p[0];
|
||||
uint8_t cond = p[1];
|
||||
uint32_t freq = (uint32_t)p[2] | ((uint32_t)p[3] << 8) | ((uint32_t)p[4] << 16);
|
||||
int32_t variation = (int32_t)((uint32_t)p[5] | ((uint32_t)p[6] << 8) | ((uint32_t)p[7] << 16));
|
||||
uint32_t misc = (uint32_t)p[8] | ((uint32_t)p[9] << 8)
|
||||
| ((uint32_t)p[10] << 16) | ((uint32_t)p[11] << 24);
|
||||
uint8_t freq_level = (cfg >> 6) & 0x03;
|
||||
uint8_t direction = (cfg >> 5) & 0x01;
|
||||
uint8_t freq_type = (cfg >> 4) & 0x01;
|
||||
uint8_t sensitivity = cfg & 0x0F;
|
||||
uint8_t condition = (cond >> 4) & 0x0F;
|
||||
uint8_t loop_state = (cond >> 3) & 0x01; /* 0=normal 1=cut */
|
||||
uint8_t car_state = (cond >> 2) & 0x01; /* 0=nocar 1=car */
|
||||
uint8_t misc_type = cond & 0x03;
|
||||
const char *fl = "high";
|
||||
const char *mt = "time";
|
||||
|
||||
if (variation & 0x800000) variation |= (int32_t)0xFF000000; /* 3B sign extend */
|
||||
|
||||
if (freq_level == 1) fl = "mid_high";
|
||||
else if (freq_level == 2) fl = "mid_low";
|
||||
else if (freq_level == 3) fl = "low";
|
||||
|
||||
if (misc_type == 1) mt = "cut_count";
|
||||
else if (misc_type == 2) mt = "flow_count";
|
||||
else if (misc_type == 3) mt = "relay_count";
|
||||
|
||||
if (c > 0) pos += snprintf(buf + pos, buf_len - pos, ",");
|
||||
pos += snprintf(buf + pos, buf_len - pos,
|
||||
"{\"ch\":%u,\"freq_level\":\"%s\",\"direction\":%u,\"freq_type\":%u,"
|
||||
"\"sensitivity\":%u,\"condition\":%u,\"loop_ok\":%s,\"has_car\":%s,"
|
||||
"\"misc_type\":\"%s\",\"freq\":%lu,\"variation\":%ld,\"misc\":%lu}",
|
||||
(unsigned)(c + 1), fl, (unsigned)direction, (unsigned)freq_type,
|
||||
(unsigned)sensitivity, (unsigned)condition,
|
||||
loop_state ? "false" : "true", car_state ? "true" : "false",
|
||||
mt, (unsigned long)freq, (long)variation, (unsigned long)misc);
|
||||
}
|
||||
pos += snprintf(buf + pos, buf_len - pos, "]}");
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "simple_json.h"
|
||||
#include "storage.h"
|
||||
#include "offlog.h"
|
||||
#include "snapshot.h"
|
||||
#include "fault_diag.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -638,59 +639,120 @@ 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 log_stat — 查询脱机事件日志统计 */
|
||||
/* 4.16 log_stat — 查询脱机日志统计 (event/snapshot 流, 协议 V1.03) */
|
||||
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);
|
||||
char stream_buf[16];
|
||||
uint32_t count, seq_last, seq_first, capacity;
|
||||
const char *stream = "event";
|
||||
|
||||
memset(stream_buf, 0, sizeof(stream_buf));
|
||||
simple_parse_json(json, "\"stream\"", stream_buf);
|
||||
if (strcmp(stream_buf, "snapshot") == 0) stream = "snapshot";
|
||||
|
||||
if (strcmp(stream, "snapshot") == 0) {
|
||||
count = snap_count();
|
||||
seq_last = snap_seq_last();
|
||||
seq_first = (count > 0) ? (seq_last - count + 1) : 0;
|
||||
capacity = SNAP_MAX_RECORDS;
|
||||
snprintf(data_json, sizeof(data_json),
|
||||
"{\"stream\":\"snapshot\",\"enabled\":%s,\"boot_seq\":%lu,"
|
||||
"\"count\":%lu,\"capacity\":%lu,\"seq_first\":%lu,\"seq_last\":%lu}",
|
||||
snap_enabled() ? "true" : "false",
|
||||
(unsigned long)snap_boot_seq(),
|
||||
(unsigned long)count, (unsigned long)capacity,
|
||||
(unsigned long)seq_first, (unsigned long)seq_last);
|
||||
PRINT("JSON: log_stat(snapshot) count=%lu seq_first=%lu seq_last=%lu\n",
|
||||
(unsigned long)count, (unsigned long)seq_first, (unsigned long)seq_last);
|
||||
} else {
|
||||
count = offlog_count();
|
||||
seq_last = offlog_seq_last();
|
||||
seq_first = (count > 0) ? (seq_last - count + 1) : 0;
|
||||
capacity = OFFLOG_MAX_RECORDS;
|
||||
snprintf(data_json, sizeof(data_json),
|
||||
"{\"stream\":\"event\",\"enabled\":%s,\"boot_seq\":%lu,"
|
||||
"\"count\":%lu,\"capacity\":%lu,\"seq_first\":%lu,\"seq_last\":%lu}",
|
||||
offlog_enabled() ? "true" : "false",
|
||||
(unsigned long)offlog_boot_seq(),
|
||||
(unsigned long)count, (unsigned long)capacity,
|
||||
(unsigned long)seq_first, (unsigned long)seq_last);
|
||||
PRINT("JSON: log_stat count=%lu seq_first=%lu seq_last=%lu\n",
|
||||
(unsigned long)count, (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) */
|
||||
/* 4.17 log_query — 分页拉取脱机日志 (event/snapshot 流; 事件≤4, 快照≤2, 协议 V1.03) */
|
||||
static void handle_log_query(uint8_t socket, uint32_t msg_id, const char *json) {
|
||||
char data_json[TCP_JSON_DATA_BUF_LEN];
|
||||
char stream_buf[16];
|
||||
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 is_snap = 0;
|
||||
|
||||
memset(stream_buf, 0, sizeof(stream_buf));
|
||||
simple_parse_json(json, "\"stream\"", stream_buf);
|
||||
if (strcmp(stream_buf, "snapshot") == 0) is_snap = 1;
|
||||
|
||||
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++;
|
||||
|
||||
if (is_snap) {
|
||||
uint32_t total = snap_count();
|
||||
uint32_t seq_last = snap_seq_last();
|
||||
uint32_t seq_first = (total > 0) ? (seq_last - total + 1) : 0;
|
||||
if (req_count > SNAP_MAX_QUERY_RECORDS) req_count = SNAP_MAX_QUERY_RECORDS;
|
||||
if (req_count == 0) req_count = SNAP_MAX_QUERY_RECORDS;
|
||||
if (snap_enabled() && 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) {
|
||||
SnapRec rec;
|
||||
if (snap_read_idx(idx + fetched, &rec) != 0) break;
|
||||
if (fetched > 0) pos += snprintf(data_json + pos, sizeof(data_json) - pos, ",");
|
||||
pos += snap_rec_to_json(&rec, data_json + pos, sizeof(data_json) - pos);
|
||||
fetched++;
|
||||
}
|
||||
}
|
||||
PRINT("JSON: log_query(snapshot) start_seq=%lu req=%lu fetched=%lu\n",
|
||||
(unsigned long)start_seq, (unsigned long)req_count, (unsigned long)fetched);
|
||||
} else {
|
||||
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;
|
||||
if (req_count == 0) req_count = OFFLOG_MAX_QUERY_RECORDS;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
PRINT("JSON: log_query start_seq=%lu req=%lu fetched=%lu\n",
|
||||
(unsigned long)start_seq, (unsigned long)req_count, (unsigned long)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) */
|
||||
/* 4.18 log_clear — 清除脱机日志 (event/snapshot 流, 审计留痕, 协议 V1.03) */
|
||||
static void handle_log_clear(uint8_t socket, uint32_t msg_id, const char *json) {
|
||||
offlog_clear();
|
||||
char stream_buf[16];
|
||||
memset(stream_buf, 0, sizeof(stream_buf));
|
||||
simple_parse_json(json, "\"stream\"", stream_buf);
|
||||
if (strcmp(stream_buf, "snapshot") == 0) {
|
||||
snap_clear(); /* 阻塞 ~45ms (逻辑清除 + 当前写扇区) */
|
||||
PRINT("JSON: log_clear(snapshot) done\n");
|
||||
} else {
|
||||
offlog_clear(); /* 阻塞 ~2.8s (63 扇区擦除) */
|
||||
PRINT("JSON: log_clear done\n");
|
||||
}
|
||||
json_send_ok(socket, msg_id, "log_clear", NULL);
|
||||
PRINT("JSON: log_clear done\n");
|
||||
}
|
||||
|
||||
/* 4.19 loop_version_query — 获取地感MCU版本号 (CMD 0x4A) */
|
||||
|
||||
Reference in New Issue
Block a user