From 6a0116a96e6d4c25a4893503992c44c1ce8a2ae1 Mon Sep 17 00:00:00 2001 From: Matej Sekoranja Date: Mon, 27 May 2013 12:56:51 +0200 Subject: [PATCH] ca provider skeleton done, connection, getField --- pvAccessApp/Makefile | 2 + pvAccessApp/ca/caChannel.cpp | 329 ++++++++++++++++++++++++++++++++++ pvAccessApp/ca/caChannel.h | 108 +++++++++++ pvAccessApp/ca/caProvider.cpp | 13 +- pvAccessApp/ca/caProvider.h | 3 +- pvAccessApp/client/pvAccess.h | 2 + pvAccessCPP.files | 2 + 7 files changed, 452 insertions(+), 7 deletions(-) create mode 100644 pvAccessApp/ca/caChannel.cpp create mode 100644 pvAccessApp/ca/caChannel.h diff --git a/pvAccessApp/Makefile b/pvAccessApp/Makefile index 493a266..a9cd00b 100644 --- a/pvAccessApp/Makefile +++ b/pvAccessApp/Makefile @@ -87,7 +87,9 @@ LIBSRCS += rpcClient.cpp SRC_DIRS += $(PVACCESS)/ca INC += caProvider.h +INC += caChannel.h LIBSRCS += caProvider.cpp +LIBSRCS += caChannel.cpp SRC_DIRS += $(PVACCESS)/mb INC += pvAccessMB.h diff --git a/pvAccessApp/ca/caChannel.cpp b/pvAccessApp/ca/caChannel.cpp new file mode 100644 index 0000000..de5a3ec --- /dev/null +++ b/pvAccessApp/ca/caChannel.cpp @@ -0,0 +1,329 @@ +/** + * 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 + +using namespace epics::pvData; +using namespace epics::pvAccess; + +#define EXCEPTION_GUARD(code) try { code; } \ + 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__); } + +#define PVACCESS_REFCOUNT_MONITOR_DEFINE(name) +#define PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(name) +#define PVACCESS_REFCOUNT_MONITOR_DESTRUCT(name) + +PVACCESS_REFCOUNT_MONITOR_DEFINE(caChannel); + +CAChannel::shared_pointer CAChannel::create(ChannelProvider::shared_pointer const & channelProvider, + epics::pvData::String const & channelName, + short priority, + ChannelRequester::shared_pointer const & channelRequester) +{ + CAChannel::shared_pointer thisPtr(new CAChannel(channelProvider, channelRequester)); + thisPtr->activate(channelName, priority); + return thisPtr; +} + +static void ca_connection_handler(struct connection_handler_args args) +{ + CAChannel *channel = static_cast(ca_puser(args.chid)); + + if (args.op == CA_OP_CONN_UP) + channel->connected(); + else if (args.op == CA_OP_CONN_DOWN) + channel->disconnected(); +} + +void CAChannel::connected() +{ + StandardPVFieldPtr standardPVField = getStandardPVField(); + PVStructure::shared_pointer pvStructure; + + // TODO + String properties("value,timeStamp"); + // TODO arrays + unsigned elementCount = ca_element_count(channelID); + + chtype type = ca_field_type(channelID); + switch (type) + { + case DBR_CHAR: + // byte + break; + case DBR_SHORT: + // short + break; + case DBR_ENUM: + // enum + break; + case DBR_LONG: + // int + break; + case DBR_FLOAT: + // float + break; + case DBR_DOUBLE: + // double + pvStructure = (elementCount > 1) ? + standardPVField->scalarArray(pvDouble, properties) : + standardPVField->scalar(pvDouble, properties); + break; + case DBR_STRING: + // string + break; + default: + // TODO !!! + break; + } + + // TODO thread sync + this->pvStructure = pvStructure; + + // TODO call channelCreated if structure has changed + EXCEPTION_GUARD(channelRequester->channelStateChange(shared_from_this(), Channel::CONNECTED)); +} + +void CAChannel::disconnected() +{ + EXCEPTION_GUARD(channelRequester->channelStateChange(shared_from_this(), Channel::DISCONNECTED)); +} + +CAChannel::CAChannel(ChannelProvider::shared_pointer const & _channelProvider, + ChannelRequester::shared_pointer const & _channelRequester) : + channelProvider(_channelProvider), + channelRequester(_channelRequester) +{ + PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(caChannel); +} + +void CAChannel::activate(epics::pvData::String const & channelName, short priority) +{ + int result = ca_create_channel(channelName.c_str(), + ca_connection_handler, + this, + priority, // TODO mapping + &channelID); + if (result == ECA_NORMAL) + { + // TODO be sure that ca_connection_handler is not called before this call + EXCEPTION_GUARD(channelRequester->channelCreated(Status::Ok, shared_from_this())); + } + else + { + Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(result))); + EXCEPTION_GUARD(channelRequester->channelCreated(errorStatus, shared_from_this())); + } +} + +CAChannel::~CAChannel() +{ + PVACCESS_REFCOUNT_MONITOR_DESTRUCT(caChannel); +} + + +std::tr1::shared_ptr CAChannel::getProvider() +{ + return channelProvider; +} + + +epics::pvData::String CAChannel::getRemoteAddress() +{ + return epics::pvData::String(ca_host_name(channelID)); +} + + +static Channel::ConnectionState cs2CS[] = +{ + Channel::NEVER_CONNECTED, // cs_never_conn + Channel::DISCONNECTED, // cs_prev_conn + Channel::CONNECTED, // cs_conn + Channel::DESTROYED // cs_closed +}; + +Channel::ConnectionState CAChannel::getConnectionState() +{ + return cs2CS[ca_state(channelID)]; +} + + +epics::pvData::String CAChannel::getChannelName() +{ + return epics::pvData::String(ca_name(channelID)); +} + + +std::tr1::shared_ptr CAChannel::getChannelRequester() +{ + return channelRequester; +} + + +bool CAChannel::isConnected() +{ + return (ca_state(channelID) == cs_conn); +} + + +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); + + if (pvField) + { + EXCEPTION_GUARD(requester->getDone(Status::Ok, pvField->getField())); + } + else + { + Status errorStatus(Status::STATUSTYPE_ERROR, "field '" + subField + "' not found"); + EXCEPTION_GUARD(requester->getDone(errorStatus, FieldConstPtr())); + } +} + + +AccessRights CAChannel::getAccessRights(epics::pvData::PVField::shared_pointer const & /*pvField*/) +{ + if (ca_write_access(channelID)) + return readWrite; + else if (ca_read_access(channelID)) + return read; + else + return none; +} + + +ChannelProcess::shared_pointer CAChannel::createChannelProcess( + ChannelProcessRequester::shared_pointer const & channelProcessRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + ChannelProcess::shared_pointer nullChannelProcess; + EXCEPTION_GUARD(channelProcessRequester->channelProcessConnect(errorStatus, nullChannelProcess)); + return nullChannelProcess; +} + +ChannelGet::shared_pointer CAChannel::createChannelGet( + ChannelGetRequester::shared_pointer const & channelGetRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + ChannelGet::shared_pointer nullChannelGet; + EXCEPTION_GUARD(channelGetRequester->channelGetConnect(errorStatus, nullChannelGet, + PVStructure::shared_pointer(), BitSet::shared_pointer())); + return nullChannelGet; +} + + +ChannelPut::shared_pointer CAChannel::createChannelPut( + ChannelPutRequester::shared_pointer const & channelPutRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + ChannelPut::shared_pointer nullChannelPut; + EXCEPTION_GUARD(channelPutRequester->channelPutConnect(errorStatus, nullChannelPut, + PVStructure::shared_pointer(), BitSet::shared_pointer())); + return nullChannelPut; +} + +ChannelPutGet::shared_pointer CAChannel::createChannelPutGet( + ChannelPutGetRequester::shared_pointer const & channelPutGetRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not supported"); + ChannelPutGet::shared_pointer nullChannelPutGet; + EXCEPTION_GUARD(channelPutGetRequester->channelPutGetConnect(errorStatus, nullChannelPutGet, + PVStructure::shared_pointer(), PVStructure::shared_pointer())); + return nullChannelPutGet; +} + + +ChannelRPC::shared_pointer CAChannel::createChannelRPC( + ChannelRPCRequester::shared_pointer const & channelRPCRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not supported"); + ChannelRPC::shared_pointer nullChannelRPC; + EXCEPTION_GUARD(channelRPCRequester->channelRPCConnect(errorStatus, nullChannelRPC)); + return nullChannelRPC; +} + + +epics::pvData::Monitor::shared_pointer CAChannel::createMonitor( + epics::pvData::MonitorRequester::shared_pointer const & monitorRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); + Monitor::shared_pointer nullMonitor; + EXCEPTION_GUARD(monitorRequester->monitorConnect(errorStatus, nullMonitor, + Structure::shared_pointer())); + return nullMonitor; +} + + +ChannelArray::shared_pointer CAChannel::createChannelArray( + ChannelArrayRequester::shared_pointer const & channelArrayRequester, + epics::pvData::PVStructure::shared_pointer const & /*pvRequest*/) +{ + Status errorStatus(Status::STATUSTYPE_ERROR, "not supported"); + ChannelArray::shared_pointer nullChannelArray; + EXCEPTION_GUARD(channelArrayRequester->channelArrayConnect(errorStatus, nullChannelArray, + PVArray::shared_pointer())); + return nullChannelArray; +} + + +void CAChannel::printInfo() +{ + String info; + printInfo(&info); + std::cout << info.c_str() << std::endl; +} + + +void CAChannel::printInfo(epics::pvData::StringBuilder out) +{ + out->append( "CHANNEL : "); out->append(getChannelName()); + ConnectionState state = getConnectionState(); + out->append("\nSTATE : "); out->append(ConnectionStateNames[state]); + if (state == CONNECTED) + { + out->append("\nADDRESS : "); out->append(getRemoteAddress()); + //out->append("\nRIGHTS : "); out->append(getAccessRights()); + } + out->append("\n"); +} + + +/* --------------- epics::pvData::Requester --------------- */ + + +String CAChannel::getRequesterName() +{ + return getChannelName(); +} + + +void CAChannel::message(String const & message,MessageType messageType) +{ + std::cout << "[" << getRequesterName() << "] message(" << message << ", " << getMessageTypeName(messageType) << ")" << std::endl; +} + + +/* --------------- epics::pvData::Destroyable --------------- */ + + +void CAChannel::destroy() +{ + // TODO +} diff --git a/pvAccessApp/ca/caChannel.h b/pvAccessApp/ca/caChannel.h new file mode 100644 index 0000000..8804272 --- /dev/null +++ b/pvAccessApp/ca/caChannel.h @@ -0,0 +1,108 @@ +/** + * 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 CACHANNEL_H +#define CACHANNEL_H + +#include + +/* for CA */ +#include + +namespace epics { +namespace pvAccess { + +class CAChannel : + public Channel, + public std::tr1::enable_shared_from_this +{ + +public: + POINTER_DEFINITIONS(CAChannel); + + static CAChannel::shared_pointer create(ChannelProvider::shared_pointer const & channelProvider, + epics::pvData::String const & channelName, + short priority, + ChannelRequester::shared_pointer const & channelRequester); + + virtual ~CAChannel(); + + void connected(); + void disconnected(); + + /* --------------- epics::pvAccess::Channel --------------- */ + + virtual std::tr1::shared_ptr getProvider(); + virtual epics::pvData::String getRemoteAddress(); + virtual ConnectionState getConnectionState(); + virtual epics::pvData::String getChannelName(); + virtual std::tr1::shared_ptr getChannelRequester(); + virtual bool isConnected(); + + virtual void getField(GetFieldRequester::shared_pointer const & requester,epics::pvData::String const & subField); + + virtual AccessRights getAccessRights(epics::pvData::PVField::shared_pointer const & pvField); + + virtual ChannelProcess::shared_pointer createChannelProcess( + ChannelProcessRequester::shared_pointer const & channelProcessRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual ChannelGet::shared_pointer createChannelGet( + ChannelGetRequester::shared_pointer const & channelGetRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual ChannelPut::shared_pointer createChannelPut( + ChannelPutRequester::shared_pointer const & channelPutRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual ChannelPutGet::shared_pointer createChannelPutGet( + ChannelPutGetRequester::shared_pointer const & channelPutGetRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual ChannelRPC::shared_pointer createChannelRPC( + ChannelRPCRequester::shared_pointer const & channelRPCRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual epics::pvData::Monitor::shared_pointer createMonitor( + epics::pvData::MonitorRequester::shared_pointer const & monitorRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual ChannelArray::shared_pointer createChannelArray( + ChannelArrayRequester::shared_pointer const & channelArrayRequester, + epics::pvData::PVStructure::shared_pointer const & pvRequest); + + virtual void printInfo(); + + virtual void printInfo(epics::pvData::StringBuilder out); + + /* --------------- epics::pvData::Requester --------------- */ + + virtual epics::pvData::String getRequesterName(); + + virtual void message(epics::pvData::String const & message, epics::pvData::MessageType messageType); + + /* --------------- epics::pvData::Destroyable --------------- */ + + virtual void destroy(); + +private: + + CAChannel(ChannelProvider::shared_pointer const & channelProvider, + ChannelRequester::shared_pointer const & channelRequester); + void activate(epics::pvData::String const & channelName, short priority); + + // TODO weak_ptr usage? + ChannelProvider::shared_pointer channelProvider; + ChannelRequester::shared_pointer channelRequester; + + chid channelID; + epics::pvData::PVStructure::shared_pointer pvStructure; +}; + + +}} + +#endif /* CACHANNEL_H */ diff --git a/pvAccessApp/ca/caProvider.cpp b/pvAccessApp/ca/caProvider.cpp index dbf12fc..270caa0 100644 --- a/pvAccessApp/ca/caProvider.cpp +++ b/pvAccessApp/ca/caProvider.cpp @@ -6,7 +6,9 @@ #include #include +#include +/* for CA */ #include using namespace epics::pvData; @@ -70,11 +72,7 @@ Channel::shared_pointer CAChannelProvider::createChannel( if (!address.empty()) throw std::invalid_argument("CA does not support 'address' parameter"); - // TODO - Status errorStatus(Status::STATUSTYPE_ERROR, "not implemented"); - Channel::shared_pointer nullChannel; - EXCEPTION_GUARD(channelRequester->channelCreated(errorStatus, nullChannel)); - return nullChannel; + return CAChannel::create(shared_from_this(), channelName, priority, channelRequester); // NOTE it's up to internal code to respond w/ error to requester and return 0 in case of errors } @@ -94,7 +92,7 @@ void CAChannelProvider::poll() void CAChannelProvider::initialize() { /* Create Channel Access */ - int result = ca_context_create(ca_disable_preemptive_callback); + 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)); @@ -103,9 +101,12 @@ void CAChannelProvider::initialize() // TODO create a ca_poll thread } +namespace epics { namespace pvAccess { + ChannelProvider::shared_pointer createCAChannelProvider() { ChannelProvider::shared_pointer ptr(new CAChannelProvider()); return ptr; } +}} diff --git a/pvAccessApp/ca/caProvider.h b/pvAccessApp/ca/caProvider.h index 7853434..0b127c3 100644 --- a/pvAccessApp/ca/caProvider.h +++ b/pvAccessApp/ca/caProvider.h @@ -13,7 +13,8 @@ namespace epics { namespace pvAccess { class CAChannelProvider : - public ChannelProvider + public ChannelProvider, + public std::tr1::enable_shared_from_this { public: diff --git a/pvAccessApp/client/pvAccess.h b/pvAccessApp/client/pvAccess.h index db3e427..d26b5ca 100644 --- a/pvAccessApp/client/pvAccess.h +++ b/pvAccessApp/client/pvAccess.h @@ -17,6 +17,8 @@ namespace epics { namespace pvAccess { + // TODO add write-only? + // change names enum AccessRights { /** * Neither read or write access is allowed. diff --git a/pvAccessCPP.files b/pvAccessCPP.files index a035c5a..0ebe177 100644 --- a/pvAccessCPP.files +++ b/pvAccessCPP.files @@ -189,3 +189,5 @@ TODO testApp/remote/pvinfo.cpp pvAccessApp/ca/caProvider.h pvAccessApp/ca/caProvider.cpp +pvAccessApp/ca/caChannel.h +pvAccessApp/ca/caChannel.cpp