Files
virtual_simulation_midware/main-20260228.py

199 lines
7.7 KiB
Python
Raw Normal View History

2026-06-16 15:40:19 +08:00
# -*- 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)