- 启用BLE连接事件长度优化,允许单CE内传输多包,提升OTA吞吐 - 缩减NimBLE mbuf池配置,单连接OTA场景节省约7KB内存 - 禁用未使用的标准GATT服务,仅保留自定义OTA service,节省8-15KB - 缩减NimBLE传输缓冲区配置以优化内存使用 refactor(debug_log): 重构环形日志缓冲区API以支持逐条弹出 - 新增ringPopOldest方法,支持弹出最旧日志并从缓冲区移除 - 重构ringDrainJson实现,基于新的逐条弹出机制 - 改进日志转义处理逻辑,支持更多特殊字符转义 perf(tasks): 优化各任务栈大小配置以节省内存 - 将ota_relay任务栈从12288字节缩减至6144字节 - 将ble_ota任务栈从8192字节缩减至4096字节 - 将ble_ota数据工作线程栈从8192字节缩减至4096字节 - 通过栈高水位监控验证实际使用情况,确保安全余量 feat(monitor): 添加任务句柄获取接口以支持系统状态监控 - 为HealthMonitor、OtaRelay、BleOta和WifiOta类添加任务句柄获取方法 - 在任务入口处记录当前任务句柄 - 便于系统状态监控和调试信息收集 feat(status): 扩展系统状态API以提供更详细的内存和任务信息 - 增加内部堆和默认堆的详细统计信息 - 添加各关键任务的栈使用情况监控 - 扩展/system/status端点返回更全面的系统资源使用情况 - 包含最大空闲块、最小空闲内存等关键指标
349 lines
12 KiB
C++
349 lines
12 KiB
C++
/*
|
||
* health_monitor.cpp - 系统健康监控
|
||
* 包含:健康状态管理 + WiFi RSSI 监控 + 温度监控
|
||
*
|
||
* 所有功能封装为 HealthMonitor 类方法。
|
||
* 所有对 m_healthState / m_lastTemperature / m_lastWifiRssi 的访问
|
||
* 均通过 m_mutex 保护,消除跨任务竞态。
|
||
*/
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include "freertos/FreeRTOS.h"
|
||
#include "freertos/task.h"
|
||
#include "freertos/semphr.h"
|
||
#include "esp_wifi.h"
|
||
#include "esp_timer.h"
|
||
#include "esp_err.h"
|
||
#include "driver/temperature_sensor.h"
|
||
|
||
#include "health_monitor.h"
|
||
#include "debug_log.h"
|
||
#include "server/wifi_ota.h"
|
||
|
||
// ============================================================
|
||
// 单例
|
||
// ============================================================
|
||
/**
|
||
* @brief 获取 HealthMonitor 单例实例。
|
||
* @return HealthMonitor 引用。
|
||
*/
|
||
HealthMonitor& HealthMonitor::instance()
|
||
{
|
||
static HealthMonitor inst;
|
||
return inst;
|
||
}
|
||
|
||
// ============================================================
|
||
// 初始化
|
||
// ============================================================
|
||
/**
|
||
* @brief 初始化健康监控模块,创建内部互斥锁与基础状态。
|
||
*/
|
||
void HealthMonitor::init()
|
||
{
|
||
m_mutex = xSemaphoreCreateMutex();
|
||
if (m_mutex == NULL) {
|
||
DEBUG_ERR("HEALTH", "创建健康状态互斥锁失败");
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 线程安全的健康状态读写
|
||
// ============================================================
|
||
/**
|
||
* @brief 加锁读取当前健康状态。
|
||
* @return 当前健康状态。
|
||
*/
|
||
health_state_t HealthMonitor::getState()
|
||
{
|
||
if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY);
|
||
health_state_t s = m_healthState;
|
||
if (m_mutex) xSemaphoreGive(m_mutex);
|
||
return s;
|
||
}
|
||
|
||
/**
|
||
* @brief 加锁设置健康状态。
|
||
* @param newState 新的健康状态。
|
||
*/
|
||
void HealthMonitor::setState(health_state_t newState)
|
||
{
|
||
if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY);
|
||
m_healthState = newState;
|
||
if (m_mutex) xSemaphoreGive(m_mutex);
|
||
}
|
||
|
||
// ============================================================
|
||
// 获取当前健康状态(public 接口)
|
||
// ============================================================
|
||
/**
|
||
* @brief 获取当前系统健康状态(线程安全)。
|
||
* @return 当前健康状态。
|
||
*/
|
||
health_state_t HealthMonitor::getCurrent()
|
||
{
|
||
return getState();
|
||
}
|
||
|
||
/**
|
||
* @brief 获取最近一次芯片温度。
|
||
* @return 温度值(摄氏度)。
|
||
*/
|
||
float HealthMonitor::getLastTemperature()
|
||
{
|
||
if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY);
|
||
float t = m_lastTemperature;
|
||
if (m_mutex) xSemaphoreGive(m_mutex);
|
||
return t;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取最近一次 WiFi RSSI 及其年龄。
|
||
* @param out_rssi 输出参数,返回 RSSI 值。
|
||
* @param out_age_ms 输出参数,返回样本年龄(毫秒)。
|
||
* @return 是否有有效数据。
|
||
*/
|
||
bool HealthMonitor::getLastWifiRssi(int8_t *out_rssi, uint32_t *out_age_ms)
|
||
{
|
||
if (m_mutex) xSemaphoreTake(m_mutex, portMAX_DELAY);
|
||
int64_t lastMs = m_lastWifiRssiMs;
|
||
int8_t rssi = m_lastWifiRssi;
|
||
if (m_mutex) xSemaphoreGive(m_mutex);
|
||
|
||
if (lastMs <= 0) {
|
||
return false;
|
||
}
|
||
|
||
int64_t now_ms = esp_timer_get_time() / 1000;
|
||
uint32_t age_ms = (now_ms > lastMs) ? (uint32_t)(now_ms - lastMs) : 0;
|
||
|
||
if (out_rssi) *out_rssi = rssi;
|
||
if (out_age_ms) *out_age_ms = age_ms;
|
||
return true;
|
||
}
|
||
|
||
// ============================================================
|
||
// 温度监控(定时器回调)
|
||
// ============================================================
|
||
/**
|
||
* @brief 温度监控定时器回调函数。
|
||
* @param arg 用户参数(未使用)。
|
||
*/
|
||
void HealthMonitor::tempMonitorCallback(void *arg)
|
||
{
|
||
auto& self = instance();
|
||
float temp = 0.0f;
|
||
esp_err_t err = temperature_sensor_get_celsius(self.m_tempHandle, &temp);
|
||
if (err != ESP_OK) {
|
||
DEBUG_WARN("TEMP", "读取温度失败: %s", esp_err_to_name(err));
|
||
return;
|
||
}
|
||
|
||
if (self.m_mutex) xSemaphoreTake(self.m_mutex, portMAX_DELAY);
|
||
self.m_lastTemperature = temp;
|
||
if (self.m_mutex) xSemaphoreGive(self.m_mutex);
|
||
|
||
if (temp >= TEMP_CRITICAL) {
|
||
self.setState(SYS_SAFE_SHUTDOWN);
|
||
DEBUG_ERR("TEMP", "!!! 危险 !!! 温度 %.1f°C >= %d°C,触发安全关机", temp, TEMP_CRITICAL);
|
||
DEBUG_ERR("TEMP", "健康状态变更为: SYS_SAFE_SHUTDOWN");
|
||
} else if (temp >= TEMP_WARNING) {
|
||
health_state_t cur = self.getState();
|
||
if (cur < SYS_WARNING) {
|
||
self.setState(SYS_WARNING);
|
||
DEBUG_WARN("TEMP", "警告: 温度 %.1f°C >= %d°C", temp, TEMP_WARNING);
|
||
DEBUG_WARN("TEMP", "健康状态变更为: SYS_WARNING");
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// FreeRTOS 任务入口
|
||
// ============================================================
|
||
/**
|
||
* @brief 健康监控 FreeRTOS 任务入口。
|
||
* @param pvParameters 任务参数。
|
||
*/
|
||
void HealthMonitor::taskEntry(void *pvParameters)
|
||
{
|
||
(void)pvParameters;
|
||
auto& self = instance();
|
||
self.m_taskHandle = xTaskGetCurrentTaskHandle();
|
||
DEBUG_INFO("MON", "=== 健康监控任务启动 ===");
|
||
|
||
temperature_sensor_config_t temp_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(20, 80);
|
||
esp_err_t err = temperature_sensor_install(&temp_config, &self.m_tempHandle);
|
||
if (err != ESP_OK) {
|
||
DEBUG_ERR("MON", "温度传感器初始化失败: %s", esp_err_to_name(err));
|
||
} else {
|
||
ESP_ERROR_CHECK(temperature_sensor_enable(self.m_tempHandle));
|
||
DEBUG_INFO("MON", "温度传感器已启用 (范围 20°C ~ 80°C)");
|
||
}
|
||
|
||
// 定时器,每1秒读取一次温度
|
||
esp_timer_handle_t temp_timer;
|
||
esp_timer_create_args_t timer_args = {};
|
||
timer_args.callback = &tempMonitorCallback;
|
||
timer_args.arg = nullptr;
|
||
timer_args.name = "temp_monitor";
|
||
timer_args.dispatch_method = ESP_TIMER_TASK;
|
||
|
||
if (esp_timer_create(&timer_args, &temp_timer) == ESP_OK) {
|
||
esp_timer_start_periodic(temp_timer, 1000000); // 1秒
|
||
DEBUG_INFO("MON", "温度监控定时器已启动 (1秒间隔)");
|
||
} else {
|
||
DEBUG_ERR("MON", "创建温度监控定时器失败");
|
||
}
|
||
|
||
// 周期性巡检
|
||
while (1) {
|
||
uint32_t free_heap = esp_get_free_heap_size();
|
||
if (free_heap < HEAP_WARNING_MIN) {
|
||
uint32_t largest_block = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
|
||
DEBUG_WARN("MON", "堆内存不足: free=%lu min=%lu largest_blk=%lu",
|
||
free_heap, esp_get_minimum_free_heap_size(), largest_block);
|
||
}
|
||
health_state_t cur = self.getState();
|
||
if (cur >= SYS_CRITICAL) {
|
||
DEBUG_ERR("MON", "健康状态危险: %d", cur);
|
||
}
|
||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||
}
|
||
}
|
||
|
||
// ============================================================
|
||
// 健康事件日志
|
||
// ============================================================
|
||
/**
|
||
* @brief 记录一条健康事件日志。
|
||
* @param event 事件名称。
|
||
* @param value 事件值。
|
||
*/
|
||
void HealthMonitor::logEvent(const char *event, int value)
|
||
{
|
||
DEBUG_INFO("HEALTH", "[健康事件] %s: %d", event, value);
|
||
}
|
||
|
||
// ============================================================
|
||
// OTA 健康检查
|
||
// ============================================================
|
||
/**
|
||
* @brief OTA 传输前执行健康检查。
|
||
* @return 健康检查状态。
|
||
*/
|
||
health_status_t HealthMonitor::checkBeforeOta()
|
||
{
|
||
health_status_t status = HEALTH_MON_OK;
|
||
health_state_t cur = getState();
|
||
|
||
if (cur >= SYS_SAFE_SHUTDOWN) {
|
||
DEBUG_WARN("HEALTH", "OTA 前检查: SYS_SAFE_SHUTDOWN,禁止传输");
|
||
return HEALTH_MON_SHUTDOWN;
|
||
} else if (cur >= SYS_CRITICAL) {
|
||
DEBUG_WARN("HEALTH", "OTA 前检查: SYS_CRITICAL,禁止传输");
|
||
return HEALTH_MON_CRITICAL;
|
||
} else if (cur >= SYS_WARNING) {
|
||
status = HEALTH_MON_WARNING;
|
||
DEBUG_WARN("HEALTH", "OTA 前检查: SYS_WARNING,继续传输但需注意");
|
||
}
|
||
|
||
if (!WifiOta::isConnected()) {
|
||
DEBUG_WARN("HEALTH", "OTA 前检查: WiFi 未连接");
|
||
return HEALTH_MON_CRITICAL;
|
||
}
|
||
|
||
int8_t last_rssi = -127;
|
||
uint32_t age_ms = 0;
|
||
if (getLastWifiRssi(&last_rssi, &age_ms)) {
|
||
if (age_ms <= OTA_WIFI_RSSI_SAMPLE_MAX_AGE_MS) {
|
||
if (last_rssi < OTA_WIFI_RSSI_CRITICAL) {
|
||
DEBUG_WARN("HEALTH", "OTA 前检查: 最近 RSSI %d < %d (样本年龄 %lu ms)",
|
||
last_rssi, OTA_WIFI_RSSI_CRITICAL, (unsigned long)age_ms);
|
||
return HEALTH_MON_CRITICAL;
|
||
} else if (last_rssi < WIFI_RSSI_NORMAL) {
|
||
if (status < HEALTH_MON_WARNING) {
|
||
status = HEALTH_MON_WARNING;
|
||
}
|
||
DEBUG_WARN("HEALTH", "OTA 前检查: 最近 RSSI %d < %d (样本年龄 %lu ms)",
|
||
last_rssi, WIFI_RSSI_NORMAL, (unsigned long)age_ms);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (status == HEALTH_MON_OK) {
|
||
DEBUG_DBG("HEALTH", "OTA 前检查: 正常");
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
/**
|
||
* @brief OTA 中继转发阶段执行健康检查(跳过 WiFi 检查)。
|
||
* @return 健康检查状态。
|
||
*/
|
||
health_status_t HealthMonitor::checkBeforeOtaRelay()
|
||
{
|
||
health_status_t status = HEALTH_MON_OK;
|
||
health_state_t cur = getState();
|
||
|
||
if (cur >= SYS_SAFE_SHUTDOWN) {
|
||
DEBUG_WARN("HEALTH", "OTA 中继检查: SYS_SAFE_SHUTDOWN,禁止传输");
|
||
return HEALTH_MON_SHUTDOWN;
|
||
} else if (cur >= SYS_CRITICAL) {
|
||
/* SYS_CRITICAL 可能是由 WiFi RSSI 过低导致的,
|
||
* 但 SPI 中继阶段不依赖 WiFi,只检查温度/硬件等本地状态。
|
||
* 温度达到 TEMP_CRITICAL(100°C) 时 m_healthState 会被设为
|
||
* SYS_SAFE_SHUTDOWN,在上面的分支已经拦截。
|
||
* 所以 SYS_CRITICAL 在中继阶段只可能是 WiFi 导致的,允许继续。 */
|
||
DEBUG_WARN("HEALTH", "OTA 中继检查: SYS_CRITICAL (WiFi 相关),SPI 中继允许继续");
|
||
} else if (cur >= SYS_WARNING) {
|
||
status = HEALTH_MON_WARNING;
|
||
DEBUG_WARN("HEALTH", "OTA 中继检查: SYS_WARNING,继续传输");
|
||
}
|
||
|
||
/* 跳过 WiFi 连接状态和 RSSI 检查:
|
||
* SPI 中继阶段固件已存储在 ESP32 Flash 中,
|
||
* 不依赖 WiFi 连接,无需检查 WiFi 状态。 */
|
||
|
||
if (status == HEALTH_MON_OK) {
|
||
DEBUG_DBG("HEALTH", "OTA 中继检查: 正常");
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
// ============================================================
|
||
// WiFi RSSI 更新
|
||
// ============================================================
|
||
/**
|
||
* @brief 根据 WiFi RSSI 更新健康状态。
|
||
* @param rssi WiFi RSSI 值。
|
||
* @return 健康检查状态。
|
||
*/
|
||
health_status_t HealthMonitor::updateFromWifiRssi(int8_t rssi)
|
||
{
|
||
if (rssi < WIFI_RSSI_CRITICAL) {
|
||
setState(SYS_CRITICAL);
|
||
DEBUG_WARN("HEALTH", "WiFi RSSI 危险: %d dBm < %d dBm", rssi, WIFI_RSSI_CRITICAL);
|
||
logEvent("WIFI危险", rssi);
|
||
return HEALTH_MON_CRITICAL;
|
||
} else if (rssi < WIFI_RSSI_NORMAL) {
|
||
health_state_t cur = getState();
|
||
if (cur < SYS_WARNING) {
|
||
setState(SYS_WARNING);
|
||
DEBUG_WARN("HEALTH", "WiFi RSSI 警告: %d dBm < %d dBm", rssi, WIFI_RSSI_NORMAL);
|
||
logEvent("WIFI警告", rssi);
|
||
}
|
||
return HEALTH_MON_WARNING;
|
||
}
|
||
|
||
health_state_t cur = getState();
|
||
if (cur == SYS_WARNING && rssi >= WIFI_RSSI_NORMAL) {
|
||
setState(SYS_NORMAL);
|
||
DEBUG_INFO("HEALTH", "WiFi RSSI 恢复: %d dBm >= %d dBm", rssi, WIFI_RSSI_NORMAL);
|
||
logEvent("WIFI恢复", rssi);
|
||
}
|
||
|
||
return HEALTH_MON_OK;
|
||
}
|