mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2026-02-04 05:15:10 +00:00
Close #208. Fix mock devices.
This commit is contained in:
@@ -1243,7 +1243,7 @@ class WebSocketClient(BaseCommunicationClient):
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
self.message_processor.send_message(message)
|
self.message_processor.send_message(message)
|
||||||
logger.debug(f"[WebSocketClient] Device status published: {device_id}.{property_name}")
|
logger.trace(f"[WebSocketClient] Device status published: {device_id}.{property_name}")
|
||||||
|
|
||||||
def publish_job_status(
|
def publish_job_status(
|
||||||
self, feedback_data: dict, item: QueueItem, status: str, return_info: Optional[dict] = None
|
self, feedback_data: dict, item: QueueItem, status: str, return_info: Optional[dict] = None
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
],
|
],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_chiller",
|
"class": "virtual_heatchill",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_filter",
|
"class": "virtual_filter",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_heater",
|
"class": "virtual_heatchill",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_pump",
|
"class": "virtual_transfer_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_rotavap",
|
"class": "virtual_rotavap",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_separator",
|
"class": "virtual_separator",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -213,7 +213,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_solenoid_valve",
|
"class": "virtual_solenoid_valve",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_stirrer_new",
|
"class": "virtual_stirrer",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_stirrer",
|
"class": "virtual_stirrer",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
@@ -289,7 +289,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_vacuum",
|
"class": "virtual_vacuum_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_chiller",
|
"class": "virtual_heatchill",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_filter",
|
"class": "virtual_filter",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_heater",
|
"class": "virtual_heatchill",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_pump",
|
"class": "virtual_transfer_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_rotavap",
|
"class": "virtual_rotavap",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_separator",
|
"class": "virtual_separator",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_solenoid_valve",
|
"class": "virtual_solenoid_valve",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_stirrer",
|
"class": "virtual_stirrer",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_stirrer_new",
|
"class": "virtual_stirrer",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": null,
|
"parent": null,
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "mock_vacuum",
|
"class": "virtual_vacuum_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 620.6111111111111,
|
"x": 620.6111111111111,
|
||||||
"y": 171,
|
"y": 171,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": "CentrifugeTestStation",
|
"parent": "CentrifugeTestStation",
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "virtual_pump",
|
"class": "virtual_transfer_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 520.6111111111111,
|
"x": 520.6111111111111,
|
||||||
"y": 300,
|
"y": 300,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": "FilterTestStation",
|
"parent": "FilterTestStation",
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "virtual_pump",
|
"class": "virtual_transfer_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 520.6111111111111,
|
"x": 520.6111111111111,
|
||||||
"y": 300,
|
"y": 300,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": "HeatChillTestStation",
|
"parent": "HeatChillTestStation",
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "virtual_pump",
|
"class": "virtual_transfer_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 520.6111111111111,
|
"x": 520.6111111111111,
|
||||||
"y": 300,
|
"y": 300,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
"children": [],
|
"children": [],
|
||||||
"parent": "StirTestStation",
|
"parent": "StirTestStation",
|
||||||
"type": "device",
|
"type": "device",
|
||||||
"class": "virtual_pump",
|
"class": "virtual_transfer_pump",
|
||||||
"position": {
|
"position": {
|
||||||
"x": 520.6111111111111,
|
"x": 520.6111111111111,
|
||||||
"y": 300,
|
"y": 300,
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import json
|
|
||||||
import sys
|
|
||||||
from datetime import datetime
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
ROOT_DIR = Path(__file__).resolve().parents[2]
|
|
||||||
if str(ROOT_DIR) not in sys.path:
|
|
||||||
sys.path.insert(0, str(ROOT_DIR))
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
from scripts.workflow import build_protocol_graph, draw_protocol_graph, draw_protocol_graph_with_ports
|
|
||||||
|
|
||||||
|
|
||||||
ROOT_DIR = Path(__file__).resolve().parents[2]
|
|
||||||
if str(ROOT_DIR) not in sys.path:
|
|
||||||
sys.path.insert(0, str(ROOT_DIR))
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_steps(data):
|
|
||||||
normalized = []
|
|
||||||
for step in data:
|
|
||||||
action = step.get("action") or step.get("operation")
|
|
||||||
if not action:
|
|
||||||
continue
|
|
||||||
raw_params = step.get("parameters") or step.get("action_args") or {}
|
|
||||||
params = dict(raw_params)
|
|
||||||
|
|
||||||
if "source" in raw_params and "sources" not in raw_params:
|
|
||||||
params["sources"] = raw_params["source"]
|
|
||||||
if "target" in raw_params and "targets" not in raw_params:
|
|
||||||
params["targets"] = raw_params["target"]
|
|
||||||
|
|
||||||
description = step.get("description") or step.get("purpose")
|
|
||||||
step_dict = {"action": action, "parameters": params}
|
|
||||||
if description:
|
|
||||||
step_dict["description"] = description
|
|
||||||
normalized.append(step_dict)
|
|
||||||
return normalized
|
|
||||||
|
|
||||||
|
|
||||||
def _normalize_labware(data):
|
|
||||||
labware = {}
|
|
||||||
for item in data:
|
|
||||||
reagent_name = item.get("reagent_name")
|
|
||||||
key = reagent_name or item.get("material_name") or item.get("name")
|
|
||||||
if not key:
|
|
||||||
continue
|
|
||||||
key = str(key)
|
|
||||||
idx = 1
|
|
||||||
original_key = key
|
|
||||||
while key in labware:
|
|
||||||
idx += 1
|
|
||||||
key = f"{original_key}_{idx}"
|
|
||||||
|
|
||||||
labware[key] = {
|
|
||||||
"slot": item.get("positions") or item.get("slot"),
|
|
||||||
"labware": item.get("material_name") or item.get("labware"),
|
|
||||||
"well": item.get("well", []),
|
|
||||||
"type": item.get("type", "reagent"),
|
|
||||||
"role": item.get("role", ""),
|
|
||||||
"name": key,
|
|
||||||
}
|
|
||||||
return labware
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("protocol_name", [
|
|
||||||
"example_bio",
|
|
||||||
# "bioyond_materials_liquidhandling_1",
|
|
||||||
"example_prcxi",
|
|
||||||
])
|
|
||||||
def test_build_protocol_graph(protocol_name):
|
|
||||||
data_path = Path(__file__).with_name(f"{protocol_name}.json")
|
|
||||||
with data_path.open("r", encoding="utf-8") as fp:
|
|
||||||
d = json.load(fp)
|
|
||||||
|
|
||||||
if "workflow" in d and "reagent" in d:
|
|
||||||
protocol_steps = d["workflow"]
|
|
||||||
labware_info = d["reagent"]
|
|
||||||
elif "steps_info" in d and "labware_info" in d:
|
|
||||||
protocol_steps = _normalize_steps(d["steps_info"])
|
|
||||||
labware_info = _normalize_labware(d["labware_info"])
|
|
||||||
else:
|
|
||||||
raise ValueError("Unsupported protocol format")
|
|
||||||
|
|
||||||
graph = build_protocol_graph(
|
|
||||||
labware_info=labware_info,
|
|
||||||
protocol_steps=protocol_steps,
|
|
||||||
workstation_name="PRCXi",
|
|
||||||
)
|
|
||||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M")
|
|
||||||
output_path = data_path.with_name(f"{protocol_name}_graph_{timestamp}.png")
|
|
||||||
draw_protocol_graph_with_ports(graph, str(output_path))
|
|
||||||
print(graph)
|
|
||||||
Reference in New Issue
Block a user