Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/user_guide/best_practice.md
Original file line number Diff line number Diff line change
Expand Up @@ -452,8 +452,9 @@ unilab --ak your_ak --sk your_sk -g test/experiments/mock_devices/mock_all.json
**操作步骤:**

1. 将两个 `container` 拖拽到 `workstation` 中
2. 将 `virtual_transfer_pump` 拖拽到 `workstation` 中
3. 在画布上连接它们(建立父子关系)
2. 将 `virtual_multiway_valve` 拖拽到 `workstation` 中
3. 将 `virtual_transfer_pump` 拖拽到 `workstation` 中
4. 在画布上连接它们(建立父子关系)

![设备连接](image/links.png)

Expand Down
Binary file modified docs/user_guide/image/links.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion unilabos/app/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class JobAddReq(BaseModel):
action_type: str = Field(
examples=["unilabos_msgs.action._str_single_input.StrSingleInput"], description="action type", default=""
)
sample_material: dict = Field(examples=[{"string": "string"}], description="sample uuid to material uuid", default_factory=dict)
sample_material: dict = Field(examples=[{"string": "string"}], description="sample uuid to material uuid")
action_args: dict = Field(examples=[{"string": "string"}], description="action arguments", default_factory=dict)
task_id: str = Field(examples=["task_id"], description="task uuid (auto-generated if empty)", default="")
job_id: str = Field(examples=["job_id"], description="goal uuid (auto-generated if empty)", default="")
Expand Down
2 changes: 2 additions & 0 deletions unilabos/app/ws_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,8 @@ async def _handle_query_action_state(self, data: Dict[str, Any]):
async def _handle_job_start(self, data: Dict[str, Any]):
"""处理job_start消息"""
try:
if not data.get("sample_material"):
data["sample_material"] = {}
req = JobAddReq(**data)

job_log = format_job_log(req.job_id, req.task_id, req.device_id, req.action)
Expand Down
29 changes: 26 additions & 3 deletions unilabos/compile/pump_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,29 @@ def get_vessel_liquid_volume(G: nx.DiGraph, vessel: str) -> float:
return total_volume


def is_integrated_pump(node_name):
return "pump" in node_name and "valve" in node_name
def is_integrated_pump(node_class: str, node_name: str = "") -> bool:
"""
判断是否为泵阀一体设备
"""
class_lower = (node_class or "").lower()
name_lower = (node_name or "").lower()

if "pump" not in class_lower and "pump" not in name_lower:
return False

integrated_markers = [
"valve",
"pump_valve",
"pumpvalve",
"integrated",
"transfer_pump",
]

for marker in integrated_markers:
if marker in class_lower or marker in name_lower:
return True

return False


def find_connected_pump(G, valve_node):
Expand Down Expand Up @@ -186,7 +207,9 @@ def build_pump_valve_maps(G, pump_backbone):
debug_print(f"🔧 过滤后的骨架: {filtered_backbone}")

for node in filtered_backbone:
if is_integrated_pump(G.nodes[node]["class"]):
node_data = G.nodes.get(node, {})
node_class = node_data.get("class", "") or ""
if is_integrated_pump(node_class, node):
pumps_from_node[node] = node
valve_from_node[node] = node
debug_print(f" - 集成泵-阀: {node}")
Expand Down
6 changes: 2 additions & 4 deletions unilabos/devices/liquid_handling/liquid_handler_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -690,16 +690,14 @@ def set_liquid(cls, wells: list[Well], liquid_names: list[str], volumes: list[fl
)

def set_liquid_from_plate(
self, plate: List[ResourceSlot], well_names: list[str], liquid_names: list[str], volumes: list[float]
self, plate: ResourceSlot, well_names: list[str], liquid_names: list[str], volumes: list[float]
) -> SetLiquidFromPlateReturn:
"""Set the liquid in wells of a plate by well names (e.g., A1, A2, B3).

如果 liquid_names 和 volumes 为空,但 plate 和 well_names 不为空,直接返回 plate 和 wells。
"""
if isinstance(plate, list): # 未来移除
plate = plate[0]
assert issubclass(plate.__class__, Plate), "plate must be a Plate"
plate: Plate = cast(Plate, plate)
plate: Plate = cast(Plate, cast(Resource, plate))
# 根据 well_names 获取对应的 Well 对象
wells = [plate.get_well(name) for name in well_names]
res_volumes = []
Expand Down
2 changes: 1 addition & 1 deletion unilabos/devices/liquid_handling/prcxi/prcxi.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ def set_liquid(self, wells: list[Well], liquid_names: list[str], volumes: list[f
return super().set_liquid(wells, liquid_names, volumes)

def set_liquid_from_plate(
self, plate: List[ResourceSlot], well_names: list[str], liquid_names: list[str], volumes: list[float]
self, plate: ResourceSlot, well_names: list[str], liquid_names: list[str], volumes: list[float]
) -> SetLiquidFromPlateReturn:
return super().set_liquid_from_plate(plate, well_names, liquid_names, volumes)

Expand Down
Loading
Loading