From ceef342860e0124efa048183c303fa5b7f5cdbeb Mon Sep 17 00:00:00 2001 From: lixinyu1011 <674842481@qq.com> Date: Mon, 27 Oct 2025 18:16:26 +0800 Subject: [PATCH] 1027byxinyu --- test/resources/YB_materials_info.json | 12 + .../electrolysis_water_platfrom.py | 296 +++++++++ .../bioyond_cell/bioyond_cell_workstation.py | 24 +- .../workstation/bioyond_studio/config.py | 38 +- .../workstation/bioyond_studio/station.py | 1 + .../resources/bioyond/bottle_carriers.yaml | 126 +--- .../registry/resources/bioyond/bottles.yaml | 134 +--- .../resources/bioyond/bottle_carriers copy.py | 148 +++++ unilabos/resources/bioyond/bottle_carriers.py | 620 ------------------ unilabos/resources/bioyond/bottles copy.py | 24 + unilabos/resources/bioyond/bottles.py | 255 ------- unilabos/resources/graphio.py | 2 + 12 files changed, 508 insertions(+), 1172 deletions(-) create mode 100644 test/resources/YB_materials_info.json create mode 100644 unilabos/devices/electrolysis_water_platfrom/electrolysis_water_platfrom.py create mode 100644 unilabos/resources/bioyond/bottle_carriers copy.py delete mode 100644 unilabos/resources/bioyond/bottle_carriers.py create mode 100644 unilabos/resources/bioyond/bottles copy.py delete mode 100644 unilabos/resources/bioyond/bottles.py diff --git a/test/resources/YB_materials_info.json b/test/resources/YB_materials_info.json new file mode 100644 index 00000000..61d2de8f --- /dev/null +++ b/test/resources/YB_materials_info.json @@ -0,0 +1,12 @@ +[ + {'id': '3a1d2656-69bf-1ccf-fc38-85a0431ee498', 'typeName': '加样头(大)', 'code': '0005-00291', 'barCode': '', 'name': 'test', 'quantity': 1.0, 'lockQuantity': 0.0, 'unit': '个', 'status': 1, 'isUse': False, 'locations': [ + {'id': '3a19da56-1379-20c8-5886-f7c4fbcb5733', 'whid': '3a19da56-1378-613b-29f2-871e1a287aa5', 'whName': '粉末加样头堆栈', 'code': '0005-0003', 'x': 3, 'y': 1, 'z': 1, 'quantity': 0 + } + ], 'detail': [] + }, + {'id': '3a1d2657-d16f-a575-3506-5c029ff6810d', 'typeName': '加样头(大)', 'code': '0005-00293', 'barCode': '', 'name': 'dsfdsfd', 'quantity': 1.0, 'lockQuantity': 0.0, 'unit': '个', 'status': 1, 'isUse': False, 'locations': [ + {'id': '3a19da56-1379-e77d-0e65-7463b238a3b9', 'whid': '3a19da56-1378-613b-29f2-871e1a287aa5', 'whName': '粉末加样头堆栈', 'code': '0005-0005', 'x': 5, 'y': 1, 'z': 1, 'quantity': 0 + } + ], 'detail': [] + } +] \ No newline at end of file diff --git a/unilabos/devices/electrolysis_water_platfrom/electrolysis_water_platfrom.py b/unilabos/devices/electrolysis_water_platfrom/electrolysis_water_platfrom.py new file mode 100644 index 00000000..cfcfdeee --- /dev/null +++ b/unilabos/devices/electrolysis_water_platfrom/electrolysis_water_platfrom.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- +import serial +import time +import csv +import threading +import os +from collections import deque +from typing import Dict, Any, Optional +from pylabrobot.resources import Deck + +from unilabos.devices.workstation.workstation_base import WorkstationBase + + +class ElectrolysisWaterPlatform(WorkstationBase): + """ + 电解水平台工作站 + 基于 WorkstationBase 的电解水实验平台,支持串口通信和数据采集 + """ + + def __init__( + self, + deck: Deck, + port: str = "COM10", + baudrate: int = 115200, + csv_path: Optional[str] = None, + timeout: float = 0.2, + **kwargs + ): + super().__init__(deck, **kwargs) + + # ========== 配置 ========== + self.port = port + self.baudrate = baudrate + # 如果没有指定路径,默认保存在代码文件所在目录 + if csv_path is None: + current_dir = os.path.dirname(os.path.abspath(__file__)) + self.csv_path = os.path.join(current_dir, "stm32_data.csv") + else: + self.csv_path = csv_path + self.ser_timeout = timeout + self.chunk_read = 128 + + # 串口对象 + self.ser: Optional[serial.Serial] = None + self.stop_flag = False + + # 线程对象 + self.rx_thread: Optional[threading.Thread] = None + self.tx_thread: Optional[threading.Thread] = None + + # ==== 接收(下位机->上位机):固定 1+13+1 = 15 字节 ==== + self.RX_HEAD = 0x3E + self.RX_TAIL = 0x3E + self.RX_FRAME_LEN = 1 + 13 + 1 # 15 + + # ==== 发送(上位机->下位机):固定 1+9+1 = 11 字节 ==== + self.TX_HEAD = 0x3E + self.TX_TAIL = 0xE3 # 协议图中标注 E3 作为帧尾 + self.TX_FRAME_LEN = 1 + 9 + 1 # 11 + + def open_serial(self, port: Optional[str] = None, baudrate: Optional[int] = None, timeout: Optional[float] = None) -> Optional[serial.Serial]: + """打开串口""" + port = port or self.port + baudrate = baudrate or self.baudrate + timeout = timeout or self.ser_timeout + try: + ser = serial.Serial(port, baudrate, timeout=timeout) + print(f"[OK] 串口 {port} 已打开,波特率 {baudrate}") + ser.reset_input_buffer() + ser.reset_output_buffer() + self.ser = ser + return ser + except serial.SerialException as e: + print(f"[ERR] 无法打开串口 {port}: {e}") + return None + + def close_serial(self): + """关闭串口""" + if self.ser and self.ser.is_open: + self.ser.close() + print("[INFO] 串口已关闭") + + @staticmethod + def u16_be(h: int, l: int) -> int: + """将两个字节组合成16位无符号整数(大端序)""" + return ((h & 0xFF) << 8) | (l & 0xFF) + + @staticmethod + def split_u16_be(val: int) -> tuple: + """返回 (高字节, 低字节),输入会夹到 0..65535""" + v = int(max(0, min(65535, int(val)))) + return (v >> 8) & 0xFF, v & 0xFF + + # ================== 接收:固定15字节 ================== + def parse_rx_payload(self, dat13: bytes) -> Optional[Dict[str, Any]]: + """解析 13 字节数据区(下位机发送到上位机)""" + if len(dat13) != 13: + return None + current_mA = self.u16_be(dat13[0], dat13[1]) + voltage_mV = self.u16_be(dat13[2], dat13[3]) + temperature_raw = self.u16_be(dat13[4], dat13[5]) + tds_ppm = self.u16_be(dat13[6], dat13[7]) + gas_sccm = self.u16_be(dat13[8], dat13[9]) + liquid_mL = self.u16_be(dat13[10], dat13[11]) + ph_raw = dat13[12] & 0xFF + + return { + "Current_mA": current_mA, + "Voltage_mV": voltage_mV, + "Temperature_C": round(temperature_raw / 100.0, 2), + "TDS_ppm": tds_ppm, + "GasFlow_sccm": gas_sccm, + "LiquidFlow_mL": liquid_mL, + "pH": round(ph_raw / 10.0, 2) + } + + def try_parse_rx_frame(self, frame15: bytes) -> Optional[Dict[str, Any]]: + """尝试解析接收帧""" + if len(frame15) != self.RX_FRAME_LEN: + return None + if frame15[0] != self.RX_HEAD or frame15[-1] != self.RX_TAIL: + return None + return self.parse_rx_payload(frame15[1:-1]) + + def rx_thread_fn(self): + """接收线程函数""" + headers = ["Timestamp", "Current_mA", "Voltage_mV", + "Temperature_C", "TDS_ppm", "GasFlow_sccm", "LiquidFlow_mL", "pH"] + + new_file = not os.path.exists(self.csv_path) + f = open(self.csv_path, mode='a', newline='', encoding='utf-8') + writer = csv.writer(f) + if new_file: + writer.writerow(headers) + f.flush() + + buf = deque(maxlen=8192) + print(f"[RX] 开始接收(帧长 {self.RX_FRAME_LEN} 字节);写入:{self.csv_path}") + + try: + while not self.stop_flag and self.ser and self.ser.is_open: + chunk = self.ser.read(self.chunk_read) + if chunk: + buf.extend(chunk) + while True: + # 找帧头 + try: + start = next(i for i, b in enumerate(buf) if b == self.RX_HEAD) + except StopIteration: + buf.clear() + break + if start > 0: + for _ in range(start): + buf.popleft() + if len(buf) < self.RX_FRAME_LEN: + break + candidate = bytes([buf[i] for i in range(self.RX_FRAME_LEN)]) + if candidate[-1] == self.RX_TAIL: + parsed = self.try_parse_rx_frame(candidate) + for _ in range(self.RX_FRAME_LEN): + buf.popleft() + if parsed: + ts = time.strftime("%Y-%m-%d %H:%M:%S") + row = [ts, + parsed["Current_mA"], parsed["Voltage_mV"], + parsed["Temperature_C"], parsed["TDS_ppm"], + parsed["GasFlow_sccm"], parsed["LiquidFlow_mL"], + parsed["pH"]] + writer.writerow(row) + f.flush() + # 若不想打印可注释下一行 + # print(f"[{ts}] I={parsed['Current_mA']} mA, V={parsed['Voltage_mV']} mV, " + # f"T={parsed['Temperature_C']} °C, TDS={parsed['TDS_ppm']}, " + # f"Gas={parsed['GasFlow_sccm']} sccm, Liq={parsed['LiquidFlow_mL']} mL, pH={parsed['pH']}") + else: + # 头不变,尾不对,丢1字节继续对齐 + buf.popleft() + else: + time.sleep(0.01) + finally: + f.close() + print("[RX] 接收线程退出,CSV 已关闭") + + # ================== 发送:固定11字节 ================== + def build_tx_frame(self, mode: int, current_ma: int, voltage_mv: int, temp_c: float, ki: float, pump_percent: float) -> bytes: + """ + 发送帧:HEAD + [mode, I_hi, I_lo, V_hi, V_lo, T_hi, T_lo, Ki_byte, Pump_byte] + TAIL + - mode: 0=恒压, 1=恒流 + - current_ma: mA (0..65535) + - voltage_mv: mV (0..65535) + - temp_c: ℃,将 *100 后拆分为高/低字节 + - ki: 0.0..20.0 -> byte = round(ki * 10) 夹到 0..200 + - pump_percent: 0..100 -> byte = round(pump * 2) 夹到 0..200 + """ + mode_b = 1 if int(mode) == 1 else 0 + + i_hi, i_lo = self.split_u16_be(current_ma) + v_hi, v_lo = self.split_u16_be(voltage_mv) + + t100 = int(round(float(temp_c) * 100.0)) + t_hi, t_lo = self.split_u16_be(t100) + + ki_b = int(max(0, min(200, round(float(ki) * 10)))) + pump_b = int(max(0, min(200, round(float(pump_percent) * 2)))) + + return bytes(( + self.TX_HEAD, + mode_b, + i_hi, i_lo, + v_hi, v_lo, + t_hi, t_lo, + ki_b, + pump_b, + self.TX_TAIL + )) + + def tx_thread_fn(self): + """ + 发送线程函数 + 用户输入 6 个用逗号分隔的数值: + mode,current_mA,voltage_mV,set_temp_C,Ki,pump_percent + 例如: 0,1000,500,0,0,50 + """ + print("\n输入 6 个值(用英文逗号分隔),顺序为:") + print("mode,current_mA,voltage_mV,set_temp_C,Ki,pump_percent") + print("示例恒压:0,500,1000,25,0,100 (stop 结束)\n") + print("示例恒流:1,1000,500,25,0,100 (stop 结束)\n") + print("示例恒流:1,2000,500,25,0,100 (stop 结束)\n") + # 1,2000,500,25,0,100 + + while not self.stop_flag and self.ser and self.ser.is_open: + try: + line = input(">>> ").strip() + except EOFError: + self.stop_flag = True + break + + if not line: + continue + if line.lower() == "stop": + self.stop_flag = True + print("[SYS] 停止程序") + break + + try: + parts = [p.strip() for p in line.split(",")] + if len(parts) != 6: + raise ValueError("需要 6 个逗号分隔的数值") + mode = int(parts[0]) + i_ma = int(float(parts[1])) + v_mv = int(float(parts[2])) + t_c = float(parts[3]) + ki = float(parts[4]) + pump = float(parts[5]) + + frame = self.build_tx_frame(mode, i_ma, v_mv, t_c, ki, pump) + self.ser.write(frame) + print("[TX]", " ".join(f"{b:02X}" for b in frame)) + except Exception as e: + print("[TX] 输入/打包失败:", e) + print("格式:mode,current_mA,voltage_mV,set_temp_C,Ki,pump_percent") + continue + + def start(self): + """启动电解水平台""" + self.ser = self.open_serial() + if self.ser: + try: + self.rx_thread = threading.Thread(target=self.rx_thread_fn, daemon=True) + self.tx_thread = threading.Thread(target=self.tx_thread_fn, daemon=True) + self.rx_thread.start() + self.tx_thread.start() + print("[INFO] 电解水平台已启动") + self.tx_thread.join() # 等待用户输入线程结束(输入 stop) + finally: + self.close_serial() + + def stop(self): + """停止电解水平台""" + self.stop_flag = True + if self.rx_thread and self.rx_thread.is_alive(): + self.rx_thread.join(timeout=2.0) + if self.tx_thread and self.tx_thread.is_alive(): + self.tx_thread.join(timeout=2.0) + self.close_serial() + print("[INFO] 电解水平台已停止") + + +# ================== 主入口 ================== +if __name__ == "__main__": + # 创建一个简单的 Deck 用于测试 + from pylabrobot.resources import Deck + + deck = Deck() + platform = ElectrolysisWaterPlatform(deck) + platform.start() 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 78880398..c60a03e3 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 @@ -10,7 +10,6 @@ from datetime import datetime, timedelta import re import threading import json - from urllib3 import response from unilabos.devices.workstation.workstation_base import WorkstationBase from unilabos.devices.workstation.bioyond_studio.station import BioyondWorkstation, BioyondResourceSynchronizer @@ -19,6 +18,8 @@ from unilabos.devices.workstation.bioyond_studio.config import ( ) from unilabos.devices.workstation.workstation_http_service import WorkstationHTTPService from unilabos.utils.log import logger +from unilabos.registry.registry import lab_registry + def _iso_local_now_ms() -> str: # 文档要求:到毫秒 + Z,例如 2025-08-15T05:43:22.814Z @@ -967,22 +968,23 @@ class BioyondCellWorkstation(BioyondWorkstation): if __name__ == "__main__": + lab_registry.setup() ws = BioyondCellWorkstation() - logger.info(ws.scheduler_stop()) + # logger.info(ws.scheduler_stop()) + logger.info(ws.scheduler_start()) - - results = ws.create_materials(SOLID_LIQUID_MAPPINGS) - for r in results: - logger.info(r) + # results = ws.create_materials(SOLID_LIQUID_MAPPINGS) + # for r in results: + # logger.info(r) # 从CSV文件读取物料列表并批量创建入库 - result = ws.create_and_inbound_materials() + # 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 3bf6250d..81a97362 100644 --- a/unilabos/devices/workstation/bioyond_studio/config.py +++ b/unilabos/devices/workstation/bioyond_studio/config.py @@ -5,13 +5,10 @@ import os # ==================== API 基础配置 ==================== - - -# ==================== 完整的 Bioyond 配置 ==================== # BioyondCellWorkstation 默认配置(包含所有必需参数) API_CONFIG = { # API 连接配置 - "api_host": os.getenv("BIOYOND_API_HOST", "http://172.16.11.219: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")), @@ -19,11 +16,9 @@ API_CONFIG = { "report_token": os.getenv("BIOYOND_REPORT_TOKEN", "CHANGE_ME_TOKEN"), # HTTP 服务配置 - "HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "0.0.0.0"), # HTTP服务监听地址(0.0.0.0 表示监听所有网络接口) + "HTTP_host": os.getenv("BIOYOND_HTTP_HOST", "172.21.32.91"), # HTTP服务监听地址,监听计算机飞连ip地址 "HTTP_port": int(os.getenv("BIOYOND_HTTP_PORT", "8080")), - "report_ip": os.getenv("BIOYOND_REPORT_IP", "172.21.32.172"), # 报送给 Bioyond 的本机IP地址(留空则自动检测) - # 调试模式 - "debug_mode": False, + "debug_mode": False,# 调试模式 } # 库位映射配置 @@ -57,29 +52,10 @@ WAREHOUSE_MAPPING = { # 物料类型配置 MATERIAL_TYPE_MAPPINGS = { - "烧杯": ("BIOYOND_PolymerStation_1FlaskCarrier", "3a14196b-24f2-ca49-9081-0cab8021bf1a"), - "试剂瓶": ("BIOYOND_PolymerStation_1BottleCarrier", ""), - "样品板": ("BIOYOND_PolymerStation_6StockCarrier", "3a14196e-b7a0-a5da-1931-35f3000281e9"), - "分装板": ("BIOYOND_PolymerStation_6VialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"), - "样品瓶": ("BIOYOND_PolymerStation_Solid_Stock", "3a14196a-cf7d-8aea-48d8-b9662c7dba94"), - "90%分装小瓶": ("BIOYOND_PolymerStation_Solid_Vial", "3a14196c-cdcf-088d-dc7d-5cf38f0ad9ea"), - "10%分装小瓶": ("BIOYOND_PolymerStation_Liquid_Vial", "3a14196c-76be-2279-4e22-7310d69aed68"), - "20ml分液瓶": ("BIOYOND_PolymerStation_6x20ml_DispensingVialCarrier", "3a14196e-5dfe-6e21-0c79-fe2036d052c4"), - "100ml液体": ("BIOYOND_PolymerStation_100ml_Liquid_Bottle", "d37166b3-ecaa-481e-bd84-3032b795ba07"), - "液": ("BIOYOND_PolymerStation_Liquid_Bottle", "3a190ca1-2add-2b23-f8e1-bbd348b7f790"), - "高粘液": ("BIOYOND_PolymerStation_High_Viscosity_Liquid_Bottle", "abe8df30-563d-43d2-85e0-cabec59ddc16"), - "加样头(大)": ("BIOYOND_PolymerStation_Large_Dispense_Head", "3a190ca0-b2f6-9aeb-8067-547e72c11469"), - "5ml分液瓶板": ("BIOYOND_PolymerStation_6x5ml_DispensingVialCarrier", "3a192fa4-007d-ec7b-456e-2a8be7a13f23"), - "5ml分液瓶": ("BIOYOND_PolymerStation_5ml_Dispensing_Vial", "3a192c2a-ebb7-58a1-480d-8b3863bf74f4"), - "20ml分液瓶板": ("BIOYOND_PolymerStation_6x20ml_DispensingVialCarrier", "3a192fa4-47db-3449-162a-eaf8aba57e27"), - "配液瓶(小)板": ("BIOYOND_PolymerStation_6x_SmallSolutionBottleCarrier", "3a190c8b-3284-af78-d29f-9a69463ad047"), - "配液瓶(小)": ("BIOYOND_PolymerStation_Small_Solution_Bottle", "3a190c8c-fe8f-bf48-0dc3-97afc7f508eb"), - "配液瓶(大)板": ("BIOYOND_PolymerStation_4x_LargeSolutionBottleCarrier", "53e50377-32dc-4781-b3c0-5ce45bc7dc27"), - "配液瓶(大)": ("BIOYOND_PolymerStation_Large_Solution_Bottle", "19c52ad1-51c5-494f-8854-576f4ca9c6ca"), - "加样头(大)板": ("BIOYOND_PolymerStation_6x_LargeDispenseHeadCarrier", "a8e714ae-2a4e-4eb9-9614-e4c140ec3f16"), - "适配器块": ("BIOYOND_PolymerStation_AdapterBlock", "efc3bb32-d504-4890-91c0-b64ed3ac80cf"), - "枪头盒": ("BIOYOND_PolymerStation_TipBox", "3a192c2e-20f3-a44a-0334-c8301839d0b3"), - "枪头": ("BIOYOND_PolymerStation_Pipette_Tip", "b6196971-1050-46da-9927-333e8dea062d"), + + "加样头(大)": ("YB_jia_yang_tou_da", "3a190ca0-b2f6-9aeb-8067-547e72c11469"), + "加样头(大)板": ("YB_jia_yang_tou_da_1X1_carrier", "a8e714ae-2a4e-4eb9-9614-e4c140ec3f16"), + # YB信息 } SOLID_LIQUID_MAPPINGS = { diff --git a/unilabos/devices/workstation/bioyond_studio/station.py b/unilabos/devices/workstation/bioyond_studio/station.py index 795b7408..3ce7353f 100644 --- a/unilabos/devices/workstation/bioyond_studio/station.py +++ b/unilabos/devices/workstation/bioyond_studio/station.py @@ -74,6 +74,7 @@ class BioyondResourceSynchronizer(ResourceSynchronizer): type_mapping=self.workstation.bioyond_config["material_type_mappings"], deck=self.workstation.deck ) + print("unilab_resources:",unilab_resources) logger.info(f"从Bioyond同步了 {len(unilab_resources)} 个资源") return True diff --git a/unilabos/registry/resources/bioyond/bottle_carriers.yaml b/unilabos/registry/resources/bioyond/bottle_carriers.yaml index 5889bccc..ac01cc35 100644 --- a/unilabos/registry/resources/bioyond/bottle_carriers.yaml +++ b/unilabos/registry/resources/bioyond/bottle_carriers.yaml @@ -1,130 +1,10 @@ -1BottleCarrier: - category: - - bottle_carriers - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_1BottleCarrier - type: pylabrobot - description: 1BottleCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -1FlaskCarrier: - category: - - bottle_carriers - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_1FlaskCarrier - type: pylabrobot - description: 1FlaskCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -6StockCarrier: - category: - - bottle_carriers - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6StockCarrier - type: pylabrobot - description: 6StockCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -6VialCarrier: - category: - - bottle_carriers - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6VialCarrier - type: pylabrobot - description: 6VialCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -6x5ml_DispensingVialCarrier: +YB_jia_yang_tou_da_1X1_carrier: category: - yb3 class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6x5ml_DispensingVialCarrier + module: unilabos.resources.bioyond.bottle_carriers:YB_jia_yang_tou_da_1X1_carrier type: pylabrobot - description: 6x5ml_DispensingVialCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -6x20ml_DispensingVialCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6x20ml_DispensingVialCarrier - type: pylabrobot - description: 6x20ml_DispensingVialCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -6x_SmallSolutionBottleCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6x_SmallSolutionBottleCarrier - type: pylabrobot - description: 6x_SmallSolutionBottleCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -4x_LargeSolutionBottleCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_4x_LargeSolutionBottleCarrier - type: pylabrobot - description: 4x_LargeSolutionBottleCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -6x_LargeDispenseHeadCarrier: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6x_LargeDispenseHeadCarrier - type: pylabrobot - description: 6x_LargeDispenseHeadCarrier - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -AdapterBlock: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_AdapterBlock - type: pylabrobot - description: AdapterBlock - handles: [] - icon: '' - init_param_schema: {} - registry_type: resource - version: 1.0.0 -TipBox: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_TipBox - type: pylabrobot - description: TipBox + description: YB_jia_yang_tou_da_1X1_carrier handles: [] icon: '' init_param_schema: {} diff --git a/unilabos/registry/resources/bioyond/bottles.yaml b/unilabos/registry/resources/bioyond/bottles.yaml index 8d71f287..556b5f8b 100644 --- a/unilabos/registry/resources/bioyond/bottles.yaml +++ b/unilabos/registry/resources/bioyond/bottles.yaml @@ -1,138 +1,8 @@ -Liquid_Vial: - category: - - bottles - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Liquid_Vial - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Reagent_Bottle: - category: - - bottles - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Reagent_Bottle - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Solid_Stock: - category: - - bottles - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solid_Stock - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Solid_Vial: - category: - - bottles - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solid_Vial - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Solution_Beaker: - category: - - bottles - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solution_Beaker - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -100ml_Liquid_Bottle: +YB_jia_yang_tou_da: category: - yb3 class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_100ml_Liquid_Bottle - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Liquid_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Liquid_Bottle - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -High_Viscosity_Liquid_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_High_Viscosity_Liquid_Bottle - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Large_Dispense_Head: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Large_Dispense_Head - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -5ml_Dispensing_Vial: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_5ml_Dispensing_Vial - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -20ml_Dispensing_Vial: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_20ml_Dispensing_Vial - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Small_Solution_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Small_Solution_Bottle - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Large_Solution_Bottle: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Large_Solution_Bottle - type: pylabrobot - handles: [] - icon: '' - init_param_schema: {} - version: 1.0.0 -Pipette_Tip: - category: - - yb3 - class: - module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Pipette_Tip + module: unilabos.resources.bioyond.bottles:YB_jia_yang_tou_da type: pylabrobot handles: [] icon: '' diff --git a/unilabos/resources/bioyond/bottle_carriers copy.py b/unilabos/resources/bioyond/bottle_carriers copy.py new file mode 100644 index 00000000..a3b0044e --- /dev/null +++ b/unilabos/resources/bioyond/bottle_carriers copy.py @@ -0,0 +1,148 @@ +from pylabrobot.resources import create_homogeneous_resources, Coordinate, ResourceHolder, create_ordered_items_2d + +from unilabos.resources.itemized_carrier import Bottle, BottleCarrier +from unilabos.resources.bioyond.bottles import ( + YB_jia_yang_tou_da, +) +# 命名约定:试剂瓶-Bottle,烧杯-Beaker,烧瓶-Flask,小瓶-Vial + + + + + +def YB_jia_yang_tou_da_1X1_carrier(name: str) -> BottleCarrier: + """加样头(大)板 - 1x1布局,1个位置""" + + # 载架尺寸 (mm) + carrier_size_x = 127.8 + carrier_size_y = 85.5 + carrier_size_z = 95.0 + + # 瓶位尺寸 + bottle_diameter = 35.0 + bottle_spacing_x = 42.0 # X方向间距 + bottle_spacing_y = 35.0 # Y方向间距 + + # 计算起始位置 (居中排列) + start_x = (carrier_size_x - (1 - 1) * bottle_spacing_x - bottle_diameter) / 2 + start_y = (carrier_size_y - (1 - 1) * bottle_spacing_y - bottle_diameter) / 2 + + sites = create_ordered_items_2d( + klass=ResourceHolder, + num_items_x=1, + num_items_y=1, + dx=start_x, + dy=start_y, + dz=5.0, + item_dx=bottle_spacing_x, + item_dy=bottle_spacing_y, + size_x=bottle_diameter, + size_y=bottle_diameter, + size_z=carrier_size_z, + ) + for k, v in sites.items(): + v.name = f"{name}_{v.name}" + + carrier = BottleCarrier( + name=name, + size_x=carrier_size_x, + size_y=carrier_size_y, + size_z=carrier_size_z, + sites=sites, + model="YB_1X1_jia_yang_tou_da_carrier", + ) + carrier.num_items_x = 1 + carrier.num_items_y = 1 + carrier.num_items_z = 1 + carrier[0] = YB_jia_yang_tou_da(f"{name}_head_1") + return carrier + + +def BIOYOND_PolymerStation_AdapterBlock(name: str) -> BottleCarrier: + """适配器块 - 单个中央位置""" + + # 载架尺寸 (mm) + carrier_size_x = 127.8 + carrier_size_y = 85.5 + carrier_size_z = 30.0 + + # 适配器尺寸 + adapter_diameter = 80.0 + + # 计算中央位置 + center_x = (carrier_size_x - adapter_diameter) / 2 + center_y = (carrier_size_y - adapter_diameter) / 2 + center_z = 0.0 + + carrier = BottleCarrier( + name=name, + size_x=carrier_size_x, + size_y=carrier_size_y, + size_z=carrier_size_z, + sites=create_homogeneous_resources( + klass=ResourceHolder, + locations=[Coordinate(center_x, center_y, center_z)], + resource_size_x=adapter_diameter, + resource_size_y=adapter_diameter, + name_prefix=name, + ), + model="AdapterBlock", + ) + carrier.num_items_x = 1 + carrier.num_items_y = 1 + carrier.num_items_z = 1 + # 适配器块本身不包含瓶子,只是一个支撑结构 + return carrier + + +def BIOYOND_PolymerStation_TipBox(name: str) -> BottleCarrier: + """枪头盒 - 8x12布局,96个位置""" + + # 载架尺寸 (mm) + carrier_size_x = 127.8 + carrier_size_y = 85.5 + carrier_size_z = 55.0 + + # 枪头尺寸 + tip_diameter = 10.0 + tip_spacing_x = 9.0 # X方向间距 + tip_spacing_y = 9.0 # Y方向间距 + + # 计算起始位置 (居中排列) + start_x = (carrier_size_x - (12 - 1) * tip_spacing_x - tip_diameter) / 2 + start_y = (carrier_size_y - (8 - 1) * tip_spacing_y - tip_diameter) / 2 + + sites = create_ordered_items_2d( + klass=ResourceHolder, + num_items_x=12, + num_items_y=8, + dx=start_x, + dy=start_y, + dz=5.0, + item_dx=tip_spacing_x, + item_dy=tip_spacing_y, + size_x=tip_diameter, + size_y=tip_diameter, + size_z=carrier_size_z, + ) + for k, v in sites.items(): + v.name = f"{name}_{v.name}" + + carrier = BottleCarrier( + name=name, + size_x=carrier_size_x, + size_y=carrier_size_y, + size_z=carrier_size_z, + sites=sites, + model="TipBox", + ) + carrier.num_items_x = 12 + carrier.num_items_y = 8 + carrier.num_items_z = 1 + # 创建96个枪头 + for i in range(96): + row = chr(65 + i // 12) # A-H + col = (i % 12) + 1 # 1-12 + carrier[i] = BIOYOND_PolymerStation_Pipette_Tip(f"{name}_tip_{row}{col}") + return carrier + diff --git a/unilabos/resources/bioyond/bottle_carriers.py b/unilabos/resources/bioyond/bottle_carriers.py deleted file mode 100644 index ab0b6560..00000000 --- a/unilabos/resources/bioyond/bottle_carriers.py +++ /dev/null @@ -1,620 +0,0 @@ -from pylabrobot.resources import create_homogeneous_resources, Coordinate, ResourceHolder, create_ordered_items_2d - -from unilabos.resources.itemized_carrier import Bottle, BottleCarrier -from unilabos.resources.bioyond.bottles import ( - BIOYOND_PolymerStation_Solid_Stock, - BIOYOND_PolymerStation_Solid_Vial, - BIOYOND_PolymerStation_Liquid_Vial, - BIOYOND_PolymerStation_Solution_Beaker, - BIOYOND_PolymerStation_Reagent_Bottle, - BIOYOND_PolymerStation_5ml_Dispensing_Vial, - BIOYOND_PolymerStation_20ml_Dispensing_Vial, - BIOYOND_PolymerStation_Small_Solution_Bottle, - BIOYOND_PolymerStation_Large_Solution_Bottle, - BIOYOND_PolymerStation_Large_Dispense_Head, - BIOYOND_PolymerStation_Pipette_Tip -) -# 命名约定:试剂瓶-Bottle,烧杯-Beaker,烧瓶-Flask,小瓶-Vial - - -def BIOYOND_Electrolyte_6VialCarrier(name: str) -> BottleCarrier: - """6瓶载架 - 2x3布局""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 50.0 - - # 瓶位尺寸 - bottle_diameter = 30.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (3 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=3, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="Electrolyte_6VialCarrier", - ) - carrier.num_items_x = 3 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - for i in range(6): - carrier[i] = BIOYOND_PolymerStation_Solid_Vial(f"{name}_vial_{i+1}") - return carrier - - -def BIOYOND_Electrolyte_1BottleCarrier(name: str) -> BottleCarrier: - """1瓶载架 - 单个中央位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 100.0 - - # 烧杯尺寸 - beaker_diameter = 80.0 - - # 计算中央位置 - center_x = (carrier_size_x - beaker_diameter) / 2 - center_y = (carrier_size_y - beaker_diameter) / 2 - center_z = 5.0 - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=create_homogeneous_resources( - klass=ResourceHolder, - locations=[Coordinate(center_x, center_y, center_z)], - resource_size_x=beaker_diameter, - resource_size_y=beaker_diameter, - name_prefix=name, - ), - model="Electrolyte_1BottleCarrier", - ) - carrier.num_items_x = 1 - carrier.num_items_y = 1 - carrier.num_items_z = 1 - carrier[0] = BIOYOND_PolymerStation_Solution_Beaker(f"{name}_beaker_1") - return carrier - - -def BIOYOND_PolymerStation_6StockCarrier(name: str) -> BottleCarrier: - """6瓶载架 - 2x3布局""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 50.0 - - # 瓶位尺寸 - bottle_diameter = 20.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (3 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=3, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="6StockCarrier", - ) - carrier.num_items_x = 3 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - ordering = ["A1", "A2", "A3", "B1", "B2", "B3"] # 自定义顺序 - for i in range(6): - carrier[i] = BIOYOND_PolymerStation_Solid_Stock(f"{name}_vial_{ordering[i]}") - return carrier - - -def BIOYOND_PolymerStation_6VialCarrier(name: str) -> BottleCarrier: - """6瓶载架 - 2x3布局""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 50.0 - - # 瓶位尺寸 - bottle_diameter = 30.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (3 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=3, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="6VialCarrier", - ) - carrier.num_items_x = 3 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - ordering = ["A1", "A2", "A3", "B1", "B2", "B3"] # 自定义顺序 - for i in range(3): - carrier[i] = BIOYOND_PolymerStation_Solid_Vial(f"{name}_solidvial_{ordering[i]}") - for i in range(3, 6): - carrier[i] = BIOYOND_PolymerStation_Liquid_Vial(f"{name}_liquidvial_{ordering[i]}") - return carrier - - -def BIOYOND_PolymerStation_1BottleCarrier(name: str) -> BottleCarrier: - """1瓶载架 - 单个中央位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 20.0 - - # 烧杯尺寸 - beaker_diameter = 60.0 - - # 计算中央位置 - center_x = (carrier_size_x - beaker_diameter) / 2 - center_y = (carrier_size_y - beaker_diameter) / 2 - center_z = 5.0 - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=create_homogeneous_resources( - klass=ResourceHolder, - locations=[Coordinate(center_x, center_y, center_z)], - resource_size_x=beaker_diameter, - resource_size_y=beaker_diameter, - name_prefix=name, - ), - model="1BottleCarrier", - ) - carrier.num_items_x = 1 - carrier.num_items_y = 1 - carrier.num_items_z = 1 - carrier[0] = BIOYOND_PolymerStation_Reagent_Bottle(f"{name}_flask_1") - return carrier - - -def BIOYOND_PolymerStation_1FlaskCarrier(name: str) -> BottleCarrier: - """1瓶载架 - 单个中央位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 20.0 - - # 烧杯尺寸 - beaker_diameter = 70.0 - - # 计算中央位置 - center_x = (carrier_size_x - beaker_diameter) / 2 - center_y = (carrier_size_y - beaker_diameter) / 2 - center_z = 5.0 - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=create_homogeneous_resources( - klass=ResourceHolder, - locations=[Coordinate(center_x, center_y, center_z)], - resource_size_x=beaker_diameter, - resource_size_y=beaker_diameter, - name_prefix=name, - ), - model="1FlaskCarrier", - ) - carrier.num_items_x = 1 - carrier.num_items_y = 1 - carrier.num_items_z = 1 - carrier[0] = BIOYOND_PolymerStation_Reagent_Bottle(f"{name}_bottle_1") - return carrier - - -def BIOYOND_PolymerStation_6x5ml_DispensingVialCarrier(name: str) -> BottleCarrier: - """5ml分液瓶板 - 4x2布局,8个位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 50.0 - - # 瓶位尺寸 - bottle_diameter = 15.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (4 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=4, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="6x5ml_DispensingVialCarrier", - ) - carrier.num_items_x = 4 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - ordering = ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"] - for i in range(8): - carrier[i] = BIOYOND_PolymerStation_5ml_Dispensing_Vial(f"{name}_vial_{ordering[i]}") - return carrier - - -def BIOYOND_PolymerStation_6x20ml_DispensingVialCarrier(name: str) -> BottleCarrier: - """20ml分液瓶板 - 4x2布局,8个位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 70.0 - - # 瓶位尺寸 - bottle_diameter = 20.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (4 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=4, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="6x20ml_DispensingVialCarrier", - ) - carrier.num_items_x = 4 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - ordering = ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"] - for i in range(8): - carrier[i] = BIOYOND_PolymerStation_20ml_Dispensing_Vial(f"{name}_vial_{ordering[i]}") - return carrier - - -def BIOYOND_PolymerStation_6x_SmallSolutionBottleCarrier(name: str) -> BottleCarrier: - """配液瓶(小)板 - 4x2布局,8个位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 65.0 - - # 瓶位尺寸 - bottle_diameter = 35.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (4 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=4, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="6x_SmallSolutionBottleCarrier", - ) - carrier.num_items_x = 4 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - ordering = ["A1", "A2", "A3", "A4", "B1", "B2", "B3", "B4"] - for i in range(8): - carrier[i] = BIOYOND_PolymerStation_Small_Solution_Bottle(f"{name}_bottle_{ordering[i]}") - return carrier - - -def BIOYOND_PolymerStation_4x_LargeSolutionBottleCarrier(name: str) -> BottleCarrier: - """配液瓶(大)板 - 2x2布局,4个位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 95.0 - - # 瓶位尺寸 - bottle_diameter = 55.0 - bottle_spacing_x = 60.0 # X方向间距 - bottle_spacing_y = 60.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (2 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (2 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=2, - num_items_y=2, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="4x_LargeSolutionBottleCarrier", - ) - carrier.num_items_x = 2 - carrier.num_items_y = 2 - carrier.num_items_z = 1 - ordering = ["A1", "A2", "B1", "B2"] - for i in range(4): - carrier[i] = BIOYOND_PolymerStation_Large_Solution_Bottle(f"{name}_bottle_{ordering[i]}") - return carrier - - -def BIOYOND_PolymerStation_6x_LargeDispenseHeadCarrier(name: str) -> BottleCarrier: - """加样头(大)板 - 1x1布局,1个位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 95.0 - - # 瓶位尺寸 - bottle_diameter = 35.0 - bottle_spacing_x = 42.0 # X方向间距 - bottle_spacing_y = 35.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (1 - 1) * bottle_spacing_x - bottle_diameter) / 2 - start_y = (carrier_size_y - (1 - 1) * bottle_spacing_y - bottle_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=1, - num_items_y=1, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=bottle_spacing_x, - item_dy=bottle_spacing_y, - size_x=bottle_diameter, - size_y=bottle_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="6x_LargeDispenseHeadCarrier", - ) - carrier.num_items_x = 1 - carrier.num_items_y = 1 - carrier.num_items_z = 1 - carrier[0] = BIOYOND_PolymerStation_Large_Dispense_Head(f"{name}_head_1") - return carrier - - -def BIOYOND_PolymerStation_AdapterBlock(name: str) -> BottleCarrier: - """适配器块 - 单个中央位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 30.0 - - # 适配器尺寸 - adapter_diameter = 80.0 - - # 计算中央位置 - center_x = (carrier_size_x - adapter_diameter) / 2 - center_y = (carrier_size_y - adapter_diameter) / 2 - center_z = 0.0 - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=create_homogeneous_resources( - klass=ResourceHolder, - locations=[Coordinate(center_x, center_y, center_z)], - resource_size_x=adapter_diameter, - resource_size_y=adapter_diameter, - name_prefix=name, - ), - model="AdapterBlock", - ) - carrier.num_items_x = 1 - carrier.num_items_y = 1 - carrier.num_items_z = 1 - # 适配器块本身不包含瓶子,只是一个支撑结构 - return carrier - - -def BIOYOND_PolymerStation_TipBox(name: str) -> BottleCarrier: - """枪头盒 - 8x12布局,96个位置""" - - # 载架尺寸 (mm) - carrier_size_x = 127.8 - carrier_size_y = 85.5 - carrier_size_z = 55.0 - - # 枪头尺寸 - tip_diameter = 10.0 - tip_spacing_x = 9.0 # X方向间距 - tip_spacing_y = 9.0 # Y方向间距 - - # 计算起始位置 (居中排列) - start_x = (carrier_size_x - (12 - 1) * tip_spacing_x - tip_diameter) / 2 - start_y = (carrier_size_y - (8 - 1) * tip_spacing_y - tip_diameter) / 2 - - sites = create_ordered_items_2d( - klass=ResourceHolder, - num_items_x=12, - num_items_y=8, - dx=start_x, - dy=start_y, - dz=5.0, - item_dx=tip_spacing_x, - item_dy=tip_spacing_y, - size_x=tip_diameter, - size_y=tip_diameter, - size_z=carrier_size_z, - ) - for k, v in sites.items(): - v.name = f"{name}_{v.name}" - - carrier = BottleCarrier( - name=name, - size_x=carrier_size_x, - size_y=carrier_size_y, - size_z=carrier_size_z, - sites=sites, - model="TipBox", - ) - carrier.num_items_x = 12 - carrier.num_items_y = 8 - carrier.num_items_z = 1 - # 创建96个枪头 - for i in range(96): - row = chr(65 + i // 12) # A-H - col = (i % 12) + 1 # 1-12 - carrier[i] = BIOYOND_PolymerStation_Pipette_Tip(f"{name}_tip_{row}{col}") - return carrier - diff --git a/unilabos/resources/bioyond/bottles copy.py b/unilabos/resources/bioyond/bottles copy.py new file mode 100644 index 00000000..2504c377 --- /dev/null +++ b/unilabos/resources/bioyond/bottles copy.py @@ -0,0 +1,24 @@ +from unilabos.resources.itemized_carrier import Bottle, BottleCarrier +# 工厂函数 + + +def YB_jia_yang_tou_da( + name: str, + diameter: float = 35.0, + height: float = 90.0, + max_volume: float = 50000.0, # 50mL + code: str = None, + barcode: str = None, +) -> Bottle: + """创建加样头(大)""" + return Bottle( + name=name, + diameter=diameter, + height=height, + max_volume=max_volume, + barcode=barcode, + code=code, + model="YB_jia_yang_tou_da", + ) + + diff --git a/unilabos/resources/bioyond/bottles.py b/unilabos/resources/bioyond/bottles.py deleted file mode 100644 index 40cb9ef6..00000000 --- a/unilabos/resources/bioyond/bottles.py +++ /dev/null @@ -1,255 +0,0 @@ -from unilabos.resources.itemized_carrier import Bottle, BottleCarrier -# 工厂函数 - - -def BIOYOND_PolymerStation_Solid_Stock( - name: str, - diameter: float = 20.0, - height: float = 100.0, - max_volume: float = 30000.0, # 30mL - barcode: str = None, -) -> Bottle: - """创建粉末瓶""" - return Bottle( - name=name, - diameter=diameter,# 未知 - height=height, - max_volume=max_volume, - barcode=barcode, - model="Solid_Stock", - ) - - -def BIOYOND_PolymerStation_Solid_Vial( - name: str, - diameter: float = 25.0, - height: float = 60.0, - max_volume: float = 30000.0, # 30mL - barcode: str = None, -) -> Bottle: - """创建粉末瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Solid_Vial", - ) - - -def BIOYOND_PolymerStation_Liquid_Vial( - name: str, - diameter: float = 25.0, - height: float = 60.0, - max_volume: float = 30000.0, # 30mL - barcode: str = None, -) -> Bottle: - """创建滴定液瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Liquid_Vial", - ) - - -def BIOYOND_PolymerStation_Solution_Beaker( - name: str, - diameter: float = 60.0, - height: float = 70.0, - max_volume: float = 200000.0, # 200mL - barcode: str = None, -) -> Bottle: - """创建溶液烧杯""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Solution_Beaker", - ) - - -def BIOYOND_PolymerStation_Reagent_Bottle( - name: str, - diameter: float = 70.0, - height: float = 120.0, - max_volume: float = 500000.0, # 500mL - barcode: str = None, -) -> Bottle: - """创建试剂瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Reagent_Bottle", - ) - - -def BIOYOND_PolymerStation_100ml_Liquid_Bottle( - name: str, - diameter: float = 50.0, - height: float = 80.0, - max_volume: float = 100000.0, # 100mL - barcode: str = None, -) -> Bottle: - """创建100ml液体瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="100ml_Liquid_Bottle", - ) - - -def BIOYOND_PolymerStation_Liquid_Bottle( - name: str, - diameter: float = 40.0, - height: float = 70.0, - max_volume: float = 50000.0, # 50mL - barcode: str = None, -) -> Bottle: - """创建液体瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Liquid_Bottle", - ) - - -def BIOYOND_PolymerStation_High_Viscosity_Liquid_Bottle( - name: str, - diameter: float = 45.0, - height: float = 75.0, - max_volume: float = 60000.0, # 60mL - barcode: str = None, -) -> Bottle: - """创建高粘液瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="High_Viscosity_Liquid_Bottle", - ) - - -def BIOYOND_PolymerStation_Large_Dispense_Head( - name: str, - diameter: float = 35.0, - height: float = 90.0, - max_volume: float = 50000.0, # 50mL - barcode: str = None, -) -> Bottle: - """创建加样头(大)""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Large_Dispense_Head", - ) - - -def BIOYOND_PolymerStation_5ml_Dispensing_Vial( - name: str, - diameter: float = 15.0, - height: float = 45.0, - max_volume: float = 5000.0, # 5mL - barcode: str = None, -) -> Bottle: - """创建5ml分液瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="5ml_Dispensing_Vial", - ) - - -def BIOYOND_PolymerStation_20ml_Dispensing_Vial( - name: str, - diameter: float = 20.0, - height: float = 65.0, - max_volume: float = 20000.0, # 20mL - barcode: str = None, -) -> Bottle: - """创建20ml分液瓶""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="20ml_Dispensing_Vial", - ) - - -def BIOYOND_PolymerStation_Small_Solution_Bottle( - name: str, - diameter: float = 35.0, - height: float = 60.0, - max_volume: float = 40000.0, # 40mL - barcode: str = None, -) -> Bottle: - """创建配液瓶(小)""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Small_Solution_Bottle", - ) - - -def BIOYOND_PolymerStation_Large_Solution_Bottle( - name: str, - diameter: float = 55.0, - height: float = 90.0, - max_volume: float = 150000.0, # 150mL - barcode: str = None, -) -> Bottle: - """创建配液瓶(大)""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Large_Solution_Bottle", - ) - - -def BIOYOND_PolymerStation_Pipette_Tip( - name: str, - diameter: float = 10.0, - height: float = 50.0, - max_volume: float = 1000.0, # 1mL - barcode: str = None, -) -> Bottle: - """创建枪头""" - return Bottle( - name=name, - diameter=diameter, - height=height, - max_volume=max_volume, - barcode=barcode, - model="Pipette_Tip", - ) - diff --git a/unilabos/resources/graphio.py b/unilabos/resources/graphio.py index bca92c95..92fcf1ed 100644 --- a/unilabos/resources/graphio.py +++ b/unilabos/resources/graphio.py @@ -636,6 +636,8 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[st plr_material: ResourcePLR = initialize_resource( {"name": material["name"], "class": className}, resource_type=ResourcePLR ) + print("plr_material:",plr_material) + print("code:",material.get("code", "")) plr_material.code = material.get("code", "") and material.get("barCode", "") or "" plr_material.unilabos_uuid = str(uuid.uuid4())