diff --git a/pvAccessApp/ca/caChannel.cpp b/pvAccessApp/ca/caChannel.cpp index e59c408..7033836 100644 --- a/pvAccessApp/ca/caChannel.cpp +++ b/pvAccessApp/ca/caChannel.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include using namespace epics::pvData; using namespace epics::pvAccess; @@ -54,38 +54,80 @@ static ScalarType dbr2ST[] = pvDouble // DBR_DOUBLE = 6 }; - -static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, String const & properties) +static Structure::const_shared_pointer createStructure(CAChannel::shared_pointer const & channel, String const & properties) { - // TODO - StandardPVFieldPtr standardPVField = getStandardPVField(); - PVStructure::shared_pointer pvStructure; + StandardFieldPtr standardField = getStandardField(); + Structure::const_shared_pointer structure; chtype channelType = channel->getNativeType(); if (channelType != DBR_ENUM) { ScalarType st = dbr2ST[channelType]; - pvStructure = (channel->getElementCount() > 1) ? - standardPVField->scalarArray(st, properties) : - standardPVField->scalar(st, properties); + structure = (channel->getElementCount() > 1) ? + standardField->scalarArray(st, properties) : + standardField->scalar(st, properties); } else { - // enum arrays not supported + // 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); - // introduce ackConnected(pvStructure), if non-enum directly call, else when labels are retrieved StringArray labels; - pvStructure = standardPVField->enumerated(labels, properties); + labels.reserve(dbr_enum_p->no_str); + for (dbr_short_t i = 0; i < dbr_enum_p->no_str; i++) + labels.push_back(dbr_enum_p->strs[i]); + + PVStringArray* labelsArray = static_cast(args.usr); + labelsArray->put(0, labels.size(), labels, 0); + } + else + { + // TODO better error handling + std::cerr << "failed to get labels for enum : " << ca_message(args.status) << std::endl; + } +} + +static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, String const & properties) +{ + PVStructure::shared_pointer pvStructure = getPVDataCreate()->createPVStructure(createStructure(channel, properties)); + if (channel->getNativeType() == DBR_ENUM) + { + + PVScalarArrayPtr pvScalarArray = pvStructure->getScalarArrayField("value.choices", pvString); + + // 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) + { + 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 + } + else + { + // TODO better error handling + std::cerr << "failed to get labels for enum " << channel->getChannelName() << ": " << ca_message(result) << std::endl; + } } return pvStructure; } - static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, chtype dbrType) { - // TODO constants - // TODO value is always there + // NOTE: value is always there String properties; if (dbrType >= DBR_CTRL_STRING) // 28 { @@ -124,11 +166,11 @@ void CAChannel::connected() (channelType != DBR_STRING && channelType != DBR_ENUM) ? "value,timeStamp,alarm,display,valueAlarm,control" : "value,timeStamp,alarm"; - PVStructure::shared_pointer pvStructure = createPVStructure(shared_from_this(), allProperties); + Structure::const_shared_pointer structure = createStructure(shared_from_this(), allProperties); // TODO thread sync // TODO we need only Structure here - this->pvStructure = pvStructure; + this->structure = structure; // TODO call channelCreated if structure has changed EXCEPTION_GUARD(channelRequester->channelStateChange(shared_from_this(), Channel::CONNECTED)); @@ -192,11 +234,6 @@ unsigned CAChannel::getElementCount() return elementCount; } -PVStructure::shared_pointer CAChannel::getPVStructure() -{ - return pvStructure; -} - std::tr1::shared_ptr CAChannel::getProvider() { @@ -245,14 +282,14 @@ bool CAChannel::isConnected() void CAChannel::getField(GetFieldRequester::shared_pointer const & requester, epics::pvData::String const & subField) { - PVField::shared_pointer pvField = - subField.empty() ? - std::tr1::static_pointer_cast(pvStructure) : - pvStructure->getSubField(subField); + Field::const_shared_pointer field = + subField.empty() ? + std::tr1::static_pointer_cast(structure) : + structure->getField(subField); - if (pvField) + if (field) { - EXCEPTION_GUARD(requester->getDone(Status::Ok, pvField->getField())); + EXCEPTION_GUARD(requester->getDone(Status::Ok, field)); } else { @@ -383,11 +420,33 @@ void CAChannel::message(String const & message,MessageType messageType) void CAChannel::destroy() { - // TODO + Lock lock(requestsMutex); + { + while (!requests.empty()) + { + ChannelRequest::shared_pointer request = requests.rbegin()->second.lock(); + if (request) + request->destroy(); + } + } + + /* Clear CA Channel */ + ca_clear_channel(channelID); } +/* ---------------------------------------------------------- */ +void CAChannel::registerRequest(ChannelRequest::shared_pointer const & request) +{ + Lock lock(requestsMutex); + requests[request.get()] = ChannelRequest::weak_pointer(request); +} +void CAChannel::unregisterRequest(ChannelRequest::shared_pointer const & request) +{ + Lock lock(requestsMutex); + requests.erase(request.get()); +} @@ -678,6 +737,17 @@ void copy_DBR_GR(const void * dbr, unsigned count, PVStructure::shared_pointer c 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) @@ -708,6 +778,18 @@ void copy_DBR_CTRL(const void * dbr, unsigned count, PVStructure::shared_pointer 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 @@ -737,7 +819,7 @@ static copyDBRtoPVStructure copyFuncTable[] = 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_STS, // DBR_GR_ENUM -> DBR_STS_ENUM + copy_DBR_GR, // DBR_GR_ENUM copy_DBR_GR, // DBR_GR_CHAR copy_DBR_GR, // DBR_GR_LONG copy_DBR_GR, // DBR_GR_DOUBLE @@ -745,7 +827,7 @@ static copyDBRtoPVStructure copyFuncTable[] = 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_STS, // DBR_CTRL_ENUM -> DBR_STS_ENUM + copy_DBR_CTRL, // DBR_CTRL_ENUM copy_DBR_CTRL, // DBR_CTRL_CHAR copy_DBR_CTRL, // DBR_CTRL_LONG copy_DBR_CTRL // DBR_CTRL_DOUBLE @@ -1267,5 +1349,8 @@ void CAChannelMonitor::release(epics::pvData::MonitorElementPtr const & /*monito void CAChannelMonitor::destroy() { + ca_clear_subscription(eventID); + // TODO + thisPointer.reset(); } diff --git a/pvAccessApp/ca/caChannel.h b/pvAccessApp/ca/caChannel.h index 4c2bc71..46e7215 100644 --- a/pvAccessApp/ca/caChannel.h +++ b/pvAccessApp/ca/caChannel.h @@ -38,8 +38,6 @@ public: chtype getNativeType(); unsigned getElementCount(); - epics::pvData::PVStructure::shared_pointer getPVStructure(); - /* --------------- epics::pvAccess::Channel --------------- */ virtual std::tr1::shared_ptr getProvider(); @@ -95,6 +93,11 @@ public: virtual void destroy(); + /* ---------------------------------------------------------------- */ + + void registerRequest(ChannelRequest::shared_pointer const & request); + void unregisterRequest(ChannelRequest::shared_pointer const & request); + private: CAChannel(ChannelProvider::shared_pointer const & channelProvider, @@ -108,7 +111,13 @@ private: chid channelID; chtype channelType; unsigned elementCount; - epics::pvData::PVStructure::shared_pointer pvStructure; + epics::pvData::Structure::const_shared_pointer structure; + + epics::pvData::Mutex requestsMutex; + // TODO std::unordered_map + // void* is not the nicest thing, but there is no fast weak_ptr== + typedef std::map RequestsList; + RequestsList requests; }; diff --git a/pvAccessApp/ca/caProvider.cpp b/pvAccessApp/ca/caProvider.cpp index 01df47e..5bef77b 100644 --- a/pvAccessApp/ca/caProvider.cpp +++ b/pvAccessApp/ca/caProvider.cpp @@ -8,6 +8,8 @@ #include #include +#include + /* for CA */ #include #include @@ -36,12 +38,6 @@ epics::pvData::String CAChannelProvider::getProviderName() return PROVIDER_NAME; } -void CAChannelProvider::destroy() -{ - /* Shut down Channel Access */ - ca_context_destroy(); -} - ChannelFind::shared_pointer CAChannelProvider::channelFind( epics::pvData::String const & channelName, ChannelFindRequester::shared_pointer const & channelFindRequester) @@ -91,6 +87,34 @@ void CAChannelProvider::poll() { } +void CAChannelProvider::destroy() +{ + Lock lock(channelsMutex); + { + while (!channels.empty()) + { + Channel::shared_pointer channel = channels.rbegin()->second.lock(); + if (channel) + channel->destroy(); + } + } + + /* Destroy CA Context */ + ca_context_destroy(); +} + +void CAChannelProvider::registerChannel(Channel::shared_pointer const & channel) +{ + Lock lock(channelsMutex); + channels[channel.get()] = Channel::weak_pointer(channel); +} + +void CAChannelProvider::unregisterChannel(Channel::shared_pointer const & channel) +{ + Lock lock(channelsMutex); + channels.erase(channel.get()); +} + void CAChannelProvider::initialize() { /* Create Channel Access */ diff --git a/pvAccessApp/ca/caProvider.h b/pvAccessApp/ca/caProvider.h index 9841fb7..80b5b2c 100644 --- a/pvAccessApp/ca/caProvider.h +++ b/pvAccessApp/ca/caProvider.h @@ -8,6 +8,7 @@ #define CAPROVIDER_H #include +#include namespace epics { namespace pvAccess { @@ -24,9 +25,9 @@ public: CAChannelProvider(); virtual ~CAChannelProvider(); - virtual epics::pvData::String getProviderName(); + /* --------------- epics::pvAccess::ChannelProvider --------------- */ - virtual void destroy(); + virtual epics::pvData::String getProviderName(); virtual ChannelFind::shared_pointer channelFind( epics::pvData::String const & channelName, @@ -48,9 +49,22 @@ public: virtual void flush(); virtual void poll(); + virtual void destroy(); + + /* ---------------------------------------------------------------- */ + + void registerChannel(Channel::shared_pointer const & channel); + void unregisterChannel(Channel::shared_pointer const & channel); + private: void initialize(); + + epics::pvData::Mutex channelsMutex; + // TODO std::unordered_map + // void* is not the nicest thing, but there is no fast weak_ptr== + typedef std::map ChannelList; + ChannelList channels; };