补充了剩下的几个protocol

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

View File

@@ -273,7 +273,6 @@ def generate_pump_protocol(
if not pump_backbone:
debug_print("PUMP_TRANSFER: 没有泵骨架节点,可能是直接容器连接或只有电磁阀")
# 🔧 对于气体传输,这是正常的,直接返回空序列
return pump_action_sequence
if transfer_flowrate == 0:
@@ -319,10 +318,31 @@ def generate_pump_protocol(
volume_left = volume
debug_print(f"PUMP_TRANSFER: 需要 {repeats} 次转移,单次最大体积 {min_transfer_volume} mL")
# 🆕 只在开头打印总体概览
if repeats > 1:
debug_print(f"🔄 分批转移概览: 总体积 {volume:.2f}mL需要 {repeats} 次转移")
logger.info(f"🔄 分批转移概览: 总体积 {volume:.2f}mL需要 {repeats} 次转移")
# 🔧 创建一个自定义的wait动作用于在执行时打印日志
def create_progress_log_action(message: str) -> Dict[str, Any]:
"""创建一个特殊的等待动作,在执行时打印进度日志"""
return {
"action_name": "wait",
"action_kwargs": {
"time": 0.1, # 很短的等待时间
"progress_message": message # 自定义字段,用于进度日志
}
}
# 生成泵操作序列
for i in range(repeats):
current_volume = min(volume_left, min_transfer_volume)
# 🆕 在每次循环开始时添加进度日志
if repeats > 1:
start_message = f"🚀 准备开始第 {i+1}/{repeats} 次转移: {current_volume:.2f}mL ({from_vessel}{to_vessel}) 🚰"
pump_action_sequence.append(create_progress_log_action(start_message))
# 🔧 修复:安全地获取边数据
def get_safe_edge_data(node_a, node_b, key):
try:
@@ -426,6 +446,426 @@ def generate_pump_protocol(
])
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
# 🆕 在每次循环结束时添加完成日志
if repeats > 1:
remaining_volume = volume_left - current_volume
if remaining_volume > 0:
end_message = f"✅ 第 {i+1}/{repeats} 次转移完成! 剩余 {remaining_volume:.2f}mL 待转移 ⏳"
else:
end_message = f"🎉 第 {i+1}/{repeats} 次转移完成! 全部 {volume:.2f}mL 转移完毕 ✨"
pump_action_sequence.append(create_progress_log_action(end_message))
volume_left -= current_volume
return pump_action_sequence
def generate_pump_protocol_with_rinsing(
G: nx.DiGraph,
from_vessel: str,
to_vessel: str,
volume: float = 0.0,
amount: str = "",
time: float = 0.0, # 🔧 修复:统一使用 time
viscous: bool = False,
rinsing_solvent: str = "",
rinsing_volume: float = 0.0,
rinsing_repeats: int = 0,
solid: bool = False,
flowrate: float = 2.5,
transfer_flowrate: float = 0.5,
rate_spec: str = "",
event: str = "",
through: str = "",
**kwargs
) -> List[Dict[str, Any]]:
"""
原有的同步版本,添加防冲突机制
"""
# 添加执行锁,防止并发调用
import threading
if not hasattr(generate_pump_protocol_with_rinsing, '_lock'):
generate_pump_protocol_with_rinsing._lock = threading.Lock()
with generate_pump_protocol_with_rinsing._lock:
debug_print("=" * 60)
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (同步版本)")
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
debug_print(f" 🕐 时间戳: {time_module.time()}")
debug_print(f" 🔒 获得执行锁")
debug_print("=" * 60)
# 短暂延迟,避免快速重复调用
time_module.sleep(0.01)
debug_print("🔍 步骤1: 开始体积处理...")
# 1. 处理体积参数
final_volume = volume
debug_print(f"📋 初始设置: final_volume = {final_volume}")
# 🔧 修复如果volume为0ROS2传入的空值从容器读取实际体积
if volume == 0.0:
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
# 直接从源容器读取实际体积
actual_volume = get_vessel_liquid_volume(G, from_vessel)
debug_print(f"📖 从容器 '{from_vessel}' 读取到体积: {actual_volume}mL")
if actual_volume > 0:
final_volume = actual_volume
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
else:
final_volume = 10.0 # 如果读取失败,使用默认值
logger.warning(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
else:
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
# 处理 amount 参数
if amount and amount.strip():
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
parsed_volume = _parse_amount_to_volume(amount)
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
if parsed_volume > 0:
final_volume = parsed_volume
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
actual_volume = get_vessel_liquid_volume(G, from_vessel)
if actual_volume > 0:
final_volume = actual_volume
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
# 最终体积验证
debug_print(f"🔍 步骤2: 最终体积验证...")
if final_volume <= 0:
logger.error(f"❌ 体积无效: {final_volume}mL")
final_volume = 10.0
logger.warning(f"⚠️ 强制设置为默认值: {final_volume}mL")
debug_print(f"✅ 最终确定体积: {final_volume}mL")
# 2. 处理流速参数
debug_print(f"🔍 步骤3: 处理流速参数...")
debug_print(f" - 原始 flowrate: {flowrate}")
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
final_flowrate = flowrate if flowrate > 0 else 2.5
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
if flowrate <= 0:
logger.warning(f"⚠️ flowrate <= 0修正为: {final_flowrate}mL/s")
if transfer_flowrate <= 0:
logger.warning(f"⚠️ transfer_flowrate <= 0修正为: {final_transfer_flowrate}mL/s")
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
# 3. 根据时间计算流速
if time > 0 and final_volume > 0:
debug_print(f"🔍 步骤4: 根据时间计算流速...")
calculated_flowrate = final_volume / time
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
if flowrate <= 0 or flowrate == 2.5:
final_flowrate = min(calculated_flowrate, 10.0)
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
final_transfer_flowrate = min(calculated_flowrate, 5.0)
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
# 4. 根据速度规格调整
if rate_spec:
debug_print(f"🔍 步骤5: 根据速度规格调整...")
debug_print(f" - 速度规格: '{rate_spec}'")
if rate_spec == "dropwise":
final_flowrate = min(final_flowrate, 0.1)
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
debug_print(f" - dropwise模式流速调整为: {final_flowrate}mL/s")
elif rate_spec == "slowly":
final_flowrate = min(final_flowrate, 0.5)
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
debug_print(f" - slowly模式流速调整为: {final_flowrate}mL/s")
elif rate_spec == "quickly":
final_flowrate = max(final_flowrate, 5.0)
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
debug_print(f" - quickly模式流速调整为: {final_flowrate}mL/s")
try:
# 🆕 修复在这里调用带有循环日志的generate_pump_protocol_with_loop_logging函数
pump_action_sequence = generate_pump_protocol_with_loop_logging(
G, from_vessel, to_vessel, final_volume,
final_flowrate, final_transfer_flowrate
)
debug_print(f"🔓 释放执行锁")
return pump_action_sequence
except Exception as e:
logger.error(f"❌ 协议生成失败: {str(e)}")
return [
{
"device_id": "system",
"action_name": "log_message",
"action_kwargs": {
"message": f"❌ 协议生成失败: {str(e)}"
}
}
]
def generate_pump_protocol_with_loop_logging(
G: nx.DiGraph,
from_vessel: str,
to_vessel: str,
volume: float,
flowrate: float = 2.5,
transfer_flowrate: float = 0.5,
) -> List[Dict[str, Any]]:
"""
生成泵操作的动作序列 - 带循环日志版本
🔧 修复:正确处理包含电磁阀的路径,并在合适时机打印循环日志
"""
pump_action_sequence = []
nodes = G.nodes(data=True)
# 验证输入参数
if volume <= 0:
logger.error(f"无效的体积参数: {volume}mL")
return pump_action_sequence
if flowrate <= 0:
flowrate = 2.5
logger.warning(f"flowrate <= 0使用默认值 {flowrate}mL/s")
if transfer_flowrate <= 0:
transfer_flowrate = 0.5
logger.warning(f"transfer_flowrate <= 0使用默认值 {transfer_flowrate}mL/s")
# 验证容器存在
if from_vessel not in G.nodes():
logger.error(f"源容器 '{from_vessel}' 不存在")
return pump_action_sequence
if to_vessel not in G.nodes():
logger.error(f"目标容器 '{to_vessel}' 不存在")
return pump_action_sequence
try:
shortest_path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
debug_print(f"PUMP_TRANSFER: 路径 {from_vessel} -> {to_vessel}: {shortest_path}")
except nx.NetworkXNoPath:
logger.error(f"无法找到从 '{from_vessel}''{to_vessel}' 的路径")
return pump_action_sequence
# 🔧 关键修复:正确构建泵骨架,排除容器和电磁阀
pump_backbone = []
for node in shortest_path:
# 跳过起始和结束容器
if node == from_vessel or node == to_vessel:
continue
# 跳过电磁阀(电磁阀不参与泵操作)
node_data = G.nodes.get(node, {})
node_class = node_data.get("class", "") or ""
if ("solenoid" in node_class.lower() or "solenoid_valve" in node.lower()):
debug_print(f"PUMP_TRANSFER: 跳过电磁阀 {node}")
continue
# 只包含多通阀和泵
if ("multiway" in node_class.lower() or "valve" in node_class.lower() or "pump" in node_class.lower()):
pump_backbone.append(node)
debug_print(f"PUMP_TRANSFER: 过滤后的泵骨架: {pump_backbone}")
if not pump_backbone:
debug_print("PUMP_TRANSFER: 没有泵骨架节点,可能是直接容器连接或只有电磁阀")
return pump_action_sequence
if transfer_flowrate == 0:
transfer_flowrate = flowrate
try:
pumps_from_node, valve_from_node = build_pump_valve_maps(G, pump_backbone)
except Exception as e:
debug_print(f"PUMP_TRANSFER: 构建泵-阀门映射失败: {str(e)}")
return pump_action_sequence
if not pumps_from_node:
debug_print("PUMP_TRANSFER: 没有可用的泵映射")
return pump_action_sequence
# 🔧 修复:安全地获取最小转移体积
try:
min_transfer_volumes = []
for node in pump_backbone:
if node in pumps_from_node:
pump_node = pumps_from_node[node]
if pump_node in nodes:
pump_config = nodes[pump_node].get("config", {})
max_volume = pump_config.get("max_volume")
if max_volume is not None:
min_transfer_volumes.append(max_volume)
if min_transfer_volumes:
min_transfer_volume = min(min_transfer_volumes)
else:
min_transfer_volume = 25.0 # 默认值
debug_print(f"PUMP_TRANSFER: 无法获取泵的最大体积,使用默认值: {min_transfer_volume}mL")
except Exception as e:
debug_print(f"PUMP_TRANSFER: 获取最小转移体积失败: {str(e)}")
min_transfer_volume = 25.0 # 默认值
repeats = int(np.ceil(volume / min_transfer_volume))
if repeats > 1 and (from_vessel.startswith("pump") or to_vessel.startswith("pump")):
logger.error("Cannot transfer volume larger than min_transfer_volume between two pumps.")
return pump_action_sequence
volume_left = volume
debug_print(f"PUMP_TRANSFER: 需要 {repeats} 次转移,单次最大体积 {min_transfer_volume} mL")
# 🆕 只在开头打印总体概览
if repeats > 1:
debug_print(f"🔄 分批转移概览: 总体积 {volume:.2f}mL需要 {repeats} 次转移")
logger.info(f"🔄 分批转移概览: 总体积 {volume:.2f}mL需要 {repeats} 次转移")
# 🔧 创建一个自定义的wait动作用于在执行时打印日志
def create_progress_log_action(message: str) -> Dict[str, Any]:
"""创建一个特殊的等待动作,在执行时打印进度日志"""
return {
"action_name": "wait",
"action_kwargs": {
"time": 0.1, # 很短的等待时间
"progress_message": message # 自定义字段,用于进度日志
}
}
# 生成泵操作序列
for i in range(repeats):
current_volume = min(volume_left, min_transfer_volume)
# 🆕 在每次循环开始时添加进度日志
if repeats > 1:
start_message = f"🚀 准备开始第 {i+1}/{repeats} 次转移: {current_volume:.2f}mL ({from_vessel}{to_vessel}) 🚰"
pump_action_sequence.append(create_progress_log_action(start_message))
# 🔧 修复:安全地获取边数据
def get_safe_edge_data(node_a, node_b, key):
try:
edge_data = G.get_edge_data(node_a, node_b)
if edge_data and "port" in edge_data:
port_data = edge_data["port"]
if isinstance(port_data, dict) and key in port_data:
return port_data[key]
return "default"
except Exception as e:
debug_print(f"PUMP_TRANSFER: 获取边数据失败 {node_a}->{node_b}: {str(e)}")
return "default"
# 从源容器吸液
if not from_vessel.startswith("pump") and pump_backbone:
first_pump_node = pump_backbone[0]
if first_pump_node in valve_from_node and first_pump_node in pumps_from_node:
port_command = get_safe_edge_data(first_pump_node, from_vessel, first_pump_node)
pump_action_sequence.extend([
{
"device_id": valve_from_node[first_pump_node],
"action_name": "set_valve_position",
"action_kwargs": {
"command": port_command
}
},
{
"device_id": pumps_from_node[first_pump_node],
"action_name": "set_position",
"action_kwargs": {
"position": float(current_volume),
"max_velocity": transfer_flowrate
}
}
])
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
# 泵间转移
for nodeA, nodeB in zip(pump_backbone[:-1], pump_backbone[1:]):
if nodeA in valve_from_node and nodeB in valve_from_node and nodeA in pumps_from_node and nodeB in pumps_from_node:
port_a = get_safe_edge_data(nodeA, nodeB, nodeA)
port_b = get_safe_edge_data(nodeB, nodeA, nodeB)
pump_action_sequence.append([
{
"device_id": valve_from_node[nodeA],
"action_name": "set_valve_position",
"action_kwargs": {
"command": port_a
}
},
{
"device_id": valve_from_node[nodeB],
"action_name": "set_valve_position",
"action_kwargs": {
"command": port_b
}
}
])
pump_action_sequence.append([
{
"device_id": pumps_from_node[nodeA],
"action_name": "set_position",
"action_kwargs": {
"position": 0.0,
"max_velocity": transfer_flowrate
}
},
{
"device_id": pumps_from_node[nodeB],
"action_name": "set_position",
"action_kwargs": {
"position": float(current_volume),
"max_velocity": transfer_flowrate
}
}
])
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
# 排液到目标容器
if not to_vessel.startswith("pump") and pump_backbone:
last_pump_node = pump_backbone[-1]
if last_pump_node in valve_from_node and last_pump_node in pumps_from_node:
port_command = get_safe_edge_data(last_pump_node, to_vessel, last_pump_node)
pump_action_sequence.extend([
{
"device_id": valve_from_node[last_pump_node],
"action_name": "set_valve_position",
"action_kwargs": {
"command": port_command
}
},
{
"device_id": pumps_from_node[last_pump_node],
"action_name": "set_position",
"action_kwargs": {
"position": 0.0,
"max_velocity": flowrate
}
}
])
pump_action_sequence.append({"action_name": "wait", "action_kwargs": {"time": 3}})
# 🆕 在每次循环结束时添加完成日志
if repeats > 1:
remaining_volume = volume_left - current_volume
if remaining_volume > 0:
end_message = f"✅ 第 {i+1}/{repeats} 次转移完成! 剩余 {remaining_volume:.2f}mL 待转移 ⏳"
else:
end_message = f"🎉 第 {i+1}/{repeats} 次转移完成! 全部 {volume:.2f}mL 转移完毕 ✨"
pump_action_sequence.append(create_progress_log_action(end_message))
volume_left -= current_volume
return pump_action_sequence
@@ -891,58 +1331,386 @@ def generate_pump_protocol_with_rinsing(
final_flowrate = max(final_flowrate, 5.0)
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
debug_print(f" - quickly模式流速调整为: {final_flowrate}mL/s")
# # 5. 处理冲洗参数
# debug_print(f"🔍 步骤6: 处理冲洗参数...")
# final_rinsing_solvent = rinsing_solvent
# final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
# final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
# if rinsing_volume <= 0:
# logger.warning(f"⚠️ rinsing_volume <= 0修正为: {final_rinsing_volume}mL")
# if rinsing_repeats <= 0:
# logger.warning(f"⚠️ rinsing_repeats <= 0修正为: {final_rinsing_repeats}次")
# # 根据物理属性调整冲洗参数
# if viscous or solid:
# final_rinsing_repeats = max(final_rinsing_repeats, 3)
# final_rinsing_volume = max(final_rinsing_volume, 10.0)
# debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
# 参数总结
debug_print("📊 最终参数总结:")
debug_print(f" - 体积: {final_volume}mL")
debug_print(f" - 流速: {final_flowrate}mL/s")
debug_print(f" - 转移流速: {final_transfer_flowrate}mL/s")
# debug_print(f" - 冲洗溶剂: '{final_rinsing_solvent}'")
# debug_print(f" - 冲洗体积: {final_rinsing_volume}mL")
# debug_print(f" - 冲洗次数: {final_rinsing_repeats}次")
# ========== 执行基础转移 ==========
debug_print("🔧 步骤7: 开始执行基础转移...")
try:
debug_print(f" - 调用 generate_pump_protocol...")
debug_print(f" - 参数: G, '{from_vessel}', '{to_vessel}', {final_volume}, {final_flowrate}, {final_transfer_flowrate}")
# # 5. 处理冲洗参数
# debug_print(f"🔍 步骤6: 处理冲洗参数...")
# final_rinsing_solvent = rinsing_solvent
# final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
# final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
pump_action_sequence = generate_pump_protocol(
G, from_vessel, to_vessel, final_volume,
final_flowrate, final_transfer_flowrate
)
# if rinsing_volume <= 0:
# logger.warning(f"⚠️ rinsing_volume <= 0修正为: {final_rinsing_volume}mL")
# if rinsing_repeats <= 0:
# logger.warning(f"⚠️ rinsing_repeats <= 0修正为: {final_rinsing_repeats}次")
debug_print(f" - generate_pump_protocol 返回结果:")
debug_print(f" - 动作序列长度: {len(pump_action_sequence)}")
debug_print(f" - 动作序列是否为空: {len(pump_action_sequence) == 0}")
# # 根据物理属性调整冲洗参数
# if viscous or solid:
# final_rinsing_repeats = max(final_rinsing_repeats, 3)
# final_rinsing_volume = max(final_rinsing_volume, 10.0)
# debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
try:
pump_action_sequence = generate_pump_protocol(
G, from_vessel, to_vessel, final_volume,
flowrate, transfer_flowrate
)
if not pump_action_sequence:
debug_print("❌ 基础转移协议生成为空,可能是路径问题")
debug_print(f" - 源容器存在: {from_vessel in G.nodes()}")
debug_print(f" - 目标容器存在: {to_vessel in G.nodes()}")
# 为每个动作添加唯一标识
# for i, action in enumerate(pump_action_sequence):
# if isinstance(action, dict):
# action['_protocol_id'] = protocol_id
# action['_action_sequence'] = i
# elif isinstance(action, list):
# for j, sub_action in enumerate(action):
# if isinstance(sub_action, dict):
# sub_action['_protocol_id'] = protocol_id
# sub_action['_action_sequence'] = f"{i}_{j}"
#
# debug_print(f"📊 协议 {protocol_id} 生成完成,共 {len(pump_action_sequence)} 个动作")
debug_print(f"🔓 释放执行锁")
return pump_action_sequence
if from_vessel in G.nodes() and to_vessel in G.nodes():
try:
path = nx.shortest_path(G, source=from_vessel, target=to_vessel)
debug_print(f" - 路径存在: {path}")
except Exception as path_error:
debug_print(f" - 无法找到路径: {str(path_error)}")
except Exception as e:
logger.error(f"❌ 协议生成失败: {str(e)}")
return [
{
"device_id": "system",
"action_name": "log_message",
"action_kwargs": {
"message": f"❌ 协议生成失败: {str(e)}"
},
'_protocol_id': protocol_id,
'_action_sequence': 0
"message": f"⚠️ 路径问题,无法转移: {final_volume}mL 从 {from_vessel}{to_vessel}"
}
}
]
debug_print(f"✅ 基础转移生成了 {len(pump_action_sequence)} 个动作")
# 打印前几个动作用于调试
if len(pump_action_sequence) > 0:
debug_print("🔍 前几个动作预览:")
for i, action in enumerate(pump_action_sequence[:3]):
debug_print(f" 动作 {i+1}: {action}")
if len(pump_action_sequence) > 3:
debug_print(f" ... 还有 {len(pump_action_sequence) - 3} 个动作")
except Exception as e:
debug_print(f"❌ 基础转移失败: {str(e)}")
import traceback
debug_print(f"详细错误: {traceback.format_exc()}")
return [
{
"device_id": "system",
"action_name": "log_message",
"action_kwargs": {
"message": f"❌ 转移失败: {final_volume}mL 从 {from_vessel}{to_vessel}, 错误: {str(e)}"
}
}
]
# ========== 执行冲洗操作 ==========
# debug_print("🔧 步骤8: 检查冲洗操作...")
# if final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0:
# debug_print(f"🧽 开始冲洗操作,溶剂: '{final_rinsing_solvent}'")
# try:
# if final_rinsing_solvent.strip() != "air":
# debug_print(" - 执行液体冲洗...")
# rinsing_actions = _generate_rinsing_sequence(
# G, from_vessel, to_vessel, final_rinsing_solvent,
# final_rinsing_volume, final_rinsing_repeats,
# final_flowrate, final_transfer_flowrate
# )
# pump_action_sequence.extend(rinsing_actions)
# debug_print(f" - 添加了 {len(rinsing_actions)} 个冲洗动作")
# else:
# debug_print(" - 执行空气冲洗...")
# air_rinsing_actions = _generate_air_rinsing_sequence(
# G, from_vessel, to_vessel, final_rinsing_volume, final_rinsing_repeats,
# final_flowrate, final_transfer_flowrate
# )
# pump_action_sequence.extend(air_rinsing_actions)
# debug_print(f" - 添加了 {len(air_rinsing_actions)} 个空气冲洗动作")
# except Exception as e:
# debug_print(f"⚠️ 冲洗操作失败: {str(e)},跳过冲洗")
# else:
# debug_print(f"⏭️ 跳过冲洗操作")
# debug_print(f" - 溶剂: '{final_rinsing_solvent}'")
# debug_print(f" - 次数: {final_rinsing_repeats}")
# debug_print(f" - 条件满足: {bool(final_rinsing_solvent and final_rinsing_solvent.strip() and final_rinsing_repeats > 0)}")
# ========== 最终结果 ==========
debug_print("=" * 60)
debug_print(f"🎉 PUMP_TRANSFER: 协议生成完成")
debug_print(f" 📊 总动作数: {len(pump_action_sequence)}")
debug_print(f" 📋 最终体积: {final_volume}mL")
debug_print(f" 🚀 执行路径: {from_vessel} -> {to_vessel}")
# 最终验证
if len(pump_action_sequence) == 0:
debug_print("🚨 协议生成结果为空!这是异常情况")
return [
{
"device_id": "system",
"action_name": "log_message",
"action_kwargs": {
"message": f"🚨 协议生成失败: 无法生成任何动作序列"
}
}
]
debug_print("=" * 60)
return pump_action_sequence
async def generate_pump_protocol_with_rinsing_async(
G: nx.DiGraph,
from_vessel: str,
to_vessel: str,
volume: float = 0.0,
amount: str = "",
time: float = 0.0,
viscous: bool = False,
rinsing_solvent: str = "",
rinsing_volume: float = 0.0,
rinsing_repeats: int = 0,
solid: bool = False,
flowrate: float = 2.5,
transfer_flowrate: float = 0.5,
rate_spec: str = "",
event: str = "",
through: str = "",
**kwargs
) -> List[Dict[str, Any]]:
"""
异步版本的泵转移协议生成器,避免并发问题
"""
debug_print("=" * 60)
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (异步版本)")
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
debug_print(f" 🕐 时间戳: {time_module.time()}")
debug_print("=" * 60)
# 添加唯一标识符
protocol_id = f"pump_transfer_{int(time_module.time() * 1000000)}"
debug_print(f"📋 协议ID: {protocol_id}")
# 调用原有的同步版本
result = generate_pump_protocol_with_rinsing(
G, from_vessel, to_vessel, volume, amount, time, viscous,
rinsing_solvent, rinsing_volume, rinsing_repeats, solid,
flowrate, transfer_flowrate, rate_spec, event, through, **kwargs
)
# 为每个动作添加唯一标识
for i, action in enumerate(result):
if isinstance(action, dict):
action['_protocol_id'] = protocol_id
action['_action_sequence'] = i
action['_timestamp'] = time_module.time()
debug_print(f"📊 协议 {protocol_id} 生成完成,共 {len(result)} 个动作")
return result
# 保持原有的同步版本兼容性
def generate_pump_protocol_with_rinsing(
G: nx.DiGraph,
from_vessel: str,
to_vessel: str,
volume: float = 0.0,
amount: str = "",
time: float = 0.0,
viscous: bool = False,
rinsing_solvent: str = "",
rinsing_volume: float = 0.0,
rinsing_repeats: int = 0,
solid: bool = False,
flowrate: float = 2.5,
transfer_flowrate: float = 0.5,
rate_spec: str = "",
event: str = "",
through: str = "",
**kwargs
) -> List[Dict[str, Any]]:
"""
原有的同步版本,添加防冲突机制
"""
# 添加执行锁,防止并发调用
import threading
if not hasattr(generate_pump_protocol_with_rinsing, '_lock'):
generate_pump_protocol_with_rinsing._lock = threading.Lock()
with generate_pump_protocol_with_rinsing._lock:
debug_print("=" * 60)
debug_print(f"PUMP_TRANSFER: 🚀 开始生成协议 (同步版本)")
debug_print(f" 📍 路径: {from_vessel} -> {to_vessel}")
debug_print(f" 🕐 时间戳: {time_module.time()}")
debug_print(f" 🔒 获得执行锁")
debug_print("=" * 60)
# 短暂延迟,避免快速重复调用
time_module.sleep(0.01)
debug_print("🔍 步骤1: 开始体积处理...")
# 1. 处理体积参数
final_volume = volume
debug_print(f"📋 初始设置: final_volume = {final_volume}")
# 🔧 修复如果volume为0ROS2传入的空值从容器读取实际体积
if volume == 0.0:
debug_print("🎯 检测到 volume=0.0,开始自动体积检测...")
# 直接从源容器读取实际体积
actual_volume = get_vessel_liquid_volume(G, from_vessel)
debug_print(f"📖 从容器 '{from_vessel}' 读取到体积: {actual_volume}mL")
if actual_volume > 0:
final_volume = actual_volume
debug_print(f"✅ 成功设置体积为: {final_volume}mL")
else:
final_volume = 10.0 # 如果读取失败,使用默认值
logger.warning(f"⚠️ 无法从容器读取体积,使用默认值: {final_volume}mL")
else:
debug_print(f"📌 体积非零,直接使用: {final_volume}mL")
# 处理 amount 参数
if amount and amount.strip():
debug_print(f"🔍 检测到 amount 参数: '{amount}',开始解析...")
parsed_volume = _parse_amount_to_volume(amount)
debug_print(f"📖 从 amount 解析得到体积: {parsed_volume}mL")
if parsed_volume > 0:
final_volume = parsed_volume
debug_print(f"✅ 使用从 amount 解析的体积: {final_volume}mL")
elif parsed_volume == 0.0 and amount.lower().strip() == "all":
debug_print("🎯 检测到 amount='all',从容器读取全部体积...")
actual_volume = get_vessel_liquid_volume(G, from_vessel)
if actual_volume > 0:
final_volume = actual_volume
debug_print(f"✅ amount='all',设置体积为: {final_volume}mL")
# 最终体积验证
debug_print(f"🔍 步骤2: 最终体积验证...")
if final_volume <= 0:
logger.error(f"❌ 体积无效: {final_volume}mL")
final_volume = 10.0
logger.warning(f"⚠️ 强制设置为默认值: {final_volume}mL")
debug_print(f"✅ 最终确定体积: {final_volume}mL")
# 2. 处理流速参数
debug_print(f"🔍 步骤3: 处理流速参数...")
debug_print(f" - 原始 flowrate: {flowrate}")
debug_print(f" - 原始 transfer_flowrate: {transfer_flowrate}")
final_flowrate = flowrate if flowrate > 0 else 2.5
final_transfer_flowrate = transfer_flowrate if transfer_flowrate > 0 else 0.5
if flowrate <= 0:
logger.warning(f"⚠️ flowrate <= 0修正为: {final_flowrate}mL/s")
if transfer_flowrate <= 0:
logger.warning(f"⚠️ transfer_flowrate <= 0修正为: {final_transfer_flowrate}mL/s")
debug_print(f"✅ 修正后流速: flowrate={final_flowrate}mL/s, transfer_flowrate={final_transfer_flowrate}mL/s")
# 3. 根据时间计算流速
if time > 0 and final_volume > 0:
debug_print(f"🔍 步骤4: 根据时间计算流速...")
calculated_flowrate = final_volume / time
debug_print(f" - 计算得到流速: {calculated_flowrate}mL/s")
if flowrate <= 0 or flowrate == 2.5:
final_flowrate = min(calculated_flowrate, 10.0)
debug_print(f" - 调整 flowrate 为: {final_flowrate}mL/s")
if transfer_flowrate <= 0 or transfer_flowrate == 0.5:
final_transfer_flowrate = min(calculated_flowrate, 5.0)
debug_print(f" - 调整 transfer_flowrate 为: {final_transfer_flowrate}mL/s")
# 4. 根据速度规格调整
if rate_spec:
debug_print(f"🔍 步骤5: 根据速度规格调整...")
debug_print(f" - 速度规格: '{rate_spec}'")
if rate_spec == "dropwise":
final_flowrate = min(final_flowrate, 0.1)
final_transfer_flowrate = min(final_transfer_flowrate, 0.1)
debug_print(f" - dropwise模式流速调整为: {final_flowrate}mL/s")
elif rate_spec == "slowly":
final_flowrate = min(final_flowrate, 0.5)
final_transfer_flowrate = min(final_transfer_flowrate, 0.3)
debug_print(f" - slowly模式流速调整为: {final_flowrate}mL/s")
elif rate_spec == "quickly":
final_flowrate = max(final_flowrate, 5.0)
final_transfer_flowrate = max(final_transfer_flowrate, 2.0)
debug_print(f" - quickly模式流速调整为: {final_flowrate}mL/s")
# # 5. 处理冲洗参数
# debug_print(f"🔍 步骤6: 处理冲洗参数...")
# final_rinsing_solvent = rinsing_solvent
# final_rinsing_volume = rinsing_volume if rinsing_volume > 0 else 5.0
# final_rinsing_repeats = rinsing_repeats if rinsing_repeats > 0 else 2
# if rinsing_volume <= 0:
# logger.warning(f"⚠️ rinsing_volume <= 0修正为: {final_rinsing_volume}mL")
# if rinsing_repeats <= 0:
# logger.warning(f"⚠️ rinsing_repeats <= 0修正为: {final_rinsing_repeats}次")
# # 根据物理属性调整冲洗参数
# if viscous or solid:
# final_rinsing_repeats = max(final_rinsing_repeats, 3)
# final_rinsing_volume = max(final_rinsing_volume, 10.0)
# debug_print(f"🧪 粘稠/固体物质,调整冲洗参数:{final_rinsing_repeats}次,{final_rinsing_volume}mL")
try:
pump_action_sequence = generate_pump_protocol(
G, from_vessel, to_vessel, final_volume,
flowrate, transfer_flowrate
)
# 为每个动作添加唯一标识
# for i, action in enumerate(pump_action_sequence):
# if isinstance(action, dict):
# action['_protocol_id'] = protocol_id
# action['_action_sequence'] = i
# elif isinstance(action, list):
# for j, sub_action in enumerate(action):
# if isinstance(sub_action, dict):
# sub_action['_protocol_id'] = protocol_id
# sub_action['_action_sequence'] = f"{i}_{j}"
#
# debug_print(f"📊 协议 {protocol_id} 生成完成,共 {len(pump_action_sequence)} 个动作")
debug_print(f"🔓 释放执行锁")
return pump_action_sequence
except Exception as e:
logger.error(f"❌ 协议生成失败: {str(e)}")
return [
{
"device_id": "system",
"action_name": "log_message",
"action_kwargs": {
"message": f"❌ 协议生成失败: {str(e)}"
}
}
]
def _parse_amount_to_volume(amount: str) -> float:
"""解析 amount 字符串为体积"""