From c16756ddb319fdbcbec8b99524db540284b80269 Mon Sep 17 00:00:00 2001 From: ZiWei <131428629+ZiWei09@users.noreply.github.com> Date: Fri, 21 Nov 2025 11:33:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(bioyond):=20=E6=9B=B4=E6=96=B0=E4=BB=93?= =?UTF-8?q?=E5=BA=93=E5=B8=83=E5=B1=80=E5=92=8C=E5=B0=BA=E5=AF=B8=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=AB=96=E5=90=91=E6=8E=92=E5=88=97=E7=9A=84?= =?UTF-8?q?=E6=B5=8B=E9=87=8F=E5=B0=8F=E7=93=B6=E5=92=8C=E8=AF=95=E5=89=82?= =?UTF-8?q?=E5=AD=98=E6=94=BE=E5=A0=86=E6=A0=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- unilabos/resources/bioyond/decks.py | 10 +++--- unilabos/resources/bioyond/warehouses.py | 38 +++++++++++++++------- unilabos/resources/graphio.py | 41 ++++++++++++++++++++---- unilabos/resources/warehouse.py | 12 +++++++ 4 files changed, 76 insertions(+), 25 deletions(-) diff --git a/unilabos/resources/bioyond/decks.py b/unilabos/resources/bioyond/decks.py index 5572536..81d873d 100644 --- a/unilabos/resources/bioyond/decks.py +++ b/unilabos/resources/bioyond/decks.py @@ -49,15 +49,13 @@ class BIOYOND_PolymerReactionStation_Deck(Deck): "测量小瓶仓库(测密度)": bioyond_warehouse_density_vial("测量小瓶仓库(测密度)"), # A01~B03 } self.warehouse_locations = { - "堆栈1左": Coordinate(0.0, 430.0, 0.0), # 左侧位置 - "堆栈1右": Coordinate(2500.0, 430.0, 0.0), # 右侧位置 - "站内试剂存放堆栈": Coordinate(640.0, 480.0, 0.0), + "堆栈1左": Coordinate(-200.0, 450.0, 0.0), # 左侧位置 + "堆栈1右": Coordinate(2350.0, 450.0, 0.0), # 右侧位置 + "站内试剂存放堆栈": Coordinate(730.0, 390.0, 0.0), # "移液站内10%分装液体准备仓库": Coordinate(1200.0, 600.0, 0.0), "站内Tip盒堆栈": Coordinate(300.0, 150.0, 0.0), - "测量小瓶仓库(测密度)": Coordinate(922.0, 552.0, 0.0), + "测量小瓶仓库(测密度)": Coordinate(940.0, 530.0, 0.0), } - self.warehouses["站内试剂存放堆栈"].rotation = Rotation(z=90) - self.warehouses["测量小瓶仓库(测密度)"].rotation = Rotation(z=270) for warehouse_name, warehouse in self.warehouses.items(): self.assign_child_resource(warehouse, location=self.warehouse_locations[warehouse_name]) diff --git a/unilabos/resources/bioyond/warehouses.py b/unilabos/resources/bioyond/warehouses.py index ae9e473..503dadb 100644 --- a/unilabos/resources/bioyond/warehouses.py +++ b/unilabos/resources/bioyond/warehouses.py @@ -46,41 +46,55 @@ def bioyond_warehouse_1x4x4_right(name: str) -> WareHouse: ) def bioyond_warehouse_density_vial(name: str) -> WareHouse: - """创建测量小瓶仓库(测密度) A01~B03""" + """创建测量小瓶仓库(测密度) - 竖向排列2列3行 + 布局(从下到上,从左到右): + | A03 | B03 | ← 顶部 + | A02 | B02 | ← 中部 + | A01 | B01 | ← 底部 + """ return warehouse_factory( name=name, - num_items_x=3, # 3列(01-03) - num_items_y=2, # 2行(A-B) + num_items_x=2, # 2列(A, B) + num_items_y=3, # 3行(01-03,从下到上) num_items_z=1, # 1层 dx=10.0, dy=10.0, dz=10.0, - item_dx=40.0, - item_dy=40.0, + item_dx=40.0, # 列间距(A到B的横向距离) + item_dy=40.0, # 行间距(01到02到03的竖向距离) item_dz=50.0, - # 用更小的 resource_size 来表现 "小点的孔位" + # ⭐ 竖向warehouse:槽位尺寸也是竖向的(小瓶已经是正方形,无需调整) resource_size_x=30.0, resource_size_y=30.0, resource_size_z=12.0, category="warehouse", col_offset=0, - layout="row-major", + layout="vertical-col-major", # ⭐ 竖向warehouse专用布局 ) def bioyond_warehouse_reagent_storage(name: str) -> WareHouse: - """创建BioYond站内试剂存放堆栈(A01~A02, 1行×2列)""" + """创建BioYond站内试剂存放堆栈 - 竖向排列1列2行 + 布局(竖向,从下到上): + | A02 | ← 顶部 + | A01 | ← 底部 + """ return warehouse_factory( name=name, - num_items_x=2, # 2列(01-02) - num_items_y=1, # 1行(A) + num_items_x=1, # 1列 + num_items_y=2, # 2行(01-02,从下到上) num_items_z=1, # 1层 dx=10.0, dy=10.0, dz=10.0, - item_dx=137.0, - item_dy=96.0, + item_dx=96.0, # 列间距(这里只有1列,不重要) + item_dy=137.0, # 行间距(A01到A02的竖向距离) item_dz=120.0, + # ⭐ 竖向warehouse:交换槽位尺寸,使槽位框也是竖向的 + resource_size_x=86.0, # 原来的 resource_size_y + resource_size_y=127.0, # 原来的 resource_size_x + resource_size_z=25.0, category="warehouse", + layout="vertical-col-major", # ⭐ 竖向warehouse专用布局 ) def bioyond_warehouse_tipbox_storage(name: str) -> WareHouse: diff --git a/unilabos/resources/graphio.py b/unilabos/resources/graphio.py index c1f5c90..3d3f767 100644 --- a/unilabos/resources/graphio.py +++ b/unilabos/resources/graphio.py @@ -763,6 +763,22 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[st if not locations: logger.debug(f"[物料位置] {unique_name} 没有location信息,跳过warehouse放置") + # ⭐ 预先检查:如果物料的任何location在竖向warehouse中,提前交换尺寸 + # 这样可以避免多个location时尺寸不一致的问题 + needs_size_swap = False + for loc in locations: + wh_name_check = loc.get("whName") + if wh_name_check in ["站内试剂存放堆栈", "测量小瓶仓库(测密度)"]: + needs_size_swap = True + break + + if needs_size_swap and hasattr(plr_material, 'size_x') and hasattr(plr_material, 'size_y'): + original_x = plr_material.size_x + original_y = plr_material.size_y + plr_material.size_x = original_y + plr_material.size_y = original_x + logger.debug(f" 物料 {unique_name} 将放入竖向warehouse,预先交换尺寸: {original_x}×{original_y} → {plr_material.size_x}×{plr_material.size_y}") + for loc in locations: wh_name = loc.get("whName") logger.debug(f"[物料位置] {unique_name} 尝试放置到 warehouse: {wh_name} (Bioyond坐标: x={loc.get('x')}, y={loc.get('y')}, z={loc.get('z')})") @@ -784,7 +800,6 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[st logger.debug(f"[Warehouse匹配] 找到warehouse: {wh_name} (容量: {warehouse.capacity}, 行×列: {warehouse.num_items_x}×{warehouse.num_items_y})") # Bioyond坐标映射 (重要!): x→行(1=A,2=B...), y→列(1=01,2=02...), z→层(通常=1) - # PyLabRobot warehouse是列优先存储: A01,B01,C01,D01, A02,B02,C02,D02, ... x = loc.get("x", 1) # 行号 (1-based: 1=A, 2=B, 3=C, 4=D) y = loc.get("y", 1) # 列号 (1-based: 1=01, 2=02, 3=03...) z = loc.get("z", 1) # 层号 (1-based, 通常为1) @@ -793,12 +808,23 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[st if wh_name == "堆栈1右": y = y - 4 # 将5-8映射到1-4 - # 特殊处理:对于1行×N列的横向warehouse(如站内试剂存放堆栈) - # Bioyond的y坐标表示线性位置序号,而不是列号 - if warehouse.num_items_y == 1: - # 1行warehouse: 直接用y作为线性索引 - idx = y - 1 - logger.debug(f"1行warehouse {wh_name}: y={y} → idx={idx}") + # 特殊处理竖向warehouse(站内试剂存放堆栈、测量小瓶仓库) + # 这些warehouse使用 vertical-col-major 布局 + if wh_name in ["站内试剂存放堆栈", "测量小瓶仓库(测密度)"]: + # vertical-col-major 布局的坐标映射: + # - Bioyond的x(1=A,2=B)对应warehouse的列(col, x方向) + # - Bioyond的y(1=01,2=02,3=03)对应warehouse的行(row, y方向),从下到上 + # vertical-col-major 中: row=0 对应底部,row=n-1 对应顶部 + # Bioyond y=1(01) 对应底部 → row=0, y=2(02) 对应中间 → row=1 + # 索引计算: idx = row * num_cols + col + col_idx = x - 1 # Bioyond的x(A,B) → col索引(0,1) + row_idx = y - 1 # Bioyond的y(01,02,03) → row索引(0,1,2) + layer_idx = z - 1 + + idx = layer_idx * (warehouse.num_items_x * warehouse.num_items_y) + row_idx * warehouse.num_items_x + col_idx + logger.debug(f"🔍 竖向warehouse {wh_name}: Bioyond(x={x},y={y},z={z}) → warehouse(col={col_idx},row={row_idx},layer={layer_idx}) → idx={idx}, capacity={warehouse.capacity}") + + # 普通横向warehouse的处理 else: # 多行warehouse: 根据 layout 使用不同的索引计算 row_idx = x - 1 # x表示行: 转为0-based @@ -822,6 +848,7 @@ def resource_bioyond_to_plr(bioyond_materials: list[dict], type_mapping: Dict[st if 0 <= idx < warehouse.capacity: if warehouse[idx] is None or isinstance(warehouse[idx], ResourceHolder): + # 物料尺寸已在放入warehouse前根据需要进行了交换 warehouse[idx] = plr_material logger.debug(f"✅ 物料 {unique_name} 放置到 {wh_name}[{idx}] (Bioyond坐标: x={loc.get('x')}, y={loc.get('y')})") else: diff --git a/unilabos/resources/warehouse.py b/unilabos/resources/warehouse.py index 4dcda6d..9fbbe09 100644 --- a/unilabos/resources/warehouse.py +++ b/unilabos/resources/warehouse.py @@ -42,6 +42,10 @@ def warehouse_factory( if layout == "row-major": # 行优先:row=0(A行) 应该显示在上方,需要较小的 y 值 y = dy + row * item_dy + elif layout == "vertical-col-major": + # 竖向warehouse: row=0 对应顶部(y小),row=n-1 对应底部(y大) + # 但标签 01 应该在底部,所以使用反向映射 + y = dy + (num_items_y - row - 1) * item_dy else: # 列优先:保持原逻辑(row=0 对应较大的 y) y = dy + (num_items_y - row - 1) * item_dy @@ -66,6 +70,14 @@ def warehouse_factory( # 行优先顺序: A01,A02,A03,A04, B01,B02,B03,B04 # locations[0] 对应 row=0, y最大(前端顶部)→ 应该是 A01 keys = [f"{LETTERS[j]}{i + 1 + col_offset:02d}" for j in range(len_y) for i in range(len_x)] + elif layout == "vertical-col-major": + # ⭐ 竖向warehouse专用布局: + # 字母(A,B,C...)对应列(横向, x方向),数字(01,02,03...)对应行(竖向, y方向,从下到上) + # locations 生成顺序: row→col (row=0,col=0 → row=0,col=1 → row=1,col=0 → ...) + # 其中 row=0 对应底部(y大),row=n-1 对应顶部(y小) + # 标签中 01 对应底部(row=0),02 对应中间(row=1),03 对应顶部(row=2) + # 标签顺序: A01,B01,A02,B02,A03,B03 + keys = [f"{LETTERS[col]}{row + 1 + col_offset:02d}" for row in range(len_y) for col in range(len_x)] else: # 列优先顺序: A01,B01,C01,D01, A02,B02,C02,D02 keys = [f"{LETTERS[j]}{i + 1 + col_offset:02d}" for i in range(len_x) for j in range(len_y)]