新增模拟硬件测试功能
This commit is contained in:
@@ -4,15 +4,21 @@
|
||||
纯业务逻辑,无 UI 依赖。通过回调与 UI 层通信。
|
||||
"""
|
||||
|
||||
import time
|
||||
from PcControl import MT2AM8Client
|
||||
import os
|
||||
import time
|
||||
from PcControl import MT2AM8Client
|
||||
from core.simulated_device import SimulatedDevice
|
||||
|
||||
|
||||
class ConnectionManager:
|
||||
"""管理 MT2-AM8 模块连接的生命周期"""
|
||||
|
||||
def __init__(self):
|
||||
self.modbus_client = None # MT2AM8Client (TCP 读压力/流量,控电机)
|
||||
def __init__(self, simulation=None):
|
||||
self.modbus_client = None # MT2AM8Client (TCP 读压力/流量,控电机)
|
||||
if simulation is None:
|
||||
simulation = os.environ.get("REINLOOP_SIMULATION", "").strip().lower() \
|
||||
in {"1", "true", "yes", "on"}
|
||||
self.simulation = bool(simulation)
|
||||
self._pressure_addr = 0 # 压力传感器模拟量通道地址
|
||||
self._flowmeter_addr = None # 流量计模拟量通道地址(None=使用手动输入)
|
||||
self._motor_addr = 0 # 电机模拟量输出通道地址
|
||||
@@ -61,19 +67,28 @@ class ConnectionManager:
|
||||
self._motor_addr = motor_addr
|
||||
self._flowmeter_addr = flowmeter_addr
|
||||
|
||||
# 创建 MT2-AM8 客户端
|
||||
self.modbus_client = MT2AM8Client(
|
||||
host=tcp_ip,
|
||||
port=tcp_port,
|
||||
pressure_range=pressure_range,
|
||||
flow_range=flow_range,
|
||||
)
|
||||
|
||||
if not self.modbus_client.connect():
|
||||
self.log(f"连接 MT2-AM8 模块失败: {tcp_ip}:{tcp_port}")
|
||||
return False
|
||||
|
||||
self.log(f"成功连接到 MT2-AM8 模块: {tcp_ip}:{tcp_port}")
|
||||
if self.simulation:
|
||||
self.modbus_client = SimulatedDevice(
|
||||
pressure_range=pressure_range,
|
||||
flow_range=flow_range,
|
||||
)
|
||||
if not self.modbus_client.connect():
|
||||
return False
|
||||
self.log("模拟设备已连接(未访问真实 TCP/串口硬件)")
|
||||
else:
|
||||
# 真实硬件路径:保留原有 MT2-AM8 Modbus TCP 实现。
|
||||
self.modbus_client = MT2AM8Client(
|
||||
host=tcp_ip,
|
||||
port=tcp_port,
|
||||
pressure_range=pressure_range,
|
||||
flow_range=flow_range,
|
||||
)
|
||||
|
||||
if not self.modbus_client.connect():
|
||||
self.log(f"连接 MT2-AM8 模块失败: {tcp_ip}:{tcp_port}")
|
||||
return False
|
||||
|
||||
self.log(f"成功连接到 MT2-AM8 模块: {tcp_ip}:{tcp_port}")
|
||||
self.log(f"压力地址: {pressure_addr}, 电机地址: {motor_addr}, 流量计地址: {flowmeter_addr}")
|
||||
|
||||
if self._on_status_change:
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
"""Deterministic in-memory device used for hardware-free ReinLoop tests.
|
||||
|
||||
The public methods intentionally mirror the MT2AM8 client and the small
|
||||
subset of the legacy PLC/motor clients used by ``tool/gui.py``. No sockets,
|
||||
serial ports, or Modbus objects are created here.
|
||||
"""
|
||||
|
||||
import time
|
||||
|
||||
|
||||
class SimulatedDevice:
|
||||
"""A small pressure/flow/valve model for offline control-loop testing."""
|
||||
|
||||
def __init__(self, pressure_range=400.0, flow_range=100.0,
|
||||
initial_pressure=0.0):
|
||||
self.pressure_range = float(pressure_range)
|
||||
self.flow_range = float(flow_range)
|
||||
self.pressure = float(initial_pressure)
|
||||
self.flow = min(self.flow_range, 50.0)
|
||||
self.position = 1000.0
|
||||
self.connected = False
|
||||
self.last_update = time.monotonic()
|
||||
self.command_log = []
|
||||
|
||||
def connect(self):
|
||||
self.connected = True
|
||||
self.last_update = time.monotonic()
|
||||
print("模拟设备连接成功(未访问真实硬件)")
|
||||
return True
|
||||
|
||||
def disconnect(self):
|
||||
self.connected = False
|
||||
print("模拟设备已断开")
|
||||
|
||||
def _advance(self):
|
||||
now = time.monotonic()
|
||||
dt = max(0.0, min(now - self.last_update, 0.5))
|
||||
self.last_update = now
|
||||
if not self.connected or dt == 0:
|
||||
return
|
||||
|
||||
# MT2AM8 的行程控制在当前程序中是反向阀位:较小行程表示更大
|
||||
# 进气。模型因此让压力向由行程决定的目标值缓慢靠近。
|
||||
opening = max(0.0, min(1.0, 1.0 - self.position / 1000.0))
|
||||
target_pressure = self.pressure_range * opening
|
||||
response = min(1.0, dt / 8.0)
|
||||
self.pressure += (target_pressure - self.pressure) * response
|
||||
|
||||
def get_pressure(self, channel=0):
|
||||
self._advance()
|
||||
return max(0.0, self.pressure)
|
||||
|
||||
def get_flow(self, channel=0):
|
||||
self._advance()
|
||||
opening = max(0.0, min(1.0, 1.0 - self.position / 1000.0))
|
||||
return self.flow * (0.25 + 0.75 * opening)
|
||||
|
||||
def set_motor_position(self, position, channel=0):
|
||||
self._advance()
|
||||
position = float(position)
|
||||
if not 0.0 <= position <= 1000.0:
|
||||
return False
|
||||
self.position = position
|
||||
self.command_log.append({"position": position, "channel": channel,
|
||||
"time": time.monotonic()})
|
||||
return True
|
||||
|
||||
# Compatibility methods used by the legacy GUI and standalone helpers.
|
||||
def get_current_p(self):
|
||||
return self.get_pressure(0)
|
||||
|
||||
def read_current_position(self):
|
||||
return self.position
|
||||
|
||||
def set_position(self, position):
|
||||
return self.set_motor_position(position)
|
||||
|
||||
def init(self):
|
||||
return True
|
||||
|
||||
def start_control(self):
|
||||
return True
|
||||
|
||||
def stop_control(self):
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user