补充了剩下的几个protocol

This commit is contained in:
KCFeng425
2025-07-16 10:38:12 +08:00
parent ac294194e6
commit ed3b22a738
10 changed files with 2229 additions and 536 deletions

View File

@@ -11,6 +11,22 @@ def debug_print(message):
print(f"[DISSOLVE] {message}", flush=True)
logger.info(f"[DISSOLVE] {message}")
# 🆕 创建进度日志动作
def create_action_log(message: str, emoji: str = "📝") -> Dict[str, Any]:
"""创建一个动作日志"""
full_message = f"{emoji} {message}"
debug_print(full_message)
logger.info(full_message)
print(f"[ACTION] {full_message}", flush=True)
return {
"action_name": "wait",
"action_kwargs": {
"time": 0.1,
"log_message": full_message
}
}
def parse_volume_input(volume_input: Union[str, float]) -> float:
"""
解析体积输入,支持带单位的字符串
@@ -22,18 +38,20 @@ def parse_volume_input(volume_input: Union[str, float]) -> float:
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}'")
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")
debug_print(f"检测到未知体积,使用默认值: {default_volume}mL 🎯")
return default_volume
# 移除空格并提取数字和单位
@@ -43,7 +61,7 @@ def parse_volume_input(volume_input: Union[str, float]) -> float:
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")
debug_print(f" 无法解析体积: '{volume_str}'使用默认值50mL")
return 50.0
value = float(match.group(1))
@@ -52,12 +70,14 @@ def parse_volume_input(volume_input: Union[str, float]) -> float:
# 转换为毫升
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")
debug_print(f"体积转换: {value}{unit}{volume}mL")
return volume
def parse_mass_input(mass_input: Union[str, float]) -> float:
@@ -71,18 +91,20 @@ def parse_mass_input(mass_input: Union[str, float]) -> float:
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}'")
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")
debug_print(f"检测到未知质量,使用默认值: {default_mass}g 🎯")
return default_mass
# 移除空格并提取数字和单位
@@ -92,7 +114,7 @@ def parse_mass_input(mass_input: Union[str, float]) -> float:
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")
debug_print(f" 无法解析质量: '{mass_str}'返回0.0g")
return 0.0
value = float(match.group(1))
@@ -101,12 +123,14 @@ def parse_mass_input(mass_input: Union[str, float]) -> float:
# 转换为克
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")
debug_print(f"质量转换: {value}{unit}{mass}g")
return mass
def parse_time_input(time_input: Union[str, float]) -> float:
@@ -120,18 +144,20 @@ def parse_time_input(time_input: Union[str, float]) -> float:
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}'")
debug_print(f"🔍 解析时间输入: '{time_str}'")
# 处理未知时间
if time_str in ['?', 'unknown', 'tbd']:
default_time = 600.0 # 默认10分钟
debug_print(f"检测到未知时间,使用默认值: {default_time}s")
debug_print(f"检测到未知时间,使用默认值: {default_time}s (10分钟) ⏰")
return default_time
# 移除空格并提取数字和单位
@@ -141,7 +167,7 @@ def parse_time_input(time_input: Union[str, float]) -> float:
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")
debug_print(f" 无法解析时间: '{time_str}'返回0s")
return 0.0
value = float(match.group(1))
@@ -150,14 +176,17 @@ def parse_time_input(time_input: Union[str, float]) -> float:
# 转换为秒
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}")
debug_print(f"时间转换: {value}{unit}{time_sec}s")
return time_sec
def parse_temperature_input(temp_input: Union[str, float]) -> float:
@@ -171,13 +200,15 @@ def parse_temperature_input(temp_input: Union[str, float]) -> float:
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}'")
debug_print(f"🔍 解析温度输入: '{temp_str}'")
# 处理特殊温度描述
temp_aliases = {
@@ -193,7 +224,7 @@ def parse_temperature_input(temp_input: Union[str, float]) -> float:
if temp_str in temp_aliases:
result = temp_aliases[temp_str]
debug_print(f"温度别名解析: '{temp_str}'{result}°C")
debug_print(f"🏷️ 温度别名解析: '{temp_str}'{result}°C")
return result
# 移除空格并提取数字和单位
@@ -203,7 +234,7 @@ def parse_temperature_input(temp_input: Union[str, float]) -> float:
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")
debug_print(f" 无法解析温度: '{temp_str}'使用默认值25°C")
return 25.0
value = float(match.group(1))
@@ -212,19 +243,22 @@ def parse_temperature_input(temp_input: Union[str, float]) -> float:
# 转换为摄氏度
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")
debug_print(f"温度转换: {value}{unit}{temp_c}°C")
return temp_c
def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
"""增强版溶剂容器查找"""
debug_print(f"查找溶剂 '{solvent}' 的容器...")
"""增强版溶剂容器查找,支持多种匹配模式"""
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', '')
@@ -237,18 +271,20 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
# 精确匹配
if reagent_name == solvent.lower() or config_reagent == solvent.lower():
debug_print(f"✅ 通过reagent字段到容器: {node}")
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}")
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}",
@@ -256,77 +292,118 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
f"{solvent_clean}_bottle",
f"solvent_{solvent_clean}",
f"reagent_{solvent_clean}",
f"reagent_bottle_{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}")
debug_print(f"✅ 通过命名规则找到容器: {name} 📝")
return name
# 🔧 方法3使用第一个试剂瓶作为备选
# 🔧 方法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}")
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}")
debug_print(f"找到连接的加热搅拌器: {heatchill} 🔗")
return heatchill
# 返回第一个加热器
if heatchill_nodes:
debug_print(f"使用第一个加热器: {heatchill_nodes[0]}")
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}")
debug_print(f"找到连接的搅拌器: {stirrer} 🔗")
return stirrer
# 返回第一个搅拌器
if stirrer_nodes:
debug_print(f"使用第一个搅拌器: {stirrer_nodes[0]}")
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}")
debug_print(f"找到固体加样器: {node} 🥄")
return node
debug_print("⚠️ 未找到固体加样器")
debug_print(f" 未找到固体加样器")
return ""
def generate_dissolve_protocol(
@@ -347,12 +424,13 @@ def generate_dissolve_protocol(
**kwargs # 🔧 关键接受所有其他参数防止unexpected keyword错误
) -> List[Dict[str, Any]]:
"""
生成溶解操作的协议序列 - 修复
生成溶解操作的协议序列 - 增强
🔧 修复要点:
1. 添加action文件中的所有参数mass, mol, reagent, event
2. 使用 **kwargs 接受所有额外参数,防止 unexpected keyword argument 错误
3. 支持固体溶解和液体溶解两种模式
4. 添加详细的emoji日志系统
支持两种溶解模式:
1. 液体溶解:指定 solvent + volume使用pump protocol转移溶剂
@@ -367,35 +445,40 @@ def generate_dissolve_protocol(
"""
debug_print("=" * 60)
debug_print("开始生成溶解协议 - 修复版")
debug_print(f"原始参数:")
debug_print(f" - vessel: '{vessel}'")
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("🧪 开始生成溶解协议")
debug_print(f"📋 原始参数:")
debug_print(f" 🥼 vessel: '{vessel}'")
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 = []
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
debug_print("🔍 步骤1: 参数验证...")
action_sequence.append(create_action_log(f"开始溶解操作 - 容器: {vessel}", "🎬"))
if not vessel:
debug_print("❌ vessel 参数不能为空")
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中")
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
debug_print("✅ 基本参数验证通过")
action_sequence.append(create_action_log("参数验证通过", ""))
# === 🔧 关键修复:参数解析 ===
debug_print("步骤2: 参数解析...")
debug_print("🔍 步骤2: 参数解析...")
action_sequence.append(create_action_log("正在解析溶解参数...", "🔍"))
# 解析各种参数为数值
final_volume = parse_volume_input(volume)
@@ -403,17 +486,18 @@ def generate_dissolve_protocol(
final_temp = parse_temperature_input(temp)
final_time = parse_time_input(time)
debug_print(f"解析结果:")
debug_print(f" - 体积: {final_volume}mL")
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(f"📊 解析结果:")
debug_print(f" 📏 体积: {final_volume}mL")
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: 判断溶解类型...")
debug_print("🔍 步骤3: 判断溶解类型...")
action_sequence.append(create_action_log("正在判断溶解类型...", "🔍"))
# 判断是固体溶解还是液体溶解
is_solid_dissolve = (final_mass > 0 or (mol and mol.strip() != "") or (reagent and reagent.strip() != ""))
@@ -427,10 +511,15 @@ def generate_dissolve_protocol(
solvent = "water" # 默认溶剂
debug_print("⚠️ 未明确指定溶解参数默认为50mL水溶解")
debug_print(f"溶解类型: {'固体溶解' if is_solid_dissolve else '液体溶解'}")
dissolve_type = "固体溶解" if is_solid_dissolve else "液体溶解"
dissolve_emoji = "🧂" if is_solid_dissolve else "💧"
debug_print(f"📋 溶解类型: {dissolve_type} {dissolve_emoji}")
action_sequence.append(create_action_log(f"确定溶解类型: {dissolve_type} {dissolve_emoji}", "📋"))
# === 查找设备 ===
debug_print("步骤4: 查找设备...")
debug_print("🔍 步骤4: 查找设备...")
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
# 查找加热搅拌器
heatchill_id = find_connected_heatchill(G, vessel)
@@ -439,21 +528,31 @@ def generate_dissolve_protocol(
# 优先使用加热搅拌器,否则使用独立搅拌器
stir_device_id = heatchill_id or stirrer_id
debug_print(f"设备映射:")
debug_print(f" - 加热器: '{heatchill_id}'")
debug_print(f" - 搅拌器: '{stirrer_id}'")
debug_print(f" - 使用设备: '{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:
action_sequence.append(create_action_log(f"找到加热搅拌器: {heatchill_id}", "🔥"))
elif stirrer_id:
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_id}", "🌪️"))
else:
action_sequence.append(create_action_log("未找到搅拌设备,将跳过搅拌", "⚠️"))
# === 执行溶解流程 ===
debug_print("步骤5: 执行溶解流程...")
debug_print("🔍 步骤5: 执行溶解流程...")
try:
# 步骤5.1: 启动加热搅拌(如果需要)
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")
debug_print(f"🔍 5.1: 启动加热搅拌,温度: {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):
# 使用加热搅拌器
action_sequence.append(create_action_log(f"启动加热搅拌器 {heatchill_id}", "🔥"))
heatchill_action = {
"device_id": heatchill_id,
"action_name": "heat_chill_start",
@@ -468,6 +567,7 @@ def generate_dissolve_protocol(
# 等待温度稳定
if final_temp > 25.0:
wait_time = min(60, abs(final_temp - 25.0) * 1.5)
action_sequence.append(create_action_log(f"等待温度稳定 ({wait_time:.0f}秒)", ""))
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": wait_time}
@@ -475,6 +575,8 @@ def generate_dissolve_protocol(
elif stirrer_id:
# 使用独立搅拌器
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {stir_speed}rpm)", "🌪️"))
stir_action = {
"device_id": stirrer_id,
"action_name": "start_stir",
@@ -487,6 +589,7 @@ def generate_dissolve_protocol(
action_sequence.append(stir_action)
# 等待搅拌稳定
action_sequence.append(create_action_log("等待搅拌稳定...", ""))
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 5}
@@ -494,10 +597,13 @@ def generate_dissolve_protocol(
if is_solid_dissolve:
# === 固体溶解路径 ===
debug_print(f"5.2: 使用固体溶解路径")
debug_print(f"🔍 5.2: 使用固体溶解路径")
action_sequence.append(create_action_log("开始固体溶解流程", "🧂"))
solid_dispenser = find_solid_dispenser(G)
if solid_dispenser:
action_sequence.append(create_action_log(f"找到固体加样器: {solid_dispenser}", "🥄"))
# 固体加样
add_kwargs = {
"vessel": vessel,
@@ -508,9 +614,12 @@ def generate_dissolve_protocol(
if final_mass > 0:
add_kwargs["mass"] = str(final_mass)
action_sequence.append(create_action_log(f"准备添加固体: {final_mass}g", "⚖️"))
if mol and mol.strip():
add_kwargs["mol"] = mol
action_sequence.append(create_action_log(f"按摩尔数添加: {mol}", "🧬"))
action_sequence.append(create_action_log("开始固体加样操作", "🥄"))
action_sequence.append({
"device_id": solid_dispenser,
"action_name": "add_solid",
@@ -518,18 +627,24 @@ def generate_dissolve_protocol(
})
debug_print(f"✅ 固体加样完成")
action_sequence.append(create_action_log("固体加样完成", ""))
else:
debug_print("⚠️ 未找到固体加样器,跳过固体添加")
action_sequence.append(create_action_log("未找到固体加样器,无法添加固体", ""))
elif is_liquid_dissolve:
# === 液体溶解路径 ===
debug_print(f"5.3: 使用液体溶解路径")
debug_print(f"🔍 5.3: 使用液体溶解路径")
action_sequence.append(create_action_log("开始液体溶解流程", "💧"))
# 查找溶剂容器
action_sequence.append(create_action_log("正在查找溶剂容器...", "🔍"))
try:
solvent_vessel = find_solvent_vessel(G, solvent)
action_sequence.append(create_action_log(f"找到溶剂容器: {solvent_vessel}", "🧪"))
except ValueError as e:
debug_print(f"⚠️ {str(e)},跳过溶剂添加")
action_sequence.append(create_action_log(f"溶剂容器查找失败: {str(e)}", ""))
solvent_vessel = None
if solvent_vessel:
@@ -537,6 +652,9 @@ def generate_dissolve_protocol(
flowrate = 1.0 # 较慢的注入速度
transfer_flowrate = 0.5 # 较慢的转移速度
action_sequence.append(create_action_log(f"设置流速: {flowrate}mL/min (缓慢注入)", ""))
action_sequence.append(create_action_log(f"开始转移 {final_volume}mL {solvent}", "🚰"))
# 调用pump protocol
pump_actions = generate_pump_protocol_with_rinsing(
G=G,
@@ -559,8 +677,10 @@ def generate_dissolve_protocol(
)
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("溶剂添加后短暂等待...", ""))
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 5}
@@ -568,10 +688,14 @@ def generate_dissolve_protocol(
# 步骤5.4: 等待溶解完成
if final_time > 0:
debug_print(f"5.4: 等待溶解完成 - {final_time}s")
debug_print(f"🔍 5.4: 等待溶解完成 - {final_time}s")
wait_minutes = final_time / 60
action_sequence.append(create_action_log(f"开始溶解等待 ({wait_minutes:.1f}分钟)", ""))
if heatchill_id:
# 使用定时加热搅拌
action_sequence.append(create_action_log(f"使用加热搅拌器进行定时溶解", "🔥"))
dissolve_action = {
"device_id": heatchill_id,
"action_name": "heat_chill",
@@ -588,6 +712,8 @@ def generate_dissolve_protocol(
elif stirrer_id:
# 使用定时搅拌
action_sequence.append(create_action_log(f"使用搅拌器进行定时溶解", "🌪️"))
stir_action = {
"device_id": stirrer_id,
"action_name": "stir",
@@ -603,6 +729,7 @@ def generate_dissolve_protocol(
else:
# 简单等待
action_sequence.append(create_action_log(f"简单等待溶解完成", ""))
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": final_time}
@@ -610,7 +737,8 @@ def generate_dissolve_protocol(
# 步骤5.5: 停止加热搅拌(如果需要)
if heatchill_id and final_time == 0 and final_temp > 25.0:
debug_print(f"5.5: 停止加热器")
debug_print(f"🔍 5.5: 停止加热器")
action_sequence.append(create_action_log("停止加热搅拌器", "🛑"))
stop_action = {
"device_id": heatchill_id,
@@ -622,7 +750,8 @@ def generate_dissolve_protocol(
action_sequence.append(stop_action)
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({
"device_id": "system",
@@ -634,21 +763,30 @@ def generate_dissolve_protocol(
# === 最终结果 ===
debug_print("=" * 60)
debug_print(f" 溶解协议生成完成")
debug_print(f"📊 总动作数: {len(action_sequence)}")
debug_print(f"📋 处理总结:")
debug_print(f" - 容器: {vessel}")
debug_print(f" - 溶解类型: {'固体溶解' if is_solid_dissolve else '液体溶解'}")
debug_print(f"🎉 溶解协议生成完成")
debug_print(f"📊 协议统计:")
debug_print(f" 📋 总动作数: {len(action_sequence)}")
debug_print(f" 🥼 容器: {vessel}")
debug_print(f" {dissolve_emoji} 溶解类型: {dissolve_type}")
if is_liquid_dissolve:
debug_print(f" - 溶剂: {solvent} ({final_volume}mL)")
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" 🧪 试剂: {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("=" * 60)
# 添加完成日志
summary_msg = f"溶解协议完成: {vessel}"
if is_liquid_dissolve:
summary_msg += f" (使用 {final_volume}mL {solvent})"
if is_solid_dissolve:
summary_msg += f" (溶解 {final_mass}g {reagent})"
action_sequence.append(create_action_log(summary_msg, "🎉"))
return action_sequence
# === 便捷函数 ===
@@ -656,6 +794,7 @@ def generate_dissolve_protocol(
def dissolve_solid_by_mass(G: nx.DiGraph, vessel: str, reagent: str, mass: Union[str, float],
temp: Union[str, float] = 25.0, time: Union[str, float] = "10 min") -> List[Dict[str, Any]]:
"""按质量溶解固体"""
debug_print(f"🧂 快速固体溶解: {reagent} ({mass}) → {vessel}")
return generate_dissolve_protocol(
G, vessel,
mass=mass,
@@ -667,6 +806,7 @@ def dissolve_solid_by_mass(G: nx.DiGraph, vessel: str, reagent: str, mass: Union
def dissolve_solid_by_moles(G: nx.DiGraph, vessel: str, reagent: str, mol: str,
temp: Union[str, float] = 25.0, time: Union[str, float] = "10 min") -> List[Dict[str, Any]]:
"""按摩尔数溶解固体"""
debug_print(f"🧬 按摩尔数溶解固体: {reagent} ({mol}) → {vessel}")
return generate_dissolve_protocol(
G, vessel,
mol=mol,
@@ -678,6 +818,7 @@ def dissolve_solid_by_moles(G: nx.DiGraph, vessel: str, reagent: str, mol: str,
def dissolve_with_solvent(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float],
temp: Union[str, float] = 25.0, time: Union[str, float] = "5 min") -> List[Dict[str, Any]]:
"""用溶剂溶解"""
debug_print(f"💧 溶剂溶解: {solvent} ({volume}) → {vessel}")
return generate_dissolve_protocol(
G, vessel,
solvent=solvent,
@@ -688,6 +829,7 @@ def dissolve_with_solvent(G: nx.DiGraph, vessel: str, solvent: str, volume: Unio
def dissolve_at_room_temp(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float]) -> List[Dict[str, Any]]:
"""室温溶解"""
debug_print(f"🌡️ 室温溶解: {solvent} ({volume}) → {vessel}")
return generate_dissolve_protocol(
G, vessel,
solvent=solvent,
@@ -699,6 +841,7 @@ def dissolve_at_room_temp(G: nx.DiGraph, vessel: str, solvent: str, volume: Unio
def dissolve_with_heating(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float],
temp: Union[str, float] = "60 °C", time: Union[str, float] = "15 min") -> List[Dict[str, Any]]:
"""加热溶解"""
debug_print(f"🔥 加热溶解: {solvent} ({volume}) → {vessel} @ {temp}")
return generate_dissolve_protocol(
G, vessel,
solvent=solvent,
@@ -710,33 +853,37 @@ def dissolve_with_heating(G: nx.DiGraph, vessel: str, solvent: str, volume: Unio
# 测试函数
def test_dissolve_protocol():
"""测试溶解协议的各种参数解析"""
print("=== DISSOLVE PROTOCOL 修复版测试 ===")
debug_print("=== DISSOLVE PROTOCOL 增强版测试 ===")
# 测试体积解析
debug_print("💧 测试体积解析...")
volumes = ["10 mL", "?", 10.0, "1 L", "500 μL"]
for vol in volumes:
result = parse_volume_input(vol)
print(f"体积解析: {vol}{result}mL")
debug_print(f"📏 体积解析: {vol}{result}mL")
# 测试质量解析
debug_print("⚖️ 测试质量解析...")
masses = ["2.9 g", "?", 2.5, "500 mg"]
for mass in masses:
result = parse_mass_input(mass)
print(f"质量解析: {mass}{result}g")
debug_print(f"⚖️ 质量解析: {mass}{result}g")
# 测试温度解析
debug_print("🌡️ 测试温度解析...")
temps = ["60 °C", "room temperature", "?", 25.0, "reflux"]
for temp in temps:
result = parse_temperature_input(temp)
print(f"温度解析: {temp}{result}°C")
debug_print(f"🌡️ 温度解析: {temp}{result}°C")
# 测试时间解析
debug_print("⏱️ 测试时间解析...")
times = ["30 min", "1 h", "?", 60.0]
for time in times:
result = parse_time_input(time)
print(f"时间解析: {time}{result}s")
debug_print(f"⏱️ 时间解析: {time}{result}s")
print("✅ 测试完成")
debug_print("✅ 测试完成")
if __name__ == "__main__":
test_dissolve_protocol()