# 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.