pvalink: use cached values for triggered scan
This commit is contained in:
36
common/pvatypemap.h
Normal file
36
common/pvatypemap.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* #define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE)
|
||||
*
|
||||
* BASETYPE is suffix of type from epicsTypes.h
|
||||
* epics ## BASETYPE -> eg. epicsUInt8
|
||||
* PVATYPE is storage type
|
||||
* PVATYPE -> eg epics::pvData::uint8
|
||||
* DBFTYPE is suffix of DBF_*
|
||||
* DBF_ ## DBFTYPE -> eg. DBF_UCHAR
|
||||
* PVACODE is suffix of ScalarType enum
|
||||
* epics::pvData::pv ## PVACODE -> epics::pvData::pvUByte
|
||||
*/
|
||||
CASE(UInt8, epics::pvData::uint8, UCHAR, UByte)
|
||||
CASE(Int8, epics::pvData::int8, CHAR, Byte)
|
||||
CASE(UInt16, epics::pvData::uint16, USHORT, UShort)
|
||||
CASE(Int16, epics::pvData::int16, SHORT, Short)
|
||||
CASE(UInt32, epics::pvData::uint32, ULONG, UInt)
|
||||
CASE(Int32, epics::pvData::int32, LONG, Int)
|
||||
CASE(Float32, float, FLOAT, Float)
|
||||
CASE(Float64, double, DOUBLE, Double)
|
||||
#ifndef CASE_SKIP_BOOL
|
||||
CASE(UInt8, epics::pvData::boolean, UCHAR, Boolean)
|
||||
#endif
|
||||
#ifdef CASE_REAL_INT64
|
||||
CASE(UInt64, epics::pvData::uint64, UINT64, ULong)
|
||||
CASE(Int64, epics::pvData::int64, INT64, Long)
|
||||
#elif defined(CASE_SQUEEZE_INT64)
|
||||
CASE(UInt32, epics::pvData::uint64, ULONG, ULong)
|
||||
CASE(Int32, epics::pvData::int64, LONG, Long)
|
||||
#else
|
||||
/* nothing */
|
||||
#endif
|
||||
#ifdef CASE_ENUM
|
||||
// yes really, Base uses SHORT (16-bit) while PVD uses Int (32-bit)
|
||||
CASE(Enum16, epics::pvData::int32, ENUM, Int)
|
||||
#endif
|
||||
/* #undef CASE */
|
@ -12,11 +12,14 @@
|
||||
#include <errlog.h>
|
||||
#include <initHooks.h>
|
||||
#include <alarm.h>
|
||||
#include <epicsAtomic.h>
|
||||
#include <epicsThreadPool.h>
|
||||
|
||||
#include <pv/pvAccess.h>
|
||||
#include <pv/clientFactory.h>
|
||||
|
||||
#include "helper.h"
|
||||
#include "iocshelper.h"
|
||||
#include "pvif.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
@ -27,6 +30,7 @@ extern "C" void (*dbAddLinkHook)(struct link *link, short dbfType);
|
||||
namespace {
|
||||
|
||||
typedef epicsGuard<pvd::Mutex> Guard;
|
||||
typedef epicsGuardRelease<pvd::Mutex> UnGuard;
|
||||
|
||||
struct pvaLink;
|
||||
struct pvaLinkChannel;
|
||||
@ -39,6 +43,16 @@ struct pvaGlobal_t {
|
||||
|
||||
pvd::Mutex lock;
|
||||
|
||||
struct Scan {
|
||||
// the PVA channel which triggered this scan
|
||||
std::tr1::weak_ptr<pvaLinkChannel> chan;
|
||||
bool usecached;
|
||||
Scan() :usecached(false) {}
|
||||
};
|
||||
|
||||
epicsThreadPrivate<Scan> scanmagic;
|
||||
epicsThreadPool *scanpool;
|
||||
|
||||
typedef std::map<std::string, std::tr1::shared_ptr<pvaLinkChannel> > channels_t;
|
||||
channels_t channels;
|
||||
|
||||
@ -52,6 +66,18 @@ struct pvaGlobal_t {
|
||||
{
|
||||
if(!provider)
|
||||
throw std::runtime_error("No pva provider");
|
||||
epicsThreadPoolConfig conf;
|
||||
epicsThreadPoolConfigDefaults(&conf);
|
||||
conf.workerPriority = epicsThreadPriorityLow+10; // similar to once thread
|
||||
conf.initialThreads = 1;
|
||||
scanpool = epicsThreadPoolCreate(&conf);
|
||||
if(!scanpool)
|
||||
throw std::runtime_error("Failed to create pvaLink scan pool");
|
||||
}
|
||||
~pvaGlobal_t()
|
||||
{
|
||||
provider->destroy();
|
||||
epicsThreadPoolDestroy(scanpool);
|
||||
}
|
||||
} *pvaGlobal;
|
||||
|
||||
@ -60,6 +86,8 @@ struct pvaLinkChannel : public pva::ChannelRequester, pva::MonitorRequester,
|
||||
{
|
||||
const std::string name;
|
||||
|
||||
static size_t refs;
|
||||
|
||||
typedef std::set<pvaLink*> links_t;
|
||||
links_t links;
|
||||
|
||||
@ -71,13 +99,28 @@ struct pvaLinkChannel : public pva::ChannelRequester, pva::MonitorRequester,
|
||||
//pva::ChannelPut::shared_pointer chanput;
|
||||
|
||||
pvd::PVStructurePtr lastval;
|
||||
pvd::PVScalarPtr isatomic;
|
||||
|
||||
epicsJob *scanjob;
|
||||
std::tr1::shared_ptr<pvaLinkChannel> scanself; // create ref loop while scan is queued
|
||||
bool scanatomic;
|
||||
|
||||
pvaLinkChannel(const char *name)
|
||||
:name(name)
|
||||
{}
|
||||
,scanjob(epicsJobCreate(pvaGlobal->scanpool, &pvaLinkChannel::scan, this))
|
||||
,scanatomic(false)
|
||||
{
|
||||
if(!scanjob)
|
||||
throw std::runtime_error("failed to create job for pvaLink");
|
||||
epics::atomic::increment(refs);
|
||||
}
|
||||
virtual ~pvaLinkChannel() {
|
||||
channelStateChange(chan, pva::Channel::DESTROYED);
|
||||
if(chan) chan->destroy();
|
||||
Guard G(lock);
|
||||
assert(links.empty());
|
||||
epicsJobDestroy(scanjob);
|
||||
scanjob = NULL;
|
||||
epics::atomic::decrement(refs);
|
||||
std::cerr<<"pvaLinkChannel: destroy "<<name<<"\n";
|
||||
}
|
||||
|
||||
void doConnect() {
|
||||
@ -86,9 +129,15 @@ struct pvaLinkChannel : public pva::ChannelRequester, pva::MonitorRequester,
|
||||
}
|
||||
void doClose() {
|
||||
Guard G(lock);
|
||||
errlogPrintf("pvaLink closing %s\n", name.c_str());
|
||||
channelStateChange(chan, pva::Channel::DESTROYED);
|
||||
chan->destroy();
|
||||
chan.reset();
|
||||
std::cerr<<"pvaLink: channel destroy "<<name<<"\n";
|
||||
}
|
||||
|
||||
static void scan(void* arg, epicsJobMode mode);
|
||||
|
||||
virtual std::string getRequesterName() { return "pvaLink"; }
|
||||
virtual void message(std::string const & message, pva::MessageType messageType)
|
||||
{
|
||||
@ -153,17 +202,33 @@ std::tr1::shared_ptr<pvaLinkChannel> pvaGlobal_t::connect(const char *name)
|
||||
|
||||
struct pvaLink
|
||||
{
|
||||
static size_t refs;
|
||||
|
||||
link * const plink;
|
||||
const short dbf;
|
||||
std::string name, field;
|
||||
const pva::Channel::shared_pointer chan;
|
||||
bool alive;
|
||||
bool alive; // attempt to catch some use after free
|
||||
|
||||
std::tr1::shared_ptr<pvaLinkChannel> lchan;
|
||||
|
||||
pvd::PVScalarPtr valueS;
|
||||
pvd::PVScalarArray::shared_pointer valueA;
|
||||
pvd::PVScalar::shared_pointer sevr, sec, nsec;
|
||||
pvd::ScalarType etype;
|
||||
|
||||
struct Value {
|
||||
bool valid;
|
||||
bool scalar;
|
||||
pvd::ScalarType etype;
|
||||
pvd::shared_vector<const void> valueA;
|
||||
dbrbuf valueS;
|
||||
epicsUInt16 sevr;
|
||||
epicsTimeStamp time;
|
||||
Value() :valid(false) {}
|
||||
};
|
||||
|
||||
Value atomcache;
|
||||
|
||||
pvaLink(link *L, const char *name, short f)
|
||||
:plink(L)
|
||||
@ -181,6 +246,7 @@ struct pvaLink
|
||||
lchan->links.insert(this);
|
||||
if(lchan->lastval)
|
||||
attach();
|
||||
epics::atomic::increment(refs);
|
||||
}
|
||||
~pvaLink()
|
||||
{
|
||||
@ -192,6 +258,7 @@ struct pvaLink
|
||||
pvaGlobal->channels.erase(lchan->name);
|
||||
lchan->doClose();
|
||||
}
|
||||
epics::atomic::decrement(refs);
|
||||
}
|
||||
|
||||
void detach()
|
||||
@ -206,6 +273,7 @@ struct pvaLink
|
||||
bool attach()
|
||||
{
|
||||
pvd::PVStructurePtr base(lchan->lastval);
|
||||
|
||||
if(!field.empty())
|
||||
base = base->getSubField<pvd::PVStructure>(field);
|
||||
if(!base) {
|
||||
@ -218,9 +286,11 @@ struct pvaLink
|
||||
{
|
||||
case pvd::scalar:
|
||||
valueS = std::tr1::static_pointer_cast<pvd::PVScalar>(value);
|
||||
etype = valueS->getScalar()->getScalarType();
|
||||
break;
|
||||
case pvd::scalarArray:
|
||||
valueA = std::tr1::static_pointer_cast<pvd::PVScalarArray>(value);
|
||||
etype = valueA->getScalarArray()->getElementType();
|
||||
break;
|
||||
default:
|
||||
errlogPrintf("pvaLink not .value : %s%c%s\n", name.c_str(), field.empty() ? ' ' : '.', field.c_str());
|
||||
@ -232,8 +302,45 @@ struct pvaLink
|
||||
nsec = base->getSubField<pvd::PVScalar>("timeStamp.nanoseconds");
|
||||
return true;
|
||||
}
|
||||
|
||||
void get(Value& v)
|
||||
{
|
||||
if(valueA) {
|
||||
valueA->getAs<const void>(v.valueA);
|
||||
v.etype = v.valueA.original_type();
|
||||
v.scalar = false;
|
||||
|
||||
} else if(valueS) {
|
||||
|
||||
switch(etype) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv ## PVACODE: v.valueS.dbf_##DBFTYPE = valueS->getAs<PVATYPE>(); break;
|
||||
#define CASE_SQUEEZE_INT64
|
||||
#include "pvatypemap.h"
|
||||
#undef CASE_SQUEEZE_INT64
|
||||
#undef CASE
|
||||
case pvd::pvString: {
|
||||
strncpy(v.valueS.dbf_STRING, valueS->getAs<std::string>().c_str(), sizeof(v.valueS.dbf_STRING));
|
||||
v.valueS.dbf_STRING[sizeof(v.valueS.dbf_STRING)-1] = '\0';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("putValue unsupported DBR code");
|
||||
}
|
||||
|
||||
v.etype = etype;
|
||||
v.scalar = true;
|
||||
}
|
||||
|
||||
v.sevr = sevr->getAs<epicsUInt16>();
|
||||
v.time.secPastEpoch = sec->getAs<epicsUInt32>()-POSIX_TIME_AT_EPICS_EPOCH;
|
||||
v.time.nsec = nsec->getAs<epicsUInt32>();
|
||||
v.valid = true;
|
||||
}
|
||||
};
|
||||
|
||||
size_t pvaLinkChannel::refs;
|
||||
size_t pvaLink::refs;
|
||||
|
||||
void pvaLinkChannel::channelStateChange(pva::Channel::shared_pointer const & channel, pva::Channel::ConnectionState connectionState)
|
||||
{
|
||||
Guard G(lock);
|
||||
@ -251,10 +358,12 @@ void pvaLinkChannel::channelStateChange(pva::Channel::shared_pointer const & cha
|
||||
L->detach();
|
||||
}
|
||||
lastval.reset();
|
||||
isatomic.reset();
|
||||
|
||||
if(chanmon) {
|
||||
chanmon->destroy();
|
||||
chanmon.reset();
|
||||
std::cerr<<"pvaLink: monitor destroy "<<name<<"\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -270,6 +379,7 @@ void pvaLinkChannel::monitorConnect(pvd::Status const & status,
|
||||
Guard G(lock);
|
||||
|
||||
lastval = pvaGlobal->create->createPVStructure(structure);
|
||||
isatomic = lastval->getSubField<pvd::PVScalar>("record._options.atomic");
|
||||
chanmon = monitor;
|
||||
|
||||
pvd::Status sstatus = monitor->start();
|
||||
@ -287,13 +397,16 @@ void pvaLinkChannel::monitorConnect(pvd::Status const & status,
|
||||
void pvaLinkChannel::monitorEvent(pva::Monitor::shared_pointer const & monitor)
|
||||
{
|
||||
Guard G(lock);
|
||||
if(!lastval) return;
|
||||
|
||||
pva::MonitorElementPtr elem;
|
||||
|
||||
bool updated = false;
|
||||
bool atomic = false;
|
||||
while(!!(elem=monitor->poll())) {
|
||||
try{
|
||||
lastval->copyUnchecked(*elem->pvStructurePtr, *elem->changedBitSet);
|
||||
atomic = isatomic->getAs<pvd::boolean>();
|
||||
updated = true;
|
||||
|
||||
monitor->release(elem);
|
||||
@ -303,8 +416,9 @@ void pvaLinkChannel::monitorEvent(pva::Monitor::shared_pointer const & monitor)
|
||||
}
|
||||
}
|
||||
|
||||
bool doscan = false;
|
||||
if(updated) {
|
||||
// std::cout<<"Update "<<lastval<<"\n";
|
||||
// check if we actually need to scan anything
|
||||
FOREACH(it, end, links) {
|
||||
pvaLink* L = *it;
|
||||
struct pv_link *ppv_link = &L->plink->value.pv_link;
|
||||
@ -312,13 +426,85 @@ void pvaLinkChannel::monitorEvent(pva::Monitor::shared_pointer const & monitor)
|
||||
if ((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && L->plink->precord->scan == 0))
|
||||
{
|
||||
// TODO: overflow once queue
|
||||
scanOnce(L->plink->precord);
|
||||
doscan = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(doscan && !scanself) { // need to scan, and not already queued, then queue
|
||||
std::cerr<<"pvaLink: queue scan from "<<name<<"\n";
|
||||
if(epicsJobQueue(scanjob)) {
|
||||
errlogPrintf("pvaLink: failed to queue scan from %s\n", name.c_str());
|
||||
} else {
|
||||
scanself = shared_from_this();
|
||||
scanatomic = atomic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pvaLinkChannel::scan(void* arg, epicsJobMode mode)
|
||||
{
|
||||
pvaLinkChannel *selfraw = (pvaLinkChannel*)arg;
|
||||
pvaGlobal_t::Scan myscan;
|
||||
try {
|
||||
std::tr1::shared_ptr<pvaLinkChannel> self;
|
||||
|
||||
Guard G(selfraw->lock);
|
||||
|
||||
selfraw->scanself.swap(self); // we take over ref, to keep channel alive, and allow re-queue
|
||||
|
||||
if(mode==epicsJobModeCleanup) return;
|
||||
|
||||
myscan.chan = self; // store a weak ref
|
||||
|
||||
links_t links(self->links); // TODO: avoid copy if set not changing
|
||||
|
||||
bool usecached = self->scanatomic;
|
||||
myscan.usecached = usecached;
|
||||
if(usecached) {
|
||||
FOREACH(it, end, links) {
|
||||
pvaLink *link = *it;
|
||||
link->get(link->atomcache);
|
||||
}
|
||||
}
|
||||
|
||||
pvaGlobal->scanmagic.set(usecached ? &myscan : NULL);
|
||||
|
||||
std::cerr<<"pvaLink: scan from "<<self->name<<" "<<(usecached?" use cached":"")<<"\n";
|
||||
|
||||
{
|
||||
UnGuard U(G);
|
||||
// we may scan a record after the originating link is re-targeted
|
||||
|
||||
FOREACH(it, end, links) {
|
||||
pvaLink *link = *it;
|
||||
struct pv_link *ppv_link = &link->plink->value.pv_link;
|
||||
dbCommon *prec=link->plink->precord;
|
||||
|
||||
if ((ppv_link->pvlMask & pvlOptCP) ||
|
||||
((ppv_link->pvlMask & pvlOptCPP) && link->plink->precord->scan == 0))
|
||||
{
|
||||
DBScanLocker L(prec);
|
||||
dbProcess(prec);
|
||||
}
|
||||
}
|
||||
}
|
||||
// another scan may be queued by this point
|
||||
|
||||
if(usecached) {
|
||||
FOREACH(it, end, links) {
|
||||
pvaLink *link = *it;
|
||||
link->atomcache.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
}catch(std::exception& e){
|
||||
errlogPrintf("%s: pvaLink exception while processing: %s\n", selfraw->name.c_str(), e.what());
|
||||
// what to do?
|
||||
}
|
||||
pvaGlobal->scanmagic.set(NULL);
|
||||
}
|
||||
|
||||
|
||||
#define TRY pvaLink *self = (pvaLink*)plink->value.pv_link.pvt; assert(self->alive); try
|
||||
#define CATCH(LOC) catch(std::exception& e) { \
|
||||
errlogPrintf("pvaLink " #LOC " fails %s: %s\n", plink->precord->name, e.what()); \
|
||||
@ -370,6 +556,8 @@ void pvaRemoveLink(struct dbLocker *locker, struct link *plink)
|
||||
int pvaIsConnected(const struct link *plink)
|
||||
{
|
||||
TRY {
|
||||
if(pvaGlobal->scanmagic.get()) return 1;
|
||||
|
||||
Guard G(self->lchan->lock);
|
||||
|
||||
return !!self->lchan->chanmon && (self->valueS || self->valueA);
|
||||
@ -381,6 +569,8 @@ int pvaIsConnected(const struct link *plink)
|
||||
int pvaGetDBFtype(const struct link *plink)
|
||||
{
|
||||
TRY {
|
||||
if(pvaGlobal->scanmagic.get()) return PVD2DBR(self->atomcache.etype);
|
||||
|
||||
Guard G(self->lchan->lock);
|
||||
pvd::ScalarType ftype;
|
||||
if(self->valueS)
|
||||
@ -390,21 +580,12 @@ int pvaGetDBFtype(const struct link *plink)
|
||||
else
|
||||
return DBF_LONG;
|
||||
switch(ftype) {
|
||||
#define MAP(DTYPE, PTYPE) case pvd::pv##PTYPE: return DBF_##DTYPE;
|
||||
MAP(CHAR, Byte);
|
||||
MAP(UCHAR, UByte);
|
||||
MAP(UCHAR, Boolean);
|
||||
MAP(SHORT, Short);
|
||||
MAP(USHORT, UShort);
|
||||
//MAP(ENUM, Int); // yes really, Base uses SHORT (16-bit) while PVD uses Int (32-bit)
|
||||
MAP(LONG, Int);
|
||||
MAP(ULONG, UInt);
|
||||
MAP(LONG, Long); // truncation
|
||||
MAP(ULONG, ULong);// truncation
|
||||
MAP(FLOAT, Float);
|
||||
MAP(DOUBLE, Double);
|
||||
MAP(STRING, String);
|
||||
#undef MAP
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: return DBF_##DBFTYPE;
|
||||
#define CASE_SQUEEZE_INT64
|
||||
#include "pvatypemap.h"
|
||||
#undef CASE_SQUEEZE_INT64
|
||||
#undef CASE
|
||||
case pvd::pvString: return DBF_STRING; // TODO: long string?
|
||||
}
|
||||
|
||||
}CATCH(pvaIsConnected)
|
||||
@ -414,6 +595,11 @@ int pvaGetDBFtype(const struct link *plink)
|
||||
long pvaGetElements(const struct link *plink, long *nelements)
|
||||
{
|
||||
TRY {
|
||||
if(pvaGlobal->scanmagic.get()) {
|
||||
if(self->atomcache.scalar) return 1;
|
||||
else return self->atomcache.valueA.size();
|
||||
}
|
||||
|
||||
Guard G(self->lchan->lock);
|
||||
if(self->valueA)
|
||||
return self->valueA->getLength();
|
||||
@ -427,9 +613,24 @@ long pvaGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
|
||||
{
|
||||
TRY {
|
||||
if(pvaGlobal->scanmagic.get()) {
|
||||
const void *buf;
|
||||
size_t count = pnRequest ? *pnRequest : 0;
|
||||
if(self->atomcache.scalar) {
|
||||
buf = (void*)&self->atomcache.valueS;
|
||||
count = std::min((size_t)1u, count);
|
||||
} else {
|
||||
buf = self->atomcache.valueA.data();
|
||||
count = std::min(self->atomcache.valueA.size(), count);
|
||||
}
|
||||
|
||||
pvd::castUnsafeV(count, DBR2PVD(dbrType), pbuffer, self->atomcache.etype, buf);
|
||||
*psevr = self->atomcache.sevr;
|
||||
*pstat = *psevr ? LINK_ALARM : 0;
|
||||
}
|
||||
|
||||
Guard G(self->lchan->lock);
|
||||
if(self->valueA) {
|
||||
//pvd::ScalarType ftype = DBR2PVD(dbrType);
|
||||
pvd::shared_vector<const void> arrval;
|
||||
self->valueA->getAs<const void>(arrval);
|
||||
|
||||
@ -437,20 +638,17 @@ long pvaGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
|
||||
pvd::castUnsafeV(nelem, DBR2PVD(dbrType), pbuffer, arrval.original_type(), arrval.data());
|
||||
|
||||
*psevr = self->sevr->getAs<epicsUInt16>();
|
||||
} else if(self->valueS) {
|
||||
|
||||
switch(dbrType) {
|
||||
#define CASE(FTYPE, CTYPE) case DBR_##FTYPE: *((CTYPE*)pbuffer) = self->valueS->getAs<CTYPE>(); break
|
||||
CASE(CHAR, epicsInt8);
|
||||
CASE(UCHAR, epicsUInt8);
|
||||
CASE(SHORT, epicsInt16);
|
||||
CASE(USHORT, epicsUInt16);
|
||||
CASE(ENUM, epicsEnum16);
|
||||
CASE(LONG, epicsInt32);
|
||||
CASE(ULONG, epicsUInt32);
|
||||
CASE(FLOAT, epicsFloat32);
|
||||
CASE(DOUBLE, epicsFloat64);
|
||||
#undef CASE
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: *((epics##BASETYPE*)pbuffer) = self->valueS->getAs<epics##BASETYPE>(); break;
|
||||
#define CASE_SKIP_BOOL
|
||||
#define CASE_ENUM
|
||||
#include "pvatypemap.h"
|
||||
#undef CASE_SKIP_BOOL
|
||||
#undef CASE_ENUM
|
||||
#undef CASE
|
||||
case DBR_STRING: {
|
||||
char *cbuf = (char*)pbuffer;
|
||||
strncpy(cbuf, self->valueS->getAs<std::string>().c_str(), MAX_STRING_SIZE);
|
||||
@ -460,7 +658,12 @@ long pvaGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
default:
|
||||
throw std::runtime_error("putValue unsupported DBR code");
|
||||
}
|
||||
|
||||
*psevr = self->sevr->getAs<epicsUInt16>();
|
||||
} else {
|
||||
*psevr = INVALID_ALARM;
|
||||
}
|
||||
*pstat = *psevr ? LINK_ALARM : 0;
|
||||
return 0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return S_dbLib_badLink;
|
||||
@ -469,7 +672,7 @@ long pvaGetValue(struct link *plink, short dbrType, void *pbuffer,
|
||||
long pvaGetControlLimits(const struct link *plink, double *lo, double *hi)
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
//Guard G(self->lchan->lock);
|
||||
*lo = *hi = 0.0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return 0;
|
||||
@ -478,7 +681,7 @@ long pvaGetControlLimits(const struct link *plink, double *lo, double *hi)
|
||||
long pvaGetGraphicLimits(const struct link *plink, double *lo, double *hi)
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
//Guard G(self->lchan->lock);
|
||||
*lo = *hi = 0.0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return 0;
|
||||
@ -488,7 +691,7 @@ long pvaGetAlarmLimits(const struct link *plink, double *lolo, double *lo,
|
||||
double *hi, double *hihi)
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
//Guard G(self->lchan->lock);
|
||||
*lolo = *lo = *hi = *hihi = 0.0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return 0;
|
||||
@ -497,7 +700,7 @@ long pvaGetAlarmLimits(const struct link *plink, double *lolo, double *lo,
|
||||
long pvaGetPrecision(const struct link *plink, short *precision)
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
//Guard G(self->lchan->lock);
|
||||
*precision = 0;
|
||||
}CATCH(pvaIsConnected)
|
||||
return 0;
|
||||
@ -506,7 +709,7 @@ long pvaGetPrecision(const struct link *plink, short *precision)
|
||||
long pvaGetUnits(const struct link *plink, char *units, int unitsSize)
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
//Guard G(self->lchan->lock);
|
||||
}CATCH(pvaIsConnected)
|
||||
return 0;
|
||||
}
|
||||
@ -516,9 +719,12 @@ long pvaGetAlarm(const struct link *plink, epicsEnum16 *status,
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
unsigned sevr = 0;
|
||||
if(self->sevr)
|
||||
unsigned sevr = INVALID_ALARM;
|
||||
if(pvaGlobal->scanmagic.get()) {
|
||||
sevr = self->atomcache.sevr;
|
||||
} else if(self->sevr) {
|
||||
sevr = self->sevr->getAs<epicsInt32>();
|
||||
}
|
||||
if(sevr)
|
||||
*status = LINK_ALARM;
|
||||
*severity = std::max(0u, std::min(sevr, 3u));
|
||||
@ -530,7 +736,9 @@ long pvaGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
|
||||
{
|
||||
TRY {
|
||||
Guard G(self->lchan->lock);
|
||||
if(self->sec && self->nsec) {
|
||||
if(pvaGlobal->scanmagic.get()) {
|
||||
*pstamp = self->atomcache.time;
|
||||
} else if(self->sec && self->nsec) {
|
||||
pstamp->secPastEpoch = self->sec->getAs<epicsUInt32>()-POSIX_TIME_AT_EPICS_EPOCH;
|
||||
pstamp->nsec = self->sec->getAs<epicsUInt32>();
|
||||
} else {
|
||||
@ -633,9 +841,24 @@ void initPVALink(initHookState state)
|
||||
std::cout<<"cleanupPVALink\n";
|
||||
dbAddLinkHook = nextAddLinkHook;
|
||||
|
||||
{
|
||||
Guard G(pvaGlobal->lock);
|
||||
if(pvaGlobal->channels.size()) {
|
||||
std::cerr<<"pvaLink still has "<<pvaGlobal->channels.size()
|
||||
<<"active channels after doCloseLinks()\n";
|
||||
}
|
||||
}
|
||||
|
||||
delete pvaGlobal;
|
||||
pvaGlobal = NULL;
|
||||
|
||||
if(epics::atomic::get(pvaLink::refs)) {
|
||||
std::cerr<<"pvaLink leaking "<<epics::atomic::get(pvaLink::refs)<<" links\n";
|
||||
}
|
||||
if(epics::atomic::get(pvaLinkChannel::refs)) {
|
||||
std::cerr<<"pvaLink leaking "<<epics::atomic::get(pvaLinkChannel::refs)<<" channels\n";
|
||||
}
|
||||
|
||||
}catch(std::exception& e){
|
||||
errlogPrintf("Error initializing pva link handling : %s\n", e.what());
|
||||
}
|
||||
@ -643,11 +866,23 @@ void initPVALink(initHookState state)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void pvalr(int level)
|
||||
{
|
||||
try {
|
||||
std::cout<<"pvaLink count "<<epics::atomic::get(pvaLink::refs)<<"\n"
|
||||
"pvaLinkChannel count "<<epics::atomic::get(pvaLinkChannel::refs)<<"\n";
|
||||
}catch(std::exception& e){
|
||||
std::cerr<<"Error :"<<e.what()<<"\n";
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void installPVAAddLinkHook()
|
||||
{
|
||||
initHookRegister(&initPVALink);
|
||||
iocshRegister<int, &pvalr>("pvalr", "level");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
epicsExportRegistrar(installPVAAddLinkHook);
|
||||
|
@ -212,19 +212,6 @@ void putTime(const pvCommon& pv, unsigned dbe, db_field_log *pfl)
|
||||
}
|
||||
}
|
||||
|
||||
union dbrbuf {
|
||||
epicsInt8 dbf_CHAR;
|
||||
epicsUInt8 dbf_UCHAR;
|
||||
epicsInt16 dbf_SHORT;
|
||||
epicsUInt16 dbf_USHORT;
|
||||
epicsEnum16 dbf_ENUM;
|
||||
epicsInt32 dbf_LONG;
|
||||
epicsUInt32 dbf_ULONG;
|
||||
epicsFloat32 dbf_FLOAT;
|
||||
epicsFloat64 dbf_DOUBLE;
|
||||
char dbf_STRING[MAX_STRING_SIZE];
|
||||
};
|
||||
|
||||
void putValue(const pvScalar& pv, unsigned dbe, db_field_log *pfl)
|
||||
{
|
||||
dbrbuf buf;
|
||||
@ -449,23 +436,32 @@ struct PVIFScalarNumeric : public PVIF
|
||||
pvd::ScalarType DBR2PVD(short dbr)
|
||||
{
|
||||
switch(dbr) {
|
||||
#define MAP(DTYPE, PTYPE) case DBR_##DTYPE: return pvd::pv##PTYPE
|
||||
MAP(CHAR, Byte);
|
||||
MAP(UCHAR, UByte);
|
||||
MAP(SHORT, Short);
|
||||
MAP(USHORT, UShort);
|
||||
MAP(ENUM, Int); // yes really, Base uses SHORT (16-bit) while PVD uses Int (32-bit)
|
||||
MAP(LONG, Int);
|
||||
MAP(ULONG, UInt);
|
||||
MAP(FLOAT, Float);
|
||||
MAP(DOUBLE, Double);
|
||||
MAP(STRING, String);
|
||||
#undef MAP
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
|
||||
#define CASE_ENUM
|
||||
#define CASE_SKIP_BOOL
|
||||
#include "pvatypemap.h"
|
||||
#undef CASE_ENUM
|
||||
#undef CASE_SKIP_BOOL
|
||||
#undef CASE
|
||||
case DBF_STRING: return pvd::pvString;
|
||||
default:
|
||||
throw std::invalid_argument("Unsupported DBR code");
|
||||
}
|
||||
}
|
||||
|
||||
short PVD2DBR(pvd::ScalarType pvt)
|
||||
{
|
||||
switch(pvt) {
|
||||
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: return DBR_##DBFTYPE;
|
||||
#define CASE_SQUEEZE_INT64
|
||||
#include "pvatypemap.h"
|
||||
#undef CASE_SQUEEZE_INT64
|
||||
#undef CASE
|
||||
default:
|
||||
throw std::invalid_argument("Unsupported pvType code");
|
||||
}
|
||||
}
|
||||
|
||||
void PVIF::Init()
|
||||
{
|
||||
}
|
||||
|
@ -13,6 +13,20 @@
|
||||
#include <pv/pvData.h>
|
||||
|
||||
epics::pvData::ScalarType DBR2PVD(short dbr);
|
||||
short PVD2DBR(epics::pvData::ScalarType pvt);
|
||||
|
||||
union dbrbuf {
|
||||
epicsInt8 dbf_CHAR;
|
||||
epicsUInt8 dbf_UCHAR;
|
||||
epicsInt16 dbf_SHORT;
|
||||
epicsUInt16 dbf_USHORT;
|
||||
epicsEnum16 dbf_ENUM;
|
||||
epicsInt32 dbf_LONG;
|
||||
epicsUInt32 dbf_ULONG;
|
||||
epicsFloat32 dbf_FLOAT;
|
||||
epicsFloat64 dbf_DOUBLE;
|
||||
char dbf_STRING[MAX_STRING_SIZE];
|
||||
};
|
||||
|
||||
struct DBCH {
|
||||
dbChannel *chan;
|
||||
@ -167,7 +181,12 @@ struct DBManyLock
|
||||
{
|
||||
dbLocker *plock;
|
||||
DBManyLock() :plock(NULL) {}
|
||||
DBManyLock(dbCommon **precs, size_t nrecs, unsigned flags=0)
|
||||
DBManyLock(const std::vector<dbCommon*>& recs, unsigned flags)
|
||||
:plock(dbLockerAlloc(&recs[0], recs.size(), flags))
|
||||
{
|
||||
if(!plock) throw std::invalid_argument("Failed to create locker");
|
||||
}
|
||||
DBManyLock(dbCommon * const *precs, size_t nrecs, unsigned flags=0)
|
||||
:plock(dbLockerAlloc(precs, nrecs, flags))
|
||||
{
|
||||
if(!plock) throw std::invalid_argument("Failed to create locker");
|
||||
@ -175,6 +194,9 @@ struct DBManyLock
|
||||
~DBManyLock() { if(plock) dbLockerFree(plock); }
|
||||
void swap(DBManyLock& O) { std::swap(plock, O.plock); }
|
||||
operator dbLocker*() { return plock; }
|
||||
private:
|
||||
DBManyLock(const DBManyLock&);
|
||||
DBManyLock& operator=(const DBManyLock&);
|
||||
};
|
||||
|
||||
struct DBManyLocker
|
||||
|
Reference in New Issue
Block a user