Files
rmon/docs/architecture.md
Martino Ferrari dca378b5ef initial commit
2026-04-11 16:30:18 +02:00

7.3 KiB

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.