/** * 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; using namespace epics::pvAccess::ca; #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(channelName, channelProvider, channelRequester)); thisPtr->activate(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(); } 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); 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 { // 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) { // NOTE: value is always there String properties; if (dbrType >= DBR_CTRL_STRING) // 28 { if (dbrType != DBR_CTRL_STRING && dbrType != DBR_CTRL_ENUM) 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) properties = "value,alarm,display,valueAlarm"; else properties = "value,alarm"; } else if (dbrType >= DBR_TIME_STRING) // 14 properties = "value,timeStamp"; else if (dbrType >= DBR_STS_STRING) // 7 properties = "value,alarm"; else properties = "value"; return createPVStructure(channel, properties); } void CAChannel::connected() { // TODO sync // we assume array if element count > 1 elementCount = ca_element_count(channelID); channelType = ca_field_type(channelID); // no valueAlarm and control,display for non-numeric type String allProperties = (channelType != DBR_STRING && channelType != DBR_ENUM) ? "value,timeStamp,alarm,display,valueAlarm,control" : "value,timeStamp,alarm"; Structure::const_shared_pointer structure = createStructure(shared_from_this(), allProperties); // TODO thread sync // TODO we need only Structure here this->structure = structure; // 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(epics::pvData::String const & _channelName, ChannelProvider::shared_pointer const & _channelProvider, ChannelRequester::shared_pointer const & _channelRequester) : channelName(_channelName), channelProvider(_channelProvider), channelRequester(_channelRequester), channelID(0), channelType(0), elementCount(0) { PVACCESS_REFCOUNT_MONITOR_CONSTRUCT(caChannel); } void CAChannel::activate(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); } chid CAChannel::getChannelID() { return channelID; } chtype CAChannel::getNativeType() { return channelType; } unsigned CAChannel::getElementCount() { return elementCount; } 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 channelName; } 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) { Field::const_shared_pointer field = subField.empty() ? std::tr1::static_pointer_cast(structure) : structure->getField(subField); if (field) { EXCEPTION_GUARD(requester->getDone(Status::Ok, field)); } 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 supported"); 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) { return CAChannelGet::create(shared_from_this(), channelGetRequester, pvRequest); } ChannelPut::shared_pointer CAChannel::createChannelPut( ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { return CAChannelPut::create(shared_from_this(), channelPutRequester, pvRequest); } 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) { return CAChannelMonitor::create(shared_from_this(), monitorRequester, pvRequest); } 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() { 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()); } ChannelGet::shared_pointer CAChannelGet::create( CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { ChannelGet::shared_pointer thisPtr(new CAChannelGet(channel, channelGetRequester, pvRequest)); static_cast(thisPtr.get())->activate(); return thisPtr; } CAChannelGet::~CAChannelGet() { // TODO } 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 or control -> DBR_CTRL_ if (fieldStructure->getNumberFields() == 0 || 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); // alarm -> DBR_STS_ if (fieldStructure->getField("alarm")) return static_cast(static_cast(nativeType) + DBR_STS_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); return nativeType; } CAChannelGet::CAChannelGet(CAChannel::shared_pointer const & _channel, ChannelGetRequester::shared_pointer const & _channelGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) : channel(_channel), channelGetRequester(_channelGetRequester), getType(getDBRType(pvRequest, _channel->getNativeType())), pvStructure(createPVStructure(_channel, getType)), bitSet(new BitSet(static_cast(pvStructure->getStructure()->getNumberFields()))) { // TODO bitSet->set(0); } void CAChannelGet::activate() { EXCEPTION_GUARD(channelGetRequester->channelGetConnect(Status::Ok, shared_from_this(), pvStructure, bitSet)); } /* --------------- epics::pvAccess::ChannelGet --------------- */ static void ca_get_handler(struct event_handler_args args) { CAChannelGet *channelGet = static_cast(args.usr); 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 = std::tr1::static_pointer_cast(pvStructure->getSubField("value")); value->put(static_cast(dbr)[0]); } else { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getScalarArrayField("value", sT)); 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 = std::tr1::static_pointer_cast(pvStructure->getSubField("value")); value->put(static_cast(dbr)[0]); } else { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getScalarArrayField("value", pvInt)); 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 = std::tr1::static_pointer_cast(pvStructure->getSubField("value")); value->put(String(static_cast(dbr))); } else { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getScalarArrayField("value", pvString)); 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) { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubField("value.index")); value->put(static_cast(dbr)[0]); } else { // not supported std::cerr << "caChannel: array of enums not supported" << std::endl; } } static 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 }; // 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->getStructureField("alarm"); // no mapping needed alarm->getIntField("status")->put(0); alarm->getIntField("severity")->put(data->severity); alarm->getStringField("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->getStructureField("timeStamp"); epics::pvData::int64 spe = data->stamp.secPastEpoch; spe += 7305*86400; ts->getLongField("secondsPastEpoch")->put(spe); ts->getIntField("nanoSeconds")->put(data->stamp.nsec); copy_DBR(&data->value, count, pvStructure); } template void copy_format(const void * /*dbr*/, PVStructure::shared_pointer const & pvDisplayStructure) { pvDisplayStructure->getStringField("format")->put("%d"); } template <> void copy_format(const void * /*dbr*/, PVStructure::shared_pointer const & pvDisplayStructure) { pvDisplayStructure->getStringField("format")->put("%s"); } #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); \ pvDisplayStructure->getStringField("format")->put(String(fmt)); \ } \ else \ { \ pvDisplayStructure->getStringField("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); PVStructure::shared_pointer alarm = pvStructure->getStructureField("alarm"); alarm->getIntField("status")->put(0); alarm->getIntField("severity")->put(data->severity); alarm->getStringField("message")->put(dbrStatus2alarmMessage[data->status]); PVStructure::shared_pointer disp = pvStructure->getStructureField("display"); disp->getStringField("units")->put(String(data->units)); disp->getDoubleField("limitHigh")->put(data->upper_disp_limit); disp->getDoubleField("limitLow")->put(data->lower_disp_limit); copy_format(dbr, disp); PVStructure::shared_pointer va = pvStructure->getStructureField("valueAlarm"); va->getDoubleField("highAlarmLimit")->put(data->upper_alarm_limit); va->getDoubleField("highWarningLimit")->put(data->upper_warning_limit); va->getDoubleField("lowWarningLimit")->put(data->lower_warning_limit); va->getDoubleField("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->getStructureField("alarm"); alarm->getIntField("status")->put(0); alarm->getIntField("severity")->put(data->severity); alarm->getStringField("message")->put(dbrStatus2alarmMessage[data->status]); PVStructure::shared_pointer disp = pvStructure->getStructureField("display"); disp->getStringField("units")->put(String(data->units)); disp->getDoubleField("limitHigh")->put(data->upper_disp_limit); disp->getDoubleField("limitLow")->put(data->lower_disp_limit); copy_format(dbr, disp); PVStructure::shared_pointer va = pvStructure->getStructureField("valueAlarm"); std::tr1::static_pointer_cast(va->getSubField("highAlarmLimit"))->put(data->upper_alarm_limit); std::tr1::static_pointer_cast(va->getSubField("highWarningLimit"))->put(data->upper_warning_limit); std::tr1::static_pointer_cast(va->getSubField("lowWarningLimit"))->put(data->lower_warning_limit); std::tr1::static_pointer_cast(va->getSubField("lowAlarmLimit"))->put(data->lower_alarm_limit); PVStructure::shared_pointer ctrl = pvStructure->getStructureField("control"); ctrl->getDoubleField("limitHigh")->put(data->upper_ctrl_limit); ctrl->getDoubleField("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 }; void CAChannelGet::getDone(struct event_handler_args &args) { 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(channelGetRequester->getDone(Status::Ok)); } else { Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(args.status))); EXCEPTION_GUARD(channelGetRequester->getDone(errorStatus)); } } void CAChannelGet::get(bool lastRequest) { int result = ca_array_get_callback(getType, channel->getElementCount(), channel->getChannelID(), ca_get_handler, this); if (result == ECA_NORMAL) { ca_flush_io(); } else { Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(result))); EXCEPTION_GUARD(channelGetRequester->getDone(errorStatus)); } if (lastRequest) destroy(); } /* --------------- epics::pvData::ChannelRequest --------------- */ void CAChannelGet::cancel() { // noop } /* --------------- epics::pvData::Destroyable --------------- */ void CAChannelGet::destroy() { // TODO } /* --------------- epics::pvData::Lockable --------------- */ void CAChannelGet::lock() { // TODO } void CAChannelGet::unlock() { // TODO } ChannelPut::shared_pointer CAChannelPut::create( CAChannel::shared_pointer const & channel, ChannelPutRequester::shared_pointer const & channelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { ChannelPut::shared_pointer thisPtr(new CAChannelPut(channel, channelPutRequester, pvRequest)); static_cast(thisPtr.get())->activate(); return thisPtr; } CAChannelPut::~CAChannelPut() { // TODO } CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & _channel, ChannelPutRequester::shared_pointer const & _channelPutRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) : channel(_channel), channelPutRequester(_channelPutRequester), getType(getDBRType(pvRequest, _channel->getNativeType())), pvStructure(createPVStructure(_channel, getType)), bitSet(new BitSet(static_cast(pvStructure->getStructure()->getNumberFields()))) { // NOTE: we require value type, we can only put value field bitSet->set(pvStructure->getSubField("value")->getFieldOffset()); } void CAChannelPut::activate() { EXCEPTION_GUARD(channelPutRequester->channelPutConnect(Status::Ok, shared_from_this(), pvStructure, bitSet)); } /* --------------- epics::pvAccess::ChannelPut --------------- */ 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->getSubField("value")); pT val = value->get(); int result = ca_array_put_callback(channel->getNativeType(), 1, channel->getChannelID(), &val, ca_put_handler, usrArg); if (result == ECA_NORMAL) { ca_flush_io(); } return result; } else { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getScalarArrayField("value", sT)); const pT* val = value->view().data(); int result = ca_array_put_callback(channel->getNativeType(), static_cast(value->getLength()), channel->getChannelID(), val, ca_put_handler, usrArg); 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->getSubField("value")); String val = value->get(); int result = ca_array_put_callback(channel->getNativeType(), 1, channel->getChannelID(), val.c_str(), ca_put_handler, usrArg); if (result == ECA_NORMAL) { ca_flush_io(); } return result; } else { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getScalarArrayField("value", pvString)); //const String* val = value->get(); int result = ECA_NOSUPPORT; // TODO /* int result = ca_array_put_callback(channel->getNativeType(), value->getLength(), channel->getChannelID(), val, ca_put_handler, usrArg); */ 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->getSubField("value.index")); dbr_enum_t val = value->get(); int result = ca_array_put_callback(channel->getNativeType(), 1, channel->getChannelID(), &val, ca_put_handler, usrArg); 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 }; void CAChannelPut::putDone(struct event_handler_args &args) { if (args.status == ECA_NORMAL) { EXCEPTION_GUARD(channelPutRequester->putDone(Status::Ok)); } else { Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(args.status))); EXCEPTION_GUARD(channelPutRequester->putDone(errorStatus)); } } void CAChannelPut::put(bool lastRequest) { doPut putFunc = doPutFuncTable[channel->getNativeType()]; if (putFunc) { int result = putFunc(channel, this, pvStructure); if (result != ECA_NORMAL) { Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(result))); EXCEPTION_GUARD(channelPutRequester->getDone(errorStatus)); } } else { // TODO remove std::cout << "no put func implemented" << std::endl; } // TODO here???!!! if (lastRequest) destroy(); } void CAChannelPut::getDone(struct event_handler_args &args) { 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(channelPutRequester->getDone(Status::Ok)); } else { Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(args.status))); EXCEPTION_GUARD(channelPutRequester->getDone(errorStatus)); } } void CAChannelPut::get() { int result = ca_array_get_callback(getType, channel->getElementCount(), channel->getChannelID(), ca_put_get_handler, this); if (result == ECA_NORMAL) { ca_flush_io(); } else { Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(result))); EXCEPTION_GUARD(channelPutRequester->getDone(errorStatus)); } } /* --------------- epics::pvData::ChannelRequest --------------- */ void CAChannelPut::cancel() { // noop } /* --------------- epics::pvData::Destroyable --------------- */ void CAChannelPut::destroy() { // TODO } /* --------------- epics::pvData::Lockable --------------- */ void CAChannelPut::lock() { // TODO } void CAChannelPut::unlock() { // TODO } Monitor::shared_pointer CAChannelMonitor::create( CAChannel::shared_pointer const & channel, epics::pvData::MonitorRequester::shared_pointer const & monitorRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { Monitor::shared_pointer thisPtr(new CAChannelMonitor(channel, monitorRequester, pvRequest)); static_cast(thisPtr.get())->activate(); return thisPtr; } CAChannelMonitor::~CAChannelMonitor() { // TODO } CAChannelMonitor::CAChannelMonitor(CAChannel::shared_pointer const & _channel, epics::pvData::MonitorRequester::shared_pointer const & _monitorRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) : channel(_channel), monitorRequester(_monitorRequester), getType(getDBRType(pvRequest, _channel->getNativeType())), pvStructure(createPVStructure(_channel, getType)), changedBitSet(new BitSet(static_cast(pvStructure->getStructure()->getNumberFields()))), overrunBitSet(new BitSet(static_cast(pvStructure->getStructure()->getNumberFields()))), count(0), element(new MonitorElement()) { // TODO changedBitSet->set(0); element->pvStructurePtr = pvStructure; element->changedBitSet = changedBitSet; element->overrunBitSet = overrunBitSet; } void CAChannelMonitor::activate() { // TODO remove thisPointer = shared_from_this(); EXCEPTION_GUARD(monitorRequester->monitorConnect(Status::Ok, shared_from_this(), pvStructure->getStructure())); } /* --------------- epics::pvData::Monitor --------------- */ static void ca_subscription_handler(struct event_handler_args args) { CAChannelMonitor *channelMonitor = static_cast(args.usr); channelMonitor->subscriptionEvent(args); } void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args) { if (args.status == ECA_NORMAL) { // TODO override indicator copyDBRtoPVStructure copyFunc = copyFuncTable[getType]; if (copyFunc) copyFunc(args.dbr, args.count, pvStructure); else { // TODO remove std::cout << "no copy func implemented" << std::endl; } { Lock lock(mutex); count = 1; } monitorRequester->monitorEvent(shared_from_this()); } else { //Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(args.status))); //EXCEPTION_GUARD(channelMonitorRequester->MonitorDone(errorStatus)); } } epics::pvData::Status CAChannelMonitor::start() { // TODO DBE_PROPERTY support int result = ca_create_subscription(getType, channel->getElementCount(), channel->getChannelID(), DBE_VALUE, ca_subscription_handler, this, &eventID); if (result == ECA_NORMAL) { ca_flush_io(); return Status::Ok; } else { return Status(Status::STATUSTYPE_ERROR, String(ca_message(result))); } } epics::pvData::Status CAChannelMonitor::stop() { int result = ca_clear_subscription(eventID); if (result == ECA_NORMAL) { return Status::Ok; } else { return Status(Status::STATUSTYPE_ERROR, String(ca_message(result))); } } epics::pvData::MonitorElementPtr CAChannelMonitor::poll() { Lock lock(mutex); if (count) { count--; return element; } else { return nullElement; } } void CAChannelMonitor::release(epics::pvData::MonitorElementPtr const & /*monitorElement*/) { // noop } /* --------------- epics::pvData::ChannelRequest --------------- */ void CAChannelMonitor::cancel() { // noop } /* --------------- epics::pvData::Destroyable --------------- */ void CAChannelMonitor::destroy() { ca_clear_subscription(eventID); // TODO thisPointer.reset(); }