From 01d281189a5d7519bd5959e9595288924b3bed63 Mon Sep 17 00:00:00 2001 From: Xuwznln <18435084+Xuwznln@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:57:50 +0800 Subject: [PATCH] Update Skills --- .cursor/skills/batch-insert-reagent/SKILL.md | 11 + .cursor/skills/host-node/SKILL.md | 240 ++++++++++++++++ .cursor/skills/host-node/action-index.md | 54 ++++ .../host-node/actions/create_resource.json | 93 ++++++ .../host-node/actions/manual_confirm.json | 32 +++ .../host-node/actions/test_latency.json | 11 + .../host-node/actions/test_resource.json | 255 +++++++++++++++++ .cursor/skills/virtual-workbench/SKILL.md | 259 +++++++++++++++++ .../skills/virtual-workbench/action-index.md | 70 +++++ .../actions/manual_confirm.json | 270 ++++++++++++++++++ .../actions/move_to_heating_station.json | 19 ++ .../actions/move_to_output.json | 24 ++ .../actions/prepare_materials.json | 20 ++ .../actions/start_heating.json | 24 ++ .../virtual-workbench/actions/transfer.json | 255 +++++++++++++++++ 15 files changed, 1637 insertions(+) create mode 100644 .cursor/skills/host-node/SKILL.md create mode 100644 .cursor/skills/host-node/action-index.md create mode 100644 .cursor/skills/host-node/actions/create_resource.json create mode 100644 .cursor/skills/host-node/actions/manual_confirm.json create mode 100644 .cursor/skills/host-node/actions/test_latency.json create mode 100644 .cursor/skills/host-node/actions/test_resource.json create mode 100644 .cursor/skills/virtual-workbench/SKILL.md create mode 100644 .cursor/skills/virtual-workbench/action-index.md create mode 100644 .cursor/skills/virtual-workbench/actions/manual_confirm.json create mode 100644 .cursor/skills/virtual-workbench/actions/move_to_heating_station.json create mode 100644 .cursor/skills/virtual-workbench/actions/move_to_output.json create mode 100644 .cursor/skills/virtual-workbench/actions/prepare_materials.json create mode 100644 .cursor/skills/virtual-workbench/actions/start_heating.json create mode 100644 .cursor/skills/virtual-workbench/actions/transfer.json diff --git a/.cursor/skills/batch-insert-reagent/SKILL.md b/.cursor/skills/batch-insert-reagent/SKILL.md index 884b0e5e..3df13fd3 100644 --- a/.cursor/skills/batch-insert-reagent/SKILL.md +++ b/.cursor/skills/batch-insert-reagent/SKILL.md @@ -176,6 +176,16 @@ cas,name,molecular_formula,smiles,stock_in_quantity,unit,supplier,production_dat 7732-18-3,水,H2O,O,10,mL,农夫山泉,2025-11-18T00:00:00Z,2026-11-18T00:00:00Z ``` +### 日期格式规则(重要) + +所有日期字段(`production_date`、`expiry_date`)**必须**使用 ISO 8601 完整格式:`YYYY-MM-DDTHH:MM:SSZ`。 + +- 用户输入 `2025-03-01` → 转换为 `"2025-03-01T00:00:00Z"` +- 用户输入 `2025/9/1` → 转换为 `"2025-09-01T00:00:00Z"` +- 用户未提供日期 → 使用当天日期 + `T00:00:00Z`,有效期默认 +1 年 + +**禁止**发送不带时间部分的日期字符串(如 `"2025-03-01"`),API 会拒绝。 + ### 执行与汇报 每次 API 调用后: @@ -193,6 +203,7 @@ cas,name,molecular_formula,smiles,stock_in_quantity,unit,supplier,production_dat | --------------------- | --------- | ---------- | ------------------------------------ | | 水 | 7732-18-3 | H2O | O | | 乙醇 | 64-17-5 | C2H6O | CCO | +| 乙酸 | 64-19-7 | C2H4O2 | CC(O)=O | | 甲醇 | 67-56-1 | CH4O | CO | | 丙酮 | 67-64-1 | C3H6O | CC(C)=O | | 二甲基亚砜(DMSO) | 67-68-5 | C2H6OS | CS(C)=O | diff --git a/.cursor/skills/host-node/SKILL.md b/.cursor/skills/host-node/SKILL.md new file mode 100644 index 00000000..52aaa87a --- /dev/null +++ b/.cursor/skills/host-node/SKILL.md @@ -0,0 +1,240 @@ +--- +name: host-node +description: Operate Uni-Lab host node via REST API — create resources, test latency, test resource tree, manual confirm. Use when the user mentions host_node, creating resources, resource management, testing latency, or any host node operation. +--- + +# Host Node API Skill + +## 设备信息 + +- **device_id**: `host_node` +- **Python 源码**: `unilabos/ros/nodes/presets/host_node.py` +- **设备类**: `HostNode` +- **动作数**: 4(`create_resource`, `test_latency`, `auto-test_resource`, `manual_confirm`) + +## 前置条件(缺一不可) + +使用本 skill 前,**必须**先确认以下信息。如果缺少任何一项,**立即向用户询问并终止**,等补齐后再继续。 + +### 1. ak / sk → AUTH + +从启动参数 `--ak` `--sk` 或 config.py 中获取,生成 token:`base64(ak:sk)` → `Authorization: Lab ` + +### 2. --addr → BASE URL + +| `--addr` 值 | BASE | +| ------------ | ----------------------------------- | +| `test` | `https://leap-lab.test.bohrium.com` | +| `uat` | `https://leap-lab.uat.bohrium.com` | +| `local` | `http://127.0.0.1:48197` | +| 不传(默认) | `https://leap-lab.bohrium.com` | + +确认后设置: + +```bash +BASE="<根据 addr 确定的 URL>" +AUTH="Authorization: Lab " +``` + +**两项全部就绪后才可发起 API 请求。** + +## Session State + +在整个对话过程中,agent 需要记住以下状态,避免重复询问用户: + +- `lab_uuid` — 实验室 UUID(首次通过 API #1 自动获取,**不需要问用户**) +- `device_name` — `host_node` + +## 请求约定 + +所有请求使用 `curl -s`,POST/PATCH/DELETE 需加 `Content-Type: application/json`。 + +> **Windows 平台**必须使用 `curl.exe`(而非 PowerShell 的 `curl` 别名)。 + +--- + +## API Endpoints + +### 1. 获取实验室信息(自动获取 lab_uuid) + +```bash +curl -s -X GET "$BASE/api/v1/edge/lab/info" -H "$AUTH" +``` + +返回 `data.uuid` 为 `lab_uuid`,`data.name` 为 `lab_name`。 + +### 2. 创建工作流 + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow/owner" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"name":"<名称>","lab_uuid":"","description":"<描述>"}' +``` + +返回 `data.uuid` 为 `workflow_uuid`。创建成功后告知用户链接:`$BASE/laboratory/$lab_uuid/workflow/$workflow_uuid` + +### 3. 创建节点 + +```bash +curl -s -X POST "$BASE/api/v1/edge/workflow/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"workflow_uuid":"","resource_template_name":"host_node","node_template_name":""}' +``` + +- `resource_template_name` 固定为 `host_node` +- `node_template_name` — action 名称(如 `create_resource`, `test_latency`) + +### 4. 删除节点 + +```bash +curl -s -X DELETE "$BASE/api/v1/lab/workflow/nodes" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"node_uuids":[""],"workflow_uuid":""}' +``` + +### 5. 更新节点参数 + +```bash +curl -s -X PATCH "$BASE/api/v1/lab/workflow/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"workflow_uuid":"","uuid":"","param":{...}}' +``` + +`param` 直接使用创建节点返回的 `data.param` 结构,修改需要填入的字段值。参考 [action-index.md](action-index.md) 确定哪些字段是 Slot。 + +### 6. 查询节点 handles + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow/node-handles" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"node_uuids":["",""]}' +``` + +### 7. 批量创建边 + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow/edges" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"edges":[{"source_node_uuid":"","target_node_uuid":"","source_handle_uuid":"","target_handle_uuid":""}]}' +``` + +### 8. 启动工作流 + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow//run" -H "$AUTH" +``` + +### 9. 运行设备单动作 + +```bash +curl -s -X POST "$BASE/api/v1/lab/mcp/run/action" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"lab_uuid":"","device_id":"host_node","action":"","action_type":"","param":{...}}' +``` + +`param` 直接放 goal 里的属性,**不要**再包一层 `{"goal": {...}}`。`action_type` 从 `actions/.json` 的 `type` 字段获取。 + +### 10. 查询任务状态 + +```bash +curl -s -X GET "$BASE/api/v1/lab/mcp/task/" -H "$AUTH" +``` + +### 11. 运行工作流单节点 + +```bash +curl -s -X POST "$BASE/api/v1/lab/mcp/run/workflow/action" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"node_uuid":""}' +``` + +### 12. 获取资源树(物料信息) + +```bash +curl -s -X GET "$BASE/api/v1/lab/material/download/$lab_uuid" -H "$AUTH" +``` + +注意 `lab_uuid` 在路径中。返回 `data.nodes[]` 含所有节点(设备 + 物料),每个节点含 `name`、`uuid`、`type`、`parent`。 + +### 13. 获取工作流模板详情 + +```bash +curl -s -X GET "$BASE/api/v1/lab/workflow/template/detail/$workflow_uuid" -H "$AUTH" +``` + +> 必须使用 `/lab/workflow/template/detail/{uuid}`,其他路径会返回 404。 + +### 14. 按名称查询物料模板 + +```bash +curl -s -X GET "$BASE/api/v1/lab/material/template/by-name?lab_uuid=$lab_uuid&name=" -H "$AUTH" +``` + +返回 `data.uuid` 为 `res_template_uuid`,用于 API #15。 + +### 15. 创建物料节点 + +```bash +curl -s -X POST "$BASE/api/v1/edge/material/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"res_template_uuid":"","name":"<名称>","display_name":"<显示名>","parent_uuid":"<父节点uuid>","data":{...}}' +``` + +### 16. 更新物料节点 + +```bash +curl -s -X PUT "$BASE/api/v1/edge/material/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"uuid":"<节点uuid>","display_name":"<新名称>","data":{...}}' +``` + +--- + +## Placeholder Slot 填写规则 + +| `placeholder_keys` 值 | Slot 类型 | 填写格式 | 选取范围 | +| --------------------- | ------------ | ----------------------------------------------------- | ---------------------- | +| `unilabos_resources` | ResourceSlot | `{"id": "/path/name", "name": "name", "uuid": "xxx"}` | 仅物料节点(非设备) | +| `unilabos_devices` | DeviceSlot | `"/parent/device_name"` | 仅设备节点(type=device) | +| `unilabos_nodes` | NodeSlot | `"/parent/node_name"` | 所有节点(设备 + 物料) | +| `unilabos_class` | ClassSlot | `"class_name"` | 注册表中已注册的资源类 | + +### host_node 设备的 Slot 字段表 + +| Action | 字段 | Slot 类型 | 说明 | +| ----------------- | ----------- | ------------ | ------------------------------ | +| `create_resource` | `res_id` | ResourceSlot | 新资源路径(可填不存在的路径) | +| `create_resource` | `device_id` | DeviceSlot | 归属设备 | +| `create_resource` | `parent` | NodeSlot | 父节点路径 | +| `create_resource` | `class_name`| ClassSlot | 资源类名如 `"container"` | +| `auto-test_resource` | `resource` | ResourceSlot | 单个测试物料 | +| `auto-test_resource` | `resources` | ResourceSlot | 测试物料数组 | +| `auto-test_resource` | `device` | DeviceSlot | 测试设备 | +| `auto-test_resource` | `devices` | DeviceSlot | 测试设备 | + +--- + +## 渐进加载策略 + +1. **SKILL.md**(本文件)— API 端点 + session state 管理 +2. **[action-index.md](action-index.md)** — 按分类浏览 4 个动作的描述和核心参数 +3. **[actions/\.json](actions/)** — 仅在需要构建具体请求时,加载对应 action 的完整 JSON Schema + +--- + +## 完整工作流 Checklist + +``` +Task Progress: +- [ ] Step 1: GET /edge/lab/info 获取 lab_uuid +- [ ] Step 2: 获取资源树 (GET #12) → 记住可用物料 +- [ ] Step 3: 读 action-index.md 确定要用的 action 名 +- [ ] Step 4: 创建工作流 (POST #2) → 记住 workflow_uuid,告知用户链接 +- [ ] Step 5: 创建节点 (POST #3, resource_template_name=host_node) → 记住 node_uuid + data.param +- [ ] Step 6: 根据 _unilabos_placeholder_info 和资源树,填写 data.param 中的 Slot 字段 +- [ ] Step 7: 更新节点参数 (PATCH #5) +- [ ] Step 8: 查询节点 handles (POST #6) → 获取各节点的 handle_uuid +- [ ] Step 9: 批量创建边 (POST #7) → 用 handle_uuid 连接节点 +- [ ] Step 10: 启动工作流 (POST #8) 或运行单节点 (POST #11) +- [ ] Step 11: 查询任务状态 (GET #10) 确认完成 +``` diff --git a/.cursor/skills/host-node/action-index.md b/.cursor/skills/host-node/action-index.md new file mode 100644 index 00000000..c565af22 --- /dev/null +++ b/.cursor/skills/host-node/action-index.md @@ -0,0 +1,54 @@ +# Action Index — host_node + +4 个动作,按功能分类。每个动作的完整 JSON Schema 在 `actions/.json`。 + +--- + +## 资源管理 + +### `create_resource` + +在资源树中创建新资源(容器、物料等),支持指定位置、类型和初始液体 + +- **Schema**: [`actions/create_resource.json`](actions/create_resource.json) +- **可选参数**: `res_id`, `device_id`, `class_name`, `parent`, `bind_locations`, `liquid_input_slot`, `liquid_type`, `liquid_volume`, `slot_on_deck` +- **占位符字段**: + - `res_id` — **ResourceSlot**(特例:目标物料可能尚不存在,直接填期望路径) + - `device_id` — **DeviceSlot**,填路径字符串如 `"/host_node"` + - `parent` — **NodeSlot**,填路径字符串如 `"/workstation/deck"` + - `class_name` — **ClassSlot**,填类名如 `"container"` + +### `auto-test_resource` + +测试资源系统,返回当前资源树和设备列表 + +- **Schema**: [`actions/test_resource.json`](actions/test_resource.json) +- **可选参数**: `resource`, `resources`, `device`, `devices` +- **占位符字段**: + - `resource` — **ResourceSlot**,单个物料节点 `{id, name, uuid}` + - `resources` — **ResourceSlot**,物料节点数组 `[{id, name, uuid}, ...]` + - `device` — **DeviceSlot**,设备路径字符串 + - `devices` — **DeviceSlot**,设备路径字符串 + +--- + +## 系统工具 + +### `test_latency` + +测试设备通信延迟,返回 RTT、时间差、任务延迟等指标 + +- **Schema**: [`actions/test_latency.json`](actions/test_latency.json) +- **参数**: 无(零参数调用) + +--- + +## 人工确认 + +### `manual_confirm` + +创建人工确认节点,等待用户手动确认后继续 + +- **Schema**: [`actions/manual_confirm.json`](actions/manual_confirm.json) +- **核心参数**: `timeout_seconds`(超时时间,秒), `assignee_user_ids`(指派用户 ID 列表) +- **占位符字段**: `assignee_user_ids` — `unilabos_manual_confirm` 类型 diff --git a/.cursor/skills/host-node/actions/create_resource.json b/.cursor/skills/host-node/actions/create_resource.json new file mode 100644 index 00000000..c7f16d5b --- /dev/null +++ b/.cursor/skills/host-node/actions/create_resource.json @@ -0,0 +1,93 @@ +{ + "type": "ResourceCreateFromOuterEasy", + "goal": { + "res_id": "res_id", + "class_name": "class_name", + "parent": "parent", + "device_id": "device_id", + "bind_locations": "bind_locations", + "liquid_input_slot": "liquid_input_slot[]", + "liquid_type": "liquid_type[]", + "liquid_volume": "liquid_volume[]", + "slot_on_deck": "slot_on_deck" + }, + "schema": { + "type": "object", + "properties": { + "res_id": { + "type": "string" + }, + "device_id": { + "type": "string" + }, + "class_name": { + "type": "string" + }, + "parent": { + "type": "string" + }, + "bind_locations": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "bind_locations", + "additionalProperties": false + }, + "liquid_input_slot": { + "type": "array", + "items": { + "type": "integer" + } + }, + "liquid_type": { + "type": "array", + "items": { + "type": "string" + } + }, + "liquid_volume": { + "type": "array", + "items": { + "type": "number" + } + }, + "slot_on_deck": { + "type": "string" + } + }, + "required": [], + "_unilabos_placeholder_info": { + "res_id": "unilabos_resources", + "device_id": "unilabos_devices", + "parent": "unilabos_nodes", + "class_name": "unilabos_class" + } + }, + "goal_default": {}, + "placeholder_keys": { + "res_id": "unilabos_resources", + "device_id": "unilabos_devices", + "parent": "unilabos_nodes", + "class_name": "unilabos_class" + } +} \ No newline at end of file diff --git a/.cursor/skills/host-node/actions/manual_confirm.json b/.cursor/skills/host-node/actions/manual_confirm.json new file mode 100644 index 00000000..ee0b220e --- /dev/null +++ b/.cursor/skills/host-node/actions/manual_confirm.json @@ -0,0 +1,32 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "timeout_seconds": "timeout_seconds", + "assignee_user_ids": "assignee_user_ids" + }, + "schema": { + "type": "object", + "properties": { + "timeout_seconds": { + "type": "integer" + }, + "assignee_user_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "timeout_seconds", + "assignee_user_ids" + ], + "_unilabos_placeholder_info": { + "assignee_user_ids": "unilabos_manual_confirm" + } + }, + "goal_default": {}, + "placeholder_keys": { + "assignee_user_ids": "unilabos_manual_confirm" + } +} \ No newline at end of file diff --git a/.cursor/skills/host-node/actions/test_latency.json b/.cursor/skills/host-node/actions/test_latency.json new file mode 100644 index 00000000..0fbd448f --- /dev/null +++ b/.cursor/skills/host-node/actions/test_latency.json @@ -0,0 +1,11 @@ +{ + "type": "UniLabJsonCommand", + "goal": {}, + "schema": { + "type": "object", + "properties": {}, + "required": [] + }, + "goal_default": {}, + "placeholder_keys": {} +} \ No newline at end of file diff --git a/.cursor/skills/host-node/actions/test_resource.json b/.cursor/skills/host-node/actions/test_resource.json new file mode 100644 index 00000000..e9459fc7 --- /dev/null +++ b/.cursor/skills/host-node/actions/test_resource.json @@ -0,0 +1,255 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "resource": "resource", + "resources": "resources", + "device": "device", + "devices": "devices" + }, + "schema": { + "type": "object", + "properties": { + "resource": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sample_id": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "parent": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "pose": { + "type": "object", + "properties": { + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "position", + "additionalProperties": false + }, + "orientation": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "w": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "title": "orientation", + "additionalProperties": false + } + }, + "required": [ + "position", + "orientation" + ], + "title": "pose", + "additionalProperties": false + }, + "config": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "title": "resource" + }, + "resources": { + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sample_id": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "parent": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "pose": { + "type": "object", + "properties": { + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "position", + "additionalProperties": false + }, + "orientation": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "w": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "title": "orientation", + "additionalProperties": false + } + }, + "required": [ + "position", + "orientation" + ], + "title": "pose", + "additionalProperties": false + }, + "config": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "title": "resources" + }, + "type": "array" + }, + "device": { + "type": "string", + "description": "device reference" + }, + "devices": { + "type": "string", + "description": "device reference" + } + }, + "required": [], + "_unilabos_placeholder_info": { + "resource": "unilabos_resources", + "resources": "unilabos_resources", + "device": "unilabos_devices", + "devices": "unilabos_devices" + } + }, + "goal_default": {}, + "placeholder_keys": { + "resource": "unilabos_resources", + "resources": "unilabos_resources", + "device": "unilabos_devices", + "devices": "unilabos_devices" + } +} \ No newline at end of file diff --git a/.cursor/skills/virtual-workbench/SKILL.md b/.cursor/skills/virtual-workbench/SKILL.md new file mode 100644 index 00000000..5fe33d28 --- /dev/null +++ b/.cursor/skills/virtual-workbench/SKILL.md @@ -0,0 +1,259 @@ +--- +name: virtual-workbench +description: Operate Virtual Workbench via REST API — prepare materials, move to heating stations, start heating, move to output, transfer resources. Use when the user mentions virtual workbench, virtual_workbench, 虚拟工作台, heating stations, material processing, or workbench operations. +--- + +# Virtual Workbench API Skill + +## 设备信息 + +- **device_id**: `virtual_workbench` +- **Python 源码**: `unilabos/devices/virtual/workbench.py` +- **设备类**: `VirtualWorkbench` +- **动作数**: 6(`auto-prepare_materials`, `auto-move_to_heating_station`, `auto-start_heating`, `auto-move_to_output`, `transfer`, `manual_confirm`) +- **设备描述**: 模拟工作台,包含 1 个机械臂(每次操作 2s,独占锁)和 3 个加热台(每次加热 60s,可并行) + +### 典型工作流程 + +1. `prepare_materials` — 生成 A1-A5 物料(5 个 output handle) +2. `move_to_heating_station` — 物料并发竞争机械臂,移动到空闲加热台 +3. `start_heating` — 启动加热(3 个加热台可并行) +4. `move_to_output` — 加热完成后移到输出位置 Cn + +## 前置条件(缺一不可) + +使用本 skill 前,**必须**先确认以下信息。如果缺少任何一项,**立即向用户询问并终止**,等补齐后再继续。 + +### 1. ak / sk → AUTH + +从启动参数 `--ak` `--sk` 或 config.py 中获取,生成 token:`base64(ak:sk)` → `Authorization: Lab ` + +### 2. --addr → BASE URL + +| `--addr` 值 | BASE | +| ------------ | ----------------------------------- | +| `test` | `https://leap-lab.test.bohrium.com` | +| `uat` | `https://leap-lab.uat.bohrium.com` | +| `local` | `http://127.0.0.1:48197` | +| 不传(默认) | `https://leap-lab.bohrium.com` | + +确认后设置: + +```bash +BASE="<根据 addr 确定的 URL>" +AUTH="Authorization: Lab " +``` + +**两项全部就绪后才可发起 API 请求。** + +## Session State + +- `lab_uuid` — 实验室 UUID(首次通过 API #1 自动获取,**不需要问用户**) +- `device_name` — `virtual_workbench` + +## 请求约定 + +所有请求使用 `curl -s`,POST/PATCH/DELETE 需加 `Content-Type: application/json`。 + +> **Windows 平台**必须使用 `curl.exe`(而非 PowerShell 的 `curl` 别名)。 + +--- + +## API Endpoints + +### 1. 获取实验室信息(自动获取 lab_uuid) + +```bash +curl -s -X GET "$BASE/api/v1/edge/lab/info" -H "$AUTH" +``` + +返回 `data.uuid` 为 `lab_uuid`,`data.name` 为 `lab_name`。 + +### 2. 创建工作流 + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow/owner" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"name":"<名称>","lab_uuid":"","description":"<描述>"}' +``` + +返回 `data.uuid` 为 `workflow_uuid`。创建成功后告知用户链接:`$BASE/laboratory/$lab_uuid/workflow/$workflow_uuid` + +### 3. 创建节点 + +```bash +curl -s -X POST "$BASE/api/v1/edge/workflow/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"workflow_uuid":"","resource_template_name":"virtual_workbench","node_template_name":""}' +``` + +- `resource_template_name` 固定为 `virtual_workbench` +- `node_template_name` — action 名称(如 `auto-prepare_materials`, `auto-move_to_heating_station`) + +### 4. 删除节点 + +```bash +curl -s -X DELETE "$BASE/api/v1/lab/workflow/nodes" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"node_uuids":[""],"workflow_uuid":""}' +``` + +### 5. 更新节点参数 + +```bash +curl -s -X PATCH "$BASE/api/v1/lab/workflow/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"workflow_uuid":"","uuid":"","param":{...}}' +``` + +参考 [action-index.md](action-index.md) 确定哪些字段是 Slot。 + +### 6. 查询节点 handles + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow/node-handles" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"node_uuids":["",""]}' +``` + +### 7. 批量创建边 + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow/edges" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"edges":[{"source_node_uuid":"","target_node_uuid":"","source_handle_uuid":"","target_handle_uuid":""}]}' +``` + +### 8. 启动工作流 + +```bash +curl -s -X POST "$BASE/api/v1/lab/workflow//run" -H "$AUTH" +``` + +### 9. 运行设备单动作 + +```bash +curl -s -X POST "$BASE/api/v1/lab/mcp/run/action" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"lab_uuid":"","device_id":"virtual_workbench","action":"","action_type":"","param":{...}}' +``` + +`param` 直接放 goal 里的属性,**不要**再包一层 `{"goal": {...}}`。`action_type` 从 `actions/.json` 的 `type` 字段获取。 + +### 10. 查询任务状态 + +```bash +curl -s -X GET "$BASE/api/v1/lab/mcp/task/" -H "$AUTH" +``` + +### 11. 运行工作流单节点 + +```bash +curl -s -X POST "$BASE/api/v1/lab/mcp/run/workflow/action" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"node_uuid":""}' +``` + +### 12. 获取资源树(物料信息) + +```bash +curl -s -X GET "$BASE/api/v1/lab/material/download/$lab_uuid" -H "$AUTH" +``` + +注意 `lab_uuid` 在路径中。返回 `data.nodes[]` 含所有节点(设备 + 物料),每个节点含 `name`、`uuid`、`type`、`parent`。 + +### 13. 获取工作流模板详情 + +```bash +curl -s -X GET "$BASE/api/v1/lab/workflow/template/detail/$workflow_uuid" -H "$AUTH" +``` + +> 必须使用 `/lab/workflow/template/detail/{uuid}`,其他路径会返回 404。 + +### 14. 按名称查询物料模板 + +```bash +curl -s -X GET "$BASE/api/v1/lab/material/template/by-name?lab_uuid=$lab_uuid&name=" -H "$AUTH" +``` + +返回 `data.uuid` 为 `res_template_uuid`,用于 API #15。 + +### 15. 创建物料节点 + +```bash +curl -s -X POST "$BASE/api/v1/edge/material/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"res_template_uuid":"","name":"<名称>","display_name":"<显示名>","parent_uuid":"<父节点uuid>","data":{...}}' +``` + +### 16. 更新物料节点 + +```bash +curl -s -X PUT "$BASE/api/v1/edge/material/node" \ + -H "$AUTH" -H "Content-Type: application/json" \ + -d '{"uuid":"<节点uuid>","display_name":"<新名称>","data":{...}}' +``` + +--- + +## Placeholder Slot 填写规则 + +| `placeholder_keys` 值 | Slot 类型 | 填写格式 | 选取范围 | +| --------------------- | ------------ | ----------------------------------------------------- | ---------------------- | +| `unilabos_resources` | ResourceSlot | `{"id": "/path/name", "name": "name", "uuid": "xxx"}` | 仅物料节点(非设备) | +| `unilabos_devices` | DeviceSlot | `"/parent/device_name"` | 仅设备节点(type=device) | +| `unilabos_nodes` | NodeSlot | `"/parent/node_name"` | 所有节点(设备 + 物料) | +| `unilabos_class` | ClassSlot | `"class_name"` | 注册表中已注册的资源类 | + +### virtual_workbench 设备的 Slot 字段表 + +| Action | 字段 | Slot 类型 | 说明 | +| ----------------- | ---------------- | ------------ | -------------------- | +| `transfer` | `resource` | ResourceSlot | 待转移物料数组 | +| `transfer` | `target_device` | DeviceSlot | 目标设备路径 | +| `transfer` | `mount_resource` | ResourceSlot | 目标孔位数组 | +| `manual_confirm` | `resource` | ResourceSlot | 确认用物料数组 | +| `manual_confirm` | `target_device` | DeviceSlot | 确认用目标设备 | +| `manual_confirm` | `mount_resource` | ResourceSlot | 确认用目标孔位数组 | + +> `prepare_materials`、`move_to_heating_station`、`start_heating`、`move_to_output` 这 4 个动作**无 Slot 字段**,参数为纯数值/整数。 + +--- + +## 渐进加载策略 + +1. **SKILL.md**(本文件)— API 端点 + session state 管理 + 设备工作流概览 +2. **[action-index.md](action-index.md)** — 按分类浏览 6 个动作的描述和核心参数 +3. **[actions/\.json](actions/)** — 仅在需要构建具体请求时,加载对应 action 的完整 JSON Schema + +--- + +## 完整工作流 Checklist + +``` +Task Progress: +- [ ] Step 1: GET /edge/lab/info 获取 lab_uuid +- [ ] Step 2: 获取资源树 (GET #12) → 记住可用物料 +- [ ] Step 3: 读 action-index.md 确定要用的 action 名 +- [ ] Step 4: 创建工作流 (POST #2) → 记住 workflow_uuid,告知用户链接 +- [ ] Step 5: 创建节点 (POST #3, resource_template_name=virtual_workbench) → 记住 node_uuid + data.param +- [ ] Step 6: 根据 _unilabos_placeholder_info 和资源树,填写 data.param 中的 Slot 字段 +- [ ] Step 7: 更新节点参数 (PATCH #5) +- [ ] Step 8: 查询节点 handles (POST #6) → 获取各节点的 handle_uuid +- [ ] Step 9: 批量创建边 (POST #7) → 用 handle_uuid 连接节点 +- [ ] Step 10: 启动工作流 (POST #8) 或运行单节点 (POST #11) +- [ ] Step 11: 查询任务状态 (GET #10) 确认完成 +``` + +### 典型 5 物料并发加热工作流示例 + +``` +prepare_materials (count=5) + ├─ channel_1 → move_to_heating_station (material_number=1) → start_heating → move_to_output + ├─ channel_2 → move_to_heating_station (material_number=2) → start_heating → move_to_output + ├─ channel_3 → move_to_heating_station (material_number=3) → start_heating → move_to_output + ├─ channel_4 → move_to_heating_station (material_number=4) → start_heating → move_to_output + └─ channel_5 → move_to_heating_station (material_number=5) → start_heating → move_to_output +``` + +创建节点时,`prepare_materials` 的 5 个 output handle(`channel_1` ~ `channel_5`)分别连接到 5 个 `move_to_heating_station` 节点的 `material_input` handle。每个 `move_to_heating_station` 的 `heating_station_output` 和 `material_number_output` 连接到对应 `start_heating` 的 `station_id_input` 和 `material_number_input`。 diff --git a/.cursor/skills/virtual-workbench/action-index.md b/.cursor/skills/virtual-workbench/action-index.md new file mode 100644 index 00000000..64f940ff --- /dev/null +++ b/.cursor/skills/virtual-workbench/action-index.md @@ -0,0 +1,70 @@ +# Action Index — virtual_workbench + +6 个动作,按功能分类。每个动作的完整 JSON Schema 在 `actions/.json`。 + +--- + +## 物料准备 + +### `auto-prepare_materials` + +批量准备物料(虚拟起始节点),生成 A1-A5 物料编号,输出 5 个 handle 供后续节点使用 + +- **Schema**: [`actions/prepare_materials.json`](actions/prepare_materials.json) +- **可选参数**: `count`(物料数量,默认 5) + +--- + +## 机械臂 & 加热台操作 + +### `auto-move_to_heating_station` + +将物料从 An 位置移动到空闲加热台(竞争机械臂,自动查找空闲加热台) + +- **Schema**: [`actions/move_to_heating_station.json`](actions/move_to_heating_station.json) +- **核心参数**: `material_number`(物料编号,integer) + +### `auto-start_heating` + +启动指定加热台的加热程序(可并行,3 个加热台同时工作) + +- **Schema**: [`actions/start_heating.json`](actions/start_heating.json) +- **核心参数**: `station_id`(加热台 ID),`material_number`(物料编号) + +### `auto-move_to_output` + +将加热完成的物料从加热台移动到输出位置 Cn + +- **Schema**: [`actions/move_to_output.json`](actions/move_to_output.json) +- **核心参数**: `station_id`(加热台 ID),`material_number`(物料编号) + +--- + +## 物料转移 + +### `transfer` + +异步转移物料到目标设备(通过 ROS 资源转移) + +- **Schema**: [`actions/transfer.json`](actions/transfer.json) +- **核心参数**: `resource`, `target_device`, `mount_resource` +- **占位符字段**: + - `resource` — **ResourceSlot**,待转移的物料数组 `[{id, name, uuid}, ...]` + - `target_device` — **DeviceSlot**,目标设备路径字符串 + - `mount_resource` — **ResourceSlot**,目标孔位数组 `[{id, name, uuid}, ...]` + +--- + +## 人工确认 + +### `manual_confirm` + +创建人工确认节点,等待用户手动确认后继续(含物料转移上下文) + +- **Schema**: [`actions/manual_confirm.json`](actions/manual_confirm.json) +- **核心参数**: `resource`, `target_device`, `mount_resource`, `timeout_seconds`, `assignee_user_ids` +- **占位符字段**: + - `resource` — **ResourceSlot**,物料数组 + - `target_device` — **DeviceSlot**,目标设备路径 + - `mount_resource` — **ResourceSlot**,目标孔位数组 + - `assignee_user_ids` — `unilabos_manual_confirm` 类型 diff --git a/.cursor/skills/virtual-workbench/actions/manual_confirm.json b/.cursor/skills/virtual-workbench/actions/manual_confirm.json new file mode 100644 index 00000000..84d06f5b --- /dev/null +++ b/.cursor/skills/virtual-workbench/actions/manual_confirm.json @@ -0,0 +1,270 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "resource": "resource", + "target_device": "target_device", + "mount_resource": "mount_resource", + "timeout_seconds": "timeout_seconds", + "assignee_user_ids": "assignee_user_ids" + }, + "schema": { + "type": "object", + "properties": { + "resource": { + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sample_id": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "parent": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "pose": { + "type": "object", + "properties": { + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "position", + "additionalProperties": false + }, + "orientation": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "w": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "title": "orientation", + "additionalProperties": false + } + }, + "required": [ + "position", + "orientation" + ], + "title": "pose", + "additionalProperties": false + }, + "config": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "title": "resource" + }, + "type": "array" + }, + "target_device": { + "type": "string", + "description": "device reference" + }, + "mount_resource": { + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sample_id": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "parent": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "pose": { + "type": "object", + "properties": { + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "position", + "additionalProperties": false + }, + "orientation": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "w": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "title": "orientation", + "additionalProperties": false + } + }, + "required": [ + "position", + "orientation" + ], + "title": "pose", + "additionalProperties": false + }, + "config": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "title": "mount_resource" + }, + "type": "array" + }, + "timeout_seconds": { + "type": "integer" + }, + "assignee_user_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "resource", + "target_device", + "mount_resource", + "timeout_seconds", + "assignee_user_ids" + ], + "_unilabos_placeholder_info": { + "resource": "unilabos_resources", + "target_device": "unilabos_devices", + "mount_resource": "unilabos_resources", + "assignee_user_ids": "unilabos_manual_confirm" + } + }, + "goal_default": {}, + "placeholder_keys": { + "resource": "unilabos_resources", + "target_device": "unilabos_devices", + "mount_resource": "unilabos_resources", + "assignee_user_ids": "unilabos_manual_confirm" + } +} \ No newline at end of file diff --git a/.cursor/skills/virtual-workbench/actions/move_to_heating_station.json b/.cursor/skills/virtual-workbench/actions/move_to_heating_station.json new file mode 100644 index 00000000..b5e55adc --- /dev/null +++ b/.cursor/skills/virtual-workbench/actions/move_to_heating_station.json @@ -0,0 +1,19 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "material_number": "material_number" + }, + "schema": { + "type": "object", + "properties": { + "material_number": { + "type": "integer" + } + }, + "required": [ + "material_number" + ] + }, + "goal_default": {}, + "placeholder_keys": {} +} \ No newline at end of file diff --git a/.cursor/skills/virtual-workbench/actions/move_to_output.json b/.cursor/skills/virtual-workbench/actions/move_to_output.json new file mode 100644 index 00000000..913e8679 --- /dev/null +++ b/.cursor/skills/virtual-workbench/actions/move_to_output.json @@ -0,0 +1,24 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "station_id": "station_id", + "material_number": "material_number" + }, + "schema": { + "type": "object", + "properties": { + "station_id": { + "type": "integer" + }, + "material_number": { + "type": "integer" + } + }, + "required": [ + "station_id", + "material_number" + ] + }, + "goal_default": {}, + "placeholder_keys": {} +} \ No newline at end of file diff --git a/.cursor/skills/virtual-workbench/actions/prepare_materials.json b/.cursor/skills/virtual-workbench/actions/prepare_materials.json new file mode 100644 index 00000000..5fbd8a9c --- /dev/null +++ b/.cursor/skills/virtual-workbench/actions/prepare_materials.json @@ -0,0 +1,20 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "count": "count" + }, + "schema": { + "type": "object", + "properties": { + "count": { + "type": "integer", + "default": 5 + } + }, + "required": [] + }, + "goal_default": { + "count": 5 + }, + "placeholder_keys": {} +} \ No newline at end of file diff --git a/.cursor/skills/virtual-workbench/actions/start_heating.json b/.cursor/skills/virtual-workbench/actions/start_heating.json new file mode 100644 index 00000000..913e8679 --- /dev/null +++ b/.cursor/skills/virtual-workbench/actions/start_heating.json @@ -0,0 +1,24 @@ +{ + "type": "UniLabJsonCommand", + "goal": { + "station_id": "station_id", + "material_number": "material_number" + }, + "schema": { + "type": "object", + "properties": { + "station_id": { + "type": "integer" + }, + "material_number": { + "type": "integer" + } + }, + "required": [ + "station_id", + "material_number" + ] + }, + "goal_default": {}, + "placeholder_keys": {} +} \ No newline at end of file diff --git a/.cursor/skills/virtual-workbench/actions/transfer.json b/.cursor/skills/virtual-workbench/actions/transfer.json new file mode 100644 index 00000000..c286c68f --- /dev/null +++ b/.cursor/skills/virtual-workbench/actions/transfer.json @@ -0,0 +1,255 @@ +{ + "type": "UniLabJsonCommandAsync", + "goal": { + "resource": "resource", + "target_device": "target_device", + "mount_resource": "mount_resource" + }, + "schema": { + "type": "object", + "properties": { + "resource": { + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sample_id": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "parent": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "pose": { + "type": "object", + "properties": { + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "position", + "additionalProperties": false + }, + "orientation": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "w": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "title": "orientation", + "additionalProperties": false + } + }, + "required": [ + "position", + "orientation" + ], + "title": "pose", + "additionalProperties": false + }, + "config": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "title": "resource" + }, + "type": "array" + }, + "target_device": { + "type": "string", + "description": "device reference" + }, + "mount_resource": { + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "sample_id": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "parent": { + "type": "string" + }, + "type": { + "type": "string" + }, + "category": { + "type": "string" + }, + "pose": { + "type": "object", + "properties": { + "position": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z" + ], + "title": "position", + "additionalProperties": false + }, + "orientation": { + "type": "object", + "properties": { + "x": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "y": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "z": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + }, + "w": { + "type": "number", + "minimum": -1.7976931348623157e+308, + "maximum": 1.7976931348623157e+308 + } + }, + "required": [ + "x", + "y", + "z", + "w" + ], + "title": "orientation", + "additionalProperties": false + } + }, + "required": [ + "position", + "orientation" + ], + "title": "pose", + "additionalProperties": false + }, + "config": { + "type": "string" + }, + "data": { + "type": "string" + } + }, + "title": "mount_resource" + }, + "type": "array" + } + }, + "required": [ + "resource", + "target_device", + "mount_resource" + ], + "_unilabos_placeholder_info": { + "resource": "unilabos_resources", + "target_device": "unilabos_devices", + "mount_resource": "unilabos_resources" + } + }, + "goal_default": {}, + "placeholder_keys": { + "resource": "unilabos_resources", + "target_device": "unilabos_devices", + "mount_resource": "unilabos_resources" + } +} \ No newline at end of file