From 9cd175f3c8cd9d495ab14f27bfbad79de032914e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 30 Jan 2025 12:30:08 -0800 Subject: [PATCH] ioc: fix DBE_ARCHIVE handling w/ singlesource Stop ignoring DBE_ARCHIVE. --- documentation/ioc.rst | 16 ++++++++++++++++ ioc/singlesource.cpp | 10 +++++++--- ioc/subscriptionctx.h | 2 ++ test/testpvreq.cpp | 13 ++++++++++++- test/testqsingle.cpp | 32 +++++++++++++++++++++++++++++++- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/documentation/ioc.rst b/documentation/ioc.rst index aeef288ef..43ff2a9bd 100644 --- a/documentation/ioc.rst +++ b/documentation/ioc.rst @@ -128,6 +128,22 @@ Currently supported format hints are: - Exponential - Engineering +When subscribing, the client provided ``pvRequest`` Value may contain the field +``records._options.DBE`` with a string or unsigned integer. +This value is mapped to a DataBase Event (DBE) bit mask. + +``DBE`` may be either a string or an unsigned integer. +Use of an integer is recommended. +The ``DBE_*`` macros from the ``caeventmask.h`` header may be used +(``DBE_VALUE=1``, ``DBE_ARCHIVE=2``, ``DBE_ALARM=4``). +``DBE_PROPERTY`` will be ignored if given as this event is always +handled specially. +Alternately, ``DBE`` may be a string using the old caProvider "parsing" +rules. This is not recommended. + +Until UNRELEASED ``DBE_ARCHIVE`` was not handled correctly. + + Group PV ^^^^^^^^ diff --git a/ioc/singlesource.cpp b/ioc/singlesource.cpp index 511843bb8..8d0ec1852 100644 --- a/ioc/singlesource.cpp +++ b/ioc/singlesource.cpp @@ -75,14 +75,18 @@ void subscriptionValueCallback(void* userArg, struct dbChannel* pChannel, int, struct db_field_log* pDbFieldLog) noexcept { auto subscriptionContext = (SingleSourceSubscriptionCtx*)userArg; subscriptionContext->hadValueEvent = true; - auto change = UpdateType::type(UpdateType::Value | UpdateType::Alarm); + auto change = subscriptionContext->pValueEventSubscription.mask; #if EPICS_VERSION_INT >= VERSION_INT(7, 0, 6, 0) if(pDbFieldLog) { // when available, use DBE mask from db_field_log - change = UpdateType::type(pDbFieldLog->mask & UpdateType::Everything); + change = pDbFieldLog->mask; } #endif - subscriptionCallback(subscriptionContext, change, pChannel, pDbFieldLog); + // ARCHIVE events will get the same data fields as VALUE + if(change & DBE_ARCHIVE) + change = (change&~DBE_ARCHIVE)|DBE_VALUE; + change &= UpdateType::Everything; // does not include DBE_ARCHIVE + subscriptionCallback(subscriptionContext, UpdateType::type(change), pChannel, pDbFieldLog); } void subscriptionPropertiesCallback(void* userArg, struct dbChannel* pChannel, int, diff --git a/ioc/subscriptionctx.h b/ioc/subscriptionctx.h index 2b3f3246a..b89844986 100644 --- a/ioc/subscriptionctx.h +++ b/ioc/subscriptionctx.h @@ -25,6 +25,7 @@ namespace ioc { class Subscription { std::shared_ptr::type> sub; // holds void* returned by db_add_event() public: + unsigned mask=0; /* Add a subscription event by calling db_add_event using the given subscriptionCtx * and selecting the correct elements based on the given type of event being added. * You need to specify the correct options that correspond to the event type. @@ -45,6 +46,7 @@ class Subscription { }); if(!sub) throw std::runtime_error("Failed to create db subscription"); + mask = select; } void cancel() { sub.reset(); diff --git a/test/testpvreq.cpp b/test/testpvreq.cpp index f87f5078d..68fc15327 100644 --- a/test/testpvreq.cpp +++ b/test/testpvreq.cpp @@ -390,11 +390,21 @@ void testArgs() ); } +void testDBE() +{ + testShow()<<__func__; + + { + auto v(client::Context::request().pvRequest("record[DBE=VALUE]").build()); + testEq(v["record._options.DBE"].as(), "VALUE"); + } +} + } // namespace MAIN(testpvreq) { - testPlan(38); + testPlan(39); testSetup(); logger_config_env(); testPvRequest(); @@ -409,6 +419,7 @@ MAIN(testpvreq) testError(); testBuilder(); testArgs(); + testDBE(); cleanup_for_valgrind(); return testDone(); } diff --git a/test/testqsingle.cpp b/test/testqsingle.cpp index 0b1721409..33ed7b7de 100644 --- a/test/testqsingle.cpp +++ b/test/testqsingle.cpp @@ -18,6 +18,7 @@ #include #include +#include "dblocker.h" #include "testioc.h" #include "utilpvt.h" @@ -874,11 +875,39 @@ void testMonitorAIFilt(TestClient& ctxt) sub2.testEmpty(); } +void testMonitorDBE(TestClient& ctxt) +{ + testDiag("%s", __func__); + + TestSubscription sub(ctxt.monitor("test:ai.TPRO") + .record("DBE", DBE_ARCHIVE) + .maskConnected(true) + .maskDisconnected(true)); + + auto val(sub.waitForUpdate()); + testShow()<tpro = 42; // event discarded + db_post_events(prec, &prec->tpro, DBE_VALUE); + prec->tpro = 43; + db_post_events(prec, &prec->tpro, DBE_VALUE|DBE_ARCHIVE); + } + + val = sub.waitForUpdate(); + testShow()<(), 43); +} + } // namespace MAIN(testqsingle) { - testPlan(88); + testPlan(89); testSetup(); pvxs::logger_config_env(); generalTimeRegisterCurrentProvider("test", 1, &testTimeCurrent); @@ -921,6 +950,7 @@ MAIN(testqsingle) testMonitorAI(mctxt); testMonitorBO(mctxt); testMonitorAIFilt(mctxt); + testMonitorDBE(mctxt); } timeSim = false; testPutBlock();