Files
pvAccess/src/ca/caChannel.cpp
2017-08-04 14:15:42 -04:00

1800 lines
58 KiB
C++

/**
* 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 <epicsVersion.h>
#include <pv/standardField.h>
#include <pv/logger.h>
#include <pv/pvAccess.h>
#define epicsExportSharedSymbols
#include <pv/caChannel.h>
#include <pv/caStatus.h>
using namespace epics::pvData;
using std::string;
namespace epics {
namespace pvAccess {
namespace 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(CAChannelProvider::shared_pointer const & channelProvider,
std::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<CAChannel*>(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<ScalarType>(-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<const dbr_gr_enum*>(args.dbr);
PVStringArray* labelsArray = static_cast<PVStringArray*>(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
{
// TODO better error handling
std::cerr << "failed to get labels for enum : " << ca_message(args.status) << std::endl;
}
}
// 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<const Structure>(field);
StructureConstPtr reqSubstruct =
std::tr1::dynamic_pointer_cast<const Structure>(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<PVStructure>("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<PVStringArray>("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)
{
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, 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()
{
{
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;
}
while(!putQueue.empty()) {
putQueue.front()->activate();
putQueue.pop();
}
while(!getQueue.empty()) {
getQueue.front()->activate();
getQueue.pop();
}
while(!monitorQueue.empty()) {
monitorQueue.front()->activate();
monitorQueue.pop();
}
std::queue<CAChannelPutPtr> putQ;
std::queue<CAChannelGetPtr> getQ;
std::queue<CAChannelMonitorPtr> monitorQ;
{
Lock lock(requestsMutex);
std::vector<CAChannelGetWPtr>::const_iterator getiter;
for (getiter = getList.begin(); getiter != getList.end(); ++getiter) {
CAChannelGetPtr temp = (*getiter).lock();
if(!temp) continue;
getQ.push(temp);
}
std::vector<CAChannelPutWPtr>::const_iterator putiter;
for (putiter = putList.begin(); putiter != putList.end(); ++putiter) {
CAChannelPutPtr temp = (*putiter).lock();
if(!temp) continue;
putQ.push(temp);
}
std::vector<CAChannelMonitorWPtr>::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();
}
EXCEPTION_GUARD(channelRequester->channelStateChange(shared_from_this(), Channel::CONNECTED));
}
void CAChannel::disconnected()
{
std::queue<CAChannelPutPtr> putQ;
std::queue<CAChannelGetPtr> getQ;
std::queue<CAChannelMonitorPtr> monitorQ;
{
Lock lock(requestsMutex);
std::vector<CAChannelGetWPtr>::const_iterator getiter;
for (getiter = getList.begin(); getiter != getList.end(); ++getiter) {
CAChannelGetPtr temp = (*getiter).lock();
if(!temp) continue;
getQ.push(temp);
}
std::vector<CAChannelPutWPtr>::const_iterator putiter;
for (putiter = putList.begin(); putiter != putList.end(); ++putiter) {
CAChannelPutPtr temp = (*putiter).lock();
if(!temp) continue;
putQ.push(temp);
}
std::vector<CAChannelMonitorWPtr>::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()->channelDisconnect(false);
putQ.pop();
}
while(!getQ.empty()) {
getQ.front()->channelDisconnect(false);
getQ.pop();
}
while(!monitorQ.empty()) {
monitorQ.front()->channelDisconnect(false);
monitorQ.pop();
}
EXCEPTION_GUARD(channelRequester->channelStateChange(shared_from_this(), Channel::DISCONNECTED));
}
CAChannel::CAChannel(std::string const & _channelName,
CAChannelProvider::shared_pointer const & _channelProvider,
ChannelRequester::shared_pointer const & _channelRequester) :
channelName(_channelName),
channelProvider(_channelProvider),
channelRequester(_channelRequester),
channelID(0),
channelType(0),
elementCount(0),
destroyed(false)
{
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)
{
channelProvider->registerChannel(shared_from_this());
// TODO be sure that ca_connection_handler is not called before this call
channelRequester->channelCreated(Status::Ok, shared_from_this());
}
else
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result)));
channelRequester->channelCreated(errorStatus, shared_from_this());
}
}
void CAChannel::addChannelGet(const CAChannelGetPtr & get)
{
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)
{
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)
{
Lock lock(requestsMutex);
for(size_t i=0; i< monitorList.size(); ++i) {
if(!(monitorList[i].lock())) {
monitorList[i] = monitor;
return;
}
}
monitorList.push_back(monitor);
}
CAChannel::~CAChannel()
{
PVACCESS_REFCOUNT_MONITOR_DESTRUCT(caChannel);
{
Lock lock(requestsMutex);
if (destroyed)
return;
destroyed = true;
}
channelProvider->unregisterChannel(this);
/* Clear CA Channel */
threadAttach();
ca_clear_channel(channelID);
}
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<ChannelProvider> CAChannel::getProvider()
{
return channelProvider;
}
std::string CAChannel::getRemoteAddress()
{
return std::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)];
}
std::string CAChannel::getChannelName()
{
return channelName;
}
std::tr1::shared_ptr<ChannelRequester> CAChannel::getChannelRequester()
{
return channelRequester;
}
void CAChannel::getField(GetFieldRequester::shared_pointer const & requester,
std::string const & subField)
{
Field::const_shared_pointer field =
subField.empty() ?
std::tr1::static_pointer_cast<const Field>(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;
}
ChannelGet::shared_pointer CAChannel::createChannelGet(
ChannelGetRequester::shared_pointer const & channelGetRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
CAChannelGetPtr channelGet = CAChannelGet::create(shared_from_this(), channelGetRequester, pvRequest);\
if(getConnectionState()==Channel::CONNECTED) {
channelGet->activate();
} else {
getQueue.push(channelGet);
}
return channelGet;
}
ChannelPut::shared_pointer CAChannel::createChannelPut(
ChannelPutRequester::shared_pointer const & channelPutRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
CAChannelPutPtr channelPut = CAChannelPut::create(shared_from_this(), channelPutRequester, pvRequest);\
if(getConnectionState()==Channel::CONNECTED) {
channelPut->activate();
} else {
putQueue.push(channelPut);
}
return channelPut;
}
Monitor::shared_pointer CAChannel::createMonitor(
MonitorRequester::shared_pointer const & monitorRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
CAChannelMonitorPtr channelMonitor = CAChannelMonitor::create(shared_from_this(), monitorRequester, pvRequest);\
if(getConnectionState()==Channel::CONNECTED) {
channelMonitor->activate();
} else {
monitorQueue.push(channelMonitor);
}
return channelMonitor;
}
void CAChannel::printInfo(std::ostream& out)
{
out << "CHANNEL : " << getChannelName() << std::endl;
ConnectionState state = getConnectionState();
out << "STATE : " << ConnectionStateNames[state] << std::endl;
if (state == CONNECTED)
{
out << "ADDRESS : " << getRemoteAddress() << std::endl;
//out << "RIGHTS : " << getAccessRights() << std::endl;
}
}
/* --------------- Destroyable --------------- */
void CAChannel::destroy()
{
Lock lock(requestsMutex);
{
if (destroyed) return;
destroyed = true;
}
channelProvider->unregisterChannel(shared_from_this());
/* Clear CA Channel */
threadAttach();
ca_clear_channel(channelID);
}
/* ---------------------------------------------------------- */
void CAChannel::threadAttach()
{
std::tr1::static_pointer_cast<CAChannelProvider>(channelProvider)->threadAttach();
}
CAChannelGetPtr CAChannelGet::create(
CAChannel::shared_pointer const & channel,
ChannelGetRequester::shared_pointer const & channelGetRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
return CAChannelGetPtr(new CAChannelGet(channel, channelGetRequester, pvRequest));
}
static chtype getDBRType(PVStructure::shared_pointer const & pvRequest, chtype nativeType)
{
// get "field" sub-structure
PVStructure::shared_pointer fieldSubField =
std::tr1::dynamic_pointer_cast<PVStructure>(pvRequest->getSubField("field"));
if (!fieldSubField)
fieldSubField = pvRequest;
Structure::const_shared_pointer fieldStructure = fieldSubField->getStructure();
// no fields or control -> DBR_CTRL_<type>
if (fieldStructure->getNumberFields() == 0 ||
fieldStructure->getField("control"))
return static_cast<chtype>(static_cast<int>(nativeType) + DBR_CTRL_STRING);
// display/valueAlarm -> DBR_GR_<type>
if (fieldStructure->getField("display") || fieldStructure->getField("valueAlarm"))
return static_cast<chtype>(static_cast<int>(nativeType) + DBR_GR_STRING);
// timeStamp -> DBR_TIME_<type>
// NOTE: that only DBR_TIME_<type> type holds timestamp, therefore if you request for
// the fields above, you will never get timestamp
if (fieldStructure->getField("timeStamp"))
return static_cast<chtype>(static_cast<int>(nativeType) + DBR_TIME_STRING);
// alarm -> DBR_STS_<type>
if (fieldStructure->getField("alarm"))
return static_cast<chtype>(static_cast<int>(nativeType) + DBR_STS_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),
pvRequest(pvRequest),
lastRequestFlag(false)
{
}
CAChannelGet::~CAChannelGet()
{
}
void CAChannelGet::activate()
{
if(pvStructure) throw std::runtime_error("CAChannelGet::activate() was called twice");
getType = getDBRType(pvRequest, channel->getNativeType());
pvStructure = createPVStructure(channel, getType, pvRequest);
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
bitSet->set(0);
channel->addChannelGet(shared_from_this());
EXCEPTION_GUARD(channelGetRequester->channelGetConnect(Status::Ok, shared_from_this(),
pvStructure->getStructure()));
}
void CAChannelGet::channelCreated(const Status& status,Channel::shared_pointer const & cl)
{
chtype newType = getDBRType(pvRequest, channel->getNativeType());
if(newType!=getType) {
pvStructure.reset();
activate();
}
}
void CAChannelGet::channelStateChange(
Channel::shared_pointer const & channel,
Channel::ConnectionState connectionState)
{
if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) {
EXCEPTION_GUARD(channelGetRequester->channelDisconnect(connectionState==Channel::DESTROYED);)
}
}
void CAChannelGet::channelDisconnect(bool destroy)
{
EXCEPTION_GUARD(channelGetRequester->channelDisconnect(destroy);)
}
/* --------------- epics::pvAccess::ChannelGet --------------- */
namespace {
static void ca_get_handler(struct event_handler_args args)
{
CAChannelGet *channelGet = static_cast<CAChannelGet*>(args.usr);
channelGet->getDone(args);
}
typedef void (*copyDBRtoPVStructure)(const void * from, unsigned count, PVStructure::shared_pointer const & to);
// template<primitive type, scalar Field, array Field>
template<typename pT, typename sF, typename aF>
void copy_DBR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
if (count == 1)
{
std::tr1::shared_ptr<sF> value = pvStructure->getSubField<sF>("value");
if (value.get()) value->put(static_cast<const pT*>(dbr)[0]);
}
else
{
std::tr1::shared_ptr<aF> value = pvStructure->getSubField<aF>("value");
if (value.get())
{
std::tr1::shared_ptr<aF> value = pvStructure->getSubField<aF>("value");
typename aF::svector temp(value->reuse());
temp.resize(count);
std::copy(static_cast<const pT*>(dbr), static_cast<const pT*>(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<primitive type, scalar Field, array Field>
template<>
void copy_DBR<dbr_long_t, PVInt, PVIntArray>(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
if (count == 1)
{
std::tr1::shared_ptr<PVInt> value = pvStructure->getSubField<PVInt>("value");
if (value.get()) value->put(static_cast<const int32*>(dbr)[0]);
}
else
{
std::tr1::shared_ptr<PVIntArray> value = pvStructure->getSubField<PVIntArray>("value");
if (value.get())
{
PVIntArray::svector temp(value->reuse());
temp.resize(count);
std::copy(static_cast<const int32*>(dbr), static_cast<const int32*>(dbr) + count, temp.begin());
value->replace(freeze(temp));
}
}
}
#endif
// string specialization
template<>
void copy_DBR<string, PVString, PVStringArray>(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
if (count == 1)
{
std::tr1::shared_ptr<PVString> value = pvStructure->getSubField<PVString>("value");
if (value.get()) value->put(std::string(static_cast<const char*>(dbr)));
}
else
{
std::tr1::shared_ptr<PVStringArray> value = pvStructure->getSubField<PVStringArray>("value");
if (value.get())
{
const dbr_string_t* dbrStrings = static_cast<const dbr_string_t*>(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<dbr_enum_t, PVString, PVStringArray>(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
if (count == 1)
{
PVIntPtr value = pvStructure->getSubField<PVInt>("value.index");
if (value.get()) value->put(static_cast<const dbr_enum_t*>(dbr)[0]);
}
else
{
// not supported
std::cerr << "caChannel: array of enums not supported" << std::endl;
}
}
// template<DBR type, primitive type, scalar Field, array Field>
template<typename T, typename pT, typename sF, typename aF>
void copy_DBR_STS(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
const T* data = static_cast<const T*>(dbr);
PVStructure::shared_pointer alarm = pvStructure->getSubField<PVStructure>("alarm");
if (alarm.get())
{
PVIntPtr status = alarm->getSubField<PVInt>("status");
if (status.get()) status->put(dbrStatus2alarmStatus[data->status]);
PVIntPtr severity = alarm->getSubField<PVInt>("severity");
if (severity.get()) severity->put(data->severity);
PVStringPtr message = alarm->getSubField<PVString>("message");
if (message.get()) message->put(dbrStatus2alarmMessage[data->status]);
}
copy_DBR<pT, sF, aF>(&data->value, count, pvStructure);
}
// template<DBR type, primitive type, scalar Field, array Field>
template<typename T, typename pT, typename sF, typename aF>
void copy_DBR_TIME(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
const T* data = static_cast<const T*>(dbr);
PVStructure::shared_pointer ts = pvStructure->getSubField<PVStructure>("timeStamp");
if (ts.get())
{
epics::pvData::int64 spe = data->stamp.secPastEpoch;
spe += 7305*86400;
PVLongPtr secondsPastEpoch = ts->getSubField<PVLong>("secondsPastEpoch");
if (secondsPastEpoch.get()) secondsPastEpoch->put(spe);
PVIntPtr nanoseconds = ts->getSubField<PVInt>("nanoseconds");
if (nanoseconds.get()) nanoseconds->put(data->stamp.nsec);
}
copy_DBR_STS<T, pT, sF, aF>(dbr, count, pvStructure);
}
template <typename T>
void copy_format(const void * /*dbr*/, PVStructure::shared_pointer const & pvDisplayStructure)
{
PVStringPtr format = pvDisplayStructure->getSubField<PVString>("format");
if (format.get()) format->put("%d");
}
#define COPY_FORMAT_FOR(T) \
template <> \
void copy_format<T>(const void * dbr, PVStructure::shared_pointer const & pvDisplayStructure) \
{ \
const T* data = static_cast<const T*>(dbr); \
\
if (data->precision) \
{ \
char fmt[16]; \
sprintf(fmt, "%%.%df", data->precision); \
PVStringPtr format = pvDisplayStructure->getSubField<PVString>("format");\
if (format.get()) format->put(std::string(fmt));\
} \
else \
{ \
PVStringPtr format = pvDisplayStructure->getSubField<PVString>("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<DBR type, primitive type, scalar Field, array Field>
template<typename T, typename pT, typename sF, typename aF>
void copy_DBR_GR(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
const T* data = static_cast<const T*>(dbr);
PVStructurePtr alarm = pvStructure->getSubField<PVStructure>("alarm");
if (alarm.get())
{
PVIntPtr status = alarm->getSubField<PVInt>("status");
if (status.get()) status->put(dbrStatus2alarmStatus[data->status]);
PVIntPtr severity = alarm->getSubField<PVInt>("severity");
if (severity.get()) severity->put(data->severity);
PVStringPtr message = alarm->getSubField<PVString>("message");
if (message.get()) message->put(dbrStatus2alarmMessage[data->status]);
}
PVStructurePtr disp = pvStructure->getSubField<PVStructure>("display");
if (disp.get())
{
PVStringPtr units = disp->getSubField<PVString>("units");
if (units.get()) units->put(std::string(data->units));
PVDoublePtr limitHigh = disp->getSubField<PVDouble>("limitHigh");
if (limitHigh.get()) limitHigh->put(data->upper_disp_limit);
PVDoublePtr limitLow = disp->getSubField<PVDouble>("limitLow");
if (limitLow.get()) limitLow->put(data->lower_disp_limit);
copy_format<T>(dbr, disp);
}
PVStructurePtr va = pvStructure->getSubField<PVStructure>("valueAlarm");
if (va.get())
{
std::tr1::shared_ptr<sF> highAlarmLimit = va->getSubField<sF>("highAlarmLimit");
if (highAlarmLimit.get()) highAlarmLimit->put(data->upper_alarm_limit);
std::tr1::shared_ptr<sF> highWarningLimit = va->getSubField<sF>("highWarningLimit");
if (highWarningLimit.get()) highWarningLimit->put(data->upper_warning_limit);
std::tr1::shared_ptr<sF> lowWarningLimit = va->getSubField<sF>("lowWarningLimit");
if (lowWarningLimit.get()) lowWarningLimit->put(data->lower_warning_limit);
std::tr1::shared_ptr<sF> lowAlarmLimit = va->getSubField<sF>("lowAlarmLimit");
if (lowAlarmLimit.get()) lowAlarmLimit->put(data->lower_alarm_limit);
}
copy_DBR<pT, sF, aF>(&data->value, count, pvStructure);
}
// enum specialization
template<>
void copy_DBR_GR<dbr_gr_enum, dbr_enum_t, PVString, PVStringArray>
(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
const dbr_gr_enum* data = static_cast<const dbr_gr_enum*>(dbr);
copy_DBR_STS<dbr_gr_enum, dbr_enum_t, PVString, PVStringArray>(data, count, pvStructure);
}
// template<DBR type, primitive type, scalar Field, array Field>
template<typename T, typename pT, typename sF, typename aF>
void copy_DBR_CTRL(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
const T* data = static_cast<const T*>(dbr);
PVStructure::shared_pointer alarm = pvStructure->getSubField<PVStructure>("alarm");
if (alarm.get())
{
PVIntPtr status = alarm->getSubField<PVInt>("status");
if (status.get()) status->put(dbrStatus2alarmStatus[data->status]);
PVIntPtr severity = alarm->getSubField<PVInt>("severity");
if (severity.get()) severity->put(data->severity);
PVStringPtr message = alarm->getSubField<PVString>("message");
if (message.get()) message->put(dbrStatus2alarmMessage[data->status]);
}
PVStructurePtr disp = pvStructure->getSubField<PVStructure>("display");
if (disp.get())
{
PVStringPtr units = disp->getSubField<PVString>("units");
if (units.get()) units->put(std::string(data->units));
PVDoublePtr limitHigh = disp->getSubField<PVDouble>("limitHigh");
if (limitHigh.get()) limitHigh->put(data->upper_disp_limit);
PVDoublePtr limitLow = disp->getSubField<PVDouble>("limitLow");
if (limitLow.get()) limitLow->put(data->lower_disp_limit);
copy_format<T>(dbr, disp);
}
PVStructurePtr va = pvStructure->getSubField<PVStructure>("valueAlarm");
if (va.get())
{
std::tr1::shared_ptr<sF> highAlarmLimit = va->getSubField<sF>("highAlarmLimit");
if (highAlarmLimit.get()) highAlarmLimit->put(data->upper_alarm_limit);
std::tr1::shared_ptr<sF> highWarningLimit = va->getSubField<sF>("highWarningLimit");
if (highWarningLimit.get()) highWarningLimit->put(data->upper_warning_limit);
std::tr1::shared_ptr<sF> lowWarningLimit = va->getSubField<sF>("lowWarningLimit");
if (lowWarningLimit.get()) lowWarningLimit->put(data->lower_warning_limit);
std::tr1::shared_ptr<sF> lowAlarmLimit = va->getSubField<sF>("lowAlarmLimit");
if (lowAlarmLimit.get()) lowAlarmLimit->put(data->lower_alarm_limit);
}
PVStructurePtr ctrl = pvStructure->getSubField<PVStructure>("control");
if (ctrl.get())
{
PVDoublePtr limitHigh = ctrl->getSubField<PVDouble>("limitHigh");
if (limitHigh.get()) limitHigh->put(data->upper_ctrl_limit);
PVDoublePtr limitLow = ctrl->getSubField<PVDouble>("limitLow");
if (limitLow.get()) limitLow->put(data->lower_ctrl_limit);
}
copy_DBR<pT, sF, aF>(&data->value, count, pvStructure);
}
// enum specialization
template<>
void copy_DBR_CTRL<dbr_ctrl_enum, dbr_enum_t, PVString, PVStringArray>
(const void * dbr, unsigned count, PVStructure::shared_pointer const & pvStructure)
{
const dbr_ctrl_enum* data = static_cast<const dbr_ctrl_enum*>(dbr);
copy_DBR_STS<dbr_ctrl_enum, dbr_enum_t, PVString, PVStringArray>(data, count, pvStructure);
}
static copyDBRtoPVStructure copyFuncTable[] =
{
copy_DBR<string, PVString, PVStringArray>, // DBR_STRING
copy_DBR<dbr_short_t, PVShort, PVShortArray>, // DBR_INT, DBR_SHORT
copy_DBR<dbr_float_t, PVFloat, PVFloatArray>, // DBR_FLOAT
copy_DBR<dbr_enum_t, PVString, PVStringArray>, // DBR_ENUM
copy_DBR<int8 /*dbr_char_t*/, PVByte, PVByteArray>, // DBR_CHAR
copy_DBR<dbr_long_t, PVInt, PVIntArray>, // DBR_LONG
copy_DBR<dbr_double_t, PVDouble, PVDoubleArray>, // DBR_DOUBLE
copy_DBR_STS<dbr_sts_string, string, PVString, PVStringArray>, // DBR_STS_STRING
copy_DBR_STS<dbr_sts_short, dbr_short_t, PVShort, PVShortArray>, // DBR_STS_INT, DBR_STS_SHORT
copy_DBR_STS<dbr_sts_float, dbr_float_t, PVFloat, PVFloatArray>, // DBR_STS_FLOAT
copy_DBR_STS<dbr_sts_enum, dbr_enum_t, PVString, PVStringArray>, // DBR_STS_ENUM
copy_DBR_STS<dbr_sts_char, int8 /*dbr_char_t*/, PVByte, PVByteArray>, // DBR_STS_CHAR
copy_DBR_STS<dbr_sts_long, dbr_long_t, PVInt, PVIntArray>, // DBR_STS_LONG
copy_DBR_STS<dbr_sts_double, dbr_double_t, PVDouble, PVDoubleArray>, // DBR_STS_DOUBLE
copy_DBR_TIME<dbr_time_string, string, PVString, PVStringArray>, // DBR_TIME_STRING
copy_DBR_TIME<dbr_time_short, dbr_short_t, PVShort, PVShortArray>, // DBR_TIME_INT, DBR_TIME_SHORT
copy_DBR_TIME<dbr_time_float, dbr_float_t, PVFloat, PVFloatArray>, // DBR_TIME_FLOAT
copy_DBR_TIME<dbr_time_enum, dbr_enum_t, PVString, PVStringArray>, // DBR_TIME_ENUM
copy_DBR_TIME<dbr_time_char, int8 /*dbr_char_t*/, PVByte, PVByteArray>, // DBR_TIME_CHAR
copy_DBR_TIME<dbr_time_long, dbr_long_t, PVInt, PVIntArray>, // DBR_TIME_LONG
copy_DBR_TIME<dbr_time_double, dbr_double_t, PVDouble, PVDoubleArray>, // DBR_TIME_DOUBLE
copy_DBR_STS<dbr_sts_string, string, PVString, PVStringArray>, // DBR_GR_STRING -> DBR_STS_STRING
copy_DBR_GR<dbr_gr_short, dbr_short_t, PVShort, PVShortArray>, // DBR_GR_INT, DBR_GR_SHORT
copy_DBR_GR<dbr_gr_float, dbr_float_t, PVFloat, PVFloatArray>, // DBR_GR_FLOAT
copy_DBR_GR<dbr_gr_enum, dbr_enum_t, PVString, PVStringArray>, // DBR_GR_ENUM
copy_DBR_GR<dbr_gr_char, int8 /*dbr_char_t*/, PVByte, PVByteArray>, // DBR_GR_CHAR
copy_DBR_GR<dbr_gr_long, dbr_long_t, PVInt, PVIntArray>, // DBR_GR_LONG
copy_DBR_GR<dbr_gr_double, dbr_double_t, PVDouble, PVDoubleArray>, // DBR_GR_DOUBLE
copy_DBR_STS<dbr_sts_string, string, PVString, PVStringArray>, // DBR_CTRL_STRING -> DBR_STS_STRING
copy_DBR_CTRL<dbr_ctrl_short, dbr_short_t, PVShort, PVShortArray>, // DBR_CTRL_INT, DBR_CTRL_SHORT
copy_DBR_CTRL<dbr_ctrl_float, dbr_float_t, PVFloat, PVFloatArray>, // DBR_CTRL_FLOAT
copy_DBR_CTRL<dbr_ctrl_enum, dbr_enum_t, PVString, PVStringArray>, // DBR_CTRL_ENUM
copy_DBR_CTRL<dbr_ctrl_char, int8 /*dbr_char_t*/, PVByte, PVByteArray>, // DBR_CTRL_CHAR
copy_DBR_CTRL<dbr_ctrl_long, dbr_long_t, PVInt, PVIntArray>, // DBR_CTRL_LONG
copy_DBR_CTRL<dbr_ctrl_double, dbr_double_t, PVDouble, PVDoubleArray> // DBR_CTRL_DOUBLE
};
} // namespace
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, shared_from_this(), pvStructure, bitSet));
}
else
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
EXCEPTION_GUARD(channelGetRequester->getDone(errorStatus, shared_from_this(), PVStructure::shared_pointer(), BitSet::shared_pointer()));
}
}
void CAChannelGet::get()
{
channel->threadAttach();
/*
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.
*/
int result = ca_array_get_callback(getType,
0,
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, shared_from_this(), PVStructure::shared_pointer(), BitSet::shared_pointer()));
}
if (lastRequestFlag)
destroy();
}
/* --------------- epics::pvData::ChannelRequest --------------- */
Channel::shared_pointer CAChannelGet::getChannel()
{
return channel;
}
void CAChannelGet::cancel()
{
// noop
}
void CAChannelGet::lastRequest()
{
// TODO sync !!!
lastRequestFlag = true;
}
/* --------------- Destroyable --------------- */
void CAChannelGet::destroy()
{
// TODO
}
CAChannelPutPtr CAChannelPut::create(
CAChannel::shared_pointer const & channel,
ChannelPutRequester::shared_pointer const & channelPutRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
return CAChannelPutPtr(new CAChannelPut(channel, channelPutRequester, pvRequest));
}
CAChannelPut::~CAChannelPut()
{
}
CAChannelPut::CAChannelPut(CAChannel::shared_pointer const & channel,
ChannelPutRequester::shared_pointer const & channelPutRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
:
channel(channel),
channelPutRequester(channelPutRequester),
pvRequest(pvRequest),
lastRequestFlag(false),
block(false)
{
}
void CAChannelPut::activate()
{
if(pvStructure) throw std::runtime_error("CAChannelPut::activate() was called twice");
getType = getDBRType(pvRequest,channel->getNativeType());
pvStructure = createPVStructure(channel, getType, pvRequest);
bitSet = BitSetPtr(new BitSet(pvStructure->getStructure()->getNumberFields()));
// NOTE: we require value type, we can only put value field
PVStringPtr pvString = pvRequest->getSubField<PVString>("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());
EXCEPTION_GUARD(channelPutRequester->channelPutConnect(Status::Ok, shared_from_this(),
pvStructure->getStructure()));
}
void CAChannelPut::channelCreated(const Status& status,Channel::shared_pointer const & c)
{
chtype newType = getDBRType(pvRequest, channel->getNativeType());
if(newType!=getType) {
pvStructure.reset();
activate();
}
}
void CAChannelPut::channelStateChange(
Channel::shared_pointer const & channel,
Channel::ConnectionState connectionState)
{
if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) {
EXCEPTION_GUARD(channelPutRequester->channelDisconnect(connectionState==Channel::DESTROYED);)
}
}
void CAChannelPut::channelDisconnect(bool destroy)
{
EXCEPTION_GUARD(channelPutRequester->channelDisconnect(destroy);)
}
/* --------------- epics::pvAccess::ChannelPut --------------- */
namespace {
static void ca_put_handler(struct event_handler_args args)
{
CAChannelPut *channelPut = static_cast<CAChannelPut*>(args.usr);
channelPut->putDone(args);
}
static void ca_put_get_handler(struct event_handler_args args)
{
CAChannelPut *channelPut = static_cast<CAChannelPut*>(args.usr);
channelPut->getDone(args);
}
typedef int (*doPut)(CAChannel::shared_pointer const & channel, void *usrArg, PVStructure::shared_pointer const & from);
// template<primitive type, ScalarType, scalar Field, array Field>
template<typename pT, epics::pvData::ScalarType sT, typename sF, typename aF>
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<sF> value = std::tr1::static_pointer_cast<sF>(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<aF> value = pvStructure->getSubFieldT<aF>("value");
const pT* val = value->view().data();
int result = 0;
if(usrArg!=NULL) {
result = ca_array_put_callback(channel->getNativeType(),
static_cast<unsigned long>(value->getLength()),
channel->getChannelID(), val,
ca_put_handler, usrArg);
} else {
result = ca_array_put(channel->getNativeType(),
static_cast<unsigned long>(value->getLength()),
channel->getChannelID(), val);
}
if (result == ECA_NORMAL)
{
ca_flush_io();
}
return result;
}
}
// string specialization
template<>
int doPut_pvStructure<string, pvString, PVString, PVStringArray>(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<PVString> value = std::tr1::static_pointer_cast<PVString>(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<PVStringArray> value = pvStructure->getSubFieldT<PVStringArray>("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<dbr_enum_t, pvString, PVString, PVStringArray>(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<PVInt> value = std::tr1::static_pointer_cast<PVInt>(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<string, pvString, PVString, PVStringArray>, // DBR_STRING
doPut_pvStructure<dbr_short_t, pvShort, PVShort, PVShortArray>, // DBR_INT, DBR_SHORT
doPut_pvStructure<dbr_float_t, pvFloat, PVFloat, PVFloatArray>, // DBR_FLOAT
doPut_pvStructure<dbr_enum_t, pvString, PVString, PVStringArray>, // DBR_ENUM
doPut_pvStructure<int8 /*dbr_char_t*/, pvByte, PVByte, PVByteArray>, // DBR_CHAR
#if defined(__vxworks) || defined(__rtems__)
doPut_pvStructure<int32, pvInt, PVInt, PVIntArray>, // DBR_LONG
#else
doPut_pvStructure<dbr_long_t, pvInt, PVInt, PVIntArray>, // DBR_LONG
#endif
doPut_pvStructure<dbr_double_t, pvDouble, PVDouble, PVDoubleArray>, // DBR_DOUBLE
};
} // namespace
void CAChannelPut::putDone(struct event_handler_args &args)
{
if (args.status == ECA_NORMAL)
{
EXCEPTION_GUARD(channelPutRequester->putDone(Status::Ok, shared_from_this()));
}
else
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
EXCEPTION_GUARD(channelPutRequester->putDone(errorStatus, shared_from_this()));
}
}
void CAChannelPut::put(PVStructure::shared_pointer const & pvPutStructure,
BitSet::shared_pointer const & /*putBitSet*/)
{
channel->threadAttach();
doPut putFunc = doPutFuncTable[channel->getNativeType()];
if (putFunc)
{
// TODO now we always put all
if(block) {
int result = putFunc(channel, this, pvPutStructure);
if (result != ECA_NORMAL)
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result)));
EXCEPTION_GUARD(channelPutRequester->putDone(errorStatus, shared_from_this()));
}
} else {
int result = putFunc(channel,NULL, pvPutStructure);
if (result == ECA_NORMAL)
{
EXCEPTION_GUARD(channelPutRequester->putDone(Status::Ok, shared_from_this()));
}
else
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(result)));
EXCEPTION_GUARD(channelPutRequester->putDone(errorStatus, shared_from_this()));
}
}
}
else
{
// TODO remove
std::cout << "no put func implemented" << std::endl;
}
// TODO here???!!!
if (lastRequestFlag)
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, shared_from_this(), pvStructure, bitSet));
}
else
{
Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
EXCEPTION_GUARD(channelPutRequester->getDone(errorStatus, shared_from_this(),
PVStructure::shared_pointer(), BitSet::shared_pointer()));
}
// TODO here???!!!
if (lastRequestFlag)
destroy();
}
void CAChannelPut::get()
{
channel->threadAttach();
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, shared_from_this(),
PVStructure::shared_pointer(), BitSet::shared_pointer()));
}
}
/* --------------- epics::pvData::ChannelRequest --------------- */
Channel::shared_pointer CAChannelPut::getChannel()
{
return channel;
}
void CAChannelPut::cancel()
{
// noop
}
void CAChannelPut::lastRequest()
{
// TODO sync !!!
lastRequestFlag = true;
}
/* --------------- Destroyable --------------- */
void CAChannelPut::destroy()
{
// TODO
}
/* --------------- Monitor --------------- */
static void ca_subscription_handler(struct event_handler_args args)
{
CAChannelMonitor *channelMonitor = static_cast<CAChannelMonitor*>(args.usr);
channelMonitor->subscriptionEvent(args);
}
class CACMonitorQueue :
public std::tr1::enable_shared_from_this<CACMonitorQueue>
{
public:
POINTER_DEFINITIONS(CACMonitorQueue);
private:
size_t queueSize;
bool overrunInProgress;
bool isStarted;
Mutex mutex;
std::queue<MonitorElementPtr> monitorElementQueue;
public:
CACMonitorQueue(
int32 queueSize)
: queueSize(queueSize),
overrunInProgress(false),
isStarted(false)
{}
~CACMonitorQueue()
{
}
void start()
{
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)
{
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;
}
}
MonitorElementPtr poll()
{
Lock guard(mutex);
if(!isStarted) return MonitorElementPtr();
if(monitorElementQueue.empty()) return MonitorElementPtr();
MonitorElementPtr retval = monitorElementQueue.front();
return retval;
}
void release(MonitorElementPtr const & monitorElement)
{
Lock guard(mutex);
if(monitorElementQueue.empty()) {
throw std::runtime_error("client error calling release");
}
monitorElementQueue.pop();
}
};
CAChannelMonitorPtr CAChannelMonitor::create(
CAChannel::shared_pointer const & channel,
MonitorRequester::shared_pointer const & monitorRequester,
epics::pvData::PVStructure::shared_pointer const & pvRequest)
{
return CAChannelMonitorPtr(new CAChannelMonitor(channel, monitorRequester, pvRequest));
}
CAChannelMonitor::~CAChannelMonitor()
{
}
CAChannelMonitor::CAChannelMonitor(
CAChannel::shared_pointer const & channel,
MonitorRequester::shared_pointer const & monitorRequester,
PVStructurePtr const & pvRequest)
:
channel(channel),
monitorRequester(monitorRequester),
pvRequest(pvRequest)
{
}
void CAChannelMonitor::activate()
{
if(pvStructure) throw std::runtime_error("CAChannelMonitor::activate() was called twice");
getType = getDBRType(pvRequest, channel->getNativeType());
pvStructure = createPVStructure(channel, getType, pvRequest);
int32 queueSize = 2;
PVStructurePtr pvOptions = pvRequest->getSubField<PVStructure>("record._options");
if (pvOptions) {
PVStringPtr pvString = pvOptions->getSubField<PVString>("queueSize");
if (pvString) {
int size;
std::stringstream ss;
ss << pvString->get();
ss >> size;
if (size > 1) queueSize = size;
}
}
monitorQueue = CACMonitorQueuePtr(new CACMonitorQueue(queueSize));
channel->addChannelMonitor(shared_from_this());
EXCEPTION_GUARD(monitorRequester->monitorConnect(Status::Ok, shared_from_this(),
pvStructure->getStructure()));
}
void CAChannelMonitor::channelCreated(const Status& status,Channel::shared_pointer const & c)
{
chtype newType = getDBRType(pvRequest, channel->getNativeType());
if(newType!=getType) {
pvStructure.reset();
activate();
}
}
void CAChannelMonitor::channelStateChange(
Channel::shared_pointer const & channel,
Channel::ConnectionState connectionState)
{
if(connectionState==Channel::DISCONNECTED || connectionState==Channel::DESTROYED) {
EXCEPTION_GUARD(monitorRequester->channelDisconnect(connectionState==Channel::DESTROYED);)
}
}
void CAChannelMonitor::channelDisconnect(bool destroy)
{
EXCEPTION_GUARD(monitorRequester->channelDisconnect(destroy);)
}
void CAChannelMonitor::subscriptionEvent(struct event_handler_args &args)
{
if (args.status == ECA_NORMAL)
{
copyDBRtoPVStructure copyFunc = copyFuncTable[getType];
if (copyFunc) {
copyFunc(args.dbr, args.count, pvStructure);
monitorQueue->event(pvStructure);
// call monitorRequester even if queue is full
monitorRequester->monitorEvent(shared_from_this());
} else {
std::cout << "no copy func implemented" << std::endl;
}
}
else
{
//Status errorStatus(Status::STATUSTYPE_ERROR, string(ca_message(args.status)));
//EXCEPTION_GUARD(channelMonitorRequester->MonitorDone(errorStatus));
}
}
epics::pvData::Status CAChannelMonitor::start()
{
channel->threadAttach();
/*
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
int result = ca_create_subscription(getType,
0,
channel->getChannelID(), DBE_VALUE,
ca_subscription_handler, this,
&eventID);
if (result == ECA_NORMAL)
{
monitorQueue->start();
ca_flush_io();
return Status::Ok;
}
else
{
return Status(Status::STATUSTYPE_ERROR, string(ca_message(result)));
}
}
epics::pvData::Status CAChannelMonitor::stop()
{
channel->threadAttach();
int result = ca_clear_subscription(eventID);
if (result == ECA_NORMAL)
{
monitorQueue->stop();
return Status::Ok;
}
else
{
return Status(Status::STATUSTYPE_ERROR, string(ca_message(result)));
}
}
MonitorElementPtr CAChannelMonitor::poll()
{
return monitorQueue->poll();
}
void CAChannelMonitor::release(MonitorElementPtr const & monitorElement)
{
monitorQueue->release(monitorElement);
}
/* --------------- epics::pvData::ChannelRequest --------------- */
void CAChannelMonitor::cancel()
{
// noop
}
/* --------------- Destroyable --------------- */
void CAChannelMonitor::destroy()
{
channel->threadAttach();
ca_clear_subscription(eventID);
}
}}}