# -*- coding: utf-8 -*- import sys import os import io import time from pathlib import Path # ========== 修复点1:安全重定向标准流(避免直接覆盖导致关闭) ========== def safe_redirect_stdio(): """安全重定向stdout/stderr为UTF-8,兼容流未初始化/已关闭场景""" try: # 仅在流可用时重定向,使用原始buffer避免递归关闭 if sys.stdout and not sys.stdout.closed: sys.stdout = io.TextIOWrapper( sys.stdout.buffer, encoding='utf-8', line_buffering=True, # 行缓冲避免数据滞留 write_through=True # 立即写入避免句柄阻塞 ) if sys.stderr and not sys.stderr.closed: sys.stderr = io.TextIOWrapper( sys.stderr.buffer, encoding='utf-8', line_buffering=True, write_through=True ) except Exception as e: # 重定向失败时使用原始stdout输出,避免崩溃 print(f"标准流重定向警告: {e}", file=sys.__stdout__) # 先执行安全重定向(放在导入前,避免导入时输出乱码) safe_redirect_stdio() # ========== 修复点2:规范导入(避免重复/顺序错误) ========== from loguru import logger from midware.ui.main_ui import start_main_ui from midware.config.base_config import ( init_base_config, update_device_config, get_device_config ) from midware.config.excel_config import load_csv_config from midware.core.error_check import check_checksum, verify_frame_integrity from midware.network.udp_handler import MultiUDPServer from midware.network.protocol_dispatcher import dispatch_udp_data import midware.config.base_config as base_config # 添加工程根目录到环境变量(确保配置文件路径正确) PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__)) sys.path.append(PROJECT_ROOT) # 修正配置文件路径为绝对路径(避免相对路径导致文件找不到) CSV_CONFIG_PATH = os.path.join(PROJECT_ROOT, "config_files/device_config.csv") # ========== 修复点3:初始化日志(避免日志写入触发流错误) ========== def init_logger(): """初始化日志,使用原始stdout避免重定向流关闭问题""" # 清空默认日志配置 logger.remove() # 确保日志目录存在 Path("logs").mkdir(exist_ok=True) # 运行日志 logger.add( "logs/run_{time:YYYY-MM-DD}.log", rotation="daily", level="INFO", format="{time} | {level} | {module}:{line} | {message}", enqueue=True, # 异步写入避免句柄阻塞 catch=True # 捕获日志写入异常 ) # 错误日志 logger.add( "logs/error_{time:YYYY-MM-DD}.log", rotation="daily", level="ERROR", format="{time} | {level} | {module}:{line} | {message}", enqueue=True, catch=True ) # 控制台输出(关键:使用原始stdout,避免重定向流关闭报错) logger.add( sys.__stdout__, level="INFO", format="{time} | {level} | {module}:{line} | {message}", enqueue=True, catch=True ) return logger # ========== 主程序逻辑(核心修复:结构清晰+资源安全释放) ========== if __name__ == "__main__": # 初始化日志(最先执行,避免后续报错无日志) log = init_logger() log.info("虚拟仿真平台中间层软件启动") logger.info("========== 虚拟仿真平台中间层软件 启动 ==========") # ========== 修复点4:安全加载CSV配置(核心解决load_csv_config报错) ========== ''' try: # 加载CSV配置(使用绝对路径,避免文件找不到导致流异常) DEVICE_CONFIG_DICT = load_csv_config(CSV_CONFIG_PATH) if not DEVICE_CONFIG_DICT: logger.error(f"CSV配置文件加载为空!路径:{CSV_CONFIG_PATH}") sys.exit(1) logger.info(f"CSV配置加载成功|设备数量:{len(DEVICE_CONFIG_DICT)}") except Exception as e: logger.error(f"加载CSV配置失败:{str(e)}", exc_info=True) sys.exit(1) ''' # 初始化基础配置 try: init_base_config() device_config = get_device_config() if not device_config: logger.error("基础配置加载为空,工程无法启动!") sys.exit(1) logger.info("基础配置初始化成功") except Exception as e: logger.error(f"基础配置初始化失败:{str(e)}", exc_info=True) sys.exit(1) # 检查外设配置 if not base_config.DEVICE_CONFIG_DICT: logger.error("外设配置加载失败,工程启动终止") sys.exit(1) else: logger.info("外设配置加载成功") # 改用logger输出,避免print触发流错误 logger.info("程序运行到这里了") # 实例化UDP服务(避免global关键字,减少生命周期问题) udp_server = None try: udp_server = MultiUDPServer() logger.info( f"工程启动成功|外设数量:{len(device_config)}|" f"UDP监听端口:{list(udp_server.udp_sockets.keys()) if hasattr(udp_server, 'udp_sockets') else []}" ) except Exception as e: logger.error(f"UDP服务初始化失败:{str(e)}", exc_info=True) sys.exit(1) # 主循环:处理UDP数据 try: # 启动主UI(如需启用,取消注释) # start_main_ui() logger.info("进入UDP数据处理主循环...") while True: if udp_server is None: break # 非阻塞接收UDP数据 recv_data = udp_server.recv_multi_udp_hex() if recv_data: for data in recv_data: # 安全取值,避免KeyError port = data.get("port", "") dev_name = data.get("dev_name", "") protocol = data.get("protocol", "") raw_data = data.get("raw_data", b"") if len(raw_data) > 0: # 改用logger输出,避免print操作关闭的流 logger.info(f"接收到 {dev_name} 的原始数据:{raw_data}, 协议类型:{protocol}") logger.info("后续根据设备的协议类型进行数据解析和转发...") # 协议类型判断 if protocol == "UART": pass # 分发处理数据(修正:传入单条数据,而非整个列表) # dispatch_udp_data([data]) # 回传数据到故障注入软件 udp_server.send_to_fault(raw_data, dev_name) # 微休眠,降低CPU占用 time.sleep(1e-6) except KeyboardInterrupt: logger.info("用户终止工程,开始释放资源...") except Exception as e: logger.error(f"主循环异常:{str(e)}", exc_info=True) finally: # ========== 修复点5:安全释放资源(避免重复关闭/空对象调用) ========== try: # 关闭UDP服务(仅当对象存在且未关闭时) if udp_server and hasattr(udp_server, "close"): udp_server.close() logger.info("UDP服务已关闭") except Exception as e: logger.warning(f"UDP服务关闭警告:{str(e)}") # 刷新并关闭日志(避免日志句柄泄漏) logger.info("========== 虚拟仿真平台中间层软件 退出 ==========") logger.remove() # 清理所有日志句柄 # 安全退出 sys.exit(0)