协议 V1.10 (3be9e50) 三处新增落地:
1. dev_info_query 响应 data 补 loop_ver/loop_hw_ver (0x4A 缓存值, 未完成/失败为空串)
2. initialize 上报同样携带 (尽力, 可为空)
3. 新增 §4.25 loop_version_query: 0x4A 异步实时查询, 响应在 iot_verq_poll() 回包
固件:
- loop_uart_proto.h/c: 新增 LoopVerCache + g_lup_ver_cache (0x4A 缓存);
lup_cache_from_info() 写缓存, lup_refresh_version_cache() 后台刷新 (通道空闲才发)
- iot_mqtt_srv.c: dev_info_query/initialize 加字段; loop_version_query 分支
(暂存 msg_id/deadline 300ms, busy 拒绝重复查询); iot_verq_poll() 异步回包
(RESPONSE_READY -> code=0 + 三字段 + 同步刷缓存; TIMEOUT -> code=5 保缓存值);
后台刷新: 上电 iot_mqtt_init + OTA done (iot_ota_report_result ok=1) 置 dirty
关键设计: 版本来源=0x4A 实时查询 (非 OTA 元数据 version=目标版本);
缓存值语义 dev_info_query/initialize 同步回包不等异步; 与 TCP 共用 g_lup_cmd
单命令通道 (后发覆盖, 先发者超时 — 低频命令, 待板上验证时序)
单测: tests/test_iot_loop_ver.c — gcc 隔离 (extract+embed 6 函数 + mock)
44 断言全过: 版本帧解析(正常+4 错误路径) / 缓存写入+格式化(空串/NULL 防护) /
verq_poll 成功回包(code=0+三字段+topic+缓存刷新+通道释放) / 超时回包(code=5+缓存值) /
后台刷新(发起/忙不消费/响应消费/残留超时清理)
验证: check_c_balance.py 3 文件 OK; 待板上验证 UART2 0x4A 与 loop_data 共用总线时序
464 lines
17 KiB
C
464 lines
17 KiB
C
/*
|
|
* test_iot_loop_ver.c — V1.10 验证: 0x4A 版本解析 + 版本缓存 + MQTT 异步回包时序
|
|
*
|
|
* 覆盖:
|
|
* 1. lup_parse_version: 0x4A 响应帧解析 (字段/错误路径)
|
|
* 2. lup_cache_from_info / iot_loop_ver_str / iot_loop_hw_ver_str: 缓存写入与格式化
|
|
* 3. iot_verq_poll 异步回包时序:
|
|
* - 命令 pending + RESPONSE_READY → code=0 回包 + 缓存刷新 + 通道释放
|
|
* - 命令 pending + TIMEOUT → code=5 回包
|
|
* - 后台刷新: dirty=1 + IDLE → 发起 0x4A (不设 pending)
|
|
* - 后台响应消费: !dirty + RESPONSE_READY → 更新缓存 + 释放
|
|
* - 残留超时清理: TIMEOUT → 归位 IDLE
|
|
*
|
|
* 编译: gcc -o test_iot_loop_ver test_iot_loop_ver.c && ./test_iot_loop_ver
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
/* ---- mock: 最小依赖 ---- */
|
|
#define LUP_MAGIC 0x7F
|
|
#define LUP_CMD_GET_VERSION 0x4A
|
|
#define LUP_MAX_PKG_LEN 70
|
|
#define IOT_MQTT_TOPIC_MAX_LEN 64
|
|
|
|
typedef struct {
|
|
uint8_t status;
|
|
uint8_t hard_main, hard_sub, hard_ssub;
|
|
uint8_t soft_main, soft_sub, soft_ssub;
|
|
char version_str[32];
|
|
} LUP_VersionInfo;
|
|
|
|
typedef struct {
|
|
uint8_t valid;
|
|
uint8_t hard_main, hard_sub, hard_ssub;
|
|
uint8_t soft_main, soft_sub, soft_ssub;
|
|
} LoopVerCache;
|
|
|
|
typedef enum {
|
|
LUP_STATE_IDLE = 0,
|
|
LUP_STATE_WAIT_RESPONSE,
|
|
LUP_STATE_RESPONSE_READY,
|
|
LUP_STATE_TIMEOUT
|
|
} LUP_CmdState;
|
|
|
|
typedef struct {
|
|
uint8_t pending_cmd;
|
|
uint32_t send_tick;
|
|
uint32_t timeout_ms;
|
|
LUP_CmdState state;
|
|
uint8_t resp_buf[LUP_MAX_PKG_LEN];
|
|
uint16_t resp_len;
|
|
} LUP_CmdTracker;
|
|
|
|
/* ---- mock 全局 ---- */
|
|
static LUP_CmdTracker g_lup_cmd;
|
|
static LoopVerCache g_lup_ver_cache;
|
|
static char g_iot_dev_serial[16] = "A1B2C3D4E5F6";
|
|
|
|
/* ---- mock 时间 ---- */
|
|
static uint32_t g_mock_ms = 100000;
|
|
static uint32_t mstick(void) { return g_mock_ms; }
|
|
static uint32_t dev_time_now(void) { return 1719000008UL; }
|
|
|
|
/* ---- mock 发布捕获 ---- */
|
|
static char g_pub_topic[64];
|
|
static char g_pub_payload[512];
|
|
static uint16_t g_pub_len;
|
|
static int g_pub_count;
|
|
static int g_getver_calls;
|
|
|
|
static int iot_mqtt_publish(const char *topic, const char *payload,
|
|
uint16_t payload_len, uint8_t qos)
|
|
{
|
|
(void)qos;
|
|
g_pub_count++;
|
|
snprintf(g_pub_topic, sizeof(g_pub_topic), "%s", topic ? topic : "");
|
|
if (payload_len < sizeof(g_pub_payload)) {
|
|
memcpy(g_pub_payload, payload, payload_len);
|
|
g_pub_payload[payload_len] = '\0';
|
|
} else {
|
|
memcpy(g_pub_payload, payload, sizeof(g_pub_payload) - 1);
|
|
g_pub_payload[sizeof(g_pub_payload) - 1] = '\0';
|
|
}
|
|
g_pub_len = payload_len;
|
|
return 0;
|
|
}
|
|
|
|
/* mock lup_send_get_version: 模拟 lup_cmd_begin 的状态推进 */
|
|
static void lup_send_get_version(void)
|
|
{
|
|
g_getver_calls++;
|
|
g_lup_cmd.pending_cmd = LUP_CMD_GET_VERSION;
|
|
g_lup_cmd.state = LUP_STATE_WAIT_RESPONSE;
|
|
g_lup_cmd.resp_len = 0;
|
|
memset(g_lup_cmd.resp_buf, 0, sizeof(g_lup_cmd.resp_buf));
|
|
}
|
|
|
|
/* mock lup_cmd_done: 释放命令通道 */
|
|
static void lup_cmd_done(void)
|
|
{
|
|
g_lup_cmd.pending_cmd = 0;
|
|
g_lup_cmd.state = LUP_STATE_IDLE;
|
|
g_lup_cmd.resp_len = 0;
|
|
}
|
|
|
|
#define PRINT(...) printf(__VA_ARGS__)
|
|
|
|
/* ---- 被测静态状态 (从 iot_mqtt_srv.c 提取的变量, 单测转为可访问全局) ---- */
|
|
static uint8_t _iot_verq_pending;
|
|
static uint32_t _iot_verq_msg_id;
|
|
static uint32_t _iot_verq_deadline;
|
|
static uint8_t _iot_ver_cache_dirty;
|
|
|
|
|
|
/* ================= 嵌入被测函数 (从真实源码提取) ================= */
|
|
|
|
int lup_parse_version(const uint8_t *pkg, uint16_t len, LUP_VersionInfo *info)
|
|
{
|
|
if (len < 10) return -1; // 1+3+7+2 = 13 min
|
|
|
|
// pkg layout: [7F][Addr=0][LEN=8][CMD=0x4A][Status...7bytes][XOR][SUM]
|
|
if (pkg[0] != LUP_MAGIC || pkg[3] != LUP_CMD_GET_VERSION) return -2;
|
|
if (pkg[2] < 7) return -3; // LEN should be >= 7
|
|
|
|
const uint8_t *val = pkg + 4; // skip magic + header(3)
|
|
info->status = val[0];
|
|
info->hard_main = val[1];
|
|
info->hard_sub = val[2];
|
|
info->hard_ssub = val[3];
|
|
info->soft_main = val[4];
|
|
info->soft_sub = val[5];
|
|
info->soft_ssub = val[6];
|
|
|
|
return 0;
|
|
}
|
|
|
|
void lup_cache_from_info(const LUP_VersionInfo *info)
|
|
{
|
|
if (!info) return;
|
|
g_lup_ver_cache.valid = 1;
|
|
g_lup_ver_cache.hard_main = info->hard_main;
|
|
g_lup_ver_cache.hard_sub = info->hard_sub;
|
|
g_lup_ver_cache.hard_ssub = info->hard_ssub;
|
|
g_lup_ver_cache.soft_main = info->soft_main;
|
|
g_lup_ver_cache.soft_sub = info->soft_sub;
|
|
g_lup_ver_cache.soft_ssub = info->soft_ssub;
|
|
}
|
|
|
|
static void iot_loop_ver_str(char *out, uint16_t out_len)
|
|
{
|
|
if (!g_lup_ver_cache.valid || !out || out_len == 0) { if (out && out_len) out[0] = '\0'; return; }
|
|
snprintf(out, out_len, "%d.%d.%d",
|
|
g_lup_ver_cache.soft_main, g_lup_ver_cache.soft_sub, g_lup_ver_cache.soft_ssub);
|
|
}
|
|
|
|
static void iot_loop_hw_ver_str(char *out, uint16_t out_len)
|
|
{
|
|
if (!g_lup_ver_cache.valid || !out || out_len == 0) { if (out && out_len) out[0] = '\0'; return; }
|
|
snprintf(out, out_len, "%d.%d.%d",
|
|
g_lup_ver_cache.hard_main, g_lup_ver_cache.hard_sub, g_lup_ver_cache.hard_ssub);
|
|
}
|
|
|
|
static void iot_verq_publish(const char *payload, uint16_t len)
|
|
{
|
|
char topic[IOT_MQTT_TOPIC_MAX_LEN];
|
|
snprintf(topic, sizeof(topic), "dld960/%s/dev", g_iot_dev_serial);
|
|
iot_mqtt_publish(topic, payload, len, 1);
|
|
}
|
|
|
|
static void iot_verq_poll(void)
|
|
{
|
|
if (_iot_verq_pending) {
|
|
/* 命令查询在途: 等 0x4A 响应或超时 */
|
|
if (g_lup_cmd.state == LUP_STATE_RESPONSE_READY) {
|
|
LUP_VersionInfo info;
|
|
char resp[320];
|
|
char ver_str[16], hw_str[16], vs_str[64];
|
|
int ok = 0;
|
|
memset(&info, 0, sizeof(info));
|
|
if (lup_parse_version(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &info) == 0) {
|
|
lup_cache_from_info(&info); /* 同步刷新缓存 */
|
|
snprintf(ver_str, sizeof(ver_str), "%d.%d.%d",
|
|
info.soft_main, info.soft_sub, info.soft_ssub);
|
|
snprintf(hw_str, sizeof(hw_str), "%d.%d.%d",
|
|
info.hard_main, info.hard_sub, info.hard_ssub);
|
|
snprintf(vs_str, sizeof(vs_str), "V%s (HW:%s)", ver_str, hw_str);
|
|
snprintf(resp, sizeof(resp),
|
|
"{\"msg_id\":%lu,\"cmd\":\"loop_version_query\",\"ts\":%lu,\"code\":0,"
|
|
"\"msg\":\"success\","
|
|
"\"data\":{\"loop_ver\":\"%s\",\"loop_hw_ver\":\"%s\",\"version_str\":\"%s\"}}",
|
|
_iot_verq_msg_id, dev_time_now(), ver_str, hw_str, vs_str);
|
|
ok = 1;
|
|
} else {
|
|
char lv[16], lhv[16];
|
|
iot_loop_ver_str(lv, sizeof(lv));
|
|
iot_loop_hw_ver_str(lhv, sizeof(lhv));
|
|
snprintf(resp, sizeof(resp),
|
|
"{\"msg_id\":%lu,\"cmd\":\"loop_version_query\",\"ts\":%lu,\"code\":5,"
|
|
"\"msg\":\"parse error\","
|
|
"\"data\":{\"loop_ver\":\"%s\",\"loop_hw_ver\":\"%s\",\"version_str\":\"\"}}",
|
|
_iot_verq_msg_id, dev_time_now(), lv, lhv);
|
|
}
|
|
lup_cmd_done();
|
|
_iot_verq_pending = 0;
|
|
iot_verq_publish(resp, strlen(resp));
|
|
PRINT("IOT: loop_version_query resp %s\n", ok ? "ok" : "parse error");
|
|
} else if (g_lup_cmd.state == LUP_STATE_TIMEOUT || mstick() > _iot_verq_deadline) {
|
|
char resp[320];
|
|
char lv[16], lhv[16];
|
|
iot_loop_ver_str(lv, sizeof(lv));
|
|
iot_loop_hw_ver_str(lhv, sizeof(lhv));
|
|
snprintf(resp, sizeof(resp),
|
|
"{\"msg_id\":%lu,\"cmd\":\"loop_version_query\",\"ts\":%lu,\"code\":5,"
|
|
"\"msg\":\"no response\","
|
|
"\"data\":{\"loop_ver\":\"%s\",\"loop_hw_ver\":\"%s\",\"version_str\":\"\"}}",
|
|
_iot_verq_msg_id, dev_time_now(), lv, lhv);
|
|
lup_cmd_done();
|
|
_iot_verq_pending = 0;
|
|
iot_verq_publish(resp, strlen(resp));
|
|
PRINT("IOT: loop_version_query timeout\n");
|
|
}
|
|
/* 其它 state (WAIT_RESPONSE) 继续等 */
|
|
} else {
|
|
/* 后台缓存刷新: 上电 / OTA done 后置 dirty, 命令通道空闲时发 0x4A */
|
|
if (_iot_ver_cache_dirty && g_lup_cmd.state == LUP_STATE_IDLE) {
|
|
lup_send_get_version();
|
|
_iot_ver_cache_dirty = 0;
|
|
PRINT("IOT: loop version cache refresh query\n");
|
|
} else if (!_iot_ver_cache_dirty && g_lup_cmd.state == LUP_STATE_RESPONSE_READY) {
|
|
/* 后台查询响应 (无命令 pending) → 更新缓存 + 释放通道 */
|
|
LUP_VersionInfo info;
|
|
memset(&info, 0, sizeof(info));
|
|
if (lup_parse_version(g_lup_cmd.resp_buf, g_lup_cmd.resp_len, &info) == 0) {
|
|
lup_cache_from_info(&info);
|
|
PRINT("IOT: loop version cache refreshed %d.%d.%d (HW:%d.%d.%d)\n",
|
|
info.soft_main, info.soft_sub, info.soft_ssub,
|
|
info.hard_main, info.hard_sub, info.hard_ssub);
|
|
}
|
|
lup_cmd_done();
|
|
} else if (g_lup_cmd.state == LUP_STATE_TIMEOUT) {
|
|
lup_cmd_done(); /* 清残留超时态 (后台查询失败或被其它命令覆盖) */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ================= 测试工具 ================= */
|
|
static int g_pass = 0, g_fail = 0;
|
|
#define CHECK(cond, msg) do { \
|
|
if (cond) { g_pass++; printf(" PASS: %s\n", msg); } \
|
|
else { g_fail++; printf(" FAIL: %s (line %d)\n", msg, __LINE__); } \
|
|
} while (0)
|
|
|
|
static void reset_state(void)
|
|
{
|
|
memset(&g_lup_cmd, 0, sizeof(g_lup_cmd));
|
|
memset(&g_lup_ver_cache, 0, sizeof(g_lup_ver_cache));
|
|
memset(&g_pub_payload, 0, sizeof(g_pub_payload));
|
|
g_pub_count = 0;
|
|
g_pub_len = 0;
|
|
g_getver_calls = 0;
|
|
_iot_verq_pending = 0;
|
|
_iot_verq_msg_id = 0;
|
|
_iot_verq_deadline = 0;
|
|
_iot_ver_cache_dirty = 0;
|
|
g_mock_ms = 100000;
|
|
}
|
|
|
|
/* 构造 0x4A 响应帧: 7F 00 LEN 4A [status][hard_m][hard_s][hard_ss][soft_m][soft_s][soft_ss] [xor][sum]
|
|
(checksum 字节 lup_parse_version 不校验, 填 0) */
|
|
static void build_version_frame(uint8_t *pkg, uint16_t *len,
|
|
uint8_t status, uint8_t hm, uint8_t hs, uint8_t hss,
|
|
uint8_t sm, uint8_t ss, uint8_t sss)
|
|
{
|
|
pkg[0] = LUP_MAGIC;
|
|
pkg[1] = 0x00;
|
|
pkg[2] = 0x08;
|
|
pkg[3] = LUP_CMD_GET_VERSION;
|
|
pkg[4] = status;
|
|
pkg[5] = hm; pkg[6] = hs; pkg[7] = hss;
|
|
pkg[8] = sm; pkg[9] = ss; pkg[10] = sss;
|
|
pkg[11] = 0; pkg[12] = 0; /* XOR/SUM 不校验 */
|
|
*len = 13;
|
|
}
|
|
|
|
static void test_parse_version(void)
|
|
{
|
|
printf("[1] lup_parse_version\n");
|
|
uint8_t pkg[16];
|
|
uint16_t len;
|
|
LUP_VersionInfo info;
|
|
|
|
build_version_frame(pkg, &len, 0x00, 1,0,0, 1,2,3);
|
|
CHECK(lup_parse_version(pkg, len, &info) == 0, "valid frame returns 0");
|
|
CHECK(info.status == 0x00, "status=0");
|
|
CHECK(info.hard_main == 1 && info.hard_sub == 0 && info.hard_ssub == 0, "hard=1.0.0");
|
|
CHECK(info.soft_main == 1 && info.soft_sub == 2 && info.soft_ssub == 3, "soft=1.2.3");
|
|
|
|
/* 错误路径 */
|
|
CHECK(lup_parse_version(pkg, 9, &info) == -1, "len<10 -> -1");
|
|
pkg[0] = 0x00;
|
|
CHECK(lup_parse_version(pkg, len, &info) == -2, "bad magic -> -2");
|
|
pkg[0] = LUP_MAGIC;
|
|
pkg[3] = 0x55;
|
|
CHECK(lup_parse_version(pkg, len, &info) == -2, "bad cmd -> -2");
|
|
pkg[3] = LUP_CMD_GET_VERSION;
|
|
pkg[2] = 0x05;
|
|
CHECK(lup_parse_version(pkg, len, &info) == -3, "LEN<7 -> -3");
|
|
}
|
|
|
|
static void test_cache(void)
|
|
{
|
|
printf("[2] 版本缓存\n");
|
|
LUP_VersionInfo info;
|
|
char out[24];
|
|
|
|
memset(&info, 0, sizeof(info));
|
|
info.hard_main = 1; info.hard_sub = 0; info.hard_ssub = 0;
|
|
info.soft_main = 1; info.soft_sub = 2; info.soft_ssub = 3;
|
|
lup_cache_from_info(&info);
|
|
CHECK(g_lup_ver_cache.valid == 1, "cache valid after write");
|
|
CHECK(g_lup_ver_cache.soft_main == 1 && g_lup_ver_cache.soft_ssub == 3, "cache soft fields");
|
|
|
|
iot_loop_ver_str(out, sizeof(out));
|
|
CHECK(strcmp(out, "1.2.3") == 0, "loop_ver str = 1.2.3");
|
|
iot_loop_hw_ver_str(out, sizeof(out));
|
|
CHECK(strcmp(out, "1.0.0") == 0, "loop_hw_ver str = 1.0.0");
|
|
|
|
/* 无效缓存 → 空串 */
|
|
memset(&g_lup_ver_cache, 0, sizeof(g_lup_ver_cache));
|
|
iot_loop_ver_str(out, sizeof(out));
|
|
CHECK(strcmp(out, "") == 0, "invalid cache -> empty str");
|
|
iot_loop_hw_ver_str(out, sizeof(out));
|
|
CHECK(strcmp(out, "") == 0, "invalid hw cache -> empty str");
|
|
|
|
/* NULL/0 长度防护 */
|
|
iot_loop_ver_str(NULL, 0);
|
|
CHECK(1, "NULL guard no crash");
|
|
}
|
|
|
|
static void test_verq_success(void)
|
|
{
|
|
printf("[3] verq_poll: 命令成功回包\n");
|
|
reset_state();
|
|
uint8_t pkg[16];
|
|
uint16_t len;
|
|
build_version_frame(pkg, &len, 0x00, 1,0,0, 1,2,3);
|
|
memcpy(g_lup_cmd.resp_buf, pkg, len);
|
|
g_lup_cmd.resp_len = len;
|
|
g_lup_cmd.state = LUP_STATE_RESPONSE_READY;
|
|
|
|
_iot_verq_pending = 1;
|
|
_iot_verq_msg_id = 407;
|
|
_iot_verq_deadline = g_mock_ms + 300;
|
|
|
|
iot_verq_poll();
|
|
|
|
CHECK(g_pub_count == 1, "publish once");
|
|
CHECK(strstr(g_pub_payload, "\"msg_id\":407") != NULL, "msg_id echoed");
|
|
CHECK(strstr(g_pub_payload, "\"code\":0") != NULL, "code=0");
|
|
CHECK(strstr(g_pub_payload, "\"loop_ver\":\"1.2.3\"") != NULL, "loop_ver=1.2.3");
|
|
CHECK(strstr(g_pub_payload, "\"loop_hw_ver\":\"1.0.0\"") != NULL, "loop_hw_ver=1.0.0");
|
|
CHECK(strstr(g_pub_payload, "\"version_str\":\"V1.2.3 (HW:1.0.0)\"") != NULL, "version_str format");
|
|
CHECK(strcmp(g_pub_topic, "dld960/A1B2C3D4E5F6/dev") == 0, "resp topic");
|
|
CHECK(_iot_verq_pending == 0, "pending cleared");
|
|
CHECK(g_lup_cmd.state == LUP_STATE_IDLE, "cmd channel released");
|
|
CHECK(g_lup_ver_cache.valid == 1, "cache refreshed by command resp");
|
|
}
|
|
|
|
static void test_verq_timeout(void)
|
|
{
|
|
printf("[4] verq_poll: 命令超时\n");
|
|
reset_state();
|
|
/* 缓存先有效, 超时回包应携带缓存值 */
|
|
g_lup_ver_cache.valid = 1;
|
|
g_lup_ver_cache.soft_main = 9; g_lup_ver_cache.soft_sub = 9; g_lup_ver_cache.soft_ssub = 9;
|
|
g_lup_ver_cache.hard_main = 8; g_lup_ver_cache.hard_sub = 8; g_lup_ver_cache.hard_ssub = 8;
|
|
|
|
g_lup_cmd.state = LUP_STATE_WAIT_RESPONSE; /* 还挂着 */
|
|
_iot_verq_pending = 1;
|
|
_iot_verq_msg_id = 408;
|
|
_iot_verq_deadline = g_mock_ms + 300;
|
|
g_mock_ms += 400; /* 超过 deadline */
|
|
|
|
iot_verq_poll();
|
|
|
|
CHECK(g_pub_count == 1, "timeout publish once");
|
|
CHECK(strstr(g_pub_payload, "\"code\":5") != NULL, "code=5");
|
|
CHECK(strstr(g_pub_payload, "\"msg\":\"no response\"") != NULL, "msg=no response");
|
|
CHECK(strstr(g_pub_payload, "\"loop_ver\":\"9.9.9\"") != NULL, "cache value kept in resp");
|
|
CHECK(_iot_verq_pending == 0, "pending cleared");
|
|
CHECK(g_lup_cmd.state == LUP_STATE_IDLE, "cmd released after timeout");
|
|
|
|
/* 直接 TIMEOUT 态 */
|
|
reset_state();
|
|
g_lup_cmd.state = LUP_STATE_TIMEOUT;
|
|
_iot_verq_pending = 1;
|
|
_iot_verq_msg_id = 409;
|
|
_iot_verq_deadline = g_mock_ms + 300;
|
|
iot_verq_poll();
|
|
CHECK(g_pub_count == 1, "TIMEOUT state publish once");
|
|
CHECK(strstr(g_pub_payload, "\"code\":5") != NULL, "TIMEOUT state code=5");
|
|
}
|
|
|
|
static void test_bg_refresh(void)
|
|
{
|
|
printf("[5] verq_poll: 后台缓存刷新\n");
|
|
reset_state();
|
|
|
|
/* 5a. dirty=1 + IDLE → 发起查询, 不设 pending */
|
|
_iot_ver_cache_dirty = 1;
|
|
g_lup_cmd.state = LUP_STATE_IDLE;
|
|
iot_verq_poll();
|
|
CHECK(g_getver_calls == 1, "bg query issued");
|
|
CHECK(_iot_ver_cache_dirty == 0, "dirty cleared");
|
|
CHECK(_iot_verq_pending == 0, "no command pending set");
|
|
CHECK(g_pub_count == 0, "no publish for bg query");
|
|
|
|
/* 5b. 通道忙 (WAIT_RESPONSE) 时 dirty 不消费 */
|
|
{
|
|
int base = g_getver_calls;
|
|
_iot_ver_cache_dirty = 1;
|
|
g_lup_cmd.state = LUP_STATE_WAIT_RESPONSE;
|
|
iot_verq_poll();
|
|
CHECK(g_getver_calls == base, "busy channel -> no query");
|
|
CHECK(_iot_ver_cache_dirty == 1, "dirty kept while busy");
|
|
}
|
|
|
|
/* 5c. 后台响应消费: !dirty + RESPONSE_READY → 更新缓存 + 释放, 不回包 */
|
|
reset_state();
|
|
uint8_t pkg[16];
|
|
uint16_t len;
|
|
build_version_frame(pkg, &len, 0x00, 2,0,0, 3,4,5);
|
|
memcpy(g_lup_cmd.resp_buf, pkg, len);
|
|
g_lup_cmd.resp_len = len;
|
|
g_lup_cmd.state = LUP_STATE_RESPONSE_READY;
|
|
_iot_ver_cache_dirty = 0;
|
|
iot_verq_poll();
|
|
CHECK(g_pub_count == 0, "bg resp no publish");
|
|
CHECK(g_lup_ver_cache.valid == 1, "bg resp cache valid");
|
|
CHECK(g_lup_ver_cache.soft_main == 3 && g_lup_ver_cache.soft_ssub == 5, "bg resp soft=3.4.5");
|
|
CHECK(g_lup_cmd.state == LUP_STATE_IDLE, "bg resp channel released");
|
|
|
|
/* 5d. 残留超时清理 */
|
|
reset_state();
|
|
g_lup_cmd.state = LUP_STATE_TIMEOUT;
|
|
iot_verq_poll();
|
|
CHECK(g_lup_cmd.state == LUP_STATE_IDLE, "stale timeout cleaned");
|
|
}
|
|
|
|
int main(void)
|
|
{
|
|
printf("=== test_iot_loop_ver: V1.10 版本缓存 + 异步回包 ===\n");
|
|
reset_state();
|
|
test_parse_version();
|
|
test_cache();
|
|
test_verq_success();
|
|
test_verq_timeout();
|
|
test_bg_refresh();
|
|
printf("\nRESULT: %d passed, %d failed\n", g_pass, g_fail);
|
|
return g_fail ? 1 : 0;
|
|
}
|