action to resource & 0.9.12

This commit is contained in:
KCFeng425
2025-07-17 04:10:15 +08:00
parent f90be18926
commit 6b7564b9f9
53 changed files with 2526 additions and 761 deletions

View File

@@ -1,12 +1,15 @@
import networkx as nx
import re
import logging
from typing import List, Dict, Any, Tuple, Union
from .pump_protocol import generate_pump_protocol_with_rinsing
logger = logging.getLogger(__name__)
def debug_print(message):
"""调试输出"""
print(f"💎 [RECRYSTALLIZE] {message}", flush=True)
logger.info(f"[RECRYSTALLIZE] {message}")
def parse_volume_with_units(volume_input: Union[str, float, int], default_unit: str = "mL") -> float:
@@ -199,48 +202,63 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
def generate_recrystallize_protocol(
G: nx.DiGraph,
vessel: dict, # 🔧 修改:从字符串改为字典类型
ratio: str,
solvent1: str,
solvent2: str,
vessel: str,
volume: Union[str, float], # 🔧 修改:支持字符串和数值
volume: Union[str, float], # 支持字符串和数值
**kwargs
) -> List[Dict[str, Any]]:
"""
生成重结晶协议序列 - 支持单位
生成重结晶协议序列 - 支持vessel字典和体积运算
Args:
G: 有向图,节点为容器和设备
vessel: 目标容器字典从XDL传入
ratio: 溶剂比例(如 "1:1", "3:7"
solvent1: 第一种溶剂名称
solvent2: 第二种溶剂名称
vessel: 目标容器
volume: 总体积(支持 "100 mL", "50", "2.5 L" 等)
**kwargs: 其他可选参数
Returns:
List[Dict[str, Any]]: 动作序列
"""
# 🔧 核心修改从字典中提取容器ID
vessel_id = vessel["id"]
action_sequence = []
debug_print("💎" * 20)
debug_print("🚀 开始生成重结晶协议(支持单位)✨")
debug_print("🚀 开始生成重结晶协议(支持vessel字典和体积运算)✨")
debug_print(f"📝 输入参数:")
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
debug_print(f" ⚖️ 比例: {ratio}")
debug_print(f" 🧪 溶剂1: {solvent1}")
debug_print(f" 🧪 溶剂2: {solvent2}")
debug_print(f" 🥽 容器: {vessel}")
debug_print(f" 💧 总体积: {volume} (类型: {type(volume)})")
debug_print("💎" * 20)
# 🔧 新增:记录重结晶前的容器状态
debug_print("🔍 记录重结晶前容器状态...")
original_liquid_volume = 0.0
if "data" in vessel and "liquid_volume" in vessel["data"]:
current_volume = vessel["data"]["liquid_volume"]
if isinstance(current_volume, list) and len(current_volume) > 0:
original_liquid_volume = current_volume[0]
elif isinstance(current_volume, (int, float)):
original_liquid_volume = current_volume
debug_print(f"📊 重结晶前液体体积: {original_liquid_volume:.2f}mL")
# 1. 验证目标容器存在
debug_print("📍 步骤1: 验证目标容器... 🔧")
if vessel not in G.nodes():
debug_print(f"❌ 目标容器 '{vessel}' 不存在于系统中! 😱")
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
debug_print(f"✅ 目标容器 '{vessel}' 验证通过 🎯")
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
debug_print(f"❌ 目标容器 '{vessel_id}' 不存在于系统中! 😱")
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
debug_print(f"✅ 目标容器 '{vessel_id}' 验证通过 🎯")
# 2. 🔧 新增:解析体积(支持单位)
# 2. 解析体积(支持单位)
debug_print("📍 步骤2: 解析体积(支持单位)... 💧")
final_volume = parse_volume_with_units(volume, "mL")
debug_print(f"🎯 体积解析完成: {volume}{final_volume}mL ✨")
@@ -281,18 +299,18 @@ def generate_recrystallize_protocol(
# 6. 验证路径存在
debug_print("📍 步骤6: 验证传输路径... 🛤️")
try:
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel)
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel_id) # 🔧 使用 vessel_id
debug_print(f" 🛤️ 溶剂1路径: {''.join(path1)}")
except nx.NetworkXNoPath:
debug_print(f" ❌ 溶剂1路径不可达: {solvent1_vessel}{vessel} 😞")
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel}' 没有可用路径")
debug_print(f" ❌ 溶剂1路径不可达: {solvent1_vessel}{vessel_id} 😞")
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
try:
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel)
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel_id) # 🔧 使用 vessel_id
debug_print(f" 🛤️ 溶剂2路径: {''.join(path2)}")
except nx.NetworkXNoPath:
debug_print(f" ❌ 溶剂2路径不可达: {solvent2_vessel}{vessel} 😞")
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel}' 没有可用路径")
debug_print(f" ❌ 溶剂2路径不可达: {solvent2_vessel}{vessel_id} 😞")
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
# 7. 添加第一种溶剂
debug_print("📍 步骤7: 添加第一种溶剂... 🧪")
@@ -302,7 +320,7 @@ def generate_recrystallize_protocol(
pump_actions1 = generate_pump_protocol_with_rinsing(
G=G,
from_vessel=solvent1_vessel,
to_vessel=vessel,
to_vessel=vessel_id, # 🔧 使用 vessel_id
volume=volume1, # 使用解析后的体积
amount="",
time=0.0,
@@ -322,12 +340,45 @@ def generate_recrystallize_protocol(
debug_print(f" ❌ 溶剂1泵协议生成失败: {str(e)} 😭")
raise ValueError(f"生成溶剂1泵协议时出错: {str(e)}")
# 🔧 新增:更新容器体积 - 添加溶剂1后
debug_print(" 🔧 更新容器体积 - 添加溶剂1后...")
new_volume_after_solvent1 = original_liquid_volume + volume1
# 更新vessel字典中的体积
if "data" in vessel and "liquid_volume" in vessel["data"]:
current_volume = vessel["data"]["liquid_volume"]
if isinstance(current_volume, list):
if len(current_volume) > 0:
vessel["data"]["liquid_volume"][0] = new_volume_after_solvent1
else:
vessel["data"]["liquid_volume"] = [new_volume_after_solvent1]
else:
vessel["data"]["liquid_volume"] = new_volume_after_solvent1
# 同时更新图中的容器数据
if vessel_id in G.nodes():
if 'data' not in G.nodes[vessel_id]:
G.nodes[vessel_id]['data'] = {}
vessel_node_data = G.nodes[vessel_id]['data']
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
if isinstance(current_node_volume, list):
if len(current_node_volume) > 0:
G.nodes[vessel_id]['data']['liquid_volume'][0] = new_volume_after_solvent1
else:
G.nodes[vessel_id]['data']['liquid_volume'] = [new_volume_after_solvent1]
else:
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume_after_solvent1
debug_print(f" 📊 体积更新: {original_liquid_volume:.2f}mL + {volume1:.2f}mL = {new_volume_after_solvent1:.2f}mL")
# 8. 等待溶剂1稳定
debug_print(" ⏳ 添加溶剂1稳定等待...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 5.0, # 🕐 缩短等待时间10.0s → 5.0s
"time": 5.0, # 缩短等待时间
"description": f"等待溶剂1 {solvent1} 稳定"
}
})
@@ -341,7 +392,7 @@ def generate_recrystallize_protocol(
pump_actions2 = generate_pump_protocol_with_rinsing(
G=G,
from_vessel=solvent2_vessel,
to_vessel=vessel,
to_vessel=vessel_id, # 🔧 使用 vessel_id
volume=volume2, # 使用解析后的体积
amount="",
time=0.0,
@@ -361,12 +412,45 @@ def generate_recrystallize_protocol(
debug_print(f" ❌ 溶剂2泵协议生成失败: {str(e)} 😭")
raise ValueError(f"生成溶剂2泵协议时出错: {str(e)}")
# 🔧 新增:更新容器体积 - 添加溶剂2后
debug_print(" 🔧 更新容器体积 - 添加溶剂2后...")
final_liquid_volume = new_volume_after_solvent1 + volume2
# 更新vessel字典中的体积
if "data" in vessel and "liquid_volume" in vessel["data"]:
current_volume = vessel["data"]["liquid_volume"]
if isinstance(current_volume, list):
if len(current_volume) > 0:
vessel["data"]["liquid_volume"][0] = final_liquid_volume
else:
vessel["data"]["liquid_volume"] = [final_liquid_volume]
else:
vessel["data"]["liquid_volume"] = final_liquid_volume
# 同时更新图中的容器数据
if vessel_id in G.nodes():
if 'data' not in G.nodes[vessel_id]:
G.nodes[vessel_id]['data'] = {}
vessel_node_data = G.nodes[vessel_id]['data']
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
if isinstance(current_node_volume, list):
if len(current_node_volume) > 0:
G.nodes[vessel_id]['data']['liquid_volume'][0] = final_liquid_volume
else:
G.nodes[vessel_id]['data']['liquid_volume'] = [final_liquid_volume]
else:
G.nodes[vessel_id]['data']['liquid_volume'] = final_liquid_volume
debug_print(f" 📊 最终体积: {new_volume_after_solvent1:.2f}mL + {volume2:.2f}mL = {final_liquid_volume:.2f}mL")
# 10. 等待溶剂2稳定
debug_print(" ⏳ 添加溶剂2稳定等待...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 5.0, # 🕐 缩短等待时间10.0s → 5.0s
"time": 5.0, # 缩短等待时间
"description": f"等待溶剂2 {solvent2} 稳定"
}
})
@@ -375,7 +459,7 @@ def generate_recrystallize_protocol(
# 11. 等待重结晶完成
debug_print("📍 步骤9: 等待重结晶完成... 💎")
# 🕐 模拟运行时间优化
# 模拟运行时间优化
debug_print(" ⏱️ 检查模拟运行时间限制...")
original_crystallize_time = 600.0 # 原始重结晶时间
simulation_time_limit = 60.0 # 模拟运行时间限制60秒
@@ -401,12 +485,15 @@ def generate_recrystallize_protocol(
if original_crystallize_time != final_crystallize_time:
debug_print(f" 🎭 模拟优化说明: 原计划 {original_crystallize_time/60:.1f}分钟,实际模拟 {final_crystallize_time/60:.1f}分钟 ⚡")
# 🎊 总结
# 总结
debug_print("💎" * 20)
debug_print(f"🎉 重结晶协议生成完成! ✨")
debug_print(f"📊 总动作数: {len(action_sequence)}")
debug_print(f"🥽 目标容器: {vessel}")
debug_print(f"💧 总体积: {final_volume}mL")
debug_print(f"🥽 目标容器: {vessel_id}")
debug_print(f"💧 总体积变化:")
debug_print(f" - 原始体积: {original_liquid_volume:.2f}mL")
debug_print(f" - 添加溶剂: {final_volume:.2f}mL")
debug_print(f" - 最终体积: {final_liquid_volume:.2f}mL")
debug_print(f"⚖️ 溶剂比例: {solvent1}:{solvent2} = {ratio1}:{ratio2}")
debug_print(f"🧪 溶剂1: {solvent1} ({volume1:.2f}mL)")
debug_print(f"🧪 溶剂2: {solvent2} ({volume2:.2f}mL)")
@@ -421,6 +508,13 @@ def test_recrystallize_protocol():
"""测试重结晶协议"""
debug_print("🧪 === RECRYSTALLIZE PROTOCOL 测试 === ✨")
# 测试体积解析
debug_print("💧 测试体积解析...")
test_volumes = ["100 mL", "2.5 L", "500", "50.5", "?", "invalid"]
for vol in test_volumes:
parsed = parse_volume_with_units(vol)
debug_print(f" 📊 体积 '{vol}' -> {parsed}mL")
# 测试比例解析
debug_print("⚖️ 测试比例解析...")
test_ratios = ["1:1", "3:7", "50:50", "1-1", "2,8", "invalid"]
@@ -430,6 +524,5 @@ def test_recrystallize_protocol():
debug_print("✅ 测试完成 🎉")
if __name__ == "__main__":
test_recrystallize_protocol()