添加抓取后物料上传

This commit is contained in:
zhangshixiang
2025-12-28 01:44:34 +08:00
parent ad1312cf26
commit cfe64b023b
3 changed files with 150 additions and 58 deletions

View File

@@ -30,10 +30,11 @@ from pylabrobot.liquid_handling.standard import (
ResourceMove, ResourceMove,
ResourceDrop, ResourceDrop,
) )
from pylabrobot.resources import ResourceHolder, ResourceStack, Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash, PlateAdapter, TubeRack from pylabrobot.resources import ResourceHolder, ResourceStack, Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash, PlateAdapter, TubeRack, create_homogeneous_resources, create_ordered_items_2d
from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract, SimpleReturn from unilabos.devices.liquid_handling.liquid_handler_abstract import LiquidHandlerAbstract, SimpleReturn
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode from unilabos.resources.itemized_carrier import ItemizedCarrier
from unilabos.ros.nodes.base_device_node import BaseROS2DeviceNode, ROS2DeviceNode
class PRCXIError(RuntimeError): class PRCXIError(RuntimeError):
@@ -86,19 +87,81 @@ class PRCXI9300Container(Plate):
category: str, category: str,
ordering: collections.OrderedDict, ordering: collections.OrderedDict,
model: Optional[str] = None, model: Optional[str] = None,
material_info: Optional[Dict[str, Any]] = None,
ordering_layout: str = "col-major",
**kwargs, **kwargs,
): ):
super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model) super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model)
self._unilabos_state = {} self._unilabos_state = {}
self.sites = kwargs.get("sites", [])
self.sites = create_homogeneous_resources(
klass=ResourceHolder,
locations=[Coordinate(0, 0, 0)],
resource_size_x=size_x,
resource_size_y=size_y,
resource_size_z=size_z,
name_prefix=name,
)[0]
# 为 ItemizedCarrier 添加 _unilabos_state 属性,以便与其他 PRCXI 组件兼容
sites_resource = ItemizedCarrier(
name=name+"_sites",
sites={name: self.sites},
size_x=size_x,
size_y=size_y,
size_z=size_z,
category="warehouse",
model=model,
)
sites_resource._unilabos_state = {} # 添加 _unilabos_state 属性
if material_info:
sites_resource._unilabos_state["Material"] = material_info
self.assign_child_resource(sites_resource, location=self.sites.location)
# 保存排序方式供graphio.py的坐标映射使用
# 使用独立属性避免与父类的layout冲突
self.ordering_layout = ordering_layout
def serialize(self) -> dict:
"""序列化时保存 ordering_layout 属性"""
data = super().serialize()
data['ordering_layout'] = self.ordering_layout
return data
def load_state(self, state: Dict[str, Any]) -> None: def load_state(self, state: Dict[str, Any]) -> None:
"""从给定的状态加载工作台信息。""" """从给定的状态加载工作台信息。"""
super().load_state(state) super().load_state(state)
self._unilabos_state = state self._unilabos_state = state
def get_site(self) -> ResourceHolder:
"""获取容器的站点"""
return self.sites
def add_resource_to_site(self, resource) -> None:
"""向站点添加资源"""
self.sites.assign_child_resource(resource)
def get_resource_at_site(self):
"""获取站点上的资源"""
return self.sites.children[0] if self.sites.children else None
def serialize_state(self) -> Dict[str, Dict[str, Any]]: def serialize_state(self) -> Dict[str, Dict[str, Any]]:
data = super().serialize_state() data = super().serialize_state()
data.update(self._unilabos_state) data.update(self._unilabos_state)
# 避免序列化 ResourceHolder 对象
if hasattr(self, 'sites') and self.sites:
# 创建 sites 的可序列化版本
if hasattr(self.sites, '__class__') and 'pylabrobot' in str(self.sites.__class__.__module__):
data['sites'] = {
"__pylabrobot_object__": True,
"class": self.sites.__class__.__name__,
"module": self.sites.__class__.__module__,
"name": getattr(self.sites, 'name', str(self.sites))
}
else:
data['sites'] = self.sites
return data return data
class PRCXI9300Plate(Plate): class PRCXI9300Plate(Plate):
""" """
@@ -210,9 +273,16 @@ class PRCXI9300TipRack(TipRack):
# 使用 ordering 参数,只包含位置信息(键) # 使用 ordering 参数,只包含位置信息(键)
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys()) ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
else: else:
# ordering 的值已经是对象,可以直接使用 # ordering 的值已经是对象,需要过滤掉 None 值
items = ordering # 只保留有效的对象,用于 ordered_items 参数
ordering_param = None valid_items = {k: v for k, v in ordering.items() if v is not None}
if valid_items:
items = valid_items
ordering_param = None
else:
# 如果没有有效对象,使用 ordering 参数
items = None
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
else: else:
items = None items = None
ordering_param = None ordering_param = None
@@ -348,9 +418,16 @@ class PRCXI9300TubeRack(TubeRack):
# 使用 ordering 参数,只包含位置信息(键) # 使用 ordering 参数,只包含位置信息(键)
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys()) ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
else: else:
# ordering 的值已经是对象,可以直接使用 # ordering 的值已经是对象,需要过滤掉 None 值
items_to_pass = ordering # 只保留有效的对象,用于 ordered_items 参数
ordering_param = None valid_items = {k: v for k, v in ordering.items() if v is not None}
if valid_items:
items_to_pass = valid_items
ordering_param = None
else:
# 如果没有有效对象,使用 ordering 参数
items_to_pass = None
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
elif items is not None: elif items is not None:
# 兼容旧的 items 参数 # 兼容旧的 items 参数
items_to_pass = items items_to_pass = items
@@ -804,7 +881,7 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
**backend_kwargs, **backend_kwargs,
): ):
return await super().move_plate( res = await super().move_plate(
plate, plate,
to, to,
intermediate_locations, intermediate_locations,
@@ -816,6 +893,12 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
target_plate_number = to, target_plate_number = to,
**backend_kwargs, **backend_kwargs,
) )
plate.unassign()
to.assign_child_resource(plate, location=Coordinate(0, 0, 0))
ROS2DeviceNode.run_async_func(self._ros_node.update_resource, True, **{
"resources": [self.deck]
})
return res
class PRCXI9300Backend(LiquidHandlerBackend): class PRCXI9300Backend(LiquidHandlerBackend):
"""PRCXI 9300 的后端实现,继承自 LiquidHandlerBackend。 """PRCXI 9300 的后端实现,继承自 LiquidHandlerBackend。

View File

@@ -4019,7 +4019,8 @@ liquid_handler:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -4175,9 +4176,11 @@ liquid_handler:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
maximum: 2147483647 items:
minimum: -2147483648 maximum: 2147483647
type: integer minimum: -2147483648
type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
@@ -5040,7 +5043,8 @@ liquid_handler.biomek:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -5183,9 +5187,11 @@ liquid_handler.biomek:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
maximum: 2147483647 items:
minimum: -2147483648 maximum: 2147483647
type: integer minimum: -2147483648
type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648
@@ -9665,7 +9671,8 @@ liquid_handler.prcxi:
mix_liquid_height: 0.0 mix_liquid_height: 0.0
mix_rate: 0 mix_rate: 0
mix_stage: '' mix_stage: ''
mix_times: 0 mix_times:
- 0
mix_vol: 0 mix_vol: 0
none_keys: none_keys:
- '' - ''
@@ -9821,9 +9828,11 @@ liquid_handler.prcxi:
mix_stage: mix_stage:
type: string type: string
mix_times: mix_times:
maximum: 2147483647 items:
minimum: -2147483648 maximum: 2147483647
type: integer minimum: -2147483648
type: integer
type: array
mix_vol: mix_vol:
maximum: 2147483647 maximum: 2147483647
minimum: -2147483648 minimum: -2147483648

View File

@@ -8,8 +8,8 @@
"parent": "", "parent": "",
"pose": { "pose": {
"size": { "size": {
"width": 562, "width": 542,
"height": 394, "height": 374,
"depth": 0 "depth": 0
} }
}, },
@@ -37,7 +37,7 @@
"model": null, "model": null,
"position": { "position": {
"x": 0, "x": 0,
"y": 240, "y": 700,
"z": 0 "z": 0
} }
}, },
@@ -50,8 +50,8 @@
"type": "deck", "type": "deck",
"class": "", "class": "",
"position": { "position": {
"x": 10, "x": 0,
"y": 10, "y": 0,
"z": 0 "z": 0
}, },
"config": { "config": {
@@ -83,7 +83,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -96,7 +96,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T1", "label": "T1",
@@ -128,7 +128,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -141,7 +141,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T2", "label": "T2",
@@ -173,7 +173,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -186,7 +186,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T3", "label": "T3",
@@ -218,7 +218,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -231,7 +231,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T4", "label": "T4",
@@ -263,7 +263,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -276,7 +276,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T5", "label": "T5",
@@ -308,7 +308,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -321,7 +321,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T6", "label": "T6",
@@ -353,7 +353,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -366,7 +366,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T7", "label": "T7",
@@ -398,7 +398,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -411,7 +411,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T8", "label": "T8",
@@ -443,7 +443,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -456,7 +456,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T9", "label": "T9",
@@ -488,7 +488,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -501,7 +501,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T10", "label": "T10",
@@ -533,7 +533,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -546,7 +546,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T11", "label": "T11",
@@ -578,7 +578,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -591,7 +591,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T12", "label": "T12",
@@ -623,7 +623,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -636,7 +636,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T13", "label": "T13",
@@ -668,7 +668,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -681,7 +681,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T14", "label": "T14",
@@ -713,7 +713,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -726,7 +726,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T15", "label": "T15",
@@ -758,7 +758,7 @@
"z": 0 "z": 0
}, },
"config": { "config": {
"type": "PRCXI9300Container", "type": "PRCXI9300PlateAdapterSite",
"size_x": 127, "size_x": 127,
"size_y": 85.5, "size_y": 85.5,
"size_z": 10, "size_z": 10,
@@ -771,7 +771,7 @@
"category": "plate", "category": "plate",
"model": null, "model": null,
"barcode": null, "barcode": null,
"ordering": {},
"sites": [ "sites": [
{ {
"label": "T16", "label": "T16",