From 16268cc9d696595015b6c99bd9c68dd103fd9489 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 20 Jul 2018 13:18:59 -0400 Subject: [PATCH 1/9] add channelConnectThread; should fix connecting to 50000 channels --- src/ca/Makefile | 1 + src/ca/caChannel.cpp | 14 +++- src/ca/caChannel.h | 14 ++++ src/ca/caProvider.cpp | 7 +- src/ca/caProviderPvt.h | 4 ++ src/ca/channelConnectThread.cpp | 115 ++++++++++++++++++++++++++++++++ src/ca/channelConnectThread.h | 71 ++++++++++++++++++++ 7 files changed, 223 insertions(+), 3 deletions(-) create mode 100644 src/ca/channelConnectThread.cpp create mode 100644 src/ca/channelConnectThread.h diff --git a/src/ca/Makefile b/src/ca/Makefile index 93e6b27..6bed7c3 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -11,6 +11,7 @@ LIB_SYS_LIBS_WIN32 += ws2_32 INC += pv/caProvider.h +pvAccessCA_SRCS += channelConnectThread.cpp pvAccessCA_SRCS += monitorEventThread.cpp pvAccessCA_SRCS += getDoneThread.cpp pvAccessCA_SRCS += putDoneThread.cpp diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index b19349b..40ba9fa 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -10,6 +10,7 @@ #include #include #include +#include "channelConnectThread.h" #include "monitorEventThread.h" #include "getDoneThread.h" #include "putDoneThread.h" @@ -62,6 +63,14 @@ void CAChannel::connected() if(DEBUG_LEVEL>0) { cout<< "CAChannel::connected " << channelName << endl; } + ChannelConnectThread->channelConnected(notifyChannelRequester); +} + +void CAChannel::notifyClient() +{ + if(DEBUG_LEVEL>0) { + cout<< "CAChannel::notifyClient " << channelName << endl; + } while(!putQueue.empty()) { putQueue.front()->activate(); putQueue.pop(); @@ -103,7 +112,8 @@ CAChannel::CAChannel(std::string const & channelName, channelProvider(channelProvider), channelRequester(channelRequester), channelID(0), - channelCreated(false) + channelCreated(false), + ChannelConnectThread(ChannelConnectThread::get()) { if(DEBUG_LEVEL>0) { cout<< "CAChannel::CAChannel " << channelName << endl; @@ -117,6 +127,8 @@ void CAChannel::activate(short priority) } ChannelRequester::shared_pointer req(channelRequester.lock()); if(!req) return; + notifyChannelRequester = NotifyChannelRequesterPtr(new NotifyChannelRequester()); + notifyChannelRequester->setChannel(shared_from_this()); attachContext(); int result = ca_create_channel(channelName.c_str(), ca_connection_handler, diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 8228627..f1203bc 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -28,6 +28,17 @@ namespace epics { namespace pvAccess { namespace ca { +class CAChannel; +typedef std::tr1::shared_ptr CAChannelPtr; +typedef std::tr1::weak_ptr CAChannelWPtr; +class ChannelConnectThread; +typedef std::tr1::shared_ptr ChannelConnectThreadPtr; + +class NotifyChannelRequester; +typedef std::tr1::shared_ptr NotifyChannelRequesterPtr; +class ChannelConnectThread; +typedef std::tr1::shared_ptr ChannelConnectThreadPtr; + class NotifyMonitorRequester; typedef std::tr1::shared_ptr NotifyMonitorRequesterPtr; class MonitorEventThread; @@ -108,6 +119,7 @@ public: void attachContext(); void disconnectChannel(); + void notifyClient(); private: virtual void destroy() {} CAChannel(std::string const & channelName, @@ -121,6 +133,8 @@ private: ChannelRequester::weak_pointer channelRequester; chid channelID; bool channelCreated; + ChannelConnectThreadPtr ChannelConnectThread; + NotifyChannelRequesterPtr notifyChannelRequester; epics::pvData::Mutex requestsMutex; std::queue getFieldQueue; diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index 5c5b22c..f670add 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -11,6 +11,7 @@ #include #include +#include "channelConnectThread.h" #include "monitorEventThread.h" #include "getDoneThread.h" #include "putDoneThread.h" @@ -39,6 +40,7 @@ CAChannelProvider::CAChannelProvider() CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&) : current_context(0), + channelConnectThread(ChannelConnectThread::get()), monitorEventThread(MonitorEventThread::get()), getDoneThread(GetDoneThread::get()), putDoneThread(PutDoneThread::get()) @@ -75,9 +77,10 @@ CAChannelProvider::~CAChannelProvider() channelQ.front()->disconnectChannel(); channelQ.pop(); } - monitorEventThread->stop(); - getDoneThread->stop(); putDoneThread->stop(); + getDoneThread->stop(); + monitorEventThread->stop(); + channelConnectThread->stop(); if(DEBUG_LEVEL>0) { std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n"; } diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index 0d48271..4a84c9d 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -24,6 +24,9 @@ namespace ca { #define DEBUG_LEVEL 0 +class ChannelConnectThread; +typedef std::tr1::shared_ptr ChannelConnectThreadPtr; + class MonitorEventThread; typedef std::tr1::shared_ptr MonitorEventThreadPtr; @@ -86,6 +89,7 @@ private: ca_client_context* current_context; epics::pvData::Mutex channelListMutex; std::vector caChannelList; + ChannelConnectThreadPtr channelConnectThread; MonitorEventThreadPtr monitorEventThread; GetDoneThreadPtr getDoneThread; PutDoneThreadPtr putDoneThread; diff --git a/src/ca/channelConnectThread.cpp b/src/ca/channelConnectThread.cpp new file mode 100644 index 0000000..c378019 --- /dev/null +++ b/src/ca/channelConnectThread.cpp @@ -0,0 +1,115 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.07 + */ + +#include "caChannel.h" +#include +#define epicsExportSharedSymbols +#include "channelConnectThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +ChannelConnectThreadPtr ChannelConnectThread::get() +{ + static ChannelConnectThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = ChannelConnectThreadPtr(new ChannelConnectThread()); + master->start(); + } + return master; +} + +ChannelConnectThread::ChannelConnectThread() +: isStop(false) +{ +} + +ChannelConnectThread::~ChannelConnectThread() +{ +//std::cout << "ChannelConnectThread::~ChannelConnectThread()\n"; +} + + +void ChannelConnectThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "channelConnectThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + + +void ChannelConnectThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + +void ChannelConnectThread::channelConnected( + NotifyChannelRequesterPtr const ¬ifyChannelRequester) +{ + { + Lock lock(mutex); + if(notifyChannelRequester->isOnQueue) return; + notifyChannelRequester->isOnQueue = true; + notifyChannelQueue.push(notifyChannelRequester); + } + waitForCommand.signal(); +} + +void ChannelConnectThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyChannelRequester* notifyChannelRequester(NULL); + { + Lock lock(mutex); + if(!notifyChannelQueue.empty()) + { + more = true; + NotifyChannelRequesterWPtr req(notifyChannelQueue.front()); + notifyChannelQueue.pop(); + NotifyChannelRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyChannelRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyChannelRequester!=NULL) + { + CAChannelPtr channel(notifyChannelRequester->channel.lock()); + if(channel) channel->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/channelConnectThread.h b/src/ca/channelConnectThread.h new file mode 100644 index 0000000..7dcb568 --- /dev/null +++ b/src/ca/channelConnectThread.h @@ -0,0 +1,71 @@ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * pvAccessCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +/** + * @author mrk + * @date 2018.07 + */ +#ifndef ChannelConnectThread_H +#define ChannelConnectThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyChannelRequester; +typedef std::tr1::shared_ptr NotifyChannelRequesterPtr; +typedef std::tr1::weak_ptr NotifyChannelRequesterWPtr; + + +class ChannelConnectThread; +typedef std::tr1::shared_ptr ChannelConnectThreadPtr; + +class CAChannel; +typedef std::tr1::shared_ptr CAChannelPtr; +typedef std::tr1::weak_ptr CAChannelWPtr; + +class NotifyChannelRequester +{ +public: + ChannelRequester::weak_pointer channelRequester; + CAChannelWPtr channel; + bool isOnQueue; + NotifyChannelRequester() : isOnQueue(false) {} + void setChannel(CAChannelPtr const &channel) + { this->channel = channel;} +}; + + +class ChannelConnectThread : + public epicsThreadRunable +{ +public: + static ChannelConnectThreadPtr get(); + ~ChannelConnectThread(); + virtual void run(); + void start(); + void stop(); + void channelConnected(NotifyChannelRequesterPtr const ¬ifyChannelRequester); +private: + ChannelConnectThread(); + + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyChannelQueue; +}; + + +}}} + +#endif /* ChannelConnectThread_H */ From bac1307136e79d35d8d91e4941bae246f9fe81de Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Tue, 24 Jul 2018 10:33:15 -0400 Subject: [PATCH 2/9] channel connect: call requester from channel access callback --- src/ca/caChannel.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index b19349b..8a89de4 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -62,6 +62,20 @@ void CAChannel::connected() if(DEBUG_LEVEL>0) { cout<< "CAChannel::connected " << channelName << endl; } + bool callChannelConnected = false; + { + Lock lock(requestsMutex); + if(!channelCreated) { + channelCreated = true; + callChannelConnected = true; + } + } + if(callChannelConnected) { + CAChannelProviderPtr provider(channelProvider.lock()); + if(provider) provider->addChannel(shared_from_this()); + ChannelRequester::shared_pointer req(channelRequester.lock()); + if(req) EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); + } while(!putQueue.empty()) { putQueue.front()->activate(); putQueue.pop(); @@ -123,13 +137,8 @@ void CAChannel::activate(short priority) this, priority, // TODO mapping &channelID); - if (result == ECA_NORMAL) + if (result != ECA_NORMAL) { - channelCreated = true; - CAChannelProviderPtr provider(channelProvider.lock()); - if(provider) provider->addChannel(shared_from_this()); - EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); - } else { Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result))); EXCEPTION_GUARD(req->channelCreated(errorStatus, shared_from_this())); } From 0d4bd9ad9255d5d25e3330422cc60488fed1b0e5 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 30 Jul 2018 14:01:42 -0400 Subject: [PATCH 3/9] fix issue #118 --- src/ca/dbdToPv.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index 636dc5e..09f30f7 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -392,7 +392,7 @@ Status DbdToPv::getFromDBD( copy_DBRScalarArray(value,count,pvValue); break; case DBR_LONG: - copy_DBRScalarArray(value,count,pvValue); + copy_DBRScalarArray(value,count,pvValue); break; case DBR_FLOAT: copy_DBRScalarArray(value,count,pvValue); @@ -430,7 +430,7 @@ Status DbdToPv::getFromDBD( case DBR_STRING: copy_DBRScalar(value,pvValue); break; case DBR_CHAR: copy_DBRScalar(value,pvValue); break; case DBR_SHORT: copy_DBRScalar(value,pvValue); break; - case DBR_LONG: copy_DBRScalar(value,pvValue); break; + case DBR_LONG: copy_DBRScalar(value,pvValue); break; case DBR_FLOAT: copy_DBRScalar(value,pvValue); break; case DBR_DOUBLE: copy_DBRScalar(value,pvValue); break; default: From 39ede57fe2b79044587845a152b802a8e8cd791c Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 10 Aug 2018 15:11:02 -0400 Subject: [PATCH 4/9] change ChannelConnectThread to channelConnectThread --- src/ca/caChannel.cpp | 7 +++++-- src/ca/caChannel.h | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 40ba9fa..c60ab53 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -63,7 +63,7 @@ void CAChannel::connected() if(DEBUG_LEVEL>0) { cout<< "CAChannel::connected " << channelName << endl; } - ChannelConnectThread->channelConnected(notifyChannelRequester); + channelConnectThread->channelConnected(notifyChannelRequester); } void CAChannel::notifyClient() @@ -113,7 +113,7 @@ CAChannel::CAChannel(std::string const & channelName, channelRequester(channelRequester), channelID(0), channelCreated(false), - ChannelConnectThread(ChannelConnectThread::get()) + channelConnectThread(ChannelConnectThread::get()) { if(DEBUG_LEVEL>0) { cout<< "CAChannel::CAChannel " << channelName << endl; @@ -487,6 +487,9 @@ void CAChannelGet::getDone(struct event_handler_args &args) void CAChannelGet::notifyClient() { + if(DEBUG_LEVEL>1) { + std::cout << "CAChannelGet::notifyClient " << channel->getChannelName() << endl; + } ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; EXCEPTION_GUARD(getRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet)); diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index f1203bc..625ad81 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -133,7 +133,7 @@ private: ChannelRequester::weak_pointer channelRequester; chid channelID; bool channelCreated; - ChannelConnectThreadPtr ChannelConnectThread; + ChannelConnectThreadPtr channelConnectThread; NotifyChannelRequesterPtr notifyChannelRequester; epics::pvData::Mutex requestsMutex; From 7b7511fc0fb44589d8db1aa701210fc1bd2a4578 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Wed, 12 Sep 2018 14:12:08 -0400 Subject: [PATCH 5/9] channelConnectThread changes --- src/ca/caChannel.cpp | 69 ++++++++++++++++++++------------------------ src/ca/caChannel.h | 5 ++-- 2 files changed, 34 insertions(+), 40 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 1e33195..687488e 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -52,17 +52,43 @@ static void ca_connection_handler(struct connection_handler_args args) CAChannel *channel = static_cast(ca_puser(args.chid)); if (args.op == CA_OP_CONN_UP) { - channel->connected(); + channel->connect(true); } else if (args.op == CA_OP_CONN_DOWN) { - channel->disconnected(); + channel->connect(false); } } -void CAChannel::connected() +void CAChannel::connect(bool isConnected) { if(DEBUG_LEVEL>0) { cout<< "CAChannel::connected " << channelName << endl; } + { + Lock lock(requestsMutex); + channelConnected = isConnected; + } + channelConnectThread->channelConnected(notifyChannelRequester); +} + +void CAChannel::notifyClient() +{ + if(DEBUG_LEVEL>0) { + cout<< "CAChannel::notifyClient " << channelName << endl; + } + CAChannelProviderPtr provider(channelProvider.lock()); + if(!provider) return; + ChannelRequester::shared_pointer req(channelRequester.lock()); + if(!req) return; + bool isConnected = false; + { + Lock lock(requestsMutex); + isConnected = channelConnected; + } + if(!isConnected) { + EXCEPTION_GUARD(req->channelStateChange( + shared_from_this(), Channel::DISCONNECTED)); + return; + } bool callChannelCreated = false; { Lock lock(requestsMutex); @@ -72,26 +98,9 @@ void CAChannel::connected() } } if(callChannelCreated) { - CAChannelProviderPtr provider(channelProvider.lock()); - if(provider) provider->addChannel(shared_from_this()); - ChannelRequester::shared_pointer req(channelRequester.lock()); - if(req) EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); + provider->addChannel(shared_from_this()); + EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); } - ChannelRequester::shared_pointer req(channelRequester.lock()); - if(req) { - EXCEPTION_GUARD(req->channelStateChange( - shared_from_this(), Channel::CONNECTED)); - } -} - -void CAChannel::notifyClient() -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::notifyClient " << channelName << endl; - } - CAChannelProviderPtr provider(channelProvider.lock()); - if(!provider) return; - provider->addChannel(shared_from_this()); while(!getFieldQueue.empty()) { getFieldQueue.front()->activate(); getFieldQueue.pop(); @@ -114,24 +123,9 @@ void CAChannel::notifyClient() addMonitor(monitor); monitorQueue.pop(); } - ChannelRequester::shared_pointer req(channelRequester.lock()); - if(!req) return; - EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); EXCEPTION_GUARD(req->channelStateChange(shared_from_this(), Channel::CONNECTED)); } -void CAChannel::disconnected() -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::disconnected " << channelName << endl; - } - - ChannelRequester::shared_pointer req(channelRequester.lock()); - if(req) { - EXCEPTION_GUARD(req->channelStateChange( - shared_from_this(), Channel::DISCONNECTED)); - } -} CAChannel::CAChannel(std::string const & channelName, CAChannelProvider::shared_pointer const & channelProvider, @@ -141,6 +135,7 @@ CAChannel::CAChannel(std::string const & channelName, channelRequester(channelRequester), channelID(0), channelCreated(false), + channelConnected(false), channelConnectThread(ChannelConnectThread::get()) { if(DEBUG_LEVEL>0) { diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index d27652d..46a4563 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -97,9 +97,6 @@ public: short priority, ChannelRequester::shared_pointer const & channelRequester); virtual ~CAChannel(); - - void connected(); - void disconnected(); chid getChannelID(); virtual std::tr1::shared_ptr getProvider(); @@ -122,6 +119,7 @@ public: void attachContext(); void disconnectChannel(); + void connect(bool isConnected); void notifyClient(); private: virtual void destroy() {} @@ -136,6 +134,7 @@ private: ChannelRequester::weak_pointer channelRequester; chid channelID; bool channelCreated; + bool channelConnected; ChannelConnectThreadPtr channelConnectThread; NotifyChannelRequesterPtr notifyChannelRequester; From 10bb8039e5a8c3e7439d642671340f405ad69ba9 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Thu, 13 Sep 2018 08:36:33 -0400 Subject: [PATCH 6/9] fix problem with pvget and pvinfo --- src/ca/caChannel.cpp | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 687488e..bd5eecb 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -61,7 +61,7 @@ static void ca_connection_handler(struct connection_handler_args args) void CAChannel::connect(bool isConnected) { if(DEBUG_LEVEL>0) { - cout<< "CAChannel::connected " << channelName << endl; + cout<< "CAChannel::connect " << channelName << endl; } { Lock lock(requestsMutex); @@ -77,34 +77,19 @@ void CAChannel::notifyClient() } CAChannelProviderPtr provider(channelProvider.lock()); if(!provider) return; - ChannelRequester::shared_pointer req(channelRequester.lock()); - if(!req) return; bool isConnected = false; { Lock lock(requestsMutex); isConnected = channelConnected; } if(!isConnected) { - EXCEPTION_GUARD(req->channelStateChange( - shared_from_this(), Channel::DISCONNECTED)); + ChannelRequester::shared_pointer req(channelRequester.lock()); + if(req) { + EXCEPTION_GUARD(req->channelStateChange( + shared_from_this(), Channel::DISCONNECTED)); + } return; } - bool callChannelCreated = false; - { - Lock lock(requestsMutex); - if(!channelCreated) { - channelCreated = true; - callChannelCreated = true; - } - } - if(callChannelCreated) { - provider->addChannel(shared_from_this()); - EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); - } - while(!getFieldQueue.empty()) { - getFieldQueue.front()->activate(); - getFieldQueue.pop(); - } while(!getFieldQueue.empty()) { getFieldQueue.front()->activate(); getFieldQueue.pop(); @@ -123,7 +108,11 @@ void CAChannel::notifyClient() addMonitor(monitor); monitorQueue.pop(); } - EXCEPTION_GUARD(req->channelStateChange(shared_from_this(), Channel::CONNECTED)); + ChannelRequester::shared_pointer req(channelRequester.lock()); + if(req) { + EXCEPTION_GUARD(req->channelStateChange( + shared_from_this(), Channel::CONNECTED)); + } } @@ -158,8 +147,13 @@ void CAChannel::activate(short priority) this, priority, // TODO mapping &channelID); - if (result != ECA_NORMAL) + if (result == ECA_NORMAL) { + channelCreated = true; + CAChannelProviderPtr provider(channelProvider.lock()); + if(provider) provider->addChannel(shared_from_this()); + EXCEPTION_GUARD(req->channelCreated(Status::Ok, shared_from_this())); + } else { Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result))); EXCEPTION_GUARD(req->channelCreated(errorStatus, shared_from_this())); } From e9d2114e965788e6653f3cf5b19cba1a728bc5d3 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 24 Sep 2018 13:45:21 -0400 Subject: [PATCH 7/9] latest changes to match caProvider.html --- src/ca/caChannel.cpp | 6 +- src/ca/dbdToPv.cpp | 179 ++++++++++++++++++++----------------------- src/ca/dbdToPv.h | 10 +-- 3 files changed, 88 insertions(+), 107 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index bd5eecb..d3baef2 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -402,8 +402,7 @@ void CAChannelGetField::callRequester(CAChannelPtr const & caChannel) if(!requester) return; PVStructurePtr pvRequest(createRequest("")); DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO); - PVStructurePtr pvStructure = dbdToPv->createPVStructure(); - Structure::const_shared_pointer structure(pvStructure->getStructure()); + Structure::const_shared_pointer structure(dbdToPv->getStructure()); Field::const_shared_pointer field = subField.empty() ? std::tr1::static_pointer_cast(structure) : @@ -471,6 +470,7 @@ void CAChannelGet::activate() std::cout << "CAChannelGet::activate " << channel->getChannelName() << endl; } dbdToPv = DbdToPv::create(channel,pvRequest,getIO); + dbdToPv->getChoices(channel); pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester()); @@ -595,6 +595,7 @@ void CAChannelPut::activate() cout << "CAChannelPut::activate " << channel->getChannelName() << endl; } dbdToPv = DbdToPv::create(channel,pvRequest,putIO); + dbdToPv->getChoices(channel); pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); PVStringPtr pvString = pvRequest->getSubField("record._options.block"); @@ -854,6 +855,7 @@ void CAChannelMonitor::activate() std::cout << "CAChannelMonitor::activate " << channel->getChannelName() << endl; } dbdToPv = DbdToPv::create(channel,pvRequest,monitorIO); + dbdToPv->getChoices(channel); pvStructure = dbdToPv->createPVStructure(); activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); int32 queueSize = 2; diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index 09f30f7..409a0ec 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,7 @@ using namespace epics::pvData; using std::string; using std::ostringstream; +using std::cout; namespace epics { namespace pvAccess { @@ -30,23 +32,7 @@ namespace ca { #define CA_PRIORITY 50 -static void enumChoicesHandler(struct event_handler_args args) -{ - DbdToPv *dbdToPv = static_cast(args.usr); - dbdToPv->getChoicesDone(args); -} -static void description_connection_handler(struct connection_handler_args args) -{ - DbdToPv *dbdToPv = static_cast(ca_puser(args.chid)); - dbdToPv->descriptionConnected(args); -} - -static void descriptionHandler(struct event_handler_args args) -{ - DbdToPv *dbdToPv = static_cast(args.usr); - dbdToPv->getDescriptionDone(args); -} DbdToPvPtr DbdToPv::create( CAChannelPtr const & caChannel, @@ -60,7 +46,7 @@ DbdToPvPtr DbdToPv::create( DbdToPv::DbdToPv(IOType ioType) : ioType(ioType), - fieldRequested(false), + valueRequested(false), alarmRequested(false), timeStampRequested(false), displayRequested(false), @@ -68,8 +54,6 @@ DbdToPv::DbdToPv(IOType ioType) valueAlarmRequested(false), isArray(false), firstTime(true), - choicesValid(false), - waitForChoicesValid(false), caValueType(-1), caRequestType(-1), maxElements(0) @@ -104,6 +88,42 @@ static chtype getDbrType(const ScalarType scalarType) throw std::runtime_error("getDbr: illegal scalarType"); } +static dbr_short_t convertDBstatus(dbr_short_t dbStatus) +{ + switch(dbStatus) { + case NO_ALARM: + return noStatus; + case READ_ALARM: + case WRITE_ALARM: + case HIHI_ALARM: + case HIGH_ALARM: + case LOLO_ALARM: + case LOW_ALARM: + case STATE_ALARM: + case COS_ALARM: + case HW_LIMIT_ALARM: + return deviceStatus; + case COMM_ALARM: + case TIMEOUT_ALARM: + return driverStatus; + case CALC_ALARM: + case SCAN_ALARM: + case LINK_ALARM: + case SOFT_ALARM: + case BAD_SUB_ALARM: + return recordStatus; + case DISABLE_ALARM: + case SIMM_ALARM: + case READ_ACCESS_ALARM: + case WRITE_ACCESS_ALARM: + return dbStatus; + case UDF_ALARM: + return undefinedStatus; + default: + return undefinedStatus; // UNDEFINED + } + +} void DbdToPv::activate( CAChannelPtr const & caChannel, @@ -131,14 +151,14 @@ void DbdToPv::activate( } if(fieldPVStructure->getPVFields().size()==0) { - fieldRequested = true; + valueRequested = true; alarmRequested = true; timeStampRequested = true; displayRequested = true; controlRequested = true; valueAlarmRequested = true; } else { - if(fieldPVStructure->getSubField("value")) fieldRequested = true; + if(fieldPVStructure->getSubField("value")) valueRequested = true; if(fieldPVStructure->getSubField("alarm")) alarmRequested = true; if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true; if(fieldPVStructure->getSubField("display")) displayRequested = true; @@ -173,18 +193,7 @@ void DbdToPv::activate( } caRequestType = (properties.size()==0 ? DBR_ENUM : DBR_TIME_ENUM); structure = standardField->enumerated(properties); - int result = ca_array_get_callback(DBR_GR_ENUM, - 1, - channelID, enumChoicesHandler, this); - if (result == ECA_NORMAL) result = ca_flush_io(); - if (result != ECA_NORMAL) { - string mess(caChannel->getChannelName()); - mess += " DbdToPv::activate getting enum cnoices "; - mess += ca_message(result); - throw std::runtime_error(mess); - } - // NOTE: we do not wait here, since all subsequent request (over TCP) is serialized - // and will guarantee that enumChoicesHandler is called first + return; } maxElements = ca_element_count(channelID); @@ -204,7 +213,7 @@ void DbdToPv::activate( FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate()); PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate()); FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder()); - if(fieldRequested) { + if(valueRequested) { if(isArray) { fieldBuilder->addArray("value",st); } else { @@ -242,38 +251,27 @@ void DbdToPv::activate( } else { caRequestType = dbf_type_to_DBR(caValueType); } - if(displayRequested) { - chid channelID; - string name(caChannel->getChannelName() + ".DESC"); - int result = ca_create_channel(name.c_str(), - description_connection_handler, - this, - CA_PRIORITY, // TODO mapping - &channelID); - if (result == ECA_NORMAL) result = ca_flush_io(); - if (result != ECA_NORMAL) { - string mess(caChannel->getChannelName()); - mess += " DbdToPv::activate getting description "; - mess += ca_message(result); - throw std::runtime_error(mess); - } + +} + +chtype DbdToPv::getRequestType() +{ + if(caRequestType<0) { + throw std::runtime_error("DbDToPv::getRequestType: bad type"); } + return caRequestType; } -void DbdToPv::descriptionConnected(struct connection_handler_args args) +Structure::const_shared_pointer DbdToPv::getStructure() { - if (args.op != CA_OP_CONN_UP) return; - ca_array_get_callback(DBR_STRING, - 0, - args.chid, descriptionHandler, this); + return structure; } -void DbdToPv::getDescriptionDone(struct event_handler_args &args) + +static void enumChoicesHandler(struct event_handler_args args) { - if(args.status!=ECA_NORMAL) return; - const dbr_string_t *value = static_cast(dbr_value_ptr(args.dbr,DBR_STRING)); - description = string(*value); - ca_clear_channel(args.chid); + DbdToPv *dbdToPv = static_cast(args.usr); + dbdToPv->getChoicesDone(args); } void DbdToPv::getChoicesDone(struct event_handler_args &args) @@ -288,21 +286,29 @@ void DbdToPv::getChoicesDone(struct event_handler_args &args) size_t num = dbr_enum_p->no_str; choices.reserve(num); for(size_t i=0; istrs[i][0])); - bool signal = false; - { - Lock lock(choicesMutex); - choicesValid = true; - if(waitForChoicesValid) signal = true; - } - if(signal) choicesEvent.signal(); + choicesEvent.signal(); } -chtype DbdToPv::getRequestType() + +void DbdToPv::getChoices(CAChannelPtr const & caChannel) { - if(caRequestType<0) { - throw std::runtime_error("DbDToPv::getRequestType: bad type"); + if(caRequestType==DBR_ENUM||caRequestType==DBR_TIME_ENUM) + { + caChannel->attachContext(); + chid channelID = caChannel->getChannelID(); + int result = ca_array_get_callback(DBR_GR_ENUM, + 1, + channelID, enumChoicesHandler, this); + if (result == ECA_NORMAL) { + result = ca_flush_io(); + choicesEvent.wait(); + } else { + string mess(caChannel->getChannelName()); + mess += " DbdToPv::activate getting enum cnoices "; + mess += ca_message(result); + throw std::runtime_error(mess); + } } - return caRequestType; } PVStructurePtr DbdToPv::createPVStructure() @@ -368,7 +374,7 @@ Status DbdToPv::getFromDBD( Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); return errorStatus; } - if(fieldRequested) + if(valueRequested) { void * value = dbr_value_ptr(args.dbr,caRequestType); if(isArray) { @@ -461,7 +467,7 @@ Status DbdToPv::getFromDBD( PVIntPtr pvStatus(pvAlarm->getSubField("status")); if(caAlarm.status!=status) { caAlarm.status = status; - pvStatus->put(status); + pvStatus->put(convertDBstatus(status)); string message("UNKNOWN STATUS"); if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]); pvMessage->put(message); @@ -488,7 +494,7 @@ Status DbdToPv::getFromDBD( bitSet->set(pvSeconds->getFieldOffset()); } if(caTimeStamp.nsec!=stamp.nsec) { - caTimeStamp.secPastEpoch = stamp.secPastEpoch; + caTimeStamp.nsec = stamp.nsec; PVIntPtr pvNano(pvTimeStamp->getSubField("nanoseconds")); pvNano->put(stamp.nsec); bitSet->set(pvNano->getFieldOffset()); @@ -590,14 +596,6 @@ Status DbdToPv::getFromDBD( pvString->put(format); bitSet->set(pvString->getFieldOffset()); } - if(!description.empty()) - { - PVStringPtr pvString = pvDisplay->getSubField("description"); - if(description.compare(pvString->get()) !=0) { - pvString->put(description); - bitSet->set(pvString->getFieldOffset()); - } - } } if(valueAlarmRequested) { double upper_alarm_limit = 0.0; @@ -668,6 +666,8 @@ Status DbdToPv::getFromDBD( return Status::Ok; } + + template const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar) { @@ -750,23 +750,6 @@ Status DbdToPv::putToDBD( switch(caValueType) { case DBR_ENUM: { - bool wait = false; - { - Lock lock(choicesMutex); - if(!choicesValid) { - wait = true; - waitForChoicesValid = true; - } - } - bool result = true; - if(wait) { - result = choicesEvent.wait(5.0); - } - if(!result) { - Status errorStatus( - Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD ")); - return errorStatus; - } dbr_enum_t indexvalue = pvStructure->getSubField("value.index")->get(); pValue = &indexvalue; break; diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h index 5ee77b5..7fa77d6 100644 --- a/src/ca/dbdToPv.h +++ b/src/ca/dbdToPv.h @@ -89,6 +89,8 @@ public: epics::pvData::PVStructurePtr const & pvRequest, IOType ioType ); + epics::pvData::Structure::const_shared_pointer getStructure(); + void getChoices(CAChannelPtr const & caChannel); epics::pvData::PVStructurePtr createPVStructure(); chtype getRequestType(); epics::pvData::Status getFromDBD( @@ -104,8 +106,6 @@ public: void *userArg ); void getChoicesDone(struct event_handler_args &args); - void descriptionConnected(struct connection_handler_args args); - void getDescriptionDone(struct event_handler_args &args); private: DbdToPv(IOType ioType); void activate( @@ -113,7 +113,7 @@ private: epics::pvData::PVStructurePtr const & pvRequest ); IOType ioType; - bool fieldRequested; + bool valueRequested; bool alarmRequested; bool timeStampRequested; bool displayRequested; @@ -121,19 +121,15 @@ private: bool valueAlarmRequested; bool isArray; bool firstTime; - bool choicesValid; - bool waitForChoicesValid; chtype caValueType; chtype caRequestType; unsigned long maxElements; - epics::pvData::Mutex choicesMutex; epics::pvData::Event choicesEvent; epicsTimeStamp caTimeStamp; CaAlarm caAlarm; CaDisplay caDisplay; CaControl caControl; CaValueAlarm caValueAlarm; - std::string description; epics::pvData::Structure::const_shared_pointer structure; std::vector choices; }; From 1204874481e8cbee5253aa00635fdd14694d2d96 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Thu, 27 Sep 2018 10:02:00 -0400 Subject: [PATCH 8/9] implement field options --- src/ca/caChannel.cpp | 14 ++- src/ca/caChannel.h | 1 + src/ca/dbdToPv.cpp | 231 ++++++++++++++++++++++++++++++++++++++++--- src/ca/dbdToPv.h | 7 ++ 4 files changed, 235 insertions(+), 18 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index d3baef2..6a1cf81 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -833,7 +833,8 @@ CAChannelMonitor::CAChannelMonitor( pvRequest(pvRequest), isStarted(false), monitorEventThread(MonitorEventThread::get()), - pevid(NULL) + pevid(NULL), + eventMask(DBE_VALUE | DBE_ALARM) {} CAChannelMonitor::~CAChannelMonitor() @@ -869,6 +870,15 @@ void CAChannelMonitor::activate() ss >> size; if (size > 1) queueSize = size; } + pvString = pvOptions->getSubField("DBE"); + if(pvString) { + std::string value(pvString->get()); + eventMask = 0; + if(value.find("VALUE")!=std::string::npos) eventMask|=DBE_VALUE; + if(value.find("ARCHIVE")!=std::string::npos) eventMask|=DBE_ARCHIVE; + if(value.find("ALARM")!=std::string::npos) eventMask|=DBE_ALARM; + if(value.find("PROPERTY")!=std::string::npos) eventMask|=DBE_PROPERTY; + } } notifyMonitorRequester = NotifyMonitorRequesterPtr(new NotifyMonitorRequester()); notifyMonitorRequester->setChannelMonitor(shared_from_this()); @@ -941,7 +951,7 @@ Status CAChannelMonitor::start() channel->attachContext(); int result = ca_create_subscription(dbdToPv->getRequestType(), 0, - channel->getChannelID(), DBE_VALUE, + channel->getChannelID(), eventMask, ca_subscription_handler, this, &pevid); if (result == ECA_NORMAL) diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 46a4563..cfaf549 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -264,6 +264,7 @@ private: bool isStarted; MonitorEventThreadPtr monitorEventThread; evid pevid; + unsigned long eventMask; NotifyMonitorRequesterPtr notifyMonitorRequester; DbdToPvPtr dbdToPv; diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index 409a0ec..254ba7f 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -46,6 +46,11 @@ DbdToPvPtr DbdToPv::create( DbdToPv::DbdToPv(IOType ioType) : ioType(ioType), + dbfIsUCHAR(false), + dbfIsUSHORT(false), + dbfIsULONG(false), + dbfIsINT64(false), + dbfIsUINT64(false), valueRequested(false), alarmRequested(false), timeStampRequested(false), @@ -53,6 +58,7 @@ DbdToPv::DbdToPv(IOType ioType) controlRequested(false), valueAlarmRequested(false), isArray(false), + charArrayIsString(false), firstTime(true), caValueType(-1), caRequestType(-1), @@ -79,10 +85,15 @@ static chtype getDbrType(const ScalarType scalarType) { case pvString : return DBR_STRING; case pvByte : return DBR_CHAR; + case pvUByte : return DBR_CHAR; case pvShort : return DBR_SHORT; + case pvUShort : return DBR_SHORT; case pvInt : return DBR_LONG; + case pvUInt : return DBR_LONG; case pvFloat : return DBR_FLOAT; case pvDouble : return DBR_DOUBLE; + case pvLong : return DBR_DOUBLE; + case pvULong : return DBR_DOUBLE; default: break; } throw std::runtime_error("getDbr: illegal scalarType"); @@ -196,25 +207,72 @@ void DbdToPv::activate( return; } + ScalarType st = dbr2ST[channelType]; + PVStringPtr pvValue = fieldPVStructure->getSubField("value._options.dbtype"); + if(pvValue) + { + std::string value(pvValue->get()); + if(value.find("DBF_UCHAR")!=std::string::npos) { + if(st==pvByte) { + dbfIsUCHAR = true; + st = pvUByte; + caValueType = DBR_CHAR; + } + } else if(value.find("DBF_USHORT")!=std::string::npos) { + if(st==pvInt) { + dbfIsUSHORT = true; + st = pvUShort; + caValueType = DBR_SHORT; + } + } else if(value.find("DBF_ULONG")!=std::string::npos) { + if(st==pvDouble) { + dbfIsULONG = true; + st = pvUInt; + caValueType = DBR_LONG; + } + } else if(value.find("DBF_INT64")!=std::string::npos) { + if(st==pvDouble) { + dbfIsINT64 = true; + st = pvLong; + } + } else if(value.find("DBF_UINT64")!=std::string::npos) { + if(st==pvDouble) { + dbfIsUINT64 = true; + st = pvULong; + } + } + + } + if(st==pvString) { + displayRequested = false; + controlRequested = false; + valueAlarmRequested = false; + } maxElements = ca_element_count(channelID); if(maxElements!=1) isArray = true; if(isArray) { controlRequested = false; valueAlarmRequested = false; + if(channelType==DBR_CHAR && fieldPVStructure) + { + PVStringPtr pvValue = fieldPVStructure->getSubField("value._options.pvtype"); + if(pvValue) { + std::string value(pvValue->get()); + if(value.find("pvString")!=std::string::npos) { + charArrayIsString = true; + st = pvString; + } + } + } } - ScalarType st = dbr2ST[channelType]; - if(st==pvString) { - displayRequested = false; - controlRequested = false; - valueAlarmRequested = false; - } + if(controlRequested || displayRequested || valueAlarmRequested) timeStampRequested = false; FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate()); PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate()); FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder()); if(valueRequested) { - if(isArray) { + if(isArray && !charArrayIsString) { fieldBuilder->addArray("value",st); } else { fieldBuilder->add("value",st); @@ -228,14 +286,19 @@ void DbdToPv::activate( switch(st) { case pvByte: + case pvUByte: fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break; case pvShort: + case pvUShort: fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break; case pvInt: + case pvUInt: fieldBuilder->add("valueAlarm",standardField->intAlarm()); break; case pvFloat: fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break; case pvDouble: + case pvLong: + case pvULong: fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break; default: throw std::runtime_error("DbDToPv::activate: bad type"); @@ -392,18 +455,51 @@ Status DbdToPv::getFromDBD( break; } case DBR_CHAR: + if(charArrayIsString) + { + const char * pchar = static_cast(value); + std::string str(pchar); + PVStringPtr pvValue(pvStructure->getSubField("value")); + pvValue->put(str); + break; + } + if(dbfIsUCHAR) + { + copy_DBRScalarArray(value,count,pvValue); + break; + } copy_DBRScalarArray(value,count,pvValue); break; case DBR_SHORT: + if(dbfIsUSHORT) + { + copy_DBRScalarArray(value,count,pvValue); + break; + } copy_DBRScalarArray(value,count,pvValue); break; case DBR_LONG: + if(dbfIsULONG) + { + copy_DBRScalarArray(value,count,pvValue); + break; + } copy_DBRScalarArray(value,count,pvValue); break; case DBR_FLOAT: copy_DBRScalarArray(value,count,pvValue); break; case DBR_DOUBLE: + if(dbfIsINT64) + { + copy_DBRScalarArray(value,count,pvValue); + break; + } + if(dbfIsUINT64) + { + copy_DBRScalarArray(value,count,pvValue); + break; + } copy_DBRScalarArray(value,count,pvValue); break; default: @@ -434,11 +530,40 @@ Status DbdToPv::getFromDBD( break; } case DBR_STRING: copy_DBRScalar(value,pvValue); break; - case DBR_CHAR: copy_DBRScalar(value,pvValue); break; - case DBR_SHORT: copy_DBRScalar(value,pvValue); break; - case DBR_LONG: copy_DBRScalar(value,pvValue); break; + case DBR_CHAR: + if(dbfIsUCHAR) + { + copy_DBRScalar(value,pvValue); + break; + } + copy_DBRScalar(value,pvValue); break; + case DBR_SHORT: + if(dbfIsUSHORT) + { + copy_DBRScalar(value,pvValue); + break; + } + copy_DBRScalar(value,pvValue); break; + case DBR_LONG: + if(dbfIsULONG) + { + copy_DBRScalar(value,pvValue); + break; + } + copy_DBRScalar(value,pvValue); break; case DBR_FLOAT: copy_DBRScalar(value,pvValue); break; - case DBR_DOUBLE: copy_DBRScalar(value,pvValue); break; + case DBR_DOUBLE: + if(dbfIsINT64) + { + copy_DBRScalar(value,pvValue); + break; + } + if(dbfIsUINT64) + { + copy_DBRScalar(value,pvValue); + break; + } + copy_DBRScalar(value,pvValue); break; default: Status errorStatus( Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); @@ -726,18 +851,63 @@ Status DbdToPv::putToDBD( break; } case DBR_CHAR: + if(charArrayIsString) + { + PVStringPtr pvValue(pvStructure->getSubField("value")); + const char * pchar = pvValue->get().c_str(); + pValue = pchar; + count = pvValue->get().length(); + break; + } + if(dbfIsUCHAR) + { + pValue = put_DBRScalarArray(&count,pvValue); + break; + } pValue = put_DBRScalarArray(&count,pvValue); break; case DBR_SHORT: + if(dbfIsUSHORT) + { + pValue = put_DBRScalarArray(&count,pvValue); + break; + } pValue = put_DBRScalarArray(&count,pvValue); break; case DBR_LONG: + if(dbfIsULONG) + { + pValue = put_DBRScalarArray(&count,pvValue); + break; + } pValue = put_DBRScalarArray(&count,pvValue); break; case DBR_FLOAT: pValue = put_DBRScalarArray(&count,pvValue); break; case DBR_DOUBLE: + if(dbfIsINT64) + { + PVLongArrayPtr pvValue(pvStructure->getSubField("value")); + PVLongArray::const_svector sv(pvValue->view()); + pvDoubleArray = PVDoubleArrayPtr(getPVDataCreate()->createPVScalarArray()); + pvDoubleArray->putFrom(sv); + const double * pdouble = pvDoubleArray->view().data(); + count = pvValue->getLength(); + pValue = pdouble; + break; + } + if(dbfIsUINT64) + { + PVULongArrayPtr pvValue(pvStructure->getSubField("value")); + PVULongArray::const_svector sv(pvValue->view()); + pvDoubleArray = PVDoubleArrayPtr(getPVDataCreate()->createPVScalarArray()); + pvDoubleArray->putFrom(sv); + const double * pdouble = pvDoubleArray->view().data(); + count = pvValue->getLength(); + pValue = pdouble; + break; + } pValue = put_DBRScalarArray(&count,pvValue); break; default: @@ -755,14 +925,43 @@ Status DbdToPv::putToDBD( break; } case DBR_STRING: pValue = pvStructure->getSubField("value")->get().c_str(); break; - case DBR_CHAR: pValue = put_DBRScalar(&bvalue,pvValue); break; - case DBR_SHORT: pValue = put_DBRScalar(&svalue,pvValue); break; - case DBR_LONG: pValue = put_DBRScalar(&lvalue,pvValue); break; + case DBR_CHAR: + if(dbfIsUCHAR) + { + pValue = put_DBRScalar(&bvalue,pvValue); + break; + } + pValue = put_DBRScalar(&bvalue,pvValue); break; + case DBR_SHORT: + if(dbfIsUSHORT) + { + pValue = put_DBRScalar(&svalue,pvValue); + break; + } + pValue = put_DBRScalar(&svalue,pvValue); break; + case DBR_LONG: + if(dbfIsULONG) + { + pValue = put_DBRScalar(&lvalue,pvValue); + break; + } + pValue = put_DBRScalar(&lvalue,pvValue); break; case DBR_FLOAT: pValue = put_DBRScalar(&fvalue,pvValue); break; - case DBR_DOUBLE: pValue = put_DBRScalar(&dvalue,pvValue); break; + case DBR_DOUBLE: + if(dbfIsINT64) + { + pValue = put_DBRScalar(&dvalue,pvValue); + break; + } + if(dbfIsUINT64) + { + pValue = put_DBRScalar(&dvalue,pvValue); + break; + } + pValue = put_DBRScalar(&dvalue,pvValue); break; default: Status errorStatus( - Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + Status::STATUSTYPE_ERROR, string("DbdToPv::putToDBD logic error")); return errorStatus; } } diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h index 7fa77d6..1c9a0fa 100644 --- a/src/ca/dbdToPv.h +++ b/src/ca/dbdToPv.h @@ -113,6 +113,11 @@ private: epics::pvData::PVStructurePtr const & pvRequest ); IOType ioType; + bool dbfIsUCHAR; + bool dbfIsUSHORT; + bool dbfIsULONG; + bool dbfIsINT64; + bool dbfIsUINT64; bool valueRequested; bool alarmRequested; bool timeStampRequested; @@ -120,6 +125,7 @@ private: bool controlRequested; bool valueAlarmRequested; bool isArray; + bool charArrayIsString; bool firstTime; chtype caValueType; chtype caRequestType; @@ -132,6 +138,7 @@ private: CaValueAlarm caValueAlarm; epics::pvData::Structure::const_shared_pointer structure; std::vector choices; + epics::pvData::PVDoubleArrayPtr pvDoubleArray; //for dbfIsINT64 and dbfIsUINT64 }; } From ad1a7196048f8b03909cc5fa326f38fe9f37c3de Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 5 Oct 2018 10:12:33 -0400 Subject: [PATCH 9/9] caProvider.md: just link to html documentation --- caProvider.md | 622 +------------------------------------------------- 1 file changed, 6 insertions(+), 616 deletions(-) diff --git a/caProvider.md b/caProvider.md index 6a479aa..29a285e 100644 --- a/caProvider.md +++ b/caProvider.md @@ -1,16 +1,20 @@ # pvAccessCPP: ca provider -2018.07.09 +2018.10.05 Editors: * Marty Kraimer -This is a description of channel provider **ca** that is implemented as part of **pvAccessCPP**. +**ca** is a channel provider **ca** that is implemented as part of **pvAccessCPP**. + It uses the **channel access** network protocol to communicate with a server, i. e. the network protocol that has been used to communicate with **EPICS IOCs** since 1990. +A description of **ca** is provided in +[caProvider](https://mrkraimer.github.io/website/caProvider/caProvider.html) + Provider **pva** is another way to connect to a **DBRecord**, But this only works if the IOC has **qsrv** installed. **qsrv**, which is provided with @@ -19,618 +23,4 @@ has full support for communicating with a **DBRecord**. The only advantage of **ca** is that it does require any changes to an existing IOC. -The following are discussed. - -* [Introduction](#S-introduction) -* [client API](#S-client) -* [Mapping DBD data to pvData](#S-dbd_to_pvdata) -* [Developing plugins for ca provider.](#S-plugin) - - - -## Introduction - -The primary purpose of the **ca** provider is to access **DBRecord**s in an EPICS IOC via the -**channel access** network protocol but to use pvData objects for the client. - -Each **DBRecord** instance has a record name that must be unique in the local area network. -A client can access any public field of a **DBRecord**; -Each **DBRecord** instance has a record name that is unique with in the local area network -A channel name is a **recordname.fieldName**. -If the fieldname is not specified then **.VAL** is assumed - -### example database - -The following: - -``` -mrk> pwd -/home/epicsv4/masterCPP/pvAccessCPP/testCa -mrk> softIoc -d testCaProvider.db -``` - -Starts an EPICS IOC that is used for all examples in this document. - -### examples - -``` -mrk> caget -d DBR_TIME_FLOAT DBRdoubleout -DBRdoubleout - Native data type: DBF_DOUBLE - Request type: DBR_TIME_FLOAT - Element count: 1 - Value: 1 - Timestamp: 2018-06-21 06:23:07.939894 - Status: NO_ALARM - Severity: NO_ALARM -mrk> pvget -p ca -r "value,alarm,timeStamp" -i DBRdoubleout -DBRdoubleout -structure - double value 1 - alarm_t alarm - int severity 0 - int status 0 - string message - time_t timeStamp - long secondsPastEpoch 1529576587 - int nanoseconds 939894210 - int userTag 0 -mrk> pvget -p ca -r "value,alarm,timeStamp" DBRdoubleout -DBRdoubleout -structure - double value 1 - alarm_t alarm NO_ALARM NO_STATUS - time_t timeStamp 2018-06-21T06:23:07.940 0 - -mrk> pvget -p ca -r value -i DBRdoubleout.SCAN -DBRdoubleout.SCAN -epics:nt/NTEnum:1.0 - enum_t value - int index 0 - string[] choices [Passive,Event,I/O Intr,10 second,5 second,2 second,1 second,.5 second,.2 second,.1 second] -``` - -### Overview of Channel Access - -**channel access**, which is provided with **epics-base**, defines and implements a protocol -for client/server network communication. - -**epics-base** provides both a client and a server implementation -This document only discusses the client API. - -For details see: - -[EPICS Channel Access 4.13.1 Reference Manual](https://epics.anl.gov/base/R7-0/1-docs/CAref.html) - -**channel access** allows a client to get, put, and monitor monitor data from a server. -The data is defined by various DBD types. - -The following, in **epics-base/include**, are the -main include files that show the **channel access** API: - -``` -cadef.h -db_access.h -``` - -The client requests data via one of the DBR types. - -For example: - -``` -DBR_STS_DOUBLE returns a double status structure (dbr_sts_double) -where -struct dbr_sts_double{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - dbr_long_t RISC_pad; /* RISC alignment */ - dbr_double_t value; /* current value */ -}; -``` - -The server converts data between the native type of the field being accessed and the DBR type. - - -### Overview of ca provider - -**ca** is a pvAccess Channel Provider that uses **channel access** to connect a client to a server. - -With **ca**, the client data appears as pvData objects, i. e. -**ca** converts the data provided by **channel access** to/from pvData - -Thus a pvAccess client can communicate with an existing V3 EPICS IOC without making -any changes to existing IOCs. - -For an overview of pvData and pvAccess see: - -[EPICS V4 Developer's Guide](https://mrkraimer.github.io/website/developerGuide/developerGuide.html) - -**ca** requests data from the server with a DBR type that matches the native type. -See the next section for more details. - -All conversion to/from other types must be done by the client. - -## Client API - -**ca** implements the following pvAccess methods : **getField**, **channelGet**, **channelPut** and **monitor**. - -For channelPut the only field that can be accessed is **value**. -For channelPut a client can issue puts with and without a callback from the server. -The default is no callback. If createChannelPut has the option "record[block=true]" then a put callback used. - -All of the other pvAccess methods provide access to fields **alarm** and **timeStamp**. - -Depending on the type associated with the **value** field the following fields may also be available: -**display**, **control** , and **valueAlarm**. - -Thus a client can make requests like: - -``` -pvget -p ca -r "value,alarm,timeStamp,display,control,valueAlarm" names ... -``` - -**ca** will create a structure that has the fields requested but only for fields that are supported -by the server. - -* For puts only value is supported. -* For gets and monitors every channel supports value, alarm, and timeStamp; -* If any of display,control, or valueAlarm are requested then timeStamp is NOT available. - -Lets discuss the various fields. - -### value - -This can be a scalar, scalarArray, or an enumerated structure. - -For a scalar or scalarArray the ScalarType is one of the following: -**pvString**, **pvByte**, **pvShort**, **pvInt**, **pvFloat**, or **pvDouble**. - -Note that **channel access** does not support unsigned integers or 64 bit integers. - -A enumerated structure is created if the native type is **DBR_ENUM**. - -Some examples are: - -``` -pvget -p ca -r value -i DBRlongout -DBRlongout -structure - int value 0 -mrk> pvget -p ca -r value -i DBRdoubleout -DBRdoubleout -structure - double value 0 -mrk> pvget -p ca -r value -i DBRshortArray -DBRshortArray -structure - short[] value [] -mrk> pvget -p ca -r value -i DBRstringArray -DBRstringArray -structure - string[] value [aa,bb,cc] -mrk> pvget -p ca -r value -i DBRmbbin -DBRmbbin -epics:nt/NTEnum:1.0 - enum_t value - int index 1 - string[] choices [zero,one,two,three,four,five,six,seven,eight,nine,ten,eleven,twelve,thirteen,fourteen,fifteen] -mrk> - -``` - - -### alarm,timeStamp,display,control, and valueAlarm - -Each of these is one of the property structures defined in pvData. - -#### Examples - -``` -mrk> pvget -p ca -r alarm -i DBRdoubleout -DBRdoubleout -structure - alarm_t alarm - int severity 2 - int status 3 - string message HIHI - -mrk> pvget -p ca -r timeStamp -i DBRdoubleout -DBRdoubleout -structure - time_t timeStamp - long secondsPastEpoch 1529923341 - int nanoseconds 314916189 - int userTag 0 -mrk> pvget -p ca -r display -i DBRdoubleout -DBRdoubleout -structure - display_t display - double limitLow -10 - double limitHigh 10 - string description - string format F8.2 - string units volts -mrk> pvget -p ca -r control -i DBRdoubleout -DBRdoubleout -structure - control_t control - double limitLow -1e+29 - double limitHigh 1e+29 - double minStep 0 -mrk> pvget -p ca -r valueAlarm -i DBRdoubleout -DBRdoubleout -structure - valueAlarm_t valueAlarm - boolean active false - double lowAlarmLimit -8 - double lowWarningLimit -6 - double highWarningLimit 6 - double highAlarmLimit 8 - int lowAlarmSeverity 0 - int lowWarningSeverity 0 - int highWarningSeverity 0 - int highAlarmSeverity 0 -``` - - - -## DBD to pvData - -### Type Conversion - -Three type systems are involved in accessing data in a **DBRecord** and converting it to/from pvData: - -* DBF The type system used for **DBRecord**s. -* DBR The type system used by **channel access**. -* pvData - -The following gives a summary of the conversions between the type systems: - -``` -rawtype DBF DBR pvData ScalarType - -char[MAX_STRING_SIZE] DBF_STRING DBR_STRING pvString -epicsInt8 DBF_CHAR DBR_CHAR pvByte -epicsUint8 DBF_UCHAR DBR_CHAR pvByte -epicsInt16 DBF_SHORT DBR_SHORT pvShort -epicsUInt16 DBF_USHORT DBR_LONG pvInt -epicsInt32 DBF_LONG DBR_LONG pvInt -epicsUInt32 DBF_ULONG DBR_DOUBLE pvDouble -epicsInt64 DBF_INT64 no support -epicsUInt64 DBF_UINT64 no support -float DBF_FLOAT DBR_FLOAT pvFloat -double DBF_DOUBLE DBR_DOUBLE pvDouble -epicsUInt16 DBF_ENUM DBR_ENUM enum structure -epicsUInt16 DBF_MENU DBR_ENUM enum structure -``` - -Notes: - -* Both DBF_CHAR and DBF_UCHAR go to DBR_CHAR. This is ambigous. -* DBF_USHORT promoted to DBR_LONG -* DBF_ULONG promoted to DBR_DOUBLE -* qsrv provides full access to all DBF types, but the IOC must have qsrv installed. - -### Accessing data in a DBRecord - -An IOC database is a memory resident database of **DBRecord** instances. - -Each **DBRecord** is an instance of one of an extensible set of record types. -Each record type has an associated dbd definition which defines a set of fields for -each record instance. - -For example an aoRecord.dbd has the definition: - -``` -recordtype(ao) { - include "dbCommon.dbd" - field(VAL,DBF_DOUBLE) { - ... - } - field(OVAL,DBF_DOUBLE) { - ... - } - ... many more fields -``` - -In addition each record type has a associated set of support code defined in recSup.h - -``` -/* record support entry table */ -struct typed_rset { - long number; /* number of support routines */ - long (*report)(void *precord); - long (*init)(); - long (*init_record)(struct dbCommon *precord, int pass); - long (*process)(struct dbCommon *precord); - long (*special)(struct dbAddr *paddr, int after); - long (*get_value)(void); /* DEPRECATED set to NULL */ - long (*cvt_dbaddr)(struct dbAddr *paddr); - long (*get_array_info)(struct dbAddr *paddr, long *no_elements, long *offset); - long (*put_array_info)(struct dbAddr *paddr, long nNew); - long (*get_units)(struct dbAddr *paddr, char *units); - long (*get_precision)(const struct dbAddr *paddr, long *precision); - long (*get_enum_str)(const struct dbAddr *paddr, char *pbuffer); - long (*get_enum_strs)(const struct dbAddr *paddr, struct dbr_enumStrs *p); - long (*put_enum_str)(const struct dbAddr *paddr, const char *pbuffer); - long (*get_graphic_double)(struct dbAddr *paddr, struct dbr_grDouble *p); - long (*get_control_double)(struct dbAddr *paddr, struct dbr_ctrlDouble *p); - long (*get_alarm_double)(struct dbAddr *paddr, struct dbr_alDouble *p); -}; -``` - -The methods that support accessing data from the record include: - -``` -cvt_dbaddr Implemented by record types that determine VAL type at record initialization -*array_info Implemented by array record types -get_units Implemented by numeric record types -get_precision Implemented by float and double record types -*_enum_* Implemented by enumerated record types -get_graphic_double NOTE Always returns limits as double -get_control_double NOTE Always returns limits as double -get_alarm_double NOTE Always returns limits as double -``` - -Each of these methods is optional, i. e. record support for a particular record type -only implements methods that make sense for the record type. - -For example the enum methods are only implemented by records that have the definition: - -``` -... - field(VAL,DBF_ENUM) { -... -} -... -``` - - -### Channel Access Data - -A client can access any public field of a **DBRecord**; -Each **DBRecord** instance has a record name that is unique within the local area network. - -A channel name is a **recordname.fieldName**. - -If the fieldname is not specified then **.VAL** is assumed and the record support methods shown -above can also be used to get additional data from the record. - -Any field that is accessable by client code must have a vald DBF_ type. - -A client gets/puts data via a **DBR_*** request. - -The basic DBR types are: -``` -rawtype DBR - -char[MAX_STRING_SIZE] DBR_STRING -epicsInt8 DBR_CHAR -epicsInt16 DBR_SHORT -epicsInt32 DBR_LONG -float DBF_FLOAT -double DBF_DOUBLE -epicsUInt16 DBR_ENUM -``` - -In addition to the DBR basic types the following DBR types provide additional data: - -``` -DBR one of the types above. -DBR_STATUS_* adds status and severity to DBR. -DBR_TIME_* adds epicsTimeStamp to DBR_STATUS. -DBR_GR_* adds display limits to DBR_STATUS. NOTE: no epicsTimeStamp -DBR_CTRL_ adds control limits to DBR_GR. NOTE: no epicsTimeStamp -DBR_CTRL_ENUM This is a special case. -``` - -NOTES: - -* status, severity, and epicsTimeStamp are the same for each DBR type. -* limits have the same types as the correspondng DBR type. -* server converts limits from double to the DBR type. -* GR and CTRL have precision only for DBR_FLOAT and DBR_DOUBLE - - -Some examples: - -``` -DBR_STS_DOUBLE returns a double status structure (dbr_sts_double) -where -struct dbr_sts_double{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - dbr_long_t RISC_pad; /* RISC alignment */ - dbr_double_t value; /* current value */ -}; - -DBR_TIME_DOUBLE returns a double time structure (dbr_time_double) -where -struct dbr_time_double{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - epicsTimeStamp stamp; /* time stamp */ - dbr_long_t RISC_pad; /* RISC alignment */ - dbr_double_t value; /* current value */ -}; - -DBR_GR_SHORT returns a graphic short structure (dbr_gr_short) -where -struct dbr_gr_short{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - char units[MAX_UNITS_SIZE]; /* units of value */ - dbr_short_t upper_disp_limit; /* upper limit of graph */ - dbr_short_t lower_disp_limit; /* lower limit of graph */ - dbr_short_t upper_alarm_limit; - dbr_short_t upper_warning_limit; - dbr_short_t lower_warning_limit; - dbr_short_t lower_alarm_limit; - dbr_short_t value; /* current value */ -}; - -DBR_GR_DOUBLE returns a graphic double structure (dbr_gr_double) -where -struct dbr_gr_double{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - dbr_short_t precision; /* number of decimal places */ - dbr_short_t RISC_pad0; /* RISC alignment */ - char units[MAX_UNITS_SIZE]; /* units of value */ - dbr_double_t upper_disp_limit; /* upper limit of graph */ - dbr_double_t lower_disp_limit; /* lower limit of graph */ - dbr_double_t upper_alarm_limit; - dbr_double_t upper_warning_limit; - dbr_double_t lower_warning_limit; - dbr_double_t lower_alarm_limit; - dbr_double_t value; /* current value */ -}; - -DBR_CTRL_DOUBLE returns a control double structure (dbr_ctrl_double) -where -struct dbr_ctrl_double{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - dbr_short_t precision; /* number of decimal places */ - dbr_short_t RISC_pad0; /* RISC alignment */ - char units[MAX_UNITS_SIZE]; /* units of value */ - dbr_double_t upper_disp_limit; /* upper limit of graph */ - dbr_double_t lower_disp_limit; /* lower limit of graph */ - dbr_double_t upper_alarm_limit; - dbr_double_t upper_warning_limit; - dbr_double_t lower_warning_limit; - dbr_double_t lower_alarm_limit; - dbr_double_t upper_ctrl_limit; /* upper control limit */ - dbr_double_t lower_ctrl_limit; /* lower control limit */ - dbr_double_t value; /* current value */ -}; - - -DBR_CTRL_ENUM returns a control enum structure (dbr_ctrl_enum) -where -struct dbr_ctrl_enum{ - dbr_short_t status; /* status of value */ - dbr_short_t severity; /* severity of alarm */ - dbr_short_t no_str; /* number of strings */ - char strs[MAX_ENUM_STATES][MAX_ENUM_STRING_SIZE]; - /* state strings */ - dbr_enum_t value; /* current value */ -}; -``` - -### PVData for a DBRrecord via ca provider - -**pvAccessCPP/src/ca** has files **dbdToPv.h** and **dbdToPv.cpp**. -This is the code that converts between DBD data and pvData. - -This code must decide which of the many **DBR_*** types to use. - - -There is a static method: - -``` -static DbdToPvPtr create( - CAChannelPtr const & caChannel, - epics::pvData::PVStructurePtr const & pvRequest, - IOType ioType); // one of getIO, putIO, and monitorIO -``` - - -When this is called the first thing is to determine which fields are requested by the client. -This is from the set **value**, **alarm**, **timeStamp**. **display**, **control** , and **valueAlarm**. - - -* If the ioType is putIO only **value** is valid. -* If the channel type is **DBR_ENUM** then **display**, **control** , and **valueAlarm** are ignored. -* If the channel is an array then **control** , and **valueAlarm** are ignored. -* If the channel type is **DBR_STRING** then **display**, **control** , and **valueAlarm** are ignored. -* If any of **display**, **control** , and **valueAlarm** are still allowed then **timeStamp** is ignored, -because the DBR type selected will not return the timeStamp. - -If ths channel type is **DBR_ENUM** a one time **ca_array_get_callback(DBR_GR_ENUM...** request is issued -to get the choices for the enumerated value. - -Depending or which fields are still valid, the DBR type is obtained via - -* If any of **display**, **control** ,or **valueAlarm** is valid then **dbf_type_to_DBR_CTRL(caValueType)** . -* else If **alarm** or **timeStamp** is valid then **dbf_type_to_DBR_TIME(caValueType)** . -* else **dbf_type_to_DBR(caValueType)** - -Where **caValueType** is one of DBR_STRING, DBR_SHORT, DBR_FLOAT, DBR_ENUM, DBR_CHAR, DBR_LONG, DBR_DOUBLE. - -If **display** is still valid then the following call is made: - -``` -string name(caChannel->getChannelName() + ".DESC"); -int result = ca_create_channel(name.c_str(), -... -``` -When the channel connects a get is issued to get the value for **display.description**. - -## Developing plugins for ca provider - -This section provides guidelines for code developers that use **ca** to connect a client to a server. -This includes plugins for things like MEDM, EDM, caqtDM, etc. -But also means any code that use **ca**: pvget, pvput, pvaClientCPP, exampleCPP/exampleClient, etc. - -The **channel access** reference manual describes channel context: - -[CA Client Contexts and Application Specific Auxiliary Threads](https://epics.anl.gov/base/R7-0/1-docs/CAref.html#Client2) - -A brief summary of channel context is: - - -* Only the thread that calls CAClientFactory::start() and associated auxillary threads -can call **ca_xxx** functions. - -The public access to **ca** is: - -``` -class epicsShareClass CAClientFactory -{ -public: - /** @brief start provider ca - * - */ - static void start(); - /** @brief get the ca_client_context - * - * This can be called by an application specific auxiliary thread. - * See ca documentation. Not for casual use. - */ - static ca_client_context * get_ca_client_context(); - /** @brief stop provider ca - * - * This does nothing since epicsAtExit is used to destroy the instance. - */ - static void stop(); -}; -``` -Any code that uses **ca** must call **CAClientFactory::start()** before making any pvAccess client requests. - -ca_context_create is called for the thread that calls CAClientFactory::start(). - -If the client creates auxillary threads the make pvAccess client requests then the auxillary threads will automatically become -a **ca** auxilary thread. - - -[Deadlock in ca_clear_subscription()](https://bugs.launchpad.net/epics-base/7.0/+bug/1751380) - -Shows a problem with monitor callbacks. -A test was created that shows that the same problem can occur with a combination of rapid get, put and monitor events. - - -In order to prevent this problem **ca** creates the following threads: -**getEventThread**, **putEventThread**, and **monitorEventThread**. - -All client callbacks are made via one of these threads. -For example a call to the requester's **monitorEvent** method is made from the monitorEventThread. - -**Notes** - -* These threads do not call **ca_attach_context**. -* No **ca_xxx** function should be called from the requester's callback method. - - -