linux_learn/learn/CAN/app/test_protocol.c

258 lines
8.2 KiB
C
Raw 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.

/**
* @file test_protocol.c
* @brief CAN 协议栈功能测试程序
*
* 测试流程:
* 1. 加载 XML 配置(模拟保护装置)
* 2. 注册回调(遥控/定值/遥信遥测读取)
* 3. 绑定 CAN 接口(默认 can0
* 4. 进入主循环:定时上送 + 接收处理
*
* 用法:
* ./can_test -c config/protection_relay.xml -i can0 -v
*
* 本测试程序可在 PC 上编译运行(使用 vcan 虚拟 CAN 接口),不依赖实际硬件。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include "can_protocol.h"
/* ================================================================
* 命令行参数
* ================================================================ */
static const char *g_xml_path = "config/protection_relay.xml";
static const char *g_can_if = "can0";
static int g_verbose = 0;
static int g_quit = 0;
/* ================================================================
* 模拟数据(应用层维护的实际信号值)
* ================================================================ */
/* v2.0: 遥信双位置 (2-bit DPS) 位对数据
* 每字节 = 4 个遥信点,每个点 2-bit:
* 01 = 分(OFF), 10 = 合(ON)
* 点0(断路器): 10(合), 点1(刀闸1): 10(合), 点2(刀闸2): 10(合)
* → Byte0 = 0b10101001 = 0xA5 (点顺序: 3,2,1,0 → 但 bit[1:0]=点0)
* 正确: bit[1:0]=点0=10, bit[3:2]=点1=10, bit[5:4]=点2=10, bit[7:6]=点3=00
* → 0b00101010 = 0x2A
*/
static uint8_t g_yx_bitmap[16] = {
0x2A, /* 点0=10(合) 点1=10(合) 点2=10(合) 点3=00(中间态) */
};
/* v2.0: 遥测浮点值 (IEEE 754 float, 实际工程值) */
static float g_yc_values[] = {
[0] = 125.0f, /* 0x0100: Ia = 125.0 A */
[1] = 124.5f, /* 0x0101: Ib = 124.5 A */
[2] = 125.2f, /* 0x0102: Ic = 125.2 A */
[3] = 2.5f, /* 0x0103: 3I0 = 2.5 A */
[4] = 6350.0f, /* 0x0104: Ua = 6350 V */
[5] = 6345.0f, /* 0x0105: Ub = 6345 V */
[6] = 6352.0f, /* 0x0106: Uc = 6352 V */
[7] = 50.0f, /* 0x0107: 3U0 = 50 V */
[8] = 50.0f, /* 0x0108: P = 50.00 MW */
[9] = 12.0f, /* 0x0109: Q = 12.00 Mvar */
[10] = 50.00f, /* 0x010A: f = 50.00 Hz */
[11] = 0.98f, /* 0x010B: cosφ = 0.98 */
};
/* ================================================================
* 回调函数实现
* ================================================================ */
/**
* 遥信读取回调 — 协议栈定时发送时调用
*/
static int yx_read_cb(uint16_t addr, uint8_t *bitmap, int max_bytes,
void *arg)
{
(void)arg;
/* 将模拟遥信数据复制给协议栈 */
int idx = (addr - 0x0001); /* 遥信起始地址 0x0001 */
if (idx >= 0 && idx < (int)sizeof(g_yx_bitmap)) {
int copy = (max_bytes < 16) ? max_bytes : 16;
memcpy(bitmap, &g_yx_bitmap[idx], copy);
}
if (g_verbose) {
printf("[YX_READ] addr=0x%04X, bitmap=0x%02X\n",
addr, bitmap[0]);
}
return 0;
}
/**
* 遥测读取回调 (v2.0: float 值)
*/
static int yc_read_cb(uint16_t addr, float *value, void *arg)
{
(void)arg;
int idx = (addr - 0x0100);
if (idx >= 0 && idx < (int)(sizeof(g_yc_values) / sizeof(g_yc_values[0])))
*value = g_yc_values[idx];
else
*value = 0.0f;
if (g_verbose)
printf("[YC_READ] addr=0x%04X, value=%.3f\n", addr, (double)*value);
return 0;
}
/**
* 遥控回调 — 收到遥控请求时调用
*/
static int yk_cb(uint8_t point, yk_op_type_e op, void *arg)
{
(void)arg;
const char *op_name[] = { "选择", "执行", "撤销" };
printf("\n========================================\n");
printf("[YK] 收到遥控命令: 点号=%u, 操作=%s\n",
point, op_name[op]);
printf("========================================\n\n");
/* 实际应用中,这里会操作 GPIO/继电器 */
return 0;
}
/**
* 定值回调 (v2.0: float 值)
*/
static int dz_cb(uint16_t addr, float value, void *arg)
{
(void)arg;
printf("\n========================================\n");
printf("[DZ] 收到定值写入: 地址=0x%04X, 值=%.3f\n", addr, (double)value);
printf("========================================\n\n");
return 0;
}
/**
* 文件接收回调 — 收到文件首帧时调用
*/
static int ft_recv_cb(uint16_t file_id, const char *filename,
uint32_t file_size, void *arg)
{
(void)arg;
printf("\n========================================\n");
printf("[FT] 收到文件传输请求:\n");
printf(" 文件ID: 0x%04X\n", file_id);
printf(" 文件名: %s\n", filename);
printf(" 大小: %u bytes\n", file_size);
printf("========================================\n\n");
return 0; /* 返回 0 表示同意接收 */
}
/* ================================================================
* 信号处理
* ================================================================ */
static void sig_handler(int signo)
{
printf("\n收到信号 %d, 退出...\n", signo);
g_quit = 1;
can_protocol_stop();
}
/* ================================================================
* 使用说明
* ================================================================ */
static void print_usage(const char *prog)
{
printf("CAN 协议栈测试程序 — 电力二次设备通讯\n\n");
printf("用法: %s [选项]\n\n", prog);
printf("选项:\n");
printf(" -c <xml> XML 配置文件路径 (默认: config/protection_relay.xml)\n");
printf(" -i <if> CAN 接口名称 (默认: can0)\n");
printf(" -v 详细输出模式\n");
printf(" -h 显示本帮助\n");
printf("\n示例:\n");
printf(" # 使用虚拟 CAN 接口测试 (需要先 modprobe vcan)\n");
printf(" sudo ip link add dev vcan0 type vcan\n");
printf(" sudo ip link set up vcan0\n");
printf(" %s -c config/protection_relay.xml -i vcan0 -v\n", prog);
printf("\n # 模拟双装置互测\n");
printf(" sudo ip link add dev vcan0 type vcan\n");
printf(" sudo ip link add dev vcan1 type vcan\n");
printf(" sudo ip link set up vcan0\n");
printf(" sudo ip link set up vcan1\n");
printf(" # 终端1: %s -c config/protection_relay.xml -i vcan0 -v\n", prog);
printf(" # 终端2: %s -c config/data_center.xml -i vcan1 -v\n", prog);
}
/* ================================================================
* 主函数
* ================================================================ */
int main(int argc, char *argv[])
{
int opt;
while ((opt = getopt(argc, argv, "c:i:vh")) != -1) {
switch (opt) {
case 'c':
g_xml_path = optarg;
break;
case 'i':
g_can_if = optarg;
break;
case 'v':
g_verbose = 1;
break;
case 'h':
default:
print_usage(argv[0]);
return (opt == 'h') ? 0 : 1;
}
}
printf("===== CAN 协议栈测试程序 =====\n");
printf("配置文件: %s\n", g_xml_path);
printf("CAN 接口: %s\n", g_can_if);
printf("\n");
/* 1. 初始化协议栈 */
if (can_protocol_init(g_xml_path) < 0) {
fprintf(stderr, "协议栈初始化失败!\n");
return 1;
}
/* 2. 注册回调 */
can_protocol_register_yx_read_callback(yx_read_cb, NULL);
can_protocol_register_yc_read_callback(yc_read_cb, NULL);
can_protocol_register_yk_callback(yk_cb, NULL);
can_protocol_register_dz_callback(dz_cb, NULL);
can_protocol_register_ft_recv_callback(ft_recv_cb, NULL);
printf("回调函数注册完成\n");
/* 3. 绑定 CAN 接口 */
if (can_protocol_bind(g_can_if) < 0) {
fprintf(stderr, "绑定 CAN 接口失败! (尝试: sudo ip link set up %s)\n",
g_can_if);
can_protocol_deinit();
return 1;
}
/* 4. 注册信号处理 */
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
/* 5. 进入主循环 */
printf("\n进入主循环... (按 Ctrl-C 退出)\n\n");
can_protocol_run();
/* 6. 清理 */
can_protocol_deinit();
printf("测试程序正常退出\n");
return 0;
}