TT12-MCU/applications/core.c
2023-09-07 15:24:38 +08:00

780 lines
22 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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 <rtthread.h>
#define LOG_TAG "core"
#define LOG_LVL LOG_LVL_DBG
#include <ulog.h>
#include <board.h>
#include <ttmsg/ttmsg.h>
//#include <cfg.h>
#include <usrcfg.h>
#include <ttTR/ttTR.h>
//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_mutex_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;//自动或手动引起TT开机
//void TTisReady(void)
//{
// rt_sem_release(TTReady);
//}
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.84\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,15,03,30,08,15,09,30\n"
"#超时时间M主动开机上报数据情况下最后一次通信后开始计时超时后关闭TT\n"
"timeout=5\n"
"# 工作模式1-TT2-BD\n"
"commMode=1\n"
"# 自毁功能开关1-开启0-跟关闭\n"
"selfDesSW=0\n"
"# 位置告警功能开关1-开启0-关闭\n"
"locAlert=1;\n"
"# 定时上报位置信息间隔M\n"
"locRepInterval=1\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(LJW_CFG_FILE_NAME, 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");
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);
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_mutex_create("okTosend", 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 (isInWindowZone() || isManualWindow()) {//开机检查是否在开窗区间内是则给TT开机
initTT();
setWindowMode();
}
}
//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();
}
void TTIsReady(void)
{
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 (isWindowMode) {
stopTM();
LOG_W("TIMEOUT stopped.[isWindowMode]");
return;
}
if (isManualWindow()) {
stopTM();
LOG_W("TIMEOUT stopped.[isManualMode]");
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状态。需求条件1TT连续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;
static rt_uint8_t d[BUFFER_ROW][200] = { };//need static?
static rt_uint8_t s[BUFFER_ROW] = { };
if (!getFileSize(f->fname)) {
LOG_W("file '%s' is empty.",f->fname);
clearFileToSend(f->fname);
rt_mutex_release(okTosend);
return;
}
if (getFileSize(f->fname) > scfg.maxSizePerFile+200) {//部分demo数据体积>>1k
LOG_W("file '%s' is too large to send.",f->fname);
clearFileToSend(f->fname);
rt_mutex_release(okTosend);
return;
}
LOG_D("ready to send '%s---%d'",f->fname,f->index);
rt_uint8_t len = pack_File(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);
for (rt_uint8_t var = 0; var < len; var++)
{
if (!f->index || (var+1) == f->index)
{ //index=0 全发或者仅发index
if (sendMsg(d[var], s[var]) == RT_EOK) {
LOG_D("send pack[%d] with %d bytes done.",var+1,s[var]);
};
rt_thread_mdelay(3000);//发送间隔目前服务器未处理暂设为3s避免粘包
}
}
LOG_I("upSendFile '%s' done.",f->fname);
clearFileToSend(f->fname);//由于没有准确的校核机制,此处自能认为是发送成功
// list_thread();
}
//当前文件处理完后置位
rt_mutex_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 * 5, 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()
{
//优先发送未达指定大小的文件
char fnow[50];
getLstCacheFileName(fnow);
if (getFileSize(fnow)) {//有数据才发
updateCacheFileName();
LOG_I("send latest data first");
rt_mutex_take(okTosend, RT_WAITING_FOREVER);
upSendFile(fnow,0);
// rt_mutex_release(okTosend);//由upSendFile释放
}
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 i = cnt-1; i > -1; i--)//倒序发送
{
// LOG_D("f[%d]=%s",i,f[i]);
// continue;
if (rt_mutex_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满足
{
LOG_I("等待TT就绪");
LOG_I("当前规则为:连续%d个采集周期TT信号质量不低于%d",scfg.minTTPeriCnt,scfg.minTTsinal);
initTT();
}
size_t maxCnt = 0xffff;
if (!isWindowMode && !isManualWindow()) {
maxCnt = scfg.maxTTRetryCnt;
}
for (size_t var = 0; var < maxCnt; var++) //轮询尝试
{
LOG_I("第%d/%d次尝试。", var + 1,maxCnt);
int rst = rt_event_recv(&chkSta, TT_IS_OK, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, rt_tick_from_millisecond(scfg.maxTTWaitTime*60*1000),
RT_NULL);
// rst = rt_sem_take(TTReady, rt_tick_from_millisecond(maxTTWaitTime * 60 * 1000));
if (rst == RT_EOK) //条件2满足
{
LOG_I("TT准备就绪");
//当前固件底层未缓存待发数据,导致前几包数据可能丢失,暂时用延时规避
rt_thread_mdelay(2*60*1000);
getAndSendFile();
break;
}
else if (rst == -RT_ETIMEOUT)//超时则重试
{
if ((var+1) < maxCnt)
{
LOG_W("第%d次尝试中TT准备失败重试。", var + 1);
deInitTT();
rt_thread_mdelay(15000);
initTT();
}
else {
LOG_E("TT准备失败");
deInitTT();
}
}
if (!isTCPok()) {
deInitTT();
break;
}
}
LOG_W("--------one mission 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 >= 3)
{
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();
// resetTM();//启动超时,激活后开启
}
}
rt_thread_mdelay(3000); //chk with 3 second interval
}
initThread=RT_NULL;
// 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);
LOG_W("shunt down TT DONE");
}
// initThread = RT_NULL;
deinitThread = RT_NULL;
// list_thread();
}
/**
* TT上电初始化TT相关的网络、TCP等并保活
*/
void initTT()
{
/* 创建 serial 线程 */
if (initThread != RT_NULL) {
LOG_W("TT is already running.");
return;
}
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,);
MSH_CMD_EXPORT(resetTM,resetTM);
MSH_CMD_EXPORT(d_upSendFile,d_upSendFile);
#endif