first commit
This commit is contained in:
37
midware/drivers/__init__.py
Normal file
37
midware/drivers/__init__.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# midware/drivers/__init__.py
|
||||
"""
|
||||
总线驱动模块:统一管理所有总线驱动的导入和初始化
|
||||
"""
|
||||
from .base_driver import BaseBusDriver
|
||||
from .bus_1553b import Bus1553BDriver
|
||||
from .bus_can import BusCANDriver
|
||||
from .bus_uart import BusUARTDriver
|
||||
from .bus_ad import BusADDriver
|
||||
from .bus_oc import BusOCDriver
|
||||
from .bus_network import BusNetworkDriver
|
||||
from loguru import logger
|
||||
|
||||
# 驱动映射表:协议名称 → 驱动类(与bus_udp_decoupler.py中的协议名对齐)
|
||||
DRIVER_MAPPING = {
|
||||
"1553B": Bus1553BDriver,
|
||||
"CAN": BusCANDriver,
|
||||
"UART": BusUARTDriver,
|
||||
"AD": BusADDriver,
|
||||
"OC": BusOCDriver,
|
||||
"网络": BusNetworkDriver
|
||||
}
|
||||
|
||||
def create_bus_driver(protocol: str, config: dict = None) -> BaseBusDriver:
|
||||
"""
|
||||
工厂函数:根据协议名称创建驱动实例
|
||||
:param protocol: 协议名称(如"1553B")
|
||||
:param config: 驱动配置
|
||||
:return: 驱动实例
|
||||
"""
|
||||
if protocol not in DRIVER_MAPPING:
|
||||
raise ValueError(f"未知协议 {protocol},无对应驱动")
|
||||
|
||||
driver_class = DRIVER_MAPPING[protocol]
|
||||
driver = driver_class(config=config)
|
||||
logger.info(f"创建 {protocol} 驱动实例成功:{driver.__class__.__name__}")
|
||||
return driver
|
||||
BIN
midware/drivers/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/base_driver.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/base_driver.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/bus_1553b.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/bus_1553b.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/bus_ad.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/bus_ad.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/bus_can.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/bus_can.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/bus_network.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/bus_network.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/bus_oc.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/bus_oc.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/bus_uart.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/bus_uart.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/renode_agent.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/renode_agent.cpython-311.pyc
Normal file
Binary file not shown.
BIN
midware/drivers/__pycache__/tcp_uart_agent.cpython-311.pyc
Normal file
BIN
midware/drivers/__pycache__/tcp_uart_agent.cpython-311.pyc
Normal file
Binary file not shown.
78
midware/drivers/base_driver.py
Normal file
78
midware/drivers/base_driver.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# midware/drivers/base_driver.py
|
||||
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
驱动基类(base_driver.py)—— 定义标准接口(核心)
|
||||
所有总线驱动继承该基类,保证接口统一,解耦 bus_udp_decoupler.py 与具体驱动实现:
|
||||
"""
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
|
||||
class BaseBusDriver(ABC):
|
||||
"""
|
||||
总线驱动基类:定义所有总线必须实现的标准接口
|
||||
遵循「开闭原则」:新增总线只需继承该类实现接口,无需修改现有代码
|
||||
"""
|
||||
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
||||
"""
|
||||
初始化驱动
|
||||
:param config: 驱动配置(如波特率、端口、IP等)
|
||||
"""
|
||||
self.config = config or {}
|
||||
self.is_connected = False # 驱动连接状态
|
||||
self._init_config() # 初始化配置
|
||||
|
||||
def _init_config(self):
|
||||
"""初始化默认配置(子类可重写)"""
|
||||
logger.info(f"初始化 {self.__class__.__name__} 默认配置")
|
||||
|
||||
@abstractmethod
|
||||
def connect(self) -> bool:
|
||||
"""
|
||||
连接总线(如打开串口、建立CAN通道、连接1553B板卡)
|
||||
:return: 连接成功返回True,失败返回False
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def disconnect(self) -> bool:
|
||||
"""
|
||||
断开总线连接
|
||||
:return: 断开成功返回True,失败返回False
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
"""
|
||||
发送数据到总线
|
||||
:param data: 待发送的原始字节数据
|
||||
:param kwargs: 扩展参数(如优先级、地址、帧ID等)
|
||||
:return: 发送成功返回True,失败返回False
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
"""
|
||||
从总线接收数据
|
||||
:param timeout: 接收超时时间(秒)
|
||||
:param kwargs: 扩展参数(如过滤条件、地址等)
|
||||
:return: 接收到的字节数据,超时/失败返回None
|
||||
"""
|
||||
pass
|
||||
|
||||
def check_status(self) -> bool:
|
||||
"""
|
||||
检查驱动状态(默认实现,子类可重写)
|
||||
:return: 驱动正常返回True,异常返回False
|
||||
"""
|
||||
return self.is_connected
|
||||
|
||||
def __del__(self):
|
||||
"""析构函数:自动断开连接"""
|
||||
if self.is_connected:
|
||||
self.disconnect()
|
||||
logger.info(f"{self.__class__.__name__} 自动断开连接")
|
||||
100
midware/drivers/bus_1553b.py
Normal file
100
midware/drivers/bus_1553b.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
|
||||
2. 具体驱动实现(以 1553B 为例,bus_1553b.py)
|
||||
其他总线(CAN/UART/AD 等)按相同逻辑实现,仅需重写基类接口:
|
||||
"""
|
||||
# midware/drivers/bus_1553b.py
|
||||
from .base_driver import BaseBusDriver
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
import time
|
||||
|
||||
class Bus1553BDriver(BaseBusDriver):
|
||||
"""1553B总线驱动实现(适配实际1553B板卡/仿真器)"""
|
||||
def _init_config(self):
|
||||
"""初始化1553B默认配置"""
|
||||
# 默认配置(可从yaml文件加载)
|
||||
self.config.setdefault("board_id", 0) # 板卡ID
|
||||
self.config.setdefault("bc_address", 0x01) # BC地址
|
||||
self.config.setdefault("rt_address", 0x02) # RT地址
|
||||
self.config.setdefault("baudrate", 1000000) # 波特率1Mbps
|
||||
logger.info(f"1553B驱动配置初始化完成:{self.config}")
|
||||
|
||||
def connect(self) -> bool:
|
||||
"""连接1553B板卡/仿真器"""
|
||||
try:
|
||||
# 实际逻辑:调用1553B板卡SDK的连接接口
|
||||
# 示例:self.board = SDK_1553B.connect(board_id=self.config["board_id"])
|
||||
time.sleep(0.1) # 模拟连接耗时
|
||||
self.is_connected = True
|
||||
logger.info("1553B总线连接成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"1553B总线连接失败:{str(e)}")
|
||||
self.is_connected = False
|
||||
return False
|
||||
|
||||
def disconnect(self) -> bool:
|
||||
"""断开1553B连接"""
|
||||
try:
|
||||
# 实际逻辑:调用SDK的断开接口
|
||||
# self.board.disconnect()
|
||||
self.is_connected = False
|
||||
logger.info("1553B总线断开成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"1553B总线断开失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
"""发送1553B数据帧"""
|
||||
if not self.is_connected:
|
||||
logger.error("1553B驱动未连接,发送失败")
|
||||
return False
|
||||
|
||||
try:
|
||||
# 扩展参数:优先级、子地址等
|
||||
priority = kwargs.get("priority", 1) # 1553B最高优先级
|
||||
sub_address = kwargs.get("sub_address", 0x00)
|
||||
|
||||
# 实际逻辑:调用SDK发送1553B帧
|
||||
logger.info(
|
||||
f"1553B发送数据|优先级:{priority}|子地址:{sub_address}|"
|
||||
f"数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
# self.board.send_frame(
|
||||
# bc_addr=self.config["bc_address"],
|
||||
# rt_addr=self.config["rt_address"],
|
||||
# sub_addr=sub_address,
|
||||
# data=data,
|
||||
# priority=priority
|
||||
# )
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"1553B数据发送失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
"""接收1553B数据帧"""
|
||||
if not self.is_connected:
|
||||
logger.error("1553B驱动未连接,接收失败")
|
||||
return None
|
||||
|
||||
try:
|
||||
# 实际逻辑:调用SDK接收1553B帧
|
||||
# frame = self.board.recv_frame(timeout=timeout)
|
||||
# if frame:
|
||||
# return frame.data
|
||||
# return None
|
||||
|
||||
# 模拟接收(替换为真实逻辑)
|
||||
time.sleep(0.001) # 模拟接收耗时
|
||||
# mock_data = b"\x01\x02\x03\x04\x05" # 模拟1553B数据
|
||||
# logger.debug(f"1553B接收数据:{mock_data.hex()}")
|
||||
#return mock_data
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"1553B数据接收失败:{str(e)}")
|
||||
return None
|
||||
190
midware/drivers/bus_ad.py
Normal file
190
midware/drivers/bus_ad.py
Normal file
@@ -0,0 +1,190 @@
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
|
||||
AD 驱动示例(bus_ad.py)—— 补充参考
|
||||
"""
|
||||
# midware/drivers/bus_can.py
|
||||
from .base_driver import BaseBusDriver
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
import time
|
||||
# 导入全局配置字典(需确保main.py已提前加载CSV配置)
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT, get_device_config
|
||||
import midware.config.base_config as base_config
|
||||
|
||||
from .base_driver import BaseBusDriver
|
||||
from .renode_agent import renode
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
class BusADDriver(BaseBusDriver):
|
||||
"""uart总线驱动实现(适配)"""
|
||||
def _init_config(self):
|
||||
self.config.setdefault("channel", 0) # CAN通道
|
||||
self.config.setdefault("baudrate", 500000) # 500kbps
|
||||
self.config.setdefault("frame_format", "standard") # 标准帧/扩展帧
|
||||
logger.info(f"UART驱动配置初始化完成:{self.config}")
|
||||
|
||||
def connect(self) -> bool:
|
||||
try:
|
||||
# 实际逻辑:打开CAN通道
|
||||
time.sleep(0.05)
|
||||
self.is_connected = True
|
||||
logger.info("CAN总线连接成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线连接失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def disconnect(self) -> bool:
|
||||
try:
|
||||
self.is_connected = False
|
||||
logger.info("CAN总线断开成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线断开失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,发送失败")
|
||||
return False
|
||||
'''
|
||||
try:
|
||||
frame_id = kwargs.get("frame_id", 0x123) # CAN帧ID
|
||||
is_extended = kwargs.get("is_extended", False) # 是否扩展帧
|
||||
logger.info(
|
||||
f"CAN发送数据|帧ID:0x{frame_id:X}|扩展帧:{is_extended}|"
|
||||
f"数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
# 实际逻辑:调用AD驱动发送接口
|
||||
# 获取优先级,设备参数,地址等参数
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据发送失败:{str(e)}")
|
||||
return False
|
||||
'''
|
||||
# ========== 核心步骤1:从kwargs提取设备名称 ==========
|
||||
#dev_name = kwargs.get("dev_name")[0]
|
||||
dev_name = kwargs.get("dev_name")#2026/4/10
|
||||
if not dev_name:
|
||||
logger.error("AD发送失败:kwargs中未传入dev_name(设备名称)")
|
||||
return False
|
||||
|
||||
# ========== 核心步骤2:从全局配置字典查找设备参数 ==========
|
||||
# 检查全局配置是否加载
|
||||
if not base_config.DEVICE_CONFIG_DICT:
|
||||
logger.error("AD发送失败:全局配置DEVICE_CONFIG_DICT未加载")
|
||||
return False
|
||||
|
||||
# 查找当前设备的配置
|
||||
|
||||
#device_config = base_config.DEVICE_CONFIG_DICT.get(dev_name)
|
||||
device_config = get_device_config().get(dev_name)
|
||||
if not device_config:
|
||||
logger.error(f"AD发送失败:设备 {dev_name} 未在DEVICE_CONFIG_DICT中配置")
|
||||
return False
|
||||
|
||||
# 提取关键配置项(兼容配置项缺失的情况)
|
||||
try:
|
||||
# 内存基地址(转为16进制整数,如"0x80000000" → 0x80000000)
|
||||
# mem_base_addr = int(str(device_config.get("base_addr", "0x0")), 16)#mem_base_addr = int(device_config.get("base_addr", "0x0"), 16)#内存基地址
|
||||
mem_base_addr = int(device_config.get("base_addr", 0))
|
||||
# 内存偏移地址
|
||||
#mem_offset = int(str(device_config.get("offset_addr", "0x0")), 16)#内存偏移地址
|
||||
mem_offset = int(device_config.get("offset_addr", 0))
|
||||
# 发送缓存区大小(字节)
|
||||
send_buffer_size = int(str(device_config.get("send_cache", 1024)))#发送缓存区大小
|
||||
# 接收缓存区大小(字节)
|
||||
recv_buffer_size = int(device_config.get("recv_cache", 1024))#接收缓存区大小
|
||||
except (ValueError, TypeError) as e:
|
||||
logger.error(f"AD发送失败:{dev_name} 配置参数解析错误 → {str(e)}")
|
||||
return False
|
||||
|
||||
# ========== 核心步骤3:使用配置项处理发送逻辑 ==========
|
||||
# 1. 校验数据长度不超过发送缓存区大小
|
||||
if len(data) > send_buffer_size:
|
||||
logger.error(
|
||||
f"AD发送失败:{dev_name} 数据长度 {len(data)} 超过发送缓存区大小 {send_buffer_size}"
|
||||
)
|
||||
return False
|
||||
|
||||
# 2. 模拟AD指令发送(结合内存地址)
|
||||
logger.info(
|
||||
f"AD发送指令|设备:{dev_name}|"
|
||||
f"内存基地址:0x{mem_base_addr:X}|偏移地址:0x{mem_offset:X}|"
|
||||
f"发送缓存区:{send_buffer_size}B|接收缓存区:{recv_buffer_size}B|"
|
||||
f"数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
|
||||
# 实际业务逻辑:
|
||||
# - 将数据写入指定内存地址(mem_base_addr + mem_offset)
|
||||
# - 配置接收缓存区大小
|
||||
# - 调用AD采集卡SDK发送指令
|
||||
# ad_card.write_mem(mem_base_addr + mem_offset, data, send_buffer_size)
|
||||
|
||||
#AD0-AD6的寄存器写入策略
|
||||
if(dev_name == "正电压采集热敏量采集"):
|
||||
for i in range(4):
|
||||
logger.info(f"AD发送指令:{i}")
|
||||
for j in range(14):
|
||||
logger.info(f"AD发送指令:{j}")
|
||||
target_addr = mem_base_addr + mem_offset + i * 32 + j*8
|
||||
canshu = data[2*(i*14+j)]*256 + data[2*(i*14+j)+1]# 2字节
|
||||
sysbus_cmd = f"sysbus WriteDoubleWord {target_addr:#010x} {canshu:#006x}"
|
||||
print(sysbus_cmd)
|
||||
pass
|
||||
|
||||
## 外设名称+函数名+字符串参数 adc1 LoadRegistersFromHexString "55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA55AA"'
|
||||
ad_cmd = f'{dev_name} LoadRegistersFromHexString "{data.hex()}"'
|
||||
print(ad_cmd)
|
||||
logger.info(f"AD发送指令:{ad_cmd}")
|
||||
response = renode.send_sync(ad_cmd,10)#2026/4/10
|
||||
ad_cmd2 = f'{dev_name} ReadAllChannelsToString02'
|
||||
logger.info(f"AD发送指令:{ad_cmd2}")
|
||||
response = renode.send_sync(ad_cmd2,10)#2026/4/10
|
||||
|
||||
return True
|
||||
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
if not self.is_connected:
|
||||
logger.error("ADC驱动未连接,接收失败")
|
||||
return None
|
||||
|
||||
try:
|
||||
# 模拟接收
|
||||
#遍历所有AD外设,循环发送读取指令:例如 adc1 ReadAllChannelsToString02
|
||||
#接收renode回复
|
||||
time.sleep(0.51)
|
||||
'''
|
||||
mock_data = b"\x11\x22\x33\x44"
|
||||
logger.debug(f"AD接收数据:{mock_data.hex()}")
|
||||
|
||||
#2026/4/10
|
||||
dev_name = kwargs.get("dev_name")
|
||||
if not dev_name:
|
||||
logger.error("AD接收失败:kwargs中未传入dev_name(设备名称)")
|
||||
return None
|
||||
|
||||
cfg = DEVICE_CONFIG_DICT[dev_name]
|
||||
base = int(cfg["内存基地址"], 16)
|
||||
offset = int(cfg["内存偏移地址"], 16)
|
||||
addr = base + offset
|
||||
|
||||
# ======================
|
||||
# 调用 Renode 读取数据
|
||||
# ======================
|
||||
cmd = f"read 0x{addr:X}"
|
||||
hex_str = renode.send_sync(cmd)
|
||||
#return bytes.fromhex(hex_str.strip())
|
||||
logger.info(f"AD接收数据:{mock_data.hex()}")
|
||||
return mock_data
|
||||
'''
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"AD数据接收失败:{str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
|
||||
133
midware/drivers/bus_can.py
Normal file
133
midware/drivers/bus_can.py
Normal file
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
|
||||
CAN 驱动示例(bus_can.py)—— 补充参考
|
||||
"""
|
||||
# midware/drivers/bus_can.py
|
||||
from .base_driver import BaseBusDriver
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
import time
|
||||
|
||||
from midware.config.base_config import get_device_config
|
||||
from .base_driver import BaseBusDriver
|
||||
from .renode_agent import renode
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
class BusCANDriver(BaseBusDriver):
|
||||
"""CAN总线驱动实现(适配CANoe/CANalyzer/USB-CAN)"""
|
||||
def _init_config(self):
|
||||
self.config.setdefault("channel", 0) # CAN通道
|
||||
self.config.setdefault("baudrate", 500000) # 500kbps
|
||||
self.config.setdefault("frame_format", "standard") # 标准帧/扩展帧
|
||||
logger.info(f"CAN驱动配置初始化完成:{self.config}")
|
||||
|
||||
def connect(self) -> bool:
|
||||
try:
|
||||
# 实际逻辑:打开CAN通道
|
||||
time.sleep(0.05)
|
||||
self.is_connected = True
|
||||
logger.info("CAN总线连接成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线连接失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def disconnect(self) -> bool:
|
||||
try:
|
||||
self.is_connected = False
|
||||
logger.info("CAN总线断开成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线断开失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,发送失败")
|
||||
return False
|
||||
# ========== 核心步骤1:从kwargs提取设备名称 ==========
|
||||
|
||||
dev_name = kwargs.get("dev_name")#2026/4/10
|
||||
if not dev_name:
|
||||
logger.error("CAN发送失败:kwargs中未传入dev_name(设备名称)")
|
||||
return False
|
||||
|
||||
device_config = get_device_config().get(dev_name)
|
||||
if not device_config:
|
||||
logger.error(f"CAN发送失败:设备 {dev_name} 未在DEVICE_CONFIG_DICT中配置")
|
||||
return False
|
||||
|
||||
try:
|
||||
frame_id = kwargs.get("frame_id", 0x123) # CAN帧ID
|
||||
is_extended = kwargs.get("is_extended", False) # 是否扩展帧
|
||||
logger.info(
|
||||
f"CAN发送数据|帧ID:0x{frame_id:X}|扩展帧:{is_extended}|"
|
||||
f"数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
# logger.info(
|
||||
# #f"UART发送数据|帧ID:0x{frame_id:X}|扩展帧:{is_extended}|"
|
||||
# f"CAN发送数据:{data.hex()}|长度:{len(data)}字节"
|
||||
# )
|
||||
## 外设名称+函数名+字符串参数 uart24 WriteRXFIFODataString "0xEB9000C571000827C00000BC1003190000106C02AC020101FF5716"'
|
||||
|
||||
## 收到的CAN帧可能是多帧连在一起,需要拆分单帧保证虚拟平台能读取。
|
||||
frame_len = 11
|
||||
frame_list = []
|
||||
total =len(data)
|
||||
#步长11字节截取完整帧,不满足的丢弃(尾帧长度可以不满11,不丢弃)
|
||||
end = 0
|
||||
for start in range(0,total, frame_len):
|
||||
'''
|
||||
if end>total:
|
||||
break
|
||||
end = start + frame_len
|
||||
frame_list.append(data[start:end])
|
||||
sysbus_cmd = f'{dev_name} SendRxBufferDataString "0x{data[start:end].hex()}"'
|
||||
#print(sysbus_cmd)
|
||||
logger.info(f"CAN发送指令:{sysbus_cmd}")
|
||||
response = renode.send_sync(sysbus_cmd)#2026/4/10
|
||||
time.sleep(0.0001)
|
||||
'''
|
||||
#计算当前帧结束的位置
|
||||
end = min(start + frame_len, total)
|
||||
frame = data[start:end]
|
||||
#尾帧长度不足11时,补0到11字节
|
||||
if len(frame)<frame_len:
|
||||
#补0
|
||||
frame = frame.ljust(frame_len, b'\x00')
|
||||
frame_list.append(frame)
|
||||
sysbus_cmd = f'{dev_name} SendRxBufferDataString "0x{frame.hex()}"'
|
||||
logger.info(f"CAN发送指令:{sysbus_cmd}")
|
||||
response = renode.send_sync(sysbus_cmd,timeout_ms=2)#2026/4/10
|
||||
# response = renode.enqueue_cmd(sysbus_cmd)#2026/6/4
|
||||
time.sleep(0.0001)
|
||||
|
||||
|
||||
#sysbus_cmd = f'{dev_name} SendRxBufferDataString "0x{data.hex()}"'
|
||||
#print(sysbus_cmd)
|
||||
#logger.info(f"CAN发送指令:{sysbus_cmd}")
|
||||
#response = renode.send_sync(sysbus_cmd)#2026/4/10
|
||||
|
||||
# 实际逻辑:调用CAN驱动发送接口
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据发送失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,接收失败")
|
||||
return None
|
||||
|
||||
try:
|
||||
# 模拟接收
|
||||
time.sleep(0.001)
|
||||
# mock_data = b"\x11\x22\x33\x44"
|
||||
# logger.debug(f"CAN接收数据:{mock_data.hex()}")
|
||||
# return mock_data
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据接收失败:{str(e)}")
|
||||
return None
|
||||
73
midware/drivers/bus_network.py
Normal file
73
midware/drivers/bus_network.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
|
||||
network 驱动示例(bus_network .py)—— 补充参考
|
||||
"""
|
||||
# midware/drivers/bus_can.py
|
||||
from .base_driver import BaseBusDriver
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
import time
|
||||
|
||||
class BusNetworkDriver(BaseBusDriver):
|
||||
"""network 总线驱动实现(适配)"""
|
||||
def _init_config(self):
|
||||
self.config.setdefault("channel", 0) # CAN通道
|
||||
self.config.setdefault("baudrate", 500000) # 500kbps
|
||||
self.config.setdefault("frame_format", "standard") # 标准帧/扩展帧
|
||||
logger.info(f"UART驱动配置初始化完成:{self.config}")
|
||||
|
||||
def connect(self) -> bool:
|
||||
try:
|
||||
# 实际逻辑:打开CAN通道
|
||||
time.sleep(0.05)
|
||||
self.is_connected = True
|
||||
logger.info("CAN总线连接成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线连接失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def disconnect(self) -> bool:
|
||||
try:
|
||||
self.is_connected = False
|
||||
logger.info("CAN总线断开成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线断开失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,发送失败")
|
||||
return False
|
||||
|
||||
try:
|
||||
frame_id = kwargs.get("frame_id", 0x123) # CAN帧ID
|
||||
is_extended = kwargs.get("is_extended", False) # 是否扩展帧
|
||||
logger.info(
|
||||
f"CAN发送数据|帧ID:0x{frame_id:X}|扩展帧:{is_extended}|"
|
||||
f"数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
# 实际逻辑:调用CAN驱动发送接口
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据发送失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,接收失败")
|
||||
return None
|
||||
|
||||
try:
|
||||
# 模拟接收
|
||||
time.sleep(0.001)
|
||||
# mock_data = b"\x11\x22\x33\x44"
|
||||
# logger.debug(f"CAN接收数据:{mock_data.hex()}")
|
||||
# return mock_data
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据接收失败:{str(e)}")
|
||||
return None
|
||||
73
midware/drivers/bus_oc.py
Normal file
73
midware/drivers/bus_oc.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
|
||||
oc 驱动示例(bus_oc.py)—— 补充参考
|
||||
"""
|
||||
# midware/drivers/bus_can.py
|
||||
from .base_driver import BaseBusDriver
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
import time
|
||||
|
||||
class BusOCDriver(BaseBusDriver):
|
||||
"""uart总线驱动实现(适配)"""
|
||||
def _init_config(self):
|
||||
self.config.setdefault("channel", 0) # CAN通道
|
||||
self.config.setdefault("baudrate", 500000) # 500kbps
|
||||
self.config.setdefault("frame_format", "standard") # 标准帧/扩展帧
|
||||
logger.info(f"UART驱动配置初始化完成:{self.config}")
|
||||
|
||||
def connect(self) -> bool:
|
||||
try:
|
||||
# 实际逻辑:打开CAN通道
|
||||
time.sleep(0.05)
|
||||
self.is_connected = True
|
||||
logger.info("CAN总线连接成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线连接失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def disconnect(self) -> bool:
|
||||
try:
|
||||
self.is_connected = False
|
||||
logger.info("CAN总线断开成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线断开失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,发送失败")
|
||||
return False
|
||||
|
||||
try:
|
||||
frame_id = kwargs.get("frame_id", 0x123) # CAN帧ID
|
||||
is_extended = kwargs.get("is_extended", False) # 是否扩展帧
|
||||
logger.info(
|
||||
f"CAN发送数据|帧ID:0x{frame_id:X}|扩展帧:{is_extended}|"
|
||||
f"数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
# 实际逻辑:调用CAN驱动发送接口
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据发送失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
if not self.is_connected:
|
||||
logger.error("CAN驱动未连接,接收失败")
|
||||
return None
|
||||
|
||||
try:
|
||||
# 模拟接收
|
||||
time.sleep(0.001)
|
||||
# mock_data = b"\x11\x22\x33\x44"
|
||||
# logger.debug(f"CAN接收数据:{mock_data.hex()}")
|
||||
# return mock_data
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"CAN数据接收失败:{str(e)}")
|
||||
return None
|
||||
131
midware/drivers/bus_uart.py
Normal file
131
midware/drivers/bus_uart.py
Normal file
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
Tan mingyan
|
||||
2026/3/9
|
||||
|
||||
uart 驱动示例(bus_can.py)—— 补充参考
|
||||
"""
|
||||
# midware/drivers/bus_can.py
|
||||
from midware.config.base_config import get_device_config
|
||||
from .base_driver import BaseBusDriver
|
||||
from typing import Optional, Dict, Any
|
||||
from loguru import logger
|
||||
import time
|
||||
|
||||
from .base_driver import BaseBusDriver
|
||||
from .renode_agent import renode
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
class BusUARTDriver(BaseBusDriver):
|
||||
"""uart总线驱动实现(适配)"""
|
||||
def _init_config(self):
|
||||
self.config.setdefault("channel", 0) # CAN通道
|
||||
self.config.setdefault("baudrate", 500000) # 500kbps
|
||||
self.config.setdefault("frame_format", "standard") # 标准帧/扩展帧
|
||||
logger.info(f"UART驱动配置初始化完成:{self.config}")
|
||||
|
||||
def connect(self) -> bool:
|
||||
try:
|
||||
# 实际逻辑:打开CAN通道
|
||||
time.sleep(0.05)
|
||||
self.is_connected = True
|
||||
logger.info("CAN总线连接成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线连接失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def disconnect(self) -> bool:
|
||||
try:
|
||||
self.is_connected = False
|
||||
logger.info("CAN总线断开成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"CAN总线断开失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def send(self, data: bytes, **kwargs) -> bool:
|
||||
if not self.is_connected:
|
||||
logger.error("UART驱动未连接,发送失败")
|
||||
return False
|
||||
# 查找当前设备的配置
|
||||
|
||||
# ========== 核心步骤1:从kwargs提取设备名称 ==========
|
||||
|
||||
dev_name = kwargs.get("dev_name")#2026/4/10
|
||||
if not dev_name:
|
||||
logger.error("UART发送失败:kwargs中未传入dev_name(设备名称)")
|
||||
return False
|
||||
|
||||
device_config = get_device_config().get(dev_name)
|
||||
if not device_config:
|
||||
logger.error(f"UART发送失败:设备 {dev_name} 未在DEVICE_CONFIG_DICT中配置")
|
||||
return False
|
||||
|
||||
try:
|
||||
#frame_id = kwargs.get("frame_id", 0x123) # CAN帧ID
|
||||
#is_extended = kwargs.get("is_extended", False) # 是否扩展帧
|
||||
logger.info(
|
||||
#f"UART发送数据|帧ID:0x{frame_id:X}|扩展帧:{is_extended}|"
|
||||
f"UART发送数据:{data.hex()}|长度:{len(data)}字节"
|
||||
)
|
||||
## 外设名称+函数名+字符串参数 uart24 WriteRXFIFODataString "0xEB9000C571000827C00000BC1003190000106C02AC020101FF5716"'
|
||||
|
||||
sysbus_cmd = f'{dev_name} WriteRXFIFODataString "0x{data.hex()}"'
|
||||
|
||||
#sysbus_cmd = f'can_a GetTxBufferDataString'
|
||||
print(sysbus_cmd)
|
||||
logger.info(f"UART发送指令:{sysbus_cmd}")
|
||||
response = renode.send_sync(sysbus_cmd,timeout_ms=20)#2026/4/10
|
||||
#response = renode.enqueue_cmd(sysbus_cmd)#2026/6/4
|
||||
# sysbus_cmd = f'uart24 GetTXFIFODataString'
|
||||
# print(sysbus_cmd)
|
||||
# response = renode.send_sync(sysbus_cmd)#2026/4/10
|
||||
# 实际逻辑:调用UART驱动发送接口
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"UART数据发送失败:{str(e)}")
|
||||
return False
|
||||
|
||||
def recv(self, timeout: float = 0.1, **kwargs) -> Optional[bytes]:
|
||||
if not self.is_connected:
|
||||
logger.error("UART驱动未连接,接收失败")
|
||||
return None
|
||||
|
||||
try:
|
||||
|
||||
#遍历所有UART外设,循环发送读取指令:例如 adc1 ReadAllChannelsToString02
|
||||
|
||||
# 模拟接收
|
||||
time.sleep(0.91)
|
||||
# mock_data = b"\x11\x22\x33\x44"
|
||||
# logger.debug(f"CAN接收数据:{mock_data.hex()}")
|
||||
#logger.info(f"CAN接收数据:")
|
||||
# return mock_data
|
||||
'''
|
||||
mock_data = b"\x11\x22\x33\x44"
|
||||
logger.debug(f"AD接收数据:{mock_data.hex()}")
|
||||
|
||||
#2026/4/10
|
||||
dev_name = kwargs.get("dev_name")
|
||||
if not dev_name:
|
||||
logger.error("AD接收失败:kwargs中未传入dev_name(设备名称)")
|
||||
return None
|
||||
|
||||
cfg = DEVICE_CONFIG_DICT[dev_name]
|
||||
base = int(cfg["内存基地址"], 16)
|
||||
offset = int(cfg["内存偏移地址"], 16)
|
||||
addr = base + offset
|
||||
|
||||
# ======================
|
||||
# 调用 Renode 读取数据
|
||||
# ======================
|
||||
cmd = f"read 0x{addr:X}"
|
||||
hex_str = renode.send_sync(cmd)
|
||||
#return bytes.fromhex(hex_str.strip())
|
||||
logger.info(f"AD接收数据:{mock_data.hex()}")
|
||||
#return mock_data
|
||||
'''
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"UART数据接收失败:{str(e)}")
|
||||
return None
|
||||
528
midware/drivers/renode_agent.py
Normal file
528
midware/drivers/renode_agent.py
Normal file
@@ -0,0 +1,528 @@
|
||||
"""
|
||||
2026/4/10
|
||||
TMY
|
||||
在connect_renode_19.py的基础上,添加单例模式,并添加总线类型映射
|
||||
"""
|
||||
# midware/drivers/renode_agent.py
|
||||
import threading
|
||||
import binascii
|
||||
import subprocess
|
||||
import time
|
||||
import threading
|
||||
import re
|
||||
import statistics
|
||||
from typing import Optional, Callable, List, Dict
|
||||
from loguru import logger
|
||||
|
||||
# ======================
|
||||
# 这里放你原来 connect_renode_19.py 的全部代码
|
||||
# 只加 2 个东西:单例 + 总线类型映射
|
||||
# ======================
|
||||
# from . import DRIVER_MAPPING
|
||||
# from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
BUS_TYPE_MAP = {
|
||||
0x11: "UART",
|
||||
0x22: "CAN",
|
||||
0x33: "1553B",
|
||||
0x44: "NET",
|
||||
0x55: "AD",
|
||||
0x66: "OC",
|
||||
}
|
||||
# 合法的总线类型第一个字节(字符串形式,方便判断)
|
||||
LEGAL_BUS_PREFIX = {"11", "22", "33", "44", "55", "66"}
|
||||
|
||||
class RenodeAgent:
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self,
|
||||
renode_path="renode",
|
||||
script_path=None,
|
||||
max_queue=50, # 指令队列最大长度(限流)
|
||||
reconnect_retries=5, # 自动重连次数
|
||||
socket_timeout=3):
|
||||
if hasattr(self, "inited"):
|
||||
return
|
||||
self.inited = True
|
||||
|
||||
# ========= 你的原有代码 =========
|
||||
# self.process = ...
|
||||
# self.async_queue = ...
|
||||
# self.start_renode()
|
||||
# self.start_async_listener()
|
||||
# 基础配置
|
||||
self.renode_path = renode_path
|
||||
self.script_path = script_path
|
||||
self.process: Optional[subprocess.Popen] = None
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# 输出与异步
|
||||
self.output_buffer: List[str] = []
|
||||
self.async_running = False
|
||||
self.async_callback: Optional[Callable[[str], None]] = None
|
||||
|
||||
# 限流队列
|
||||
self.max_queue = max_queue
|
||||
self.cmd_queue: List[str] = []
|
||||
self.busy = False
|
||||
|
||||
# 自动重连
|
||||
self.reconnect_retries = reconnect_retries
|
||||
self.connected = False
|
||||
|
||||
# 时延统计
|
||||
self.latency_stats: Dict[str, List[float]] = {
|
||||
"send": [], "process": [], "total": []
|
||||
}
|
||||
|
||||
# ====================== 超强过滤规则 ======================
|
||||
self.filter_re = re.compile(
|
||||
r"\(.*?\)" # 过滤 (machine) (TestPlatform) 等
|
||||
r"|\d{2}:\d{2}:\d{2}\.\d+" # 过滤时间 20:15:26.1234
|
||||
r"|renode>|\$|->" # 过滤提示符
|
||||
r"|\x1b\[[0-9;]*m" # 过滤颜色码
|
||||
r"|^\s*$" # 过滤空行
|
||||
)
|
||||
|
||||
self.last_send_t =0.0
|
||||
self.send_min_gap = 0.01 #控制下发间隔
|
||||
|
||||
'''
|
||||
# 你原来的函数,完全不变
|
||||
def send_sync(self, cmd: str, timeout=1.0):
|
||||
# 你的原有逻辑
|
||||
pass
|
||||
|
||||
def enqueue_cmd(self, cmd: str):
|
||||
# 你的原有逻辑
|
||||
pass
|
||||
|
||||
def start_async_listener(self, callback):
|
||||
# 你的原有异步监听
|
||||
pass
|
||||
'''
|
||||
def start(self) -> bool:
|
||||
try:
|
||||
args = [
|
||||
self.renode_path,
|
||||
"--console",
|
||||
#"--disable-ansi-colors",
|
||||
"-e", "set echo off; set line-editing off; set raw on"
|
||||
]
|
||||
if self.script_path:
|
||||
args.extend(["-e", f"include @{self.script_path}"])
|
||||
#args.extend(["-e", f"{self.script_path}"])
|
||||
|
||||
self.process = subprocess.Popen(
|
||||
args,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
bufsize=0,
|
||||
universal_newlines=True,
|
||||
creationflags=subprocess.CREATE_NEW_CONSOLE #LWB
|
||||
)
|
||||
|
||||
self.async_running = True
|
||||
self.connected = True
|
||||
threading.Thread(target=self._read_thread, daemon=True).start()
|
||||
threading.Thread(target=self._queue_worker, daemon=True).start()
|
||||
time.sleep(1)
|
||||
self.clear_buffers()
|
||||
print("✅ 仿真平台 启动成功")
|
||||
|
||||
sysbus_cmd = "logLevel 3" #3级-只显示error
|
||||
response = renode.send_sync(sysbus_cmd)
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 启动失败: {e}")
|
||||
self.connected = False
|
||||
return self._try_reconnect()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 后台读取线程(永远实时读取,永不堆积)
|
||||
# -------------------------------------------------------------------------
|
||||
def _read_thread(self):
|
||||
while self.async_running and self.process.poll() is None:
|
||||
try:
|
||||
line = self.process.stdout.readline()
|
||||
if not line:
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
cleaned = self.filter_line(line)
|
||||
if not cleaned:
|
||||
continue
|
||||
|
||||
with self.lock:
|
||||
self.output_buffer.append(cleaned)
|
||||
|
||||
if self.async_callback:
|
||||
self.async_callback(cleaned)
|
||||
|
||||
except Exception:
|
||||
break
|
||||
|
||||
self.connected = False
|
||||
print("⚠️ Renode 已断开")
|
||||
self._try_reconnect()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 过滤垃圾输出
|
||||
# -------------------------------------------------------------------------
|
||||
def filter_line(self, line: str) -> Optional[str]:
|
||||
res = self.filter_re.sub("", line).strip()
|
||||
return res if res else None
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 同步发送(带时延测量 + 限流),
|
||||
# -------------------------------------------------------------------------
|
||||
def send_sync(
|
||||
self,
|
||||
cmd: str,
|
||||
timeout_ms: int = 20
|
||||
) -> tuple[Optional[str], float, float, float]:
|
||||
if not self.connected or not self.process:
|
||||
return None, 0, 0, 0
|
||||
|
||||
# 限流:队列满则等待
|
||||
while len(self.cmd_queue) >= self.max_queue:
|
||||
time.sleep(0.001)
|
||||
|
||||
# self.clear_buffers() #同步发送不再清空全局输出缓冲区,缓冲区只有一部读取线程消费 2026/6/3
|
||||
t0 = time.perf_counter()
|
||||
|
||||
# 发送
|
||||
try:
|
||||
self.process.stdin.write(f"{cmd}\n")
|
||||
self.process.stdin.flush()
|
||||
except:
|
||||
return None, 0, 0, 0
|
||||
|
||||
t1 = time.perf_counter()
|
||||
|
||||
# 等待结果
|
||||
timeout = time.time() + timeout_ms / 1000
|
||||
while time.time() < timeout:
|
||||
with self.lock:
|
||||
if self.output_buffer:
|
||||
res = "\n".join(self.output_buffer)
|
||||
self.output_buffer.clear()
|
||||
t2 = time.perf_counter()
|
||||
|
||||
send_lat = (t1 - t0) * 1000
|
||||
proc_lat = (t2 - t1) * 1000
|
||||
total_lat = (t2 - t0) * 1000
|
||||
|
||||
self.latency_stats["send"].append(send_lat)
|
||||
self.latency_stats["process"].append(proc_lat)
|
||||
self.latency_stats["total"].append(total_lat)
|
||||
|
||||
return res, send_lat, proc_lat, total_lat
|
||||
time.sleep(0.001)
|
||||
|
||||
return None, 0, 0, 0
|
||||
|
||||
'''
|
||||
# -------------------------------------------------------------------------
|
||||
# 同步发送(有时延测量 + 限流,不等待回复),
|
||||
# 2026/6/4
|
||||
# -------------------------------------------------------------------------
|
||||
def send_sync(
|
||||
self,
|
||||
cmd: str,
|
||||
timeout_ms: int = 20
|
||||
) -> tuple[Optional[str], float, float, float]:
|
||||
if not self.connected or not self.process:
|
||||
return None,0,0,0
|
||||
#队列限流等待
|
||||
while len(self.cmd_queue) >=self.max_queue:
|
||||
time.sleep(0.001)
|
||||
t0 = time.perf_counter()
|
||||
|
||||
try:
|
||||
self.process.stdin.write(f"{cmd}\n")
|
||||
self.process.stdin.flush()
|
||||
|
||||
except:
|
||||
return None,0,0,0
|
||||
|
||||
t1 = time.perf_counter()
|
||||
|
||||
#【关键改动:不在阻塞轮询收应答,直接退出;所有应答给dispacher消费】
|
||||
send_lat = (t1 - t0)*1000
|
||||
|
||||
return None,send_lat, 0 , send_lat
|
||||
'''
|
||||
# -------------------------------------------------------------------------
|
||||
# 异步监听
|
||||
# -------------------------------------------------------------------------
|
||||
def start_async_listener(self, callback: Callable[[str], None]):
|
||||
self.async_callback = callback
|
||||
|
||||
def clear_buffers(self):
|
||||
with self.lock:
|
||||
self.output_buffer.clear()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 指令队列限流(后台异步发送)
|
||||
# -------------------------------------------------------------------------
|
||||
def enqueue_cmd(self, cmd: str):
|
||||
if len(self.cmd_queue) < self.max_queue:
|
||||
self.cmd_queue.append(cmd)
|
||||
'''
|
||||
def _queue_worker(self): #老版本,停用 2026、6、4
|
||||
while self.async_running:
|
||||
if not self.connected or self.busy or not self.cmd_queue:
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
self.busy = True
|
||||
cmd = self.cmd_queue.pop(0)
|
||||
self.send_sync(cmd, timeout_ms=1) #150->1
|
||||
self.busy = False
|
||||
'''
|
||||
'''
|
||||
def _queue_worker(self): #新版本,2026、6、4
|
||||
while self.async_running:
|
||||
if not self.connected or not self.cmd_queue:
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
cmd = self.cmd_queue.pop(0)
|
||||
self.send_sync(cmd) #150->1
|
||||
logger.info(f" _queue_worker向管道发送数据:{cmd}")
|
||||
'''
|
||||
def _queue_worker(self): #新版本,2026、6、4
|
||||
while self.async_running:
|
||||
now = time.time()
|
||||
#时间没有到,让出CPU
|
||||
if now - self.last_send_t < self.send_min_gap:
|
||||
time.sleep(0.0005)
|
||||
continue
|
||||
if not self.connected or not self.cmd_queue:
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
|
||||
cmd = self.cmd_queue.pop(0)
|
||||
self.send_sync(cmd) #150->1
|
||||
logger.info(f" _queue_worker向管道发送数据:{cmd}")
|
||||
# -------------------------------------------------------------------------
|
||||
# 自动重连
|
||||
# -------------------------------------------------------------------------
|
||||
def _try_reconnect(self) -> bool:
|
||||
print(f"🔌 尝试自动重连 ({self.reconnect_retries} 次)")
|
||||
for i in range(self.reconnect_retries):
|
||||
self.stop()
|
||||
time.sleep(1)
|
||||
if self.start():
|
||||
print(f"✅ 第 {i+1} 次重连成功")
|
||||
return True
|
||||
time.sleep(1)
|
||||
print("❌ 重连全部失败")
|
||||
return False
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 时延统计
|
||||
# -------------------------------------------------------------------------
|
||||
def get_latency_report(self) -> Dict[str, float]:
|
||||
def stats(arr):
|
||||
if not arr: return 0,0,0
|
||||
return round(statistics.mean(arr),2), round(max(arr),2), round(min(arr),2)
|
||||
return {
|
||||
"send_avg,max,min": stats(self.latency_stats["send"]),
|
||||
"process_avg,max,min": stats(self.latency_stats["process"]),
|
||||
"total_avg,max,min": stats(self.latency_stats["total"]),
|
||||
}
|
||||
|
||||
def clear_latency(self):
|
||||
for k in self.latency_stats:
|
||||
self.latency_stats[k].clear()
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# 停止
|
||||
# -------------------------------------------------------------------------
|
||||
def stop(self):
|
||||
self.async_running = False
|
||||
self.connected = False
|
||||
try:
|
||||
if self.process:
|
||||
self.process.stdin.write("quit\n")
|
||||
self.process.stdin.flush()
|
||||
time.sleep(0.2)
|
||||
self.process.terminate()
|
||||
except:
|
||||
pass
|
||||
self.process = None
|
||||
print("🔌 Renode 已停止")
|
||||
# midware/drivers/renode_agent.py
|
||||
|
||||
|
||||
# 第三步:异步数据分发(完全不影响你现有架构)
|
||||
#在 renode_agent.py 里加全局分发回调,直接把数据投递到你现有的驱动 recv
|
||||
# def renode_async_data_dispatcher(line: str):
|
||||
# try:
|
||||
# # 1. 转成字节
|
||||
|
||||
# byte_data = binascii.unhexlify(line.strip())
|
||||
# print(byte_data)
|
||||
|
||||
# # 2. 解析头部 3 字节
|
||||
# if len(byte_data) < 3:
|
||||
# return
|
||||
# bus_type_id = byte_data[0]
|
||||
# machine_type = byte_data[1]
|
||||
# machine_id = byte_data[2]
|
||||
# payload = byte_data[3:]
|
||||
|
||||
# # 3. 映射协议类型
|
||||
# protocol = BUS_TYPE_MAP.get(bus_type_id)
|
||||
# if not protocol:
|
||||
# return
|
||||
|
||||
# # 4. 找到对应设备(根据单机编号)
|
||||
# dev_name = None
|
||||
# for name, cfg in DEVICE_CONFIG_DICT.items():
|
||||
# if cfg.get("协议类型") == protocol and cfg.get("单机编号") == machine_id:
|
||||
# dev_name = name
|
||||
# break
|
||||
# if not dev_name:
|
||||
# return
|
||||
|
||||
# # 5. 把数据 **直接放入你现有的总线接收队列**
|
||||
# from midware.core.bus_udp_decoupler import bus_recv_queues
|
||||
# q = bus_recv_queues[protocol]
|
||||
# q.put({
|
||||
# "dev_name": dev_name,
|
||||
# "raw_data": payload,
|
||||
# "protocol": protocol,
|
||||
# })
|
||||
|
||||
# except Exception:
|
||||
# pass
|
||||
|
||||
|
||||
def renode_async_data_dispatcher(line: str):
|
||||
try:
|
||||
# -----------------------
|
||||
# 🔥 延迟导入(放在函数内部,不会循环引用!)
|
||||
# -----------------------
|
||||
from midware.core.bus_udp_decoupler import bus_recv_queues
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
# print(f"异步监听到的数据{line}")
|
||||
logger.info(
|
||||
f"异步监听到的数据{line}"
|
||||
)
|
||||
# -----------------------
|
||||
# 0.1. 基础过滤
|
||||
# -----------------------
|
||||
line = line.strip()
|
||||
if len(line) < 6: # 至少3字节 = 6个十六进制字符
|
||||
return
|
||||
|
||||
# -----------------------
|
||||
# 0.2. 【关键】判断前2个字符是否是合法总线类型
|
||||
# -----------------------
|
||||
first_byte_str = line[:2]
|
||||
if first_byte_str not in LEGAL_BUS_PREFIX:
|
||||
# 不是我们要的总线数据 → 直接丢弃
|
||||
return
|
||||
|
||||
# -----------------------
|
||||
# 0.3. 判断整个字符串是否是合法十六进制(安全转换)
|
||||
# -----------------------
|
||||
def is_hex(s):
|
||||
try:
|
||||
int(s, 16)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
if not is_hex(line):
|
||||
return
|
||||
|
||||
# 1. 把 Renode 返回的字符串转成字节,首先判断首字符是否为
|
||||
byte_data = bytes.fromhex(line.strip())
|
||||
|
||||
if len(byte_data) < 3:
|
||||
return
|
||||
|
||||
# 2. 解析 3 字节头部
|
||||
bus_type_id = byte_data[0]
|
||||
machine_type = byte_data[1]
|
||||
machine_id = byte_data[2]
|
||||
payload = byte_data[3:]
|
||||
|
||||
# 3. 映射协议名
|
||||
protocol = BUS_TYPE_MAP.get(bus_type_id)
|
||||
if not protocol:
|
||||
return
|
||||
|
||||
# 4. 根据 协议类型 + 单机编号 → 找到设备名
|
||||
dev_name = None
|
||||
for name, cfg in DEVICE_CONFIG_DICT.items():
|
||||
if (cfg.get("protocol") == protocol and
|
||||
cfg.get("number") == machine_id):
|
||||
dev_name = name
|
||||
break
|
||||
|
||||
if not dev_name:
|
||||
return
|
||||
|
||||
# 5. 放入你现有的接收队列(完全兼容你原来架构)
|
||||
q = bus_recv_queues[protocol]
|
||||
try:
|
||||
q.put_nowait({
|
||||
"dev_name": dev_name,
|
||||
"raw_data": payload,
|
||||
"protocol": protocol,
|
||||
})
|
||||
except:
|
||||
pass
|
||||
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
# 在 renode_agent.py 末尾加这个函数
|
||||
def start_renode_uart_redirect():
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
renode = RenodeAgent() # 单例
|
||||
|
||||
for dev_name, cfg in DEVICE_CONFIG_DICT.items():
|
||||
tcp_local = cfg.get("tcp_local_port", 1)
|
||||
tcp_bus = cfg.get("tcp_bus_port", 1)
|
||||
|
||||
if tcp_local != 1 and tcp_bus != 1:
|
||||
term_name = f"term_{dev_name}"
|
||||
cmd1 = f'emulation CreateServerSocketTerminal {tcp_bus} "{term_name}"'#临时注释2026/6/2
|
||||
cmd2 = f'connector Connect sysbus.{dev_name} {term_name}'
|
||||
|
||||
print(f"[Renode] 配置UART重定向: {cmd1}")
|
||||
renode.send_sync(cmd1, timeout_ms=2)
|
||||
time.sleep(0.1)
|
||||
|
||||
print(f"[Renode] 连接外设: {cmd2}")
|
||||
renode.send_sync(cmd2, timeout_ms=2)
|
||||
time.sleep(0.2)
|
||||
print("[Renode] UART重定向配置完成!")
|
||||
|
||||
# 全局单例,全局唯一
|
||||
renode = RenodeAgent(
|
||||
renode_path = r"E:\simulation\1_simulation\卫星仿真平台_后端.exe", #r"E:\codes\1_simulation\卫星仿真平台_后端.exe", #r"C:\Users\PingCe\Desktop\1_simulation\卫星仿真平台_后端.exe", # Windows可写绝对路径如 C:/Renode/renode.exe
|
||||
script_path = r'E:\simulation\1_simulation\generated.resc',#r'E:\codes\1_simulation\generated_771_ADC1.resc', #r'include @C:\Users\PingCe\Desktop\1_simulation\generated_771_ADC1.resc',
|
||||
max_queue=25, # 最多同时排队5条指令,防止压爆
|
||||
reconnect_retries=5
|
||||
)
|
||||
226
midware/drivers/tcp_uart_agent.py
Normal file
226
midware/drivers/tcp_uart_agent.py
Normal file
@@ -0,0 +1,226 @@
|
||||
from queue import Full
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
from typing import Dict, Optional
|
||||
from loguru import logger
|
||||
|
||||
import telnetlib #2026/6/2
|
||||
|
||||
# 全局:TCP客户端管理(每个UART一个实例)
|
||||
tcp_clients: Dict[str, "TCPUARTClient"] = {}
|
||||
# HEAD = b"\xeb\x90" #地测帧头
|
||||
# TAIL = b"\x57\x16" #地测帧尾
|
||||
# MIN_FRAME = 23 ##地测长度最小值
|
||||
# MAX_FRAME = 1000 ##地测帧长度最大值
|
||||
|
||||
class TCPUARTClient:
|
||||
def __init__(self, dev_name: str, local_port: int, remote_port: int, remote_ip="127.0.0.1"):
|
||||
self.dev_name = dev_name
|
||||
self.local_port = local_port
|
||||
self.remote_port = remote_port
|
||||
self.remote_ip = remote_ip
|
||||
self.socket = None
|
||||
self.running = False
|
||||
self.thread = None
|
||||
|
||||
self.HEAD = b"\xeb\x90" #地测帧头
|
||||
self.TAIL = b"\x57\x16" #地测帧尾
|
||||
self.MIN_LEN = 23 ##地测长度最小值
|
||||
self.MAX_LEN = 1000 ##地测帧长度最大值
|
||||
self.tcp_buf = b''
|
||||
|
||||
#self.tn = None
|
||||
|
||||
|
||||
def connect(self):
|
||||
try:
|
||||
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
# 关键点:禁止Nagle算法,模仿网络调试助手,强制使用纯raw socket连接,但是导致TCP收不到数据 2026/6/2
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #停用
|
||||
#self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) #telnet
|
||||
self.socket.bind(("127.0.0.1", self.local_port))
|
||||
self.socket.connect((self.remote_ip, self.remote_port))
|
||||
self.socket.setblocking(False)
|
||||
|
||||
self.running = True
|
||||
print(f"[TCP-UART] {self.dev_name} 连接成功: local={self.local_port} remote={self.remote_port}")
|
||||
|
||||
#telnet
|
||||
#self.tn = telnetlib.Telnet("127.0.0.1",{self.remote_port},timeout=5)
|
||||
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"[TCP-UART] {self.dev_name} 连接失败: {e}")
|
||||
return False
|
||||
'''
|
||||
# 去除转义FF的函数 2026/6/2,可能有安全问题
|
||||
def unescape_ff(raw:bytes)->bytes:
|
||||
buf = bytearray()
|
||||
i = 0
|
||||
n = len(raw)
|
||||
while i<n:
|
||||
buf.append(raw[i])
|
||||
|
||||
#当前字节FF,下一个字节FF:跳过下一个字节,只保留一个
|
||||
if raw[i] == 0xFF and i+1<n and raw[i+1]==0xFF:
|
||||
i +=1
|
||||
i +=1
|
||||
return bytes(buf)
|
||||
'''
|
||||
# 去除转义FF的函数 2026/6/2
|
||||
def unescape_ff(self, raw:bytes)->bytes:
|
||||
out = bytearray()
|
||||
idx=0
|
||||
length = len(raw)
|
||||
while idx<length:
|
||||
cur = raw[idx]
|
||||
out.append(cur)
|
||||
if cur == 0xff and (idx + 1 < length) and raw[idx+1]==0xff:
|
||||
idx+=1
|
||||
idx+=1
|
||||
return bytes(out)
|
||||
|
||||
def receive_loop(self):
|
||||
from midware.core.bus_udp_decoupler import bus_recv_queues
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
|
||||
#data = self.tn.read_some()
|
||||
|
||||
data = self.socket.recv(8192)#4096修改为16384 2026/5/12 tmy 65536->8192 2026/5/15
|
||||
if not data:
|
||||
time.sleep(0.001)
|
||||
continue
|
||||
self.tcp_buf += data #新数据追加到缓存尾部2026/6/2
|
||||
|
||||
dev_name = None
|
||||
protocol = None
|
||||
for name, cfg in DEVICE_CONFIG_DICT.items():
|
||||
if cfg.get("tcp_bus_port") == self.remote_port:
|
||||
dev_name = name
|
||||
protocol = cfg.get("protocol")
|
||||
break
|
||||
|
||||
if not dev_name or not protocol:
|
||||
continue
|
||||
|
||||
#循环拆包,大多数情况是只跑0~1次循环 2026/6/2
|
||||
while True:
|
||||
if len(self.tcp_buf)>self.MAX_LEN * 2:
|
||||
self.tcp_buf = self.tcp_buf[-1000:]
|
||||
h_pos = self.tcp_buf.find(self.HEAD)
|
||||
if h_pos ==-1:
|
||||
break
|
||||
#截断帧头前面垃圾
|
||||
if h_pos>0:
|
||||
self.tcp_buf = self.tcp_buf[h_pos:]
|
||||
#缓存不足最小帧,直接退出
|
||||
if(len(self.tcp_buf)<self.MIN_LEN):
|
||||
break
|
||||
#从帧头向后查找结束符号
|
||||
t_pos = self.tcp_buf.find(self.TAIL,2)
|
||||
if t_pos == -1:
|
||||
break
|
||||
frame = self.tcp_buf[:t_pos+2]
|
||||
#logger.info(f'去除转义FF帧前的有问题帧内容:{frame.hex()}')
|
||||
|
||||
if(self.MIN_LEN<= len(frame) <= self.MAX_LEN):
|
||||
#去除转义FF
|
||||
#logger.info(f'去除转义FF帧内容:')
|
||||
real_data = self.unescape_ff(frame)
|
||||
|
||||
#logger.info(f'去除转义FF帧内容:{real_data.hex()}')
|
||||
#入队
|
||||
try:
|
||||
q = bus_recv_queues.get(protocol)
|
||||
if q:
|
||||
try:
|
||||
q.put_nowait({
|
||||
"dev_name": dev_name,
|
||||
"raw_data": real_data,
|
||||
"protocol": protocol,
|
||||
})
|
||||
logger.debug(f'接受TCP重定向数据,存入bus_recv_queues,内容:{real_data.hex()}')
|
||||
except Full as e:
|
||||
print("队列已满",e)
|
||||
|
||||
except Exception as e:
|
||||
print("其他异常",repr(e))
|
||||
except:
|
||||
pass
|
||||
#切掉已经解析的帧
|
||||
self.tcp_buf = self.tcp_buf[(t_pos+2) :]
|
||||
|
||||
|
||||
|
||||
'''
|
||||
# 匹配设备名
|
||||
dev_name = None
|
||||
protocol = None
|
||||
for name, cfg in DEVICE_CONFIG_DICT.items():
|
||||
if cfg.get("tcp_bus_port") == self.remote_port:
|
||||
dev_name = name
|
||||
protocol = cfg.get("protocol")
|
||||
break
|
||||
|
||||
if not dev_name or not protocol:
|
||||
continue
|
||||
|
||||
logger.info(f'接受TCP重定向数据,存入bus_recv_queues,内容:{data.hex()}')
|
||||
|
||||
|
||||
# 推入现有接收队列(完全兼容你原有架构)
|
||||
q = bus_recv_queues.get(protocol)
|
||||
if q:
|
||||
try:
|
||||
q.put_nowait({
|
||||
"dev_name": dev_name,
|
||||
"raw_data": data,
|
||||
"protocol": protocol,
|
||||
})
|
||||
except:
|
||||
pass
|
||||
'''
|
||||
except BlockingIOError:
|
||||
time.sleep(0.001)
|
||||
except Exception as e:
|
||||
print(f"[TCP-UART] {self.dev_name} 接收异常: {e}")
|
||||
break
|
||||
|
||||
self.running = False
|
||||
|
||||
def start(self):
|
||||
if self.connect():
|
||||
self.thread = threading.Thread(target=self.receive_loop, daemon=True)
|
||||
self.thread.start()
|
||||
tcp_clients[self.dev_name] = self
|
||||
|
||||
def stop(self):
|
||||
self.running = False
|
||||
if self.socket:
|
||||
self.socket.close()
|
||||
|
||||
# ======================
|
||||
# 批量启动所有UART TCP客户端
|
||||
# ======================
|
||||
def start_all_tcp_uart_clients():
|
||||
from midware.config.base_config import DEVICE_CONFIG_DICT
|
||||
|
||||
for dev_name, cfg in DEVICE_CONFIG_DICT.items():
|
||||
tcp_local = cfg.get("tcp_local_port", 1)
|
||||
tcp_bus = cfg.get("tcp_bus_port", 1)
|
||||
|
||||
# 只启动端口 !=1 的UART设备
|
||||
if tcp_local != 1 and tcp_bus != 1:
|
||||
client = TCPUARTClient(
|
||||
dev_name=dev_name,
|
||||
local_port=tcp_local,
|
||||
remote_port=tcp_bus
|
||||
)
|
||||
client.start()
|
||||
time.sleep(0.1)
|
||||
Reference in New Issue
Block a user