From 99d20ae903040f3d81ebd703c72e18ca05a7a60d Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 16 Feb 2018 06:03:57 -0500 Subject: [PATCH 01/36] get and monitor: only report fields that have changed --- src/ca/caChannel.cpp | 93 +++++++++++++++++++++++++++--------------- src/ca/caChannel.h | 6 +++ src/ca/caProvider.cpp | 6 ++- src/ca/caProviderPvt.h | 2 +- 4 files changed, 72 insertions(+), 35 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index a6c2541..fd92bfd 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -755,7 +755,8 @@ CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & channel, : channel(channel), channelGetRequester(channelGetRequester), - pvRequest(pvRequest) + pvRequest(pvRequest), + firstTime(true) { if(DEBUG_LEVEL>0) { cout << "CAChannelGet::CAChannelGet() " << channel->getChannelName() << endl; @@ -780,7 +781,9 @@ void CAChannelGet::activate() getType = getDBRType(pvRequest, channel->getNativeType()); pvStructure = createPVStructure(channel, getType, pvRequest); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - bitSet->set(0); + pvCopy = PVCopy::create( + createPVStructure(channel, getType, pvRequest), + pvRequest,""); channel->addChannelGet(shared_from_this()); if(channel->getConnectionState()==Channel::CONNECTED) { EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), @@ -793,6 +796,7 @@ void CAChannelGet::channelCreated(const Status& status,Channel::shared_pointer c if(DEBUG_LEVEL>0) { std::cout << "CAChannelGet::channelCreated " << channel->getChannelName() << endl; } + firstTime = true; ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; chtype newType = getDBRType(pvRequest, channel->getNativeType()); @@ -800,7 +804,7 @@ void CAChannelGet::channelCreated(const Status& status,Channel::shared_pointer c getType = getDBRType(pvRequest, channel->getNativeType()); pvStructure = createPVStructure(channel, getType, pvRequest); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - bitSet->set(0); + pvCopy = PVCopy::create(pvStructure,pvRequest,""); } EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); @@ -1195,7 +1199,10 @@ static copyDBRtoPVStructure copyFuncTable[] = void CAChannelGet::getDone(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { - std::cout << "CAChannelGet::getDone " << channel->getChannelName() << endl; + std::cout << "CAChannelGet::getDone " + << channel->getChannelName() + << " firstTime " << (firstTime ? "true" : "false") + << endl; } ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; @@ -1208,6 +1215,12 @@ void CAChannelGet::getDone(struct event_handler_args &args) { throw std::runtime_error("CAChannelGet::getDone no copy func implemented"); } + pvCopy->updateMasterSetBitSet(pvStructure,bitSet); + if(firstTime) { + bitSet->clear(); + bitSet->set(0); + firstTime = false; + } EXCEPTION_GUARD(getRequester->getDone(Status::Ok, shared_from_this(), pvStructure, bitSet)); } else @@ -1235,7 +1248,7 @@ void CAChannelGet::get() Prior to R3.14.12 requesting zero elements in a ca_array_get_callback() call was illegal and would fail immediately. */ - + bitSet->clear(); int result = ca_array_get_callback(getType, 0, channel->getChannelID(), ca_get_handler, this); @@ -1720,7 +1733,6 @@ public: POINTER_DEFINITIONS(CACMonitorQueue); private: size_t queueSize; - bool overrunInProgress; bool isStarted; Mutex mutex; @@ -1729,7 +1741,6 @@ public: CACMonitorQueue( int32 queueSize) : queueSize(queueSize), - overrunInProgress(false), isStarted(false) {} ~CACMonitorQueue() @@ -1739,39 +1750,29 @@ public: { Lock guard(mutex); while(!monitorElementQueue.empty()) monitorElementQueue.pop(); - overrunInProgress = false; isStarted = true; } void stop() { Lock guard(mutex); while(!monitorElementQueue.empty()) monitorElementQueue.pop(); - overrunInProgress = false; isStarted = false; } - // return true if added to queue - bool event(const PVStructurePtr &pvStructure) + + bool event( + const PVStructurePtr &pvStructure, + const MonitorElementPtr & activeElement) { Lock guard(mutex); if(!isStarted) return false; - if(monitorElementQueue.size()==queueSize) - { - overrunInProgress = true; - return false; - } else { - PVStructure::shared_pointer pvs = - getPVDataCreate()->createPVStructure(pvStructure->getStructure()); - pvs->copy(*pvStructure); - MonitorElementPtr monitorElement(new MonitorElement(pvs)); - monitorElement->changedBitSet->set(0); - if(overrunInProgress) { - overrunInProgress = false; - monitorElement->overrunBitSet->set(0); - } - monitorElementQueue.push(monitorElement); - return true; - } - + if(monitorElementQueue.size()==queueSize) return false; + PVStructure::shared_pointer pvs = + getPVDataCreate()->createPVStructure(pvStructure); + MonitorElementPtr monitorElement(new MonitorElement(pvs)); + *(monitorElement->changedBitSet) = *(activeElement->changedBitSet); + *(monitorElement->overrunBitSet) = *(activeElement->overrunBitSet); + monitorElementQueue.push(monitorElement); + return true; } MonitorElementPtr poll() { @@ -1823,7 +1824,8 @@ CAChannelMonitor::CAChannelMonitor( channel(channel), monitorRequester(monitorRequester), pvRequest(pvRequest), - isStarted(false) + isStarted(false), + firstTime(true) { if(DEBUG_LEVEL>0) { cout << "CAChannelMonitor::CAChannelMonitor() " << channel->getChannelName() << endl; @@ -1840,6 +1842,10 @@ void CAChannelMonitor::activate() if(pvStructure) throw std::runtime_error("CAChannelMonitor::activate() was called twice"); getType = getDBRType(pvRequest, channel->getNativeType()); pvStructure = createPVStructure(channel, getType, pvRequest); + activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); + pvCopy = PVCopy::create( + createPVStructure(channel, getType, pvRequest), + pvRequest,""); int32 queueSize = 2; PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); if (pvOptions) { @@ -1865,12 +1871,17 @@ void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_point if(DEBUG_LEVEL>0) { std::cout << "CAChannelMonitor::channelCreated " << channel->getChannelName() << endl; } + firstTime = true; MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; chtype newType = getDBRType(pvRequest, channel->getNativeType()); if(newType!=getType) { getType = getDBRType(pvRequest, channel->getNativeType()); pvStructure = createPVStructure(channel, getType, pvRequest); + activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); + pvCopy = PVCopy::create( + createPVStructure(channel, getType, pvRequest), + pvRequest,""); int32 queueSize = 2; PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); if (pvOptions) { @@ -1917,7 +1928,10 @@ void CAChannelMonitor::channelDisconnect(bool destroy) void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { - std::cout << "CAChannelMonitor::subscriptionEvent " << channel->getChannelName() << endl; + std::cout << "CAChannelMonitor::subscriptionEvent " + << channel->getChannelName() + << " firstTime " << (firstTime ? "true" : "false") + << endl; } MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; @@ -1926,7 +1940,20 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; if (copyFunc) { copyFunc(args.dbr, args.count, pvStructure); - monitorQueue->event(pvStructure); + pvCopy->updateMasterSetBitSet(pvStructure,activeElement->changedBitSet); + if(firstTime) { + activeElement->changedBitSet->clear(); + activeElement->overrunBitSet->clear(); + activeElement->changedBitSet->set(0); + firstTime = false; + } + if(monitorQueue->event(pvStructure,activeElement)) { + activeElement->changedBitSet->clear(); + activeElement->overrunBitSet->clear(); + } else { + *(activeElement->overrunBitSet) |= *(activeElement->changedBitSet); + } + // call monitorRequester even if queue is full requester->monitorEvent(shared_from_this()); } else { @@ -1966,6 +1993,7 @@ epics::pvData::Status CAChannelMonitor::start() */ // TODO DBE_PROPERTY support + monitorQueue->start(); int result = ca_create_subscription(getType, 0, channel->getChannelID(), DBE_VALUE, @@ -1974,7 +2002,6 @@ epics::pvData::Status CAChannelMonitor::start() if (result == ECA_NORMAL) { isStarted = true; - monitorQueue->start(); result = ca_flush_io(); } if (result == ECA_NORMAL) return status; diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 4a2c40c..98e6b1b 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -11,6 +11,7 @@ #include #include +#include /* for CA */ @@ -196,8 +197,10 @@ private: CAChannelPtr channel; ChannelGetRequester::weak_pointer channelGetRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; + volatile bool firstTime; chtype getType; + epics::pvData::PVCopyPtr pvCopy; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; @@ -329,7 +332,10 @@ private: bool isStarted; chtype getType; + volatile bool firstTime; + epics::pvData::PVCopyPtr pvCopy; epics::pvData::PVStructure::shared_pointer pvStructure; + epics::pvData::MonitorElementPtr activeElement; evid eventID; CACMonitorQueuePtr monitorQueue; }; diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index 8f380b6..6ec6fb9 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -169,7 +169,11 @@ void CAChannelProvider::poll() void CAChannelProvider::attachContext() { - if(ca_current_context()) return; + ca_client_context* thread_context = ca_current_context(); + if (thread_context == current_context) return; + if (thread_context != NULL) { + throw std::runtime_error("CAChannelProvider: Foreign CA context in use"); + } int result = ca_attach_context(current_context); if (result != ECA_NORMAL) { std::cout << diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index c5c8883..7f656b8 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -73,7 +73,7 @@ public: void attachContext(); private: - virtual void destroy() EPICS_DEPRECATED {}; + virtual void destroy() EPICS_DEPRECATED {} void initialize(); ca_client_context* current_context; epics::pvData::Mutex channelListMutex; From 9d234c6f2f378c061a81aa299a7d196f92ab8f9f Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 19 Feb 2018 12:31:44 -0500 Subject: [PATCH 02/36] make sure pvCopy has some field order as pvStructure --- src/ca/caChannel.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index fd92bfd..f496afe 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -783,7 +783,8 @@ void CAChannelGet::activate() bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); pvCopy = PVCopy::create( createPVStructure(channel, getType, pvRequest), - pvRequest,""); + CreateRequest::create()->createRequest("field()"), + ""); channel->addChannelGet(shared_from_this()); if(channel->getConnectionState()==Channel::CONNECTED) { EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), @@ -804,7 +805,10 @@ void CAChannelGet::channelCreated(const Status& status,Channel::shared_pointer c getType = getDBRType(pvRequest, channel->getNativeType()); pvStructure = createPVStructure(channel, getType, pvRequest); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - pvCopy = PVCopy::create(pvStructure,pvRequest,""); + pvCopy = PVCopy::create( + createPVStructure(channel, getType, pvRequest), + CreateRequest::create()->createRequest("field()"), + ""); } EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); @@ -1845,7 +1849,8 @@ void CAChannelMonitor::activate() activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); pvCopy = PVCopy::create( createPVStructure(channel, getType, pvRequest), - pvRequest,""); + CreateRequest::create()->createRequest("field()"), + ""); int32 queueSize = 2; PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); if (pvOptions) { @@ -1880,8 +1885,9 @@ void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_point pvStructure = createPVStructure(channel, getType, pvRequest); activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); pvCopy = PVCopy::create( - createPVStructure(channel, getType, pvRequest), - pvRequest,""); + createPVStructure(channel, getType, pvRequest), + CreateRequest::create()->createRequest("field()"), + ""); int32 queueSize = 2; PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); if (pvOptions) { From 2c5abf114dafa9c02430bbd64979ba1fc40b371c Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Wed, 21 Feb 2018 06:59:05 -0500 Subject: [PATCH 03/36] remove volatile from firstTime --- src/ca/caChannel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 98e6b1b..ad3986e 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -197,7 +197,7 @@ private: CAChannelPtr channel; ChannelGetRequester::weak_pointer channelGetRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; - volatile bool firstTime; + bool firstTime; chtype getType; epics::pvData::PVCopyPtr pvCopy; @@ -332,7 +332,7 @@ private: bool isStarted; chtype getType; - volatile bool firstTime; + bool firstTime; epics::pvData::PVCopyPtr pvCopy; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::MonitorElementPtr activeElement; From 71303063a5923afd5e32edbb0a75660057b37209 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Sat, 31 Mar 2018 06:46:25 -0400 Subject: [PATCH 04/36] redo conversion between DBD and pvData --- src/ca/Makefile | 3 +- src/ca/caChannel.cpp | 1331 ++++------------------------------------- src/ca/caChannel.h | 46 +- src/ca/caProvider.cpp | 2 +- src/ca/caStatus.cpp | 69 --- src/ca/dbdToPv.cpp | 926 ++++++++++++++++++++++++++++ src/ca/dbdToPv.h | 138 +++++ src/ca/pv/caStatus.h | 29 - 8 files changed, 1208 insertions(+), 1336 deletions(-) delete mode 100644 src/ca/caStatus.cpp create mode 100644 src/ca/dbdToPv.cpp create mode 100644 src/ca/dbdToPv.h delete mode 100644 src/ca/pv/caStatus.h diff --git a/src/ca/Makefile b/src/ca/Makefile index bc376f0..d2b4d6d 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -11,10 +11,9 @@ SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_ LIB_SYS_LIBS_WIN32 += ws2_32 INC += pv/caProvider.h -INC += pv/caStatus.h pvAccessCA_SRCS += caProvider.cpp pvAccessCA_SRCS += caChannel.cpp -pvAccessCA_SRCS += caStatus.cpp +pvAccessCA_SRCS += dbdToPv.cpp include $(TOP)/configure/RULES diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index f496afe..21ed1e8 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -14,7 +14,6 @@ #define epicsExportSharedSymbols #include "caChannel.h" -#include using namespace epics::pvData; using std::string; @@ -56,248 +55,11 @@ static void ca_connection_handler(struct connection_handler_args args) } } - -static ScalarType dbr2ST[] = -{ - pvString, // DBR_STRING = 0 - pvShort, // DBR_SHORT. DBR_INT = 1 - pvFloat, // DBR_FLOAT = 2 - static_cast(-1), // DBR_ENUM = 3 - pvByte, // DBR_CHAR = 4 - pvInt, // DBR_LONG = 5 - pvDouble // DBR_DOUBLE = 6 -}; - -static Structure::const_shared_pointer createStructure(CAChannel::shared_pointer const & channel, string const & properties) -{ - StandardFieldPtr standardField = getStandardField(); - Structure::const_shared_pointer structure; - - chtype channelType = channel->getNativeType(); - if (channelType != DBR_ENUM) - { - ScalarType st = dbr2ST[channelType]; - structure = (channel->getElementCount() > 1) ? - standardField->scalarArray(st, properties) : - standardField->scalar(st, properties); - } - else - { - // NOTE: enum arrays not supported - structure = standardField->enumerated(properties); - } - - return structure; -} - -static void ca_get_labels_handler(struct event_handler_args args) -{ - - if (args.status == ECA_NORMAL) - { - const dbr_gr_enum* dbr_enum_p = static_cast(args.dbr); - - PVStringArray* labelsArray = static_cast(args.usr); - if (labelsArray) - { - PVStringArray::svector labels(labelsArray->reuse()); - labels.resize(dbr_enum_p->no_str); - std::copy(dbr_enum_p->strs, dbr_enum_p->strs + dbr_enum_p->no_str, labels.begin()); - labelsArray->replace(freeze(labels)); - } - } - else - { - string mess("ca_get_labels_handler "); - mess += ca_message(args.status); - throw std::runtime_error(mess); - } -} - -// Filter out unrequested fields from a source structure according to a -// structure conforming to the format of the "field" field of a pvRequest, -// preserving type ids of unchanged structures. If ntTop is true also preserve -// type id if none of the deleted top-level subfields are the value field. -static StructureConstPtr refineStructure(StructureConstPtr const & source, - StructureConstPtr const & requestedFields, bool ntTop) -{ - if (requestedFields.get() == NULL || requestedFields->getNumberFields() == 0) - return source; - - FieldBuilderPtr builder = getFieldCreate()->createFieldBuilder(); - bool addId = true; - - FieldConstPtrArray fields = source->getFields(); - StringArray names = source->getFieldNames(); - size_t i = 0; - for (FieldConstPtrArray::const_iterator it = fields.begin(); it != fields.end(); ++it) - { - FieldConstPtr field = *it; - const std::string & name = names[i++]; - FieldConstPtr reqField = requestedFields->getField(name); - if (reqField.get()) - { - if (field->getType() != structure || (reqField->getType() != structure)) - builder->add(name,field); - else - { - StructureConstPtr substruct = - std::tr1::dynamic_pointer_cast(field); - - StructureConstPtr reqSubstruct = - std::tr1::dynamic_pointer_cast(reqField); - - StructureConstPtr nested = refineStructure(substruct, reqSubstruct, false); - builder->add(name,nested); - if (nested->getID() != substruct->getID()) - addId = false; - } - } - else if (!ntTop || name == "value") - addId = false; - } - if (addId) - builder->setId(source->getID()); - return builder->createStructure(); -} - -static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, string const & properties, PVStructurePtr pvRequest) -{ - StructureConstPtr unrefinedStructure = createStructure(channel, properties); - - PVStructurePtr fieldPVStructure = pvRequest->getSubField("field"); - StructureConstPtr finalStructure = fieldPVStructure.get() ? - refineStructure(unrefinedStructure, fieldPVStructure->getStructure(),true) : - unrefinedStructure; - - PVStructure::shared_pointer pvStructure = getPVDataCreate()->createPVStructure(finalStructure); - if (channel->getNativeType() == DBR_ENUM) - { - PVScalarArrayPtr pvScalarArray = pvStructure->getSubField("value.choices"); - - // TODO avoid getting labels if DBR_GR_ENUM or DBR_CTRL_ENUM is used in subsequent get - int result = ca_array_get_callback( - DBR_GR_ENUM, 1, channel->getChannelID(), ca_get_labels_handler, pvScalarArray.get()); - if (result == ECA_NORMAL) - { - result = ca_flush_io(); - // NOTE: we do not wait here, since all subsequent request (over TCP) is serialized - // and will guarantee that ca_get_labels_handler is called first - } - if (result != ECA_NORMAL){ - string mess(channel->getChannelName() + "PVStructure::shared_pointer createPVStructure "); - mess += "failed to get labels for enum "; - throw std::runtime_error(mess); - } - } - return pvStructure; -} - -static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, chtype dbrType, PVStructurePtr pvRequest) -{ - // Match to closest DBR type - // NOTE: value is always there - string properties; - bool isArray = channel->getElementCount() > 1; - if (dbrType >= DBR_CTRL_STRING) // 28 - { - if (dbrType != DBR_CTRL_STRING && dbrType != DBR_CTRL_ENUM) - { - if (isArray) - properties = "value,alarm,display"; - else - properties = "value,alarm,display,valueAlarm,control"; - } - else - properties = "value,alarm"; - } - else if (dbrType >= DBR_GR_STRING) // 21 - { - if (dbrType != DBR_GR_STRING && dbrType != DBR_GR_ENUM) - { - if (isArray) - properties = "value,alarm,display"; - else - properties = "value,alarm,display,valueAlarm"; - } - else - properties = "value,alarm"; - } - else if (dbrType >= DBR_TIME_STRING) // 14 - properties = "value,alarm,timeStamp"; - else if (dbrType >= DBR_STS_STRING) // 7 - properties = "value,alarm"; - else - properties = "value"; - - return createPVStructure(channel, properties, pvRequest); -} - - void CAChannel::connected() { if(DEBUG_LEVEL>0) { cout<< "CAChannel::connected " << channelName << endl; } - std::queue putQ; - std::queue getQ; - std::queue monitorQ; - { - Lock lock(requestsMutex); - // we assume array if element count > 1 - elementCount = ca_element_count(channelID); - channelType = ca_field_type(channelID); - bool isArray = elementCount > 1; - - // no valueAlarm and control,display for non-numeric type - // no control,display for numeric arrays - string allProperties = - (channelType != DBR_STRING && channelType != DBR_ENUM) ? - isArray ? - "value,timeStamp,alarm,display" : - "value,timeStamp,alarm,display,valueAlarm,control" : - "value,timeStamp,alarm"; - Structure::const_shared_pointer structure = createStructure( - shared_from_this(), allProperties); - - // TODO we need only Structure here - this->structure = structure; - - std::vector::const_iterator getiter; - for (getiter = getList.begin(); getiter != getList.end(); ++getiter) { - CAChannelGetPtr temp = (*getiter).lock(); - if(!temp) continue; - getQ.push(temp); - } - std::vector::const_iterator putiter; - for (putiter = putList.begin(); putiter != putList.end(); ++putiter) { - CAChannelPutPtr temp = (*putiter).lock(); - if(!temp) continue; - putQ.push(temp); - } - std::vector::const_iterator monitoriter; - for (monitoriter = monitorList.begin(); monitoriter != monitorList.end(); ++monitoriter) { - CAChannelMonitorPtr temp = (*monitoriter).lock(); - if(!temp) continue; - monitorQ.push(temp); - } - } - while(!putQ.empty()) { - putQ.front()->channelCreated(Status::Ok,shared_from_this()); - putQ.pop(); - } - while(!getQ.empty()) { - getQ.front()->channelCreated(Status::Ok,shared_from_this()); - getQ.pop(); - } - while(!monitorQ.empty()) { - monitorQ.front()->channelCreated(Status::Ok,shared_from_this()); - monitorQ.pop(); - } - while(!getFieldQueue.empty()) { - getFieldQueue.front()->callRequester(shared_from_this()); - getFieldQueue.pop(); - } while(!putQueue.empty()) { putQueue.front()->activate(); putQueue.pop(); @@ -322,41 +84,17 @@ void CAChannel::disconnected() if(DEBUG_LEVEL>0) { cout<< "CAChannel::disconnected " << channelName << endl; } - std::queue putQ; - std::queue getQ; - std::queue monitorQ; - { - Lock lock(requestsMutex); - std::vector::const_iterator getiter; - for (getiter = getList.begin(); getiter != getList.end(); ++getiter) { - CAChannelGetPtr temp = (*getiter).lock(); - if(!temp) continue; - getQ.push(temp); - } - std::vector::const_iterator putiter; - for (putiter = putList.begin(); putiter != putList.end(); ++putiter) { - CAChannelPutPtr temp = (*putiter).lock(); - if(!temp) continue; - putQ.push(temp); - } - std::vector::const_iterator monitoriter; - for (monitoriter = monitorList.begin(); monitoriter != monitorList.end(); ++monitoriter) { - CAChannelMonitorPtr temp = (*monitoriter).lock(); - if(!temp) continue; - monitorQ.push(temp); - } + while(!putQueue.empty()) { + putQueue.front()->channelDisconnect(false); + putQueue.pop(); } - while(!putQ.empty()) { - putQ.front()->channelDisconnect(false); - putQ.pop(); + while(!getQueue.empty()) { + getQueue.front()->channelDisconnect(false); + getQueue.pop(); } - while(!getQ.empty()) { - getQ.front()->channelDisconnect(false); - getQ.pop(); - } - while(!monitorQ.empty()) { - monitorQ.front()->channelDisconnect(false); - monitorQ.pop(); + while(!monitorQueue.empty()) { + monitorQueue.front()->channelDisconnect(false); + monitorQueue.pop(); } ChannelRequester::shared_pointer req(channelRequester.lock()); if(req) { @@ -374,8 +112,6 @@ CAChannel::CAChannel(std::string const & _channelName, channelProvider(_channelProvider), channelRequester(_channelRequester), channelID(0), - channelType(0), - elementCount(0), channelCreated(false) { if(DEBUG_LEVEL>0) { @@ -430,7 +166,10 @@ void CAChannel::disconnectChannel() channelCreated = false; } /* Clear CA Channel */ - attachContext(); + CAChannelProviderPtr provider(channelProvider.lock()); + if(provider) { + std::tr1::static_pointer_cast(provider)->attachContext(); + } int result = ca_clear_channel(channelID); if (result == ECA_NORMAL) return; string mess("CAChannel::disconnectChannel() "); @@ -491,24 +230,6 @@ chid CAChannel::getChannelID() return channelID; } - -chtype CAChannel::getNativeType() -{ - return channelType; -} - - -unsigned CAChannel::getElementCount() -{ - return elementCount; -} - -Structure::const_shared_pointer CAChannel::getStructure() -{ - return structure; -} - - std::tr1::shared_ptr CAChannel::getProvider() { return channelProvider.lock(); @@ -676,7 +397,10 @@ void CAChannelGetField::callRequester(CAChannelPtr const & caChannel) } GetFieldRequester::shared_pointer requester(getFieldRequester.lock()); if(!requester) return; - epics::pvData::Structure::const_shared_pointer structure(caChannel->getStructure()); + PVStructurePtr pvRequest(createRequest("")); + DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO); + PVStructurePtr pvStructure = dbdToPv->createPVStructure(); + epics::pvData::Structure::const_shared_pointer structure(pvStructure->getStructure()); Field::const_shared_pointer field = subField.empty() ? std::tr1::static_pointer_cast(structure) : @@ -700,42 +424,11 @@ void CAChannel::attachContext() CAChannelProviderPtr provider(channelProvider.lock()); if(provider) { std::tr1::static_pointer_cast(provider)->attachContext(); + return; } -} - -static chtype getDBRType(PVStructure::shared_pointer const & pvRequest, chtype nativeType) -{ - // get "field" sub-structure - PVStructure::shared_pointer fieldSubField = - std::tr1::dynamic_pointer_cast(pvRequest->getSubField("field")); - if (!fieldSubField) - fieldSubField = pvRequest; - Structure::const_shared_pointer fieldStructure = fieldSubField->getStructure(); - - // no fields - if (fieldStructure->getNumberFields() == 0) - { - return static_cast(static_cast(nativeType) + DBR_TIME_STRING); - } - // control -> DBR_CTRL_ - if (fieldStructure->getField("control")) - return static_cast(static_cast(nativeType) + DBR_CTRL_STRING); - - // display/valueAlarm -> DBR_GR_ - if (fieldStructure->getField("display") || fieldStructure->getField("valueAlarm")) - return static_cast(static_cast(nativeType) + DBR_GR_STRING); - - // timeStamp -> DBR_TIME_ - // NOTE: that only DBR_TIME_ type holds timestamp, therefore if you request for - // the fields above, you will never get timestamp - if (fieldStructure->getField("timeStamp")) - return static_cast(static_cast(nativeType) + DBR_TIME_STRING); - - // alarm -> DBR_STS_ - if (fieldStructure->getField("alarm")) - return static_cast(static_cast(nativeType) + DBR_STS_STRING); - - return nativeType; + string mess("CAChannel::attachContext "); + mess += getChannelName(); + throw std::runtime_error(mess); } size_t CAChannelGet::num_instances; @@ -745,23 +438,20 @@ CAChannelGetPtr CAChannelGet::create( ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { + if(DEBUG_LEVEL>0) { + cout << "CAChannelGet::create " << channel->getChannelName() << endl; + } return CAChannelGetPtr(new CAChannelGet(channel, channelGetRequester, pvRequest)); } - CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) : channel(channel), channelGetRequester(channelGetRequester), - pvRequest(pvRequest), - firstTime(true) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelGet::CAChannelGet() " << channel->getChannelName() << endl; - } -} + pvRequest(pvRequest) +{} CAChannelGet::~CAChannelGet() { @@ -770,46 +460,20 @@ CAChannelGet::~CAChannelGet() } } +void CAChannelGet::channelCreated(const epics::pvData::Status& s,Channel::shared_pointer const & c) +{} + void CAChannelGet::activate() { ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::activate " << channel->getChannelName() << " requester "<getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); + if(DEBUG_LEVEL>0) { + std::cout << "CAChannelGet::activate " << channel->getChannelName() << endl; + } + dbdToPv = DbdToPv::create(channel,pvRequest,getIO); + pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - pvCopy = PVCopy::create( - createPVStructure(channel, getType, pvRequest), - CreateRequest::create()->createRequest("field()"), - ""); channel->addChannelGet(shared_from_this()); - if(channel->getConnectionState()==Channel::CONNECTED) { - EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), - pvStructure->getStructure())); - } -} - -void CAChannelGet::channelCreated(const Status& status,Channel::shared_pointer const & cl) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::channelCreated " << channel->getChannelName() << endl; - } - firstTime = true; - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - chtype newType = getDBRType(pvRequest, channel->getNativeType()); - if(newType!=getType) { - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - pvCopy = PVCopy::create( - createPVStructure(channel, getType, pvRequest), - CreateRequest::create()->createRequest("field()"), - ""); - } EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } @@ -818,14 +482,9 @@ void CAChannelGet::channelStateChange( Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) { - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::channelStateChange " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) { - EXCEPTION_GUARD(getRequester->channelDisconnect(connectionState==Channel::DESTROYED);) - } + string mess("CAChannelGet::channelStateChange was called "); + mess += channel->getChannelName(); + throw std::runtime_error(mess); } void CAChannelGet::channelDisconnect(bool destroy) @@ -836,6 +495,7 @@ void CAChannelGet::channelDisconnect(bool destroy) ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; EXCEPTION_GUARD(getRequester->channelDisconnect(destroy);) + if(!destroy) channel->addChannelGet(shared_from_this()); } /* --------------- epics::pvAccess::ChannelGet --------------- */ @@ -848,394 +508,21 @@ static void ca_get_handler(struct event_handler_args args) channelGet->getDone(args); } -typedef void (*copyDBRtoPVStructure)(const void * from, unsigned count, PVStructure::shared_pointer const & to); - - -// template -template -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - if (count == 1) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) value->put(static_cast(dbr)[0]); - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - typename aF::svector temp(value->reuse()); - temp.resize(count); - std::copy(static_cast(dbr), static_cast(dbr) + count, temp.begin()); - value->replace(freeze(temp)); - } - } -} - -#if defined(__vxworks) || defined(__rtems__) -// dbr_long_t is defined as "int", pvData uses int32 which can be defined as "long int" (32-bit) -// template -template<> -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - if (count == 1) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) value->put(static_cast(dbr)[0]); - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) - { - PVIntArray::svector temp(value->reuse()); - temp.resize(count); - std::copy(static_cast(dbr), static_cast(dbr) + count, temp.begin()); - value->replace(freeze(temp)); - } - } -} -#endif - -// string specialization -template<> -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - if (count == 1) - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) value->put(std::string(static_cast(dbr))); - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubField("value"); - if (value.get()) - { - const dbr_string_t* dbrStrings = static_cast(dbr); - PVStringArray::svector sA(value->reuse()); - sA.resize(count); - std::copy(dbrStrings, dbrStrings + count, sA.begin()); - value->replace(freeze(sA)); - } - } -} - -// enum specialization -template<> -void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - if (count == 1) - { - PVIntPtr value = pvStructure->getSubField("value.index"); - if (value.get()) value->put(static_cast(dbr)[0]); - } - else - { - // not supported - std::cerr << "caChannel: array of enums not supported" << std::endl; - } -} - -// template -template -void copy_DBR_STS(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const T* data = static_cast(dbr); - - PVStructure::shared_pointer alarm = pvStructure->getSubField("alarm"); - if (alarm.get()) - { - PVIntPtr status = alarm->getSubField("status"); - if (status.get()) status->put(dbrStatus2alarmStatus[data->status]); - - PVIntPtr severity = alarm->getSubField("severity"); - if (severity.get()) severity->put(data->severity); - - PVStringPtr message = alarm->getSubField("message"); - if (message.get()) message->put(dbrStatus2alarmMessage[data->status]); - } - - copy_DBR(&data->value, count, pvStructure); -} - -// template -template -void copy_DBR_TIME(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const T* data = static_cast(dbr); - - PVStructure::shared_pointer ts = pvStructure->getSubField("timeStamp"); - if (ts.get()) - { - epics::pvData::int64 spe = data->stamp.secPastEpoch; - spe += 7305*86400; - - PVLongPtr secondsPastEpoch = ts->getSubField("secondsPastEpoch"); - if (secondsPastEpoch.get()) secondsPastEpoch->put(spe); - - PVIntPtr nanoseconds = ts->getSubField("nanoseconds"); - if (nanoseconds.get()) nanoseconds->put(data->stamp.nsec); - } - - copy_DBR_STS(dbr, count, pvStructure); -} - - -template -void copy_format(const void * /*dbr*/, PVStructure::shared_pointer const & pvDisplayStructure) -{ - PVStringPtr format = pvDisplayStructure->getSubField("format"); - if (format.get()) format->put("%d"); -} - -#define COPY_FORMAT_FOR(T) \ -template <> \ -void copy_format(const void * dbr, PVStructure::shared_pointer const & pvDisplayStructure) \ -{ \ - const T* data = static_cast(dbr); \ -\ - if (data->precision) \ - { \ - char fmt[16]; \ - sprintf(fmt, "%%.%df", data->precision); \ - PVStringPtr format = pvDisplayStructure->getSubField("format");\ - if (format.get()) format->put(std::string(fmt));\ - } \ - else \ - { \ - PVStringPtr format = pvDisplayStructure->getSubField("format");\ - if (format.get()) format->put("%f");\ - } \ -} - -COPY_FORMAT_FOR(dbr_gr_float) -COPY_FORMAT_FOR(dbr_ctrl_float) -COPY_FORMAT_FOR(dbr_gr_double) -COPY_FORMAT_FOR(dbr_ctrl_double) - -#undef COPY_FORMAT_FOR - -// template -template -void copy_DBR_GR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const T* data = static_cast(dbr); - - PVStructurePtr alarm = pvStructure->getSubField("alarm"); - if (alarm.get()) - { - PVIntPtr status = alarm->getSubField("status"); - if (status.get()) status->put(dbrStatus2alarmStatus[data->status]); - - PVIntPtr severity = alarm->getSubField("severity"); - if (severity.get()) severity->put(data->severity); - - PVStringPtr message = alarm->getSubField("message"); - if (message.get()) message->put(dbrStatus2alarmMessage[data->status]); - } - - PVStructurePtr disp = pvStructure->getSubField("display"); - if (disp.get()) - { - PVStringPtr units = disp->getSubField("units"); - if (units.get()) units->put(std::string(data->units)); - - PVDoublePtr limitHigh = disp->getSubField("limitHigh"); - if (limitHigh.get()) limitHigh->put(data->upper_disp_limit); - - PVDoublePtr limitLow = disp->getSubField("limitLow"); - if (limitLow.get()) limitLow->put(data->lower_disp_limit); - - copy_format(dbr, disp); - } - - PVStructurePtr va = pvStructure->getSubField("valueAlarm"); - if (va.get()) - { - std::tr1::shared_ptr highAlarmLimit = va->getSubField("highAlarmLimit"); - if (highAlarmLimit.get()) highAlarmLimit->put(data->upper_alarm_limit); - - std::tr1::shared_ptr highWarningLimit = va->getSubField("highWarningLimit"); - if (highWarningLimit.get()) highWarningLimit->put(data->upper_warning_limit); - - std::tr1::shared_ptr lowWarningLimit = va->getSubField("lowWarningLimit"); - if (lowWarningLimit.get()) lowWarningLimit->put(data->lower_warning_limit); - - std::tr1::shared_ptr lowAlarmLimit = va->getSubField("lowAlarmLimit"); - if (lowAlarmLimit.get()) lowAlarmLimit->put(data->lower_alarm_limit); - } - - copy_DBR(&data->value, count, pvStructure); -} - -// enum specialization -template<> -void copy_DBR_GR -(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const dbr_gr_enum* data = static_cast(dbr); - - copy_DBR_STS(data, count, pvStructure); -} - - -// template -template -void copy_DBR_CTRL(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const T* data = static_cast(dbr); - - PVStructure::shared_pointer alarm = pvStructure->getSubField("alarm"); - if (alarm.get()) - { - PVIntPtr status = alarm->getSubField("status"); - if (status.get()) status->put(dbrStatus2alarmStatus[data->status]); - - PVIntPtr severity = alarm->getSubField("severity"); - if (severity.get()) severity->put(data->severity); - - PVStringPtr message = alarm->getSubField("message"); - if (message.get()) message->put(dbrStatus2alarmMessage[data->status]); - } - - PVStructurePtr disp = pvStructure->getSubField("display"); - if (disp.get()) - { - PVStringPtr units = disp->getSubField("units"); - if (units.get()) units->put(std::string(data->units)); - - PVDoublePtr limitHigh = disp->getSubField("limitHigh"); - if (limitHigh.get()) limitHigh->put(data->upper_disp_limit); - - PVDoublePtr limitLow = disp->getSubField("limitLow"); - if (limitLow.get()) limitLow->put(data->lower_disp_limit); - - copy_format(dbr, disp); - } - - PVStructurePtr va = pvStructure->getSubField("valueAlarm"); - if (va.get()) - { - std::tr1::shared_ptr highAlarmLimit = va->getSubField("highAlarmLimit"); - if (highAlarmLimit.get()) highAlarmLimit->put(data->upper_alarm_limit); - - std::tr1::shared_ptr highWarningLimit = va->getSubField("highWarningLimit"); - if (highWarningLimit.get()) highWarningLimit->put(data->upper_warning_limit); - - std::tr1::shared_ptr lowWarningLimit = va->getSubField("lowWarningLimit"); - if (lowWarningLimit.get()) lowWarningLimit->put(data->lower_warning_limit); - - std::tr1::shared_ptr lowAlarmLimit = va->getSubField("lowAlarmLimit"); - if (lowAlarmLimit.get()) lowAlarmLimit->put(data->lower_alarm_limit); - } - - PVStructurePtr ctrl = pvStructure->getSubField("control"); - if (ctrl.get()) - { - PVDoublePtr limitHigh = ctrl->getSubField("limitHigh"); - if (limitHigh.get()) limitHigh->put(data->upper_ctrl_limit); - - PVDoublePtr limitLow = ctrl->getSubField("limitLow"); - if (limitLow.get()) limitLow->put(data->lower_ctrl_limit); - } - - copy_DBR(&data->value, count, pvStructure); -} - -// enum specialization -template<> -void copy_DBR_CTRL -(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) -{ - const dbr_ctrl_enum* data = static_cast(dbr); - - copy_DBR_STS(data, count, pvStructure); -} - - -static copyDBRtoPVStructure copyFuncTable[] = -{ - copy_DBR, // DBR_STRING - copy_DBR, // DBR_INT, DBR_SHORT - copy_DBR, // DBR_FLOAT - copy_DBR, // DBR_ENUM - copy_DBR, // DBR_CHAR - copy_DBR, // DBR_LONG - copy_DBR, // DBR_DOUBLE - - copy_DBR_STS, // DBR_STS_STRING - copy_DBR_STS, // DBR_STS_INT, DBR_STS_SHORT - copy_DBR_STS, // DBR_STS_FLOAT - copy_DBR_STS, // DBR_STS_ENUM - copy_DBR_STS, // DBR_STS_CHAR - copy_DBR_STS, // DBR_STS_LONG - copy_DBR_STS, // DBR_STS_DOUBLE - - copy_DBR_TIME, // DBR_TIME_STRING - copy_DBR_TIME, // DBR_TIME_INT, DBR_TIME_SHORT - copy_DBR_TIME, // DBR_TIME_FLOAT - copy_DBR_TIME, // DBR_TIME_ENUM - copy_DBR_TIME, // DBR_TIME_CHAR - copy_DBR_TIME, // DBR_TIME_LONG - copy_DBR_TIME, // DBR_TIME_DOUBLE - - copy_DBR_STS, // DBR_GR_STRING -> DBR_STS_STRING - copy_DBR_GR, // DBR_GR_INT, DBR_GR_SHORT - copy_DBR_GR, // DBR_GR_FLOAT - copy_DBR_GR, // DBR_GR_ENUM - copy_DBR_GR, // DBR_GR_CHAR - copy_DBR_GR, // DBR_GR_LONG - copy_DBR_GR, // DBR_GR_DOUBLE - - copy_DBR_STS, // DBR_CTRL_STRING -> DBR_STS_STRING - copy_DBR_CTRL, // DBR_CTRL_INT, DBR_CTRL_SHORT - copy_DBR_CTRL, // DBR_CTRL_FLOAT - copy_DBR_CTRL, // DBR_CTRL_ENUM - copy_DBR_CTRL, // DBR_CTRL_CHAR - copy_DBR_CTRL, // DBR_CTRL_LONG - copy_DBR_CTRL // DBR_CTRL_DOUBLE -}; - } // namespace void CAChannelGet::getDone(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { std::cout << "CAChannelGet::getDone " - << channel->getChannelName() - << " firstTime " << (firstTime ? "true" : "false") - << endl; + << channel->getChannelName() << endl; } + ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; - if (args.status == ECA_NORMAL) - { - copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; - if (copyFunc) - copyFunc(args.dbr, args.count, pvStructure); - else - { - throw std::runtime_error("CAChannelGet::getDone no copy func implemented"); - } - pvCopy->updateMasterSetBitSet(pvStructure,bitSet); - if(firstTime) { - bitSet->clear(); - bitSet->set(0); - firstTime = false; - } - EXCEPTION_GUARD(getRequester->getDone(Status::Ok, shared_from_this(), pvStructure, bitSet)); - } - else - { - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - EXCEPTION_GUARD(getRequester->getDone(errorStatus, shared_from_this(), - PVStructure::shared_pointer(), BitSet::shared_pointer())); - } + Status status = dbdToPv->getFromDBD(pvStructure,bitSet,args); + EXCEPTION_GUARD(getRequester->getDone(status, shared_from_this(), pvStructure, bitSet)); } - void CAChannelGet::get() { if(DEBUG_LEVEL>1) { @@ -1244,31 +531,22 @@ void CAChannelGet::get() ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; channel->attachContext(); - - /* - From R3.14.12 onwards ca_array_get_callback() replies will give a CA client application the current number - of elements in an array field, provided they specified an element count of zero in their original request. - The element count is passed in the callback argument structure. - Prior to R3.14.12 requesting zero elements in a ca_array_get_callback() call was illegal and would fail - immediately. - */ bitSet->clear(); - int result = ca_array_get_callback(getType, + int result = ca_array_get_callback(dbdToPv->getRequestType(), 0, channel->getChannelID(), ca_get_handler, this); if (result == ECA_NORMAL) { result = ca_flush_io(); } - if (result == ECA_NORMAL) return; - string mess("CAChannelGet::get "); - mess += ca_message(result); - throw std::runtime_error(mess); + if (result != ECA_NORMAL) + { + string mess("CAChannelGet::get "); + mess += channel->getChannelName() + " message " + ca_message(result); + throw std::runtime_error(mess); + } } - -/* --------------- epics::pvData::ChannelRequest --------------- */ - Channel::shared_pointer CAChannelGet::getChannel() { return channel; @@ -1282,26 +560,19 @@ void CAChannelGet::lastRequest() { } - +size_t CAChannelPut::num_instances; CAChannelPutPtr CAChannelPut::create( CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { + if(DEBUG_LEVEL>0) { + cout << "CAChannelPut::create " << channel->getChannelName() << endl; + } return CAChannelPutPtr(new CAChannelPut(channel, channelPutRequester, pvRequest)); } - -CAChannelPut::~CAChannelPut() -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelPut::~CAChannelPut() " << channel->getChannelName() << endl; - } -} - -size_t CAChannelPut::num_instances; - CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) @@ -1310,56 +581,34 @@ CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel, channelPutRequester(channelPutRequester), pvRequest(pvRequest), block(false) +{} + +CAChannelPut::~CAChannelPut() { if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::CAChannePut() " << channel->getChannelName() << endl; + std::cout << "CAChannelPut::~CAChannelPut() " << channel->getChannelName() << endl; } } +void CAChannelPut::channelCreated(const Status& status,Channel::shared_pointer const & c) +{} + void CAChannelPut::activate() { + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); + if(!putRequester) return; if(DEBUG_LEVEL>0) { cout << "CAChannelPut::activate " << channel->getChannelName() << endl; } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - if(pvStructure) throw std::runtime_error("CAChannelPut::activate() was called twice"); - getType = getDBRType(pvRequest,channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); + dbdToPv = DbdToPv::create(channel,pvRequest,putIO); + pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); PVStringPtr pvString = pvRequest->getSubField("record._options.block"); if(pvString) { std::string val = pvString->get(); if(val.compare("true")==0) block = true; } - bitSet->set(pvStructure->getSubFieldT("value")->getFieldOffset()); channel->addChannelPut(shared_from_this()); - if(channel->getConnectionState()==Channel::CONNECTED) { - EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(), - pvStructure->getStructure())); - } -} - - -void CAChannelPut::channelCreated(const Status& status,Channel::shared_pointer const & c) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::channelCreated " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - chtype newType = getDBRType(pvRequest, channel->getNativeType()); - if(newType!=getType) { - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - PVStringPtr pvString = pvRequest->getSubField("record._options.block"); - if(pvString) { - std::string val = pvString->get(); - if(val.compare("true")==0) block = true; - } - bitSet->set(0); - } EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } @@ -1368,14 +617,9 @@ void CAChannelPut::channelStateChange( Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) { - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::channelStateChange " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) { - EXCEPTION_GUARD(putRequester->channelDisconnect(connectionState==Channel::DESTROYED);) - } + string mess("CAChannelPut::channelStateChange was called "); + mess += channel->getChannelName(); + throw std::runtime_error(mess); } void CAChannelPut::channelDisconnect(bool destroy) @@ -1386,224 +630,21 @@ void CAChannelPut::channelDisconnect(bool destroy) ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; EXCEPTION_GUARD(putRequester->channelDisconnect(destroy);) + if(!destroy) channel->addChannelPut(shared_from_this()); } /* --------------- epics::pvAccess::ChannelPut --------------- */ namespace { -static void ca_put_handler(struct event_handler_args args) -{ - CAChannelPut *channelPut = static_cast(args.usr); - channelPut->putDone(args); -} - - static void ca_put_get_handler(struct event_handler_args args) { CAChannelPut *channelPut = static_cast(args.usr); channelPut->getDone(args); } -typedef int (*doPut)(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & from); - - -// template -template -int doPut_pvStructure(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & pvStructure) -{ - bool isScalarValue = pvStructure->getStructure()->getField("value")->getType() == scalar; - - if (isScalarValue) - { - std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubFieldT("value")); - - pT val = value->get(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback(channel->getNativeType(), 1, - channel->getChannelID(), &val, - ca_put_handler, usrArg); - } else { - result = ca_array_put(channel->getNativeType(), 1, - channel->getChannelID(), &val); - } - - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubFieldT("value"); - - const pT* val = value->view().data(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback(channel->getNativeType(), - static_cast(value->getLength()), - channel->getChannelID(), val, - ca_put_handler, usrArg); - } else { - result = ca_array_put(channel->getNativeType(), - static_cast(value->getLength()), - channel->getChannelID(), val); - } - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } -} - -// string specialization -template<> -int doPut_pvStructure(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & pvStructure) -{ - bool isScalarValue = pvStructure->getStructure()->getField("value")->getType() == scalar; - - if (isScalarValue) - { - std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubFieldT("value")); - - string val = value->get(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback( - channel->getNativeType(), 1, - channel->getChannelID(), val.c_str(), - ca_put_handler, usrArg); - } else { - result = ca_array_put( - channel->getNativeType(), 1, - channel->getChannelID(), val.c_str()); - } - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } - else - { - std::tr1::shared_ptr value = pvStructure->getSubFieldT("value"); - - PVStringArray::const_svector stringArray(value->view()); - - size_t arraySize = stringArray.size(); - size_t ca_stringBufferSize = arraySize * MAX_STRING_SIZE; - char* ca_stringBuffer = new char[ca_stringBufferSize]; - memset(ca_stringBuffer, 0, ca_stringBufferSize); - - char *p = ca_stringBuffer; - for(size_t i = 0; i < arraySize; i++) - { - string value = stringArray[i]; - size_t len = value.length(); - if (len >= MAX_STRING_SIZE) - len = MAX_STRING_SIZE - 1; - memcpy(p, value.c_str(), len); - p += MAX_STRING_SIZE; - } - - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback( - channel->getNativeType(), arraySize, - channel->getChannelID(), ca_stringBuffer, - ca_put_handler, usrArg); - } else { - result = ca_array_put( - channel->getNativeType(), arraySize, - channel->getChannelID(), ca_stringBuffer); - } - delete[] ca_stringBuffer; - - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } -} - -// enum specialization -template<> -int doPut_pvStructure(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & pvStructure) -{ - bool isScalarValue = pvStructure->getStructure()->getField("value")->getType() == structure; - - if (isScalarValue) - { - std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubFieldT("value.index")); - - dbr_enum_t val = value->get(); - int result = 0; - if(usrArg!=NULL) { - result = ca_array_put_callback( - channel->getNativeType(), 1, - channel->getChannelID(), &val, - ca_put_handler, usrArg); - } else { - result = ca_array_put( - channel->getNativeType(), 1, - channel->getChannelID(), &val); - } - if (result == ECA_NORMAL) - { - ca_flush_io(); - } - - return result; - } - else - { - // no enum arrays in V3 - return ECA_NOSUPPORT; - } -} - -static doPut doPutFuncTable[] = -{ - doPut_pvStructure, // DBR_STRING - doPut_pvStructure, // DBR_INT, DBR_SHORT - doPut_pvStructure, // DBR_FLOAT - doPut_pvStructure, // DBR_ENUM - doPut_pvStructure, // DBR_CHAR -#if defined(__vxworks) || defined(__rtems__) - doPut_pvStructure, // DBR_LONG -#else - doPut_pvStructure, // DBR_LONG -#endif - doPut_pvStructure, // DBR_DOUBLE -}; - } // namespace -void CAChannelPut::putDone(struct event_handler_args &args) -{ - if(DEBUG_LEVEL>1) { - cout << "CAChannelPut::putDone " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - if (args.status == ECA_NORMAL) - { - EXCEPTION_GUARD(putRequester->putDone(Status::Ok, shared_from_this())); - } - else - { - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - EXCEPTION_GUARD(putRequester->putDone(errorStatus, shared_from_this())); - } -} void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure, BitSet::shared_pointer const & /*putBitSet*/) @@ -1613,100 +654,49 @@ void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure, } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - doPut putFunc = doPutFuncTable[channel->getNativeType()]; - if (putFunc) - { - // TODO now we always put all - if(block) { - channel->attachContext(); - int result = putFunc(channel, this, pvPutStructure); - if (result != ECA_NORMAL) - { - string message(ca_message(result)); - Status errorStatus(Status::STATUSTYPE_ERROR, message); - EXCEPTION_GUARD(putRequester->putDone(errorStatus, shared_from_this())); - } - } else { - channel->attachContext(); - int result = putFunc(channel,NULL, pvPutStructure); - if (result == ECA_NORMAL) - { - EXCEPTION_GUARD(putRequester->putDone(Status::Ok, shared_from_this())); - } - else - { - string message(ca_message(result)); - Status errorStatus(Status::STATUSTYPE_ERROR,message); - EXCEPTION_GUARD(putRequester->putDone(errorStatus, shared_from_this())); - } - } - } - else - { - // TODO remove - std::cout << "no put func implemented" << std::endl; - } - + Status status = dbdToPv->putToDBD(channel,pvPutStructure,block); + EXCEPTION_GUARD(putRequester->putDone(status, shared_from_this())); } void CAChannelPut::getDone(struct event_handler_args &args) { - if(DEBUG_LEVEL>1) { + if(DEBUG_LEVEL>1) { cout << "CAChannelPut::getDone " << channel->getChannelName() << endl; } + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - if (args.status == ECA_NORMAL) - { - copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; - if (copyFunc) - copyFunc(args.dbr, args.count, pvStructure); - else - { - // TODO remove - std::cout << "no copy func implemented" << std::endl; - } - - EXCEPTION_GUARD(putRequester->getDone(Status::Ok, shared_from_this(), pvStructure, bitSet)); - } - else - { - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - EXCEPTION_GUARD(putRequester->getDone(errorStatus, shared_from_this(), - PVStructure::shared_pointer(), BitSet::shared_pointer())); - } - + Status status = dbdToPv->getFromDBD(pvStructure,bitSet,args); + EXCEPTION_GUARD(putRequester->getDone(status, shared_from_this(), pvStructure, bitSet)); } void CAChannelPut::get() { if(DEBUG_LEVEL>1) { - cout << "CAChannelPut::get " << channel->getChannelName() << endl; + std::cout << "CAChannelPut::get " << channel->getChannelName() << endl; } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; channel->attachContext(); - - int result = ca_array_get_callback(getType, channel->getElementCount(), + bitSet->clear(); + int result = ca_array_get_callback(dbdToPv->getRequestType(), + 0, channel->getChannelID(), ca_put_get_handler, this); - if (result == ECA_NORMAL) { result = ca_flush_io(); } - if (result == ECA_NORMAL) return; - string message(ca_message(result)); - Status errorStatus(Status::STATUSTYPE_ERROR, message); - EXCEPTION_GUARD(putRequester->getDone(errorStatus, shared_from_this(), - PVStructure::shared_pointer(), BitSet::shared_pointer())); + if (result != ECA_NORMAL) + { + string mess("CAChannelPut::get "); + mess += channel->getChannelName() + " message " +ca_message(result); + throw std::runtime_error(mess); + } } - -/* --------------- epics::pvData::ChannelRequest --------------- */ - Channel::shared_pointer CAChannelPut::getChannel() { return channel; @@ -1790,20 +780,37 @@ public: { Lock guard(mutex); if(monitorElementQueue.empty()) { - throw std::runtime_error("client error calling release"); + string mess("CAChannelMonitor::release client error calling release "); + throw std::runtime_error(mess); } monitorElementQueue.pop(); } }; +size_t CAChannelMonitor::num_instances; + CAChannelMonitorPtr CAChannelMonitor::create( CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { + if(DEBUG_LEVEL>0) { + cout << "CAChannelMonitor::create " << channel->getChannelName() << endl; + } return CAChannelMonitorPtr(new CAChannelMonitor(channel, monitorRequester, pvRequest)); } +CAChannelMonitor::CAChannelMonitor( + CAChannel::shared_pointer const & channel, + MonitorRequester::shared_pointer const & monitorRequester, + PVStructurePtr const & pvRequest) +: + channel(channel), + monitorRequester(monitorRequester), + pvRequest(pvRequest), + isStarted(false) +{} + CAChannelMonitor::~CAChannelMonitor() { if(DEBUG_LEVEL>0) { @@ -1818,39 +825,19 @@ CAChannelMonitor::~CAChannelMonitor() cerr << mess << endl; } -size_t CAChannelMonitor::num_instances; - -CAChannelMonitor::CAChannelMonitor( - CAChannel::shared_pointer const & channel, - MonitorRequester::shared_pointer const & monitorRequester, - PVStructurePtr const & pvRequest) -: - channel(channel), - monitorRequester(monitorRequester), - pvRequest(pvRequest), - isStarted(false), - firstTime(true) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelMonitor::CAChannelMonitor() " << channel->getChannelName() << endl; - } -} +void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_pointer const & c) +{} void CAChannelMonitor::activate() { + MonitorRequester::shared_pointer requester(monitorRequester.lock()); + if(!requester) return; if(DEBUG_LEVEL>0) { std::cout << "CAChannelMonitor::activate " << channel->getChannelName() << endl; } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - if(pvStructure) throw std::runtime_error("CAChannelMonitor::activate() was called twice"); - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); + dbdToPv = DbdToPv::create(channel,pvRequest,monitorIO); + pvStructure = dbdToPv->createPVStructure(); activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); - pvCopy = PVCopy::create( - createPVStructure(channel, getType, pvRequest), - CreateRequest::create()->createRequest("field()"), - ""); int32 queueSize = 2; PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); if (pvOptions) { @@ -1865,59 +852,18 @@ void CAChannelMonitor::activate() } monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); channel->addChannelMonitor(shared_from_this()); - if(channel->getConnectionState()==Channel::CONNECTED) { - EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), - pvStructure->getStructure())); - } -} - -void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_pointer const & c) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::channelCreated " << channel->getChannelName() << endl; - } - firstTime = true; - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - chtype newType = getDBRType(pvRequest, channel->getNativeType()); - if(newType!=getType) { - getType = getDBRType(pvRequest, channel->getNativeType()); - pvStructure = createPVStructure(channel, getType, pvRequest); - activeElement = MonitorElementPtr(new MonitorElement(pvStructure)); - pvCopy = PVCopy::create( - createPVStructure(channel, getType, pvRequest), - CreateRequest::create()->createRequest("field()"), - ""); - int32 queueSize = 2; - PVStructurePtr pvOptions = pvRequest->getSubField("record._options"); - if (pvOptions) { - PVStringPtr pvString = pvOptions->getSubField("queueSize"); - if (pvString) { - int size; - std::stringstream ss; - ss << pvString->get(); - ss >> size; - if (size > 1) queueSize = size; - } - } - monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); - } EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } + void CAChannelMonitor::channelStateChange( Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) { - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::channelStateChange " << channel->getChannelName() << endl; - } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) { - EXCEPTION_GUARD(requester->channelDisconnect(connectionState==Channel::DESTROYED);) - } + string mess("CAChannelMonitor::channelStateChange was called "); + mess += channel->getChannelName(); + throw std::runtime_error(mess); } @@ -1929,47 +875,35 @@ void CAChannelMonitor::channelDisconnect(bool destroy) MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; EXCEPTION_GUARD(requester->channelDisconnect(destroy);) + if(!destroy) channel->addChannelMonitor(shared_from_this()); } void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { std::cout << "CAChannelMonitor::subscriptionEvent " - << channel->getChannelName() - << " firstTime " << (firstTime ? "true" : "false") - << endl; + << channel->getChannelName() << endl; } MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; - if (args.status == ECA_NORMAL) + Status status = dbdToPv->getFromDBD(pvStructure,activeElement->changedBitSet,args); + + if(status.isOK()) { - copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; - if (copyFunc) { - copyFunc(args.dbr, args.count, pvStructure); - pvCopy->updateMasterSetBitSet(pvStructure,activeElement->changedBitSet); - if(firstTime) { - activeElement->changedBitSet->clear(); - activeElement->overrunBitSet->clear(); - activeElement->changedBitSet->set(0); - firstTime = false; - } - if(monitorQueue->event(pvStructure,activeElement)) { - activeElement->changedBitSet->clear(); - activeElement->overrunBitSet->clear(); - } else { - *(activeElement->overrunBitSet) |= *(activeElement->changedBitSet); - } - - // call monitorRequester even if queue is full - requester->monitorEvent(shared_from_this()); + if(monitorQueue->event(pvStructure,activeElement)) { + activeElement->changedBitSet->clear(); + activeElement->overrunBitSet->clear(); } else { - std::cout << "no copy func implemented" << std::endl; - + *(activeElement->overrunBitSet) |= *(activeElement->changedBitSet); } + + // call monitorRequester even if queue is full + requester->monitorEvent(shared_from_this()); } else { string mess("CAChannelMonitor::subscriptionEvent "); + mess += channel->getChannelName(); mess += ca_message(args.status); throw std::runtime_error(mess); } @@ -1986,21 +920,8 @@ epics::pvData::Status CAChannelMonitor::start() return status; } channel->attachContext(); - - /* - From R3.14.12 onwards when using the IOC server and the C++ client libraries monitor callbacks - replies will give a CA client application the current number of elements in an array field, - provided they specified an element count of zero in their original request. - The element count is passed in the callback argument structure. - Prior to R3.14.12 you could request a zero-length subscription and the zero would mean - “use the value of chid->element_count() for this particular channel”, - but the length of the data you got in your callbacks would never change - (the server would zero-fill everything after the current length of the field). - */ - - // TODO DBE_PROPERTY support monitorQueue->start(); - int result = ca_create_subscription(getType, + int result = ca_create_subscription(dbdToPv->getRequestType(), 0, channel->getChannelID(), DBE_VALUE, ca_subscription_handler, this, diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index ad3986e..6d49186 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -11,13 +11,12 @@ #include #include -#include - /* for CA */ #include #include "caProviderPvt.h" +#include "dbdToPv.h" namespace epics { namespace pvAccess { @@ -71,11 +70,6 @@ public: void disconnected(); chid getChannelID(); - chtype getNativeType(); - unsigned getElementCount(); - epics::pvData::Structure::const_shared_pointer getStructure(); - - /* --------------- epics::pvAccess::Channel --------------- */ virtual std::tr1::shared_ptr getProvider(); virtual std::string getRemoteAddress(); @@ -89,15 +83,15 @@ public: virtual ChannelGet::shared_pointer createChannelGet( ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); virtual ChannelPut::shared_pointer createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); virtual Monitor::shared_pointer createMonitor( MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); virtual void printInfo(std::ostream& out); @@ -128,10 +122,7 @@ private: ChannelRequester::weak_pointer channelRequester; chid channelID; - chtype channelType; - unsigned elementCount; bool channelCreated; - epics::pvData::Structure::const_shared_pointer structure; epics::pvData::Mutex requestsMutex; @@ -159,7 +150,7 @@ public: static CAChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelGet(); @@ -179,7 +170,7 @@ public: Channel::shared_pointer const & channel); virtual void channelStateChange( Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); + Channel::ConnectionState cosnectionState); virtual std::string getRequesterName() { return "CAChannelGet";} /* --------------- ChannelBaseRequester --------------- */ virtual void channelDisconnect(bool destroy); @@ -192,15 +183,13 @@ private: CAChannelGet(CAChannel::shared_pointer const & _channel, ChannelGetRequester::shared_pointer const & _channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); CAChannelPtr channel; ChannelGetRequester::weak_pointer channelGetRequester; - const epics::pvData::PVStructure::shared_pointer pvRequest; - bool firstTime; + epics::pvData::PVStructurePtr const & pvRequest; - chtype getType; - epics::pvData::PVCopyPtr pvCopy; + DbdToPvPtr dbdToPv; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; @@ -221,11 +210,10 @@ public: static CAChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelPut(); - void putDone(struct event_handler_args &args); void getDone(struct event_handler_args &args); /* --------------- epics::pvAccess::ChannelPut --------------- */ @@ -253,7 +241,7 @@ public: /* --------------- ChannelBaseRequester --------------- */ virtual void channelDisconnect(bool destroy); - void activate(); + void activate(); private: /* --------------- Destroyable --------------- */ @@ -262,14 +250,14 @@ private: CAChannelPut(CAChannel::shared_pointer const & _channel, ChannelPutRequester::shared_pointer const & _channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); CAChannelPtr channel; ChannelPutRequester::weak_pointer channelPutRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool block; - chtype getType; + DbdToPvPtr dbdToPv; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; @@ -291,7 +279,7 @@ public: static CAChannelMonitor::shared_pointer create(CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelMonitor(); @@ -323,17 +311,15 @@ private: CAChannelMonitor(CAChannel::shared_pointer const & _channel, MonitorRequester::shared_pointer const & _monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest); + epics::pvData::PVStructurePtr const & pvRequest); CAChannelPtr channel; MonitorRequester::weak_pointer monitorRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool isStarted; - chtype getType; - bool firstTime; - epics::pvData::PVCopyPtr pvCopy; + DbdToPvPtr dbdToPv; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::MonitorElementPtr activeElement; evid eventID; diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index 6ec6fb9..c434283 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -66,6 +66,7 @@ CAChannelProvider::~CAChannelProvider() } caChannelList.clear(); } + attachContext(); while(!channelQ.empty()) { if(DEBUG_LEVEL>0) { std::cout << "disconnectAllChannels calling disconnectChannel " @@ -75,7 +76,6 @@ CAChannelProvider::~CAChannelProvider() channelQ.front()->disconnectChannel(); channelQ.pop(); } - attachContext(); ca_flush_io(); ca_context_destroy(); } diff --git a/src/ca/caStatus.cpp b/src/ca/caStatus.cpp deleted file mode 100644 index 3e9d352..0000000 --- a/src/ca/caStatus.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/** - * 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. - */ - -#define epicsExportSharedSymbols -#include - -namespace epics { -namespace pvAccess { -namespace ca { - -epicsShareDef std::string dbrStatus2alarmMessage[] = { - "NO_ALARM", // 0 .. - "READ_ALARM", - "WRITE_ALARM", - "HIHI_ALARM", - "HIGH_ALARM", - "LOLO_ALARM", - "LOW_ALARM", - "STATE_ALARM", - "COS_ALARM", - "COMM_ALARM", - "TIMEOUT_ALARM", - "HW_LIMIT_ALARM", - "CALC_ALARM", - "SCAN_ALARM", - "LINK_ALARM", - "SOFT_ALARM", - "BAD_SUB_ALARM", - "UDF_ALARM", - "DISABLE_ALARM", - "SIMM_ALARM", - "READ_ACCESS_ALARM", - "WRITE_ACCESS_ALARM" // .. 21 -}; - -epicsShareDef int dbrStatus2alarmStatus[] = { - noStatus, //"NO_ALARM" - driverStatus, //"READ_ALARM", - driverStatus, //"WRITE_ALARM", - recordStatus, //"HIHI_ALARM", - recordStatus, //"HIGH_ALARM", - recordStatus, //"LOLO_ALARM", - recordStatus, //"LOW_ALARM", - recordStatus, //"STATE_ALARM", - recordStatus, //"COS_ALARM", - driverStatus, //"COMM_ALARM", - driverStatus, //"TIMEOUT_ALARM", - deviceStatus, //"HW_LIMIT_ALARM", - recordStatus, //"CALC_ALARM", - dbStatus, //"SCAN_ALARM", - dbStatus, //"LINK_ALARM", - dbStatus, //"SOFT_ALARM", - confStatus, //"BAD_SUB_ALARM", - recordStatus, //"UDF_ALARM", - recordStatus, //"DISABLE_ALARM", - recordStatus, //"SIMM_ALARM", - clientStatus, //"READ_ACCESS_ALARM", - clientStatus //"WRITE_ACCESS_ALARM" // .. 21 -}; - - -} -} -} - - diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp new file mode 100644 index 0000000..b8313cf --- /dev/null +++ b/src/ca/dbdToPv.cpp @@ -0,0 +1,926 @@ +/** + * 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. + */ + + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "caChannel.h" +#define epicsExportSharedSymbols +#include "dbdToPv.h" + +using namespace epics::pvData; +using std::string; +using std::ostringstream; +using std::cout; +using std::cerr; +using std::endl; + +namespace epics { +namespace pvAccess { +namespace ca { + +#define CA_TIMEOUT 2.0 +#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); +} + +static void putHandler(struct event_handler_args args) +{ + DbdToPv *dbdToPv = static_cast(args.usr); + dbdToPv->putDone(args); +} + +DbdToPvPtr DbdToPv::create( + CAChannelPtr const & caChannel, + PVStructurePtr const & pvRequest, + IOType ioType + ) +{ + DbdToPvPtr dbdToPv(new DbdToPv(ioType)); + dbdToPv->activate(caChannel,pvRequest); + return dbdToPv; +} + +DbdToPv::DbdToPv(IOType ioType) +: ioType(ioType), + fieldRequested(false), + alarmRequested(false), + timeStampRequested(false), + displayRequested(false), + controlRequested(false), + valueAlarmRequested(false), + isArray(false), + firstTime(true), + caValueType(-1), + caRequestType(-1) +{ +} + +static ScalarType dbr2ST[] = +{ + pvString, // DBR_STRING = 0 + pvShort, // DBR_SHORT. DBR_INT = 1 + pvFloat, // DBR_FLOAT = 2 + static_cast(-1), // DBR_ENUM = 3 + pvByte, // DBR_CHAR = 4 + pvInt, // DBR_LONG = 5 + pvDouble // DBR_DOUBLE = 6 +}; + +static chtype getDbrType(const ScalarType scalarType) +{ + switch(scalarType) + { + case pvString : return DBR_STRING; + case pvByte : return DBR_CHAR; + case pvShort : return DBR_SHORT; + case pvInt : return DBR_LONG; + case pvFloat : return DBR_FLOAT; + case pvDouble : return DBR_DOUBLE; + default: break; + } + throw std::runtime_error("getDbr: illegal scalarType"); +} + + +void DbdToPv::activate( + CAChannelPtr const & caChannel, + PVStructurePtr const & pvRequest) +{ + chid channelID = caChannel->getChannelID(); + chtype channelType = ca_field_type(channelID); + caValueType = (channelType==DBR_ENUM ? DBR_ENUM : getDbrType(dbr2ST[channelType])); + PVStructurePtr fieldPVStructure = pvRequest->getSubField("field"); + if(fieldPVStructure->getPVFields().size()==0) + { + fieldRequested = true; + alarmRequested = true; + timeStampRequested = true; + displayRequested = true; + controlRequested = true; + valueAlarmRequested = true; + } else { + if(fieldPVStructure->getSubField("value")) fieldRequested = true; + if(fieldPVStructure->getSubField("alarm")) alarmRequested = true; + if(fieldPVStructure->getSubField("timeStamp")) timeStampRequested = true; + if(fieldPVStructure->getSubField("display")) displayRequested = true; + if(fieldPVStructure->getSubField("control")) controlRequested = true; + if(fieldPVStructure->getSubField("valueAlarm")) valueAlarmRequested = true; + } + switch(ioType) + { + case getIO : break; + case putIO: + alarmRequested = false; + timeStampRequested = false; // no break + case monitorIO: + displayRequested = false; + controlRequested = false; + valueAlarmRequested = false; + } + StandardFieldPtr standardField = getStandardField(); + if(channelType==DBR_ENUM) + { + displayRequested = false; + controlRequested = false; + valueAlarmRequested = false; + caRequestType = DBR_ENUM; + string properties; + if(alarmRequested && timeStampRequested) { + properties += "alarm,timeStamp"; + caRequestType += DBR_TIME_STRING + caValueType; + } else if(timeStampRequested) { + properties += "timeStamp"; + caRequestType += DBR_TIME_STRING + caValueType; + } else if(alarmRequested) { + properties += "alarm"; + caRequestType += DBR_STS_STRING + caValueType; + } + 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; + } + if(ca_element_count(channelID)!=1) isArray = true; + if(isArray) + { + controlRequested = false; + valueAlarmRequested = false; + } + 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(fieldRequested) { + if(isArray) { + fieldBuilder->addArray("value",st); + } else { + fieldBuilder->add("value",st); + } + } + if(alarmRequested) fieldBuilder->add("alarm",standardField->alarm()); + if(timeStampRequested) fieldBuilder->add("timeStamp",standardField->timeStamp()); + if(displayRequested) fieldBuilder->add("display",standardField->display()); + if(controlRequested) fieldBuilder->add("control",standardField->control()); + if(valueAlarmRequested) { + switch(st) + { + case pvByte: + fieldBuilder->add("valueAlarm",standardField->byteAlarm()); break; + case pvShort: + fieldBuilder->add("valueAlarm",standardField->shortAlarm()); break; + case pvInt: + fieldBuilder->add("valueAlarm",standardField->intAlarm()); break; + case pvFloat: + fieldBuilder->add("valueAlarm",standardField->floatAlarm()); break; + case pvDouble: + fieldBuilder->add("valueAlarm",standardField->doubleAlarm()); break; + default: + throw std::runtime_error("DbDToPv::activate: bad type"); + } + } + structure = fieldBuilder->createStructure(); + caRequestType = caValueType; + if(displayRequested || controlRequested || valueAlarmRequested) + { + caRequestType = DBR_CTRL_STRING + caValueType; + } else if(timeStampRequested) { + caRequestType = DBR_TIME_STRING + caValueType; + } else if(alarmRequested) { + caRequestType = DBR_STS_STRING + 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); + } + } +} + +void DbdToPv::descriptionConnected(struct connection_handler_args args) +{ +cout << "DbdToPv::descriptionConnected\n"; + if (args.op != CA_OP_CONN_UP) return; + ca_array_get_callback(DBR_STRING, + 0, + args.chid, descriptionHandler, this); +} + +void DbdToPv::getDescriptionDone(struct event_handler_args &args) +{ +cout << "DbdToPv::getDescriptionDone\n"; + 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); +} + +void DbdToPv::getChoicesDone(struct event_handler_args &args) +{ + if(args.status!=ECA_NORMAL) + { + string message("DbdToPv::getChoicesDone ca_message "); + message += ca_message(args.status); + throw std::runtime_error(message); + } + const dbr_gr_enum* dbr_enum_p = static_cast(args.dbr); + size_t num = dbr_enum_p->no_str; + choices.reserve(num); + for(size_t i=0; istrs[i][0])); +} + +chtype DbdToPv::getRequestType() +{ + if(caRequestType<0) { + throw std::runtime_error("DbDToPv::getRequestType: bad type"); + } + return caRequestType; +} + +PVStructurePtr DbdToPv::createPVStructure() +{ + return getPVDataCreate()->createPVStructure(structure); +} + +Status DbdToPv::getFromDBD( + PVStructurePtr const & pvStructure, + BitSet::shared_pointer const & bitSet, + struct event_handler_args &args) +{ + if(args.status!=ECA_NORMAL) + { + Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); + return errorStatus; + } + if(fieldRequested) + { + void * value = dbr_value_ptr(args.dbr,caRequestType); + long count = args.count; + switch(caValueType) { + case DBR_ENUM: + { + const dbr_enum_t *dbrval = static_cast(value); + PVIntPtr value = pvStructure->getSubField("value.index"); + value->put(*dbrval); + PVStringArrayPtr pvChoices + = pvStructure->getSubField("value.choices"); + if(pvChoices->getLength()==0) + { + ConvertPtr convert = getConvert(); + size_t n = choices.size(); + pvChoices->setLength(n); + convert->fromStringArray(pvChoices,0,n,choices,0); + bitSet->set(pvStructure->getSubField("value")->getFieldOffset()); + } else { + bitSet->set(value->getFieldOffset()); + } + break; + } + case DBR_STRING: + { + const dbr_string_t *dbrval = static_cast(value); + if(isArray) { + PVStringArrayPtr pvValue = pvStructure->getSubField("value"); + PVStringArray::svector arr(pvValue->reuse()); + arr.resize(count); + std::copy(dbrval, dbrval + count, arr.begin()); + pvValue->replace(freeze(arr)); + } else { + PVStringPtr pvValue = pvStructure->getSubField("value"); + pvValue->put(*dbrval); + } + break; + } + case DBR_CHAR: + { + const dbr_char_t *dbrval = static_cast(value); + if(isArray) { + PVByteArrayPtr pvValue = pvStructure->getSubField("value"); + PVByteArray::svector arr(pvValue->reuse()); + arr.resize(count); + for(long i=0; ireplace(freeze(arr)); + } else { + PVBytePtr pvValue = pvStructure->getSubField("value"); + pvValue->put(*dbrval); + } + break; + } + case DBR_SHORT: + { + const dbr_short_t *dbrval = static_cast(value); + if(isArray) { + PVShortArrayPtr pvValue = pvStructure->getSubField("value"); + PVShortArray::svector arr(pvValue->reuse()); + arr.resize(count); + for(long i=0; ireplace(freeze(arr)); + } else { + PVShortPtr pvValue = pvStructure->getSubField("value"); + pvValue->put(*dbrval); + } + break; + } + case DBR_LONG: + { + const dbr_int_t *dbrval = static_cast(value); + if(isArray) { + PVIntArrayPtr pvValue = pvStructure->getSubField("value"); + PVIntArray::svector arr(pvValue->reuse()); + arr.resize(count); + for(long i=0; ireplace(freeze(arr)); + } else { + PVIntPtr pvValue = pvStructure->getSubField("value"); + pvValue->put(*dbrval); + } + break; + } + case DBR_FLOAT: + { + const dbr_float_t *dbrval = static_cast(value); + if(isArray) { + PVFloatArrayPtr pvValue = pvStructure->getSubField("value"); + PVFloatArray::svector arr(pvValue->reuse()); + arr.resize(count); + for(long i=0; ireplace(freeze(arr)); + } else { + PVFloatPtr pvValue = pvStructure->getSubField("value"); + pvValue->put(*dbrval); + } + break; + } + case DBR_DOUBLE: + { + const dbr_double_t *dbrval = static_cast(value); + if(isArray) { + PVDoubleArrayPtr pvValue + = pvStructure->getSubField("value"); + PVDoubleArray::svector arr(pvValue->reuse()); + arr.resize(count); + for(long i=0; ireplace(freeze(arr)); + } else { + PVDoublePtr pvValue = pvStructure->getSubField("value"); + pvValue->put(*dbrval); + } + break; + } + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::FromDBD logic error")); + return errorStatus; + } + if(caValueType!=DBR_ENUM) { + bitSet->set(pvStructure->getSubField("value")->getFieldOffset()); + } + } + chtype type = args.type; + dbr_short_t status{0}; + dbr_short_t severity(0); + epicsTimeStamp stamp{0,0}; + if(caRequestType>=DBR_CTRL_STRING) { + string units; + string format; + double upper_disp_limit; + double lower_disp_limit; + double upper_alarm_limit; + double upper_warning_limit; + double lower_warning_limit; + double lower_alarm_limit; + double upper_ctrl_limit; + double lower_ctrl_limit; + switch(type) { + case DBR_CTRL_CHAR: + { + const dbr_ctrl_char *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + units = data->units; + upper_disp_limit = data->upper_disp_limit; + lower_disp_limit = data->lower_disp_limit; + upper_alarm_limit = data->upper_alarm_limit; + upper_warning_limit = data->upper_warning_limit; + lower_warning_limit = data->lower_warning_limit; + lower_alarm_limit = data->lower_alarm_limit; + upper_ctrl_limit = data->upper_ctrl_limit; + lower_ctrl_limit = data->lower_ctrl_limit; + format = "I4"; + break; + } + case DBR_CTRL_SHORT: + { + const dbr_ctrl_short *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + units = data->units; + upper_disp_limit = data->upper_disp_limit; + lower_disp_limit = data->lower_disp_limit; + upper_alarm_limit = data->upper_alarm_limit; + upper_warning_limit = data->upper_warning_limit; + lower_warning_limit = data->lower_warning_limit; + lower_alarm_limit = data->lower_alarm_limit; + upper_ctrl_limit = data->upper_ctrl_limit; + lower_ctrl_limit = data->lower_ctrl_limit; + format = "I6"; + break; + } + case DBR_CTRL_LONG: + { + const dbr_ctrl_long *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + units = data->units; + upper_disp_limit = data->upper_disp_limit; + lower_disp_limit = data->lower_disp_limit; + upper_alarm_limit = data->upper_alarm_limit; + upper_warning_limit = data->upper_warning_limit; + lower_warning_limit = data->lower_warning_limit; + lower_alarm_limit = data->lower_alarm_limit; + upper_ctrl_limit = data->upper_ctrl_limit; + lower_ctrl_limit = data->lower_ctrl_limit; + format = "I12"; + break; + } + case DBR_CTRL_FLOAT: + { + const dbr_ctrl_float *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + units = data->units; + upper_disp_limit = data->upper_disp_limit; + lower_disp_limit = data->lower_disp_limit; + upper_alarm_limit = data->upper_alarm_limit; + upper_warning_limit = data->upper_warning_limit; + lower_warning_limit = data->lower_warning_limit; + lower_alarm_limit = data->lower_alarm_limit; + upper_ctrl_limit = data->upper_ctrl_limit; + lower_ctrl_limit = data->lower_ctrl_limit; + int prec = data->precision; + ostringstream s; + s << "F" << prec + 6 << "." << prec; + format = s.str(); + break; + } + case DBR_CTRL_DOUBLE: + { + const dbr_ctrl_double *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + units = data->units; + upper_disp_limit = data->upper_disp_limit; + lower_disp_limit = data->lower_disp_limit; + upper_alarm_limit = data->upper_alarm_limit; + upper_warning_limit = data->upper_warning_limit; + lower_warning_limit = data->lower_warning_limit; + lower_alarm_limit = data->lower_alarm_limit; + upper_ctrl_limit = data->upper_ctrl_limit; + lower_ctrl_limit = data->lower_ctrl_limit; + int prec = data->precision; + ostringstream s; + s << "F" << prec + 6 << "." << prec; + format = s.str(); + break; + } + default : + throw std::runtime_error("DbdToPv::getDone logic error"); + } + if(displayRequested) + { + PVStructurePtr pvDisplay(pvStructure->getSubField("display")); + if(caDisplay.lower_disp_limit!=lower_disp_limit) { + caDisplay.lower_disp_limit = lower_disp_limit; + PVDoublePtr pvDouble = pvDisplay->getSubField("limitLow"); + pvDouble->put(lower_disp_limit); + bitSet->set(pvDouble->getFieldOffset()); + } + if(caDisplay.upper_disp_limit!=upper_disp_limit) { + caDisplay.upper_disp_limit = upper_disp_limit; + PVDoublePtr pvDouble = pvDisplay->getSubField("limitHigh"); + pvDouble->put(upper_disp_limit); + bitSet->set(pvDouble->getFieldOffset()); + } + if(caDisplay.units!=units) { + caDisplay.units = units; + PVStringPtr pvString = pvDisplay->getSubField("units"); + pvString->put(units); + bitSet->set(pvString->getFieldOffset()); + } + if(caDisplay.format!=format) { + caDisplay.format = format; + PVStringPtr pvString = pvDisplay->getSubField("format"); + 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(controlRequested) + { + PVStructurePtr pvControl(pvStructure->getSubField("control")); + if(caControl.upper_ctrl_limit!=upper_ctrl_limit) { + caControl.upper_ctrl_limit = upper_ctrl_limit; + PVDoublePtr pv = pvControl->getSubField("limitHigh"); + pv->put(upper_ctrl_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caControl.lower_ctrl_limit!=lower_ctrl_limit) { + caControl.lower_ctrl_limit = lower_ctrl_limit; + PVDoublePtr pv = pvControl->getSubField("limitLow"); + pv->put(lower_ctrl_limit); + bitSet->set(pv->getFieldOffset()); + } + } + if(valueAlarmRequested) { + ConvertPtr convert(getConvert()); + PVStructurePtr pvValueAlarm(pvStructure->getSubField("valueAlarm")); + if(caValueAlarm.upper_alarm_limit!=upper_alarm_limit) { + caValueAlarm.upper_alarm_limit = upper_alarm_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("highAlarmLimit"); + convert->fromDouble(pv,upper_alarm_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.upper_warning_limit!=upper_warning_limit) { + caValueAlarm.upper_warning_limit = upper_warning_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("highWarningLimit"); + convert->fromDouble(pv,upper_warning_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.lower_warning_limit!=lower_warning_limit) { + caValueAlarm.lower_warning_limit = lower_warning_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("lowWarningLimit"); + convert->fromDouble(pv,lower_warning_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.lower_alarm_limit!=lower_alarm_limit) { + caValueAlarm.lower_alarm_limit = lower_alarm_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("lowAlarmLimit"); + convert->fromDouble(pv,lower_alarm_limit); + bitSet->set(pv->getFieldOffset()); + } + } + } else if(caRequestType>=DBR_TIME_STRING) { + switch(type) { + case DBR_TIME_STRING: + { + const dbr_time_string *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + stamp = data->stamp; + break; + } + case DBR_TIME_CHAR: + { + const dbr_time_char *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + stamp = data->stamp; + break; + } + case DBR_TIME_SHORT: + { + const dbr_time_short *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + stamp = data->stamp; + break; + } + case DBR_TIME_LONG: + { + const dbr_time_long *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + stamp = data->stamp; + break; + } + case DBR_TIME_FLOAT: + { + const dbr_time_float *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + stamp = data->stamp; + break; + } + case DBR_TIME_DOUBLE: + { + const dbr_time_double *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + stamp = data->stamp; + break; + } + default: + throw std::runtime_error("DbdToPv::getDone logic error"); + } + } else if(caRequestType>=DBR_STS_STRING) { + switch(type) { + case DBR_STS_STRING: + { + const dbr_sts_string *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + break; + } + case DBR_STS_CHAR: + { + const dbr_sts_char *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + break; + } + case DBR_STS_SHORT: + { + const dbr_sts_short *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + break; + } + case DBR_STS_LONG: + { + const dbr_sts_long *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + break; + } + case DBR_STS_FLOAT: + { + const dbr_sts_float *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + break; + } + case DBR_STS_DOUBLE: + { + const dbr_sts_double *data = static_cast(args.dbr); + status = data->status; + severity = data->severity; + break; + } + default: + throw std::runtime_error("DbdToPv::getDone logic error"); + } + } + if(alarmRequested) { + bool statusChanged = false; + bool severityChanged = false; + PVStructurePtr pvAlarm(pvStructure->getSubField("alarm")); + PVIntPtr pvSeverity(pvAlarm->getSubField("severity")); + if(caAlarm.severity!=severity) { + caAlarm.severity = severity; + pvSeverity->put(severity); + severityChanged = true; + } + PVStringPtr pvMessage(pvAlarm->getSubField("message")); + PVIntPtr pvStatus(pvAlarm->getSubField("status")); + if(caAlarm.status!=status) { + caAlarm.status = status; + pvStatus->put(status); + string message("UNKNOWN STATUS"); + if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]); + pvMessage->put(message); + statusChanged = true; + } + if(statusChanged&&severityChanged) { + bitSet->set(pvAlarm->getFieldOffset()); + } else if(severityChanged) { + bitSet->set(pvSeverity->getFieldOffset()); + } else if(statusChanged) { + bitSet->set(pvStatus->getFieldOffset()); + bitSet->set(pvMessage->getFieldOffset()); + } + } + if(timeStampRequested) { + PVStructurePtr pvTimeStamp(pvStructure->getSubField("timeStamp")); + if(caTimeStamp.secPastEpoch!=stamp.secPastEpoch) { + caTimeStamp.secPastEpoch = stamp.secPastEpoch; + PVLongPtr pvSeconds(pvTimeStamp->getSubField("secondsPastEpoch")); + pvSeconds->put(stamp.secPastEpoch+posixEpochAtEpicsEpoch); + bitSet->set(pvSeconds->getFieldOffset()); + } + if(caTimeStamp.nsec!=stamp.nsec) { + caTimeStamp.secPastEpoch = stamp.secPastEpoch; + PVIntPtr pvNano(pvTimeStamp->getSubField("nanoseconds")); + pvNano->put(stamp.nsec); + bitSet->set(pvNano->getFieldOffset()); + } + + } + if(firstTime) { + firstTime = false; + bitSet->clear(); + bitSet->set(0); + } + return Status::Ok; +} + +Status DbdToPv::putToDBD( + CAChannelPtr const & caChannel, + PVStructurePtr const & pvStructure, + bool block) +{ + chid channelID = caChannel->getChannelID(); + const void *pValue = NULL; + unsigned long count = 1; + dbr_enum_t indexvalue(0); + dbr_char_t bvalue(0); + dbr_short_t svalue(0); + dbr_long_t ivalue(0); + dbr_float_t fvalue(0); + dbr_double_t dvalue(0); + char *ca_stringBuffer(0); + + + switch(caValueType) { + case DBR_ENUM: + { + indexvalue = pvStructure->getSubField("value.index")->get(); + pValue = &indexvalue; + break; + } + case DBR_STRING: + { + if(isArray) { + PVStringArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + if(count<1) break; + int nbytes = count*MAX_STRING_SIZE; + ca_stringBuffer = new char[nbytes]; + memset(ca_stringBuffer, 0, nbytes); + pValue = ca_stringBuffer; + PVStringArray::const_svector stringArray(pvValue->view()); + char *pnext = ca_stringBuffer; + for(size_t i=0; i= MAX_STRING_SIZE) len = MAX_STRING_SIZE - 1; + memcpy(pnext, value.c_str(), len); + pnext += MAX_STRING_SIZE; + } + } else { + pValue = pvStructure->getSubField("value")->get().c_str(); + } + break; + } + case DBR_CHAR: + { + if(isArray) { + PVByteArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + pValue = pvValue->view().data(); + } else { + bvalue = pvStructure->getSubField("value")->get(); + pValue = &bvalue; + } + break; + } + case DBR_SHORT: + { + if(isArray) { + PVShortArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + pValue = pvValue->view().data(); + } else { + svalue = pvStructure->getSubField("value")->get(); + pValue = &svalue; + } + break; + } + case DBR_LONG: + { + if(isArray) { + PVIntArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + pValue = pvValue->view().data(); + } else { + ivalue = pvStructure->getSubField("value")->get(); + pValue = &ivalue; + } + break; + } + case DBR_FLOAT: + { + if(isArray) { + PVFloatArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + pValue = pvValue->view().data(); + } else { + fvalue = pvStructure->getSubField("value")->get(); + pValue = &fvalue; + } + break; + } + case DBR_DOUBLE: + { + if(isArray) { + PVDoubleArrayPtr pvValue = pvStructure->getSubField("value"); + count = pvValue->getLength(); + pValue = pvValue->view().data(); + } else { + dvalue = pvStructure->getSubField("value")->get(); + pValue = &dvalue; + } + break; + } + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::FromDBD logic error")); + return errorStatus; + } + int result = 0; + if(block) { + caChannel->attachContext(); + result = ca_array_put_callback(caValueType,count,channelID,pValue,putHandler,this); + if(result==ECA_NORMAL) { + ca_flush_io(); + if(!waitForCallback.wait(2.0)) { + throw std::runtime_error("DbDToPv::putToDBD waitForCallback timeout"); + } + return putStatus; + } + } else { + caChannel->attachContext(); + result = ca_array_put(caValueType,count,channelID,pValue); + ca_flush_io(); + } + if(ca_stringBuffer!=NULL) delete[] ca_stringBuffer; + if(result==ECA_NORMAL) return Status::Ok; + Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result))); + return errorStatus; +} + +void DbdToPv::putDone(struct event_handler_args &args) +{ + if(args.status!=ECA_NORMAL) + { + string message("DbdToPv::putDone ca_message "); + message += ca_message(args.status); + putStatus = Status(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); + } else { + putStatus = Status::Ok; + } + waitForCallback.signal(); +} + + +}}} diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h new file mode 100644 index 0000000..72d63e3 --- /dev/null +++ b/src/ca/dbdToPv.h @@ -0,0 +1,138 @@ +/** + * 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.03 + */ +#ifndef DbdToPv_H +#define DbdToPv_H + +#include +#include +#include +#include +#include "caChannel.h" + +namespace epics { +namespace pvAccess { +namespace ca { + +enum IOType {getIO,putIO,monitorIO}; + +class AlarmDbd; +typedef std::tr1::shared_ptr AlarmDbdPtr; +class TimeStampDbd; +typedef std::tr1::shared_ptr TimeStampDbdPtr; +class DisplayDbd; +typedef std::tr1::shared_ptr DisplayDbdPtr; +class ControlDbd; +typedef std::tr1::shared_ptr ControlDbdPtr; +class ValueAlarmDbd; +typedef std::tr1::shared_ptr ValueAlarmDbdPtr; + +struct CaAlarm +{ + dbr_short_t status; + dbr_short_t severity; +}; + +struct CaDisplay +{ + double lower_disp_limit; + double upper_disp_limit; + std::string units; + std::string format; +}; + +struct CaControl +{ + double upper_ctrl_limit; + double lower_ctrl_limit; +}; + +struct CaValueAlarm +{ + double upper_alarm_limit; + double upper_warning_limit; + double lower_warning_limit; + double lower_alarm_limit; +}; + +class DbdToPv; +typedef std::tr1::shared_ptr DbdToPvPtr; + +/** + * @brief DbdToPv converts between DBD data and pvData. + * + * + */ +class DbdToPv +{ +public: + POINTER_DEFINITIONS(DbdToPv); + /** @brief + * + */ + static DbdToPvPtr create( + CAChannelPtr const & caChannel, + epics::pvData::PVStructurePtr const & pvRequest, + IOType ioType + ); + /** @brief + * + */ + epics::pvData::PVStructurePtr createPVStructure(); + chtype getRequestType(); + epics::pvData::Status getFromDBD( + epics::pvData::PVStructurePtr const & pvStructure, + epics::pvData::BitSet::shared_pointer const & bitSet, + struct event_handler_args &args + ); + epics::pvData::Status putToDBD( + CAChannelPtr const & caChannel, + epics::pvData::PVStructurePtr const & pvStructure, + bool block + ); + void getChoicesDone(struct event_handler_args &args); + void descriptionConnected(struct connection_handler_args args); + void getDescriptionDone(struct event_handler_args &args); + void putDone(struct event_handler_args &args); + +private: + DbdToPv(IOType ioType); + + void activate( + CAChannelPtr const & caChannel, + epics::pvData::PVStructurePtr const & pvRequest + ); + IOType ioType; + bool fieldRequested; + bool alarmRequested; + bool timeStampRequested; + bool displayRequested; + bool controlRequested; + bool valueAlarmRequested; + bool isArray; + bool firstTime; + chtype caValueType; + chtype caRequestType; + epicsTimeStamp caTimeStamp; + CaAlarm caAlarm; + CaDisplay caDisplay; + CaControl caControl; + CaValueAlarm caValueAlarm; + std::string description; + epics::pvData::Event waitForCallback; + epics::pvData::Status putStatus; + epics::pvData::Structure::const_shared_pointer structure; + std::vector choices; +}; + +} +} +} + +#endif /* DbdToPv_H */ diff --git a/src/ca/pv/caStatus.h b/src/ca/pv/caStatus.h deleted file mode 100644 index 33ba6f3..0000000 --- a/src/ca/pv/caStatus.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * 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. - */ - -#ifndef CASTATUS_H -#define CASTATUS_H - -#include -#include - -namespace epics { -namespace pvAccess { -namespace ca { - -enum AlarmStatus { - noStatus,deviceStatus,driverStatus,recordStatus, - dbStatus,confStatus,undefinedStatus,clientStatus -}; - -epicsShareExtern std::string dbrStatus2alarmMessage[]; -epicsShareExtern int dbrStatus2alarmStatus[]; - -} -} -} - -#endif /* CASTATUS_H */ From 32c51c89ab49b36c35d024f6b9e1729843eb028f Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 2 Apr 2018 14:01:18 -0400 Subject: [PATCH 05/36] all null request; remove some debug statements --- src/ca/dbdToPv.cpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index b8313cf..e6117fe 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -22,9 +22,6 @@ using namespace epics::pvData; using std::string; using std::ostringstream; -using std::cout; -using std::cerr; -using std::endl; namespace epics { namespace pvAccess { @@ -117,7 +114,23 @@ void DbdToPv::activate( chid channelID = caChannel->getChannelID(); chtype channelType = ca_field_type(channelID); caValueType = (channelType==DBR_ENUM ? DBR_ENUM : getDbrType(dbr2ST[channelType])); - PVStructurePtr fieldPVStructure = pvRequest->getSubField("field"); + if(!pvRequest) { + string mess(caChannel->getChannelName()); + mess += " DbdToPv::activate pvRequest is null"; + throw std::runtime_error(mess); + } + PVStructurePtr fieldPVStructure; + if(pvRequest->getPVFields().size()==0) { + fieldPVStructure = pvRequest; + } else { + fieldPVStructure = pvRequest->getSubField("field"); + } + if(!fieldPVStructure) { + ostringstream mess; + mess << caChannel->getChannelName() + << " DbdToPv::activate illegal pvRequest " << pvRequest; + throw std::runtime_error(mess.str()); + } if(fieldPVStructure->getPVFields().size()==0) { fieldRequested = true; @@ -253,7 +266,6 @@ void DbdToPv::activate( void DbdToPv::descriptionConnected(struct connection_handler_args args) { -cout << "DbdToPv::descriptionConnected\n"; if (args.op != CA_OP_CONN_UP) return; ca_array_get_callback(DBR_STRING, 0, @@ -262,7 +274,6 @@ cout << "DbdToPv::descriptionConnected\n"; void DbdToPv::getDescriptionDone(struct event_handler_args &args) { -cout << "DbdToPv::getDescriptionDone\n"; if(args.status!=ECA_NORMAL) return; const dbr_string_t *value = static_cast(dbr_value_ptr(args.dbr,DBR_STRING)); description = string(*value); From 596bc2273864c080b41c80f386f3b193dffc9a5f Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Tue, 3 Apr 2018 05:44:05 -0400 Subject: [PATCH 06/36] do not use c++11 initialization --- src/ca/dbdToPv.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index e6117fe..67def5c 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -442,20 +442,20 @@ Status DbdToPv::getFromDBD( } } chtype type = args.type; - dbr_short_t status{0}; - dbr_short_t severity(0); - epicsTimeStamp stamp{0,0}; + dbr_short_t status = 0; + dbr_short_t severity = 0; + epicsTimeStamp stamp = {0,0}; if(caRequestType>=DBR_CTRL_STRING) { string units; string format; - double upper_disp_limit; - double lower_disp_limit; - double upper_alarm_limit; - double upper_warning_limit; - double lower_warning_limit; - double lower_alarm_limit; - double upper_ctrl_limit; - double lower_ctrl_limit; + double upper_disp_limit = 0.0; + double lower_disp_limit = 0.0; + double upper_alarm_limit = 0.0; + double upper_warning_limit = 0.0; + double lower_warning_limit = 0.0; + double lower_alarm_limit = 0.0; + double upper_ctrl_limit = 0.0; + double lower_ctrl_limit = 0.0; switch(type) { case DBR_CTRL_CHAR: { From 46c1cc3b383bc5da76440620d0a1546d4c40fda2 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Tue, 3 Apr 2018 10:23:21 -0400 Subject: [PATCH 07/36] add #include --- src/ca/dbdToPv.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index 67def5c..dc05ef8 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include From 1e5f44d26e68ee6eec4fec40d626e12f16e3d0da Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 13 Apr 2018 10:06:44 -0400 Subject: [PATCH 08/36] major changes to dbdToPv --- src/ca/caChannel.cpp | 13 +- src/ca/caChannel.h | 93 +---- src/ca/dbdToPv.cpp | 907 ++++++++++++++++++------------------------- src/ca/dbdToPv.h | 9 +- 4 files changed, 404 insertions(+), 618 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 21ed1e8..6d7d187 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -38,10 +38,10 @@ CAChannel::shared_pointer CAChannel::create(CAChannelProvider::shared_pointer co if(DEBUG_LEVEL>0) { cout<< "CAChannel::create " << channelName << endl; } - CAChannelPtr thisPtr( + CAChannelPtr caChannel( new CAChannel(channelName, channelProvider, channelRequester)); - thisPtr->activate(priority); - return thisPtr; + caChannel->activate(priority); + return caChannel; } static void ca_connection_handler(struct connection_handler_args args) @@ -487,6 +487,8 @@ void CAChannelGet::channelStateChange( throw std::runtime_error(mess); } +std::string CAChannelGet::getRequesterName() { return "CAChannelGet";} + void CAChannelGet::channelDisconnect(bool destroy) { if(DEBUG_LEVEL>0) { @@ -498,8 +500,6 @@ void CAChannelGet::channelDisconnect(bool destroy) if(!destroy) channel->addChannelGet(shared_from_this()); } -/* --------------- epics::pvAccess::ChannelGet --------------- */ - namespace { static void ca_get_handler(struct event_handler_args args) @@ -622,6 +622,8 @@ void CAChannelPut::channelStateChange( throw std::runtime_error(mess); } +std::string CAChannelPut::getRequesterName() { return "CAChannelPut";} + void CAChannelPut::channelDisconnect(bool destroy) { if(DEBUG_LEVEL>0) { @@ -866,6 +868,7 @@ void CAChannelMonitor::channelStateChange( throw std::runtime_error(mess); } +std::string CAChannelMonitor::getRequesterName() { return "CAChannelMonitor";} void CAChannelMonitor::channelDisconnect(bool destroy) { diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 6d49186..a79b65b 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -22,7 +22,6 @@ namespace epics { namespace pvAccess { namespace ca { - class CAChannelGetField; typedef std::tr1::shared_ptr CAChannelGetFieldPtr; typedef std::tr1::weak_ptr CAChannelGetFieldWPtr; @@ -53,22 +52,18 @@ class CAChannel : public Channel, public std::tr1::enable_shared_from_this { - public: POINTER_DEFINITIONS(CAChannel); - static size_t num_instances; - - static CAChannelPtr create(CAChannelProvider::shared_pointer const & channelProvider, - std::string const & channelName, - short priority, - ChannelRequester::shared_pointer const & channelRequester); - + static CAChannelPtr create( + CAChannelProvider::shared_pointer const & channelProvider, + std::string const & channelName, + short priority, + ChannelRequester::shared_pointer const & channelRequester); virtual ~CAChannel(); void connected(); void disconnected(); - chid getChannelID(); virtual std::tr1::shared_ptr getProvider(); @@ -76,54 +71,36 @@ public: virtual ConnectionState getConnectionState(); virtual std::string getChannelName(); virtual std::tr1::shared_ptr getChannelRequester(); - virtual void getField(GetFieldRequester::shared_pointer const & requester,std::string const & subField); - virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField); - virtual ChannelGet::shared_pointer createChannelGet( ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructurePtr const & pvRequest); - virtual ChannelPut::shared_pointer createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructurePtr const & pvRequest); - virtual Monitor::shared_pointer createMonitor( MonitorRequester::shared_pointer const & monitorRequester, epics::pvData::PVStructurePtr const & pvRequest); - virtual void printInfo(std::ostream& out); - /* ---------------------------------------------------------------- */ - void attachContext(); - void addChannelGet(const CAChannelGetPtr & get); void addChannelPut(const CAChannelPutPtr & get); void addChannelMonitor(const CAChannelMonitorPtr & get); void disconnectChannel(); - - private: - - /* --------------- Destroyable --------------- */ - virtual void destroy() {} - CAChannel(std::string const & channelName, CAChannelProvider::shared_pointer const & channelProvider, ChannelRequester::shared_pointer const & channelRequester); void activate(short priority); std::string channelName; - CAChannelProviderWPtr channelProvider; ChannelRequester::weak_pointer channelRequester; - chid channelID; bool channelCreated; - epics::pvData::Mutex requestsMutex; std::queue getFieldQueue; @@ -142,45 +119,32 @@ class CAChannelGet : public ChannelBaseRequester, public std::tr1::enable_shared_from_this { - public: POINTER_DEFINITIONS(CAChannelGet); - static size_t num_instances; - static CAChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructurePtr const & pvRequest); - virtual ~CAChannelGet(); - void getDone(struct event_handler_args &args); - - /* --------------- epics::pvAccess::ChannelGet --------------- */ virtual void get(); - - /* --------------- epics::pvData::ChannelRequest --------------- */ virtual Channel::shared_pointer getChannel(); virtual void cancel(); virtual void lastRequest(); - /* --------------- ChannelRequester --------------- */ virtual void channelCreated( const epics::pvData::Status& status, Channel::shared_pointer const & channel); virtual void channelStateChange( Channel::shared_pointer const & channel, Channel::ConnectionState cosnectionState); - virtual std::string getRequesterName() { return "CAChannelGet";} - /* --------------- ChannelBaseRequester --------------- */ + virtual std::string getRequesterName(); virtual void channelDisconnect(bool destroy); void activate(); private: - /* --------------- Destroyable --------------- */ - virtual void destroy() {} - + virtual void destroy() {} CAChannelGet(CAChannel::shared_pointer const & _channel, ChannelGetRequester::shared_pointer const & _channelGetRequester, epics::pvData::PVStructurePtr const & pvRequest); @@ -188,14 +152,11 @@ private: CAChannelPtr channel; ChannelGetRequester::weak_pointer channelGetRequester; epics::pvData::PVStructurePtr const & pvRequest; - DbdToPvPtr dbdToPv; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; - - class CAChannelPut : public ChannelPut, public ChannelRequester, @@ -205,58 +166,40 @@ class CAChannelPut : public: POINTER_DEFINITIONS(CAChannelPut); - static size_t num_instances; - static CAChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructurePtr const & pvRequest); - virtual ~CAChannelPut(); - void getDone(struct event_handler_args &args); - - /* --------------- epics::pvAccess::ChannelPut --------------- */ - virtual void put( epics::pvData::PVStructure::shared_pointer const & pvPutStructure, epics::pvData::BitSet::shared_pointer const & putBitSet ); virtual void get(); - - /* --------------- epics::pvData::ChannelRequest --------------- */ - virtual Channel::shared_pointer getChannel(); virtual void cancel(); virtual void lastRequest(); - /* --------------- ChannelRequester --------------- */ virtual void channelCreated( const epics::pvData::Status& status, Channel::shared_pointer const & channel); virtual void channelStateChange( Channel::shared_pointer const & channel, Channel::ConnectionState connectionState); - virtual std::string getRequesterName() { return "CAChannelPut";} - /* --------------- ChannelBaseRequester --------------- */ + virtual std::string getRequesterName(); virtual void channelDisconnect(bool destroy); void activate(); - private: - /* --------------- Destroyable --------------- */ - - virtual void destroy() {} - + virtual void destroy() {} CAChannelPut(CAChannel::shared_pointer const & _channel, ChannelPutRequester::shared_pointer const & _channelPutRequester, epics::pvData::PVStructurePtr const & pvRequest); - CAChannelPtr channel; ChannelPutRequester::weak_pointer channelPutRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool block; - DbdToPvPtr dbdToPv; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; @@ -274,46 +217,34 @@ class CAChannelMonitor : public: POINTER_DEFINITIONS(CAChannelMonitor); - static size_t num_instances; - static CAChannelMonitor::shared_pointer create(CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, epics::pvData::PVStructurePtr const & pvRequest); - virtual ~CAChannelMonitor(); - void subscriptionEvent(struct event_handler_args &args); - /* --------------- Monitor --------------- */ - virtual epics::pvData::Status start(); virtual epics::pvData::Status stop(); virtual MonitorElementPtr poll(); virtual void release(MonitorElementPtr const & monitorElement); - - /* --------------- epics::pvData::ChannelRequest --------------- */ virtual void cancel(); - /* --------------- ChannelRequester --------------- */ + virtual void channelCreated( const epics::pvData::Status& status, Channel::shared_pointer const & channel); virtual void channelStateChange( Channel::shared_pointer const & channel, Channel::ConnectionState connectionState); - virtual std::string getRequesterName() { return "CAChannelMonitor";} - /* --------------- ChannelBaseRequester --------------- */ + virtual std::string getRequesterName(); + virtual void channelDisconnect(bool destroy); void activate(); private: - /* --------------- Destroyable --------------- */ virtual void destroy() {} - CAChannelMonitor(CAChannel::shared_pointer const & _channel, MonitorRequester::shared_pointer const & _monitorRequester, epics::pvData::PVStructurePtr const & pvRequest); - - CAChannelPtr channel; MonitorRequester::weak_pointer monitorRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index dc05ef8..fd9c361 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -28,7 +28,6 @@ namespace epics { namespace pvAccess { namespace ca { -#define CA_TIMEOUT 2.0 #define CA_PRIORITY 50 static void enumChoicesHandler(struct event_handler_args args) @@ -58,8 +57,7 @@ static void putHandler(struct event_handler_args args) DbdToPvPtr DbdToPv::create( CAChannelPtr const & caChannel, PVStructurePtr const & pvRequest, - IOType ioType - ) + IOType ioType) { DbdToPvPtr dbdToPv(new DbdToPv(ioType)); dbdToPv->activate(caChannel,pvRequest); @@ -77,9 +75,9 @@ DbdToPv::DbdToPv(IOType ioType) isArray(false), firstTime(true), caValueType(-1), - caRequestType(-1) -{ -} + caRequestType(-1), + maxElements(0) +{} static ScalarType dbr2ST[] = { @@ -153,11 +151,12 @@ void DbdToPv::activate( case getIO : break; case putIO: alarmRequested = false; - timeStampRequested = false; // no break - case monitorIO: + timeStampRequested = false; displayRequested = false; controlRequested = false; valueAlarmRequested = false; + break; + case monitorIO: break; } StandardFieldPtr standardField = getStandardField(); if(channelType==DBR_ENUM) @@ -165,18 +164,15 @@ void DbdToPv::activate( displayRequested = false; controlRequested = false; valueAlarmRequested = false; - caRequestType = DBR_ENUM; string properties; if(alarmRequested && timeStampRequested) { properties += "alarm,timeStamp"; - caRequestType += DBR_TIME_STRING + caValueType; } else if(timeStampRequested) { properties += "timeStamp"; - caRequestType += DBR_TIME_STRING + caValueType; } else if(alarmRequested) { properties += "alarm"; - caRequestType += DBR_STS_STRING + caValueType; } + caRequestType = (properties.size()==0 ? DBR_ENUM : DBR_TIME_ENUM); structure = standardField->enumerated(properties); int result = ca_array_get_callback(DBR_GR_ENUM, 1, @@ -192,7 +188,8 @@ void DbdToPv::activate( // and will guarantee that enumChoicesHandler is called first return; } - if(ca_element_count(channelID)!=1) isArray = true; + maxElements = ca_element_count(channelID); + if(maxElements!=1) isArray = true; if(isArray) { controlRequested = false; @@ -205,7 +202,6 @@ void DbdToPv::activate( valueAlarmRequested = false; } if(controlRequested || displayRequested || valueAlarmRequested) timeStampRequested = false; - FieldCreatePtr fieldCreate(FieldCreate::getFieldCreate()); PVDataCreatePtr pvDataCreate(PVDataCreate::getPVDataCreate()); FieldBuilderPtr fieldBuilder(fieldCreate->createFieldBuilder()); @@ -241,11 +237,11 @@ void DbdToPv::activate( caRequestType = caValueType; if(displayRequested || controlRequested || valueAlarmRequested) { - caRequestType = DBR_CTRL_STRING + caValueType; - } else if(timeStampRequested) { - caRequestType = DBR_TIME_STRING + caValueType; - } else if(alarmRequested) { - caRequestType = DBR_STS_STRING + caValueType; + caRequestType = dbf_type_to_DBR_CTRL(caValueType); + } else if(timeStampRequested || alarmRequested) { + caRequestType = dbf_type_to_DBR_TIME(caValueType); + } else { + caRequestType = dbf_type_to_DBR(caValueType); } if(displayRequested) { chid channelID; @@ -308,6 +304,54 @@ PVStructurePtr DbdToPv::createPVStructure() return getPVDataCreate()->createPVStructure(structure); } +template +void copy_DBRScalar(const void * dbr, PVScalar::shared_pointer const & pvScalar) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvScalar); + value->put(static_cast(dbr)[0]); +} + +template +void copy_DBRScalarArray(const void * dbr, unsigned count, PVScalarArray::shared_pointer const & pvArray) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvArray); + typename pvT::svector temp(value->reuse()); + temp.resize(count); + std::copy( + static_cast(dbr), + static_cast(dbr) + count, + temp.begin()); + value->replace(freeze(temp)); +} + +template +void get_DBRControl(const void * dbr, double *upper_ctrl_limit,double *lower_ctrl_limit) +{ + *upper_ctrl_limit = static_cast(dbr)->upper_ctrl_limit; + *lower_ctrl_limit = static_cast(dbr)->lower_ctrl_limit; +} + +template +void get_DBRDisplay( + const void * dbr, double *upper_disp_limit,double *lower_disp_limit,string *units) +{ + *upper_disp_limit = static_cast(dbr)->upper_disp_limit; + *lower_disp_limit = static_cast(dbr)->lower_disp_limit; + *units = static_cast(dbr)->units; +} + +template +void get_DBRValueAlarm( + const void * dbr, + double *upper_alarm_limit,double *upper_warning_limit, + double *lower_warning_limit,double *lower_alarm_limit) +{ + *upper_alarm_limit = static_cast(dbr)->upper_alarm_limit; + *upper_warning_limit = static_cast(dbr)->upper_warning_limit; + *lower_warning_limit = static_cast(dbr)->lower_warning_limit; + *lower_alarm_limit = static_cast(dbr)->lower_alarm_limit; +} + Status DbdToPv::getFromDBD( PVStructurePtr const & pvStructure, BitSet::shared_pointer const & bitSet, @@ -321,8 +365,43 @@ Status DbdToPv::getFromDBD( if(fieldRequested) { void * value = dbr_value_ptr(args.dbr,caRequestType); - long count = args.count; - switch(caValueType) { + if(isArray) { + long count = args.count; + PVScalarArrayPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_STRING: + { + const dbr_string_t *dbrval = static_cast(value); + PVStringArrayPtr pvValue = pvStructure->getSubField("value"); + PVStringArray::svector arr(pvValue->reuse()); + arr.resize(count); + std::copy(dbrval, dbrval + count, arr.begin()); + pvValue->replace(freeze(arr)); + break; + } + case DBR_CHAR: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_SHORT: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_LONG: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_FLOAT: + copy_DBRScalarArray(value,count,pvValue); + break; + case DBR_DOUBLE: + copy_DBRScalarArray(value,count,pvValue); + break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } else { + PVScalarPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { case DBR_ENUM: { const dbr_enum_t *dbrval = static_cast(value); @@ -342,427 +421,59 @@ Status DbdToPv::getFromDBD( } break; } - case DBR_STRING: - { - const dbr_string_t *dbrval = static_cast(value); - if(isArray) { - PVStringArrayPtr pvValue = pvStructure->getSubField("value"); - PVStringArray::svector arr(pvValue->reuse()); - arr.resize(count); - std::copy(dbrval, dbrval + count, arr.begin()); - pvValue->replace(freeze(arr)); - } else { - PVStringPtr pvValue = pvStructure->getSubField("value"); - pvValue->put(*dbrval); - } - break; - } - case DBR_CHAR: - { - const dbr_char_t *dbrval = static_cast(value); - if(isArray) { - PVByteArrayPtr pvValue = pvStructure->getSubField("value"); - PVByteArray::svector arr(pvValue->reuse()); - arr.resize(count); - for(long i=0; ireplace(freeze(arr)); - } else { - PVBytePtr pvValue = pvStructure->getSubField("value"); - pvValue->put(*dbrval); - } - break; - } - case DBR_SHORT: - { - const dbr_short_t *dbrval = static_cast(value); - if(isArray) { - PVShortArrayPtr pvValue = pvStructure->getSubField("value"); - PVShortArray::svector arr(pvValue->reuse()); - arr.resize(count); - for(long i=0; ireplace(freeze(arr)); - } else { - PVShortPtr pvValue = pvStructure->getSubField("value"); - pvValue->put(*dbrval); - } - break; - } - case DBR_LONG: - { - const dbr_int_t *dbrval = static_cast(value); - if(isArray) { - PVIntArrayPtr pvValue = pvStructure->getSubField("value"); - PVIntArray::svector arr(pvValue->reuse()); - arr.resize(count); - for(long i=0; ireplace(freeze(arr)); - } else { - PVIntPtr pvValue = pvStructure->getSubField("value"); - pvValue->put(*dbrval); - } - break; - } - case DBR_FLOAT: - { - const dbr_float_t *dbrval = static_cast(value); - if(isArray) { - PVFloatArrayPtr pvValue = pvStructure->getSubField("value"); - PVFloatArray::svector arr(pvValue->reuse()); - arr.resize(count); - for(long i=0; ireplace(freeze(arr)); - } else { - PVFloatPtr pvValue = pvStructure->getSubField("value"); - pvValue->put(*dbrval); - } - break; - } - case DBR_DOUBLE: - { - const dbr_double_t *dbrval = static_cast(value); - if(isArray) { - PVDoubleArrayPtr pvValue - = pvStructure->getSubField("value"); - PVDoubleArray::svector arr(pvValue->reuse()); - arr.resize(count); - for(long i=0; ireplace(freeze(arr)); - } else { - PVDoublePtr pvValue = pvStructure->getSubField("value"); - pvValue->put(*dbrval); - } - 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_FLOAT: copy_DBRScalar(value,pvValue); break; + case DBR_DOUBLE: copy_DBRScalar(value,pvValue); break; default: Status errorStatus( - Status::STATUSTYPE_ERROR, string("DbdToPv::FromDBD logic error")); + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); return errorStatus; + } } if(caValueType!=DBR_ENUM) { bitSet->set(pvStructure->getSubField("value")->getFieldOffset()); } } - chtype type = args.type; - dbr_short_t status = 0; - dbr_short_t severity = 0; - epicsTimeStamp stamp = {0,0}; - if(caRequestType>=DBR_CTRL_STRING) { - string units; - string format; - double upper_disp_limit = 0.0; - double lower_disp_limit = 0.0; - double upper_alarm_limit = 0.0; - double upper_warning_limit = 0.0; - double lower_warning_limit = 0.0; - double lower_alarm_limit = 0.0; - double upper_ctrl_limit = 0.0; - double lower_ctrl_limit = 0.0; - switch(type) { - case DBR_CTRL_CHAR: - { - const dbr_ctrl_char *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - units = data->units; - upper_disp_limit = data->upper_disp_limit; - lower_disp_limit = data->lower_disp_limit; - upper_alarm_limit = data->upper_alarm_limit; - upper_warning_limit = data->upper_warning_limit; - lower_warning_limit = data->lower_warning_limit; - lower_alarm_limit = data->lower_alarm_limit; - upper_ctrl_limit = data->upper_ctrl_limit; - lower_ctrl_limit = data->lower_ctrl_limit; - format = "I4"; - break; - } - case DBR_CTRL_SHORT: - { - const dbr_ctrl_short *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - units = data->units; - upper_disp_limit = data->upper_disp_limit; - lower_disp_limit = data->lower_disp_limit; - upper_alarm_limit = data->upper_alarm_limit; - upper_warning_limit = data->upper_warning_limit; - lower_warning_limit = data->lower_warning_limit; - lower_alarm_limit = data->lower_alarm_limit; - upper_ctrl_limit = data->upper_ctrl_limit; - lower_ctrl_limit = data->lower_ctrl_limit; - format = "I6"; - break; - } - case DBR_CTRL_LONG: - { - const dbr_ctrl_long *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - units = data->units; - upper_disp_limit = data->upper_disp_limit; - lower_disp_limit = data->lower_disp_limit; - upper_alarm_limit = data->upper_alarm_limit; - upper_warning_limit = data->upper_warning_limit; - lower_warning_limit = data->lower_warning_limit; - lower_alarm_limit = data->lower_alarm_limit; - upper_ctrl_limit = data->upper_ctrl_limit; - lower_ctrl_limit = data->lower_ctrl_limit; - format = "I12"; - break; - } - case DBR_CTRL_FLOAT: - { - const dbr_ctrl_float *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - units = data->units; - upper_disp_limit = data->upper_disp_limit; - lower_disp_limit = data->lower_disp_limit; - upper_alarm_limit = data->upper_alarm_limit; - upper_warning_limit = data->upper_warning_limit; - lower_warning_limit = data->lower_warning_limit; - lower_alarm_limit = data->lower_alarm_limit; - upper_ctrl_limit = data->upper_ctrl_limit; - lower_ctrl_limit = data->lower_ctrl_limit; - int prec = data->precision; - ostringstream s; - s << "F" << prec + 6 << "." << prec; - format = s.str(); - break; - } - case DBR_CTRL_DOUBLE: - { - const dbr_ctrl_double *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - units = data->units; - upper_disp_limit = data->upper_disp_limit; - lower_disp_limit = data->lower_disp_limit; - upper_alarm_limit = data->upper_alarm_limit; - upper_warning_limit = data->upper_warning_limit; - lower_warning_limit = data->lower_warning_limit; - lower_alarm_limit = data->lower_alarm_limit; - upper_ctrl_limit = data->upper_ctrl_limit; - lower_ctrl_limit = data->lower_ctrl_limit; - int prec = data->precision; - ostringstream s; - s << "F" << prec + 6 << "." << prec; - format = s.str(); - break; - } - default : - throw std::runtime_error("DbdToPv::getDone logic error"); - } - if(displayRequested) - { - PVStructurePtr pvDisplay(pvStructure->getSubField("display")); - if(caDisplay.lower_disp_limit!=lower_disp_limit) { - caDisplay.lower_disp_limit = lower_disp_limit; - PVDoublePtr pvDouble = pvDisplay->getSubField("limitLow"); - pvDouble->put(lower_disp_limit); - bitSet->set(pvDouble->getFieldOffset()); - } - if(caDisplay.upper_disp_limit!=upper_disp_limit) { - caDisplay.upper_disp_limit = upper_disp_limit; - PVDoublePtr pvDouble = pvDisplay->getSubField("limitHigh"); - pvDouble->put(upper_disp_limit); - bitSet->set(pvDouble->getFieldOffset()); - } - if(caDisplay.units!=units) { - caDisplay.units = units; - PVStringPtr pvString = pvDisplay->getSubField("units"); - pvString->put(units); - bitSet->set(pvString->getFieldOffset()); - } - if(caDisplay.format!=format) { - caDisplay.format = format; - PVStringPtr pvString = pvDisplay->getSubField("format"); - 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(controlRequested) - { - PVStructurePtr pvControl(pvStructure->getSubField("control")); - if(caControl.upper_ctrl_limit!=upper_ctrl_limit) { - caControl.upper_ctrl_limit = upper_ctrl_limit; - PVDoublePtr pv = pvControl->getSubField("limitHigh"); - pv->put(upper_ctrl_limit); - bitSet->set(pv->getFieldOffset()); - } - if(caControl.lower_ctrl_limit!=lower_ctrl_limit) { - caControl.lower_ctrl_limit = lower_ctrl_limit; - PVDoublePtr pv = pvControl->getSubField("limitLow"); - pv->put(lower_ctrl_limit); - bitSet->set(pv->getFieldOffset()); - } - } - if(valueAlarmRequested) { - ConvertPtr convert(getConvert()); - PVStructurePtr pvValueAlarm(pvStructure->getSubField("valueAlarm")); - if(caValueAlarm.upper_alarm_limit!=upper_alarm_limit) { - caValueAlarm.upper_alarm_limit = upper_alarm_limit; - PVScalarPtr pv = pvValueAlarm->getSubField("highAlarmLimit"); - convert->fromDouble(pv,upper_alarm_limit); - bitSet->set(pv->getFieldOffset()); - } - if(caValueAlarm.upper_warning_limit!=upper_warning_limit) { - caValueAlarm.upper_warning_limit = upper_warning_limit; - PVScalarPtr pv = pvValueAlarm->getSubField("highWarningLimit"); - convert->fromDouble(pv,upper_warning_limit); - bitSet->set(pv->getFieldOffset()); - } - if(caValueAlarm.lower_warning_limit!=lower_warning_limit) { - caValueAlarm.lower_warning_limit = lower_warning_limit; - PVScalarPtr pv = pvValueAlarm->getSubField("lowWarningLimit"); - convert->fromDouble(pv,lower_warning_limit); - bitSet->set(pv->getFieldOffset()); - } - if(caValueAlarm.lower_alarm_limit!=lower_alarm_limit) { - caValueAlarm.lower_alarm_limit = lower_alarm_limit; - PVScalarPtr pv = pvValueAlarm->getSubField("lowAlarmLimit"); - convert->fromDouble(pv,lower_alarm_limit); - bitSet->set(pv->getFieldOffset()); - } - } - } else if(caRequestType>=DBR_TIME_STRING) { - switch(type) { - case DBR_TIME_STRING: - { - const dbr_time_string *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - stamp = data->stamp; - break; - } - case DBR_TIME_CHAR: - { - const dbr_time_char *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - stamp = data->stamp; - break; - } - case DBR_TIME_SHORT: - { - const dbr_time_short *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - stamp = data->stamp; - break; - } - case DBR_TIME_LONG: - { - const dbr_time_long *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - stamp = data->stamp; - break; - } - case DBR_TIME_FLOAT: - { - const dbr_time_float *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - stamp = data->stamp; - break; - } - case DBR_TIME_DOUBLE: - { - const dbr_time_double *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - stamp = data->stamp; - break; - } - default: - throw std::runtime_error("DbdToPv::getDone logic error"); - } - } else if(caRequestType>=DBR_STS_STRING) { - switch(type) { - case DBR_STS_STRING: - { - const dbr_sts_string *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - break; - } - case DBR_STS_CHAR: - { - const dbr_sts_char *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - break; - } - case DBR_STS_SHORT: - { - const dbr_sts_short *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - break; - } - case DBR_STS_LONG: - { - const dbr_sts_long *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - break; - } - case DBR_STS_FLOAT: - { - const dbr_sts_float *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - break; - } - case DBR_STS_DOUBLE: - { - const dbr_sts_double *data = static_cast(args.dbr); - status = data->status; - severity = data->severity; - break; - } - default: - throw std::runtime_error("DbdToPv::getDone logic error"); - } - } if(alarmRequested) { - bool statusChanged = false; - bool severityChanged = false; - PVStructurePtr pvAlarm(pvStructure->getSubField("alarm")); - PVIntPtr pvSeverity(pvAlarm->getSubField("severity")); - if(caAlarm.severity!=severity) { - caAlarm.severity = severity; - pvSeverity->put(severity); - severityChanged = true; - } - PVStringPtr pvMessage(pvAlarm->getSubField("message")); - PVIntPtr pvStatus(pvAlarm->getSubField("status")); - if(caAlarm.status!=status) { - caAlarm.status = status; - pvStatus->put(status); - string message("UNKNOWN STATUS"); - if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]); - pvMessage->put(message); - statusChanged = true; - } - if(statusChanged&&severityChanged) { - bitSet->set(pvAlarm->getFieldOffset()); - } else if(severityChanged) { - bitSet->set(pvSeverity->getFieldOffset()); - } else if(statusChanged) { - bitSet->set(pvStatus->getFieldOffset()); - bitSet->set(pvMessage->getFieldOffset()); - } + // Note that status and severity are aways the first two members of DBR_ + const dbr_sts_string *data = static_cast(args.dbr); + dbr_short_t status = data->status; + dbr_short_t severity = data->severity; + bool statusChanged = false; + bool severityChanged = false; + PVStructurePtr pvAlarm(pvStructure->getSubField("alarm")); + PVIntPtr pvSeverity(pvAlarm->getSubField("severity")); + if(caAlarm.severity!=severity) { + caAlarm.severity = severity; + pvSeverity->put(severity); + severityChanged = true; + } + PVStringPtr pvMessage(pvAlarm->getSubField("message")); + PVIntPtr pvStatus(pvAlarm->getSubField("status")); + if(caAlarm.status!=status) { + caAlarm.status = status; + pvStatus->put(status); + string message("UNKNOWN STATUS"); + if(status<=ALARM_NSTATUS) message = string(epicsAlarmConditionStrings[status]); + pvMessage->put(message); + statusChanged = true; + } + if(statusChanged&&severityChanged) { + bitSet->set(pvAlarm->getFieldOffset()); + } else if(severityChanged) { + bitSet->set(pvSeverity->getFieldOffset()); + } else if(statusChanged) { + bitSet->set(pvStatus->getFieldOffset()); + bitSet->set(pvMessage->getFieldOffset()); + } } if(timeStampRequested) { + // Note that epicsTimeStamp always follows status and severity + const dbr_time_string *data = static_cast(args.dbr); + epicsTimeStamp stamp = data->stamp; PVStructurePtr pvTimeStamp(pvStructure->getSubField("timeStamp")); if(caTimeStamp.secPastEpoch!=stamp.secPastEpoch) { caTimeStamp.secPastEpoch = stamp.secPastEpoch; @@ -776,7 +487,172 @@ Status DbdToPv::getFromDBD( pvNano->put(stamp.nsec); bitSet->set(pvNano->getFieldOffset()); } - + } + if(controlRequested) + { + double upper_ctrl_limit = 0.0; + double lower_ctrl_limit = 0.0; + switch(caRequestType) { + case DBR_CTRL_CHAR: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_SHORT: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_LONG: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_FLOAT: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + case DBR_CTRL_DOUBLE: + get_DBRControl(args.dbr,&upper_ctrl_limit,&lower_ctrl_limit); break; + default : + throw std::runtime_error("DbdToPv::getFromDBD logic error"); + } + PVStructurePtr pvControl(pvStructure->getSubField("control")); + if(caControl.upper_ctrl_limit!=upper_ctrl_limit) { + caControl.upper_ctrl_limit = upper_ctrl_limit; + PVDoublePtr pv = pvControl->getSubField("limitHigh"); + pv->put(upper_ctrl_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caControl.lower_ctrl_limit!=lower_ctrl_limit) { + caControl.lower_ctrl_limit = lower_ctrl_limit; + PVDoublePtr pv = pvControl->getSubField("limitLow"); + pv->put(lower_ctrl_limit); + bitSet->set(pv->getFieldOffset()); + } + } + if(displayRequested) + { + string units; + string format; + double upper_disp_limit = 0.0; + double lower_disp_limit = 0.0; + switch(caRequestType) { + case DBR_CTRL_CHAR: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + format = "I4"; break; + case DBR_CTRL_SHORT: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + format = "I6"; break; + case DBR_CTRL_LONG: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + format = "I12"; break; + case DBR_CTRL_FLOAT: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + { + const dbr_ctrl_float *data = static_cast(args.dbr); + int prec = data->precision; + ostringstream s; + s << "F" << prec + 6 << "." << prec; + format = s.str(); + } + break; + case DBR_CTRL_DOUBLE: + get_DBRDisplay(args.dbr,&upper_disp_limit,&lower_disp_limit,&units); + { + const dbr_ctrl_double *data = static_cast(args.dbr); + int prec = data->precision; + ostringstream s; + s << "F" << prec + 6 << "." << prec; + format = s.str(); + } + break; + default : + throw std::runtime_error("DbdToPv::getFromDBD logic error"); + } + PVStructurePtr pvDisplay(pvStructure->getSubField("display")); + if(caDisplay.lower_disp_limit!=lower_disp_limit) { + caDisplay.lower_disp_limit = lower_disp_limit; + PVDoublePtr pvDouble = pvDisplay->getSubField("limitLow"); + pvDouble->put(lower_disp_limit); + bitSet->set(pvDouble->getFieldOffset()); + } + if(caDisplay.upper_disp_limit!=upper_disp_limit) { + caDisplay.upper_disp_limit = upper_disp_limit; + PVDoublePtr pvDouble = pvDisplay->getSubField("limitHigh"); + pvDouble->put(upper_disp_limit); + bitSet->set(pvDouble->getFieldOffset()); + } + if(caDisplay.units!=units) { + caDisplay.units = units; + PVStringPtr pvString = pvDisplay->getSubField("units"); + pvString->put(units); + bitSet->set(pvString->getFieldOffset()); + } + if(caDisplay.format!=format) { + caDisplay.format = format; + PVStringPtr pvString = pvDisplay->getSubField("format"); + 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; + double upper_warning_limit = 0.0; + double lower_warning_limit = 0.0; + double lower_alarm_limit = 0.0; + switch(caRequestType) { + case DBR_CTRL_CHAR: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_SHORT: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_LONG: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_FLOAT: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + case DBR_CTRL_DOUBLE: + get_DBRValueAlarm(args.dbr, + &upper_alarm_limit,&upper_warning_limit, + &lower_warning_limit,&lower_alarm_limit); + break; + default : + throw std::runtime_error("DbdToPv::getFromDBD logic error"); + } + ConvertPtr convert(getConvert()); + PVStructurePtr pvValueAlarm(pvStructure->getSubField("valueAlarm")); + if(caValueAlarm.upper_alarm_limit!=upper_alarm_limit) { + caValueAlarm.upper_alarm_limit = upper_alarm_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("highAlarmLimit"); + convert->fromDouble(pv,upper_alarm_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.upper_warning_limit!=upper_warning_limit) { + caValueAlarm.upper_warning_limit = upper_warning_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("highWarningLimit"); + convert->fromDouble(pv,upper_warning_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.lower_warning_limit!=lower_warning_limit) { + caValueAlarm.lower_warning_limit = lower_warning_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("lowWarningLimit"); + convert->fromDouble(pv,lower_warning_limit); + bitSet->set(pv->getFieldOffset()); + } + if(caValueAlarm.lower_alarm_limit!=lower_alarm_limit) { + caValueAlarm.lower_alarm_limit = lower_alarm_limit; + PVScalarPtr pv = pvValueAlarm->getSubField("lowAlarmLimit"); + convert->fromDouble(pv,lower_alarm_limit); + bitSet->set(pv->getFieldOffset()); + } } if(firstTime) { firstTime = false; @@ -786,6 +662,23 @@ Status DbdToPv::getFromDBD( return Status::Ok; } +template +const void * put_DBRScalar(dbrT *val,PVScalar::shared_pointer const & pvScalar) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvScalar); + *val = value->get(); + return val; +} + +template +const void * put_DBRScalarArray(unsigned long*count, PVScalarArray::shared_pointer const & pvArray) +{ + std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvArray); + *count = value->getLength(); + return value->view().data(); +} + + Status DbdToPv::putToDBD( CAChannelPtr const & caChannel, PVStructurePtr const & pvStructure, @@ -794,28 +687,21 @@ Status DbdToPv::putToDBD( chid channelID = caChannel->getChannelID(); const void *pValue = NULL; unsigned long count = 1; - dbr_enum_t indexvalue(0); + char *ca_stringBuffer(0); dbr_char_t bvalue(0); dbr_short_t svalue(0); - dbr_long_t ivalue(0); + dbr_long_t lvalue(0); dbr_float_t fvalue(0); dbr_double_t dvalue(0); - char *ca_stringBuffer(0); - - - switch(caValueType) { - case DBR_ENUM: - { - indexvalue = pvStructure->getSubField("value.index")->get(); - pValue = &indexvalue; - break; - } - case DBR_STRING: - { - if(isArray) { + if(isArray) { + PVScalarArrayPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_STRING: + { PVStringArrayPtr pvValue = pvStructure->getSubField("value"); count = pvValue->getLength(); if(count<1) break; + if(count>maxElements) count = maxElements; int nbytes = count*MAX_STRING_SIZE; ca_stringBuffer = new char[nbytes]; memset(ca_stringBuffer, 0, nbytes); @@ -829,78 +715,51 @@ Status DbdToPv::putToDBD( memcpy(pnext, value.c_str(), len); pnext += MAX_STRING_SIZE; } - } else { - pValue = pvStructure->getSubField("value")->get().c_str(); + break; } - break; - } - case DBR_CHAR: - { - if(isArray) { - PVByteArrayPtr pvValue = pvStructure->getSubField("value"); - count = pvValue->getLength(); - pValue = pvValue->view().data(); - } else { - bvalue = pvStructure->getSubField("value")->get(); - pValue = &bvalue; - } - break; - } - case DBR_SHORT: - { - if(isArray) { - PVShortArrayPtr pvValue = pvStructure->getSubField("value"); - count = pvValue->getLength(); - pValue = pvValue->view().data(); - } else { - svalue = pvStructure->getSubField("value")->get(); - pValue = &svalue; - } - break; - } - case DBR_LONG: - { - if(isArray) { - PVIntArrayPtr pvValue = pvStructure->getSubField("value"); - count = pvValue->getLength(); - pValue = pvValue->view().data(); - } else { - ivalue = pvStructure->getSubField("value")->get(); - pValue = &ivalue; - } - break; - } - case DBR_FLOAT: - { - if(isArray) { - PVFloatArrayPtr pvValue = pvStructure->getSubField("value"); - count = pvValue->getLength(); - pValue = pvValue->view().data(); - } else { - fvalue = pvStructure->getSubField("value")->get(); - pValue = &fvalue; - } - break; - } - case DBR_DOUBLE: - { - if(isArray) { - PVDoubleArrayPtr pvValue = pvStructure->getSubField("value"); - count = pvValue->getLength(); - pValue = pvValue->view().data(); - } else { - dvalue = pvStructure->getSubField("value")->get(); - pValue = &dvalue; - } - break; - } - default: - Status errorStatus( - Status::STATUSTYPE_ERROR, string("DbdToPv::FromDBD logic error")); - return errorStatus; - } - int result = 0; - if(block) { + case DBR_CHAR: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_SHORT: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_LONG: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_FLOAT: + pValue = put_DBRScalarArray(&count,pvValue); + break; + case DBR_DOUBLE: + pValue = put_DBRScalarArray(&count,pvValue); + break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } else { + PVScalarPtr pvValue = pvStructure->getSubField("value"); + switch(caValueType) { + case DBR_ENUM: + { + dbr_enum_t indexvalue = pvStructure->getSubField("value.index")->get(); + pValue = &indexvalue; + 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_FLOAT: pValue = put_DBRScalar(&fvalue,pvValue); break; + case DBR_DOUBLE: pValue = put_DBRScalar(&dvalue,pvValue); break; + default: + Status errorStatus( + Status::STATUSTYPE_ERROR, string("DbdToPv::getFromDBD logic error")); + return errorStatus; + } + } + int result = 0; + if(block) { caChannel->attachContext(); result = ca_array_put_callback(caValueType,count,channelID,pValue,putHandler,this); if(result==ECA_NORMAL) { @@ -910,15 +769,15 @@ Status DbdToPv::putToDBD( } return putStatus; } - } else { + } else { caChannel->attachContext(); result = ca_array_put(caValueType,count,channelID,pValue); ca_flush_io(); - } - if(ca_stringBuffer!=NULL) delete[] ca_stringBuffer; - if(result==ECA_NORMAL) return Status::Ok; - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result))); - return errorStatus; + } + if(ca_stringBuffer!=NULL) delete[] ca_stringBuffer; + if(result==ECA_NORMAL) return Status::Ok; + Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result))); + return errorStatus; } void DbdToPv::putDone(struct event_handler_args &args) diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h index 72d63e3..fcb1d62 100644 --- a/src/ca/dbdToPv.h +++ b/src/ca/dbdToPv.h @@ -73,17 +73,11 @@ class DbdToPv { public: POINTER_DEFINITIONS(DbdToPv); - /** @brief - * - */ static DbdToPvPtr create( CAChannelPtr const & caChannel, epics::pvData::PVStructurePtr const & pvRequest, IOType ioType ); - /** @brief - * - */ epics::pvData::PVStructurePtr createPVStructure(); chtype getRequestType(); epics::pvData::Status getFromDBD( @@ -100,10 +94,8 @@ public: void descriptionConnected(struct connection_handler_args args); void getDescriptionDone(struct event_handler_args &args); void putDone(struct event_handler_args &args); - private: DbdToPv(IOType ioType); - void activate( CAChannelPtr const & caChannel, epics::pvData::PVStructurePtr const & pvRequest @@ -119,6 +111,7 @@ private: bool firstTime; chtype caValueType; chtype caRequestType; + unsigned long maxElements; epicsTimeStamp caTimeStamp; CaAlarm caAlarm; CaDisplay caDisplay; From eafe9021a7902b9e7375e9f82a6e4c08bbbb865c Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 7 May 2018 16:03:56 -0400 Subject: [PATCH 09/36] add tests for provider ca --- testApp/Makefile | 3 +- testApp/ca/Makefile | 8 + testApp/ca/testCaProvider.cpp | 681 ++++++++++++++++++++++++++++++++++ testApp/ca/testCaProvider.db | 290 +++++++++++++++ testApp/pvAccessAllTests.c | 5 + 5 files changed, 986 insertions(+), 1 deletion(-) create mode 100644 testApp/ca/Makefile create mode 100644 testApp/ca/testCaProvider.cpp create mode 100644 testApp/ca/testCaProvider.db diff --git a/testApp/Makefile b/testApp/Makefile index 4d59b61..d23979e 100644 --- a/testApp/Makefile +++ b/testApp/Makefile @@ -10,10 +10,11 @@ USR_CPPFLAGS += -I$(TOP)/src/remoteClient PVACCESS_TEST = $(TOP)/testApp -PROD_LIBS += pvAccess pvData Com +PROD_LIBS += pvAccess pvAccessCA pvData Com include $(PVACCESS_TEST)/utils/Makefile include $(PVACCESS_TEST)/remote/Makefile +include $(PVACCESS_TEST)/ca/Makefile # pvAccessAllTests runs all the test programs in a known working order. testHarness_SRCS += pvAccessAllTests.c diff --git a/testApp/ca/Makefile b/testApp/ca/Makefile new file mode 100644 index 0000000..5c38178 --- /dev/null +++ b/testApp/ca/Makefile @@ -0,0 +1,8 @@ +# This is a Makefile fragment, see ../Makefile + +SRC_DIRS += $(PVACCESS_TEST)/ca + +TESTPROD_HOST += testCaProvider +testCaProvider += testCaProvider.cpp +testHarness_SRCS += testCaProvider.cpp +TESTS += testCaProvider diff --git a/testApp/ca/testCaProvider.cpp b/testApp/ca/testCaProvider.cpp new file mode 100644 index 0000000..38a879c --- /dev/null +++ b/testApp/ca/testCaProvider.cpp @@ -0,0 +1,681 @@ +/* testCaProvider.cpp */ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ +/* Author: Marty Kraimer Date: 2018.05 */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace epics::pvAccess::ca; +using std::string; + +class TestChannel; +typedef std::tr1::shared_ptr TestChannelPtr; + +class TestChannel: + public ChannelRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannel); + string getRequesterName(); + void message( + string const & message, + MessageType messageType); + virtual void channelCreated(const Status& status, Channel::shared_pointer const & channel); + virtual void channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState); + string getChannelName(); + Channel::shared_pointer getChannel(); + static TestChannelPtr create(string const & channelName); + void connect(); + void waitConnect(double timeout); +private: + TestChannel(string const & channelName); + string channelName; + Event waitForConnect; + Channel::shared_pointer channel; +}; + +string TestChannel::getChannelName() { return channelName;} + +Channel::shared_pointer TestChannel::getChannel() { return channel;} + +TestChannelPtr TestChannel::create(string const & channelName) +{ + TestChannelPtr testChannel(new TestChannel(channelName)); + testChannel->connect(); + return testChannel; +} + +TestChannel::TestChannel(string const & channelName) +: channelName(channelName) +{ +} + +string TestChannel::getRequesterName() { return "testChannel";} +void TestChannel::message(string const & message,MessageType messageType) {}; + +void TestChannel::channelCreated(const Status& status, Channel::shared_pointer const & channel) +{ + if(channel->isConnected()) waitForConnect.signal(); +} + +void TestChannel::channelStateChange(Channel::shared_pointer const & channel, Channel::ConnectionState connectionState) +{ + if(connectionState==Channel::CONNECTED) waitForConnect.signal(); +} + +void TestChannel::connect() +{ + ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); + if(!channelProvider) throw std::runtime_error(channelName + " provider ca not registered"); + channel = channelProvider->createChannel(channelName,shared_from_this(),ChannelProvider::PRIORITY_DEFAULT); + if(!channel) throw std::runtime_error(channelName + " channelCreate failed "); + waitConnect(5.0); +} + +void TestChannel::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(channelName + " TestChannel::waitConnect failed "); +} + +class TestChannelGet; +typedef std::tr1::shared_ptr TestChannelGetPtr; + +class TestChannelGetRequester; +typedef std::tr1::shared_ptr TestChannelGetRequesterPtr; +typedef std::tr1::weak_ptr TestChannelGetRequesterWPtr; + +class TestChannelGetRequester +{ +public: + virtual void getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) = 0; +}; + +class TestChannelGet: + public ChannelGetRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelGet); + virtual string getRequesterName(); + virtual void message(string const & message, epics::pvData::MessageType messageType) {} + virtual void channelGetConnect( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + Structure::const_shared_pointer const & structure); + virtual void getDone( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestChannelGetPtr create( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + void connect(); + void waitConnect(double timeout); + void get(); +private: + TestChannelGet( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + + TestChannelGetRequesterWPtr getRequester; + TestChannelPtr testChannel; + PVStructurePtr pvRequest; + PVStructurePtr pvStructure; + Event waitForConnect; + ChannelGet::shared_pointer channelGet; +}; + +TestChannelGetPtr TestChannelGet::create( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) +{ + TestChannelGetPtr testChannelGet(new TestChannelGet(getRequester,testChannel,pvRequest)); + testChannelGet->connect(); + testChannelGet->waitConnect(5.0); + return testChannelGet; +} + +TestChannelGet::TestChannelGet( + TestChannelGetRequesterPtr const &getRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) + : getRequester(getRequester), + testChannel(testChannel), + pvRequest(pvRequest) +{ +} + +string TestChannelGet::getRequesterName() {return "TestChannelGet";} + +void TestChannelGet::channelGetConnect( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + Structure::const_shared_pointer const & structure) +{ + waitForConnect.signal(); +} + +void TestChannelGet::getDone( + const Status& status, + ChannelGet::shared_pointer const & channelGet, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + TestChannelGetRequesterPtr req(getRequester.lock()); + if(!req) return; + if(status.isOK()) { + req->getDone(pvStructure,bitSet); + return; + } + string message = string("channel ") + + testChannel->getChannelName() + + " TestChannelGet::getDone " + + status.getMessage(); + throw std::runtime_error(message); +} + +void TestChannelGet::connect() +{ + channelGet = testChannel->getChannel()->createChannelGet(shared_from_this(),pvRequest); + if(!channelGet) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed "); +} + +void TestChannelGet::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestChannelGet::waitConnect failed "); +} + + +void TestChannelGet::get() +{ + channelGet->get(); +} + +class TestChannelPut; +typedef std::tr1::shared_ptr TestChannelPutPtr; + +class TestChannelPutRequester; +typedef std::tr1::shared_ptr TestChannelPutRequesterPtr; +typedef std::tr1::weak_ptr TestChannelPutRequesterWPtr; + +class TestChannelPutRequester +{ +public: + virtual void putDone() = 0; +}; + +class TestChannelPut: + public ChannelPutRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelPut); + virtual string getRequesterName(); + virtual void message(string const & message, MessageType messageType) {} + virtual void channelPutConnect( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + Structure::const_shared_pointer const & structure); + virtual void putDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut); + virtual void getDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestChannelPutPtr create( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel); + void connect(); + void waitConnect(double timeout); + void put(string const & value); +private: + TestChannelPut( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel); + + TestChannelPutRequesterWPtr putRequester; + TestChannelPtr testChannel; + PVStructurePtr pvStructure; + BitSetPtr bitSet; + Event waitForConnect; + ChannelPut::shared_pointer channelPut; +}; + +TestChannelPutPtr TestChannelPut::create( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel) +{ + TestChannelPutPtr testChannelPut(new TestChannelPut(putRequester,testChannel)); + testChannelPut->connect(); + testChannelPut->waitConnect(5.0); + return testChannelPut; +} + +TestChannelPut::TestChannelPut( + TestChannelPutRequesterPtr const &putRequester, + TestChannelPtr const &testChannel) + : putRequester(putRequester), + testChannel(testChannel) +{ +} + +string TestChannelPut::getRequesterName() {return "TestChannelPut";} + +void TestChannelPut::channelPutConnect( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + Structure::const_shared_pointer const & structure) +{ + pvStructure = PVDataCreate::getPVDataCreate()->createPVStructure(structure); + bitSet = BitSetPtr(new BitSet(pvStructure->getNumberFields())); + waitForConnect.signal(); +} + +void TestChannelPut::getDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut, + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + throw std::runtime_error("TestChannelPut::getDone should not be called"); +} + +void TestChannelPut::putDone( + const Status& status, + ChannelPut::shared_pointer const & channelPut) +{ + TestChannelPutRequesterPtr req(putRequester.lock()); + if(!req) return; + if(status.isOK()) { + req->putDone(); + return; + } + string message = string("channel ") + + testChannel->getChannelName() + + " TestChannelPut::putDone " + + status.getMessage(); + throw std::runtime_error(message); +} + +void TestChannelPut::connect() +{ + string request("value"); + PVStructurePtr pvRequest(createRequest(request)); + + channelPut = testChannel->getChannel()->createChannelPut(shared_from_this(),pvRequest); + if(!channelPut) throw std::runtime_error(testChannel->getChannelName() + " channelCreate failed "); +} + +void TestChannelPut::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::waitConnect failed "); +} + + +void TestChannelPut::put(string const & value) +{ + PVFieldPtr pvField(pvStructure->getSubField("value")); + if(!pvField) throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put no value "); + FieldConstPtr field(pvField->getField()); + Type type(field->getType()); + if(type==scalar) { + PVScalarPtr pvScalar(std::tr1::static_pointer_cast(pvField)); + getConvert()->fromString(pvScalar,value); + bitSet->set(pvField->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put not supported type"); +} + +class TestChannelMonitor; +typedef std::tr1::shared_ptr TestChannelMonitorPtr; + +class TestChannelMonitorRequester; +typedef std::tr1::shared_ptr TestChannelMonitorRequesterPtr; +typedef std::tr1::weak_ptr TestChannelMonitorRequesterWPtr; + +class TestChannelMonitorRequester +{ +public: + virtual void monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) = 0; +}; + +class TestChannelMonitor: + public MonitorRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestChannelMonitor); + virtual string getRequesterName(); + virtual void message(string const & message, MessageType messageType) {} + virtual void monitorConnect( + Status const & status, + MonitorPtr const & monitor, + StructureConstPtr const & structure); + virtual void monitorEvent(MonitorPtr const & monitor); + virtual void unlisten(MonitorPtr const & monitor); + static TestChannelMonitorPtr create( + TestChannelMonitorRequesterPtr const &putRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + void connect(); + void waitConnect(double timeout); + void stopEvents(); +private: + TestChannelMonitor( + TestChannelMonitorRequesterPtr const &putRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest); + + TestChannelMonitorRequesterWPtr monitorRequester; + TestChannelPtr testChannel; + PVStructurePtr pvRequest; + Event waitForConnect; + Monitor::shared_pointer channelMonitor; +}; + +TestChannelMonitorPtr TestChannelMonitor::create( + TestChannelMonitorRequesterPtr const &monitorRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) +{ + TestChannelMonitorPtr testChannelMonitor(new TestChannelMonitor(monitorRequester,testChannel,pvRequest)); + testChannelMonitor->connect(); + testChannelMonitor->waitConnect(5.0); + return testChannelMonitor; +} + +TestChannelMonitor::TestChannelMonitor( + TestChannelMonitorRequesterPtr const &monitorRequester, + TestChannelPtr const &testChannel, + PVStructurePtr const & pvRequest) + : monitorRequester(monitorRequester), + testChannel(testChannel), + pvRequest(pvRequest) +{ +} + +string TestChannelMonitor::getRequesterName() {return "TestChannelMonitor";} + +void TestChannelMonitor::monitorConnect( + Status const & status, + MonitorPtr const & monitor, + StructureConstPtr const & structure) +{ + waitForConnect.signal(); +} + + +void TestChannelMonitor::monitorEvent(MonitorPtr const & monitor) +{ + TestChannelMonitorRequesterPtr req(monitorRequester.lock()); + if(!req) return; + while(true) { + MonitorElementPtr monitorElement = monitor->poll(); + if(!monitorElement) return; + req->monitorEvent(monitorElement->pvStructurePtr,monitorElement->changedBitSet); + monitor->release(monitorElement); + } +} + + +void TestChannelMonitor::unlisten(MonitorPtr const & monitor) +{ +} + +void TestChannelMonitor::connect() +{ + channelMonitor = testChannel->getChannel()->createMonitor(shared_from_this(),pvRequest); + if(!channelMonitor) throw std::runtime_error(testChannel->getChannelName() + " TestChannelMonitor::connect failed "); +} + +void TestChannelMonitor::waitConnect(double timeout) +{ + if(waitForConnect.wait(timeout)) { + channelMonitor->start(); + return; + } + throw std::runtime_error(testChannel->getChannelName() + " TestChannelMonitor::waitConnect failed "); +} + +void TestChannelMonitor::stopEvents() +{ + channelMonitor->stop(); +} + +class TestClient; +typedef std::tr1::shared_ptr TestClientPtr; + +class TestClient: + public TestChannelGetRequester, + public TestChannelPutRequester, + public TestChannelMonitorRequester, + public std::tr1::enable_shared_from_this +{ +public: + POINTER_DEFINITIONS(TestClient); + virtual void getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + virtual void putDone(); + virtual void monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet); + static TestClientPtr create(string const &channelName,PVStructurePtr const & pvRequest); + void connect(); + void get(); + void put(string const & value); + void waitGet(double timeout); + void waitPut(double timeout); + void stopEvents(); +private: + TestClient(string const &channelName,PVStructurePtr const & pvRequest); + string channelName; + PVStructurePtr pvRequest; + TestChannelPtr testChannel; + TestChannelGetPtr testChannelGet; + TestChannelPutPtr testChannelPut; + TestChannelMonitorPtr testChannelMonitor; + Event waitForGet; + Event waitForPut; +}; + +TestClientPtr TestClient::create(string const &channelName,PVStructurePtr const & pvRequest) +{ + TestClientPtr testClient(new TestClient(channelName,pvRequest)); + testClient->connect(); + return testClient; +} + +TestClient::TestClient(string const &channelName,PVStructurePtr const & pvRequest) + : channelName(channelName), + pvRequest(pvRequest) +{ +} + +void TestClient::connect() +{ + testChannel = TestChannel::create(channelName); + testChannelGet = TestChannelGet::create(shared_from_this(),testChannel,pvRequest); + testChannelPut = TestChannelPut::create(shared_from_this(),testChannel); + testChannelMonitor = TestChannelMonitor::create(shared_from_this(),testChannel,pvRequest); +} + +void TestClient::getDone( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + testOk(pvStructure!=NULL,"pvStructure not null"); + testOk(pvStructure->getSubField("value")!=NULL,"value not null"); + testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null"); + testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null"); + waitForGet.signal(); +} + +void TestClient::putDone() +{ + waitForPut.signal(); +} + +void TestClient::monitorEvent( + PVStructure::shared_pointer const & pvStructure, + BitSet::shared_pointer const & bitSet) +{ + std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" + << " bitSet " << *bitSet + << " pvStructure\n" << pvStructure << "\n"; +} + +void TestClient::get() +{ + testChannelGet->get(); + waitGet(5.0); +} + +void TestClient::waitGet(double timeout) +{ + if(waitForGet.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitGet failed "); +} + +void TestClient::put(string const & value) +{ + testChannelPut->put(value); + waitPut(5.0); +} + +void TestClient::waitPut(double timeout) +{ + if(waitForPut.wait(timeout)) return; + throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitPut failed "); +} + +void TestClient::stopEvents() +{ + testChannelMonitor->stopEvents(); +} + +class TestIoc; +typedef std::tr1::shared_ptr TestIocPtr; + +class TestIoc : + public epicsThreadRunable +{ +public: + virtual void run(); + static TestIocPtr create(); + void start(); +private: + std::auto_ptr thread; +}; + +TestIocPtr TestIoc::create() +{ + return TestIocPtr(new TestIoc()); +} + +void TestIoc::start() +{ + thread = std::auto_ptr(new epicsThread( + *this, + "testIoc", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + +void TestIoc::run() +{ + system("softIoc -d ../ca/testCaProvider.db"); +} + +MAIN(testCaProvider) +{ + testPlan(11); + testDiag("===Test caProvider==="); + CAClientFactory::start(); + ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); + try{ + TestIocPtr testIoc(new TestIoc()); + testIoc->start(); + if(!channelProvider) { + throw std::runtime_error(" provider ca not registered"); + } + string channelName; + channelName = "DBRdoubleout"; + string request("value,alarm,timeStamp"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr scalarout(TestClient::create(channelName,pvRequest)); + testOk(scalarout!=NULL,"DBRdoubleout not null"); + scalarout->put("1.5"); + scalarout->get(); + scalarout->stopEvents(); + channelName = "DBRstringout"; + scalarout = TestClient::create(channelName,pvRequest); + testOk(scalarout!=NULL,"DBRstringout not null"); + scalarout->put("test"); + scalarout->get(); + scalarout->stopEvents(); + // put to record that makes IOC exit + channelName = "DBRexit"; + scalarout = TestClient::create(channelName,pvRequest); + testOk(scalarout!=NULL,"DBRexit not null"); + scalarout->put("1"); + }catch(std::exception& e){ + PRINT_EXCEPTION(e); + testAbort("Unexpected Exception: %s", e.what()); + } + return testDone();; +} + diff --git a/testApp/ca/testCaProvider.db b/testApp/ca/testCaProvider.db new file mode 100644 index 0000000..d66e322 --- /dev/null +++ b/testApp/ca/testCaProvider.db @@ -0,0 +1,290 @@ + +record(calcout, "DBRcalcout") +{ + field(DESC, "calcout") + field(CALC, "(A Date: Wed, 9 May 2018 10:49:32 -0400 Subject: [PATCH 10/36] moved ca tests from testApp to testCa --- Makefile | 5 ++++ testApp/Makefile | 3 +-- testApp/ca/Makefile | 8 ------ testApp/pvAccessAllTests.c | 5 ---- testCa/Makefile | 33 +++++++++++++++++++++++ testCa/rtemsTestHarness.c | 12 +++++++++ {testApp/ca => testCa}/testCaProvider.cpp | 27 ++++++++++++------- {testApp/ca => testCa}/testCaProvider.db | 0 8 files changed, 68 insertions(+), 25 deletions(-) delete mode 100644 testApp/ca/Makefile create mode 100644 testCa/Makefile create mode 100644 testCa/rtemsTestHarness.c rename {testApp/ca => testCa}/testCaProvider.cpp (96%) rename {testApp/ca => testCa}/testCaProvider.db (100%) diff --git a/Makefile b/Makefile index 71c65d9..9914eca 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,11 @@ pvtoolsSrc_DEPEND_DIRS = src src/ca DIRS += testApp testApp_DEPEND_DIRS = src + +DIRS += testCa +testCa_DEPEND_DIRS = src + + DIRS += examples examples_DEPEND_DIRS += src src/ca diff --git a/testApp/Makefile b/testApp/Makefile index d23979e..4d59b61 100644 --- a/testApp/Makefile +++ b/testApp/Makefile @@ -10,11 +10,10 @@ USR_CPPFLAGS += -I$(TOP)/src/remoteClient PVACCESS_TEST = $(TOP)/testApp -PROD_LIBS += pvAccess pvAccessCA pvData Com +PROD_LIBS += pvAccess pvData Com include $(PVACCESS_TEST)/utils/Makefile include $(PVACCESS_TEST)/remote/Makefile -include $(PVACCESS_TEST)/ca/Makefile # pvAccessAllTests runs all the test programs in a known working order. testHarness_SRCS += pvAccessAllTests.c diff --git a/testApp/ca/Makefile b/testApp/ca/Makefile deleted file mode 100644 index 5c38178..0000000 --- a/testApp/ca/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# This is a Makefile fragment, see ../Makefile - -SRC_DIRS += $(PVACCESS_TEST)/ca - -TESTPROD_HOST += testCaProvider -testCaProvider += testCaProvider.cpp -testHarness_SRCS += testCaProvider.cpp -TESTS += testCaProvider diff --git a/testApp/pvAccessAllTests.c b/testApp/pvAccessAllTests.c index 7ec5c33..c4932fc 100644 --- a/testApp/pvAccessAllTests.c +++ b/testApp/pvAccessAllTests.c @@ -19,9 +19,6 @@ int testInetAddressUtils(void); int testCodec(void); int testChannelAccess(void); -/* ca */ -int testCaProvider(void); - void pvAccessAllTests(void) { testHarness(); @@ -34,8 +31,6 @@ void pvAccessAllTests(void) /* remote */ runTest(testCodec); runTest(testChannelAccess); - /* ca */ - runTest(testCaProvider); epicsExit(0); /* Trigger test harness */ } diff --git a/testCa/Makefile b/testCa/Makefile new file mode 100644 index 0000000..905b2b5 --- /dev/null +++ b/testCa/Makefile @@ -0,0 +1,33 @@ +# Makefile for the ca tests + +TOP = .. +include $(TOP)/configure/CONFIG + +USR_CPPFLAGS += -I$(TOP)/src/ca + +CAPROVIDER_TEST = $(TOP)/testCa + +PROD_LIBS += pvAccess pvAccessCA pvData Com + +TESTPROD_HOST += testCaProvider +testCaProvider_SRCS += testCaProvider.cpp +testHarness_SRCS += testCaProvider.cpp +TESTS += testCaProvider + + +# Name the application caTestHarness +caTestHarness_SRCS = $(testHarness_SRCS) + +# Build for vxWorks +PROD_vxWorks = caTestHarness +TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + +# Build for RTEMS, with harness code & configuration +PROD_RTEMS += caTestHarness +caTestHarness_SRCS_RTEMS += rtemsTestHarness.c +TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + +# Build test scripts for hosts +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES diff --git a/testCa/rtemsTestHarness.c b/testCa/rtemsTestHarness.c new file mode 100644 index 0000000..9c09d22 --- /dev/null +++ b/testCa/rtemsTestHarness.c @@ -0,0 +1,12 @@ +/* + * Copyright information and license terms for this software can be + * found in the file LICENSE that is included with the distribution + */ + +extern void pvCaAllTests(void); + +int main(int argc, char **argv) +{ + pvCaAllTests(); /* calls epicsExit(0) */ + return 0; +} diff --git a/testApp/ca/testCaProvider.cpp b/testCa/testCaProvider.cpp similarity index 96% rename from testApp/ca/testCaProvider.cpp rename to testCa/testCaProvider.cpp index 38a879c..f62482d 100644 --- a/testApp/ca/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -7,14 +7,14 @@ #include #include -#include #include #include #include -#include +#include #include + #include #include #include @@ -355,14 +355,16 @@ void TestChannelPut::connect() void TestChannelPut::waitConnect(double timeout) { if(waitForConnect.wait(timeout)) return; - throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::waitConnect failed "); + throw std::runtime_error(testChannel->getChannelName() + + " TestChannelPut::waitConnect failed "); } void TestChannelPut::put(string const & value) { PVFieldPtr pvField(pvStructure->getSubField("value")); - if(!pvField) throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put no value "); + if(!pvField) throw std::runtime_error(testChannel->getChannelName() + + " TestChannelPut::put no value "); FieldConstPtr field(pvField->getField()); Type type(field->getType()); if(type==scalar) { @@ -372,7 +374,8 @@ void TestChannelPut::put(string const & value) channelPut->put(pvStructure,bitSet); return; } - throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put not supported type"); + throw std::runtime_error(testChannel->getChannelName() + + " TestChannelPut::put not supported type"); } class TestChannelMonitor; @@ -476,7 +479,8 @@ void TestChannelMonitor::unlisten(MonitorPtr const & monitor) void TestChannelMonitor::connect() { channelMonitor = testChannel->getChannel()->createMonitor(shared_from_this(),pvRequest); - if(!channelMonitor) throw std::runtime_error(testChannel->getChannelName() + " TestChannelMonitor::connect failed "); + if(!channelMonitor) throw std::runtime_error(testChannel->getChannelName() + + " TestChannelMonitor::connect failed "); } void TestChannelMonitor::waitConnect(double timeout) @@ -485,7 +489,8 @@ void TestChannelMonitor::waitConnect(double timeout) channelMonitor->start(); return; } - throw std::runtime_error(testChannel->getChannelName() + " TestChannelMonitor::waitConnect failed "); + throw std::runtime_error(testChannel->getChannelName() + + " TestChannelMonitor::waitConnect failed "); } void TestChannelMonitor::stopEvents() @@ -636,19 +641,20 @@ void TestIoc::start() void TestIoc::run() { - system("softIoc -d ../ca/testCaProvider.db"); + system("softIoc -d ../testCaProvider.db"); } MAIN(testCaProvider) { + + TestIocPtr testIoc(new TestIoc()); + testIoc->start(); testPlan(11); testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); try{ - TestIocPtr testIoc(new TestIoc()); - testIoc->start(); if(!channelProvider) { throw std::runtime_error(" provider ca not registered"); } @@ -672,6 +678,7 @@ MAIN(testCaProvider) scalarout = TestClient::create(channelName,pvRequest); testOk(scalarout!=NULL,"DBRexit not null"); scalarout->put("1"); + scalarout->stopEvents(); }catch(std::exception& e){ PRINT_EXCEPTION(e); testAbort("Unexpected Exception: %s", e.what()); diff --git a/testApp/ca/testCaProvider.db b/testCa/testCaProvider.db similarity index 100% rename from testApp/ca/testCaProvider.db rename to testCa/testCaProvider.db From 055897215712a2ba16505c51502f31a48788d092 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Thu, 10 May 2018 07:03:00 -0400 Subject: [PATCH 11/36] added more tests; added path for softIoc; --- testCa/testCaProvider.cpp | 144 +++++++++++++++++++++++++++++++++----- 1 file changed, 126 insertions(+), 18 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index f62482d..ad156e4 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -40,7 +40,7 @@ using namespace epics::pvData; using namespace epics::pvAccess; using namespace epics::pvAccess::ca; -using std::string; +using namespace std; class TestChannel; typedef std::tr1::shared_ptr TestChannelPtr; @@ -374,6 +374,37 @@ void TestChannelPut::put(string const & value) channelPut->put(pvStructure,bitSet); return; } + if(type==scalarArray) { + PVScalarArrayPtr pvScalarArray(std::tr1::static_pointer_cast(pvField)); + std::vector values; + size_t pos = 0; + size_t n = 1; + while(true) + { + size_t offset = value.find(" ",pos); + if(offset==string::npos) { + values.push_back(value.substr(pos)); + break; + } + values.push_back(value.substr(pos,offset-pos)); + pos = offset+1; + n++; + } + pvScalarArray->setLength(n); + getConvert()->fromStringArray(pvScalarArray,0,n,values,0); + bitSet->set(pvField->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + if(type==structure) { + PVScalarPtr pvScalar(pvStructure->getSubField("value.index")); + if(pvScalar) { + getConvert()->fromString(pvScalar,value); + bitSet->set(pvScalar->getFieldOffset()); + channelPut->put(pvStructure,bitSet); + return; + } + } throw std::runtime_error(testChannel->getChannelName() + " TestChannelPut::put not supported type"); } @@ -564,6 +595,9 @@ void TestClient::getDone( testOk(pvStructure->getSubField("value")!=NULL,"value not null"); testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null"); testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null"); + std::cout << testChannel->getChannelName() + " TestClient::getDone" + << " bitSet " << *bitSet + << " pvStructure\n" << pvStructure << "\n"; waitForGet.signal(); } @@ -583,7 +617,9 @@ void TestClient::monitorEvent( void TestClient::get() { +cout << "TestClient::get() calling get\n"; testChannelGet->get(); +cout << "TestClient::get() calling waitGet\n"; waitGet(5.0); } @@ -641,7 +677,7 @@ void TestIoc::start() void TestIoc::run() { - system("softIoc -d ../testCaProvider.db"); + system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -d ../testCaProvider.db"); } MAIN(testCaProvider) @@ -649,7 +685,7 @@ MAIN(testCaProvider) TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(11); + testPlan(56); testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); @@ -659,26 +695,98 @@ MAIN(testCaProvider) throw std::runtime_error(" provider ca not registered"); } string channelName; - channelName = "DBRdoubleout"; string request("value,alarm,timeStamp"); PVStructurePtr pvRequest(createRequest(request)); - TestClientPtr scalarout(TestClient::create(channelName,pvRequest)); - testOk(scalarout!=NULL,"DBRdoubleout not null"); - scalarout->put("1.5"); - scalarout->get(); - scalarout->stopEvents(); + TestClientPtr client; + + channelName = "DBRlongout"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("5"); + client->get(); + client->stopEvents(); + channelName = "DBRdoubleout"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1.5"); + client->get(); + client->stopEvents(); channelName = "DBRstringout"; - scalarout = TestClient::create(channelName,pvRequest); - testOk(scalarout!=NULL,"DBRstringout not null"); - scalarout->put("test"); - scalarout->get(); - scalarout->stopEvents(); + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("test"); + client->get(); + client->stopEvents(); + channelName = "DBRbyteArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + client->stopEvents(); + channelName = "DBRshortArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + client->stopEvents(); + channelName = "DBRintArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + channelName = "DBRubyteArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + client->stopEvents(); + channelName = "DBRushortArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + client->stopEvents(); + channelName = "DBRuintArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + channelName = "DBRfloatArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + client->stopEvents(); + channelName = "DBRdoubleArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1 2 3"); + client->get(); + client->stopEvents(); + channelName = "DBRstringArray"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("aa bb cc"); + client->get(); + client->stopEvents(); + channelName = "DBRmbbout"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("2"); + client->get(); + client->stopEvents(); + channelName = "DBRbinaryout"; + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1"); + client->get(); + client->stopEvents(); // put to record that makes IOC exit channelName = "DBRexit"; - scalarout = TestClient::create(channelName,pvRequest); - testOk(scalarout!=NULL,"DBRexit not null"); - scalarout->put("1"); - scalarout->stopEvents(); + client = TestClient::create(channelName,pvRequest); + if(!client) throw std::runtime_error(channelName + " client null"); + client->put("1"); + client->stopEvents(); }catch(std::exception& e){ PRINT_EXCEPTION(e); testAbort("Unexpected Exception: %s", e.what()); From 911108a554a3da378742ffdabbbd7bb179da03d7 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 11 May 2018 06:57:28 -0400 Subject: [PATCH 12/36] attempt to make RTEMS work --- testCa/qemuRunTest.sh | 1 + 1 file changed, 1 insertion(+) create mode 100755 testCa/qemuRunTest.sh diff --git a/testCa/qemuRunTest.sh b/testCa/qemuRunTest.sh new file mode 100755 index 0000000..b9cd499 --- /dev/null +++ b/testCa/qemuRunTest.sh @@ -0,0 +1 @@ +qemu-system-i386 --kernel O.RTEMS-pc386/rtemsTestHarness -netdev user,id=mynet0 -device ne2k_isa,netdev=mynet0 -redir tcp:5075::5075 -redir udp:5076::5076 -m 1024 --no-reboot -curses From ded28bbd3b3fa32e8ce596f6b68b25e4db6d440a Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 11 May 2018 11:00:10 -0400 Subject: [PATCH 13/36] try to get more travis build to work --- src/ca/Makefile | 3 +-- testCa/Makefile | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ca/Makefile b/src/ca/Makefile index d2b4d6d..3a4cf10 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -2,8 +2,7 @@ TOP = ../.. include $(TOP)/configure/CONFIG LIBRARY += pvAccessCA -pvAccessCA_LIBS += ca pvAccess pvData -LIB_LIBS += Com +pvAccessCA_LIBS += pvAccess pvData ca Com SHRLIB_VERSION ?= $(EPICS_PVA_MAJOR_VERSION).$(EPICS_PVA_MINOR_VERSION).$(EPICS_PVA_MAINTENANCE_VERSION) diff --git a/testCa/Makefile b/testCa/Makefile index 905b2b5..3fcb2ed 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -7,7 +7,7 @@ USR_CPPFLAGS += -I$(TOP)/src/ca CAPROVIDER_TEST = $(TOP)/testCa -PROD_LIBS += pvAccess pvAccessCA pvData Com +PROD_LIBS += pvAccess pvAccessCA pvData ca Com TESTPROD_HOST += testCaProvider testCaProvider_SRCS += testCaProvider.cpp From 00c56ed12e69eee5df4ec8d435c3fb08b093d3bd Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 11 May 2018 13:20:29 -0400 Subject: [PATCH 14/36] another attempt to get RTEMS to build --- testCa/Makefile | 1 + testCa/pvCaAllTests.c | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 testCa/pvCaAllTests.c diff --git a/testCa/Makefile b/testCa/Makefile index 3fcb2ed..c2700f0 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -13,6 +13,7 @@ TESTPROD_HOST += testCaProvider testCaProvider_SRCS += testCaProvider.cpp testHarness_SRCS += testCaProvider.cpp TESTS += testCaProvider +testHarness_SRCS += pvCaAllTests.c # Name the application caTestHarness diff --git a/testCa/pvCaAllTests.c b/testCa/pvCaAllTests.c new file mode 100644 index 0000000..43d3705 --- /dev/null +++ b/testCa/pvCaAllTests.c @@ -0,0 +1,18 @@ +/* + * + */ + +#include +#include +#include +#include + +int testCaProvider(void); + +void pvCaAllTests(void) +{ + testHarness(); + runTest(testCaProvider); + + epicsExit(0); /* Trigger test harness */ +} From 5bc12317e41d56930a0e9243a77756d79ed25645 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Sat, 12 May 2018 12:00:56 -0400 Subject: [PATCH 15/36] more info if ioc not started --- testCa/testCaProvider.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index ad156e4..f8aa96a 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -677,7 +677,19 @@ void TestIoc::start() void TestIoc::run() { - system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -d ../testCaProvider.db"); + char * base; + base = getenv("EPICS_BASE"); + if(base==NULL) throw std::runtime_error("TestIoc::run $EPICS_BASE not defined"); + char * arch; + arch = getenv("EPICS_HOST_ARCH"); + if(arch==NULL) throw std::runtime_error("TestIoc::run $$EPICS_HOST_ARCH not defined"); + if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -d ../testCaProvider.db")!=0) { + string message(base); + message += "/bin/"; + message += arch; + message += "/softIoc -d ../testCaProvider.db not started"; + throw std::runtime_error(message); + } } MAIN(testCaProvider) From 9b305e5e2bd3b3e2fbc5c030b291ef171ca68fed Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Tue, 15 May 2018 09:11:05 -0400 Subject: [PATCH 16/36] try to make travis BRBASE builds succeed --- testCa/Makefile | 2 ++ testCa/testCaProvider.cpp | 16 ++-------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/testCa/Makefile b/testCa/Makefile index c2700f0..4300613 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -15,6 +15,8 @@ testHarness_SRCS += testCaProvider.cpp TESTS += testCaProvider testHarness_SRCS += pvCaAllTests.c +# testCaProvider needs EPICS_HOST_ARCH set in the environment +export EPICS_HOST_ARCH # Name the application caTestHarness caTestHarness_SRCS = $(testHarness_SRCS) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index f8aa96a..4f623aa 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include @@ -25,17 +25,6 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include using namespace epics::pvData; using namespace epics::pvAccess; @@ -800,8 +789,7 @@ MAIN(testCaProvider) client->put("1"); client->stopEvents(); }catch(std::exception& e){ - PRINT_EXCEPTION(e); - testAbort("Unexpected Exception: %s", e.what()); + testFail("caught un-expected exception: %s", e.what()); } return testDone();; } From d155487ddad6f255051f8515206834ba3cb5594f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 May 2018 13:13:28 -0400 Subject: [PATCH 17/36] testCa: Remove extraneous bi record, fix bo.OUT link --- testCa/testCaProvider.db | 6 ------ 1 file changed, 6 deletions(-) diff --git a/testCa/testCaProvider.db b/testCa/testCaProvider.db index d66e322..647c0c5 100644 --- a/testCa/testCaProvider.db +++ b/testCa/testCaProvider.db @@ -131,12 +131,6 @@ record(stringin,"DBRstringin") { record(bo,"DBRbinaryout") { field(DESC, "bo") - field(OUT, "DBRbi.VAL PP NMS") - field(ZNAM,"zero") - field(ONAM,"one") -} -record(bi,"DBRbinaryin") { - field(DESC, "bi") field(ZNAM,"zero") field(ONAM,"one") } From 3e0748d488f308e986030cc035f9051580c93425 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 26 May 2018 13:23:04 -0400 Subject: [PATCH 18/36] testCa: Adjust test output The DEBUG flag must be 0 when running under a test harness to remove extraneous output which can confuse the output parser. This commit also converts some throws into tests, and adds more test diagnostic messages. --- testCa/testCaProvider.cpp | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 4f623aa..52c37ac 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -26,6 +26,9 @@ #include #include +// DEBUG must be 0 to run under the automated test harness +#define DEBUG 0 + using namespace epics::pvData; using namespace epics::pvAccess; using namespace epics::pvAccess::ca; @@ -584,7 +587,7 @@ void TestClient::getDone( testOk(pvStructure->getSubField("value")!=NULL,"value not null"); testOk(pvStructure->getSubField("timeStamp")!=NULL,"timeStamp not null"); testOk(pvStructure->getSubField("alarm")!=NULL,"alarm not null"); - std::cout << testChannel->getChannelName() + " TestClient::getDone" + if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::getDone" << " bitSet " << *bitSet << " pvStructure\n" << pvStructure << "\n"; waitForGet.signal(); @@ -599,35 +602,38 @@ void TestClient::monitorEvent( PVStructure::shared_pointer const & pvStructure, BitSet::shared_pointer const & bitSet) { - std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" + if (DEBUG) std::cout << testChannel->getChannelName() + " TestClient::monitorEvent" << " bitSet " << *bitSet << " pvStructure\n" << pvStructure << "\n"; } void TestClient::get() { -cout << "TestClient::get() calling get\n"; + testDiag("TestClient::get %s", + testChannel->getChannelName().c_str()); testChannelGet->get(); -cout << "TestClient::get() calling waitGet\n"; + if (DEBUG) cout << "TestClient::get() calling waitGet\n"; waitGet(5.0); } void TestClient::waitGet(double timeout) { - if(waitForGet.wait(timeout)) return; - throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitGet failed "); + testOk(waitForGet.wait(timeout), + "waitGet(%s) succeeded", testChannel->getChannelName().c_str()); } void TestClient::put(string const & value) { + testDiag("TestClient::put %s := %s", + testChannel->getChannelName().c_str(), value.c_str()); testChannelPut->put(value); waitPut(5.0); } void TestClient::waitPut(double timeout) { - if(waitForPut.wait(timeout)) return; - throw std::runtime_error(testChannel->getChannelName() + " TestClient::waitPut failed "); + testOk(waitForPut.wait(timeout), + "waitPut(%s) succeeded", testChannel->getChannelName().c_str()); } void TestClient::stopEvents() @@ -686,7 +692,7 @@ MAIN(testCaProvider) TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(56); + testPlan(84); testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); From 872004f75ff0aff9997ba21f16ec7e117d7eeeae Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 00:31:32 -0400 Subject: [PATCH 19/36] testCa: Run IOC using dbUnitTest API where possible Essential for VxWorks and RTEMS which don't support system(). Build embedded IOC tests only against Base-3.15 and later. --- testCa/Makefile | 43 ++++++++++++++++++++++------------- testCa/testCaProvider.cpp | 47 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 18 deletions(-) diff --git a/testCa/Makefile b/testCa/Makefile index 4300613..da17a17 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -3,34 +3,45 @@ TOP = .. include $(TOP)/configure/CONFIG +# Need access to caProviderPvt.h USR_CPPFLAGS += -I$(TOP)/src/ca -CAPROVIDER_TEST = $(TOP)/testCa - -PROD_LIBS += pvAccess pvAccessCA pvData ca Com +PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS) TESTPROD_HOST += testCaProvider testCaProvider_SRCS += testCaProvider.cpp -testHarness_SRCS += testCaProvider.cpp +caTestHarness_SRCS += testCaProvider.cpp TESTS += testCaProvider -testHarness_SRCS += pvCaAllTests.c +ifdef BASE_3_15 + testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp + REGRDDFLAGS = -l +else + # testCaProvider needs EPICS_HOST_ARCH set in the environment + export EPICS_HOST_ARCH +endif -# testCaProvider needs EPICS_HOST_ARCH set in the environment -export EPICS_HOST_ARCH +ifdef BASE_3_15 + # Embedded OSes need dbUnitTest, Base-3.15 and higher only -# Name the application caTestHarness -caTestHarness_SRCS = $(testHarness_SRCS) + # The test collection is caTestHarness + caTestHarness_SRCS += pvCaAllTests.c -# Build for vxWorks -PROD_vxWorks = caTestHarness -TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + # Build for vxWorks + PROD_vxWorks = caTestHarness + TESTSPEC_vxWorks = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests -# Build for RTEMS, with harness code & configuration -PROD_RTEMS += caTestHarness -caTestHarness_SRCS_RTEMS += rtemsTestHarness.c -TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests + # Build for RTEMS, with harness code & configuration + PROD_RTEMS += caTestHarness + caTestHarness_SRCS_RTEMS += rtemsTestHarness.c + TESTSPEC_RTEMS = caTestHarness.$(MUNCH_SUFFIX); pvCaAllTests +endif # Build test scripts for hosts TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES + +ifdef BASE_3_15 + $(COMMON_DIR)/testIoc.dbd: $(EPICS_BASE)/dbd/softIoc.dbd + $(CP) $< $@ +endif diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 52c37ac..abd4822 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -13,7 +13,21 @@ #include #include +#include + #if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) + #define USE_DBUNITTEST + // USE_TYPED_RSET prevents deprecation warnings + #define USE_TYPED_RSET + #define EXIT_TESTS 0 + #include + #include + #include + + extern "C" int testIoc_registerRecordDeviceDriver(struct dbBase *pbase); +#else + #define EXIT_TESTS 1 +#endif #include #include @@ -651,8 +665,11 @@ public: virtual void run(); static TestIocPtr create(); void start(); + void shutdown(); private: +#ifndef USE_DBUNITTEST std::auto_ptr thread; +#endif }; TestIocPtr TestIoc::create() @@ -662,16 +679,30 @@ TestIocPtr TestIoc::create() void TestIoc::start() { +#ifdef USE_DBUNITTEST + testdbPrepare(); + testdbReadDatabase("testIoc.dbd", NULL, NULL); + testIoc_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase("testCaProvider.db", NULL, NULL); + eltc(0); + testIocInitOk(); + eltc(1); +#else thread = std::auto_ptr(new epicsThread( *this, "testIoc", epicsThreadGetStackSize(epicsThreadStackSmall), epicsThreadPriorityLow)); thread->start(); +#endif } void TestIoc::run() { +#ifndef USE_DBUNITTEST + // Base-3.14 doesn't provide the dbUnitTest APIs. + // This code only works on workstation targets, it runs the + // softIoc from Base as a separate process, using system(). char * base; base = getenv("EPICS_BASE"); if(base==NULL) throw std::runtime_error("TestIoc::run $EPICS_BASE not defined"); @@ -685,6 +716,15 @@ void TestIoc::run() message += "/softIoc -d ../testCaProvider.db not started"; throw std::runtime_error(message); } +#endif +} + +void TestIoc::shutdown() +{ +#ifdef USE_DBUNITTEST + testIocShutdownOk(); + testdbCleanup(); +#endif } MAIN(testCaProvider) @@ -692,12 +732,12 @@ MAIN(testCaProvider) TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(84); + testPlan(84 + EXIT_TESTS); testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); - ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); try{ + ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); if(!channelProvider) { throw std::runtime_error(" provider ca not registered"); } @@ -788,15 +828,18 @@ MAIN(testCaProvider) client->put("1"); client->get(); client->stopEvents(); +#ifndef USE_DBUNITTEST // put to record that makes IOC exit channelName = "DBRexit"; client = TestClient::create(channelName,pvRequest); if(!client) throw std::runtime_error(channelName + " client null"); client->put("1"); client->stopEvents(); +#endif }catch(std::exception& e){ testFail("caught un-expected exception: %s", e.what()); } + testIoc->shutdown(); return testDone();; } From db15689e1cc159286045c44d55e8ebbdc80e4eb3 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 00:33:18 -0400 Subject: [PATCH 20/36] testCa: Use the softIoc's built-in exit subrecord --- testCa/testCaProvider.cpp | 4 ++-- testCa/testCaProvider.db | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index abd4822..fc1be44 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -709,7 +709,7 @@ void TestIoc::run() char * arch; arch = getenv("EPICS_HOST_ARCH"); if(arch==NULL) throw std::runtime_error("TestIoc::run $$EPICS_HOST_ARCH not defined"); - if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -d ../testCaProvider.db")!=0) { + if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) { string message(base); message += "/bin/"; message += arch; @@ -830,7 +830,7 @@ MAIN(testCaProvider) client->stopEvents(); #ifndef USE_DBUNITTEST // put to record that makes IOC exit - channelName = "DBRexit"; + channelName = "test:exit"; client = TestClient::create(channelName,pvRequest); if(!client) throw std::runtime_error(channelName + " client null"); client->put("1"); diff --git a/testCa/testCaProvider.db b/testCa/testCaProvider.db index 647c0c5..04cdcf7 100644 --- a/testCa/testCaProvider.db +++ b/testCa/testCaProvider.db @@ -39,12 +39,6 @@ record(calc, "DBRcalcin") field(LLSV, "MAJOR") } -record(sub, "DBRexit") -{ - field(DESC, "exit") - field(SNAM, "exit") -} - record(longin,"DBRlongin") { field(DESC, "longin") field(EGU, "volts") From c57b1352359eb83250405ac420d2244986bbe4ba Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 01:14:47 -0400 Subject: [PATCH 21/36] testCa: Configure CA to search localhost only --- testCa/testCaProvider.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index fc1be44..3437d61 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -709,6 +709,8 @@ void TestIoc::run() char * arch; arch = getenv("EPICS_HOST_ARCH"); if(arch==NULL) throw std::runtime_error("TestIoc::run $$EPICS_HOST_ARCH not defined"); + setenv("EPICS_CA_ADDR_LIST", "localhost", 1); + setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1); if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) { string message(base); message += "/bin/"; From 2cddd3359e3c538e8ada837175e59aa411599fb6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 01:25:25 -0400 Subject: [PATCH 22/36] testCa: Replace duplicated test code with a subroutine Also uses testAbort() instead of throwing in the event of a problem. --- testCa/testCaProvider.cpp | 141 ++++++++++++-------------------------- 1 file changed, 44 insertions(+), 97 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 3437d61..15dfc16 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -15,7 +15,7 @@ #include #include - #if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) +#if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) #define USE_DBUNITTEST // USE_TYPED_RSET prevents deprecation warnings #define USE_TYPED_RSET @@ -729,119 +729,66 @@ void TestIoc::shutdown() #endif } +void checkClient(const string &channelName, const string &putValue) +{ + string request("value,alarm,timeStamp"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr client = TestClient::create(channelName,pvRequest); + if (!client) + testAbort("NULL client for %s", channelName.c_str()); + client->put(putValue); + client->get(); + client->stopEvents(); +} + MAIN(testCaProvider) { + testPlan(84 + EXIT_TESTS); TestIocPtr testIoc(new TestIoc()); testIoc->start(); - testPlan(84 + EXIT_TESTS); + testDiag("===Test caProvider==="); CAClientFactory::start(); ChannelProviderRegistry::shared_pointer reg(ChannelProviderRegistry::clients()); - try{ + try { ChannelProvider::shared_pointer channelProvider(reg->getProvider("ca")); - if(!channelProvider) { - throw std::runtime_error(" provider ca not registered"); - } - string channelName; - string request("value,alarm,timeStamp"); - PVStructurePtr pvRequest(createRequest(request)); - TestClientPtr client; + if (!channelProvider) + testAbort("Channel provider 'ca' not registered"); + + checkClient("DBRlongout", "5"); + checkClient("DBRdoubleout", "1.5"); + checkClient("DBRstringout", "test"); + checkClient("DBRbyteArray", "1 2 3"); + checkClient("DBRshortArray", "1 2 3"); + checkClient("DBRintArray", "1 2 3"); + checkClient("DBRubyteArray", "1 2 3"); + checkClient("DBRushortArray", "1 2 3"); + checkClient("DBRuintArray", "1 2 3"); + checkClient("DBRfloatArray", "1 2 3"); + checkClient("DBRdoubleArray", "1 2 3"); + checkClient("DBRstringArray", "aa bb cc"); + checkClient("DBRmbbout", "2"); + checkClient("DBRbinaryout", "1"); - channelName = "DBRlongout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("5"); - client->get(); - client->stopEvents(); - channelName = "DBRdoubleout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1.5"); - client->get(); - client->stopEvents(); - channelName = "DBRstringout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("test"); - client->get(); - client->stopEvents(); - channelName = "DBRbyteArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRshortArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRintArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - channelName = "DBRubyteArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRushortArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRuintArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - channelName = "DBRfloatArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRdoubleArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1 2 3"); - client->get(); - client->stopEvents(); - channelName = "DBRstringArray"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("aa bb cc"); - client->get(); - client->stopEvents(); - channelName = "DBRmbbout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("2"); - client->get(); - client->stopEvents(); - channelName = "DBRbinaryout"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); - client->put("1"); - client->get(); - client->stopEvents(); #ifndef USE_DBUNITTEST // put to record that makes IOC exit - channelName = "test:exit"; - client = TestClient::create(channelName,pvRequest); - if(!client) throw std::runtime_error(channelName + " client null"); + string channelName = "test:exit"; + string request("value"); + PVStructurePtr pvRequest(createRequest(request)); + TestClientPtr client = TestClient::create(channelName,pvRequest); + if (!client) + testAbort("NULL client for %s", channelName.c_str()); client->put("1"); client->stopEvents(); #endif - }catch(std::exception& e){ - testFail("caught un-expected exception: %s", e.what()); } + catch (std::exception& e) { + testAbort("caught un-expected exception: %s", e.what()); + } + testIoc->shutdown(); + return testDone();; } From eacfbeeea8a73822b16c62e5b948941dc10c46de Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 15:04:19 -0400 Subject: [PATCH 23/36] Building testCa requires src/ca --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9914eca..249e8b3 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ testApp_DEPEND_DIRS = src DIRS += testCa -testCa_DEPEND_DIRS = src +testCa_DEPEND_DIRS = src src/ca DIRS += examples From 206d080de18b1815295318daf5ac5eef4ed83978 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 15:05:54 -0400 Subject: [PATCH 24/36] testCa: Fix version check for Base-3.14 builds --- testCa/testCaProvider.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 15dfc16..8b5f439 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -15,7 +15,8 @@ #include #include -#if defined(VERSION_INT) && EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) +#ifdef EPICS_VERSION_INT + #if EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) #define USE_DBUNITTEST // USE_TYPED_RSET prevents deprecation warnings #define USE_TYPED_RSET @@ -25,8 +26,11 @@ #include extern "C" int testIoc_registerRecordDeviceDriver(struct dbBase *pbase); -#else - #define EXIT_TESTS 1 + #endif +#endif + +#ifndef EXIT_TESTS + #define EXIT_TESTS 1 #endif #include From 6681bdc4c6083da3ebc9ae2c1675d3a251d6857c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 15:21:54 -0500 Subject: [PATCH 25/36] testCa: Embedded IOCs need Base-3.16.2 or higher --- testCa/Makefile | 8 ++++---- testCa/testCaProvider.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/testCa/Makefile b/testCa/Makefile index da17a17..90ab926 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -12,7 +12,7 @@ TESTPROD_HOST += testCaProvider testCaProvider_SRCS += testCaProvider.cpp caTestHarness_SRCS += testCaProvider.cpp TESTS += testCaProvider -ifdef BASE_3_15 +ifdef BASE_3_16 testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp REGRDDFLAGS = -l else @@ -20,8 +20,8 @@ else export EPICS_HOST_ARCH endif -ifdef BASE_3_15 - # Embedded OSes need dbUnitTest, Base-3.15 and higher only +ifdef BASE_3_16 + # Embedded OSes need Base-3.16.2 or higher to pass tests # The test collection is caTestHarness caTestHarness_SRCS += pvCaAllTests.c @@ -41,7 +41,7 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES -ifdef BASE_3_15 +ifdef BASE_3_16 $(COMMON_DIR)/testIoc.dbd: $(EPICS_BASE)/dbd/softIoc.dbd $(CP) $< $@ endif diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 8b5f439..9b3c1e2 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -16,7 +16,7 @@ #include #ifdef EPICS_VERSION_INT - #if EPICS_VERSION_INT >= VERSION_INT(3,15,0,1) + #if EPICS_VERSION_INT >= VERSION_INT(3,16,2,0) #define USE_DBUNITTEST // USE_TYPED_RSET prevents deprecation warnings #define USE_TYPED_RSET @@ -704,8 +704,9 @@ void TestIoc::start() void TestIoc::run() { #ifndef USE_DBUNITTEST - // Base-3.14 doesn't provide the dbUnitTest APIs. - // This code only works on workstation targets, it runs the + // Base-3.14 doesn't provide the dbUnitTest APIs, and the CA + // tests with an embedded IOC fail with a Base before 3.16.2. + // This version only works on workstation targets, it runs the // softIoc from Base as a separate process, using system(). char * base; base = getenv("EPICS_BASE"); From 7bf404913a22dc6bb013a6279c7122e90982eb2a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 27 May 2018 15:35:04 -0500 Subject: [PATCH 26/36] testCa: Move IOC environment checks & configuration from run() to start() This lets us use testAbort() instead of throw to flag errors. --- testCa/testCaProvider.cpp | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/testCa/testCaProvider.cpp b/testCa/testCaProvider.cpp index 9b3c1e2..5a1664f 100644 --- a/testCa/testCaProvider.cpp +++ b/testCa/testCaProvider.cpp @@ -673,6 +673,8 @@ public: private: #ifndef USE_DBUNITTEST std::auto_ptr thread; + const char *base; + const char *arch; #endif }; @@ -692,7 +694,16 @@ void TestIoc::start() testIocInitOk(); eltc(1); #else - thread = std::auto_ptr(new epicsThread( + base = getenv("EPICS_BASE"); + if (!base) + testAbort("Environment variable $EPICS_BASE not defined"); + arch = getenv("EPICS_HOST_ARCH"); + if (!arch) + testAbort("Environment variable $EPICS_HOST_ARCH not defined"); + setenv("EPICS_CA_ADDR_LIST", "localhost", 1); + setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1); + + thread = std::auto_ptr(new epicsThread( *this, "testIoc", epicsThreadGetStackSize(epicsThreadStackSmall), @@ -708,14 +719,6 @@ void TestIoc::run() // tests with an embedded IOC fail with a Base before 3.16.2. // This version only works on workstation targets, it runs the // softIoc from Base as a separate process, using system(). - char * base; - base = getenv("EPICS_BASE"); - if(base==NULL) throw std::runtime_error("TestIoc::run $EPICS_BASE not defined"); - char * arch; - arch = getenv("EPICS_HOST_ARCH"); - if(arch==NULL) throw std::runtime_error("TestIoc::run $$EPICS_HOST_ARCH not defined"); - setenv("EPICS_CA_ADDR_LIST", "localhost", 1); - setenv("EPICS_CA_AUTO_ADDR_LIST", "NO", 1); if(system("$EPICS_BASE/bin/$EPICS_HOST_ARCH/softIoc -x test -d ../testCaProvider.db")!=0) { string message(base); message += "/bin/"; From 720335e537ed0da6132c5088d47ca1c901b130a1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 28 May 2018 11:05:42 -0500 Subject: [PATCH 27/36] testCa: export EPICS_HOST_ARCH unconditionally --- testCa/Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testCa/Makefile b/testCa/Makefile index 90ab926..6a5eba0 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -15,11 +15,11 @@ TESTS += testCaProvider ifdef BASE_3_16 testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp REGRDDFLAGS = -l -else - # testCaProvider needs EPICS_HOST_ARCH set in the environment - export EPICS_HOST_ARCH endif +# Ensure EPICS_HOST_ARCH is set in the environment +export EPICS_HOST_ARCH + ifdef BASE_3_16 # Embedded OSes need Base-3.16.2 or higher to pass tests From 30fcce2a9c0a1ca867bf730d3071dc1543861738 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 28 May 2018 11:07:13 -0500 Subject: [PATCH 28/36] testCa: Add missing source file to embedded builds --- testCa/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testCa/Makefile b/testCa/Makefile index 6a5eba0..9cfe317 100644 --- a/testCa/Makefile +++ b/testCa/Makefile @@ -9,13 +9,13 @@ USR_CPPFLAGS += -I$(TOP)/src/ca PROD_LIBS += pvAccess pvAccessCA pvData $(EPICS_BASE_IOC_LIBS) TESTPROD_HOST += testCaProvider -testCaProvider_SRCS += testCaProvider.cpp -caTestHarness_SRCS += testCaProvider.cpp +testCaProvider_SRCS += testCaProvider.cpp TESTS += testCaProvider ifdef BASE_3_16 - testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp + testCaProvider_SRCS += testIoc_registerRecordDeviceDriver.cpp REGRDDFLAGS = -l endif +caTestHarness_SRCS += $(testCaProvider_SRCS) # Ensure EPICS_HOST_ARCH is set in the environment export EPICS_HOST_ARCH @@ -23,7 +23,7 @@ export EPICS_HOST_ARCH ifdef BASE_3_16 # Embedded OSes need Base-3.16.2 or higher to pass tests - # The test collection is caTestHarness + # Code that runs all tests in the collection caTestHarness_SRCS += pvCaAllTests.c # Build for vxWorks From fe8184cf95c39a4579aad32164aaae7baca40eb7 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Fri, 8 Jun 2018 15:22:08 -0400 Subject: [PATCH 29/36] added aux thread to call ca_clear_subscription --- src/ca/Makefile | 1 + src/ca/caChannel.cpp | 195 ++++++----------------------------- src/ca/caChannel.h | 44 ++------ src/ca/caProvider.cpp | 49 ++++----- src/ca/caProviderPvt.h | 17 ++- src/ca/dbdToPv.h | 5 + src/ca/stopMonitorThread.cpp | 140 +++++++++++++++++++++++++ src/ca/stopMonitorThread.h | 56 ++++++++++ 8 files changed, 275 insertions(+), 232 deletions(-) create mode 100644 src/ca/stopMonitorThread.cpp create mode 100644 src/ca/stopMonitorThread.h diff --git a/src/ca/Makefile b/src/ca/Makefile index 3a4cf10..df0ae03 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 += stopMonitorThread.cpp pvAccessCA_SRCS += caProvider.cpp pvAccessCA_SRCS += caChannel.cpp pvAccessCA_SRCS += dbdToPv.cpp diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 6d7d187..7d2dc09 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -11,6 +11,7 @@ #include #include #include +#include "stopMonitorThread.h" #define epicsExportSharedSymbols #include "caChannel.h" @@ -84,18 +85,7 @@ void CAChannel::disconnected() if(DEBUG_LEVEL>0) { cout<< "CAChannel::disconnected " << channelName << endl; } - while(!putQueue.empty()) { - putQueue.front()->channelDisconnect(false); - putQueue.pop(); - } - while(!getQueue.empty()) { - getQueue.front()->channelDisconnect(false); - getQueue.pop(); - } - while(!monitorQueue.empty()) { - monitorQueue.front()->channelDisconnect(false); - monitorQueue.pop(); - } + ChannelRequester::shared_pointer req(channelRequester.lock()); if(req) { EXCEPTION_GUARD(req->channelStateChange( @@ -105,12 +95,12 @@ void CAChannel::disconnected() size_t CAChannel::num_instances; -CAChannel::CAChannel(std::string const & _channelName, - CAChannelProvider::shared_pointer const & _channelProvider, - ChannelRequester::shared_pointer const & _channelRequester) : - channelName(_channelName), - channelProvider(_channelProvider), - channelRequester(_channelRequester), +CAChannel::CAChannel(std::string const & channelName, + CAChannelProvider::shared_pointer const & channelProvider, + ChannelRequester::shared_pointer const & channelRequester) : + channelName(channelName), + channelProvider(channelProvider), + channelRequester(channelRequester), channelID(0), channelCreated(false) { @@ -149,6 +139,10 @@ CAChannel::~CAChannel() if(DEBUG_LEVEL>0) { cout << "CAChannel::~CAChannel() " << channelName << endl; } + { + Lock lock(requestsMutex); + if(!channelCreated) return; + } disconnectChannel(); } @@ -177,54 +171,6 @@ void CAChannel::disconnectChannel() cerr << mess << endl; } - - -void CAChannel::addChannelGet(const CAChannelGetPtr & get) -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::addChannelGet " << channelName << endl; - } - Lock lock(requestsMutex); - for(size_t i=0; i< getList.size(); ++i) { - if(!(getList[i].lock())) { - getList[i] = get; - return; - } - } - getList.push_back(get); -} - -void CAChannel::addChannelPut(const CAChannelPutPtr & put) -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::addChannelPut " << channelName << endl; - } - Lock lock(requestsMutex); - for(size_t i=0; i< putList.size(); ++i) { - if(!(putList[i].lock())) { - putList[i] = put; - return; - } - } - putList.push_back(put); -} - - -void CAChannel::addChannelMonitor(const CAChannelMonitorPtr & monitor) -{ - if(DEBUG_LEVEL>0) { - cout<< "CAChannel::addChannelMonitor " << channelName << endl; - } - Lock lock(requestsMutex); - for(size_t i=0; i< monitorList.size(); ++i) { - if(!(monitorList[i].lock())) { - monitorList[i] = monitor; - return; - } - } - monitorList.push_back(monitor); -} - chid CAChannel::getChannelID() { return channelID; @@ -426,11 +372,12 @@ void CAChannel::attachContext() std::tr1::static_pointer_cast(provider)->attachContext(); return; } - string mess("CAChannel::attachContext "); + string mess("CAChannel::attachContext provider does not exist "); mess += getChannelName(); throw std::runtime_error(mess); } + size_t CAChannelGet::num_instances; CAChannelGetPtr CAChannelGet::create( @@ -460,9 +407,6 @@ CAChannelGet::~CAChannelGet() } } -void CAChannelGet::channelCreated(const epics::pvData::Status& s,Channel::shared_pointer const & c) -{} - void CAChannelGet::activate() { ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); @@ -473,33 +417,12 @@ void CAChannelGet::activate() dbdToPv = DbdToPv::create(channel,pvRequest,getIO); pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); - channel->addChannelGet(shared_from_this()); EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } -void CAChannelGet::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - string mess("CAChannelGet::channelStateChange was called "); - mess += channel->getChannelName(); - throw std::runtime_error(mess); -} - std::string CAChannelGet::getRequesterName() { return "CAChannelGet";} -void CAChannelGet::channelDisconnect(bool destroy) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelGet::channelDisconnect " << channel->getChannelName() << endl; - } - ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); - if(!getRequester) return; - EXCEPTION_GUARD(getRequester->channelDisconnect(destroy);) - if(!destroy) channel->addChannelGet(shared_from_this()); -} - namespace { static void ca_get_handler(struct event_handler_args args) @@ -590,8 +513,6 @@ CAChannelPut::~CAChannelPut() } } -void CAChannelPut::channelCreated(const Status& status,Channel::shared_pointer const & c) -{} void CAChannelPut::activate() { @@ -608,32 +529,12 @@ void CAChannelPut::activate() std::string val = pvString->get(); if(val.compare("true")==0) block = true; } - channel->addChannelPut(shared_from_this()); EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } -void CAChannelPut::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - string mess("CAChannelPut::channelStateChange was called "); - mess += channel->getChannelName(); - throw std::runtime_error(mess); -} - std::string CAChannelPut::getRequesterName() { return "CAChannelPut";} -void CAChannelPut::channelDisconnect(bool destroy) -{ - if(DEBUG_LEVEL>0) { - cout << "CAChannelPut::channelDisconnect " << channel->getChannelName() << endl; - } - ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); - if(!putRequester) return; - EXCEPTION_GUARD(putRequester->channelDisconnect(destroy);) - if(!destroy) channel->addChannelPut(shared_from_this()); -} /* --------------- epics::pvAccess::ChannelPut --------------- */ @@ -781,6 +682,7 @@ public: void release(MonitorElementPtr const & monitorElement) { Lock guard(mutex); + if(!isStarted) return; if(monitorElementQueue.empty()) { string mess("CAChannelMonitor::release client error calling release "); throw std::runtime_error(mess); @@ -810,26 +712,22 @@ CAChannelMonitor::CAChannelMonitor( channel(channel), monitorRequester(monitorRequester), pvRequest(pvRequest), - isStarted(false) + isStarted(false), + stopMonitorThread(StopMonitorThread::get()) {} CAChannelMonitor::~CAChannelMonitor() { if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::~CAChannelMonitor() " << channel->getChannelName() << endl; + std::cout << "CAChannelMonitor::~CAChannelMonitor() " + << channel->getChannelName() + << " isStarted " << (isStarted ? "true" : "false") + << endl; } - if(!isStarted) return; - channel->attachContext(); - int result = ca_clear_subscription(eventID); - if (result == ECA_NORMAL) return; - string mess("CAChannelMonitor::~CAChannelMonitor() "); - mess += ca_message(result); - cerr << mess << endl; + if(isStarted) stop(); + stopMonitorThread->waitForNoEvents(); } -void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_pointer const & c) -{} - void CAChannelMonitor::activate() { MonitorRequester::shared_pointer requester(monitorRequester.lock()); @@ -853,44 +751,22 @@ void CAChannelMonitor::activate() } } monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); - channel->addChannelMonitor(shared_from_this()); EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } - -void CAChannelMonitor::channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState) -{ - string mess("CAChannelMonitor::channelStateChange was called "); - mess += channel->getChannelName(); - throw std::runtime_error(mess); -} - std::string CAChannelMonitor::getRequesterName() { return "CAChannelMonitor";} -void CAChannelMonitor::channelDisconnect(bool destroy) -{ - if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::channelDisconnect " << channel->getChannelName() << endl; - } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - EXCEPTION_GUARD(requester->channelDisconnect(destroy);) - if(!destroy) channel->addChannelMonitor(shared_from_this()); -} - void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { std::cout << "CAChannelMonitor::subscriptionEvent " << channel->getChannelName() << endl; } + if(!isStarted) return; MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; Status status = dbdToPv->getFromDBD(pvStructure,activeElement->changedBitSet,args); - if(status.isOK()) { if(monitorQueue->event(pvStructure,activeElement)) { @@ -943,22 +819,17 @@ epics::pvData::Status CAChannelMonitor::start() epics::pvData::Status CAChannelMonitor::stop() { if(DEBUG_LEVEL>0) { - std::cout << "CAChannelMonitor::stop " << channel->getChannelName() << endl; + std::cout << "CAChannelMonitor::stop " + << channel->getChannelName() + << " isStarted " << (isStarted ? "true" : "false") + << endl; } - Status status = Status::Ok; if(!isStarted) return Status(Status::STATUSTYPE_WARNING,"already stopped"); - channel->attachContext(); - int result = ca_clear_subscription(eventID); - if (result == ECA_NORMAL) - { - isStarted = false; - monitorQueue->stop(); - result = ca_flush_io(); - } - if (result == ECA_NORMAL) return status; - string mess("CAChannelMonitor::stop() "); - mess += ca_message(result); - return Status(Status::STATUSTYPE_ERROR,mess); + isStarted = false; + monitorQueue->stop(); + stopMonitorThread->callStop(eventID); + eventID = NULL; + return Status::Ok; } diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index a79b65b..529d17d 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -11,6 +11,7 @@ #include #include +#include /* for CA */ #include @@ -22,6 +23,9 @@ namespace epics { namespace pvAccess { namespace ca { +class StopMonitorThread; +typedef std::tr1::shared_ptr StopMonitorThreadPtr; + class CAChannelGetField; typedef std::tr1::shared_ptr CAChannelGetFieldPtr; typedef std::tr1::weak_ptr CAChannelGetFieldWPtr; @@ -85,9 +89,6 @@ public: virtual void printInfo(std::ostream& out); void attachContext(); - void addChannelGet(const CAChannelGetPtr & get); - void addChannelPut(const CAChannelPutPtr & get); - void addChannelMonitor(const CAChannelMonitorPtr & get); void disconnectChannel(); private: virtual void destroy() {} @@ -101,22 +102,17 @@ private: ChannelRequester::weak_pointer channelRequester; chid channelID; bool channelCreated; - epics::pvData::Mutex requestsMutex; + epics::pvData::Mutex requestsMutex; std::queue getFieldQueue; std::queue putQueue; std::queue getQueue; std::queue monitorQueue; - std::vector getList; - std::vector putList; - std::vector monitorList; }; class CAChannelGet : public ChannelGet, - public ChannelRequester, - public ChannelBaseRequester, public std::tr1::enable_shared_from_this { public: @@ -131,15 +127,7 @@ public: virtual Channel::shared_pointer getChannel(); virtual void cancel(); virtual void lastRequest(); - - virtual void channelCreated( - const epics::pvData::Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState cosnectionState); virtual std::string getRequesterName(); - virtual void channelDisconnect(bool destroy); void activate(); @@ -159,8 +147,6 @@ private: class CAChannelPut : public ChannelPut, - public ChannelRequester, - public ChannelBaseRequester, public std::tr1::enable_shared_from_this { @@ -181,15 +167,7 @@ public: virtual void cancel(); virtual void lastRequest(); - virtual void channelCreated( - const epics::pvData::Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); virtual std::string getRequesterName(); - virtual void channelDisconnect(bool destroy); - void activate(); private: virtual void destroy() {} @@ -210,8 +188,6 @@ typedef std::tr1::shared_ptr CACMonitorQueuePtr; class CAChannelMonitor : public Monitor, - public ChannelRequester, - public ChannelBaseRequester, public std::tr1::enable_shared_from_this { @@ -229,16 +205,7 @@ public: virtual MonitorElementPtr poll(); virtual void release(MonitorElementPtr const & monitorElement); virtual void cancel(); - - virtual void channelCreated( - const epics::pvData::Status& status, - Channel::shared_pointer const & channel); - virtual void channelStateChange( - Channel::shared_pointer const & channel, - Channel::ConnectionState connectionState); virtual std::string getRequesterName(); - - virtual void channelDisconnect(bool destroy); void activate(); private: virtual void destroy() {} @@ -249,6 +216,7 @@ private: MonitorRequester::weak_pointer monitorRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool isStarted; + StopMonitorThreadPtr stopMonitorThread; DbdToPvPtr dbdToPv; epics::pvData::PVStructure::shared_pointer pvStructure; diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index b3b2ae1..db09e38 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -15,6 +15,8 @@ #include #include +#include "stopMonitorThread.h" + #define epicsExportSharedSymbols #include #include "caProviderPvt.h" @@ -40,13 +42,12 @@ CAChannelProvider::CAChannelProvider() } CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&) - : current_context(0) + : current_context(0), + stopMonitorThread(StopMonitorThread::get()) { if(DEBUG_LEVEL>0) { std::cout<< "CAChannelProvider::CAChannelProvider\n"; } - // Ignoring Configuration as CA only allows config via. environment, - // and we don't want to change this here. initialize(); } @@ -60,23 +61,26 @@ CAChannelProvider::~CAChannelProvider() std::queue channelQ; { Lock lock(channelListMutex); - for(size_t i=0; i< caChannelList.size(); ++i) { + for(size_t i=0; i< caChannelList.size(); ++i) + { CAChannelPtr caChannel(caChannelList[i].lock()); if(caChannel) channelQ.push(caChannel); } caChannelList.clear(); } - attachContext(); while(!channelQ.empty()) { if(DEBUG_LEVEL>0) { - std::cout << "disconnectAllChannels calling disconnectChannel " + std::cout << "~CAChannelProvider() calling disconnectChannel " << channelQ.front()->getChannelName() << std::endl; } channelQ.front()->disconnectChannel(); channelQ.pop(); } - ca_flush_io(); + stopMonitorThread->stop(); + if(DEBUG_LEVEL>0) { + std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n"; + } ca_context_destroy(); } @@ -90,12 +94,12 @@ ChannelFind::shared_pointer CAChannelProvider::channelFind( ChannelFindRequester::shared_pointer const & channelFindRequester) { if (channelName.empty()) - throw std::invalid_argument("empty channel name"); + throw std::invalid_argument("CAChannelProvider::channelFind empty channel name"); if (!channelFindRequester) - throw std::invalid_argument("null requester"); + throw std::invalid_argument("CAChannelProvider::channelFind null requester"); - Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + Status errorStatus(Status::STATUSTYPE_ERROR, "CAChannelProvider::channelFind not implemented"); ChannelFind::shared_pointer nullChannelFind; EXCEPTION_GUARD(channelFindRequester->channelFindResult(errorStatus, nullChannelFind, false)); return nullChannelFind; @@ -105,9 +109,9 @@ ChannelFind::shared_pointer CAChannelProvider::channelList( ChannelListRequester::shared_pointer const & channelListRequester) { if (!channelListRequester.get()) - throw std::runtime_error("null requester"); + throw std::runtime_error("CAChannelProvider::channelList null requester"); - Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + Status errorStatus(Status::STATUSTYPE_ERROR, "CAChannelProvider::channelList not implemented"); ChannelFind::shared_pointer nullChannelFind; PVStringArray::const_svector none; EXCEPTION_GUARD(channelListRequester->channelListResult(errorStatus, nullChannelFind, none, false)); @@ -131,7 +135,7 @@ Channel::shared_pointer CAChannelProvider::createChannel( std::string const & address) { if (!address.empty()) - throw std::invalid_argument("CA does not support 'address' parameter"); + throw std::invalid_argument("CAChannelProvider::createChannel does not support 'address' parameter"); return CAChannel::create(shared_from_this(), channelName, priority, channelRequester); } @@ -165,40 +169,39 @@ void CAChannelProvider::poll() { } - void CAChannelProvider::attachContext() { ca_client_context* thread_context = ca_current_context(); if (thread_context == current_context) return; if (thread_context != NULL) { - throw std::runtime_error("CAChannelProvider: Foreign CA context in use"); + throw std::runtime_error("CAChannelProvider::attachContext Foreign CA context in use"); } int result = ca_attach_context(current_context); if (result != ECA_NORMAL) { - std::cout << - "CA error %s occurred while calling ca_attach_context:" - << ca_message(result) << std::endl; + std::string mess("CAChannelProvider::attachContext error calling ca_attach_context "); + mess += ca_message(result); + throw std::runtime_error(mess); } } void CAChannelProvider::initialize() { if(DEBUG_LEVEL>0) std::cout << "CAChannelProvider::initialize()\n"; - /* Create Channel Access */ + StopMonitorThreadPtr thread(StopMonitorThread::get()); int result = ca_context_create(ca_enable_preemptive_callback); if (result != ECA_NORMAL) { - throw std::runtime_error( - std::string("CA error %s occurred while trying to start channel access:") - + ca_message(result)); + std::string mess("CAChannelProvider::initialize error calling ca_context_create "); + mess += ca_message(result); + throw std::runtime_error(mess); } current_context = ca_current_context(); + thread->attachContext(current_context); } void CAClientFactory::start() { if(DEBUG_LEVEL>0) std::cout << "CAClientFactory::start()\n"; if(ChannelProviderRegistry::clients()->getProvider("ca")) { - // do not start twice return; } epicsSignalInstallSigAlarmIgnore(); diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index 7f656b8..3ba1cab 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -17,7 +17,10 @@ namespace epics { namespace pvAccess { namespace ca { -#define DEBUG_LEVEL 0 +#define DEBUG_LEVEL 1 + +class StopMonitorThread; +typedef std::tr1::shared_ptr StopMonitorThreadPtr; class CAChannel; typedef std::tr1::shared_ptr CAChannelPtr; @@ -66,22 +69,18 @@ public: virtual void flush(); virtual void poll(); - void addChannel(const CAChannelPtr & channel); - - /* ---------------------------------------------------------------- */ - void attachContext(); - + void addChannel(const CAChannelPtr & channel); private: + virtual void destroy() EPICS_DEPRECATED {} void initialize(); ca_client_context* current_context; epics::pvData::Mutex channelListMutex; std::vector caChannelList; + StopMonitorThreadPtr stopMonitorThread; }; -} -} -} +}}} #endif /* CAPROVIDERPVT_H */ diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h index fcb1d62..dea4a4b 100644 --- a/src/ca/dbdToPv.h +++ b/src/ca/dbdToPv.h @@ -35,12 +35,14 @@ typedef std::tr1::shared_ptr ValueAlarmDbdPtr; struct CaAlarm { + CaAlarm() : status(0), severity(0) {} dbr_short_t status; dbr_short_t severity; }; struct CaDisplay { + CaDisplay() : lower_disp_limit(0),upper_disp_limit(0) {} double lower_disp_limit; double upper_disp_limit; std::string units; @@ -49,12 +51,15 @@ struct CaDisplay struct CaControl { + CaControl() : upper_ctrl_limit(0),lower_ctrl_limit(0) {} double upper_ctrl_limit; double lower_ctrl_limit; }; struct CaValueAlarm { + CaValueAlarm() : upper_alarm_limit(0),upper_warning_limit(0),lower_warning_limit(0),lower_alarm_limit(0) + {} double upper_alarm_limit; double upper_warning_limit; double lower_warning_limit; diff --git a/src/ca/stopMonitorThread.cpp b/src/ca/stopMonitorThread.cpp new file mode 100644 index 0000000..ab1c356 --- /dev/null +++ b/src/ca/stopMonitorThread.cpp @@ -0,0 +1,140 @@ +/** + * 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.04 + */ + +#include "caChannel.h" +#include +#define epicsExportSharedSymbols +#include "stopMonitorThread.h" + +using namespace epics::pvData; + +namespace epics { +namespace pvAccess { +namespace ca { + +StopMonitorThreadPtr StopMonitorThread::get() +{ + static StopMonitorThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = StopMonitorThreadPtr(new StopMonitorThread()); + master->start(); + } + return master; +} + +StopMonitorThread::StopMonitorThread() +: isStop(false), + isAttachContext(false), + isWaitForNoEvents(false), + current_context(NULL) +{ +} + +StopMonitorThread::~StopMonitorThread() +{ +std::cout << "StopMonitorThread::~StopMonitorThread()\n"; +} + +void StopMonitorThread::attachContext(ca_client_context* current_context) +{ + Lock xx(mutex); + isAttachContext = true; + this->current_context = current_context; + waitForCommand.signal(); +} + +void StopMonitorThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "stopMonitorThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + +void StopMonitorThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + +void StopMonitorThread::callStop(evid pevid) +{ + { + Lock xx(mutex); + evidQueue.push(&(*pevid)); + } + waitForCommand.signal(); +} + +void StopMonitorThread::waitForNoEvents() +{ + while(true) + { + { + Lock xx(mutex); + if(evidQueue.size()==0) return; + isWaitForNoEvents = true; + } + waitForCommand.signal(); + noMoreEvents.wait(); + } +} + +void StopMonitorThread::run() +{ + while(true) + { + waitForCommand.wait(); + Lock lock(mutex); + if(isAttachContext) + { + int result = ca_attach_context(current_context); + if(result != ECA_NORMAL) { + std::string mess("StopMonitorThread::run() while calling ca_attach_context "); + mess += ca_message(result); + throw std::runtime_error(mess); + } + isAttachContext = false; + } + if(evidQueue.size()>0) + { + while(!evidQueue.empty()) + { + evid pvid = evidQueue.front(); + evidQueue.pop(); + int result = ca_clear_subscription(pvid); + if(result!=ECA_NORMAL) + { + std::cout << "StopMonitorThread::run() ca_clear_subscription error " + << ca_message(result) << "\n"; + } + } + } + if(isWaitForNoEvents) + { + isWaitForNoEvents = false; + noMoreEvents.signal(); + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/stopMonitorThread.h b/src/ca/stopMonitorThread.h new file mode 100644 index 0000000..4b88627 --- /dev/null +++ b/src/ca/stopMonitorThread.h @@ -0,0 +1,56 @@ +/** + * 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.04 + */ +#ifndef StopMonitorThread_H +#define StopMonitorThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class StopMonitorThread; +typedef std::tr1::shared_ptr StopMonitorThreadPtr; + +class StopMonitorThread : + public epicsThreadRunable +{ +public: + ~StopMonitorThread(); + virtual void run(); + void start(); + void stop(); + static StopMonitorThreadPtr get(); + void callStop(evid pevid); + void attachContext(ca_client_context* current_context); + void waitForNoEvents(); +private: + StopMonitorThread(); + + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + epics::pvData::Event noMoreEvents; + std::queue evidQueue; + bool isStop; + bool isAttachContext; + bool isWaitForNoEvents; + ca_client_context* current_context; +}; + + +}}} + +#endif /* StopMonitorThread_H */ From c377d35aee6e17f201c5051ca0d4fa38e63fd54e Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Sun, 10 Jun 2018 14:28:23 -0400 Subject: [PATCH 30/36] StopMonitorThread addNoEventsCallback replaces waitForNoEvent --- src/ca/caChannel.cpp | 9 ++++++--- src/ca/caChannel.h | 1 + src/ca/caProviderPvt.h | 2 +- src/ca/stopMonitorThread.cpp | 35 +++++++++++++++-------------------- src/ca/stopMonitorThread.h | 7 ++++--- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 7d2dc09..47b4fe3 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -137,7 +137,9 @@ void CAChannel::activate(short priority) CAChannel::~CAChannel() { if(DEBUG_LEVEL>0) { - cout << "CAChannel::~CAChannel() " << channelName << endl; + cout << "CAChannel::~CAChannel() " << channelName + << " channelCreated " << (channelCreated ? "true" : "false") + << endl; } { Lock lock(requestsMutex); @@ -725,7 +727,8 @@ CAChannelMonitor::~CAChannelMonitor() << endl; } if(isStarted) stop(); - stopMonitorThread->waitForNoEvents(); + stopMonitorThread->addNoEventsCallback(&waitForNoEvents); + waitForNoEvents.wait(); } void CAChannelMonitor::activate() @@ -800,6 +803,7 @@ epics::pvData::Status CAChannelMonitor::start() } channel->attachContext(); monitorQueue->start(); + isStarted = true; int result = ca_create_subscription(dbdToPv->getRequestType(), 0, channel->getChannelID(), DBE_VALUE, @@ -807,7 +811,6 @@ epics::pvData::Status CAChannelMonitor::start() &eventID); if (result == ECA_NORMAL) { - isStarted = true; result = ca_flush_io(); } if (result == ECA_NORMAL) return status; diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 529d17d..24b0dfd 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -219,6 +219,7 @@ private: StopMonitorThreadPtr stopMonitorThread; DbdToPvPtr dbdToPv; + epics::pvData::Event waitForNoEvents; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::MonitorElementPtr activeElement; evid eventID; diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index 3ba1cab..c04361e 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -17,7 +17,7 @@ namespace epics { namespace pvAccess { namespace ca { -#define DEBUG_LEVEL 1 +#define DEBUG_LEVEL 0 class StopMonitorThread; typedef std::tr1::shared_ptr StopMonitorThreadPtr; diff --git a/src/ca/stopMonitorThread.cpp b/src/ca/stopMonitorThread.cpp index ab1c356..794bcd6 100644 --- a/src/ca/stopMonitorThread.cpp +++ b/src/ca/stopMonitorThread.cpp @@ -34,7 +34,6 @@ StopMonitorThreadPtr StopMonitorThread::get() StopMonitorThread::StopMonitorThread() : isStop(false), isAttachContext(false), - isWaitForNoEvents(false), current_context(NULL) { } @@ -74,27 +73,19 @@ void StopMonitorThread::stop() void StopMonitorThread::callStop(evid pevid) { - { - Lock xx(mutex); - evidQueue.push(&(*pevid)); - } + Lock xx(mutex); + evidQueue.push(&(*pevid)); waitForCommand.signal(); } -void StopMonitorThread::waitForNoEvents() +void StopMonitorThread::addNoEventsCallback(Event * event) { - while(true) - { - { - Lock xx(mutex); - if(evidQueue.size()==0) return; - isWaitForNoEvents = true; - } - waitForCommand.signal(); - noMoreEvents.wait(); - } + Lock xx(mutex); + noEventsCallbackQueue.push(event); + waitForCommand.signal(); } + void StopMonitorThread::run() { while(true) @@ -112,7 +103,7 @@ void StopMonitorThread::run() isAttachContext = false; } if(evidQueue.size()>0) - { + { while(!evidQueue.empty()) { evid pvid = evidQueue.front(); @@ -125,10 +116,14 @@ void StopMonitorThread::run() } } } - if(isWaitForNoEvents) + if(noEventsCallbackQueue.size()>0) { - isWaitForNoEvents = false; - noMoreEvents.signal(); + while(!noEventsCallbackQueue.empty()) + { + Event * event = noEventsCallbackQueue.front(); + noEventsCallbackQueue.pop(); + event->signal(); + } } if(isStop) { waitForStop.signal(); diff --git a/src/ca/stopMonitorThread.h b/src/ca/stopMonitorThread.h index 4b88627..d2e7bd2 100644 --- a/src/ca/stopMonitorThread.h +++ b/src/ca/stopMonitorThread.h @@ -20,9 +20,11 @@ namespace epics { namespace pvAccess { namespace ca { + class StopMonitorThread; typedef std::tr1::shared_ptr StopMonitorThreadPtr; + class StopMonitorThread : public epicsThreadRunable { @@ -34,7 +36,7 @@ public: static StopMonitorThreadPtr get(); void callStop(evid pevid); void attachContext(ca_client_context* current_context); - void waitForNoEvents(); + void addNoEventsCallback(epics::pvData::Event * event); private: StopMonitorThread(); @@ -42,11 +44,10 @@ private: epics::pvData::Mutex mutex; epics::pvData::Event waitForCommand; epics::pvData::Event waitForStop; - epics::pvData::Event noMoreEvents; std::queue evidQueue; + std::queue noEventsCallbackQueue; bool isStop; bool isAttachContext; - bool isWaitForNoEvents; ca_client_context* current_context; }; From 184e92b346d8b8f365a1aa5e5e2dcea401e19ec4 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Tue, 19 Jun 2018 13:11:01 -0400 Subject: [PATCH 31/36] delete stopMonitorThread; add monitorEventThread --- src/ca/Makefile | 2 +- src/ca/caChannel.cpp | 115 ++++++++++++++++++++--------- src/ca/caChannel.h | 19 +++-- src/ca/caProvider.cpp | 30 +++++--- src/ca/caProviderPvt.h | 8 +- src/ca/dbdToPv.cpp | 5 +- src/ca/monitorEventThread.cpp | 113 ++++++++++++++++++++++++++++ src/ca/monitorEventThread.h | 71 ++++++++++++++++++ src/ca/pv/caProvider.h | 23 ++++-- src/ca/stopMonitorThread.cpp | 135 ---------------------------------- src/ca/stopMonitorThread.h | 57 -------------- 11 files changed, 328 insertions(+), 250 deletions(-) create mode 100644 src/ca/monitorEventThread.cpp create mode 100644 src/ca/monitorEventThread.h delete mode 100644 src/ca/stopMonitorThread.cpp delete mode 100644 src/ca/stopMonitorThread.h diff --git a/src/ca/Makefile b/src/ca/Makefile index df0ae03..7aa4247 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -11,7 +11,7 @@ LIB_SYS_LIBS_WIN32 += ws2_32 INC += pv/caProvider.h -pvAccessCA_SRCS += stopMonitorThread.cpp +pvAccessCA_SRCS += monitorEventThread.cpp pvAccessCA_SRCS += caProvider.cpp pvAccessCA_SRCS += caChannel.cpp pvAccessCA_SRCS += dbdToPv.cpp diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index 47b4fe3..a4294a3 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -11,7 +11,7 @@ #include #include #include -#include "stopMonitorThread.h" +#include "monitorEventThread.h" #define epicsExportSharedSymbols #include "caChannel.h" @@ -70,7 +70,9 @@ void CAChannel::connected() getQueue.pop(); } while(!monitorQueue.empty()) { - monitorQueue.front()->activate(); + CAChannelMonitorPtr monitor(monitorQueue.front()); + monitor->activate(); + addMonitor(monitor); monitorQueue.pop(); } ChannelRequester::shared_pointer req(channelRequester.lock()); @@ -161,6 +163,14 @@ void CAChannel::disconnectChannel() if(!channelCreated) return; channelCreated = false; } + std::vector::iterator it; + for(it = monitorlist.begin(); it!=monitorlist.end(); ++it) + { + CAChannelMonitorPtr mon = (*it).lock(); + if(!mon) continue; + mon->stop(); + } + monitorlist.resize(0); /* Clear CA Channel */ CAChannelProviderPtr provider(channelProvider.lock()); if(provider) { @@ -233,7 +243,7 @@ void CAChannel::getField(GetFieldRequester::shared_pointer const & requester, } -AccessRights CAChannel::getAccessRights(epics::pvData::PVField::shared_pointer const & /*pvField*/) +AccessRights CAChannel::getAccessRights(PVField::shared_pointer const & /*pvField*/) { if (ca_write_access(channelID)) return readWrite; @@ -246,7 +256,7 @@ AccessRights CAChannel::getAccessRights(epics::pvData::PVField::shared_pointer c ChannelGet::shared_pointer CAChannel::createChannelGet( ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannel::createChannelGet " << channelName << endl; @@ -267,7 +277,7 @@ ChannelGet::shared_pointer CAChannel::createChannelGet( ChannelPut::shared_pointer CAChannel::createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannel::createChannelPut " << channelName << endl; @@ -288,7 +298,7 @@ ChannelPut::shared_pointer CAChannel::createChannelPut( Monitor::shared_pointer CAChannel::createMonitor( MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannel::createMonitor " << channelName << endl; @@ -303,9 +313,22 @@ Monitor::shared_pointer CAChannel::createMonitor( } } channelMonitor->activate(); + addMonitor(channelMonitor); return channelMonitor; } +void CAChannel::addMonitor(CAChannelMonitorPtr const & monitor) +{ + std::vector::iterator it; + for(it = monitorlist.begin(); it!=monitorlist.end(); ++it) + { + CAChannelMonitorWPtr mon = *it; + if(mon.lock()) continue; + mon = monitor; + return; + } + monitorlist.push_back(monitor); +} void CAChannel::printInfo(std::ostream& out) { @@ -348,7 +371,7 @@ void CAChannelGetField::callRequester(CAChannelPtr const & caChannel) PVStructurePtr pvRequest(createRequest("")); DbdToPvPtr dbdToPv = DbdToPv::create(caChannel,pvRequest,getIO); PVStructurePtr pvStructure = dbdToPv->createPVStructure(); - epics::pvData::Structure::const_shared_pointer structure(pvStructure->getStructure()); + Structure::const_shared_pointer structure(pvStructure->getStructure()); Field::const_shared_pointer field = subField.empty() ? std::tr1::static_pointer_cast(structure) : @@ -385,7 +408,7 @@ size_t CAChannelGet::num_instances; CAChannelGetPtr CAChannelGet::create( CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannelGet::create " << channel->getChannelName() << endl; @@ -395,7 +418,7 @@ CAChannelGetPtr CAChannelGet::create( CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) : channel(channel), channelGetRequester(channelGetRequester), @@ -490,7 +513,7 @@ size_t CAChannelPut::num_instances; CAChannelPutPtr CAChannelPut::create( CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannelPut::create " << channel->getChannelName() << endl; @@ -500,7 +523,7 @@ CAChannelPutPtr CAChannelPut::create( CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) : channel(channel), channelPutRequester(channelPutRequester), @@ -698,7 +721,7 @@ size_t CAChannelMonitor::num_instances; CAChannelMonitorPtr CAChannelMonitor::create( CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, - epics::pvData::PVStructure::shared_pointer const & pvRequest) + PVStructure::shared_pointer const & pvRequest) { if(DEBUG_LEVEL>0) { cout << "CAChannelMonitor::create " << channel->getChannelName() << endl; @@ -715,7 +738,8 @@ CAChannelMonitor::CAChannelMonitor( monitorRequester(monitorRequester), pvRequest(pvRequest), isStarted(false), - stopMonitorThread(StopMonitorThread::get()) + monitorEventThread(MonitorEventThread::get()), + pevid(NULL) {} CAChannelMonitor::~CAChannelMonitor() @@ -726,9 +750,7 @@ CAChannelMonitor::~CAChannelMonitor() << " isStarted " << (isStarted ? "true" : "false") << endl; } - if(isStarted) stop(); - stopMonitorThread->addNoEventsCallback(&waitForNoEvents); - waitForNoEvents.wait(); + stop(); } void CAChannelMonitor::activate() @@ -753,6 +775,9 @@ void CAChannelMonitor::activate() if (size > 1) queueSize = size; } } + notifyRequester = NotifyRequesterPtr(new NotifyRequester()); + + notifyRequester->setChannelMonitor(shared_from_this()); monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); @@ -766,7 +791,10 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) std::cout << "CAChannelMonitor::subscriptionEvent " << channel->getChannelName() << endl; } - if(!isStarted) return; + { + Lock lock(mutex); + if(!isStarted) return; + } MonitorRequester::shared_pointer requester(monitorRequester.lock()); if(!requester) return; Status status = dbdToPv->getFromDBD(pvStructure,activeElement->changedBitSet,args); @@ -778,9 +806,7 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) } else { *(activeElement->overrunBitSet) |= *(activeElement->changedBitSet); } - - // call monitorRequester even if queue is full - requester->monitorEvent(shared_from_this()); + monitorEventThread->event(notifyRequester); } else { @@ -791,24 +817,27 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) } } -epics::pvData::Status CAChannelMonitor::start() +Status CAChannelMonitor::start() { if(DEBUG_LEVEL>0) { std::cout << "CAChannelMonitor::start " << channel->getChannelName() << endl; } Status status = Status::Ok; - if(isStarted) { - status = Status(Status::STATUSTYPE_WARNING,"already started"); - return status; + { + Lock lock(mutex); + if(isStarted) { + status = Status(Status::STATUSTYPE_WARNING,"already started"); + return status; + } + isStarted = true; + monitorQueue->start(); } channel->attachContext(); - monitorQueue->start(); - isStarted = true; int result = ca_create_subscription(dbdToPv->getRequestType(), 0, channel->getChannelID(), DBE_VALUE, ca_subscription_handler, this, - &eventID); + &pevid); if (result == ECA_NORMAL) { result = ca_flush_io(); @@ -819,7 +848,7 @@ epics::pvData::Status CAChannelMonitor::start() return Status(Status::STATUSTYPE_ERROR,message); } -epics::pvData::Status CAChannelMonitor::stop() +Status CAChannelMonitor::stop() { if(DEBUG_LEVEL>0) { std::cout << "CAChannelMonitor::stop " @@ -827,12 +856,26 @@ epics::pvData::Status CAChannelMonitor::stop() << " isStarted " << (isStarted ? "true" : "false") << endl; } - if(!isStarted) return Status(Status::STATUSTYPE_WARNING,"already stopped"); - isStarted = false; + { + Lock lock(mutex); + if(!isStarted) return Status(Status::STATUSTYPE_WARNING,"already stopped"); + isStarted = false; + } monitorQueue->stop(); - stopMonitorThread->callStop(eventID); - eventID = NULL; - return Status::Ok; + int result = ca_clear_subscription(pevid); + if(result==ECA_NORMAL) return Status::Ok; + return Status(Status::STATUSTYPE_ERROR,string(ca_message(result))); +} + +void CAChannelMonitor::notifyClient() +{ + { + Lock lock(mutex); + if(!isStarted) return; + } + MonitorRequester::shared_pointer requester(monitorRequester.lock()); + if(!requester) return; + requester->monitorEvent(shared_from_this()); } @@ -841,6 +884,10 @@ MonitorElementPtr CAChannelMonitor::poll() if(DEBUG_LEVEL>1) { std::cout << "CAChannelMonitor::poll " << channel->getChannelName() << endl; } + { + Lock lock(mutex); + if(!isStarted) return MonitorElementPtr(); + } return monitorQueue->poll(); } @@ -853,7 +900,7 @@ void CAChannelMonitor::release(MonitorElementPtr const & monitorElement) monitorQueue->release(monitorElement); } -/* --------------- epics::pvData::ChannelRequest --------------- */ +/* --------------- ChannelRequest --------------- */ void CAChannelMonitor::cancel() { diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index 24b0dfd..ae641c0 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -23,8 +23,12 @@ namespace epics { namespace pvAccess { namespace ca { -class StopMonitorThread; -typedef std::tr1::shared_ptr StopMonitorThreadPtr; +class NotifyRequester; +typedef std::tr1::shared_ptr NotifyRequesterPtr; + +class MonitorEventThread; +typedef std::tr1::shared_ptr MonitorEventThreadPtr; + class CAChannelGetField; typedef std::tr1::shared_ptr CAChannelGetFieldPtr; @@ -96,6 +100,7 @@ private: CAChannelProvider::shared_pointer const & channelProvider, ChannelRequester::shared_pointer const & channelRequester); void activate(short priority); + void addMonitor(CAChannelMonitorPtr const & monitor); std::string channelName; CAChannelProviderWPtr channelProvider; @@ -108,6 +113,7 @@ private: std::queue putQueue; std::queue getQueue; std::queue monitorQueue; + std::vector monitorlist; }; @@ -207,6 +213,7 @@ public: virtual void cancel(); virtual std::string getRequesterName(); void activate(); + void notifyClient(); private: virtual void destroy() {} CAChannelMonitor(CAChannel::shared_pointer const & _channel, @@ -216,13 +223,15 @@ private: MonitorRequester::weak_pointer monitorRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool isStarted; - StopMonitorThreadPtr stopMonitorThread; + NotifyRequesterPtr notifyRequester; + MonitorEventThreadPtr monitorEventThread; + evid pevid; DbdToPvPtr dbdToPv; - epics::pvData::Event waitForNoEvents; + epics::pvData::Mutex mutex; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::MonitorElementPtr activeElement; - evid eventID; + CACMonitorQueuePtr monitorQueue; }; diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index db09e38..27386e2 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -15,7 +15,7 @@ #include #include -#include "stopMonitorThread.h" +#include "monitorEventThread.h" #define epicsExportSharedSymbols #include @@ -43,7 +43,7 @@ CAChannelProvider::CAChannelProvider() CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&) : current_context(0), - stopMonitorThread(StopMonitorThread::get()) + monitorEventThread(MonitorEventThread::get()) { if(DEBUG_LEVEL>0) { std::cout<< "CAChannelProvider::CAChannelProvider\n"; @@ -77,11 +77,12 @@ CAChannelProvider::~CAChannelProvider() channelQ.front()->disconnectChannel(); channelQ.pop(); } - stopMonitorThread->stop(); + monitorEventThread->stop(); if(DEBUG_LEVEL>0) { std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n"; } ca_context_destroy(); +std::cout << "CAChannelProvider::~CAChannelProvider() returning\n"; } std::string CAChannelProvider::getProviderName() @@ -187,7 +188,6 @@ void CAChannelProvider::attachContext() void CAChannelProvider::initialize() { if(DEBUG_LEVEL>0) std::cout << "CAChannelProvider::initialize()\n"; - StopMonitorThreadPtr thread(StopMonitorThread::get()); int result = ca_context_create(ca_enable_preemptive_callback); if (result != ECA_NORMAL) { std::string mess("CAChannelProvider::initialize error calling ca_context_create "); @@ -195,7 +195,11 @@ void CAChannelProvider::initialize() throw std::runtime_error(mess); } current_context = ca_current_context(); - thread->attachContext(current_context); +} + +ca_client_context * CAChannelProvider::get_ca_client_context() +{ + return current_context; } void CAClientFactory::start() @@ -218,13 +222,21 @@ void CAClientFactory::start() } } +ca_client_context * CAClientFactory::get_ca_client_context() +{ + if(DEBUG_LEVEL>0) std::cout << "CAClientFactory::get_ca_client_context\n"; + ChannelProvider::shared_pointer channelProvider( + ChannelProviderRegistry::clients()->getProvider("ca")); + if(!channelProvider) throw std::runtime_error("CAClientFactory::start() was not called"); + CAChannelProviderPtr cacChannelProvider + = std::tr1::static_pointer_cast(channelProvider); + return cacChannelProvider->get_ca_client_context(); +} + void CAClientFactory::stop() { // unregister now done with exit hook } - -} -} -} +}}} diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index c04361e..af0c4d9 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -19,8 +19,9 @@ namespace ca { #define DEBUG_LEVEL 0 -class StopMonitorThread; -typedef std::tr1::shared_ptr StopMonitorThreadPtr; +class MonitorEventThread; +typedef std::tr1::shared_ptr MonitorEventThreadPtr; + class CAChannel; typedef std::tr1::shared_ptr CAChannelPtr; @@ -71,6 +72,7 @@ public: void attachContext(); void addChannel(const CAChannelPtr & channel); + ca_client_context* get_ca_client_context(); private: virtual void destroy() EPICS_DEPRECATED {} @@ -78,7 +80,7 @@ private: ca_client_context* current_context; epics::pvData::Mutex channelListMutex; std::vector caChannelList; - StopMonitorThreadPtr stopMonitorThread; + MonitorEventThreadPtr monitorEventThread; }; }}} diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index fd9c361..f8fe8ca 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -77,7 +77,10 @@ DbdToPv::DbdToPv(IOType ioType) caValueType(-1), caRequestType(-1), maxElements(0) -{} +{ + caTimeStamp.secPastEpoch = 0; + caTimeStamp.nsec = 0; +} static ScalarType dbr2ST[] = { diff --git a/src/ca/monitorEventThread.cpp b/src/ca/monitorEventThread.cpp new file mode 100644 index 0000000..e95d101 --- /dev/null +++ b/src/ca/monitorEventThread.cpp @@ -0,0 +1,113 @@ +/** + * 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.06 + */ + +#include "caChannel.h" +#include +#define epicsExportSharedSymbols +#include "monitorEventThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +MonitorEventThreadPtr MonitorEventThread::get() +{ + static MonitorEventThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = MonitorEventThreadPtr(new MonitorEventThread()); + master->start(); + } + return master; +} + +MonitorEventThread::MonitorEventThread() +: isStop(false) +{ +} + +MonitorEventThread::~MonitorEventThread() +{ +std::cout << "MonitorEventThread::~MonitorEventThread()\n"; +} + +void MonitorEventThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "monitorEventThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + +void MonitorEventThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + + +void MonitorEventThread::event(NotifyRequesterPtr const &stopMonitor) +{ + { + Lock lock(mutex); + if(stopMonitor->isOnQueue) return; + stopMonitor->isOnQueue = true; + notifyMonitorQueue.push(stopMonitor); + } + waitForCommand.signal(); +} + +void MonitorEventThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyRequester* notifyRequester(NULL); + { + Lock lock(mutex); + if(!notifyMonitorQueue.empty()) + { + more = true; + NotifyRequesterWPtr req(notifyMonitorQueue.front()); + notifyMonitorQueue.pop(); + NotifyRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyRequester!=NULL) + { + CAChannelMonitorPtr channelMonitor(notifyRequester->channelMonitor.lock()); + if(channelMonitor) channelMonitor->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/monitorEventThread.h b/src/ca/monitorEventThread.h new file mode 100644 index 0000000..0784d07 --- /dev/null +++ b/src/ca/monitorEventThread.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.06 + */ +#ifndef MonitorEventThread_H +#define MonitorEventThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyRequester; +typedef std::tr1::shared_ptr NotifyRequesterPtr; +typedef std::tr1::weak_ptr NotifyRequesterWPtr; + + +class MonitorEventThread; +typedef std::tr1::shared_ptr MonitorEventThreadPtr; + +class CAChannelMonitor; +typedef std::tr1::shared_ptr CAChannelMonitorPtr; +typedef std::tr1::weak_ptr CAChannelMonitorWPtr; + +class NotifyRequester +{ +public: + MonitorRequester::weak_pointer monitorRequester; + CAChannelMonitorWPtr channelMonitor; + bool isOnQueue; + NotifyRequester() : isOnQueue(false) {} + void setChannelMonitor(CAChannelMonitorPtr const &channelMonitor) + { this->channelMonitor = channelMonitor;} +}; + + +class MonitorEventThread : + public epicsThreadRunable +{ +public: + static MonitorEventThreadPtr get(); + ~MonitorEventThread(); + virtual void run(); + void start(); + void stop(); + void event(NotifyRequesterPtr const ¬ifyRequester); +private: + MonitorEventThread(); + + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyMonitorQueue; +}; + + +}}} + +#endif /* MonitorEventThread_H */ diff --git a/src/ca/pv/caProvider.h b/src/ca/pv/caProvider.h index 3f75c4a..a147d26 100644 --- a/src/ca/pv/caProvider.h +++ b/src/ca/pv/caProvider.h @@ -8,6 +8,7 @@ #define CAPROVIDER_H #include +#include #include namespace epics { @@ -17,23 +18,35 @@ namespace ca { /** * @brief CAClientFactory is a channel provider for the ca network provider. * + * A single instance is created the first time CAClientFactory::start is called. + * epicsAtExit is used to destroy the instance. + * + * The thread that calls start, or a ca auxillary thread, are the only threads + * that can call the ca_* functions. + * + * Note the monitor callbacks are made from a separate thread that must NOT call any ca_* function. * */ class epicsShareClass CAClientFactory { public: - /** @brief start the provider + /** @brief start provider ca * */ static void start(); - /** @brief stop the provider + /** @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(); }; -} -} -} +}}} #endif /* CAPROVIDER_H */ diff --git a/src/ca/stopMonitorThread.cpp b/src/ca/stopMonitorThread.cpp deleted file mode 100644 index 794bcd6..0000000 --- a/src/ca/stopMonitorThread.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/** - * 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.04 - */ - -#include "caChannel.h" -#include -#define epicsExportSharedSymbols -#include "stopMonitorThread.h" - -using namespace epics::pvData; - -namespace epics { -namespace pvAccess { -namespace ca { - -StopMonitorThreadPtr StopMonitorThread::get() -{ - static StopMonitorThreadPtr master; - static Mutex mutex; - Lock xx(mutex); - if(!master) { - master = StopMonitorThreadPtr(new StopMonitorThread()); - master->start(); - } - return master; -} - -StopMonitorThread::StopMonitorThread() -: isStop(false), - isAttachContext(false), - current_context(NULL) -{ -} - -StopMonitorThread::~StopMonitorThread() -{ -std::cout << "StopMonitorThread::~StopMonitorThread()\n"; -} - -void StopMonitorThread::attachContext(ca_client_context* current_context) -{ - Lock xx(mutex); - isAttachContext = true; - this->current_context = current_context; - waitForCommand.signal(); -} - -void StopMonitorThread::start() -{ - thread = std::tr1::shared_ptr(new epicsThread( - *this, - "stopMonitorThread", - epicsThreadGetStackSize(epicsThreadStackSmall), - epicsThreadPriorityLow)); - thread->start(); -} - -void StopMonitorThread::stop() -{ - { - Lock xx(mutex); - isStop = true; - } - waitForCommand.signal(); - waitForStop.wait(); -} - -void StopMonitorThread::callStop(evid pevid) -{ - Lock xx(mutex); - evidQueue.push(&(*pevid)); - waitForCommand.signal(); -} - -void StopMonitorThread::addNoEventsCallback(Event * event) -{ - Lock xx(mutex); - noEventsCallbackQueue.push(event); - waitForCommand.signal(); -} - - -void StopMonitorThread::run() -{ - while(true) - { - waitForCommand.wait(); - Lock lock(mutex); - if(isAttachContext) - { - int result = ca_attach_context(current_context); - if(result != ECA_NORMAL) { - std::string mess("StopMonitorThread::run() while calling ca_attach_context "); - mess += ca_message(result); - throw std::runtime_error(mess); - } - isAttachContext = false; - } - if(evidQueue.size()>0) - { - while(!evidQueue.empty()) - { - evid pvid = evidQueue.front(); - evidQueue.pop(); - int result = ca_clear_subscription(pvid); - if(result!=ECA_NORMAL) - { - std::cout << "StopMonitorThread::run() ca_clear_subscription error " - << ca_message(result) << "\n"; - } - } - } - if(noEventsCallbackQueue.size()>0) - { - while(!noEventsCallbackQueue.empty()) - { - Event * event = noEventsCallbackQueue.front(); - noEventsCallbackQueue.pop(); - event->signal(); - } - } - if(isStop) { - waitForStop.signal(); - break; - } - } -} - -}}} diff --git a/src/ca/stopMonitorThread.h b/src/ca/stopMonitorThread.h deleted file mode 100644 index d2e7bd2..0000000 --- a/src/ca/stopMonitorThread.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * 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.04 - */ -#ifndef StopMonitorThread_H -#define StopMonitorThread_H -#include -#include -#include -#include -#include -#include - -namespace epics { -namespace pvAccess { -namespace ca { - - -class StopMonitorThread; -typedef std::tr1::shared_ptr StopMonitorThreadPtr; - - -class StopMonitorThread : - public epicsThreadRunable -{ -public: - ~StopMonitorThread(); - virtual void run(); - void start(); - void stop(); - static StopMonitorThreadPtr get(); - void callStop(evid pevid); - void attachContext(ca_client_context* current_context); - void addNoEventsCallback(epics::pvData::Event * event); -private: - StopMonitorThread(); - - std::tr1::shared_ptr thread; - epics::pvData::Mutex mutex; - epics::pvData::Event waitForCommand; - epics::pvData::Event waitForStop; - std::queue evidQueue; - std::queue noEventsCallbackQueue; - bool isStop; - bool isAttachContext; - ca_client_context* current_context; -}; - - -}}} - -#endif /* StopMonitorThread_H */ From b871b2de869276815911e38af8023e42711f8ef4 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 25 Jun 2018 14:00:24 -0400 Subject: [PATCH 32/36] caProvider.md is documentation for provider ca --- caProvider.md | 636 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 636 insertions(+) create mode 100644 caProvider.md diff --git a/caProvider.md b/caProvider.md new file mode 100644 index 0000000..2ceb15a --- /dev/null +++ b/caProvider.md @@ -0,0 +1,636 @@ +#pvAccessCPP: ca provider + +2018.06.25 + +Editors: + +* Marty Kraimer + +This is a description of 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. + +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 +[pva2pva](https://github.com/epics-base/pva2pva), +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(). + +Client code can create an Auxillary Thread by calling: + +``` +ca_client_context* current_context = CAClientFactory::get_ca_client_context(); +int result = ca_attach_context(current_context); + +``` + + +[Deadlock in ca_clear_subscription()](https://bugs.launchpad.net/epics-base/7.0/+bug/1751380) + +Shows a problem with monitor callbacks. + + +In order to prevent this problem **ca** creates a monitorEventThread. +All calls to the requester's **monitorEvent** method are made from the monitorEventThread. + +**Note** the monitorEventThread does not call **ca_attach_context**. +This means that no **ca_xxx** function can be called from +the requester's **monitorEvent** method. + + + + From 98a019aec8d8e85f937f85d12f70312efa5d3b11 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 25 Jun 2018 14:38:08 -0400 Subject: [PATCH 33/36] fix document title --- caProvider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/caProvider.md b/caProvider.md index 2ceb15a..c744c36 100644 --- a/caProvider.md +++ b/caProvider.md @@ -1,4 +1,4 @@ -#pvAccessCPP: ca provider +# pvAccessCPP: ca provider 2018.06.25 From 02cf890d60d50af0c717f20e95846598e74d85d5 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Mon, 25 Jun 2018 14:42:40 -0400 Subject: [PATCH 34/36] fix spaces in headers --- caProvider.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/caProvider.md b/caProvider.md index c744c36..7f6c536 100644 --- a/caProvider.md +++ b/caProvider.md @@ -39,7 +39,7 @@ Each **DBRecord** instance has a record name that is unique with in the local ar A channel name is a **recordname.fieldName**. If the fieldname is not specified then **.VAL** is assumed -###example database +### example database The following: @@ -51,7 +51,7 @@ mrk> softIoc -d testCaProvider.db Starts an EPICS IOC that is used for all examples in this document. -###examples +### examples ``` mrk> caget -d DBR_TIME_FLOAT DBRdoubleout @@ -178,7 +178,7 @@ by the server. Lets discuss the various fields. -###value +### value This can be a scalar, scalarArray, or an enumerated structure. @@ -219,11 +219,11 @@ mrk> ``` -###alarm,timeStamp,display,control, and valueAlarm +### alarm,timeStamp,display,control, and valueAlarm Each of these is one of the property structures defined in pvData. -####Examples +#### Examples ``` mrk> pvget -p ca -r alarm -i DBRdoubleout @@ -276,7 +276,7 @@ structure ## DBD to pvData -###Type Conversion +### Type Conversion Three type systems are involved in accessing data in a **DBRecord** and converting it to/from pvData: @@ -311,7 +311,7 @@ Notes: * 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 +### Accessing data in a DBRecord An IOC database is a memory resident database of **DBRecord** instances. @@ -386,7 +386,7 @@ For example the enum methods are only implemented by records that have the defin ``` -###Channel Access Data +### 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. @@ -518,7 +518,7 @@ struct dbr_ctrl_enum{ }; ``` -###PVData for a DBRrecord via ca provider +### 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. From f83c6c87d62551c9d4a6bf6fb4acd8180502dad0 Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Tue, 26 Jun 2018 08:43:40 -0400 Subject: [PATCH 35/36] remove warnings andrew saw while building testCaProvider.cpp --- src/ca/pv/caProvider.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ca/pv/caProvider.h b/src/ca/pv/caProvider.h index a147d26..2a18acd 100644 --- a/src/ca/pv/caProvider.h +++ b/src/ca/pv/caProvider.h @@ -8,9 +8,10 @@ #define CAPROVIDER_H #include -#include #include +struct ca_client_context; + namespace epics { namespace pvAccess { namespace ca { @@ -39,7 +40,7 @@ public: * 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(); + static struct ca_client_context * get_ca_client_context(); /** @brief stop provider ca * * This does nothing since epicsAtExit is used to destroy the instance. From df45c70149ec8f87bdea37f637b9ca4eea95100c Mon Sep 17 00:00:00 2001 From: mrkraimer Date: Sat, 14 Jul 2018 11:46:26 -0400 Subject: [PATCH 36/36] add putDoneThread and getDoneThread; fix issue 114 --- caProvider.md | 26 +++---- src/ca/Makefile | 2 + src/ca/caChannel.cpp | 127 ++++++++++++++++++++++++---------- src/ca/caChannel.h | 45 ++++++++---- src/ca/caProvider.cpp | 38 +++------- src/ca/caProviderPvt.h | 16 +++-- src/ca/dbdToPv.cpp | 75 ++++++++++---------- src/ca/dbdToPv.h | 27 +++++--- src/ca/getDoneThread.cpp | 114 ++++++++++++++++++++++++++++++ src/ca/getDoneThread.h | 71 +++++++++++++++++++ src/ca/monitorEventThread.cpp | 22 +++--- src/ca/monitorEventThread.h | 14 ++-- src/ca/putDoneThread.cpp | 114 ++++++++++++++++++++++++++++++ src/ca/putDoneThread.h | 70 +++++++++++++++++++ src/ca/pv/caProvider.h | 18 +++-- 15 files changed, 615 insertions(+), 164 deletions(-) create mode 100644 src/ca/getDoneThread.cpp create mode 100644 src/ca/getDoneThread.h create mode 100644 src/ca/putDoneThread.cpp create mode 100644 src/ca/putDoneThread.h diff --git a/caProvider.md b/caProvider.md index 7f6c536..6a479aa 100644 --- a/caProvider.md +++ b/caProvider.md @@ -1,6 +1,6 @@ # pvAccessCPP: ca provider -2018.06.25 +2018.07.09 Editors: @@ -610,26 +610,26 @@ Any code that uses **ca** must call **CAClientFactory::start()** before making a ca_context_create is called for the thread that calls CAClientFactory::start(). -Client code can create an Auxillary Thread by calling: - -``` -ca_client_context* current_context = CAClientFactory::get_ca_client_context(); -int result = ca_attach_context(current_context); - -``` +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 a monitorEventThread. -All calls to the requester's **monitorEvent** method are made from the monitorEventThread. +In order to prevent this problem **ca** creates the following threads: +**getEventThread**, **putEventThread**, and **monitorEventThread**. -**Note** the monitorEventThread does not call **ca_attach_context**. -This means that no **ca_xxx** function can be called from -the requester's **monitorEvent** method. +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. diff --git a/src/ca/Makefile b/src/ca/Makefile index 7aa4247..93e6b27 100644 --- a/src/ca/Makefile +++ b/src/ca/Makefile @@ -12,6 +12,8 @@ LIB_SYS_LIBS_WIN32 += ws2_32 INC += pv/caProvider.h pvAccessCA_SRCS += monitorEventThread.cpp +pvAccessCA_SRCS += getDoneThread.cpp +pvAccessCA_SRCS += putDoneThread.cpp pvAccessCA_SRCS += caProvider.cpp pvAccessCA_SRCS += caChannel.cpp pvAccessCA_SRCS += dbdToPv.cpp diff --git a/src/ca/caChannel.cpp b/src/ca/caChannel.cpp index a4294a3..b19349b 100644 --- a/src/ca/caChannel.cpp +++ b/src/ca/caChannel.cpp @@ -10,8 +10,9 @@ #include #include #include -#include #include "monitorEventThread.h" +#include "getDoneThread.h" +#include "putDoneThread.h" #define epicsExportSharedSymbols #include "caChannel.h" @@ -95,8 +96,6 @@ void CAChannel::disconnected() } } -size_t CAChannel::num_instances; - CAChannel::CAChannel(std::string const & channelName, CAChannelProvider::shared_pointer const & channelProvider, ChannelRequester::shared_pointer const & channelRequester) : @@ -402,9 +401,6 @@ void CAChannel::attachContext() throw std::runtime_error(mess); } - -size_t CAChannelGet::num_instances; - CAChannelGetPtr CAChannelGet::create( CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, @@ -422,7 +418,9 @@ CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & channel, : channel(channel), channelGetRequester(channelGetRequester), - pvRequest(pvRequest) + pvRequest(pvRequest), + getStatus(Status::Ok), + getDoneThread(GetDoneThread::get()) {} CAChannelGet::~CAChannelGet() @@ -442,10 +440,14 @@ void CAChannelGet::activate() dbdToPv = DbdToPv::create(channel,pvRequest,getIO); pvStructure = dbdToPv->createPVStructure(); bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields())); + notifyGetRequester = NotifyGetRequesterPtr(new NotifyGetRequester()); + notifyGetRequester->setChannelGet(shared_from_this()); EXCEPTION_GUARD(getRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } + + std::string CAChannelGet::getRequesterName() { return "CAChannelGet";} namespace { @@ -467,8 +469,15 @@ void CAChannelGet::getDone(struct event_handler_args &args) ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); if(!getRequester) return; - Status status = dbdToPv->getFromDBD(pvStructure,bitSet,args); - EXCEPTION_GUARD(getRequester->getDone(status, shared_from_this(), pvStructure, bitSet)); + getStatus = dbdToPv->getFromDBD(pvStructure,bitSet,args); + getDoneThread->getDone(notifyGetRequester); +} + +void CAChannelGet::notifyClient() +{ + ChannelGetRequester::shared_pointer getRequester(channelGetRequester.lock()); + if(!getRequester) return; + EXCEPTION_GUARD(getRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet)); } void CAChannelGet::get() @@ -491,7 +500,8 @@ void CAChannelGet::get() { string mess("CAChannelGet::get "); mess += channel->getChannelName() + " message " + ca_message(result); - throw std::runtime_error(mess); + getStatus = Status(Status::STATUSTYPE_ERROR,mess); + notifyClient(); } } @@ -508,8 +518,6 @@ void CAChannelGet::lastRequest() { } -size_t CAChannelPut::num_instances; - CAChannelPutPtr CAChannelPut::create( CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, @@ -528,7 +536,11 @@ CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel, channel(channel), channelPutRequester(channelPutRequester), pvRequest(pvRequest), - block(false) + block(false), + isPut(false), + getStatus(Status::Ok), + putStatus(Status::Ok), + putDoneThread(PutDoneThread::get()) {} CAChannelPut::~CAChannelPut() @@ -554,6 +566,8 @@ void CAChannelPut::activate() std::string val = pvString->get(); if(val.compare("true")==0) block = true; } + notifyPutRequester = NotifyPutRequesterPtr(new NotifyPutRequester()); + notifyPutRequester->setChannelPut(shared_from_this()); EXCEPTION_GUARD(putRequester->channelPutConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } @@ -565,6 +579,12 @@ std::string CAChannelPut::getRequesterName() { return "CAChannelPut";} namespace { +static void ca_put_handler(struct event_handler_args args) +{ + CAChannelPut *channelPut = static_cast(args.usr); + channelPut->putDone(args); +} + static void ca_put_get_handler(struct event_handler_args args) { CAChannelPut *channelPut = static_cast(args.usr); @@ -582,11 +602,33 @@ void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure, } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - Status status = dbdToPv->putToDBD(channel,pvPutStructure,block); - EXCEPTION_GUARD(putRequester->putDone(status, shared_from_this())); + { + Lock lock(mutex); + isPut = true; + } + putStatus = dbdToPv->putToDBD(channel,pvPutStructure,block,&ca_put_handler,this); + if(!block || !putStatus.isOK()) { + EXCEPTION_GUARD(putRequester->putDone(putStatus, shared_from_this())); + } } +void CAChannelPut::putDone(struct event_handler_args &args) +{ + if(DEBUG_LEVEL>1) { + cout << "CAChannelPut::putDone " << channel->getChannelName() << endl; + } + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); + if(!putRequester) return; + if(args.status!=ECA_NORMAL) + { + putStatus = Status(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); + } else { + putStatus = Status::Ok; + } + putDoneThread->putDone(notifyPutRequester); +} + void CAChannelPut::getDone(struct event_handler_args &args) { if(DEBUG_LEVEL>1) { @@ -595,8 +637,19 @@ void CAChannelPut::getDone(struct event_handler_args &args) ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; - Status status = dbdToPv->getFromDBD(pvStructure,bitSet,args); - EXCEPTION_GUARD(putRequester->getDone(status, shared_from_this(), pvStructure, bitSet)); + getStatus = dbdToPv->getFromDBD(pvStructure,bitSet,args); + putDoneThread->putDone(notifyPutRequester); +} + +void CAChannelPut::notifyClient() +{ + ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); + if(!putRequester) return; + if(isPut) { + EXCEPTION_GUARD(putRequester->putDone(putStatus, shared_from_this())); + } else { + EXCEPTION_GUARD(putRequester->getDone(getStatus, shared_from_this(), pvStructure, bitSet)); + } } @@ -607,6 +660,11 @@ void CAChannelPut::get() } ChannelPutRequester::shared_pointer putRequester(channelPutRequester.lock()); if(!putRequester) return; + { + Lock lock(mutex); + isPut = false; + } + channel->attachContext(); bitSet->clear(); int result = ca_array_get_callback(dbdToPv->getRequestType(), @@ -620,7 +678,8 @@ void CAChannelPut::get() { string mess("CAChannelPut::get "); mess += channel->getChannelName() + " message " +ca_message(result); - throw std::runtime_error(mess); + Status status(Status::STATUSTYPE_ERROR,mess); + EXCEPTION_GUARD(putRequester->getDone(status, shared_from_this(), pvStructure, bitSet)); } } @@ -716,8 +775,6 @@ public: } }; -size_t CAChannelMonitor::num_instances; - CAChannelMonitorPtr CAChannelMonitor::create( CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, @@ -775,9 +832,8 @@ void CAChannelMonitor::activate() if (size > 1) queueSize = size; } } - notifyRequester = NotifyRequesterPtr(new NotifyRequester()); - - notifyRequester->setChannelMonitor(shared_from_this()); + notifyMonitorRequester = NotifyMonitorRequesterPtr(new NotifyMonitorRequester()); + notifyMonitorRequester->setChannelMonitor(shared_from_this()); monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize)); EXCEPTION_GUARD(requester->monitorConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); @@ -806,7 +862,7 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) } else { *(activeElement->overrunBitSet) |= *(activeElement->changedBitSet); } - monitorEventThread->event(notifyRequester); + monitorEventThread->event(notifyMonitorRequester); } else { @@ -817,6 +873,18 @@ void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) } } + +void CAChannelMonitor::notifyClient() +{ + { + Lock lock(mutex); + if(!isStarted) return; + } + MonitorRequester::shared_pointer requester(monitorRequester.lock()); + if(!requester) return; + requester->monitorEvent(shared_from_this()); +} + Status CAChannelMonitor::start() { if(DEBUG_LEVEL>0) { @@ -867,17 +935,6 @@ Status CAChannelMonitor::stop() return Status(Status::STATUSTYPE_ERROR,string(ca_message(result))); } -void CAChannelMonitor::notifyClient() -{ - { - Lock lock(mutex); - if(!isStarted) return; - } - MonitorRequester::shared_pointer requester(monitorRequester.lock()); - if(!requester) return; - requester->monitorEvent(shared_from_this()); -} - MonitorElementPtr CAChannelMonitor::poll() { diff --git a/src/ca/caChannel.h b/src/ca/caChannel.h index ae641c0..8228627 100644 --- a/src/ca/caChannel.h +++ b/src/ca/caChannel.h @@ -4,6 +4,11 @@ * in file LICENSE that is included with this distribution. */ +/** + * @author msekoranja, mrk + * @date 2018.07 + */ + #ifndef CACHANNEL_H #define CACHANNEL_H @@ -23,12 +28,22 @@ namespace epics { namespace pvAccess { namespace ca { -class NotifyRequester; -typedef std::tr1::shared_ptr NotifyRequesterPtr; - +class NotifyMonitorRequester; +typedef std::tr1::shared_ptr NotifyMonitorRequesterPtr; class MonitorEventThread; typedef std::tr1::shared_ptr MonitorEventThreadPtr; +class NotifyGetRequester; +typedef std::tr1::shared_ptr NotifyGetRequesterPtr; +typedef std::tr1::weak_ptr NotifyGetRequesterWPtr; +class GetDoneThread; +typedef std::tr1::shared_ptr GetDoneThreadPtr; + +class NotifyPutRequester; +typedef std::tr1::shared_ptr NotifyPutRequesterPtr; +typedef std::tr1::weak_ptr NotifyPutRequesterWPtr; +class PutDoneThread; +typedef std::tr1::shared_ptr PutDoneThreadPtr; class CAChannelGetField; typedef std::tr1::shared_ptr CAChannelGetFieldPtr; @@ -62,7 +77,6 @@ class CAChannel : { public: POINTER_DEFINITIONS(CAChannel); - static size_t num_instances; static CAChannelPtr create( CAChannelProvider::shared_pointer const & channelProvider, std::string const & channelName, @@ -123,7 +137,6 @@ class CAChannelGet : { public: POINTER_DEFINITIONS(CAChannelGet); - static size_t num_instances; static CAChannelGet::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructurePtr const & pvRequest); @@ -136,7 +149,7 @@ public: virtual std::string getRequesterName(); void activate(); - + void notifyClient(); private: virtual void destroy() {} CAChannelGet(CAChannel::shared_pointer const & _channel, @@ -146,7 +159,11 @@ private: CAChannelPtr channel; ChannelGetRequester::weak_pointer channelGetRequester; epics::pvData::PVStructurePtr const & pvRequest; + epics::pvData::Status getStatus; + GetDoneThreadPtr getDoneThread; + NotifyGetRequesterPtr notifyGetRequester; DbdToPvPtr dbdToPv; + epics::pvData::Mutex mutex; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; @@ -158,11 +175,11 @@ class CAChannelPut : public: POINTER_DEFINITIONS(CAChannelPut); - static size_t num_instances; static CAChannelPut::shared_pointer create(CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructurePtr const & pvRequest); virtual ~CAChannelPut(); + void putDone(struct event_handler_args &args); void getDone(struct event_handler_args &args); virtual void put( epics::pvData::PVStructure::shared_pointer const & pvPutStructure, @@ -175,6 +192,7 @@ public: virtual std::string getRequesterName(); void activate(); + void notifyClient(); private: virtual void destroy() {} CAChannelPut(CAChannel::shared_pointer const & _channel, @@ -184,7 +202,13 @@ private: ChannelPutRequester::weak_pointer channelPutRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool block; + bool isPut; + epics::pvData::Status getStatus; + epics::pvData::Status putStatus; + PutDoneThreadPtr putDoneThread; + NotifyPutRequesterPtr notifyPutRequester; DbdToPvPtr dbdToPv; + epics::pvData::Mutex mutex; epics::pvData::PVStructure::shared_pointer pvStructure; epics::pvData::BitSet::shared_pointer bitSet; }; @@ -199,7 +223,6 @@ class CAChannelMonitor : public: POINTER_DEFINITIONS(CAChannelMonitor); - static size_t num_instances; static CAChannelMonitor::shared_pointer create(CAChannel::shared_pointer const & channel, MonitorRequester::shared_pointer const & monitorRequester, epics::pvData::PVStructurePtr const & pvRequest); @@ -223,9 +246,9 @@ private: MonitorRequester::weak_pointer monitorRequester; const epics::pvData::PVStructure::shared_pointer pvRequest; bool isStarted; - NotifyRequesterPtr notifyRequester; MonitorEventThreadPtr monitorEventThread; evid pevid; + NotifyMonitorRequesterPtr notifyMonitorRequester; DbdToPvPtr dbdToPv; epics::pvData::Mutex mutex; @@ -235,8 +258,6 @@ private: CACMonitorQueuePtr monitorQueue; }; -} -} -} +}}} #endif /* CACHANNEL_H */ diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index 27386e2..5c5b22c 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -4,18 +4,16 @@ * in file LICENSE that is included with this distribution. */ -#include - #include #include #include #include #include -#include #include -#include #include "monitorEventThread.h" +#include "getDoneThread.h" +#include "putDoneThread.h" #define epicsExportSharedSymbols #include @@ -33,8 +31,6 @@ using namespace epics::pvData; catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \ catch (...) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__); } -size_t CAChannelProvider::num_instances; - CAChannelProvider::CAChannelProvider() : current_context(0) { @@ -43,7 +39,9 @@ CAChannelProvider::CAChannelProvider() CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&) : current_context(0), - monitorEventThread(MonitorEventThread::get()) + monitorEventThread(MonitorEventThread::get()), + getDoneThread(GetDoneThread::get()), + putDoneThread(PutDoneThread::get()) { if(DEBUG_LEVEL>0) { std::cout<< "CAChannelProvider::CAChannelProvider\n"; @@ -78,11 +76,13 @@ CAChannelProvider::~CAChannelProvider() channelQ.pop(); } monitorEventThread->stop(); + getDoneThread->stop(); + putDoneThread->stop(); if(DEBUG_LEVEL>0) { std::cout << "CAChannelProvider::~CAChannelProvider() calling ca_context_destroy\n"; } ca_context_destroy(); -std::cout << "CAChannelProvider::~CAChannelProvider() returning\n"; +//std::cout << "CAChannelProvider::~CAChannelProvider() returning\n"; } std::string CAChannelProvider::getProviderName() @@ -197,11 +197,6 @@ void CAChannelProvider::initialize() current_context = ca_current_context(); } -ca_client_context * CAChannelProvider::get_ca_client_context() -{ - return current_context; -} - void CAClientFactory::start() { if(DEBUG_LEVEL>0) std::cout << "CAClientFactory::start()\n"; @@ -210,29 +205,12 @@ void CAClientFactory::start() } epicsSignalInstallSigAlarmIgnore(); epicsSignalInstallSigPipeIgnore(); - registerRefCounter("CAChannelProvider", &CAChannelProvider::num_instances); - registerRefCounter("CAChannel", &CAChannel::num_instances); - registerRefCounter("CAChannelGet", &CAChannelGet::num_instances); - registerRefCounter("CAChannelPut", &CAChannelPut::num_instances); - registerRefCounter("CAChannelMonitor", &CAChannelMonitor::num_instances); - if(!ChannelProviderRegistry::clients()->add("ca", true)) { throw std::runtime_error("CAClientFactory::start failed"); } } -ca_client_context * CAClientFactory::get_ca_client_context() -{ - if(DEBUG_LEVEL>0) std::cout << "CAClientFactory::get_ca_client_context\n"; - ChannelProvider::shared_pointer channelProvider( - ChannelProviderRegistry::clients()->getProvider("ca")); - if(!channelProvider) throw std::runtime_error("CAClientFactory::start() was not called"); - CAChannelProviderPtr cacChannelProvider - = std::tr1::static_pointer_cast(channelProvider); - return cacChannelProvider->get_ca_client_context(); -} - void CAClientFactory::stop() { // unregister now done with exit hook diff --git a/src/ca/caProviderPvt.h b/src/ca/caProviderPvt.h index af0c4d9..0d48271 100644 --- a/src/ca/caProviderPvt.h +++ b/src/ca/caProviderPvt.h @@ -4,6 +4,11 @@ * in file LICENSE that is included with this distribution. */ +/** + * @author msekoranja, mrk + * @date 2018.07 + */ + #ifndef CAPROVIDERPVT_H #define CAPROVIDERPVT_H @@ -22,6 +27,11 @@ namespace ca { class MonitorEventThread; typedef std::tr1::shared_ptr MonitorEventThreadPtr; +class GetDoneThread; +typedef std::tr1::shared_ptr GetDoneThreadPtr; + +class PutDoneThread; +typedef std::tr1::shared_ptr PutDoneThreadPtr; class CAChannel; typedef std::tr1::shared_ptr CAChannelPtr; @@ -37,9 +47,6 @@ class CAChannelProvider : { public: POINTER_DEFINITIONS(CAChannelProvider); - - static size_t num_instances; - CAChannelProvider(); CAChannelProvider(const std::tr1::shared_ptr&); virtual ~CAChannelProvider(); @@ -72,7 +79,6 @@ public: void attachContext(); void addChannel(const CAChannelPtr & channel); - ca_client_context* get_ca_client_context(); private: virtual void destroy() EPICS_DEPRECATED {} @@ -81,6 +87,8 @@ private: epics::pvData::Mutex channelListMutex; std::vector caChannelList; MonitorEventThreadPtr monitorEventThread; + GetDoneThreadPtr getDoneThread; + PutDoneThreadPtr putDoneThread; }; }}} diff --git a/src/ca/dbdToPv.cpp b/src/ca/dbdToPv.cpp index f8fe8ca..636dc5e 100644 --- a/src/ca/dbdToPv.cpp +++ b/src/ca/dbdToPv.cpp @@ -48,12 +48,6 @@ static void descriptionHandler(struct event_handler_args args) dbdToPv->getDescriptionDone(args); } -static void putHandler(struct event_handler_args args) -{ - DbdToPv *dbdToPv = static_cast(args.usr); - dbdToPv->putDone(args); -} - DbdToPvPtr DbdToPv::create( CAChannelPtr const & caChannel, PVStructurePtr const & pvRequest, @@ -74,6 +68,8 @@ DbdToPv::DbdToPv(IOType ioType) valueAlarmRequested(false), isArray(false), firstTime(true), + choicesValid(false), + waitForChoicesValid(false), caValueType(-1), caRequestType(-1), maxElements(0) @@ -292,6 +288,13 @@ 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(); } chtype DbdToPv::getRequestType() @@ -685,7 +688,9 @@ const void * put_DBRScalarArray(unsigned long*count, PVScalarArray::shared_point Status DbdToPv::putToDBD( CAChannelPtr const & caChannel, PVStructurePtr const & pvStructure, - bool block) + bool block, + caCallbackFunc putHandler, + void * userarg) { chid channelID = caChannel->getChannelID(); const void *pValue = NULL; @@ -745,6 +750,23 @@ 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; @@ -761,40 +783,21 @@ Status DbdToPv::putToDBD( return errorStatus; } } + Status status = Status::Ok; int result = 0; + caChannel->attachContext(); if(block) { - caChannel->attachContext(); - result = ca_array_put_callback(caValueType,count,channelID,pValue,putHandler,this); - if(result==ECA_NORMAL) { - ca_flush_io(); - if(!waitForCallback.wait(2.0)) { - throw std::runtime_error("DbDToPv::putToDBD waitForCallback timeout"); - } - return putStatus; - } + result = ca_array_put_callback(caValueType,count,channelID,pValue,putHandler,userarg); } else { - caChannel->attachContext(); result = ca_array_put(caValueType,count,channelID,pValue); - ca_flush_io(); + } + if(result==ECA_NORMAL) { + ca_flush_io(); + } else { + status = Status(Status::STATUSTYPE_ERROR, string(ca_message(result))); } if(ca_stringBuffer!=NULL) delete[] ca_stringBuffer; - if(result==ECA_NORMAL) return Status::Ok; - Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result))); - return errorStatus; -} - -void DbdToPv::putDone(struct event_handler_args &args) -{ - if(args.status!=ECA_NORMAL) - { - string message("DbdToPv::putDone ca_message "); - message += ca_message(args.status); - putStatus = Status(Status::STATUSTYPE_ERROR, string(ca_message(args.status))); - } else { - putStatus = Status::Ok; - } - waitForCallback.signal(); -} - + return status; +} }}} diff --git a/src/ca/dbdToPv.h b/src/ca/dbdToPv.h index dea4a4b..5ee77b5 100644 --- a/src/ca/dbdToPv.h +++ b/src/ca/dbdToPv.h @@ -35,40 +35,46 @@ typedef std::tr1::shared_ptr ValueAlarmDbdPtr; struct CaAlarm { - CaAlarm() : status(0), severity(0) {} dbr_short_t status; dbr_short_t severity; + CaAlarm() : status(0), severity(0) {} }; struct CaDisplay { - CaDisplay() : lower_disp_limit(0),upper_disp_limit(0) {} double lower_disp_limit; double upper_disp_limit; std::string units; std::string format; + CaDisplay() : lower_disp_limit(0),upper_disp_limit(0) {} }; struct CaControl { - CaControl() : upper_ctrl_limit(0),lower_ctrl_limit(0) {} double upper_ctrl_limit; double lower_ctrl_limit; + CaControl() : upper_ctrl_limit(0),lower_ctrl_limit(0) {} }; struct CaValueAlarm { - CaValueAlarm() : upper_alarm_limit(0),upper_warning_limit(0),lower_warning_limit(0),lower_alarm_limit(0) - {} double upper_alarm_limit; double upper_warning_limit; double lower_warning_limit; double lower_alarm_limit; + CaValueAlarm() : + upper_alarm_limit(0), + upper_warning_limit(0), + lower_warning_limit(0), + lower_alarm_limit(0) + {} }; class DbdToPv; typedef std::tr1::shared_ptr DbdToPvPtr; +typedef void ( caCallbackFunc ) (struct event_handler_args); + /** * @brief DbdToPv converts between DBD data and pvData. * @@ -93,12 +99,13 @@ public: epics::pvData::Status putToDBD( CAChannelPtr const & caChannel, epics::pvData::PVStructurePtr const & pvStructure, - bool block + bool block, + caCallbackFunc putHandler, + void *userArg ); void getChoicesDone(struct event_handler_args &args); void descriptionConnected(struct connection_handler_args args); void getDescriptionDone(struct event_handler_args &args); - void putDone(struct event_handler_args &args); private: DbdToPv(IOType ioType); void activate( @@ -114,17 +121,19 @@ 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::Event waitForCallback; - epics::pvData::Status putStatus; epics::pvData::Structure::const_shared_pointer structure; std::vector choices; }; diff --git a/src/ca/getDoneThread.cpp b/src/ca/getDoneThread.cpp new file mode 100644 index 0000000..ae01f1a --- /dev/null +++ b/src/ca/getDoneThread.cpp @@ -0,0 +1,114 @@ +/** + * 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 "getDoneThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +GetDoneThreadPtr GetDoneThread::get() +{ + static GetDoneThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = GetDoneThreadPtr(new GetDoneThread()); + master->start(); + } + return master; +} + +GetDoneThread::GetDoneThread() +: isStop(false) +{ +} + +GetDoneThread::~GetDoneThread() +{ +//std::cout << "GetDoneThread::~GetDoneThread()\n"; +} + + +void GetDoneThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "getDoneThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + + +void GetDoneThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + +void GetDoneThread::getDone(NotifyGetRequesterPtr const ¬ifyGetRequester) +{ + { + Lock lock(mutex); + if(notifyGetRequester->isOnQueue) return; + notifyGetRequester->isOnQueue = true; + notifyGetQueue.push(notifyGetRequester); + } + waitForCommand.signal(); +} + +void GetDoneThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyGetRequester* notifyGetRequester(NULL); + { + Lock lock(mutex); + if(!notifyGetQueue.empty()) + { + more = true; + NotifyGetRequesterWPtr req(notifyGetQueue.front()); + notifyGetQueue.pop(); + NotifyGetRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyGetRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyGetRequester!=NULL) + { + CAChannelGetPtr channelGet(notifyGetRequester->channelGet.lock()); + if(channelGet) channelGet->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/getDoneThread.h b/src/ca/getDoneThread.h new file mode 100644 index 0000000..6522f0a --- /dev/null +++ b/src/ca/getDoneThread.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 GetDoneThread_H +#define GetDoneThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyGetRequester; +typedef std::tr1::shared_ptr NotifyGetRequesterPtr; +typedef std::tr1::weak_ptr NotifyGetRequesterWPtr; + + +class GetDoneThread; +typedef std::tr1::shared_ptr GetDoneThreadPtr; + +class CAChannelGet; +typedef std::tr1::shared_ptr CAChannelGetPtr; +typedef std::tr1::weak_ptr CAChannelGetWPtr; + +class NotifyGetRequester +{ +public: + ChannelGetRequester::weak_pointer channelGetRequester; + CAChannelGetWPtr channelGet; + bool isOnQueue; + NotifyGetRequester() : isOnQueue(false) {} + void setChannelGet(CAChannelGetPtr const &channelGet) + { this->channelGet = channelGet;} +}; + + +class GetDoneThread : + public epicsThreadRunable +{ +public: + static GetDoneThreadPtr get(); + ~GetDoneThread(); + virtual void run(); + void start(); + void stop(); + void getDone(NotifyGetRequesterPtr const ¬ifyGetRequester); +private: + GetDoneThread(); + + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyGetQueue; +}; + + +}}} + +#endif /* GetDoneThread_H */ diff --git a/src/ca/monitorEventThread.cpp b/src/ca/monitorEventThread.cpp index e95d101..d74cbd1 100644 --- a/src/ca/monitorEventThread.cpp +++ b/src/ca/monitorEventThread.cpp @@ -39,7 +39,7 @@ MonitorEventThread::MonitorEventThread() MonitorEventThread::~MonitorEventThread() { -std::cout << "MonitorEventThread::~MonitorEventThread()\n"; +//std::cout << "MonitorEventThread::~MonitorEventThread()\n"; } void MonitorEventThread::start() @@ -63,13 +63,13 @@ void MonitorEventThread::stop() } -void MonitorEventThread::event(NotifyRequesterPtr const &stopMonitor) +void MonitorEventThread::event(NotifyMonitorRequesterPtr const ¬ifyMonitorRequester) { { Lock lock(mutex); - if(stopMonitor->isOnQueue) return; - stopMonitor->isOnQueue = true; - notifyMonitorQueue.push(stopMonitor); + if(notifyMonitorRequester->isOnQueue) return; + notifyMonitorRequester->isOnQueue = true; + notifyMonitorQueue.push(notifyMonitorRequester); } waitForCommand.signal(); } @@ -81,25 +81,25 @@ void MonitorEventThread::run() waitForCommand.wait(); while(true) { bool more = false; - NotifyRequester* notifyRequester(NULL); + NotifyMonitorRequester* notifyMonitorRequester(NULL); { Lock lock(mutex); if(!notifyMonitorQueue.empty()) { more = true; - NotifyRequesterWPtr req(notifyMonitorQueue.front()); + NotifyMonitorRequesterWPtr req(notifyMonitorQueue.front()); notifyMonitorQueue.pop(); - NotifyRequesterPtr reqPtr(req.lock()); + NotifyMonitorRequesterPtr reqPtr(req.lock()); if(reqPtr) { - notifyRequester = reqPtr.get(); + notifyMonitorRequester = reqPtr.get(); reqPtr->isOnQueue = false; } } } if(!more) break; - if(notifyRequester!=NULL) + if(notifyMonitorRequester!=NULL) { - CAChannelMonitorPtr channelMonitor(notifyRequester->channelMonitor.lock()); + CAChannelMonitorPtr channelMonitor(notifyMonitorRequester->channelMonitor.lock()); if(channelMonitor) channelMonitor->notifyClient(); } } diff --git a/src/ca/monitorEventThread.h b/src/ca/monitorEventThread.h index 0784d07..0a03ee7 100644 --- a/src/ca/monitorEventThread.h +++ b/src/ca/monitorEventThread.h @@ -20,9 +20,9 @@ namespace epics { namespace pvAccess { namespace ca { -class NotifyRequester; -typedef std::tr1::shared_ptr NotifyRequesterPtr; -typedef std::tr1::weak_ptr NotifyRequesterWPtr; +class NotifyMonitorRequester; +typedef std::tr1::shared_ptr NotifyMonitorRequesterPtr; +typedef std::tr1::weak_ptr NotifyMonitorRequesterWPtr; class MonitorEventThread; @@ -32,13 +32,13 @@ class CAChannelMonitor; typedef std::tr1::shared_ptr CAChannelMonitorPtr; typedef std::tr1::weak_ptr CAChannelMonitorWPtr; -class NotifyRequester +class NotifyMonitorRequester { public: MonitorRequester::weak_pointer monitorRequester; CAChannelMonitorWPtr channelMonitor; bool isOnQueue; - NotifyRequester() : isOnQueue(false) {} + NotifyMonitorRequester() : isOnQueue(false) {} void setChannelMonitor(CAChannelMonitorPtr const &channelMonitor) { this->channelMonitor = channelMonitor;} }; @@ -53,7 +53,7 @@ public: virtual void run(); void start(); void stop(); - void event(NotifyRequesterPtr const ¬ifyRequester); + void event(NotifyMonitorRequesterPtr const ¬ifyMonitorRequester); private: MonitorEventThread(); @@ -62,7 +62,7 @@ private: epics::pvData::Mutex mutex; epics::pvData::Event waitForCommand; epics::pvData::Event waitForStop; - std::queue notifyMonitorQueue; + std::queue notifyMonitorQueue; }; diff --git a/src/ca/putDoneThread.cpp b/src/ca/putDoneThread.cpp new file mode 100644 index 0000000..79eb6e9 --- /dev/null +++ b/src/ca/putDoneThread.cpp @@ -0,0 +1,114 @@ +/** + * 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 "putDoneThread.h" + +using namespace epics::pvData; +using namespace std; + +namespace epics { +namespace pvAccess { +namespace ca { + +PutDoneThreadPtr PutDoneThread::get() +{ + static PutDoneThreadPtr master; + static Mutex mutex; + Lock xx(mutex); + if(!master) { + master = PutDoneThreadPtr(new PutDoneThread()); + master->start(); + } + return master; +} + +PutDoneThread::PutDoneThread() +: isStop(false) +{ +} + +PutDoneThread::~PutDoneThread() +{ +//std::cout << "PutDoneThread::~PutDoneThread()\n"; +} + + +void PutDoneThread::start() +{ + thread = std::tr1::shared_ptr(new epicsThread( + *this, + "putDoneThread", + epicsThreadGetStackSize(epicsThreadStackSmall), + epicsThreadPriorityLow)); + thread->start(); +} + + +void PutDoneThread::stop() +{ + { + Lock xx(mutex); + isStop = true; + } + waitForCommand.signal(); + waitForStop.wait(); +} + +void PutDoneThread::putDone(NotifyPutRequesterPtr const ¬ifyPutRequester) +{ + { + Lock lock(mutex); + if(notifyPutRequester->isOnQueue) return; + notifyPutRequester->isOnQueue = true; + notifyPutQueue.push(notifyPutRequester); + } + waitForCommand.signal(); +} + +void PutDoneThread::run() +{ + while(true) + { + waitForCommand.wait(); + while(true) { + bool more = false; + NotifyPutRequester* notifyPutRequester(NULL); + { + Lock lock(mutex); + if(!notifyPutQueue.empty()) + { + more = true; + NotifyPutRequesterWPtr req(notifyPutQueue.front()); + notifyPutQueue.pop(); + NotifyPutRequesterPtr reqPtr(req.lock()); + if(reqPtr) { + notifyPutRequester = reqPtr.get(); + reqPtr->isOnQueue = false; + } + } + } + if(!more) break; + if(notifyPutRequester!=NULL) + { + CAChannelPutPtr channelPut(notifyPutRequester->channelPut.lock()); + if(channelPut) channelPut->notifyClient(); + } + } + if(isStop) { + waitForStop.signal(); + break; + } + } +} + +}}} diff --git a/src/ca/putDoneThread.h b/src/ca/putDoneThread.h new file mode 100644 index 0000000..bab0f21 --- /dev/null +++ b/src/ca/putDoneThread.h @@ -0,0 +1,70 @@ +/** + * 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 PutDoneThread_H +#define PutDoneThread_H +#include +#include +#include +#include +#include +#include + +namespace epics { +namespace pvAccess { +namespace ca { + +class NotifyPutRequester; +typedef std::tr1::shared_ptr NotifyPutRequesterPtr; +typedef std::tr1::weak_ptr NotifyPutRequesterWPtr; + + +class PutDoneThread; +typedef std::tr1::shared_ptr PutDoneThreadPtr; + +class CAChannelPut; +typedef std::tr1::shared_ptr CAChannelPutPtr; +typedef std::tr1::weak_ptr CAChannelPutWPtr; + +class NotifyPutRequester +{ +public: + ChannelPutRequester::weak_pointer channelPutRequester; + CAChannelPutWPtr channelPut; + bool isOnQueue; + NotifyPutRequester() : isOnQueue(false) {} + void setChannelPut(CAChannelPutPtr const &channelPut) + { this->channelPut = channelPut;} +}; + + +class PutDoneThread : + public epicsThreadRunable +{ +public: + static PutDoneThreadPtr get(); + ~PutDoneThread(); + virtual void run(); + void start(); + void stop(); + void putDone(NotifyPutRequesterPtr const ¬ifyPutRequester); +private: + PutDoneThread(); + bool isStop; + std::tr1::shared_ptr thread; + epics::pvData::Mutex mutex; + epics::pvData::Event waitForCommand; + epics::pvData::Event waitForStop; + std::queue notifyPutQueue; +}; + + +}}} + +#endif /* PutDoneThread_H */ diff --git a/src/ca/pv/caProvider.h b/src/ca/pv/caProvider.h index 2a18acd..90b880e 100644 --- a/src/ca/pv/caProvider.h +++ b/src/ca/pv/caProvider.h @@ -3,6 +3,9 @@ * pvAccessCPP is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ +/** + * @author msekoranja + */ #ifndef CAPROVIDER_H #define CAPROVIDER_H @@ -22,10 +25,17 @@ namespace ca { * A single instance is created the first time CAClientFactory::start is called. * epicsAtExit is used to destroy the instance. * + * The single instance calls: + * ca_context_create(ca_enable_preemptive_callback); + * * The thread that calls start, or a ca auxillary thread, are the only threads * that can call the ca_* functions. * - * Note the monitor callbacks are made from a separate thread that must NOT call any ca_* function. + * NOTE: callbacks for monitor, get, and put are made from a separate thread. + * This is done to prevent a deadly embrace that can occur + * when rapid gets, puts, and monitor events are happening. + * The callbacks should not call any pvAccess method. + * If any such call is made the separate thread becomes a ca auxillary thread. * */ class epicsShareClass CAClientFactory @@ -35,12 +45,6 @@ public: * */ 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 struct ca_client_context * get_ca_client_context(); /** @brief stop provider ca * * This does nothing since epicsAtExit is used to destroy the instance.