Files
GateWay/.trae/documents/cat1-remote-upgrade-plan.md
YuanHongbin 920bddf949 feat(cat1): 空闲保活重连/通道分流重构/远程调试命令链路, 注释编码统一UTF-8
Cat1通信稳定性:
- 新增空闲5分钟无上传自动断开重建TCP保活(只建链不上传, exe端客户端列表自动刷新)
- 注册超时不再重启模组(兼容网络调试助手等不回注册应答的对端), 连接自愈由LIPSEND失败与空闲重连兜底
- 日志流分离: 调试回应(Debug_Printf)经duchRing透传, 系统级rt_kprintf日志固定走RS485 Ch1, 消除trace/AT应答自激灌环

通道分流重构(USART1固定接Cat1):
- much/auch/duch/cuch恢复按配置分流; dbg通道选项从全部配置命令删除(ParseChannel/帮助文本/CH_DBG互斥残留)
- 下行分流规则: 0x7D升级帧第一优先(NetUpgradeFeed), 0x7A业务帧按Much/Auch回调, 其余明文直通DebugAnalyze
- Cat1升级不受分流限制: 无论Cat1配置在哪个通道均可响应0x81握手与完整升级会话
- 删除DUCH_MODE独占透传状态(duch=cat1纯调试统一走WAIT_SEND), 删除CAT1_ERR_RESET_DLY_MAX(1小时空闲重启由5分钟TCP重连替代)

远程调试命令链路:
- 服务端明文/hex命令经Cat1下行直通DebugAnalyze执行, 兼容带/不带\r\n(修复strlen<rLen误判截断URC丢弃)
- LIPURC解析增加Cat1IsHexText合法性校验(修复AsciiToHex对非hex明文静默产出乱码绕过直通分支)
- 命令回应经QISEND明文直发(网络调试助手可读, 发送前等URC流静默防吞), 升级应答帧保持LIPSEND hex(exe协议不变)

注释与编码治理:
- 全工程C/H文件统一UTF-8: CatOneATCmdStr 10处历史乱码注释还原为正确中文
- DebugCmd.c 35处函数头乱码注释(U+FFFD固化)从git 314ff67历史按函数名恢复, 5个新增函数注释按实现补写
- 修正错位/错误描述: DebugCmdTimeSync(系统时间同步)/DebugCmdRegister(重新注册)/DebugCmdCat1OnOff(调试开关)/DebugCmdSetCollectTime等4处按实际功能重写, 低功耗错别字修正
- 修复CatOneATCmdStr[16]数组17元素越界且CAT_ONE_CCLK索引错位(时间同步从未生效), 扩容[17]并归位

升级模块(承接f206b8f联调结论):
- TX队列rt_hw_interrupt_disable/enable关中断临界区(修并发越界HardFault)
- 断点续传永久取消(每次点击升级从0全新下载); 升级启动/完结均清进度记录(扇区4037)
- 暂存区按需擦除NupErasedMap(修二次升级NOR脏位损坏); 0x7D帧头MAC与本机不一致丢弃(防串台)
- AsciiToHex长度参数uint8->uint16(修432字符帧截断)
2026-10-10 14:14:16 +08:00

86 lines
7.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cat1 远程升级方案设计(待评审)
## 总体架构
App 扮演"接收端"角色(与 Boot 在 RS485 升级中的角色相同),服务器扮演升级主机:
```
服务器(升级主机) --TCP--> 合宙Cat1模组 --USART1--> App(RT-Thread)
│ ├─ 收0x7D帧(0x81/0x82) → 固件分包写外部SPI Flash暂存区
│←────────── 0x01 ACK(带进度) ──────────────┤
│ └─ 收完→CRC32校验→置UpdateFlag=APP_UPDATE_SPI_FLAG→复位
复位
└→ Boot: 检测APP_UPDATE_SPI_FLAG
→ SPI Flash逐扇区读 → 写内部B区 → CRC32(B)
→ 搬运B→A → CRC32(A) → 擦SPI Flash暂存区 → 置完成标志 → 复位跳App
```
**升级协议完全复用 RS485 的 0x7D 帧**:帧头 0x7D + DevMac(6) + Cmd(1) + PayloadLen(1) + Payload + CRC16(Modbus),命令 0x81 升级请求{PackageNum, AppSize, AppCrc32} / 0x82 数据{PackageIndex, PackageNum, DataLen, Data[200]} / 0x01 ACK / 0x02 请求数据。服务器端协议不变。
## 关键决策与理由
1. **Boot 不驱动 Cat1**:AT 注网 30 秒起、Boot 仅 32KB 分区(当前 Code 16KB)、模组 TCP 连接态在 MCU 复位后不可控。收包/重试/续传等复杂逻辑全部放 App(RTOS 资源丰富),Boot 只做"从外部 Flash 搬运到 B 区"这一简单确定性动作。
2. **固件暂存外部 SPI Flash**:16MB 尾部划 58×4KB=232KB 暂存区(扇区 4038~4095),日志环形写入回绕点同步从 16MB 末尾前移到扇区 4038。
3. **复用 UpdateFlag 机制**:新增标志值 `APP_UPDATE_SPI_FLAG`(建议 0xA5A55A5A,与现有 START=0x55AA5A5A / UPDATE=0xA5A5AA55 / BOOT_UPGRADE=0x5A5A55A5 不冲突),`boot_para_t` 结构不变,RS485 本地升级路径完全不受影响。
4. **依赖 TCP 可靠有序**:应用层丢包仅发生在断连/模组重启,因此用"断点续传 + 超时重发请求"而非滑动窗选择重传,状态机与 Boot 版本同构,服务器实现简单。
## 升级流程(含 Cat1 特化处理)
1. 服务器 → `0x81{PackageNum, AppSize, AppCrc32}`
2. App 校验 DevMac/合法性(AppSize==0 或 >232KB 拒绝)→ 回 `0x01 ACK`:
- 载荷建议扩展为 `{Status, ResumedIdx}`:`0x20`=全新开始(App 擦暂存区)、`0x21`=断点续传(已收 ResumedIdx 包,服务器从该包续发)。**注意:原 0x01 载荷 1 字节(0x20),扩展为 3 字节需服务器端同步适配(新版本服务器)**
3. 服务器从 ResumedIdx 起发 `0x82{PackageIndex, ...}`:
- **流控(推荐窗口式)**:服务器连发 W 包(建议 W=8~16)→ App 校验 PackageIdx 顺序写入暂存区(乱序/重复包丢弃)→ App 主动发 `0x02{期望包号}` → 服务器继续。停等式(每包一问一答)在 Cat1 上太慢:1160 包 × RTT≈2~5 分钟+,窗口式约 30~60 秒
- TCP 保证有序,App 端只需顺序校验 + 期望包号判定
4. App 收完 → `CRC32(暂存区, AppSize)`:
- 通过 → 回 `0x01{0x30 完成}` → 写 boot_para_t{AppSize, Crc32Check, UpdateFlag=APP_UPDATE_SPI_FLAG} → 200ms → 复位
- 失败 → 回 `0x01{0x31 CRC错}` → 不复位,清暂存区等待服务器重发(可多次)
5. Boot 检测 `APP_UPDATE_SPI_FLAG` → 逐 8KB:读外部 2×4KB 扇区 → 擦写内部 B 区 1×8KB 扇区 → `CRC32(B)` → `CopyAppBtoA()`(现有)→ `CRC32(A)` → **擦外部暂存区(防残留误判,同 RS485 路径 B 区擦除教训)** → 置 `APP_BOOT_UPGRADE_FLAG` → 复位跳 App
6. App 启动(`APP_BOOT_UPGRADE_FLAG`)→ 擦外部参数分区 → 上线后服务器可查版本确认升级成功
### 超时与重试参数(Cat1 特化,均为 App 侧状态机)
| 参数 | Boot RS485 值 | Cat1 版建议 | 理由 |
|------|--------------|------------|------|
| 等包超时 | 500ms | **3 秒** | Cat1 RTT + 模组缓冲 |
| 重试次数 | 3 | **5** | 偶发断连需模组重连 |
| 断连恢复 | — | 模组重连(现有逻辑)后服务器重发 0x81,ACK 带进度续传 | TCP 断开后应用层无从补偿 |
| 整体超时 | — | 30 分钟无进展 → 放弃升级(清标志,不复位) | 防止无限等待 |
### 断电自愈矩阵(Cat1 路径)
| 断电/异常时机 | 恢复行为 |
|--------------|---------|
| App 收包中断连/断电 | 暂存区+进度保留 → 重连后 0x81 续传 |
| App 置标志后、复位前 | Boot 正常走 SPI Flash→B→A |
| Boot 搬运中(写B区)断电 | UpdateFlag 仍为 SPI_FLAG → B 区 CRC 有效则重搬;无效则清标志回老 App |
| 搬运完、擦暂存区中断电 | 参数未写 → B CRC 判定重搬(B 仍在);参数已写 → 跳新 App |
## 改动清单
### App(GateWay 工程)
| 文件 | 改动 |
|------|------|
| `spiflash.h/c` | 新增 `FLASH_FW_STASH_BASE`(扇区 4038 起)+ StashErase/StashWrite/StashRead;`AddLog` 回绕点 16MB→扇区 4038;新增 CRC32(与 Boot GetCrc32 同算法 0xEDB88320) |
| `CatOneTask.c`(或新文件) | Cat1 通道增加 0x7D 帧组帧解析(`+LIPURC` hex→二进制后按 0x7D 组帧,与现有 0x7A 链路并存,按帧头区分);升级接收状态机(移植 Boot AnalyzeData+BL 状态机,超时/重试参数按上表) |
| `Update.c` | 新增 `UpdateFromStash()`:写 boot_para_t(APP_UPDATE_SPI_FLAG + AppSize + Crc32Check)→ 复位 |
| `bsp.h`(App) | `APP_UPDATE_SPI_FLAG` 宏;`boot_para_t` 不动 |
| `main.h`(App) | 版本号随迭代递增 |
### Boot(HC32F460BootLoader 工程)
| 文件 | 改动 |
|------|------|
| `bsp.c/h` | SPI Flash 驱动:与 App 相同总线(需确认外部 Flash 挂载的 SPI 外设与引脚,Boot 复用),W25Q 写使能/扇区擦/页读命令;约 +2~3KB(Boot 当前 16KB,32KB 分区内余量足够) |
| `main.c/h` | `APP_UPDATE_SPI_FLAG` 宏 + main() 决策分支(SPI_FLAG → 新状态 `BL_STASH_TO_B`);`BL_STASH_TO_B`:逐 8KB 外部读→B 区写→CRC(B) 通过进 `BL_COPY_TO_A`(现有),失败清标志回老 App;`BL_COPY_TO_A` 成功分支追加"擦外部暂存区" |
### 服务器端(不在本仓库,需同步开发)
- 实现 0x81/0x82 升级主机逻辑:窗口流控(W 包一批)、解析 0x02 期望包号、断点续传(ACK 的 ResumedIdx)、完成/失败状态处理
## 风险与待确认项
1. **调试口与 Cat1 共用 USART1(PB08 方向切换)**:升级期间必须保证 UART 方向锁定在 Cat1 侧(`CAT1_ON()`),升级状态机运行期间禁止调试打印切向(`U_DBG_LOG` 若触发 `DBG_ON()` 会破坏 TCP 数据)。实施时需审查 DEBUG=1 下所有升级路径的打印。
2. **外部 Flash 挂载总线待确认**:读 spiflash.c 确认 SPI 外设号/引脚/QSPI 模式,Boot 驱动按此实现。
3. **日志区历史数据**:回绕点前移到扇区 4038(约 15.77MB),已存历史日志超出的部分会被覆盖——现网日志增长慢,实际影响小,但需知晓。
4. **0x01 ACK 载荷扩展**(1→3 字节)需服务器端配合,若服务器不改动则退化为停等式流控(慢但兼容)。
5. **升级流量**:232KB/次,Cat1 资费可忽略。