mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-06 15:05:13 +00:00
优化了全protocol的运行时间,除了pumptransfer相关的还没
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
@@ -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:
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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__":
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user