149 lines
7.3 KiB
Markdown
149 lines
7.3 KiB
Markdown
# RMon Architecture
|
|
|
|
## Overview
|
|
|
|
RMon is split into two executables that communicate over a TCP connection tunneled through SSH:
|
|
|
|
```
|
|
Operator Machine Remote Network
|
|
┌─────────────────────────────┐ ┌──────────────────────────────────────┐
|
|
│ rmon-ui │ │ rmon-agent │
|
|
│ ┌──────────┐ ┌───────────┐ │ SSH │ ┌──────────┐ ┌──────────────────┐ │
|
|
│ │ Plot │ │ Signal │ │ Tunnel │ │ Protocol │ │ Source Adapters │ │
|
|
│ │ Panels │ │ Tree │◄├──────────┤► │ Server │◄─│ CSV / UDP / EPICS│ │
|
|
│ └──────────┘ └───────────┘ │ │ └──────────┘ └──────────────────┘ │
|
|
│ ┌──────────────────────┐ │ │ ┌──────────────────┐ │
|
|
│ │ SSH Connection Mgr │ │ │ │ Storage Engine │ │
|
|
│ └──────────────────────┘ │ │ │ Continuous/Circ. │ │
|
|
└─────────────────────────────┘ │ └──────────────────┘ │
|
|
└──────────────────────────────────────┘
|
|
```
|
|
|
|
## Language and Build Targets
|
|
|
|
**Single language: Rust** throughout agent, UI, and shared library.
|
|
|
|
| Crate | Purpose | Build Target |
|
|
|-------|---------|--------------|
|
|
| `rmon-common` | Shared types and protocol messages | (library, no binary) |
|
|
| `rmon-agent` | Remote data acquisition server | `x86_64-unknown-linux-musl` (fully static, zero runtime deps) |
|
|
| `rmon-ui` | Operator desktop application | native platform (dynamically links X11 + libGL) |
|
|
|
|
### Why musl for the agent
|
|
|
|
The agent must run on remote machines with arbitrary (potentially old) Linux environments without root access. Compiling with `x86_64-unknown-linux-musl` produces a single binary with:
|
|
- No glibc dependency
|
|
- No dynamic linker requirement
|
|
- Works on any Linux kernel ≥ 3.2
|
|
- Can be copied with `scp` and run immediately
|
|
|
|
The UI runs on the operator's own modern desktop, so strict portability is not required there.
|
|
|
|
### Why egui for the UI
|
|
|
|
- Immediate-mode rendering — natural fit for real-time data that changes every frame
|
|
- `glow` backend uses OpenGL 2.1+, available on all modern desktop Linux setups
|
|
- Cross-platform (Linux, Windows, macOS) from a single Rust codebase
|
|
- Ships as a single binary (dynamically links only X11/Wayland + libGL which are always present on any desktop Linux)
|
|
- `egui_plot` provides interactive time-series plots with zoom/pan; custom `egui` painter used for high-throughput rendering paths
|
|
|
|
## Cargo Workspace Layout
|
|
|
|
```
|
|
rmon/
|
|
├── Cargo.toml # workspace root
|
|
├── rmon-common/ # shared types and protocol (lib crate)
|
|
│ └── src/
|
|
│ ├── signal.rs # SignalId, SignalInfo, Sample, StorageMode
|
|
│ └── protocol.rs # ClientMessage, AgentMessage enums
|
|
├── rmon-agent/ # remote acquisition agent
|
|
│ ├── .cargo/config.toml # sets default target to x86_64-unknown-linux-musl
|
|
│ └── src/
|
|
│ ├── main.rs
|
|
│ ├── config.rs # TOML config file parsing
|
|
│ ├── sources/ # one module per data source type
|
|
│ │ ├── trait.rs # DataSource trait
|
|
│ │ ├── csv.rs
|
|
│ │ ├── udp.rs
|
|
│ │ └── epics.rs
|
|
│ ├── storage/
|
|
│ │ ├── continuous.rs
|
|
│ │ └── circular.rs
|
|
│ └── server/
|
|
│ ├── listener.rs # TCP accept loop
|
|
│ └── session.rs # per-client session handler
|
|
└── rmon-ui/ # operator desktop UI
|
|
└── src/
|
|
├── main.rs
|
|
├── app.rs # top-level App struct, egui App impl
|
|
├── connection/
|
|
│ ├── ssh.rs # SSH tunnel lifecycle
|
|
│ └── client.rs # async protocol client (wraps TCP stream)
|
|
├── state/
|
|
│ └── session.rs # connected session state, signal cache
|
|
└── ui/
|
|
├── connect_dialog.rs
|
|
├── signal_tree.rs
|
|
├── plot_panel.rs # one per open plot window
|
|
└── toolbar.rs
|
|
```
|
|
|
|
## Remote Access: SSH-Based Deployment
|
|
|
|
RMon follows the same pattern as VSCode Remote: the UI manages the agent lifecycle transparently, leveraging the user's existing `~/.ssh/config` (including `ProxyJump` for multi-hop networks).
|
|
|
|
### Connection flow
|
|
|
|
1. User enters an SSH host alias (from their `~/.ssh/config`) and optional agent port
|
|
2. UI runs: `ssh {host} 'uname -m'` to detect remote architecture
|
|
3. UI checks if agent binary is current: `ssh {host} 'cat ~/.rmon/agent.sha256'`
|
|
4. If stale/missing: UI pipes the correct pre-built binary via SSH:
|
|
```
|
|
cat rmon-agent-{arch} | ssh {host} 'mkdir -p ~/.rmon && cat > ~/.rmon/agent && chmod +x ~/.rmon/agent'
|
|
```
|
|
5. UI starts the agent (if not already running):
|
|
```
|
|
ssh {host} 'pgrep -f "rmon-agent --port {port}" || nohup ~/.rmon/agent --port {port} >> ~/.rmon/agent.log 2>&1 &'
|
|
```
|
|
6. UI establishes SSH local port forward: `ssh -N -L {local_port}:localhost:{remote_port} {host}`
|
|
7. UI opens TCP connection to `localhost:{local_port}` and sends `Handshake`
|
|
|
|
No special software needs to be installed on the remote machine. All multi-hop SSH configuration is handled transparently by the system `ssh` binary reading the user's `~/.ssh/config`.
|
|
|
|
## Data Flow (steady state)
|
|
|
|
```
|
|
Source (e.g. UDP)
|
|
│ raw packets + source timestamp
|
|
▼
|
|
DataSource adapter
|
|
│ normalizes timestamp → i64 nanoseconds since Unix epoch
|
|
│ applies scale/offset
|
|
▼
|
|
Dispatcher (tokio broadcast channel per signal)
|
|
│
|
|
├──► Storage engine (writes to ring buffer or append log)
|
|
│
|
|
└──► Session handlers (one per connected UI client)
|
|
│ batches samples, sends DataBatch over TCP
|
|
▼
|
|
rmon-ui client
|
|
│ updates per-signal ring buffer (last N seconds for display)
|
|
▼
|
|
Plot panels (egui repaint)
|
|
```
|
|
|
|
## Protocol Summary
|
|
|
|
Binary framing: `[u32 little-endian length][bincode-serialized payload]`
|
|
|
|
Messages are typed Rust enums defined in `rmon-common::protocol`. See `docs/protocol.md` for the full message reference.
|
|
|
|
## Agent Configuration
|
|
|
|
The agent is configured via a TOML file (default: `~/.rmon/config.toml`). It declares:
|
|
- Which data sources to connect to (type, address, signal definitions)
|
|
- Default storage policy
|
|
|
|
See `docs/agent-spec.md` for the full configuration reference.
|