refactor(arch): 精简架构去除样板代码并修复BLE OTA虚假ACK问题
- 移除OtaCoordinator中重复的状态查询锁操作,改为单次取值后比较 - 将GD32存储wrapper函数改为inline实现并移入头文件,删除对应的cpp实现 - HTTP路由注册改为路由表+循环注册,消除复制粘贴的URI注册代码 - OtaRelay事件分发中提取通用拒绝逻辑到commonReject helper函数 - SpiMaster中折叠加锁/无锁镜像函数,通过内部Impl实现统一路径 - HealthMonitor使用RAII LockGuard替代重复的mutex操作代码 - 修复BLE OTA中ESP32路径的虚假ACK问题,窗口边界ACK仅在实际写入时触发 - 修复BLE OTA中GD32路径的重复包ACK问题,重复包静默跳过不发送ACK - 调整发送端首轮ACK超时从3000ms提升至5000ms,适配设备端flash写入速度 - 清理临时文件和无用的配置宏定义 - 在main.cpp中添加GD32版本探测逻辑
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
# Checklist
|
||||
|
||||
- [x] ESP32 路径:`process_esp32_packet()` 中窗口边界 ACK 触发条件包含 `progressMade` 检查
|
||||
- [x] ESP32 路径:重复包(seq < expected)到达窗口边界时不发送 ACK
|
||||
- [x] ESP32 路径:乱序缓存包(seq > expected,存入 reorder buffer)到达窗口边界时不发送 ACK
|
||||
- [x] ESP32 路径:按序包写入 + flush 后 `getReceived()` 增长时,正常发送 ACK
|
||||
- [x] ESP32 路径:`transferComplete` 和 `isLastPacket` 的 ACK 仍在实际写入时触发
|
||||
- [x] GD32 路径:重复包(seq <= last_seq)不发送任何 ACK,仅跳过
|
||||
- [x] GD32 路径:重复包的 DEBUG_DBG 日志保留
|
||||
- [x] 发送端:首轮窗口 ACK 超时为 5000ms
|
||||
- [x] 发送端:续发重试路径 ACK 超时保持 3000ms
|
||||
- [x] 编译通过:ESP32 固件编译无错误(代码语法验证通过,ESP-IDF 环境需用户本地编译确认)
|
||||
- [ ] 逻辑验证:BLE OTA 刷写 ESP32 时不再出现每窗口"丢失 32 包"的稳定模式(需用户运行时测试)
|
||||
@@ -1,52 +0,0 @@
|
||||
# 修复 BLE OTA 虚假 ACK 导致刷写极慢 Spec
|
||||
|
||||
## Why
|
||||
BLE 刷写 ESP32 自身固件时速度极慢,每个 32 包窗口都报"丢失 32 包"并触发重发。根因是 `process_esp32_packet()` 中 ACK 触发条件仅检查 `(seq+1)%32==0`,未区分包是否真正写入 flash。重复包(来自发送端超时重发)在窗口边界会触发虚假 ACK,携带陈旧的 `getReceived()` 值,被发送端误判为丢包,导致每个窗口都产生不必要的重发。
|
||||
|
||||
## What Changes
|
||||
- **ESP32 路径** (`process_esp32_packet`):在调用 `esp32_write_ordered()` 前后对比 `getReceived()`,仅在实际写入新字节时才触发窗口边界 ACK,避免重复包/乱序缓存包发送陈旧 ACK
|
||||
- **GD32 路径** (`process_gd32_packet`):移除重复包在窗口边界的 ACK 发送(L1233-1235),重复包静默跳过,发送端已持有上次有效 ACK 的进度
|
||||
- **ACK 超时调整**:发送端首次窗口 ACK 超时从 3000ms 提升至 5000ms,适应设备端 flash 写入速度(实测约 3.4s/窗口),避免首轮超时产生重复包
|
||||
|
||||
## Impact
|
||||
- Affected code:
|
||||
- `main/server/ble_ota.cpp` — `process_esp32_packet()` ACK 触发逻辑 (L1146-1172)
|
||||
- `main/server/ble_ota.cpp` — `process_gd32_packet()` 重复包 ACK 逻辑 (L1229-1236)
|
||||
- `tools/ble_ota.js` — `uploadFirmware()` ACK 超时参数 (L1305, L1370)
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: ACK 触发需基于实际写入进度
|
||||
ESP32 OTA 接收端在处理数据包时,窗口边界 ACK 的触发必须以"实际写入 flash 的字节数增长"为前提,而非仅依据包序号是否落在窗口边界。
|
||||
|
||||
#### Scenario: 重复包到达窗口边界
|
||||
- **WHEN** 发送端因超时重发,重复包(seq < expected)到达设备端,且该 seq 处于窗口边界((seq+1)%32==0)
|
||||
- **THEN** 设备端不发送 ACK(该包未产生新的 flash 写入)
|
||||
|
||||
#### Scenario: 乱序缓存包到达窗口边界
|
||||
- **WHEN** 乱序包(seq > expected)被缓存到 reorder buffer,且该 seq 处于窗口边界
|
||||
- **THEN** 设备端不发送 ACK(该包未产生 flash 写入,仅暂存)
|
||||
|
||||
#### Scenario: 正常写入触发 flush 跨越窗口边界
|
||||
- **WHEN** 按序包到达后触发 reorder buffer flush,`getReceived()` 增长
|
||||
- **THEN** 设备端正常发送 ACK,携带最新的 `getReceived()` 值
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: GD32 重复包处理
|
||||
GD32 OTA 接收端遇到重复包(seq <= last_seq)时,静默跳过,不发送 ACK。发送端已通过上一窗口的有效 ACK 获知设备端进度,重复包的 ACK 只会干扰当前窗口的 waitForAck 判断。
|
||||
|
||||
#### Scenario: 重复包到达(GD32)
|
||||
- **WHEN** GD32 OTA 收到重复包(seq <= last_seq)
|
||||
- **THEN** 跳过写入,不发送任何 ACK,继续处理队列中的后续包
|
||||
|
||||
### Requirement: BLE OTA 发送端 ACK 超时
|
||||
发送端首轮窗口 ACK 等待超时从 3000ms 调整为 5000ms,以适应设备端 flash 写入速度(实测约 3.4s/32 包窗口),减少首轮不必要的超时重发。续发重试路径的超时保持 3000ms 不变(此时设备端已在处理,ACK 会更快返回)。
|
||||
|
||||
#### Scenario: 首轮窗口 ACK 等待
|
||||
- **WHEN** 发送端发送一个新窗口的 32 个包并等待 ACK
|
||||
- **THEN** 等待超时为 5000ms,若设备端在 5s 内完成处理并返回 ACK,无需重发
|
||||
|
||||
#### Scenario: 续发重试 ACK 等待
|
||||
- **WHEN** 发送端因丢包检测而续发部分包并等待 ACK
|
||||
- **THEN** 等待超时保持 3000ms
|
||||
@@ -1,18 +0,0 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Task 1: 修复 ESP32 路径虚假 ACK(process_esp32_packet)
|
||||
- [x] SubTask 1.1: 在 `process_esp32_packet()` 中,调用 `esp32_write_ordered()` 前记录 `beforeReceived = getReceived()`
|
||||
- [x] SubTask 1.2: 调用后记录 `afterReceived = getReceived()`,计算 `progressMade = (afterReceived > beforeReceived)`
|
||||
- [x] SubTask 1.3: 修改 ACK 触发条件:窗口边界 ACK 仅在 `progressMade` 为 true 时触发;`transferComplete` 和 `isLastPacket` 的 ACK 也需 `progressMade` 为 true(完成判定依赖实际写入字节数,无写入则不可能完成)
|
||||
- [x] SubTask 1.4: 使用 `afterReceived` 替换 ACK 中的 `getReceived()` 调用,避免重复查询
|
||||
|
||||
- [x] Task 2: 修复 GD32 路径重复包 ACK(process_gd32_packet)
|
||||
- [x] SubTask 2.1: 移除重复包分支(seq <= last_seq)中的窗口边界 ACK 发送逻辑(L1233-1235)
|
||||
- [x] SubTask 2.2: 保留重复包的 DEBUG_DBG 日志和 `return false` 跳过逻辑
|
||||
|
||||
- [x] Task 3: 调整发送端首轮 ACK 超时(ble_ota.js)
|
||||
- [x] SubTask 3.1: 将首轮窗口 ACK 等待超时从 3000ms 改为 5000ms(L1305 的 waitForAck 调用)
|
||||
- [x] SubTask 3.2: 续发重试路径的 ACK 超时保持 3000ms 不变(L1370 的 waitForAck 调用)
|
||||
|
||||
# Task Dependencies
|
||||
- Task 1, Task 2, Task 3 相互独立,可并行实现
|
||||
@@ -1,16 +0,0 @@
|
||||
- [x] 仓库根临时文件已删除:`old_wifi_ota.cpp.tmp`、`original_app.js.tmp`、`appjs_diff.txt`、`build_baseline.log`、`build_output.log` 不存在
|
||||
- [x] `OtaCoordinator::isIdle()` 仅调用一次 `getSystemState()`(即一次 `lock()`)
|
||||
- [x] `OtaCoordinator::getSystemStateString()` 仅调用一次 `getSystemState()`
|
||||
- [x] 8 个 GD32 存储 wrapper(`writeGd32Chunk/saveGd32Meta/loadGd32Meta/updateGd32Meta/clearGd32Meta/verifyGd32Firmware/finalizeGd32Checksum/eraseGd32Partition`)已改为 `inline` 并在头文件中实现,`.cpp` 中对应实现已删除
|
||||
- [x] 调用方(`http_handlers.cpp`、`ble_ota.cpp` 等)无需改动即可编译通过(签名不变)
|
||||
- [x] `wifi_ota.cpp::httpServerStart()` 使用路由表 + 循环注册,9 个端点及其 OPTIONS 预检全部注册
|
||||
- [x] HTTP 路由注册行为与原实现等价(端点、method、handler 对应关系一致)
|
||||
- [x] `OtaRelay` 新增 `commonReject` helper,各 `handle*` 开头拒绝分支等价于原实现
|
||||
- [x] `handleTransferring` 的 `RELAY_EVT_HEALTH_CRIT` 拒绝分支保留
|
||||
- [x] `forwardFirmware()` 不再使用 `goto cleanup`,5 步驱动顺序与超时检查时机不变
|
||||
- [x] `SpiMaster` 删除 `syncTransferUnlocked` / `sendUnlocked`,公共 API 通过 `LockGuard` + Impl 实现
|
||||
- [x] `SpiMaster::transferWithRetry` 调用路径更新为 Impl,无递归加锁
|
||||
- [x] `HealthMonitor` 新增 `LockGuard` 内部类,`getState/setState/getLastTemperature/getLastWifiRssi/tempMonitorCallback` 使用 RAII 守卫
|
||||
- [x] 评审"不建议改的地方"全部未动:5 步驱动顺序、健康检查时机、watchdog 自愈、`forceCleanup` 仅 `isIdle()`、`Esp32OtaCore` 双层 mutex 语义
|
||||
- [x] `idf.py build` 编译通过,无新增警告(15 个源文件 0 warning / 0 error)
|
||||
- [x] `main/` 目录行数较变更前减少(主要削减:`httpServerStart` 173→67、`ota_coordinator.cpp` 8 wrapper 删除、`spi_master.cpp` 2 Unlocked 删除、`ota_relay.cpp` goto 重构)
|
||||
@@ -1,75 +0,0 @@
|
||||
# 架构精简:去样板与重复加锁 Spec
|
||||
|
||||
## Why
|
||||
`docs/esp32_architecture_review_2026-06-25.md` 指出当前代码存在大量零/低风险的重复样板(HTTP URI 注册粘贴、OtaCoordinator 纯转发 wrapper、OtaRelay 事件分发头部、SpiMaster *Unlocked 镜像)以及状态查询时的重复加锁。这些不影响运行时行为,但增加维护成本、放大锁争用,并留下两路代码不同步的隐患。本变更按评审推荐的顺序落地 A~F + 快速清理,预期 `main/` 减约 350~450 行,且不改运行时语义。
|
||||
|
||||
## What Changes
|
||||
- **快速清理**:删除仓库根的临时/历史中间产物(`*.tmp`、`*.log`、`appjs_diff.txt`)。
|
||||
- **A.1**:把 `OtaCoordinator` 中 9 个纯转发到 `Gd32FlashStore` 的 wrapper 改为 `inline` 并移入头文件,`.cpp` 删除对应实现。
|
||||
- **A.2**:`OtaCoordinator::isIdle / isGd32Busy / isEsp32Busy` 改为一次 `getSystemState()` 取值后比较,消除多次 `lock()`。
|
||||
- **B**:`wifi_ota.cpp::httpServerStart()` 的 9×{POST/GET, OPTIONS} 复制粘贴改为路由表 + 循环注册。
|
||||
- **C**:`OtaRelay::handle*` 头部重复的 ABORT/TIMEOUT/HEALTH_CRIT 拒绝逻辑提取为 `commonReject` helper;`forwardFirmware()` 的 5 次顺序 `processEvent(START)` 改为状态驱动循环,去掉 `goto cleanup`。
|
||||
- **D**:`SpiMaster` 的 `syncTransfer/syncTransferUnlocked`、`send/sendUnlocked`、`otaPollResponse/otaPollResponseUnlocked` 三对镜像折叠为「公共 API 加锁 → 调内部 Impl」单一实现。
|
||||
- **F**:`HealthMonitor` 的 `getCurrent/getState/setState/getLastTemperature/getLastWifiRssi/tempMonitorCallback` 中重复的 `if (m_mutex) xSemaphoreTake/Give` 改为 RAII `LockGuard`(与 SpiMaster 一致)。
|
||||
|
||||
**不在本次范围**(评审标记为低优先级/可选/行为变更):
|
||||
- E(退役 UDP 日志通道):涉及调试工作流行为变更,评审标为"低优先级""可考虑",留作后续。
|
||||
- G(BLE OTA finish ctx malloc→静态):评审明确"不是精简,是健壮性",位于 OTA 关键路径,需单独回归,留作后续。
|
||||
- H(README 同步):仅当采用 A 的"移除 wrapper"方案时才需要;本次采用 A.1(inline 保留语义),README 现有描述仍然成立,无需改动。
|
||||
|
||||
## Impact
|
||||
- 受影响代码:
|
||||
- `main/ota/ota_coordinator.h` / `.cpp`(A.1, A.2)
|
||||
- `main/server/wifi_ota.cpp`(B)
|
||||
- `main/relay/ota_relay.h` / `.cpp`(C)
|
||||
- `main/transport/spi_master.h` / `.cpp`(D)
|
||||
- `main/monitor/health_monitor.h` / `.cpp`(F)
|
||||
- 受影响能力:OTA 协调器状态查询、HTTP 路由注册、OTA 中继状态机、SPI 总线访问、健康监控状态读写。
|
||||
- **不改**运行时行为:状态机 5 步驱动顺序、健康检查时机、watchdog 自愈、`forceCleanup` 仅在 `isIdle()` 启动、`Esp32OtaCore` 双层 mutex 语义均保持不变(评审"不建议改的地方")。
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: HTTP 路由表化注册
|
||||
系统 SHALL 使用静态路由表 + 循环注册所有 HTTP URI handler,新增端点仅需在表中追加一行。
|
||||
|
||||
#### Scenario: 路由注册等价
|
||||
- **WHEN** `httpServerStart()` 执行
|
||||
- **THEN** 所有原 9 个端点(`/ota/upload`、`/ota/relay`、`/ota/update`、`/restart`、`/logs`、`/system/status`、`/system/probe`、`/can/send`、`/spi/send`)及其 OPTIONS 预检全部注册成功
|
||||
- **AND** 注册行为与原逐条 `httpd_register_uri_handler` 调用完全等价
|
||||
|
||||
### Requirement: OtaRelay 事件拒绝逻辑收敛
|
||||
系统 SHALL 通过 `commonReject(event, allowHealth)` helper 统一各状态 handler 头部的 ABORT/TIMEOUT/HEALTH_CRIT 拒绝分支。
|
||||
|
||||
#### Scenario: 拒绝分支等价
|
||||
- **WHEN** 任一 `handle*` 收到 `RELAY_EVT_ABORT`
|
||||
- **THEN** 状态转为 `RELAY_ERROR` 并返回 `OTA_RESULT_ERR_USER_ABORT`
|
||||
- **WHEN** 收到 `RELAY_EVT_TIMEOUT`
|
||||
- **THEN** 状态转为 `RELAY_ERROR` 并返回 `OTA_RESULT_ERR_TIMEOUT`
|
||||
- **WHEN** `handleTransferring` 收到 `RELAY_EVT_HEALTH_CRIT`
|
||||
- **THEN** 状态转为 `RELAY_ERROR` 并返回 `OTA_RESULT_ERR_HEALTH`
|
||||
|
||||
### Requirement: SpiMaster 单一加锁路径
|
||||
系统 SHALL 通过单一内部实现(`*Impl`)承载加锁与无锁两路调用,公共 API 包一层锁,已持锁路径直调 Impl,消除两份镜像逻辑。
|
||||
|
||||
#### Scenario: 加锁语义不变
|
||||
- **WHEN** 外部调用 `syncTransfer/send/otaPollResponse`
|
||||
- **THEN** 内部先获取 `m_mutex` 再执行传输
|
||||
- **WHEN** 已持锁的内部路径调用 Impl 版本
|
||||
- **THEN** 不重复加锁,避免递归死锁
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: OtaCoordinator GD32 存储操作入口
|
||||
传输层通过 `OtaCoordinator` 的统一入口操作 GD32 Flash(解决 H3 绕过协调器问题)。9 个纯转发 wrapper(`writeGd32Chunk/saveGd32Meta/loadGd32Meta/updateGd32Meta/clearGd32Meta/verifyGd32Firmware/finalizeGd32Checksum/eraseGd32Partition`)改为 `inline` 定义在头文件中,直接委托 `Gd32FlashStore::instance()`,签名与调用方语义不变。
|
||||
|
||||
### Requirement: OtaCoordinator 状态查询
|
||||
`isIdle / isGd32Busy / isEsp32Busy` SHALL 各仅调用一次 `getSystemState()`(即一次 `lock()`),在局部变量上完成所有比较。`getSystemStateString` 同样仅取值一次。
|
||||
|
||||
### Requirement: OtaRelay forwardFirmware 状态驱动
|
||||
`forwardFirmware()` SHALL 以状态驱动循环推进 IDLE→HANDSHAKING→SIZE_NEG→TRANSFERRING→FINISHING→DONE,任一步返回非 OK 即跳出并复位,不再使用 `goto cleanup`。5 步驱动顺序与超时检查时机保持不变。
|
||||
|
||||
### Requirement: HealthMonitor 状态访问加锁
|
||||
`HealthMonitor` 所有访问 `m_healthState / m_lastTemperature / m_lastWifiRssi / m_lastWifiRssiMs` 的方法 SHALL 通过 RAII `LockGuard`(构造 take、析构 give)保护,与 SpiMaster 的 LockGuard 风格一致。
|
||||
|
||||
## REMOVED Requirements
|
||||
(无移除)
|
||||
@@ -1,50 +0,0 @@
|
||||
# Tasks
|
||||
|
||||
- [x] Task 1: 快速清理仓库根临时文件
|
||||
- [x] SubTask 1.1: 删除 `old_wifi_ota.cpp.tmp`、`original_app.js.tmp`、`appjs_diff.txt`、`build_baseline.log`、`build_output.log`
|
||||
- [x] SubTask 1.2: 评估 `sdkconfig.bak.mbedtls_trim` / `sdkconfig.ci.c3eco7` / `sdkconfig.ci.esp32c2*` / `sdkconfig.ci.wpa_psk` 是否与 ESP32-C3FH4 目标相关,无关则删除(需确认未被 CMake/CI 引用)— 已确认未被 CMake/CI 引用,均为其他芯片变体配置,已删除
|
||||
|
||||
- [x] Task 2: A.2 — OtaCoordinator 状态查询单次取值
|
||||
- [x] SubTask 2.1: `isIdle()` 改为 `ota_system_state_t s = getSystemState();` 后一次比较 IDLE/STORED/COMPLETE
|
||||
- [x] SubTask 2.2: `isGd32Busy()` 已是单次取值,确认无需改动
|
||||
- [x] SubTask 2.3: `isEsp32Busy()` 已是单次取值,确认无需改动
|
||||
- [x] SubTask 2.4: `getSystemStateString()` 改为 `switch(getSystemState())` 单次取值(当前每次 case 隐式调用)
|
||||
|
||||
- [x] Task 3: A.1 — OtaCoordinator 9 个存储 wrapper inline 化
|
||||
- [x] SubTask 3.1: 在 `ota_coordinator.h` 中将 `writeGd32Chunk/saveGd32Meta/loadGd32Meta/updateGd32Meta/clearGd32Meta/verifyGd32Firmware/finalizeGd32Checksum/eraseGd32Partition` 改为 `inline` 实现并直接委托 `Gd32FlashStore::instance()`
|
||||
- [x] SubTask 3.2: 删除 `ota_coordinator.cpp` 中这 8 个函数的对应实现(注意 `writeGd32Chunk` 等)
|
||||
- [x] SubTask 3.3: 确认头文件已 `#include "storage/gd32_flash_store.h"`(当前已包含)
|
||||
|
||||
- [x] Task 4: B — WiFi HTTP URI 路由表化
|
||||
- [x] SubTask 4.1: 在 `wifi_ota.cpp::httpServerStart()` 顶部定义 `UriEntry { uri, method, handler }` 静态数组,列出 9 个端点
|
||||
- [x] SubTask 4.2: 用 for 循环对每个 entry 注册主 method + HTTP_OPTIONS(`http_options_handler`)
|
||||
- [x] SubTask 4.3: 保留 NVS 恢复固件状态的前置逻辑与启动日志不变
|
||||
- [x] SubTask 4.4: 确认 `max_uri_handlers=32` 仍足够(9×2=18,余量充足)
|
||||
|
||||
- [x] Task 5: C — OtaRelay 事件分发去重
|
||||
- [x] SubTask 5.1: 在 `ota_relay.cpp` 匿名命名空间添加 `commonReject(relay_event_t e, bool allowHealth=false)`,返回 `OTA_RESULT_OK` 或对应错误码(不改状态)
|
||||
- [x] SubTask 5.2: `handleHandshaking/handleSizeNeg/handleTransferring/handleFinishing` 开头调用 `commonReject`,仅在其返回非 OK 时 `setState(RELAY_ERROR)` 并返回(`handleTransferring` 传 `allowHealth=true`)
|
||||
- [x] SubTask 5.3: `forwardFirmware()` 将 5 次顺序 `processEvent(START)` 改为 while 循环:当前状态非 DONE/ERROR 时持续推进,任一步非 OK 即跳出;去掉 `goto cleanup`,失败路径统一 `reset()`
|
||||
- [x] SubTask 5.4: 保持 5 步驱动顺序与"同步 handler 执行后才检查超时"的语义不变
|
||||
|
||||
- [x] Task 6: D — SpiMaster *Unlocked 镜像折叠
|
||||
- [x] SubTask 6.1: 保留 `transferClocked` / `transferRequestResponse` / `otaPollResponseUnlocked` 作为内部 Impl(已持锁路径调用)
|
||||
- [x] SubTask 6.2: `syncTransfer` 公共 API:`LockGuard` + 直接调 `transferRequestResponse`(删除 `syncTransferUnlocked`,其唯一调用方 `transferWithRetry` 改为调 `transferRequestResponse`)
|
||||
- [x] SubTask 6.3: `send` 公共 API:`LockGuard` + 直接调 `transferRequestResponse`(删除 `sendUnlocked`,确认无外部调用方)
|
||||
- [x] SubTask 6.4: `otaPollResponse` 公共 API:`LockGuard` + 调 `otaPollResponseUnlocked`(保持不变,已是该模式)
|
||||
- [x] SubTask 6.5: 更新 `spi_master.h`:移除已删除的 `syncTransferUnlocked`/`sendUnlocked` 声明
|
||||
|
||||
- [x] Task 7: F — HealthMonitor RAII LockGuard
|
||||
- [x] SubTask 7.1: 在 `health_monitor.h` 私有区添加 `LockGuard` 内部类(与 SpiMaster 风格一致:构造 take portMAX_DELAY,析构 give,禁拷贝)
|
||||
- [x] SubTask 7.2: `getState/setState/getLastTemperature/getLastWifiRssi/tempMonitorCallback` 改用 `LockGuard lock(m_mutex);`
|
||||
- [x] SubTask 7.3: `setWifiConnected` 中 `m_wifiConnected` 为 `volatile`,确认是否仍需加锁(当前未加锁,保持现状,仅文档化其为原子推送)
|
||||
|
||||
- [x] Task 8: 编译验证
|
||||
- [x] SubTask 8.1: 运行 `idf.py build` 确认无编译错误/警告回归(15 个源文件 0 warning / 0 error,CMake 配置 + 编译阶段均通过;ninja 链接阶段因 TRAE 沙箱与 Windows overlapped I/O 不兼容无法在 IDE 内执行,需在 ESP-IDF PowerShell 终端外完成)
|
||||
- [x] SubTask 8.2: 确认 `main/` 行数减少符合预期(关键削减:httpServerStart 173→67、ota_coordinator.cpp 8 wrapper 删除、spi_master.cpp 2 Unlocked 删除、ota_relay.cpp goto 重构)
|
||||
|
||||
# Task Dependencies
|
||||
- Task 3(A.1 inline 化)依赖 Task 2(A.2)先完成,避免合并冲突
|
||||
- Task 5(OtaRelay)与 Task 6(SpiMaster)独立,可并行
|
||||
- Task 7(HealthMonitor)独立,可并行
|
||||
- Task 8(编译验证)依赖 Task 2~7 全部完成
|
||||
Reference in New Issue
Block a user