Files
Uni-Lab-OS/docs/developer_guide/add_yaml.md
2025-10-11 03:11:17 +08:00

611 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# yaml 注册表编写指南
## 快速开始:使用注册表编辑器
推荐使用 UniLabOS 自带的可视化编辑器,它能帮你自动生成大部分配置,省去手写的麻烦。
### 怎么用编辑器
1. 启动 UniLabOS
2. 在浏览器中打开"注册表编辑器"页面
3. 选择你的 Python 设备驱动文件
4. 点击"分析文件",让系统读取你的类信息
5. 填写一些基本信息(设备描述、图标啥的)
6. 点击"生成注册表",复制生成的内容
7. 把内容保存到 `devices/` 目录下
我们为你准备了一个测试驱动用于在界面上尝试注册表生成参见目录test\registry\example_devices.py
---
## 手动编写指南
如果你想自己写 yaml 文件,或者想深入了解结构,查阅下方说明。
## 注册表的基本结构
yaml 注册表就是设备的配置文件,里面定义了设备怎么用、有什么功能。好消息是系统会自动帮你填大部分内容,你只需要写两个必需的东西:设备名和 class 信息。
### 各字段用途
| 字段名 | 类型 | 需要手写 | 说明 |
| ----------------- | ------ | -------- | ----------------------------------- |
| 设备标识符 | string | 是 | 设备的唯一名字,比如 `mock_chiller` |
| class | object | 部分 | 设备的核心信息,必须写 |
| description | string | 否 | 设备描述,系统默认给空字符串 |
| handles | array | 否 | 连接关系,默认是空的 |
| icon | string | 否 | 图标路径,默认为空 |
| init_param_schema | object | 否 | 初始化参数,系统自动分析生成 |
| version | string | 否 | 版本号,默认 "1.0.0" |
| category | array | 否 | 设备分类,默认用文件名 |
| config_info | array | 否 | 嵌套配置,默认为空 |
| file_path | string | 否 | 文件路径,系统自动设置 |
| registry_type | string | 否 | 注册表类型,自动设为 "device" |
### class 字段里有啥
class 是核心部分,包含这些内容:
| 字段名 | 类型 | 需要手写 | 说明 |
| --------------------- | ------ | -------- | ---------------------------------- |
| module | string | 是 | Python 类的路径,必须写 |
| type | string | 是 | 驱动类型,一般写 "python" |
| status_types | object | 否 | 状态类型,系统自动分析生成 |
| action_value_mappings | object | 部分 | 动作配置,系统会自动生成一些基础的 |
## 怎么创建新的注册表
### 创建文件
在 devices 文件夹里新建一个 yaml 文件,比如 `new_device.yaml`
### 完整结构是什么样的
```yaml
new_device: # 设备名,要唯一
class: # 核心配置
action_value_mappings: # 动作配置(后面会详细说)
action_name:
# 具体的动作设置
module: unilabos.devices.your_module.new_device:NewDeviceClass # 你的 Python 类
status_types: # 状态类型(系统会自动生成)
status: str
temperature: float
# 其他状态
type: python # 驱动类型,一般就是 python
description: New Device Description # 设备描述
handles: [] # 连接关系,通常是空的
icon: '' # 图标路径
init_param_schema: # 初始化参数(系统会自动生成)
config: # 初始化时需要的参数
properties:
port:
default: DEFAULT_PORT
type: string
required: []
type: object
data: # 前端显示用的数据类型
properties:
status:
type: string
temperature:
type: number
required:
- status
type: object
version: 0.0.1 # 版本号
category:
- device_category # 设备类别
config_info: [] # 嵌套配置,通常为空
```
## action_value_mappings 怎么写
这个部分定义设备能做哪些动作。好消息是系统会自动生成大部分动作,你通常只需要添加一些特殊的自定义动作。
### 系统自动生成哪些动作
系统会帮你生成这些:
1.`auto-` 开头的动作:从你 Python 类的方法自动生成
2. 通用的驱动动作:
- `_execute_driver_command`:同步执行驱动命令(仅本地可用)
- `_execute_driver_command_async`:异步执行驱动命令(仅本地可用)
### 如果要手动定义动作
如果你需要自定义一些特殊动作,需要这些字段:
| 字段名 | 需要手写 | 说明 |
| ---------------- | -------- | -------------------------------- |
| type | 是 | 动作类型,必须指定 |
| goal | 是 | 输入参数怎么映射 |
| feedback | 否 | 实时反馈,通常为空 |
| result | 是 | 结果怎么返回 |
| goal_default | 部分 | 参数默认值ROS 动作会自动生成 |
| schema | 部分 | 前端表单配置ROS 动作会自动生成 |
| handles | 否 | 连接关系,默认为空 |
| placeholder_keys | 否 | 特殊输入字段配置 |
### 动作类型有哪些
| 类型 | 什么时候用 | 系统会自动生成什么 |
| ---------------------- | -------------------- | ---------------------- |
| UniLabJsonCommand | 自定义同步 JSON 命令 | 啥都不生成 |
| UniLabJsonCommandAsync | 自定义异步 JSON 命令 | 啥都不生成 |
| ROS 动作类型 | 标准 ROS 动作 | goal_default 和 schema |
常用的 ROS 动作类型:
- `SendCmd`:发送简单命令
- `NavigateThroughPoses`:导航动作
- `SingleJointPosition`:单关节位置控制
- `Stir`:搅拌动作
- `HeatChill``HeatChillStart`:加热冷却动作
### 复杂一点的例子
```yaml
heat_chill_start:
type: HeatChillStart
goal:
purpose: purpose
temp: temp
goal_default: # ROS动作会自动生成你也可以手动覆盖
purpose: ''
temp: 0.0
handles:
output:
- handler_key: labware
label: Labware
data_type: resource
data_source: handle
data_key: liquid
placeholder_keys:
purpose: unilabos_resources
result:
status: status
success: success
# schema 系统会自动生成,不用写
```
### 动作名字怎么起
根据设备用途来起名字:
- 启动停止类:`start``stop``pause``resume`
- 设置参数类:`set_speed``set_temperature``set_timer`
- 移动控制类:`move_to_position``move_through_points`
- 功能操作类:`stir``heat_chill_start``heat_chill_stop`
- 开关控制类:`valve_open_cmd``valve_close_cmd``push_to`
- 命令执行类:`send_nav_task``execute_command_from_outer`
### 常用的动作类型
- `UniLabJsonCommand`:自定义 JSON 命令(不走 ROS
- `UniLabJsonCommandAsync`:异步 JSON 命令(不走 ROS
- `SendCmd`:发送简单命令
- `NavigateThroughPoses`:导航相关
- `SingleJointPosition`:单关节控制
- `Stir`:搅拌
- `HeatChill``HeatChillStart`:加热冷却
- 其他的 ROS 动作类型:看具体的 ROS 服务
### 示例:完整的动作配置
```yaml
heat_chill_start:
type: HeatChillStart
goal:
purpose: purpose
temp: temp
goal_default:
purpose: ''
temp: 0.0
handles:
output:
- handler_key: labware
label: Labware
data_type: resource
data_source: handle
data_key: liquid
placeholder_keys:
purpose: unilabos_resources
result:
status: status
success: success
schema:
description: '启动加热冷却功能'
properties:
goal:
properties:
purpose:
type: string
description: '用途说明'
temp:
type: number
description: '目标温度'
required:
- purpose
- temp
title: HeatChillStart_Goal
type: object
required:
- goal
title: HeatChillStart
type: object
feedback: {}
```
## 系统自动生成的字段
### status_types
系统会扫描你的 Python 类,从状态方法自动生成这部分:
```yaml
status_types:
current_temperature: float # 从 get_current_temperature() 方法来的
is_heating: bool # 从 get_is_heating() 方法来的
status: str # 从 get_status() 方法来的
```
注意几点:
- 系统会找所有 `get_` 开头的方法
- 类型会自动转成 ROS 类型(比如 `str` 变成 `String`
- 如果类型是 `Any``None` 或者不知道的,就默认用 `String`
### init_param_schema
这个完全是系统自动生成的,你不用管:
```yaml
init_param_schema:
config: # 从你类的 __init__ 方法分析出来的
properties:
port:
type: string
default: '/dev/ttyUSB0'
baudrate:
type: integer
default: 9600
required: []
type: object
data: # 根据 status_types 生成的前端用的类型
properties:
current_temperature:
type: number
is_heating:
type: boolean
status:
type: string
required:
- status
type: object
```
生成规则很简单:
- `config` 部分:看你类的 `__init__` 方法有什么参数,类型和默认值是啥
- `data` 部分:根据 `status_types` 生成前端显示用的类型定义
### 其他自动填充的字段
```yaml
version: '1.0.0' # 默认版本
category: ['文件名'] # 用你的 yaml 文件名当类别
description: '' # 默认为空,你可以手动改
icon: '' # 默认为空,你可以加图标
handles: [] # 默认空数组
config_info: [] # 默认空数组
file_path: '/path/to/file' # 系统自动填文件路径
registry_type: 'device' # 自动设为设备类型
```
### handles 字段
这个是定义设备连接关系的,类似动作里的 handles 一样:
```yaml
handles: # 大多数时候都是空的,除非设备本身需要连接啥
- handler_key: device_output
label: Device Output
data_type: resource
data_source: value
data_key: default_value
```
### 其他可以配置的字段
```yaml
description: '设备的详细描述' # 写清楚设备是干啥的
icon: 'device_icon.webp' # 设备图标文件名会上传到OSS
version: '0.0.1' # 版本号
category: # 设备分类,前端会用这个分组
- 'heating'
- 'cooling'
- 'temperature_control'
config_info: # 嵌套配置,如果设备包含子设备
- children:
- opentrons_24_tuberack_nest_1point5ml_snapcap_A1
- other_nested_component
```
## 完整的例子
这里是一个比较完整的设备配置示例:
```yaml
my_temperature_controller:
class:
action_value_mappings:
heat_start:
type: HeatChillStart
goal:
target_temp: temp
vessel: vessel
goal_default:
target_temp: 25.0
vessel: ''
handles:
output:
- handler_key: heated_sample
label: Heated Sample
data_type: resource
data_source: handle
data_key: sample
placeholder_keys:
vessel: unilabos_resources
result:
status: status
success: success
schema:
description: '启动加热功能'
properties:
goal:
properties:
target_temp:
type: number
description: '目标温度'
vessel:
type: string
description: '容器标识'
required:
- target_temp
- vessel
title: HeatStart_Goal
type: object
required:
- goal
title: HeatStart
type: object
feedback: {}
stop:
type: UniLabJsonCommand
goal: {}
goal_default: {}
handles: {}
result:
status: status
schema:
description: '停止设备'
properties:
goal:
type: object
title: Stop_Goal
title: Stop
type: object
feedback: {}
module: unilabos.devices.temperature.my_controller:MyTemperatureController
status_types:
current_temperature: float
target_temperature: float
is_heating: bool
is_cooling: bool
status: str
vessel: str
type: python
description: '我的温度控制器设备'
handles: []
icon: 'temperature_controller.webp'
init_param_schema:
config:
properties:
port:
default: '/dev/ttyUSB0'
type: string
baudrate:
default: 9600
type: number
required: []
type: object
data:
properties:
current_temperature:
type: number
target_temperature:
type: number
is_heating:
type: boolean
is_cooling:
type: boolean
status:
type: string
vessel:
type: string
required:
- current_temperature
- target_temperature
- status
type: object
version: '1.0.0'
category:
- 'temperature_control'
- 'heating'
config_info: []
```
## 怎么部署和使用
### 方法一:用编辑器(推荐)
1. 先写好你的 Python 驱动类
2. 用注册表编辑器自动生成 yaml 配置
3. 把生成的文件保存到 `devices/` 目录
4. 重启 UniLabOS 就能用了
### 方法二:手动写(简化版)
1. 创建最简配置:
```yaml
# devices/my_device.yaml
my_device:
class:
module: unilabos.devices.my_module.my_device:MyDevice
type: python
```
2. 启动系统时用 `complete_registry=True` 参数,让系统自动补全
3. 检查一下生成的配置是不是你想要的
### Python 驱动类要怎么写
你的设备类要符合这些要求:
```python
from unilabos.common.device_base import DeviceBase
class MyDevice(DeviceBase):
def __init__(self, config):
"""初始化,参数会自动分析到 init_param_schema.config"""
super().__init__(config)
self.port = config.get('port', '/dev/ttyUSB0')
# 状态方法(会自动生成到 status_types
def get_status(self):
"""返回设备状态"""
return "idle"
def get_temperature(self):
"""返回当前温度"""
return 25.0
# 动作方法(会自动生成 auto- 开头的动作)
async def start_heating(self, temperature: float):
"""开始加热到指定温度"""
pass
def stop(self):
"""停止操作"""
pass
```
### 系统集成
1. 把 yaml 文件放到 `devices/` 目录下
2. 系统启动时会自动扫描并加载设备
3. 系统会自动补全所有缺失的字段
4. 设备马上就能在前端界面中使用
### 高级配置
如果需要特殊设置,可以手动加:
```yaml
my_device:
class:
module: unilabos.devices.my_module.my_device:MyDevice
type: python
action_value_mappings:
# 自定义动作
special_command:
type: UniLabJsonCommand
goal: {}
result: {}
# 可选的自定义配置
description: '我的特殊设备'
icon: 'my_device.webp'
category: ['temperature', 'heating']
```
## 常见问题怎么排查
### 设备加载不了
1. 检查模块路径:确认 `class.module` 路径写对了
2. 确认类能导入:看看你的 Python 驱动类能不能正常导入
3. 检查语法:用 yaml 验证器看看文件格式对不对
4. 查看日志:看 UniLabOS 启动时有没有报错信息
### 自动生成失败了
1. 类分析出问题:确认你的类继承了正确的基类
2. 方法类型不明确:确保状态方法的返回类型写清楚了
3. 导入有问题:检查类能不能被动态导入
4. 没开完整注册:确认启用了 `complete_registry=True`
### 前端显示有问题
1. 重新生成:删掉旧的 yaml 文件,用编辑器重新生成
2. 清除缓存:清除浏览器缓存,重新加载页面
3. 检查字段:确认必需的字段(比如 `schema`)都有
4. 验证数据:检查 `goal_default``schema` 的数据类型是不是一致
### 动作执行出错
1. 方法名不对:确认动作方法名符合规范(比如 `execute_<action_name>`
2. 参数映射错误:检查 `goal` 字段的参数映射是否正确
3. 返回格式不对:确认方法返回值格式符合 `result` 映射
4. 没异常处理:在驱动类里加上异常处理
## 最佳实践
### 开发流程
1. **优先使用编辑器**:除非有特殊需求,否则优先使用注册表编辑器
2. **最小化配置**:手动配置时只定义必要字段,让系统自动生成其他内容
3. **增量开发**:先创建基本配置,后续根据需要添加特殊动作
### 代码规范
1. **方法命名**:状态方法使用 `get_` 前缀,动作方法使用动词开头
2. **类型注解**:为方法参数和返回值添加类型注解
3. **文档字符串**:为类和方法添加详细的文档字符串
4. **异常处理**:实现完善的错误处理和日志记录
### 配置管理
1. **版本控制**:所有 yaml 文件纳入版本控制
2. **命名一致性**:设备 ID、文件名、类名保持一致的命名风格
3. **定期更新**:定期运行完整注册以更新自动生成的字段
4. **备份配置**:在修改前备份重要的手动配置
### 测试验证
1. **本地测试**:在本地环境充分测试后再部署
2. **渐进部署**:先部署到测试环境,验证无误后再上生产环境
3. **监控日志**:密切监控设备加载和运行日志
4. **回滚准备**:准备快速回滚机制,以应对紧急情况
### 性能优化
1. **按需加载**:只加载实际使用的设备类型
2. **缓存利用**:充分利用系统的注册表缓存机制
3. **资源管理**:合理管理设备连接和资源占用
4. **监控指标**:设置关键性能指标的监控和告警