0
0
Files
build/hook-rs422_kk.cpp

737 lines
22 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.

/*******************************************************************************
* hook_rs422.cpp
*
* FPGA扩展RS422串口外挂程序
* 功能:监听/dev/pts/25串口处理数据并执行相应操作
*
* 1. 将产生的虚拟遥测写入/dev/pts/12串口
* 2. 将收到的上行指令写入/dev/pts/12串口
* 3. 将从串口得到的数据通过DDS接口发送出去
*
* 使用方式: ./Com_kk <发送串口> <接收串口>
* 示例: ./Com_kk /dev/pts/12 /dev/pts/25
******************************************************************************/
#include "hook-rs422_kk.h"
#include "SimMsg.h"
#include <iostream>
#include <cstring>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <poll.h>
#include <signal.h>
#include <time.h>
using namespace std;
// 全局变量定义
volatile int keep_running = 1;
uint8_t scomm_enable = 1;
// DDS相关全局变量
SimMsg* simMsg = nullptr;
string pkgname = "";
string topic_name_o2_can = "o2-can";
string topic_name_o1_can = "o1-can";
string topic_name_kk = "comu-kk";
// 互斥锁保护共享数据
std::mutex g_data_mutex;
// 本地参数声明
uint8_t tm_buffer[256] = { 0 };
S_Comm_telemetry_data_t s_tele = { 0 };
uint8_t isReceiveCmder = 0;
uint8_t rec_cmder_fromdds[255] = { 0 };
uint16_t rec_len_fromdds = 0;
string Msgfrom = "O2-A";
//====================================== 串口管理 ======================================//
// Socat虚拟串口结构
typedef struct {
int fd; // 文件描述符
bool enabled; // 使能标志
char device_path[256]; // 串口设备路径
const char* port_name; // 串口名称(用于日志标识)
const char* port_role; // 串口角色
} SocatPort;
// 串口枚举定义
enum SocatPortId {
PORT_TX = 0, // 发送串口 (argv[1])
PORT_RX = 1 // 接收串口 (argv[2])
};
// 串口实例
static SocatPort socat_ports[2] = {
{ -1, false, "", "发送串口", "发送" },
{ -1, false, "", "接收串口", "接收" }
};
// 线程相关
static pthread_t listen_thread;
//====================================== 函数声明 ======================================//
// 日志函数
void WriteLog(const string &msg);
// DDS接口函数
void pkginit(uint8_t domainid, string appname, string pkgname_);
void PkgToO2_AkkDataPub(uint8_t* o2_kk_data, uint16_t len);
void PkgToO_1kkDataPub(uint8_t* o1_kk_data, uint16_t len);
void pkgSubO_1KKData(message_func_t MessageCallback);
void PkgSubO2_AKkData(message_func_t MessageCallback);
// 数据处理函数
void cjb_rs422_kk_receive(string src, string dest, string type, string reserve1,
string reserve2, vector<uint8_t>& data);
void process_tm_buffer(uint8_t* buffer, uint16_t length);
void generate_s_tele(uint8_t* tele);
uint8_t generate_cmder(uint8_t* tele);
// 串口管理函数
int init_socat_serial(SocatPortId port_id, const char* device_path);
int read_from_serial(SocatPortId port_id, uint8_t* buffer, uint16_t buffer_size);
void forward_to_socat(uint8_t* data, uint16_t len, SocatPortId port_id);
void cleanup_socat_serial(SocatPortId port_id);
void cleanup_all_socat_serial();
// 线程函数
void* listen_serial_thread(void* arg);
int start_listen_thread();
void stop_listen_thread();
// 主程序接口
void hook_rs422_kk(const char* tx_port, const char* rx_port);
void set_scomm_enable(uint8_t s_en);
void cleanup_hook_rs422();
// 信号处理
void signal_handler(int signum);
//====================================== 日志函数 ======================================//
void WriteLog(const string &msg) {
time_t now = time(0);
char buf[80];
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&now));
cout << "[" << buf << "] " << msg << endl;
}
//====================================== FastDDS函数 ======================================//
void pkginit(uint8_t domainid, string appname, string pkgname_) {
if (simMsg == nullptr) {
WriteLog("初始化FastDDS通信...");
simMsg = new SimMsg(domainid, 3000, appname, WriteLog);
simMsg->create_pub(topic_name_o2_can);
simMsg->create_pub(topic_name_o1_can);
simMsg->create_pub(topic_name_kk);
pkgname = pkgname_;
WriteLog("FastDDS初始化完成: domainid=" + to_string(domainid) +
", appname=" + appname + ", pkgname=" + pkgname_);
}
}
void PkgToO2_AkkDataPub(uint8_t* o2_kk_data, uint16_t len) {
if (!simMsg || !scomm_enable) return;
simMsg->publish(topic_name_kk, pkgname, "O2-A", "", o2_kk_data, len);
string hex_str = "发送到O2-A DDS(" + to_string(len) + "字节): ";
for (int i = 0; i < (len < 10 ? len : 10); i++) {
char buf[4];
snprintf(buf, sizeof(buf), "%02X ", o2_kk_data[i]);
hex_str += buf;
}
if (len > 10) hex_str += "...";
WriteLog(hex_str);
}
void PkgToO_1kkDataPub(uint8_t* o1_kk_data, uint16_t len) {
if (!simMsg || !scomm_enable) return;
simMsg->publish(topic_name_kk, pkgname, "O-1", "", o1_kk_data, len);
string hex_str = "发送到O-1 DDS(" + to_string(len) + "字节): ";
for (int i = 0; i < (len < 10 ? len : 10); i++) {
char buf[4];
snprintf(buf, sizeof(buf), "%02X ", o1_kk_data[i]);
hex_str += buf;
}
if (len > 10) hex_str += "...";
WriteLog(hex_str);
}
void pkgSubO_1KKData(message_func_t MessageCallback) {
if (!simMsg) return;
vector<string> parameters;
string expression = "src = 'O-1'";
simMsg->create_sub(topic_name_kk,
static_cast<message_func_t>(MessageCallback),
expression, parameters);
WriteLog("订阅O-1数据成功");
}
void PkgSubO2_AKkData(message_func_t MessageCallback) {
if (!simMsg) return;
vector<string> parameters;
string expression = "src = 'O2-A'";
simMsg->create_sub(topic_name_kk,
static_cast<message_func_t>(MessageCallback),
expression, parameters);
WriteLog("订阅O2-A数据成功");
}
//====================================== DDS接收回调 ======================================//
void cjb_rs422_kk_receive(string src, string dest, string type, string reserve1,
string reserve2, vector<uint8_t>& data) {
if (data.empty()) {
WriteLog("警告:收到空数据");
return;
}
WriteLog("收到来自 " + src + " 的数据,大小: " + to_string(data.size()) + " 字节");
if (dest == "CJ") {
lock_guard<mutex> lock(g_data_mutex);
Msgfrom = src;
uint8_t checksum = 0;
uint16_t frameLen = 0;
// 安全复制数据
uint16_t copy_size = (data.size() < sizeof(rec_cmder_fromdds)) ?
data.size() : sizeof(rec_cmder_fromdds) - 1;
for (size_t kc = 0; kc < copy_size; kc++) {
if (kc == 5 || kc == 6) continue;
if (frameLen < sizeof(rec_cmder_fromdds)) {
rec_cmder_fromdds[frameLen++] = data[kc];
}
}
rec_len_fromdds = frameLen;
// 更新指令ID为遥控指令查询ID 0x4B
if (rec_len_fromdds > 2) {
rec_cmder_fromdds[2] = 0xB4;
}
// 调整长度字段
if (rec_len_fromdds > 4) {
if (rec_cmder_fromdds[4] >= 0x02) {
rec_cmder_fromdds[4] -= 0x02;
} else {
rec_cmder_fromdds[4] = 0;
}
}
// 重新计算校验和
for (int kc = 2; kc < rec_len_fromdds - 1; kc++) {
checksum += rec_cmder_fromdds[kc];
}
if (rec_len_fromdds > 0) {
rec_cmder_fromdds[rec_len_fromdds - 1] = checksum;
}
isReceiveCmder = 1;
WriteLog("已存储空空遥控指令,长度: " + to_string(rec_len_fromdds) + " 字节");
// 打印指令内容
string hex_str = "指令内容: ";
int print_len = (rec_len_fromdds < 20 ? rec_len_fromdds : 20);
for (int i = 0; i < print_len; i++) {
char buf[4];
snprintf(buf, sizeof(buf), "%02X ", rec_cmder_fromdds[i]);
hex_str += buf;
}
if (rec_len_fromdds > 20) hex_str += "...";
WriteLog(hex_str);
} else {
WriteLog("非CJ目标的数据忽略");
}
}
//====================================== 串口管理 ======================================//
int init_socat_serial(SocatPortId port_id, const char* device_path) {
if (port_id < 0 || port_id >= 2) {
WriteLog("错误无效的串口ID");
return -1;
}
SocatPort* port = &socat_ports[port_id];
// 更新设备路径
if (device_path && strlen(device_path) > 0) {
strncpy(port->device_path, device_path, sizeof(port->device_path) - 1);
port->device_path[sizeof(port->device_path) - 1] = '\0';
}
if (strlen(port->device_path) == 0) {
WriteLog("错误:串口" + string(port->port_role) + "路径为空");
return -1;
}
// 打开串口
port->fd = open(port->device_path, O_RDWR | O_NOCTTY);
if (port->fd < 0) {
WriteLog("错误:无法打开" + string(port->port_role) + " " +
string(port->device_path) + " (错误: " + strerror(errno) + ")");
port->enabled = false;
return -1;
}
// 配置串口参数
struct termios tty;
memset(&tty, 0, sizeof(tty));
if (tcgetattr(port->fd, &tty) != 0) {
WriteLog("错误:无法获取" + string(port->port_role) + "属性");
close(port->fd);
port->fd = -1;
port->enabled = false;
return -1;
}
// 设置波特率 115200
cfsetospeed(&tty, B115200);
cfsetispeed(&tty, B115200);
// 8N1配置
tty.c_cflag &= ~PARENB; // 无奇偶校验
tty.c_cflag &= ~CSTOPB; // 1位停止位
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8位数据位
tty.c_cflag |= CREAD | CLOCAL; // 启用接收,忽略调制解调器状态
// 关闭硬件流控
tty.c_cflag &= ~CRTSCTS;
// 输入模式
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // 非规范模式
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // 关闭软件流控
tty.c_iflag &= ~(INLCR | ICRNL | IGNCR);
// 输出模式
tty.c_oflag &= ~OPOST; // 原始输出
tty.c_oflag &= ~ONLCR;
// 超时设置
if (port_id == PORT_RX) {
tty.c_cc[VMIN] = 1; // 至少读取1个字节
tty.c_cc[VTIME] = 10; // 1秒超时
} else {
tty.c_cc[VMIN] = 0; // 非阻塞
tty.c_cc[VTIME] = 5; // 0.5秒超时
}
if (tcsetattr(port->fd, TCSANOW, &tty) != 0) {
WriteLog("错误:无法设置" + string(port->port_role) + "属性");
close(port->fd);
port->fd = -1;
port->enabled = false;
return -1;
}
// 清空缓冲区
tcflush(port->fd, TCIOFLUSH);
port->enabled = true;
WriteLog("成功初始化" + string(port->port_role) + "" +
string(port->device_path) + " (" + string(port->port_name) + ")");
return 0;
}
int read_from_serial(SocatPortId port_id, uint8_t* buffer, uint16_t buffer_size) {
if (port_id < 0 || port_id >= 2 || buffer == nullptr || buffer_size == 0) {
return -1;
}
SocatPort* port = &socat_ports[port_id];
if (!port->enabled || port->fd < 0) {
return -1;
}
// 使用poll检查是否有数据可读
struct pollfd fds[1];
fds[0].fd = port->fd;
fds[0].events = POLLIN;
int ret = poll(fds, 1, 100); // 100ms超时
if (ret > 0 && (fds[0].revents & POLLIN)) {
ssize_t bytes_read = read(port->fd, buffer, buffer_size);
if (bytes_read > 0) {
string hex_str = "" + string(port->port_role) + "(" +
string(port->device_path) + ")收到数据(" +
to_string(bytes_read) + "字节): ";
for (int i = 0; i < (bytes_read < 8 ? bytes_read : 8); i++) {
char buf[4];
snprintf(buf, sizeof(buf), "%02X ", buffer[i]);
hex_str += buf;
}
if (bytes_read > 8) hex_str += "...";
WriteLog(hex_str);
return bytes_read;
} else if (bytes_read == 0) {
return 0;
} else {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
WriteLog("错误:读取" + string(port->port_role) + "数据失败: " +
string(strerror(errno)));
}
return -1;
}
}
return 0; // 没有数据可读
}
void forward_to_socat(uint8_t* data, uint16_t len, SocatPortId port_id) {
if (port_id < 0 || port_id >= 2) return;
SocatPort* port = &socat_ports[port_id];
if (!port->enabled || port->fd < 0 || len == 0) return;
// 打印发送的数据
string hex_str = "" + string(port->port_role) + "(" +
string(port->device_path) + ")发送数据(" +
to_string(len) + "字节): ";
for (int i = 0; i < (len < 8 ? len : 8); i++) {
char buf[4];
snprintf(buf, sizeof(buf), "%02X ", data[i]);
hex_str += buf;
}
if (len > 8) hex_str += "...";
WriteLog(hex_str);
// 写入数据
ssize_t written = write(port->fd, data, len);
if (written != len) {
WriteLog("警告:未能完全写入所有数据到" + string(port->port_role) +
" (期望" + to_string(len) + "字节,实际" + to_string(written) + "字节)");
}
// 确保数据发送完成
tcdrain(port->fd);
}
void cleanup_socat_serial(SocatPortId port_id) {
if (port_id < 0 || port_id >= 2) return;
SocatPort* port = &socat_ports[port_id];
port->enabled = false;
if (port->fd >= 0) {
close(port->fd);
port->fd = -1;
WriteLog("清理" + string(port->port_role) + "" + string(port->device_path));
}
}
void cleanup_all_socat_serial() {
for (int i = 0; i < 2; i++) {
cleanup_socat_serial(static_cast<SocatPortId>(i));
}
}
//====================================== 数据处理 ======================================//
void process_tm_buffer(uint8_t* buffer, uint16_t length) {
if (length < 3) {
WriteLog("警告:接收数据长度不足");
return;
}
char type_str[10];
snprintf(type_str, sizeof(type_str), "0x%02X", buffer[2]);
WriteLog("处理指令类型: " + string(type_str));
uint8_t scomm[56];
uint8_t cmder[255];
uint8_t cmder_len;
switch (buffer[2]) {
case 0x0F: // 取常规遥测数据
WriteLog("指令0x0F: 取常规遥测数据");
generate_s_tele(scomm);
WriteLog("发送常规遥测数据到发送串口");
forward_to_socat(scomm, 56, PORT_TX);
break;
case 0x1E: // 遥控指令
WriteLog("指令0x1E: 遥控指令(本地处理)");
break;
case 0x4B: // 取主星空空的遥控指令
WriteLog("指令0x4B: 请求空空遥控指令");
{
lock_guard<mutex> lock(g_data_mutex);
cmder_len = generate_cmder(cmder);
}
if (cmder_len != 0) {
WriteLog("发送空空遥控指令到发送串口");
forward_to_socat(cmder, cmder_len, PORT_TX);
} else {
WriteLog("没有可用的空空遥控指令,返回默认响应");
forward_to_socat(cmder, 6, PORT_TX);
}
break;
case 0x5A: // 发送下行遥测
WriteLog("指令0x5A: 发送下行遥测到DDS");
if (Msgfrom == "O2-A") {
if (scomm_enable) {
PkgToO2_AkkDataPub(buffer, length);
WriteLog("发送到O2-A DDS");
}
} else if (Msgfrom == "O-1") {
if (scomm_enable) {
PkgToO_1kkDataPub(buffer, length);
WriteLog("发送到O-1 DDS");
}
} else {
if (scomm_enable) {
PkgToO2_AkkDataPub(buffer, length);
WriteLog("发送到默认O2-A DDS");
}
}
break;
default:
char unknown_str[10];
snprintf(unknown_str, sizeof(unknown_str), "0x%02X", buffer[2]);
WriteLog("未知指令类型: " + string(unknown_str));
WriteLog("数据将转发到发送串口");
forward_to_socat(buffer, length, PORT_TX);
break;
}
}
void generate_s_tele(uint8_t* tele) {
static uint8_t packet_cnt = 0;
uint8_t checksum = 0;
s_tele.packet_cnt_1 = packet_cnt++;
s_tele.para2.trans = 1;
// 填充示例数据
s_tele.snrInfoChannel = 80;
s_tele.receive_RSSI = 60;
tele[0] = 0xEB; tele[1] = 0x90; tele[2] = 0xF0;
tele[3] = 0x00; tele[4] = 0x32;
tele[5] = 0x00; tele[6] = 0x30; tele[7] = 0x01;
tele[8] = 0xA6; tele[9] = 0xC0;
tele[10] = packet_cnt; tele[11] = 0x00; tele[12] = 0x29;
memcpy(&tele[13], &s_tele, sizeof(S_Comm_telemetry_data_t));
for (int kc = 2; kc < 55; kc++)
checksum += tele[kc];
tele[55] = checksum;
WriteLog("生成遥测数据包,包计数: " + to_string(packet_cnt));
}
uint8_t generate_cmder(uint8_t* tele) {
if (isReceiveCmder) {
memcpy(tele, rec_cmder_fromdds, rec_len_fromdds);
isReceiveCmder = 0;
memset(rec_cmder_fromdds, 0, 255);
return rec_len_fromdds;
} else {
tele[0] = 0xEB; tele[1] = 0x90; tele[2] = 0xB4;
tele[3] = 0x00; tele[4] = 0x00; tele[5] = 0xB4;
return 6;
}
}
//====================================== 监听线程 ======================================//
void* listen_serial_thread(void* arg) {
WriteLog("串口监听线程启动,监听接收串口");
uint8_t read_buffer[256];
while (keep_running) {
int bytes_read = read_from_serial(PORT_RX, read_buffer, sizeof(read_buffer));
if (bytes_read > 0) {
process_tm_buffer(read_buffer, bytes_read);
} else if (bytes_read < 0) {
usleep(100000); // 100ms
} else {
usleep(10000); // 10ms
}
}
WriteLog("串口监听线程停止");
return NULL;
}
int start_listen_thread() {
if (socat_ports[PORT_RX].fd < 0) {
WriteLog("错误:接收串口未初始化");
return -1;
}
keep_running = 1;
int ret = pthread_create(&listen_thread, NULL, listen_serial_thread, NULL);
if (ret != 0) {
WriteLog("错误:无法创建监听线程");
return -1;
}
return 0;
}
void stop_listen_thread() {
keep_running = 0;
if (listen_thread) {
pthread_join(listen_thread, NULL);
listen_thread = 0;
}
}
//====================================== 主程序接口 ======================================//
void hook_rs422_kk(const char* tx_port, const char* rx_port) {
WriteLog("初始化RS422通信模块");
WriteLog("发送串口: " + string(tx_port ? tx_port : "未指定"));
WriteLog("接收串口: " + string(rx_port ? rx_port : "未指定"));
// 初始化串口
int success_count = 0;
if (init_socat_serial(PORT_TX, tx_port) == 0) {
success_count++;
}
if (init_socat_serial(PORT_RX, rx_port) == 0) {
success_count++;
}
if (success_count >= 2) {
WriteLog("成功初始化所有串口");
} else if (success_count >= 1) {
WriteLog("警告:只初始化了 " + to_string(success_count) + " 个串口");
WriteLog("错误:无法正常工作,至少需要两个串口");
return;
} else {
WriteLog("错误:未能初始化任何串口");
return;
}
// 初始化DDS
pkginit(1, "rs422_app", "hook_rs422");
// 订阅DDS消息
PkgSubO2_AKkData(cjb_rs422_kk_receive);
pkgSubO_1KKData(cjb_rs422_kk_receive);
WriteLog("已订阅O2-A和O-1数据");
// 启动监听线程
if (start_listen_thread() == 0) {
WriteLog("串口监听线程已启动");
} else {
WriteLog("错误:未能启动串口监听线程");
}
}
void set_scomm_enable(uint8_t s_en) {
scomm_enable = s_en;
WriteLog("设置通信使能: " + to_string(s_en));
}
void cleanup_hook_rs422() {
WriteLog("清理RS422通信模块资源");
stop_listen_thread();
cleanup_all_socat_serial();
if (simMsg) {
delete simMsg;
simMsg = nullptr;
WriteLog("清理DDS资源");
}
}
//====================================== 信号处理 ======================================//
void signal_handler(int signum) {
keep_running = 0;
}
//====================================== 主函数 ======================================//
int main(int argc, char* argv[]) {
WriteLog("========================================");
WriteLog("RS422通信程序启动");
WriteLog("使用方式: " + string(argv[0]) + " <发送串口> <接收串口>");
WriteLog("示例: " + string(argv[0]) + " /dev/pts/12 /dev/pts/25");
WriteLog("========================================");
// 检查参数
if (argc < 3) {
WriteLog("错误:参数不足");
WriteLog("请指定发送串口和接收串口:");
WriteLog(" " + string(argv[0]) + " /dev/pts/12 /dev/pts/25");
WriteLog("或使用默认值:");
WriteLog(" " + string(argv[0]) + " /dev/pts/12 /dev/pts/25");
return 1;
}
const char* tx_port = argv[1];
const char* rx_port = argv[2];
// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
signal(SIGQUIT, signal_handler);
// 启动RS422通信模块
hook_rs422_kk(tx_port, rx_port);
// 主循环
while (keep_running) {
sleep(1);
static int counter = 0;
if (counter++ % 60 == 0) {
string tx_path = socat_ports[PORT_TX].device_path;
string rx_path = socat_ports[PORT_RX].device_path;
WriteLog("程序运行中... 发送串口:" + tx_path + " 接收串口:" + rx_path);
}
}
// 清理资源
cleanup_hook_rs422();
return 0;
}