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
+54 -20
View File
@@ -17,12 +17,46 @@
#include "mbedtls/private/sha256.h"
#include "esp32_ota_core.h"
#include "ota_mutex.h"
#include "app_config.h"
#include "monitor/debug_log.h"
#define TAG "OTA_CORE"
// ============================================================
// 分区互斥锁(M1: 内聚自 ota_mutex.h 全局变量)
// ============================================================
SemaphoreHandle_t Esp32OtaCore::s_mutex = NULL;
volatile bool Esp32OtaCore::s_occupied = false;
void Esp32OtaCore::initMutex()
{
if (!s_mutex) {
s_mutex = xSemaphoreCreateMutex();
s_occupied = false;
}
}
bool Esp32OtaCore::acquireMutex(uint32_t timeout_ms)
{
if (!s_mutex) return false;
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(timeout_ms)) != pdTRUE) return false;
if (s_occupied) {
xSemaphoreGive(s_mutex);
return false;
}
s_occupied = true;
xSemaphoreGive(s_mutex);
return true;
}
void Esp32OtaCore::releaseMutex()
{
if (!s_mutex) return;
xSemaphoreTake(s_mutex, portMAX_DELAY);
s_occupied = false;
xSemaphoreGive(s_mutex);
}
/**
* @brief 获取 Esp32OtaCore 单例实例。
* @return Esp32OtaCore 引用。
@@ -56,7 +90,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size)
{
// ★ 全局互斥:必须在清理旧会话之前检查,
// 否则 WiFi 的 begin() 会先释放 BLE 持有的锁,再自己获取,导致互斥失效
if (!esp32_ota_mutex_acquire(1000)) {
if (!acquireMutex(1000)) {
DEBUG_ERR(TAG, "ESP32 OTA 被其他通道占用,拒绝 begin");
return ESP_ERR_INVALID_STATE;
}
@@ -67,10 +101,10 @@ esp_err_t Esp32OtaCore::begin(size_t total_size)
if (!m_ended) {
esp_ota_abort(m_handle);
}
esp32_ota_mutex_release();
releaseMutex();
resetState();
// 重新获取全局互斥(刚刚释放了)
if (!esp32_ota_mutex_acquire(1000)) {
if (!acquireMutex(1000)) {
DEBUG_ERR(TAG, "ESP32 OTA 重新获取互斥失败");
return ESP_ERR_INVALID_STATE;
}
@@ -91,7 +125,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size)
if (m_partition == NULL) {
DEBUG_ERR(TAG, "找不到可用的 OTA 分区");
esp32_ota_mutex_release();
releaseMutex();
return ESP_FAIL;
}
@@ -102,7 +136,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size)
DEBUG_ERR(TAG, "固件太大: %u > 分区大小: %u",
(unsigned)total_size, (unsigned)m_partition->size);
m_partition = nullptr;
esp32_ota_mutex_release();
releaseMutex();
return ESP_FAIL;
}
@@ -119,7 +153,7 @@ esp_err_t Esp32OtaCore::begin(size_t total_size)
DEBUG_ERR(TAG, "esp_ota_begin 失败: %s", esp_err_to_name(err));
m_partition = nullptr;
m_handle = 0;
esp32_ota_mutex_release();
releaseMutex();
return err;
}
@@ -248,7 +282,7 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot,
DEBUG_ERR(TAG, "SHA 计算失败: %s,中止", esp_err_to_name(sha_err));
esp_ota_abort(m_handle);
m_ended = false;
esp32_ota_mutex_release();
releaseMutex();
resetState();
return sha_err;
}
@@ -279,7 +313,7 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot,
frontend_sha_hex, device_hex);
esp_ota_abort(m_handle);
m_ended = false;
esp32_ota_mutex_release();
releaseMutex();
resetState();
return ESP_ERR_INVALID_VERSION;
}
@@ -300,7 +334,7 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot,
// end 失败时仍可 abort(未真正 end)
esp_ota_abort(m_handle);
m_ended = false;
esp32_ota_mutex_release();
releaseMutex();
resetState();
return err;
}
@@ -310,12 +344,12 @@ esp_err_t Esp32OtaCore::finalize(const char *frontend_sha_hex, bool reboot,
err = esp_ota_set_boot_partition(m_partition);
if (err != ESP_OK) {
DEBUG_ERR(TAG, "esp_ota_set_boot_partition 失败: %s", esp_err_to_name(err));
esp32_ota_mutex_release();
releaseMutex();
resetState();
return err;
}
esp32_ota_mutex_release();
releaseMutex();
DEBUG_INFO(TAG, "ESP32 OTA 收尾完成,共 %u 字节", (unsigned)m_received);
// 保持 m_active 直到重启或下次 begin;resetState 由 begin/abort 处理
@@ -341,7 +375,7 @@ void Esp32OtaCore::abort()
if (!m_ended) {
esp_ota_abort(m_handle);
}
esp32_ota_mutex_release();
releaseMutex();
resetState();
DEBUG_INFO(TAG, "ESP32 OTA 已中止并清理");
}
@@ -351,7 +385,7 @@ void Esp32OtaCore::abort()
*
* 用于新通道(WiFi/BLE)发现锁被残留占用时,主动走完整的状态机退出流程:
* 1. 若会话活跃(m_active)→ 走 abort() 完整清理(esp_ota_abort + 释放锁 + 复位状态)
* 2. 若会话不活跃但锁被占(g_esp32OtaOccupied)→ 仅释放残留锁
* 2. 若会话不活跃但锁被占(s_occupied)→ 仅释放残留锁
* 3. 否则无需操作
*
* @return true 执行了清理(调用方可重试 begin),false 无需清理。
@@ -366,16 +400,16 @@ bool Esp32OtaCore::forceCleanup()
}
// 会话不活跃,但锁可能被残留占用
if (!g_esp32OtaMutex) return false;
if (!s_mutex) return false;
// 在互斥保护下检查 g_esp32OtaOccupied
if (xSemaphoreTake(g_esp32OtaMutex, pdMS_TO_TICKS(1000)) != pdTRUE) return false;
// 在互斥保护下检查 s_occupied
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) return false;
bool was_occupied = g_esp32OtaOccupied;
bool was_occupied = s_occupied;
if (was_occupied) {
g_esp32OtaOccupied = false;
s_occupied = false;
}
xSemaphoreGive(g_esp32OtaMutex);
xSemaphoreGive(s_mutex);
if (was_occupied) {
DEBUG_WARN(TAG, "强制清理: 释放残留 OTA 锁(无活跃会话)");
+15
View File
@@ -19,6 +19,8 @@
#include "esp_err.h"
#include "esp_ota_ops.h"
#include "esp_partition.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
class Esp32OtaCore {
public:
@@ -99,9 +101,22 @@ public:
bool isActive() const { return m_active; }
/** @brief 获取已接收的字节数。 */
size_t getReceived() const { return m_received; }
// ── 分区互斥锁管理(M1: 从 ota_mutex.h 全局变量内聚到此类)──
/**
* @brief 初始化 ESP32 OTA 分区互斥锁(在 app_main 中调用)。
* 替代旧的全局 g_esp32OtaMutex / g_esp32OtaOccupied。
*/
static void initMutex();
private:
Esp32OtaCore() = default;
// ── 分区互斥锁(M1: 内聚自 ota_mutex.h)──
static SemaphoreHandle_t s_mutex; ///< ESP32 OTA 分区互斥锁
static volatile bool s_occupied; ///< 分区占用标志
static bool acquireMutex(uint32_t timeout_ms);
static void releaseMutex();
// 从 OTA 分区读回计算 SHA-256(WiFi 风格),结果写入 m_sha256,置 m_shaComputed=true
/**
* @brief 从 OTA 分区读回计算 SHA-256。
+33 -10
View File
@@ -27,7 +27,7 @@ OtaCoordinator& OtaCoordinator::instance()
// ============================================================
// 线程安全辅助
// ============================================================
bool OtaCoordinator::lock()
bool OtaCoordinator::lock() const
{
// ★ 修复: 返回 take 结果,由调用方传回 unlock()。
// 旧实现用成员 bool m_locked 记录 take 结果,多任务并发 lock() 时
@@ -42,7 +42,7 @@ bool OtaCoordinator::lock()
return ok;
}
void OtaCoordinator::unlock(bool handle)
void OtaCoordinator::unlock(bool handle) const
{
// ★ 修复: 仅在对应的 lock() 成功 take 时才 give。
if (handle && m_mutex) {
@@ -266,7 +266,8 @@ esp_err_t OtaCoordinator::triggerGd32Relay()
}
// 向 OtaRelay task 发送启动消息
if (h && !OtaRelay::dispatchQueue) {
QueueHandle_t dq = OtaRelay::getDispatchQueue();
if (h && !dq) {
DEBUG_ERR(TAG, "OtaRelay dispatchQueue 未就绪");
unlock(h);
return ESP_ERR_INVALID_STATE;
@@ -282,7 +283,7 @@ esp_err_t OtaCoordinator::triggerGd32Relay()
DEBUG_INFO(TAG, "触发 GD32 中继, 系统状态 → GD32_RELAYING");
uint32_t cmd = 1;
BaseType_t ret = xQueueSend(OtaRelay::dispatchQueue, &cmd, pdMS_TO_TICKS(200));
BaseType_t ret = xQueueSend(dq, &cmd, pdMS_TO_TICKS(200));
if (ret != pdTRUE) {
DEBUG_ERR(TAG, "向 OtaRelay 发送启动消息失败");
m_sysState = OTA_SYS_GD32_STORED;
@@ -341,6 +342,28 @@ void OtaCoordinator::onRelayComplete(bool success)
notifyRelayComplete(success);
}
// ============================================================
// BLE 射频共存管理(L1: 消除 ble_ota → wifi_ota 直接依赖)
// ============================================================
void OtaCoordinator::setBleCoexListener(IBleCoexListener *listener)
{
m_bleCoexListener = listener;
}
void OtaCoordinator::notifyBleCoexStart()
{
if (m_bleCoexListener) {
m_bleCoexListener->onBleOtaStart();
}
}
void OtaCoordinator::notifyBleCoexEnd()
{
if (m_bleCoexListener) {
m_bleCoexListener->onBleOtaEnd();
}
}
// ============================================================
// ESP32 OTA 阶段
// ============================================================
@@ -511,10 +534,10 @@ void OtaCoordinator::abortCurrent()
ota_system_state_t OtaCoordinator::getSystemState() const
{
// 对于纯读取,短暂加锁保证一致性
OtaCoordinator* self = const_cast<OtaCoordinator*>(this);
bool h = self->lock();
// ★ M1/L3: m_mutex 为 mutable + lock()/unlock() 为 const,无需 const_cast
bool h = lock();
ota_system_state_t s = m_sysState;
self->unlock(h);
unlock(h);
return s;
}
@@ -569,10 +592,10 @@ bool OtaCoordinator::getGd32AppAlive() const
/** @brief 获取 GD32 固件是否已存储完成(线程安全)。 */
bool OtaCoordinator::isGd32StorageDone() const
{
OtaCoordinator* self = const_cast<OtaCoordinator*>(this);
bool h = self->lock();
// ★ M1/L3: m_mutex 为 mutable + lock()/unlock() 为 const,无需 const_cast
bool h = lock();
bool done = m_gd32StorageDone;
self->unlock(h);
unlock(h);
return done;
}
+43 -4
View File
@@ -37,6 +37,25 @@ typedef enum {
OTA_SYS_ERROR // 错误状态
} ota_system_state_t;
// ============================================================
// IBleCoexListener - BLE 射频共存通知接口(解决 L1 横向耦合)
// ============================================================
// WifiOta 实现此接口并注册到 OtaCoordinator,
// BLE OTA 开始/结束时通过协调器通知 WiFi 层,
// 消除 ble_ota → wifi_ota 的直接依赖。
/**
* @brief BLE 射频共存监听器接口。
* BLE OTA 开始/结束时,协调器通过此接口通知 WiFi 层暂停/恢复射频活动。
*/
class IBleCoexListener {
public:
virtual ~IBleCoexListener() = default;
/** @brief BLE OTA 开始,暂停 WiFi RSSI 轮询等射频活动。 */
virtual void onBleOtaStart() = 0;
/** @brief BLE OTA 结束,恢复 WiFi 射频活动。 */
virtual void onBleOtaEnd() = 0;
};
/**
* @brief ESP32 OTA 收尾结果(由 finalizeForReboot 返回)。
* err 为 ESP_OK 时 device_sha_hex 有效;SHA 不匹配时也填充 device_sha_hex 供前端核对。
@@ -295,6 +314,23 @@ public:
*/
void onRelayComplete(bool success) override;
// ── BLE 射频共存管理(L1: 替代 ble_ota → wifi_ota 直接调用)──
/**
* @brief 注册 BLE 共存监听器(由 main 在初始化时调用)。
* @param listener WifiOta 实现的监听器指针,nullptr 取消注册。
*/
void setBleCoexListener(IBleCoexListener *listener);
/**
* @brief BLE OTA 开始时调用,通知 WiFi 层暂停 RSSI 轮询。
*/
void notifyBleCoexStart();
/**
* @brief BLE OTA 结束时调用,通知 WiFi 层恢复射频活动。
*/
void notifyBleCoexEnd();
private:
OtaCoordinator() = default;
~OtaCoordinator() = default;
@@ -306,8 +342,8 @@ private:
// 旧实现用共享 bool m_locked 记录 take 结果,多任务并发 lock() 时
// 超时方会把 m_locked 置 false,覆盖持锁方的 true,导致 unlock() 跳过 give,
// mutex 永久死锁 → 后续 take 超时触发 vTaskPriorityDisinherit assert → PANIC。
bool lock();
void unlock(bool handle);
bool lock() const;
void unlock(bool handle) const;
// ★ 卡死自愈检查:若忙状态停留超过 watchdog 超时,强制转为 ERROR。
// 用于在中继 task 因任何原因(崩溃/SPI 死锁/看门狗复位残留)未能回调
@@ -324,8 +360,11 @@ private:
// GD32 存储跟踪
bool m_gd32StorageDone = false;
// 互斥锁
SemaphoreHandle_t m_mutex = nullptr;
// 互斥锁(M1/L3: mutable 允许在 const 方法中加锁,消除 const_cast)
mutable SemaphoreHandle_t m_mutex = nullptr;
// BLE 共存监听器(L1: 由 WifiOta 实现并注册)
IBleCoexListener *m_bleCoexListener = nullptr;
static constexpr TickType_t kLockTimeout = pdMS_TO_TICKS(100);
// ★ 忙状态 watchdog 超时。GD32 中继理论最大耗时 ≈ 60s(全局超时) + 15s(APP存活轮询) + 1s(bootloader) ≈ 76s,