mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 13:25:13 +00:00
Merge pull request #206 from ALITTLELZ/prcxi9320
Fix startup json for 9320
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import collections
|
import collections
|
||||||
|
from collections import OrderedDict
|
||||||
import contextlib
|
import contextlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
import time
|
import time
|
||||||
|
import uuid
|
||||||
from typing import Any, List, Dict, Optional, Tuple, TypedDict, Union, Sequence, Iterator, Literal
|
from typing import Any, List, Dict, Optional, Tuple, TypedDict, Union, Sequence, Iterator, Literal
|
||||||
from pylabrobot.liquid_handling.standard import GripDirection
|
from pylabrobot.liquid_handling.standard import GripDirection
|
||||||
|
|
||||||
@@ -28,7 +30,7 @@ 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
|
from pylabrobot.resources import ResourceHolder, ResourceStack, Tip, Deck, Plate, Well, TipRack, Resource, Container, Coordinate, TipSpot, Trash, PlateAdapter, TubeRack
|
||||||
|
|
||||||
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.ros.nodes.base_device_node import BaseROS2DeviceNode
|
||||||
@@ -69,7 +71,35 @@ class PRCXI9300Deck(Deck):
|
|||||||
def __init__(self, name: str, size_x: float, size_y: float, size_z: float, **kwargs):
|
def __init__(self, name: str, size_x: float, size_y: float, size_z: float, **kwargs):
|
||||||
super().__init__(name, size_x, size_y, size_z)
|
super().__init__(name, size_x, size_y, size_z)
|
||||||
self.slots = [None] * 6 # PRCXI 9300 有 6 个槽位
|
self.slots = [None] * 6 # PRCXI 9300 有 6 个槽位
|
||||||
|
class PRCXI9300Container(Plate):
|
||||||
|
"""PRCXI 9300 的专用 Container 类,继承自 Plate,用于槽位定位和未知模块。
|
||||||
|
|
||||||
|
该类定义了 PRCXI 9300 的工作台布局和槽位信息。
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
size_x: float,
|
||||||
|
size_y: float,
|
||||||
|
size_z: float,
|
||||||
|
category: str,
|
||||||
|
ordering: collections.OrderedDict,
|
||||||
|
model: Optional[str] = None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
super().__init__(name, size_x, size_y, size_z, category=category, ordering=ordering, model=model)
|
||||||
|
self._unilabos_state = {}
|
||||||
|
|
||||||
|
def load_state(self, state: Dict[str, Any]) -> None:
|
||||||
|
"""从给定的状态加载工作台信息。"""
|
||||||
|
super().load_state(state)
|
||||||
|
self._unilabos_state = state
|
||||||
|
|
||||||
|
def serialize_state(self) -> Dict[str, Dict[str, Any]]:
|
||||||
|
data = super().serialize_state()
|
||||||
|
data.update(self._unilabos_state)
|
||||||
|
return data
|
||||||
class PRCXI9300Plate(Plate):
|
class PRCXI9300Plate(Plate):
|
||||||
"""
|
"""
|
||||||
专用孔板类:
|
专用孔板类:
|
||||||
@@ -83,11 +113,43 @@ class PRCXI9300Plate(Plate):
|
|||||||
model: Optional[str] = None,
|
model: Optional[str] = None,
|
||||||
material_info: Optional[Dict[str, Any]] = None,
|
material_info: Optional[Dict[str, Any]] = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
items = ordered_items if ordered_items is not None else ordering
|
# 如果 ordered_items 不为 None,直接使用
|
||||||
super().__init__(name, size_x, size_y, size_z,
|
if ordered_items is not None:
|
||||||
ordered_items=items,
|
items = ordered_items
|
||||||
category=category,
|
elif ordering is not None:
|
||||||
model=model, **kwargs)
|
# 检查 ordering 中的值是否是字符串(从 JSON 反序列化时的情况)
|
||||||
|
# 如果是字符串,说明这是位置名称,需要让 Plate 自己创建 Well 对象
|
||||||
|
# 我们只传递位置信息(键),不传递值,使用 ordering 参数
|
||||||
|
if ordering and isinstance(next(iter(ordering.values()), None), str):
|
||||||
|
# ordering 的值是字符串,只使用键(位置信息)创建新的 OrderedDict
|
||||||
|
# 传递 ordering 参数而不是 ordered_items,让 Plate 自己创建 Well 对象
|
||||||
|
items = None
|
||||||
|
# 使用 ordering 参数,只包含位置信息(键)
|
||||||
|
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
|
||||||
|
else:
|
||||||
|
# ordering 的值已经是对象,可以直接使用
|
||||||
|
items = ordering
|
||||||
|
ordering_param = None
|
||||||
|
else:
|
||||||
|
items = None
|
||||||
|
ordering_param = None
|
||||||
|
|
||||||
|
# 根据情况传递不同的参数
|
||||||
|
if items is not None:
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
ordered_items=items,
|
||||||
|
category=category,
|
||||||
|
model=model, **kwargs)
|
||||||
|
elif ordering_param is not None:
|
||||||
|
# 传递 ordering 参数,让 Plate 自己创建 Well 对象
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
ordering=ordering_param,
|
||||||
|
category=category,
|
||||||
|
model=model, **kwargs)
|
||||||
|
else:
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
category=category,
|
||||||
|
model=model, **kwargs)
|
||||||
|
|
||||||
self._unilabos_state = {}
|
self._unilabos_state = {}
|
||||||
if material_info:
|
if material_info:
|
||||||
@@ -124,8 +186,7 @@ class PRCXI9300Plate(Plate):
|
|||||||
safe_state[k] = v
|
safe_state[k] = v
|
||||||
|
|
||||||
data.update(safe_state)
|
data.update(safe_state)
|
||||||
return data
|
return data # 其他顶层属性也进行类型检查
|
||||||
|
|
||||||
class PRCXI9300TipRack(TipRack):
|
class PRCXI9300TipRack(TipRack):
|
||||||
""" 专用吸头盒类 """
|
""" 专用吸头盒类 """
|
||||||
def __init__(self, name: str, size_x: float, size_y: float, size_z: float,
|
def __init__(self, name: str, size_x: float, size_y: float, size_z: float,
|
||||||
@@ -135,11 +196,43 @@ class PRCXI9300TipRack(TipRack):
|
|||||||
model: Optional[str] = None,
|
model: Optional[str] = None,
|
||||||
material_info: Optional[Dict[str, Any]] = None,
|
material_info: Optional[Dict[str, Any]] = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
items = ordered_items if ordered_items is not None else ordering
|
# 如果 ordered_items 不为 None,直接使用
|
||||||
super().__init__(name, size_x, size_y, size_z,
|
if ordered_items is not None:
|
||||||
ordered_items=items,
|
items = ordered_items
|
||||||
category=category,
|
elif ordering is not None:
|
||||||
model=model, **kwargs)
|
# 检查 ordering 中的值是否是字符串(从 JSON 反序列化时的情况)
|
||||||
|
# 如果是字符串,说明这是位置名称,需要让 TipRack 自己创建 Tip 对象
|
||||||
|
# 我们只传递位置信息(键),不传递值,使用 ordering 参数
|
||||||
|
if ordering and isinstance(next(iter(ordering.values()), None), str):
|
||||||
|
# ordering 的值是字符串,只使用键(位置信息)创建新的 OrderedDict
|
||||||
|
# 传递 ordering 参数而不是 ordered_items,让 TipRack 自己创建 Tip 对象
|
||||||
|
items = None
|
||||||
|
# 使用 ordering 参数,只包含位置信息(键)
|
||||||
|
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
|
||||||
|
else:
|
||||||
|
# ordering 的值已经是对象,可以直接使用
|
||||||
|
items = ordering
|
||||||
|
ordering_param = None
|
||||||
|
else:
|
||||||
|
items = None
|
||||||
|
ordering_param = None
|
||||||
|
|
||||||
|
# 根据情况传递不同的参数
|
||||||
|
if items is not None:
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
ordered_items=items,
|
||||||
|
category=category,
|
||||||
|
model=model, **kwargs)
|
||||||
|
elif ordering_param is not None:
|
||||||
|
# 传递 ordering 参数,让 TipRack 自己创建 Tip 对象
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
ordering=ordering_param,
|
||||||
|
category=category,
|
||||||
|
model=model, **kwargs)
|
||||||
|
else:
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
category=category,
|
||||||
|
model=model, **kwargs)
|
||||||
self._unilabos_state = {}
|
self._unilabos_state = {}
|
||||||
if material_info:
|
if material_info:
|
||||||
self._unilabos_state["Material"] = material_info
|
self._unilabos_state["Material"] = material_info
|
||||||
@@ -235,16 +328,53 @@ class PRCXI9300TubeRack(TubeRack):
|
|||||||
category: str = "tube_rack",
|
category: str = "tube_rack",
|
||||||
items: Optional[Dict[str, Any]] = None,
|
items: Optional[Dict[str, Any]] = None,
|
||||||
ordered_items: Optional[OrderedDict] = None,
|
ordered_items: Optional[OrderedDict] = None,
|
||||||
|
ordering: Optional[OrderedDict] = None,
|
||||||
model: Optional[str] = None,
|
model: Optional[str] = None,
|
||||||
material_info: Optional[Dict[str, Any]] = None,
|
material_info: Optional[Dict[str, Any]] = None,
|
||||||
**kwargs):
|
**kwargs):
|
||||||
|
|
||||||
# 兼容处理:PLR 的 TubeRack 构造函数可能接受 items 或 ordered_items
|
# 如果 ordered_items 不为 None,直接使用
|
||||||
items_to_pass = items if items is not None else ordered_items
|
if ordered_items is not None:
|
||||||
super().__init__(name, size_x, size_y, size_z,
|
items_to_pass = ordered_items
|
||||||
ordered_items=ordered_items,
|
ordering_param = None
|
||||||
model=model,
|
elif ordering is not None:
|
||||||
**kwargs)
|
# 检查 ordering 中的值是否是字符串(从 JSON 反序列化时的情况)
|
||||||
|
# 如果是字符串,说明这是位置名称,需要让 TubeRack 自己创建 Tube 对象
|
||||||
|
# 我们只传递位置信息(键),不传递值,使用 ordering 参数
|
||||||
|
if ordering and isinstance(next(iter(ordering.values()), None), str):
|
||||||
|
# ordering 的值是字符串,只使用键(位置信息)创建新的 OrderedDict
|
||||||
|
# 传递 ordering 参数而不是 ordered_items,让 TubeRack 自己创建 Tube 对象
|
||||||
|
items_to_pass = None
|
||||||
|
# 使用 ordering 参数,只包含位置信息(键)
|
||||||
|
ordering_param = collections.OrderedDict((k, None) for k in ordering.keys())
|
||||||
|
else:
|
||||||
|
# ordering 的值已经是对象,可以直接使用
|
||||||
|
items_to_pass = ordering
|
||||||
|
ordering_param = None
|
||||||
|
elif items is not None:
|
||||||
|
# 兼容旧的 items 参数
|
||||||
|
items_to_pass = items
|
||||||
|
ordering_param = None
|
||||||
|
else:
|
||||||
|
items_to_pass = None
|
||||||
|
ordering_param = None
|
||||||
|
|
||||||
|
# 根据情况传递不同的参数
|
||||||
|
if items_to_pass is not None:
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
ordered_items=items_to_pass,
|
||||||
|
model=model,
|
||||||
|
**kwargs)
|
||||||
|
elif ordering_param is not None:
|
||||||
|
# 传递 ordering 参数,让 TubeRack 自己创建 Tube 对象
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
ordering=ordering_param,
|
||||||
|
model=model,
|
||||||
|
**kwargs)
|
||||||
|
else:
|
||||||
|
super().__init__(name, size_x, size_y, size_z,
|
||||||
|
model=model,
|
||||||
|
**kwargs)
|
||||||
|
|
||||||
self._unilabos_state = {}
|
self._unilabos_state = {}
|
||||||
if material_info:
|
if material_info:
|
||||||
|
|||||||
@@ -812,7 +812,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300TipRack",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -6515,7 +6515,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -10587,7 +10587,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300TipRack",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -16291,7 +16291,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -20365,7 +20365,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -24438,7 +24438,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -28512,7 +28512,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -32585,7 +32585,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
@@ -36658,7 +36658,7 @@
|
|||||||
"z": 0
|
"z": 0
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"type": "PRCXI9300Container",
|
"type": "PRCXI9300Plate",
|
||||||
"size_x": 127,
|
"size_x": 127,
|
||||||
"size_y": 85.5,
|
"size_y": 85.5,
|
||||||
"size_z": 10,
|
"size_z": 10,
|
||||||
|
|||||||
Reference in New Issue
Block a user