mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 13:25:13 +00:00
action to resource & 0.9.12 (#64)
* action to resource & 0.9.12 * stir和adjustph的中的bug修不好
This commit is contained in:
@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n environment_name
|
||||
|
||||
# Currently, you need to install the `unilabos_msgs` package
|
||||
# You can download the system-specific package from the Release page
|
||||
conda install ros-humble-unilabos-msgs-0.9.10-xxxxx.tar.bz2
|
||||
conda install ros-humble-unilabos-msgs-0.9.12-xxxxx.tar.bz2
|
||||
|
||||
# Install PyLabRobot and other prerequisites
|
||||
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
||||
|
||||
@@ -49,7 +49,7 @@ conda env update --file unilabos-[YOUR_OS].yml -n 环境名
|
||||
|
||||
# 现阶段,需要安装 `unilabos_msgs` 包
|
||||
# 可以前往 Release 页面下载系统对应的包进行安装
|
||||
conda install ros-humble-unilabos-msgs-0.9.11-xxxxx.tar.bz2
|
||||
conda install ros-humble-unilabos-msgs-0.9.12-xxxxx.tar.bz2
|
||||
|
||||
# 安装PyLabRobot等前置
|
||||
git clone https://github.com/PyLabRobot/pylabrobot plr_repo
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package:
|
||||
name: ros-humble-unilabos-msgs
|
||||
version: 0.9.11
|
||||
version: 0.9.12
|
||||
source:
|
||||
path: ../../unilabos_msgs
|
||||
folder: ros-humble-unilabos-msgs/src/work
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package:
|
||||
name: unilabos
|
||||
version: "0.9.11"
|
||||
version: "0.9.12"
|
||||
|
||||
source:
|
||||
path: ../..
|
||||
|
||||
2
setup.py
2
setup.py
@@ -4,7 +4,7 @@ package_name = 'unilabos'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='0.9.11',
|
||||
version='0.9.12',
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
install_requires=['setuptools'],
|
||||
|
||||
@@ -311,7 +311,7 @@ def create_action_log(message: str, emoji: str = "📝") -> Dict[str, Any]:
|
||||
|
||||
def generate_add_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:现在接收字典类型的 vessel
|
||||
reagent: str,
|
||||
# 🔧 修复:所有参数都用 Union 类型,支持字符串和数值
|
||||
volume: Union[str, float] = 0.0,
|
||||
@@ -334,6 +334,7 @@ def generate_add_protocol(
|
||||
生成添加试剂协议 - 修复版
|
||||
|
||||
支持所有XDL参数和单位:
|
||||
- vessel: Resource类型字典,包含id字段
|
||||
- volume: "2.7 mL", "2.67 mL", "?" 或数值
|
||||
- mass: "19.3 g", "4.5 g" 或数值
|
||||
- time: "1 h", "20 min" 或数值(秒)
|
||||
@@ -343,11 +344,19 @@ def generate_add_protocol(
|
||||
- equiv: "1.1"
|
||||
- ratio: "?", "1:1"
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
# 🔧 修改:更新容器的液体体积(假设有 liquid_volume 字段)
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
if isinstance(vessel["data"]["liquid_volume"], list) and len(vessel["data"]["liquid_volume"]) > 0:
|
||||
vessel["data"]["liquid_volume"][0] -= parse_volume_input(volume)
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("🚀 开始生成添加试剂协议")
|
||||
debug_print(f"📋 原始参数:")
|
||||
debug_print(f" 🥼 vessel: '{vessel}'")
|
||||
debug_print(f" 🥼 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" 🧪 reagent: '{reagent}'")
|
||||
debug_print(f" 📏 volume: {volume} (类型: {type(volume)})")
|
||||
debug_print(f" ⚖️ mass: {mass} (类型: {type(mass)})")
|
||||
@@ -363,18 +372,18 @@ def generate_add_protocol(
|
||||
|
||||
# === 参数验证 ===
|
||||
debug_print("🔍 步骤1: 参数验证...")
|
||||
action_sequence.append(create_action_log(f"开始添加试剂 '{reagent}' 到容器 '{vessel}'", "🎬"))
|
||||
action_sequence.append(create_action_log(f"开始添加试剂 '{reagent}' 到容器 '{vessel_id}'", "🎬"))
|
||||
|
||||
if not vessel:
|
||||
if not vessel or not vessel_id:
|
||||
debug_print("❌ vessel 参数不能为空")
|
||||
raise ValueError("vessel 参数不能为空")
|
||||
if not reagent:
|
||||
debug_print("❌ reagent 参数不能为空")
|
||||
raise ValueError("reagent 参数不能为空")
|
||||
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中")
|
||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||
if vessel_id not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel_id}' 不存在于系统中")
|
||||
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
||||
|
||||
debug_print("✅ 基本参数验证通过")
|
||||
|
||||
@@ -432,7 +441,7 @@ def generate_add_protocol(
|
||||
debug_print("🌪️ 准备启动搅拌...")
|
||||
action_sequence.append(create_action_log("准备启动搅拌器", "🌪️"))
|
||||
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
||||
if stirrer_id:
|
||||
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {stir_speed} rpm)", "🔄"))
|
||||
|
||||
@@ -440,7 +449,7 @@ def generate_add_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"stir_speed": stir_speed,
|
||||
"purpose": f"准备添加固体 {reagent}"
|
||||
}
|
||||
@@ -454,7 +463,7 @@ def generate_add_protocol(
|
||||
|
||||
# 固体加样
|
||||
add_kwargs = {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"reagent": reagent,
|
||||
"purpose": purpose,
|
||||
"event": event,
|
||||
@@ -509,7 +518,7 @@ def generate_add_protocol(
|
||||
debug_print("🌪️ 准备启动搅拌...")
|
||||
action_sequence.append(create_action_log("准备启动搅拌器", "🌪️"))
|
||||
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
||||
if stirrer_id:
|
||||
action_sequence.append(create_action_log(f"启动搅拌器 {stirrer_id} (速度: {stir_speed} rpm)", "🔄"))
|
||||
|
||||
@@ -517,7 +526,7 @@ def generate_add_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"stir_speed": stir_speed,
|
||||
"purpose": f"准备添加液体 {reagent}"
|
||||
}
|
||||
@@ -555,7 +564,7 @@ def generate_add_protocol(
|
||||
pump_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=reagent_vessel,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
volume=final_volume,
|
||||
amount=amount,
|
||||
time=final_time,
|
||||
@@ -594,7 +603,7 @@ def generate_add_protocol(
|
||||
debug_print(f"📋 处理总结:")
|
||||
debug_print(f" 🧪 试剂: {reagent}")
|
||||
debug_print(f" {add_emoji} 添加类型: {add_type}")
|
||||
debug_print(f" 🥼 目标容器: {vessel}")
|
||||
debug_print(f" 🥼 目标容器: {vessel_id}")
|
||||
if is_liquid:
|
||||
debug_print(f" 📏 体积: {final_volume}mL")
|
||||
if is_solid:
|
||||
@@ -603,7 +612,7 @@ def generate_add_protocol(
|
||||
debug_print("=" * 60)
|
||||
|
||||
# 添加完成日志
|
||||
summary_msg = f"试剂添加协议完成: {reagent} → {vessel}"
|
||||
summary_msg = f"试剂添加协议完成: {reagent} → {vessel_id}"
|
||||
if is_liquid:
|
||||
summary_msg += f" ({final_volume}mL)"
|
||||
if is_solid:
|
||||
@@ -614,11 +623,13 @@ def generate_add_protocol(
|
||||
return action_sequence
|
||||
|
||||
# === 便捷函数 ===
|
||||
# 🔧 修改便捷函数的参数类型
|
||||
|
||||
def add_liquid_volume(G: nx.DiGraph, vessel: str, reagent: str, volume: Union[str, float],
|
||||
def add_liquid_volume(G: nx.DiGraph, vessel: dict, reagent: str, volume: Union[str, float],
|
||||
time: Union[str, float] = 0.0, rate_spec: str = "") -> List[Dict[str, Any]]:
|
||||
"""添加指定体积的液体试剂"""
|
||||
debug_print(f"💧 快速添加液体: {reagent} ({volume}) → {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"💧 快速添加液体: {reagent} ({volume}) → {vessel_id}")
|
||||
return generate_add_protocol(
|
||||
G, vessel, reagent,
|
||||
volume=volume,
|
||||
@@ -626,30 +637,33 @@ def add_liquid_volume(G: nx.DiGraph, vessel: str, reagent: str, volume: Union[st
|
||||
rate_spec=rate_spec
|
||||
)
|
||||
|
||||
def add_solid_mass(G: nx.DiGraph, vessel: str, reagent: str, mass: Union[str, float],
|
||||
def add_solid_mass(G: nx.DiGraph, vessel: dict, reagent: str, mass: Union[str, float],
|
||||
event: str = "") -> List[Dict[str, Any]]:
|
||||
"""添加指定质量的固体试剂"""
|
||||
debug_print(f"🧂 快速添加固体: {reagent} ({mass}) → {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🧂 快速添加固体: {reagent} ({mass}) → {vessel_id}")
|
||||
return generate_add_protocol(
|
||||
G, vessel, reagent,
|
||||
mass=mass,
|
||||
event=event
|
||||
)
|
||||
|
||||
def add_solid_moles(G: nx.DiGraph, vessel: str, reagent: str, mol: str,
|
||||
def add_solid_moles(G: nx.DiGraph, vessel: dict, reagent: str, mol: str,
|
||||
event: str = "") -> List[Dict[str, Any]]:
|
||||
"""按摩尔数添加固体试剂"""
|
||||
debug_print(f"🧬 按摩尔数添加固体: {reagent} ({mol}) → {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🧬 按摩尔数添加固体: {reagent} ({mol}) → {vessel_id}")
|
||||
return generate_add_protocol(
|
||||
G, vessel, reagent,
|
||||
mol=mol,
|
||||
event=event
|
||||
)
|
||||
|
||||
def add_dropwise_liquid(G: nx.DiGraph, vessel: str, reagent: str, volume: Union[str, float],
|
||||
def add_dropwise_liquid(G: nx.DiGraph, vessel: dict, reagent: str, volume: Union[str, float],
|
||||
time: Union[str, float] = "20 min", event: str = "") -> List[Dict[str, Any]]:
|
||||
"""滴加液体试剂"""
|
||||
debug_print(f"💧 滴加液体: {reagent} ({volume}) → {vessel} (用时: {time})")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"💧 滴加液体: {reagent} ({volume}) → {vessel_id} (用时: {time})")
|
||||
return generate_add_protocol(
|
||||
G, vessel, reagent,
|
||||
volume=volume,
|
||||
@@ -658,10 +672,11 @@ def add_dropwise_liquid(G: nx.DiGraph, vessel: str, reagent: str, volume: Union[
|
||||
event=event
|
||||
)
|
||||
|
||||
def add_portionwise_solid(G: nx.DiGraph, vessel: str, reagent: str, mass: Union[str, float],
|
||||
def add_portionwise_solid(G: nx.DiGraph, vessel: dict, reagent: str, mass: Union[str, float],
|
||||
time: Union[str, float] = "1 h", event: str = "") -> List[Dict[str, Any]]:
|
||||
"""分批添加固体试剂"""
|
||||
debug_print(f"🧂 分批添加固体: {reagent} ({mass}) → {vessel} (用时: {time})")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🧂 分批添加固体: {reagent} ({mass}) → {vessel_id} (用时: {time})")
|
||||
return generate_add_protocol(
|
||||
G, vessel, reagent,
|
||||
mass=mass,
|
||||
|
||||
@@ -216,7 +216,7 @@ def calculate_reagent_volume(target_ph_value: float, reagent: str, vessel_volume
|
||||
|
||||
def generate_adjust_ph_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
ph_value: float,
|
||||
reagent: str,
|
||||
**kwargs
|
||||
@@ -226,7 +226,7 @@ def generate_adjust_ph_protocol(
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为容器和设备
|
||||
vessel: 目标容器(需要调节pH的容器)
|
||||
vessel: 目标容器字典(需要调节pH的容器)
|
||||
ph_value: 目标pH值(从XDL传入)
|
||||
reagent: 酸碱试剂名称(从XDL传入)
|
||||
**kwargs: 其他可选参数,使用默认值
|
||||
@@ -235,10 +235,13 @@ def generate_adjust_ph_protocol(
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("🧪 开始生成pH调节协议")
|
||||
debug_print(f"📋 原始参数:")
|
||||
debug_print(f" 🥼 vessel: '{vessel}'")
|
||||
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}")
|
||||
@@ -262,14 +265,14 @@ def generate_adjust_ph_protocol(
|
||||
|
||||
# 开始处理
|
||||
action_sequence.append(create_action_log(f"开始调节pH至 {ph_value}", "🧪"))
|
||||
action_sequence.append(create_action_log(f"目标容器: {vessel}", "🥼"))
|
||||
action_sequence.append(create_action_log(f"目标容器: {vessel_id}", "🥼"))
|
||||
action_sequence.append(create_action_log(f"使用试剂: {reagent}", "⚗️"))
|
||||
|
||||
# 1. 验证目标容器存在
|
||||
debug_print(f"🔍 步骤1: 验证目标容器...")
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 目标容器 '{vessel}' 不存在于系统中")
|
||||
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
|
||||
if vessel_id not in G.nodes():
|
||||
debug_print(f"❌ 目标容器 '{vessel_id}' 不存在于系统中")
|
||||
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
||||
|
||||
debug_print(f"✅ 目标容器验证通过")
|
||||
action_sequence.append(create_action_log("目标容器验证通过", "✅"))
|
||||
@@ -293,7 +296,7 @@ def generate_adjust_ph_protocol(
|
||||
action_sequence.append(create_action_log("开始自动估算试剂体积", "🧮"))
|
||||
|
||||
# 获取目标容器的体积信息
|
||||
vessel_data = G.nodes[vessel].get('data', {})
|
||||
vessel_data = G.nodes[vessel_id].get('data', {})
|
||||
vessel_volume = vessel_data.get('max_volume', 100.0) # 默认100mL
|
||||
debug_print(f"📏 容器最大体积: {vessel_volume}mL")
|
||||
|
||||
@@ -310,13 +313,13 @@ def generate_adjust_ph_protocol(
|
||||
action_sequence.append(create_action_log("验证转移路径...", "🛤️"))
|
||||
|
||||
try:
|
||||
path = nx.shortest_path(G, source=reagent_vessel, target=vessel)
|
||||
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)}", "🛤️"))
|
||||
except nx.NetworkXNoPath:
|
||||
debug_print(f"❌ 无法找到转移路径")
|
||||
action_sequence.append(create_action_log("转移路径不存在", "❌"))
|
||||
raise ValueError(f"从试剂容器 '{reagent_vessel}' 到目标容器 '{vessel}' 没有可用路径")
|
||||
raise ValueError(f"从试剂容器 '{reagent_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
||||
|
||||
# 5. 搅拌器设置
|
||||
debug_print(f"🔍 步骤5: 搅拌器设置...")
|
||||
@@ -325,7 +328,7 @@ def generate_adjust_ph_protocol(
|
||||
action_sequence.append(create_action_log("准备启动搅拌器", "🌪️"))
|
||||
|
||||
try:
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||
|
||||
if stirrer_id:
|
||||
debug_print(f"✅ 找到搅拌器 {stirrer_id},启动搅拌")
|
||||
@@ -335,7 +338,7 @@ def generate_adjust_ph_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id,
|
||||
"stir_speed": stir_speed,
|
||||
"purpose": f"pH调节: 启动搅拌,准备添加 {reagent}"
|
||||
}
|
||||
@@ -373,7 +376,7 @@ def generate_adjust_ph_protocol(
|
||||
pump_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=reagent_vessel,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id,
|
||||
volume=volume,
|
||||
amount="",
|
||||
time=addition_time,
|
||||
@@ -390,6 +393,54 @@ def generate_adjust_ph_protocol(
|
||||
debug_print(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"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
debug_print(f"📊 添加前容器体积: {current_volume}")
|
||||
|
||||
# 处理不同的体积数据格式
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
# 增加体积(添加试剂)
|
||||
vessel["data"]["liquid_volume"][0] += volume
|
||||
debug_print(f"📊 添加后容器体积: {vessel['data']['liquid_volume'][0]:.2f}mL (+{volume:.2f}mL)")
|
||||
else:
|
||||
# 如果列表为空,创建新的体积记录
|
||||
vessel["data"]["liquid_volume"] = [volume]
|
||||
debug_print(f"📊 初始化容器体积: {volume:.2f}mL")
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
# 直接数值类型
|
||||
vessel["data"]["liquid_volume"] += volume
|
||||
debug_print(f"📊 添加后容器体积: {vessel['data']['liquid_volume']:.2f}mL (+{volume:.2f}mL)")
|
||||
else:
|
||||
debug_print(f"⚠️ 未知的体积数据格式: {type(current_volume)}")
|
||||
# 创建新的体积记录
|
||||
vessel["data"]["liquid_volume"] = volume
|
||||
else:
|
||||
debug_print(f"📊 容器无液体体积数据,创建新记录: {volume:.2f}mL")
|
||||
# 确保vessel有data字段
|
||||
if "data" not in vessel:
|
||||
vessel["data"] = {}
|
||||
vessel["data"]["liquid_volume"] = volume
|
||||
|
||||
# 🔧 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
vessel_node_data = G.nodes[vessel_id].get('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] += volume
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = [volume]
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = current_node_volume + volume
|
||||
|
||||
debug_print(f"✅ 图节点体积数据已更新")
|
||||
|
||||
action_sequence.append(create_action_log(f"容器体积已更新 (+{volume:.2f}mL)", "📊"))
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"❌ 生成泵协议时出错: {str(e)}")
|
||||
action_sequence.append(create_action_log(f"泵协议生成失败: {str(e)}", "❌"))
|
||||
@@ -439,18 +490,18 @@ def generate_adjust_ph_protocol(
|
||||
debug_print(f" 🧪 试剂: {reagent}")
|
||||
debug_print(f" 📏 体积: {volume:.2f}mL")
|
||||
debug_print(f" 📊 目标pH: {ph_value}")
|
||||
debug_print(f" 🥼 目标容器: {vessel}")
|
||||
debug_print(f" 🥼 目标容器: {vessel_id}")
|
||||
debug_print("=" * 60)
|
||||
|
||||
# 添加完成日志
|
||||
summary_msg = f"pH调节协议完成: {vessel} → pH {ph_value} (使用 {volume:.2f}mL {reagent})"
|
||||
summary_msg = f"pH调节协议完成: {vessel_id} → pH {ph_value} (使用 {volume:.2f}mL {reagent})"
|
||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
||||
|
||||
return action_sequence
|
||||
|
||||
def generate_adjust_ph_protocol_stepwise(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
ph_value: float,
|
||||
reagent: str,
|
||||
max_volume: float = 10.0,
|
||||
@@ -461,7 +512,7 @@ def generate_adjust_ph_protocol_stepwise(
|
||||
|
||||
Args:
|
||||
G: 网络图
|
||||
vessel: 目标容器
|
||||
vessel: 目标容器字典
|
||||
ph_value: 目标pH值
|
||||
reagent: 酸碱试剂
|
||||
max_volume: 最大试剂体积
|
||||
@@ -470,10 +521,13 @@ def generate_adjust_ph_protocol_stepwise(
|
||||
Returns:
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print(f"🔄 开始分步pH调节")
|
||||
debug_print(f"📋 分步参数:")
|
||||
debug_print(f" 🥼 vessel: {vessel}")
|
||||
debug_print(f" 🥼 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" 📊 ph_value: {ph_value}")
|
||||
debug_print(f" 🧪 reagent: {reagent}")
|
||||
debug_print(f" 📏 max_volume: {max_volume}mL")
|
||||
@@ -496,7 +550,7 @@ def generate_adjust_ph_protocol_stepwise(
|
||||
# 生成单步协议
|
||||
step_actions = generate_adjust_ph_protocol(
|
||||
G=G,
|
||||
vessel=vessel,
|
||||
vessel=vessel, # 🔧 直接传递vessel字典
|
||||
ph_value=ph_value,
|
||||
reagent=reagent,
|
||||
volume=step_volume,
|
||||
@@ -530,35 +584,38 @@ def generate_adjust_ph_protocol_stepwise(
|
||||
# 便捷函数:常用pH调节
|
||||
def generate_acidify_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
target_ph: float = 2.0,
|
||||
acid: str = "hydrochloric acid"
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""酸化协议"""
|
||||
debug_print(f"🍋 生成酸化协议: {vessel} → pH {target_ph} (使用 {acid})")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🍋 生成酸化协议: {vessel_id} → pH {target_ph} (使用 {acid})")
|
||||
return generate_adjust_ph_protocol(
|
||||
G, vessel, target_ph, acid
|
||||
)
|
||||
|
||||
def generate_basify_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
target_ph: float = 12.0,
|
||||
base: str = "sodium hydroxide"
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""碱化协议"""
|
||||
debug_print(f"🧂 生成碱化协议: {vessel} → pH {target_ph} (使用 {base})")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🧂 生成碱化协议: {vessel_id} → pH {target_ph} (使用 {base})")
|
||||
return generate_adjust_ph_protocol(
|
||||
G, vessel, target_ph, base
|
||||
)
|
||||
|
||||
def generate_neutralize_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
reagent: str = "sodium hydroxide"
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""中和协议(pH=7)"""
|
||||
debug_print(f"⚖️ 生成中和协议: {vessel} → pH 7.0 (使用 {reagent})")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"⚖️ 生成中和协议: {vessel_id} → pH 7.0 (使用 {reagent})")
|
||||
return generate_adjust_ph_protocol(
|
||||
G, vessel, 7.0, reagent
|
||||
)
|
||||
|
||||
@@ -145,7 +145,7 @@ def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
|
||||
|
||||
def generate_clean_vessel_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
solvent: str,
|
||||
volume: float,
|
||||
temp: float,
|
||||
@@ -165,7 +165,7 @@ def generate_clean_vessel_protocol(
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为设备和容器,边为流体管道
|
||||
vessel: 要清洗的容器名称
|
||||
vessel: 要清洗的容器字典(包含id字段)
|
||||
solvent: 用于清洗的溶剂名称
|
||||
volume: 每次清洗使用的溶剂体积
|
||||
temp: 清洗时的温度
|
||||
@@ -178,20 +178,23 @@ def generate_clean_vessel_protocol(
|
||||
ValueError: 当找不到必要的容器或设备时抛出异常
|
||||
|
||||
Examples:
|
||||
clean_protocol = generate_clean_vessel_protocol(G, "main_reactor", "water", 100.0, 60.0, 2)
|
||||
clean_protocol = generate_clean_vessel_protocol(G, {"id": "main_reactor"}, "water", 100.0, 60.0, 2)
|
||||
"""
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
action_sequence = []
|
||||
|
||||
print(f"CLEAN_VESSEL: 开始生成容器清洗协议")
|
||||
print(f" - 目标容器: {vessel}")
|
||||
print(f" - 目标容器: {vessel} (ID: {vessel_id})")
|
||||
print(f" - 清洗溶剂: {solvent}")
|
||||
print(f" - 清洗体积: {volume} mL")
|
||||
print(f" - 清洗温度: {temp}°C")
|
||||
print(f" - 重复次数: {repeats}")
|
||||
|
||||
# 验证目标容器存在
|
||||
if vessel not in G.nodes():
|
||||
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
|
||||
if vessel_id not in G.nodes():
|
||||
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
||||
|
||||
# 查找溶剂容器
|
||||
try:
|
||||
@@ -208,12 +211,23 @@ def generate_clean_vessel_protocol(
|
||||
raise ValueError(f"无法找到废液容器: {str(e)}")
|
||||
|
||||
# 查找加热设备(可选)
|
||||
heatchill_id = find_connected_heatchill(G, vessel)
|
||||
heatchill_id = find_connected_heatchill(G, vessel_id) # 🔧 使用 vessel_id
|
||||
if heatchill_id:
|
||||
print(f"CLEAN_VESSEL: 找到加热设备: {heatchill_id}")
|
||||
else:
|
||||
print(f"CLEAN_VESSEL: 未找到加热设备,将在室温下清洗")
|
||||
|
||||
# 🔧 新增:记录清洗前的容器状态
|
||||
print(f"CLEAN_VESSEL: 记录清洗前容器状态...")
|
||||
original_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
print(f"CLEAN_VESSEL: 清洗前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# 第一步:如果需要加热且有加热设备,启动加热
|
||||
if temp > 25.0 and heatchill_id:
|
||||
print(f"CLEAN_VESSEL: 启动加热至 {temp}°C")
|
||||
@@ -221,7 +235,7 @@ def generate_clean_vessel_protocol(
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill_start",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": temp,
|
||||
"purpose": f"cleaning with {solvent}"
|
||||
}
|
||||
@@ -240,18 +254,61 @@ def generate_clean_vessel_protocol(
|
||||
print(f"CLEAN_VESSEL: 执行第 {repeat + 1} 次清洗")
|
||||
|
||||
# 2a. 使用 pump_protocol 将溶剂转移到目标容器
|
||||
print(f"CLEAN_VESSEL: 将 {volume} mL {solvent} 转移到 {vessel}")
|
||||
print(f"CLEAN_VESSEL: 将 {volume} mL {solvent} 转移到 {vessel_id}")
|
||||
try:
|
||||
# 调用成熟的 pump_protocol 算法
|
||||
add_solvent_actions = generate_pump_protocol(
|
||||
G=G,
|
||||
from_vessel=solvent_vessel,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
volume=volume,
|
||||
flowrate=2.5, # 适中的流速,避免飞溅
|
||||
transfer_flowrate=2.5
|
||||
)
|
||||
action_sequence.extend(add_solvent_actions)
|
||||
|
||||
# 🔧 新增:更新容器体积(添加清洗溶剂)
|
||||
print(f"CLEAN_VESSEL: 更新容器体积 - 添加清洗溶剂 {volume:.2f}mL")
|
||||
if "data" not in vessel:
|
||||
vessel["data"] = {}
|
||||
|
||||
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] += volume
|
||||
print(f"CLEAN_VESSEL: 添加溶剂后体积: {vessel['data']['liquid_volume'][0]:.2f}mL (+{volume:.2f}mL)")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [volume]
|
||||
print(f"CLEAN_VESSEL: 初始化清洗体积: {volume:.2f}mL")
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
vessel["data"]["liquid_volume"] += volume
|
||||
print(f"CLEAN_VESSEL: 添加溶剂后体积: {vessel['data']['liquid_volume']:.2f}mL (+{volume:.2f}mL)")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = volume
|
||||
print(f"CLEAN_VESSEL: 重置体积为: {volume:.2f}mL")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = volume
|
||||
print(f"CLEAN_VESSEL: 创建新体积记录: {volume:.2f}mL")
|
||||
|
||||
# 🔧 同时更新图中的容器数据
|
||||
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] += volume
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = [volume]
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = current_node_volume + volume
|
||||
|
||||
print(f"CLEAN_VESSEL: 图节点体积数据已更新")
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"无法将溶剂转移到容器: {str(e)}")
|
||||
|
||||
@@ -265,18 +322,52 @@ def generate_clean_vessel_protocol(
|
||||
action_sequence.append(wait_action)
|
||||
|
||||
# 2c. 使用 pump_protocol 将清洗液转移到废液容器
|
||||
print(f"CLEAN_VESSEL: 将清洗液从 {vessel} 转移到废液容器")
|
||||
print(f"CLEAN_VESSEL: 将清洗液从 {vessel_id} 转移到废液容器")
|
||||
try:
|
||||
# 调用成熟的 pump_protocol 算法
|
||||
remove_waste_actions = generate_pump_protocol(
|
||||
G=G,
|
||||
from_vessel=vessel,
|
||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
to_vessel=waste_vessel,
|
||||
volume=volume,
|
||||
flowrate=2.5, # 适中的流速
|
||||
transfer_flowrate=2.5
|
||||
)
|
||||
action_sequence.extend(remove_waste_actions)
|
||||
|
||||
# 🔧 新增:更新容器体积(移除清洗液)
|
||||
print(f"CLEAN_VESSEL: 更新容器体积 - 移除清洗液 {volume:.2f}mL")
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
vessel["data"]["liquid_volume"][0] = max(0.0, vessel["data"]["liquid_volume"][0] - volume)
|
||||
print(f"CLEAN_VESSEL: 移除清洗液后体积: {vessel['data']['liquid_volume'][0]:.2f}mL (-{volume:.2f}mL)")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [0.0]
|
||||
print(f"CLEAN_VESSEL: 重置体积为0mL")
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
vessel["data"]["liquid_volume"] = max(0.0, current_volume - volume)
|
||||
print(f"CLEAN_VESSEL: 移除清洗液后体积: {vessel['data']['liquid_volume']:.2f}mL (-{volume:.2f}mL)")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = 0.0
|
||||
print(f"CLEAN_VESSEL: 重置体积为0mL")
|
||||
|
||||
# 🔧 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
vessel_node_data = G.nodes[vessel_id].get('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] = max(0.0, current_node_volume[0] - volume)
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = [0.0]
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = max(0.0, current_node_volume - volume)
|
||||
|
||||
print(f"CLEAN_VESSEL: 图节点体积数据已更新")
|
||||
|
||||
except Exception as e:
|
||||
raise ValueError(f"无法将清洗液转移到废液容器: {str(e)}")
|
||||
|
||||
@@ -296,13 +387,24 @@ def generate_clean_vessel_protocol(
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill_stop",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel
|
||||
"vessel": vessel_id # 🔧 使用 vessel_id
|
||||
}
|
||||
}
|
||||
action_sequence.append(heatchill_stop_action)
|
||||
|
||||
print(f"CLEAN_VESSEL: 生成了 {len(action_sequence)} 个动作")
|
||||
print(f"CLEAN_VESSEL: 清洗协议生成完成")
|
||||
# 🔧 新增:清洗完成后的状态报告
|
||||
final_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
final_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
final_liquid_volume = current_volume
|
||||
|
||||
print(f"CLEAN_VESSEL: 清洗完成")
|
||||
print(f" - 清洗前体积: {original_liquid_volume:.2f}mL")
|
||||
print(f" - 清洗后体积: {final_liquid_volume:.2f}mL")
|
||||
print(f" - 生成了 {len(action_sequence)} 个动作")
|
||||
|
||||
return action_sequence
|
||||
|
||||
@@ -310,7 +412,7 @@ def generate_clean_vessel_protocol(
|
||||
# 便捷函数:常用清洗方案
|
||||
def generate_quick_clean_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
solvent: str = "water",
|
||||
volume: float = 100.0
|
||||
) -> List[Dict[str, Any]]:
|
||||
@@ -320,7 +422,7 @@ def generate_quick_clean_protocol(
|
||||
|
||||
def generate_thorough_clean_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
solvent: str = "water",
|
||||
volume: float = 150.0,
|
||||
temp: float = 60.0
|
||||
@@ -331,7 +433,7 @@ def generate_thorough_clean_protocol(
|
||||
|
||||
def generate_organic_clean_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
volume: float = 100.0
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""有机清洗:先用有机溶剂,再用水清洗"""
|
||||
|
||||
@@ -408,7 +408,7 @@ def find_solid_dispenser(G: nx.DiGraph) -> str:
|
||||
|
||||
def generate_dissolve_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
# 🔧 修复:按照checklist.md的DissolveProtocol参数
|
||||
solvent: str = "",
|
||||
volume: Union[str, float] = 0.0,
|
||||
@@ -427,10 +427,11 @@ def generate_dissolve_protocol(
|
||||
生成溶解操作的协议序列 - 增强版
|
||||
|
||||
🔧 修复要点:
|
||||
1. 添加action文件中的所有参数(mass, mol, reagent, event)
|
||||
2. 使用 **kwargs 接受所有额外参数,防止 unexpected keyword argument 错误
|
||||
3. 支持固体溶解和液体溶解两种模式
|
||||
4. 添加详细的emoji日志系统
|
||||
1. 修改vessel参数类型为dict,并提取vessel_id
|
||||
2. 添加action文件中的所有参数(mass, mol, reagent, event)
|
||||
3. 使用 **kwargs 接受所有额外参数,防止 unexpected keyword argument 错误
|
||||
4. 支持固体溶解和液体溶解两种模式
|
||||
5. 添加详细的体积运算逻辑
|
||||
|
||||
支持两种溶解模式:
|
||||
1. 液体溶解:指定 solvent + volume,使用pump protocol转移溶剂
|
||||
@@ -444,10 +445,13 @@ def generate_dissolve_protocol(
|
||||
- mol: "0.12 mol", "16.2 mmol"
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("🧪 开始生成溶解协议")
|
||||
debug_print(f"📋 原始参数:")
|
||||
debug_print(f" 🥼 vessel: '{vessel}'")
|
||||
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)})")
|
||||
@@ -463,19 +467,30 @@ def generate_dissolve_protocol(
|
||||
|
||||
# === 参数验证 ===
|
||||
debug_print("🔍 步骤1: 参数验证...")
|
||||
action_sequence.append(create_action_log(f"开始溶解操作 - 容器: {vessel}", "🎬"))
|
||||
action_sequence.append(create_action_log(f"开始溶解操作 - 容器: {vessel_id}", "🎬"))
|
||||
|
||||
if not vessel:
|
||||
if not vessel_id:
|
||||
debug_print("❌ vessel 参数不能为空")
|
||||
raise ValueError("vessel 参数不能为空")
|
||||
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中")
|
||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||
if vessel_id not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel_id}' 不存在于系统中")
|
||||
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
||||
|
||||
debug_print("✅ 基本参数验证通过")
|
||||
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
||||
|
||||
# 🔧 新增:记录溶解前的容器状态
|
||||
debug_print("🔍 记录溶解前容器状态...")
|
||||
original_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
debug_print(f"📊 溶解前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# === 🔧 关键修复:参数解析 ===
|
||||
debug_print("🔍 步骤2: 参数解析...")
|
||||
action_sequence.append(create_action_log("正在解析溶解参数...", "🔍"))
|
||||
@@ -522,8 +537,8 @@ def generate_dissolve_protocol(
|
||||
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
||||
|
||||
# 查找加热搅拌器
|
||||
heatchill_id = find_connected_heatchill(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
heatchill_id = find_connected_heatchill(G, vessel_id)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||
|
||||
# 优先使用加热搅拌器,否则使用独立搅拌器
|
||||
stir_device_id = heatchill_id or stirrer_id
|
||||
@@ -557,7 +572,7 @@ def generate_dissolve_protocol(
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill_start",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id,
|
||||
"temp": final_temp,
|
||||
"purpose": f"溶解准备 - {event}" if event else "溶解准备"
|
||||
}
|
||||
@@ -581,7 +596,7 @@ def generate_dissolve_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id,
|
||||
"stir_speed": stir_speed,
|
||||
"purpose": f"溶解搅拌 - {event}" if event else "溶解搅拌"
|
||||
}
|
||||
@@ -606,7 +621,7 @@ def generate_dissolve_protocol(
|
||||
|
||||
# 固体加样
|
||||
add_kwargs = {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id,
|
||||
"reagent": reagent or amount or "solid reagent",
|
||||
"purpose": f"溶解固体试剂 - {event}" if event else "溶解固体试剂",
|
||||
"event": event
|
||||
@@ -628,6 +643,12 @@ def generate_dissolve_protocol(
|
||||
|
||||
debug_print(f"✅ 固体加样完成")
|
||||
action_sequence.append(create_action_log("固体加样完成", "✅"))
|
||||
|
||||
# 🔧 新增:固体溶解体积运算 - 固体本身不会显著增加体积,但可能有少量变化
|
||||
debug_print(f"🔧 固体溶解 - 体积变化很小,主要是质量变化")
|
||||
# 固体通常不会显著改变液体体积,这里只记录日志
|
||||
action_sequence.append(create_action_log(f"固体已添加: {final_mass}g", "📊"))
|
||||
|
||||
else:
|
||||
debug_print("⚠️ 未找到固体加样器,跳过固体添加")
|
||||
action_sequence.append(create_action_log("未找到固体加样器,无法添加固体", "❌"))
|
||||
@@ -659,7 +680,7 @@ def generate_dissolve_protocol(
|
||||
pump_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent_vessel,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id,
|
||||
volume=final_volume,
|
||||
amount=amount,
|
||||
time=0.0, # 不在pump level控制时间
|
||||
@@ -679,6 +700,52 @@ def generate_dissolve_protocol(
|
||||
debug_print(f"✅ 溶剂转移完成,添加了 {len(pump_actions)} 个动作")
|
||||
action_sequence.append(create_action_log(f"溶剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
||||
|
||||
# 🔧 新增:液体溶解体积运算 - 添加溶剂后更新容器体积
|
||||
debug_print(f"🔧 更新容器液体体积 - 添加溶剂 {final_volume:.2f}mL")
|
||||
|
||||
# 确保vessel有data字段
|
||||
if "data" not in vessel:
|
||||
vessel["data"] = {}
|
||||
|
||||
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] += final_volume
|
||||
debug_print(f"📊 添加溶剂后体积: {vessel['data']['liquid_volume'][0]:.2f}mL (+{final_volume:.2f}mL)")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [final_volume]
|
||||
debug_print(f"📊 初始化溶解体积: {final_volume:.2f}mL")
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
vessel["data"]["liquid_volume"] += final_volume
|
||||
debug_print(f"📊 添加溶剂后体积: {vessel['data']['liquid_volume']:.2f}mL (+{final_volume:.2f}mL)")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = final_volume
|
||||
debug_print(f"📊 重置体积为: {final_volume:.2f}mL")
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = final_volume
|
||||
debug_print(f"📊 创建新体积记录: {final_volume:.2f}mL")
|
||||
|
||||
# 🔧 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
if 'data' not in G.nodes[vessel_id]:
|
||||
G.nodes[vessel_id]['data'] = {}
|
||||
|
||||
vessel_node_data = G.nodes[vessel_id]['data']
|
||||
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
|
||||
|
||||
if isinstance(current_node_volume, list):
|
||||
if len(current_node_volume) > 0:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'][0] += final_volume
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = [final_volume]
|
||||
else:
|
||||
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("溶剂添加后短暂等待...", "⏳"))
|
||||
action_sequence.append({
|
||||
@@ -700,7 +767,7 @@ def generate_dissolve_protocol(
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id,
|
||||
"temp": final_temp,
|
||||
"time": final_time,
|
||||
"stir": True,
|
||||
@@ -718,7 +785,7 @@ def generate_dissolve_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id,
|
||||
"stir_time": final_time,
|
||||
"stir_speed": stir_speed,
|
||||
"settling_time": 0,
|
||||
@@ -744,7 +811,7 @@ def generate_dissolve_protocol(
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill_stop",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel
|
||||
"vessel": vessel_id
|
||||
}
|
||||
}
|
||||
action_sequence.append(stop_action)
|
||||
@@ -761,12 +828,21 @@ def generate_dissolve_protocol(
|
||||
}
|
||||
})
|
||||
|
||||
# 🔧 新增:溶解完成后的状态报告
|
||||
final_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
final_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
final_liquid_volume = current_volume
|
||||
|
||||
# === 最终结果 ===
|
||||
debug_print("=" * 60)
|
||||
debug_print(f"🎉 溶解协议生成完成")
|
||||
debug_print(f"📊 协议统计:")
|
||||
debug_print(f" 📋 总动作数: {len(action_sequence)}")
|
||||
debug_print(f" 🥼 容器: {vessel}")
|
||||
debug_print(f" 🥼 容器: {vessel_id}")
|
||||
debug_print(f" {dissolve_emoji} 溶解类型: {dissolve_type}")
|
||||
if is_liquid_dissolve:
|
||||
debug_print(f" 💧 溶剂: {solvent} ({final_volume}mL)")
|
||||
@@ -776,10 +852,12 @@ def generate_dissolve_protocol(
|
||||
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}"
|
||||
summary_msg = f"溶解协议完成: {vessel_id}"
|
||||
if is_liquid_dissolve:
|
||||
summary_msg += f" (使用 {final_volume}mL {solvent})"
|
||||
if is_solid_dissolve:
|
||||
@@ -789,12 +867,15 @@ def generate_dissolve_protocol(
|
||||
|
||||
return action_sequence
|
||||
|
||||
# === 便捷函数 ===
|
||||
|
||||
def dissolve_solid_by_mass(G: nx.DiGraph, vessel: str, reagent: str, mass: Union[str, float],
|
||||
# === 便捷函数 ===
|
||||
# 🔧 修改便捷函数的参数类型
|
||||
|
||||
def dissolve_solid_by_mass(G: nx.DiGraph, vessel: dict, 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}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🧂 快速固体溶解: {reagent} ({mass}) → {vessel_id}")
|
||||
return generate_dissolve_protocol(
|
||||
G, vessel,
|
||||
mass=mass,
|
||||
@@ -803,10 +884,11 @@ def dissolve_solid_by_mass(G: nx.DiGraph, vessel: str, reagent: str, mass: Union
|
||||
time=time
|
||||
)
|
||||
|
||||
def dissolve_solid_by_moles(G: nx.DiGraph, vessel: str, reagent: str, mol: str,
|
||||
def dissolve_solid_by_moles(G: nx.DiGraph, vessel: dict, 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}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🧬 按摩尔数溶解固体: {reagent} ({mol}) → {vessel_id}")
|
||||
return generate_dissolve_protocol(
|
||||
G, vessel,
|
||||
mol=mol,
|
||||
@@ -815,10 +897,11 @@ def dissolve_solid_by_moles(G: nx.DiGraph, vessel: str, reagent: str, mol: str,
|
||||
time=time
|
||||
)
|
||||
|
||||
def dissolve_with_solvent(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float],
|
||||
def dissolve_with_solvent(G: nx.DiGraph, vessel: dict, 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}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"💧 溶剂溶解: {solvent} ({volume}) → {vessel_id}")
|
||||
return generate_dissolve_protocol(
|
||||
G, vessel,
|
||||
solvent=solvent,
|
||||
@@ -827,9 +910,10 @@ def dissolve_with_solvent(G: nx.DiGraph, vessel: str, solvent: str, volume: Unio
|
||||
time=time
|
||||
)
|
||||
|
||||
def dissolve_at_room_temp(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float]) -> List[Dict[str, Any]]:
|
||||
def dissolve_at_room_temp(G: nx.DiGraph, vessel: dict, solvent: str, volume: Union[str, float]) -> List[Dict[str, Any]]:
|
||||
"""室温溶解"""
|
||||
debug_print(f"🌡️ 室温溶解: {solvent} ({volume}) → {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🌡️ 室温溶解: {solvent} ({volume}) → {vessel_id}")
|
||||
return generate_dissolve_protocol(
|
||||
G, vessel,
|
||||
solvent=solvent,
|
||||
@@ -838,10 +922,11 @@ def dissolve_at_room_temp(G: nx.DiGraph, vessel: str, solvent: str, volume: Unio
|
||||
time="5 min"
|
||||
)
|
||||
|
||||
def dissolve_with_heating(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float],
|
||||
def dissolve_with_heating(G: nx.DiGraph, vessel: dict, 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}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🔥 加热溶解: {solvent} ({volume}) → {vessel_id} @ {temp}")
|
||||
return generate_dissolve_protocol(
|
||||
G, vessel,
|
||||
solvent=solvent,
|
||||
|
||||
@@ -46,8 +46,8 @@ def find_connected_heater(G: nx.DiGraph, vessel: str) -> str:
|
||||
|
||||
def generate_dry_protocol(
|
||||
G: nx.DiGraph,
|
||||
compound: str,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
compound: str = "", # 🔧 修改:参数顺序调整,并设置默认值
|
||||
**kwargs # 接收其他可能的参数但不使用
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
@@ -55,13 +55,16 @@ def generate_dry_protocol(
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为容器和设备
|
||||
compound: 化合物名称(从XDL传入)
|
||||
vessel: 目标容器(从XDL传入)
|
||||
vessel: 目标容器字典(从XDL传入)
|
||||
compound: 化合物名称(从XDL传入,可选)
|
||||
**kwargs: 其他可选参数,但不使用
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
action_sequence = []
|
||||
|
||||
# 默认参数
|
||||
@@ -70,33 +73,83 @@ def generate_dry_protocol(
|
||||
simulation_time = 60.0 # 模拟时间 1分钟
|
||||
|
||||
print(f"🌡️ DRY: 开始生成干燥协议 ✨")
|
||||
print(f" 🧪 化合物: {compound}")
|
||||
print(f" 🥽 容器: {vessel}")
|
||||
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
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
print(f"📊 干燥前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# 1. 验证目标容器存在
|
||||
print(f"\n📋 步骤1: 验证目标容器 '{vessel}' 是否存在...")
|
||||
if vessel not in G.nodes():
|
||||
print(f"⚠️ DRY: 警告 - 容器 '{vessel}' 不存在于系统中,跳过干燥 😢")
|
||||
print(f"\n📋 步骤1: 验证目标容器 '{vessel_id}' 是否存在...")
|
||||
if vessel_id not in G.nodes():
|
||||
print(f"⚠️ DRY: 警告 - 容器 '{vessel_id}' 不存在于系统中,跳过干燥 😢")
|
||||
return action_sequence
|
||||
print(f"✅ 容器 '{vessel}' 验证通过!")
|
||||
print(f"✅ 容器 '{vessel_id}' 验证通过!")
|
||||
|
||||
# 2. 查找相连的加热器
|
||||
print(f"\n🔍 步骤2: 查找与容器相连的加热器...")
|
||||
heater_id = find_connected_heater(G, vessel)
|
||||
heater_id = find_connected_heater(G, vessel_id) # 🔧 使用 vessel_id
|
||||
|
||||
if heater_id is None:
|
||||
print(f"😭 DRY: 警告 - 未找到与容器 '{vessel}' 相连的加热器,跳过干燥")
|
||||
print(f"😭 DRY: 警告 - 未找到与容器 '{vessel_id}' 相连的加热器,跳过干燥")
|
||||
print(f"🎭 添加模拟干燥动作...")
|
||||
# 添加一个等待动作,表示干燥过程(模拟)
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
"time": 10.0, # 模拟等待时间
|
||||
"description": f"模拟干燥 {compound} (无加热器可用)"
|
||||
"description": f"模拟干燥 {compound or '化合物'} (无加热器可用)"
|
||||
}
|
||||
})
|
||||
|
||||
# 🔧 新增:模拟干燥的体积变化(溶剂蒸发)
|
||||
print(f"🔧 模拟干燥过程的体积减少...")
|
||||
if original_liquid_volume > 0:
|
||||
# 假设干燥过程中损失10%的体积(溶剂蒸发)
|
||||
volume_loss = original_liquid_volume * 0.1
|
||||
new_volume = max(0.0, original_liquid_volume - volume_loss)
|
||||
|
||||
# 更新vessel字典中的体积
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
vessel["data"]["liquid_volume"][0] = new_volume
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [new_volume]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
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
|
||||
|
||||
print(f"📊 模拟干燥体积变化: {original_liquid_volume:.2f}mL → {new_volume:.2f}mL (-{volume_loss:.2f}mL)")
|
||||
|
||||
print(f"📄 DRY: 协议生成完成,共 {len(action_sequence)} 个动作 🎯")
|
||||
return action_sequence
|
||||
|
||||
@@ -112,9 +165,9 @@ def generate_dry_protocol(
|
||||
"device_id": heater_id,
|
||||
"action_name": "heat_chill_start",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": dry_temp,
|
||||
"purpose": f"干燥 {compound}"
|
||||
"purpose": f"干燥 {compound or '化合物'}"
|
||||
}
|
||||
})
|
||||
print(f" ✅ 加热器启动命令已添加 🔥")
|
||||
@@ -136,21 +189,67 @@ def generate_dry_protocol(
|
||||
"device_id": heater_id,
|
||||
"action_name": "heat_chill",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": dry_temp,
|
||||
"time": simulation_time,
|
||||
"purpose": f"干燥 {compound},保持温度 {dry_temp}°C"
|
||||
"purpose": f"干燥 {compound or '化合物'},保持温度 {dry_temp}°C"
|
||||
}
|
||||
})
|
||||
print(f" ✅ 温度保持命令已添加 🌡️⏰")
|
||||
|
||||
# 🔧 新增:干燥过程中的体积变化计算
|
||||
print(f"🔧 计算干燥过程中的体积变化...")
|
||||
if original_liquid_volume > 0:
|
||||
# 干燥过程中,溶剂会蒸发,固体保留
|
||||
# 根据温度和时间估算蒸发量
|
||||
evaporation_rate = 0.001 * dry_temp # 每秒每°C蒸发0.001mL
|
||||
total_evaporation = min(original_liquid_volume * 0.8,
|
||||
evaporation_rate * simulation_time) # 最多蒸发80%
|
||||
|
||||
new_volume = max(0.0, original_liquid_volume - total_evaporation)
|
||||
|
||||
# 更新vessel字典中的体积
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
vessel["data"]["liquid_volume"][0] = new_volume
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [new_volume]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
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
|
||||
|
||||
print(f"📊 干燥体积变化计算:")
|
||||
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 停止加热
|
||||
print(f" ⏹️ 动作4: 停止加热...")
|
||||
action_sequence.append({
|
||||
"device_id": heater_id,
|
||||
"action_name": "heat_chill_stop",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"purpose": f"干燥完成,停止加热"
|
||||
}
|
||||
})
|
||||
@@ -162,18 +261,67 @@ def generate_dry_protocol(
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
"time": 10.0, # 等待10秒冷却
|
||||
"description": f"等待 {compound} 冷却"
|
||||
"description": f"等待 {compound or '化合物'} 冷却"
|
||||
}
|
||||
})
|
||||
print(f" ✅ 冷却等待命令已添加 🧊")
|
||||
|
||||
# 🔧 新增:干燥完成后的状态报告
|
||||
final_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
final_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
final_liquid_volume = current_volume
|
||||
|
||||
print(f"\n🎊 DRY: 协议生成完成,共 {len(action_sequence)} 个动作 🎯")
|
||||
print(f"⏱️ DRY: 预计总时间: {(dry_time + 360)/60:.0f} 分钟 ⌛")
|
||||
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
|
||||
|
||||
|
||||
# 🔧 新增:便捷函数
|
||||
def generate_quick_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
||||
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)
|
||||
|
||||
|
||||
def generate_thorough_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
||||
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)
|
||||
|
||||
|
||||
def generate_gentle_dry_protocol(G: nx.DiGraph, vessel: dict, compound: str = "",
|
||||
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)
|
||||
|
||||
|
||||
# 测试函数
|
||||
def test_dry_protocol():
|
||||
"""测试干燥协议"""
|
||||
|
||||
@@ -270,7 +270,7 @@ def find_gas_solenoid_valve(G: nx.DiGraph, gas_source: str) -> Optional[str]:
|
||||
|
||||
def generate_evacuateandrefill_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
gas: str,
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
@@ -279,7 +279,7 @@ def generate_evacuateandrefill_protocol(
|
||||
|
||||
Args:
|
||||
G: 设备图
|
||||
vessel: 目标容器名称(必需)
|
||||
vessel: 目标容器字典(必需)
|
||||
gas: 气体名称(必需)
|
||||
**kwargs: 其他参数(兼容性)
|
||||
|
||||
@@ -287,6 +287,9 @@ def generate_evacuateandrefill_protocol(
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
# 硬编码重复次数为 3
|
||||
repeats = 3
|
||||
|
||||
@@ -297,7 +300,7 @@ def generate_evacuateandrefill_protocol(
|
||||
debug_print("=" * 60)
|
||||
debug_print("🧪 开始生成抽真空充气协议")
|
||||
debug_print(f"📋 原始参数:")
|
||||
debug_print(f" 🥼 容器: '{vessel}'")
|
||||
debug_print(f" 🥼 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" 💨 气体: '{gas}'")
|
||||
debug_print(f" 🔄 循环次数: {repeats} (硬编码)")
|
||||
debug_print(f" 📦 其他参数: {kwargs}")
|
||||
@@ -307,12 +310,12 @@ def generate_evacuateandrefill_protocol(
|
||||
|
||||
# === 参数验证和修正 ===
|
||||
debug_print("🔍 步骤1: 参数验证和修正...")
|
||||
action_sequence.append(create_action_log(f"开始抽真空充气操作 - 容器: {vessel}", "🎬"))
|
||||
action_sequence.append(create_action_log(f"开始抽真空充气操作 - 容器: {vessel_id}", "🎬"))
|
||||
action_sequence.append(create_action_log(f"目标气体: {gas}", "💨"))
|
||||
action_sequence.append(create_action_log(f"循环次数: {repeats}", "🔄"))
|
||||
|
||||
# 验证必需参数
|
||||
if not vessel:
|
||||
if not vessel_id:
|
||||
debug_print("❌ 容器参数不能为空")
|
||||
raise ValueError("容器参数不能为空")
|
||||
|
||||
@@ -320,9 +323,9 @@ def generate_evacuateandrefill_protocol(
|
||||
debug_print("❌ 气体参数不能为空")
|
||||
raise ValueError("气体参数不能为空")
|
||||
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel}' 在系统中不存在")
|
||||
raise ValueError(f"容器 '{vessel}' 在系统中不存在")
|
||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print(f"❌ 容器 '{vessel_id}' 在系统中不存在")
|
||||
raise ValueError(f"容器 '{vessel_id}' 在系统中不存在")
|
||||
|
||||
debug_print("✅ 基本参数验证通过")
|
||||
action_sequence.append(create_action_log("参数验证通过", "✅"))
|
||||
@@ -351,7 +354,7 @@ def generate_evacuateandrefill_protocol(
|
||||
debug_print(f"🔄 标准化气体名称: {original_gas} -> {gas}")
|
||||
action_sequence.append(create_action_log(f"气体名称标准化: {original_gas} -> {gas}", "🔄"))
|
||||
|
||||
debug_print(f"📋 最终参数: 容器={vessel}, 气体={gas}, 重复={repeats}")
|
||||
debug_print(f"📋 最终参数: 容器={vessel_id}, 气体={gas}, 重复={repeats}")
|
||||
|
||||
# === 查找设备 ===
|
||||
debug_print("🔍 步骤2: 查找设备...")
|
||||
@@ -376,7 +379,7 @@ def generate_evacuateandrefill_protocol(
|
||||
else:
|
||||
action_sequence.append(create_action_log("未找到气源电磁阀", "⚠️"))
|
||||
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
||||
if stirrer_id:
|
||||
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_id}", "🌪️"))
|
||||
else:
|
||||
@@ -444,8 +447,8 @@ def generate_evacuateandrefill_protocol(
|
||||
|
||||
try:
|
||||
# 验证抽真空路径
|
||||
if nx.has_path(G, vessel, vacuum_pump):
|
||||
vacuum_path = nx.shortest_path(G, source=vessel, target=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)
|
||||
debug_print(f"✅ 真空路径: {' -> '.join(vacuum_path)}")
|
||||
action_sequence.append(create_action_log(f"真空路径: {' -> '.join(vacuum_path)}", "🛤️"))
|
||||
else:
|
||||
@@ -453,8 +456,8 @@ def generate_evacuateandrefill_protocol(
|
||||
action_sequence.append(create_action_log("真空路径检查: 路径不存在", "⚠️"))
|
||||
|
||||
# 验证充气路径
|
||||
if nx.has_path(G, gas_source, vessel):
|
||||
gas_path = nx.shortest_path(G, source=gas_source, target=vessel)
|
||||
if nx.has_path(G, gas_source, vessel_id): # 🔧 使用 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)}", "🛤️"))
|
||||
else:
|
||||
@@ -476,7 +479,7 @@ def generate_evacuateandrefill_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"stir_speed": STIR_SPEED,
|
||||
"purpose": "抽真空充气前预搅拌"
|
||||
}
|
||||
@@ -524,13 +527,13 @@ def generate_evacuateandrefill_protocol(
|
||||
})
|
||||
|
||||
# 抽真空操作
|
||||
debug_print(f"🌪️ 抽真空操作: {vessel} -> {vacuum_pump}")
|
||||
action_sequence.append(create_action_log(f"开始抽真空: {vessel} -> {vacuum_pump}", "🌪️"))
|
||||
debug_print(f"🌪️ 抽真空操作: {vessel_id} -> {vacuum_pump}")
|
||||
action_sequence.append(create_action_log(f"开始抽真空: {vessel_id} -> {vacuum_pump}", "🌪️"))
|
||||
|
||||
try:
|
||||
vacuum_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=vessel,
|
||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
to_vessel=vacuum_pump,
|
||||
volume=VACUUM_VOLUME,
|
||||
amount="",
|
||||
@@ -622,14 +625,14 @@ def generate_evacuateandrefill_protocol(
|
||||
})
|
||||
|
||||
# 充气操作
|
||||
debug_print(f"💨 充气操作: {gas_source} -> {vessel}")
|
||||
action_sequence.append(create_action_log(f"开始气体充气: {gas_source} -> {vessel}", "💨"))
|
||||
debug_print(f"💨 充气操作: {gas_source} -> {vessel_id}")
|
||||
action_sequence.append(create_action_log(f"开始气体充气: {gas_source} -> {vessel_id}", "💨"))
|
||||
|
||||
try:
|
||||
gas_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=gas_source,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
volume=REFILL_VOLUME,
|
||||
amount="",
|
||||
time=0.0,
|
||||
@@ -709,7 +712,7 @@ def generate_evacuateandrefill_protocol(
|
||||
action_sequence.append({
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "stop_stir",
|
||||
"action_kwargs": {"vessel": vessel}
|
||||
"action_kwargs": {"vessel": vessel_id} # 🔧 使用 vessel_id
|
||||
})
|
||||
else:
|
||||
action_sequence.append(create_action_log("跳过搅拌器停止", "⏭️"))
|
||||
@@ -729,37 +732,41 @@ def generate_evacuateandrefill_protocol(
|
||||
debug_print(f"📊 协议统计:")
|
||||
debug_print(f" 📋 总动作数: {len(action_sequence)}")
|
||||
debug_print(f" ⏱️ 预计总时间: {total_time:.0f}s ({total_time/60:.1f} 分钟)")
|
||||
debug_print(f" 🥼 处理容器: {vessel}")
|
||||
debug_print(f" 🥼 处理容器: {vessel_id}")
|
||||
debug_print(f" 💨 使用气体: {gas}")
|
||||
debug_print(f" 🔄 重复次数: {repeats}")
|
||||
debug_print("=" * 60)
|
||||
|
||||
# 添加完成日志
|
||||
summary_msg = f"抽真空充气协议完成: {vessel} (使用 {gas},{repeats} 次循环)"
|
||||
summary_msg = f"抽真空充气协议完成: {vessel_id} (使用 {gas},{repeats} 次循环)"
|
||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
||||
|
||||
return action_sequence
|
||||
|
||||
# === 便捷函数 ===
|
||||
|
||||
def generate_nitrogen_purge_protocol(G: nx.DiGraph, vessel: str, **kwargs) -> List[Dict[str, Any]]:
|
||||
def generate_nitrogen_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
||||
"""生成氮气置换协议"""
|
||||
debug_print(f"💨 生成氮气置换协议: {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"💨 生成氮气置换协议: {vessel_id}")
|
||||
return generate_evacuateandrefill_protocol(G, vessel, "nitrogen", **kwargs)
|
||||
|
||||
def generate_argon_purge_protocol(G: nx.DiGraph, vessel: str, **kwargs) -> List[Dict[str, Any]]:
|
||||
def generate_argon_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
||||
"""生成氩气置换协议"""
|
||||
debug_print(f"💨 生成氩气置换协议: {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"💨 生成氩气置换协议: {vessel_id}")
|
||||
return generate_evacuateandrefill_protocol(G, vessel, "argon", **kwargs)
|
||||
|
||||
def generate_air_purge_protocol(G: nx.DiGraph, vessel: str, **kwargs) -> List[Dict[str, Any]]:
|
||||
def generate_air_purge_protocol(G: nx.DiGraph, vessel: dict, **kwargs) -> List[Dict[str, Any]]: # 🔧 修改参数类型
|
||||
"""生成空气置换协议"""
|
||||
debug_print(f"💨 生成空气置换协议: {vessel}")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"💨 生成空气置换协议: {vessel_id}")
|
||||
return generate_evacuateandrefill_protocol(G, vessel, "air", **kwargs)
|
||||
|
||||
def generate_inert_atmosphere_protocol(G: nx.DiGraph, vessel: str, 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]]: # 🔧 修改参数类型
|
||||
"""生成惰性气氛协议"""
|
||||
debug_print(f"🛡️ 生成惰性气氛协议: {vessel} (使用 {gas})")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🛡️ 生成惰性气氛协议: {vessel_id} (使用 {gas})")
|
||||
return generate_evacuateandrefill_protocol(G, vessel, gas, **kwargs)
|
||||
|
||||
# 测试函数
|
||||
|
||||
@@ -175,7 +175,7 @@ def find_connected_vessel(G: nx.DiGraph, rotavap_device: str) -> Optional[str]:
|
||||
|
||||
def generate_evaporate_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
pressure: float = 0.1,
|
||||
temp: float = 60.0,
|
||||
time: Union[str, float] = "180", # 🔧 修改:支持字符串时间
|
||||
@@ -184,11 +184,11 @@ def generate_evaporate_protocol(
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成蒸发操作的协议序列 - 支持单位
|
||||
生成蒸发操作的协议序列 - 支持单位和体积运算
|
||||
|
||||
Args:
|
||||
G: 设备图
|
||||
vessel: 容器名称或旋转蒸发仪名称
|
||||
vessel: 容器字典(从XDL传入)
|
||||
pressure: 真空度 (bar),默认0.1
|
||||
temp: 加热温度 (°C),默认60
|
||||
time: 蒸发时间(支持 "3 min", "180", "0.5 h" 等)
|
||||
@@ -200,10 +200,13 @@ def generate_evaporate_protocol(
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("🌟" * 20)
|
||||
debug_print("🌪️ 开始生成蒸发协议(支持单位)✨")
|
||||
debug_print("🌪️ 开始生成蒸发协议(支持单位和体积运算)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel}")
|
||||
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)})")
|
||||
@@ -211,16 +214,27 @@ def generate_evaporate_protocol(
|
||||
debug_print(f" 🧪 solvent: '{solvent}'")
|
||||
debug_print("🌟" * 20)
|
||||
|
||||
# 🔧 新增:记录蒸发前的容器状态
|
||||
debug_print("🔍 记录蒸发前容器状态...")
|
||||
original_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
debug_print(f"📊 蒸发前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# === 步骤1: 查找旋转蒸发仪设备 ===
|
||||
debug_print("📍 步骤1: 查找旋转蒸发仪设备... 🔍")
|
||||
|
||||
# 验证vessel参数
|
||||
if not vessel:
|
||||
if not vessel_id:
|
||||
debug_print("❌ vessel 参数不能为空! 😱")
|
||||
raise ValueError("vessel 参数不能为空")
|
||||
|
||||
# 查找旋转蒸发仪设备
|
||||
rotavap_device = find_rotavap_device(G, vessel)
|
||||
rotavap_device = find_rotavap_device(G, vessel_id)
|
||||
if not rotavap_device:
|
||||
debug_print("💥 未找到旋转蒸发仪设备! 😭")
|
||||
raise ValueError(f"未找到旋转蒸发仪设备。请检查组态图中是否包含 class 包含 'rotavap'、'rotary' 或 'evaporat' 的设备")
|
||||
@@ -230,10 +244,10 @@ def generate_evaporate_protocol(
|
||||
# === 步骤2: 确定目标容器 ===
|
||||
debug_print("📍 步骤2: 确定目标容器... 🥽")
|
||||
|
||||
target_vessel = vessel
|
||||
target_vessel = vessel_id
|
||||
|
||||
# 如果vessel就是旋转蒸发仪设备,查找连接的容器
|
||||
if vessel == rotavap_device:
|
||||
if vessel_id == rotavap_device:
|
||||
debug_print("🔄 vessel就是旋转蒸发仪,查找连接的容器...")
|
||||
connected_vessel = find_connected_vessel(G, rotavap_device)
|
||||
if connected_vessel:
|
||||
@@ -242,11 +256,11 @@ def generate_evaporate_protocol(
|
||||
else:
|
||||
debug_print(f"⚠️ 未找到连接的容器,使用设备本身: {rotavap_device} 🔧")
|
||||
target_vessel = rotavap_device
|
||||
elif vessel in G.nodes() and G.nodes[vessel].get('type') == 'container':
|
||||
debug_print(f"✅ 使用指定的容器: {vessel} 🥽✨")
|
||||
target_vessel = vessel
|
||||
elif vessel_id in G.nodes() and G.nodes[vessel_id].get('type') == 'container':
|
||||
debug_print(f"✅ 使用指定的容器: {vessel_id} 🥽✨")
|
||||
target_vessel = vessel_id
|
||||
else:
|
||||
debug_print(f"⚠️ 容器 '{vessel}' 不存在或类型不正确,使用旋转蒸发仪设备: {rotavap_device} 🔧")
|
||||
debug_print(f"⚠️ 容器 '{vessel_id}' 不存在或类型不正确,使用旋转蒸发仪设备: {rotavap_device} 🔧")
|
||||
target_vessel = rotavap_device
|
||||
|
||||
# === 🔧 新增:步骤3:单位解析处理 ===
|
||||
@@ -308,8 +322,49 @@ def generate_evaporate_protocol(
|
||||
|
||||
debug_print(f"🎯 最终参数: pressure={pressure} bar 💨, temp={temp}°C 🌡️, time={final_time}s ⏰, stir_speed={stir_speed} RPM 🌪️")
|
||||
|
||||
# === 步骤5: 生成动作序列 ===
|
||||
debug_print("📍 步骤5: 生成动作序列... 🎬")
|
||||
# === 🔧 新增:步骤5:蒸发体积计算 ===
|
||||
debug_print("📍 步骤5: 蒸发体积计算... 📊")
|
||||
|
||||
# 根据温度、真空度、时间和溶剂类型估算蒸发量
|
||||
evaporation_volume = 0.0
|
||||
if original_liquid_volume > 0:
|
||||
# 基础蒸发速率(mL/min)
|
||||
base_evap_rate = 0.5 # 基础速率
|
||||
|
||||
# 温度系数(高温蒸发更快)
|
||||
temp_factor = 1.0 + (temp - 25.0) / 100.0
|
||||
|
||||
# 真空系数(真空度越高蒸发越快)
|
||||
vacuum_factor = 1.0 + (1.0 - pressure) * 2.0
|
||||
|
||||
# 溶剂系数
|
||||
solvent_factor = 1.0
|
||||
if solvent:
|
||||
solvent_lower = solvent.lower()
|
||||
if any(s in solvent_lower for s in ['water', 'h2o']):
|
||||
solvent_factor = 0.8 # 水蒸发较慢
|
||||
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
|
||||
solvent_factor = 1.5 # 易挥发溶剂蒸发快
|
||||
elif any(s in solvent_lower for s in ['dmso', 'dmi']):
|
||||
solvent_factor = 0.3 # 高沸点溶剂蒸发慢
|
||||
|
||||
# 计算总蒸发量
|
||||
total_evap_rate = base_evap_rate * temp_factor * vacuum_factor * solvent_factor
|
||||
evaporation_volume = min(
|
||||
original_liquid_volume * 0.95, # 最多蒸发95%
|
||||
total_evap_rate * (final_time / 60.0) # 时间相关的蒸发量
|
||||
)
|
||||
|
||||
debug_print(f"📊 蒸发量计算:")
|
||||
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 = []
|
||||
|
||||
@@ -334,7 +389,7 @@ def generate_evaporate_protocol(
|
||||
"device_id": rotavap_device,
|
||||
"action_name": "evaporate",
|
||||
"action_kwargs": {
|
||||
"vessel": target_vessel,
|
||||
"vessel": target_vessel, # 使用 target_vessel
|
||||
"pressure": pressure,
|
||||
"temp": temp,
|
||||
"time": final_time,
|
||||
@@ -345,6 +400,42 @@ def generate_evaporate_protocol(
|
||||
action_sequence.append(evaporate_action)
|
||||
debug_print(" ✅ 蒸发动作已添加 🌪️✨")
|
||||
|
||||
# 🔧 新增:蒸发过程中的体积变化
|
||||
debug_print(" 🔧 更新容器体积 - 蒸发过程...")
|
||||
if evaporation_volume > 0:
|
||||
new_volume = max(0.0, original_liquid_volume - evaporation_volume)
|
||||
|
||||
# 更新vessel字典中的体积
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
vessel["data"]["liquid_volume"][0] = new_volume
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [new_volume]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
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" 📊 蒸发体积变化: {original_liquid_volume:.2f}mL → {new_volume:.2f}mL (-{evaporation_volume:.2f}mL)")
|
||||
|
||||
# 3. 蒸发后等待
|
||||
debug_print(" 🔄 动作3: 添加蒸发后等待... ⏳")
|
||||
action_sequence.append({
|
||||
@@ -353,6 +444,15 @@ def generate_evaporate_protocol(
|
||||
})
|
||||
debug_print(" ✅ 蒸发后等待动作已添加 ⏳✨")
|
||||
|
||||
# 🔧 新增:蒸发完成后的状态报告
|
||||
final_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
final_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
final_liquid_volume = current_volume
|
||||
|
||||
# === 总结 ===
|
||||
debug_print("🎊" * 20)
|
||||
debug_print(f"🎉 蒸发协议生成完成! ✨")
|
||||
@@ -361,6 +461,10 @@ def generate_evaporate_protocol(
|
||||
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
|
||||
|
||||
@@ -50,16 +50,16 @@ def validate_vessel(G: nx.DiGraph, vessel: str, vessel_type: str = "容器") ->
|
||||
|
||||
def generate_filter_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
filtrate_vessel: str = "",
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成过滤操作的协议序列
|
||||
生成过滤操作的协议序列 - 支持体积运算
|
||||
|
||||
Args:
|
||||
G: 设备图
|
||||
vessel: 过滤容器名称(必需)- 包含需要过滤的混合物
|
||||
vessel: 过滤容器字典(必需)- 包含需要过滤的混合物
|
||||
filtrate_vessel: 滤液容器名称(可选)- 如果提供则收集滤液
|
||||
**kwargs: 其他参数(兼容性)
|
||||
|
||||
@@ -67,22 +67,36 @@ def generate_filter_protocol(
|
||||
List[Dict[str, Any]]: 过滤操作的动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("🌊" * 20)
|
||||
debug_print("🚀 开始生成过滤协议 ✨")
|
||||
debug_print("🚀 开始生成过滤协议(支持体积运算)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel}")
|
||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" 🧪 filtrate_vessel: {filtrate_vessel}")
|
||||
debug_print(f" ⚙️ 其他参数: {kwargs}")
|
||||
debug_print("🌊" * 20)
|
||||
|
||||
action_sequence = []
|
||||
|
||||
# 🔧 新增:记录过滤前的容器状态
|
||||
debug_print("🔍 记录过滤前容器状态...")
|
||||
original_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
debug_print(f"📊 过滤前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# === 参数验证 ===
|
||||
debug_print("📍 步骤1: 参数验证... 🔧")
|
||||
|
||||
# 验证必需参数
|
||||
debug_print(" 🔍 验证必需参数...")
|
||||
validate_vessel(G, vessel, "过滤容器")
|
||||
validate_vessel(G, vessel_id, "过滤容器") # 🔧 使用 vessel_id
|
||||
debug_print(" ✅ 必需参数验证完成 🎯")
|
||||
|
||||
# 验证可选参数
|
||||
@@ -106,18 +120,46 @@ def generate_filter_protocol(
|
||||
debug_print(f" ❌ 设备查找失败: {str(e)} 😭")
|
||||
raise ValueError(f"设备查找失败: {str(e)}")
|
||||
|
||||
# 🔧 新增:过滤效率和体积分配估算
|
||||
debug_print("📍 步骤2.5: 过滤体积分配估算... 📊")
|
||||
|
||||
# 估算过滤分离比例(基于经验数据)
|
||||
solid_ratio = 0.1 # 假设10%是固体(保留在过滤器上)
|
||||
liquid_ratio = 0.9 # 假设90%是液体(通过过滤器)
|
||||
volume_loss_ratio = 0.05 # 假设5%体积损失(残留在过滤器等)
|
||||
|
||||
# 从kwargs中获取过滤参数进行优化
|
||||
if "solid_content" in kwargs:
|
||||
try:
|
||||
solid_ratio = float(kwargs["solid_content"])
|
||||
liquid_ratio = 1.0 - solid_ratio
|
||||
debug_print(f"📋 使用指定的固体含量: {solid_ratio*100:.1f}%")
|
||||
except:
|
||||
debug_print("⚠️ 固体含量参数无效,使用默认值")
|
||||
|
||||
if original_liquid_volume > 0:
|
||||
expected_filtrate_volume = original_liquid_volume * liquid_ratio * (1.0 - volume_loss_ratio)
|
||||
expected_solid_volume = original_liquid_volume * solid_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 != filter_device:
|
||||
debug_print(f" 🚛 需要转移: {vessel} → {filter_device} 📦")
|
||||
if vessel_id != filter_device: # 🔧 使用 vessel_id
|
||||
debug_print(f" 🚛 需要转移: {vessel_id} → {filter_device} 📦")
|
||||
|
||||
try:
|
||||
debug_print(" 🔄 开始执行转移操作...")
|
||||
# 使用pump protocol转移液体到过滤器
|
||||
transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=vessel,
|
||||
from_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
to_vessel=filter_device,
|
||||
volume=0.0, # 转移所有液体
|
||||
amount="",
|
||||
@@ -134,6 +176,26 @@ def generate_filter_protocol(
|
||||
if transfer_actions:
|
||||
action_sequence.extend(transfer_actions)
|
||||
debug_print(f" ✅ 添加了 {len(transfer_actions)} 个转移动作 🚚✨")
|
||||
|
||||
# 🔧 新增:转移后更新容器体积
|
||||
debug_print(" 🔧 更新转移后的容器体积...")
|
||||
|
||||
# 原容器体积变为0(所有液体已转移)
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
vessel["data"]["liquid_volume"] = [0.0] if len(current_volume) > 0 else [0.0]
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = 0.0
|
||||
|
||||
# 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
if 'data' not in G.nodes[vessel_id]:
|
||||
G.nodes[vessel_id]['data'] = {}
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = 0.0
|
||||
|
||||
debug_print(f" 📊 转移完成,{vessel_id} 体积更新为 0.0mL")
|
||||
|
||||
else:
|
||||
debug_print(" ⚠️ 转移协议返回空序列 🤔")
|
||||
|
||||
@@ -206,6 +268,26 @@ def generate_filter_protocol(
|
||||
if collect_actions:
|
||||
action_sequence.extend(collect_actions)
|
||||
debug_print(f" ✅ 添加了 {len(collect_actions)} 个收集动作 🧪✨")
|
||||
|
||||
# 🔧 新增:收集滤液后的体积更新
|
||||
debug_print(" 🔧 更新滤液容器体积...")
|
||||
|
||||
# 更新filtrate_vessel在图中的体积(如果它是节点)
|
||||
if filtrate_vessel in G.nodes():
|
||||
if 'data' not in G.nodes[filtrate_vessel]:
|
||||
G.nodes[filtrate_vessel]['data'] = {}
|
||||
|
||||
current_filtrate_volume = G.nodes[filtrate_vessel]['data'].get('liquid_volume', 0.0)
|
||||
if isinstance(current_filtrate_volume, list):
|
||||
if len(current_filtrate_volume) > 0:
|
||||
G.nodes[filtrate_vessel]['data']['liquid_volume'][0] += expected_filtrate_volume
|
||||
else:
|
||||
G.nodes[filtrate_vessel]['data']['liquid_volume'] = [expected_filtrate_volume]
|
||||
else:
|
||||
G.nodes[filtrate_vessel]['data']['liquid_volume'] = current_filtrate_volume + expected_filtrate_volume
|
||||
|
||||
debug_print(f" 📊 滤液容器 {filtrate_vessel} 体积增加 {expected_filtrate_volume:.2f}mL")
|
||||
|
||||
else:
|
||||
debug_print(" ⚠️ 收集协议返回空序列 🤔")
|
||||
|
||||
@@ -215,6 +297,37 @@ def generate_filter_protocol(
|
||||
else:
|
||||
debug_print(" 🧱 未指定滤液容器,固体保留在过滤器中 🔬")
|
||||
|
||||
# 🔧 新增:过滤完成后的容器状态更新
|
||||
debug_print("📍 步骤5.5: 过滤完成后状态更新... 📊")
|
||||
|
||||
if vessel_id == filter_device:
|
||||
# 如果过滤容器就是过滤器,需要更新其体积状态
|
||||
if original_liquid_volume > 0:
|
||||
if filtrate_vessel:
|
||||
# 收集滤液模式:过滤器中主要保留固体
|
||||
remaining_volume = expected_solid_volume
|
||||
debug_print(f" 🧱 过滤器中保留固体: {remaining_volume:.2f}mL")
|
||||
else:
|
||||
# 保留固体模式:过滤器中保留所有物质
|
||||
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"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
vessel["data"]["liquid_volume"] = [remaining_volume] if len(current_volume) > 0 else [remaining_volume]
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = remaining_volume
|
||||
|
||||
# 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
if 'data' not in G.nodes[vessel_id]:
|
||||
G.nodes[vessel_id]['data'] = {}
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = remaining_volume
|
||||
|
||||
debug_print(f" 📊 过滤器 {vessel_id} 体积更新为: {remaining_volume:.2f}mL")
|
||||
|
||||
# === 最终等待 ===
|
||||
debug_print("📍 步骤6: 最终等待... ⏰")
|
||||
action_sequence.append({
|
||||
@@ -223,14 +336,31 @@ def generate_filter_protocol(
|
||||
})
|
||||
debug_print(" ✅ 最终等待动作已添加 ⏰✨")
|
||||
|
||||
# 🔧 新增:过滤完成后的状态报告
|
||||
final_vessel_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
final_vessel_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
final_vessel_volume = current_volume
|
||||
|
||||
# === 总结 ===
|
||||
debug_print("🎊" * 20)
|
||||
debug_print(f"🎉 过滤协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个 📝")
|
||||
debug_print(f"🥽 过滤容器: {vessel} 🧪")
|
||||
debug_print(f"🥽 过滤容器: {vessel_id} 🧪")
|
||||
debug_print(f"🌊 过滤器设备: {filter_device} 🔧")
|
||||
debug_print(f"💧 滤液容器: {filtrate_vessel 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
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ def validate_and_fix_params(temp: float, time: float, stir_speed: float) -> tupl
|
||||
|
||||
def generate_heat_chill_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
temp: float = 25.0,
|
||||
time: Union[str, float] = "300",
|
||||
temp_spec: str = "",
|
||||
@@ -196,29 +196,50 @@ def generate_heat_chill_protocol(
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成加热/冷却操作的协议序列
|
||||
生成加热/冷却操作的协议序列 - 支持vessel字典
|
||||
|
||||
Args:
|
||||
G: 设备图
|
||||
vessel: 容器字典(从XDL传入)
|
||||
temp: 目标温度 (°C)
|
||||
time: 加热时间(支持字符串如 "30 min")
|
||||
temp_spec: 温度规格说明(优先级高于temp)
|
||||
time_spec: 时间规格说明(优先级高于time)
|
||||
pressure: 压力设置
|
||||
reflux_solvent: 回流溶剂
|
||||
stir: 是否搅拌
|
||||
stir_speed: 搅拌速度 (RPM)
|
||||
purpose: 操作目的说明
|
||||
**kwargs: 其他参数(兼容性)
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: 加热/冷却操作的动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("🌡️" * 20)
|
||||
debug_print("🚀 开始生成加热冷却协议 ✨")
|
||||
debug_print("🚀 开始生成加热冷却协议(支持vessel字典)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel}")
|
||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" 🌡️ temp: {temp}°C")
|
||||
debug_print(f" ⏰ time: {time}")
|
||||
debug_print(f" 🎯 temp_spec: {temp_spec}")
|
||||
debug_print(f" ⏱️ time_spec: {time_spec}")
|
||||
debug_print(f" 🌪️ stir: {stir} ({stir_speed} RPM)")
|
||||
debug_print(f" 🎭 purpose: '{purpose}'")
|
||||
debug_print("🌡️" * 20)
|
||||
|
||||
# 📋 参数验证
|
||||
debug_print("📍 步骤1: 参数验证... 🔧")
|
||||
if not vessel:
|
||||
if not vessel_id: # 🔧 使用 vessel_id
|
||||
debug_print("❌ vessel 参数不能为空! 😱")
|
||||
raise ValueError("vessel 参数不能为空")
|
||||
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中! 😞")
|
||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print(f"❌ 容器 '{vessel_id}' 不存在于系统中! 😞")
|
||||
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
||||
|
||||
debug_print("✅ 基础参数验证通过 🎯")
|
||||
|
||||
@@ -239,7 +260,7 @@ def generate_heat_chill_protocol(
|
||||
# 🔍 查找设备
|
||||
debug_print("📍 步骤3: 查找加热设备... 🔍")
|
||||
try:
|
||||
heatchill_id = find_connected_heatchill(G, vessel)
|
||||
heatchill_id = find_connected_heatchill(G, vessel_id) # 🔧 使用 vessel_id
|
||||
debug_print(f"🎉 使用加热设备: {heatchill_id} ✨")
|
||||
except Exception as e:
|
||||
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
|
||||
@@ -265,7 +286,7 @@ def generate_heat_chill_protocol(
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": float(final_temp),
|
||||
"time": float(final_time),
|
||||
"stir": bool(stir),
|
||||
@@ -284,7 +305,7 @@ def generate_heat_chill_protocol(
|
||||
debug_print("🎊" * 20)
|
||||
debug_print(f"🎉 加热冷却协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
debug_print(f"🥽 加热容器: {vessel}")
|
||||
debug_print(f"🥽 加热容器: {vessel_id}")
|
||||
debug_print(f"🌡️ 目标温度: {final_temp}°C")
|
||||
debug_print(f"⏰ 加热时间: {final_time}s ({final_time/60:.1f}分钟)")
|
||||
debug_print("🎊" * 20)
|
||||
@@ -293,41 +314,45 @@ def generate_heat_chill_protocol(
|
||||
|
||||
def generate_heat_chill_to_temp_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改参数类型
|
||||
temp: float = 25.0,
|
||||
time: Union[str, float] = 100.0,
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""生成加热到指定温度的协议(简化版)"""
|
||||
debug_print(f"🌡️ 生成加热到温度协议: {vessel} → {temp}°C")
|
||||
vessel_id = vessel["id"]
|
||||
debug_print(f"🌡️ 生成加热到温度协议: {vessel_id} → {temp}°C")
|
||||
return generate_heat_chill_protocol(G, vessel, temp, time, **kwargs)
|
||||
|
||||
def generate_heat_chill_start_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改参数类型
|
||||
temp: float = 25.0,
|
||||
purpose: str = "",
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""生成开始加热操作的协议序列"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("🔥 开始生成启动加热协议 ✨")
|
||||
debug_print(f"🥽 vessel: {vessel}, 🌡️ temp: {temp}°C")
|
||||
debug_print(f"🥽 vessel: {vessel} (ID: {vessel_id}), 🌡️ temp: {temp}°C")
|
||||
|
||||
# 基础验证
|
||||
if not vessel or vessel not in G.nodes():
|
||||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print("❌ 容器验证失败!")
|
||||
raise ValueError("vessel 参数无效")
|
||||
|
||||
# 查找设备
|
||||
heatchill_id = find_connected_heatchill(G, vessel)
|
||||
heatchill_id = find_connected_heatchill(G, vessel_id) # 🔧 使用 vessel_id
|
||||
|
||||
# 生成动作
|
||||
action_sequence = [{
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill_start",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": temp,
|
||||
"purpose": purpose or f"开始加热到 {temp}°C"
|
||||
}
|
||||
@@ -338,39 +363,34 @@ def generate_heat_chill_start_protocol(
|
||||
|
||||
def generate_heat_chill_stop_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: dict, # 🔧 修改参数类型
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""生成停止加热操作的协议序列"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("🛑 开始生成停止加热协议 ✨")
|
||||
debug_print(f"🥽 vessel: {vessel}")
|
||||
debug_print(f"🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||
|
||||
# 基础验证
|
||||
if not vessel or vessel not in G.nodes():
|
||||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print("❌ 容器验证失败!")
|
||||
raise ValueError("vessel 参数无效")
|
||||
|
||||
# 查找设备
|
||||
heatchill_id = find_connected_heatchill(G, vessel)
|
||||
heatchill_id = find_connected_heatchill(G, vessel_id) # 🔧 使用 vessel_id
|
||||
|
||||
# 生成动作
|
||||
action_sequence = [{
|
||||
"device_id": heatchill_id,
|
||||
"action_name": "heat_chill_stop",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel
|
||||
"vessel": vessel_id # 🔧 使用 vessel_id
|
||||
}
|
||||
}]
|
||||
|
||||
debug_print(f"✅ 停止加热协议生成完成 🎯")
|
||||
return action_sequence
|
||||
|
||||
# 测试函数
|
||||
def test_heatchill_protocol():
|
||||
"""测试加热协议"""
|
||||
debug_print("🧪 === HEATCHILL PROTOCOL 测试 === ✨")
|
||||
debug_print("✅ 测试完成 🎉")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_heatchill_protocol()
|
||||
@@ -150,75 +150,103 @@ def find_connected_device(G: nx.DiGraph, vessel: str, device_type: str) -> str:
|
||||
|
||||
def generate_hydrogenate_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
temp: str,
|
||||
time: str,
|
||||
vessel: str,
|
||||
**kwargs # 接收其他可能的参数但不使用
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成氢化反应协议序列
|
||||
生成氢化反应协议序列 - 支持vessel字典
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为容器和设备
|
||||
vessel: 反应容器字典(从XDL传入)
|
||||
temp: 反应温度(如 "45 °C")
|
||||
time: 反应时间(如 "2 h")
|
||||
vessel: 反应容器
|
||||
**kwargs: 其他可选参数,但不使用
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
action_sequence = []
|
||||
|
||||
# 解析参数
|
||||
temperature = parse_temperature(temp)
|
||||
reaction_time = parse_time(time)
|
||||
|
||||
print(f"HYDROGENATE: 开始生成氢化反应协议")
|
||||
print(f" - 反应温度: {temperature}°C")
|
||||
print(f" - 反应时间: {reaction_time/3600:.1f} 小时")
|
||||
print(f" - 反应容器: {vessel}")
|
||||
print("🧪" * 20)
|
||||
print(f"HYDROGENATE: 开始生成氢化反应协议(支持vessel字典)✨")
|
||||
print(f"📝 输入参数:")
|
||||
print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||
print(f" 🌡️ 反应温度: {temperature}°C")
|
||||
print(f" ⏰ 反应时间: {reaction_time/3600:.1f} 小时")
|
||||
print("🧪" * 20)
|
||||
|
||||
# 🔧 新增:记录氢化前的容器状态(可选,氢化反应通常不改变体积)
|
||||
original_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
print(f"📊 氢化前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# 1. 验证目标容器存在
|
||||
if vessel not in G.nodes():
|
||||
print(f"HYDROGENATE: 警告 - 容器 '{vessel}' 不存在于系统中,跳过氢化反应")
|
||||
print("📍 步骤1: 验证目标容器...")
|
||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
print(f"⚠️ HYDROGENATE: 警告 - 容器 '{vessel_id}' 不存在于系统中,跳过氢化反应")
|
||||
return action_sequence
|
||||
print(f"✅ 容器 '{vessel_id}' 验证通过")
|
||||
|
||||
# 2. 查找相连的设备
|
||||
heater_id = find_connected_device(G, vessel, 'heater')
|
||||
stirrer_id = find_connected_device(G, vessel, 'stirrer')
|
||||
gas_source_id = find_connected_device(G, vessel, 'gas_source')
|
||||
print("📍 步骤2: 查找相连设备...")
|
||||
heater_id = find_connected_device(G, vessel_id, 'heater') # 🔧 使用 vessel_id
|
||||
stirrer_id = find_connected_device(G, vessel_id, 'stirrer') # 🔧 使用 vessel_id
|
||||
gas_source_id = find_connected_device(G, vessel_id, 'gas_source') # 🔧 使用 vessel_id
|
||||
|
||||
print(f"🔧 设备配置:")
|
||||
print(f" 🔥 加热器: {heater_id or '未找到'}")
|
||||
print(f" 🌪️ 搅拌器: {stirrer_id or '未找到'}")
|
||||
print(f" 💨 气源: {gas_source_id or '未找到'}")
|
||||
|
||||
# 3. 启动搅拌器
|
||||
print("📍 步骤3: 启动搅拌器...")
|
||||
if stirrer_id:
|
||||
print(f"HYDROGENATE: 启动搅拌器 {stirrer_id}")
|
||||
print(f"🌪️ 启动搅拌器 {stirrer_id}")
|
||||
action_sequence.append({
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"stir_speed": 300.0,
|
||||
"purpose": "氢化反应: 开始搅拌"
|
||||
}
|
||||
})
|
||||
print("✅ 搅拌器启动动作已添加")
|
||||
else:
|
||||
print(f"HYDROGENATE: 警告 - 未找到搅拌器,继续执行")
|
||||
print(f"⚠️ HYDROGENATE: 警告 - 未找到搅拌器,继续执行")
|
||||
|
||||
# 4. 启动气源(氢气)- 修复版本
|
||||
# 4. 启动气源(氢气)
|
||||
print("📍 步骤4: 启动氢气源...")
|
||||
if gas_source_id:
|
||||
print(f"HYDROGENATE: 启动气源 {gas_source_id} (氢气)")
|
||||
print(f"💨 启动气源 {gas_source_id} (氢气)")
|
||||
action_sequence.append({
|
||||
"device_id": gas_source_id,
|
||||
"action_name": "set_status", # 修改为 set_status
|
||||
"action_name": "set_status",
|
||||
"action_kwargs": {
|
||||
"string": "ON" # 修改参数格式
|
||||
"string": "ON"
|
||||
}
|
||||
})
|
||||
|
||||
# 查找相关的电磁阀
|
||||
gas_solenoid = find_associated_solenoid_valve(G, gas_source_id)
|
||||
if gas_solenoid:
|
||||
print(f"HYDROGENATE: 开启气源电磁阀 {gas_solenoid}")
|
||||
print(f"🚪 开启气源电磁阀 {gas_solenoid}")
|
||||
action_sequence.append({
|
||||
"device_id": gas_solenoid,
|
||||
"action_name": "set_valve_position",
|
||||
@@ -226,10 +254,12 @@ def generate_hydrogenate_protocol(
|
||||
"command": "OPEN"
|
||||
}
|
||||
})
|
||||
print("✅ 氢气源启动动作已添加")
|
||||
else:
|
||||
print(f"HYDROGENATE: 警告 - 未找到气源,继续执行")
|
||||
print(f"⚠️ HYDROGENATE: 警告 - 未找到气源,继续执行")
|
||||
|
||||
# 5. 等待气体稳定
|
||||
print("📍 步骤5: 等待气体环境稳定...")
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
@@ -237,15 +267,17 @@ def generate_hydrogenate_protocol(
|
||||
"description": "等待氢气环境稳定"
|
||||
}
|
||||
})
|
||||
print("✅ 气体稳定等待动作已添加")
|
||||
|
||||
# 6. 启动加热器
|
||||
print("📍 步骤6: 启动加热反应...")
|
||||
if heater_id:
|
||||
print(f"HYDROGENATE: 启动加热器 {heater_id} 到 {temperature}°C")
|
||||
print(f"🔥 启动加热器 {heater_id} 到 {temperature}°C")
|
||||
action_sequence.append({
|
||||
"device_id": heater_id,
|
||||
"action_name": "heat_chill_start",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": temperature,
|
||||
"purpose": f"氢化反应: 加热到 {temperature}°C"
|
||||
}
|
||||
@@ -261,23 +293,23 @@ def generate_hydrogenate_protocol(
|
||||
})
|
||||
|
||||
# 🕐 模拟运行时间优化
|
||||
print("HYDROGENATE: 检查模拟运行时间限制...")
|
||||
print(" ⏰ 检查模拟运行时间限制...")
|
||||
original_reaction_time = reaction_time
|
||||
simulation_time_limit = 60.0 # 模拟运行时间限制:60秒
|
||||
|
||||
if reaction_time > simulation_time_limit:
|
||||
reaction_time = simulation_time_limit
|
||||
print(f"HYDROGENATE: 模拟运行优化: {original_reaction_time}s → {reaction_time}s (限制为{simulation_time_limit}s)")
|
||||
print(f"HYDROGENATE: 时间缩短: {original_reaction_time/3600:.2f}小时 → {reaction_time/60:.1f}分钟")
|
||||
print(f" 🎮 模拟运行优化: {original_reaction_time}s → {reaction_time}s (限制为{simulation_time_limit}s)")
|
||||
print(f" 📊 时间缩短: {original_reaction_time/3600:.2f}小时 → {reaction_time/60:.1f}分钟")
|
||||
else:
|
||||
print(f"HYDROGENATE: 时间在限制内: {reaction_time}s ({reaction_time/60:.1f}分钟) 保持不变")
|
||||
print(f" ✅ 时间在限制内: {reaction_time}s ({reaction_time/60:.1f}分钟) 保持不变")
|
||||
|
||||
# 保持反应温度
|
||||
action_sequence.append({
|
||||
"device_id": heater_id,
|
||||
"action_name": "heat_chill",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"temp": temperature,
|
||||
"time": reaction_time,
|
||||
"purpose": f"氢化反应: 保持 {temperature}°C,反应 {reaction_time/60:.1f}分钟" + (f" (模拟时间)" if original_reaction_time != reaction_time else "")
|
||||
@@ -286,22 +318,24 @@ def generate_hydrogenate_protocol(
|
||||
|
||||
# 显示时间调整信息
|
||||
if original_reaction_time != reaction_time:
|
||||
print(f"HYDROGENATE: 模拟优化说明: 原计划 {original_reaction_time/3600:.2f}小时,实际模拟 {reaction_time/60:.1f}分钟")
|
||||
print(f" 🎭 模拟优化说明: 原计划 {original_reaction_time/3600:.2f}小时,实际模拟 {reaction_time/60:.1f}分钟")
|
||||
|
||||
print("✅ 加热反应动作已添加")
|
||||
|
||||
else:
|
||||
print(f"HYDROGENATE: 警告 - 未找到加热器,使用室温反应")
|
||||
print(f"⚠️ HYDROGENATE: 警告 - 未找到加热器,使用室温反应")
|
||||
|
||||
# 🕐 室温反应也需要时间优化
|
||||
print("HYDROGENATE: 检查室温反应模拟时间限制...")
|
||||
print(" ⏰ 检查室温反应模拟时间限制...")
|
||||
original_reaction_time = reaction_time
|
||||
simulation_time_limit = 60.0 # 模拟运行时间限制:60秒
|
||||
|
||||
if reaction_time > simulation_time_limit:
|
||||
reaction_time = simulation_time_limit
|
||||
print(f"HYDROGENATE: 室温反应时间优化: {original_reaction_time}s → {reaction_time}s")
|
||||
print(f"HYDROGENATE: 时间缩短: {original_reaction_time/3600:.2f}小时 → {reaction_time/60:.1f}分钟")
|
||||
print(f" 🎮 室温反应时间优化: {original_reaction_time}s → {reaction_time}s")
|
||||
print(f" 📊 时间缩短: {original_reaction_time/3600:.2f}小时 → {reaction_time/60:.1f}分钟")
|
||||
else:
|
||||
print(f"HYDROGENATE: 室温反应时间在限制内: {reaction_time}s 保持不变")
|
||||
print(f" ✅ 室温反应时间在限制内: {reaction_time}s 保持不变")
|
||||
|
||||
# 室温反应,只等待时间
|
||||
action_sequence.append({
|
||||
@@ -314,20 +348,25 @@ def generate_hydrogenate_protocol(
|
||||
|
||||
# 显示时间调整信息
|
||||
if original_reaction_time != reaction_time:
|
||||
print(f"HYDROGENATE: 室温反应优化说明: 原计划 {original_reaction_time/3600:.2f}小时,实际模拟 {reaction_time/60:.1f}分钟")
|
||||
print(f" 🎭 室温反应优化说明: 原计划 {original_reaction_time/3600:.2f}小时,实际模拟 {reaction_time/60:.1f}分钟")
|
||||
|
||||
print("✅ 室温反应等待动作已添加")
|
||||
|
||||
# 7. 停止加热
|
||||
print("📍 步骤7: 停止加热...")
|
||||
if heater_id:
|
||||
action_sequence.append({
|
||||
"device_id": heater_id,
|
||||
"action_name": "heat_chill_stop",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"purpose": "氢化反应完成,停止加热"
|
||||
}
|
||||
})
|
||||
print("✅ 停止加热动作已添加")
|
||||
|
||||
# 8. 等待冷却
|
||||
print("📍 步骤8: 等待冷却...")
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
@@ -335,13 +374,15 @@ def generate_hydrogenate_protocol(
|
||||
"description": "等待反应混合物冷却"
|
||||
}
|
||||
})
|
||||
print("✅ 冷却等待动作已添加")
|
||||
|
||||
# 9. 停止气源 - 修复版本
|
||||
# 9. 停止气源
|
||||
print("📍 步骤9: 停止氢气源...")
|
||||
if gas_source_id:
|
||||
# 先关闭电磁阀
|
||||
gas_solenoid = find_associated_solenoid_valve(G, gas_source_id)
|
||||
if gas_solenoid:
|
||||
print(f"HYDROGENATE: 关闭气源电磁阀 {gas_solenoid}")
|
||||
print(f"🚪 关闭气源电磁阀 {gas_solenoid}")
|
||||
action_sequence.append({
|
||||
"device_id": gas_solenoid,
|
||||
"action_name": "set_valve_position",
|
||||
@@ -353,25 +394,41 @@ def generate_hydrogenate_protocol(
|
||||
# 再关闭气源
|
||||
action_sequence.append({
|
||||
"device_id": gas_source_id,
|
||||
"action_name": "set_status", # 修改为 set_status
|
||||
"action_name": "set_status",
|
||||
"action_kwargs": {
|
||||
"string": "OFF" # 修改参数格式
|
||||
"string": "OFF"
|
||||
}
|
||||
})
|
||||
print("✅ 氢气源停止动作已添加")
|
||||
|
||||
# 10. 停止搅拌
|
||||
print("📍 步骤10: 停止搅拌...")
|
||||
if stirrer_id:
|
||||
action_sequence.append({
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "stop_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"purpose": "氢化反应完成,停止搅拌"
|
||||
}
|
||||
})
|
||||
print("✅ 停止搅拌动作已添加")
|
||||
|
||||
print(f"HYDROGENATE: 协议生成完成,共 {len(action_sequence)} 个动作")
|
||||
print(f"HYDROGENATE: 预计总时间: {(reaction_time + 450)/3600:.1f} 小时")
|
||||
# 🔧 新增:氢化完成后的状态(氢化反应通常不改变体积)
|
||||
final_liquid_volume = original_liquid_volume # 氢化反应体积基本不变
|
||||
|
||||
# 总结
|
||||
print("🎊" * 20)
|
||||
print(f"🎉 氢化反应协议生成完成! ✨")
|
||||
print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
print(f"🥽 反应容器: {vessel_id}")
|
||||
print(f"🌡️ 反应温度: {temperature}°C")
|
||||
print(f"⏰ 反应时间: {reaction_time/60:.1f}分钟")
|
||||
print(f"⏱️ 预计总时间: {(reaction_time + 450)/3600:.1f} 小时")
|
||||
print(f"📊 体积状态:")
|
||||
print(f" - 反应前体积: {original_liquid_volume:.2f}mL")
|
||||
print(f" - 反应后体积: {final_liquid_volume:.2f}mL (氢化反应体积基本不变)")
|
||||
print("🎊" * 20)
|
||||
|
||||
return action_sequence
|
||||
|
||||
@@ -379,7 +436,7 @@ def generate_hydrogenate_protocol(
|
||||
# 测试函数
|
||||
def test_hydrogenate_protocol():
|
||||
"""测试氢化反应协议"""
|
||||
print("=== HYDROGENATE PROTOCOL 测试 ===")
|
||||
print("🧪 === HYDROGENATE PROTOCOL 测试 === ✨")
|
||||
|
||||
# 测试温度解析
|
||||
test_temps = ["45 °C", "45°C", "45", "25 C", "invalid"]
|
||||
@@ -393,7 +450,7 @@ def test_hydrogenate_protocol():
|
||||
parsed = parse_time(time)
|
||||
print(f"时间 '{time}' -> {parsed/3600:.1f} 小时")
|
||||
|
||||
print("测试完成")
|
||||
print("✅ 测试完成 🎉")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import networkx as nx
|
||||
import re
|
||||
import logging
|
||||
from typing import List, Dict, Any, Tuple, Union
|
||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def debug_print(message):
|
||||
"""调试输出"""
|
||||
print(f"💎 [RECRYSTALLIZE] {message}", flush=True)
|
||||
logger.info(f"[RECRYSTALLIZE] {message}")
|
||||
|
||||
|
||||
def parse_volume_with_units(volume_input: Union[str, float, int], default_unit: str = "mL") -> float:
|
||||
@@ -199,48 +202,63 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
|
||||
def generate_recrystallize_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
ratio: str,
|
||||
solvent1: str,
|
||||
solvent2: str,
|
||||
vessel: str,
|
||||
volume: Union[str, float], # 🔧 修改:支持字符串和数值
|
||||
volume: Union[str, float], # 支持字符串和数值
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成重结晶协议序列 - 支持单位
|
||||
生成重结晶协议序列 - 支持vessel字典和体积运算
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为容器和设备
|
||||
vessel: 目标容器字典(从XDL传入)
|
||||
ratio: 溶剂比例(如 "1:1", "3:7")
|
||||
solvent1: 第一种溶剂名称
|
||||
solvent2: 第二种溶剂名称
|
||||
vessel: 目标容器
|
||||
volume: 总体积(支持 "100 mL", "50", "2.5 L" 等)
|
||||
**kwargs: 其他可选参数
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: 动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
action_sequence = []
|
||||
|
||||
debug_print("💎" * 20)
|
||||
debug_print("🚀 开始生成重结晶协议(支持单位)✨")
|
||||
debug_print("🚀 开始生成重结晶协议(支持vessel字典和体积运算)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" ⚖️ 比例: {ratio}")
|
||||
debug_print(f" 🧪 溶剂1: {solvent1}")
|
||||
debug_print(f" 🧪 溶剂2: {solvent2}")
|
||||
debug_print(f" 🥽 容器: {vessel}")
|
||||
debug_print(f" 💧 总体积: {volume} (类型: {type(volume)})")
|
||||
debug_print("💎" * 20)
|
||||
|
||||
# 🔧 新增:记录重结晶前的容器状态
|
||||
debug_print("🔍 记录重结晶前容器状态...")
|
||||
original_liquid_volume = 0.0
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list) and len(current_volume) > 0:
|
||||
original_liquid_volume = current_volume[0]
|
||||
elif isinstance(current_volume, (int, float)):
|
||||
original_liquid_volume = current_volume
|
||||
debug_print(f"📊 重结晶前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# 1. 验证目标容器存在
|
||||
debug_print("📍 步骤1: 验证目标容器... 🔧")
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 目标容器 '{vessel}' 不存在于系统中! 😱")
|
||||
raise ValueError(f"目标容器 '{vessel}' 不存在于系统中")
|
||||
debug_print(f"✅ 目标容器 '{vessel}' 验证通过 🎯")
|
||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print(f"❌ 目标容器 '{vessel_id}' 不存在于系统中! 😱")
|
||||
raise ValueError(f"目标容器 '{vessel_id}' 不存在于系统中")
|
||||
debug_print(f"✅ 目标容器 '{vessel_id}' 验证通过 🎯")
|
||||
|
||||
# 2. 🔧 新增:解析体积(支持单位)
|
||||
# 2. 解析体积(支持单位)
|
||||
debug_print("📍 步骤2: 解析体积(支持单位)... 💧")
|
||||
final_volume = parse_volume_with_units(volume, "mL")
|
||||
debug_print(f"🎯 体积解析完成: {volume} → {final_volume}mL ✨")
|
||||
@@ -281,18 +299,18 @@ def generate_recrystallize_protocol(
|
||||
# 6. 验证路径存在
|
||||
debug_print("📍 步骤6: 验证传输路径... 🛤️")
|
||||
try:
|
||||
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel)
|
||||
path1 = nx.shortest_path(G, source=solvent1_vessel, target=vessel_id) # 🔧 使用 vessel_id
|
||||
debug_print(f" 🛤️ 溶剂1路径: {' → '.join(path1)} ✅")
|
||||
except nx.NetworkXNoPath:
|
||||
debug_print(f" ❌ 溶剂1路径不可达: {solvent1_vessel} → {vessel} 😞")
|
||||
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel}' 没有可用路径")
|
||||
debug_print(f" ❌ 溶剂1路径不可达: {solvent1_vessel} → {vessel_id} 😞")
|
||||
raise ValueError(f"从溶剂1容器 '{solvent1_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
||||
|
||||
try:
|
||||
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel)
|
||||
path2 = nx.shortest_path(G, source=solvent2_vessel, target=vessel_id) # 🔧 使用 vessel_id
|
||||
debug_print(f" 🛤️ 溶剂2路径: {' → '.join(path2)} ✅")
|
||||
except nx.NetworkXNoPath:
|
||||
debug_print(f" ❌ 溶剂2路径不可达: {solvent2_vessel} → {vessel} 😞")
|
||||
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel}' 没有可用路径")
|
||||
debug_print(f" ❌ 溶剂2路径不可达: {solvent2_vessel} → {vessel_id} 😞")
|
||||
raise ValueError(f"从溶剂2容器 '{solvent2_vessel}' 到目标容器 '{vessel_id}' 没有可用路径")
|
||||
|
||||
# 7. 添加第一种溶剂
|
||||
debug_print("📍 步骤7: 添加第一种溶剂... 🧪")
|
||||
@@ -302,7 +320,7 @@ def generate_recrystallize_protocol(
|
||||
pump_actions1 = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent1_vessel,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
volume=volume1, # 使用解析后的体积
|
||||
amount="",
|
||||
time=0.0,
|
||||
@@ -322,12 +340,45 @@ def generate_recrystallize_protocol(
|
||||
debug_print(f" ❌ 溶剂1泵协议生成失败: {str(e)} 😭")
|
||||
raise ValueError(f"生成溶剂1泵协议时出错: {str(e)}")
|
||||
|
||||
# 🔧 新增:更新容器体积 - 添加溶剂1后
|
||||
debug_print(" 🔧 更新容器体积 - 添加溶剂1后...")
|
||||
new_volume_after_solvent1 = original_liquid_volume + volume1
|
||||
|
||||
# 更新vessel字典中的体积
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
vessel["data"]["liquid_volume"][0] = new_volume_after_solvent1
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [new_volume_after_solvent1]
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = new_volume_after_solvent1
|
||||
|
||||
# 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
if 'data' not in G.nodes[vessel_id]:
|
||||
G.nodes[vessel_id]['data'] = {}
|
||||
|
||||
vessel_node_data = G.nodes[vessel_id]['data']
|
||||
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
|
||||
|
||||
if isinstance(current_node_volume, list):
|
||||
if len(current_node_volume) > 0:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'][0] = new_volume_after_solvent1
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = [new_volume_after_solvent1]
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume_after_solvent1
|
||||
|
||||
debug_print(f" 📊 体积更新: {original_liquid_volume:.2f}mL + {volume1:.2f}mL = {new_volume_after_solvent1:.2f}mL")
|
||||
|
||||
# 8. 等待溶剂1稳定
|
||||
debug_print(" ⏳ 添加溶剂1稳定等待...")
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
"time": 5.0, # 🕐 缩短等待时间:10.0s → 5.0s
|
||||
"time": 5.0, # 缩短等待时间
|
||||
"description": f"等待溶剂1 {solvent1} 稳定"
|
||||
}
|
||||
})
|
||||
@@ -341,7 +392,7 @@ def generate_recrystallize_protocol(
|
||||
pump_actions2 = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent2_vessel,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
volume=volume2, # 使用解析后的体积
|
||||
amount="",
|
||||
time=0.0,
|
||||
@@ -361,12 +412,45 @@ def generate_recrystallize_protocol(
|
||||
debug_print(f" ❌ 溶剂2泵协议生成失败: {str(e)} 😭")
|
||||
raise ValueError(f"生成溶剂2泵协议时出错: {str(e)}")
|
||||
|
||||
# 🔧 新增:更新容器体积 - 添加溶剂2后
|
||||
debug_print(" 🔧 更新容器体积 - 添加溶剂2后...")
|
||||
final_liquid_volume = new_volume_after_solvent1 + volume2
|
||||
|
||||
# 更新vessel字典中的体积
|
||||
if "data" in vessel and "liquid_volume" in vessel["data"]:
|
||||
current_volume = vessel["data"]["liquid_volume"]
|
||||
if isinstance(current_volume, list):
|
||||
if len(current_volume) > 0:
|
||||
vessel["data"]["liquid_volume"][0] = final_liquid_volume
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = [final_liquid_volume]
|
||||
else:
|
||||
vessel["data"]["liquid_volume"] = final_liquid_volume
|
||||
|
||||
# 同时更新图中的容器数据
|
||||
if vessel_id in G.nodes():
|
||||
if 'data' not in G.nodes[vessel_id]:
|
||||
G.nodes[vessel_id]['data'] = {}
|
||||
|
||||
vessel_node_data = G.nodes[vessel_id]['data']
|
||||
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
|
||||
|
||||
if isinstance(current_node_volume, list):
|
||||
if len(current_node_volume) > 0:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'][0] = final_liquid_volume
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = [final_liquid_volume]
|
||||
else:
|
||||
G.nodes[vessel_id]['data']['liquid_volume'] = final_liquid_volume
|
||||
|
||||
debug_print(f" 📊 最终体积: {new_volume_after_solvent1:.2f}mL + {volume2:.2f}mL = {final_liquid_volume:.2f}mL")
|
||||
|
||||
# 10. 等待溶剂2稳定
|
||||
debug_print(" ⏳ 添加溶剂2稳定等待...")
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
"time": 5.0, # 🕐 缩短等待时间:10.0s → 5.0s
|
||||
"time": 5.0, # 缩短等待时间
|
||||
"description": f"等待溶剂2 {solvent2} 稳定"
|
||||
}
|
||||
})
|
||||
@@ -375,7 +459,7 @@ def generate_recrystallize_protocol(
|
||||
# 11. 等待重结晶完成
|
||||
debug_print("📍 步骤9: 等待重结晶完成... 💎")
|
||||
|
||||
# 🕐 模拟运行时间优化
|
||||
# 模拟运行时间优化
|
||||
debug_print(" ⏱️ 检查模拟运行时间限制...")
|
||||
original_crystallize_time = 600.0 # 原始重结晶时间
|
||||
simulation_time_limit = 60.0 # 模拟运行时间限制:60秒
|
||||
@@ -401,12 +485,15 @@ def generate_recrystallize_protocol(
|
||||
if original_crystallize_time != final_crystallize_time:
|
||||
debug_print(f" 🎭 模拟优化说明: 原计划 {original_crystallize_time/60:.1f}分钟,实际模拟 {final_crystallize_time/60:.1f}分钟 ⚡")
|
||||
|
||||
# 🎊 总结
|
||||
# 总结
|
||||
debug_print("💎" * 20)
|
||||
debug_print(f"🎉 重结晶协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
debug_print(f"🥽 目标容器: {vessel}")
|
||||
debug_print(f"💧 总体积: {final_volume}mL")
|
||||
debug_print(f"🥽 目标容器: {vessel_id}")
|
||||
debug_print(f"💧 总体积变化:")
|
||||
debug_print(f" - 原始体积: {original_liquid_volume:.2f}mL")
|
||||
debug_print(f" - 添加溶剂: {final_volume:.2f}mL")
|
||||
debug_print(f" - 最终体积: {final_liquid_volume:.2f}mL")
|
||||
debug_print(f"⚖️ 溶剂比例: {solvent1}:{solvent2} = {ratio1}:{ratio2}")
|
||||
debug_print(f"🧪 溶剂1: {solvent1} ({volume1:.2f}mL)")
|
||||
debug_print(f"🧪 溶剂2: {solvent2} ({volume2:.2f}mL)")
|
||||
@@ -421,6 +508,13 @@ def test_recrystallize_protocol():
|
||||
"""测试重结晶协议"""
|
||||
debug_print("🧪 === RECRYSTALLIZE PROTOCOL 测试 === ✨")
|
||||
|
||||
# 测试体积解析
|
||||
debug_print("💧 测试体积解析...")
|
||||
test_volumes = ["100 mL", "2.5 L", "500", "50.5", "?", "invalid"]
|
||||
for vol in test_volumes:
|
||||
parsed = parse_volume_with_units(vol)
|
||||
debug_print(f" 📊 体积 '{vol}' -> {parsed}mL")
|
||||
|
||||
# 测试比例解析
|
||||
debug_print("⚖️ 测试比例解析...")
|
||||
test_ratios = ["1:1", "3:7", "50:50", "1-1", "2,8", "invalid"]
|
||||
@@ -430,6 +524,5 @@ def test_recrystallize_protocol():
|
||||
|
||||
debug_print("✅ 测试完成 🎉")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_recrystallize_protocol()
|
||||
@@ -1,12 +1,67 @@
|
||||
import networkx as nx
|
||||
from typing import List, Dict, Any
|
||||
import logging
|
||||
import sys
|
||||
from typing import List, Dict, Any, Optional
|
||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||
|
||||
# 设置日志
|
||||
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):
|
||||
"""调试输出"""
|
||||
print(f"🔄 [RESET_HANDLING] {message}", flush=True)
|
||||
"""调试输出函数 - 支持中文"""
|
||||
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:
|
||||
"""
|
||||
@@ -19,7 +74,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
Returns:
|
||||
str: 溶剂容器ID
|
||||
"""
|
||||
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器... 🧪")
|
||||
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器...")
|
||||
|
||||
# 构建可能的容器名称
|
||||
possible_names = [
|
||||
@@ -33,30 +88,30 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
f"vessel_{solvent}", # vessel_methanol
|
||||
]
|
||||
|
||||
debug_print(f"📋 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个) 📝")
|
||||
debug_print(f"🎯 候选容器名称: {possible_names[:3]}... (共{len(possible_names)}个)")
|
||||
|
||||
# 第一步:通过容器名称匹配
|
||||
debug_print(" 🎯 步骤1: 精确名称匹配...")
|
||||
debug_print("📋 方法1: 精确名称匹配...")
|
||||
for vessel_name in possible_names:
|
||||
if vessel_name in G.nodes():
|
||||
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name} ✨")
|
||||
debug_print(f"✅ 通过名称匹配找到容器: {vessel_name}")
|
||||
return vessel_name
|
||||
debug_print(" 😞 精确名称匹配失败,尝试模糊匹配... 🔍")
|
||||
debug_print("⚠️ 精确名称匹配失败,尝试模糊匹配...")
|
||||
|
||||
# 第二步:通过模糊匹配
|
||||
debug_print(" 🔍 步骤2: 模糊名称匹配...")
|
||||
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} ✨")
|
||||
debug_print(f"✅ 通过模糊匹配找到容器: {node_id}")
|
||||
return node_id
|
||||
debug_print(" 😞 模糊匹配失败,尝试液体类型匹配... 🧪")
|
||||
debug_print("⚠️ 模糊匹配失败,尝试液体类型匹配...")
|
||||
|
||||
# 第三步:通过液体类型匹配
|
||||
debug_print(" 🧪 步骤3: 液体类型匹配...")
|
||||
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', {})
|
||||
@@ -68,11 +123,11 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
reagent_name = vessel_data.get('reagent_name', '').lower()
|
||||
|
||||
if solvent.lower() in liquid_type or solvent.lower() in reagent_name:
|
||||
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id} ✨")
|
||||
debug_print(f"✅ 通过液体类型匹配找到容器: {node_id}")
|
||||
return node_id
|
||||
|
||||
# 列出可用容器帮助调试
|
||||
debug_print(" 📊 显示可用容器信息...")
|
||||
debug_print("📊 显示可用容器信息...")
|
||||
available_containers = []
|
||||
for node_id in G.nodes():
|
||||
if G.nodes[node_id].get('type') == 'container':
|
||||
@@ -88,30 +143,31 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
'reagent_name': vessel_data.get('reagent_name', '')
|
||||
})
|
||||
|
||||
debug_print(f" 📋 可用容器列表 (共{len(available_containers)}个):")
|
||||
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']}")
|
||||
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" ... 还有 {len(available_containers)-5} 个容器")
|
||||
|
||||
debug_print(f"❌ 找不到溶剂 '{solvent}' 对应的容器 😭")
|
||||
debug_print(f"❌ 找不到溶剂 '{solvent}' 对应的容器")
|
||||
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器。尝试了: {possible_names[:3]}...")
|
||||
|
||||
|
||||
def generate_reset_handling_protocol(
|
||||
G: nx.DiGraph,
|
||||
solvent: str,
|
||||
vessel: Optional[str] = None, # 🆕 新增可选vessel参数
|
||||
**kwargs # 接收其他可能的参数但不使用
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成重置处理协议序列
|
||||
生成重置处理协议序列 - 支持自定义容器
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为容器和设备
|
||||
solvent: 溶剂名称(从XDL传入)
|
||||
vessel: 目标容器名称(可选,默认为 "main_reactor")
|
||||
**kwargs: 其他可选参数,但不使用
|
||||
|
||||
Returns:
|
||||
@@ -119,51 +175,79 @@ def generate_reset_handling_protocol(
|
||||
"""
|
||||
action_sequence = []
|
||||
|
||||
# 固定参数
|
||||
target_vessel = "main_reactor" # 默认目标容器
|
||||
# 🔧 修改:支持自定义vessel参数
|
||||
target_vessel = vessel if vessel is not None else "main_reactor" # 默认目标容器
|
||||
volume = 50.0 # 默认体积 50 mL
|
||||
|
||||
debug_print("🔄" * 20)
|
||||
debug_print("🚀 开始生成重置处理协议 ✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print("=" * 60)
|
||||
debug_print("🚀 开始生成重置处理协议")
|
||||
debug_print(f"📋 输入参数:")
|
||||
debug_print(f" 🧪 溶剂: {solvent}")
|
||||
debug_print(f" 🥽 目标容器: {target_vessel}")
|
||||
debug_print(f" 🥽 目标容器: {target_vessel} {'(默认)' if vessel is None else '(指定)'}")
|
||||
debug_print(f" 💧 体积: {volume} mL")
|
||||
debug_print(f" ⚙️ 其他参数: {kwargs}")
|
||||
debug_print("🔄" * 20)
|
||||
debug_print("=" * 60)
|
||||
|
||||
# 添加初始日志
|
||||
action_sequence.append(create_action_log(f"开始重置处理操作 - 容器: {target_vessel}", "🎬"))
|
||||
action_sequence.append(create_action_log(f"使用溶剂: {solvent}", "🧪"))
|
||||
action_sequence.append(create_action_log(f"重置体积: {volume}mL", "💧"))
|
||||
|
||||
if vessel is None:
|
||||
action_sequence.append(create_action_log("使用默认目标容器: main_reactor", "⚙️"))
|
||||
else:
|
||||
action_sequence.append(create_action_log(f"使用指定目标容器: {vessel}", "🎯"))
|
||||
|
||||
# 1. 验证目标容器存在
|
||||
debug_print("📍 步骤1: 验证目标容器... 🔧")
|
||||
debug_print("🔍 步骤1: 验证目标容器...")
|
||||
action_sequence.append(create_action_log("正在验证目标容器...", "🔍"))
|
||||
|
||||
if target_vessel not in G.nodes():
|
||||
debug_print(f"❌ 目标容器 '{target_vessel}' 不存在于系统中! 😱")
|
||||
debug_print(f"❌ 目标容器 '{target_vessel}' 不存在于系统中!")
|
||||
action_sequence.append(create_action_log(f"目标容器 '{target_vessel}' 不存在", "❌"))
|
||||
raise ValueError(f"目标容器 '{target_vessel}' 不存在于系统中")
|
||||
debug_print(f"✅ 目标容器 '{target_vessel}' 验证通过 🎯")
|
||||
|
||||
debug_print(f"✅ 目标容器 '{target_vessel}' 验证通过")
|
||||
action_sequence.append(create_action_log(f"目标容器验证通过: {target_vessel}", "✅"))
|
||||
|
||||
# 2. 查找溶剂容器
|
||||
debug_print("📍 步骤2: 查找溶剂容器... 🔍")
|
||||
debug_print("🔍 步骤2: 查找溶剂容器...")
|
||||
action_sequence.append(create_action_log("正在查找溶剂容器...", "🔍"))
|
||||
|
||||
try:
|
||||
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}", "✅"))
|
||||
except ValueError as e:
|
||||
debug_print(f"❌ 溶剂容器查找失败: {str(e)} 😭")
|
||||
debug_print(f"❌ 溶剂容器查找失败: {str(e)}")
|
||||
action_sequence.append(create_action_log(f"溶剂容器查找失败: {str(e)}", "❌"))
|
||||
raise ValueError(f"无法找到溶剂 '{solvent}': {str(e)}")
|
||||
|
||||
# 3. 验证路径存在
|
||||
debug_print("📍 步骤3: 验证传输路径... 🛤️")
|
||||
debug_print("🔍 步骤3: 验证传输路径...")
|
||||
action_sequence.append(create_action_log("正在验证传输路径...", "🛤️"))
|
||||
|
||||
try:
|
||||
path = nx.shortest_path(G, source=solvent_vessel, target=target_vessel)
|
||||
debug_print(f"🛤️ 找到路径: {' → '.join(path)} ✅")
|
||||
debug_print(f"✅ 找到路径: {' → '.join(path)}")
|
||||
action_sequence.append(create_action_log(f"传输路径: {' → '.join(path)}", "🛤️"))
|
||||
except nx.NetworkXNoPath:
|
||||
debug_print(f"❌ 路径不可达: {solvent_vessel} → {target_vessel} 😞")
|
||||
debug_print(f"❌ 路径不可达: {solvent_vessel} → {target_vessel}")
|
||||
action_sequence.append(create_action_log(f"路径不可达: {solvent_vessel} → {target_vessel}", "❌"))
|
||||
raise ValueError(f"从溶剂容器 '{solvent_vessel}' 到目标容器 '{target_vessel}' 没有可用路径")
|
||||
|
||||
# 4. 使用pump_protocol转移溶剂
|
||||
debug_print("📍 步骤4: 转移溶剂... 🚰")
|
||||
debug_print(f" 🚛 开始转移: {solvent_vessel} → {target_vessel}")
|
||||
debug_print(f" 💧 转移体积: {volume} mL")
|
||||
debug_print("🔍 步骤4: 转移溶剂...")
|
||||
action_sequence.append(create_action_log("开始溶剂转移操作...", "🚰"))
|
||||
|
||||
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:
|
||||
debug_print(" 🔄 生成泵送协议...")
|
||||
debug_print("🔄 生成泵送协议...")
|
||||
action_sequence.append(create_action_log("正在生成泵送协议...", "🔄"))
|
||||
|
||||
pump_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent_vessel,
|
||||
@@ -181,60 +265,104 @@ def generate_reset_handling_protocol(
|
||||
)
|
||||
|
||||
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)} 个操作)", "✅"))
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f" ❌ 泵送协议生成失败: {str(e)} 😭")
|
||||
debug_print(f"❌ 泵送协议生成失败: {str(e)}")
|
||||
action_sequence.append(create_action_log(f"泵送协议生成失败: {str(e)}", "❌"))
|
||||
raise ValueError(f"生成泵协议时出错: {str(e)}")
|
||||
|
||||
# 5. 等待溶剂稳定
|
||||
debug_print("📍 步骤5: 等待溶剂稳定... ⏳")
|
||||
debug_print("🔍 步骤5: 等待溶剂稳定...")
|
||||
action_sequence.append(create_action_log("等待溶剂稳定...", "⏳"))
|
||||
|
||||
# 🕐 模拟运行时间优化
|
||||
debug_print(" ⏱️ 检查模拟运行时间限制...")
|
||||
# 模拟运行时间优化
|
||||
debug_print("⏱️ 检查模拟运行时间限制...")
|
||||
original_wait_time = 10.0 # 原始等待时间
|
||||
simulation_time_limit = 5.0 # 模拟运行时间限制:5秒
|
||||
|
||||
final_wait_time = min(original_wait_time, simulation_time_limit)
|
||||
|
||||
if original_wait_time > simulation_time_limit:
|
||||
debug_print(f" 🎮 模拟运行优化: {original_wait_time}s → {final_wait_time}s ⚡")
|
||||
debug_print(f" 📊 时间缩短: {original_wait_time}s → {final_wait_time}s 🚀")
|
||||
debug_print(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:
|
||||
debug_print(f" ✅ 时间在限制内: {final_wait_time}s 保持不变 🎯")
|
||||
debug_print(f"✅ 时间在限制内: {final_wait_time}s 保持不变")
|
||||
action_sequence.append(create_action_log(f"等待时间: {final_wait_time}s", "⏰"))
|
||||
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {
|
||||
"time": final_wait_time,
|
||||
"description": f"等待溶剂 {solvent} 稳定" + (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 ⏰✨")
|
||||
debug_print(f"✅ 稳定等待已添加: {final_wait_time}s")
|
||||
|
||||
# 显示时间调整信息
|
||||
if original_wait_time != final_wait_time:
|
||||
debug_print(f" 🎭 模拟优化说明: 原计划 {original_wait_time}s,实际模拟 {final_wait_time}s ⚡")
|
||||
debug_print(f"🎭 模拟优化说明: 原计划 {original_wait_time}s,实际模拟 {final_wait_time}s")
|
||||
action_sequence.append(create_action_log("应用模拟时间优化", "🎭"))
|
||||
|
||||
# 🎊 总结
|
||||
debug_print("🔄" * 20)
|
||||
debug_print(f"🎉 重置处理协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
debug_print(f"🧪 溶剂: {solvent}")
|
||||
debug_print(f"🥽 源容器: {solvent_vessel}")
|
||||
debug_print(f"🥽 目标容器: {target_vessel}")
|
||||
debug_print(f"💧 转移体积: {volume} mL")
|
||||
debug_print(f"⏱️ 预计总时间: {(final_wait_time + 5):.0f} 秒 ⌛")
|
||||
debug_print(f"🎯 已添加 {volume} mL {solvent} 到 {target_vessel} 🚰✨")
|
||||
debug_print("🔄" * 20)
|
||||
# 总结
|
||||
debug_print("=" * 60)
|
||||
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})"
|
||||
if vessel is None:
|
||||
summary_msg += " [默认容器]"
|
||||
else:
|
||||
summary_msg += " [指定容器]"
|
||||
|
||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
||||
|
||||
return action_sequence
|
||||
|
||||
# === 便捷函数 ===
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
# 测试函数
|
||||
def test_reset_handling_protocol():
|
||||
"""测试重置处理协议"""
|
||||
debug_print("🧪 === RESET HANDLING PROTOCOL 测试 === ✨")
|
||||
debug_print("=== 重置处理协议增强中文版测试 ===")
|
||||
|
||||
# 测试溶剂名称
|
||||
debug_print("🧪 测试常用溶剂名称...")
|
||||
@@ -242,8 +370,18 @@ def test_reset_handling_protocol():
|
||||
for solvent in test_solvents:
|
||||
debug_print(f" 🔍 测试溶剂: {solvent}")
|
||||
|
||||
debug_print("✅ 测试完成 🎉")
|
||||
|
||||
# 测试容器参数
|
||||
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__":
|
||||
test_reset_handling_protocol()
|
||||
@@ -8,7 +8,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
def debug_print(message):
|
||||
"""调试输出"""
|
||||
print(f"[RUN_COLUMN] {message}", flush=True)
|
||||
print(f"🏛️ [RUN_COLUMN] {message}", flush=True)
|
||||
logger.info(f"[RUN_COLUMN] {message}")
|
||||
|
||||
def parse_percentage(pct_str: str) -> float:
|
||||
@@ -25,7 +25,7 @@ def parse_percentage(pct_str: str) -> float:
|
||||
return 0.0
|
||||
|
||||
pct_str = pct_str.strip().lower()
|
||||
debug_print(f"解析百分比: '{pct_str}'")
|
||||
debug_print(f"🔍 解析百分比: '{pct_str}'")
|
||||
|
||||
# 移除百分号和空格
|
||||
pct_clean = re.sub(r'[%\s]', '', pct_str)
|
||||
@@ -34,7 +34,7 @@ def parse_percentage(pct_str: str) -> float:
|
||||
match = re.search(r'([0-9]*\.?[0-9]+)', pct_clean)
|
||||
if match:
|
||||
value = float(match.group(1))
|
||||
debug_print(f"百分比解析结果: {value}%")
|
||||
debug_print(f"✅ 百分比解析结果: {value}%")
|
||||
return value
|
||||
|
||||
debug_print(f"⚠️ 无法解析百分比: '{pct_str}',返回0.0")
|
||||
@@ -54,7 +54,7 @@ def parse_ratio(ratio_str: str) -> tuple:
|
||||
return (50.0, 50.0) # 默认1:1
|
||||
|
||||
ratio_str = ratio_str.strip()
|
||||
debug_print(f"解析比例: '{ratio_str}'")
|
||||
debug_print(f"🔍 解析比例: '{ratio_str}'")
|
||||
|
||||
# 支持多种分隔符:: / -
|
||||
if ':' in ratio_str:
|
||||
@@ -79,7 +79,7 @@ def parse_ratio(ratio_str: str) -> tuple:
|
||||
pct1 = (ratio1 / total) * 100
|
||||
pct2 = (ratio2 / total) * 100
|
||||
|
||||
debug_print(f"比例解析结果: {ratio1}:{ratio2} -> {pct1:.1f}%:{pct2:.1f}%")
|
||||
debug_print(f"✅ 比例解析结果: {ratio1}:{ratio2} -> {pct1:.1f}%:{pct2:.1f}%")
|
||||
return (pct1, pct2)
|
||||
except ValueError as e:
|
||||
debug_print(f"⚠️ 比例数值转换失败: {str(e)}")
|
||||
@@ -101,12 +101,12 @@ def parse_rf_value(rf_str: str) -> float:
|
||||
return 0.3 # 默认Rf值
|
||||
|
||||
rf_str = rf_str.strip().lower()
|
||||
debug_print(f"解析Rf值: '{rf_str}'")
|
||||
debug_print(f"🔍 解析Rf值: '{rf_str}'")
|
||||
|
||||
# 处理未知Rf值
|
||||
if rf_str in ['?', 'unknown', 'tbd', 'to be determined']:
|
||||
default_rf = 0.3
|
||||
debug_print(f"检测到未知Rf值,使用默认值: {default_rf}")
|
||||
debug_print(f"❓ 检测到未知Rf值,使用默认值: {default_rf}")
|
||||
return default_rf
|
||||
|
||||
# 提取数字
|
||||
@@ -117,7 +117,7 @@ def parse_rf_value(rf_str: str) -> float:
|
||||
if value > 1.0:
|
||||
value = value / 100.0 # 可能是百分比形式
|
||||
value = max(0.0, min(1.0, value)) # 限制在0-1范围
|
||||
debug_print(f"Rf值解析结果: {value}")
|
||||
debug_print(f"✅ Rf值解析结果: {value}")
|
||||
return value
|
||||
|
||||
debug_print(f"⚠️ 无法解析Rf值: '{rf_str}',使用默认值0.3")
|
||||
@@ -125,7 +125,7 @@ def parse_rf_value(rf_str: str) -> float:
|
||||
|
||||
def find_column_device(G: nx.DiGraph) -> str:
|
||||
"""查找柱层析设备"""
|
||||
debug_print("查找柱层析设备...")
|
||||
debug_print("🔍 查找柱层析设备...")
|
||||
|
||||
# 查找虚拟柱设备
|
||||
for node in G.nodes():
|
||||
@@ -133,14 +133,14 @@ def find_column_device(G: nx.DiGraph) -> str:
|
||||
node_class = node_data.get('class', '') or ''
|
||||
|
||||
if 'virtual_column' in node_class.lower() or 'column' in node_class.lower():
|
||||
debug_print(f"✅ 找到柱层析设备: {node}")
|
||||
debug_print(f"🎉 找到柱层析设备: {node} ✨")
|
||||
return node
|
||||
|
||||
# 如果没有找到,尝试创建虚拟设备名称
|
||||
possible_names = ['column_1', 'virtual_column_1', 'chromatography_column_1']
|
||||
for name in possible_names:
|
||||
if name in G.nodes():
|
||||
debug_print(f"✅ 找到柱设备: {name}")
|
||||
debug_print(f"🎉 找到柱设备: {name} ✨")
|
||||
return name
|
||||
|
||||
debug_print("⚠️ 未找到柱层析设备,将使用pump protocol直接转移")
|
||||
@@ -148,13 +148,13 @@ def find_column_device(G: nx.DiGraph) -> str:
|
||||
|
||||
def find_column_vessel(G: nx.DiGraph, column: str) -> str:
|
||||
"""查找柱容器"""
|
||||
debug_print(f"查找柱容器: '{column}'")
|
||||
debug_print(f"🔍 查找柱容器: '{column}'")
|
||||
|
||||
# 直接检查column参数是否是容器
|
||||
if column in G.nodes():
|
||||
node_type = G.nodes[column].get('type', '')
|
||||
if node_type == 'container':
|
||||
debug_print(f"✅ 找到柱容器: {column}")
|
||||
debug_print(f"🎉 找到柱容器: {column} ✨")
|
||||
return column
|
||||
|
||||
# 尝试常见的命名规则
|
||||
@@ -174,7 +174,7 @@ def find_column_vessel(G: nx.DiGraph, column: str) -> str:
|
||||
if vessel_name in G.nodes():
|
||||
node_type = G.nodes[vessel_name].get('type', '')
|
||||
if node_type == 'container':
|
||||
debug_print(f"✅ 找到柱容器: {vessel_name}")
|
||||
debug_print(f"🎉 找到柱容器: {vessel_name} ✨")
|
||||
return vessel_name
|
||||
|
||||
debug_print(f"⚠️ 未找到柱容器,将直接在源容器中进行分离")
|
||||
@@ -186,7 +186,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
return ""
|
||||
|
||||
solvent = solvent.strip().replace(' ', '_').lower()
|
||||
debug_print(f"查找溶剂容器: '{solvent}'")
|
||||
debug_print(f"🔍 查找溶剂容器: '{solvent}'")
|
||||
|
||||
# 🔧 方法1:直接搜索 data.reagent_name
|
||||
for node in G.nodes():
|
||||
@@ -200,16 +200,16 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
|
||||
# 检查 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})")
|
||||
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})")
|
||||
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})")
|
||||
debug_print(f"🎉 通过config.reagent模糊匹配到溶剂容器: {node} (reagent: {reagent_config}) ✨")
|
||||
return node
|
||||
|
||||
# 🔧 方法2:常见的溶剂容器命名规则
|
||||
@@ -227,7 +227,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
if vessel_name in G.nodes():
|
||||
node_type = G.nodes[vessel_name].get('type', '')
|
||||
if node_type == 'container':
|
||||
debug_print(f"✅ 通过命名规则找到溶剂容器: {vessel_name}")
|
||||
debug_print(f"🎉 通过命名规则找到溶剂容器: {vessel_name} ✨")
|
||||
return vessel_name
|
||||
|
||||
# 🔧 方法3:节点名称模糊匹配
|
||||
@@ -235,7 +235,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
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}")
|
||||
debug_print(f"🎉 通过节点名称模糊匹配到溶剂容器: {node} ✨")
|
||||
return node
|
||||
|
||||
# 🔧 方法4:特殊溶剂名称映射
|
||||
@@ -253,84 +253,118 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
|
||||
# 查找映射的同义词
|
||||
for canonical_name, synonyms in solvent_mapping.items():
|
||||
if solvent in synonyms:
|
||||
debug_print(f"检测到溶剂同义词: '{solvent}' -> '{canonical_name}'")
|
||||
debug_print(f"🔍 检测到溶剂同义词: '{solvent}' -> '{canonical_name}'")
|
||||
return find_solvent_vessel(G, canonical_name) # 递归搜索
|
||||
|
||||
debug_print(f"⚠️ 未找到溶剂 '{solvent}' 的容器")
|
||||
return ""
|
||||
|
||||
def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
|
||||
"""获取容器中的液体体积 - 增强版"""
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"⚠️ 节点 '{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
|
||||
|
||||
node_type = G.nodes[vessel].get('type', '')
|
||||
vessel_data = G.nodes[vessel].get('data', {})
|
||||
vessel_data = vessel["data"]
|
||||
vessel_id = vessel.get("id", "unknown")
|
||||
|
||||
debug_print(f"读取节点 '{vessel}' (类型: {node_type}) 体积数据: {vessel_data}")
|
||||
debug_print(f"🔍 读取容器 '{vessel_id}' 体积数据: {vessel_data}")
|
||||
|
||||
# 🔧 如果是设备类型,尝试查找关联的容器
|
||||
if node_type == 'device':
|
||||
debug_print(f"'{vessel}' 是设备,尝试查找关联容器...")
|
||||
# 检查liquid_volume字段
|
||||
if "liquid_volume" in vessel_data:
|
||||
liquid_volume = vessel_data["liquid_volume"]
|
||||
|
||||
# 查找是否有内置容器数据
|
||||
config_data = G.nodes[vessel].get('config', {})
|
||||
if 'volume' in config_data:
|
||||
default_volume = config_data.get('volume', 50.0)
|
||||
debug_print(f"使用设备默认容量: {default_volume}mL")
|
||||
return default_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)
|
||||
|
||||
# 对于旋蒸等设备,使用默认值
|
||||
if 'rotavap' in vessel.lower():
|
||||
default_volume = 50.0
|
||||
debug_print(f"旋蒸设备使用默认容量: {default_volume}mL")
|
||||
return default_volume
|
||||
|
||||
debug_print(f"⚠️ 设备 '{vessel}' 无法确定容量,返回0")
|
||||
return 0.0
|
||||
# 处理直接数值格式
|
||||
elif isinstance(liquid_volume, (int, float)):
|
||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {liquid_volume}mL (数值格式)")
|
||||
return float(liquid_volume)
|
||||
|
||||
# 🔧 如果是容器类型,正常读取体积
|
||||
total_volume = 0.0
|
||||
|
||||
# 方法1:检查液体列表
|
||||
liquids = vessel_data.get('liquid', [])
|
||||
if isinstance(liquids, list):
|
||||
for liquid in liquids:
|
||||
if isinstance(liquid, dict):
|
||||
volume = liquid.get('volume') or liquid.get('liquid_volume', 0.0)
|
||||
total_volume += volume
|
||||
|
||||
# 方法2:检查直接体积字段
|
||||
if total_volume == 0.0:
|
||||
volume_keys = ['current_volume', 'total_volume', 'volume', 'liquid_volume']
|
||||
for key in volume_keys:
|
||||
if key in vessel_data:
|
||||
try:
|
||||
total_volume = float(vessel_data[key])
|
||||
if total_volume > 0:
|
||||
break
|
||||
except (ValueError, TypeError):
|
||||
continue
|
||||
|
||||
# 方法3:检查配置中的初始体积
|
||||
if total_volume == 0.0:
|
||||
config_data = G.nodes[vessel].get('config', {})
|
||||
if 'current_volume' in config_data:
|
||||
# 检查其他可能的体积字段
|
||||
volume_keys = ['current_volume', 'total_volume', 'volume']
|
||||
for key in volume_keys:
|
||||
if key in vessel_data:
|
||||
try:
|
||||
total_volume = float(config_data['current_volume'])
|
||||
volume = float(vessel_data[key])
|
||||
if volume > 0:
|
||||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (字段: {key})")
|
||||
return volume
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
continue
|
||||
|
||||
debug_print(f"容器 '{vessel}' 总体积: {total_volume}mL")
|
||||
return total_volume
|
||||
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:
|
||||
"""根据百分比计算溶剂体积"""
|
||||
volume1 = (total_volume * pct1) / 100.0
|
||||
volume2 = (total_volume * pct2) / 100.0
|
||||
|
||||
debug_print(f"溶剂体积计算: 总体积{total_volume}mL")
|
||||
debug_print(f"🧮 溶剂体积计算: 总体积{total_volume}mL")
|
||||
debug_print(f" - 溶剂1: {pct1}% = {volume1}mL")
|
||||
debug_print(f" - 溶剂2: {pct2}% = {volume2}mL")
|
||||
|
||||
@@ -338,8 +372,8 @@ def calculate_solvent_volumes(total_volume: float, pct1: float, pct2: float) ->
|
||||
|
||||
def generate_run_column_protocol(
|
||||
G: nx.DiGraph,
|
||||
from_vessel: str,
|
||||
to_vessel: str,
|
||||
from_vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
to_vessel: dict, # 🔧 修改:从字符串改为字典类型
|
||||
column: str,
|
||||
rf: str = "",
|
||||
pct1: str = "",
|
||||
@@ -350,14 +384,12 @@ def generate_run_column_protocol(
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成柱层析分离的协议序列 - 增强版
|
||||
|
||||
支持新版XDL的所有参数,具有高兼容性和容错性
|
||||
生成柱层析分离的协议序列 - 支持vessel字典和体积运算
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为设备和容器,边为流体管道
|
||||
from_vessel: 源容器的名称,即样品起始所在的容器(必需)
|
||||
to_vessel: 目标容器的名称,分离后的样品要到达的容器(必需)
|
||||
from_vessel: 源容器字典(从XDL传入)
|
||||
to_vessel: 目标容器字典(从XDL传入)
|
||||
column: 所使用的柱子的名称(必需)
|
||||
rf: Rf值(可选,支持 "?" 表示未知)
|
||||
pct1: 第一种溶剂百分比(如 "40 %",可选)
|
||||
@@ -371,51 +403,60 @@ def generate_run_column_protocol(
|
||||
List[Dict[str, Any]]: 柱层析分离操作的动作序列
|
||||
"""
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("开始生成柱层析协议")
|
||||
debug_print(f"输入参数:")
|
||||
debug_print(f" - from_vessel: '{from_vessel}'")
|
||||
debug_print(f" - to_vessel: '{to_vessel}'")
|
||||
debug_print(f" - column: '{column}'")
|
||||
debug_print(f" - rf: '{rf}'")
|
||||
debug_print(f" - pct1: '{pct1}'")
|
||||
debug_print(f" - pct2: '{pct2}'")
|
||||
debug_print(f" - solvent1: '{solvent1}'")
|
||||
debug_print(f" - solvent2: '{solvent2}'")
|
||||
debug_print(f" - ratio: '{ratio}'")
|
||||
debug_print(f" - 其他参数: {kwargs}")
|
||||
debug_print("=" * 60)
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
from_vessel_id = from_vessel["id"]
|
||||
to_vessel_id = to_vessel["id"]
|
||||
|
||||
debug_print("🏛️" * 20)
|
||||
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 = []
|
||||
|
||||
# === 参数验证 ===
|
||||
debug_print("步骤1: 参数验证...")
|
||||
# 🔧 新增:记录柱层析前的容器状态
|
||||
debug_print("🔍 记录柱层析前容器状态...")
|
||||
original_from_volume = get_vessel_liquid_volume(from_vessel)
|
||||
original_to_volume = get_vessel_liquid_volume(to_vessel)
|
||||
|
||||
if not from_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: # 🔧 使用 from_vessel_id
|
||||
raise ValueError("from_vessel 参数不能为空")
|
||||
if not to_vessel:
|
||||
if not to_vessel_id: # 🔧 使用 to_vessel_id
|
||||
raise ValueError("to_vessel 参数不能为空")
|
||||
if not column:
|
||||
raise ValueError("column 参数不能为空")
|
||||
|
||||
if from_vessel not in G.nodes():
|
||||
raise ValueError(f"源容器 '{from_vessel}' 不存在于系统中")
|
||||
if to_vessel not in G.nodes():
|
||||
raise ValueError(f"目标容器 '{to_vessel}' 不存在于系统中")
|
||||
if from_vessel_id not in G.nodes(): # 🔧 使用 from_vessel_id
|
||||
raise ValueError(f"源容器 '{from_vessel_id}' 不存在于系统中")
|
||||
if to_vessel_id not in G.nodes(): # 🔧 使用 to_vessel_id
|
||||
raise ValueError(f"目标容器 '{to_vessel_id}' 不存在于系统中")
|
||||
|
||||
debug_print("✅ 基本参数验证通过")
|
||||
|
||||
# === 参数解析 ===
|
||||
debug_print("步骤2: 参数解析...")
|
||||
debug_print("📍 步骤2: 参数解析...")
|
||||
|
||||
# 解析Rf值
|
||||
final_rf = parse_rf_value(rf)
|
||||
debug_print(f"最终Rf值: {final_rf}")
|
||||
debug_print(f"🎯 最终Rf值: {final_rf}")
|
||||
|
||||
# 解析溶剂比例(ratio优先级高于pct1/pct2)
|
||||
if ratio and ratio.strip():
|
||||
final_pct1, final_pct2 = parse_ratio(ratio)
|
||||
debug_print(f"使用ratio参数: {final_pct1:.1f}% : {final_pct2:.1f}%")
|
||||
debug_print(f"📊 使用ratio参数: {final_pct1:.1f}% : {final_pct2:.1f}%")
|
||||
else:
|
||||
final_pct1 = parse_percentage(pct1) if pct1 else 50.0
|
||||
final_pct2 = parse_percentage(pct2) if pct2 else 50.0
|
||||
@@ -428,16 +469,16 @@ def generate_run_column_protocol(
|
||||
final_pct1 = (final_pct1 / total_pct) * 100
|
||||
final_pct2 = (final_pct2 / total_pct) * 100
|
||||
|
||||
debug_print(f"使用百分比参数: {final_pct1:.1f}% : {final_pct2:.1f}%")
|
||||
debug_print(f"📊 使用百分比参数: {final_pct1:.1f}% : {final_pct2:.1f}%")
|
||||
|
||||
# 设置默认溶剂(如果未指定)
|
||||
final_solvent1 = solvent1.strip() if solvent1 else "ethyl_acetate"
|
||||
final_solvent2 = solvent2.strip() if solvent2 else "hexane"
|
||||
|
||||
debug_print(f"最终溶剂: {final_solvent1} : {final_solvent2}")
|
||||
debug_print(f"🧪 最终溶剂: {final_solvent1} : {final_solvent2}")
|
||||
|
||||
# === 查找设备和容器 ===
|
||||
debug_print("步骤3: 查找设备和容器...")
|
||||
debug_print("📍 步骤3: 查找设备和容器...")
|
||||
|
||||
# 查找柱层析设备
|
||||
column_device_id = find_column_device(G)
|
||||
@@ -449,16 +490,16 @@ def generate_run_column_protocol(
|
||||
solvent1_vessel = find_solvent_vessel(G, final_solvent1)
|
||||
solvent2_vessel = find_solvent_vessel(G, final_solvent2)
|
||||
|
||||
debug_print(f"设备映射:")
|
||||
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: 获取源容器体积...")
|
||||
debug_print("📍 步骤4: 获取源容器体积...")
|
||||
|
||||
source_volume = get_vessel_liquid_volume(G, from_vessel)
|
||||
source_volume = original_from_volume
|
||||
if source_volume <= 0:
|
||||
source_volume = 50.0 # 默认体积
|
||||
debug_print(f"⚠️ 无法获取源容器体积,使用默认值: {source_volume}mL")
|
||||
@@ -466,7 +507,7 @@ def generate_run_column_protocol(
|
||||
debug_print(f"✅ 源容器体积: {source_volume}mL")
|
||||
|
||||
# === 计算溶剂体积 ===
|
||||
debug_print("步骤5: 计算溶剂体积...")
|
||||
debug_print("📍 步骤5: 计算溶剂体积...")
|
||||
|
||||
# 洗脱溶剂通常是样品体积的2-5倍
|
||||
total_elution_volume = source_volume * 3.0
|
||||
@@ -475,17 +516,22 @@ def generate_run_column_protocol(
|
||||
)
|
||||
|
||||
# === 执行柱层析流程 ===
|
||||
debug_print("步骤6: 执行柱层析流程...")
|
||||
debug_print("📍 步骤6: 执行柱层析流程...")
|
||||
|
||||
# 🔧 新增:体积变化跟踪变量
|
||||
current_from_volume = source_volume
|
||||
current_to_volume = original_to_volume
|
||||
current_column_volume = 0.0
|
||||
|
||||
try:
|
||||
# 步骤6.1: 样品上柱(如果有独立的柱容器)
|
||||
if column_vessel and column_vessel != from_vessel:
|
||||
debug_print(f"6.1: 样品上柱 - {source_volume}mL 从 {from_vessel} 到 {column_vessel}")
|
||||
if column_vessel and column_vessel != from_vessel_id: # 🔧 使用 from_vessel_id
|
||||
debug_print(f"📍 6.1: 样品上柱 - {source_volume}mL 从 {from_vessel_id} 到 {column_vessel}")
|
||||
|
||||
try:
|
||||
sample_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=from_vessel,
|
||||
from_vessel=from_vessel_id, # 🔧 使用 from_vessel_id
|
||||
to_vessel=column_vessel,
|
||||
volume=source_volume,
|
||||
flowrate=1.0, # 慢速上柱
|
||||
@@ -496,15 +542,29 @@ def generate_run_column_protocol(
|
||||
)
|
||||
action_sequence.extend(sample_transfer_actions)
|
||||
debug_print(f"✅ 样品上柱完成,添加了 {len(sample_transfer_actions)} 个动作")
|
||||
|
||||
# 🔧 新增:更新体积 - 样品转移到柱上
|
||||
current_from_volume = 0.0 # 源容器体积变为0
|
||||
current_column_volume = source_volume # 柱容器体积增加
|
||||
|
||||
update_vessel_volume(from_vessel, G, current_from_volume, "样品上柱后,源容器清空")
|
||||
|
||||
# 如果柱容器在图中,也更新其体积
|
||||
if column_vessel in G.nodes():
|
||||
if 'data' not in G.nodes[column_vessel]:
|
||||
G.nodes[column_vessel]['data'] = {}
|
||||
G.nodes[column_vessel]['data']['liquid_volume'] = current_column_volume
|
||||
debug_print(f"📊 柱容器 '{column_vessel}' 体积更新为: {current_column_volume:.2f}mL")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"⚠️ 样品上柱失败: {str(e)}")
|
||||
|
||||
# 步骤6.2: 添加洗脱溶剂1(如果有溶剂容器)
|
||||
if solvent1_vessel and solvent1_volume > 0:
|
||||
debug_print(f"6.2: 添加洗脱溶剂1 - {solvent1_volume:.1f}mL {final_solvent1}")
|
||||
debug_print(f"📍 6.2: 添加洗脱溶剂1 - {solvent1_volume:.1f}mL {final_solvent1}")
|
||||
|
||||
try:
|
||||
target_vessel = column_vessel if column_vessel else from_vessel
|
||||
target_vessel = column_vessel if column_vessel else from_vessel_id # 🔧 使用 from_vessel_id
|
||||
solvent1_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent1_vessel,
|
||||
@@ -515,15 +575,26 @@ def generate_run_column_protocol(
|
||||
)
|
||||
action_sequence.extend(solvent1_transfer_actions)
|
||||
debug_print(f"✅ 溶剂1添加完成,添加了 {len(solvent1_transfer_actions)} 个动作")
|
||||
|
||||
# 🔧 新增:更新体积 - 添加溶剂1
|
||||
if target_vessel == column_vessel:
|
||||
current_column_volume += solvent1_volume
|
||||
if column_vessel in G.nodes():
|
||||
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:
|
||||
current_from_volume += solvent1_volume
|
||||
update_vessel_volume(from_vessel, G, current_from_volume, "添加溶剂1后")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"⚠️ 溶剂1添加失败: {str(e)}")
|
||||
|
||||
# 步骤6.3: 添加洗脱溶剂2(如果有溶剂容器)
|
||||
if solvent2_vessel and solvent2_volume > 0:
|
||||
debug_print(f"6.3: 添加洗脱溶剂2 - {solvent2_volume:.1f}mL {final_solvent2}")
|
||||
debug_print(f"📍 6.3: 添加洗脱溶剂2 - {solvent2_volume:.1f}mL {final_solvent2}")
|
||||
|
||||
try:
|
||||
target_vessel = column_vessel if column_vessel else from_vessel
|
||||
target_vessel = column_vessel if column_vessel else from_vessel_id # 🔧 使用 from_vessel_id
|
||||
solvent2_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent2_vessel,
|
||||
@@ -534,19 +605,30 @@ def generate_run_column_protocol(
|
||||
)
|
||||
action_sequence.extend(solvent2_transfer_actions)
|
||||
debug_print(f"✅ 溶剂2添加完成,添加了 {len(solvent2_transfer_actions)} 个动作")
|
||||
|
||||
# 🔧 新增:更新体积 - 添加溶剂2
|
||||
if target_vessel == column_vessel:
|
||||
current_column_volume += solvent2_volume
|
||||
if column_vessel in G.nodes():
|
||||
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:
|
||||
current_from_volume += solvent2_volume
|
||||
update_vessel_volume(from_vessel, G, current_from_volume, "添加溶剂2后")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"⚠️ 溶剂2添加失败: {str(e)}")
|
||||
|
||||
# 步骤6.4: 使用柱层析设备执行分离(如果有设备)
|
||||
if column_device_id:
|
||||
debug_print(f"6.4: 使用柱层析设备执行分离")
|
||||
debug_print(f"📍 6.4: 使用柱层析设备执行分离")
|
||||
|
||||
column_separation_action = {
|
||||
"device_id": column_device_id,
|
||||
"action_name": "run_column",
|
||||
"action_kwargs": {
|
||||
"from_vessel": from_vessel,
|
||||
"to_vessel": to_vessel,
|
||||
"from_vessel": from_vessel_id, # 🔧 使用 from_vessel_id
|
||||
"to_vessel": to_vessel_id, # 🔧 使用 to_vessel_id
|
||||
"column": column,
|
||||
"rf": rf,
|
||||
"pct1": pct1,
|
||||
@@ -560,7 +642,7 @@ def generate_run_column_protocol(
|
||||
debug_print(f"✅ 柱层析设备动作已添加")
|
||||
|
||||
# 等待分离完成
|
||||
separation_time = max(30, int(total_elution_volume / 2)) # 基于体积估算时间
|
||||
separation_time = max(30, min(120, int(total_elution_volume / 2))) # 30-120秒,基于体积
|
||||
action_sequence.append({
|
||||
"action_name": "wait",
|
||||
"action_kwargs": {"time": separation_time}
|
||||
@@ -568,41 +650,61 @@ def generate_run_column_protocol(
|
||||
debug_print(f"✅ 等待分离完成: {separation_time}秒")
|
||||
|
||||
# 步骤6.5: 产物收集(从柱容器到目标容器)
|
||||
if column_vessel and column_vessel != to_vessel:
|
||||
debug_print(f"6.5: 产物收集 - 从 {column_vessel} 到 {to_vessel}")
|
||||
if column_vessel and column_vessel != to_vessel_id: # 🔧 使用 to_vessel_id
|
||||
debug_print(f"📍 6.5: 产物收集 - 从 {column_vessel} 到 {to_vessel_id}")
|
||||
|
||||
try:
|
||||
# 估算产物体积(原始样品体积的70-90%)
|
||||
# 估算产物体积(原始样品体积的70-90%,收率考虑)
|
||||
product_volume = source_volume * 0.8
|
||||
|
||||
product_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=column_vessel,
|
||||
to_vessel=to_vessel,
|
||||
to_vessel=to_vessel_id, # 🔧 使用 to_vessel_id
|
||||
volume=product_volume,
|
||||
flowrate=1.5,
|
||||
transfer_flowrate=0.8
|
||||
)
|
||||
action_sequence.extend(product_transfer_actions)
|
||||
debug_print(f"✅ 产物收集完成,添加了 {len(product_transfer_actions)} 个动作")
|
||||
|
||||
# 🔧 新增:更新体积 - 产物收集到目标容器
|
||||
current_to_volume += product_volume
|
||||
current_column_volume -= product_volume # 柱容器体积减少
|
||||
|
||||
update_vessel_volume(to_vessel, G, current_to_volume, "产物收集后")
|
||||
|
||||
# 更新柱容器体积
|
||||
if column_vessel in G.nodes():
|
||||
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:
|
||||
debug_print(f"⚠️ 产物收集失败: {str(e)}")
|
||||
|
||||
# 步骤6.6: 如果没有独立的柱设备和容器,执行简化的直接转移
|
||||
if not column_device_id and not column_vessel:
|
||||
debug_print(f"6.6: 简化模式 - 直接转移 {source_volume}mL 从 {from_vessel} 到 {to_vessel}")
|
||||
debug_print(f"📍 6.6: 简化模式 - 直接转移 {source_volume}mL 从 {from_vessel_id} 到 {to_vessel_id}")
|
||||
|
||||
try:
|
||||
direct_transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=from_vessel,
|
||||
to_vessel=to_vessel,
|
||||
from_vessel=from_vessel_id, # 🔧 使用 from_vessel_id
|
||||
to_vessel=to_vessel_id, # 🔧 使用 to_vessel_id
|
||||
volume=source_volume,
|
||||
flowrate=2.0,
|
||||
transfer_flowrate=1.0
|
||||
)
|
||||
action_sequence.extend(direct_transfer_actions)
|
||||
debug_print(f"✅ 直接转移完成,添加了 {len(direct_transfer_actions)} 个动作")
|
||||
|
||||
# 🔧 新增:更新体积 - 直接转移
|
||||
current_from_volume = 0.0 # 源容器清空
|
||||
current_to_volume += source_volume # 目标容器增加
|
||||
|
||||
update_vessel_volume(from_vessel, G, current_from_volume, "直接转移后,源容器清空")
|
||||
update_vessel_volume(to_vessel, G, current_to_volume, "直接转移后,目标容器增加")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"⚠️ 直接转移失败: {str(e)}")
|
||||
|
||||
@@ -624,17 +726,77 @@ def generate_run_column_protocol(
|
||||
}
|
||||
})
|
||||
|
||||
# 🔧 新增:柱层析完成后的最终状态报告
|
||||
final_from_volume = get_vessel_liquid_volume(from_vessel)
|
||||
final_to_volume = get_vessel_liquid_volume(to_vessel)
|
||||
|
||||
# 🎊 总结
|
||||
debug_print("🧪" * 20)
|
||||
debug_print("🏛️" * 20)
|
||||
debug_print(f"🎉 柱层析协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
debug_print(f"🥽 路径: {from_vessel} → {to_vessel}")
|
||||
debug_print(f"🥽 路径: {from_vessel_id} → {to_vessel_id}")
|
||||
debug_print(f"🏛️ 柱子: {column}")
|
||||
debug_print(f"🧪 溶剂: {final_solvent1}:{final_solvent2}")
|
||||
debug_print("🧪" * 20)
|
||||
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
|
||||
|
||||
# 🔧 新增:便捷函数
|
||||
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]]:
|
||||
"""乙酸乙酯-己烷柱层析(常用组合)"""
|
||||
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,
|
||||
solvent1="ethyl_acetate", solvent2="hexane", ratio=ratio)
|
||||
|
||||
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]]:
|
||||
"""甲醇-二氯甲烷柱层析"""
|
||||
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,
|
||||
solvent1="methanol", solvent2="dichloromethane", ratio=ratio)
|
||||
|
||||
def generate_gradient_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
||||
column: str, start_ratio: str = "10:90",
|
||||
end_ratio: str = "50:50") -> 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} ({start_ratio} → {end_ratio})")
|
||||
# 使用中间比例作为近似
|
||||
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,
|
||||
column: str) -> 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}")
|
||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
||||
solvent1="ethyl_acetate", solvent2="hexane", ratio="70:30")
|
||||
|
||||
def generate_nonpolar_column_protocol(G: nx.DiGraph, from_vessel: dict, to_vessel: dict,
|
||||
column: str) -> 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}")
|
||||
return generate_run_column_protocol(G, from_vessel, to_vessel, column,
|
||||
solvent1="ethyl_acetate", solvent2="hexane", ratio="5:95")
|
||||
|
||||
# 测试函数
|
||||
def test_run_column_protocol():
|
||||
"""测试柱层析协议"""
|
||||
|
||||
@@ -20,18 +20,18 @@ def debug_print(message):
|
||||
try:
|
||||
# 确保消息是字符串格式
|
||||
safe_message = str(message)
|
||||
print(f"[分离协议] {safe_message}", flush=True)
|
||||
logger.info(f"[分离协议] {safe_message}")
|
||||
print(f"🌀 [SEPARATE] {safe_message}", flush=True)
|
||||
logger.info(f"[SEPARATE] {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}")
|
||||
print(f"🌀 [SEPARATE] {safe_message}", flush=True)
|
||||
logger.info(f"[SEPARATE] {safe_message}")
|
||||
except Exception as e:
|
||||
# 最后的安全措施
|
||||
fallback_message = f"日志输出错误: {repr(message)}"
|
||||
print(f"[分离协议] {fallback_message}", flush=True)
|
||||
logger.info(f"[分离协议] {fallback_message}")
|
||||
print(f"🌀 [SEPARATE] {fallback_message}", flush=True)
|
||||
logger.info(f"[SEPARATE] {fallback_message}")
|
||||
|
||||
def create_action_log(message: str, emoji: str = "📝") -> Dict[str, Any]:
|
||||
"""创建一个动作日志 - 支持中文和emoji"""
|
||||
@@ -264,19 +264,119 @@ def find_connected_stirrer(G: nx.DiGraph, vessel: str) -> str:
|
||||
debug_print("❌ 未找到搅拌器")
|
||||
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 generate_separate_protocol(
|
||||
G: nx.DiGraph,
|
||||
# 🔧 基础参数,支持XDL的vessel参数
|
||||
vessel: str = "", # XDL: 分离容器
|
||||
purpose: str = "separate", # 分离目的
|
||||
product_phase: str = "top", # 产物相
|
||||
vessel: dict = None, # 🔧 修改:从字符串改为字典类型
|
||||
purpose: str = "separate", # 分离目的
|
||||
product_phase: str = "top", # 产物相
|
||||
# 🔧 可选的详细参数
|
||||
from_vessel: str = "", # 源容器(通常在separate前已经transfer了)
|
||||
separation_vessel: str = "", # 分离容器(与vessel同义)
|
||||
to_vessel: str = "", # 目标容器(可选)
|
||||
waste_phase_to_vessel: str = "", # 废相目标容器
|
||||
product_vessel: str = "", # XDL: 产物容器(与to_vessel同义)
|
||||
waste_vessel: str = "", # XDL: 废液容器(与waste_phase_to_vessel同义)
|
||||
from_vessel: Union[str, dict] = "", # 源容器(通常在separate前已经transfer了)
|
||||
separation_vessel: Union[str, dict] = "", # 分离容器(与vessel同义)
|
||||
to_vessel: Union[str, dict] = "", # 目标容器(可选)
|
||||
waste_phase_to_vessel: Union[str, dict] = "", # 废相目标容器
|
||||
product_vessel: Union[str, dict] = "", # XDL: 产物容器(与to_vessel同义)
|
||||
waste_vessel: Union[str, dict] = "", # XDL: 废液容器(与waste_phase_to_vessel同义)
|
||||
# 🔧 溶剂相关参数
|
||||
solvent: str = "", # 溶剂名称
|
||||
solvent_volume: Union[str, float] = 0.0, # 溶剂体积
|
||||
@@ -290,10 +390,10 @@ def generate_separate_protocol(
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成分离操作的协议序列 - 增强中文版
|
||||
生成分离操作的协议序列 - 支持vessel字典和体积运算
|
||||
|
||||
支持XDL参数格式:
|
||||
- vessel: 分离容器(必需)
|
||||
- vessel: 分离容器字典(必需)
|
||||
- purpose: "wash", "extract", "separate"
|
||||
- product_phase: "top", "bottom"
|
||||
- product_vessel: 产物收集容器
|
||||
@@ -310,10 +410,20 @@ def generate_separate_protocol(
|
||||
5. 重复指定次数
|
||||
"""
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("🧪 开始生成分离协议 - 增强中文版")
|
||||
debug_print(f"📋 原始参数:")
|
||||
debug_print(f" 🥼 容器: '{vessel}'")
|
||||
# 🔧 核心修改:vessel参数兼容处理
|
||||
if vessel is None:
|
||||
if isinstance(separation_vessel, dict):
|
||||
vessel = separation_vessel
|
||||
else:
|
||||
raise ValueError("必须提供vessel字典参数")
|
||||
|
||||
# 🔧 核心修改:从字典中提取容器ID
|
||||
vessel_id = vessel["id"]
|
||||
|
||||
debug_print("🌀" * 20)
|
||||
debug_print("🚀 开始生成分离协议(支持vessel字典和体积运算)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel} (ID: {vessel_id})")
|
||||
debug_print(f" 🎯 分离目的: '{purpose}'")
|
||||
debug_print(f" 📊 产物相: '{product_phase}'")
|
||||
debug_print(f" 💧 溶剂: '{solvent}'")
|
||||
@@ -322,24 +432,33 @@ def generate_separate_protocol(
|
||||
debug_print(f" 🎯 产物容器: '{product_vessel}'")
|
||||
debug_print(f" 🗑️ 废液容器: '{waste_vessel}'")
|
||||
debug_print(f" 📦 其他参数: {kwargs}")
|
||||
debug_print("=" * 60)
|
||||
debug_print("🌀" * 20)
|
||||
|
||||
action_sequence = []
|
||||
|
||||
# 🔧 新增:记录分离前的容器状态
|
||||
debug_print("🔍 记录分离前容器状态...")
|
||||
original_liquid_volume = get_vessel_liquid_volume(vessel)
|
||||
debug_print(f"📊 分离前液体体积: {original_liquid_volume:.2f}mL")
|
||||
|
||||
# === 参数验证和标准化 ===
|
||||
debug_print("🔍 步骤1: 参数验证和标准化...")
|
||||
action_sequence.append(create_action_log(f"开始分离操作 - 容器: {vessel}", "🎬"))
|
||||
action_sequence.append(create_action_log(f"开始分离操作 - 容器: {vessel_id}", "🎬"))
|
||||
action_sequence.append(create_action_log(f"分离目的: {purpose}", "🧪"))
|
||||
action_sequence.append(create_action_log(f"产物相: {product_phase}", "📊"))
|
||||
|
||||
# 统一容器参数
|
||||
final_vessel = vessel or separation_vessel
|
||||
if not final_vessel:
|
||||
debug_print("❌ 必须指定分离容器")
|
||||
raise ValueError("必须指定分离容器 (vessel 或 separation_vessel)")
|
||||
# 统一容器参数 - 支持字典和字符串
|
||||
def extract_vessel_id(vessel_param):
|
||||
if isinstance(vessel_param, dict):
|
||||
return vessel_param.get("id", "")
|
||||
elif isinstance(vessel_param, str):
|
||||
return vessel_param
|
||||
else:
|
||||
return ""
|
||||
|
||||
final_to_vessel = to_vessel or product_vessel
|
||||
final_waste_vessel = waste_phase_to_vessel or waste_vessel
|
||||
final_vessel_id = vessel_id
|
||||
final_to_vessel_id = extract_vessel_id(to_vessel) or extract_vessel_id(product_vessel)
|
||||
final_waste_vessel_id = extract_vessel_id(waste_phase_to_vessel) or extract_vessel_id(waste_vessel)
|
||||
|
||||
# 统一体积参数
|
||||
final_volume = parse_volume_input(volume or solvent_volume)
|
||||
@@ -350,13 +469,13 @@ def generate_separate_protocol(
|
||||
debug_print(f"⚠️ 重复次数参数 <= 0,自动设置为 1")
|
||||
|
||||
debug_print(f"🔧 标准化后的参数:")
|
||||
debug_print(f" 🥼 分离容器: '{final_vessel}'")
|
||||
debug_print(f" 🎯 产物容器: '{final_to_vessel}'")
|
||||
debug_print(f" 🗑️ 废液容器: '{final_waste_vessel}'")
|
||||
debug_print(f" 🥼 分离容器: '{final_vessel_id}'")
|
||||
debug_print(f" 🎯 产物容器: '{final_to_vessel_id}'")
|
||||
debug_print(f" 🗑️ 废液容器: '{final_waste_vessel_id}'")
|
||||
debug_print(f" 📏 溶剂体积: {final_volume}mL")
|
||||
debug_print(f" 🔄 重复次数: {repeats}")
|
||||
|
||||
action_sequence.append(create_action_log(f"分离容器: {final_vessel}", "🧪"))
|
||||
action_sequence.append(create_action_log(f"分离容器: {final_vessel_id}", "🧪"))
|
||||
action_sequence.append(create_action_log(f"溶剂体积: {final_volume}mL", "📏"))
|
||||
action_sequence.append(create_action_log(f"重复次数: {repeats}", "🔄"))
|
||||
|
||||
@@ -382,7 +501,7 @@ def generate_separate_protocol(
|
||||
action_sequence.append(create_action_log("正在查找相关设备...", "🔍"))
|
||||
|
||||
# 查找分离器设备
|
||||
separator_device = find_separator_device(G, final_vessel)
|
||||
separator_device = find_separator_device(G, final_vessel_id) # 🔧 使用 final_vessel_id
|
||||
if separator_device:
|
||||
action_sequence.append(create_action_log(f"找到分离器设备: {separator_device}", "🧪"))
|
||||
else:
|
||||
@@ -390,7 +509,7 @@ def generate_separate_protocol(
|
||||
action_sequence.append(create_action_log("未找到分离器设备", "⚠️"))
|
||||
|
||||
# 查找搅拌器
|
||||
stirrer_device = find_connected_stirrer(G, final_vessel)
|
||||
stirrer_device = find_connected_stirrer(G, final_vessel_id) # 🔧 使用 final_vessel_id
|
||||
if stirrer_device:
|
||||
action_sequence.append(create_action_log(f"找到搅拌器: {stirrer_device}", "🌪️"))
|
||||
else:
|
||||
@@ -414,6 +533,9 @@ def generate_separate_protocol(
|
||||
debug_print("🔍 步骤3: 执行分离流程...")
|
||||
action_sequence.append(create_action_log("开始分离工作流程", "🎯"))
|
||||
|
||||
# 🔧 新增:体积变化跟踪变量
|
||||
current_volume = original_liquid_volume
|
||||
|
||||
try:
|
||||
for repeat_idx in range(repeats):
|
||||
cycle_num = repeat_idx + 1
|
||||
@@ -430,7 +552,7 @@ def generate_separate_protocol(
|
||||
pump_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent_vessel,
|
||||
to_vessel=final_vessel,
|
||||
to_vessel=final_vessel_id, # 🔧 使用 final_vessel_id
|
||||
volume=final_volume,
|
||||
amount="",
|
||||
time=0.0,
|
||||
@@ -450,6 +572,10 @@ def generate_separate_protocol(
|
||||
debug_print(f"✅ 溶剂添加完成,添加了 {len(pump_actions)} 个动作")
|
||||
action_sequence.append(create_action_log(f"溶剂转移完成 ({len(pump_actions)} 个操作)", "✅"))
|
||||
|
||||
# 🔧 新增:更新体积 - 添加溶剂后
|
||||
current_volume += final_volume
|
||||
update_vessel_volume(vessel, G, current_volume, f"添加{final_volume}mL {solvent}后")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f"❌ 溶剂添加失败: {str(e)}")
|
||||
action_sequence.append(create_action_log(f"溶剂添加失败: {str(e)}", "❌"))
|
||||
@@ -466,7 +592,7 @@ def generate_separate_protocol(
|
||||
"device_id": stirrer_device,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": final_vessel,
|
||||
"vessel": final_vessel_id, # 🔧 使用 final_vessel_id
|
||||
"stir_speed": stir_speed,
|
||||
"purpose": f"分离混合 - {purpose}"
|
||||
}
|
||||
@@ -485,7 +611,7 @@ def generate_separate_protocol(
|
||||
action_sequence.append({
|
||||
"device_id": stirrer_device,
|
||||
"action_name": "stop_stir",
|
||||
"action_kwargs": {"vessel": final_vessel}
|
||||
"action_kwargs": {"vessel": final_vessel_id} # 🔧 使用 final_vessel_id
|
||||
})
|
||||
|
||||
else:
|
||||
@@ -517,10 +643,10 @@ def generate_separate_protocol(
|
||||
"action_kwargs": {
|
||||
"purpose": purpose,
|
||||
"product_phase": product_phase,
|
||||
"from_vessel": from_vessel or final_vessel,
|
||||
"separation_vessel": final_vessel,
|
||||
"to_vessel": final_to_vessel or final_vessel,
|
||||
"waste_phase_to_vessel": final_waste_vessel or final_vessel,
|
||||
"from_vessel": extract_vessel_id(from_vessel) or final_vessel_id, # 🔧 使用vessel_id
|
||||
"separation_vessel": final_vessel_id, # 🔧 使用 final_vessel_id
|
||||
"to_vessel": final_to_vessel_id or final_vessel_id, # 🔧 使用vessel_id
|
||||
"waste_phase_to_vessel": final_waste_vessel_id or final_vessel_id, # 🔧 使用vessel_id
|
||||
"solvent": solvent,
|
||||
"solvent_volume": final_volume,
|
||||
"through": through,
|
||||
@@ -534,11 +660,17 @@ def generate_separate_protocol(
|
||||
debug_print(f"✅ 分离操作已添加")
|
||||
action_sequence.append(create_action_log("分离操作完成", "✅"))
|
||||
|
||||
# 🔧 新增:分离后体积估算(分离通常不改变总体积,但会重新分配)
|
||||
# 假设分离后保持体积(实际情况可能有少量损失)
|
||||
separated_volume = current_volume * 0.95 # 假设5%损失
|
||||
update_vessel_volume(vessel, G, separated_volume, f"分离操作后(第{cycle_num}轮)")
|
||||
current_volume = separated_volume
|
||||
|
||||
# 收集结果
|
||||
if final_to_vessel:
|
||||
action_sequence.append(create_action_log(f"产物 ({product_phase}相) 收集到: {final_to_vessel}", "📦"))
|
||||
if final_waste_vessel:
|
||||
action_sequence.append(create_action_log(f"废相收集到: {final_waste_vessel}", "🗑️"))
|
||||
if final_to_vessel_id:
|
||||
action_sequence.append(create_action_log(f"产物 ({product_phase}相) 收集到: {final_to_vessel_id}", "📦"))
|
||||
if final_waste_vessel_id:
|
||||
action_sequence.append(create_action_log(f"废相收集到: {final_waste_vessel_id}", "🗑️"))
|
||||
|
||||
else:
|
||||
debug_print(f"🔄 第{cycle_num}轮 步骤4: 无分离器设备,跳过分离")
|
||||
@@ -572,99 +704,37 @@ def generate_separate_protocol(
|
||||
}
|
||||
})
|
||||
|
||||
# 🔧 新增:分离完成后的最终状态报告
|
||||
final_liquid_volume = get_vessel_liquid_volume(vessel)
|
||||
|
||||
# === 最终结果 ===
|
||||
total_time = (stir_time + settling_time + 15) * repeats # 估算总时间
|
||||
|
||||
debug_print("=" * 60)
|
||||
debug_print("🌀" * 20)
|
||||
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" 🥼 分离容器: {final_vessel}")
|
||||
debug_print(f" 🥼 分离容器: {final_vessel_id}")
|
||||
debug_print(f" 🎯 分离目的: {purpose}")
|
||||
debug_print(f" 📊 产物相: {product_phase}")
|
||||
debug_print(f" 🔄 重复次数: {repeats}")
|
||||
debug_print(f"💧 体积变化统计:")
|
||||
debug_print(f" - 分离前体积: {original_liquid_volume:.2f}mL")
|
||||
debug_print(f" - 分离后体积: {final_liquid_volume:.2f}mL")
|
||||
if solvent:
|
||||
debug_print(f" 💧 溶剂: {solvent} ({final_volume}mL)")
|
||||
if final_to_vessel:
|
||||
debug_print(f" 🎯 产物容器: {final_to_vessel}")
|
||||
if final_waste_vessel:
|
||||
debug_print(f" 🗑️ 废液容器: {final_waste_vessel}")
|
||||
debug_print("=" * 60)
|
||||
debug_print(f" 💧 溶剂: {solvent} ({final_volume}mL × {repeats}轮 = {final_volume * repeats:.2f}mL)")
|
||||
if final_to_vessel_id:
|
||||
debug_print(f" 🎯 产物容器: {final_to_vessel_id}")
|
||||
if final_waste_vessel_id:
|
||||
debug_print(f" 🗑️ 废液容器: {final_waste_vessel_id}")
|
||||
debug_print("🌀" * 20)
|
||||
|
||||
# 添加完成日志
|
||||
summary_msg = f"分离协议完成: {final_vessel} ({purpose},{repeats} 次循环)"
|
||||
summary_msg = f"分离协议完成: {final_vessel_id} ({purpose},{repeats} 次循环)"
|
||||
if solvent:
|
||||
summary_msg += f",使用 {final_volume}mL {solvent}"
|
||||
summary_msg += f",使用 {final_volume * repeats:.2f}mL {solvent}"
|
||||
action_sequence.append(create_action_log(summary_msg, "🎉"))
|
||||
|
||||
return action_sequence
|
||||
|
||||
# === 便捷函数 ===
|
||||
|
||||
def separate_phases_only(G: nx.DiGraph, vessel: str, product_phase: str = "top",
|
||||
product_vessel: str = "", waste_vessel: str = "") -> List[Dict[str, Any]]:
|
||||
"""仅进行相分离(不添加溶剂)"""
|
||||
debug_print(f"⚡ 快速相分离: {vessel} ({product_phase}相)")
|
||||
return generate_separate_protocol(
|
||||
G, vessel=vessel,
|
||||
purpose="separate",
|
||||
product_phase=product_phase,
|
||||
product_vessel=product_vessel,
|
||||
waste_vessel=waste_vessel
|
||||
)
|
||||
|
||||
def wash_with_solvent(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float],
|
||||
product_phase: str = "top", repeats: int = 1) -> List[Dict[str, Any]]:
|
||||
"""用溶剂洗涤"""
|
||||
debug_print(f"🧽 用{solvent}洗涤: {vessel} ({repeats} 次)")
|
||||
return generate_separate_protocol(
|
||||
G, vessel=vessel,
|
||||
purpose="wash",
|
||||
product_phase=product_phase,
|
||||
solvent=solvent,
|
||||
volume=volume,
|
||||
repeats=repeats
|
||||
)
|
||||
|
||||
def extract_with_solvent(G: nx.DiGraph, vessel: str, solvent: str, volume: Union[str, float],
|
||||
product_phase: str = "bottom", repeats: int = 3) -> List[Dict[str, Any]]:
|
||||
"""用溶剂萃取"""
|
||||
debug_print(f"🧪 用{solvent}萃取: {vessel} ({repeats} 次)")
|
||||
return generate_separate_protocol(
|
||||
G, vessel=vessel,
|
||||
purpose="extract",
|
||||
product_phase=product_phase,
|
||||
solvent=solvent,
|
||||
volume=volume,
|
||||
repeats=repeats
|
||||
)
|
||||
|
||||
def separate_aqueous_organic(G: nx.DiGraph, vessel: str, organic_phase: str = "top",
|
||||
product_vessel: str = "", waste_vessel: str = "") -> List[Dict[str, Any]]:
|
||||
"""水-有机相分离"""
|
||||
debug_print(f"💧 水-有机相分离: {vessel} (有机相: {organic_phase})")
|
||||
return generate_separate_protocol(
|
||||
G, vessel=vessel,
|
||||
purpose="separate",
|
||||
product_phase=organic_phase,
|
||||
product_vessel=product_vessel,
|
||||
waste_vessel=waste_vessel
|
||||
)
|
||||
|
||||
# 测试函数
|
||||
def test_separate_protocol():
|
||||
"""测试分离协议的各种参数解析"""
|
||||
debug_print("=== 分离协议增强中文版测试 ===")
|
||||
|
||||
# 测试体积解析
|
||||
debug_print("🧪 测试体积解析...")
|
||||
volumes = ["200 mL", "?", 100.0, "1 L", "500 μL", "未知", "50毫升"]
|
||||
for vol in volumes:
|
||||
result = parse_volume_input(vol)
|
||||
debug_print(f"📊 体积解析结果: {vol} -> {result}mL")
|
||||
|
||||
debug_print("✅ 测试完成")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_separate_protocol()
|
||||
|
||||
@@ -138,9 +138,50 @@ def validate_and_fix_params(stir_time: float, stir_speed: float, settling_time:
|
||||
|
||||
return stir_time, stir_speed, settling_time
|
||||
|
||||
def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
||||
"""
|
||||
从vessel参数中提取vessel_id
|
||||
|
||||
Args:
|
||||
vessel: vessel字典或vessel_id字符串
|
||||
|
||||
Returns:
|
||||
str: vessel_id
|
||||
"""
|
||||
if isinstance(vessel, dict):
|
||||
vessel_id = vessel.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(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: Union[str, dict], # 支持vessel字典或字符串
|
||||
time: Union[str, float, int] = "300",
|
||||
stir_time: Union[str, float, int] = "0",
|
||||
time_spec: str = "",
|
||||
@@ -149,14 +190,39 @@ def generate_stir_protocol(
|
||||
settling_time: Union[str, float] = "60",
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成搅拌操作的协议序列(精简版)
|
||||
"""
|
||||
"""生成搅拌操作的协议序列 - 修复vessel参数传递"""
|
||||
|
||||
# 🔧 核心修改:正确处理vessel参数
|
||||
vessel_id = extract_vessel_id(vessel)
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
|
||||
# 🔧 关键修复:确保vessel_resource是完整的Resource对象
|
||||
if isinstance(vessel, dict):
|
||||
vessel_resource = vessel # 已经是完整的Resource字典
|
||||
debug_print(f"✅ 使用传入的vessel Resource对象")
|
||||
else:
|
||||
# 如果只是字符串,构建一个基本的Resource对象
|
||||
vessel_resource = {
|
||||
"id": vessel,
|
||||
"name": "",
|
||||
"category": "",
|
||||
"children": [],
|
||||
"config": "",
|
||||
"data": "",
|
||||
"parent": "",
|
||||
"pose": {
|
||||
"orientation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0},
|
||||
"position": {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||
},
|
||||
"sample_id": "",
|
||||
"type": ""
|
||||
}
|
||||
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||
|
||||
debug_print("🌪️" * 20)
|
||||
debug_print("🚀 开始生成搅拌协议 ✨")
|
||||
debug_print("🚀 开始生成搅拌协议(支持vessel字典)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel}")
|
||||
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}")
|
||||
@@ -166,13 +232,13 @@ def generate_stir_protocol(
|
||||
|
||||
# 📋 参数验证
|
||||
debug_print("📍 步骤1: 参数验证... 🔧")
|
||||
if not vessel:
|
||||
if not vessel_id: # 🔧 使用 vessel_id
|
||||
debug_print("❌ vessel 参数不能为空! 😱")
|
||||
raise ValueError("vessel 参数不能为空")
|
||||
|
||||
if vessel not in G.nodes():
|
||||
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中! 😞")
|
||||
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
|
||||
if vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print(f"❌ 容器 '{vessel_id}' 不存在于系统中! 😞")
|
||||
raise ValueError(f"容器 '{vessel_id}' 不存在于系统中")
|
||||
|
||||
debug_print("✅ 基础参数验证通过 🎯")
|
||||
|
||||
@@ -220,7 +286,7 @@ def generate_stir_protocol(
|
||||
# 🔍 查找设备
|
||||
debug_print("📍 步骤3: 查找搅拌设备... 🔍")
|
||||
try:
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id) # 🔧 使用 vessel_id
|
||||
debug_print(f"🎉 使用搅拌设备: {stirrer_id} ✨")
|
||||
except Exception as e:
|
||||
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
|
||||
@@ -234,13 +300,14 @@ def generate_stir_protocol(
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"time": str(time), # 保持原始格式
|
||||
# 🔧 关键修复:传递vessel_id字符串,而不是完整的Resource对象
|
||||
"vessel": vessel_id, # 传递字符串ID,不是Resource对象
|
||||
"time": str(time),
|
||||
"event": event,
|
||||
"time_spec": time_spec,
|
||||
"stir_time": float(parsed_time), # 确保是数字
|
||||
"stir_speed": float(stir_speed), # 确保是数字
|
||||
"settling_time": float(parsed_settling_time) # 确保是数字
|
||||
"stir_time": float(parsed_time),
|
||||
"stir_speed": float(stir_speed),
|
||||
"settling_time": float(parsed_settling_time)
|
||||
}
|
||||
}
|
||||
action_sequence.append(stir_action)
|
||||
@@ -256,7 +323,7 @@ def generate_stir_protocol(
|
||||
debug_print("🎊" * 20)
|
||||
debug_print(f"🎉 搅拌协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
debug_print(f"🥽 搅拌容器: {vessel}")
|
||||
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)
|
||||
@@ -265,18 +332,47 @@ def generate_stir_protocol(
|
||||
|
||||
def generate_start_stir_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: Union[str, dict],
|
||||
stir_speed: float = 300.0,
|
||||
purpose: str = "",
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""生成开始搅拌操作的协议序列"""
|
||||
"""生成开始搅拌操作的协议序列 - 修复vessel参数传递"""
|
||||
|
||||
debug_print("🔄 开始生成启动搅拌协议 ✨")
|
||||
debug_print(f"🥽 vessel: {vessel}, 🌪️ speed: {stir_speed} RPM")
|
||||
# 🔧 核心修改:正确处理vessel参数
|
||||
vessel_id = extract_vessel_id(vessel)
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
|
||||
# 🔧 关键修复:确保vessel_resource是完整的Resource对象
|
||||
if isinstance(vessel, dict):
|
||||
vessel_resource = vessel # 已经是完整的Resource字典
|
||||
debug_print(f"✅ 使用传入的vessel Resource对象")
|
||||
else:
|
||||
# 如果只是字符串,构建一个基本的Resource对象
|
||||
vessel_resource = {
|
||||
"id": vessel,
|
||||
"name": "",
|
||||
"category": "",
|
||||
"children": [],
|
||||
"config": "",
|
||||
"data": "",
|
||||
"parent": "",
|
||||
"pose": {
|
||||
"orientation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0},
|
||||
"position": {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||
},
|
||||
"sample_id": "",
|
||||
"type": ""
|
||||
}
|
||||
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||
|
||||
debug_print("🔄 开始生成启动搅拌协议(修复vessel参数)✨")
|
||||
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
||||
debug_print(f"🌪️ speed: {stir_speed} RPM")
|
||||
debug_print(f"🎯 purpose: {purpose}")
|
||||
|
||||
# 基础验证
|
||||
if not vessel or vessel not in G.nodes():
|
||||
if not vessel_id or vessel_id not in G.nodes():
|
||||
debug_print("❌ 容器验证失败!")
|
||||
raise ValueError("vessel 参数无效")
|
||||
|
||||
@@ -286,14 +382,15 @@ def generate_start_stir_protocol(
|
||||
stir_speed = 300.0
|
||||
|
||||
# 查找设备
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||
|
||||
# 生成动作
|
||||
# 🔧 关键修复:传递vessel_id字符串
|
||||
action_sequence = [{
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "start_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
# 🔧 关键修复:传递vessel_id字符串,而不是完整的Resource对象
|
||||
"vessel": vessel_id, # 传递字符串ID,不是Resource对象
|
||||
"stir_speed": stir_speed,
|
||||
"purpose": purpose or f"启动搅拌 {stir_speed} RPM"
|
||||
}
|
||||
@@ -304,38 +401,144 @@ def generate_start_stir_protocol(
|
||||
|
||||
def generate_stop_stir_protocol(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: Union[str, dict],
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""生成停止搅拌操作的协议序列"""
|
||||
"""生成停止搅拌操作的协议序列 - 修复vessel参数传递"""
|
||||
|
||||
debug_print("🛑 开始生成停止搅拌协议 ✨")
|
||||
debug_print(f"🥽 vessel: {vessel}")
|
||||
# 🔧 核心修改:正确处理vessel参数
|
||||
vessel_id = extract_vessel_id(vessel)
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
|
||||
# 🔧 关键修复:确保vessel_resource是完整的Resource对象
|
||||
if isinstance(vessel, dict):
|
||||
vessel_resource = vessel # 已经是完整的Resource字典
|
||||
debug_print(f"✅ 使用传入的vessel Resource对象")
|
||||
else:
|
||||
# 如果只是字符串,构建一个基本的Resource对象
|
||||
vessel_resource = {
|
||||
"id": vessel,
|
||||
"name": "",
|
||||
"category": "",
|
||||
"children": [],
|
||||
"config": "",
|
||||
"data": "",
|
||||
"parent": "",
|
||||
"pose": {
|
||||
"orientation": {"w": 1.0, "x": 0.0, "y": 0.0, "z": 0.0},
|
||||
"position": {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||
},
|
||||
"sample_id": "",
|
||||
"type": ""
|
||||
}
|
||||
debug_print(f"🔧 构建了基本的vessel Resource对象: {vessel}")
|
||||
|
||||
debug_print("🛑 开始生成停止搅拌协议(修复vessel参数)✨")
|
||||
debug_print(f"🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
||||
|
||||
# 基础验证
|
||||
if not vessel or vessel not in G.nodes():
|
||||
if not vessel_id or vessel_id not in G.nodes():
|
||||
debug_print("❌ 容器验证失败!")
|
||||
raise ValueError("vessel 参数无效")
|
||||
|
||||
# 查找设备
|
||||
stirrer_id = find_connected_stirrer(G, vessel)
|
||||
stirrer_id = find_connected_stirrer(G, vessel_id)
|
||||
|
||||
# 生成动作
|
||||
# 🔧 关键修复:传递vessel_id字符串
|
||||
action_sequence = [{
|
||||
"device_id": stirrer_id,
|
||||
"action_name": "stop_stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel
|
||||
# 🔧 关键修复:传递vessel_id字符串,而不是完整的Resource对象
|
||||
"vessel": vessel_id # 传递字符串ID,不是Resource对象
|
||||
}
|
||||
}]
|
||||
|
||||
debug_print(f"✅ 停止搅拌协议生成完成 🎯")
|
||||
return action_sequence
|
||||
|
||||
# 🔧 新增:便捷函数
|
||||
def stir_briefly(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
speed: float = 300.0) -> List[Dict[str, Any]]:
|
||||
"""短时间搅拌(30秒)"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"⚡ 短时间搅拌: {vessel_display} @ {speed}RPM (30s)")
|
||||
return generate_stir_protocol(G, vessel, time="30", stir_speed=speed)
|
||||
|
||||
def stir_slowly(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
time: Union[str, float] = "10 min") -> List[Dict[str, Any]]:
|
||||
"""慢速搅拌"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"🐌 慢速搅拌: {vessel_display} @ 150RPM")
|
||||
return generate_stir_protocol(G, vessel, time=time, stir_speed=150.0)
|
||||
|
||||
def stir_vigorously(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
time: Union[str, float] = "5 min") -> List[Dict[str, Any]]:
|
||||
"""剧烈搅拌"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"💨 剧烈搅拌: {vessel_display} @ 800RPM")
|
||||
return generate_stir_protocol(G, vessel, time=time, stir_speed=800.0)
|
||||
|
||||
def stir_for_reaction(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
time: Union[str, float] = "1 h") -> List[Dict[str, Any]]:
|
||||
"""反应搅拌(标准速度,长时间)"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"🧪 反应搅拌: {vessel_display} @ 400RPM")
|
||||
return generate_stir_protocol(G, vessel, time=time, stir_speed=400.0)
|
||||
|
||||
def stir_for_dissolution(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
time: Union[str, float] = "15 min") -> List[Dict[str, Any]]:
|
||||
"""溶解搅拌(中等速度)"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"💧 溶解搅拌: {vessel_display} @ 500RPM")
|
||||
return generate_stir_protocol(G, vessel, time=time, stir_speed=500.0)
|
||||
|
||||
def stir_gently(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
time: Union[str, float] = "30 min") -> List[Dict[str, Any]]:
|
||||
"""温和搅拌"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"🍃 温和搅拌: {vessel_display} @ 200RPM")
|
||||
return generate_stir_protocol(G, vessel, time=time, stir_speed=200.0)
|
||||
|
||||
def stir_overnight(G: nx.DiGraph, vessel: Union[str, dict]) -> List[Dict[str, Any]]:
|
||||
"""过夜搅拌(模拟时缩短为2小时)"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"🌙 过夜搅拌(模拟2小时): {vessel_display} @ 300RPM")
|
||||
return generate_stir_protocol(G, vessel, time="2 h", stir_speed=300.0)
|
||||
|
||||
def start_continuous_stirring(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
speed: float = 300.0, purpose: str = "continuous stirring") -> List[Dict[str, Any]]:
|
||||
"""开始连续搅拌"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"🔄 开始连续搅拌: {vessel_display} @ {speed}RPM")
|
||||
return generate_start_stir_protocol(G, vessel, stir_speed=speed, purpose=purpose)
|
||||
|
||||
def stop_all_stirring(G: nx.DiGraph, vessel: Union[str, dict]) -> List[Dict[str, Any]]:
|
||||
"""停止所有搅拌"""
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
debug_print(f"🛑 停止搅拌: {vessel_display}")
|
||||
return generate_stop_stir_protocol(G, vessel)
|
||||
|
||||
# 测试函数
|
||||
def test_stir_protocol():
|
||||
"""测试搅拌协议"""
|
||||
debug_print("🧪 === STIR PROTOCOL 测试 === ✨")
|
||||
|
||||
# 测试vessel参数处理
|
||||
debug_print("🔧 测试vessel参数处理...")
|
||||
|
||||
# 测试字典格式
|
||||
vessel_dict = {"id": "flask_1", "name": "反应瓶1"}
|
||||
vessel_id = extract_vessel_id(vessel_dict)
|
||||
vessel_display = get_vessel_display_info(vessel_dict)
|
||||
debug_print(f" 字典格式: {vessel_dict} → ID: {vessel_id}, 显示: {vessel_display}")
|
||||
|
||||
# 测试字符串格式
|
||||
vessel_str = "flask_2"
|
||||
vessel_id = extract_vessel_id(vessel_str)
|
||||
vessel_display = get_vessel_display_info(vessel_str)
|
||||
debug_print(f" 字符串格式: {vessel_str} → ID: {vessel_id}, 显示: {vessel_display}")
|
||||
|
||||
debug_print("✅ 测试完成 🎉")
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -154,12 +154,153 @@ def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
||||
debug_print(f"⚠️ 使用默认滤液容器: waste_workup")
|
||||
return "waste_workup"
|
||||
|
||||
def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
||||
"""
|
||||
从vessel参数中提取vessel_id
|
||||
|
||||
Args:
|
||||
vessel: vessel字典或vessel_id字符串
|
||||
|
||||
Returns:
|
||||
str: vessel_id
|
||||
"""
|
||||
if isinstance(vessel, dict):
|
||||
vessel_id = vessel.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(
|
||||
G: nx.DiGraph,
|
||||
vessel: str,
|
||||
vessel: Union[str, dict], # 🔧 修改:支持vessel字典
|
||||
solvent: str,
|
||||
volume: Union[float, str] = "50",
|
||||
filtrate_vessel: str = "",
|
||||
filtrate_vessel: Union[str, dict] = "", # 🔧 修改:支持vessel字典
|
||||
temp: float = 25.0,
|
||||
stir: bool = False,
|
||||
stir_speed: float = 0.0,
|
||||
@@ -172,21 +313,58 @@ def generate_wash_solid_protocol(
|
||||
**kwargs
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
生成固体清洗协议(精简版)
|
||||
生成固体清洗协议 - 支持vessel字典和体积运算
|
||||
|
||||
Args:
|
||||
G: 有向图,节点为设备和容器,边为流体管道
|
||||
vessel: 清洗容器字典(从XDL传入)或容器ID字符串
|
||||
solvent: 清洗溶剂名称
|
||||
volume: 溶剂体积(每次清洗)
|
||||
filtrate_vessel: 滤液收集容器字典或容器ID字符串
|
||||
temp: 清洗温度(°C)
|
||||
stir: 是否搅拌
|
||||
stir_speed: 搅拌速度(RPM)
|
||||
time: 搅拌时间
|
||||
repeats: 清洗重复次数
|
||||
volume_spec: 体积规格(small/medium/large)
|
||||
repeats_spec: 重复次数规格(few/several/many)
|
||||
mass: 固体质量(用于计算溶剂用量)
|
||||
event: 事件描述
|
||||
**kwargs: 其他可选参数
|
||||
|
||||
Returns:
|
||||
List[Dict[str, Any]]: 固体清洗操作的动作序列
|
||||
"""
|
||||
|
||||
# 🔧 核心修改:从vessel参数中提取vessel_id
|
||||
vessel_id = extract_vessel_id(vessel)
|
||||
vessel_display = get_vessel_display_info(vessel)
|
||||
|
||||
# 🔧 处理filtrate_vessel参数
|
||||
filtrate_vessel_id = extract_vessel_id(filtrate_vessel) if filtrate_vessel else ""
|
||||
|
||||
debug_print("🧼" * 20)
|
||||
debug_print("🚀 开始生成固体清洗协议 ✨")
|
||||
debug_print("🚀 开始生成固体清洗协议(支持vessel字典和体积运算)✨")
|
||||
debug_print(f"📝 输入参数:")
|
||||
debug_print(f" 🥽 vessel: {vessel}")
|
||||
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):
|
||||
original_volume = get_vessel_liquid_volume(vessel)
|
||||
debug_print(f"📊 清洗前液体体积: {original_volume:.2f}mL")
|
||||
else:
|
||||
original_volume = 0.0
|
||||
debug_print(f"📊 vessel为字符串格式,无法获取体积信息")
|
||||
|
||||
# 📋 快速验证
|
||||
if not vessel or vessel not in G.nodes():
|
||||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||||
debug_print("❌ 容器验证失败! 😱")
|
||||
raise ValueError("vessel 参数无效")
|
||||
|
||||
@@ -225,8 +403,10 @@ def generate_wash_solid_protocol(
|
||||
debug_print("📍 步骤2: 查找设备... 🔍")
|
||||
try:
|
||||
solvent_source = find_solvent_source(G, solvent)
|
||||
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel)
|
||||
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:
|
||||
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
|
||||
raise ValueError(f"设备查找失败: {str(e)}")
|
||||
@@ -235,6 +415,10 @@ def generate_wash_solid_protocol(
|
||||
debug_print("📍 步骤3: 生成清洗动作... 🧼")
|
||||
action_sequence = []
|
||||
|
||||
# 🔧 新增:体积变化跟踪变量
|
||||
current_volume = original_volume
|
||||
total_solvent_used = 0.0
|
||||
|
||||
for cycle in range(final_repeats):
|
||||
debug_print(f" 🔄 第{cycle+1}/{final_repeats}次清洗...")
|
||||
|
||||
@@ -242,10 +426,11 @@ def generate_wash_solid_protocol(
|
||||
try:
|
||||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||||
|
||||
debug_print(f" 💧 添加溶剂: {final_volume}mL {solvent}")
|
||||
transfer_actions = generate_pump_protocol_with_rinsing(
|
||||
G=G,
|
||||
from_vessel=solvent_source,
|
||||
to_vessel=vessel,
|
||||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||||
volume=final_volume,
|
||||
amount="",
|
||||
time=0.0,
|
||||
@@ -261,17 +446,26 @@ def generate_wash_solid_protocol(
|
||||
if transfer_actions:
|
||||
action_sequence.extend(transfer_actions)
|
||||
debug_print(f" ✅ 转移动作: {len(transfer_actions)}个 🚚")
|
||||
|
||||
# 🔧 新增:更新体积 - 添加溶剂后
|
||||
current_volume += final_volume
|
||||
total_solvent_used += final_volume
|
||||
|
||||
if isinstance(vessel, dict):
|
||||
update_vessel_volume(vessel, G, current_volume,
|
||||
f"第{cycle+1}次清洗添加{final_volume}mL溶剂后")
|
||||
|
||||
except Exception as e:
|
||||
debug_print(f" ❌ 转移失败: {str(e)} 😞")
|
||||
|
||||
# 2. 搅拌(如果需要)
|
||||
if stir and final_time > 0:
|
||||
debug_print(f" 🌪️ 搅拌: {final_time}s @ {stir_speed}RPM")
|
||||
stir_action = {
|
||||
"device_id": "stirrer_1",
|
||||
"action_name": "stir",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"time": str(time),
|
||||
"stir_time": final_time,
|
||||
"stir_speed": stir_speed,
|
||||
@@ -282,11 +476,12 @@ def generate_wash_solid_protocol(
|
||||
debug_print(f" ✅ 搅拌动作: {final_time}s, {stir_speed}RPM 🌪️")
|
||||
|
||||
# 3. 过滤
|
||||
debug_print(f" 🌊 过滤到: {actual_filtrate_vessel}")
|
||||
filter_action = {
|
||||
"device_id": "filter_1",
|
||||
"action_name": "filter",
|
||||
"action_kwargs": {
|
||||
"vessel": vessel,
|
||||
"vessel": vessel_id, # 🔧 使用 vessel_id
|
||||
"filtrate_vessel": actual_filtrate_vessel,
|
||||
"temp": temp,
|
||||
"volume": final_volume
|
||||
@@ -295,6 +490,15 @@ def generate_wash_solid_protocol(
|
||||
action_sequence.append(filter_action)
|
||||
debug_print(f" ✅ 过滤动作: → {actual_filtrate_vessel} 🌊")
|
||||
|
||||
# 🔧 新增:更新体积 - 过滤后(液体被滤除)
|
||||
# 假设滤液完全被移除,固体残留在容器中
|
||||
filtered_volume = current_volume * 0.9 # 假设90%的液体被过滤掉
|
||||
current_volume = current_volume - filtered_volume
|
||||
|
||||
if isinstance(vessel, dict):
|
||||
update_vessel_volume(vessel, G, current_volume,
|
||||
f"第{cycle+1}次清洗过滤后")
|
||||
|
||||
# 4. 等待(缩短时间)
|
||||
wait_time = 5.0 # 🕐 缩短等待时间:10s → 5s
|
||||
action_sequence.append({
|
||||
@@ -303,14 +507,146 @@ def generate_wash_solid_protocol(
|
||||
})
|
||||
debug_print(f" ✅ 等待: {wait_time}s ⏰")
|
||||
|
||||
# 🔧 新增:清洗完成后的最终状态报告
|
||||
if isinstance(vessel, dict):
|
||||
final_volume_vessel = get_vessel_liquid_volume(vessel)
|
||||
else:
|
||||
final_volume_vessel = current_volume
|
||||
|
||||
# 🎊 总结
|
||||
debug_print("🧼" * 20)
|
||||
debug_print(f"🎉 固体清洗协议生成完成! ✨")
|
||||
debug_print(f"📊 总动作数: {len(action_sequence)} 个")
|
||||
debug_print(f"🥽 清洗容器: {vessel}")
|
||||
debug_print(f"🧪 使用溶剂: {solvent}")
|
||||
debug_print(f"💧 清洗体积: {final_volume}mL × {final_repeats}次")
|
||||
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],
|
||||
volume: Union[float, str] = "50",
|
||||
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)
|
||||
|
||||
def wash_with_ethanol(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
volume: Union[float, str] = "30",
|
||||
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)
|
||||
|
||||
def wash_with_acetone(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
volume: Union[float, str] = "25",
|
||||
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)
|
||||
|
||||
def wash_with_ether(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
volume: Union[float, str] = "40",
|
||||
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)
|
||||
|
||||
def wash_with_cold_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
solvent: str, volume: Union[float, str] = "30",
|
||||
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,
|
||||
temp=5.0, repeats=repeats)
|
||||
|
||||
def wash_with_hot_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
solvent: str, volume: Union[float, str] = "50",
|
||||
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,
|
||||
temp=60.0, repeats=repeats)
|
||||
|
||||
def wash_with_stirring(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
solvent: str, volume: Union[float, str] = "50",
|
||||
stir_time: Union[str, float] = "5 min",
|
||||
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,
|
||||
stir=True, stir_speed=200.0,
|
||||
time=stir_time, repeats=repeats)
|
||||
|
||||
def thorough_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
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)
|
||||
|
||||
def quick_rinse(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
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)
|
||||
|
||||
def sequential_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
||||
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 = []
|
||||
for solvent in solvents:
|
||||
wash_actions = generate_wash_solid_protocol(G, vessel, solvent,
|
||||
volume=volume, repeats=1)
|
||||
action_sequence.extend(wash_actions)
|
||||
|
||||
return action_sequence
|
||||
|
||||
# 测试函数
|
||||
def test_wash_solid_protocol():
|
||||
"""测试固体清洗协议"""
|
||||
debug_print("🧪 === WASH SOLID PROTOCOL 测试 === ✨")
|
||||
|
||||
# 测试vessel参数处理
|
||||
debug_print("🔧 测试vessel参数处理...")
|
||||
|
||||
# 测试字典格式
|
||||
vessel_dict = {"id": "filter_flask_1", "name": "过滤瓶1",
|
||||
"data": {"liquid_volume": 25.0}}
|
||||
vessel_id = extract_vessel_id(vessel_dict)
|
||||
vessel_display = get_vessel_display_info(vessel_dict)
|
||||
volume = get_vessel_liquid_volume(vessel_dict)
|
||||
debug_print(f" 字典格式: {vessel_dict}")
|
||||
debug_print(f" → ID: {vessel_id}, 显示: {vessel_display}, 体积: {volume}mL")
|
||||
|
||||
# 测试字符串格式
|
||||
vessel_str = "filter_flask_2"
|
||||
vessel_id = extract_vessel_id(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("✅ 测试完成 🎉")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_wash_solid_protocol()
|
||||
@@ -11,8 +11,8 @@ class Point3D(BaseModel):
|
||||
|
||||
class PumpTransferProtocol(BaseModel):
|
||||
# === 核心参数(保持必需) ===
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
from_vessel: dict
|
||||
to_vessel: dict
|
||||
|
||||
# === 所有其他参数都改为可选,添加默认值 ===
|
||||
volume: float = 0.0 # 🔧 改为-1,表示转移全部体积
|
||||
@@ -95,7 +95,7 @@ class PumpTransferProtocol(BaseModel):
|
||||
|
||||
|
||||
class CleanProtocol(BaseModel):
|
||||
vessel: str
|
||||
vessel: dict
|
||||
solvent: str
|
||||
volume: float
|
||||
temp: float
|
||||
@@ -105,10 +105,10 @@ class CleanProtocol(BaseModel):
|
||||
class SeparateProtocol(BaseModel):
|
||||
purpose: str
|
||||
product_phase: str
|
||||
from_vessel: str
|
||||
separation_vessel: str
|
||||
to_vessel: str
|
||||
waste_phase_to_vessel: str
|
||||
from_vessel: dict
|
||||
separation_vessel: dict
|
||||
to_vessel: dict
|
||||
waste_phase_to_vessel: dict
|
||||
solvent: str
|
||||
solvent_volume: float
|
||||
through: str
|
||||
@@ -120,7 +120,7 @@ class SeparateProtocol(BaseModel):
|
||||
|
||||
class EvaporateProtocol(BaseModel):
|
||||
# === 核心参数(必需) ===
|
||||
vessel: str = Field(..., description="蒸发容器名称")
|
||||
vessel: dict = Field(..., description="蒸发容器名称")
|
||||
|
||||
# === 所有其他参数都改为可选,添加默认值 ===
|
||||
pressure: float = Field(0.1, description="真空度 (bar),默认0.1 bar")
|
||||
@@ -183,7 +183,7 @@ class EvaporateProtocol(BaseModel):
|
||||
|
||||
class EvacuateAndRefillProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="目标容器名称")
|
||||
vessel: dict = Field(..., description="目标容器名称")
|
||||
gas: str = Field(..., description="气体名称")
|
||||
|
||||
# 🔧 删除 repeats 参数,直接在代码中硬编码为 3 次
|
||||
@@ -219,7 +219,7 @@ class AGVTransferProtocol(BaseModel):
|
||||
|
||||
#=============新添加的新的协议================
|
||||
class AddProtocol(BaseModel):
|
||||
vessel: str
|
||||
vessel: dict
|
||||
reagent: str
|
||||
volume: float
|
||||
mass: float
|
||||
@@ -231,17 +231,17 @@ class AddProtocol(BaseModel):
|
||||
purpose: str
|
||||
|
||||
class CentrifugeProtocol(BaseModel):
|
||||
vessel: str
|
||||
vessel: dict
|
||||
speed: float
|
||||
time: float
|
||||
temp: float
|
||||
|
||||
class FilterProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="过滤容器名称")
|
||||
vessel: dict = Field(..., description="过滤容器名称")
|
||||
|
||||
# === 可选参数 ===
|
||||
filtrate_vessel: str = Field("", description="滤液容器名称(可选,自动查找)")
|
||||
filtrate_vessel: dict = Field("", description="滤液容器名称(可选,自动查找)")
|
||||
|
||||
def model_post_init(self, __context):
|
||||
"""后处理:参数验证"""
|
||||
@@ -251,7 +251,7 @@ class FilterProtocol(BaseModel):
|
||||
|
||||
class HeatChillProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="加热容器名称")
|
||||
vessel: dict = Field(..., description="加热容器名称")
|
||||
|
||||
# === 可选参数 - 温度相关 ===
|
||||
temp: float = Field(25.0, description="目标温度 (°C)")
|
||||
@@ -375,7 +375,7 @@ class HeatChillProtocol(BaseModel):
|
||||
|
||||
class HeatChillStartProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="加热容器名称")
|
||||
vessel: dict = Field(..., description="加热容器名称")
|
||||
|
||||
# === 可选参数 - 温度相关 ===
|
||||
temp: float = Field(25.0, description="目标温度 (°C)")
|
||||
@@ -393,12 +393,12 @@ class HeatChillStartProtocol(BaseModel):
|
||||
|
||||
class HeatChillStopProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="加热容器名称")
|
||||
vessel: dict = Field(..., description="加热容器名称")
|
||||
|
||||
|
||||
class StirProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="搅拌容器名称")
|
||||
vessel: dict = Field(..., description="搅拌容器名称")
|
||||
|
||||
# === 可选参数 ===
|
||||
time: str = Field("5 min", description="搅拌时间(如 '0.5 h', '30 min')")
|
||||
@@ -482,7 +482,7 @@ class StirProtocol(BaseModel):
|
||||
|
||||
class StartStirProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="搅拌容器名称")
|
||||
vessel: dict = Field(..., description="搅拌容器名称")
|
||||
|
||||
# === 可选参数,添加默认值 ===
|
||||
stir_speed: float = Field(200.0, description="搅拌速度 (RPM),默认200 RPM")
|
||||
@@ -505,7 +505,7 @@ class StartStirProtocol(BaseModel):
|
||||
|
||||
class StopStirProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="搅拌容器名称")
|
||||
vessel: dict = Field(..., description="搅拌容器名称")
|
||||
|
||||
def model_post_init(self, __context):
|
||||
"""后处理:参数验证"""
|
||||
@@ -515,8 +515,8 @@ class StopStirProtocol(BaseModel):
|
||||
raise ValueError("vessel 参数不能为空")
|
||||
|
||||
class TransferProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
from_vessel: dict
|
||||
to_vessel: dict
|
||||
volume: float
|
||||
amount: str = ""
|
||||
time: float = 0
|
||||
@@ -527,14 +527,14 @@ class TransferProtocol(BaseModel):
|
||||
solid: bool = False
|
||||
|
||||
class CleanVesselProtocol(BaseModel):
|
||||
vessel: str
|
||||
vessel: dict
|
||||
solvent: str
|
||||
volume: float
|
||||
temp: float
|
||||
repeats: int = 1
|
||||
|
||||
class DissolveProtocol(BaseModel):
|
||||
vessel: str
|
||||
vessel: dict
|
||||
solvent: str
|
||||
volume: float
|
||||
amount: str = ""
|
||||
@@ -543,8 +543,8 @@ class DissolveProtocol(BaseModel):
|
||||
stir_speed: float = 0.0
|
||||
|
||||
class FilterThroughProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
from_vessel: dict
|
||||
to_vessel: dict
|
||||
filter_through: str
|
||||
eluting_solvent: str = ""
|
||||
eluting_volume: float = 0.0
|
||||
@@ -552,18 +552,18 @@ class FilterThroughProtocol(BaseModel):
|
||||
residence_time: float = 0.0
|
||||
|
||||
class RunColumnProtocol(BaseModel):
|
||||
from_vessel: str
|
||||
to_vessel: str
|
||||
from_vessel: dict
|
||||
to_vessel: dict
|
||||
column: str
|
||||
|
||||
class WashSolidProtocol(BaseModel):
|
||||
# === 必需参数 ===
|
||||
vessel: str = Field(..., description="装有固体的容器名称")
|
||||
vessel: dict = Field(..., description="装有固体的容器名称")
|
||||
solvent: str = Field(..., description="清洗溶剂名称")
|
||||
volume: float = Field(..., description="清洗溶剂体积 (mL)")
|
||||
|
||||
# === 可选参数,添加默认值 ===
|
||||
filtrate_vessel: str = Field("", description="滤液收集容器(可选,自动查找)")
|
||||
filtrate_vessel: dict = Field("", description="滤液收集容器(可选,自动查找)")
|
||||
temp: float = Field(25.0, description="清洗温度 (°C),默认25°C")
|
||||
stir: bool = Field(False, description="是否搅拌,默认False")
|
||||
stir_speed: float = Field(0.0, description="搅拌速度 (RPM),默认0")
|
||||
@@ -604,7 +604,7 @@ class WashSolidProtocol(BaseModel):
|
||||
self.repeats = 10
|
||||
|
||||
class AdjustPHProtocol(BaseModel):
|
||||
vessel: str = Field(..., description="目标容器")
|
||||
vessel: dict = Field(..., description="目标容器")
|
||||
ph_value: float = Field(..., description="目标pH值") # 改为 ph_value
|
||||
reagent: str = Field(..., description="酸碱试剂名称")
|
||||
# 移除其他可选参数,使用默认值
|
||||
@@ -614,19 +614,19 @@ class ResetHandlingProtocol(BaseModel):
|
||||
|
||||
class DryProtocol(BaseModel):
|
||||
compound: str = Field(..., description="化合物名称")
|
||||
vessel: str = Field(..., description="目标容器")
|
||||
vessel: dict = Field(..., description="目标容器")
|
||||
|
||||
class RecrystallizeProtocol(BaseModel):
|
||||
ratio: str = Field(..., description="溶剂比例(如 '1:1', '3:7')")
|
||||
solvent1: str = Field(..., description="第一种溶剂名称")
|
||||
solvent2: str = Field(..., description="第二种溶剂名称")
|
||||
vessel: str = Field(..., description="目标容器")
|
||||
vessel: dict = Field(..., description="目标容器")
|
||||
volume: float = Field(..., description="总体积 (mL)")
|
||||
|
||||
class HydrogenateProtocol(BaseModel):
|
||||
temp: str = Field(..., description="反应温度(如 '45 °C')")
|
||||
time: str = Field(..., description="反应时间(如 '2 h')")
|
||||
vessel: str = Field(..., description="反应容器")
|
||||
vessel: dict = Field(..., description="反应容器")
|
||||
|
||||
__all__ = [
|
||||
"Point3D", "PumpTransferProtocol", "CleanProtocol", "SeparateProtocol",
|
||||
|
||||
@@ -3650,3 +3650,176 @@ virtual_vacuum_pump:
|
||||
- status
|
||||
type: object
|
||||
version: 0.0.1
|
||||
virtual_solid_dispenser:
|
||||
class:
|
||||
action_value_mappings:
|
||||
auto-cleanup:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: cleanup的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties: {}
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: cleanup参数
|
||||
type: object
|
||||
type: UniLabJsonCommandAsync
|
||||
auto-initialize:
|
||||
feedback: {}
|
||||
goal: {}
|
||||
goal_default: {}
|
||||
handles: []
|
||||
result: {}
|
||||
schema:
|
||||
description: initialize的参数schema
|
||||
properties:
|
||||
feedback: {}
|
||||
goal:
|
||||
properties: {}
|
||||
type: object
|
||||
result: {}
|
||||
required:
|
||||
- goal
|
||||
title: initialize参数
|
||||
type: object
|
||||
type: UniLabJsonCommandAsync
|
||||
add_solid:
|
||||
feedback:
|
||||
current_status: status
|
||||
progress: progress
|
||||
goal:
|
||||
vessel: vessel
|
||||
reagent: reagent
|
||||
mass: mass
|
||||
mol: mol
|
||||
purpose: purpose
|
||||
event: event
|
||||
rate_spec: rate_spec
|
||||
equiv: equiv
|
||||
ratio: ratio
|
||||
goal_default:
|
||||
vessel: ''
|
||||
reagent: ''
|
||||
mass: ''
|
||||
mol: ''
|
||||
purpose: ''
|
||||
event: ''
|
||||
rate_spec: ''
|
||||
equiv: ''
|
||||
ratio: ''
|
||||
handles: []
|
||||
result:
|
||||
success: success
|
||||
message: message
|
||||
return_info: return_info
|
||||
schema:
|
||||
description: ''
|
||||
properties:
|
||||
feedback:
|
||||
properties:
|
||||
current_status:
|
||||
type: string
|
||||
progress:
|
||||
type: number
|
||||
type: object
|
||||
goal:
|
||||
properties:
|
||||
vessel:
|
||||
type: string
|
||||
reagent:
|
||||
type: string
|
||||
mass:
|
||||
type: string
|
||||
mol:
|
||||
type: string
|
||||
purpose:
|
||||
type: string
|
||||
event:
|
||||
type: string
|
||||
rate_spec:
|
||||
type: string
|
||||
equiv:
|
||||
type: string
|
||||
ratio:
|
||||
type: string
|
||||
type: object
|
||||
result:
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
message:
|
||||
type: string
|
||||
return_info:
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- goal
|
||||
title: AddSolid
|
||||
type: object
|
||||
type: Add # 🔧 使用 Add action type
|
||||
module: unilabos.devices.virtual.virtual_solid_dispenser:VirtualSolidDispenser
|
||||
status_types:
|
||||
status: str
|
||||
current_reagent: str
|
||||
dispensed_amount: float
|
||||
total_operations: int
|
||||
type: python
|
||||
description: Virtual Solid Dispenser for Add Protocol Testing - supports mass and molar additions
|
||||
handles:
|
||||
- data_key: solid_out
|
||||
data_source: executor
|
||||
data_type: resource
|
||||
description: 固体试剂输出口
|
||||
handler_key: SolidOut
|
||||
io_type: source
|
||||
label: SolidOut
|
||||
side: SOUTH
|
||||
- data_key: solid_in
|
||||
data_source: handle
|
||||
data_type: resource
|
||||
description: 固体试剂输入口(连接试剂瓶)
|
||||
handler_key: SolidIn
|
||||
io_type: target
|
||||
label: SolidIn
|
||||
side: NORTH
|
||||
icon: ''
|
||||
init_param_schema:
|
||||
config:
|
||||
properties:
|
||||
max_capacity:
|
||||
default: 100.0
|
||||
type: number
|
||||
precision:
|
||||
default: 0.001
|
||||
type: number
|
||||
config:
|
||||
type: object
|
||||
device_id:
|
||||
type: string
|
||||
required: []
|
||||
type: object
|
||||
data:
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
current_reagent:
|
||||
type: string
|
||||
dispensed_amount:
|
||||
type: number
|
||||
total_operations:
|
||||
type: integer
|
||||
required:
|
||||
- status
|
||||
- current_reagent
|
||||
- dispensed_amount
|
||||
- total_operations
|
||||
type: object
|
||||
version: 0.0.1
|
||||
@@ -508,47 +508,90 @@ def convert_from_ros_msg_with_mapping(ros_msg: Any, value_mapping: Dict[str, str
|
||||
Python字典
|
||||
"""
|
||||
data: Dict[str, Any] = {}
|
||||
|
||||
# # 🔧 添加调试信息
|
||||
# print(f"🔍 convert_from_ros_msg_with_mapping 开始")
|
||||
# print(f"🔍 ros_msg 类型: {type(ros_msg)}")
|
||||
# print(f"🔍 ros_msg 内容: {ros_msg}")
|
||||
# print(f"🔍 value_mapping: {value_mapping}")
|
||||
# print("-" * 60)
|
||||
|
||||
for msg_name, attr_name in value_mapping.items():
|
||||
# print(f"🔍 处理映射: {msg_name} -> {attr_name}")
|
||||
|
||||
msg_path = msg_name.split(".")
|
||||
current = ros_msg
|
||||
|
||||
|
||||
# print(f"🔍 msg_path: {msg_path}")
|
||||
# print(f"🔍 current 初始值: {current} (类型: {type(current)})")
|
||||
|
||||
try:
|
||||
if not attr_name.endswith("[]"):
|
||||
# 处理单值映射
|
||||
for name in msg_path:
|
||||
current = getattr(current, name)
|
||||
data[attr_name] = convert_from_ros_msg(current)
|
||||
# print(f"🔍 处理单值映射")
|
||||
for i, name in enumerate(msg_path):
|
||||
# print(f"🔍 步骤 {i}: 获取属性 '{name}' 从 {type(current)}")
|
||||
if hasattr(current, name):
|
||||
current = getattr(current, name)
|
||||
# print(f"🔍 获取到: {current} (类型: {type(current)})")
|
||||
else:
|
||||
# print(f"❌ 属性 '{name}' 不存在于 {type(current)}")
|
||||
break
|
||||
|
||||
converted_value = convert_from_ros_msg(current)
|
||||
# print(f"🔍 转换后的值: {converted_value} (类型: {type(converted_value)})")
|
||||
data[attr_name] = converted_value
|
||||
# print(f"✅ 设置 data['{attr_name}'] = {converted_value}")
|
||||
else:
|
||||
# 处理列表值映射
|
||||
for name in msg_path:
|
||||
# print(f"🔍 处理列表值映射")
|
||||
for i, name in enumerate(msg_path):
|
||||
# print(f"🔍 列表步骤 {i}: 处理 '{name}' 从 {type(current)}")
|
||||
if name.endswith("[]"):
|
||||
base_name = name[:-2]
|
||||
# print(f"🔍 数组字段 base_name: '{base_name}'")
|
||||
if hasattr(current, base_name):
|
||||
current = list(getattr(current, base_name))
|
||||
# print(f"🔍 获取数组: {current} (长度: {len(current)})")
|
||||
else:
|
||||
# print(f"❌ 数组字段 '{base_name}' 不存在")
|
||||
current = []
|
||||
break
|
||||
else:
|
||||
if isinstance(current, list):
|
||||
# print(f"🔍 从列表中获取属性 '{name}'")
|
||||
next_level = []
|
||||
for item in current:
|
||||
if hasattr(item, name):
|
||||
next_level.append(getattr(item, name))
|
||||
current = next_level
|
||||
# print(f"🔍 列表处理结果: {current} (长度: {len(current)})")
|
||||
elif hasattr(current, name):
|
||||
current = getattr(current, name)
|
||||
# print(f"🔍 获取到属性: {current} (类型: {type(current)})")
|
||||
else:
|
||||
# print(f"❌ 属性 '{name}' 不存在")
|
||||
current = []
|
||||
break
|
||||
|
||||
attr_key = attr_name[:-2]
|
||||
if current:
|
||||
data[attr_key] = [convert_from_ros_msg(item) for item in current]
|
||||
except (AttributeError, TypeError):
|
||||
converted_list = [convert_from_ros_msg(item) for item in current]
|
||||
data[attr_key] = converted_list
|
||||
# print(f"✅ 设置 data['{attr_key}'] = {converted_list}")
|
||||
else:
|
||||
print(f"⚠️ 列表为空,跳过 '{attr_key}'")
|
||||
except (AttributeError, TypeError) as e:
|
||||
# print(f"❌ 映射转换错误 {msg_name} -> {attr_name}: {e}")
|
||||
logger.debug(f"Mapping conversion error for {msg_name} -> {attr_name}")
|
||||
continue
|
||||
|
||||
# print(f"🔍 当前 data 状态: {data}")
|
||||
# print("-" * 40)
|
||||
|
||||
#print(f"🔍 convert_from_ros_msg_with_mapping 结束")
|
||||
#print(f"🔍 最终 data: {data}")
|
||||
#print("=" * 60)
|
||||
return data
|
||||
|
||||
|
||||
|
||||
@@ -629,7 +629,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
execution_success = False
|
||||
action_return_value = None
|
||||
|
||||
self.lab_logger().info(f"执行动作: {action_name}")
|
||||
##### self.lab_logger().info(f"执行动作: {action_name}")
|
||||
goal = goal_handle.request
|
||||
|
||||
# 从目标消息中提取参数, 并调用对应的方法
|
||||
@@ -685,14 +685,14 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
final_resource = [convert_resources_to_type([i], final_type)[0] for i in resources_list]
|
||||
action_kwargs[k] = self.resource_tracker.figure_resource(final_resource)
|
||||
|
||||
self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}")
|
||||
##### self.lab_logger().info(f"准备执行: {action_kwargs}, 函数: {ACTION.__name__}")
|
||||
time_start = time.time()
|
||||
time_overall = 100
|
||||
|
||||
# 将阻塞操作放入线程池执行
|
||||
if asyncio.iscoroutinefunction(ACTION):
|
||||
try:
|
||||
self.lab_logger().info(f"异步执行动作 {ACTION}")
|
||||
##### self.lab_logger().info(f"异步执行动作 {ACTION}")
|
||||
future = ROS2DeviceNode.run_async_func(ACTION, trace_error=False, **action_kwargs)
|
||||
|
||||
def _handle_future_exception(fut):
|
||||
@@ -702,7 +702,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
execution_success = True
|
||||
except Exception as e:
|
||||
execution_error = traceback.format_exc()
|
||||
error(f"异步任务 {ACTION.__name__} 报错了")
|
||||
##### error(f"异步任务 {ACTION.__name__} 报错了")
|
||||
error(traceback.format_exc())
|
||||
|
||||
future.add_done_callback(_handle_future_exception)
|
||||
@@ -710,7 +710,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
self.lab_logger().error(f"创建异步任务失败: {traceback.format_exc()}")
|
||||
raise e
|
||||
else:
|
||||
self.lab_logger().info(f"同步执行动作 {ACTION}")
|
||||
##### self.lab_logger().info(f"同步执行动作 {ACTION}")
|
||||
future = self._executor.submit(ACTION, **action_kwargs)
|
||||
|
||||
def _handle_future_exception(fut):
|
||||
@@ -765,7 +765,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
self.lab_logger().info(f"动作 {action_name} 已取消")
|
||||
return action_type.Result()
|
||||
|
||||
self.lab_logger().info(f"动作执行完成: {action_name}")
|
||||
##### self.lab_logger().info(f"动作执行完成: {action_name}")
|
||||
del future
|
||||
|
||||
# 向Host更新物料当前状态
|
||||
@@ -801,7 +801,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
|
||||
# 发布结果
|
||||
goal_handle.succeed()
|
||||
self.lab_logger().info(f"设置动作成功: {action_name}")
|
||||
##### self.lab_logger().info(f"设置动作成功: {action_name}")
|
||||
|
||||
result_values = {}
|
||||
for msg_name, attr_name in action_value_mapping["result"].items():
|
||||
@@ -820,7 +820,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
|
||||
elif attr_name == "return_info":
|
||||
setattr(result_msg, attr_name, serialize_result_info(execution_error, execution_success, action_return_value))
|
||||
|
||||
self.lab_logger().info(f"动作 {action_name} 完成并返回结果")
|
||||
##### self.lab_logger().info(f"动作 {action_name} 完成并返回结果")
|
||||
return result_msg
|
||||
|
||||
return execute_callback
|
||||
|
||||
@@ -563,7 +563,7 @@ class HostNode(BaseROS2DeviceNode):
|
||||
if hasattr(bridge, "publish_device_status"):
|
||||
bridge.publish_device_status(self.device_status, device_id, property_name)
|
||||
self.lab_logger().debug(
|
||||
f"[Host Node] Status updated: {device_id}.{property_name} = {msg.data}"
|
||||
f"[Host Node] Status updated: {device_id}.{property_name} = {msg.data}"
|
||||
)
|
||||
|
||||
def send_goal(
|
||||
|
||||
@@ -182,26 +182,54 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
# 从目标消息中提取参数, 并调用Protocol生成器(根据设备连接图)生成action步骤
|
||||
goal = goal_handle.request
|
||||
protocol_kwargs = convert_from_ros_msg_with_mapping(goal, action_value_mapping["goal"])
|
||||
|
||||
# # 🔧 添加调试信息
|
||||
# print(f"🔍 转换后的 protocol_kwargs: {protocol_kwargs}")
|
||||
# print(f"🔍 vessel 在转换后: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
||||
|
||||
# 向Host查询物料当前状态
|
||||
for k, v in goal.get_fields_and_field_types().items():
|
||||
if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
|
||||
r = ResourceGet.Request()
|
||||
resource_id = (
|
||||
protocol_kwargs[k]["id"] if v == "unilabos_msgs/Resource" else protocol_kwargs[k][0]["id"]
|
||||
)
|
||||
r.id = resource_id
|
||||
r.with_children = True
|
||||
response = await self._resource_clients["resource_get"].call_async(r)
|
||||
protocol_kwargs[k] = list_to_nested_dict(
|
||||
[convert_from_ros_msg(rs) for rs in response.resources]
|
||||
)
|
||||
# # 🔧 完全禁用Host查询,直接使用转换后的数据
|
||||
# print(f"🔧 跳过Host查询,直接使用转换后的数据")
|
||||
|
||||
# 🔧 额外验证:确保vessel数据完整
|
||||
if 'vessel' in protocol_kwargs:
|
||||
vessel_data = protocol_kwargs['vessel']
|
||||
#print(f"🔍 验证vessel数据: {vessel_data}")
|
||||
|
||||
# 如果vessel是空字典,尝试重新构建
|
||||
if not vessel_data or (isinstance(vessel_data, dict) and not vessel_data):
|
||||
# print(f"⚠️ vessel数据为空,尝试从原始goal重新提取...")
|
||||
|
||||
# 直接从原始goal提取vessel
|
||||
if hasattr(goal, 'vessel') and goal.vessel:
|
||||
# print(f"🔍 原始goal.vessel: {goal.vessel}")
|
||||
# 手动转换vessel
|
||||
vessel_data = {
|
||||
'id': goal.vessel.id,
|
||||
'name': goal.vessel.name,
|
||||
'type': goal.vessel.type,
|
||||
'category': goal.vessel.category,
|
||||
'config': goal.vessel.config,
|
||||
'data': goal.vessel.data
|
||||
}
|
||||
protocol_kwargs['vessel'] = vessel_data
|
||||
# print(f"✅ 手动重建vessel数据: {vessel_data}")
|
||||
else:
|
||||
# print(f"❌ 无法从原始goal提取vessel数据")
|
||||
# 创建一个基本的vessel
|
||||
vessel_data = {'id': 'default_vessel'}
|
||||
protocol_kwargs['vessel'] = vessel_data
|
||||
# print(f"🔧 创建默认vessel: {vessel_data}")
|
||||
|
||||
#print(f"🔍 最终传递给协议的 protocol_kwargs: {protocol_kwargs}")
|
||||
#print(f"🔍 最终的 vessel: {protocol_kwargs.get('vessel', 'NOT_FOUND')}")
|
||||
|
||||
from unilabos.resources.graphio import physical_setup_graph
|
||||
|
||||
self.lab_logger().info(f"Working on physical setup: {physical_setup_graph}")
|
||||
self.lab_logger().info(f"Protocol kwargs: {goal}")
|
||||
self.lab_logger().info(f"Protocol kwargs: {action_value_mapping}")
|
||||
protocol_steps = protocol_steps_generator(G=physical_setup_graph, **protocol_kwargs)
|
||||
|
||||
|
||||
self.lab_logger().info(f"Goal received: {protocol_kwargs}, running steps: \n{protocol_steps}")
|
||||
|
||||
time_start = time.time()
|
||||
@@ -235,14 +263,14 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
}
|
||||
)
|
||||
|
||||
# 向Host更新物料当前状态
|
||||
for k, v in goal.get_fields_and_field_types().items():
|
||||
if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
|
||||
r = ResourceUpdate.Request()
|
||||
r.resources = [
|
||||
convert_to_ros_msg(Resource, rs) for rs in nested_dict_to_list(protocol_kwargs[k])
|
||||
]
|
||||
response = await self._resource_clients["resource_update"].call_async(r)
|
||||
# # 向Host更新物料当前状态
|
||||
# for k, v in goal.get_fields_and_field_types().items():
|
||||
# if v in ["unilabos_msgs/Resource", "sequence<unilabos_msgs/Resource>"]:
|
||||
# r = ResourceUpdate.Request()
|
||||
# r.resources = [
|
||||
# convert_to_ros_msg(Resource, rs) for rs in nested_dict_to_list(protocol_kwargs[k])
|
||||
# ]
|
||||
# response = await self._resource_clients["resource_update"].call_async(r)
|
||||
|
||||
# 设置成功状态和返回值
|
||||
execution_success = True
|
||||
@@ -287,7 +315,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
serialize_result_info(execution_error, execution_success, protocol_return_value),
|
||||
)
|
||||
|
||||
self.lab_logger().info(f"协议 {protocol_name} 完成并返回结果")
|
||||
self.lab_logger().info(f"🤩🤩🤩🤩🤩🤩协议 {protocol_name} 完成并返回结果😎😎😎😎😎😎")
|
||||
return result
|
||||
|
||||
return execute_protocol
|
||||
@@ -309,7 +337,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
action_client = self._action_clients[action_id]
|
||||
goal_msg = convert_to_ros_msg(action_client._action_type.Goal(), action_kwargs)
|
||||
|
||||
self.lab_logger().info(f"发送动作请求到: {action_id}")
|
||||
##### self.lab_logger().info(f"发送动作请求到: {action_id}")
|
||||
action_client.wait_for_server()
|
||||
|
||||
# 等待动作完成
|
||||
@@ -321,7 +349,7 @@ class ROS2ProtocolNode(BaseROS2DeviceNode):
|
||||
return None
|
||||
|
||||
result_future = await handle.get_result_async()
|
||||
self.lab_logger().info(f"动作完成: {action_name}")
|
||||
##### self.lab_logger().info(f"动作完成: {action_name}")
|
||||
|
||||
return result_future.result
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 添加试剂的目标参数
|
||||
string vessel # 目标容器(必需)
|
||||
Resource vessel # 目标容器(必需)
|
||||
string reagent # 试剂名称(必需)
|
||||
string volume # 体积(如 "2.7 mL",可选)
|
||||
string mass # 质量(如 "19.3 g",可选)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 固体加样操作的目标参数
|
||||
string vessel # 目标容器(必需)
|
||||
Resource vessel # 目标容器(必需)
|
||||
string reagent # 试剂名称(必需)
|
||||
string mass # 质量字符串(如 "2.9 g",可选)
|
||||
string mol # 摩尔数字符串(如 "0.12 mol",可选)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Request - 与您的 AdjustPHProtocol 类匹配
|
||||
string vessel
|
||||
Resource vessel
|
||||
float64 ph_value
|
||||
string reagent
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 离心操作的目标参数
|
||||
string vessel # 离心容器
|
||||
Resource vessel # 离心容器
|
||||
float64 speed # 离心速度 (rpm)
|
||||
float64 time # 离心时间 (秒)
|
||||
float64 temp # 温度 (可选,摄氏度)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Organic
|
||||
string vessel # Vessel to clean.
|
||||
Resource vessel # Vessel to clean.
|
||||
string solvent # Solvent to clean vessel with.
|
||||
float64 volume # Optional. Volume of solvent to clean vessel with.
|
||||
float64 temp # Optional. Temperature to heat vessel to while cleaning.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
string vessel # 要清洗的容器名称
|
||||
Resource vessel # 要清洗的容器名称
|
||||
string solvent # 用于清洗容器的溶剂名称
|
||||
float64 volume # 清洗溶剂的体积,可选参数
|
||||
float64 temp # 清洗时的温度,可选参数
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 结晶操作的目标参数
|
||||
string vessel # 结晶容器
|
||||
Resource vessel # 结晶容器
|
||||
float64 ramp_time # 升温/降温时间 (可选,秒)
|
||||
float64 ramp_temp # 目标温度 (可选,摄氏度)
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 溶解操作的目标参数
|
||||
string vessel # 装有要溶解物质的容器名称(必需)
|
||||
Resource vessel # 装有要溶解物质的容器名称(必需)
|
||||
string solvent # 用于溶解物质的溶剂名称(可选)
|
||||
string volume # 溶剂的体积(如 "10 mL",可选)
|
||||
string amount # 要溶解物质的量描述(可选)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Request
|
||||
string compound # 化合物
|
||||
string vessel # 干燥容器
|
||||
Resource vessel # 干燥容器
|
||||
---
|
||||
# Result
|
||||
bool success # 操作是否成功
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Organic Synthesis Station EvacuateAndRefill Action
|
||||
string vessel
|
||||
Resource vessel
|
||||
string gas
|
||||
---
|
||||
string return_info
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Organic Synthesis Station Evaporate Action
|
||||
string vessel # 目标容器
|
||||
Resource vessel # 目标容器
|
||||
float64 pressure # 真空度
|
||||
float64 temp # 温度
|
||||
string time # 🔧 蒸发时间(支持带单位,如"3 min","180",默认秒)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Goal - 过滤操作的目标参数
|
||||
string vessel # 过滤容器(必需)
|
||||
string filtrate_vessel # 滤液容器(可选)
|
||||
Resource vessel # 过滤容器(必需)
|
||||
Resource filtrate_vessel # 滤液容器(可选)
|
||||
bool stir # 是否搅拌(默认false)
|
||||
float64 stir_speed # 搅拌速度(默认0.0)
|
||||
float64 temp # 温度(默认25.0)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
string from_vessel # 源容器的名称,即物质起始所在的容器
|
||||
string to_vessel # 目标容器的名称,物质过滤后要到达的容器
|
||||
string filter_through # 过滤时所通过的介质,如滤纸、柱子等
|
||||
Resource from_vessel # 源容器的名称,即物质起始所在的容器
|
||||
Resource to_vessel # 目标容器的名称,物质过滤后要到达的容器
|
||||
Resource filter_through # 过滤时所通过的介质,如滤纸、柱子等
|
||||
string eluting_solvent # 洗脱溶剂的名称,可选参数
|
||||
float64 eluting_volume # 洗脱溶剂的体积,可选参数
|
||||
int32 eluting_repeats # 洗脱操作的重复次数,默认为 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 加热冷却操作的目标参数
|
||||
string vessel # 加热容器名称(必需)
|
||||
Resource vessel # 加热容器名称(必需)
|
||||
float64 temp # 目标温度(可选,默认25.0)
|
||||
string time # 🔧 加热时间(支持带单位,如"5 min","300",默认秒)
|
||||
string temp_spec # 温度规格(可选)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Organic
|
||||
string vessel
|
||||
Resource vessel
|
||||
float64 temp
|
||||
string purpose
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Organic
|
||||
string vessel
|
||||
Resource vessel
|
||||
---
|
||||
string return_info
|
||||
bool success
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Request
|
||||
string temp
|
||||
string time
|
||||
string vessel
|
||||
Resource vessel
|
||||
---
|
||||
# Result
|
||||
bool success
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Organic
|
||||
string from_vessel
|
||||
string to_vessel
|
||||
Resource from_vessel
|
||||
Resource to_vessel
|
||||
float64 volume
|
||||
string amount
|
||||
float64 time
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 清洗/吹扫操作的目标参数
|
||||
string vessel # 清洗容器
|
||||
Resource vessel # 清洗容器
|
||||
string gas # 清洗气体 (可选)
|
||||
float64 time # 清洗时间 (可选,秒)
|
||||
float64 pressure # 压力 (可选,Pa)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
string ratio # 溶剂比例(如"1:1","3:7")
|
||||
string solvent1 # 第一种溶剂
|
||||
string solvent2 # 第二种溶剂
|
||||
string vessel # 目标容器
|
||||
Resource vessel # 目标容器
|
||||
string volume # 🔧 总体积(支持带单位,如"100 mL","50",默认mL)
|
||||
---
|
||||
# Result
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Request
|
||||
string solvent
|
||||
Resource vessel
|
||||
---
|
||||
# Result
|
||||
bool success
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Goal - 柱层析操作的目标参数
|
||||
string from_vessel # 源容器的名称,即样品起始所在的容器(必需)
|
||||
string to_vessel # 目标容器的名称,分离后的样品要到达的容器(必需)
|
||||
Resource from_vessel # 源容器的名称,即样品起始所在的容器(必需)
|
||||
Resource to_vessel # 目标容器的名称,分离后的样品要到达的容器(必需)
|
||||
string column # 所使用的柱子的名称(必需)
|
||||
string rf # Rf值(可选)
|
||||
string pct1 # 第一种溶剂百分比(如 "40 %",可选)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
# Goal - 分离操作的目标参数
|
||||
string vessel # 分离容器名称(XDL参数,必需)
|
||||
Resource vessel # 分离容器名称(XDL参数,必需)
|
||||
string purpose # 分离目的 ('wash', 'extract', 'separate',可选)
|
||||
string product_phase # 产物相 ('top', 'bottom',可选)
|
||||
string from_vessel # 源容器(可选)
|
||||
string separation_vessel # 分离容器(与vessel同义,可选)
|
||||
string to_vessel # 目标容器(可选)
|
||||
string waste_phase_to_vessel # 废相目标容器(可选)
|
||||
string product_vessel # 产物收集容器(XDL参数,可选)
|
||||
string waste_vessel # 废液收集容器(XDL参数,可选)
|
||||
Resource from_vessel # 源容器(可选)
|
||||
Resource separation_vessel # 分离容器(与vessel同义,可选)
|
||||
Resource to_vessel # 目标容器(可选)
|
||||
Resource waste_phase_to_vessel # 废相目标容器(可选)
|
||||
Resource product_vessel # 产物收集容器(XDL参数,可选)
|
||||
Resource waste_vessel # 废液收集容器(XDL参数,可选)
|
||||
string solvent # 溶剂名称(可选)
|
||||
string solvent_volume # 溶剂体积(如 "200 mL",可选)
|
||||
string volume # 体积规格(XDL参数,如 "?",可选)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 启动清洗/吹扫操作的目标参数
|
||||
string vessel # 清洗容器
|
||||
Resource vessel # 清洗容器
|
||||
string gas # 清洗气体 (可选)
|
||||
float64 pressure # 压力 (可选,Pa)
|
||||
float64 flow_rate # 流速 (可选,mL/min)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 启动搅拌操作的目标参数
|
||||
string vessel # 搅拌容器
|
||||
Resource vessel # 搅拌容器
|
||||
float64 stir_speed # 搅拌速度 (可选,rpm)
|
||||
string purpose # 搅拌目的 (可选)
|
||||
---
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 搅拌操作的目标参数
|
||||
string vessel # 搅拌容器名称(必需)
|
||||
Resource vessel # 搅拌容器名称(必需)
|
||||
string time # 🔧 搅拌时间(如 "0.5 h", "30 min", "300",默认秒)
|
||||
string event # 事件标识(如 "A", "B")
|
||||
string time_spec # 时间规格(如 "several minutes")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 停止清洗/吹扫操作的目标参数
|
||||
string vessel # 清洗容器
|
||||
Resource vessel # 清洗容器
|
||||
---
|
||||
# Result - 操作结果
|
||||
bool success # 操作是否成功
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Goal - 停止搅拌操作的目标参数
|
||||
string vessel # 搅拌容器
|
||||
Resource vessel # 搅拌容器
|
||||
---
|
||||
# Result - 操作结果
|
||||
bool success # 操作是否成功
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Goal - 固体清洗操作的目标参数
|
||||
string vessel # 装有固体的容器名称(必需)
|
||||
Resource vessel # 装有固体的容器名称(必需)
|
||||
string solvent # 清洗溶剂名称(必需)
|
||||
string volume # 🔧 体积(支持数字和带单位的字符串,如"100 mL","?")
|
||||
string filtrate_vessel # 滤液收集容器(可选,默认"")
|
||||
Resource filtrate_vessel # 滤液收集容器(可选,默认"")
|
||||
float64 temp # 清洗温度(可选,默认25.0)
|
||||
bool stir # 是否搅拌(可选,默认false)
|
||||
float64 stir_speed # 搅拌速度(可选,默认0.0)
|
||||
|
||||
Reference in New Issue
Block a user