mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-03-28 08:23:08 +00:00
add:skill&agent
This commit is contained in:
381
.cursor/skills/edit-experiment-graph/SKILL.md
Normal file
381
.cursor/skills/edit-experiment-graph/SKILL.md
Normal file
@@ -0,0 +1,381 @@
|
||||
---
|
||||
name: edit-experiment-graph
|
||||
description: Guide for creating and editing experiment graph files in Uni-Lab-OS (创建/编辑实验组态图). Covers node types, link types, parent-child relationships, deck configuration, and common graph patterns. Use when the user wants to create a graph file, edit an experiment configuration, set up device topology, or mentions 图文件/graph/组态/拓扑/实验图/experiment JSON.
|
||||
---
|
||||
|
||||
# 创建/编辑实验组态图
|
||||
|
||||
实验图(Graph File)定义设备拓扑、物理连接和物料配置。系统启动时加载图文件,初始化所有设备和连接关系。
|
||||
|
||||
路径:`unilabos/test/experiments/<name>.json`
|
||||
|
||||
---
|
||||
|
||||
## 第一步:确认需求
|
||||
|
||||
向用户确认:
|
||||
|
||||
| 信息 | 说明 |
|
||||
|------|------|
|
||||
| 场景类型 | 单设备调试 / 多设备联调 / 工作站完整图 |
|
||||
| 包含的设备 | 设备 ID、注册表 class 名、配置参数 |
|
||||
| 连接关系 | 物理连接(管道)/ 通信连接(串口)/ 无连接 |
|
||||
| 父子关系 | 是否有工作站包含子设备 |
|
||||
| 物料需求 | 是否需要 Deck、容器、试剂瓶 |
|
||||
|
||||
---
|
||||
|
||||
## 第二步:JSON 顶层结构
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [],
|
||||
"links": []
|
||||
}
|
||||
```
|
||||
|
||||
> `links` 也可写作 `edges`,加载时两者等效。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:定义 Nodes
|
||||
|
||||
### 节点字段
|
||||
|
||||
| 字段 | 类型 | 必需 | 默认值 | 说明 |
|
||||
|------|------|------|--------|------|
|
||||
| `id` | string | **是** | — | 节点唯一标识,links 和 children 中引用此值 |
|
||||
| `class` | string | **是** | — | 对应注册表名(设备/资源 YAML 的 key),容器可为 `null` |
|
||||
| `name` | string | 否 | 同 `id` | 显示名称,缺省时自动用 `id` |
|
||||
| `type` | string | 否 | `"device"` | 节点类型(见下表),缺省时自动设为 `"device"` |
|
||||
| `children` | string[] | 否 | `[]` | 子节点 ID 列表 |
|
||||
| `parent` | string\|null | 否 | `null` | 父节点 ID,顶层设备为 `null` |
|
||||
| `position` | object | 否 | `{x:0,y:0,z:0}` | 空间坐标 |
|
||||
| `config` | object | 否 | `{}` | 传给驱动 `__init__` 的参数 |
|
||||
| `data` | object | 否 | `{}` | 初始运行状态 |
|
||||
| `size_x/y/z` | float | 否 | — | 节点物理尺寸(工作站节点常用) |
|
||||
|
||||
> 非标准字段(如 `api_host`)会自动移入 `config`。
|
||||
|
||||
### 节点类型
|
||||
|
||||
| `type` | 用途 | `class` 要求 |
|
||||
|--------|------|-------------|
|
||||
| `device` | 设备(默认) | 注册表中的设备名 |
|
||||
| `deck` | 工作台面 | Deck 工厂函数/类名 |
|
||||
| `container` | 容器(烧瓶、反应釜) | `null` 或具体容器类名 |
|
||||
|
||||
### 设备节点模板
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "my_device",
|
||||
"name": "我的设备",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "registry_device_name",
|
||||
"position": {"x": 0, "y": 0, "z": 0},
|
||||
"config": {
|
||||
"port": "/dev/ttyUSB0",
|
||||
"baudrate": 115200
|
||||
},
|
||||
"data": {
|
||||
"status": "Idle"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 容器节点模板
|
||||
|
||||
容器用于协议系统中表示试剂瓶、反应釜等,`class` 通常为 `null`:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "flask_DMF",
|
||||
"name": "DMF试剂瓶",
|
||||
"children": [],
|
||||
"parent": "my_station",
|
||||
"type": "container",
|
||||
"class": null,
|
||||
"position": {"x": 200, "y": 500, "z": 0},
|
||||
"config": {"max_volume": 1000.0},
|
||||
"data": {
|
||||
"liquid": [{"liquid_type": "DMF", "liquid_volume": 800.0}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Deck 节点模板
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "my_deck",
|
||||
"name": "my_deck",
|
||||
"children": [],
|
||||
"parent": "my_station",
|
||||
"type": "deck",
|
||||
"class": "MyStation_Deck",
|
||||
"position": {"x": 0, "y": 0, "z": 0},
|
||||
"config": {
|
||||
"type": "MyStation_Deck",
|
||||
"setup": true,
|
||||
"rotation": {"x": 0, "y": 0, "z": 0, "type": "Rotation"}
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第四步:定义 Links
|
||||
|
||||
### Link 字段
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `source` | string | 源节点 ID |
|
||||
| `target` | string | 目标节点 ID |
|
||||
| `type` | string | `"physical"` / `"fluid"` / `"communication"` |
|
||||
| `port` | object | 端口映射 `{source_id: "port_name", target_id: "port_name"}` |
|
||||
|
||||
### 物理/流体连接
|
||||
|
||||
设备间的管道连接,协议系统用此查找路径:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "multiway_valve_1",
|
||||
"target": "flask_DMF",
|
||||
"type": "fluid",
|
||||
"port": {
|
||||
"multiway_valve_1": "2",
|
||||
"flask_DMF": "outlet"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 通信连接
|
||||
|
||||
设备间的串口/IO 通信代理,加载时自动将端口信息写入目标设备 config:
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "pump_1",
|
||||
"target": "serial_device",
|
||||
"type": "communication",
|
||||
"port": {
|
||||
"pump_1": "port",
|
||||
"serial_device": "port"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第五步:父子关系与工作站配置
|
||||
|
||||
### 工作站 + 子设备
|
||||
|
||||
工作站节点的 `children` 列出所有子节点 ID,子节点的 `parent` 指向工作站:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "my_station",
|
||||
"children": ["my_deck", "pump_1", "valve_1", "reactor_1"],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "workstation",
|
||||
"config": {
|
||||
"protocol_type": ["PumpTransferProtocol", "CleanProtocol"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 工作站 + Deck 引用
|
||||
|
||||
工作站节点中通过 `deck` 字段引用 Deck:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "my_station",
|
||||
"children": ["my_deck", "sub_device_1"],
|
||||
"deck": {
|
||||
"data": {
|
||||
"_resource_child_name": "my_deck",
|
||||
"_resource_type": "unilabos.resources.my_module.decks:MyDeck"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**关键约束:**
|
||||
- `_resource_child_name` 必须与 Deck 节点的 `id` 一致
|
||||
- `_resource_type` 为 Deck 类/工厂函数的完整 Python 路径
|
||||
|
||||
---
|
||||
|
||||
## 常见图模式
|
||||
|
||||
### 模式 A:单设备调试
|
||||
|
||||
最简形式,一个设备节点,无连接:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "my_device",
|
||||
"name": "my_device",
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"type": "device",
|
||||
"class": "motor.zdt_x42",
|
||||
"position": {"x": 0, "y": 0, "z": 0},
|
||||
"config": {"port": "/dev/ttyUSB0", "baudrate": 115200},
|
||||
"data": {"status": "idle"}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
```
|
||||
|
||||
### 模式 B:Protocol 工作站(泵+阀+容器)
|
||||
|
||||
工作站配合泵、阀、容器和物理连接,用于协议编译:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "station", "name": "协议工作站",
|
||||
"class": "workstation", "type": "device", "parent": null,
|
||||
"children": ["pump", "valve", "flask_solvent", "reactor", "waste"],
|
||||
"config": {"protocol_type": ["PumpTransferProtocol"]}
|
||||
},
|
||||
{"id": "pump", "name": "转移泵", "class": "virtual_transfer_pump",
|
||||
"type": "device", "parent": "station",
|
||||
"config": {"port": "VIRTUAL", "max_volume": 25.0},
|
||||
"data": {"status": "Idle", "position": 0.0, "valve_position": "0"}},
|
||||
{"id": "valve", "name": "多通阀", "class": "virtual_multiway_valve",
|
||||
"type": "device", "parent": "station",
|
||||
"config": {"port": "VIRTUAL", "positions": 8}},
|
||||
{"id": "flask_solvent", "name": "溶剂瓶", "type": "container",
|
||||
"class": null, "parent": "station",
|
||||
"config": {"max_volume": 1000.0},
|
||||
"data": {"liquid": [{"liquid_type": "DMF", "liquid_volume": 500}]}},
|
||||
{"id": "reactor", "name": "反应器", "type": "container",
|
||||
"class": null, "parent": "station"},
|
||||
{"id": "waste", "name": "废液瓶", "type": "container",
|
||||
"class": null, "parent": "station"}
|
||||
],
|
||||
"links": [
|
||||
{"source": "pump", "target": "valve", "type": "fluid",
|
||||
"port": {"pump": "transferpump", "valve": "transferpump"}},
|
||||
{"source": "valve", "target": "flask_solvent", "type": "fluid",
|
||||
"port": {"valve": "1", "flask_solvent": "outlet"}},
|
||||
{"source": "valve", "target": "reactor", "type": "fluid",
|
||||
"port": {"valve": "2", "reactor": "inlet"}},
|
||||
{"source": "valve", "target": "waste", "type": "fluid",
|
||||
"port": {"valve": "3", "waste": "inlet"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 模式 C:外部系统工作站 + Deck
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "bioyond_station", "class": "reaction_station.bioyond",
|
||||
"parent": null, "children": ["bioyond_deck"],
|
||||
"config": {
|
||||
"api_host": "http://192.168.1.100:8080",
|
||||
"api_key": "YOUR_KEY",
|
||||
"material_type_mappings": {},
|
||||
"warehouse_mapping": {}
|
||||
},
|
||||
"deck": {
|
||||
"data": {
|
||||
"_resource_child_name": "bioyond_deck",
|
||||
"_resource_type": "unilabos.resources.bioyond.decks:BIOYOND_PolymerReactionStation_Deck"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "bioyond_deck", "class": "BIOYOND_PolymerReactionStation_Deck",
|
||||
"parent": "bioyond_station", "type": "deck",
|
||||
"config": {"type": "BIOYOND_PolymerReactionStation_Deck", "setup": true}
|
||||
}
|
||||
],
|
||||
"links": []
|
||||
}
|
||||
```
|
||||
|
||||
### 模式 D:通信代理(串口设备)
|
||||
|
||||
泵通过串口设备通信,使用 `communication` 类型的 link。加载时系统会自动将串口端口信息写入泵的 `config`:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{"id": "station", "name": "工作站", "type": "device",
|
||||
"class": "workstation", "parent": null,
|
||||
"children": ["serial_1", "pump_1"]},
|
||||
{"id": "serial_1", "name": "串口", "type": "device",
|
||||
"class": "serial", "parent": "station",
|
||||
"config": {"port": "COM7", "baudrate": 9600}},
|
||||
{"id": "pump_1", "name": "注射泵", "type": "device",
|
||||
"class": "syringe_pump_with_valve.runze.SY03B-T08", "parent": "station"}
|
||||
],
|
||||
"links": [
|
||||
{"source": "pump_1", "target": "serial_1", "type": "communication",
|
||||
"port": {"pump_1": "port", "serial_1": "port"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 验证
|
||||
|
||||
```bash
|
||||
# 启动测试
|
||||
unilab -g unilabos/test/experiments/<name>.json --complete_registry
|
||||
|
||||
# 仅检查注册表
|
||||
python -m unilabos --check_mode --skip_env_check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 高级模式
|
||||
|
||||
处理复杂图文件时,详见 [reference.md](reference.md):ResourceDict 完整字段 schema、Pose 标准化规则、Handle 验证机制、GraphML 格式支持、外部系统工作站完整 config 结构。
|
||||
|
||||
---
|
||||
|
||||
## 常见错误
|
||||
|
||||
| 错误 | 原因 | 修复 |
|
||||
|------|------|------|
|
||||
| `class` 找不到 | 注册表中无此设备名 | 在 `unilabos/registry/devices/` 或 `resources/` 中搜索正确名称 |
|
||||
| children/parent 不一致 | 子节点 `parent` 与父节点 `children` 不匹配 | 确保双向一致 |
|
||||
| `_resource_child_name` 不匹配 | Deck 引用名与 Deck 节点 `id` 不同 | 保持一致 |
|
||||
| Link 端口错误 | `port` 中的 key 不是 source/target 的 `id` | key 必须是对应节点的 `id` |
|
||||
| 重复 UUID | 多个节点有相同 `uuid` | 删除或修改 UUID |
|
||||
|
||||
---
|
||||
|
||||
## 参考路径
|
||||
|
||||
| 内容 | 路径 |
|
||||
|------|------|
|
||||
| 图文件目录 | `unilabos/test/experiments/` |
|
||||
| 协议测试站 | `unilabos/test/experiments/Protocol_Test_Station/` |
|
||||
| 图加载代码 | `unilabos/resources/graphio.py` |
|
||||
| 节点模型 | `unilabos/resources/resource_tracker.py` |
|
||||
| 设备注册表 | `unilabos/registry/devices/` |
|
||||
| 资源注册表 | `unilabos/registry/resources/` |
|
||||
| 用户文档 | `docs/user_guide/graph_files.md` |
|
||||
255
.cursor/skills/edit-experiment-graph/reference.md
Normal file
255
.cursor/skills/edit-experiment-graph/reference.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# 实验图高级参考
|
||||
|
||||
本文件是 SKILL.md 的补充,包含 ResourceDict 完整 schema、Handle 验证、GraphML 格式、Pose 标准化规则和复杂图文件结构。Agent 在需要处理这些场景时按需阅读。
|
||||
|
||||
---
|
||||
|
||||
## 1. ResourceDict 完整字段
|
||||
|
||||
`unilabos/resources/resource_tracker.py` 中定义的节点数据模型:
|
||||
|
||||
| 字段 | 类型 | 别名 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `id` | `str` | — | 节点唯一标识 |
|
||||
| `uuid` | `str` | — | 全局唯一标识 |
|
||||
| `name` | `str` | — | 显示名称 |
|
||||
| `description` | `str` | — | 描述(默认 `""` ) |
|
||||
| `resource_schema` | `Dict[str, Any]` | `schema` | 资源 schema |
|
||||
| `model` | `Dict[str, Any]` | — | 3D 模型信息 |
|
||||
| `icon` | `str` | — | 图标(默认 `""` ) |
|
||||
| `parent_uuid` | `Optional[str]` | — | 父节点 UUID |
|
||||
| `parent` | `Optional[ResourceDict]` | — | 父节点引用(序列化时 exclude) |
|
||||
| `type` | `Union[Literal["device"], str]` | — | 节点类型 |
|
||||
| `klass` | `str` | `class` | 注册表类名 |
|
||||
| `pose` | `ResourceDictPosition` | — | 位姿信息 |
|
||||
| `config` | `Dict[str, Any]` | — | 配置参数 |
|
||||
| `data` | `Dict[str, Any]` | — | 运行时数据 |
|
||||
| `extra` | `Dict[str, Any]` | — | 扩展数据 |
|
||||
|
||||
### Pose 完整结构(ResourceDictPosition)
|
||||
|
||||
| 字段 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `size` | `{width, height, depth}` | `{0,0,0}` | 节点尺寸 |
|
||||
| `scale` | `{x, y, z}` | `{1,1,1}` | 缩放比例 |
|
||||
| `layout` | `"2d"/"x-y"/"z-y"/"x-z"` | `"x-y"` | 布局方向 |
|
||||
| `position` | `{x, y, z}` | `{0,0,0}` | 2D 位置 |
|
||||
| `position3d` | `{x, y, z}` | `{0,0,0}` | 3D 位置 |
|
||||
| `rotation` | `{x, y, z}` | `{0,0,0}` | 旋转角度 |
|
||||
| `cross_section_type` | `"rectangle"/"circle"/"rounded_rectangle"` | `"rectangle"` | 横截面形状 |
|
||||
|
||||
---
|
||||
|
||||
## 2. Position / Pose 标准化规则
|
||||
|
||||
图文件中的 `position` 有多种写法,加载时自动标准化。
|
||||
|
||||
### 输入格式兼容
|
||||
|
||||
```json
|
||||
// 格式 A: 直接 {x, y, z}(最常用)
|
||||
"position": {"x": 100, "y": 200, "z": 0}
|
||||
|
||||
// 格式 B: 嵌套 position
|
||||
"position": {"position": {"x": 100, "y": 200, "z": 0}}
|
||||
|
||||
// 格式 C: 使用 pose 字段
|
||||
"pose": {"position": {"x": 100, "y": 200, "z": 0}}
|
||||
|
||||
// 格式 D: 顶层 x, y, z(无 position 字段)
|
||||
"x": 100, "y": 200, "z": 0
|
||||
```
|
||||
|
||||
### 标准化流程
|
||||
|
||||
1. **graphio.py `canonicalize_nodes_data`**:若 `position` 不是 dict,从节点顶层提取 `x/y/z` 填入 `pose.position`
|
||||
2. **resource_tracker.py `get_resource_instance_from_dict`**:若 `position.x` 存在(旧格式),转为 `{"position": {"x":..., "y":..., "z":...}}`
|
||||
3. `pose.size` 从 `config.size_x/size_y/size_z` 自动填充
|
||||
|
||||
---
|
||||
|
||||
## 3. Handle 验证
|
||||
|
||||
启动时系统验证 link 中的 `sourceHandle` / `targetHandle` 是否在注册表的 `handles` 中定义。
|
||||
|
||||
```python
|
||||
# unilabos/app/main.py (约 449-481 行)
|
||||
source_handler_keys = [
|
||||
h["handler_key"] for h in materials[source_node.klass]["handles"]
|
||||
if h["io_type"] == "source"
|
||||
]
|
||||
target_handler_keys = [
|
||||
h["handler_key"] for h in materials[target_node.klass]["handles"]
|
||||
if h["io_type"] == "target"
|
||||
]
|
||||
if source_handle not in source_handler_keys:
|
||||
print_status(f"节点 {source_node.id} 的source端点 {source_handle} 不存在", "error")
|
||||
resource_edge_info.pop(...) # 移除非法 link
|
||||
```
|
||||
|
||||
**Handle 定义在注册表 YAML 中:**
|
||||
|
||||
```yaml
|
||||
my_device:
|
||||
handles:
|
||||
- handler_key: access
|
||||
io_type: target
|
||||
data_type: fluid
|
||||
side: NORTH
|
||||
label: access
|
||||
```
|
||||
|
||||
> 大多数简单设备不定义 handles,此验证仅对有 `sourceHandle`/`targetHandle` 的 link 生效。
|
||||
|
||||
---
|
||||
|
||||
## 4. GraphML 格式支持
|
||||
|
||||
除 JSON 外,系统也支持 GraphML 格式(`unilabos/resources/graphio.py::read_graphml`)。
|
||||
|
||||
### 与 JSON 的关键差异
|
||||
|
||||
| 特性 | JSON | GraphML |
|
||||
|------|------|---------|
|
||||
| 父子关系 | `parent`/`children` 字段 | `::` 分隔的节点 ID(如 `station::pump_1`) |
|
||||
| 加载后 | 直接解析 | 先 `nx.read_graphml` 再转 JSON 格式 |
|
||||
| 输出 | 不生成副本 | 自动生成等价的 `.json` 文件 |
|
||||
|
||||
### GraphML 转换流程
|
||||
|
||||
```
|
||||
nx.read_graphml(file)
|
||||
↓ 用 label 重映射节点名
|
||||
↓ 从 "::" 推断 parent_relation
|
||||
nx.relabel_nodes + nx.node_link_data
|
||||
↓ canonicalize_nodes_data + canonicalize_links_ports
|
||||
↓ 写出等价 JSON 文件
|
||||
physical_setup_graph + handle_communications
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 复杂图文件结构示例
|
||||
|
||||
### 外部系统工作站完整 config
|
||||
|
||||
以 `reaction_station_bioyond.json` 为例,工作站 `config` 中的关键字段:
|
||||
|
||||
```json
|
||||
{
|
||||
"config": {
|
||||
"api_key": "DE9BDDA0",
|
||||
"api_host": "http://172.21.103.36:45388",
|
||||
|
||||
"workflow_mappings": {
|
||||
"scheduler_start": {"workflow": "start", "params": {}},
|
||||
"create_order": {"workflow": "create_order", "params": {}}
|
||||
},
|
||||
|
||||
"material_type_mappings": {
|
||||
"BIOYOND_PolymerStation_Reactor": ["反应器", "type-uuid-here"],
|
||||
"BIOYOND_PolymerStation_1BottleCarrier": ["试剂瓶", "type-uuid-here"]
|
||||
},
|
||||
|
||||
"warehouse_mapping": {
|
||||
"堆栈1左": {
|
||||
"uuid": "warehouse-uuid-here",
|
||||
"site_uuids": {
|
||||
"A01": "site-uuid-1",
|
||||
"A02": "site-uuid-2"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"http_service_config": {
|
||||
"enabled": true,
|
||||
"host": "0.0.0.0",
|
||||
"port": 45399,
|
||||
"routes": ["/callback/workflow", "/callback/material"]
|
||||
},
|
||||
|
||||
"deck": {
|
||||
"data": {
|
||||
"_resource_child_name": "Bioyond_Deck",
|
||||
"_resource_type": "unilabos.resources.bioyond.decks:BIOYOND_PolymerReactionStation_Deck"
|
||||
}
|
||||
},
|
||||
|
||||
"size_x": 2700.0,
|
||||
"size_y": 1080.0,
|
||||
"size_z": 2500.0,
|
||||
"protocol_type": [],
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 子设备 Reactor 节点
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "reactor_1",
|
||||
"name": "reactor_1",
|
||||
"parent": "reaction_station_bioyond",
|
||||
"type": "device",
|
||||
"class": "bioyond_reactor",
|
||||
"position": {"x": 1150, "y": 300, "z": 0},
|
||||
"config": {
|
||||
"reactor_index": 0,
|
||||
"bioyond_workflow_key": "reactor_1"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Deck 节点
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "Bioyond_Deck",
|
||||
"name": "Bioyond_Deck",
|
||||
"parent": "reaction_station_bioyond",
|
||||
"type": "deck",
|
||||
"class": "BIOYOND_PolymerReactionStation_Deck",
|
||||
"position": {"x": 0, "y": 0, "z": 0},
|
||||
"config": {
|
||||
"type": "BIOYOND_PolymerReactionStation_Deck",
|
||||
"setup": true,
|
||||
"rotation": {"x": 0, "y": 0, "z": 0, "type": "Rotation"}
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Link 端口标准化
|
||||
|
||||
`graphio.py::canonicalize_links_ports` 处理 `port` 字段的多种格式:
|
||||
|
||||
```python
|
||||
# 输入: 字符串格式 "(A,B)"
|
||||
"port": "(pump_1, valve_1)"
|
||||
# 输出: 字典格式
|
||||
"port": {"source_id": "pump_1", "target_id": "valve_1"}
|
||||
|
||||
# 输入: 已是字典
|
||||
"port": {"pump_1": "port", "serial_1": "port"}
|
||||
# 保持不变
|
||||
|
||||
# 输入: 无 port 字段
|
||||
# 自动补充空 port
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 关键路径
|
||||
|
||||
| 内容 | 路径 |
|
||||
|------|------|
|
||||
| ResourceDict 模型 | `unilabos/resources/resource_tracker.py` |
|
||||
| 图加载 + 标准化 | `unilabos/resources/graphio.py` |
|
||||
| Handle 验证 | `unilabos/app/main.py` (449-481 行) |
|
||||
| 反应站图文件 | `unilabos/test/experiments/reaction_station_bioyond.json` |
|
||||
| 配液站图文件 | `unilabos/test/experiments/dispensing_station_bioyond.json` |
|
||||
| 用户文档 | `docs/user_guide/graph_files.md` |
|
||||
Reference in New Issue
Block a user