refactor(main): 移除ota_mutex.h全局变量并重构ESP32 OTA互斥锁管理

- 将全局变量g_esp32OtaMutex和g_esp32OtaOccupied内聚到Esp32OtaCore类中
- 通过Esp32OtaCore::initMutex()方法初始化互斥锁替代原main.cpp中的全局初始化
- 重构HealthMonitor中WiFi连接状态管理,推送连接状态到本地成员而非直接查询
- 优化OtaCoordinator中const方法的线程安全实现,移除不必要的const_cast转换
- 封装OtaRelay的队列访问器,提供getDispatchQueue()和getResultQueue()方法
- 解决BLE与WiFi之间的横向耦合问题,通过OtaCoordinator进行射频共存管理
- 移除transport/spi_master.cpp中的CANFD接口实现到独立的canfd_bridge模块
- 优化BLE OTA中ESP32会话的幂等性和新会话处理逻辑

BREAKING CHANGE: 移除了ota_mutex.h全局头文件,相关功能迁移至Esp32OtaCore类中管理
This commit is contained in:
小龙虾
2026-06-22 01:53:16 +08:00
parent 00ce8dbbb5
commit 1491e9a3de
23 changed files with 598 additions and 290 deletions
+34 -20
View File
@@ -38,9 +38,7 @@
// SHA-256 计算已抽离到 Esp32OtaCore,本文件不再直接引用 mbedtls
#include "app_config.h"
#include "ota_mutex.h"
#include "ble_ota.h"
#include "wifi_ota.h"
#include "esp32_ota_core.h"
#include "ota_coordinator.h"
#include "ota_relay.h"
@@ -234,7 +232,8 @@ static void ble_ota_boost_wifi_ps()
{
if (s_wifi_ps_boosted) return;
s_wifi_ps_boosted = true;
WifiOta::instance().setBleOtaActive(true); // ★ 暂停 WiFi RSSI 轮询
// ★ L1: 通过协调器通知 WiFi 层(替代直接调用 WifiOta)
OtaCoordinator::instance().notifyBleCoexStart();
DEBUG_INFO(TAG, "BLE OTA 进行中:暂停 WiFi RSSI 轮询(保持 WIFI_PS_NONE,STA 仍可响应 HTTP)");
}
@@ -245,7 +244,8 @@ static void ble_ota_restore_wifi_ps()
{
if (!s_wifi_ps_boosted) return;
s_wifi_ps_boosted = false;
WifiOta::instance().setBleOtaActive(false); // ★ 恢复 WiFi RSSI 轮询
// ★ L1: 通过协调器通知 WiFi 层(替代直接调用 WifiOta)
OtaCoordinator::instance().notifyBleCoexEnd();
DEBUG_INFO(TAG, "BLE OTA 结束:恢复 WiFi RSSI 轮询");
}
@@ -571,12 +571,10 @@ static esp_err_t ble_esp32_ota_begin(size_t total_size)
*/
static void ble_esp32_ota_abort()
{
// ★ 通知协调器释放 ESP32 通道占用(m_sysState → IDLE),
// 否则后续 begin() 会因协调器仍记为 ESP32_RECEIVING 而假拒绝。
ota_system_state_t cur = OtaCoordinator::instance().getSystemState();
if (cur == OTA_SYS_ESP32_RECEIVING || cur == OTA_SYS_ESP32_FINALIZING) {
OtaCoordinator::instance().abortCurrent();
}
// ★ 无条件通知协调器释放通道占用(m_sysState → IDLE)。
// abortCurrent() 对 IDLE 状态是空操作,对其他状态均复位到 IDLE,
// 确保后续 begin() 不会被残留的 ESP32_RECEIVING / ERROR 等状态假拒绝。
OtaCoordinator::instance().abortCurrent();
Esp32OtaCore::instance().abort();
BleOta::instance().m_esp32RebootPending = false;
// ★ 释放 reorder buffer
@@ -672,8 +670,9 @@ static void ble_gd32_relay_wait_task(void *pvParameters)
uint32_t result_msg = (uint32_t)OTA_RESULT_ERR_TIMEOUT;
bool got_result = false;
if (OtaRelay::resultQueue) {
got_result = (xQueueReceive(OtaRelay::resultQueue, &result_msg, pdMS_TO_TICKS(120000)) == pdTRUE);
QueueHandle_t rq = OtaRelay::getResultQueue();
if (rq) {
got_result = (xQueueReceive(rq, &result_msg, pdMS_TO_TICKS(120000)) == pdTRUE);
}
if (!got_result) {
@@ -783,9 +782,9 @@ static int ble_ota_ctrl_access(uint16_t conn_handle, uint16_t attr_handle,
DEBUG_INFO(TAG, "GD32 固件就绪且 SHA-256 匹配 (%" PRIu32 " / %" PRIu32 " 字节),通过协调器触发中继",
meta.stored_size, meta.total_size);
if (OtaRelay::resultQueue) {
if (OtaRelay::getResultQueue()) {
uint32_t stale = 0;
while (xQueueReceive(OtaRelay::resultQueue, &stale, 0) == pdTRUE) {
while (xQueueReceive(OtaRelay::getResultQueue(), &stale, 0) == pdTRUE) {
DEBUG_WARN(TAG, "清理旧 GD32 中继结果: %" PRIu32, stale);
}
}
@@ -1020,13 +1019,28 @@ static void ble_ota_process_data_batch(const uint8_t *buf, size_t recv_len)
DEBUG_ERR(TAG, "ESP32 固件大小非法: %" PRIu32, total_size);
return;
}
// ★ 幂等保护:若已有活跃 ESP32 OTA 会话,说明这是客户端因 ACK 超时
// 重传的 seq=0(同一会话续传)。若再次走 begin,会被协调器以
// "OTA 已 ESP32_RECEIVING" 拒绝 → 误报"被其他通道占用"并中止整条
// OTA 流程。此处跳过 begin 复用现有会话,后续 reorder buffer 会
// 按 seq 去重(seq=0 < expected 时作为重复包丢弃)。
// ★ 会话判定:若已有活跃 ESP32 OTA 会话,根据已接收字节数区分两种情况:
// ① m_received == 0:seq=0 尚未写入 flash,说明是客户端 ACK 超时后
// 重传的 seq=0(同一会话续传),跳过 begin。
// ② m_received > 0:seq=0 已写入且有后续数据,说明这是新的上传会话
// (前端 abort 后 BLE 断开未完成就发起了新传输),需 abort 旧会话
// 再 begin,否则 reorder buffer 以旧 expected 去重导致全部丢弃。
if (Esp32OtaCore::instance().isActive()) {
DEBUG_INFO(TAG, "ESP32 OTA 会话已激活,seq=0 视为重传,跳过 begin");
if (Esp32OtaCore::instance().getReceived() > 0) {
DEBUG_WARN(TAG, "ESP32 OTA 新会话检测: received=%u > 0, "
"中止旧会话并重新开始",
(unsigned)Esp32OtaCore::instance().getReceived());
ble_esp32_ota_abort();
esp_err_t err = ble_esp32_ota_begin(total_size);
if (err != ESP_OK) {
if (err == ESP_ERR_INVALID_STATE) {
sendFinishEvent(BLE_OTA_FINISH_ERR_MUTEX, 0);
}
return;
}
} else {
DEBUG_INFO(TAG, "ESP32 OTA 会话已激活,seq=0 视为重传,跳过 begin");
}
} else {
// 分区选择/大小校验/互斥由 Esp32OtaCore::begin 内部完成
esp_err_t err = ble_esp32_ota_begin(total_size);
+4 -2
View File
@@ -24,6 +24,7 @@
#include "health_monitor.h"
#include "monitor/debug_log.h"
#include "spi_master.h"
#include "canfd_bridge.h"
#include "ble_ota.h"
static constexpr uint32_t GD32_OTA_FLASH_SECTOR_SIZE = 4096U;
@@ -180,7 +181,8 @@ esp_err_t ota_upload_handler(httpd_req_t *req)
DEBUG_INFO("WIFI", "收到固件上传请求, 内容长度: %d 字节", req->content_len);
esp_wifi_set_ps(WIFI_PS_NONE);
WifiOta::instance().setBleOtaActive(false);
// ★ L1: WiFi OTA 开始时恢复 BLE 共存状态(通过协调器)
OtaCoordinator::instance().notifyBleCoexEnd();
char peek;
int peek_ret = httpd_req_recv(req, &peek, 1);
@@ -563,7 +565,7 @@ esp_err_t can_send_handler(httpd_req_t *req)
cJSON_Delete(root);
uint8_t resp_data[CAN_MAX_DLC] = {0};
int resp_len = SpiMaster::instance().canSend(ch, ext_id, tx_data, tx_dlc,
int resp_len = canFdSend(ch, ext_id, tx_data, tx_dlc,
resp_data, sizeof(resp_data), 1000);
char resp[512];
+17 -1
View File
@@ -22,7 +22,6 @@
#include "esp_mac.h"
#include "app_config.h"
#include "ota_mutex.h"
#include "wifi_ota.h"
#include "esp32_ota_core.h"
#include "ota_coordinator.h"
@@ -149,6 +148,8 @@ void WifiOta::wifiEventHandler(void* arg, esp_event_base_t event_base,
inst.setStaRetryCount(0);
inst.setStaRetryExhausted(false); // ★ 连接成功,重置耗尽标志
inst.stateUnlock();
// ★ M2: 推送 WiFi 连接状态给 HealthMonitor(解耦监控层主动查询)
HealthMonitor::instance().setWifiConnected(true);
if (inst.m_staConnectedSem) {
xSemaphoreGive(inst.m_staConnectedSem);
}
@@ -161,6 +162,8 @@ void WifiOta::wifiEventHandler(void* arg, esp_event_base_t event_base,
bool exhausted = inst.getStaRetryExhausted();
int retryCount = inst.getStaRetryCount();
inst.stateUnlock();
// ★ M2: 推送 WiFi 断开状态给 HealthMonitor
HealthMonitor::instance().setWifiConnected(false);
if (!exhausted) {
DEBUG_WARN("WIFI", "STA 断开, reason=%d, retry=%d",
@@ -602,6 +605,19 @@ bool WifiOta::isConnected()
return WifiOta::instance().getStaConnected();
}
// ============================================================
// IBleCoexListener 接口实现(L1: 通过协调器接收 BLE 共存通知)
// ============================================================
void WifiOta::onBleOtaStart()
{
setBleOtaActive(true);
}
void WifiOta::onBleOtaEnd()
{
setBleOtaActive(false);
}
// ----------------------------------------
// ★ WiFi 早期初始化(在 app_main 调度器启动前调用)
// 包括:NVS → netif → event handler → WiFi driver → 配置 → connect
+9 -1
View File
@@ -14,13 +14,15 @@
#include "freertos/task.h"
#include <atomic>
#include "ota/ota_coordinator.h" // IBleCoexListener 接口定义
/** @brief WiFi 工作模式枚举,区分 AP(设备热点)和 STA(连接路由器)模式。 */
typedef enum {
OTA_WIFI_MODE_AP, ///< AP 模式:设备作为热点供局域网设备连接
OTA_WIFI_MODE_STA ///< STA 模式:设备连接上级路由器
} ota_wifi_mode_t;
class WifiOta {
class WifiOta : public IBleCoexListener {
public:
/**
* @brief 获取 WifiOta 单例实例。
@@ -93,6 +95,12 @@ public:
/** @brief BLE OTA 开始传输时调用,暂停 WiFi RSSI 轮询等射频活动。 */
void setBleOtaActive(bool v) { m_bleOtaActive.store(v); }
// ── IBleCoexListener 接口实现(L1: 通过协调器接收 BLE 共存通知)──
/** @brief BLE OTA 开始,暂停 WiFi RSSI 轮询。 */
void onBleOtaStart() override;
/** @brief BLE OTA 结束,恢复 WiFi 射频活动。 */
void onBleOtaEnd() override;
/** @brief 获取 WiFi OTA 任务句柄。 */
TaskHandle_t getTaskHandle() const { return m_taskHandle; }