Files
Uni-Lab-OS/unilabos/resources/bioyond/README_WAREHOUSE.md
Xuwznln 2a5ddd611d Upgrade to py 3.11.14; ROS2 Humble 0.7; unilabos 0.10.16
Workbench example, adjust log level, and ci check (#220)

* TestLatency Return Value Example & gitignore update

* Adjust log level & Add workbench virtual example & Add not action decorator & Add check_mode &

* Add CI Check

Fix/workstation yb revision (#217)

* Revert log change & update registry

* Revert opcua client & move electrolyte node

Workstation yb merge dev ready 260113 (#216)

* feat(bioyond): 添加计算实验设计功能,支持化合物配比和滴定比例参数

* feat(bioyond): 添加测量小瓶功能,支持基本参数配置

* feat(bioyond): 添加测量小瓶配置,支持新设备参数

* feat(bioyond): 更新仓库布局和尺寸,支持竖向排列的测量小瓶和试剂存放堆栈

* feat(bioyond): 优化任务创建流程,确保无论成功与否都清理任务队列以避免重复累积

* feat(bioyond): 添加设置反应器温度功能,支持温度范围和异常处理

* feat(bioyond): 调整反应器位置配置,统一坐标格式

* feat(bioyond): 添加调度器启动功能,支持任务队列执行并处理异常

* feat(bioyond): 优化调度器启动功能,添加异常处理并更新相关配置

* feat(opcua): 增强节点ID解析兼容性和数据类型处理

改进节点ID解析逻辑以支持多种格式,包括字符串和数字标识符
添加数据类型转换处理,确保写入值时类型匹配
优化错误提示信息,便于调试节点连接问题

* feat(registry): 新增后处理站的设备配置文件

添加后处理站的YAML配置文件,包含动作映射、状态类型和设备描述

* 添加调度器启动功能,合并物料参数配置,优化物料参数处理逻辑

* 添加从 Bioyond 系统自动同步工作流序列的功能,并更新相关配置

* fix:兼容 BioyondReactionStation 中 workflow_sequence 被重写为 property

* fix:同步工作流序列

* feat: remove commented workflow synchronization from `reaction_station.py`.

* 添加时间约束功能及相关配置

* fix:自动更新物料缓存功能,添加物料时更新缓存并在删除时移除缓存项

* fix:在添加物料时处理字符串和字典返回值,确保正确更新缓存

* fix:更新奔曜错误处理报送为物料变更报送,调整日志记录和响应消息

* feat:添加实验报告简化功能,去除冗余信息并保留关键信息

* feat: 添加任务状态事件发布功能,监控并报告任务运行、超时、完成和错误状态

* fix: 修复添加物料时数据格式错误

* Refactor bioyond_dispensing_station and reaction_station_bioyond YAML configurations

- Removed redundant action value mappings from bioyond_dispensing_station.
- Updated goal properties in bioyond_dispensing_station to use enums for target_stack and other parameters.
- Changed data types for end_point and start_point in reaction_station_bioyond to use string enums (Start, End).
- Simplified descriptions and updated measurement units from μL to mL where applicable.
- Removed unused commands from reaction_station_bioyond to streamline the configuration.

* fix:Change the material unit from μL to mL

* fix:refresh_material_cache

* feat: 动态获取工作流步骤ID,优化工作流配置

* feat: 添加清空服务端所有非核心工作流功能

* fix:修复Bottle类的序列化和反序列化方法

* feat:增强材料缓存更新逻辑,支持处理返回数据中的详细信息

* Add debug log

* feat(workstation): update bioyond config migration and coin cell material search logic

- Migrate bioyond_cell config to JSON structure and remove global variable dependencies
- Implement material search confirmation dialog auto-handling
- Add documentation: 20260113_物料搜寻确认弹窗自动处理功能.md and 20260113_配置迁移修改总结.md

* Refactor module paths for Bioyond devices in YAML configuration files

- Updated the module path for BioyondDispensingStation in bioyond_dispensing_station.yaml to reflect the new directory structure.
- Updated the module path for BioyondReactionStation and BioyondReactor in reaction_station_bioyond.yaml to align with the revised organization of the codebase.

* fix: WareHouse 的不可哈希类型错误,优化父节点去重逻辑

* refactor: Move config from module to instance initialization

* fix: 修正 reaction_station 目录名拼写错误

* feat: Integrate material search logic and cleanup deprecated files

- Update coin_cell_assembly.py with material search dialog handling
- Update YB_warehouses.py with latest warehouse configurations
- Remove outdated documentation and test data files

* Refactor: Use instance attributes for action names and workflow step IDs

* refactor: Split tipbox storage into left and right warehouses

* refactor: Merge tipbox storage left and right into single warehouse

---------

Co-authored-by: ZiWei <131428629+ZiWei09@users.noreply.github.com>
Co-authored-by: Andy6M <xieqiming1132@qq.com>

fix: WareHouse 的不可哈希类型错误,优化父节点去重逻辑

fix parent_uuid fetch when bind_parent_id == node_name

物料更新也是用父节点进行报送

Add None conversion for tube rack etc.

Add set_liquid example.

Add create_resource and test_resource example.

Add restart.
Temp allow action message.

Add no_update_feedback option.

Create session_id by edge.

bump version to 0.10.15

temp cancel update req
2026-01-27 15:21:55 +08:00

549 lines
17 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

# Bioyond 仓库系统开发指南
本文档详细说明 Bioyond 仓库Warehouse系统的架构、配置和使用方法帮助开发者快速理解和维护仓库相关代码。
## 📚 目录
- [系统架构](#系统架构)
- [核心概念](#核心概念)
- [三层映射关系](#三层映射关系)
- [warehouse_factory 详解](#warehouse_factory-详解)
- [创建新仓库](#创建新仓库)
- [常见问题](#常见问题)
- [调试技巧](#调试技巧)
---
## 系统架构
Bioyond 仓库系统采用**三层架构**,实现从前端显示到后端 API 的完整映射:
```
┌─────────────────────────────────────────────────────────┐
│ 前端显示层 (YB_warehouses.py) │
│ - warehouse_factory 自动生成库位网格 │
│ - 生成库位名称A01, B02, C03... │
│ - 存储在 WareHouse.sites 字典中 │
└────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ Deck 布局层 (decks.py) │
│ - 定义仓库在 Deck 上的物理位置 │
│ - 组织多个仓库形成完整布局 │
└────────────────┬────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ UUID 映射层 (config.py) │
│ - 将库位名称映射到 Bioyond 系统 UUID │
│ - 用于 API 调用时的物料入库操作 │
└─────────────────────────────────────────────────────────┘
```
---
## 核心概念
### 仓库Warehouse
仓库是一个**三维网格**,用于存放物料。由以下参数定义:
- **num_items_x**: 列数X 轴)
- **num_items_y**: 行数Y 轴)
- **num_items_z**: 层数Z 轴)
例如:`5行×3列×1层` = 5×3×1 = 15个库位
### 库位Site
库位是仓库中的单个存储位置,由**字母行+数字列**命名:
- **字母行**A, B, C, D, E, F...(对应 Y 轴)
- **数字列**01, 02, 03, 04...(对应 X 轴或 Z 轴)
示例:`A01`, `B02`, `C03`
### 布局模式Layout
控制库位的排序和 Y 坐标计算:
| 模式 | 说明 | 生成顺序 | Y 坐标计算 | 显示效果 |
|------|------|----------|-----------|---------|
| `col-major` | 列优先(默认) | A01, B01, C01, A02... | `dy + (num_y - row - 1) * item_dy` | A 可能在下 |
| `row-major` | 行优先 | A01, A02, A03, B01... | `dy + row * item_dy` | **A 在上** ✓ |
**重要:** 使用 `row-major` 可以避免上下颠倒问题!
---
## 三层映射关系
### 示例手动传递窗右A01-E03
#### 1⃣ 前端显示层 - [`YB_warehouses.py`](YB_warehouses.py)
```python
def bioyond_warehouse_5x3x1(name: str, row_offset: int = 0) -> WareHouse:
"""创建 5行×3列×1层 仓库"""
return warehouse_factory(
name=name,
num_items_x=3, # 3列
num_items_y=5, # 5行
num_items_z=1, # 1层
row_offset=row_offset,
layout="row-major",
)
```
**自动生成的库位:** A01, A02, A03, B01, B02, B03, ..., E01, E02, E03
#### 2⃣ Deck 布局层 - [`decks.py`](decks.py)
```python
self.warehouses = {
"手动传递窗右": bioyond_warehouse_5x3x1("手动传递窗右", row_offset=0),
}
self.warehouse_locations = {
"手动传递窗右": Coordinate(4160.0, 877.0, 0.0),
}
```
**作用:**
- 创建仓库实例
- 设置在 Deck 上的物理坐标
#### 3⃣ UUID 映射层 - [`config.py`](../../devices/workstation/bioyond_studio/config.py)
```python
WAREHOUSE_MAPPING = {
"手动传递窗右": {
"uuid": "",
"site_uuids": {
"A01": "3a19deae-2c7a-36f5-5e41-02c5b66feaea",
"A02": "3a19deae-2c7a-dc6d-c41e-ef285d946cfe",
# ... 其他库位
}
}
}
```
**作用:**
- 用户拖拽物料到"手动传递窗右"的"A01"位置时
- 系统查找 `WAREHOUSE_MAPPING["手动传递窗右"]["site_uuids"]["A01"]`
- 获取 UUID `"3a19deae-2c7a-36f5-5e41-02c5b66feaea"`
- 调用 Bioyond API 将物料入库到该 UUID 位置
---
## 实际配置案例
### 案例:手动传递窗左/右的完整配置
本案例展示如何为"手动传递窗右"和"手动传递窗左"建立完整的三层映射。
#### 背景需求
- **手动传递窗右**: 需要 A01-E035行×3列=15个库位
- **手动传递窗左**: 需要 F01-J035行×3列=15个库位
- 这两个仓库共享同一个物理堆栈的 UUID"手动堆栈"
#### 实施步骤
**1⃣ 修复前端布局** - [`YB_warehouses.py`](YB_warehouses.py)
```python
# 创建新的 5×3×1 仓库函数(之前是错误的 1×3×3
def bioyond_warehouse_5x3x1(name: str, row_offset: int = 0) -> WareHouse:
"""创建5行×3列×1层仓库支持行偏移生成不同字母行"""
return warehouse_factory(
name=name,
num_items_x=3, # 3列
num_items_y=5, # 5行 ← 修正
num_items_z=1, # 1层 ← 修正
row_offset=row_offset, # ← 支持 F-J 行
layout="row-major", # ← 避免上下颠倒
)
```
**2⃣ 更新 Deck 配置** - [`decks.py`](decks.py)
```python
from unilabos.resources.bioyond.YB_warehouses import (
bioyond_warehouse_5x3x1, # 新增导入
)
class BIOYOND_YB_Deck(Deck):
def setup(self) -> None:
self.warehouses = {
# 修改前: bioyond_warehouse_1x3x3 (错误尺寸)
# 修改后: bioyond_warehouse_5x3x1 (正确尺寸)
"手动传递窗右": bioyond_warehouse_5x3x1("手动传递窗右", row_offset=0), # A01-E03
"手动传递窗左": bioyond_warehouse_5x3x1("手动传递窗左", row_offset=5), # F01-J03
}
```
**3⃣ 添加 UUID 映射** - [`config.py`](../../devices/workstation/bioyond_studio/config.py)
```python
WAREHOUSE_MAPPING = {
# 保持原有的"手动堆栈"配置不变A01-J03共30个库位
"手动堆栈": {
"uuid": "",
"site_uuids": {
"A01": "3a19deae-2c7a-36f5-5e41-02c5b66feaea",
# ... A02-E03 共15个
"F01": "3a19deae-2c7a-d594-fd6a-0d20de3c7c4a",
# ... F02-J03 共15个
}
},
# [新增] 手动传递窗右 - 复用"手动堆栈"的 A01-E03 UUID
"手动传递窗右": {
"uuid": "",
"site_uuids": {
"A01": "3a19deae-2c7a-36f5-5e41-02c5b66feaea", # ← 与手动堆栈A01相同
"A02": "3a19deae-2c7a-dc6d-c41e-ef285d946cfe",
"A03": "3a19deae-2c7a-5876-c454-6b7e224ca927",
"B01": "3a19deae-2c7a-2426-6d71-e9de3cb158b1",
"B02": "3a19deae-2c7a-79b0-5e44-efaafd1e4cf3",
"B03": "3a19deae-2c7a-b9eb-f4e3-e308e0cf839a",
"C01": "3a19deae-2c7a-32bc-768e-556647e292f3",
"C02": "3a19deae-2c7a-e97a-8484-f5a4599447c4",
"C03": "3a19deae-2c7a-3056-6504-10dc73fbc276",
"D01": "3a19deae-2c7a-ffad-875e-8c4cda61d440",
"D02": "3a19deae-2c7a-61be-601c-b6fb5610499a",
"D03": "3a19deae-2c7a-c0f7-05a7-e3fe2491e560",
"E01": "3a19deae-2c7a-a6f4-edd1-b436a7576363",
"E02": "3a19deae-2c7a-4367-96dd-1ca2186f4910",
"E03": "3a19deae-2c7a-b163-2219-23df15200311",
}
},
# [新增] 手动传递窗左 - 复用"手动堆栈"的 F01-J03 UUID
"手动传递窗左": {
"uuid": "",
"site_uuids": {
"F01": "3a19deae-2c7a-d594-fd6a-0d20de3c7c4a", # ← 与手动堆栈F01相同
"F02": "3a19deae-2c7a-a194-ea63-8b342b8d8679",
"F03": "3a19deae-2c7a-f7c4-12bd-425799425698",
"G01": "3a19deae-2c7a-0b56-72f1-8ab86e53b955",
"G02": "3a19deae-2c7a-204e-95ed-1f1950f28343",
"G03": "3a19deae-2c7a-392b-62f1-4907c66343f8",
"H01": "3a19deae-2c7a-5602-e876-d27aca4e3201",
"H02": "3a19deae-2c7a-f15c-70e0-25b58a8c9702",
"H03": "3a19deae-2c7a-780b-8965-2e1345f7e834",
"I01": "3a19deae-2c7a-8849-e172-07de14ede928",
"I02": "3a19deae-2c7a-4772-a37f-ff99270bafc0",
"I03": "3a19deae-2c7a-cce7-6e4a-25ea4a2068c4",
"J01": "3a19deae-2c7a-1848-de92-b5d5ed054cc6",
"J02": "3a19deae-2c7a-1d45-b4f8-6f866530e205",
"J03": "3a19deae-2c7a-f237-89d9-8fe19025dee9"
}
},
}
```
#### 关键要点
1. **UUID 可以复用**: 三个仓库(手动堆栈、手动传递窗右、手动传递窗左)可以共享相同的物理库位 UUID
2. **库位名称必须匹配**: 前端生成的库位名称(如 F01必须与 config.py 中的键名完全一致
3. **row_offset 的妙用**:
- `row_offset=0` → 生成 A-E 行
- `row_offset=5` → 生成 F-J 行跳过前5个字母
#### 验证结果
配置完成后,拖拽测试:
| 拖拽位置 | 前端库位 | 查找路径 | UUID | 结果 |
|---------|---------|---------|------|------|
| 手动传递窗右/A01 | A01 | `WAREHOUSE_MAPPING["手动传递窗右"]["site_uuids"]["A01"]` | `3a19...eaea` | ✅ 正确入库 |
| 手动传递窗左/F01 | F01 | `WAREHOUSE_MAPPING["手动传递窗左"]["site_uuids"]["F01"]` | `3a19...c4a` | ✅ 正确入库 |
| 手动堆栈/A01 | A01 | `WAREHOUSE_MAPPING["手动堆栈"]["site_uuids"]["A01"]` | `3a19...eaea` | ✅ 仍然正常 |
---
## warehouse_factory 详解
### 函数签名
```python
def warehouse_factory(
name: str,
num_items_x: int = 1, # 列数
num_items_y: int = 4, # 行数
num_items_z: int = 4, # 层数
dx: float = 137.0, # X 起始偏移
dy: float = 96.0, # Y 起始偏移
dz: float = 120.0, # Z 起始偏移
item_dx: float = 10.0, # X 间距
item_dy: float = 10.0, # Y 间距
item_dz: float = 10.0, # Z 间距
col_offset: int = 0, # 列偏移(影响数字)
row_offset: int = 0, # 行偏移(影响字母)
layout: str = "col-major", # 布局模式
) -> WareHouse:
```
### 参数说明
#### 尺寸参数
- **num_items_x, y, z**: 定义仓库的网格尺寸
- **注意**: 当 `num_items_z > 1`Z 轴会被映射为数字列
#### 位置参数
- **dx, dy, dz**: 第一个库位的起始坐标
- **item_dx, dy, dz**: 库位之间的间距
#### 偏移参数
- **col_offset**: 列起始偏移,用于生成 A05-D08 等命名
```python
col_offset=4 # 生成 A05, A06, A07, A08
```
- **row_offset**: 行起始偏移,用于生成 F01-J03 等命名
```python
row_offset=5 # 生成 F01, F02, F03跳过 A-E
```
#### 布局参数
- **layout**:
- `"col-major"`: 列优先(默认),可能导致上下颠倒
- `"row-major"`: 行优先,**推荐使用**A 显示在上
### 库位生成逻辑
```python
# row-major 模式(推荐)
keys = [f"{LETTERS[j + row_offset]}{i + 1 + col_offset:02d}"
for j in range(num_y)
for i in range(num_x)]
# 示例num_y=2, num_x=3, row_offset=0, col_offset=0
# 生成A01, A02, A03, B01, B02, B03
```
### Y 坐标计算
```python
if layout == "row-major":
# A 在上Y 较小)
y = dy + row * item_dy
else:
# A 在下Y 较大)- 不推荐
y = dy + (num_items_y - row - 1) * item_dy
```
---
## 创建新仓库
### 步骤 1: 在 YB_warehouses.py 中创建函数
```python
def bioyond_warehouse_3x4x1(name: str) -> WareHouse:
"""创建 3行×4列×1层 仓库
布局:
A01 | A02 | A03 | A04
B01 | B02 | B03 | B04
C01 | C02 | C03 | C04
"""
return warehouse_factory(
name=name,
num_items_x=4, # 4列
num_items_y=3, # 3行
num_items_z=1, # 1层
dx=10.0,
dy=10.0,
dz=10.0,
item_dx=137.0,
item_dy=120.0,
item_dz=120.0,
category="warehouse",
layout="row-major", # ⭐ 推荐使用
)
```
### 步骤 2: 在 decks.py 中使用
```python
# 1. 导入函数
from unilabos.resources.bioyond.YB_warehouses import (
bioyond_warehouse_3x4x1, # 新增
)
# 2. 在 setup() 中添加
self.warehouses = {
"我的新仓库": bioyond_warehouse_3x4x1("我的新仓库"),
}
self.warehouse_locations = {
"我的新仓库": Coordinate(100.0, 200.0, 0.0),
}
```
### 步骤 3: 在 config.py 中配置 UUID可选
```python
WAREHOUSE_MAPPING = {
"我的新仓库": {
"uuid": "",
"site_uuids": {
"A01": "从 Bioyond 系统获取的 UUID",
"A02": "从 Bioyond 系统获取的 UUID",
# ... 其他 11 个库位
}
}
}
```
**注意:** 如果不需要拖拽入库功能,可跳过此步骤。
---
## 常见问题
### Q1: 为什么库位显示上下颠倒C 在上A 在下)?
**原因:** 使用了默认的 `col-major` 布局。
**解决:** 在 `warehouse_factory` 中添加 `layout="row-major"`
```python
return warehouse_factory(
...
layout="row-major", # ← 添加这行
)
```
### Q2: 我需要 1×3×3 还是 3×3×1
**判断方法:**
- **1×3×3**: 1列×3行×3**层**(垂直堆叠,有高度)
- **3×3×1**: 3行×3列×1**层**(平面网格)
**推荐:** 大多数情况使用 `X×Y×1`(平面网格)更直观。
### Q3: 如何生成 F01-J03 而非 A01-E03
**方法:** 使用 `row_offset` 参数
```python
bioyond_warehouse_5x3x1("仓库名", row_offset=5)
# row_offset=5 跳过 A-E从 F 开始
```
### Q4: 拖拽物料后找不到 UUID 怎么办?
**检查清单:**
1. `config.py` 中是否有该仓库的配置?
2. 仓库名称是否完全匹配?
3. 库位名称(如 A01是否在 `site_uuids` 中?
**示例错误:**
```python
# decks.py
"手动传递窗右": bioyond_warehouse_5x3x1(...)
# config.py - ❌ 名称不匹配
"手动传递窗": { ... } # 缺少"右"字
```
### Q5: 库位重叠怎么办?
**原因:** 间距(`item_dx/dy/dz`)太小。
**解决:** 增大间距参数
```python
item_dx=150.0, # 增大 X 间距
item_dy=130.0, # 增大 Y 间距
```
---
## 调试技巧
### 1. 查看生成的库位
```python
warehouse = bioyond_warehouse_5x3x1("测试仓库")
print(list(warehouse.sites.keys()))
# 输出:['A01', 'A02', 'A03', 'B01', 'B02', ...]
```
### 2. 检查库位坐标
```python
for name, site in warehouse.sites.items():
print(f"{name}: {site.location}")
# 输出:
# A01: Coordinate(x=10.0, y=10.0, z=120.0)
# A02: Coordinate(x=147.0, y=10.0, z=120.0)
# ...
```
### 3. 验证 UUID 映射
```python
from unilabos.devices.workstation.bioyond_studio.config import WAREHOUSE_MAPPING
warehouse_name = "手动传递窗右"
location_code = "A01"
if warehouse_name in WAREHOUSE_MAPPING:
uuid = WAREHOUSE_MAPPING[warehouse_name]["site_uuids"].get(location_code)
print(f"{warehouse_name}/{location_code} → {uuid}")
else:
print(f"❌ 未找到仓库: {warehouse_name}")
```
---
## 文件关系图
```
unilabos/
├── resources/
│ ├── warehouse.py # warehouse_factory 核心实现
│ └── bioyond/
│ ├── YB_warehouses.py # ⭐ 仓库函数定义
│ ├── decks.py # ⭐ Deck 布局配置
│ └── README_WAREHOUSE.md # 📖 本文档
└── devices/
└── workstation/
└── bioyond_studio/
├── config.py # ⭐ UUID 映射配置
└── bioyond_cell/
└── bioyond_cell_workstation.py # 业务逻辑
```
---
## 版本历史
- **v1.1** (2026-01-08): 补充实际配置案例
- 添加"手动传递窗右"和"手动传递窗左"的完整配置示例
- 展示 UUID 复用的实际应用
- 说明三个仓库共享物理堆栈的配置方法
- **v1.0** (2026-01-07): 初始版本
- 新增 `row_offset` 参数支持
- 创建 `bioyond_warehouse_5x3x1` 和 `bioyond_warehouse_2x2x1`
- 修复多个仓库的上下颠倒问题
---
## 相关资源
- [warehouse.py](../warehouse.py) - 核心工厂函数实现
- [YB_warehouses.py](YB_warehouses.py) - 所有仓库定义
- [decks.py](decks.py) - Deck 布局配置
- [config.py](../../devices/workstation/bioyond_studio/config.py) - UUID 映射
---
**维护者:** Uni-Lab-OS 开发团队
**最后更新:** 2026-01-07