BEAM:CURRENT and ORBIT:XPOS —
only process the pair if both arrive in the same polling window.
Discard if either times out."
rxcpp::observable<>::interval(500ms)
.flat_map([&ctx](long) {
// Both reads fire on background threads in parallel
return rxcpp::observable<>::zip(
[](double c, double x) {
return process(c, x); },
rxepics::read_pv<double>(
"BEAM:CURRENT", ctx),
rxepics::read_pv<double>(
"ORBIT:XPOS", ctx)
);
})
.subscribe([](auto result) {
publish(result); });
.as<T>() — template selects type.
TEST:CALC (10 Hz IOC push), compute a 5-sample
sliding average, write back only if it deviates more than 10% from the
last written value."
rxepics::monitor_pv<double>("TEST:CALC")
// sliding window — no deque, no index arithmetic
.buffer(5, 1)
.map([](const auto& w) {
return std::accumulate(
w.begin(), w.end(), 0.0) / w.size();
})
// skip write if within 10% of last value
.distinct_until_changed(
[](double p, double c) {
return std::abs(c-p)/p < 0.1; })
.flat_map([&ctx](double avg) {
return rxepics::write_pv<double>(
"TEST:DOUBLE", avg, ctx); });
rxcpp::observable<>::merge(
rxepics::monitor_pv<double>("PV:ONE"),
rxepics::monitor_pv<double>("PV:TWO"),
rxepics::monitor_pv<double>("PV:THREE")
)
.filter([](double v) {
return std::abs(v) > THRESHOLD; })
.subscribe([](double v) {
notify_operator(v); });
Single-shot reads via ctx.get(pv).exec()->wait();
writes via ctx.put(pv).set("value",v).exec();
push via ctx.monitor(pv).event(cb).exec().
No commands — write to a command PV instead.
read_pv<T> · write_pv<T>
Dispatched on std::thread; emits one value then completes.
5 s timeout via wait(5.0).
monitor_pv<T> — PVXS monitor callback delivers values
to rxcpp::subscriber<T> via shared_ptr<mutex>.
Dispose destroys the Monitor handle → subscription cancelled.
flat_map · zip · merge ·
buffer · filter · scan ·
sample_with_time · distinct_until_changed
Write-back, alarm, HMI — pure functions, no shared state.
First EPICS subproject in the suite with a ReactiveX conformance test.
rxepics::EpicsClient()
.read("TEST:CALC")
.map([](std::any v) -> std::any {
return std::any_cast<double>(v) * 1.0328;
})
.write("TEST:DOUBLE")
.subscribe(
[](std::any v) {
std::cout << std::any_cast<double>(v);
});
EPICS_PVA_ADDR_LIST=localhost ./build/tests/verify_contract