From bc2dccf95c65a032c77e10d27f291f0c8f96119e Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 7 Jul 2017 13:42:45 +0200 Subject: [PATCH] start on pvaTestClient --- src/Makefile | 1 + src/testing/Makefile | 10 ++ src/testing/pv/pvaTestClient.h | 164 +++++++++++++++++++++ src/testing/pvaTestClient.cpp | 159 ++++++++++++++++++++ src/testing/pvaTestClientGet.cpp | 205 ++++++++++++++++++++++++++ src/testing/pvaTestClientMonitor.cpp | 209 +++++++++++++++++++++++++++ src/testing/pvaTestClientRPC.cpp | 155 ++++++++++++++++++++ 7 files changed, 903 insertions(+) create mode 100644 src/testing/Makefile create mode 100644 src/testing/pv/pvaTestClient.h create mode 100644 src/testing/pvaTestClient.cpp create mode 100644 src/testing/pvaTestClientGet.cpp create mode 100644 src/testing/pvaTestClientMonitor.cpp create mode 100644 src/testing/pvaTestClientRPC.cpp diff --git a/src/Makefile b/src/Makefile index 631c60e..18687a2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,6 +23,7 @@ include $(PVACCESS_SRC)/rpcService/Makefile include $(PVACCESS_SRC)/rpcClient/Makefile include $(PVACCESS_SRC)/pipelineService/Makefile include $(PVACCESS_SRC)/ca/Makefile +include $(PVACCESS_SRC)/testing/Makefile include $(PVACCESS_SRC)/mb/Makefile LIBRARY = pvAccess diff --git a/src/testing/Makefile b/src/testing/Makefile new file mode 100644 index 0000000..72be481 --- /dev/null +++ b/src/testing/Makefile @@ -0,0 +1,10 @@ +# This is a Makefile fragment, see ../Makefile + +SRC_DIRS += $(PVACCESS_SRC)/testing + +INC += pv/pvaTestClient.h + +LIBSRCS += pvaTestClient.cpp +LIBSRCS += pvaTestClientGet.cpp +LIBSRCS += pvaTestClientRPC.cpp +LIBSRCS += pvaTestClientMonitor.cpp diff --git a/src/testing/pv/pvaTestClient.h b/src/testing/pv/pvaTestClient.h new file mode 100644 index 0000000..fe9490c --- /dev/null +++ b/src/testing/pv/pvaTestClient.h @@ -0,0 +1,164 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +#ifndef PVATESTCLIENT_H +#define PVATESTCLIENT_H + +#include + +#include +#include + +namespace epics {namespace pvAccess { +class ChannelProvider; +class Channel; +class Monitor; +class Configuration; +}}//namespace epics::pvAccess + +struct epicsShareClass TestOperation +{ + struct Impl + { + virtual ~Impl() {} + virtual std::string name() const =0; + virtual void cancel() =0; + }; + + TestOperation() {} + TestOperation(const std::tr1::shared_ptr&); + ~TestOperation(); + std::string name() const; + void cancel(); + +protected: + std::tr1::shared_ptr impl; +}; + +struct epicsShareClass TestPutEvent +{ + enum event_t { + Fail, + Cancel, + Success, + } event; + std::string message; + void *priv; +}; + +struct epicsShareClass TestGetEvent : public TestPutEvent +{ + epics::pvData::PVStructure::const_shared_pointer value; +}; + +struct epicsShareClass TestMonitor +{ + struct Impl; + TestMonitor() {} + TestMonitor(const std::tr1::shared_ptr&); + ~TestMonitor(); + + std::string name() const; + void cancel(); + //! updates root, changed, overrun + //! return true if root!=NULL + bool poll(); + //! true if all events received. + bool complete() const; + epics::pvData::PVStructure::const_shared_pointer root; + epics::pvData::BitSet changed, + overrun; + +protected: + std::tr1::shared_ptr impl; +}; + +struct epicsShareClass TestMonitorEvent +{ + enum event_t { + Fail=1, // subscription ends in an error + Cancel=2, // subscription ends in cancellation + Disconnect=4,// subscription interrupted to do lose of communication + Data=8, // Data queue not empty + } event; + std::string message; // set for event=Fail + void *priv; +}; + +struct epicsShareClass TestConnectEvent +{ + bool connected; +}; + +class epicsShareClass TestClientChannel +{ + struct Impl; + std::tr1::shared_ptr impl; +public: + struct Options { + short priority; + std::string address; + Options(); + }; + + TestClientChannel() {} + TestClientChannel(const std::tr1::shared_ptr& provider, + const std::string& name, + const Options& opt = Options()); + ~TestClientChannel(); + + struct epicsShareClass GetCallback { + virtual ~GetCallback() {} + virtual void getDone(const TestGetEvent& evt)=0; + }; + + TestOperation get(GetCallback* cb, + epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()); + + TestOperation rpc(GetCallback* cb, + const epics::pvData::PVStructure::const_shared_pointer& arguments, + epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()); + + struct epicsShareClass PutCallback { + virtual ~PutCallback() {} + virtual epics::pvData::PVStructure::const_shared_pointer putBuild(const epics::pvData::StructureConstPtr& build) =0; + virtual void putDone(const TestPutEvent& evt)=0; + }; + + TestOperation put(PutCallback* cb, + epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()); + + struct epicsShareClass MonitorCallback { + virtual ~MonitorCallback() {} + virtual void monitorEvent(const TestMonitorEvent& evt)=0; + }; + + TestMonitor monitor(MonitorCallback *cb, + epics::pvData::PVStructure::const_shared_pointer pvRequest = epics::pvData::PVStructure::const_shared_pointer()); + + struct epicsShareClass ConnectCallback { + virtual ~ConnectCallback() {} + virtual void connectEvent(const TestConnectEvent& evt)=0; + }; + void addConnectListener(ConnectCallback*); + void removeConnectListener(ConnectCallback*); + +private: + std::tr1::shared_ptr getChannel(); +}; + +class epicsShareClass TestClientProvider +{ + std::tr1::shared_ptr provider; +public: + + TestClientProvider(const std::string& providerName, + const std::tr1::shared_ptr& conf = std::tr1::shared_ptr()); + ~TestClientProvider(); + + TestClientChannel connect(const std::string& name, + const TestClientChannel::Options& conf = TestClientChannel::Options()); +}; + +#endif // PVATESTCLIENT_H diff --git a/src/testing/pvaTestClient.cpp b/src/testing/pvaTestClient.cpp new file mode 100644 index 0000000..80dde2d --- /dev/null +++ b/src/testing/pvaTestClient.cpp @@ -0,0 +1,159 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +#include +#include + +#include +#include + +#define epicsExportSharedSymbols +#include "pv/logger.h" +#include "pv/pvaTestClient.h" +#include "pv/pvAccess.h" +#include "pv/configuration.h" + +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; + +typedef epicsGuard Guard; + +struct TestClientChannel::Impl : public pva::ChannelRequester +{ + epicsMutex mutex; + pva::Channel::shared_pointer channel; + // assume few listeners per channel, store in vector + typedef std::vector listeners_t; + listeners_t listeners; + + virtual ~Impl() {} + + virtual std::string getRequesterName() OVERRIDE FINAL { return "TestClientChannel::Impl"; } + + virtual void channelCreated(const pvd::Status& status, pva::Channel::shared_pointer const & channel) OVERRIDE FINAL {} + + virtual void channelStateChange(pva::Channel::shared_pointer const & channel, pva::Channel::ConnectionState connectionState) OVERRIDE FINAL + { + listeners_t notify; + { + Guard G(mutex); + notify = listeners; + } + TestConnectEvent evt; + evt.connected = connectionState==pva::Channel::CONNECTED; + for(listeners_t::const_iterator it=notify.begin(), end=notify.end(); it!=end; ++it) + { + try { + (*it)->connectEvent(evt); + }catch(std::exception& e){ + LOG(pva::logLevelError, "Unhandled exception in connection state listener: %s\n", e.what()); + + Guard G(mutex); + for(listeners_t::iterator it2=listeners.begin(), end2=listeners.end(); it2!=end2; ++it2) { + if(*it==*it2) { + listeners.erase(it2); + break; + } + } + } + } + } +}; + +TestClientChannel::Options::Options() + :priority(0) + ,address() +{} + +TestOperation::TestOperation(const std::tr1::shared_ptr& i) + :impl(i) +{} + +TestOperation::~TestOperation() {} + + +std::string TestOperation::name() const +{ + return impl ? impl->name() : ""; +} + +void TestOperation::cancel() +{ + if(impl) impl->cancel(); +} + +TestClientChannel::TestClientChannel(const std::tr1::shared_ptr& provider, + const std::string& name, + const Options& opt) + :impl(new Impl) +{ + if(!provider) + throw std::logic_error("NULL ChannelProvider"); + impl->channel = provider->createChannel(name, impl, opt.priority, opt.address); + if(!impl->channel) + throw std::logic_error("ChannelProvider failed to create Channel"); +} + +TestClientChannel::~TestClientChannel() {} + +void TestClientChannel::addConnectListener(ConnectCallback* cb) +{ + if(!impl) throw std::logic_error("Dead Channel"); + TestConnectEvent evt; + { + Guard G(impl->mutex); + + for(Impl::listeners_t::const_iterator it=impl->listeners.begin(), end=impl->listeners.end(); it!=end; ++it) + { + if(cb==*it) return; // no duplicates + } + impl->listeners.push_back(cb); + evt.connected = impl->channel->isConnected(); + } + try{ + cb->connectEvent(evt); + }catch(...){ + removeConnectListener(cb); + throw; + } +} + +void TestClientChannel::removeConnectListener(ConnectCallback* cb) +{ + if(!impl) throw std::logic_error("Dead Channel"); + Guard G(impl->mutex); + + for(Impl::listeners_t::iterator it=impl->listeners.begin(), end=impl->listeners.end(); it!=end; ++it) + { + if(cb==*it) { + impl->listeners.erase(it); + return; + } + } +} + +std::tr1::shared_ptr +TestClientChannel::getChannel() +{ return impl->channel; } + +TestClientProvider::TestClientProvider(const std::string& providerName, + const std::tr1::shared_ptr& conf) + :provider(pva::ChannelProviderRegistry::clients()->createProvider(providerName, + conf ? conf : pva::ConfigurationBuilder() + .push_env() + .build())) +{ + if(!provider) + THROW_EXCEPTION2(std::invalid_argument, providerName); +} + +TestClientProvider::~TestClientProvider() {} + +TestClientChannel +TestClientProvider::connect(const std::string& name, + const TestClientChannel::Options& conf) +{ + return TestClientChannel(provider, name, conf); +} diff --git a/src/testing/pvaTestClientGet.cpp b/src/testing/pvaTestClientGet.cpp new file mode 100644 index 0000000..569e784 --- /dev/null +++ b/src/testing/pvaTestClientGet.cpp @@ -0,0 +1,205 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +#include +#include + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "pv/logger.h" +#include "pv/pvaTestClient.h" +#include "pv/pvAccess.h" + +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; + +namespace { + +struct GetPutter : public pva::ChannelPutRequester, + public TestOperation::Impl +{ + mutable epicsMutex mutex; + + bool started; + operation_type::shared_pointer op; + + TestClientChannel::GetCallback *getcb; + TestClientChannel::PutCallback *putcb; + TestGetEvent event; + + GetPutter(TestClientChannel::GetCallback* cb) :started(false), getcb(cb), putcb(0) {} + GetPutter(TestClientChannel::PutCallback* cb) :started(false), getcb(0), putcb(cb) {} + virtual ~GetPutter() {} + + void callEvent(Guard& G, TestGetEvent::event_t evt = TestGetEvent::Fail) + { + if(!putcb && !getcb) return; + + event.event = evt; + if(putcb) { + TestClientChannel::PutCallback *cb=putcb; + putcb = 0; + UnGuard U(G); + cb->putDone(event); + } + if(getcb) { + TestClientChannel::GetCallback *cb=getcb; + getcb = 0; + UnGuard U(G); + cb->getDone(event); + } + } + + virtual std::string name() const OVERRIDE FINAL + { + Guard G(mutex); + return op ? op->getChannel()->getChannelName() : ""; + } + + virtual void cancel() OVERRIDE FINAL + { + Guard G(mutex); + if(started && op) op->cancel(); + callEvent(G, TestGetEvent::Cancel); + } + + virtual std::string getRequesterName() OVERRIDE FINAL + { return "GetPutter"; } + + virtual void channelPutConnect( + const epics::pvData::Status& status, + pva::ChannelPut::shared_pointer const & channelPut, + epics::pvData::Structure::const_shared_pointer const & structure) OVERRIDE FINAL + { + Guard G(mutex); + if(started) return; + if(!putcb && !getcb) return; + + if(!status.isOK()) { + event.message = status.getMessage(); + } else { + event.message.clear(); + } + if(!status.isSuccess()) { + callEvent(G); + + } else if(getcb){ + channelPut->lastRequest(); + channelPut->get(); + started = true; + + } else if(putcb){ + TestClientChannel::PutCallback *cb(putcb); + pvd::PVStructure::const_shared_pointer val; + try { + UnGuard U(G); + val = cb->putBuild(structure); + }catch(std::exception& e){ + if(putcb) { + event.message = e.what(); + callEvent(G); + } else { + LOG(pva::logLevelInfo, "Lost exception in %s: %s", CURRENT_FUNCTION, e.what()); + } + } + if(putcb) { + pvd::BitSet::shared_pointer all(new pvd::BitSet); + all->set(0); + channelPut->lastRequest(); + channelPut->put(std::tr1::const_pointer_cast(val), all); + started = true; + } + } + } + + virtual void channelDisconnect(bool destroy) OVERRIDE FINAL + { + Guard G(mutex); + event.message = "Disconnect"; + + callEvent(G); + } + + virtual void putDone( + const epics::pvData::Status& status, + pva::ChannelPut::shared_pointer const & channelPut) OVERRIDE FINAL + { + Guard G(mutex); + if(!getcb) return; + + if(!status.isOK()) { + event.message = status.getMessage(); + } else { + event.message.clear(); + } + + callEvent(G, status.isSuccess()? TestGetEvent::Success : TestGetEvent::Fail); + } + + virtual void getDone( + const epics::pvData::Status& status, + pva::ChannelPut::shared_pointer const & channelPut, + epics::pvData::PVStructure::shared_pointer const & pvStructure, + epics::pvData::BitSet::shared_pointer const & bitSet) OVERRIDE FINAL + { + Guard G(mutex); + if(!getcb) return; + + if(!status.isOK()) { + event.message = status.getMessage(); + } else { + event.message.clear(); + } + event.value = pvStructure; + // assume bitSet->get(0)==true as we only make one request + + callEvent(G, status.isSuccess()? TestGetEvent::Success : TestGetEvent::Fail); + } +}; + +} //namespace + +TestOperation +TestClientChannel::get(TestClientChannel::GetCallback* cb, + epics::pvData::PVStructure::const_shared_pointer pvRequest) +{ + if(!impl) throw std::logic_error("Dead Channel"); + if(!pvRequest) + pvRequest = pvd::createRequest("field()"); + + std::tr1::shared_ptr ret(new GetPutter(cb)); + + { + Guard G(ret->mutex); + ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast(pvRequest)); + } + + return TestOperation(ret); +} + + +TestOperation +TestClientChannel::put(PutCallback* cb, + epics::pvData::PVStructure::const_shared_pointer pvRequest) +{ + if(!impl) throw std::logic_error("Dead Channel"); + if(!pvRequest) + pvRequest = pvd::createRequest("field()"); + + std::tr1::shared_ptr ret(new GetPutter(cb)); + + { + Guard G(ret->mutex); + ret->op = getChannel()->createChannelPut(ret, std::tr1::const_pointer_cast(pvRequest)); + } + + return TestOperation(ret); + +} diff --git a/src/testing/pvaTestClientMonitor.cpp b/src/testing/pvaTestClientMonitor.cpp new file mode 100644 index 0000000..c51a790 --- /dev/null +++ b/src/testing/pvaTestClientMonitor.cpp @@ -0,0 +1,209 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +#include +#include + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "pv/logger.h" +#include "pv/pvaTestClient.h" +#include "pv/pvAccess.h" + +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; + +struct TestMonitor::Impl : public pva::MonitorRequester +{ + mutable epicsMutex mutex; + pva::Channel::shared_pointer chan; + operation_type::shared_pointer op; + bool started, done, seenEmpty; + + TestClientChannel::MonitorCallback *cb; + TestMonitorEvent event; + + pva::MonitorElement::Ref last; + + Impl(TestClientChannel::MonitorCallback* cb) + :started(false) + ,done(false) + ,seenEmpty(false) + ,cb(cb) + {} + virtual ~Impl() {} + + void callEvent(Guard& G, TestMonitorEvent::event_t evt = TestMonitorEvent::Fail) + { + TestClientChannel::MonitorCallback *cb=this->cb; + if(!cb) return; + + event.event = evt; + + if(evt==TestMonitorEvent::Fail || evt==TestMonitorEvent::Cancel) + this->cb = 0; // last event + try { + UnGuard U(G); + cb->monitorEvent(event); + return; + }catch(std::exception& e){ + if(!this->cb || evt==TestMonitorEvent::Fail) { + LOG(pva::logLevelError, "Unhandled exception in TestClientChannel::MonitorCallback::monitorEvent(): %s", e.what()); + } else { + event.event = TestMonitorEvent::Fail; + event.message = e.what(); + } + } + // continues error handling + try { + UnGuard U(G); + cb->monitorEvent(event); + return; + }catch(std::exception& e){ + LOG(pva::logLevelError, "Unhandled exception following exception in TestClientChannel::MonitorCallback::monitorEvent(): %s", e.what()); + } + } + + + virtual std::string getRequesterName() OVERRIDE FINAL + { return "RPCer"; } + + + virtual void monitorConnect(pvd::Status const & status, + pva::MonitorPtr const & operation, + pvd::StructureConstPtr const & structure) OVERRIDE FINAL + { + Guard G(mutex); + if(!cb || started || done) return; + + if(!status.isOK()) { + event.message = status.getMessage(); + } else { + event.message.clear(); + } + if(!status.isSuccess()) { + callEvent(G); + + } else { + pvd::Status sts(operation->start()); + if(sts.isSuccess()) { + started = true; + last.attach(operation); + } else { + event.message = sts.getMessage(); + callEvent(G); + } + } + } + + + virtual void channelDisconnect(bool destroy) OVERRIDE FINAL + { + Guard G(mutex); + if(!cb || done) return; + event.message = "Disconnect"; + started = false; + callEvent(G, TestMonitorEvent::Disconnect); + } + + virtual void monitorEvent(pva::MonitorPtr const & monitor) OVERRIDE FINAL + { + Guard G(mutex); + if(!cb || done) return; + event.message.clear(); + + callEvent(G, TestMonitorEvent::Data); + } + + virtual void unlisten(pva::MonitorPtr const & monitor) OVERRIDE FINAL + { + Guard G(mutex); + if(!cb || done) return; + done = true; + + if(seenEmpty) + callEvent(G, TestMonitorEvent::Data); + // else // wait until final poll() + } +}; + +TestMonitor::TestMonitor(const std::tr1::shared_ptr& impl) + :impl(impl) +{} + +TestMonitor::~TestMonitor() {} + + +std::string TestMonitor::name() const +{ + return impl ? impl->chan->getChannelName() : ""; +} + +void TestMonitor::cancel() +{ + if(!impl) return; + Guard G(impl->mutex); + + root.reset(); + changed.clear(); + overrun.clear(); + impl->last.reset(); + + if(impl->started) { + impl->op->stop(); + impl->started = false; + } + impl->op->destroy(); + impl->callEvent(G, TestMonitorEvent::Cancel); +} + +bool TestMonitor::poll() +{ + if(!impl) return false; + Guard G(impl->mutex); + + if(!impl->done && impl->last.next()) { + root = impl->last->pvStructurePtr; + changed = *impl->last->changedBitSet; + overrun = *impl->last->overrunBitSet; + + } else { + root.reset(); + changed.clear(); + overrun.clear(); + } + return impl->seenEmpty = !!root; +} + +bool TestMonitor::complete() const +{ + if(impl) return true; + Guard G(impl->mutex); + return impl->done && impl->seenEmpty; +} + +TestMonitor +TestClientChannel::monitor(MonitorCallback *cb, + epics::pvData::PVStructure::const_shared_pointer pvRequest) +{ + if(!impl) throw std::logic_error("Dead Channel"); + if(!pvRequest) + pvRequest = pvd::createRequest("field()"); + + std::tr1::shared_ptr ret(new TestMonitor::Impl(cb)); + ret->chan = getChannel(); + + { + Guard G(ret->mutex); + ret->op = ret->chan->createMonitor(ret, std::tr1::const_pointer_cast(pvRequest)); + } + + return TestMonitor(ret); +} diff --git a/src/testing/pvaTestClientRPC.cpp b/src/testing/pvaTestClientRPC.cpp new file mode 100644 index 0000000..a2972d7 --- /dev/null +++ b/src/testing/pvaTestClientRPC.cpp @@ -0,0 +1,155 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +#include +#include + +#include +#include + +#define epicsExportSharedSymbols +#include "pv/logger.h" +#include "pv/pvaTestClient.h" +#include "pv/pvAccess.h" + +namespace pvd = epics::pvData; +namespace pva = epics::pvAccess; +typedef epicsGuard Guard; +typedef epicsGuardRelease UnGuard; + +namespace { + +struct RPCer : public pva::ChannelRPCRequester, + public TestOperation::Impl +{ + mutable epicsMutex mutex; + + bool started; + operation_type::shared_pointer op; + + TestClientChannel::GetCallback *cb; + TestGetEvent event; + + pvd::PVStructure::const_shared_pointer args; + + RPCer(TestClientChannel::GetCallback* cb, + const pvd::PVStructure::const_shared_pointer& args) :started(false), cb(cb), args(args) {} + virtual ~RPCer() {} + + void callEvent(Guard& G, TestGetEvent::event_t evt = TestGetEvent::Fail) + { + TestClientChannel::GetCallback *cb=this->cb; + if(!cb) return; + + event.event = evt; + + this->cb = 0; + + try { + UnGuard U(G); + cb->getDone(event); + return; + }catch(std::exception& e){ + if(!this->cb || evt==TestGetEvent::Fail) { + LOG(pva::logLevelError, "Unhandled exception in TestClientChannel::GetCallback::getDone(): %s", e.what()); + } else { + event.event = TestGetEvent::Fail; + event.message = e.what(); + } + } + // continues error handling + try { + UnGuard U(G); + cb->getDone(event); + return; + }catch(std::exception& e){ + LOG(pva::logLevelError, "Unhandled exception following exception in TestClientChannel::GetCallback::monitorEvent(): %s", e.what()); + } + } + + virtual std::string name() const OVERRIDE FINAL + { + Guard G(mutex); + return op ? op->getChannel()->getChannelName() : ""; + } + + virtual void cancel() + { + Guard G(mutex); + if(started && op) op->cancel(); + callEvent(G, TestGetEvent::Cancel); + } + + virtual std::string getRequesterName() OVERRIDE FINAL + { return "RPCer"; } + + virtual void channelRPCConnect( + const epics::pvData::Status& status, + pva::ChannelRPC::shared_pointer const & operation) + { + Guard G(mutex); + if(!cb || started) return; + + if(!status.isOK()) { + event.message = status.getMessage(); + } else { + event.message.clear(); + } + if(!status.isSuccess()) { + callEvent(G); + + } else { + operation->request(std::tr1::const_pointer_cast(args)); + started = true; + } + } + + virtual void channelDisconnect(bool destroy) OVERRIDE FINAL + { + Guard G(mutex); + event.message = "Disconnect"; + + callEvent(G); + } + + virtual void requestDone( + const epics::pvData::Status& status, + pva::ChannelRPC::shared_pointer const & operation, + epics::pvData::PVStructure::shared_pointer const & pvResponse) + { + Guard G(mutex); + if(!cb) return; + + if(!status.isOK()) { + event.message = status.getMessage(); + } else { + event.message.clear(); + } + event.value = pvResponse; + + callEvent(G, status.isSuccess()? TestGetEvent::Success : TestGetEvent::Fail); + } +}; + +}//namespace + +TestOperation +TestClientChannel::rpc(GetCallback* cb, + const epics::pvData::PVStructure::const_shared_pointer& arguments, + epics::pvData::PVStructure::const_shared_pointer pvRequest) +{ + if(!impl) throw std::logic_error("Dead Channel"); + if(!pvRequest) + pvRequest = pvd::createRequest("field()"); + + std::tr1::shared_ptr ret(new RPCer(cb, arguments)); + + { + Guard G(ret->mutex); + ret->op = getChannel()->createChannelRPC(ret, std::tr1::const_pointer_cast(pvRequest)); + } + + return TestOperation(ret); +}