项目需求
原代码是稳定的,现我实现EMQ平台断开连接的时候,把HSL的模拟点位数据采集到网关,然后存入Flash,当EMQ平台连接的时候,把Flash里面的点位数据放在消息队列里面,不影响实时采集。
核心1:EMQ平台断开,保持存储稳定
核心2:不影响实时采集的前提下去把flash的数据读出来在空闲的时候发出去
核心3:进入断点续传的条件是连续5次发布失败进入断点续传
问题描述
核心1遇到了问题,就是EMQ断开20分钟左右会死机,没日志输出。
最后的日志,然后就一直在进行度队列操作,队列ID是垃圾值
初步分析
1.队列可能在使用的时候没有创建
2.队列的id存的地方被覆盖了
[2024-07-24 17:20:49.179]# RECV ASCII>
[INFO][556][Plc_s7_Task:1404] RDataCnt:30,RWarnCnt:0 CDataCnt:30,CWarnCnt:0 Tol:30,elaps: 251ms
[INFO][556][getNetSignalValue:1037] EC20 Get Net Signle: "25"
[INFO][556][print_memory_usage:594] m_aucSysMem0 2000d4f8 Memory Used:5992, Free:135072, Tol:141064, MallocCnt: 21082, FreeCnt: 20964, Malloc-Free: 118
[WARN]osSwTmrTask:111 timer_handler(080540fd) cost too many ms(5995)
代码
void point_timer_Callback(UINT32 arg)
{
UINT32 uwReportRet = LOS_OK;
UINT32 uwCollectRet = LOS_OK;
uint16_t ep_resources[] = {RESOURCES_STATUS};
char buff[64] = {0};
char *buf[1] = {buff};
LOS_SwtmrStop(point_pusSwTmrID);
// 反写任务正在执行,跳过本次检查
if (g_reverse_write_processing == true) {
// if (g_reverse_write_processing == true) {
//重新开启定时器,准备下一次检查
LOS_SwtmrStart(point_pusSwTmrID);
return;
}
if (!Get_Point_Status())
{
LOS_SwtmrStart(point_pusSwTmrID);
return;
}
//启动上报任务
uwReportRet = start_report_task();
if(LOS_OK != uwReportRet)
{
ATINY_LOG(LOG_ERR, "start_report_task Error!");
stop_report_task();
}
//启动采集任务
uwCollectRet = start_collect_task();
if (LOS_OK != uwCollectRet)
{
ATINY_LOG(LOG_ERR, "start_collect_task Error!");
stop_collect_task();
}
if (LOS_OK != uwCollectRet || LOS_OK != uwReportRet)
{
//检查是否绑定
if(0 == read_resoures(LWM2M_EP_OBJECT_ID, 0, ep_resources, buf, 1, RESOURCES_POINT_SAVE_STRING_MAX))
{
if(0 == atoi(buf[0]))
{
ATINY_LOG(LOG_INFO, "Wait Bing!");
}
else
{
ATINY_LOG(LOG_INFO, "Restart Report || Collect!");
}
}
else
{
ATINY_LOG(LOG_ERR, "File System Error, Need Clean Config & Restart!");
}
}
//检查系统异常
//TODO:是否应该独立为一个检测线程,但上报通道依赖mqtt,mqtt又和数据点采集耦合在一起。应先解耦mqtt和数据采集
if(LOS_OK == check_report_task())
{
check_sys_warning();
}
south_report_gateway();
print_memory_usage();
//重新开启定时器,准备下一次检查
LOS_SwtmrStart(point_pusSwTmrID);
}
解决方法
080540fd 地址定位到回调函数是void point_timer_Callback(UINT32 arg)
根据日志知道是这个回调时间有点长。
那为啥不是立即死而是过了几十分钟死呢?
代码优化等级从2调为0,进一步调试
LOS_SwtmrStop(point_pusSwTmrID);
他知道他自己耗时还在函数这里把定时器关了,怒。那为啥会调试的时候死在读队列(队列操作)里面呢?因为定时器的回调函数其实都是存在定时器回调队列里面的,如果一个回调过长,因为定时器的回调优先级0,其他高优先级任务都执行不下去。在实时性操作系统里会紊乱系统,加上屎山代码,就会死机。所以在liteos这种系统里面,多加日志输出,不可完全信调试。调试只是一瞬间的观察,调试可以判断这一段时间进而想一下自己的改动点和日志做分析得出怀疑点。
上面判断条件
if (g_reverse_write_processing == true )
改为
if (g_reverse_write_processing == true || BreakPoint_State == BreakPointSaveOnline)
OK,不要动不动在回调函数写耗时操作,一两次可能没问题,但是一直进行回调的话,会造成奇怪的紊乱。