#include "auto_alerts.h" #include "esp_check.h" #include "esp_log.h" #include "esp_timer.h" #include "freertos/FreeRTOS.h" static const char *TAG = "auto_alerts"; // 日志标签 // 用于保护全局状态的自旋锁(临界区) static portMUX_TYPE s_alerts_lock = portMUX_INITIALIZER_UNLOCKED; // 用户注册的回调函数 static auto_alert_callback_t s_callback = NULL; // 回调函数的用户上下文指针 static void *s_user_ctx = NULL; // 土壤湿度告警是否已激活 static bool s_soil_alarm_active = false; // 光照强度告警是否已激活 static bool s_light_alarm_active = false; /** * @brief 发送自动告警事件 * * @param metric 告警指标类型(如土壤湿度、光照强度) * @param state 告警状态(告警或恢复正常) * @param value 当前测量值 * @param threshold 触发告警的阈值 */ static void auto_alerts_emit(auto_alert_metric_t metric, auto_alert_state_t state, float value, float threshold) { auto_alert_event_t event = { .metric = metric, .state = state, .value = value, .threshold = threshold, .timestamp_ms = esp_timer_get_time() / 1000, // 转换为毫秒时间戳 }; auto_alert_callback_t callback = NULL; void *user_ctx = NULL; // 进入临界区,安全读取回调和上下文 taskENTER_CRITICAL(&s_alerts_lock); callback = s_callback; user_ctx = s_user_ctx; taskEXIT_CRITICAL(&s_alerts_lock); if (callback != NULL) { callback(&event, user_ctx); // 调用用户注册的回调函数 } // 打印日志信息 ESP_LOGI(TAG, "alert metric=%d state=%d value=%.1f threshold=%.1f", (int)event.metric, (int)event.state, event.value, event.threshold); } /** * @brief 初始化自动告警模块 * * 将所有告警状态重置为未激活。 */ void auto_alerts_init(void) { taskENTER_CRITICAL(&s_alerts_lock); s_soil_alarm_active = false; s_light_alarm_active = false; taskEXIT_CRITICAL(&s_alerts_lock); } /** * @brief 注册自动告警回调函数 * * @param callback 用户定义的回调函数 * @param user_ctx 用户上下文指针 * @return esp_err_t 总是返回 ESP_OK */ esp_err_t auto_alerts_register_callback(auto_alert_callback_t callback, void *user_ctx) { taskENTER_CRITICAL(&s_alerts_lock); s_callback = callback; s_user_ctx = user_ctx; taskEXIT_CRITICAL(&s_alerts_lock); return ESP_OK; } /** * @brief 根据当前传感器数据和阈值评估是否触发或解除告警 * * @param soil_valid 土壤湿度数据是否有效 * @param soil_moisture_pct 当前土壤湿度百分比 * @param light_valid 光照数据是否有效 * @param light_lux 当前光照强度(单位:lux) * @param thresholds 自动控制阈值配置结构体指针 */ void auto_alerts_evaluate(bool soil_valid, float soil_moisture_pct, bool light_valid, float light_lux, const auto_ctrl_thresholds_t *thresholds) { if (thresholds == NULL) { return; // 阈值为空,直接返回 } // 处理土壤湿度告警逻辑 if (soil_valid) { bool emit_alarm = false; // 是否需要触发告警 bool emit_recover = false; // 是否需要恢复通知 taskENTER_CRITICAL(&s_alerts_lock); // 如果当前未告警,且土壤湿度低于启动水泵的阈值,则触发告警 if (!s_soil_alarm_active && soil_moisture_pct < thresholds->pump_on_soil_below_pct) { s_soil_alarm_active = true; emit_alarm = true; } // 如果当前处于告警状态,且土壤湿度高于关闭水泵的阈值,则恢复 else if (s_soil_alarm_active && soil_moisture_pct > thresholds->pump_off_soil_above_pct) { s_soil_alarm_active = false; emit_recover = true; } taskEXIT_CRITICAL(&s_alerts_lock); if (emit_alarm) { auto_alerts_emit(AUTO_ALERT_METRIC_SOIL_MOISTURE, AUTO_ALERT_STATE_ALARM, soil_moisture_pct, thresholds->pump_on_soil_below_pct); } if (emit_recover) { auto_alerts_emit(AUTO_ALERT_METRIC_SOIL_MOISTURE, AUTO_ALERT_STATE_NORMAL, soil_moisture_pct, thresholds->pump_off_soil_above_pct); } } // 处理光照强度告警逻辑 if (light_valid) { bool emit_alarm = false; // 是否需要触发告警 bool emit_recover = false; // 是否需要恢复通知 taskENTER_CRITICAL(&s_alerts_lock); // 如果当前未告警,且光照强度低于开启补光灯的阈值,则触发告警 if (!s_light_alarm_active && light_lux < thresholds->light_on_lux_below) { s_light_alarm_active = true; emit_alarm = true; } // 如果当前处于告警状态,且光照强度高于关闭补光灯的阈值,则恢复 else if (s_light_alarm_active && light_lux > thresholds->light_off_lux_above) { s_light_alarm_active = false; emit_recover = true; } taskEXIT_CRITICAL(&s_alerts_lock); if (emit_alarm) { auto_alerts_emit(AUTO_ALERT_METRIC_LIGHT_INTENSITY, AUTO_ALERT_STATE_ALARM, light_lux, thresholds->light_on_lux_below); } if (emit_recover) { auto_alerts_emit(AUTO_ALERT_METRIC_LIGHT_INTENSITY, AUTO_ALERT_STATE_NORMAL, light_lux, thresholds->light_off_lux_above); } } }