Monitoring changes
✅ Verified ·
pvmonitor.rs·demo_monitor.py· checkdocs_verify·The badge reports the whole
docs-verifysuite, not this chapter alone.
What you'll build
A subscription: one request, then updates pushed by the server for as long as you care to listen. This is what PVAccess is actually for — polling a PV in a loop is almost always the wrong answer.
Rust
#![allow(unused)] fn main() { let client = PvaClient::builder().build(); let cb = |value: &spvirit_codec::spvd_decode::DecodedValue| { println!("{value}"); ControlFlow::Continue(()) }; let refs: Vec<&str> = fields.iter().map(String::as_str).collect(); if let Some(q) = pipeline { client .pvmonitor_with_options(&pv, &refs, MonitorOptions::pipelined(q), cb) .await?; } else if fields.is_empty() { client.pvmonitor(&pv, cb).await?; } else { client.pvmonitor_fields(&pv, &refs, cb).await?; } }
The callback returns a ControlFlow: Continue(()) to keep going,
Break(()) to unsubscribe. pvmonitor runs until the callback breaks or
the connection drops.
MonitorOptions::pipelined(q) asks the server for flow control with a
queue depth of q — useful on high-rate PVs where a slow consumer would
otherwise fall behind.
Python
client = spvirit.Client()
seen = 0
def on_update(value):
global seen
seen += 1
print(f"{seen}: {value['value']:.3f}")
return seen < 5 # returning False ends the monitor
client.monitor("SIM:TEMPERATURE", on_update)
client.monitor(...) blocks until the callback returns False or
raises. For a non-blocking version use client.subscribe(...), which
returns a Subscription you can close(), and which runs the callback on
a background thread:
sub = client.subscribe("SIM:TEMPERATURE", on_update)
...
sub.close()
If a subscription ends on a network error, sub.error holds the message
and sub.is_active becomes False — worth checking, because a silently
dead subscription looks exactly like a quiet PV.
From the command line
$ spmonitor SIM:TEMPERATURE
What to notice
The first update is the current value. A subscription delivers the present state immediately, then changes. You do not need a GET before a monitor.
Monitors respect the record's MDEL. This is the single most useful
thing on this page. A record with mdel=1.0 posts an update only when the
value has moved at least 1.0 from the last posted value — not from the
last set value. Writing 0.1, 0.2, 0.3, 5.0, 5.1, 5.2, 20.0 to such a PV
delivers three updates:
posted to monitor: [0.0, 5.0, 20.0]
0.0 is the initial value on subscribe; 5.0 and 20.0 each cleared the
deadband. The intermediate writes landed in the record — a GET would show
5.2 — they just were not broadcast.
MDEL defaults to 0, meaning no deadband. A record you never gave an
mdel posts every change. On a 1 kHz PV with a hundred subscribers, that
is a decision, so make it deliberately.
Severity changes always get through. The deadband is bypassed when the
alarm severity changes, so a PV crossing into MAJOR is never silently
swallowed by a large MDEL.
ADEL is not the same thing. ADEL is the archive deadband; it is
parsed, stored, and served over field access, but PVAccess monitors use
MDEL.
Deadbands are a record-level feature. A raw-NT source posting with
put_nt has no MDEL to consult, and every post goes out. See
Records vs raw NT.
Run it
# Terminal 1
python spvirit-py/examples/demo_scan.py
# Terminal 2
cargo run -p spvirit-client --example pvmonitor -- SIM:TEMPERATURE
# or
python spvirit-py/examples/demo_monitor.py
# or
spmonitor SIM:TEMPERATURE
demo_scan.py updates ten times a second, so terminal 2 fills immediately:
$ spmonitor SIM:TEMPERATURE
SIM:TEMPERATURE 2026-08-06 09:25:29.221 21.677613
SIM:TEMPERATURE 21.734644
SIM:TEMPERATURE 21.8092
SIM:TEMPERATURE 21.89275
SIM:TEMPERATURE 21.956506
SIM:TEMPERATURE 22.051046
SIM:TEMPERATURE 22.151852
SIM:TEMPERATURE 22.240413
SIM:TEMPERATURE 2026-08-06 09:25:30.028 22.347997
SIM:TEMPERATURE 22.440441
SIM:TEMPERATURE 22.534399
...
spmonitor prints the timestamp only on the first update of each
wall-clock second — that is a display convenience, not a change in the
data. Every update carries a full timestamp on the wire. Counting the
lines between two timestamps is a quick way to see your actual update rate.
Stop it with Ctrl-C.