Files
virtual_simulation_midware/main-20260228.py
2026-06-16 15:40:19 +08:00

199 lines
7.7 KiB
Python
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.
# -*- 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)