add virtual_separator virtual_rotavap

fix transfer_pump
This commit is contained in:
Xuwznln
2025-06-16 12:15:54 +08:00
parent 57cb120c8c
commit 9f8f6e55c4
5 changed files with 692 additions and 168 deletions

View File

@@ -46,6 +46,8 @@ class HTTPClient:
headers={"Authorization": f"lab {self.auth}"},
timeout=5,
)
if response.status_code != 200:
logger.error(f"添加物料关系失败: {response.text}")
return response
def resource_add(self, resources: List[Dict[str, Any]], database_process_later: bool) -> requests.Response:
@@ -64,6 +66,8 @@ class HTTPClient:
headers={"Authorization": f"lab {self.auth}"},
timeout=5,
)
if response.status_code != 200:
logger.error(f"添加物料失败: {response.text}")
return response
def resource_get(self, id: str, with_children: bool = False) -> Dict[str, Any]:

View File

@@ -0,0 +1,172 @@
import asyncio
import logging
from typing import Dict, Any, Optional
class VirtualRotavap:
"""Virtual rotary evaporator device for EvaporateProtocol testing"""
def __init__(self, device_id: Optional[str] = None, config: Optional[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_rotavap"
self.config = config or {}
self.logger = logging.getLogger(f"VirtualRotavap.{self.device_id}")
self.data = {}
# 添加调试信息
print(f"=== VirtualRotavap {self.device_id} is being created! ===")
print(f"=== Config: {self.config} ===")
print(f"=== Kwargs: {kwargs} ===")
# 从config或kwargs中获取配置参数
self.port = self.config.get("port") or kwargs.get("port", "VIRTUAL")
self._max_temp = self.config.get("max_temp") or kwargs.get("max_temp", 180.0)
self._max_rotation_speed = self.config.get("max_rotation_speed") or kwargs.get("max_rotation_speed", 280.0)
# 处理其他kwargs参数但跳过已知的配置参数
skip_keys = {"port", "max_temp", "max_rotation_speed"}
for key, value in kwargs.items():
if key not in skip_keys and not hasattr(self, key):
setattr(self, key, value)
async def initialize(self) -> bool:
"""Initialize virtual rotary evaporator"""
print(f"=== VirtualRotavap {self.device_id} initialize() called! ===")
self.logger.info(f"Initializing virtual rotary evaporator {self.device_id}")
self.data.update(
{
"status": "Idle",
"rotavap_state": "Ready",
"current_temp": 25.0,
"target_temp": 25.0,
"max_temp": self._max_temp,
"rotation_speed": 0.0,
"max_rotation_speed": self._max_rotation_speed,
"vacuum_pressure": 1.0, # atmospheric pressure
"evaporated_volume": 0.0,
"progress": 0.0,
"message": "",
}
)
return True
async def cleanup(self) -> bool:
"""Cleanup virtual rotary evaporator"""
self.logger.info(f"Cleaning up virtual rotary evaporator {self.device_id}")
return True
async def evaporate(
self, vessel: str, pressure: float = 0.5, temp: float = 60.0, time: float = 300.0, stir_speed: float = 100.0
) -> bool:
"""Execute evaporate action - matches Evaporate action"""
self.logger.info(f"Evaporate: vessel={vessel}, pressure={pressure}, temp={temp}, time={time}")
# 验证参数
if temp > self._max_temp:
self.logger.error(f"Temperature {temp} exceeds maximum {self._max_temp}")
self.data["message"] = f"温度 {temp} 超过最大值 {self._max_temp}"
return False
if stir_speed > self._max_rotation_speed:
self.logger.error(f"Rotation speed {stir_speed} exceeds maximum {self._max_rotation_speed}")
self.data["message"] = f"旋转速度 {stir_speed} 超过最大值 {self._max_rotation_speed}"
return False
if pressure < 0.01 or pressure > 1.0:
self.logger.error(f"Pressure {pressure} bar is out of valid range (0.01-1.0)")
self.data["message"] = f"真空度 {pressure} bar 超出有效范围 (0.01-1.0)"
return False
# 开始蒸发
self.data.update(
{
"status": "Running",
"rotavap_state": "Evaporating",
"target_temp": temp,
"current_temp": temp,
"rotation_speed": stir_speed,
"vacuum_pressure": pressure,
"vessel": vessel,
"target_time": time,
"progress": 0.0,
"message": f"正在蒸发: {vessel}",
}
)
# 模拟蒸发过程
simulation_time = min(time / 60.0, 10.0) # 最多模拟10秒
for progress in range(0, 101, 10):
await asyncio.sleep(simulation_time / 10)
self.data["progress"] = progress
self.data["evaporated_volume"] = progress * 0.5 # 假设最多蒸发50mL
# 蒸发完成
evaporated_vol = 50.0 # 假设蒸发了50mL
self.data.update(
{
"status": "Idle",
"rotavap_state": "Ready",
"current_temp": 25.0,
"target_temp": 25.0,
"rotation_speed": 0.0,
"vacuum_pressure": 1.0,
"evaporated_volume": evaporated_vol,
"progress": 100.0,
"message": f"蒸发完成: {evaporated_vol}mL",
}
)
self.logger.info(f"Evaporation completed: {evaporated_vol}mL from {vessel}")
return True
# 状态属性
@property
def status(self) -> str:
return self.data.get("status", "Unknown")
@property
def rotavap_state(self) -> str:
return self.data.get("rotavap_state", "Unknown")
@property
def current_temp(self) -> float:
return self.data.get("current_temp", 25.0)
@property
def target_temp(self) -> float:
return self.data.get("target_temp", 25.0)
@property
def max_temp(self) -> float:
return self.data.get("max_temp", self._max_temp)
@property
def rotation_speed(self) -> float:
return self.data.get("rotation_speed", 0.0)
@property
def max_rotation_speed(self) -> float:
return self.data.get("max_rotation_speed", self._max_rotation_speed)
@property
def vacuum_pressure(self) -> float:
return self.data.get("vacuum_pressure", 1.0)
@property
def evaporated_volume(self) -> float:
return self.data.get("evaporated_volume", 0.0)
@property
def progress(self) -> float:
return self.data.get("progress", 0.0)
@property
def message(self) -> str:
return self.data.get("message", "")

View File

@@ -0,0 +1,184 @@
import asyncio
import logging
from typing import Dict, Any, Optional
class VirtualSeparator:
"""Virtual separator device for SeparateProtocol testing"""
def __init__(self, device_id: Optional[str] = None, config: Optional[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_separator"
self.config = config or {}
self.logger = logging.getLogger(f"VirtualSeparator.{self.device_id}")
self.data = {}
# 添加调试信息
print(f"=== VirtualSeparator {self.device_id} is being created! ===")
print(f"=== Config: {self.config} ===")
print(f"=== Kwargs: {kwargs} ===")
# 从config或kwargs中获取配置参数
self.port = self.config.get("port") or kwargs.get("port", "VIRTUAL")
self._volume = self.config.get("volume") or kwargs.get("volume", 250.0)
self._has_phases = self.config.get("has_phases") or kwargs.get("has_phases", True)
# 处理其他kwargs参数但跳过已知的配置参数
skip_keys = {"port", "volume", "has_phases"}
for key, value in kwargs.items():
if key not in skip_keys and not hasattr(self, key):
setattr(self, key, value)
async def initialize(self) -> bool:
"""Initialize virtual separator"""
print(f"=== VirtualSeparator {self.device_id} initialize() called! ===")
self.logger.info(f"Initializing virtual separator {self.device_id}")
self.data.update(
{
"status": "Ready",
"separator_state": "Ready",
"volume": self._volume,
"has_phases": self._has_phases,
"phase_separation": False,
"stir_speed": 0.0,
"settling_time": 0.0,
"progress": 0.0,
"message": "",
}
)
return True
async def cleanup(self) -> bool:
"""Cleanup virtual separator"""
self.logger.info(f"Cleaning up virtual separator {self.device_id}")
return True
async def separate(
self,
purpose: str,
product_phase: str,
from_vessel: str,
separation_vessel: str,
to_vessel: str,
waste_phase_to_vessel: str = "",
solvent: str = "",
solvent_volume: float = 50.0,
through: str = "",
repeats: int = 1,
stir_time: float = 30.0,
stir_speed: float = 300.0,
settling_time: float = 300.0,
) -> bool:
"""Execute separate action - matches Separate action"""
self.logger.info(f"Separate: purpose={purpose}, product_phase={product_phase}, from_vessel={from_vessel}")
# 验证参数
if product_phase not in ["top", "bottom"]:
self.logger.error(f"Invalid product_phase {product_phase}, must be 'top' or 'bottom'")
self.data["message"] = f"产物相位 {product_phase} 无效,必须是 'top''bottom'"
return False
if purpose not in ["wash", "extract"]:
self.logger.error(f"Invalid purpose {purpose}, must be 'wash' or 'extract'")
self.data["message"] = f"分离目的 {purpose} 无效,必须是 'wash''extract'"
return False
# 开始分离
self.data.update(
{
"status": "Running",
"separator_state": "Separating",
"purpose": purpose,
"product_phase": product_phase,
"from_vessel": from_vessel,
"separation_vessel": separation_vessel,
"to_vessel": to_vessel,
"waste_phase_to_vessel": waste_phase_to_vessel,
"solvent": solvent,
"solvent_volume": solvent_volume,
"repeats": repeats,
"stir_speed": stir_speed,
"settling_time": settling_time,
"phase_separation": True,
"progress": 0.0,
"message": f"正在分离: {from_vessel} -> {to_vessel}",
}
)
# 模拟分离过程
total_time = (stir_time + settling_time) * repeats
simulation_time = min(total_time / 60.0, 15.0) # 最多模拟15秒
for repeat in range(repeats):
# 搅拌阶段
for progress in range(0, 51, 10):
await asyncio.sleep(simulation_time / (repeats * 10))
overall_progress = ((repeat * 100) + (progress * 0.5)) / repeats
self.data["progress"] = overall_progress
self.data["message"] = f"{repeat+1}次分离 - 搅拌中 ({progress}%)"
# 静置分相阶段
for progress in range(50, 101, 10):
await asyncio.sleep(simulation_time / (repeats * 10))
overall_progress = ((repeat * 100) + (progress * 0.5)) / repeats
self.data["progress"] = overall_progress
self.data["message"] = f"{repeat+1}次分离 - 静置分相中 ({progress}%)"
# 分离完成
self.data.update(
{
"status": "Ready",
"separator_state": "Ready",
"phase_separation": False,
"stir_speed": 0.0,
"progress": 100.0,
"message": f"分离完成: {repeats}次分离操作",
}
)
self.logger.info(f"Separation completed: {repeats} cycles from {from_vessel} to {to_vessel}")
return True
# 状态属性
@property
def status(self) -> str:
return self.data.get("status", "Unknown")
@property
def separator_state(self) -> str:
return self.data.get("separator_state", "Unknown")
@property
def volume(self) -> float:
return self.data.get("volume", self._volume)
@property
def has_phases(self) -> bool:
return self.data.get("has_phases", self._has_phases)
@property
def phase_separation(self) -> bool:
return self.data.get("phase_separation", False)
@property
def stir_speed(self) -> float:
return self.data.get("stir_speed", 0.0)
@property
def settling_time(self) -> float:
return self.data.get("settling_time", 0.0)
@property
def progress(self) -> float:
return self.data.get("progress", 0.0)
@property
def message(self) -> str:
return self.data.get("message", "")

View File

@@ -14,9 +14,10 @@ class VirtualPumpMode(Enum):
class VirtualPump:
"""虚拟泵类 - 模拟泵的基本功能,无需实际硬件"""
def __init__(self, device_id: str = None, max_volume: float = 25.0, mode: VirtualPumpMode = VirtualPumpMode.Normal):
def __init__(self, device_id: str = None, max_volume: float = 25.0, mode: VirtualPumpMode = VirtualPumpMode.Normal, transfer_rate=0):
self.device_id = device_id or "virtual_pump"
self.max_volume = max_volume
self._transfer_rate = transfer_rate
self.mode = mode
# 状态变量
@@ -24,7 +25,7 @@ class VirtualPump:
self._position = 0.0 # 当前柱塞位置 (ml)
self._max_velocity = 5.0 # 默认最大速度 (ml/s)
self._current_volume = 0.0 # 当前注射器中的体积
self.logger = logging.getLogger(f"VirtualPump.{self.device_id}")
async def initialize(self) -> bool:
@@ -60,6 +61,10 @@ class VirtualPump:
def max_velocity(self) -> float:
return self._max_velocity
@property
def transfer_rate(self) -> float:
return self._transfer_rate
def set_max_velocity(self, velocity: float):
"""设置最大速度 (ml/s)"""
self._max_velocity = max(0.1, min(50.0, velocity)) # 限制在合理范围内

View File

@@ -44,6 +44,17 @@
# 描述:分离纯化设备,用于样品纯化
# 连接特性1个输入口 + 1个输出口
# 数据类型resource资源/样品连接)
# 9. virtual_rotavap - 虚拟旋转蒸发仪
# 描述:旋转蒸发仪用于溶剂蒸发和浓缩,具有加热、旋转和真空功能
# 连接特性1个输入口样品1个输出口浓缩物1个冷凝器出口回收溶剂
# 数据类型resource资源/样品连接)
# 10. virtual_separator - 虚拟分液器
# 描述:分液器用于两相液体的分离,可进行萃取和洗涤操作
# 连接特性1个输入口混合液2个输出口上相和下相
# 数据类型fluid流体连接
virtual_pump:
description: Virtual Pump for PumpTransferProtocol Testing
class:
@@ -52,7 +63,7 @@ virtual_pump:
status_types:
status: String
position: Float64
valve_position: Int32 # 修复:使用 Int32 而不是 String
valve_position: Int32 # 修复:使用 Int32 而不是 String
max_volume: Float64
current_volume: Float64
action_value_mappings:
@@ -83,13 +94,13 @@ virtual_pump:
success: success
# 虚拟泵节点配置 - 具有多通道阀门特性根据valve_position可连接多个容器
handles:
- handler_key: pump-inlet
label: Pump Inlet
data_type: fluid
io_type: target
data_source: handle
data_key: fluid_in
description: "泵的进液口,连接源容器"
- handler_key: pump-inlet
label: Pump Inlet
data_type: fluid
io_type: target
data_source: handle
data_key: fluid_in
description: "泵的进液口,连接源容器"
schema:
type: object
properties:
@@ -139,14 +150,14 @@ virtual_stirrer:
success: success
# 虚拟搅拌器节点配置 - 机械连接设备,单一双向连接点
handles:
- handler_key: stirrer-vessel
label: Vessel Connection
data_type: mechanical
side: SOUTH
io_type: undirected
data_source: handle
data_key: vessel
description: "搅拌器的机械连接口,直接与反应容器连接提供搅拌功能"
- handler_key: stirrer-vessel
label: Vessel Connection
data_type: mechanical
side: SOUTH
io_type: undirected
data_source: handle
data_key: vessel
description: "搅拌器的机械连接口,直接与反应容器连接提供搅拌功能"
schema:
type: object
properties:
@@ -182,77 +193,77 @@ virtual_multiway_valve:
success: success
# 八通阀门节点配置 - 1个输入口8个输出口可切换流向
handles:
- handler_key: multiway-valve-inlet
label: Valve Inlet
data_type: fluid
io_type: target
data_source: handle
data_key: fluid_in
description: "八通阀门进液口,接收来源流体"
- handler_key: multiway-valve-port-1
label: 1
data_type: fluid
side: NORTH
io_type: source
data_source: executor
data_key: fluid_port_1
description: "八通阀门端口1position=1时流体从此口流出"
- handler_key: multiway-valve-port-2
label: 2
data_type: fluid
side: EAST
io_type: source
data_source: executor
data_key: fluid_port_2
description: "八通阀门端口2position=2时流体从此口流出"
- handler_key: multiway-valve-port-3
label: 3
data_type: fluid
side: EAST
io_type: source
data_source: executor
data_key: fluid_port_3
description: "八通阀门端口3position=3时流体从此口流出"
- handler_key: multiway-valve-port-4
label: 4
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: fluid_port_4
description: "八通阀门端口4position=4时流体从此口流出"
- handler_key: multiway-valve-port-5
label: 5
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: fluid_port_5
description: "八通阀门端口5position=5时流体从此口流出"
- handler_key: multiway-valve-port-7
label: 7
data_type: fluid
side: WEST
io_type: source
data_source: executor
data_key: fluid_port_7
description: "八通阀门端口7position=7时流体从此口流出"
- handler_key: multiway-valve-port-6
label: 6
data_type: fluid
side: WEST
io_type: source
data_source: executor
data_key: fluid_port_6
description: "八通阀门端口6position=6时流体从此口流出"
- handler_key: multiway-valve-port-8
label: 8
data_type: fluid
side: NORTH
io_type: source
data_source: executor
data_key: fluid_port_8
description: "八通阀门端口8position=8时流体从此口流出"
- handler_key: multiway-valve-inlet
label: Valve Inlet
data_type: fluid
io_type: target
data_source: handle
data_key: fluid_in
description: "八通阀门进液口,接收来源流体"
- handler_key: multiway-valve-port-1
label: 1
data_type: fluid
side: NORTH
io_type: source
data_source: executor
data_key: fluid_port_1
description: "八通阀门端口1position=1时流体从此口流出"
- handler_key: multiway-valve-port-2
label: 2
data_type: fluid
side: EAST
io_type: source
data_source: executor
data_key: fluid_port_2
description: "八通阀门端口2position=2时流体从此口流出"
- handler_key: multiway-valve-port-3
label: 3
data_type: fluid
side: EAST
io_type: source
data_source: executor
data_key: fluid_port_3
description: "八通阀门端口3position=3时流体从此口流出"
- handler_key: multiway-valve-port-4
label: 4
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: fluid_port_4
description: "八通阀门端口4position=4时流体从此口流出"
- handler_key: multiway-valve-port-5
label: 5
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: fluid_port_5
description: "八通阀门端口5position=5时流体从此口流出"
- handler_key: multiway-valve-port-7
label: 7
data_type: fluid
side: WEST
io_type: source
data_source: executor
data_key: fluid_port_7
description: "八通阀门端口7position=7时流体从此口流出"
- handler_key: multiway-valve-port-6
label: 6
data_type: fluid
side: WEST
io_type: source
data_source: executor
data_key: fluid_port_6
description: "八通阀门端口6position=6时流体从此口流出"
- handler_key: multiway-valve-port-8
label: 8
data_type: fluid
side: NORTH
io_type: source
data_source: executor
data_key: fluid_port_8
description: "八通阀门端口8position=8时流体从此口流出"
schema:
type: object
properties:
@@ -270,19 +281,19 @@ virtual_solenoid_valve:
type: python
status_types:
status: String
valve_state: String # "open" or "closed"
valve_state: String # "open" or "closed"
is_open: Bool
action_value_mappings:
open:
type: SendCmd
goal:
goal:
command: "open"
feedback: {}
result:
success: success
close:
type: SendCmd
goal:
goal:
command: "close"
feedback: {}
result:
@@ -290,26 +301,26 @@ virtual_solenoid_valve:
set_state:
type: SendCmd
goal:
command: command
command: command
feedback: {}
result:
success: success
# 电磁阀门节点配置 - 双向流通的开关型阀门,流动方向由泵决定
handles:
- handler_key: solenoid-valve-port-in
label: in
data_type: fluid
io_type: undirected
data_source: handle
data_key: fluid_port
description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断"
- handler_key: solenoid-valve-port-out
label: out
data_type: fluid
io_type: undirected
data_source: handle
data_key: fluid_port
description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断"
- handler_key: solenoid-valve-port-in
label: in
data_type: fluid
io_type: undirected
data_source: handle
data_key: fluid_port
description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断"
- handler_key: solenoid-valve-port-out
label: out
data_type: fluid
io_type: undirected
data_source: handle
data_key: fluid_port
description: "电磁阀的双向流体口,开启时允许流体双向通过,关闭时完全阻断"
schema:
type: object
properties:
@@ -357,13 +368,13 @@ virtual_centrifuge:
message: message
# 虚拟离心机节点配置 - 单个样品处理设备,输入输出都是同一个样品容器
handles:
- handler_key: centrifuge-sample
label: Sample Input/Output
data_type: transport
io_type: undirected
data_source: handle
data_key: vessel
description: "需要离心的样品容器"
- handler_key: centrifuge-sample
label: Sample Input/Output
data_type: transport
io_type: undirected
data_source: handle
data_key: vessel
description: "需要离心的样品容器"
schema:
type: object
properties:
@@ -418,30 +429,30 @@ virtual_filter:
message: message
# 虚拟过滤器节点配置 - 分离设备1个输入(原始样品)2个输出(滤液和滤渣)
handles:
- handler_key: filter-in
label: Input
data_type: fluid
side: NORTH
io_type: target
data_source: handle
data_key: vessel
description: "需要过滤的原始样品容器"
- handler_key: filter-filtrate-out
label: Output
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: filtrate_vessel
description: "过滤后的滤液容器"
- handler_key: filter-residue-out
label: Residue
data_type: resource
side: WEST
io_type: source
data_source: executor
data_key: residue_vessel
description: "过滤后的滤渣(固体残留物)"
- handler_key: filter-in
label: Input
data_type: fluid
side: NORTH
io_type: target
data_source: handle
data_key: vessel
description: "需要过滤的原始样品容器"
- handler_key: filter-filtrate-out
label: Output
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: filtrate_vessel
description: "过滤后的滤液容器"
- handler_key: filter-residue-out
label: Residue
data_type: resource
side: WEST
io_type: source
data_source: executor
data_key: residue_vessel
description: "过滤后的滤渣(固体残留物)"
schema:
type: object
properties:
@@ -497,14 +508,14 @@ virtual_heatchill:
success: success
# 虚拟加热/冷却器节点配置 - 温控设备,单一双向连接点用于放置容器
handles:
- handler_key: heatchill-vessel
label: Connection
data_type: mechanical
side: NORTH
io_type: undirected
data_source: handle
data_key: vessel
description: "加热/冷却器的物理连接口,容器直接放置在设备上进行温度控制"
- handler_key: heatchill-vessel
label: Connection
data_type: mechanical
side: NORTH
io_type: undirected
data_source: handle
data_key: vessel
description: "加热/冷却器的物理连接口,容器直接放置在设备上进行温度控制"
schema:
type: object
properties:
@@ -516,7 +527,7 @@ virtual_heatchill:
default: 200.0
min_temp:
type: number
default: -80.0
default: -80
max_stir_speed:
type: number
default: 1000.0
@@ -533,10 +544,6 @@ virtual_transfer_pump:
max_volume: Float64
transfer_rate: Float64
from_vessel: String
to_vessel: String
progress: Float64
transferred_volume: Float64
current_status: String
action_value_mappings:
transfer:
type: Transfer
@@ -612,22 +619,22 @@ virtual_column:
message: message
# 虚拟色谱柱节点配置 - 分离纯化设备1个样品输入口1个纯化产物输出口
handles:
- handler_key: column-sample-inlet
label: Sample Input
data_type: fluid
side: NORTH
io_type: target
data_source: handle
data_key: from_vessel
description: "需要纯化的样品输入口"
- handler_key: column-product-outlet
label: Purified Product
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: to_vessel
description: "经过色谱柱纯化的产物输出口"
- handler_key: column-sample-inlet
label: Sample Input
data_type: fluid
side: NORTH
io_type: target
data_source: handle
data_key: from_vessel
description: "需要纯化的样品输入口"
- handler_key: column-product-outlet
label: Purified Product
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: to_vessel
description: "经过色谱柱纯化的产物输出口"
schema:
type: object
properties:
@@ -643,4 +650,156 @@ virtual_column:
column_diameter:
type: number
default: 2.0
additionalProperties: false
additionalProperties: false
virtual_rotavap:
description: Virtual Rotary Evaporator for EvaporateProtocol Testing
class:
module: unilabos.devices.virtual.virtual_rotavap:VirtualRotavap
type: python
status_types:
status: String
rotavap_state: String
current_temp: Float64
target_temp: Float64
max_temp: Float64
rotation_speed: Float64
max_rotation_speed: Float64
vacuum_pressure: Float64
evaporated_volume: Float64
progress: Float64
message: String
action_value_mappings:
evaporate:
type: Evaporate
goal:
vessel: vessel
pressure: pressure
temp: temp
time: time
stir_speed: stir_speed
feedback:
progress: progress
current_temp: current_temp
evaporated_volume: evaporated_volume
current_status: status
result:
success: success
message: message
# 虚拟旋转蒸发仪节点配置 - 蒸发浓缩设备1个输入口(样品)2个输出口(浓缩物和冷凝液)
handles:
- handler_key: rotavap-sample-inlet
label: Sample Input
data_type: fluid
side: NORTH
io_type: target
data_source: handle
data_key: vessel
description: "需要蒸发的样品输入口"
- handler_key: rotavap-concentrate-outlet
label: Concentrate
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: concentrate_vessel
description: "蒸发浓缩后的产物输出口"
- handler_key: rotavap-distillate-outlet
label: Distillate
data_type: fluid
side: WEST
io_type: source
data_source: executor
data_key: distillate_vessel
description: "冷凝回收的溶剂输出口"
schema:
type: object
properties:
port:
type: string
default: "VIRTUAL"
max_temp:
type: number
default: 180.0
max_rotation_speed:
type: number
default: 280.0
additionalProperties: false
virtual_separator:
description: Virtual Separator for SeparateProtocol Testing
class:
module: unilabos.devices.virtual.virtual_separator:VirtualSeparator
type: python
status_types:
status: String
separator_state: String
volume: Float64
has_phases: Bool
phase_separation: Bool
stir_speed: Float64
settling_time: Float64
progress: Float64
message: String
action_value_mappings:
separate:
type: Separate
goal:
purpose: purpose
product_phase: product_phase
from_vessel: from_vessel
separation_vessel: separation_vessel
to_vessel: to_vessel
waste_phase_to_vessel: waste_phase_to_vessel
solvent: solvent
solvent_volume: solvent_volume
through: through
repeats: repeats
stir_time: stir_time
stir_speed: stir_speed
settling_time: settling_time
feedback:
progress: progress
current_status: status
result:
success: success
message: message
# 虚拟分液器节点配置 - 分离设备1个输入口(混合液)2个输出口(上相和下相)
handles:
- handler_key: separator-inlet
label: Mixed Input
data_type: fluid
side: NORTH
io_type: target
data_source: handle
data_key: from_vessel
description: "需要分离的混合液体输入口"
- handler_key: separator-top-outlet
label: Top Phase
data_type: fluid
side: EAST
io_type: source
data_source: executor
data_key: top_outlet
description: "上相(轻相)液体输出口"
- handler_key: separator-bottom-outlet
label: Bottom Phase
data_type: fluid
side: SOUTH
io_type: source
data_source: executor
data_key: bottom_outlet
description: "下相(重相)液体输出口"
schema:
type: object
properties:
port:
type: string
default: "VIRTUAL"
volume:
type: number
default: 250.0
has_phases:
type: boolean
default: true
additionalProperties: false