feat: RNA land resource-system mega plan Phases 1-4 plus Phase 5 stack orientation

- Phase 1: fix _publish_resource_tree_update to call update_resource via run_async_func with deck resource list.
- Phase 2: add ID-first material placement resolver chain with material_info and warehouse_inventory caches.
- Phase 3: classify stock-material rows as slot_labware vs liquid_content; idempotent reagent attachment by Bioyond materialId.
- Phase 4: introduce SirnaResourceSynchronizer over BioyondResourceSynchronizer; install once in post_init without double-sync.
- Phase 5: numeric stack orientation and bioyond_axis support in bioyond warehouses/__init__/decks (carries forward prior in-progress edits).
- _resolve_location_to_warehouse now raises on ambiguity; deck constructor accepts warehouse_bioyond_ids kwarg.
This commit is contained in:
yxz321
2026-05-08 21:41:03 +08:00
parent 1f93740580
commit 7c83e1bd51
4 changed files with 732 additions and 80 deletions

View File

@@ -0,0 +1 @@
from . import sirna_materials # noqa: F401 ensure @resource classes are importable for PLR deserialize

View File

@@ -1,4 +1,5 @@
from os import name
from pylabrobot.resources import Deck, Coordinate, Rotation
from unilabos.registry.decorators import resource
@@ -112,6 +113,16 @@ class BIOYOND_PolymerPreparationStation_Deck(Deck):
icon="配液站.webp",
)
class BIOYOND_SirnaStation_Deck(Deck):
WAREHOUSE_BIOYOND_AXIS = {
"G3移液站": "xy_col_row",
"自动化堆栈": "xy_col_row",
"离心机配平板堆栈": "xy_col_row",
}
# Bioyond warehouse UUID -> 本地仓库名称 映射。
# 留空时由配置station config 的 ``warehouse_bioyond_ids``)注入。
# graph 节点也可在 deck.config.warehouse_bioyond_ids 覆盖。
WAREHOUSE_BIOYOND_IDS: dict = {}
def __init__(
self,
name: str = "SirnaStation_Deck",
@@ -119,9 +130,15 @@ class BIOYOND_SirnaStation_Deck(Deck):
size_y: float = 1080.0,
size_z: float = 1500.0,
category: str = "deck",
setup: bool = False
setup: bool = False,
warehouse_bioyond_ids: dict | None = None,
**kwargs,
) -> None:
super().__init__(name=name, size_x=size_x, size_y=size_y, size_z=size_z)
# 按需写入实例级覆盖;保留默认空 mapping避免改动模型常量。
self.warehouse_bioyond_ids: dict = dict(self.WAREHOUSE_BIOYOND_IDS)
if warehouse_bioyond_ids:
self.warehouse_bioyond_ids.update(warehouse_bioyond_ids)
if setup:
self.setup()
@@ -130,7 +147,15 @@ class BIOYOND_SirnaStation_Deck(Deck):
if data.get("children") and data.get("setup") is True:
data = data.copy()
data["setup"] = False
return super().deserialize(data, allow_marshal=allow_marshal)
result = super().deserialize(data, allow_marshal=allow_marshal)
result._ensure_sirna_warehouse_axis()
return result
def _ensure_sirna_warehouse_axis(self) -> None:
for child in getattr(self, "children", []):
axis = self.WAREHOUSE_BIOYOND_AXIS.get(getattr(child, "name", ""))
if axis and not hasattr(child, "bioyond_axis"):
child.bioyond_axis = axis
def setup(self) -> None:
# Sirna 读接口 /api/storage/location/locations-by-type 返回完整固定堆栈清单。

View File

@@ -4,6 +4,19 @@ from pylabrobot.resources.carrier import ResourceHolder, create_homogeneous_reso
from unilabos.resources.warehouse import WareHouse, warehouse_factory
class BioyondWareHouse(WareHouse):
"""Bioyond 仓库,额外保存服务端 x/y 坐标语义。"""
def __init__(self, *args, bioyond_axis: str = "xy_row_col", **kwargs):
super().__init__(*args, **kwargs)
self.bioyond_axis = bioyond_axis
def serialize(self) -> dict:
data = super().serialize()
data["bioyond_axis"] = self.bioyond_axis
return data
def bioyond_warehouse_numeric_stack(
name: str,
rows: int = 10,
@@ -44,7 +57,7 @@ def bioyond_warehouse_numeric_stack(
for row in range(num_items_y)
for col in range(num_items_x)
]
warehouse = WareHouse(
warehouse = BioyondWareHouse(
name=name,
size_x=dx + item_dx * num_items_x,
size_y=dy + item_dy * num_items_y,
@@ -55,8 +68,8 @@ def bioyond_warehouse_numeric_stack(
ordering_layout="row-major",
sites={key: holder for key, holder in zip(keys, holders.values())},
category="warehouse",
bioyond_axis=bioyond_axis,
)
warehouse.bioyond_axis = bioyond_axis
return warehouse