添加抓取后物料上传

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,
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.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):
@@ -86,19 +87,81 @@ class PRCXI9300Container(Plate):
category: str,
ordering: collections.OrderedDict,
model: Optional[str] = None,
material_info: Optional[Dict[str, Any]] = None,
ordering_layout: str = "col-major",
**kwargs,
):
super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model)
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:
"""从给定的状态加载工作台信息。"""
super().load_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]]:
data = super().serialize_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
class PRCXI9300Plate(Plate):
"""
@@ -210,9 +273,16 @@ class PRCXI9300TipRack(TipRack):
# 使用 ordering 参数,只包含位置信息(键)
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
else:
# ordering 的值已经是对象,可以直接使用
items = ordering
ordering_param = None
# ordering 的值已经是对象,需要过滤掉 None 值
# 只保留有效的对象,用于 ordered_items 参数
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:
items = None
ordering_param = None
@@ -348,9 +418,16 @@ class PRCXI9300TubeRack(TubeRack):
# 使用 ordering 参数,只包含位置信息(键)
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
else:
# ordering 的值已经是对象,可以直接使用
items_to_pass = ordering
ordering_param = None
# ordering 的值已经是对象,需要过滤掉 None 值
# 只保留有效的对象,用于 ordered_items 参数
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:
# 兼容旧的 items 参数
items_to_pass = items
@@ -804,7 +881,7 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
**backend_kwargs,
):
return await super().move_plate(
res = await super().move_plate(
plate,
to,
intermediate_locations,
@@ -816,6 +893,12 @@ class PRCXI9300Handler(LiquidHandlerAbstract):
target_plate_number = to,
**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):
"""PRCXI 9300 的后端实现,继承自 LiquidHandlerBackend。

View File

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

View File

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