修改工作流上传以及lh的物料初步判定

This commit is contained in:
q434343
2026-02-26 10:52:41 +08:00
committed by Xuwznln
parent 786498904d
commit 5d208c832b
3 changed files with 77 additions and 17 deletions

View File

@@ -1208,11 +1208,11 @@ class LiquidHandlerAbstract(LiquidHandlerMiddleware):
len_asp_vols = len(asp_vols) len_asp_vols = len(asp_vols)
len_dis_vols = len(dis_vols) len_dis_vols = len(dis_vols)
if num_targets != 1 and num_sources != 1: # if num_targets != 1 and num_sources != 1:
if len_asp_vols != num_sources and len_asp_vols != num_targets: # if len_asp_vols != num_sources and len_asp_vols != num_targets:
raise ValueError(f"asp_vols length must be equal to sources or targets length, but got {len_asp_vols} and {num_sources} and {num_targets}") # raise ValueError(f"asp_vols length must be equal to sources or targets length, but got {len_asp_vols} and {num_sources} and {num_targets}")
if len_dis_vols != num_sources and len_dis_vols != num_targets: # if len_dis_vols != num_sources and len_dis_vols != num_targets:
raise ValueError(f"dis_vols length must be equal to sources or targets length, but got {len_dis_vols} and {num_sources} and {num_targets}") # raise ValueError(f"dis_vols length must be equal to sources or targets length, but got {len_dis_vols} and {num_sources} and {num_targets}")
if len(use_channels) == 1: if len(use_channels) == 1:
max_len = max(num_sources, num_targets) max_len = max(num_sources, num_targets)

View File

@@ -581,6 +581,71 @@ class ResourceTreeSet(object):
d["model"] = res.config.get("model", None) d["model"] = res.config.get("model", None)
return d return d
# deserialize 会单独处理的元数据 key不传给构造函数
_META_KEYS = {"type", "parent_name", "location", "children", "rotation", "barcode"}
# deserialize 自定义逻辑使用的 key如 TipSpot 用 prototype_tip 构建 make_tip需保留
_DESERIALIZE_PRESERVED_KEYS = {"prototype_tip"}
def remove_incompatible_params(plr_d: dict) -> None:
"""递归移除 PLR 类不接受的参数,避免 deserialize 报错。
- 移除构造函数不接受的参数(如 compute_height_from_volume、ordering、category
- 对 TubeRack将 ordering 转为 ordered_items
- 保留 deserialize 自定义逻辑需要的 key如 prototype_tip
"""
if "type" in plr_d:
sub_cls = find_subclass(plr_d["type"], PLRResource)
if sub_cls is not None:
spec = inspect.signature(sub_cls)
valid_params = set(spec.parameters.keys())
# TubeRack 特殊处理:先转换 ordering再参与后续过滤
if "ordering" not in valid_params and "ordering" in plr_d:
ordering = plr_d.pop("ordering", None)
if sub_cls.__name__ == "TubeRack":
plr_d["ordered_items"] = (
_ordering_to_ordered_items(plr_d, ordering)
if ordering
else {}
)
# 移除构造函数不接受的参数(保留 META 和 deserialize 自定义逻辑需要的 key
for key in list(plr_d.keys()):
if (
key not in _META_KEYS
and key not in _DESERIALIZE_PRESERVED_KEYS
and key not in valid_params
):
plr_d.pop(key, None)
for child in plr_d.get("children", []):
remove_incompatible_params(child)
def _ordering_to_ordered_items(plr_d: dict, ordering: dict) -> dict:
"""将 ordering 转为 ordered_items从 children 构建 Tube 对象"""
from pylabrobot.resources import Tube, Coordinate
from pylabrobot.serializer import deserialize as plr_deserialize
children = plr_d.get("children", [])
ordered_items = {}
for idx, (ident, child_name) in enumerate(ordering.items()):
child_data = children[idx] if idx < len(children) else None
if child_data is None:
continue
loc_data = child_data.get("location")
loc = (
plr_deserialize(loc_data)
if loc_data
else Coordinate(0, 0, 0)
)
tube = Tube(
name=child_data.get("name", child_name or ident),
size_x=child_data.get("size_x", 10),
size_y=child_data.get("size_y", 10),
size_z=child_data.get("size_z", 50),
max_volume=child_data.get("max_volume", 1000),
)
tube.location = loc
ordered_items[ident] = tube
plr_d["children"] = [] # 已并入 ordered_items避免重复反序列化
return ordered_items
plr_resources = [] plr_resources = []
tracker = DeviceNodeResourceTracker() tracker = DeviceNodeResourceTracker()
@@ -600,9 +665,7 @@ class ResourceTreeSet(object):
raise ValueError( raise ValueError(
f"无法找到类型 {plr_dict['type']} 对应的 PLR 资源类。原始信息:{tree.root_node.res_content}" f"无法找到类型 {plr_dict['type']} 对应的 PLR 资源类。原始信息:{tree.root_node.res_content}"
) )
spec = inspect.signature(sub_cls) remove_incompatible_params(plr_dict)
if "category" not in spec.parameters:
plr_dict.pop("category", None)
plr_resource = sub_cls.deserialize(plr_dict, allow_marshal=True) plr_resource = sub_cls.deserialize(plr_dict, allow_marshal=True)
from pylabrobot.resources import Coordinate from pylabrobot.resources import Coordinate
from pylabrobot.serializer import deserialize from pylabrobot.serializer import deserialize

View File

@@ -413,17 +413,14 @@ def build_protocol_graph(
for slot, info in slots_info.items(): for slot, info in slots_info.items():
node_id = str(uuid.uuid4()) node_id = str(uuid.uuid4())
res_id = info["res_id"] res_id = info["res_id"]
res_type_name = info["labware"] res_type_name = info["labware"].lower().replace(".", "point")
if "tip" in res_type_name.lower(): res_type_name = f"lab_{res_type_name}"
res_type = "tip_rack"
else:
res_type = "plate"
G.add_node( G.add_node(
node_id, node_id,
template_name="create_resource", template_name="create_resource",
resource_name="host_node", resource_name="host_node",
name=f"{res_type} {slot}", name=f"{res_type_name}_slot{slot}",
description=f"Create plate on slot {slot}", description=f"Create plate on slot {slot}",
lab_node_type="Labware", lab_node_type="Labware",
footer="create_resource-host_node", footer="create_resource-host_node",
@@ -434,14 +431,14 @@ def build_protocol_graph(
param={ param={
"res_id": res_id, "res_id": res_id,
"device_id": CREATE_RESOURCE_DEFAULTS["device_id"], "device_id": CREATE_RESOURCE_DEFAULTS["device_id"],
"class_name": CLASS_NAMES_MAPPING[res_type], "class_name": res_type_name,
"parent": CREATE_RESOURCE_DEFAULTS["parent_template"].format(slot=slot), "parent": CREATE_RESOURCE_DEFAULTS["parent_template"].format(slot=slot),
"bind_locations": {"x": 0.0, "y": 0.0, "z": 0.0}, "bind_locations": {"x": 0.0, "y": 0.0, "z": 0.0},
"slot_on_deck": slot, "slot_on_deck": slot,
}, },
) )
slot_to_create_resource[slot] = node_id slot_to_create_resource[slot] = node_id
if res_type == "tip_rack": if "tip" in res_type_name and "rack" in res_type_name:
resource_last_writer[info["labware_id"]] = f"{node_id}:labware" resource_last_writer[info["labware_id"]] = f"{node_id}:labware"
# create_resource 之间不需要 ready 连接 # create_resource 之间不需要 ready 连接