diff --git a/unilabos/devices/liquid_handling/prcxi/prcxi_labware.py b/unilabos/devices/liquid_handling/prcxi/prcxi_labware.py index ce754a78..74579b77 100644 --- a/unilabos/devices/liquid_handling/prcxi/prcxi_labware.py +++ b/unilabos/devices/liquid_handling/prcxi/prcxi_labware.py @@ -5,8 +5,8 @@ from pylabrobot.resources.tip import Tip, TipCreator from pylabrobot.resources.tip_rack import TipRack, TipSpot from pylabrobot.resources.utils import create_ordered_items_2d from pylabrobot.resources.height_volume_functions import ( - compute_height_from_volume_rectangle, - compute_volume_from_height_rectangle, + compute_height_from_volume_rectangle, + compute_volume_from_height_rectangle, ) from .prcxi import PRCXI9300Plate, PRCXI9300TipRack, PRCXI9300Trash, PRCXI9300TubeRack, PRCXI9300PlateAdapter @@ -17,60 +17,53 @@ def _make_tip_helper(volume: float, length: float, depth: float) -> Tip: """ return Tip( has_filter=False, # 默认无滤芯 - maximal_volume=volume, - total_tip_length=length, + maximal_volume=volume, + total_tip_length=length, fitting_depth=depth ) + # ========================================================================= -# 标准品 参照 PLR 标准库的参数,但是用 PRCXI9300Plate 实例化,并注入 UUID +# Plates # ========================================================================= -def PRCXI_BioER_96_wellplate(name: str) -> PRCXI9300Plate: +def PRCXI_BioER_96_wellplate(name: str) -> PRCXI9300Plate: """ 对应 JSON Code: ZX-019-2.2 (2.2ml 深孔板) 原型: pylabrobot.resources.bioer.BioER_96_wellplate_Vb_2200uL """ return PRCXI9300Plate( name=name, - size_x=127.1, - size_y=85.0, - size_z=44.2, - lid=None, - model="PRCXI_BioER_96_wellplate", + size_x=127.1, + size_y=85.0, + size_z=44.2, + model="PRCXI_BioER_96_wellplate", category="plate", - material_info={ - "uuid": "ca877b8b114a4310b429d1de4aae96ee", - "Code": "ZX-019-2.2", - "Name": "2.2ml 深孔板", - "materialEnum": 0, - "SupplyType": 1 - }, - + material_info={"uuid": "ca877b8b114a4310b429d1de4aae96ee", "Code": "ZX-019-2.2", "Name": "2.2ml 深孔板", "materialEnum": 0, "SupplyType": 1}, ordered_items=create_ordered_items_2d( Well, - size_x=8.25, - size_y=8.25, - size_z=39.3, # 修改过 - dx=9.5, - dy=7.5, - dz=6, - material_z_thickness=0.8, + num_items_x=12, + num_items_y=8, + dx=9.5, + dy=7.5, + dz=6.0, item_dx=9.0, item_dy=9.0, - num_items_x=12, - num_items_y=8, + size_x=8.25, + size_y=8.25, + size_z=39.3, + max_volume=2200.0, + material_z_thickness=0.8, + bottom_type=WellBottomType.V, cross_section_type=CrossSectionType.RECTANGLE, - bottom_type=WellBottomType.V, # 是否需要修改? - max_volume=2200, ), ) -def PRCXI_nest_1_troughplate(name: str) -> PRCXI9300Plate: +def PRCXI_nest_1_troughplate(name: str) -> PRCXI9300Plate: """ 对应 JSON Code: ZX-58-10000 (储液槽) 原型: pylabrobot.resources.nest.nest_1_troughplate_195000uL_Vb """ - well_size_x = 127.76 - (14.38 - 9 / 2) * 2 - well_size_y = 85.48 - (11.24 - 9 / 2) * 2 + well_size_x = 108.0 + well_size_y = 72.0 well_kwargs = { "size_x": well_size_x, "size_y": well_size_y, @@ -82,7 +75,7 @@ def PRCXI_nest_1_troughplate(name: str) -> PRCXI9300Plate: "compute_volume_from_height": lambda liquid_height: compute_volume_from_height_rectangle( liquid_height=liquid_height, well_length=well_size_x, well_width=well_size_y ), - "material_z_thickness": 31.4 - 26.85 - 3.55, + "material_z_thickness": 0.9999999999999973, } return PRCXI9300Plate( @@ -91,212 +84,317 @@ def PRCXI_nest_1_troughplate(name: str) -> PRCXI9300Plate: size_y=85.48, size_z=31.4, lid=None, - model="PRCXI_Nest_1_troughplate", + model="PRCXI_Nest_1_troughplate", category="plate", - material_info={ - "uuid": "04211a2dc93547fe9bf6121eac533650", - "Code": "ZX-58-10000", - "Name": "储液槽", - "materialEnum": 0, - "SupplyType": 1 - }, - + material_info={"uuid": "04211a2dc93547fe9bf6121eac533650", "Code": "ZX-58-10000", "Name": "储液槽", "materialEnum": 0, "SupplyType": 1}, ordered_items=create_ordered_items_2d( Well, num_items_x=1, num_items_y=1, - dx=14.38 - 9 / 2, - dy=11.24 - 9 / 2, - dz=3.55, - item_dx=9.0, + dx=9.88, + dy=6.74, + dz=3.55, + item_dx=9.0, item_dy=9.0, - **well_kwargs, # 传入上面计算好的孔参数 + **well_kwargs, ), ) -def PRCXI_BioRad_384_wellplate(name: str) -> PRCXI9300Plate: +def PRCXI_BioRad_384_wellplate(name: str) -> PRCXI9300Plate: """ 对应 JSON Code: q3 (384板) 原型: pylabrobot.resources.biorad.BioRad_384_wellplate_50uL_Vb """ return PRCXI9300Plate( name=name, - # 直接抄录 PLR 标准品的物理尺寸 size_x=127.76, size_y=85.48, - size_z=10.40, + size_z=10.4, model="BioRad_384_wellplate_50uL_Vb", category="plate", - # 2. 注入 Unilab 必须的 UUID 信息 - material_info={ - "uuid": "853dcfb6226f476e8b23c250217dc7da", - "Code": "q3", - "Name": "384板", - "SupplyType": 1, - }, - # 3. 定义孔的排列 (抄录标准参数) + material_info={"uuid": "853dcfb6226f476e8b23c250217dc7da", "Code": "q3", "Name": "384板", "SupplyType": 1}, ordered_items=create_ordered_items_2d( Well, num_items_x=24, num_items_y=16, - dx=10.58, # A1 左边缘距离板子左边缘 需要进一步测量 - dy=7.44, # P1 下边缘距离板子下边缘 需要进一步测量 + dx=10.58, + dy=7.44, dz=1.05, item_dx=4.5, item_dy=4.5, - size_x=3.10, - size_y=3.10, + size_x=3.1, + size_y=3.1, size_z=9.35, - max_volume=50, - material_z_thickness=1, + max_volume=50.0, + material_z_thickness=1.0, bottom_type=WellBottomType.V, cross_section_type=CrossSectionType.CIRCLE, - ) + ), ) def PRCXI_AGenBio_4_troughplate(name: str) -> PRCXI9300Plate: - """ + """ 对应 JSON Code: sdfrth654 (4道储液槽) 原型: pylabrobot.resources.agenbio.AGenBio_4_troughplate_75000uL_Vb - """ - INNER_WELL_WIDTH = 26.1 - INNER_WELL_LENGTH = 71.2 - well_kwargs = { - "size_x": 26, - "size_y": 71.2, - "size_z": 42.55, - "bottom_type": WellBottomType.FLAT, - "cross_section_type": CrossSectionType.RECTANGLE, - "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( - liquid_volume, - INNER_WELL_LENGTH, - INNER_WELL_WIDTH, - ), - "compute_volume_from_height": lambda liquid_height: compute_volume_from_height_rectangle( - liquid_height, - INNER_WELL_LENGTH, - INNER_WELL_WIDTH, - ), - "material_z_thickness": 1, - } + """ + well_size_x = 26.0 + well_size_y = 71.2 + well_kwargs = { + "size_x": well_size_x, + "size_y": well_size_y, + "size_z": 42.55, + "bottom_type": WellBottomType.FLAT, + "cross_section_type": CrossSectionType.RECTANGLE, + "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( + liquid_volume=liquid_volume, well_length=well_size_x, well_width=well_size_y + ), + "compute_volume_from_height": lambda liquid_height: compute_volume_from_height_rectangle( + liquid_height=liquid_height, well_length=well_size_x, well_width=well_size_y + ), + "material_z_thickness": 1.0, + } - return PRCXI9300Plate( - name=name, - size_x=127.76, - size_y=85.48, - size_z=43.80, - model="PRCXI_AGenBio_4_troughplate", - category="plate", - material_info={ - "uuid": "01953864f6f140ccaa8ddffd4f3e46f5", - "Code": "sdfrth654", - "Name": "4道储液槽", - "materialEnum": 0, - "SupplyType": 1 - }, - - ordered_items=create_ordered_items_2d( - Well, - num_items_x=4, - num_items_y=1, - dx=9.8, - dy=7.2, - dz=0.9, - item_dx=INNER_WELL_WIDTH + 1, # 1 mm wall thickness - item_dy=INNER_WELL_LENGTH, - **well_kwargs, - ), - ) + return PRCXI9300Plate( + name=name, + size_x=127.76, + size_y=85.48, + size_z=43.8, + lid=None, + model="PRCXI_AGenBio_4_troughplate", + category="plate", + material_info={"uuid": "01953864f6f140ccaa8ddffd4f3e46f5", "Code": "sdfrth654", "Name": "4道储液槽", "materialEnum": 0, "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=4, + num_items_y=1, + dx=9.8, + dy=7.2, + dz=0.9, + item_dx=27.1, + item_dy=9.0, + **well_kwargs, + ), + ) def PRCXI_nest_12_troughplate(name: str) -> PRCXI9300Plate: - """ + """ 对应 JSON Code: 12道储液槽 (12道储液槽) 原型: pylabrobot.resources.nest.nest_12_troughplate_15000uL_Vb """ - well_size_x = 8.2 - well_size_y = 71.2 - well_kwargs = { - "size_x": well_size_x, - "size_y": well_size_y, - "size_z": 26.85, - "bottom_type": WellBottomType.V, - "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( - liquid_volume=liquid_volume, well_length=well_size_x, well_width=well_size_y - ), - "compute_volume_from_height": lambda liquid_height: compute_volume_from_height_rectangle( - liquid_height=liquid_height, well_length=well_size_x, well_width=well_size_y - ), - "material_z_thickness": 31.4 - 26.85 - 3.55, - } + well_size_x = 8.2 + well_size_y = 71.2 + well_kwargs = { + "size_x": well_size_x, + "size_y": well_size_y, + "size_z": 26.85, + "bottom_type": WellBottomType.V, + "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( + liquid_volume=liquid_volume, well_length=well_size_x, well_width=well_size_y + ), + "compute_volume_from_height": lambda liquid_height: compute_volume_from_height_rectangle( + liquid_height=liquid_height, well_length=well_size_x, well_width=well_size_y + ), + "material_z_thickness": 0.9999999999999973, + } - return PRCXI9300Plate( - name=name, - size_x=127.76, - size_y=85.48, - size_z=31.4, - lid=None, - model="PRCXI_nest_12_troughplate", - category="plate", - material_info={ - "uuid": "0f1639987b154e1fac78f4fb29a1f7c1", - "Code": "12道储液槽", - "Name": "12道储液槽", - "materialEnum": 0, - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=12, - num_items_y=1, - dx=14.38 - 8.2 / 2, - dy=(85.48 - 71.2) / 2, - dz=3.55, - item_dx=9.0, - item_dy=9.0, - **well_kwargs, - ), - ) + return PRCXI9300Plate( + name=name, + size_x=127.76, + size_y=85.48, + size_z=31.4, + lid=None, + model="PRCXI_nest_12_troughplate", + category="plate", + material_info={"uuid": "0f1639987b154e1fac78f4fb29a1f7c1", "Code": "12道储液槽", "Name": "12道储液槽", "materialEnum": 0, "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=12, + num_items_y=1, + dx=10.28, + dy=7.14, + dz=3.55, + item_dx=9.0, + item_dy=9.0, + **well_kwargs, + ), + ) def PRCXI_CellTreat_96_wellplate(name: str) -> PRCXI9300Plate: - """ + """ 对应 JSON Code: ZX-78-096 (细菌培养皿) 原型: pylabrobot.resources.celltreat.CellTreat_96_wellplate_350ul_Fb """ - well_kwargs = { - "size_x": 6.96, - "size_y": 6.96, - "size_z": 10.04, - "bottom_type": WellBottomType.FLAT, - "material_z_thickness": 1.75, - "cross_section_type": CrossSectionType.CIRCLE, - "max_volume": 300, - } - - return PRCXI9300Plate( - name=name, - size_x=127.61, - size_y=85.24, - size_z=14.30, - lid=None, - model="PRCXI_CellTreat_96_wellplate", - category="plate", - material_info={ - "uuid": "b05b3b2aafd94ec38ea0cd3215ecea8f", - "Code": "ZX-78-096", - "Name": "细菌培养皿", - "materialEnum": 4, - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=12, - num_items_y=8, - dx=10.83, - dy=7.67, - dz=4.05, - item_dx=9, - item_dy=9, - **well_kwargs, - ), - ) + return PRCXI9300Plate( + name=name, + size_x=127.61, + size_y=85.24, + size_z=14.3, + model="PRCXI_CellTreat_96_wellplate", + category="plate", + material_info={"uuid": "b05b3b2aafd94ec38ea0cd3215ecea8f", "Code": "ZX-78-096", "Name": "细菌培养皿", "materialEnum": 4, "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=12, + num_items_y=8, + dx=10.83, + dy=7.67, + dz=4.05, + item_dx=9.0, + item_dy=9.0, + size_x=6.96, + size_y=6.96, + size_z=10.04, + max_volume=300.0, + material_z_thickness=1.75, + cross_section_type=CrossSectionType.CIRCLE, + ), + ) +def PRCXI_PCR_Plate_200uL_nonskirted(name: str) -> PRCXI9300Plate: + """ + 对应 JSON Code: ZX-023-0.2 (0.2ml PCR 板) + """ + return PRCXI9300Plate( + name=name, + size_x=119.5, + size_y=80.0, + size_z=26.0, + plate_type="non-skirted", + model="PRCXI_PCR_Plate_200uL_nonskirted", + category="plate", + material_info={"uuid": "73bb9b10bc394978b70e027bf45ce2d3", "Code": "ZX-023-0.2", "Name": "0.2ml PCR 板", "materialEnum": 0, "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=12, + num_items_y=8, + dx=7.0, + dy=5.0, + dz=0.0, + item_dx=9.0, + item_dy=9.0, + size_x=6.0, + size_y=6.0, + size_z=15.17, + max_volume=428.92164499461444, + bottom_type=WellBottomType.V, + cross_section_type=CrossSectionType.CIRCLE, + ), + ) +def PRCXI_PCR_Plate_200uL_semiskirted(name: str) -> PRCXI9300Plate: + """ + 对应 JSON Code: ZX-023-0.2 (0.2ml PCR 板) + """ + return PRCXI9300Plate( + name=name, + size_x=126.0, + size_y=86.0, + size_z=21.2, + plate_type="semi-skirted", + model="PRCXI_PCR_Plate_200uL_semiskirted", + category="plate", + material_info={"uuid": "73bb9b10bc394978b70e027bf45ce2d3", "Code": "ZX-023-0.2", "Name": "0.2ml PCR 板", "materialEnum": 0, "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=12, + num_items_y=8, + dx=11.0, + dy=8.0, + dz=0.0, + item_dx=9.0, + item_dy=9.0, + size_x=6.0, + size_y=6.0, + size_z=15.17, + max_volume=428.92164499461444, + bottom_type=WellBottomType.V, + cross_section_type=CrossSectionType.CIRCLE, + ), + ) +def PRCXI_PCR_Plate_200uL_skirted(name: str) -> PRCXI9300Plate: + """ + 对应 JSON Code: ZX-023-0.2 (0.2ml PCR 板) + """ + return PRCXI9300Plate( + name=name, + size_x=127.76, + size_y=86.0, + size_z=16.1, + plate_type="skirted", + model="PRCXI_PCR_Plate_200uL_skirted", + category="plate", + material_info={"uuid": "73bb9b10bc394978b70e027bf45ce2d3", "Code": "ZX-023-0.2", "Name": "0.2ml PCR 板", "materialEnum": 0, "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=12, + num_items_y=8, + dx=11.0, + dy=8.49, + dz=0.8, + item_dx=9.0, + item_dy=9.0, + size_x=6.0, + size_y=6.0, + size_z=15.1, + max_volume=426.94244162285287, + bottom_type=WellBottomType.V, + cross_section_type=CrossSectionType.CIRCLE, + ), + ) +def PRCXI_96_DeepWell(name: str) -> PRCXI9300Plate: + """ + 对应 JSON Code: q2 (96深孔板) + """ + return PRCXI9300Plate( + name=name, + size_x=127.3, + size_y=85.35, + size_z=45.0, + model="PRCXI_96_DeepWell", + category="plate", + material_info={"uuid": "57b1e4711e9e4a32b529f3132fc5931f", "Code": "q2", "Name": "96深孔板", "materialEnum": 0}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=12, + num_items_y=8, + dx=10.9, + dy=8.25, + dz=2.0, + item_dx=9.0, + item_dy=9.0, + size_x=8.2, + size_y=8.2, + size_z=42.0, + max_volume=2200.0, + bottom_type=WellBottomType.UNKNOWN, + cross_section_type=CrossSectionType.CIRCLE, + ), + ) +def PRCXI_48_DeepWell(name: str) -> PRCXI9300Plate: + """ + Code: 22 (48孔深孔板) + """ + return PRCXI9300Plate( + name=name, + size_x=127.0, + size_y=85.0, + size_z=44.0, + model="PRCXI_48_DeepWell", + category="plate", + material_info={"uuid": "026c5d5cf3d94e56b4e16b7fb53a995b", "Code": "22", "Name": "48孔深孔板", "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + Well, + num_items_x=6, + num_items_y=8, + dx=10.0, + dy=10.0, + dz=1.0, + item_dx=18.5, + item_dy=9.0, + size_x=8.0, + size_y=8.0, + size_z=40.0, + max_volume=2010.6192982974676, + bottom_type=WellBottomType.UNKNOWN, + cross_section_type=CrossSectionType.CIRCLE, + ), + ) # ========================================================================= -# 自定义/需测量品 (Custom Measurement) +# Tip Racks # ========================================================================= def PRCXI_10ul_eTips(name: str) -> PRCXI9300TipRack: """ @@ -304,29 +402,24 @@ def PRCXI_10ul_eTips(name: str) -> PRCXI9300TipRack: """ return PRCXI9300TipRack( name=name, - size_x=122.11, - size_y=85.48, #修改 - size_z=58.23, + size_x=127.76, + size_y=85.48, + size_z=58.0, model="PRCXI_10ul_eTips", - material_info={ - "uuid": "068b3815e36b4a72a59bae017011b29f", - "Code": "ZX-001-10+", - "Name": "10μL加长 Tip头", - "SupplyType": 1 - }, + material_info={"uuid": "068b3815e36b4a72a59bae017011b29f", "Code": "ZX-001-10+", "Name": "10μL加长 Tip头", "SupplyType": 1}, ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, num_items_y=8, - dx=7.97, #需要修改 - dy=5.0, #需修改 - dz=2.0, #需修改 + dx=10.63, + dy=7.49, + dz=14.6, item_dx=9.0, item_dy=9.0, - size_x=7.0, - size_y=7.0, - size_z=0, - make_tip=lambda: _make_tip_helper(volume=10, length=52.0, depth=45.1) + size_x=7.5, + size_y=7.5, + size_z=0.0, + make_tip=lambda: _make_tip_helper(volume=10.0, length=52.0, depth=8.2) ) ) def PRCXI_300ul_Tips(name: str) -> PRCXI9300TipRack: @@ -336,136 +429,159 @@ def PRCXI_300ul_Tips(name: str) -> PRCXI9300TipRack: """ return PRCXI9300TipRack( name=name, - size_x=122.11, - size_y=85.48, #修改 - size_z=58.23, + size_x=127.76, + size_y=85.48, + size_z=58.0, model="PRCXI_300ul_Tips", - material_info={ - "uuid": "076250742950465b9d6ea29a225dfb00", - "Code": "ZX-001-300", - "Name": "300μL Tip头", - "SupplyType": 1 - }, + material_info={"uuid": "076250742950465b9d6ea29a225dfb00", "Code": "ZX-001-300", "Name": "300μL Tip头", "SupplyType": 1}, ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, num_items_y=8, - dx=7.97, #需要修改 - dy=5.0, #需修改 - dz=2.0, #需修改 + dx=10.63, + dy=7.49, + dz=6.6, + item_dx=9.0, + item_dy=9.0, + size_x=7.5, + size_y=7.5, + size_z=0.0, + make_tip=lambda: _make_tip_helper(volume=300.0, length=60.0, depth=8.2) + ) + ) +def PRCXI_1250uL_Tips(name: str) -> PRCXI9300TipRack: + """ + Code: ZX-001-1250 + """ + return PRCXI9300TipRack( + name=name, + size_x=127.76, + size_y=85.48, + size_z=98.0, + model="PRCXI_1250uL_Tips", + material_info={"uuid": "7960f49ddfe9448abadda89bd1556936", "Code": "ZX-001-1250", "Name": "1250μL Tip头", "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + TipSpot, + num_items_x=12, + num_items_y=8, + dx=10.63, + dy=7.49, + dz=6.6, + item_dx=9.0, + item_dy=9.0, + size_x=7.5, + size_y=7.5, + size_z=100.0, + make_tip=lambda: _make_tip_helper(volume=1250.0, length=100.0, depth=8.2) + ) + ) +def PRCXI_10uL_Tips(name: str) -> PRCXI9300TipRack: + """ + Code: ZX-001-10 + """ + return PRCXI9300TipRack( + name=name, + size_x=127.76, + size_y=85.48, + size_z=58.0, + model="PRCXI_10uL_Tips", + material_info={"uuid": "45f2ed3ad925484d96463d675a0ebf66", "Code": "ZX-001-10", "Name": "10μL Tip头", "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + TipSpot, + num_items_x=12, + num_items_y=8, + dx=10.63, + dy=7.49, + dz=14.6, + item_dx=9.0, + item_dy=9.0, + size_x=7.5, + size_y=7.5, + size_z=0.0, + make_tip=lambda: _make_tip_helper(volume=10.0, length=52.0, depth=8.2) + ) + ) +def PRCXI_1000uL_Tips(name: str) -> PRCXI9300TipRack: + """ + Code: ZX-001-1000 + """ + return PRCXI9300TipRack( + name=name, + size_x=127.76, + size_y=85.48, + size_z=98.0, + model="PRCXI_1000uL_Tips", + material_info={"uuid": "80652665f6a54402b2408d50b40398df", "Code": "ZX-001-1000", "Name": "1000μL Tip头", "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + TipSpot, + num_items_x=12, + num_items_y=8, + dx=10.63, + dy=7.49, + dz=6.6, + item_dx=9.0, + item_dy=9.0, + size_x=7.5, + size_y=7.5, + size_z=100.0, + make_tip=lambda: _make_tip_helper(volume=1000.0, length=100.0, depth=8.2) + ) + ) +def PRCXI_200uL_Tips(name: str) -> PRCXI9300TipRack: + """ + Code: ZX-001-200 + """ + return PRCXI9300TipRack( + name=name, + size_x=120.98, + size_y=82.12, + size_z=66.9, + model="PRCXI_200uL_Tips", + material_info={"uuid": "7a73bb9e5c264515a8fcbe88aed0e6f7", "Code": "ZX-001-200", "Name": "200μL Tip头", "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + TipSpot, + num_items_x=12, + num_items_y=8, + dx=8.24, + dy=6.81, + dz=2.0, item_dx=9.0, item_dy=9.0, size_x=7.0, size_y=7.0, - size_z=0, - make_tip=lambda: _make_tip_helper(volume=300, length=60.0, depth=51.0) + size_z=0.0, + make_tip=lambda: _make_tip_helper(volume=300.0, length=60.0, depth=51.0) ) ) -def PRCXI_PCR_Plate_200uL_nonskirted(name: str) -> PRCXI9300Plate: +def PRCXI_50uL_tips(name: str) -> PRCXI9300TipRack: """ - 对应 JSON Code: ZX-023-0.2 (0.2ml PCR 板) + Code: """ - return PRCXI9300Plate( - name=name, - size_x=119.5, - size_y=80.0, - size_z=26.0, - model="PRCXI_PCR_Plate_200uL_nonskirted", - plate_type="non-skirted", - category="plate", - material_info={ - "uuid": "73bb9b10bc394978b70e027bf45ce2d3", - "Code": "ZX-023-0.2", - "Name": "0.2ml PCR 板", - "materialEnum": 0, - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=12, - num_items_y=8, - dx=7, - dy=5, - dz=0.0, - item_dx=9, - item_dy=9, - size_x=6, - size_y=6, - size_z=15.17, - bottom_type=WellBottomType.V, - cross_section_type=CrossSectionType.CIRCLE, - ), -) -def PRCXI_PCR_Plate_200uL_semiskirted(name: str) -> PRCXI9300Plate: - """ - 对应 JSON Code: ZX-023-0.2 (0.2ml PCR 板) - """ - return PRCXI9300Plate( - name=name, - size_x=126, - size_y=86, - size_z=21.2, - model="PRCXI_PCR_Plate_200uL_semiskirted", - plate_type="semi-skirted", - category="plate", - material_info={ - "uuid": "73bb9b10bc394978b70e027bf45ce2d3", - "Code": "ZX-023-0.2", - "Name": "0.2ml PCR 板", - "materialEnum": 0, - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=12, - num_items_y=8, - dx=11, - dy=8, - dz=0.0, - item_dx=9, - item_dy=9, - size_x=6, - size_y=6, - size_z=15.17, - bottom_type=WellBottomType.V, - cross_section_type=CrossSectionType.CIRCLE, - ), -) -def PRCXI_PCR_Plate_200uL_skirted(name: str) -> PRCXI9300Plate: - """ - 对应 JSON Code: ZX-023-0.2 (0.2ml PCR 板) - """ - return PRCXI9300Plate( - name=name, - size_x=127.76, - size_y=86, - size_z=16.1, - model="PRCXI_PCR_Plate_200uL_skirted", - plate_type="skirted", - category="plate", - material_info={ - "uuid": "73bb9b10bc394978b70e027bf45ce2d3", - "Code": "ZX-023-0.2", - "Name": "0.2ml PCR 板", - "materialEnum": 0, - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=12, - num_items_y=8, - dx=11, - dy=8.49, - dz=0.8, - item_dx=9, - item_dy=9, - size_x=6, - size_y=6, - size_z=15.1, - bottom_type=WellBottomType.V, - cross_section_type=CrossSectionType.CIRCLE, - ), -) + return PRCXI9300TipRack( + name=name, + size_x=127.76, + size_y=85.48, + size_z=58.0, + model="PRCXI_50uL_tips", + material_info={"uuid": "", "Code": "", "Name": "", "SupplyType": 1}, + ordered_items=create_ordered_items_2d( + TipSpot, + num_items_x=12, + num_items_y=8, + dx=10.63, + dy=7.49, + dz=13.6, + item_dx=9.0, + item_dy=9.0, + size_x=7.5, + size_y=7.5, + size_z=0.0, + make_tip=lambda: _make_tip_helper(volume=50.0, length=53.0, depth=8.2) + ) + ) +# ========================================================================= +# Trash +# ========================================================================= def PRCXI_trash(name: str = "trash") -> PRCXI9300Trash: """ 对应 JSON Code: q1 (废弃槽) @@ -473,66 +589,20 @@ def PRCXI_trash(name: str = "trash") -> PRCXI9300Trash: return PRCXI9300Trash( name="trash", size_x=126.59, - size_y=84.87, - size_z=89.5, # 修改 + size_y=84.87, + size_z=89.5, category="trash", model="PRCXI_trash", - material_info={ - "uuid": "730067cf07ae43849ddf4034299030e9", - "Code": "q1", - "Name": "废弃槽", - "materialEnum": 0, - "SupplyType": 1 - } - ) -def PRCXI_96_DeepWell(name: str) -> PRCXI9300Plate: - """ - 对应 JSON Code: q2 (96深孔板) - """ - return PRCXI9300Plate( - name=name, - size_x=127.3, - size_y=85.35, - size_z=45.0, #修改 - model="PRCXI_96_DeepWell", - material_info={ - "uuid": "57b1e4711e9e4a32b529f3132fc5931f", # 对应 q2 uuid - "Code": "q2", - "Name": "96深孔板", - "materialEnum": 0 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=12, - num_items_y=8, - dx=10.9, - dy=8.25, - dz=2.0, - item_dx=9.0, - item_dy=9.0, - size_x=8.2, - size_y=8.2, - size_z=42.0, - max_volume=2200 - ) + material_info={"uuid": "730067cf07ae43849ddf4034299030e9", "Code": "q1", "Name": "废弃槽", "materialEnum": 0, "SupplyType": 1} ) +# ========================================================================= +# Tube Racks +# ========================================================================= def PRCXI_EP_Adapter(name: str) -> PRCXI9300TubeRack: """ 对应 JSON Code: 1 (ep适配器) 这是一个 4x6 的 EP 管架,适配 1.5mL/2.0mL 离心管 """ - ep_tube_prototype = Tube( - name="EP_Tube_1.5mL", - size_x=10.6, - size_y=10.6, - size_z=40.0, # 管子本身的高度,通常比架子孔略高或持平 - max_volume=1500, - model="EP_Tube_1.5mL" - ) - - # 计算 PRCXI9300TubeRack 中孔的起始位置 dx, dy - dy_calc = 85.8 - 10.5 - (3 * 18) - 10.6 - dx_calc = 3.54 return PRCXI9300TubeRack( name=name, size_x=128.04, @@ -540,306 +610,130 @@ def PRCXI_EP_Adapter(name: str) -> PRCXI9300TubeRack: size_z=42.66, model="PRCXI_EP_Adapter", category="tube_rack", - material_info={ - "uuid": "e146697c395e4eabb3d6b74f0dd6aaf7", - "Code": "1", - "Name": "ep适配器", - "materialEnum": 0, - "SupplyType": 1 - }, + material_info={"uuid": "e146697c395e4eabb3d6b74f0dd6aaf7", "Code": "1", "Name": "ep适配器", "materialEnum": 0, "SupplyType": 1}, ordered_items=create_ordered_items_2d( - Tube, + Tube, num_items_x=6, num_items_y=4, - dx=dx_calc, - dy=dy_calc, - dz=42.66 - 38.08, # 架高 - 孔深 - item_dx=21.0, - item_dy=18.0, + dx=3.54, + dy=10.7, + dz=4.58, + item_dx=21.0, + item_dy=18.0, size_x=10.6, size_y=10.6, size_z=40.0, - max_volume=1500 + max_volume=1500.0 ) ) # ========================================================================= -# 无实物,需要测量 +# Plate Adapters # ========================================================================= def PRCXI_Tip1250_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-58-1250 """ return PRCXI9300PlateAdapter( - name=name, - size_x=128, - size_y=85, - size_z=20, - material_info={ - "uuid": "3b6f33ffbf734014bcc20e3c63e124d4", - "Code": "ZX-58-1250", - "Name": "Tip头适配器 1250uL", - "SupplyType": 2 - } + name=name, + size_x=128.0, + size_y=85.0, + size_z=20.0, + material_info={"uuid": "3b6f33ffbf734014bcc20e3c63e124d4", "Code": "ZX-58-1250", "Name": "Tip头适配器 1250uL", "SupplyType": 2} ) def PRCXI_Tip300_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-58-300 """ return PRCXI9300PlateAdapter( - name=name, - size_x=127, - size_y=85, - size_z=81, - material_info={ - "uuid": "7c822592b360451fb59690e49ac6b181", - "Code": "ZX-58-300", - "Name": "ZHONGXI 适配器 300uL", - "SupplyType": 2 - } + name=name, + size_x=127.0, + size_y=85.0, + size_z=81.0, + material_info={"uuid": "7c822592b360451fb59690e49ac6b181", "Code": "ZX-58-300", "Name": "ZHONGXI 适配器 300uL", "SupplyType": 2} ) def PRCXI_Tip10_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-58-10 """ return PRCXI9300PlateAdapter( - name=name, - size_x=128, - size_y=85, + name=name, + size_x=128.0, + size_y=85.0, size_z=72.3, - material_info={ - "uuid": "8cc3dce884ac41c09f4570d0bcbfb01c", - "Code": "ZX-58-10", - "Name": "吸头10ul 适配器", - "SupplyType": 2 - } - ) -def PRCXI_1250uL_Tips(name: str) -> PRCXI9300TipRack: - """ Code: ZX-001-1250 """ - return PRCXI9300TipRack( - name=name, - size_x=118.09, - size_y=80.7, - size_z=107.67, - model="PRCXI_1250uL_Tips", - material_info={ - "uuid": "7960f49ddfe9448abadda89bd1556936", - "Code": "ZX-001-1250", - "Name": "1250μL Tip头", - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - TipSpot, - num_items_x=12, - num_items_y=8, - dx=9.545 - 7.95/2, - dy=8.85 - 7.95/2, - dz=2.0, - item_dx=9, - item_dy=9, - size_x=7.0, - size_y=7.0, - size_z=0, - make_tip=lambda: _make_tip_helper(volume=1250, length=107.67, depth=8) - ) - ) -def PRCXI_10uL_Tips(name: str) -> PRCXI9300TipRack: - """ Code: ZX-001-10 """ - return PRCXI9300TipRack( - name=name, - size_x=120.98, - size_y=82.12, - size_z=67, - model="PRCXI_10uL_Tips", - material_info={ - "uuid": "45f2ed3ad925484d96463d675a0ebf66", - "Code": "ZX-001-10", - "Name": "10μL Tip头", - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - TipSpot, - num_items_x=12, - num_items_y=8, - dx=10.99 - 5/2, - dy=9.56 - 5/2, - dz=2.0, - item_dx=9, - item_dy=9, - size_x=7.0, - size_y=7.0, - size_z=0, - make_tip=lambda: _make_tip_helper(volume=1250, length=52.0, depth=5) - ) - ) -def PRCXI_1000uL_Tips(name: str) -> PRCXI9300TipRack: - """ Code: ZX-001-1000 """ - return PRCXI9300TipRack( - name=name, - size_x=128.09, - size_y=85.8, - size_z=98, - model="PRCXI_1000uL_Tips", - material_info={ - "uuid": "80652665f6a54402b2408d50b40398df", - "Code": "ZX-001-1000", - "Name": "1000μL Tip头", - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - TipSpot, - num_items_x=12, - num_items_y=8, - dx=14.5 - 7.95/2, - dy=7.425, - dz=2.0, - item_dx=9, - item_dy=9, - size_x=7.0, - size_y=7.0, - size_z=0, - make_tip=lambda: _make_tip_helper(volume=1000, length=55.0, depth=8) - ) - ) -def PRCXI_200uL_Tips(name: str) -> PRCXI9300TipRack: - """ Code: ZX-001-200 """ - return PRCXI9300TipRack( - name=name, - size_x=120.98, - size_y=82.12, - size_z=66.9, - model="PRCXI_200uL_Tips", - material_info={ - "uuid": "7a73bb9e5c264515a8fcbe88aed0e6f7", - "Code": "ZX-001-200", - "Name": "200μL Tip头", - "SupplyType": 1}, - ordered_items=create_ordered_items_2d( - TipSpot, - num_items_x=12, - num_items_y=8, - dx=10.99 - 5.5/2, - dy=9.56 - 5.5/2, - dz=2.0, - item_dx=9, - item_dy=9, - size_x=7.0, - size_z=0, - size_y=7.0, - make_tip=lambda: _make_tip_helper(volume=200, length=52.0, depth=5) - ) + material_info={"uuid": "8cc3dce884ac41c09f4570d0bcbfb01c", "Code": "ZX-58-10", "Name": "吸头10ul 适配器", "SupplyType": 2} ) def PRCXI_PCR_Adapter(name: str) -> PRCXI9300PlateAdapter: - """ - 对应 JSON Code: ZX-58-0001 (全裙边 PCR适配器) - """ + """ 对应 JSON Code: ZX-58-0001 (全裙边 PCR适配器) """ return PRCXI9300PlateAdapter( name=name, size_x=127.76, size_y=85.48, size_z=21.69, model="PRCXI_PCR_Adapter", - material_info={ - "uuid": "4a043a07c65a4f9bb97745e1f129b165", - "Code": "ZX-58-0001", - "Name": "全裙边 PCR适配器", - "materialEnum": 3, - "SupplyType": 2 - } + material_info={"uuid": "4a043a07c65a4f9bb97745e1f129b165", "Code": "ZX-58-0001", "Name": "全裙边 PCR适配器", "materialEnum": 3, "SupplyType": 2} ) def PRCXI_Reservoir_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-ADP-001 """ return PRCXI9300PlateAdapter( - name=name, - size_x=133, - size_y=91.8, - size_z=70, - material_info={ - "uuid": "6bdfdd7069df453896b0806df50f2f4d", - "Code": "ZX-ADP-001", - "Name": "储液槽 适配器", - "SupplyType": 2 - } + name=name, + size_x=133.0, + size_y=91.8, + size_z=70.0, + material_info={"uuid": "6bdfdd7069df453896b0806df50f2f4d", "Code": "ZX-ADP-001", "Name": "储液槽 适配器", "SupplyType": 2} ) def PRCXI_Deep300_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-002-300 """ return PRCXI9300PlateAdapter( - name=name, - size_x=136.4, - size_y=93.8, - size_z=96, - material_info={ - "uuid": "9a439bed8f3344549643d6b3bc5a5eb4", - "Code": "ZX-002-300", - "Name": "300ul深孔板适配器", - "SupplyType": 2 - } + name=name, + size_x=136.4, + size_y=93.8, + size_z=96.0, + material_info={"uuid": "9a439bed8f3344549643d6b3bc5a5eb4", "Code": "ZX-002-300", "Name": "300ul深孔板适配器", "SupplyType": 2} ) def PRCXI_Deep10_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-002-10 """ return PRCXI9300PlateAdapter( - name=name, - size_x=136.5, - size_y=93.8, + name=name, + size_x=136.5, + size_y=93.8, size_z=121.5, - material_info={ - "uuid": "4dc8d6ecfd0449549683b8ef815a861b", - "Code": "ZX-002-10", - "Name": "10ul专用深孔板适配器", - "SupplyType": 2 - } + material_info={"uuid": "4dc8d6ecfd0449549683b8ef815a861b", "Code": "ZX-002-10", "Name": "10ul专用深孔板适配器", "SupplyType": 2} ) def PRCXI_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: Fhh478 """ return PRCXI9300PlateAdapter( - name=name, - size_x=120, - size_y=90, - size_z=86, - material_info={ - "uuid": "adfabfffa8f24af5abfbba67b8d0f973", - "Code": "Fhh478", - "Name": "适配器", - "SupplyType": 2 - } - ) -def PRCXI_48_DeepWell(name: str) -> PRCXI9300Plate: - """ Code: 22 (48孔深孔板) """ - print("Warning: Code '22' (48孔深孔板) dimensions are null in JSON.") - return PRCXI9300Plate( - name=name, - size_x=127, - size_y=85, - size_z=44, - model="PRCXI_48_DeepWell", - material_info={ - "uuid": "026c5d5cf3d94e56b4e16b7fb53a995b", - "Code": "22", - "Name": "48孔深孔板", - "SupplyType": 1 - }, - ordered_items=create_ordered_items_2d( - Well, - num_items_x=6, - num_items_y=8, - dx=10, - dy=10, - dz=1, - item_dx=18.5, - item_dy=9, - size_x=8, - size_y=8, - size_z=40 - ) + name=name, + size_x=120.0, + size_y=90.0, + size_z=86.0, + material_info={"uuid": "adfabfffa8f24af5abfbba67b8d0f973", "Code": "Fhh478", "Name": "适配器", "SupplyType": 2} ) def PRCXI_30mm_Adapter(name: str) -> PRCXI9300PlateAdapter: """ Code: ZX-58-30 """ return PRCXI9300PlateAdapter( - name=name, - size_x=132, - size_y=93.5, - size_z=30, - material_info={ - "uuid": "a0757a90d8e44e81a68f306a608694f2", - "Code": "ZX-58-30", - "Name": "30mm适配器", - "SupplyType": 2 - } + name=name, + size_x=132.0, + size_y=93.5, + size_z=30.0, + material_info={"uuid": "a0757a90d8e44e81a68f306a608694f2", "Code": "ZX-58-30", "Name": "30mm适配器", "SupplyType": 2} ) +PRCXI_TEMPLATE_FACTORY_KINDS: List[Tuple[Callable[..., Any], str]] = [ + (PRCXI_BioER_96_wellplate, "plate"), + (PRCXI_nest_1_troughplate, "plate"), + (PRCXI_BioRad_384_wellplate, "plate"), + (PRCXI_AGenBio_4_troughplate, "plate"), + (PRCXI_nest_12_troughplate, "plate"), + (PRCXI_CellTreat_96_wellplate, "plate"), + (PRCXI_10ul_eTips, "tip_rack"), + (PRCXI_300ul_Tips, "tip_rack"), + (PRCXI_PCR_Plate_200uL_nonskirted, "plate"), + (PRCXI_PCR_Plate_200uL_semiskirted, "plate"), + (PRCXI_PCR_Plate_200uL_skirted, "plate"), + (PRCXI_trash, "trash"), + (PRCXI_96_DeepWell, "plate"), + (PRCXI_EP_Adapter, "tube_rack"), + (PRCXI_1250uL_Tips, "tip_rack"), + (PRCXI_10uL_Tips, "tip_rack"), + (PRCXI_1000uL_Tips, "tip_rack"), + (PRCXI_200uL_Tips, "tip_rack"), + (PRCXI_48_DeepWell, "plate"), +] + # --------------------------------------------------------------------------- # 协议上传 / workflow 用:与设备端耗材字典字段对齐的模板描述(供 common 自动匹配) @@ -872,30 +766,6 @@ def _first_child_capacity_for_match(resource: Any) -> float: return 0.0 -# (factory, kind) — 不含各类 Adapter,避免与真实板子误匹配 -PRCXI_TEMPLATE_FACTORY_KINDS: List[Tuple[Callable[..., Any], str]] = [ - (PRCXI_BioER_96_wellplate, "plate"), - (PRCXI_nest_1_troughplate, "plate"), - (PRCXI_BioRad_384_wellplate, "plate"), - (PRCXI_AGenBio_4_troughplate, "plate"), - (PRCXI_nest_12_troughplate, "plate"), - (PRCXI_CellTreat_96_wellplate, "plate"), - (PRCXI_10ul_eTips, "tip_rack"), - (PRCXI_300ul_Tips, "tip_rack"), - (PRCXI_PCR_Plate_200uL_nonskirted, "plate"), - (PRCXI_PCR_Plate_200uL_semiskirted, "plate"), - (PRCXI_PCR_Plate_200uL_skirted, "plate"), - (PRCXI_trash, "trash"), - (PRCXI_96_DeepWell, "plate"), - (PRCXI_EP_Adapter, "tube_rack"), - (PRCXI_1250uL_Tips, "tip_rack"), - (PRCXI_10uL_Tips, "tip_rack"), - (PRCXI_1000uL_Tips, "tip_rack"), - (PRCXI_200uL_Tips, "tip_rack"), - (PRCXI_48_DeepWell, "plate"), -] - - def get_prcxi_labware_template_specs() -> List[Dict[str, Any]]: """返回与 ``prcxi._match_and_create_matrix`` 中耗材字段兼容的模板列表,用于按孔数+容量打分。""" global _PRCXI_TEMPLATE_SPECS_CACHE @@ -936,4 +806,4 @@ def get_prcxi_labware_template_specs() -> List[Dict[str, Any]]: ) _PRCXI_TEMPLATE_SPECS_CACHE = out - return out \ No newline at end of file + return out diff --git a/unilabos/labware_manager/importer.py b/unilabos/labware_manager/importer.py index aa3b0874..b6d30a2a 100644 --- a/unilabos/labware_manager/importer.py +++ b/unilabos/labware_manager/importer.py @@ -399,6 +399,11 @@ def import_from_code() -> LabwareDB: tip_fitting_depth=tip_depth, has_filter=tip_filter, ) + # 计算 tip_above_rack_length = tip_length - (size_z - dz) + if grid_data: + _dz = grid_data.get("dz", 0.0) + _above = tip_length - (item.size_z - _dz) + item.tip.tip_above_rack_length = round(_above, 4) if _above > 0 else None elif type_name == "tube_rack" and children: if grid_data: diff --git a/unilabos/labware_manager/labware_db.json b/unilabos/labware_manager/labware_db.json index 4856cda0..b559a8f7 100644 --- a/unilabos/labware_manager/labware_db.json +++ b/unilabos/labware_manager/labware_db.json @@ -306,9 +306,9 @@ "type": "tip_rack", "function_name": "PRCXI_10ul_eTips", "docstring": "对应 JSON Code: ZX-001-10+", - "size_x": 122.11, + "size_x": 127.76, "size_y": 85.48, - "size_z": 58.23, + "size_z": 58.0, "model": "PRCXI_10ul_eTips", "category": "tip_rack", "plate_type": null, @@ -327,21 +327,22 @@ "grid": { "num_items_x": 12, "num_items_y": 8, - "dx": 7.97, - "dy": 5.0, - "dz": 2.0, + "dx": 10.63, + "dy": 7.49, + "dz": 14.6, "item_dx": 9.0, "item_dy": 9.0 }, "well": null, "volume_functions": null, "tip": { - "spot_size_x": 7.0, - "spot_size_y": 7.0, + "spot_size_x": 7.5, + "spot_size_y": 7.5, "spot_size_z": 0.0, - "tip_volume": 300.0, - "tip_length": 60.0, - "tip_fitting_depth": 51.0, + "tip_volume": 10.0, + "tip_length": 52.0, + "tip_fitting_depth": 8.2, + "tip_above_rack_length": 8.6, "has_filter": false }, "tube": null, @@ -354,9 +355,9 @@ "type": "tip_rack", "function_name": "PRCXI_300ul_Tips", "docstring": "对应 JSON Code: ZX-001-300\n吸头盒通常比较特殊,需要定义 Tip 对象", - "size_x": 122.11, + "size_x": 127.76, "size_y": 85.48, - "size_z": 58.23, + "size_z": 58.0, "model": "PRCXI_300ul_Tips", "category": "tip_rack", "plate_type": null, @@ -375,21 +376,22 @@ "grid": { "num_items_x": 12, "num_items_y": 8, - "dx": 7.97, - "dy": 5.0, - "dz": 2.0, + "dx": 10.63, + "dy": 7.49, + "dz": 6.6, "item_dx": 9.0, "item_dy": 9.0 }, "well": null, "volume_functions": null, "tip": { - "spot_size_x": 7.0, - "spot_size_y": 7.0, + "spot_size_x": 7.5, + "spot_size_y": 7.5, "spot_size_z": 0.0, "tip_volume": 300.0, "tip_length": 60.0, - "tip_fitting_depth": 51.0, + "tip_fitting_depth": 8.2, + "tip_above_rack_length": 8.6, "has_filter": false }, "tube": null, @@ -788,9 +790,9 @@ "type": "tip_rack", "function_name": "PRCXI_1250uL_Tips", "docstring": "Code: ZX-001-1250", - "size_x": 118.09, - "size_y": 80.7, - "size_z": 107.67, + "size_x": 127.76, + "size_y": 85.48, + "size_z": 98.0, "model": "PRCXI_1250uL_Tips", "category": "tip_rack", "plate_type": null, @@ -809,21 +811,22 @@ "grid": { "num_items_x": 12, "num_items_y": 8, - "dx": 5.57, - "dy": 4.875, - "dz": 2.0, + "dx": 10.63, + "dy": 7.49, + "dz": 6.6, "item_dx": 9.0, "item_dy": 9.0 }, "well": null, "volume_functions": null, "tip": { - "spot_size_x": 7.0, - "spot_size_y": 7.0, - "spot_size_z": 0.0, - "tip_volume": 300.0, - "tip_length": 60.0, - "tip_fitting_depth": 51.0, + "spot_size_x": 7.5, + "spot_size_y": 7.5, + "spot_size_z": 100.0, + "tip_volume": 1250.0, + "tip_length": 100.0, + "tip_fitting_depth": 8.2, + "tip_above_rack_length": 8.6, "has_filter": false }, "tube": null, @@ -836,9 +839,9 @@ "type": "tip_rack", "function_name": "PRCXI_10uL_Tips", "docstring": "Code: ZX-001-10", - "size_x": 120.98, - "size_y": 82.12, - "size_z": 67.0, + "size_x": 127.76, + "size_y": 85.48, + "size_z": 58.0, "model": "PRCXI_10uL_Tips", "category": "tip_rack", "plate_type": null, @@ -857,21 +860,22 @@ "grid": { "num_items_x": 12, "num_items_y": 8, - "dx": 8.49, - "dy": 7.06, - "dz": 2.0, + "dx": 10.63, + "dy": 7.49, + "dz": 14.6, "item_dx": 9.0, "item_dy": 9.0 }, "well": null, "volume_functions": null, "tip": { - "spot_size_x": 7.0, - "spot_size_y": 7.0, + "spot_size_x": 7.5, + "spot_size_y": 7.5, "spot_size_z": 0.0, - "tip_volume": 300.0, - "tip_length": 60.0, - "tip_fitting_depth": 51.0, + "tip_volume": 10.0, + "tip_length": 52.0, + "tip_fitting_depth": 8.2, + "tip_above_rack_length": 8.6, "has_filter": false }, "tube": null, @@ -884,8 +888,8 @@ "type": "tip_rack", "function_name": "PRCXI_1000uL_Tips", "docstring": "Code: ZX-001-1000", - "size_x": 128.09, - "size_y": 85.8, + "size_x": 127.76, + "size_y": 85.48, "size_z": 98.0, "model": "PRCXI_1000uL_Tips", "category": "tip_rack", @@ -905,21 +909,22 @@ "grid": { "num_items_x": 12, "num_items_y": 8, - "dx": 10.525, - "dy": 7.425, - "dz": 2.0, + "dx": 10.63, + "dy": 7.49, + "dz": 6.6, "item_dx": 9.0, "item_dy": 9.0 }, "well": null, "volume_functions": null, "tip": { - "spot_size_x": 7.0, - "spot_size_y": 7.0, - "spot_size_z": 0.0, - "tip_volume": 300.0, - "tip_length": 60.0, - "tip_fitting_depth": 51.0, + "spot_size_x": 7.5, + "spot_size_y": 7.5, + "spot_size_z": 100.0, + "tip_volume": 1000.0, + "tip_length": 100.0, + "tip_fitting_depth": 8.2, + "tip_above_rack_length": 8.6, "has_filter": false }, "tube": null, @@ -968,6 +973,7 @@ "tip_volume": 300.0, "tip_length": 60.0, "tip_fitting_depth": 51.0, + "tip_above_rack_length": null, "has_filter": false }, "tube": null, @@ -1256,6 +1262,55 @@ }, "include_in_template_matching": false, "template_kind": null + }, + { + "id": "5bb65eb2", + "type": "tip_rack", + "function_name": "PRCXI_50uL_tips", + "docstring": "", + "size_x": 127.76, + "size_y": 85.48, + "size_z": 58.0, + "model": "PRCXI_50uL_tips", + "category": null, + "plate_type": null, + "material_info": { + "uuid": "", + "Code": "", + "Name": "", + "materialEnum": null, + "SupplyType": 1 + }, + "registry_category": [ + "prcxi", + "tip_racks" + ], + "registry_description": "", + "grid": { + "num_items_x": 12, + "num_items_y": 8, + "dx": 10.63, + "dy": 7.49, + "dz": 13.6, + "item_dx": 9.0, + "item_dy": 9.0 + }, + "well": null, + "volume_functions": null, + "tip": { + "spot_size_x": 7.5, + "spot_size_y": 7.5, + "spot_size_z": 0.0, + "tip_volume": 50.0, + "tip_length": 53.0, + "tip_fitting_depth": 8.2, + "tip_above_rack_length": 8.6, + "has_filter": false + }, + "tube": null, + "adapter": null, + "include_in_template_matching": false, + "template_kind": "tip_rack" } ] } \ No newline at end of file diff --git a/unilabos/labware_manager/models.py b/unilabos/labware_manager/models.py index c206af30..cd415aaf 100644 --- a/unilabos/labware_manager/models.py +++ b/unilabos/labware_manager/models.py @@ -53,6 +53,7 @@ class TipInfo(BaseModel): tip_volume: float = 300.0 tip_length: float = 60.0 tip_fitting_depth: float = 51.0 + tip_above_rack_length: Optional[float] = None has_filter: bool = False diff --git a/unilabos/labware_manager/static/form_handler.js b/unilabos/labware_manager/static/form_handler.js index 6fa878be..c54a431b 100644 --- a/unilabos/labware_manager/static/form_handler.js +++ b/unilabos/labware_manager/static/form_handler.js @@ -107,6 +107,7 @@ function collectFormData() { tip_volume: g('f-tip_vol') || 300, tip_length: g('f-tip_len') || 60, tip_fitting_depth: g('f-tip_dep') || 51, + tip_above_rack_length: g('f-tip_above'), has_filter: g('f-tip_filter') || false, }; } @@ -155,6 +156,60 @@ document.addEventListener('DOMContentLoaded', () => { if (!form) return; form.addEventListener('input', updatePreview); form.addEventListener('change', updatePreview); + + // tip_above_rack_length 与 dz 互算 + // 公式: tip_length = tip_above_rack_length + size_z - dz + // 规则: 填 tip_above → 自动算 dz;填 dz → 自动算 tip_above + // 改 size_z / tip_length → 优先重算 tip_above(若有值),否则算 dz + + function _getVal(id) { + const el = document.getElementById(id); + return (el && el.value !== '') ? parseFloat(el.value) : null; + } + function _setVal(id, v) { + const el = document.getElementById(id); + if (el) el.value = Math.round(v * 1000) / 1000; + } + + function autoCalcTipAbove(changedId) { + const typeEl = document.getElementById('f-type'); + if (!typeEl || typeEl.value !== 'tip_rack') return; + + const tipLen = _getVal('f-tip_len'); + const sizeZ = _getVal('f-size_z'); + const dz = _getVal('f-grid_dz'); + const above = _getVal('f-tip_above'); + + // 需要 tip_length 和 size_z 才能计算 + if (tipLen == null || sizeZ == null) return; + + if (changedId === 'f-tip_above') { + // 用户填了 tip_above → 算 dz + if (above != null) _setVal('f-grid_dz', above + sizeZ - tipLen); + } else if (changedId === 'f-grid_dz') { + // 用户填了 dz → 算 tip_above + if (dz != null) _setVal('f-tip_above', tipLen - sizeZ + dz); + } else { + // size_z 或 tip_length 变了 → 优先重算 tip_above(若已有值或 dz 已有值) + if (dz != null) { + _setVal('f-tip_above', tipLen - sizeZ + dz); + } else if (above != null) { + _setVal('f-grid_dz', above + sizeZ - tipLen); + } + } + } + + // 绑定 input 事件 + for (const id of ['f-tip_len', 'f-size_z', 'f-grid_dz', 'f-tip_above']) { + const el = document.getElementById(id); + if (el) el.addEventListener('input', () => autoCalcTipAbove(id)); + } + + // 编辑已有 tip_rack 条目时自动补算 tip_above_rack_length + const typeEl = document.getElementById('f-type'); + if (typeEl && typeEl.value === 'tip_rack') { + autoCalcTipAbove('f-grid_dz'); + } }); // 自动居中:根据板尺寸和孔阵列参数计算 dx/dy diff --git a/unilabos/labware_manager/static/labware_viz.js b/unilabos/labware_manager/static/labware_viz.js index a2d0af9c..4a3dc4ee 100644 --- a/unilabos/labware_manager/static/labware_viz.js +++ b/unilabos/labware_manager/static/labware_viz.js @@ -161,6 +161,27 @@ function renderTopDown(container, data) { el.addEventListener('mouseleave', () => el.setAttribute('fill', '#fff')); } } + + // dx / dy 标注(板边到首个子元素左上角) + const dimColor = '#e67e22'; + const firstLeft = pad + dx; // 首列子元素左边 X + const firstTop = pad + dy; // 首行子元素上边 Y + if (dx > 0.1) { + // dx: 板左边 → 首列子元素左边,画在第一行子元素中心高度 + const annY = firstTop + csy / 2; + _line(svg, pad, annY, firstLeft, annY, dimColor, '1,1'); + _line(svg, pad, annY - 2, pad, annY + 2, dimColor); + _line(svg, firstLeft, annY - 2, firstLeft, annY + 2, dimColor); + _text(svg, pad + dx / 2, annY - 2, `dx=${dx}`, '2.5', 'middle', dimColor); + } + if (dy > 0.1) { + // dy: 板上边 → 首行子元素上边,画在第一列子元素中心宽度 + const annX = firstLeft + csx / 2; + _line(svg, annX, pad, annX, firstTop, dimColor, '1,1'); + _line(svg, annX - 2, pad, annX + 2, pad, dimColor); + _line(svg, annX - 2, firstTop, annX + 2, firstTop, dimColor); + _text(svg, annX + 4, pad + dy / 2 + 1, `dy=${dy}`, '2.5', 'start', dimColor); + } } else if (data.type === 'plate_adapter' && data.adapter) { // 绘制适配器凹槽 const adp = data.adapter; @@ -190,17 +211,33 @@ function renderSideProfile(container, data) { // 按比例缩放,侧面以 X-Z 面 const scaleH = Math.max(1, sz / 60); // 让较矮的板子不会太小 + + // 计算枪头露出高度(仅 tip_rack) + const tip = data.tip; + const grid = data.grid; + let tipAbove = 0; + if (data.type === 'tip_rack' && tip) { + if (tip.tip_above_rack_length != null && tip.tip_above_rack_length > 0) { + tipAbove = tip.tip_above_rack_length; + } else if (tip.tip_length && grid) { + const dz = grid.dz || 0; + const calc = tip.tip_length - (sz - dz); + if (calc > 0) tipAbove = calc; + } + } + const drawW = sx; const drawH = sz; const w = drawW + pad * 2 + 30; // 额外空间给标注 - const h = drawH + pad * 2 + 10; + const h = drawH + tipAbove + pad * 2 + 10; const svg = _makeSVG(w, h); const color = TYPE_COLORS[data.type] || '#3b82f6'; - const baseY = pad + drawH; // 底部 Y + const baseY = pad + tipAbove + drawH; // 底部 Y + const rackTopY = pad + tipAbove; // rack 顶部 Y // 板壳矩形 - _rect(svg, pad, pad, drawW, drawH, color + '15', color); + _rect(svg, pad, rackTopY, drawW, drawH, color + '15', color); // 尺寸标注 // X 方向 @@ -208,21 +245,19 @@ function renderSideProfile(container, data) { _text(svg, pad + drawW / 2, baseY + 12, `${sx} mm`, '3.5', 'middle', '#333'); // Z 方向 - _line(svg, pad + drawW + 5, pad, pad + drawW + 5, baseY, '#333'); + _line(svg, pad + drawW + 5, rackTopY, pad + drawW + 5, baseY, '#333'); const zt = document.createElementNS(_svgNS(), 'text'); zt.setAttribute('x', pad + drawW + 12); - zt.setAttribute('y', pad + drawH / 2); + zt.setAttribute('y', rackTopY + drawH / 2); zt.setAttribute('font-size', '3.5'); zt.setAttribute('text-anchor', 'middle'); zt.setAttribute('fill', '#333'); zt.setAttribute('font-family', 'sans-serif'); - zt.setAttribute('transform', `rotate(-90, ${pad + drawW + 12}, ${pad + drawH / 2})`); + zt.setAttribute('transform', `rotate(-90, ${pad + drawW + 12}, ${rackTopY + drawH / 2})`); zt.textContent = `${sz} mm`; svg.appendChild(zt); - const grid = data.grid; const well = data.well; - const tip = data.tip; const tube = data.tube; if (grid && (well || tip || tube)) { @@ -230,6 +265,7 @@ function renderSideProfile(container, data) { const dz = grid.dz || 0; const idx = grid.item_dx || 9; const nx = Math.min(grid.num_items_x || 1, 24); // 最多画24列 + const dimColor = '#e67e22'; const child = well || tube; const childTip = tip; @@ -275,31 +311,71 @@ function renderSideProfile(container, data) { // dz 标注 if (dz > 0) { const lx = pad + dx + 0.5 * idx * nDraw + csx / 2 + 5; - _line(svg, lx, baseY, lx, baseY - dz, '#999', '1,1'); - _text(svg, lx + 6, baseY - dz / 2, `dz=${dz}`, '2.5', 'start', '#999'); + _line(svg, lx, baseY, lx, baseY - dz, dimColor, '1,1'); + _line(svg, lx - 2, baseY, lx + 2, baseY, dimColor); + _line(svg, lx - 2, baseY - dz, lx + 2, baseY - dz, dimColor); + _text(svg, lx + 4, baseY - dz / 2 + 1, `dz=${dz}`, '2.5', 'start', dimColor); + } + // dx 标注 + if (dx > 0.1) { + const annY = rackTopY + 4; + _line(svg, pad, annY, pad + dx, annY, dimColor, '1,1'); + _line(svg, pad, annY - 2, pad, annY + 2, dimColor); + _line(svg, pad + dx, annY - 2, pad + dx, annY + 2, dimColor); + _text(svg, pad + dx / 2, annY - 2, `dx=${dx}`, '2.5', 'middle', dimColor); } } if (childTip) { // 枪头截面 + const tipLen = childTip.tip_length || 50; const nDraw = Math.min(nx, 12); for (let i = 0; i < nDraw; i++) { const cx = pad + dx + 3.5 + i * idx; - const topZ = pad + dz; - const tipLen = childTip.tip_length || 50; - const drawLen = Math.min(tipLen, sz - dz); + // 枪头顶部 = rack顶部 - 露出长度 + const tipTopZ = rackTopY - tipAbove; + const drawLen = Math.min(tipLen, sz - dz + tipAbove); // 枪头轮廓 (梯形) const topW = 4; const botW = 1.5; const p = document.createElementNS(_svgNS(), 'polygon'); p.setAttribute('points', - `${cx - topW / 2},${topZ} ${cx + topW / 2},${topZ} ${cx + botW / 2},${topZ + drawLen} ${cx - botW / 2},${topZ + drawLen}`); + `${cx - topW / 2},${tipTopZ} ${cx + topW / 2},${tipTopZ} ${cx + botW / 2},${tipTopZ + drawLen} ${cx - botW / 2},${tipTopZ + drawLen}`); p.setAttribute('fill', '#10b98133'); p.setAttribute('stroke', '#10b981'); p.setAttribute('stroke-width', '0.3'); svg.appendChild(p); } + + // dz 标注 + if (dz > 0) { + const lx = pad + dx + nDraw * idx + 5; + _line(svg, lx, baseY, lx, baseY - dz, dimColor, '1,1'); + _line(svg, lx - 2, baseY, lx + 2, baseY, dimColor); + _line(svg, lx - 2, baseY - dz, lx + 2, baseY - dz, dimColor); + _text(svg, lx + 4, baseY - dz / 2 + 1, `dz=${dz}`, '2.5', 'start', dimColor); + } + // dx 标注 + if (dx > 0.1) { + const annY = rackTopY + 4; + _line(svg, pad, annY, pad + dx, annY, dimColor, '1,1'); + _line(svg, pad, annY - 2, pad, annY + 2, dimColor); + _line(svg, pad + dx, annY - 2, pad + dx, annY + 2, dimColor); + _text(svg, pad + dx / 2, annY - 2, `dx=${dx}`, '2.5', 'middle', dimColor); + } + + // 露出长度标注线 + if (tipAbove > 0) { + const annotX = pad + dx + nDraw * idx + 8; + // rack 顶部水平参考线 + _line(svg, annotX - 3, rackTopY, annotX + 3, rackTopY, '#10b981'); + // 枪头顶部水平参考线 + _line(svg, annotX - 3, rackTopY - tipAbove, annotX + 3, rackTopY - tipAbove, '#10b981'); + // 竖直标注线 + _line(svg, annotX, rackTopY - tipAbove, annotX, rackTopY, '#10b981', '1,1'); + _text(svg, annotX + 2, rackTopY - tipAbove / 2 + 1, `露出=${Math.round(tipAbove * 100) / 100}mm`, '2.5', 'start', '#10b981'); + } } } else if (data.type === 'plate_adapter' && data.adapter) { const adp = data.adapter; @@ -309,10 +385,10 @@ function renderSideProfile(container, data) { const ahx = adp.adapter_hole_size_x || 127; // 凹槽截面 - _rect(svg, pad + adx_val, pad + adz, ahx, ahz, '#ede9fe', '#8b5cf6'); - _text(svg, pad + adx_val + ahx / 2, pad + adz + ahz / 2 + 1, `hole: ${ahz}mm deep`, '3', 'middle', '#8b5cf6'); + _rect(svg, pad + adx_val, rackTopY + adz, ahx, ahz, '#ede9fe', '#8b5cf6'); + _text(svg, pad + adx_val + ahx / 2, rackTopY + adz + ahz / 2 + 1, `hole: ${ahz}mm deep`, '3', 'middle', '#8b5cf6'); } else if (data.type === 'trash') { - _text(svg, pad + drawW / 2, pad + drawH / 2, 'TRASH', '8', 'middle', '#ef4444'); + _text(svg, pad + drawW / 2, rackTopY + drawH / 2, 'TRASH', '8', 'middle', '#ef4444'); } container.appendChild(svg); @@ -323,13 +399,21 @@ function renderSideProfile(container, data) { function _enableZoomPan(svgEl, origViewBox) { const parts = origViewBox.split(' ').map(Number); let vx = parts[0], vy = parts[1], vw = parts[2], vh = parts[3]; - const origW = vw, origH = vh; + const origVx = vx, origVy = vy, origW = vw, origH = vh; const MIN_SCALE = 0.5, MAX_SCALE = 5; function applyViewBox() { svgEl.setAttribute('viewBox', `${vx} ${vy} ${vw} ${vh}`); } + function resetView() { + vx = origVx; vy = origVy; vw = origW; vh = origH; + applyViewBox(); + } + + // 将 resetView 挂到 svg 元素上,方便外部调用 + svgEl._resetView = resetView; + svgEl.addEventListener('wheel', function (e) { e.preventDefault(); if (e.ctrlKey) { @@ -356,3 +440,11 @@ function _enableZoomPan(svgEl, origViewBox) { applyViewBox(); }, { passive: false }); } + +// 回中按钮:重置指定容器内 SVG 的 viewBox +function resetSvgView(containerId) { + const container = document.getElementById(containerId); + if (!container) return; + const svg = container.querySelector('svg'); + if (svg && svg._resetView) svg._resetView(); +} diff --git a/unilabos/labware_manager/templates/detail.html b/unilabos/labware_manager/templates/detail.html index 9219fc96..a358f9d1 100644 --- a/unilabos/labware_manager/templates/detail.html +++ b/unilabos/labware_manager/templates/detail.html @@ -85,7 +85,10 @@ Spot 尺寸{{ item.tip.spot_size_x }} x {{ item.tip.spot_size_y }} x {{ item.tip.spot_size_z }} 容量{{ item.tip.tip_volume }} uL 长度{{ item.tip.tip_length }} mm - 配合深度{{ item.tip.tip_fitting_depth }} mm + 取枪头插入深度{{ item.tip.tip_fitting_depth }} mm + {% if item.tip.tip_above_rack_length is not none %} + 枪头露出枪头盒长度{{ item.tip.tip_above_rack_length }} mm + {% endif %} 有滤芯{{ item.tip.has_filter }} @@ -127,11 +130,17 @@
-

俯视图 (Top-Down)

+

+ 俯视图 (Top-Down) + +

-

侧面截面图 (Side Profile)

+

+ 侧面截面图 (Side Profile) + +

diff --git a/unilabos/labware_manager/templates/edit.html b/unilabos/labware_manager/templates/edit.html index 5d5eb2c1..23e58efc 100644 --- a/unilabos/labware_manager/templates/edit.html +++ b/unilabos/labware_manager/templates/edit.html @@ -147,8 +147,15 @@
-
-
+
+
+
+
+ + + 公式: tip_length = tip_above + size_z - dz;填 tip_above 自动算 dz,填 dz 自动算 tip_above
@@ -219,11 +226,17 @@
-

预览: 俯视图

+

+ 预览: 俯视图 + +

-

预览: 侧面截面图

+

+ 预览: 侧面截面图 + +

diff --git a/unilabos/registry/resources/prcxi/plate_adapters.yaml b/unilabos/registry/resources/prcxi/plate_adapters.yaml index 3e960f2e..93cde166 100644 --- a/unilabos/registry/resources/prcxi/plate_adapters.yaml +++ b/unilabos/registry/resources/prcxi/plate_adapters.yaml @@ -3,7 +3,7 @@ PRCXI_30mm_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_30mm_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_30mm_Adapter' type: pylabrobot description: '30mm适配器 (Code: ZX-58-30)' handles: [] @@ -15,7 +15,7 @@ PRCXI_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Adapter' type: pylabrobot description: '适配器 (Code: Fhh478)' handles: [] @@ -27,7 +27,7 @@ PRCXI_Deep10_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep10_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep10_Adapter' type: pylabrobot description: '10ul专用深孔板适配器 (Code: ZX-002-10)' handles: [] @@ -39,7 +39,7 @@ PRCXI_Deep300_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep300_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Deep300_Adapter' type: pylabrobot description: '300ul深孔板适配器 (Code: ZX-002-300)' handles: [] @@ -51,7 +51,7 @@ PRCXI_PCR_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Adapter' type: pylabrobot description: '全裙边 PCR适配器 (Code: ZX-58-0001)' handles: [] @@ -63,7 +63,7 @@ PRCXI_Reservoir_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Reservoir_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Reservoir_Adapter' type: pylabrobot description: '储液槽 适配器 (Code: ZX-ADP-001)' handles: [] @@ -75,7 +75,7 @@ PRCXI_Tip10_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip10_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip10_Adapter' type: pylabrobot description: '吸头10ul 适配器 (Code: ZX-58-10)' handles: [] @@ -87,7 +87,7 @@ PRCXI_Tip1250_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip1250_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip1250_Adapter' type: pylabrobot description: 'Tip头适配器 1250uL (Code: ZX-58-1250)' handles: [] @@ -99,7 +99,7 @@ PRCXI_Tip300_Adapter: - prcxi - plate_adapters class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip300_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_Tip300_Adapter' type: pylabrobot description: 'ZHONGXI 适配器 300uL (Code: ZX-58-300)' handles: [] diff --git a/unilabos/registry/resources/prcxi/plates.yaml b/unilabos/registry/resources/prcxi/plates.yaml index b8527dbf..00bf7fa9 100644 --- a/unilabos/registry/resources/prcxi/plates.yaml +++ b/unilabos/registry/resources/prcxi/plates.yaml @@ -3,7 +3,7 @@ PRCXI_48_DeepWell: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_48_DeepWell + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_48_DeepWell' type: pylabrobot description: '48孔深孔板 (Code: 22)' handles: [] @@ -15,7 +15,7 @@ PRCXI_96_DeepWell: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_96_DeepWell + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_96_DeepWell' type: pylabrobot description: '96深孔板 (Code: q2)' handles: [] @@ -27,7 +27,7 @@ PRCXI_AGenBio_4_troughplate: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_AGenBio_4_troughplate + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_AGenBio_4_troughplate' type: pylabrobot description: '4道储液槽 (Code: sdfrth654)' handles: [] @@ -39,7 +39,7 @@ PRCXI_BioER_96_wellplate: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioER_96_wellplate + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioER_96_wellplate' type: pylabrobot description: '2.2ml 深孔板 (Code: ZX-019-2.2)' handles: [] @@ -51,7 +51,7 @@ PRCXI_BioRad_384_wellplate: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioRad_384_wellplate + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_BioRad_384_wellplate' type: pylabrobot description: '384板 (Code: q3)' handles: [] @@ -63,7 +63,7 @@ PRCXI_CellTreat_96_wellplate: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_CellTreat_96_wellplate + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_CellTreat_96_wellplate' type: pylabrobot description: '细菌培养皿 (Code: ZX-78-096)' handles: [] @@ -75,7 +75,7 @@ PRCXI_PCR_Plate_200uL_nonskirted: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_nonskirted + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_nonskirted' type: pylabrobot description: '0.2ml PCR 板 (Code: ZX-023-0.2)' handles: [] @@ -87,7 +87,7 @@ PRCXI_PCR_Plate_200uL_semiskirted: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_semiskirted + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_semiskirted' type: pylabrobot description: '0.2ml PCR 板 (Code: ZX-023-0.2)' handles: [] @@ -99,7 +99,7 @@ PRCXI_PCR_Plate_200uL_skirted: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_skirted + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_PCR_Plate_200uL_skirted' type: pylabrobot description: '0.2ml PCR 板 (Code: ZX-023-0.2)' handles: [] @@ -111,7 +111,7 @@ PRCXI_nest_12_troughplate: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_12_troughplate + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_12_troughplate' type: pylabrobot description: '12道储液槽 (Code: 12道储液槽)' handles: [] @@ -123,7 +123,7 @@ PRCXI_nest_1_troughplate: - prcxi - plates class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_1_troughplate + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_nest_1_troughplate' type: pylabrobot description: '储液槽 (Code: ZX-58-10000)' handles: [] diff --git a/unilabos/registry/resources/prcxi/tip_racks.yaml b/unilabos/registry/resources/prcxi/tip_racks.yaml index f6d2e7f0..f0bc73e9 100644 --- a/unilabos/registry/resources/prcxi/tip_racks.yaml +++ b/unilabos/registry/resources/prcxi/tip_racks.yaml @@ -3,7 +3,7 @@ PRCXI_1000uL_Tips: - prcxi - tip_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1000uL_Tips + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1000uL_Tips' type: pylabrobot description: '1000μL Tip头 (Code: ZX-001-1000)' handles: [] @@ -15,7 +15,7 @@ PRCXI_10uL_Tips: - prcxi - tip_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10uL_Tips + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10uL_Tips' type: pylabrobot description: '10μL Tip头 (Code: ZX-001-10)' handles: [] @@ -27,7 +27,7 @@ PRCXI_10ul_eTips: - prcxi - tip_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10ul_eTips + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_10ul_eTips' type: pylabrobot description: '10μL加长 Tip头 (Code: ZX-001-10+)' handles: [] @@ -39,7 +39,7 @@ PRCXI_1250uL_Tips: - prcxi - tip_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1250uL_Tips + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_1250uL_Tips' type: pylabrobot description: '1250μL Tip头 (Code: ZX-001-1250)' handles: [] @@ -51,7 +51,7 @@ PRCXI_200uL_Tips: - prcxi - tip_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_200uL_Tips + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_200uL_Tips' type: pylabrobot description: '200μL Tip头 (Code: ZX-001-200)' handles: [] @@ -63,10 +63,22 @@ PRCXI_300ul_Tips: - prcxi - tip_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_300ul_Tips + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_300ul_Tips' type: pylabrobot description: '300μL Tip头 (Code: ZX-001-300)' handles: [] icon: '' init_param_schema: {} version: 1.0.0 +PRCXI_50uL_tips: + category: + - prcxi + - tip_racks + class: + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_50uL_tips' + type: pylabrobot + description: PRCXI_50uL_tips + handles: [] + icon: '' + init_param_schema: {} + version: 1.0.0 diff --git a/unilabos/registry/resources/prcxi/trash.yaml b/unilabos/registry/resources/prcxi/trash.yaml index 952a832b..65f034fb 100644 --- a/unilabos/registry/resources/prcxi/trash.yaml +++ b/unilabos/registry/resources/prcxi/trash.yaml @@ -3,7 +3,7 @@ PRCXI_trash: - prcxi - trash class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_trash + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_trash' type: pylabrobot description: '废弃槽 (Code: q1)' handles: [] diff --git a/unilabos/registry/resources/prcxi/tube_racks.yaml b/unilabos/registry/resources/prcxi/tube_racks.yaml index 6510c16c..c0e54d1d 100644 --- a/unilabos/registry/resources/prcxi/tube_racks.yaml +++ b/unilabos/registry/resources/prcxi/tube_racks.yaml @@ -3,7 +3,7 @@ PRCXI_EP_Adapter: - prcxi - tube_racks class: - module: unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_EP_Adapter + module: 'unilabos.devices.liquid_handling.prcxi.prcxi_labware:PRCXI_EP_Adapter' type: pylabrobot description: 'ep适配器 (Code: 1)' handles: []