initial commit
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user