mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-14 21:24:40 +00:00
* Cleanup registry to be easy-understanding (#76) * delete deprecated mock devices * rename categories * combine chromatographic devices * rename rviz simulation nodes * organic virtual devices * parse vessel_id * run registry completion before merge --------- Co-authored-by: Xuwznln <18435084+Xuwznln@users.noreply.github.com> * fix: workstation handlers and vessel_id parsing * fix: working dir error when input config path feat: report publish topic when error * modify default discovery_interval to 15s * feat: add trace log level * feat: 添加ChinWe设备控制类,支持串口通信和电机控制功能 (#79) * fix: drop_tips not using auto resource select * fix: discard_tips error * fix: discard_tips * fix: prcxi_res * add: prcxi res fix: startup slow * feat: workstation example * fix pumps and liquid_handler handle * feat: 优化protocol node节点运行日志 * fix all protocol_compilers and remove deprecated devices * feat: 新增use_remote_resource参数 * fix and remove redundant info * bugfixes on organic protocols * fix filter protocol * fix protocol node * 临时兼容错误的driver写法 * fix: prcxi import error * use call_async in all service to avoid deadlock * fix: figure_resource * Update recipe.yaml * add workstation template and battery example * feat: add sk & ak * update workstation base * Create workstation_architecture.md * refactor: workstation_base 重构为仅含业务逻辑,通信和子设备管理交给 ProtocolNode * refactor: ProtocolNode→WorkstationNode * Add:msgs.action (#83) * update: Workstation dev 将版本号从 0.10.3 更新为 0.10.4 (#84) * Add:msgs.action * update: 将版本号从 0.10.3 更新为 0.10.4 * simplify resource system * uncompleted refactor * example for use WorkstationBase * feat: websocket * feat: websocket test * feat: workstation example * feat: action status * fix: station自己的方法注册错误 * fix: 还原protocol node处理方法 * fix: build * fix: missing job_id key * ws test version 1 * ws test version 2 * ws protocol * 增加物料关系上传日志 * 增加物料关系上传日志 * 修正物料关系上传 * 修复工站的tracker实例追踪失效问题 * 增加handle检测,增加material edge关系上传 * 修复event loop错误 * 修复edge上报错误 * 修复async错误 * 更新schema的title字段 * 主机节点信息等支持自动刷新 * 注册表编辑器 * 修复status密集发送时,消息出错 * 增加addr参数 * fix: addr param * fix: addr param * 取消labid 和 强制config输入 * Add action definitions for LiquidHandlerSetGroup and LiquidHandlerTransferGroup - Created LiquidHandlerSetGroup.action with fields for group name, wells, and volumes. - Created LiquidHandlerTransferGroup.action with fields for source and target group names and unit volume. - Both actions include response fields for return information and success status. * Add LiquidHandlerSetGroup and LiquidHandlerTransferGroup actions to CMakeLists * Add set_group and transfer_group methods to PRCXI9300Handler and update liquid_handler.yaml * result_info改为字典类型 * 新增uat的地址替换 * runze multiple pump support (cherry picked from commit49354fcf39) * remove runze multiple software obtainer (cherry picked from commit8bcc92a394) * support multiple backbone (cherry picked from commit4771ff2347) * Update runze pump format * Correct runze multiple backbone * Update runze_multiple_backbone * Correct runze pump multiple receive method. * Correct runze pump multiple receive method. * 对于PRCXI9320的transfer_group,一对多和多对多 * 移除MQTT,更新launch文档,提供注册表示例文件,更新到0.10.5 * fix import error * fix dupe upload registry * refactor ws client * add server timeout * Fix: run-column with correct vessel id (#86) * fix run_column * Update run_column_protocol.py (cherry picked from commite5aa4d940a) * resource_update use resource_add * 新增版位推荐功能 * 重新规定了版位推荐的入参 * update registry with nested obj * fix protocol node log_message, added create_resource return value * fix protocol node log_message, added create_resource return value * try fix add protocol * fix resource_add * 修复移液站错误的aspirate注册表 * Feature/xprbalance-zhida (#80) * feat(devices): add Zhida GC/MS pretreatment automation workstation * feat(devices): add mettler_toledo xpr balance * balance * 重新补全zhida注册表 * PRCXI9320 json * PRCXI9320 json * PRCXI9320 json * fix resource download * remove class for resource * bump version to 0.10.6 * 更新所有注册表 * 修复protocolnode的兼容性 * 修复protocolnode的兼容性 * Update install md * Add Defaultlayout * 更新物料接口 * fix dict to tree/nested-dict converter * coin_cell_station draft * refactor: rename "station_resource" to "deck" * add standardized BIOYOND resources: bottle_carrier, bottle * refactor and add BIOYOND resources tests * add BIOYOND deck assignment and pass all tests * fix: update resource with correct structure; remove deprecated liquid_handler set_group action * feat: 将新威电池测试系统驱动与配置文件并入 workstation_dev_YB2 (#92) * feat: 新威电池测试系统驱动与注册文件 * feat: bring neware driver & battery.json into workstation_dev_YB2 * add bioyond studio draft * bioyond station with communication init and resource sync * fix bioyond station and registry * fix: update resource with correct structure; remove deprecated liquid_handler set_group action * frontend_docs * create/update resources with POST/PUT for big amount/ small amount data * create/update resources with POST/PUT for big amount/ small amount data * refactor: add itemized_carrier instead of carrier consists of ResourceHolder * create warehouse by factory func * update bioyond launch json * add child_size for itemized_carrier * fix bioyond resource io * Workstation templates: Resources and its CRUD, and workstation tasks (#95) * coin_cell_station draft * refactor: rename "station_resource" to "deck" * add standardized BIOYOND resources: bottle_carrier, bottle * refactor and add BIOYOND resources tests * add BIOYOND deck assignment and pass all tests * fix: update resource with correct structure; remove deprecated liquid_handler set_group action * feat: 将新威电池测试系统驱动与配置文件并入 workstation_dev_YB2 (#92) * feat: 新威电池测试系统驱动与注册文件 * feat: bring neware driver & battery.json into workstation_dev_YB2 * add bioyond studio draft * bioyond station with communication init and resource sync * fix bioyond station and registry * create/update resources with POST/PUT for big amount/ small amount data * refactor: add itemized_carrier instead of carrier consists of ResourceHolder * create warehouse by factory func * update bioyond launch json * add child_size for itemized_carrier * fix bioyond resource io --------- Co-authored-by: h840473807 <47357934+h840473807@users.noreply.github.com> Co-authored-by: Xie Qiming <97236197+Andy6M@users.noreply.github.com> * 更新物料接口 * Workstation dev yb2 (#100) * Refactor and extend reaction station action messages * Refactor dispensing station tasks to enhance parameter clarity and add batch processing capabilities - Updated `create_90_10_vial_feeding_task` to include detailed parameters for 90%/10% vial feeding, improving clarity and usability. - Introduced `create_batch_90_10_vial_feeding_task` for batch processing of 90%/10% vial feeding tasks with JSON formatted input. - Added `create_batch_diamine_solution_task` for batch preparation of diamine solution, also utilizing JSON formatted input. - Refined `create_diamine_solution_task` to include additional parameters for better task configuration. - Enhanced schema descriptions and default values for improved user guidance. * 修复to_plr_resources * add update remove * 支持选择器注册表自动生成 支持转运物料 * 修复资源添加 * 修复transfer_resource_to_another生成 * 更新transfer_resource_to_another参数,支持spot入参 * 新增test_resource动作 * fix host_node error * fix host_node test_resource error * fix host_node test_resource error * 过滤本地动作 * 移动内部action以兼容host node * 修复同步任务报错不显示的bug * feat: 允许返回非本节点物料,后面可以通过decoration进行区分,就不进行warning了 * update todo * modify bioyond/plr converter, bioyond resource registry, and tests * pass the tests * update todo * add conda-pack-build.yml * add auto install script for conda-pack-build.yml (cherry picked from commit172599adcf) * update conda-pack-build.yml * update conda-pack-build.yml * update conda-pack-build.yml * update conda-pack-build.yml * update conda-pack-build.yml * Add version in __init__.py Update conda-pack-build.yml Add create_zip_archive.py * Update conda-pack-build.yml * Update conda-pack-build.yml (with mamba) * Update conda-pack-build.yml * Fix FileNotFoundError * Try fix 'charmap' codec can't encode characters in position 16-23: character maps to <undefined> * Fix unilabos msgs search error * Fix environment_check.py * Update recipe.yaml * Update registry. Update uuid loop figure method. Update install docs. * Fix nested conda pack * Fix one-key installation path error * Bump version to 0.10.7 * Workshop bj (#99) * Add LaiYu Liquid device integration and tests Introduce LaiYu Liquid device implementation, including backend, controllers, drivers, configuration, and resource files. Add hardware connection, tip pickup, and simplified test scripts, as well as experiment and registry configuration for LaiYu Liquid. Documentation and .gitignore for the device are also included. * feat(LaiYu_Liquid): 重构设备模块结构并添加硬件文档 refactor: 重新组织LaiYu_Liquid模块目录结构 docs: 添加SOPA移液器和步进电机控制指令文档 fix: 修正设备配置中的最大体积默认值 test: 新增工作台配置测试用例 chore: 删除过时的测试脚本和配置文件 * add * 重构: 将 LaiYu_Liquid.py 重命名为 laiyu_liquid_main.py 并更新所有导入引用 - 使用 git mv 将 LaiYu_Liquid.py 重命名为 laiyu_liquid_main.py - 更新所有相关文件中的导入引用 - 保持代码功能不变,仅改善命名一致性 - 测试确认所有导入正常工作 * 修复: 在 core/__init__.py 中添加 LaiYuLiquidBackend 导出 - 添加 LaiYuLiquidBackend 到导入列表 - 添加 LaiYuLiquidBackend 到 __all__ 导出列表 - 确保所有主要类都可以正确导入 * 修复大小写文件夹名字 * 电池装配工站二次开发教程(带目录)上传至dev (#94) * 电池装配工站二次开发教程 * Update intro.md * 物料教程 * 更新物料教程,json格式注释 * Update prcxi driver & fix transfer_liquid mix_times (#90) * Update prcxi driver & fix transfer_liquid mix_times * fix: correct mix_times type * Update liquid_handler registry * test: prcxi.py * Update registry from pr * fix ony-key script not exist * clean files --------- Co-authored-by: Junhan Chang <changjh@dp.tech> Co-authored-by: ZiWei <131428629+ZiWei09@users.noreply.github.com> Co-authored-by: Guangxin Zhang <guangxin.zhang.bio@gmail.com> Co-authored-by: Xie Qiming <97236197+Andy6M@users.noreply.github.com> Co-authored-by: h840473807 <47357934+h840473807@users.noreply.github.com> Co-authored-by: LccLink <1951855008@qq.com> Co-authored-by: lixinyu1011 <61094742+lixinyu1011@users.noreply.github.com> Co-authored-by: shiyubo0410 <shiyubo@dp.tech>
548 lines
22 KiB
Python
548 lines
22 KiB
Python
from typing import List, Dict, Any, Union
|
||
import networkx as nx
|
||
import logging
|
||
import re
|
||
|
||
from .utils.unit_parser import parse_time_input, parse_volume_input
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
def debug_print(message):
|
||
"""调试输出"""
|
||
logger.info(f"[WASH_SOLID] {message}")
|
||
|
||
|
||
def find_solvent_source(G: nx.DiGraph, solvent: str) -> str:
|
||
"""查找溶剂源(精简版)"""
|
||
debug_print(f"🔍 查找溶剂源: {solvent}")
|
||
|
||
# 简化搜索列表
|
||
search_patterns = [
|
||
f"flask_{solvent}", f"bottle_{solvent}", f"reagent_{solvent}",
|
||
"liquid_reagent_bottle_1", "flask_1", "solvent_bottle"
|
||
]
|
||
|
||
for pattern in search_patterns:
|
||
if pattern in G.nodes():
|
||
debug_print(f"🎉 找到溶剂源: {pattern}")
|
||
return pattern
|
||
|
||
debug_print(f"⚠️ 使用默认溶剂源: flask_{solvent}")
|
||
return f"flask_{solvent}"
|
||
|
||
def find_filtrate_vessel(G: nx.DiGraph, filtrate_vessel: str = "") -> str:
|
||
"""查找滤液容器(精简版)"""
|
||
debug_print(f"🔍 查找滤液容器: {filtrate_vessel}")
|
||
|
||
# 如果指定了且存在,直接使用
|
||
if filtrate_vessel and filtrate_vessel in G.nodes():
|
||
debug_print(f"✅ 使用指定容器: {filtrate_vessel}")
|
||
return filtrate_vessel
|
||
|
||
# 简化搜索列表
|
||
default_vessels = ["waste_workup", "filtrate_vessel", "flask_1", "collection_bottle_1"]
|
||
|
||
for vessel in default_vessels:
|
||
if vessel in G.nodes():
|
||
debug_print(f"🎉 找到滤液容器: {vessel}")
|
||
return vessel
|
||
|
||
debug_print(f"⚠️ 使用默认滤液容器: waste_workup")
|
||
return "waste_workup"
|
||
|
||
def extract_vessel_id(vessel: Union[str, dict]) -> str:
|
||
"""
|
||
从vessel参数中提取vessel_id
|
||
|
||
Args:
|
||
vessel: vessel字典或vessel_id字符串
|
||
|
||
Returns:
|
||
str: vessel_id
|
||
"""
|
||
if isinstance(vessel, dict):
|
||
vessel_id = list(vessel.values())[0].get("id", "")
|
||
debug_print(f"🔧 从vessel字典提取ID: {vessel_id}")
|
||
return vessel_id
|
||
elif isinstance(vessel, str):
|
||
debug_print(f"🔧 vessel参数为字符串: {vessel}")
|
||
return vessel
|
||
else:
|
||
debug_print(f"⚠️ 无效的vessel参数类型: {type(vessel)}")
|
||
return ""
|
||
|
||
def get_vessel_display_info(vessel: Union[str, dict]) -> str:
|
||
"""
|
||
获取容器的显示信息(用于日志)
|
||
|
||
Args:
|
||
vessel: vessel字典或vessel_id字符串
|
||
|
||
Returns:
|
||
str: 显示信息
|
||
"""
|
||
if isinstance(vessel, dict):
|
||
vessel_id = vessel.get("id", "unknown")
|
||
vessel_name = vessel.get("name", "")
|
||
if vessel_name:
|
||
return f"{vessel_id} ({vessel_name})"
|
||
else:
|
||
return vessel_id
|
||
else:
|
||
return str(vessel)
|
||
|
||
def get_vessel_liquid_volume(vessel: dict) -> float:
|
||
"""
|
||
获取容器中的液体体积 - 支持vessel字典
|
||
|
||
Args:
|
||
vessel: 容器字典
|
||
|
||
Returns:
|
||
float: 液体体积(mL)
|
||
"""
|
||
if not vessel or "data" not in vessel:
|
||
debug_print(f"⚠️ 容器数据为空,返回 0.0mL")
|
||
return 0.0
|
||
|
||
vessel_data = vessel["data"]
|
||
vessel_id = vessel.get("id", "unknown")
|
||
|
||
debug_print(f"🔍 读取容器 '{vessel_id}' 体积数据: {vessel_data}")
|
||
|
||
# 检查liquid_volume字段
|
||
if "liquid_volume" in vessel_data:
|
||
liquid_volume = vessel_data["liquid_volume"]
|
||
|
||
# 处理列表格式
|
||
if isinstance(liquid_volume, list):
|
||
if len(liquid_volume) > 0:
|
||
volume = liquid_volume[0]
|
||
if isinstance(volume, (int, float)):
|
||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (列表格式)")
|
||
return float(volume)
|
||
|
||
# 处理直接数值格式
|
||
elif isinstance(liquid_volume, (int, float)):
|
||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {liquid_volume}mL (数值格式)")
|
||
return float(liquid_volume)
|
||
|
||
# 检查其他可能的体积字段
|
||
volume_keys = ['current_volume', 'total_volume', 'volume']
|
||
for key in volume_keys:
|
||
if key in vessel_data:
|
||
try:
|
||
volume = float(vessel_data[key])
|
||
if volume > 0:
|
||
debug_print(f"✅ 容器 '{vessel_id}' 体积: {volume}mL (字段: {key})")
|
||
return volume
|
||
except (ValueError, TypeError):
|
||
continue
|
||
|
||
debug_print(f"⚠️ 无法获取容器 '{vessel_id}' 的体积,返回默认值 0.0mL")
|
||
return 0.0
|
||
|
||
def update_vessel_volume(vessel: dict, G: nx.DiGraph, new_volume: float, description: str = "") -> None:
|
||
"""
|
||
更新容器体积(同时更新vessel字典和图节点)
|
||
|
||
Args:
|
||
vessel: 容器字典
|
||
G: 网络图
|
||
new_volume: 新体积
|
||
description: 更新描述
|
||
"""
|
||
vessel_id = vessel.get("id", "unknown")
|
||
|
||
if description:
|
||
debug_print(f"🔧 更新容器体积 - {description}")
|
||
|
||
# 更新vessel字典中的体积
|
||
if "data" in vessel:
|
||
if "liquid_volume" in vessel["data"]:
|
||
current_volume = vessel["data"]["liquid_volume"]
|
||
if isinstance(current_volume, list):
|
||
if len(current_volume) > 0:
|
||
vessel["data"]["liquid_volume"][0] = new_volume
|
||
else:
|
||
vessel["data"]["liquid_volume"] = [new_volume]
|
||
else:
|
||
vessel["data"]["liquid_volume"] = new_volume
|
||
else:
|
||
vessel["data"]["liquid_volume"] = new_volume
|
||
else:
|
||
vessel["data"] = {"liquid_volume": new_volume}
|
||
|
||
# 同时更新图中的容器数据
|
||
if vessel_id in G.nodes():
|
||
if 'data' not in G.nodes[vessel_id]:
|
||
G.nodes[vessel_id]['data'] = {}
|
||
|
||
vessel_node_data = G.nodes[vessel_id]['data']
|
||
current_node_volume = vessel_node_data.get('liquid_volume', 0.0)
|
||
|
||
if isinstance(current_node_volume, list):
|
||
if len(current_node_volume) > 0:
|
||
G.nodes[vessel_id]['data']['liquid_volume'][0] = new_volume
|
||
else:
|
||
G.nodes[vessel_id]['data']['liquid_volume'] = [new_volume]
|
||
else:
|
||
G.nodes[vessel_id]['data']['liquid_volume'] = new_volume
|
||
|
||
debug_print(f"📊 容器 '{vessel_id}' 体积已更新为: {new_volume:.2f}mL")
|
||
|
||
def generate_wash_solid_protocol(
|
||
G: nx.DiGraph,
|
||
vessel: Union[str, dict], # 🔧 修改:支持vessel字典
|
||
solvent: str,
|
||
volume: Union[float, str] = "50",
|
||
filtrate_vessel: Union[str, dict] = "", # 🔧 修改:支持vessel字典
|
||
temp: float = 25.0,
|
||
stir: bool = False,
|
||
stir_speed: float = 0.0,
|
||
time: Union[str, float] = "0",
|
||
repeats: int = 1,
|
||
volume_spec: str = "",
|
||
repeats_spec: str = "",
|
||
mass: str = "",
|
||
event: str = "",
|
||
**kwargs
|
||
) -> List[Dict[str, Any]]:
|
||
"""
|
||
生成固体清洗协议 - 支持vessel字典和体积运算
|
||
|
||
Args:
|
||
G: 有向图,节点为设备和容器,边为流体管道
|
||
vessel: 清洗容器字典(从XDL传入)或容器ID字符串
|
||
solvent: 清洗溶剂名称
|
||
volume: 溶剂体积(每次清洗)
|
||
filtrate_vessel: 滤液收集容器字典或容器ID字符串
|
||
temp: 清洗温度(°C)
|
||
stir: 是否搅拌
|
||
stir_speed: 搅拌速度(RPM)
|
||
time: 搅拌时间
|
||
repeats: 清洗重复次数
|
||
volume_spec: 体积规格(small/medium/large)
|
||
repeats_spec: 重复次数规格(few/several/many)
|
||
mass: 固体质量(用于计算溶剂用量)
|
||
event: 事件描述
|
||
**kwargs: 其他可选参数
|
||
|
||
Returns:
|
||
List[Dict[str, Any]]: 固体清洗操作的动作序列
|
||
"""
|
||
|
||
# 🔧 核心修改:从vessel参数中提取vessel_id
|
||
vessel_id = extract_vessel_id(vessel)
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
|
||
# 🔧 处理filtrate_vessel参数
|
||
filtrate_vessel_id = extract_vessel_id(filtrate_vessel) if filtrate_vessel else ""
|
||
|
||
debug_print("🧼" * 20)
|
||
debug_print("🚀 开始生成固体清洗协议(支持vessel字典和体积运算)✨")
|
||
debug_print(f"📝 输入参数:")
|
||
debug_print(f" 🥽 vessel: {vessel_display} (ID: {vessel_id})")
|
||
debug_print(f" 🧪 solvent: {solvent}")
|
||
debug_print(f" 💧 volume: {volume}")
|
||
debug_print(f" 🗑️ filtrate_vessel: {filtrate_vessel_id}")
|
||
debug_print(f" ⏰ time: {time}")
|
||
debug_print(f" 🔄 repeats: {repeats}")
|
||
debug_print("🧼" * 20)
|
||
|
||
# 🔧 新增:记录清洗前的容器状态
|
||
debug_print("🔍 记录清洗前容器状态...")
|
||
if isinstance(vessel, dict):
|
||
original_volume = get_vessel_liquid_volume(vessel)
|
||
debug_print(f"📊 清洗前液体体积: {original_volume:.2f}mL")
|
||
else:
|
||
original_volume = 0.0
|
||
debug_print(f"📊 vessel为字符串格式,无法获取体积信息")
|
||
|
||
# 📋 快速验证
|
||
if not vessel_id or vessel_id not in G.nodes(): # 🔧 使用 vessel_id
|
||
debug_print("❌ 容器验证失败! 😱")
|
||
raise ValueError("vessel 参数无效")
|
||
|
||
if not solvent:
|
||
debug_print("❌ 溶剂不能为空! 😱")
|
||
raise ValueError("solvent 参数不能为空")
|
||
|
||
debug_print("✅ 基础验证通过 🎯")
|
||
|
||
# 🔄 参数解析
|
||
debug_print("📍 步骤1: 参数解析... ⚡")
|
||
final_volume = parse_volume_input(volume, volume_spec, mass)
|
||
final_time = parse_time_input(time)
|
||
|
||
# 重复次数处理(简化)
|
||
if repeats_spec:
|
||
spec_map = {'few': 2, 'several': 3, 'many': 4, 'thorough': 5}
|
||
final_repeats = next((v for k, v in spec_map.items() if k in repeats_spec.lower()), repeats)
|
||
else:
|
||
final_repeats = max(1, min(repeats, 5)) # 限制1-5次
|
||
|
||
# 🕐 模拟时间优化
|
||
debug_print(" ⏱️ 模拟时间优化...")
|
||
original_time = final_time
|
||
if final_time > 60.0:
|
||
final_time = 60.0 # 限制最长60秒
|
||
debug_print(f" 🎮 时间优化: {original_time}s → {final_time}s ⚡")
|
||
|
||
# 参数修正
|
||
temp = max(25.0, min(temp, 80.0)) # 温度范围25-80°C
|
||
stir_speed = max(0.0, min(stir_speed, 300.0)) if stir else 0.0 # 速度范围0-300
|
||
|
||
debug_print(f"🎯 最终参数: 体积={final_volume}mL, 时间={final_time}s, 重复={final_repeats}次")
|
||
|
||
# 🔍 查找设备
|
||
debug_print("📍 步骤2: 查找设备... 🔍")
|
||
try:
|
||
solvent_source = find_solvent_source(G, solvent)
|
||
actual_filtrate_vessel = find_filtrate_vessel(G, filtrate_vessel_id)
|
||
debug_print(f"🎉 设备配置完成 ✨")
|
||
debug_print(f" 🧪 溶剂源: {solvent_source}")
|
||
debug_print(f" 🗑️ 滤液容器: {actual_filtrate_vessel}")
|
||
except Exception as e:
|
||
debug_print(f"❌ 设备查找失败: {str(e)} 😭")
|
||
raise ValueError(f"设备查找失败: {str(e)}")
|
||
|
||
# 🚀 生成动作序列
|
||
debug_print("📍 步骤3: 生成清洗动作... 🧼")
|
||
action_sequence = []
|
||
|
||
# 🔧 新增:体积变化跟踪变量
|
||
current_volume = original_volume
|
||
total_solvent_used = 0.0
|
||
|
||
for cycle in range(final_repeats):
|
||
debug_print(f" 🔄 第{cycle+1}/{final_repeats}次清洗...")
|
||
|
||
# 1. 转移溶剂
|
||
try:
|
||
from .pump_protocol import generate_pump_protocol_with_rinsing
|
||
|
||
debug_print(f" 💧 添加溶剂: {final_volume}mL {solvent}")
|
||
transfer_actions = generate_pump_protocol_with_rinsing(
|
||
G=G,
|
||
from_vessel=solvent_source,
|
||
to_vessel=vessel_id, # 🔧 使用 vessel_id
|
||
volume=final_volume,
|
||
amount="",
|
||
time=0.0,
|
||
viscous=False,
|
||
rinsing_solvent="",
|
||
rinsing_volume=0.0,
|
||
rinsing_repeats=0,
|
||
solid=False,
|
||
flowrate=2.5,
|
||
transfer_flowrate=0.5
|
||
)
|
||
|
||
if transfer_actions:
|
||
action_sequence.extend(transfer_actions)
|
||
debug_print(f" ✅ 转移动作: {len(transfer_actions)}个 🚚")
|
||
|
||
# 🔧 新增:更新体积 - 添加溶剂后
|
||
current_volume += final_volume
|
||
total_solvent_used += final_volume
|
||
|
||
if isinstance(vessel, dict):
|
||
update_vessel_volume(vessel, G, current_volume,
|
||
f"第{cycle+1}次清洗添加{final_volume}mL溶剂后")
|
||
|
||
except Exception as e:
|
||
debug_print(f" ❌ 转移失败: {str(e)} 😞")
|
||
|
||
# 2. 搅拌(如果需要)
|
||
if stir and final_time > 0:
|
||
debug_print(f" 🌪️ 搅拌: {final_time}s @ {stir_speed}RPM")
|
||
stir_action = {
|
||
"device_id": "stirrer_1",
|
||
"action_name": "stir",
|
||
"action_kwargs": {
|
||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
||
"time": str(time),
|
||
"stir_time": final_time,
|
||
"stir_speed": stir_speed,
|
||
"settling_time": 10.0 # 🕐 缩短沉降时间
|
||
}
|
||
}
|
||
action_sequence.append(stir_action)
|
||
debug_print(f" ✅ 搅拌动作: {final_time}s, {stir_speed}RPM 🌪️")
|
||
|
||
# 3. 过滤
|
||
debug_print(f" 🌊 过滤到: {actual_filtrate_vessel}")
|
||
filter_action = {
|
||
"device_id": "filter_1",
|
||
"action_name": "filter",
|
||
"action_kwargs": {
|
||
"vessel": {"id": vessel_id}, # 🔧 使用 vessel_id
|
||
"filtrate_vessel": actual_filtrate_vessel,
|
||
"temp": temp,
|
||
"volume": final_volume
|
||
}
|
||
}
|
||
action_sequence.append(filter_action)
|
||
debug_print(f" ✅ 过滤动作: → {actual_filtrate_vessel} 🌊")
|
||
|
||
# 🔧 新增:更新体积 - 过滤后(液体被滤除)
|
||
# 假设滤液完全被移除,固体残留在容器中
|
||
filtered_volume = current_volume * 0.9 # 假设90%的液体被过滤掉
|
||
current_volume = current_volume - filtered_volume
|
||
|
||
if isinstance(vessel, dict):
|
||
update_vessel_volume(vessel, G, current_volume,
|
||
f"第{cycle+1}次清洗过滤后")
|
||
|
||
# 4. 等待(缩短时间)
|
||
wait_time = 5.0 # 🕐 缩短等待时间:10s → 5s
|
||
action_sequence.append({
|
||
"action_name": "wait",
|
||
"action_kwargs": {"time": wait_time}
|
||
})
|
||
debug_print(f" ✅ 等待: {wait_time}s ⏰")
|
||
|
||
# 🔧 新增:清洗完成后的最终状态报告
|
||
if isinstance(vessel, dict):
|
||
final_volume_vessel = get_vessel_liquid_volume(vessel)
|
||
else:
|
||
final_volume_vessel = current_volume
|
||
|
||
# 🎊 总结
|
||
debug_print("🧼" * 20)
|
||
debug_print(f"🎉 固体清洗协议生成完成! ✨")
|
||
debug_print(f"📊 协议统计:")
|
||
debug_print(f" 📋 总动作数: {len(action_sequence)} 个")
|
||
debug_print(f" 🥽 清洗容器: {vessel_display}")
|
||
debug_print(f" 🧪 使用溶剂: {solvent}")
|
||
debug_print(f" 💧 单次体积: {final_volume}mL")
|
||
debug_print(f" 🔄 清洗次数: {final_repeats}次")
|
||
debug_print(f" 💧 总溶剂用量: {total_solvent_used:.2f}mL")
|
||
debug_print(f"📊 体积变化统计:")
|
||
debug_print(f" - 清洗前体积: {original_volume:.2f}mL")
|
||
debug_print(f" - 清洗后体积: {final_volume_vessel:.2f}mL")
|
||
debug_print(f" - 溶剂总用量: {total_solvent_used:.2f}mL")
|
||
debug_print(f"⏱️ 预计总时间: {(final_time + 5) * final_repeats / 60:.1f} 分钟")
|
||
debug_print("🧼" * 20)
|
||
|
||
return action_sequence
|
||
|
||
# 🔧 新增:便捷函数
|
||
def wash_with_water(G: nx.DiGraph, vessel: Union[str, dict],
|
||
volume: Union[float, str] = "50",
|
||
repeats: int = 2) -> List[Dict[str, Any]]:
|
||
"""用水清洗固体"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"💧 水洗固体: {vessel_display} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, "water", volume=volume, repeats=repeats)
|
||
|
||
def wash_with_ethanol(G: nx.DiGraph, vessel: Union[str, dict],
|
||
volume: Union[float, str] = "30",
|
||
repeats: int = 1) -> List[Dict[str, Any]]:
|
||
"""用乙醇清洗固体"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"🍺 乙醇洗固体: {vessel_display} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, "ethanol", volume=volume, repeats=repeats)
|
||
|
||
def wash_with_acetone(G: nx.DiGraph, vessel: Union[str, dict],
|
||
volume: Union[float, str] = "25",
|
||
repeats: int = 1) -> List[Dict[str, Any]]:
|
||
"""用丙酮清洗固体"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"💨 丙酮洗固体: {vessel_display} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, "acetone", volume=volume, repeats=repeats)
|
||
|
||
def wash_with_ether(G: nx.DiGraph, vessel: Union[str, dict],
|
||
volume: Union[float, str] = "40",
|
||
repeats: int = 2) -> List[Dict[str, Any]]:
|
||
"""用乙醚清洗固体"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"🌬️ 乙醚洗固体: {vessel_display} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, "diethyl_ether", volume=volume, repeats=repeats)
|
||
|
||
def wash_with_cold_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
||
solvent: str, volume: Union[float, str] = "30",
|
||
repeats: int = 1) -> List[Dict[str, Any]]:
|
||
"""用冷溶剂清洗固体"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"❄️ 冷{solvent}洗固体: {vessel_display} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
||
temp=5.0, repeats=repeats)
|
||
|
||
def wash_with_hot_solvent(G: nx.DiGraph, vessel: Union[str, dict],
|
||
solvent: str, volume: Union[float, str] = "50",
|
||
repeats: int = 1) -> List[Dict[str, Any]]:
|
||
"""用热溶剂清洗固体"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"🔥 热{solvent}洗固体: {vessel_display} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
||
temp=60.0, repeats=repeats)
|
||
|
||
def wash_with_stirring(G: nx.DiGraph, vessel: Union[str, dict],
|
||
solvent: str, volume: Union[float, str] = "50",
|
||
stir_time: Union[str, float] = "5 min",
|
||
repeats: int = 1) -> List[Dict[str, Any]]:
|
||
"""带搅拌的溶剂清洗"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"🌪️ 搅拌清洗: {vessel_display} with {solvent} ({repeats} 次)")
|
||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume,
|
||
stir=True, stir_speed=200.0,
|
||
time=stir_time, repeats=repeats)
|
||
|
||
def thorough_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
||
solvent: str, volume: Union[float, str] = "50") -> List[Dict[str, Any]]:
|
||
"""彻底清洗(多次重复)"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"🔄 彻底清洗: {vessel_display} with {solvent} (5 次)")
|
||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume, repeats=5)
|
||
|
||
def quick_rinse(G: nx.DiGraph, vessel: Union[str, dict],
|
||
solvent: str, volume: Union[float, str] = "20") -> List[Dict[str, Any]]:
|
||
"""快速冲洗(单次,小体积)"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"⚡ 快速冲洗: {vessel_display} with {solvent}")
|
||
return generate_wash_solid_protocol(G, vessel, solvent, volume=volume, repeats=1)
|
||
|
||
def sequential_wash(G: nx.DiGraph, vessel: Union[str, dict],
|
||
solvents: list, volume: Union[float, str] = "40") -> List[Dict[str, Any]]:
|
||
"""连续多溶剂清洗"""
|
||
vessel_display = get_vessel_display_info(vessel)
|
||
debug_print(f"📝 连续清洗: {vessel_display} with {' → '.join(solvents)}")
|
||
|
||
action_sequence = []
|
||
for solvent in solvents:
|
||
wash_actions = generate_wash_solid_protocol(G, vessel, solvent,
|
||
volume=volume, repeats=1)
|
||
action_sequence.extend(wash_actions)
|
||
|
||
return action_sequence
|
||
|
||
# 测试函数
|
||
def test_wash_solid_protocol():
|
||
"""测试固体清洗协议"""
|
||
debug_print("🧪 === WASH SOLID PROTOCOL 测试 === ✨")
|
||
|
||
# 测试vessel参数处理
|
||
debug_print("🔧 测试vessel参数处理...")
|
||
|
||
# 测试字典格式
|
||
vessel_dict = {"id": "filter_flask_1", "name": "过滤瓶1",
|
||
"data": {"liquid_volume": 25.0}}
|
||
vessel_id = extract_vessel_id(vessel_dict)
|
||
vessel_display = get_vessel_display_info(vessel_dict)
|
||
volume = get_vessel_liquid_volume(vessel_dict)
|
||
debug_print(f" 字典格式: {vessel_dict}")
|
||
debug_print(f" → ID: {vessel_id}, 显示: {vessel_display}, 体积: {volume}mL")
|
||
|
||
# 测试字符串格式
|
||
vessel_str = "filter_flask_2"
|
||
vessel_id = extract_vessel_id(vessel_str)
|
||
vessel_display = get_vessel_display_info(vessel_str)
|
||
debug_print(f" 字符串格式: {vessel_str}")
|
||
debug_print(f" → ID: {vessel_id}, 显示: {vessel_display}")
|
||
|
||
debug_print("✅ 测试完成 🎉")
|
||
|
||
if __name__ == "__main__":
|
||
test_wash_solid_protocol() |