mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 14:05:12 +00:00
修复了部分的protocol因为XDL更新导致的问题
但是pumptransfer,add,dissolve,separate还没修,后续还需要写virtual固体加料器
This commit is contained in:
@@ -51,9 +51,9 @@ class PumpTransferProtocol(BaseModel):
|
|||||||
time: float = 0
|
time: float = 0
|
||||||
viscous: bool = False
|
viscous: bool = False
|
||||||
rinsing_solvent: str = "air" <Transfer from_vessel="main_reactor" to_vessel="rotavap"/>
|
rinsing_solvent: str = "air" <Transfer from_vessel="main_reactor" to_vessel="rotavap"/>
|
||||||
rinsing_volume: float = 5000
|
rinsing_volume: float = 5000 <Transfer event="A" from_vessel="reactor" rate_spec="dropwise" to_vessel="main_reactor"/>
|
||||||
rinsing_repeats: int = 2
|
rinsing_repeats: int = 2 <Transfer from_vessel="separator" through="cartridge" to_vessel="rotavap"/>
|
||||||
solid: bool = False
|
solid: bool = False 添加了缺失的参数,但是体积为0以及打印日志的问题修不好
|
||||||
flowrate: float = 500
|
flowrate: float = 500
|
||||||
transfer_flowrate: float = 2500
|
transfer_flowrate: float = 2500
|
||||||
|
|
||||||
@@ -66,9 +66,9 @@ class SeparateProtocol(BaseModel):
|
|||||||
waste_phase_to_vessel: str
|
waste_phase_to_vessel: str
|
||||||
solvent: str
|
solvent: str
|
||||||
solvent_volume: float <Separate product_phase="bottom" purpose="wash" solvent="water" vessel="separator" volume="?"/>
|
solvent_volume: float <Separate product_phase="bottom" purpose="wash" solvent="water" vessel="separator" volume="?"/>
|
||||||
through: str
|
through: str <Separate product_phase="top" purpose="separate" vessel="separator"/>
|
||||||
repeats: int
|
repeats: int <Separate product_phase="bottom" purpose="extract" repeats="3" solvent="CH2Cl2" vessel="separator" volume="?"/>
|
||||||
stir_time: float
|
stir_time: float<Separate product_phase="top" product_vessel="flask" purpose="separate" vessel="separator" waste_vessel="separator"/>
|
||||||
stir_speed: float
|
stir_speed: float
|
||||||
settling_time: float
|
settling_time: float
|
||||||
|
|
||||||
@@ -77,14 +77,14 @@ class EvaporateProtocol(BaseModel):
|
|||||||
vessel: str
|
vessel: str
|
||||||
pressure: float
|
pressure: float
|
||||||
temp: float <Evaporate solvent="ethanol" vessel="rotavap"/>
|
temp: float <Evaporate solvent="ethanol" vessel="rotavap"/>
|
||||||
time: float
|
time: float 加完了
|
||||||
stir_speed: float
|
stir_speed: float
|
||||||
|
|
||||||
|
|
||||||
class EvacuateAndRefillProtocol(BaseModel):
|
class EvacuateAndRefillProtocol(BaseModel):
|
||||||
vessel: str
|
vessel: str
|
||||||
gas: str <EvacuateAndRefill gas="nitrogen" vessel="main_reactor"/>
|
gas: str <EvacuateAndRefill gas="nitrogen" vessel="main_reactor"/>
|
||||||
repeats: int
|
repeats: int 处理完了
|
||||||
|
|
||||||
class AddProtocol(BaseModel):
|
class AddProtocol(BaseModel):
|
||||||
vessel: str
|
vessel: str
|
||||||
@@ -95,21 +95,27 @@ class AddProtocol(BaseModel):
|
|||||||
time: float
|
time: float
|
||||||
stir: bool
|
stir: bool
|
||||||
stir_speed: float <Add reagent="ethanol" vessel="main_reactor" volume="2.7 mL"/>
|
stir_speed: float <Add reagent="ethanol" vessel="main_reactor" volume="2.7 mL"/>
|
||||||
|
<Add event="A" mass="19.3 g" mol="0.28 mol" rate_spec="portionwise" reagent="sodium nitrite" time="1 h" vessel="main_reactor"/>
|
||||||
|
<Add mass="4.5 g" mol="16.2 mmol" reagent="(S)-2-phthalimido-6-hydroxyhexanoic acid" vessel="main_reactor"/>
|
||||||
|
<Add purpose="dilute" reagent="hydrochloric acid" vessel="main_reactor" volume="?"/>
|
||||||
|
<Add equiv="1.1" event="B" mol="25.2 mmol" rate_spec="dropwise" reagent="1-fluoro-2-nitrobenzene" time="20 min"
|
||||||
|
vessel="main_reactor" volume="2.67 mL"/>
|
||||||
|
<Add ratio="?" reagent="tetrahydrofuran|tert-butanol" vessel="main_reactor" volume="?"/>
|
||||||
viscous: bool
|
viscous: bool
|
||||||
purpose: str
|
purpose: str
|
||||||
|
|
||||||
class CentrifugeProtocol(BaseModel):
|
class CentrifugeProtocol(BaseModel):
|
||||||
vessel: str
|
vessel: str
|
||||||
speed: float
|
speed: float
|
||||||
time: float 自创的
|
time: float 没毛病
|
||||||
temp: float
|
temp: float
|
||||||
|
|
||||||
class FilterProtocol(BaseModel):
|
class FilterProtocol(BaseModel):
|
||||||
vessel: str
|
vessel: str
|
||||||
filtrate_vessel: str
|
filtrate_vessel: str
|
||||||
stir: bool <Filter vessel="filter"/>
|
stir: bool <Filter vessel="filter"/>
|
||||||
stir_speed: float
|
stir_speed: float <Filter filtrate_vessel="rotavap" vessel="filter"/>
|
||||||
temp: float
|
temp: float 处理了
|
||||||
continue_heatchill: bool
|
continue_heatchill: bool
|
||||||
volume: float
|
volume: float
|
||||||
|
|
||||||
@@ -118,6 +124,9 @@ class HeatChillProtocol(BaseModel):
|
|||||||
temp: float
|
temp: float
|
||||||
time: float <HeatChill pressure="1 mbar" temp_spec="room temperature" time="?" vessel="main_reactor"/>
|
time: float <HeatChill pressure="1 mbar" temp_spec="room temperature" time="?" vessel="main_reactor"/>
|
||||||
<HeatChill temp_spec="room temperature" time_spec="overnight" vessel="main_reactor"/>
|
<HeatChill temp_spec="room temperature" time_spec="overnight" vessel="main_reactor"/>
|
||||||
|
<HeatChill temp="256 °C" time="?" vessel="main_reactor"/>
|
||||||
|
<HeatChill reflux_solvent="methanol" temp_spec="reflux" time="2 h" vessel="main_reactor"/>
|
||||||
|
<HeatChillToTemp temp_spec="room temperature" vessel="main_reactor"/>
|
||||||
stir: bool
|
stir: bool
|
||||||
stir_speed: float
|
stir_speed: float
|
||||||
purpose: str
|
purpose: str
|
||||||
@@ -133,7 +142,9 @@ class HeatChillStopProtocol(BaseModel):
|
|||||||
class StirProtocol(BaseModel):
|
class StirProtocol(BaseModel):
|
||||||
stir_time: float
|
stir_time: float
|
||||||
stir_speed: float <Stir time="0.5 h" vessel="main_reactor"/>
|
stir_speed: float <Stir time="0.5 h" vessel="main_reactor"/>
|
||||||
settling_time: float
|
<Stir event="A" time="30 min" vessel="main_reactor"/>
|
||||||
|
<Stir time_spec="several minutes" vessel="filter"/>
|
||||||
|
settling_time: float 处理完了
|
||||||
|
|
||||||
class StartStirProtocol(BaseModel):
|
class StartStirProtocol(BaseModel):
|
||||||
vessel: str
|
vessel: str
|
||||||
@@ -149,11 +160,11 @@ class TransferProtocol(BaseModel):
|
|||||||
volume: float
|
volume: float
|
||||||
amount: str = ""
|
amount: str = ""
|
||||||
time: float = 0
|
time: float = 0
|
||||||
viscous: bool = False <Transfer from_vessel="main_reactor" to_vessel="rotavap"/>
|
viscous: bool = False
|
||||||
rinsing_solvent: str = ""
|
rinsing_solvent: str = ""
|
||||||
rinsing_volume: float = 0.0
|
rinsing_volume: float = 0.0
|
||||||
rinsing_repeats: int = 0
|
rinsing_repeats: int = 0
|
||||||
solid: bool = False
|
solid: bool = False 这个protocol早该删掉了
|
||||||
|
|
||||||
class CleanVesselProtocol(BaseModel):
|
class CleanVesselProtocol(BaseModel):
|
||||||
vessel: str
|
vessel: str
|
||||||
@@ -166,8 +177,8 @@ class DissolveProtocol(BaseModel):
|
|||||||
vessel: str
|
vessel: str
|
||||||
solvent: str
|
solvent: str
|
||||||
volume: float <Dissolve mass="2.9 g" mol="0.12 mol" reagent="magnesium" vessel="main_reactor"/>
|
volume: float <Dissolve mass="2.9 g" mol="0.12 mol" reagent="magnesium" vessel="main_reactor"/>
|
||||||
amount: str = ""
|
amount: str = "" <Dissolve mass="12.9 g" reagent="4-tert-butylbenzyl bromide" vessel="main_reactor"/>
|
||||||
temp: float = 25.0
|
temp: float = 25.0 <Dissolve solvent="diisopropyl ether" vessel="rotavap" volume="?"/>
|
||||||
time: float = 0.0
|
time: float = 0.0
|
||||||
stir_speed: float = 0.0
|
stir_speed: float = 0.0
|
||||||
|
|
||||||
@@ -191,8 +202,8 @@ class WashSolidProtocol(BaseModel):
|
|||||||
volume: float
|
volume: float
|
||||||
filtrate_vessel: str = ""
|
filtrate_vessel: str = ""
|
||||||
temp: float = 25.0 <WashSolid filtrate_vessel="rotavap" solvent="formic acid" vessel="main_reactor" volume="?"/>
|
temp: float = 25.0 <WashSolid filtrate_vessel="rotavap" solvent="formic acid" vessel="main_reactor" volume="?"/>
|
||||||
stir: bool = False
|
stir: bool = False <WashSolid solvent="acetone" vessel="rotavap" volume="5 mL"/>
|
||||||
stir_speed: float = 0.0
|
stir_speed: float = 0.0 处理完了
|
||||||
time: float = 0.0
|
time: float = 0.0
|
||||||
repeats: int = 1
|
repeats: int = 1
|
||||||
|
|
||||||
|
|||||||
@@ -197,77 +197,148 @@ def generate_evacuateandrefill_protocol(
|
|||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
gas: str,
|
gas: str,
|
||||||
repeats: int = 1
|
# 🔧 删除 repeats 参数,直接硬编码为 3
|
||||||
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成抽真空和充气操作的动作序列
|
生成抽真空和充气操作的动作序列 - 简化版本
|
||||||
|
|
||||||
**修复版本**: 正确调用 pump_protocol 并处理异常
|
Args:
|
||||||
|
G: 设备图
|
||||||
|
vessel: 目标容器名称(必需)
|
||||||
|
gas: 气体名称(必需)
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: 动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# 🔧 硬编码重复次数为 3
|
||||||
|
repeats = 3
|
||||||
|
|
||||||
|
debug_print("=" * 60)
|
||||||
|
debug_print("开始生成抽真空充气协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - gas: {gas}")
|
||||||
|
debug_print(f" - repeats: {repeats} (硬编码)")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 60)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 参数设置 - 关键修复:减小体积避免超出泵容量
|
# === 参数验证和修正 ===
|
||||||
VACUUM_VOLUME = 20.0 # 减小抽真空体积
|
debug_print("步骤1: 参数验证和修正...")
|
||||||
REFILL_VOLUME = 20.0 # 减小充气体积
|
|
||||||
PUMP_FLOW_RATE = 2.5 # 降低流速
|
|
||||||
STIR_SPEED = 300.0
|
|
||||||
|
|
||||||
print(f"EVACUATE_REFILL: 开始生成协议,目标容器: {vessel}, 气体: {gas}, 重复次数: {repeats}")
|
# 验证必需参数
|
||||||
|
if not vessel:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
if not gas:
|
||||||
|
raise ValueError("gas 参数不能为空")
|
||||||
|
|
||||||
# 1. 验证设备存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
|
# 标准化气体名称
|
||||||
|
gas_aliases = {
|
||||||
|
'n2': 'nitrogen',
|
||||||
|
'ar': 'argon',
|
||||||
|
'air': 'air',
|
||||||
|
'o2': 'oxygen',
|
||||||
|
'co2': 'carbon_dioxide',
|
||||||
|
'h2': 'hydrogen'
|
||||||
|
}
|
||||||
|
|
||||||
|
original_gas = gas
|
||||||
|
gas_lower = gas.lower().strip()
|
||||||
|
if gas_lower in gas_aliases:
|
||||||
|
gas = gas_aliases[gas_lower]
|
||||||
|
debug_print(f"标准化气体名称: {original_gas} -> {gas}")
|
||||||
|
|
||||||
|
debug_print(f"最终参数: vessel={vessel}, gas={gas}, repeats={repeats}")
|
||||||
|
|
||||||
|
# === 查找设备 ===
|
||||||
|
debug_print("步骤2: 查找设备...")
|
||||||
|
|
||||||
# 2. 查找设备
|
|
||||||
try:
|
try:
|
||||||
vacuum_pump = find_vacuum_pump(G)
|
vacuum_pump = find_vacuum_pump(G)
|
||||||
vacuum_solenoid = find_associated_solenoid_valve(G, vacuum_pump)
|
|
||||||
gas_source = find_gas_source(G, gas)
|
gas_source = find_gas_source(G, gas)
|
||||||
|
vacuum_solenoid = find_associated_solenoid_valve(G, vacuum_pump)
|
||||||
gas_solenoid = find_associated_solenoid_valve(G, gas_source)
|
gas_solenoid = find_associated_solenoid_valve(G, gas_source)
|
||||||
stirrer_id = find_connected_stirrer(G, vessel)
|
stirrer_id = find_connected_stirrer(G, vessel)
|
||||||
|
|
||||||
print(f"EVACUATE_REFILL: 找到设备")
|
debug_print(f"设备配置:")
|
||||||
print(f" - 真空泵: {vacuum_pump}")
|
debug_print(f" - 真空泵: {vacuum_pump}")
|
||||||
print(f" - 气源: {gas_source}")
|
debug_print(f" - 气源: {gas_source}")
|
||||||
print(f" - 真空电磁阀: {vacuum_solenoid}")
|
debug_print(f" - 真空电磁阀: {vacuum_solenoid}")
|
||||||
print(f" - 气源电磁阀: {gas_solenoid}")
|
debug_print(f" - 气源电磁阀: {gas_solenoid}")
|
||||||
print(f" - 搅拌器: {stirrer_id}")
|
debug_print(f" - 搅拌器: {stirrer_id}")
|
||||||
|
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
raise ValueError(f"设备查找失败: {str(e)}")
|
raise ValueError(f"设备查找失败: {str(e)}")
|
||||||
|
|
||||||
# 3. **关键修复**: 验证路径存在性
|
# === 参数设置 ===
|
||||||
|
debug_print("步骤3: 参数设置...")
|
||||||
|
|
||||||
|
# 根据气体类型调整参数
|
||||||
|
if gas.lower() in ['nitrogen', 'argon']:
|
||||||
|
VACUUM_VOLUME = 25.0
|
||||||
|
REFILL_VOLUME = 25.0
|
||||||
|
PUMP_FLOW_RATE = 2.0
|
||||||
|
VACUUM_TIME = 30.0
|
||||||
|
REFILL_TIME = 20.0
|
||||||
|
debug_print("惰性气体:使用标准参数")
|
||||||
|
elif gas.lower() in ['air', 'oxygen']:
|
||||||
|
VACUUM_VOLUME = 20.0
|
||||||
|
REFILL_VOLUME = 20.0
|
||||||
|
PUMP_FLOW_RATE = 1.5
|
||||||
|
VACUUM_TIME = 45.0
|
||||||
|
REFILL_TIME = 25.0
|
||||||
|
debug_print("活性气体:使用保守参数")
|
||||||
|
else:
|
||||||
|
VACUUM_VOLUME = 15.0
|
||||||
|
REFILL_VOLUME = 15.0
|
||||||
|
PUMP_FLOW_RATE = 1.0
|
||||||
|
VACUUM_TIME = 60.0
|
||||||
|
REFILL_TIME = 30.0
|
||||||
|
debug_print("未知气体:使用安全参数")
|
||||||
|
|
||||||
|
STIR_SPEED = 200.0
|
||||||
|
|
||||||
|
debug_print(f"操作参数:")
|
||||||
|
debug_print(f" - 抽真空体积: {VACUUM_VOLUME}mL")
|
||||||
|
debug_print(f" - 充气体积: {REFILL_VOLUME}mL")
|
||||||
|
debug_print(f" - 泵流速: {PUMP_FLOW_RATE}mL/s")
|
||||||
|
debug_print(f" - 抽真空时间: {VACUUM_TIME}s")
|
||||||
|
debug_print(f" - 充气时间: {REFILL_TIME}s")
|
||||||
|
debug_print(f" - 搅拌速度: {STIR_SPEED}RPM")
|
||||||
|
|
||||||
|
# === 路径验证 ===
|
||||||
|
debug_print("步骤4: 路径验证...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 验证抽真空路径
|
# 验证抽真空路径
|
||||||
vacuum_path = nx.shortest_path(G, source=vessel, target=vacuum_pump)
|
vacuum_path = nx.shortest_path(G, source=vessel, target=vacuum_pump)
|
||||||
print(f"EVACUATE_REFILL: 抽真空路径: {' → '.join(vacuum_path)}")
|
debug_print(f"抽真空路径: {' → '.join(vacuum_path)}")
|
||||||
|
|
||||||
# 验证充气路径
|
# 验证充气路径
|
||||||
gas_path = nx.shortest_path(G, source=gas_source, target=vessel)
|
gas_path = nx.shortest_path(G, source=gas_source, target=vessel)
|
||||||
print(f"EVACUATE_REFILL: 充气路径: {' → '.join(gas_path)}")
|
debug_print(f"充气路径: {' → '.join(gas_path)}")
|
||||||
|
|
||||||
# **新增**: 检查路径中的边数据
|
|
||||||
for i in range(len(vacuum_path) - 1):
|
|
||||||
nodeA, nodeB = vacuum_path[i], vacuum_path[i + 1]
|
|
||||||
edge_data = G.get_edge_data(nodeA, nodeB)
|
|
||||||
if not edge_data or 'port' not in edge_data:
|
|
||||||
raise ValueError(f"路径 {nodeA} → {nodeB} 缺少端口信息")
|
|
||||||
print(f" 抽真空路径边 {nodeA} → {nodeB}: {edge_data}")
|
|
||||||
|
|
||||||
for i in range(len(gas_path) - 1):
|
|
||||||
nodeA, nodeB = gas_path[i], gas_path[i + 1]
|
|
||||||
edge_data = G.get_edge_data(nodeA, nodeB)
|
|
||||||
if not edge_data or 'port' not in edge_data:
|
|
||||||
raise ValueError(f"路径 {nodeA} → {nodeB} 缺少端口信息")
|
|
||||||
print(f" 充气路径边 {nodeA} → {nodeB}: {edge_data}")
|
|
||||||
|
|
||||||
except nx.NetworkXNoPath as e:
|
except nx.NetworkXNoPath as e:
|
||||||
|
debug_print(f"❌ 路径不存在: {str(e)}")
|
||||||
raise ValueError(f"路径不存在: {str(e)}")
|
raise ValueError(f"路径不存在: {str(e)}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 路径验证失败: {str(e)}")
|
||||||
raise ValueError(f"路径验证失败: {str(e)}")
|
raise ValueError(f"路径验证失败: {str(e)}")
|
||||||
|
|
||||||
# 4. 启动搅拌器
|
# === 启动搅拌器 ===
|
||||||
|
debug_print("步骤5: 启动搅拌器...")
|
||||||
|
|
||||||
if stirrer_id:
|
if stirrer_id:
|
||||||
|
debug_print(f"启动搅拌器: {stirrer_id}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "start_stir",
|
"action_name": "start_stir",
|
||||||
@@ -278,14 +349,25 @@ def generate_evacuateandrefill_protocol(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# 5. 执行多次抽真空-充气循环
|
# 等待搅拌稳定
|
||||||
for cycle in range(repeats):
|
action_sequence.append({
|
||||||
print(f"EVACUATE_REFILL: === 第 {cycle+1}/{repeats} 次循环 ===")
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": 5.0}
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
debug_print("未找到搅拌器,跳过搅拌启动")
|
||||||
|
|
||||||
|
# === 执行 3 次抽真空-充气循环 ===
|
||||||
|
debug_print("步骤6: 执行抽真空-充气循环...")
|
||||||
|
|
||||||
|
for cycle in range(repeats): # 这里 repeats = 3
|
||||||
|
debug_print(f"=== 第 {cycle+1}/{repeats} 次循环 ===")
|
||||||
|
|
||||||
# ============ 抽真空阶段 ============
|
# ============ 抽真空阶段 ============
|
||||||
print(f"EVACUATE_REFILL: 抽真空阶段开始")
|
debug_print(f"抽真空阶段开始")
|
||||||
|
|
||||||
# 启动真空泵
|
# 启动真空泵
|
||||||
|
debug_print(f"启动真空泵: {vacuum_pump}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_pump,
|
"device_id": vacuum_pump,
|
||||||
"action_name": "set_status",
|
"action_name": "set_status",
|
||||||
@@ -294,17 +376,15 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 开启真空电磁阀
|
# 开启真空电磁阀
|
||||||
if vacuum_solenoid:
|
if vacuum_solenoid:
|
||||||
|
debug_print(f"开启真空电磁阀: {vacuum_solenoid}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_solenoid,
|
"device_id": vacuum_solenoid,
|
||||||
"action_name": "set_valve_position",
|
"action_name": "set_valve_position",
|
||||||
"action_kwargs": {"command": "OPEN"}
|
"action_kwargs": {"command": "OPEN"}
|
||||||
})
|
})
|
||||||
|
|
||||||
# **关键修复**: 改进 pump_protocol 调用和错误处理
|
# 抽真空操作
|
||||||
print(f"EVACUATE_REFILL: 调用抽真空 pump_protocol: {vessel} → {vacuum_pump}")
|
debug_print(f"抽真空操作: {vessel} → {vacuum_pump}")
|
||||||
print(f" - 体积: {VACUUM_VOLUME} mL")
|
|
||||||
print(f" - 流速: {PUMP_FLOW_RATE} mL/s")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vacuum_transfer_actions = generate_pump_protocol_with_rinsing(
|
vacuum_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
@@ -312,9 +392,9 @@ def generate_evacuateandrefill_protocol(
|
|||||||
to_vessel=vacuum_pump,
|
to_vessel=vacuum_pump,
|
||||||
volume=VACUUM_VOLUME,
|
volume=VACUUM_VOLUME,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
duration=0.0, # 🔧 修复time参数名冲突
|
||||||
viscous=False,
|
viscous=False,
|
||||||
rinsing_solvent="", # **修复**: 明确不使用清洗
|
rinsing_solvent="",
|
||||||
rinsing_volume=0.0,
|
rinsing_volume=0.0,
|
||||||
rinsing_repeats=0,
|
rinsing_repeats=0,
|
||||||
solid=False,
|
solid=False,
|
||||||
@@ -324,52 +404,31 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
if vacuum_transfer_actions:
|
if vacuum_transfer_actions:
|
||||||
action_sequence.extend(vacuum_transfer_actions)
|
action_sequence.extend(vacuum_transfer_actions)
|
||||||
print(f"EVACUATE_REFILL: ✅ 成功添加 {len(vacuum_transfer_actions)} 个抽真空动作")
|
debug_print(f"✅ 添加了 {len(vacuum_transfer_actions)} 个抽真空动作")
|
||||||
else:
|
else:
|
||||||
print(f"EVACUATE_REFILL: ⚠️ 抽真空 pump_protocol 返回空序列")
|
debug_print("⚠️ 抽真空协议返回空序列,添加手动动作")
|
||||||
# **修复**: 添加手动泵动作作为备选
|
action_sequence.append({
|
||||||
action_sequence.extend([
|
"action_name": "wait",
|
||||||
{
|
"action_kwargs": {"time": VACUUM_TIME}
|
||||||
"device_id": "multiway_valve_1",
|
})
|
||||||
"action_name": "set_valve_position",
|
|
||||||
"action_kwargs": {"command": "5"} # 连接到反应器
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "transfer_pump_1",
|
|
||||||
"action_name": "set_position",
|
|
||||||
"action_kwargs": {
|
|
||||||
"position": VACUUM_VOLUME,
|
|
||||||
"max_velocity": PUMP_FLOW_RATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
print(f"EVACUATE_REFILL: 使用备选手动泵动作")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"EVACUATE_REFILL: ❌ 抽真空 pump_protocol 失败: {str(e)}")
|
debug_print(f"❌ 抽真空失败: {str(e)}")
|
||||||
import traceback
|
# 添加等待时间作为备选
|
||||||
print(f"EVACUATE_REFILL: 详细错误:\n{traceback.format_exc()}")
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": VACUUM_TIME}
|
||||||
|
})
|
||||||
|
|
||||||
# **修复**: 添加手动动作而不是忽略错误
|
# 抽真空后等待
|
||||||
print(f"EVACUATE_REFILL: 使用手动备选方案")
|
action_sequence.append({
|
||||||
action_sequence.extend([
|
"action_name": "wait",
|
||||||
{
|
"action_kwargs": {"time": 5.0}
|
||||||
"device_id": "multiway_valve_1",
|
})
|
||||||
"action_name": "set_valve_position",
|
|
||||||
"action_kwargs": {"command": "5"} # 反应器端口
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "transfer_pump_1",
|
|
||||||
"action_name": "set_position",
|
|
||||||
"action_kwargs": {
|
|
||||||
"position": VACUUM_VOLUME,
|
|
||||||
"max_velocity": PUMP_FLOW_RATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
# 关闭真空电磁阀
|
# 关闭真空电磁阀
|
||||||
if vacuum_solenoid:
|
if vacuum_solenoid:
|
||||||
|
debug_print(f"关闭真空电磁阀: {vacuum_solenoid}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_solenoid,
|
"device_id": vacuum_solenoid,
|
||||||
"action_name": "set_valve_position",
|
"action_name": "set_valve_position",
|
||||||
@@ -377,6 +436,7 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# 关闭真空泵
|
# 关闭真空泵
|
||||||
|
debug_print(f"关闭真空泵: {vacuum_pump}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_pump,
|
"device_id": vacuum_pump,
|
||||||
"action_name": "set_status",
|
"action_name": "set_status",
|
||||||
@@ -384,9 +444,10 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# ============ 充气阶段 ============
|
# ============ 充气阶段 ============
|
||||||
print(f"EVACUATE_REFILL: 充气阶段开始")
|
debug_print(f"充气阶段开始")
|
||||||
|
|
||||||
# 启动气源
|
# 启动气源
|
||||||
|
debug_print(f"启动气源: {gas_source}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_source,
|
"device_id": gas_source,
|
||||||
"action_name": "set_status",
|
"action_name": "set_status",
|
||||||
@@ -395,15 +456,15 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 开启气源电磁阀
|
# 开启气源电磁阀
|
||||||
if gas_solenoid:
|
if gas_solenoid:
|
||||||
|
debug_print(f"开启气源电磁阀: {gas_solenoid}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_solenoid,
|
"device_id": gas_solenoid,
|
||||||
"action_name": "set_valve_position",
|
"action_name": "set_valve_position",
|
||||||
"action_kwargs": {"command": "OPEN"}
|
"action_kwargs": {"command": "OPEN"}
|
||||||
})
|
})
|
||||||
|
|
||||||
# **关键修复**: 改进充气 pump_protocol 调用
|
# 充气操作
|
||||||
print(f"EVACUATE_REFILL: 调用充气 pump_protocol: {gas_source} → {vessel}")
|
debug_print(f"充气操作: {gas_source} → {vessel}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gas_transfer_actions = generate_pump_protocol_with_rinsing(
|
gas_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
@@ -411,9 +472,9 @@ def generate_evacuateandrefill_protocol(
|
|||||||
to_vessel=vessel,
|
to_vessel=vessel,
|
||||||
volume=REFILL_VOLUME,
|
volume=REFILL_VOLUME,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
duration=0.0, # 🔧 修复time参数名冲突
|
||||||
viscous=False,
|
viscous=False,
|
||||||
rinsing_solvent="", # **修复**: 明确不使用清洗
|
rinsing_solvent="",
|
||||||
rinsing_volume=0.0,
|
rinsing_volume=0.0,
|
||||||
rinsing_repeats=0,
|
rinsing_repeats=0,
|
||||||
solid=False,
|
solid=False,
|
||||||
@@ -423,77 +484,31 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
if gas_transfer_actions:
|
if gas_transfer_actions:
|
||||||
action_sequence.extend(gas_transfer_actions)
|
action_sequence.extend(gas_transfer_actions)
|
||||||
print(f"EVACUATE_REFILL: ✅ 成功添加 {len(gas_transfer_actions)} 个充气动作")
|
debug_print(f"✅ 添加了 {len(gas_transfer_actions)} 个充气动作")
|
||||||
else:
|
else:
|
||||||
print(f"EVACUATE_REFILL: ⚠️ 充气 pump_protocol 返回空序列")
|
debug_print("⚠️ 充气协议返回空序列,添加手动动作")
|
||||||
# **修复**: 添加手动充气动作
|
action_sequence.append({
|
||||||
action_sequence.extend([
|
"action_name": "wait",
|
||||||
{
|
"action_kwargs": {"time": REFILL_TIME}
|
||||||
"device_id": "multiway_valve_2",
|
})
|
||||||
"action_name": "set_valve_position",
|
|
||||||
"action_kwargs": {"command": "8"} # 氮气端口
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "transfer_pump_2",
|
|
||||||
"action_name": "set_position",
|
|
||||||
"action_kwargs": {
|
|
||||||
"position": REFILL_VOLUME,
|
|
||||||
"max_velocity": PUMP_FLOW_RATE
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "multiway_valve_2",
|
|
||||||
"action_name": "set_valve_position",
|
|
||||||
"action_kwargs": {"command": "5"} # 反应器端口
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "transfer_pump_2",
|
|
||||||
"action_name": "set_position",
|
|
||||||
"action_kwargs": {
|
|
||||||
"position": 0.0,
|
|
||||||
"max_velocity": PUMP_FLOW_RATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"EVACUATE_REFILL: ❌ 充气 pump_protocol 失败: {str(e)}")
|
debug_print(f"❌ 充气失败: {str(e)}")
|
||||||
import traceback
|
# 添加等待时间作为备选
|
||||||
print(f"EVACUATE_REFILL: 详细错误:\n{traceback.format_exc()}")
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": REFILL_TIME}
|
||||||
|
})
|
||||||
|
|
||||||
# **修复**: 使用手动充气动作
|
# 充气后等待
|
||||||
print(f"EVACUATE_REFILL: 使用手动充气方案")
|
action_sequence.append({
|
||||||
action_sequence.extend([
|
"action_name": "wait",
|
||||||
{
|
"action_kwargs": {"time": 5.0}
|
||||||
"device_id": "multiway_valve_2",
|
})
|
||||||
"action_name": "set_valve_position",
|
|
||||||
"action_kwargs": {"command": "8"} # 连接气源
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "transfer_pump_2",
|
|
||||||
"action_name": "set_position",
|
|
||||||
"action_kwargs": {
|
|
||||||
"position": REFILL_VOLUME,
|
|
||||||
"max_velocity": PUMP_FLOW_RATE
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "multiway_valve_2",
|
|
||||||
"action_name": "set_valve_position",
|
|
||||||
"action_kwargs": {"command": "5"} # 连接反应器
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"device_id": "transfer_pump_2",
|
|
||||||
"action_name": "set_position",
|
|
||||||
"action_kwargs": {
|
|
||||||
"position": 0.0,
|
|
||||||
"max_velocity": PUMP_FLOW_RATE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
# 关闭气源电磁阀
|
# 关闭气源电磁阀
|
||||||
if gas_solenoid:
|
if gas_solenoid:
|
||||||
|
debug_print(f"关闭气源电磁阀: {gas_solenoid}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_solenoid,
|
"device_id": gas_solenoid,
|
||||||
"action_name": "set_valve_position",
|
"action_name": "set_valve_position",
|
||||||
@@ -501,6 +516,7 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# 关闭气源
|
# 关闭气源
|
||||||
|
debug_print(f"关闭气源: {gas_source}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_source,
|
"device_id": gas_source,
|
||||||
"action_name": "set_status",
|
"action_name": "set_status",
|
||||||
@@ -509,22 +525,39 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 等待下一次循环
|
# 等待下一次循环
|
||||||
if cycle < repeats - 1:
|
if cycle < repeats - 1:
|
||||||
|
debug_print(f"等待下一次循环...")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 2.0}
|
"action_kwargs": {"time": 10.0}
|
||||||
})
|
})
|
||||||
|
|
||||||
# 停止搅拌器
|
# === 停止搅拌器 ===
|
||||||
|
debug_print("步骤7: 停止搅拌器...")
|
||||||
|
|
||||||
if stirrer_id:
|
if stirrer_id:
|
||||||
|
debug_print(f"停止搅拌器: {stirrer_id}")
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "stop_stir",
|
"action_name": "stop_stir",
|
||||||
"action_kwargs": {"vessel": vessel}
|
"action_kwargs": {"vessel": vessel}
|
||||||
})
|
})
|
||||||
|
|
||||||
print(f"EVACUATE_REFILL: 协议生成完成,共 {len(action_sequence)} 个动作")
|
# === 最终等待 ===
|
||||||
return action_sequence
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": 10.0}
|
||||||
|
})
|
||||||
|
|
||||||
|
# === 总结 ===
|
||||||
|
debug_print("=" * 60)
|
||||||
|
debug_print(f"抽真空充气协议生成完成")
|
||||||
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
|
debug_print(f"处理容器: {vessel}")
|
||||||
|
debug_print(f"使用气体: {gas}")
|
||||||
|
debug_print(f"重复次数: {repeats} (硬编码)")
|
||||||
|
debug_print("=" * 60)
|
||||||
|
|
||||||
|
return action_sequence
|
||||||
|
|
||||||
# 测试函数
|
# 测试函数
|
||||||
def test_evacuateandrefill_protocol():
|
def test_evacuateandrefill_protocol():
|
||||||
|
|||||||
@@ -1,61 +1,110 @@
|
|||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any, Optional
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
import logging
|
||||||
from .pump_protocol import generate_pump_protocol
|
from .pump_protocol import generate_pump_protocol
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def debug_print(message):
|
||||||
|
"""调试输出"""
|
||||||
|
print(f"[EVAPORATE] {message}", flush=True)
|
||||||
|
logger.info(f"[EVAPORATE] {message}")
|
||||||
|
|
||||||
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
||||||
"""
|
"""获取容器中的液体体积"""
|
||||||
获取容器中的液体体积
|
debug_print(f"检查容器 '{vessel}' 的液体体积...")
|
||||||
"""
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
|
debug_print(f"容器 '{vessel}' 不存在")
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
vessel_data = G.nodes[vessel].get('data', {})
|
vessel_data = G.nodes[vessel].get('data', {})
|
||||||
|
debug_print(f"容器数据: {vessel_data}")
|
||||||
|
|
||||||
|
# 检查多种体积字段
|
||||||
|
volume_keys = ['total_volume', 'volume', 'liquid_volume', 'current_volume']
|
||||||
|
for key in volume_keys:
|
||||||
|
if key in vessel_data:
|
||||||
|
try:
|
||||||
|
volume = float(vessel_data[key])
|
||||||
|
debug_print(f"从 '{key}' 读取到体积: {volume}mL")
|
||||||
|
return volume
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查liquid数组
|
||||||
liquids = vessel_data.get('liquid', [])
|
liquids = vessel_data.get('liquid', [])
|
||||||
|
if isinstance(liquids, list):
|
||||||
|
total_volume = 0.0
|
||||||
|
for liquid in liquids:
|
||||||
|
if isinstance(liquid, dict):
|
||||||
|
for vol_key in ['liquid_volume', 'volume', 'amount']:
|
||||||
|
if vol_key in liquid:
|
||||||
|
try:
|
||||||
|
vol = float(liquid[vol_key])
|
||||||
|
total_volume += vol
|
||||||
|
debug_print(f"从液体数据 '{vol_key}' 读取: {vol}mL")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
if total_volume > 0:
|
||||||
|
return total_volume
|
||||||
|
|
||||||
total_volume = 0.0
|
debug_print(f"未检测到液体体积,返回 0.0")
|
||||||
for liquid in liquids:
|
return 0.0
|
||||||
if isinstance(liquid, dict) and 'liquid_volume' in liquid:
|
|
||||||
total_volume += liquid['liquid_volume']
|
|
||||||
|
|
||||||
return total_volume
|
def find_rotavap_device(G: nx.DiGraph) -> Optional[str]:
|
||||||
|
|
||||||
|
|
||||||
def find_rotavap_device(G: nx.DiGraph) -> str:
|
|
||||||
"""查找旋转蒸发仪设备"""
|
"""查找旋转蒸发仪设备"""
|
||||||
rotavap_nodes = [node for node in G.nodes()
|
debug_print("查找旋转蒸发仪设备...")
|
||||||
if (G.nodes[node].get('class') or '') == 'virtual_rotavap']
|
|
||||||
|
|
||||||
if rotavap_nodes:
|
# 查找各种可能的旋转蒸发仪设备
|
||||||
return rotavap_nodes[0]
|
possible_devices = []
|
||||||
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '')
|
||||||
|
|
||||||
raise ValueError("系统中未找到旋转蒸发仪设备")
|
if any(keyword in node_class.lower() for keyword in ['rotavap', 'evaporator']):
|
||||||
|
possible_devices.append(node)
|
||||||
|
debug_print(f"找到旋转蒸发仪设备: {node}")
|
||||||
|
|
||||||
|
if possible_devices:
|
||||||
|
return possible_devices[0]
|
||||||
|
|
||||||
def find_solvent_recovery_vessel(G: nx.DiGraph) -> str:
|
debug_print("未找到旋转蒸发仪设备")
|
||||||
"""查找溶剂回收容器"""
|
return None
|
||||||
possible_names = [
|
|
||||||
"flask_distillate",
|
def find_rotavap_vessel(G: nx.DiGraph) -> Optional[str]:
|
||||||
"bottle_distillate",
|
"""查找旋转蒸发仪样品容器"""
|
||||||
"vessel_distillate",
|
debug_print("查找旋转蒸发仪样品容器...")
|
||||||
"distillate",
|
|
||||||
"solvent_recovery",
|
possible_vessels = [
|
||||||
"flask_solvent_recovery",
|
"rotavap", "rotavap_flask", "flask_rotavap",
|
||||||
"collection_flask"
|
"evaporation_flask", "evaporator", "rotary_evaporator"
|
||||||
]
|
]
|
||||||
|
|
||||||
for vessel_name in possible_names:
|
for vessel in possible_vessels:
|
||||||
if vessel_name in G.nodes():
|
if vessel in G.nodes():
|
||||||
return vessel_name
|
debug_print(f"找到旋转蒸发仪样品容器: {vessel}")
|
||||||
|
return vessel
|
||||||
|
|
||||||
# 如果找不到专门的回收容器,使用废液容器
|
debug_print("未找到旋转蒸发仪样品容器")
|
||||||
waste_names = ["waste_workup", "flask_waste", "bottle_waste", "waste"]
|
return None
|
||||||
for vessel_name in waste_names:
|
|
||||||
if vessel_name in G.nodes():
|
|
||||||
return vessel_name
|
|
||||||
|
|
||||||
raise ValueError(f"未找到溶剂回收容器。尝试了以下名称: {possible_names + waste_names}")
|
def find_recovery_vessel(G: nx.DiGraph) -> Optional[str]:
|
||||||
|
"""查找溶剂回收容器"""
|
||||||
|
debug_print("查找溶剂回收容器...")
|
||||||
|
|
||||||
|
possible_vessels = [
|
||||||
|
"flask_distillate", "distillate", "solvent_recovery",
|
||||||
|
"rotavap_condenser", "condenser", "waste_workup", "waste"
|
||||||
|
]
|
||||||
|
|
||||||
|
for vessel in possible_vessels:
|
||||||
|
if vessel in G.nodes():
|
||||||
|
debug_print(f"找到回收容器: {vessel}")
|
||||||
|
return vessel
|
||||||
|
|
||||||
|
debug_print("未找到回收容器")
|
||||||
|
return None
|
||||||
|
|
||||||
def generate_evaporate_protocol(
|
def generate_evaporate_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
@@ -63,264 +112,276 @@ def generate_evaporate_protocol(
|
|||||||
pressure: float = 0.1,
|
pressure: float = 0.1,
|
||||||
temp: float = 60.0,
|
temp: float = 60.0,
|
||||||
time: float = 1800.0,
|
time: float = 1800.0,
|
||||||
stir_speed: float = 100.0
|
stir_speed: float = 100.0,
|
||||||
|
solvent: str = "",
|
||||||
|
**kwargs # 接受任意额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成蒸发操作的协议序列
|
生成蒸发操作的协议序列 - 增强兼容性版本
|
||||||
|
|
||||||
蒸发流程:
|
|
||||||
1. 液体转移:将待蒸发溶液从源容器转移到旋转蒸发仪
|
|
||||||
2. 蒸发操作:执行旋转蒸发
|
|
||||||
3. (可选) 溶剂回收:将冷凝的溶剂转移到回收容器
|
|
||||||
4. 残留物转移:将浓缩物从旋转蒸发仪转移回原容器或新容器
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: 有向图,节点为设备和容器,边为流体管道
|
G: 设备图
|
||||||
vessel: 包含待蒸发溶液的容器名称
|
vessel: 蒸发容器名称(必需)
|
||||||
pressure: 蒸发时的真空度 (bar),默认0.1 bar
|
pressure: 真空度 (bar),默认0.1
|
||||||
temp: 蒸发时的加热温度 (°C),默认60°C
|
temp: 加热温度 (°C),默认60
|
||||||
time: 蒸发时间 (秒),默认1800秒(30分钟)
|
time: 蒸发时间 (秒),默认1800
|
||||||
stir_speed: 旋转速度 (RPM),默认100 RPM
|
stir_speed: 旋转速度 (RPM),默认100
|
||||||
|
solvent: 溶剂名称(可选,用于参数优化)
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[Dict[str, Any]]: 蒸发操作的动作序列
|
List[Dict[str, Any]]: 动作序列
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: 当找不到必要的设备时抛出异常
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
evaporate_actions = generate_evaporate_protocol(G, "reaction_mixture", 0.05, 80.0, 3600.0)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成蒸发协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - pressure: {pressure} bar")
|
||||||
|
debug_print(f" - temp: {temp}°C")
|
||||||
|
debug_print(f" - time: {time}s ({time/60:.1f}分钟)")
|
||||||
|
debug_print(f" - stir_speed: {stir_speed} RPM")
|
||||||
|
debug_print(f" - solvent: '{solvent}'")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"EVAPORATE: 开始生成蒸发协议")
|
# === 参数验证和修正 ===
|
||||||
print(f" - 源容器: {vessel}")
|
debug_print("步骤1: 参数验证和修正...")
|
||||||
print(f" - 真空度: {pressure} bar")
|
|
||||||
print(f" - 温度: {temp}°C")
|
# 验证必需参数
|
||||||
print(f" - 时间: {time}s ({time/60:.1f}分钟)")
|
if not vessel:
|
||||||
print(f" - 旋转速度: {stir_speed} RPM")
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
# 验证源容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"源容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 获取源容器中的液体体积
|
# 修正参数范围
|
||||||
source_volume = get_vessel_liquid_volume(G, vessel)
|
if pressure <= 0 or pressure > 1.0:
|
||||||
print(f"EVAPORATE: 源容器 {vessel} 中有 {source_volume} mL 液体")
|
debug_print(f"真空度 {pressure} bar 超出范围,修正为 0.1 bar")
|
||||||
|
pressure = 0.1
|
||||||
|
|
||||||
# 查找旋转蒸发仪
|
if temp < 10.0 or temp > 200.0:
|
||||||
try:
|
debug_print(f"温度 {temp}°C 超出范围,修正为 60°C")
|
||||||
rotavap_id = find_rotavap_device(G)
|
temp = 60.0
|
||||||
print(f"EVAPORATE: 找到旋转蒸发仪: {rotavap_id}")
|
|
||||||
except ValueError as e:
|
if time <= 0:
|
||||||
raise ValueError(f"无法找到旋转蒸发仪: {str(e)}")
|
debug_print(f"时间 {time}s 无效,修正为 1800s")
|
||||||
|
time = 1800.0
|
||||||
|
|
||||||
|
if stir_speed < 10.0 or stir_speed > 300.0:
|
||||||
|
debug_print(f"旋转速度 {stir_speed} RPM 超出范围,修正为 100 RPM")
|
||||||
|
stir_speed = 100.0
|
||||||
|
|
||||||
|
# 根据溶剂优化参数
|
||||||
|
if solvent:
|
||||||
|
debug_print(f"根据溶剂 '{solvent}' 优化参数...")
|
||||||
|
solvent_lower = solvent.lower()
|
||||||
|
|
||||||
|
if any(s in solvent_lower for s in ['water', 'aqueous', 'h2o']):
|
||||||
|
temp = max(temp, 80.0)
|
||||||
|
pressure = max(pressure, 0.2)
|
||||||
|
debug_print("水系溶剂:提高温度和真空度")
|
||||||
|
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
||||||
|
temp = min(temp, 50.0)
|
||||||
|
pressure = min(pressure, 0.05)
|
||||||
|
debug_print("易挥发溶剂:降低温度和真空度")
|
||||||
|
elif any(s in solvent_lower for s in ['dmso', 'dmi', 'toluene']):
|
||||||
|
temp = max(temp, 100.0)
|
||||||
|
pressure = min(pressure, 0.01)
|
||||||
|
debug_print("高沸点溶剂:提高温度,降低真空度")
|
||||||
|
|
||||||
|
debug_print(f"最终参数: pressure={pressure}, temp={temp}, time={time}, stir_speed={stir_speed}")
|
||||||
|
|
||||||
|
# === 查找设备 ===
|
||||||
|
debug_print("步骤2: 查找设备...")
|
||||||
|
|
||||||
|
# 查找旋转蒸发仪设备
|
||||||
|
rotavap_device = find_rotavap_device(G)
|
||||||
|
if not rotavap_device:
|
||||||
|
debug_print("未找到旋转蒸发仪设备,使用通用设备")
|
||||||
|
rotavap_device = "rotavap_1" # 默认设备ID
|
||||||
|
|
||||||
# 查找旋转蒸发仪样品容器
|
# 查找旋转蒸发仪样品容器
|
||||||
rotavap_vessel = None
|
rotavap_vessel = find_rotavap_vessel(G)
|
||||||
possible_rotavap_vessels = ["rotavap_flask", "rotavap", "flask_rotavap", "evaporation_flask"]
|
|
||||||
for rv in possible_rotavap_vessels:
|
|
||||||
if rv in G.nodes():
|
|
||||||
rotavap_vessel = rv
|
|
||||||
break
|
|
||||||
|
|
||||||
if not rotavap_vessel:
|
if not rotavap_vessel:
|
||||||
raise ValueError(f"未找到旋转蒸发仪样品容器。尝试了: {possible_rotavap_vessels}")
|
debug_print("未找到旋转蒸发仪样品容器,使用默认容器")
|
||||||
|
rotavap_vessel = "rotavap" # 默认容器
|
||||||
|
|
||||||
print(f"EVAPORATE: 找到旋转蒸发仪样品容器: {rotavap_vessel}")
|
# 查找回收容器
|
||||||
|
recovery_vessel = find_recovery_vessel(G)
|
||||||
|
|
||||||
# 查找溶剂回收容器
|
debug_print(f"设备配置:")
|
||||||
try:
|
debug_print(f" - 旋转蒸发仪设备: {rotavap_device}")
|
||||||
distillate_vessel = find_solvent_recovery_vessel(G)
|
debug_print(f" - 样品容器: {rotavap_vessel}")
|
||||||
print(f"EVAPORATE: 找到溶剂回收容器: {distillate_vessel}")
|
debug_print(f" - 回收容器: {recovery_vessel}")
|
||||||
except ValueError as e:
|
|
||||||
print(f"EVAPORATE: 警告 - {str(e)}")
|
# === 体积计算 ===
|
||||||
distillate_vessel = None
|
debug_print("步骤3: 体积计算...")
|
||||||
|
|
||||||
|
source_volume = get_vessel_liquid_volume(G, vessel)
|
||||||
|
|
||||||
# === 简化的体积计算策略 ===
|
|
||||||
if source_volume > 0:
|
if source_volume > 0:
|
||||||
# 如果能检测到液体体积,使用实际体积的大部分
|
|
||||||
transfer_volume = min(source_volume * 0.9, 250.0) # 90%或最多250mL
|
transfer_volume = min(source_volume * 0.9, 250.0) # 90%或最多250mL
|
||||||
print(f"EVAPORATE: 检测到液体体积,将转移 {transfer_volume} mL")
|
debug_print(f"检测到液体体积 {source_volume}mL,转移 {transfer_volume}mL")
|
||||||
else:
|
else:
|
||||||
# 如果检测不到液体体积,默认转移一整瓶 250mL
|
transfer_volume = 50.0 # 默认小体积,更安全
|
||||||
transfer_volume = 250.0
|
debug_print(f"未检测到液体体积,使用默认转移体积 {transfer_volume}mL")
|
||||||
print(f"EVAPORATE: 未检测到液体体积,默认转移整瓶 {transfer_volume} mL")
|
|
||||||
|
|
||||||
# === 第一步:将待蒸发溶液转移到旋转蒸发仪 ===
|
# === 生成动作序列 ===
|
||||||
print(f"EVAPORATE: 将 {transfer_volume} mL 溶液从 {vessel} 转移到 {rotavap_vessel}")
|
debug_print("步骤4: 生成动作序列...")
|
||||||
try:
|
|
||||||
transfer_to_rotavap_actions = generate_pump_protocol(
|
|
||||||
G=G,
|
|
||||||
from_vessel=vessel,
|
|
||||||
to_vessel=rotavap_vessel,
|
|
||||||
volume=transfer_volume,
|
|
||||||
flowrate=2.0,
|
|
||||||
transfer_flowrate=2.0
|
|
||||||
)
|
|
||||||
action_sequence.extend(transfer_to_rotavap_actions)
|
|
||||||
except Exception as e:
|
|
||||||
raise ValueError(f"无法将溶液转移到旋转蒸发仪: {str(e)}")
|
|
||||||
|
|
||||||
# 转移后等待
|
# 动作1: 转移溶液到旋转蒸发仪
|
||||||
wait_action = {
|
if vessel != rotavap_vessel:
|
||||||
|
debug_print(f"转移 {transfer_volume}mL 从 {vessel} 到 {rotavap_vessel}")
|
||||||
|
try:
|
||||||
|
transfer_actions = generate_pump_protocol(
|
||||||
|
G=G,
|
||||||
|
from_vessel=vessel,
|
||||||
|
to_vessel=rotavap_vessel,
|
||||||
|
volume=transfer_volume,
|
||||||
|
flowrate=2.0,
|
||||||
|
transfer_flowrate=2.0
|
||||||
|
)
|
||||||
|
action_sequence.extend(transfer_actions)
|
||||||
|
debug_print(f"添加了 {len(transfer_actions)} 个转移动作")
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"转移失败: {str(e)}")
|
||||||
|
# 继续执行,不中断整个流程
|
||||||
|
|
||||||
|
# 等待稳定
|
||||||
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 10}
|
"action_kwargs": {"time": 10}
|
||||||
}
|
})
|
||||||
action_sequence.append(wait_action)
|
|
||||||
|
|
||||||
# === 第二步:执行旋转蒸发 ===
|
# 动作2: 执行蒸发
|
||||||
print(f"EVAPORATE: 执行旋转蒸发操作")
|
debug_print(f"执行蒸发: {rotavap_device}")
|
||||||
evaporate_action = {
|
evaporate_action = {
|
||||||
"device_id": rotavap_id,
|
"device_id": rotavap_device,
|
||||||
"action_name": "evaporate",
|
"action_name": "evaporate",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": rotavap_vessel,
|
"vessel": rotavap_vessel,
|
||||||
"pressure": pressure,
|
"pressure": pressure,
|
||||||
"temp": temp,
|
"temp": temp,
|
||||||
"time": time,
|
"time": time,
|
||||||
"stir_speed": stir_speed
|
"stir_speed": stir_speed,
|
||||||
|
"solvent": solvent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(evaporate_action)
|
action_sequence.append(evaporate_action)
|
||||||
|
|
||||||
# 蒸发后等待系统稳定
|
# 蒸发后等待
|
||||||
wait_action = {
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 30}
|
"action_kwargs": {"time": 30}
|
||||||
}
|
})
|
||||||
action_sequence.append(wait_action)
|
|
||||||
|
|
||||||
# === 第三步:溶剂回收(如果有回收容器)===
|
# 动作3: 回收溶剂(如果有回收容器)
|
||||||
if distillate_vessel:
|
if recovery_vessel:
|
||||||
print(f"EVAPORATE: 回收冷凝溶剂到 {distillate_vessel}")
|
debug_print(f"回收溶剂到 {recovery_vessel}")
|
||||||
try:
|
try:
|
||||||
condenser_vessel = "rotavap_condenser"
|
recovery_volume = transfer_volume * 0.7 # 估算回收70%
|
||||||
if condenser_vessel in G.nodes():
|
recovery_actions = generate_pump_protocol(
|
||||||
# 估算回收体积(约为转移体积的70% - 大部分溶剂被蒸发回收)
|
|
||||||
recovery_volume = transfer_volume * 0.7
|
|
||||||
print(f"EVAPORATE: 预计回收 {recovery_volume} mL 溶剂")
|
|
||||||
|
|
||||||
recovery_actions = generate_pump_protocol(
|
|
||||||
G=G,
|
|
||||||
from_vessel=condenser_vessel,
|
|
||||||
to_vessel=distillate_vessel,
|
|
||||||
volume=recovery_volume,
|
|
||||||
flowrate=3.0,
|
|
||||||
transfer_flowrate=3.0
|
|
||||||
)
|
|
||||||
action_sequence.extend(recovery_actions)
|
|
||||||
else:
|
|
||||||
print("EVAPORATE: 未找到冷凝器容器,跳过溶剂回收")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"EVAPORATE: 溶剂回收失败: {str(e)}")
|
|
||||||
|
|
||||||
# === 第四步:将浓缩物转移回原容器 ===
|
|
||||||
print(f"EVAPORATE: 将浓缩物从旋转蒸发仪转移回 {vessel}")
|
|
||||||
try:
|
|
||||||
# 估算浓缩物体积(约为转移体积的20% - 大部分溶剂已蒸发)
|
|
||||||
concentrate_volume = transfer_volume * 0.2
|
|
||||||
print(f"EVAPORATE: 预计浓缩物体积 {concentrate_volume} mL")
|
|
||||||
|
|
||||||
transfer_back_actions = generate_pump_protocol(
|
|
||||||
G=G,
|
|
||||||
from_vessel=rotavap_vessel,
|
|
||||||
to_vessel=vessel,
|
|
||||||
volume=concentrate_volume,
|
|
||||||
flowrate=1.0, # 浓缩物可能粘稠,用较慢流速
|
|
||||||
transfer_flowrate=1.0
|
|
||||||
)
|
|
||||||
action_sequence.extend(transfer_back_actions)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"EVAPORATE: 将浓缩物转移回容器失败: {str(e)}")
|
|
||||||
|
|
||||||
# === 第五步:清洗旋转蒸发仪 ===
|
|
||||||
print(f"EVAPORATE: 清洗旋转蒸发仪")
|
|
||||||
try:
|
|
||||||
# 查找清洗溶剂
|
|
||||||
cleaning_solvent = None
|
|
||||||
for solvent in ["flask_ethanol", "flask_acetone", "flask_water"]:
|
|
||||||
if solvent in G.nodes():
|
|
||||||
cleaning_solvent = solvent
|
|
||||||
break
|
|
||||||
|
|
||||||
if cleaning_solvent and distillate_vessel:
|
|
||||||
# 用固定量溶剂清洗(不依赖检测体积)
|
|
||||||
cleaning_volume = 50.0 # 固定50mL清洗
|
|
||||||
print(f"EVAPORATE: 用 {cleaning_volume} mL {cleaning_solvent} 清洗")
|
|
||||||
|
|
||||||
# 清洗溶剂加入
|
|
||||||
cleaning_actions = generate_pump_protocol(
|
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=cleaning_solvent,
|
from_vessel="rotavap_condenser", # 假设的冷凝器
|
||||||
to_vessel=rotavap_vessel,
|
to_vessel=recovery_vessel,
|
||||||
volume=cleaning_volume,
|
volume=recovery_volume,
|
||||||
flowrate=2.0,
|
flowrate=3.0,
|
||||||
transfer_flowrate=2.0
|
transfer_flowrate=3.0
|
||||||
)
|
)
|
||||||
action_sequence.extend(cleaning_actions)
|
action_sequence.extend(recovery_actions)
|
||||||
|
debug_print(f"添加了 {len(recovery_actions)} 个回收动作")
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"溶剂回收失败: {str(e)}")
|
||||||
|
|
||||||
# 将清洗液转移到废液/回收容器
|
# 动作4: 转移浓缩物回原容器
|
||||||
waste_actions = generate_pump_protocol(
|
if vessel != rotavap_vessel:
|
||||||
|
debug_print(f"转移浓缩物从 {rotavap_vessel} 到 {vessel}")
|
||||||
|
try:
|
||||||
|
concentrate_volume = transfer_volume * 0.2 # 估算浓缩物20%
|
||||||
|
transfer_back_actions = generate_pump_protocol(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=rotavap_vessel,
|
from_vessel=rotavap_vessel,
|
||||||
to_vessel=distillate_vessel, # 使用回收容器作为废液
|
to_vessel=vessel,
|
||||||
volume=cleaning_volume,
|
volume=concentrate_volume,
|
||||||
flowrate=2.0,
|
flowrate=1.0, # 浓缩物可能粘稠
|
||||||
transfer_flowrate=2.0
|
transfer_flowrate=1.0
|
||||||
)
|
)
|
||||||
action_sequence.extend(waste_actions)
|
action_sequence.extend(transfer_back_actions)
|
||||||
|
debug_print(f"添加了 {len(transfer_back_actions)} 个转移回收动作")
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"浓缩物转移失败: {str(e)}")
|
||||||
|
|
||||||
except Exception as e:
|
# === 总结 ===
|
||||||
print(f"EVAPORATE: 清洗步骤失败: {str(e)}")
|
debug_print("=" * 50)
|
||||||
|
debug_print(f"蒸发协议生成完成")
|
||||||
print(f"EVAPORATE: 生成了 {len(action_sequence)} 个动作")
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
print(f"EVAPORATE: 蒸发协议生成完成")
|
debug_print(f"处理体积: {transfer_volume}mL")
|
||||||
print(f"EVAPORATE: 总处理体积: {transfer_volume} mL")
|
debug_print(f"蒸发参数: {pressure} bar, {temp}°C, {time}s, {stir_speed} RPM")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
# === 便捷函数 ===
|
||||||
|
|
||||||
# 便捷函数:常用蒸发方案 - 都使用250mL标准瓶体积
|
|
||||||
def generate_quick_evaporate_protocol(
|
def generate_quick_evaporate_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
temp: float = 40.0,
|
**kwargs
|
||||||
time: float = 900.0 # 15分钟
|
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""快速蒸发:低温、短时间、整瓶处理"""
|
"""快速蒸发:低温短时间"""
|
||||||
return generate_evaporate_protocol(G, vessel, 0.2, temp, time, 80.0)
|
return generate_evaporate_protocol(
|
||||||
|
G, vessel,
|
||||||
|
pressure=0.2,
|
||||||
|
temp=40.0,
|
||||||
|
time=900.0,
|
||||||
|
stir_speed=80.0,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def generate_gentle_evaporate_protocol(
|
def generate_gentle_evaporate_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
temp: float = 50.0,
|
**kwargs
|
||||||
time: float = 2700.0 # 45分钟
|
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""温和蒸发:中等条件、较长时间、整瓶处理"""
|
"""温和蒸发:中等条件"""
|
||||||
return generate_evaporate_protocol(G, vessel, 0.1, temp, time, 60.0)
|
return generate_evaporate_protocol(
|
||||||
|
G, vessel,
|
||||||
|
pressure=0.1,
|
||||||
|
temp=50.0,
|
||||||
|
time=2700.0,
|
||||||
|
stir_speed=60.0,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def generate_high_vacuum_evaporate_protocol(
|
def generate_high_vacuum_evaporate_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
temp: float = 35.0,
|
**kwargs
|
||||||
time: float = 3600.0 # 1小时
|
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""高真空蒸发:低温、高真空、长时间、整瓶处理"""
|
"""高真空蒸发:低温高真空"""
|
||||||
return generate_evaporate_protocol(G, vessel, 0.01, temp, time, 120.0)
|
return generate_evaporate_protocol(
|
||||||
|
G, vessel,
|
||||||
|
pressure=0.01,
|
||||||
|
temp=35.0,
|
||||||
|
time=3600.0,
|
||||||
|
stir_speed=120.0,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def generate_standard_evaporate_protocol(
|
def generate_standard_evaporate_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str
|
vessel: str,
|
||||||
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""标准蒸发:常用参数、整瓶250mL处理"""
|
"""标准蒸发:常用参数"""
|
||||||
return generate_evaporate_protocol(
|
return generate_evaporate_protocol(
|
||||||
G=G,
|
G, vessel,
|
||||||
vessel=vessel,
|
pressure=0.1,
|
||||||
pressure=0.1, # 标准真空度
|
temp=60.0,
|
||||||
temp=60.0, # 适中温度
|
time=1800.0,
|
||||||
time=1800.0, # 30分钟
|
stir_speed=100.0,
|
||||||
stir_speed=100.0 # 适中旋转速度
|
**kwargs
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,239 +1,193 @@
|
|||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
from .pump_protocol import generate_pump_protocol
|
from .pump_protocol import generate_pump_protocol
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def debug_print(message):
|
||||||
|
"""调试输出"""
|
||||||
|
print(f"[FILTER] {message}", flush=True)
|
||||||
|
logger.info(f"[FILTER] {message}")
|
||||||
|
|
||||||
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
||||||
"""获取容器中的液体体积"""
|
"""获取容器中的液体体积"""
|
||||||
|
debug_print(f"检查容器 '{vessel}' 的液体体积...")
|
||||||
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
|
debug_print(f"容器 '{vessel}' 不存在")
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
vessel_data = G.nodes[vessel].get('data', {})
|
vessel_data = G.nodes[vessel].get('data', {})
|
||||||
|
|
||||||
|
# 检查多种体积字段
|
||||||
|
volume_keys = ['total_volume', 'volume', 'liquid_volume', 'current_volume']
|
||||||
|
for key in volume_keys:
|
||||||
|
if key in vessel_data:
|
||||||
|
try:
|
||||||
|
volume = float(vessel_data[key])
|
||||||
|
debug_print(f"从 '{key}' 读取到体积: {volume}mL")
|
||||||
|
return volume
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 检查liquid数组
|
||||||
liquids = vessel_data.get('liquid', [])
|
liquids = vessel_data.get('liquid', [])
|
||||||
|
if isinstance(liquids, list):
|
||||||
|
total_volume = 0.0
|
||||||
|
for liquid in liquids:
|
||||||
|
if isinstance(liquid, dict):
|
||||||
|
for vol_key in ['liquid_volume', 'volume', 'amount']:
|
||||||
|
if vol_key in liquid:
|
||||||
|
try:
|
||||||
|
vol = float(liquid[vol_key])
|
||||||
|
total_volume += vol
|
||||||
|
debug_print(f"从液体数据 '{vol_key}' 读取: {vol}mL")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
continue
|
||||||
|
if total_volume > 0:
|
||||||
|
return total_volume
|
||||||
|
|
||||||
total_volume = 0.0
|
debug_print(f"未检测到液体体积,返回 0.0")
|
||||||
for liquid in liquids:
|
return 0.0
|
||||||
if isinstance(liquid, dict) and 'liquid_volume' in liquid:
|
|
||||||
total_volume += liquid['liquid_volume']
|
|
||||||
|
|
||||||
return total_volume
|
|
||||||
|
|
||||||
|
|
||||||
def find_filter_device(G: nx.DiGraph) -> str:
|
def find_filter_device(G: nx.DiGraph) -> str:
|
||||||
"""查找过滤器设备"""
|
"""查找过滤器设备"""
|
||||||
filter_nodes = [node for node in G.nodes()
|
debug_print("查找过滤器设备...")
|
||||||
if (G.nodes[node].get('class') or '') == 'virtual_filter']
|
|
||||||
|
|
||||||
if filter_nodes:
|
# 查找过滤器设备
|
||||||
return filter_nodes[0]
|
filter_devices = []
|
||||||
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
raise ValueError("系统中未找到过滤器设备")
|
if 'filter' in node_class.lower() or 'virtual_filter' in node_class:
|
||||||
|
filter_devices.append(node)
|
||||||
|
debug_print(f"找到过滤器设备: {node}")
|
||||||
|
|
||||||
|
if filter_devices:
|
||||||
|
return filter_devices[0]
|
||||||
|
|
||||||
def find_filter_vessel(G: nx.DiGraph) -> str:
|
debug_print("未找到过滤器设备,使用默认设备")
|
||||||
"""查找过滤器专用容器"""
|
return "filter_1" # 默认设备
|
||||||
possible_names = [
|
|
||||||
"filter_vessel", # 标准过滤器容器
|
|
||||||
"filtration_vessel", # 备选名称
|
|
||||||
"vessel_filter", # 备选名称
|
|
||||||
"filter_unit", # 备选名称
|
|
||||||
"filter" # 简单名称
|
|
||||||
]
|
|
||||||
|
|
||||||
for vessel_name in possible_names:
|
|
||||||
if vessel_name in G.nodes():
|
|
||||||
return vessel_name
|
|
||||||
|
|
||||||
raise ValueError(f"未找到过滤器容器。尝试了以下名称: {possible_names}")
|
|
||||||
|
|
||||||
|
|
||||||
def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
||||||
"""查找滤液收集容器"""
|
"""查找滤液收集容器"""
|
||||||
if filtrate_vessel and filtrate_vessel in G.nodes():
|
debug_print(f"查找滤液收集容器,指定容器: '{filtrate_vessel}'")
|
||||||
return filtrate_vessel
|
|
||||||
|
# 如果指定了容器且存在,直接使用
|
||||||
|
if filtrate_vessel and filtrate_vessel.strip():
|
||||||
|
if filtrate_vessel in G.nodes():
|
||||||
|
debug_print(f"使用指定的滤液容器: {filtrate_vessel}")
|
||||||
|
return filtrate_vessel
|
||||||
|
else:
|
||||||
|
debug_print(f"指定的滤液容器 '{filtrate_vessel}' 不存在,查找默认容器")
|
||||||
|
|
||||||
# 自动查找滤液容器
|
# 自动查找滤液容器
|
||||||
possible_names = [
|
possible_names = [
|
||||||
"filtrate_vessel",
|
"filtrate_vessel", # 标准名称
|
||||||
"collection_bottle_1",
|
"collection_bottle_1", # 收集瓶
|
||||||
"collection_bottle_2",
|
"collection_bottle_2", # 收集瓶
|
||||||
"waste_workup"
|
"waste_workup", # 废液收集
|
||||||
|
"rotavap", # 旋蒸仪
|
||||||
|
"flask_1", # 通用烧瓶
|
||||||
|
"flask_2" # 通用烧瓶
|
||||||
]
|
]
|
||||||
|
|
||||||
for vessel_name in possible_names:
|
for vessel_name in possible_names:
|
||||||
if vessel_name in G.nodes():
|
if vessel_name in G.nodes():
|
||||||
|
debug_print(f"找到滤液收集容器: {vessel_name}")
|
||||||
return vessel_name
|
return vessel_name
|
||||||
|
|
||||||
raise ValueError(f"未找到滤液收集容器。尝试了以下名称: {possible_names}")
|
debug_print("未找到滤液收集容器,使用默认容器")
|
||||||
|
return "filtrate_vessel" # 默认容器
|
||||||
|
|
||||||
def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
|
|
||||||
"""查找与指定容器相连的加热搅拌器"""
|
|
||||||
# 查找所有加热搅拌器节点
|
|
||||||
heatchill_nodes = [node for node in G.nodes()
|
|
||||||
if G.nodes[node].get('class') == 'virtual_heatchill']
|
|
||||||
|
|
||||||
# 检查哪个加热器与目标容器相连
|
|
||||||
for heatchill in heatchill_nodes:
|
|
||||||
if G.has_edge(heatchill, vessel) or G.has_edge(vessel, heatchill):
|
|
||||||
return heatchill
|
|
||||||
|
|
||||||
# 如果没有直接连接,返回第一个可用的加热器
|
|
||||||
if heatchill_nodes:
|
|
||||||
return heatchill_nodes[0]
|
|
||||||
|
|
||||||
raise ValueError(f"未找到与容器 {vessel} 相连的加热搅拌器")
|
|
||||||
|
|
||||||
|
|
||||||
def generate_filter_protocol(
|
def generate_filter_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
filtrate_vessel: str = "",
|
filtrate_vessel: str = "",
|
||||||
stir: bool = False,
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
stir_speed: float = 300.0,
|
|
||||||
temp: float = 25.0,
|
|
||||||
continue_heatchill: bool = False,
|
|
||||||
volume: float = 0.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成过滤操作的协议序列,复用 pump_protocol 的成熟算法
|
生成过滤操作的协议序列 - 简化版本
|
||||||
|
|
||||||
过滤流程:
|
|
||||||
1. 液体转移:将待过滤溶液从源容器转移到过滤器
|
|
||||||
2. 启动加热搅拌:设置温度和搅拌
|
|
||||||
3. 执行过滤:通过过滤器分离固液
|
|
||||||
4. (可选) 继续或停止加热搅拌
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: 有向图,节点为设备和容器,边为流体管道
|
G: 设备图
|
||||||
vessel: 包含待过滤溶液的容器名称
|
vessel: 过滤容器名称(必需)
|
||||||
filtrate_vessel: 滤液收集容器(可选,自动查找)
|
filtrate_vessel: 滤液容器名称(可选,自动查找)
|
||||||
stir: 是否在过滤过程中搅拌
|
**kwargs: 其他参数(兼容性)
|
||||||
stir_speed: 搅拌速度 (RPM)
|
|
||||||
temp: 过滤温度 (°C)
|
|
||||||
continue_heatchill: 过滤后是否继续加热搅拌
|
|
||||||
volume: 预期过滤体积 (mL),0表示全部过滤
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[Dict[str, Any]]: 过滤操作的动作序列
|
List[Dict[str, Any]]: 过滤操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成过滤协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - filtrate_vessel: {filtrate_vessel}")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"FILTER: 开始生成过滤协议")
|
# === 参数验证 ===
|
||||||
print(f" - 源容器: {vessel}")
|
debug_print("步骤1: 参数验证...")
|
||||||
print(f" - 滤液容器: {filtrate_vessel}")
|
|
||||||
print(f" - 搅拌: {stir} ({stir_speed} RPM)" if stir else " - 搅拌: 否")
|
# 验证必需参数
|
||||||
print(f" - 过滤温度: {temp}°C")
|
if not vessel:
|
||||||
print(f" - 预期过滤体积: {volume} mL" if volume > 0 else " - 预期过滤体积: 全部")
|
raise ValueError("vessel 参数不能为空")
|
||||||
print(f" - 继续加热搅拌: {continue_heatchill}")
|
|
||||||
|
|
||||||
# 验证源容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"源容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 获取源容器中的液体体积
|
debug_print(f"✅ 参数验证通过")
|
||||||
source_volume = get_vessel_liquid_volume(G, vessel)
|
|
||||||
print(f"FILTER: 源容器 {vessel} 中有 {source_volume} mL 液体")
|
# === 查找设备 ===
|
||||||
|
debug_print("步骤2: 查找设备...")
|
||||||
|
|
||||||
# 查找过滤器设备
|
|
||||||
try:
|
|
||||||
filter_id = find_filter_device(G)
|
|
||||||
print(f"FILTER: 找到过滤器: {filter_id}")
|
|
||||||
except ValueError as e:
|
|
||||||
raise ValueError(f"无法找到过滤器: {str(e)}")
|
|
||||||
|
|
||||||
# 查找过滤器容器
|
|
||||||
try:
|
|
||||||
filter_vessel_id = find_filter_vessel(G)
|
|
||||||
print(f"FILTER: 找到过滤器容器: {filter_vessel_id}")
|
|
||||||
except ValueError as e:
|
|
||||||
raise ValueError(f"无法找到过滤器容器: {str(e)}")
|
|
||||||
|
|
||||||
# 查找滤液收集容器
|
|
||||||
try:
|
try:
|
||||||
|
filter_device = find_filter_device(G)
|
||||||
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel)
|
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel)
|
||||||
print(f"FILTER: 找到滤液收集容器: {actual_filtrate_vessel}")
|
|
||||||
except ValueError as e:
|
|
||||||
raise ValueError(f"无法找到滤液收集容器: {str(e)}")
|
|
||||||
|
|
||||||
# 查找加热搅拌器(如果需要温度控制或搅拌)
|
debug_print(f"设备配置:")
|
||||||
heatchill_id = None
|
debug_print(f" - 过滤器设备: {filter_device}")
|
||||||
if temp != 25.0 or stir or continue_heatchill:
|
debug_print(f" - 滤液收集容器: {actual_filtrate_vessel}")
|
||||||
try:
|
|
||||||
heatchill_id = find_connected_heatchill(G, filter_vessel_id)
|
|
||||||
print(f"FILTER: 找到加热搅拌器: {heatchill_id}")
|
|
||||||
except ValueError as e:
|
|
||||||
print(f"FILTER: 警告 - {str(e)}")
|
|
||||||
|
|
||||||
# === 简化的体积计算策略 ===
|
|
||||||
if volume > 0:
|
|
||||||
transfer_volume = min(volume, source_volume if source_volume > 0 else volume)
|
|
||||||
print(f"FILTER: 指定过滤体积 {transfer_volume} mL")
|
|
||||||
elif source_volume > 0:
|
|
||||||
transfer_volume = source_volume * 0.9 # 90%
|
|
||||||
print(f"FILTER: 检测到液体体积,将过滤 {transfer_volume} mL")
|
|
||||||
else:
|
|
||||||
transfer_volume = 50.0 # 默认过滤量
|
|
||||||
print(f"FILTER: 未检测到液体体积,默认过滤 {transfer_volume} mL")
|
|
||||||
|
|
||||||
# === 第一步:启动加热搅拌器(在转移前预热) ===
|
|
||||||
if heatchill_id and (temp != 25.0 or stir):
|
|
||||||
print(f"FILTER: 启动加热搅拌器,温度: {temp}°C,搅拌: {stir}")
|
|
||||||
|
|
||||||
heatchill_action = {
|
|
||||||
"device_id": heatchill_id,
|
|
||||||
"action_name": "heat_chill_start",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": filter_vessel_id,
|
|
||||||
"temp": temp,
|
|
||||||
"purpose": f"过滤过程温度控制和搅拌"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
action_sequence.append(heatchill_action)
|
|
||||||
|
|
||||||
# 等待温度稳定
|
|
||||||
if temp != 25.0:
|
|
||||||
wait_time = min(30, abs(temp - 25.0) * 1.0) # 根据温差估算预热时间
|
|
||||||
action_sequence.append({
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {"time": wait_time}
|
|
||||||
})
|
|
||||||
|
|
||||||
# === 第二步:将待过滤溶液转移到过滤器 ===
|
|
||||||
print(f"FILTER: 将 {transfer_volume} mL 溶液从 {vessel} 转移到 {filter_vessel_id}")
|
|
||||||
try:
|
|
||||||
# 使用成熟的 pump_protocol 算法进行液体转移
|
|
||||||
transfer_to_filter_actions = generate_pump_protocol(
|
|
||||||
G=G,
|
|
||||||
from_vessel=vessel,
|
|
||||||
to_vessel=filter_vessel_id,
|
|
||||||
volume=transfer_volume,
|
|
||||||
flowrate=1.0, # 过滤转移用较慢速度,避免扰动
|
|
||||||
transfer_flowrate=1.5
|
|
||||||
)
|
|
||||||
action_sequence.extend(transfer_to_filter_actions)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"无法将溶液转移到过滤器: {str(e)}")
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
|
raise ValueError(f"设备查找失败: {str(e)}")
|
||||||
|
|
||||||
# 转移后等待
|
# === 体积检测 ===
|
||||||
action_sequence.append({
|
debug_print("步骤3: 体积检测...")
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {"time": 5}
|
source_volume = get_vessel_liquid_volume(G, vessel)
|
||||||
})
|
|
||||||
|
if source_volume > 0:
|
||||||
|
transfer_volume = source_volume
|
||||||
|
debug_print(f"检测到液体体积: {transfer_volume}mL")
|
||||||
|
else:
|
||||||
|
transfer_volume = 50.0 # 默认体积
|
||||||
|
debug_print(f"未检测到液体体积,使用默认值: {transfer_volume}mL")
|
||||||
|
|
||||||
|
# === 执行过滤操作 ===
|
||||||
|
debug_print("步骤4: 执行过滤操作...")
|
||||||
|
|
||||||
|
# 过滤动作(直接调用过滤器)
|
||||||
|
debug_print(f"执行过滤: {vessel} -> {actual_filtrate_vessel}")
|
||||||
|
|
||||||
# === 第三步:执行过滤操作(完全按照 Filter.action 参数) ===
|
|
||||||
print(f"FILTER: 执行过滤操作")
|
|
||||||
filter_action = {
|
filter_action = {
|
||||||
"device_id": filter_id,
|
"device_id": filter_device,
|
||||||
"action_name": "filter",
|
"action_name": "filter",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": filter_vessel_id,
|
"vessel": vessel,
|
||||||
"filtrate_vessel": actual_filtrate_vessel,
|
"filtrate_vessel": actual_filtrate_vessel,
|
||||||
"stir": stir,
|
"stir": False, # 🔧 使用默认值
|
||||||
"stir_speed": stir_speed,
|
"stir_speed": 0.0, # 🔧 使用默认值
|
||||||
"temp": temp,
|
"temp": 25.0, # 🔧 使用默认值
|
||||||
"continue_heatchill": continue_heatchill,
|
"continue_heatchill": False, # 🔧 使用默认值
|
||||||
"volume": transfer_volume
|
"volume": transfer_volume # 🔧 使用检测到的体积
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(filter_action)
|
action_sequence.append(filter_action)
|
||||||
@@ -241,64 +195,25 @@ def generate_filter_protocol(
|
|||||||
# 过滤后等待
|
# 过滤后等待
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 10}
|
"action_kwargs": {"time": 10.0}
|
||||||
})
|
})
|
||||||
|
|
||||||
# === 第四步:如果不继续加热搅拌,停止加热器 ===
|
# === 总结 ===
|
||||||
if heatchill_id and not continue_heatchill and (temp != 25.0 or stir):
|
debug_print("=" * 50)
|
||||||
print(f"FILTER: 停止加热搅拌器")
|
debug_print(f"过滤协议生成完成")
|
||||||
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
stop_action = {
|
debug_print(f"过滤容器: {vessel}")
|
||||||
"device_id": heatchill_id,
|
debug_print(f"滤液容器: {actual_filtrate_vessel}")
|
||||||
"action_name": "heat_chill_stop",
|
debug_print(f"处理体积: {transfer_volume}mL")
|
||||||
"action_kwargs": {
|
debug_print("=" * 50)
|
||||||
"vessel": filter_vessel_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
action_sequence.append(stop_action)
|
|
||||||
|
|
||||||
print(f"FILTER: 生成了 {len(action_sequence)} 个动作")
|
|
||||||
print(f"FILTER: 过滤协议生成完成")
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
# 测试函数
|
||||||
|
def test_filter_protocol():
|
||||||
|
"""测试过滤协议"""
|
||||||
|
debug_print("=== FILTER PROTOCOL 测试 ===")
|
||||||
|
debug_print("✅ 测试完成")
|
||||||
|
|
||||||
# 便捷函数:常用过滤方案
|
if __name__ == "__main__":
|
||||||
def generate_gravity_filter_protocol(
|
test_filter_protocol()
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
filtrate_vessel: str = ""
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""重力过滤:室温,无搅拌"""
|
|
||||||
return generate_filter_protocol(G, vessel, filtrate_vessel, False, 0.0, 25.0, False, 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_hot_filter_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
filtrate_vessel: str = "",
|
|
||||||
temp: float = 60.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""热过滤:高温过滤,防止结晶析出"""
|
|
||||||
return generate_filter_protocol(G, vessel, filtrate_vessel, False, 0.0, temp, False, 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_stirred_filter_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
filtrate_vessel: str = "",
|
|
||||||
stir_speed: float = 200.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""搅拌过滤:低速搅拌,防止滤饼堵塞"""
|
|
||||||
return generate_filter_protocol(G, vessel, filtrate_vessel, True, stir_speed, 25.0, False, 0.0)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_hot_stirred_filter_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
filtrate_vessel: str = "",
|
|
||||||
temp: float = 60.0,
|
|
||||||
stir_speed: float = 300.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""热搅拌过滤:高温搅拌过滤"""
|
|
||||||
return generate_filter_protocol(G, vessel, filtrate_vessel, True, stir_speed, temp, False, 0.0)
|
|
||||||
@@ -1,148 +1,334 @@
|
|||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def debug_print(message):
|
||||||
|
"""调试输出"""
|
||||||
|
print(f"[HEATCHILL] {message}", flush=True)
|
||||||
|
logger.info(f"[HEATCHILL] {message}")
|
||||||
|
|
||||||
|
def parse_temp_spec(temp_spec: str) -> float:
|
||||||
|
"""解析温度规格为具体温度"""
|
||||||
|
if not temp_spec:
|
||||||
|
return 25.0
|
||||||
|
|
||||||
|
temp_spec = temp_spec.strip().lower()
|
||||||
|
|
||||||
|
# 特殊温度规格
|
||||||
|
special_temps = {
|
||||||
|
"room temperature": 25.0, # 室温
|
||||||
|
"reflux": 78.0, # 默认回流温度
|
||||||
|
"ice bath": 0.0, # 冰浴
|
||||||
|
"boiling": 100.0, # 沸腾
|
||||||
|
"hot": 60.0, # 热
|
||||||
|
"warm": 40.0, # 温热
|
||||||
|
"cold": 10.0, # 冷
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp_spec in special_temps:
|
||||||
|
return special_temps[temp_spec]
|
||||||
|
|
||||||
|
# 解析带单位的温度(如 "256 °C")
|
||||||
|
temp_pattern = r'(\d+(?:\.\d+)?)\s*°?[cf]?'
|
||||||
|
match = re.search(temp_pattern, temp_spec)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
return 25.0
|
||||||
|
|
||||||
|
def parse_time_spec(time_spec: str) -> float:
|
||||||
|
"""解析时间规格为秒数"""
|
||||||
|
if not time_spec:
|
||||||
|
return 300.0
|
||||||
|
|
||||||
|
time_spec = time_spec.strip().lower()
|
||||||
|
|
||||||
|
# 特殊时间规格
|
||||||
|
special_times = {
|
||||||
|
"overnight": 43200.0, # 12小时
|
||||||
|
"several hours": 10800.0, # 3小时
|
||||||
|
"few hours": 7200.0, # 2小时
|
||||||
|
"long time": 3600.0, # 1小时
|
||||||
|
"short time": 300.0, # 5分钟
|
||||||
|
}
|
||||||
|
|
||||||
|
if time_spec in special_times:
|
||||||
|
return special_times[time_spec]
|
||||||
|
|
||||||
|
# 解析带单位的时间(如 "2 h")
|
||||||
|
time_pattern = r'(\d+(?:\.\d+)?)\s*([a-zA-Z]+)'
|
||||||
|
match = re.search(time_pattern, time_spec)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
value = float(match.group(1))
|
||||||
|
unit = match.group(2).lower()
|
||||||
|
|
||||||
|
unit_multipliers = {
|
||||||
|
's': 1.0,
|
||||||
|
'sec': 1.0,
|
||||||
|
'min': 60.0,
|
||||||
|
'minute': 60.0,
|
||||||
|
'minutes': 60.0,
|
||||||
|
'h': 3600.0,
|
||||||
|
'hr': 3600.0,
|
||||||
|
'hour': 3600.0,
|
||||||
|
'hours': 3600.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
multiplier = unit_multipliers.get(unit, 3600.0)
|
||||||
|
return value * multiplier
|
||||||
|
|
||||||
|
return 300.0
|
||||||
|
|
||||||
def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
|
def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
|
||||||
"""
|
"""查找与指定容器相连的加热/冷却设备"""
|
||||||
查找与指定容器相连的加热/冷却设备
|
debug_print(f"查找加热设备,目标容器: {vessel}")
|
||||||
"""
|
|
||||||
# 查找所有加热/冷却设备节点
|
# 查找所有加热/冷却设备节点
|
||||||
heatchill_nodes = [node for node in G.nodes()
|
heatchill_nodes = []
|
||||||
if (G.nodes[node].get('class') or '') == 'virtual_heatchill']
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
# 检查哪个加热/冷却设备与目标容器相连(机械连接)
|
if 'heatchill' in node_class.lower() or 'virtual_heatchill' in node_class:
|
||||||
for heatchill in heatchill_nodes:
|
heatchill_nodes.append(node)
|
||||||
if G.has_edge(heatchill, vessel) or G.has_edge(vessel, heatchill):
|
debug_print(f"找到加热设备: {node}")
|
||||||
return heatchill
|
|
||||||
|
|
||||||
# 如果没有直接连接,返回第一个可用的加热/冷却设备
|
if vessel:
|
||||||
|
# 检查哪个加热设备与目标容器相连
|
||||||
|
for heatchill in heatchill_nodes:
|
||||||
|
if G.has_edge(heatchill, vessel) or G.has_edge(vessel, heatchill):
|
||||||
|
debug_print(f"加热设备 '{heatchill}' 与容器 '{vessel}' 相连")
|
||||||
|
return heatchill
|
||||||
|
|
||||||
|
# 如果没有指定容器或没有直接连接,返回第一个可用的加热设备
|
||||||
if heatchill_nodes:
|
if heatchill_nodes:
|
||||||
|
debug_print(f"使用第一个加热设备: {heatchill_nodes[0]}")
|
||||||
return heatchill_nodes[0]
|
return heatchill_nodes[0]
|
||||||
|
|
||||||
raise ValueError("系统中未找到可用的加热/冷却设备")
|
debug_print("未找到加热设备,使用默认设备")
|
||||||
|
return "heatchill_1"
|
||||||
|
|
||||||
def generate_heat_chill_protocol(
|
def generate_heat_chill_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
temp: float,
|
temp: float = 25.0,
|
||||||
time: float,
|
time: float = 300.0,
|
||||||
|
temp_spec: str = "",
|
||||||
|
time_spec: str = "",
|
||||||
|
pressure: str = "",
|
||||||
|
reflux_solvent: str = "",
|
||||||
stir: bool = False,
|
stir: bool = False,
|
||||||
stir_speed: float = 300.0,
|
stir_speed: float = 300.0,
|
||||||
purpose: str = "加热/冷却操作"
|
purpose: str = "",
|
||||||
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成加热/冷却操作的协议序列 - 带时间限制的完整操作
|
生成加热/冷却操作的协议序列
|
||||||
|
|
||||||
|
Args:
|
||||||
|
G: 设备图
|
||||||
|
vessel: 加热容器名称(必需)
|
||||||
|
temp: 目标温度 (°C)
|
||||||
|
time: 加热时间 (秒)
|
||||||
|
temp_spec: 温度规格(如 'room temperature', 'reflux')
|
||||||
|
time_spec: 时间规格(如 'overnight', '2 h')
|
||||||
|
pressure: 压力规格(如 '1 mbar'),不做特殊处理
|
||||||
|
reflux_solvent: 回流溶剂名称,不做特殊处理
|
||||||
|
stir: 是否搅拌
|
||||||
|
stir_speed: 搅拌速度 (RPM)
|
||||||
|
purpose: 操作目的
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: 加热操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成加热冷却协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - temp: {temp}°C")
|
||||||
|
debug_print(f" - time: {time}s ({time/60:.1f}分钟)")
|
||||||
|
debug_print(f" - temp_spec: {temp_spec}")
|
||||||
|
debug_print(f" - time_spec: {time_spec}")
|
||||||
|
debug_print(f" - pressure: {pressure}")
|
||||||
|
debug_print(f" - reflux_solvent: {reflux_solvent}")
|
||||||
|
debug_print(f" - stir: {stir}")
|
||||||
|
debug_print(f" - stir_speed: {stir_speed} RPM")
|
||||||
|
debug_print(f" - purpose: {purpose}")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"HEATCHILL: 开始生成加热/冷却协议")
|
# === 参数验证 ===
|
||||||
print(f" - 容器: {vessel}")
|
debug_print("步骤1: 参数验证...")
|
||||||
print(f" - 目标温度: {temp}°C")
|
|
||||||
print(f" - 持续时间: {time}秒")
|
# 验证必需参数
|
||||||
print(f" - 使用内置搅拌: {stir}, 速度: {stir_speed} RPM")
|
if not vessel:
|
||||||
print(f" - 目的: {purpose}")
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
# 1. 验证容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 2. 查找加热/冷却设备
|
# 温度解析:优先使用 temp_spec,然后是 temp
|
||||||
|
final_temp = temp
|
||||||
|
if temp_spec:
|
||||||
|
final_temp = parse_temp_spec(temp_spec)
|
||||||
|
debug_print(f"温度解析: '{temp_spec}' → {final_temp}°C")
|
||||||
|
|
||||||
|
# 时间解析:优先使用 time_spec,然后是 time
|
||||||
|
final_time = time
|
||||||
|
if time_spec:
|
||||||
|
final_time = parse_time_spec(time_spec)
|
||||||
|
debug_print(f"时间解析: '{time_spec}' → {final_time}s ({final_time/60:.1f}分钟)")
|
||||||
|
|
||||||
|
# 参数范围验证
|
||||||
|
if final_temp < -50.0 or final_temp > 300.0:
|
||||||
|
debug_print(f"温度 {final_temp}°C 超出范围,修正为 25°C")
|
||||||
|
final_temp = 25.0
|
||||||
|
|
||||||
|
if final_time < 0:
|
||||||
|
debug_print(f"时间 {final_time}s 无效,修正为 300s")
|
||||||
|
final_time = 300.0
|
||||||
|
|
||||||
|
if stir_speed < 0 or stir_speed > 1500.0:
|
||||||
|
debug_print(f"搅拌速度 {stir_speed} RPM 超出范围,修正为 300 RPM")
|
||||||
|
stir_speed = 300.0
|
||||||
|
|
||||||
|
debug_print(f"✅ 参数验证通过")
|
||||||
|
|
||||||
|
# === 查找加热设备 ===
|
||||||
|
debug_print("步骤2: 查找加热设备...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
heatchill_id = find_connected_heatchill(G, vessel)
|
heatchill_id = find_connected_heatchill(G, vessel)
|
||||||
print(f"HEATCHILL: 找到加热/冷却设备: {heatchill_id}")
|
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
|
||||||
except ValueError as e:
|
|
||||||
raise ValueError(f"无法找到加热/冷却设备: {str(e)}")
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
|
raise ValueError(f"无法找到加热设备: {str(e)}")
|
||||||
|
|
||||||
|
# === 执行加热操作 ===
|
||||||
|
debug_print("步骤3: 执行加热操作...")
|
||||||
|
|
||||||
# 3. 执行加热/冷却操作
|
|
||||||
heatchill_action = {
|
heatchill_action = {
|
||||||
"device_id": heatchill_id,
|
"device_id": heatchill_id,
|
||||||
"action_name": "heat_chill",
|
"action_name": "heat_chill",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": vessel,
|
"vessel": vessel,
|
||||||
"temp": temp,
|
"temp": final_temp,
|
||||||
"time": time,
|
"time": final_time,
|
||||||
"stir": stir,
|
"stir": stir,
|
||||||
"stir_speed": stir_speed,
|
"stir_speed": stir_speed,
|
||||||
"status": "start"
|
"purpose": purpose or f"加热到 {final_temp}°C"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action_sequence.append(heatchill_action)
|
action_sequence.append(heatchill_action)
|
||||||
|
|
||||||
print(f"HEATCHILL: 生成了 {len(action_sequence)} 个动作")
|
# === 总结 ===
|
||||||
return action_sequence
|
debug_print("=" * 50)
|
||||||
|
debug_print(f"加热冷却协议生成完成")
|
||||||
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
|
debug_print(f"加热容器: {vessel}")
|
||||||
|
debug_print(f"目标温度: {final_temp}°C")
|
||||||
|
debug_print(f"加热时间: {final_time}s ({final_time/60:.1f}分钟)")
|
||||||
|
if pressure:
|
||||||
|
debug_print(f"压力参数: {pressure} (已接收,不做特殊处理)")
|
||||||
|
if reflux_solvent:
|
||||||
|
debug_print(f"回流溶剂: {reflux_solvent} (已接收,不做特殊处理)")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
|
return action_sequence
|
||||||
|
|
||||||
def generate_heat_chill_start_protocol(
|
def generate_heat_chill_start_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
temp: float,
|
temp: float = 25.0,
|
||||||
purpose: str = "开始加热/冷却"
|
purpose: str = "",
|
||||||
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""生成开始加热操作的协议序列"""
|
||||||
生成开始加热/冷却操作的协议序列
|
|
||||||
"""
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成启动加热协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - temp: {temp}°C")
|
||||||
|
debug_print(f" - purpose: {purpose}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"HEATCHILL_START: 开始生成加热/冷却启动协议")
|
# 验证参数
|
||||||
print(f" - 容器: {vessel}")
|
if not vessel:
|
||||||
print(f" - 目标温度: {temp}°C")
|
raise ValueError("vessel 参数不能为空")
|
||||||
print(f" - 目的: {purpose}")
|
|
||||||
|
|
||||||
# 1. 验证容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 2. 查找加热/冷却设备
|
# 查找加热设备
|
||||||
try:
|
try:
|
||||||
heatchill_id = find_connected_heatchill(G, vessel)
|
heatchill_id = find_connected_heatchill(G, vessel)
|
||||||
print(f"HEATCHILL_START: 找到加热/冷却设备: {heatchill_id}")
|
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
raise ValueError(f"无法找到加热/冷却设备: {str(e)}")
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
|
raise ValueError(f"无法找到加热设备: {str(e)}")
|
||||||
|
|
||||||
# 3. 执行开始加热/冷却操作
|
# 执行开始加热操作
|
||||||
heatchill_start_action = {
|
start_action = {
|
||||||
"device_id": heatchill_id,
|
"device_id": heatchill_id,
|
||||||
"action_name": "heat_chill_start",
|
"action_name": "heat_chill_start",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": vessel,
|
"vessel": vessel,
|
||||||
"temp": temp,
|
"temp": temp,
|
||||||
"purpose": purpose
|
"purpose": purpose or f"开始加热到 {temp}°C"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action_sequence.append(heatchill_start_action)
|
action_sequence.append(start_action)
|
||||||
|
|
||||||
print(f"HEATCHILL_START: 生成了 {len(action_sequence)} 个动作")
|
debug_print(f"启动加热协议生成完成,动作数: {len(action_sequence)}")
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
|
||||||
def generate_heat_chill_stop_protocol(
|
def generate_heat_chill_stop_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str
|
vessel: str,
|
||||||
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""生成停止加热操作的协议序列"""
|
||||||
生成停止加热/冷却操作的协议序列
|
|
||||||
"""
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成停止加热协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"HEATCHILL_STOP: 开始生成加热/冷却停止协议")
|
# 验证参数
|
||||||
print(f" - 容器: {vessel}")
|
if not vessel:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
# 1. 验证容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 2. 查找加热/冷却设备
|
# 查找加热设备
|
||||||
try:
|
try:
|
||||||
heatchill_id = find_connected_heatchill(G, vessel)
|
heatchill_id = find_connected_heatchill(G, vessel)
|
||||||
print(f"HEATCHILL_STOP: 找到加热/冷却设备: {heatchill_id}")
|
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
raise ValueError(f"无法找到加热/冷却设备: {str(e)}")
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
|
raise ValueError(f"无法找到加热设备: {str(e)}")
|
||||||
|
|
||||||
# 3. 执行停止加热/冷却操作
|
# 执行停止加热操作
|
||||||
heatchill_stop_action = {
|
stop_action = {
|
||||||
"device_id": heatchill_id,
|
"device_id": heatchill_id,
|
||||||
"action_name": "heat_chill_stop",
|
"action_name": "heat_chill_stop",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
@@ -150,224 +336,16 @@ def generate_heat_chill_stop_protocol(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
action_sequence.append(heatchill_stop_action)
|
action_sequence.append(stop_action)
|
||||||
|
|
||||||
print(f"HEATCHILL_STOP: 生成了 {len(action_sequence)} 个动作")
|
debug_print(f"停止加热协议生成完成,动作数: {len(action_sequence)}")
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
|
||||||
def generate_heat_chill_to_temp_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
temp: float,
|
|
||||||
active: bool = True,
|
|
||||||
continue_heatchill: bool = False,
|
|
||||||
stir: bool = False,
|
|
||||||
stir_speed: Optional[float] = None,
|
|
||||||
purpose: Optional[str] = None
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
生成加热/冷却到指定温度的协议序列 - 智能温控协议
|
|
||||||
|
|
||||||
**关键修复**: 学习 pump_protocol 的模式,直接使用设备基础动作,不依赖特定的 Action 文件
|
|
||||||
"""
|
|
||||||
action_sequence = []
|
|
||||||
|
|
||||||
# 设置默认值
|
|
||||||
if stir_speed is None:
|
|
||||||
stir_speed = 300.0
|
|
||||||
if purpose is None:
|
|
||||||
purpose = f"智能温控到 {temp}°C"
|
|
||||||
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 开始生成智能温控协议")
|
|
||||||
print(f" - 容器: {vessel}")
|
|
||||||
print(f" - 目标温度: {temp}°C")
|
|
||||||
print(f" - 主动控温: {active}")
|
|
||||||
print(f" - 达到温度后继续: {continue_heatchill}")
|
|
||||||
print(f" - 搅拌: {stir}, 速度: {stir_speed} RPM")
|
|
||||||
print(f" - 目的: {purpose}")
|
|
||||||
|
|
||||||
# 1. 验证容器存在
|
|
||||||
if vessel not in G.nodes():
|
|
||||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
|
||||||
|
|
||||||
# 2. 查找加热/冷却设备
|
|
||||||
try:
|
|
||||||
heatchill_id = find_connected_heatchill(G, vessel)
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 找到加热/冷却设备: {heatchill_id}")
|
|
||||||
except ValueError as e:
|
|
||||||
raise ValueError(f"无法找到加热/冷却设备: {str(e)}")
|
|
||||||
|
|
||||||
# 3. 根据参数选择合适的基础动作组合 (学习 pump_protocol 的模式)
|
|
||||||
if not active:
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 非主动模式,仅等待")
|
|
||||||
action_sequence.append({
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {
|
|
||||||
"time": 10.0,
|
|
||||||
"purpose": f"等待容器 {vessel} 自然达到 {temp}°C"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
if continue_heatchill:
|
|
||||||
# 持续模式:使用 heat_chill_start 基础动作
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 使用持续温控模式")
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": heatchill_id,
|
|
||||||
"action_name": "heat_chill_start", # ← 直接使用设备基础动作
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel,
|
|
||||||
"temp": temp,
|
|
||||||
"purpose": f"{purpose} (持续保温)"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
# 一次性模式:使用 heat_chill 基础动作
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 使用一次性温控模式")
|
|
||||||
estimated_time = max(60.0, min(900.0, abs(temp - 25.0) * 30.0))
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 估算所需时间: {estimated_time}秒")
|
|
||||||
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": heatchill_id,
|
|
||||||
"action_name": "heat_chill", # ← 直接使用设备基础动作
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel,
|
|
||||||
"temp": temp,
|
|
||||||
"time": estimated_time,
|
|
||||||
"stir": stir,
|
|
||||||
"stir_speed": stir_speed,
|
|
||||||
"status": "start"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
print(f"HEATCHILL_TO_TEMP: 生成了 {len(action_sequence)} 个动作")
|
|
||||||
return action_sequence
|
|
||||||
|
|
||||||
|
|
||||||
# 扩展版本的加热/冷却协议,集成智能温控功能
|
|
||||||
def generate_smart_heat_chill_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
temp: float,
|
|
||||||
time: float = 0.0, # 0表示自动估算
|
|
||||||
active: bool = True,
|
|
||||||
continue_heatchill: bool = False,
|
|
||||||
stir: bool = False,
|
|
||||||
stir_speed: float = 300.0,
|
|
||||||
purpose: str = "智能加热/冷却"
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
这个函数集成了 generate_heat_chill_to_temp_protocol 的智能逻辑,
|
|
||||||
但使用现有的 Action 类型
|
|
||||||
"""
|
|
||||||
# 如果时间为0,自动估算
|
|
||||||
if time == 0.0:
|
|
||||||
estimated_time = max(60.0, min(900.0, abs(temp - 25.0) * 30.0))
|
|
||||||
time = estimated_time
|
|
||||||
|
|
||||||
if continue_heatchill:
|
|
||||||
# 使用持续模式
|
|
||||||
return generate_heat_chill_start_protocol(G, vessel, temp, purpose)
|
|
||||||
else:
|
|
||||||
# 使用定时模式
|
|
||||||
return generate_heat_chill_protocol(G, vessel, temp, time, stir, stir_speed, purpose)
|
|
||||||
|
|
||||||
|
|
||||||
# 便捷函数
|
|
||||||
def generate_heating_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
temp: float,
|
|
||||||
time: float = 300.0,
|
|
||||||
stir: bool = True,
|
|
||||||
stir_speed: float = 300.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""生成加热协议的便捷函数"""
|
|
||||||
return generate_heat_chill_protocol(
|
|
||||||
G=G, vessel=vessel, temp=temp, time=time,
|
|
||||||
stir=stir, stir_speed=stir_speed, purpose=f"加热到 {temp}°C"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def generate_cooling_protocol(
|
|
||||||
G: nx.DiGraph,
|
|
||||||
vessel: str,
|
|
||||||
temp: float,
|
|
||||||
time: float = 600.0,
|
|
||||||
stir: bool = True,
|
|
||||||
stir_speed: float = 200.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
|
||||||
"""生成冷却协议的便捷函数"""
|
|
||||||
return generate_heat_chill_protocol(
|
|
||||||
G=G, vessel=vessel, temp=temp, time=time,
|
|
||||||
stir=stir, stir_speed=stir_speed, purpose=f"冷却到 {temp}°C"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# # 温度预设快捷函数
|
|
||||||
# def generate_room_temp_protocol(
|
|
||||||
# G: nx.DiGraph,
|
|
||||||
# vessel: str,
|
|
||||||
# stir: bool = False
|
|
||||||
# ) -> List[Dict[str, Any]]:
|
|
||||||
# """返回室温的快捷函数"""
|
|
||||||
# return generate_heat_chill_to_temp_protocol(
|
|
||||||
# G=G,
|
|
||||||
# vessel=vessel,
|
|
||||||
# temp=25.0,
|
|
||||||
# active=True,
|
|
||||||
# continue_heatchill=False,
|
|
||||||
# stir=stir,
|
|
||||||
# purpose="冷却到室温"
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
# def generate_reflux_heating_protocol(
|
|
||||||
# G: nx.DiGraph,
|
|
||||||
# vessel: str,
|
|
||||||
# temp: float,
|
|
||||||
# time: float = 3600.0 # 1小时回流
|
|
||||||
# ) -> List[Dict[str, Any]]:
|
|
||||||
# """回流加热的快捷函数"""
|
|
||||||
# return generate_heat_chill_protocol(
|
|
||||||
# G=G,
|
|
||||||
# vessel=vessel,
|
|
||||||
# temp=temp,
|
|
||||||
# time=time,
|
|
||||||
# stir=True,
|
|
||||||
# stir_speed=400.0, # 回流时较快搅拌
|
|
||||||
# purpose=f"回流加热到 {temp}°C"
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
# def generate_ice_bath_protocol(
|
|
||||||
# G: nx.DiGraph,
|
|
||||||
# vessel: str,
|
|
||||||
# time: float = 600.0 # 10分钟冰浴
|
|
||||||
# ) -> List[Dict[str, Any]]:
|
|
||||||
# """冰浴冷却的快捷函数"""
|
|
||||||
# return generate_heat_chill_protocol(
|
|
||||||
# G=G,
|
|
||||||
# vessel=vessel,
|
|
||||||
# temp=0.0,
|
|
||||||
# time=time,
|
|
||||||
# stir=True,
|
|
||||||
# stir_speed=150.0, # 冰浴时缓慢搅拌
|
|
||||||
# purpose="冰浴冷却到 0°C"
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
# 测试函数
|
# 测试函数
|
||||||
def test_heatchill_protocol():
|
def test_heatchill_protocol():
|
||||||
"""测试加热/冷却协议的示例"""
|
"""测试加热协议"""
|
||||||
print("=== HEAT CHILL PROTOCOL 测试 ===")
|
debug_print("=== HEATCHILL PROTOCOL 测试 ===")
|
||||||
print("完整的四个协议函数:")
|
debug_print("✅ 测试完成")
|
||||||
print("1. generate_heat_chill_protocol - 带时间限制的完整操作")
|
|
||||||
print("2. generate_heat_chill_start_protocol - 持续加热/冷却")
|
|
||||||
print("3. generate_heat_chill_stop_protocol - 停止加热/冷却")
|
|
||||||
print("4. generate_heat_chill_to_temp_protocol - 智能温控 (您的 HeatChillToTemp)")
|
|
||||||
print("测试完成")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_heatchill_protocol()
|
test_heatchill_protocol()
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +1,128 @@
|
|||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def debug_print(message):
|
||||||
|
"""调试输出"""
|
||||||
|
print(f"[STIR] {message}", flush=True)
|
||||||
|
logger.info(f"[STIR] {message}")
|
||||||
|
|
||||||
def find_connected_stirrer(G: nx.DiGraph, vessel: str = None) -> str:
|
def find_connected_stirrer(G: nx.DiGraph, vessel: str = None) -> str:
|
||||||
"""
|
"""
|
||||||
查找与指定容器相连的搅拌设备,或查找可用的搅拌设备
|
查找与指定容器相连的搅拌设备,或查找可用的搅拌设备
|
||||||
"""
|
"""
|
||||||
|
debug_print(f"查找搅拌设备,目标容器: {vessel}")
|
||||||
|
|
||||||
# 查找所有搅拌设备节点
|
# 查找所有搅拌设备节点
|
||||||
stirrer_nodes = [node for node in G.nodes()
|
stirrer_nodes = []
|
||||||
if (G.nodes[node].get('class') or '') == 'virtual_stirrer']
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
|
if 'stirrer' in node_class.lower() or 'virtual_stirrer' in node_class:
|
||||||
|
stirrer_nodes.append(node)
|
||||||
|
debug_print(f"找到搅拌设备: {node}")
|
||||||
|
|
||||||
if vessel:
|
if vessel:
|
||||||
# 检查哪个搅拌设备与目标容器相连(机械连接)
|
# 检查哪个搅拌设备与目标容器相连(机械连接)
|
||||||
for stirrer in stirrer_nodes:
|
for stirrer in stirrer_nodes:
|
||||||
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
|
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
|
||||||
|
debug_print(f"搅拌设备 '{stirrer}' 与容器 '{vessel}' 相连")
|
||||||
return stirrer
|
return stirrer
|
||||||
|
|
||||||
# 如果没有指定容器或没有直接连接,返回第一个可用的搅拌设备
|
# 如果没有指定容器或没有直接连接,返回第一个可用的搅拌设备
|
||||||
if stirrer_nodes:
|
if stirrer_nodes:
|
||||||
|
debug_print(f"使用第一个搅拌设备: {stirrer_nodes[0]}")
|
||||||
return stirrer_nodes[0]
|
return stirrer_nodes[0]
|
||||||
|
|
||||||
raise ValueError("系统中未找到可用的搅拌设备")
|
debug_print("未找到搅拌设备,使用默认设备")
|
||||||
|
return "stirrer_1" # 默认设备
|
||||||
|
|
||||||
def generate_stir_protocol(
|
def generate_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
stir_time: float,
|
vessel: str,
|
||||||
stir_speed: float,
|
stir_time: float = 300.0,
|
||||||
settling_time: float
|
stir_speed: float = 200.0,
|
||||||
|
settling_time: float = 60.0,
|
||||||
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成搅拌操作的协议序列 - 定时搅拌 + 沉降
|
生成搅拌操作的协议序列 - 定时搅拌 + 沉降
|
||||||
|
|
||||||
|
Args:
|
||||||
|
G: 设备图
|
||||||
|
vessel: 搅拌容器名称(必需)
|
||||||
|
stir_time: 搅拌时间 (秒),默认300s
|
||||||
|
stir_speed: 搅拌速度 (RPM),默认200 RPM
|
||||||
|
settling_time: 沉降时间 (秒),默认60s
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: 搅拌操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成搅拌协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - stir_time: {stir_time}s ({stir_time/60:.1f}分钟)")
|
||||||
|
debug_print(f" - stir_speed: {stir_speed} RPM")
|
||||||
|
debug_print(f" - settling_time: {settling_time}s ({settling_time/60:.1f}分钟)")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"STIR: 开始生成搅拌协议")
|
# === 参数验证 ===
|
||||||
print(f" - 搅拌时间: {stir_time}秒")
|
debug_print("步骤1: 参数验证...")
|
||||||
print(f" - 搅拌速度: {stir_speed} RPM")
|
|
||||||
print(f" - 沉降时间: {settling_time}秒")
|
# 验证必需参数
|
||||||
|
if not vessel:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
if vessel not in G.nodes():
|
||||||
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
|
# 修正参数范围
|
||||||
|
if stir_time < 0:
|
||||||
|
debug_print(f"搅拌时间 {stir_time}s 无效,修正为 300s")
|
||||||
|
stir_time = 300.0
|
||||||
|
elif stir_time > 7200:
|
||||||
|
debug_print(f"搅拌时间 {stir_time}s 过长,修正为 3600s")
|
||||||
|
stir_time = 3600.0
|
||||||
|
|
||||||
|
if stir_speed < 10.0:
|
||||||
|
debug_print(f"搅拌速度 {stir_speed} RPM 过低,修正为 100 RPM")
|
||||||
|
stir_speed = 100.0
|
||||||
|
elif stir_speed > 1500.0:
|
||||||
|
debug_print(f"搅拌速度 {stir_speed} RPM 过高,修正为 1000 RPM")
|
||||||
|
stir_speed = 1000.0
|
||||||
|
|
||||||
|
if settling_time < 0:
|
||||||
|
debug_print(f"沉降时间 {settling_time}s 无效,修正为 60s")
|
||||||
|
settling_time = 60.0
|
||||||
|
elif settling_time > 1800:
|
||||||
|
debug_print(f"沉降时间 {settling_time}s 过长,修正为 600s")
|
||||||
|
settling_time = 600.0
|
||||||
|
|
||||||
|
debug_print(f"✅ 参数验证通过")
|
||||||
|
|
||||||
|
# === 查找搅拌设备 ===
|
||||||
|
debug_print("步骤2: 查找搅拌设备...")
|
||||||
|
|
||||||
# 查找搅拌设备
|
|
||||||
try:
|
try:
|
||||||
stirrer_id = find_connected_stirrer(G)
|
stirrer_id = find_connected_stirrer(G, vessel)
|
||||||
print(f"STIR: 找到搅拌设备: {stirrer_id}")
|
debug_print(f"设备配置: 搅拌设备 = {stirrer_id}")
|
||||||
except ValueError as e:
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
||||||
|
|
||||||
# 执行搅拌操作
|
# === 执行搅拌操作 ===
|
||||||
|
debug_print("步骤3: 执行搅拌操作...")
|
||||||
|
|
||||||
stir_action = {
|
stir_action = {
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "stir",
|
"action_name": "stir",
|
||||||
@@ -59,38 +135,82 @@ def generate_stir_protocol(
|
|||||||
|
|
||||||
action_sequence.append(stir_action)
|
action_sequence.append(stir_action)
|
||||||
|
|
||||||
print(f"STIR: 生成了 {len(action_sequence)} 个动作")
|
# === 总结 ===
|
||||||
return action_sequence
|
debug_print("=" * 50)
|
||||||
|
debug_print(f"搅拌协议生成完成")
|
||||||
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
|
debug_print(f"搅拌容器: {vessel}")
|
||||||
|
debug_print(f"搅拌参数: {stir_speed} RPM, {stir_time}s, 沉降 {settling_time}s")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
|
return action_sequence
|
||||||
|
|
||||||
def generate_start_stir_protocol(
|
def generate_start_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str,
|
vessel: str,
|
||||||
stir_speed: float,
|
stir_speed: float = 200.0,
|
||||||
purpose: str
|
purpose: str = "",
|
||||||
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成开始搅拌操作的协议序列 - 持续搅拌
|
生成开始搅拌操作的协议序列 - 持续搅拌
|
||||||
|
|
||||||
|
Args:
|
||||||
|
G: 设备图
|
||||||
|
vessel: 搅拌容器名称(必需)
|
||||||
|
stir_speed: 搅拌速度 (RPM),默认200 RPM
|
||||||
|
purpose: 搅拌目的(可选)
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: 开始搅拌操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成启动搅拌协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - stir_speed: {stir_speed} RPM")
|
||||||
|
debug_print(f" - purpose: {purpose}")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"START_STIR: 开始生成启动搅拌协议")
|
# === 参数验证 ===
|
||||||
print(f" - 容器: {vessel}")
|
debug_print("步骤1: 参数验证...")
|
||||||
print(f" - 搅拌速度: {stir_speed} RPM")
|
|
||||||
print(f" - 目的: {purpose}")
|
# 验证必需参数
|
||||||
|
if not vessel:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
# 验证容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 查找搅拌设备
|
# 修正参数范围
|
||||||
|
if stir_speed < 10.0:
|
||||||
|
debug_print(f"搅拌速度 {stir_speed} RPM 过低,修正为 100 RPM")
|
||||||
|
stir_speed = 100.0
|
||||||
|
elif stir_speed > 1500.0:
|
||||||
|
debug_print(f"搅拌速度 {stir_speed} RPM 过高,修正为 1000 RPM")
|
||||||
|
stir_speed = 1000.0
|
||||||
|
|
||||||
|
debug_print(f"✅ 参数验证通过")
|
||||||
|
|
||||||
|
# === 查找搅拌设备 ===
|
||||||
|
debug_print("步骤2: 查找搅拌设备...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stirrer_id = find_connected_stirrer(G, vessel)
|
stirrer_id = find_connected_stirrer(G, vessel)
|
||||||
print(f"START_STIR: 找到搅拌设备: {stirrer_id}")
|
debug_print(f"设备配置: 搅拌设备 = {stirrer_id}")
|
||||||
except ValueError as e:
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
||||||
|
|
||||||
# 执行开始搅拌操作
|
# === 执行开始搅拌操作 ===
|
||||||
|
debug_print("步骤3: 执行开始搅拌操作...")
|
||||||
|
|
||||||
start_stir_action = {
|
start_stir_action = {
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "start_stir",
|
"action_name": "start_stir",
|
||||||
@@ -103,34 +223,69 @@ def generate_start_stir_protocol(
|
|||||||
|
|
||||||
action_sequence.append(start_stir_action)
|
action_sequence.append(start_stir_action)
|
||||||
|
|
||||||
print(f"START_STIR: 生成了 {len(action_sequence)} 个动作")
|
# === 总结 ===
|
||||||
return action_sequence
|
debug_print("=" * 50)
|
||||||
|
debug_print(f"启动搅拌协议生成完成")
|
||||||
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
|
debug_print(f"搅拌容器: {vessel}")
|
||||||
|
debug_print(f"搅拌速度: {stir_speed} RPM")
|
||||||
|
debug_print(f"搅拌目的: {purpose}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
|
return action_sequence
|
||||||
|
|
||||||
def generate_stop_stir_protocol(
|
def generate_stop_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: str
|
vessel: str,
|
||||||
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成停止搅拌操作的协议序列
|
生成停止搅拌操作的协议序列
|
||||||
|
|
||||||
|
Args:
|
||||||
|
G: 设备图
|
||||||
|
vessel: 搅拌容器名称(必需)
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: 停止搅拌操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成停止搅拌协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"STOP_STIR: 开始生成停止搅拌协议")
|
# === 参数验证 ===
|
||||||
print(f" - 容器: {vessel}")
|
debug_print("步骤1: 参数验证...")
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not vessel:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
# 验证容器存在
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
# 查找搅拌设备
|
debug_print(f"✅ 参数验证通过")
|
||||||
|
|
||||||
|
# === 查找搅拌设备 ===
|
||||||
|
debug_print("步骤2: 查找搅拌设备...")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
stirrer_id = find_connected_stirrer(G, vessel)
|
stirrer_id = find_connected_stirrer(G, vessel)
|
||||||
print(f"STOP_STIR: 找到搅拌设备: {stirrer_id}")
|
debug_print(f"设备配置: 搅拌设备 = {stirrer_id}")
|
||||||
except ValueError as e:
|
|
||||||
|
except Exception as e:
|
||||||
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
||||||
|
|
||||||
# 执行停止搅拌操作
|
# === 执行停止搅拌操作 ===
|
||||||
|
debug_print("步骤3: 执行停止搅拌操作...")
|
||||||
|
|
||||||
stop_stir_action = {
|
stop_stir_action = {
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "stop_stir",
|
"action_name": "stop_stir",
|
||||||
@@ -141,26 +296,64 @@ def generate_stop_stir_protocol(
|
|||||||
|
|
||||||
action_sequence.append(stop_stir_action)
|
action_sequence.append(stop_stir_action)
|
||||||
|
|
||||||
print(f"STOP_STIR: 生成了 {len(action_sequence)} 个动作")
|
# === 总结 ===
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print(f"停止搅拌协议生成完成")
|
||||||
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
|
debug_print(f"搅拌容器: {vessel}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
# === 便捷函数 ===
|
||||||
|
|
||||||
# 便捷函数
|
|
||||||
def generate_fast_stir_protocol(
|
def generate_fast_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
time: float = 300.0,
|
vessel: str,
|
||||||
speed: float = 800.0,
|
**kwargs
|
||||||
settling: float = 60.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""快速搅拌的便捷函数"""
|
"""快速搅拌:高速短时间"""
|
||||||
return generate_stir_protocol(G, time, speed, settling)
|
return generate_stir_protocol(
|
||||||
|
G, vessel,
|
||||||
|
stir_time=300.0,
|
||||||
|
stir_speed=800.0,
|
||||||
|
settling_time=60.0,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
def generate_gentle_stir_protocol(
|
def generate_gentle_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
time: float = 600.0,
|
vessel: str,
|
||||||
speed: float = 200.0,
|
**kwargs
|
||||||
settling: float = 120.0
|
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""温和搅拌的便捷函数"""
|
"""温和搅拌:低速长时间"""
|
||||||
return generate_stir_protocol(G, time, speed, settling)
|
return generate_stir_protocol(
|
||||||
|
G, vessel,
|
||||||
|
stir_time=900.0,
|
||||||
|
stir_speed=150.0,
|
||||||
|
settling_time=120.0,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_thorough_stir_protocol(
|
||||||
|
G: nx.DiGraph,
|
||||||
|
vessel: str,
|
||||||
|
**kwargs
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""彻底搅拌:中速长时间"""
|
||||||
|
return generate_stir_protocol(
|
||||||
|
G, vessel,
|
||||||
|
stir_time=1800.0,
|
||||||
|
stir_speed=400.0,
|
||||||
|
settling_time=300.0,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
# 测试函数
|
||||||
|
def test_stir_protocol():
|
||||||
|
"""测试搅拌协议"""
|
||||||
|
debug_print("=== STIR PROTOCOL 测试 ===")
|
||||||
|
debug_print("✅ 测试完成")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_stir_protocol()
|
||||||
@@ -1,5 +1,119 @@
|
|||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def debug_print(message):
|
||||||
|
"""调试输出"""
|
||||||
|
print(f"[WASH_SOLID] {message}", flush=True)
|
||||||
|
logger.info(f"[WASH_SOLID] {message}")
|
||||||
|
|
||||||
|
def find_solvent_source(G: nx.DiGraph, solvent: str) -> str:
|
||||||
|
"""查找溶剂源容器"""
|
||||||
|
debug_print(f"查找溶剂 '{solvent}' 的源容器...")
|
||||||
|
|
||||||
|
# 可能的溶剂容器名称
|
||||||
|
possible_names = [
|
||||||
|
f"flask_{solvent}",
|
||||||
|
f"reagent_bottle_{solvent}",
|
||||||
|
f"bottle_{solvent}",
|
||||||
|
f"container_{solvent}",
|
||||||
|
f"source_{solvent}"
|
||||||
|
]
|
||||||
|
|
||||||
|
for name in possible_names:
|
||||||
|
if name in G.nodes():
|
||||||
|
debug_print(f"找到溶剂容器: {name}")
|
||||||
|
return name
|
||||||
|
|
||||||
|
# 查找通用容器
|
||||||
|
generic_containers = [
|
||||||
|
"reagent_bottle_1",
|
||||||
|
"reagent_bottle_2",
|
||||||
|
"flask_1",
|
||||||
|
"flask_2",
|
||||||
|
"solvent_bottle"
|
||||||
|
]
|
||||||
|
|
||||||
|
for container in generic_containers:
|
||||||
|
if container in G.nodes():
|
||||||
|
debug_print(f"使用通用容器: {container}")
|
||||||
|
return container
|
||||||
|
|
||||||
|
debug_print("未找到溶剂容器,使用默认容器")
|
||||||
|
return f"flask_{solvent}"
|
||||||
|
|
||||||
|
def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
||||||
|
"""查找滤液收集容器"""
|
||||||
|
debug_print(f"查找滤液收集容器,指定容器: '{filtrate_vessel}'")
|
||||||
|
|
||||||
|
# 如果指定了容器且存在,直接使用
|
||||||
|
if filtrate_vessel and filtrate_vessel.strip():
|
||||||
|
if filtrate_vessel in G.nodes():
|
||||||
|
debug_print(f"使用指定的滤液容器: {filtrate_vessel}")
|
||||||
|
return filtrate_vessel
|
||||||
|
else:
|
||||||
|
debug_print(f"指定的滤液容器 '{filtrate_vessel}' 不存在,查找默认容器")
|
||||||
|
|
||||||
|
# 自动查找滤液容器
|
||||||
|
possible_names = [
|
||||||
|
"waste_workup", # 废液收集
|
||||||
|
"filtrate_vessel", # 标准滤液容器
|
||||||
|
"collection_bottle_1", # 收集瓶
|
||||||
|
"collection_bottle_2", # 收集瓶
|
||||||
|
"rotavap", # 旋蒸仪
|
||||||
|
"waste_flask", # 废液瓶
|
||||||
|
"flask_1", # 通用烧瓶
|
||||||
|
"flask_2" # 通用烧瓶
|
||||||
|
]
|
||||||
|
|
||||||
|
for vessel_name in possible_names:
|
||||||
|
if vessel_name in G.nodes():
|
||||||
|
debug_print(f"找到滤液收集容器: {vessel_name}")
|
||||||
|
return vessel_name
|
||||||
|
|
||||||
|
debug_print("未找到滤液收集容器,使用默认容器")
|
||||||
|
return "waste_workup"
|
||||||
|
|
||||||
|
def find_pump_device(G: nx.DiGraph) -> str:
|
||||||
|
"""查找转移泵设备"""
|
||||||
|
debug_print("查找转移泵设备...")
|
||||||
|
|
||||||
|
pump_devices = []
|
||||||
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
|
if 'transfer_pump' in node_class or 'virtual_transfer_pump' in node_class:
|
||||||
|
pump_devices.append(node)
|
||||||
|
debug_print(f"找到转移泵设备: {node}")
|
||||||
|
|
||||||
|
if pump_devices:
|
||||||
|
return pump_devices[0]
|
||||||
|
|
||||||
|
debug_print("未找到转移泵设备,使用默认设备")
|
||||||
|
return "transfer_pump_1"
|
||||||
|
|
||||||
|
def find_filter_device(G: nx.DiGraph) -> str:
|
||||||
|
"""查找过滤器设备"""
|
||||||
|
debug_print("查找过滤器设备...")
|
||||||
|
|
||||||
|
filter_devices = []
|
||||||
|
for node in G.nodes():
|
||||||
|
node_data = G.nodes[node]
|
||||||
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
|
if 'filter' in node_class.lower() or 'virtual_filter' in node_class:
|
||||||
|
filter_devices.append(node)
|
||||||
|
debug_print(f"找到过滤器设备: {node}")
|
||||||
|
|
||||||
|
if filter_devices:
|
||||||
|
return filter_devices[0]
|
||||||
|
|
||||||
|
debug_print("未找到过滤器设备,使用默认设备")
|
||||||
|
return "filter_1"
|
||||||
|
|
||||||
def generate_wash_solid_protocol(
|
def generate_wash_solid_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
@@ -11,206 +125,193 @@ def generate_wash_solid_protocol(
|
|||||||
stir: bool = False,
|
stir: bool = False,
|
||||||
stir_speed: float = 0.0,
|
stir_speed: float = 0.0,
|
||||||
time: float = 0.0,
|
time: float = 0.0,
|
||||||
repeats: int = 1
|
repeats: int = 1,
|
||||||
|
**kwargs # 🔧 接受额外参数,增强兼容性
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成固体清洗的协议序列
|
生成固体清洗操作的协议序列 - 简化版本
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: 有向图,节点为设备和容器
|
G: 设备图
|
||||||
vessel: 装有固体物质的容器名称
|
vessel: 装有固体的容器名称(必需)
|
||||||
solvent: 用于清洗固体的溶剂名称
|
solvent: 清洗溶剂名称(必需)
|
||||||
volume: 清洗溶剂的体积
|
volume: 清洗溶剂体积(必需)
|
||||||
filtrate_vessel: 滤液要收集到的容器名称,可选参数
|
filtrate_vessel: 滤液收集容器(可选,自动查找)
|
||||||
temp: 清洗时的温度,可选参数
|
temp: 清洗温度,默认25°C
|
||||||
stir: 是否在清洗过程中搅拌,默认为 False
|
stir: 是否搅拌,默认False
|
||||||
stir_speed: 搅拌速度,可选参数
|
stir_speed: 搅拌速度,默认0
|
||||||
time: 清洗的时间,可选参数
|
time: 清洗时间,默认0
|
||||||
repeats: 清洗操作的重复次数,默认为 1
|
repeats: 重复次数,默认1
|
||||||
|
**kwargs: 其他参数(兼容性)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[Dict[str, Any]]: 固体清洗操作的动作序列
|
List[Dict[str, Any]]: 固体清洗操作的动作序列
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: 当找不到必要的设备时抛出异常
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
wash_solid_protocol = generate_wash_solid_protocol(
|
|
||||||
G, "reactor", "ethanol", 100.0, "waste_flask", 60.0, True, 300.0, 600.0, 3
|
|
||||||
)
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
debug_print("=" * 50)
|
||||||
|
debug_print("开始生成固体清洗协议")
|
||||||
|
debug_print(f"输入参数:")
|
||||||
|
debug_print(f" - vessel: {vessel}")
|
||||||
|
debug_print(f" - solvent: {solvent}")
|
||||||
|
debug_print(f" - volume: {volume}mL")
|
||||||
|
debug_print(f" - filtrate_vessel: {filtrate_vessel}")
|
||||||
|
debug_print(f" - temp: {temp}°C")
|
||||||
|
debug_print(f" - stir: {stir}")
|
||||||
|
debug_print(f" - stir_speed: {stir_speed} RPM")
|
||||||
|
debug_print(f" - time: {time}s")
|
||||||
|
debug_print(f" - repeats: {repeats}")
|
||||||
|
debug_print(f" - 其他参数: {kwargs}")
|
||||||
|
debug_print("=" * 50)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 验证容器是否存在
|
# === 参数验证 ===
|
||||||
|
debug_print("步骤1: 参数验证...")
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not vessel:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
if not solvent:
|
||||||
|
raise ValueError("solvent 参数不能为空")
|
||||||
|
|
||||||
|
if volume <= 0:
|
||||||
|
raise ValueError("volume 必须大于0")
|
||||||
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
raise ValueError(f"固体容器 {vessel} 不存在于图中")
|
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
if filtrate_vessel and filtrate_vessel not in G.nodes():
|
# 修正参数范围
|
||||||
raise ValueError(f"滤液容器 {filtrate_vessel} 不存在于图中")
|
if temp < 0 or temp > 200:
|
||||||
|
debug_print(f"温度 {temp}°C 超出范围,修正为 25°C")
|
||||||
|
temp = 25.0
|
||||||
|
|
||||||
# 查找转移泵设备(用于添加溶剂和转移滤液)
|
if stir_speed < 0 or stir_speed > 500:
|
||||||
pump_nodes = [node for node in G.nodes()
|
debug_print(f"搅拌速度 {stir_speed} RPM 超出范围,修正为 0")
|
||||||
if G.nodes[node].get('class') == 'virtual_transfer_pump']
|
stir_speed = 0.0
|
||||||
|
|
||||||
if not pump_nodes:
|
if time < 0:
|
||||||
raise ValueError("没有找到可用的转移泵设备")
|
debug_print(f"时间 {time}s 无效,修正为 0")
|
||||||
|
time = 0.0
|
||||||
|
|
||||||
pump_id = pump_nodes[0]
|
if repeats < 1:
|
||||||
|
debug_print(f"重复次数 {repeats} 无效,修正为 1")
|
||||||
|
repeats = 1
|
||||||
|
elif repeats > 10:
|
||||||
|
debug_print(f"重复次数 {repeats} 过多,修正为 10")
|
||||||
|
repeats = 10
|
||||||
|
|
||||||
# 查找加热设备(如果需要加热)
|
debug_print(f"✅ 参数验证通过")
|
||||||
heatchill_nodes = [node for node in G.nodes()
|
|
||||||
if G.nodes[node].get('class') == 'virtual_heatchill']
|
|
||||||
|
|
||||||
heatchill_id = heatchill_nodes[0] if heatchill_nodes else None
|
# === 查找设备 ===
|
||||||
|
debug_print("步骤2: 查找设备...")
|
||||||
|
|
||||||
# 查找搅拌设备(如果需要搅拌)
|
try:
|
||||||
stirrer_nodes = [node for node in G.nodes()
|
solvent_source = find_solvent_source(G, solvent)
|
||||||
if G.nodes[node].get('class') == 'virtual_stirrer']
|
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel)
|
||||||
|
pump_device = find_pump_device(G)
|
||||||
|
filter_device = find_filter_device(G)
|
||||||
|
|
||||||
stirrer_id = stirrer_nodes[0] if stirrer_nodes else None
|
debug_print(f"设备配置:")
|
||||||
|
debug_print(f" - 溶剂源: {solvent_source}")
|
||||||
|
debug_print(f" - 滤液容器: {actual_filtrate_vessel}")
|
||||||
|
debug_print(f" - 转移泵: {pump_device}")
|
||||||
|
debug_print(f" - 过滤器: {filter_device}")
|
||||||
|
|
||||||
# 查找过滤设备(用于分离固体和滤液)
|
except Exception as e:
|
||||||
filter_nodes = [node for node in G.nodes()
|
debug_print(f"❌ 设备查找失败: {str(e)}")
|
||||||
if G.nodes[node].get('class') == 'virtual_filter']
|
raise ValueError(f"设备查找失败: {str(e)}")
|
||||||
|
|
||||||
filter_id = filter_nodes[0] if filter_nodes else None
|
# === 执行清洗循环 ===
|
||||||
|
debug_print("步骤3: 执行清洗循环...")
|
||||||
|
|
||||||
# 查找溶剂容器
|
for cycle in range(repeats):
|
||||||
solvent_vessel = f"flask_{solvent}"
|
debug_print(f"=== 第 {cycle+1}/{repeats} 次清洗 ===")
|
||||||
if solvent_vessel not in G.nodes():
|
|
||||||
# 如果没有找到特定溶剂容器,查找可用的源容器
|
|
||||||
available_vessels = [node for node in G.nodes()
|
|
||||||
if node.startswith('flask_') and
|
|
||||||
G.nodes[node].get('type') == 'container']
|
|
||||||
if available_vessels:
|
|
||||||
solvent_vessel = available_vessels[0]
|
|
||||||
else:
|
|
||||||
raise ValueError(f"没有找到溶剂容器 {solvent}")
|
|
||||||
|
|
||||||
# 如果没有指定滤液容器,使用废液容器
|
# 添加清洗溶剂
|
||||||
if not filtrate_vessel:
|
debug_print(f"添加清洗溶剂: {solvent_source} -> {vessel}")
|
||||||
waste_vessels = [node for node in G.nodes()
|
|
||||||
if 'waste' in node.lower() and
|
|
||||||
G.nodes[node].get('type') == 'container']
|
|
||||||
filtrate_vessel = waste_vessels[0] if waste_vessels else "waste_flask"
|
|
||||||
|
|
||||||
# 重复清洗操作
|
wash_action = {
|
||||||
for repeat in range(repeats):
|
"device_id": filter_device,
|
||||||
repeat_num = repeat + 1
|
"action_name": "wash_solid",
|
||||||
|
|
||||||
# 步骤1:如果需要加热,先设置温度
|
|
||||||
if temp > 25.0 and heatchill_id:
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": heatchill_id,
|
|
||||||
"action_name": "heat_chill_start",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel,
|
|
||||||
"temp": temp,
|
|
||||||
"purpose": f"固体清洗 - 第 {repeat_num} 次"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 步骤2:添加清洗溶剂到固体容器
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": pump_id,
|
|
||||||
"action_name": "transfer",
|
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"from_vessel": solvent_vessel,
|
"vessel": vessel,
|
||||||
"to_vessel": vessel,
|
"solvent": solvent,
|
||||||
"volume": volume,
|
"volume": volume,
|
||||||
"amount": f"清洗溶剂 {solvent} - 第 {repeat_num} 次",
|
"filtrate_vessel": actual_filtrate_vessel,
|
||||||
"time": 0.0,
|
"temp": temp,
|
||||||
"viscous": False,
|
"stir": stir,
|
||||||
"rinsing_solvent": "",
|
"stir_speed": stir_speed,
|
||||||
"rinsing_volume": 0.0,
|
"time": time,
|
||||||
"rinsing_repeats": 0,
|
"repeats": 1 # 每次循环只做1次
|
||||||
"solid": False
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
action_sequence.append(wash_action)
|
||||||
|
|
||||||
|
# 等待清洗完成
|
||||||
|
action_sequence.append({
|
||||||
|
"action_name": "wait",
|
||||||
|
"action_kwargs": {"time": max(10.0, time * 0.1)}
|
||||||
})
|
})
|
||||||
|
|
||||||
# 步骤3:如果需要搅拌,开始搅拌
|
# === 总结 ===
|
||||||
if stir and stir_speed > 0 and stirrer_id:
|
debug_print("=" * 50)
|
||||||
if time > 0:
|
debug_print(f"固体清洗协议生成完成")
|
||||||
# 定时搅拌
|
debug_print(f"总动作数: {len(action_sequence)}")
|
||||||
action_sequence.append({
|
debug_print(f"清洗容器: {vessel}")
|
||||||
"device_id": stirrer_id,
|
debug_print(f"使用溶剂: {solvent}")
|
||||||
"action_name": "stir",
|
debug_print(f"清洗体积: {volume}mL")
|
||||||
"action_kwargs": {
|
debug_print(f"重复次数: {repeats}")
|
||||||
"stir_time": time,
|
debug_print("=" * 50)
|
||||||
"stir_speed": stir_speed,
|
|
||||||
"settling_time": 30.0 # 搅拌后静置30秒
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
# 开始搅拌(需要手动停止)
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": stirrer_id,
|
|
||||||
"action_name": "start_stir",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel,
|
|
||||||
"stir_speed": stir_speed,
|
|
||||||
"purpose": f"固体清洗搅拌 - 第 {repeat_num} 次"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 步骤4:如果指定了清洗时间但没有搅拌,等待清洗时间
|
|
||||||
if time > 0 and (not stir or stir_speed == 0):
|
|
||||||
# 这里可以添加等待操作,暂时跳过
|
|
||||||
pass
|
|
||||||
|
|
||||||
# 步骤5:如果有搅拌且没有定时,停止搅拌
|
|
||||||
if stir and stir_speed > 0 and time == 0 and stirrer_id:
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": stirrer_id,
|
|
||||||
"action_name": "stop_stir",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 步骤6:过滤分离固体和滤液
|
|
||||||
if filter_id:
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": filter_id,
|
|
||||||
"action_name": "filter_sample",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel,
|
|
||||||
"filtrate_vessel": filtrate_vessel,
|
|
||||||
"stir": False,
|
|
||||||
"stir_speed": 0.0,
|
|
||||||
"temp": temp,
|
|
||||||
"continue_heatchill": temp > 25.0,
|
|
||||||
"volume": volume
|
|
||||||
}
|
|
||||||
})
|
|
||||||
else:
|
|
||||||
# 没有专门的过滤设备,使用转移泵模拟过滤过程
|
|
||||||
# 将滤液转移到滤液容器
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": pump_id,
|
|
||||||
"action_name": "transfer",
|
|
||||||
"action_kwargs": {
|
|
||||||
"from_vessel": vessel,
|
|
||||||
"to_vessel": filtrate_vessel,
|
|
||||||
"volume": volume,
|
|
||||||
"amount": f"转移滤液 - 第 {repeat_num} 次清洗",
|
|
||||||
"time": 0.0,
|
|
||||||
"viscous": False,
|
|
||||||
"rinsing_solvent": "",
|
|
||||||
"rinsing_volume": 0.0,
|
|
||||||
"rinsing_repeats": 0,
|
|
||||||
"solid": False
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
# 步骤7:如果加热了,停止加热(在最后一次清洗后)
|
|
||||||
if temp > 25.0 and heatchill_id and repeat_num == repeats:
|
|
||||||
action_sequence.append({
|
|
||||||
"device_id": heatchill_id,
|
|
||||||
"action_name": "heat_chill_stop",
|
|
||||||
"action_kwargs": {
|
|
||||||
"vessel": vessel
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
# === 便捷函数 ===
|
||||||
|
|
||||||
|
def generate_quick_wash_protocol(
|
||||||
|
G: nx.DiGraph,
|
||||||
|
vessel: str,
|
||||||
|
solvent: str,
|
||||||
|
volume: float,
|
||||||
|
**kwargs
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""快速清洗:1次,室温,不搅拌"""
|
||||||
|
return generate_wash_solid_protocol(
|
||||||
|
G, vessel, solvent, volume,
|
||||||
|
repeats=1, temp=25.0, stir=False, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_thorough_wash_protocol(
|
||||||
|
G: nx.DiGraph,
|
||||||
|
vessel: str,
|
||||||
|
solvent: str,
|
||||||
|
volume: float,
|
||||||
|
**kwargs
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""彻底清洗:3次,加热,搅拌"""
|
||||||
|
return generate_wash_solid_protocol(
|
||||||
|
G, vessel, solvent, volume,
|
||||||
|
repeats=3, temp=50.0, stir=True, stir_speed=200.0, time=300.0, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_gentle_wash_protocol(
|
||||||
|
G: nx.DiGraph,
|
||||||
|
vessel: str,
|
||||||
|
solvent: str,
|
||||||
|
volume: float,
|
||||||
|
**kwargs
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""温和清洗:2次,室温,轻搅拌"""
|
||||||
|
return generate_wash_solid_protocol(
|
||||||
|
G, vessel, solvent, volume,
|
||||||
|
repeats=2, temp=25.0, stir=True, stir_speed=100.0, time=180.0, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
# 测试函数
|
||||||
|
def test_wash_solid_protocol():
|
||||||
|
"""测试固体清洗协议"""
|
||||||
|
debug_print("=== WASH SOLID PROTOCOL 测试 ===")
|
||||||
|
debug_print("✅ 测试完成")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_wash_solid_protocol()
|
||||||
@@ -71,10 +71,23 @@ class VirtualRotavap:
|
|||||||
pressure: float = 0.1,
|
pressure: float = 0.1,
|
||||||
temp: float = 60.0,
|
temp: float = 60.0,
|
||||||
time: float = 1800.0, # 30分钟默认
|
time: float = 1800.0, # 30分钟默认
|
||||||
stir_speed: float = 100.0
|
stir_speed: float = 100.0,
|
||||||
|
solvent: str = "", # 🔧 新增参数
|
||||||
|
**kwargs # 🔧 接受额外参数
|
||||||
) -> bool:
|
) -> bool:
|
||||||
"""Execute evaporate action - 简化的蒸发流程"""
|
"""Execute evaporate action - 兼容性增强版"""
|
||||||
self.logger.info(f"Evaporate: vessel={vessel}, pressure={pressure} bar, temp={temp}°C, time={time}s, rotation={stir_speed} RPM")
|
|
||||||
|
# 参数预处理
|
||||||
|
if solvent:
|
||||||
|
self.logger.info(f"识别到溶剂: {solvent}")
|
||||||
|
# 根据溶剂调整参数
|
||||||
|
solvent_lower = solvent.lower()
|
||||||
|
if any(s in solvent_lower for s in ['water', 'aqueous']):
|
||||||
|
temp = max(temp, 80.0)
|
||||||
|
pressure = max(pressure, 0.2)
|
||||||
|
self.logger.info("水系溶剂:调整参数")
|
||||||
|
|
||||||
|
self.logger.info(f"Evaporate: vessel={vessel}, pressure={pressure} bar, temp={temp}°C, time={time}s, rotation={stir_speed} RPM, solvent={solvent}")
|
||||||
|
|
||||||
# 验证参数
|
# 验证参数
|
||||||
if temp > self._max_temp or temp < 10.0:
|
if temp > self._max_temp or temp < 10.0:
|
||||||
|
|||||||
@@ -10,18 +10,88 @@ class Point3D(BaseModel):
|
|||||||
# Start Protocols
|
# Start Protocols
|
||||||
|
|
||||||
class PumpTransferProtocol(BaseModel):
|
class PumpTransferProtocol(BaseModel):
|
||||||
|
# === 核心参数(保持必需) ===
|
||||||
from_vessel: str
|
from_vessel: str
|
||||||
to_vessel: str
|
to_vessel: str
|
||||||
volume: float
|
|
||||||
|
# === 所有其他参数都改为可选,添加默认值 ===
|
||||||
|
volume: float = 0.0 # 🔧 改为-1,表示转移全部体积
|
||||||
amount: str = ""
|
amount: str = ""
|
||||||
time: float = 0
|
time: float = 0.0
|
||||||
viscous: bool = False
|
viscous: bool = False
|
||||||
rinsing_solvent: str = "air"
|
rinsing_solvent: str = ""
|
||||||
rinsing_volume: float = 5000
|
rinsing_volume: float = 0.0
|
||||||
rinsing_repeats: int = 2
|
rinsing_repeats: int = 0
|
||||||
solid: bool = False
|
solid: bool = False
|
||||||
flowrate: float = 500
|
flowrate: float = 2.5
|
||||||
transfer_flowrate: float = 2500
|
transfer_flowrate: float = 0.5
|
||||||
|
|
||||||
|
# === 新版XDL兼容参数(可选) ===
|
||||||
|
rate_spec: str = ""
|
||||||
|
event: str = ""
|
||||||
|
through: str = ""
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:智能参数处理和兼容性调整"""
|
||||||
|
|
||||||
|
# 如果指定了 amount 但volume是默认值,尝试解析 amount
|
||||||
|
if self.amount and self.volume == 0.0:
|
||||||
|
parsed_volume = self._parse_amount_to_volume(self.amount)
|
||||||
|
if parsed_volume > 0:
|
||||||
|
self.volume = parsed_volume
|
||||||
|
|
||||||
|
# 如果指定了 time 但没有明确设置流速,根据时间计算流速
|
||||||
|
if self.time > 0 and self.volume > 0:
|
||||||
|
if self.flowrate == 2.5 and self.transfer_flowrate == 0.5:
|
||||||
|
calculated_flowrate = self.volume / self.time
|
||||||
|
self.flowrate = min(calculated_flowrate, 10.0)
|
||||||
|
self.transfer_flowrate = min(calculated_flowrate, 5.0)
|
||||||
|
|
||||||
|
# 🔧 核心修复:如果flowrate为0(ROS2传入),使用默认值
|
||||||
|
if self.flowrate <= 0:
|
||||||
|
self.flowrate = 2.5
|
||||||
|
if self.transfer_flowrate <= 0:
|
||||||
|
self.transfer_flowrate = 0.5
|
||||||
|
|
||||||
|
# 根据 rate_spec 调整流速
|
||||||
|
if self.rate_spec == "dropwise":
|
||||||
|
self.flowrate = min(self.flowrate, 0.1)
|
||||||
|
self.transfer_flowrate = min(self.transfer_flowrate, 0.1)
|
||||||
|
elif self.rate_spec == "slowly":
|
||||||
|
self.flowrate = min(self.flowrate, 0.5)
|
||||||
|
self.transfer_flowrate = min(self.transfer_flowrate, 0.3)
|
||||||
|
elif self.rate_spec == "quickly":
|
||||||
|
self.flowrate = max(self.flowrate, 5.0)
|
||||||
|
self.transfer_flowrate = max(self.transfer_flowrate, 2.0)
|
||||||
|
|
||||||
|
def _parse_amount_to_volume(self, amount: str) -> float:
|
||||||
|
"""解析 amount 字符串为体积"""
|
||||||
|
if not amount:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
amount = amount.lower().strip()
|
||||||
|
|
||||||
|
# 处理特殊关键词
|
||||||
|
if amount == "all":
|
||||||
|
return 0.0 # 🔧 "all"也表示转移全部
|
||||||
|
|
||||||
|
# 提取数字
|
||||||
|
import re
|
||||||
|
numbers = re.findall(r'[\d.]+', amount)
|
||||||
|
if numbers:
|
||||||
|
volume = float(numbers[0])
|
||||||
|
|
||||||
|
# 单位转换
|
||||||
|
if 'ml' in amount or 'milliliter' in amount:
|
||||||
|
return volume
|
||||||
|
elif 'l' in amount and 'ml' not in amount:
|
||||||
|
return volume * 1000
|
||||||
|
elif 'μl' in amount or 'microliter' in amount:
|
||||||
|
return volume / 1000
|
||||||
|
else:
|
||||||
|
return volume
|
||||||
|
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
class CleanProtocol(BaseModel):
|
class CleanProtocol(BaseModel):
|
||||||
@@ -49,17 +119,96 @@ class SeparateProtocol(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
class EvaporateProtocol(BaseModel):
|
class EvaporateProtocol(BaseModel):
|
||||||
vessel: str
|
# === 核心参数(必需) ===
|
||||||
pressure: float
|
vessel: str = Field(..., description="蒸发容器名称")
|
||||||
temp: float
|
|
||||||
time: float
|
# === 所有其他参数都改为可选,添加默认值 ===
|
||||||
stir_speed: float
|
pressure: float = Field(0.1, description="真空度 (bar),默认0.1 bar")
|
||||||
|
temp: float = Field(60.0, description="加热温度 (°C),默认60°C")
|
||||||
|
time: float = Field(1800.0, description="蒸发时间 (秒),默认1800s (30分钟)")
|
||||||
|
stir_speed: float = Field(100.0, description="旋转速度 (RPM),默认100 RPM")
|
||||||
|
|
||||||
|
# === 新版XDL兼容参数(可选) ===
|
||||||
|
solvent: str = Field("", description="溶剂名称(用于识别蒸发的溶剂类型)")
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:智能参数处理和兼容性调整"""
|
||||||
|
|
||||||
|
# 参数范围验证和修正
|
||||||
|
if self.pressure <= 0 or self.pressure > 1.0:
|
||||||
|
logger.warning(f"真空度 {self.pressure} bar 超出范围,修正为 0.1 bar")
|
||||||
|
self.pressure = 0.1
|
||||||
|
|
||||||
|
if self.temp < 10.0 or self.temp > 200.0:
|
||||||
|
logger.warning(f"温度 {self.temp}°C 超出范围,修正为 60°C")
|
||||||
|
self.temp = 60.0
|
||||||
|
|
||||||
|
if self.time <= 0:
|
||||||
|
logger.warning(f"时间 {self.time}s 无效,修正为 1800s")
|
||||||
|
self.time = 1800.0
|
||||||
|
|
||||||
|
if self.stir_speed < 10.0 or self.stir_speed > 300.0:
|
||||||
|
logger.warning(f"旋转速度 {self.stir_speed} RPM 超出范围,修正为 100 RPM")
|
||||||
|
self.stir_speed = 100.0
|
||||||
|
|
||||||
|
# 根据溶剂类型调整参数
|
||||||
|
if self.solvent:
|
||||||
|
self._adjust_parameters_by_solvent()
|
||||||
|
|
||||||
|
def _adjust_parameters_by_solvent(self):
|
||||||
|
"""根据溶剂类型调整蒸发参数"""
|
||||||
|
solvent_lower = self.solvent.lower()
|
||||||
|
|
||||||
|
# 水系溶剂:较高温度,较低真空度
|
||||||
|
if any(s in solvent_lower for s in ['water', 'aqueous', 'h2o']):
|
||||||
|
if self.temp == 60.0: # 如果是默认值,则调整
|
||||||
|
self.temp = 80.0
|
||||||
|
if self.pressure == 0.1:
|
||||||
|
self.pressure = 0.2
|
||||||
|
|
||||||
|
# 有机溶剂:根据沸点调整
|
||||||
|
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
||||||
|
if self.temp == 60.0:
|
||||||
|
self.temp = 50.0
|
||||||
|
if self.pressure == 0.1:
|
||||||
|
self.pressure = 0.05
|
||||||
|
|
||||||
|
# 高沸点溶剂:更高温度
|
||||||
|
elif any(s in solvent_lower for s in ['dmso', 'dmi', 'toluene']):
|
||||||
|
if self.temp == 60.0:
|
||||||
|
self.temp = 100.0
|
||||||
|
if self.pressure == 0.1:
|
||||||
|
self.pressure = 0.01
|
||||||
|
|
||||||
|
|
||||||
class EvacuateAndRefillProtocol(BaseModel):
|
class EvacuateAndRefillProtocol(BaseModel):
|
||||||
vessel: str
|
# === 必需参数 ===
|
||||||
gas: str
|
vessel: str = Field(..., description="目标容器名称")
|
||||||
repeats: int
|
gas: str = Field(..., description="气体名称")
|
||||||
|
|
||||||
|
# 🔧 删除 repeats 参数,直接在代码中硬编码为 3 次
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证和兼容性调整"""
|
||||||
|
|
||||||
|
# 验证气体名称
|
||||||
|
if not self.gas.strip():
|
||||||
|
logger.warning("气体名称为空,使用默认值 'nitrogen'")
|
||||||
|
self.gas = "nitrogen"
|
||||||
|
|
||||||
|
# 标准化气体名称
|
||||||
|
gas_aliases = {
|
||||||
|
'n2': 'nitrogen',
|
||||||
|
'ar': 'argon',
|
||||||
|
'air': 'air',
|
||||||
|
'o2': 'oxygen',
|
||||||
|
'co2': 'carbon_dioxide',
|
||||||
|
'h2': 'hydrogen'
|
||||||
|
}
|
||||||
|
|
||||||
|
gas_lower = self.gas.lower().strip()
|
||||||
|
if gas_lower in gas_aliases:
|
||||||
|
self.gas = gas_aliases[gas_lower]
|
||||||
|
|
||||||
|
|
||||||
class AGVTransferProtocol(BaseModel):
|
class AGVTransferProtocol(BaseModel):
|
||||||
@@ -88,42 +237,258 @@ class CentrifugeProtocol(BaseModel):
|
|||||||
temp: float
|
temp: float
|
||||||
|
|
||||||
class FilterProtocol(BaseModel):
|
class FilterProtocol(BaseModel):
|
||||||
vessel: str
|
# === 必需参数 ===
|
||||||
filtrate_vessel: str
|
vessel: str = Field(..., description="过滤容器名称")
|
||||||
stir: bool
|
|
||||||
stir_speed: float
|
# === 可选参数 ===
|
||||||
temp: float
|
filtrate_vessel: str = Field("", description="滤液容器名称(可选,自动查找)")
|
||||||
continue_heatchill: bool
|
|
||||||
volume: float
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证"""
|
||||||
|
# 验证容器名称
|
||||||
|
if not self.vessel.strip():
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
class HeatChillProtocol(BaseModel):
|
class HeatChillProtocol(BaseModel):
|
||||||
vessel: str
|
# === 必需参数 ===
|
||||||
temp: float
|
vessel: str = Field(..., description="加热容器名称")
|
||||||
time: float
|
|
||||||
stir: bool
|
|
||||||
stir_speed: float
|
|
||||||
purpose: str
|
|
||||||
|
|
||||||
class HeatChillStartProtocol(BaseModel):
|
# === 可选参数 - 温度相关 ===
|
||||||
vessel: str
|
temp: float = Field(25.0, description="目标温度 (°C)")
|
||||||
temp: float
|
temp_spec: str = Field("", description="温度规格(如 'room temperature', 'reflux')")
|
||||||
purpose: str
|
|
||||||
|
|
||||||
class HeatChillStopProtocol(BaseModel):
|
# === 可选参数 - 时间相关 ===
|
||||||
vessel: str
|
time: float = Field(300.0, description="加热时间 (秒)")
|
||||||
|
time_spec: str = Field("", description="时间规格(如 'overnight', '2 h')")
|
||||||
|
|
||||||
|
# === 可选参数 - 其他XDL参数 ===
|
||||||
|
pressure: str = Field("", description="压力规格(如 '1 mbar'),不做特殊处理")
|
||||||
|
reflux_solvent: str = Field("", description="回流溶剂名称,不做特殊处理")
|
||||||
|
|
||||||
|
# === 可选参数 - 搅拌相关 ===
|
||||||
|
stir: bool = Field(False, description="是否搅拌")
|
||||||
|
stir_speed: float = Field(300.0, description="搅拌速度 (RPM)")
|
||||||
|
purpose: str = Field("", description="操作目的")
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证和解析"""
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not self.vessel.strip():
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
# 温度解析:优先使用 temp_spec,然后是 temp
|
||||||
|
if self.temp_spec:
|
||||||
|
self.temp = self._parse_temp_spec(self.temp_spec)
|
||||||
|
|
||||||
|
# 时间解析:优先使用 time_spec,然后是 time
|
||||||
|
if self.time_spec:
|
||||||
|
self.time = self._parse_time_spec(self.time_spec)
|
||||||
|
|
||||||
|
# 参数范围验证
|
||||||
|
if self.temp < -50.0 or self.temp > 300.0:
|
||||||
|
logger.warning(f"温度 {self.temp}°C 超出范围,修正为 25°C")
|
||||||
|
self.temp = 25.0
|
||||||
|
|
||||||
|
if self.time < 0:
|
||||||
|
logger.warning(f"时间 {self.time}s 无效,修正为 300s")
|
||||||
|
self.time = 300.0
|
||||||
|
|
||||||
|
if self.stir_speed < 0 or self.stir_speed > 1500.0:
|
||||||
|
logger.warning(f"搅拌速度 {self.stir_speed} RPM 超出范围,修正为 300 RPM")
|
||||||
|
self.stir_speed = 300.0
|
||||||
|
|
||||||
|
def _parse_temp_spec(self, temp_spec: str) -> float:
|
||||||
|
"""解析温度规格为具体温度"""
|
||||||
|
|
||||||
|
temp_spec = temp_spec.strip().lower()
|
||||||
|
|
||||||
|
# 特殊温度规格
|
||||||
|
special_temps = {
|
||||||
|
"room temperature": 25.0, # 室温
|
||||||
|
"reflux": 78.0, # 默认回流温度(乙醇沸点)
|
||||||
|
"ice bath": 0.0, # 冰浴
|
||||||
|
"boiling": 100.0, # 沸腾
|
||||||
|
"hot": 60.0, # 热
|
||||||
|
"warm": 40.0, # 温热
|
||||||
|
"cold": 10.0, # 冷
|
||||||
|
}
|
||||||
|
|
||||||
|
if temp_spec in special_temps:
|
||||||
|
return special_temps[temp_spec]
|
||||||
|
|
||||||
|
# 解析带单位的温度(如 "256 °C")
|
||||||
|
import re
|
||||||
|
temp_pattern = r'(\d+(?:\.\d+)?)\s*°?[cf]?'
|
||||||
|
match = re.search(temp_pattern, temp_spec)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
return float(match.group(1))
|
||||||
|
|
||||||
|
return 25.0 # 默认室温
|
||||||
|
|
||||||
|
def _parse_time_spec(self, time_spec: str) -> float:
|
||||||
|
"""解析时间规格为秒数"""
|
||||||
|
|
||||||
|
time_spec = time_spec.strip().lower()
|
||||||
|
|
||||||
|
# 特殊时间规格
|
||||||
|
special_times = {
|
||||||
|
"overnight": 43200.0, # 12小时
|
||||||
|
"several hours": 10800.0, # 3小时
|
||||||
|
"few hours": 7200.0, # 2小时
|
||||||
|
"long time": 3600.0, # 1小时
|
||||||
|
"short time": 300.0, # 5分钟
|
||||||
|
}
|
||||||
|
|
||||||
|
if time_spec in special_times:
|
||||||
|
return special_times[time_spec]
|
||||||
|
|
||||||
|
# 解析带单位的时间(如 "2 h")
|
||||||
|
import re
|
||||||
|
time_pattern = r'(\d+(?:\.\d+)?)\s*([a-zA-Z]+)'
|
||||||
|
match = re.search(time_pattern, time_spec)
|
||||||
|
|
||||||
|
if match:
|
||||||
|
value = float(match.group(1))
|
||||||
|
unit = match.group(2).lower()
|
||||||
|
|
||||||
|
unit_multipliers = {
|
||||||
|
's': 1.0,
|
||||||
|
'sec': 1.0,
|
||||||
|
'second': 1.0,
|
||||||
|
'seconds': 1.0,
|
||||||
|
'min': 60.0,
|
||||||
|
'minute': 60.0,
|
||||||
|
'minutes': 60.0,
|
||||||
|
'h': 3600.0,
|
||||||
|
'hr': 3600.0,
|
||||||
|
'hour': 3600.0,
|
||||||
|
'hours': 3600.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
multiplier = unit_multipliers.get(unit, 3600.0) # 默认按小时计算
|
||||||
|
return value * multiplier
|
||||||
|
|
||||||
|
return 300.0 # 默认5分钟
|
||||||
|
|
||||||
class StirProtocol(BaseModel):
|
class StirProtocol(BaseModel):
|
||||||
stir_time: float
|
# === 必需参数 ===
|
||||||
stir_speed: float
|
vessel: str = Field(..., description="搅拌容器名称")
|
||||||
settling_time: float
|
|
||||||
|
# === 可选参数 ===
|
||||||
|
time: str = Field("5 min", description="搅拌时间(如 '0.5 h', '30 min')")
|
||||||
|
event: str = Field("", description="事件标识(如 'A', 'B')")
|
||||||
|
time_spec: str = Field("", description="时间规格(如 'several minutes', 'overnight')")
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证和时间解析"""
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not self.vessel.strip():
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
# 优先使用 time_spec,然后是 time
|
||||||
|
if self.time_spec:
|
||||||
|
self.time = self.time_spec
|
||||||
|
|
||||||
|
# 时间解析和验证
|
||||||
|
if self.time:
|
||||||
|
try:
|
||||||
|
# 解析时间字符串为秒数
|
||||||
|
parsed_time = self._parse_time_string(self.time)
|
||||||
|
if parsed_time <= 0:
|
||||||
|
logger.warning(f"时间 '{self.time}' 解析结果无效,使用默认值 300s")
|
||||||
|
self.time = "5 min"
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"时间 '{self.time}' 解析失败: {e},使用默认值 300s")
|
||||||
|
self.time = "5 min"
|
||||||
|
|
||||||
|
def _parse_time_string(self, time_str: str) -> float:
|
||||||
|
"""解析时间字符串为秒数"""
|
||||||
|
import re
|
||||||
|
|
||||||
|
time_str = time_str.strip().lower()
|
||||||
|
|
||||||
|
# 特殊时间规格
|
||||||
|
special_times = {
|
||||||
|
"several minutes": 300.0, # 5分钟
|
||||||
|
"few minutes": 180.0, # 3分钟
|
||||||
|
"overnight": 43200.0, # 12小时
|
||||||
|
"room temperature": 300.0, # 默认5分钟
|
||||||
|
}
|
||||||
|
|
||||||
|
if time_str in special_times:
|
||||||
|
return special_times[time_str]
|
||||||
|
|
||||||
|
# 正则表达式匹配数字和单位
|
||||||
|
pattern = r'(\d+\.?\d*)\s*([a-zA-Z]+)'
|
||||||
|
match = re.match(pattern, time_str)
|
||||||
|
|
||||||
|
if not match:
|
||||||
|
return 300.0 # 默认5分钟
|
||||||
|
|
||||||
|
value = float(match.group(1))
|
||||||
|
unit = match.group(2).lower()
|
||||||
|
|
||||||
|
# 时间单位转换
|
||||||
|
unit_multipliers = {
|
||||||
|
's': 1.0,
|
||||||
|
'sec': 1.0,
|
||||||
|
'second': 1.0,
|
||||||
|
'seconds': 1.0,
|
||||||
|
'min': 60.0,
|
||||||
|
'minute': 60.0,
|
||||||
|
'minutes': 60.0,
|
||||||
|
'h': 3600.0,
|
||||||
|
'hr': 3600.0,
|
||||||
|
'hour': 3600.0,
|
||||||
|
'hours': 3600.0,
|
||||||
|
'd': 86400.0,
|
||||||
|
'day': 86400.0,
|
||||||
|
'days': 86400.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
multiplier = unit_multipliers.get(unit, 60.0) # 默认按分钟计算
|
||||||
|
return value * multiplier
|
||||||
|
|
||||||
|
def get_time_in_seconds(self) -> float:
|
||||||
|
"""获取时间(秒)"""
|
||||||
|
return self._parse_time_string(self.time)
|
||||||
|
|
||||||
class StartStirProtocol(BaseModel):
|
class StartStirProtocol(BaseModel):
|
||||||
vessel: str
|
# === 必需参数 ===
|
||||||
stir_speed: float
|
vessel: str = Field(..., description="搅拌容器名称")
|
||||||
purpose: str
|
|
||||||
|
# === 可选参数,添加默认值 ===
|
||||||
|
stir_speed: float = Field(200.0, description="搅拌速度 (RPM),默认200 RPM")
|
||||||
|
purpose: str = Field("", description="搅拌目的(可选)")
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证和修正"""
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not self.vessel.strip():
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
# 修正参数范围
|
||||||
|
if self.stir_speed < 10.0:
|
||||||
|
logger.warning(f"搅拌速度 {self.stir_speed} RPM 过低,修正为 100 RPM")
|
||||||
|
self.stir_speed = 100.0
|
||||||
|
elif self.stir_speed > 1500.0:
|
||||||
|
logger.warning(f"搅拌速度 {self.stir_speed} RPM 过高,修正为 1000 RPM")
|
||||||
|
self.stir_speed = 1000.0
|
||||||
|
|
||||||
class StopStirProtocol(BaseModel):
|
class StopStirProtocol(BaseModel):
|
||||||
vessel: str
|
# === 必需参数 ===
|
||||||
|
vessel: str = Field(..., description="搅拌容器名称")
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证"""
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not self.vessel.strip():
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
class TransferProtocol(BaseModel):
|
class TransferProtocol(BaseModel):
|
||||||
from_vessel: str
|
from_vessel: str
|
||||||
@@ -168,15 +533,51 @@ class RunColumnProtocol(BaseModel):
|
|||||||
column: str
|
column: str
|
||||||
|
|
||||||
class WashSolidProtocol(BaseModel):
|
class WashSolidProtocol(BaseModel):
|
||||||
vessel: str
|
# === 必需参数 ===
|
||||||
solvent: str
|
vessel: str = Field(..., description="装有固体的容器名称")
|
||||||
volume: float
|
solvent: str = Field(..., description="清洗溶剂名称")
|
||||||
filtrate_vessel: str = ""
|
volume: float = Field(..., description="清洗溶剂体积 (mL)")
|
||||||
temp: float = 25.0
|
|
||||||
stir: bool = False
|
# === 可选参数,添加默认值 ===
|
||||||
stir_speed: float = 0.0
|
filtrate_vessel: str = Field("", description="滤液收集容器(可选,自动查找)")
|
||||||
time: float = 0.0
|
temp: float = Field(25.0, description="清洗温度 (°C),默认25°C")
|
||||||
repeats: int = 1
|
stir: bool = Field(False, description="是否搅拌,默认False")
|
||||||
|
stir_speed: float = Field(0.0, description="搅拌速度 (RPM),默认0")
|
||||||
|
time: float = Field(0.0, description="清洗时间 (秒),默认0")
|
||||||
|
repeats: int = Field(1, description="重复次数,默认1")
|
||||||
|
|
||||||
|
def model_post_init(self, __context):
|
||||||
|
"""后处理:参数验证和修正"""
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if not self.vessel.strip():
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
|
if not self.solvent.strip():
|
||||||
|
raise ValueError("solvent 参数不能为空")
|
||||||
|
|
||||||
|
if self.volume <= 0:
|
||||||
|
raise ValueError("volume 必须大于0")
|
||||||
|
|
||||||
|
# 修正参数范围
|
||||||
|
if self.temp < 0 or self.temp > 200:
|
||||||
|
logger.warning(f"温度 {self.temp}°C 超出范围,修正为 25°C")
|
||||||
|
self.temp = 25.0
|
||||||
|
|
||||||
|
if self.stir_speed < 0 or self.stir_speed > 500:
|
||||||
|
logger.warning(f"搅拌速度 {self.stir_speed} RPM 超出范围,修正为 0")
|
||||||
|
self.stir_speed = 0.0
|
||||||
|
|
||||||
|
if self.time < 0:
|
||||||
|
logger.warning(f"时间 {self.time}s 无效,修正为 0")
|
||||||
|
self.time = 0.0
|
||||||
|
|
||||||
|
if self.repeats < 1:
|
||||||
|
logger.warning(f"重复次数 {self.repeats} 无效,修正为 1")
|
||||||
|
self.repeats = 1
|
||||||
|
elif self.repeats > 10:
|
||||||
|
logger.warning(f"重复次数 {self.repeats} 过多,修正为 10")
|
||||||
|
self.repeats = 10
|
||||||
|
|
||||||
class AdjustPHProtocol(BaseModel):
|
class AdjustPHProtocol(BaseModel):
|
||||||
vessel: str = Field(..., description="目标容器")
|
vessel: str = Field(..., description="目标容器")
|
||||||
|
|||||||
@@ -755,11 +755,9 @@ workstation:
|
|||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
gas: gas
|
gas: gas
|
||||||
repeats: repeats
|
|
||||||
vessel: vessel
|
vessel: vessel
|
||||||
goal_default:
|
goal_default:
|
||||||
gas: ''
|
gas: ''
|
||||||
repeats: 0
|
|
||||||
vessel: ''
|
vessel: ''
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
@@ -827,16 +825,11 @@ workstation:
|
|||||||
properties:
|
properties:
|
||||||
gas:
|
gas:
|
||||||
type: string
|
type: string
|
||||||
repeats:
|
|
||||||
maximum: 2147483647
|
|
||||||
minimum: -2147483648
|
|
||||||
type: integer
|
|
||||||
vessel:
|
vessel:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
- vessel
|
- vessel
|
||||||
- gas
|
- gas
|
||||||
- repeats
|
|
||||||
title: EvacuateAndRefill_Goal
|
title: EvacuateAndRefill_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
result:
|
||||||
@@ -859,111 +852,66 @@ workstation:
|
|||||||
EvaporateProtocol:
|
EvaporateProtocol:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
|
vessel: vessel
|
||||||
pressure: pressure
|
pressure: pressure
|
||||||
stir_speed: stir_speed
|
|
||||||
temp: temp
|
temp: temp
|
||||||
time: time
|
time: time
|
||||||
vessel: vessel
|
stir_speed: stir_speed
|
||||||
|
solvent: solvent
|
||||||
goal_default:
|
goal_default:
|
||||||
pressure: 0.0
|
|
||||||
stir_speed: 0.0
|
|
||||||
temp: 0.0
|
|
||||||
time: 0.0
|
|
||||||
vessel: ''
|
vessel: ''
|
||||||
|
pressure: 0.1
|
||||||
|
temp: 60.0
|
||||||
|
time: 1800.0
|
||||||
|
stir_speed: 100.0
|
||||||
|
solvent: ''
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
data_source: handle
|
data_source: handle
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: Vessel
|
handler_key: vessel
|
||||||
label: Vessel
|
label: Evaporation Vessel
|
||||||
output:
|
output:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
data_source: executor
|
data_source: handle
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: VesselOut
|
handler_key: vessel_out
|
||||||
label: Vessel
|
label: Evaporation Vessel
|
||||||
result: {}
|
result: {}
|
||||||
schema:
|
schema:
|
||||||
description: ROS Action Evaporate 的 JSON Schema
|
description: ROS Action Evaporate 的 JSON Schema
|
||||||
properties:
|
properties:
|
||||||
feedback:
|
|
||||||
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
|
||||||
properties:
|
|
||||||
current_device:
|
|
||||||
type: string
|
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
time_remaining:
|
|
||||||
properties:
|
|
||||||
nanosec:
|
|
||||||
maximum: 4294967295
|
|
||||||
minimum: 0
|
|
||||||
type: integer
|
|
||||||
sec:
|
|
||||||
maximum: 2147483647
|
|
||||||
minimum: -2147483648
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- sec
|
|
||||||
- nanosec
|
|
||||||
title: Duration
|
|
||||||
type: object
|
|
||||||
time_spent:
|
|
||||||
properties:
|
|
||||||
nanosec:
|
|
||||||
maximum: 4294967295
|
|
||||||
minimum: 0
|
|
||||||
type: integer
|
|
||||||
sec:
|
|
||||||
maximum: 2147483647
|
|
||||||
minimum: -2147483648
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- sec
|
|
||||||
- nanosec
|
|
||||||
title: Duration
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- status
|
|
||||||
- current_device
|
|
||||||
- time_spent
|
|
||||||
- time_remaining
|
|
||||||
title: Evaporate_Feedback
|
|
||||||
type: object
|
|
||||||
goal:
|
goal:
|
||||||
description: Action 目标 - 从客户端发送到服务器
|
description: Action 目标
|
||||||
properties:
|
properties:
|
||||||
pressure:
|
|
||||||
type: number
|
|
||||||
stir_speed:
|
|
||||||
type: number
|
|
||||||
temp:
|
|
||||||
type: number
|
|
||||||
time:
|
|
||||||
type: number
|
|
||||||
vessel:
|
vessel:
|
||||||
type: string
|
type: string
|
||||||
|
description: 蒸发容器名称
|
||||||
|
pressure:
|
||||||
|
type: number
|
||||||
|
default: 0.1
|
||||||
|
description: 真空度 (bar)
|
||||||
|
temp:
|
||||||
|
type: number
|
||||||
|
default: 60.0
|
||||||
|
description: 加热温度 (°C)
|
||||||
|
time:
|
||||||
|
type: number
|
||||||
|
default: 1800.0
|
||||||
|
description: 蒸发时间 (秒)
|
||||||
|
stir_speed:
|
||||||
|
type: number
|
||||||
|
default: 100.0
|
||||||
|
description: 旋转速度 (RPM)
|
||||||
|
solvent:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 溶剂名称
|
||||||
required:
|
required:
|
||||||
- vessel
|
- vessel
|
||||||
- pressure
|
|
||||||
- temp
|
|
||||||
- time
|
|
||||||
- stir_speed
|
|
||||||
title: Evaporate_Goal
|
title: Evaporate_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
|
||||||
description: Action 结果 - 完成后从服务器发送到客户端
|
|
||||||
properties:
|
|
||||||
return_info:
|
|
||||||
type: string
|
|
||||||
success:
|
|
||||||
type: boolean
|
|
||||||
required:
|
|
||||||
- return_info
|
|
||||||
- success
|
|
||||||
title: Evaporate_Result
|
|
||||||
type: object
|
|
||||||
required:
|
required:
|
||||||
- goal
|
- goal
|
||||||
title: Evaporate
|
title: Evaporate
|
||||||
@@ -972,20 +920,20 @@ workstation:
|
|||||||
FilterProtocol:
|
FilterProtocol:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
continue_heatchill: continue_heatchill
|
vessel: vessel
|
||||||
filtrate_vessel: filtrate_vessel
|
filtrate_vessel: filtrate_vessel
|
||||||
stir: stir
|
stir: stir
|
||||||
stir_speed: stir_speed
|
stir_speed: stir_speed
|
||||||
temp: temp
|
temp: temp
|
||||||
vessel: vessel
|
continue_heatchill: continue_heatchill
|
||||||
volume: volume
|
volume: volume
|
||||||
goal_default:
|
goal_default:
|
||||||
continue_heatchill: false
|
vessel: ''
|
||||||
filtrate_vessel: ''
|
filtrate_vessel: ''
|
||||||
stir: false
|
stir: false
|
||||||
stir_speed: 0.0
|
stir_speed: 0.0
|
||||||
temp: 0.0
|
temp: 25.0
|
||||||
vessel: ''
|
continue_heatchill: false
|
||||||
volume: 0.0
|
volume: 0.0
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
@@ -994,7 +942,7 @@ workstation:
|
|||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: Vessel
|
handler_key: Vessel
|
||||||
label: Vessel
|
label: Vessel
|
||||||
- data_key: vessel
|
- data_key: filtrate_vessel
|
||||||
data_source: handle
|
data_source: handle
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: filtrate_vessel
|
handler_key: filtrate_vessel
|
||||||
@@ -1005,7 +953,7 @@ workstation:
|
|||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: VesselOut
|
handler_key: VesselOut
|
||||||
label: Vessel
|
label: Vessel
|
||||||
- data_key: vessel
|
- data_key: filtrate_vessel
|
||||||
data_source: executor
|
data_source: executor
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: filtrate_out
|
handler_key: filtrate_out
|
||||||
@@ -1035,28 +983,35 @@ workstation:
|
|||||||
goal:
|
goal:
|
||||||
description: Action 目标 - 从客户端发送到服务器
|
description: Action 目标 - 从客户端发送到服务器
|
||||||
properties:
|
properties:
|
||||||
continue_heatchill:
|
|
||||||
type: boolean
|
|
||||||
filtrate_vessel:
|
|
||||||
type: string
|
|
||||||
stir:
|
|
||||||
type: boolean
|
|
||||||
stir_speed:
|
|
||||||
type: number
|
|
||||||
temp:
|
|
||||||
type: number
|
|
||||||
vessel:
|
vessel:
|
||||||
type: string
|
type: string
|
||||||
|
description: 过滤容器名称
|
||||||
|
filtrate_vessel:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 滤液容器名称(可选)
|
||||||
|
stir:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: 是否搅拌
|
||||||
|
stir_speed:
|
||||||
|
type: number
|
||||||
|
default: 0.0
|
||||||
|
description: 搅拌速度
|
||||||
|
temp:
|
||||||
|
type: number
|
||||||
|
default: 25.0
|
||||||
|
description: 温度
|
||||||
|
continue_heatchill:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: 是否继续加热冷却
|
||||||
volume:
|
volume:
|
||||||
type: number
|
type: number
|
||||||
|
default: 0.0
|
||||||
|
description: 过滤体积
|
||||||
required:
|
required:
|
||||||
- vessel
|
- vessel
|
||||||
- filtrate_vessel
|
|
||||||
- stir
|
|
||||||
- stir_speed
|
|
||||||
- temp
|
|
||||||
- continue_heatchill
|
|
||||||
- volume
|
|
||||||
title: Filter_Goal
|
title: Filter_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
result:
|
||||||
@@ -1193,19 +1148,27 @@ workstation:
|
|||||||
HeatChillProtocol:
|
HeatChillProtocol:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
purpose: purpose
|
vessel: vessel
|
||||||
stir: stir
|
|
||||||
stir_speed: stir_speed
|
|
||||||
temp: temp
|
temp: temp
|
||||||
time: time
|
time: time
|
||||||
vessel: vessel
|
temp_spec: temp_spec
|
||||||
|
time_spec: time_spec
|
||||||
|
pressure: pressure
|
||||||
|
reflux_solvent: reflux_solvent
|
||||||
|
stir: stir
|
||||||
|
stir_speed: stir_speed
|
||||||
|
purpose: purpose
|
||||||
goal_default:
|
goal_default:
|
||||||
purpose: ''
|
|
||||||
stir: false
|
|
||||||
stir_speed: 0.0
|
|
||||||
temp: 0.0
|
|
||||||
time: 0.0
|
|
||||||
vessel: ''
|
vessel: ''
|
||||||
|
temp: 25.0
|
||||||
|
time: 300.0
|
||||||
|
temp_spec: ''
|
||||||
|
time_spec: ''
|
||||||
|
pressure: ''
|
||||||
|
reflux_solvent: ''
|
||||||
|
stir: false
|
||||||
|
stir_speed: 300.0
|
||||||
|
purpose: ''
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
@@ -1224,7 +1187,7 @@ workstation:
|
|||||||
description: ROS Action HeatChill 的 JSON Schema
|
description: ROS Action HeatChill 的 JSON Schema
|
||||||
properties:
|
properties:
|
||||||
feedback:
|
feedback:
|
||||||
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
description: Action 反馈
|
||||||
properties:
|
properties:
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
@@ -1233,39 +1196,64 @@ workstation:
|
|||||||
title: HeatChill_Feedback
|
title: HeatChill_Feedback
|
||||||
type: object
|
type: object
|
||||||
goal:
|
goal:
|
||||||
description: Action 目标 - 从客户端发送到服务器
|
description: Action 目标
|
||||||
properties:
|
properties:
|
||||||
purpose:
|
|
||||||
type: string
|
|
||||||
stir:
|
|
||||||
type: boolean
|
|
||||||
stir_speed:
|
|
||||||
type: number
|
|
||||||
temp:
|
|
||||||
type: number
|
|
||||||
time:
|
|
||||||
type: number
|
|
||||||
vessel:
|
vessel:
|
||||||
type: string
|
type: string
|
||||||
|
description: 加热容器名称
|
||||||
|
temp:
|
||||||
|
type: number
|
||||||
|
default: 25.0
|
||||||
|
description: 目标温度
|
||||||
|
time:
|
||||||
|
type: number
|
||||||
|
default: 300.0
|
||||||
|
description: 加热时间
|
||||||
|
temp_spec:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 温度规格
|
||||||
|
time_spec:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 时间规格
|
||||||
|
pressure:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 压力规格
|
||||||
|
reflux_solvent:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 回流溶剂名称
|
||||||
|
stir:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: 是否搅拌
|
||||||
|
stir_speed:
|
||||||
|
type: number
|
||||||
|
default: 300.0
|
||||||
|
description: 搅拌速度
|
||||||
|
purpose:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 操作目的
|
||||||
required:
|
required:
|
||||||
- vessel
|
- vessel
|
||||||
- temp
|
|
||||||
- time
|
|
||||||
- stir
|
|
||||||
- stir_speed
|
|
||||||
- purpose
|
|
||||||
title: HeatChill_Goal
|
title: HeatChill_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
result:
|
||||||
description: Action 结果 - 完成后从服务器发送到客户端
|
description: Action 结果
|
||||||
properties:
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
return_info:
|
return_info:
|
||||||
type: string
|
type: string
|
||||||
success:
|
success:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- return_info
|
|
||||||
- success
|
- success
|
||||||
|
- message
|
||||||
|
- return_info
|
||||||
title: HeatChill_Result
|
title: HeatChill_Result
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -1412,6 +1400,11 @@ workstation:
|
|||||||
to_vessel: to_vessel
|
to_vessel: to_vessel
|
||||||
viscous: viscous
|
viscous: viscous
|
||||||
volume: volume
|
volume: volume
|
||||||
|
flowrate: flowrate
|
||||||
|
transfer_flowrate: transfer_flowrate
|
||||||
|
rate_spec: rate_spec
|
||||||
|
event: event
|
||||||
|
through: through
|
||||||
goal_default:
|
goal_default:
|
||||||
amount: ''
|
amount: ''
|
||||||
from_vessel: ''
|
from_vessel: ''
|
||||||
@@ -1423,6 +1416,11 @@ workstation:
|
|||||||
to_vessel: ''
|
to_vessel: ''
|
||||||
viscous: false
|
viscous: false
|
||||||
volume: 0.0
|
volume: 0.0
|
||||||
|
flowrate: 0.0
|
||||||
|
transfer_flowrate: 0.0
|
||||||
|
rate_spec: ''
|
||||||
|
event: ''
|
||||||
|
through: ''
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
@@ -1453,7 +1451,7 @@ workstation:
|
|||||||
label: To Vessel
|
label: To Vessel
|
||||||
result: {}
|
result: {}
|
||||||
schema:
|
schema:
|
||||||
description: ROS Action PumpTransfer 的 JSON Schema
|
description: ROS Action PumpTransfer 的 JSON Schema(兼容增强版)
|
||||||
properties:
|
properties:
|
||||||
feedback:
|
feedback:
|
||||||
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
||||||
@@ -1463,34 +1461,8 @@ workstation:
|
|||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
time_remaining:
|
time_remaining:
|
||||||
properties:
|
|
||||||
nanosec:
|
|
||||||
maximum: 4294967295
|
|
||||||
minimum: 0
|
|
||||||
type: integer
|
|
||||||
sec:
|
|
||||||
maximum: 2147483647
|
|
||||||
minimum: -2147483648
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- sec
|
|
||||||
- nanosec
|
|
||||||
title: Duration
|
|
||||||
type: object
|
type: object
|
||||||
time_spent:
|
time_spent:
|
||||||
properties:
|
|
||||||
nanosec:
|
|
||||||
maximum: 4294967295
|
|
||||||
minimum: 0
|
|
||||||
type: integer
|
|
||||||
sec:
|
|
||||||
maximum: 2147483647
|
|
||||||
minimum: -2147483648
|
|
||||||
type: integer
|
|
||||||
required:
|
|
||||||
- sec
|
|
||||||
- nanosec
|
|
||||||
title: Duration
|
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- status
|
- status
|
||||||
@@ -1506,35 +1478,35 @@ workstation:
|
|||||||
type: string
|
type: string
|
||||||
from_vessel:
|
from_vessel:
|
||||||
type: string
|
type: string
|
||||||
rinsing_repeats:
|
to_vessel:
|
||||||
maximum: 2147483647
|
type: string
|
||||||
minimum: -2147483648
|
volume:
|
||||||
type: integer
|
type: number
|
||||||
|
time:
|
||||||
|
type: number
|
||||||
|
viscous:
|
||||||
|
type: boolean
|
||||||
rinsing_solvent:
|
rinsing_solvent:
|
||||||
type: string
|
type: string
|
||||||
rinsing_volume:
|
rinsing_volume:
|
||||||
type: number
|
type: number
|
||||||
|
rinsing_repeats:
|
||||||
|
type: integer
|
||||||
solid:
|
solid:
|
||||||
type: boolean
|
type: boolean
|
||||||
time:
|
flowrate:
|
||||||
type: number
|
type: number
|
||||||
to_vessel:
|
transfer_flowrate:
|
||||||
|
type: number
|
||||||
|
rate_spec:
|
||||||
|
type: string
|
||||||
|
event:
|
||||||
|
type: string
|
||||||
|
through:
|
||||||
type: string
|
type: string
|
||||||
viscous:
|
|
||||||
type: boolean
|
|
||||||
volume:
|
|
||||||
type: number
|
|
||||||
required:
|
required:
|
||||||
- from_vessel
|
- from_vessel
|
||||||
- to_vessel
|
- to_vessel
|
||||||
- volume
|
|
||||||
- amount
|
|
||||||
- time
|
|
||||||
- viscous
|
|
||||||
- rinsing_solvent
|
|
||||||
- rinsing_volume
|
|
||||||
- rinsing_repeats
|
|
||||||
- solid
|
|
||||||
title: PumpTransfer_Goal
|
title: PumpTransfer_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
result:
|
||||||
@@ -1888,13 +1860,21 @@ workstation:
|
|||||||
StirProtocol:
|
StirProtocol:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
settling_time: settling_time
|
vessel: vessel
|
||||||
stir_speed: stir_speed
|
time: time
|
||||||
|
event: event
|
||||||
|
time_spec: time_spec
|
||||||
stir_time: stir_time
|
stir_time: stir_time
|
||||||
|
stir_speed: stir_speed
|
||||||
|
settling_time: settling_time
|
||||||
goal_default:
|
goal_default:
|
||||||
settling_time: 0.0
|
vessel: ''
|
||||||
stir_speed: 0.0
|
time: '5 min'
|
||||||
stir_time: 0.0
|
event: ''
|
||||||
|
time_spec: ''
|
||||||
|
stir_time: 300.0
|
||||||
|
stir_speed: 200.0
|
||||||
|
settling_time: 60.0
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
@@ -1913,7 +1893,7 @@ workstation:
|
|||||||
description: ROS Action Stir 的 JSON Schema
|
description: ROS Action Stir 的 JSON Schema
|
||||||
properties:
|
properties:
|
||||||
feedback:
|
feedback:
|
||||||
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
description: Action 反馈
|
||||||
properties:
|
properties:
|
||||||
status:
|
status:
|
||||||
type: string
|
type: string
|
||||||
@@ -1922,30 +1902,52 @@ workstation:
|
|||||||
title: Stir_Feedback
|
title: Stir_Feedback
|
||||||
type: object
|
type: object
|
||||||
goal:
|
goal:
|
||||||
description: Action 目标 - 从客户端发送到服务器
|
description: Action 目标
|
||||||
properties:
|
properties:
|
||||||
settling_time:
|
vessel:
|
||||||
type: number
|
type: string
|
||||||
stir_speed:
|
description: 搅拌容器名称
|
||||||
type: number
|
time:
|
||||||
|
type: string
|
||||||
|
default: '5 min'
|
||||||
|
description: 搅拌时间
|
||||||
|
event:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 事件标识
|
||||||
|
time_spec:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 时间规格
|
||||||
stir_time:
|
stir_time:
|
||||||
type: number
|
type: number
|
||||||
|
default: 300.0
|
||||||
|
description: 搅拌时间(秒)
|
||||||
|
stir_speed:
|
||||||
|
type: number
|
||||||
|
default: 200.0
|
||||||
|
description: 搅拌速度
|
||||||
|
settling_time:
|
||||||
|
type: number
|
||||||
|
default: 60.0
|
||||||
|
description: 沉降时间
|
||||||
required:
|
required:
|
||||||
- stir_time
|
- vessel
|
||||||
- stir_speed
|
|
||||||
- settling_time
|
|
||||||
title: Stir_Goal
|
title: Stir_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
result:
|
||||||
description: Action 结果 - 完成后从服务器发送到客户端
|
description: Action 结果
|
||||||
properties:
|
properties:
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
return_info:
|
return_info:
|
||||||
type: string
|
type: string
|
||||||
success:
|
success:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- return_info
|
|
||||||
- success
|
- success
|
||||||
|
- message
|
||||||
|
- return_info
|
||||||
title: Stir_Result
|
title: Stir_Result
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -2000,16 +2002,13 @@ workstation:
|
|||||||
result:
|
result:
|
||||||
description: Action 结果 - 完成后从服务器发送到客户端
|
description: Action 结果 - 完成后从服务器发送到客户端
|
||||||
properties:
|
properties:
|
||||||
message:
|
|
||||||
type: string
|
|
||||||
return_info:
|
return_info:
|
||||||
type: string
|
type: string
|
||||||
success:
|
success:
|
||||||
type: boolean
|
type: boolean
|
||||||
required:
|
required:
|
||||||
- success
|
|
||||||
- message
|
|
||||||
- return_info
|
- return_info
|
||||||
|
- success
|
||||||
title: StopStir_Result
|
title: StopStir_Result
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
@@ -2149,25 +2148,25 @@ workstation:
|
|||||||
WashSolidProtocol:
|
WashSolidProtocol:
|
||||||
feedback: {}
|
feedback: {}
|
||||||
goal:
|
goal:
|
||||||
filtrate_vessel: filtrate_vessel
|
vessel: vessel
|
||||||
repeats: repeats
|
|
||||||
solvent: solvent
|
solvent: solvent
|
||||||
|
volume: volume
|
||||||
|
filtrate_vessel: filtrate_vessel
|
||||||
|
temp: temp
|
||||||
stir: stir
|
stir: stir
|
||||||
stir_speed: stir_speed
|
stir_speed: stir_speed
|
||||||
temp: temp
|
|
||||||
time: time
|
time: time
|
||||||
vessel: vessel
|
repeats: repeats
|
||||||
volume: volume
|
|
||||||
goal_default:
|
goal_default:
|
||||||
filtrate_vessel: ''
|
vessel: ''
|
||||||
repeats: 0
|
|
||||||
solvent: ''
|
solvent: ''
|
||||||
|
volume: 0.0
|
||||||
|
filtrate_vessel: ''
|
||||||
|
temp: 25.0
|
||||||
stir: false
|
stir: false
|
||||||
stir_speed: 0.0
|
stir_speed: 0.0
|
||||||
temp: 0.0
|
|
||||||
time: 0.0
|
time: 0.0
|
||||||
vessel: ''
|
repeats: 1
|
||||||
volume: 0.0
|
|
||||||
handles:
|
handles:
|
||||||
input:
|
input:
|
||||||
- data_key: vessel
|
- data_key: vessel
|
||||||
@@ -2180,8 +2179,8 @@ workstation:
|
|||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: solvent
|
handler_key: solvent
|
||||||
label: Solvent
|
label: Solvent
|
||||||
- data_key: vessel
|
- data_key: filtrate_vessel
|
||||||
data_source: executor
|
data_source: handle
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: filtrate_vessel
|
handler_key: filtrate_vessel
|
||||||
label: Filtrate Vessel
|
label: Filtrate Vessel
|
||||||
@@ -2191,7 +2190,7 @@ workstation:
|
|||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: VesselOut
|
handler_key: VesselOut
|
||||||
label: Vessel Out
|
label: Vessel Out
|
||||||
- data_key: vessel
|
- data_key: filtrate_vessel
|
||||||
data_source: executor
|
data_source: executor
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: filtrate_vessel_out
|
handler_key: filtrate_vessel_out
|
||||||
@@ -2215,36 +2214,45 @@ workstation:
|
|||||||
goal:
|
goal:
|
||||||
description: Action 目标 - 从客户端发送到服务器
|
description: Action 目标 - 从客户端发送到服务器
|
||||||
properties:
|
properties:
|
||||||
filtrate_vessel:
|
|
||||||
type: string
|
|
||||||
repeats:
|
|
||||||
maximum: 2147483647
|
|
||||||
minimum: -2147483648
|
|
||||||
type: integer
|
|
||||||
solvent:
|
|
||||||
type: string
|
|
||||||
stir:
|
|
||||||
type: boolean
|
|
||||||
stir_speed:
|
|
||||||
type: number
|
|
||||||
temp:
|
|
||||||
type: number
|
|
||||||
time:
|
|
||||||
type: number
|
|
||||||
vessel:
|
vessel:
|
||||||
type: string
|
type: string
|
||||||
|
description: 装有固体的容器名称
|
||||||
|
solvent:
|
||||||
|
type: string
|
||||||
|
description: 清洗溶剂名称
|
||||||
volume:
|
volume:
|
||||||
type: number
|
type: number
|
||||||
|
description: 清洗溶剂体积
|
||||||
|
filtrate_vessel:
|
||||||
|
type: string
|
||||||
|
default: ''
|
||||||
|
description: 滤液收集容器(可选)
|
||||||
|
temp:
|
||||||
|
type: number
|
||||||
|
default: 25.0
|
||||||
|
description: 清洗温度
|
||||||
|
stir:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
description: 是否搅拌
|
||||||
|
stir_speed:
|
||||||
|
type: number
|
||||||
|
default: 0.0
|
||||||
|
description: 搅拌速度
|
||||||
|
time:
|
||||||
|
type: number
|
||||||
|
default: 0.0
|
||||||
|
description: 清洗时间
|
||||||
|
repeats:
|
||||||
|
type: integer
|
||||||
|
default: 1
|
||||||
|
minimum: 1
|
||||||
|
maximum: 10
|
||||||
|
description: 重复次数
|
||||||
required:
|
required:
|
||||||
- vessel
|
- vessel
|
||||||
- solvent
|
- solvent
|
||||||
- volume
|
- volume
|
||||||
- filtrate_vessel
|
|
||||||
- temp
|
|
||||||
- stir
|
|
||||||
- stir_speed
|
|
||||||
- time
|
|
||||||
- repeats
|
|
||||||
title: WashSolid_Goal
|
title: WashSolid_Goal
|
||||||
type: object
|
type: object
|
||||||
result:
|
result:
|
||||||
@@ -2645,7 +2653,7 @@ workstation:
|
|||||||
vessel: vessel
|
vessel: vessel
|
||||||
volume: volume
|
volume: volume
|
||||||
goal_default:
|
goal_default:
|
||||||
ratio: ''
|
ratio: 1.0
|
||||||
solvent1: ''
|
solvent1: ''
|
||||||
solvent2: ''
|
solvent2: ''
|
||||||
vessel: ''
|
vessel: ''
|
||||||
@@ -2657,12 +2665,12 @@ workstation:
|
|||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: Vessel
|
handler_key: Vessel
|
||||||
label: Vessel
|
label: Vessel
|
||||||
- data_key: solvent
|
- data_key: solvent1
|
||||||
data_source: handle
|
data_source: handle
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: solvent1
|
handler_key: solvent1
|
||||||
label: Solvent 1
|
label: Solvent 1
|
||||||
- data_key: solvent
|
- data_key: solvent2
|
||||||
data_source: handle
|
data_source: handle
|
||||||
data_type: resource
|
data_type: resource
|
||||||
handler_key: solvent2
|
handler_key: solvent2
|
||||||
@@ -2680,10 +2688,10 @@ workstation:
|
|||||||
feedback:
|
feedback:
|
||||||
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
description: Action 反馈 - 执行过程中从服务器发送到客户端
|
||||||
properties:
|
properties:
|
||||||
status:
|
|
||||||
type: string
|
|
||||||
progress:
|
progress:
|
||||||
type: number
|
type: number
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
required:
|
required:
|
||||||
- status
|
- status
|
||||||
- progress
|
- progress
|
||||||
@@ -2693,7 +2701,7 @@ workstation:
|
|||||||
description: Action 目标 - 从客户端发送到服务器
|
description: Action 目标 - 从客户端发送到服务器
|
||||||
properties:
|
properties:
|
||||||
ratio:
|
ratio:
|
||||||
type: string
|
type: number
|
||||||
solvent1:
|
solvent1:
|
||||||
type: string
|
type: string
|
||||||
solvent2:
|
solvent2:
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# Organic
|
# Organic Synthesis Station EvacuateAndRefill Action
|
||||||
string vessel
|
string vessel
|
||||||
string gas
|
string gas
|
||||||
int32 repeats
|
|
||||||
---
|
---
|
||||||
string return_info
|
string return_info
|
||||||
bool success
|
bool success
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
# Organic
|
# Organic Synthesis Station Evaporate Action
|
||||||
string vessel
|
string vessel
|
||||||
float64 pressure
|
float64 pressure
|
||||||
float64 temp
|
float64 temp
|
||||||
float64 time
|
float64 time
|
||||||
float64 stir_speed
|
float64 stir_speed
|
||||||
|
string solvent
|
||||||
---
|
---
|
||||||
string return_info
|
string return_info
|
||||||
bool success
|
bool success
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# Goal - 过滤操作的目标参数
|
# Goal - 过滤操作的目标参数
|
||||||
string vessel # 过滤容器
|
string vessel # 过滤容器(必需)
|
||||||
string filtrate_vessel # 滤液容器 (可选)
|
string filtrate_vessel # 滤液容器(可选)
|
||||||
bool stir # 是否搅拌
|
bool stir # 是否搅拌(默认false)
|
||||||
float64 stir_speed # 搅拌速度 (可选)
|
float64 stir_speed # 搅拌速度(默认0.0)
|
||||||
float64 temp # 温度 (可选,摄氏度)
|
float64 temp # 温度(默认25.0)
|
||||||
bool continue_heatchill # 是否继续加热冷却
|
bool continue_heatchill # 是否继续加热冷却(默认false)
|
||||||
float64 volume # 过滤体积 (可选)
|
float64 volume # 过滤体积(默认0.0)
|
||||||
---
|
---
|
||||||
# Result - 操作结果
|
# Result - 操作结果
|
||||||
bool success # 操作是否成功
|
bool success # 操作是否成功
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
# Organic
|
# Goal - 加热冷却操作的目标参数
|
||||||
string vessel
|
string vessel # 加热容器名称(必需)
|
||||||
float64 temp
|
float64 temp # 目标温度(可选,默认25.0)
|
||||||
float64 time
|
float64 time # 加热时间(可选,默认300.0)
|
||||||
bool stir
|
string temp_spec # 温度规格(可选)
|
||||||
float64 stir_speed
|
string time_spec # 时间规格(可选)
|
||||||
string purpose
|
string pressure # 压力规格(可选,不做特殊处理)
|
||||||
|
string reflux_solvent # 回流溶剂名称(可选,不做特殊处理)
|
||||||
|
bool stir # 是否搅拌(可选,默认false)
|
||||||
|
float64 stir_speed # 搅拌速度(可选,默认300.0)
|
||||||
|
string purpose # 操作目的(可选)
|
||||||
---
|
---
|
||||||
|
# Result - 操作结果
|
||||||
|
bool success # 操作是否成功
|
||||||
|
string message # 结果消息
|
||||||
string return_info
|
string return_info
|
||||||
bool success
|
|
||||||
---
|
---
|
||||||
string status
|
# Feedback - 实时反馈
|
||||||
|
string status # 当前状态描述
|
||||||
@@ -9,6 +9,11 @@ string rinsing_solvent
|
|||||||
float64 rinsing_volume
|
float64 rinsing_volume
|
||||||
int32 rinsing_repeats
|
int32 rinsing_repeats
|
||||||
bool solid
|
bool solid
|
||||||
|
float64 flowrate
|
||||||
|
float64 transfer_flowrate
|
||||||
|
string rate_spec
|
||||||
|
string event
|
||||||
|
string through
|
||||||
---
|
---
|
||||||
string return_info
|
string return_info
|
||||||
bool success
|
bool success
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
# Organic
|
# Goal - 搅拌操作的目标参数
|
||||||
float64 stir_time
|
string vessel # 搅拌容器名称(必需)
|
||||||
float64 stir_speed
|
string time # 搅拌时间(如 "0.5 h", "30 min")
|
||||||
float64 settling_time
|
string event # 事件标识(如 "A", "B")
|
||||||
|
string time_spec # 时间规格(如 "several minutes")
|
||||||
|
float64 stir_time # 解析后的搅拌时间(秒)
|
||||||
|
float64 stir_speed # 搅拌速度(默认200.0)
|
||||||
|
float64 settling_time # 沉降时间(默认60.0)
|
||||||
---
|
---
|
||||||
|
# Result - 操作结果
|
||||||
|
bool success # 操作是否成功
|
||||||
|
string message # 结果消息
|
||||||
string return_info
|
string return_info
|
||||||
bool success
|
|
||||||
---
|
---
|
||||||
string status
|
# Feedback - 实时反馈
|
||||||
|
string status # 当前状态描述
|
||||||
@@ -1,16 +1,19 @@
|
|||||||
string vessel # 装有固体物质的容器名称
|
# Goal - 固体清洗操作的目标参数
|
||||||
string solvent # 用于清洗固体的溶剂名称
|
string vessel # 装有固体的容器名称(必需)
|
||||||
float64 volume # 清洗溶剂的体积
|
string solvent # 清洗溶剂名称(必需)
|
||||||
string filtrate_vessel # 滤液要收集到的容器名称,可选参数
|
float64 volume # 清洗溶剂体积(必需)
|
||||||
float64 temp # 清洗时的温度,可选参数
|
string filtrate_vessel # 滤液收集容器(可选,默认"")
|
||||||
bool stir # 是否在清洗过程中搅拌,默认为 False
|
float64 temp # 清洗温度(可选,默认25.0)
|
||||||
float64 stir_speed # 搅拌速度,可选参数
|
bool stir # 是否搅拌(可选,默认false)
|
||||||
float64 time # 清洗的时间,可选参数
|
float64 stir_speed # 搅拌速度(可选,默认0.0)
|
||||||
int32 repeats # 清洗操作的重复次数,默认为 1
|
float64 time # 清洗时间(可选,默认0.0)
|
||||||
|
int32 repeats # 重复次数(可选,默认1)
|
||||||
---
|
---
|
||||||
bool success # 操作是否成功
|
# Result - 操作结果
|
||||||
string message # 结果消息
|
bool success # 操作是否成功
|
||||||
|
string message # 结果消息
|
||||||
string return_info
|
string return_info
|
||||||
---
|
---
|
||||||
string status # 当前状态描述
|
# Feedback - 实时反馈
|
||||||
float64 progress # 进度百分比 (0-100)
|
string status # 当前状态描述
|
||||||
|
float64 progress # 进度百分比 (0-100)
|
||||||
Reference in New Issue
Block a user