Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,834 @@
|
||||
#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 <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*============================================================================
|
||||
* 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();
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user