#include "plat_comm/ieszt/bs_ies_ctrl.h" #include "plat_comm/ieszt/bs_ies_interface.h" #include "plat_comm/impl/bs_connect_impl.h" #include "plat_comm/impl/bs_public_impl.h" #if WH_GM5_EN #include "plat_comm/4g_module/bs_wh_gm5.h" #endif #if (BS_IES_EN) #define BS_TIME_1S 10 //1秒 发送周期1秒计数 S_BS_IES_CTRL_DATA *private_ies_data; #endif #if (BS_IES_EN) //内部控制函数 void v_ies_send(U8_T *u8_sendBuf, U32_T sendLen) { u8_sendBuf[sendLen] = u16_crc_checksum(&u8_sendBuf[3], sendLen-3); //和校验 sendLen ++; //更新帧长度域 u8_sendBuf[1] = (sendLen-4)>>8; u8_sendBuf[2] = sendLen-4; v_bs_send(private_ies_data->id,u8_sendBuf,sendLen); } //发送周期计数累加 1秒加一 static void v_ies_timer_add() { U8_T i; static ULONG time_old = 0; ULONG time_ing = get_current_seconds(); //获取当前秒数 if(time_old != time_ing) { time_old = time_ing; for (i = 0; i < IES_TIMERCNT_NUM; i++) { if(private_ies_data->s_timerCnt[i].u16_timerCnt < private_ies_data->s_timerCnt[i].u16_timeNUM) { private_ies_data->s_timerCnt[i].u16_timerCnt ++; } } } } //获取发送周期计数到时标志 static U8_T u8_ies_timer_ctrl(IES_SEND_TIMER cmd ,FLAG_CTLR flag) { U8_T ret = 0; if(flag == CLEAR_FALG) //清空对应计时计数 { private_ies_data->s_timerCnt[(U8_T)cmd].u16_timerCnt = 0; ret = 1; } else if(private_ies_data->s_timerCnt[(U8_T)cmd].u16_timeNUM != 0 && private_ies_data->s_timerCnt[(U8_T)cmd].u16_timerCnt >= private_ies_data->s_timerCnt[(U8_T)cmd].u16_timeNUM) { private_ies_data->s_timerCnt[(U8_T)cmd].u16_timerCnt = 0; ret = 1; } return ret; } #endif #if (BS_IES_EN) //发送相关 /** * ************************************************************************ * @brief - 故障信息变化检查 * * * @return U8_T - 0未变化 1变化 * * @author jiankalka * @date 2025-09-20 * ************************************************************************ */ static U8_T u8_cheak_fault() { static U8_T u8_old_fault_num=0; U8_T u8_fault_num,ret = 0; u8_fault_num = u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NOW_FAULT_ALARM_NUM,0) + u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NOW_FAULT_ALARM_NUM,1); //获取故障 告警数量 if(u8_fault_num != u8_old_fault_num) { u8_old_fault_num = u8_fault_num; ret = 1; } return ret; } /** * ************************************************************************ * @brief - 抢连接变化检查 * * @param[in] u8_gunNo - 抢号 0-N * * @return U8_T - 0未变化 1已经变化 * * @author jiankalka * @date 2025-09-20 * ************************************************************************ */ static U8_T u8_cheak_gunlink(U8_T u8_gunNo) { static U8_T u8_old_gunLink[GUN_MAX_CNT] = {0}; U8_T u8_gunLink = u8_ies_get_gunLink_state(u8_gunNo); //获取枪连接状态 0未连接 1已连接 //u8_gunLink = (u8_gunLink == 0 ? 0 : 1); if(u8_gunLink != u8_old_gunLink[u8_gunNo]) { //printf("%d枪变位,原状态:%d,现状态:%d\n",u8_gunNo,u8_old_gunLink[u8_gunNo],u8_gunLink); u8_old_gunLink[u8_gunNo] = u8_gunLink; return 1; } return 0; } /** * ************************************************************************ * @brief - 抢状态变化检查 * * @param[in] u8_gunNo - 抢号 0-N * * @return U8_T - 0未变化 1已经变化 * * @author jiankalka * @date 2025-09-20 * ************************************************************************ */ static U8_T u8_cheak_gunState(U8_T u8_gunNo) { static U8_T u8_old_gunState[GUN_MAX_CNT] = {0}; U8_T u8_gunState = u8_ies_get_gun_state(u8_gunNo); //获取枪状态 0未连接 1已连接 //u8_gunLink = (u8_gunLink == 0 ? 0 : 1); if(u8_gunState != u8_old_gunState[u8_gunNo]) { //printf("%d枪变位,原状态:%d,现状态:%d\n",u8_gunNo,u8_old_gunState[u8_gunNo],u8_gunState); u8_old_gunState[u8_gunNo] = u8_gunState; return 1; } return 0; } //发送控制 static void v_ies_send_mag_ctrl() { S_BS_CTRL_DATA *s_bs_ctrl = &s_bs_task_ctrl.s_bs_ctrl[private_ies_data->id]; //获取控制数据 U8_T i,u8_data; //临时变量 使用前注意赋值 U8_T u8_gunCnt = s_bs_task_ctrl.u8_gunCnt; U8_T u8_get_flow_state[GUN_MAX_CNT]; static U8_T fee_flag = 0; //连接平台后直接请求一次计费模型标志 //检查心跳周期是否到位 if(u8_ies_timer_ctrl(IES_TIMER_Heartbeat,GET_FLAG) == 1) { v_ies_heart(); return; } //电表底值上送 for(i=0;is_ctrl_flag[i].u8_charge_req == 1) { v_ies_start_req(i); return; } } //请求费率 if(fee_flag == 0 ||(private_ies_data->txFlag_askFeeMode == 0 && u8_ies_timer_ctrl(IES_TIMER_FEE_REQ,GET_FLAG) == 1)) { v_ies_fee_req(); fee_flag = 1; return; } //订单上送 for(i=0; is_gundata[i].txFlag_tradUpload == 1) { if( s_bs_ctrl->s_gundata[i].u8_tradUpload_cnt < 6) //充电中 只发6次 u8_data = 1; } else s_bs_ctrl->s_gundata[i].u8_tradUpload_cnt = 0; if(u8_data == 1 && u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_TRADE_INFO + i), GET_FLAG) == 1) { v_ies_bill_pack(i); s_bs_ctrl->s_gundata[i].u8_tradUpload_cnt ++; return; } } //故障状态上送-有故障变化时上送,无故障变化时,3分钟上送一次 u8_data = u8_cheak_fault(); if(u8_data == 1 || u8_ies_timer_ctrl(IES_TIMER_FAULT_GUN ,GET_FLAG) == 1) { u8_ies_timer_ctrl(IES_TIMER_FAULT_GUN ,CLEAR_FALG); //清空计数 v_ies_fault_V35(i); return; } //充电/空闲实时信息上报 for(i=0; is_ies_gun[i].u8_real_state_cnt &= 0x0F; if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 3) { private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = 10; //前3帧 10秒周期上送 } else { private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = private_ies_data->u16_work_info_period; //上送周期等于充电数据上送周期设置值 } if(u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_REAL_WORK_GUN + i), GET_FLAG) == 1) { // printf("%d枪,充电中,u8_real_state_cnt=%02x\n",i,private_ies_data->s_ies_gun[i].u8_real_state_cnt); v_ies_rtd_chging(i); if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 3) { private_ies_data->s_ies_gun[i].u8_real_state_cnt ++; } return; } } else { //当状态由变化时 前3帧按10秒发送--u8_real_state_cnt 0x00-0x0F 充电中计数 0x10-0x1F 非充电中计数 private_ies_data->s_ies_gun[i].u8_real_state_cnt &= 0xF0; //清除低位计数值 if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 0x30) { private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = 3; //前3帧 3秒周期上送 } else { private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = private_ies_data->u16_free_info_period; //上送周期等于充电数据上送周期设置值 } if(u8_ies_timer_ctrl((IES_SEND_TIMER)(IES_TIMER_REAL_WORK_GUN + i), GET_FLAG) == 1 //|| u8_cheak_gunlink(i) || u8_cheak_gunState(i)) //延时到时间上送、枪状态改变上送 { //printf("%d枪,非充电中,u8_real_state_cnt=%02x\n",i,private_ies_data->s_ies_gun[i].u8_real_state_cnt); v_ies_rtd_free(i); if(private_ies_data->s_ies_gun[i].u8_real_state_cnt < 0x30) { private_ies_data->s_ies_gun[i].u8_real_state_cnt += 0x10; } return; } } } } #endif #if (BS_IES_EN) //接收 static void v_bs_rx_cmd_hanle(U8_T *u8_recvBuf) //具体报文解帧 { U8_T u8_cmd = u8_recvBuf[22]; switch(u8_cmd) { case NT_LOGIN_ACK: //0x02 //登录注册应答 { if(private_ies_data->u8_register_flag == 0) { v_ies_register_ack(u8_recvBuf); } break; } case NT_TICK_ACK: //0x04 //心跳应答 { v_ies_heart_ack(); break; } case NT_FEE_MODULE_ACK: //0x06 //计费模型应答 { v_ies_fee_ack(u8_recvBuf); break; } case NT_MDU_DATA_ACK: //0x17 //充电桩整流模块数据应答 { break; } case NT_START_CHG_ACK: //0x42 //平台确认桩主动申请启动充电 { v_ies_rx_start(u8_recvBuf); break; } case NT_REMOTE_START_REQ: //0x44 //平台远程控制启机 { if(private_ies_data->txFlag_askFeeMode == 1) //未收到计费模型,不启动充电 { Plat_Comm_LOG("private_ies_data->txFlag_askFeeMode=%d\n",private_ies_data->txFlag_askFeeMode); v_ies_rmt_start(u8_recvBuf); } break; } case NT_REMOTE_STOP_REQ: //0x46 //平台远程停机 { v_ies_rmt_stop(u8_recvBuf); break; } case NT_BILL_UPLOAD_ACK: //0x48 //平台结算账单确认 { v_ies_bill_upload_ack(u8_recvBuf); break; } case NT_USERACNTINFO_ACK: //0x4A //平台回复查询用户账单信息(未用) { //v_bs_user_acntInfo_ack(u8_recvBuf); break; } case NT_CARD_BILL_UPLOAD_ACK: //0x4C //平台刷卡结算订单应答 { //v_bs_bill_card_ack(u8_recvBuf); break; } case NT_WORK_PARA_CFG_REQ: //0x72 //平台设置工作参数 { //v_bs_set_wkPara(u8_recvBuf); break; } case NT_WORK_PARA_QUERY_REQ: //0x74 //平台查询工作参数 { //v_bs_query_wkPara(u8_recvBuf); break; } case NT_CLK_SYNC_REQ: //0x76 //平台对时 { v_ies_set_clk_sync(u8_recvBuf); break; } case NT_REMOTE_OPRT_REQ: //0x78 //平台远程操作 { //v_bs_rmt_oprt(u8_recvBuf); break; } case NT_FEE_CFG_REQ: //0x7D //平台设置计费模型 { v_ies_set_fee(u8_recvBuf); break; } case NT_UPDATE_AUTO: //0x90 //平台下发FTP升级命令 { //v_ies_upgrade_data_auto(u8_recvBuf); break; } case NT_QR_REQ_CALL: //0x0A 平台召唤二维码 { v_ies_qr_call(u8_recvBuf); break; } case NT_QR_REQ_DIS: //0x08 平台下发二维码 { Plat_Comm_LOG("Receive platform QR code command\n"); // printf("收到平台下发二维码命令\n"); // for(int i=0;i < 100;i++) // { // printf("%.2x ",u8_recvBuf[i]); // } // printf("\n"); v_ies_qr_dis(u8_recvBuf); break; } default: break; } } // static void v_bs_unpack_handle_ieszt(U8_T *u8_recvBuf, U16_T u16_rcvLen) //解帧函数 // { // U8_T u8_crc; // U16_T u16_rcvByteNum = 0; //解粘包指针 // U16_T u16_oneFrmLen = 0; //一完整帧理论总长度 // U16_T u16_dataLen = 0; //数据域长度 // U8_T upackBuf[BS_RECV_BUF_LEN] = {0}; //待解析帧缓存 // while(u16_rcvLen >= 23) //帧头长度=23 // { // if(u8_recvBuf[u16_rcvByteNum] != NT_HEADER) //找帧头 // { // u16_rcvByteNum++; // u16_rcvLen--; // continue; // } // u16_dataLen = (U16_T)((u8_recvBuf[u16_rcvByteNum+1]<<8) + u8_recvBuf[u16_rcvByteNum+2]); // if(u16_dataLen >= 256) // return; // u16_oneFrmLen = u16_dataLen + 4; // if(u16_oneFrmLen <= u16_rcvLen) //构成完整帧 // { // memcpy(&upackBuf[0], &u8_recvBuf[u16_rcvByteNum], u16_oneFrmLen); // //和校验 // u8_crc = u16_crc_checksum(&upackBuf[3], u16_oneFrmLen-4); // if(u8_crc == upackBuf[u16_oneFrmLen-1]) // { // v_bs_rx_cmd_hanle(upackBuf); //和校验通过,解析数据 // } // u16_rcvByteNum += u16_oneFrmLen; // u16_rcvLen -= u16_oneFrmLen; // } // else // return; // } // } static void v_bs_unpack_handle_ieszt(U8_T *u8_recvBuf, U16_T u16_rcvLen) //解帧函数 { U8_T u8_crc; U16_T u16_rcvByteNum = 0; //解粘包指针 U16_T u16_oneFrmLen = 0; //一完整帧理论总长度 U16_T u16_dataLen = 0; //数据域长度 U8_T upackBuf[BS_RECV_BUF_LEN] = {0}; //待解析帧缓存 static U8_T u8_save_buf[BS_RECV_BUF_LEN*2] = {0}; static U16_T u16_save_len = 0; // 检查缓冲区是否溢出 if(u16_rcvLen + u16_save_len >= BS_RECV_BUF_LEN*2) { // 移除前四分之一数据以腾出空间 U16_T remove_len = u16_save_len / 4; // 计算要移除的长度 U16_T remain_len = u16_save_len - remove_len; // 剩余数据长度 if(remain_len > 0) { // 将后面的数据向前移动,覆盖前四分之一 memmove(u8_save_buf, u8_save_buf + remove_len, remain_len); } u16_save_len = remain_len; // 更新保存的数据长度 } // 将接收到的数据添加到缓存区中 if(u16_rcvLen > 0) { memcpy(&u8_save_buf[u16_save_len], u8_recvBuf, u16_rcvLen); u16_save_len += u16_rcvLen; } // 使用静态缓冲区数据进行解析 U16_T process_len = u16_save_len; // 当前要处理的数据长度 u16_rcvByteNum = 0; // 重置解析指针 while(process_len >= 23) //帧头长度=23 { if(u8_save_buf[u16_rcvByteNum] != NT_HEADER) //找帧头 { u16_rcvByteNum++; process_len--; continue; } u16_dataLen = (U16_T)((u8_save_buf[u16_rcvByteNum+1]<<8) + u8_save_buf[u16_rcvByteNum+2]); if(u16_dataLen >= 256) { u16_rcvByteNum++; process_len--; continue; // 数据长度异常,跳过这个帧头 } u16_oneFrmLen = u16_dataLen + 4; if(u16_oneFrmLen <= process_len) //构成完整帧 { memcpy(&upackBuf[0], &u8_save_buf[u16_rcvByteNum], u16_oneFrmLen); //和校验 u8_crc = u16_crc_checksum(&upackBuf[3], u16_oneFrmLen-4); if(u8_crc == upackBuf[u16_oneFrmLen-1]) { v_bs_rx_cmd_hanle(upackBuf); //和校验通过,解析数据 } u16_rcvByteNum += u16_oneFrmLen; process_len -= u16_oneFrmLen; } else break; // 数据不完整,等待下次接收更多数据 } // 将处理完的数据从静态缓冲区中移除 if(u16_rcvByteNum > 0) // 有处理完的数据需要移除 { // 计算剩余未处理数据的长度 U16_T remaining_len = u16_save_len - u16_rcvByteNum; if(remaining_len > 0) { // 将未处理的数据向前移动,覆盖已处理的数据 memmove(u8_save_buf, u8_save_buf + u16_rcvByteNum, remaining_len); } // 更新静态缓冲区的数据长度 u16_save_len = remaining_len; // 清空移动后空出的部分(可选,为了调试清晰) if(remaining_len < BS_RECV_BUF_LEN*2) { memset(u8_save_buf + remaining_len, 0, BS_RECV_BUF_LEN*2 - remaining_len); } } } #endif #if (BS_IES_EN) //外部控制使用 /**************************************************** *函数名称: v_ies_init_data() *功能说明: 程序运行前 参数初始化 *参数说明: * 输入参数: * 输出参数: 0失败 1成功 ****************************************************/ void v_ies_init_data(U8_T bs_flag) { U8_T i; private_ies_data = &s_bs_task_ctrl.s_ies_data; //指针数据初始化 memset(private_ies_data,0,sizeof(S_BS_IES_CTRL_DATA)); private_ies_data->id = bs_flag; //平台位置 // if (bs_flag == 0U) { // s_bs_task_ctrl.s_bs_ctrl[bs_flag].u8_netType = // (u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NET_TYPE,bs_flag) == 1U) ? 0U : 1U; // } else { // s_bs_task_ctrl.s_bs_ctrl[bs_flag].u8_netType = // (u32_bs_get_sys_data(0,E_BS_GET_SYS_DATA_NET_TYPE,bs_flag) == 1U) ? 0U : 1U; // } s_bs_task_ctrl.s_bs_ctrl[bs_flag].fd = -1; s_bs_task_ctrl.s_bs_ctrl[bs_flag].ulong_restart_timer = get_current_seconds(); if(s_bs_task_ctrl.s_bs_ctrl[bs_flag].u8_netType == 0) //4G { v_bs_4G_data_init(); } //周期发送控制初始化 private_ies_data->u16_work_info_period = 30; //工作实时状态 上送周期 private_ies_data->u16_free_info_period = 120; //空闲实时状态 上送周期 //周期发送控制初始化 private_ies_data->s_timerCnt[IES_TIMER_Register].u16_timeNUM = 20; //秒 登录帧上送周期 private_ies_data->s_timerCnt[IES_TIMER_Register].u16_timerCnt = 20; //确保立马执行登录帧 private_ies_data->s_timerCnt[IES_TIMER_Heartbeat].u16_timeNUM = 15; //秒 心跳帧上送周期 private_ies_data->s_timerCnt[IES_TIMER_FEE_REQ].u16_timeNUM = 60; //秒 费率请求 private_ies_data->s_timerCnt[IES_TIMER_FAULT_GUN].u16_timeNUM = 60*3; //秒 故障信息上报 for ( i = 0; i < GUN_MAX_CNT; i++) { private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timeNUM = 30; //秒 工作实时状态 private_ies_data->s_timerCnt[IES_TIMER_REAL_WORK_GUN + i].u16_timerCnt = 0xFFFF; private_ies_data->s_timerCnt[IES_TIMER_TRADE_INFO + i].u16_timeNUM = 30; //秒 订单信息上送 private_ies_data->s_timerCnt[IES_TIMER_MeterValues_GUN + i].u16_timeNUM = 60*30; //秒 电表低值上送 } } /**************************************************** *函数名称: v_ies_tcp_connect() *功能说明: TCP连接功能实现 - 配置4G模块 *参数说明: * 输入参数: * 输出参数: 0失败 1成功 ****************************************************/ U8_T v_ies_tcp_connect(void) { return u8_tcp_connect(private_ies_data->id); } /**************************************************** *函数名称: v_ies_logo_on() *功能说明: 平台桩信息登录 - *参数说明: * 输入参数: * 输出参数: 0失败 1成功 ****************************************************/ U8_T v_ies_logo_on(void) { v_ies_timer_add(); //计时计数+1 //检查登录周期是否到位 if(u8_ies_timer_ctrl(IES_TIMER_Register,GET_FLAG) == 1) { v_ies_register_req(); //登录帧 Plat_Comm_LOG("Send login platform packet, u8_register_flag:%d\n",private_ies_data->u8_register_flag); if(private_ies_data->u8_register_out_cnt++ > 6) //两分钟登录帧未连接成功 { v_set_bs_renew(private_ies_data->id); //设置主函数 联网重连 v_ies_init_data(private_ies_data->id); //设置 子函数参数重置 重走初始化过程 Plat_Comm_LOG("Login frame send timeout\n"); } } if(private_ies_data->u8_register_flag == 1) { private_ies_data->u8_register_out_cnt = 0; return 1; } else return 0; } /**************************************************** *函数名称: v_ies_recv_data() *功能说明: 数据接收处理 - 控制任务周期调用 使用网口或者4G非分包功能时使用,需内部实现从串口获取数据 *参数说明: * 输入参数: * 输出参数: ****************************************************/ void v_ies_recv_data(void) { S16_T s16_recvLen = 0; U8_T u8_recvBuf[BS_RECV_BUF_LEN] = {0}; s16_recvLen = u16_bs_recv(private_ies_data->id,u8_recvBuf,BS_RECV_BUF_LEN); if(s16_recvLen > 0) { v_bs_unpack_handle_ieszt(&u8_recvBuf[0], s16_recvLen); } } /** * ************************************************************************ * @brief - 控制任务接收解析完数据后调用 4G 模块多包接收时使用-当4G使用多socket数据接收时,串口数据获取和分包功能由外部实现,函数传入参数为本平台的接收数据 * * @param[in] id - 平台地址 * @param[in] recvBuf - 接收数据 * @param[in] u16_len - 接收数据长度 * * * @author jiankalka * @date 2025-09-02 * ************************************************************************ */ void v_ies_recv_unpack(U8_T id, U8_T *recvBuf,U16_T u16_len) { v_bs_unpack_handle_ieszt(recvBuf,u16_len); } /**************************************************** *函数名称: v_ies_send_ctrl() *功能说明: 发送控制函数 - *参数说明: * 输入参数: * 输出参数: ****************************************************/ void v_ies_send_ctrl(void) { v_ies_timer_add(); //计时计数+1 v_ies_send_mag_ctrl(); //发送控制 } /**************************************************** *函数名称: v_ies_private_logic() *功能说明: 私有协议逻辑控制 - *参数说明: * 输入参数: * 输出参数: ****************************************************/ void v_ies_private_logic(U8_T bs_flag) { if(private_ies_data->u8_heart_outTime_cnt > 3) //发送三次心跳 未回复 则进行重连 { v_set_bs_renew(private_ies_data->id); //设置主函数 联网重连 v_ies_init_data(private_ies_data->id); //设置 子函数参数重置 重走初始化过程 //轻质化群充测试过程中发现与平台连接超时后不重连 //检查发现iotx_linkkit_ctx_t结构体中is_opened,is_connected未复位,原因未知,为确保现场使用,此地暂时强制复位 //v_connected_resset(); Plat_Comm_LOG("Timeout reconnect\n"); } } #endif