Work_SY2.c 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2024-08-16 RuoFeng the first version
  9. */
  10. /*********************↓ define ↓************************/
  11. #define DBG_ENABLE
  12. #define DBG_COLOR
  13. #define DBG_TAG "sy2"
  14. #define DBG_LEVEL DBG_INFO
  15. #include <rtdbg.h>
  16. #define LOG_INR 10 // 默认的log打印间隔(作用域在本文件)
  17. #define RTU_OFF_MAX 180
  18. #define WAIT_NET_MAX 150
  19. #define ENABLE_WORK (Check_Allow_Work() == res_ok)
  20. #define NEW_TIMESTAMP tm_to_timestamp()
  21. /*********************↑ define ↑************************/
  22. /**
  23. *
  24. *
  25. *
  26. *
  27. * */
  28. /*********************↓ include ↓************************/
  29. #include <Work_SY2.h>
  30. #include "rs485.h"
  31. #include "control.h"
  32. #include "test.h"
  33. #include <easyflash.h>
  34. #include "dtu_uart2.h"
  35. /*********************↑ include ↑************************/
  36. /**
  37. *
  38. *
  39. *
  40. *
  41. * */
  42. /*********************↓ extern ↓************************/
  43. extern Temporal_list Check_Work_time_period(uint8_t coll_ch);
  44. extern works_def ws_now;
  45. extern Device_Status_typedef device_status;
  46. extern void HMI_WRITE(char *fmt, ...);
  47. extern JD_Test_Sta jts;
  48. extern Res_def Mtor_Control_ZP(Motor_Ctl_type mct, int8_t ctime);
  49. extern Sensor sensor[MAX_SENSOR_NUM];
  50. extern Temporal_list Check_Work_time_period_0oclock(void);
  51. extern RTC_DateTypeDef GetDate;
  52. extern RTC_TimeTypeDef GetTime;
  53. extern uint8_t ReFlash_RTC(void);
  54. extern void CTL_CAM1(void);
  55. extern void CTL_CAM0(void);
  56. extern uint32_t tm_to_timestamp(void);
  57. extern char Demo_Mode_NEW;
  58. /*********************↑ extern ↑************************/
  59. /**
  60. *
  61. *
  62. *
  63. *
  64. * */
  65. /********************↓ declaration ↓***********************/
  66. static void rtu1(void);
  67. static void rtu0(void);
  68. static char rturead(void);
  69. void Request_ntp(void);
  70. static void Photo_Base_Save(uint32_t v); // 拍照基点持久化(ENV + RTC备份寄存器)
  71. static uint32_t Photo_Base_Load(void); // 拍照基点恢复
  72. /********************↑ declaration ↑***********************/
  73. /**
  74. *
  75. *
  76. *
  77. *
  78. * */
  79. /*********************↓ global variables ↓************************/
  80. static uint16_t takephoto_cnt = 0; // 拍照触发计数
  81. static uint16_t wait_data_cnt = 0; // 上报数据计数
  82. static uint32_t tm_data = 0; // 计算数据时间
  83. static uint32_t tm_photo = 0; // 计算图片时间
  84. volatile SYSCFG syscfg = {0}; // 系统配置
  85. volatile Limit limit = {0}; // 各种控的开关和参数
  86. volatile wm_Def workmode = TypeMAX_wx; // 工作模式
  87. WorkMode_Def WorkModeList[TypeMAX_wx] = // 工作模式类型
  88. {
  89. {RunTime_wx, "时控间隔模式"}};
  90. volatile uint16_t RayTigCnt = 0; // 红外触发计数
  91. volatile static char RayTiged = 0; // 红外已触发过
  92. volatile static char RayOneLess = 0; // 红外触发记录
  93. volatile char CamFree = 1; // 相机空闲状态
  94. volatile uint16_t INR_PIC = 0; // 图片上传间隔
  95. volatile uint16_t INR_DAT = 0; // 图片上传间隔
  96. volatile char stop_getinfo = 0; // dtuwrite时暂停 getinfo
  97. static uint8_t rain_exit = 0; // 雨控退出进度
  98. volatile char errcnt_getinfo = 0; // err计数
  99. // volatile char RTUGetUp = 0; // RTU上电开机
  100. /*********************↑ global variables ↑************************/
  101. /**
  102. *
  103. *
  104. *
  105. *
  106. * */
  107. /**
  108. * @brief
  109. *
  110. * @return int8_t -1(超时) >1(进度百分比)
  111. */
  112. // int8_t Get_luxworkTime(void)
  113. //{
  114. // // ReFlash_RTC();
  115. // //
  116. // // uint32_t start = 0, now = 0;
  117. // //
  118. // // start = lwork.start_time.Hours * 60 + lwork.start_time.Minutes;
  119. // // now = GetTime.Hours * 60 + GetTime.Minutes;
  120. // //
  121. // // if (abs(GetTime.Hours - lwork.start_time.Hours) >= 10 && GetTime.Hours < lwork.start_time.Hours)
  122. // // {
  123. // // now += 1440;
  124. // // }
  125. // // if ((now - start) > (limit.LuxTime * 60))
  126. // // {
  127. // // return -2;
  128. // // }
  129. // // else
  130. // // {
  131. // // LOG_I("luxtime %d%% \n", ((now - start) * 100) / (limit.LuxTime * 60));
  132. // // return ((now - start) * 100) / (limit.LuxTime * 60);
  133. // // }
  134. //
  135. // return -1;
  136. // }
  137. /**
  138. * @brief Get the Coll Info object
  139. *
  140. * @return uint8_t persen
  141. */
  142. uint8_t GetCollInfo(void)
  143. {
  144. // uint16_t inr = 0;
  145. // (syscfg.photim >= 5) ? (inr = syscfg.photim - 1) : (inr = TakeINR);
  146. /* 直接按原始时间戳计算秒差, 不再调用 math_timestampe_NEW(避免其超窗时重置 tm_photo) */
  147. ReFlash_RTC();
  148. uint32_t now = NEW_TIMESTAMP;
  149. uint32_t last = tm_photo;
  150. uint32_t diff = (now > last) ? (now - last) : 0;
  151. if (INR_PIC == 0)
  152. {
  153. return 0;
  154. }
  155. return (diff * 100) / (INR_PIC);
  156. }
  157. /**
  158. * @brief 获取晨曦模式工作进度 不在工作范围 返回负数
  159. *
  160. * @return int8_t val(-1 未开始工作 -2 参数校验失败)
  161. */
  162. int8_t Get_swork_percent(void)
  163. {
  164. uint32_t start = 0, end = 0, temp1 = 0, now = 0;
  165. now = GetTime.Hours * 3600 + GetTime.Minutes * 60 + GetTime.Seconds;
  166. start = device_status.collecting_sh1 * 3600;
  167. end = device_status.collecting_eh1 * 3600;
  168. if (now < start && now > end)
  169. {
  170. return -1;
  171. }
  172. if (device_status.collecting_sh1 > device_status.collecting_eh1)
  173. {
  174. end += (24 * 3600);
  175. }
  176. temp1 = end - start;
  177. // rt_kprintf("now %d ALL %d\n" , (now - start) , temp1);
  178. if (((now - start) > temp1) || ((now - start) * 100 / temp1) > 100)
  179. {
  180. return -3;
  181. }
  182. return (now - start) * 100 / temp1;
  183. }
  184. /**
  185. * @brief 确认当前工作模式
  186. *
  187. */
  188. void Get_work_mode(void)
  189. {
  190. /**
  191. * @brief 开始确认
  192. *
  193. */
  194. static uint8_t ENPrint = 1;
  195. wm_Def bak = workmode;
  196. if (syscfg.en_tigmode == 0)
  197. {
  198. workmode = RunTime_wx;
  199. if (ENPrint || bak != workmode)
  200. {
  201. rt_kprintf("\n 时控间隔模式 %d-%d 时 > %d\n\n", device_status.collecting_sh1, device_status.collecting_eh1, ws_now);
  202. bak = workmode;
  203. ENPrint = 0;
  204. }
  205. }
  206. }
  207. #define Temp_Tig_MX 20
  208. /**
  209. * @brief 检查是否允许工作
  210. *
  211. * @return Caw_def 校验值
  212. */
  213. Caw_def Check_Allow_Work(void)
  214. {
  215. uint16_t log_en_inr = LOG_INR * 3; // 打印log的速度 数值越小 打印越频繁
  216. static int16_t log_en_cnt = -1;
  217. rt_thread_mdelay(15);
  218. Get_work_mode();
  219. /**
  220. * @brief 雨控
  221. *
  222. */
  223. // if (device_status.rain_update == 1) // 还需要再改
  224. // {
  225. // LOG_W("CAW:雨控\n");
  226. // device_status.rain_update = 0;
  227. // if (device_status.rain_status == 0)
  228. // {
  229. // device_status.rain_status = 1;
  230. // Mtor_Control_CM(mct_Foreward); // 关仓门
  231. // {
  232. // Recode_e_code(e_rain_tig, 0, "进入雨控 关仓门");
  233. // char *rain_tig = makeJson_Warn("Rain Tig In", "STA");
  234. // DTU_Write("%s", rain_tig);
  235. // rt_free(rain_tig);
  236. // }
  237. // }
  238. // rain_exit = 90;
  239. // rt_thread_mdelay(1000);
  240. // return res_Rain;
  241. // }
  242. // else if (rain_exit > 0)
  243. // {
  244. // rain_exit--;
  245. // if (rain_exit == 0)
  246. // {
  247. // LOG_W(" exit rain , open the door\n");
  248. // //Mtor_Control_CM(mct_Reversal); // 开仓门
  249. // {
  250. // Recode_e_code(e_rain_tig, 1, "退出雨控 开仓门");
  251. // device_status.rain_status = 0;
  252. // char *rain_tig = makeJson_Warn("Rain Tig Out", "STA");
  253. // DTU_Write("%s", rain_tig);
  254. // rt_free(rain_tig);
  255. // }
  256. // }
  257. // else
  258. // {
  259. // LOG_W("ready to exit rain > %d\n", rain_exit);
  260. // }
  261. // rt_thread_mdelay(500);
  262. // return res_Rain;
  263. // }
  264. if (device_status.ds != 1) // 250327 新增 关机状态
  265. {
  266. if (chk_log_time)
  267. {
  268. LOG_I("CAW:ds == 0\n");
  269. }
  270. return res_PwrOff;
  271. }
  272. switch (workmode)
  273. {
  274. /**
  275. * @brief 时控模式
  276. *
  277. */
  278. case RunTime_wx:
  279. if ((device_status.collecting_sh1 == device_status.collecting_eh1) ||
  280. (device_status.collecting_sh1 == 0 && device_status.collecting_eh1 == 24)) // 全天候牛马
  281. {
  282. if (chk_log_time)
  283. {
  284. LOG_I("CAW:全天候牛马\n");
  285. }
  286. }
  287. else if (device_status.collecting_eh1 < device_status.collecting_sh1) // 跨0点牛马
  288. {
  289. if (Check_Work_time_period_0oclock() != tl_Center)
  290. {
  291. if (chk_log_time)
  292. {
  293. LOG_I("CAW:未在工作时间 0点\n");
  294. }
  295. return res_Time;
  296. }
  297. }
  298. else // 正常牛马
  299. {
  300. if (Check_Work_time_period(1) != tl_Center)
  301. {
  302. if (chk_log_time)
  303. {
  304. LOG_I("CAW:未在工作时间\n");
  305. }
  306. return res_Time;
  307. }
  308. }
  309. break;
  310. default:
  311. break;
  312. }
  313. static uint8_t temp_up_cnt = 0;
  314. static uint8_t temp_dn_cnt = 0;
  315. /**
  316. * @brief 温湿度
  317. *
  318. */
  319. // rt_kprintf("sensor[s_Temp].value %d Luxlimit %d\n" , sensor[s_Temp].value , limit.temp_up);
  320. int32_t temp = 0;
  321. if (sensor[s_Temp].online >= 1)
  322. {
  323. temp = sensor[s_Temp].value;
  324. }
  325. else
  326. {
  327. temp = atoi(device_status.tempture);
  328. }
  329. if (sensor[s_Temp].online >= 1 && temp > (limit.temp_up * 10) && limit.temhumEN == ON)
  330. {
  331. if (temp_up_cnt >= Temp_Tig_MX)
  332. {
  333. LOG_I("CAW:触发温控上限 %d\n", temp_up_cnt);
  334. return res_Temp;
  335. }
  336. temp_up_cnt++;
  337. rt_thread_mdelay(1000);
  338. if (temp_up_cnt >= Temp_Tig_MX)
  339. {
  340. if (device_status.temp_status == 0)
  341. {
  342. LOG_I("CAW:触发温控上限 %d (%d > %d)\n", temp_up_cnt, temp, limit.temp_up * 10);
  343. }
  344. device_status.temp_status = 1;
  345. return res_Temp;
  346. }
  347. }
  348. else if (temp_up_cnt > 0)
  349. {
  350. temp_up_cnt--;
  351. rt_thread_mdelay(1000);
  352. if (temp_up_cnt == 0)
  353. {
  354. if (device_status.temp_status > 0)
  355. LOG_I("CAW:解除温控上限\n");
  356. device_status.temp_status = 0;
  357. }
  358. }
  359. if (sensor[s_Temp].online >= 1 && temp < (limit.temp_dw * 10) && limit.temhumEN == ON)
  360. {
  361. if (temp_dn_cnt >= Temp_Tig_MX)
  362. {
  363. return res_Temp;
  364. }
  365. temp_dn_cnt++;
  366. rt_thread_mdelay(1000);
  367. if (temp_dn_cnt >= Temp_Tig_MX)
  368. {
  369. if (device_status.temp_status == 0)
  370. {
  371. LOG_I("CAW:触发温控下限 %d (%d < %d)\n", temp_dn_cnt, temp, limit.temp_dw * 10);
  372. }
  373. device_status.temp_status = 2;
  374. return res_Temp;
  375. }
  376. }
  377. else if (temp_dn_cnt > 0)
  378. {
  379. temp_dn_cnt--;
  380. rt_thread_mdelay(1000);
  381. if (temp_dn_cnt == 0)
  382. {
  383. if (device_status.temp_status > 0)
  384. LOG_I("CAW:解除温控下限\n");
  385. device_status.temp_status = 0;
  386. }
  387. }
  388. /**
  389. * @brief 温湿度
  390. *
  391. */
  392. Check_TestFree();
  393. return res_ok;
  394. }
  395. extern uint8_t stopreflashTakePhoto;
  396. extern uint8_t holdon_stareflash;
  397. /**
  398. * @brief 刷新工作状态
  399. *
  400. */
  401. void ReflashWorkSta(void)
  402. {
  403. if (ws_now >= ws_typemax)
  404. {
  405. rt_kprintf("ReflashWorkSta illegl wstype\n");
  406. }
  407. if (holdon_stareflash)
  408. {
  409. return;
  410. }
  411. work_day_def work_day = {0};
  412. switch (ws_now)
  413. {
  414. case ws_standby:
  415. HMI_WRITE("workSta.workinfo.txt=\"待机中...\"");
  416. break;
  417. // res_ok = 0, // 没问题
  418. // res_Time, // 时控
  419. // res_Light, // 光控
  420. // res_TempH, // 温湿度
  421. // res_Rain // 雨控
  422. case ws_wait0:
  423. switch (Check_Allow_Work())
  424. {
  425. case res_ok:
  426. HMI_WRITE("workSta.workinfo.txt=\"达到工作条件\"");
  427. break;
  428. case res_Time:
  429. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 时控\"");
  430. break;
  431. case res_Light:
  432. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 光控\"");
  433. break;
  434. case res_Temp:
  435. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 温控\"");
  436. break;
  437. case res_Humi:
  438. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 湿控\"");
  439. break;
  440. case res_Temp_D:
  441. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 温控(基于定位)\"");
  442. break;
  443. case res_Humi_D:
  444. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 湿控 (基于定位)\"");
  445. break;
  446. case res_Rain:
  447. HMI_WRITE("workSta.workinfo.txt=\"未达工作条件 - 雨控\"");
  448. break;
  449. default:
  450. HMI_WRITE("workSta.workinfo.txt=\"未达到工作条件 %d\"", Check_Allow_Work());
  451. break;
  452. }
  453. break;
  454. case ws_start_coll:
  455. HMI_WRITE("workSta.workinfo.txt=\"STEP1:收集开始\"");
  456. break;
  457. case ws_wait_coll:
  458. HMI_WRITE("workSta.workinfo.txt=\"STEP2:等待收集完成 %d%%\"", GetCollInfo());
  459. break;
  460. case ws_takephoto:
  461. HMI_WRITE("workSta.workinfo.txt=\"STEP3:开始拍照\"");
  462. break;
  463. case ws_jumptake:
  464. HMI_WRITE("workSta.workinfo.txt=\"STEP4:采集中途退出 %d\"", Check_Allow_Work());
  465. break;
  466. case ws_flap:
  467. HMI_WRITE("workSta.workinfo.txt=\"STEP5:翻板中\"");
  468. break;
  469. case ws_once_finsh:
  470. HMI_WRITE("workSta.workinfo.txt=\"STEP6:单次工作循环结束\"");
  471. break;
  472. case ws_finsh:
  473. ef_get_env_blob("work_day", &work_day, sizeof(work_day), NULL);
  474. HMI_WRITE("workSta.workinfo.txt=\"今日工作周期结束 %02d.%02d.%02d\"", work_day.year % 100, work_day.month, work_day.day);
  475. break;
  476. default:
  477. break;
  478. }
  479. }
  480. static void TigTakePho(void)
  481. {
  482. device_status.work_status = 3;
  483. LOG_W("STEP3 :takephoto");
  484. ws_now = ws_takephoto;
  485. ctr_led(ON);
  486. DTU_Write("takephoto.mp3");
  487. // 延时
  488. rt_thread_delay(1000);
  489. Hk_Take_io(1);
  490. Request_ntp();
  491. if (jts.switchled_test != ON && RunTime_wx == workmode)
  492. {
  493. ctr_led(OFF);
  494. }
  495. else
  496. {
  497. LOG_W("STEP4 :jump takephoto");
  498. ws_now = ws_jumptake;
  499. rt_thread_delay(3000);
  500. if (jts.switchled_test != ON && RunTime_wx == workmode)
  501. {
  502. ctr_led(OFF);
  503. }
  504. }
  505. takephoto_cnt = 0;
  506. }
  507. /**
  508. * @brief 执行一次主要功能
  509. *
  510. */
  511. void work_mode_pkg(void)
  512. {
  513. #define cleek_A 3
  514. #define cleek_B 9
  515. ctr_led(ON);
  516. for (char a = 0; a < 24; a++)
  517. {
  518. rt_thread_mdelay(1000);
  519. rt_kprintf("work_mode_pkg waiting %d \n" , a);
  520. }
  521. Mtor_Control_CM(mct_Foreward, 1500); // 复位
  522. LOG_W("STEP3 :Mtor_Control_ZP A1");
  523. Mtor_Control_ZP(mct_Foreward, 0);
  524. rt_thread_mdelay(1500);
  525. LOG_W("STEP3 :Mtor_Control_ZP A2");
  526. Mtor_Control_ZP(mct_Foreward, 0);
  527. Mtor_Control_CM(mct_Reversal, 1500); // 顶住
  528. LOG_W("STEP4 :takephoto A");
  529. rt_thread_mdelay(3500);
  530. TigTakePho();
  531. Mtor_Control_CM(mct_Foreward, 1500); // 复位
  532. LOG_W("STEP5 : Mtor_Control_ZP B1");
  533. Mtor_Control_ZP(mct_Foreward, 0);
  534. rt_thread_mdelay(1000);
  535. LOG_W("STEP5 : Mtor_Control_ZP B2");
  536. Mtor_Control_ZP(mct_Foreward, 0);
  537. rt_thread_mdelay(1000);
  538. LOG_W("STEP5 : Mtor_Control_ZP B3");
  539. Mtor_Control_ZP(mct_Foreward, 0);
  540. rt_thread_mdelay(1000);
  541. LOG_W("STEP5 : Mtor_Control_ZP B4");
  542. Mtor_Control_ZP(mct_Foreward, 0);
  543. rt_thread_mdelay(1000);
  544. LOG_W("STEP6 :takephoto B");
  545. TigTakePho();
  546. Mtor_Control_CM(mct_Foreward, 1500); // 复位
  547. // rt_thread_mdelay(1000);
  548. LOG_W("STEP5 : Mtor_Control_ZP B5");
  549. Mtor_Control_ZP(mct_Foreward, 0);
  550. Mtor_Control_CM(mct_Reversal, 1500); // 顶住
  551. LOG_W("STEP7 :one cycle finsh");
  552. ws_now = ws_once_finsh;
  553. rt_thread_mdelay(1000);
  554. }
  555. /**
  556. * @brief 记录当前时刻
  557. *
  558. * @param time 时间戳
  559. */
  560. void recode_timestampe_NEW(uint32_t *tim)
  561. {
  562. ReFlash_RTC();
  563. // 拍照基点只能由"定时触发"推进(工作窗口内到点 / 待机定点 / 退出窗口补拍);
  564. // 手动与测试拍照(按键手动模式、立即拍照测试、一键测试)一律不得改动基点,
  565. // 否则会把下次定时拍照的时间基准带偏。
  566. if (tim == &tm_photo &&
  567. (ws_now == ws_KeyTig || jts.takephoto_test == ON || jts.autotest == ON))
  568. {
  569. LOG_W("photo base NOT advance (manual/test mode)\n");
  570. rt_thread_mdelay(1000);
  571. return;
  572. }
  573. *tim = NEW_TIMESTAMP;
  574. if (tim == &tm_photo)
  575. {
  576. rt_kprintf("recode_timestampe (photo) > %d sec\n", *tim);
  577. Photo_Base_Save(*tim); // 持久化拍照时间基点: ENV + RTC备份寄存器(重启/升级都不丢)
  578. }
  579. else if (tim == &tm_data)
  580. {
  581. rt_kprintf("recode_timestampe (data ) > %d sec\n", *tim);
  582. }
  583. rt_thread_mdelay(1000);
  584. }
  585. /**
  586. * @brief 计算差值
  587. *
  588. * @param time 时间戳
  589. * @return uint32_t 差值
  590. */
  591. uint32_t math_timestampe_NEW(uint32_t *tim)
  592. {
  593. // uint16_t log_en_inr = LOG_INR * 3; // 打印log的速度 数值越小 打印越频繁
  594. // static int16_t log_en_cnt = -1;
  595. static struct tm tm_bak;
  596. static char err_cnt = 0;
  597. char time_bak[30] = {0};
  598. if (*tim == NEW_TIMESTAMP)
  599. {
  600. rt_thread_mdelay(1000);
  601. }
  602. ReFlash_RTC();
  603. uint32_t now = NEW_TIMESTAMP;
  604. uint32_t val = (now - *tim);
  605. if (val > 0 && val < ((syscfg.dattim + syscfg.photim) * 60))
  606. {
  607. // if (chk_log_time)
  608. // {
  609. // if (tim == &tm_photo)
  610. // {
  611. // rt_kprintf("math_timestampe (photo) > %d sec\n", val);
  612. // }
  613. // else
  614. // {
  615. // rt_kprintf("math_timestampe (data ) > %d sec\n", val);
  616. // }
  617. // }
  618. tm_bak.tm_year = GetDate.Year;
  619. tm_bak.tm_mon = GetDate.Month;
  620. tm_bak.tm_yday = GetDate.Date;
  621. tm_bak.tm_hour = GetTime.Hours;
  622. tm_bak.tm_min = GetTime.Minutes;
  623. tm_bak.tm_sec = GetTime.Seconds;
  624. err_cnt = 0;
  625. return val;
  626. }
  627. else
  628. {
  629. if (GetDate.Year > 200)
  630. {
  631. sprintf(time_bak, "%02d-%02d-%02d %02d:%02d:%02d",
  632. tm_bak.tm_year,
  633. tm_bak.tm_mon,
  634. tm_bak.tm_yday,
  635. tm_bak.tm_hour,
  636. tm_bak.tm_min,
  637. tm_bak.tm_sec);
  638. if (tm_bak.tm_year > 0 && tm_bak.tm_year < 200)
  639. {
  640. LOG_W("Write bak time %s!", time_bak);
  641. EXE_NTP_SETTIME(&tm_bak);
  642. }
  643. err_cnt++;
  644. if (err_cnt == 5)
  645. {
  646. Recode_e_code(e_rtc_fail, 0x9D, time_bak);
  647. }
  648. if (err_cnt > 30)
  649. {
  650. rt_hw_cpu_reset();
  651. }
  652. }
  653. if (tim == &tm_photo)
  654. {
  655. rt_kprintf("math_timestampe fail (photo) > %d (now : %d >>> tim : %d) \n", val, now, *tim);
  656. recode_timestampe_NEW(tim);
  657. }
  658. else
  659. {
  660. rt_kprintf("math_timestampe fail (data ) > %d (now : %d >>> tim : %d) \n", val, now, *tim);
  661. recode_timestampe_NEW(tim);
  662. }
  663. }
  664. return 0;
  665. }
  666. /* ==================== 拍照定时触发(间隔N天 + 指定时分) ==================== */
  667. #define TIMESTAMP_2000 946684800UL // 2000-01-01 00:00:00 (Unix秒), 时间戳有效性判定下限
  668. #define TIMESTAMP_MAX_VALID 4102444800UL // 2100-01-01 00:00:00, 有效性上限(防备份区掉电读出0xFFFFFFFF)
  669. #define PHO_NEAR_SEC 300 // 接近拍照时刻提前量(秒), 用于保持RTU上电
  670. /* ==================== 拍照基点持久化 ==================== */
  671. #define PHO_BASE_MAGIC 0x5048U // BKP有效性魔数('P''H'), 备份区被清/掉电后内容不可信, 靠它判定
  672. /**
  673. * @brief 写入拍照基点(ENV 与 RTC备份寄存器双写)
  674. *
  675. * ENV : 内部Flash分区 -> 整机断电(含VBAT)也不丢; 但固件升级可能擦掉
  676. * BKP : RTC备份区 -> 升级不受影响; 但靠VBAT供电, 电池耗尽/更换会丢
  677. * 两处同时写同一值, 任一处失效都能从另一处恢复。F1备份寄存器为16位, 故拆高低两个。
  678. * 魔数最后写: 备份区被清/掉电后内容不可信, 只有魔数匹配才认那两个寄存器的值。
  679. */
  680. static void Photo_Base_Save(uint32_t v)
  681. {
  682. if (ef_set_env_blob("tm_photo", &v, sizeof(v)) != EF_NO_ERR)
  683. {
  684. LOG_E("tm_photo ENV save FAIL\n");
  685. }
  686. BSP_Write_RTC_Reg(PhotoBase_DR10, Reg_Low_16, 0); // 先失效, 防止读到半更新的值
  687. BSP_Write_RTC_Reg(PhotoBase_DR8, Reg_Low_16, v & 0xFFFFUL);
  688. BSP_Write_RTC_Reg(PhotoBase_DR9, Reg_Low_16, (v >> 16) & 0xFFFFUL);
  689. BSP_Write_RTC_Reg(PhotoBase_DR10, Reg_Low_16, PHO_BASE_MAGIC); // 再提交
  690. }
  691. /**
  692. * @brief 读取拍照基点(ENV + RTC备份寄存器双源)
  693. *
  694. * 取两者中"合法且较大"的那个: 基点是单调递增的时间戳, 取大者即最近一次成功写入。
  695. * 这样 ENV 被升级擦掉、或 BKP 因VBAT掉电变成垃圾值, 都能自动绕开。
  696. *
  697. * @return uint32_t 基点(Unix秒); 两处均无效时返回 0
  698. */
  699. static uint32_t Photo_Base_Load(void)
  700. {
  701. uint32_t env_v = 0;
  702. uint32_t bkp_v = 0;
  703. if (ef_get_env_blob("tm_photo", &env_v, sizeof(env_v), NULL) != sizeof(env_v))
  704. {
  705. env_v = 0;
  706. }
  707. if (BSP_Read_RTC_Reg(PhotoBase_DR10, Reg_Low_16) == PHO_BASE_MAGIC) // 魔数不符视为无记录
  708. {
  709. bkp_v = ((BSP_Read_RTC_Reg(PhotoBase_DR9, Reg_Low_16) & 0xFFFFUL) << 16)
  710. | (BSP_Read_RTC_Reg(PhotoBase_DR8, Reg_Low_16) & 0xFFFFUL);
  711. }
  712. if (env_v < TIMESTAMP_2000 || env_v > TIMESTAMP_MAX_VALID)
  713. {
  714. env_v = 0;
  715. }
  716. if (bkp_v < TIMESTAMP_2000 || bkp_v > TIMESTAMP_MAX_VALID)
  717. {
  718. bkp_v = 0;
  719. }
  720. return (env_v > bkp_v) ? env_v : bkp_v;
  721. }
  722. /**
  723. * @brief 计算本次拍照的目标触发时刻(Unix秒, 本地时间)
  724. *
  725. * 判定基准: 取上次拍照结束【当天 00:00:00】+ 间隔天数 + 目标时分。
  726. * - 基于Unix秒连续运算, 跨月/跨年/闰年天然正确;
  727. * - 基准取"当天0点"而非"拍照完成时刻", 拍照耗时不影响下次触发时刻, 周期稳定。
  728. *
  729. * @return uint32_t 目标时刻; RTC未同步或上次拍照时间戳无效时返回 0
  730. */
  731. static uint32_t Get_Photo_Target(void)
  732. {
  733. if (GetDate.Year <= 20) // RTC 未同步(年<2021), 不计算
  734. {
  735. return 0;
  736. }
  737. uint32_t last = tm_photo; // 上次拍照结束时间戳
  738. if (last < TIMESTAMP_2000) // 上次拍照时间戳无效(未初始化/异常)
  739. {
  740. return 0;
  741. }
  742. /* 参数兜底(正常流程由开机校验保证合法, 此处仅防御) */
  743. 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;
  744. uint32_t hour = (syscfg.pho_t_hour <= 23) ? syscfg.pho_t_hour : PHO_T_HOUR_DEF;
  745. uint32_t min = (syscfg.pho_t_min <= 59) ? syscfg.pho_t_min : PHO_T_MIN_DEF;
  746. uint32_t last_day0 = last - (last % 86400UL); // 上次拍照当天 00:00:00(本地无夏令时)
  747. return last_day0 + day * 86400UL
  748. + hour * 3600UL + min * 60UL;
  749. }
  750. /**
  751. * @brief 时间戳(Unix秒, 本地) 格式化为 "MM-DD HH:MM:SS"
  752. *
  753. * 与 tm_to_timestamp() / Get_Photo_Target() 同一时基(本地, 无夏令时)。
  754. */
  755. static void Photo_Tm_Str(uint32_t tm, char *buf, uint16_t len)
  756. {
  757. if (tm < TIMESTAMP_2000)
  758. {
  759. rt_snprintf(buf, len, "--");
  760. return;
  761. }
  762. uint32_t days = tm / 86400UL;
  763. uint32_t sec = tm % 86400UL;
  764. uint16_t year = 1970;
  765. while (1)
  766. {
  767. uint16_t dy = ((year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 366 : 365);
  768. if (days < dy)
  769. {
  770. break;
  771. }
  772. days -= dy;
  773. year++;
  774. }
  775. const uint8_t mdays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  776. uint8_t mon = 0;
  777. while (1)
  778. {
  779. uint8_t dy = mdays[mon];
  780. if (mon == 1 && (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)))
  781. {
  782. dy = 29;
  783. }
  784. if (days < dy)
  785. {
  786. break;
  787. }
  788. days -= dy;
  789. mon++;
  790. }
  791. rt_snprintf(buf, len, "%02d-%02d %02d:%02d:%02d",
  792. mon + 1, (uint8_t)(days + 1),
  793. (uint8_t)(sec / 3600UL), (uint8_t)((sec % 3600UL) / 60UL), (uint8_t)(sec % 60UL));
  794. }
  795. /**
  796. * @brief 是否到达拍照触发时刻
  797. *
  798. * @return uint8_t 1-应触发拍照 0-未到时刻
  799. */
  800. uint8_t Check_Photo_Trigger(void)
  801. {
  802. ReFlash_RTC();
  803. uint32_t target = Get_Photo_Target();
  804. if (target == 0)
  805. {
  806. return 0;
  807. }
  808. uint32_t now = NEW_TIMESTAMP;
  809. return (now >= target) ? 1 : 0;
  810. }
  811. /**
  812. * @brief 是否接近拍照触发时刻(提前 PHO_NEAR_SEC 秒)
  813. *
  814. * 用于数据上报后"接近拍照时刻不再断电", 避免拍照前重新给RTU上电。
  815. *
  816. * @return uint8_t 1-接近拍照时刻 0-远离
  817. */
  818. uint8_t Check_Photo_Near(void)
  819. {
  820. ReFlash_RTC();
  821. uint32_t target = Get_Photo_Target();
  822. if (target == 0)
  823. {
  824. return 0;
  825. }
  826. uint32_t now = NEW_TIMESTAMP;
  827. return (now + PHO_NEAR_SEC >= target) ? 1 : 0;
  828. }
  829. /**
  830. * @brief 拍照定时参数合法性校验(开机读取后调用)
  831. *
  832. * 三个参数全为0 -> 恢复默认(间隔1天 21:00);
  833. * 单个参数越界 -> 恢复默认值并保存。
  834. */
  835. void Check_Photo_Param_Valid(void)
  836. {
  837. uint8_t save = 0;
  838. if (syscfg.pho_inr_day == 0 && syscfg.pho_t_hour == 0 && syscfg.pho_t_min == 0)
  839. {
  840. syscfg.pho_inr_day = PHO_INR_DAY_DEF;
  841. syscfg.pho_t_hour = PHO_T_HOUR_DEF;
  842. syscfg.pho_t_min = PHO_T_MIN_DEF;
  843. save = 1;
  844. LOG_W("photo param all-zero, SET DEF (%d天 %02d:%02d)", syscfg.pho_inr_day, syscfg.pho_t_hour, syscfg.pho_t_min);
  845. }
  846. else
  847. {
  848. if (syscfg.pho_inr_day < PHO_INR_DAY_MIN || syscfg.pho_inr_day > PHO_INR_DAY_MAX)
  849. {
  850. syscfg.pho_inr_day = PHO_INR_DAY_DEF;
  851. save = 1;
  852. LOG_W("pho_inr_day invalid, SET DEF");
  853. }
  854. if (syscfg.pho_t_hour > 23)
  855. {
  856. syscfg.pho_t_hour = PHO_T_HOUR_DEF;
  857. save = 1;
  858. LOG_W("pho_t_hour invalid, SET DEF");
  859. }
  860. if (syscfg.pho_t_min > 59)
  861. {
  862. syscfg.pho_t_min = PHO_T_MIN_DEF;
  863. save = 1;
  864. LOG_W("pho_t_min invalid, SET DEF");
  865. }
  866. }
  867. if (save)
  868. {
  869. ef_set_env_blob("syscfg", &syscfg, sizeof(syscfg));
  870. }
  871. }
  872. /**
  873. * @brief 定点拍照的拦截条件(与时控窗口无关, 只为硬安全)
  874. *
  875. * @return char * NULL-可拍照; 否则返回被拦截的中文原因
  876. */
  877. static char *Photo_Block_Reason(void)
  878. {
  879. if (device_status.ds != 1)
  880. {
  881. return "设备已关机";
  882. }
  883. if (Demo_Mode_NEW == 1)
  884. {
  885. return "演示模式";
  886. }
  887. if (ws_now == ws_KeyTig)
  888. {
  889. return "手动触发模式";
  890. }
  891. if (device_status.bat_status == 1)
  892. {
  893. return "电池保护";
  894. }
  895. // 测试进行中不得掺和: 否则基点不推进(见 recode_timestampe_NEW 的护栏)会导致反复触发
  896. if (jts.takephoto_test == ON || jts.autotest == ON)
  897. {
  898. return "测试进行中";
  899. }
  900. return RT_NULL;
  901. }
  902. /**
  903. * @brief 时控窗口外的定点拍照(间隔N天 + 指定时分)
  904. *
  905. * 拍照不再受工作时控窗口约束: 到点即自管上电 -> 执行一次工作包 -> 下电。
  906. * 触发判定只用 syscfg.pho_inr_day / pho_t_hour / pho_t_min, 不看时控时间。
  907. * 被硬安全条件拦截时不重置基点, 条件解除后仍会补拍。
  908. *
  909. * @return uint8_t 1-本次执行了拍照 0-未到触发时刻或被拦截
  910. */
  911. static uint8_t Photo_Pkg_OnStandby(void)
  912. {
  913. if (Check_Photo_Trigger() == 0)
  914. {
  915. return 0;
  916. }
  917. char *block = Photo_Block_Reason();
  918. if (block != RT_NULL)
  919. {
  920. LOG_W("STANDBY :photo blocked by %s\n", block);
  921. return 0;
  922. }
  923. LOG_I("STANDBY :photo trig , out of work window\n");
  924. /* 拍照前把 RTU/相机/补光灯拉起(与工作窗口内同一套时序) */
  925. if (rt_pin_read(CAM_CTL) != ON || rt_pin_read(RTU_DTU) != ON)
  926. {
  927. ctr_RTU_DTU(ON);
  928. CTL_CAM1();
  929. ctr_led(ON);
  930. for (uint16_t a = 0; a < 45; a++)
  931. {
  932. rt_thread_mdelay(1000);
  933. rt_kprintf("wait for RTU start S1\n");
  934. }
  935. }
  936. /* 基点先重置: 不论成败都按本次起算, 避免失败后每秒重入 */
  937. recode_timestampe_NEW(&tm_photo);
  938. Recode_e_code(e_work_mode_pkg, 2, "时控外执行一次工作包");
  939. work_mode_pkg(); // 执行一次工作包
  940. ctr_RTU_DTU(OFF);
  941. CTL_CAM0();
  942. ctr_led(OFF);
  943. return 1;
  944. }
  945. /**
  946. * @brief 简易的数据上报,用于在等待图片上报过程中检查是否需要数据上报
  947. *
  948. */
  949. void Updata_INR_Simple(void)
  950. {
  951. INR_DAT = syscfg.dattim * 60 - 10; // 减去30 消除上报所耗时间
  952. if (math_timestampe_NEW(&tm_data) > INR_DAT)
  953. {
  954. LOG_I(" >>> watting mode > STEP A start report data C\n");
  955. recode_timestampe_NEW(&tm_data); // 此时重置时间戳
  956. wait_data_cnt = 0;
  957. UpLoad_Data(0);
  958. }
  959. }
  960. /**
  961. * @brief Construct a new Updata_INR object
  962. *
  963. * @param type > enable power off
  964. */
  965. static uint16_t Updata_INR(uint8_t type)
  966. {
  967. static uint16_t rtuon_cnt = 0; // 非工作模式时 rtu上电时间记录
  968. static char Once_poweroff = 1;
  969. /**
  970. * @brief ↓↓↓ 不在工作模式的时候 间隔上线上报数据 在此处实现 ↓↓↓
  971. *
  972. */
  973. INR_DAT = syscfg.dattim * 60 - 10; // 减去30 消除上报所耗时间
  974. wait_data_cnt++;
  975. if (wait_data_cnt >= INR_DAT || (math_timestampe_NEW(&tm_data) > INR_DAT))
  976. {
  977. if (wait_data_cnt >= INR_DAT)
  978. {
  979. LOG_I(" >>> watting mode > STEP A start report data A\n");
  980. }
  981. else
  982. {
  983. LOG_I(" >>> watting mode > STEP A start report data B\n");
  984. }
  985. recode_timestampe_NEW(&tm_data); // 此时重置时间戳
  986. // recode_timestampe(&tm_photo); // 记录数据上报开始时间
  987. wait_data_cnt = 0;
  988. ctr_RTU_DTU(ON);
  989. sprintf(device_status.net, "1"); // 去掉网络状态
  990. while (++wait_data_cnt < (WAIT_NET_MAX / 2))
  991. {
  992. SendReadInfo('D');
  993. if (atoi(device_status.net) == 0)
  994. {
  995. LOG_I(" >>> watting mode > STEP B1 RTU net get up\n");
  996. rt_thread_mdelay(2000);
  997. rt_thread_mdelay(10000); // 留有指令下发时间
  998. UpLoad_Data(0);
  999. rt_thread_mdelay(2000);
  1000. break;
  1001. }
  1002. rt_thread_mdelay(1000);
  1003. }
  1004. if (wait_data_cnt >= (WAIT_NET_MAX / 2))
  1005. {
  1006. UpLoad_Data(0);
  1007. LOG_W(" >>> watting mode > STEP B2 RTU net get failed\n");
  1008. Recode_e_code(e_rtu_net_fail, 1, "间隔上报时联网失败 > B2");
  1009. rt_thread_mdelay(5000);
  1010. }
  1011. wait_data_cnt = 0;
  1012. if (Check_Photo_Near() && (Check_Allow_Work() == res_ok)) // 接近拍照时刻 不再断电
  1013. {
  1014. LOG_I("CLOSE UP_PIC , JUMP RTU0");
  1015. }
  1016. else
  1017. {
  1018. ctr_RTU_DTU(OFF);
  1019. }
  1020. Request_ntp();
  1021. }
  1022. if (math_timestampe_NEW(&tm_data) == 0)
  1023. {
  1024. LOG_W("math_timestampe_NEW tm_data fail ,SET NOW");
  1025. }
  1026. /**
  1027. * @brief ↑↑↑ 不在工作模式的时候 间隔上线上报数据 在此处实现 ↑↑↑
  1028. *
  1029. */
  1030. /**
  1031. * @brief ↓↓↓ 首次上电后 留有三分钟数据上报 指令下发时间 ↓↓↓
  1032. *
  1033. */
  1034. if (rturead() && Once_poweroff)
  1035. {
  1036. rtuon_cnt++;
  1037. if (rtuon_cnt >= RTU_OFF_MAX)
  1038. {
  1039. LOG_I("RTU ON Time out of range , now turn it off A\n\n");
  1040. ctr_RTU_DTU(OFF);
  1041. rtuon_cnt = 0;
  1042. Once_poweroff = 0;
  1043. }
  1044. }
  1045. /**
  1046. * @brief ↑↑↑ 首次上电后 留有三分钟数据上报 指令下发时间 ↑↑↑
  1047. *
  1048. */
  1049. return wait_data_cnt;
  1050. }
  1051. void KeyTigCheck(void)
  1052. {
  1053. #define manual_mode_MX 60
  1054. #define demo_mode_MX 120
  1055. uint16_t log_en_inr = LOG_INR * 5; // 打印log的速度 数值越小 打印越频繁
  1056. static int16_t log_en_cnt = -1;
  1057. uint32_t cnt = 0;
  1058. if (ws_now == ws_KeyTig)
  1059. {
  1060. Recode_e_code(e_manual_mode, 0, "进入手动触发模式");
  1061. while (cnt++ < 60 * manual_mode_MX)
  1062. {
  1063. rt_thread_mdelay(1000);
  1064. if (ws_now != ws_KeyTig)
  1065. {
  1066. break;
  1067. }
  1068. if (chk_log_time)
  1069. rt_kprintf("处于手动触发模式 %d/%d\n", cnt, manual_mode_MX * 60);
  1070. if (cnt % 30 == 0)
  1071. {
  1072. SendReadInfo('E');
  1073. }
  1074. }
  1075. Recode_e_code(e_manual_mode, 1, "退出手动触发模式");
  1076. }
  1077. if (Demo_Mode_NEW == 1)
  1078. {
  1079. Recode_e_code(e_demo_mode, 0, "进入演示模式");
  1080. cnt = 0;
  1081. rt_kprintf("演示模式 暂停工作 %d / %d\n", cnt, demo_mode_MX * 60);
  1082. while (cnt++ < demo_mode_MX * 60)
  1083. {
  1084. rt_thread_mdelay(1000);
  1085. if (Demo_Mode_NEW == 0)
  1086. {
  1087. break;
  1088. }
  1089. if (cnt % 100 == 0)
  1090. {
  1091. rt_kprintf("演示模式 暂停工作 %d / %d\n", cnt, demo_mode_MX * 60);
  1092. }
  1093. ws_now = ws_demo;
  1094. ctr_RTU_DTU(ON);
  1095. CTL_CAM1();
  1096. }
  1097. Demo_Mode_NEW = 0;
  1098. Recode_e_code(e_demo_mode, 1, "退出演示模式");
  1099. }
  1100. }
  1101. /**
  1102. * @brief 天牛工作流程
  1103. *
  1104. */
  1105. void WorkTask_SY2(void)
  1106. {
  1107. uint16_t log_en_inr = LOG_INR * 5; // 打印log的速度 数值越小 打印越频繁
  1108. static int16_t log_en_cnt = -1;
  1109. LOG_I(" WorkTask_SY2 ");
  1110. if (Check_Allow_Work() != res_ok)
  1111. {
  1112. if (jts.switchled_test != ON)
  1113. {
  1114. ctr_led(OFF);
  1115. }
  1116. }
  1117. rt_thread_mdelay(1000);
  1118. /* 从 ENV + RTC备份寄存器 双源恢复拍照基点(重启/升级/掉电都不丢, 不推迟下次拍照) */
  1119. tm_photo = Photo_Base_Load();
  1120. if (tm_photo == 0)
  1121. {
  1122. LOG_W("photo base invalid in ENV & BKP\n"); // 老设备/首次, 下方以当前时间起算
  1123. }
  1124. /* 数据上报基点: 仅存内存, 0则重置(频繁上报无需持久化) */
  1125. if (tm_data == 0 && GetDate.Year > 20)
  1126. {
  1127. recode_timestampe_NEW(&tm_data);
  1128. }
  1129. /* 拍照基点: 双源均无有效记录时, 以当前时间起算并持久化 */
  1130. if (tm_photo == 0 && GetDate.Year > 20)
  1131. {
  1132. recode_timestampe_NEW(&tm_photo);
  1133. LOG_I("recode_timestampe INIT");
  1134. }
  1135. while (Check_Allow_Work() != res_ok) // 新增数据上报逻辑(开启rtu 上报一次数据)
  1136. {
  1137. log_en_inr = LOG_INR * 5;
  1138. rt_thread_mdelay(1000);
  1139. KeyTigCheck();
  1140. ws_now = ws_wait0; // 待机
  1141. if (chk_log_time)
  1142. {
  1143. LOG_I("STEP0 :wait work start");
  1144. }
  1145. Photo_Pkg_OnStandby(); // 定点拍照(不受时控窗口约束)
  1146. Updata_INR(1);
  1147. }
  1148. rt_thread_mdelay(1000);
  1149. uint16_t tig_cnt = 0;
  1150. char last_once = 0;
  1151. while (Check_Allow_Work() == res_ok)
  1152. {
  1153. LOG_W("STEP1 :start work > %d > %d ", workmode, takephoto_cnt);
  1154. rt_thread_mdelay(1000);
  1155. device_status.work_status = 1;
  1156. (syscfg.photim >= 5) ? (INR_PIC = (syscfg.photim * 60)) : (INR_PIC = (TakeINR * 60));
  1157. switch (workmode)
  1158. {
  1159. case RunTime_wx:
  1160. log_en_inr = LOG_INR * 5;
  1161. while ((Check_Allow_Work() == res_ok) && (workmode == RunTime_wx))
  1162. {
  1163. if (chk_log_time)
  1164. {
  1165. LOG_W("STEP2 :wait coll finsh %d%%", GetCollInfo());
  1166. }
  1167. KeyTigCheck();
  1168. ws_now = ws_wait_coll;
  1169. rt_thread_mdelay(1000);
  1170. if (Check_Photo_Trigger()) // 拍照部分(间隔N天 + 指定时分)
  1171. {
  1172. LOG_I("Read to takephoto\n");
  1173. recode_timestampe_NEW(&tm_photo);
  1174. if (rt_pin_read(CAM_CTL) != ON || rt_pin_read(RTU_DTU) != ON)
  1175. {
  1176. rt_kprintf("检测到拍照前未开启RTU");
  1177. ctr_RTU_DTU(ON);
  1178. CTL_CAM1();
  1179. ctr_led(ON);
  1180. for (uint16_t a = 0; a < 45; a++)
  1181. {
  1182. rt_thread_mdelay(1000);
  1183. rt_kprintf("wait for RTU start A2\n");
  1184. }
  1185. }
  1186. Recode_e_code(e_work_mode_pkg , 0 ,"执行一次工作包 A");
  1187. work_mode_pkg(); // 执行一次工作包
  1188. last_once = 1;
  1189. takephoto_cnt = 0;
  1190. if (math_timestampe_NEW(&tm_data) > (INR_DAT - 30)) // 接近数据上报 不再断电
  1191. {
  1192. LOG_I("CLOSE UPDATA , JUMP RTU0");
  1193. }
  1194. else
  1195. {
  1196. ctr_RTU_DTU(OFF);
  1197. }
  1198. }
  1199. // else if (math_timestampe_NEW(&tm_photo) >= ((INR_PIC)-WAIT_NET_MAX)) // RTU提前上电
  1200. // {
  1201. // // rt_kprintf("takephoto_cnt close >> %d\n",math_timestampe_NEW(&tm_photo));
  1202. // ctr_RTU_DTU(ON);
  1203. // CTL_CAM1();
  1204. // ctr_led(ON);
  1205. // } USB工业相机 开启速度极快 不需要这么麻烦了
  1206. else // 数据部分
  1207. {
  1208. Updata_INR(0);
  1209. }
  1210. if (GetDate.Year <= 20) // RTC 未同步, 拍照等待(RTC恢复后自动继续)
  1211. {
  1212. LOG_W("RTC not ready , photo wait\n");
  1213. }
  1214. }
  1215. // 时控模式退出后 本次未拍照且已到拍照时刻 则补拍一次(与主触发同一判定, 保持间隔语义)
  1216. if (last_once == 0 && Check_Photo_Trigger())
  1217. {
  1218. recode_timestampe_NEW(&tm_photo);
  1219. if (rt_pin_read(CAM_CTL) != ON || rt_pin_read(RTU_DTU) != ON)
  1220. {
  1221. rt_kprintf("检测到拍照前未开启RTU");
  1222. ctr_RTU_DTU(ON);
  1223. CTL_CAM1();
  1224. ctr_led(ON);
  1225. for (uint16_t a = 0; a < 45; a++)
  1226. {
  1227. rt_thread_mdelay(1000);
  1228. rt_kprintf("wait for RTU start A3\n");
  1229. }
  1230. }
  1231. Recode_e_code(e_work_mode_pkg , 1 ,"执行一次工作包 B");
  1232. work_mode_pkg(); // 执行一次工作包
  1233. takephoto_cnt = 0;
  1234. ctr_RTU_DTU(OFF);
  1235. CTL_CAM0();
  1236. ctr_led(OFF);
  1237. }
  1238. else
  1239. {
  1240. rt_kprintf("photo not trigger, last_once %d\n\n", last_once);
  1241. }
  1242. last_once = 0;
  1243. // rt_kprintf("takephoto_cnt >> %d > %d > %d\n" ,inr , inr * 60 , ((inr * 60) - WAIT_NET_MAX));
  1244. break;
  1245. default:
  1246. break;
  1247. }
  1248. }
  1249. if (jts.switchled_test != ON)
  1250. {
  1251. ctr_led(OFF);
  1252. }
  1253. device_status.work_status = 0;
  1254. device_status.traplampsta = 0;
  1255. }
  1256. MSH_CMD_EXPORT(work_mode_pkg , work_mode_pkg);
  1257. static void TigRay(void)
  1258. {
  1259. RayTiged = 1;
  1260. }
  1261. MSH_CMD_EXPORT(TigRay, TigRay)
  1262. /********** 红外触发驱动 ************/
  1263. /* 触发标志位(Ray开头) */
  1264. static volatile rt_bool_t RayTrigFlagA = RT_FALSE;
  1265. static volatile rt_bool_t RayTrigFlagB = RT_FALSE;
  1266. static rt_timer_t RayDebounceTimer; // 防抖定时器
  1267. static rt_timer_t RaySequenceTimer; // 序列检测定时器(新增)
  1268. /* 防抖定时器回调 */
  1269. static void RayDebounceHandler(void *param)
  1270. {
  1271. // rt_kprintf("[Ray] <%d:%d> Flags cleared by debounce\n", RayTrigFlagA, RayTrigFlagB);
  1272. }
  1273. /* 序列检测定时器回调(新增) */
  1274. static void RaySequenceHandler(void *param)
  1275. {
  1276. rt_kprintf("[Ray] Sequence timeout, flags cleared [A%d | B%d]\n", RayTrigFlagA, RayTrigFlagB);
  1277. RayTrigFlagA = RT_FALSE;
  1278. RayTrigFlagB = RT_FALSE;
  1279. rt_timer_stop(RaySequenceTimer);
  1280. }
  1281. /****************** 优化后的中断回调 ******************/
  1282. /* RayTigA中断回调 */
  1283. static rt_err_t RayIrqHandlerA(void *args)
  1284. {
  1285. if (!RayTrigFlagA)
  1286. {
  1287. RayTrigFlagA = RT_TRUE;
  1288. rt_kprintf("RayIrqHandlerA triggered\n");
  1289. // 如果B已经触发且在50ms内
  1290. if (RayTrigFlagB)
  1291. {
  1292. RayTigCnt++;
  1293. RayTiged = RT_TRUE;
  1294. RayOneLess = RT_TRUE;
  1295. rt_kprintf("[Ray] Item detected! Count: %d\n", RTC_GET_TigVal());
  1296. // 清除标志和定时器
  1297. RayTrigFlagA = RT_FALSE;
  1298. RayTrigFlagB = RT_FALSE;
  1299. rt_timer_stop(RayDebounceTimer);
  1300. rt_timer_stop(RaySequenceTimer);
  1301. }
  1302. else
  1303. {
  1304. // 启动防抖定时器
  1305. rt_timer_start(RayDebounceTimer);
  1306. }
  1307. }
  1308. return RT_EOK;
  1309. }
  1310. /* RayTigB中断回调 */
  1311. static rt_err_t RayIrqHandlerB(void *args)
  1312. {
  1313. if (!RayTrigFlagB)
  1314. {
  1315. RayTrigFlagB = RT_TRUE;
  1316. rt_kprintf("RayIrqHandlerB triggered\n");
  1317. // 启动序列检测定时器(50ms)
  1318. rt_timer_control(RaySequenceTimer, RT_TIMER_CTRL_SET_TIME, (void *)50);
  1319. rt_timer_start(RaySequenceTimer);
  1320. RayTrigFlagA = RT_FALSE;
  1321. // 启动防抖定时器
  1322. rt_timer_start(RayDebounceTimer);
  1323. }
  1324. return RT_EOK;
  1325. }
  1326. /****************** 初始化函数 ******************/
  1327. int RayDualTriggerInit(void)
  1328. {
  1329. /* GPIO模式配置 */
  1330. rt_pin_mode(RayTigA, PIN_MODE_INPUT_PULLDOWN);
  1331. rt_pin_mode(RayTigB, PIN_MODE_INPUT_PULLDOWN);
  1332. /* 创建防抖定时器 */
  1333. RayDebounceTimer = rt_timer_create("RayDebounce",
  1334. RayDebounceHandler,
  1335. RT_NULL,
  1336. 10, // 10ms防抖
  1337. RT_TIMER_FLAG_ONE_SHOT);
  1338. /* 创建序列检测定时器(新增) */
  1339. RaySequenceTimer = rt_timer_create("RaySequence",
  1340. RaySequenceHandler,
  1341. RT_NULL,
  1342. 200, // 初始50ms超时
  1343. RT_TIMER_FLAG_ONE_SHOT);
  1344. /* 绑定双中断 */
  1345. rt_pin_attach_irq(RayTigA, PIN_IRQ_MODE_RISING, RayIrqHandlerA, RT_NULL);
  1346. rt_pin_attach_irq(RayTigB, PIN_IRQ_MODE_RISING, RayIrqHandlerB, RT_NULL);
  1347. /* 使能中断 */
  1348. rt_pin_irq_enable(RayTigA, PIN_IRQ_ENABLE);
  1349. rt_pin_irq_enable(RayTigB, PIN_IRQ_ENABLE);
  1350. /* 读出保存的数据 */
  1351. RayTigCnt = RTC_GET_TigVal();
  1352. rt_kprintf("[Ray] Dual trigger initialized! Current count: %d\n", RayTigCnt);
  1353. return RT_EOK;
  1354. }
  1355. /* 命令导出(命名统一) */
  1356. // MSH_CMD_EXPORT(RayDualTriggerInit, Ray dual trigger demo);
  1357. /********** 红外触发驱动 ************/
  1358. void ChangeInrMode(void)
  1359. {
  1360. syscfg.en_tigmode = 0;
  1361. ef_set_env_blob("syscfg", (const char *)&syscfg, sizeof(syscfg));
  1362. rt_kprintf("Change Workmod Inr OK\n");
  1363. }
  1364. void ChangeTigMode(void)
  1365. {
  1366. syscfg.en_tigmode = 1;
  1367. ef_set_env_blob("syscfg", (const char *)&syscfg, sizeof(syscfg));
  1368. rt_kprintf("Change Workmod Tig OK\n");
  1369. }
  1370. MSH_CMD_EXPORT(ChangeInrMode, ChangeInrMode);
  1371. MSH_CMD_EXPORT(ChangeTigMode, ChangeTigMode);
  1372. /********** RTU Power Ctl ************/
  1373. static void rtu1(void)
  1374. {
  1375. ctr_RTU_DTU(ON);
  1376. }
  1377. static void rtu0(void)
  1378. {
  1379. ctr_RTU_DTU(OFF);
  1380. }
  1381. static char rturead(void)
  1382. {
  1383. char res = rt_pin_read(RTU_DTU);
  1384. // res ? rt_kprintf("RTU power supply : ON\n") : rt_kprintf("RTU power supply : OFF\n");
  1385. return res;
  1386. }
  1387. static char readrtu(void)
  1388. {
  1389. char res = rt_pin_read(RTU_DTU);
  1390. res ? rt_kprintf("RTU power supply : ON\n") : rt_kprintf("RTU power supply : OFF\n");
  1391. return res;
  1392. }
  1393. MSH_CMD_EXPORT(rtu1, rtu1)
  1394. MSH_CMD_EXPORT(rtu0, rtu0)
  1395. MSH_CMD_EXPORT(readrtu, readrtu)
  1396. /********** RTU Power Ctl ************/
  1397. static char temp[64] = {0};
  1398. #include "cJSON.h"
  1399. extern uint8_t get_mem_per(void);
  1400. void status_Pri(uint8_t type)
  1401. {
  1402. rt_kprintf("CAM_CTL >> %d \n", rt_pin_read(CAM_CTL));
  1403. rt_kprintf("RTU_DTU >> %d \n", rt_pin_read(RTU_DTU));
  1404. // 创建根对象
  1405. cJSON *root = cJSON_CreateObject();
  1406. if (root == NULL)
  1407. {
  1408. // rt_kprintf("return A\n");
  1409. return;
  1410. }
  1411. // 添加 cmd 字段
  1412. cJSON_AddStringToObject(root, "cmd", "data");
  1413. // 创建 ext 对象
  1414. cJSON *ext = cJSON_CreateObject();
  1415. if (ext == NULL)
  1416. {
  1417. cJSON_Delete(root);
  1418. // rt_kprintf("return B\n");
  1419. return;
  1420. }
  1421. sprintf(temp, "%02d-%02d-%02d %02d:%02d:%02d",
  1422. GetDate.Year, GetDate.Month, GetDate.Date,
  1423. GetTime.Hours, GetTime.Minutes, GetTime.Seconds);
  1424. cJSON_AddStringToObject(ext, "当前时间", temp);
  1425. if (workmode != TypeMAX_wx)
  1426. {
  1427. cJSON_AddStringToObject(ext, "工作模式", WorkModeList[workmode].keyword);
  1428. }
  1429. // rt_kprintf("mark A\n");
  1430. switch (workmode)
  1431. {
  1432. case RunTime_wx:
  1433. sprintf(temp, "%d - %d", device_status.collecting_sh1, device_status.collecting_eh1);
  1434. cJSON_AddStringToObject(ext, "时控时间", temp);
  1435. break;
  1436. default:
  1437. break;
  1438. }
  1439. // rt_kprintf("mark B\n");
  1440. sprintf(temp, "未在工作 %d ", Check_Allow_Work());
  1441. (Check_Allow_Work() == res_ok) ? cJSON_AddStringToObject(ext, "当前状态", "工作中") : cJSON_AddStringToObject(ext, "当前状态", temp);
  1442. sprintf(temp, "%d 分 (%d / %d)",
  1443. syscfg.dattim,
  1444. math_timestampe_NEW(&tm_data), INR_DAT);
  1445. cJSON_AddStringToObject(ext, "数据间隔", temp);
  1446. sprintf(temp, "%d天 %02d:%02d",
  1447. syscfg.pho_inr_day, syscfg.pho_t_hour, syscfg.pho_t_min);
  1448. cJSON_AddStringToObject(ext, "照片间隔", temp);
  1449. sprintf(temp, "%d%%", get_mem_per());
  1450. cJSON_AddStringToObject(ext, "内存使用", temp);
  1451. sprintf(temp, "%d-%d", device_status.rain_status, rain_exit);
  1452. cJSON_AddStringToObject(ext, "雨控状态", temp);
  1453. sprintf(temp, "%d-%d", RTC_GET_TigVal() , Get_REMAIN());
  1454. cJSON_AddStringToObject(ext, "卷带计数", temp);
  1455. /* 拍照诊断: 基点 / 下次时刻 / 距触发剩余 / 上次结果(判断"为什么没拍/拍了失败") */
  1456. {
  1457. char tbuf[32] = {0};
  1458. uint32_t tgt = Get_Photo_Target();
  1459. uint32_t now = NEW_TIMESTAMP;
  1460. if (GetDate.Year <= 20)
  1461. {
  1462. cJSON_AddStringToObject(ext, "拍照基点", "时间未同步");
  1463. cJSON_AddStringToObject(ext, "下次拍照", "时间未同步");
  1464. cJSON_AddStringToObject(ext, "距拍照", "--");
  1465. }
  1466. else if (tm_photo < TIMESTAMP_2000)
  1467. {
  1468. cJSON_AddStringToObject(ext, "拍照基点", "未定");
  1469. cJSON_AddStringToObject(ext, "下次拍照", "未定");
  1470. cJSON_AddStringToObject(ext, "距拍照", "--");
  1471. }
  1472. else
  1473. {
  1474. Photo_Tm_Str(tm_photo, tbuf, sizeof(tbuf));
  1475. cJSON_AddStringToObject(ext, "拍照基点", tbuf);
  1476. Photo_Tm_Str(tgt, tbuf, sizeof(tbuf));
  1477. cJSON_AddStringToObject(ext, "下次拍照", tbuf);
  1478. if (tgt <= now)
  1479. {
  1480. sprintf(temp, "已到点 %u 秒 (待触发)", (unsigned int)(now - tgt));
  1481. }
  1482. else
  1483. {
  1484. sprintf(temp, "%u 秒后", (unsigned int)(tgt - now));
  1485. }
  1486. cJSON_AddStringToObject(ext, "距拍照", temp);
  1487. }
  1488. if (photo_stat.res == 0)
  1489. {
  1490. cJSON_AddStringToObject(ext, "上次拍照", "无记录");
  1491. }
  1492. else
  1493. {
  1494. char *r = "未知";
  1495. switch (photo_stat.res)
  1496. {
  1497. case 1:
  1498. r = "成功";
  1499. break;
  1500. case 2:
  1501. r = "失败(图像传输超时)";
  1502. break;
  1503. case 3:
  1504. r = "未执行(相机忙)";
  1505. break;
  1506. case 4:
  1507. r = "进行中";
  1508. break;
  1509. default:
  1510. break;
  1511. }
  1512. Photo_Tm_Str(photo_stat.res_tm ? photo_stat.res_tm : photo_stat.tig_tm, tbuf, sizeof(tbuf));
  1513. sprintf(temp, "%s %s", r, tbuf);
  1514. cJSON_AddStringToObject(ext, "上次拍照", temp);
  1515. }
  1516. /* 到点却没拍时, 这里能看出被哪条硬安全条件拦住 */
  1517. char *blk = Photo_Block_Reason();
  1518. cJSON_AddStringToObject(ext, "拍照拦截", blk ? blk : "无");
  1519. }
  1520. if (type == 2)
  1521. {
  1522. cJSON_AddStringToObject(ext, "测试", "翻板测试开始");
  1523. }
  1524. if (type == 3)
  1525. {
  1526. cJSON_AddStringToObject(ext, "测试", "上仓门测试开始");
  1527. }
  1528. // rt_kprintf("mark C\n");
  1529. // 将 ext 对象添加到根对象
  1530. cJSON_AddItemToObject(root, "ext", ext);
  1531. // 生成 JSON 字符串
  1532. char *json_str = cJSON_Print(root);
  1533. rt_kprintf("status_Pri > %s", json_str);
  1534. if (json_str != NULL)
  1535. free(json_str);
  1536. if (type != 1)
  1537. {
  1538. char *json_strU = cJSON_PrintUnformatted(root);
  1539. DTU_Write(json_strU);
  1540. if (json_strU != NULL)
  1541. free(json_strU);
  1542. }
  1543. // 清理
  1544. cJSON_Delete(root);
  1545. }
  1546. extern works_def ws_now;
  1547. static void status_(void)
  1548. {
  1549. rt_kprintf("ws_now > %d \n", ws_now);
  1550. status_Pri(1);
  1551. }
  1552. MSH_CMD_EXPORT(status_, status_);
  1553. void Request_ntp(void)
  1554. {
  1555. rt_thread_mdelay(600);
  1556. char readinfo[] = "{\"request\":\"ntp_time\"}";
  1557. DTU_Write("%s", readinfo);
  1558. rt_kprintf("Request_ntp > %s\n", readinfo);
  1559. rt_thread_mdelay(600);
  1560. }
  1561. void ctr_RTU_DTU(uint8_t status_rtu)
  1562. {
  1563. static uint32_t protect_ON = 0;
  1564. static uint32_t protect_OFF = 0;
  1565. static uint8_t protect_sec = 8;
  1566. uint8_t now = 0;
  1567. if (protect_ON == 0)
  1568. {
  1569. recode_timestampe_NEW(&protect_ON);
  1570. }
  1571. if (protect_OFF == 0)
  1572. {
  1573. recode_timestampe_NEW(&protect_OFF);
  1574. }
  1575. rt_pin_mode(RTU_DTU, PIN_MODE_OUTPUT);
  1576. if (status_rtu == ON)
  1577. {
  1578. now = math_timestampe_NEW(&protect_OFF);
  1579. while (now < protect_sec)
  1580. {
  1581. LOG_W("ctr_RTU_DTU ON wait for %d sec", protect_sec - now);
  1582. rt_thread_mdelay(1000);
  1583. now++;
  1584. }
  1585. rt_pin_write(RTU_DTU, ON);
  1586. recode_timestampe_NEW(&protect_ON);
  1587. }
  1588. else if (Demo_Mode_NEW == 0 && ws_now != ws_KeyTig && (syscfg.not_poweroff == 0)) // 这两种情况下 不允许断电
  1589. {
  1590. now = math_timestampe_NEW(&protect_ON);
  1591. while (now < protect_sec)
  1592. {
  1593. LOG_W("ctr_RTU_DTU OFF wait for %d sec", protect_sec - now);
  1594. rt_thread_mdelay(1000);
  1595. now++;
  1596. }
  1597. LOG_W("RTU电源复位!!!");
  1598. rt_pin_write(RTU_DTU, OFF);
  1599. CTL_CAM0();
  1600. syscfg.rtu_sta = 0;
  1601. sprintf(device_status.net, "1"); // 断电的时候 网络状态置为断开
  1602. recode_timestampe_NEW(&protect_OFF);
  1603. }
  1604. else
  1605. {
  1606. LOG_W("RTU电源复位被打断 ws_now %d , Demo_mode %d\n", ws_now, Demo_Mode_NEW);
  1607. }
  1608. }
  1609. static void tig_plus(void)
  1610. {
  1611. RayTigCnt++;
  1612. }
  1613. MSH_CMD_EXPORT(tig_plus, tig_plus);