优化了全protocol的运行时间,除了pumptransfer相关的还没

This commit is contained in:
KCFeng425
2025-07-15 10:31:19 +08:00
parent 23eb1139a9
commit ac294194e6
17 changed files with 1800 additions and 2165 deletions

View File

@@ -4,6 +4,11 @@ from typing import List, Dict, Any, Tuple, Union
from .pump_protocol import generate_pump_protocol_with_rinsing
def debug_print(message):
"""调试输出"""
print(f"💎 [RECRYSTALLIZE] {message}", flush=True)
def parse_volume_with_units(volume_input: Union[str, float, int], default_unit: str = "mL") -> float:
"""
解析带单位的体积输入
@@ -16,22 +21,23 @@ def parse_volume_with_units(volume_input: Union[str, float, int], default_unit:
float: 体积(毫升)
"""
if not volume_input:
debug_print("⚠️ 体积输入为空,返回 0.0mL 📦")
return 0.0
# 处理数值输入
if isinstance(volume_input, (int, float)):
result = float(volume_input)
print(f"RECRYSTALLIZE: 数值体积输入: {volume_input}{result}mL默认单位")
debug_print(f"🔢 数值体积输入: {volume_input}{result}mL默认单位💧")
return result
# 处理字符串输入
volume_str = str(volume_input).lower().strip()
print(f"RECRYSTALLIZE: 解析体积字符串: '{volume_str}'")
debug_print(f"🔍 解析体积字符串: '{volume_str}' 📝")
# 处理特殊值
if volume_str in ['?', 'unknown', 'tbd', 'to be determined']:
default_volume = 50.0 # 50mL默认值
print(f"RECRYSTALLIZE: 检测到未知体积,使用默认值: {default_volume}mL")
debug_print(f" 检测到未知体积,使用默认值: {default_volume}mL 🎯")
return default_volume
# 如果是纯数字,使用默认单位
@@ -45,7 +51,7 @@ def parse_volume_with_units(volume_input: Union[str, float, int], default_unit:
result = value / 1000.0
else:
result = value # 默认mL
print(f"RECRYSTALLIZE: 纯数字输入: {volume_str}{result}mL单位: {default_unit}")
debug_print(f"🔢 纯数字输入: {volume_str}{result}mL单位: {default_unit}📏")
return result
except ValueError:
pass
@@ -57,7 +63,7 @@ def parse_volume_with_units(volume_input: Union[str, float, int], default_unit:
match = re.match(r'([0-9]*\.?[0-9]+)\s*(ml|l|μl|ul|microliter|milliliter|liter)?', volume_clean)
if not match:
print(f"RECRYSTALLIZE: ⚠️ 无法解析体积: '{volume_str}',使用默认值: 50mL")
debug_print(f"⚠️ 无法解析体积: '{volume_str}',使用默认值: 50mL 🎯")
return 50.0
value = float(match.group(1))
@@ -66,12 +72,15 @@ def parse_volume_with_units(volume_input: Union[str, float, int], default_unit:
# 转换为毫升
if unit in ['l', 'liter']:
volume = value * 1000.0 # L -> mL
debug_print(f"📏 升转毫升: {value}L → {volume}mL 💧")
elif unit in ['μl', 'ul', 'microliter']:
volume = value / 1000.0 # μL -> mL
debug_print(f"📏 微升转毫升: {value}μL → {volume}mL 💧")
else: # ml, milliliter 或默认
volume = value # 已经是mL
debug_print(f"📏 毫升单位: {value}mL → {volume}mL 💧")
print(f"RECRYSTALLIZE: 体积解析: '{volume_str}'{value} {unit}{volume}mL")
debug_print(f" 体积解析完成: '{volume_str}'{volume}mL")
return volume
@@ -85,6 +94,8 @@ def parse_ratio(ratio_str: str) -> Tuple[float, float]:
Returns:
Tuple[float, float]: 比例元组 (ratio1, ratio2)
"""
debug_print(f"⚖️ 开始解析比例: '{ratio_str}' 📊")
try:
# 处理 "1:1", "3:7", "50:50" 等格式
if ":" in ratio_str:
@@ -92,6 +103,7 @@ def parse_ratio(ratio_str: str) -> Tuple[float, float]:
if len(parts) == 2:
ratio1 = float(parts[0])
ratio2 = float(parts[1])
debug_print(f"✅ 冒号格式解析成功: {ratio1}:{ratio2} 🎯")
return ratio1, ratio2
# 处理 "1-1", "3-7" 等格式
@@ -100,6 +112,7 @@ def parse_ratio(ratio_str: str) -> Tuple[float, float]:
if len(parts) == 2:
ratio1 = float(parts[0])
ratio2 = float(parts[1])
debug_print(f"✅ 横线格式解析成功: {ratio1}:{ratio2} 🎯")
return ratio1, ratio2
# 处理 "1,1", "3,7" 等格式
@@ -108,14 +121,15 @@ def parse_ratio(ratio_str: str) -> Tuple[float, float]:
if len(parts) == 2:
ratio1 = float(parts[0])
ratio2 = float(parts[1])
debug_print(f"✅ 逗号格式解析成功: {ratio1}:{ratio2} 🎯")
return ratio1, ratio2
# 默认 1:1
print(f"RECRYSTALLIZE: 无法解析比例 '{ratio_str}',使用默认比例 1:1")
debug_print(f"⚠️ 无法解析比例 '{ratio_str}',使用默认比例 1:1 🎭")
return 1.0, 1.0
except ValueError:
print(f"RECRYSTALLIZE: 比例解析错误 '{ratio_str}',使用默认比例 1:1")
debug_print(f" 比例解析错误 '{ratio_str}',使用默认比例 1:1 🎭")
return 1.0, 1.0
@@ -130,7 +144,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
Returns:
str: 溶剂容器ID
"""
print(f"RECRYSTALLIZE: 正在查找溶剂 '{solvent}' 的容器...")
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器... 🧪")
# 构建可能的容器名称
possible_names = [
@@ -144,22 +158,27 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
f"vessel_{solvent}",
]
debug_print(f"📋 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个) 📝")
# 第一步:通过容器名称匹配
debug_print(" 🎯 步骤1: 精确名称匹配...")
for vessel_name in possible_names:
if vessel_name in G.nodes():
print(f"RECRYSTALLIZE: 通过名称匹配找到容器: {vessel_name}")
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name}")
return vessel_name
# 第二步:通过模糊匹配
debug_print(" 🔍 步骤2: 模糊名称匹配...")
for node_id in G.nodes():
if G.nodes[node_id].get('type') == 'container':
node_name = G.nodes[node_id].get('name', '').lower()
if solvent.lower() in node_id.lower() or solvent.lower() in node_name:
print(f"RECRYSTALLIZE: 通过模糊匹配找到容器: {node_id}")
debug_print(f" 🎉 通过模糊匹配找到容器: {node_id}")
return node_id
# 第三步:通过液体类型匹配
debug_print(" 🧪 步骤3: 液体类型匹配...")
for node_id in G.nodes():
if G.nodes[node_id].get('type') == 'container':
vessel_data = G.nodes[node_id].get('data', {})
@@ -171,9 +190,10 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
reagent_name = vessel_data.get('reagent_name', '').lower()
if solvent.lower() in liquid_type or solvent.lower() in reagent_name:
print(f"RECRYSTALLIZE: 通过液体类型匹配找到容器: {node_id}")
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id}")
return node_id
debug_print(f"❌ 找不到溶剂 '{solvent}' 对应的容器 😭")
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器")
@@ -203,61 +223,80 @@ def generate_recrystallize_protocol(
"""
action_sequence = []
print(f"RECRYSTALLIZE: 开始生成重结晶协议(支持单位)")
print(f" - 比例: {ratio}")
print(f" - 溶剂1: {solvent1}")
print(f" - 溶剂2: {solvent2}")
print(f" - 容器: {vessel}")
print(f" - 总体积: {volume} (类型: {type(volume)})")
debug_print("💎" * 20)
debug_print("🚀 开始生成重结晶协议(支持单位)✨")
debug_print(f"📝 输入参数:")
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)
# 1. 验证目标容器存在
debug_print("📍 步骤1: 验证目标容器... 🔧")
if vessel not in G.nodes():
debug_print(f"❌ 目标容器 '{vessel}' 不存在于系统中! 😱")
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
debug_print(f"✅ 目标容器 '{vessel}' 验证通过 🎯")
# 2. 🔧 新增:解析体积(支持单位)
debug_print("📍 步骤2: 解析体积(支持单位)... 💧")
final_volume = parse_volume_with_units(volume, "mL")
print(f"RECRYSTALLIZE: 解析体积: {volume}{final_volume}mL")
debug_print(f"🎯 体积解析完成: {volume}{final_volume}mL")
# 3. 解析比例
debug_print("📍 步骤3: 解析比例... ⚖️")
ratio1, ratio2 = parse_ratio(ratio)
total_ratio = ratio1 + ratio2
debug_print(f"🎯 比例解析完成: {ratio1}:{ratio2} (总比例: {total_ratio}) ✨")
# 4. 计算各溶剂体积
debug_print("📍 步骤4: 计算各溶剂体积... 🧮")
volume1 = final_volume * (ratio1 / total_ratio)
volume2 = final_volume * (ratio2 / total_ratio)
print(f"RECRYSTALLIZE: 解析比例: {ratio1}:{ratio2}")
print(f"RECRYSTALLIZE: {solvent1} 体积: {volume1:.2f} mL")
print(f"RECRYSTALLIZE: {solvent2} 体积: {volume2:.2f} mL")
debug_print(f"🧪 {solvent1} 体积: {volume1:.2f} mL ({ratio1}/{total_ratio} × {final_volume})")
debug_print(f"🧪 {solvent2} 体积: {volume2:.2f} mL ({ratio2}/{total_ratio} × {final_volume})")
debug_print(f"✅ 体积计算完成: 总计 {volume1 + volume2:.2f} mL 🎯")
# 5. 查找溶剂容器
debug_print("📍 步骤5: 查找溶剂容器... 🔍")
try:
debug_print(f" 🔍 查找溶剂1容器...")
solvent1_vessel = find_solvent_vessel(G, solvent1)
print(f"RECRYSTALLIZE: 找到溶剂1容器: {solvent1_vessel}")
debug_print(f" 🎉 找到溶剂1容器: {solvent1_vessel}")
except ValueError as e:
debug_print(f" ❌ 溶剂1容器查找失败: {str(e)} 😭")
raise ValueError(f"无法找到溶剂1 '{solvent1}': {str(e)}")
try:
debug_print(f" 🔍 查找溶剂2容器...")
solvent2_vessel = find_solvent_vessel(G, solvent2)
print(f"RECRYSTALLIZE: 找到溶剂2容器: {solvent2_vessel}")
debug_print(f" 🎉 找到溶剂2容器: {solvent2_vessel}")
except ValueError as e:
debug_print(f" ❌ 溶剂2容器查找失败: {str(e)} 😭")
raise ValueError(f"无法找到溶剂2 '{solvent2}': {str(e)}")
# 6. 验证路径存在
debug_print("📍 步骤6: 验证传输路径... 🛤️")
try:
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel)
print(f"RECRYSTALLIZE: 溶剂1路径: {''.join(path1)}")
debug_print(f" 🛤️ 溶剂1路径: {''.join(path1)}")
except nx.NetworkXNoPath:
debug_print(f" ❌ 溶剂1路径不可达: {solvent1_vessel}{vessel} 😞")
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel}' 没有可用路径")
try:
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel)
print(f"RECRYSTALLIZE: 溶剂2路径: {''.join(path2)}")
debug_print(f" 🛤️ 溶剂2路径: {''.join(path2)}")
except nx.NetworkXNoPath:
debug_print(f" ❌ 溶剂2路径不可达: {solvent2_vessel}{vessel} 😞")
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel}' 没有可用路径")
# 7. 添加第一种溶剂
print(f"RECRYSTALLIZE: 开始添加溶剂1 {volume1:.2f} mL")
debug_print("📍 步骤7: 添加第一种溶剂... 🧪")
debug_print(f" 🚰 开始添加溶剂1: {solvent1} ({volume1:.2f} mL)")
try:
pump_actions1 = generate_pump_protocol_with_rinsing(
@@ -277,21 +316,26 @@ def generate_recrystallize_protocol(
)
action_sequence.extend(pump_actions1)
debug_print(f" ✅ 溶剂1泵送动作已添加: {len(pump_actions1)} 个动作 🚰✨")
except Exception as e:
debug_print(f" ❌ 溶剂1泵协议生成失败: {str(e)} 😭")
raise ValueError(f"生成溶剂1泵协议时出错: {str(e)}")
# 8. 等待溶剂1稳定
debug_print(" ⏳ 添加溶剂1稳定等待...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 10.0,
"time": 5.0, # 🕐 缩短等待时间10.0s → 5.0s
"description": f"等待溶剂1 {solvent1} 稳定"
}
})
debug_print(" ✅ 溶剂1稳定等待已添加 ⏰✨")
# 9. 添加第二种溶剂
print(f"RECRYSTALLIZE: 开始添加溶剂2 {volume2:.2f} mL")
debug_print("📍 步骤8: 添加第二种溶剂... 🧪")
debug_print(f" 🚰 开始添加溶剂2: {solvent2} ({volume2:.2f} mL)")
try:
pump_actions2 = generate_pump_protocol_with_rinsing(
@@ -311,31 +355,63 @@ def generate_recrystallize_protocol(
)
action_sequence.extend(pump_actions2)
debug_print(f" ✅ 溶剂2泵送动作已添加: {len(pump_actions2)} 个动作 🚰✨")
except Exception as e:
debug_print(f" ❌ 溶剂2泵协议生成失败: {str(e)} 😭")
raise ValueError(f"生成溶剂2泵协议时出错: {str(e)}")
# 10. 等待溶剂2稳定
debug_print(" ⏳ 添加溶剂2稳定等待...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 10.0,
"time": 5.0, # 🕐 缩短等待时间10.0s → 5.0s
"description": f"等待溶剂2 {solvent2} 稳定"
}
})
debug_print(" ✅ 溶剂2稳定等待已添加 ⏰✨")
# 11. 等待重结晶完成
debug_print("📍 步骤9: 等待重结晶完成... 💎")
# 🕐 模拟运行时间优化
debug_print(" ⏱️ 检查模拟运行时间限制...")
original_crystallize_time = 600.0 # 原始重结晶时间
simulation_time_limit = 60.0 # 模拟运行时间限制60秒
final_crystallize_time = min(original_crystallize_time, simulation_time_limit)
if original_crystallize_time > simulation_time_limit:
debug_print(f" 🎮 模拟运行优化: {original_crystallize_time}s → {final_crystallize_time}s ⚡")
debug_print(f" 📊 时间缩短: {original_crystallize_time/60:.1f}分钟 → {final_crystallize_time/60:.1f}分钟 🚀")
else:
debug_print(f" ✅ 时间在限制内: {final_crystallize_time}s 保持不变 🎯")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 600.0, # 等待10分钟进行重结晶
"description": f"等待重结晶完成({solvent1}:{solvent2} = {ratio},总体积 {final_volume}mL"
"time": final_crystallize_time,
"description": f"等待重结晶完成({solvent1}:{solvent2} = {ratio},总体积 {final_volume}mL" + (f" (模拟时间)" if original_crystallize_time != final_crystallize_time else "")
}
})
debug_print(f" ✅ 重结晶等待已添加: {final_crystallize_time}s 💎✨")
print(f"RECRYSTALLIZE: 协议生成完成,共 {len(action_sequence)} 个动作")
print(f"RECRYSTALLIZE: 预计总时间: {620/60:.1f} 分钟")
print(f"RECRYSTALLIZE: 总体积: {final_volume}mL")
# 显示时间调整信息
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"⚖️ 溶剂比例: {solvent1}:{solvent2} = {ratio1}:{ratio2}")
debug_print(f"🧪 溶剂1: {solvent1} ({volume1:.2f}mL)")
debug_print(f"🧪 溶剂2: {solvent2} ({volume2:.2f}mL)")
debug_print(f"⏱️ 预计总时间: {(final_crystallize_time + 10)/60:.1f} 分钟 ⌛")
debug_print("💎" * 20)
return action_sequence
@@ -343,15 +419,16 @@ def generate_recrystallize_protocol(
# 测试函数
def test_recrystallize_protocol():
"""测试重结晶协议"""
print("=== RECRYSTALLIZE PROTOCOL 测试 ===")
debug_print("🧪 === RECRYSTALLIZE PROTOCOL 测试 ===")
# 测试比例解析
debug_print("⚖️ 测试比例解析...")
test_ratios = ["1:1", "3:7", "50:50", "1-1", "2,8", "invalid"]
for ratio in test_ratios:
r1, r2 = parse_ratio(ratio)
print(f"比例 '{ratio}' -> {r1}:{r2}")
debug_print(f" 📊 比例 '{ratio}' -> {r1}:{r2}")
print("测试完成")
debug_print("测试完成 🎉")
if __name__ == "__main__":