#include "meter_calculate/meter_coll_task.h" #include "app_init/app_init.h" #include "app_init/task_lock.h" #include "mylog/mylog.h" #include "publicdata/publicdata.h" #include "wdt_task/wdt_task.h" #include "usart.h" #include "fault_cheak/fault_interface.h" #define METERCOLL_TASK_ID (TASK_ID_MeterColl) #define METERCOLL_SLEEP_TIME_MS() (MY_GET_SLEEP_TIME(METERCOLL_TASK_ID)) #define METERCOLL_LOCK_INIT() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_INIT)) #define METERCOLL_LOCK_TAKE() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_TAKE)) #define METERCOLL_LOCK_GIVE() (u8_task_lock_ctrl(METERCOLL_TASK_ID, TASK_LOCK_OP_GIVE)) #define METERCOLL_FEED_DOG() (v_wdt_setFlag(METERCOLL_TASK_ID)) #define METERCOLL_LOG(fmt, ...) MYLOG_MSG(METERCOLL_TASK_ID, (fmt), ##__VA_ARGS__) #define D_Print_flag (0) //printf 打印标志 #define D_YaDa_Flag (1) //雅达电表 使用高精度指令不一样 其组合有功电量获取为 00 0E 00 00 XXXXX.XXX #define D_FrameStart 0x68 /* 帧头 */ #define D_FrameEnd 0x16 /* 帧尾 */ //645协议 #define D_645_FCReadData 0x11 /* 读实时数据 */ #define D_645_FCReadDataResp 0x91 /* 读实时数据正常应答 */ //645协议标准精度与高精度度数据标识 #define DATA_645_ENERGY 0x00000000 //电量(标准) #define DATA_645_VOL 0x00010102 //电压(标准) #define DATA_645_CUR 0x00010202 //电流(标准) #define DATA_645_HIGH_CUR 0x00011102 //电流(扩展) #define DATA_645_HIGH_VOL 0x00011002 //电压(扩展帧) #if (D_YaDa_Flag == 0) #define DATA_645_HIGH_ENERGY 0x00006200 //电量(高精度) #else #define DATA_645_HIGH_ENERGY 0x00006000 //电量(高精度) 使用正向有功电量获取 #endif #define DATE_645_DATA_NUM (6) //645协议标准内容数量 static const Meter_Data_Item st_MeterDataSheet[DATE_645_DATA_NUM] = { //数据标识 字节数 系数 {DATA_645_ENERGY, 4, 100, }, //组合电量 (标准) XXXXXX.XX kWh {DATA_645_VOL, 2, 10, }, //电压 (标准) XXX.X V {DATA_645_CUR, 3, 1000, }, //电流(标准)XXX.XXX A {DATA_645_HIGH_ENERGY, 5, 10000, }, //组合电量(扩展帧)xxxxxx.xxxx kWh {DATA_645_HIGH_VOL, 3, 100, }, //电压(扩展帧) xxxx.xx V {DATA_645_HIGH_CUR, 4, 10000, }, //电流(扩展帧) xxxx.xxxx A }; S_METER_CTRL_DATA_DATA s_memter_ctrl; //电表控制任务初始化 #if SYSTEM_TYPE == SYSTEM_TYPE_CCU601E_D const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_6,E_USART_5}; //双枪对应通信口 #elif SYSTEM_TYPE == SYSTEM_TYPE_CCU621_M const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_3,E_USART_4}; //双枪对应通信口 #else const U8_T u8_commPort[GUN_MAX_CNT] = {E_USART_3,E_USART_4}; //双枪对应通信口 #endif /****************************************************************************系统集成改动部分-开始******************************************************/ #if 1 //接口函数 /** * ************************************************************************ * @brief - 电表数据发送接口函数 发送485数据 * * @param[in] u8_gunNo - 枪号 0-1 函数内部自行匹配串口号 * @param[in] u8_sendBuf - 发送数据地址 * @param[in] u16_len - 发送数据长度 * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static void v_com_meterSend(U8_T u8_gunNo , U8_T *u8_sendBuf , U16_T u16_len) { if(u8_gunNo >= METER_COMM_MAX_CNT) return; u16_usart_send(u8_commPort[u8_gunNo],u8_sendBuf, u16_len); #if D_Print_flag //调试打印 if(u8_gunNo == D_Print_flag - 1) { printf("[%d] GUN meter send,len is [%d]\r\n",u8_gunNo , u16_len); printf_hex("--",u8_sendBuf,u16_len); } #endif } /** * ************************************************************************ * @brief - 电表数据接收接口函数 接收485数据 * * @param[in] u8_gunNo - 枪号 0-1 函数内部自行匹配串口号 * @param[in] u8_recvBuf - 接收数据地址 * @param[in] u16_len - 预期接收数据长度 * * @return U16_T - 已接收数据长度 * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static U16_T u16_com_meterRecv(U8_T u8_gunNo , U8_T *u8_recvBuf ,U16_T u16_len) { U16_T recv_len = 0; if(u8_gunNo >= METER_COMM_MAX_CNT) return 0; recv_len = u16_usart_recv(u8_commPort[u8_gunNo],u8_recvBuf,u16_len); #if D_Print_flag //调试打印 if(u8_gunNo == D_Print_flag - 1) { if(recv_len == 0) return 0; printf("[%d] GUN meter recv,len is [%d]\r\n",u8_gunNo , recv_len); printf_hex("--",u8_recvBuf,recv_len); } #endif return recv_len; } #endif #if 1 //对外数据提供 -- /**2 * ************************************************************************ * @brief - 外部获取电表数据 * * @param[in] u8_gunNo - 抢号 0-1 * @param[in] u8_id - 该枪的电表号 默认0为直流表 1为交流表 * @param[in] u8_type - 数据类型 0电量 1电压 2电流 * * @return U64_T - 返回值 电压、电流、电量 -精度都为0.0001 4位小数 * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ U64_T u64_get_meter_data(U8_T u8_gunNo ,U8_T u8_id , E_METER_DATA_TYPE e_type) { U64_T u64_ret = 0; S_METER_DATA *s_meter_data =NULL; //数据合法性判断 if(u8_gunNo >= GUN_MAX_CNT || u8_id >= METER_COMM_MAX_CNT || e_type >= METER_COMM_TYPE_CNT) return u64_ret; METERCOLL_LOCK_TAKE(); //数据上锁 s_meter_data = &s_memter_ctrl.s_meters[u8_gunNo][u8_id]; //获取电表数据地址 switch (e_type) { case E_METER_DATA_POWER: u64_ret = s_meter_data->u64_energy; break; case E_METER_DATA_VOL: u64_ret = s_meter_data->u32_voltage; break; case E_METER_DATA_CUR: u64_ret = s_meter_data->u32_current; break; default: break; } METERCOLL_LOCK_GIVE(); //数据解锁 return u64_ret; } /** * ************************************************************************ * @brief - 获取电表号 * * @param[in] u8_gunNo - 枪号 0-N * @param[in] u8_id - 该枪的电表号 默认0为直流表 1为交流表 * * @return U8_T - 0未获取 1获取 * * @author jiankalka * @date 2025-11-27 * ************************************************************************ */ U8_T u8_get_meter_id_str(U8_T u8_gunNo ,U8_T u8_id ,U8_T *u8_getData , U8_T u8_len) { if(u8_gunNo >= GUN_MAX_CNT || u8_id >= METER_COMM_MAX_CNT) return 0; memset(u8_getData, 0, u8_len); strncpy((char *)u8_getData , (char *)&s_memter_ctrl.s_meters[u8_gunNo][u8_id].u8_meterNumber[0] , u8_len); return 1; } #endif #if 1 //外部数据获取 /** * ************************************************************************ * @brief - 获取电表地址 * * @param[in] s_memter_data 电表数据 * * * @author jiankalka * @date 2025-08-06 * ************************************************************************ */ static void v_meter_get_para(S_METER_CTRL_DATA_DATA *s_memter_data) { U8_T i, k, cnt; S_MEMET_PARA *s_para = &g_t_share_data.t_sys_fix_cfg.s_memter; U8_T gun_cnt = g_t_share_data.t_sys_fix_cfg.u8_gunCnt; if (s_memter_data == NULL) { return; } if ((gun_cnt == 0U) || (gun_cnt > GUN_MAX_CNT)) { gun_cnt = GUN_MAX_CNT; } s_memter_data->u8_gunCnt = gun_cnt; // 枪数量 for (i = 0; i < s_memter_data->u8_gunCnt; i++) { cnt = 0; for (k = 0; k < s_para->u8_meter_num; k++) { //遍历所有电表 if (s_para->s_data[k].u8_inGun == (U8_T)(i + 1U)) { //参数设置电表 为本枪 则保存数据 if (cnt >= METER_COMM_MAX_CNT) { break; // 单枪电表位已满 } s_memter_data->s_meters[i][cnt].u8_enFlag = 1; //使能标志 0不使能 1使能 用于区分该位置是否存在电表 s_memter_data->s_meters[i][cnt].u8_isAC = s_para->s_data[k].u8_isAC; // 是否为交流电表标识 (1:交流 0:直流) s_memter_data->s_meters[i][cnt].u8_highPrec = s_para->s_data[k].u8_highPrec; //是否支持高精度 0 不支持 1支持 memcpy((char *)&s_memter_data->s_meters[i][cnt].u8_meterNumber[0], (char *)&s_para->s_data[k].u8_meterAddr[0], 6); cnt++; } } } } /** * ************************************************************************ * @brief - 设置电表通信超时状态 用于外部触发故障告警 * * @param[in] u8_gunNo - 枪号 0-1 * @param[in] u8_id - 该枪电表序号 默认0直流表 1交流表 * @param[in] u8_flag - 故障动作 0通信恢复 1 通信超时故障 * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static void v_meter_set_comm_timeout(U8_T u8_gunNo , U8_T u8_id , U8_T u8_flag) { if(u8_flag == 1) { v_set_fault_data((E_FAULT_SITE)(u8_gunNo + E_GUN), E_FAULT, E_FAULT_13071, 0, u8_id, 0); //电表通讯故障 } else v_set_fault_data((E_FAULT_SITE)(u8_gunNo + E_GUN), E_RES, E_FAULT_13071, 0, u8_id, 0); //电表通讯故障 return; } #endif /****************************************************************************系统集成改动部分-结束******************************************************/ /****************************************************************************内部逻辑-开始******************************************************/ #if 1 //发送控制 /************************************************************* 函数名称: u8_645_SendFrame(U8_T u8_frm_type, MeterAddress meteraddr, U32_T datesign, U8_T* u8_sendBuf) 函数功能: 打包组帧发送645协议命令帧 输入参数: u8_frm_type 控制码 meteraddr 电表地址 datesign 数据标识 u8_sendBuf 需要发送的数据 输出参数: **************************************************************/ static void v_645_SendFrame(S_METER_FRAME *s_meterFrame) //组帧发送 { U8_T u8_cnt = 0; U8_T CheckSum; U8_T u8_sendBuf[NUM_64] = {0}; u8_sendBuf[u8_cnt++] = D_FrameStart; //帧起始域 memcpy(&u8_sendBuf[u8_cnt], &s_meterFrame->address[0], 6); u8_cnt += 6; u8_sendBuf[u8_cnt++] = D_FrameStart; //帧起始域 u8_sendBuf[u8_cnt++] = s_meterFrame->control_code; //控制码 0x11 读数据 u8_sendBuf[u8_cnt++] = 0x04; //数据长度 // 数据标识(拆分为高字节和低字节) u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 24) & 0xFF) + 0x33; u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 16) & 0xFF) + 0x33; u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign >> 8) & 0xFF) + 0x33; u8_sendBuf[u8_cnt++] = ((s_meterFrame->u32_datesign) & 0xFF) + 0x33; /* 打包校验和与帧尾 */ CheckSum = u16_crc_checksum(u8_sendBuf, u8_cnt); u8_sendBuf[u8_cnt++] = CheckSum; u8_sendBuf[u8_cnt++] = D_FrameEnd; v_com_meterSend(s_meterFrame->u8_gunNo,u8_sendBuf, u8_cnt); } /** * ************************************************************************ * @brief - 电表数据采集发送组帧函数 * * @param[in] u8_gunNo - 枪地址索引号 0-1 * @param[in] u8_type - 0 电量 1电压 2电流 * @param[in] s_meter_data - 电表数据 * * * @author jiankalka * @date 2025-08-06 * ************************************************************************ */ static void v_memter_SendFrame(U8_T u8_gunNo, U8_T u8_type , S_METER_DATA *s_meter_data) { S_METER_FRAME s_meterFrame; memset(&s_meterFrame , 0, sizeof(S_METER_FRAME)); s_meterFrame.u8_gunNo = u8_gunNo; s_meterFrame.control_code = D_645_FCReadData; //控制码 0x11 读数据 memcpy(&s_meterFrame.address[0], s_meter_data->u8_meterNumber, 6); if(u8_type % 3 == 0){ //电量 if(s_meter_data->u8_highPrec == 1) //高精度 s_meterFrame.u32_datesign = DATA_645_HIGH_ENERGY; else s_meterFrame.u32_datesign = DATA_645_ENERGY; } else if(u8_type % 3 == 1){ //电压 if(s_meter_data->u8_highPrec == 1) //高精度 s_meterFrame.u32_datesign = DATA_645_HIGH_VOL; else s_meterFrame.u32_datesign = DATA_645_VOL; } else if(u8_type % 3 == 2){ //电流 if(s_meter_data->u8_highPrec == 1) //高精度 s_meterFrame.u32_datesign = DATA_645_HIGH_CUR; else s_meterFrame.u32_datesign = DATA_645_CUR; } //printf("send ctrl , gun(%d) id(0x%08x) \n",s_meterFrame.u8_gunNo ,s_meterFrame.u32_datesign ); v_645_SendFrame(&s_meterFrame); } /** * ************************************************************************ * @brief - 电表数据采集发送控制函数 用于对两路电表分别进行电压、电流、电量的数据采集发送-- * * @param[in] u16_sleepTime - 任务睡眠时间 * * * @author jiankalka * @date 2025-08-06 * ************************************************************************ */ static void v_meter_send_ctrl(U16_T u16_sleepTime) { U8_T i; S_METER_DATA *s_meter_data =NULL; S_METER_COMM_CTRL *s_comm_ctrl = NULL; for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量 { s_comm_ctrl = &s_memter_ctrl.s_commCtrl[i]; //获取通信控制内容 s_meter_data = &s_memter_ctrl.s_meters[i][s_comm_ctrl->u8_send_type/METER_COMM_TYPE_CNT]; //获取该枪电表首地址 当send type 大于3时,则为2枪数据 if(s_meter_data->u8_enFlag == 0) //不存在电表 则不执行; 当电表2不存在时,send type从0开始执行 { s_comm_ctrl->u8_send_type = 0; continue; } if(s_comm_ctrl->u8_recv_flag == 0) //未存在 等待接收数据 { v_memter_SendFrame(i,s_comm_ctrl->u8_send_type, s_meter_data); //控制数据发送 if(++s_comm_ctrl->u8_send_type >= METER_COMM_TYPE_CNT*2) //发送数据类型 递增 { s_comm_ctrl->u8_send_type = 0; } s_comm_ctrl->u8_recv_flag = 1; //发送后等待数据接收 s_comm_ctrl->u16_recv_timeout_cnt = 0; //超时计数清空 } else //等待接收状态 判断超时 { s_comm_ctrl->u16_recv_timeout_cnt += u16_sleepTime; if(s_comm_ctrl->u16_recv_timeout_cnt >= METER_RECV_TIMEOUT_MS) //数据接收超时时间 600ms ,则清空标识 重新进行发送 { s_comm_ctrl->u8_recv_flag = 0; s_comm_ctrl->u16_recv_timeout_cnt = 0; //超时计数清空 } } } } #endif #if 1 //接收解析 /** * ************************************************************************ * @brief - 电表数据内容解析 并存储; 查找接收数据对应的枪号、表号、数据内容等 * * @param[in] s_meterFrame - 接收报文解析后的数据包-默认可用数据为正确数据 * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static void v_meter_unpack(S_METER_FRAME *s_meterFrame) { U8_T i,id; U64_T u64_unpackData = 0,u64_recvData=0; S_METER_DATA *s_meter = NULL; //printf_hex("解析成功////data",s_meterFrame->data,s_meterFrame->data_len); //前4个字节 转换成 数据标识 s_meterFrame->u32_datesign = 0; for (i = 0; i < NUM_4; i++) { s_meterFrame->u32_datesign += ((s_meterFrame->data[3-i]- 0x33) << i*8); } //遍历所有可解析数据标识 查询对应标识 for (id = 0; id < DATE_645_DATA_NUM; id++) { if(s_meterFrame->u32_datesign == st_MeterDataSheet[id].iFlagCode) break; } //查找失败,则退出 if(id >= DATE_645_DATA_NUM){ // printf("find fail , 0x%08x\n",s_meterFrame->u32_datesign); return; } //长度一致性检查 if(st_MeterDataSheet[id].iLenth != s_meterFrame->data_len-4){ // printf("len fail , 0x%08x\n",s_meterFrame->u32_datesign); return; } //将报文数据 -0x33 BCD转int 组成整形数据 for (i = 0; i < st_MeterDataSheet[id].iLenth; i++) { U8_T u8_data = (s_meterFrame->data[i+4]- 0x33); u64_recvData = (u64_recvData << 8); u64_recvData += u8_data; } //将BCD码 转换成 十进制 -- u64 8字节 for (i = 0; i < st_MeterDataSheet[id].iLenth; i++) { // 提取当前BCD数字(4位) U8_T u8_data = (u64_recvData >> (8 * i))&0xFF; U8_T digit = bcd_to_dec(u8_data); // 累积结果(每次乘以10并加上新数字) u64_unpackData = u64_unpackData * 100 + digit; } //电表号赋值 -- 一路单电表时,可以不设置电表号 --当一路多电表使用时,电表号为已经设置好-因此此处仅对0地址电表进行赋值 s_meter = &s_memter_ctrl.s_meters[s_meterFrame->u8_gunNo][0]; if(s_meter->u8_meterNumber[0] == 0xAA && s_meter->u8_meterNumber[1] == 0xAA){ memcpy(&s_meter->u8_meterNumber[0] , &s_meterFrame->address[0] , 6); //凯弦欣能电表特殊处理 //打印 if(s_meterFrame->u8_gunNo == 1) { if(s_meter->u8_meterNumber[0] == 0x01 && s_meter->u8_meterNumber[1] == 0x00 && s_meter->u8_meterNumber[2] == 0x00 && s_meter->u8_meterNumber[3] == 0x00 && s_meter->u8_meterNumber[4] == 0x00 && s_meter->u8_meterNumber[5] == 0x00 ) { s_meter->u8_meterNumber[0] = 2; } } } //printf("u64_unpackData is %lld\n", u64_unpackData); METERCOLL_LOCK_TAKE(); // 数据上锁 //查找电表号一致的电表 - 赋值对应内容 for (i = 0; i < METER_COMM_MAX_CNT; i++) { s_meter = &s_memter_ctrl.s_meters[s_meterFrame->u8_gunNo][i]; //if(strncmp((char *)s_meter->u8_meterNumber , (char *)s_meterFrame->address , 6) == 0) //电表号相等 if (memcmp(s_meter->u8_meterNumber, s_meterFrame->address, 6) == 0) { // printf("find ok , gun(%d) id(0x%08x) data(%lld)\n",s_meterFrame->u8_gunNo ,s_meterFrame->u32_datesign , u64_unpackData); //存储电压、电流、电量值的精度 都是0.0001 四位小数,因此根据数据系数做统一转换 switch (st_MeterDataSheet[id].iFlagCode) { case DATA_645_ENERGY : //0x00000000 //电量(标准) XXXXXX.XX kWh case DATA_645_HIGH_ENERGY : //0x00006200 //电量(高精度) xxxxxx.xxxx kWh { if(st_MeterDataSheet[id].iCoefficient != 0) s_meter->u64_energy = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电量值(单位:0.0001 kWh) s_meter->u16_timeout_cnt[0] = 0; break; } case DATA_645_VOL : //0x00010102 //电压(标准) XXX.X V case DATA_645_HIGH_VOL : //0x00011002 //电压(扩展帧 xxxx.xx V { if(st_MeterDataSheet[id].iCoefficient != 0) s_meter->u32_voltage = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电压值(单位:0.0001V) s_meter->u16_timeout_cnt[1] = 0; break; } case DATA_645_CUR : //0x00010202 //电流(标准) XXX.XXX A case DATA_645_HIGH_CUR : //0x00011102 //电流(扩展) xxxx.xxxx A { if(st_MeterDataSheet[id].iCoefficient != 0) s_meter->u32_current = u64_unpackData *(10000 / st_MeterDataSheet[id].iCoefficient) ; //电流值(单位:0.0001 A) s_meter->u16_timeout_cnt[2] = 0; break; } default: break; } break; } } METERCOLL_LOCK_GIVE(); //数据解锁 //接收标识清空 s_memter_ctrl.s_commCtrl[s_meterFrame->u8_gunNo].u8_recv_flag = 0; //数据接收标识 已清空 // if(s_meterFrame->u8_gunNo == 1) // printf("unpack is ok , gun(%d) id(0x%08x) data(%lld)\n",s_meterFrame->u8_gunNo ,s_meterFrame->u32_datesign , u64_unpackData); } /** * ************************************************************************ * @brief - 接收电表数据解帧函数 - 内部包括多包、残包等异常数据内容的处理方法 * * @param[in] new_data - 新接收数据内容 * @param[in] new_len - 新接收数据内容长度 * @param[in] s_frame - 电表数据帧结构体指针 用于存储解析后的电表数据 * * @return U8_T - 0解析失败 1解析成功 * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static U8_T u8_meter_recvFrame(const U8_T *new_data, U8_T new_len,S_METER_FRAME *s_frame) { S_METER_COMM_CTRL *s_ctrl = &s_memter_ctrl.s_commCtrl[s_frame->u8_gunNo]; // 1. 检查输入有效性 if (!new_data || new_len <= 0 || !s_frame) { return 0; } // 2. 添加新数据到缓冲区 int free_space = MAX_BUFFER_SIZE - s_ctrl->u8_buffer_len; int copy_len = (new_len > free_space) ? free_space : new_len; if (copy_len > 0) { memcpy(s_ctrl->u8_buffer + s_ctrl->u8_buffer_len, new_data, copy_len); s_ctrl->u8_buffer_len += copy_len; } // 3. 查找有效帧(最小帧长为12字节) if (s_ctrl->u8_buffer_len < 12) { return 0; // 数据不足 } // 4. 尝试查找帧起始位置 int frame_start = -1; int frame_end = -1; for (int i = 0; i <= s_ctrl->u8_buffer_len - 12; i++) { // 检查帧起始标识(跳过前导符) int start_idx = i; if (s_ctrl->u8_buffer[start_idx] != D_FrameStart) continue; // 检查地址后的帧起始符(位于start_idx+7) if (start_idx + 7 >= s_ctrl->u8_buffer_len) continue; if (s_ctrl->u8_buffer[start_idx + 7] != D_FrameStart) continue; // 检查数据长度域(位于start_idx+9) if (start_idx + 9 >= s_ctrl->u8_buffer_len) continue; // 检查帧完整长度 frame_end = start_idx + 11 + s_ctrl->u8_buffer[start_idx + 9]; // 包含结束符的位置 if (frame_end >= s_ctrl->u8_buffer_len) continue; // 检查帧结束符(0x16) if (s_ctrl->u8_buffer[frame_end] != D_FrameEnd) continue; frame_start = start_idx; break; } // 5. 没有找到完整帧 if (frame_start < 0) { // 缓冲区已满但无有效帧,清除部分数据 if (s_ctrl->u8_buffer_len >= MAX_BUFFER_SIZE / 2) { // 保留最后1/4的数据 int keep_len = MAX_BUFFER_SIZE / 4; if (s_ctrl->u8_buffer_len > keep_len) { memmove(s_ctrl->u8_buffer, s_ctrl->u8_buffer + (s_ctrl->u8_buffer_len - keep_len), keep_len); s_ctrl->u8_buffer_len = keep_len; } } return 0; } // 6. 计算校验和 uint8_t calc_checksum = u16_crc_checksum(&s_ctrl->u8_buffer[frame_start] , frame_end - frame_start - 1); if (calc_checksum != s_ctrl->u8_buffer[frame_end - 1]) { return 0; } // 8. 解包帧数据 // 地址域(小端格式) memcpy(s_frame->address, s_ctrl->u8_buffer + frame_start + 1, 6); // 控制码 s_frame->control_code = s_ctrl->u8_buffer[frame_start + 8]; // 数据域长度和内容 s_frame->data_len = s_ctrl->u8_buffer[frame_start + 9]; s_frame->data_len = (s_frame->data_len > 0xFF) ? 0xFF : s_frame->data_len; // 安全限制 if (s_frame->data_len > 0) { memcpy(s_frame->data, s_ctrl->u8_buffer + frame_start + 10, s_frame->data_len); } // // 计算完整帧长度(包括前导符) // s_frame->data_len = (frame_end - frame_start + 1); // 9. 移除已处理数据 int process_len = (frame_end + 1); s_ctrl->u8_buffer_len -= process_len; if (s_ctrl->u8_buffer_len > 0) { memmove(s_ctrl->u8_buffer, s_ctrl->u8_buffer + process_len, s_ctrl->u8_buffer_len); } else memset(s_ctrl->u8_buffer, 0 , MAX_BUFFER_SIZE); return s_frame->data_len; // 返回处理的字节数 } /** * ************************************************************************ * @brief - 电表数据接收函数 遍历接收两个枪号的电表数据 * * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static void v_memter_recv_ctrl() { U8_T i,u8_readLen,u8_recvBuf[NUM_128]; S_METER_FRAME s_meterFrame; for (i = 0; i < METER_COMM_MAX_CNT; i++) { memset(u8_recvBuf , 0 , NUM_128); u8_readLen = u16_com_meterRecv(i,u8_recvBuf , NUM_128); if(u8_readLen > 0) { memset(&s_meterFrame , 0, sizeof(S_METER_FRAME)); s_meterFrame.u8_gunNo = i; if(u8_meter_recvFrame(u8_recvBuf , u8_readLen, &s_meterFrame ) != 0) { //printf("gun[%d] data_len is %d\n", i, s_meterFrame.data_len); if(s_meterFrame.data_len >= 6) //数据有效 否则可能为错误数据 最小长度为7 { v_meter_unpack(&s_meterFrame); } } } } } #endif #if 1 //通信超时 /** * ************************************************************************ * @brief - 通信超时检查函数 超时计数累加,接收数据时会清空计数,由此判断接受超时 * * @param[in] u16_sleepTime - 任务执行周期 ms * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static void v_meter_timeout_cheak(U16_T u16_sleepTime) { U8_T i,k; U16_T u16_max_timeout_cnt; S_METER_DATA *s_meter_data =NULL; for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量 { for (k = 0; k < METER_COMM_MAX_CNT; k++) //遍历所有电表位置 { s_meter_data = &s_memter_ctrl.s_meters[i][k]; //获取该枪电表首地址 if(s_meter_data->u8_enFlag == 0) //电表不存在 则不判断超时 continue; if (s_meter_data->u16_timeout_cnt[0] >= s_meter_data->u16_timeout_cnt[1] && s_meter_data->u16_timeout_cnt[0] >= s_meter_data->u16_timeout_cnt[2]) { u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[0]; } else if (s_meter_data->u16_timeout_cnt[1] >= s_meter_data->u16_timeout_cnt[0] && s_meter_data->u16_timeout_cnt[1] >= s_meter_data->u16_timeout_cnt[2]) { u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[1]; } else { u16_max_timeout_cnt = s_meter_data->u16_timeout_cnt[2]; } if(s_meter_data->u8_comm_flag == 0) //未通信 , 判通信恢复 { if(u16_max_timeout_cnt < METER_TIMEOUT_CNT) { s_meter_data->u8_comm_flag = 1; v_meter_set_comm_timeout(i,k,0); //触发电表通信恢复 METERCOLL_LOG("gun[%d] id[%d], comm is ok\n", i,k); // printf("gun[%d] id[%d], comm is ok\n", i,k); } } else { if(u16_max_timeout_cnt < METER_TIMEOUT_CNT) { s_meter_data->u16_timeout_cnt[0] += u16_sleepTime; s_meter_data->u16_timeout_cnt[1] += u16_sleepTime; s_meter_data->u16_timeout_cnt[2] += u16_sleepTime; } else { s_meter_data->u8_comm_flag = 0; //memset(s_meter_data, 0 ,sizeof(S_METER_DATA)); //不能全部清空 仅情况采集值即可 s_meter_data->u32_voltage =0; s_meter_data->u32_current =0; s_meter_data->u64_energy =0; v_meter_set_comm_timeout(i,k,1); //触发电表通信超时故障 METERCOLL_LOG("gun[%d] id[%d], comm is fail\n", i,k); // printf("gun[%d] id[%d], comm is fail\n", i,k); } } } } } #endif /** * ************************************************************************ * @brief - 电表数据初始化 初始化数据,并获取外部数据 * * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ static void v_memter_data_init() { U8_T i,k; S_METER_DATA *s_meter_data =NULL; memset(&s_memter_ctrl , 0 ,sizeof(S_METER_CTRL_DATA_DATA)); v_meter_get_para(&s_memter_ctrl); for (i = 0; i < s_memter_ctrl.u8_gunCnt; i++) //遍历所有枪数量 { for (k = 0; k < METER_COMM_MAX_CNT; k++) //遍历所有电表位置 { s_meter_data = &s_memter_ctrl.s_meters[i][k]; //获取该枪电表首地址 if(s_meter_data->u8_enFlag == 0) //电表不存在 则不判断超时 continue; s_meter_data->u8_comm_flag = 1; s_meter_data->u16_timeout_cnt[0] = 0;//0xFFFF; //电表默认通信超时 s_meter_data->u16_timeout_cnt[1] = 0; s_meter_data->u16_timeout_cnt[2] = 0; } } } /** * ************************************************************************ * @brief - 电表数据采集函数 - 循环执行数据发生、数据接收、超时判断函数 * * @param[in] p - * * * @author jiankalka * @date 2025-08-07 * ************************************************************************ */ void v_Meter_Coll_task(void *argument) { uint16_t sleep_time_ms; uint32_t last_send_time; uint32_t current_time; (void)argument; (void)METERCOLL_LOCK_INIT(); sleep_time_ms = METERCOLL_SLEEP_TIME_MS(); last_send_time = get_current_seconds(); v_memter_data_init(); for (;;) { METERCOLL_FEED_DOG(); current_time = get_current_seconds(); if ((TASK_RUN_PRINT_TIME != 0U) && (u32_safe_seconds_since(last_send_time) >= TASK_RUN_PRINT_TIME)) { if (METERCOLL_LOCK_TAKE() == 1U) { METERCOLL_LOG("meter coll task is runing, sleep time is %ums", sleep_time_ms); (void)METERCOLL_LOCK_GIVE(); } last_send_time = current_time; } v_memter_recv_ctrl(); //电表数据接收 v_meter_send_ctrl(sleep_time_ms); //电表数据发生控制 v_meter_timeout_cheak(sleep_time_ms); //电表通信超时检查 mSleep(sleep_time_ms); } }