/******************************************************************************* * 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 #include #include #include #include #include #include #include #include #include #include #include #include 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& 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 parameters; string expression = "src = 'O-1'"; simMsg->create_sub(topic_name_kk, static_cast(MessageCallback), expression, parameters); WriteLog("订阅O-1数据成功"); } void PkgSubO2_AKkData(message_func_t MessageCallback) { if (!simMsg) return; vector parameters; string expression = "src = 'O2-A'"; simMsg->create_sub(topic_name_kk, static_cast(MessageCallback), expression, parameters); WriteLog("订阅O2-A数据成功"); } //====================================== DDS接收回调 ======================================// void cjb_rs422_kk_receive(string src, string dest, string type, string reserve1, string reserve2, vector& data) { if (data.empty()) { WriteLog("警告:收到空数据"); return; } WriteLog("收到来自 " + src + " 的数据,大小: " + to_string(data.size()) + " 字节"); if (dest == "CJ") { lock_guard 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(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 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; }