work_scd.c 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566
  1. /*
  2. * Work Control Module - v2.3 (修复版)
  3. *
  4. * 修复:工作结束后必须等待天亮才能再次工作
  5. * 优化:保护逻辑简化版
  6. *
  7. * Version: 2.3.1
  8. */
  9. /*********************↓ include ↓************************/
  10. #define DBG_TAG "WORK"
  11. #define DBG_LVL DBG_INFO
  12. #include <rtdbg.h>
  13. #include <rtthread.h>
  14. #include <rtdevice.h>
  15. #include <board.h>
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include "ecode.h"
  19. #include "ym310_protecl.h"
  20. /*********************↑ include ↑************************/
  21. /*********************↓ define ↓************************/
  22. /* 工作线程基础时间片(毫秒)*/
  23. #define WORK_BASE_MS 100
  24. /* ========== 光控阈值定义 ========== */
  25. // #define LIGHT_VAL_NIGHT_THRESHOLD 50 /* 夜晚阈值:≤5%亮度 */
  26. // #define LIGHT_VAL_DAY_THRESHOLD 150 /* 白天阈值:≥15%亮度 */
  27. #define LIGHT_VAL_NIGHT_THRESHOLD 20 /* 夜晚阈值:≤2%亮度 */
  28. #define LIGHT_VAL_DAY_THRESHOLD 150 /* 白天阈值:≥15%亮度 */
  29. #define PV_VOLTAGE_NIGHT_THRESHOLD limit.vsunset /* 夜晚阈值:太阳能板电压≤1.5V */
  30. #define PV_VOLTAGE_DAY_THRESHOLD limit.vsunrise /* 白天阈值:太阳能板电压≥3.3V */
  31. /* ========== 状态转换延迟参数 ========== */
  32. #define LIGHT_DEBOUNCE_SEC 10 /* 光控状态防抖10秒 */
  33. #define WORK_COOLDOWN_SEC 300 /* 工作结束后冷却5分钟 */
  34. #define PROTECT_RELEASE_DELAY_SEC 5 /* 保护解除后延迟5秒再恢复工作能力 */
  35. #define LIGHT_BREAK_WORK_SEC 300 /* 持续光控保护 到达后 退出本次工作 */
  36. /* ========== 工作状态定义 ========== */
  37. typedef enum
  38. {
  39. WORK_STATE_IDLE = 0,
  40. WORK_STATE_WORKING,
  41. WORK_STATE_LIGHT_PRT,
  42. WORK_STATE_COOLDOWN,
  43. WORK_STATE_WAIT_DAY,
  44. WORK_STATE_OTHER_PRT
  45. } Work_status;
  46. /* 保护激活标志值 */
  47. #define PROTECT_ACTIVE 0x9D
  48. /* ========== RTC备份寄存器地址定义 ========== */
  49. #define RTC_BKP_WORK_A RTC_BKP_DR1 /* DR1: 工作开始时间戳 */
  50. #define RTC_BKP_WORK_B RTC_BKP_DR2 /* DR2: 工作持续时间秒数 */
  51. #define RTC_BKP_WORK_C RTC_BKP_DR3 /* DR3: 打包状态寄存器 */
  52. #define RTC_BKP_WORK_D RTC_BKP_DR4 /* DR4: 打包状态寄存器 */
  53. #define RTC_BKP_WORK_E RTC_BKP_DR5 /* DR5: 打包状态寄存器 */
  54. /* 打包状态寄存器格式 */
  55. #define RTC_WORK_VALID_MAGIC 0xA5U /* 魔数,用于校验数据有效性 */
  56. /*********************↑ define ↑************************/
  57. /*********************↓ 外部变量声明 ↓***********************/
  58. extern SYS_STATUS sys_sta;
  59. extern DEV_INFO dev_info;
  60. extern struct rtc_time time_now;
  61. extern void gpio_lamp_ctrl(rt_bool_t on);
  62. extern void gpio_hv_ctrl(rt_bool_t on);
  63. extern char ef_cfged;
  64. extern RTC_HandleTypeDef hrtc;
  65. extern char start_ftp;
  66. /*********************↑ 外部变量声明 ↑***********************/
  67. /*********************↓ 全局变量(供MQTT上报) ↓***********************/
  68. extern uint8_t g_work_state;
  69. extern uint8_t g_work_mode;
  70. extern uint32_t g_work_elapsed_sec;
  71. extern uint32_t g_work_total_sec;
  72. extern uint8_t g_work_progress;
  73. extern uint32_t g_cooldown_sec;
  74. extern uint8_t g_work_start_hour;
  75. extern uint8_t g_work_start_minute;
  76. extern char KEY_IN_TEST_TIME;
  77. /*********************↑ 全局变量 ↑***********************/
  78. /*********************↓ 静态变量 ↓***********************/
  79. static rt_thread_t s_thread = RT_NULL; /* 工作线程句柄 */
  80. static rt_bool_t s_running = RT_FALSE; /* 线程运行标志 */
  81. /* ========== 工作状态核心变量 ========== */
  82. /**
  83. * @brief 以下这些放rtc里面
  84. *
  85. */
  86. static uint8_t s_state = WORK_STATE_IDLE; /* 当前工作状态 */
  87. static uint8_t s_light_protect = 0; /* 光控保护状态 */
  88. static uint8_t s_start_hour = 0; /* 工作开始小时 */
  89. static uint8_t s_start_minute = 0; /* 工作开始分钟 */ // 寄存器1
  90. static uint32_t s_start_time = 0; /* 工作开始时间戳(秒)*/ // 开启时间 // 寄存器2
  91. static uint32_t s_duration_sec = 0; /* 工作持续时长(秒)*/ // 需要工作时长 // 寄存器3
  92. static uint32_t s_cooldown_until = 0; /* 冷却结束时间戳(秒)*/ // 寄存器4
  93. /**
  94. * 先不放rtc里面 这个可以根据参数还原
  95. */
  96. static uint8_t s_auto_mode = 0; /* 自动模式标志 */
  97. /* ========== 光控保护状态(带防抖)========== */
  98. /* ========== 保护相关变量(简化版)========== */
  99. static uint32_t s_protect_release_time = 0; /* 保护解除后可以恢复工作的时间戳 */
  100. static uint32_t light_prt_start_time = 0; // 光控触发后的时间计算
  101. /* ========== GPIO状态缓存 ========== */
  102. /*********************↑ 静态变量 ↑***********************/
  103. /*********************↓ 函数声明 ↓***********************/
  104. static uint32_t get_timestamp(void);
  105. static void sync_mqtt_vars(void);
  106. static void safe_start(char type);
  107. static int in_time_range(void);
  108. static uint32_t get_elapsed_sec(void);
  109. static void work_start(void);
  110. static void work_stop(const char *reason, uint8_t start_cooldown);
  111. static uint8_t is_duration_expired(void);
  112. static uint8_t other_protect_active(void);
  113. static const char *other_protect_name(void);
  114. uint8_t calculate_hours_diff(uint8_t start, uint8_t ent);
  115. /*********************↑ 函数声明 ↑***********************/
  116. /*********************↓ RTC备份寄存器存储方案 ↓***********************/
  117. uint32_t crc32_update(uint32_t data, uint32_t *crc)
  118. {
  119. if (crc == NULL)
  120. return 0;
  121. if (*crc == 0)
  122. {
  123. *crc = 0xFFFFFFFFUL;
  124. }
  125. for (int byte_idx = 3; byte_idx >= 0; byte_idx--)
  126. {
  127. uint8_t byte = (data >> (byte_idx * 8)) & 0xFF;
  128. *crc ^= byte;
  129. for (int bit = 0; bit < 8; bit++)
  130. {
  131. if (*crc & 1)
  132. {
  133. *crc = (*crc >> 1) ^ 0xEDB88320UL;
  134. }
  135. else
  136. {
  137. *crc >>= 1;
  138. }
  139. }
  140. }
  141. return *crc ^ 0xFFFFFFFFUL;
  142. }
  143. // /**
  144. // * 计算两个时间点之间的小时差,支持跨天(24小时制)
  145. // * @param start 开始时间(小时,0-23)
  146. // * @param end 结束时间(小时,0-23)
  147. // * @return 从 start 到 end 经过的小时数(0-23)
  148. // */
  149. // uint8_t calculate_hours_diff(uint8_t start, uint8_t end)
  150. // {
  151. // // 参数合法性检查(可根据需要选择是否启用)
  152. // if (start < 0 || start > 23 || end < 0 || end > 23)
  153. // {
  154. // return -1; // 返回错误码
  155. // }
  156. // if (start <= end)
  157. // {
  158. // // 同一天内:直接相减
  159. // return end - start;
  160. // }
  161. // else
  162. // {
  163. // // 跨天:(到当天结束的小时数) + (第二天开始到 end 的小时数)
  164. // return (24 - start) + end;
  165. // }
  166. // }
  167. /**
  168. * 计算两个时间点之间的小时差,支持跨天(24小时制)
  169. * @param start 开始时间(小时,0-23)
  170. * @param end 结束时间(小时,0-23)
  171. * @return 从 start 到 end 经过的小时数(0-24)
  172. */
  173. uint8_t calculate_hours_diff(uint8_t start, uint8_t end)
  174. {
  175. if (start == 0 && end == 23)
  176. {
  177. return 24;
  178. }
  179. if (start <= end)
  180. {
  181. return end - start;
  182. }
  183. else
  184. {
  185. return (24 - start) + end;
  186. }
  187. }
  188. /**
  189. * @brief 将工作状态打包到单个32位寄存器
  190. */
  191. static uint32_t pack_work_state(void)
  192. {
  193. return ((uint32_t)s_state << 24) |
  194. ((uint32_t)s_light_protect << 16) |
  195. ((uint32_t)s_start_hour << 8) |
  196. ((uint32_t)s_start_minute);
  197. }
  198. /**
  199. * @brief 从32位寄存器解包工作状态
  200. */
  201. static int unpack_work_state(uint32_t packed)
  202. {
  203. s_state = (packed >> 24) & 0xFF;
  204. s_light_protect = (packed >> 16) & 0xFF;
  205. s_start_hour = (packed >> 8) & 0xFF;
  206. s_start_minute = packed & 0xFF;
  207. return 1;
  208. }
  209. /**
  210. * @brief 保存工作状态到RTC备份寄存器
  211. */
  212. static void save_work_state(void)
  213. {
  214. uint32_t crc = 0, final_crc = 0, packeg = 0;
  215. static uint32_t e = 0;
  216. packeg = pack_work_state();
  217. final_crc = crc32_update(packeg, &crc);
  218. final_crc = crc32_update(s_start_time, &crc);
  219. final_crc = crc32_update(s_duration_sec, &crc);
  220. final_crc = crc32_update(s_cooldown_until, &crc);
  221. e = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_E);
  222. if (e != final_crc)
  223. {
  224. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_A, pack_work_state());
  225. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_B, s_start_time);
  226. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_C, s_duration_sec);
  227. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_D, s_cooldown_until);
  228. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_E, final_crc);
  229. LOG_I("工作状态已更新到RTC: time=%u, dur=%lu, CRC = 0x%08X",
  230. s_start_time, s_duration_sec, final_crc);
  231. e = final_crc;
  232. }
  233. }
  234. /**
  235. * @brief 清除RTC备份寄存器中的工作状态
  236. */
  237. static void clear_work_state(void)
  238. {
  239. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_A, 0);
  240. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_B, 0);
  241. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_C, 0);
  242. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_D, 0);
  243. HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_WORK_E, 0);
  244. LOG_D("工作状态已清除");
  245. }
  246. /**
  247. * @brief 从RTC备份寄存器恢复工作状态
  248. */
  249. static int restore_work_state(void)
  250. {
  251. uint32_t crc = 0, final_crc = 0, a = 0, b = 0, c = 0, d = 0, e = 0;
  252. a = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_A);
  253. b = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_B);
  254. c = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_C);
  255. d = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_D);
  256. e = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_WORK_E);
  257. final_crc = crc32_update(a, &crc);
  258. final_crc = crc32_update(b, &crc);
  259. final_crc = crc32_update(c, &crc);
  260. final_crc = crc32_update(d, &crc);
  261. LOG_I("读取RTC: time=%u, dur=%lu, CRC = 0x%08X",
  262. b, c, e);
  263. if (final_crc != e)
  264. {
  265. LOG_I("无有效工作状态数据(CRC FAIL) %d", (final_crc != e));
  266. return 0;
  267. }
  268. if (!unpack_work_state(a))
  269. {
  270. LOG_W("工作状态数据校验失败");
  271. return 0;
  272. }
  273. if (s_auto_mode > 1)
  274. {
  275. LOG_W("自动模式标志无效");
  276. return 0;
  277. }
  278. s_start_time = b;
  279. s_duration_sec = c;
  280. s_cooldown_until = d;
  281. LOG_I("成功恢复工作状态: 开始时间=%u, 持续=%lu秒, 自动模式=%d",
  282. s_start_time, s_duration_sec, s_auto_mode);
  283. return 1;
  284. }
  285. /*********************↑ RTC备份寄存器存储方案 ↑***********************/
  286. /*********************↓ 辅助函数 ↓***********************/
  287. /**
  288. * @brief 获取当前Unix时间戳
  289. */
  290. static uint32_t get_timestamp(void)
  291. {
  292. uint32_t ts = 0;
  293. for (uint16_t y = 2000; y < time_now.year; y++)
  294. {
  295. ts += ((y % 4 == 0 && y % 100 != 0) || (y % 400 == 0)) ? 366 : 365;
  296. }
  297. ts *= 86400;
  298. static const uint16_t md[12] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};
  299. ts += (md[time_now.month - 1] + time_now.date - 1) * 86400;
  300. if ((time_now.year % 4 == 0 && time_now.year % 100 != 0) && time_now.month > 2)
  301. {
  302. ts += 86400;
  303. }
  304. ts += time_now.hour * 3600 + time_now.minute * 60 + time_now.second;
  305. return ts;
  306. }
  307. /**
  308. * @brief 同步MQTT上报变量
  309. */
  310. static void sync_mqtt_vars(void)
  311. {
  312. g_work_state = s_state;
  313. g_work_mode = (s_state == WORK_STATE_WORKING) ? 1 : 0;
  314. if (s_state == WORK_STATE_WORKING && s_start_time > 0)
  315. {
  316. uint32_t now = get_timestamp();
  317. g_work_elapsed_sec = (now > s_start_time) ? now - s_start_time : 0;
  318. }
  319. else
  320. {
  321. g_work_elapsed_sec = 0;
  322. }
  323. g_work_total_sec = s_duration_sec;
  324. if (s_duration_sec > 0 && !s_auto_mode && g_work_elapsed_sec <= s_duration_sec)
  325. {
  326. g_work_progress = (g_work_elapsed_sec * 100) / s_duration_sec;
  327. }
  328. else
  329. {
  330. g_work_progress = 0;
  331. }
  332. g_work_start_hour = s_start_hour;
  333. g_work_start_minute = s_start_minute;
  334. if (sys_sta.light_protect)
  335. {
  336. sys_sta.light_protect = 0x9D;
  337. }
  338. else
  339. {
  340. sys_sta.light_protect = 0;
  341. }
  342. save_work_state();
  343. }
  344. extern rt_bool_t gpio_hv_get_status(void);
  345. extern rt_bool_t gpio_lamp_get_status(void);
  346. /**
  347. * @brief 安全控制灯管
  348. */
  349. static char safe_lamp(rt_bool_t on)
  350. {
  351. if (gpio_lamp_get_status() != on)
  352. {
  353. gpio_lamp_ctrl(on);
  354. return 1;
  355. }
  356. return 0;
  357. }
  358. /**
  359. * @brief 安全控制高压
  360. */
  361. static char safe_hv(rt_bool_t on)
  362. {
  363. if (gpio_hv_get_status() != on)
  364. {
  365. gpio_hv_ctrl(on);
  366. return 1;
  367. }
  368. return 0;
  369. }
  370. /**
  371. * @brief 安全停止所有输出
  372. */
  373. static void safe_stop(char type)
  374. {
  375. if (KEY_IN_TEST_TIME == VALID)
  376. {
  377. LOG_D("safe_stop %c skipped (test mode)", type);
  378. return;
  379. }
  380. char res = safe_lamp(RT_FALSE) + safe_hv(RT_FALSE);
  381. if (res)
  382. {
  383. LOG_I(" safe_stop %c \n\n", type);
  384. }
  385. }
  386. /**
  387. * @brief 安全启动所有输出
  388. */
  389. static void safe_start(char type)
  390. {
  391. char res = safe_lamp(RT_TRUE) + safe_hv(RT_TRUE);
  392. if (res)
  393. {
  394. safe_lamp(1);
  395. safe_hv(1);
  396. LOG_I(" safe_start %c \n\n", type);
  397. }
  398. }
  399. /**
  400. * @brief 判断当前时间是否在时控时段内
  401. */
  402. static int in_time_range(void)
  403. {
  404. uint8_t s = dev_info.time_start;
  405. uint8_t e = dev_info.time_end;
  406. uint8_t h = time_now.hour;
  407. // 全天工作
  408. if ((s == 0 && e == 23) || (s == e))
  409. {
  410. return 1;
  411. }
  412. if (s <= e)
  413. {
  414. return (h >= s && h < e);
  415. }
  416. else
  417. {
  418. return (h >= s || h < e);
  419. }
  420. }
  421. /**
  422. * @brief 检查是否有其他保护激活
  423. */
  424. static uint8_t other_protect_active(void)
  425. {
  426. #define PROTECT_LOW_TEMP 0x7A
  427. #define PROTECT_HIGH_TEMP 0x9D
  428. uint8_t res = (sys_sta.undervolt_protect == PROTECT_ACTIVE ||
  429. sys_sta.temp_protect == PROTECT_HIGH_TEMP ||
  430. sys_sta.temp_protect == PROTECT_LOW_TEMP ||
  431. sys_sta.tilt_protect == PROTECT_ACTIVE ||
  432. sys_sta.rain_protect == PROTECT_ACTIVE);
  433. if (res && KEY_IN_TEST_TIME != VALID)
  434. {
  435. char power_off = safe_lamp(RT_FALSE) + safe_hv(RT_FALSE);
  436. if (power_off)
  437. {
  438. char buf[50] = {0};
  439. sprintf(buf, "停止工作:%s", other_protect_name());
  440. Recode_e_code(e_protect_stopwork, res + power_off, buf);
  441. }
  442. }
  443. return res;
  444. }
  445. /**
  446. * @brief 获取当前激活的保护名称
  447. */
  448. static const char *other_protect_name(void)
  449. {
  450. if (sys_sta.undervolt_protect == PROTECT_ACTIVE)
  451. return "欠压保护";
  452. if (sys_sta.temp_protect == PROTECT_HIGH_TEMP)
  453. return "高温保护";
  454. if (sys_sta.temp_protect == PROTECT_LOW_TEMP)
  455. return "低温保护";
  456. if (sys_sta.tilt_protect == PROTECT_ACTIVE)
  457. return "倾倒保护";
  458. if (sys_sta.rain_protect == PROTECT_ACTIVE)
  459. return "雨控保护";
  460. return "";
  461. }
  462. /*********************↑ 辅助函数 ↑***********************/
  463. /*********************↓ 光控保护(带防抖) ↓***********************/
  464. /**
  465. * @brief 获取当前光控条件
  466. */
  467. static uint8_t get_light_condition(void)
  468. {
  469. if (dev_info.light_flag == 0)
  470. {
  471. if (sys_sta.charge_voltage >= PV_VOLTAGE_DAY_THRESHOLD)
  472. {
  473. return 1;
  474. }
  475. else if (sys_sta.charge_voltage <= PV_VOLTAGE_NIGHT_THRESHOLD)
  476. {
  477. return 0;
  478. }
  479. else
  480. {
  481. return 2;
  482. }
  483. }
  484. else
  485. {
  486. if (sys_sta.light_percent >= LIGHT_VAL_DAY_THRESHOLD)
  487. {
  488. return 1;
  489. }
  490. else if (sys_sta.light_percent <= LIGHT_VAL_NIGHT_THRESHOLD)
  491. {
  492. return 0;
  493. }
  494. else
  495. {
  496. return 2;
  497. }
  498. }
  499. }
  500. /**
  501. * @brief 更新光控保护状态(带防抖机制) <带防抖的确认当前是否光控以及是否解除>
  502. */
  503. static char check_light_protect(void)
  504. {
  505. uint8_t is_protect = get_light_condition();
  506. uint32_t now = get_timestamp();
  507. static uint32_t start_change_time = 0;
  508. static char ready_change = 0x9D;
  509. static uint32_t count_bak = 0;
  510. static work_state_DEF old_work_state = work_state_IDLE;
  511. static uint8_t next_status;
  512. if (next_status != 0)
  513. {
  514. next_status = 0;
  515. }
  516. if (is_protect == 2) // 光控水平介于
  517. {
  518. if (ready_change != 0x9D) // 本次切换取消
  519. {
  520. rt_kprintf("本次光控切换放弃(光控中间态) 当前:%d\n", s_light_protect);
  521. start_change_time = 0;
  522. ready_change = 0x9D;
  523. count_bak = 0;
  524. }
  525. return s_light_protect;
  526. }
  527. if (s_light_protect != is_protect && ready_change == 0x9D) // 本次切换开始
  528. {
  529. rt_kprintf("开启光控切换 :%d -> %d [%d]\n", s_light_protect, is_protect, sys_sta.light_percent);
  530. start_change_time = now;
  531. ready_change = 1;
  532. count_bak = 0;
  533. next_status = is_protect;
  534. }
  535. else if (s_light_protect != is_protect) // 本次切换结果确认
  536. {
  537. count_bak++;
  538. if ((now - start_change_time) > LIGHT_DEBOUNCE_SEC) // 检查是否光控触发或解除
  539. {
  540. s_light_protect = is_protect;
  541. rt_kprintf("(time)本次光控切换完成 当前:%d\n", s_light_protect);
  542. start_change_time = 0;
  543. ready_change = 0x9D;
  544. count_bak = 0;
  545. }
  546. else if (count_bak > ((1000 / WORK_BASE_MS * LIGHT_DEBOUNCE_SEC) + 5)) // 备用方案 给时间戳逻辑补漏
  547. {
  548. s_light_protect = is_protect;
  549. rt_kprintf("(count)本次光控切换完成 当前:%d\n", s_light_protect);
  550. start_change_time = 0;
  551. ready_change = 0x9D;
  552. count_bak = 0;
  553. }
  554. if (ready_change == 0x9D)
  555. {
  556. if (s_light_protect && (dev_info.light_flag == 0) && (sys_sta.work_state != work_state_CHG)) // 太阳能板光控 进入充电状态
  557. {
  558. old_work_state = sys_sta.work_state;
  559. sys_sta.work_state = work_state_CHG;
  560. }
  561. if ((s_light_protect == 0) && (sys_sta.work_state == work_state_CHG)) // 光控解除了 一起解除充电状态
  562. {
  563. if (old_work_state < work_state_CHG)
  564. {
  565. sys_sta.work_state = old_work_state;
  566. }
  567. else
  568. {
  569. sys_sta.work_state = work_state_IDLE;
  570. }
  571. }
  572. }
  573. }
  574. else if (ready_change != 0x9D) // 本次切换取消
  575. {
  576. rt_kprintf("本次光控切换放弃 当前:%d\n", s_light_protect);
  577. start_change_time = 0;
  578. ready_change = 0x9D;
  579. count_bak = 0;
  580. }
  581. if (s_light_protect && (dev_info.light_flag == 0) && (sys_sta.work_state != work_state_CHG))
  582. {
  583. old_work_state = sys_sta.work_state;
  584. sys_sta.work_state = work_state_CHG;
  585. }
  586. return s_light_protect;
  587. }
  588. /*********************↑ 光控保护 ↑***********************/
  589. /*********************↓ 工作时长管理 ↓***********************/
  590. /**
  591. * @brief 检查工作时间是否已到
  592. */
  593. static uint8_t is_duration_expired(void)
  594. {
  595. if (s_auto_mode)
  596. return 0;
  597. if (s_start_time == 0)
  598. return 0;
  599. uint32_t elapsed = get_timestamp() - s_start_time;
  600. return elapsed >= s_duration_sec;
  601. }
  602. /**
  603. * @brief 获取已工作时长(秒)
  604. */
  605. static uint32_t get_elapsed_sec(void)
  606. {
  607. if (s_start_time == 0)
  608. return 0;
  609. uint32_t elapsed = get_timestamp() - s_start_time;
  610. return (elapsed < s_duration_sec) ? elapsed : s_duration_sec;
  611. }
  612. /*********************↑ 工作时长管理 ↑***********************/
  613. /*********************↓ 工作控制 ↓***********************/
  614. /**
  615. * @brief 开始工作
  616. */
  617. static void work_start(void)
  618. {
  619. char log_buf[52];
  620. if (s_state == WORK_STATE_WORKING)
  621. {
  622. return;
  623. }
  624. s_start_time = get_timestamp();
  625. if (dev_info.time_ctrl_enable == 0) // 光控模式
  626. {
  627. s_duration_sec = dev_info.light_work_duration * 3600; /* 分钟转秒 */
  628. s_auto_mode = (dev_info.light_work_duration == 0);
  629. }
  630. else if (in_time_range()) // 时控模式 && 在时段内(中途进入)
  631. {
  632. uint8_t start = dev_info.time_start;
  633. uint8_t end = dev_info.time_end;
  634. uint8_t cur_hour = time_now.hour;
  635. uint8_t cur_min = time_now.minute;
  636. uint8_t cur_sec = time_now.second;
  637. uint32_t elapsed_sec = 0;
  638. /* 计算从时段开始到当前已过的时间 */
  639. if (start < end)
  640. {
  641. /* 同天:17-20,当前18点 -> 已过1小时 */
  642. elapsed_sec = (cur_hour - start) * 3600 + cur_min * 60 + cur_sec;
  643. }
  644. else
  645. {
  646. /* 跨天:22-6,当前1点 -> 已过 (24-22) + 1 = 3小时 */
  647. if (cur_hour >= start)
  648. {
  649. /* 第一天:22-23点 */
  650. elapsed_sec = (cur_hour - start) * 3600 + cur_min * 60 + cur_sec;
  651. }
  652. else
  653. {
  654. /* 第二天:0-5点 */
  655. elapsed_sec = (24 - start + cur_hour) * 3600 + cur_min * 60 + cur_sec;
  656. }
  657. }
  658. uint32_t full_sec = calculate_hours_diff(start, end) * 3600;
  659. s_duration_sec = (elapsed_sec < full_sec) ? (full_sec - elapsed_sec) : 0;
  660. s_auto_mode = 0;
  661. LOG_I("时控中途进入: 完整=%lu秒, 已过=%lu秒, 剩余=%lu秒", full_sec, elapsed_sec, s_duration_sec);
  662. }
  663. else // 时控模式 && 不在时段内(正常进入,等进入时段)
  664. {
  665. s_duration_sec = calculate_hours_diff(dev_info.time_start, dev_info.time_end) * 3600;
  666. s_auto_mode = 0;
  667. }
  668. s_start_hour = time_now.hour;
  669. s_start_minute = time_now.minute;
  670. safe_start('A');
  671. sys_sta.work_state = work_state_WORK;
  672. sys_sta.hv_count++;
  673. if (dev_info.time_ctrl_enable == 0)
  674. {
  675. if (s_auto_mode)
  676. {
  677. rt_snprintf(log_buf, sizeof(log_buf), "工作开始:自动模式(天亮结束)");
  678. LOG_I("%s", log_buf);
  679. Recode_e_code(e_work_start, 1, log_buf);
  680. }
  681. else
  682. {
  683. rt_snprintf(log_buf, sizeof(log_buf), "工作开始:光控模式 %d小时", dev_info.light_work_duration);
  684. LOG_I("%s", log_buf);
  685. Recode_e_code(e_work_start, 2, log_buf);
  686. }
  687. }
  688. else
  689. {
  690. uint8_t start = dev_info.time_start;
  691. uint8_t end = dev_info.time_end;
  692. uint8_t hour_diff = calculate_hours_diff(start, end);
  693. rt_snprintf(log_buf, sizeof(log_buf), "工作开始:时控模式 %d小时(%02d-%02d)", hour_diff, start, end);
  694. LOG_I("%s", log_buf);
  695. Recode_e_code(e_work_start, 3, log_buf);
  696. }
  697. }
  698. /**
  699. * @brief 停止工作
  700. */
  701. static void work_stop(const char *reason, uint8_t start_cooldown)
  702. {
  703. char log_buf[52];
  704. clear_work_state();
  705. safe_stop('A');
  706. s_state = WORK_STATE_IDLE;
  707. s_start_time = 0;
  708. s_start_hour = 0;
  709. s_start_minute = 0;
  710. sys_sta.work_state = work_state_IDLE;
  711. rt_snprintf(log_buf, sizeof(log_buf), "工作停止:%s", reason);
  712. LOG_I("%s", log_buf);
  713. Recode_e_code(e_work_stop, 0, log_buf);
  714. if (start_cooldown)
  715. {
  716. s_state = WORK_STATE_COOLDOWN;
  717. s_cooldown_until = get_timestamp() + WORK_COOLDOWN_SEC;
  718. LOG_I("冷却开始: %d秒内不能工作", WORK_COOLDOWN_SEC);
  719. }
  720. }
  721. /**
  722. * @brief 检查是否还在工作时间
  723. *
  724. * @return char 0-不在 1-在
  725. */
  726. static char check_time_vaild(void)
  727. {
  728. char res = 0;
  729. if (dev_info.time_ctrl_enable) // 时控模式
  730. {
  731. res = in_time_range();
  732. if (res)
  733. {
  734. return 1;
  735. }
  736. }
  737. else if (!is_duration_expired())
  738. {
  739. return 1;
  740. }
  741. return 0;
  742. }
  743. /*********************↑ 工作控制 ↑***********************/
  744. /*********************↓ 主线程 ↓***********************/
  745. /**
  746. * @brief 判断NTP时间是否已同步成功
  747. * @return 1=已同步, 0=未同步
  748. *
  749. * 判断逻辑:当前系统时间若为2026年5月及以前,认为未同步
  750. * 因为设备实际运行时间在2026年6月之后
  751. */
  752. static uint8_t is_ntp_synced(void)
  753. {
  754. /* 计算当前时间的 (年*12 + 月) 值 */
  755. uint32_t current_value = (uint32_t)(time_now.year - 2000) * 12 + time_now.month;
  756. /* 2026年5月的参考值:26*12 + 5 = 317 */
  757. uint32_t threshold = 26 * 12 + 5;
  758. // rt_kprintf("current_value %d threshold %d res = %d" , current_value ,threshold, (current_value > threshold));
  759. /* 如果当前时间 <= 2026年5月,认为NTP未同步 */
  760. if (current_value <= threshold)
  761. {
  762. return 0; /* 未同步 */
  763. }
  764. return 1; /* 已同步 */
  765. }
  766. /**
  767. * @brief 工作控制主线程
  768. */
  769. static void work_thread_entry(void *param)
  770. {
  771. uint16_t ftp_wait_cnt = 0;
  772. uint32_t last_sync_time = 0;
  773. uint32_t last_log_time = 0;
  774. char log_process = 0;
  775. uint8_t s_state_last = WORK_STATE_IDLE; // 备用状态
  776. int8_t last_sta = WORK_STATE_IDLE; // 打印日志用
  777. /* 等待配置加载完成 */
  778. int wait_cnt = 0;
  779. while (ef_cfged != 1 && wait_cnt < 100)
  780. {
  781. rt_thread_mdelay(100);
  782. wait_cnt++;
  783. }
  784. rt_thread_mdelay(3000);
  785. /* ===== 新增:等待时间同步完成(最多2分钟)===== */
  786. {
  787. uint32_t time_wait_cnt = 0;
  788. const uint32_t TIME_WAIT_MAX = 120; /* 最多等待120秒 */
  789. const uint8_t TIME_CHECK_INTERVAL = 1; /* 每秒检查一次 */
  790. /* 检查RTC时间是否有效(非默认值2026-01-01) */
  791. while (time_wait_cnt < TIME_WAIT_MAX)
  792. {
  793. /* 检查RTC是否已从默认值更新 */
  794. if (is_ntp_synced())
  795. {
  796. LOG_I("时间同步已完成: %04d-%02d-%02d %02d:%02d:%02d",
  797. time_now.year, time_now.month, time_now.date,
  798. time_now.hour, time_now.minute, time_now.second);
  799. break;
  800. }
  801. time_wait_cnt++;
  802. if (time_wait_cnt % 10 == 0)
  803. {
  804. LOG_I("等待时间同步中... (%d/%d秒)", time_wait_cnt, TIME_WAIT_MAX);
  805. }
  806. rt_thread_mdelay(TIME_CHECK_INTERVAL * 1000);
  807. }
  808. if (time_wait_cnt >= TIME_WAIT_MAX)
  809. {
  810. Recode_e_code(e_work_without_ntp, 0, "工作开启时未同步时间");
  811. LOG_W("时间同步等待超时(2分钟),使用默认时间继续工作");
  812. }
  813. }
  814. /* ============================================= */
  815. /* 从RTC恢复之前的工作状态 */
  816. if (restore_work_state())
  817. {
  818. uint32_t now = get_timestamp();
  819. uint32_t elapsed = (now > s_start_time) ? (now - s_start_time) : 0;
  820. if (elapsed < s_duration_sec)
  821. {
  822. safe_start('B');
  823. sys_sta.work_state = work_state_WORK;
  824. uint32_t remain = s_duration_sec - elapsed;
  825. LOG_I("工作恢复: (已工作%lu时%lu分, 剩余%lu时%lu分)", elapsed / 3600, elapsed % 3600 / 60, remain / 3600, remain % 3600 / 60);
  826. Recode_e_code(e_work_resume, 0, "工作恢复:重启后恢复(RTC)");
  827. while (other_protect_active())
  828. {
  829. LOG_I("等待工作恢复: %s", other_protect_name());
  830. rt_thread_mdelay(10000);
  831. sync_mqtt_vars();
  832. }
  833. }
  834. else
  835. {
  836. clear_work_state();
  837. s_state = WORK_STATE_IDLE;
  838. s_start_time = 0;
  839. LOG_I("保存的工作已过期,不恢复");
  840. }
  841. }
  842. /* 主循环 */
  843. while (1)
  844. {
  845. uint32_t now = get_timestamp();
  846. uint8_t protect_active = other_protect_active(); // 刷新其他保护
  847. check_light_protect(); // 刷新光控保护
  848. sync_mqtt_vars();
  849. /* 3.1 关机检查 */
  850. if (dev_info.power_state == 0)
  851. {
  852. if (s_state == WORK_STATE_WORKING)
  853. {
  854. work_stop("设备关机", 0);
  855. s_state = WORK_STATE_IDLE;
  856. safe_stop('G');
  857. }
  858. rt_thread_mdelay(WORK_BASE_MS);
  859. continue;
  860. }
  861. /* 3.3 保护处理(简化版)*/
  862. if (protect_active && (s_state != WORK_STATE_OTHER_PRT))
  863. {
  864. LOG_I("保护触发: %s", other_protect_name());
  865. s_state_last = s_state;
  866. // if (s_state == WORK_STATE_WORKING)
  867. // {
  868. // s_state_last = WORK_STATE_WORKING;
  869. // }
  870. // else
  871. // {
  872. // s_state_last = WORK_STATE_IDLE;
  873. // }
  874. s_state = WORK_STATE_OTHER_PRT;
  875. }
  876. if (last_sta != s_state)
  877. {
  878. LOG_I("Detect sta chang %d -> %d", last_sta, s_state);
  879. last_sta = s_state;
  880. }
  881. if (light_prt_start_time != 0 && s_state != WORK_STATE_LIGHT_PRT) // 异常状态切换保护 WORK_STATE_LIGHT_PRT 专用
  882. {
  883. light_prt_start_time = 0;
  884. rt_kprintf("light_prt_start_time CLEAR\n");
  885. }
  886. if (s_protect_release_time != 0 && s_state != WORK_STATE_OTHER_PRT) // 异常状态切换保护 WORK_STATE_OTHER_PRT 专用
  887. {
  888. s_protect_release_time = 0;
  889. // rt_kprintf("s_protect_release_time CLEAR\n");
  890. }
  891. if ((start_ftp == VALID) && (ftp_wait_cnt * WORK_BASE_MS < (600000))) // 检查是否在升级
  892. {
  893. ftp_wait_cnt++;
  894. if (ftp_wait_cnt > 0 && (ftp_wait_cnt % (10000 / WORK_BASE_MS) == 0))
  895. {
  896. rt_kprintf("work_thread_entry continue %d/%d\n", ftp_wait_cnt * WORK_BASE_MS / 1000, 600);
  897. }
  898. rt_thread_mdelay(WORK_BASE_MS);
  899. safe_stop('H');
  900. continue;
  901. }
  902. else if (ftp_wait_cnt != 0)
  903. {
  904. ftp_wait_cnt = 0;
  905. }
  906. /* ========== 3.5 状态机处理 ========== */
  907. switch (s_state)
  908. {
  909. case WORK_STATE_IDLE: /* 空闲状态 */
  910. {
  911. uint8_t can_work = 0;
  912. if (dev_info.time_ctrl_enable) // 时控模式
  913. {
  914. can_work = in_time_range();
  915. if (can_work)
  916. {
  917. LOG_I("时控条件满足,开始工作");
  918. }
  919. }
  920. else if (!s_light_protect && !get_light_condition())
  921. {
  922. can_work = 1;
  923. LOG_I("光控条件满足(夜晚),开始工作");
  924. }
  925. if (can_work)
  926. {
  927. work_start();
  928. s_state = WORK_STATE_WORKING;
  929. continue;
  930. }
  931. safe_stop('B');
  932. break;
  933. }
  934. case WORK_STATE_WORKING: /* 工作中状态 */
  935. {
  936. uint8_t should_stop = 0;
  937. const char *stop_reason = "";
  938. if (is_duration_expired())
  939. {
  940. should_stop = 1;
  941. stop_reason = "时长到";
  942. }
  943. else if ((s_auto_mode && s_light_protect) && (dev_info.time_ctrl_enable == 0))
  944. {
  945. should_stop = 1;
  946. stop_reason = "天亮";
  947. }
  948. if (should_stop)
  949. {
  950. work_stop(stop_reason, 1);
  951. s_state = WORK_STATE_COOLDOWN;
  952. continue;
  953. }
  954. else if (s_light_protect && (dev_info.time_ctrl_enable == 0)) // 检查光控
  955. {
  956. s_state = WORK_STATE_LIGHT_PRT;
  957. continue;
  958. }
  959. else
  960. {
  961. if (now - last_sync_time >= 1)
  962. {
  963. last_sync_time = now;
  964. }
  965. if (!s_auto_mode && s_duration_sec > 0)
  966. {
  967. uint32_t elapsed = get_elapsed_sec();
  968. uint32_t progress = (elapsed * 100) / s_duration_sec;
  969. uint32_t remain = s_duration_sec - elapsed;
  970. if (log_process != progress)
  971. {
  972. log_process = progress;
  973. LOG_I("工作进度: %u%% (已工作%lu时%lu分, 剩余%lu时%lu分)", progress, elapsed / 3600, elapsed % 3600 / 60, remain / 3600, remain % 3600 / 60);
  974. }
  975. }
  976. }
  977. safe_start('C');
  978. break;
  979. }
  980. case WORK_STATE_LIGHT_PRT: // 光控保护
  981. {
  982. if ((dev_info.time_ctrl_enable == 1))
  983. {
  984. s_state = WORK_STATE_IDLE;
  985. LOG_W("时控模式 禁用光控保护\n");
  986. continue;
  987. }
  988. if (light_prt_start_time == 0)
  989. {
  990. light_prt_start_time = get_timestamp();
  991. }
  992. if (s_light_protect) // 光控着吗?
  993. {
  994. // rt_kprintf("WORK_STATE_LIGHT_PRT A\n");
  995. if (is_duration_expired()) // 工作时间到没?
  996. {
  997. work_stop("光控至工作时间结束", 1);
  998. light_prt_start_time = 0;
  999. s_state = WORK_STATE_COOLDOWN;
  1000. continue;
  1001. }
  1002. else if ((now - light_prt_start_time) >= LIGHT_BREAK_WORK_SEC) // 光控超时没
  1003. {
  1004. work_stop("长时间光控退出", 1);
  1005. light_prt_start_time = 0;
  1006. s_state = WORK_STATE_COOLDOWN;
  1007. }
  1008. }
  1009. else // 保护解除
  1010. {
  1011. s_state = WORK_STATE_WORKING;
  1012. light_prt_start_time = 0;
  1013. continue;
  1014. }
  1015. safe_stop('C');
  1016. break;
  1017. }
  1018. case WORK_STATE_COOLDOWN: /* 冷却状态 */
  1019. {
  1020. if ((dev_info.time_ctrl_enable == 1))
  1021. {
  1022. s_state = WORK_STATE_IDLE;
  1023. LOG_W("时控模式 禁用光控状态\n");
  1024. continue;
  1025. }
  1026. if (s_cooldown_until > 0 && now >= s_cooldown_until)
  1027. {
  1028. s_cooldown_until = 0;
  1029. if (dev_info.time_ctrl_enable == 0)
  1030. {
  1031. s_state = WORK_STATE_WAIT_DAY;
  1032. LOG_I("冷却结束,等待天亮后才能再次工作");
  1033. }
  1034. else
  1035. {
  1036. s_state = WORK_STATE_IDLE;
  1037. LOG_I("冷却结束,随时可以工作(时控)");
  1038. }
  1039. continue;
  1040. }
  1041. else if (s_cooldown_until == 0)
  1042. {
  1043. s_cooldown_until = get_timestamp() + WORK_COOLDOWN_SEC;
  1044. LOG_I("异常进入冷却开始");
  1045. }
  1046. safe_stop('D');
  1047. break;
  1048. }
  1049. case WORK_STATE_WAIT_DAY: /* 等待天亮状态 */
  1050. {
  1051. if ((dev_info.time_ctrl_enable == 1))
  1052. {
  1053. s_state = WORK_STATE_IDLE;
  1054. LOG_W("时控模式 禁用光控状态\n");
  1055. continue;
  1056. }
  1057. if (now - last_log_time >= 60)
  1058. {
  1059. last_log_time = now;
  1060. LOG_D("等待天亮中... 光控保护=%s", s_light_protect ? "白天" : "夜晚");
  1061. }
  1062. if (s_light_protect) // 天亮了
  1063. {
  1064. s_state = WORK_STATE_IDLE;
  1065. continue;
  1066. }
  1067. safe_stop('E');
  1068. break;
  1069. }
  1070. case WORK_STATE_OTHER_PRT:
  1071. {
  1072. if (!protect_active && s_protect_release_time == 0) // 保护解除了
  1073. {
  1074. s_protect_release_time = now + PROTECT_RELEASE_DELAY_SEC;
  1075. LOG_I("保护已解除,延迟%d秒后恢复", PROTECT_RELEASE_DELAY_SEC);
  1076. }
  1077. else if (!protect_active)
  1078. {
  1079. if (now >= s_protect_release_time)
  1080. {
  1081. s_state = s_state_last;
  1082. LOG_I("退出保护:恢复工作状态=%d\n", s_state_last);
  1083. continue;
  1084. }
  1085. }
  1086. // else if (!check_time_vaild())
  1087. // {
  1088. // s_state = WORK_STATE_IDLE;
  1089. // LOG_I("失效保护:(已不在工作时间)恢复至空闲状态\n");
  1090. // continue;
  1091. // }
  1092. safe_stop('F');
  1093. break;
  1094. }
  1095. default:
  1096. {
  1097. LOG_W("未知状态: %d, 重置为空闲", s_state);
  1098. s_state = WORK_STATE_IDLE;
  1099. break;
  1100. }
  1101. }
  1102. rt_thread_mdelay(WORK_BASE_MS);
  1103. }
  1104. }
  1105. /*********************↑ 主线程 ↑***********************/
  1106. /*********************↓ 初始化 ↓***********************/
  1107. /**
  1108. * @brief 启动工作控制模块
  1109. */
  1110. static void work_startup(void)
  1111. {
  1112. if (s_thread)
  1113. return;
  1114. s_running = RT_TRUE;
  1115. s_thread = rt_thread_create("work", work_thread_entry, NULL, 2048, 13, 10);
  1116. if (s_thread)
  1117. {
  1118. rt_thread_startup(s_thread);
  1119. // LOG_I("工作控制模块已启动");
  1120. }
  1121. else
  1122. {
  1123. LOG_E("创建工作线程失败");
  1124. }
  1125. }
  1126. /**
  1127. * @brief 工作模块自动初始化
  1128. */
  1129. static int work_init(void)
  1130. {
  1131. work_startup();
  1132. return 0;
  1133. }
  1134. INIT_APP_EXPORT(work_init);
  1135. /*********************↑ 初始化 ↑***********************/
  1136. /*********************↓ MSH命令 ↓***********************/
  1137. /**
  1138. * @brief MSH命令:查看工作状态
  1139. */
  1140. static void cmd_work_status(int argc, char **argv)
  1141. {
  1142. uint32_t now = get_timestamp();
  1143. uint32_t elapsed = 0;
  1144. uint32_t remain = 0;
  1145. uint32_t progress = 0;
  1146. uint32_t cooldown_remain = 0;
  1147. if (s_state == WORK_STATE_WORKING && s_start_time > 0)
  1148. {
  1149. elapsed = (now > s_start_time) ? now - s_start_time : 0;
  1150. if (elapsed > s_duration_sec)
  1151. elapsed = s_duration_sec;
  1152. if (s_duration_sec > 0 && !s_auto_mode)
  1153. {
  1154. progress = (elapsed * 100) / s_duration_sec;
  1155. remain = s_duration_sec - elapsed;
  1156. }
  1157. }
  1158. if (s_state == WORK_STATE_COOLDOWN && s_cooldown_until > now)
  1159. {
  1160. cooldown_remain = s_cooldown_until - now;
  1161. }
  1162. rt_kprintf("\n");
  1163. rt_kprintf("╔══════════════════════════════════════════════════════════════╗\n");
  1164. rt_kprintf("║ WORK STATUS REPORT ║\n");
  1165. rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n");
  1166. rt_kprintf("║ 开关机状态 : %-46s ║\n", dev_info.power_state ? "开机" : "关机");
  1167. char buf[16] = {0};
  1168. switch (s_state)
  1169. {
  1170. case WORK_STATE_IDLE:
  1171. sprintf(buf, "空闲");
  1172. break;
  1173. case WORK_STATE_WORKING:
  1174. sprintf(buf, "工作中");
  1175. break;
  1176. case WORK_STATE_LIGHT_PRT:
  1177. sprintf(buf, "光控中");
  1178. break;
  1179. case WORK_STATE_COOLDOWN:
  1180. sprintf(buf, "冷却中");
  1181. break;
  1182. case WORK_STATE_WAIT_DAY:
  1183. sprintf(buf, "等天亮");
  1184. break;
  1185. case WORK_STATE_OTHER_PRT:
  1186. sprintf(buf, "保护中");
  1187. break;
  1188. default:
  1189. sprintf(buf, "未知?");
  1190. break;
  1191. }
  1192. rt_kprintf("║ 工作状态 : %s ║\n", buf);
  1193. if (s_state == WORK_STATE_WORKING)
  1194. {
  1195. if (dev_info.time_ctrl_enable == 0)
  1196. rt_kprintf("║ 光控模式 : %-46s ║\n", s_auto_mode ? "自动模式" : "光控模式");
  1197. rt_kprintf("║ 开始时间 : %02d:%02d%-44s ║\n", s_start_hour, s_start_minute, "");
  1198. if (elapsed >= 60)
  1199. {
  1200. uint32_t min = elapsed / 60;
  1201. uint32_t sec = elapsed % 60;
  1202. rt_kprintf("║ 已工作时长 : %u分%u秒%-42s ║\n", min, sec, "");
  1203. }
  1204. else
  1205. {
  1206. rt_kprintf("║ 已工作时长 : %u秒%-45s ║\n", elapsed, "");
  1207. }
  1208. if (!s_auto_mode)
  1209. {
  1210. if (remain >= 60)
  1211. {
  1212. uint32_t min = remain / 60;
  1213. uint32_t sec = remain % 60;
  1214. rt_kprintf("║ 剩余时长 : %u分%u秒%-42s ║\n", min, sec, "");
  1215. }
  1216. else
  1217. {
  1218. rt_kprintf("║ 剩余时长 : %u秒%-45s ║\n", remain, "");
  1219. }
  1220. rt_kprintf("║ 进度 : %3d%% %-46s ║\n", progress, "");
  1221. rt_kprintf("║ [");
  1222. uint8_t bar_len = progress / 2;
  1223. for (uint8_t i = 0; i < 50; i++)
  1224. {
  1225. if (i < bar_len)
  1226. rt_kprintf("=");
  1227. else if (i == bar_len)
  1228. rt_kprintf(">");
  1229. else
  1230. rt_kprintf(" ");
  1231. }
  1232. rt_kprintf("] %3d%%%27s ║\n", progress, "");
  1233. }
  1234. else
  1235. {
  1236. rt_kprintf("║ 工作模式 : 自动(天亮结束)%-35s ║\n", "");
  1237. }
  1238. if (dev_info.light_work_duration > 0)
  1239. {
  1240. rt_kprintf("║ 配置工作时长 : %d小时 ║\n", dev_info.light_work_duration);
  1241. }
  1242. else
  1243. {
  1244. rt_kprintf("║ 配置工作时长 : 自动模式%-43s ║\n", "");
  1245. }
  1246. }
  1247. if (s_state == WORK_STATE_COOLDOWN && cooldown_remain > 0)
  1248. {
  1249. if (cooldown_remain >= 60)
  1250. {
  1251. uint32_t min = cooldown_remain / 60;
  1252. uint32_t sec = cooldown_remain % 60;
  1253. rt_kprintf("║ 冷却剩余时间 : %u分%u秒%-42s ║\n", min, sec, "");
  1254. }
  1255. else
  1256. {
  1257. rt_kprintf("║ 冷却剩余时间 : %u秒%-45s ║\n", cooldown_remain, "");
  1258. }
  1259. }
  1260. if (s_state == WORK_STATE_WAIT_DAY)
  1261. {
  1262. rt_kprintf("║ 状态说明 : 等待天亮后才能再次工作%-26s ║\n", "");
  1263. }
  1264. if (s_state == WORK_STATE_LIGHT_PRT && s_protect_release_time > 0)
  1265. {
  1266. uint32_t remain = (s_protect_release_time > now) ? s_protect_release_time - now : 0;
  1267. rt_kprintf("║ 保护延迟剩余 : %lu秒%-44s ║\n", remain, "");
  1268. }
  1269. rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n");
  1270. rt_kprintf("║ 控制模式 : %-46s ║\n", dev_info.time_ctrl_enable ? "时控模式" : "光控模式");
  1271. if (dev_info.time_ctrl_enable == 0)
  1272. {
  1273. rt_kprintf("║ 光控来源 : %-46s ║\n", dev_info.light_flag == 0 ? "太阳能板" : "光敏二极管");
  1274. rt_kprintf("║ 当前亮度 : %d.%d%%%-44s ║\n",
  1275. sys_sta.light_percent / 10, sys_sta.light_percent % 10, "");
  1276. rt_kprintf("║ 充电电压 : %dmV%-45s ║\n", sys_sta.charge_voltage, "");
  1277. rt_kprintf("║ 光控保护 : %-46s ║\n", s_light_protect ? "激活(白天)" : "未激活(夜晚)");
  1278. }
  1279. else
  1280. {
  1281. char range[32];
  1282. rt_snprintf(range, sizeof(range), "%02d:00 - %02d:00", dev_info.time_start, dev_info.time_end);
  1283. rt_kprintf("║ 时控时段 : %-46s ║\n", range);
  1284. rt_kprintf("║ 当前小时 : %d点%-46s ║\n", time_now.hour, "");
  1285. rt_kprintf("║ 是否在时段内 : %-46s ║\n", in_time_range() ? "是" : "否");
  1286. }
  1287. rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n");
  1288. rt_kprintf("║ 保护状态 : ║\n");
  1289. rt_kprintf("║ 光控保护 : %-46s ║\n", s_light_protect ? "激活" : "正常");
  1290. rt_kprintf("║ 欠压保护 : %-46s ║\n",
  1291. sys_sta.undervolt_protect == PROTECT_ACTIVE ? "激活" : "正常");
  1292. rt_kprintf("║ 高温保护 : %-46s ║\n",
  1293. sys_sta.temp_protect == PROTECT_HIGH_TEMP ? "激活" : "正常");
  1294. rt_kprintf("║ 低温保护 : %-46s ║\n",
  1295. sys_sta.temp_protect == PROTECT_LOW_TEMP ? "激活" : "正常");
  1296. rt_kprintf("║ 倾倒保护 : %-46s ║\n",
  1297. sys_sta.tilt_protect == PROTECT_ACTIVE ? "激活" : "正常");
  1298. rt_kprintf("║ 雨控保护 : %-46s ║\n",
  1299. sys_sta.rain_protect == PROTECT_ACTIVE ? "激活" : "正常");
  1300. rt_kprintf("╠══════════════════════════════════════════════════════════════╣\n");
  1301. rt_kprintf("║ 电气参数 : ║\n");
  1302. rt_kprintf("║ 电池电压 : %dmV%-45s ║\n", sys_sta.battery_voltage, "");
  1303. rt_kprintf("║ 主板温度 : %d.%d°C%-44s ║\n",
  1304. sys_sta.board_temp / 10, sys_sta.board_temp % 10, "");
  1305. rt_kprintf("║ 灯管电流 : %u%-47s ║\n", sys_sta.lamp_current, "");
  1306. rt_kprintf("║ 高压次数 : %u%-47s ║\n", sys_sta.hv_count, "");
  1307. rt_kprintf("║ 灯管开关 : %u%-47s ║\n", gpio_lamp_get_status(), "");
  1308. rt_kprintf("║ 高压开关 : %u%-47s ║\n", gpio_hv_get_status(), "");
  1309. rt_kprintf("╚══════════════════════════════════════════════════════════════╝\n");
  1310. }
  1311. MSH_CMD_EXPORT(cmd_work_status, 查看工作状态);
  1312. /**
  1313. * @brief MSH命令:手动控制工作
  1314. */
  1315. static void cmd_work_ctrl(int argc, char **argv)
  1316. {
  1317. if (argc < 2)
  1318. {
  1319. rt_kprintf("用法: work [start|stop|set <分钟>]\n");
  1320. rt_kprintf(" start - 手动开始工作\n");
  1321. rt_kprintf(" stop - 手动停止工作\n");
  1322. rt_kprintf(" set 5 - 设置工作时长为5分钟\n");
  1323. return;
  1324. }
  1325. if (strcmp(argv[1], "start") == 0)
  1326. {
  1327. if (dev_info.power_state == 0)
  1328. {
  1329. rt_kprintf("错误: 设备已关机\n");
  1330. return;
  1331. }
  1332. s_cooldown_until = 0;
  1333. s_protect_release_time = 0;
  1334. s_state = WORK_STATE_IDLE;
  1335. work_start();
  1336. rt_kprintf("已手动开始工作\n");
  1337. }
  1338. else if (strcmp(argv[1], "stop") == 0)
  1339. {
  1340. work_stop("手动停止", 0);
  1341. rt_kprintf("已手动停止工作\n");
  1342. }
  1343. else if (strcmp(argv[1], "set") == 0 && argc >= 3)
  1344. {
  1345. uint16_t minutes = atoi(argv[2]);
  1346. dev_info.light_work_duration = minutes;
  1347. save_dev_info();
  1348. if (minutes >= 60)
  1349. {
  1350. uint32_t hours = minutes / 60;
  1351. uint32_t mins = minutes % 60;
  1352. if (mins > 0)
  1353. {
  1354. rt_kprintf("工作时长已设置为 %u小时%u分钟\n", hours, mins);
  1355. }
  1356. else
  1357. {
  1358. rt_kprintf("工作时长已设置为 %u小时\n", hours);
  1359. }
  1360. }
  1361. else
  1362. {
  1363. rt_kprintf("工作时长已设置为 %d分钟\n", minutes);
  1364. }
  1365. }
  1366. else
  1367. {
  1368. rt_kprintf("未知命令: %s\n", argv[1]);
  1369. }
  1370. }
  1371. MSH_CMD_EXPORT(cmd_work_ctrl, 手动控制工作);
  1372. /**
  1373. * @brief MSH命令:调试信息
  1374. */
  1375. static void cmd_work_debug(int argc, char **argv)
  1376. {
  1377. rt_kprintf("\n========== WORK DEBUG ==========\n");
  1378. rt_kprintf("s_state: %d\n", s_state);
  1379. rt_kprintf("s_start_time: %u\n", s_start_time);
  1380. rt_kprintf("s_duration_sec: %lu\n", s_duration_sec);
  1381. rt_kprintf("s_auto_mode: %d\n", s_auto_mode);
  1382. rt_kprintf("s_cooldown_until: %u\n", s_cooldown_until);
  1383. rt_kprintf("s_light_protect: %d\n", s_light_protect);
  1384. rt_kprintf("s_protect_release_time: %u\n", s_protect_release_time);
  1385. rt_kprintf("light_percent: %d.%d%%\n",
  1386. sys_sta.light_percent / 10, sys_sta.light_percent % 10);
  1387. rt_kprintf("charge_voltage: %dmV\n", sys_sta.charge_voltage);
  1388. rt_kprintf("===============================\n");
  1389. }
  1390. MSH_CMD_EXPORT(cmd_work_debug, 调试信息);
  1391. /*********************↑ MSH命令 ↑***********************/