From ff76bb1a76d3114cae616e2ad5ecc34b61cc159b Mon Sep 17 00:00:00 2001 From: KCFeng425 <2100011801@stu.pku.edu.cn> Date: Thu, 5 Jun 2025 13:33:56 +0800 Subject: [PATCH] Add Device MockChiller Add device MockChiller --- test/experiments/MockChiller.json | 29 +++ .../devices/Mock/MockChiller/MockChiller.py | 170 ++++++++++++++++++ unilabos/devices/Mock/MockChiller/__init__.py | 0 unilabos/registry/devices/temperature.yaml | 56 +++++- 4 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 test/experiments/MockChiller.json create mode 100644 unilabos/devices/Mock/MockChiller/MockChiller.py create mode 100644 unilabos/devices/Mock/MockChiller/__init__.py diff --git a/test/experiments/MockChiller.json b/test/experiments/MockChiller.json new file mode 100644 index 0000000..793a98c --- /dev/null +++ b/test/experiments/MockChiller.json @@ -0,0 +1,29 @@ +{ + "nodes": [ + { + "id": "MockChiller1", + "name": "模拟冷却器", + "children": [], + "parent": null, + "type": "device", + "class": "MockChiller", + "position": { + "x": 620.6111111111111, + "y": 171, + "z": 0 + }, + "config": { + "port": "MOCK" + }, + "data": { + "current_temperature": 25.0, + "target_temperature": 25.0, + "status": "Idle", + "power_on": false, + "is_cooling": false, + "is_heating": false + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/unilabos/devices/Mock/MockChiller/MockChiller.py b/unilabos/devices/Mock/MockChiller/MockChiller.py new file mode 100644 index 0000000..faa6ff8 --- /dev/null +++ b/unilabos/devices/Mock/MockChiller/MockChiller.py @@ -0,0 +1,170 @@ +import time +import threading + + +class MockChiller: + def __init__(self, port: str = "MOCK"): + self.port = port + self._current_temperature: float = 25.0 # 室温开始 + self._target_temperature: float = 25.0 + self._status: str = "Idle" + self._is_cooling: bool = False + self._is_heating: bool = False + self._power_on: bool = False + + # 模拟温度变化的线程 + self._temperature_thread = None + self._running = False + + @property + def current_temperature(self) -> float: + """当前温度 - 会被自动识别的设备属性""" + return self._current_temperature + + @property + def target_temperature(self) -> float: + """目标温度""" + return self._target_temperature + + @property + def status(self) -> str: + """设备状态 - 会被自动识别的设备属性""" + return self._status + + @property + def power_on(self) -> bool: + """电源状态""" + return self._power_on + + @property + def is_cooling(self) -> bool: + """是否正在冷却""" + return self._is_cooling + + @property + def is_heating(self) -> bool: + """是否正在加热""" + return self._is_heating + + def set_temperature(self, temperature: float): + """设置目标温度 - 需要在注册表添加的设备动作""" + if not self._power_on: + self._status = "Error: Power Off" + return False + + # 将传入温度转换为 float,并限制在允许范围内 + temperature = float(temperature) + self._target_temperature = temperature + + # 立即更新状态 + diff = self._target_temperature - self._current_temperature + if abs(diff) < 0.1: + self._status = "At Target Temperature" + self._is_cooling = False + self._is_heating = False + elif diff < 0: + self._status = "Cooling" + self._is_cooling = True + self._is_heating = False + else: + self._status = "Heating" + self._is_heating = True + self._is_cooling = False + + # 启动温度控制 + self._start_temperature_control() + return True + + def power_on_off(self, power_state: str): + """开关机控制""" + if power_state == "on": + self._power_on = True + # 不在这里直接设置状态和加热/制冷标志 + self._start_temperature_control() + else: + self._power_on = False + self._status = "Power Off" + self._stop_temperature_control() + self._is_cooling = False + self._is_heating = False + + def _start_temperature_control(self): + """启动温度控制线程""" + if self._power_on: # 移除 not self._running 检查 + self._running = True + if self._temperature_thread is None or not self._temperature_thread.is_alive(): + self._temperature_thread = threading.Thread(target=self._temperature_control_loop) + self._temperature_thread.daemon = True + self._temperature_thread.start() + + def _stop_temperature_control(self): + """停止温度控制""" + self._running = False + if self._temperature_thread: + self._temperature_thread.join(timeout=1.0) + + def _temperature_control_loop(self): + """温度控制循环 - 模拟真实冷却器的温度变化""" + while self._running and self._power_on: + temp_diff = self._target_temperature - self._current_temperature + + if abs(temp_diff) < 0.1: # 将判断范围从0.5改小到0.1 + self._status = "At Target Temperature" + self._is_cooling = False + self._is_heating = False + elif temp_diff < 0: # 需要冷却 + self._status = "Cooling" + self._is_cooling = True + self._is_heating = False + # 模拟冷却过程,每秒降低0.5度 + self._current_temperature -= 0.5 + else: # 需要加热 + self._status = "Heating" + self._is_heating = True + self._is_cooling = False + # 模拟加热过程,每秒升高0.3度 + self._current_temperature += 0.3 + + # 限制温度范围 + self._current_temperature = max(-20.0, min(80.0, self._current_temperature)) + + time.sleep(1.0) # 每秒更新一次 + + def emergency_stop(self): + """紧急停止""" + self._status = "Emergency Stop" + self._stop_temperature_control() + self._is_cooling = False + self._is_heating = False + + def get_status_info(self) -> dict: + """获取完整状态信息""" + return { + "current_temperature": self._current_temperature, + "target_temperature": self._target_temperature, + "status": self._status, + "power_on": self._power_on, + "is_cooling": self._is_cooling, + "is_heating": self._is_heating + } + + +# 用于测试的主函数 +if __name__ == "__main__": + chiller = MockChiller() + + # 测试基本功能 + print("启动冷却器测试...") + chiller.power_on_off("on") + print(f"初始状态: {chiller.get_status_info()}") + + # 设置目标温度为5度 + chiller.set_temperature(5.0) + + # 模拟运行10秒 + for i in range(10): + time.sleep(1) + print(f"第{i+1}秒: 当前温度={chiller.current_temperature:.1f}°C, 状态={chiller.status}") + + chiller.emergency_stop() + print("测试完成") \ No newline at end of file diff --git a/unilabos/devices/Mock/MockChiller/__init__.py b/unilabos/devices/Mock/MockChiller/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/unilabos/registry/devices/temperature.yaml b/unilabos/registry/devices/temperature.yaml index 1c01b4e..daaddc6 100644 --- a/unilabos/registry/devices/temperature.yaml +++ b/unilabos/registry/devices/temperature.yaml @@ -62,4 +62,58 @@ tempsensor: command: command feedback: {} result: - success: success \ No newline at end of file + success: success +MockChiller: + description: Mock Chiller Device + class: + module: unilabos.devices.Mock.MockChiller.MockChiller:MockChiller + type: python + status_types: + current_temperature: Float64 + target_temperature: Float64 + status: String + power_on: Bool + is_cooling: Bool + is_heating: Bool + action_value_mappings: + set_temperature: + type: SendCmd + goal: + command: temperature + feedback: {} + result: + success: success + power_on_off: + type: SendCmd + goal: + command: power_state + feedback: {} + result: + success: success + schema: + type: object + properties: + current_temperature: + type: number + description: Current temperature of the chiller + target_temperature: + type: number + description: Target temperature setting + status: + type: string + description: Current status of the device + power_on: + type: boolean + description: Power state of the device + is_cooling: + type: boolean + description: Whether the device is actively cooling + is_heating: + type: boolean + description: Whether the device is actively heating + required: + - current_temperature + - target_temperature + - status + - power_on + additionalProperties: false