mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 14:05:12 +00:00
bump version to 0.9.7 新增一个测试PumpTransferProtocol的teststation,亲测可以运行,将八通阀们和转移泵与pump_protocol适配
This commit is contained in:
@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n environment_name
|
|||||||
|
|
||||||
# Currently, you need to install the `unilabos_msgs` package
|
# Currently, you need to install the `unilabos_msgs` package
|
||||||
# You can download the system-specific package from the Release page
|
# You can download the system-specific package from the Release page
|
||||||
conda install ros-humble-unilabos-msgs-0.9.6-xxxxx.tar.bz2
|
conda install ros-humble-unilabos-msgs-0.9.7-xxxxx.tar.bz2
|
||||||
|
|
||||||
# Install PyLabRobot and other prerequisites
|
# Install PyLabRobot and other prerequisites
|
||||||
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n 环境名
|
|||||||
|
|
||||||
# 现阶段,需要安装 `unilabos_msgs` 包
|
# 现阶段,需要安装 `unilabos_msgs` 包
|
||||||
# 可以前往 Release 页面下载系统对应的包进行安装
|
# 可以前往 Release 页面下载系统对应的包进行安装
|
||||||
conda install ros-humble-unilabos-msgs-0.9.6-xxxxx.tar.bz2
|
conda install ros-humble-unilabos-msgs-0.9.7-xxxxx.tar.bz2
|
||||||
|
|
||||||
# 安装PyLabRobot等前置
|
# 安装PyLabRobot等前置
|
||||||
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package:
|
package:
|
||||||
name: ros-humble-unilabos-msgs
|
name: ros-humble-unilabos-msgs
|
||||||
version: 0.9.6
|
version: 0.9.7
|
||||||
source:
|
source:
|
||||||
path: ../../unilabos_msgs
|
path: ../../unilabos_msgs
|
||||||
folder: ros-humble-unilabos-msgs/src/work
|
folder: ros-humble-unilabos-msgs/src/work
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package:
|
package:
|
||||||
name: unilabos
|
name: unilabos
|
||||||
version: "0.9.6"
|
version: "0.9.7"
|
||||||
|
|
||||||
source:
|
source:
|
||||||
path: ../..
|
path: ../..
|
||||||
|
|||||||
2
setup.py
2
setup.py
@@ -4,7 +4,7 @@ package_name = 'unilabos'
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name=package_name,
|
name=package_name,
|
||||||
version='0.9.6',
|
version='0.9.7',
|
||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=['setuptools'],
|
install_requires=['setuptools'],
|
||||||
|
|||||||
304
test/experiments/mock_protocol/pumptransferteststation.json
Normal file
304
test/experiments/mock_protocol/pumptransferteststation.json
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "SimpleProtocolStation",
|
||||||
|
"name": "简单协议工作站",
|
||||||
|
"children": [
|
||||||
|
"transfer_pump_1",
|
||||||
|
"multiway_valve_1",
|
||||||
|
"flask_DMF",
|
||||||
|
"flask_ethyl_acetate",
|
||||||
|
"flask_methanol",
|
||||||
|
"main_reactor",
|
||||||
|
"waste_workup",
|
||||||
|
"collection_bottle_1",
|
||||||
|
"flask_air"
|
||||||
|
],
|
||||||
|
"parent": null,
|
||||||
|
"type": "device",
|
||||||
|
"class": "workstation",
|
||||||
|
"position": {
|
||||||
|
"x": 500,
|
||||||
|
"y": 200,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"protocol_type": ["PumpTransferProtocol"]
|
||||||
|
},
|
||||||
|
"data": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "transfer_pump_1",
|
||||||
|
"name": "转移泵1",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "device",
|
||||||
|
"class": "virtual_transfer_pump",
|
||||||
|
"position": {
|
||||||
|
"x": 500,
|
||||||
|
"y": 300,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "VIRTUAL",
|
||||||
|
"max_volume": 25.0,
|
||||||
|
"transfer_rate": 5.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"position": 0.0,
|
||||||
|
"status": "Idle",
|
||||||
|
"valve_position": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "multiway_valve_1",
|
||||||
|
"name": "八通阀1",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "device",
|
||||||
|
"class": "virtual_multiway_valve",
|
||||||
|
"position": {
|
||||||
|
"x": 500,
|
||||||
|
"y": 400,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"port": "VIRTUAL",
|
||||||
|
"positions": 8
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"current_position": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_DMF",
|
||||||
|
"name": "DMF试剂瓶",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 200,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 1000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "DMF",
|
||||||
|
"liquid_volume": 800.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_ethyl_acetate",
|
||||||
|
"name": "乙酸乙酯试剂瓶",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 300,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 1000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "ethyl_acetate",
|
||||||
|
"liquid_volume": 800.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_methanol",
|
||||||
|
"name": "甲醇试剂瓶",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 400,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 1000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": [
|
||||||
|
{
|
||||||
|
"liquid_type": "methanol",
|
||||||
|
"liquid_volume": 800.0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "main_reactor",
|
||||||
|
"name": "主反应器",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 600,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "waste_workup",
|
||||||
|
"name": "废液处理瓶",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 700,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 2000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "collection_bottle_1",
|
||||||
|
"name": "收集瓶1",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 800,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 1000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "flask_air",
|
||||||
|
"name": "空气瓶",
|
||||||
|
"children": [],
|
||||||
|
"parent": "SimpleProtocolStation",
|
||||||
|
"type": "container",
|
||||||
|
"class": null,
|
||||||
|
"position": {
|
||||||
|
"x": 100,
|
||||||
|
"y": 500,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"max_volume": 1000.0
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"liquid": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
{
|
||||||
|
"id": "link_pump_valve",
|
||||||
|
"source": "transfer_pump_1",
|
||||||
|
"target": "multiway_valve_1",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"transfer_pump_1": "transferpump",
|
||||||
|
"multiway_valve_1": "transferpump"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_air",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "flask_air",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "1",
|
||||||
|
"flask_air": "top"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_DMF",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "flask_DMF",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "2",
|
||||||
|
"flask_DMF": "outlet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_ethyl_acetate",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "flask_ethyl_acetate",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "3",
|
||||||
|
"flask_ethyl_acetate": "outlet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_methanol",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "flask_methanol",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "4",
|
||||||
|
"flask_methanol": "outlet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_reactor",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "main_reactor",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "5",
|
||||||
|
"main_reactor": "inlet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_waste",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "waste_workup",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "6",
|
||||||
|
"waste_workup": "inlet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "link_valve_collection",
|
||||||
|
"source": "multiway_valve_1",
|
||||||
|
"target": "collection_bottle_1",
|
||||||
|
"type": "fluid",
|
||||||
|
"port": {
|
||||||
|
"multiway_valve_1": "7",
|
||||||
|
"collection_bottle_1": "inlet"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -192,6 +192,16 @@ class VirtualMultiwayValve:
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"VirtualMultiwayValve(Position: {self._current_position}/{self.max_positions}, Port: {self.get_current_port()}, Status: {self._status})"
|
return f"VirtualMultiwayValve(Position: {self._current_position}/{self.max_positions}, Port: {self.get_current_port()}, Status: {self._status})"
|
||||||
|
|
||||||
|
def set_valve_position(self, command: Union[int, str]):
|
||||||
|
"""
|
||||||
|
设置阀门位置 - 兼容pump_protocol调用
|
||||||
|
这是set_position的别名方法,用于兼容pump_protocol.py
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command: 目标位置 (0-8) 或位置字符串
|
||||||
|
"""
|
||||||
|
return self.set_position(command)
|
||||||
|
|
||||||
|
|
||||||
# 使用示例
|
# 使用示例
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -11,22 +11,38 @@ class VirtualPumpMode(Enum):
|
|||||||
AccuratePosVel = 2
|
AccuratePosVel = 2
|
||||||
|
|
||||||
|
|
||||||
class VirtualPump:
|
class VirtualTransferPump:
|
||||||
"""虚拟泵类 - 模拟泵的基本功能,无需实际硬件"""
|
"""虚拟转移泵类 - 模拟泵的基本功能,无需实际硬件"""
|
||||||
|
|
||||||
def __init__(self, device_id: str = None, max_volume: float = 25.0, mode: VirtualPumpMode = VirtualPumpMode.Normal, transfer_rate=0):
|
def __init__(self, device_id: str = None, config: dict = None, **kwargs):
|
||||||
self.device_id = device_id or "virtual_pump"
|
"""
|
||||||
self.max_volume = max_volume
|
初始化虚拟转移泵
|
||||||
self._transfer_rate = transfer_rate
|
|
||||||
self.mode = mode
|
|
||||||
|
|
||||||
# 状态变量
|
Args:
|
||||||
|
device_id: 设备ID
|
||||||
|
config: 配置字典,包含max_volume, port等参数
|
||||||
|
**kwargs: 其他参数,确保兼容性
|
||||||
|
"""
|
||||||
|
self.device_id = device_id or "virtual_transfer_pump"
|
||||||
|
|
||||||
|
# 从config或kwargs中获取参数,确保类型正确
|
||||||
|
if config:
|
||||||
|
self.max_volume = float(config.get('max_volume', 25.0))
|
||||||
|
self.port = config.get('port', 'VIRTUAL')
|
||||||
|
else:
|
||||||
|
self.max_volume = float(kwargs.get('max_volume', 25.0))
|
||||||
|
self.port = kwargs.get('port', 'VIRTUAL')
|
||||||
|
|
||||||
|
self._transfer_rate = float(kwargs.get('transfer_rate', 0))
|
||||||
|
self.mode = kwargs.get('mode', VirtualPumpMode.Normal)
|
||||||
|
|
||||||
|
# 状态变量 - 确保都是正确类型
|
||||||
self._status = "Idle"
|
self._status = "Idle"
|
||||||
self._position = 0.0 # 当前柱塞位置 (ml)
|
self._position = 0.0 # float
|
||||||
self._max_velocity = 5.0 # 默认最大速度 (ml/s)
|
self._max_velocity = 5.0 # float
|
||||||
self._current_volume = 0.0 # 当前注射器中的体积
|
self._current_volume = 0.0 # float
|
||||||
|
|
||||||
self.logger = logging.getLogger(f"VirtualPump.{self.device_id}")
|
self.logger = logging.getLogger(f"VirtualTransferPump.{self.device_id}")
|
||||||
|
|
||||||
async def initialize(self) -> bool:
|
async def initialize(self) -> bool:
|
||||||
"""初始化虚拟泵"""
|
"""初始化虚拟泵"""
|
||||||
@@ -86,30 +102,73 @@ class VirtualPump:
|
|||||||
velocity = self._max_velocity
|
velocity = self._max_velocity
|
||||||
return abs(volume) / velocity
|
return abs(volume) / velocity
|
||||||
|
|
||||||
# 基本泵操作
|
# 新的set_position方法 - 专门用于SetPumpPosition动作
|
||||||
async def set_position(self, position: float, velocity: float = None):
|
async def set_position(self, position: float, max_velocity: float = None):
|
||||||
"""
|
"""
|
||||||
移动到绝对位置
|
移动到绝对位置 - 专门用于SetPumpPosition动作
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
position (float): 目标位置 (ml)
|
position (float): 目标位置 (ml)
|
||||||
velocity (float): 移动速度 (ml/s)
|
max_velocity (float): 移动速度 (ml/s)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: 符合SetPumpPosition.action定义的结果
|
||||||
"""
|
"""
|
||||||
position = max(0, min(self.max_volume, position)) # 限制在有效范围内
|
try:
|
||||||
|
# 验证并转换参数
|
||||||
|
target_position = float(position)
|
||||||
|
velocity = float(max_velocity) if max_velocity is not None else self._max_velocity
|
||||||
|
|
||||||
volume_to_move = abs(position - self._position)
|
# 限制位置在有效范围内
|
||||||
duration = self._calculate_duration(volume_to_move, velocity)
|
target_position = max(0.0, min(float(self.max_volume), target_position))
|
||||||
|
|
||||||
self.logger.info(f"Moving to position {position} ml (current: {self._position} ml)")
|
# 计算移动距离和时间
|
||||||
|
volume_to_move = abs(target_position - self._position)
|
||||||
|
duration = self._calculate_duration(volume_to_move, velocity)
|
||||||
|
|
||||||
# 模拟移动过程
|
self.logger.info(f"SET_POSITION: Moving to {target_position} ml (current: {self._position} ml), velocity: {velocity} ml/s")
|
||||||
await self._simulate_operation(duration)
|
|
||||||
|
|
||||||
self._position = position
|
# 模拟移动过程
|
||||||
self._current_volume = position # 假设位置等于体积
|
start_position = self._position
|
||||||
|
steps = 10 if duration > 0.1 else 1 # 如果移动距离很小,只用1步
|
||||||
|
step_duration = duration / steps if steps > 1 else duration
|
||||||
|
|
||||||
self.logger.info(f"Reached position {self._position} ml")
|
for i in range(steps + 1):
|
||||||
|
# 计算当前位置和进度
|
||||||
|
progress = (i / steps) * 100 if steps > 0 else 100
|
||||||
|
current_pos = start_position + (target_position - start_position) * (i / steps) if steps > 0 else target_position
|
||||||
|
|
||||||
|
# 更新状态
|
||||||
|
self._status = "Moving" if i < steps else "Idle"
|
||||||
|
self._position = current_pos
|
||||||
|
self._current_volume = current_pos
|
||||||
|
|
||||||
|
# 等待一小步时间
|
||||||
|
if i < steps and step_duration > 0:
|
||||||
|
await asyncio.sleep(step_duration)
|
||||||
|
|
||||||
|
# 确保最终位置准确
|
||||||
|
self._position = target_position
|
||||||
|
self._current_volume = target_position
|
||||||
|
self._status = "Idle"
|
||||||
|
|
||||||
|
self.logger.info(f"SET_POSITION: Reached position {self._position} ml, current volume: {self._current_volume} ml")
|
||||||
|
|
||||||
|
# 返回符合action定义的结果
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"message": f"Successfully moved to position {self._position} ml"
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = f"Failed to set position: {str(e)}"
|
||||||
|
self.logger.error(error_msg)
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"message": error_msg
|
||||||
|
}
|
||||||
|
|
||||||
|
# 其他泵操作方法
|
||||||
async def pull_plunger(self, volume: float, velocity: float = None):
|
async def pull_plunger(self, volume: float, velocity: float = None):
|
||||||
"""
|
"""
|
||||||
拉取柱塞(吸液)
|
拉取柱塞(吸液)
|
||||||
@@ -164,34 +223,15 @@ class VirtualPump:
|
|||||||
|
|
||||||
# 便捷操作方法
|
# 便捷操作方法
|
||||||
async def aspirate(self, volume: float, velocity: float = None):
|
async def aspirate(self, volume: float, velocity: float = None):
|
||||||
"""
|
"""吸液操作"""
|
||||||
吸液操作
|
|
||||||
|
|
||||||
Args:
|
|
||||||
volume (float): 吸液体积 (ml)
|
|
||||||
velocity (float): 吸液速度 (ml/s)
|
|
||||||
"""
|
|
||||||
await self.pull_plunger(volume, velocity)
|
await self.pull_plunger(volume, velocity)
|
||||||
|
|
||||||
async def dispense(self, volume: float, velocity: float = None):
|
async def dispense(self, volume: float, velocity: float = None):
|
||||||
"""
|
"""排液操作"""
|
||||||
排液操作
|
|
||||||
|
|
||||||
Args:
|
|
||||||
volume (float): 排液体积 (ml)
|
|
||||||
velocity (float): 排液速度 (ml/s)
|
|
||||||
"""
|
|
||||||
await self.push_plunger(volume, velocity)
|
await self.push_plunger(volume, velocity)
|
||||||
|
|
||||||
async def transfer(self, volume: float, aspirate_velocity: float = None, dispense_velocity: float = None):
|
async def transfer(self, volume: float, aspirate_velocity: float = None, dispense_velocity: float = None):
|
||||||
"""
|
"""转移操作(先吸后排)"""
|
||||||
转移操作(先吸后排)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
volume (float): 转移体积 (ml)
|
|
||||||
aspirate_velocity (float): 吸液速度 (ml/s)
|
|
||||||
dispense_velocity (float): 排液速度 (ml/s)
|
|
||||||
"""
|
|
||||||
# 吸液
|
# 吸液
|
||||||
await self.aspirate(volume, aspirate_velocity)
|
await self.aspirate(volume, aspirate_velocity)
|
||||||
|
|
||||||
@@ -252,7 +292,7 @@ class VirtualPump:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"VirtualPump({self.device_id}: {self._current_volume:.2f}/{self.max_volume} ml, {self._status})"
|
return f"VirtualTransferPump({self.device_id}: {self._current_volume:.2f}/{self.max_volume} ml, {self._status})"
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return self.__str__()
|
return self.__str__()
|
||||||
@@ -261,26 +301,24 @@ class VirtualPump:
|
|||||||
# 使用示例
|
# 使用示例
|
||||||
async def demo():
|
async def demo():
|
||||||
"""虚拟泵使用示例"""
|
"""虚拟泵使用示例"""
|
||||||
pump = VirtualPump("demo_pump", max_volume=50.0)
|
pump = VirtualTransferPump("demo_pump", {"max_volume": 50.0})
|
||||||
|
|
||||||
await pump.initialize()
|
await pump.initialize()
|
||||||
|
|
||||||
print(f"Initial state: {pump}")
|
print(f"Initial state: {pump}")
|
||||||
|
|
||||||
|
# 测试set_position方法
|
||||||
|
result = await pump.set_position(10.0, max_velocity=2.0)
|
||||||
|
print(f"Set position result: {result}")
|
||||||
|
print(f"After setting position to 10ml: {pump}")
|
||||||
|
|
||||||
# 吸液测试
|
# 吸液测试
|
||||||
await pump.aspirate(10.0, velocity=2.0)
|
await pump.aspirate(5.0, velocity=2.0)
|
||||||
print(f"After aspirating 10ml: {pump}")
|
print(f"After aspirating 5ml: {pump}")
|
||||||
|
|
||||||
# 排液测试
|
|
||||||
await pump.dispense(5.0, velocity=3.0)
|
|
||||||
print(f"After dispensing 5ml: {pump}")
|
|
||||||
|
|
||||||
# 转移测试
|
|
||||||
await pump.transfer(3.0)
|
|
||||||
print(f"After transfer 3ml: {pump}")
|
|
||||||
|
|
||||||
# 清空测试
|
# 清空测试
|
||||||
await pump.empty_syringe()
|
result = await pump.set_position(0.0)
|
||||||
|
print(f"Empty result: {result}")
|
||||||
print(f"After emptying: {pump}")
|
print(f"After emptying: {pump}")
|
||||||
|
|
||||||
print("\nPump info:", pump.get_pump_info())
|
print("\nPump info:", pump.get_pump_info())
|
||||||
|
|||||||
@@ -202,7 +202,14 @@ virtual_multiway_valve:
|
|||||||
set_position:
|
set_position:
|
||||||
type: SendCmd
|
type: SendCmd
|
||||||
goal:
|
goal:
|
||||||
command: position
|
command: command
|
||||||
|
feedback: {}
|
||||||
|
result:
|
||||||
|
success: success
|
||||||
|
set_valve_position:
|
||||||
|
type: SendCmd
|
||||||
|
goal:
|
||||||
|
command: command
|
||||||
feedback: {}
|
feedback: {}
|
||||||
result:
|
result:
|
||||||
success: success
|
success: success
|
||||||
@@ -559,13 +566,14 @@ virtual_transfer_pump:
|
|||||||
description: Virtual Transfer Pump for TransferProtocol Testing (Syringe-style)
|
description: Virtual Transfer Pump for TransferProtocol Testing (Syringe-style)
|
||||||
icon: Pump.webp
|
icon: Pump.webp
|
||||||
class:
|
class:
|
||||||
module: unilabos.devices.virtual.virtual_transferpump:VirtualPump
|
module: unilabos.devices.virtual.virtual_transferpump:VirtualTransferPump
|
||||||
type: python
|
type: python
|
||||||
status_types:
|
status_types:
|
||||||
status: String
|
status: String
|
||||||
current_volume: Float64
|
current_volume: Float64
|
||||||
max_volume: Float64
|
max_volume: Float64
|
||||||
transfer_rate: Float64
|
transfer_rate: Float64
|
||||||
|
position: Float64
|
||||||
action_value_mappings:
|
action_value_mappings:
|
||||||
transfer:
|
transfer:
|
||||||
type: Transfer
|
type: Transfer
|
||||||
@@ -587,6 +595,18 @@ virtual_transfer_pump:
|
|||||||
result:
|
result:
|
||||||
success: success
|
success: success
|
||||||
message: message
|
message: message
|
||||||
|
set_position:
|
||||||
|
type: SetPumpPosition # ← 使用新的动作类型
|
||||||
|
goal:
|
||||||
|
position: position # ← 直接映射参数名
|
||||||
|
max_velocity: max_velocity # ← 直接映射参数名
|
||||||
|
feedback:
|
||||||
|
status: status
|
||||||
|
current_position: current_position
|
||||||
|
progress: progress
|
||||||
|
result:
|
||||||
|
success: success
|
||||||
|
message: message
|
||||||
# 注射器式转移泵节点配置 - 只有一个双向连接口,可吸入和排出液体
|
# 注射器式转移泵节点配置 - 只有一个双向连接口,可吸入和排出液体
|
||||||
handles:
|
handles:
|
||||||
- handler_key: transferpump
|
- handler_key: transferpump
|
||||||
@@ -603,12 +623,15 @@ virtual_transfer_pump:
|
|||||||
port:
|
port:
|
||||||
type: string
|
type: string
|
||||||
default: "VIRTUAL"
|
default: "VIRTUAL"
|
||||||
|
description: "通信端口"
|
||||||
max_volume:
|
max_volume:
|
||||||
type: number
|
type: number
|
||||||
default: 50.0
|
default: 50.0
|
||||||
|
description: "最大注射器容量 (mL)"
|
||||||
transfer_rate:
|
transfer_rate:
|
||||||
type: number
|
type: number
|
||||||
default: 5.0
|
default: 5.0
|
||||||
|
description: "默认转移速率 (mL/s)"
|
||||||
additionalProperties: false
|
additionalProperties: false
|
||||||
|
|
||||||
virtual_column:
|
virtual_column:
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ set(action_files
|
|||||||
"action/StopPurge.action"
|
"action/StopPurge.action"
|
||||||
"action/StopStir.action"
|
"action/StopStir.action"
|
||||||
"action/Transfer.action"
|
"action/Transfer.action"
|
||||||
|
"action/SetPumpPosition.action"
|
||||||
"action/LiquidHandlerProtocolCreation.action"
|
"action/LiquidHandlerProtocolCreation.action"
|
||||||
"action/LiquidHandlerAspirate.action"
|
"action/LiquidHandlerAspirate.action"
|
||||||
"action/LiquidHandlerDiscardTips.action"
|
"action/LiquidHandlerDiscardTips.action"
|
||||||
|
|||||||
13
unilabos_msgs/action/SetPumpPosition.action
Normal file
13
unilabos_msgs/action/SetPumpPosition.action
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Goal - 目标参数
|
||||||
|
float64 position # 目标位置 (ml)
|
||||||
|
float64 max_velocity # 最大速度 (ml/s)
|
||||||
|
---
|
||||||
|
# Result - 结果
|
||||||
|
string return_info
|
||||||
|
bool success # 操作是否成功
|
||||||
|
string message # 操作结果消息
|
||||||
|
---
|
||||||
|
# Feedback - 反馈
|
||||||
|
string status # 当前状态
|
||||||
|
float64 current_position # 当前位置
|
||||||
|
float64 progress # 进度百分比 (0-100)
|
||||||
Reference in New Issue
Block a user