3.7 KiB
RMon Data Model
Signal
A signal is a named scalar time-series. Every signal has a unique SignalId and associated metadata.
/// Globally unique signal identifier.
/// Convention: "{source_type}://{source_name}/{path/to/signal}"
/// Examples:
/// "udp://imu_stream/accel_x"
/// "csv:///mnt/data/temperatures.csv/sensor_3"
/// "epics://PV:SYS:TEMP:READBACK"
type SignalId = String;
struct SignalInfo {
id: SignalId,
label: String, // human-readable display name
unit: String, // e.g. "m/s²", "°C", ""
scale: f64, // raw_value * scale + offset = engineering_value
offset: f64,
sampling_period: Option<Duration>,// None if irregular/event-driven
source_type: SourceType,
path: Vec<String>, // tree path for UI grouping, e.g. ["imu", "accel"]
}
enum SourceType { Csv, Udp, Epics, Custom(String) }
Signals are read-only from the UI's perspective. The agent owns all signal definitions (from its config file).
Sample
struct Sample {
/// Nanoseconds since Unix epoch (UTC).
/// All source timestamps are normalized to this by the adapter.
timestamp: i64,
value: f64,
}
All timestamps are normalized at ingestion time inside the adapter. The adapter knows the source-specific format (string, u64 seconds, etc.) and converts it. The rest of the system only sees i64 nanoseconds.
Timestamp Normalization Examples
| Source format | Example | Agent conversion |
|---|---|---|
%Y.%m.%d %H:%M:%S string |
"2024.01.15 14:32:07" |
parse → UTC i64 ns |
| u64 microseconds since epoch | 1705329127000000 |
multiply by 1000 |
| u64 seconds since epoch | 1705329127 |
multiply by 1_000_000_000 |
| Relative milliseconds | 12345 |
add agent start epoch |
Adapters declare their TimestampFormat in config; the normalization logic lives in a shared utility in rmon-agent::sources.
Storage Modes
The agent can store signal data in two modes per signal group:
Continuous Storage
StorageMode::Continuous
- Data is appended to an on-disk log starting when recording is requested
- Data is kept until explicitly deleted (either via UI command or manual deletion)
- File format: binary append log of
(i64 timestamp, f64 value)pairs, one file per signal - File path:
{storage_dir}/{signal_id_escaped}/{start_timestamp}.bin
Circular (Loop) Storage
StorageMode::Circular { window: Duration }
- Agent maintains an in-memory ring buffer of the last
windowduration - On UI request, can also persist to disk (same format as continuous)
windowis configured per signal group in the agent config
Storage Location
Configured per agent in config.toml. Defaults to ~/.rmon/data/. The agent never deletes data automatically (except from in-memory circular buffers).
Signal Tree
For UI navigation, signals are organized into a tree using the path field of SignalInfo. Each element of the Vec<String> is a tree level.
Example paths:
["system", "imu", "accelerometer"] → system > imu > accelerometer > {signals}
["temperatures", "coolant"] → temperatures > coolant > {signals}
The UI renders this as a collapsible tree panel. Signals can be dragged from the tree onto plot panels.
UI-Side Signal Cache
The UI maintains a per-signal ring buffer for display (configurable, default: last 60 seconds). Incoming DataBatch messages are appended to this buffer. Historical query responses fill in older portions.
The display buffer is separate from the agent's storage — it only holds what the UI needs to render the current view.