Compare commits

...

5 Commits

Author SHA1 Message Date
Xuwznln
45efbfcd12 fix deck & host_node 2026-02-11 17:33:26 +08:00
Xuwznln
8da6fdfd0b set liquid with tube 2026-02-11 16:20:07 +08:00
Xuwznln
29ea9909a5 Merge branch 'dev' into feat/lab_resource 2026-02-11 14:04:49 +08:00
Xuwznln
ee6307a568 registry update & workflow update 2026-02-10 22:45:51 +08:00
Xuwznln
8a0116c852 add resource 2026-02-10 22:44:45 +08:00
6 changed files with 10589 additions and 18 deletions

View File

@@ -21,7 +21,7 @@ from pylabrobot.resources import (
ResourceHolder, ResourceHolder,
Lid, Lid,
Trash, Trash,
Tip, Tip, TubeRack,
) )
from typing_extensions import TypedDict from typing_extensions import TypedDict
@@ -696,10 +696,13 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
如果 liquid_names 和 volumes 为空,但 plate 和 well_names 不为空,直接返回 plate 和 wells。 如果 liquid_names 和 volumes 为空,但 plate 和 well_names 不为空,直接返回 plate 和 wells。
""" """
assert issubclass(plate.__class__, Plate), "plate must be a Plate" assert issubclass(plate.__class__, Plate) or issubclass(plate.__class__, TubeRack) , f"plate must be a Plate, now: {type(plate)}"
plate: Plate = cast(Plate, cast(Resource, plate)) plate: Union[Plate, TubeRack]
# 根据 well_names 获取对应的 Well 对象 # 根据 well_names 获取对应的 Well 对象
wells = [plate.get_well(name) for name in well_names] if issubclass(plate.__class__, Plate):
wells = [plate.get_well(name) for name in well_names]
elif issubclass(plate.__class__, TubeRack):
wells = [plate.get_tube(name) for name in well_names]
res_volumes = [] res_volumes = []
# 如果 liquid_names 和 volumes 都为空,直接返回 # 如果 liquid_names 和 volumes 都为空,直接返回

View File

@@ -91,7 +91,7 @@ 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__(size_x, size_y, size_z, name)
self.slots = [None] * 16 # PRCXI 9300/9320 最大有 16 个槽位 self.slots = [None] * 16 # PRCXI 9300/9320 最大有 16 个槽位
self.slot_locations = [Coordinate(0, 0, 0)] * 16 self.slot_locations = [Coordinate(0, 0, 0)] * 16
@@ -248,14 +248,15 @@ class PRCXI9300TipRack(TipRack):
if ordered_items is not None: if ordered_items is not None:
items = ordered_items items = ordered_items
elif ordering is not None: elif ordering is not None:
# 检查 ordering 中的值是否是字符串(从 JSON 反序列化时的情况) # 检查 ordering 中的值类型来决定如何处理:
# 如果是字符串,说明这是位置名称,需要让 TipRack 自己创建 Tip 对象 # - 字符串值(从 JSON 反序列化): 只用键创建 ordering_param
# 我们只传递位置信息(键),不传递值,使用 ordering 参数 # - None 值(从第二次往返序列化): 同样只用键创建 ordering_param
if ordering and isinstance(next(iter(ordering.values()), None), str): # - 对象值(已经是实际的 Resource 对象): 直接作为 ordered_items 使用
# ordering 的值是字符串,只使用键(位置信息)创建新的 OrderedDict first_val = next(iter(ordering.values()), None) if ordering else None
if not ordering or first_val is None or isinstance(first_val, str):
# ordering 的值是字符串或 None只使用键位置信息创建新的 OrderedDict
# 传递 ordering 参数而不是 ordered_items让 TipRack 自己创建 Tip 对象 # 传递 ordering 参数而不是 ordered_items让 TipRack 自己创建 Tip 对象
items = None items = None
# 使用 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 的值已经是对象,可以直接使用
@@ -397,14 +398,15 @@ class PRCXI9300TubeRack(TubeRack):
items_to_pass = ordered_items items_to_pass = ordered_items
ordering_param = None ordering_param = None
elif ordering is not None: elif ordering is not None:
# 检查 ordering 中的值是否是字符串(从 JSON 反序列化时的情况) # 检查 ordering 中的值类型来决定如何处理:
# 如果是字符串,说明这是位置名称,需要让 TubeRack 自己创建 Tube 对象 # - 字符串值(从 JSON 反序列化): 只用键创建 ordering_param
# 我们只传递位置信息(键),不传递值,使用 ordering 参数 # - None 值(从第二次往返序列化): 同样只用键创建 ordering_param
if ordering and isinstance(next(iter(ordering.values()), None), str): # - 对象值(已经是实际的 Resource 对象): 直接作为 ordered_items 使用
# ordering 的值是字符串,只使用键(位置信息)创建新的 OrderedDict first_val = next(iter(ordering.values()), None) if ordering else None
if not ordering or first_val is None or isinstance(first_val, str):
# ordering 的值是字符串或 None只使用键位置信息创建新的 OrderedDict
# 传递 ordering 参数而不是 ordered_items让 TubeRack 自己创建 Tube 对象 # 传递 ordering 参数而不是 ordered_items让 TubeRack 自己创建 Tube 对象
items_to_pass = None items_to_pass = None
# 使用 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 的值已经是对象,可以直接使用

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -460,7 +460,7 @@ class BaseROS2DeviceNode(Node, Generic[T]):
} }
res.response = json.dumps(final_response) res.response = json.dumps(final_response)
# 如果driver自己就有assign的方法那就使用driver自己的assign方法 # 如果driver自己就有assign的方法那就使用driver自己的assign方法
if hasattr(self.driver_instance, "create_resource"): if hasattr(self.driver_instance, "create_resource") and self.node_name != "host_node":
create_resource_func = getattr(self.driver_instance, "create_resource") create_resource_func = getattr(self.driver_instance, "create_resource")
try: try:
ret = create_resource_func( ret = create_resource_func(