mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-14 13:14:39 +00:00
PRCXI Update
修改prcxi连线 prcxi样例图 Create example_prcxi.json
This commit is contained in:
@@ -92,7 +92,7 @@ def refactor_data(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|||||||
# 定义操作映射,包含生物实验和有机化学的所有操作
|
# 定义操作映射,包含生物实验和有机化学的所有操作
|
||||||
OPERATION_MAPPING = {
|
OPERATION_MAPPING = {
|
||||||
# 生物实验操作
|
# 生物实验操作
|
||||||
"transfer_liquid": "SynBioFactory-liquid_handler.transfer_liquid",
|
"transfer_liquid": "SynBioFactory-liquid_handler.prcxi-transfer_liquid",
|
||||||
"transfer": "SynBioFactory-liquid_handler.biomek-transfer",
|
"transfer": "SynBioFactory-liquid_handler.biomek-transfer",
|
||||||
"incubation": "SynBioFactory-liquid_handler.biomek-incubation",
|
"incubation": "SynBioFactory-liquid_handler.biomek-incubation",
|
||||||
"move_labware": "SynBioFactory-liquid_handler.biomek-move_labware",
|
"move_labware": "SynBioFactory-liquid_handler.biomek-move_labware",
|
||||||
@@ -159,7 +159,10 @@ def build_protocol_graph(
|
|||||||
protocol_steps = refactor_data(protocol_steps)
|
protocol_steps = refactor_data(protocol_steps)
|
||||||
|
|
||||||
# 检查协议步骤中的模板来判断协议类型
|
# 检查协议步骤中的模板来判断协议类型
|
||||||
has_biomek_template = any("biomek" in step.get("template", "") for step in protocol_steps)
|
has_biomek_template = any(
|
||||||
|
("biomek" in step.get("template", "")) or ("prcxi" in step.get("template", ""))
|
||||||
|
for step in protocol_steps
|
||||||
|
)
|
||||||
|
|
||||||
if has_biomek_template:
|
if has_biomek_template:
|
||||||
# 生物实验协议图构建
|
# 生物实验协议图构建
|
||||||
@@ -178,12 +181,14 @@ def build_protocol_graph(
|
|||||||
resource_last_writer[labware_id] = f"{node_id}:labware"
|
resource_last_writer[labware_id] = f"{node_id}:labware"
|
||||||
|
|
||||||
# 处理协议步骤
|
# 处理协议步骤
|
||||||
|
prev_node = None
|
||||||
for i, step in enumerate(protocol_steps):
|
for i, step in enumerate(protocol_steps):
|
||||||
node_id = str(uuid.uuid4())
|
node_id = str(uuid.uuid4())
|
||||||
G.add_node(node_id, **step)
|
G.add_node(node_id, **step)
|
||||||
|
|
||||||
# 添加控制流边
|
# 添加控制流边
|
||||||
G.add_edge(prev_node, node_id, source_port="ready", target_port="ready")
|
if prev_node is not None:
|
||||||
|
G.add_edge(prev_node, node_id, source_port="ready", target_port="ready")
|
||||||
prev_node = node_id
|
prev_node = node_id
|
||||||
|
|
||||||
# 处理物料流
|
# 处理物料流
|
||||||
@@ -198,7 +203,8 @@ def build_protocol_graph(
|
|||||||
# 添加协议结束节点
|
# 添加协议结束节点
|
||||||
end_id = str(uuid.uuid4())
|
end_id = str(uuid.uuid4())
|
||||||
G.add_node(end_id, template=f"{LAB_NAME}-liquid_handler.biomek-run_protocol")
|
G.add_node(end_id, template=f"{LAB_NAME}-liquid_handler.biomek-run_protocol")
|
||||||
G.add_edge(prev_node, end_id, source_port="ready", target_port="ready")
|
if prev_node is not None:
|
||||||
|
G.add_edge(prev_node, end_id, source_port="ready", target_port="ready")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# 有机化学协议图构建
|
# 有机化学协议图构建
|
||||||
|
|||||||
BIN
test/workflow/example_bio_graph.png
Normal file
BIN
test/workflow/example_bio_graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
63
test/workflow/example_prcxi.json
Normal file
63
test/workflow/example_prcxi.json
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"steps_info": [
|
||||||
|
{
|
||||||
|
"step_number": 1,
|
||||||
|
"action": "transfer_liquid",
|
||||||
|
"parameters": {
|
||||||
|
"source": "sample supernatant",
|
||||||
|
"target": "antibody-coated well",
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_number": 2,
|
||||||
|
"action": "transfer_liquid",
|
||||||
|
"parameters": {
|
||||||
|
"source": "washing buffer",
|
||||||
|
"target": "antibody-coated well",
|
||||||
|
"volume": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_number": 3,
|
||||||
|
"action": "transfer_liquid",
|
||||||
|
"parameters": {
|
||||||
|
"source": "washing buffer",
|
||||||
|
"target": "antibody-coated well",
|
||||||
|
"volume": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_number": 4,
|
||||||
|
"action": "transfer_liquid",
|
||||||
|
"parameters": {
|
||||||
|
"source": "washing buffer",
|
||||||
|
"target": "antibody-coated well",
|
||||||
|
"volume": 200
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step_number": 5,
|
||||||
|
"action": "transfer_liquid",
|
||||||
|
"parameters": {
|
||||||
|
"source": "TMB substrate",
|
||||||
|
"target": "antibody-coated well",
|
||||||
|
"volume": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"labware_info": [
|
||||||
|
{"reagent_name": "sample supernatant", "material_name": "96深孔板", "positions": 1},
|
||||||
|
{"reagent_name": "washing buffer", "material_name": "储液槽", "positions": 2},
|
||||||
|
{"reagent_name": "TMB substrate", "material_name": "储液槽", "positions": 3},
|
||||||
|
{"reagent_name": "antibody-coated well", "material_name": "96 细胞培养皿", "positions": 4},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 5},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 6},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 7},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 8},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 9},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 10},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 11},
|
||||||
|
{"reagent_name": "", "material_name": "300μL Tip头", "positions": 13}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
test/workflow/example_prcxi_graph.png
Normal file
BIN
test/workflow/example_prcxi_graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
BIN
test/workflow/example_prcxi_graph_20251022_1359.png
Normal file
BIN
test/workflow/example_prcxi_graph_20251022_1359.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 117 KiB |
@@ -1,14 +1,94 @@
|
|||||||
import pytest
|
|
||||||
import json
|
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
|
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", [
|
@pytest.mark.parametrize("protocol_name", [
|
||||||
"example_bio",
|
"example_bio",
|
||||||
# "bioyond_materials_liquidhandling_1",
|
# "bioyond_materials_liquidhandling_1",
|
||||||
|
"example_prcxi",
|
||||||
])
|
])
|
||||||
def test_build_protocol_graph(protocol_name):
|
def test_build_protocol_graph(protocol_name):
|
||||||
d = json.load(open(f"{protocol_name}.json"))
|
data_path = Path(__file__).with_name(f"{protocol_name}.json")
|
||||||
graph = build_protocol_graph(labware_info=d["reagent"], protocol_steps=d["workflow"], workstation_name="PRCXi")
|
with data_path.open("r", encoding="utf-8") as fp:
|
||||||
draw_protocol_graph_with_ports(graph, "graph.png")
|
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)
|
print(graph)
|
||||||
Reference in New Issue
Block a user