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:
+54
-20
@@ -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 锁(无活跃会话)");
|
||||
|
||||
@@ -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。
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user