From 1a47e5fb0af46cec0f6a8b9fd911dd40fd6aae3b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 18 Jul 2017 14:02:14 +0200 Subject: [PATCH] pvac: add synchronous put to 'value' field --- documentation/examples.h | 9 +++ documentation/mainpage.h | 1 + examples/Makefile | 1 + examples/miniput.cpp | 33 ++++++++++ src/client/clientSync.cpp | 133 ++++++++++++++++++++++++++++++++++---- src/client/pva/client.h | 32 ++++++++- 6 files changed, 196 insertions(+), 13 deletions(-) create mode 100644 examples/miniput.cpp diff --git a/documentation/examples.h b/documentation/examples.h index 188f7b6..83997ca 100644 --- a/documentation/examples.h +++ b/documentation/examples.h @@ -9,6 +9,15 @@ The shortest possible PVA get() example. */ /** +@page examples_miniput Simple Client Put Example + +The shortest possible PVA put() example. + +@include miniput.cpp + +*/ +/** + @page examples_getme Client Get Example This example demonstrates a client which issues a Get operation on startup, diff --git a/documentation/mainpage.h b/documentation/mainpage.h index 1994291..afe1571 100644 --- a/documentation/mainpage.h +++ b/documentation/mainpage.h @@ -15,6 +15,7 @@ - Simplest (shortest) possible - @ref examples_miniget + - @ref examples_miniput - More complete - @ref examples_getme - @ref examples_putme diff --git a/examples/Makefile b/examples/Makefile index f031a4f..117db1f 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -10,6 +10,7 @@ TESTPROD_HOST += monitorme TESTPROD_HOST += spamme TESTPROD_HOST += miniget +TESTPROD_HOST += miniput include $(TOP)/configure/RULES #---------------------------------------- diff --git a/examples/miniput.cpp b/examples/miniput.cpp new file mode 100644 index 0000000..a360e6e --- /dev/null +++ b/examples/miniput.cpp @@ -0,0 +1,33 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +// The simplest possible PVA put + +#include + +#include "pva/client.h" + +int main(int argc, char *argv[]) +{ + try { + if(argc<=2) { + std::cerr<<"Usage: "< \n"; + return 1; + } + + pvac::ClientProvider provider("pva"); + + pvac::ClientChannel channel(provider.connect(argv[1])); + + std::cout<<"Before "<(argv[2]); + + std::cout<<"After "< Guard; typedef epicsGuardRelease UnGuard; namespace { -struct GetWait : pvac::ClientChannel::GetCallback +struct WaitCommon { epicsMutex mutex; epicsEvent event; bool done; + + WaitCommon() :done(false) {} + void wait(double timeout) + { + Guard G(mutex); + while(!done) { + UnGuard U(G); + if(!event.wait(timeout)) { + throw pvac::Timeout(); + } + } + } +}; + +struct GetWait : public pvac::ClientChannel::GetCallback, + public WaitCommon +{ pvac::GetEvent result; - GetWait() :done(false) {} + GetWait() {} virtual ~GetWait() {} virtual void getDone(const pvac::GetEvent& evt) { @@ -55,16 +72,7 @@ pvac::ClientChannel::get(double timeout, { GetWait waiter; Operation op(get(&waiter, pvRequest)); - { - Guard G(waiter.mutex); - while(!waiter.done) { - UnGuard U(G); - if(!waiter.event.wait(timeout)) { - op.cancel(); - throw Timeout(); - } - } - } + waiter.wait(timeout); if(waiter.result.event==pvac::GetEvent::Success) return waiter.result.value; else @@ -94,4 +102,105 @@ pvac::ClientChannel::rpc(double timeout, throw std::runtime_error(waiter.result.message); } +namespace { +struct PutValCommon : public pvac::ClientChannel::PutCallback, + public WaitCommon +{ + pvac::PutEvent result; + + PutValCommon() {} + virtual ~PutValCommon() {} + + virtual void putDone(const PutEvent& evt) + { + { + Guard G(mutex); + if(done) { + LOG(pva::logLevelWarn, "oops, double event to PutCallback"); + } else { + result = evt; + done = true; + } + } + event.signal(); + } +}; + +struct PutValScalar : public PutValCommon +{ + const void* value; + pvd::ScalarType vtype; + + PutValScalar(const void* value, pvd::ScalarType vtype) :value(value), vtype(vtype) {} + virtual ~PutValScalar() {} + + virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) + { + pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build)); + pvd::PVScalarPtr value(root->getSubField("value")); + if(value) { + value->putFrom(this->value, vtype); + args.tosend.set(value->getFieldOffset()); + } else { + // TODO: handle enum + throw std::runtime_error("PV has no scalar 'value' sub-field"); + } + args.root = root; + } + +}; + +struct PutValArray : public PutValCommon +{ + pvd::shared_vector arr; + + PutValArray(const pvd::shared_vector& arr) :arr(arr) {} + virtual ~PutValArray() {} + + virtual void putBuild(const epics::pvData::StructureConstPtr& build, Args& args) + { + pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(build)); + pvd::PVScalarArrayPtr value(root->getSubField("value")); + if(value) { + value->putFrom(arr); + args.tosend.set(value->getFieldOffset()); + } else { + throw std::runtime_error("PV has no scalar array 'value' sub-field"); + } + args.root = root; + } + +}; +} //namespace + +void +ClientChannel::putValue(const void* value, + pvd::ScalarType vtype, + double timeout, + pvd::PVStructure::const_shared_pointer pvRequest) +{ + PutValScalar waiter(value, vtype); + Operation op(put(&waiter, pvRequest)); + waiter.wait(timeout); + if(waiter.result.event==PutEvent::Success) + return; + else + throw std::runtime_error(waiter.result.message); +} + +void +ClientChannel::putValue(const epics::pvData::shared_vector& value, + double timeout, + epics::pvData::PVStructure::const_shared_pointer pvRequest) +{ + PutValArray waiter(value); + Operation op(put(&waiter, pvRequest)); + waiter.wait(timeout); + if(waiter.result.event==PutEvent::Success) + return; + else + throw std::runtime_error(waiter.result.message); +} + + }//namespace pvac diff --git a/src/client/pva/client.h b/src/client/pva/client.h index ca39e71..c0a8c11 100644 --- a/src/client/pva/client.h +++ b/src/client/pva/client.h @@ -135,7 +135,15 @@ struct epicsShareClass Timeout : public std::runtime_error Timeout(); }; -//! Represents a single channel +/** Represents a single channel + * + * This class has two sets of methods, those which block for completion, and + * those which use callbacks to signal completion. + * + * Those which block accept a 'timeout' argument (in seconds). + * + * Those which use callbacks accept a 'cb' argument and return an Operation or Monitor handle object. + */ class epicsShareClass ClientChannel { struct Impl; @@ -233,6 +241,28 @@ public: Operation put(PutCallback* cb, epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()); + //! Put to the 'value' field and block until complete. + //! Accepts a scalar value + template + inline void putValue(typename epics::pvData::meta::arg_type::type>::type value, + double timeout = 3.0, + epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()) + { + putValue(&value, ID, timeout, pvRequest); + } + + //! Put to the 'value' field and block until complete. + //! Accepts untyped scalar value + void putValue(const void* value, epics::pvData::ScalarType vtype, + double timeout, + epics::pvData::PVStructure::const_shared_pointer pvRequest); + + //! Put to the 'value' field and block until complete. + //! Accepts scalar array + void putValue(const epics::pvData::shared_vector& value, + double timeout, + epics::pvData::PVStructure::const_shared_pointer pvRequest); + //! Monitor event notification struct epicsShareClass MonitorCallback { virtual ~MonitorCallback() {}