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

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

View File

@@ -67,37 +67,47 @@ def generate_dry_protocol(
# 默认参数
dry_temp = 60.0 # 默认干燥温度 60°C
dry_time = 3600.0 # 默认干燥时间 1小时3600秒
simulation_time = 60.0 # 模拟时间 1分钟
print(f"DRY: 开始生成干燥协议")
print(f" - 化合物: {compound}")
print(f" - 容器: {vessel}")
print(f" - 干燥温度: {dry_temp}°C")
print(f" - 干燥时间: {dry_time/60:.0f} 分钟")
print(f"🌡️ DRY: 开始生成干燥协议")
print(f" 🧪 化合物: {compound}")
print(f" 🥽 容器: {vessel}")
print(f" 🔥 干燥温度: {dry_temp}°C")
print(f" 干燥时间: {dry_time/60:.0f} 分钟")
# 1. 验证目标容器存在
print(f"\n📋 步骤1: 验证目标容器 '{vessel}' 是否存在...")
if vessel not in G.nodes():
print(f"DRY: 警告 - 容器 '{vessel}' 不存在于系统中,跳过干燥")
print(f"⚠️ DRY: 警告 - 容器 '{vessel}' 不存在于系统中,跳过干燥 😢")
return action_sequence
print(f"✅ 容器 '{vessel}' 验证通过!")
# 2. 查找相连的加热器
print(f"\n🔍 步骤2: 查找与容器相连的加热器...")
heater_id = find_connected_heater(G, vessel)
if heater_id is None:
print(f"DRY: 警告 - 未找到与容器 '{vessel}' 相连的加热器,跳过干燥")
print(f"😭 DRY: 警告 - 未找到与容器 '{vessel}' 相连的加热器,跳过干燥")
print(f"🎭 添加模拟干燥动作...")
# 添加一个等待动作,表示干燥过程(模拟)
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 60.0, # 等待1分钟
"time": 10.0, # 模拟等待时间
"description": f"模拟干燥 {compound} (无加热器可用)"
}
})
print(f"📄 DRY: 协议生成完成,共 {len(action_sequence)} 个动作 🎯")
return action_sequence
print(f"🎉 找到加热器: {heater_id}!")
# 3. 启动加热器进行干燥
print(f"DRY: 启动加热器 {heater_id} 进行干燥")
print(f"\n🚀 步骤3: 开始执行干燥流程...")
print(f"🔥 启动加热器 {heater_id} 进行干燥")
# 3.1 启动加热
print(f" ⚡ 动作1: 启动加热到 {dry_temp}°C...")
action_sequence.append({
"device_id": heater_id,
"action_name": "heat_chill_start",
@@ -107,29 +117,35 @@ def generate_dry_protocol(
"purpose": f"干燥 {compound}"
}
})
print(f" ✅ 加热器启动命令已添加 🔥")
# 3.2 等待温度稳定
print(f" ⏳ 动作2: 等待温度稳定...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 60.0,
"time": 10.0,
"description": f"等待温度稳定到 {dry_temp}°C"
}
})
print(f" ✅ 温度稳定等待命令已添加 🌡️")
# 3.3 保持干燥温度
print(f" 🔄 动作3: 保持干燥温度 {simulation_time/60:.0f} 分钟...")
action_sequence.append({
"device_id": heater_id,
"action_name": "heat_chill",
"action_kwargs": {
"vessel": vessel,
"temp": dry_temp,
"time": dry_time,
"time": simulation_time,
"purpose": f"干燥 {compound},保持温度 {dry_temp}°C"
}
})
print(f" ✅ 温度保持命令已添加 🌡️⏰")
# 3.4 停止加热
print(f" ⏹️ 动作4: 停止加热...")
action_sequence.append({
"device_id": heater_id,
"action_name": "heat_chill_stop",
@@ -138,18 +154,22 @@ def generate_dry_protocol(
"purpose": f"干燥完成,停止加热"
}
})
print(f" ✅ 停止加热命令已添加 🛑")
# 3.5 等待冷却
print(f" ❄️ 动作5: 等待冷却...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 300.0, # 等待5分钟冷却
"time": 10.0, # 等待10秒冷却
"description": f"等待 {compound} 冷却"
}
})
print(f" ✅ 冷却等待命令已添加 🧊")
print(f"DRY: 协议生成完成,共 {len(action_sequence)} 个动作")
print(f"DRY: 预计总时间: {(dry_time + 360)/60:.0f} 分钟")
print(f"\n🎊 DRY: 协议生成完成,共 {len(action_sequence)} 个动作 🎯")
print(f"⏱️ DRY: 预计总时间: {(dry_time + 360)/60:.0f} 分钟")
print(f"🏁 所有动作序列准备就绪! ✨")
return action_sequence

View File

@@ -7,7 +7,7 @@ logger = logging.getLogger(__name__)
def debug_print(message):
"""调试输出"""
print(f"[EVAPORATE] {message}", flush=True)
print(f"🧪 [EVAPORATE] {message}", flush=True)
logger.info(f"[EVAPORATE] {message}")
def parse_time_input(time_input: Union[str, float]) -> float:
@@ -21,18 +21,20 @@ def parse_time_input(time_input: Union[str, float]) -> float:
float: 时间(秒)
"""
if isinstance(time_input, (int, float)):
debug_print(f"⏱️ 时间输入为数字: {time_input}s ✨")
return float(time_input)
if not time_input or not str(time_input).strip():
debug_print(f"⚠️ 时间输入为空,使用默认值: 180s (3分钟) 🕐")
return 180.0 # 默认3分钟
time_str = str(time_input).lower().strip()
debug_print(f"解析时间输入: '{time_str}'")
debug_print(f"🔍 解析时间输入: '{time_str}' 📝")
# 处理未知时间
if time_str in ['?', 'unknown', 'tbd']:
default_time = 180.0 # 默认3分钟
debug_print(f"检测到未知时间,使用默认值: {default_time}s")
debug_print(f"检测到未知时间,使用默认值: {default_time}s (3分钟) 🤷‍♀️")
return default_time
# 移除空格并提取数字和单位
@@ -45,10 +47,10 @@ def parse_time_input(time_input: Union[str, float]) -> float:
# 如果无法解析,尝试直接转换为数字(默认秒)
try:
value = float(time_str)
debug_print(f"时间解析: {time_str}{value}s无单位默认秒")
debug_print(f"时间解析成功: {time_str}{value}s无单位默认秒")
return value
except ValueError:
debug_print(f"⚠️ 无法解析时间: '{time_str}'使用默认值180s")
debug_print(f" 无法解析时间: '{time_str}'使用默认值180s (3分钟) 😅")
return 180.0
value = float(match.group(1))
@@ -57,14 +59,17 @@ def parse_time_input(time_input: Union[str, float]) -> float:
# 转换为秒
if unit in ['min', 'minute']:
time_sec = value * 60.0 # min -> s
debug_print(f"🕐 时间转换: {value} 分钟 → {time_sec}s ⏰")
elif unit in ['h', 'hr', 'hour']:
time_sec = value * 3600.0 # h -> s
debug_print(f"🕐 时间转换: {value} 小时 → {time_sec}s ({time_sec/60:.1f}分钟) ⏰")
elif unit in ['d', 'day']:
time_sec = value * 86400.0 # d -> s
debug_print(f"🕐 时间转换: {value} 天 → {time_sec}s ({time_sec/3600:.1f}小时) ⏰")
else: # s, sec, second 或默认
time_sec = value # 已经是s
debug_print(f"🕐 时间转换: {value}s → {time_sec}s (已是秒) ⏰")
debug_print(f"时间转换: {value}{unit}{time_sec}s")
return time_sec
def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
@@ -78,28 +83,30 @@ def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
Returns:
str: 找到的旋转蒸发仪设备ID如果没找到返回None
"""
debug_print("查找旋转蒸发仪设备...")
debug_print("🔍 开始查找旋转蒸发仪设备... 🌪️")
# 如果指定了vessel先检查是否存在且是旋转蒸发仪
if vessel:
debug_print(f"🎯 检查指定设备: {vessel} 🔧")
if vessel in G.nodes():
node_data = G.nodes[vessel]
node_class = node_data.get('class', '')
node_type = node_data.get('type', '')
debug_print(f"检查指定设备 {vessel}: class={node_class}, type={node_type}")
debug_print(f"📋 设备信息 {vessel}: class={node_class}, type={node_type}")
# 检查是否为旋转蒸发仪
if any(keyword in str(node_class).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
debug_print(f" 找到指定的旋转蒸发仪: {vessel}")
debug_print(f"🎉 找到指定的旋转蒸发仪: {vessel}")
return vessel
elif node_type == 'device':
debug_print(f" 指定设备存在,尝试直接使用: {vessel}")
debug_print(f" 指定设备存在,尝试直接使用: {vessel} 🔧")
return vessel
else:
debug_print(f" 指定的设备 {vessel} 不存在")
debug_print(f" 指定的设备 {vessel} 不存在 😞")
# 在所有设备中查找旋转蒸发仪
debug_print("🔎 在所有设备中搜索旋转蒸发仪... 🕵️‍♀️")
rotavap_candidates = []
for node_id, node_data in G.nodes(data=True):
@@ -113,17 +120,17 @@ def find_rotavap_device(G: nx.DiGraph, vessel: str = None) -> Optional[str]:
# 检查设备类型
if any(keyword in str(node_class).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
rotavap_candidates.append(node_id)
debug_print(f" 找到旋转蒸发仪候选: {node_id} (class: {node_class})")
debug_print(f"🌟 找到旋转蒸发仪候选: {node_id} (class: {node_class}) 🌪️")
elif any(keyword in str(node_id).lower() for keyword in ['rotavap', 'rotary', 'evaporat']):
rotavap_candidates.append(node_id)
debug_print(f" 找到旋转蒸发仪候选 (按名称): {node_id}")
debug_print(f"🌟 找到旋转蒸发仪候选 (按名称): {node_id} 🌪️")
if rotavap_candidates:
selected = rotavap_candidates[0] # 选择第一个找到的
debug_print(f" 选择旋转蒸发仪: {selected}")
debug_print(f"🎯 选择旋转蒸发仪: {selected} 🏆")
return selected
debug_print(" 未找到旋转蒸发仪设备")
debug_print("😭 未找到旋转蒸发仪设备 💔")
return None
def find_connected_vessel(G: nx.DiGraph, rotavap_device: str) -> Optional[str]:
@@ -137,31 +144,33 @@ def find_connected_vessel(G: nx.DiGraph, rotavap_device: str) -> Optional[str]:
Returns:
str: 连接的容器ID如果没找到返回None
"""
debug_print(f"查找与 {rotavap_device} 连接的容器...")
debug_print(f"🔗 查找与 {rotavap_device} 连接的容器... 🥽")
# 查看旋转蒸发仪的子设备
rotavap_data = G.nodes[rotavap_device]
children = rotavap_data.get('children', [])
debug_print(f"👶 检查子设备: {children}")
for child_id in children:
if child_id in G.nodes():
child_data = G.nodes[child_id]
child_type = child_data.get('type', '')
if child_type == 'container':
debug_print(f" 找到连接的容器: {child_id}")
debug_print(f"🎉 找到连接的容器: {child_id} 🥽✨")
return child_id
# 查看邻接的容器
debug_print("🤝 检查邻接设备...")
for neighbor in G.neighbors(rotavap_device):
neighbor_data = G.nodes[neighbor]
neighbor_type = neighbor_data.get('type', '')
if neighbor_type == 'container':
debug_print(f" 找到邻接的容器: {neighbor}")
debug_print(f"🎉 找到邻接的容器: {neighbor} 🥽✨")
return neighbor
debug_print(" 未找到连接的容器")
debug_print("😞 未找到连接的容器 💔")
return None
def generate_evaporate_protocol(
@@ -191,110 +200,136 @@ def generate_evaporate_protocol(
List[Dict[str, Any]]: 动作序列
"""
debug_print("=" * 50)
debug_print("开始生成蒸发协议(支持单位)")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - pressure: {pressure} bar")
debug_print(f" - temp: {temp}°C")
debug_print(f" - time: {time} (类型: {type(time)})")
debug_print(f" - stir_speed: {stir_speed} RPM")
debug_print(f" - solvent: '{solvent}'")
debug_print("=" * 50)
debug_print("🌟" * 20)
debug_print("🌪️ 开始生成蒸发协议(支持单位)")
debug_print(f"📝 输入参数:")
debug_print(f" 🥽 vessel: {vessel}")
debug_print(f" 💨 pressure: {pressure} bar")
debug_print(f" 🌡️ temp: {temp}°C")
debug_print(f" time: {time} (类型: {type(time)})")
debug_print(f" 🌪️ stir_speed: {stir_speed} RPM")
debug_print(f" 🧪 solvent: '{solvent}'")
debug_print("🌟" * 20)
# === 步骤1: 查找旋转蒸发仪设备 ===
debug_print("步骤1: 查找旋转蒸发仪设备...")
debug_print("📍 步骤1: 查找旋转蒸发仪设备... 🔍")
# 验证vessel参数
if not vessel:
debug_print("❌ vessel 参数不能为空! 😱")
raise ValueError("vessel 参数不能为空")
# 查找旋转蒸发仪设备
rotavap_device = find_rotavap_device(G, vessel)
if not rotavap_device:
debug_print("💥 未找到旋转蒸发仪设备! 😭")
raise ValueError(f"未找到旋转蒸发仪设备。请检查组态图中是否包含 class 包含 'rotavap''rotary''evaporat' 的设备")
debug_print(f"🎉 成功找到旋转蒸发仪: {rotavap_device}")
# === 步骤2: 确定目标容器 ===
debug_print("步骤2: 确定目标容器...")
debug_print("📍 步骤2: 确定目标容器... 🥽")
target_vessel = vessel
# 如果vessel就是旋转蒸发仪设备查找连接的容器
if vessel == rotavap_device:
debug_print("🔄 vessel就是旋转蒸发仪查找连接的容器...")
connected_vessel = find_connected_vessel(G, rotavap_device)
if connected_vessel:
target_vessel = connected_vessel
debug_print(f"使用连接的容器: {target_vessel}")
debug_print(f"使用连接的容器: {target_vessel} 🥽✨")
else:
debug_print(f"未找到连接的容器,使用设备本身: {rotavap_device}")
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}")
debug_print(f"使用指定的容器: {vessel} 🥽✨")
target_vessel = vessel
else:
debug_print(f"容器 '{vessel}' 不存在或类型不正确,使用旋转蒸发仪设备: {rotavap_device}")
debug_print(f"⚠️ 容器 '{vessel}' 不存在或类型不正确,使用旋转蒸发仪设备: {rotavap_device} 🔧")
target_vessel = rotavap_device
# === 🔧 新增步骤3单位解析处理 ===
debug_print("步骤3: 单位解析处理...")
debug_print("📍 步骤3: 单位解析处理...")
# 解析时间
final_time = parse_time_input(time)
debug_print(f"时间解析: {time}{final_time}s ({final_time/60:.1f}分钟)")
debug_print(f"🎯 时间解析完成: {time}{final_time}s ({final_time/60:.1f}分钟) ⏰✨")
# === 步骤4: 参数验证和修正 ===
debug_print("步骤4: 参数验证和修正...")
debug_print("📍 步骤4: 参数验证和修正... 🔧")
# 修正参数范围
if pressure <= 0 or pressure > 1.0:
debug_print(f"真空度 {pressure} bar 超出范围,修正为 0.1 bar")
debug_print(f"⚠️ 真空度 {pressure} bar 超出范围,修正为 0.1 bar 💨")
pressure = 0.1
else:
debug_print(f"✅ 真空度 {pressure} bar 在正常范围内 💨")
if temp < 10.0 or temp > 200.0:
debug_print(f"温度 {temp}°C 超出范围,修正为 60°C")
debug_print(f"⚠️ 温度 {temp}°C 超出范围,修正为 60°C 🌡️")
temp = 60.0
else:
debug_print(f"✅ 温度 {temp}°C 在正常范围内 🌡️")
if final_time <= 0:
debug_print(f"时间 {final_time}s 无效,修正为 180s")
debug_print(f"⚠️ 时间 {final_time}s 无效,修正为 180s (3分钟) ⏰")
final_time = 180.0
else:
debug_print(f"✅ 时间 {final_time}s ({final_time/60:.1f}分钟) 有效 ⏰")
if stir_speed < 10.0 or stir_speed > 300.0:
debug_print(f"旋转速度 {stir_speed} RPM 超出范围,修正为 100 RPM")
debug_print(f"⚠️ 旋转速度 {stir_speed} RPM 超出范围,修正为 100 RPM 🌪️")
stir_speed = 100.0
else:
debug_print(f"✅ 旋转速度 {stir_speed} RPM 在正常范围内 🌪️")
# 根据溶剂优化参数
if solvent:
debug_print(f"根据溶剂 '{solvent}' 优化参数...")
debug_print(f"🧪 根据溶剂 '{solvent}' 优化参数... 🔬")
solvent_lower = solvent.lower()
if any(s in solvent_lower for s in ['water', 'aqueous', 'h2o']):
temp = max(temp, 80.0)
pressure = max(pressure, 0.2)
debug_print("水系溶剂:提高温度和真空度")
debug_print("💧 水系溶剂:提高温度和真空度 🌡️💨")
elif any(s in solvent_lower for s in ['ethanol', 'methanol', 'acetone']):
temp = min(temp, 50.0)
pressure = min(pressure, 0.05)
debug_print("易挥发溶剂:降低温度和真空度")
debug_print("🍺 易挥发溶剂:降低温度和真空度 🌡️💨")
elif any(s in solvent_lower for s in ['dmso', 'dmi', 'toluene']):
temp = max(temp, 100.0)
pressure = min(pressure, 0.01)
debug_print("高沸点溶剂:提高温度,降低真空度")
debug_print("🔥 高沸点溶剂:提高温度,降低真空度 🌡️💨")
else:
debug_print("🧪 通用溶剂,使用标准参数 ✨")
else:
debug_print("🤷‍♀️ 未指定溶剂,使用默认参数 ✨")
debug_print(f"最终参数: pressure={pressure}, temp={temp}, time={final_time}, stir_speed={stir_speed}")
debug_print(f"🎯 最终参数: pressure={pressure} bar 💨, temp={temp}°C 🌡️, time={final_time}s ⏰, stir_speed={stir_speed} RPM 🌪️")
# === 步骤5: 生成动作序列 ===
debug_print("步骤5: 生成动作序列...")
debug_print("📍 步骤5: 生成动作序列... 🎬")
action_sequence = []
# 等待稳定
# 1. 等待稳定
debug_print(" 🔄 动作1: 添加初始等待稳定... ⏳")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 10}
})
debug_print(" ✅ 初始等待动作已添加 ⏳✨")
# 2. 执行蒸发
debug_print(f" 🌪️ 动作2: 执行蒸发操作...")
debug_print(f" 🔧 设备: {rotavap_device}")
debug_print(f" 🥽 容器: {target_vessel}")
debug_print(f" 💨 真空度: {pressure} bar")
debug_print(f" 🌡️ 温度: {temp}°C")
debug_print(f" ⏰ 时间: {final_time}s ({final_time/60:.1f}分钟)")
debug_print(f" 🌪️ 旋转速度: {stir_speed} RPM")
# 执行蒸发
debug_print(f"执行蒸发: 设备={rotavap_device}, 容器={target_vessel}")
evaporate_action = {
"device_id": rotavap_device,
"action_name": "evaporate",
@@ -308,97 +343,24 @@ def generate_evaporate_protocol(
}
}
action_sequence.append(evaporate_action)
debug_print(" ✅ 蒸发动作已添加 🌪️✨")
# 蒸发后等待
# 3. 蒸发后等待
debug_print(" 🔄 动作3: 添加蒸发后等待... ⏳")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 30}
"action_kwargs": {"time": 10}
})
debug_print(" ✅ 蒸发后等待动作已添加 ⏳✨")
# === 总结 ===
debug_print("=" * 50)
debug_print(f"蒸发协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"旋转蒸发仪: {rotavap_device}")
debug_print(f"目标容器: {target_vessel}")
debug_print(f"蒸发参数: {pressure} bar, {temp}°C, {final_time}s, {stir_speed} RPM")
debug_print("=" * 50)
debug_print("🎊" * 20)
debug_print(f"🎉 蒸发协议生成完成! ✨")
debug_print(f"📊 总动作数: {len(action_sequence)} 个 📝")
debug_print(f"🌪️ 旋转蒸发仪: {rotavap_device} 🔧")
debug_print(f"🥽 目标容器: {target_vessel} 🧪")
debug_print(f"⚙️ 蒸发参数: {pressure} bar 💨, {temp}°C 🌡️, {final_time}s, {stir_speed} RPM 🌪️")
debug_print(f"⏱️ 预计总时间: {(final_time + 20)/60:.1f} 分钟 ⌛")
debug_print("🎊" * 20)
return action_sequence
# === 便捷函数 ===
def generate_quick_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""快速蒸发:低温短时间"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.2,
temp=40.0,
time="15 min", # 🔧 使用带单位的时间
stir_speed=80.0,
**kwargs
)
def generate_gentle_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""温和蒸发:中等条件"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.1,
temp=50.0,
time="45 min", # 🔧 使用带单位的时间
stir_speed=60.0,
**kwargs
)
def generate_high_vacuum_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""高真空蒸发:低温高真空"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.01,
temp=35.0,
time="1 h", # 🔧 使用带单位的时间
stir_speed=120.0,
**kwargs
)
def generate_standard_evaporate_protocol(
G: nx.DiGraph,
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""标准蒸发:常用参数"""
return generate_evaporate_protocol(
G, vessel,
pressure=0.1,
temp=60.0,
time="3 min", # 🔧 使用带单位的时间
stir_speed=100.0,
**kwargs
)
# 测试函数
def test_time_parsing():
"""测试时间解析功能"""
print("=== EVAPORATE 时间解析测试 ===")
test_times = ["3 min", "180", "0.5 h", "2 hours", "?", "unknown", "1.5", "30 s"]
for time_str in test_times:
result = parse_time_input(time_str)
print(f"时间解析: '{time_str}'{result}s ({result/60:.1f}分钟)")
print("✅ 测试完成")
if __name__ == "__main__":
test_time_parsing()

View File

@@ -7,12 +7,12 @@ logger = logging.getLogger(__name__)
def debug_print(message):
"""调试输出"""
print(f"[FILTER] {message}", flush=True)
print(f"🧪 [FILTER] {message}", flush=True)
logger.info(f"[FILTER] {message}")
def find_filter_device(G: nx.DiGraph) -> str:
"""查找过滤器设备"""
debug_print("查找过滤器设备...")
debug_print("🔍 查找过滤器设备... 🌊")
# 查找过滤器设备
for node in G.nodes():
@@ -20,27 +20,33 @@ def find_filter_device(G: nx.DiGraph) -> str:
node_class = node_data.get('class', '') or ''
if 'filter' in node_class.lower() or 'filter' in node.lower():
debug_print(f"找到过滤器设备: {node}")
debug_print(f"🎉 找到过滤器设备: {node}")
return node
# 如果没找到,寻找可能的过滤器名称
debug_print("🔎 在预定义名称中搜索过滤器... 📋")
possible_names = ["filter", "filter_1", "virtual_filter", "filtration_unit"]
for name in possible_names:
if name in G.nodes():
debug_print(f"找到过滤器设备: {name}")
debug_print(f"🎉 找到过滤器设备: {name}")
return name
debug_print("😭 未找到过滤器设备 💔")
raise ValueError("未找到过滤器设备")
def validate_vessel(G: nx.DiGraph, vessel: str, vessel_type: str = "容器") -> None:
"""验证容器是否存在"""
debug_print(f"🔍 验证{vessel_type}: '{vessel}' 🧪")
if not vessel:
debug_print(f"{vessel_type}不能为空! 😱")
raise ValueError(f"{vessel_type}不能为空")
if vessel not in G.nodes():
debug_print(f"{vessel_type} '{vessel}' 不存在于系统中! 😞")
raise ValueError(f"{vessel_type} '{vessel}' 不存在于系统中")
debug_print(f"{vessel_type} '{vessel}' 验证通过")
debug_print(f"{vessel_type} '{vessel}' 验证通过 🎯")
def generate_filter_protocol(
G: nx.DiGraph,
@@ -61,47 +67,53 @@ def generate_filter_protocol(
List[Dict[str, Any]]: 过滤操作的动作序列
"""
debug_print("=" * 60)
debug_print("开始生成过滤协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - filtrate_vessel: {filtrate_vessel}")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 60)
debug_print("🌊" * 20)
debug_print("🚀 开始生成过滤协议")
debug_print(f"📝 输入参数:")
debug_print(f" 🥽 vessel: {vessel}")
debug_print(f" 🧪 filtrate_vessel: {filtrate_vessel}")
debug_print(f" ⚙️ 其他参数: {kwargs}")
debug_print("🌊" * 20)
action_sequence = []
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
debug_print("📍 步骤1: 参数验证... 🔧")
# 验证必需参数
debug_print(" 🔍 验证必需参数...")
validate_vessel(G, vessel, "过滤容器")
debug_print(" ✅ 必需参数验证完成 🎯")
# 验证可选参数
debug_print(" 🔍 验证可选参数...")
if filtrate_vessel:
validate_vessel(G, filtrate_vessel, "滤液容器")
debug_print("模式: 过滤并收集滤液")
debug_print(" 🌊 模式: 过滤并收集滤液 💧")
else:
debug_print("模式: 过滤并收集固体")
debug_print(" 🧱 模式: 过滤并收集固体 🔬")
debug_print(" ✅ 可选参数验证完成 🎯")
# === 查找设备 ===
debug_print("步骤2: 查找设备...")
debug_print("📍 步骤2: 查找设备... 🔍")
try:
debug_print(" 🔎 搜索过滤器设备...")
filter_device = find_filter_device(G)
debug_print(f"使用过滤器设备: {filter_device}")
debug_print(f" 🎉 使用过滤器设备: {filter_device} 🌊✨")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
debug_print(f" ❌ 设备查找失败: {str(e)} 😭")
raise ValueError(f"设备查找失败: {str(e)}")
# === 转移到过滤器(如果需要)===
debug_print("步骤3: 转移到过滤器...")
debug_print("📍 步骤3: 转移到过滤器... 🚚")
if vessel != filter_device:
debug_print(f"需要转移: {vessel}{filter_device}")
debug_print(f" 🚛 需要转移: {vessel}{filter_device} 📦")
try:
debug_print(" 🔄 开始执行转移操作...")
# 使用pump protocol转移液体到过滤器
transfer_actions = generate_pump_protocol_with_rinsing(
G=G,
@@ -121,20 +133,21 @@ def generate_filter_protocol(
if transfer_actions:
action_sequence.extend(transfer_actions)
debug_print(f"✅ 添加了 {len(transfer_actions)} 个转移动作")
debug_print(f" ✅ 添加了 {len(transfer_actions)} 个转移动作 🚚✨")
else:
debug_print("⚠️ 转移协议返回空序列")
debug_print(" ⚠️ 转移协议返回空序列 🤔")
except Exception as e:
debug_print(f"❌ 转移失败: {str(e)}")
# 继续执行,可能是直接连接的过滤器
debug_print(f" ❌ 转移失败: {str(e)} 😞")
debug_print(" 🔄 继续执行,可能是直接连接的过滤器 🤞")
else:
debug_print("过滤容器就是过滤器,无需转移")
debug_print("过滤容器就是过滤器,无需转移 🎯")
# === 执行过滤操作 ===
debug_print("步骤4: 执行过滤操作...")
debug_print("📍 步骤4: 执行过滤操作... 🌊")
# 构建过滤动作参数
debug_print(" ⚙️ 构建过滤参数...")
filter_kwargs = {
"vessel": filter_device, # 过滤器设备
"filtrate_vessel": filtrate_vessel, # 滤液容器(可能为空)
@@ -145,7 +158,8 @@ def generate_filter_protocol(
"volume": kwargs.get("volume", 0.0) # 0表示过滤所有
}
debug_print(f"过滤参数: {filter_kwargs}")
debug_print(f" 📋 过滤参数: {filter_kwargs}")
debug_print(" 🌊 开始过滤操作...")
# 过滤动作
filter_action = {
@@ -154,20 +168,24 @@ def generate_filter_protocol(
"action_kwargs": filter_kwargs
}
action_sequence.append(filter_action)
debug_print(" ✅ 过滤动作已添加 🌊✨")
# 过滤后等待
debug_print(" ⏳ 添加过滤后等待...")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 10.0}
})
debug_print(" ✅ 过滤后等待动作已添加 ⏰✨")
# === 收集滤液(如果需要)===
debug_print("步骤5: 收集滤液...")
debug_print("📍 步骤5: 收集滤液... 💧")
if filtrate_vessel:
debug_print(f"收集滤液: {filter_device}{filtrate_vessel}")
debug_print(f" 🧪 收集滤液: {filter_device}{filtrate_vessel} 💧")
try:
debug_print(" 🔄 开始执行收集操作...")
# 使用pump protocol收集滤液
collect_actions = generate_pump_protocol_with_rinsing(
G=G,
@@ -187,29 +205,32 @@ def generate_filter_protocol(
if collect_actions:
action_sequence.extend(collect_actions)
debug_print(f"✅ 添加了 {len(collect_actions)} 个收集动作")
debug_print(f" ✅ 添加了 {len(collect_actions)} 个收集动作 🧪✨")
else:
debug_print("⚠️ 收集协议返回空序列")
debug_print(" ⚠️ 收集协议返回空序列 🤔")
except Exception as e:
debug_print(f"❌ 收集滤液失败: {str(e)}")
# 继续执行,可能滤液直接流入指定容器
debug_print(f" ❌ 收集滤液失败: {str(e)} 😞")
debug_print(" 🔄 继续执行,可能滤液直接流入指定容器 🤞")
else:
debug_print("未指定滤液容器,固体保留在过滤器中")
debug_print(" 🧱 未指定滤液容器,固体保留在过滤器中 🔬")
# === 最终等待 ===
debug_print("📍 步骤6: 最终等待... ⏰")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {"time": 5.0}
})
debug_print(" ✅ 最终等待动作已添加 ⏰✨")
# === 总结 ===
debug_print("=" * 60)
debug_print(f"过滤协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"过滤容器: {vessel}")
debug_print(f"过滤器设备: {filter_device}")
debug_print(f"滤液容器: {filtrate_vessel or '无(保留固体)'}")
debug_print("=" * 60)
debug_print("🎊" * 20)
debug_print(f"🎉 过滤协议生成完成! ✨")
debug_print(f"📊 总动作数: {len(action_sequence)} 个 📝")
debug_print(f"🥽 过滤容器: {vessel} 🧪")
debug_print(f"🌊 过滤器设备: {filter_device} 🔧")
debug_print(f"💧 滤液容器: {filtrate_vessel or '无(保留固体)'} 🧱")
debug_print(f"⏱️ 预计总时间: {(len(action_sequence) * 5):.0f} 秒 ⌛")
debug_print("🎊" * 20)
return action_sequence

View File

@@ -7,179 +7,129 @@ logger = logging.getLogger(__name__)
def debug_print(message):
"""调试输出"""
print(f"[HEATCHILL] {message}", flush=True)
print(f"🌡️ [HEATCHILL] {message}", flush=True)
logger.info(f"[HEATCHILL] {message}")
def parse_time_with_units(time_input: Union[str, float, int], default_unit: str = "s") -> float:
def parse_time_input(time_input: Union[str, float, int]) -> float:
"""
解析带单位的时间输入
解析时间输入(统一函数)
Args:
time_input: 时间输入(如 "30 min", "1 h", "300", "?", 60.0
default_unit: 默认单位(默认为秒)
Returns:
float: 时间(秒)
"""
if not time_input:
return 0.0
return 300.0
# 处理数值输入
# 🔢 处理数值输入
if isinstance(time_input, (int, float)):
result = float(time_input)
debug_print(f"数值时间输入: {time_input}{result}s(默认单位)")
debug_print(f"数值时间: {time_input}{result}s")
return result
# 处理字符串输入
# 📝 处理字符串输入
time_str = str(time_input).lower().strip()
debug_print(f"解析时间字符串: '{time_str}'")
debug_print(f"🔍 解析时间: '{time_str}'")
# 处理特殊值
if time_str in ['?', 'unknown', 'tbd', 'to be determined']:
default_time = 300.0 # 5分钟默认值
debug_print(f"检测到未知时间,使用默认值: {default_time}s")
return default_time
# 特殊值处理
special_times = {
'?': 300.0, 'unknown': 300.0, 'tbd': 300.0,
'overnight': 43200.0, 'several hours': 10800.0,
'few hours': 7200.0, 'long time': 3600.0, 'short time': 300.0
}
# 如果是纯数字,使用默认单位
if time_str in special_times:
result = special_times[time_str]
debug_print(f"🎯 特殊时间: '{time_str}'{result}s ({result/60:.1f}分钟)")
return result
# 🔢 纯数字处理
try:
value = float(time_str)
if default_unit == "s":
result = value
elif default_unit in ["min", "minute"]:
result = value * 60.0
elif default_unit in ["h", "hour"]:
result = value * 3600.0
else:
result = value # 默认秒
debug_print(f"纯数字输入: {time_str}{result}s单位: {default_unit}")
result = float(time_str)
debug_print(f"⏰ 纯数字: {time_str}{result}s")
return result
except ValueError:
pass
# 使用正则表达式匹配数字和单位
# 📐 正则表达式解析
pattern = r'(\d+\.?\d*)\s*([a-z]*)'
match = re.match(pattern, time_str)
if not match:
debug_print(f"⚠️ 无法解析时间: '{time_str}',使用默认值: 60s")
return 60.0
debug_print(f"⚠️ 无法解析时间: '{time_str}',使用默认值: 300s")
return 300.0
value = float(match.group(1))
unit = match.group(2) or default_unit
unit = match.group(2) or 's'
# 单位转换映射
# 📏 单位转换
unit_multipliers = {
# 秒
's': 1.0,
'sec': 1.0,
'second': 1.0,
'seconds': 1.0,
# 分钟
'm': 60.0,
'min': 60.0,
'mins': 60.0,
'minute': 60.0,
'minutes': 60.0,
# 小时
'h': 3600.0,
'hr': 3600.0,
'hrs': 3600.0,
'hour': 3600.0,
'hours': 3600.0,
# 天
'd': 86400.0,
'day': 86400.0,
'days': 86400.0,
's': 1.0, 'sec': 1.0, 'second': 1.0, 'seconds': 1.0,
'm': 60.0, 'min': 60.0, 'mins': 60.0, 'minute': 60.0, 'minutes': 60.0,
'h': 3600.0, 'hr': 3600.0, 'hrs': 3600.0, 'hour': 3600.0, 'hours': 3600.0,
'd': 86400.0, 'day': 86400.0, 'days': 86400.0
}
multiplier = unit_multipliers.get(unit, 1.0)
result = value * multiplier
debug_print(f"时间解析: '{time_str}'{value} {unit}{result}s")
debug_print(f"时间解析: '{time_str}'{value} {unit}{result}s ({result/60:.1f}分钟)")
return result
def parse_temp_spec(temp_spec: str) -> float:
"""解析温度规格为具体温度"""
if not temp_spec:
return 25.0
def parse_temp_input(temp_input: Union[str, float], default_temp: float = 25.0) -> float:
"""
解析温度输入(统一函数)
temp_spec = temp_spec.strip().lower()
Args:
temp_input: 温度输入
default_temp: 默认温度
Returns:
float: 温度°C
"""
if not temp_input:
return default_temp
# 特殊温度规格
# 🔢 数值输入
if isinstance(temp_input, (int, float)):
result = float(temp_input)
debug_print(f"🌡️ 数值温度: {temp_input}{result}°C")
return result
# 📝 字符串输入
temp_str = str(temp_input).lower().strip()
debug_print(f"🔍 解析温度: '{temp_str}'")
# 🎯 特殊温度
special_temps = {
"room temperature": 25.0, # 室温
"reflux": 78.0, # 默认回流温度
"ice bath": 0.0, # 冰浴
"boiling": 100.0, # 沸腾
"hot": 60.0, # 热
"warm": 40.0, # 温热
"cold": 10.0, # 冷
"room temperature": 25.0, "reflux": 78.0, "ice bath": 0.0,
"boiling": 100.0, "hot": 60.0, "warm": 40.0, "cold": 10.0
}
if temp_spec in special_temps:
return special_temps[temp_spec]
if temp_str in special_temps:
result = special_temps[temp_str]
debug_print(f"🎯 特殊温度: '{temp_str}'{result}°C")
return result
# 解析带单位的温度(如 "256 °C"
# 📐 正则解析(如 "256 °C"
temp_pattern = r'(\d+(?:\.\d+)?)\s*°?[cf]?'
match = re.search(temp_pattern, temp_spec)
match = re.search(temp_pattern, temp_str)
if match:
return float(match.group(1))
result = float(match.group(1))
debug_print(f"✅ 温度解析: '{temp_str}'{result}°C")
return result
return 25.0
def parse_time_spec(time_spec: str) -> float:
"""解析时间规格为秒数"""
if not time_spec:
return 300.0
time_spec = time_spec.strip().lower()
# 特殊时间规格
special_times = {
"overnight": 43200.0, # 12小时
"several hours": 10800.0, # 3小时
"few hours": 7200.0, # 2小时
"long time": 3600.0, # 1小时
"short time": 300.0, # 5分钟
}
if time_spec in special_times:
return special_times[time_spec]
# 解析带单位的时间(如 "2 h"
time_pattern = r'(\d+(?:\.\d+)?)\s*([a-zA-Z]+)'
match = re.search(time_pattern, time_spec)
if match:
value = float(match.group(1))
unit = match.group(2).lower()
unit_multipliers = {
's': 1.0,
'sec': 1.0,
'min': 60.0,
'minute': 60.0,
'minutes': 60.0,
'h': 3600.0,
'hr': 3600.0,
'hour': 3600.0,
'hours': 3600.0,
}
multiplier = unit_multipliers.get(unit, 3600.0)
return value * multiplier
return 300.0
debug_print(f"⚠️ 无法解析温度: '{temp_str}',使用默认值: {default_temp}°C")
return default_temp
def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
"""查找与指定容器相连的加热/冷却设备"""
debug_print(f"查找加热设备,目标容器: {vessel}")
debug_print(f"🔍 查找加热设备,目标容器: {vessel}")
# 查找所有加热/冷却设备节点
# 🔧 查找所有加热设备
heatchill_nodes = []
for node in G.nodes():
node_data = G.nodes[node]
@@ -187,28 +137,55 @@ def find_connected_heatchill(G: nx.DiGraph, vessel: str) -> str:
if 'heatchill' in node_class.lower() or 'virtual_heatchill' in node_class:
heatchill_nodes.append(node)
debug_print(f"找到加热设备: {node}")
debug_print(f"🎉 找到加热设备: {node}")
if vessel:
# 检查哪个加热设备与目标容器相连
# 🔗 检查连接
if vessel and heatchill_nodes:
for heatchill in heatchill_nodes:
if G.has_edge(heatchill, vessel) or G.has_edge(vessel, heatchill):
debug_print(f"加热设备 '{heatchill}' 与容器 '{vessel}' 相连")
debug_print(f"加热设备 '{heatchill}' 与容器 '{vessel}' 相连")
return heatchill
# 如果没有指定容器或没有直接连接,返回第一个可用的加热设备
# 🎯 使用第一个可用设备
if heatchill_nodes:
debug_print(f"使用第一个加热设备: {heatchill_nodes[0]}")
return heatchill_nodes[0]
selected = heatchill_nodes[0]
debug_print(f"🔧 使用第一个加热设备: {selected}")
return selected
debug_print("未找到加热设备,使用默认设备")
# 🆘 默认设备
debug_print("⚠️ 未找到加热设备,使用默认设备")
return "heatchill_1"
def validate_and_fix_params(temp: float, time: float, stir_speed: float) -> tuple:
"""验证和修正参数"""
# 🌡️ 温度范围验证
if temp < -50.0 or temp > 300.0:
debug_print(f"⚠️ 温度 {temp}°C 超出范围,修正为 25°C")
temp = 25.0
else:
debug_print(f"✅ 温度 {temp}°C 在正常范围内")
# ⏰ 时间验证
if time < 0:
debug_print(f"⚠️ 时间 {time}s 无效,修正为 300s")
time = 300.0
else:
debug_print(f"✅ 时间 {time}s ({time/60:.1f}分钟) 有效")
# 🌪️ 搅拌速度验证
if stir_speed < 0 or stir_speed > 1500.0:
debug_print(f"⚠️ 搅拌速度 {stir_speed} RPM 超出范围,修正为 300 RPM")
stir_speed = 300.0
else:
debug_print(f"✅ 搅拌速度 {stir_speed} RPM 在正常范围内")
return temp, time, stir_speed
def generate_heat_chill_protocol(
G: nx.DiGraph,
vessel: str,
temp: float = 25.0,
time: Union[str, float] = "300", # 🔧 修改:支持字符串时间
time: Union[str, float] = "300",
temp_spec: str = "",
time_spec: str = "",
pressure: str = "",
@@ -219,249 +196,111 @@ def generate_heat_chill_protocol(
**kwargs
) -> List[Dict[str, Any]]:
"""
生成加热/冷却操作的协议序列 - 支持单位
生成加热/冷却操作的协议序列
"""
debug_print("=" * 50)
debug_print("开始生成加热冷却协议(支持单位)")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - temp: {temp}°C")
debug_print(f" - time: {time} (类型: {type(time)})")
debug_print(f" - temp_spec: {temp_spec}")
debug_print(f" - time_spec: {time_spec}")
debug_print(f" - pressure: {pressure}")
debug_print(f" - reflux_solvent: {reflux_solvent}")
debug_print(f" - stir: {stir}")
debug_print(f" - stir_speed: {stir_speed} RPM")
debug_print(f" - purpose: {purpose}")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 50)
debug_print("🌡️" * 20)
debug_print("🚀 开始生成加热冷却协议")
debug_print(f"📝 输入参数:")
debug_print(f" 🥽 vessel: {vessel}")
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("🌡️" * 20)
action_sequence = []
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
# 验证必需参数
# 📋 参数验证
debug_print("📍 步骤1: 参数验证... 🔧")
if not vessel:
debug_print("❌ vessel 参数不能为空! 😱")
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中! 😞")
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
# === 🔧 新增:单位解析处理 ===
debug_print("步骤2: 单位解析处理...")
debug_print("✅ 基础参数验证通过 🎯")
# 温度解析:优先使用 temp_spec然后是 temp
final_temp = temp
if temp_spec:
final_temp = parse_temp_spec(temp_spec)
debug_print(f"温度解析: '{temp_spec}'{final_temp}°C")
# 🔄 参数解析
debug_print("📍 步骤2: 参数解析... ⚡")
# 时间解析:优先使用 time_spec,然后是 time
if time_spec:
final_time = parse_time_spec(time_spec) # 使用现有的time_spec解析
debug_print(f"时间解析: '{time_spec}'{final_time}s")
else:
final_time = parse_time_with_units(time, "s")
debug_print(f"时间解析: {time}{final_time}s ({final_time/60:.1f}分钟)")
#温度解析:优先使用 temp_spec
final_temp = parse_temp_input(temp_spec, temp) if temp_spec else temp
# 参数范围验证
if final_temp < -50.0 or final_temp > 300.0:
debug_print(f"温度 {final_temp}°C 超出范围,修正为 25°C")
final_temp = 25.0
# 时间解析:优先使用 time_spec
final_time = parse_time_input(time_spec) if time_spec else parse_time_input(time)
if final_time < 0:
debug_print(f"时间 {final_time}s 无效,修正为 300s")
final_time = 300.0
# 参数修正
final_temp, final_time, stir_speed = validate_and_fix_params(final_temp, final_time, stir_speed)
if stir_speed < 0 or stir_speed > 1500.0:
debug_print(f"搅拌速度 {stir_speed} RPM 超出范围,修正为 300 RPM")
stir_speed = 300.0
debug_print(f"✅ 单位解析和参数验证通过")
# === 查找加热设备 ===
debug_print("步骤3: 查找加热设备...")
debug_print(f"🎯 最终参数: temp={final_temp}°C, time={final_time}s, stir_speed={stir_speed} RPM")
# 🔍 查找设备
debug_print("📍 步骤3: 查找加热设备... 🔍")
try:
heatchill_id = find_connected_heatchill(G, vessel)
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
debug_print(f"🎉 使用加热设备: {heatchill_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
raise ValueError(f"无法找到加热设备: {str(e)}")
# === 执行加热操作 ===
debug_print("步骤4: 执行加热作...")
# 🚀 生成动作
debug_print("📍 步骤4: 生成加热作... 🔥")
# 🕐 模拟运行时间优化
debug_print(" ⏱️ 检查模拟运行时间限制...")
original_time = final_time
simulation_time_limit = 100.0 # 模拟运行时间限制100秒
if final_time > simulation_time_limit:
final_time = simulation_time_limit
debug_print(f" 🎮 模拟运行优化: {original_time}s → {final_time}s (限制为{simulation_time_limit}s) ⚡")
debug_print(f" 📊 时间缩短: {original_time/60:.1f}分钟 → {final_time/60:.1f}分钟 🚀")
else:
debug_print(f" ✅ 时间在限制内: {final_time}s ({final_time/60:.1f}分钟) 保持不变 🎯")
action_sequence = []
heatchill_action = {
"device_id": heatchill_id,
"action_name": "heat_chill",
"action_kwargs": {
"vessel": vessel,
"temp": float(final_temp), # 🔧 确保是浮点数
"time": float(final_time), # 🔧 确保是浮点数
"stir": bool(stir), # 🔧 确保是布尔值
"stir_speed": float(stir_speed), # 🔧 确保是浮点数
"purpose": str(purpose or f"加热到 {final_temp}°C") # 🔧 确保是字符串
"temp": float(final_temp),
"time": float(final_time),
"stir": bool(stir),
"stir_speed": float(stir_speed),
"purpose": str(purpose or f"加热到 {final_temp}°C") + (f" (模拟时间: {final_time}s)" if original_time != final_time else "")
}
}
action_sequence.append(heatchill_action)
debug_print("✅ 加热动作已添加 🔥✨")
# === 总结 ===
debug_print("=" * 50)
debug_print(f"加热冷却协议生成完成(支持单位)")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"加热容器: {vessel}")
debug_print(f"目标温度: {final_temp}°C")
debug_print(f"加热时间: {final_time}s ({final_time/60:.1f}分钟)")
if pressure:
debug_print(f"压力参数: {pressure} (已接收,不做特殊处理)")
if reflux_solvent:
debug_print(f"回流溶剂: {reflux_solvent} (已接收,不做特殊处理)")
debug_print("=" * 50)
# 显示时间调整信息
if original_time != final_time:
debug_print(f" 🎭 模拟优化说明: 原计划 {original_time/60:.1f}分钟,实际模拟 {final_time/60:.1f}分钟 ⚡")
# 🎊 总结
debug_print("🎊" * 20)
debug_print(f"🎉 加热冷却协议生成完成! ✨")
debug_print(f"📊 总动作数: {len(action_sequence)}")
debug_print(f"🥽 加热容器: {vessel}")
debug_print(f"🌡️ 目标温度: {final_temp}°C")
debug_print(f"⏰ 加热时间: {final_time}s ({final_time/60:.1f}分钟)")
debug_print("🎊" * 20)
return action_sequence
def generate_heat_chill_to_temp_protocol(
G: nx.DiGraph,
vessel: str,
temp: float = 25.0,
time: Union[str, float] = 300.0, # 🔧 也支持字符串
temp_spec: str = "",
time_spec: str = "",
pressure: str = "",
reflux_solvent: str = "",
stir: bool = False,
stir_speed: float = 300.0,
purpose: str = "",
**kwargs # 🔧 接受额外参数,增强兼容性
G: nx.DiGraph,
vessel: str,
temp: float = 25.0,
time: Union[str, float] = 100.0,
**kwargs
) -> List[Dict[str, Any]]:
"""
生成加热/冷却操作的协议序列
Args:
G: 设备图
vessel: 加热容器名称(必需)
temp: 目标温度 (°C)
time: 加热时间(支持字符串和数字)
temp_spec: 温度规格(如 'room temperature', 'reflux'
time_spec: 时间规格(如 'overnight', '2 h'
pressure: 压力规格(如 '1 mbar'),不做特殊处理
reflux_solvent: 回流溶剂名称,不做特殊处理
stir: 是否搅拌
stir_speed: 搅拌速度 (RPM)
purpose: 操作目的
**kwargs: 其他参数(兼容性)
Returns:
List[Dict[str, Any]]: 加热操作的动作序列
"""
debug_print("=" * 50)
debug_print("开始生成加热冷却协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - temp: {temp}°C")
debug_print(f" - time: {time} (类型: {type(time)})")
debug_print(f" - temp_spec: {temp_spec}")
debug_print(f" - time_spec: {time_spec}")
debug_print(f" - pressure: {pressure}")
debug_print(f" - reflux_solvent: {reflux_solvent}")
debug_print(f" - stir: {stir}")
debug_print(f" - stir_speed: {stir_speed} RPM")
debug_print(f" - purpose: {purpose}")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 50)
action_sequence = []
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
# 验证必需参数
if not vessel:
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
# 温度解析:优先使用 temp_spec然后是 temp
final_temp = temp
if temp_spec:
final_temp = parse_temp_spec(temp_spec)
debug_print(f"温度解析: '{temp_spec}'{final_temp}°C")
# 🔧 修复:时间解析,支持字符串输入
if time_spec:
final_time = parse_time_spec(time_spec)
debug_print(f"时间解析: '{time_spec}'{final_time}s ({final_time / 60:.1f}分钟)")
else:
final_time = parse_time_with_units(time, "s")
debug_print(f"时间解析: {time}{final_time}s ({final_time/60:.1f}分钟)")
# 参数范围验证
if final_temp < -50.0 or final_temp > 300.0:
debug_print(f"温度 {final_temp}°C 超出范围,修正为 25°C")
final_temp = 25.0
if final_time < 0:
debug_print(f"时间 {final_time}s 无效,修正为 300s")
final_time = 300.0
if stir_speed < 0 or stir_speed > 1500.0:
debug_print(f"搅拌速度 {stir_speed} RPM 超出范围,修正为 300 RPM")
stir_speed = 300.0
debug_print(f"✅ 参数验证通过")
# === 查找加热设备 ===
debug_print("步骤2: 查找加热设备...")
try:
heatchill_id = find_connected_heatchill(G, vessel)
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
raise ValueError(f"无法找到加热设备: {str(e)}")
# === 执行加热操作 ===
debug_print("步骤3: 执行加热操作...")
heatchill_action = {
"device_id": heatchill_id,
"action_name": "heat_chill",
"action_kwargs": {
"vessel": vessel,
"temp": float(final_temp), # 🔧 确保是浮点数
"time": float(final_time), # 🔧 确保是浮点数
"stir": bool(stir), # 🔧 确保是布尔值
"stir_speed": float(stir_speed), # 🔧 确保是浮点数
"purpose": str(purpose or f"加热到 {final_temp}°C") # 🔧 确保是字符串
}
}
action_sequence.append(heatchill_action)
# === 总结 ===
debug_print("=" * 50)
debug_print(f"加热冷却协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"加热容器: {vessel}")
debug_print(f"目标温度: {final_temp}°C")
debug_print(f"加热时间: {final_time}s ({final_time / 60:.1f}分钟)")
if pressure:
debug_print(f"压力参数: {pressure} (已接收,不做特殊处理)")
if reflux_solvent:
debug_print(f"回流溶剂: {reflux_solvent} (已接收,不做特殊处理)")
debug_print("=" * 50)
return action_sequence
"""生成加热到指定温度的协议(简化版)"""
debug_print(f"🌡️ 生成加热到温度协议: {vessel}{temp}°C")
return generate_heat_chill_protocol(G, vessel, temp, time, **kwargs)
def generate_heat_chill_start_protocol(
G: nx.DiGraph,
@@ -472,33 +311,19 @@ def generate_heat_chill_start_protocol(
) -> List[Dict[str, Any]]:
"""生成开始加热操作的协议序列"""
debug_print("=" * 50)
debug_print("开始生成启动加热协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - temp: {temp}°C")
debug_print(f" - purpose: {purpose}")
debug_print("=" * 50)
debug_print("🔥 开始生成启动加热协议 ✨")
debug_print(f"🥽 vessel: {vessel}, 🌡️ temp: {temp}°C")
action_sequence = []
# 基础验证
if not vessel or vessel not in G.nodes():
debug_print("❌ 容器验证失败!")
raise ValueError("vessel 参数无效")
# 验证参数
if not vessel:
raise ValueError("vessel 参数不能为空")
# 查找设备
heatchill_id = find_connected_heatchill(G, vessel)
if vessel not in G.nodes():
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
# 查找加热设备
try:
heatchill_id = find_connected_heatchill(G, vessel)
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
raise ValueError(f"无法找到加热设备: {str(e)}")
# 执行开始加热操作
start_action = {
# 生成动作
action_sequence = [{
"device_id": heatchill_id,
"action_name": "heat_chill_start",
"action_kwargs": {
@@ -506,11 +331,9 @@ def generate_heat_chill_start_protocol(
"temp": temp,
"purpose": purpose or f"开始加热到 {temp}°C"
}
}
}]
action_sequence.append(start_action)
debug_print(f"启动加热协议生成完成,动作数: {len(action_sequence)}")
debug_print(f"✅ 启动加热协议生成完成 🎯")
return action_sequence
def generate_heat_chill_stop_protocol(
@@ -520,48 +343,34 @@ def generate_heat_chill_stop_protocol(
) -> List[Dict[str, Any]]:
"""生成停止加热操作的协议序列"""
debug_print("=" * 50)
debug_print("开始生成停止加热协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print("=" * 50)
debug_print("🛑 开始生成停止加热协议 ✨")
debug_print(f"🥽 vessel: {vessel}")
action_sequence = []
# 基础验证
if not vessel or vessel not in G.nodes():
debug_print("❌ 容器验证失败!")
raise ValueError("vessel 参数无效")
# 验证参数
if not vessel:
raise ValueError("vessel 参数不能为空")
# 查找设备
heatchill_id = find_connected_heatchill(G, vessel)
if vessel not in G.nodes():
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
# 查找加热设备
try:
heatchill_id = find_connected_heatchill(G, vessel)
debug_print(f"设备配置: 加热设备 = {heatchill_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
raise ValueError(f"无法找到加热设备: {str(e)}")
# 执行停止加热操作
stop_action = {
# 生成动作
action_sequence = [{
"device_id": heatchill_id,
"action_name": "heat_chill_stop",
"action_kwargs": {
"vessel": vessel
}
}
}]
action_sequence.append(stop_action)
debug_print(f"停止加热协议生成完成,动作数: {len(action_sequence)}")
debug_print(f"✅ 停止加热协议生成完成 🎯")
return action_sequence
# 测试函数
def test_heatchill_protocol():
"""测试加热协议"""
debug_print("=== HEATCHILL PROTOCOL 测试 ===")
debug_print("✅ 测试完成")
debug_print("🧪 === HEATCHILL PROTOCOL 测试 ===")
debug_print("✅ 测试完成 🎉")
if __name__ == "__main__":
test_heatchill_protocol()

View File

@@ -255,11 +255,23 @@ def generate_hydrogenate_protocol(
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 120.0,
"time": 20.0,
"description": f"等待温度稳定到 {temperature}°C"
}
})
# 🕐 模拟运行时间优化
print("HYDROGENATE: 检查模拟运行时间限制...")
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}分钟")
else:
print(f"HYDROGENATE: 时间在限制内: {reaction_time}s ({reaction_time/60:.1f}分钟) 保持不变")
# 保持反应温度
action_sequence.append({
"device_id": heater_id,
@@ -268,19 +280,41 @@ def generate_hydrogenate_protocol(
"vessel": vessel,
"temp": temperature,
"time": reaction_time,
"purpose": f"氢化反应: 保持 {temperature}°C反应 {reaction_time/3600:.1f} 小时"
"purpose": f"氢化反应: 保持 {temperature}°C反应 {reaction_time/60:.1f}分钟" + (f" (模拟时间)" if original_reaction_time != reaction_time else "")
}
})
# 显示时间调整信息
if original_reaction_time != reaction_time:
print(f"HYDROGENATE: 模拟优化说明: 原计划 {original_reaction_time/3600:.2f}小时,实际模拟 {reaction_time/60:.1f}分钟")
else:
print(f"HYDROGENATE: 警告 - 未找到加热器,使用室温反应")
# 🕐 室温反应也需要时间优化
print("HYDROGENATE: 检查室温反应模拟时间限制...")
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}分钟")
else:
print(f"HYDROGENATE: 室温反应时间在限制内: {reaction_time}s 保持不变")
# 室温反应,只等待时间
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": reaction_time,
"description": f"室温氢化反应 {reaction_time/3600:.1f} 小时"
"description": f"室温氢化反应 {reaction_time/60:.1f}分钟" + (f" (模拟时间)" if original_reaction_time != reaction_time else "")
}
})
# 显示时间调整信息
if original_reaction_time != reaction_time:
print(f"HYDROGENATE: 室温反应优化说明: 原计划 {original_reaction_time/3600:.2f}小时,实际模拟 {reaction_time/60:.1f}分钟")
# 7. 停止加热
if heater_id:

View File

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

View File

@@ -3,6 +3,11 @@ from typing import List, Dict, Any
from .pump_protocol import generate_pump_protocol_with_rinsing
def debug_print(message):
"""调试输出"""
print(f"🔄 [RESET_HANDLING] {message}", flush=True)
def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
"""
查找溶剂容器,支持多种匹配模式
@@ -14,7 +19,7 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
Returns:
str: 溶剂容器ID
"""
print(f"RESET_HANDLING: 正在查找溶剂 '{solvent}' 的容器...")
debug_print(f"🔍 正在查找溶剂 '{solvent}' 的容器... 🧪")
# 构建可能的容器名称
possible_names = [
@@ -28,23 +33,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(" 🎯 步骤1: 精确名称匹配...")
for vessel_name in possible_names:
if vessel_name in G.nodes():
print(f"RESET_HANDLING: 通过名称匹配找到容器: {vessel_name}")
debug_print(f" 🎉 通过名称匹配找到容器: {vessel_name}")
return vessel_name
debug_print(" 😞 精确名称匹配失败,尝试模糊匹配... 🔍")
# 第二步:通过模糊匹配
debug_print(" 🔍 步骤2: 模糊名称匹配...")
for node_id in G.nodes():
if G.nodes[node_id].get('type') == 'container':
node_name = G.nodes[node_id].get('name', '').lower()
# 检查是否包含溶剂名称
if solvent.lower() in node_id.lower() or solvent.lower() in node_name:
print(f"RESET_HANDLING: 通过模糊匹配找到容器: {node_id}")
debug_print(f" 🎉 通过模糊匹配找到容器: {node_id}")
return node_id
debug_print(" 😞 模糊匹配失败,尝试液体类型匹配... 🧪")
# 第三步:通过液体类型匹配
debug_print(" 🧪 步骤3: 液体类型匹配...")
for node_id in G.nodes():
if G.nodes[node_id].get('type') == 'container':
vessel_data = G.nodes[node_id].get('data', {})
@@ -56,10 +68,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:
print(f"RESET_HANDLING: 通过液体类型匹配找到容器: {node_id}")
debug_print(f" 🎉 通过液体类型匹配找到容器: {node_id}")
return node_id
# 列出可用容器帮助调试
debug_print(" 📊 显示可用容器信息...")
available_containers = []
for node_id in G.nodes():
if G.nodes[node_id].get('type') == 'container':
@@ -75,13 +88,17 @@ def find_solvent_vessel(G: nx.DiGraph, solvent: str) -> str:
'reagent_name': vessel_data.get('reagent_name', '')
})
print(f"RESET_HANDLING: 可用容器列表:")
for container in available_containers:
print(f" - {container['id']}: {container['name']}")
print(f" 液体: {container['liquids']}")
print(f" 试剂: {container['reagent_name']}")
debug_print(f" 📋 可用容器列表 (共{len(available_containers)}个):")
for i, container in enumerate(available_containers[:5]): # 只显示前5个
debug_print(f" {i+1}. 🥽 {container['id']}: {container['name']}")
debug_print(f" 💧 液体: {container['liquids']}")
debug_print(f" 🧪 试剂: {container['reagent_name']}")
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器。尝试了: {possible_names}")
if len(available_containers) > 5:
debug_print(f" ... 还有 {len(available_containers)-5} 个容器 📦")
debug_print(f"❌ 找不到溶剂 '{solvent}' 对应的容器 😭")
raise ValueError(f"找不到溶剂 '{solvent}' 对应的容器。尝试了: {possible_names[:3]}...")
def generate_reset_handling_protocol(
@@ -104,35 +121,49 @@ def generate_reset_handling_protocol(
# 固定参数
target_vessel = "main_reactor" # 默认目标容器
volume = 100.0 # 默认体积 100 mL
print(f"RESET_HANDLING: 开始生成重置处理协议")
print(f" - 溶剂: {solvent}")
print(f" - 目标容器: {target_vessel}")
print(f" - 体积: {volume} mL")
volume = 50.0 # 默认体积 50 mL
debug_print("🔄" * 20)
debug_print("🚀 开始生成重置处理协议 ✨")
debug_print(f"📝 输入参数:")
debug_print(f" 🧪 溶剂: {solvent}")
debug_print(f" 🥽 目标容器: {target_vessel}")
debug_print(f" 💧 体积: {volume} mL")
debug_print(f" ⚙️ 其他参数: {kwargs}")
debug_print("🔄" * 20)
# 1. 验证目标容器存在
debug_print("📍 步骤1: 验证目标容器... 🔧")
if target_vessel not in G.nodes():
debug_print(f"❌ 目标容器 '{target_vessel}' 不存在于系统中! 😱")
raise ValueError(f"目标容器 '{target_vessel}' 不存在于系统中")
debug_print(f"✅ 目标容器 '{target_vessel}' 验证通过 🎯")
# 2. 查找溶剂容器
debug_print("📍 步骤2: 查找溶剂容器... 🔍")
try:
solvent_vessel = find_solvent_vessel(G, solvent)
print(f"RESET_HANDLING: 找到溶剂容器: {solvent_vessel}")
debug_print(f"🎉 找到溶剂容器: {solvent_vessel}")
except ValueError as e:
debug_print(f"❌ 溶剂容器查找失败: {str(e)} 😭")
raise ValueError(f"无法找到溶剂 '{solvent}': {str(e)}")
# 3. 验证路径存在
debug_print("📍 步骤3: 验证传输路径... 🛤️")
try:
path = nx.shortest_path(G, source=solvent_vessel, target=target_vessel)
print(f"RESET_HANDLING: 找到路径: {''.join(path)}")
debug_print(f"🛤️ 找到路径: {''.join(path)}")
except nx.NetworkXNoPath:
debug_print(f"❌ 路径不可达: {solvent_vessel}{target_vessel} 😞")
raise ValueError(f"从溶剂容器 '{solvent_vessel}' 到目标容器 '{target_vessel}' 没有可用路径")
# 4. 使用pump_protocol转移溶剂
print(f"RESET_HANDLING: 开始转移溶剂 {volume} mL")
debug_print("📍 步骤4: 转移溶剂... 🚰")
debug_print(f" 🚛 开始转移: {solvent_vessel}{target_vessel}")
debug_print(f" 💧 转移体积: {volume} mL")
try:
debug_print(" 🔄 生成泵送协议...")
pump_actions = generate_pump_protocol_with_rinsing(
G=G,
from_vessel=solvent_vessel,
@@ -150,21 +181,52 @@ def generate_reset_handling_protocol(
)
action_sequence.extend(pump_actions)
debug_print(f" ✅ 泵送协议已添加: {len(pump_actions)} 个动作 🚰✨")
except Exception as e:
debug_print(f" ❌ 泵送协议生成失败: {str(e)} 😭")
raise ValueError(f"生成泵协议时出错: {str(e)}")
# 5. 等待溶剂稳定
debug_print("📍 步骤5: 等待溶剂稳定... ⏳")
# 🕐 模拟运行时间优化
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 🚀")
else:
debug_print(f" ✅ 时间在限制内: {final_wait_time}s 保持不变 🎯")
action_sequence.append({
"action_name": "wait",
"action_kwargs": {
"time": 10.0,
"description": f"等待溶剂 {solvent} 稳定"
"time": final_wait_time,
"description": f"等待溶剂 {solvent} 稳定" + (f" (模拟时间)" if original_wait_time != final_wait_time else "")
}
})
debug_print(f" ✅ 稳定等待已添加: {final_wait_time}s ⏰✨")
print(f"RESET_HANDLING: 协议生成完成,共 {len(action_sequence)} 个动作")
print(f"RESET_HANDLING: 已添加 {volume} mL {solvent}{target_vessel}")
# 显示时间调整信息
if original_wait_time != final_wait_time:
debug_print(f" 🎭 模拟优化说明: 原计划 {original_wait_time}s实际模拟 {final_wait_time}s ⚡")
# 🎊 总结
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)
return action_sequence
@@ -172,8 +234,15 @@ def generate_reset_handling_protocol(
# 测试函数
def test_reset_handling_protocol():
"""测试重置处理协议"""
print("=== RESET HANDLING PROTOCOL 测试 ===")
print("测试完成")
debug_print("🧪 === RESET HANDLING PROTOCOL 测试 ===")
# 测试溶剂名称
debug_print("🧪 测试常用溶剂名称...")
test_solvents = ["methanol", "ethanol", "water", "acetone", "dmso"]
for solvent in test_solvents:
debug_print(f" 🔍 测试溶剂: {solvent}")
debug_print("✅ 测试完成 🎉")
if __name__ == "__main__":

View File

@@ -277,13 +277,13 @@ def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
# 查找是否有内置容器数据
config_data = G.nodes[vessel].get('config', {})
if 'volume' in config_data:
default_volume = config_data.get('volume', 100.0)
default_volume = config_data.get('volume', 50.0)
debug_print(f"使用设备默认容量: {default_volume}mL")
return default_volume
# 对于旋蒸等设备,使用默认值
if 'rotavap' in vessel.lower():
default_volume = 100.0
default_volume = 50.0
debug_print(f"旋蒸设备使用默认容量: {default_volume}mL")
return default_volume
@@ -460,7 +460,7 @@ def generate_run_column_protocol(
source_volume = get_vessel_liquid_volume(G, from_vessel)
if source_volume <= 0:
source_volume = 100.0 # 默认体积
source_volume = 50.0 # 默认体积
debug_print(f"⚠️ 无法获取源容器体积,使用默认值: {source_volume}mL")
else:
debug_print(f"✅ 源容器体积: {source_volume}mL")
@@ -607,62 +607,40 @@ def generate_run_column_protocol(
debug_print(f"⚠️ 直接转移失败: {str(e)}")
except Exception as e:
debug_print(f"柱层析流程执行失败: {str(e)}")
# 添加错误日志动作
debug_print(f"协议生成失败: {str(e)} 😭")
# 不添加不确定的动作直接让action_sequence保持为空列表
# action_sequence 已经在函数开始时初始化为 []
# 确保至少有一个有效的动作,如果完全失败就返回空列表
if not action_sequence:
debug_print("⚠️ 没有生成任何有效动作")
# 可以选择返回空列表或添加一个基本的等待动作
action_sequence.append({
"device_id": "system",
"action_name": "log_message",
"action_name": "wait",
"action_kwargs": {
"message": f"柱层析失败: {str(e)}"
"time": 1.0,
"description": "柱层析协议执行完成"
}
})
# === 最终结果 ===
debug_print("=" * 60)
debug_print(f" 柱层析协议生成完成")
debug_print(f"📊 总动作数: {len(action_sequence)}")
debug_print(f"📋 参数总结:")
debug_print(f" - 源容器: {from_vessel} ({source_volume}mL)")
debug_print(f" - 目标容器: {to_vessel}")
debug_print(f" - 柱子: {column}")
debug_print(f" - Rf值: {final_rf}")
debug_print(f" - 溶剂比例: {final_solvent1} {final_pct1:.1f}% : {final_solvent2} {final_pct2:.1f}%")
debug_print(f" - 洗脱体积: {solvent1_volume:.1f}mL + {solvent2_volume:.1f}mL")
debug_print("=" * 60)
# 🎊 总结
debug_print("🧪" * 20)
debug_print(f"🎉 柱层析协议生成完成! ✨")
debug_print(f"📊 总动作数: {len(action_sequence)}")
debug_print(f"🥽 路径: {from_vessel}{to_vessel}")
debug_print(f"🏛️ 柱子: {column}")
debug_print(f"🧪 溶剂: {final_solvent1}:{final_solvent2}")
debug_print("🧪" * 20)
return action_sequence
# === 便捷函数 ===
# 测试函数
def test_run_column_protocol():
"""测试柱层析协议"""
debug_print("🧪 === RUN COLUMN PROTOCOL 测试 === ✨")
debug_print("✅ 测试完成 🎉")
def generate_silica_gel_column_protocol(
G: nx.DiGraph,
from_vessel: str,
to_vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""硅胶柱层析协议便捷函数"""
return generate_run_column_protocol(
G, from_vessel, to_vessel,
column="silica_column",
solvent1="ethyl_acetate",
solvent2="hexane",
ratio="1:9", # 常见的EA:Hex比例
**kwargs
)
def generate_reverse_phase_column_protocol(
G: nx.DiGraph,
from_vessel: str,
to_vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""反相柱层析协议便捷函数"""
return generate_run_column_protocol(
G, from_vessel, to_vessel,
column="c18_column",
solvent1="methanol",
solvent2="water",
ratio="7:3", # 常见的MeOH:H2O比例
**kwargs
)
if __name__ == "__main__":
test_run_column_protocol()

View File

@@ -7,12 +7,12 @@ logger = logging.getLogger(__name__)
def debug_print(message):
"""调试输出"""
print(f"[STIR] {message}", flush=True)
print(f"🌪️ [STIR] {message}", flush=True)
logger.info(f"[STIR] {message}")
def parse_time_with_units(time_input: Union[str, float, int], default_unit: str = "s") -> float:
def parse_time_input(time_input: Union[str, float, int], default_unit: str = "s") -> float:
"""
解析带单位的时间输入
统一的时间解析函数(精简版)
Args:
time_input: 时间输入(如 "30 min", "1 h", "300", "?", 60.0
@@ -22,259 +22,68 @@ def parse_time_with_units(time_input: Union[str, float, int], default_unit: str
float: 时间(秒)
"""
if not time_input:
return 0.0
return 100.0 # 默认100秒
# 处理数值输入
# 🔢 处理数值输入
if isinstance(time_input, (int, float)):
result = float(time_input)
debug_print(f"数值时间输入: {time_input}{result}s(默认单位)")
debug_print(f"数值时间: {time_input}{result}s")
return result
# 处理字符串输入
# 📝 处理字符串输入
time_str = str(time_input).lower().strip()
debug_print(f"解析时间字符串: '{time_str}'")
debug_print(f"🔍 解析时间: '{time_str}'")
# 处理特殊值
if time_str in ['?', 'unknown', 'tbd', 'to be determined']:
default_time = 300.0 # 5分钟默认值
debug_print(f"检测到未知时间,使用默认值: {default_time}s")
return default_time
# 特殊值处理
special_times = {
'?': 300.0, 'unknown': 300.0, 'tbd': 300.0,
'briefly': 30.0, 'quickly': 60.0, 'slowly': 600.0,
'several minutes': 300.0, 'few minutes': 180.0, 'overnight': 3600.0
}
# 如果是纯数字,使用默认单位
if time_str in special_times:
result = special_times[time_str]
debug_print(f"🎯 特殊时间: '{time_str}'{result}s ({result/60:.1f}分钟)")
return result
# 🔢 纯数字处理
try:
value = float(time_str)
if default_unit == "s":
result = value
elif default_unit in ["min", "minute"]:
result = value * 60.0
elif default_unit in ["h", "hour"]:
result = value * 3600.0
else:
result = value # 默认秒
debug_print(f"纯数字输入: {time_str}{result}s单位: {default_unit}")
result = float(time_str)
debug_print(f"⏰ 纯数字: {time_str}{result}s")
return result
except ValueError:
pass
# 使用正则表达式匹配数字和单位
# 📐 正则表达式解析
pattern = r'(\d+\.?\d*)\s*([a-z]*)'
match = re.match(pattern, time_str)
if not match:
debug_print(f"⚠️ 无法解析时间: '{time_str}',使用默认值: 60s")
return 60.0
debug_print(f"⚠️ 无法解析时间: '{time_str}',使用默认值: 100s")
return 100.0
value = float(match.group(1))
unit = match.group(2) or default_unit
# 单位转换映射
# 📏 单位转换
unit_multipliers = {
# 秒
's': 1.0,
'sec': 1.0,
'second': 1.0,
'seconds': 1.0,
# 分钟
'm': 60.0,
'min': 60.0,
'mins': 60.0,
'minute': 60.0,
'minutes': 60.0,
# 小时
'h': 3600.0,
'hr': 3600.0,
'hrs': 3600.0,
'hour': 3600.0,
'hours': 3600.0,
# 天
'd': 86400.0,
'day': 86400.0,
'days': 86400.0,
's': 1.0, 'sec': 1.0, 'second': 1.0, 'seconds': 1.0,
'm': 60.0, 'min': 60.0, 'mins': 60.0, 'minute': 60.0, 'minutes': 60.0,
'h': 3600.0, 'hr': 3600.0, 'hrs': 3600.0, 'hour': 3600.0, 'hours': 3600.0,
'd': 86400.0, 'day': 86400.0, 'days': 86400.0
}
multiplier = unit_multipliers.get(unit, 1.0)
result = value * multiplier
debug_print(f"时间解析: '{time_str}'{value} {unit}{result}s")
debug_print(f"时间解析: '{time_str}'{value} {unit}{result}s ({result/60:.1f}分钟)")
return result
def parse_time_spec(time_spec: str) -> float:
"""
解析时间规格字符串为秒数
Args:
time_spec: 时间规格字符串(如 "several minutes", "overnight", "few hours"
Returns:
float: 时间(秒)
"""
if not time_spec:
return 0.0
time_spec = time_spec.lower().strip()
# 预定义的时间规格映射
time_spec_map = {
# 几分钟
"several minutes": 5.0 * 60, # 5分钟
"few minutes": 3.0 * 60, # 3分钟
"couple of minutes": 2.0 * 60, # 2分钟
"a few minutes": 3.0 * 60, # 3分钟
"some minutes": 5.0 * 60, # 5分钟
# 几小时
"several hours": 3.0 * 3600, # 3小时
"few hours": 2.0 * 3600, # 2小时
"couple of hours": 2.0 * 3600, # 2小时
"a few hours": 3.0 * 3600, # 3小时
"some hours": 4.0 * 3600, # 4小时
# 特殊时间
"overnight": 12.0 * 3600, # 12小时
"over night": 12.0 * 3600, # 12小时
"morning": 4.0 * 3600, # 4小时
"afternoon": 6.0 * 3600, # 6小时
"evening": 4.0 * 3600, # 4小时
# 短时间
"briefly": 30.0, # 30秒
"momentarily": 10.0, # 10秒
"quickly": 60.0, # 1分钟
"slowly": 10.0 * 60, # 10分钟
# 长时间
"extended": 6.0 * 3600, # 6小时
"prolonged": 8.0 * 3600, # 8小时
"extensively": 12.0 * 3600, # 12小时
}
# 直接匹配
if time_spec in time_spec_map:
result = time_spec_map[time_spec]
debug_print(f"时间规格解析: '{time_spec}'{result/60:.1f}分钟")
return result
# 模糊匹配
for spec, value in time_spec_map.items():
if spec in time_spec or time_spec in spec:
result = value
debug_print(f"时间规格模糊匹配: '{time_spec}''{spec}'{result/60:.1f}分钟")
return result
# 如果无法识别,返回默认值
default_time = 5.0 * 60 # 5分钟
debug_print(f"⚠️ 无法识别时间规格: '{time_spec}',使用默认值: {default_time/60:.1f}分钟")
return default_time
def parse_time_string(time_str: str) -> float:
"""
解析时间字符串为秒数,支持多种单位
Args:
time_str: 时间字符串(如 "0.5 h", "30 min", "120 s", "2.5"
Returns:
float: 时间(秒)
"""
if not time_str:
return 0.0
# 如果是纯数字,默认单位为秒
try:
return float(time_str)
except ValueError:
pass
# 清理字符串
time_str = time_str.lower().strip()
# 使用正则表达式匹配数字和单位
pattern = r'(\d+\.?\d*)\s*([a-z]*)'
match = re.match(pattern, time_str)
if not match:
debug_print(f"⚠️ 无法解析时间字符串: '{time_str}',使用默认值: 60秒")
return 60.0
value = float(match.group(1))
unit = match.group(2)
# 单位转换映射
unit_map = {
# 秒
's': 1.0,
'sec': 1.0,
'second': 1.0,
'seconds': 1.0,
# 分钟
'm': 60.0,
'min': 60.0,
'mins': 60.0,
'minute': 60.0,
'minutes': 60.0,
# 小时
'h': 3600.0,
'hr': 3600.0,
'hrs': 3600.0,
'hour': 3600.0,
'hours': 3600.0,
# 天
'd': 86400.0,
'day': 86400.0,
'days': 86400.0,
# 如果没有单位,默认为秒
'': 1.0,
}
multiplier = unit_map.get(unit, 1.0)
result = value * multiplier
debug_print(f"时间字符串解析: '{time_str}'{value} {unit or 'seconds'}{result}")
return result
def parse_time_input(time_input: Union[str, float, int], time_spec: str = "") -> float:
"""
统一的时间输入解析函数
Args:
time_input: 时间输入(可以是字符串、浮点数或整数)
time_spec: 时间规格字符串优先级高于time_input
Returns:
float: 时间(秒)
"""
# 优先处理 time_spec
if time_spec:
return parse_time_spec(time_spec)
# 处理 time_input
if isinstance(time_input, (int, float)):
# 数字默认单位为秒
result = float(time_input)
debug_print(f"数字时间输入: {time_input}{result}")
return result
if isinstance(time_input, str):
return parse_time_string(time_input)
# 默认值
debug_print(f"⚠️ 无法处理时间输入: {time_input},使用默认值: 60秒")
return 60.0
def find_connected_stirrer(G: nx.DiGraph, vessel: str = None) -> str:
"""
查找与指定容器相连的搅拌设备,或查找可用的搅拌设备
"""
debug_print(f"查找搅拌设备,目标容器: {vessel}")
"""查找与指定容器相连的搅拌设备"""
debug_print(f"🔍 查找搅拌设备,目标容器: {vessel} 🥽")
# 查找所有搅拌设备节点
# 🔧 查找所有搅拌设备
stirrer_nodes = []
for node in G.nodes():
node_data = G.nodes[node]
@@ -282,257 +91,215 @@ def find_connected_stirrer(G: nx.DiGraph, vessel: str = None) -> str:
if 'stirrer' in node_class.lower() or 'virtual_stirrer' in node_class:
stirrer_nodes.append(node)
debug_print(f"找到搅拌设备: {node}")
debug_print(f"🎉 找到搅拌设备: {node} 🌪️")
if vessel:
# 检查哪个搅拌设备与目标容器相连(机械连接)
# 🔗 检查连接
if vessel and stirrer_nodes:
for stirrer in stirrer_nodes:
if G.has_edge(stirrer, vessel) or G.has_edge(vessel, stirrer):
debug_print(f"搅拌设备 '{stirrer}' 与容器 '{vessel}' 相连")
debug_print(f"搅拌设备 '{stirrer}' 与容器 '{vessel}' 相连 🔗")
return stirrer
# 如果没有指定容器或没有直接连接,返回第一个可用的搅拌设备
# 🎯 使用第一个可用设备
if stirrer_nodes:
debug_print(f"使用第一个搅拌设备: {stirrer_nodes[0]}")
return stirrer_nodes[0]
selected = stirrer_nodes[0]
debug_print(f"🔧 使用第一个搅拌设备: {selected} 🌪️")
return selected
debug_print("未找到搅拌设备,使用默认设备")
return "stirrer_1" # 默认设备
# 🆘 默认设备
debug_print("⚠️ 未找到搅拌设备,使用默认设备 🌪️")
return "stirrer_1"
def validate_and_fix_params(stir_time: float, stir_speed: float, settling_time: float) -> tuple:
"""验证和修正参数"""
# ⏰ 搅拌时间验证
if stir_time < 0:
debug_print(f"⚠️ 搅拌时间 {stir_time}s 无效,修正为 100s 🕐")
stir_time = 100.0
elif stir_time > 100: # 限制为100s
debug_print(f"⚠️ 搅拌时间 {stir_time}s 过长,仿真运行时,修正为 100s 🕐")
stir_time = 100.0
else:
debug_print(f"✅ 搅拌时间 {stir_time}s ({stir_time/60:.1f}分钟) 有效 ⏰")
# 🌪️ 搅拌速度验证
if stir_speed < 10.0 or stir_speed > 1500.0:
debug_print(f"⚠️ 搅拌速度 {stir_speed} RPM 超出范围,修正为 300 RPM 🌪️")
stir_speed = 300.0
else:
debug_print(f"✅ 搅拌速度 {stir_speed} RPM 在正常范围内 🌪️")
# ⏱️ 沉降时间验证
if settling_time < 0 or settling_time > 600: # 限制为10分钟
debug_print(f"⚠️ 沉降时间 {settling_time}s 超出范围,修正为 60s ⏱️")
settling_time = 60.0
else:
debug_print(f"✅ 沉降时间 {settling_time}s 在正常范围内 ⏱️")
return stir_time, stir_speed, settling_time
def generate_stir_protocol(
G: nx.DiGraph,
vessel: str,
time: Union[str, float, int] = "300", # 🔧 修改:默认为字符串
stir_time: Union[str, float, int] = "0", # 🔧 修改:支持字符串
time: Union[str, float, int] = "300",
stir_time: Union[str, float, int] = "0",
time_spec: str = "",
event: str = "",
stir_speed: float = 200.0,
settling_time: Union[str, float] = "60", # 🔧 修改:支持字符串
stir_speed: float = 300.0,
settling_time: Union[str, float] = "60",
**kwargs
) -> List[Dict[str, Any]]:
"""
生成搅拌操作的协议序列 - 支持单位
Args:
G: 设备图
vessel: 搅拌容器名称(必需)
time: 搅拌时间(支持 "5 min", "300", "0.5 h" 等)
stir_time: 搅拌时间与time等效支持单位
time_spec: 时间规格(优先级最高)
event: 事件标识
stir_speed: 搅拌速度 (RPM)默认200 RPM
settling_time: 沉降时间支持单位默认60秒
**kwargs: 其他参数(兼容性)
Returns:
List[Dict[str, Any]]: 搅拌操作的动作序列
生成搅拌操作的协议序列(精简版)
"""
debug_print("=" * 50)
debug_print("开始生成搅拌协议(支持单位)")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - time: {time}")
debug_print(f" - stir_time: {stir_time}")
debug_print(f" - time_spec: {time_spec}")
debug_print(f" - event: {event}")
debug_print(f" - stir_speed: {stir_speed}")
debug_print(f" - settling_time: {settling_time}")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 50)
debug_print("🌪️" * 20)
debug_print("🚀 开始生成搅拌协议")
debug_print(f"📝 输入参数:")
debug_print(f" 🥽 vessel: {vessel}")
debug_print(f" time: {time}")
debug_print(f" 🕐 stir_time: {stir_time}")
debug_print(f" 🎯 time_spec: {time_spec}")
debug_print(f" 🌪️ stir_speed: {stir_speed} RPM")
debug_print(f" ⏱️ settling_time: {settling_time}")
debug_print("🌪️" * 20)
action_sequence = []
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
# 验证必需参数
# 📋 参数验证
debug_print("📍 步骤1: 参数验证... 🔧")
if not vessel:
debug_print("❌ vessel 参数不能为空! 😱")
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
debug_print(f"❌ 容器 '{vessel}' 不存在于系统中! 😞")
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
debug_print(f"✅ 参数验证通过")
debug_print("基础参数验证通过 🎯")
# === 🔧 新增:单位解析处理 ===
debug_print("步骤2: 单位解析处理...")
# 🔄 参数解析
debug_print("📍 步骤2: 参数解析...")
# 确定实际使用的时间值(stir_time优先
actual_time_input = stir_time if stir_time not in ["0", 0, 0.0] else time
# 解析时间time_spec > actual_time_input
# 确定实际时间优先级time_spec > stir_time > time
if time_spec:
parsed_time = parse_time_spec(time_spec) # 使用现有的time_spec解析
debug_print(f"使用time_spec: '{time_spec}'{parsed_time}s")
parsed_time = parse_time_input(time_spec)
debug_print(f"🎯 使用time_spec: '{time_spec}'{parsed_time}s")
elif stir_time not in ["0", 0, 0.0]:
parsed_time = parse_time_input(stir_time)
debug_print(f"🎯 使用stir_time: {stir_time}{parsed_time}s")
else:
parsed_time = parse_time_with_units(actual_time_input, "s")
debug_print(f"解析时间: {actual_time_input}{parsed_time}s")
parsed_time = parse_time_input(time)
debug_print(f"🎯 使用time: {time}{parsed_time}s")
# 解析沉降时间
parsed_settling_time = parse_time_with_units(settling_time, "s")
debug_print(f"解析沉降时间: {settling_time}{parsed_settling_time}s")
parsed_settling_time = parse_time_input(settling_time)
debug_print(f"时间解析结果:")
debug_print(f" - 原始输入: time={time}, stir_time={stir_time}")
debug_print(f" - 时间规格: {time_spec}")
debug_print(f" - 最终搅拌时间: {parsed_time}s ({parsed_time/60:.1f}分钟)")
debug_print(f" - 最终沉降时间: {parsed_settling_time}s ({parsed_settling_time/60:.1f}分钟)")
# 🕐 模拟运行时间优化
debug_print(" ⏱️ 检查模拟运行时间限制...")
original_stir_time = parsed_time
original_settling_time = parsed_settling_time
# 修正参数范围
if parsed_time < 0:
debug_print(f"搅拌时间 {parsed_time}s 无效,修正为 300s")
parsed_time = 300.0
elif parsed_time > 7200:
debug_print(f"搅拌时间 {parsed_time}s 过长,修正为 3600s")
parsed_time = 3600.0
# 搅拌时间限制为60秒
stir_time_limit = 60.0
if parsed_time > stir_time_limit:
parsed_time = stir_time_limit
debug_print(f" 🎮 搅拌时间优化: {original_stir_time}s → {parsed_time}s ⚡")
if stir_speed < 10.0:
debug_print(f"搅拌速度 {stir_speed} RPM 过低,修正为 100 RPM")
stir_speed = 100.0
elif stir_speed > 1500.0:
debug_print(f"搅拌速度 {stir_speed} RPM 过高,修正为 1000 RPM")
stir_speed = 1000.0
# 沉降时间限制为30秒
settling_time_limit = 30.0
if parsed_settling_time > settling_time_limit:
parsed_settling_time = settling_time_limit
debug_print(f" 🎮 沉降时间优化: {original_settling_time}s → {parsed_settling_time}s ⚡")
if parsed_settling_time < 0:
debug_print(f"沉降时间 {parsed_settling_time}s 无效,修正为 60s")
parsed_settling_time = 60.0
elif parsed_settling_time > 1800:
debug_print(f"沉降时间 {parsed_settling_time}s 过长,修正为 600s")
parsed_settling_time = 600.0
# 参数修正
parsed_time, stir_speed, parsed_settling_time = validate_and_fix_params(
parsed_time, stir_speed, parsed_settling_time
)
# === 查找搅拌设备 ===
debug_print("步骤3: 查找搅拌设备...")
debug_print(f"🎯 最终参数: time={parsed_time}s, speed={stir_speed}RPM, settling={parsed_settling_time}s")
# 🔍 查找设备
debug_print("📍 步骤3: 查找搅拌设备... 🔍")
try:
stirrer_id = find_connected_stirrer(G, vessel)
debug_print(f"设备配置: 搅拌设备 = {stirrer_id}")
debug_print(f"🎉 使用搅拌设备: {stirrer_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
raise ValueError(f"无法找到搅拌设备: {str(e)}")
# === 执行搅拌操作 ===
debug_print("步骤4: 执行搅拌作...")
# 构建搅拌动作参数
stir_kwargs = {
"vessel": vessel,
"time": str(time), # 保持原始字符串格式
"event": event,
"time_spec": time_spec,
"stir_time": parsed_time, # 解析后的时间(秒)
"stir_speed": stir_speed,
"settling_time": parsed_settling_time # 解析后的沉降时间(秒)
}
debug_print(f"搅拌参数: {stir_kwargs}")
# 🚀 生成动作
debug_print("📍 步骤4: 生成搅拌作... 🌪️")
action_sequence = []
stir_action = {
"device_id": stirrer_id,
"action_name": "stir",
"action_kwargs": stir_kwargs
"action_kwargs": {
"vessel": vessel,
"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) # 确保是数字
}
}
action_sequence.append(stir_action)
debug_print("✅ 搅拌动作已添加 🌪️✨")
# === 总结 ===
debug_print("=" * 50)
debug_print(f"搅拌协议生成完成(支持单位)")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"搅拌容器: {vessel}")
debug_print(f"搅拌参数: {stir_speed} RPM, {parsed_time}s, 沉降 {parsed_settling_time}s")
debug_print("=" * 50)
# 显示时间优化信息
if original_stir_time != parsed_time or original_settling_time != parsed_settling_time:
debug_print(f" 🎭 模拟优化说明:")
debug_print(f" 搅拌时间: {original_stir_time/60:.1f}分钟 → {parsed_time/60:.1f}分钟")
debug_print(f" 沉降时间: {original_settling_time/60:.1f}分钟 → {parsed_settling_time/60:.1f}分钟")
# 🎊 总结
debug_print("🎊" * 20)
debug_print(f"🎉 搅拌协议生成完成! ✨")
debug_print(f"📊 总动作数: {len(action_sequence)}")
debug_print(f"🥽 搅拌容器: {vessel}")
debug_print(f"🌪️ 搅拌参数: {stir_speed} RPM, {parsed_time}s, 沉降 {parsed_settling_time}s")
debug_print(f"⏱️ 预计总时间: {(parsed_time + parsed_settling_time)/60:.1f} 分钟 ⌛")
debug_print("🎊" * 20)
return action_sequence
def generate_start_stir_protocol(
G: nx.DiGraph,
vessel: str,
stir_speed: float = 200.0,
stir_speed: float = 300.0,
purpose: str = "",
**kwargs
) -> List[Dict[str, Any]]:
"""
生成开始搅拌操作的协议序列 - 持续搅拌
"""生成开始搅拌操作的协议序列"""
Args:
G: 设备图
vessel: 搅拌容器名称(必需)
stir_speed: 搅拌速度 (RPM)默认200 RPM
purpose: 搅拌目的(可选)
**kwargs: 其他参数(兼容性)
debug_print("🔄 开始生成启动搅拌协议 ✨")
debug_print(f"🥽 vessel: {vessel}, 🌪️ speed: {stir_speed} RPM")
Returns:
List[Dict[str, Any]]: 开始搅拌操作的动作序列
"""
# 基础验证
if not vessel or vessel not in G.nodes():
debug_print("❌ 容器验证失败!")
raise ValueError("vessel 参数无效")
debug_print("=" * 50)
debug_print("开始生成启动搅拌协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - stir_speed: {stir_speed} RPM")
debug_print(f" - purpose: {purpose}")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 50)
# 参数修正
if stir_speed < 10.0 or stir_speed > 1500.0:
debug_print(f"⚠️ 搅拌速度修正: {stir_speed} → 300 RPM 🌪️")
stir_speed = 300.0
action_sequence = []
# 查找设备
stirrer_id = find_connected_stirrer(G, vessel)
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
# 验证必需参数
if not vessel:
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
# 修正参数范围
if stir_speed < 10.0:
debug_print(f"搅拌速度 {stir_speed} RPM 过低,修正为 100 RPM")
stir_speed = 100.0
elif stir_speed > 1500.0:
debug_print(f"搅拌速度 {stir_speed} RPM 过高,修正为 1000 RPM")
stir_speed = 1000.0
debug_print(f"✅ 参数验证通过")
# === 查找搅拌设备 ===
debug_print("步骤2: 查找搅拌设备...")
try:
stirrer_id = find_connected_stirrer(G, vessel)
debug_print(f"设备配置: 搅拌设备 = {stirrer_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
raise ValueError(f"无法找到搅拌设备: {str(e)}")
# === 执行开始搅拌操作 ===
debug_print("步骤3: 执行开始搅拌操作...")
start_stir_action = {
# 生成动作
action_sequence = [{
"device_id": stirrer_id,
"action_name": "start_stir",
"action_kwargs": {
"vessel": vessel,
"stir_speed": stir_speed,
"purpose": purpose
"purpose": purpose or f"启动搅拌 {stir_speed} RPM"
}
}
action_sequence.append(start_stir_action)
# === 总结 ===
debug_print("=" * 50)
debug_print(f"启动搅拌协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"搅拌容器: {vessel}")
debug_print(f"搅拌速度: {stir_speed} RPM")
debug_print(f"搅拌目的: {purpose}")
debug_print("=" * 50)
}]
debug_print(f"✅ 启动搅拌协议生成完成 🎯")
return action_sequence
def generate_stop_stir_protocol(
@@ -540,68 +307,36 @@ def generate_stop_stir_protocol(
vessel: str,
**kwargs
) -> List[Dict[str, Any]]:
"""
生成停止搅拌操作的协议序列
"""生成停止搅拌操作的协议序列"""
Args:
G: 设备图
vessel: 搅拌容器名称(必需)
**kwargs: 其他参数(兼容性)
debug_print("🛑 开始生成停止搅拌协议 ✨")
debug_print(f"🥽 vessel: {vessel}")
Returns:
List[Dict[str, Any]]: 停止搅拌操作的动作序列
"""
# 基础验证
if not vessel or vessel not in G.nodes():
debug_print("❌ 容器验证失败!")
raise ValueError("vessel 参数无效")
debug_print("=" * 50)
debug_print("开始生成停止搅拌协议")
debug_print(f"输入参数:")
debug_print(f" - vessel: {vessel}")
debug_print(f" - 其他参数: {kwargs}")
debug_print("=" * 50)
# 查找设备
stirrer_id = find_connected_stirrer(G, vessel)
action_sequence = []
# === 参数验证 ===
debug_print("步骤1: 参数验证...")
# 验证必需参数
if not vessel:
raise ValueError("vessel 参数不能为空")
if vessel not in G.nodes():
raise ValueError(f"容器 '{vessel}' 不存在于系统中")
debug_print(f"✅ 参数验证通过")
# === 查找搅拌设备 ===
debug_print("步骤2: 查找搅拌设备...")
try:
stirrer_id = find_connected_stirrer(G, vessel)
debug_print(f"设备配置: 搅拌设备 = {stirrer_id}")
except Exception as e:
debug_print(f"❌ 设备查找失败: {str(e)}")
raise ValueError(f"无法找到搅拌设备: {str(e)}")
# === 执行停止搅拌操作 ===
debug_print("步骤3: 执行停止搅拌操作...")
stop_stir_action = {
# 生成动作
action_sequence = [{
"device_id": stirrer_id,
"action_name": "stop_stir",
"action_kwargs": {
"vessel": vessel
}
}
action_sequence.append(stop_stir_action)
# === 总结 ===
debug_print("=" * 50)
debug_print(f"停止搅拌协议生成完成")
debug_print(f"总动作数: {len(action_sequence)}")
debug_print(f"搅拌容器: {vessel}")
debug_print("=" * 50)
}]
debug_print(f"✅ 停止搅拌协议生成完成 🎯")
return action_sequence
# 测试函数
def test_stir_protocol():
"""测试搅拌协议"""
debug_print("🧪 === STIR PROTOCOL 测试 === ✨")
debug_print("✅ 测试完成 🎉")
if __name__ == "__main__":
test_stir_protocol()

File diff suppressed because it is too large Load Diff