mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-03-26 17:43:08 +00:00
refactor: 14个协议编译器去重精简,删除死代码
- 统一debug_print为共享import,移除14个本地定义 - 移除重复工具函数(find_connected_stirrer, get_vessel_liquid_volume等) - 精简装饰性日志(emoji分隔线、进度提示),保留关键决策点 - 删除evacuateandrefill_protocol_old.py死代码 - 涉及文件:add, adjustph, clean_vessel, dissolve, dry, evacuateandrefill, evaporate, filter, pump, recrystallize, reset_handling, run_column, stir, wash_solid Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,20 +2,13 @@ from functools import partial
|
|||||||
|
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import re
|
import re
|
||||||
import logging
|
|
||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
|
|
||||||
from .utils.unit_parser import parse_volume_input, parse_mass_input, parse_time_input
|
from .utils.unit_parser import parse_volume_input, parse_mass_input, parse_time_input
|
||||||
from .utils.vessel_parser import get_vessel, find_solid_dispenser, find_connected_stirrer, find_reagent_vessel
|
from .utils.vessel_parser import get_vessel, find_solid_dispenser, find_connected_stirrer, find_reagent_vessel
|
||||||
from .utils.logger_util import action_log
|
from .utils.logger_util import action_log, debug_print
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[ADD] {message}")
|
|
||||||
|
|
||||||
|
|
||||||
# 🆕 创建进度日志动作
|
# 🆕 创建进度日志动作
|
||||||
create_action_log = partial(action_log, prefix="[ADD]")
|
create_action_log = partial(action_log, prefix="[ADD]")
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
|
from functools import partial
|
||||||
|
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
|
||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
from .utils.vessel_parser import get_vessel
|
from .utils.vessel_parser import get_vessel, find_connected_stirrer
|
||||||
|
from .utils.logger_util import action_log, debug_print
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
create_action_log = partial(action_log, prefix="[ADJUST_PH]")
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[ADJUST_PH] {message}")
|
|
||||||
|
|
||||||
def find_acid_base_vessel(G: nx.DiGraph, reagent: str) -> str:
|
def find_acid_base_vessel(G: nx.DiGraph, reagent: str) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -100,52 +98,11 @@ def find_acid_base_vessel(G: nx.DiGraph, reagent: str) -> str:
|
|||||||
return node_id
|
return node_id
|
||||||
|
|
||||||
# 列出可用容器帮助调试
|
# 列出可用容器帮助调试
|
||||||
debug_print(f"📊 列出可用容器帮助调试...")
|
available_containers = [node_id for node_id in G.nodes()
|
||||||
available_containers = []
|
if G.nodes[node_id].get('type') == 'container']
|
||||||
for node_id in G.nodes():
|
debug_print(f"所有匹配方法失败,可用容器: {available_containers}")
|
||||||
if G.nodes[node_id].get('type') == 'container':
|
|
||||||
vessel_data = G.nodes[node_id].get('data', {})
|
|
||||||
liquids = vessel_data.get('liquid', [])
|
|
||||||
liquid_types = [liquid.get('liquid_type', '') or liquid.get('name', '')
|
|
||||||
for liquid in liquids if isinstance(liquid, dict)]
|
|
||||||
|
|
||||||
available_containers.append({
|
|
||||||
'id': node_id,
|
|
||||||
'name': G.nodes[node_id].get('name', ''),
|
|
||||||
'liquids': liquid_types,
|
|
||||||
'reagent_name': vessel_data.get('reagent_name', '')
|
|
||||||
})
|
|
||||||
|
|
||||||
debug_print(f"📋 可用容器列表:")
|
|
||||||
for container in available_containers:
|
|
||||||
debug_print(f" - 🧪 {container['id']}: {container['name']}")
|
|
||||||
debug_print(f" 💧 液体: {container['liquids']}")
|
|
||||||
debug_print(f" 🏷️ 试剂: {container['reagent_name']}")
|
|
||||||
|
|
||||||
debug_print(f"❌ 所有匹配方法都失败了")
|
|
||||||
raise ValueError(f"找不到试剂 '{reagent}' 对应的容器。尝试了: {possible_names[:10]}...")
|
raise ValueError(f"找不到试剂 '{reagent}' 对应的容器。尝试了: {possible_names[:10]}...")
|
||||||
|
|
||||||
def find_connected_stirrer(G: nx.DiGraph, vessel: str) -> str:
|
|
||||||
"""查找与容器相连的搅拌器"""
|
|
||||||
debug_print(f"🔍 查找连接到容器 '{vessel}' 的搅拌器...")
|
|
||||||
|
|
||||||
stirrer_nodes = [node for node in G.nodes()
|
|
||||||
if (G.nodes[node].get('class') or '') == 'virtual_stirrer']
|
|
||||||
|
|
||||||
debug_print(f"📊 发现 {len(stirrer_nodes)} 个搅拌器: {stirrer_nodes}")
|
|
||||||
|
|
||||||
for stirrer in stirrer_nodes:
|
|
||||||
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
|
|
||||||
debug_print(f"✅ 找到连接的搅拌器: {stirrer} 🔗")
|
|
||||||
return stirrer
|
|
||||||
|
|
||||||
if stirrer_nodes:
|
|
||||||
debug_print(f"⚠️ 未找到直接连接的搅拌器,使用第一个: {stirrer_nodes[0]} 🔄")
|
|
||||||
return stirrer_nodes[0]
|
|
||||||
|
|
||||||
debug_print(f"❌ 未找到任何搅拌器")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def calculate_reagent_volume(target_ph_value: float, reagent: str, vessel_volume: float = 100.0) -> float:
|
def calculate_reagent_volume(target_ph_value: float, reagent: str, vessel_volume: float = 100.0) -> float:
|
||||||
"""
|
"""
|
||||||
估算需要的试剂体积来调节pH
|
估算需要的试剂体积来调节pH
|
||||||
@@ -158,44 +115,30 @@ def calculate_reagent_volume(target_ph_value: float, reagent: str, vessel_volume
|
|||||||
Returns:
|
Returns:
|
||||||
float: 估算的试剂体积 (mL)
|
float: 估算的试剂体积 (mL)
|
||||||
"""
|
"""
|
||||||
debug_print(f"🧮 计算试剂体积...")
|
debug_print(f"计算试剂体积: pH={target_ph_value}, reagent={reagent}, vessel={vessel_volume}mL")
|
||||||
debug_print(f" 📍 目标pH: {target_ph_value}")
|
|
||||||
debug_print(f" 🧪 试剂: {reagent}")
|
|
||||||
debug_print(f" 📏 容器体积: {vessel_volume}mL")
|
|
||||||
|
|
||||||
# 简化的pH调节体积估算(实际应用中需要更精确的计算)
|
# 简化的pH调节体积估算
|
||||||
if "acid" in reagent.lower() or "hcl" in reagent.lower():
|
if "acid" in reagent.lower() or "hcl" in reagent.lower():
|
||||||
debug_print(f"🍋 检测到酸性试剂")
|
|
||||||
# 酸性试剂:pH越低需要的体积越大
|
|
||||||
if target_ph_value < 3:
|
if target_ph_value < 3:
|
||||||
volume = vessel_volume * 0.05 # 5%
|
volume = vessel_volume * 0.05
|
||||||
debug_print(f" 💪 强酸性 (pH<3): 使用 5% 体积")
|
|
||||||
elif target_ph_value < 5:
|
elif target_ph_value < 5:
|
||||||
volume = vessel_volume * 0.02 # 2%
|
volume = vessel_volume * 0.02
|
||||||
debug_print(f" 🔸 中酸性 (pH<5): 使用 2% 体积")
|
|
||||||
else:
|
else:
|
||||||
volume = vessel_volume * 0.01 # 1%
|
volume = vessel_volume * 0.01
|
||||||
debug_print(f" 🔹 弱酸性 (pH≥5): 使用 1% 体积")
|
|
||||||
|
|
||||||
elif "hydroxide" in reagent.lower() or "naoh" in reagent.lower():
|
elif "hydroxide" in reagent.lower() or "naoh" in reagent.lower():
|
||||||
debug_print(f"🧂 检测到碱性试剂")
|
|
||||||
# 碱性试剂:pH越高需要的体积越大
|
|
||||||
if target_ph_value > 11:
|
if target_ph_value > 11:
|
||||||
volume = vessel_volume * 0.05 # 5%
|
volume = vessel_volume * 0.05
|
||||||
debug_print(f" 💪 强碱性 (pH>11): 使用 5% 体积")
|
|
||||||
elif target_ph_value > 9:
|
elif target_ph_value > 9:
|
||||||
volume = vessel_volume * 0.02 # 2%
|
volume = vessel_volume * 0.02
|
||||||
debug_print(f" 🔸 中碱性 (pH>9): 使用 2% 体积")
|
|
||||||
else:
|
else:
|
||||||
volume = vessel_volume * 0.01 # 1%
|
volume = vessel_volume * 0.01
|
||||||
debug_print(f" 🔹 弱碱性 (pH≤9): 使用 1% 体积")
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 未知试剂,使用默认值
|
# 未知试剂,使用默认值
|
||||||
volume = vessel_volume * 0.01
|
volume = vessel_volume * 0.01
|
||||||
debug_print(f"❓ 未知试剂类型,使用默认 1% 体积")
|
|
||||||
|
|
||||||
debug_print(f"📊 计算结果: {volume:.2f}mL")
|
debug_print(f"估算试剂体积: {volume:.2f}mL")
|
||||||
return volume
|
return volume
|
||||||
|
|
||||||
def generate_adjust_ph_protocol(
|
def generate_adjust_ph_protocol(
|
||||||
@@ -222,33 +165,18 @@ def generate_adjust_ph_protocol(
|
|||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
if not vessel_id:
|
if not vessel_id:
|
||||||
debug_print(f"❌ vessel 参数无效,必须包含id字段或直接提供容器ID. vessel: {vessel}")
|
|
||||||
raise ValueError("vessel 参数无效,必须包含id字段或直接提供容器ID")
|
raise ValueError("vessel 参数无效,必须包含id字段或直接提供容器ID")
|
||||||
|
|
||||||
debug_print("=" * 60)
|
debug_print(f"开始生成pH调节协议: vessel={vessel_id}, ph={ph_value}, reagent='{reagent}'")
|
||||||
debug_print("🧪 开始生成pH调节协议")
|
|
||||||
debug_print(f"📋 原始参数:")
|
|
||||||
debug_print(f" 🥼 vessel: {vessel} (ID: {vessel_id})")
|
|
||||||
debug_print(f" 📊 ph_value: {ph_value}")
|
|
||||||
debug_print(f" 🧪 reagent: '{reagent}'")
|
|
||||||
debug_print(f" 📦 kwargs: {kwargs}")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 从kwargs中获取可选参数,如果没有则使用默认值
|
# 从kwargs中获取可选参数
|
||||||
volume = kwargs.get('volume', 0.0) # 自动估算体积
|
volume = kwargs.get('volume', 0.0)
|
||||||
stir = kwargs.get('stir', True) # 默认搅拌
|
stir = kwargs.get('stir', True)
|
||||||
stir_speed = kwargs.get('stir_speed', 300.0) # 默认搅拌速度
|
stir_speed = kwargs.get('stir_speed', 300.0)
|
||||||
stir_time = kwargs.get('stir_time', 60.0) # 默认搅拌时间
|
stir_time = kwargs.get('stir_time', 60.0)
|
||||||
settling_time = kwargs.get('settling_time', 30.0) # 默认平衡时间
|
settling_time = kwargs.get('settling_time', 30.0)
|
||||||
|
|
||||||
debug_print(f"🔧 处理后的参数:")
|
|
||||||
debug_print(f" 📏 volume: {volume}mL (0.0表示自动估算)")
|
|
||||||
debug_print(f" 🌪️ stir: {stir}")
|
|
||||||
debug_print(f" 🔄 stir_speed: {stir_speed}rpm")
|
|
||||||
debug_print(f" ⏱️ stir_time: {stir_time}s")
|
|
||||||
debug_print(f" ⏳ settling_time: {settling_time}s")
|
|
||||||
|
|
||||||
# 开始处理
|
# 开始处理
|
||||||
action_sequence.append(create_action_log(f"开始调节pH至 {ph_value}", "🧪"))
|
action_sequence.append(create_action_log(f"开始调节pH至 {ph_value}", "🧪"))
|
||||||
@@ -256,60 +184,46 @@ def generate_adjust_ph_protocol(
|
|||||||
action_sequence.append(create_action_log(f"使用试剂: {reagent}", "⚗️"))
|
action_sequence.append(create_action_log(f"使用试剂: {reagent}", "⚗️"))
|
||||||
|
|
||||||
# 1. 验证目标容器存在
|
# 1. 验证目标容器存在
|
||||||
debug_print(f"🔍 步骤1: 验证目标容器...")
|
|
||||||
if vessel_id not in G.nodes():
|
if vessel_id not in G.nodes():
|
||||||
debug_print(f"❌ 目标容器 '{vessel_id}' 不存在于系统中")
|
|
||||||
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
||||||
|
|
||||||
debug_print(f"✅ 目标容器验证通过")
|
|
||||||
action_sequence.append(create_action_log("目标容器验证通过", "✅"))
|
action_sequence.append(create_action_log("目标容器验证通过", "✅"))
|
||||||
|
|
||||||
# 2. 查找酸碱试剂容器
|
# 2. 查找酸碱试剂容器
|
||||||
debug_print(f"🔍 步骤2: 查找试剂容器...")
|
|
||||||
action_sequence.append(create_action_log("正在查找试剂容器...", "🔍"))
|
action_sequence.append(create_action_log("正在查找试剂容器...", "🔍"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reagent_vessel = find_acid_base_vessel(G, reagent)
|
reagent_vessel = find_acid_base_vessel(G, reagent)
|
||||||
debug_print(f"✅ 找到试剂容器: {reagent_vessel}")
|
|
||||||
action_sequence.append(create_action_log(f"找到试剂容器: {reagent_vessel}", "🧪"))
|
action_sequence.append(create_action_log(f"找到试剂容器: {reagent_vessel}", "🧪"))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
debug_print(f"❌ 无法找到试剂容器: {str(e)}")
|
|
||||||
action_sequence.append(create_action_log(f"试剂容器查找失败: {str(e)}", "❌"))
|
action_sequence.append(create_action_log(f"试剂容器查找失败: {str(e)}", "❌"))
|
||||||
raise ValueError(f"无法找到试剂 '{reagent}': {str(e)}")
|
raise ValueError(f"无法找到试剂 '{reagent}': {str(e)}")
|
||||||
|
|
||||||
# 3. 体积估算
|
# 3. 体积估算
|
||||||
debug_print(f"🔍 步骤3: 体积处理...")
|
|
||||||
if volume <= 0:
|
if volume <= 0:
|
||||||
action_sequence.append(create_action_log("开始自动估算试剂体积", "🧮"))
|
action_sequence.append(create_action_log("开始自动估算试剂体积", "🧮"))
|
||||||
|
|
||||||
# 获取目标容器的体积信息
|
# 获取目标容器的体积信息
|
||||||
vessel_data = G.nodes[vessel_id].get('data', {})
|
vessel_data = G.nodes[vessel_id].get('data', {})
|
||||||
vessel_volume = vessel_data.get('max_volume', 100.0) # 默认100mL
|
vessel_volume = vessel_data.get('max_volume', 100.0)
|
||||||
debug_print(f"📏 容器最大体积: {vessel_volume}mL")
|
|
||||||
|
|
||||||
estimated_volume = calculate_reagent_volume(ph_value, reagent, vessel_volume)
|
estimated_volume = calculate_reagent_volume(ph_value, reagent, vessel_volume)
|
||||||
volume = estimated_volume
|
volume = estimated_volume
|
||||||
debug_print(f"✅ 自动估算试剂体积: {volume:.2f} mL")
|
|
||||||
action_sequence.append(create_action_log(f"估算试剂体积: {volume:.2f}mL", "📊"))
|
action_sequence.append(create_action_log(f"估算试剂体积: {volume:.2f}mL", "📊"))
|
||||||
else:
|
else:
|
||||||
debug_print(f"📏 使用指定体积: {volume}mL")
|
|
||||||
action_sequence.append(create_action_log(f"使用指定体积: {volume}mL", "📏"))
|
action_sequence.append(create_action_log(f"使用指定体积: {volume}mL", "📏"))
|
||||||
|
|
||||||
# 4. 验证路径存在
|
# 4. 验证路径存在
|
||||||
debug_print(f"🔍 步骤4: 路径验证...")
|
|
||||||
action_sequence.append(create_action_log("验证转移路径...", "🛤️"))
|
action_sequence.append(create_action_log("验证转移路径...", "🛤️"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path = nx.shortest_path(G, source=reagent_vessel, target=vessel_id)
|
path = nx.shortest_path(G, source=reagent_vessel, target=vessel_id)
|
||||||
debug_print(f"✅ 找到路径: {' → '.join(path)}")
|
action_sequence.append(create_action_log(f"找到转移路径: {' -> '.join(path)}", "🛤️"))
|
||||||
action_sequence.append(create_action_log(f"找到转移路径: {' → '.join(path)}", "🛤️"))
|
|
||||||
except nx.NetworkXNoPath:
|
except nx.NetworkXNoPath:
|
||||||
debug_print(f"❌ 无法找到转移路径")
|
|
||||||
action_sequence.append(create_action_log("转移路径不存在", "❌"))
|
action_sequence.append(create_action_log("转移路径不存在", "❌"))
|
||||||
raise ValueError(f"从试剂容器 '{reagent_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
raise ValueError(f"从试剂容器 '{reagent_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
||||||
|
|
||||||
# 5. 搅拌器设置
|
# 5. 搅拌器设置
|
||||||
debug_print(f"🔍 步骤5: 搅拌器设置...")
|
|
||||||
stirrer_id = None
|
stirrer_id = None
|
||||||
if stir:
|
if stir:
|
||||||
action_sequence.append(create_action_log("准备启动搅拌器", "🌪️"))
|
action_sequence.append(create_action_log("准备启动搅拌器", "🌪️"))
|
||||||
@@ -318,7 +232,6 @@ def generate_adjust_ph_protocol(
|
|||||||
stirrer_id = find_connected_stirrer(G, vessel_id)
|
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||||
|
|
||||||
if stirrer_id:
|
if stirrer_id:
|
||||||
debug_print(f"✅ 找到搅拌器 {stirrer_id},启动搅拌")
|
|
||||||
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {stir_speed}rpm)", "🔄"))
|
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {stir_speed}rpm)", "🔄"))
|
||||||
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
@@ -338,23 +251,18 @@ def generate_adjust_ph_protocol(
|
|||||||
"action_kwargs": {"time": 5}
|
"action_kwargs": {"time": 5}
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
debug_print(f"⚠️ 未找到搅拌器,继续执行")
|
|
||||||
action_sequence.append(create_action_log("未找到搅拌器,跳过搅拌", "⚠️"))
|
action_sequence.append(create_action_log("未找到搅拌器,跳过搅拌", "⚠️"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 搅拌器配置出错: {str(e)}")
|
|
||||||
action_sequence.append(create_action_log(f"搅拌器配置失败: {str(e)}", "❌"))
|
action_sequence.append(create_action_log(f"搅拌器配置失败: {str(e)}", "❌"))
|
||||||
else:
|
else:
|
||||||
debug_print(f"📋 跳过搅拌设置")
|
|
||||||
action_sequence.append(create_action_log("跳过搅拌设置", "⏭️"))
|
action_sequence.append(create_action_log("跳过搅拌设置", "⏭️"))
|
||||||
|
|
||||||
# 6. 试剂添加
|
# 6. 试剂添加
|
||||||
debug_print(f"🔍 步骤6: 试剂添加...")
|
|
||||||
action_sequence.append(create_action_log(f"开始添加试剂 {volume:.2f}mL", "🚰"))
|
action_sequence.append(create_action_log(f"开始添加试剂 {volume:.2f}mL", "🚰"))
|
||||||
|
|
||||||
# 计算添加时间(pH调节需要缓慢添加)
|
# 计算添加时间(pH调节需要缓慢添加)
|
||||||
addition_time = max(30.0, volume * 2.0) # 至少30秒,每mL需要2秒
|
addition_time = max(30.0, volume * 2.0)
|
||||||
debug_print(f"⏱️ 计算添加时间: {addition_time}s (缓慢注入)")
|
|
||||||
action_sequence.append(create_action_log(f"设置添加时间: {addition_time:.0f}s (缓慢注入)", "⏱️"))
|
action_sequence.append(create_action_log(f"设置添加时间: {addition_time:.0f}s (缓慢注入)", "⏱️"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -377,11 +285,9 @@ def generate_adjust_ph_protocol(
|
|||||||
)
|
)
|
||||||
|
|
||||||
action_sequence.extend(pump_actions)
|
action_sequence.extend(pump_actions)
|
||||||
debug_print(f"✅ 泵协议生成完成,添加了 {len(pump_actions)} 个动作")
|
|
||||||
action_sequence.append(create_action_log(f"试剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
action_sequence.append(create_action_log(f"试剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
||||||
|
|
||||||
# 🔧 修复体积运算 - 试剂添加成功后更新容器液体体积
|
# 体积运算 - 试剂添加成功后更新容器液体体积
|
||||||
debug_print(f"🔧 更新容器液体体积...")
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
debug_print(f"📊 添加前容器体积: {current_volume}")
|
debug_print(f"📊 添加前容器体积: {current_volume}")
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
from .utils.vessel_parser import get_vessel, find_solvent_vessel
|
from .utils.vessel_parser import get_vessel, find_solvent_vessel, find_connected_heatchill
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
from .pump_protocol import generate_pump_protocol
|
from .pump_protocol import generate_pump_protocol
|
||||||
|
from .utils.resource_helper import get_resource_liquid_volume
|
||||||
|
|
||||||
|
|
||||||
def find_solvent_vessel_by_any_match(G: nx.DiGraph, solvent: str) -> str:
|
def find_solvent_vessel_by_any_match(G: nx.DiGraph, solvent: str) -> str:
|
||||||
@@ -31,29 +33,9 @@ def find_waste_vessel(G: nx.DiGraph) -> str:
|
|||||||
raise ValueError(f"未找到废液容器。尝试了以下名称: {possible_waste_names}")
|
raise ValueError(f"未找到废液容器。尝试了以下名称: {possible_waste_names}")
|
||||||
|
|
||||||
|
|
||||||
def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
|
|
||||||
"""
|
|
||||||
查找与指定容器相连的加热冷却设备
|
|
||||||
"""
|
|
||||||
# 查找所有加热冷却设备节点
|
|
||||||
heatchill_nodes = [node for node in G.nodes()
|
|
||||||
if (G.nodes[node].get('class') or '') == 'virtual_heatchill']
|
|
||||||
|
|
||||||
# 检查哪个加热设备与目标容器相连(机械连接)
|
|
||||||
for heatchill in heatchill_nodes:
|
|
||||||
if G.has_edge(heatchill, vessel) or G.has_edge(vessel, heatchill):
|
|
||||||
return heatchill
|
|
||||||
|
|
||||||
# 如果没有直接连接,返回第一个可用的加热设备
|
|
||||||
if heatchill_nodes:
|
|
||||||
return heatchill_nodes[0]
|
|
||||||
|
|
||||||
return None # 没有加热设备也可以工作,只是不能加热
|
|
||||||
|
|
||||||
|
|
||||||
def generate_clean_vessel_protocol(
|
def generate_clean_vessel_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
solvent: str,
|
solvent: str,
|
||||||
volume: float,
|
volume: float,
|
||||||
temp: float,
|
temp: float,
|
||||||
@@ -81,24 +63,12 @@ def generate_clean_vessel_protocol(
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[Dict[str, Any]]: 容器清洗操作的动作序列
|
List[Dict[str, Any]]: 容器清洗操作的动作序列
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: 当找不到必要的容器或设备时抛出异常
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
clean_protocol = generate_clean_vessel_protocol(G, {"id": "main_reactor"}, "water", 100.0, 60.0, 2)
|
|
||||||
"""
|
"""
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
print(f"CLEAN_VESSEL: 开始生成容器清洗协议")
|
debug_print(f"开始生成容器清洗协议: vessel={vessel_id}, solvent={solvent}, volume={volume}mL, temp={temp}°C, repeats={repeats}")
|
||||||
print(f" - 目标容器: {vessel} (ID: {vessel_id})")
|
|
||||||
print(f" - 清洗溶剂: {solvent}")
|
|
||||||
print(f" - 清洗体积: {volume} mL")
|
|
||||||
print(f" - 清洗温度: {temp}°C")
|
|
||||||
print(f" - 重复次数: {repeats}")
|
|
||||||
|
|
||||||
# 验证目标容器存在
|
# 验证目标容器存在
|
||||||
if vessel_id not in G.nodes():
|
if vessel_id not in G.nodes():
|
||||||
@@ -107,26 +77,25 @@ def generate_clean_vessel_protocol(
|
|||||||
# 查找溶剂容器
|
# 查找溶剂容器
|
||||||
try:
|
try:
|
||||||
solvent_vessel = find_solvent_vessel(G, solvent)
|
solvent_vessel = find_solvent_vessel(G, solvent)
|
||||||
print(f"CLEAN_VESSEL: 找到溶剂容器: {solvent_vessel}")
|
debug_print(f"找到溶剂容器: {solvent_vessel}")
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ValueError(f"无法找到溶剂容器: {str(e)}")
|
raise ValueError(f"无法找到溶剂容器: {str(e)}")
|
||||||
|
|
||||||
# 查找废液容器
|
# 查找废液容器
|
||||||
try:
|
try:
|
||||||
waste_vessel = find_waste_vessel(G)
|
waste_vessel = find_waste_vessel(G)
|
||||||
print(f"CLEAN_VESSEL: 找到废液容器: {waste_vessel}")
|
debug_print(f"找到废液容器: {waste_vessel}")
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise ValueError(f"无法找到废液容器: {str(e)}")
|
raise ValueError(f"无法找到废液容器: {str(e)}")
|
||||||
|
|
||||||
# 查找加热设备(可选)
|
# 查找加热设备(可选)
|
||||||
heatchill_id = find_connected_heatchill(G, vessel_id) # 🔧 使用 vessel_id
|
heatchill_id = find_connected_heatchill(G, vessel_id)
|
||||||
if heatchill_id:
|
if heatchill_id:
|
||||||
print(f"CLEAN_VESSEL: 找到加热设备: {heatchill_id}")
|
debug_print(f"找到加热设备: {heatchill_id}")
|
||||||
else:
|
else:
|
||||||
print(f"CLEAN_VESSEL: 未找到加热设备,将在室温下清洗")
|
debug_print(f"未找到加热设备,将在室温下清洗")
|
||||||
|
|
||||||
# 🔧 新增:记录清洗前的容器状态
|
# 记录清洗前的容器状态
|
||||||
print(f"CLEAN_VESSEL: 记录清洗前容器状态...")
|
|
||||||
original_liquid_volume = 0.0
|
original_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -134,49 +103,44 @@ def generate_clean_vessel_protocol(
|
|||||||
original_liquid_volume = current_volume[0]
|
original_liquid_volume = current_volume[0]
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
original_liquid_volume = current_volume
|
original_liquid_volume = current_volume
|
||||||
print(f"CLEAN_VESSEL: 清洗前液体体积: {original_liquid_volume:.2f}mL")
|
|
||||||
|
|
||||||
# 第一步:如果需要加热且有加热设备,启动加热
|
# 第一步:如果需要加热且有加热设备,启动加热
|
||||||
if temp > 25.0 and heatchill_id:
|
if temp > 25.0 and heatchill_id:
|
||||||
print(f"CLEAN_VESSEL: 启动加热至 {temp}°C")
|
debug_print(f"启动加热至 {temp}°C")
|
||||||
heatchill_start_action = {
|
heatchill_start_action = {
|
||||||
"device_id": heatchill_id,
|
"device_id": heatchill_id,
|
||||||
"action_name": "heat_chill_start",
|
"action_name": "heat_chill_start",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"temp": temp,
|
"temp": temp,
|
||||||
"purpose": f"cleaning with {solvent}"
|
"purpose": f"cleaning with {solvent}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(heatchill_start_action)
|
action_sequence.append(heatchill_start_action)
|
||||||
|
|
||||||
# 等待温度稳定
|
|
||||||
wait_action = {
|
wait_action = {
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 30} # 等待30秒让温度稳定
|
"action_kwargs": {"time": 30}
|
||||||
}
|
}
|
||||||
action_sequence.append(wait_action)
|
action_sequence.append(wait_action)
|
||||||
|
|
||||||
# 第二步:重复清洗操作
|
# 第二步:重复清洗操作
|
||||||
for repeat in range(repeats):
|
for repeat in range(repeats):
|
||||||
print(f"CLEAN_VESSEL: 执行第 {repeat + 1} 次清洗")
|
debug_print(f"执行第 {repeat + 1}/{repeats} 次清洗")
|
||||||
|
|
||||||
# 2a. 使用 pump_protocol 将溶剂转移到目标容器
|
# 2a. 使用 pump_protocol 将溶剂转移到目标容器
|
||||||
print(f"CLEAN_VESSEL: 将 {volume} mL {solvent} 转移到 {vessel_id}")
|
|
||||||
try:
|
try:
|
||||||
# 调用成熟的 pump_protocol 算法
|
|
||||||
add_solvent_actions = generate_pump_protocol(
|
add_solvent_actions = generate_pump_protocol(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=solvent_vessel,
|
from_vessel=solvent_vessel,
|
||||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
to_vessel=vessel_id,
|
||||||
volume=volume,
|
volume=volume,
|
||||||
flowrate=2.5, # 适中的流速,避免飞溅
|
flowrate=2.5,
|
||||||
transfer_flowrate=2.5
|
transfer_flowrate=2.5
|
||||||
)
|
)
|
||||||
action_sequence.extend(add_solvent_actions)
|
action_sequence.extend(add_solvent_actions)
|
||||||
|
|
||||||
# 🔧 新增:更新容器体积(添加清洗溶剂)
|
# 更新容器体积(添加清洗溶剂)
|
||||||
print(f"CLEAN_VESSEL: 更新容器体积 - 添加清洗溶剂 {volume:.2f}mL")
|
|
||||||
if "data" not in vessel:
|
if "data" not in vessel:
|
||||||
vessel["data"] = {}
|
vessel["data"] = {}
|
||||||
|
|
||||||
@@ -185,21 +149,16 @@ def generate_clean_vessel_protocol(
|
|||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
if len(current_volume) > 0:
|
if len(current_volume) > 0:
|
||||||
vessel["data"]["liquid_volume"][0] += volume
|
vessel["data"]["liquid_volume"][0] += volume
|
||||||
print(f"CLEAN_VESSEL: 添加溶剂后体积: {vessel['data']['liquid_volume'][0]:.2f}mL (+{volume:.2f}mL)")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = [volume]
|
vessel["data"]["liquid_volume"] = [volume]
|
||||||
print(f"CLEAN_VESSEL: 初始化清洗体积: {volume:.2f}mL")
|
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
vessel["data"]["liquid_volume"] += volume
|
vessel["data"]["liquid_volume"] += volume
|
||||||
print(f"CLEAN_VESSEL: 添加溶剂后体积: {vessel['data']['liquid_volume']:.2f}mL (+{volume:.2f}mL)")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = volume
|
vessel["data"]["liquid_volume"] = volume
|
||||||
print(f"CLEAN_VESSEL: 重置体积为: {volume:.2f}mL")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = volume
|
vessel["data"]["liquid_volume"] = volume
|
||||||
print(f"CLEAN_VESSEL: 创建新体积记录: {volume:.2f}mL")
|
|
||||||
|
|
||||||
# 🔧 同时更新图中的容器数据
|
# 同时更新图中的容器数据
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
@@ -215,14 +174,11 @@ def generate_clean_vessel_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = current_node_volume + volume
|
G.nodes[vessel_id]['data']['liquid_volume'] = current_node_volume + volume
|
||||||
|
|
||||||
print(f"CLEAN_VESSEL: 图节点体积数据已更新")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"无法将溶剂转移到容器: {str(e)}")
|
raise ValueError(f"无法将溶剂转移到容器: {str(e)}")
|
||||||
|
|
||||||
# 2b. 等待清洗作用时间(让溶剂充分清洗容器)
|
# 2b. 等待清洗作用时间
|
||||||
cleaning_wait_time = 60 if temp > 50.0 else 30 # 高温下等待更久
|
cleaning_wait_time = 60 if temp > 50.0 else 30
|
||||||
print(f"CLEAN_VESSEL: 等待清洗作用 {cleaning_wait_time} 秒")
|
|
||||||
wait_action = {
|
wait_action = {
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": cleaning_wait_time}
|
"action_kwargs": {"time": cleaning_wait_time}
|
||||||
@@ -230,38 +186,31 @@ def generate_clean_vessel_protocol(
|
|||||||
action_sequence.append(wait_action)
|
action_sequence.append(wait_action)
|
||||||
|
|
||||||
# 2c. 使用 pump_protocol 将清洗液转移到废液容器
|
# 2c. 使用 pump_protocol 将清洗液转移到废液容器
|
||||||
print(f"CLEAN_VESSEL: 将清洗液从 {vessel_id} 转移到废液容器")
|
|
||||||
try:
|
try:
|
||||||
# 调用成熟的 pump_protocol 算法
|
|
||||||
remove_waste_actions = generate_pump_protocol(
|
remove_waste_actions = generate_pump_protocol(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
from_vessel=vessel_id,
|
||||||
to_vessel=waste_vessel,
|
to_vessel=waste_vessel,
|
||||||
volume=volume,
|
volume=volume,
|
||||||
flowrate=2.5, # 适中的流速
|
flowrate=2.5,
|
||||||
transfer_flowrate=2.5
|
transfer_flowrate=2.5
|
||||||
)
|
)
|
||||||
action_sequence.extend(remove_waste_actions)
|
action_sequence.extend(remove_waste_actions)
|
||||||
|
|
||||||
# 🔧 新增:更新容器体积(移除清洗液)
|
# 更新容器体积(移除清洗液)
|
||||||
print(f"CLEAN_VESSEL: 更新容器体积 - 移除清洗液 {volume:.2f}mL")
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
if len(current_volume) > 0:
|
if len(current_volume) > 0:
|
||||||
vessel["data"]["liquid_volume"][0] = max(0.0, vessel["data"]["liquid_volume"][0] - volume)
|
vessel["data"]["liquid_volume"][0] = max(0.0, vessel["data"]["liquid_volume"][0] - volume)
|
||||||
print(f"CLEAN_VESSEL: 移除清洗液后体积: {vessel['data']['liquid_volume'][0]:.2f}mL (-{volume:.2f}mL)")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = [0.0]
|
vessel["data"]["liquid_volume"] = [0.0]
|
||||||
print(f"CLEAN_VESSEL: 重置体积为0mL")
|
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
vessel["data"]["liquid_volume"] = max(0.0, current_volume - volume)
|
vessel["data"]["liquid_volume"] = max(0.0, current_volume - volume)
|
||||||
print(f"CLEAN_VESSEL: 移除清洗液后体积: {vessel['data']['liquid_volume']:.2f}mL (-{volume:.2f}mL)")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = 0.0
|
vessel["data"]["liquid_volume"] = 0.0
|
||||||
print(f"CLEAN_VESSEL: 重置体积为0mL")
|
|
||||||
|
|
||||||
# 🔧 同时更新图中的容器数据
|
# 同时更新图中的容器数据
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
vessel_node_data = G.nodes[vessel_id].get('data', {})
|
vessel_node_data = G.nodes[vessel_id].get('data', {})
|
||||||
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
|
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
|
||||||
@@ -274,14 +223,11 @@ def generate_clean_vessel_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = max(0.0, current_node_volume - volume)
|
G.nodes[vessel_id]['data']['liquid_volume'] = max(0.0, current_node_volume - volume)
|
||||||
|
|
||||||
print(f"CLEAN_VESSEL: 图节点体积数据已更新")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise ValueError(f"无法将清洗液转移到废液容器: {str(e)}")
|
raise ValueError(f"无法将清洗液转移到废液容器: {str(e)}")
|
||||||
|
|
||||||
# 2d. 清洗循环间的短暂等待
|
# 2d. 清洗循环间的短暂等待
|
||||||
if repeat < repeats - 1: # 不是最后一次清洗
|
if repeat < repeats - 1:
|
||||||
print(f"CLEAN_VESSEL: 清洗循环间等待")
|
|
||||||
wait_action = {
|
wait_action = {
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 10}
|
"action_kwargs": {"time": 10}
|
||||||
@@ -290,17 +236,16 @@ def generate_clean_vessel_protocol(
|
|||||||
|
|
||||||
# 第三步:如果加热了,停止加热
|
# 第三步:如果加热了,停止加热
|
||||||
if temp > 25.0 and heatchill_id:
|
if temp > 25.0 and heatchill_id:
|
||||||
print(f"CLEAN_VESSEL: 停止加热")
|
|
||||||
heatchill_stop_action = {
|
heatchill_stop_action = {
|
||||||
"device_id": heatchill_id,
|
"device_id": heatchill_id,
|
||||||
"action_name": "heat_chill_stop",
|
"action_name": "heat_chill_stop",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(heatchill_stop_action)
|
action_sequence.append(heatchill_stop_action)
|
||||||
|
|
||||||
# 🔧 新增:清洗完成后的状态报告
|
# 清洗完成后的状态
|
||||||
final_liquid_volume = 0.0
|
final_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -309,18 +254,15 @@ def generate_clean_vessel_protocol(
|
|||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
final_liquid_volume = current_volume
|
final_liquid_volume = current_volume
|
||||||
|
|
||||||
print(f"CLEAN_VESSEL: 清洗完成")
|
debug_print(f"清洗完成: {len(action_sequence)} 个动作, 体积 {original_liquid_volume:.2f} -> {final_liquid_volume:.2f}mL")
|
||||||
print(f" - 清洗前体积: {original_liquid_volume:.2f}mL")
|
|
||||||
print(f" - 清洗后体积: {final_liquid_volume:.2f}mL")
|
|
||||||
print(f" - 生成了 {len(action_sequence)} 个动作")
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
|
||||||
# 便捷函数:常用清洗方案
|
# 便捷函数
|
||||||
def generate_quick_clean_protocol(
|
def generate_quick_clean_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
solvent: str = "water",
|
solvent: str = "water",
|
||||||
volume: float = 100.0
|
volume: float = 100.0
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
@@ -330,7 +272,7 @@ def generate_quick_clean_protocol(
|
|||||||
|
|
||||||
def generate_thorough_clean_protocol(
|
def generate_thorough_clean_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
solvent: str = "water",
|
solvent: str = "water",
|
||||||
volume: float = 150.0,
|
volume: float = 150.0,
|
||||||
temp: float = 60.0
|
temp: float = 60.0
|
||||||
@@ -341,7 +283,7 @@ def generate_thorough_clean_protocol(
|
|||||||
|
|
||||||
def generate_organic_clean_protocol(
|
def generate_organic_clean_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
volume: float = 100.0
|
volume: float = 100.0
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""有机清洗:先用有机溶剂,再用水清洗"""
|
"""有机清洗:先用有机溶剂,再用水清洗"""
|
||||||
@@ -354,14 +296,13 @@ def generate_organic_clean_protocol(
|
|||||||
)
|
)
|
||||||
action_sequence.extend(organic_actions)
|
action_sequence.extend(organic_actions)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
# 如果没有丙酮,尝试乙醇
|
|
||||||
try:
|
try:
|
||||||
organic_actions = generate_clean_vessel_protocol(
|
organic_actions = generate_clean_vessel_protocol(
|
||||||
G, vessel, "ethanol", volume, 25.0, 2
|
G, vessel, "ethanol", volume, 25.0, 2
|
||||||
)
|
)
|
||||||
action_sequence.extend(organic_actions)
|
action_sequence.extend(organic_actions)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
print("警告:未找到有机溶剂,跳过有机清洗步骤")
|
debug_print("未找到有机溶剂,跳过有机清洗步骤")
|
||||||
|
|
||||||
# 第二步:水清洗
|
# 第二步:水清洗
|
||||||
water_actions = generate_clean_vessel_protocol(
|
water_actions = generate_clean_vessel_protocol(
|
||||||
@@ -372,24 +313,6 @@ def generate_organic_clean_protocol(
|
|||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
|
||||||
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
|
||||||
"""获取容器中的液体体积(修复版)"""
|
|
||||||
if vessel not in G.nodes():
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
vessel_data = G.nodes[vessel].get('data', {})
|
|
||||||
liquids = vessel_data.get('liquid', [])
|
|
||||||
|
|
||||||
total_volume = 0.0
|
|
||||||
for liquid in liquids:
|
|
||||||
if isinstance(liquid, dict):
|
|
||||||
# 支持两种格式:新格式 (name, volume) 和旧格式 (liquid_type, liquid_volume)
|
|
||||||
volume = liquid.get('volume') or liquid.get('liquid_volume', 0.0)
|
|
||||||
total_volume += volume
|
|
||||||
|
|
||||||
return total_volume
|
|
||||||
|
|
||||||
|
|
||||||
def get_vessel_liquid_types(G: nx.DiGraph, vessel: str) -> List[str]:
|
def get_vessel_liquid_types(G: nx.DiGraph, vessel: str) -> List[str]:
|
||||||
"""获取容器中所有液体的类型"""
|
"""获取容器中所有液体的类型"""
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
@@ -401,7 +324,6 @@ def get_vessel_liquid_types(G: nx.DiGraph, vessel: str) -> List[str]:
|
|||||||
liquid_types = []
|
liquid_types = []
|
||||||
for liquid in liquids:
|
for liquid in liquids:
|
||||||
if isinstance(liquid, dict):
|
if isinstance(liquid, dict):
|
||||||
# 支持两种格式的液体类型字段
|
|
||||||
liquid_type = liquid.get('liquid_type') or liquid.get('name', '')
|
liquid_type = liquid.get('liquid_type') or liquid.get('name', '')
|
||||||
if liquid_type:
|
if liquid_type:
|
||||||
liquid_types.append(liquid_type)
|
liquid_types.append(liquid_type)
|
||||||
@@ -412,24 +334,20 @@ def get_vessel_liquid_types(G: nx.DiGraph, vessel: str) -> List[str]:
|
|||||||
def find_vessel_by_content(G: nx.DiGraph, content: str) -> List[str]:
|
def find_vessel_by_content(G: nx.DiGraph, content: str) -> List[str]:
|
||||||
"""
|
"""
|
||||||
根据内容物查找所有匹配的容器
|
根据内容物查找所有匹配的容器
|
||||||
返回匹配容器的ID列表
|
|
||||||
"""
|
"""
|
||||||
matching_vessels = []
|
matching_vessels = []
|
||||||
|
|
||||||
for node_id in G.nodes():
|
for node_id in G.nodes():
|
||||||
if G.nodes[node_id].get('type') == 'container':
|
if G.nodes[node_id].get('type') == 'container':
|
||||||
# 检查容器名称匹配
|
|
||||||
node_name = G.nodes[node_id].get('name', '').lower()
|
node_name = G.nodes[node_id].get('name', '').lower()
|
||||||
if content.lower() in node_id.lower() or content.lower() in node_name:
|
if content.lower() in node_id.lower() or content.lower() in node_name:
|
||||||
matching_vessels.append(node_id)
|
matching_vessels.append(node_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 检查液体类型匹配
|
|
||||||
vessel_data = G.nodes[node_id].get('data', {})
|
vessel_data = G.nodes[node_id].get('data', {})
|
||||||
liquids = vessel_data.get('liquid', [])
|
liquids = vessel_data.get('liquid', [])
|
||||||
config_data = G.nodes[node_id].get('config', {})
|
config_data = G.nodes[node_id].get('config', {})
|
||||||
|
|
||||||
# 检查 reagent_name 和 config.reagent
|
|
||||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
reagent_name = vessel_data.get('reagent_name', '').lower()
|
||||||
config_reagent = config_data.get('reagent', '').lower()
|
config_reagent = config_data.get('reagent', '').lower()
|
||||||
|
|
||||||
@@ -438,7 +356,6 @@ def find_vessel_by_content(G: nx.DiGraph, content: str) -> List[str]:
|
|||||||
matching_vessels.append(node_id)
|
matching_vessels.append(node_id)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 检查液体列表
|
|
||||||
for liquid in liquids:
|
for liquid in liquids:
|
||||||
if isinstance(liquid, dict):
|
if isinstance(liquid, dict):
|
||||||
liquid_type = liquid.get('liquid_type') or liquid.get('name', '')
|
liquid_type = liquid.get('liquid_type') or liquid.get('name', '')
|
||||||
|
|||||||
@@ -1,402 +1,19 @@
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import re
|
|
||||||
import logging
|
import logging
|
||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
|
|
||||||
from .utils.vessel_parser import get_vessel
|
from .utils.logger_util import debug_print, action_log
|
||||||
from .utils.logger_util import action_log
|
from .utils.unit_parser import parse_volume_input, parse_mass_input, parse_time_input, parse_temperature_input
|
||||||
|
from .utils.vessel_parser import get_vessel, find_solvent_vessel, find_connected_heatchill, find_connected_stirrer, find_solid_dispenser
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
# 创建进度日志动作
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[DISSOLVE] {message}")
|
|
||||||
|
|
||||||
# 🆕 创建进度日志动作
|
|
||||||
create_action_log = partial(action_log, prefix="[DISSOLVE]")
|
create_action_log = partial(action_log, prefix="[DISSOLVE]")
|
||||||
|
|
||||||
def parse_volume_input(volume_input: Union[str, float]) -> float:
|
|
||||||
"""
|
|
||||||
解析体积输入,支持带单位的字符串
|
|
||||||
|
|
||||||
Args:
|
|
||||||
volume_input: 体积输入(如 "10 mL", "?", 10.0)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 体积(毫升)
|
|
||||||
"""
|
|
||||||
if isinstance(volume_input, (int, float)):
|
|
||||||
debug_print(f"📏 体积输入为数值: {volume_input}")
|
|
||||||
return float(volume_input)
|
|
||||||
|
|
||||||
if not volume_input or not str(volume_input).strip():
|
|
||||||
debug_print(f"⚠️ 体积输入为空,返回0.0mL")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
volume_str = str(volume_input).lower().strip()
|
|
||||||
debug_print(f"🔍 解析体积输入: '{volume_str}'")
|
|
||||||
|
|
||||||
# 处理未知体积
|
|
||||||
if volume_str in ['?', 'unknown', 'tbd', 'to be determined']:
|
|
||||||
default_volume = 50.0 # 默认50mL
|
|
||||||
debug_print(f"❓ 检测到未知体积,使用默认值: {default_volume}mL 🎯")
|
|
||||||
return default_volume
|
|
||||||
|
|
||||||
# 移除空格并提取数字和单位
|
|
||||||
volume_clean = re.sub(r'\s+', '', volume_str)
|
|
||||||
|
|
||||||
# 匹配数字和单位的正则表达式
|
|
||||||
match = re.match(r'([0-9]*\.?[0-9]+)\s*(ml|l|μl|ul|microliter|milliliter|liter)?', volume_clean)
|
|
||||||
|
|
||||||
if not match:
|
|
||||||
debug_print(f"❌ 无法解析体积: '{volume_str}',使用默认值50mL")
|
|
||||||
return 50.0
|
|
||||||
|
|
||||||
value = float(match.group(1))
|
|
||||||
unit = match.group(2) or 'ml' # 默认单位为毫升
|
|
||||||
|
|
||||||
# 转换为毫升
|
|
||||||
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"✅ 体积已为mL: {volume}mL")
|
|
||||||
|
|
||||||
return volume
|
|
||||||
|
|
||||||
def parse_mass_input(mass_input: Union[str, float]) -> float:
|
|
||||||
"""
|
|
||||||
解析质量输入,支持带单位的字符串
|
|
||||||
|
|
||||||
Args:
|
|
||||||
mass_input: 质量输入(如 "2.9 g", "?", 2.5)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 质量(克)
|
|
||||||
"""
|
|
||||||
if isinstance(mass_input, (int, float)):
|
|
||||||
debug_print(f"⚖️ 质量输入为数值: {mass_input}g")
|
|
||||||
return float(mass_input)
|
|
||||||
|
|
||||||
if not mass_input or not str(mass_input).strip():
|
|
||||||
debug_print(f"⚠️ 质量输入为空,返回0.0g")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
mass_str = str(mass_input).lower().strip()
|
|
||||||
debug_print(f"🔍 解析质量输入: '{mass_str}'")
|
|
||||||
|
|
||||||
# 处理未知质量
|
|
||||||
if mass_str in ['?', 'unknown', 'tbd', 'to be determined']:
|
|
||||||
default_mass = 1.0 # 默认1g
|
|
||||||
debug_print(f"❓ 检测到未知质量,使用默认值: {default_mass}g 🎯")
|
|
||||||
return default_mass
|
|
||||||
|
|
||||||
# 移除空格并提取数字和单位
|
|
||||||
mass_clean = re.sub(r'\s+', '', mass_str)
|
|
||||||
|
|
||||||
# 匹配数字和单位的正则表达式
|
|
||||||
match = re.match(r'([0-9]*\.?[0-9]+)\s*(g|mg|kg|gram|milligram|kilogram)?', mass_clean)
|
|
||||||
|
|
||||||
if not match:
|
|
||||||
debug_print(f"❌ 无法解析质量: '{mass_str}',返回0.0g")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
value = float(match.group(1))
|
|
||||||
unit = match.group(2) or 'g' # 默认单位为克
|
|
||||||
|
|
||||||
# 转换为克
|
|
||||||
if unit in ['mg', 'milligram']:
|
|
||||||
mass = value / 1000.0 # mg -> g
|
|
||||||
debug_print(f"🔄 质量转换: {value}mg → {mass}g")
|
|
||||||
elif unit in ['kg', 'kilogram']:
|
|
||||||
mass = value * 1000.0 # kg -> g
|
|
||||||
debug_print(f"🔄 质量转换: {value}kg → {mass}g")
|
|
||||||
else: # g, gram 或默认
|
|
||||||
mass = value # 已经是g
|
|
||||||
debug_print(f"✅ 质量已为g: {mass}g")
|
|
||||||
|
|
||||||
return mass
|
|
||||||
|
|
||||||
def parse_time_input(time_input: Union[str, float]) -> float:
|
|
||||||
"""
|
|
||||||
解析时间输入,支持带单位的字符串
|
|
||||||
|
|
||||||
Args:
|
|
||||||
time_input: 时间输入(如 "30 min", "1 h", "?", 60.0)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 时间(秒)
|
|
||||||
"""
|
|
||||||
if isinstance(time_input, (int, float)):
|
|
||||||
debug_print(f"⏱️ 时间输入为数值: {time_input}秒")
|
|
||||||
return float(time_input)
|
|
||||||
|
|
||||||
if not time_input or not str(time_input).strip():
|
|
||||||
debug_print(f"⚠️ 时间输入为空,返回0秒")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
time_str = str(time_input).lower().strip()
|
|
||||||
debug_print(f"🔍 解析时间输入: '{time_str}'")
|
|
||||||
|
|
||||||
# 处理未知时间
|
|
||||||
if time_str in ['?', 'unknown', 'tbd']:
|
|
||||||
default_time = 600.0 # 默认10分钟
|
|
||||||
debug_print(f"❓ 检测到未知时间,使用默认值: {default_time}s (10分钟) ⏰")
|
|
||||||
return default_time
|
|
||||||
|
|
||||||
# 移除空格并提取数字和单位
|
|
||||||
time_clean = re.sub(r'\s+', '', time_str)
|
|
||||||
|
|
||||||
# 匹配数字和单位的正则表达式
|
|
||||||
match = re.match(r'([0-9]*\.?[0-9]+)\s*(s|sec|second|min|minute|h|hr|hour|d|day)?', time_clean)
|
|
||||||
|
|
||||||
if not match:
|
|
||||||
debug_print(f"❌ 无法解析时间: '{time_str}',返回0s")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
value = float(match.group(1))
|
|
||||||
unit = match.group(2) or 's' # 默认单位为秒
|
|
||||||
|
|
||||||
# 转换为秒
|
|
||||||
if unit in ['min', 'minute']:
|
|
||||||
time_sec = value * 60.0 # min -> s
|
|
||||||
debug_print(f"🔄 时间转换: {value}分钟 → {time_sec}秒")
|
|
||||||
elif unit in ['h', 'hr', 'hour']:
|
|
||||||
time_sec = value * 3600.0 # h -> s
|
|
||||||
debug_print(f"🔄 时间转换: {value}小时 → {time_sec}秒")
|
|
||||||
elif unit in ['d', 'day']:
|
|
||||||
time_sec = value * 86400.0 # d -> s
|
|
||||||
debug_print(f"🔄 时间转换: {value}天 → {time_sec}秒")
|
|
||||||
else: # s, sec, second 或默认
|
|
||||||
time_sec = value # 已经是s
|
|
||||||
debug_print(f"✅ 时间已为秒: {time_sec}秒")
|
|
||||||
|
|
||||||
return time_sec
|
|
||||||
|
|
||||||
def parse_temperature_input(temp_input: Union[str, float]) -> float:
|
|
||||||
"""
|
|
||||||
解析温度输入,支持带单位的字符串
|
|
||||||
|
|
||||||
Args:
|
|
||||||
temp_input: 温度输入(如 "60 °C", "room temperature", "?", 25.0)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 温度(摄氏度)
|
|
||||||
"""
|
|
||||||
if isinstance(temp_input, (int, float)):
|
|
||||||
debug_print(f"🌡️ 温度输入为数值: {temp_input}°C")
|
|
||||||
return float(temp_input)
|
|
||||||
|
|
||||||
if not temp_input or not str(temp_input).strip():
|
|
||||||
debug_print(f"⚠️ 温度输入为空,使用默认室温25°C")
|
|
||||||
return 25.0 # 默认室温
|
|
||||||
|
|
||||||
temp_str = str(temp_input).lower().strip()
|
|
||||||
debug_print(f"🔍 解析温度输入: '{temp_str}'")
|
|
||||||
|
|
||||||
# 处理特殊温度描述
|
|
||||||
temp_aliases = {
|
|
||||||
'room temperature': 25.0,
|
|
||||||
'rt': 25.0,
|
|
||||||
'ambient': 25.0,
|
|
||||||
'cold': 4.0,
|
|
||||||
'ice': 0.0,
|
|
||||||
'reflux': 80.0, # 默认回流温度
|
|
||||||
'?': 25.0,
|
|
||||||
'unknown': 25.0
|
|
||||||
}
|
|
||||||
|
|
||||||
if temp_str in temp_aliases:
|
|
||||||
result = temp_aliases[temp_str]
|
|
||||||
debug_print(f"🏷️ 温度别名解析: '{temp_str}' → {result}°C")
|
|
||||||
return result
|
|
||||||
|
|
||||||
# 移除空格并提取数字和单位
|
|
||||||
temp_clean = re.sub(r'\s+', '', temp_str)
|
|
||||||
|
|
||||||
# 匹配数字和单位的正则表达式
|
|
||||||
match = re.match(r'([0-9]*\.?[0-9]+)\s*(°c|c|celsius|°f|f|fahrenheit|k|kelvin)?', temp_clean)
|
|
||||||
|
|
||||||
if not match:
|
|
||||||
debug_print(f"❌ 无法解析温度: '{temp_str}',使用默认值25°C")
|
|
||||||
return 25.0
|
|
||||||
|
|
||||||
value = float(match.group(1))
|
|
||||||
unit = match.group(2) or 'c' # 默认单位为摄氏度
|
|
||||||
|
|
||||||
# 转换为摄氏度
|
|
||||||
if unit in ['°f', 'f', 'fahrenheit']:
|
|
||||||
temp_c = (value - 32) * 5/9 # F -> C
|
|
||||||
debug_print(f"🔄 温度转换: {value}°F → {temp_c:.1f}°C")
|
|
||||||
elif unit in ['k', 'kelvin']:
|
|
||||||
temp_c = value - 273.15 # K -> C
|
|
||||||
debug_print(f"🔄 温度转换: {value}K → {temp_c:.1f}°C")
|
|
||||||
else: # °c, c, celsius 或默认
|
|
||||||
temp_c = value # 已经是C
|
|
||||||
debug_print(f"✅ 温度已为°C: {temp_c}°C")
|
|
||||||
|
|
||||||
return temp_c
|
|
||||||
|
|
||||||
def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|
||||||
"""增强版溶剂容器查找,支持多种匹配模式"""
|
|
||||||
debug_print(f"🔍 开始查找溶剂 '{solvent}' 的容器...")
|
|
||||||
|
|
||||||
# 🔧 方法1:直接搜索 data.reagent_name 和 config.reagent
|
|
||||||
debug_print(f"📋 方法1: 搜索reagent字段...")
|
|
||||||
for node in G.nodes():
|
|
||||||
node_data = G.nodes[node].get('data', {})
|
|
||||||
node_type = G.nodes[node].get('type', '')
|
|
||||||
config_data = G.nodes[node].get('config', {})
|
|
||||||
|
|
||||||
# 只搜索容器类型的节点
|
|
||||||
if node_type == 'container':
|
|
||||||
reagent_name = node_data.get('reagent_name', '').lower()
|
|
||||||
config_reagent = config_data.get('reagent', '').lower()
|
|
||||||
|
|
||||||
# 精确匹配
|
|
||||||
if reagent_name == solvent.lower() or config_reagent == solvent.lower():
|
|
||||||
debug_print(f"✅ 通过reagent字段精确匹配到容器: {node} 🎯")
|
|
||||||
return node
|
|
||||||
|
|
||||||
# 模糊匹配
|
|
||||||
if (solvent.lower() in reagent_name and reagent_name) or \
|
|
||||||
(solvent.lower() in config_reagent and config_reagent):
|
|
||||||
debug_print(f"✅ 通过reagent字段模糊匹配到容器: {node} 🔍")
|
|
||||||
return node
|
|
||||||
|
|
||||||
# 🔧 方法2:常见的容器命名规则
|
|
||||||
debug_print(f"📋 方法2: 使用命名规则查找...")
|
|
||||||
solvent_clean = solvent.lower().replace(' ', '_').replace('-', '_')
|
|
||||||
possible_names = [
|
|
||||||
solvent_clean,
|
|
||||||
f"flask_{solvent_clean}",
|
|
||||||
f"bottle_{solvent_clean}",
|
|
||||||
f"vessel_{solvent_clean}",
|
|
||||||
f"{solvent_clean}_flask",
|
|
||||||
f"{solvent_clean}_bottle",
|
|
||||||
f"solvent_{solvent_clean}",
|
|
||||||
f"reagent_{solvent_clean}",
|
|
||||||
f"reagent_bottle_{solvent_clean}",
|
|
||||||
f"reagent_bottle_1", # 通用试剂瓶
|
|
||||||
f"reagent_bottle_2",
|
|
||||||
f"reagent_bottle_3"
|
|
||||||
]
|
|
||||||
|
|
||||||
debug_print(f"🔍 尝试的容器名称: {possible_names[:5]}... (共{len(possible_names)}个)")
|
|
||||||
|
|
||||||
for name in possible_names:
|
|
||||||
if name in G.nodes():
|
|
||||||
node_type = G.nodes[name].get('type', '')
|
|
||||||
if node_type == 'container':
|
|
||||||
debug_print(f"✅ 通过命名规则找到容器: {name} 📝")
|
|
||||||
return name
|
|
||||||
|
|
||||||
# 🔧 方法3:节点名称模糊匹配
|
|
||||||
debug_print(f"📋 方法3: 节点名称模糊匹配...")
|
|
||||||
for node_id in G.nodes():
|
|
||||||
node_data = G.nodes[node_id]
|
|
||||||
if node_data.get('type') == 'container':
|
|
||||||
# 检查节点名称是否包含溶剂名称
|
|
||||||
if solvent_clean in node_id.lower():
|
|
||||||
debug_print(f"✅ 通过节点名称模糊匹配到容器: {node_id} 🔍")
|
|
||||||
return node_id
|
|
||||||
|
|
||||||
# 检查液体类型匹配
|
|
||||||
vessel_data = node_data.get('data', {})
|
|
||||||
liquids = vessel_data.get('liquid', [])
|
|
||||||
for liquid in liquids:
|
|
||||||
if isinstance(liquid, dict):
|
|
||||||
liquid_type = liquid.get('liquid_type') or liquid.get('name', '')
|
|
||||||
if liquid_type.lower() == solvent.lower():
|
|
||||||
debug_print(f"✅ 通过液体类型匹配到容器: {node_id} 💧")
|
|
||||||
return node_id
|
|
||||||
|
|
||||||
# 🔧 方法4:使用第一个试剂瓶作为备选
|
|
||||||
debug_print(f"📋 方法4: 查找备选试剂瓶...")
|
|
||||||
for node_id in G.nodes():
|
|
||||||
node_data = G.nodes[node_id]
|
|
||||||
if (node_data.get('type') == 'container' and
|
|
||||||
('reagent' in node_id.lower() or 'bottle' in node_id.lower() or 'flask' in node_id.lower())):
|
|
||||||
debug_print(f"⚠️ 未找到专用容器,使用备选试剂瓶: {node_id} 🔄")
|
|
||||||
return node_id
|
|
||||||
|
|
||||||
debug_print(f"❌ 所有方法都失败了,无法找到容器!")
|
|
||||||
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器")
|
|
||||||
|
|
||||||
def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
|
|
||||||
"""查找连接到指定容器的加热搅拌器"""
|
|
||||||
debug_print(f"🔍 查找连接到容器 '{vessel}' 的加热搅拌器...")
|
|
||||||
|
|
||||||
heatchill_nodes = []
|
|
||||||
for node in G.nodes():
|
|
||||||
node_class = G.nodes[node].get('class', '').lower()
|
|
||||||
if 'heatchill' in node_class:
|
|
||||||
heatchill_nodes.append(node)
|
|
||||||
debug_print(f"📋 发现加热搅拌器: {node}")
|
|
||||||
|
|
||||||
debug_print(f"📊 共找到 {len(heatchill_nodes)} 个加热搅拌器")
|
|
||||||
|
|
||||||
# 查找连接到容器的加热器
|
|
||||||
for heatchill in heatchill_nodes:
|
|
||||||
if G.has_edge(heatchill, vessel) or G.has_edge(vessel, heatchill):
|
|
||||||
debug_print(f"✅ 找到连接的加热搅拌器: {heatchill} 🔗")
|
|
||||||
return heatchill
|
|
||||||
|
|
||||||
# 返回第一个加热器
|
|
||||||
if heatchill_nodes:
|
|
||||||
debug_print(f"⚠️ 未找到直接连接的加热搅拌器,使用第一个: {heatchill_nodes[0]} 🔄")
|
|
||||||
return heatchill_nodes[0]
|
|
||||||
|
|
||||||
debug_print(f"❌ 未找到任何加热搅拌器")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def find_connected_stirrer(G: nx.DiGraph, vessel: str) -> str:
|
|
||||||
"""查找连接到指定容器的搅拌器"""
|
|
||||||
debug_print(f"🔍 查找连接到容器 '{vessel}' 的搅拌器...")
|
|
||||||
|
|
||||||
stirrer_nodes = []
|
|
||||||
for node in G.nodes():
|
|
||||||
node_class = G.nodes[node].get('class', '').lower()
|
|
||||||
if 'stirrer' in node_class:
|
|
||||||
stirrer_nodes.append(node)
|
|
||||||
debug_print(f"📋 发现搅拌器: {node}")
|
|
||||||
|
|
||||||
debug_print(f"📊 共找到 {len(stirrer_nodes)} 个搅拌器")
|
|
||||||
|
|
||||||
# 查找连接到容器的搅拌器
|
|
||||||
for stirrer in stirrer_nodes:
|
|
||||||
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
|
|
||||||
debug_print(f"✅ 找到连接的搅拌器: {stirrer} 🔗")
|
|
||||||
return stirrer
|
|
||||||
|
|
||||||
# 返回第一个搅拌器
|
|
||||||
if stirrer_nodes:
|
|
||||||
debug_print(f"⚠️ 未找到直接连接的搅拌器,使用第一个: {stirrer_nodes[0]} 🔄")
|
|
||||||
return stirrer_nodes[0]
|
|
||||||
|
|
||||||
debug_print(f"❌ 未找到任何搅拌器")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def find_solid_dispenser(G: nx.DiGraph) -> str:
|
|
||||||
"""查找固体加样器"""
|
|
||||||
debug_print(f"🔍 查找固体加样器...")
|
|
||||||
|
|
||||||
for node in G.nodes():
|
|
||||||
node_class = G.nodes[node].get('class', '').lower()
|
|
||||||
if 'solid_dispenser' in node_class or 'dispenser' in node_class:
|
|
||||||
debug_print(f"✅ 找到固体加样器: {node} 🥄")
|
|
||||||
return node
|
|
||||||
|
|
||||||
debug_print(f"❌ 未找到固体加样器")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def generate_dissolve_protocol(
|
def generate_dissolve_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||||
@@ -436,28 +53,16 @@ def generate_dissolve_protocol(
|
|||||||
- mol: "0.12 mol", "16.2 mmol"
|
- mol: "0.12 mol", "16.2 mmol"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
# 从字典中提取容器ID
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
debug_print("=" * 60)
|
debug_print(f"开始生成溶解协议: vessel={vessel_id}, solvent='{solvent}', "
|
||||||
debug_print("🧪 开始生成溶解协议")
|
f"volume={volume}, mass={mass}, temp={temp}, time={time}, "
|
||||||
debug_print(f"📋 原始参数:")
|
f"reagent='{reagent}', mol='{mol}', event='{event}'")
|
||||||
debug_print(f" 🥼 vessel: {vessel} (ID: {vessel_id})")
|
|
||||||
debug_print(f" 💧 solvent: '{solvent}'")
|
|
||||||
debug_print(f" 📏 volume: {volume} (类型: {type(volume)})")
|
|
||||||
debug_print(f" ⚖️ mass: {mass} (类型: {type(mass)})")
|
|
||||||
debug_print(f" 🌡️ temp: {temp} (类型: {type(temp)})")
|
|
||||||
debug_print(f" ⏱️ time: {time} (类型: {type(time)})")
|
|
||||||
debug_print(f" 🧪 reagent: '{reagent}'")
|
|
||||||
debug_print(f" 🧬 mol: '{mol}'")
|
|
||||||
debug_print(f" 🎯 event: '{event}'")
|
|
||||||
debug_print(f" 📦 kwargs: {kwargs}") # 显示额外参数
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# === 参数验证 ===
|
# === 参数验证 ===
|
||||||
debug_print("🔍 步骤1: 参数验证...")
|
|
||||||
action_sequence.append(create_action_log(f"开始溶解操作 - 容器: {vessel_id}", "🎬"))
|
action_sequence.append(create_action_log(f"开始溶解操作 - 容器: {vessel_id}", "🎬"))
|
||||||
|
|
||||||
if not vessel_id:
|
if not vessel_id:
|
||||||
@@ -465,14 +70,11 @@ def generate_dissolve_protocol(
|
|||||||
raise ValueError("vessel 参数不能为空")
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
if vessel_id not in G.nodes():
|
if vessel_id not in G.nodes():
|
||||||
debug_print(f"❌ 容器 '{vessel_id}' 不存在于系统中")
|
|
||||||
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
||||||
|
|
||||||
debug_print("✅ 基本参数验证通过")
|
|
||||||
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
||||||
|
|
||||||
# 🔧 新增:记录溶解前的容器状态
|
# 记录溶解前的容器状态
|
||||||
debug_print("🔍 记录溶解前容器状态...")
|
|
||||||
original_liquid_volume = 0.0
|
original_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -480,10 +82,8 @@ def generate_dissolve_protocol(
|
|||||||
original_liquid_volume = current_volume[0]
|
original_liquid_volume = current_volume[0]
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
original_liquid_volume = current_volume
|
original_liquid_volume = current_volume
|
||||||
debug_print(f"📊 溶解前液体体积: {original_liquid_volume:.2f}mL")
|
|
||||||
|
|
||||||
# === 🔧 关键修复:参数解析 ===
|
# === 参数解析 ===
|
||||||
debug_print("🔍 步骤2: 参数解析...")
|
|
||||||
action_sequence.append(create_action_log("正在解析溶解参数...", "🔍"))
|
action_sequence.append(create_action_log("正在解析溶解参数...", "🔍"))
|
||||||
|
|
||||||
# 解析各种参数为数值
|
# 解析各种参数为数值
|
||||||
@@ -492,17 +92,10 @@ def generate_dissolve_protocol(
|
|||||||
final_temp = parse_temperature_input(temp)
|
final_temp = parse_temperature_input(temp)
|
||||||
final_time = parse_time_input(time)
|
final_time = parse_time_input(time)
|
||||||
|
|
||||||
debug_print(f"📊 解析结果:")
|
debug_print(f"解析结果: volume={final_volume}mL, mass={final_mass}g, "
|
||||||
debug_print(f" 📏 体积: {final_volume}mL")
|
f"temp={final_temp}°C, time={final_time}s")
|
||||||
debug_print(f" ⚖️ 质量: {final_mass}g")
|
|
||||||
debug_print(f" 🌡️ 温度: {final_temp}°C")
|
|
||||||
debug_print(f" ⏱️ 时间: {final_time}s")
|
|
||||||
debug_print(f" 🧪 试剂: '{reagent}'")
|
|
||||||
debug_print(f" 🧬 摩尔: '{mol}'")
|
|
||||||
debug_print(f" 🎯 事件: '{event}'")
|
|
||||||
|
|
||||||
# === 判断溶解类型 ===
|
# === 判断溶解类型 ===
|
||||||
debug_print("🔍 步骤3: 判断溶解类型...")
|
|
||||||
action_sequence.append(create_action_log("正在判断溶解类型...", "🔍"))
|
action_sequence.append(create_action_log("正在判断溶解类型...", "🔍"))
|
||||||
|
|
||||||
# 判断是固体溶解还是液体溶解
|
# 判断是固体溶解还是液体溶解
|
||||||
@@ -519,12 +112,11 @@ def generate_dissolve_protocol(
|
|||||||
|
|
||||||
dissolve_type = "固体溶解" if is_solid_dissolve else "液体溶解"
|
dissolve_type = "固体溶解" if is_solid_dissolve else "液体溶解"
|
||||||
dissolve_emoji = "🧂" if is_solid_dissolve else "💧"
|
dissolve_emoji = "🧂" if is_solid_dissolve else "💧"
|
||||||
debug_print(f"📋 溶解类型: {dissolve_type} {dissolve_emoji}")
|
debug_print(f"溶解类型: {dissolve_type}")
|
||||||
|
|
||||||
action_sequence.append(create_action_log(f"确定溶解类型: {dissolve_type} {dissolve_emoji}", "📋"))
|
action_sequence.append(create_action_log(f"确定溶解类型: {dissolve_type} {dissolve_emoji}", "📋"))
|
||||||
|
|
||||||
# === 查找设备 ===
|
# === 查找设备 ===
|
||||||
debug_print("🔍 步骤4: 查找设备...")
|
|
||||||
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
||||||
|
|
||||||
# 查找加热搅拌器
|
# 查找加热搅拌器
|
||||||
@@ -533,11 +125,7 @@ def generate_dissolve_protocol(
|
|||||||
|
|
||||||
# 优先使用加热搅拌器,否则使用独立搅拌器
|
# 优先使用加热搅拌器,否则使用独立搅拌器
|
||||||
stir_device_id = heatchill_id or stirrer_id
|
stir_device_id = heatchill_id or stirrer_id
|
||||||
|
debug_print(f"设备映射: heatchill='{heatchill_id}', stirrer='{stirrer_id}', 使用='{stir_device_id}'")
|
||||||
debug_print(f"📊 设备映射:")
|
|
||||||
debug_print(f" 🔥 加热器: '{heatchill_id}'")
|
|
||||||
debug_print(f" 🌪️ 搅拌器: '{stirrer_id}'")
|
|
||||||
debug_print(f" 🎯 使用设备: '{stir_device_id}'")
|
|
||||||
|
|
||||||
if heatchill_id:
|
if heatchill_id:
|
||||||
action_sequence.append(create_action_log(f"找到加热搅拌器: {heatchill_id}", "🔥"))
|
action_sequence.append(create_action_log(f"找到加热搅拌器: {heatchill_id}", "🔥"))
|
||||||
@@ -547,12 +135,9 @@ def generate_dissolve_protocol(
|
|||||||
action_sequence.append(create_action_log("未找到搅拌设备,将跳过搅拌", "⚠️"))
|
action_sequence.append(create_action_log("未找到搅拌设备,将跳过搅拌", "⚠️"))
|
||||||
|
|
||||||
# === 执行溶解流程 ===
|
# === 执行溶解流程 ===
|
||||||
debug_print("🔍 步骤5: 执行溶解流程...")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 步骤5.1: 启动加热搅拌(如果需要)
|
# 步骤5.1: 启动加热搅拌(如果需要)
|
||||||
if stir_device_id and (final_temp > 25.0 or final_time > 0 or stir_speed > 0):
|
if stir_device_id and (final_temp > 25.0 or final_time > 0 or stir_speed > 0):
|
||||||
debug_print(f"🔍 5.1: 启动加热搅拌,温度: {final_temp}°C")
|
|
||||||
action_sequence.append(create_action_log(f"准备加热搅拌 (目标温度: {final_temp}°C)", "🔥"))
|
action_sequence.append(create_action_log(f"准备加热搅拌 (目标温度: {final_temp}°C)", "🔥"))
|
||||||
|
|
||||||
if heatchill_id and (final_temp > 25.0 or final_time > 0):
|
if heatchill_id and (final_temp > 25.0 or final_time > 0):
|
||||||
@@ -603,7 +188,6 @@ def generate_dissolve_protocol(
|
|||||||
|
|
||||||
if is_solid_dissolve:
|
if is_solid_dissolve:
|
||||||
# === 固体溶解路径 ===
|
# === 固体溶解路径 ===
|
||||||
debug_print(f"🔍 5.2: 使用固体溶解路径")
|
|
||||||
action_sequence.append(create_action_log("开始固体溶解流程", "🧂"))
|
action_sequence.append(create_action_log("开始固体溶解流程", "🧂"))
|
||||||
|
|
||||||
solid_dispenser = find_solid_dispenser(G)
|
solid_dispenser = find_solid_dispenser(G)
|
||||||
@@ -632,12 +216,9 @@ def generate_dissolve_protocol(
|
|||||||
"action_kwargs": add_kwargs
|
"action_kwargs": add_kwargs
|
||||||
})
|
})
|
||||||
|
|
||||||
debug_print(f"✅ 固体加样完成")
|
|
||||||
action_sequence.append(create_action_log("固体加样完成", "✅"))
|
action_sequence.append(create_action_log("固体加样完成", "✅"))
|
||||||
|
|
||||||
# 🔧 新增:固体溶解体积运算 - 固体本身不会显著增加体积,但可能有少量变化
|
# 固体溶解体积运算 - 固体本身不会显著增加体积
|
||||||
debug_print(f"🔧 固体溶解 - 体积变化很小,主要是质量变化")
|
|
||||||
# 固体通常不会显著改变液体体积,这里只记录日志
|
|
||||||
action_sequence.append(create_action_log(f"固体已添加: {final_mass}g", "📊"))
|
action_sequence.append(create_action_log(f"固体已添加: {final_mass}g", "📊"))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@@ -646,7 +227,6 @@ def generate_dissolve_protocol(
|
|||||||
|
|
||||||
elif is_liquid_dissolve:
|
elif is_liquid_dissolve:
|
||||||
# === 液体溶解路径 ===
|
# === 液体溶解路径 ===
|
||||||
debug_print(f"🔍 5.3: 使用液体溶解路径")
|
|
||||||
action_sequence.append(create_action_log("开始液体溶解流程", "💧"))
|
action_sequence.append(create_action_log("开始液体溶解流程", "💧"))
|
||||||
|
|
||||||
# 查找溶剂容器
|
# 查找溶剂容器
|
||||||
@@ -688,11 +268,9 @@ def generate_dissolve_protocol(
|
|||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
action_sequence.extend(pump_actions)
|
action_sequence.extend(pump_actions)
|
||||||
debug_print(f"✅ 溶剂转移完成,添加了 {len(pump_actions)} 个动作")
|
|
||||||
action_sequence.append(create_action_log(f"溶剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
action_sequence.append(create_action_log(f"溶剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
||||||
|
|
||||||
# 🔧 新增:液体溶解体积运算 - 添加溶剂后更新容器体积
|
# 液体溶解体积运算 - 添加溶剂后更新容器体积
|
||||||
debug_print(f"🔧 更新容器液体体积 - 添加溶剂 {final_volume:.2f}mL")
|
|
||||||
|
|
||||||
# 确保vessel有data字段
|
# 确保vessel有data字段
|
||||||
if "data" not in vessel:
|
if "data" not in vessel:
|
||||||
@@ -703,19 +281,14 @@ def generate_dissolve_protocol(
|
|||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
if len(current_volume) > 0:
|
if len(current_volume) > 0:
|
||||||
vessel["data"]["liquid_volume"][0] += final_volume
|
vessel["data"]["liquid_volume"][0] += final_volume
|
||||||
debug_print(f"📊 添加溶剂后体积: {vessel['data']['liquid_volume'][0]:.2f}mL (+{final_volume:.2f}mL)")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = [final_volume]
|
vessel["data"]["liquid_volume"] = [final_volume]
|
||||||
debug_print(f"📊 初始化溶解体积: {final_volume:.2f}mL")
|
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
vessel["data"]["liquid_volume"] += final_volume
|
vessel["data"]["liquid_volume"] += final_volume
|
||||||
debug_print(f"📊 添加溶剂后体积: {vessel['data']['liquid_volume']:.2f}mL (+{final_volume:.2f}mL)")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = final_volume
|
vessel["data"]["liquid_volume"] = final_volume
|
||||||
debug_print(f"📊 重置体积为: {final_volume:.2f}mL")
|
|
||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = final_volume
|
vessel["data"]["liquid_volume"] = final_volume
|
||||||
debug_print(f"📊 创建新体积记录: {final_volume:.2f}mL")
|
|
||||||
|
|
||||||
# 🔧 同时更新图中的容器数据
|
# 🔧 同时更新图中的容器数据
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
@@ -733,8 +306,6 @@ def generate_dissolve_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = current_node_volume + final_volume
|
G.nodes[vessel_id]['data']['liquid_volume'] = current_node_volume + final_volume
|
||||||
|
|
||||||
debug_print(f"✅ 图节点体积数据已更新")
|
|
||||||
|
|
||||||
action_sequence.append(create_action_log(f"容器体积已更新 (+{final_volume:.2f}mL)", "📊"))
|
action_sequence.append(create_action_log(f"容器体积已更新 (+{final_volume:.2f}mL)", "📊"))
|
||||||
|
|
||||||
# 溶剂添加后等待
|
# 溶剂添加后等待
|
||||||
@@ -746,7 +317,6 @@ def generate_dissolve_protocol(
|
|||||||
|
|
||||||
# 步骤5.4: 等待溶解完成
|
# 步骤5.4: 等待溶解完成
|
||||||
if final_time > 0:
|
if final_time > 0:
|
||||||
debug_print(f"🔍 5.4: 等待溶解完成 - {final_time}s")
|
|
||||||
wait_minutes = final_time / 60
|
wait_minutes = final_time / 60
|
||||||
action_sequence.append(create_action_log(f"开始溶解等待 ({wait_minutes:.1f}分钟)", "⏰"))
|
action_sequence.append(create_action_log(f"开始溶解等待 ({wait_minutes:.1f}分钟)", "⏰"))
|
||||||
|
|
||||||
@@ -795,7 +365,6 @@ def generate_dissolve_protocol(
|
|||||||
|
|
||||||
# 步骤5.5: 停止加热搅拌(如果需要)
|
# 步骤5.5: 停止加热搅拌(如果需要)
|
||||||
if heatchill_id and final_time == 0 and final_temp > 25.0:
|
if heatchill_id and final_time == 0 and final_temp > 25.0:
|
||||||
debug_print(f"🔍 5.5: 停止加热器")
|
|
||||||
action_sequence.append(create_action_log("停止加热搅拌器", "🛑"))
|
action_sequence.append(create_action_log("停止加热搅拌器", "🛑"))
|
||||||
|
|
||||||
stop_action = {
|
stop_action = {
|
||||||
@@ -829,23 +398,8 @@ def generate_dissolve_protocol(
|
|||||||
final_liquid_volume = current_volume
|
final_liquid_volume = current_volume
|
||||||
|
|
||||||
# === 最终结果 ===
|
# === 最终结果 ===
|
||||||
debug_print("=" * 60)
|
debug_print(f"溶解协议生成完成: {vessel_id}, 类型={dissolve_type}, "
|
||||||
debug_print(f"🎉 溶解协议生成完成")
|
f"动作数={len(action_sequence)}, 前后体积={original_liquid_volume:.2f}→{final_liquid_volume:.2f}mL")
|
||||||
debug_print(f"📊 协议统计:")
|
|
||||||
debug_print(f" 📋 总动作数: {len(action_sequence)}")
|
|
||||||
debug_print(f" 🥼 容器: {vessel_id}")
|
|
||||||
debug_print(f" {dissolve_emoji} 溶解类型: {dissolve_type}")
|
|
||||||
if is_liquid_dissolve:
|
|
||||||
debug_print(f" 💧 溶剂: {solvent} ({final_volume}mL)")
|
|
||||||
if is_solid_dissolve:
|
|
||||||
debug_print(f" 🧪 试剂: {reagent}")
|
|
||||||
debug_print(f" ⚖️ 质量: {final_mass}g")
|
|
||||||
debug_print(f" 🧬 摩尔: {mol}")
|
|
||||||
debug_print(f" 🌡️ 温度: {final_temp}°C")
|
|
||||||
debug_print(f" ⏱️ 时间: {final_time}s")
|
|
||||||
debug_print(f" 📊 溶解前体积: {original_liquid_volume:.2f}mL")
|
|
||||||
debug_print(f" 📊 溶解后体积: {final_liquid_volume:.2f}mL")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
# 添加完成日志
|
# 添加完成日志
|
||||||
summary_msg = f"溶解协议完成: {vessel_id}"
|
summary_msg = f"溶解协议完成: {vessel_id}"
|
||||||
|
|||||||
@@ -1,56 +1,15 @@
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
|
|
||||||
from unilabos.compile.utils.vessel_parser import get_vessel
|
from .utils.vessel_parser import get_vessel, find_connected_heatchill
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
|
|
||||||
def find_connected_heater(G: nx.DiGraph, vessel: str) -> str:
|
|
||||||
"""
|
|
||||||
查找与容器相连的加热器
|
|
||||||
|
|
||||||
Args:
|
|
||||||
G: 网络图
|
|
||||||
vessel: 容器名称
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 加热器ID,如果没有则返回None
|
|
||||||
"""
|
|
||||||
print(f"DRY: 正在查找与容器 '{vessel}' 相连的加热器...")
|
|
||||||
|
|
||||||
# 查找所有加热器节点
|
|
||||||
heater_nodes = [node for node in G.nodes()
|
|
||||||
if ('heater' in node.lower() or
|
|
||||||
'heat' in node.lower() or
|
|
||||||
G.nodes[node].get('class') == 'virtual_heatchill' or
|
|
||||||
G.nodes[node].get('type') == 'heater')]
|
|
||||||
|
|
||||||
print(f"DRY: 找到的加热器节点: {heater_nodes}")
|
|
||||||
|
|
||||||
# 检查是否有加热器与目标容器相连
|
|
||||||
for heater in heater_nodes:
|
|
||||||
if G.has_edge(heater, vessel) or G.has_edge(vessel, heater):
|
|
||||||
print(f"DRY: 找到与容器 '{vessel}' 相连的加热器: {heater}")
|
|
||||||
return heater
|
|
||||||
|
|
||||||
# 如果没有直接连接,查找距离最近的加热器
|
|
||||||
for heater in heater_nodes:
|
|
||||||
try:
|
|
||||||
path = nx.shortest_path(G, source=heater, target=vessel)
|
|
||||||
if len(path) <= 3: # 最多2个中间节点
|
|
||||||
print(f"DRY: 找到距离较近的加热器: {heater}, 路径: {' → '.join(path)}")
|
|
||||||
return heater
|
|
||||||
except nx.NetworkXNoPath:
|
|
||||||
continue
|
|
||||||
|
|
||||||
print(f"DRY: 未找到与容器 '{vessel}' 相连的加热器")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def generate_dry_protocol(
|
def generate_dry_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
compound: str = "", # 🔧 修改:参数顺序调整,并设置默认值
|
compound: str = "",
|
||||||
**kwargs # 接收其他可能的参数但不使用
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成干燥协议序列
|
生成干燥协议序列
|
||||||
@@ -64,24 +23,18 @@ def generate_dry_protocol(
|
|||||||
Returns:
|
Returns:
|
||||||
List[Dict[str, Any]]: 动作序列
|
List[Dict[str, Any]]: 动作序列
|
||||||
"""
|
"""
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 默认参数
|
# 默认参数
|
||||||
dry_temp = 60.0 # 默认干燥温度 60°C
|
dry_temp = 60.0
|
||||||
dry_time = 3600.0 # 默认干燥时间 1小时(3600秒)
|
dry_time = 3600.0
|
||||||
simulation_time = 60.0 # 模拟时间 1分钟
|
simulation_time = 60.0
|
||||||
|
|
||||||
print(f"🌡️ DRY: 开始生成干燥协议 ✨")
|
debug_print(f"开始生成干燥协议: vessel={vessel_id}, compound={compound or '未指定'}, temp={dry_temp}°C")
|
||||||
print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
|
||||||
print(f" 🧪 化合物: {compound or '未指定'}")
|
|
||||||
print(f" 🔥 干燥温度: {dry_temp}°C")
|
|
||||||
print(f" ⏰ 干燥时间: {dry_time/60:.0f} 分钟")
|
|
||||||
|
|
||||||
# 🔧 新增:记录干燥前的容器状态
|
# 记录干燥前的容器状态
|
||||||
print(f"🔍 记录干燥前容器状态...")
|
|
||||||
original_liquid_volume = 0.0
|
original_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -89,39 +42,30 @@ def generate_dry_protocol(
|
|||||||
original_liquid_volume = current_volume[0]
|
original_liquid_volume = current_volume[0]
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
original_liquid_volume = current_volume
|
original_liquid_volume = current_volume
|
||||||
print(f"📊 干燥前液体体积: {original_liquid_volume:.2f}mL")
|
|
||||||
|
|
||||||
# 1. 验证目标容器存在
|
# 1. 验证目标容器存在
|
||||||
print(f"\n📋 步骤1: 验证目标容器 '{vessel_id}' 是否存在...")
|
|
||||||
if vessel_id not in G.nodes():
|
if vessel_id not in G.nodes():
|
||||||
print(f"⚠️ DRY: 警告 - 容器 '{vessel_id}' 不存在于系统中,跳过干燥 😢")
|
debug_print(f"容器 '{vessel_id}' 不存在于系统中,跳过干燥")
|
||||||
return action_sequence
|
return action_sequence
|
||||||
print(f"✅ 容器 '{vessel_id}' 验证通过!")
|
|
||||||
|
|
||||||
# 2. 查找相连的加热器
|
# 2. 查找相连的加热器
|
||||||
print(f"\n🔍 步骤2: 查找与容器相连的加热器...")
|
heater_id = find_connected_heatchill(G, vessel_id)
|
||||||
heater_id = find_connected_heater(G, vessel_id) # 🔧 使用 vessel_id
|
|
||||||
|
|
||||||
if heater_id is None:
|
if heater_id is None:
|
||||||
print(f"😭 DRY: 警告 - 未找到与容器 '{vessel_id}' 相连的加热器,跳过干燥")
|
debug_print(f"未找到与容器 '{vessel_id}' 相连的加热器,添加模拟干燥动作")
|
||||||
print(f"🎭 添加模拟干燥动作...")
|
|
||||||
# 添加一个等待动作,表示干燥过程(模拟)
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"time": 10.0, # 模拟等待时间
|
"time": 10.0,
|
||||||
"description": f"模拟干燥 {compound or '化合物'} (无加热器可用)"
|
"description": f"模拟干燥 {compound or '化合物'} (无加热器可用)"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# 🔧 新增:模拟干燥的体积变化(溶剂蒸发)
|
# 模拟干燥的体积变化
|
||||||
print(f"🔧 模拟干燥过程的体积减少...")
|
|
||||||
if original_liquid_volume > 0:
|
if original_liquid_volume > 0:
|
||||||
# 假设干燥过程中损失10%的体积(溶剂蒸发)
|
|
||||||
volume_loss = original_liquid_volume * 0.1
|
volume_loss = original_liquid_volume * 0.1
|
||||||
new_volume = max(0.0, original_liquid_volume - volume_loss)
|
new_volume = max(0.0, original_liquid_volume - volume_loss)
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -134,7 +78,6 @@ def generate_dry_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
vessel["data"]["liquid_volume"] = new_volume
|
||||||
|
|
||||||
# 🔧 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
@@ -150,32 +93,26 @@ def generate_dry_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
||||||
|
|
||||||
print(f"📊 模拟干燥体积变化: {original_liquid_volume:.2f}mL → {new_volume:.2f}mL (-{volume_loss:.2f}mL)")
|
debug_print(f"模拟干燥体积变化: {original_liquid_volume:.2f}mL -> {new_volume:.2f}mL")
|
||||||
|
|
||||||
print(f"📄 DRY: 协议生成完成,共 {len(action_sequence)} 个动作 🎯")
|
debug_print(f"协议生成完成,共 {len(action_sequence)} 个动作")
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
print(f"🎉 找到加热器: {heater_id}!")
|
debug_print(f"找到加热器: {heater_id}")
|
||||||
|
|
||||||
# 3. 启动加热器进行干燥
|
# 3. 启动加热器进行干燥
|
||||||
print(f"\n🚀 步骤3: 开始执行干燥流程...")
|
|
||||||
print(f"🔥 启动加热器 {heater_id} 进行干燥")
|
|
||||||
|
|
||||||
# 3.1 启动加热
|
# 3.1 启动加热
|
||||||
print(f" ⚡ 动作1: 启动加热到 {dry_temp}°C...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": heater_id,
|
"device_id": heater_id,
|
||||||
"action_name": "heat_chill_start",
|
"action_name": "heat_chill_start",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"temp": dry_temp,
|
"temp": dry_temp,
|
||||||
"purpose": f"干燥 {compound or '化合物'}"
|
"purpose": f"干燥 {compound or '化合物'}"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
print(f" ✅ 加热器启动命令已添加 🔥")
|
|
||||||
|
|
||||||
# 3.2 等待温度稳定
|
# 3.2 等待温度稳定
|
||||||
print(f" ⏳ 动作2: 等待温度稳定...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
@@ -183,34 +120,27 @@ def generate_dry_protocol(
|
|||||||
"description": f"等待温度稳定到 {dry_temp}°C"
|
"description": f"等待温度稳定到 {dry_temp}°C"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
print(f" ✅ 温度稳定等待命令已添加 🌡️")
|
|
||||||
|
|
||||||
# 3.3 保持干燥温度
|
# 3.3 保持干燥温度
|
||||||
print(f" 🔄 动作3: 保持干燥温度 {simulation_time/60:.0f} 分钟...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": heater_id,
|
"device_id": heater_id,
|
||||||
"action_name": "heat_chill",
|
"action_name": "heat_chill",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"temp": dry_temp,
|
"temp": dry_temp,
|
||||||
"time": simulation_time,
|
"time": simulation_time,
|
||||||
"purpose": f"干燥 {compound or '化合物'},保持温度 {dry_temp}°C"
|
"purpose": f"干燥 {compound or '化合物'},保持温度 {dry_temp}°C"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
print(f" ✅ 温度保持命令已添加 🌡️⏰")
|
|
||||||
|
|
||||||
# 🔧 新增:干燥过程中的体积变化计算
|
# 干燥过程中的体积变化计算
|
||||||
print(f"🔧 计算干燥过程中的体积变化...")
|
|
||||||
if original_liquid_volume > 0:
|
if original_liquid_volume > 0:
|
||||||
# 干燥过程中,溶剂会蒸发,固体保留
|
evaporation_rate = 0.001 * dry_temp
|
||||||
# 根据温度和时间估算蒸发量
|
|
||||||
evaporation_rate = 0.001 * dry_temp # 每秒每°C蒸发0.001mL
|
|
||||||
total_evaporation = min(original_liquid_volume * 0.8,
|
total_evaporation = min(original_liquid_volume * 0.8,
|
||||||
evaporation_rate * simulation_time) # 最多蒸发80%
|
evaporation_rate * simulation_time)
|
||||||
|
|
||||||
new_volume = max(0.0, original_liquid_volume - total_evaporation)
|
new_volume = max(0.0, original_liquid_volume - total_evaporation)
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -223,7 +153,6 @@ def generate_dry_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
vessel["data"]["liquid_volume"] = new_volume
|
||||||
|
|
||||||
# 🔧 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
@@ -239,36 +168,28 @@ def generate_dry_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
||||||
|
|
||||||
print(f"📊 干燥体积变化计算:")
|
debug_print(f"干燥体积变化: {original_liquid_volume:.2f}mL -> {new_volume:.2f}mL (-{total_evaporation:.2f}mL)")
|
||||||
print(f" - 初始体积: {original_liquid_volume:.2f}mL")
|
|
||||||
print(f" - 蒸发量: {total_evaporation:.2f}mL")
|
|
||||||
print(f" - 剩余体积: {new_volume:.2f}mL")
|
|
||||||
print(f" - 蒸发率: {(total_evaporation/original_liquid_volume*100):.1f}%")
|
|
||||||
|
|
||||||
# 3.4 停止加热
|
# 3.4 停止加热
|
||||||
print(f" ⏹️ 动作4: 停止加热...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": heater_id,
|
"device_id": heater_id,
|
||||||
"action_name": "heat_chill_stop",
|
"action_name": "heat_chill_stop",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"purpose": f"干燥完成,停止加热"
|
"purpose": f"干燥完成,停止加热"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
print(f" ✅ 停止加热命令已添加 🛑")
|
|
||||||
|
|
||||||
# 3.5 等待冷却
|
# 3.5 等待冷却
|
||||||
print(f" ❄️ 动作5: 等待冷却...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"time": 10.0, # 等待10秒冷却
|
"time": 10.0,
|
||||||
"description": f"等待 {compound or '化合物'} 冷却"
|
"description": f"等待 {compound or '化合物'} 冷却"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
print(f" ✅ 冷却等待命令已添加 🧊")
|
|
||||||
|
|
||||||
# 🔧 新增:干燥完成后的状态报告
|
# 最终状态
|
||||||
final_liquid_volume = 0.0
|
final_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -277,58 +198,35 @@ def generate_dry_protocol(
|
|||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
final_liquid_volume = current_volume
|
final_liquid_volume = current_volume
|
||||||
|
|
||||||
print(f"\n🎊 DRY: 协议生成完成,共 {len(action_sequence)} 个动作 🎯")
|
debug_print(f"干燥协议生成完成: {len(action_sequence)} 个动作, 体积 {original_liquid_volume:.2f} -> {final_liquid_volume:.2f}mL")
|
||||||
print(f"⏱️ DRY: 预计总时间: {(simulation_time + 30)/60:.0f} 分钟 ⌛")
|
|
||||||
print(f"📊 干燥结果:")
|
|
||||||
print(f" - 容器: {vessel_id}")
|
|
||||||
print(f" - 化合物: {compound or '未指定'}")
|
|
||||||
print(f" - 干燥前体积: {original_liquid_volume:.2f}mL")
|
|
||||||
print(f" - 干燥后体积: {final_liquid_volume:.2f}mL")
|
|
||||||
print(f" - 蒸发体积: {(original_liquid_volume - final_liquid_volume):.2f}mL")
|
|
||||||
print(f"🏁 所有动作序列准备就绪! ✨")
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
|
|
||||||
# 🔧 新增:便捷函数
|
# 便捷函数
|
||||||
def generate_quick_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
def generate_quick_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
||||||
temp: float = 40.0, time: float = 30.0) -> List[Dict[str, Any]]:
|
temp: float = 40.0, time: float = 30.0) -> List[Dict[str, Any]]:
|
||||||
"""快速干燥:低温短时间"""
|
"""快速干燥:低温短时间"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
print(f"🌡️ 快速干燥: {compound or '化合物'} → {vessel_id} @ {temp}°C ({time}min)")
|
|
||||||
|
|
||||||
# 临时修改默认参数
|
|
||||||
import types
|
|
||||||
temp_func = types.FunctionType(
|
|
||||||
generate_dry_protocol.__code__,
|
|
||||||
generate_dry_protocol.__globals__
|
|
||||||
)
|
|
||||||
|
|
||||||
# 直接调用原函数,但修改内部参数
|
|
||||||
return generate_dry_protocol(G, vessel, compound)
|
return generate_dry_protocol(G, vessel, compound)
|
||||||
|
|
||||||
|
|
||||||
def generate_thorough_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
def generate_thorough_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
||||||
temp: float = 80.0, time: float = 120.0) -> List[Dict[str, Any]]:
|
temp: float = 80.0, time: float = 120.0) -> List[Dict[str, Any]]:
|
||||||
"""深度干燥:高温长时间"""
|
"""深度干燥:高温长时间"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
print(f"🔥 深度干燥: {compound or '化合物'} → {vessel_id} @ {temp}°C ({time}min)")
|
|
||||||
return generate_dry_protocol(G, vessel, compound)
|
return generate_dry_protocol(G, vessel, compound)
|
||||||
|
|
||||||
|
|
||||||
def generate_gentle_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
def generate_gentle_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
||||||
temp: float = 30.0, time: float = 180.0) -> List[Dict[str, Any]]:
|
temp: float = 30.0, time: float = 180.0) -> List[Dict[str, Any]]:
|
||||||
"""温和干燥:低温长时间"""
|
"""温和干燥:低温长时间"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
print(f"🌡️ 温和干燥: {compound or '化合物'} → {vessel_id} @ {temp}°C ({time}min)")
|
|
||||||
return generate_dry_protocol(G, vessel, compound)
|
return generate_dry_protocol(G, vessel, compound)
|
||||||
|
|
||||||
|
|
||||||
# 测试函数
|
# 测试函数
|
||||||
def test_dry_protocol():
|
def test_dry_protocol():
|
||||||
"""测试干燥协议"""
|
"""测试干燥协议"""
|
||||||
print("=== DRY PROTOCOL 测试 ===")
|
debug_print("=== DRY PROTOCOL 测试 ===")
|
||||||
print("测试完成")
|
debug_print("测试完成")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -3,38 +3,14 @@ from functools import partial
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
import uuid
|
import uuid
|
||||||
import sys
|
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
from .utils.vessel_parser import get_vessel
|
from .utils.vessel_parser import get_vessel, find_connected_stirrer
|
||||||
from .utils.logger_util import action_log
|
from .utils.logger_util import debug_print, action_log
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing, generate_pump_protocol
|
from .pump_protocol import generate_pump_protocol_with_rinsing, generate_pump_protocol
|
||||||
|
|
||||||
# 设置日志
|
# 设置日志
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 确保输出编码为UTF-8
|
|
||||||
if hasattr(sys.stdout, 'reconfigure'):
|
|
||||||
try:
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8')
|
|
||||||
sys.stderr.reconfigure(encoding='utf-8')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出函数 - 支持中文"""
|
|
||||||
try:
|
|
||||||
# 确保消息是字符串格式
|
|
||||||
safe_message = str(message)
|
|
||||||
logger.info(f"[抽真空充气] {safe_message}")
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
# 如果编码失败,尝试替换不支持的字符
|
|
||||||
safe_message = str(message).encode('utf-8', errors='replace').decode('utf-8')
|
|
||||||
logger.info(f"[抽真空充气] {safe_message}")
|
|
||||||
except Exception as e:
|
|
||||||
# 最后的安全措施
|
|
||||||
fallback_message = f"日志输出错误: {repr(message)}"
|
|
||||||
logger.info(f"[抽真空充气] {fallback_message}")
|
|
||||||
|
|
||||||
create_action_log = partial(action_log, prefix="[抽真空充气]")
|
create_action_log = partial(action_log, prefix="[抽真空充气]")
|
||||||
|
|
||||||
def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
||||||
@@ -44,10 +20,9 @@ def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
|||||||
2. 气体类型匹配(data.gas_type)
|
2. 气体类型匹配(data.gas_type)
|
||||||
3. 默认气源
|
3. 默认气源
|
||||||
"""
|
"""
|
||||||
debug_print(f"🔍 正在查找气体 '{gas}' 的气源...")
|
debug_print(f"正在查找气体 '{gas}' 的气源...")
|
||||||
|
|
||||||
# 第一步:通过容器名称匹配
|
# 通过容器名称匹配
|
||||||
debug_print(f"📋 方法1: 容器名称匹配...")
|
|
||||||
gas_source_patterns = [
|
gas_source_patterns = [
|
||||||
f"gas_source_{gas}",
|
f"gas_source_{gas}",
|
||||||
f"gas_{gas}",
|
f"gas_{gas}",
|
||||||
@@ -58,42 +33,35 @@ def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
|||||||
f"bottle_{gas}"
|
f"bottle_{gas}"
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_print(f"🎯 尝试的容器名称: {gas_source_patterns}")
|
|
||||||
|
|
||||||
for pattern in gas_source_patterns:
|
for pattern in gas_source_patterns:
|
||||||
if pattern in G.nodes():
|
if pattern in G.nodes():
|
||||||
debug_print(f"✅ 通过名称找到气源: {pattern}")
|
debug_print(f"通过名称找到气源: {pattern}")
|
||||||
return pattern
|
return pattern
|
||||||
|
|
||||||
# 第二步:通过气体类型匹配 (data.gas_type)
|
# 通过气体类型匹配 (data.gas_type)
|
||||||
debug_print(f"📋 方法2: 气体类型匹配...")
|
|
||||||
for node_id in G.nodes():
|
for node_id in G.nodes():
|
||||||
node_data = G.nodes[node_id]
|
node_data = G.nodes[node_id]
|
||||||
node_class = node_data.get('class', '') or ''
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
# 检查是否是气源设备
|
|
||||||
if ('gas_source' in node_class or
|
if ('gas_source' in node_class or
|
||||||
'gas' in node_id.lower() or
|
'gas' in node_id.lower() or
|
||||||
node_id.startswith('flask_')):
|
node_id.startswith('flask_')):
|
||||||
|
|
||||||
# 检查 data.gas_type
|
|
||||||
data = node_data.get('data', {})
|
data = node_data.get('data', {})
|
||||||
gas_type = data.get('gas_type', '')
|
gas_type = data.get('gas_type', '')
|
||||||
|
|
||||||
if gas_type.lower() == gas.lower():
|
if gas_type.lower() == gas.lower():
|
||||||
debug_print(f"✅ 通过气体类型找到气源: {node_id} (气体类型: {gas_type})")
|
debug_print(f"通过气体类型找到气源: {node_id} (气体类型: {gas_type})")
|
||||||
return node_id
|
return node_id
|
||||||
|
|
||||||
# 检查 config.gas_type
|
|
||||||
config = node_data.get('config', {})
|
config = node_data.get('config', {})
|
||||||
config_gas_type = config.get('gas_type', '')
|
config_gas_type = config.get('gas_type', '')
|
||||||
|
|
||||||
if config_gas_type.lower() == gas.lower():
|
if config_gas_type.lower() == gas.lower():
|
||||||
debug_print(f"✅ 通过配置气体类型找到气源: {node_id} (配置气体类型: {config_gas_type})")
|
debug_print(f"通过配置气体类型找到气源: {node_id} (配置气体类型: {config_gas_type})")
|
||||||
return node_id
|
return node_id
|
||||||
|
|
||||||
# 第三步:查找所有可用的气源设备
|
# 查找所有可用的气源设备
|
||||||
debug_print(f"📋 方法3: 查找可用气源...")
|
|
||||||
available_gas_sources = []
|
available_gas_sources = []
|
||||||
for node_id in G.nodes():
|
for node_id in G.nodes():
|
||||||
node_data = G.nodes[node_id]
|
node_data = G.nodes[node_id]
|
||||||
@@ -107,10 +75,7 @@ def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
|||||||
gas_type = data.get('gas_type', '未知')
|
gas_type = data.get('gas_type', '未知')
|
||||||
available_gas_sources.append(f"{node_id} (气体类型: {gas_type})")
|
available_gas_sources.append(f"{node_id} (气体类型: {gas_type})")
|
||||||
|
|
||||||
debug_print(f"📊 可用气源: {available_gas_sources}")
|
# 如果找不到特定气体,使用默认的第一个气源
|
||||||
|
|
||||||
# 第四步:如果找不到特定气体,使用默认的第一个气源
|
|
||||||
debug_print(f"📋 方法4: 查找默认气源...")
|
|
||||||
default_gas_sources = [
|
default_gas_sources = [
|
||||||
node for node in G.nodes()
|
node for node in G.nodes()
|
||||||
if ((G.nodes[node].get('class') or '').find('virtual_gas_source') != -1
|
if ((G.nodes[node].get('class') or '').find('virtual_gas_source') != -1
|
||||||
@@ -119,16 +84,13 @@ def find_gas_source(G: nx.DiGraph, gas: str) -> str:
|
|||||||
|
|
||||||
if default_gas_sources:
|
if default_gas_sources:
|
||||||
default_source = default_gas_sources[0]
|
default_source = default_gas_sources[0]
|
||||||
debug_print(f"⚠️ 未找到特定气体 '{gas}',使用默认气源: {default_source}")
|
debug_print(f"未找到特定气体 '{gas}',使用默认气源: {default_source}")
|
||||||
return default_source
|
return default_source
|
||||||
|
|
||||||
debug_print(f"❌ 所有方法都失败了!")
|
|
||||||
raise ValueError(f"无法找到气体 '{gas}' 的气源。可用气源: {available_gas_sources}")
|
raise ValueError(f"无法找到气体 '{gas}' 的气源。可用气源: {available_gas_sources}")
|
||||||
|
|
||||||
def find_vacuum_pump(G: nx.DiGraph) -> str:
|
def find_vacuum_pump(G: nx.DiGraph) -> str:
|
||||||
"""查找真空泵设备"""
|
"""查找真空泵设备"""
|
||||||
debug_print("🔍 正在查找真空泵...")
|
|
||||||
|
|
||||||
vacuum_pumps = []
|
vacuum_pumps = []
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
node_data = G.nodes[node]
|
node_data = G.nodes[node]
|
||||||
@@ -138,49 +100,15 @@ def find_vacuum_pump(G: nx.DiGraph) -> str:
|
|||||||
'vacuum_pump' in node.lower() or
|
'vacuum_pump' in node.lower() or
|
||||||
'vacuum' in node_class.lower()):
|
'vacuum' in node_class.lower()):
|
||||||
vacuum_pumps.append(node)
|
vacuum_pumps.append(node)
|
||||||
debug_print(f"📋 发现真空泵: {node}")
|
|
||||||
|
|
||||||
if not vacuum_pumps:
|
if not vacuum_pumps:
|
||||||
debug_print(f"❌ 系统中未找到真空泵")
|
|
||||||
raise ValueError("系统中未找到真空泵")
|
raise ValueError("系统中未找到真空泵")
|
||||||
|
|
||||||
debug_print(f"✅ 使用真空泵: {vacuum_pumps[0]}")
|
debug_print(f"使用真空泵: {vacuum_pumps[0]}")
|
||||||
return vacuum_pumps[0]
|
return vacuum_pumps[0]
|
||||||
|
|
||||||
def find_connected_stirrer(G: nx.DiGraph, vessel: str) -> Optional[str]:
|
|
||||||
"""查找与指定容器相连的搅拌器"""
|
|
||||||
debug_print(f"🔍 正在查找与容器 {vessel} 连接的搅拌器...")
|
|
||||||
|
|
||||||
stirrer_nodes = []
|
|
||||||
for node in G.nodes():
|
|
||||||
node_data = G.nodes[node]
|
|
||||||
node_class = node_data.get('class', '') or ''
|
|
||||||
|
|
||||||
if 'virtual_stirrer' in node_class or 'stirrer' in node.lower():
|
|
||||||
stirrer_nodes.append(node)
|
|
||||||
debug_print(f"📋 发现搅拌器: {node}")
|
|
||||||
|
|
||||||
debug_print(f"📊 找到的搅拌器总数: {len(stirrer_nodes)}")
|
|
||||||
|
|
||||||
# 检查哪个搅拌器与目标容器相连
|
|
||||||
for stirrer in stirrer_nodes:
|
|
||||||
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
|
|
||||||
debug_print(f"✅ 找到连接的搅拌器: {stirrer}")
|
|
||||||
return stirrer
|
|
||||||
|
|
||||||
# 如果没有连接的搅拌器,返回第一个可用的
|
|
||||||
if stirrer_nodes:
|
|
||||||
debug_print(f"⚠️ 未找到直接连接的搅拌器,使用第一个可用的: {stirrer_nodes[0]}")
|
|
||||||
return stirrer_nodes[0]
|
|
||||||
|
|
||||||
debug_print("❌ 未找到搅拌器")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def find_vacuum_solenoid_valve(G: nx.DiGraph, vacuum_pump: str) -> Optional[str]:
|
def find_vacuum_solenoid_valve(G: nx.DiGraph, vacuum_pump: str) -> Optional[str]:
|
||||||
"""查找真空泵相关的电磁阀"""
|
"""查找真空泵相关的电磁阀"""
|
||||||
debug_print(f"🔍 正在查找真空泵 {vacuum_pump} 的电磁阀...")
|
|
||||||
|
|
||||||
# 查找所有电磁阀
|
|
||||||
solenoid_valves = []
|
solenoid_valves = []
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
node_data = G.nodes[node]
|
node_data = G.nodes[node]
|
||||||
@@ -188,32 +116,24 @@ def find_vacuum_solenoid_valve(G: nx.DiGraph, vacuum_pump: str) -> Optional[str]
|
|||||||
|
|
||||||
if ('solenoid' in node_class.lower() or 'solenoid_valve' in node.lower()):
|
if ('solenoid' in node_class.lower() or 'solenoid_valve' in node.lower()):
|
||||||
solenoid_valves.append(node)
|
solenoid_valves.append(node)
|
||||||
debug_print(f"📋 发现电磁阀: {node}")
|
|
||||||
|
|
||||||
debug_print(f"📊 找到的电磁阀: {solenoid_valves}")
|
|
||||||
|
|
||||||
# 检查连接关系
|
# 检查连接关系
|
||||||
debug_print(f"📋 方法1: 检查连接关系...")
|
|
||||||
for solenoid in solenoid_valves:
|
for solenoid in solenoid_valves:
|
||||||
if G.has_edge(solenoid, vacuum_pump) or G.has_edge(vacuum_pump, solenoid):
|
if G.has_edge(solenoid, vacuum_pump) or G.has_edge(vacuum_pump, solenoid):
|
||||||
debug_print(f"✅ 找到连接的真空电磁阀: {solenoid}")
|
debug_print(f"找到连接的真空电磁阀: {solenoid}")
|
||||||
return solenoid
|
return solenoid
|
||||||
|
|
||||||
# 通过命名规则查找
|
# 通过命名规则查找
|
||||||
debug_print(f"📋 方法2: 检查命名规则...")
|
|
||||||
for solenoid in solenoid_valves:
|
for solenoid in solenoid_valves:
|
||||||
if 'vacuum' in solenoid.lower() or solenoid == 'solenoid_valve_1':
|
if 'vacuum' in solenoid.lower() or solenoid == 'solenoid_valve_1':
|
||||||
debug_print(f"✅ 通过命名找到真空电磁阀: {solenoid}")
|
debug_print(f"通过命名找到真空电磁阀: {solenoid}")
|
||||||
return solenoid
|
return solenoid
|
||||||
|
|
||||||
debug_print("⚠️ 未找到真空电磁阀")
|
debug_print("未找到真空电磁阀")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def find_gas_solenoid_valve(G: nx.DiGraph, gas_source: str) -> Optional[str]:
|
def find_gas_solenoid_valve(G: nx.DiGraph, gas_source: str) -> Optional[str]:
|
||||||
"""查找气源相关的电磁阀"""
|
"""查找气源相关的电磁阀"""
|
||||||
debug_print(f"🔍 正在查找气源 {gas_source} 的电磁阀...")
|
|
||||||
|
|
||||||
# 查找所有电磁阀
|
|
||||||
solenoid_valves = []
|
solenoid_valves = []
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
node_data = G.nodes[node]
|
node_data = G.nodes[node]
|
||||||
@@ -222,33 +142,29 @@ def find_gas_solenoid_valve(G: nx.DiGraph, gas_source: str) -> Optional[str]:
|
|||||||
if ('solenoid' in node_class.lower() or 'solenoid_valve' in node.lower()):
|
if ('solenoid' in node_class.lower() or 'solenoid_valve' in node.lower()):
|
||||||
solenoid_valves.append(node)
|
solenoid_valves.append(node)
|
||||||
|
|
||||||
debug_print(f"📊 找到的电磁阀: {solenoid_valves}")
|
|
||||||
|
|
||||||
# 检查连接关系
|
# 检查连接关系
|
||||||
debug_print(f"📋 方法1: 检查连接关系...")
|
|
||||||
for solenoid in solenoid_valves:
|
for solenoid in solenoid_valves:
|
||||||
if G.has_edge(gas_source, solenoid) or G.has_edge(solenoid, gas_source):
|
if G.has_edge(gas_source, solenoid) or G.has_edge(solenoid, gas_source):
|
||||||
debug_print(f"✅ 找到连接的气源电磁阀: {solenoid}")
|
debug_print(f"找到连接的气源电磁阀: {solenoid}")
|
||||||
return solenoid
|
return solenoid
|
||||||
|
|
||||||
# 通过命名规则查找
|
# 通过命名规则查找
|
||||||
debug_print(f"📋 方法2: 检查命名规则...")
|
|
||||||
for solenoid in solenoid_valves:
|
for solenoid in solenoid_valves:
|
||||||
if 'gas' in solenoid.lower() or solenoid == 'solenoid_valve_2':
|
if 'gas' in solenoid.lower() or solenoid == 'solenoid_valve_2':
|
||||||
debug_print(f"✅ 通过命名找到气源电磁阀: {solenoid}")
|
debug_print(f"通过命名找到气源电磁阀: {solenoid}")
|
||||||
return solenoid
|
return solenoid
|
||||||
|
|
||||||
debug_print("⚠️ 未找到气源电磁阀")
|
debug_print("未找到气源电磁阀")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def generate_evacuateandrefill_protocol(
|
def generate_evacuateandrefill_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
gas: str,
|
gas: str,
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成抽真空和充气操作的动作序列 - 中文版
|
生成抽真空和充气操作的动作序列
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
G: 设备图
|
G: 设备图
|
||||||
@@ -260,51 +176,34 @@ def generate_evacuateandrefill_protocol(
|
|||||||
List[Dict[str, Any]]: 动作序列
|
List[Dict[str, Any]]: 动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
# 硬编码重复次数为 3
|
# 硬编码重复次数为 3
|
||||||
repeats = 3
|
repeats = 3
|
||||||
|
|
||||||
# 生成协议ID
|
|
||||||
protocol_id = str(uuid.uuid4())
|
protocol_id = str(uuid.uuid4())
|
||||||
debug_print(f"🆔 生成协议ID: {protocol_id}")
|
|
||||||
|
|
||||||
debug_print("=" * 60)
|
debug_print(f"开始生成抽真空充气协议: vessel={vessel_id}, gas={gas}, repeats={repeats}")
|
||||||
debug_print("🧪 开始生成抽真空充气协议")
|
|
||||||
debug_print(f"📋 原始参数:")
|
|
||||||
debug_print(f" 🥼 vessel: {vessel} (ID: {vessel_id})")
|
|
||||||
debug_print(f" 💨 气体: '{gas}'")
|
|
||||||
debug_print(f" 🔄 循环次数: {repeats} (硬编码)")
|
|
||||||
debug_print(f" 📦 其他参数: {kwargs}")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# === 参数验证和修正 ===
|
# === 参数验证和修正 ===
|
||||||
debug_print("🔍 步骤1: 参数验证和修正...")
|
|
||||||
action_sequence.append(create_action_log(f"开始抽真空充气操作 - 容器: {vessel_id}", "🎬"))
|
action_sequence.append(create_action_log(f"开始抽真空充气操作 - 容器: {vessel_id}", "🎬"))
|
||||||
action_sequence.append(create_action_log(f"目标气体: {gas}", "💨"))
|
action_sequence.append(create_action_log(f"目标气体: {gas}", "💨"))
|
||||||
action_sequence.append(create_action_log(f"循环次数: {repeats}", "🔄"))
|
action_sequence.append(create_action_log(f"循环次数: {repeats}", "🔄"))
|
||||||
|
|
||||||
# 验证必需参数
|
|
||||||
if not vessel_id:
|
if not vessel_id:
|
||||||
debug_print("❌ 容器参数不能为空")
|
|
||||||
raise ValueError("容器参数不能为空")
|
raise ValueError("容器参数不能为空")
|
||||||
|
|
||||||
if not gas:
|
if not gas:
|
||||||
debug_print("❌ 气体参数不能为空")
|
|
||||||
raise ValueError("气体参数不能为空")
|
raise ValueError("气体参数不能为空")
|
||||||
|
|
||||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
if vessel_id not in G.nodes():
|
||||||
debug_print(f"❌ 容器 '{vessel_id}' 在系统中不存在")
|
|
||||||
raise ValueError(f"容器 '{vessel_id}' 在系统中不存在")
|
raise ValueError(f"容器 '{vessel_id}' 在系统中不存在")
|
||||||
|
|
||||||
debug_print("✅ 基本参数验证通过")
|
|
||||||
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
||||||
|
|
||||||
# 标准化气体名称
|
# 标准化气体名称
|
||||||
debug_print("🔧 标准化气体名称...")
|
|
||||||
gas_aliases = {
|
gas_aliases = {
|
||||||
'n2': 'nitrogen',
|
'n2': 'nitrogen',
|
||||||
'ar': 'argon',
|
'ar': 'argon',
|
||||||
@@ -324,13 +223,12 @@ def generate_evacuateandrefill_protocol(
|
|||||||
gas_lower = gas.lower().strip()
|
gas_lower = gas.lower().strip()
|
||||||
if gas_lower in gas_aliases:
|
if gas_lower in gas_aliases:
|
||||||
gas = gas_aliases[gas_lower]
|
gas = gas_aliases[gas_lower]
|
||||||
debug_print(f"🔄 标准化气体名称: {original_gas} -> {gas}")
|
debug_print(f"标准化气体名称: {original_gas} -> {gas}")
|
||||||
action_sequence.append(create_action_log(f"气体名称标准化: {original_gas} -> {gas}", "🔄"))
|
action_sequence.append(create_action_log(f"气体名称标准化: {original_gas} -> {gas}", "🔄"))
|
||||||
|
|
||||||
debug_print(f"📋 最终参数: 容器={vessel_id}, 气体={gas}, 重复={repeats}")
|
debug_print(f"最终参数: 容器={vessel_id}, 气体={gas}, 重复={repeats}")
|
||||||
|
|
||||||
# === 查找设备 ===
|
# === 查找设备 ===
|
||||||
debug_print("🔍 步骤2: 查找设备...")
|
|
||||||
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -352,26 +250,20 @@ def generate_evacuateandrefill_protocol(
|
|||||||
else:
|
else:
|
||||||
action_sequence.append(create_action_log("未找到气源电磁阀", "⚠️"))
|
action_sequence.append(create_action_log("未找到气源电磁阀", "⚠️"))
|
||||||
|
|
||||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||||
if stirrer_id:
|
if stirrer_id:
|
||||||
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_id}", "🌪️"))
|
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_id}", "🌪️"))
|
||||||
else:
|
else:
|
||||||
action_sequence.append(create_action_log("未找到搅拌器", "⚠️"))
|
action_sequence.append(create_action_log("未找到搅拌器", "⚠️"))
|
||||||
|
|
||||||
debug_print(f"📊 设备配置:")
|
debug_print(f"设备配置: 真空泵={vacuum_pump}, 气源={gas_source}, 搅拌器={stirrer_id}")
|
||||||
debug_print(f" 🌪️ 真空泵: {vacuum_pump}")
|
|
||||||
debug_print(f" 💨 气源: {gas_source}")
|
|
||||||
debug_print(f" 🚪 真空电磁阀: {vacuum_solenoid}")
|
|
||||||
debug_print(f" 🚪 气源电磁阀: {gas_solenoid}")
|
|
||||||
debug_print(f" 🌪️ 搅拌器: {stirrer_id}")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 设备查找失败: {str(e)}")
|
debug_print(f"设备查找失败: {str(e)}")
|
||||||
action_sequence.append(create_action_log(f"设备查找失败: {str(e)}", "❌"))
|
action_sequence.append(create_action_log(f"设备查找失败: {str(e)}", "❌"))
|
||||||
raise ValueError(f"设备查找失败: {str(e)}")
|
raise ValueError(f"设备查找失败: {str(e)}")
|
||||||
|
|
||||||
# === 参数设置 ===
|
# === 参数设置 ===
|
||||||
debug_print("🔍 步骤3: 参数设置...")
|
|
||||||
action_sequence.append(create_action_log("设置操作参数...", "⚙️"))
|
action_sequence.append(create_action_log("设置操作参数...", "⚙️"))
|
||||||
|
|
||||||
# 根据气体类型调整参数
|
# 根据气体类型调整参数
|
||||||
@@ -381,7 +273,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
PUMP_FLOW_RATE = 2.0
|
PUMP_FLOW_RATE = 2.0
|
||||||
VACUUM_TIME = 30.0
|
VACUUM_TIME = 30.0
|
||||||
REFILL_TIME = 20.0
|
REFILL_TIME = 20.0
|
||||||
debug_print("💨 惰性气体: 使用标准参数")
|
|
||||||
action_sequence.append(create_action_log("检测到惰性气体,使用标准参数", "💨"))
|
action_sequence.append(create_action_log("检测到惰性气体,使用标准参数", "💨"))
|
||||||
elif gas.lower() in ['air', 'oxygen']:
|
elif gas.lower() in ['air', 'oxygen']:
|
||||||
VACUUM_VOLUME = 20.0
|
VACUUM_VOLUME = 20.0
|
||||||
@@ -389,7 +280,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
PUMP_FLOW_RATE = 1.5
|
PUMP_FLOW_RATE = 1.5
|
||||||
VACUUM_TIME = 45.0
|
VACUUM_TIME = 45.0
|
||||||
REFILL_TIME = 25.0
|
REFILL_TIME = 25.0
|
||||||
debug_print("🔥 活性气体: 使用保守参数")
|
|
||||||
action_sequence.append(create_action_log("检测到活性气体,使用保守参数", "🔥"))
|
action_sequence.append(create_action_log("检测到活性气体,使用保守参数", "🔥"))
|
||||||
else:
|
else:
|
||||||
VACUUM_VOLUME = 15.0
|
VACUUM_VOLUME = 15.0
|
||||||
@@ -397,91 +287,65 @@ def generate_evacuateandrefill_protocol(
|
|||||||
PUMP_FLOW_RATE = 1.0
|
PUMP_FLOW_RATE = 1.0
|
||||||
VACUUM_TIME = 60.0
|
VACUUM_TIME = 60.0
|
||||||
REFILL_TIME = 30.0
|
REFILL_TIME = 30.0
|
||||||
debug_print("❓ 未知气体: 使用安全参数")
|
|
||||||
action_sequence.append(create_action_log("未知气体类型,使用安全参数", "❓"))
|
action_sequence.append(create_action_log("未知气体类型,使用安全参数", "❓"))
|
||||||
|
|
||||||
STIR_SPEED = 200.0
|
STIR_SPEED = 200.0
|
||||||
|
|
||||||
debug_print(f"⚙️ 操作参数:")
|
|
||||||
debug_print(f" 📏 真空体积: {VACUUM_VOLUME}mL")
|
|
||||||
debug_print(f" 📏 充气体积: {REFILL_VOLUME}mL")
|
|
||||||
debug_print(f" ⚡ 泵流速: {PUMP_FLOW_RATE}mL/s")
|
|
||||||
debug_print(f" ⏱️ 真空时间: {VACUUM_TIME}s")
|
|
||||||
debug_print(f" ⏱️ 充气时间: {REFILL_TIME}s")
|
|
||||||
debug_print(f" 🌪️ 搅拌速度: {STIR_SPEED}RPM")
|
|
||||||
|
|
||||||
action_sequence.append(create_action_log(f"真空体积: {VACUUM_VOLUME}mL", "📏"))
|
action_sequence.append(create_action_log(f"真空体积: {VACUUM_VOLUME}mL", "📏"))
|
||||||
action_sequence.append(create_action_log(f"充气体积: {REFILL_VOLUME}mL", "📏"))
|
action_sequence.append(create_action_log(f"充气体积: {REFILL_VOLUME}mL", "📏"))
|
||||||
action_sequence.append(create_action_log(f"泵流速: {PUMP_FLOW_RATE}mL/s", "⚡"))
|
action_sequence.append(create_action_log(f"泵流速: {PUMP_FLOW_RATE}mL/s", "⚡"))
|
||||||
|
|
||||||
# === 路径验证 ===
|
# === 路径验证 ===
|
||||||
debug_print("🔍 步骤4: 路径验证...")
|
|
||||||
action_sequence.append(create_action_log("验证传输路径...", "🛤️"))
|
action_sequence.append(create_action_log("验证传输路径...", "🛤️"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 验证抽真空路径
|
if nx.has_path(G, vessel_id, vacuum_pump):
|
||||||
if nx.has_path(G, vessel_id, vacuum_pump): # 🔧 使用 vessel_id
|
|
||||||
vacuum_path = nx.shortest_path(G, source=vessel_id, target=vacuum_pump)
|
vacuum_path = nx.shortest_path(G, source=vessel_id, target=vacuum_pump)
|
||||||
debug_print(f"✅ 真空路径: {' -> '.join(vacuum_path)}")
|
|
||||||
action_sequence.append(create_action_log(f"真空路径: {' -> '.join(vacuum_path)}", "🛤️"))
|
action_sequence.append(create_action_log(f"真空路径: {' -> '.join(vacuum_path)}", "🛤️"))
|
||||||
else:
|
else:
|
||||||
debug_print(f"⚠️ 真空路径不存在,继续执行但可能有问题")
|
|
||||||
action_sequence.append(create_action_log("真空路径检查: 路径不存在", "⚠️"))
|
action_sequence.append(create_action_log("真空路径检查: 路径不存在", "⚠️"))
|
||||||
|
|
||||||
# 验证充气路径
|
if nx.has_path(G, gas_source, vessel_id):
|
||||||
if nx.has_path(G, gas_source, vessel_id): # 🔧 使用 vessel_id
|
|
||||||
gas_path = nx.shortest_path(G, source=gas_source, target=vessel_id)
|
gas_path = nx.shortest_path(G, source=gas_source, target=vessel_id)
|
||||||
debug_print(f"✅ 气体路径: {' -> '.join(gas_path)}")
|
|
||||||
action_sequence.append(create_action_log(f"气体路径: {' -> '.join(gas_path)}", "🛤️"))
|
action_sequence.append(create_action_log(f"气体路径: {' -> '.join(gas_path)}", "🛤️"))
|
||||||
else:
|
else:
|
||||||
debug_print(f"⚠️ 气体路径不存在,继续执行但可能有问题")
|
|
||||||
action_sequence.append(create_action_log("气体路径检查: 路径不存在", "⚠️"))
|
action_sequence.append(create_action_log("气体路径检查: 路径不存在", "⚠️"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 路径验证失败: {str(e)},继续执行")
|
|
||||||
action_sequence.append(create_action_log(f"路径验证失败: {str(e)}", "⚠️"))
|
action_sequence.append(create_action_log(f"路径验证失败: {str(e)}", "⚠️"))
|
||||||
|
|
||||||
# === 启动搅拌器 ===
|
# === 启动搅拌器 ===
|
||||||
debug_print("🔍 步骤5: 启动搅拌器...")
|
|
||||||
|
|
||||||
if stirrer_id:
|
if stirrer_id:
|
||||||
debug_print(f"🌪️ 启动搅拌器: {stirrer_id}")
|
|
||||||
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {STIR_SPEED}rpm)", "🌪️"))
|
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {STIR_SPEED}rpm)", "🌪️"))
|
||||||
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "start_stir",
|
"action_name": "start_stir",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"stir_speed": STIR_SPEED,
|
"stir_speed": STIR_SPEED,
|
||||||
"purpose": "抽真空充气前预搅拌"
|
"purpose": "抽真空充气前预搅拌"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# 等待搅拌稳定
|
|
||||||
action_sequence.append(create_action_log("等待搅拌稳定...", "⏳"))
|
action_sequence.append(create_action_log("等待搅拌稳定...", "⏳"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 5.0}
|
"action_kwargs": {"time": 5.0}
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
debug_print("⚠️ 未找到搅拌器,跳过搅拌器启动")
|
|
||||||
action_sequence.append(create_action_log("跳过搅拌器启动", "⏭️"))
|
action_sequence.append(create_action_log("跳过搅拌器启动", "⏭️"))
|
||||||
|
|
||||||
# === 执行循环 ===
|
# === 执行循环 ===
|
||||||
debug_print("🔍 步骤6: 执行抽真空-充气循环...")
|
|
||||||
action_sequence.append(create_action_log(f"开始 {repeats} 次抽真空-充气循环", "🔄"))
|
action_sequence.append(create_action_log(f"开始 {repeats} 次抽真空-充气循环", "🔄"))
|
||||||
|
|
||||||
for cycle in range(repeats):
|
for cycle in range(repeats):
|
||||||
debug_print(f"=== 第 {cycle+1}/{repeats} 轮循环 ===")
|
|
||||||
action_sequence.append(create_action_log(f"第 {cycle+1}/{repeats} 轮循环开始", "🚀"))
|
action_sequence.append(create_action_log(f"第 {cycle+1}/{repeats} 轮循环开始", "🚀"))
|
||||||
|
|
||||||
# ============ 抽真空阶段 ============
|
# ============ 抽真空阶段 ============
|
||||||
debug_print(f"🌪️ 抽真空阶段开始")
|
|
||||||
action_sequence.append(create_action_log("开始抽真空阶段", "🌪️"))
|
action_sequence.append(create_action_log("开始抽真空阶段", "🌪️"))
|
||||||
|
|
||||||
# 启动真空泵
|
# 启动真空泵
|
||||||
debug_print(f"🔛 启动真空泵: {vacuum_pump}")
|
|
||||||
action_sequence.append(create_action_log(f"启动真空泵: {vacuum_pump}", "🔛"))
|
action_sequence.append(create_action_log(f"启动真空泵: {vacuum_pump}", "🔛"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_pump,
|
"device_id": vacuum_pump,
|
||||||
@@ -491,7 +355,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 开启真空电磁阀
|
# 开启真空电磁阀
|
||||||
if vacuum_solenoid:
|
if vacuum_solenoid:
|
||||||
debug_print(f"🚪 打开真空电磁阀: {vacuum_solenoid}")
|
|
||||||
action_sequence.append(create_action_log(f"打开真空电磁阀: {vacuum_solenoid}", "🚪"))
|
action_sequence.append(create_action_log(f"打开真空电磁阀: {vacuum_solenoid}", "🚪"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_solenoid,
|
"device_id": vacuum_solenoid,
|
||||||
@@ -500,13 +363,12 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# 抽真空操作
|
# 抽真空操作
|
||||||
debug_print(f"🌪️ 抽真空操作: {vessel_id} -> {vacuum_pump}")
|
|
||||||
action_sequence.append(create_action_log(f"开始抽真空: {vessel_id} -> {vacuum_pump}", "🌪️"))
|
action_sequence.append(create_action_log(f"开始抽真空: {vessel_id} -> {vacuum_pump}", "🌪️"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
vacuum_transfer_actions = generate_pump_protocol_with_rinsing(
|
vacuum_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
from_vessel=vessel_id,
|
||||||
to_vessel=vacuum_pump,
|
to_vessel=vacuum_pump,
|
||||||
volume=VACUUM_VOLUME,
|
volume=VACUUM_VOLUME,
|
||||||
amount="",
|
amount="",
|
||||||
@@ -522,10 +384,8 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
if vacuum_transfer_actions:
|
if vacuum_transfer_actions:
|
||||||
action_sequence.extend(vacuum_transfer_actions)
|
action_sequence.extend(vacuum_transfer_actions)
|
||||||
debug_print(f"✅ 添加了 {len(vacuum_transfer_actions)} 个抽真空动作")
|
|
||||||
action_sequence.append(create_action_log(f"抽真空协议完成 ({len(vacuum_transfer_actions)} 个操作)", "✅"))
|
action_sequence.append(create_action_log(f"抽真空协议完成 ({len(vacuum_transfer_actions)} 个操作)", "✅"))
|
||||||
else:
|
else:
|
||||||
debug_print("⚠️ 抽真空协议返回空序列,添加手动动作")
|
|
||||||
action_sequence.append(create_action_log("抽真空协议为空,使用手动等待", "⚠️"))
|
action_sequence.append(create_action_log("抽真空协议为空,使用手动等待", "⚠️"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -533,7 +393,7 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 抽真空失败: {str(e)}")
|
debug_print(f"抽真空失败: {str(e)}")
|
||||||
action_sequence.append(create_action_log(f"抽真空失败: {str(e)}", "❌"))
|
action_sequence.append(create_action_log(f"抽真空失败: {str(e)}", "❌"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -550,7 +410,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 关闭真空电磁阀
|
# 关闭真空电磁阀
|
||||||
if vacuum_solenoid:
|
if vacuum_solenoid:
|
||||||
debug_print(f"🚪 关闭真空电磁阀: {vacuum_solenoid}")
|
|
||||||
action_sequence.append(create_action_log(f"关闭真空电磁阀: {vacuum_solenoid}", "🚪"))
|
action_sequence.append(create_action_log(f"关闭真空电磁阀: {vacuum_solenoid}", "🚪"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_solenoid,
|
"device_id": vacuum_solenoid,
|
||||||
@@ -559,7 +418,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# 关闭真空泵
|
# 关闭真空泵
|
||||||
debug_print(f"🔴 停止真空泵: {vacuum_pump}")
|
|
||||||
action_sequence.append(create_action_log(f"停止真空泵: {vacuum_pump}", "🔴"))
|
action_sequence.append(create_action_log(f"停止真空泵: {vacuum_pump}", "🔴"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": vacuum_pump,
|
"device_id": vacuum_pump,
|
||||||
@@ -575,11 +433,9 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# ============ 充气阶段 ============
|
# ============ 充气阶段 ============
|
||||||
debug_print(f"💨 充气阶段开始")
|
|
||||||
action_sequence.append(create_action_log("开始气体充气阶段", "💨"))
|
action_sequence.append(create_action_log("开始气体充气阶段", "💨"))
|
||||||
|
|
||||||
# 启动气源
|
# 启动气源
|
||||||
debug_print(f"🔛 启动气源: {gas_source}")
|
|
||||||
action_sequence.append(create_action_log(f"启动气源: {gas_source}", "🔛"))
|
action_sequence.append(create_action_log(f"启动气源: {gas_source}", "🔛"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_source,
|
"device_id": gas_source,
|
||||||
@@ -589,7 +445,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 开启气源电磁阀
|
# 开启气源电磁阀
|
||||||
if gas_solenoid:
|
if gas_solenoid:
|
||||||
debug_print(f"🚪 打开气源电磁阀: {gas_solenoid}")
|
|
||||||
action_sequence.append(create_action_log(f"打开气源电磁阀: {gas_solenoid}", "🚪"))
|
action_sequence.append(create_action_log(f"打开气源电磁阀: {gas_solenoid}", "🚪"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_solenoid,
|
"device_id": gas_solenoid,
|
||||||
@@ -598,14 +453,13 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# 充气操作
|
# 充气操作
|
||||||
debug_print(f"💨 充气操作: {gas_source} -> {vessel_id}")
|
|
||||||
action_sequence.append(create_action_log(f"开始气体充气: {gas_source} -> {vessel_id}", "💨"))
|
action_sequence.append(create_action_log(f"开始气体充气: {gas_source} -> {vessel_id}", "💨"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
gas_transfer_actions = generate_pump_protocol_with_rinsing(
|
gas_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=gas_source,
|
from_vessel=gas_source,
|
||||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
to_vessel=vessel_id,
|
||||||
volume=REFILL_VOLUME,
|
volume=REFILL_VOLUME,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
@@ -620,10 +474,8 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
if gas_transfer_actions:
|
if gas_transfer_actions:
|
||||||
action_sequence.extend(gas_transfer_actions)
|
action_sequence.extend(gas_transfer_actions)
|
||||||
debug_print(f"✅ 添加了 {len(gas_transfer_actions)} 个充气动作")
|
|
||||||
action_sequence.append(create_action_log(f"气体充气协议完成 ({len(gas_transfer_actions)} 个操作)", "✅"))
|
action_sequence.append(create_action_log(f"气体充气协议完成 ({len(gas_transfer_actions)} 个操作)", "✅"))
|
||||||
else:
|
else:
|
||||||
debug_print("⚠️ 充气协议返回空序列,添加手动动作")
|
|
||||||
action_sequence.append(create_action_log("充气协议为空,使用手动等待", "⚠️"))
|
action_sequence.append(create_action_log("充气协议为空,使用手动等待", "⚠️"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -631,7 +483,7 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 气体充气失败: {str(e)}")
|
debug_print(f"气体充气失败: {str(e)}")
|
||||||
action_sequence.append(create_action_log(f"气体充气失败: {str(e)}", "❌"))
|
action_sequence.append(create_action_log(f"气体充气失败: {str(e)}", "❌"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -648,7 +500,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 关闭气源电磁阀
|
# 关闭气源电磁阀
|
||||||
if gas_solenoid:
|
if gas_solenoid:
|
||||||
debug_print(f"🚪 关闭气源电磁阀: {gas_solenoid}")
|
|
||||||
action_sequence.append(create_action_log(f"关闭气源电磁阀: {gas_solenoid}", "🚪"))
|
action_sequence.append(create_action_log(f"关闭气源电磁阀: {gas_solenoid}", "🚪"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_solenoid,
|
"device_id": gas_solenoid,
|
||||||
@@ -657,7 +508,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
})
|
})
|
||||||
|
|
||||||
# 关闭气源
|
# 关闭气源
|
||||||
debug_print(f"🔴 停止气源: {gas_source}")
|
|
||||||
action_sequence.append(create_action_log(f"停止气源: {gas_source}", "🔴"))
|
action_sequence.append(create_action_log(f"停止气源: {gas_source}", "🔴"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": gas_source,
|
"device_id": gas_source,
|
||||||
@@ -667,7 +517,6 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# 循环间等待
|
# 循环间等待
|
||||||
if cycle < repeats - 1:
|
if cycle < repeats - 1:
|
||||||
debug_print(f"⏳ 等待下一个循环...")
|
|
||||||
action_sequence.append(create_action_log("等待下一个循环...", "⏳"))
|
action_sequence.append(create_action_log("等待下一个循环...", "⏳"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -677,15 +526,12 @@ def generate_evacuateandrefill_protocol(
|
|||||||
action_sequence.append(create_action_log(f"第 {cycle+1}/{repeats} 轮循环完成", "✅"))
|
action_sequence.append(create_action_log(f"第 {cycle+1}/{repeats} 轮循环完成", "✅"))
|
||||||
|
|
||||||
# === 停止搅拌器 ===
|
# === 停止搅拌器 ===
|
||||||
debug_print("🔍 步骤7: 停止搅拌器...")
|
|
||||||
|
|
||||||
if stirrer_id:
|
if stirrer_id:
|
||||||
debug_print(f"🛑 停止搅拌器: {stirrer_id}")
|
|
||||||
action_sequence.append(create_action_log(f"停止搅拌器: {stirrer_id}", "🛑"))
|
action_sequence.append(create_action_log(f"停止搅拌器: {stirrer_id}", "🛑"))
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"device_id": stirrer_id,
|
"device_id": stirrer_id,
|
||||||
"action_name": "stop_stir",
|
"action_name": "stop_stir",
|
||||||
"action_kwargs": {"vessel": {"id": vessel_id},} # 🔧 使用 vessel_id
|
"action_kwargs": {"vessel": {"id": vessel_id},}
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
action_sequence.append(create_action_log("跳过搅拌器停止", "⏭️"))
|
action_sequence.append(create_action_log("跳过搅拌器停止", "⏭️"))
|
||||||
@@ -700,17 +546,8 @@ def generate_evacuateandrefill_protocol(
|
|||||||
# === 总结 ===
|
# === 总结 ===
|
||||||
total_time = (VACUUM_TIME + REFILL_TIME + 25) * repeats + 20
|
total_time = (VACUUM_TIME + REFILL_TIME + 25) * repeats + 20
|
||||||
|
|
||||||
debug_print("=" * 60)
|
debug_print(f"抽真空充气协议生成完成: {len(action_sequence)} 个动作, 预计 {total_time:.0f}s")
|
||||||
debug_print(f"🎉 抽真空充气协议生成完成")
|
|
||||||
debug_print(f"📊 协议统计:")
|
|
||||||
debug_print(f" 📋 总动作数: {len(action_sequence)}")
|
|
||||||
debug_print(f" ⏱️ 预计总时间: {total_time:.0f}s ({total_time/60:.1f} 分钟)")
|
|
||||||
debug_print(f" 🥼 处理容器: {vessel_id}")
|
|
||||||
debug_print(f" 💨 使用气体: {gas}")
|
|
||||||
debug_print(f" 🔄 重复次数: {repeats}")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
# 添加完成日志
|
|
||||||
summary_msg = f"抽真空充气协议完成: {vessel_id} (使用 {gas},{repeats} 次循环)"
|
summary_msg = f"抽真空充气协议完成: {vessel_id} (使用 {gas},{repeats} 次循环)"
|
||||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
||||||
|
|
||||||
@@ -718,35 +555,27 @@ def generate_evacuateandrefill_protocol(
|
|||||||
|
|
||||||
# === 便捷函数 ===
|
# === 便捷函数 ===
|
||||||
|
|
||||||
def generate_nitrogen_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
def generate_nitrogen_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""生成氮气置换协议"""
|
"""生成氮气置换协议"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
debug_print(f"💨 生成氮气置换协议: {vessel_id}")
|
|
||||||
return generate_evacuateandrefill_protocol(G, vessel, "nitrogen", **kwargs)
|
return generate_evacuateandrefill_protocol(G, vessel, "nitrogen", **kwargs)
|
||||||
|
|
||||||
def generate_argon_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
def generate_argon_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""生成氩气置换协议"""
|
"""生成氩气置换协议"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
debug_print(f"💨 生成氩气置换协议: {vessel_id}")
|
|
||||||
return generate_evacuateandrefill_protocol(G, vessel, "argon", **kwargs)
|
return generate_evacuateandrefill_protocol(G, vessel, "argon", **kwargs)
|
||||||
|
|
||||||
def generate_air_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
def generate_air_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""生成空气置换协议"""
|
"""生成空气置换协议"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
debug_print(f"💨 生成空气置换协议: {vessel_id}")
|
|
||||||
return generate_evacuateandrefill_protocol(G, vessel, "air", **kwargs)
|
return generate_evacuateandrefill_protocol(G, vessel, "air", **kwargs)
|
||||||
|
|
||||||
def generate_inert_atmosphere_protocol(G: nx.DiGraph, vessel: dict, gas: str = "nitrogen", **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
def generate_inert_atmosphere_protocol(G: nx.DiGraph, vessel: dict, gas: str = "nitrogen", **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""生成惰性气氛协议"""
|
"""生成惰性气氛协议"""
|
||||||
vessel_id = vessel["id"]
|
|
||||||
debug_print(f"🛡️ 生成惰性气氛协议: {vessel_id} (使用 {gas})")
|
|
||||||
return generate_evacuateandrefill_protocol(G, vessel, gas, **kwargs)
|
return generate_evacuateandrefill_protocol(G, vessel, gas, **kwargs)
|
||||||
|
|
||||||
# 测试函数
|
# 测试函数
|
||||||
def test_evacuateandrefill_protocol():
|
def test_evacuateandrefill_protocol():
|
||||||
"""测试抽真空充气协议"""
|
"""测试抽真空充气协议"""
|
||||||
debug_print("=== 抽真空充气协议增强中文版测试 ===")
|
debug_print("=== 抽真空充气协议测试 ===")
|
||||||
debug_print("✅ 测试完成")
|
debug_print("测试完成")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_evacuateandrefill_protocol()
|
test_evacuateandrefill_protocol()
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
# import numpy as np
|
|
||||||
# import networkx as nx
|
|
||||||
|
|
||||||
|
|
||||||
# def generate_evacuateandrefill_protocol(
|
|
||||||
# G: nx.DiGraph,
|
|
||||||
# vessel: str,
|
|
||||||
# gas: str,
|
|
||||||
# repeats: int = 1
|
|
||||||
# ) -> list[dict]:
|
|
||||||
# """
|
|
||||||
# 生成泵操作的动作序列。
|
|
||||||
|
|
||||||
# :param G: 有向图, 节点为容器和注射泵, 边为流体管道, A→B边的属性为管道接A端的阀门位置
|
|
||||||
# :param from_vessel: 容器A
|
|
||||||
# :param to_vessel: 容器B
|
|
||||||
# :param volume: 转移的体积
|
|
||||||
# :param flowrate: 最终注入容器B时的流速
|
|
||||||
# :param transfer_flowrate: 泵骨架中转移流速(若不指定,默认与注入流速相同)
|
|
||||||
# :return: 泵操作的动作序列
|
|
||||||
# """
|
|
||||||
|
|
||||||
# # 生成电磁阀、真空泵、气源操作的动作序列
|
|
||||||
# vacuum_action_sequence = []
|
|
||||||
# nodes = G.nodes(data=True)
|
|
||||||
|
|
||||||
# # 找到和 vessel 相连的电磁阀和真空泵、气源
|
|
||||||
# vacuum_backbone = {"vessel": vessel}
|
|
||||||
|
|
||||||
# for neighbor in G.neighbors(vessel):
|
|
||||||
# if nodes[neighbor]["class"].startswith("solenoid_valve"):
|
|
||||||
# for neighbor2 in G.neighbors(neighbor):
|
|
||||||
# if neighbor2 == vessel:
|
|
||||||
# continue
|
|
||||||
# if nodes[neighbor2]["class"].startswith("vacuum_pump"):
|
|
||||||
# vacuum_backbone.update({"vacuum_valve": neighbor, "pump": neighbor2})
|
|
||||||
# break
|
|
||||||
# elif nodes[neighbor2]["class"].startswith("gas_source"):
|
|
||||||
# vacuum_backbone.update({"gas_valve": neighbor, "gas": neighbor2})
|
|
||||||
# break
|
|
||||||
# # 判断是否设备齐全
|
|
||||||
# if len(vacuum_backbone) < 5:
|
|
||||||
# print(f"\n\n\n{vacuum_backbone}\n\n\n")
|
|
||||||
# raise ValueError("Not all devices are connected to the vessel.")
|
|
||||||
|
|
||||||
# # 生成操作的动作序列
|
|
||||||
# for i in range(repeats):
|
|
||||||
# # 打开真空泵阀门、关闭气源阀门
|
|
||||||
# vacuum_action_sequence.append([
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["vacuum_valve"],
|
|
||||||
# "action_name": "set_valve_position",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "command": "OPEN"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["gas_valve"],
|
|
||||||
# "action_name": "set_valve_position",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "command": "CLOSED"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# ])
|
|
||||||
|
|
||||||
# # 打开真空泵、关闭气源
|
|
||||||
# vacuum_action_sequence.append([
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["pump"],
|
|
||||||
# "action_name": "set_status",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "string": "ON"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["gas"],
|
|
||||||
# "action_name": "set_status",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "string": "OFF"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# ])
|
|
||||||
# vacuum_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 60}})
|
|
||||||
|
|
||||||
# # 关闭真空泵阀门、打开气源阀门
|
|
||||||
# vacuum_action_sequence.append([
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["vacuum_valve"],
|
|
||||||
# "action_name": "set_valve_position",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "command": "CLOSED"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["gas_valve"],
|
|
||||||
# "action_name": "set_valve_position",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "command": "OPEN"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# ])
|
|
||||||
|
|
||||||
# # 关闭真空泵、打开气源
|
|
||||||
# vacuum_action_sequence.append([
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["pump"],
|
|
||||||
# "action_name": "set_status",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "string": "OFF"
|
|
||||||
# }
|
|
||||||
# },
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["gas"],
|
|
||||||
# "action_name": "set_status",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "string": "ON"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# ])
|
|
||||||
# vacuum_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 60}})
|
|
||||||
|
|
||||||
# # 关闭气源
|
|
||||||
# vacuum_action_sequence.append(
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["gas"],
|
|
||||||
# "action_name": "set_status",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "string": "OFF"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
|
|
||||||
# # 关闭阀门
|
|
||||||
# vacuum_action_sequence.append(
|
|
||||||
# {
|
|
||||||
# "device_id": vacuum_backbone["gas_valve"],
|
|
||||||
# "action_name": "set_valve_position",
|
|
||||||
# "action_kwargs": {
|
|
||||||
# "command": "CLOSED"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# )
|
|
||||||
# return vacuum_action_sequence
|
|
||||||
@@ -4,13 +4,10 @@ import logging
|
|||||||
import re
|
import re
|
||||||
from .utils.vessel_parser import get_vessel
|
from .utils.vessel_parser import get_vessel
|
||||||
from .utils.unit_parser import parse_time_input
|
from .utils.unit_parser import parse_time_input
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[EVAPORATE] {message}")
|
|
||||||
|
|
||||||
|
|
||||||
def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
|
def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
@@ -23,102 +20,76 @@ def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
|
|||||||
Returns:
|
Returns:
|
||||||
str: 找到的旋转蒸发仪设备ID,如果没找到返回None
|
str: 找到的旋转蒸发仪设备ID,如果没找到返回None
|
||||||
"""
|
"""
|
||||||
debug_print("🔍 开始查找旋转蒸发仪设备... 🌪️")
|
|
||||||
|
|
||||||
# 如果指定了vessel,先检查是否存在且是旋转蒸发仪
|
# 如果指定了vessel,先检查是否存在且是旋转蒸发仪
|
||||||
if vessel:
|
if vessel:
|
||||||
debug_print(f"🎯 检查指定设备: {vessel} 🔧")
|
|
||||||
if vessel in G.nodes():
|
if vessel in G.nodes():
|
||||||
node_data = G.nodes[vessel]
|
node_data = G.nodes[vessel]
|
||||||
node_class = node_data.get('class', '')
|
node_class = node_data.get('class', '')
|
||||||
node_type = node_data.get('type', '')
|
node_type = node_data.get('type', '')
|
||||||
|
|
||||||
debug_print(f"📋 设备信息 {vessel}: class={node_class}, type={node_type}")
|
|
||||||
|
|
||||||
# 检查是否为旋转蒸发仪
|
|
||||||
if any(keyword in str(node_class).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
|
if any(keyword in str(node_class).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
|
||||||
debug_print(f"🎉 找到指定的旋转蒸发仪: {vessel} ✨")
|
debug_print(f"找到指定的旋转蒸发仪: {vessel}")
|
||||||
return vessel
|
return vessel
|
||||||
elif node_type == 'device':
|
elif node_type == 'device':
|
||||||
debug_print(f"✅ 指定设备存在,尝试直接使用: {vessel} 🔧")
|
debug_print(f"指定设备存在,尝试直接使用: {vessel}")
|
||||||
return vessel
|
return vessel
|
||||||
else:
|
|
||||||
debug_print(f"❌ 指定的设备 {vessel} 不存在 😞")
|
|
||||||
|
|
||||||
# 在所有设备中查找旋转蒸发仪
|
# 在所有设备中查找旋转蒸发仪
|
||||||
debug_print("🔎 在所有设备中搜索旋转蒸发仪... 🕵️♀️")
|
|
||||||
rotavap_candidates = []
|
rotavap_candidates = []
|
||||||
|
|
||||||
for node_id, node_data in G.nodes(data=True):
|
for node_id, node_data in G.nodes(data=True):
|
||||||
node_class = node_data.get('class', '')
|
node_class = node_data.get('class', '')
|
||||||
node_type = node_data.get('type', '')
|
node_type = node_data.get('type', '')
|
||||||
|
|
||||||
# 跳过非设备节点
|
|
||||||
if node_type != 'device':
|
if node_type != 'device':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# 检查设备类型
|
|
||||||
if any(keyword in str(node_class).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
|
if any(keyword in str(node_class).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
|
||||||
rotavap_candidates.append(node_id)
|
rotavap_candidates.append(node_id)
|
||||||
debug_print(f"🌟 找到旋转蒸发仪候选: {node_id} (class: {node_class}) 🌪️")
|
|
||||||
elif any(keyword in str(node_id).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
|
elif any(keyword in str(node_id).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
|
||||||
rotavap_candidates.append(node_id)
|
rotavap_candidates.append(node_id)
|
||||||
debug_print(f"🌟 找到旋转蒸发仪候选 (按名称): {node_id} 🌪️")
|
|
||||||
|
|
||||||
if rotavap_candidates:
|
if rotavap_candidates:
|
||||||
selected = rotavap_candidates[0] # 选择第一个找到的
|
selected = rotavap_candidates[0]
|
||||||
debug_print(f"🎯 选择旋转蒸发仪: {selected} 🏆")
|
debug_print(f"选择旋转蒸发仪: {selected}")
|
||||||
return selected
|
return selected
|
||||||
|
|
||||||
debug_print("😭 未找到旋转蒸发仪设备 💔")
|
debug_print("未找到旋转蒸发仪设备")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def find_connected_vessel(G: nx.DiGraph, rotavap_device: str) -> Optional[str]:
|
def find_connected_vessel(G: nx.DiGraph, rotavap_device: str) -> Optional[str]:
|
||||||
"""
|
"""
|
||||||
查找与旋转蒸发仪连接的容器
|
查找与旋转蒸发仪连接的容器
|
||||||
|
|
||||||
Args:
|
|
||||||
G: 设备图
|
|
||||||
rotavap_device: 旋转蒸发仪设备ID
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 连接的容器ID,如果没找到返回None
|
|
||||||
"""
|
"""
|
||||||
debug_print(f"🔗 查找与 {rotavap_device} 连接的容器... 🥽")
|
|
||||||
|
|
||||||
# 查看旋转蒸发仪的子设备
|
|
||||||
rotavap_data = G.nodes[rotavap_device]
|
rotavap_data = G.nodes[rotavap_device]
|
||||||
children = rotavap_data.get('children', [])
|
children = rotavap_data.get('children', [])
|
||||||
|
|
||||||
debug_print(f"👶 检查子设备: {children}")
|
|
||||||
for child_id in children:
|
for child_id in children:
|
||||||
if child_id in G.nodes():
|
if child_id in G.nodes():
|
||||||
child_data = G.nodes[child_id]
|
child_data = G.nodes[child_id]
|
||||||
child_type = child_data.get('type', '')
|
child_type = child_data.get('type', '')
|
||||||
|
|
||||||
if child_type == 'container':
|
if child_type == 'container':
|
||||||
debug_print(f"🎉 找到连接的容器: {child_id} 🥽✨")
|
debug_print(f"找到连接的容器: {child_id}")
|
||||||
return child_id
|
return child_id
|
||||||
|
|
||||||
# 查看邻接的容器
|
|
||||||
debug_print("🤝 检查邻接设备...")
|
|
||||||
for neighbor in G.neighbors(rotavap_device):
|
for neighbor in G.neighbors(rotavap_device):
|
||||||
neighbor_data = G.nodes[neighbor]
|
neighbor_data = G.nodes[neighbor]
|
||||||
neighbor_type = neighbor_data.get('type', '')
|
neighbor_type = neighbor_data.get('type', '')
|
||||||
|
|
||||||
if neighbor_type == 'container':
|
if neighbor_type == 'container':
|
||||||
debug_print(f"🎉 找到邻接的容器: {neighbor} 🥽✨")
|
debug_print(f"找到邻接的容器: {neighbor}")
|
||||||
return neighbor
|
return neighbor
|
||||||
|
|
||||||
debug_print("😞 未找到连接的容器 💔")
|
debug_print("未找到连接的容器")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def generate_evaporate_protocol(
|
def generate_evaporate_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
pressure: float = 0.1,
|
pressure: float = 0.1,
|
||||||
temp: float = 60.0,
|
temp: float = 60.0,
|
||||||
time: Union[str, float] = "180", # 🔧 修改:支持字符串时间
|
time: Union[str, float] = "180",
|
||||||
stir_speed: float = 100.0,
|
stir_speed: float = 100.0,
|
||||||
solvent: str = "",
|
solvent: str = "",
|
||||||
**kwargs
|
**kwargs
|
||||||
@@ -140,22 +111,11 @@ def generate_evaporate_protocol(
|
|||||||
List[Dict[str, Any]]: 动作序列
|
List[Dict[str, Any]]: 动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
debug_print("🌟" * 20)
|
debug_print(f"开始生成蒸发协议: vessel={vessel_id}, pressure={pressure}, temp={temp}, time={time}")
|
||||||
debug_print("🌪️ 开始生成蒸发协议(支持单位和体积运算)✨")
|
|
||||||
debug_print(f"📝 输入参数:")
|
|
||||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
|
||||||
debug_print(f" 💨 pressure: {pressure} bar")
|
|
||||||
debug_print(f" 🌡️ temp: {temp}°C")
|
|
||||||
debug_print(f" ⏰ time: {time} (类型: {type(time)})")
|
|
||||||
debug_print(f" 🌪️ stir_speed: {stir_speed} RPM")
|
|
||||||
debug_print(f" 🧪 solvent: '{solvent}'")
|
|
||||||
debug_print("🌟" * 20)
|
|
||||||
|
|
||||||
# 🔧 新增:记录蒸发前的容器状态
|
# 记录蒸发前的容器状态
|
||||||
debug_print("🔍 记录蒸发前容器状态...")
|
|
||||||
original_liquid_volume = 0.0
|
original_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -163,168 +123,97 @@ def generate_evaporate_protocol(
|
|||||||
original_liquid_volume = current_volume[0]
|
original_liquid_volume = current_volume[0]
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
original_liquid_volume = current_volume
|
original_liquid_volume = current_volume
|
||||||
debug_print(f"📊 蒸发前液体体积: {original_liquid_volume:.2f}mL")
|
|
||||||
|
|
||||||
# === 步骤1: 查找旋转蒸发仪设备 ===
|
|
||||||
debug_print("📍 步骤1: 查找旋转蒸发仪设备... 🔍")
|
|
||||||
|
|
||||||
# 验证vessel参数
|
|
||||||
if not vessel_id:
|
|
||||||
debug_print("❌ vessel 参数不能为空! 😱")
|
|
||||||
raise ValueError("vessel 参数不能为空")
|
|
||||||
|
|
||||||
# 查找旋转蒸发仪设备
|
# 查找旋转蒸发仪设备
|
||||||
|
if not vessel_id:
|
||||||
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
rotavap_device = find_rotavap_device(G, vessel_id)
|
rotavap_device = find_rotavap_device(G, vessel_id)
|
||||||
if not rotavap_device:
|
if not rotavap_device:
|
||||||
debug_print("💥 未找到旋转蒸发仪设备! 😭")
|
|
||||||
raise ValueError(f"未找到旋转蒸发仪设备。请检查组态图中是否包含 class 包含 'rotavap'、'rotary' 或 'evaporat' 的设备")
|
raise ValueError(f"未找到旋转蒸发仪设备。请检查组态图中是否包含 class 包含 'rotavap'、'rotary' 或 'evaporat' 的设备")
|
||||||
|
|
||||||
debug_print(f"🎉 成功找到旋转蒸发仪: {rotavap_device} ✨")
|
# 确定目标容器
|
||||||
|
|
||||||
# === 步骤2: 确定目标容器 ===
|
|
||||||
debug_print("📍 步骤2: 确定目标容器... 🥽")
|
|
||||||
|
|
||||||
target_vessel = vessel_id
|
target_vessel = vessel_id
|
||||||
|
|
||||||
# 如果vessel就是旋转蒸发仪设备,查找连接的容器
|
|
||||||
if vessel_id == rotavap_device:
|
if vessel_id == rotavap_device:
|
||||||
debug_print("🔄 vessel就是旋转蒸发仪,查找连接的容器...")
|
|
||||||
connected_vessel = find_connected_vessel(G, rotavap_device)
|
connected_vessel = find_connected_vessel(G, rotavap_device)
|
||||||
if connected_vessel:
|
if connected_vessel:
|
||||||
target_vessel = connected_vessel
|
target_vessel = connected_vessel
|
||||||
debug_print(f"✅ 使用连接的容器: {target_vessel} 🥽✨")
|
|
||||||
else:
|
else:
|
||||||
debug_print(f"⚠️ 未找到连接的容器,使用设备本身: {rotavap_device} 🔧")
|
|
||||||
target_vessel = rotavap_device
|
target_vessel = rotavap_device
|
||||||
elif vessel_id in G.nodes() and G.nodes[vessel_id].get('type') == 'container':
|
elif vessel_id in G.nodes() and G.nodes[vessel_id].get('type') == 'container':
|
||||||
debug_print(f"✅ 使用指定的容器: {vessel_id} 🥽✨")
|
|
||||||
target_vessel = vessel_id
|
target_vessel = vessel_id
|
||||||
else:
|
else:
|
||||||
debug_print(f"⚠️ 容器 '{vessel_id}' 不存在或类型不正确,使用旋转蒸发仪设备: {rotavap_device} 🔧")
|
|
||||||
target_vessel = rotavap_device
|
target_vessel = rotavap_device
|
||||||
|
|
||||||
# === 🔧 新增:步骤3:单位解析处理 ===
|
# 单位解析处理
|
||||||
debug_print("📍 步骤3: 单位解析处理... ⚡")
|
|
||||||
|
|
||||||
# 解析时间
|
|
||||||
final_time = parse_time_input(time)
|
final_time = parse_time_input(time)
|
||||||
debug_print(f"🎯 时间解析完成: {time} → {final_time}s ({final_time/60:.1f}分钟) ⏰✨")
|
debug_print(f"时间解析: {time} -> {final_time}s ({final_time/60:.1f}分钟)")
|
||||||
|
|
||||||
# === 步骤4: 参数验证和修正 ===
|
# 参数验证和修正
|
||||||
debug_print("📍 步骤4: 参数验证和修正... 🔧")
|
|
||||||
|
|
||||||
# 修正参数范围
|
|
||||||
if pressure <= 0 or pressure > 1.0:
|
if pressure <= 0 or pressure > 1.0:
|
||||||
debug_print(f"⚠️ 真空度 {pressure} bar 超出范围,修正为 0.1 bar 💨")
|
|
||||||
pressure = 0.1
|
pressure = 0.1
|
||||||
else:
|
|
||||||
debug_print(f"✅ 真空度 {pressure} bar 在正常范围内 💨")
|
|
||||||
|
|
||||||
if temp < 10.0 or temp > 200.0:
|
if temp < 10.0 or temp > 200.0:
|
||||||
debug_print(f"⚠️ 温度 {temp}°C 超出范围,修正为 60°C 🌡️")
|
|
||||||
temp = 60.0
|
temp = 60.0
|
||||||
else:
|
|
||||||
debug_print(f"✅ 温度 {temp}°C 在正常范围内 🌡️")
|
|
||||||
|
|
||||||
if final_time <= 0:
|
if final_time <= 0:
|
||||||
debug_print(f"⚠️ 时间 {final_time}s 无效,修正为 180s (3分钟) ⏰")
|
|
||||||
final_time = 180.0
|
final_time = 180.0
|
||||||
else:
|
|
||||||
debug_print(f"✅ 时间 {final_time}s ({final_time/60:.1f}分钟) 有效 ⏰")
|
|
||||||
|
|
||||||
if stir_speed < 10.0 or stir_speed > 300.0:
|
if stir_speed < 10.0 or stir_speed > 300.0:
|
||||||
debug_print(f"⚠️ 旋转速度 {stir_speed} RPM 超出范围,修正为 100 RPM 🌪️")
|
|
||||||
stir_speed = 100.0
|
stir_speed = 100.0
|
||||||
else:
|
|
||||||
debug_print(f"✅ 旋转速度 {stir_speed} RPM 在正常范围内 🌪️")
|
|
||||||
|
|
||||||
# 根据溶剂优化参数
|
# 根据溶剂优化参数
|
||||||
if solvent:
|
if solvent:
|
||||||
debug_print(f"🧪 根据溶剂 '{solvent}' 优化参数... 🔬")
|
|
||||||
solvent_lower = solvent.lower()
|
solvent_lower = solvent.lower()
|
||||||
|
|
||||||
if any(s in solvent_lower for s in ['water', 'aqueous', 'h2o']):
|
if any(s in solvent_lower for s in ['water', 'aqueous', 'h2o']):
|
||||||
temp = max(temp, 80.0)
|
temp = max(temp, 80.0)
|
||||||
pressure = max(pressure, 0.2)
|
pressure = max(pressure, 0.2)
|
||||||
debug_print("💧 水系溶剂:提高温度和真空度 🌡️💨")
|
|
||||||
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
||||||
temp = min(temp, 50.0)
|
temp = min(temp, 50.0)
|
||||||
pressure = min(pressure, 0.05)
|
pressure = min(pressure, 0.05)
|
||||||
debug_print("🍺 易挥发溶剂:降低温度和真空度 🌡️💨")
|
|
||||||
elif any(s in solvent_lower for s in ['dmso', 'dmi', 'toluene']):
|
elif any(s in solvent_lower for s in ['dmso', 'dmi', 'toluene']):
|
||||||
temp = max(temp, 100.0)
|
temp = max(temp, 100.0)
|
||||||
pressure = min(pressure, 0.01)
|
pressure = min(pressure, 0.01)
|
||||||
debug_print("🔥 高沸点溶剂:提高温度,降低真空度 🌡️💨")
|
|
||||||
else:
|
|
||||||
debug_print("🧪 通用溶剂,使用标准参数 ✨")
|
|
||||||
else:
|
|
||||||
debug_print("🤷♀️ 未指定溶剂,使用默认参数 ✨")
|
|
||||||
|
|
||||||
debug_print(f"🎯 最终参数: pressure={pressure} bar 💨, temp={temp}°C 🌡️, time={final_time}s ⏰, stir_speed={stir_speed} RPM 🌪️")
|
debug_print(f"最终参数: pressure={pressure}bar, temp={temp}°C, time={final_time}s, stir_speed={stir_speed}RPM")
|
||||||
|
|
||||||
# === 🔧 新增:步骤5:蒸发体积计算 ===
|
# 蒸发体积计算
|
||||||
debug_print("📍 步骤5: 蒸发体积计算... 📊")
|
|
||||||
|
|
||||||
# 根据温度、真空度、时间和溶剂类型估算蒸发量
|
|
||||||
evaporation_volume = 0.0
|
evaporation_volume = 0.0
|
||||||
if original_liquid_volume > 0:
|
if original_liquid_volume > 0:
|
||||||
# 基础蒸发速率(mL/min)
|
base_evap_rate = 0.5
|
||||||
base_evap_rate = 0.5 # 基础速率
|
|
||||||
|
|
||||||
# 温度系数(高温蒸发更快)
|
|
||||||
temp_factor = 1.0 + (temp - 25.0) / 100.0
|
temp_factor = 1.0 + (temp - 25.0) / 100.0
|
||||||
|
|
||||||
# 真空系数(真空度越高蒸发越快)
|
|
||||||
vacuum_factor = 1.0 + (1.0 - pressure) * 2.0
|
vacuum_factor = 1.0 + (1.0 - pressure) * 2.0
|
||||||
|
|
||||||
# 溶剂系数
|
|
||||||
solvent_factor = 1.0
|
solvent_factor = 1.0
|
||||||
if solvent:
|
if solvent:
|
||||||
solvent_lower = solvent.lower()
|
solvent_lower = solvent.lower()
|
||||||
if any(s in solvent_lower for s in ['water', 'h2o']):
|
if any(s in solvent_lower for s in ['water', 'h2o']):
|
||||||
solvent_factor = 0.8 # 水蒸发较慢
|
solvent_factor = 0.8
|
||||||
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
||||||
solvent_factor = 1.5 # 易挥发溶剂蒸发快
|
solvent_factor = 1.5
|
||||||
elif any(s in solvent_lower for s in ['dmso', 'dmi']):
|
elif any(s in solvent_lower for s in ['dmso', 'dmi']):
|
||||||
solvent_factor = 0.3 # 高沸点溶剂蒸发慢
|
solvent_factor = 0.3
|
||||||
|
|
||||||
# 计算总蒸发量
|
|
||||||
total_evap_rate = base_evap_rate * temp_factor * vacuum_factor * solvent_factor
|
total_evap_rate = base_evap_rate * temp_factor * vacuum_factor * solvent_factor
|
||||||
evaporation_volume = min(
|
evaporation_volume = min(
|
||||||
original_liquid_volume * 0.95, # 最多蒸发95%
|
original_liquid_volume * 0.95,
|
||||||
total_evap_rate * (final_time / 60.0) # 时间相关的蒸发量
|
total_evap_rate * (final_time / 60.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
debug_print(f"📊 蒸发量计算:")
|
debug_print(f"预计蒸发量: {evaporation_volume:.2f}mL ({evaporation_volume/original_liquid_volume*100:.1f}%)")
|
||||||
debug_print(f" - 基础蒸发速率: {base_evap_rate} mL/min")
|
|
||||||
debug_print(f" - 温度系数: {temp_factor:.2f} (基于 {temp}°C)")
|
|
||||||
debug_print(f" - 真空系数: {vacuum_factor:.2f} (基于 {pressure} bar)")
|
|
||||||
debug_print(f" - 溶剂系数: {solvent_factor:.2f} ({solvent or '通用'})")
|
|
||||||
debug_print(f" - 总蒸发速率: {total_evap_rate:.2f} mL/min")
|
|
||||||
debug_print(f" - 预计蒸发量: {evaporation_volume:.2f}mL ({evaporation_volume/original_liquid_volume*100:.1f}%)")
|
|
||||||
|
|
||||||
# === 步骤6: 生成动作序列 ===
|
|
||||||
debug_print("📍 步骤6: 生成动作序列... 🎬")
|
|
||||||
|
|
||||||
|
# 生成动作序列
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 1. 等待稳定
|
# 1. 等待稳定
|
||||||
debug_print(" 🔄 动作1: 添加初始等待稳定... ⏳")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 10}
|
"action_kwargs": {"time": 10}
|
||||||
})
|
})
|
||||||
debug_print(" ✅ 初始等待动作已添加 ⏳✨")
|
|
||||||
|
|
||||||
# 2. 执行蒸发
|
# 2. 执行蒸发
|
||||||
debug_print(f" 🌪️ 动作2: 执行蒸发操作...")
|
|
||||||
debug_print(f" 🔧 设备: {rotavap_device}")
|
|
||||||
debug_print(f" 🥽 容器: {target_vessel}")
|
|
||||||
debug_print(f" 💨 真空度: {pressure} bar")
|
|
||||||
debug_print(f" 🌡️ 温度: {temp}°C")
|
|
||||||
debug_print(f" ⏰ 时间: {final_time}s ({final_time/60:.1f}分钟)")
|
|
||||||
debug_print(f" 🌪️ 旋转速度: {stir_speed} RPM")
|
|
||||||
|
|
||||||
evaporate_action = {
|
evaporate_action = {
|
||||||
"device_id": rotavap_device,
|
"device_id": rotavap_device,
|
||||||
"action_name": "evaporate",
|
"action_name": "evaporate",
|
||||||
@@ -332,20 +221,17 @@ def generate_evaporate_protocol(
|
|||||||
"vessel": {"id": target_vessel},
|
"vessel": {"id": target_vessel},
|
||||||
"pressure": float(pressure),
|
"pressure": float(pressure),
|
||||||
"temp": float(temp),
|
"temp": float(temp),
|
||||||
"time": float(final_time), # 🔧 强制转换为float类型
|
"time": float(final_time),
|
||||||
"stir_speed": float(stir_speed),
|
"stir_speed": float(stir_speed),
|
||||||
"solvent": str(solvent)
|
"solvent": str(solvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(evaporate_action)
|
action_sequence.append(evaporate_action)
|
||||||
debug_print(" ✅ 蒸发动作已添加 🌪️✨")
|
|
||||||
|
|
||||||
# 🔧 新增:蒸发过程中的体积变化
|
# 蒸发过程中的体积变化
|
||||||
debug_print(" 🔧 更新容器体积 - 蒸发过程...")
|
|
||||||
if evaporation_volume > 0:
|
if evaporation_volume > 0:
|
||||||
new_volume = max(0.0, original_liquid_volume - evaporation_volume)
|
new_volume = max(0.0, original_liquid_volume - evaporation_volume)
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -358,7 +244,6 @@ def generate_evaporate_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
vessel["data"]["liquid_volume"] = new_volume
|
||||||
|
|
||||||
# 🔧 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
@@ -374,17 +259,15 @@ def generate_evaporate_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
||||||
|
|
||||||
debug_print(f" 📊 蒸发体积变化: {original_liquid_volume:.2f}mL → {new_volume:.2f}mL (-{evaporation_volume:.2f}mL)")
|
debug_print(f"蒸发体积变化: {original_liquid_volume:.2f}mL -> {new_volume:.2f}mL (-{evaporation_volume:.2f}mL)")
|
||||||
|
|
||||||
# 3. 蒸发后等待
|
# 3. 蒸发后等待
|
||||||
debug_print(" 🔄 动作3: 添加蒸发后等待... ⏳")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 10}
|
"action_kwargs": {"time": 10}
|
||||||
})
|
})
|
||||||
debug_print(" ✅ 蒸发后等待动作已添加 ⏳✨")
|
|
||||||
|
|
||||||
# 🔧 新增:蒸发完成后的状态报告
|
# 最终状态
|
||||||
final_liquid_volume = 0.0
|
final_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -393,18 +276,6 @@ def generate_evaporate_protocol(
|
|||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
final_liquid_volume = current_volume
|
final_liquid_volume = current_volume
|
||||||
|
|
||||||
# === 总结 ===
|
debug_print(f"蒸发协议生成完成: {len(action_sequence)} 个动作, 设备={rotavap_device}, 容器={target_vessel}")
|
||||||
debug_print("🎊" * 20)
|
|
||||||
debug_print(f"🎉 蒸发协议生成完成! ✨")
|
|
||||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个 📝")
|
|
||||||
debug_print(f"🌪️ 旋转蒸发仪: {rotavap_device} 🔧")
|
|
||||||
debug_print(f"🥽 目标容器: {target_vessel} 🧪")
|
|
||||||
debug_print(f"⚙️ 蒸发参数: {pressure} bar 💨, {temp}°C 🌡️, {final_time}s ⏰, {stir_speed} RPM 🌪️")
|
|
||||||
debug_print(f"⏱️ 预计总时间: {(final_time + 20)/60:.1f} 分钟 ⌛")
|
|
||||||
debug_print(f"📊 体积变化:")
|
|
||||||
debug_print(f" - 蒸发前: {original_liquid_volume:.2f}mL")
|
|
||||||
debug_print(f" - 蒸发后: {final_liquid_volume:.2f}mL")
|
|
||||||
debug_print(f" - 蒸发量: {evaporation_volume:.2f}mL ({evaporation_volume/max(original_liquid_volume, 0.01)*100:.1f}%)")
|
|
||||||
debug_print("🎊" * 20)
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|||||||
@@ -2,55 +2,40 @@ from typing import List, Dict, Any, Optional
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
from .utils.vessel_parser import get_vessel
|
from .utils.vessel_parser import get_vessel
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[FILTER] {message}")
|
|
||||||
|
|
||||||
def find_filter_device(G: nx.DiGraph) -> str:
|
def find_filter_device(G: nx.DiGraph) -> str:
|
||||||
"""查找过滤器设备"""
|
"""查找过滤器设备"""
|
||||||
debug_print("🔍 查找过滤器设备... 🌊")
|
|
||||||
|
|
||||||
# 查找过滤器设备
|
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
node_data = G.nodes[node]
|
node_data = G.nodes[node]
|
||||||
node_class = node_data.get('class', '') or ''
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
if 'filter' in node_class.lower() or 'filter' in node.lower():
|
if 'filter' in node_class.lower() or 'filter' in node.lower():
|
||||||
debug_print(f"🎉 找到过滤器设备: {node} ✨")
|
debug_print(f"找到过滤器设备: {node}")
|
||||||
return node
|
return node
|
||||||
|
|
||||||
# 如果没找到,寻找可能的过滤器名称
|
|
||||||
debug_print("🔎 在预定义名称中搜索过滤器... 📋")
|
|
||||||
possible_names = ["filter", "filter_1", "virtual_filter", "filtration_unit"]
|
possible_names = ["filter", "filter_1", "virtual_filter", "filtration_unit"]
|
||||||
for name in possible_names:
|
for name in possible_names:
|
||||||
if name in G.nodes():
|
if name in G.nodes():
|
||||||
debug_print(f"🎉 找到过滤器设备: {name} ✨")
|
debug_print(f"找到过滤器设备: {name}")
|
||||||
return name
|
return name
|
||||||
|
|
||||||
debug_print("😭 未找到过滤器设备 💔")
|
|
||||||
raise ValueError("未找到过滤器设备")
|
raise ValueError("未找到过滤器设备")
|
||||||
|
|
||||||
def validate_vessel(G: nx.DiGraph, vessel: str, vessel_type: str = "容器") -> None:
|
def validate_vessel(G: nx.DiGraph, vessel: str, vessel_type: str = "容器") -> None:
|
||||||
"""验证容器是否存在"""
|
"""验证容器是否存在"""
|
||||||
debug_print(f"🔍 验证{vessel_type}: '{vessel}' 🧪")
|
|
||||||
|
|
||||||
if not vessel:
|
if not vessel:
|
||||||
debug_print(f"❌ {vessel_type}不能为空! 😱")
|
|
||||||
raise ValueError(f"{vessel_type}不能为空")
|
raise ValueError(f"{vessel_type}不能为空")
|
||||||
|
|
||||||
if vessel not in G.nodes():
|
if vessel not in G.nodes():
|
||||||
debug_print(f"❌ {vessel_type} '{vessel}' 不存在于系统中! 😞")
|
|
||||||
raise ValueError(f"{vessel_type} '{vessel}' 不存在于系统中")
|
raise ValueError(f"{vessel_type} '{vessel}' 不存在于系统中")
|
||||||
|
|
||||||
debug_print(f"✅ {vessel_type} '{vessel}' 验证通过 🎯")
|
|
||||||
|
|
||||||
def generate_filter_protocol(
|
def generate_filter_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
filtrate_vessel: dict = {"id": "waste"},
|
filtrate_vessel: dict = {"id": "waste"},
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
@@ -67,22 +52,14 @@ def generate_filter_protocol(
|
|||||||
List[Dict[str, Any]]: 过滤操作的动作序列
|
List[Dict[str, Any]]: 过滤操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
filtrate_vessel_id, filtrate_vessel_data = get_vessel(filtrate_vessel)
|
filtrate_vessel_id, filtrate_vessel_data = get_vessel(filtrate_vessel)
|
||||||
|
|
||||||
debug_print("🌊" * 20)
|
debug_print(f"开始生成过滤协议: vessel={vessel_id}, filtrate_vessel={filtrate_vessel_id}")
|
||||||
debug_print("🚀 开始生成过滤协议(支持体积运算)✨")
|
|
||||||
debug_print(f"📝 输入参数:")
|
|
||||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
|
||||||
debug_print(f" 🧪 filtrate_vessel: {filtrate_vessel}")
|
|
||||||
debug_print(f" ⚙️ 其他参数: {kwargs}")
|
|
||||||
debug_print("🌊" * 20)
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 🔧 新增:记录过滤前的容器状态
|
# 记录过滤前的容器状态
|
||||||
debug_print("🔍 记录过滤前容器状态...")
|
|
||||||
original_liquid_volume = 0.0
|
original_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -90,79 +67,45 @@ def generate_filter_protocol(
|
|||||||
original_liquid_volume = current_volume[0]
|
original_liquid_volume = current_volume[0]
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
original_liquid_volume = current_volume
|
original_liquid_volume = current_volume
|
||||||
debug_print(f"📊 过滤前液体体积: {original_liquid_volume:.2f}mL")
|
|
||||||
|
|
||||||
# === 参数验证 ===
|
# === 参数验证 ===
|
||||||
debug_print("📍 步骤1: 参数验证... 🔧")
|
validate_vessel(G, vessel_id, "过滤容器")
|
||||||
|
|
||||||
# 验证必需参数
|
|
||||||
debug_print(" 🔍 验证必需参数...")
|
|
||||||
validate_vessel(G, vessel_id, "过滤容器") # 🔧 使用 vessel_id
|
|
||||||
debug_print(" ✅ 必需参数验证完成 🎯")
|
|
||||||
|
|
||||||
# 验证可选参数
|
|
||||||
debug_print(" 🔍 验证可选参数...")
|
|
||||||
if filtrate_vessel:
|
if filtrate_vessel:
|
||||||
validate_vessel(G, filtrate_vessel_id, "滤液容器")
|
validate_vessel(G, filtrate_vessel_id, "滤液容器")
|
||||||
debug_print(" 🌊 模式: 过滤并收集滤液 💧")
|
|
||||||
else:
|
|
||||||
debug_print(" 🧱 模式: 过滤并收集固体 🔬")
|
|
||||||
debug_print(" ✅ 可选参数验证完成 🎯")
|
|
||||||
|
|
||||||
# === 查找设备 ===
|
# === 查找设备 ===
|
||||||
debug_print("📍 步骤2: 查找设备... 🔍")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug_print(" 🔎 搜索过滤器设备...")
|
|
||||||
filter_device = find_filter_device(G)
|
filter_device = find_filter_device(G)
|
||||||
debug_print(f" 🎉 使用过滤器设备: {filter_device} 🌊✨")
|
debug_print(f"使用过滤器设备: {filter_device}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f" ❌ 设备查找失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"设备查找失败: {str(e)}")
|
raise ValueError(f"设备查找失败: {str(e)}")
|
||||||
|
|
||||||
# 🔧 新增:过滤效率和体积分配估算
|
# 过滤体积分配估算
|
||||||
debug_print("📍 步骤2.5: 过滤体积分配估算... 📊")
|
solid_ratio = 0.1
|
||||||
|
liquid_ratio = 0.9
|
||||||
|
volume_loss_ratio = 0.05
|
||||||
|
|
||||||
# 估算过滤分离比例(基于经验数据)
|
|
||||||
solid_ratio = 0.1 # 假设10%是固体(保留在过滤器上)
|
|
||||||
liquid_ratio = 0.9 # 假设90%是液体(通过过滤器)
|
|
||||||
volume_loss_ratio = 0.05 # 假设5%体积损失(残留在过滤器等)
|
|
||||||
|
|
||||||
# 从kwargs中获取过滤参数进行优化
|
|
||||||
if "solid_content" in kwargs:
|
if "solid_content" in kwargs:
|
||||||
try:
|
try:
|
||||||
solid_ratio = float(kwargs["solid_content"])
|
solid_ratio = float(kwargs["solid_content"])
|
||||||
liquid_ratio = 1.0 - solid_ratio
|
liquid_ratio = 1.0 - solid_ratio
|
||||||
debug_print(f"📋 使用指定的固体含量: {solid_ratio*100:.1f}%")
|
|
||||||
except:
|
except:
|
||||||
debug_print("⚠️ 固体含量参数无效,使用默认值")
|
pass
|
||||||
|
|
||||||
if original_liquid_volume > 0:
|
if original_liquid_volume > 0:
|
||||||
expected_filtrate_volume = original_liquid_volume * liquid_ratio * (1.0 - volume_loss_ratio)
|
expected_filtrate_volume = original_liquid_volume * liquid_ratio * (1.0 - volume_loss_ratio)
|
||||||
expected_solid_volume = original_liquid_volume * solid_ratio
|
expected_solid_volume = original_liquid_volume * solid_ratio
|
||||||
volume_loss = original_liquid_volume * volume_loss_ratio
|
volume_loss = original_liquid_volume * volume_loss_ratio
|
||||||
|
|
||||||
debug_print(f"📊 过滤体积分配估算:")
|
|
||||||
debug_print(f" - 原始体积: {original_liquid_volume:.2f}mL")
|
|
||||||
debug_print(f" - 预计滤液体积: {expected_filtrate_volume:.2f}mL ({liquid_ratio*100:.1f}%)")
|
|
||||||
debug_print(f" - 预计固体体积: {expected_solid_volume:.2f}mL ({solid_ratio*100:.1f}%)")
|
|
||||||
debug_print(f" - 预计损失体积: {volume_loss:.2f}mL ({volume_loss_ratio*100:.1f}%)")
|
|
||||||
|
|
||||||
# === 转移到过滤器(如果需要)===
|
# === 转移到过滤器(如果需要)===
|
||||||
debug_print("📍 步骤3: 转移到过滤器... 🚚")
|
if vessel_id != filter_device:
|
||||||
|
|
||||||
if vessel_id != filter_device: # 🔧 使用 vessel_id
|
|
||||||
debug_print(f" 🚛 需要转移: {vessel_id} → {filter_device} 📦")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug_print(" 🔄 开始执行转移操作...")
|
|
||||||
# 使用pump protocol转移液体到过滤器
|
|
||||||
transfer_actions = generate_pump_protocol_with_rinsing(
|
transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel={"id": vessel_id}, # 🔧 使用 vessel_id
|
from_vessel={"id": vessel_id},
|
||||||
to_vessel={"id": filter_device},
|
to_vessel={"id": filter_device},
|
||||||
volume=0.0, # 转移所有液体
|
volume=0.0,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
viscous=False,
|
viscous=False,
|
||||||
@@ -176,12 +119,9 @@ def generate_filter_protocol(
|
|||||||
|
|
||||||
if transfer_actions:
|
if transfer_actions:
|
||||||
action_sequence.extend(transfer_actions)
|
action_sequence.extend(transfer_actions)
|
||||||
debug_print(f" ✅ 添加了 {len(transfer_actions)} 个转移动作 🚚✨")
|
debug_print(f"添加了 {len(transfer_actions)} 个转移动作")
|
||||||
|
|
||||||
# 🔧 新增:转移后更新容器体积
|
# 更新容器体积
|
||||||
debug_print(" 🔧 更新转移后的容器体积...")
|
|
||||||
|
|
||||||
# 原容器体积变为0(所有液体已转移)
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -189,72 +129,46 @@ def generate_filter_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = 0.0
|
vessel["data"]["liquid_volume"] = 0.0
|
||||||
|
|
||||||
# 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = 0.0
|
G.nodes[vessel_id]['data']['liquid_volume'] = 0.0
|
||||||
|
|
||||||
debug_print(f" 📊 转移完成,{vessel_id} 体积更新为 0.0mL")
|
|
||||||
|
|
||||||
else:
|
|
||||||
debug_print(" ⚠️ 转移协议返回空序列 🤔")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f" ❌ 转移失败: {str(e)} 😞")
|
debug_print(f"转移失败: {str(e)},继续执行")
|
||||||
debug_print(" 🔄 继续执行,可能是直接连接的过滤器 🤞")
|
|
||||||
else:
|
|
||||||
debug_print(" ✅ 过滤容器就是过滤器,无需转移 🎯")
|
|
||||||
|
|
||||||
# === 执行过滤操作 ===
|
# === 执行过滤操作 ===
|
||||||
debug_print("📍 步骤4: 执行过滤操作... 🌊")
|
|
||||||
|
|
||||||
# 构建过滤动作参数
|
|
||||||
debug_print(" ⚙️ 构建过滤参数...")
|
|
||||||
filter_kwargs = {
|
filter_kwargs = {
|
||||||
"vessel": {"id": filter_device}, # 过滤器设备
|
"vessel": {"id": filter_device},
|
||||||
"filtrate_vessel": {"id": filtrate_vessel_id}, # 滤液容器(可能为空)
|
"filtrate_vessel": {"id": filtrate_vessel_id},
|
||||||
"stir": kwargs.get("stir", False),
|
"stir": kwargs.get("stir", False),
|
||||||
"stir_speed": kwargs.get("stir_speed", 0.0),
|
"stir_speed": kwargs.get("stir_speed", 0.0),
|
||||||
"temp": kwargs.get("temp", 25.0),
|
"temp": kwargs.get("temp", 25.0),
|
||||||
"continue_heatchill": kwargs.get("continue_heatchill", False),
|
"continue_heatchill": kwargs.get("continue_heatchill", False),
|
||||||
"volume": kwargs.get("volume", 0.0) # 0表示过滤所有
|
"volume": kwargs.get("volume", 0.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_print(f" 📋 过滤参数: {filter_kwargs}")
|
|
||||||
debug_print(" 🌊 开始过滤操作...")
|
|
||||||
|
|
||||||
# 过滤动作
|
|
||||||
filter_action = {
|
filter_action = {
|
||||||
"device_id": filter_device,
|
"device_id": filter_device,
|
||||||
"action_name": "filter",
|
"action_name": "filter",
|
||||||
"action_kwargs": filter_kwargs
|
"action_kwargs": filter_kwargs
|
||||||
}
|
}
|
||||||
action_sequence.append(filter_action)
|
action_sequence.append(filter_action)
|
||||||
debug_print(" ✅ 过滤动作已添加 🌊✨")
|
|
||||||
|
|
||||||
# 过滤后等待
|
# 过滤后等待
|
||||||
debug_print(" ⏳ 添加过滤后等待...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 10.0}
|
"action_kwargs": {"time": 10.0}
|
||||||
})
|
})
|
||||||
debug_print(" ✅ 过滤后等待动作已添加 ⏰✨")
|
|
||||||
|
|
||||||
# === 收集滤液(如果需要)===
|
# === 收集滤液(如果需要)===
|
||||||
debug_print("📍 步骤5: 收集滤液... 💧")
|
|
||||||
|
|
||||||
if filtrate_vessel_id and filtrate_vessel_id not in G.neighbors(filter_device):
|
if filtrate_vessel_id and filtrate_vessel_id not in G.neighbors(filter_device):
|
||||||
debug_print(f" 🧪 收集滤液: {filter_device} → {filtrate_vessel_id} 💧")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug_print(" 🔄 开始执行收集操作...")
|
|
||||||
# 使用pump protocol收集滤液
|
|
||||||
collect_actions = generate_pump_protocol_with_rinsing(
|
collect_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=filter_device,
|
from_vessel=filter_device,
|
||||||
to_vessel=filtrate_vessel,
|
to_vessel=filtrate_vessel,
|
||||||
volume=0.0, # 收集所有滤液
|
volume=0.0,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
viscous=False,
|
viscous=False,
|
||||||
@@ -268,12 +182,8 @@ def generate_filter_protocol(
|
|||||||
|
|
||||||
if collect_actions:
|
if collect_actions:
|
||||||
action_sequence.extend(collect_actions)
|
action_sequence.extend(collect_actions)
|
||||||
debug_print(f" ✅ 添加了 {len(collect_actions)} 个收集动作 🧪✨")
|
|
||||||
|
|
||||||
# 🔧 新增:收集滤液后的体积更新
|
# 更新滤液容器体积
|
||||||
debug_print(" 🔧 更新滤液容器体积...")
|
|
||||||
|
|
||||||
# 更新filtrate_vessel在图中的体积(如果它是节点)
|
|
||||||
if filtrate_vessel_id in G.nodes():
|
if filtrate_vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[filtrate_vessel_id]:
|
if 'data' not in G.nodes[filtrate_vessel_id]:
|
||||||
G.nodes[filtrate_vessel_id]['data'] = {}
|
G.nodes[filtrate_vessel_id]['data'] = {}
|
||||||
@@ -287,33 +197,17 @@ def generate_filter_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[filtrate_vessel_id]['data']['liquid_volume'] = current_filtrate_volume + expected_filtrate_volume
|
G.nodes[filtrate_vessel_id]['data']['liquid_volume'] = current_filtrate_volume + expected_filtrate_volume
|
||||||
|
|
||||||
debug_print(f" 📊 滤液容器 {filtrate_vessel_id} 体积增加 {expected_filtrate_volume:.2f}mL")
|
|
||||||
|
|
||||||
else:
|
|
||||||
debug_print(" ⚠️ 收集协议返回空序列 🤔")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f" ❌ 收集滤液失败: {str(e)} 😞")
|
debug_print(f"收集滤液失败: {str(e)},继续执行")
|
||||||
debug_print(" 🔄 继续执行,可能滤液直接流入指定容器 🤞")
|
|
||||||
else:
|
|
||||||
debug_print(" 🧱 未指定滤液容器,固体保留在过滤器中 🔬")
|
|
||||||
|
|
||||||
# 🔧 新增:过滤完成后的容器状态更新
|
|
||||||
debug_print("📍 步骤5.5: 过滤完成后状态更新... 📊")
|
|
||||||
|
|
||||||
|
# 过滤完成后容器状态更新
|
||||||
if vessel_id == filter_device:
|
if vessel_id == filter_device:
|
||||||
# 如果过滤容器就是过滤器,需要更新其体积状态
|
|
||||||
if original_liquid_volume > 0:
|
if original_liquid_volume > 0:
|
||||||
if filtrate_vessel:
|
if filtrate_vessel:
|
||||||
# 收集滤液模式:过滤器中主要保留固体
|
|
||||||
remaining_volume = expected_solid_volume
|
remaining_volume = expected_solid_volume
|
||||||
debug_print(f" 🧱 过滤器中保留固体: {remaining_volume:.2f}mL")
|
|
||||||
else:
|
else:
|
||||||
# 保留固体模式:过滤器中保留所有物质
|
|
||||||
remaining_volume = original_liquid_volume * (1.0 - volume_loss_ratio)
|
remaining_volume = original_liquid_volume * (1.0 - volume_loss_ratio)
|
||||||
debug_print(f" 🔬 过滤器中保留所有物质: {remaining_volume:.2f}mL")
|
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -321,23 +215,18 @@ def generate_filter_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = remaining_volume
|
vessel["data"]["liquid_volume"] = remaining_volume
|
||||||
|
|
||||||
# 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = remaining_volume
|
G.nodes[vessel_id]['data']['liquid_volume'] = remaining_volume
|
||||||
|
|
||||||
debug_print(f" 📊 过滤器 {vessel_id} 体积更新为: {remaining_volume:.2f}mL")
|
|
||||||
|
|
||||||
# === 最终等待 ===
|
# === 最终等待 ===
|
||||||
debug_print("📍 步骤6: 最终等待... ⏰")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": 5.0}
|
"action_kwargs": {"time": 5.0}
|
||||||
})
|
})
|
||||||
debug_print(" ✅ 最终等待动作已添加 ⏰✨")
|
|
||||||
|
|
||||||
# 🔧 新增:过滤完成后的状态报告
|
# 最终状态
|
||||||
final_vessel_volume = 0.0
|
final_vessel_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -346,21 +235,6 @@ def generate_filter_protocol(
|
|||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
final_vessel_volume = current_volume
|
final_vessel_volume = current_volume
|
||||||
|
|
||||||
# === 总结 ===
|
debug_print(f"过滤协议生成完成: {len(action_sequence)} 个动作, 容器={vessel_id}, 过滤器={filter_device}")
|
||||||
debug_print("🎊" * 20)
|
|
||||||
debug_print(f"🎉 过滤协议生成完成! ✨")
|
|
||||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个 📝")
|
|
||||||
debug_print(f"🥽 过滤容器: {vessel_id} 🧪")
|
|
||||||
debug_print(f"🌊 过滤器设备: {filter_device} 🔧")
|
|
||||||
debug_print(f"💧 滤液容器: {filtrate_vessel_id or '无(保留固体)'} 🧱")
|
|
||||||
debug_print(f"⏱️ 预计总时间: {(len(action_sequence) * 5):.0f} 秒 ⌛")
|
|
||||||
if original_liquid_volume > 0:
|
|
||||||
debug_print(f"📊 体积变化统计:")
|
|
||||||
debug_print(f" - 过滤前体积: {original_liquid_volume:.2f}mL")
|
|
||||||
debug_print(f" - 过滤后容器体积: {final_vessel_volume:.2f}mL")
|
|
||||||
if filtrate_vessel:
|
|
||||||
debug_print(f" - 预计滤液体积: {expected_filtrate_volume:.2f}mL")
|
|
||||||
debug_print(f" - 预计损失体积: {volume_loss:.2f}mL")
|
|
||||||
debug_print("🎊" * 20)
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|||||||
@@ -2,99 +2,18 @@ import traceback
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import asyncio
|
import asyncio
|
||||||
import time as time_module # 🔧 重命名time模块
|
import time as time_module # 重命名time模块
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from unilabos.compile.utils.vessel_parser import get_vessel
|
from .utils.logger_util import debug_print
|
||||||
|
from .utils.vessel_parser import get_vessel
|
||||||
|
from .utils.resource_helper import get_resource_liquid_volume
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""强制输出调试信息"""
|
|
||||||
output = f"[TRANSFER] {message}"
|
|
||||||
logger.info(output)
|
|
||||||
|
|
||||||
|
|
||||||
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
|
||||||
"""
|
|
||||||
从容器节点的数据中获取液体体积
|
|
||||||
"""
|
|
||||||
debug_print(f"🔍 开始读取容器 '{vessel}' 的液体体积...")
|
|
||||||
|
|
||||||
if vessel not in G.nodes():
|
|
||||||
logger.error(f"❌ 容器 '{vessel}' 不存在于系统图中")
|
|
||||||
debug_print(f" - 系统中的容器: {list(G.nodes())}")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
vessel_data = G.nodes[vessel].get('data', {})
|
|
||||||
debug_print(f"📋 容器 '{vessel}' 的数据结构: {vessel_data}")
|
|
||||||
|
|
||||||
total_volume = 0.0
|
|
||||||
|
|
||||||
# 方法1:检查 'liquid' 字段(列表格式)
|
|
||||||
debug_print("🔍 方法1: 检查 'liquid' 字段...")
|
|
||||||
if 'liquid' in vessel_data:
|
|
||||||
liquids = vessel_data['liquid']
|
|
||||||
debug_print(f" - liquid 字段类型: {type(liquids)}")
|
|
||||||
debug_print(f" - liquid 字段内容: {liquids}")
|
|
||||||
|
|
||||||
if isinstance(liquids, list):
|
|
||||||
debug_print(f" - liquid 是列表,包含 {len(liquids)} 个元素")
|
|
||||||
for i, liquid in enumerate(liquids):
|
|
||||||
debug_print(f" 液体 {i + 1}: {liquid}")
|
|
||||||
if isinstance(liquid, dict):
|
|
||||||
volume_keys = ['liquid_volume', 'volume', 'amount', 'quantity']
|
|
||||||
for key in volume_keys:
|
|
||||||
if key in liquid:
|
|
||||||
try:
|
|
||||||
vol = float(liquid[key])
|
|
||||||
total_volume += vol
|
|
||||||
debug_print(f" ✅ 从 '{key}' 读取体积: {vol}mL")
|
|
||||||
break
|
|
||||||
except (ValueError, TypeError) as e:
|
|
||||||
logger.warning(f" ⚠️ 无法转换 '{key}': {liquid[key]} -> {str(e)}")
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
debug_print(f" - liquid 不是列表: {type(liquids)}")
|
|
||||||
else:
|
|
||||||
debug_print(" - 没有 'liquid' 字段")
|
|
||||||
|
|
||||||
# 方法2:检查直接的体积字段
|
|
||||||
debug_print("🔍 方法2: 检查直接体积字段...")
|
|
||||||
volume_keys = ['total_volume', 'volume', 'liquid_volume', 'amount', 'current_volume']
|
|
||||||
for key in volume_keys:
|
|
||||||
if key in vessel_data:
|
|
||||||
try:
|
|
||||||
vol = float(vessel_data[key])
|
|
||||||
total_volume = max(total_volume, vol) # 取最大值
|
|
||||||
debug_print(f" ✅ 从容器数据 '{key}' 读取体积: {vol}mL")
|
|
||||||
break
|
|
||||||
except (ValueError, TypeError) as e:
|
|
||||||
logger.warning(f" ⚠️ 无法转换 '{key}': {vessel_data[key]} -> {str(e)}")
|
|
||||||
continue
|
|
||||||
|
|
||||||
# 方法3:检查 'state' 或 'status' 字段
|
|
||||||
debug_print("🔍 方法3: 检查 'state' 字段...")
|
|
||||||
if 'state' in vessel_data and isinstance(vessel_data['state'], dict):
|
|
||||||
state = vessel_data['state']
|
|
||||||
debug_print(f" - state 字段内容: {state}")
|
|
||||||
if 'volume' in state:
|
|
||||||
try:
|
|
||||||
vol = float(state['volume'])
|
|
||||||
total_volume = max(total_volume, vol)
|
|
||||||
debug_print(f" ✅ 从容器状态读取体积: {vol}mL")
|
|
||||||
except (ValueError, TypeError) as e:
|
|
||||||
logger.warning(f" ⚠️ 无法转换 state.volume: {state['volume']} -> {str(e)}")
|
|
||||||
else:
|
|
||||||
debug_print(" - 没有 'state' 字段或不是字典")
|
|
||||||
|
|
||||||
debug_print(f"📊 容器 '{vessel}' 最终检测体积: {total_volume}mL")
|
|
||||||
return total_volume
|
|
||||||
|
|
||||||
|
|
||||||
def is_integrated_pump(node_class: str, node_name: str = "") -> bool:
|
def is_integrated_pump(node_class: str, node_name: str = "") -> bool:
|
||||||
"""
|
"""
|
||||||
判断是否为泵阀一体设备
|
判断是否为泵阀一体设备
|
||||||
@@ -515,188 +434,132 @@ def generate_pump_protocol_with_rinsing(
|
|||||||
to_vessel_id, _ = get_vessel(to_vessel)
|
to_vessel_id, _ = get_vessel(to_vessel)
|
||||||
|
|
||||||
with generate_pump_protocol_with_rinsing._lock:
|
with generate_pump_protocol_with_rinsing._lock:
|
||||||
debug_print("=" * 60)
|
debug_print(f"PUMP_TRANSFER: {from_vessel_id} -> {to_vessel_id}, volume={volume}, flowrate={flowrate}")
|
||||||
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (同步版本)")
|
|
||||||
debug_print(f" 📍 路径: {from_vessel_id} -> {to_vessel_id}")
|
|
||||||
debug_print(f" 🕐 时间戳: {time_module.time()}")
|
|
||||||
debug_print(f" 🔒 获得执行锁")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
# 短暂延迟,避免快速重复调用
|
# 短暂延迟,避免快速重复调用
|
||||||
time_module.sleep(0.01)
|
time_module.sleep(0.01)
|
||||||
|
|
||||||
debug_print("🔍 步骤1: 开始体积处理...")
|
|
||||||
|
|
||||||
# 1. 处理体积参数
|
# 1. 处理体积参数
|
||||||
final_volume = volume
|
final_volume = volume
|
||||||
debug_print(f"📋 初始设置: final_volume = {final_volume}")
|
debug_print(f"初始体积: {final_volume}")
|
||||||
|
|
||||||
# 🔧 修复:如果volume为0(ROS2传入的空值),从容器读取实际体积
|
# 如果volume为0,从容器读取实际体积
|
||||||
if volume == 0.0:
|
if volume == 0.0:
|
||||||
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
|
|
||||||
|
|
||||||
# 直接从源容器读取实际体积
|
actual_volume = get_resource_liquid_volume(G.nodes.get(from_vessel_id, {}))
|
||||||
actual_volume = get_vessel_liquid_volume(G, from_vessel_id)
|
|
||||||
debug_print(f"📖 从容器 '{from_vessel_id}' 读取到体积: {actual_volume}mL")
|
|
||||||
|
|
||||||
if actual_volume > 0:
|
if actual_volume > 0:
|
||||||
final_volume = actual_volume
|
final_volume = actual_volume
|
||||||
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
|
debug_print(f"从容器读取体积: {final_volume}mL")
|
||||||
else:
|
else:
|
||||||
final_volume = 10.0 # 如果读取失败,使用默认值
|
final_volume = 10.0
|
||||||
logger.warning(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
|
logger.warning(f"无法从容器读取体积,使用默认值: {final_volume}mL")
|
||||||
else:
|
else:
|
||||||
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
|
debug_print(f"使用指定体积: {final_volume}mL")
|
||||||
|
|
||||||
# 处理 amount 参数
|
# 处理 amount 参数
|
||||||
if amount and amount.strip():
|
if amount and amount.strip():
|
||||||
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
|
|
||||||
parsed_volume = _parse_amount_to_volume(amount)
|
parsed_volume = _parse_amount_to_volume(amount)
|
||||||
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
|
|
||||||
|
|
||||||
if parsed_volume > 0:
|
if parsed_volume > 0:
|
||||||
final_volume = parsed_volume
|
final_volume = parsed_volume
|
||||||
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
|
|
||||||
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
|
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
|
||||||
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
|
actual_volume = get_resource_liquid_volume(G.nodes.get(from_vessel_id, {}))
|
||||||
actual_volume = get_vessel_liquid_volume(G, from_vessel_id)
|
|
||||||
if actual_volume > 0:
|
if actual_volume > 0:
|
||||||
final_volume = actual_volume
|
final_volume = actual_volume
|
||||||
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
|
|
||||||
|
|
||||||
# 最终体积验证
|
# 最终体积验证
|
||||||
debug_print(f"🔍 步骤2: 最终体积验证...")
|
|
||||||
if final_volume <= 0:
|
if final_volume <= 0:
|
||||||
logger.error(f"❌ 体积无效: {final_volume}mL")
|
logger.error(f"体积无效: {final_volume}mL")
|
||||||
final_volume = 10.0
|
final_volume = 10.0
|
||||||
logger.warning(f"⚠️ 强制设置为默认值: {final_volume}mL")
|
logger.warning(f"强制设置为默认值: {final_volume}mL")
|
||||||
|
|
||||||
debug_print(f"✅ 最终确定体积: {final_volume}mL")
|
debug_print(f"最终体积: {final_volume}mL")
|
||||||
|
|
||||||
# 2. 处理流速参数
|
# 2. 处理流速参数
|
||||||
debug_print(f"🔍 步骤3: 处理流速参数...")
|
|
||||||
debug_print(f" - 原始 flowrate: {flowrate}")
|
|
||||||
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
|
|
||||||
|
|
||||||
final_flowrate = flowrate if flowrate > 0 else 2.5
|
final_flowrate = flowrate if flowrate > 0 else 2.5
|
||||||
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
|
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
|
||||||
|
|
||||||
if flowrate <= 0:
|
if flowrate <= 0:
|
||||||
logger.warning(f"⚠️ flowrate <= 0,修正为: {final_flowrate}mL/s")
|
logger.warning(f"flowrate <= 0,修正为: {final_flowrate}mL/s")
|
||||||
if transfer_flowrate <= 0:
|
if transfer_flowrate <= 0:
|
||||||
logger.warning(f"⚠️ transfer_flowrate <= 0,修正为: {final_transfer_flowrate}mL/s")
|
logger.warning(f"transfer_flowrate <= 0,修正为: {final_transfer_flowrate}mL/s")
|
||||||
|
|
||||||
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
|
|
||||||
|
|
||||||
# 3. 根据时间计算流速
|
# 3. 根据时间计算流速
|
||||||
if time > 0 and final_volume > 0:
|
if time > 0 and final_volume > 0:
|
||||||
debug_print(f"🔍 步骤4: 根据时间计算流速...")
|
|
||||||
calculated_flowrate = final_volume / time
|
calculated_flowrate = final_volume / time
|
||||||
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
|
|
||||||
|
|
||||||
if flowrate <= 0 or flowrate == 2.5:
|
if flowrate <= 0 or flowrate == 2.5:
|
||||||
final_flowrate = min(calculated_flowrate, 10.0)
|
final_flowrate = min(calculated_flowrate, 10.0)
|
||||||
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
|
|
||||||
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
|
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
|
||||||
final_transfer_flowrate = min(calculated_flowrate, 5.0)
|
final_transfer_flowrate = min(calculated_flowrate, 5.0)
|
||||||
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
|
|
||||||
|
|
||||||
# 4. 根据速度规格调整
|
# 4. 根据速度规格调整
|
||||||
if rate_spec:
|
if rate_spec:
|
||||||
debug_print(f"🔍 步骤5: 根据速度规格调整...")
|
|
||||||
debug_print(f" - 速度规格: '{rate_spec}'")
|
|
||||||
|
|
||||||
if rate_spec == "dropwise":
|
if rate_spec == "dropwise":
|
||||||
final_flowrate = min(final_flowrate, 0.1)
|
final_flowrate = min(final_flowrate, 0.1)
|
||||||
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
|
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
|
||||||
debug_print(f" - dropwise模式,流速调整为: {final_flowrate}mL/s")
|
|
||||||
elif rate_spec == "slowly":
|
elif rate_spec == "slowly":
|
||||||
final_flowrate = min(final_flowrate, 0.5)
|
final_flowrate = min(final_flowrate, 0.5)
|
||||||
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
|
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
|
||||||
debug_print(f" - slowly模式,流速调整为: {final_flowrate}mL/s")
|
|
||||||
elif rate_spec == "quickly":
|
elif rate_spec == "quickly":
|
||||||
final_flowrate = max(final_flowrate, 5.0)
|
final_flowrate = max(final_flowrate, 5.0)
|
||||||
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
|
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
|
||||||
debug_print(f" - quickly模式,流速调整为: {final_flowrate}mL/s")
|
debug_print(f"速度规格 '{rate_spec}' 应用后: flowrate={final_flowrate}, transfer={final_transfer_flowrate}")
|
||||||
|
|
||||||
# 5. 处理冲洗参数
|
# 5. 处理冲洗参数
|
||||||
debug_print(f"🔍 步骤6: 处理冲洗参数...")
|
|
||||||
final_rinsing_solvent = rinsing_solvent
|
final_rinsing_solvent = rinsing_solvent
|
||||||
final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
|
final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
|
||||||
final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
|
final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
|
||||||
|
|
||||||
if rinsing_volume <= 0:
|
if rinsing_volume <= 0:
|
||||||
logger.warning(f"⚠️ rinsing_volume <= 0,修正为: {final_rinsing_volume}mL")
|
logger.warning(f"rinsing_volume <= 0,修正为: {final_rinsing_volume}mL")
|
||||||
if rinsing_repeats <= 0:
|
if rinsing_repeats <= 0:
|
||||||
logger.warning(f"⚠️ rinsing_repeats <= 0,修正为: {final_rinsing_repeats}次")
|
logger.warning(f"rinsing_repeats <= 0,修正为: {final_rinsing_repeats}次")
|
||||||
|
|
||||||
# 根据物理属性调整冲洗参数
|
# 根据物理属性调整冲洗参数
|
||||||
if viscous or solid:
|
if viscous or solid:
|
||||||
final_rinsing_repeats = max(final_rinsing_repeats, 3)
|
final_rinsing_repeats = max(final_rinsing_repeats, 3)
|
||||||
final_rinsing_volume = max(final_rinsing_volume, 10.0)
|
final_rinsing_volume = max(final_rinsing_volume, 10.0)
|
||||||
debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
|
|
||||||
|
|
||||||
# 参数总结
|
# 参数总结
|
||||||
debug_print("📊 最终参数总结:")
|
debug_print(f"最终参数: volume={final_volume}mL, flowrate={final_flowrate}mL/s, "
|
||||||
debug_print(f" - 体积: {final_volume}mL")
|
f"transfer_flowrate={final_transfer_flowrate}mL/s, "
|
||||||
debug_print(f" - 流速: {final_flowrate}mL/s")
|
f"rinsing={final_rinsing_solvent}/{final_rinsing_volume}mL/{final_rinsing_repeats}次")
|
||||||
debug_print(f" - 转移流速: {final_transfer_flowrate}mL/s")
|
|
||||||
debug_print(f" - 冲洗溶剂: '{final_rinsing_solvent}'")
|
|
||||||
debug_print(f" - 冲洗体积: {final_rinsing_volume}mL")
|
|
||||||
debug_print(f" - 冲洗次数: {final_rinsing_repeats}次")
|
|
||||||
|
|
||||||
# ========== 执行基础转移 ==========
|
# ========== 执行基础转移 ==========
|
||||||
|
|
||||||
debug_print("🔧 步骤7: 开始执行基础转移...")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug_print(f" - 调用 generate_pump_protocol...")
|
|
||||||
debug_print(
|
|
||||||
f" - 参数: G, '{from_vessel_id}', '{to_vessel_id}', {final_volume}, {final_flowrate}, {final_transfer_flowrate}")
|
|
||||||
|
|
||||||
pump_action_sequence = generate_pump_protocol(
|
pump_action_sequence = generate_pump_protocol(
|
||||||
G, from_vessel_id, to_vessel_id, final_volume,
|
G, from_vessel_id, to_vessel_id, final_volume,
|
||||||
final_flowrate, final_transfer_flowrate
|
final_flowrate, final_transfer_flowrate
|
||||||
)
|
)
|
||||||
|
|
||||||
debug_print(f" - generate_pump_protocol 返回结果:")
|
debug_print(f"基础转移生成了 {len(pump_action_sequence)} 个动作")
|
||||||
debug_print(f" - 动作序列长度: {len(pump_action_sequence)}")
|
|
||||||
debug_print(f" - 动作序列是否为空: {len(pump_action_sequence) == 0}")
|
|
||||||
|
|
||||||
if not pump_action_sequence:
|
if not pump_action_sequence:
|
||||||
debug_print("❌ 基础转移协议生成为空,可能是路径问题")
|
debug_print("基础转移协议生成为空,可能是路径问题")
|
||||||
debug_print(f" - 源容器存在: {from_vessel_id in G.nodes()}")
|
|
||||||
debug_print(f" - 目标容器存在: {to_vessel_id in G.nodes()}")
|
|
||||||
|
|
||||||
if from_vessel_id in G.nodes() and to_vessel_id in G.nodes():
|
if from_vessel_id in G.nodes() and to_vessel_id in G.nodes():
|
||||||
try:
|
try:
|
||||||
path = nx.shortest_path(G, source=from_vessel_id, target=to_vessel_id)
|
path = nx.shortest_path(G, source=from_vessel_id, target=to_vessel_id)
|
||||||
debug_print(f" - 路径存在: {path}")
|
debug_print(f"路径存在: {path}")
|
||||||
except Exception as path_error:
|
except Exception as path_error:
|
||||||
debug_print(f" - 无法找到路径: {str(path_error)}")
|
debug_print(f"无法找到路径: {str(path_error)}")
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"device_id": "system",
|
"device_id": "system",
|
||||||
"action_name": "log_message",
|
"action_name": "log_message",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"message": f"⚠️ 路径问题,无法转移: {final_volume}mL 从 {from_vessel_id} 到 {to_vessel_id}"
|
"message": f"路径问题,无法转移: {final_volume}mL 从 {from_vessel_id} 到 {to_vessel_id}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_print(f"✅ 基础转移生成了 {len(pump_action_sequence)} 个动作")
|
|
||||||
|
|
||||||
# 打印前几个动作用于调试
|
|
||||||
if len(pump_action_sequence) > 0:
|
|
||||||
debug_print("🔍 前几个动作预览:")
|
|
||||||
for i, action in enumerate(pump_action_sequence[:3]):
|
|
||||||
debug_print(f" 动作 {i + 1}: {action}")
|
|
||||||
if len(pump_action_sequence) > 3:
|
|
||||||
debug_print(f" ... 还有 {len(pump_action_sequence) - 3} 个动作")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 基础转移失败: {str(e)}")
|
debug_print(f"基础转移失败: {str(e)}")
|
||||||
import traceback
|
import traceback
|
||||||
debug_print(f"详细错误: {traceback.format_exc()}")
|
debug_print(f"详细错误: {traceback.format_exc()}")
|
||||||
return [
|
return [
|
||||||
@@ -704,111 +567,82 @@ def generate_pump_protocol_with_rinsing(
|
|||||||
"device_id": "system",
|
"device_id": "system",
|
||||||
"action_name": "log_message",
|
"action_name": "log_message",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"message": f"❌ 转移失败: {final_volume}mL 从 {from_vessel_id} 到 {to_vessel_id}, 错误: {str(e)}"
|
"message": f"转移失败: {final_volume}mL 从 {from_vessel_id} 到 {to_vessel_id}, 错误: {str(e)}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
# ========== 执行冲洗操作 ==========
|
# ========== 执行冲洗操作 ==========
|
||||||
|
|
||||||
debug_print("🔧 步骤8: 检查冲洗操作...")
|
|
||||||
|
|
||||||
if final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0:
|
if final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0:
|
||||||
debug_print(f"🧽 开始冲洗操作,溶剂: '{final_rinsing_solvent}'")
|
debug_print(f"开始冲洗操作,溶剂: '{final_rinsing_solvent}'")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if final_rinsing_solvent.strip() != "air":
|
if final_rinsing_solvent.strip() != "air":
|
||||||
debug_print(" - 执行液体冲洗...")
|
|
||||||
rinsing_actions = _generate_rinsing_sequence(
|
rinsing_actions = _generate_rinsing_sequence(
|
||||||
G, from_vessel_id, to_vessel_id, final_rinsing_solvent,
|
G, from_vessel_id, to_vessel_id, final_rinsing_solvent,
|
||||||
final_rinsing_volume, final_rinsing_repeats,
|
final_rinsing_volume, final_rinsing_repeats,
|
||||||
final_flowrate, final_transfer_flowrate
|
final_flowrate, final_transfer_flowrate
|
||||||
)
|
)
|
||||||
pump_action_sequence.extend(rinsing_actions)
|
pump_action_sequence.extend(rinsing_actions)
|
||||||
debug_print(f" - 添加了 {len(rinsing_actions)} 个冲洗动作")
|
|
||||||
else:
|
else:
|
||||||
debug_print(" - 执行空气冲洗...")
|
|
||||||
air_rinsing_actions = _generate_air_rinsing_sequence(
|
air_rinsing_actions = _generate_air_rinsing_sequence(
|
||||||
G, from_vessel_id, to_vessel_id, final_rinsing_volume, final_rinsing_repeats,
|
G, from_vessel_id, to_vessel_id, final_rinsing_volume, final_rinsing_repeats,
|
||||||
final_flowrate, final_transfer_flowrate
|
final_flowrate, final_transfer_flowrate
|
||||||
)
|
)
|
||||||
pump_action_sequence.extend(air_rinsing_actions)
|
pump_action_sequence.extend(air_rinsing_actions)
|
||||||
debug_print(f" - 添加了 {len(air_rinsing_actions)} 个空气冲洗动作")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 冲洗操作失败: {str(e)},跳过冲洗")
|
debug_print(f"冲洗操作失败: {str(e)},跳过冲洗")
|
||||||
else:
|
else:
|
||||||
debug_print(f"⏭️ 跳过冲洗操作")
|
debug_print(f"跳过冲洗操作 (solvent='{final_rinsing_solvent}', repeats={final_rinsing_repeats})")
|
||||||
debug_print(f" - 溶剂: '{final_rinsing_solvent}'")
|
|
||||||
debug_print(f" - 次数: {final_rinsing_repeats}")
|
|
||||||
debug_print(f" - 条件满足: {bool(final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0)}")
|
|
||||||
|
|
||||||
# ========== 最终结果 ==========
|
# ========== 最终结果 ==========
|
||||||
|
debug_print(f"PUMP_TRANSFER 完成: {from_vessel_id} -> {to_vessel_id}, "
|
||||||
debug_print("=" * 60)
|
f"volume={final_volume}mL, 动作数={len(pump_action_sequence)}")
|
||||||
debug_print(f"🎉 PUMP_TRANSFER: 协议生成完成")
|
|
||||||
debug_print(f" 📊 总动作数: {len(pump_action_sequence)}")
|
|
||||||
debug_print(f" 📋 最终体积: {final_volume}mL")
|
|
||||||
debug_print(f" 🚀 执行路径: {from_vessel_id} -> {to_vessel_id}")
|
|
||||||
|
|
||||||
# 最终验证
|
# 最终验证
|
||||||
if len(pump_action_sequence) == 0:
|
if len(pump_action_sequence) == 0:
|
||||||
debug_print("🚨 协议生成结果为空!这是异常情况")
|
debug_print("协议生成结果为空!")
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"device_id": "system",
|
"device_id": "system",
|
||||||
"action_name": "log_message",
|
"action_name": "log_message",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"message": f"🚨 协议生成失败: 无法生成任何动作序列"
|
"message": "协议生成失败: 无法生成任何动作序列"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
debug_print("=" * 60)
|
|
||||||
return pump_action_sequence
|
return pump_action_sequence
|
||||||
|
|
||||||
|
|
||||||
def _parse_amount_to_volume(amount: str) -> float:
|
def _parse_amount_to_volume(amount: str) -> float:
|
||||||
"""解析 amount 字符串为体积"""
|
"""解析 amount 字符串为体积"""
|
||||||
debug_print(f"🔍 解析 amount: '{amount}'")
|
|
||||||
|
|
||||||
if not amount:
|
if not amount:
|
||||||
debug_print(" - amount 为空,返回 0.0")
|
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
amount = amount.lower().strip()
|
amount = amount.lower().strip()
|
||||||
debug_print(f" - 处理后的 amount: '{amount}'")
|
|
||||||
|
|
||||||
# 处理特殊关键词
|
# 处理特殊关键词
|
||||||
if amount == "all":
|
if amount == "all":
|
||||||
debug_print(" - 检测到 'all',返回 0.0(需要后续处理)")
|
|
||||||
return 0.0 # 返回0.0,让调用者处理
|
return 0.0 # 返回0.0,让调用者处理
|
||||||
|
|
||||||
# 提取数字
|
# 提取数字
|
||||||
import re
|
import re
|
||||||
numbers = re.findall(r'[\d.]+', amount)
|
numbers = re.findall(r'[\d.]+', amount)
|
||||||
debug_print(f" - 提取到的数字: {numbers}")
|
|
||||||
|
|
||||||
if numbers:
|
if numbers:
|
||||||
volume = float(numbers[0])
|
volume = float(numbers[0])
|
||||||
debug_print(f" - 基础体积: {volume}")
|
|
||||||
|
|
||||||
# 单位转换
|
# 单位转换
|
||||||
if 'ml' in amount or 'milliliter' in amount:
|
if 'ml' in amount or 'milliliter' in amount:
|
||||||
debug_print(f" - 单位: mL,最终体积: {volume}")
|
|
||||||
return volume
|
return volume
|
||||||
elif 'l' in amount and 'ml' not in amount:
|
elif 'l' in amount and 'ml' not in amount:
|
||||||
final_volume = volume * 1000
|
return volume * 1000
|
||||||
debug_print(f" - 单位: L,最终体积: {final_volume}mL")
|
|
||||||
return final_volume
|
|
||||||
elif 'μl' in amount or 'microliter' in amount:
|
elif 'μl' in amount or 'microliter' in amount:
|
||||||
final_volume = volume / 1000
|
return volume / 1000
|
||||||
debug_print(f" - 单位: μL,最终体积: {final_volume}mL")
|
|
||||||
return final_volume
|
|
||||||
else:
|
else:
|
||||||
debug_print(f" - 无单位,假设为 mL: {volume}")
|
return volume # 默认mL
|
||||||
return volume
|
|
||||||
|
|
||||||
debug_print(" - 无法解析,返回 0.0")
|
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,11 @@ import logging
|
|||||||
from typing import List, Dict, Any, Tuple, Union
|
from typing import List, Dict, Any, Tuple, Union
|
||||||
from .utils.vessel_parser import get_vessel, find_solvent_vessel
|
from .utils.vessel_parser import get_vessel, find_solvent_vessel
|
||||||
from .utils.unit_parser import parse_volume_input
|
from .utils.unit_parser import parse_volume_input
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[RECRYSTALLIZE] {message}")
|
|
||||||
|
|
||||||
|
|
||||||
def parse_ratio(ratio_str: str) -> Tuple[float, float]:
|
def parse_ratio(ratio_str: str) -> Tuple[float, float]:
|
||||||
"""
|
"""
|
||||||
@@ -23,52 +20,43 @@ def parse_ratio(ratio_str: str) -> Tuple[float, float]:
|
|||||||
Returns:
|
Returns:
|
||||||
Tuple[float, float]: 比例元组 (ratio1, ratio2)
|
Tuple[float, float]: 比例元组 (ratio1, ratio2)
|
||||||
"""
|
"""
|
||||||
debug_print(f"⚖️ 开始解析比例: '{ratio_str}' 📊")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 处理 "1:1", "3:7", "50:50" 等格式
|
|
||||||
if ":" in ratio_str:
|
if ":" in ratio_str:
|
||||||
parts = ratio_str.split(":")
|
parts = ratio_str.split(":")
|
||||||
if len(parts) == 2:
|
if len(parts) == 2:
|
||||||
ratio1 = float(parts[0])
|
ratio1 = float(parts[0])
|
||||||
ratio2 = float(parts[1])
|
ratio2 = float(parts[1])
|
||||||
debug_print(f"✅ 冒号格式解析成功: {ratio1}:{ratio2} 🎯")
|
|
||||||
return ratio1, ratio2
|
return ratio1, ratio2
|
||||||
|
|
||||||
# 处理 "1-1", "3-7" 等格式
|
|
||||||
if "-" in ratio_str:
|
if "-" in ratio_str:
|
||||||
parts = ratio_str.split("-")
|
parts = ratio_str.split("-")
|
||||||
if len(parts) == 2:
|
if len(parts) == 2:
|
||||||
ratio1 = float(parts[0])
|
ratio1 = float(parts[0])
|
||||||
ratio2 = float(parts[1])
|
ratio2 = float(parts[1])
|
||||||
debug_print(f"✅ 横线格式解析成功: {ratio1}:{ratio2} 🎯")
|
|
||||||
return ratio1, ratio2
|
return ratio1, ratio2
|
||||||
|
|
||||||
# 处理 "1,1", "3,7" 等格式
|
|
||||||
if "," in ratio_str:
|
if "," in ratio_str:
|
||||||
parts = ratio_str.split(",")
|
parts = ratio_str.split(",")
|
||||||
if len(parts) == 2:
|
if len(parts) == 2:
|
||||||
ratio1 = float(parts[0])
|
ratio1 = float(parts[0])
|
||||||
ratio2 = float(parts[1])
|
ratio2 = float(parts[1])
|
||||||
debug_print(f"✅ 逗号格式解析成功: {ratio1}:{ratio2} 🎯")
|
|
||||||
return ratio1, ratio2
|
return ratio1, ratio2
|
||||||
|
|
||||||
# 默认 1:1
|
debug_print(f"无法解析比例 '{ratio_str}',使用默认比例 1:1")
|
||||||
debug_print(f"⚠️ 无法解析比例 '{ratio_str}',使用默认比例 1:1 🎭")
|
|
||||||
return 1.0, 1.0
|
return 1.0, 1.0
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
debug_print(f"❌ 比例解析错误 '{ratio_str}',使用默认比例 1:1 🎭")
|
debug_print(f"比例解析错误 '{ratio_str}',使用默认比例 1:1")
|
||||||
return 1.0, 1.0
|
return 1.0, 1.0
|
||||||
|
|
||||||
|
|
||||||
def generate_recrystallize_protocol(
|
def generate_recrystallize_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
vessel: dict,
|
||||||
ratio: str,
|
ratio: str,
|
||||||
solvent1: str,
|
solvent1: str,
|
||||||
solvent2: str,
|
solvent2: str,
|
||||||
volume: Union[str, float], # 支持字符串和数值
|
volume: Union[str, float],
|
||||||
**kwargs
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
@@ -87,23 +75,13 @@ def generate_recrystallize_protocol(
|
|||||||
List[Dict[str, Any]]: 动作序列
|
List[Dict[str, Any]]: 动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
vessel_id, vessel_data = get_vessel(vessel)
|
vessel_id, vessel_data = get_vessel(vessel)
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
debug_print("💎" * 20)
|
debug_print(f"开始生成重结晶协议: vessel={vessel_id}, ratio={ratio}, solvent1={solvent1}, solvent2={solvent2}, volume={volume}")
|
||||||
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" 💧 总体积: {volume} (类型: {type(volume)})")
|
|
||||||
debug_print("💎" * 20)
|
|
||||||
|
|
||||||
# 🔧 新增:记录重结晶前的容器状态
|
# 记录重结晶前的容器状态
|
||||||
debug_print("🔍 记录重结晶前容器状态...")
|
|
||||||
original_liquid_volume = 0.0
|
original_liquid_volume = 0.0
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
@@ -111,102 +89,73 @@ def generate_recrystallize_protocol(
|
|||||||
original_liquid_volume = current_volume[0]
|
original_liquid_volume = current_volume[0]
|
||||||
elif isinstance(current_volume, (int, float)):
|
elif isinstance(current_volume, (int, float)):
|
||||||
original_liquid_volume = current_volume
|
original_liquid_volume = current_volume
|
||||||
debug_print(f"📊 重结晶前液体体积: {original_liquid_volume:.2f}mL")
|
|
||||||
|
|
||||||
# 1. 验证目标容器存在
|
# 1. 验证目标容器存在
|
||||||
debug_print("📍 步骤1: 验证目标容器... 🔧")
|
if vessel_id not in G.nodes():
|
||||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
|
||||||
debug_print(f"❌ 目标容器 '{vessel_id}' 不存在于系统中! 😱")
|
|
||||||
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
||||||
debug_print(f"✅ 目标容器 '{vessel_id}' 验证通过 🎯")
|
|
||||||
|
|
||||||
# 2. 解析体积(支持单位)
|
# 2. 解析体积(支持单位)
|
||||||
debug_print("📍 步骤2: 解析体积(支持单位)... 💧")
|
|
||||||
final_volume = parse_volume_input(volume, "mL")
|
final_volume = parse_volume_input(volume, "mL")
|
||||||
debug_print(f"🎯 体积解析完成: {volume} → {final_volume}mL ✨")
|
debug_print(f"体积解析: {volume} -> {final_volume}mL")
|
||||||
|
|
||||||
# 3. 解析比例
|
# 3. 解析比例
|
||||||
debug_print("📍 步骤3: 解析比例... ⚖️")
|
|
||||||
ratio1, ratio2 = parse_ratio(ratio)
|
ratio1, ratio2 = parse_ratio(ratio)
|
||||||
total_ratio = ratio1 + ratio2
|
total_ratio = ratio1 + ratio2
|
||||||
debug_print(f"🎯 比例解析完成: {ratio1}:{ratio2} (总比例: {total_ratio}) ✨")
|
|
||||||
|
|
||||||
# 4. 计算各溶剂体积
|
# 4. 计算各溶剂体积
|
||||||
debug_print("📍 步骤4: 计算各溶剂体积... 🧮")
|
|
||||||
volume1 = final_volume * (ratio1 / total_ratio)
|
volume1 = final_volume * (ratio1 / total_ratio)
|
||||||
volume2 = final_volume * (ratio2 / total_ratio)
|
volume2 = final_volume * (ratio2 / total_ratio)
|
||||||
|
|
||||||
debug_print(f"🧪 {solvent1} 体积: {volume1:.2f} mL ({ratio1}/{total_ratio} × {final_volume})")
|
debug_print(f"溶剂体积: {solvent1}={volume1:.2f}mL, {solvent2}={volume2:.2f}mL")
|
||||||
debug_print(f"🧪 {solvent2} 体积: {volume2:.2f} mL ({ratio2}/{total_ratio} × {final_volume})")
|
|
||||||
debug_print(f"✅ 体积计算完成: 总计 {volume1 + volume2:.2f} mL 🎯")
|
|
||||||
|
|
||||||
# 5. 查找溶剂容器
|
# 5. 查找溶剂容器
|
||||||
debug_print("📍 步骤5: 查找溶剂容器... 🔍")
|
|
||||||
try:
|
try:
|
||||||
debug_print(f" 🔍 查找溶剂1容器...")
|
|
||||||
solvent1_vessel = find_solvent_vessel(G, solvent1)
|
solvent1_vessel = find_solvent_vessel(G, solvent1)
|
||||||
debug_print(f" 🎉 找到溶剂1容器: {solvent1_vessel} ✨")
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
debug_print(f" ❌ 溶剂1容器查找失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"无法找到溶剂1 '{solvent1}': {str(e)}")
|
raise ValueError(f"无法找到溶剂1 '{solvent1}': {str(e)}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug_print(f" 🔍 查找溶剂2容器...")
|
|
||||||
solvent2_vessel = find_solvent_vessel(G, solvent2)
|
solvent2_vessel = find_solvent_vessel(G, solvent2)
|
||||||
debug_print(f" 🎉 找到溶剂2容器: {solvent2_vessel} ✨")
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
debug_print(f" ❌ 溶剂2容器查找失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"无法找到溶剂2 '{solvent2}': {str(e)}")
|
raise ValueError(f"无法找到溶剂2 '{solvent2}': {str(e)}")
|
||||||
|
|
||||||
# 6. 验证路径存在
|
# 6. 验证路径存在
|
||||||
debug_print("📍 步骤6: 验证传输路径... 🛤️")
|
|
||||||
try:
|
try:
|
||||||
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel_id) # 🔧 使用 vessel_id
|
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel_id)
|
||||||
debug_print(f" 🛤️ 溶剂1路径: {' → '.join(path1)} ✅")
|
|
||||||
except nx.NetworkXNoPath:
|
except nx.NetworkXNoPath:
|
||||||
debug_print(f" ❌ 溶剂1路径不可达: {solvent1_vessel} → {vessel_id} 😞")
|
|
||||||
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel_id) # 🔧 使用 vessel_id
|
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel_id)
|
||||||
debug_print(f" 🛤️ 溶剂2路径: {' → '.join(path2)} ✅")
|
|
||||||
except nx.NetworkXNoPath:
|
except nx.NetworkXNoPath:
|
||||||
debug_print(f" ❌ 溶剂2路径不可达: {solvent2_vessel} → {vessel_id} 😞")
|
|
||||||
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
||||||
|
|
||||||
# 7. 添加第一种溶剂
|
# 7. 添加第一种溶剂
|
||||||
debug_print("📍 步骤7: 添加第一种溶剂... 🧪")
|
|
||||||
debug_print(f" 🚰 开始添加溶剂1: {solvent1} ({volume1:.2f} mL)")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pump_actions1 = generate_pump_protocol_with_rinsing(
|
pump_actions1 = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=solvent1_vessel,
|
from_vessel=solvent1_vessel,
|
||||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
to_vessel=vessel_id,
|
||||||
volume=volume1, # 使用解析后的体积
|
volume=volume1,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
viscous=False,
|
viscous=False,
|
||||||
rinsing_solvent="", # 重结晶不需要清洗
|
rinsing_solvent="",
|
||||||
rinsing_volume=0.0,
|
rinsing_volume=0.0,
|
||||||
rinsing_repeats=0,
|
rinsing_repeats=0,
|
||||||
solid=False,
|
solid=False,
|
||||||
flowrate=2.0, # 正常流速
|
flowrate=2.0,
|
||||||
transfer_flowrate=0.5
|
transfer_flowrate=0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
action_sequence.extend(pump_actions1)
|
action_sequence.extend(pump_actions1)
|
||||||
debug_print(f" ✅ 溶剂1泵送动作已添加: {len(pump_actions1)} 个动作 🚰✨")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f" ❌ 溶剂1泵协议生成失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"生成溶剂1泵协议时出错: {str(e)}")
|
raise ValueError(f"生成溶剂1泵协议时出错: {str(e)}")
|
||||||
|
|
||||||
# 🔧 新增:更新容器体积 - 添加溶剂1后
|
# 更新容器体积 - 添加溶剂1后
|
||||||
debug_print(" 🔧 更新容器体积 - 添加溶剂1后...")
|
|
||||||
new_volume_after_solvent1 = original_liquid_volume + volume1
|
new_volume_after_solvent1 = original_liquid_volume + volume1
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -217,7 +166,6 @@ def generate_recrystallize_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = new_volume_after_solvent1
|
vessel["data"]["liquid_volume"] = new_volume_after_solvent1
|
||||||
|
|
||||||
# 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
@@ -233,52 +181,41 @@ def generate_recrystallize_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume_after_solvent1
|
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稳定
|
# 8. 等待溶剂1稳定
|
||||||
debug_print(" ⏳ 添加溶剂1稳定等待...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"time": 5.0, # 缩短等待时间
|
"time": 5.0,
|
||||||
"description": f"等待溶剂1 {solvent1} 稳定"
|
"description": f"等待溶剂1 {solvent1} 稳定"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
debug_print(" ✅ 溶剂1稳定等待已添加 ⏰✨")
|
|
||||||
|
|
||||||
# 9. 添加第二种溶剂
|
# 9. 添加第二种溶剂
|
||||||
debug_print("📍 步骤8: 添加第二种溶剂... 🧪")
|
|
||||||
debug_print(f" 🚰 开始添加溶剂2: {solvent2} ({volume2:.2f} mL)")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pump_actions2 = generate_pump_protocol_with_rinsing(
|
pump_actions2 = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=solvent2_vessel,
|
from_vessel=solvent2_vessel,
|
||||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
to_vessel=vessel_id,
|
||||||
volume=volume2, # 使用解析后的体积
|
volume=volume2,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
viscous=False,
|
viscous=False,
|
||||||
rinsing_solvent="", # 重结晶不需要清洗
|
rinsing_solvent="",
|
||||||
rinsing_volume=0.0,
|
rinsing_volume=0.0,
|
||||||
rinsing_repeats=0,
|
rinsing_repeats=0,
|
||||||
solid=False,
|
solid=False,
|
||||||
flowrate=2.0, # 正常流速
|
flowrate=2.0,
|
||||||
transfer_flowrate=0.5
|
transfer_flowrate=0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
action_sequence.extend(pump_actions2)
|
action_sequence.extend(pump_actions2)
|
||||||
debug_print(f" ✅ 溶剂2泵送动作已添加: {len(pump_actions2)} 个动作 🚰✨")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f" ❌ 溶剂2泵协议生成失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"生成溶剂2泵协议时出错: {str(e)}")
|
raise ValueError(f"生成溶剂2泵协议时出错: {str(e)}")
|
||||||
|
|
||||||
# 🔧 新增:更新容器体积 - 添加溶剂2后
|
# 更新容器体积 - 添加溶剂2后
|
||||||
debug_print(" 🔧 更新容器体积 - 添加溶剂2后...")
|
|
||||||
final_liquid_volume = new_volume_after_solvent1 + volume2
|
final_liquid_volume = new_volume_after_solvent1 + volume2
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||||
current_volume = vessel["data"]["liquid_volume"]
|
current_volume = vessel["data"]["liquid_volume"]
|
||||||
if isinstance(current_volume, list):
|
if isinstance(current_volume, list):
|
||||||
@@ -289,7 +226,6 @@ def generate_recrystallize_protocol(
|
|||||||
else:
|
else:
|
||||||
vessel["data"]["liquid_volume"] = final_liquid_volume
|
vessel["data"]["liquid_volume"] = final_liquid_volume
|
||||||
|
|
||||||
# 同时更新图中的容器数据
|
|
||||||
if vessel_id in G.nodes():
|
if vessel_id in G.nodes():
|
||||||
if 'data' not in G.nodes[vessel_id]:
|
if 'data' not in G.nodes[vessel_id]:
|
||||||
G.nodes[vessel_id]['data'] = {}
|
G.nodes[vessel_id]['data'] = {}
|
||||||
@@ -305,34 +241,23 @@ def generate_recrystallize_protocol(
|
|||||||
else:
|
else:
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = final_liquid_volume
|
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稳定
|
# 10. 等待溶剂2稳定
|
||||||
debug_print(" ⏳ 添加溶剂2稳定等待...")
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"time": 5.0, # 缩短等待时间
|
"time": 5.0,
|
||||||
"description": f"等待溶剂2 {solvent2} 稳定"
|
"description": f"等待溶剂2 {solvent2} 稳定"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
debug_print(" ✅ 溶剂2稳定等待已添加 ⏰✨")
|
|
||||||
|
|
||||||
# 11. 等待重结晶完成
|
# 11. 等待重结晶完成
|
||||||
debug_print("📍 步骤9: 等待重结晶完成... 💎")
|
original_crystallize_time = 600.0
|
||||||
|
simulation_time_limit = 60.0
|
||||||
# 模拟运行时间优化
|
|
||||||
debug_print(" ⏱️ 检查模拟运行时间限制...")
|
|
||||||
original_crystallize_time = 600.0 # 原始重结晶时间
|
|
||||||
simulation_time_limit = 60.0 # 模拟运行时间限制:60秒
|
|
||||||
|
|
||||||
final_crystallize_time = min(original_crystallize_time, simulation_time_limit)
|
final_crystallize_time = min(original_crystallize_time, simulation_time_limit)
|
||||||
|
|
||||||
if 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}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_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -341,26 +266,8 @@ def generate_recrystallize_protocol(
|
|||||||
"description": f"等待重结晶完成({solvent1}:{solvent2} = {ratio},总体积 {final_volume}mL)" + (f" (模拟时间)" if original_crystallize_time != final_crystallize_time else "")
|
"description": f"等待重结晶完成({solvent1}:{solvent2} = {ratio},总体积 {final_volume}mL)" + (f" (模拟时间)" if original_crystallize_time != final_crystallize_time else "")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
debug_print(f" ✅ 重结晶等待已添加: {final_crystallize_time}s 💎✨")
|
|
||||||
|
|
||||||
# 显示时间调整信息
|
debug_print(f"重结晶协议生成完成: {len(action_sequence)} 个动作, 容器={vessel_id}, 体积变化: {original_liquid_volume:.2f} -> {final_liquid_volume:.2f}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_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)")
|
|
||||||
debug_print(f"⏱️ 预计总时间: {(final_crystallize_time + 10)/60:.1f} 分钟 ⌛")
|
|
||||||
debug_print("💎" * 20)
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
@@ -368,23 +275,19 @@ def generate_recrystallize_protocol(
|
|||||||
# 测试函数
|
# 测试函数
|
||||||
def test_recrystallize_protocol():
|
def test_recrystallize_protocol():
|
||||||
"""测试重结晶协议"""
|
"""测试重结晶协议"""
|
||||||
debug_print("🧪 === RECRYSTALLIZE PROTOCOL 测试 === ✨")
|
debug_print("=== RECRYSTALLIZE PROTOCOL 测试 ===")
|
||||||
|
|
||||||
# 测试体积解析
|
|
||||||
debug_print("💧 测试体积解析...")
|
|
||||||
test_volumes = ["100 mL", "2.5 L", "500", "50.5", "?", "invalid"]
|
test_volumes = ["100 mL", "2.5 L", "500", "50.5", "?", "invalid"]
|
||||||
for vol in test_volumes:
|
for vol in test_volumes:
|
||||||
parsed = parse_volume_input(vol)
|
parsed = parse_volume_input(vol)
|
||||||
debug_print(f" 📊 体积 '{vol}' -> {parsed}mL")
|
debug_print(f"体积 '{vol}' -> {parsed}mL")
|
||||||
|
|
||||||
# 测试比例解析
|
|
||||||
debug_print("⚖️ 测试比例解析...")
|
|
||||||
test_ratios = ["1:1", "3:7", "50:50", "1-1", "2,8", "invalid"]
|
test_ratios = ["1:1", "3:7", "50:50", "1-1", "2,8", "invalid"]
|
||||||
for ratio in test_ratios:
|
for ratio in test_ratios:
|
||||||
r1, r2 = parse_ratio(ratio)
|
r1, r2 = parse_ratio(ratio)
|
||||||
debug_print(f" 📊 比例 '{ratio}' -> {r1}:{r2}")
|
debug_print(f"比例 '{ratio}' -> {r1}:{r2}")
|
||||||
|
|
||||||
debug_print("✅ 测试完成 🎉")
|
debug_print("测试完成")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_recrystallize_protocol()
|
test_recrystallize_protocol()
|
||||||
@@ -1,165 +1,20 @@
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
import sys
|
|
||||||
from typing import List, Dict, Any, Optional
|
from typing import List, Dict, Any, Optional
|
||||||
|
from .utils.logger_util import debug_print, action_log
|
||||||
|
from .utils.vessel_parser import find_solvent_vessel
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
# 设置日志
|
# 设置日志
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# 确保输出编码为UTF-8
|
create_action_log = action_log
|
||||||
if hasattr(sys.stdout, 'reconfigure'):
|
|
||||||
try:
|
|
||||||
sys.stdout.reconfigure(encoding='utf-8')
|
|
||||||
sys.stderr.reconfigure(encoding='utf-8')
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出函数 - 支持中文"""
|
|
||||||
try:
|
|
||||||
# 确保消息是字符串格式
|
|
||||||
safe_message = str(message)
|
|
||||||
print(f"[重置处理] {safe_message}", flush=True)
|
|
||||||
logger.info(f"[重置处理] {safe_message}")
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
# 如果编码失败,尝试替换不支持的字符
|
|
||||||
safe_message = str(message).encode('utf-8', errors='replace').decode('utf-8')
|
|
||||||
print(f"[重置处理] {safe_message}", flush=True)
|
|
||||||
logger.info(f"[重置处理] {safe_message}")
|
|
||||||
except Exception as e:
|
|
||||||
# 最后的安全措施
|
|
||||||
fallback_message = f"日志输出错误: {repr(message)}"
|
|
||||||
print(f"[重置处理] {fallback_message}", flush=True)
|
|
||||||
logger.info(f"[重置处理] {fallback_message}")
|
|
||||||
|
|
||||||
def create_action_log(message: str, emoji: str = "📝") -> Dict[str, Any]:
|
|
||||||
"""创建一个动作日志 - 支持中文和emoji"""
|
|
||||||
try:
|
|
||||||
full_message = f"{emoji} {message}"
|
|
||||||
debug_print(full_message)
|
|
||||||
logger.info(full_message)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {
|
|
||||||
"time": 0.1,
|
|
||||||
"log_message": full_message,
|
|
||||||
"progress_message": full_message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
# 如果emoji有问题,使用纯文本
|
|
||||||
safe_message = f"[日志] {message}"
|
|
||||||
debug_print(safe_message)
|
|
||||||
logger.info(safe_message)
|
|
||||||
|
|
||||||
return {
|
|
||||||
"action_name": "wait",
|
|
||||||
"action_kwargs": {
|
|
||||||
"time": 0.1,
|
|
||||||
"log_message": safe_message,
|
|
||||||
"progress_message": safe_message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|
||||||
"""
|
|
||||||
查找溶剂容器,支持多种匹配模式
|
|
||||||
|
|
||||||
Args:
|
|
||||||
G: 网络图
|
|
||||||
solvent: 溶剂名称(如 "methanol", "ethanol", "water")
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 溶剂容器ID
|
|
||||||
"""
|
|
||||||
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器...")
|
|
||||||
|
|
||||||
# 构建可能的容器名称
|
|
||||||
possible_names = [
|
|
||||||
f"flask_{solvent}", # flask_methanol
|
|
||||||
f"bottle_{solvent}", # bottle_methanol
|
|
||||||
f"reagent_{solvent}", # reagent_methanol
|
|
||||||
f"reagent_bottle_{solvent}", # reagent_bottle_methanol
|
|
||||||
f"{solvent}_flask", # methanol_flask
|
|
||||||
f"{solvent}_bottle", # methanol_bottle
|
|
||||||
f"{solvent}", # methanol
|
|
||||||
f"vessel_{solvent}", # vessel_methanol
|
|
||||||
]
|
|
||||||
|
|
||||||
debug_print(f"🎯 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个)")
|
|
||||||
|
|
||||||
# 第一步:通过容器名称匹配
|
|
||||||
debug_print("📋 方法1: 精确名称匹配...")
|
|
||||||
for vessel_name in possible_names:
|
|
||||||
if vessel_name in G.nodes():
|
|
||||||
debug_print(f"✅ 通过名称匹配找到容器: {vessel_name}")
|
|
||||||
return vessel_name
|
|
||||||
debug_print("⚠️ 精确名称匹配失败,尝试模糊匹配...")
|
|
||||||
|
|
||||||
# 第二步:通过模糊匹配
|
|
||||||
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:
|
|
||||||
debug_print(f"✅ 通过模糊匹配找到容器: {node_id}")
|
|
||||||
return node_id
|
|
||||||
debug_print("⚠️ 模糊匹配失败,尝试液体类型匹配...")
|
|
||||||
|
|
||||||
# 第三步:通过液体类型匹配
|
|
||||||
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', {})
|
|
||||||
liquids = vessel_data.get('liquid', [])
|
|
||||||
|
|
||||||
for liquid in liquids:
|
|
||||||
if isinstance(liquid, dict):
|
|
||||||
liquid_type = (liquid.get('liquid_type') or liquid.get('name', '')).lower()
|
|
||||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
|
||||||
|
|
||||||
if solvent.lower() in liquid_type or solvent.lower() in reagent_name:
|
|
||||||
debug_print(f"✅ 通过液体类型匹配找到容器: {node_id}")
|
|
||||||
return node_id
|
|
||||||
|
|
||||||
# 列出可用容器帮助调试
|
|
||||||
debug_print("📊 显示可用容器信息...")
|
|
||||||
available_containers = []
|
|
||||||
for node_id in G.nodes():
|
|
||||||
if G.nodes[node_id].get('type') == 'container':
|
|
||||||
vessel_data = G.nodes[node_id].get('data', {})
|
|
||||||
liquids = vessel_data.get('liquid', [])
|
|
||||||
liquid_types = [liquid.get('liquid_type', '') or liquid.get('name', '')
|
|
||||||
for liquid in liquids if isinstance(liquid, dict)]
|
|
||||||
|
|
||||||
available_containers.append({
|
|
||||||
'id': node_id,
|
|
||||||
'name': G.nodes[node_id].get('name', ''),
|
|
||||||
'liquids': liquid_types,
|
|
||||||
'reagent_name': vessel_data.get('reagent_name', '')
|
|
||||||
})
|
|
||||||
|
|
||||||
debug_print(f"📋 可用容器列表 (共{len(available_containers)}个):")
|
|
||||||
for i, container in enumerate(available_containers[:5]): # 只显示前5个
|
|
||||||
debug_print(f" {i+1}. 🥽 {container['id']}: {container['name']}")
|
|
||||||
debug_print(f" 💧 液体: {container['liquids']}")
|
|
||||||
debug_print(f" 🧪 试剂: {container['reagent_name']}")
|
|
||||||
|
|
||||||
if len(available_containers) > 5:
|
|
||||||
debug_print(f" ... 还有 {len(available_containers)-5} 个容器")
|
|
||||||
|
|
||||||
debug_print(f"❌ 找不到溶剂 '{solvent}' 对应的容器")
|
|
||||||
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器。尝试了: {possible_names[:3]}...")
|
|
||||||
|
|
||||||
def generate_reset_handling_protocol(
|
def generate_reset_handling_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
solvent: str,
|
solvent: str,
|
||||||
vessel: Optional[str] = None, # 🆕 新增可选vessel参数
|
vessel: Optional[str] = None,
|
||||||
**kwargs # 接收其他可能的参数但不使用
|
**kwargs
|
||||||
) -> List[Dict[str, Any]]:
|
) -> List[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
生成重置处理协议序列 - 支持自定义容器
|
生成重置处理协议序列 - 支持自定义容器
|
||||||
@@ -175,78 +30,57 @@ def generate_reset_handling_protocol(
|
|||||||
"""
|
"""
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 🔧 修改:支持自定义vessel参数
|
target_vessel = vessel if vessel is not None else "main_reactor"
|
||||||
target_vessel = vessel if vessel is not None else "main_reactor" # 默认目标容器
|
volume = 50.0
|
||||||
volume = 50.0 # 默认体积 50 mL
|
|
||||||
|
|
||||||
debug_print("=" * 60)
|
debug_print(f"开始生成重置处理协议: solvent={solvent}, vessel={target_vessel}, volume={volume}mL")
|
||||||
debug_print("🚀 开始生成重置处理协议")
|
|
||||||
debug_print(f"📋 输入参数:")
|
|
||||||
debug_print(f" 🧪 溶剂: {solvent}")
|
|
||||||
debug_print(f" 🥽 目标容器: {target_vessel} {'(默认)' if vessel is None else '(指定)'}")
|
|
||||||
debug_print(f" 💧 体积: {volume} mL")
|
|
||||||
debug_print(f" ⚙️ 其他参数: {kwargs}")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
# 添加初始日志
|
# 添加初始日志
|
||||||
action_sequence.append(create_action_log(f"开始重置处理操作 - 容器: {target_vessel}", "🎬"))
|
action_sequence.append(action_log(f"开始重置处理操作 - 容器: {target_vessel}", "🎬"))
|
||||||
action_sequence.append(create_action_log(f"使用溶剂: {solvent}", "🧪"))
|
action_sequence.append(action_log(f"使用溶剂: {solvent}", "🧪"))
|
||||||
action_sequence.append(create_action_log(f"重置体积: {volume}mL", "💧"))
|
action_sequence.append(action_log(f"重置体积: {volume}mL", "💧"))
|
||||||
|
|
||||||
if vessel is None:
|
if vessel is None:
|
||||||
action_sequence.append(create_action_log("使用默认目标容器: main_reactor", "⚙️"))
|
action_sequence.append(action_log("使用默认目标容器: main_reactor", "⚙️"))
|
||||||
else:
|
else:
|
||||||
action_sequence.append(create_action_log(f"使用指定目标容器: {vessel}", "🎯"))
|
action_sequence.append(action_log(f"使用指定目标容器: {vessel}", "🎯"))
|
||||||
|
|
||||||
# 1. 验证目标容器存在
|
# 1. 验证目标容器存在
|
||||||
debug_print("🔍 步骤1: 验证目标容器...")
|
action_sequence.append(action_log("正在验证目标容器...", "🔍"))
|
||||||
action_sequence.append(create_action_log("正在验证目标容器...", "🔍"))
|
|
||||||
|
|
||||||
if target_vessel not in G.nodes():
|
if target_vessel not in G.nodes():
|
||||||
debug_print(f"❌ 目标容器 '{target_vessel}' 不存在于系统中!")
|
action_sequence.append(action_log(f"目标容器 '{target_vessel}' 不存在", "❌"))
|
||||||
action_sequence.append(create_action_log(f"目标容器 '{target_vessel}' 不存在", "❌"))
|
|
||||||
raise ValueError(f"目标容器 '{target_vessel}' 不存在于系统中")
|
raise ValueError(f"目标容器 '{target_vessel}' 不存在于系统中")
|
||||||
|
|
||||||
debug_print(f"✅ 目标容器 '{target_vessel}' 验证通过")
|
action_sequence.append(action_log(f"目标容器验证通过: {target_vessel}", "✅"))
|
||||||
action_sequence.append(create_action_log(f"目标容器验证通过: {target_vessel}", "✅"))
|
|
||||||
|
|
||||||
# 2. 查找溶剂容器
|
# 2. 查找溶剂容器
|
||||||
debug_print("🔍 步骤2: 查找溶剂容器...")
|
action_sequence.append(action_log("正在查找溶剂容器...", "🔍"))
|
||||||
action_sequence.append(create_action_log("正在查找溶剂容器...", "🔍"))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
solvent_vessel = find_solvent_vessel(G, solvent)
|
solvent_vessel = find_solvent_vessel(G, solvent)
|
||||||
debug_print(f"✅ 找到溶剂容器: {solvent_vessel}")
|
debug_print(f"找到溶剂容器: {solvent_vessel}")
|
||||||
action_sequence.append(create_action_log(f"找到溶剂容器: {solvent_vessel}", "✅"))
|
action_sequence.append(action_log(f"找到溶剂容器: {solvent_vessel}", "✅"))
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
debug_print(f"❌ 溶剂容器查找失败: {str(e)}")
|
action_sequence.append(action_log(f"溶剂容器查找失败: {str(e)}", "❌"))
|
||||||
action_sequence.append(create_action_log(f"溶剂容器查找失败: {str(e)}", "❌"))
|
|
||||||
raise ValueError(f"无法找到溶剂 '{solvent}': {str(e)}")
|
raise ValueError(f"无法找到溶剂 '{solvent}': {str(e)}")
|
||||||
|
|
||||||
# 3. 验证路径存在
|
# 3. 验证路径存在
|
||||||
debug_print("🔍 步骤3: 验证传输路径...")
|
action_sequence.append(action_log("正在验证传输路径...", "🛤️"))
|
||||||
action_sequence.append(create_action_log("正在验证传输路径...", "🛤️"))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
path = nx.shortest_path(G, source=solvent_vessel, target=target_vessel)
|
path = nx.shortest_path(G, source=solvent_vessel, target=target_vessel)
|
||||||
debug_print(f"✅ 找到路径: {' → '.join(path)}")
|
action_sequence.append(action_log(f"传输路径: {' → '.join(path)}", "🛤️"))
|
||||||
action_sequence.append(create_action_log(f"传输路径: {' → '.join(path)}", "🛤️"))
|
|
||||||
except nx.NetworkXNoPath:
|
except nx.NetworkXNoPath:
|
||||||
debug_print(f"❌ 路径不可达: {solvent_vessel} → {target_vessel}")
|
action_sequence.append(action_log(f"路径不可达: {solvent_vessel} → {target_vessel}", "❌"))
|
||||||
action_sequence.append(create_action_log(f"路径不可达: {solvent_vessel} → {target_vessel}", "❌"))
|
|
||||||
raise ValueError(f"从溶剂容器 '{solvent_vessel}' 到目标容器 '{target_vessel}' 没有可用路径")
|
raise ValueError(f"从溶剂容器 '{solvent_vessel}' 到目标容器 '{target_vessel}' 没有可用路径")
|
||||||
|
|
||||||
# 4. 使用pump_protocol转移溶剂
|
# 4. 使用pump_protocol转移溶剂
|
||||||
debug_print("🔍 步骤4: 转移溶剂...")
|
action_sequence.append(action_log("开始溶剂转移操作...", "🚰"))
|
||||||
action_sequence.append(create_action_log("开始溶剂转移操作...", "🚰"))
|
action_sequence.append(action_log(f"转移: {solvent_vessel} → {target_vessel} ({volume}mL)", "🚛"))
|
||||||
|
|
||||||
debug_print(f"🚛 开始转移: {solvent_vessel} → {target_vessel}")
|
|
||||||
debug_print(f"💧 转移体积: {volume} mL")
|
|
||||||
action_sequence.append(create_action_log(f"转移: {solvent_vessel} → {target_vessel} ({volume}mL)", "🚛"))
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
debug_print("🔄 生成泵送协议...")
|
action_sequence.append(action_log("正在生成泵送协议...", "🔄"))
|
||||||
action_sequence.append(create_action_log("正在生成泵送协议...", "🔄"))
|
|
||||||
|
|
||||||
pump_actions = generate_pump_protocol_with_rinsing(
|
pump_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
@@ -256,40 +90,33 @@ def generate_reset_handling_protocol(
|
|||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
viscous=False,
|
viscous=False,
|
||||||
rinsing_solvent="", # 重置处理不需要清洗
|
rinsing_solvent="",
|
||||||
rinsing_volume=0.0,
|
rinsing_volume=0.0,
|
||||||
rinsing_repeats=0,
|
rinsing_repeats=0,
|
||||||
solid=False,
|
solid=False,
|
||||||
flowrate=2.5, # 正常流速
|
flowrate=2.5,
|
||||||
transfer_flowrate=0.5 # 正常转移流速
|
transfer_flowrate=0.5
|
||||||
)
|
)
|
||||||
|
|
||||||
action_sequence.extend(pump_actions)
|
action_sequence.extend(pump_actions)
|
||||||
debug_print(f"✅ 泵送协议已添加: {len(pump_actions)} 个动作")
|
debug_print(f"泵送协议已添加: {len(pump_actions)} 个动作")
|
||||||
action_sequence.append(create_action_log(f"泵送协议完成 ({len(pump_actions)} 个操作)", "✅"))
|
action_sequence.append(action_log(f"泵送协议完成 ({len(pump_actions)} 个操作)", "✅"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 泵送协议生成失败: {str(e)}")
|
action_sequence.append(action_log(f"泵送协议生成失败: {str(e)}", "❌"))
|
||||||
action_sequence.append(create_action_log(f"泵送协议生成失败: {str(e)}", "❌"))
|
|
||||||
raise ValueError(f"生成泵协议时出错: {str(e)}")
|
raise ValueError(f"生成泵协议时出错: {str(e)}")
|
||||||
|
|
||||||
# 5. 等待溶剂稳定
|
# 5. 等待溶剂稳定
|
||||||
debug_print("🔍 步骤5: 等待溶剂稳定...")
|
action_sequence.append(action_log("等待溶剂稳定...", "⏳"))
|
||||||
action_sequence.append(create_action_log("等待溶剂稳定...", "⏳"))
|
|
||||||
|
|
||||||
# 模拟运行时间优化
|
|
||||||
debug_print("⏱️ 检查模拟运行时间限制...")
|
|
||||||
original_wait_time = 10.0 # 原始等待时间
|
|
||||||
simulation_time_limit = 5.0 # 模拟运行时间限制:5秒
|
|
||||||
|
|
||||||
|
original_wait_time = 10.0
|
||||||
|
simulation_time_limit = 5.0
|
||||||
final_wait_time = min(original_wait_time, simulation_time_limit)
|
final_wait_time = min(original_wait_time, simulation_time_limit)
|
||||||
|
|
||||||
if original_wait_time > simulation_time_limit:
|
if original_wait_time > simulation_time_limit:
|
||||||
debug_print(f"🎮 模拟运行优化: {original_wait_time}s → {final_wait_time}s")
|
action_sequence.append(action_log(f"时间优化: {original_wait_time}s → {final_wait_time}s", "⚡"))
|
||||||
action_sequence.append(create_action_log(f"时间优化: {original_wait_time}s → {final_wait_time}s", "⚡"))
|
|
||||||
else:
|
else:
|
||||||
debug_print(f"✅ 时间在限制内: {final_wait_time}s 保持不变")
|
action_sequence.append(action_log(f"等待时间: {final_wait_time}s", "⏰"))
|
||||||
action_sequence.append(create_action_log(f"等待时间: {final_wait_time}s", "⏰"))
|
|
||||||
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
@@ -298,34 +125,20 @@ def generate_reset_handling_protocol(
|
|||||||
"description": f"等待溶剂 {solvent} 在容器 {target_vessel} 中稳定" + (f" (模拟时间)" if original_wait_time != final_wait_time else "")
|
"description": f"等待溶剂 {solvent} 在容器 {target_vessel} 中稳定" + (f" (模拟时间)" if original_wait_time != final_wait_time else "")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
debug_print(f"✅ 稳定等待已添加: {final_wait_time}s")
|
|
||||||
|
|
||||||
# 显示时间调整信息
|
|
||||||
if original_wait_time != final_wait_time:
|
if original_wait_time != final_wait_time:
|
||||||
debug_print(f"🎭 模拟优化说明: 原计划 {original_wait_time}s,实际模拟 {final_wait_time}s")
|
action_sequence.append(action_log("应用模拟时间优化", "🎭"))
|
||||||
action_sequence.append(create_action_log("应用模拟时间优化", "🎭"))
|
|
||||||
|
|
||||||
# 总结
|
# 总结
|
||||||
debug_print("=" * 60)
|
debug_print(f"重置处理协议生成完成: {len(action_sequence)} 个动作, {solvent_vessel} -> {target_vessel}, {volume}mL")
|
||||||
debug_print(f"🎉 重置处理协议生成完成!")
|
|
||||||
debug_print(f"📊 总结信息:")
|
|
||||||
debug_print(f" 📋 总动作数: {len(action_sequence)} 个")
|
|
||||||
debug_print(f" 🧪 溶剂: {solvent}")
|
|
||||||
debug_print(f" 🥽 源容器: {solvent_vessel}")
|
|
||||||
debug_print(f" 🥽 目标容器: {target_vessel} {'(默认)' if vessel is None else '(指定)'}")
|
|
||||||
debug_print(f" 💧 转移体积: {volume} mL")
|
|
||||||
debug_print(f" ⏱️ 预计总时间: {(final_wait_time + 5):.0f} 秒")
|
|
||||||
debug_print(f" 🎯 操作结果: 已添加 {volume} mL {solvent} 到 {target_vessel}")
|
|
||||||
debug_print("=" * 60)
|
|
||||||
|
|
||||||
# 添加完成日志
|
|
||||||
summary_msg = f"重置处理完成: {target_vessel} (使用 {volume}mL {solvent})"
|
summary_msg = f"重置处理完成: {target_vessel} (使用 {volume}mL {solvent})"
|
||||||
if vessel is None:
|
if vessel is None:
|
||||||
summary_msg += " [默认容器]"
|
summary_msg += " [默认容器]"
|
||||||
else:
|
else:
|
||||||
summary_msg += " [指定容器]"
|
summary_msg += " [指定容器]"
|
||||||
|
|
||||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
action_sequence.append(action_log(summary_msg, "🎉"))
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
@@ -333,55 +146,29 @@ def generate_reset_handling_protocol(
|
|||||||
|
|
||||||
def reset_main_reactor(G: nx.DiGraph, solvent: str = "methanol", **kwargs) -> List[Dict[str, Any]]:
|
def reset_main_reactor(G: nx.DiGraph, solvent: str = "methanol", **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""重置主反应器 (默认行为)"""
|
"""重置主反应器 (默认行为)"""
|
||||||
debug_print(f"🔄 重置主反应器,使用溶剂: {solvent}")
|
|
||||||
return generate_reset_handling_protocol(G, solvent=solvent, vessel=None, **kwargs)
|
return generate_reset_handling_protocol(G, solvent=solvent, vessel=None, **kwargs)
|
||||||
|
|
||||||
def reset_custom_vessel(G: nx.DiGraph, vessel: str, solvent: str = "methanol", **kwargs) -> List[Dict[str, Any]]:
|
def reset_custom_vessel(G: nx.DiGraph, vessel: str, solvent: str = "methanol", **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""重置指定容器"""
|
"""重置指定容器"""
|
||||||
debug_print(f"🔄 重置指定容器: {vessel},使用溶剂: {solvent}")
|
|
||||||
return generate_reset_handling_protocol(G, solvent=solvent, vessel=vessel, **kwargs)
|
return generate_reset_handling_protocol(G, solvent=solvent, vessel=vessel, **kwargs)
|
||||||
|
|
||||||
def reset_with_water(G: nx.DiGraph, vessel: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
def reset_with_water(G: nx.DiGraph, vessel: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""使用水重置容器"""
|
"""使用水重置容器"""
|
||||||
target = vessel or "main_reactor"
|
|
||||||
debug_print(f"💧 使用水重置容器: {target}")
|
|
||||||
return generate_reset_handling_protocol(G, solvent="water", vessel=vessel, **kwargs)
|
return generate_reset_handling_protocol(G, solvent="water", vessel=vessel, **kwargs)
|
||||||
|
|
||||||
def reset_with_methanol(G: nx.DiGraph, vessel: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
def reset_with_methanol(G: nx.DiGraph, vessel: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""使用甲醇重置容器"""
|
"""使用甲醇重置容器"""
|
||||||
target = vessel or "main_reactor"
|
|
||||||
debug_print(f"🧪 使用甲醇重置容器: {target}")
|
|
||||||
return generate_reset_handling_protocol(G, solvent="methanol", vessel=vessel, **kwargs)
|
return generate_reset_handling_protocol(G, solvent="methanol", vessel=vessel, **kwargs)
|
||||||
|
|
||||||
def reset_with_ethanol(G: nx.DiGraph, vessel: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
def reset_with_ethanol(G: nx.DiGraph, vessel: Optional[str] = None, **kwargs) -> List[Dict[str, Any]]:
|
||||||
"""使用乙醇重置容器"""
|
"""使用乙醇重置容器"""
|
||||||
target = vessel or "main_reactor"
|
|
||||||
debug_print(f"🧪 使用乙醇重置容器: {target}")
|
|
||||||
return generate_reset_handling_protocol(G, solvent="ethanol", vessel=vessel, **kwargs)
|
return generate_reset_handling_protocol(G, solvent="ethanol", vessel=vessel, **kwargs)
|
||||||
|
|
||||||
# 测试函数
|
# 测试函数
|
||||||
def test_reset_handling_protocol():
|
def test_reset_handling_protocol():
|
||||||
"""测试重置处理协议"""
|
"""测试重置处理协议"""
|
||||||
debug_print("=== 重置处理协议增强中文版测试 ===")
|
debug_print("=== 重置处理协议测试 ===")
|
||||||
|
debug_print("测试完成")
|
||||||
# 测试溶剂名称
|
|
||||||
debug_print("🧪 测试常用溶剂名称...")
|
|
||||||
test_solvents = ["methanol", "ethanol", "water", "acetone", "dmso"]
|
|
||||||
for solvent in test_solvents:
|
|
||||||
debug_print(f" 🔍 测试溶剂: {solvent}")
|
|
||||||
|
|
||||||
# 测试容器参数
|
|
||||||
debug_print("🥽 测试容器参数...")
|
|
||||||
test_cases = [
|
|
||||||
{"solvent": "methanol", "vessel": None, "desc": "默认容器"},
|
|
||||||
{"solvent": "ethanol", "vessel": "reactor_2", "desc": "指定容器"},
|
|
||||||
{"solvent": "water", "vessel": "flask_1", "desc": "自定义容器"}
|
|
||||||
]
|
|
||||||
|
|
||||||
for case in test_cases:
|
|
||||||
debug_print(f" 🧪 测试案例: {case['desc']} - {case['solvent']} -> {case['vessel'] or 'main_reactor'}")
|
|
||||||
|
|
||||||
debug_print("✅ 测试完成")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_reset_handling_protocol()
|
test_reset_handling_protocol()
|
||||||
@@ -2,15 +2,13 @@ from typing import List, Dict, Any, Union
|
|||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
from .utils.vessel_parser import get_vessel
|
from .utils.vessel_parser import get_vessel, find_solvent_vessel
|
||||||
|
from .utils.resource_helper import get_resource_id, get_resource_data, get_resource_liquid_volume, update_vessel_volume
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[RUN_COLUMN] {message}")
|
|
||||||
|
|
||||||
def parse_percentage(pct_str: str) -> float:
|
def parse_percentage(pct_str: str) -> float:
|
||||||
"""
|
"""
|
||||||
解析百分比字符串为数值
|
解析百分比字符串为数值
|
||||||
@@ -25,19 +23,16 @@ def parse_percentage(pct_str: str) -> float:
|
|||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
pct_str = pct_str.strip().lower()
|
pct_str = pct_str.strip().lower()
|
||||||
debug_print(f"🔍 解析百分比: '{pct_str}'")
|
|
||||||
|
|
||||||
# 移除百分号和空格
|
# 移除百分号和空格
|
||||||
pct_clean = re.sub(r'[%\s]', '', pct_str)
|
pct_clean = re.sub(r'[%\s]', '', pct_str)
|
||||||
|
|
||||||
# 提取数字
|
|
||||||
match = re.search(r'([0-9]*\.?[0-9]+)', pct_clean)
|
match = re.search(r'([0-9]*\.?[0-9]+)', pct_clean)
|
||||||
if match:
|
if match:
|
||||||
value = float(match.group(1))
|
value = float(match.group(1))
|
||||||
debug_print(f"✅ 百分比解析结果: {value}%")
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
debug_print(f"⚠️ 无法解析百分比: '{pct_str}',返回0.0")
|
debug_print(f"无法解析百分比: '{pct_str}',返回0.0")
|
||||||
return 0.0
|
return 0.0
|
||||||
|
|
||||||
def parse_ratio(ratio_str: str) -> tuple:
|
def parse_ratio(ratio_str: str) -> tuple:
|
||||||
@@ -48,13 +43,12 @@ def parse_ratio(ratio_str: str) -> tuple:
|
|||||||
ratio_str: 比例字符串(如 "5:95", "1:1", "40:60")
|
ratio_str: 比例字符串(如 "5:95", "1:1", "40:60")
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
tuple: (ratio1, ratio2) 两个比例值
|
tuple: (ratio1, ratio2) 两个比例值(百分比)
|
||||||
"""
|
"""
|
||||||
if not ratio_str or not ratio_str.strip():
|
if not ratio_str or not ratio_str.strip():
|
||||||
return (50.0, 50.0) # 默认1:1
|
return (50.0, 50.0)
|
||||||
|
|
||||||
ratio_str = ratio_str.strip()
|
ratio_str = ratio_str.strip()
|
||||||
debug_print(f"🔍 解析比例: '{ratio_str}'")
|
|
||||||
|
|
||||||
# 支持多种分隔符:: / -
|
# 支持多种分隔符:: / -
|
||||||
if ':' in ratio_str:
|
if ':' in ratio_str:
|
||||||
@@ -66,7 +60,7 @@ def parse_ratio(ratio_str: str) -> tuple:
|
|||||||
elif 'to' in ratio_str.lower():
|
elif 'to' in ratio_str.lower():
|
||||||
parts = ratio_str.lower().split('to')
|
parts = ratio_str.lower().split('to')
|
||||||
else:
|
else:
|
||||||
debug_print(f"⚠️ 无法解析比例格式: '{ratio_str}',使用默认1:1")
|
debug_print(f"无法解析比例格式: '{ratio_str}',使用默认1:1")
|
||||||
return (50.0, 50.0)
|
return (50.0, 50.0)
|
||||||
|
|
||||||
if len(parts) >= 2:
|
if len(parts) >= 2:
|
||||||
@@ -75,16 +69,14 @@ def parse_ratio(ratio_str: str) -> tuple:
|
|||||||
ratio2 = float(parts[1].strip())
|
ratio2 = float(parts[1].strip())
|
||||||
total = ratio1 + ratio2
|
total = ratio1 + ratio2
|
||||||
|
|
||||||
# 转换为百分比
|
|
||||||
pct1 = (ratio1 / total) * 100
|
pct1 = (ratio1 / total) * 100
|
||||||
pct2 = (ratio2 / total) * 100
|
pct2 = (ratio2 / total) * 100
|
||||||
|
|
||||||
debug_print(f"✅ 比例解析结果: {ratio1}:{ratio2} -> {pct1:.1f}%:{pct2:.1f}%")
|
|
||||||
return (pct1, pct2)
|
return (pct1, pct2)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
debug_print(f"⚠️ 比例数值转换失败: {str(e)}")
|
debug_print(f"比例数值转换失败: {str(e)}")
|
||||||
|
|
||||||
debug_print(f"⚠️ 比例解析失败,使用默认1:1")
|
debug_print(f"比例解析失败,使用默认1:1")
|
||||||
return (50.0, 50.0)
|
return (50.0, 50.0)
|
||||||
|
|
||||||
def parse_rf_value(rf_str: str) -> float:
|
def parse_rf_value(rf_str: str) -> float:
|
||||||
@@ -98,66 +90,49 @@ def parse_rf_value(rf_str: str) -> float:
|
|||||||
float: Rf值(0-1)
|
float: Rf值(0-1)
|
||||||
"""
|
"""
|
||||||
if not rf_str or not rf_str.strip():
|
if not rf_str or not rf_str.strip():
|
||||||
return 0.3 # 默认Rf值
|
return 0.3
|
||||||
|
|
||||||
rf_str = rf_str.strip().lower()
|
rf_str = rf_str.strip().lower()
|
||||||
debug_print(f"🔍 解析Rf值: '{rf_str}'")
|
|
||||||
|
|
||||||
# 处理未知Rf值
|
|
||||||
if rf_str in ['?', 'unknown', 'tbd', 'to be determined']:
|
if rf_str in ['?', 'unknown', 'tbd', 'to be determined']:
|
||||||
default_rf = 0.3
|
return 0.3
|
||||||
debug_print(f"❓ 检测到未知Rf值,使用默认值: {default_rf}")
|
|
||||||
return default_rf
|
|
||||||
|
|
||||||
# 提取数字
|
|
||||||
match = re.search(r'([0-9]*\.?[0-9]+)', rf_str)
|
match = re.search(r'([0-9]*\.?[0-9]+)', rf_str)
|
||||||
if match:
|
if match:
|
||||||
value = float(match.group(1))
|
value = float(match.group(1))
|
||||||
# 确保Rf值在0-1范围内
|
|
||||||
if value > 1.0:
|
if value > 1.0:
|
||||||
value = value / 100.0 # 可能是百分比形式
|
value = value / 100.0
|
||||||
value = max(0.0, min(1.0, value)) # 限制在0-1范围
|
value = max(0.0, min(1.0, value))
|
||||||
debug_print(f"✅ Rf值解析结果: {value}")
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
debug_print(f"⚠️ 无法解析Rf值: '{rf_str}',使用默认值0.3")
|
|
||||||
return 0.3
|
return 0.3
|
||||||
|
|
||||||
def find_column_device(G: nx.DiGraph) -> str:
|
def find_column_device(G: nx.DiGraph) -> str:
|
||||||
"""查找柱层析设备"""
|
"""查找柱层析设备"""
|
||||||
debug_print("🔍 查找柱层析设备...")
|
|
||||||
|
|
||||||
# 查找虚拟柱设备
|
|
||||||
for node in G.nodes():
|
for node in G.nodes():
|
||||||
node_data = G.nodes[node]
|
node_data = G.nodes[node]
|
||||||
node_class = node_data.get('class', '') or ''
|
node_class = node_data.get('class', '') or ''
|
||||||
|
|
||||||
if 'virtual_column' in node_class.lower() or 'column' in node_class.lower():
|
if 'virtual_column' in node_class.lower() or 'column' in node_class.lower():
|
||||||
debug_print(f"🎉 找到柱层析设备: {node} ✨")
|
debug_print(f"找到柱层析设备: {node}")
|
||||||
return node
|
return node
|
||||||
|
|
||||||
# 如果没有找到,尝试创建虚拟设备名称
|
|
||||||
possible_names = ['column_1', 'virtual_column_1', 'chromatography_column_1']
|
possible_names = ['column_1', 'virtual_column_1', 'chromatography_column_1']
|
||||||
for name in possible_names:
|
for name in possible_names:
|
||||||
if name in G.nodes():
|
if name in G.nodes():
|
||||||
debug_print(f"🎉 找到柱设备: {name} ✨")
|
debug_print(f"找到柱设备: {name}")
|
||||||
return name
|
return name
|
||||||
|
|
||||||
debug_print("⚠️ 未找到柱层析设备,将使用pump protocol直接转移")
|
debug_print("未找到柱层析设备,将使用pump protocol直接转移")
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def find_column_vessel(G: nx.DiGraph, column: str) -> str:
|
def find_column_vessel(G: nx.DiGraph, column: str) -> str:
|
||||||
"""查找柱容器"""
|
"""查找柱容器"""
|
||||||
debug_print(f"🔍 查找柱容器: '{column}'")
|
|
||||||
|
|
||||||
# 直接检查column参数是否是容器
|
|
||||||
if column in G.nodes():
|
if column in G.nodes():
|
||||||
node_type = G.nodes[column].get('type', '')
|
node_type = G.nodes[column].get('type', '')
|
||||||
if node_type == 'container':
|
if node_type == 'container':
|
||||||
debug_print(f"🎉 找到柱容器: {column} ✨")
|
|
||||||
return column
|
return column
|
||||||
|
|
||||||
# 尝试常见的命名规则
|
|
||||||
possible_names = [
|
possible_names = [
|
||||||
f"column_{column}",
|
f"column_{column}",
|
||||||
f"{column}_column",
|
f"{column}_column",
|
||||||
@@ -174,206 +149,20 @@ def find_column_vessel(G: nx.DiGraph, column: str) -> str:
|
|||||||
if vessel_name in G.nodes():
|
if vessel_name in G.nodes():
|
||||||
node_type = G.nodes[vessel_name].get('type', '')
|
node_type = G.nodes[vessel_name].get('type', '')
|
||||||
if node_type == 'container':
|
if node_type == 'container':
|
||||||
debug_print(f"🎉 找到柱容器: {vessel_name} ✨")
|
|
||||||
return vessel_name
|
return vessel_name
|
||||||
|
|
||||||
debug_print(f"⚠️ 未找到柱容器,将直接在源容器中进行分离")
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
|
||||||
"""查找溶剂容器 - 增强版"""
|
|
||||||
if not solvent or not solvent.strip():
|
|
||||||
return ""
|
|
||||||
|
|
||||||
solvent = solvent.strip().replace(' ', '_').lower()
|
|
||||||
debug_print(f"🔍 查找溶剂容器: '{solvent}'")
|
|
||||||
|
|
||||||
# 🔧 方法1:直接搜索 data.reagent_name
|
|
||||||
for node in G.nodes():
|
|
||||||
node_data = G.nodes[node].get('data', {})
|
|
||||||
node_type = G.nodes[node].get('type', '')
|
|
||||||
|
|
||||||
# 只搜索容器类型的节点
|
|
||||||
if node_type == 'container':
|
|
||||||
reagent_name = node_data.get('reagent_name', '').lower()
|
|
||||||
reagent_config = G.nodes[node].get('config', {}).get('reagent', '').lower()
|
|
||||||
|
|
||||||
# 检查 data.reagent_name 和 config.reagent
|
|
||||||
if reagent_name == solvent or reagent_config == solvent:
|
|
||||||
debug_print(f"🎉 通过reagent_name找到溶剂容器: {node} (reagent: {reagent_name or reagent_config}) ✨")
|
|
||||||
return node
|
|
||||||
|
|
||||||
# 模糊匹配 reagent_name
|
|
||||||
if solvent in reagent_name or reagent_name in solvent:
|
|
||||||
debug_print(f"🎉 通过reagent_name模糊匹配到溶剂容器: {node} (reagent: {reagent_name}) ✨")
|
|
||||||
return node
|
|
||||||
|
|
||||||
if solvent in reagent_config or reagent_config in solvent:
|
|
||||||
debug_print(f"🎉 通过config.reagent模糊匹配到溶剂容器: {node} (reagent: {reagent_config}) ✨")
|
|
||||||
return node
|
|
||||||
|
|
||||||
# 🔧 方法2:常见的溶剂容器命名规则
|
|
||||||
possible_names = [
|
|
||||||
f"flask_{solvent}",
|
|
||||||
f"bottle_{solvent}",
|
|
||||||
f"reagent_{solvent}",
|
|
||||||
f"{solvent}_bottle",
|
|
||||||
f"{solvent}_flask",
|
|
||||||
f"solvent_{solvent}",
|
|
||||||
f"reagent_bottle_{solvent}"
|
|
||||||
]
|
|
||||||
|
|
||||||
for vessel_name in possible_names:
|
|
||||||
if vessel_name in G.nodes():
|
|
||||||
node_type = G.nodes[vessel_name].get('type', '')
|
|
||||||
if node_type == 'container':
|
|
||||||
debug_print(f"🎉 通过命名规则找到溶剂容器: {vessel_name} ✨")
|
|
||||||
return vessel_name
|
|
||||||
|
|
||||||
# 🔧 方法3:节点名称模糊匹配
|
|
||||||
for node in G.nodes():
|
|
||||||
node_type = G.nodes[node].get('type', '')
|
|
||||||
if node_type == 'container':
|
|
||||||
if ('flask_' in node or 'bottle_' in node or 'reagent_' in node) and solvent in node.lower():
|
|
||||||
debug_print(f"🎉 通过节点名称模糊匹配到溶剂容器: {node} ✨")
|
|
||||||
return node
|
|
||||||
|
|
||||||
# 🔧 方法4:特殊溶剂名称映射
|
|
||||||
solvent_mapping = {
|
|
||||||
'dmf': ['dmf', 'dimethylformamide', 'n,n-dimethylformamide'],
|
|
||||||
'ethyl_acetate': ['ethyl_acetate', 'ethylacetate', 'etoac', 'ea'],
|
|
||||||
'hexane': ['hexane', 'hexanes', 'n-hexane'],
|
|
||||||
'methanol': ['methanol', 'meoh', 'ch3oh'],
|
|
||||||
'water': ['water', 'h2o', 'distilled_water'],
|
|
||||||
'acetone': ['acetone', 'ch3coch3', '2-propanone'],
|
|
||||||
'dichloromethane': ['dichloromethane', 'dcm', 'ch2cl2', 'methylene_chloride'],
|
|
||||||
'chloroform': ['chloroform', 'chcl3', 'trichloromethane']
|
|
||||||
}
|
|
||||||
|
|
||||||
# 查找映射的同义词
|
|
||||||
for canonical_name, synonyms in solvent_mapping.items():
|
|
||||||
if solvent in synonyms:
|
|
||||||
debug_print(f"🔍 检测到溶剂同义词: '{solvent}' -> '{canonical_name}'")
|
|
||||||
return find_solvent_vessel(G, canonical_name) # 递归搜索
|
|
||||||
|
|
||||||
debug_print(f"⚠️ 未找到溶剂 '{solvent}' 的容器")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_vessel_liquid_volume(vessel: dict) -> float:
|
|
||||||
"""
|
|
||||||
获取容器中的液体体积 - 支持vessel字典
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vessel: 容器字典
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 液体体积(mL)
|
|
||||||
"""
|
|
||||||
if not vessel or "data" not in vessel:
|
|
||||||
debug_print(f"⚠️ 容器数据为空,返回 0.0mL")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
vessel_data = vessel["data"]
|
|
||||||
vessel_id = vessel.get("id", "unknown")
|
|
||||||
|
|
||||||
debug_print(f"🔍 读取容器 '{vessel_id}' 体积数据: {vessel_data}")
|
|
||||||
|
|
||||||
# 检查liquid_volume字段
|
|
||||||
if "liquid_volume" in vessel_data:
|
|
||||||
liquid_volume = vessel_data["liquid_volume"]
|
|
||||||
|
|
||||||
# 处理列表格式
|
|
||||||
if isinstance(liquid_volume, list):
|
|
||||||
if len(liquid_volume) > 0:
|
|
||||||
volume = liquid_volume[0]
|
|
||||||
if isinstance(volume, (int, float)):
|
|
||||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (列表格式)")
|
|
||||||
return float(volume)
|
|
||||||
|
|
||||||
# 处理直接数值格式
|
|
||||||
elif isinstance(liquid_volume, (int, float)):
|
|
||||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {liquid_volume}mL (数值格式)")
|
|
||||||
return float(liquid_volume)
|
|
||||||
|
|
||||||
# 检查其他可能的体积字段
|
|
||||||
volume_keys = ['current_volume', 'total_volume', 'volume']
|
|
||||||
for key in volume_keys:
|
|
||||||
if key in vessel_data:
|
|
||||||
try:
|
|
||||||
volume = float(vessel_data[key])
|
|
||||||
if volume > 0:
|
|
||||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (字段: {key})")
|
|
||||||
return volume
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
debug_print(f"⚠️ 无法获取容器 '{vessel_id}' 的体积,返回默认值 50.0mL")
|
|
||||||
return 50.0
|
|
||||||
|
|
||||||
def update_vessel_volume(vessel: dict, G: nx.DiGraph, new_volume: float, description: str = "") -> None:
|
|
||||||
"""
|
|
||||||
更新容器体积(同时更新vessel字典和图节点)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vessel: 容器字典
|
|
||||||
G: 网络图
|
|
||||||
new_volume: 新体积
|
|
||||||
description: 更新描述
|
|
||||||
"""
|
|
||||||
vessel_id = vessel.get("id", "unknown")
|
|
||||||
|
|
||||||
if description:
|
|
||||||
debug_print(f"🔧 更新容器体积 - {description}")
|
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel:
|
|
||||||
if "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
|
|
||||||
else:
|
|
||||||
vessel["data"]["liquid_volume"] = [new_volume]
|
|
||||||
else:
|
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
|
||||||
else:
|
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
|
||||||
else:
|
|
||||||
vessel["data"] = {"liquid_volume": new_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] = new_volume
|
|
||||||
else:
|
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = [new_volume]
|
|
||||||
else:
|
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
|
||||||
|
|
||||||
debug_print(f"📊 容器 '{vessel_id}' 体积已更新为: {new_volume:.2f}mL")
|
|
||||||
|
|
||||||
def calculate_solvent_volumes(total_volume: float, pct1: float, pct2: float) -> tuple:
|
def calculate_solvent_volumes(total_volume: float, pct1: float, pct2: float) -> tuple:
|
||||||
"""根据百分比计算溶剂体积"""
|
"""根据百分比计算溶剂体积"""
|
||||||
volume1 = (total_volume * pct1) / 100.0
|
volume1 = (total_volume * pct1) / 100.0
|
||||||
volume2 = (total_volume * pct2) / 100.0
|
volume2 = (total_volume * pct2) / 100.0
|
||||||
|
|
||||||
debug_print(f"🧮 溶剂体积计算: 总体积{total_volume}mL")
|
|
||||||
debug_print(f" - 溶剂1: {pct1}% = {volume1}mL")
|
|
||||||
debug_print(f" - 溶剂2: {pct2}% = {volume2}mL")
|
|
||||||
|
|
||||||
return (volume1, volume2)
|
return (volume1, volume2)
|
||||||
|
|
||||||
def generate_run_column_protocol(
|
def generate_run_column_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
from_vessel: dict, # 🔧 修改:从字符串改为字典类型
|
from_vessel: dict,
|
||||||
to_vessel: dict, # 🔧 修改:从字符串改为字典类型
|
to_vessel: dict,
|
||||||
column: str,
|
column: str,
|
||||||
rf: str = "",
|
rf: str = "",
|
||||||
pct1: str = "",
|
pct1: str = "",
|
||||||
@@ -403,65 +192,39 @@ def generate_run_column_protocol(
|
|||||||
List[Dict[str, Any]]: 柱层析分离操作的动作序列
|
List[Dict[str, Any]]: 柱层析分离操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从字典中提取容器ID
|
|
||||||
from_vessel_id, _ = get_vessel(from_vessel)
|
from_vessel_id, _ = get_vessel(from_vessel)
|
||||||
to_vessel_id, _ = get_vessel(to_vessel)
|
to_vessel_id, _ = get_vessel(to_vessel)
|
||||||
|
|
||||||
debug_print("🏛️" * 20)
|
debug_print(f"开始生成柱层析协议: {from_vessel_id} -> {to_vessel_id}, column={column}")
|
||||||
debug_print("🚀 开始生成柱层析协议(支持vessel字典和体积运算)✨")
|
|
||||||
debug_print(f"📝 输入参数:")
|
|
||||||
debug_print(f" 🥽 from_vessel: {from_vessel} (ID: {from_vessel_id})")
|
|
||||||
debug_print(f" 🥽 to_vessel: {to_vessel} (ID: {to_vessel_id})")
|
|
||||||
debug_print(f" 🏛️ column: '{column}'")
|
|
||||||
debug_print(f" 📊 rf: '{rf}'")
|
|
||||||
debug_print(f" 🧪 溶剂配比: pct1='{pct1}', pct2='{pct2}', ratio='{ratio}'")
|
|
||||||
debug_print(f" 🧪 溶剂名称: solvent1='{solvent1}', solvent2='{solvent2}'")
|
|
||||||
debug_print("🏛️" * 20)
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 🔧 新增:记录柱层析前的容器状态
|
# 记录柱层析前的容器状态
|
||||||
debug_print("🔍 记录柱层析前容器状态...")
|
original_from_volume = get_resource_liquid_volume(from_vessel)
|
||||||
original_from_volume = get_vessel_liquid_volume(from_vessel)
|
original_to_volume = get_resource_liquid_volume(to_vessel)
|
||||||
original_to_volume = get_vessel_liquid_volume(to_vessel)
|
|
||||||
|
|
||||||
debug_print(f"📊 柱层析前状态:")
|
|
||||||
debug_print(f" - 源容器 {from_vessel_id}: {original_from_volume:.2f}mL")
|
|
||||||
debug_print(f" - 目标容器 {to_vessel_id}: {original_to_volume:.2f}mL")
|
|
||||||
|
|
||||||
# === 参数验证 ===
|
# === 参数验证 ===
|
||||||
debug_print("📍 步骤1: 参数验证...")
|
if not from_vessel_id:
|
||||||
|
|
||||||
if not from_vessel_id: # 🔧 使用 from_vessel_id
|
|
||||||
raise ValueError("from_vessel 参数不能为空")
|
raise ValueError("from_vessel 参数不能为空")
|
||||||
if not to_vessel_id: # 🔧 使用 to_vessel_id
|
if not to_vessel_id:
|
||||||
raise ValueError("to_vessel 参数不能为空")
|
raise ValueError("to_vessel 参数不能为空")
|
||||||
if not column:
|
if not column:
|
||||||
raise ValueError("column 参数不能为空")
|
raise ValueError("column 参数不能为空")
|
||||||
|
|
||||||
if from_vessel_id not in G.nodes(): # 🔧 使用 from_vessel_id
|
if from_vessel_id not in G.nodes():
|
||||||
raise ValueError(f"源容器 '{from_vessel_id}' 不存在于系统中")
|
raise ValueError(f"源容器 '{from_vessel_id}' 不存在于系统中")
|
||||||
if to_vessel_id not in G.nodes(): # 🔧 使用 to_vessel_id
|
if to_vessel_id not in G.nodes():
|
||||||
raise ValueError(f"目标容器 '{to_vessel_id}' 不存在于系统中")
|
raise ValueError(f"目标容器 '{to_vessel_id}' 不存在于系统中")
|
||||||
|
|
||||||
debug_print("✅ 基本参数验证通过")
|
|
||||||
|
|
||||||
# === 参数解析 ===
|
# === 参数解析 ===
|
||||||
debug_print("📍 步骤2: 参数解析...")
|
|
||||||
|
|
||||||
# 解析Rf值
|
|
||||||
final_rf = parse_rf_value(rf)
|
final_rf = parse_rf_value(rf)
|
||||||
debug_print(f"🎯 最终Rf值: {final_rf}")
|
|
||||||
|
|
||||||
# 解析溶剂比例(ratio优先级高于pct1/pct2)
|
|
||||||
if ratio and ratio.strip():
|
if ratio and ratio.strip():
|
||||||
final_pct1, final_pct2 = parse_ratio(ratio)
|
final_pct1, final_pct2 = parse_ratio(ratio)
|
||||||
debug_print(f"📊 使用ratio参数: {final_pct1:.1f}% : {final_pct2:.1f}%")
|
|
||||||
else:
|
else:
|
||||||
final_pct1 = parse_percentage(pct1) if pct1 else 50.0
|
final_pct1 = parse_percentage(pct1) if pct1 else 50.0
|
||||||
final_pct2 = parse_percentage(pct2) if pct2 else 50.0
|
final_pct2 = parse_percentage(pct2) if pct2 else 50.0
|
||||||
|
|
||||||
# 如果百分比和不是100%,进行归一化
|
|
||||||
total_pct = final_pct1 + final_pct2
|
total_pct = final_pct1 + final_pct2
|
||||||
if total_pct == 0:
|
if total_pct == 0:
|
||||||
final_pct1, final_pct2 = 50.0, 50.0
|
final_pct1, final_pct2 = 50.0, 50.0
|
||||||
@@ -469,102 +232,67 @@ def generate_run_column_protocol(
|
|||||||
final_pct1 = (final_pct1 / total_pct) * 100
|
final_pct1 = (final_pct1 / total_pct) * 100
|
||||||
final_pct2 = (final_pct2 / total_pct) * 100
|
final_pct2 = (final_pct2 / total_pct) * 100
|
||||||
|
|
||||||
debug_print(f"📊 使用百分比参数: {final_pct1:.1f}% : {final_pct2:.1f}%")
|
|
||||||
|
|
||||||
# 设置默认溶剂(如果未指定)
|
|
||||||
final_solvent1 = solvent1.strip() if solvent1 else "ethyl_acetate"
|
final_solvent1 = solvent1.strip() if solvent1 else "ethyl_acetate"
|
||||||
final_solvent2 = solvent2.strip() if solvent2 else "hexane"
|
final_solvent2 = solvent2.strip() if solvent2 else "hexane"
|
||||||
|
|
||||||
debug_print(f"🧪 最终溶剂: {final_solvent1} : {final_solvent2}")
|
debug_print(f"参数: rf={final_rf}, 溶剂={final_solvent1}:{final_solvent2} = {final_pct1:.1f}%:{final_pct2:.1f}%")
|
||||||
|
|
||||||
# === 查找设备和容器 ===
|
# === 查找设备和容器 ===
|
||||||
debug_print("📍 步骤3: 查找设备和容器...")
|
|
||||||
|
|
||||||
# 查找柱层析设备
|
|
||||||
column_device_id = find_column_device(G)
|
column_device_id = find_column_device(G)
|
||||||
|
|
||||||
# 查找柱容器
|
|
||||||
column_vessel = find_column_vessel(G, column)
|
column_vessel = find_column_vessel(G, column)
|
||||||
|
|
||||||
# 查找溶剂容器
|
|
||||||
solvent1_vessel = find_solvent_vessel(G, final_solvent1)
|
solvent1_vessel = find_solvent_vessel(G, final_solvent1)
|
||||||
solvent2_vessel = find_solvent_vessel(G, final_solvent2)
|
solvent2_vessel = find_solvent_vessel(G, final_solvent2)
|
||||||
|
|
||||||
debug_print(f"🔧 设备映射:")
|
|
||||||
debug_print(f" - 柱设备: '{column_device_id}'")
|
|
||||||
debug_print(f" - 柱容器: '{column_vessel}'")
|
|
||||||
debug_print(f" - 溶剂1容器: '{solvent1_vessel}'")
|
|
||||||
debug_print(f" - 溶剂2容器: '{solvent2_vessel}'")
|
|
||||||
|
|
||||||
# === 获取源容器体积 ===
|
# === 获取源容器体积 ===
|
||||||
debug_print("📍 步骤4: 获取源容器体积...")
|
|
||||||
|
|
||||||
source_volume = original_from_volume
|
source_volume = original_from_volume
|
||||||
if source_volume <= 0:
|
if source_volume <= 0:
|
||||||
source_volume = 50.0 # 默认体积
|
source_volume = 50.0
|
||||||
debug_print(f"⚠️ 无法获取源容器体积,使用默认值: {source_volume}mL")
|
|
||||||
else:
|
|
||||||
debug_print(f"✅ 源容器体积: {source_volume}mL")
|
|
||||||
|
|
||||||
# === 计算溶剂体积 ===
|
# === 计算溶剂体积 ===
|
||||||
debug_print("📍 步骤5: 计算溶剂体积...")
|
|
||||||
|
|
||||||
# 洗脱溶剂通常是样品体积的2-5倍
|
|
||||||
total_elution_volume = source_volume * 3.0
|
total_elution_volume = source_volume * 3.0
|
||||||
solvent1_volume, solvent2_volume = calculate_solvent_volumes(
|
solvent1_volume, solvent2_volume = calculate_solvent_volumes(
|
||||||
total_elution_volume, final_pct1, final_pct2
|
total_elution_volume, final_pct1, final_pct2
|
||||||
)
|
)
|
||||||
|
|
||||||
# === 执行柱层析流程 ===
|
# === 执行柱层析流程 ===
|
||||||
debug_print("📍 步骤6: 执行柱层析流程...")
|
|
||||||
|
|
||||||
# 🔧 新增:体积变化跟踪变量
|
|
||||||
current_from_volume = source_volume
|
current_from_volume = source_volume
|
||||||
current_to_volume = original_to_volume
|
current_to_volume = original_to_volume
|
||||||
current_column_volume = 0.0
|
current_column_volume = 0.0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 步骤6.1: 样品上柱(如果有独立的柱容器)
|
# 步骤1: 样品上柱
|
||||||
if column_vessel and column_vessel != from_vessel_id: # 🔧 使用 from_vessel_id
|
if column_vessel and column_vessel != from_vessel_id:
|
||||||
debug_print(f"📍 6.1: 样品上柱 - {source_volume}mL 从 {from_vessel_id} 到 {column_vessel}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
sample_transfer_actions = generate_pump_protocol_with_rinsing(
|
sample_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=from_vessel_id, # 🔧 使用 from_vessel_id
|
from_vessel=from_vessel_id,
|
||||||
to_vessel=column_vessel,
|
to_vessel=column_vessel,
|
||||||
volume=source_volume,
|
volume=source_volume,
|
||||||
flowrate=1.0, # 慢速上柱
|
flowrate=1.0,
|
||||||
transfer_flowrate=0.5,
|
transfer_flowrate=0.5,
|
||||||
rinsing_solvent="", # 暂不冲洗
|
rinsing_solvent="",
|
||||||
rinsing_volume=0.0,
|
rinsing_volume=0.0,
|
||||||
rinsing_repeats=0
|
rinsing_repeats=0
|
||||||
)
|
)
|
||||||
action_sequence.extend(sample_transfer_actions)
|
action_sequence.extend(sample_transfer_actions)
|
||||||
debug_print(f"✅ 样品上柱完成,添加了 {len(sample_transfer_actions)} 个动作")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 样品转移到柱上
|
current_from_volume = 0.0
|
||||||
current_from_volume = 0.0 # 源容器体积变为0
|
current_column_volume = source_volume
|
||||||
current_column_volume = source_volume # 柱容器体积增加
|
|
||||||
|
|
||||||
update_vessel_volume(from_vessel, G, current_from_volume, "样品上柱后,源容器清空")
|
update_vessel_volume(from_vessel, G, current_from_volume, "样品上柱后,源容器清空")
|
||||||
|
|
||||||
# 如果柱容器在图中,也更新其体积
|
|
||||||
if column_vessel in G.nodes():
|
if column_vessel in G.nodes():
|
||||||
if 'data' not in G.nodes[column_vessel]:
|
if 'data' not in G.nodes[column_vessel]:
|
||||||
G.nodes[column_vessel]['data'] = {}
|
G.nodes[column_vessel]['data'] = {}
|
||||||
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
||||||
debug_print(f"📊 柱容器 '{column_vessel}' 体积更新为: {current_column_volume:.2f}mL")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 样品上柱失败: {str(e)}")
|
debug_print(f"样品上柱失败: {str(e)}")
|
||||||
|
|
||||||
# 步骤6.2: 添加洗脱溶剂1(如果有溶剂容器)
|
# 步骤2: 添加洗脱溶剂1
|
||||||
if solvent1_vessel and solvent1_volume > 0:
|
if solvent1_vessel and solvent1_volume > 0:
|
||||||
debug_print(f"📍 6.2: 添加洗脱溶剂1 - {solvent1_volume:.1f}mL {final_solvent1}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target_vessel = column_vessel if column_vessel else from_vessel_id # 🔧 使用 from_vessel_id
|
target_vessel = column_vessel if column_vessel else from_vessel_id
|
||||||
solvent1_transfer_actions = generate_pump_protocol_with_rinsing(
|
solvent1_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=solvent1_vessel,
|
from_vessel=solvent1_vessel,
|
||||||
@@ -574,27 +302,22 @@ def generate_run_column_protocol(
|
|||||||
transfer_flowrate=1.0
|
transfer_flowrate=1.0
|
||||||
)
|
)
|
||||||
action_sequence.extend(solvent1_transfer_actions)
|
action_sequence.extend(solvent1_transfer_actions)
|
||||||
debug_print(f"✅ 溶剂1添加完成,添加了 {len(solvent1_transfer_actions)} 个动作")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 添加溶剂1
|
|
||||||
if target_vessel == column_vessel:
|
if target_vessel == column_vessel:
|
||||||
current_column_volume += solvent1_volume
|
current_column_volume += solvent1_volume
|
||||||
if column_vessel in G.nodes():
|
if column_vessel in G.nodes():
|
||||||
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
||||||
debug_print(f"📊 柱容器体积增加: +{solvent1_volume:.2f}mL = {current_column_volume:.2f}mL")
|
|
||||||
elif target_vessel == from_vessel_id:
|
elif target_vessel == from_vessel_id:
|
||||||
current_from_volume += solvent1_volume
|
current_from_volume += solvent1_volume
|
||||||
update_vessel_volume(from_vessel, G, current_from_volume, "添加溶剂1后")
|
update_vessel_volume(from_vessel, G, current_from_volume, "添加溶剂1后")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 溶剂1添加失败: {str(e)}")
|
debug_print(f"溶剂1添加失败: {str(e)}")
|
||||||
|
|
||||||
# 步骤6.3: 添加洗脱溶剂2(如果有溶剂容器)
|
# 步骤3: 添加洗脱溶剂2
|
||||||
if solvent2_vessel and solvent2_volume > 0:
|
if solvent2_vessel and solvent2_volume > 0:
|
||||||
debug_print(f"📍 6.3: 添加洗脱溶剂2 - {solvent2_volume:.1f}mL {final_solvent2}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
target_vessel = column_vessel if column_vessel else from_vessel_id # 🔧 使用 from_vessel_id
|
target_vessel = column_vessel if column_vessel else from_vessel_id
|
||||||
solvent2_transfer_actions = generate_pump_protocol_with_rinsing(
|
solvent2_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=solvent2_vessel,
|
from_vessel=solvent2_vessel,
|
||||||
@@ -604,31 +327,26 @@ def generate_run_column_protocol(
|
|||||||
transfer_flowrate=1.0
|
transfer_flowrate=1.0
|
||||||
)
|
)
|
||||||
action_sequence.extend(solvent2_transfer_actions)
|
action_sequence.extend(solvent2_transfer_actions)
|
||||||
debug_print(f"✅ 溶剂2添加完成,添加了 {len(solvent2_transfer_actions)} 个动作")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 添加溶剂2
|
|
||||||
if target_vessel == column_vessel:
|
if target_vessel == column_vessel:
|
||||||
current_column_volume += solvent2_volume
|
current_column_volume += solvent2_volume
|
||||||
if column_vessel in G.nodes():
|
if column_vessel in G.nodes():
|
||||||
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
||||||
debug_print(f"📊 柱容器体积增加: +{solvent2_volume:.2f}mL = {current_column_volume:.2f}mL")
|
|
||||||
elif target_vessel == from_vessel_id:
|
elif target_vessel == from_vessel_id:
|
||||||
current_from_volume += solvent2_volume
|
current_from_volume += solvent2_volume
|
||||||
update_vessel_volume(from_vessel, G, current_from_volume, "添加溶剂2后")
|
update_vessel_volume(from_vessel, G, current_from_volume, "添加溶剂2后")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 溶剂2添加失败: {str(e)}")
|
debug_print(f"溶剂2添加失败: {str(e)}")
|
||||||
|
|
||||||
# 步骤6.4: 使用柱层析设备执行分离(如果有设备)
|
# 步骤4: 使用柱层析设备执行分离
|
||||||
if column_device_id:
|
if column_device_id:
|
||||||
debug_print(f"📍 6.4: 使用柱层析设备执行分离")
|
|
||||||
|
|
||||||
column_separation_action = {
|
column_separation_action = {
|
||||||
"device_id": column_device_id,
|
"device_id": column_device_id,
|
||||||
"action_name": "run_column",
|
"action_name": "run_column",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"from_vessel": from_vessel_id, # 🔧 使用 from_vessel_id
|
"from_vessel": from_vessel_id,
|
||||||
"to_vessel": to_vessel_id, # 🔧 使用 to_vessel_id
|
"to_vessel": to_vessel_id,
|
||||||
"column": column,
|
"column": column,
|
||||||
"rf": rf,
|
"rf": rf,
|
||||||
"pct1": pct1,
|
"pct1": pct1,
|
||||||
@@ -639,85 +357,65 @@ def generate_run_column_protocol(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(column_separation_action)
|
action_sequence.append(column_separation_action)
|
||||||
debug_print(f"✅ 柱层析设备动作已添加")
|
|
||||||
|
|
||||||
# 等待分离完成
|
separation_time = max(30, min(120, int(total_elution_volume / 2)))
|
||||||
separation_time = max(30, min(120, int(total_elution_volume / 2))) # 30-120秒,基于体积
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": separation_time}
|
"action_kwargs": {"time": separation_time}
|
||||||
})
|
})
|
||||||
debug_print(f"✅ 等待分离完成: {separation_time}秒")
|
|
||||||
|
|
||||||
# 步骤6.5: 产物收集(从柱容器到目标容器)
|
|
||||||
if column_vessel and column_vessel != to_vessel_id: # 🔧 使用 to_vessel_id
|
|
||||||
debug_print(f"📍 6.5: 产物收集 - 从 {column_vessel} 到 {to_vessel_id}")
|
|
||||||
|
|
||||||
|
# 步骤5: 产物收集
|
||||||
|
if column_vessel and column_vessel != to_vessel_id:
|
||||||
try:
|
try:
|
||||||
# 估算产物体积(原始样品体积的70-90%,收率考虑)
|
|
||||||
product_volume = source_volume * 0.8
|
product_volume = source_volume * 0.8
|
||||||
|
|
||||||
product_transfer_actions = generate_pump_protocol_with_rinsing(
|
product_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=column_vessel,
|
from_vessel=column_vessel,
|
||||||
to_vessel=to_vessel_id, # 🔧 使用 to_vessel_id
|
to_vessel=to_vessel_id,
|
||||||
volume=product_volume,
|
volume=product_volume,
|
||||||
flowrate=1.5,
|
flowrate=1.5,
|
||||||
transfer_flowrate=0.8
|
transfer_flowrate=0.8
|
||||||
)
|
)
|
||||||
action_sequence.extend(product_transfer_actions)
|
action_sequence.extend(product_transfer_actions)
|
||||||
debug_print(f"✅ 产物收集完成,添加了 {len(product_transfer_actions)} 个动作")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 产物收集到目标容器
|
|
||||||
current_to_volume += product_volume
|
current_to_volume += product_volume
|
||||||
current_column_volume -= product_volume # 柱容器体积减少
|
current_column_volume -= product_volume
|
||||||
|
|
||||||
update_vessel_volume(to_vessel, G, current_to_volume, "产物收集后")
|
update_vessel_volume(to_vessel, G, current_to_volume, "产物收集后")
|
||||||
|
|
||||||
# 更新柱容器体积
|
|
||||||
if column_vessel in G.nodes():
|
if column_vessel in G.nodes():
|
||||||
G.nodes[column_vessel]['data']['liquid_volume'] = max(0.0, current_column_volume)
|
G.nodes[column_vessel]['data']['liquid_volume'] = max(0.0, current_column_volume)
|
||||||
debug_print(f"📊 柱容器体积减少: -{product_volume:.2f}mL = {current_column_volume:.2f}mL")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 产物收集失败: {str(e)}")
|
debug_print(f"产物收集失败: {str(e)}")
|
||||||
|
|
||||||
# 步骤6.6: 如果没有独立的柱设备和容器,执行简化的直接转移
|
# 步骤6: 简化模式 - 直接转移
|
||||||
if not column_device_id and not column_vessel:
|
if not column_device_id and not column_vessel:
|
||||||
debug_print(f"📍 6.6: 简化模式 - 直接转移 {source_volume}mL 从 {from_vessel_id} 到 {to_vessel_id}")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
direct_transfer_actions = generate_pump_protocol_with_rinsing(
|
direct_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=from_vessel_id, # 🔧 使用 from_vessel_id
|
from_vessel=from_vessel_id,
|
||||||
to_vessel=to_vessel_id, # 🔧 使用 to_vessel_id
|
to_vessel=to_vessel_id,
|
||||||
volume=source_volume,
|
volume=source_volume,
|
||||||
flowrate=2.0,
|
flowrate=2.0,
|
||||||
transfer_flowrate=1.0
|
transfer_flowrate=1.0
|
||||||
)
|
)
|
||||||
action_sequence.extend(direct_transfer_actions)
|
action_sequence.extend(direct_transfer_actions)
|
||||||
debug_print(f"✅ 直接转移完成,添加了 {len(direct_transfer_actions)} 个动作")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 直接转移
|
current_from_volume = 0.0
|
||||||
current_from_volume = 0.0 # 源容器清空
|
current_to_volume += source_volume
|
||||||
current_to_volume += source_volume # 目标容器增加
|
|
||||||
|
|
||||||
update_vessel_volume(from_vessel, G, current_from_volume, "直接转移后,源容器清空")
|
update_vessel_volume(from_vessel, G, current_from_volume, "直接转移后,源容器清空")
|
||||||
update_vessel_volume(to_vessel, G, current_to_volume, "直接转移后,目标容器增加")
|
update_vessel_volume(to_vessel, G, current_to_volume, "直接转移后,目标容器增加")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"⚠️ 直接转移失败: {str(e)}")
|
debug_print(f"直接转移失败: {str(e)}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 协议生成失败: {str(e)} 😭")
|
debug_print(f"协议生成失败: {str(e)}")
|
||||||
|
|
||||||
# 不添加不确定的动作,直接让action_sequence保持为空列表
|
|
||||||
# action_sequence 已经在函数开始时初始化为 []
|
|
||||||
|
|
||||||
# 确保至少有一个有效的动作,如果完全失败就返回空列表
|
|
||||||
if not action_sequence:
|
if not action_sequence:
|
||||||
debug_print("⚠️ 没有生成任何有效动作")
|
|
||||||
# 可以选择返回空列表或添加一个基本的等待动作
|
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
@@ -726,46 +424,23 @@ def generate_run_column_protocol(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
# 🔧 新增:柱层析完成后的最终状态报告
|
final_from_volume = get_resource_liquid_volume(from_vessel)
|
||||||
final_from_volume = get_vessel_liquid_volume(from_vessel)
|
final_to_volume = get_resource_liquid_volume(to_vessel)
|
||||||
final_to_volume = get_vessel_liquid_volume(to_vessel)
|
|
||||||
|
|
||||||
# 🎊 总结
|
debug_print(f"柱层析协议生成完成: {len(action_sequence)} 个动作, {from_vessel_id} -> {to_vessel_id}, 收集={final_to_volume - original_to_volume:.2f}mL")
|
||||||
debug_print("🏛️" * 20)
|
|
||||||
debug_print(f"🎉 柱层析协议生成完成! ✨")
|
|
||||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
|
||||||
debug_print(f"🥽 路径: {from_vessel_id} → {to_vessel_id}")
|
|
||||||
debug_print(f"🏛️ 柱子: {column}")
|
|
||||||
debug_print(f"🧪 溶剂: {final_solvent1}:{final_solvent2} = {final_pct1:.1f}%:{final_pct2:.1f}%")
|
|
||||||
debug_print(f"📊 体积变化统计:")
|
|
||||||
debug_print(f" 源容器 {from_vessel_id}:")
|
|
||||||
debug_print(f" - 柱层析前: {original_from_volume:.2f}mL")
|
|
||||||
debug_print(f" - 柱层析后: {final_from_volume:.2f}mL")
|
|
||||||
debug_print(f" 目标容器 {to_vessel_id}:")
|
|
||||||
debug_print(f" - 柱层析前: {original_to_volume:.2f}mL")
|
|
||||||
debug_print(f" - 柱层析后: {final_to_volume:.2f}mL")
|
|
||||||
debug_print(f" - 收集体积: {final_to_volume - original_to_volume:.2f}mL")
|
|
||||||
debug_print(f"⏱️ 预计总时间: {len(action_sequence) * 5:.0f} 秒 ⌛")
|
|
||||||
debug_print("🏛️" * 20)
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
# 🔧 新增:便捷函数
|
# 便捷函数
|
||||||
def generate_ethyl_acetate_hexane_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
def generate_ethyl_acetate_hexane_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
||||||
column: str, ratio: str = "30:70") -> List[Dict[str, Any]]:
|
column: str, ratio: str = "30:70") -> List[Dict[str, Any]]:
|
||||||
"""乙酸乙酯-己烷柱层析(常用组合)"""
|
"""乙酸乙酯-己烷柱层析(常用组合)"""
|
||||||
from_vessel_id = from_vessel["id"]
|
|
||||||
to_vessel_id = to_vessel["id"]
|
|
||||||
debug_print(f"🧪⛽ 乙酸乙酯-己烷柱层析: {from_vessel_id} → {to_vessel_id} @ {ratio}")
|
|
||||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
||||||
solvent1="ethyl_acetate", solvent2="hexane", ratio=ratio)
|
solvent1="ethyl_acetate", solvent2="hexane", ratio=ratio)
|
||||||
|
|
||||||
def generate_methanol_dcm_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
def generate_methanol_dcm_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
||||||
column: str, ratio: str = "5:95") -> List[Dict[str, Any]]:
|
column: str, ratio: str = "5:95") -> List[Dict[str, Any]]:
|
||||||
"""甲醇-二氯甲烷柱层析"""
|
"""甲醇-二氯甲烷柱层析"""
|
||||||
from_vessel_id = from_vessel["id"]
|
|
||||||
to_vessel_id = to_vessel["id"]
|
|
||||||
debug_print(f"🧪🧪 甲醇-DCM柱层析: {from_vessel_id} → {to_vessel_id} @ {ratio}")
|
|
||||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
||||||
solvent1="methanol", solvent2="dichloromethane", ratio=ratio)
|
solvent1="methanol", solvent2="dichloromethane", ratio=ratio)
|
||||||
|
|
||||||
@@ -773,35 +448,25 @@ def generate_gradient_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vesse
|
|||||||
column: str, start_ratio: str = "10:90",
|
column: str, start_ratio: str = "10:90",
|
||||||
end_ratio: str = "50:50") -> List[Dict[str, Any]]:
|
end_ratio: str = "50:50") -> List[Dict[str, Any]]:
|
||||||
"""梯度洗脱柱层析(中等比例)"""
|
"""梯度洗脱柱层析(中等比例)"""
|
||||||
from_vessel_id, _ = get_vessel(from_vessel)
|
|
||||||
to_vessel_id, _ = get_vessel(to_vessel)
|
|
||||||
debug_print(f"📈 梯度柱层析: {from_vessel_id} → {to_vessel_id} ({start_ratio} → {end_ratio})")
|
|
||||||
# 使用中间比例作为近似
|
|
||||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column, ratio="30:70")
|
return generate_run_column_protocol(G, from_vessel, to_vessel, column, ratio="30:70")
|
||||||
|
|
||||||
def generate_polar_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
def generate_polar_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
||||||
column: str) -> List[Dict[str, Any]]:
|
column: str) -> List[Dict[str, Any]]:
|
||||||
"""极性化合物柱层析(高极性溶剂比例)"""
|
"""极性化合物柱层析(高极性溶剂比例)"""
|
||||||
from_vessel_id, _ = get_vessel(from_vessel)
|
|
||||||
to_vessel_id, _ = get_vessel(to_vessel)
|
|
||||||
debug_print(f"⚡ 极性化合物柱层析: {from_vessel_id} → {to_vessel_id}")
|
|
||||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
||||||
solvent1="ethyl_acetate", solvent2="hexane", ratio="70:30")
|
solvent1="ethyl_acetate", solvent2="hexane", ratio="70:30")
|
||||||
|
|
||||||
def generate_nonpolar_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
def generate_nonpolar_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
||||||
column: str) -> List[Dict[str, Any]]:
|
column: str) -> List[Dict[str, Any]]:
|
||||||
"""非极性化合物柱层析(低极性溶剂比例)"""
|
"""非极性化合物柱层析(低极性溶剂比例)"""
|
||||||
from_vessel_id, _ = get_vessel(from_vessel)
|
|
||||||
to_vessel_id, _ = get_vessel(to_vessel)
|
|
||||||
debug_print(f"🛢️ 非极性化合物柱层析: {from_vessel_id} → {to_vessel_id}")
|
|
||||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
||||||
solvent1="ethyl_acetate", solvent2="hexane", ratio="5:95")
|
solvent1="ethyl_acetate", solvent2="hexane", ratio="5:95")
|
||||||
|
|
||||||
# 测试函数
|
# 测试函数
|
||||||
def test_run_column_protocol():
|
def test_run_column_protocol():
|
||||||
"""测试柱层析协议"""
|
"""测试柱层析协议"""
|
||||||
debug_print("🧪 === RUN COLUMN PROTOCOL 测试 === ✨")
|
debug_print("=== RUN COLUMN PROTOCOL 测试 ===")
|
||||||
debug_print("✅ 测试完成 🎉")
|
debug_print("测试完成")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_run_column_protocol()
|
test_run_column_protocol()
|
||||||
|
|||||||
@@ -1,48 +1,14 @@
|
|||||||
from typing import List, Dict, Any, Union
|
from typing import List, Dict, Any, Union
|
||||||
import networkx as nx
|
import networkx as nx
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
|
|
||||||
from .utils.unit_parser import parse_time_input
|
from .utils.unit_parser import parse_time_input
|
||||||
|
from .utils.resource_helper import get_resource_id, get_resource_display_info
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
|
from .utils.vessel_parser import find_connected_stirrer
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[STIR] {message}")
|
|
||||||
|
|
||||||
|
|
||||||
def find_connected_stirrer(G: nx.DiGraph, vessel: str = None) -> str:
|
|
||||||
"""查找与指定容器相连的搅拌设备"""
|
|
||||||
debug_print(f"🔍 查找搅拌设备,目标容器: {vessel} 🥽")
|
|
||||||
|
|
||||||
# 🔧 查找所有搅拌设备
|
|
||||||
stirrer_nodes = []
|
|
||||||
for node in G.nodes():
|
|
||||||
node_data = G.nodes[node]
|
|
||||||
node_class = node_data.get('class', '') or ''
|
|
||||||
|
|
||||||
if 'stirrer' in node_class.lower() or 'virtual_stirrer' in node_class:
|
|
||||||
stirrer_nodes.append(node)
|
|
||||||
debug_print(f"🎉 找到搅拌设备: {node} 🌪️")
|
|
||||||
|
|
||||||
# 🔗 检查连接
|
|
||||||
if vessel and stirrer_nodes:
|
|
||||||
for stirrer in stirrer_nodes:
|
|
||||||
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
|
|
||||||
debug_print(f"✅ 搅拌设备 '{stirrer}' 与容器 '{vessel}' 相连 🔗")
|
|
||||||
return stirrer
|
|
||||||
|
|
||||||
# 🎯 使用第一个可用设备
|
|
||||||
if stirrer_nodes:
|
|
||||||
selected = stirrer_nodes[0]
|
|
||||||
debug_print(f"🔧 使用第一个搅拌设备: {selected} 🌪️")
|
|
||||||
return selected
|
|
||||||
|
|
||||||
# 🆘 默认设备
|
|
||||||
debug_print("⚠️ 未找到搅拌设备,使用默认设备 🌪️")
|
|
||||||
return "stirrer_1"
|
|
||||||
|
|
||||||
def validate_and_fix_params(stir_time: float, stir_speed: float, settling_time: float) -> tuple:
|
def validate_and_fix_params(stir_time: float, stir_speed: float, settling_time: float) -> tuple:
|
||||||
"""验证和修正参数"""
|
"""验证和修正参数"""
|
||||||
# ⏰ 搅拌时间验证
|
# ⏰ 搅拌时间验证
|
||||||
@@ -71,46 +37,13 @@ def validate_and_fix_params(stir_time: float, stir_speed: float, settling_time:
|
|||||||
|
|
||||||
return stir_time, stir_speed, settling_time
|
return stir_time, stir_speed, settling_time
|
||||||
|
|
||||||
def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
def extract_vessel_id(vessel) -> str:
|
||||||
"""
|
"""从vessel参数中提取vessel_id,兼容 str / dict / ResourceDictInstance"""
|
||||||
从vessel参数中提取vessel_id
|
return get_resource_id(vessel)
|
||||||
|
|
||||||
Args:
|
def get_vessel_display_info(vessel) -> str:
|
||||||
vessel: vessel字典或vessel_id字符串
|
"""获取容器的显示信息(用于日志),兼容 str / dict / ResourceDictInstance"""
|
||||||
|
return get_resource_display_info(vessel)
|
||||||
Returns:
|
|
||||||
str: vessel_id
|
|
||||||
"""
|
|
||||||
if isinstance(vessel, dict):
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
|
||||||
return vessel_id
|
|
||||||
elif isinstance(vessel, str):
|
|
||||||
debug_print(f"🔧 vessel参数为字符串: {vessel}")
|
|
||||||
return vessel
|
|
||||||
else:
|
|
||||||
debug_print(f"⚠️ 无效的vessel参数类型: {type(vessel)}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_vessel_display_info(vessel: Union[str, dict]) -> str:
|
|
||||||
"""
|
|
||||||
获取容器的显示信息(用于日志)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vessel: vessel字典或vessel_id字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 显示信息
|
|
||||||
"""
|
|
||||||
if isinstance(vessel, dict):
|
|
||||||
vessel_id = vessel.get("id", "unknown")
|
|
||||||
vessel_name = vessel.get("name", "")
|
|
||||||
if vessel_name:
|
|
||||||
return f"{vessel_id} ({vessel_name})"
|
|
||||||
else:
|
|
||||||
return vessel_id
|
|
||||||
else:
|
|
||||||
return str(vessel)
|
|
||||||
|
|
||||||
def generate_stir_protocol(
|
def generate_stir_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
@@ -152,48 +85,28 @@ def generate_stir_protocol(
|
|||||||
}
|
}
|
||||||
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||||
|
|
||||||
debug_print("🌪️" * 20)
|
debug_print(f"开始生成搅拌协议: vessel={vessel_id}, time={time}, "
|
||||||
debug_print("🚀 开始生成搅拌协议(支持vessel字典)✨")
|
f"stir_time={stir_time}, stir_speed={stir_speed}RPM, settling={settling_time}")
|
||||||
debug_print(f"📝 输入参数:")
|
|
||||||
debug_print(f" 🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
|
||||||
debug_print(f" ⏰ time: {time}")
|
|
||||||
debug_print(f" 🕐 stir_time: {stir_time}")
|
|
||||||
debug_print(f" 🎯 time_spec: {time_spec}")
|
|
||||||
debug_print(f" 🌪️ stir_speed: {stir_speed} RPM")
|
|
||||||
debug_print(f" ⏱️ settling_time: {settling_time}")
|
|
||||||
debug_print("🌪️" * 20)
|
|
||||||
|
|
||||||
# 📋 参数验证
|
# 参数验证
|
||||||
debug_print("📍 步骤1: 参数验证... 🔧")
|
if not vessel_id:
|
||||||
if not vessel_id: # 🔧 使用 vessel_id
|
|
||||||
debug_print("❌ vessel 参数不能为空! 😱")
|
|
||||||
raise ValueError("vessel 参数不能为空")
|
raise ValueError("vessel 参数不能为空")
|
||||||
|
|
||||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
if vessel_id not in G.nodes():
|
||||||
debug_print(f"❌ 容器 '{vessel_id}' 不存在于系统中! 😞")
|
|
||||||
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
||||||
|
|
||||||
debug_print("✅ 基础参数验证通过 🎯")
|
# 参数解析 — 确定实际时间(优先级:time_spec > stir_time > time)
|
||||||
|
|
||||||
# 🔄 参数解析
|
|
||||||
debug_print("📍 步骤2: 参数解析... ⚡")
|
|
||||||
|
|
||||||
# 确定实际时间(优先级:time_spec > stir_time > time)
|
|
||||||
if time_spec:
|
if time_spec:
|
||||||
parsed_time = parse_time_input(time_spec)
|
parsed_time = parse_time_input(time_spec)
|
||||||
debug_print(f"🎯 使用time_spec: '{time_spec}' → {parsed_time}s")
|
|
||||||
elif stir_time not in ["0", 0, 0.0]:
|
elif stir_time not in ["0", 0, 0.0]:
|
||||||
parsed_time = parse_time_input(stir_time)
|
parsed_time = parse_time_input(stir_time)
|
||||||
debug_print(f"🎯 使用stir_time: {stir_time} → {parsed_time}s")
|
|
||||||
else:
|
else:
|
||||||
parsed_time = parse_time_input(time)
|
parsed_time = parse_time_input(time)
|
||||||
debug_print(f"🎯 使用time: {time} → {parsed_time}s")
|
|
||||||
|
|
||||||
# 解析沉降时间
|
# 解析沉降时间
|
||||||
parsed_settling_time = parse_time_input(settling_time)
|
parsed_settling_time = parse_time_input(settling_time)
|
||||||
|
|
||||||
# 🕐 模拟运行时间优化
|
# 模拟运行时间优化
|
||||||
debug_print(" ⏱️ 检查模拟运行时间限制...")
|
|
||||||
original_stir_time = parsed_time
|
original_stir_time = parsed_time
|
||||||
original_settling_time = parsed_settling_time
|
original_settling_time = parsed_settling_time
|
||||||
|
|
||||||
@@ -201,32 +114,26 @@ def generate_stir_protocol(
|
|||||||
stir_time_limit = 60.0
|
stir_time_limit = 60.0
|
||||||
if parsed_time > stir_time_limit:
|
if parsed_time > stir_time_limit:
|
||||||
parsed_time = stir_time_limit
|
parsed_time = stir_time_limit
|
||||||
debug_print(f" 🎮 搅拌时间优化: {original_stir_time}s → {parsed_time}s ⚡")
|
|
||||||
|
|
||||||
# 沉降时间限制为30秒
|
# 沉降时间限制为30秒
|
||||||
settling_time_limit = 30.0
|
settling_time_limit = 30.0
|
||||||
if parsed_settling_time > settling_time_limit:
|
if parsed_settling_time > settling_time_limit:
|
||||||
parsed_settling_time = settling_time_limit
|
parsed_settling_time = settling_time_limit
|
||||||
debug_print(f" 🎮 沉降时间优化: {original_settling_time}s → {parsed_settling_time}s ⚡")
|
|
||||||
|
|
||||||
# 参数修正
|
# 参数修正
|
||||||
parsed_time, stir_speed, parsed_settling_time = validate_and_fix_params(
|
parsed_time, stir_speed, parsed_settling_time = validate_and_fix_params(
|
||||||
parsed_time, stir_speed, parsed_settling_time
|
parsed_time, stir_speed, parsed_settling_time
|
||||||
)
|
)
|
||||||
|
|
||||||
debug_print(f"🎯 最终参数: time={parsed_time}s, speed={stir_speed}RPM, settling={parsed_settling_time}s")
|
debug_print(f"最终参数: time={parsed_time}s, speed={stir_speed}RPM, settling={parsed_settling_time}s")
|
||||||
|
|
||||||
# 🔍 查找设备
|
# 查找设备
|
||||||
debug_print("📍 步骤3: 查找搅拌设备... 🔍")
|
|
||||||
try:
|
try:
|
||||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||||
debug_print(f"🎉 使用搅拌设备: {stirrer_id} ✨")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
raise ValueError(f"无法找到搅拌设备: {str(e)}")
|
||||||
|
|
||||||
# 🚀 生成动作
|
# 生成动作
|
||||||
debug_print("📍 步骤4: 生成搅拌动作... 🌪️")
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
stir_action = {
|
stir_action = {
|
||||||
@@ -244,22 +151,14 @@ def generate_stir_protocol(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(stir_action)
|
action_sequence.append(stir_action)
|
||||||
debug_print("✅ 搅拌动作已添加 🌪️✨")
|
|
||||||
|
|
||||||
# 显示时间优化信息
|
# 时间优化信息
|
||||||
if original_stir_time != parsed_time or original_settling_time != parsed_settling_time:
|
if original_stir_time != parsed_time or original_settling_time != parsed_settling_time:
|
||||||
debug_print(f" 🎭 模拟优化说明:")
|
debug_print(f"模拟优化: 搅拌 {original_stir_time/60:.1f}min→{parsed_time/60:.1f}min, "
|
||||||
debug_print(f" 搅拌时间: {original_stir_time/60:.1f}分钟 → {parsed_time/60:.1f}分钟")
|
f"沉降 {original_settling_time/60:.1f}min→{parsed_settling_time/60:.1f}min")
|
||||||
debug_print(f" 沉降时间: {original_settling_time/60:.1f}分钟 → {parsed_settling_time/60:.1f}分钟")
|
|
||||||
|
|
||||||
# 🎊 总结
|
debug_print(f"搅拌协议生成完成: {vessel_display}, {stir_speed}RPM, "
|
||||||
debug_print("🎊" * 20)
|
f"{parsed_time}s, 沉降{parsed_settling_time}s, 总{(parsed_time + parsed_settling_time)/60:.1f}min")
|
||||||
debug_print(f"🎉 搅拌协议生成完成! ✨")
|
|
||||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
|
||||||
debug_print(f"🥽 搅拌容器: {vessel_display}")
|
|
||||||
debug_print(f"🌪️ 搅拌参数: {stir_speed} RPM, {parsed_time}s, 沉降 {parsed_settling_time}s")
|
|
||||||
debug_print(f"⏱️ 预计总时间: {(parsed_time + parsed_settling_time)/60:.1f} 分钟 ⌛")
|
|
||||||
debug_print("🎊" * 20)
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
@@ -297,21 +196,16 @@ def generate_start_stir_protocol(
|
|||||||
"sample_id": "",
|
"sample_id": "",
|
||||||
"type": ""
|
"type": ""
|
||||||
}
|
}
|
||||||
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
debug_print(f"构建了基本的vessel Resource对象: {vessel}")
|
||||||
|
|
||||||
debug_print("🔄 开始生成启动搅拌协议(修复vessel参数)✨")
|
debug_print(f"启动搅拌协议: vessel={vessel_id}, speed={stir_speed}RPM, purpose='{purpose}'")
|
||||||
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
|
||||||
debug_print(f"🌪️ speed: {stir_speed} RPM")
|
|
||||||
debug_print(f"🎯 purpose: {purpose}")
|
|
||||||
|
|
||||||
# 基础验证
|
# 基础验证
|
||||||
if not vessel_id or vessel_id not in G.nodes():
|
if not vessel_id or vessel_id not in G.nodes():
|
||||||
debug_print("❌ 容器验证失败!")
|
|
||||||
raise ValueError("vessel 参数无效")
|
raise ValueError("vessel 参数无效")
|
||||||
|
|
||||||
# 参数修正
|
# 参数修正
|
||||||
if stir_speed < 10.0 or stir_speed > 1500.0:
|
if stir_speed < 10.0 or stir_speed > 1500.0:
|
||||||
debug_print(f"⚠️ 搅拌速度修正: {stir_speed} → 300 RPM 🌪️")
|
|
||||||
stir_speed = 300.0
|
stir_speed = 300.0
|
||||||
|
|
||||||
# 查找设备
|
# 查找设备
|
||||||
@@ -329,7 +223,7 @@ def generate_start_stir_protocol(
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
debug_print(f"✅ 启动搅拌协议生成完成 🎯")
|
debug_print(f"启动搅拌协议生成完成: {stirrer_id}")
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
def generate_stop_stir_protocol(
|
def generate_stop_stir_protocol(
|
||||||
@@ -364,14 +258,12 @@ def generate_stop_stir_protocol(
|
|||||||
"sample_id": "",
|
"sample_id": "",
|
||||||
"type": ""
|
"type": ""
|
||||||
}
|
}
|
||||||
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
debug_print(f"构建了基本的vessel Resource对象: {vessel}")
|
||||||
|
|
||||||
debug_print("🛑 开始生成停止搅拌协议(修复vessel参数)✨")
|
debug_print(f"停止搅拌协议: vessel={vessel_id}")
|
||||||
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
|
||||||
|
|
||||||
# 基础验证
|
# 基础验证
|
||||||
if not vessel_id or vessel_id not in G.nodes():
|
if not vessel_id or vessel_id not in G.nodes():
|
||||||
debug_print("❌ 容器验证失败!")
|
|
||||||
raise ValueError("vessel 参数无效")
|
raise ValueError("vessel 参数无效")
|
||||||
|
|
||||||
# 查找设备
|
# 查找设备
|
||||||
@@ -387,10 +279,10 @@ def generate_stop_stir_protocol(
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
|
|
||||||
debug_print(f"✅ 停止搅拌协议生成完成 🎯")
|
debug_print(f"停止搅拌协议生成完成: {stirrer_id}")
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
# 🔧 新增:便捷函数
|
# 便捷函数
|
||||||
def stir_briefly(G: nx.DiGraph, vessel: Union[str, dict],
|
def stir_briefly(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
speed: float = 300.0) -> List[Dict[str, Any]]:
|
speed: float = 300.0) -> List[Dict[str, Any]]:
|
||||||
"""短时间搅拌(30秒)"""
|
"""短时间搅拌(30秒)"""
|
||||||
|
|||||||
@@ -4,19 +4,14 @@ import logging
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from .utils.unit_parser import parse_time_input, parse_volume_input
|
from .utils.unit_parser import parse_time_input, parse_volume_input
|
||||||
|
from .utils.resource_helper import get_resource_id, get_resource_display_info, get_resource_liquid_volume, update_vessel_volume
|
||||||
|
from .utils.logger_util import debug_print
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def debug_print(message):
|
|
||||||
"""调试输出"""
|
|
||||||
logger.info(f"[WASH_SOLID] {message}")
|
|
||||||
|
|
||||||
|
|
||||||
def find_solvent_source(G: nx.DiGraph, solvent: str) -> str:
|
def find_solvent_source(G: nx.DiGraph, solvent: str) -> str:
|
||||||
"""查找溶剂源(精简版)"""
|
"""查找溶剂源"""
|
||||||
debug_print(f"🔍 查找溶剂源: {solvent}")
|
|
||||||
|
|
||||||
# 简化搜索列表
|
|
||||||
search_patterns = [
|
search_patterns = [
|
||||||
f"flask_{solvent}", f"bottle_{solvent}", f"reagent_{solvent}",
|
f"flask_{solvent}", f"bottle_{solvent}", f"reagent_{solvent}",
|
||||||
"liquid_reagent_bottle_1", "flask_1", "solvent_bottle"
|
"liquid_reagent_bottle_1", "flask_1", "solvent_bottle"
|
||||||
@@ -24,179 +19,40 @@ def find_solvent_source(G: nx.DiGraph, solvent: str) -> str:
|
|||||||
|
|
||||||
for pattern in search_patterns:
|
for pattern in search_patterns:
|
||||||
if pattern in G.nodes():
|
if pattern in G.nodes():
|
||||||
debug_print(f"🎉 找到溶剂源: {pattern}")
|
debug_print(f"找到溶剂源: {pattern}")
|
||||||
return pattern
|
return pattern
|
||||||
|
|
||||||
debug_print(f"⚠️ 使用默认溶剂源: flask_{solvent}")
|
debug_print(f"使用默认溶剂源: flask_{solvent}")
|
||||||
return f"flask_{solvent}"
|
return f"flask_{solvent}"
|
||||||
|
|
||||||
def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
||||||
"""查找滤液容器(精简版)"""
|
"""查找滤液容器"""
|
||||||
debug_print(f"🔍 查找滤液容器: {filtrate_vessel}")
|
|
||||||
|
|
||||||
# 如果指定了且存在,直接使用
|
|
||||||
if filtrate_vessel and filtrate_vessel in G.nodes():
|
if filtrate_vessel and filtrate_vessel in G.nodes():
|
||||||
debug_print(f"✅ 使用指定容器: {filtrate_vessel}")
|
|
||||||
return filtrate_vessel
|
return filtrate_vessel
|
||||||
|
|
||||||
# 简化搜索列表
|
|
||||||
default_vessels = ["waste_workup", "filtrate_vessel", "flask_1", "collection_bottle_1"]
|
default_vessels = ["waste_workup", "filtrate_vessel", "flask_1", "collection_bottle_1"]
|
||||||
|
|
||||||
for vessel in default_vessels:
|
for vessel in default_vessels:
|
||||||
if vessel in G.nodes():
|
if vessel in G.nodes():
|
||||||
debug_print(f"🎉 找到滤液容器: {vessel}")
|
debug_print(f"找到滤液容器: {vessel}")
|
||||||
return vessel
|
return vessel
|
||||||
|
|
||||||
debug_print(f"⚠️ 使用默认滤液容器: waste_workup")
|
|
||||||
return "waste_workup"
|
return "waste_workup"
|
||||||
|
|
||||||
def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
def extract_vessel_id(vessel) -> str:
|
||||||
"""
|
"""从vessel参数中提取vessel_id,兼容 str / dict / ResourceDictInstance"""
|
||||||
从vessel参数中提取vessel_id
|
return get_resource_id(vessel)
|
||||||
|
|
||||||
Args:
|
def get_vessel_display_info(vessel) -> str:
|
||||||
vessel: vessel字典或vessel_id字符串
|
"""获取容器的显示信息(用于日志),兼容 str / dict / ResourceDictInstance"""
|
||||||
|
return get_resource_display_info(vessel)
|
||||||
Returns:
|
|
||||||
str: vessel_id
|
|
||||||
"""
|
|
||||||
if isinstance(vessel, dict):
|
|
||||||
vessel_id = list(vessel.values())[0].get("id", "")
|
|
||||||
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
|
||||||
return vessel_id
|
|
||||||
elif isinstance(vessel, str):
|
|
||||||
debug_print(f"🔧 vessel参数为字符串: {vessel}")
|
|
||||||
return vessel
|
|
||||||
else:
|
|
||||||
debug_print(f"⚠️ 无效的vessel参数类型: {type(vessel)}")
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_vessel_display_info(vessel: Union[str, dict]) -> str:
|
|
||||||
"""
|
|
||||||
获取容器的显示信息(用于日志)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vessel: vessel字典或vessel_id字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 显示信息
|
|
||||||
"""
|
|
||||||
if isinstance(vessel, dict):
|
|
||||||
vessel_id = vessel.get("id", "unknown")
|
|
||||||
vessel_name = vessel.get("name", "")
|
|
||||||
if vessel_name:
|
|
||||||
return f"{vessel_id} ({vessel_name})"
|
|
||||||
else:
|
|
||||||
return vessel_id
|
|
||||||
else:
|
|
||||||
return str(vessel)
|
|
||||||
|
|
||||||
def get_vessel_liquid_volume(vessel: dict) -> float:
|
|
||||||
"""
|
|
||||||
获取容器中的液体体积 - 支持vessel字典
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vessel: 容器字典
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
float: 液体体积(mL)
|
|
||||||
"""
|
|
||||||
if not vessel or "data" not in vessel:
|
|
||||||
debug_print(f"⚠️ 容器数据为空,返回 0.0mL")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
vessel_data = vessel["data"]
|
|
||||||
vessel_id = vessel.get("id", "unknown")
|
|
||||||
|
|
||||||
debug_print(f"🔍 读取容器 '{vessel_id}' 体积数据: {vessel_data}")
|
|
||||||
|
|
||||||
# 检查liquid_volume字段
|
|
||||||
if "liquid_volume" in vessel_data:
|
|
||||||
liquid_volume = vessel_data["liquid_volume"]
|
|
||||||
|
|
||||||
# 处理列表格式
|
|
||||||
if isinstance(liquid_volume, list):
|
|
||||||
if len(liquid_volume) > 0:
|
|
||||||
volume = liquid_volume[0]
|
|
||||||
if isinstance(volume, (int, float)):
|
|
||||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (列表格式)")
|
|
||||||
return float(volume)
|
|
||||||
|
|
||||||
# 处理直接数值格式
|
|
||||||
elif isinstance(liquid_volume, (int, float)):
|
|
||||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {liquid_volume}mL (数值格式)")
|
|
||||||
return float(liquid_volume)
|
|
||||||
|
|
||||||
# 检查其他可能的体积字段
|
|
||||||
volume_keys = ['current_volume', 'total_volume', 'volume']
|
|
||||||
for key in volume_keys:
|
|
||||||
if key in vessel_data:
|
|
||||||
try:
|
|
||||||
volume = float(vessel_data[key])
|
|
||||||
if volume > 0:
|
|
||||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (字段: {key})")
|
|
||||||
return volume
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
continue
|
|
||||||
|
|
||||||
debug_print(f"⚠️ 无法获取容器 '{vessel_id}' 的体积,返回默认值 0.0mL")
|
|
||||||
return 0.0
|
|
||||||
|
|
||||||
def update_vessel_volume(vessel: dict, G: nx.DiGraph, new_volume: float, description: str = "") -> None:
|
|
||||||
"""
|
|
||||||
更新容器体积(同时更新vessel字典和图节点)
|
|
||||||
|
|
||||||
Args:
|
|
||||||
vessel: 容器字典
|
|
||||||
G: 网络图
|
|
||||||
new_volume: 新体积
|
|
||||||
description: 更新描述
|
|
||||||
"""
|
|
||||||
vessel_id = vessel.get("id", "unknown")
|
|
||||||
|
|
||||||
if description:
|
|
||||||
debug_print(f"🔧 更新容器体积 - {description}")
|
|
||||||
|
|
||||||
# 更新vessel字典中的体积
|
|
||||||
if "data" in vessel:
|
|
||||||
if "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
|
|
||||||
else:
|
|
||||||
vessel["data"]["liquid_volume"] = [new_volume]
|
|
||||||
else:
|
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
|
||||||
else:
|
|
||||||
vessel["data"]["liquid_volume"] = new_volume
|
|
||||||
else:
|
|
||||||
vessel["data"] = {"liquid_volume": new_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] = new_volume
|
|
||||||
else:
|
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = [new_volume]
|
|
||||||
else:
|
|
||||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
|
||||||
|
|
||||||
debug_print(f"📊 容器 '{vessel_id}' 体积已更新为: {new_volume:.2f}mL")
|
|
||||||
|
|
||||||
def generate_wash_solid_protocol(
|
def generate_wash_solid_protocol(
|
||||||
G: nx.DiGraph,
|
G: nx.DiGraph,
|
||||||
vessel: Union[str, dict], # 🔧 修改:支持vessel字典
|
vessel: Union[str, dict],
|
||||||
solvent: str,
|
solvent: str,
|
||||||
volume: Union[float, str] = "50",
|
volume: Union[float, str] = "50",
|
||||||
filtrate_vessel: Union[str, dict] = "", # 🔧 修改:支持vessel字典
|
filtrate_vessel: Union[str, dict] = "",
|
||||||
temp: float = 25.0,
|
temp: float = 25.0,
|
||||||
stir: bool = False,
|
stir: bool = False,
|
||||||
stir_speed: float = 0.0,
|
stir_speed: float = 0.0,
|
||||||
@@ -232,101 +88,73 @@ def generate_wash_solid_protocol(
|
|||||||
List[Dict[str, Any]]: 固体清洗操作的动作序列
|
List[Dict[str, Any]]: 固体清洗操作的动作序列
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 🔧 核心修改:从vessel参数中提取vessel_id
|
|
||||||
vessel_id = extract_vessel_id(vessel)
|
vessel_id = extract_vessel_id(vessel)
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
vessel_display = get_vessel_display_info(vessel)
|
||||||
|
|
||||||
# 🔧 处理filtrate_vessel参数
|
|
||||||
filtrate_vessel_id = extract_vessel_id(filtrate_vessel) if filtrate_vessel else ""
|
filtrate_vessel_id = extract_vessel_id(filtrate_vessel) if filtrate_vessel else ""
|
||||||
|
|
||||||
debug_print("🧼" * 20)
|
debug_print(f"开始生成固体清洗协议: vessel={vessel_id}, solvent={solvent}, volume={volume}, repeats={repeats}")
|
||||||
debug_print("🚀 开始生成固体清洗协议(支持vessel字典和体积运算)✨")
|
|
||||||
debug_print(f"📝 输入参数:")
|
|
||||||
debug_print(f" 🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
|
||||||
debug_print(f" 🧪 solvent: {solvent}")
|
|
||||||
debug_print(f" 💧 volume: {volume}")
|
|
||||||
debug_print(f" 🗑️ filtrate_vessel: {filtrate_vessel_id}")
|
|
||||||
debug_print(f" ⏰ time: {time}")
|
|
||||||
debug_print(f" 🔄 repeats: {repeats}")
|
|
||||||
debug_print("🧼" * 20)
|
|
||||||
|
|
||||||
# 🔧 新增:记录清洗前的容器状态
|
# 记录清洗前的容器状态
|
||||||
debug_print("🔍 记录清洗前容器状态...")
|
|
||||||
if isinstance(vessel, dict):
|
if isinstance(vessel, dict):
|
||||||
original_volume = get_vessel_liquid_volume(vessel)
|
original_volume = get_resource_liquid_volume(vessel)
|
||||||
debug_print(f"📊 清洗前液体体积: {original_volume:.2f}mL")
|
|
||||||
else:
|
else:
|
||||||
original_volume = 0.0
|
original_volume = 0.0
|
||||||
debug_print(f"📊 vessel为字符串格式,无法获取体积信息")
|
|
||||||
|
|
||||||
# 📋 快速验证
|
# 快速验证
|
||||||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
if not vessel_id or vessel_id not in G.nodes():
|
||||||
debug_print("❌ 容器验证失败! 😱")
|
|
||||||
raise ValueError("vessel 参数无效")
|
raise ValueError("vessel 参数无效")
|
||||||
|
|
||||||
if not solvent:
|
if not solvent:
|
||||||
debug_print("❌ 溶剂不能为空! 😱")
|
|
||||||
raise ValueError("solvent 参数不能为空")
|
raise ValueError("solvent 参数不能为空")
|
||||||
|
|
||||||
debug_print("✅ 基础验证通过 🎯")
|
# 参数解析
|
||||||
|
|
||||||
# 🔄 参数解析
|
|
||||||
debug_print("📍 步骤1: 参数解析... ⚡")
|
|
||||||
final_volume = parse_volume_input(volume, volume_spec, mass)
|
final_volume = parse_volume_input(volume, volume_spec, mass)
|
||||||
final_time = parse_time_input(time)
|
final_time = parse_time_input(time)
|
||||||
|
|
||||||
# 重复次数处理(简化)
|
# 重复次数处理
|
||||||
if repeats_spec:
|
if repeats_spec:
|
||||||
spec_map = {'few': 2, 'several': 3, 'many': 4, 'thorough': 5}
|
spec_map = {'few': 2, 'several': 3, 'many': 4, 'thorough': 5}
|
||||||
final_repeats = next((v for k, v in spec_map.items() if k in repeats_spec.lower()), repeats)
|
final_repeats = next((v for k, v in spec_map.items() if k in repeats_spec.lower()), repeats)
|
||||||
else:
|
else:
|
||||||
final_repeats = max(1, min(repeats, 5)) # 限制1-5次
|
final_repeats = max(1, min(repeats, 5))
|
||||||
|
|
||||||
# 🕐 模拟时间优化
|
# 模拟时间优化
|
||||||
debug_print(" ⏱️ 模拟时间优化...")
|
|
||||||
original_time = final_time
|
original_time = final_time
|
||||||
if final_time > 60.0:
|
if final_time > 60.0:
|
||||||
final_time = 60.0 # 限制最长60秒
|
final_time = 60.0
|
||||||
debug_print(f" 🎮 时间优化: {original_time}s → {final_time}s ⚡")
|
debug_print(f"时间优化: {original_time}s -> {final_time}s")
|
||||||
|
|
||||||
# 参数修正
|
# 参数修正
|
||||||
temp = max(25.0, min(temp, 80.0)) # 温度范围25-80°C
|
temp = max(25.0, min(temp, 80.0))
|
||||||
stir_speed = max(0.0, min(stir_speed, 300.0)) if stir else 0.0 # 速度范围0-300
|
stir_speed = max(0.0, min(stir_speed, 300.0)) if stir else 0.0
|
||||||
|
|
||||||
debug_print(f"🎯 最终参数: 体积={final_volume}mL, 时间={final_time}s, 重复={final_repeats}次")
|
debug_print(f"最终参数: 体积={final_volume}mL, 时间={final_time}s, 重复={final_repeats}次")
|
||||||
|
|
||||||
# 🔍 查找设备
|
# 查找设备
|
||||||
debug_print("📍 步骤2: 查找设备... 🔍")
|
|
||||||
try:
|
try:
|
||||||
solvent_source = find_solvent_source(G, solvent)
|
solvent_source = find_solvent_source(G, solvent)
|
||||||
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel_id)
|
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel_id)
|
||||||
debug_print(f"🎉 设备配置完成 ✨")
|
|
||||||
debug_print(f" 🧪 溶剂源: {solvent_source}")
|
|
||||||
debug_print(f" 🗑️ 滤液容器: {actual_filtrate_vessel}")
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
|
|
||||||
raise ValueError(f"设备查找失败: {str(e)}")
|
raise ValueError(f"设备查找失败: {str(e)}")
|
||||||
|
|
||||||
# 🚀 生成动作序列
|
# 生成动作序列
|
||||||
debug_print("📍 步骤3: 生成清洗动作... 🧼")
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
|
|
||||||
# 🔧 新增:体积变化跟踪变量
|
|
||||||
current_volume = original_volume
|
current_volume = original_volume
|
||||||
total_solvent_used = 0.0
|
total_solvent_used = 0.0
|
||||||
|
|
||||||
for cycle in range(final_repeats):
|
for cycle in range(final_repeats):
|
||||||
debug_print(f" 🔄 第{cycle+1}/{final_repeats}次清洗...")
|
debug_print(f"第{cycle+1}/{final_repeats}次清洗")
|
||||||
|
|
||||||
# 1. 转移溶剂
|
# 1. 转移溶剂
|
||||||
try:
|
try:
|
||||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||||
|
|
||||||
debug_print(f" 💧 添加溶剂: {final_volume}mL {solvent}")
|
|
||||||
transfer_actions = generate_pump_protocol_with_rinsing(
|
transfer_actions = generate_pump_protocol_with_rinsing(
|
||||||
G=G,
|
G=G,
|
||||||
from_vessel=solvent_source,
|
from_vessel=solvent_source,
|
||||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
to_vessel=vessel_id,
|
||||||
volume=final_volume,
|
volume=final_volume,
|
||||||
amount="",
|
amount="",
|
||||||
time=0.0,
|
time=0.0,
|
||||||
@@ -341,9 +169,7 @@ def generate_wash_solid_protocol(
|
|||||||
|
|
||||||
if transfer_actions:
|
if transfer_actions:
|
||||||
action_sequence.extend(transfer_actions)
|
action_sequence.extend(transfer_actions)
|
||||||
debug_print(f" ✅ 转移动作: {len(transfer_actions)}个 🚚")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 添加溶剂后
|
|
||||||
current_volume += final_volume
|
current_volume += final_volume
|
||||||
total_solvent_used += final_volume
|
total_solvent_used += final_volume
|
||||||
|
|
||||||
@@ -352,121 +178,90 @@ def generate_wash_solid_protocol(
|
|||||||
f"第{cycle+1}次清洗添加{final_volume}mL溶剂后")
|
f"第{cycle+1}次清洗添加{final_volume}mL溶剂后")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f" ❌ 转移失败: {str(e)} 😞")
|
debug_print(f"转移失败: {str(e)}")
|
||||||
|
|
||||||
# 2. 搅拌(如果需要)
|
# 2. 搅拌(如果需要)
|
||||||
if stir and final_time > 0:
|
if stir and final_time > 0:
|
||||||
debug_print(f" 🌪️ 搅拌: {final_time}s @ {stir_speed}RPM")
|
|
||||||
stir_action = {
|
stir_action = {
|
||||||
"device_id": "stirrer_1",
|
"device_id": "stirrer_1",
|
||||||
"action_name": "stir",
|
"action_name": "stir",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"time": str(time),
|
"time": str(time),
|
||||||
"stir_time": final_time,
|
"stir_time": final_time,
|
||||||
"stir_speed": stir_speed,
|
"stir_speed": stir_speed,
|
||||||
"settling_time": 10.0 # 🕐 缩短沉降时间
|
"settling_time": 10.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(stir_action)
|
action_sequence.append(stir_action)
|
||||||
debug_print(f" ✅ 搅拌动作: {final_time}s, {stir_speed}RPM 🌪️")
|
|
||||||
|
|
||||||
# 3. 过滤
|
# 3. 过滤
|
||||||
debug_print(f" 🌊 过滤到: {actual_filtrate_vessel}")
|
|
||||||
filter_action = {
|
filter_action = {
|
||||||
"device_id": "filter_1",
|
"device_id": "filter_1",
|
||||||
"action_name": "filter",
|
"action_name": "filter",
|
||||||
"action_kwargs": {
|
"action_kwargs": {
|
||||||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
"vessel": {"id": vessel_id},
|
||||||
"filtrate_vessel": actual_filtrate_vessel,
|
"filtrate_vessel": actual_filtrate_vessel,
|
||||||
"temp": temp,
|
"temp": temp,
|
||||||
"volume": final_volume
|
"volume": final_volume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
action_sequence.append(filter_action)
|
action_sequence.append(filter_action)
|
||||||
debug_print(f" ✅ 过滤动作: → {actual_filtrate_vessel} 🌊")
|
|
||||||
|
|
||||||
# 🔧 新增:更新体积 - 过滤后(液体被滤除)
|
# 更新体积 - 过滤后
|
||||||
# 假设滤液完全被移除,固体残留在容器中
|
filtered_volume = current_volume * 0.9
|
||||||
filtered_volume = current_volume * 0.9 # 假设90%的液体被过滤掉
|
|
||||||
current_volume = current_volume - filtered_volume
|
current_volume = current_volume - filtered_volume
|
||||||
|
|
||||||
if isinstance(vessel, dict):
|
if isinstance(vessel, dict):
|
||||||
update_vessel_volume(vessel, G, current_volume,
|
update_vessel_volume(vessel, G, current_volume,
|
||||||
f"第{cycle+1}次清洗过滤后")
|
f"第{cycle+1}次清洗过滤后")
|
||||||
|
|
||||||
# 4. 等待(缩短时间)
|
# 4. 等待
|
||||||
wait_time = 5.0 # 🕐 缩短等待时间:10s → 5s
|
wait_time = 5.0
|
||||||
action_sequence.append({
|
action_sequence.append({
|
||||||
"action_name": "wait",
|
"action_name": "wait",
|
||||||
"action_kwargs": {"time": wait_time}
|
"action_kwargs": {"time": wait_time}
|
||||||
})
|
})
|
||||||
debug_print(f" ✅ 等待: {wait_time}s ⏰")
|
|
||||||
|
|
||||||
# 🔧 新增:清洗完成后的最终状态报告
|
# 最终状态
|
||||||
if isinstance(vessel, dict):
|
if isinstance(vessel, dict):
|
||||||
final_volume_vessel = get_vessel_liquid_volume(vessel)
|
final_volume_vessel = get_resource_liquid_volume(vessel)
|
||||||
else:
|
else:
|
||||||
final_volume_vessel = current_volume
|
final_volume_vessel = current_volume
|
||||||
|
|
||||||
# 🎊 总结
|
debug_print(f"固体清洗协议生成完成: {len(action_sequence)} 个动作, {final_repeats}次清洗, 溶剂总用量={total_solvent_used:.2f}mL")
|
||||||
debug_print("🧼" * 20)
|
|
||||||
debug_print(f"🎉 固体清洗协议生成完成! ✨")
|
|
||||||
debug_print(f"📊 协议统计:")
|
|
||||||
debug_print(f" 📋 总动作数: {len(action_sequence)} 个")
|
|
||||||
debug_print(f" 🥽 清洗容器: {vessel_display}")
|
|
||||||
debug_print(f" 🧪 使用溶剂: {solvent}")
|
|
||||||
debug_print(f" 💧 单次体积: {final_volume}mL")
|
|
||||||
debug_print(f" 🔄 清洗次数: {final_repeats}次")
|
|
||||||
debug_print(f" 💧 总溶剂用量: {total_solvent_used:.2f}mL")
|
|
||||||
debug_print(f"📊 体积变化统计:")
|
|
||||||
debug_print(f" - 清洗前体积: {original_volume:.2f}mL")
|
|
||||||
debug_print(f" - 清洗后体积: {final_volume_vessel:.2f}mL")
|
|
||||||
debug_print(f" - 溶剂总用量: {total_solvent_used:.2f}mL")
|
|
||||||
debug_print(f"⏱️ 预计总时间: {(final_time + 5) * final_repeats / 60:.1f} 分钟")
|
|
||||||
debug_print("🧼" * 20)
|
|
||||||
|
|
||||||
return action_sequence
|
return action_sequence
|
||||||
|
|
||||||
# 🔧 新增:便捷函数
|
# 便捷函数
|
||||||
def wash_with_water(G: nx.DiGraph, vessel: Union[str, dict],
|
def wash_with_water(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
volume: Union[float, str] = "50",
|
volume: Union[float, str] = "50",
|
||||||
repeats: int = 2) -> List[Dict[str, Any]]:
|
repeats: int = 2) -> List[Dict[str, Any]]:
|
||||||
"""用水清洗固体"""
|
"""用水清洗固体"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"💧 水洗固体: {vessel_display} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, "water", volume=volume, repeats=repeats)
|
return generate_wash_solid_protocol(G, vessel, "water", volume=volume, repeats=repeats)
|
||||||
|
|
||||||
def wash_with_ethanol(G: nx.DiGraph, vessel: Union[str, dict],
|
def wash_with_ethanol(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
volume: Union[float, str] = "30",
|
volume: Union[float, str] = "30",
|
||||||
repeats: int = 1) -> List[Dict[str, Any]]:
|
repeats: int = 1) -> List[Dict[str, Any]]:
|
||||||
"""用乙醇清洗固体"""
|
"""用乙醇清洗固体"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"🍺 乙醇洗固体: {vessel_display} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, "ethanol", volume=volume, repeats=repeats)
|
return generate_wash_solid_protocol(G, vessel, "ethanol", volume=volume, repeats=repeats)
|
||||||
|
|
||||||
def wash_with_acetone(G: nx.DiGraph, vessel: Union[str, dict],
|
def wash_with_acetone(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
volume: Union[float, str] = "25",
|
volume: Union[float, str] = "25",
|
||||||
repeats: int = 1) -> List[Dict[str, Any]]:
|
repeats: int = 1) -> List[Dict[str, Any]]:
|
||||||
"""用丙酮清洗固体"""
|
"""用丙酮清洗固体"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"💨 丙酮洗固体: {vessel_display} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, "acetone", volume=volume, repeats=repeats)
|
return generate_wash_solid_protocol(G, vessel, "acetone", volume=volume, repeats=repeats)
|
||||||
|
|
||||||
def wash_with_ether(G: nx.DiGraph, vessel: Union[str, dict],
|
def wash_with_ether(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
volume: Union[float, str] = "40",
|
volume: Union[float, str] = "40",
|
||||||
repeats: int = 2) -> List[Dict[str, Any]]:
|
repeats: int = 2) -> List[Dict[str, Any]]:
|
||||||
"""用乙醚清洗固体"""
|
"""用乙醚清洗固体"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"🌬️ 乙醚洗固体: {vessel_display} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, "diethyl_ether", volume=volume, repeats=repeats)
|
return generate_wash_solid_protocol(G, vessel, "diethyl_ether", volume=volume, repeats=repeats)
|
||||||
|
|
||||||
def wash_with_cold_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
def wash_with_cold_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
solvent: str, volume: Union[float, str] = "30",
|
solvent: str, volume: Union[float, str] = "30",
|
||||||
repeats: int = 1) -> List[Dict[str, Any]]:
|
repeats: int = 1) -> List[Dict[str, Any]]:
|
||||||
"""用冷溶剂清洗固体"""
|
"""用冷溶剂清洗固体"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"❄️ 冷{solvent}洗固体: {vessel_display} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
||||||
temp=5.0, repeats=repeats)
|
temp=5.0, repeats=repeats)
|
||||||
|
|
||||||
@@ -474,8 +269,6 @@ def wash_with_hot_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
|||||||
solvent: str, volume: Union[float, str] = "50",
|
solvent: str, volume: Union[float, str] = "50",
|
||||||
repeats: int = 1) -> List[Dict[str, Any]]:
|
repeats: int = 1) -> List[Dict[str, Any]]:
|
||||||
"""用热溶剂清洗固体"""
|
"""用热溶剂清洗固体"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"🔥 热{solvent}洗固体: {vessel_display} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
||||||
temp=60.0, repeats=repeats)
|
temp=60.0, repeats=repeats)
|
||||||
|
|
||||||
@@ -484,8 +277,6 @@ def wash_with_stirring(G: nx.DiGraph, vessel: Union[str, dict],
|
|||||||
stir_time: Union[str, float] = "5 min",
|
stir_time: Union[str, float] = "5 min",
|
||||||
repeats: int = 1) -> List[Dict[str, Any]]:
|
repeats: int = 1) -> List[Dict[str, Any]]:
|
||||||
"""带搅拌的溶剂清洗"""
|
"""带搅拌的溶剂清洗"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"🌪️ 搅拌清洗: {vessel_display} with {solvent} ({repeats} 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
||||||
stir=True, stir_speed=200.0,
|
stir=True, stir_speed=200.0,
|
||||||
time=stir_time, repeats=repeats)
|
time=stir_time, repeats=repeats)
|
||||||
@@ -493,23 +284,16 @@ def wash_with_stirring(G: nx.DiGraph, vessel: Union[str, dict],
|
|||||||
def thorough_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
def thorough_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
solvent: str, volume: Union[float, str] = "50") -> List[Dict[str, Any]]:
|
solvent: str, volume: Union[float, str] = "50") -> List[Dict[str, Any]]:
|
||||||
"""彻底清洗(多次重复)"""
|
"""彻底清洗(多次重复)"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"🔄 彻底清洗: {vessel_display} with {solvent} (5 次)")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume, repeats=5)
|
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume, repeats=5)
|
||||||
|
|
||||||
def quick_rinse(G: nx.DiGraph, vessel: Union[str, dict],
|
def quick_rinse(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
solvent: str, volume: Union[float, str] = "20") -> List[Dict[str, Any]]:
|
solvent: str, volume: Union[float, str] = "20") -> List[Dict[str, Any]]:
|
||||||
"""快速冲洗(单次,小体积)"""
|
"""快速冲洗(单次,小体积)"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"⚡ 快速冲洗: {vessel_display} with {solvent}")
|
|
||||||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume, repeats=1)
|
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume, repeats=1)
|
||||||
|
|
||||||
def sequential_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
def sequential_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
||||||
solvents: list, volume: Union[float, str] = "40") -> List[Dict[str, Any]]:
|
solvents: list, volume: Union[float, str] = "40") -> List[Dict[str, Any]]:
|
||||||
"""连续多溶剂清洗"""
|
"""连续多溶剂清洗"""
|
||||||
vessel_display = get_vessel_display_info(vessel)
|
|
||||||
debug_print(f"📝 连续清洗: {vessel_display} with {' → '.join(solvents)}")
|
|
||||||
|
|
||||||
action_sequence = []
|
action_sequence = []
|
||||||
for solvent in solvents:
|
for solvent in solvents:
|
||||||
wash_actions = generate_wash_solid_protocol(G, vessel, solvent,
|
wash_actions = generate_wash_solid_protocol(G, vessel, solvent,
|
||||||
@@ -521,28 +305,21 @@ def sequential_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
|||||||
# 测试函数
|
# 测试函数
|
||||||
def test_wash_solid_protocol():
|
def test_wash_solid_protocol():
|
||||||
"""测试固体清洗协议"""
|
"""测试固体清洗协议"""
|
||||||
debug_print("🧪 === WASH SOLID PROTOCOL 测试 === ✨")
|
debug_print("=== WASH SOLID PROTOCOL 测试 ===")
|
||||||
|
|
||||||
# 测试vessel参数处理
|
|
||||||
debug_print("🔧 测试vessel参数处理...")
|
|
||||||
|
|
||||||
# 测试字典格式
|
|
||||||
vessel_dict = {"id": "filter_flask_1", "name": "过滤瓶1",
|
vessel_dict = {"id": "filter_flask_1", "name": "过滤瓶1",
|
||||||
"data": {"liquid_volume": 25.0}}
|
"data": {"liquid_volume": 25.0}}
|
||||||
vessel_id = extract_vessel_id(vessel_dict)
|
vessel_id = extract_vessel_id(vessel_dict)
|
||||||
vessel_display = get_vessel_display_info(vessel_dict)
|
vessel_display = get_vessel_display_info(vessel_dict)
|
||||||
volume = get_vessel_liquid_volume(vessel_dict)
|
volume = get_resource_liquid_volume(vessel_dict)
|
||||||
debug_print(f" 字典格式: {vessel_dict}")
|
debug_print(f"字典格式: ID={vessel_id}, 显示={vessel_display}, 体积={volume}mL")
|
||||||
debug_print(f" → ID: {vessel_id}, 显示: {vessel_display}, 体积: {volume}mL")
|
|
||||||
|
|
||||||
# 测试字符串格式
|
|
||||||
vessel_str = "filter_flask_2"
|
vessel_str = "filter_flask_2"
|
||||||
vessel_id = extract_vessel_id(vessel_str)
|
vessel_id = extract_vessel_id(vessel_str)
|
||||||
vessel_display = get_vessel_display_info(vessel_str)
|
vessel_display = get_vessel_display_info(vessel_str)
|
||||||
debug_print(f" 字符串格式: {vessel_str}")
|
debug_print(f"字符串格式: ID={vessel_id}, 显示={vessel_display}")
|
||||||
debug_print(f" → ID: {vessel_id}, 显示: {vessel_display}")
|
|
||||||
|
|
||||||
debug_print("✅ 测试完成 🎉")
|
debug_print("测试完成")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_wash_solid_protocol()
|
test_wash_solid_protocol()
|
||||||
Reference in New Issue
Block a user