"""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 # 行程与阀门开度反向:行程越大,阀门开度越小。阀门开度减小 # 时系统压力升高;默认量程下行程 0/1000 分别对应 10/400 kPa。 travel_ratio = max(0.0, min(1.0, self.position / 1000.0)) target_pressure = 10.0 + (self.pressure_range - 10.0) * travel_ratio 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