/* * Work Control Module - v2.3 (修复版) * * 修复:工作结束后必须等待天亮才能再次工作 * 优化:保护逻辑简化版 * * Version: 2.3.1 */ /*********************↓ include ↓************************/ #define DBG_TAG "WORK" #define DBG_LVL DBG_INFO #include #include #include #include #include #include #include "ecode.h" #include "ym310_protecl.h" /*********************↑ include ↑************************/ /*********************↓ define ↓************************/ /* 工作线程基础时间片(毫秒)*/ #define WORK_BASE_MS 100 /* ========== 光控阈值定义 ========== */ // #define LIGHT_VAL_NIGHT_THRESHOLD 50 /* 夜晚阈值:≤5%亮度 */ // #define LIGHT_VAL_DAY_THRESHOLD 150 /* 白天阈值:≥15%亮度 */ #define LIGHT_VAL_NIGHT_THRESHOLD 20 /* 夜晚阈值:≤2%亮度 */ #define LIGHT_VAL_DAY_THRESHOLD 150 /* 白天阈值:≥15%亮度 */ #define PV_VOLTAGE_NIGHT_THRESHOLD limit.vsunset /* 夜晚阈值:太阳能板电压≤1.5V */ #define PV_VOLTAGE_DAY_THRESHOLD limit.vsunrise /* 白天阈值:太阳能板电压≥3.3V */ /* ========== 状态转换延迟参数 ========== */ #define LIGHT_DEBOUNCE_SEC 10 /* 光控状态防抖10秒 */ #define WORK_COOLDOWN_SEC 300 /* 工作结束后冷却5分钟 */ #define PROTECT_RELEASE_DELAY_SEC 5 /* 保护解除后延迟5秒再恢复工作能力 */ #define LIGHT_BREAK_WORK_SEC 300 /* 持续光控保护 到达后 退出本次工作 */ /* ========== 工作状态定义 ========== */ typedef enum { WORK_STATE_IDLE = 0, WORK_STATE_WORKING, WORK_STATE_LIGHT_PRT, WORK_STATE_COOLDOWN, WORK_STATE_WAIT_DAY, WORK_STATE_OTHER_PRT } Work_status; /* 保护激活标志值 */ #define PROTECT_ACTIVE 0x9D /* ========== RTC备份寄存器地址定义 ========== */ #define RTC_BKP_WORK_A RTC_BKP_DR1 /* DR1: 工作开始时间戳 */ #define RTC_BKP_WORK_B RTC_BKP_DR2 /* DR2: 工作持续时间秒数 */ #define RTC_BKP_WORK_C RTC_BKP_DR3 /* DR3: 打包状态寄存器 */ #define RTC_BKP_WORK_D RTC_BKP_DR4 /* DR4: 打包状态寄存器 */ #define RTC_BKP_WORK_E RTC_BKP_DR5 /* DR5: 打包状态寄存器 */ /* 打包状态寄存器格式 */ #define RTC_WORK_VALID_MAGIC 0xA5U /* 魔数,用于校验数据有效性 */ /*********************↑ define ↑************************/ /*********************↓ 外部变量声明 ↓***********************/ extern SYS_STATUS sys_sta; extern DEV_INFO dev_info; extern struct rtc_time time_now; extern void gpio_lamp_ctrl(rt_bool_t on); extern void gpio_hv_ctrl(rt_bool_t on); extern char ef_cfged; extern RTC_HandleTypeDef hrtc; extern char start_ftp; /*********************↑ 外部变量声明 ↑***********************/ /*********************↓ 全局变量(供MQTT上报) ↓***********************/ extern uint8_t g_work_state; extern uint8_t g_work_mode; extern uint32_t g_work_elapsed_sec; extern uint32_t g_work_total_sec; extern uint8_t g_work_progress; extern uint32_t g_cooldown_sec; extern uint8_t g_work_start_hour; extern uint8_t g_work_start_minute; extern char KEY_IN_TEST_TIME; /*********************↑ 全局变量 ↑***********************/ /*********************↓ 静态变量 ↓***********************/ static rt_thread_t s_thread = RT_NULL; /* 工作线程句柄 */ static rt_bool_t s_running = RT_FALSE; /* 线程运行标志 */ /* ========== 工作状态核心变量 ========== */ /** * @brief 以下这些放rtc里面 * */ static uint8_t s_state = WORK_STATE_IDLE; /* 当前工作状态 */ static uint8_t s_light_protect = 0; /* 光控保护状态 */ static uint8_t s_start_hour = 0; /* 工作开始小时 */ static uint8_t s_start_minute = 0; /* 工作开始分钟 */ // 寄存器1 static uint32_t s_start_time = 0; /* 工作开始时间戳(秒)*/ // 开启时间 // 寄存器2 static uint32_t s_duration_sec = 0; /* 工作持续时长(秒)*/ // 需要工作时长 // 寄存器3 static uint32_t s_cooldown_until = 0; /* 冷却结束时间戳(秒)*/ // 寄存器4 /** * 先不放rtc里面 这个可以根据参数还原 */ static uint8_t s_auto_mode = 0; /* 自动模式标志 */ /* ========== 光控保护状态(带防抖)========== */ /* ========== 保护相关变量(简化版)========== */ static uint32_t s_protect_release_time = 0; /* 保护解除后可以恢复工作的时间戳 */ static uint32_t light_prt_start_time = 0; // 光控触发后的时间计算 /* ========== GPIO状态缓存 ========== */ /*********************↑ 静态变量 ↑***********************/ /*********************↓ 函数声明 ↓***********************/ static uint32_t get_timestamp(void); static void sync_mqtt_vars(void); static void safe_start(char type); static int in_time_range(void); static uint32_t get_elapsed_sec(void); static void work_start(void); static void work_stop(const char *reason, uint8_t start_cooldown); static uint8_t is_duration_expired(void); static uint8_t other_protect_active(void); static const char *other_protect_name(void); uint8_t calculate_hours_diff(uint8_t start, uint8_t ent); /*********************↑ 函数声明 ↑***********************/ /*********************↓ RTC备份寄存器存储方案 ↓***********************/ uint32_t crc32_update(uint32_t data, uint32_t *crc) { if (crc == NULL) return 0; if (*crc == 0) { *crc = 0xFFFFFFFFUL; } for (int byte_idx = 3; byte_idx >= 0; byte_idx--) { uint8_t byte = (data >> (byte_idx * 8)) & 0xFF; *crc ^= byte; for (int bit = 0; bit < 8; bit++) { if (*crc & 1) { *crc = (*crc >> 1) ^ 0xEDB88320UL; } else { *crc >>= 1; } } } return *crc ^ 0xFFFFFFFFUL; } // /** // * 计算两个时间点之间的小时差,支持跨天(24小时制) // * @param start 开始时间(小时,0-23) // * @param end 结束时间(小时,0-23) // * @return 从 start 到 end 经过的小时数(0-23) // */ // uint8_t calculate_hours_diff(uint8_t start, uint8_t end) // { // // 参数合法性检查(可根据需要选择是否启用) // if (start < 0 || start > 23 || end < 0 || end > 23) // { // return -1; // 返回错误码 // } // if (start <= end) // { // // 同一天内:直接相减 // return end - start; // } // else // { // // 跨天:(到当天结束的小时数) + (第二天开始到 end 的小时数) // return (24 - start) + end; // } // } /** * 计算两个时间点之间的小时差,支持跨天(24小时制) * @param start 开始时间(小时,0-23) * @param end 结束时间(小时,0-23) * @return 从 start 到 end 经过的小时数(0-24) */ uint8_t calculate_hours_diff(uint8_t start, uint8_t end) { if (start == 0 && end == 23) { return 24; } if (start <= end) { return end - start; } else { return (24 - start) + end; } } /** * @brief 将工作状态打包到单个32位寄存器 */ static uint32_t pack_work_state(void) { return ((uint32_t)s_state << 24) | ((uint32_t)s_light_protect << 16) | ((uint32_t)s_start_hour << 8) | ((uint32_t)s_start_minute); } /** * @brief 从32位寄存器解包工作状态 */ static int unpack_work_state(uint32_t packed) { s_state = (packed >> 24) & 0xFF; s_light_protect = (packed >> 16) & 0xFF; s_start_hour = (packed >> 8) & 0xFF; s_start_minute = packed & 0xFF; return 1; } /** * @brief 保存工作状态到RTC备份寄存器 */ static void save_work_state(void) { uint32_t crc = 0, final_crc = 0, packeg = 0; static uint32_t e = 0; packeg = pack_work_state(); final_crc = crc32_update(packeg, &crc); final_crc = crc32_update(s_start_time, &crc); final_crc = crc32_update(s_duration_sec, &crc); final_crc = crc32_update(s_cooldown_until, &crc); e = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_E); if (e != final_crc) { HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_A, pack_work_state()); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_B, s_start_time); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_C, s_duration_sec); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_D, s_cooldown_until); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_E, final_crc); LOG_I("工作状态已更新到RTC: time=%u, dur=%lu, CRC = 0x%08X", s_start_time, s_duration_sec, final_crc); e = final_crc; } } /** * @brief 清除RTC备份寄存器中的工作状态 */ static void clear_work_state(void) { HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_A, 0); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_B, 0); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_C, 0); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_D, 0); HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_E, 0); LOG_D("工作状态已清除"); } /** * @brief 从RTC备份寄存器恢复工作状态 */ static int restore_work_state(void) { uint32_t crc = 0, final_crc = 0, a = 0, b = 0, c = 0, d = 0, e = 0; a = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_A); b = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_B); c = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_C); d = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_D); e = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_E); final_crc = crc32_update(a, &crc); final_crc = crc32_update(b, &crc); final_crc = crc32_update(c, &crc); final_crc = crc32_update(d, &crc); LOG_I("读取RTC: time=%u, dur=%lu, CRC = 0x%08X", b, c, e); if (final_crc != e) { LOG_I("无有效工作状态数据(CRC FAIL) %d", (final_crc != e)); return 0; } if (!unpack_work_state(a)) { LOG_W("工作状态数据校验失败"); return 0; } if (s_auto_mode > 1) { LOG_W("自动模式标志无效"); return 0; } s_start_time = b; s_duration_sec = c; s_cooldown_until = d; LOG_I("成功恢复工作状态: 开始时间=%u, 持续=%lu秒, 自动模式=%d", s_start_time, s_duration_sec, s_auto_mode); return 1; } /*********************↑ RTC备份寄存器存储方案 ↑***********************/ /*********************↓ 辅助函数 ↓***********************/ /** * @brief 获取当前Unix时间戳 */ static uint32_t get_timestamp(void) { uint32_t ts = 0; for (uint16_t y = 2000; y < time_now.year; y++) { ts += ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) ? 366 : 365; } ts *= 86400; static const uint16_t md[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; ts += (md[time_now.month - 1] + time_now.date - 1) * 86400; if ((time_now.year % 4 == 0 && time_now.year % 100 != 0) && time_now.month > 2) { ts += 86400; } ts += time_now.hour * 3600 + time_now.minute * 60 + time_now.second; return ts; } /** * @brief 同步MQTT上报变量 */ static void sync_mqtt_vars(void) { g_work_state = s_state; g_work_mode = (s_state == WORK_STATE_WORKING) ? 1 : 0; if (s_state == WORK_STATE_WORKING && s_start_time > 0) { uint32_t now = get_timestamp(); g_work_elapsed_sec = (now > s_start_time) ? now - s_start_time : 0; } else { g_work_elapsed_sec = 0; } g_work_total_sec = s_duration_sec; if (s_duration_sec > 0 && !s_auto_mode && g_work_elapsed_sec <= s_duration_sec) { g_work_progress = (g_work_elapsed_sec * 100) / s_duration_sec; } else { g_work_progress = 0; } g_work_start_hour = s_start_hour; g_work_start_minute = s_start_minute; if (sys_sta.light_protect) { sys_sta.light_protect = 0x9D; } else { sys_sta.light_protect = 0; } save_work_state(); } extern rt_bool_t gpio_hv_get_status(void); extern rt_bool_t gpio_lamp_get_status(void); /** * @brief 安全控制灯管 */ static char safe_lamp(rt_bool_t on) { if (gpio_lamp_get_status() != on) { gpio_lamp_ctrl(on); return 1; } return 0; } /** * @brief 安全控制高压 */ static char safe_hv(rt_bool_t on) { if (gpio_hv_get_status() != on) { gpio_hv_ctrl(on); return 1; } return 0; } /** * @brief 安全停止所有输出 */ static void safe_stop(char type) { if (KEY_IN_TEST_TIME == VALID) { LOG_D("safe_stop %c skipped (test mode)", type); return; } char res = safe_lamp(RT_FALSE) + safe_hv(RT_FALSE); if (res) { LOG_I(" safe_stop %c \n\n", type); } } /** * @brief 安全启动所有输出 */ static void safe_start(char type) { char res = safe_lamp(RT_TRUE) + safe_hv(RT_TRUE); if (res) { safe_lamp(1); safe_hv(1); LOG_I(" safe_start %c \n\n", type); } } /** * @brief 判断当前时间是否在时控时段内 */ static int in_time_range(void) { uint8_t s = dev_info.time_start; uint8_t e = dev_info.time_end; uint8_t h = time_now.hour; // 全天工作 if ((s == 0 && e == 23) || (s == e)) { return 1; } if (s <= e) { return (h >= s && h < e); } else { return (h >= s || h < e); } } /** * @brief 检查是否有其他保护激活 */ static uint8_t other_protect_active(void) { #define PROTECT_LOW_TEMP 0x7A #define PROTECT_HIGH_TEMP 0x9D uint8_t res = (sys_sta.undervolt_protect == PROTECT_ACTIVE || sys_sta.temp_protect == PROTECT_HIGH_TEMP || sys_sta.temp_protect == PROTECT_LOW_TEMP || sys_sta.tilt_protect == PROTECT_ACTIVE || sys_sta.rain_protect == PROTECT_ACTIVE); if (res && KEY_IN_TEST_TIME != VALID) { char power_off = safe_lamp(RT_FALSE) + safe_hv(RT_FALSE); if (power_off) { char buf[50] = {0}; sprintf(buf, "停止工作:%s", other_protect_name()); Recode_e_code(e_protect_stopwork, res + power_off, buf); } } return res; } /** * @brief 获取当前激活的保护名称 */ static const char *other_protect_name(void) { if (sys_sta.undervolt_protect == PROTECT_ACTIVE) return "欠压保护"; if (sys_sta.temp_protect == PROTECT_HIGH_TEMP) return "高温保护"; if (sys_sta.temp_protect == PROTECT_LOW_TEMP) return "低温保护"; if (sys_sta.tilt_protect == PROTECT_ACTIVE) return "倾倒保护"; if (sys_sta.rain_protect == PROTECT_ACTIVE) return "雨控保护"; return ""; } /*********************↑ 辅助函数 ↑***********************/ /*********************↓ 光控保护(带防抖) ↓***********************/ /** * @brief 获取当前光控条件 */ static uint8_t get_light_condition(void) { if (dev_info.light_flag == 0) { if (sys_sta.charge_voltage >= PV_VOLTAGE_DAY_THRESHOLD) { return 1; } else if (sys_sta.charge_voltage <= PV_VOLTAGE_NIGHT_THRESHOLD) { return 0; } else { return 2; } } else { if (sys_sta.light_percent >= LIGHT_VAL_DAY_THRESHOLD) { return 1; } else if (sys_sta.light_percent <= LIGHT_VAL_NIGHT_THRESHOLD) { return 0; } else { return 2; } } } /** * @brief 更新光控保护状态(带防抖机制) <带防抖的确认当前是否光控以及是否解除> */ static char check_light_protect(void) { uint8_t is_protect = get_light_condition(); uint32_t now = get_timestamp(); static uint32_t start_change_time = 0; static char ready_change = 0x9D; static uint32_t count_bak = 0; static work_state_DEF old_work_state = work_state_IDLE; static uint8_t next_status; if (next_status != 0) { next_status = 0; } if (is_protect == 2) // 光控水平介于 { if (ready_change != 0x9D) // 本次切换取消 { rt_kprintf("本次光控切换放弃(光控中间态) 当前:%d\n", s_light_protect); start_change_time = 0; ready_change = 0x9D; count_bak = 0; } return s_light_protect; } if (s_light_protect != is_protect && ready_change == 0x9D) // 本次切换开始 { rt_kprintf("开启光控切换 :%d -> %d [%d]\n", s_light_protect, is_protect, sys_sta.light_percent); start_change_time = now; ready_change = 1; count_bak = 0; next_status = is_protect; } else if (s_light_protect != is_protect) // 本次切换结果确认 { count_bak++; if ((now - start_change_time) > LIGHT_DEBOUNCE_SEC) // 检查是否光控触发或解除 { s_light_protect = is_protect; rt_kprintf("(time)本次光控切换完成 当前:%d\n", s_light_protect); start_change_time = 0; ready_change = 0x9D; count_bak = 0; } else if (count_bak > ((1000 / WORK_BASE_MS * LIGHT_DEBOUNCE_SEC) + 5)) // 备用方案 给时间戳逻辑补漏 { s_light_protect = is_protect; rt_kprintf("(count)本次光控切换完成 当前:%d\n", s_light_protect); start_change_time = 0; ready_change = 0x9D; count_bak = 0; } if (ready_change == 0x9D) { if (s_light_protect && (dev_info.light_flag == 0) && (sys_sta.work_state != work_state_CHG)) // 太阳能板光控 进入充电状态 { old_work_state = sys_sta.work_state; sys_sta.work_state = work_state_CHG; } if ((s_light_protect == 0) && (sys_sta.work_state == work_state_CHG)) // 光控解除了 一起解除充电状态 { if (old_work_state < work_state_CHG) { sys_sta.work_state = old_work_state; } else { sys_sta.work_state = work_state_IDLE; } } } } else if (ready_change != 0x9D) // 本次切换取消 { rt_kprintf("本次光控切换放弃 当前:%d\n", s_light_protect); start_change_time = 0; ready_change = 0x9D; count_bak = 0; } if (s_light_protect && (dev_info.light_flag == 0) && (sys_sta.work_state != work_state_CHG)) { old_work_state = sys_sta.work_state; sys_sta.work_state = work_state_CHG; } return s_light_protect; } /*********************↑ 光控保护 ↑***********************/ /*********************↓ 工作时长管理 ↓***********************/ /** * @brief 检查工作时间是否已到 */ static uint8_t is_duration_expired(void) { if (s_auto_mode) return 0; if (s_start_time == 0) return 0; uint32_t elapsed = get_timestamp() - s_start_time; return elapsed >= s_duration_sec; } /** * @brief 获取已工作时长(秒) */ static uint32_t get_elapsed_sec(void) { if (s_start_time == 0) return 0; uint32_t elapsed = get_timestamp() - s_start_time; return (elapsed < s_duration_sec) ? elapsed : s_duration_sec; } /*********************↑ 工作时长管理 ↑***********************/ /*********************↓ 工作控制 ↓***********************/ /** * @brief 开始工作 */ static void work_start(void) { char log_buf[52]; if (s_state == WORK_STATE_WORKING) { return; } s_start_time = get_timestamp(); if (dev_info.time_ctrl_enable == 0) // 光控模式 { s_duration_sec = dev_info.light_work_duration * 3600; /* 分钟转秒 */ s_auto_mode = (dev_info.light_work_duration == 0); } else if (in_time_range()) // 时控模式 && 在时段内(中途进入) { uint8_t start = dev_info.time_start; uint8_t end = dev_info.time_end; uint8_t cur_hour = time_now.hour; uint8_t cur_min = time_now.minute; uint8_t cur_sec = time_now.second; uint32_t elapsed_sec = 0; /* 计算从时段开始到当前已过的时间 */ if (start < end) { /* 同天:17-20,当前18点 -> 已过1小时 */ elapsed_sec = (cur_hour - start) * 3600 + cur_min * 60 + cur_sec; } else { /* 跨天:22-6,当前1点 -> 已过 (24-22) + 1 = 3小时 */ if (cur_hour >= start) { /* 第一天:22-23点 */ elapsed_sec = (cur_hour - start) * 3600 + cur_min * 60 + cur_sec; } else { /* 第二天:0-5点 */ elapsed_sec = (24 - start + cur_hour) * 3600 + cur_min * 60 + cur_sec; } } uint32_t full_sec = calculate_hours_diff(start, end) * 3600; s_duration_sec = (elapsed_sec < full_sec) ? (full_sec - elapsed_sec) : 0; s_auto_mode = 0; LOG_I("时控中途进入: 完整=%lu秒, 已过=%lu秒, 剩余=%lu秒", full_sec, elapsed_sec, s_duration_sec); } else // 时控模式 && 不在时段内(正常进入,等进入时段) { s_duration_sec = calculate_hours_diff(dev_info.time_start, dev_info.time_end) * 3600; s_auto_mode = 0; } s_start_hour = time_now.hour; s_start_minute = time_now.minute; safe_start('A'); sys_sta.work_state = work_state_WORK; sys_sta.hv_count++; if (dev_info.time_ctrl_enable == 0) { if (s_auto_mode) { rt_snprintf(log_buf, sizeof(log_buf), "工作开始:自动模式(天亮结束)"); LOG_I("%s", log_buf); Recode_e_code(e_work_start, 1, log_buf); } else { rt_snprintf(log_buf, sizeof(log_buf), "工作开始:光控模式 %d小时", dev_info.light_work_duration); LOG_I("%s", log_buf); Recode_e_code(e_work_start, 2, log_buf); } } else { uint8_t start = dev_info.time_start; uint8_t end = dev_info.time_end; uint8_t hour_diff = calculate_hours_diff(start, end); rt_snprintf(log_buf, sizeof(log_buf), "工作开始:时控模式 %d小时(%02d-%02d)", hour_diff, start, end); LOG_I("%s", log_buf); Recode_e_code(e_work_start, 3, log_buf); } } /** * @brief 停止工作 */ static void work_stop(const char *reason, uint8_t start_cooldown) { char log_buf[52]; clear_work_state(); safe_stop('A'); s_state = WORK_STATE_IDLE; s_start_time = 0; s_start_hour = 0; s_start_minute = 0; sys_sta.work_state = work_state_IDLE; rt_snprintf(log_buf, sizeof(log_buf), "工作停止:%s", reason); LOG_I("%s", log_buf); Recode_e_code(e_work_stop, 0, log_buf); if (start_cooldown) { s_state = WORK_STATE_COOLDOWN; s_cooldown_until = get_timestamp() + WORK_COOLDOWN_SEC; LOG_I("冷却开始: %d秒内不能工作", WORK_COOLDOWN_SEC); } } /** * @brief 检查是否还在工作时间 * * @return char 0-不在 1-在 */ static char check_time_vaild(void) { char res = 0; if (dev_info.time_ctrl_enable) // 时控模式 { res = in_time_range(); if (res) { return 1; } } else if (!is_duration_expired()) { return 1; } return 0; } /*********************↑ 工作控制 ↑***********************/ /*********************↓ 主线程 ↓***********************/ /** * @brief 判断NTP时间是否已同步成功 * @return 1=已同步, 0=未同步 * * 判断逻辑:当前系统时间若为2026年5月及以前,认为未同步 * 因为设备实际运行时间在2026年6月之后 */ static uint8_t is_ntp_synced(void) { /* 计算当前时间的 (年*12 + 月) 值 */ uint32_t current_value = (uint32_t)(time_now.year - 2000) * 12 + time_now.month; /* 2026年5月的参考值:26*12 + 5 = 317 */ uint32_t threshold = 26 * 12 + 5; // rt_kprintf("current_value %d threshold %d res = %d" , current_value ,threshold, (current_value > threshold)); /* 如果当前时间 <= 2026年5月,认为NTP未同步 */ if (current_value <= threshold) { return 0; /* 未同步 */ } return 1; /* 已同步 */ } /** * @brief 工作控制主线程 */ static void work_thread_entry(void *param) { uint16_t ftp_wait_cnt = 0; uint32_t last_sync_time = 0; uint32_t last_log_time = 0; char log_process = 0; uint8_t s_state_last = WORK_STATE_IDLE; // 备用状态 int8_t last_sta = WORK_STATE_IDLE; // 打印日志用 /* 等待配置加载完成 */ int wait_cnt = 0; while (ef_cfged != 1 && wait_cnt < 100) { rt_thread_mdelay(100); wait_cnt++; } rt_thread_mdelay(3000); /* ===== 新增:等待时间同步完成(最多2分钟)===== */ { uint32_t time_wait_cnt = 0; const uint32_t TIME_WAIT_MAX = 120; /* 最多等待120秒 */ const uint8_t TIME_CHECK_INTERVAL = 1; /* 每秒检查一次 */ /* 检查RTC时间是否有效(非默认值2026-01-01) */ while (time_wait_cnt < TIME_WAIT_MAX) { /* 检查RTC是否已从默认值更新 */ if (is_ntp_synced()) { LOG_I("时间同步已完成: %04d-%02d-%02d %02d:%02d:%02d", time_now.year, time_now.month, time_now.date, time_now.hour, time_now.minute, time_now.second); break; } time_wait_cnt++; if (time_wait_cnt % 10 == 0) { LOG_I("等待时间同步中... (%d/%d秒)", time_wait_cnt, TIME_WAIT_MAX); } rt_thread_mdelay(TIME_CHECK_INTERVAL * 1000); } if (time_wait_cnt >= TIME_WAIT_MAX) { Recode_e_code(e_work_without_ntp, 0, "工作开启时未同步时间"); LOG_W("时间同步等待超时(2分钟),使用默认时间继续工作"); } } /* ============================================= */ /* 从RTC恢复之前的工作状态 */ if (restore_work_state()) { uint32_t now = get_timestamp(); uint32_t elapsed = (now > s_start_time) ? (now - s_start_time) : 0; if (elapsed < s_duration_sec) { safe_start('B'); sys_sta.work_state = work_state_WORK; uint32_t remain = s_duration_sec - elapsed; LOG_I("工作恢复: (已工作%lu时%lu分, 剩余%lu时%lu分)", elapsed / 3600, elapsed % 3600 / 60, remain / 3600, remain % 3600 / 60); Recode_e_code(e_work_resume, 0, "工作恢复:重启后恢复(RTC)"); while (other_protect_active()) { LOG_I("等待工作恢复: %s", other_protect_name()); rt_thread_mdelay(10000); sync_mqtt_vars(); } } else { clear_work_state(); s_state = WORK_STATE_IDLE; s_start_time = 0; LOG_I("保存的工作已过期,不恢复"); } } /* 主循环 */ while (1) { uint32_t now = get_timestamp(); uint8_t protect_active = other_protect_active(); // 刷新其他保护 check_light_protect(); // 刷新光控保护 sync_mqtt_vars(); /* 3.1 关机检查 */ if (dev_info.power_state == 0) { if (s_state == WORK_STATE_WORKING) { work_stop("设备关机", 0); s_state = WORK_STATE_IDLE; safe_stop('G'); } rt_thread_mdelay(WORK_BASE_MS); continue; } /* 3.3 保护处理(简化版)*/ if (protect_active && (s_state != WORK_STATE_OTHER_PRT)) { LOG_I("保护触发: %s", other_protect_name()); s_state_last = s_state; // if (s_state == WORK_STATE_WORKING) // { // s_state_last = WORK_STATE_WORKING; // } // else // { // s_state_last = WORK_STATE_IDLE; // } s_state = WORK_STATE_OTHER_PRT; } if (last_sta != s_state) { LOG_I("Detect sta chang %d -> %d", last_sta, s_state); last_sta = s_state; } if (light_prt_start_time != 0 && s_state != WORK_STATE_LIGHT_PRT) // 异常状态切换保护 WORK_STATE_LIGHT_PRT 专用 { light_prt_start_time = 0; rt_kprintf("light_prt_start_time CLEAR\n"); } if (s_protect_release_time != 0 && s_state != WORK_STATE_OTHER_PRT) // 异常状态切换保护 WORK_STATE_OTHER_PRT 专用 { s_protect_release_time = 0; // rt_kprintf("s_protect_release_time CLEAR\n"); } if ((start_ftp == VALID) && (ftp_wait_cnt * WORK_BASE_MS < (600000))) // 检查是否在升级 { ftp_wait_cnt++; if (ftp_wait_cnt > 0 && (ftp_wait_cnt % (10000 / WORK_BASE_MS) == 0)) { rt_kprintf("work_thread_entry continue %d/%d\n", ftp_wait_cnt * WORK_BASE_MS / 1000, 600); } rt_thread_mdelay(WORK_BASE_MS); safe_stop('H'); continue; } else if (ftp_wait_cnt != 0) { ftp_wait_cnt = 0; } /* ========== 3.5 状态机处理 ========== */ switch (s_state) { case WORK_STATE_IDLE: /* 空闲状态 */ { uint8_t can_work = 0; if (dev_info.time_ctrl_enable) // 时控模式 { can_work = in_time_range(); if (can_work) { LOG_I("时控条件满足,开始工作"); } } else if (!s_light_protect && !get_light_condition()) { can_work = 1; LOG_I("光控条件满足(夜晚),开始工作"); } if (can_work) { work_start(); s_state = WORK_STATE_WORKING; continue; } safe_stop('B'); break; } case WORK_STATE_WORKING: /* 工作中状态 */ { uint8_t should_stop = 0; const char *stop_reason = ""; if (is_duration_expired()) { should_stop = 1; stop_reason = "时长到"; } else if ((s_auto_mode && s_light_protect) && (dev_info.time_ctrl_enable == 0)) { should_stop = 1; stop_reason = "天亮"; } if (should_stop) { work_stop(stop_reason, 1); s_state = WORK_STATE_COOLDOWN; continue; } else if (s_light_protect && (dev_info.time_ctrl_enable == 0)) // 检查光控 { s_state = WORK_STATE_LIGHT_PRT; continue; } else { if (now - last_sync_time >= 1) { last_sync_time = now; } if (!s_auto_mode && s_duration_sec > 0) { uint32_t elapsed = get_elapsed_sec(); uint32_t progress = (elapsed * 100) / s_duration_sec; uint32_t remain = s_duration_sec - elapsed; if (log_process != progress) { log_process = progress; LOG_I("工作进度: %u%% (已工作%lu时%lu分, 剩余%lu时%lu分)", progress, elapsed / 3600, elapsed % 3600 / 60, remain / 3600, remain % 3600 / 60); } } } safe_start('C'); break; } case WORK_STATE_LIGHT_PRT: // 光控保护 { if ((dev_info.time_ctrl_enable == 1)) { s_state = WORK_STATE_IDLE; LOG_W("时控模式 禁用光控保护\n"); continue; } if (light_prt_start_time == 0) { light_prt_start_time = get_timestamp(); } if (s_light_protect) // 光控着吗? { // rt_kprintf("WORK_STATE_LIGHT_PRT A\n"); if (is_duration_expired()) // 工作时间到没? { work_stop("光控至工作时间结束", 1); light_prt_start_time = 0; s_state = WORK_STATE_COOLDOWN; continue; } else if ((now - light_prt_start_time) >= LIGHT_BREAK_WORK_SEC) // 光控超时没 { work_stop("长时间光控退出", 1); light_prt_start_time = 0; s_state = WORK_STATE_COOLDOWN; } } else // 保护解除 { s_state = WORK_STATE_WORKING; light_prt_start_time = 0; continue; } safe_stop('C'); break; } case WORK_STATE_COOLDOWN: /* 冷却状态 */ { if ((dev_info.time_ctrl_enable == 1)) { s_state = WORK_STATE_IDLE; LOG_W("时控模式 禁用光控状态\n"); continue; } if (s_cooldown_until > 0 && now >= s_cooldown_until) { s_cooldown_until = 0; if (dev_info.time_ctrl_enable == 0) { s_state = WORK_STATE_WAIT_DAY; LOG_I("冷却结束,等待天亮后才能再次工作"); } else { s_state = WORK_STATE_IDLE; LOG_I("冷却结束,随时可以工作(时控)"); } continue; } else if (s_cooldown_until == 0) { s_cooldown_until = get_timestamp() + WORK_COOLDOWN_SEC; LOG_I("异常进入冷却开始"); } safe_stop('D'); break; } case WORK_STATE_WAIT_DAY: /* 等待天亮状态 */ { if ((dev_info.time_ctrl_enable == 1)) { s_state = WORK_STATE_IDLE; LOG_W("时控模式 禁用光控状态\n"); continue; } if (now - last_log_time >= 60) { last_log_time = now; LOG_D("等待天亮中... 光控保护=%s", s_light_protect ? "白天" : "夜晚"); } if (s_light_protect) // 天亮了 { s_state = WORK_STATE_IDLE; continue; } safe_stop('E'); break; } case WORK_STATE_OTHER_PRT: { if (!protect_active && s_protect_release_time == 0) // 保护解除了 { s_protect_release_time = now + PROTECT_RELEASE_DELAY_SEC; LOG_I("保护已解除,延迟%d秒后恢复", PROTECT_RELEASE_DELAY_SEC); } else if (!protect_active) { if (now >= s_protect_release_time) { s_state = s_state_last; LOG_I("退出保护:恢复工作状态=%d\n", s_state_last); continue; } } // else if (!check_time_vaild()) // { // s_state = WORK_STATE_IDLE; // LOG_I("失效保护:(已不在工作时间)恢复至空闲状态\n"); // continue; // } safe_stop('F'); break; } default: { LOG_W("未知状态: %d, 重置为空闲", s_state); s_state = WORK_STATE_IDLE; break; } } rt_thread_mdelay(WORK_BASE_MS); } } /*********************↑ 主线程 ↑***********************/ /*********************↓ 初始化 ↓***********************/ /** * @brief 启动工作控制模块 */ static void work_startup(void) { if (s_thread) return; s_running = RT_TRUE; s_thread = rt_thread_create("work", work_thread_entry, NULL, 2048, 13, 10); if (s_thread) { rt_thread_startup(s_thread); // LOG_I("工作控制模块已启动"); } else { LOG_E("创建工作线程失败"); } } /** * @brief 工作模块自动初始化 */ static int work_init(void) { work_startup(); return 0; } INIT_APP_EXPORT(work_init); /*********************↑ 初始化 ↑***********************/ /*********************↓ MSH命令 ↓***********************/ /** * @brief MSH命令:查看工作状态 */ static void cmd_work_status(int argc, char **argv) { uint32_t now = get_timestamp(); uint32_t elapsed = 0; uint32_t remain = 0; uint32_t progress = 0; uint32_t cooldown_remain = 0; if (s_state == WORK_STATE_WORKING && s_start_time > 0) { elapsed = (now > s_start_time) ? now - s_start_time : 0; if (elapsed > s_duration_sec) elapsed = s_duration_sec; if (s_duration_sec > 0 && !s_auto_mode) { progress = (elapsed * 100) / s_duration_sec; remain = s_duration_sec - elapsed; } } if (s_state == WORK_STATE_COOLDOWN && s_cooldown_until > now) { cooldown_remain = s_cooldown_until - now; } rt_kprintf("\n"); rt_kprintf("╔══════════════════════════════════════════════════════════════╗\n"); rt_kprintf("║ WORK STATUS REPORT ║\n"); rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n"); rt_kprintf("║ 开关机状态 : %-46s ║\n", dev_info.power_state ? "开机" : "关机"); char buf[16] = {0}; switch (s_state) { case WORK_STATE_IDLE: sprintf(buf, "空闲"); break; case WORK_STATE_WORKING: sprintf(buf, "工作中"); break; case WORK_STATE_LIGHT_PRT: sprintf(buf, "光控中"); break; case WORK_STATE_COOLDOWN: sprintf(buf, "冷却中"); break; case WORK_STATE_WAIT_DAY: sprintf(buf, "等天亮"); break; case WORK_STATE_OTHER_PRT: sprintf(buf, "保护中"); break; default: sprintf(buf, "未知?"); break; } rt_kprintf("║ 工作状态 : %s ║\n", buf); if (s_state == WORK_STATE_WORKING) { if (dev_info.time_ctrl_enable == 0) rt_kprintf("║ 光控模式 : %-46s ║\n", s_auto_mode ? "自动模式" : "光控模式"); rt_kprintf("║ 开始时间 : %02d:%02d%-44s ║\n", s_start_hour, s_start_minute, ""); if (elapsed >= 60) { uint32_t min = elapsed / 60; uint32_t sec = elapsed % 60; rt_kprintf("║ 已工作时长 : %u分%u秒%-42s ║\n", min, sec, ""); } else { rt_kprintf("║ 已工作时长 : %u秒%-45s ║\n", elapsed, ""); } if (!s_auto_mode) { if (remain >= 60) { uint32_t min = remain / 60; uint32_t sec = remain % 60; rt_kprintf("║ 剩余时长 : %u分%u秒%-42s ║\n", min, sec, ""); } else { rt_kprintf("║ 剩余时长 : %u秒%-45s ║\n", remain, ""); } rt_kprintf("║ 进度 : %3d%% %-46s ║\n", progress, ""); rt_kprintf("║ ["); uint8_t bar_len = progress / 2; for (uint8_t i = 0; i < 50; i++) { if (i < bar_len) rt_kprintf("="); else if (i == bar_len) rt_kprintf(">"); else rt_kprintf(" "); } rt_kprintf("] %3d%%%27s ║\n", progress, ""); } else { rt_kprintf("║ 工作模式 : 自动(天亮结束)%-35s ║\n", ""); } if (dev_info.light_work_duration > 0) { rt_kprintf("║ 配置工作时长 : %d小时 ║\n", dev_info.light_work_duration); } else { rt_kprintf("║ 配置工作时长 : 自动模式%-43s ║\n", ""); } } if (s_state == WORK_STATE_COOLDOWN && cooldown_remain > 0) { if (cooldown_remain >= 60) { uint32_t min = cooldown_remain / 60; uint32_t sec = cooldown_remain % 60; rt_kprintf("║ 冷却剩余时间 : %u分%u秒%-42s ║\n", min, sec, ""); } else { rt_kprintf("║ 冷却剩余时间 : %u秒%-45s ║\n", cooldown_remain, ""); } } if (s_state == WORK_STATE_WAIT_DAY) { rt_kprintf("║ 状态说明 : 等待天亮后才能再次工作%-26s ║\n", ""); } if (s_state == WORK_STATE_LIGHT_PRT && s_protect_release_time > 0) { uint32_t remain = (s_protect_release_time > now) ? s_protect_release_time - now : 0; rt_kprintf("║ 保护延迟剩余 : %lu秒%-44s ║\n", remain, ""); } rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n"); rt_kprintf("║ 控制模式 : %-46s ║\n", dev_info.time_ctrl_enable ? "时控模式" : "光控模式"); if (dev_info.time_ctrl_enable == 0) { rt_kprintf("║ 光控来源 : %-46s ║\n", dev_info.light_flag == 0 ? "太阳能板" : "光敏二极管"); rt_kprintf("║ 当前亮度 : %d.%d%%%-44s ║\n", sys_sta.light_percent / 10, sys_sta.light_percent % 10, ""); rt_kprintf("║ 充电电压 : %dmV%-45s ║\n", sys_sta.charge_voltage, ""); rt_kprintf("║ 光控保护 : %-46s ║\n", s_light_protect ? "激活(白天)" : "未激活(夜晚)"); } else { char range[32]; rt_snprintf(range, sizeof(range), "%02d:00 - %02d:00", dev_info.time_start, dev_info.time_end); rt_kprintf("║ 时控时段 : %-46s ║\n", range); rt_kprintf("║ 当前小时 : %d点%-46s ║\n", time_now.hour, ""); rt_kprintf("║ 是否在时段内 : %-46s ║\n", in_time_range() ? "是" : "否"); } rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n"); rt_kprintf("║ 保护状态 : ║\n"); rt_kprintf("║ 光控保护 : %-46s ║\n", s_light_protect ? "激活" : "正常"); rt_kprintf("║ 欠压保护 : %-46s ║\n", sys_sta.undervolt_protect == PROTECT_ACTIVE ? "激活" : "正常"); rt_kprintf("║ 高温保护 : %-46s ║\n", sys_sta.temp_protect == PROTECT_HIGH_TEMP ? "激活" : "正常"); rt_kprintf("║ 低温保护 : %-46s ║\n", sys_sta.temp_protect == PROTECT_LOW_TEMP ? "激活" : "正常"); rt_kprintf("║ 倾倒保护 : %-46s ║\n", sys_sta.tilt_protect == PROTECT_ACTIVE ? "激活" : "正常"); rt_kprintf("║ 雨控保护 : %-46s ║\n", sys_sta.rain_protect == PROTECT_ACTIVE ? "激活" : "正常"); rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n"); rt_kprintf("║ 电气参数 : ║\n"); rt_kprintf("║ 电池电压 : %dmV%-45s ║\n", sys_sta.battery_voltage, ""); rt_kprintf("║ 主板温度 : %d.%d°C%-44s ║\n", sys_sta.board_temp / 10, sys_sta.board_temp % 10, ""); rt_kprintf("║ 灯管电流 : %u%-47s ║\n", sys_sta.lamp_current, ""); rt_kprintf("║ 高压次数 : %u%-47s ║\n", sys_sta.hv_count, ""); rt_kprintf("║ 灯管开关 : %u%-47s ║\n", gpio_lamp_get_status(), ""); rt_kprintf("║ 高压开关 : %u%-47s ║\n", gpio_hv_get_status(), ""); rt_kprintf("╚══════════════════════════════════════════════════════════════╝\n"); } MSH_CMD_EXPORT(cmd_work_status, 查看工作状态); /** * @brief MSH命令:手动控制工作 */ static void cmd_work_ctrl(int argc, char **argv) { if (argc < 2) { rt_kprintf("用法: work [start|stop|set <分钟>]\n"); rt_kprintf(" start - 手动开始工作\n"); rt_kprintf(" stop - 手动停止工作\n"); rt_kprintf(" set 5 - 设置工作时长为5分钟\n"); return; } if (strcmp(argv[1], "start") == 0) { if (dev_info.power_state == 0) { rt_kprintf("错误: 设备已关机\n"); return; } s_cooldown_until = 0; s_protect_release_time = 0; s_state = WORK_STATE_IDLE; work_start(); rt_kprintf("已手动开始工作\n"); } else if (strcmp(argv[1], "stop") == 0) { work_stop("手动停止", 0); rt_kprintf("已手动停止工作\n"); } else if (strcmp(argv[1], "set") == 0 && argc >= 3) { uint16_t minutes = atoi(argv[2]); dev_info.light_work_duration = minutes; save_dev_info(); if (minutes >= 60) { uint32_t hours = minutes / 60; uint32_t mins = minutes % 60; if (mins > 0) { rt_kprintf("工作时长已设置为 %u小时%u分钟\n", hours, mins); } else { rt_kprintf("工作时长已设置为 %u小时\n", hours); } } else { rt_kprintf("工作时长已设置为 %d分钟\n", minutes); } } else { rt_kprintf("未知命令: %s\n", argv[1]); } } MSH_CMD_EXPORT(cmd_work_ctrl, 手动控制工作); /** * @brief MSH命令:调试信息 */ static void cmd_work_debug(int argc, char **argv) { rt_kprintf("\n========== WORK DEBUG ==========\n"); rt_kprintf("s_state: %d\n", s_state); rt_kprintf("s_start_time: %u\n", s_start_time); rt_kprintf("s_duration_sec: %lu\n", s_duration_sec); rt_kprintf("s_auto_mode: %d\n", s_auto_mode); rt_kprintf("s_cooldown_until: %u\n", s_cooldown_until); rt_kprintf("s_light_protect: %d\n", s_light_protect); rt_kprintf("s_protect_release_time: %u\n", s_protect_release_time); rt_kprintf("light_percent: %d.%d%%\n", sys_sta.light_percent / 10, sys_sta.light_percent % 10); rt_kprintf("charge_voltage: %dmV\n", sys_sta.charge_voltage); rt_kprintf("===============================\n"); } MSH_CMD_EXPORT(cmd_work_debug, 调试信息); /*********************↑ MSH命令 ↑***********************/