mirror of
https://github.com/dptech-corp/Uni-Lab-OS.git
synced 2025-12-17 13:01:12 +00:00
Update registry. Update uuid loop figure method. Update install docs.
This commit is contained in:
197
docs/user_guide/quick_install_guide.md
Normal file
197
docs/user_guide/quick_install_guide.md
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
# Uni-Lab-OS 一键安装快速指南
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本指南提供最快速的 Uni-Lab-OS 安装方法,使用预打包的 conda 环境,无需手动配置依赖。
|
||||||
|
|
||||||
|
## 前置要求
|
||||||
|
|
||||||
|
- 已安装 Conda/Miniconda/Miniforge/Mamba
|
||||||
|
- 至少 10GB 可用磁盘空间
|
||||||
|
- Windows 10+, macOS 10.14+, 或 Linux (Ubuntu 20.04+)
|
||||||
|
|
||||||
|
## 安装步骤
|
||||||
|
|
||||||
|
### 第一步:下载预打包环境
|
||||||
|
|
||||||
|
1. 访问 [GitHub Actions - Conda Pack Build](https://github.com/dptech-corp/Uni-Lab-OS/actions/workflows/conda-pack-build.yml)
|
||||||
|
|
||||||
|
2. 选择最新的成功构建记录(绿色勾号 ✓)
|
||||||
|
|
||||||
|
3. 在页面底部的 "Artifacts" 部分,下载对应你操作系统的压缩包:
|
||||||
|
- Windows: `unilab-pack-win-64-{branch}.zip`
|
||||||
|
- macOS (Intel): `unilab-pack-osx-64-{branch}.tar.gz`
|
||||||
|
- macOS (Apple Silicon): `unilab-pack-osx-arm64-{branch}.tar.gz`
|
||||||
|
- Linux: `unilab-pack-linux-64-{branch}.tar.gz`
|
||||||
|
|
||||||
|
### 第二步:解压并运行安装脚本
|
||||||
|
|
||||||
|
#### Windows
|
||||||
|
|
||||||
|
```batch
|
||||||
|
REM 使用 Windows 资源管理器解压下载的 zip 文件
|
||||||
|
REM 或使用命令行:
|
||||||
|
tar -xzf unilab-pack-win-64-dev.zip
|
||||||
|
|
||||||
|
REM 进入解压后的目录
|
||||||
|
cd unilab-pack-win-64-dev
|
||||||
|
|
||||||
|
REM 双击运行 install_unilab.bat
|
||||||
|
REM 或在命令行中执行:
|
||||||
|
install_unilab.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
#### macOS
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 解压下载的压缩包
|
||||||
|
tar -xzf unilab-pack-osx-arm64-dev.tar.gz
|
||||||
|
|
||||||
|
# 进入解压后的目录
|
||||||
|
cd unilab-pack-osx-arm64-dev
|
||||||
|
|
||||||
|
# 运行安装脚本
|
||||||
|
bash install_unilab.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Linux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 解压下载的压缩包
|
||||||
|
tar -xzf unilab-pack-linux-64-dev.tar.gz
|
||||||
|
|
||||||
|
# 进入解压后的目录
|
||||||
|
cd unilab-pack-linux-64-dev
|
||||||
|
|
||||||
|
# 添加执行权限(如果需要)
|
||||||
|
chmod +x install_unilab.sh
|
||||||
|
|
||||||
|
# 运行安装脚本
|
||||||
|
./install_unilab.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第三步:激活环境
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda activate unilab
|
||||||
|
```
|
||||||
|
|
||||||
|
### 第四步:验证安装(推荐)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 确保已激活环境
|
||||||
|
conda activate unilab
|
||||||
|
|
||||||
|
# 运行验证脚本
|
||||||
|
python verify_installation.py
|
||||||
|
```
|
||||||
|
|
||||||
|
如果看到 "✓ All checks passed!",说明安装成功!
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q: 安装脚本找不到 conda?
|
||||||
|
|
||||||
|
**A:** 确保你已经安装了 conda/miniconda/miniforge,并且安装在标准位置:
|
||||||
|
|
||||||
|
- **Windows**:
|
||||||
|
|
||||||
|
- `%USERPROFILE%\miniforge3`
|
||||||
|
- `%USERPROFILE%\miniconda3`
|
||||||
|
- `%USERPROFILE%\anaconda3`
|
||||||
|
- `C:\ProgramData\miniforge3`
|
||||||
|
|
||||||
|
- **macOS/Linux**:
|
||||||
|
- `~/miniforge3`
|
||||||
|
- `~/miniconda3`
|
||||||
|
- `~/anaconda3`
|
||||||
|
- `/opt/conda`
|
||||||
|
|
||||||
|
如果安装在其他位置,可以先激活 conda base 环境,然后手动运行安装脚本。
|
||||||
|
|
||||||
|
### Q: 安装后激活环境提示找不到?
|
||||||
|
|
||||||
|
**A:** 尝试以下方法:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方法 1: 使用 conda activate
|
||||||
|
conda activate unilab
|
||||||
|
|
||||||
|
# 方法 2: 使用完整路径激活(Windows)
|
||||||
|
call C:\Users\{YourUsername}\miniforge3\envs\unilab\Scripts\activate.bat
|
||||||
|
|
||||||
|
# 方法 2: 使用完整路径激活(Unix)
|
||||||
|
source ~/miniforge3/envs/unilab/bin/activate
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: conda-unpack 失败怎么办?
|
||||||
|
|
||||||
|
**A:** 尝试手动运行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Windows
|
||||||
|
cd %CONDA_PREFIX%\envs\unilab
|
||||||
|
.\Scripts\conda-unpack.exe
|
||||||
|
|
||||||
|
# macOS/Linux
|
||||||
|
cd $CONDA_PREFIX/envs/unilab
|
||||||
|
./bin/conda-unpack
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: 验证脚本报错?
|
||||||
|
|
||||||
|
**A:** 首先确认环境已激活:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 检查当前环境
|
||||||
|
conda env list
|
||||||
|
|
||||||
|
# 应该看到 unilab 前面有 * 标记
|
||||||
|
```
|
||||||
|
|
||||||
|
如果仍有问题,查看具体报错信息,可能需要:
|
||||||
|
|
||||||
|
- 重新运行安装脚本
|
||||||
|
- 检查磁盘空间
|
||||||
|
- 查看详细文档
|
||||||
|
|
||||||
|
### Q: 环境很大,有办法减小吗?
|
||||||
|
|
||||||
|
**A:** 预打包的环境包含所有依赖,通常较大(压缩后 2-5GB)。这是为了确保离线安装和完整功能。如果空间有限,考虑使用手动安装方式,只安装需要的组件。
|
||||||
|
|
||||||
|
### Q: 如何更新到最新版本?
|
||||||
|
|
||||||
|
**A:** 重新下载最新的预打包环境,运行安装脚本时选择覆盖现有环境。
|
||||||
|
|
||||||
|
或者在现有环境中更新:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
conda activate unilab
|
||||||
|
|
||||||
|
# 更新 unilabos
|
||||||
|
cd /path/to/Uni-Lab-OS
|
||||||
|
git pull
|
||||||
|
pip install -e . --upgrade
|
||||||
|
|
||||||
|
# 更新 ros-humble-unilabos-msgs
|
||||||
|
mamba update ros-humble-unilabos-msgs -c uni-lab -c robostack-staging -c conda-forge
|
||||||
|
```
|
||||||
|
|
||||||
|
## 下一步
|
||||||
|
|
||||||
|
安装完成后,你可以:
|
||||||
|
|
||||||
|
1. **查看启动指南**: {doc}`launch`
|
||||||
|
2. **运行示例**: {doc}`../boot_examples/index`
|
||||||
|
3. **配置设备**: 编辑 `unilabos_data/startup_config.json`
|
||||||
|
4. **阅读开发文档**: {doc}`../developer_guide/workstation_architecture`
|
||||||
|
|
||||||
|
## 需要帮助?
|
||||||
|
|
||||||
|
- **文档**: [docs/user_guide/installation.md](installation.md)
|
||||||
|
- **问题反馈**: [GitHub Issues](https://github.com/dptech-corp/Uni-Lab-OS/issues)
|
||||||
|
- **开发版安装**: 参考 {doc}`installation` 的方式二
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**提示**: 这个预打包环境包含了从指定分支(通常是 `dev`)构建的最新代码。如果需要稳定版本,请使用方式二手动安装 release 版本。
|
||||||
@@ -4741,6 +4741,7 @@ virtual_stirrer:
|
|||||||
status_types:
|
status_types:
|
||||||
current_speed: float
|
current_speed: float
|
||||||
current_vessel: str
|
current_vessel: str
|
||||||
|
device_info: dict
|
||||||
is_stirring: bool
|
is_stirring: bool
|
||||||
max_speed: float
|
max_speed: float
|
||||||
min_speed: float
|
min_speed: float
|
||||||
@@ -4775,6 +4776,8 @@ virtual_stirrer:
|
|||||||
type: number
|
type: number
|
||||||
current_vessel:
|
current_vessel:
|
||||||
type: string
|
type: string
|
||||||
|
device_info:
|
||||||
|
type: object
|
||||||
is_stirring:
|
is_stirring:
|
||||||
type: boolean
|
type: boolean
|
||||||
max_speed:
|
max_speed:
|
||||||
@@ -4796,6 +4799,7 @@ virtual_stirrer:
|
|||||||
- remaining_time
|
- remaining_time
|
||||||
- max_speed
|
- max_speed
|
||||||
- min_speed
|
- min_speed
|
||||||
|
- device_info
|
||||||
type: object
|
type: object
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
virtual_transfer_pump:
|
virtual_transfer_pump:
|
||||||
|
|||||||
@@ -22,18 +22,6 @@ BIOYOND_PolymerStation_1FlaskCarrier:
|
|||||||
init_param_schema: {}
|
init_param_schema: {}
|
||||||
registry_type: resource
|
registry_type: resource
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
BIOYOND_PolymerStation_6VialCarrier:
|
|
||||||
category:
|
|
||||||
- bottle_carriers
|
|
||||||
class:
|
|
||||||
module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6VialCarrier
|
|
||||||
type: pylabrobot
|
|
||||||
description: BIOYOND_PolymerStation_6VialCarrier
|
|
||||||
handles: []
|
|
||||||
icon: ''
|
|
||||||
init_param_schema: {}
|
|
||||||
registry_type: resource
|
|
||||||
version: 1.0.0
|
|
||||||
BIOYOND_PolymerStation_6StockCarrier:
|
BIOYOND_PolymerStation_6StockCarrier:
|
||||||
category:
|
category:
|
||||||
- bottle_carriers
|
- bottle_carriers
|
||||||
@@ -46,4 +34,15 @@ BIOYOND_PolymerStation_6StockCarrier:
|
|||||||
init_param_schema: {}
|
init_param_schema: {}
|
||||||
registry_type: resource
|
registry_type: resource
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
BIOYOND_PolymerStation_6VialCarrier:
|
||||||
|
category:
|
||||||
|
- bottle_carriers
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottle_carriers:BIOYOND_PolymerStation_6VialCarrier
|
||||||
|
type: pylabrobot
|
||||||
|
description: BIOYOND_PolymerStation_6VialCarrier
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
registry_type: resource
|
||||||
|
version: 1.0.0
|
||||||
|
|||||||
@@ -1,24 +1,50 @@
|
|||||||
BIOYOND_PolymerStation_Solid_Stock:
|
|
||||||
class:
|
|
||||||
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solid_Stock
|
|
||||||
type: pylabrobot
|
|
||||||
|
|
||||||
BIOYOND_PolymerStation_Solid_Vial:
|
|
||||||
class:
|
|
||||||
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solid_Vial
|
|
||||||
type: pylabrobot
|
|
||||||
|
|
||||||
BIOYOND_PolymerStation_Liquid_Vial:
|
BIOYOND_PolymerStation_Liquid_Vial:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
class:
|
class:
|
||||||
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Liquid_Vial
|
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Liquid_Vial
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
BIOYOND_PolymerStation_Solution_Beaker:
|
icon: ''
|
||||||
class:
|
init_param_schema: {}
|
||||||
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solution_Beaker
|
version: 1.0.0
|
||||||
type: pylabrobot
|
|
||||||
|
|
||||||
BIOYOND_PolymerStation_Reagent_Bottle:
|
BIOYOND_PolymerStation_Reagent_Bottle:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
class:
|
class:
|
||||||
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Reagent_Bottle
|
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Reagent_Bottle
|
||||||
type: pylabrobot
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
BIOYOND_PolymerStation_Solid_Stock:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solid_Stock
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
BIOYOND_PolymerStation_Solid_Vial:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solid_Vial
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
BIOYOND_PolymerStation_Solution_Beaker:
|
||||||
|
category:
|
||||||
|
- bottles
|
||||||
|
class:
|
||||||
|
module: unilabos.resources.bioyond.bottles:BIOYOND_PolymerStation_Solution_Beaker
|
||||||
|
type: pylabrobot
|
||||||
|
handles: []
|
||||||
|
icon: ''
|
||||||
|
init_param_schema: {}
|
||||||
|
version: 1.0.0
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import copy
|
|
||||||
import json
|
import json
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
@@ -183,14 +182,22 @@ def slave(
|
|||||||
)
|
)
|
||||||
tree_response: SerialCommand_Response = rclient.call_async(request).result()
|
tree_response: SerialCommand_Response = rclient.call_async(request).result()
|
||||||
uuid_mapping = json.loads(tree_response.response)
|
uuid_mapping = json.loads(tree_response.response)
|
||||||
|
# 创建反向映射:new_uuid -> old_uuid
|
||||||
|
reverse_uuid_mapping = {new_uuid: old_uuid for old_uuid, new_uuid in uuid_mapping.items()}
|
||||||
for node in resources_config.root_nodes:
|
for node in resources_config.root_nodes:
|
||||||
if node.res_content.type == "device":
|
if node.res_content.type == "device":
|
||||||
for sub_node in node.children:
|
for sub_node in node.children:
|
||||||
# 只有二级子设备
|
# 只有二级子设备
|
||||||
if sub_node.res_content.type != "device":
|
if sub_node.res_content.type != "device":
|
||||||
device_tracker = devices_instances[node.res_content.id].resource_tracker
|
device_tracker = devices_instances[node.res_content.id].resource_tracker
|
||||||
resource_instance = device_tracker.figure_resource(
|
# sub_node.res_content.uuid 已经是新UUID,需要用旧UUID去查找
|
||||||
{"uuid": sub_node.res_content.uuid})
|
old_uuid = reverse_uuid_mapping.get(sub_node.res_content.uuid)
|
||||||
|
if old_uuid:
|
||||||
|
# 找到旧UUID,使用UUID查找
|
||||||
|
resource_instance = device_tracker.figure_resource({"uuid": old_uuid})
|
||||||
|
else:
|
||||||
|
# 未找到旧UUID,使用name查找
|
||||||
|
resource_instance = device_tracker.figure_resource({"name": sub_node.res_content.name})
|
||||||
device_tracker.loop_update_uuid(resource_instance, uuid_mapping)
|
device_tracker.loop_update_uuid(resource_instance, uuid_mapping)
|
||||||
else:
|
else:
|
||||||
logger.error("Slave模式不允许新增非设备节点下的物料")
|
logger.error("Slave模式不允许新增非设备节点下的物料")
|
||||||
|
|||||||
@@ -252,6 +252,8 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
)
|
)
|
||||||
# resources_config 通过各个设备的 resource_tracker 进行uuid更新,利用uuid_mapping
|
# resources_config 通过各个设备的 resource_tracker 进行uuid更新,利用uuid_mapping
|
||||||
# resources_config 的 root node 是
|
# resources_config 的 root node 是
|
||||||
|
# 创建反向映射:new_uuid -> old_uuid
|
||||||
|
reverse_uuid_mapping = {new_uuid: old_uuid for old_uuid, new_uuid in uuid_mapping.items()}
|
||||||
for tree in resources_config.trees:
|
for tree in resources_config.trees:
|
||||||
node = tree.root_node
|
node = tree.root_node
|
||||||
if node.res_content.type == "device":
|
if node.res_content.type == "device":
|
||||||
@@ -260,8 +262,16 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
if sub_node.res_content.type != "device":
|
if sub_node.res_content.type != "device":
|
||||||
# slave节点走c2s更新接口,拿到add自行update uuid
|
# slave节点走c2s更新接口,拿到add自行update uuid
|
||||||
device_tracker = self.devices_instances[node.res_content.id].resource_tracker
|
device_tracker = self.devices_instances[node.res_content.id].resource_tracker
|
||||||
resource_instance = device_tracker.figure_resource(
|
# sub_node.res_content.uuid 已经是新UUID,需要用旧UUID去查找
|
||||||
{"uuid": sub_node.res_content.uuid})
|
old_uuid = reverse_uuid_mapping.get(sub_node.res_content.uuid)
|
||||||
|
if old_uuid:
|
||||||
|
# 找到旧UUID,使用UUID查找
|
||||||
|
resource_instance = device_tracker.figure_resource({"uuid": old_uuid})
|
||||||
|
else:
|
||||||
|
# 未找到旧UUID,使用name查找
|
||||||
|
resource_instance = device_tracker.figure_resource(
|
||||||
|
{"name": sub_node.res_content.name}
|
||||||
|
)
|
||||||
device_tracker.loop_update_uuid(resource_instance, uuid_mapping)
|
device_tracker.loop_update_uuid(resource_instance, uuid_mapping)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -897,6 +907,7 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
uuid_list: List[str] = data["data"]
|
uuid_list: List[str] = data["data"]
|
||||||
with_children: bool = data["with_children"]
|
with_children: bool = data["with_children"]
|
||||||
from unilabos.app.web.client import http_client
|
from unilabos.app.web.client import http_client
|
||||||
|
|
||||||
resource_response = http_client.resource_tree_get(uuid_list, with_children)
|
resource_response = http_client.resource_tree_get(uuid_list, with_children)
|
||||||
response.response = json.dumps(resource_response)
|
response.response = json.dumps(resource_response)
|
||||||
|
|
||||||
@@ -920,6 +931,7 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
)
|
)
|
||||||
|
|
||||||
from unilabos.app.web.client import http_client
|
from unilabos.app.web.client import http_client
|
||||||
|
|
||||||
resource_start_time = time.time()
|
resource_start_time = time.time()
|
||||||
uuid_mapping = http_client.resource_tree_update(resource_tree_set, "", False)
|
uuid_mapping = http_client.resource_tree_update(resource_tree_set, "", False)
|
||||||
success = bool(uuid_mapping)
|
success = bool(uuid_mapping)
|
||||||
@@ -1254,7 +1266,9 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
"status": "success",
|
"status": "success",
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_resource(self, resource: ResourceSlot, resources: List[ResourceSlot], device: DeviceSlot, devices: List[DeviceSlot]):
|
def test_resource(
|
||||||
|
self, resource: ResourceSlot, resources: List[ResourceSlot], device: DeviceSlot, devices: List[DeviceSlot]
|
||||||
|
):
|
||||||
return {
|
return {
|
||||||
"resources": ResourceTreeSet.from_plr_resources([resource, *resources]).dump(),
|
"resources": ResourceTreeSet.from_plr_resources([resource, *resources]).dump(),
|
||||||
"devices": [device, *devices],
|
"devices": [device, *devices],
|
||||||
@@ -1280,9 +1294,7 @@ class HostNode(BaseROS2DeviceNode):
|
|||||||
else:
|
else:
|
||||||
self.lab_logger().warning("⚠️ 收到无效的Pong响应(缺少ping_id)")
|
self.lab_logger().warning("⚠️ 收到无效的Pong响应(缺少ping_id)")
|
||||||
|
|
||||||
def notify_resource_tree_update(
|
def notify_resource_tree_update(self, device_id: str, action: str, resource_uuid_list: List[str]) -> bool:
|
||||||
self, device_id: str, action: str, resource_uuid_list: List[str]
|
|
||||||
) -> bool:
|
|
||||||
"""
|
"""
|
||||||
通知设备节点更新资源树
|
通知设备节点更新资源树
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
<<<<<<<< HEAD:unilabos/ros/nodes/presets/workstation.py
|
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from typing import List, Dict, Any, Optional, TYPE_CHECKING
|
from typing import List, Dict, Any, Optional, TYPE_CHECKING
|
||||||
========
|
|
||||||
from pprint import pprint, saferepr, pformat
|
|
||||||
from typing import Union
|
|
||||||
>>>>>>>> main:unilabos/ros/nodes/presets/protocol_node.py
|
|
||||||
|
|
||||||
import rclpy
|
import rclpy
|
||||||
from rosidl_runtime_py import message_to_ordereddict
|
from rosidl_runtime_py import message_to_ordereddict
|
||||||
@@ -198,13 +193,8 @@ class ROS2WorkstationNode(BaseROS2DeviceNode):
|
|||||||
execute_callback=self._create_protocol_execute_callback(action_name, protocol_steps_generator),
|
execute_callback=self._create_protocol_execute_callback(action_name, protocol_steps_generator),
|
||||||
callback_group=ReentrantCallbackGroup(),
|
callback_group=ReentrantCallbackGroup(),
|
||||||
)
|
)
|
||||||
<<<<<<<< HEAD:unilabos/ros/nodes/presets/workstation.py
|
|
||||||
self.lab_logger().trace(f"发布动作: {action_name}, 类型: {str_action_type}")
|
self.lab_logger().trace(f"发布动作: {action_name}, 类型: {str_action_type}")
|
||||||
return
|
return
|
||||||
========
|
|
||||||
|
|
||||||
self.lab_logger().trace(f"发布动作: {action_name}, 类型: {str_action_type}")
|
|
||||||
>>>>>>>> main:unilabos/ros/nodes/presets/protocol_node.py
|
|
||||||
|
|
||||||
def _create_protocol_execute_callback(self, protocol_name, protocol_steps_generator):
|
def _create_protocol_execute_callback(self, protocol_name, protocol_steps_generator):
|
||||||
async def execute_protocol(goal_handle: ServerGoalHandle):
|
async def execute_protocol(goal_handle: ServerGoalHandle):
|
||||||
@@ -255,15 +245,10 @@ class ROS2WorkstationNode(BaseROS2DeviceNode):
|
|||||||
logs.append(step)
|
logs.append(step)
|
||||||
elif isinstance(step, list):
|
elif isinstance(step, list):
|
||||||
logs.append(step)
|
logs.append(step)
|
||||||
<<<<<<<< HEAD:unilabos/ros/nodes/presets/workstation.py
|
|
||||||
self.lab_logger().info(
|
self.lab_logger().info(
|
||||||
f"Goal received: {protocol_kwargs}, running steps: "
|
f"Goal received: {protocol_kwargs}, running steps: "
|
||||||
f"{json.dumps(logs, indent=4, ensure_ascii=False)}"
|
f"{json.dumps(logs, indent=4, ensure_ascii=False)}"
|
||||||
)
|
)
|
||||||
========
|
|
||||||
self.lab_logger().info(f"Goal received: {protocol_kwargs}, running steps: "
|
|
||||||
f"{json.dumps(logs, indent=4, ensure_ascii=False)}")
|
|
||||||
>>>>>>>> main:unilabos/ros/nodes/presets/protocol_node.py
|
|
||||||
|
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
time_overall = 100
|
time_overall = 100
|
||||||
@@ -278,7 +263,6 @@ class ROS2WorkstationNode(BaseROS2DeviceNode):
|
|||||||
time.sleep(action["action_kwargs"]["time"])
|
time.sleep(action["action_kwargs"]["time"])
|
||||||
step_results.append({"step": i + 1, "action": "wait", "result": "completed"})
|
step_results.append({"step": i + 1, "action": "wait", "result": "completed"})
|
||||||
else:
|
else:
|
||||||
<<<<<<<< HEAD:unilabos/ros/nodes/presets/workstation.py
|
|
||||||
try:
|
try:
|
||||||
result = await self.execute_single_action(**action)
|
result = await self.execute_single_action(**action)
|
||||||
step_results.append({"step": i + 1, "action": action["action_name"], "result": result})
|
step_results.append({"step": i + 1, "action": action["action_name"], "result": result})
|
||||||
@@ -289,13 +273,6 @@ class ROS2WorkstationNode(BaseROS2DeviceNode):
|
|||||||
step_results.append(
|
step_results.append(
|
||||||
{"step": i + 1, "action": action["action_name"], "result": ex.args[0]}
|
{"step": i + 1, "action": action["action_name"], "result": ex.args[0]}
|
||||||
)
|
)
|
||||||
========
|
|
||||||
result = await self.execute_single_action(**action)
|
|
||||||
step_results.append({"step": i + 1, "action": action["action_name"], "result": result})
|
|
||||||
ret_info = json.loads(getattr(result, "return_info", "{}"))
|
|
||||||
if not ret_info.get("suc", False):
|
|
||||||
raise RuntimeError(f"Step {i + 1} failed.")
|
|
||||||
>>>>>>>> main:unilabos/ros/nodes/presets/protocol_node.py
|
|
||||||
elif isinstance(action, list):
|
elif isinstance(action, list):
|
||||||
# 如果是并行动作,同时执行
|
# 如果是并行动作,同时执行
|
||||||
actions = action
|
actions = action
|
||||||
@@ -333,7 +310,6 @@ class ROS2WorkstationNode(BaseROS2DeviceNode):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# 捕获并记录错误信息
|
# 捕获并记录错误信息
|
||||||
<<<<<<<< HEAD:unilabos/ros/nodes/presets/workstation.py
|
|
||||||
str_step_results = [
|
str_step_results = [
|
||||||
{
|
{
|
||||||
k: dict(message_to_ordereddict(v)) if k == "result" and hasattr(v, "SLOT_TYPES") else v
|
k: dict(message_to_ordereddict(v)) if k == "result" and hasattr(v, "SLOT_TYPES") else v
|
||||||
@@ -341,9 +317,6 @@ class ROS2WorkstationNode(BaseROS2DeviceNode):
|
|||||||
}
|
}
|
||||||
for i in step_results
|
for i in step_results
|
||||||
]
|
]
|
||||||
========
|
|
||||||
str_step_results = [{k: dict(message_to_ordereddict(v)) if k == "result" and hasattr(v, "SLOT_TYPES") else v for k, v in i.items()} for i in step_results]
|
|
||||||
>>>>>>>> main:unilabos/ros/nodes/presets/protocol_node.py
|
|
||||||
execution_error = f"{traceback.format_exc()}\n\nStep Result: {pformat(str_step_results)}"
|
execution_error = f"{traceback.format_exc()}\n\nStep Result: {pformat(str_step_results)}"
|
||||||
execution_success = False
|
execution_success = False
|
||||||
self.lab_logger().error(f"协议 {protocol_name} 执行出错: {str(e)} \n{traceback.format_exc()}")
|
self.lab_logger().error(f"协议 {protocol_name} 执行出错: {str(e)} \n{traceback.format_exc()}")
|
||||||
|
|||||||
@@ -1060,6 +1060,8 @@ class DeviceNodeResourceTracker(object):
|
|||||||
else:
|
else:
|
||||||
# 对于实例类型,需要特殊处理 uuid 字段
|
# 对于实例类型,需要特殊处理 uuid 字段
|
||||||
# 如果查找的是 unilabos_uuid,使用 getattr
|
# 如果查找的是 unilabos_uuid,使用 getattr
|
||||||
|
if identifier_key == "uuid":
|
||||||
|
identifier_key = "unilabos_uuid"
|
||||||
if hasattr(resource, identifier_key):
|
if hasattr(resource, identifier_key):
|
||||||
if getattr(resource, identifier_key) == compare_value:
|
if getattr(resource, identifier_key) == compare_value:
|
||||||
res_list.append((parent_res, resource))
|
res_list.append((parent_res, resource))
|
||||||
|
|||||||
Reference in New Issue
Block a user