''' 功能对等客户端:模拟故障注入软件,发送/接收UDP数据 author:Tan Mingyan 更新日期:2025/2/26 ''' ''' 2. 功能自测步骤 步骤 1:启动工程主程序 运行main.py,查看日志确认: 外设配置成功加载(10 个外设); UDP 服务初始化完成,所有端口正常监听; 无套接字绑定、配置解析错误。 步骤 2:运行 UDP 测试客户端 运行test/udp_test_client.py,执行单端口发送和多端口并行发送,验证以下核心功能: 发送成功:客户端打印📤 发送数据成功,无端口连接失败; 中间层接收成功:查看工程logs/run_*.log,打印对应端口的数据接收日志,无 16 进制解析错误; 端口隔离性:多端口并行发送时,各端口数据独立接收,无串包、丢包(日志中各外设数据互不干扰); 校验和检测:若发送数据包含正确校验位,日志打印校验和检测通过;若修改校验位为错误值,日志打印校验和检测失败并记录到logs/error_*.log; 回传数据接收:若在工程中调用udp_server.send_to_fault()(如协议解析后回传),客户端能打印📥 接收中间层回传数据,数据格式正确。 3. 常见问题排查 表格 问题现象 排查方向 客户端提示发送数据失败 1. 中间层主程序是否未启动;2. 目标端口是否在 CSV 配置中;3. 端口是否被占用 中间层未接收数据 1. 客户端发送的 IP 是否为127.0.0.1;2. 16 进制流格式是否正确(无乱码);3. 查看logs/error_*.log是否有数据源过滤(非故障注入 IP) 校验和检测失败 1. 发送数据的校验位是否与error_check.py的算法匹配;2. 数据是否包含校验位(长度是否足够) 多端口串包 1. CSV 配置中是否有重复端口;2. 工程udp_handler.py的_get_dev_by_port是否正确映射外设 ''' import socket import binascii import threading import time import sys import os import io # 设置标准输出和标准错误的编码为UTF-8 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') from pathlib import Path # 获取当前文件的目录 current_dir = os.path.dirname(os.path.abspath(__file__)) # 获取上级目录(项目根目录) parent_dir = os.path.dirname(current_dir) # 将项目根目录添加到系统路径 sys.path.append(parent_dir) #sys.path.append(str(Path(__file__).parent.parent.parent)) from midware.config.base_config import UDP_CONFIG class UDPTestClient: """UDP测试客户端:模拟故障注入软件,发送/接收UDP数据""" def __init__(self): self.local_ip = UDP_CONFIG["LOCAL_IP"] self.fault_port = UDP_CONFIG["FAULT_INJECT_PORT"] # 故障注入软件接收端口(客户端监听) self.buffer_size = UDP_CONFIG["BUFFER_SIZE"] # 发送套接字(向中间层外设端口发包) self.send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.send_sock.setblocking(False) # 接收套接字(监听故障注入端口,接收中间层回传) self.recv_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.recv_sock.bind((self.local_ip, self.fault_port)) self.recv_sock.setblocking(False) # 接收线程标识 self.recv_running = True # 启动接收线程(后台接收,不阻塞发包) self.recv_thread = threading.Thread(target=self._recv_loop, daemon=True) self.recv_thread.start() print(f"UDP测试客户端初始化完成 | 监听回传数据:{self.local_ip}:{self.fault_port}") def _recv_loop(self): """后台接收线程:接收中间层回传的16进制流数据""" while self.recv_running: try: data, addr = self.recv_sock.recvfrom(self.buffer_size) hex_data = data.decode("utf-8").strip() raw_data = binascii.unhexlify(hex_data) if hex_data else b"" print(f"\n📥 接收中间层回传数据 | 来源:{addr} | 16进制流:{hex_data[:64]}... | 原始数据长度:{len(raw_data)}字节") except BlockingIOError: time.sleep(0.001) continue except Exception as e: print(f"\n❌ 接收数据异常:{str(e)}") continue def send_hex_data(self, dev_port, raw_data): """ 向中间层指定外设端口发送16进制流数据(模拟故障注入软件发包) :param dev_port: 外设UDP端口(中间层监听端口) :param raw_data: 原始二进制数据,自动转为16进制流 """ try: # 原始数据转16进制流(符合工程数据格式要求) hex_data = binascii.hexlify(raw_data).decode("utf-8") self.send_sock.sendto(hex_data.encode("utf-8"), (self.local_ip, dev_port)) print(f"\n📤 发送数据成功 | 目标端口:{dev_port} | 16进制流:{hex_data[:64]}... | 原始数据长度:{len(raw_data)}字节") return True except Exception as e: print(f"\n❌ 发送数据失败 | 目标端口:{dev_port} | 异常:{str(e)}") return False def close(self): """关闭套接字,释放资源""" self.recv_running = False self.send_sock.close() self.recv_sock.close() print("\n✅ UDP测试客户端已关闭") # 多端口并行发送测试(线程版) def multi_port_send(client, port_data_list): """ 多端口并行发送数据,验证端口隔离性 :param port_data_list: 列表,元素为(端口, 原始数据) """ def send_task(port, data): client.send_hex_data(port, data) # 模拟不同单机的发包间隔 time.sleep(0.001) threads = [] for port, data in port_data_list: t = threading.Thread(target=send_task, args=(port, data), daemon=True) threads.append(t) t.start() # 等待所有线程完成 for t in threads: t.join() print("\n===== 多端口并行发送任务完成 =====") if __name__ == "__main__": # 初始化测试客户端 client = UDPTestClient() # 测试用原始数据(可自定义,建议包含校验位,适配error_check.py的校验逻辑) test_data1 = b"UART_TEST_DATA_001" + b'\x9c\x47' # UART数据+CRC16校验位 test_data2 = b"CAN_TEST_DATA_001" + b'\x1a\x2b' # CAN数据+CRC16校验位 test_data3 = b"1553B_TEST_DATA_001" + b'\x0f\x3d' # 1553B数据+CRC16校验位 test_data4 = b"AD_TEST_DATA_001" + b'\x5f' # AD数据+累加和校验位 test_data5 = b"OC_TEST_DATA_001" + b'\x6a' # OC数据+累加和校验位 try: # 1. 单端口发送测试(例:向Uart0(8880)发送数据) print("===== 单端口发送测试 =====") # client.send_hex_data(8880, test_data1) client.send_hex_data(4000, test_data1) time.sleep(1) # 2. 多端口并行发送测试(验证端口互不干扰) print("\n===== 多端口并行发送测试 =====") port_data = [ (4000, test_data1), (4001, test_data2), (4002, test_data3), (4003, test_data4), (4004, test_data1) ] multi_port_send(client, port_data) time.sleep(3) except KeyboardInterrupt: print("\n\n用户终止测试") finally: # 关闭客户端 client.close() time.sleep(1)