Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,15 @@ jobs:
uv pip install -r unilabos/utils/requirements.txt
uv pip install pywinauto git+https://github.com/Xuwznln/pylabrobot.git
uv pip uninstall enum34 || echo enum34 not installed, skipping
uv pip install pytest
uv pip install .

- name: Run HostLink networking tests
run: |
call conda activate check-env
echo Running HostLink, ROS2 domain and networking runtime tests...
python -m pytest -q tests\hostlink tests\networking tests\ros\test_domain_init.py -p no:launch_testing -p no:launch_ros

- name: Run check mode (AST registry validation)
# check_mode 会真实 import 所有设备类(连带 matplotlib/opencv 等原生库)。
# Windows runner 上偶发原生崩溃(退出码 -1073741819 = 0xC0000005 访问违例),
Expand Down
51 changes: 44 additions & 7 deletions docs/advanced_usage/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Uni-Lab 使用 Python 格式的配置文件(`.py`),默认为 `unilabos_dat
class BasicConfig:
ak = "" # 实验室网页给您提供的ak代码
sk = "" # 实验室网页给您提供的sk代码
port = 8002 # 管理端 HTTP/Web API 与主微前端端口
disable_browser = False # 只禁止自动打开浏览器


# WebSocket配置,一般无需调整
Expand Down Expand Up @@ -60,6 +62,8 @@ class BasicConfig:
enable_resource_load = True # 是否启用资源加载
communication_protocol = "websocket" # 通信协议
log_level = "DEBUG" # 日志级别:TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL
port = 8002 # 管理端 HTTP/Web API 与主微前端端口
disable_browser = False # 只禁止自动打开浏览器,不停止管理端服务

# WebSocket配置
class WSConfig:
Expand All @@ -71,6 +75,18 @@ class WSConfig:
class HTTPConfig:
remote_addr = "https://leap-lab.bohrium.com/api/v1" # 远程服务器地址

# Host/Slave ROS2 组网控制通道
class HostLinkConfig:
enable = True
host = "" # Slave 的 HostNode IP;推荐用 --host-node-ip 覆盖
port = 7302
bind = "0.0.0.0"
advertise_ip = "" # Host 多网卡时显式填写 Slave 可达 IP
ros_domain_id = "" # Host 下发的 ROS_DOMAIN_ID
ros_discovery_range = ""
ros_static_peers = ""
ros_discovery_server = "" # 外部 host:port;off 表示禁用

# ROS配置
class ROSConfig:
modules = [
Expand Down Expand Up @@ -107,6 +123,8 @@ class ROSConfig:
| `ak` / `sk` | `--ak` / `--sk` | **安全考虑**:避免敏感信息泄露 |
| `working_dir` | `--working_dir` | **灵活性**:不同环境可能使用不同目录 |
| `is_host_mode` | `--is_slave` | **运行模式**:由启动场景决定,不固定 |
| HostNode 地址 | `--host-node-ip` | **组网目标**:Slave 按部署指定 Host IP |
| ROS2 domain | `--ros-domain-id` | **网络隔离**:由 Host 统一发布给 Slave |
| `slave_no_host` | `--slave_no_host` | **运行模式**:从站特殊配置,按需使用 |
| `upload_registry` | `--upload_registry` | **临时操作**:仅首次启动或更新时需要 |
| `vis_2d_enable` | `--2d_vis` | **调试功能**:按需临时启用 |
Expand Down Expand Up @@ -173,18 +191,37 @@ Uni-Lab 允许通过命令行参数覆盖配置文件中的设置,提供更灵
| `BasicConfig` | `upload_registry` | `--upload_registry` | 启动时上传注册表信息 |
| `BasicConfig` | `vis_2d_enable` | `--2d_vis` | 启用 2D 可视化 |
| `HTTPConfig` | `remote_addr` | `--addr` | 远程服务地址 |
| `HostLinkConfig` | `host` | `--host-node-ip` | Slave 连接的 HostNode IP/端口 |
| `HostLinkConfig` | `port` | `--hostlink-port` | HostLink TCP 端口,默认 7302 |
| `HostLinkConfig` | `bind` | `--hostlink-bind` | Host 监听地址 |
| `HostLinkConfig` | `advertise_ip` | `--hostlink-advertise-ip` | Host 多网卡发布地址 |
| `HostLinkConfig` | `enable` | `--disable-hostlink` | 禁用 HostLink |
| `HostLinkConfig` | `heartbeat_interval` | `--hostlink-heartbeat-interval` | Slave 心跳周期 |
| `HostLinkConfig` | `heartbeat_timeout` | `--hostlink-heartbeat-timeout` | Host 离线判定时间 |
| `HostLinkConfig` | `connect_timeout` | `--hostlink-connect-timeout` | TCP 连接/握手超时 |
| `HostLinkConfig` | `request_timeout` | `--hostlink-request-timeout` | 控制请求超时 |
| `HostLinkConfig` | `ros_domain_id`| `--ros-domain-id` | Host 发布或 Slave 本地兜底 domain |
| `HostLinkConfig` | `ros_discovery_range` | `--ros-discovery-range` | ROS 自动发现范围 |
| `HostLinkConfig` | `ros_static_peers` | `--ros-static-peers` | 分号分隔的静态对端 |
| `HostLinkConfig` | `ros_discovery_server` | `--ros-discovery-server` | 外部 Fast DDS Server |
| `HostLinkConfig` | `ros_assist_apply` | `--no-ros-assist` | 不应用 Host 下发的 ROS 环境 |

### 特殊命令行参数

除了直接覆盖配置项的参数外,还有一些特殊的命令行参数:

| 参数 | 说明 |
| ------------------- | ------------------------------------ |
| `--config` | 指定配置文件路径 |
| `--port` | Web 服务端口(不影响配置文件) |
| `--disable_browser` | 禁用自动打开浏览器(不影响配置文件) |
| `--visual` | 可视化工具选择(不影响配置文件) |
| `--skip_env_check` | 跳过环境检查(不影响配置文件) |
| 参数 | 说明 |
| --- | --- |
| `--config` | 指定配置文件路径 |
| `--port-management` / `--port_management` | 管理端 HTTP/Web API 与主微前端端口,默认 `8002`;`--port` 是兼容缩写 |
| `--hostlink-port` | HostLink TCP 端口,默认 `7302`,与管理端口独立 |
| `--disable-browser` / `--disable_browser` | 只禁用启动时自动打开浏览器,管理端 HTTP/Web 服务仍会启动 |
| `--visual` | 可视化工具选择(不影响配置文件) |
| `--skip_env_check` | 跳过环境检查(不影响配置文件) |

`--port-management` 最终覆盖 `BasicConfig.port`。主前端和 API 客户端都连接该
端口;即使设置 `--disable-browser`,该端口仍会监听。浏览器参数不影响
HostLink 的 `7302/TCP`。

### 命令行覆盖使用示例

Expand Down
93 changes: 84 additions & 9 deletions docs/developer_guide/networking_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ unilab --ak your_ak --sk your_sk -g host_devices.json
**启动命令**:

```bash
unilab --ak your_ak --sk your_sk -g slave_devices.json --is_slave
unilab --ak your_ak --sk your_sk -g slave_devices.json \
--is_slave --host-node-ip 192.168.1.10
```

---
Expand Down Expand Up @@ -105,6 +106,54 @@ ros2 topic list
ros2 action list
```

### HostLink 组网控制通道

Host 运行 ROS backend 时会在 TCP `7302` 监听 HostLink。Slave 通过
`--host-node-ip <host-ip>[:port]` 建立控制连接,在 `rclpy.init` 前完成两件事:

- 上报启动图中的设备 ID,供 Host 发现 Slave 及其设备归属;
- 接收并应用 Host 的 `ROS_DOMAIN_ID`、发现范围、静态对端和外部 Fast DDS
Discovery Server 地址。

HostLink 只辅助 ROS2 组网。设备 Action、节点注册和资源同步仍走现有 ROS2
接口;本阶段没有通过 HostLink 提供物料查询或无 ROS backend。

#### 端口与前端归属

| 服务 | 默认地址 | 协议 | 使用者 |
|---|---|---|---|
| 主 Web/API | `0.0.0.0:8002` | HTTP/WebSocket over TCP | 状态页、主微前端、API 客户端;由 `--port-management` 配置 |
| HostLink | `0.0.0.0:7302` | NDJSON over raw TCP | Host/Slave 进程,不供浏览器访问 |
| F003 Local Bridge API | `127.0.0.1:8014` | HTTP | 仅完整集成分支中的本地工作流微前端 |

因此微前端不访问 `7302`。接入主 OS API 的微前端跟随
`--port-management`(`--port` 为兼容缩写),默认访问 `8002`;F003 本地桥接
微前端仍使用其独立的 `8014`。`--disable-browser` 只禁止自动打开页面,不会停止
`8002` 的 HTTP/Web 服务。两个独立 TCP 服务不能绑定同一个 IP/端口。

#### HostLink 与 ROS2 参数

| 参数 | 作用域 | 默认值 | 说明 |
|---|---|---:|---|
| `--host-node-ip` | Slave | 空 | Host IP/主机名;兼容 `ip:port` |
| `--hostlink-port` | Host + Slave | `7302` | HostLink TCP 监听/连接端口;优先于 `--host-node-ip` 中的端口 |
| `--hostlink-bind` | Host | `0.0.0.0` | HostLink 监听网卡 |
| `--hostlink-advertise-ip` | Host | 自动探测 | 多网卡时发布给 Slave 的可达 IP |
| `--disable-hostlink` | Host + Slave | 否 | 禁用 HostLink,回退原 ROS2 发现 |
| `--hostlink-heartbeat-interval` | Slave | `5` 秒 | 心跳发送间隔 |
| `--hostlink-heartbeat-timeout` | Host | `15` 秒 | Slave 离线判定时间 |
| `--hostlink-connect-timeout` | Slave | `5` 秒 | 单次 TCP 连接和握手超时 |
| `--hostlink-request-timeout` | Slave | `10` 秒 | 控制请求超时 |
| `--ros-domain-id` | Host + Slave | 环境值 | Host 下发给 Slave;Slave 本地值仅作连接前兜底 |
| `--ros-discovery-range` | Host | 环境值 | `SYSTEM_DEFAULT/SUBNET/LOCALHOST/OFF` |
| `--ros-static-peers` | Host | 自动加入 Host IP | 分号分隔的静态发现对端 |
| `--ros-discovery-server` | Host | 环境值 | 外部 Fast DDS `host:port`;`off` 清除继承值 |
| `--no-ros-assist` | Slave | 否 | 保留 HostLink 心跳/设备发现,但不应用 Host ROS 参数 |

本切片没有启动 Fast DDS Discovery Server 进程,因此没有
`--ros-discovery-port`;该参数应与托管 Discovery Server 功能一并引入,不能成为
无效果的占位参数。

### WebSocket 通信

**用途**: 主节点与云端通信
Expand Down Expand Up @@ -188,14 +237,16 @@ unilab --ak your_ak --sk your_sk -g all_devices.json
**主节点**:

```bash
unilab --ak your_ak --sk your_sk -g host.json
unilab --ak your_ak --sk your_sk -g host.json --ros-domain-id 42
```

**从节点**:

```bash
unilab --ak your_ak --sk your_sk -g slave1.json --is_slave
unilab --ak your_ak --sk your_sk -g slave2.json --is_slave --port 8003
unilab --ak your_ak --sk your_sk -g slave1.json \
--is-slave --host-node-ip 192.168.1.10 --hostlink-port 7302
unilab --ak your_ak --sk your_sk -g slave2.json \
--is-slave --host-node-ip 192.168.1.10 --hostlink-port 7302 --port-management 8003
```

### 云端集成模式
Expand Down Expand Up @@ -254,7 +305,7 @@ unilab --ak your_ak --sk your_sk -g host.json
unilab --ak your_ak --sk your_sk -g host.json --upload_registry

# 指定端口
unilab --ak your_ak --sk your_sk -g host.json --port 8002
unilab --ak your_ak --sk your_sk -g host.json --port-management 8002
```

#### 3. 验证主节点
Expand Down Expand Up @@ -301,7 +352,7 @@ ros2 service list | grep host_node
unilab --ak your_ak --sk your_sk -g slave1.json --is_slave

# 指定不同端口(如果多个从节点在同一台机器)
unilab --ak your_ak --sk your_sk -g slave1.json --is_slave --port 8003
unilab --ak your_ak --sk your_sk -g slave1.json --is_slave --port-management 8003

# 跳过等待主节点(独立测试)
unilab --ak your_ak --sk your_sk -g slave1.json --is_slave --slave_no_host
Expand Down Expand Up @@ -358,10 +409,34 @@ ping <slave_node_ip>
export ROS_DOMAIN_ID=42
```

推荐由 Host 启动参数统一 domain,Slave 不再重复维护:

```bash
# Host:发布 domain 42
unilab -g host.json --ros-domain-id 42

# Slave:通过 HostLink 获取 domain 42 和发现配置
unilab -g slave.json --is-slave --host-node-ip 192.168.1.10
```

如实验室使用已有 Fast DDS Discovery Server,可在 Host 指定并下发:

```bash
unilab -g host.json --ros-domain-id 42 \
--ros-discovery-server 192.168.1.10:11811
```

此功能切片不会自动启动 Discovery Server 进程;未指定时仍沿用 ROS2/DDS
原有发现机制,并把 Host IP 加入 `ROS_STATIC_PEERS`。

### 防火墙配置

**建议做法**:

HostLink 需要 Slave 能访问 Host 的 TCP `7302`(若在 `--host-node-ip` 中指定
其他端口,则开放对应端口)。该端口只承载组网握手、心跳和设备 ID,不承载设备
动作或物料数据。

为了确保 ROS2 DDS 通信正常,建议直接关闭防火墙,而不是配置特定端口。ROS2 使用动态端口范围,配置特定端口可能导致通信问题。

**Linux**:
Expand Down Expand Up @@ -464,21 +539,21 @@ curl https://leap-lab.bohrium.com/api/v1/health

```bash
# host.json
unilab --ak your_ak --sk your_sk -g host.json --port 8002
unilab --ak your_ak --sk your_sk -g host.json --port-management 8002
```

### 房间 B - 从节点 1

```bash
# liquid_handler.json
unilab --ak your_ak --sk your_sk -g liquid_handler.json --is_slave --port 8003
unilab --ak your_ak --sk your_sk -g liquid_handler.json --is_slave --port-management 8003
```

### 房间 C - 从节点 2

```bash
# analytical.json
unilab --ak your_ak --sk your_sk -g analytical.json --is_slave --port 8004
unilab --ak your_ak --sk your_sk -g analytical.json --is_slave --port-management 8004
```

---
Expand Down
6 changes: 3 additions & 3 deletions docs/user_guide/best_practice.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ unilab --ak your_ak --sk your_sk -g example_empty.json
# 禁用自动打开浏览器
unilab --ak your_ak --sk your_sk -g graph.json --disable_browser

# 使用不同端口
unilab --ak your_ak --sk your_sk -g graph.json --port 8080
# 使用不同的管理 Web/API 端口
unilab --ak your_ak --sk your_sk -g graph.json --port-management 8080

# 测试环境
unilab --addr test --ak your_ak --sk your_sk -g graph.json
Expand Down Expand Up @@ -1631,7 +1631,7 @@ python -m unilabos.app.main \
--devices <克隆目录下的设备包目录> \
--external_devices_only \
--ak your_ak --sk your_sk --addr test --upload_registry \
--disable_browser --port 8100 \
--disable_browser --port-management 8100 \
-g <仓库内提供的图文件>
```

Expand Down
37 changes: 33 additions & 4 deletions docs/user_guide/launch.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ options:
--slave_no_host Skip waiting for host service in slave mode
--upload_registry Upload registry information when starting unilab
--config CONFIG Configuration file path, supports .py format Python config files
--port PORT Port for web service information page
--disable_browser Disable opening information page on startup
--port_management PORT_MANAGEMENT, --port-management PORT_MANAGEMENT, --port PORT_MANAGEMENT
管理端 HTTP/Web API 与主微前端端口,默认 8002
--disable_browser, --disable-browser
只禁止自动打开浏览器,管理端服务仍会启动
--2d_vis Enable 2D visualization when starting pylabrobot instance
--visual {rviz,web,disable}
Choose visualization tool: rviz, web, or disable
Expand Down Expand Up @@ -155,6 +157,33 @@ unilab --config path/to/your/config.py

局域网内分别启动的 Uni-Lab 主站/从站将自动组网,互相能访问所有设备状态、传感器信息并发送指令。

推荐由 Host 统一发布 ROS2 domain,Slave 只指定 Host IP:

```bash
# Host:8002 是管理 Web/API 和主微前端端口,7302 是 HostLink TCP
unilab -g host.json --port-management 8002 \
--hostlink-port 7302 --ros-domain-id 42

# Slave:管理端口只影响本机 Web/API;HostLink 目标端口单独配置
unilab -g slave.json --is-slave \
--host-node-ip 192.168.1.10 --hostlink-port 7302 --port-management 8003
```

主要组网参数:

- `--host-node-ip`:Slave 指定 Host IP/主机名。
- `--port-management` / `--port_management`:管理端 HTTP/Web API 和主微前端端口,默认 `8002`;`--port` 是兼容缩写。
- `--disable-browser` / `--disable_browser`:只禁止启动时自动打开浏览器,不会停止管理端口。
- `--hostlink-port`:HostLink TCP 端口,默认 `7302`,与管理端口独立。
- `--hostlink-bind` / `--hostlink-advertise-ip`:Host 监听地址与多网卡发布地址。
- `--ros-domain-id`:Host 下发给 Slave 的 ROS2 domain。
- `--ros-discovery-range` / `--ros-static-peers` / `--ros-discovery-server`:ROS2 发现策略。
- `--no-ros-assist`:仅保留 HostLink 设备发现,不覆盖 Slave 的 ROS2 环境。
- `--disable-hostlink`:完全关闭 HostLink,使用原 ROS2 发现流程。

浏览器和主微前端访问管理端口(默认 `8002`),不会访问 HostLink 的 `7302`。
即使使用 `--disable-browser`,前端仍可手动访问 `http://<节点 IP>:8002`。

## 可视化选项

### 2D 可视化
Expand Down Expand Up @@ -205,8 +234,8 @@ unilab --ak your_ak --sk your_sk --is_slave
# 启用可视化
unilab --ak your_ak --sk your_sk --visual web --2d_vis

# 指定本地信息网页服务端口和禁用自动跳出浏览器
unilab --ak your_ak --sk your_sk --port 8080 --disable_browser
# 指定管理端口并禁止自动打开浏览器;HTTP/Web 服务仍在 8080 启动
unilab --ak your_ak --sk your_sk --port-management 8080 --disable-browser
```

## 常见问题
Expand Down
1 change: 1 addition & 0 deletions tests/hostlink/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""HostLink unit tests."""
Loading