feat(vd960DBN): Loop MCU 远程 OTA 固件实现 (MQTT V1.08, ROADMAP P1.4 ①)
先存后刷: MQTT 分片下载 → W25Qxx OTA 暂存区 (0x010000 512KB) → 全镜像 CRC32 复核 → 本地 0x9F ISP 透传刷写 Loop MCU (AT32F421) - 新增 ota_srv.c/h: CRC32(ISO-HDLC 逐位零表)/OtaMeta 双备份(节流 4KB flush)/ 会话状态机(begin/data/end/abort/flash)/本地刷写状态机(非阻塞 tick: A5→A6→A7 停等 ACK 1s×3)/安全窗口(有车拒绝 force 跳过)/失败 event_report ota_error - iot_mqtt_srv.c: 6 个 ota_* 命令分发 + 会话静默(event_report 积压/offlog·快照 落盘暂停, 结束补发) + IOT_EVT_OTA_ERROR + iot_any_car - usart_biz.c: uart_srv OTA 分支 ACK 分发 (本地刷写→ota_flash_feed_ack, BLE 透传不变) - peripheral_main.c: ota_init + ota_poll 挂主循环 - offlog: OTA_START 0x60/OTA_RESULT 0x61 审计事件 - tests/test_ota_srv.c: gcc 隔离单测 9 组全过 (mock NOR/UART2/时钟, 嵌入真实实现) 抓到 2 个真 bug: ①A7 序号未递减(bootloader 等不到末包) ②重发时 retry 清零(超时永不失败) - 语法检查: ota_srv/iot_mqtt_srv/usart_biz/offlog 0 新增错误 (基线 interrupt 假阳性除外) - devlog 置顶条目 + 待板上验证清单
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
/* mock CONFIG.h — ota_srv.c 隔离单测用 (gcc, 无 WCH 环境) */
|
||||
#ifndef __MOCK_CONFIG_H__
|
||||
#define __MOCK_CONFIG_H__
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
/* mock cmcng.h — ota_srv.c 隔离单测用 (gcc, 无 WCH 环境) */
|
||||
#ifndef __MOCK_CMCNG_H__
|
||||
#define __MOCK_CMCNG_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t flag;
|
||||
uint32_t timeout;
|
||||
uint32_t tick;
|
||||
} Flag_Counter;
|
||||
|
||||
extern Flag_Counter g_flag_counter_key;
|
||||
extern Flag_Counter g_flag_counter_ota;
|
||||
|
||||
uint32_t mstick(void);
|
||||
void UART2_SendString(uint8_t *buf, uint16_t len);
|
||||
|
||||
#define PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* ota_srv.c 隔离单测 (gcc, 无 WCH 环境)
|
||||
*
|
||||
* 策略: mock CONFIG.h/cmcng.h + mock 外设 (NOR flash 模拟 / UART2 发送记录 /
|
||||
* 可推进时钟 / offlog 记录) → #include 真实 ../APP/ota_srv.c (同编译单元,
|
||||
* static 函数可见) → 断言会话状态机 + 刷写协议帧 + 超时重试。
|
||||
*
|
||||
* 编译:
|
||||
* gcc -I tests/ota_mock -I APP/include -I APP -o /tmp/test_ota_srv \
|
||||
* tests/test_ota_srv.c
|
||||
* 运行: /tmp/test_ota_srv
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "cmcng.h" /* mock: Flag_Counter / PRINT / mstick / UART2_SendString */
|
||||
|
||||
/* ============================================================================
|
||||
* mock 外设
|
||||
* ============================================================================*/
|
||||
Flag_Counter g_flag_counter_key = {0, 0, 0};
|
||||
Flag_Counter g_flag_counter_ota = {0, 0, 0};
|
||||
|
||||
static uint32_t _mock_ms = 0;
|
||||
uint32_t mstick(void) { return _mock_ms; }
|
||||
static void mock_advance(uint32_t ms) { _mock_ms += ms; }
|
||||
|
||||
/* UART2 发送记录: 保存最近一帧 */
|
||||
static uint8_t _tx_buf[512];
|
||||
static uint16_t _tx_len = 0;
|
||||
static uint16_t _tx_count = 0;
|
||||
void UART2_SendString(uint8_t *buf, uint16_t len)
|
||||
{
|
||||
_tx_len = (len <= sizeof(_tx_buf)) ? len : (uint16_t)sizeof(_tx_buf);
|
||||
memcpy(_tx_buf, buf, _tx_len);
|
||||
_tx_count++;
|
||||
}
|
||||
static void tx_reset(void) { _tx_len = 0; _tx_count = 0; }
|
||||
|
||||
/* ---- mock NOR flash (W25Q32 语义: 写=AND, 擦=0xFF) ---- */
|
||||
#define MOCK_FLASH_SIZE (4 * 1024 * 1024)
|
||||
static uint8_t flash[MOCK_FLASH_SIZE];
|
||||
static void flash_init(void) { memset(flash, 0xFF, sizeof(flash)); }
|
||||
|
||||
void SPI_Flash_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t size)
|
||||
{
|
||||
assert(ReadAddr + size <= MOCK_FLASH_SIZE);
|
||||
memcpy(pBuffer, &flash[ReadAddr], size);
|
||||
}
|
||||
void SPI_Flash_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t size)
|
||||
{
|
||||
uint16_t i;
|
||||
assert(WriteAddr + size <= MOCK_FLASH_SIZE);
|
||||
for (i = 0; i < size; i++) flash[WriteAddr + i] &= pBuffer[i];
|
||||
}
|
||||
void SPI_Flash_Erase_Sector(uint32_t Dst_Addr)
|
||||
{
|
||||
assert(Dst_Addr * 4096UL + 4096UL <= MOCK_FLASH_SIZE);
|
||||
memset(&flash[Dst_Addr * 4096UL], 0xFF, 4096);
|
||||
}
|
||||
/* 与 storage.c 语义一致: 目标区非 0xFF → 擦扇区 → 读-改-写保留同扇区其他数据 */
|
||||
void SPI_Flash_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t size)
|
||||
{
|
||||
uint32_t secpos, secoff, secremain;
|
||||
uint16_t i;
|
||||
while (size > 0) {
|
||||
secpos = WriteAddr / 4096;
|
||||
secoff = WriteAddr % 4096;
|
||||
secremain = 4096 - secoff;
|
||||
if (size <= secremain) secremain = size;
|
||||
/* 目标区是否全 0xFF */
|
||||
{
|
||||
uint8_t need_erase = 0;
|
||||
for (i = 0; i < secremain; i++) {
|
||||
if (flash[secpos * 4096 + secoff + i] != 0xFF) { need_erase = 1; break; }
|
||||
}
|
||||
if (need_erase) {
|
||||
uint8_t sector[4096];
|
||||
memcpy(sector, &flash[secpos * 4096], 4096);
|
||||
for (i = 0; i < secremain; i++) sector[secoff + i] = pBuffer[i];
|
||||
memset(&flash[secpos * 4096], 0xFF, 4096);
|
||||
for (i = 0; i < 4096; i++) flash[secpos * 4096 + i] &= sector[i];
|
||||
} else {
|
||||
for (i = 0; i < secremain; i++) flash[WriteAddr + i] &= pBuffer[i];
|
||||
}
|
||||
}
|
||||
pBuffer += secremain;
|
||||
WriteAddr += (uint32_t)secremain;
|
||||
size -= (uint16_t)secremain;
|
||||
}
|
||||
}
|
||||
uint8_t SPI_Flash_ReadJEDEC_ID(void) { return 0x16; } /* W25Q32 */
|
||||
|
||||
/* ---- mock offlog (记录最近 OTA 事件) ---- */
|
||||
static uint32_t _ota_start_cnt = 0, _ota_result_cnt = 0;
|
||||
static uint32_t _last_ota_result = 0;
|
||||
void offlog_evt(uint8_t type, const uint8_t *payload, uint8_t len) { (void)type; (void)payload; (void)len; }
|
||||
void offlog_ota_start(uint8_t slot, uint32_t size) { _ota_start_cnt++; (void)slot; (void)size; }
|
||||
void offlog_ota_result(uint32_t code) { _ota_result_cnt++; _last_ota_result = code; }
|
||||
void offlog_boot(uint32_t r) { (void)r; }
|
||||
|
||||
/* ---- mock loop_uart_proto ---- */
|
||||
void lup_frame_reset(void) { }
|
||||
|
||||
/* ---- mock iot_mqtt_srv ---- */
|
||||
static uint8_t _mock_any_car = 0;
|
||||
static uint32_t _ota_err_report = 0, _ota_err_cnt = 0;
|
||||
uint8_t iot_any_car(void) { return _mock_any_car; }
|
||||
void iot_evt_report_ota_error(uint32_t err) { _ota_err_report = err; _ota_err_cnt++; }
|
||||
|
||||
/* ============================================================================
|
||||
* 嵌入真实 ota_srv.c (同一编译单元, static 可见)
|
||||
* ============================================================================*/
|
||||
#include "../APP/ota_srv.c"
|
||||
|
||||
/* ============================================================================
|
||||
* 测试
|
||||
* ============================================================================*/
|
||||
#define CHK(cond) do { if (!(cond)) { \
|
||||
printf("FAIL %s:%d: %s\n", __FILE__, __LINE__, #cond); return 1; } } while (0)
|
||||
|
||||
/* 构造测试镜像 (5120B = 20 片) */
|
||||
static void make_image(uint8_t *buf, uint32_t size)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < size; i++) buf[i] = (uint8_t)(i * 7 + 3);
|
||||
}
|
||||
|
||||
static int test_crc32(void)
|
||||
{
|
||||
/* CRC-32/ISO-HDLC 标准向量 */
|
||||
CHK(ota_crc32((const uint8_t *)"123456789", 9) == 0xCBF43926UL);
|
||||
CHK(ota_crc32(NULL, 0) == 0x00000000UL);
|
||||
printf("PASS test_crc32\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_hex_decode(void)
|
||||
{
|
||||
uint8_t out[4];
|
||||
CHK(ota_hex_decode("0102aBff", out, 4) == 0);
|
||||
CHK(out[0] == 0x01 && out[1] == 0x02 && out[2] == 0xAB && out[3] == 0xFF);
|
||||
CHK(ota_hex_decode("01z2", out, 2) != 0); /* 非法字符 */
|
||||
CHK(ota_hex_decode("010", out, 2) != 0); /* 长度不足 */
|
||||
printf("PASS test_hex_decode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_meta_restore(void)
|
||||
{
|
||||
flash_init();
|
||||
tx_reset();
|
||||
ota_init();
|
||||
CHK(_meta.state == OTA_STATE_IDLE);
|
||||
/* 写一份 downloading 元数据 → 重新 init → 恢复续传状态 */
|
||||
_meta.magic = OTA_MAGIC;
|
||||
_meta.state = OTA_STATE_DOWNLOADING;
|
||||
_meta.size = 512;
|
||||
_meta.received = 256;
|
||||
ota_meta_write(&_meta);
|
||||
ota_init();
|
||||
CHK(_meta.state == OTA_STATE_DOWNLOADING);
|
||||
CHK(_meta.received == 256);
|
||||
printf("PASS test_meta_restore\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_session_flow(void)
|
||||
{
|
||||
uint8_t img[5120];
|
||||
uint32_t i;
|
||||
make_image(img, sizeof(img));
|
||||
flash_init();
|
||||
tx_reset();
|
||||
ota_init();
|
||||
|
||||
/* begin */
|
||||
CHK(ota_cmd_begin(sizeof(img), ota_crc32(img, sizeof(img)), "1.1.0", 0) == 0);
|
||||
CHK(_meta.state == OTA_STATE_DOWNLOADING);
|
||||
CHK(_meta.received == 0);
|
||||
|
||||
/* 20 片下发 */
|
||||
for (i = 0; i < sizeof(img); i += 256) {
|
||||
uint32_t clen = (sizeof(img) - i > 256) ? 256 : (sizeof(img) - i);
|
||||
uint8_t chunk[256];
|
||||
char hex[513];
|
||||
uint32_t j;
|
||||
memcpy(chunk, &img[i], clen);
|
||||
for (j = 0; j < clen; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(i, ota_crc32(chunk, clen), hex) == 0);
|
||||
}
|
||||
CHK(_meta.received == sizeof(img));
|
||||
|
||||
/* end → ready */
|
||||
CHK(ota_cmd_end(ota_crc32(img, sizeof(img))) == 0);
|
||||
CHK(_meta.state == OTA_STATE_READY);
|
||||
|
||||
/* 暂存区与镜像一致 (读回比对) */
|
||||
{
|
||||
uint8_t back[5120];
|
||||
SPI_Flash_Read(back, OTA_SLOT_A_BASE, sizeof(back));
|
||||
CHK(memcmp(back, img, sizeof(img)) == 0);
|
||||
}
|
||||
printf("PASS test_session_flow\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_begin_resume(void)
|
||||
{
|
||||
uint8_t img[5120];
|
||||
uint32_t i;
|
||||
make_image(img, sizeof(img));
|
||||
flash_init();
|
||||
ota_init();
|
||||
|
||||
/* 下载 16 片 (4096B, 触发元数据节流 flush) 后"断电重启" */
|
||||
CHK(ota_cmd_begin(sizeof(img), ota_crc32(img, sizeof(img)), "", 0) == 0);
|
||||
for (i = 0; i < 16 * 256; i += 256) {
|
||||
uint8_t chunk[256];
|
||||
char hex[513];
|
||||
uint32_t j;
|
||||
memcpy(chunk, &img[i], 256);
|
||||
for (j = 0; j < 256; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(i, ota_crc32(chunk, 256), hex) == 0);
|
||||
}
|
||||
CHK(_meta.received == 16 * 256); /* 4096B, 已 flush */
|
||||
|
||||
/* 重启 */
|
||||
ota_init();
|
||||
CHK(_meta.state == OTA_STATE_DOWNLOADING);
|
||||
CHK(_meta.received == 16 * 256); /* 节流点精确续传 */
|
||||
|
||||
/* 重新 begin → 续传点 */
|
||||
CHK(ota_cmd_begin(sizeof(img), ota_crc32(img, sizeof(img)), "", 0) == 0);
|
||||
CHK(_meta.received == 16 * 256);
|
||||
|
||||
/* 继续下载剩余 → ready */
|
||||
for (i = 16 * 256; i < sizeof(img); i += 256) {
|
||||
uint32_t clen = (sizeof(img) - i > 256) ? 256 : (sizeof(img) - i);
|
||||
uint8_t chunk[256];
|
||||
char hex[513];
|
||||
uint32_t j;
|
||||
memcpy(chunk, &img[i], clen);
|
||||
for (j = 0; j < clen; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(i, ota_crc32(chunk, clen), hex) == 0);
|
||||
}
|
||||
CHK(ota_cmd_end(ota_crc32(img, sizeof(img))) == 0);
|
||||
CHK(_meta.state == OTA_STATE_READY);
|
||||
printf("PASS test_begin_resume\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_dup_and_gap(void)
|
||||
{
|
||||
uint8_t img[1024];
|
||||
uint8_t chunk[256];
|
||||
char hex[513];
|
||||
uint32_t j;
|
||||
make_image(img, sizeof(img));
|
||||
flash_init();
|
||||
ota_init();
|
||||
CHK(ota_cmd_begin(sizeof(img), ota_crc32(img, sizeof(img)), "", 0) == 0);
|
||||
|
||||
memcpy(chunk, &img[0], 256);
|
||||
for (j = 0; j < 256; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(0, ota_crc32(chunk, 256), hex) == 0);
|
||||
|
||||
/* 重复片幂等 */
|
||||
CHK(ota_cmd_data(0, ota_crc32(chunk, 256), hex) == 0);
|
||||
CHK(_meta.received == 256);
|
||||
|
||||
/* 乱序缺片 (跳 256 直接发 512) → 拒绝 */
|
||||
memcpy(chunk, &img[512], 256);
|
||||
for (j = 0; j < 256; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(512, ota_crc32(chunk, 256), hex) == 1);
|
||||
CHK(_meta.received == 256);
|
||||
|
||||
/* 单片 CRC 错 → 拒绝 */
|
||||
memcpy(chunk, &img[256], 256);
|
||||
for (j = 0; j < 256; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(256, 0xDEADBEEFUL, hex) == 1);
|
||||
CHK(_meta.received == 256);
|
||||
printf("PASS test_dup_and_gap\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 模拟 bootloader: 根据收到的 A7 块回 data ACK */
|
||||
static void flash_step_until_ack(uint32_t *blocks_done, uint32_t total_blocks)
|
||||
{
|
||||
/* ota_poll 推进一帧发送 + 喂 ACK, 直到收到末块 */
|
||||
uint32_t guard = 0;
|
||||
while (*blocks_done < total_blocks && guard++ < 10000) {
|
||||
ota_poll(); /* 发 A7 块 */
|
||||
CHK(_tx_len >= 7 && _tx_buf[0] == 0x9F);
|
||||
CHK(_tx_buf[4] == 0xA7);
|
||||
/* 构造 data ACK: 9F SubL SubH 02 A7 00 CHECK */
|
||||
{
|
||||
uint8_t ack[7];
|
||||
uint8_t sum = 0;
|
||||
ack[0] = 0x9F; ack[1] = _tx_buf[1]; ack[2] = _tx_buf[2];
|
||||
ack[3] = 0x02; ack[4] = 0xA7; ack[5] = 0x00;
|
||||
sum = (uint8_t)(ack[1] + ack[2] + ack[3] + ack[4] + ack[5]);
|
||||
ack[6] = sum;
|
||||
ota_flash_feed_ack(ack, 7);
|
||||
}
|
||||
(*blocks_done)++;
|
||||
}
|
||||
CHK(*blocks_done == total_blocks);
|
||||
}
|
||||
|
||||
static int test_flash_flow(void)
|
||||
{
|
||||
uint8_t img[5120];
|
||||
make_image(img, sizeof(img));
|
||||
flash_init();
|
||||
tx_reset();
|
||||
_ota_err_cnt = 0;
|
||||
ota_init();
|
||||
|
||||
/* 下载 → ready */
|
||||
CHK(ota_cmd_begin(sizeof(img), ota_crc32(img, sizeof(img)), "", 0) == 0);
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; i < sizeof(img); i += 256) {
|
||||
uint32_t clen = (sizeof(img) - i > 256) ? 256 : (sizeof(img) - i);
|
||||
uint8_t chunk[256];
|
||||
char hex[513];
|
||||
uint32_t j;
|
||||
memcpy(chunk, &img[i], clen);
|
||||
for (j = 0; j < clen; j++) sprintf(hex + j * 2, "%02x", chunk[j]);
|
||||
CHK(ota_cmd_data(i, ota_crc32(chunk, clen), hex) == 0);
|
||||
}
|
||||
}
|
||||
CHK(ota_cmd_end(ota_crc32(img, sizeof(img))) == 0);
|
||||
CHK(_meta.state == OTA_STATE_READY);
|
||||
|
||||
/* flash: 无车 → 启动 */
|
||||
_mock_any_car = 0;
|
||||
tx_reset();
|
||||
CHK(ota_cmd_flash(0, 0) == 0);
|
||||
CHK(_meta.state == OTA_STATE_FLASHING);
|
||||
CHK(g_ota_flash_active == 1);
|
||||
CHK(g_flag_counter_ota.flag == 1);
|
||||
CHK(_ota_start_cnt >= 1); /* offlog 升级开始已写 */
|
||||
|
||||
/* START → 发 A5 启动帧 */
|
||||
ota_poll();
|
||||
CHK(_tx_len == 6 && _tx_buf[0] == 0x9F && _tx_buf[4] == 0xA5 && _tx_buf[5] == 0xA7);
|
||||
|
||||
/* pre_ok → ADDR */
|
||||
{
|
||||
uint8_t pre_ok[7] = {0x9F, 0x01, 0x00, 0x02, 0xA5, 0x00, 0xA8};
|
||||
ota_flash_feed_ack(pre_ok, 7);
|
||||
}
|
||||
ota_poll();
|
||||
/* A6 地址帧: 9F 01 00 05 A6 08 00 34 00 CHECK */
|
||||
CHK(_tx_len == 10 && _tx_buf[4] == 0xA6);
|
||||
CHK(_tx_buf[5] == 0x08 && _tx_buf[6] == 0x00 && _tx_buf[7] == 0x34 && _tx_buf[8] == 0x00);
|
||||
/* 校验和: SubL+SubH+LEN+CMD+ADDR = 01+00+05+A6+08+00+34+00 */
|
||||
CHK(_tx_buf[9] == (uint8_t)(0x01 + 0x00 + 0x05 + 0xA6 + 0x08 + 0x00 + 0x34 + 0x00));
|
||||
|
||||
/* addr_ok → DATA 阶段 (总块数 = ceil(5120/248) = 21) */
|
||||
{
|
||||
uint8_t addr_ok[7] = {0x9F, 0x01, 0x00, 0x02, 0xA6, 0x00, 0xA9};
|
||||
ota_flash_feed_ack(addr_ok, 7);
|
||||
}
|
||||
CHK(_fs_sub == 21); /* 首块序号 = 总块数 */
|
||||
|
||||
/* 逐块 A7 + ACK 直到完成 */
|
||||
flash_step_until_ack(&(uint32_t){0}, 21);
|
||||
|
||||
/* 完成后: 恢复 0x7F 模式, 元数据 ready (镜像保留可重刷), 结果已记 */
|
||||
CHK(_meta.state == OTA_STATE_READY);
|
||||
CHK(g_ota_flash_active == 0);
|
||||
CHK(g_flag_counter_ota.flag == 0);
|
||||
CHK(_ota_result_cnt >= 1);
|
||||
CHK(_last_ota_result == 0);
|
||||
CHK(_ota_err_cnt == 0); /* 无失败告警 */
|
||||
printf("PASS test_flash_flow (21 blocks)\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_flash_reject_car(void)
|
||||
{
|
||||
flash_init();
|
||||
ota_init();
|
||||
/* 直接构造 ready 态 */
|
||||
_meta.state = OTA_STATE_READY;
|
||||
_meta.size = 512;
|
||||
_mock_any_car = 1;
|
||||
CHK(ota_cmd_flash(0, 0) == 3); /* 有车 → 拒绝 */
|
||||
CHK(g_ota_flash_active == 0);
|
||||
_mock_any_car = 1;
|
||||
CHK(ota_cmd_flash(0, 1) == 0); /* force → 跳过 */
|
||||
CHK(g_ota_flash_active == 1);
|
||||
printf("PASS test_flash_reject_car\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_flash_ready_timeout(void)
|
||||
{
|
||||
flash_init();
|
||||
tx_reset();
|
||||
_ota_err_cnt = 0;
|
||||
ota_init();
|
||||
_meta.state = OTA_STATE_READY;
|
||||
_meta.size = 512;
|
||||
_mock_any_car = 0;
|
||||
|
||||
CHK(ota_cmd_flash(0, 0) == 0);
|
||||
ota_poll(); /* 发 A5 (首发) */
|
||||
/* 不喂 ACK: 首发后 3 次超时重发 + 第 4 个超时周期 → 失败
|
||||
轮次: 超时1(→START) A5(2) 超时2(→START) A5(3) 超时3(→START) A5(4) 超时4(→fail) */
|
||||
{
|
||||
int k;
|
||||
for (k = 0; k < 7; k++) { mock_advance(1001); ota_poll(); }
|
||||
}
|
||||
CHK(_meta.state == OTA_STATE_FLASH_FAILED);
|
||||
CHK(_meta.last_result == OTA_ERR_READY_TIMEOUT);
|
||||
CHK(_ota_err_cnt == 1); /* event_report ota_error 告警 */
|
||||
CHK(_ota_err_report == OTA_ERR_READY_TIMEOUT);
|
||||
CHK(g_ota_flash_active == 0);
|
||||
CHK(g_flag_counter_ota.flag == 0);
|
||||
CHK(_ota_result_cnt >= 1);
|
||||
CHK(_last_ota_result == OTA_ERR_READY_TIMEOUT);
|
||||
printf("PASS test_flash_ready_timeout\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails = 0;
|
||||
fails += test_crc32();
|
||||
fails += test_hex_decode();
|
||||
fails += test_meta_restore();
|
||||
fails += test_session_flow();
|
||||
fails += test_begin_resume();
|
||||
fails += test_dup_and_gap();
|
||||
fails += test_flash_flow();
|
||||
fails += test_flash_reject_car();
|
||||
fails += test_flash_ready_timeout();
|
||||
if (fails == 0) {
|
||||
printf("\nALL PASS\n");
|
||||
return 0;
|
||||
}
|
||||
printf("\n%d TEST(S) FAILED\n", fails);
|
||||
return 1;
|
||||
}
|
||||
Reference in New Issue
Block a user