蓝牙通道补齐脱机日志读取 (此前仅 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 行
243 lines
7.9 KiB
C
243 lines
7.9 KiB
C
/**
|
||
* 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;
|
||
}
|