mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-03-24 13:19:14 +00:00
修改工作流上传以及lh的物料初步判定
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -572,6 +572,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()
|
||||||
|
|
||||||
@@ -591,9 +656,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
|
||||||
|
|||||||
@@ -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 连接
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user