feat(DBNMQTTool): OTA 页签 + 协议单测 (MQTT V1.08 工具先行)
- protocol.py: OTA 模块 — 命令常量/ota_crc32(ISO-HDLC)/ota_split_bin(256B×96KB)/ data_ota_* 构建器/状态·阶段·错误码描述 - main.py: OTA 页签 — bin 选择(≤96KB+CRC32 显示)/版本·force·slot 参数/ 下载(OtaDownloadThread: begin→data×N→end, 断点续传·缺片重定位·CRC 重发)/ 刷写(二次确认)/中止/状态查询; 进度条 + ota_report/ota_status 实时显示 - tests/test_ota_protocol.py: 22 断言 — CRC32 标准向量/分片边界/构建器/ OtaDeviceSim 设备状态机(顺序·幂等·乱序·单片CRC·全镜像复核·续传)/完整下载流 - devlog 置顶条目
This commit is contained in:
@@ -9,6 +9,7 @@ DLD960 IoT MQTT 协议定义
|
||||
import json
|
||||
import time
|
||||
import struct
|
||||
import zlib
|
||||
from typing import Optional, Any
|
||||
from dataclasses import dataclass, field, asdict
|
||||
|
||||
@@ -273,6 +274,94 @@ def data_log_clear(stream: str = STREAM_EVENT) -> dict:
|
||||
return {}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# OTA 远程升级 (V1.08: Loop MCU 先存后刷, ROADMAP P1.4 ①)
|
||||
# 协议依据《DLD960_IoT_MQTT协议.md》§4.19~4.24 / §5.5
|
||||
# ============================================================
|
||||
|
||||
CMD_OTA_BEGIN = "ota_begin"
|
||||
CMD_OTA_DATA = "ota_data"
|
||||
CMD_OTA_END = "ota_end"
|
||||
CMD_OTA_ABORT = "ota_abort"
|
||||
CMD_OTA_FLASH = "ota_flash"
|
||||
CMD_OTA_STATUS = "ota_status"
|
||||
CMD_OTA_REPORT = "ota_report" # dev→srv 主动上报
|
||||
|
||||
OTA_TARGET_LOOP = "loop"
|
||||
OTA_CHUNK_SIZE = 256 # 单片 256B(协议常量, 不接受协商)
|
||||
OTA_MAX_SIZE = 96 * 1024 # 镜像上限 96KB (Slot 100KB - 4KB 边界余量)
|
||||
OTA_SLOT_A = "a"
|
||||
OTA_SLOT_B = "b"
|
||||
|
||||
OTA_STATE_DESC = {
|
||||
"idle": "空闲",
|
||||
"downloading": "下载中",
|
||||
"ready": "校验通过(可刷写)",
|
||||
"flashing": "刷写中",
|
||||
"flash_failed": "刷写失败",
|
||||
"aborted": "已中止",
|
||||
}
|
||||
|
||||
OTA_STAGE_DESC = {
|
||||
"begin": "会话开启",
|
||||
"downloading": "分片落盘",
|
||||
"ready": "校验通过",
|
||||
"flashing": "刷写中",
|
||||
"done": "刷写成功(Loop 已重启)",
|
||||
"failed": "刷写失败",
|
||||
}
|
||||
|
||||
OTA_ERROR_DESC = {
|
||||
0x1001: "启动帧无响应",
|
||||
0x1002: "地址帧错误",
|
||||
0x1003: "数据块 ACK 超限",
|
||||
0x1004: "全镜像校验失败",
|
||||
0x1005: "安全窗口拒绝后强制失败",
|
||||
}
|
||||
|
||||
|
||||
def ota_crc32(data: bytes) -> int:
|
||||
"""CRC-32/ISO-HDLC (协议 §4.19.1; zlib.crc32 即此算法, 设备查表法一致)"""
|
||||
return zlib.crc32(data) & 0xFFFFFFFF
|
||||
|
||||
|
||||
def ota_split_bin(data: bytes, chunk_size: int = OTA_CHUNK_SIZE) -> list[tuple[int, int, str]]:
|
||||
"""bin → 分片列表 [(offset, crc32, hex_str), ...] (单片 256B, hex 512 字符)"""
|
||||
if len(data) > OTA_MAX_SIZE:
|
||||
raise ValueError(f"镜像 {len(data)}B 超上限 {OTA_MAX_SIZE}B (96KB)")
|
||||
return [(off, ota_crc32(data[off:off + chunk_size]), data[off:off + chunk_size].hex())
|
||||
for off in range(0, len(data), chunk_size)]
|
||||
|
||||
|
||||
def data_ota_begin(size: int, crc32: int, version: str = "",
|
||||
target: str = OTA_TARGET_LOOP, force: bool = False) -> dict:
|
||||
"""ota_begin 请求 data (开启会话 / 断点续传定位)"""
|
||||
return {"target": target, "size": size, "crc32": crc32,
|
||||
"version": version, "force": force}
|
||||
|
||||
|
||||
def data_ota_data(offset: int, crc32: int, data_hex: str,
|
||||
target: str = OTA_TARGET_LOOP) -> dict:
|
||||
"""ota_data 请求 data (分片下发, offset 256 对齐)"""
|
||||
return {"target": target, "offset": offset, "crc32": crc32, "data": data_hex}
|
||||
|
||||
|
||||
def data_ota_end(crc32: int, target: str = OTA_TARGET_LOOP) -> dict:
|
||||
"""ota_end 请求 data (结束下载, 全镜像 CRC32 复核)"""
|
||||
return {"target": target, "crc32": crc32}
|
||||
|
||||
|
||||
def data_ota_abort(target: str = OTA_TARGET_LOOP) -> dict:
|
||||
"""ota_abort 请求 data (中止会话, 释放暂存)"""
|
||||
return {"target": target}
|
||||
|
||||
|
||||
def data_ota_flash(slot: str = OTA_SLOT_A, force: bool = False,
|
||||
target: str = OTA_TARGET_LOOP) -> dict:
|
||||
"""ota_flash 请求 data (触发本地 ISP 刷写, 仅 ready 态)"""
|
||||
return {"target": target, "slot": slot, "force": force}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 解析设备上报
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user