/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2023-06-01 murmur the first version * 处理TT相关的初始化、状态切换等逻辑 */ #include #define LOG_TAG "core" #define LOG_LVL LOG_LVL_DBG #include #include #include //#include #include #include //static struct rt_event sw_check; //软件条件 static struct rt_event chkSta; //发送条件,含两部分,1-超时或文件准备好,2-TT满足通信条件 #define ALL_READY 1 //static int maxTTWaitTime = 4; //static int maxTTRetryCnt = 3; rt_sem_t TTReady= RT_NULL;//天通具备发送状态后 rt_sem_release(TTReady); rt_sem_t cfgUpdate = RT_NULL;//配置文件更新 rt_sem_t shuntDownTT = RT_NULL;//关闭TT rt_sem_t okTosend = RT_NULL;//可以发送后续文件。仅一个线程,文件依次发送 rt_sem_t okToreport = RT_NULL;//配置信息ACK static rt_thread_t initThread=RT_NULL; static rt_thread_t deinitThread=RT_NULL; static rt_timer_t tmrToPNTT=RT_NULL; static uint8_t isWindowMode=0;//RTC引起TT开机 static uint8_t isFileMode=0;//file is ok //是否为idle状态。idle状态下TT未开启,232无数据 uint8_t isIdle(void) { if (!isTCPok() && !isEthUP() &&!isFileMode && !isWindowMode && !isManualWindow() && !is232Busy()) { return 1; } else { return 0; } } uint8_t isInFileMode(void) { return isFileMode; } SYS_CFG scfg={ .sendInterval =60, .maxTTWaitTime = 4, .maxTTRetryCnt = 3, .minTTPeriCnt=5, .minTTsinal=5, .timeout=5, .maxSizePerFile=1024, // .openWindowTime[]={0x02, 0x0F, 0x03, 0x1E, 0x08, 0x0F, 0x09, 0x1E} }; /** * 初始化必要的文件 */ void initFiles() { //无配置文件则新建 if (!getFileSize("cfg.ini")) { //copy backup if (getFileSize("sd/cfg.ini")) { copy("sd/cfg.ini","cfg.ini"); LOG_I("copied new cfg file."); } //creat new else { const char *cfgstr = "[config]\n" "# V1.9\n" "#定时(默认每小时)上报时刻,\n" "sendInterval=15\n" "# 最大文件大小,超过此大小则进入发送流程\n" "maxSizePerFile=1024 \n" "# 最小等待时间(S),超时后进入待机模式(暂未使用)\n" "minSecToSleep=30 \n" "# TT最长等待激活时间(M),超时后重启\n" "maxTTWaitTime=10\n" "# TT最大重试次数\n" "maxTTRetryCnt=3\n" "# TT最低信号值\n" "minTTsinal=3\n" "# TT状态检测周期数,连续满足归为一次判决,减少波动的影响\n" "minTTPeriCnt=3\n" "# 压缩方式,0-不压缩\n" "compressType=0\n" "# 加密方式,0-不加密\n" "encrytType=1\n" "# 开窗时间(UTC+8),持续时间(H)。以【,】为分隔符\n" "openWindowTime=02,00,03,30,08,00,09,00\n" "#超时时间(M),主动开机上报数据情况下最后一次通信后开始计时,超时后关闭TT\n" "timeout=10\n" "# 工作模式,1-TT,2-BD\n" "commMode=1\n" "# 自毁功能开关,1-开启,0-跟关闭\n" "selfDesSW=0\n" "# 位置告警功能开关,1-开启,0-关闭\n" "locAlert=0;\n" "# 定时上报位置信息间隔(M)\n" "locRepInterval=5\n" "isMaWin=-1\n" "[stats]\n" "# 继电器开关次数,影响继电器寿命(<10W次)\n" "swCnt=12\n" "# 第一次上电就满足硬件条件的次数,\n" "oneShotCnt=0\n" "# 重启次数\n" "bootCnt=13\n" "# 发送总次数,由软件条件触发\n" "sendCnt=0\n" "# TT初始化失败次数,由MaxRetryCnt次失败后触发\n" "errCnt=0\n" "# 最短激活时间\n" "minActiveTime=0\n" "# 最长激活时间\n" "maxActiveTime=0\n" "# 平均激活时间,暂不考虑实现\n" "meanActiveTime=0\n" "# 总定时器超时次数\n" "allCnt=0\n" "# 发送成功次数\n" "okCnt=0\n"; LOG_W("no cfg file found, creat one."); int fd = open(LJW_CFG_FILE_NAME, O_WRONLY | O_CREAT); if (fd > 0) { write(fd, cfgstr, strlen(cfgstr)); close(fd); } else { LOG_E("failed to creat cfg file."); } } } //map file if (!getFileSize("map.geojson")) { if (getFileSize("sd/map.geojson")) { copy("sd/map.geojson","map.geojson"); LOG_I("copied new map file."); } else { const char *cfgstr = "{\"type\":\"FeatureCollection\",\"features\":[{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"coordinates\":[[[109.85768145990244,18.422149049902515],[109.85578152088834,18.418353201926465],[109.86089674131063,18.41424527209267],[109.86531775324693,18.418387867583846],[109.86491584307038,18.421785068111333],[109.85768145990244,18.422149049902515]]],\"type\":\"Polygon\"}}]}"; LOG_W("no map file found, creat map one."); int fd = open("map.geojson", O_WRONLY | O_CREAT); if (fd > 0) { write(fd, cfgstr, strlen(cfgstr)); close(fd); } else { LOG_E("failed to creat map file."); } } } // checkFlash(); } /** * 更新全局参数 */ static void initCfg(void) { initFiles(); scfg.maxTTWaitTime = get_cfg("maxTTWaitTime"); scfg.maxTTRetryCnt = get_cfg("maxTTRetryCnt"); scfg.minTTPeriCnt = get_cfg("minTTPeriCnt"); scfg.minTTsinal = get_cfg("minTTsinal"); scfg.timeout = get_cfg("timeout"); scfg.maxSizePerFile = get_cfg("maxSizePerFile"); scfg.locRepInterval = get_cfg("locRepInterval"); scfg.isMaWin = get_cfg("isMaWin"); scfg.locAlert = get_cfg("locAlert"); char str[sizeof(scfg.openWindowTime)*4]; get_cfgs("openWindowTime", str); size_t len = str2Byte(str, 3, 10, scfg.openWindowTime); // LOG_HEX("win",16,scfg.openWindowTime,len); updateAlarm(scfg.openWindowTime, len); startAlarm(); LOG_D("cfg updated."); } static void updatecfg(void) { //等待更新 while(1) { // rt_thread_mdelay(10*1000);//10s写入一次数据到文件 if(rt_sem_take(cfgUpdate, RT_WAITING_FOREVER) == RT_EOK) { updateAllSysCfg(&scfg, 0); } } } void sysSemInit() { okTosend = rt_sem_create("okTosend", 0, RT_IPC_FLAG_FIFO);//同一时间仅一个线程发送文件 cfgUpdate = rt_sem_create("cfgUpdate", 0, RT_IPC_FLAG_FIFO);//更新cfg shuntDownTT = rt_sem_create("shuntDNTT", 0, RT_IPC_FLAG_FIFO);//关闭TT okToreport = rt_sem_create("reportINFO", 0, RT_IPC_FLAG_FIFO);//关闭TT // rt_event_init(&chkSta, "chkSta", RT_IPC_FLAG_FIFO);//检查天通状态 // rt_sem_release(cfgUpdate); //上电更新值 // initCfgMutex(); // void reportINFO(); reportINFO(); /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("updatecfg", updatecfg, RT_NULL, 1024 * 5, 27+1, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { LOG_E("thread 'updatecfg' create failure."); return; } // LOG_D("sysSemInit DONE."); } void sysEventInit(void) { rt_err_t result = rt_event_init(&chkSta, "chkSta", RT_IPC_FLAG_PRIO); if (result != RT_EOK) { LOG_E("init event failed.\n"); } } void sysInit(void) { sysSemInit(); initCfg(); checkManualWindow(); if (isManualWindow()) {//手动开窗优先级最高 initTT(); } else if (isInWindowZone()) {//开机检查是否在开窗区间内,是则给TT开机 setWindowMode(); initTT(); } else if (isNeedRestore()) {//Restoring configuration after a crash initTT(); } if (getCntOfFileToSend()) { LOG_I("cached file(s) to send"); fileIsReady(); } } //INIT_COMPONENT_EXPORT(sysInit); void timerIsReady(void) { rt_event_send(&chkSta, TIMER_IS_OUT); stopTM();//停止而不是重置,避免超时时间小于未激活重试时间的情况下意外关机。 } void fileIsReady(void) { rt_event_send(&chkSta, FILE_IS_OK); stopTM(); } static int isTTok=0; void TTIsReady(void) { isTTok=1; // rt_event_send(&chkSta, TT_IS_OK); // rt_sem_release(TTReady); // resetTM(); } void TTIsNotReady(void) { isTTok=0; // rt_event_send(&chkSta, TT_IS_OK); // resetTM(); } /** * 关闭超时定时器,定时时间到后关闭TT */ void stopTM(void) { if (!tmrToPNTT) { return; } rt_timer_stop(tmrToPNTT); rt_timer_stop(tmrToPNTT); } /** * 更新开窗flag以关闭超时机制 */ void setWindowMode(void) { isWindowMode = 1; stopTM(); } /** * 恢复超时机制 */ void clearWindowMode(void) { isWindowMode = 0; stopTM(); } /** * 重启超时定时器,定时时间到后关闭TT * 逻辑是非开窗(包括自动、手动)状态下TT无接收数据则启动关机倒计时 */ void d_remain(void); void resetTM(void) { // LOG_D("try to reset"); if (tmrToPNTT == RT_NULL) { LOG_E("tmrToPNTT is NULL"); return; } if (isManualWindow()) { stopAlarm(); stopTM(); LOG_W("TIMEOUT stopped.[isManualMode]"); return; } if (isWindowMode) { stopTM(); LOG_W("TIMEOUT stopped.[isWindowMode]"); return; } if (isFileMode) { stopTM(); LOG_W("TIMEOUT stopped.[isFileMode]"); return; } rt_tick_t t= rt_tick_from_millisecond(scfg.timeout*60*1000); rt_timer_control(tmrToPNTT, RT_TIMER_CTRL_SET_TIME,(void*) &t); // rt_timer_stop(tmrToPNTT); int i=0; while(rt_timer_start(tmrToPNTT) != RT_EOK) { if (i++ > 10) { LOG_E("TIMEOUT fault."); return; break; } rt_thread_mdelay(200); } LOG_W("%d minutes from now to power down TT.",scfg.timeout); // d_remain(); // rt_thread_mdelay(500); // d_remain(); // rt_thread_mdelay(500); // d_remain(); // rt_thread_mdelay(500); // d_remain(); } void d_remain(void) { if (tmrToPNTT == RT_NULL) { LOG_W("tmrToPNTT is NULL"); return; } rt_tick_t arg1,arg2; rt_timer_control(tmrToPNTT, RT_TIMER_CTRL_GET_TIME, (void*)&arg1); rt_timer_control(tmrToPNTT, RT_TIMER_CTRL_GET_REMAIN_TIME, (void*)&arg2); rt_uint32_t arg3=0; rt_timer_control(tmrToPNTT, RT_TIMER_CTRL_GET_STATE, (void*)&arg3); LOG_D("%s",arg3?"YES":"NO"); if (arg3) { int tmp=(arg2-rt_tick_get())/1000; LOG_D("t=%ld,0x%X -> 0x%X",tmp,rt_tick_get(),arg2); LOG_D("%dm%ds / %d min",tmp/60,tmp%60,arg1/60000); } } /* 定时器超时函数 */ static void timeoutFunc(void *parameter) { stopTM(); if (deinitThread) { LOG_W("timeout leads to shunt down TT"); // rt_thread_mdelay(3000);// no delay deInitTT();//Function[rt_mutex_take] shall not be used in ISR } } //extern void ttinfoInit(void); //extern void startTTinfo(void); /** * 监控TT状态。需求条件1:TT连续5个周期为激活状态且信号强度不低于5。 */ void checkTT() { repGetTT(); //持续更新 } typedef struct { char fname[60]; uint8_t index; }FILE_INFO; static void upSendFile_thread_entry(void *parameter) { FILE_INFO *f = RT_NULL; f = (FILE_INFO *) parameter; // rt_mutex_take(okTosend, RT_WAITING_FOREVER); static rt_uint8_t d[BUFFER_ROW][200] = { };//need static? static rt_uint8_t s[BUFFER_ROW] = { }; memset(s,0,BUFFER_ROW); if (!getFileSize(f->fname)) { LOG_W("file '%s' is empty.",f->fname); clearFileToSend(f->fname); rt_sem_release(okTosend); return; } if (getFileSize(f->fname) > scfg.maxSizePerFile+500) {//部分demo数据体积>>1k LOG_W("file '%s' is too large to send.",f->fname); clearFileToSend(f->fname); rt_sem_release(okTosend); return; } LOG_D("ready to send '%s---%d'",f->fname,f->index); // uint8_t len = pack_File(f->fname, 0, d, s); uint8_t len = pack_data(f->fname, 0, d, s); // for (size_t i = 0; i < len; i++) { // LOG_HEX("msg",27,d[i],s[i]); // } // list_thread(); if (len) { LOG_D("%d pack(s) to send", f->index ? 1 : len); uint8_t issendall=1; for (size_t var = 0; (var < len && s[var]!=0); var++) { if (!f->index || (var+1) == f->index) { //index=0 全发,或者仅发index if (isTTjh() && sendMsg(d[var], s[var]) == RT_EOK) { LOG_D("send pack[%d] with %d bytes done.",var+1,s[var]); } else { LOG_E("TT未到位或发送失败"); issendall=0; // break; } rt_thread_mdelay(4000);//发送间隔,目前服务器未处理,暂设为3s避免粘包 } } if (issendall) { LOG_I("upSendFile '%s' done.",f->fname); clearFileToSend(f->fname);//由于没有准确的校核机制,此处自能认为是发送成功 } // list_thread(); } //当前文件处理完后置位 rt_sem_release(okTosend); } /** * 发送文件 * @param f 待发文件,指完整路径名 * @param index 指定发送的切片索引,为0时表示全部发送 */ void upSendFile(const char *f, int index) { static FILE_INFO info; rt_memset(&info, 0, sizeof(FILE_INFO)); strcpy(info.fname, f); info.index = index; /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("SendFile", upSendFile_thread_entry, (void *) &info, 1024 * 10, 27, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { LOG_E("thread 'upSendFile' create failure."); return; } } void d_upSendFile(int argc, char **argv) { int index=0; if (argc == 3) { index = atoi(argv[2]); } upSendFile(argv[1], index); } /** * 获取本地待发文件列表并倒序发送 */ void getAndSendFile() { // resetTM(); isFileMode=0;//逻辑不是很准确 //优先发送未达指定大小的文件 char fnow[50]; getLstCacheFileName(fnow); if (getFileSize(fnow)) {//非空情况下加入待发列表 manualAaddlst(fnow); updateCacheFileName(); cryptCdata(fnow);//密文覆盖明文 } // rt_thread_mdelay(1000); uint8_t cnt = (uint8_t)getCntOfFileToSend(); if(cnt) { LOG_I("ready to send %d file(s).",cnt); } else { LOG_W("no files waiting to be sent."); resetTM();//启动超时 return; } rt_sem_release(okTosend);//初始赋值 static int index[MAX_KEY_LEN]; char f[cnt][MAX_KEY_LEN]; getFilesToSend(f, index); for (int var = 0; var < 1; ++var) { for (int i = cnt-1; i > -1; i--)//倒序发送 { LOG_D("f[%d]=%s",i,f[i]); // continue; if (rt_sem_take(okTosend, RT_WAITING_FOREVER) == RT_EOK) { rt_thread_mdelay(1000);//thread close upSendFile(f[i],index[i]); } } } //发送完毕 LOG_D("mission done."); resetTM(); } static void chkAndSendFile_thread_entry() { while (1) { // int e; LOG_I("ready and waiting",scfg.sendInterval); if (rt_event_recv(&chkSta, FILE_IS_OK | TIMER_IS_OUT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, RT_NULL) == RT_EOK) //条件1满足 { isFileMode=1; LOG_I("等待TT就绪"); LOG_I("当前规则为:连续%d个采集周期TT信号质量不低于%d",scfg.minTTPeriCnt,scfg.minTTsinal); initTT(); } TTIsNotReady();//复位 size_t maxCnt = 0xffff; if (!isWindowMode && !isManualWindow()) { maxCnt = scfg.maxTTRetryCnt; } for (size_t var = 0; var < maxCnt; var++) //轮询尝试 { rt_tick_t tm = rt_tick_get_millisecond(); LOG_I("第%d/%d次尝试。", var + 1, maxCnt); int flag=0; while (1) { if (isTTok) { isTTok=0; rt_thread_mdelay(15000); getAndSendFile(); flag=1; break; } else { //not ready if ((rt_tick_get_millisecond() - tm) > rt_tick_from_millisecond(scfg.maxTTWaitTime * 60 * 1000)) //超时 { break; } else { rt_thread_mdelay(1000); } } } //end while if (flag) { break; } if ((var + 1) < maxCnt) { LOG_W("第%d次尝试中TT准备失败,重试。", var + 1); deInitTT(); rt_thread_mdelay(5000); initTT(); } else { LOG_E("TT准备失败"); deInitTT(); isFileMode=0; break; } } // LOG_W("--------one loop DONE--------"); } } /** * 检查状态并发送文件 */ void chkAndSendFile() { sysEventInit();//必须此处初始化,不然EVENT无法加入链表 if (!tmrToPNTT) { tmrToPNTT = rt_timer_create("TTtimeout", timeoutFunc, RT_NULL, rt_tick_from_millisecond(scfg.timeout*60*1000), RT_TIMER_FLAG_PERIODIC);//|RT_TIMER_FLAG_SOFT_TIMER); } /* 创建 serial 线程 */ rt_thread_t thread = rt_thread_create("chk&send", chkAndSendFile_thread_entry, RT_NULL, 1024 * 5, 19, 10); /* 创建成功则启动线程 */ if (thread != RT_NULL) { rt_thread_startup(thread); } else { LOG_E("thread 'chk&send' create failure."); return; } } INIT_APP_EXPORT(chkAndSendFile); //RT_TICK_PER_SECOND //tcp连接保活 //实际场景不存在中间断掉的可能 void initTT_thread_entry() { pwTT_thread_entry("1"); // rt_thread_mdelay(15000);//wait TT to boot up static uint8_t cnt=0; while (1) { if (!isTTon()) { break; } if (!tmrToPNTT) { tmrToPNTT = rt_timer_create("TTtimeout", timeoutFunc, RT_NULL, rt_tick_from_millisecond(scfg.timeout*60*1000), RT_TIMER_FLAG_ONE_SHOT);//|RT_TIMER_FLAG_SOFT_TIMER); } if (!isEthUP())//只初始化一次 { LOG_D("init eth..."); rt_pin_write(ETH_RESET_PIN, PIN_HIGH); if (rt_hw_stm32_eth_init() == RT_EOK)//激活网口 { LOG_I("eth inited DONE."); }; } else if (!isTCPok()) //判断TCP连接是否正常,异常自动重连 { if (tcpInit() != RT_EOK) { if (cnt >= 10) { cnt=0; LOG_E("TT is not responded, check the power or TT device."); deInitTT(); break; } if (isEthUP()) { cnt++; LOG_D("[%d] retry.",cnt); rt_hw_stm32_eth_deinit(); continue; } } else { LOG_I("TCP is ready."); tcpRecMQ(); //开启tcp接收线程 recTT(); repGetTT(); reportLoc(); if (getCntOfFileToSend()) fileIsReady(); // resetTM();//启动超时,激活后开启 } } rt_thread_mdelay(3000); //chk with 3 second interval } initThread=RT_NULL; pwTT_thread_entry("0"); // list_thread(); // cmd_free(); } void deInitTT_thread_entry() { if (deinitThread != RT_NULL && rt_sem_take(shuntDownTT, RT_WAITING_FOREVER) == RT_EOK) { deinitThread =RT_NULL; tcpClose(); if (isEthUP()) { rt_hw_stm32_eth_deinit(); //qu激活网口 } pwTT_thread_entry("0"); // startAlarm(); clearWindowMode(); // setManualWindow(-1); setWorkSta(0); // isFileMode=0; LOG_W("shunt down TT DONE"); } // initThread = RT_NULL; deinitThread = RT_NULL; pwTT_thread_entry("0"); // list_thread(); } /** * TT上电,初始化TT相关的网络、TCP等,并保活 */ void initTT() { /* 创建 serial 线程 */ if (initThread != RT_NULL) { LOG_W("TT is already running."); return; } set3SRTC(); setWorkSta(1); // isFileMode=1; initThread = rt_thread_create("initTT", initTT_thread_entry, RT_NULL, 1024 * 5, 25, 10); /* 创建成功则启动线程 */ if (initThread != RT_NULL) { rt_thread_startup(initThread); } else { LOG_E("thread 'initTT' create failure."); return; } /* 创建 serial 线程 */ if (deinitThread != RT_NULL) { return; } deinitThread = rt_thread_create("deInitTT", deInitTT_thread_entry, RT_NULL, 1024 * 5, 25, 10); /* 创建成功则启动线程 */ if (deinitThread != RT_NULL) { rt_thread_startup(deinitThread); } else { LOG_E("thread 'deInitTT' create failure."); return; } } /** * TT上电,初始化TT相关的网络、TCP等,并保活 */ void deInitTT() { rt_sem_release(shuntDownTT); } #define FUNC_DEMO #ifdef FUNC_DEMO //测试时导出命令到控制台 MSH_CMD_EXPORT_ALIAS(TTIsReady,ttisok,TT_IS_OK); MSH_CMD_EXPORT(fileIsReady,fileIsReady); //MSH_CMD_EXPORT(chkAndSendFile, chkAndSendFile); //MSH_CMD_EXPORT(getAndSendFile, getAndSendFile); MSH_CMD_EXPORT(initTT,初始化TT); MSH_CMD_EXPORT(deInitTT,去初始化TT,勿在初始化过程中使用); MSH_CMD_EXPORT(d_remain,time remain); MSH_CMD_EXPORT(resetTM,resetTM); MSH_CMD_EXPORT(d_upSendFile,d_upSendFile); #endif