修复了部分的protocol因为XDL更新导致的问题

但是pumptransfer,add,dissolve,separate还没修,后续还需要写virtual固体加料器
This commit is contained in:
KCFeng425
2025-07-06 19:21:53 +08:00
parent bef44b2293
commit 4c6e437eb1
18 changed files with 3034 additions and 1606 deletions

View File

@@ -1,5 +1,119 @@
from typing import List, Dict, Any
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(
G: nx.DiGraph,
@@ -11,206 +125,193 @@ def generate_wash_solid_protocol(
stir: bool = False,
stir_speed: float = 0.0,
time: float = 0.0,
repeats: int = 1
repeats: int = 1,
**kwargs # 🔧 接受额外参数,增强兼容性
) -> List[Dict[str, Any]]:
"""
生成固体清洗的协议序列
生成固体清洗操作的协议序列 - 简化版本
Args:
G: 有向图,节点为设备和容器
vessel: 装有固体物质的容器名称
solvent: 用于清洗固体的溶剂名称
volume: 清洗溶剂体积
filtrate_vessel: 滤液收集到的容器名称,可选参数
temp: 清洗时的温度,可选参数
stir: 是否在清洗过程中搅拌,默认False
stir_speed: 搅拌速度,可选参数
time: 清洗时间,可选参数
repeats: 清洗操作的重复次数,默认1
G: 设备图
vessel: 装有固体的容器名称(必需)
solvent: 清洗溶剂名称(必需)
volume: 清洗溶剂体积(必需)
filtrate_vessel: 滤液收集容器(可选,自动查找)
temp: 清洗温度,默认25°C
stir: 是否搅拌默认False
stir_speed: 搅拌速度,默认0
time: 清洗时间,默认0
repeats: 重复次数默认1
**kwargs: 其他参数(兼容性)
Returns:
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 = []
# 验证容器是否存在
# === 参数验证 ===
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():
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
# 查找转移泵设备(用于添加溶剂和转移滤液)
pump_nodes = [node for node in G.nodes()
if G.nodes[node].get('class') == 'virtual_transfer_pump']
if stir_speed < 0 or stir_speed > 500:
debug_print(f"搅拌速度 {stir_speed} RPM 超出范围,修正为 0")
stir_speed = 0.0
if not pump_nodes:
raise ValueError("没有找到可用的转移泵设备")
if time < 0:
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
# 查找加热设备(如果需要加热)
heatchill_nodes = [node for node in G.nodes()
if G.nodes[node].get('class') == 'virtual_heatchill']
debug_print(f"✅ 参数验证通过")
heatchill_id = heatchill_nodes[0] if heatchill_nodes else None
# === 查找设备 ===
debug_print("步骤2: 查找设备...")
# 查找搅拌设备(如果需要搅拌)
stirrer_nodes = [node for node in G.nodes()
if G.nodes[node].get('class') == 'virtual_stirrer']
stirrer_id = stirrer_nodes[0] if stirrer_nodes else None
# 查找过滤设备(用于分离固体和滤液)
filter_nodes = [node for node in G.nodes()
if G.nodes[node].get('class') == 'virtual_filter']
filter_id = filter_nodes[0] if filter_nodes else None
# 查找溶剂容器
solvent_vessel = f"flask_{solvent}"
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:
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"
# 重复清洗操作
for repeat in range(repeats):
repeat_num = repeat + 1
try:
solvent_source = find_solvent_source(G, solvent)
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel)
pump_device = find_pump_device(G)
filter_device = find_filter_device(G)
# 步骤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}"
}
})
debug_print(f"设备配置:")
debug_print(f" - 溶剂源: {solvent_source}")
debug_print(f" - 滤液容器: {actual_filtrate_vessel}")
debug_print(f" - 转移泵: {pump_device}")
debug_print(f" - 过滤器: {filter_device}")
# 步骤2添加清洗溶剂到固体容器
action_sequence.append({
"device_id": pump_id,
"action_name": "transfer",
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
raise ValueError(f"设备查找失败: {str(e)}")
# === 执行清洗循环 ===
debug_print("步骤3: 执行清洗循环...")
for cycle in range(repeats):
debug_print(f"=== 第 {cycle+1}/{repeats} 次清洗 ===")
# 添加清洗溶剂
debug_print(f"添加清洗溶剂: {solvent_source} -> {vessel}")
wash_action = {
"device_id": filter_device,
"action_name": "wash_solid",
"action_kwargs": {
"from_vessel": solvent_vessel,
"to_vessel": vessel,
"vessel": vessel,
"solvent": solvent,
"volume": volume,
"amount": f"清洗溶剂 {solvent} - 第 {repeat_num}",
"time": 0.0,
"viscous": False,
"rinsing_solvent": "",
"rinsing_volume": 0.0,
"rinsing_repeats": 0,
"solid": False
"filtrate_vessel": actual_filtrate_vessel,
"temp": temp,
"stir": stir,
"stir_speed": stir_speed,
"time": time,
"repeats": 1 # 每次循环只做1次
}
}
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:
if time > 0:
# 定时搅拌
action_sequence.append({
"device_id": stirrer_id,
"action_name": "stir",
"action_kwargs": {
"stir_time": time,
"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
# === 总结 ===
debug_print("=" * 50)
debug_print(f"固体清洗协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"清洗容器: {vessel}")
debug_print(f"使用溶剂: {solvent}")
debug_print(f"清洗体积: {volume}mL")
debug_print(f"重复次数: {repeats}")
debug_print("=" * 50)
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()