#include "PCM_comm.h" #include "lwip/sockets.h" #include "mdu_comm/mdu_comm_task.h" #include "mdu_comm/mdu_interface.h" #include "app_init/app_init.h" #include "mylog/mylog.h" #include "publicdata/publicdata.h" #include "wdt_task/wdt_task.h" #include "app_init/app_init.h" #include "app_init/app_test.h" #include "app_init/task_lock.h" #include "flowctrl/flowctrl_task.h" #include "flowctrl/flow_interface.h" #include "collect_ctrl/collect_task.h" #include #include /*============================================================================ * PCM UDP 通信模块(终端角色) * * 功能:实现群充终端(分桩)与主机之间的UDP通信协议 * 协议定义参考:主机终端通信协议.md * * 本机角色:终端(分桩) * - 主动发送:分桩心跳帧(0x10) 周期1000ms * - 主动发送:分桩请求帧(0x12) 周期200ms * - 接收处理:主机心跳帧(0x11) * - 接收处理:分桩请求应答帧(0x13) * * 帧结构: * 5A 99 - 帧头(2B) * xx - 帧功能码(1B) * xx - 数据长度(1B) * data - 帧数据(NB) * xx - CRC校验(1B) * A5 - 帧尾(1B) *============================================================================*/ /*============================================================================ * 局部宏定义 *============================================================================*/ #define PCM_COMM_TASK_ID (TASK_ID_MduComm) #define PCM_COMM_LOG(fmt, ...) MYLOG_MSG(PCM_COMM_TASK_ID, (fmt), ##__VA_ARGS__) /* 分桩请求帧:每组枪数据长度(11字节) */ #define PCM_TERM_REQUEST_GUN_DATA_LEN 11 /* 分桩请求应答帧:每组枪数据长度(9字节) */ #define PCM_HOST_RESPONSE_GUN_DATA_LEN 9 /* 0x12 分桩请求帧测试开关: * 0: 使用系统实时数据 * 1: 使用本文件内置测试结构体数据(脱离系统数据源联调) */ #ifndef PCM_TERM_REQUEST_TEST_EN #define PCM_TERM_REQUEST_TEST_EN 0 #endif /* UDP收发内容打印开关(默认关闭,避免刷屏/占用CPU) * 0: 关闭 * 1: 打印 TX/RX 数据(十六进制,截断到最大字节数) */ #ifndef PCM_UDP_PRINT_EN #define PCM_UDP_PRINT_EN 0 #endif #ifndef PCM_UDP_PRINT_MAX_BYTES #define PCM_UDP_PRINT_MAX_BYTES 64U #endif /*============================================================================ * 局部变量 *============================================================================*/ /* UDP 套接字句柄 */ static int g_pcm_udp_sock = -1; /* UDP 目标地址结构体(主机地址) */ static struct sockaddr_in g_pcm_peer_addr; /* PCM 通信数据 */ S_PCM_COMM_DATA s_pcm_comm; /* 接收缓冲区 */ static U8_T g_u8_recv_buf[PCM_FRAME_MAX_LEN]; #if (PCM_UDP_PRINT_EN != 0) static void v_pcm_log_hex(const char *tag, const U8_T *data, U16_T len) { char line[PCM_UDP_PRINT_MAX_BYTES * 3U + 16U]; U16_T n = len; U16_T i; int pos = 0; if ((tag == NULL) || (data == NULL)) { return; } if (n > (U16_T)PCM_UDP_PRINT_MAX_BYTES) { n = (U16_T)PCM_UDP_PRINT_MAX_BYTES; } for (i = 0U; i < n; i++) { int w = snprintf(&line[pos], sizeof(line) - (size_t)pos, "%02X ", (unsigned)data[i]); if (w <= 0) { break; } pos += w; if (pos >= (int)sizeof(line) - 1) { break; } } line[(pos < (int)sizeof(line)) ? pos : ((int)sizeof(line) - 1)] = '\0'; if (len > n) { printf("%s len=%u show=%u: %s...\r\n", tag, (unsigned)len, (unsigned)n, line); } else { printf("%s len=%u: %s\r\n", tag, (unsigned)len, line); } } #endif #if (PCM_UDP_PRINT_EN != 0) static void v_pcm_log_addr_port(const char *tag, const struct sockaddr_in *addr) { U32_T ip_host; U16_T port_host; if ((tag == NULL) || (addr == NULL)) { return; } ip_host = PP_NTOHL(addr->sin_addr.s_addr); port_host = PP_NTOHS(addr->sin_port); printf("%s %u.%u.%u.%u:%u\r\n", tag, (unsigned)((ip_host >> 24) & 0xFFU), (unsigned)((ip_host >> 16) & 0xFFU), (unsigned)((ip_host >> 8) & 0xFFU), (unsigned)(ip_host & 0xFFU), (unsigned)port_host); } #endif #if (PCM_TERM_REQUEST_TEST_EN != 0) typedef struct { U8_T u8_gun_num; S_PCM_GUN_TERM_DATA s_gun_data[GUN_MAX_CNT]; } S_PCM_TERM_REQUEST_TEST_DATA; /* 0x12 测试固定数据(仅在 PCM_TERM_REQUEST_TEST_EN=1 时生效) */ static S_PCM_TERM_REQUEST_TEST_DATA s_pcm_term_req_test_data = { 2U, { /* gun_addr, work_state, contactor_dc, contactor_discharge, need_vol(0.1V), need_cur(0.1A), fault_state, fault_reason */ {1U, 0U, 0U, 0U, 0U, 0U, 0U, 0U}, {2U, 0U, 0U, 0U, 0U, 0U, 0U, 0U} } }; #endif /*============================================================================ * 局部函数声明 *============================================================================*/ static void v_pcm_udp_socket_init(void); //static U8_T u8_pcm_verify_frame(const S_PCM_FRAME *ps_frame); static S8_T s8_pcm_send_frame(U8_T u8_cmd, const U8_T *pu8_data, U8_T u8_len); static void v_pcm_parse_host_heartbeat(const S_PCM_FRAME *ps_frame); static void v_pcm_parse_host_response(const S_PCM_FRAME *ps_frame); static void v_pcm_check_comm_timeout(U16_T u16_sleep_time_ms); /*============================================================================ * 函数实现 *============================================================================*/ /** * @brief 初始化 PCM UDP socket * * 参照 mylog.c 中 v_mylog_udp_init 的实现方式: * - 创建 UDP socket * - 绑定本地端口 * - 失败时关闭socket并重试 */ static void v_pcm_udp_socket_init(void) { struct sockaddr_in local_addr; if (g_pcm_udp_sock >= 0) { return; } g_pcm_udp_sock = lwip_socket(AF_INET, SOCK_DGRAM, 0); if (g_pcm_udp_sock < 0) { PCM_COMM_LOG("pcm udp socket create fail"); return; } memset(&local_addr, 0, sizeof(local_addr)); local_addr.sin_family = AF_INET; local_addr.sin_port = PP_HTONS(PCM_UDP_SRC_PORT); local_addr.sin_addr.s_addr = PP_HTONL(INADDR_ANY); if (lwip_bind(g_pcm_udp_sock, (const struct sockaddr *)&local_addr, sizeof(local_addr)) != 0) { PCM_COMM_LOG("pcm udp bind fail, port=%d", PCM_UDP_SRC_PORT); lwip_close(g_pcm_udp_sock); g_pcm_udp_sock = -1; return; } PCM_COMM_LOG("pcm udp socket init ok, port=%d", PCM_UDP_SRC_PORT); } ///** // * @brief 校验接收帧的合法性 // * // * @param ps_frame 帧指针 // * @return U8_T 0无效 1有效 // */ //static U8_T u8_pcm_verify_frame(const S_PCM_FRAME *ps_frame) //{ // U8_T u8_calc_crc; // /* 校验帧头 */ // if (ps_frame->u16_head != PCM_FRAME_HEAD) { // return 0; // } // /* 校验帧尾 */ // if (ps_frame->u8_tail != PCM_FRAME_TAIL) { // return 0; // } // /* 校验数据长度合法性 */ // if (ps_frame->u8_len > PCM_FRAME_MAX_DATA_LEN) { // return 0; // } // /* 校验CRC:计算范围 = 帧头(2) + 功能码(1) + 长度(1) + 数据(u8_len) */ // u8_calc_crc = u8_pcm_calc_crc((const U8_T *)&ps_frame->u16_head, // (U16_T)(sizeof(U16_T) + sizeof(U8_T) + sizeof(U8_T) + ps_frame->u8_len)); // if (u8_calc_crc != ps_frame->u8_crc) { // return 0; // } // return 1; //} /** * @brief 发送PCM帧 * * @param u8_cmd 帧功能码 * @param pu8_data 数据指针 * @param u8_len 数据长度 * @return S8_T 0成功 -1失败 */ static S8_T s8_pcm_send_frame(U8_T u8_cmd, const U8_T *pu8_data, U8_T u8_len) { U8_T au8_send_buf[PCM_FRAME_MIN_LEN + PCM_FRAME_MAX_DATA_LEN]; U8_T u8_send_len; U8_T u8_crc; S8_T s8_ret; if (g_pcm_udp_sock < 0) { v_pcm_udp_socket_init(); } if (g_pcm_udp_sock < 0) { return -1; } if (u8_len > PCM_FRAME_MAX_DATA_LEN) { return -1; } /* 手动组帧:紧凑排列,无空洞 */ u8_send_len = 0; /* 帧头(2B) */ au8_send_buf[u8_send_len++] = (U8_T)(PCM_FRAME_HEAD >> 8); au8_send_buf[u8_send_len++] = (U8_T)(PCM_FRAME_HEAD & 0xFF); /* 功能码(1B) */ au8_send_buf[u8_send_len++] = u8_cmd; /* 数据长度(1B) */ au8_send_buf[u8_send_len++] = u8_len; /* 数据(NB) */ if (pu8_data != NULL && u8_len > 0) { memcpy(&au8_send_buf[u8_send_len], pu8_data, u8_len); u8_send_len += u8_len; } /* CRC校验(1B):计算范围 = 帧头 + 功能码 + 长度 + 数据 */ u8_crc = u16_crc_checksum(&au8_send_buf[4], u8_len); au8_send_buf[u8_send_len++] = u8_crc; /* 帧尾(1B) */ au8_send_buf[u8_send_len++] = PCM_FRAME_TAIL; #if (PCM_UDP_PRINT_EN != 0) v_pcm_log_addr_port("[PCM][UDP][TX][DST]", &g_pcm_peer_addr); v_pcm_log_hex("[PCM][UDP][TX]", au8_send_buf, (U16_T)u8_send_len); #endif /* 发送 */ s8_ret = (S8_T)lwip_sendto(g_pcm_udp_sock, (const void *)au8_send_buf, (size_t)u8_send_len, 0, (const struct sockaddr *)&g_pcm_peer_addr, sizeof(g_pcm_peer_addr)); if (s8_ret < 0) { return -1; } return 0; } /** * @brief 解析主机心跳帧(功能码0x11) * * 数据格式: * - 终端枪数量(1B) * - 终端枪地址(1B) x N */ static void v_pcm_parse_host_heartbeat(const S_PCM_FRAME *ps_frame) { U8_T u8_gun_num; if (ps_frame->u8_len < 1) { return; } u8_gun_num = ps_frame->u8_data[0]; if (u8_gun_num == 0 || u8_gun_num > GUN_MAX_CNT) { return; } /* 校验数据长度:1(枪数量) + u8_gun_num(枪地址) */ if (ps_frame->u8_len < (U8_T)(1 + u8_gun_num)) { return; } /* 更新主机通信状态 */ s_pcm_comm.s_host.u32_last_recv_time = (U32_T)u64_get_current_millis(); s_pcm_comm.s_host.u8_comm_ok = 1; PCM_COMM_LOG("recv host heartbeat, gun_num=%d", u8_gun_num); } /** * @brief 解析分桩请求应答帧(功能码0x13) * * 数据格式(每组枪数据): * - 终端枪地址(1B) * - 模块启停状态(1B) * - 输出电压(2B) BE 0.1V * - 输出电流(2B) BE 0.1A * - 主机故障状态(1B) * - 主机故障原因(2B) BE * 每组共 9 字节 */ static void v_pcm_parse_host_response(const S_PCM_FRAME *ps_frame) { U8_T u8_gun_num; U8_T i; U8_T u8_offset; U8_T u8_expected_len; if (ps_frame->u8_len < 1) { return; } u8_gun_num = ps_frame->u8_data[0]; if (u8_gun_num == 0 || u8_gun_num > GUN_MAX_CNT) { return; } /* 校验数据长度:1(枪数量) + u8_gun_num * 9(每组枪数据) */ u8_expected_len = (U8_T)(1 + u8_gun_num * PCM_HOST_RESPONSE_GUN_DATA_LEN); if (ps_frame->u8_len < u8_expected_len) { return; } /* 更新主机通信状态 */ // s_pcm_comm.s_host.u32_last_recv_time = (U32_T)u64_get_current_millis(); s_pcm_comm.s_host.u8_comm_ok = 1; s_pcm_comm.s_host.u8_gun_num = u8_gun_num; u8_offset = 1; for (i = 0; i < u8_gun_num && i < GUN_MAX_CNT; i++) { S_PCM_GUN_HOST_DATA *ps_gun = &s_pcm_comm.s_host.s_gun_data[i]; U16_T u16_value; ps_gun->u8_gun_addr = ps_frame->u8_data[u8_offset++]; ps_gun->u8_module_state = ps_frame->u8_data[u8_offset++]; /* 输出电压 BE: 高字节在前 */ u16_value = (U16_T)ps_frame->u8_data[u8_offset++] << 8; u16_value |= (U16_T)ps_frame->u8_data[u8_offset++]; ps_gun->u16_out_vol = u16_value; /* 输出电流 BE: 高字节在前 */ u16_value = (U16_T)ps_frame->u8_data[u8_offset++] << 8; u16_value |= (U16_T)ps_frame->u8_data[u8_offset++]; ps_gun->u16_out_cur = u16_value; ps_gun->u8_host_fault = ps_frame->u8_data[u8_offset++]; /* 主机故障原因 BE: 高字节在前 */ u16_value = (U16_T)ps_frame->u8_data[u8_offset++] << 8; u16_value |= (U16_T)ps_frame->u8_data[u8_offset++]; ps_gun->u16_host_fault_reason = u16_value; if(ps_gun->u16_out_vol > 2000 && ps_gun->u8_module_state == 0) ps_gun->u8_module_state = 1; // u8_offset += PCM_HOST_RESPONSE_GUN_DATA_LEN; // PCM_COMM_LOG("recv host response, gun_addr=%d, module_state=%d, out_vol=%d, out_cur=%d, host_fault=%d, host_fault_reason=%d", // ps_gun->u8_gun_addr, ps_gun->u8_module_state, ps_gun->u16_out_vol, ps_gun->u16_out_cur, ps_gun->u8_host_fault, ps_gun->u16_host_fault_reason); } } /** * @brief 检查主机通信超时 * * @param u16_sleep_time_ms 任务周期时间(ms) */ static void v_pcm_check_comm_timeout(U16_T u16_sleep_time_ms) { U64_T u64_now_ms __attribute__((unused)); U64_T u64_elapsed; // (void)u16_sleep_time_ms; if (s_pcm_comm.s_host.u32_last_recv_time == 0) { return; } u64_now_ms = u64_get_current_millis(); u64_elapsed = u64_safe_millis_since(s_pcm_comm.s_host.u32_last_recv_time); if (u64_elapsed >= PCM_COMM_TIMEOUT_MS) { if (s_pcm_comm.s_host.u8_comm_ok != 0) { s_pcm_comm.s_host.u8_comm_ok = 0; PCM_COMM_LOG("pcm host comm timeout"); } } } #if 1 //获取外部数据接口 #endif /*============================================================================ * 全局函数实现 *============================================================================*/ /** * @brief 初始化 PCM UDP 通信(终端角色) * * @param u8_term_no 终端编号(1开始,对应主机编号) * @return S8_T 0成功 -1失败 */ S8_T s8_pcm_udp_init(void) { /* 初始化socket */ v_pcm_udp_socket_init(); if (g_pcm_udp_sock < 0) { return -1; } /* 初始化通信数据 */ memset(&s_pcm_comm, 0, sizeof(S_PCM_COMM_DATA)); //s_pcm_comm.u8_term_no = u8_term_no; /* 构建本机(终端)IP地址:192.168.1.100 + (终端编号-1)*10 */ { s_pcm_comm.u32_term_ip = PP_HTONL(LWIP_MAKEU32(g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[0], g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[1], g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[2], g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[3])); } s_pcm_comm.u16_term_port = PP_HTONS(PCM_UDP_SRC_PORT); /* 构建目标主机IP地址:192.168.1.200 + (终端编号-1) */ { U8_T u8_ip3 = (U8_T)(PCM_HOST_IP_BASE3 + ((g_t_share_data.t_sys_fix_cfg.s_net_cfg.u8_eth_ip[3] - 100)/PCM_TERM_IP_OFFSET)*PCM_TERM_IP_OFFSET); s_pcm_comm.u32_host_ip = PP_HTONL(LWIP_MAKEU32(PCM_HOST_IP_BASE0, PCM_HOST_IP_BASE1, PCM_HOST_IP_BASE2, u8_ip3)); } s_pcm_comm.u16_host_port = PP_HTONS(PCM_UDP_DST_PORT); /* 设置目标地址(主机地址) */ memset(&g_pcm_peer_addr, 0, sizeof(g_pcm_peer_addr)); g_pcm_peer_addr.sin_family = AF_INET; g_pcm_peer_addr.sin_port = s_pcm_comm.u16_host_port; g_pcm_peer_addr.sin_addr.s_addr = s_pcm_comm.u32_host_ip; /* 初始化定时器 */ s_pcm_comm.u32_heartbeat_tick = 0; s_pcm_comm.u32_request_tick = 0; return 0; } /** * @brief 发送分桩心跳帧(功能码0x10) * * 数据格式: * - 终端枪数量(1B) * - 终端枪地址(1B) x N * * @return S8_T 0成功 -1失败 */ S8_T s8_pcm_send_term_heartbeat() { SYS_FIX_CFG_T *t_sys_fix_cfg = &g_t_share_data.t_sys_fix_cfg; U8_T au8_data[1 + GUN_MAX_CNT]; U8_T u8_len = 0; au8_data[u8_len++] = u16_flow_get_sys_data(0,E_FLOW_GET_DATA_GUN_CNT,0); //获取枪数量 au8_data[u8_len++] = t_sys_fix_cfg->u8_terminal[0]; //枪1地址 au8_data[u8_len++] = t_sys_fix_cfg->u8_terminal[1]; //枪2地址 return s8_pcm_send_frame(PCM_CMD_TERM_HEARTBEAT, au8_data, u8_len); } /** * @brief 发送分桩请求帧(功能码0x12) * * 数据格式(每组枪数据): * - 终端枪地址(1B) * - 终端枪工作状态(1B) * - 直流输出接触器状态(1B) * - 直流输出泄放接触器状态(1B) * - 需求电压(2B) BE 0.1V * - 需求电流(2B) BE 0.1A * - 终端枪故障状态(1B) * - 终端枪故障原因(2B) BE * 每组共 11 字节 * * @return S8_T 0成功 -1失败 */ S8_T s8_pcm_send_term_request(void) { U8_T au8_data[1 + GUN_MAX_CNT * PCM_TERM_REQUEST_GUN_DATA_LEN]; U8_T u8_len = 0; U8_T i; U8_T u8_gun_num; U16_T u16_data; SYS_FIX_CFG_T *t_sys_fix_cfg = &g_t_share_data.t_sys_fix_cfg; #if (PCM_TERM_REQUEST_TEST_EN != 0) u8_gun_num = s_pcm_term_req_test_data.u8_gun_num; if (u8_gun_num > GUN_MAX_CNT) { u8_gun_num = GUN_MAX_CNT; } #else u8_gun_num = u16_flow_get_sys_data(0, E_FLOW_GET_DATA_GUN_CNT, 0); //获取枪数量 au8_data[u8_len++] = u8_gun_num; /* 枪数量 */ #endif // /* 枪数量 */ for (i = 0; i < u8_gun_num ; i++) { #if (PCM_TERM_REQUEST_TEST_EN != 0) const S_PCM_GUN_TERM_DATA *ps_test = &s_pcm_term_req_test_data.s_gun_data[i]; au8_data[u8_len++] = u16_flow_get_sys_data(0, E_FLOW_GET_DATA_GUN_CNT, 0); au8_data[u8_len++] = ps_test->u8_gun_addr; /* 枪地址 */ au8_data[u8_len++] = ps_test->u8_work_state; /* 工作状态 */ au8_data[u8_len++] = ps_test->u8_contactor_dc; /* 直流接触器 */ au8_data[u8_len++] = ps_test->u8_contactor_discharge; /* 泄放接触器 */ /* 需求电压 BE */ u16_data = ps_test->u16_need_vol; au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF); au8_data[u8_len++] = (U8_T)(u16_data & 0xFF); /* 需求电流 BE */ u16_data = ps_test->u16_need_cur; au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF); au8_data[u8_len++] = (U8_T)(u16_data & 0xFF); au8_data[u8_len++] = ps_test->u8_fault_state; /* 故障状态 */ /* 故障原因 BE */ u16_data = ps_test->u16_fault_reason; au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF); au8_data[u8_len++] = (U8_T)(u16_data & 0xFF); #else au8_data[u8_len++] = t_sys_fix_cfg->u8_terminal[i]; /* 枪地址 */ au8_data[u8_len++] = u8_mdu_put_data(i); /* 工作状态 */ au8_data[u8_len++] = (U8_T)u8_get_gun_yx_data(YX_GUN_DC_KM,i); /* 直流接触器 */ au8_data[u8_len++] = (U8_T)u8_get_gun_yx_data(YX_GUN_RELIF,i); /* 泄放接触器 */ /* 需求电压 BE */ u16_data = (U16_T)u16_flow_put_data(i, E_FLOW_PUT_DATA_GUN_NEED_VOL, 0); au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF); au8_data[u8_len++] = (U8_T)(u16_data & 0xFF); /* 需求电流 BE */ u16_data = (U16_T)u16_flow_put_data(i, E_FLOW_PUT_DATA_GUN_NEED_CUR, 0); au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF); au8_data[u8_len++] = (U8_T)(u16_data & 0xFF); /* 故障状态 */ if(u8_get_fault_sum(i,0) > 0) { au8_data[u8_len++] = 1; } else { au8_data[u8_len++] = 0; } /* 故障原因 BE */ u16_data = u16_flow_get_sys_data(i, E_FLOW_GET_DATA_GUN_FAULT_INFO, 0); au8_data[u8_len++] = (U8_T)((u16_data >> 8) & 0xFF); au8_data[u8_len++] = (U8_T)(u16_data & 0xFF); #endif } return s8_pcm_send_frame(PCM_CMD_TERM_REQUEST, au8_data, u8_len); } /** * @brief PCM UDP 非阻塞接收处理 * * 参照 mylog.c 中 v_mylog_udp_recv_echo 的实现方式: * - 使用 MSG_DONTWAIT 非阻塞接收 * - 解析接收到的帧 * * @return U8_T 0无数据 1收到有效数据 */ U8_T u8_pcm_udp_recv_process(void) { int recv_len; socklen_t peer_len; struct sockaddr_in peer_addr; S_PCM_FRAME s_frame; S_PCM_FRAME *ps_frame = &s_frame; if (g_pcm_udp_sock < 0) { return 0; } peer_len = (socklen_t)sizeof(peer_addr); recv_len = lwip_recvfrom(g_pcm_udp_sock, (void *)g_u8_recv_buf, sizeof(g_u8_recv_buf), MSG_DONTWAIT, (struct sockaddr *)&peer_addr, &peer_len); if (recv_len <= 0) { return 0; } #if (PCM_UDP_PRINT_EN != 0) v_pcm_log_addr_port("[PCM][UDP][RX][SRC]", &peer_addr); v_pcm_log_hex("[PCM][UDP][RX]", g_u8_recv_buf, (U16_T)recv_len); #endif /* 检查最小帧长度 */ if (recv_len < PCM_FRAME_MIN_LEN) { return 0; } memset(&s_frame, 0, sizeof(S_PCM_FRAME)); ps_frame->u16_head = (U16_T)((g_u8_recv_buf[0] << 8) | g_u8_recv_buf[1]); ps_frame->u8_cmd = g_u8_recv_buf[2]; ps_frame->u8_len = g_u8_recv_buf[3]; // if (ps_frame->u8_len > PCM_FRAME_MAX_DATA_LEN) { // return 0; // } memcpy(ps_frame->u8_data, &g_u8_recv_buf[4], ps_frame->u8_len); ps_frame->u8_crc = g_u8_recv_buf[4 + ps_frame->u8_len]; ps_frame->u8_tail = g_u8_recv_buf[4 + ps_frame->u8_len + 1]; /* 校验帧合法性 */ // if (u8_pcm_verify_frame(ps_frame) == 0) { // return 0; // } /* 根据功能码分发处理 */ switch (ps_frame->u8_cmd) { case PCM_CMD_HOST_HEARTBEAT: /* 0x11 主机心跳帧 */ v_pcm_parse_host_heartbeat(ps_frame); break; case PCM_CMD_HOST_RESPONSE: /* 0x13 分桩请求应答帧 */ v_pcm_parse_host_response(ps_frame); break; default: /* 未知功能码,忽略 */ break; } return 1; } /** * @brief PCM UDP 周期处理函数(终端角色) * * 处理逻辑: * - 每1000ms发送分桩心跳帧(0x10) * - 每200ms发送分桩请求帧(0x12) * - 每周期检查通信超时 * * @param u16_sleep_time_ms 任务周期时间(ms) */ void v_pcm_udp_periodic_handler(U16_T u16_sleep_time_ms) { if (g_pcm_udp_sock < 0) { return; } /* 更新定时器 */ s_pcm_comm.u32_heartbeat_tick += u16_sleep_time_ms; s_pcm_comm.u32_request_tick += u16_sleep_time_ms; /* 每1000ms发送分桩心跳帧(0x10) */ if (s_pcm_comm.u32_heartbeat_tick >= PCM_HEARTBEAT_PERIOD_MS) { s_pcm_comm.u32_heartbeat_tick = 0; (void)s8_pcm_send_term_heartbeat(); } /* 每200ms发送分桩请求帧(0x12) */ if (s_pcm_comm.u32_request_tick >= PCM_REQUEST_PERIOD_MS) { s_pcm_comm.u32_request_tick = 0; s8_pcm_send_term_request(); } /* 检查通信超时 */ v_pcm_check_comm_timeout(u16_sleep_time_ms); } /** * @brief 获取主机通信状态 * * @return U8_T 0超时 1正常 */ U8_T u8_pcm_get_host_comm_status(void) { return s_pcm_comm.s_host.u8_comm_ok; } /** * @brief 获取主机应答枪数据 * * @param u8_gun_index 枪索引 * @return S_PCM_GUN_HOST_DATA* 主机应答数据指针,失败返回NULL */ S_PCM_GUN_HOST_DATA* ps_pcm_get_host_gun_data(U8_T u8_gun_index) { if (u8_gun_index >= GUN_MAX_CNT) { return NULL; } if (u8_gun_index >= s_pcm_comm.s_host.u8_gun_num) { return NULL; } return &s_pcm_comm.s_host.s_gun_data[u8_gun_index]; } /** * @brief 更新本机(终端)枪数据(由外部模块调用) * * @param u8_gun_index 枪索引 * @param ps_data 终端枪数据指针 */ void v_pcm_update_term_gun_data(U8_T u8_gun_index, const S_PCM_GUN_TERM_DATA *ps_data) { if (u8_gun_index >= GUN_MAX_CNT || ps_data == NULL) { return; } memcpy(&s_pcm_comm.s_gun_data[u8_gun_index], ps_data, sizeof(S_PCM_GUN_TERM_DATA)); } /** * @brief PCM 通信主处理函数(由 mdu_comm_task 调用) * * 该函数在 u8_mdu_type == 0x02 时被 mdu_comm_task 主循环调用。 * 负责 PCM UDP 通信的初始化、周期收发和接收处理。 * * @param u16_sleep_time_ms 任务周期时间(ms) */ void v_pcm_comm_gw_action(U16_T u16_sleep_time_ms) { static U8_T u8_pcm_init_done = 0; /* 首次运行时初始化 PCM UDP */ if (u8_pcm_init_done == 0) { S8_T s8_ret = s8_pcm_udp_init(); if (s8_ret == 0) { u8_pcm_init_done = 1; PCM_COMM_LOG("pcm udp init success"); } else { /* 初始化失败(可能lwIP网络协议栈未就绪),下次循环继续重试 */ return; } } /* PCM UDP 周期处理:发送心跳(1000ms)、发送请求(200ms)、超时检测(5000ms) */ v_pcm_udp_periodic_handler(u16_sleep_time_ms); /* PCM UDP 非阻塞接收处理 */ (void)u8_pcm_udp_recv_process(); }