/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2024-08-16 RuoFeng the first version */ /*********************↓ define ↓************************/ #define DBG_ENABLE #define DBG_COLOR #define DBG_TAG "sy2" #define DBG_LEVEL DBG_INFO #include #define LOG_INR 10 // 默认的log打印间隔(作用域在本文件) #define RTU_OFF_MAX 180 #define WAIT_NET_MAX 150 #define ENABLE_WORK (Check_Allow_Work() == res_ok) #define NEW_TIMESTAMP tm_to_timestamp() /*********************↑ define ↑************************/ /** * * * * * */ /*********************↓ include ↓************************/ #include #include "rs485.h" #include "control.h" #include "test.h" #include #include "dtu_uart2.h" /*********************↑ include ↑************************/ /** * * * * * */ /*********************↓ extern ↓************************/ extern Temporal_list Check_Work_time_period(uint8_t coll_ch); extern works_def ws_now; extern Device_Status_typedef device_status; extern void HMI_WRITE(char *fmt, ...); extern JD_Test_Sta jts; extern Res_def Mtor_Control_ZP(Motor_Ctl_type mct, int8_t ctime); extern Sensor sensor[MAX_SENSOR_NUM]; extern Temporal_list Check_Work_time_period_0oclock(void); extern RTC_DateTypeDef GetDate; extern RTC_TimeTypeDef GetTime; extern uint8_t ReFlash_RTC(void); extern void CTL_CAM1(void); extern void CTL_CAM0(void); extern uint32_t tm_to_timestamp(void); extern char Demo_Mode_NEW; /*********************↑ extern ↑************************/ /** * * * * * */ /********************↓ declaration ↓***********************/ static void rtu1(void); static void rtu0(void); static char rturead(void); void Request_ntp(void); static void Photo_Base_Save(uint32_t v); // 拍照基点持久化(ENV + RTC备份寄存器) static uint32_t Photo_Base_Load(void); // 拍照基点恢复 /********************↑ declaration ↑***********************/ /** * * * * * */ /*********************↓ global variables ↓************************/ static uint16_t takephoto_cnt = 0; // 拍照触发计数 static uint16_t wait_data_cnt = 0; // 上报数据计数 static uint32_t tm_data = 0; // 计算数据时间 static uint32_t tm_photo = 0; // 计算图片时间 volatile SYSCFG syscfg = {0}; // 系统配置 volatile Limit limit = {0}; // 各种控的开关和参数 volatile wm_Def workmode = TypeMAX_wx; // 工作模式 WorkMode_Def WorkModeList[TypeMAX_wx] = // 工作模式类型 { {RunTime_wx, "时控间隔模式"}}; volatile uint16_t RayTigCnt = 0; // 红外触发计数 volatile static char RayTiged = 0; // 红外已触发过 volatile static char RayOneLess = 0; // 红外触发记录 volatile char CamFree = 1; // 相机空闲状态 volatile uint16_t INR_PIC = 0; // 图片上传间隔 volatile uint16_t INR_DAT = 0; // 图片上传间隔 volatile char stop_getinfo = 0; // dtuwrite时暂停 getinfo static uint8_t rain_exit = 0; // 雨控退出进度 volatile char errcnt_getinfo = 0; // err计数 // volatile char RTUGetUp = 0; // RTU上电开机 /*********************↑ global variables ↑************************/ /** * * * * * */ /** * @brief * * @return int8_t -1(超时) >1(进度百分比) */ // int8_t Get_luxworkTime(void) //{ // // ReFlash_RTC(); // // // // uint32_t start = 0, now = 0; // // // // start = lwork.start_time.Hours * 60 + lwork.start_time.Minutes; // // now = GetTime.Hours * 60 + GetTime.Minutes; // // // // if (abs(GetTime.Hours - lwork.start_time.Hours) >= 10 && GetTime.Hours < lwork.start_time.Hours) // // { // // now += 1440; // // } // // if ((now - start) > (limit.LuxTime * 60)) // // { // // return -2; // // } // // else // // { // // LOG_I("luxtime %d%% \n", ((now - start) * 100) / (limit.LuxTime * 60)); // // return ((now - start) * 100) / (limit.LuxTime * 60); // // } // // return -1; // } /** * @brief Get the Coll Info object * * @return uint8_t persen */ uint8_t GetCollInfo(void) { // uint16_t inr = 0; // (syscfg.photim >= 5) ? (inr = syscfg.photim - 1) : (inr = TakeINR); /* 直接按原始时间戳计算秒差, 不再调用 math_timestampe_NEW(避免其超窗时重置 tm_photo) */ ReFlash_RTC(); uint32_t now = NEW_TIMESTAMP; uint32_t last = tm_photo; uint32_t diff = (now > last) ? (now - last) : 0; if (INR_PIC == 0) { return 0; } return (diff * 100) / (INR_PIC); } /** * @brief 获取晨曦模式工作进度 不在工作范围 返回负数 * * @return int8_t val(-1 未开始工作 -2 参数校验失败) */ int8_t Get_swork_percent(void) { uint32_t start = 0, end = 0, temp1 = 0, now = 0; now = GetTime.Hours * 3600 + GetTime.Minutes * 60 + GetTime.Seconds; start = device_status.collecting_sh1 * 3600; end = device_status.collecting_eh1 * 3600; if (now < start && now > end) { return -1; } if (device_status.collecting_sh1 > device_status.collecting_eh1) { end += (24 * 3600); } temp1 = end - start; // rt_kprintf("now %d ALL %d\n" , (now - start) , temp1); if (((now - start) > temp1) || ((now - start) * 100 / temp1) > 100) { return -3; } return (now - start) * 100 / temp1; } /** * @brief 确认当前工作模式 * */ void Get_work_mode(void) { /** * @brief 开始确认 * */ static uint8_t ENPrint = 1; wm_Def bak = workmode; if (syscfg.en_tigmode == 0) { workmode = RunTime_wx; if (ENPrint || bak != workmode) { rt_kprintf("\n 时控间隔模式 %d-%d 时 > %d\n\n", device_status.collecting_sh1, device_status.collecting_eh1, ws_now); bak = workmode; ENPrint = 0; } } } #define Temp_Tig_MX 20 /** * @brief 检查是否允许工作 * * @return Caw_def 校验值 */ Caw_def Check_Allow_Work(void) { uint16_t log_en_inr = LOG_INR * 3; // 打印log的速度 数值越小 打印越频繁 static int16_t log_en_cnt = -1; rt_thread_mdelay(15); Get_work_mode(); /** * @brief 雨控 * */ // if (device_status.rain_update == 1) // 还需要再改 // { // LOG_W("CAW:雨控\n"); // device_status.rain_update = 0; // if (device_status.rain_status == 0) // { // device_status.rain_status = 1; // Mtor_Control_CM(mct_Foreward); // 关仓门 // { // Recode_e_code(e_rain_tig, 0, "进入雨控 关仓门"); // char *rain_tig = makeJson_Warn("Rain Tig In", "STA"); // DTU_Write("%s", rain_tig); // rt_free(rain_tig); // } // } // rain_exit = 90; // rt_thread_mdelay(1000); // return res_Rain; // } // else if (rain_exit > 0) // { // rain_exit--; // if (rain_exit == 0) // { // LOG_W(" exit rain , open the door\n"); // //Mtor_Control_CM(mct_Reversal); // 开仓门 // { // Recode_e_code(e_rain_tig, 1, "退出雨控 开仓门"); // device_status.rain_status = 0; // char *rain_tig = makeJson_Warn("Rain Tig Out", "STA"); // DTU_Write("%s", rain_tig); // rt_free(rain_tig); // } // } // else // { // LOG_W("ready to exit rain > %d\n", rain_exit); // } // rt_thread_mdelay(500); // return res_Rain; // } if (device_status.ds != 1) // 250327 新增 关机状态 { if (chk_log_time) { LOG_I("CAW:ds == 0\n"); } return res_PwrOff; } switch (workmode) { /** * @brief 时控模式 * */ case RunTime_wx: if ((device_status.collecting_sh1 == device_status.collecting_eh1) || (device_status.collecting_sh1 == 0 && device_status.collecting_eh1 == 24)) // 全天候牛马 { if (chk_log_time) { LOG_I("CAW:全天候牛马\n"); } } else if (device_status.collecting_eh1 < device_status.collecting_sh1) // 跨0点牛马 { if (Check_Work_time_period_0oclock() != tl_Center) { if (chk_log_time) { LOG_I("CAW:未在工作时间 0点\n"); } return res_Time; } } else // 正常牛马 { if (Check_Work_time_period(1) != tl_Center) { if (chk_log_time) { LOG_I("CAW:未在工作时间\n"); } return res_Time; } } break; default: break; } static uint8_t temp_up_cnt = 0; static uint8_t temp_dn_cnt = 0; /** * @brief 温湿度 * */ // rt_kprintf("sensor[s_Temp].value %d Luxlimit %d\n" , sensor[s_Temp].value , limit.temp_up); int32_t temp = 0; if (sensor[s_Temp].online >= 1) { temp = sensor[s_Temp].value; } else { temp = atoi(device_status.tempture); } if (sensor[s_Temp].online >= 1 && temp > (limit.temp_up * 10) && limit.temhumEN == ON) { if (temp_up_cnt >= Temp_Tig_MX) { LOG_I("CAW:触发温控上限 %d\n", temp_up_cnt); return res_Temp; } temp_up_cnt++; rt_thread_mdelay(1000); if (temp_up_cnt >= Temp_Tig_MX) { if (device_status.temp_status == 0) { LOG_I("CAW:触发温控上限 %d (%d > %d)\n", temp_up_cnt, temp, limit.temp_up * 10); } device_status.temp_status = 1; return res_Temp; } } else if (temp_up_cnt > 0) { temp_up_cnt--; rt_thread_mdelay(1000); if (temp_up_cnt == 0) { if (device_status.temp_status > 0) LOG_I("CAW:解除温控上限\n"); device_status.temp_status = 0; } } if (sensor[s_Temp].online >= 1 && temp < (limit.temp_dw * 10) && limit.temhumEN == ON) { if (temp_dn_cnt >= Temp_Tig_MX) { return res_Temp; } temp_dn_cnt++; rt_thread_mdelay(1000); if (temp_dn_cnt >= Temp_Tig_MX) { if (device_status.temp_status == 0) { LOG_I("CAW:触发温控下限 %d (%d < %d)\n", temp_dn_cnt, temp, limit.temp_dw * 10); } device_status.temp_status = 2; return res_Temp; } } else if (temp_dn_cnt > 0) { temp_dn_cnt--; rt_thread_mdelay(1000); if (temp_dn_cnt == 0) { if (device_status.temp_status > 0) LOG_I("CAW:解除温控下限\n"); device_status.temp_status = 0; } } /** * @brief 温湿度 * */ Check_TestFree(); return res_ok; } extern uint8_t stopreflashTakePhoto; extern uint8_t holdon_stareflash; /** * @brief 刷新工作状态 * */ void ReflashWorkSta(void) { if (ws_now >= ws_typemax) { rt_kprintf("ReflashWorkSta illegl wstype\n"); } if (holdon_stareflash) { return; } work_day_def work_day = {0}; switch (ws_now) { case ws_standby: HMI_WRITE("workSta.workinfo.txt=\"待机中...\""); break; // res_ok = 0, // 没问题 // res_Time, // 时控 // res_Light, // 光控 // res_TempH, // 温湿度 // res_Rain // 雨控 case ws_wait0: switch (Check_Allow_Work()) { case res_ok: HMI_WRITE("workSta.workinfo.txt=\"达到工作条件\""); break; case res_Time: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 时控\""); break; case res_Light: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 光控\""); break; case res_Temp: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 温控\""); break; case res_Humi: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 湿控\""); break; case res_Temp_D: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 温控(基于定位)\""); break; case res_Humi_D: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 湿控 (基于定位)\""); break; case res_Rain: HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 雨控\""); break; default: HMI_WRITE("workSta.workinfo.txt=\"未达到工作条件 %d\"", Check_Allow_Work()); break; } break; case ws_start_coll: HMI_WRITE("workSta.workinfo.txt=\"STEP1:收集开始\""); break; case ws_wait_coll: HMI_WRITE("workSta.workinfo.txt=\"STEP2:等待收集完成 %d%%\"", GetCollInfo()); break; case ws_takephoto: HMI_WRITE("workSta.workinfo.txt=\"STEP3:开始拍照\""); break; case ws_jumptake: HMI_WRITE("workSta.workinfo.txt=\"STEP4:采集中途退出 %d\"", Check_Allow_Work()); break; case ws_flap: HMI_WRITE("workSta.workinfo.txt=\"STEP5:翻板中\""); break; case ws_once_finsh: HMI_WRITE("workSta.workinfo.txt=\"STEP6:单次工作循环结束\""); break; case ws_finsh: ef_get_env_blob("work_day", &work_day, sizeof(work_day), NULL); HMI_WRITE("workSta.workinfo.txt=\"今日工作周期结束 %02d.%02d.%02d\"", work_day.year % 100, work_day.month, work_day.day); break; default: break; } } static void TigTakePho(void) { device_status.work_status = 3; LOG_W("STEP3 :takephoto"); ws_now = ws_takephoto; ctr_led(ON); DTU_Write("takephoto.mp3"); // 延时 rt_thread_delay(1000); Hk_Take_io(1); Request_ntp(); if (jts.switchled_test != ON && RunTime_wx == workmode) { ctr_led(OFF); } else { LOG_W("STEP4 :jump takephoto"); ws_now = ws_jumptake; rt_thread_delay(3000); if (jts.switchled_test != ON && RunTime_wx == workmode) { ctr_led(OFF); } } takephoto_cnt = 0; } /** * @brief 执行一次主要功能 * */ void work_mode_pkg(void) { #define cleek_A 3 #define cleek_B 9 ctr_led(ON); for (char a = 0; a < 24; a++) { rt_thread_mdelay(1000); rt_kprintf("work_mode_pkg waiting %d \n" , a); } Mtor_Control_CM(mct_Foreward, 1500); // 复位 LOG_W("STEP3 :Mtor_Control_ZP A1"); Mtor_Control_ZP(mct_Foreward, 0); rt_thread_mdelay(1500); LOG_W("STEP3 :Mtor_Control_ZP A2"); Mtor_Control_ZP(mct_Foreward, 0); Mtor_Control_CM(mct_Reversal, 1500); // 顶住 LOG_W("STEP4 :takephoto A"); rt_thread_mdelay(3500); TigTakePho(); Mtor_Control_CM(mct_Foreward, 1500); // 复位 LOG_W("STEP5 : Mtor_Control_ZP B1"); Mtor_Control_ZP(mct_Foreward, 0); rt_thread_mdelay(1000); LOG_W("STEP5 : Mtor_Control_ZP B2"); Mtor_Control_ZP(mct_Foreward, 0); rt_thread_mdelay(1000); LOG_W("STEP5 : Mtor_Control_ZP B3"); Mtor_Control_ZP(mct_Foreward, 0); rt_thread_mdelay(1000); LOG_W("STEP5 : Mtor_Control_ZP B4"); Mtor_Control_ZP(mct_Foreward, 0); rt_thread_mdelay(1000); LOG_W("STEP6 :takephoto B"); TigTakePho(); Mtor_Control_CM(mct_Foreward, 1500); // 复位 // rt_thread_mdelay(1000); LOG_W("STEP5 : Mtor_Control_ZP B5"); Mtor_Control_ZP(mct_Foreward, 0); Mtor_Control_CM(mct_Reversal, 1500); // 顶住 LOG_W("STEP7 :one cycle finsh"); ws_now = ws_once_finsh; rt_thread_mdelay(1000); } /** * @brief 记录当前时刻 * * @param time 时间戳 */ void recode_timestampe_NEW(uint32_t *tim) { ReFlash_RTC(); // 拍照基点只能由"定时触发"推进(工作窗口内到点 / 待机定点 / 退出窗口补拍); // 手动与测试拍照(按键手动模式、立即拍照测试、一键测试)一律不得改动基点, // 否则会把下次定时拍照的时间基准带偏。 if (tim == &tm_photo && (ws_now == ws_KeyTig || jts.takephoto_test == ON || jts.autotest == ON)) { LOG_W("photo base NOT advance (manual/test mode)\n"); rt_thread_mdelay(1000); return; } *tim = NEW_TIMESTAMP; if (tim == &tm_photo) { rt_kprintf("recode_timestampe (photo) > %d sec\n", *tim); Photo_Base_Save(*tim); // 持久化拍照时间基点: ENV + RTC备份寄存器(重启/升级都不丢) } else if (tim == &tm_data) { rt_kprintf("recode_timestampe (data ) > %d sec\n", *tim); } rt_thread_mdelay(1000); } /** * @brief 计算差值 * * @param time 时间戳 * @return uint32_t 差值 */ uint32_t math_timestampe_NEW(uint32_t *tim) { // uint16_t log_en_inr = LOG_INR * 3; // 打印log的速度 数值越小 打印越频繁 // static int16_t log_en_cnt = -1; static struct tm tm_bak; static char err_cnt = 0; char time_bak[30] = {0}; if (*tim == NEW_TIMESTAMP) { rt_thread_mdelay(1000); } ReFlash_RTC(); uint32_t now = NEW_TIMESTAMP; uint32_t val = (now - *tim); if (val > 0 && val < ((syscfg.dattim + syscfg.photim) * 60)) { // if (chk_log_time) // { // if (tim == &tm_photo) // { // rt_kprintf("math_timestampe (photo) > %d sec\n", val); // } // else // { // rt_kprintf("math_timestampe (data ) > %d sec\n", val); // } // } tm_bak.tm_year = GetDate.Year; tm_bak.tm_mon = GetDate.Month; tm_bak.tm_yday = GetDate.Date; tm_bak.tm_hour = GetTime.Hours; tm_bak.tm_min = GetTime.Minutes; tm_bak.tm_sec = GetTime.Seconds; err_cnt = 0; return val; } else { if (GetDate.Year > 200) { sprintf(time_bak, "%02d-%02d-%02d %02d:%02d:%02d", tm_bak.tm_year, tm_bak.tm_mon, tm_bak.tm_yday, tm_bak.tm_hour, tm_bak.tm_min, tm_bak.tm_sec); if (tm_bak.tm_year > 0 && tm_bak.tm_year < 200) { LOG_W("Write bak time %s!", time_bak); EXE_NTP_SETTIME(&tm_bak); } err_cnt++; if (err_cnt == 5) { Recode_e_code(e_rtc_fail, 0x9D, time_bak); } if (err_cnt > 30) { rt_hw_cpu_reset(); } } if (tim == &tm_photo) { rt_kprintf("math_timestampe fail (photo) > %d (now : %d >>> tim : %d) \n", val, now, *tim); recode_timestampe_NEW(tim); } else { rt_kprintf("math_timestampe fail (data ) > %d (now : %d >>> tim : %d) \n", val, now, *tim); recode_timestampe_NEW(tim); } } return 0; } /* ==================== 拍照定时触发(间隔N天 + 指定时分) ==================== */ #define TIMESTAMP_2000 946684800UL // 2000-01-01 00:00:00 (Unix秒), 时间戳有效性判定下限 #define TIMESTAMP_MAX_VALID 4102444800UL // 2100-01-01 00:00:00, 有效性上限(防备份区掉电读出0xFFFFFFFF) #define PHO_NEAR_SEC 300 // 接近拍照时刻提前量(秒), 用于保持RTU上电 /* ==================== 拍照基点持久化 ==================== */ #define PHO_BASE_MAGIC 0x5048U // BKP有效性魔数('P''H'), 备份区被清/掉电后内容不可信, 靠它判定 /** * @brief 写入拍照基点(ENV 与 RTC备份寄存器双写) * * ENV : 内部Flash分区 -> 整机断电(含VBAT)也不丢; 但固件升级可能擦掉 * BKP : RTC备份区 -> 升级不受影响; 但靠VBAT供电, 电池耗尽/更换会丢 * 两处同时写同一值, 任一处失效都能从另一处恢复。F1备份寄存器为16位, 故拆高低两个。 * 魔数最后写: 备份区被清/掉电后内容不可信, 只有魔数匹配才认那两个寄存器的值。 */ static void Photo_Base_Save(uint32_t v) { if (ef_set_env_blob("tm_photo", &v, sizeof(v)) != EF_NO_ERR) { LOG_E("tm_photo ENV save FAIL\n"); } BSP_Write_RTC_Reg(PhotoBase_DR10, Reg_Low_16, 0); // 先失效, 防止读到半更新的值 BSP_Write_RTC_Reg(PhotoBase_DR8, Reg_Low_16, v & 0xFFFFUL); BSP_Write_RTC_Reg(PhotoBase_DR9, Reg_Low_16, (v >> 16) & 0xFFFFUL); BSP_Write_RTC_Reg(PhotoBase_DR10, Reg_Low_16, PHO_BASE_MAGIC); // 再提交 } /** * @brief 读取拍照基点(ENV + RTC备份寄存器双源) * * 取两者中"合法且较大"的那个: 基点是单调递增的时间戳, 取大者即最近一次成功写入。 * 这样 ENV 被升级擦掉、或 BKP 因VBAT掉电变成垃圾值, 都能自动绕开。 * * @return uint32_t 基点(Unix秒); 两处均无效时返回 0 */ static uint32_t Photo_Base_Load(void) { uint32_t env_v = 0; uint32_t bkp_v = 0; if (ef_get_env_blob("tm_photo", &env_v, sizeof(env_v), NULL) != sizeof(env_v)) { env_v = 0; } if (BSP_Read_RTC_Reg(PhotoBase_DR10, Reg_Low_16) == PHO_BASE_MAGIC) // 魔数不符视为无记录 { bkp_v = ((BSP_Read_RTC_Reg(PhotoBase_DR9, Reg_Low_16) & 0xFFFFUL) << 16) | (BSP_Read_RTC_Reg(PhotoBase_DR8, Reg_Low_16) & 0xFFFFUL); } if (env_v < TIMESTAMP_2000 || env_v > TIMESTAMP_MAX_VALID) { env_v = 0; } if (bkp_v < TIMESTAMP_2000 || bkp_v > TIMESTAMP_MAX_VALID) { bkp_v = 0; } return (env_v > bkp_v) ? env_v : bkp_v; } /** * @brief 计算本次拍照的目标触发时刻(Unix秒, 本地时间) * * 判定基准: 取上次拍照结束【当天 00:00:00】+ 间隔天数 + 目标时分。 * - 基于Unix秒连续运算, 跨月/跨年/闰年天然正确; * - 基准取"当天0点"而非"拍照完成时刻", 拍照耗时不影响下次触发时刻, 周期稳定。 * * @return uint32_t 目标时刻; RTC未同步或上次拍照时间戳无效时返回 0 */ static uint32_t Get_Photo_Target(void) { if (GetDate.Year <= 20) // RTC 未同步(年<2021), 不计算 { return 0; } uint32_t last = tm_photo; // 上次拍照结束时间戳 if (last < TIMESTAMP_2000) // 上次拍照时间戳无效(未初始化/异常) { return 0; } /* 参数兜底(正常流程由开机校验保证合法, 此处仅防御) */ uint32_t day = (syscfg.pho_inr_day >= PHO_INR_DAY_MIN && syscfg.pho_inr_day <= PHO_INR_DAY_MAX) ? syscfg.pho_inr_day : PHO_INR_DAY_DEF; uint32_t hour = (syscfg.pho_t_hour <= 23) ? syscfg.pho_t_hour : PHO_T_HOUR_DEF; uint32_t min = (syscfg.pho_t_min <= 59) ? syscfg.pho_t_min : PHO_T_MIN_DEF; uint32_t last_day0 = last - (last % 86400UL); // 上次拍照当天 00:00:00(本地无夏令时) return last_day0 + day * 86400UL + hour * 3600UL + min * 60UL; } /** * @brief 时间戳(Unix秒, 本地) 格式化为 "MM-DD HH:MM:SS" * * 与 tm_to_timestamp() / Get_Photo_Target() 同一时基(本地, 无夏令时)。 */ static void Photo_Tm_Str(uint32_t tm, char *buf, uint16_t len) { if (tm < TIMESTAMP_2000) { rt_snprintf(buf, len, "--"); return; } uint32_t days = tm / 86400UL; uint32_t sec = tm % 86400UL; uint16_t year = 1970; while (1) { uint16_t dy = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 366 : 365); if (days < dy) { break; } days -= dy; year++; } const uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; uint8_t mon = 0; while (1) { uint8_t dy = mdays[mon]; if (mon == 1 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) { dy = 29; } if (days < dy) { break; } days -= dy; mon++; } rt_snprintf(buf, len, "%02d-%02d %02d:%02d:%02d", mon + 1, (uint8_t)(days + 1), (uint8_t)(sec / 3600UL), (uint8_t)((sec % 3600UL) / 60UL), (uint8_t)(sec % 60UL)); } /** * @brief 是否到达拍照触发时刻 * * @return uint8_t 1-应触发拍照 0-未到时刻 */ uint8_t Check_Photo_Trigger(void) { ReFlash_RTC(); uint32_t target = Get_Photo_Target(); if (target == 0) { return 0; } uint32_t now = NEW_TIMESTAMP; return (now >= target) ? 1 : 0; } /** * @brief 是否接近拍照触发时刻(提前 PHO_NEAR_SEC 秒) * * 用于数据上报后"接近拍照时刻不再断电", 避免拍照前重新给RTU上电。 * * @return uint8_t 1-接近拍照时刻 0-远离 */ uint8_t Check_Photo_Near(void) { ReFlash_RTC(); uint32_t target = Get_Photo_Target(); if (target == 0) { return 0; } uint32_t now = NEW_TIMESTAMP; return (now + PHO_NEAR_SEC >= target) ? 1 : 0; } /** * @brief 拍照定时参数合法性校验(开机读取后调用) * * 三个参数全为0 -> 恢复默认(间隔1天 21:00); * 单个参数越界 -> 恢复默认值并保存。 */ void Check_Photo_Param_Valid(void) { uint8_t save = 0; if (syscfg.pho_inr_day == 0 && syscfg.pho_t_hour == 0 && syscfg.pho_t_min == 0) { syscfg.pho_inr_day = PHO_INR_DAY_DEF; syscfg.pho_t_hour = PHO_T_HOUR_DEF; syscfg.pho_t_min = PHO_T_MIN_DEF; save = 1; LOG_W("photo param all-zero, SET DEF (%d天 %02d:%02d)", syscfg.pho_inr_day, syscfg.pho_t_hour, syscfg.pho_t_min); } else { if (syscfg.pho_inr_day < PHO_INR_DAY_MIN || syscfg.pho_inr_day > PHO_INR_DAY_MAX) { syscfg.pho_inr_day = PHO_INR_DAY_DEF; save = 1; LOG_W("pho_inr_day invalid, SET DEF"); } if (syscfg.pho_t_hour > 23) { syscfg.pho_t_hour = PHO_T_HOUR_DEF; save = 1; LOG_W("pho_t_hour invalid, SET DEF"); } if (syscfg.pho_t_min > 59) { syscfg.pho_t_min = PHO_T_MIN_DEF; save = 1; LOG_W("pho_t_min invalid, SET DEF"); } } if (save) { ef_set_env_blob("syscfg", &syscfg, sizeof(syscfg)); } } /** * @brief 定点拍照的拦截条件(与时控窗口无关, 只为硬安全) * * @return char * NULL-可拍照; 否则返回被拦截的中文原因 */ static char *Photo_Block_Reason(void) { if (device_status.ds != 1) { return "设备已关机"; } if (Demo_Mode_NEW == 1) { return "演示模式"; } if (ws_now == ws_KeyTig) { return "手动触发模式"; } if (device_status.bat_status == 1) { return "电池保护"; } // 测试进行中不得掺和: 否则基点不推进(见 recode_timestampe_NEW 的护栏)会导致反复触发 if (jts.takephoto_test == ON || jts.autotest == ON) { return "测试进行中"; } return RT_NULL; } /** * @brief 时控窗口外的定点拍照(间隔N天 + 指定时分) * * 拍照不再受工作时控窗口约束: 到点即自管上电 -> 执行一次工作包 -> 下电。 * 触发判定只用 syscfg.pho_inr_day / pho_t_hour / pho_t_min, 不看时控时间。 * 被硬安全条件拦截时不重置基点, 条件解除后仍会补拍。 * * @return uint8_t 1-本次执行了拍照 0-未到触发时刻或被拦截 */ static uint8_t Photo_Pkg_OnStandby(void) { if (Check_Photo_Trigger() == 0) { return 0; } char *block = Photo_Block_Reason(); if (block != RT_NULL) { LOG_W("STANDBY :photo blocked by %s\n", block); return 0; } LOG_I("STANDBY :photo trig , out of work window\n"); /* 拍照前把 RTU/相机/补光灯拉起(与工作窗口内同一套时序) */ if (rt_pin_read(CAM_CTL) != ON || rt_pin_read(RTU_DTU) != ON) { ctr_RTU_DTU(ON); CTL_CAM1(); ctr_led(ON); for (uint16_t a = 0; a < 45; a++) { rt_thread_mdelay(1000); rt_kprintf("wait for RTU start S1\n"); } } /* 基点先重置: 不论成败都按本次起算, 避免失败后每秒重入 */ recode_timestampe_NEW(&tm_photo); Recode_e_code(e_work_mode_pkg, 2, "时控外执行一次工作包"); work_mode_pkg(); // 执行一次工作包 ctr_RTU_DTU(OFF); CTL_CAM0(); ctr_led(OFF); return 1; } /** * @brief 简易的数据上报,用于在等待图片上报过程中检查是否需要数据上报 * */ void Updata_INR_Simple(void) { INR_DAT = syscfg.dattim * 60 - 10; // 减去30 消除上报所耗时间 if (math_timestampe_NEW(&tm_data) > INR_DAT) { LOG_I(" >>> watting mode > STEP A start report data C\n"); recode_timestampe_NEW(&tm_data); // 此时重置时间戳 wait_data_cnt = 0; UpLoad_Data(0); } } /** * @brief Construct a new Updata_INR object * * @param type > enable power off */ static uint16_t Updata_INR(uint8_t type) { static uint16_t rtuon_cnt = 0; // 非工作模式时 rtu上电时间记录 static char Once_poweroff = 1; /** * @brief ↓↓↓ 不在工作模式的时候 间隔上线上报数据 在此处实现 ↓↓↓ * */ INR_DAT = syscfg.dattim * 60 - 10; // 减去30 消除上报所耗时间 wait_data_cnt++; if (wait_data_cnt >= INR_DAT || (math_timestampe_NEW(&tm_data) > INR_DAT)) { if (wait_data_cnt >= INR_DAT) { LOG_I(" >>> watting mode > STEP A start report data A\n"); } else { LOG_I(" >>> watting mode > STEP A start report data B\n"); } recode_timestampe_NEW(&tm_data); // 此时重置时间戳 // recode_timestampe(&tm_photo); // 记录数据上报开始时间 wait_data_cnt = 0; ctr_RTU_DTU(ON); sprintf(device_status.net, "1"); // 去掉网络状态 while (++wait_data_cnt < (WAIT_NET_MAX / 2)) { SendReadInfo('D'); if (atoi(device_status.net) == 0) { LOG_I(" >>> watting mode > STEP B1 RTU net get up\n"); rt_thread_mdelay(2000); rt_thread_mdelay(10000); // 留有指令下发时间 UpLoad_Data(0); rt_thread_mdelay(2000); break; } rt_thread_mdelay(1000); } if (wait_data_cnt >= (WAIT_NET_MAX / 2)) { UpLoad_Data(0); LOG_W(" >>> watting mode > STEP B2 RTU net get failed\n"); Recode_e_code(e_rtu_net_fail, 1, "间隔上报时联网失败 > B2"); rt_thread_mdelay(5000); } wait_data_cnt = 0; if (Check_Photo_Near() && (Check_Allow_Work() == res_ok)) // 接近拍照时刻 不再断电 { LOG_I("CLOSE UP_PIC , JUMP RTU0"); } else { ctr_RTU_DTU(OFF); } Request_ntp(); } if (math_timestampe_NEW(&tm_data) == 0) { LOG_W("math_timestampe_NEW tm_data fail ,SET NOW"); } /** * @brief ↑↑↑ 不在工作模式的时候 间隔上线上报数据 在此处实现 ↑↑↑ * */ /** * @brief ↓↓↓ 首次上电后 留有三分钟数据上报 指令下发时间 ↓↓↓ * */ if (rturead() && Once_poweroff) { rtuon_cnt++; if (rtuon_cnt >= RTU_OFF_MAX) { LOG_I("RTU ON Time out of range , now turn it off A\n\n"); ctr_RTU_DTU(OFF); rtuon_cnt = 0; Once_poweroff = 0; } } /** * @brief ↑↑↑ 首次上电后 留有三分钟数据上报 指令下发时间 ↑↑↑ * */ return wait_data_cnt; } void KeyTigCheck(void) { #define manual_mode_MX 60 #define demo_mode_MX 120 uint16_t log_en_inr = LOG_INR * 5; // 打印log的速度 数值越小 打印越频繁 static int16_t log_en_cnt = -1; uint32_t cnt = 0; if (ws_now == ws_KeyTig) { Recode_e_code(e_manual_mode, 0, "进入手动触发模式"); while (cnt++ < 60 * manual_mode_MX) { rt_thread_mdelay(1000); if (ws_now != ws_KeyTig) { break; } if (chk_log_time) rt_kprintf("处于手动触发模式 %d/%d\n", cnt, manual_mode_MX * 60); if (cnt % 30 == 0) { SendReadInfo('E'); } } Recode_e_code(e_manual_mode, 1, "退出手动触发模式"); } if (Demo_Mode_NEW == 1) { Recode_e_code(e_demo_mode, 0, "进入演示模式"); cnt = 0; rt_kprintf("演示模式 暂停工作 %d / %d\n", cnt, demo_mode_MX * 60); while (cnt++ < demo_mode_MX * 60) { rt_thread_mdelay(1000); if (Demo_Mode_NEW == 0) { break; } if (cnt % 100 == 0) { rt_kprintf("演示模式 暂停工作 %d / %d\n", cnt, demo_mode_MX * 60); } ws_now = ws_demo; ctr_RTU_DTU(ON); CTL_CAM1(); } Demo_Mode_NEW = 0; Recode_e_code(e_demo_mode, 1, "退出演示模式"); } } /** * @brief 天牛工作流程 * */ void WorkTask_SY2(void) { uint16_t log_en_inr = LOG_INR * 5; // 打印log的速度 数值越小 打印越频繁 static int16_t log_en_cnt = -1; LOG_I(" WorkTask_SY2 "); if (Check_Allow_Work() != res_ok) { if (jts.switchled_test != ON) { ctr_led(OFF); } } rt_thread_mdelay(1000); /* 从 ENV + RTC备份寄存器 双源恢复拍照基点(重启/升级/掉电都不丢, 不推迟下次拍照) */ tm_photo = Photo_Base_Load(); if (tm_photo == 0) { LOG_W("photo base invalid in ENV & BKP\n"); // 老设备/首次, 下方以当前时间起算 } /* 数据上报基点: 仅存内存, 0则重置(频繁上报无需持久化) */ if (tm_data == 0 && GetDate.Year > 20) { recode_timestampe_NEW(&tm_data); } /* 拍照基点: 双源均无有效记录时, 以当前时间起算并持久化 */ if (tm_photo == 0 && GetDate.Year > 20) { recode_timestampe_NEW(&tm_photo); LOG_I("recode_timestampe INIT"); } while (Check_Allow_Work() != res_ok) // 新增数据上报逻辑(开启rtu 上报一次数据) { log_en_inr = LOG_INR * 5; rt_thread_mdelay(1000); KeyTigCheck(); ws_now = ws_wait0; // 待机 if (chk_log_time) { LOG_I("STEP0 :wait work start"); } Photo_Pkg_OnStandby(); // 定点拍照(不受时控窗口约束) Updata_INR(1); } rt_thread_mdelay(1000); uint16_t tig_cnt = 0; char last_once = 0; while (Check_Allow_Work() == res_ok) { LOG_W("STEP1 :start work > %d > %d ", workmode, takephoto_cnt); rt_thread_mdelay(1000); device_status.work_status = 1; (syscfg.photim >= 5) ? (INR_PIC = (syscfg.photim * 60)) : (INR_PIC = (TakeINR * 60)); switch (workmode) { case RunTime_wx: log_en_inr = LOG_INR * 5; while ((Check_Allow_Work() == res_ok) && (workmode == RunTime_wx)) { if (chk_log_time) { LOG_W("STEP2 :wait coll finsh %d%%", GetCollInfo()); } KeyTigCheck(); ws_now = ws_wait_coll; rt_thread_mdelay(1000); if (Check_Photo_Trigger()) // 拍照部分(间隔N天 + 指定时分) { LOG_I("Read to takephoto\n"); recode_timestampe_NEW(&tm_photo); if (rt_pin_read(CAM_CTL) != ON || rt_pin_read(RTU_DTU) != ON) { rt_kprintf("检测到拍照前未开启RTU"); ctr_RTU_DTU(ON); CTL_CAM1(); ctr_led(ON); for (uint16_t a = 0; a < 45; a++) { rt_thread_mdelay(1000); rt_kprintf("wait for RTU start A2\n"); } } Recode_e_code(e_work_mode_pkg , 0 ,"执行一次工作包 A"); work_mode_pkg(); // 执行一次工作包 last_once = 1; takephoto_cnt = 0; if (math_timestampe_NEW(&tm_data) > (INR_DAT - 30)) // 接近数据上报 不再断电 { LOG_I("CLOSE UPDATA , JUMP RTU0"); } else { ctr_RTU_DTU(OFF); } } // else if (math_timestampe_NEW(&tm_photo) >= ((INR_PIC)-WAIT_NET_MAX)) // RTU提前上电 // { // // rt_kprintf("takephoto_cnt close >> %d\n",math_timestampe_NEW(&tm_photo)); // ctr_RTU_DTU(ON); // CTL_CAM1(); // ctr_led(ON); // } USB工业相机 开启速度极快 不需要这么麻烦了 else // 数据部分 { Updata_INR(0); } if (GetDate.Year <= 20) // RTC 未同步, 拍照等待(RTC恢复后自动继续) { LOG_W("RTC not ready , photo wait\n"); } } // 时控模式退出后 本次未拍照且已到拍照时刻 则补拍一次(与主触发同一判定, 保持间隔语义) if (last_once == 0 && Check_Photo_Trigger()) { recode_timestampe_NEW(&tm_photo); if (rt_pin_read(CAM_CTL) != ON || rt_pin_read(RTU_DTU) != ON) { rt_kprintf("检测到拍照前未开启RTU"); ctr_RTU_DTU(ON); CTL_CAM1(); ctr_led(ON); for (uint16_t a = 0; a < 45; a++) { rt_thread_mdelay(1000); rt_kprintf("wait for RTU start A3\n"); } } Recode_e_code(e_work_mode_pkg , 1 ,"执行一次工作包 B"); work_mode_pkg(); // 执行一次工作包 takephoto_cnt = 0; ctr_RTU_DTU(OFF); CTL_CAM0(); ctr_led(OFF); } else { rt_kprintf("photo not trigger, last_once %d\n\n", last_once); } last_once = 0; // rt_kprintf("takephoto_cnt >> %d > %d > %d\n" ,inr , inr * 60 , ((inr * 60) - WAIT_NET_MAX)); break; default: break; } } if (jts.switchled_test != ON) { ctr_led(OFF); } device_status.work_status = 0; device_status.traplampsta = 0; } MSH_CMD_EXPORT(work_mode_pkg , work_mode_pkg); static void TigRay(void) { RayTiged = 1; } MSH_CMD_EXPORT(TigRay, TigRay) /********** 红外触发驱动 ************/ /* 触发标志位(Ray开头) */ static volatile rt_bool_t RayTrigFlagA = RT_FALSE; static volatile rt_bool_t RayTrigFlagB = RT_FALSE; static rt_timer_t RayDebounceTimer; // 防抖定时器 static rt_timer_t RaySequenceTimer; // 序列检测定时器(新增) /* 防抖定时器回调 */ static void RayDebounceHandler(void *param) { // rt_kprintf("[Ray] <%d:%d> Flags cleared by debounce\n", RayTrigFlagA, RayTrigFlagB); } /* 序列检测定时器回调(新增) */ static void RaySequenceHandler(void *param) { rt_kprintf("[Ray] Sequence timeout, flags cleared [A%d | B%d]\n", RayTrigFlagA, RayTrigFlagB); RayTrigFlagA = RT_FALSE; RayTrigFlagB = RT_FALSE; rt_timer_stop(RaySequenceTimer); } /****************** 优化后的中断回调 ******************/ /* RayTigA中断回调 */ static rt_err_t RayIrqHandlerA(void *args) { if (!RayTrigFlagA) { RayTrigFlagA = RT_TRUE; rt_kprintf("RayIrqHandlerA triggered\n"); // 如果B已经触发且在50ms内 if (RayTrigFlagB) { RayTigCnt++; RayTiged = RT_TRUE; RayOneLess = RT_TRUE; rt_kprintf("[Ray] Item detected! Count: %d\n", RTC_GET_TigVal()); // 清除标志和定时器 RayTrigFlagA = RT_FALSE; RayTrigFlagB = RT_FALSE; rt_timer_stop(RayDebounceTimer); rt_timer_stop(RaySequenceTimer); } else { // 启动防抖定时器 rt_timer_start(RayDebounceTimer); } } return RT_EOK; } /* RayTigB中断回调 */ static rt_err_t RayIrqHandlerB(void *args) { if (!RayTrigFlagB) { RayTrigFlagB = RT_TRUE; rt_kprintf("RayIrqHandlerB triggered\n"); // 启动序列检测定时器(50ms) rt_timer_control(RaySequenceTimer, RT_TIMER_CTRL_SET_TIME, (void *)50); rt_timer_start(RaySequenceTimer); RayTrigFlagA = RT_FALSE; // 启动防抖定时器 rt_timer_start(RayDebounceTimer); } return RT_EOK; } /****************** 初始化函数 ******************/ int RayDualTriggerInit(void) { /* GPIO模式配置 */ rt_pin_mode(RayTigA, PIN_MODE_INPUT_PULLDOWN); rt_pin_mode(RayTigB, PIN_MODE_INPUT_PULLDOWN); /* 创建防抖定时器 */ RayDebounceTimer = rt_timer_create("RayDebounce", RayDebounceHandler, RT_NULL, 10, // 10ms防抖 RT_TIMER_FLAG_ONE_SHOT); /* 创建序列检测定时器(新增) */ RaySequenceTimer = rt_timer_create("RaySequence", RaySequenceHandler, RT_NULL, 200, // 初始50ms超时 RT_TIMER_FLAG_ONE_SHOT); /* 绑定双中断 */ rt_pin_attach_irq(RayTigA, PIN_IRQ_MODE_RISING, RayIrqHandlerA, RT_NULL); rt_pin_attach_irq(RayTigB, PIN_IRQ_MODE_RISING, RayIrqHandlerB, RT_NULL); /* 使能中断 */ rt_pin_irq_enable(RayTigA, PIN_IRQ_ENABLE); rt_pin_irq_enable(RayTigB, PIN_IRQ_ENABLE); /* 读出保存的数据 */ RayTigCnt = RTC_GET_TigVal(); rt_kprintf("[Ray] Dual trigger initialized! Current count: %d\n", RayTigCnt); return RT_EOK; } /* 命令导出(命名统一) */ // MSH_CMD_EXPORT(RayDualTriggerInit, Ray dual trigger demo); /********** 红外触发驱动 ************/ void ChangeInrMode(void) { syscfg.en_tigmode = 0; ef_set_env_blob("syscfg", (const char *)&syscfg, sizeof(syscfg)); rt_kprintf("Change Workmod Inr OK\n"); } void ChangeTigMode(void) { syscfg.en_tigmode = 1; ef_set_env_blob("syscfg", (const char *)&syscfg, sizeof(syscfg)); rt_kprintf("Change Workmod Tig OK\n"); } MSH_CMD_EXPORT(ChangeInrMode, ChangeInrMode); MSH_CMD_EXPORT(ChangeTigMode, ChangeTigMode); /********** RTU Power Ctl ************/ static void rtu1(void) { ctr_RTU_DTU(ON); } static void rtu0(void) { ctr_RTU_DTU(OFF); } static char rturead(void) { char res = rt_pin_read(RTU_DTU); // res ? rt_kprintf("RTU power supply : ON\n") : rt_kprintf("RTU power supply : OFF\n"); return res; } static char readrtu(void) { char res = rt_pin_read(RTU_DTU); res ? rt_kprintf("RTU power supply : ON\n") : rt_kprintf("RTU power supply : OFF\n"); return res; } MSH_CMD_EXPORT(rtu1, rtu1) MSH_CMD_EXPORT(rtu0, rtu0) MSH_CMD_EXPORT(readrtu, readrtu) /********** RTU Power Ctl ************/ static char temp[64] = {0}; #include "cJSON.h" extern uint8_t get_mem_per(void); void status_Pri(uint8_t type) { rt_kprintf("CAM_CTL >> %d \n", rt_pin_read(CAM_CTL)); rt_kprintf("RTU_DTU >> %d \n", rt_pin_read(RTU_DTU)); // 创建根对象 cJSON *root = cJSON_CreateObject(); if (root == NULL) { // rt_kprintf("return A\n"); return; } // 添加 cmd 字段 cJSON_AddStringToObject(root, "cmd", "data"); // 创建 ext 对象 cJSON *ext = cJSON_CreateObject(); if (ext == NULL) { cJSON_Delete(root); // rt_kprintf("return B\n"); return; } sprintf(temp, "%02d-%02d-%02d %02d:%02d:%02d", GetDate.Year, GetDate.Month, GetDate.Date, GetTime.Hours, GetTime.Minutes, GetTime.Seconds); cJSON_AddStringToObject(ext, "当前时间", temp); if (workmode != TypeMAX_wx) { cJSON_AddStringToObject(ext, "工作模式", WorkModeList[workmode].keyword); } // rt_kprintf("mark A\n"); switch (workmode) { case RunTime_wx: sprintf(temp, "%d - %d", device_status.collecting_sh1, device_status.collecting_eh1); cJSON_AddStringToObject(ext, "时控时间", temp); break; default: break; } // rt_kprintf("mark B\n"); sprintf(temp, "未在工作 %d ", Check_Allow_Work()); (Check_Allow_Work() == res_ok) ? cJSON_AddStringToObject(ext, "当前状态", "工作中") : cJSON_AddStringToObject(ext, "当前状态", temp); sprintf(temp, "%d 分 (%d / %d)", syscfg.dattim, math_timestampe_NEW(&tm_data), INR_DAT); cJSON_AddStringToObject(ext, "数据间隔", temp); sprintf(temp, "%d天 %02d:%02d", syscfg.pho_inr_day, syscfg.pho_t_hour, syscfg.pho_t_min); cJSON_AddStringToObject(ext, "照片间隔", temp); sprintf(temp, "%d%%", get_mem_per()); cJSON_AddStringToObject(ext, "内存使用", temp); sprintf(temp, "%d-%d", device_status.rain_status, rain_exit); cJSON_AddStringToObject(ext, "雨控状态", temp); sprintf(temp, "%d-%d", RTC_GET_TigVal() , Get_REMAIN()); cJSON_AddStringToObject(ext, "卷带计数", temp); /* 拍照诊断: 基点 / 下次时刻 / 距触发剩余 / 上次结果(判断"为什么没拍/拍了失败") */ { char tbuf[32] = {0}; uint32_t tgt = Get_Photo_Target(); uint32_t now = NEW_TIMESTAMP; if (GetDate.Year <= 20) { cJSON_AddStringToObject(ext, "拍照基点", "时间未同步"); cJSON_AddStringToObject(ext, "下次拍照", "时间未同步"); cJSON_AddStringToObject(ext, "距拍照", "--"); } else if (tm_photo < TIMESTAMP_2000) { cJSON_AddStringToObject(ext, "拍照基点", "未定"); cJSON_AddStringToObject(ext, "下次拍照", "未定"); cJSON_AddStringToObject(ext, "距拍照", "--"); } else { Photo_Tm_Str(tm_photo, tbuf, sizeof(tbuf)); cJSON_AddStringToObject(ext, "拍照基点", tbuf); Photo_Tm_Str(tgt, tbuf, sizeof(tbuf)); cJSON_AddStringToObject(ext, "下次拍照", tbuf); if (tgt <= now) { sprintf(temp, "已到点 %u 秒 (待触发)", (unsigned int)(now - tgt)); } else { sprintf(temp, "%u 秒后", (unsigned int)(tgt - now)); } cJSON_AddStringToObject(ext, "距拍照", temp); } if (photo_stat.res == 0) { cJSON_AddStringToObject(ext, "上次拍照", "无记录"); } else { char *r = "未知"; switch (photo_stat.res) { case 1: r = "成功"; break; case 2: r = "失败(图像传输超时)"; break; case 3: r = "未执行(相机忙)"; break; case 4: r = "进行中"; break; default: break; } Photo_Tm_Str(photo_stat.res_tm ? photo_stat.res_tm : photo_stat.tig_tm, tbuf, sizeof(tbuf)); sprintf(temp, "%s %s", r, tbuf); cJSON_AddStringToObject(ext, "上次拍照", temp); } /* 到点却没拍时, 这里能看出被哪条硬安全条件拦住 */ char *blk = Photo_Block_Reason(); cJSON_AddStringToObject(ext, "拍照拦截", blk ? blk : "无"); } if (type == 2) { cJSON_AddStringToObject(ext, "测试", "翻板测试开始"); } if (type == 3) { cJSON_AddStringToObject(ext, "测试", "上仓门测试开始"); } // rt_kprintf("mark C\n"); // 将 ext 对象添加到根对象 cJSON_AddItemToObject(root, "ext", ext); // 生成 JSON 字符串 char *json_str = cJSON_Print(root); rt_kprintf("status_Pri > %s", json_str); if (json_str != NULL) free(json_str); if (type != 1) { char *json_strU = cJSON_PrintUnformatted(root); DTU_Write(json_strU); if (json_strU != NULL) free(json_strU); } // 清理 cJSON_Delete(root); } extern works_def ws_now; static void status_(void) { rt_kprintf("ws_now > %d \n", ws_now); status_Pri(1); } MSH_CMD_EXPORT(status_, status_); void Request_ntp(void) { rt_thread_mdelay(600); char readinfo[] = "{\"request\":\"ntp_time\"}"; DTU_Write("%s", readinfo); rt_kprintf("Request_ntp > %s\n", readinfo); rt_thread_mdelay(600); } void ctr_RTU_DTU(uint8_t status_rtu) { static uint32_t protect_ON = 0; static uint32_t protect_OFF = 0; static uint8_t protect_sec = 8; uint8_t now = 0; if (protect_ON == 0) { recode_timestampe_NEW(&protect_ON); } if (protect_OFF == 0) { recode_timestampe_NEW(&protect_OFF); } rt_pin_mode(RTU_DTU, PIN_MODE_OUTPUT); if (status_rtu == ON) { now = math_timestampe_NEW(&protect_OFF); while (now < protect_sec) { LOG_W("ctr_RTU_DTU ON wait for %d sec", protect_sec - now); rt_thread_mdelay(1000); now++; } rt_pin_write(RTU_DTU, ON); recode_timestampe_NEW(&protect_ON); } else if (Demo_Mode_NEW == 0 && ws_now != ws_KeyTig && (syscfg.not_poweroff == 0)) // 这两种情况下 不允许断电 { now = math_timestampe_NEW(&protect_ON); while (now < protect_sec) { LOG_W("ctr_RTU_DTU OFF wait for %d sec", protect_sec - now); rt_thread_mdelay(1000); now++; } LOG_W("RTU电源复位!!!"); rt_pin_write(RTU_DTU, OFF); CTL_CAM0(); syscfg.rtu_sta = 0; sprintf(device_status.net, "1"); // 断电的时候 网络状态置为断开 recode_timestampe_NEW(&protect_OFF); } else { LOG_W("RTU电源复位被打断 ws_now %d , Demo_mode %d\n", ws_now, Demo_Mode_NEW); } } static void tig_plus(void) { RayTigCnt++; } MSH_CMD_EXPORT(tig_plus, tig_plus);