mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-05 05:45:10 +00:00
359 lines
12 KiB
Python
359 lines
12 KiB
Python
from typing import List, Dict, Any
|
||
import networkx as nx
|
||
from .pump_protocol import generate_pump_protocol
|
||
|
||
|
||
def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||
"""
|
||
查找溶剂容器
|
||
"""
|
||
# 按照pump_protocol的命名规则查找溶剂瓶
|
||
solvent_vessel_id = f"flask_{solvent}"
|
||
|
||
if solvent_vessel_id in G.nodes():
|
||
return solvent_vessel_id
|
||
|
||
# 如果直接匹配失败,尝试模糊匹配
|
||
for node in G.nodes():
|
||
if node.startswith('flask_') and solvent.lower() in node.lower():
|
||
return node
|
||
|
||
# 如果还是找不到,列出所有可用的溶剂瓶
|
||
available_flasks = [node for node in G.nodes()
|
||
if node.startswith('flask_')
|
||
and G.nodes[node].get('type') == 'container']
|
||
|
||
raise ValueError(f"找不到溶剂 '{solvent}' 对应的溶剂瓶。可用溶剂瓶: {available_flasks}")
|
||
|
||
|
||
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
|
||
|
||
# 如果没有直接连接,返回第一个可用的加热器
|
||
return heatchill_nodes[0] if heatchill_nodes else None
|
||
|
||
|
||
def generate_dissolve_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
solvent: str,
|
||
volume: float,
|
||
amount: str = "",
|
||
temp: float = 25.0,
|
||
time: float = 0.0,
|
||
stir_speed: float = 300.0
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
生成溶解操作的协议序列,复用 pump_protocol 的成熟算法
|
||
|
||
溶解流程:
|
||
1. 溶剂转移:将溶剂从溶剂瓶转移到目标容器
|
||
2. 启动加热搅拌:设置温度和搅拌
|
||
3. 等待溶解:监控溶解过程
|
||
4. 停止加热搅拌:完成溶解
|
||
|
||
Args:
|
||
G: 有向图,节点为设备和容器,边为流体管道
|
||
vessel: 目标容器(要进行溶解的容器)
|
||
solvent: 溶剂名称(用于查找对应的溶剂瓶)
|
||
volume: 溶剂体积 (mL)
|
||
amount: 要溶解的物质描述
|
||
temp: 溶解温度 (°C),默认25°C(室温)
|
||
time: 溶解时间 (秒),默认0(立即完成)
|
||
stir_speed: 搅拌速度 (RPM),默认300 RPM
|
||
|
||
Returns:
|
||
List[Dict[str, Any]]: 溶解操作的动作序列
|
||
|
||
Raises:
|
||
ValueError: 当找不到必要的设备或容器时
|
||
|
||
Examples:
|
||
dissolve_actions = generate_dissolve_protocol(G, "reaction_mixture", "DMF", 10.0, "NaCl 2g", 60.0, 600.0, 400.0)
|
||
"""
|
||
action_sequence = []
|
||
|
||
print(f"DISSOLVE: 开始生成溶解协议")
|
||
print(f" - 目标容器: {vessel}")
|
||
print(f" - 溶剂: {solvent}")
|
||
print(f" - 溶剂体积: {volume} mL")
|
||
print(f" - 要溶解的物质: {amount}")
|
||
print(f" - 溶解温度: {temp}°C")
|
||
print(f" - 溶解时间: {time}s ({time/60:.1f}分钟)" if time > 0 else " - 溶解时间: 立即完成")
|
||
print(f" - 搅拌速度: {stir_speed} RPM")
|
||
|
||
# 验证目标容器存在
|
||
if vessel not in G.nodes():
|
||
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
|
||
|
||
# 查找溶剂瓶
|
||
try:
|
||
solvent_vessel = find_solvent_vessel(G, solvent)
|
||
print(f"DISSOLVE: 找到溶剂瓶: {solvent_vessel}")
|
||
except ValueError as e:
|
||
raise ValueError(f"无法找到溶剂 '{solvent}': {str(e)}")
|
||
|
||
# 验证是否存在从溶剂瓶到目标容器的路径
|
||
try:
|
||
path = nx.shortest_path(G, source=solvent_vessel, target=vessel)
|
||
print(f"DISSOLVE: 找到路径 {solvent_vessel} -> {vessel}: {path}")
|
||
except nx.NetworkXNoPath:
|
||
raise ValueError(f"从溶剂瓶 '{solvent_vessel}' 到目标容器 '{vessel}' 没有可用路径")
|
||
|
||
# 查找加热搅拌器
|
||
heatchill_id = None
|
||
if temp > 25.0 or stir_speed > 0 or time > 0:
|
||
try:
|
||
heatchill_id = find_connected_heatchill(G, vessel)
|
||
if heatchill_id:
|
||
print(f"DISSOLVE: 找到加热搅拌器: {heatchill_id}")
|
||
else:
|
||
print(f"DISSOLVE: 警告 - 需要加热/搅拌但未找到与容器 {vessel} 相连的加热搅拌器")
|
||
except Exception as e:
|
||
print(f"DISSOLVE: 加热搅拌器配置出错: {str(e)}")
|
||
|
||
# === 第一步:启动加热搅拌(在添加溶剂前) ===
|
||
if heatchill_id and (temp > 25.0 or time > 0):
|
||
print(f"DISSOLVE: 启动加热搅拌器,温度: {temp}°C")
|
||
|
||
if time > 0:
|
||
# 如果指定了时间,使用定时加热搅拌
|
||
heatchill_action = {
|
||
"device_id": heatchill_id,
|
||
"action_name": "heat_chill",
|
||
"action_kwargs": {
|
||
"vessel": vessel,
|
||
"temp": temp,
|
||
"time": time,
|
||
"stir": True,
|
||
"stir_speed": stir_speed,
|
||
"purpose": f"溶解 {amount} 在 {solvent} 中"
|
||
}
|
||
}
|
||
else:
|
||
# 如果没有指定时间,使用持续加热搅拌
|
||
heatchill_action = {
|
||
"device_id": heatchill_id,
|
||
"action_name": "heat_chill_start",
|
||
"action_kwargs": {
|
||
"vessel": vessel,
|
||
"temp": temp,
|
||
"purpose": f"溶解 {amount} 在 {solvent} 中"
|
||
}
|
||
}
|
||
|
||
action_sequence.append(heatchill_action)
|
||
|
||
# 等待温度稳定
|
||
if temp > 25.0:
|
||
wait_time = min(60, abs(temp - 25.0) * 1.5) # 根据温差估算预热时间
|
||
action_sequence.append({
|
||
"action_name": "wait",
|
||
"action_kwargs": {"time": wait_time}
|
||
})
|
||
|
||
# === 第二步:添加溶剂到目标容器 ===
|
||
if volume > 0:
|
||
print(f"DISSOLVE: 将 {volume} mL {solvent} 从 {solvent_vessel} 转移到 {vessel}")
|
||
|
||
# 计算流速 - 溶解时通常用较慢的速度,避免飞溅
|
||
transfer_flowrate = 1.0 # 较慢的转移速度
|
||
flowrate = 0.5 # 较慢的注入速度
|
||
|
||
try:
|
||
# 使用成熟的 pump_protocol 算法进行液体转移
|
||
pump_actions = generate_pump_protocol(
|
||
G=G,
|
||
from_vessel=solvent_vessel,
|
||
to_vessel=vessel,
|
||
volume=volume,
|
||
flowrate=flowrate, # 注入速度 - 较慢避免飞溅
|
||
transfer_flowrate=transfer_flowrate # 转移速度
|
||
)
|
||
|
||
action_sequence.extend(pump_actions)
|
||
|
||
except Exception as e:
|
||
raise ValueError(f"生成泵协议时出错: {str(e)}")
|
||
|
||
# 溶剂添加后等待
|
||
action_sequence.append({
|
||
"action_name": "wait",
|
||
"action_kwargs": {"time": 5}
|
||
})
|
||
|
||
# === 第三步:如果没有使用定时加热搅拌,但需要等待溶解 ===
|
||
if time > 0 and heatchill_id and temp <= 25.0:
|
||
# 只需要搅拌等待,不需要加热
|
||
print(f"DISSOLVE: 室温搅拌 {time}s 等待溶解")
|
||
|
||
stir_action = {
|
||
"device_id": heatchill_id,
|
||
"action_name": "heat_chill",
|
||
"action_kwargs": {
|
||
"vessel": vessel,
|
||
"temp": 25.0, # 室温
|
||
"time": time,
|
||
"stir": True,
|
||
"stir_speed": stir_speed,
|
||
"purpose": f"室温搅拌溶解 {amount}"
|
||
}
|
||
}
|
||
action_sequence.append(stir_action)
|
||
|
||
# === 第四步:如果使用了持续加热,需要手动停止 ===
|
||
if heatchill_id and time == 0 and temp > 25.0:
|
||
print(f"DISSOLVE: 停止加热搅拌器")
|
||
|
||
stop_action = {
|
||
"device_id": heatchill_id,
|
||
"action_name": "heat_chill_stop",
|
||
"action_kwargs": {
|
||
"vessel": vessel
|
||
}
|
||
}
|
||
action_sequence.append(stop_action)
|
||
|
||
print(f"DISSOLVE: 生成了 {len(action_sequence)} 个动作")
|
||
print(f"DISSOLVE: 溶解协议生成完成")
|
||
|
||
return action_sequence
|
||
|
||
|
||
# 便捷函数:常用溶解方案
|
||
def generate_room_temp_dissolve_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
solvent: str,
|
||
volume: float,
|
||
amount: str = "",
|
||
stir_time: float = 300.0 # 5分钟
|
||
) -> List[Dict[str, Any]]:
|
||
"""室温溶解:快速搅拌,短时间"""
|
||
return generate_dissolve_protocol(G, vessel, solvent, volume, amount, 25.0, stir_time, 400.0)
|
||
|
||
|
||
def generate_heated_dissolve_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
solvent: str,
|
||
volume: float,
|
||
amount: str = "",
|
||
temp: float = 60.0,
|
||
dissolve_time: float = 900.0 # 15分钟
|
||
) -> List[Dict[str, Any]]:
|
||
"""加热溶解:中等温度,较长时间"""
|
||
return generate_dissolve_protocol(G, vessel, solvent, volume, amount, temp, dissolve_time, 300.0)
|
||
|
||
|
||
def generate_gentle_dissolve_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
solvent: str,
|
||
volume: float,
|
||
amount: str = "",
|
||
temp: float = 40.0,
|
||
dissolve_time: float = 1800.0 # 30分钟
|
||
) -> List[Dict[str, Any]]:
|
||
"""温和溶解:低温,长时间,慢搅拌"""
|
||
return generate_dissolve_protocol(G, vessel, solvent, volume, amount, temp, dissolve_time, 200.0)
|
||
|
||
|
||
def generate_hot_dissolve_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
solvent: str,
|
||
volume: float,
|
||
amount: str = "",
|
||
temp: float = 80.0,
|
||
dissolve_time: float = 600.0 # 10分钟
|
||
) -> List[Dict[str, Any]]:
|
||
"""高温溶解:高温,中等时间,快搅拌"""
|
||
return generate_dissolve_protocol(G, vessel, solvent, volume, amount, temp, dissolve_time, 500.0)
|
||
|
||
|
||
def generate_sequential_dissolve_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: str,
|
||
dissolve_steps: List[Dict[str, Any]]
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
生成连续溶解多种物质的协议
|
||
|
||
Args:
|
||
G: 网络图
|
||
vessel: 目标容器
|
||
dissolve_steps: 溶解步骤列表,每个元素包含溶解参数
|
||
|
||
Returns:
|
||
List[Dict[str, Any]]: 完整的动作序列
|
||
|
||
Example:
|
||
dissolve_steps = [
|
||
{
|
||
"solvent": "water",
|
||
"volume": 5.0,
|
||
"amount": "NaCl 1g",
|
||
"temp": 25.0,
|
||
"time": 300.0,
|
||
"stir_speed": 300.0
|
||
},
|
||
{
|
||
"solvent": "ethanol",
|
||
"volume": 2.0,
|
||
"amount": "organic compound 0.5g",
|
||
"temp": 40.0,
|
||
"time": 600.0,
|
||
"stir_speed": 400.0
|
||
}
|
||
]
|
||
"""
|
||
action_sequence = []
|
||
|
||
for i, step in enumerate(dissolve_steps):
|
||
print(f"DISSOLVE: 处理第 {i+1}/{len(dissolve_steps)} 个溶解步骤")
|
||
|
||
# 生成单个溶解步骤的协议
|
||
dissolve_actions = generate_dissolve_protocol(
|
||
G=G,
|
||
vessel=vessel,
|
||
solvent=step.get('solvent'),
|
||
volume=step.get('volume', 0.0),
|
||
amount=step.get('amount', ''),
|
||
temp=step.get('temp', 25.0),
|
||
time=step.get('time', 0.0),
|
||
stir_speed=step.get('stir_speed', 300.0)
|
||
)
|
||
|
||
action_sequence.extend(dissolve_actions)
|
||
|
||
# 在步骤之间加入等待时间
|
||
if i < len(dissolve_steps) - 1: # 不是最后一个步骤
|
||
action_sequence.append({
|
||
"action_name": "wait",
|
||
"action_kwargs": {"time": 10}
|
||
})
|
||
|
||
print(f"DISSOLVE: 连续溶解协议生成完成,共 {len(action_sequence)} 个动作")
|
||
return action_sequence
|
||
|
||
|
||
# 测试函数
|
||
def test_dissolve_protocol():
|
||
"""测试溶解协议的示例"""
|
||
print("=== DISSOLVE PROTOCOL 测试 ===")
|
||
print("测试完成")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
test_dissolve_protocol() |