Files

854 lines
32 KiB
C
Raw Permalink 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.
#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);
}
}