<p align="center">
<img src="./res/logo.png" alt="Aoba Logo" width="240" />
</p>
<p align="center">
<h1 align="center">
青叶(Aoba)
</h1>
</p>
<p align="center">
<a href="https://github.com/celestia-island/aoba/actions/workflows/checks.yml">
<img src="https://github.com/celestia-island/aoba/actions/workflows/checks.yml/badge.svg?branch=master" alt="Checks status" />
</a>
<a href="https://github.com/celestia-island/aoba/actions/workflows/e2e-tests-tui.yml">
<img src="https://github.com/celestia-island/aoba/actions/workflows/e2e-tests-tui.yml/badge.svg?branch=master" alt="E2E TUI status" />
</a>
<a href="https://github.com/celestia-island/aoba/actions/workflows/e2e-tests-cli.yml">
<img src="https://github.com/celestia-island/aoba/actions/workflows/e2e-tests-cli.yml/badge.svg?branch=master" alt="E2E CLI status" />
</a>
<a href="https://github.com/celestia-island/aoba/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/celestia-island/aoba?color=blue" alt="License" />
</a>
<a href="https://github.com/celestia-island/aoba/releases/latest">
<img src="https://img.shields.io/github/v/tag/celestia-island/aoba?label=version&sort=semver" alt="Latest Version" />
</a>
</p>
<p align="center">
<a href="./README.md">EN</a> | ZH
</p>
专用于 Modbus RTU 协议的调试与转换工具,支持硬件串口与网络端口的转发协议,提供 TUI 界面与 CLI 接口。
## 功能
- 支持 Modbus RTU(主/从)协议的调试与模拟,支持四种寄存器类型:保持寄存器 (holding)、输入寄存器 (input)、线圈 (coils) 与离散输入 (discrete)。
- 提供功能丰富的 CLI:端口检测/查询(`--list-ports` / `--check-port`)、主/从模式(`--master-provide` / `--slave-listen`)及其持久化模式(`--*-persist`),输出可为 JSON/JSONL,适用于脚本与 CI 集成。
- 交互式 TUI:通过终端 UI 进行端口、站点与寄存器的图形化配置;支持保存/加载配置(`Ctrl+S` 保存并自动启用端口),并与 CLI 通过 IPC 通信进行集成测试与自动化使用。
- 多种数据源与协议支持:支持物理/虚拟串口(通过 `socat` 管理)、HTTP、MQTT、IPC(Unix socket / 命名管道)、文件与管道(FIFO)作为数据下行/上行来源或输出目标。
- 端口转发(Port Forwarding / 透明转发):在 TUI 内部配置源端口与目标端口实现数据转发或数据复制,用于监控、桥接或测试场景。
- 守护进程模式(daemon):以非交互方式运行,自动加载 TUI 保存的配置并启动所有已配置的端口/站点,适用于嵌入式部署与 CI 环境。
- 虚拟端口与测试工具:包含 `scripts/socat_init.sh`(用于创建虚拟串口 vcom)及广泛的 E2E 示例(`examples/cli_e2e`、`examples/tui_e2e`),方便本地与 CI 测试。
- 可扩展与集成:支持将串口数据通过 HTTP/MQTT/IPC 转发或接收,便于与其他服务集成与远程控制。
> 注意:使用 `--no-config-cache` 标志可以禁用保存/加载 TUI 配置(即不使用配置缓存),`--config-file <FILE>` 与 `--no-config-cache` 互斥。
## 快速开始
1. 安装 Rust 工具链
2. 克隆本仓库并进入目录
3. 安装:
- 从源码构建并安装:`cargo install aoba`
- 或使用 CI 预构建的 release(如果已发布)通过 `cargo-binstall` 安装:
- 示例:`cargo binstall --manifest-path ./Cargo.toml --version <version>`
- 可使用 `--target <triple>` 指定目标平台(例如 `x86_64-unknown-linux-gnu`)。
4. 运行 `aoba`,默认启动 TUI 界面进行配置与操作;如需在 TUI 模式下保存配置并留到后续使用,请参考下文“持久配置文件”章节
## 持久配置文件
`--config-file <FILE>` 用于显式指定 TUI 的配置文件路径(守护进程可通过 `--daemon-config` 指定),该选项与 `--no-config-cache` 冲突。`--no-config-cache` 会禁用配置的加载与保存(即不开启配置缓存),因此不能与 `--config-file <FILE>` 同时使用,命令行会拒绝此组合。
示例:
```bash
# 启动 TUI 并显式使用自定义配置文件(允许加载/保存)
aoba --tui --config-file /path/to/config.json
# 启动 TUI 并禁用配置缓存(不加载/保存配置),这是默认选项
aoba --tui --no-config-cache
```
在此之后,可以以非交互式方式运行 Aoba 守护进程,加载之前保存的配置文件:
```bash
# 启动 Aoba 守护进程,加载之前保存的配置文件
aoba --daemon --config-file /path/to/config.json
```
建议使用 systemd 或其他进程管理工具来管理守护进程,以下是一个简单的 systemd 服务单元示例:
```ini
# 写入 /etc/systemd/system/aoba.service 并启用服务
sudo tee /etc/systemd/system/aoba.service <<EOF
[Unit]
Description=Aoba Modbus RTU Daemon
Wants=network.target
After=network.target network-service
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=/home/youruser
ExecStart=/usr/local/bin/aoba --daemon --config-file /home/youruser/config.json
Restart=always
RestartSec=1s
[Install]
WantedBy=multi-user.target
EOF
```
## 典型使用场景
- **自动化测试**:CI/CD 环境中自动启动 Modbus 模拟器
- **嵌入式系统**:在诸如树莓派这样的开发板上运行 Modbus 守护进程,配合 CH340 等 USB 转串口模块工作
## 编程 API
Aoba 提供了基于 trait 的 Rust API,用于将 Modbus 功能嵌入到您的应用程序中。API 支持主站(客户端)和从站(服务器)角色,并可自定义钩子和数据源。
### API 快速示例
**Modbus 主站(轮询从站):**
```rust
use aoba::api::modbus::{ModbusBuilder, RegisterMode};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 创建并启动主站轮询从站
let master = ModbusBuilder::new_master(1)
.with_port("/dev/ttyUSB0")
.with_register(RegisterMode::Holding, 0, 10)
.start_master(None, None)?;
// 通过迭代器接口接收响应
while let Some(response) = master.recv_timeout(std::time::Duration::from_secs(2)) {
println!("接收到: {:?}", response.values);
}
Ok(())
}
```
**Modbus 从站(响应请求):**
```rust
use aoba::api::modbus::{ModbusBuilder, RegisterMode};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 创建并启动从站响应主站请求
let slave = ModbusBuilder::new_slave(1)
.with_port("/dev/ttyUSB0")
.with_register(RegisterMode::Holding, 0, 10)
.start_slave(None)?;
// 通过迭代器接口接收请求通知
while let Some(notification) = slave.recv_timeout(std::time::Duration::from_secs(10)) {
println!("已处理请求: {:?}", notification.values);
}
Ok(())
}
```
### 运行 API 示例
**方法 1:使用测试脚本(推荐)**
提供了一个 Python 测试脚本,可以同时运行主站和从站示例,并输出带颜色的前缀标记:
```bash
# 运行 30 秒
python3 scripts/run_api_test.py --duration 30
# 无限运行(按 Ctrl+C 停止)
python3 scripts/run_api_test.py
# 自定义端口
python3 scripts/run_api_test.py --master-port /dev/ttyUSB0 --slave-port /dev/ttyUSB1
# 跳过自动构建(使用已有的二进制文件)
python3 scripts/run_api_test.py --no-build
```
> **注意**:日志中可能会看到 "Operation timed out" 超时警告,这是正常行为:
>
> - 从站在等待主站请求时会超时(1秒超时)
> - 主站在等待从站响应时会超时(2秒超时)
> - 双方都会自动重试并继续运行
> - 尽管有这些警告,通信仍然成功
**方法 2:手动执行**
在不同终端中运行:
```bash
# 终端 1:先启动从站
cargo run --package api_slave -- /tmp/vcom2
# 终端 2:再启动主站
cargo run --package api_master -- /tmp/vcom1
```
注意:在 Linux/WSL 上需要先初始化虚拟串口:
```bash
./scripts/socat_init.sh
```
### 完整示例
完整示例包含中间件钩子和数据源,请参考:
- [`examples/api_master`](examples/api_master) - 主站示例(带日志钩子)
- [`examples/api_slave`](examples/api_slave) - 从站示例(带请求监控和统计)