feat(vd960DBN): BLE 读取脱机日志 — OFFLOG_STAT/QUERY/CLEAR (0x25/0x26/0x27)
蓝牙通道补齐脱机日志读取 (此前仅 MQTT V1.06 / TCP JSON V1.02 有 log_* 命令): - 3 条 BLE 命令, 语义对齐 MQTT log_stat/log_query/log_clear - QUERY 直接传 32B OfflogEvt 原始结构 (二进制协议, 无需 JSON), 复用 idx = start_seq - seq_first 定位, 不新增 offlog API - 缓冲扩容: MAX_BLE_TMP_BUF_LEN 100→132, 新增 MAX_BLE_DAT_BUF_LEN=132 (QUERY 响应 1+4x32B=129B), clear_buf_dbn_ble_all memset 同步 - 新协议文档 docs/DLD960_BLE协议.md V1.00 (帧格式+分包+命令+记录结构) - 隔离测试 test_ble_offlog.c 嵌入源文件 3 case 真实文本, 7 例全过; offlog 回归 8 例 ALL PASS - devlog V4.0; README 协议矩阵补 BLE 行
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
#include "storage.h"
|
||||
#include <string.h>
|
||||
#include "net_srv.h"
|
||||
|
||||
#include "offlog.h"
|
||||
|
||||
uint8_t g_flag_notify_temp = 0; //临时通知notify flag, 0 disable, 1 enable
|
||||
|
||||
@@ -65,7 +65,7 @@ void clear_buf_dbn_ble_all(Buf_DBN_BLE * buf)
|
||||
buf->flag = 0;
|
||||
buf->magic = 0;
|
||||
buf->cmd = 0;
|
||||
memset(buf->dat, 0, MAX_BLE_BUF_LEN);
|
||||
memset(buf->dat, 0, MAX_BLE_DAT_BUF_LEN);
|
||||
buf->dat_len = 0;
|
||||
buf->dat_offset = 0;
|
||||
buf->pkg_amount = 0;
|
||||
@@ -876,6 +876,93 @@ void manage_dbn_ble_default(uint8_t *pkg, uint8_t len)
|
||||
}
|
||||
PRINT("Rcv_acs_enable_flag: %d\n", g_dbn_ble_state_acs_enable.flag);
|
||||
} break;
|
||||
case CMD_DBN_OFFLOG_STAT: {
|
||||
/* offlog 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 = offlog_count();
|
||||
uint32_t _seq_last = offlog_seq_last();
|
||||
uint32_t _seq_first = (_total > 0) ? (_seq_last - _total + 1) : 0;
|
||||
uint32_t _boot = offlog_boot_seq();
|
||||
uint32_t _cap = OFFLOG_MAX_RECORDS;
|
||||
|
||||
if (!offlog_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: offlog_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_OFFLOG_QUERY: {
|
||||
/* paged fetch: req pkg[4..7]=start_seq(LE32) pkg[8]=count(1..4)
|
||||
resp: status(1) count(1) + N x 32B OfflogEvt raw (LE) */
|
||||
uint32_t _start_seq = 0;
|
||||
uint32_t _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;
|
||||
uint8_t _i = 0, _j = 0;
|
||||
|
||||
if (!offlog_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 > OFFLOG_MAX_QUERY_RECORDS) _req_count = OFFLOG_MAX_QUERY_RECORDS;
|
||||
if (_req_count == 0) _req_count = OFFLOG_MAX_QUERY_RECORDS;
|
||||
|
||||
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) {
|
||||
OfflogEvt _evt;
|
||||
if (offlog_read_idx((uint16_t)(_idx + _j), &_evt) != 0) break;
|
||||
memcpy(&tmp_ble_buf[_i], &_evt, sizeof(OfflogEvt));
|
||||
_i += sizeof(OfflogEvt);
|
||||
_j++;
|
||||
}
|
||||
}
|
||||
tmp_ble_buf[1] = _j;
|
||||
set_response_buf(&g_buf_ble_response, MAGIC_BYTE_DBN_DEFAULT, _cmd, tmp_ble_buf, _i);
|
||||
PRINT("BLE: offlog_query start_seq=%lu req=%lu fetched=%u\n",
|
||||
(unsigned long)_start_seq, (unsigned long)_req_count, (unsigned)_j);
|
||||
} break;
|
||||
case CMD_DBN_OFFLOG_CLEAR: {
|
||||
/* clear offlog (audit trail). blocking ~2.8s (63 sectors erase), main-loop ctx ok */
|
||||
uint8_t _i = 0;
|
||||
offlog_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: offlog_clear done\n");
|
||||
} break;
|
||||
default:
|
||||
{
|
||||
} break;
|
||||
|
||||
@@ -49,6 +49,10 @@
|
||||
#define CMD_DBN_SET_CJQ_PARAM 0x23 // 设置 车检器参数
|
||||
#define CMD_DBN_GET_CJQ_PARAM 0x24 // 读取 车检器参数
|
||||
|
||||
#define CMD_DBN_OFFLOG_STAT 0x25 // query offlog stat
|
||||
#define CMD_DBN_OFFLOG_QUERY 0x26 // query offlog records by seq
|
||||
#define CMD_DBN_OFFLOG_CLEAR 0x27 // clear offlog (audit trail)
|
||||
|
||||
|
||||
#define CMD_DBN_RW_UART_BAUD 0x31
|
||||
|
||||
@@ -73,7 +77,8 @@
|
||||
#define MAX_BLE_DAT_RESPONSE_LEN (BLE_BUFF_MAX_LEN - 4) // 14 //返回 的所有字节数不能超过20个字节,除去 magic、header和ckb校验字节,剩下数据的字节数为14
|
||||
#define MAX_BLE_Notify_Buf_LEN BLE_BUFF_MAX_LEN //24
|
||||
#define MAX_BLE_BUF_LEN BLE_BUFF_MAX_LEN //128
|
||||
#define MAX_BLE_TMP_BUF_LEN BLE_BUFF_MAX_LEN
|
||||
#define MAX_BLE_TMP_BUF_LEN 132 // offlog QUERY: 1 status + 4x32B records
|
||||
#define MAX_BLE_DAT_BUF_LEN 132 // response data buffer (align tmp buf)
|
||||
|
||||
typedef struct _BLE_Notify_Buf_
|
||||
{
|
||||
@@ -91,7 +96,7 @@ typedef struct _BUF_DBN_BLE_
|
||||
uint8_t dat_offset;
|
||||
uint8_t pkg_amount;
|
||||
uint8_t pkg_seq;
|
||||
uint8_t dat[MAX_BLE_BUF_LEN];
|
||||
uint8_t dat[MAX_BLE_DAT_BUF_LEN];
|
||||
} Buf_DBN_BLE;
|
||||
|
||||
extern Buf_DBN_BLE g_buf_ble_response;
|
||||
|
||||
@@ -6,6 +6,54 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-10 — BLE 读取脱机日志接口 (OFFLOG_STAT/QUERY/CLEAR)
|
||||
|
||||
### 背景
|
||||
|
||||
offlog 日志已落地 MQTT V1.06 / TCP JSON V1.02 分发(2026-08-05),但 BLE 侧(小程序/APP 现场离线取证)无读取通道。现场常见场景:设备无网/断网,需蓝牙直连拉日志判断"重复上线"是平台问题还是设备复位。
|
||||
|
||||
### 方案:3 条 BLE 命令,复用 offlog 底层 API
|
||||
|
||||
| 命令码 | 名称 | 语义(对齐 MQTT log_*) |
|
||||
|--------|------|--------------------------|
|
||||
| `0x25` | `CMD_DBN_OFFLOG_STAT` | 日志统计:status + boot_seq(2) + count(4) + capacity(4) + seq_first(4) + seq_last(4),全 LE,19B |
|
||||
| `0x26` | `CMD_DBN_OFFLOG_QUERY` | 分页拉取:请求 `start_seq(LE32) + count(1)`;响应 `status(1) + count(1) + N×32B OfflogEvt 原始结构`,N≤4(对齐 OFFLOG_MAX_QUERY_RECORDS) |
|
||||
| `0x27` | `CMD_DBN_OFFLOG_CLEAR` | 清空(审计留痕),阻塞 ~2.8s,主循环上下文可接受 |
|
||||
|
||||
- QUERY 定位复用 MQTT 同款公式 `idx = start_seq - seq_first` → `offlog_read_idx()`,不新增 offlog API
|
||||
- 记录直接传 32B 二进制 OfflogEvt(BLE 是二进制协议,无需 JSON 化;文档给出结构偏移 + 事件类型表)
|
||||
- 响应超 96B 自动分包(`set_response_buf` 既有机制):QUERY 最大 130B → 2 包
|
||||
|
||||
### 缓冲扩容(RAM +64B)
|
||||
|
||||
| 宏 | 旧值 | 新值 | 原因 |
|
||||
|----|------|------|------|
|
||||
| `MAX_BLE_TMP_BUF_LEN` | `BLE_BUFF_MAX_LEN`(100) | **132** | QUERY 组包 1+4×32=129B |
|
||||
| `Buf_DBN_BLE.dat` | `MAX_BLE_BUF_LEN`(100) | **`MAX_BLE_DAT_BUF_LEN`(132)** | set_response_buf 拷贝 129B |
|
||||
|
||||
新增 `MAX_BLE_DAT_BUF_LEN=132`;`clear_buf_dbn_ble_all` 的 memset 同步改用新宏。`tmp_ble_buf`/`Buf_DBN_BLE` 仅 dbn_ble_srv.c/h 内使用,影响面局部。
|
||||
|
||||
### 关键决策与坑
|
||||
|
||||
1. **GBK+CRLF 文件二进制编辑**:dbn_ble_srv.c/h 是 GBK 编码(file 报 ISO-8859),`patch` 工具会静默转码成 UTF-8 致中文注释乱码。全部用 Python rb/wb 精确替换,每处 count==1 校验;改后 file 复核仍 ISO-8859。新增注释用 ASCII 规避编码问题。
|
||||
2. **case 内变量名冲突**:`manage_dbn_ble_default` 顶部已有 `uint8_t i = 0, k = 0`,case 内再声明 `i` 会 redefinition。全部改用 `_i`/`_j`。
|
||||
3. **QUERY 短帧防御**:`len < 11`(magic+header+len+cmd+5data+2ckb)返回 status=0x02,防 pkg[4..8] 越界读。
|
||||
4. **LOG_CLEAR 阻塞告警**:BLE 连接期间 2.8s 擦除阻塞,若 MQTT 在线会导致 WCHNET 短时失服务(60s keepalive 可吸收),文档已标注。
|
||||
5. **编码验证**:check_c_balance.py 自身 docstring 有转义 bug(`\\x` 在普通字符串触发 unicodeescape),已改 raw string 修复。
|
||||
|
||||
### 验证
|
||||
|
||||
- 新增 `tests/test_ble_offlog.c`:**隔离测试框架嵌入 dbn_ble_srv.c 提取的 3 case 真实文本**(`extract_offlog_cases.py` 提取,生成物 gitignore),mock offlog_* + set_response_buf,7 例全过:
|
||||
- STAT 正常(19B 字段逐字节验证)/ disabled
|
||||
- QUERY 正常(4×32B 记录 + seq/type/payload 偏移验证)/ 越界空 / 短帧 / disabled
|
||||
- CLEAR 正常
|
||||
- offlog 回归 8 例 ALL PASS(既有 7 例 + export_json 未破坏)
|
||||
- 新增 `docs/DLD960_BLE协议.md` V1.00(帧格式 + 分包 + 3 命令 + OfflogEvt 32B 结构 + 事件类型表)
|
||||
- README 协议文档表新增 BLE 协议行
|
||||
- 本地无 RISC-V 工具链,MRS 真编译待板上联调(语法级 check_c_balance 通过)
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-05 — offlog 协议导出命令分发落地 (MQTT V1.06 + TCP JSON V1.02)
|
||||
|
||||
### 背景
|
||||
@@ -756,6 +804,7 @@ TCP 超时要等 ~2 分钟才触发 `SINT_STAT_TIM_OUT`,
|
||||
|
||||
| 版本 | 时间 | 说明 |
|
||||
|------|------|------|
|
||||
| V4.0 | 2026-08-10 | BLE 脱机日志接口: OFFLOG_STAT/QUERY/CLEAR (0x25/0x26/0x27), 32B OfflogEvt 二进制直传, 缓冲扩容 132B, 隔离测试 7 例 + 协议文档 V1.00 |
|
||||
| V3.9 | 2026-08-05 | offlog 协议导出命令分发: log_stat/log_query/log_clear 落地 MQTT V1.06 + TCP JSON V1.02 (seq_first/seq_last 计算、idx 映射、OFFLOG_MAX_QUERY_RECORDS=4、log_clear 阻塞~2.8s) |
|
||||
| V3.8 | 2026-08-04 | offlog: TIME_ANCHOR 严格用平台下发 unix_ts (offlog_evt_ts), 单测 7 例 |
|
||||
| V3.7 | 2026-08-04 | tcp_json_srv 缓冲宏化: data_json[2048]→TCP_JSON_DATA_BUF_LEN(1024), 省栈1KB×3 |
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
"""从 dbn_ble_srv.c 提取脱机日志 3 case 文本, 供 test_ble_offlog.c 隔离验证。
|
||||
|
||||
用法: python3 extract_offlog_cases.py
|
||||
输出: ./offlog_cases_embedded.c (在 tests/ 目录下运行)
|
||||
|
||||
原理: 测的是源文件真实代码而非拷贝 — 提取 `case CMD_DBN_OFFLOG_STAT`
|
||||
到 `default:` 之间的文本 (含 STAT/QUERY/CLEAR 3 case), 嵌入测试框架编译。
|
||||
"""
|
||||
import os
|
||||
|
||||
SRC = os.path.join(os.path.dirname(__file__),
|
||||
"../BLE/OnlyUpdateApp_Peripheral/APP/dbn_ble_srv.c")
|
||||
OUT = os.path.join(os.path.dirname(__file__), "offlog_cases_embedded.c")
|
||||
|
||||
with open(SRC, "rb") as f:
|
||||
content = f.read()
|
||||
text = content.decode("utf-8", errors="replace").replace("\r\n", "\n")
|
||||
|
||||
start = text.find("case CMD_DBN_OFFLOG_STAT")
|
||||
end = text.find("default:\n", start)
|
||||
if start == -1 or end == -1:
|
||||
raise SystemExit("ERROR: 未找到 offlog 3 case (确认 dbn_ble_srv.c 已修改)")
|
||||
|
||||
cases = text[start:end]
|
||||
for c in ("CMD_DBN_OFFLOG_STAT", "CMD_DBN_OFFLOG_QUERY", "CMD_DBN_OFFLOG_CLEAR"):
|
||||
if c not in cases:
|
||||
raise SystemExit(f"ERROR: 提取内容缺少 {c}")
|
||||
|
||||
with open(OUT, "w") as f:
|
||||
f.write(cases)
|
||||
print(f"OK: {OUT} ({len(cases)} bytes, 3 cases)")
|
||||
@@ -0,0 +1,242 @@
|
||||
/**
|
||||
* test_ble_offlog.c — dbn_ble_srv.c 脱机日志 3 case 的隔离验证
|
||||
*
|
||||
* 直接从 dbn_ble_srv.c 提取 case 文本嵌入本文件, 测的是源文件真实代码。
|
||||
* Mock: offlog_* / set_response_buf / PRINT / Buf_DBN_BLE 类型。
|
||||
*
|
||||
* 编译: gcc -I../BLE/OnlyUpdateApp_Peripheral/APP/include \
|
||||
* -o test_ble_offlog test_ble_offlog.c (需先跑 extract_offlog_cases.py)
|
||||
* 运行: ./test_ble_offlog
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* ============ 类型/宏 (来自 dbn_ble_srv.h 编辑后, 避免 include BLE 栈) ============ */
|
||||
#define MAGIC_BYTE_DBN_DEFAULT 0x8F
|
||||
#define CMD_DBN_OFFLOG_STAT 0x25
|
||||
#define CMD_DBN_OFFLOG_QUERY 0x26
|
||||
#define CMD_DBN_OFFLOG_CLEAR 0x27
|
||||
#define MAX_BLE_TMP_BUF_LEN 132
|
||||
#define MAX_BLE_DAT_BUF_LEN 132
|
||||
#define MAX_BLE_DAT_RESPONSE_LEN (100 - 4) /* 单包数据上限 96B */
|
||||
|
||||
typedef struct _BLE_Notify_Buf_ {
|
||||
uint8_t flag;
|
||||
uint8_t len;
|
||||
uint8_t buf[100];
|
||||
} BLE_Notify_Buf;
|
||||
|
||||
typedef struct _BUF_DBN_BLE_ {
|
||||
uint8_t flag;
|
||||
uint8_t magic;
|
||||
uint8_t cmd;
|
||||
uint8_t dat_len ;
|
||||
uint8_t dat_offset;
|
||||
uint8_t pkg_amount;
|
||||
uint8_t pkg_seq;
|
||||
uint8_t dat[MAX_BLE_DAT_BUF_LEN];
|
||||
} Buf_DBN_BLE;
|
||||
|
||||
#include "offlog.h"
|
||||
|
||||
#define PRINT(...) ((void)0)
|
||||
|
||||
/* ============ 全局 (来自 dbn_ble_srv.c) ============ */
|
||||
static uint8_t tmp_ble_buf[MAX_BLE_TMP_BUF_LEN];
|
||||
static Buf_DBN_BLE g_buf_ble_response;
|
||||
|
||||
/* ============ offlog mock ============ */
|
||||
static uint32_t mock_count = 0;
|
||||
static uint32_t mock_seq_last = 0;
|
||||
static uint32_t mock_boot = 0;
|
||||
static uint8_t mock_enabled = 1;
|
||||
|
||||
uint16_t offlog_count(void) { return (uint16_t)mock_count; }
|
||||
uint32_t offlog_seq_last(void) { return mock_seq_last; }
|
||||
uint32_t offlog_boot_seq(void) { return mock_boot; }
|
||||
uint8_t offlog_enabled(void) { return mock_enabled; }
|
||||
void offlog_clear(void) { mock_count = 1; mock_seq_last++; } /* 审计留痕: count 归 1, seq 继续 */
|
||||
|
||||
int offlog_read_idx(uint16_t idx, OfflogEvt *out)
|
||||
{
|
||||
uint32_t seq_first;
|
||||
if (idx >= mock_count) return -1;
|
||||
seq_first = mock_seq_last - mock_count + 1;
|
||||
memset(out, 0, sizeof(OfflogEvt));
|
||||
out->magic = OFFLOG_EVT_MAGIC;
|
||||
out->type = (idx % 3 == 0) ? OFFLOG_EVT_BOOT :
|
||||
(idx % 3 == 1) ? OFFLOG_EVT_IOT_READY : OFFLOG_EVT_COIL;
|
||||
out->seq = seq_first + idx;
|
||||
out->boot_seq = 2;
|
||||
out->ts_ms = 1000 + idx * 100;
|
||||
out->unix_ts = 1784768575UL;
|
||||
if (idx % 3 == 2) { /* COIL: sub=1 ch=1 value=97 */
|
||||
out->payload[0] = 1; out->payload[1] = 1;
|
||||
out->payload[2] = 0; out->payload[3] = 0; out->payload[4] = 0; out->payload[5] = 97;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============ set_response_buf mock: 只记录调用参数 ============ */
|
||||
static uint8_t last_cmd = 0;
|
||||
static uint8_t last_len = 0;
|
||||
static uint8_t last_dat[160];
|
||||
|
||||
uint8_t set_response_buf(Buf_DBN_BLE *response_dst, uint8_t magic, uint8_t cmd,
|
||||
uint8_t *dat, uint8_t dat_len)
|
||||
{
|
||||
uint8_t i;
|
||||
memset(last_dat, 0, sizeof(last_dat));
|
||||
last_cmd = cmd;
|
||||
last_len = dat_len;
|
||||
for (i = 0; i < dat_len; i++) last_dat[i] = dat[i];
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ============ 被测: 从 dbn_ble_srv.c 提取的 3 case (嵌入 switch) ============ */
|
||||
static void run_case(uint8_t cmd, uint8_t *pkg, uint8_t len)
|
||||
{
|
||||
uint8_t _cmd = cmd;
|
||||
switch (_cmd) {
|
||||
#include "offlog_cases_embedded.c"
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============ 断言 ============ */
|
||||
static int failures = 0;
|
||||
#define CHECK(cond) do { \
|
||||
if (!(cond)) { printf("FAIL %s:%d %s\n", __FILE__, __LINE__, #cond); failures++; } \
|
||||
} while (0)
|
||||
|
||||
static uint8_t pkg[16];
|
||||
|
||||
static void test_stat_ok(void)
|
||||
{
|
||||
mock_enabled = 1; mock_count = 1234; mock_seq_last = 1333; mock_boot = 2;
|
||||
memset(tmp_ble_buf, 0, sizeof(tmp_ble_buf));
|
||||
run_case(0x25, NULL, 0);
|
||||
CHECK(last_cmd == 0x25);
|
||||
CHECK(last_len == 19);
|
||||
/* status=0 */
|
||||
CHECK(last_dat[0] == 0x00);
|
||||
/* boot_seq=2 LE */
|
||||
CHECK(last_dat[1] == 0x02 && last_dat[2] == 0x00);
|
||||
/* count=1234 LE */
|
||||
CHECK(last_dat[3] == 0xD2 && last_dat[4] == 0x04 && last_dat[5] == 0x00 && last_dat[6] == 0x00);
|
||||
/* capacity=8064 LE */
|
||||
CHECK(last_dat[7] == 0x80 && last_dat[8] == 0x1F && last_dat[9] == 0x00 && last_dat[10] == 0x00);
|
||||
/* seq_first=100 LE */
|
||||
CHECK(last_dat[11] == 0x64 && last_dat[12] == 0x00 && last_dat[13] == 0x00 && last_dat[14] == 0x00);
|
||||
/* seq_last=1333 LE */
|
||||
CHECK(last_dat[15] == 0x35 && last_dat[16] == 0x05 && last_dat[17] == 0x00 && last_dat[18] == 0x00);
|
||||
printf("PASS test_stat_ok\n");
|
||||
}
|
||||
|
||||
static void test_stat_disabled(void)
|
||||
{
|
||||
mock_enabled = 0;
|
||||
run_case(0x25, NULL, 0);
|
||||
CHECK(last_cmd == 0x25);
|
||||
CHECK(last_len == 1);
|
||||
CHECK(last_dat[0] == 0x01);
|
||||
printf("PASS test_stat_disabled\n");
|
||||
}
|
||||
|
||||
static void test_query_ok(void)
|
||||
{
|
||||
/* 10 条: seq 11..20, 请求 start_seq=13 count=4 → 返回 seq 13,14,15,16 */
|
||||
mock_enabled = 1; mock_count = 10; mock_seq_last = 20; mock_boot = 2;
|
||||
memset(pkg, 0, sizeof(pkg));
|
||||
pkg[4] = 13; /* start_seq LE */
|
||||
pkg[5] = 0; pkg[6] = 0; pkg[7] = 0;
|
||||
pkg[8] = 4; /* count */
|
||||
memset(tmp_ble_buf, 0, sizeof(tmp_ble_buf));
|
||||
run_case(0x26, pkg, 11);
|
||||
|
||||
CHECK(last_cmd == 0x26);
|
||||
CHECK(last_len == 2 + 4 * 32); /* status + count + 4×32B */
|
||||
CHECK(last_dat[0] == 0x00); /* status ok */
|
||||
CHECK(last_dat[1] == 4); /* fetched 4 */
|
||||
/* 记录0 (idx=2, seq=13): type=coil (mock: idx%3==2 → COIL), 仅查 magic+seq */
|
||||
CHECK(last_dat[2] == OFFLOG_EVT_MAGIC);
|
||||
CHECK(last_dat[6] == 13); /* seq LE32 偏移4 */
|
||||
CHECK(last_dat[7] == 0);
|
||||
/* 记录1 (idx=3, seq=14): type=boot */
|
||||
CHECK(last_dat[2+32+1] == OFFLOG_EVT_BOOT);
|
||||
CHECK(last_dat[2+32+4] == 14);
|
||||
/* 记录2 (idx=4, seq=15): type=iot_ready, payload 空 */
|
||||
CHECK(last_dat[2+64+1] == OFFLOG_EVT_IOT_READY);
|
||||
CHECK(last_dat[2+64+4] == 15);
|
||||
/* 记录3 (idx=5, seq=16): type=coil, payload sub=1 ch=1 value=97 */
|
||||
CHECK(last_dat[2+96+1] == OFFLOG_EVT_COIL);
|
||||
CHECK(last_dat[2+96+4] == 16);
|
||||
CHECK(last_dat[2+96+20] == 1); /* payload[0] sub */
|
||||
CHECK(last_dat[2+96+21] == 1); /* payload[1] ch */
|
||||
CHECK(last_dat[2+96+25] == 97); /* payload[5] value LSB */
|
||||
printf("PASS test_query_ok\n");
|
||||
}
|
||||
|
||||
static void test_query_range_error(void)
|
||||
{
|
||||
mock_enabled = 1; mock_count = 10; mock_seq_last = 20; mock_boot = 2;
|
||||
memset(pkg, 0, sizeof(pkg));
|
||||
pkg[4] = 100; /* 越界 > seq_last */
|
||||
pkg[8] = 4;
|
||||
run_case(0x26, pkg, 11);
|
||||
CHECK(last_cmd == 0x26);
|
||||
CHECK(last_len == 2);
|
||||
CHECK(last_dat[0] == 0x00);
|
||||
CHECK(last_dat[1] == 0); /* 空 records */
|
||||
printf("PASS test_query_range_error\n");
|
||||
}
|
||||
|
||||
static void test_query_short_frame(void)
|
||||
{
|
||||
mock_enabled = 1;
|
||||
pkg[8] = 4;
|
||||
run_case(0x26, pkg, 9); /* len < 11 */
|
||||
CHECK(last_cmd == 0x26);
|
||||
CHECK(last_len == 1);
|
||||
CHECK(last_dat[0] == 0x02); /* bad request */
|
||||
printf("PASS test_query_short_frame\n");
|
||||
}
|
||||
|
||||
static void test_query_disabled(void)
|
||||
{
|
||||
mock_enabled = 0;
|
||||
pkg[8] = 4;
|
||||
run_case(0x26, pkg, 11);
|
||||
CHECK(last_cmd == 0x26);
|
||||
CHECK(last_len == 1);
|
||||
CHECK(last_dat[0] == 0x01); /* disabled */
|
||||
printf("PASS test_query_disabled\n");
|
||||
}
|
||||
|
||||
static void test_clear(void)
|
||||
{
|
||||
mock_enabled = 1; mock_count = 5; mock_seq_last = 30;
|
||||
run_case(0x27, NULL, 0);
|
||||
CHECK(last_cmd == 0x27);
|
||||
CHECK(last_len == 1);
|
||||
CHECK(last_dat[0] == 0x00);
|
||||
/* offlog_clear mock: count 归 1, seq_last 递增 */
|
||||
CHECK(mock_count == 1);
|
||||
CHECK(mock_seq_last == 31);
|
||||
printf("PASS test_clear\n");
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_stat_ok();
|
||||
test_stat_disabled();
|
||||
test_query_ok();
|
||||
test_query_range_error();
|
||||
test_query_short_frame();
|
||||
test_query_disabled();
|
||||
test_clear();
|
||||
if (failures == 0) printf("\nALL PASS\n");
|
||||
else printf("\n%d FAILURES\n", failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user