From 0fba4cf2752853f403da4ee55a7d4faddd6deaa1 Mon Sep 17 00:00:00 2001 From: calvincao Date: Thu, 30 Oct 2025 19:56:34 +0800 Subject: [PATCH 1/7] =?UTF-8?q?feat(unilabos):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E9=85=8D=E7=BD=AE=E5=92=8C=E8=B5=84=E6=BA=90?= =?UTF-8?q?=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改了 bioyond_cell.yaml 中的 xlsx_path 路径分隔符为反斜杠- 在 bioyond_cell.yaml 中新增多个自动命令定义,包括创建物料、处理报告和调度重置等功能- 修改 coin_cell_assembly.py 中 func_pack_send_msg_cmd 函数签名并调整调用参数 - 新增 qiming_coin_cell_code 方法用于设置启明扣电配置参数 - 更新 coin_cell_assembly_a.csv 文件中的寄存器描述和新增压制模式及清洁忽略选项- 修改 bioyond_studio 配置文件中的默认 API 主机地址 - 更新 new_cellconfig3c.json 中的设备类名为 coincellassemblyworkstation_device- 删除 reaction_station_bioyond.yaml 的全部内容,仅保留空对象 -重新组织 YB_bottle.yaml 和 YB_bottle_carriers.yaml 中的资源分类和命名定义 --- .../bioyond_cell/2025092701.xlsx | Bin 9287 -> 18159 bytes .../bioyond_cell/样品导入模板.xlsx | Bin 9686 -> 22129 bytes .../workstation/bioyond_studio/config.py | 2 +- .../coin_cell_assembly/coin_cell_assembly.py | 27 +- .../coin_cell_assembly_a.csv | 5 +- .../coin_cell_assembly/new_cellconfig3c.json | 2 +- unilabos/registry/devices/bioyond_cell.yaml | 216 +- .../devices/coin_cell_workstation.yaml | 576 ++ .../devices/reaction_station_bioyond.yaml | 528 +- unilabos/registry/devices/work_station.yaml | 6698 ----------------- .../registry/resources/bioyond/YB_bottle.yaml | 106 +- .../resources/bioyond/YB_bottle_carriers.yaml | 218 +- 12 files changed, 977 insertions(+), 7401 deletions(-) create mode 100644 unilabos/registry/devices/coin_cell_workstation.yaml delete mode 100644 unilabos/registry/devices/work_station.yaml diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx index 3ce894f1e5033c9a9bbfc0b61024bce1ce3fbf49..d1e9d2f31a2385b1028209891e31c975ebc9744b 100644 GIT binary patch literal 18159 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 literal 9287 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 Q03G%Tg6(;#X@C3mf3EfhZ2$lO diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/样品导入模板.xlsx b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/样品导入模板.xlsx index e6addeb9855082c3ba401dbff45cc4aecf81e8ca..0d8d0e297d6ff5c2d304bc438c499a27da375b01 100644 GIT binary patch literal 22129 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 literal 9686 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 xmGVzK06Qxi diff --git a/unilabos/devices/workstation/bioyond_studio/config.py b/unilabos/devices/workstation/bioyond_studio/config.py index 504cf459..52feff6f 100644 --- a/unilabos/devices/workstation/bioyond_studio/config.py +++ b/unilabos/devices/workstation/bioyond_studio/config.py @@ -8,7 +8,7 @@ import os # BioyondCellWorkstation 默认配置(包含所有必需参数) API_CONFIG = { # API 连接配置 - "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.10.169:44388"), + "api_host": os.getenv("BIOYOND_API_HOST", "http://172.21.32.103:44388"), "api_key": os.getenv("BIOYOND_API_KEY", "8A819E5C"), "timeout": int(os.getenv("BIOYOND_TIMEOUT", "30")), diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py index cf06f704..e520628e 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py @@ -766,7 +766,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): # self.success = True # return self.success - def func_pack_send_msg_cmd(self, elec_use_num, elec_vol, assembly_type, assembly_pressure) -> bool: + def func_pack_send_msg_cmd(self, elec_use_num, elec_vol, assembly_type) -> bool: """UNILAB写参数""" while (self.request_rec_msg_status) == False: print("wait for request_rec_msg_status to True") @@ -782,9 +782,6 @@ class CoinCellAssemblyWorkstation(WorkstationBase): #发送电解液组装类型 self._unilab_send_msg_assembly_type(assembly_type) time.sleep(1) - #发送电池压制力 - self._unilab_send_msg_assembly_pressure(assembly_pressure) - time.sleep(1) self._unilab_send_msg_succ_cmd(True) time.sleep(1) while (self.request_rec_msg_status) == True: @@ -892,10 +889,22 @@ class CoinCellAssemblyWorkstation(WorkstationBase): self.success = True return self.success + def qiming_coin_cell_code(self, fujipian_panshu:int, fujipian_juzhendianwei:int=0, gemopanshu:int=0, gemo_juzhendianwei:int=0, lvbodian:bool=True, battery_pressure_mode:bool=True, battery_pressure:int=4000, battery_clean_ignore:bool=False) -> bool: + self.success = False + self.client.use_node('REG_MSG_NE_PLATE_NUM').write(fujipian_panshu) + self.client.use_node('REG_MSG_NE_PLATE_MATRIX').write(fujipian_juzhendianwei) + self.client.use_node('REG_MSG_SEPARATOR_PLATE_NUM').write(gemopanshu) + self.client.use_node('REG_MSG_SEPARATOR_PLATE_MATRIX').write(gemo_juzhendianwei) + self.client.use_node('COIL_ALUMINUM_FOIL').write(lvbodian) + self.client.use_node('REG_MSG_PRESS_MODE').write(battery_pressure_mode) + # self.client.use_node('REG_MSG_ASSEMBLY_PRESSURE').write(battery_pressure) + self.client.use_node('REG_MSG_BATTERY_CLEAN_IGNORE').write(battery_clean_ignore) + self.success = True + + return self.success - - def func_allpack_cmd(self, elec_num, elec_use_num, elec_vol:int=50, assembly_type:int=7, assembly_pressure:int=4200, file_path: str="D:\\coin_cell_data") -> bool: - elec_num, elec_use_num, elec_vol, assembly_type, assembly_pressure = int(elec_num), int(elec_use_num), int(elec_vol), int(assembly_type), int(assembly_pressure) + def func_allpack_cmd(self, elec_num, elec_use_num, elec_vol:int=50, assembly_type:int=7, file_path: str="D:\\coin_cell_data") -> bool: + elec_num, elec_use_num, elec_vol, assembly_type = int(elec_num), int(elec_use_num), int(elec_vol), int(assembly_type) summary_csv_file = os.path.join(file_path, "duandian.csv") # 如果断点文件存在,先读取之前的进度 if os.path.exists(summary_csv_file): @@ -945,7 +954,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): print(f"开始第{last_i+i+1}瓶电解液的组装") #第一个循环从上次断点继续,后续循环从0开始 j_start = last_j if i == last_i else 0 - self.func_pack_send_msg_cmd(elec_use_num-j_start, elec_vol, assembly_type, assembly_pressure) + self.func_pack_send_msg_cmd(elec_use_num-j_start, elec_vol, assembly_type) for j in range(j_start, elec_use_num): print(f"开始第{last_i+i+1}瓶电解液的第{j+j_start+1}个电池组装") @@ -1217,8 +1226,10 @@ class CoinCellAssemblyWorkstation(WorkstationBase): ''' + if __name__ == "__main__": # 简单测试 workstation = CoinCellAssemblyWorkstation() + workstation.qiming_coin_cell_code(fujipian_panshu=1, fujipian_juzhendianwei=2, gemopanshu=3, gemo_juzhendianwei=4, lvbodian=False, battery_pressure_mode=False, battery_pressure=4200, battery_clean_ignore=False) print(f"工作站创建成功: {workstation.deck.name}") print(f"料盘数量: {len(workstation.deck.children)}") diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv index bb66a7a7..98f90383 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly_a.csv @@ -43,21 +43,22 @@ REG_DATA_ELECTROLYTE_USE_NUM,INT16,,,,hold_register,10000, UNILAB_SEND_FINISHED_CMD,BOOL,,,,coil,8730, UNILAB_RECE_FINISHED_CMD,BOOL,,,,coil,8530, REG_DATA_ASSEMBLY_TYPE,INT16,,,,hold_register,10018,ASSEMBLY_TYPE7or8 -COIL_ALUMINUM_FOIL,BOOL,,,,coil,8340, +COIL_ALUMINUM_FOIL,BOOL,,使用铝箔垫,,coil,8340, REG_MSG_NE_PLATE_MATRIX,INT16,,负极片矩阵点位,,hold_register,440, REG_MSG_SEPARATOR_PLATE_MATRIX,INT16,,隔膜矩阵点位,,hold_register,450, REG_MSG_TIP_BOX_MATRIX,INT16,,移液枪头矩阵点位,,hold_register,480, REG_MSG_NE_PLATE_NUM,INT16,,负极片盘数,,hold_register,443, REG_MSG_SEPARATOR_PLATE_NUM,INT16,,隔膜盘数,,hold_register,453, +REG_MSG_PRESS_MODE,BOOL,,压制模式(false:压力检测模式,True:距离模式),,coil,8360,电池压制模式 ,,,,,,, ,BOOL,,视觉对位(false:使用,true:忽略),,coil,8300,视觉对位 ,BOOL,,复检(false:使用,true:忽略),,coil,8310,视觉复检 ,BOOL,,手套箱_左仓(false:使用,true:忽略),,coil,8320,手套箱左仓 ,BOOL,,手套箱_右仓(false:使用,true:忽略),,coil,8420,手套箱右仓 ,BOOL,,真空检知(false:使用,true:忽略),,coil,8350,真空检知 -,BOOL,,压制模式(false:压力检测模式,True:距离模式),,coil,8360,电池压制模式 ,BOOL,,电解液添加模式(false:单次滴液,true:二次滴液),,coil,8370,滴液模式 ,BOOL,,正极片称重(false:使用,true:忽略),,coil,8380,正极片称重 ,BOOL,,正负极片组装方式(false:正装,true:倒装),,coil,8390,正负极反装 ,BOOL,,压制清洁(false:使用,true:忽略),,coil,8400,压制清洁 ,BOOL,,物料盘摆盘方式(false:水平摆盘,true:堆叠摆盘),,coil,8410,负极片摆盘方式 +REG_MSG_BATTERY_CLEAN_IGNORE,BOOL,,忽略电池清洁(false:使用,true:忽略),,coil,8460, \ No newline at end of file diff --git a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json index c96e6b2b..36190858 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json +++ b/unilabos/devices/workstation/coin_cell_assembly/new_cellconfig3c.json @@ -22,7 +22,7 @@ ], "parent": null, "type": "device", - "class": "bettery_station_registry", + "class": "coincellassemblyworkstation_device", "position": { "x": 600, "y": 400, diff --git a/unilabos/registry/devices/bioyond_cell.yaml b/unilabos/registry/devices/bioyond_cell.yaml index c50a1a80..a54e7021 100644 --- a/unilabos/registry/devices/bioyond_cell.yaml +++ b/unilabos/registry/devices/bioyond_cell.yaml @@ -137,7 +137,7 @@ bioyond_cell: WH4_x5_y1_z1_5_quantity: 0.0 WH4_x5_y2_z1_10_materialName: '' WH4_x5_y2_z1_10_quantity: 0.0 - xlsx_path: unilabos/devices/workstation/bioyond_cell/样品导入模板.xlsx + xlsx_path: unilabos\devices\workstation\bioyond_studio\bioyond_cell\样品导入模板.xlsx handles: {} placeholder_keys: {} result: {} @@ -463,7 +463,7 @@ bioyond_cell: default: 0.0 type: number xlsx_path: - default: unilabos/devices/workstation/bioyond_cell/样品导入模板.xlsx + default: unilabos\devices\workstation\bioyond_studio\bioyond_cell\样品导入模板.xlsx type: string required: [] type: object @@ -498,6 +498,63 @@ bioyond_cell: title: auto_feeding4to3_from_xlsx参数 type: object type: UniLabJsonCommand + auto-create_and_inbound_materials: + feedback: {} + goal: {} + goal_default: + material_names: null + type_id: 3a190ca0-b2f6-9aeb-8067-547e72c11469 + warehouse_name: 粉末加样头堆栈 + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + material_names: + type: string + type_id: + default: 3a190ca0-b2f6-9aeb-8067-547e72c11469 + type: string + warehouse_name: + default: 粉末加样头堆栈 + type: string + required: [] + type: object + result: {} + required: + - goal + title: create_and_inbound_materials参数 + type: object + type: UniLabJsonCommand + auto-create_materials: + feedback: {} + goal: {} + goal_default: + mappings: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + mappings: + type: object + required: + - mappings + type: object + result: {} + required: + - goal + title: create_materials参数 + type: object + type: UniLabJsonCommand auto-create_orders: feedback: {} goal: {} @@ -576,6 +633,84 @@ bioyond_cell: title: order_list_v2参数 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 + type: object + result: {} + required: + - goal + title: process_order_finish_report参数 + type: object + type: UniLabJsonCommand + auto-process_sample_finish_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_sample_finish_report参数 + type: object + type: UniLabJsonCommand + auto-process_step_finish_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_step_finish_report参数 + type: object + type: UniLabJsonCommand auto-report_material_change: feedback: {} goal: {} @@ -622,6 +757,27 @@ bioyond_cell: title: scheduler_continue参数 type: object type: UniLabJsonCommand + auto-scheduler_reset: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: scheduler_reset参数 + type: object + type: UniLabJsonCommand auto-scheduler_start: feedback: {} goal: {} @@ -778,6 +934,62 @@ bioyond_cell: title: transfer_3_to_2_to_1参数 type: object type: UniLabJsonCommand + auto-update_push_ip: + feedback: {} + goal: {} + goal_default: + ip: null + port: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ip: + type: string + port: + type: string + required: [] + type: object + result: {} + required: + - goal + title: update_push_ip参数 + type: object + type: UniLabJsonCommand + auto-wait_for_order_finish: + feedback: {} + goal: {} + goal_default: + order_code: null + timeout: 1800 + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + order_code: + type: string + timeout: + default: 1800 + type: integer + required: + - order_code + type: object + result: {} + required: + - goal + title: wait_for_order_finish参数 + type: object + type: UniLabJsonCommand auto-wait_for_transfer_task: feedback: {} goal: {} diff --git a/unilabos/registry/devices/coin_cell_workstation.yaml b/unilabos/registry/devices/coin_cell_workstation.yaml new file mode 100644 index 00000000..7e541eb8 --- /dev/null +++ b/unilabos/registry/devices/coin_cell_workstation.yaml @@ -0,0 +1,576 @@ +coincellassemblyworkstation_device: + category: + - coin_cell_workstation + class: + action_value_mappings: + auto-change_hole_sheet_to_2: + feedback: {} + goal: {} + goal_default: + hole: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + hole: + type: object + required: + - hole + type: object + result: {} + required: + - goal + title: change_hole_sheet_to_2参数 + type: object + type: UniLabJsonCommandAsync + auto-fill_plate: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: fill_plate参数 + type: object + type: UniLabJsonCommandAsync + auto-fun_wuliao_test: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: fun_wuliao_test参数 + type: object + type: UniLabJsonCommand + auto-func_allpack_cmd: + feedback: {} + goal: {} + goal_default: + assembly_type: 7 + elec_num: null + elec_use_num: null + elec_vol: 50 + file_path: D:\coin_cell_data + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + assembly_type: + default: 7 + type: integer + elec_num: + type: string + elec_use_num: + type: string + elec_vol: + default: 50 + type: integer + file_path: + default: D:\coin_cell_data + type: string + required: + - elec_num + - elec_use_num + type: object + result: {} + required: + - goal + title: func_allpack_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_get_csv_export_status: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_get_csv_export_status参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_auto: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_auto参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_init: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_init参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_start: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_start参数 + type: object + type: UniLabJsonCommand + auto-func_pack_device_stop: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_device_stop参数 + type: object + type: UniLabJsonCommand + auto-func_pack_get_msg_cmd: + feedback: {} + goal: {} + goal_default: + file_path: D:\coin_cell_data + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + default: D:\coin_cell_data + type: string + required: [] + type: object + result: {} + required: + - goal + title: func_pack_get_msg_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_pack_send_bottle_num: + feedback: {} + goal: {} + goal_default: + bottle_num: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + bottle_num: + type: string + required: + - bottle_num + type: object + result: {} + required: + - goal + title: func_pack_send_bottle_num参数 + type: object + type: UniLabJsonCommand + auto-func_pack_send_finished_cmd: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_pack_send_finished_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_pack_send_msg_cmd: + feedback: {} + goal: {} + goal_default: + assembly_type: null + elec_use_num: null + elec_vol: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + assembly_type: + type: string + elec_use_num: + type: string + elec_vol: + type: string + required: + - elec_use_num + - elec_vol + - assembly_type + type: object + result: {} + required: + - goal + title: func_pack_send_msg_cmd参数 + type: object + type: UniLabJsonCommand + auto-func_read_data_and_output: + feedback: {} + goal: {} + goal_default: + file_path: D:\coin_cell_data + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + file_path: + default: D:\coin_cell_data + type: string + required: [] + type: object + result: {} + required: + - goal + title: func_read_data_and_output参数 + type: object + type: UniLabJsonCommand + auto-func_stop_read_data: + feedback: {} + goal: {} + goal_default: {} + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: {} + required: [] + type: object + result: {} + required: + - goal + title: func_stop_read_data参数 + type: object + type: UniLabJsonCommand + auto-modify_deck_name: + feedback: {} + goal: {} + goal_default: + resource_name: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + resource_name: + type: string + required: + - resource_name + type: object + result: {} + required: + - goal + title: modify_deck_name参数 + type: object + type: UniLabJsonCommand + auto-post_init: + feedback: {} + goal: {} + goal_default: + ros_node: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + ros_node: + type: object + required: + - ros_node + type: object + result: {} + required: + - goal + title: post_init参数 + type: object + type: UniLabJsonCommand + auto-qiming_coin_cell_code: + feedback: {} + goal: {} + goal_default: + battery_clean_ignore: false + battery_pressure: 4000 + battery_pressure_mode: true + fujipian_juzhendianwei: 0 + fujipian_panshu: null + gemo_juzhendianwei: 0 + gemopanshu: 0 + lvbodian: true + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + battery_clean_ignore: + default: false + type: boolean + battery_pressure: + default: 4000 + type: integer + battery_pressure_mode: + default: true + type: boolean + fujipian_juzhendianwei: + default: 0 + type: integer + fujipian_panshu: + type: integer + gemo_juzhendianwei: + default: 0 + type: integer + gemopanshu: + default: 0 + type: integer + lvbodian: + default: true + type: boolean + required: + - fujipian_panshu + type: object + result: {} + required: + - goal + title: qiming_coin_cell_code参数 + type: object + type: UniLabJsonCommand + module: unilabos.devices.workstation.coin_cell_assembly.coin_cell_assembly:CoinCellAssemblyWorkstation + status_types: + data_assembly_coin_cell_num: int + data_assembly_pressure: int + data_assembly_time: float + data_axis_x_pos: float + data_axis_y_pos: float + data_axis_z_pos: float + data_coin_cell_code: str + data_coin_num: int + data_electrolyte_code: str + data_electrolyte_volume: int + data_glove_box_o2_content: float + data_glove_box_pressure: float + data_glove_box_water_content: float + data_open_circuit_voltage: float + data_pole_weight: float + request_rec_msg_status: bool + request_send_msg_status: bool + sys_mode: str + sys_status: str + type: python + config_info: [] + description: '' + handles: [] + icon: '' + init_param_schema: + config: + properties: + address: + default: 172.21.32.111 + type: string + debug_mode: + default: false + type: boolean + deck: + type: object + port: + default: '502' + type: string + required: [] + type: object + data: + properties: + data_assembly_coin_cell_num: + type: integer + data_assembly_pressure: + type: integer + data_assembly_time: + type: number + data_axis_x_pos: + type: number + data_axis_y_pos: + type: number + data_axis_z_pos: + type: number + data_coin_cell_code: + type: string + data_coin_num: + type: integer + data_electrolyte_code: + type: string + data_electrolyte_volume: + type: integer + data_glove_box_o2_content: + type: number + data_glove_box_pressure: + type: number + data_glove_box_water_content: + type: number + data_open_circuit_voltage: + type: number + data_pole_weight: + type: number + request_rec_msg_status: + type: boolean + request_send_msg_status: + type: boolean + sys_mode: + type: string + sys_status: + type: string + required: + - sys_status + - sys_mode + - request_rec_msg_status + - request_send_msg_status + - data_assembly_coin_cell_num + - data_assembly_time + - data_open_circuit_voltage + - data_axis_x_pos + - data_axis_y_pos + - data_axis_z_pos + - data_pole_weight + - data_assembly_pressure + - data_electrolyte_volume + - data_coin_num + - data_coin_cell_code + - data_electrolyte_code + - data_glove_box_pressure + - data_glove_box_o2_content + - data_glove_box_water_content + type: object + registry_type: device + version: 1.0.0 diff --git a/unilabos/registry/devices/reaction_station_bioyond.yaml b/unilabos/registry/devices/reaction_station_bioyond.yaml index f1a16ec2..0967ef42 100644 --- a/unilabos/registry/devices/reaction_station_bioyond.yaml +++ b/unilabos/registry/devices/reaction_station_bioyond.yaml @@ -1,527 +1 @@ -reaction_station.bioyond: - category: - - work_station - - reaction_station_bioyond - class: - action_value_mappings: - auto-post_init: - feedback: {} - goal: {} - goal_default: - ros_node: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - ros_node: - type: object - required: - - ros_node - type: object - result: {} - required: - - goal - title: post_init参数 - type: object - type: UniLabJsonCommand - auto-process_web_workflows: - feedback: {} - goal: {} - goal_default: - json_str: null - handles: {} - placeholder_keys: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - json_str: - type: string - volume: - description: 分液公式(μL) - type: string - required: - - volume - - assign_material_name - - time - - torque_variation - - titration_type - - temperature - type: object - result: {} - required: - - goal - title: drip_back参数 - type: object - type: UniLabJsonCommand - drip_back: - feedback: {} - goal: - assign_material_name: assign_material_name - temperature: temperature - time: time - titration_type: titration_type - torque_variation: torque_variation - volume: volume - goal_default: - assign_material_name: '' - temperature: '' - time: '' - titration_type: '' - torque_variation: '' - volume: '' - handles: {} - result: {} - schema: - description: 滴回去 - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称(不能为空) - type: string - temperature: - description: 温度设定(°C) - type: string - time: - description: 观察时间(分钟) - type: string - required: - - file_path - type: object - result: {} - required: - - goal - title: load_bioyond_data_from_file参数 - type: object - type: UniLabJsonCommand - liquid_feeding_beaker: - feedback: {} - goal: - assign_material_name: assign_material_name - temperature: temperature - time: time - titration_type: titration_type - torque_variation: torque_variation - volume: volume - goal_default: - assign_material_name: '' - temperature: '' - time: '' - titration_type: '' - torque_variation: '' - volume: '' - handles: {} - result: {} - schema: - description: 液体进料烧杯 - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称 - type: string - temperature: - description: 温度设定(°C) - type: string - time: - description: 观察时间(分钟) - type: string - titration_type: - description: 是否滴定(1=否, 2=是) - type: string - torque_variation: - description: 是否观察 (1=否, 2=是) - type: string - volume: - description: 分液公式(μL) - type: string - required: - - volume - - assign_material_name - - time - - torque_variation - - titration_type - - temperature - type: object - result: {} - required: - - goal - title: liquid_feeding_beaker参数 - type: object - type: UniLabJsonCommand - liquid_feeding_solvents: - feedback: {} - goal: - assign_material_name: assign_material_name - solvents: solvents - temperature: temperature - time: time - titration_type: titration_type - torque_variation: torque_variation - volume: volume - goal_default: - assign_material_name: '' - solvents: '' - temperature: '25.00' - time: '360' - titration_type: '1' - torque_variation: '2' - volume: '' - handles: - input: - - data_key: solvents - data_source: handle - data_type: object - handler_key: solvents - io_type: source - label: Solvents Data From Calculation Node - result: {} - schema: - description: 液体投料-溶剂。可以直接提供volume(μL),或通过solvents对象自动从additional_solvent(mL)计算volume。 - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称 - type: string - solvents: - description: '溶剂信息对象(可选),包含: additional_solvent(溶剂体积mL), total_liquid_volume(总液体体积mL)。如果提供,将自动计算volume' - type: string - temperature: - default: '25.00' - description: 温度设定(°C),默认25.00 - type: string - time: - default: '360' - description: 观察时间(分钟),默认360 - type: string - titration_type: - default: '1' - description: 是否滴定(1=否, 2=是),默认1 - type: string - torque_variation: - default: '2' - description: 是否观察 (1=否, 2=是),默认2 - type: string - volume: - description: 分液量(μL)。可直接提供,或通过solvents参数自动计算 - type: string - required: - - assign_material_name - type: object - result: {} - required: - - goal - title: liquid_feeding_solvents参数 - type: object - type: UniLabJsonCommand - liquid_feeding_titration: - feedback: {} - goal: - assign_material_name: assign_material_name - temperature: temperature - time: time - titration_type: titration_type - torque_variation: torque_variation - volume_formula: volume_formula - goal_default: - assign_material_name: '' - temperature: '' - time: '' - titration_type: '' - torque_variation: '' - volume_formula: '' - handles: {} - result: {} - schema: - description: 液体进料(滴定) - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称 - type: string - temperature: - description: 温度设定(°C) - type: string - time: - description: 观察时间(分钟) - type: string - titration_type: - description: 是否滴定(1=否, 2=是) - type: string - torque_variation: - description: 是否观察 (1=否, 2=是) - type: string - volume_formula: - description: 分液公式(μL) - type: string - required: - - volume_formula - - assign_material_name - - time - - torque_variation - - titration_type - - temperature - type: object - result: {} - required: - - goal - title: liquid_feeding_titration参数 - type: object - type: UniLabJsonCommand - liquid_feeding_vials_non_titration: - feedback: {} - goal: - assign_material_name: assign_material_name - temperature: temperature - time: time - titration_type: titration_type - torque_variation: torque_variation - volume_formula: volume_formula - goal_default: - assign_material_name: '' - temperature: '' - time: '' - titration_type: '' - torque_variation: '' - volume_formula: '' - handles: {} - result: {} - schema: - description: 液体进料小瓶(非滴定) - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称 - type: string - temperature: - description: 温度设定(°C) - type: string - time: - description: 观察时间(分钟) - type: string - titration_type: - description: 是否滴定(1=否, 2=是) - type: string - torque_variation: - description: 是否观察 (1=否, 2=是) - type: string - volume_formula: - description: 分液公式(μL) - type: string - required: - - volume_formula - - assign_material_name - - time - - torque_variation - - titration_type - - temperature - type: object - result: {} - required: - - goal - title: liquid_feeding_vials_non_titration参数 - type: object - type: UniLabJsonCommand - process_and_execute_workflow: - feedback: {} - goal: - task_name: task_name - workflow_name: workflow_name - goal_default: - task_name: '' - workflow_name: '' - handles: {} - result: {} - schema: - description: 处理并执行工作流 - properties: - feedback: {} - goal: - properties: - task_name: - description: 任务名称 - type: string - workflow_name: - description: 工作流名称 - type: string - required: - - workflow_name - - task_name - type: object - result: {} - required: - - goal - title: process_and_execute_workflow参数 - type: object - type: UniLabJsonCommand - reactor_taken_in: - feedback: {} - goal: - assign_material_name: assign_material_name - cutoff: cutoff - temperature: temperature - goal_default: - assign_material_name: '' - cutoff: '' - temperature: '' - handles: {} - result: {} - schema: - description: 反应器放入 - 将反应器放入工作站,配置物料名称、粘度上限和温度参数 - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称 - type: string - cutoff: - description: 粘度上限 - type: string - temperature: - description: 温度设定(°C) - type: string - required: - - cutoff - - temperature - - assign_material_name - type: object - result: {} - required: - - goal - title: reactor_taken_in参数 - type: object - type: UniLabJsonCommand - reactor_taken_out: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: 反应器取出 - 从工作站中取出反应器,无需参数的简单操作 - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: - properties: - code: - description: 操作结果代码(1表示成功,0表示失败) - type: integer - return_info: - description: 操作结果详细信息 - type: string - type: object - required: - - goal - title: reactor_taken_out参数 - type: object - type: UniLabJsonCommand - solid_feeding_vials: - feedback: {} - goal: - assign_material_name: assign_material_name - material_id: material_id - temperature: temperature - time: time - torque_variation: torque_variation - goal_default: - assign_material_name: '' - material_id: '' - temperature: '' - time: '' - torque_variation: '' - handles: {} - result: {} - schema: - description: 固体进料小瓶 - 通过小瓶向反应器中添加固体物料,支持多种粉末类型(盐、面粉、BTDA) - properties: - feedback: {} - goal: - properties: - assign_material_name: - description: 物料名称(用于获取试剂瓶位ID) - type: string - material_id: - description: 粉末类型ID,1=盐(21分钟),2=面粉(27分钟),3=BTDA(38分钟) - type: string - temperature: - description: 温度设定(°C) - type: string - time: - description: 观察时间(分钟) - type: string - torque_variation: - description: 是否观察 (1=否, 2=是) - type: string - required: - - assign_material_name - - material_id - - time - - torque_variation - - temperature - type: object - result: {} - required: - - goal - title: solid_feeding_vials参数 - type: object - type: UniLabJsonCommand - module: unilabos.devices.workstation.bioyond_studio.reaction_station:BioyondReactionStation - protocol_type: [] - status_types: - all_workflows: dict - bioyond_status: dict - station_info: dict - workstation_status: dict - type: python - config_info: [] - description: Bioyond反应站 - handles: [] - icon: reaction_station.webp - init_param_schema: - config: - properties: - config: - type: object - deck: - type: string - required: [] - type: object - data: - properties: - all_workflows: - type: object - bioyond_status: - type: object - station_info: - type: object - workstation_status: - type: object - required: - - bioyond_status - - all_workflows - - station_info - - workstation_status - type: object - version: 1.0.0 +{} diff --git a/unilabos/registry/devices/work_station.yaml b/unilabos/registry/devices/work_station.yaml deleted file mode 100644 index ba076917..00000000 --- a/unilabos/registry/devices/work_station.yaml +++ /dev/null @@ -1,6698 +0,0 @@ -bettery_station_registry: - category: - - work_station - class: - action_value_mappings: - auto-change_hole_sheet_to_2: - feedback: {} - goal: {} - goal_default: - hole: null - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - hole: - type: object - required: - - hole - type: object - result: {} - required: - - goal - title: change_hole_sheet_to_2参数 - type: object - type: UniLabJsonCommandAsync - auto-fill_plate: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: fill_plate参数 - type: object - type: UniLabJsonCommandAsync - auto-fun_wuliao_test: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: fun_wuliao_test参数 - type: object - type: UniLabJsonCommand - auto-func_allpack_cmd: - feedback: {} - goal: {} - goal_default: - assembly_pressure: 4200 - assembly_type: 7 - elec_num: null - elec_use_num: null - elec_vol: 50 - file_path: D:\coin_cell_data - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - assembly_pressure: - default: 4200 - type: integer - assembly_type: - default: 7 - type: integer - elec_num: - type: string - elec_use_num: - type: string - elec_vol: - default: 50 - type: integer - file_path: - default: D:\coin_cell_data - type: string - required: - - elec_num - - elec_use_num - type: object - result: {} - required: - - goal - title: func_allpack_cmd参数 - type: object - type: UniLabJsonCommand - auto-func_get_csv_export_status: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_get_csv_export_status参数 - type: object - type: UniLabJsonCommand - auto-func_pack_device_auto: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_pack_device_auto参数 - type: object - type: UniLabJsonCommand - auto-func_pack_device_init: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_pack_device_init参数 - type: object - type: UniLabJsonCommand - auto-func_pack_device_start: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_pack_device_start参数 - type: object - type: UniLabJsonCommand - auto-func_pack_device_stop: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_pack_device_stop参数 - type: object - type: UniLabJsonCommand - auto-func_pack_get_msg_cmd: - feedback: {} - goal: {} - goal_default: - file_path: D:\coin_cell_data - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - file_path: - default: D:\coin_cell_data - type: string - required: [] - type: object - result: {} - required: - - goal - title: func_pack_get_msg_cmd参数 - type: object - type: UniLabJsonCommand - auto-func_pack_send_bottle_num: - feedback: {} - goal: {} - goal_default: - bottle_num: null - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - bottle_num: - type: string - required: - - bottle_num - type: object - result: {} - required: - - goal - title: func_pack_send_bottle_num参数 - type: object - type: UniLabJsonCommand - auto-func_pack_send_finished_cmd: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_pack_send_finished_cmd参数 - type: object - type: UniLabJsonCommand - auto-func_pack_send_msg_cmd: - feedback: {} - goal: {} - goal_default: - assembly_pressure: null - assembly_type: null - elec_use_num: null - elec_vol: null - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - assembly_pressure: - type: string - assembly_type: - type: string - elec_use_num: - type: string - elec_vol: - type: string - required: - - elec_use_num - - elec_vol - - assembly_type - - assembly_pressure - type: object - result: {} - required: - - goal - title: func_pack_send_msg_cmd参数 - type: object - type: UniLabJsonCommand - auto-func_read_data_and_output: - feedback: {} - goal: {} - goal_default: - file_path: D:\coin_cell_data - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - file_path: - default: D:\coin_cell_data - type: string - required: [] - type: object - result: {} - required: - - goal - title: func_read_data_and_output参数 - type: object - type: UniLabJsonCommand - auto-func_stop_read_data: - feedback: {} - goal: {} - goal_default: {} - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: {} - required: [] - type: object - result: {} - required: - - goal - title: func_stop_read_data参数 - type: object - type: UniLabJsonCommand - auto-modify_deck_name: - feedback: {} - goal: {} - goal_default: - resource_name: null - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - resource_name: - type: string - required: - - resource_name - type: object - result: {} - required: - - goal - title: modify_deck_name参数 - type: object - type: UniLabJsonCommand - auto-post_init: - feedback: {} - goal: {} - goal_default: - ros_node: null - handles: {} - result: {} - schema: - description: '' - properties: - feedback: {} - goal: - properties: - ros_node: - type: object - required: - - ros_node - type: object - result: {} - required: - - goal - title: post_init参数 - type: object - type: UniLabJsonCommand - module: unilabos.devices.workstation.coin_cell_assembly.coin_cell_assembly:CoinCellAssemblyWorkstation - status_types: - data_assembly_coin_cell_num: int - data_assembly_pressure: int - data_assembly_time: float - data_axis_x_pos: float - data_axis_y_pos: float - data_axis_z_pos: float - data_coin_cell_code: str - data_coin_num: int - data_electrolyte_code: str - data_electrolyte_volume: int - data_glove_box_o2_content: float - data_glove_box_pressure: float - data_glove_box_water_content: float - data_open_circuit_voltage: float - data_pole_weight: float - request_rec_msg_status: bool - request_send_msg_status: bool - sys_mode: str - sys_status: str - type: python - config_info: [] - description: '' - handles: [] - icon: '' - init_param_schema: - config: - properties: - address: - default: 192.168.1.20 - type: string - debug_mode: - default: true - type: boolean - port: - default: '502' - type: string - station_resource: - type: object - required: - - station_resource - type: object - data: - properties: - data_assembly_coin_cell_num: - type: integer - data_assembly_pressure: - type: integer - data_assembly_time: - type: number - data_axis_x_pos: - type: number - data_axis_y_pos: - type: number - data_axis_z_pos: - type: number - data_coin_cell_code: - type: string - data_coin_num: - type: integer - data_electrolyte_code: - type: string - data_electrolyte_volume: - type: integer - data_glove_box_o2_content: - type: number - data_glove_box_pressure: - type: number - data_glove_box_water_content: - type: number - data_open_circuit_voltage: - type: number - data_pole_weight: - type: number - request_rec_msg_status: - type: boolean - request_send_msg_status: - type: boolean - sys_mode: - type: string - sys_status: - type: string - required: - - sys_status - - sys_mode - - request_rec_msg_status - - request_send_msg_status - - data_assembly_coin_cell_num - - data_assembly_time - - data_open_circuit_voltage - - data_axis_x_pos - - data_axis_y_pos - - data_axis_z_pos - - data_pole_weight - - data_assembly_pressure - - data_electrolyte_volume - - data_coin_num - - data_coin_cell_code - - data_electrolyte_code - - data_glove_box_pressure - - data_glove_box_o2_content - - data_glove_box_water_content - type: object - version: 1.0.0 -workstation: - category: - - work_station - class: - action_value_mappings: - AGVTransferProtocol: - feedback: {} - goal: - from_repo: from_repo - from_repo_position: from_repo_position - to_repo: to_repo - to_repo_position: to_repo_position - goal_default: - from_repo: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - from_repo_position: '' - to_repo: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - to_repo_position: '' - handles: {} - result: {} - schema: - description: '' - properties: - feedback: - properties: - status: - type: string - required: - - status - title: AGVTransfer_Feedback - type: object - goal: - properties: - from_repo: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: from_repo - type: object - from_repo_position: - type: string - to_repo: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: to_repo - type: object - to_repo_position: - type: string - required: - - from_repo - - from_repo_position - - to_repo - - to_repo_position - title: AGVTransfer_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: AGVTransfer_Result - type: object - required: - - goal - title: AGVTransfer - type: object - type: AGVTransfer - AddProtocol: - feedback: {} - goal: - amount: amount - equiv: equiv - event: event - mass: mass - mol: mol - purpose: purpose - rate_spec: rate_spec - ratio: ratio - reagent: reagent - stir: stir - stir_speed: stir_speed - time: time - vessel: vessel - viscous: viscous - volume: volume - goal_default: - amount: '' - equiv: '' - event: '' - mass: '' - mol: '' - purpose: '' - rate_spec: '' - ratio: '' - reagent: '' - stir: false - stir_speed: 0.0 - time: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - viscous: false - volume: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: reagent - data_source: handle - data_type: resource - handler_key: reagent - label: Reagent - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_status: - type: string - progress: - type: number - required: - - progress - - current_status - title: Add_Feedback - type: object - goal: - properties: - amount: - type: string - equiv: - type: string - event: - type: string - mass: - type: string - mol: - type: string - purpose: - type: string - rate_spec: - type: string - ratio: - type: string - reagent: - type: string - stir: - type: boolean - stir_speed: - type: number - time: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - viscous: - type: boolean - volume: - type: string - required: - - vessel - - reagent - - volume - - mass - - amount - - time - - stir - - stir_speed - - viscous - - purpose - - event - - mol - - rate_spec - - equiv - - ratio - title: Add_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Add_Result - type: object - required: - - goal - title: Add - type: object - type: Add - AdjustPHProtocol: - feedback: {} - goal: - ph_value: ph_value - reagent: reagent - settling_time: settling_time - stir: stir - stir_speed: stir_speed - stir_time: stir_time - vessel: vessel - volume: volume - goal_default: - ph_value: 0.0 - reagent: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: reagent - data_source: handle - data_type: resource - handler_key: reagent - label: Reagent - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: AdjustPH_Feedback - type: object - goal: - properties: - ph_value: - type: number - reagent: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - ph_value - - reagent - title: AdjustPH_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: AdjustPH_Result - type: object - required: - - goal - title: AdjustPH - type: object - type: AdjustPH - CentrifugeProtocol: - feedback: {} - goal: - speed: speed - temp: temp - time: time - vessel: vessel - goal_default: - speed: 0.0 - temp: 0.0 - time: 0.0 - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_speed: - type: number - current_status: - type: string - current_temp: - type: number - progress: - type: number - required: - - progress - - current_speed - - current_temp - - current_status - title: Centrifuge_Feedback - type: object - goal: - properties: - speed: - type: number - temp: - type: number - time: - type: number - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - speed - - time - - temp - title: Centrifuge_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Centrifuge_Result - type: object - required: - - goal - title: Centrifuge - type: object - type: Centrifuge - CleanProtocol: - feedback: {} - goal: - repeats: repeats - solvent: solvent - temp: temp - vessel: vessel - volume: volume - goal_default: - repeats: 0 - solvent: '' - temp: 0.0 - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: 0.0 - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Solvent - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_device: - type: string - status: - type: string - time_remaining: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_remaining - type: object - time_spent: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_spent - type: object - required: - - status - - current_device - - time_spent - - time_remaining - title: Clean_Feedback - type: object - goal: - properties: - repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - solvent: - type: string - temp: - type: number - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: number - required: - - vessel - - solvent - - volume - - temp - - repeats - title: Clean_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: Clean_Result - type: object - required: - - goal - title: Clean - type: object - type: Clean - CleanVesselProtocol: - feedback: {} - goal: - repeats: repeats - solvent: solvent - temp: temp - vessel: vessel - volume: volume - goal_default: - repeats: 0 - solvent: '' - temp: 0.0 - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: 0.0 - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Solvent - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: CleanVessel_Feedback - type: object - goal: - properties: - repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - solvent: - type: string - temp: - type: number - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: number - required: - - vessel - - solvent - - volume - - temp - - repeats - title: CleanVessel_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: CleanVessel_Result - type: object - required: - - goal - title: CleanVessel - type: object - type: CleanVessel - DissolveProtocol: - feedback: {} - goal: - amount: amount - event: event - mass: mass - mol: mol - reagent: reagent - solvent: solvent - stir_speed: stir_speed - temp: temp - time: time - vessel: vessel - volume: volume - goal_default: - amount: '' - event: '' - mass: '' - mol: '' - reagent: '' - solvent: '' - stir_speed: 0.0 - temp: '' - time: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Solvent - - data_key: reagent - data_source: handle - data_type: resource - handler_key: reagent - label: Reagent - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: Dissolve_Feedback - type: object - goal: - properties: - amount: - type: string - event: - type: string - mass: - type: string - mol: - type: string - reagent: - type: string - solvent: - type: string - stir_speed: - type: number - temp: - type: string - time: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: string - required: - - vessel - - solvent - - volume - - amount - - temp - - time - - stir_speed - - mass - - mol - - reagent - - event - title: Dissolve_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Dissolve_Result - type: object - required: - - goal - title: Dissolve - type: object - type: Dissolve - DryProtocol: - feedback: {} - goal: - compound: compound - vessel: vessel - goal_default: - compound: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: Dry_Feedback - type: object - goal: - properties: - compound: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - compound - - vessel - title: Dry_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Dry_Result - type: object - required: - - goal - title: Dry - type: object - type: Dry - EvacuateAndRefillProtocol: - feedback: {} - goal: - gas: gas - vessel: vessel - goal_default: - gas: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_device: - type: string - status: - type: string - time_remaining: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_remaining - type: object - time_spent: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_spent - type: object - required: - - status - - current_device - - time_spent - - time_remaining - title: EvacuateAndRefill_Feedback - type: object - goal: - properties: - gas: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - gas - title: EvacuateAndRefill_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: EvacuateAndRefill_Result - type: object - required: - - goal - title: EvacuateAndRefill - type: object - type: EvacuateAndRefill - EvaporateProtocol: - feedback: {} - goal: - pressure: pressure - solvent: solvent - stir_speed: stir_speed - temp: temp - time: time - vessel: vessel - goal_default: - pressure: 0.0 - solvent: '' - stir_speed: 0.0 - temp: 0.0 - time: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Evaporation Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Eluting Solvent - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: VesselOut - label: Evaporation Vessel - placeholder_keys: - vessel: unilabos_nodes - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_device: - type: string - status: - type: string - time_remaining: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_remaining - type: object - time_spent: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_spent - type: object - required: - - status - - current_device - - time_spent - - time_remaining - title: Evaporate_Feedback - type: object - goal: - properties: - pressure: - type: number - solvent: - type: string - stir_speed: - type: number - temp: - type: number - time: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - pressure - - temp - - time - - stir_speed - - solvent - title: Evaporate_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: Evaporate_Result - type: object - required: - - goal - title: Evaporate - type: object - type: Evaporate - FilterProtocol: - feedback: {} - goal: - continue_heatchill: continue_heatchill - filtrate_vessel: filtrate_vessel - stir: stir - stir_speed: stir_speed - temp: temp - vessel: vessel - volume: volume - goal_default: - continue_heatchill: false - filtrate_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - stir: false - stir_speed: 0.0 - temp: 0.0 - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: 0.0 - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: filtrate_vessel - data_source: handle - data_type: resource - handler_key: FiltrateVessel - label: Filtrate Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - - data_key: filtrate_vessel - data_source: executor - data_type: resource - handler_key: FiltrateOut - label: Filtrate Vessel - placeholder_keys: - filtrate_vessel: unilabos_resources - vessel: unilabos_nodes - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_status: - type: string - current_temp: - type: number - filtered_volume: - type: number - progress: - type: number - required: - - progress - - current_temp - - filtered_volume - - current_status - title: Filter_Feedback - type: object - goal: - properties: - continue_heatchill: - type: boolean - filtrate_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: filtrate_vessel - type: object - stir: - type: boolean - stir_speed: - type: number - temp: - type: number - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: number - required: - - vessel - - filtrate_vessel - - stir - - stir_speed - - temp - - continue_heatchill - - volume - title: Filter_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Filter_Result - type: object - required: - - goal - title: Filter - type: object - type: Filter - FilterThroughProtocol: - feedback: {} - goal: - eluting_repeats: eluting_repeats - eluting_solvent: eluting_solvent - eluting_volume: eluting_volume - filter_through: filter_through - from_vessel: from_vessel - residence_time: residence_time - to_vessel: to_vessel - goal_default: - eluting_repeats: 0 - eluting_solvent: '' - eluting_volume: 0.0 - filter_through: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - from_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - residence_time: 0.0 - to_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVessel - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVessel - label: To Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Eluting Solvent - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVesselOut - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVesselOut - label: To Vessel - placeholder_keys: - from_vessel: unilabos_resources - to_vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: FilterThrough_Feedback - type: object - goal: - properties: - eluting_repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - eluting_solvent: - type: string - eluting_volume: - type: number - filter_through: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: filter_through - type: object - from_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: from_vessel - type: object - residence_time: - type: number - to_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: to_vessel - type: object - required: - - from_vessel - - to_vessel - - filter_through - - eluting_solvent - - eluting_volume - - eluting_repeats - - residence_time - title: FilterThrough_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: FilterThrough_Result - type: object - required: - - goal - title: FilterThrough - type: object - type: FilterThrough - HeatChillProtocol: - feedback: {} - goal: - pressure: pressure - purpose: purpose - reflux_solvent: reflux_solvent - stir: stir - stir_speed: stir_speed - temp: temp - temp_spec: temp_spec - time: time - time_spec: time_spec - vessel: vessel - goal_default: - pressure: '' - purpose: '' - reflux_solvent: '' - stir: false - stir_speed: 0.0 - temp: 0.0 - temp_spec: '' - time: '' - time_spec: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - status: - type: string - required: - - status - title: HeatChill_Feedback - type: object - goal: - properties: - pressure: - type: string - purpose: - type: string - reflux_solvent: - type: string - stir: - type: boolean - stir_speed: - type: number - temp: - type: number - temp_spec: - type: string - time: - type: string - time_spec: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - temp - - time - - temp_spec - - time_spec - - pressure - - reflux_solvent - - stir - - stir_speed - - purpose - title: HeatChill_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: HeatChill_Result - type: object - required: - - goal - title: HeatChill - type: object - type: HeatChill - HeatChillStartProtocol: - feedback: {} - goal: - purpose: purpose - temp: temp - vessel: vessel - goal_default: - purpose: '' - temp: 0.0 - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - status: - type: string - required: - - status - title: HeatChillStart_Feedback - type: object - goal: - properties: - purpose: - type: string - temp: - type: number - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - temp - - purpose - title: HeatChillStart_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: HeatChillStart_Result - type: object - required: - - goal - title: HeatChillStart - type: object - type: HeatChillStart - HeatChillStopProtocol: - feedback: {} - goal: - vessel: vessel - goal_default: - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - status: - type: string - required: - - status - title: HeatChillStop_Feedback - type: object - goal: - properties: - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - title: HeatChillStop_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: HeatChillStop_Result - type: object - required: - - goal - title: HeatChillStop - type: object - type: HeatChillStop - HydrogenateProtocol: - feedback: {} - goal: - temp: temp - time: time - vessel: vessel - goal_default: - temp: '' - time: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: Hydrogenate_Feedback - type: object - goal: - properties: - temp: - type: string - time: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - temp - - time - - vessel - title: Hydrogenate_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Hydrogenate_Result - type: object - required: - - goal - title: Hydrogenate - type: object - type: Hydrogenate - PumpTransferProtocol: - feedback: {} - goal: - amount: amount - event: event - flowrate: flowrate - from_vessel: from_vessel - rate_spec: rate_spec - rinsing_repeats: rinsing_repeats - rinsing_solvent: rinsing_solvent - rinsing_volume: rinsing_volume - solid: solid - through: through - time: time - to_vessel: to_vessel - transfer_flowrate: transfer_flowrate - viscous: viscous - volume: volume - goal_default: - amount: '' - event: '' - flowrate: 0.0 - from_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - rate_spec: '' - rinsing_repeats: 0 - rinsing_solvent: '' - rinsing_volume: 0.0 - solid: false - through: '' - time: 0.0 - to_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - transfer_flowrate: 0.0 - viscous: false - volume: 0.0 - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVessel - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVessel - label: To Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Rinsing Solvent - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVesselOut - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVesselOut - label: To Vessel - placeholder_keys: - from_vessel: unilabos_nodes - to_vessel: unilabos_nodes - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_device: - type: string - status: - type: string - time_remaining: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_remaining - type: object - time_spent: - properties: - nanosec: - maximum: 4294967295 - minimum: 0 - type: integer - sec: - maximum: 2147483647 - minimum: -2147483648 - type: integer - required: - - sec - - nanosec - title: time_spent - type: object - required: - - status - - current_device - - time_spent - - time_remaining - title: PumpTransfer_Feedback - type: object - goal: - properties: - amount: - type: string - event: - type: string - flowrate: - type: number - from_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: from_vessel - type: object - rate_spec: - type: string - rinsing_repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - rinsing_solvent: - type: string - rinsing_volume: - type: number - solid: - type: boolean - through: - type: string - time: - type: number - to_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: to_vessel - type: object - transfer_flowrate: - type: number - viscous: - type: boolean - volume: - type: number - required: - - from_vessel - - to_vessel - - volume - - amount - - time - - viscous - - rinsing_solvent - - rinsing_volume - - rinsing_repeats - - solid - - flowrate - - transfer_flowrate - - rate_spec - - event - - through - title: PumpTransfer_Goal - type: object - result: - properties: - return_info: - type: string - success: - type: boolean - required: - - return_info - - success - title: PumpTransfer_Result - type: object - required: - - goal - title: PumpTransfer - type: object - type: PumpTransfer - RecrystallizeProtocol: - feedback: {} - goal: - ratio: ratio - solvent1: solvent1 - solvent2: solvent2 - vessel: vessel - volume: volume - goal_default: - ratio: '' - solvent1: '' - solvent2: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: solvent1 - data_source: handle - data_type: resource - handler_key: solvent1 - label: Solvent 1 - - data_key: solvent2 - data_source: handle - data_type: resource - handler_key: solvent2 - label: Solvent 2 - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: Recrystallize_Feedback - type: object - goal: - properties: - ratio: - type: string - solvent1: - type: string - solvent2: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: string - required: - - ratio - - solvent1 - - solvent2 - - vessel - - volume - title: Recrystallize_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Recrystallize_Result - type: object - required: - - goal - title: Recrystallize - type: object - type: Recrystallize - ResetHandlingProtocol: - feedback: {} - goal: - solvent: solvent - goal_default: - solvent: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Solvent - output: [] - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: ResetHandling_Feedback - type: object - goal: - properties: - solvent: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - solvent - - vessel - title: ResetHandling_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: ResetHandling_Result - type: object - required: - - goal - title: ResetHandling - type: object - type: ResetHandling - RunColumnProtocol: - feedback: {} - goal: - column: column - from_vessel: from_vessel - to_vessel: to_vessel - goal_default: - column: '' - from_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - pct1: '' - pct2: '' - ratio: '' - rf: '' - solvent1: '' - solvent2: '' - to_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVessel - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVessel - label: To Vessel - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVesselOut - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVesselOut - label: To Vessel - placeholder_keys: - column: unilabos_devices - from_vessel: unilabos_resources - to_vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: RunColumn_Feedback - type: object - goal: - properties: - column: - type: string - from_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: from_vessel - type: object - pct1: - type: string - pct2: - type: string - ratio: - type: string - rf: - type: string - solvent1: - type: string - solvent2: - type: string - to_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: to_vessel - type: object - required: - - from_vessel - - to_vessel - - column - - rf - - pct1 - - pct2 - - solvent1 - - solvent2 - - ratio - title: RunColumn_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: RunColumn_Result - type: object - required: - - goal - title: RunColumn - type: object - type: RunColumn - SeparateProtocol: - feedback: {} - goal: - from_vessel: from_vessel - product_phase: product_phase - purpose: purpose - repeats: repeats - separation_vessel: separation_vessel - settling_time: settling_time - solvent: solvent - solvent_volume: solvent_volume - stir_speed: stir_speed - stir_time: stir_time - through: through - to_vessel: to_vessel - waste_phase_to_vessel: waste_phase_to_vessel - goal_default: - from_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - product_phase: '' - product_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - purpose: '' - repeats: 0 - separation_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - settling_time: 0.0 - solvent: '' - solvent_volume: '' - stir_speed: 0.0 - stir_time: 0.0 - through: '' - to_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: '' - waste_phase_to_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - waste_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVessel - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVessel - label: To Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Solvent - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVesselOut - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVesselOut - label: To Vessel - placeholder_keys: - from_vessel: unilabos_resources - to_vessel: unilabos_resources - waste_phase_to_vessel: unilabos_resources - waste_vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: Separate_Feedback - type: object - goal: - properties: - from_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: from_vessel - type: object - product_phase: - type: string - product_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: product_vessel - type: object - purpose: - type: string - repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - separation_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: separation_vessel - type: object - settling_time: - type: number - solvent: - type: string - solvent_volume: - type: string - stir_speed: - type: number - stir_time: - type: number - through: - type: string - to_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: to_vessel - type: object - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: string - waste_phase_to_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: waste_phase_to_vessel - type: object - waste_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: waste_vessel - type: object - required: - - vessel - - purpose - - product_phase - - from_vessel - - separation_vessel - - to_vessel - - waste_phase_to_vessel - - product_vessel - - waste_vessel - - solvent - - solvent_volume - - volume - - through - - repeats - - stir_time - - stir_speed - - settling_time - title: Separate_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Separate_Result - type: object - required: - - goal - title: Separate - type: object - type: Separate - StartStirProtocol: - feedback: {} - goal: - purpose: purpose - stir_speed: stir_speed - vessel: vessel - goal_default: - purpose: '' - stir_speed: 0.0 - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_speed: - type: number - current_status: - type: string - progress: - type: number - required: - - progress - - current_speed - - current_status - title: StartStir_Feedback - type: object - goal: - properties: - purpose: - type: string - stir_speed: - type: number - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - stir_speed - - purpose - title: StartStir_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: StartStir_Result - type: object - required: - - goal - title: StartStir - type: object - type: StartStir - StirProtocol: - feedback: {} - goal: - event: event - settling_time: settling_time - stir_speed: stir_speed - stir_time: stir_time - time: time - time_spec: time_spec - vessel: vessel - goal_default: - event: '' - settling_time: '' - stir_speed: 0.0 - stir_time: 0.0 - time: '' - time_spec: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - status: - type: string - required: - - status - title: Stir_Feedback - type: object - goal: - properties: - event: - type: string - settling_time: - type: string - stir_speed: - type: number - stir_time: - type: number - time: - type: string - time_spec: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - - time - - event - - time_spec - - stir_time - - stir_speed - - settling_time - title: Stir_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Stir_Result - type: object - required: - - goal - title: Stir - type: object - type: Stir - StopStirProtocol: - feedback: {} - goal: - vessel: vessel - goal_default: - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - output: - - data_key: vessel - data_source: executor - data_type: resource - handler_key: VesselOut - label: Vessel - placeholder_keys: - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_status: - type: string - progress: - type: number - required: - - progress - - current_status - title: StopStir_Feedback - type: object - goal: - properties: - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - required: - - vessel - title: StopStir_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: StopStir_Result - type: object - required: - - goal - title: StopStir - type: object - type: StopStir - TransferProtocol: - feedback: {} - goal: - amount: amount - from_vessel: from_vessel - rinsing_repeats: rinsing_repeats - rinsing_solvent: rinsing_solvent - rinsing_volume: rinsing_volume - solid: solid - time: time - to_vessel: to_vessel - viscous: viscous - volume: volume - goal_default: - amount: '' - from_vessel: '' - rinsing_repeats: 0 - rinsing_solvent: '' - rinsing_volume: 0.0 - solid: false - time: 0.0 - to_vessel: '' - viscous: false - volume: 0.0 - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVessel - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVessel - label: To Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Rinsing Solvent - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: FromVesselOut - label: From Vessel - - data_key: vessel - data_source: executor - data_type: resource - handler_key: ToVesselOut - label: To Vessel - placeholder_keys: - from_vessel: unilabos_nodes - to_vessel: unilabos_nodes - result: {} - schema: - description: '' - properties: - feedback: - properties: - current_status: - type: string - progress: - type: number - transferred_volume: - type: number - required: - - progress - - transferred_volume - - current_status - title: Transfer_Feedback - type: object - goal: - properties: - amount: - type: string - from_vessel: - type: string - rinsing_repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - rinsing_solvent: - type: string - rinsing_volume: - type: number - solid: - type: boolean - time: - type: number - to_vessel: - type: string - viscous: - type: boolean - volume: - type: number - required: - - from_vessel - - to_vessel - - volume - - amount - - time - - viscous - - rinsing_solvent - - rinsing_volume - - rinsing_repeats - - solid - title: Transfer_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: Transfer_Result - type: object - required: - - goal - title: Transfer - type: object - type: Transfer - WashSolidProtocol: - feedback: {} - goal: - filtrate_vessel: filtrate_vessel - repeats: repeats - solvent: solvent - stir: stir - stir_speed: stir_speed - temp: temp - time: time - vessel: vessel - volume: volume - goal_default: - event: '' - filtrate_vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - mass: '' - repeats: 0 - repeats_spec: '' - solvent: '' - stir: false - stir_speed: 0.0 - temp: 0.0 - time: '' - vessel: - category: '' - children: [] - config: '' - data: '' - id: '' - name: '' - parent: '' - pose: - orientation: - w: 1.0 - x: 0.0 - y: 0.0 - z: 0.0 - position: - x: 0.0 - y: 0.0 - z: 0.0 - sample_id: '' - type: '' - volume: '' - volume_spec: '' - handles: - input: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: Vessel - label: Vessel - - data_key: solvent - data_source: handle - data_type: resource - handler_key: solvent - label: Solvent - - data_key: filtrate_vessel - data_source: handle - data_type: resource - handler_key: filtrate_vessel - label: Filtrate Vessel - output: - - data_key: vessel - data_source: handle - data_type: resource - handler_key: VesselOut - label: Vessel Out - - data_key: filtrate_vessel - data_source: executor - data_type: resource - handler_key: filtrate_vessel_out - label: Filtrate Vessel - placeholder_keys: - filtrate_vessel: unilabos_resources - vessel: unilabos_resources - result: {} - schema: - description: '' - properties: - feedback: - properties: - progress: - type: number - status: - type: string - required: - - status - - progress - title: WashSolid_Feedback - type: object - goal: - properties: - event: - type: string - filtrate_vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: filtrate_vessel - type: object - mass: - type: string - repeats: - maximum: 2147483647 - minimum: -2147483648 - type: integer - repeats_spec: - type: string - solvent: - type: string - stir: - type: boolean - stir_speed: - type: number - temp: - type: number - time: - type: string - vessel: - properties: - category: - type: string - children: - items: - type: string - type: array - config: - type: string - data: - type: string - id: - type: string - name: - type: string - parent: - type: string - pose: - properties: - orientation: - properties: - w: - type: number - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - - w - title: orientation - type: object - position: - properties: - x: - type: number - y: - type: number - z: - type: number - required: - - x - - y - - z - title: position - type: object - required: - - position - - orientation - title: pose - type: object - sample_id: - type: string - type: - type: string - required: - - id - - name - - sample_id - - children - - parent - - type - - category - - pose - - config - - data - title: vessel - type: object - volume: - type: string - volume_spec: - type: string - required: - - vessel - - solvent - - volume - - filtrate_vessel - - temp - - stir - - stir_speed - - time - - repeats - - volume_spec - - repeats_spec - - mass - - event - title: WashSolid_Goal - type: object - result: - properties: - message: - type: string - return_info: - type: string - success: - type: boolean - required: - - success - - message - - return_info - title: WashSolid_Result - type: object - required: - - goal - title: WashSolid - type: object - type: WashSolid - auto-create_ros_action_server: - feedback: {} - goal: {} - goal_default: - action_name: null - action_value_mapping: null - handles: {} - result: {} - schema: - description: create_ros_action_server的参数schema - properties: - feedback: {} - goal: - properties: - action_name: - type: string - action_value_mapping: - type: string - required: - - action_name - - action_value_mapping - type: object - result: {} - required: - - goal - title: create_ros_action_server参数 - type: object - type: UniLabJsonCommand - auto-execute_single_action: - feedback: {} - goal: {} - goal_default: - action_kwargs: null - action_name: null - device_id: null - handles: {} - result: {} - schema: - description: execute_single_action的参数schema - properties: - feedback: {} - goal: - properties: - action_kwargs: - type: string - action_name: - type: string - device_id: - type: string - required: - - device_id - - action_name - - action_kwargs - type: object - result: {} - required: - - goal - title: execute_single_action参数 - type: object - type: UniLabJsonCommandAsync - auto-initialize_device: - feedback: {} - goal: {} - goal_default: - device_config: null - device_id: null - handles: {} - result: {} - schema: - description: initialize_device的参数schema - properties: - feedback: {} - goal: - properties: - device_config: - type: string - device_id: - type: string - required: - - device_id - - device_config - type: object - result: {} - required: - - goal - title: initialize_device参数 - type: object - type: UniLabJsonCommand - module: unilabos.ros.nodes.presets.workstation:ROS2WorkstationNode - status_types: {} - type: ros2 - config_info: [] - description: Workstation - handles: [] - icon: '' - init_param_schema: - config: - properties: - action_value_mappings: - type: object - children: - type: object - device_id: - type: string - driver_instance: - type: string - hardware_interface: - type: object - print_publish: - default: true - type: string - protocol_type: - items: - type: string - type: array - resource_tracker: - type: string - status_types: - type: object - required: - - protocol_type - - children - - driver_instance - - device_id - - status_types - - action_value_mappings - - hardware_interface - type: object - data: - properties: {} - required: [] - type: object - version: 1.0.0 -workstation.example: - category: - - work_station - class: - action_value_mappings: {} - module: unilabos.devices.workstation.workstation_base:WorkstationExample - status_types: {} - type: python - config_info: [] - description: '' - handles: [] - icon: '' - init_param_schema: - config: - properties: - station_resource: - type: object - required: - - station_resource - type: object - data: - properties: {} - required: [] - type: object - version: 1.0.0 diff --git a/unilabos/registry/resources/bioyond/YB_bottle.yaml b/unilabos/registry/resources/bioyond/YB_bottle.yaml index 51ea6e48..6d8a380d 100644 --- a/unilabos/registry/resources/bioyond/YB_bottle.yaml +++ b/unilabos/registry/resources/bioyond/YB_bottle.yaml @@ -1,58 +1,7 @@ -YB_fen_ye_5ml_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottles:YB_fen_ye_5ml_Bottle - type: pylabrobot - description: YB_fen_ye_5ml_Bottle - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_fen_ye_20ml_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottles:YB_fen_ye_20ml_Bottle - type: pylabrobot - description: YB_fen_ye_20ml_Bottle - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_pei_ye_xiao_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottles:YB_pei_ye_xiao_Bottle - type: pylabrobot - description: YB_pei_ye_xiao_Bottle - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_pei_ye_da_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottles:YB_pei_ye_da_Bottle - type: pylabrobot - description: YB_pei_ye_da_Bottle - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - YB_Pipette_Tip: category: - yb3 + - YB_bottle class: module: unilabos.resources.bioyond.YB_bottles:YB_Pipette_Tip type: pylabrobot @@ -62,4 +11,55 @@ YB_Pipette_Tip: init_param_schema: {} registry_type: resource version: 1.0.0 - +YB_fen_ye_20ml_Bottle: + category: + - yb3 + - YB_bottle + class: + module: unilabos.resources.bioyond.YB_bottles:YB_fen_ye_20ml_Bottle + type: pylabrobot + description: YB_fen_ye_20ml_Bottle + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_fen_ye_5ml_Bottle: + category: + - yb3 + - YB_bottle + class: + module: unilabos.resources.bioyond.YB_bottles:YB_fen_ye_5ml_Bottle + type: pylabrobot + description: YB_fen_ye_5ml_Bottle + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_pei_ye_da_Bottle: + category: + - yb3 + - YB_bottle + class: + module: unilabos.resources.bioyond.YB_bottles:YB_pei_ye_da_Bottle + type: pylabrobot + description: YB_pei_ye_da_Bottle + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_pei_ye_xiao_Bottle: + category: + - yb3 + - YB_bottle + class: + module: unilabos.resources.bioyond.YB_bottles:YB_pei_ye_xiao_Bottle + type: pylabrobot + description: YB_pei_ye_xiao_Bottle + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 diff --git a/unilabos/registry/resources/bioyond/YB_bottle_carriers.yaml b/unilabos/registry/resources/bioyond/YB_bottle_carriers.yaml index 1c5b029e..eb2ff6fc 100644 --- a/unilabos/registry/resources/bioyond/YB_bottle_carriers.yaml +++ b/unilabos/registry/resources/bioyond/YB_bottle_carriers.yaml @@ -1,59 +1,7 @@ - -YB_6StockCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6StockCarrier - type: pylabrobot - description: YB_6StockCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_6VialCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6VialCarrier - type: pylabrobot - description: YB_6VialCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_1BottleCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_1BottleCarrier - type: pylabrobot - description: YB_1BottleCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_1GaoNianYeBottleCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_1GaoNianYeBottleCarrier - type: pylabrobot - description: YB_1GaoNianYeBottleCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - YB_1Bottle100mlCarrier: category: - yb3 + - YB_bottle_carriers class: module: unilabos.resources.bioyond.YB_bottle_carriers:YB_1Bottle100mlCarrier type: pylabrobot @@ -63,49 +11,36 @@ YB_1Bottle100mlCarrier: init_param_schema: {} registry_type: resource version: 1.0.0 - -YB_6x5ml_DispensingVialCarrier: +YB_1BottleCarrier: category: - yb3 + - YB_bottle_carriers class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6x5ml_DispensingVialCarrier + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_1BottleCarrier type: pylabrobot - description: YB_6x5ml_DispensingVialCarrier + description: YB_1BottleCarrier handles: [] icon: '' init_param_schema: {} registry_type: resource version: 1.0.0 - -YB_6x20ml_DispensingVialCarrier: +YB_1GaoNianYeBottleCarrier: category: - yb3 + - YB_bottle_carriers class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6x20ml_DispensingVialCarrier + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_1GaoNianYeBottleCarrier type: pylabrobot - description: YB_6x20ml_DispensingVialCarrier + description: YB_1GaoNianYeBottleCarrier handles: [] icon: '' init_param_schema: {} registry_type: resource version: 1.0.0 - -YB_6x_SmallSolutionBottleCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6x_SmallSolutionBottleCarrier - type: pylabrobot - description: YB_6x_SmallSolutionBottleCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - YB_4x_LargeSolutionBottleCarrier: category: - yb3 + - YB_bottle_carriers class: module: unilabos.resources.bioyond.YB_bottle_carriers:YB_4x_LargeSolutionBottleCarrier type: pylabrobot @@ -115,23 +50,75 @@ YB_4x_LargeSolutionBottleCarrier: init_param_schema: {} registry_type: resource version: 1.0.0 - -YB_jia_yang_tou_da_1X1_carrier: +YB_6StockCarrier: category: - yb3 + - YB_bottle_carriers class: - module: unilabos.resources.bioyond.YB_bottle_carriers:YB_jia_yang_tou_da_1X1_carrier + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6StockCarrier type: pylabrobot - description: YB_jia_yang_tou_da_1X1_carrier + description: YB_6StockCarrier + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_6VialCarrier: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6VialCarrier + type: pylabrobot + description: YB_6VialCarrier + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_6x20ml_DispensingVialCarrier: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6x20ml_DispensingVialCarrier + type: pylabrobot + description: YB_6x20ml_DispensingVialCarrier + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_6x5ml_DispensingVialCarrier: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6x5ml_DispensingVialCarrier + type: pylabrobot + description: YB_6x5ml_DispensingVialCarrier + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_6x_SmallSolutionBottleCarrier: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_6x_SmallSolutionBottleCarrier + type: pylabrobot + description: YB_6x_SmallSolutionBottleCarrier handles: [] icon: '' init_param_schema: {} registry_type: resource version: 1.0.0 - YB_AdapterBlock: category: - yb3 + - YB_bottle_carriers class: module: unilabos.resources.bioyond.YB_bottle_carriers:YB_AdapterBlock type: pylabrobot @@ -141,10 +128,10 @@ YB_AdapterBlock: init_param_schema: {} registry_type: resource version: 1.0.0 - YB_TipBox: category: - yb3 + - YB_bottle_carriers class: module: unilabos.resources.bioyond.YB_bottle_carriers:YB_TipBox type: pylabrobot @@ -154,36 +141,10 @@ YB_TipBox: init_param_schema: {} registry_type: resource version: 1.0.0 - -YB_ye_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottles:YB_ye_Bottle - type: pylabrobot - description: YB_ye_Bottle - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - -YB_ye_100ml_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.YB_bottles:YB_ye_100ml_Bottle - type: pylabrobot - description: YB_ye_100ml_Bottle - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 - YB_gao_nian_ye_Bottle: category: - yb3 + - YB_bottle_carriers class: module: unilabos.resources.bioyond.YB_bottles:YB_gao_nian_ye_Bottle type: pylabrobot @@ -193,10 +154,10 @@ YB_gao_nian_ye_Bottle: init_param_schema: {} registry_type: resource version: 1.0.0 - YB_jia_yang_tou_da: category: - yb3 + - YB_bottle_carriers class: module: unilabos.resources.bioyond.YB_bottles:YB_jia_yang_tou_da type: pylabrobot @@ -206,3 +167,42 @@ YB_jia_yang_tou_da: init_param_schema: {} registry_type: resource version: 1.0.0 +YB_jia_yang_tou_da_1X1_carrier: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottle_carriers:YB_jia_yang_tou_da_1X1_carrier + type: pylabrobot + description: YB_jia_yang_tou_da_1X1_carrier + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_ye_100ml_Bottle: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottles:YB_ye_100ml_Bottle + type: pylabrobot + description: YB_ye_100ml_Bottle + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 +YB_ye_Bottle: + category: + - yb3 + - YB_bottle_carriers + class: + module: unilabos.resources.bioyond.YB_bottles:YB_ye_Bottle + type: pylabrobot + description: YB_ye_Bottle + handles: [] + icon: '' + init_param_schema: {} + registry_type: resource + version: 1.0.0 From fe4e49e56d69cb7257ef1b597add40c671a17283 Mon Sep 17 00:00:00 2001 From: calvincao Date: Fri, 31 Oct 2025 13:53:58 +0800 Subject: [PATCH 2/7] =?UTF-8?q?feat(workstation):=20=E6=9B=B4=E6=96=B0=20B?= =?UTF-8?q?ioyond=20=E5=92=8C=20Coin=20Cell=20=E7=BB=84=E8=A3=85=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E7=AB=99=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修改 Bioyond Studio 配置文件中的 API 主机地址 - 更新 bioyond_cell_workstation.py 中的默认模板路径 - 新增物料模板文件 material_template.xlsx - 扩展 func_pack_send_msg_cmd 函数以支持 assembly_pressure 参数 - 更新 coin_cell_workstation.yaml 文件以包含 assembly_pressure 的默认值和类型定义 --- .../bioyond_cell/bioyond_cell_workstation.py | 2 +- ...品导入模板.xlsx => material_template.xlsx} | Bin 22129 -> 22168 bytes .../workstation/bioyond_studio/config.py | 2 +- .../coin_cell_assembly/coin_cell_assembly.py | 15 +++++++++------ .../devices/coin_cell_workstation.yaml | 8 ++++++++ 5 files changed, 19 insertions(+), 8 deletions(-) rename unilabos/devices/workstation/bioyond_studio/bioyond_cell/{样品导入模板.xlsx => material_template.xlsx} (88%) diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py index ff29574d..5ce49e00 100644 --- a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py +++ b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py @@ -253,7 +253,7 @@ class BioyondCellWorkstation(BioyondWorkstation): def auto_feeding4to3( self, # ★ 修改点:默认模板路径 - xlsx_path: Optional[str] = "unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\样品导入模板.xlsx", + xlsx_path: Optional[str] = "/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx", # ---------------- WH4 - 加样头面 (Z=1, 12个点位) ---------------- WH4_x1_y1_z1_1_materialName: str = "", WH4_x1_y1_z1_1_quantity: float = 0.0, WH4_x2_y1_z1_2_materialName: str = "", WH4_x2_y1_z1_2_quantity: float = 0.0, diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/样品导入模板.xlsx b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx similarity index 88% rename from unilabos/devices/workstation/bioyond_studio/bioyond_cell/样品导入模板.xlsx rename to unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx index 0d8d0e297d6ff5c2d304bc438c499a27da375b01..844fc84d932f618891abbdf6efdefc4bcf16289f 100644 GIT binary patch delta 128 zcmeykhH=JP#tr?de40E$H#?*l7(iHpVe*9#smFu?W$XZCM#I-ZC+quEyD>{GWkhJ vIHSa5n@}6Zvy&@g-0L> diff --git a/unilabos/devices/workstation/bioyond_studio/config.py b/unilabos/devices/workstation/bioyond_studio/config.py index 52feff6f..504cf459 100644 --- a/unilabos/devices/workstation/bioyond_studio/config.py +++ b/unilabos/devices/workstation/bioyond_studio/config.py @@ -8,7 +8,7 @@ import os # BioyondCellWorkstation 默认配置(包含所有必需参数) API_CONFIG = { # API 连接配置 - "api_host": os.getenv("BIOYOND_API_HOST", "http://172.21.32.103:44388"), + "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.10.169:44388"), "api_key": os.getenv("BIOYOND_API_KEY", "8A819E5C"), "timeout": int(os.getenv("BIOYOND_TIMEOUT", "30")), diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py index e520628e..d8bb5975 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py @@ -766,7 +766,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): # self.success = True # return self.success - def func_pack_send_msg_cmd(self, elec_use_num, elec_vol, assembly_type) -> bool: + def func_pack_send_msg_cmd(self, elec_use_num, elec_vol, assembly_type, assembly_pressure) -> bool: """UNILAB写参数""" while (self.request_rec_msg_status) == False: print("wait for request_rec_msg_status to True") @@ -782,6 +782,9 @@ class CoinCellAssemblyWorkstation(WorkstationBase): #发送电解液组装类型 self._unilab_send_msg_assembly_type(assembly_type) time.sleep(1) + #发送电池压制力 + self._unilab_send_msg_assembly_pressure(assembly_pressure) + time.sleep(1) self._unilab_send_msg_succ_cmd(True) time.sleep(1) while (self.request_rec_msg_status) == True: @@ -895,16 +898,16 @@ class CoinCellAssemblyWorkstation(WorkstationBase): self.client.use_node('REG_MSG_NE_PLATE_MATRIX').write(fujipian_juzhendianwei) self.client.use_node('REG_MSG_SEPARATOR_PLATE_NUM').write(gemopanshu) self.client.use_node('REG_MSG_SEPARATOR_PLATE_MATRIX').write(gemo_juzhendianwei) - self.client.use_node('COIL_ALUMINUM_FOIL').write(lvbodian) - self.client.use_node('REG_MSG_PRESS_MODE').write(battery_pressure_mode) + self.client.use_node('COIL_ALUMINUM_FOIL').write(not lvbodian) + self.client.use_node('REG_MSG_PRESS_MODE').write(not battery_pressure_mode) # self.client.use_node('REG_MSG_ASSEMBLY_PRESSURE').write(battery_pressure) self.client.use_node('REG_MSG_BATTERY_CLEAN_IGNORE').write(battery_clean_ignore) self.success = True return self.success - def func_allpack_cmd(self, elec_num, elec_use_num, elec_vol:int=50, assembly_type:int=7, file_path: str="D:\\coin_cell_data") -> bool: - elec_num, elec_use_num, elec_vol, assembly_type = int(elec_num), int(elec_use_num), int(elec_vol), int(assembly_type) + def func_allpack_cmd(self, elec_num, elec_use_num, elec_vol:int=50, assembly_type:int=7, assembly_pressure:int=4200, file_path: str="D:\\coin_cell_data") -> bool: + elec_num, elec_use_num, elec_vol, assembly_type, assembly_pressure = int(elec_num), int(elec_use_num), int(elec_vol), int(assembly_type), int(assembly_pressure) summary_csv_file = os.path.join(file_path, "duandian.csv") # 如果断点文件存在,先读取之前的进度 if os.path.exists(summary_csv_file): @@ -954,7 +957,7 @@ class CoinCellAssemblyWorkstation(WorkstationBase): print(f"开始第{last_i+i+1}瓶电解液的组装") #第一个循环从上次断点继续,后续循环从0开始 j_start = last_j if i == last_i else 0 - self.func_pack_send_msg_cmd(elec_use_num-j_start, elec_vol, assembly_type) + self.func_pack_send_msg_cmd(elec_use_num-j_start, elec_vol, assembly_type, assembly_pressure) for j in range(j_start, elec_use_num): print(f"开始第{last_i+i+1}瓶电解液的第{j+j_start+1}个电池组装") diff --git a/unilabos/registry/devices/coin_cell_workstation.yaml b/unilabos/registry/devices/coin_cell_workstation.yaml index 7e541eb8..4738946b 100644 --- a/unilabos/registry/devices/coin_cell_workstation.yaml +++ b/unilabos/registry/devices/coin_cell_workstation.yaml @@ -74,6 +74,7 @@ coincellassemblyworkstation_device: feedback: {} goal: {} goal_default: + assembly_pressure: 4200 assembly_type: 7 elec_num: null elec_use_num: null @@ -88,6 +89,9 @@ coincellassemblyworkstation_device: feedback: {} goal: properties: + assembly_pressure: + default: 4200 + type: integer assembly_type: default: 7 type: integer @@ -291,6 +295,7 @@ coincellassemblyworkstation_device: feedback: {} goal: {} goal_default: + assembly_pressure: null assembly_type: null elec_use_num: null elec_vol: null @@ -303,6 +308,8 @@ coincellassemblyworkstation_device: feedback: {} goal: properties: + assembly_pressure: + type: string assembly_type: type: string elec_use_num: @@ -313,6 +320,7 @@ coincellassemblyworkstation_device: - elec_use_num - elec_vol - assembly_type + - assembly_pressure type: object result: {} required: From 2eb9986edb662c73d7f91369d24df48a0b456f75 Mon Sep 17 00:00:00 2001 From: lixinyu1011 <674842481@qq.com> Date: Fri, 31 Oct 2025 13:54:58 +0800 Subject: [PATCH 3/7] 123 --- .../workstation/coin_cell_assembly.zip | Bin 0 -> 19794 bytes .../{coin_cell_assembly => }/__init__.py | 0 .../button_battery_station.py | 0 .../coin_cell_assembly.py | 14 +- .../coin_cell_assembly_a.csv | 0 .../new_cellconfig3c.json | 0 .../coin_cell_assembly/workstation_base.py | 489 ------------------ 7 files changed, 7 insertions(+), 496 deletions(-) create mode 100644 unilabos/devices/workstation/coin_cell_assembly.zip rename unilabos/devices/workstation/coin_cell_assembly/{coin_cell_assembly => }/__init__.py (100%) rename unilabos/devices/workstation/coin_cell_assembly/{coin_cell_assembly => }/button_battery_station.py (100%) rename unilabos/devices/workstation/coin_cell_assembly/{coin_cell_assembly => }/coin_cell_assembly.py (99%) rename unilabos/devices/workstation/coin_cell_assembly/{coin_cell_assembly => }/coin_cell_assembly_a.csv (100%) rename unilabos/devices/workstation/coin_cell_assembly/{coin_cell_assembly => }/new_cellconfig3c.json (100%) delete mode 100644 unilabos/devices/workstation/coin_cell_assembly/workstation_base.py diff --git a/unilabos/devices/workstation/coin_cell_assembly.zip b/unilabos/devices/workstation/coin_cell_assembly.zip new file mode 100644 index 0000000000000000000000000000000000000000..b95b7f4a3f731c9d33a00a977343af3c5962ded5 GIT binary patch literal 19794 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 None: - """从给定的状态加载工作台信息。""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - data = super().serialize_state() - data.update( - self._unilabos_state - ) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - - -def get_workstation_plate_resource(name: str) -> PLRResource: # 要给定一个返回plr的方法 - """ - 用于获取一些模板,例如返回一个带有特定信息/子物料的 Plate,这里需要到注册表注册,例如unilabos/registry/resources/organic/workstation.yaml - 可以直接运行该函数或者利用注册表补全机制,来检查是否资源出错 - :param name: 资源名称 - :return: Resource对象 - """ - plate = WorkStationContainer( - name, size_x=50, size_y=50, size_z=10, category="plate", ordering=collections.OrderedDict() - ) - tip_rack = WorkStationContainer( - "tip_rack_inside_plate", - size_x=50, - size_y=50, - size_z=10, - category="tip_rack", - ordering=collections.OrderedDict(), - ) - plate.assign_child_resource(tip_rack, Coordinate.zero()) - return plate - - -class ResourceSynchronizer(ABC): - """资源同步器基类 - - 负责与外部物料系统的同步,并对 self.deck 做修改 - """ - - def __init__(self, workstation: "WorkstationBase"): - self.workstation = workstation - - @abstractmethod - async def sync_from_external(self) -> bool: - """从外部系统同步物料到本地deck""" - pass - - @abstractmethod - async def sync_to_external(self, plr_resource: PLRResource) -> bool: - """将本地物料同步到外部系统""" - pass - - @abstractmethod - async def handle_external_change(self, change_info: Dict[str, Any]) -> bool: - """处理外部系统的变更通知""" - pass - - -class WorkstationBase(ABC): - """工作站基类 - 简化版 - - 核心功能: - 1. 基于 PLR Deck 的物料系统,支持格式转换 - 2. 可选的资源同步器支持外部物料系统 - 3. 简化的工作流管理 - """ - - _ros_node: ROS2WorkstationNode - - @property - def _children(self) -> Dict[str, Any]: # 不要删除这个下划线,不然会自动导入注册表,后面改成装饰器识别 - return self._ros_node.children - - async def update_resource_example(self): - return await self._ros_node.update_resource([get_workstation_plate_resource("test")]) - - def __init__( - self, - station_resource: PLRResource, - *args, - **kwargs, # 必须有kwargs - ): - # 基本配置 - print(station_resource) - self.deck_config = station_resource - - # PLR 物料系统 - self.deck: Optional[Deck] = None - self.plr_resources: Dict[str, PLRResource] = {} - - # 资源同步器(可选) - # self.resource_synchronizer = ResourceSynchronizer(self) # 要在driver中自行初始化,只有workstation用 - - # 硬件接口 - self.hardware_interface: Union[Any, str] = None - - # 工作流状态 - self.current_workflow_status = WorkflowStatus.IDLE - self.current_workflow_info = None - self.workflow_start_time = None - self.workflow_parameters = {} - - # 支持的工作流(静态预定义) - self.supported_workflows: Dict[str, WorkflowInfo] = {} - - # 初始化物料系统 - self._initialize_material_system() - - # 注册支持的工作流 - # self._register_supported_workflows() - - # logger.info(f"工作站 {device_id} 初始化完成(简化版)") - - def _initialize_material_system(self): - """初始化物料系统 - 使用 graphio 转换""" - try: - from unilabos.resources.graphio import resource_ulab_to_plr - - # # 1. 合并 deck_config 和 children 创建完整的资源树 - # complete_resource_config = self._create_complete_resource_config() - - # # 2. 使用 graphio 转换为 PLR 资源 - # self.deck = resource_ulab_to_plr(complete_resource_config, plr_model=True) - - # # 3. 建立资源映射 - # self._build_resource_mappings(self.deck) - - # # 4. 如果有资源同步器,执行初始同步 - # if self.resource_synchronizer: - # # 这里可以异步执行,暂时跳过 - # pass - - # logger.info(f"工作站 {self.device_id} 物料系统初始化成功,创建了 {len(self.plr_resources)} 个资源") - pass - except Exception as e: - # logger.error(f"工作站 {self.device_id} 物料系统初始化失败: {e}") - raise - - def _create_complete_resource_config(self) -> Dict[str, Any]: - """创建完整的资源配置 - 合并 deck_config 和 children""" - # 创建主 deck 配置 - deck_resource = { - "id": f"{self.device_id}_deck", - "name": f"{self.device_id}_deck", - "type": "deck", - "position": {"x": 0, "y": 0, "z": 0}, - "config": { - "size_x": self.deck_config.get("size_x", 1000.0), - "size_y": self.deck_config.get("size_y", 1000.0), - "size_z": self.deck_config.get("size_z", 100.0), - **{k: v for k, v in self.deck_config.items() if k not in ["size_x", "size_y", "size_z"]}, - }, - "data": {}, - "children": [], - "parent": None, - } - - # 添加子资源 - if self._children: - children_list = [] - for child_id, child_config in self._children.items(): - child_resource = self._normalize_child_resource(child_id, child_config, deck_resource["id"]) - children_list.append(child_resource) - deck_resource["children"] = children_list - - return deck_resource - - def _normalize_child_resource(self, resource_id: str, config: Dict[str, Any], parent_id: str) -> Dict[str, Any]: - """标准化子资源配置""" - return { - "id": resource_id, - "name": config.get("name", resource_id), - "type": config.get("type", "container"), - "position": self._normalize_position(config.get("position", {})), - "config": config.get("config", {}), - "data": config.get("data", {}), - "children": [], # 简化版本:只支持一层子资源 - "parent": parent_id, - } - - def _normalize_position(self, position: Any) -> Dict[str, float]: - """标准化位置信息""" - if isinstance(position, dict): - return { - "x": float(position.get("x", 0)), - "y": float(position.get("y", 0)), - "z": float(position.get("z", 0)), - } - elif isinstance(position, (list, tuple)) and len(position) >= 2: - return { - "x": float(position[0]), - "y": float(position[1]), - "z": float(position[2]) if len(position) > 2 else 0.0, - } - else: - return {"x": 0.0, "y": 0.0, "z": 0.0} - - def _build_resource_mappings(self, deck: Deck): - """递归构建资源映射""" - - def add_resource_recursive(resource: PLRResource): - if hasattr(resource, "name"): - self.plr_resources[resource.name] = resource - - if hasattr(resource, "children"): - for child in resource.children: - add_resource_recursive(child) - - add_resource_recursive(deck) - - # ============ 硬件接口管理 ============ - - def set_hardware_interface(self, hardware_interface: Union[Any, str]): - """设置硬件接口""" - self.hardware_interface = hardware_interface - logger.info(f"工作站 {self.device_id} 硬件接口设置: {type(hardware_interface).__name__}") - - def set_workstation_node(self, workstation_node: "ROS2WorkstationNode"): - """设置协议节点引用(用于代理模式)""" - self._ros_node = workstation_node - logger.info(f"工作站 {self.device_id} 关联协议节点") - - # ============ 设备操作接口 ============ - - def call_device_method(self, method: str, *args, **kwargs) -> Any: - """调用设备方法的统一接口""" - # 1. 代理模式:通过协议节点转发 - if isinstance(self.hardware_interface, str) and self.hardware_interface.startswith("proxy:"): - if not self._ros_node: - raise RuntimeError("代理模式需要设置workstation_node") - - device_id = self.hardware_interface[6:] # 移除 "proxy:" 前缀 - return self._ros_node.call_device_method(device_id, method, *args, **kwargs) - - # 2. 直接模式:直接调用硬件接口方法 - elif self.hardware_interface and hasattr(self.hardware_interface, method): - return getattr(self.hardware_interface, method)(*args, **kwargs) - - else: - raise AttributeError(f"硬件接口不支持方法: {method}") - - def get_device_status(self) -> Dict[str, Any]: - """获取设备状态""" - try: - return self.call_device_method("get_status") - except AttributeError: - # 如果设备不支持get_status方法,返回基础状态 - return { - "status": "unknown", - "interface_type": type(self.hardware_interface).__name__, - "timestamp": time.time(), - } - - def is_device_available(self) -> bool: - """检查设备是否可用""" - try: - self.get_device_status() - return True - except: - return False - - # ============ 物料系统接口 ============ - - def get_deck(self) -> Deck: - """获取主 Deck""" - return self.deck - - def get_all_resources(self) -> Dict[str, PLRResource]: - """获取所有 PLR 资源""" - return self.plr_resources.copy() - - def find_resource_by_name(self, name: str) -> Optional[PLRResource]: - """按名称查找资源""" - return self.plr_resources.get(name) - - def find_resources_by_type(self, resource_type: type) -> List[PLRResource]: - """按类型查找资源""" - return [res for res in self.plr_resources.values() if isinstance(res, resource_type)] - - async def sync_with_external_system(self) -> bool: - """与外部物料系统同步""" - if not self.resource_synchronizer: - logger.info(f"工作站 {self.device_id} 没有配置资源同步器") - return True - - try: - success = await self.resource_synchronizer.sync_from_external() - if success: - logger.info(f"工作站 {self.device_id} 外部同步成功") - else: - logger.warning(f"工作站 {self.device_id} 外部同步失败") - return success - except Exception as e: - logger.error(f"工作站 {self.device_id} 外部同步异常: {e}") - return False - - # ============ 简化的工作流控制 ============ - - def execute_workflow(self, workflow_name: str, parameters: Dict[str, Any]) -> bool: - """执行工作流""" - try: - # 设置工作流状态 - self.current_workflow_status = WorkflowStatus.INITIALIZING - self.workflow_parameters = parameters - self.workflow_start_time = time.time() - - # 委托给子类实现 - success = self._execute_workflow_impl(workflow_name, parameters) - - if success: - self.current_workflow_status = WorkflowStatus.RUNNING - logger.info(f"工作站 {self.device_id} 工作流 {workflow_name} 启动成功") - else: - self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self.device_id} 工作流 {workflow_name} 启动失败") - - return success - - except Exception as e: - self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self.device_id} 执行工作流失败: {e}") - return False - - def stop_workflow(self, emergency: bool = False) -> bool: - """停止工作流""" - try: - if self.current_workflow_status in [WorkflowStatus.IDLE, WorkflowStatus.STOPPED]: - logger.warning(f"工作站 {self.device_id} 没有正在运行的工作流") - return True - - self.current_workflow_status = WorkflowStatus.STOPPING - - # 委托给子类实现 - success = self._stop_workflow_impl(emergency) - - if success: - self.current_workflow_status = WorkflowStatus.STOPPED - logger.info(f"工作站 {self.device_id} 工作流停止成功 (紧急: {emergency})") - else: - self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self.device_id} 工作流停止失败") - - return success - - except Exception as e: - self.current_workflow_status = WorkflowStatus.ERROR - logger.error(f"工作站 {self.device_id} 停止工作流失败: {e}") - return False - - # ============ 状态属性 ============ - - @property - def workflow_status(self) -> WorkflowStatus: - """获取当前工作流状态""" - return self.current_workflow_status - - @property - def is_busy(self) -> bool: - """检查工作站是否忙碌""" - return self.current_workflow_status in [ - WorkflowStatus.INITIALIZING, - WorkflowStatus.RUNNING, - WorkflowStatus.STOPPING, - ] - - @property - def workflow_runtime(self) -> float: - """获取工作流运行时间(秒)""" - if self.workflow_start_time is None: - return 0.0 - return time.time() - self.workflow_start_time - - # ============ 抽象方法 - 子类必须实现 ============ - - # @abstractmethod - # def _register_supported_workflows(self): - # """注册支持的工作流 - 子类必须实现""" - # pass - - # @abstractmethod - # def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool: - # """执行工作流的具体实现 - 子类必须实现""" - # pass - - # @abstractmethod - # def _stop_workflow_impl(self, emergency: bool = False) -> bool: - # """停止工作流的具体实现 - 子类必须实现""" - # pass - -class WorkstationExample(WorkstationBase): - """工作站示例实现""" - - def _register_supported_workflows(self): - """注册支持的工作流""" - self.supported_workflows["example_workflow"] = WorkflowInfo( - name="example_workflow", - description="这是一个示例工作流", - estimated_duration=300.0, - required_materials=["sample_plate"], - output_product="processed_plate", - parameters_schema={"param1": "string", "param2": "integer"}, - ) - - def _execute_workflow_impl(self, workflow_name: str, parameters: Dict[str, Any]) -> bool: - """执行工作流的具体实现""" - if workflow_name not in self.supported_workflows: - logger.error(f"工作站 {self.device_id} 不支持工作流: {workflow_name}") - return False - - # 这里添加实际的工作流逻辑 - logger.info(f"工作站 {self.device_id} 正在执行工作流: {workflow_name} with parameters {parameters}") - return True - - def _stop_workflow_impl(self, emergency: bool = False) -> bool: - """停止工作流的具体实现""" - # 这里添加实际的停止逻辑 - logger.info(f"工作站 {self.device_id} 正在停止工作流 (紧急: {emergency})") - return True \ No newline at end of file From 6d7c39da9e9e5ddb2b1727aec4bfa0a8055d3d64 Mon Sep 17 00:00:00 2001 From: lixinyu1011 <674842481@qq.com> Date: Fri, 31 Oct 2025 15:29:59 +0800 Subject: [PATCH 4/7] 1031 --- test/resources/test.json | 191 ++++++++++++++++++ .../bioyond_cell/bioyond_cell_workstation.py | 132 +++++++++++- .../workstation/bioyond_studio/config.py | 2 +- unilabos/registry/devices/laiyu_liquid.yaml | 11 +- unilabos/registry/devices/liquid_handler.yaml | 33 ++- 5 files changed, 333 insertions(+), 36 deletions(-) create mode 100644 test/resources/test.json diff --git a/test/resources/test.json b/test/resources/test.json new file mode 100644 index 00000000..9fa92372 --- /dev/null +++ b/test/resources/test.json @@ -0,0 +1,191 @@ +{ + "data": [ + { + "orderCode": "BSO2025103100006", + "orderName": "DP20250927001", + "errorMessage": null, + "usedMaterials": [ + { + "id": "3a1d4b13-25a6-cfb2-7315-159f14b32425", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", + "materialId": "3a1d4b13-2467-e64d-d8bc-3957fb6e3240", + "materialName": "适配器块", + "materialCode": "0018-00065", + "quantity": "1块", + "materialTypeId": "efc3bb32-d504-4890-91c0-b64ed3ac80cf", + "materialTypeCode": "0018", + "materialTypeMode": "Consumables", + "materialTypeName": "适配器块", + "locationId": "3a1abd46-18fe-1f56-6ced-a1f7fe08e36c", + "locationCode": "0014-0001", + "locationShowName": "0014-0001" + }, + { + "id": "3a1d4b13-2420-8cfe-17f1-5f77a6ff6dc3", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", + "materialId": "3a1d4b11-e448-bf90-d0bd-b20758425370", + "materialName": "test1", + "materialCode": "0001-00063", + "quantity": "1块", + "materialTypeId": "3a190c8b-3284-af78-d29f-9a69463ad047", + "materialTypeCode": "0001", + "materialTypeMode": "Sample", + "materialTypeName": "配液瓶(小)板", + "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", + "locationCode": "4", + "locationShowName": "4" + }, + { + "id": "3a1d4b13-2420-73a1-2b4d-7bf6dd993c36", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", + "materialId": "3a1d4b11-e448-fea3-8291-0b66ecd06d72", + "materialName": "test1", + "materialCode": "0002-00282", + "quantity": "1块", + "materialTypeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "materialTypeCode": "0002", + "materialTypeMode": "Sample", + "materialTypeName": "配液瓶(小)", + "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", + "locationCode": "4-1/1", + "locationShowName": "4-1/1" + }, + { + "id": "3a1d4b13-2420-e45f-192d-639887ad73b7", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", + "materialId": "3a1d4b12-67fc-5f91-13ed-c223d0155399", + "materialName": "test2", + "materialCode": "0010-00059", + "quantity": "1块", + "materialTypeId": "3a192fa4-007d-ec7b-456e-2a8be7a13f23", + "materialTypeCode": "0010", + "materialTypeMode": "Sample", + "materialTypeName": "5ml分液瓶板", + "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", + "locationCode": "5", + "locationShowName": "5" + }, + { + "id": "3a1d4b13-2420-c052-93cc-002f0aae79fc", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", + "materialId": "3a1d4b12-67fc-60f7-1129-3d1ef2a2d1f8", + "materialName": "test2", + "materialCode": "0007-00211", + "quantity": "1块", + "materialTypeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4", + "materialTypeCode": "0007", + "materialTypeMode": "Sample", + "materialTypeName": "5ml分液瓶", + "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", + "locationCode": "5-1/1", + "locationShowName": "5-1/1" + } + ] + }, + { + "orderCode": "BSO2025103100007", + "orderName": "DP20250927002", + "errorMessage": null, + "usedMaterials": [ + { + "id": "3a1d4b13-264b-aca7-9e97-ab4df186d5c2", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", + "materialId": "3a1d4b13-2467-e64d-d8bc-3957fb6e3240", + "materialName": "适配器块", + "materialCode": "0018-00065", + "quantity": "1块", + "materialTypeId": "efc3bb32-d504-4890-91c0-b64ed3ac80cf", + "materialTypeCode": "0018", + "materialTypeMode": "Consumables", + "materialTypeName": "适配器块", + "locationId": "3a1abd46-18fe-1f56-6ced-a1f7fe08e36c", + "locationCode": "0014-0001", + "locationShowName": "0014-0001" + }, + { + "id": "3a1d4b13-263e-873e-1331-7e668b411e98", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", + "materialId": "3a1d4b11-e448-bf90-d0bd-b20758425370", + "materialName": "test1", + "materialCode": "0001-00063", + "quantity": "1块", + "materialTypeId": "3a190c8b-3284-af78-d29f-9a69463ad047", + "materialTypeCode": "0001", + "materialTypeMode": "Sample", + "materialTypeName": "配液瓶(小)板", + "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", + "locationCode": "4", + "locationShowName": "4" + }, + { + "id": "3a1d4b13-263e-7884-d9e0-b010478b7448", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", + "materialId": "3a1d4b11-e448-82e0-6a64-6230ee1bf0a9", + "materialName": "test1", + "materialCode": "0002-00283", + "quantity": "1块", + "materialTypeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "materialTypeCode": "0002", + "materialTypeMode": "Sample", + "materialTypeName": "配液瓶(小)", + "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", + "locationCode": "4-1/2", + "locationShowName": "4-1/2" + }, + { + "id": "3a1d4b13-263e-6e99-b513-66047191643f", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", + "materialId": "3a1d4b12-67fc-5f91-13ed-c223d0155399", + "materialName": "test2", + "materialCode": "0010-00059", + "quantity": "1块", + "materialTypeId": "3a192fa4-007d-ec7b-456e-2a8be7a13f23", + "materialTypeCode": "0010", + "materialTypeMode": "Sample", + "materialTypeName": "5ml分液瓶板", + "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", + "locationCode": "5", + "locationShowName": "5" + }, + { + "id": "3a1d4b13-263e-5b21-2c41-53e4ea7fe947", + "destinationType": "TempOrder", + "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", + "materialId": "3a1d4b12-67fc-131a-82ff-87e9e7708f9f", + "materialName": "test2", + "materialCode": "0007-00212", + "quantity": "1块", + "materialTypeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4", + "materialTypeCode": "0007", + "materialTypeMode": "Sample", + "materialTypeName": "5ml分液瓶", + "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", + "locationCode": "5-1/2", + "locationShowName": "5-1/2" + } + ] + } + ], + "code": 1, + "message": "", + "timestamp": 1761891208109 +} + +25-10-31 [14:27:52,203] [ERROR] 从Bioyond同步物料数据失败: 'BottleCarrier' object has no attribute 'tracker' [sync_from_external:83] [unilabos.utils.log.station] +Traceback (most recent call last): + File "C:\ML\GitHub\Uni-Lab-OS\unilabos\devices\workstation\bioyond_studio\station.py", line 73, in sync_from_external + unilab_resources = resource_bioyond_to_plr( + ^^^^^^^^^^^^^^^^^^^^^^^^ + File "C:\ML\GitHub\Uni-Lab-OS\unilabos\resources\graphio.py", line 661, in resource_bioyond_to_plr + bottle.tracker.liquids = [ + ^^^^^^^^^^^^^^ +AttributeError: 'BottleCarrier' object has no attribute 'tracker' \ No newline at end of file diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py index 5ce49e00..c40945e7 100644 --- a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py +++ b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py @@ -253,7 +253,7 @@ class BioyondCellWorkstation(BioyondWorkstation): def auto_feeding4to3( self, # ★ 修改点:默认模板路径 - xlsx_path: Optional[str] = "/Users/calvincao/Desktop/work/uni-lab-all/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx", + xlsx_path: Optional[str] = "unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\material_template.xlsx", # ---------------- WH4 - 加样头面 (Z=1, 12个点位) ---------------- WH4_x1_y1_z1_1_materialName: str = "", WH4_x1_y1_z1_1_quantity: float = 0.0, WH4_x2_y1_z1_2_materialName: str = "", WH4_x2_y1_z1_2_quantity: float = 0.0, @@ -630,7 +630,12 @@ class BioyondCellWorkstation(BioyondWorkstation): response = self._post_lims("/api/lims/order/orders", orders) print(response) # 等待任务报送成功 - order_code = response.get("data", {}).get("orderCode") + data_list = response.get("data", []) + if data_list: + order_code = data_list[0].get("orderCode") + else: + order_code = None + if not order_code: logger.error("上料任务未返回有效 orderCode!") return response @@ -963,6 +968,119 @@ class BioyondCellWorkstation(BioyondWorkstation): logger.error(f"✗ 执行失败: {e}") return {"success": False, "error": str(e)} +def create_material( + self, + material_name: str, + type_id: str, + warehouse_name: str, + location_name_or_id: Optional[str] = None + ) -> Dict[str, Any]: + """创建单个物料并可选入库。 + Args: + material_name: 物料名称(会优先匹配配置模板)。 + type_id: 物料类型 ID(若为空则尝试从配置推断)。 + warehouse_name: 需要入库的仓库名称;若为空则仅创建不入库。 + location_name_or_id: 具体库位名称(如 A01)或库位 UUID,由用户指定。 + Returns: + 包含创建结果、物料ID以及入库结果的字典。 + """ + material_name = (material_name or "").strip() + if not material_name: + raise ValueError("material_name 不能为空") + resolved_type_id = (type_id or "").strip() + # 优先从 SOLID_LIQUID_MAPPINGS 中获取模板数据 + template = SOLID_LIQUID_MAPPINGS.get(material_name) + if not template: + raise ValueError(f"在配置中未找到物料 {material_name} 的模板,请检查 SOLID_LIQUID_MAPPINGS。") + material_data: Dict[str, Any] + material_data = deepcopy(template) + # 最终确保 typeId 为调用方传入的值 + if resolved_type_id: + material_data["typeId"] = resolved_type_id + material_data["name"] = material_name + # 生成唯一编码 + def _generate_code(prefix: str) -> str: + normalized = re.sub(r"\W+", "_", prefix) + normalized = normalized.strip("_") or "material" + return f"{normalized}_{datetime.now().strftime('%Y%m%d%H%M%S')}" + if not material_data.get("code"): + material_data["code"] = _generate_code(material_name) + if not material_data.get("barCode"): + material_data["barCode"] = "" + # 处理数量字段类型 + def _to_number(value: Any, default: float = 0.0) -> float: + try: + if value is None: + return default + if isinstance(value, (int, float)): + return float(value) + if isinstance(value, str) and value.strip() == "": + return default + return float(value) + except (TypeError, ValueError): + return default + material_data["quantity"] = _to_number(material_data.get("quantity"), 1.0) + material_data["warningQuantity"] = _to_number(material_data.get("warningQuantity"), 0.0) + unit = material_data.get("unit") or "个" + material_data["unit"] = unit + if not material_data.get("parameters"): + material_data["parameters"] = json.dumps({"unit": unit}, ensure_ascii=False) + # 补充子物料信息 + details = material_data.get("details") or [] + if not isinstance(details, list): + logger.warning("details 字段不是列表,已忽略。") + details = [] + else: + for idx, detail in enumerate(details, start=1): + if not isinstance(detail, dict): + continue + if not detail.get("code"): + detail["code"] = f"{material_data['code']}_{idx:02d}" + if not detail.get("name"): + detail["name"] = f"{material_name}_detail_{idx:02d}" + if not detail.get("unit"): + detail["unit"] = unit + if not detail.get("parameters"): + detail["parameters"] = json.dumps({"unit": detail.get("unit", unit)}, ensure_ascii=False) + if "quantity" in detail: + detail["quantity"] = _to_number(detail.get("quantity"), 1.0) + material_data["details"] = details + create_result = self._post_lims("/api/lims/storage/material", material_data) + # 解析创建结果中的物料 ID + material_id: Optional[str] = None + if isinstance(create_result, dict): + data_field = create_result.get("data") + if isinstance(data_field, str): + material_id = data_field + elif isinstance(data_field, dict): + material_id = data_field.get("id") or data_field.get("materialId") + inbound_result: Optional[Dict[str, Any]] = None + location_id: Optional[str] = None + # 按用户指定位置入库 + if warehouse_name and material_id and location_name_or_id: + try: + location_ids, position_names = self._load_warehouse_locations(warehouse_name) + position_to_id = {name: loc_id for name, loc_id in zip(position_names, location_ids)} + target_location_id = position_to_id.get(location_name_or_id, location_name_or_id) + if target_location_id: + location_id = target_location_id + inbound_result = self.storage_inbound(material_id, target_location_id) + else: + inbound_result = {"error": f"未找到匹配的库位: {location_name_or_id}"} + except Exception as exc: + logger.error(f"获取仓库 {warehouse_name} 位置失败: {exc}") + inbound_result = {"error": str(exc)} + return { + "success": bool(isinstance(create_result, dict) and create_result.get("code") == 1 and material_id), + "material_name": material_name, + "material_id": material_id, + "warehouse": warehouse_name, + "location_id": location_id, + "location_name_or_id": location_name_or_id, + "create_result": create_result, + "inbound_result": inbound_result, + } + # -------------------------------- @@ -971,7 +1089,7 @@ if __name__ == "__main__": lab_registry.setup() ws = BioyondCellWorkstation() # logger.info(ws.scheduler_stop()) - # logger.info(ws.scheduler_start()) + logger.info(ws.scheduler_start()) # results = ws.create_materials(SOLID_LIQUID_MAPPINGS) # for r in results: @@ -980,11 +1098,11 @@ if __name__ == "__main__": # result = ws.create_and_inbound_materials() # 继续后续流程 - # logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 + logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 # # 使用正斜杠或 Path 对象来指定文件路径 - # excel_path = Path("unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\2025092701.xlsx") - # logger.info(ws.create_orders(excel_path)) - # logger.info(ws.transfer_3_to_2_to_1()) + excel_path = Path("unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\2025092701.xlsx") + logger.info(ws.create_orders(excel_path)) + logger.info(ws.transfer_3_to_2_to_1()) # logger.info(ws.transfer_1_to_2()) # logger.info(ws.scheduler_start()) diff --git a/unilabos/devices/workstation/bioyond_studio/config.py b/unilabos/devices/workstation/bioyond_studio/config.py index 504cf459..519e6869 100644 --- a/unilabos/devices/workstation/bioyond_studio/config.py +++ b/unilabos/devices/workstation/bioyond_studio/config.py @@ -16,7 +16,7 @@ API_CONFIG = { "report_token": os.getenv("BIOYOND_REPORT_TOKEN", "CHANGE_ME_TOKEN"), # HTTP 服务配置 - "HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.21.32.210"), # HTTP服务监听地址,监听计算机飞连ip地址 + "HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.21.33.174"), # HTTP服务监听地址,监听计算机飞连ip地址 "HTTP_port": int(os.getenv("BIOYOND_HTTP_PORT", "8080")), "debug_mode": False,# 调试模式 } diff --git a/unilabos/registry/devices/laiyu_liquid.yaml b/unilabos/registry/devices/laiyu_liquid.yaml index 98201a7d..64c0c182 100644 --- a/unilabos/registry/devices/laiyu_liquid.yaml +++ b/unilabos/registry/devices/laiyu_liquid.yaml @@ -1361,8 +1361,7 @@ laiyu_liquid: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: - - 0 + mix_times: 0 mix_vol: 0 none_keys: - '' @@ -1492,11 +1491,9 @@ laiyu_liquid: mix_stage: type: string mix_times: - items: - maximum: 2147483647 - minimum: -2147483648 - type: integer - type: array + maximum: 2147483647 + minimum: -2147483648 + type: integer mix_vol: maximum: 2147483647 minimum: -2147483648 diff --git a/unilabos/registry/devices/liquid_handler.yaml b/unilabos/registry/devices/liquid_handler.yaml index b21ccd7e..99c92333 100644 --- a/unilabos/registry/devices/liquid_handler.yaml +++ b/unilabos/registry/devices/liquid_handler.yaml @@ -3994,8 +3994,7 @@ liquid_handler: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: - - 0 + mix_times: 0 mix_vol: 0 none_keys: - '' @@ -4151,11 +4150,9 @@ liquid_handler: mix_stage: type: string mix_times: - items: - maximum: 2147483647 - minimum: -2147483648 - type: integer - type: array + maximum: 2147483647 + minimum: -2147483648 + type: integer mix_vol: maximum: 2147483647 minimum: -2147483648 @@ -5015,8 +5012,7 @@ liquid_handler.biomek: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: - - 0 + mix_times: 0 mix_vol: 0 none_keys: - '' @@ -5159,11 +5155,9 @@ liquid_handler.biomek: mix_stage: type: string mix_times: - items: - maximum: 2147483647 - minimum: -2147483648 - type: integer - type: array + maximum: 2147483647 + minimum: -2147483648 + type: integer mix_vol: maximum: 2147483647 minimum: -2147483648 @@ -7807,8 +7801,7 @@ liquid_handler.prcxi: mix_liquid_height: 0.0 mix_rate: 0 mix_stage: '' - mix_times: - - 0 + mix_times: 0 mix_vol: 0 none_keys: - '' @@ -7937,11 +7930,9 @@ liquid_handler.prcxi: mix_stage: type: string mix_times: - items: - maximum: 2147483647 - minimum: -2147483648 - type: integer - type: array + maximum: 2147483647 + minimum: -2147483648 + type: integer mix_vol: maximum: 2147483647 minimum: -2147483648 From a62896eda298eccd61f971c0fb2e5b835584331b Mon Sep 17 00:00:00 2001 From: lixinyu1011 <674842481@qq.com> Date: Fri, 31 Oct 2025 18:57:38 +0800 Subject: [PATCH 5/7] 1031_byxinyu --- test/resources/test copy.json | 99 ++++++ test/resources/test.json | 305 +++++++----------- .../bioyond_cell/bioyond_cell_workstation.py | 73 ++++- .../bioyond_cell/material_template.xlsx | Bin 22168 -> 22207 bytes 4 files changed, 276 insertions(+), 201 deletions(-) create mode 100644 test/resources/test copy.json diff --git a/test/resources/test copy.json b/test/resources/test copy.json new file mode 100644 index 00000000..f9e9aa0d --- /dev/null +++ b/test/resources/test copy.json @@ -0,0 +1,99 @@ + { + "typeId": "3a190c8b-3284-af78-d29f-9a69463ad047", + "code": "", + "barCode": "", + "name": "test", + "unit": "", + "parameters": "{}", + "quantity": "", + "details": [ + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)11", + "quantity": "1", + "x": 1, + "y": 1, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)21", + "quantity": "1", + "x": 2, + "y": 1, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)12", + "quantity": "1", + "x": 1, + "y": 2, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)22", + "quantity": "1", + "x": 2, + "y": 2, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)13", + "quantity": "1", + "x": 1, + "y": 3, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)23", + "quantity": "1", + "x": 2, + "y": 3, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)14", + "quantity": "1", + "x": 1, + "y": 4, + "z": 1, + "unit": "", + "parameters": "{}" + }, + { + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", + "code": "", + "name": "配液瓶(小)24", + "quantity": "1", + "x": 2, + "y": 4, + "z": 1, + "unit": "", + "parameters": "{}" + } + ] + } \ No newline at end of file diff --git a/test/resources/test.json b/test/resources/test.json index 9fa92372..ee1be0f1 100644 --- a/test/resources/test.json +++ b/test/resources/test.json @@ -1,191 +1,114 @@ -{ - "data": [ - { - "orderCode": "BSO2025103100006", - "orderName": "DP20250927001", - "errorMessage": null, - "usedMaterials": [ - { - "id": "3a1d4b13-25a6-cfb2-7315-159f14b32425", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", - "materialId": "3a1d4b13-2467-e64d-d8bc-3957fb6e3240", - "materialName": "适配器块", - "materialCode": "0018-00065", - "quantity": "1块", - "materialTypeId": "efc3bb32-d504-4890-91c0-b64ed3ac80cf", - "materialTypeCode": "0018", - "materialTypeMode": "Consumables", - "materialTypeName": "适配器块", - "locationId": "3a1abd46-18fe-1f56-6ced-a1f7fe08e36c", - "locationCode": "0014-0001", - "locationShowName": "0014-0001" - }, - { - "id": "3a1d4b13-2420-8cfe-17f1-5f77a6ff6dc3", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", - "materialId": "3a1d4b11-e448-bf90-d0bd-b20758425370", - "materialName": "test1", - "materialCode": "0001-00063", - "quantity": "1块", - "materialTypeId": "3a190c8b-3284-af78-d29f-9a69463ad047", - "materialTypeCode": "0001", - "materialTypeMode": "Sample", - "materialTypeName": "配液瓶(小)板", - "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", - "locationCode": "4", - "locationShowName": "4" - }, - { - "id": "3a1d4b13-2420-73a1-2b4d-7bf6dd993c36", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", - "materialId": "3a1d4b11-e448-fea3-8291-0b66ecd06d72", - "materialName": "test1", - "materialCode": "0002-00282", - "quantity": "1块", - "materialTypeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", - "materialTypeCode": "0002", - "materialTypeMode": "Sample", - "materialTypeName": "配液瓶(小)", - "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", - "locationCode": "4-1/1", - "locationShowName": "4-1/1" - }, - { - "id": "3a1d4b13-2420-e45f-192d-639887ad73b7", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", - "materialId": "3a1d4b12-67fc-5f91-13ed-c223d0155399", - "materialName": "test2", - "materialCode": "0010-00059", - "quantity": "1块", - "materialTypeId": "3a192fa4-007d-ec7b-456e-2a8be7a13f23", - "materialTypeCode": "0010", - "materialTypeMode": "Sample", - "materialTypeName": "5ml分液瓶板", - "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", - "locationCode": "5", - "locationShowName": "5" - }, - { - "id": "3a1d4b13-2420-c052-93cc-002f0aae79fc", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-23cb-63e5-10df-6a1d38335163", - "materialId": "3a1d4b12-67fc-60f7-1129-3d1ef2a2d1f8", - "materialName": "test2", - "materialCode": "0007-00211", - "quantity": "1块", - "materialTypeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4", - "materialTypeCode": "0007", - "materialTypeMode": "Sample", - "materialTypeName": "5ml分液瓶", - "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", - "locationCode": "5-1/1", - "locationShowName": "5-1/1" - } - ] - }, - { - "orderCode": "BSO2025103100007", - "orderName": "DP20250927002", - "errorMessage": null, - "usedMaterials": [ - { - "id": "3a1d4b13-264b-aca7-9e97-ab4df186d5c2", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", - "materialId": "3a1d4b13-2467-e64d-d8bc-3957fb6e3240", - "materialName": "适配器块", - "materialCode": "0018-00065", - "quantity": "1块", - "materialTypeId": "efc3bb32-d504-4890-91c0-b64ed3ac80cf", - "materialTypeCode": "0018", - "materialTypeMode": "Consumables", - "materialTypeName": "适配器块", - "locationId": "3a1abd46-18fe-1f56-6ced-a1f7fe08e36c", - "locationCode": "0014-0001", - "locationShowName": "0014-0001" - }, - { - "id": "3a1d4b13-263e-873e-1331-7e668b411e98", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", - "materialId": "3a1d4b11-e448-bf90-d0bd-b20758425370", - "materialName": "test1", - "materialCode": "0001-00063", - "quantity": "1块", - "materialTypeId": "3a190c8b-3284-af78-d29f-9a69463ad047", - "materialTypeCode": "0001", - "materialTypeMode": "Sample", - "materialTypeName": "配液瓶(小)板", - "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", - "locationCode": "4", - "locationShowName": "4" - }, - { - "id": "3a1d4b13-263e-7884-d9e0-b010478b7448", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", - "materialId": "3a1d4b11-e448-82e0-6a64-6230ee1bf0a9", - "materialName": "test1", - "materialCode": "0002-00283", - "quantity": "1块", - "materialTypeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb", - "materialTypeCode": "0002", - "materialTypeMode": "Sample", - "materialTypeName": "配液瓶(小)", - "locationId": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", - "locationCode": "4-1/2", - "locationShowName": "4-1/2" - }, - { - "id": "3a1d4b13-263e-6e99-b513-66047191643f", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", - "materialId": "3a1d4b12-67fc-5f91-13ed-c223d0155399", - "materialName": "test2", - "materialCode": "0010-00059", - "quantity": "1块", - "materialTypeId": "3a192fa4-007d-ec7b-456e-2a8be7a13f23", - "materialTypeCode": "0010", - "materialTypeMode": "Sample", - "materialTypeName": "5ml分液瓶板", - "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", - "locationCode": "5", - "locationShowName": "5" - }, - { - "id": "3a1d4b13-263e-5b21-2c41-53e4ea7fe947", - "destinationType": "TempOrder", - "destinationId": "3a1d4b13-260c-9239-5c8a-ecb6fd96dc86", - "materialId": "3a1d4b12-67fc-131a-82ff-87e9e7708f9f", - "materialName": "test2", - "materialCode": "0007-00212", - "quantity": "1块", - "materialTypeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4", - "materialTypeCode": "0007", - "materialTypeMode": "Sample", - "materialTypeName": "5ml分液瓶", - "locationId": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", - "locationCode": "5-1/2", - "locationShowName": "5-1/2" - } - ] - } - ], - "code": 1, - "message": "", - "timestamp": 1761891208109 -} - -25-10-31 [14:27:52,203] [ERROR] 从Bioyond同步物料数据失败: 'BottleCarrier' object has no attribute 'tracker' [sync_from_external:83] [unilabos.utils.log.station] -Traceback (most recent call last): - File "C:\ML\GitHub\Uni-Lab-OS\unilabos\devices\workstation\bioyond_studio\station.py", line 73, in sync_from_external - unilab_resources = resource_bioyond_to_plr( - ^^^^^^^^^^^^^^^^^^^^^^^^ - File "C:\ML\GitHub\Uni-Lab-OS\unilabos\resources\graphio.py", line 661, in resource_bioyond_to_plr - bottle.tracker.liquids = [ - ^^^^^^^^^^^^^^ -AttributeError: 'BottleCarrier' object has no attribute 'tracker' \ No newline at end of file +[ + { + "id": "3a1d4b7e-4bdc-16bf-7169-f60350d03c7e", + "typeName": "配液瓶(小)板", + "code": "0001-00088", + "barCode": "", + "name": "test1", + "quantity": 1.0, + "lockQuantity": 0.0, + "unit": "块", + "status": 1, + "isUse": false, + "locations": [ + { + "id": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", + "whid": "3a19deae-2c79-05a3-9c76-8e6760424841", + "whName": "手动堆栈", + "code": "4", + "x": 2, + "y": 1, + "z": 1, + "quantity": 0 + } + ], + "detail": [ + { + "id": "3a1d4b7e-4bdc-12e8-4d26-dddc77b03f63", + "detailMaterialId": "3a1d4b7e-4bdc-4e9e-8a3c-e9ba4a26457e", + "code": null, + "name": "test1", + "quantity": "1", + "lockQuantity": "0", + "unit": "块", + "x": 1, + "y": 2, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + }, + { + "id": "3a1d4b7e-4bdc-35b6-22d4-e6f3235e1c27", + "detailMaterialId": "3a1d4b7e-4bdc-ce0f-1fbb-b88de76fce98", + "code": null, + "name": "test1", + "quantity": "1", + "lockQuantity": "0", + "unit": "块", + "x": 1, + "y": 1, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + } + ] + }, + { + "id": "3a1d4b7e-ee61-ae87-9cd0-31c7e6621b18", + "typeName": "5ml分液瓶板", + "code": "0010-00089", + "barCode": "", + "name": "test2", + "quantity": 1.0, + "lockQuantity": 0.0, + "unit": "块", + "status": 1, + "isUse": false, + "locations": [ + { + "id": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", + "whid": "3a19deae-2c79-05a3-9c76-8e6760424841", + "whName": "手动堆栈", + "code": "5", + "x": 2, + "y": 2, + "z": 1, + "quantity": 0 + } + ], + "detail": [ + { + "id": "3a1d4b7e-ee61-8fb3-9a39-2c2841c3c8d0", + "detailMaterialId": "3a1d4b7e-ee61-305c-fe30-2620017ca1bd", + "code": null, + "name": "test2", + "quantity": "1", + "lockQuantity": "0", + "unit": "块", + "x": 1, + "y": 1, + "z": 1, + "associateId": null, + "typeName": "5ml分液瓶", + "typeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4" + }, + { + "id": "3a1d4b7e-ee61-ef5f-a7d1-f9399a4d3145", + "detailMaterialId": "3a1d4b7e-ee61-2f1d-6969-202ad3cbe226", + "code": null, + "name": "test2", + "quantity": "1", + "lockQuantity": "0", + "unit": "块", + "x": 1, + "y": 2, + "z": 1, + "associateId": null, + "typeName": "5ml分液瓶", + "typeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4" + } + ] + } +] \ No newline at end of file diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py index c40945e7..092a87fe 100644 --- a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py +++ b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/bioyond_cell_workstation.py @@ -968,7 +968,7 @@ class BioyondCellWorkstation(BioyondWorkstation): logger.error(f"✗ 执行失败: {e}") return {"success": False, "error": str(e)} -def create_material( + def create_material( self, material_name: str, type_id: str, @@ -985,8 +985,7 @@ def create_material( 包含创建结果、物料ID以及入库结果的字典。 """ material_name = (material_name or "").strip() - if not material_name: - raise ValueError("material_name 不能为空") + resolved_type_id = (type_id or "").strip() # 优先从 SOLID_LIQUID_MAPPINGS 中获取模板数据 template = SOLID_LIQUID_MAPPINGS.get(material_name) @@ -1082,14 +1081,68 @@ def create_material( } -# -------------------------------- + def create_sample( + self, + name: str, + board_type: str, + bottle_type: str, + location_code: str + ) -> Dict[str, Any]: + """创建配液板物料并自动入库。 + Args: + material_name: 物料名称,支持 "5ml分液瓶板"/"5ml分液瓶"、"配液瓶(小)板"/"配液瓶(小)"。 + quantity: 主物料与明细的数量,默认 1。 + location_code: 库位编号,例如 "A01",将自动映射为 "手动堆栈" 下的 UUID。 + """ + carrier_type_id = MATERIAL_TYPE_MAPPINGS[board_type][1] + bottle_type_id = MATERIAL_TYPE_MAPPINGS[bottle_type][1] + location_id = WAREHOUSE_MAPPING["手动堆栈"]["site_uuids"][location_code] + + # 新建小瓶 + details = [] + for y in range(1, 5): + for x in range(1, 3): + details.append({ + "typeId": bottle_type_id, + "code": "", + "name": str(bottle_type) + str(x) + str(y), + "quantity": "1", + "x": x, + "y": y, + "z": 1, + "unit": "个", + "parameters": json.dumps({"unit": "个"}, ensure_ascii=False), + }) + + data = { + "typeId": carrier_type_id, + "code": "", + "barCode": "", + "name": name, + "unit": "块", + "parameters": json.dumps({"unit": "块"}, ensure_ascii=False), + "quantity": "1", + "details": details, + } + # print("xxx:",data) + create_result = self._post_lims("/api/lims/storage/material", data) + sample_uuid = create_result.get("data") + + final_result = self._post_lims("/api/lims/storage/inbound", { + "materialId": sample_uuid, + "locationId": location_id, + }) + return final_result + + if __name__ == "__main__": lab_registry.setup() ws = BioyondCellWorkstation() + ws.create_sample(name="test", board_type="配液瓶(小)板", bottle_type="配液瓶(小)", location_code="B01") # logger.info(ws.scheduler_stop()) - logger.info(ws.scheduler_start()) + # logger.info(ws.scheduler_start()) # results = ws.create_materials(SOLID_LIQUID_MAPPINGS) # for r in results: @@ -1098,11 +1151,11 @@ if __name__ == "__main__": # result = ws.create_and_inbound_materials() # 继续后续流程 - logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 - # # 使用正斜杠或 Path 对象来指定文件路径 - excel_path = Path("unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\2025092701.xlsx") - logger.info(ws.create_orders(excel_path)) - logger.info(ws.transfer_3_to_2_to_1()) + # logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 + # # # 使用正斜杠或 Path 对象来指定文件路径 + # excel_path = Path("unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\2025092701.xlsx") + # logger.info(ws.create_orders(excel_path)) + # logger.info(ws.transfer_3_to_2_to_1()) # logger.info(ws.transfer_1_to_2()) # logger.info(ws.scheduler_start()) diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx index 844fc84d932f618891abbdf6efdefc4bcf16289f..abaf145e68e8910ddf877637d20ff10748c489ad 100644 GIT binary patch delta 109 zcmbQSmT~`D#tr?d0{b^wimsPpU;tqW1_p-727wZrx2viNO%~8*V=^+ETx_Ac`L+26 z8BVy$$xlMU8Pg`)gxWAkyG*VMwPx&^ye-s%@&Dxap|*_ilXb)F7+*}z2~%U!3jvzQ F006IYBS-)M delta 100 zcmdnLmT|^f#tr?de40E$H#?*l7(iHpVX|DP)aLD~YC@Cit$8;KT6~n@ge#cL9U9JP vKRG$nhUts* Date: Fri, 31 Oct 2025 19:02:06 +0800 Subject: [PATCH 6/7] Delete button_battery_station.py --- .../button_battery_station.py | 1006 ----------------- 1 file changed, 1006 deletions(-) delete mode 100644 unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py diff --git a/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py b/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py deleted file mode 100644 index eae09b84..00000000 --- a/unilabos/devices/workstation/coin_cell_assembly/button_battery_station.py +++ /dev/null @@ -1,1006 +0,0 @@ -""" -纽扣电池组装工作站物料类定义 -Button Battery Assembly Station Resource Classes -""" - -from __future__ import annotations - -from collections import OrderedDict -from typing import Any, Dict, List, Optional, TypedDict, Union, cast - -from pylabrobot.resources.coordinate import Coordinate -from pylabrobot.resources.container import Container -from pylabrobot.resources.deck import Deck -from pylabrobot.resources.itemized_resource import ItemizedResource -from pylabrobot.resources.resource import Resource -from pylabrobot.resources.resource_stack import ResourceStack -from pylabrobot.resources.tip_rack import TipRack, TipSpot -from pylabrobot.resources.trash import Trash -from pylabrobot.resources.utils import create_ordered_items_2d - - -class ElectrodeSheetState(TypedDict): - diameter: float # 直径 (mm) - thickness: float # 厚度 (mm) - mass: float # 质量 (g) - material_type: str # 材料类型(正极、负极、隔膜、弹片、垫片、铝箔等) - height: float - electrolyte_name: str - data_electrolyte_code: str - open_circuit_voltage: float - assembly_pressure: float - electrolyte_volume: float - - info: Optional[str] # 附加信息 - -class ElectrodeSheet(Resource): - """极片类 - 包含正负极片、隔膜、弹片、垫片、铝箔等所有片状材料""" - - def __init__( - self, - name: str = "极片", - size_x=10, - size_y=10, - size_z=10, - category: str = "electrode_sheet", - model: Optional[str] = None, - ): - """初始化极片 - - Args: - name: 极片名称 - category: 类别 - model: 型号 - """ - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - category=category, - model=model, - ) - self._unilabos_state: ElectrodeSheetState = ElectrodeSheetState( - diameter=14, - thickness=0.1, - mass=0.5, - material_type="copper", - info=None - ) - - # TODO: 这个还要不要?给self._unilabos_state赋值的? - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - #序列化 - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - -# TODO: 这个应该只能放一个极片 -class MaterialHoleState(TypedDict): - diameter: int - depth: int - max_sheets: int - info: Optional[str] # 附加信息 - -class MaterialHole(Resource): - """料板洞位类""" - children: List[ElectrodeSheet] = [] - - def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - category: str = "material_hole", - **kwargs - ): - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - category=category, - ) - self._unilabos_state: MaterialHoleState = MaterialHoleState( - diameter=20, - depth=10, - max_sheets=1, - info=None - ) - - def get_all_sheet_info(self): - info_list = [] - for sheet in self.children: - info_list.append(sheet._unilabos_state["info"]) - return info_list - - #这个函数函数好像没用,一般不会集中赋值质量 - def set_all_sheet_mass(self): - for sheet in self.children: - sheet._unilabos_state["mass"] = 0.5 # 示例:设置质量为0.5g - - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - #移动极片前先取出对象 - def get_sheet_with_name(self, name: str) -> Optional[ElectrodeSheet]: - for sheet in self.children: - if sheet.name == name: - return sheet - return None - - def has_electrode_sheet(self) -> bool: - """检查洞位是否有极片""" - return len(self.children) > 0 - - def assign_child_resource( - self, - resource: ElectrodeSheet, - location: Optional[Coordinate], - reassign: bool = True, - ): - """放置极片""" - # TODO: 这里要改,diameter找不到,加入._unilabos_state后应该没问题 - #if resource._unilabos_state["diameter"] > self._unilabos_state["diameter"]: - # raise ValueError(f"极片直径 {resource._unilabos_state['diameter']} 超过洞位直径 {self._unilabos_state['diameter']}") - #if len(self.children) >= self._unilabos_state["max_sheets"]: - # raise ValueError(f"洞位已满,无法放置更多极片") - super().assign_child_resource(resource, location, reassign) - - # 根据children的编号取物料对象。 - def get_electrode_sheet_info(self, index: int) -> ElectrodeSheet: - return self.children[index] - - - -class MaterialPlateState(TypedDict): - hole_spacing_x: float - hole_spacing_y: float - hole_diameter: float - info: Optional[str] # 附加信息 - -class MaterialPlate(ItemizedResource[MaterialHole]): - """料板类 - 4x4个洞位,每个洞位放1个极片""" - - children: List[MaterialHole] - - def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - ordered_items: Optional[Dict[str, MaterialHole]] = None, - ordering: Optional[OrderedDict[str, str]] = None, - category: str = "material_plate", - model: Optional[str] = None, - fill: bool = False - ): - """初始化料板 - - Args: - name: 料板名称 - size_x: 长度 (mm) - size_y: 宽度 (mm) - size_z: 高度 (mm) - hole_diameter: 洞直径 (mm) - hole_depth: 洞深度 (mm) - hole_spacing_x: X方向洞位间距 (mm) - hole_spacing_y: Y方向洞位间距 (mm) - number: 编号 - category: 类别 - model: 型号 - """ - self._unilabos_state: MaterialPlateState = MaterialPlateState( - hole_spacing_x=24.0, - hole_spacing_y=24.0, - hole_diameter=20.0, - info="", - ) - # 创建4x4的洞位 - # TODO: 这里要改,对应不同形状 - holes = create_ordered_items_2d( - klass=MaterialHole, - num_items_x=4, - num_items_y=4, - dx=(size_x - 4 * self._unilabos_state["hole_spacing_x"]) / 2, # 居中 - dy=(size_y - 4 * self._unilabos_state["hole_spacing_y"]) / 2, # 居中 - dz=size_z, - item_dx=self._unilabos_state["hole_spacing_x"], - item_dy=self._unilabos_state["hole_spacing_y"], - size_x = 16, - size_y = 16, - size_z = 16, - ) - if fill: - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - ordered_items=holes, - category=category, - model=model, - ) - else: - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - ordered_items=ordered_items, - ordering=ordering, - category=category, - model=model, - ) - - def update_locations(self): - # TODO:调多次相加 - holes = create_ordered_items_2d( - klass=MaterialHole, - num_items_x=4, - num_items_y=4, - dx=(self._size_x - 3 * self._unilabos_state["hole_spacing_x"]) / 2, # 居中 - dy=(self._size_y - 3 * self._unilabos_state["hole_spacing_y"]) / 2, # 居中 - dz=self._size_z, - item_dx=self._unilabos_state["hole_spacing_x"], - item_dy=self._unilabos_state["hole_spacing_y"], - size_x = 1, - size_y = 1, - size_z = 1, - ) - for item, original_item in zip(holes.items(), self.children): - original_item.location = item[1].location - - -class PlateSlot(ResourceStack): - """板槽位类 - 1个槽上能堆放8个板,移板只能操作最上方的板""" - - def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - max_plates: int = 8, - category: str = "plate_slot", - model: Optional[str] = None - ): - """初始化板槽位 - - Args: - name: 槽位名称 - max_plates: 最大板数量 - category: 类别 - """ - super().__init__( - name=name, - direction="z", # Z方向堆叠 - resources=[], - ) - self.max_plates = max_plates - self.category = category - - def can_add_plate(self) -> bool: - """检查是否可以添加板""" - return len(self.children) < self.max_plates - - def add_plate(self, plate: MaterialPlate) -> None: - """添加料板""" - if not self.can_add_plate(): - raise ValueError(f"槽位 {self.name} 已满,无法添加更多板") - self.assign_child_resource(plate) - - def get_top_plate(self) -> MaterialPlate: - """获取最上方的板""" - if len(self.children) == 0: - raise ValueError(f"槽位 {self.name} 为空") - return cast(MaterialPlate, self.get_top_item()) - - def take_top_plate(self) -> MaterialPlate: - """取出最上方的板""" - top_plate = self.get_top_plate() - self.unassign_child_resource(top_plate) - return top_plate - - def can_access_for_picking(self) -> bool: - """检查是否可以进行取料操作(只有最上方的板能进行取料操作)""" - return len(self.children) > 0 - - def serialize(self) -> dict: - return { - **super().serialize(), - "max_plates": self.max_plates, - } - - -class ClipMagazineHole(Container): - """子弹夹洞位类""" - - def __init__( - self, - name: str, - diameter: float, - depth: float, - max_sheets: int = 100, - category: str = "clip_magazine_hole", - ): - """初始化子弹夹洞位 - - Args: - name: 洞位名称 - diameter: 洞直径 (mm) - depth: 洞深度 (mm) - max_sheets: 最大极片数量 - category: 类别 - """ - super().__init__( - name=name, - size_x=diameter, - size_y=diameter, - size_z=depth, - category=category, - ) - self.diameter = diameter - self.depth = depth - self.max_sheets = max_sheets - self._sheets: List[ElectrodeSheet] = [] - - def can_add_sheet(self, sheet: ElectrodeSheet) -> bool: - """检查是否可以添加极片""" - return (len(self._sheets) < self.max_sheets and - sheet.diameter <= self.diameter) - - def add_sheet(self, sheet: ElectrodeSheet) -> None: - """添加极片""" - if not self.can_add_sheet(sheet): - raise ValueError(f"无法向洞位 {self.name} 添加极片") - self._sheets.append(sheet) - - def take_sheet(self) -> ElectrodeSheet: - """取出极片""" - if len(self._sheets) == 0: - raise ValueError(f"洞位 {self.name} 没有极片") - return self._sheets.pop() - - def get_sheet_count(self) -> int: - """获取极片数量""" - return len(self._sheets) - - def serialize_state(self) -> Dict[str, Any]: - return { - "sheet_count": len(self._sheets), - "sheets": [sheet.serialize() for sheet in self._sheets], - } - -# TODO: 这个要改 -class ClipMagazine(Resource): - """子弹夹类 - 有6个洞位,每个洞位放多个极片""" - - def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - hole_spacing: float = 25.0, - max_sheets_per_hole: int = 100, - category: str = "clip_magazine", - model: Optional[str] = None, - ): - """初始化子弹夹 - - Args: - name: 子弹夹名称 - size_x: 长度 (mm) - size_y: 宽度 (mm) - size_z: 高度 (mm) - hole_diameter: 洞直径 (mm) - hole_depth: 洞深度 (mm) - hole_spacing: 洞位间距 (mm) - max_sheets_per_hole: 每个洞位最大极片数量 - category: 类别 - model: 型号 - """ - # 创建6个洞位,排成2x3布局 - holes = create_ordered_items_2d( - klass=ClipMagazineHole, - num_items_x=3, - num_items_y=2, - dx=(size_x - 2 * hole_spacing) / 2, # 居中 - dy=(size_y - hole_spacing) / 2, # 居中 - dz=size_z - 0, - item_dx=hole_spacing, - item_dy=hole_spacing, - diameter=0, - depth=0, - ) - - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - ordered_items=holes, - category=category, - model=model, - ) - - self.hole_diameter = hole_diameter - self.hole_depth = hole_depth - self.max_sheets_per_hole = max_sheets_per_hole - - def serialize(self) -> dict: - return { - **super().serialize(), - "hole_diameter": self.hole_diameter, - "hole_depth": self.hole_depth, - "max_sheets_per_hole": self.max_sheets_per_hole, - } -#是一种类型注解,不用self -class BatteryState(TypedDict): - """电池状态字典""" - diameter: float - height: float - assembly_pressure: float - electrolyte_volume: float - electrolyte_name: str - -class Battery(Resource): - """电池类 - 可容纳极片""" - children: List[ElectrodeSheet] = [] - - def __init__( - self, - name: str, - size_x=1, - size_y=1, - size_z=1, - category: str = "battery", - ): - """初始化电池 - - Args: - name: 电池名称 - diameter: 直径 (mm) - height: 高度 (mm) - max_volume: 最大容量 (μL) - barcode: 二维码编号 - category: 类别 - model: 型号 - """ - super().__init__( - name=name, - size_x=1, - size_y=1, - size_z=1, - category=category, - ) - self._unilabos_state: BatteryState = BatteryState( - diameter = 1.0, - height = 1.0, - assembly_pressure = 1.0, - electrolyte_volume = 1.0, - electrolyte_name = "DP001" - ) - - def add_electrolyte_with_bottle(self, bottle: Bottle) -> bool: - to_add_name = bottle._unilabos_state["electrolyte_name"] - if bottle.aspirate_electrolyte(10): - if self.add_electrolyte(to_add_name, 10): - pass - else: - bottle._unilabos_state["electrolyte_volume"] += 10 - - def set_electrolyte(self, name: str, volume: float) -> None: - """设置电解液信息""" - self._unilabos_state["electrolyte_name"] = name - self._unilabos_state["electrolyte_volume"] = volume - #这个应该没用,不会有加了后再加的事情 - def add_electrolyte(self, name: str, volume: float) -> bool: - """添加电解液信息""" - if name != self._unilabos_state["electrolyte_name"]: - return False - self._unilabos_state["electrolyte_volume"] += volume - - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - -# 电解液作为属性放进去 - -class BatteryPressSlotState(TypedDict): - """电池状态字典""" - diameter: float =20.0 - depth: float = 4.0 - -class BatteryPressSlot(Resource): - """电池压制槽类 - 设备,可容纳一个电池""" - children: List[Battery] = [] - - def __init__( - self, - name: str = "BatteryPressSlot", - category: str = "battery_press_slot", - ): - """初始化电池压制槽 - - Args: - name: 压制槽名称 - diameter: 直径 (mm) - depth: 深度 (mm) - category: 类别 - model: 型号 - """ - super().__init__( - name=name, - size_x=10, - size_y=12, - size_z=13, - category=category, - ) - self._unilabos_state: BatteryPressSlotState = BatteryPressSlotState() - - def has_battery(self) -> bool: - """检查是否有电池""" - return len(self.children) > 0 - - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - - def assign_child_resource( - self, - resource: Battery, - location: Optional[Coordinate], - reassign: bool = True, - ): - """放置极片""" - # TODO: 让高京看下槽位只有一个电池时是否这么写。 - if self.has_battery(): - raise ValueError(f"槽位已含有一个电池,无法再放置其他电池") - super().assign_child_resource(resource, location, reassign) - - # 根据children的编号取物料对象。 - def get_battery_info(self, index: int) -> Battery: - return self.children[0] - -# TODO:这个移液枪架子看一下从哪继承 -class TipBox64State(TypedDict): - """电池状态字典""" - tip_diameter: float = 5.0 - tip_length: float = 50.0 - with_tips: bool = True - -class TipBox64(TipRack): - """64孔枪头盒类""" - - children: List[TipSpot] = [] - def __init__( - self, - name: str, - size_x: float = 127.8, - size_y: float = 85.5, - size_z: float = 60.0, - category: str = "tip_box_64", - model: Optional[str] = None, - ): - """初始化64孔枪头盒 - - Args: - name: 枪头盒名称 - size_x: 长度 (mm) - size_y: 宽度 (mm) - size_z: 高度 (mm) - tip_diameter: 枪头直径 (mm) - tip_length: 枪头长度 (mm) - category: 类别 - model: 型号 - with_tips: 是否带枪头 - """ - from pylabrobot.resources.tip import Tip - - # 创建8x8=64个枪头位 - def make_tip(): - return Tip( - has_filter=False, - total_tip_length=20.0, - maximal_volume=1000, # 1mL - fitting_depth=8.0, - ) - - tip_spots = create_ordered_items_2d( - klass=TipSpot, - num_items_x=8, - num_items_y=8, - dx=8.0, - dy=8.0, - dz=0.0, - item_dx=9.0, - item_dy=9.0, - size_x=10, - size_y=10, - size_z=0.0, - make_tip=make_tip, - ) - self._unilabos_state: WasteTipBoxstate = WasteTipBoxstate() - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - ordered_items=tip_spots, - category=category, - model=model, - with_tips=True, - ) - - - -class WasteTipBoxstate(TypedDict): - """"废枪头盒状态字典""" - max_tips: int = 100 - tip_count: int = 0 - -#枪头不是一次性的(同一溶液则反复使用),根据寄存器判断 -class WasteTipBox(Trash): - """废枪头盒类 - 100个枪头容量""" - - def __init__( - self, - name: str, - size_x: float = 127.8, - size_y: float = 85.5, - size_z: float = 60.0, - category: str = "waste_tip_box", - model: Optional[str] = None, - ): - """初始化废枪头盒 - - Args: - name: 废枪头盒名称 - size_x: 长度 (mm) - size_y: 宽度 (mm) - size_z: 高度 (mm) - max_tips: 最大枪头容量 - category: 类别 - model: 型号 - """ - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - category=category, - model=model, - ) - self._unilabos_state: WasteTipBoxstate = WasteTipBoxstate() - - def add_tip(self) -> None: - """添加废枪头""" - if self._unilabos_state["tip_count"] >= self._unilabos_state["max_tips"]: - raise ValueError(f"废枪头盒 {self.name} 已满") - self._unilabos_state["tip_count"] += 1 - - def get_tip_count(self) -> int: - """获取枪头数量""" - return self._unilabos_state["tip_count"] - - def empty(self) -> None: - """清空废枪头盒""" - self._unilabos_state["tip_count"] = 0 - - - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - - -class BottleRackState(TypedDict): - """ bottle_diameter: 瓶子直径 (mm) - bottle_height: 瓶子高度 (mm) - position_spacing: 位置间距 (mm)""" - bottle_diameter: float - bottle_height: float - name_to_index: dict - - - -class BottleRack(Resource): - """瓶架类 - 12个待配位置+12个已配位置""" - children: List[Bottle] = [] - - def __init__( - self, - name: str, - size_x: float, - size_y: float, - size_z: float, - category: str = "bottle_rack", - model: Optional[str] = None, - ): - """初始化瓶架 - - Args: - name: 瓶架名称 - size_x: 长度 (mm) - size_y: 宽度 (mm) - size_z: 高度 (mm) - category: 类别 - model: 型号 - """ - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - category=category, - model=model, - ) - # TODO: 添加瓶位坐标映射 - self.index_to_pos = { - 0: Coordinate.zero(), - 1: Coordinate(x=1, y=2, z=3) # 添加 - } - self.name_to_index = {} - self.name_to_pos = {} - - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - - # TODO: 这里有些问题要重新写一下 - def assign_child_resource(self, resource: Bottle, location=Coordinate.zero(), reassign = True): - assert len(self.children) <= 12, "瓶架已满,无法添加更多瓶子" - index = len(self.children) - location = Coordinate(x=20 + (index % 4) * 15, y=20 + (index // 4) * 15, z=0) - self.name_to_pos[resource.name] = location - self.name_to_index[resource.name] = index - return super().assign_child_resource(resource, location, reassign) - - def assign_child_resource_by_index(self, resource: Bottle, index: int): - assert 0 <= index < 12, "无效的瓶子索引" - self.name_to_index[resource.name] = index - location = self.index_to_pos[index] - return super().assign_child_resource(resource, location) - - def unassign_child_resource(self, resource: Bottle): - super().unassign_child_resource(resource) - self.index_to_pos.pop(self.name_to_index.pop(resource.name, None), None) - - # def serialize(self): - # self.children.sort(key=lambda x: self.name_to_index.get(x.name, 0)) - # return super().serialize() - - -class BottleState(TypedDict): - diameter: float - height: float - electrolyte_name: str - electrolyte_volume: float - max_volume: float - -class Bottle(Resource): - """瓶子类 - 容纳电解液""" - - def __init__( - self, - name: str, - category: str = "bottle", - ): - """初始化瓶子 - - Args: - name: 瓶子名称 - diameter: 直径 (mm) - height: 高度 (mm) - max_volume: 最大体积 (μL) - barcode: 二维码 - category: 类别 - model: 型号 - """ - super().__init__( - name=name, - size_x=1, - size_y=1, - size_z=1, - category=category, - ) - self._unilabos_state: BottleState = BottleState() - - def aspirate_electrolyte(self, volume: float) -> bool: - current_volume = self._unilabos_state["electrolyte_volume"] - assert current_volume > volume, f"Cannot aspirate {volume}μL, only {current_volume}μL available." - self._unilabos_state["electrolyte_volume"] -= volume - return True - - def load_state(self, state: Dict[str, Any]) -> None: - """格式不变""" - super().load_state(state) - self._unilabos_state = state - - def serialize_state(self) -> Dict[str, Dict[str, Any]]: - """格式不变""" - data = super().serialize_state() - data.update(self._unilabos_state) # Container自身的信息,云端物料将保存这一data,本地也通过这里的data进行读写,当前类用来表示这个物料的长宽高大小的属性,而data(state用来表示物料的内容,细节等) - return data - -class CoincellDeck(Deck): - """纽扣电池组装工作站台面类""" - - def __init__( - self, - name: str = "coin_cell_deck", - size_x: float = 1620.0, # 3.66m - size_y: float = 1270.0, # 1.23m - size_z: float = 500.0, - origin: Coordinate = Coordinate(0, 0, 0), - category: str = "coin_cell_deck", - ): - """初始化纽扣电池组装工作站台面 - - Args: - name: 台面名称 - size_x: 长度 (mm) - 3.66m - size_y: 宽度 (mm) - 1.23m - size_z: 高度 (mm) - origin: 原点坐标 - category: 类别 - """ - super().__init__( - name=name, - size_x=size_x, - size_y=size_y, - size_z=size_z, - origin=origin, - category=category, - ) - -#if __name__ == "__main__": -# # 转移极片的测试代码 -# deck = CoincellDeck("coin_cell_deck") -# ban_cao_wei = PlateSlot("ban_cao_wei", max_plates=8) -# deck.assign_child_resource(ban_cao_wei, Coordinate(x=0, y=0, z=0)) -# -# plate_1 = MaterialPlate("plate_1", 1,1,1, fill=True) -# for i, hole in enumerate(plate_1.children): -# sheet = ElectrodeSheet(f"hole_{i}_sheet_1") -# sheet._unilabos_state = { -# "diameter": 14, -# "info": "NMC", -# "mass": 5.0, -# "material_type": "positive_electrode", -# "thickness": 0.1 -# } -# hole._unilabos_state = { -# "depth": 1.0, -# "diameter": 14, -# "info": "", -# "max_sheets": 1 -# } -# hole.assign_child_resource(sheet, Coordinate.zero()) -# plate_1._unilabos_state = { -# "hole_spacing_x": 20.0, -# "hole_spacing_y": 20.0, -# "hole_diameter": 5, -# "info": "这是第一块料板" -# } -# plate_1.update_locations() -# ban_cao_wei.assign_child_resource(plate_1, Coordinate.zero()) -# # zi_dan_jia = ClipMagazine("zi_dan_jia", 1, 1, 1) -# # deck.assign_child_resource(ban_cao_wei, Coordinate(x=200, y=200, z=0)) -# -# from unilabos.resources.graphio import * -# A = tree_to_list([resource_plr_to_ulab(deck)]) -# with open("test.json", "w") as f: -# json.dump(A, f) -# -# -#def get_plate_with_14mm_hole(name=""): -# plate = MaterialPlate(name=name) -# for i in range(4): -# for j in range(4): -# hole = MaterialHole(f"{i+1}x{j+1}") -# hole._unilabos_state["diameter"] = 14 -# hole._unilabos_state["max_sheets"] = 1 -# plate.assign_child_resource(hole) -# return plate - -import json - -if __name__ == "__main__": - #electrode1 = BatteryPressSlot() - #print(electrode1.get_size_x()) - #print(electrode1.get_size_y()) - #print(electrode1.get_size_z()) - #jipian = ElectrodeSheet() - #jipian._unilabos_state["diameter"] = 18 - #print(jipian.serialize()) - #print(jipian.serialize_state()) - - deck = CoincellDeck(size_x=1000, - size_y=1000, - size_z=900) - - #liaopan = TipBox64(name="liaopan") - - #创建一个4*4的物料板 - liaopan1 = MaterialPlate(name="liaopan1", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) - #把物料板放到桌子上 - deck.assign_child_resource(liaopan1, Coordinate(x=0, y=0, z=0)) - #创建一个极片 - for i in range(16): - jipian = ElectrodeSheet(name=f"jipian1_{i}", size_x= 12, size_y=12, size_z=0.1) - liaopan1.children[i].assign_child_resource(jipian, location=None) -# - #创建一个4*4的物料板 - liaopan2 = MaterialPlate(name="liaopan2", size_x=120.8, size_y=120.5, size_z=10.0, fill=True) - #把物料板放到桌子上 - deck.assign_child_resource(liaopan2, Coordinate(x=500, y=0, z=0)) - - #创建一个4*4的物料板 - liaopan3 = MaterialPlate(name="电池料盘", size_x=120.8, size_y=160.5, size_z=10.0, fill=True) - #把物料板放到桌子上 - deck.assign_child_resource(liaopan3, Coordinate(x=100, y=100, z=0)) - - - - #liaopan.children[3].assign_child_resource(jipian, location=None) - print(deck) - - - from unilabos.resources.graphio import convert_resources_from_type - from unilabos.config.config import BasicConfig - BasicConfig.ak = "4d5ce6ae-7234-4639-834e-93899b9caf94" - BasicConfig.sk = "505d3b0a-620e-459a-9905-1efcffce382a" - from unilabos.app.web.client import http_client - - resources = convert_resources_from_type([deck], [Resource]) - json.dump({"nodes": resources, "links": []}, open("button_battery_station_resources_unilab.json", "w"), indent=2) - - - #print(resources) - http_client.remote_addr = "https://uni-lab.test.bohrium.com/api/v1" - - http_client.resource_add(resources) \ No newline at end of file From 03745c5d0875f2ca22c29465317c98f7a1258f0f Mon Sep 17 00:00:00 2001 From: lixinyu1011 <674842481@qq.com> Date: Sat, 1 Nov 2025 10:37:45 +0800 Subject: [PATCH 7/7] byxinyu111 --- new_cellconfig3c.json | 38 ++++ test/resources/test.json | 170 +++++++++++------- test/resources/test_resourcetreeset.py | 7 +- .../bioyond_cell/2025092701.xlsx | Bin 18159 -> 18159 bytes .../bioyond_cell/bioyond_cell_workstation.py | 76 +------- .../bioyond_cell/material_template.xlsx | Bin 22207 -> 22149 bytes .../workstation/bioyond_studio/config.py | 5 +- .../coin_cell_assembly/coin_cell_assembly.py | 3 +- unilabos/registry/devices/bioyond_cell.yaml | 77 +++++++- 9 files changed, 230 insertions(+), 146 deletions(-) create mode 100644 new_cellconfig3c.json diff --git a/new_cellconfig3c.json b/new_cellconfig3c.json new file mode 100644 index 00000000..36190858 --- /dev/null +++ b/new_cellconfig3c.json @@ -0,0 +1,38 @@ +{ + "nodes": [ + { + "id": "bioyond_cell_workstation", + "name": "配液分液工站", + "children": [ + ], + "parent": null, + "type": "device", + "class": "bioyond_cell", + "config": { + "protocol_type": [], + "station_resource": {} + }, + "data": {} + }, + { + "id": "BatteryStation", + "name": "扣电工作站", + "children": [ + "coin_cell_deck" + ], + "parent": null, + "type": "device", + "class": "coincellassemblyworkstation_device", + "position": { + "x": 600, + "y": 400, + "z": 0 + }, + "config": { + "debug_mode": false, + "protocol_type": [] + } + } + ], + "links": [] +} \ No newline at end of file diff --git a/test/resources/test.json b/test/resources/test.json index ee1be0f1..95a29d20 100644 --- a/test/resources/test.json +++ b/test/resources/test.json @@ -1,22 +1,22 @@ [ { - "id": "3a1d4b7e-4bdc-16bf-7169-f60350d03c7e", + "id": "3a1d4c14-a9fb-d7dc-9e96-7a3ad6e50219", "typeName": "配液瓶(小)板", - "code": "0001-00088", + "code": "0001-00093", "barCode": "", - "name": "test1", - "quantity": 1.0, + "name": "test", + "quantity": 2.0, "lockQuantity": 0.0, "unit": "块", "status": 1, "isUse": false, "locations": [ { - "id": "3a19deae-2c7a-2426-6d71-e9de3cb158b1", + "id": "3a19deae-2c7a-36f5-5e41-02c5b66feaea", "whid": "3a19deae-2c79-05a3-9c76-8e6760424841", "whName": "手动堆栈", - "code": "4", - "x": 2, + "code": "1", + "x": 1, "y": 1, "z": 1, "quantity": 0 @@ -24,13 +24,58 @@ ], "detail": [ { - "id": "3a1d4b7e-4bdc-12e8-4d26-dddc77b03f63", - "detailMaterialId": "3a1d4b7e-4bdc-4e9e-8a3c-e9ba4a26457e", - "code": null, - "name": "test1", + "id": "3a1d4c14-a9fc-1daa-71fa-146cb1ccb930", + "detailMaterialId": "3a1d4c14-a9fc-4f38-4c48-68486c391c42", + "code": "0001-00093 - 05", + "name": "配液瓶(小)", "quantity": "1", "lockQuantity": "0", - "unit": "块", + "unit": "个", + "x": 1, + "y": 3, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + }, + { + "id": "3a1d4c14-a9fc-3659-ea61-cd587da9e131", + "detailMaterialId": "3a1d4c14-a9fc-018f-93e5-c49343d37758", + "code": "0001-00093 - 08", + "name": "配液瓶(小)", + "quantity": "1", + "lockQuantity": "0", + "unit": "个", + "x": 2, + "y": 4, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + }, + { + "id": "3a1d4c14-a9fc-3f94-de83-979d2646e313", + "detailMaterialId": "3a1d4c14-a9fc-9987-c0ef-4b7cbad49e6b", + "code": "0001-00093 - 01", + "name": "配液瓶(小)", + "quantity": "1", + "lockQuantity": "0", + "unit": "个", + "x": 1, + "y": 1, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + }, + { + "id": "3a1d4c14-a9fc-8c35-6b25-913b11dbaf4e", + "detailMaterialId": "3a1d4c14-a9fc-9a83-865b-0c26ea5e8cc4", + "code": "0001-00093 - 03", + "name": "配液瓶(小)", + "quantity": "1", + "lockQuantity": "0", + "unit": "个", "x": 1, "y": 2, "z": 1, @@ -39,75 +84,64 @@ "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" }, { - "id": "3a1d4b7e-4bdc-35b6-22d4-e6f3235e1c27", - "detailMaterialId": "3a1d4b7e-4bdc-ce0f-1fbb-b88de76fce98", - "code": null, - "name": "test1", + "id": "3a1d4c14-a9fc-b41f-e968-64953bfddccd", + "detailMaterialId": "3a1d4c14-a9fc-daf7-9d64-e5ec8d3ae0e2", + "code": "0001-00093 - 07", + "name": "配液瓶(小)", "quantity": "1", "lockQuantity": "0", - "unit": "块", + "unit": "个", "x": 1, + "y": 4, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + }, + { + "id": "3a1d4c14-a9fc-c20f-c26e-b1bb2cdc3bca", + "detailMaterialId": "3a1d4c14-a9fc-673b-ac83-aaaf71287f1f", + "code": "0001-00093 - 06", + "name": "配液瓶(小)", + "quantity": "1", + "lockQuantity": "0", + "unit": "个", + "x": 2, + "y": 3, + "z": 1, + "associateId": null, + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" + }, + { + "id": "3a1d4c14-a9fc-cf21-059c-fde361d82b6f", + "detailMaterialId": "3a1d4c14-a9fc-25b1-e736-6b0d8dac0fae", + "code": "0001-00093 - 02", + "name": "配液瓶(小)", + "quantity": "1", + "lockQuantity": "0", + "unit": "个", + "x": 2, "y": 1, "z": 1, "associateId": null, "typeName": "配液瓶(小)", "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" - } - ] - }, - { - "id": "3a1d4b7e-ee61-ae87-9cd0-31c7e6621b18", - "typeName": "5ml分液瓶板", - "code": "0010-00089", - "barCode": "", - "name": "test2", - "quantity": 1.0, - "lockQuantity": 0.0, - "unit": "块", - "status": 1, - "isUse": false, - "locations": [ + }, { - "id": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3", - "whid": "3a19deae-2c79-05a3-9c76-8e6760424841", - "whName": "手动堆栈", - "code": "5", + "id": "3a1d4c14-a9fc-d732-2b93-9b2bd2bf581b", + "detailMaterialId": "3a1d4c14-a9fc-7f5d-b6b6-8bcb2e15f320", + "code": "0001-00093 - 04", + "name": "配液瓶(小)", + "quantity": "1", + "lockQuantity": "0", + "unit": "个", "x": 2, "y": 2, "z": 1, - "quantity": 0 - } - ], - "detail": [ - { - "id": "3a1d4b7e-ee61-8fb3-9a39-2c2841c3c8d0", - "detailMaterialId": "3a1d4b7e-ee61-305c-fe30-2620017ca1bd", - "code": null, - "name": "test2", - "quantity": "1", - "lockQuantity": "0", - "unit": "块", - "x": 1, - "y": 1, - "z": 1, "associateId": null, - "typeName": "5ml分液瓶", - "typeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4" - }, - { - "id": "3a1d4b7e-ee61-ef5f-a7d1-f9399a4d3145", - "detailMaterialId": "3a1d4b7e-ee61-2f1d-6969-202ad3cbe226", - "code": null, - "name": "test2", - "quantity": "1", - "lockQuantity": "0", - "unit": "块", - "x": 1, - "y": 2, - "z": 1, - "associateId": null, - "typeName": "5ml分液瓶", - "typeId": "3a192c2a-ebb7-58a1-480d-8b3863bf74f4" + "typeName": "配液瓶(小)", + "typeId": "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb" } ] } diff --git a/test/resources/test_resourcetreeset.py b/test/resources/test_resourcetreeset.py index b7602ed1..99c6a984 100644 --- a/test/resources/test_resourcetreeset.py +++ b/test/resources/test_resourcetreeset.py @@ -9,6 +9,7 @@ from unilabos.ros.nodes.resource_tracker import ResourceTreeSet from unilabos.registry.registry import lab_registry from unilabos.resources.bioyond.decks import BIOYOND_PolymerReactionStation_Deck +from unilabos.resources.bioyond.decks import YB_Deck lab_registry.setup() @@ -16,6 +17,8 @@ lab_registry.setup() type_mapping = { "加样头(大)": ("YB_jia_yang_tou_da_1X1_carrier", "3a190ca0-b2f6-9aeb-8067-547e72c11469"), "液": ("YB_1BottleCarrier", "3a190ca1-2add-2b23-f8e1-bbd348b7f790"), + "配液瓶(小)板": ("YB_6x_SmallSolutionBottleCarrier", "3a190c8b-3284-af78-d29f-9a69463ad047"), + "配液瓶(小)": ("YB_pei_ye_xiao_Bottler", "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb"), } @@ -56,10 +59,10 @@ def bioyond_materials_liquidhandling_2() -> list[dict]: def test_resourcetreeset_from_plr() -> list[dict]: # 直接加载 bioyond_materials_reaction.json 文件 current_dir = os.path.dirname(os.path.abspath(__file__)) - json_path = os.path.join(current_dir, "YB_materials_info.json") + json_path = os.path.join(current_dir, "test.json") with open(json_path, "r", encoding="utf-8") as f: materials = json.load(f) - deck = BIOYOND_PolymerReactionStation_Deck("test_deck") + deck = YB_Deck("test_deck") output = resource_bioyond_to_plr(materials, type_mapping=type_mapping, deck=deck) print(output) # print(deck.summary()) diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/2025092701.xlsx index d1e9d2f31a2385b1028209891e31c975ebc9744b..4147095ed2784e56ccc6f637aec02c2a575c2cc8 100644 GIT binary patch delta 66 zcmaFg%lN*RaYMfJj7zwu^!&0=Oo!_Av@&M*O~ R2K`N Dict[str, Any]: + def wait_for_order_finish(self, order_code: str, timeout: int = 36000) -> Dict[str, Any]: """ 等待指定 orderCode 的 /report/order_finish 报送。 Args: @@ -253,7 +253,7 @@ class BioyondCellWorkstation(BioyondWorkstation): def auto_feeding4to3( self, # ★ 修改点:默认模板路径 - xlsx_path: Optional[str] = "unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\material_template.xlsx", + xlsx_path: Optional[str] = "C:/ML/GitHub/Uni-Lab-OS/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx", # ---------------- WH4 - 加样头面 (Z=1, 12个点位) ---------------- WH4_x1_y1_z1_1_materialName: str = "", WH4_x1_y1_z1_1_quantity: float = 0.0, WH4_x2_y1_z1_2_materialName: str = "", WH4_x2_y1_z1_2_quantity: float = 0.0, @@ -391,73 +391,7 @@ class BioyondCellWorkstation(BioyondWorkstation): # 等待完成报送 result = self.wait_for_order_finish(order_code) return result - - - - # 3.30 自动化上料(老版本) - def auto_feeding4to3_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: - """ - 根据固定模板解析 Excel: - - 四号手套箱加样头面 (2-13行, 3-7列) - - 四号手套箱原液瓶面 (15-23行, 3-9列) - - 三号手套箱人工堆栈 (26-40行, 3-7列) - """ - path = Path(xlsx_path) - if not path.exists(): - raise FileNotFoundError(f"未找到 Excel 文件:{path}") - - try: - df = pd.read_excel(path, sheet_name=0, header=None, engine="openpyxl") - except Exception as e: - raise RuntimeError(f"读取 Excel 失败:{e}") - - items: List[Dict[str, Any]] = [] - - # 四号手套箱 - 加样头面(2-13行, 3-7列) - for _, row in df.iloc[1:13, 2:7].iterrows(): - item = { - "sourceWHName": "四号手套箱堆栈", - "posX": int(row[2]), - "posY": int(row[3]), - "posZ": int(row[4]), - "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", - "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, - } - if item["materialName"]: - items.append(item) - - # 四号手套箱 - 原液瓶面(15-23行, 3-9列) - for _, row in df.iloc[14:23, 2:9].iterrows(): - item = { - "sourceWHName": "四号手套箱堆栈", - "posX": int(row[2]), - "posY": int(row[3]), - "posZ": int(row[4]), - "materialName": str(row[5]).strip() if pd.notna(row[5]) else "", - "quantity": float(row[6]) if pd.notna(row[6]) else 0.0, - "materialType": str(row[7]).strip() if pd.notna(row[7]) else "", - "targetWH": str(row[8]).strip() if pd.notna(row[8]) else "", - } - if item["materialName"]: - items.append(item) - - # 三号手套箱人工堆栈(26-40行, 3-7列) - for _, row in df.iloc[25:40, 2:7].iterrows(): - item = { - "sourceWHName": "三号手套箱人工堆栈", - "posX": int(row[2]), - "posY": int(row[3]), - "posZ": int(row[4]), - "materialType": str(row[5]).strip() if pd.notna(row[5]) else "", - "materialId": str(row[6]).strip() if pd.notna(row[6]) else "", - "quantity": 1 # 默认数量1 - } - if item["materialId"] or item["materialType"]: - items.append(item) - - response = self._post_lims("/api/lims/order/auto-feeding4to3", items) - self._wait_for_response_orders(response, "auto_feeding4to3_from_xlsx") - return response + def auto_batch_outbound_from_xlsx(self, xlsx_path: str) -> Dict[str, Any]: """ @@ -523,7 +457,7 @@ class BioyondCellWorkstation(BioyondWorkstation): }) response = self._post_lims("/api/lims/storage/auto-batch-out-bound", items) - self._wait_for_response_orders(response, "auto_batch_outbound_from_xlsx") + self.wait_for_response_orders(response, "auto_batch_outbound_from_xlsx") return response # 2.14 新建实验 @@ -1151,7 +1085,7 @@ if __name__ == "__main__": # result = ws.create_and_inbound_materials() # 继续后续流程 - # logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 + logger.info(ws.auto_feeding4to3()) #搬运物料到3号箱 # # # 使用正斜杠或 Path 对象来指定文件路径 # excel_path = Path("unilabos\\devices\\workstation\\bioyond_studio\\bioyond_cell\\2025092701.xlsx") # logger.info(ws.create_orders(excel_path)) diff --git a/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx b/unilabos/devices/workstation/bioyond_studio/bioyond_cell/material_template.xlsx index abaf145e68e8910ddf877637d20ff10748c489ad..2467ab05e52c39e2aa0b3cabc49d44b7c5754247 100644 GIT binary patch delta 179 zcmdnLma%m${ieK|cNxxQoLpea%Is!nFWZOlNW|sGX_k)8*0ILbh22OEu-q> TkT5&Ob(1HDsj+JYMwV* delta 194 zcmZo&%ea3nFU`LzMxW^LoSiU^x0UknLnOq;A3YQrS$GC4ienz3v0 l;!q35|C8^8+A_s^O?LDWo~#$9#rR@!WSAP8UI+sa006B+KTZGu diff --git a/unilabos/devices/workstation/bioyond_studio/config.py b/unilabos/devices/workstation/bioyond_studio/config.py index 577833f2..6acc8f60 100644 --- a/unilabos/devices/workstation/bioyond_studio/config.py +++ b/unilabos/devices/workstation/bioyond_studio/config.py @@ -8,7 +8,8 @@ import os # BioyondCellWorkstation 默认配置(包含所有必需参数) API_CONFIG = { # API 连接配置 - "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.10.169:44388"), + # "api_host": os.getenv("BIOYOND_API_HOST", "http://172.21.32.65:44389"),#实机 + "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.10.169:44388"),# 仿真机 "api_key": os.getenv("BIOYOND_API_KEY", "8A819E5C"), "timeout": int(os.getenv("BIOYOND_TIMEOUT", "30")), @@ -16,7 +17,7 @@ API_CONFIG = { "report_token": os.getenv("BIOYOND_REPORT_TOKEN", "CHANGE_ME_TOKEN"), # HTTP 服务配置 - "HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.21.33.174"), # HTTP服务监听地址,监听计算机飞连ip地址 + "HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.21.33.30"), # HTTP服务监听地址,监听计算机飞连ip地址 "HTTP_port": int(os.getenv("BIOYOND_HTTP_PORT", "8080")), "debug_mode": False,# 调试模式 } diff --git a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py index d8bb5975..f2571af5 100644 --- a/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py +++ b/unilabos/devices/workstation/coin_cell_assembly/coin_cell_assembly.py @@ -906,10 +906,11 @@ class CoinCellAssemblyWorkstation(WorkstationBase): return self.success - def func_allpack_cmd(self, elec_num, elec_use_num, elec_vol:int=50, assembly_type:int=7, assembly_pressure:int=4200, file_path: str="D:\\coin_cell_data") -> bool: + def func_allpack_cmd(self, elec_num, elec_use_num, elec_vol:int=50, assembly_type:int=7, assembly_pressure:int=4200, file_path: str="C:\\Users\\67484\\Desktop") -> bool: elec_num, elec_use_num, elec_vol, assembly_type, assembly_pressure = int(elec_num), int(elec_use_num), int(elec_vol), int(assembly_type), int(assembly_pressure) summary_csv_file = os.path.join(file_path, "duandian.csv") # 如果断点文件存在,先读取之前的进度 + if os.path.exists(summary_csv_file): read_status_flag = True with open(summary_csv_file, 'r', newline='', encoding='utf-8') as csvfile: diff --git a/unilabos/registry/devices/bioyond_cell.yaml b/unilabos/registry/devices/bioyond_cell.yaml index a54e7021..d6988b7f 100644 --- a/unilabos/registry/devices/bioyond_cell.yaml +++ b/unilabos/registry/devices/bioyond_cell.yaml @@ -137,7 +137,7 @@ bioyond_cell: WH4_x5_y1_z1_5_quantity: 0.0 WH4_x5_y2_z1_10_materialName: '' WH4_x5_y2_z1_10_quantity: 0.0 - xlsx_path: unilabos\devices\workstation\bioyond_studio\bioyond_cell\样品导入模板.xlsx + xlsx_path: unilabos\devices\workstation\bioyond_studio\bioyond_cell\material_template.xlsx handles: {} placeholder_keys: {} result: {} @@ -463,7 +463,7 @@ bioyond_cell: default: 0.0 type: number xlsx_path: - default: unilabos\devices\workstation\bioyond_studio\bioyond_cell\样品导入模板.xlsx + default: unilabos\devices\workstation\bioyond_studio\bioyond_cell\material_template.xlsx type: string required: [] type: object @@ -530,6 +530,42 @@ bioyond_cell: title: create_and_inbound_materials参数 type: object type: UniLabJsonCommand + auto-create_material: + feedback: {} + goal: {} + goal_default: + location_name_or_id: null + material_name: null + type_id: null + warehouse_name: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + location_name_or_id: + type: string + material_name: + type: string + type_id: + type: string + warehouse_name: + type: string + required: + - material_name + - type_id + - warehouse_name + type: object + result: {} + required: + - goal + title: create_material参数 + type: object + type: UniLabJsonCommand auto-create_materials: feedback: {} goal: {} @@ -580,6 +616,43 @@ bioyond_cell: title: create_orders参数 type: object type: UniLabJsonCommand + auto-create_sample: + feedback: {} + goal: {} + goal_default: + board_type: null + bottle_type: null + location_code: null + name: null + handles: {} + placeholder_keys: {} + result: {} + schema: + description: '' + properties: + feedback: {} + goal: + properties: + board_type: + type: string + bottle_type: + type: string + location_code: + type: string + name: + type: string + required: + - name + - board_type + - bottle_type + - location_code + type: object + result: {} + required: + - goal + title: create_sample参数 + type: object + type: UniLabJsonCommand auto-order_list_v2: feedback: {} goal: {}