Squash merge from dev

Update recipe.yaml

fix: figure_resource

use call_async in all service to avoid deadlock

fix: prcxi import error

临时兼容错误的driver写法

fix protocol node

fix filter protocol

bugfixes on organic protocols

fix and remove redundant info

feat: 新增use_remote_resource参数

fix all protocol_compilers and remove deprecated devices

feat: 优化protocol node节点运行日志

fix pumps and liquid_handler handle

feat: workstation example

add: prcxi res
fix: startup slow

fix: prcxi_res

fix: discard_tips

fix: discard_tips error

fix: drop_tips not using auto resource select

feat: 添加ChinWe设备控制类,支持串口通信和电机控制功能 (#79)

feat: add trace log level

modify default discovery_interval to 15s

fix: working dir error when input config path
feat: report publish topic when error

fix: workstation handlers and vessel_id parsing

Cleanup registry to be easy-understanding (#76)

* delete deprecated mock devices

* rename categories

* combine chromatographic devices

* rename rviz simulation nodes

* organic virtual devices

* parse vessel_id

* run registry completion before merge

---------

Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com>
This commit is contained in:
Junhan Chang
2025-08-03 11:21:37 +08:00
committed by Xuwznln
parent a555c59dc2
commit 0bfb52df00
97 changed files with 5033 additions and 164837 deletions

View File

@@ -3,6 +3,8 @@ import logging
import time as time_module
from typing import Dict, Any, Optional
from unilabos.compile.utils.vessel_parser import get_vessel
class VirtualFilter:
"""Virtual filter device - 完全按照 Filter.action 规范 🌊"""
@@ -40,7 +42,6 @@ class VirtualFilter:
"progress": 0.0, # Filter.action feedback
"current_temp": 25.0, # Filter.action feedback
"filtered_volume": 0.0, # Filter.action feedback
"current_status": "Ready for filtration", # Filter.action feedback
"message": "Ready for filtration"
})
@@ -52,9 +53,7 @@ class VirtualFilter:
self.logger.info(f"🧹 清理虚拟过滤器 {self.device_id} 🔚")
self.data.update({
"status": "Offline",
"current_status": "System offline",
"message": "System offline"
"status": "Offline"
})
self.logger.info(f"✅ 过滤器 {self.device_id} 清理完成 💤")
@@ -62,8 +61,8 @@ class VirtualFilter:
async def filter(
self,
vessel: str,
filtrate_vessel: str = "",
vessel: dict,
filtrate_vessel: dict = {},
stir: bool = False,
stir_speed: float = 300.0,
temp: float = 25.0,
@@ -71,7 +70,9 @@ class VirtualFilter:
volume: float = 0.0
) -> bool:
"""Execute filter action - 完全按照 Filter.action 参数 🌊"""
vessel_id, _ = get_vessel(vessel)
filtrate_vessel_id, _ = get_vessel(filtrate_vessel) if filtrate_vessel else (f"{vessel_id}_filtrate", {})
# 🔧 新增:温度自动调整
original_temp = temp
if temp == 0.0:
@@ -81,7 +82,7 @@ class VirtualFilter:
temp = 4.0 # 小于4度自动设置为4度
self.logger.info(f"🌡️ 温度自动调整: {original_temp}°C → {temp}°C (最低温度) ❄️")
self.logger.info(f"🌊 开始过滤操作: {vessel}{filtrate_vessel} 🚰")
self.logger.info(f"🌊 开始过滤操作: {vessel_id}{filtrate_vessel_id} 🚰")
self.logger.info(f" 🌪️ 搅拌: {stir} ({stir_speed} RPM)")
self.logger.info(f" 🌡️ 温度: {temp}°C")
self.logger.info(f" 💧 体积: {volume}mL")
@@ -93,7 +94,6 @@ class VirtualFilter:
self.logger.error(f"{error_msg}")
self.data.update({
"status": f"Error: 温度超出范围 ⚠️",
"current_status": f"Error: 温度超出范围 ⚠️",
"message": error_msg
})
return False
@@ -103,7 +103,6 @@ class VirtualFilter:
self.logger.error(f"{error_msg}")
self.data.update({
"status": f"Error: 搅拌速度超出范围 ⚠️",
"current_status": f"Error: 搅拌速度超出范围 ⚠️",
"message": error_msg
})
return False
@@ -112,8 +111,7 @@ class VirtualFilter:
error_msg = f"💧 过滤体积 {volume} mL 超出范围 (0-{self._max_volume} mL) ⚠️"
self.logger.error(f"{error_msg}")
self.data.update({
"status": f"Error: 体积超出范围 ⚠️",
"current_status": f"Error: 体积超出范围 ⚠️",
"status": f"Error",
"message": error_msg
})
return False
@@ -123,12 +121,11 @@ class VirtualFilter:
self.logger.info(f"🚀 开始过滤 {filter_volume}mL 液体 💧")
self.data.update({
"status": f"🌊 过滤中: {vessel}",
"status": f"Running",
"current_temp": temp,
"filtered_volume": 0.0,
"progress": 0.0,
"current_status": f"🌊 Filtering {vessel}{filtrate_vessel}",
"message": f"🚀 Starting filtration: {vessel}{filtrate_vessel}"
"message": f"🚀 Starting filtration: {vessel_id}{filtrate_vessel_id}"
})
try:
@@ -164,8 +161,7 @@ class VirtualFilter:
"progress": progress, # Filter.action feedback
"current_temp": temp, # Filter.action feedback
"filtered_volume": current_filtered, # Filter.action feedback
"current_status": f"🌊 Filtering: {progress:.1f}% complete", # Filter.action feedback
"status": status_msg,
"status": "Running",
"message": f"🌊 Filtering: {progress:.1f}% complete, {current_filtered:.1f}mL filtered"
})
@@ -190,11 +186,10 @@ class VirtualFilter:
"progress": 100.0, # Filter.action feedback
"current_temp": final_temp, # Filter.action feedback
"filtered_volume": filter_volume, # Filter.action feedback
"current_status": f"✅ Filtration completed: {filter_volume}mL", # Filter.action feedback
"message": f"✅ Filtration completed: {filter_volume}mL filtered from {vessel}"
"message": f"✅ Filtration completed: {filter_volume}mL filtered from {vessel_id}"
})
self.logger.info(f"🎉 过滤完成! 💧 {filter_volume}mL 从 {vessel} 过滤到 {filtrate_vessel}")
self.logger.info(f"🎉 过滤完成! 💧 {filter_volume}mL 从 {vessel_id} 过滤到 {filtrate_vessel_id}")
self.logger.info(f"📊 最终状态: 温度 {final_temp}°C | 进度 100% | 体积 {filter_volume}mL 🏁")
return True
@@ -202,8 +197,7 @@ class VirtualFilter:
error_msg = f"过滤过程中发生错误: {str(e)} 💥"
self.logger.error(f"{error_msg}")
self.data.update({
"status": f"❌ 过滤错误: {str(e)}",
"current_status": f"❌ Filtration failed: {str(e)}",
"status": f"Error",
"message": f"❌ Filtration failed: {str(e)}"
})
return False
@@ -222,17 +216,17 @@ class VirtualFilter:
def current_temp(self) -> float:
"""Filter.action feedback 字段 🌡️"""
return self.data.get("current_temp", 25.0)
@property
def filtered_volume(self) -> float:
"""Filter.action feedback 字段 💧"""
return self.data.get("filtered_volume", 0.0)
@property
def current_status(self) -> str:
"""Filter.action feedback 字段 📋"""
return self.data.get("current_status", "")
@property
def filtered_volume(self) -> float:
"""Filter.action feedback 字段 💧"""
return self.data.get("filtered_volume", 0.0)
@property
def message(self) -> str:
return self.data.get("message", "")

View File

@@ -67,8 +67,8 @@ class VirtualHeatChill:
self.logger.info(f"✅ 温控设备 {self.device_id} 清理完成 💤")
return True
async def heat_chill(self, vessel: str, temp: float, time, stir: bool,
stir_speed: float, purpose: str) -> bool:
async def heat_chill(self, temp: float, time, stir: bool,
stir_speed: float, purpose: str, vessel: dict = {}) -> bool:
"""Execute heat chill action - 🔧 修复:确保参数类型正确"""
# 🔧 关键修复:确保所有参数类型正确
@@ -77,7 +77,6 @@ class VirtualHeatChill:
time_value = float(time) # 强制转换为浮点数
stir_speed = float(stir_speed)
stir = bool(stir)
vessel = str(vessel)
purpose = str(purpose)
except (ValueError, TypeError) as e:
error_msg = f"参数类型转换错误: temp={temp}({type(temp)}), time={time}({type(time)}), error={str(e)}"
@@ -102,8 +101,7 @@ class VirtualHeatChill:
operation_mode = "Maintaining"
status_action = "保温"
self.logger.info(f"🌡️ 开始温控操作: {vessel}{temp}°C {temp_emoji}")
self.logger.info(f" 🥽 容器: {vessel}")
self.logger.info(f"🌡️ 开始温控操作: {temp}°C {temp_emoji}")
self.logger.info(f" 🎯 目标温度: {temp}°C {temp_emoji}")
self.logger.info(f" ⏰ 持续时间: {time_value}s")
self.logger.info(f" 🌪️ 搅拌: {stir} ({stir_speed} RPM)")
@@ -147,7 +145,7 @@ class VirtualHeatChill:
stir_info = f" | 🌪️ 搅拌: {stir_speed} RPM" if stir else ""
self.data.update({
"status": f"{temp_emoji} 运行中: {status_action} {vessel}{temp}°C | ⏰ 剩余: {total_time:.0f}s{stir_info}",
"status": f"{temp_emoji} 运行中: {status_action}{temp}°C | ⏰ 剩余: {total_time:.0f}s{stir_info}",
"operation_mode": operation_mode,
"is_stirring": stir,
"stir_speed": stir_speed if stir else 0.0,
@@ -165,7 +163,7 @@ class VirtualHeatChill:
# 更新剩余时间和状态
self.data.update({
"remaining_time": remaining,
"status": f"{temp_emoji} 运行中: {status_action} {vessel}{temp}°C | ⏰ 剩余: {remaining:.0f}s{stir_info}",
"status": f"{temp_emoji} 运行中: {status_action}{temp}°C | ⏰ 剩余: {remaining:.0f}s{stir_info}",
"progress": progress
})
@@ -185,7 +183,7 @@ class VirtualHeatChill:
final_stir_info = f" | 🌪️ 搅拌: {stir_speed} RPM" if stir else ""
self.data.update({
"status": f"✅ 完成: {vessel} 已达到 {temp}°C {temp_emoji} | ⏱️ 用时: {total_time:.0f}s{final_stir_info}",
"status": f"✅ 完成: 已达到 {temp}°C {temp_emoji} | ⏱️ 用时: {total_time:.0f}s{final_stir_info}",
"operation_mode": "Completed",
"remaining_time": 0.0,
"is_stirring": False,
@@ -195,7 +193,6 @@ class VirtualHeatChill:
self.logger.info(f"🎉 温控操作完成! ✨")
self.logger.info(f"📊 操作结果:")
self.logger.info(f" 🥽 容器: {vessel}")
self.logger.info(f" 🌡️ 达到温度: {temp}°C {temp_emoji}")
self.logger.info(f" ⏱️ 总用时: {total_time:.0f}s")
if stir:
@@ -204,13 +201,12 @@ class VirtualHeatChill:
return True
async def heat_chill_start(self, vessel: str, temp: float, purpose: str) -> bool:
async def heat_chill_start(self, temp: float, purpose: str, vessel: dict = {}) -> bool:
"""Start continuous heat chill 🔄"""
# 🔧 添加类型转换
try:
temp = float(temp)
vessel = str(vessel)
purpose = str(purpose)
except (ValueError, TypeError) as e:
error_msg = f"参数类型转换错误: {str(e)}"
@@ -235,8 +231,7 @@ class VirtualHeatChill:
operation_mode = "Maintaining"
status_action = "恒温保持"
self.logger.info(f"🔄 启动持续温控: {vessel}{temp}°C {temp_emoji}")
self.logger.info(f" 🥽 容器: {vessel}")
self.logger.info(f"🔄 启动持续温控: {temp}°C {temp_emoji}")
self.logger.info(f" 🎯 目标温度: {temp}°C {temp_emoji}")
self.logger.info(f" 🔄 模式: {status_action}")
self.logger.info(f" 📝 目的: {purpose}")
@@ -252,7 +247,7 @@ class VirtualHeatChill:
return False
self.data.update({
"status": f"🔄 启动: {status_action} {vessel}{temp}°C {temp_emoji} | ♾️ 持续运行",
"status": f"🔄 启动: {status_action}{temp}°C {temp_emoji} | ♾️ 持续运行",
"operation_mode": operation_mode,
"is_stirring": False,
"stir_speed": 0.0,
@@ -262,28 +257,20 @@ class VirtualHeatChill:
self.logger.info(f"✅ 持续温控已启动! {temp_emoji} {status_action}模式 🚀")
return True
async def heat_chill_stop(self, vessel: str) -> bool:
async def heat_chill_stop(self, vessel: dict = {}) -> bool:
"""Stop heat chill 🛑"""
# 🔧 添加类型转换
try:
vessel = str(vessel)
except (ValueError, TypeError) as e:
error_msg = f"参数类型转换错误: {str(e)}"
self.logger.error(f"{error_msg}")
return False
self.logger.info(f"🛑 停止温控: {vessel}")
self.logger.info(f"🛑 停止温控:")
self.data.update({
"status": f"🛑 已停止: {vessel} 温控停止",
"status": f"🛑 {self.device_id} 温控停止",
"operation_mode": "Stopped",
"is_stirring": False,
"stir_speed": 0.0,
"remaining_time": 0.0,
})
self.logger.info(f"✅ 温控设备已停止 {vessel} 温度控制 🏁")
self.logger.info(f"✅ 温控设备已停止 {self.device_id} 温度控制 🏁")
return True
# 状态属性

View File

@@ -21,19 +21,6 @@ class VirtualMultiwayValve:
self._current_position = 0 # 默认在0号位transfer pump位置
self._target_position = 0
# 位置映射说明
self.position_map = {
0: "transfer_pump", # 0号位连接转移泵
1: "port_1", # 1号位
2: "port_2", # 2号位
3: "port_3", # 3号位
4: "port_4", # 4号位
5: "port_5", # 5号位
6: "port_6", # 6号位
7: "port_7", # 7号位
8: "port_8" # 8号位
}
print(f"🔄 === 虚拟多通阀门已创建 === ✨")
print(f"🎯 端口: {port} | 📊 位置范围: 0-{self.max_positions} | 🏠 初始位置: 0 (transfer_pump)")
self.logger.info(f"🔧 多通阀门初始化: 端口={port}, 最大位置={self.max_positions}")
@@ -60,7 +47,7 @@ class VirtualMultiwayValve:
def get_current_port(self) -> str:
"""获取当前连接的端口名称 🔌"""
return self.position_map.get(self._current_position, "unknown")
return self._current_position
def set_position(self, command: Union[int, str]):
"""
@@ -115,7 +102,7 @@ class VirtualMultiwayValve:
old_position = self._current_position
old_port = self.get_current_port()
self.logger.info(f"🔄 阀门切换: {old_position}({old_port}) → {pos}({self.position_map.get(pos, 'unknown')}) {pos_emoji}")
self.logger.info(f"🔄 阀门切换: {old_position}({old_port}) → {pos} {pos_emoji}")
self._status = "Busy"
self._valve_state = "Moving"
@@ -190,6 +177,17 @@ class VirtualMultiwayValve:
"""获取阀门位置 - 兼容性方法 📍"""
return self._current_position
def set_valve_position(self, command: Union[int, str]):
"""
设置阀门位置 - 兼容pump_protocol调用 🎯
这是set_position的别名方法用于兼容pump_protocol.py
Args:
command: 目标位置 (0-8) 或位置字符串
"""
# 删除debug日志self.logger.debug(f"🎯 兼容性调用: set_valve_position({command})")
return self.set_position(command)
def is_at_position(self, position: int) -> bool:
"""检查是否在指定位置 🎯"""
result = self._current_position == position
@@ -210,17 +208,6 @@ class VirtualMultiwayValve:
# 删除debug日志self.logger.debug(f"🔌 端口{port_number}检查: {port_status} (当前位置: {self._current_position})")
return result
def get_available_positions(self) -> list:
"""获取可用位置列表 📋"""
positions = list(range(0, self.max_positions + 1))
# 删除debug日志self.logger.debug(f"📋 可用位置: {positions}")
return positions
def get_available_ports(self) -> Dict[int, str]:
"""获取可用端口映射 🗺️"""
# 删除debug日志self.logger.debug(f"🗺️ 端口映射: {self.position_map}")
return self.position_map.copy()
def reset(self):
"""重置阀门到transfer pump位置0号位🔄"""
self.logger.info(f"🔄 重置阀门到泵位置...")
@@ -253,41 +240,12 @@ class VirtualMultiwayValve:
# 删除debug日志self.logger.debug(f"🌊 当前流路: {flow_path}")
return flow_path
def get_info(self) -> dict:
"""获取阀门详细信息 📊"""
info = {
"port": self.port,
"max_positions": self.max_positions,
"total_positions": self.total_positions,
"current_position": self._current_position,
"current_port": self.get_current_port(),
"target_position": self._target_position,
"status": self._status,
"valve_state": self._valve_state,
"flow_path": self.get_flow_path(),
"position_map": self.position_map
}
# 删除debug日志self.logger.debug(f"📊 阀门信息: 位置={self._current_position}, 状态={self._status}, 端口={self.get_current_port()}")
return info
def __str__(self):
current_port = self.get_current_port()
status_emoji = "" if self._status == "Idle" else "🔄" if self._status == "Busy" else ""
return f"🔄 VirtualMultiwayValve({status_emoji} 位置: {self._current_position}/{self.max_positions}, 端口: {current_port}, 状态: {self._status})"
def set_valve_position(self, command: Union[int, str]):
"""
设置阀门位置 - 兼容pump_protocol调用 🎯
这是set_position的别名方法用于兼容pump_protocol.py
Args:
command: 目标位置 (0-8) 或位置字符串
"""
# 删除debug日志self.logger.debug(f"🎯 兼容性调用: set_valve_position({command})")
return self.set_position(command)
# 使用示例
if __name__ == "__main__":
@@ -309,13 +267,6 @@ if __name__ == "__main__":
print(f"\n🔌 切换到2号位: {valve.set_to_port(2)}")
print(f"📍 当前状态: {valve}")
# 显示所有可用位置
print(f"\n📋 可用位置: {valve.get_available_positions()}")
print(f"🗺️ 端口映射: {valve.get_available_ports()}")
# 获取详细信息
print(f"\n📊 详细信息: {valve.get_info()}")
# 测试切换功能
print(f"\n🔄 智能切换测试:")
print(f"当前位置: {valve._current_position}")

View File

@@ -1,197 +0,0 @@
import asyncio
import logging
from typing import Dict, Any, Optional
class VirtualPump:
"""Virtual pump device for transfer and cleaning operations"""
def __init__(self, device_id: str = None, config: Dict[str, Any] = None, **kwargs):
# 处理可能的不同调用方式
if device_id is None and 'id' in kwargs:
device_id = kwargs.pop('id')
if config is None and 'config' in kwargs:
config = kwargs.pop('config')
# 设置默认值
self.device_id = device_id or "unknown_pump"
self.config = config or {}
self.logger = logging.getLogger(f"VirtualPump.{self.device_id}")
self.data = {}
# 从config或kwargs中获取配置参数
self.port = self.config.get('port') or kwargs.get('port', 'VIRTUAL')
self._max_volume = self.config.get('max_volume') or kwargs.get('max_volume', 50.0)
self._transfer_rate = self.config.get('transfer_rate') or kwargs.get('transfer_rate', 10.0)
print(f"=== VirtualPump {self.device_id} created with max_volume={self._max_volume}, transfer_rate={self._transfer_rate} ===")
async def initialize(self) -> bool:
"""Initialize virtual pump"""
self.logger.info(f"Initializing virtual pump {self.device_id}")
self.data.update({
"status": "Idle",
"valve_position": 0,
"current_volume": 0.0,
"max_volume": self._max_volume,
"transfer_rate": self._transfer_rate,
"from_vessel": "",
"to_vessel": "",
"progress": 0.0,
"transferred_volume": 0.0,
"current_status": "Ready"
})
return True
async def cleanup(self) -> bool:
"""Cleanup virtual pump"""
self.logger.info(f"Cleaning up virtual pump {self.device_id}")
return True
async def transfer(self, from_vessel: str, to_vessel: str, volume: float,
amount: str = "", time: float = 0.0, viscous: bool = False,
rinsing_solvent: str = "", rinsing_volume: float = 0.0,
rinsing_repeats: int = 0, solid: bool = False) -> bool:
"""Execute transfer operation"""
self.logger.info(f"Transferring {volume}mL from {from_vessel} to {to_vessel}")
# 计算转移时间
transfer_time = volume / self._transfer_rate if time == 0 else time
self.data.update({
"status": "Running",
"from_vessel": from_vessel,
"to_vessel": to_vessel,
"current_status": "Transferring",
"progress": 0.0,
"transferred_volume": 0.0
})
# 模拟转移过程
steps = 10
step_time = transfer_time / steps
step_volume = volume / steps
for i in range(steps):
await asyncio.sleep(step_time)
progress = (i + 1) / steps * 100
current_volume = step_volume * (i + 1)
self.data.update({
"progress": progress,
"transferred_volume": current_volume,
"current_status": f"Transferring: {progress:.1f}%"
})
self.logger.info(f"Transfer progress: {progress:.1f}%")
self.data.update({
"status": "Idle",
"current_status": "Transfer completed",
"progress": 100.0,
"transferred_volume": volume
})
return True
async def clean_vessel(self, vessel: str, solvent: str, volume: float,
temp: float, repeats: int = 1) -> bool:
"""Execute vessel cleaning operation - matches CleanVessel action"""
self.logger.info(f"Starting vessel cleaning: {vessel} with {solvent} ({volume}mL at {temp}°C, {repeats} repeats)")
# 更新设备状态
self.data.update({
"status": "Running",
"from_vessel": f"flask_{solvent}",
"to_vessel": vessel,
"current_status": "Cleaning in progress",
"progress": 0.0,
"transferred_volume": 0.0
})
# 计算清洗时间(基于体积和重复次数)
# 假设清洗速度为 transfer_rate 的一半(因为需要加载和排放)
cleaning_rate = self._transfer_rate / 2
cleaning_time_per_cycle = volume / cleaning_rate
total_cleaning_time = cleaning_time_per_cycle * repeats
# 模拟清洗过程
steps_per_repeat = 10 # 每次重复清洗分10个步骤
total_steps = steps_per_repeat * repeats
step_time = total_cleaning_time / total_steps
for repeat in range(repeats):
self.logger.info(f"Starting cleaning cycle {repeat + 1}/{repeats}")
for step in range(steps_per_repeat):
await asyncio.sleep(step_time)
# 计算当前进度
current_step = repeat * steps_per_repeat + step + 1
progress = (current_step / total_steps) * 100
# 计算已处理的体积
volume_processed = (current_step / total_steps) * volume * repeats
# 更新状态
self.data.update({
"progress": progress,
"transferred_volume": volume_processed,
"current_status": f"Cleaning cycle {repeat + 1}/{repeats} - Step {step + 1}/{steps_per_repeat} ({progress:.1f}%)"
})
self.logger.info(f"Cleaning progress: {progress:.1f}% (Cycle {repeat + 1}/{repeats})")
# 清洗完成
self.data.update({
"status": "Idle",
"current_status": "Cleaning completed successfully",
"progress": 100.0,
"transferred_volume": volume * repeats,
"from_vessel": "",
"to_vessel": ""
})
self.logger.info(f"Vessel cleaning completed: {vessel}")
return True
# 状态属性
@property
def status(self) -> str:
return self.data.get("status", "Unknown")
@property
def valve_position(self) -> int:
return self.data.get("valve_position", 0)
@property
def current_volume(self) -> float:
return self.data.get("current_volume", 0.0)
@property
def max_volume(self) -> float:
return self.data.get("max_volume", 0.0)
@property
def transfer_rate(self) -> float:
return self.data.get("transfer_rate", 0.0)
@property
def from_vessel(self) -> str:
return self.data.get("from_vessel", "")
@property
def to_vessel(self) -> str:
return self.data.get("to_vessel", "")
@property
def progress(self) -> float:
return self.data.get("progress", 0.0)
@property
def transferred_volume(self) -> float:
return self.data.get("transferred_volume", 0.0)
@property
def current_status(self) -> str:
return self.data.get("current_status", "Ready")

View File

@@ -99,8 +99,8 @@ class VirtualRotavap:
self.logger.error(f"❌ 时间参数类型无效: {type(time)}使用默认值180.0秒")
time = 180.0
# 确保time是float类型
time = float(time)
# 确保time是float类型; 并加速
time = float(time) / 10.0
# 🔧 简化处理如果vessel就是设备自己直接操作
if vessel == self.device_id:

View File

@@ -48,20 +48,6 @@ class VirtualSolenoidValve:
"""获取阀门位置状态"""
return "OPEN" if self._is_open else "CLOSED"
@property
def state(self) -> dict:
"""获取阀门完整状态"""
return {
"device_id": self.device_id,
"port": self.port,
"voltage": self.voltage,
"response_time": self.response_time,
"is_open": self._is_open,
"valve_state": self._valve_state,
"status": self._status,
"position": self.valve_position
}
async def set_valve_position(self, command: str = None, **kwargs):
"""
设置阀门位置 - ROS动作接口

View File

@@ -319,21 +319,6 @@ class VirtualSolidDispenser:
def total_operations(self) -> int:
return self._total_operations
def get_device_info(self) -> Dict[str, Any]:
"""获取设备状态信息 📊"""
info = {
"device_id": self.device_id,
"status": self._status,
"current_reagent": self._current_reagent,
"last_dispensed_amount": self._dispensed_amount,
"total_operations": self._total_operations,
"max_capacity": self.max_capacity,
"precision": self.precision
}
self.logger.debug(f"📊 设备信息: 状态={self._status}, 试剂={self._current_reagent}, 加样量={self._dispensed_amount:.6f}g")
return info
def __str__(self):
status_emoji = "" if self._status == "Ready" else "🔄" if self._status == "Dispensing" else "" if self._status == "Error" else "🏠"
return f"⚗️ VirtualSolidDispenser({status_emoji} {self.device_id}: {self._status}, 最后加样 {self._dispensed_amount:.3f}g)"
@@ -380,8 +365,6 @@ async def test_solid_dispenser():
mass="150 g" # 超过100g限制
)
print(f"📊 测试4结果: {result4}")
print(f"\n📊 最终设备信息: {dispenser.get_device_info()}")
print(f"✅ === 测试完成 === 🎉")

View File

@@ -321,7 +321,7 @@ class VirtualStirrer:
"min_speed": self._min_speed
}
self.logger.debug(f"📊 设备信息: 模式={self.operation_mode}, 速度={self.current_speed} RPM, 搅拌={self.is_stirring}")
# self.logger.debug(f"📊 设备信息: 模式={self.operation_mode}, 速度={self.current_speed} RPM, 搅拌={self.is_stirring}")
return info
def __str__(self):

View File

@@ -380,22 +380,6 @@ class VirtualTransferPump:
"""检查是否已满"""
return self._current_volume >= (self.max_volume - 0.01) # 允许小量误差
# 调试和状态信息
def get_pump_info(self) -> dict:
"""获取泵的详细信息"""
return {
"device_id": self.device_id,
"status": self._status,
"position": self._position,
"current_volume": self._current_volume,
"max_volume": self.max_volume,
"max_velocity": self._max_velocity,
"mode": self.mode.name,
"is_empty": self.is_empty(),
"is_full": self.is_full(),
"remaining_capacity": self.get_remaining_capacity()
}
def __str__(self):
return f"VirtualTransferPump({self.device_id}: {self._current_volume:.2f}/{self.max_volume} ml, {self._status})"
@@ -425,8 +409,6 @@ async def demo():
result = await pump.set_position(0.0)
print(f"Empty result: {result}")
print(f"After emptying: {pump}")
print("\nPump info:", pump.get_pump_info())
if __name__ == "__main__":