fix(vd960DBN): OTA 刷写成功误判未启动 — 状态回 idle + 补 ota_report 主动上报
现场: 远程平台发起 OTA, 设备物理刷写成功 (70块全ACK, flash DONE), 但平台 ota_status 轮询只见 state=ready → 误判'刷写未启动'。 根因: 1. ota_flash_done 刷写成功后台侧状态置 READY (与协议状态机 'FLASHING──成功──▶IDLE(清槽/保留)' 不符), 平台无法区分 '待刷 ready' 与 '已刷完' → 持续 ready 判未启动 2. 固件漏实现协议 §5.5 ota_report 主动上报 (done/failed 重发 3次×5s), 平台收不到成功信号, 只能靠 ota_status 轮询兜底 修复 (协议 V1.09 + 固件): - 协议: 刷写成功状态明确回 idle (镜像保留, last_result=0, 可重刷); ota_report 升级为刷写结果主依据; 平台判定指引 (idle+size>0+last_result=0 =成功; 不得以轮询未见 flashing 或持续 ready 判未启动) - ota_srv.c: ota_flash_done → OTA_STATE_IDLE (镜像保留) + 上报 done; ota_flash_fail → 补 ota_report failed (保留 event_report 告警); ota_cmd_begin 兼容 idle+size/crc32 一致 → 直接回 ready (免下载重刷) - iot_mqtt_srv.c: 新增 ota_report 上报状态机 (立即首发 + 5s×3 重发, 同 msg_id/ts), 共享 _iot_pub_payload (event_report 复用, RAM 零新增) - 单测: test_flash_flow/test_flash_ready_timeout 断言 ota_report 上报, state 断言 READY→IDLE; 新增 test_begin_reflash; 10/10 全过 + 22 py 断言
This commit is contained in:
@@ -1004,6 +1004,10 @@ dld960/{dev_serial}/{direction}
|
||||
- 从 W25Qxx 暂存读镜像,按 ≤254B/块发 A7(**非阻塞 tick 驱动**:每轮主循环发送 1~2 块并检查 ACK,绝不阻塞主循环,保证刷写窗口内 MQTT PINGREQ/心跳/IWDG 喂狗正常);停等 ACK,1s 超时重发 ×3
|
||||
- 末块(sub_amount=1)→ bootloader 写剩余 → 清 flag → 复位跑新 APP
|
||||
4. 进度经 `ota_report` 上行(§5.5);失败重试 ×3 仍失败 → 元数据 `state=flash_failed` + `event_report{type:ota_error}` 告警(平台必答)
|
||||
5. **刷写结果(设备侧,V1.09 明确)**:
|
||||
- 成功(末块 ACK)→ 元数据 `state=idle`(**镜像保留**:`size`/`crc32`/`version`/`slot` 不变,`last_result=0`,`flash_cnt+1`)→ 立即上报 `ota_report{stage:done}`(重发 3 次×5s,同 `msg_id`/`ts`)→ 恢复 `event_report` 发送与 offlog/快照落盘
|
||||
- 失败 → 元数据 `state=flash_failed` + `ota_report{stage:failed}` + `event_report{type:ota_error}`(done/failed 同重发策略)
|
||||
- **状态语义**:刷写完成后 DBN 侧状态回 `idle`("本轮刷写已结束"),与"下载完成待刷(`ready`)"严格区分——**平台不得把 `state=ready` 判为"刷写未启动"**:本地刷写 70 块(17224B)实测 <1s,平台轮询间隔可能错过 `flashing` 中间态
|
||||
|
||||
**响应:** `code=0` 仅表示已启动,不代表刷写成功——结果以 `ota_report` / `ota_status` 为准。
|
||||
|
||||
@@ -1038,7 +1042,7 @@ dld960/{dev_serial}/{direction}
|
||||
|
||||
| data 字段 | 类型 | 说明 |
|
||||
|-----------|------|------|
|
||||
| `state` | string | `idle` / `downloading` / `ready` / `flashing` / `flash_failed` / `aborted` |
|
||||
| `state` | string | `idle` / `downloading` / `ready` / `flashing` / `flash_failed` / `aborted`;**`idle` 且 `size>0` = 已刷写完成(镜像保留可重刷)** |
|
||||
| `progress.sent` | uint32 | 刷写阶段已送 Loop 的字节数 |
|
||||
| `last_result` | uint32 | 上次刷写结果:0=无/成功,非 0=错误码 |
|
||||
| `last_error` | uint32 | 上次失败细分错误码 |
|
||||
@@ -1051,11 +1055,16 @@ dld960/{dev_serial}/{direction}
|
||||
▲ │ ▲ │
|
||||
│ ota_abort │ │ ota_end(CRC✗) │ ota_flash(安全检查✓)
|
||||
│ / flash_failed │ └──────────────┐ ▼
|
||||
└────────────────────────┴─────────────────┴───── FLASHING ──成功──▶ (Loop 重启) ──▶ IDLE(清槽/保留)
|
||||
└────────────────────────┴─────────────────┴───── FLASHING ──成功──▶ IDLE(镜像保留, last_result=0)
|
||||
│
|
||||
└──失败×3──▶ FLASH_FAILED ──ota_abort/ota_begin──▶ IDLE
|
||||
```
|
||||
|
||||
**平台判定指引(V1.09):**
|
||||
- **主依据**:`ota_report` 主动上报(`stage=done` 成功 / `stage=failed` 失败,§5.5)——`ota_flash` 后平台应优先等该信号
|
||||
- **兜底**:`ota_status` 查询——`state=idle 且 size>0 且 last_result=0` = 刷写成功(镜像保留,可重刷);`state=flash_failed` = 失败;`state=ready` = 下载完成待刷(**不是**"刷写未启动")
|
||||
- 刷写窗口极短(<1s),轮询可能捕捉不到 `flashing` 中间态,不得以此判失败
|
||||
|
||||
---
|
||||
|
||||
# 5 设备主动上报
|
||||
@@ -1332,6 +1341,8 @@ dld960/{dev_serial}/{direction}
|
||||
|
||||
**上报节奏**:`begin`/`ready`/`done`/`failed` 各 1 次;`flashing` 阶段按进度节流(建议每 64 块或每 8KB 一次,避免刷写期间消息风暴)。`done`/`failed` 设备侧重发 3 次(间隔 5s,同 `msg_id`/`ts`),平台去重窗口建议 10 分钟(与 `event_report` 同策略)。
|
||||
|
||||
**平台判定(V1.09 明确)**:`ota_report` 是刷写结果的**主依据**——收到 `stage=done` 判成功、`stage=failed` 判失败,无需再轮询 `ota_status`。设备 `ota_flash` 响应 `code=0` 仅表示已启动(异步),**不得以"轮询 `ota_status` 未见 `flashing`"或"状态持续为 `ready`"判"刷写未启动"**——本地刷写 <1s 完成,轮询大概率错过中间态;兜底判定见 §4.24。
|
||||
|
||||
**会话期间静默**(V1.08):OTA 会话期间(`ota_begin` ~ 结束)设备暂停 `event_report` 发送(入队积压,结束后补发)与 offlog/快照落盘("升级开始"日志暂停前写入、"升级结果"恢复后补记),保证刷写窗口内 MQTT 保活与 IWDG 喂狗不受影响。
|
||||
|
||||
---
|
||||
@@ -1348,5 +1359,6 @@ dld960/{dev_serial}/{direction}
|
||||
| V1.05 | 2026-07-15 | 增加**设备时钟同步**(§2.3,方案B):设备无 RTC,`initialize` 上线后平台经 `report_config` 命令下发 Unix `ts`,设备据此校准,之后上行 `ts` 为真实 Unix 时间;校准前为上电秒数 | wangfq |
|
||||
| V1.06 | 2026-08-04 | 增加**脱机事件日志**命令:`log_stat`(统计/分页定位)、`log_query`(按全局序号分页,count≤4)、`log_clear`(清除+审计留痕);§2.3 补充日志双时间戳语义(`ts_ms` 相对 + `unix_ts` 已同步,0=未同步,锚点回算规则) | wangfq |
|
||||
| V1.07 | 2026-08-18 | `log_stat` / `log_query` / `log_clear` 增加**快照流**支持(`stream=snapshot`,与 BLE 0x28/0x29/0x2A 同语义):快照统计 capacity 随芯片动态(48064~449472)、快照分页 count≤1(4 通道记录 JSON ~810B 超发送缓冲,实测修正;BLE 原始通道仍 ≤2)、快照清除审计留痕;`capacity`/`count` 类型修正为 uint32(W25Q256 事件流 130944 超 16bit) | wangfq |
|
||||
| V1.09 | 2026-08-20 | **OTA 刷写结果判定修复**(现场:刷写物理成功但平台误判"刷写未启动"):① 刷写成功后台侧状态回 `idle`(镜像保留:`size`/`crc32`/`version` 不变,`last_result=0`,可重刷),与"下载完成待刷 `ready`"严格区分;② `ota_report` 升级为刷写结果**主依据**(`stage=done`/`failed` 设备必报,重发 3 次×5s),`ota_status` 仅兜底;③ 明确平台判定指引——`idle+size>0+last_result=0`=成功,不得以轮询未见 `flashing` 或状态持续 `ready` 判"刷写未启动"(本地刷写 <1s,轮询大概率错过中间态);④ `ota_begin` 兼容 `idle+size/crc32 一致` → 免下载直接可刷(重刷) | wangfq |
|
||||
| V1.08 | 2026-08-20 | 增加 **Loop MCU 远程 OTA**(ROADMAP P1.4 ①,先存后刷):命令 `ota_begin` / `ota_data` / `ota_end` / `ota_abort` / `ota_flash` / `ota_status`(srv→dev)+ `ota_report`(dev→srv);单片 256B + 单片/全镜像 CRC32(ISO-HDLC,§4.19.1);断点续传(`ota_begin` 返回 offset);Slot A/B 双槽回滚(镜像 ≤96KB);`ota_flash` 安全窗口检查 + 非阻塞 tick 驱动刷写(刷写窗口内 MQTT 保活/IWDG 不受影响);会话期间暂停 `event_report` 发送与脱机日志落盘;`event_report` 扩展 `type=ota_error` 失败告警;§2.2 补 OTA 细分错误码(err_code);老固件兼容(`ota_*` 回 code=4) | wangfq |
|
||||
|
||||
|
||||
@@ -324,6 +324,11 @@ typedef struct { /* 64B */
|
||||
|
||||
**响应:** 标准成功/失败(`code=0` 仅表示已启动,不代表刷写成功——结果以 `ota_report`/`ota_status` 为准)。
|
||||
|
||||
**刷写结果(V1.09 明确):**
|
||||
- 成功(末块 ACK)→ 元数据 `state=idle`(**镜像保留**:`size`/`crc32`/`version`/`slot` 不变,`last_result=0`,`flash_cnt+1`)→ 上报 `ota_report{stage:done}`(重发 3 次×5s)→ 恢复 event_report/offlog 落盘
|
||||
- 失败 → `state=flash_failed` + `ota_report{stage:failed}` + `event_report{type:ota_error}`
|
||||
- **状态语义**:刷写完成后回 `idle`("本轮刷写已结束"),与"下载完成待刷 `ready`"严格区分——平台**不得把 `state=ready` 判为"刷写未启动"**:本地刷写 <1s,轮询大概率错过 `flashing` 中间态;平台判定以 `ota_report done/failed` 为主依据,`ota_status` 兜底(`idle+size>0+last_result=0`=成功)
|
||||
|
||||
## 5.6 查询状态 `ota_status`
|
||||
|
||||
> Topic: `dld960/{sn}/srv`
|
||||
@@ -366,11 +371,13 @@ typedef struct { /* 64B */
|
||||
▲ │ ▲ │
|
||||
│ ota_abort │ │ ota_end(CRC✗) │ ota_flash(安全检查✓)
|
||||
│ / flash_failed │ └──────────────┐ ▼
|
||||
└────────────────────────┴─────────────────┴───── FLASHING ──成功──▶ (Loop 重启) ──▶ IDLE(清槽/保留)
|
||||
└────────────────────────┴─────────────────┴───── FLASHING ──成功──▶ IDLE(镜像保留, last_result=0)
|
||||
│
|
||||
└──失败×3──▶ FLASH_FAILED ──ota_abort/ota_begin──▶ IDLE
|
||||
```
|
||||
|
||||
**平台判定(V1.09)**:刷写结果以 `ota_report`(`stage=done`/`failed`)为主依据;`ota_status` 兜底——`state=idle 且 size>0 且 last_result=0` = 成功(镜像保留可重刷);`state=ready` = 待刷(**不是**"刷写未启动");轮询可能错过 `flashing` 中间态,不得以此判失败。
|
||||
|
||||
---
|
||||
|
||||
# 6 主动上报 `ota_report`
|
||||
@@ -403,6 +410,8 @@ typedef struct { /* 64B */
|
||||
|
||||
**上报节奏**:`begin`/`ready`/`done`/`failed` 各 1 次;`flashing` 阶段按块进度节流(建议每 64 块或每 8KB 一次,避免刷写期间消息风暴)。`done`/`failed` 设备侧重发 3 次(间隔 5s,同 msg_id/ts),平台去重窗口建议 10 分钟(与 event_report 同策略)。
|
||||
|
||||
**平台判定(V1.09 明确)**:`ota_report` 是刷写结果**主依据**——`stage=done` 判成功、`stage=failed` 判失败,无需轮询 `ota_status`。`ota_flash` 响应 `code=0` 仅表示已启动(异步),**不得以"轮询未见 `flashing`"或"状态持续 `ready`"判"刷写未启动"**(本地刷写 <1s,轮询大概率错过中间态);兜底判定见 §5.6。
|
||||
|
||||
## 6.1 失败告警(扩展 event_report)
|
||||
|
||||
刷写整体失败(重试 ×3 仍失败)时,设备经 `event_report` 上报(**平台必答**,复用 §5.3 ACK + 重发机制):
|
||||
|
||||
@@ -106,7 +106,7 @@ DLD960 是一款基于环形线圈(LC 振荡)检测原理的四通道车辆
|
||||
|------|------|------|------|
|
||||
| DLD960 串口通信协议 | V1.01 | TTL | 设备管理、参数配置、数据上报 |
|
||||
| DLD960 TCP JSON 协议 | V1.03 | ETH :5960 | 密码鉴权 + 18 条命令;event_report 客户端必答(5s×3 重发);脱机日志 log_stat/log_query/log_clear(事件/快照流,stream 区分) |
|
||||
| DLD960 IoT MQTT 协议 | V1.08 | ETH → Broker | 双主题 `dld960/{sn}/srv`+`/dev`;initialize 上线、loop_data 三档调度、event_report 平台必答、设备时钟同步、脱机日志 log_stat/log_query/log_clear(事件/快照流,stream 区分);Loop 远程 OTA(ota_begin/ota_data/ota_end/ota_abort/ota_flash/ota_status + ota_report,256B/片 CRC32,先存后刷断点续传,Slot A/B 双槽回滚) |
|
||||
| DLD960 IoT MQTT 协议 | V1.09 | ETH → Broker | 双主题 `dld960/{sn}/srv`+`/dev`;initialize 上线、loop_data 三档调度、event_report 平台必答、设备时钟同步、脱机日志 log_stat/log_query/log_clear(事件/快照流,stream 区分);Loop 远程 OTA(ota_begin/ota_data/ota_end/ota_abort/ota_flash/ota_status + ota_report,256B/片 CRC32,先存后刷断点续传,Slot A/B 双槽回滚) |
|
||||
| DLD960Loop 串口协议 | V1.05 | MCU 间(内部) | 0x7F 帧、0xC0 传感上报(variation 3B 有符号) |
|
||||
| DLD960 BLE 协议 | V1.02 | 蓝牙 BLE | 帧格式 + 分包;脱机日志 OFFLOG_STAT/QUERY/CLEAR + 传感快照 SNAP_STAT/QUERY/CLEAR(0x28/0x29/0x2A),与 MQTT/TCP 同语义 |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user