From d57e5ffdae21b7fc130d4d7ff2047d1d314f63f6 Mon Sep 17 00:00:00 2001 From: ZiWei <131428629+ZiWei09@users.noreply.github.com> Date: Thu, 25 Dec 2025 12:19:37 +0800 Subject: [PATCH] Refactor bioyond_dispensing_station and reaction_station_bioyond YAML configurations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed redundant action value mappings from bioyond_dispensing_station. - Updated goal properties in bioyond_dispensing_station to use enums for target_stack and other parameters. - Changed data types for end_point and start_point in reaction_station_bioyond to use string enums (Start, End). - Simplified descriptions and updated measurement units from μL to mL where applicable. - Removed unused commands from reaction_station_bioyond to streamline the configuration. --- .../bioyond_studio/reaction_station.py | 230 ++++++----- .../devices/bioyond_dispensing_station.yaml | 200 +--------- .../devices/reaction_station_bioyond.yaml | 373 ++++-------------- 3 files changed, 217 insertions(+), 586 deletions(-) diff --git a/unilabos/devices/workstation/bioyond_studio/reaction_station.py b/unilabos/devices/workstation/bioyond_studio/reaction_station.py index 6ea76df..bd0d94b 100644 --- a/unilabos/devices/workstation/bioyond_studio/reaction_station.py +++ b/unilabos/devices/workstation/bioyond_studio/reaction_station.py @@ -49,16 +49,16 @@ class BioyondReactor: class BioyondReactionStation(BioyondWorkstation): """Bioyond反应站类 - 继承自BioyondWorkstation,提供反应站特定的业务方法 + 继承自BioyondWorkstation,提供反应站特定的业务方法 """ def __init__(self, config: dict = None, deck=None, protocol_type=None, **kwargs): """初始化反应站 Args: - config: 配置字典,应包含workflow_mappings等配置 + config: 配置字典,应包含workflow_mappings等配置 deck: Deck对象 - protocol_type: 协议类型(由ROS系统传递,此处忽略) + protocol_type: 协议类型(由ROS系统传递,此处忽略) **kwargs: 其他可能的参数 """ if deck is None and config: @@ -123,10 +123,10 @@ class BioyondReactionStation(BioyondWorkstation): return json.dumps({"suc": True}) def scheduler_start(self) -> dict: - """启动调度器 - 启动Bioyond工作站的任务调度器,开始执行队列中的任务 + """启动调度器 - 启动Bioyond工作站的任务调度器,开始执行队列中的任务 Returns: - dict: 包含return_info的字典,return_info为整型(1=成功) + dict: 包含return_info的字典,return_info为整型(1=成功) Raises: BioyondException: 调度器启动失败时抛出异常 @@ -137,7 +137,7 @@ class BioyondReactionStation(BioyondWorkstation): self.hardware_interface._logger.info(f"调度器启动结果: {result}") if result != 1: - error_msg = "启动调度器失败: 有未处理错误,调度无法启动。请检查Bioyond系统状态。" + error_msg = "启动调度器失败: 有未处理错误,调度无法启动。请检查Bioyond系统状态。" self.hardware_interface._logger.error(error_msg) raise BioyondException(error_msg) @@ -152,12 +152,12 @@ class BioyondReactionStation(BioyondWorkstation): """反应器放入 Args: - assign_material_name: 物料名称(不能为空) - cutoff: 粘度上限(需为有效数字字符串,默认 "900000") - temperature: 温度设定(°C,范围:-50.00 至 100.00) + assign_material_name: 物料名称(不能为空) + cutoff: 粘度上限(需为有效数字字符串,默认 "900000") + temperature: 温度设定(C,范围:-50.00 至 100.00) Returns: - str: JSON 字符串,格式为 {"suc": True} + str: JSON 字符串,格式为 {"suc": True} Raises: ValueError: 若物料名称无效或 cutoff 格式错误 @@ -177,6 +177,7 @@ class BioyondReactionStation(BioyondWorkstation): if isinstance(temperature, str): temperature = float(temperature) + step_id = WORKFLOW_STEP_IDS["reactor_taken_in"]["config"] reactor_taken_in_params = { "param_values": { @@ -208,12 +209,19 @@ class BioyondReactionStation(BioyondWorkstation): """固体进料小瓶 Args: - material_id: 粉末类型ID,1=盐(21分钟),2=面粉(27分钟),3=BTDA(38分钟) + material_id: 粉末类型ID, Salt=1, Flour=2, BTDA=3 time: 观察时间(分钟) - torque_variation: 是否观察(int类型, 1=否, 2=是) + torque_variation: 是否观察(NO=1, YES=2) assign_material_name: 物料名称(用于获取试剂瓶位ID) - temperature: 温度设定(°C) + temperature: 温度设定(C) """ + # 参数映射 + material_map = {"Salt": "1", "Flour": "2", "BTDA": "3", "1": "1", "2": "2", "3": "3"} + torque_map = {"NO": "1", "YES": "2", 1: "1", 2: "2", "1": "1", "2": "2"} + + mapped_material_id = material_map.get(str(material_id), str(material_id)) + mapped_torque_variation = int(torque_map.get(str(torque_variation), "1")) + self.append_to_workflow_sequence('{"web_workflow_name": "Solid_feeding_vials"}') material_id_m = self.hardware_interface._get_material_id_by_name(assign_material_name) if assign_material_name else None @@ -227,14 +235,14 @@ class BioyondReactionStation(BioyondWorkstation): "param_values": { feeding_step_id: { ACTION_NAMES["solid_feeding_vials"]["feeding"]: [ - {"m": 0, "n": 3, "Key": "materialId", "Value": material_id}, + {"m": 0, "n": 3, "Key": "materialId", "Value": mapped_material_id}, {"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id_m} if material_id_m else {} ] }, observe_step_id: { ACTION_NAMES["solid_feeding_vials"]["observe"]: [ {"m": 1, "n": 0, "Key": "time", "Value": time}, - {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)}, + {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(mapped_torque_variation)}, {"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"} ] } @@ -242,7 +250,7 @@ class BioyondReactionStation(BioyondWorkstation): } self.pending_task_params.append(solid_feeding_vials_params) - print(f"成功添加固体进料小瓶参数: material_id={material_id}, time={time}min, torque={torque_variation}, temp={temperature:.2f}°C") + print(f"成功添加固体进料小瓶参数: material_id={material_id}, time={time}min, torque={torque_variation}, temp={temperature:.2f}C") print(f"当前队列长度: {len(self.pending_task_params)}") return json.dumps({"suc": True}) @@ -260,11 +268,18 @@ class BioyondReactionStation(BioyondWorkstation): Args: volume_formula: 分液公式(μL) assign_material_name: 物料名称 - titration_type: 是否滴定(1=否, 2=是) + titration_type: 是否滴定(NO=1, YES=2) time: 观察时间(分钟) - torque_variation: 是否观察(int类型, 1=否, 2=是) - temperature: 温度(°C) + torque_variation: 是否观察(NO=1, YES=2) + temperature: 温度(C) """ + # 参数映射 + titration_map = {"NO": "1", "YES": "2", "1": "1", "2": "2"} + torque_map = {"NO": "1", "YES": "2", 1: "1", 2: "2", "1": "1", "2": "2"} + + mapped_titration_type = titration_map.get(str(titration_type), "1") + mapped_torque_variation = int(torque_map.get(str(torque_variation), "1")) + self.append_to_workflow_sequence('{"web_workflow_name": "Liquid_feeding_vials(non-titration)"}') material_id = self.hardware_interface._get_material_id_by_name(assign_material_name) if material_id is None: @@ -282,13 +297,13 @@ class BioyondReactionStation(BioyondWorkstation): ACTION_NAMES["liquid_feeding_vials_non_titration"]["liquid"]: [ {"m": 0, "n": 3, "Key": "volumeFormula", "Value": volume_formula}, {"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id}, - {"m": 0, "n": 3, "Key": "titrationType", "Value": titration_type} + {"m": 0, "n": 3, "Key": "titrationType", "Value": mapped_titration_type} ] }, observe_step_id: { ACTION_NAMES["liquid_feeding_vials_non_titration"]["observe"]: [ {"m": 1, "n": 0, "Key": "time", "Value": time}, - {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)}, + {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(mapped_torque_variation)}, {"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"} ] } @@ -321,11 +336,18 @@ class BioyondReactionStation(BioyondWorkstation): "total_liquid_volume": 48.00916988195499 } 如果提供solvents,则从中提取additional_solvent并转换为μL - titration_type: 是否滴定(1=否, 2=是) + titration_type: 是否滴定(NO=1, YES=2) time: 观察时间(分钟) - torque_variation: 是否观察(int类型, 1=否, 2=是) - temperature: 温度设定(°C) + torque_variation: 是否观察(NO=1, YES=2) + temperature: 温度设定(C) """ + # 参数映射 + titration_map = {"NO": "1", "YES": "2", "1": "1", "2": "2"} + torque_map = {"NO": "1", "YES": "2", 1: "1", 2: "2", "1": "1", "2": "2"} + + mapped_titration_type = titration_map.get(str(titration_type), "1") + mapped_torque_variation = int(torque_map.get(str(torque_variation), "1")) + # 处理 volume 参数:优先使用直接传入的 volume,否则从 solvents 中提取 if not volume and solvents is not None: # 参数类型转换:如果是字符串则解析为字典 @@ -364,7 +386,7 @@ class BioyondReactionStation(BioyondWorkstation): "param_values": { liquid_step_id: { ACTION_NAMES["liquid_feeding_solvents"]["liquid"]: [ - {"m": 0, "n": 1, "Key": "titrationType", "Value": titration_type}, + {"m": 0, "n": 1, "Key": "titrationType", "Value": mapped_titration_type}, {"m": 0, "n": 1, "Key": "volume", "Value": volume}, {"m": 0, "n": 1, "Key": "assignMaterialName", "Value": material_id} ] @@ -372,7 +394,7 @@ class BioyondReactionStation(BioyondWorkstation): observe_step_id: { ACTION_NAMES["liquid_feeding_solvents"]["observe"]: [ {"m": 1, "n": 0, "Key": "time", "Value": time}, - {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)}, + {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(mapped_torque_variation)}, {"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"} ] } @@ -408,10 +430,10 @@ class BioyondReactionStation(BioyondWorkstation): x_value: 手工输入的x值,格式如 "1-2-3" feeding_order_data: feeding_order JSON字符串或对象,用于获取m二酐值 extracted_actuals: 从报告提取的实际加料量JSON字符串,包含actualTargetWeigh和actualVolume - titration_type: 是否滴定(1=否, 2=是),默认2 + titration_type: 是否滴定(NO=1, YES=2),默认2 time: 观察时间(分钟) - torque_variation: 是否观察(int类型, 1=否, 2=是) - temperature: 温度(°C) + torque_variation: 是否观察(NO=1, YES=2) + temperature: 温度(C) 自动公式模板: 1000*(m二酐-x)*V二酐滴定/m二酐滴定 其中: @@ -420,6 +442,13 @@ class BioyondReactionStation(BioyondWorkstation): - x = x_value (手工输入) - m二酐 = feeding_order中type为"main_anhydride"的amount值 """ + # 参数映射 + titration_map = {"NO": "1", "YES": "2", "1": "1", "2": "2"} + torque_map = {"NO": "1", "YES": "2", 1: "1", 2: "2", "1": "1", "2": "2"} + + mapped_titration_type = titration_map.get(str(titration_type), "2") + mapped_torque_variation = int(torque_map.get(str(torque_variation), "1")) + self.append_to_workflow_sequence('{"web_workflow_name": "Liquid_feeding(titration)"}') material_id = self.hardware_interface._get_material_id_by_name(assign_material_name) if material_id is None: @@ -514,14 +543,14 @@ class BioyondReactionStation(BioyondWorkstation): liquid_step_id: { ACTION_NAMES["liquid_feeding_titration"]["liquid"]: [ {"m": 0, "n": 3, "Key": "volumeFormula", "Value": volume_formula}, - {"m": 0, "n": 3, "Key": "titrationType", "Value": titration_type}, + {"m": 0, "n": 3, "Key": "titrationType", "Value": mapped_titration_type}, {"m": 0, "n": 3, "Key": "assignMaterialName", "Value": material_id} ] }, observe_step_id: { ACTION_NAMES["liquid_feeding_titration"]["observe"]: [ {"m": 1, "n": 0, "Key": "time", "Value": time}, - {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)}, + {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(mapped_torque_variation)}, {"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"} ] } @@ -565,7 +594,7 @@ class BioyondReactionStation(BioyondWorkstation): } def _simplify_report(self, report) -> Dict[str, Any]: - """简化实验报告,只保留关键信息,去除冗余的工作流参数""" + """简化实验报告,只保留关键信息,去除冗余的工作流参数""" if not isinstance(report, dict): return report @@ -588,7 +617,7 @@ class BioyondReactionStation(BioyondWorkstation): 'statusName': data.get('statusName'), } - # 提取物料信息(简化版) + # 提取物料信息(简化版) pre_intakes = data.get('preIntakes', []) if pre_intakes and isinstance(pre_intakes, list): first_intake = pre_intakes[0] @@ -801,7 +830,7 @@ class BioyondReactionStation(BioyondWorkstation): check_interval = int(check_interval) if check_interval else 10 if not batch_create_result or batch_create_result == "": raise ValueError( - "batch_create_result参数为空,请确保:\n" + "batch_create_result参数为空,请确保:\n" "1. batch_create节点与wait节点之间正确连接了handle\n" "2. batch_create节点成功执行并返回了结果\n" "3. 检查上游batch_create任务是否成功创建了订单" @@ -822,10 +851,10 @@ class BioyondReactionStation(BioyondWorkstation): raise ValueError(f"解析batch_create_result失败: {e}") if not order_codes or not order_ids: raise ValueError( - "batch_create_result中未找到order_codes或order_ids,或者为空。\n" + "batch_create_result中未找到order_codes或order_ids,或者为空。\n" "可能的原因:\n" - "1. batch_create任务执行失败(检查任务是否报错)\n" - "2. 物料配置问题(如'物料样品板分配失败')\n" + "1. batch_create任务执行失败(检查任务是否报错)\n" + "2. 物料配置问题(如'物料样品板分配失败')\n" "3. Bioyond系统状态异常\n" f"batch_create_result内容: {batch_create_result[:200]}..." ) @@ -882,7 +911,7 @@ class BioyondReactionStation(BioyondWorkstation): if not rep: rep = {"error": "无法获取报告"} else: - # 简化报告,去除冗余信息 + # 简化报告,去除冗余信息 rep = self._simplify_report(rep) reports.append({ "order_code": oc, @@ -962,9 +991,16 @@ class BioyondReactionStation(BioyondWorkstation): assign_material_name: 物料名称(试剂瓶位) time: 观察时间(分钟) torque_variation: 是否观察(int类型, 1=否, 2=是) - titration_type: 是否滴定(1=否, 2=是) - temperature: 温度设定(°C) + titration_type: 是否滴定(NO=1, YES=2) + temperature: 温度设定(C) """ + # 参数映射 + titration_map = {"NO": "1", "YES": "2", "1": "1", "2": "2"} + torque_map = {"NO": "1", "YES": "2", 1: "1", 2: "2", "1": "1", "2": "2"} + + mapped_titration_type = titration_map.get(str(titration_type), "1") + mapped_torque_variation = int(torque_map.get(str(torque_variation), "1")) + self.append_to_workflow_sequence('{"web_workflow_name": "liquid_feeding_beaker"}') material_id = self.hardware_interface._get_material_id_by_name(assign_material_name) if material_id is None: @@ -982,13 +1018,13 @@ class BioyondReactionStation(BioyondWorkstation): ACTION_NAMES["liquid_feeding_beaker"]["liquid"]: [ {"m": 0, "n": 2, "Key": "volume", "Value": volume}, {"m": 0, "n": 2, "Key": "assignMaterialName", "Value": material_id}, - {"m": 0, "n": 2, "Key": "titrationType", "Value": titration_type} + {"m": 0, "n": 2, "Key": "titrationType", "Value": mapped_titration_type} ] }, observe_step_id: { ACTION_NAMES["liquid_feeding_beaker"]["observe"]: [ {"m": 1, "n": 0, "Key": "time", "Value": time}, - {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)}, + {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(mapped_torque_variation)}, {"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"} ] } @@ -1014,11 +1050,18 @@ class BioyondReactionStation(BioyondWorkstation): Args: assign_material_name: 物料名称(液体种类) volume: 分液量(μL) - titration_type: 是否滴定(1=否, 2=是) + titration_type: 是否滴定(NO=1, YES=2) time: 观察时间(分钟) - torque_variation: 是否观察(int类型, 1=否, 2=是) - temperature: 温度(°C) + torque_variation: 是否观察(NO=1, YES=2) + temperature: 温度(C) """ + # 参数映射 + titration_map = {"NO": "1", "YES": "2", "1": "1", "2": "2"} + torque_map = {"NO": "1", "YES": "2", 1: "1", 2: "2", "1": "1", "2": "2"} + + mapped_titration_type = titration_map.get(str(titration_type), "1") + mapped_torque_variation = int(torque_map.get(str(torque_variation), "1")) + self.append_to_workflow_sequence('{"web_workflow_name": "drip_back"}') material_id = self.hardware_interface._get_material_id_by_name(assign_material_name) if material_id is None: @@ -1034,7 +1077,7 @@ class BioyondReactionStation(BioyondWorkstation): "param_values": { liquid_step_id: { ACTION_NAMES["drip_back"]["liquid"]: [ - {"m": 0, "n": 1, "Key": "titrationType", "Value": titration_type}, + {"m": 0, "n": 1, "Key": "titrationType", "Value": mapped_titration_type}, {"m": 0, "n": 1, "Key": "assignMaterialName", "Value": material_id}, {"m": 0, "n": 1, "Key": "volume", "Value": volume} ] @@ -1042,7 +1085,7 @@ class BioyondReactionStation(BioyondWorkstation): observe_step_id: { ACTION_NAMES["drip_back"]["observe"]: [ {"m": 1, "n": 0, "Key": "time", "Value": time}, - {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(torque_variation)}, + {"m": 1, "n": 0, "Key": "torqueVariation", "Value": str(mapped_torque_variation)}, {"m": 1, "n": 0, "Key": "temperature", "Value": f"{temperature:.2f}"} ] } @@ -1068,14 +1111,21 @@ class BioyondReactionStation(BioyondWorkstation): duration: 时间(秒) start_step_key: 起点步骤Key (可选, 默认为空则自动选择) end_step_key: 终点步骤Key (可选, 默认为空则自动选择) - start_point: 起点计时点 (0=开始前, 1=结束后) - end_point: 终点计时点 (0=开始前, 1=结束后) + start_point: 起点计时点 (Start=0, End=1) + end_point: 终点计时点 (Start=0, End=1) """ - # 注意:此方法应在添加完起点工作流后,添加终点工作流前调用 + # 参数映射 + point_map = {"Start": 0, "End": 1, 0: 0, 1: 1, "0": 0, "1": 1} + + mapped_start_point = point_map.get(start_point, 0) + mapped_end_point = point_map.get(end_point, 0) + + # 注意:此方法应在添加完起点工作流后,添加终点工作流前调用 + current_count = len(self._cached_workflow_sequence) if current_count == 0: - print("⚠️ 无法添加时间约束:当前没有工作流") + print("⚠️ 无法添加时间约束:当前没有工作流") return start_index = current_count - 1 @@ -1087,8 +1137,8 @@ class BioyondReactionStation(BioyondWorkstation): "end_index": end_index, "end_step_key": end_step_key, "duration": duration, - "start_point": start_point, - "end_point": end_point + "start_point": mapped_start_point, + "end_point": mapped_end_point } self.pending_time_constraints.append(constraint) print(f"已添加时间约束: Workflow[{start_index}].{start_step_key} -> Workflow[{end_index}].{end_step_key} ({duration}s)") @@ -1104,7 +1154,7 @@ class BioyondReactionStation(BioyondWorkstation): """ id_to_name = {workflow_id: name for name, workflow_id in self.workflow_mappings.items()} workflow_names = [] - # 使用内部缓存的列表,而不是属性(属性返回 JSON 字符串) + # 使用内部缓存的列表,而不是属性(属性返回 JSON 字符串) for workflow_id in self._cached_workflow_sequence: workflow_name = id_to_name.get(workflow_id, workflow_id) workflow_names.append(workflow_name) @@ -1113,7 +1163,7 @@ class BioyondReactionStation(BioyondWorkstation): def sync_workflow_sequence_from_bioyond(self) -> dict: """从 Bioyond 系统同步工作流序列 - 查询 Bioyond 系统中的工作流列表,并更新本地 workflow_sequence + 查询 Bioyond 系统中的工作流列表,并更新本地 workflow_sequence Returns: dict: 包含同步结果的字典 @@ -1142,7 +1192,7 @@ class BioyondReactionStation(BioyondWorkstation): print(f"[同步工作流序列] 查询返回数据: {workflows_data}") if not workflows_data: - error_msg = "未能从 Bioyond 系统获取工作流数据(返回为空)" + error_msg = "未能从 Bioyond 系统获取工作流数据(返回为空)" print(f"⚠️ [同步工作流序列] {error_msg}") return { "success": False, @@ -1150,7 +1200,7 @@ class BioyondReactionStation(BioyondWorkstation): "message": error_msg } - # 获取工作流列表 - Bioyond API 返回的字段是 items,不是 list + # 获取工作流列表 - Bioyond API 返回的字段是 items,不是 list workflow_list = workflows_data.get("items", workflows_data.get("list", [])) print(f"[同步工作流序列] 从 Bioyond 查询到 {len(workflow_list)} 个工作流") @@ -1243,7 +1293,7 @@ class BioyondReactionStation(BioyondWorkstation): def hard_delete_merged_workflows(self, workflow_ids: List[str]) -> Dict[str, Any]: """ - 调用新接口:硬删除合并后的工作流 + 调用新接口:硬删除合并后的工作流 Args: workflow_ids: 要删除的工作流ID数组 @@ -1265,11 +1315,11 @@ class BioyondReactionStation(BioyondWorkstation): """项目接口通用POST调用 参数: - endpoint: 接口路径(例如 /api/lims/order/skip-titration-steps) + endpoint: 接口路径(例如 /api/lims/order/skip-titration-steps) data: 请求体中的 data 字段内容 返回: - dict: 服务端响应,失败时返回 {code:0,message,...} + dict: 服务端响应,失败时返回 {code:0,message,...} """ request_data = { "apiKey": API_CONFIG["api_key"], @@ -1305,11 +1355,11 @@ class BioyondReactionStation(BioyondWorkstation): """项目接口通用DELETE调用 参数: - endpoint: 接口路径(例如 /api/lims/order/workflows) + endpoint: 接口路径(例如 /api/lims/order/workflows) data: 请求体中的 data 字段内容 返回: - dict: 服务端响应,失败时返回 {code:0,message,...} + dict: 服务端响应,失败时返回 {code:0,message,...} """ request_data = { "apiKey": API_CONFIG["api_key"], @@ -1359,16 +1409,16 @@ class BioyondReactionStation(BioyondWorkstation): for name in web_workflow_list: workflow_id = self.workflow_mappings.get(name, "") if not workflow_id: - print(f"警告:未找到工作流名称 {name} 对应的 ID") + print(f"警告:未找到工作流名称 {name} 对应的 ID") continue workflows_result.append({"id": workflow_id, "name": name}) print(f"process_web_workflows 输出: {workflows_result}") return workflows_result except json.JSONDecodeError as e: - print(f"错误:无法解析 web_workflow_json: {e}") + print(f"错误:无法解析 web_workflow_json: {e}") return [] except Exception as e: - print(f"错误:处理工作流失败: {e}") + print(f"错误:处理工作流失败: {e}") return [] def _build_workflows_with_parameters(self, workflows_result: list) -> list: @@ -1376,7 +1426,7 @@ class BioyondReactionStation(BioyondWorkstation): 构建带参数的工作流列表 Args: - workflows_result: 处理后的工作流列表(应为包含 id 和 name 的字典列表) + workflows_result: 处理后的工作流列表(应为包含 id 和 name 的字典列表) Returns: 符合新接口格式的工作流参数结构 @@ -1388,24 +1438,24 @@ class BioyondReactionStation(BioyondWorkstation): for idx, workflow_info in enumerate(workflows_result): if not isinstance(workflow_info, dict): - print(f"错误:workflows_result[{idx}] 不是字典,而是 {type(workflow_info)}: {workflow_info}") + print(f"错误:workflows_result[{idx}] 不是字典,而是 {type(workflow_info)}: {workflow_info}") continue workflow_id = workflow_info.get("id") if not workflow_id: - print(f"警告:workflows_result[{idx}] 缺少 'id' 键") + print(f"警告:workflows_result[{idx}] 缺少 'id' 键") continue workflow_name = workflow_info.get("name", "") # print(f"\n🔧 处理工作流 [{idx}]: {workflow_name} (ID: {workflow_id})") if idx >= len(self.pending_task_params): - # print(f" ⚠️ 无对应参数,跳过") + # print(f" ⚠️ 无对应参数,跳过") workflows_with_params.append({"id": workflow_id}) continue param_data = self.pending_task_params[idx] param_values = param_data.get("param_values", {}) if not param_values: - # print(f" ⚠️ 参数为空,跳过") + # print(f" ⚠️ 参数为空,跳过") workflows_with_params.append({"id": workflow_id}) continue @@ -1464,10 +1514,10 @@ class BioyondReactionStation(BioyondWorkstation): def merge_workflow_with_parameters(self, json_str: str) -> dict: """ - 调用新接口:合并工作流并传递参数 + 调用新接口:合并工作流并传递参数 Args: - json_str: JSON格式的字符串,包含: + json_str: JSON格式的字符串,包含: - name: 工作流名称 - workflows: [{"id": "工作流ID", "stepParameters": {...}}] @@ -1477,7 +1527,7 @@ class BioyondReactionStation(BioyondWorkstation): try: data = json.loads(json_str) - # 在工作流名称后面添加时间戳,避免重复 + # 在工作流名称后面添加时间戳,避免重复 if "name" in data and data["name"]: timestamp = self.hardware_interface.get_current_time_iso8601().replace(":", "-").replace(".", "-") original_name = data["name"] @@ -1524,7 +1574,7 @@ class BioyondReactionStation(BioyondWorkstation): return None if result.get("code") == 1: - print(f"✅ 工作流合并成功(带参数)") + print(f"✅ 工作流合并成功(带参数)") return result.get("data", {}) else: error_msg = result.get('message', '未知错误') @@ -1545,7 +1595,7 @@ class BioyondReactionStation(BioyondWorkstation): return None def _validate_and_refresh_workflow_if_needed(self, workflow_name: str) -> bool: - """验证工作流ID是否有效,如果无效则重新合并 + """验证工作流ID是否有效,如果无效则重新合并 Args: workflow_name: 工作流名称 @@ -1555,7 +1605,7 @@ class BioyondReactionStation(BioyondWorkstation): """ print(f"\n🔍 验证工作流ID有效性...") if not self._cached_workflow_sequence: - print(f" ⚠️ 工作流序列为空,需要重新合并") + print(f" ⚠️ 工作流序列为空,需要重新合并") return False first_workflow_id = self._cached_workflow_sequence[0] try: @@ -1564,7 +1614,7 @@ class BioyondReactionStation(BioyondWorkstation): print(f" ✅ 工作流ID有效") return True else: - print(f" ⚠️ 工作流ID已过期,需要重新合并") + print(f" ⚠️ 工作流ID已过期,需要重新合并") return False except Exception as e: print(f" ❌ 工作流ID验证失败: {e}") @@ -1573,7 +1623,7 @@ class BioyondReactionStation(BioyondWorkstation): def process_and_execute_workflow(self, workflow_name: str, task_name: str) -> dict: """ - 一站式处理工作流程:解析网页工作流列表,合并工作流(带参数),然后发布任务 + 一站式处理工作流程:解析网页工作流列表,合并工作流(带参数),然后发布任务 Args: workflow_name: 合并后的工作流名称 @@ -1630,7 +1680,7 @@ class BioyondReactionStation(BioyondWorkstation): start_wf_name = workflow_names_by_index[start_idx] end_wf_name = workflow_names_by_index[end_idx] - # 辅助函数:根据名称查找 config 中的 key + # 辅助函数:根据名称查找 config 中的 key def find_config_key(name): # 1. 直接匹配 if name in WORKFLOW_STEP_IDS: @@ -1702,7 +1752,7 @@ class BioyondReactionStation(BioyondWorkstation): "tcmBs": tcm_bs_list } - # print(f"\n🔄 合并工作流(带参数),名称: {workflow_name}") + # print(f"\n🔄 合并工作流(带参数),名称: {workflow_name}") merged_workflow = self.merge_workflow_with_parameters(json.dumps(merge_data)) if not merged_workflow: @@ -1719,28 +1769,28 @@ class BioyondReactionStation(BioyondWorkstation): "paramValues": {} }] - # 尝试创建订单:无论成功或失败,都需要在本次尝试结束后清理本地队列,避免下一次重复累积 + # 尝试创建订单:无论成功或失败,都需要在本次尝试结束后清理本地队列,避免下一次重复累积 try: result = self.create_order(json.dumps(order_params)) if not result: - # 返回错误结果之前先记录情况(稍后由 finally 清理队列) - print("⚠️ 创建任务返回空或失败响应,稍后将清理本地队列以避免重复累积") + # 返回错误结果之前先记录情况(稍后由 finally 清理队列) + print("⚠️ 创建任务返回空或失败响应,稍后将清理本地队列以避免重复累积") return self._create_error_result("创建任务失败", "create_order") finally: - # 无论任务创建成功与否,都要清空本地保存的参数和工作流序列,防止下次重复 + # 无论任务创建成功与否,都要清空本地保存的参数和工作流序列,防止下次重复 try: self.pending_task_params = [] - self.clear_workflows() # 清空工作流序列,避免重复累积 + self.clear_workflows() # 清空工作流序列,避免重复累积 print("✅ 已清理 pending_task_params 与 workflow_sequence") except Exception as _ex: - # 记录清理失败,但不要阻塞原始返回 + # 记录清理失败,但不要阻塞原始返回 print(f"❌ 清理队列时发生异常: {_ex}") # print(f"\n✅ 任务创建成功: {result}") # print(f"\n✅ 任务创建成功") print(f"{'='*60}\n") - # 返回结果,包含合并后的工作流数据和订单参数 + # 返回结果,包含合并后的工作流数据和订单参数 return json.dumps({ "success": True, "result": result, @@ -1757,7 +1807,7 @@ class BioyondReactionStation(BioyondWorkstation): preintake_id: 通量ID Returns: - Dict[str, Any]: 服务器响应,包含状态码、消息和时间戳 + Dict[str, Any]: 服务器响应,包含状态码,消息和时间戳 """ try: return self._post_project_api("/api/lims/order/skip-titration-steps", preintake_id) @@ -1774,7 +1824,7 @@ class BioyondReactionStation(BioyondWorkstation): temperature: 目标温度 (°C) Returns: - str: JSON 字符串,格式为 {"suc": True/False, "msg": "描述信息"} + str: JSON 字符串,格式为 {"suc": True/False, "msg": "描述信息"} """ if reactor_id not in range(1, 6): return json.dumps({"suc": False, "msg": "反应器编号必须在 1-5 之间"}) @@ -1793,6 +1843,6 @@ class BioyondReactionStation(BioyondWorkstation): if resp.status_code == 200: return json.dumps({"suc": True, "msg": "温度设置成功"}) else: - return json.dumps({"suc": False, "msg": f"温度设置失败,HTTP {resp.status_code}"}) + return json.dumps({"suc": False, "msg": f"温度设置失败,HTTP {resp.status_code}"}) except Exception as e: return json.dumps({"suc": False, "msg": f"温度设置异常: {str(e)}"}) diff --git a/unilabos/registry/devices/bioyond_dispensing_station.yaml b/unilabos/registry/devices/bioyond_dispensing_station.yaml index c979173..f2b866f 100644 --- a/unilabos/registry/devices/bioyond_dispensing_station.yaml +++ b/unilabos/registry/devices/bioyond_dispensing_station.yaml @@ -5,200 +5,6 @@ bioyond_dispensing_station: - bioyond_dispensing_station class: action_value_mappings: - auto-brief_step_parameters: - feedback: {} - goal: {} - goal_default: - data: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - data: - type: object - required: - - data - type: object - result: {} - required: - - goal - title: brief_step_parameters参数 - type: object - type: UniLabJsonCommand - auto-compute_experiment_design: - feedback: {} - goal: {} - goal_default: - m_tot: '70' - ratio: null - titration_percent: '0.03' - wt_percent: '0.25' - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - m_tot: - default: '70' - type: string - ratio: - type: object - titration_percent: - default: '0.03' - type: string - wt_percent: - default: '0.25' - type: string - required: - - ratio - type: object - result: - properties: - feeding_order: - items: {} - title: Feeding Order - type: array - return_info: - title: Return Info - type: string - solutions: - items: {} - title: Solutions - type: array - solvents: - additionalProperties: true - title: Solvents - type: object - titration: - additionalProperties: true - title: Titration - type: object - required: - - solutions - - titration - - solvents - - feeding_order - - return_info - title: ComputeExperimentDesignReturn - type: object - required: - - goal - title: compute_experiment_design参数 - type: object - type: UniLabJsonCommand - auto-process_order_finish_report: - feedback: {} - goal: {} - goal_default: - report_request: null - used_materials: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - report_request: - type: string - used_materials: - type: string - required: - - report_request - - used_materials - type: object - result: {} - required: - - goal - title: process_order_finish_report参数 - type: object - type: UniLabJsonCommand - auto-project_order_report: - feedback: {} - goal: {} - goal_default: - order_id: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - order_id: - type: string - required: - - order_id - type: object - result: {} - required: - - goal - title: project_order_report参数 - type: object - type: UniLabJsonCommand - auto-query_resource_by_name: - feedback: {} - goal: {} - goal_default: - material_name: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - material_name: - type: string - required: - - material_name - type: object - result: {} - required: - - goal - title: query_resource_by_name参数 - type: object - type: UniLabJsonCommand - auto-workflow_sample_locations: - feedback: {} - goal: {} - goal_default: - workflow_id: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - workflow_id: - type: string - required: - - workflow_id - type: object - result: {} - required: - - goal - title: workflow_sample_locations参数 - type: object - type: UniLabJsonCommand batch_create_90_10_vial_feeding_tasks: feedback: {} goal: @@ -745,7 +551,11 @@ bioyond_dispensing_station: description: 目标库位(手动输入,如"A01") type: string target_stack: - description: 目标堆栈名称(手动输入,如"堆栈1左") + description: 目标堆栈名称(从列表选择) + enum: + - 堆栈1左 + - 堆栈1右 + - 站内试剂存放堆栈 type: string required: - materials diff --git a/unilabos/registry/devices/reaction_station_bioyond.yaml b/unilabos/registry/devices/reaction_station_bioyond.yaml index ec1f250..ec3487b 100644 --- a/unilabos/registry/devices/reaction_station_bioyond.yaml +++ b/unilabos/registry/devices/reaction_station_bioyond.yaml @@ -30,16 +30,22 @@ reaction_station.bioyond: description: 时间(秒) type: integer end_point: - default: 0 - description: 终点计时点 (0=开始前, 1=结束后) - type: integer + default: Start + description: 终点计时点 (Start=开始前, End=结束后) + enum: + - Start + - End + type: string end_step_key: description: 终点步骤Key (可选, 默认为空则自动选择) type: string start_point: - default: 0 - description: 起点计时点 (0=开始前, 1=结束后) - type: integer + default: Start + description: 起点计时点 (Start=开始前, End=结束后) + enum: + - Start + - End + type: string start_step_key: description: 起点步骤Key (例如 "feeding", "liquid", 可选, 默认为空则自动选择) type: string @@ -52,253 +58,6 @@ reaction_station.bioyond: title: add_time_constraint参数 type: object type: UniLabJsonCommand - auto-create_order: - feedback: {} - goal: {} - goal_default: - json_str: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - json_str: - type: string - required: - - json_str - type: object - result: {} - required: - - goal - title: create_order参数 - type: object - type: UniLabJsonCommand - auto-hard_delete_merged_workflows: - feedback: {} - goal: {} - goal_default: - workflow_ids: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - workflow_ids: - items: - type: string - type: array - required: - - workflow_ids - type: object - result: {} - required: - - goal - title: hard_delete_merged_workflows参数 - type: object - type: UniLabJsonCommand - auto-merge_workflow_with_parameters: - feedback: {} - goal: {} - goal_default: - json_str: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - json_str: - type: string - required: - - json_str - type: object - result: {} - required: - - goal - title: merge_workflow_with_parameters参数 - type: object - type: UniLabJsonCommand - auto-process_temperature_cutoff_report: - feedback: {} - goal: {} - goal_default: - report_request: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - report_request: - type: string - required: - - report_request - type: object - result: {} - required: - - goal - title: process_temperature_cutoff_report参数 - type: object - type: UniLabJsonCommand - auto-process_web_workflows: - feedback: {} - goal: {} - goal_default: - web_workflow_json: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - web_workflow_json: - type: string - required: - - web_workflow_json - type: object - result: {} - required: - - goal - title: process_web_workflows参数 - type: object - type: UniLabJsonCommand - auto-skip_titration_steps: - feedback: {} - goal: {} - goal_default: - preintake_id: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - preintake_id: - type: string - required: - - preintake_id - type: object - result: {} - required: - - goal - title: skip_titration_steps参数 - type: object - type: UniLabJsonCommand - auto-sync_workflow_sequence_from_bioyond: - feedback: {} - goal: {} - goal_default: {} - handles: {} - placeholder_keys: {} - result: {} - schema: - description: 从Bioyond系统同步工作流序列 - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: - properties: - message: - type: string - success: - type: boolean - workflows: - items: - properties: - id: - type: string - name: - type: string - status: - type: string - type: object - type: array - type: object - required: - - goal - title: sync_workflow_sequence_from_bioyond参数 - type: object - type: UniLabJsonCommand - auto-wait_for_multiple_orders_and_get_reports: - feedback: {} - goal: {} - goal_default: - batch_create_result: null - check_interval: 10 - timeout: 7200 - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - batch_create_result: - type: string - check_interval: - default: 10 - type: integer - timeout: - default: 7200 - type: integer - required: [] - type: object - result: {} - required: - - goal - title: wait_for_multiple_orders_and_get_reports参数 - type: object - type: UniLabJsonCommand - auto-workflow_step_query: - feedback: {} - goal: {} - goal_default: - workflow_id: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - workflow_id: - type: string - required: - - workflow_id - type: object - result: {} - required: - - goal - title: workflow_step_query参数 - type: object - type: UniLabJsonCommand drip_back: feedback: {} goal: @@ -333,13 +92,19 @@ reaction_station.bioyond: description: 观察时间(分钟) type: string titration_type: - description: 是否滴定(1=否, 2=是) + description: 是否滴定(NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string torque_variation: - description: 是否观察 (1=否, 2=是) + description: 是否观察 (NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string volume: - description: 分液公式(μL) + description: 分液公式(mL) type: string required: - volume @@ -439,13 +204,19 @@ reaction_station.bioyond: description: 观察时间(分钟) type: string titration_type: - description: 是否滴定(1=否, 2=是) + description: 是否滴定(NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string torque_variation: - description: 是否观察 (1=否, 2=是) + description: 是否观察 (NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string volume: - description: 分液公式(μL) + description: 分液公式(mL) type: string required: - volume @@ -489,7 +260,7 @@ reaction_station.bioyond: label: Solvents Data From Calculation Node result: {} schema: - description: 液体投料-溶剂。可以直接提供volume(μL),或通过solvents对象自动从additional_solvent(mL)计算volume。 + description: 液体投料-溶剂。可以直接提供volume(mL),或通过solvents对象自动从additional_solvent(mL)计算volume。 properties: feedback: {} goal: @@ -509,15 +280,21 @@ reaction_station.bioyond: description: 观察时间(分钟),默认360 type: string titration_type: - default: '1' - description: 是否滴定(1=否, 2=是),默认1 + default: 'NO' + description: 是否滴定(NO=否, YES=是),默认NO + enum: + - 'NO' + - 'YES' type: string torque_variation: - default: '2' - description: 是否观察 (1=否, 2=是),默认2 + default: 'YES' + description: 是否观察 (NO=否, YES=是),默认YES + enum: + - 'NO' + - 'YES' type: string volume: - description: 分液量(μL)。可直接提供,或通过solvents参数自动计算 + description: 分液量(mL)。可直接提供,或通过solvents参数自动计算 type: string required: - assign_material_name @@ -590,15 +367,21 @@ reaction_station.bioyond: description: 观察时间(分钟),默认90 type: string titration_type: - default: '2' - description: 是否滴定(1=否, 2=是),默认2 + default: 'YES' + description: 是否滴定(NO=否, YES=是),默认YES + enum: + - 'NO' + - 'YES' type: string torque_variation: - default: '2' - description: 是否观察 (1=否, 2=是),默认2 + default: 'YES' + description: 是否观察 (NO=否, YES=是),默认YES + enum: + - 'NO' + - 'YES' type: string volume_formula: - description: 分液公式(μL)。可直接提供固定公式,或留空由系统根据x_value、feeding_order_data、extracted_actuals自动生成 + description: 分液公式(mL)。可直接提供固定公式,或留空由系统根据x_value、feeding_order_data、extracted_actuals自动生成 type: string x_value: description: 公式中的x值,手工输入,格式为"{{1-2-3}}"(包含双花括号)。用于自动公式计算 @@ -646,13 +429,19 @@ reaction_station.bioyond: description: 观察时间(分钟) type: string titration_type: - description: 是否滴定(1=否, 2=是) + description: 是否滴定(NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string torque_variation: - description: 是否观察 (1=否, 2=是) + description: 是否观察 (NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string volume_formula: - description: 分液公式(μL) + description: 分液公式(mL) type: string required: - volume_formula @@ -821,7 +610,11 @@ reaction_station.bioyond: description: 物料名称(用于获取试剂瓶位ID) type: string material_id: - description: 粉末类型ID,1=盐(21分钟),2=面粉(27分钟),3=BTDA(38分钟) + description: 粉末类型ID,Salt=盐(21分钟),Flour=面粉(27分钟),BTDA=BTDA(38分钟) + enum: + - Salt + - Flour + - BTDA type: string temperature: description: 温度设定(°C) @@ -830,7 +623,10 @@ reaction_station.bioyond: description: 观察时间(分钟) type: string torque_variation: - description: 是否观察 (1=否, 2=是) + description: 是否观察 (NO=否, YES=是) + enum: + - 'NO' + - 'YES' type: string required: - assign_material_name @@ -890,32 +686,7 @@ reaction_station.reactor: - reactor - reaction_station_bioyond class: - action_value_mappings: - auto-update_metrics: - feedback: {} - goal: {} - goal_default: - payload: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - payload: - type: object - required: - - payload - type: object - result: {} - required: - - goal - title: update_metrics参数 - type: object - type: UniLabJsonCommand + action_value_mappings: {} module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactor status_types: average_viscosity: Float64