/** * 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(); } 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 PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, String const & properties) { // TODO StandardPVFieldPtr standardPVField = getStandardPVField(); PVStructure::shared_pointer pvStructure; chtype channelType = channel->getNativeType(); if (channelType != DBR_ENUM) { ScalarType st = dbr2ST[channelType]; pvStructure = (channel->getElementCount() > 1) ? standardPVField->scalarArray(st, properties) : standardPVField->scalar(st, properties); } else { // TODO handle enum // introduce ackConnected(pvStructure), if non-enum directly call, else when labels are retrieved } return pvStructure; } static PVStructure::shared_pointer createPVStructure(CAChannel::shared_pointer const & channel, chtype dbrType) { // TODO constants // TODO 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,display"; } else if (dbrType >= DBR_GR_STRING) // 21 { if (dbrType != DBR_GR_STRING && dbrType != DBR_GR_STRING) properties = "value,alarm,display,valueAlarm"; else properties = "value,alarm,display"; } 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 for non-numeric type String allProperties = channelType != DBR_STRING && channelType != DBR_ENUM ? "value,timeStamp,alarm,display,valueAlarm,control" : "value,timeStamp,alarm,display"; PVStructure::shared_pointer pvStructure = createPVStructure(shared_from_this(), allProperties); // TODO thread sync // TODO we need only Structure here 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), channelID(0), channelType(0), elementCount(0) { 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); } chid CAChannel::getChannelID() { return channelID; } chtype CAChannel::getNativeType() { return channelType; } unsigned CAChannel::getElementCount() { return elementCount; } PVStructure::shared_pointer CAChannel::getPVStructure() { return pvStructure; } 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 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*/) { 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 } ChannelGet::shared_pointer CAChannelGet::create( CAChannel::shared_pointer const & channel, ChannelGetRequester::shared_pointer const & channelGetRequester, epics::pvData::PVStructure::shared_pointer const & pvRequest) { // TODO for not pvRequest ignored 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(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 & tp); // 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)); value->put(0, count, static_cast(dbr), 0); } } #ifdef vxWorks // dbr_long_t is defined as "int", pvData uses int32 which can be defined as "long int" (32-bit) // this makes static cast to fail, this is a workaround (compiler will optimize this efficiently) // 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)); value->put(0, count, static_cast(dbr), 0); } } #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); StringArray sA; sA.reserve(count); for (unsigned i = 0; i < count; i++) sA.push_back(dbrStrings[i]); value->put(0, count, sA, 0); } } // enum specialization template<> void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure) { // TODO if (count == 1) { std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getSubField("value.index")); value->put(static_cast(dbr)[0]); } else { // TODO /* std::tr1::shared_ptr value = std::tr1::static_pointer_cast(pvStructure->getScalarArrayField("value", pvString)); const dbr_string_t* dbrStrings = static_cast(dbr); StringArray sA; sA.reserve(count); for (unsigned i = 0; i < count; i++) sA.push_back(dbrStrings[i]); value->put(0, count, sA, 0); */ } } 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"); // TODO any mapping 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); } // 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"); 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); 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); } 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_TIME, // DBR_GR_STRING -> DBR_STS_STRING copy_DBR_GR, // DBR_GR_INT, DBR_GR_SHORT copy_DBR_GR, // DBR_GR_FLOAT 0,// TODO 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_TIME, // DBR_CTRL_STRING -> DBR_STS_STRING copy_DBR_CTRL, // DBR_CTRL_INT, DBR_CTRL_SHORT copy_DBR_CTRL, // DBR_CTRL_FLOAT 0,// TODO 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; } // TODO EXCEPTION_GUARD(channelGetRequester->getDone(Status::Ok)); } else { // TODO check: ca_message from args.status Status errorStatus(Status::STATUSTYPE_ERROR, String(ca_message(args.status))); EXCEPTION_GUARD(channelGetRequester->getDone(errorStatus)); } } void CAChannelGet::get(bool lastRequest) { // TODO error handling 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::Destroyable --------------- */ void CAChannelGet::destroy() { // TODO } /* --------------- epics::pvData::Lockable --------------- */ void CAChannelGet::lock() { // TODO } void CAChannelGet::unlock() { // TODO }