feat(vd960DBN): 传感快照区落地 snapshot.c + BLE SNAP_STAT/QUERY/CLEAR

- snapshot.h/c: 64B 定长 SnapRec (头部16B + 4x12B 0xC0线圈数据原样)
  环形分区独立于事件日志, 快照区 = 总容量 - 固定区576KB - 事件区
- 线程模型: USART2 ISR 只打包+RAM暂存(8深满丢新), 主循环 snap_flush 落盘
  (iot_sensor_ingest 在中断上下文, SPI 45ms 擦除严禁进中断)
- dbn_ble_srv: SNAP_STAT(0x28)/SNAP_QUERY(0x29)/SNAP_CLEAR(0x2A)
  QUERY 上限 2 条 (64x2+2=130B, ODR 教训防御)
- iot_mqtt_srv: ingest 挂 snap_enqueue, publish_sensor 挂 snap_flush
  (放在 READY 检查前, 断网照常落盘)
- peripheral_main: offlog_init 后加 snap_init
- 清空审计: 写事件流 log_clear payload[0]=2
- 测试: test_snapshot.c 8 例全过, offlog/ble_offlog 回归全过
- 文档: DLD960_BLE协议 V1.02 + ROADMAP P1.2 状态 + devlog
This commit is contained in:
wangfq
2026-08-12 17:40:14 +08:00
parent a8120cc852
commit 92887a3d92
12 changed files with 1123 additions and 8 deletions
@@ -11,6 +11,7 @@
#include <string.h>
#include "net_srv.h"
#include "offlog.h"
#include "snapshot.h"
extern uint16_t peripheral_get_mtu(void);
uint8_t g_flag_notify_temp = 0; //临时通知notify flag, 0 disable, 1 enable
@@ -1012,6 +1013,97 @@ void manage_dbn_ble_default(uint8_t *pkg, uint8_t len)
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
PRINT("BLE: offlog_clear done\n");
} break;
case CMD_DBN_SNAP_STAT: {
/* snap stat: status(1) boot_seq(2) count(4) capacity(4) seq_first(4) seq_last(4), all LE */
uint8_t _i = 0;
uint32_t _total = snap_count();
uint32_t _seq_last = snap_seq_last();
uint32_t _seq_first = (_total > 0) ? (_seq_last - _total + 1) : 0;
uint32_t _boot = snap_boot_seq();
uint32_t _cap = SNAP_MAX_RECORDS;
if (!snap_enabled()) {
tmp_ble_buf[_i++] = 0x01; /* disabled */
} else {
tmp_ble_buf[_i++] = 0x00; /* ok */
tmp_ble_buf[_i++] = (uint8_t)(_boot & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_boot >> 8) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)(_total & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_total >> 8) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_total >> 16) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_total >> 24) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)(_cap & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_cap >> 8) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_cap >> 16) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_cap >> 24) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)(_seq_first & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_seq_first >> 8) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_seq_first >> 16) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_seq_first >> 24) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)(_seq_last & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_seq_last >> 8) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_seq_last >> 16) & 0xFF);
tmp_ble_buf[_i++] = (uint8_t)((_seq_last >> 24) & 0xFF);
}
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
PRINT("BLE: snap_stat count=%lu seq_first=%lu seq_last=%lu\n",
(unsigned long)_total, (unsigned long)_seq_first, (unsigned long)_seq_last);
} break;
case CMD_DBN_SNAP_QUERY: {
/* paged fetch: req pkg[4..7]=start_seq(LE32) pkg[8]=count(1..2)
resp: status(1) count(1) + N x 64B SnapRec raw (LE) */
uint32_t _start_seq = 0;
uint32_t _req_count = 0;
uint32_t _total = snap_count();
uint32_t _seq_last = snap_seq_last();
uint32_t _seq_first = (_total > 0) ? (_seq_last - _total + 1) : 0;
uint8_t _i = 0, _j = 0;
if (!snap_enabled()) {
tmp_ble_buf[_i++] = 0x01; /* disabled */
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
break;
}
if (len < 11) { /* frame too short: magic+header+len+cmd+5data+2ckb */
tmp_ble_buf[_i++] = 0x02; /* bad request */
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
break;
}
_start_seq = (uint32_t)pkg[4] | ((uint32_t)pkg[5] << 8)
| ((uint32_t)pkg[6] << 16) | ((uint32_t)pkg[7] << 24);
_req_count = pkg[8];
if (_req_count > SNAP_MAX_QUERY_RECORDS) _req_count = SNAP_MAX_QUERY_RECORDS;
if (_req_count == 0) _req_count = SNAP_MAX_QUERY_RECORDS;
/* overflow guard: resp dat = 2 + N*64 must fit tmp buf (ODR lesson) */
{ uint16_t _max_rec = (MAX_BLE_TMP_BUF_LEN >= 2) ?
((MAX_BLE_TMP_BUF_LEN - 2) / (uint16_t)sizeof(SnapRec)) : 0;
if (_req_count > _max_rec) _req_count = _max_rec; }
tmp_ble_buf[_i++] = 0x00; /* status ok */
tmp_ble_buf[_i++] = 0; /* count placeholder */
if (_total > 0 && _start_seq >= _seq_first && _start_seq <= _seq_last) {
uint32_t _idx = _start_seq - _seq_first;
while (_j < _req_count && (_idx + _j) < _total) {
SnapRec _rec;
if (snap_read_idx(_idx + _j, &_rec) != 0) break;
memcpy(&tmp_ble_buf[_i], &_rec, sizeof(SnapRec));
_i += sizeof(SnapRec);
_j++;
}
}
tmp_ble_buf[1] = _j;
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
PRINT("BLE: snap_query start_seq=%lu req=%lu fetched=%u\n",
(unsigned long)_start_seq, (unsigned long)_req_count, (unsigned)_j);
} break;
case CMD_DBN_SNAP_CLEAR: {
/* clear snapshot (audit trail). blocking (sectors erase), main-loop ctx ok */
uint8_t _i = 0;
snap_clear();
tmp_ble_buf[_i++] = 0x00; /* ok */
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
PRINT("BLE: snap_clear done\n");
} break;
default:
{
} break;