mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 21:35:09 +00:00
388 lines
13 KiB
Python
388 lines
13 KiB
Python
from typing import List, Dict, Any, Optional
|
||
import networkx as nx
|
||
import logging
|
||
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:
|
||
"""获取容器中的液体体积"""
|
||
debug_print(f"检查容器 '{vessel}' 的液体体积...")
|
||
|
||
if vessel not in G.nodes():
|
||
debug_print(f"容器 '{vessel}' 不存在")
|
||
return 0.0
|
||
|
||
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', [])
|
||
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
|
||
|
||
debug_print(f"未检测到液体体积,返回 0.0")
|
||
return 0.0
|
||
|
||
def find_rotavap_device(G: nx.DiGraph) -> Optional[str]:
|
||
"""查找旋转蒸发仪设备"""
|
||
debug_print("查找旋转蒸发仪设备...")
|
||
|
||
# 查找各种可能的旋转蒸发仪设备
|
||
possible_devices = []
|
||
for node in G.nodes():
|
||
node_data = G.nodes[node]
|
||
node_class = node_data.get('class', '')
|
||
|
||
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]
|
||
|
||
debug_print("未找到旋转蒸发仪设备")
|
||
return None
|
||
|
||
def find_rotavap_vessel(G: nx.DiGraph) -> Optional[str]:
|
||
"""查找旋转蒸发仪样品容器"""
|
||
debug_print("查找旋转蒸发仪样品容器...")
|
||
|
||
possible_vessels = [
|
||
"rotavap", "rotavap_flask", "flask_rotavap",
|
||
"evaporation_flask", "evaporator", "rotary_evaporator"
|
||
]
|
||
|
||
for vessel in possible_vessels:
|
||
if vessel in G.nodes():
|
||
debug_print(f"找到旋转蒸发仪样品容器: {vessel}")
|
||
return vessel
|
||
|
||
debug_print("未找到旋转蒸发仪样品容器")
|
||
return None
|
||
|
||
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(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
pressure: float = 0.1,
|
||
temp: float = 60.0,
|
||
time: float = 1800.0,
|
||
stir_speed: float = 100.0,
|
||
solvent: str = "",
|
||
**kwargs # 接受任意额外参数,增强兼容性
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
生成蒸发操作的协议序列 - 增强兼容性版本
|
||
|
||
Args:
|
||
G: 设备图
|
||
vessel: 蒸发容器名称(必需)
|
||
pressure: 真空度 (bar),默认0.1
|
||
temp: 加热温度 (°C),默认60
|
||
time: 蒸发时间 (秒),默认1800
|
||
stir_speed: 旋转速度 (RPM),默认100
|
||
solvent: 溶剂名称(可选,用于参数优化)
|
||
**kwargs: 其他参数(兼容性)
|
||
|
||
Returns:
|
||
List[Dict[str, Any]]: 动作序列
|
||
"""
|
||
|
||
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 = []
|
||
|
||
# === 参数验证和修正 ===
|
||
debug_print("步骤1: 参数验证和修正...")
|
||
|
||
# 验证必需参数
|
||
if not vessel:
|
||
raise ValueError("vessel 参数不能为空")
|
||
|
||
if vessel not in G.nodes():
|
||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||
|
||
# 修正参数范围
|
||
if pressure <= 0 or pressure > 1.0:
|
||
debug_print(f"真空度 {pressure} bar 超出范围,修正为 0.1 bar")
|
||
pressure = 0.1
|
||
|
||
if temp < 10.0 or temp > 200.0:
|
||
debug_print(f"温度 {temp}°C 超出范围,修正为 60°C")
|
||
temp = 60.0
|
||
|
||
if time <= 0:
|
||
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 = find_rotavap_vessel(G)
|
||
if not rotavap_vessel:
|
||
debug_print("未找到旋转蒸发仪样品容器,使用默认容器")
|
||
rotavap_vessel = "rotavap" # 默认容器
|
||
|
||
# 查找回收容器
|
||
recovery_vessel = find_recovery_vessel(G)
|
||
|
||
debug_print(f"设备配置:")
|
||
debug_print(f" - 旋转蒸发仪设备: {rotavap_device}")
|
||
debug_print(f" - 样品容器: {rotavap_vessel}")
|
||
debug_print(f" - 回收容器: {recovery_vessel}")
|
||
|
||
# === 体积计算 ===
|
||
debug_print("步骤3: 体积计算...")
|
||
|
||
source_volume = get_vessel_liquid_volume(G, vessel)
|
||
|
||
if source_volume > 0:
|
||
transfer_volume = min(source_volume * 0.9, 250.0) # 90%或最多250mL
|
||
debug_print(f"检测到液体体积 {source_volume}mL,转移 {transfer_volume}mL")
|
||
else:
|
||
transfer_volume = 50.0 # 默认小体积,更安全
|
||
debug_print(f"未检测到液体体积,使用默认转移体积 {transfer_volume}mL")
|
||
|
||
# === 生成动作序列 ===
|
||
debug_print("步骤4: 生成动作序列...")
|
||
|
||
# 动作1: 转移溶液到旋转蒸发仪
|
||
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_kwargs": {"time": 10}
|
||
})
|
||
|
||
# 动作2: 执行蒸发
|
||
debug_print(f"执行蒸发: {rotavap_device}")
|
||
evaporate_action = {
|
||
"device_id": rotavap_device,
|
||
"action_name": "evaporate",
|
||
"action_kwargs": {
|
||
"vessel": rotavap_vessel,
|
||
"pressure": pressure,
|
||
"temp": temp,
|
||
"time": time,
|
||
"stir_speed": stir_speed,
|
||
"solvent": solvent
|
||
}
|
||
}
|
||
action_sequence.append(evaporate_action)
|
||
|
||
# 蒸发后等待
|
||
action_sequence.append({
|
||
"action_name": "wait",
|
||
"action_kwargs": {"time": 30}
|
||
})
|
||
|
||
# 动作3: 回收溶剂(如果有回收容器)
|
||
if recovery_vessel:
|
||
debug_print(f"回收溶剂到 {recovery_vessel}")
|
||
try:
|
||
recovery_volume = transfer_volume * 0.7 # 估算回收70%
|
||
recovery_actions = generate_pump_protocol(
|
||
G=G,
|
||
from_vessel="rotavap_condenser", # 假设的冷凝器
|
||
to_vessel=recovery_vessel,
|
||
volume=recovery_volume,
|
||
flowrate=3.0,
|
||
transfer_flowrate=3.0
|
||
)
|
||
action_sequence.extend(recovery_actions)
|
||
debug_print(f"添加了 {len(recovery_actions)} 个回收动作")
|
||
except Exception as e:
|
||
debug_print(f"溶剂回收失败: {str(e)}")
|
||
|
||
# 动作4: 转移浓缩物回原容器
|
||
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,
|
||
from_vessel=rotavap_vessel,
|
||
to_vessel=vessel,
|
||
volume=concentrate_volume,
|
||
flowrate=1.0, # 浓缩物可能粘稠
|
||
transfer_flowrate=1.0
|
||
)
|
||
action_sequence.extend(transfer_back_actions)
|
||
debug_print(f"添加了 {len(transfer_back_actions)} 个转移回收动作")
|
||
except Exception as e:
|
||
debug_print(f"浓缩物转移失败: {str(e)}")
|
||
|
||
# === 总结 ===
|
||
debug_print("=" * 50)
|
||
debug_print(f"蒸发协议生成完成")
|
||
debug_print(f"总动作数: {len(action_sequence)}")
|
||
debug_print(f"处理体积: {transfer_volume}mL")
|
||
debug_print(f"蒸发参数: {pressure} bar, {temp}°C, {time}s, {stir_speed} RPM")
|
||
debug_print("=" * 50)
|
||
|
||
return action_sequence
|
||
|
||
# === 便捷函数 ===
|
||
|
||
def generate_quick_evaporate_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""快速蒸发:低温短时间"""
|
||
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(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""温和蒸发:中等条件"""
|
||
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(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""高真空蒸发:低温高真空"""
|
||
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(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""标准蒸发:常用参数"""
|
||
return generate_evaporate_protocol(
|
||
G, vessel,
|
||
pressure=0.1,
|
||
temp=60.0,
|
||
time=1800.0,
|
||
stir_speed=100.0,
|
||
**kwargs
|
||
)
|