Files
pva2pva/pdbApp/pvif.h
2019-09-25 13:47:03 -07:00

415 lines
11 KiB
C++

#ifndef PVIF_H
#define PVIF_H
#include <map>
#include <asLib.h>
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
#include <dbLock.h>
#include <dbEvent.h>
#include <epicsVersion.h>
#include <pv/status.h>
#include <pv/bitSet.h>
#include <pv/pvData.h>
#include <pv/anyscalar.h>
#include <pv/qsrv.h>
#ifndef VERSION_INT
# define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#endif
#ifndef EPICS_VERSION_INT
# define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
#if EPICS_VERSION_INT>=VERSION_INT(3,16,0,2)
# define USE_MULTILOCK
#endif
namespace epics {
namespace pvAccess {
class ChannelRequester;
}
}
short PVD2DBR(epics::pvData::ScalarType pvt);
// copy from PVField (.value sub-field) to DBF buffer
QSRV_API
long copyPVD2DBF(const epics::pvData::PVField::const_shared_pointer& in,
void *outbuf, short outdbf, long *outnReq);
// copy from DBF buffer to PVField (.value sub-field)
QSRV_API
long copyDBF2PVD(const epics::pvData::shared_vector<const void>& buf,
const epics::pvData::PVField::shared_pointer& out,
epics::pvData::BitSet &changed,
const epics::pvData::PVStringArray::const_svector& choices);
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;
#ifdef EPICS_VERSION_INT
# if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
epicsInt64 dbf_INT64;
epicsUInt64 dbf_UINT64;
# endif
#endif
char dbf_STRING[MAX_STRING_SIZE];
};
struct QSRV_API DBCH {
dbChannel *chan;
DBCH() :chan(0) {}
explicit DBCH(dbChannel *ch); // calls dbChannelOpen()
explicit DBCH(const std::string& name);
~DBCH();
void swap(DBCH&);
operator dbChannel*() { return chan; }
operator const dbChannel*() const { return chan; }
dbChannel *operator->() { return chan; }
const dbChannel *operator->() const { return chan; }
private:
DBCH(const DBCH&);
DBCH& operator=(const DBCH&);
void prepare();
};
struct ASCred {
// string storage must be safely mutable. cf. asAddClient()
std::vector<char> user, host;
std::vector<std::vector<char> > groups;
void update(const std::tr1::shared_ptr<epics::pvAccess::ChannelRequester>& request);
};
struct ASCLIENT {
ASCLIENTPVT aspvt;
std::vector<ASCLIENTPVT> grppvt;
ASCLIENT() :aspvt(0) {}
~ASCLIENT();
// ASCred storage must remain valid
void add(dbChannel* chan, ASCred& cred);
bool canWrite();
};
struct pdbRecordInfo {
DBENTRY ent;
pdbRecordInfo(const char *name)
{
dbInitEntry(pdbbase, &ent);
if(dbFindRecordPart(&ent, &name))
throw std::runtime_error(ent.message);
}
~pdbRecordInfo()
{
dbFinishEntry(&ent);
}
const char *info(const char *key, const char *def =0)
{
if(dbFindInfo(&ent, key))
return def;
return dbGetInfoString(&ent);
}
};
struct pdbRecordIterator {
DBENTRY ent;
bool m_done;
pdbRecordIterator()
{
dbInitEntry(pdbbase, &ent);
m_done = dbFirstRecordType(&ent)!=0;
while(!m_done) {
if(dbFirstRecord(&ent)==0)
break;
// not instances of this type
m_done = dbNextRecordType(&ent)!=0;
}
}
pdbRecordIterator(const dbChannel *chan)
{
#if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
dbInitEntryFromRecord(dbChannelRecord(chan), &ent);
#else
dbInitEntry(pdbbase, &ent);
if(dbFindRecord(&ent, dbChannelRecord(chan)->name)!=0)
throw std::logic_error("Record not found");
#endif
m_done = false;
}
#if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
pdbRecordIterator(dbCommon *prec)
{
dbInitEntryFromRecord(prec, &ent);
m_done = false;
}
#endif
~pdbRecordIterator()
{
dbFinishEntry(&ent);
}
bool done() const { return m_done; }
bool next() {
if(!m_done && dbNextRecord(&ent)!=0)
{
// done with this recordType
while(true) {
m_done = dbNextRecordType(&ent)!=0;
if(m_done) break;
if(dbFirstRecord(&ent)==0)
break;
// not instances of this type
}
}
return m_done;
}
dbCommon* record() const {
return m_done ? NULL : (dbCommon*)ent.precnode->precord;
}
const char *name() const {
return m_done ? NULL : ent.precnode->recordname;
}
const char *info(const char *key, const char *def =0)
{
if(m_done || dbFindInfo(&ent, key))
return def;
return dbGetInfoString(&ent);
}
};
struct pdbInfoIterator {
DBENTRY ent;
bool m_done;
pdbInfoIterator(const pdbRecordIterator& I)
{
dbCopyEntryContents(&const_cast<pdbRecordIterator&>(I).ent, &ent);
m_done = dbFirstInfo(&ent)!=0;
}
~pdbInfoIterator()
{
dbFinishEntry(&ent);
}
bool done() const { return m_done; }
bool next() {
m_done = dbNextInfo(&ent)!=0;
return m_done;
}
const char *name() { return dbGetInfoName(&ent); }
const char *value() { return dbGetInfoString(&ent); }
};
struct DBEvent
{
dbEventSubscription subscript;
unsigned dbe_mask;
void *self;
unsigned index;
dbChannel *chan;
DBEvent() :subscript(NULL), self(NULL), index(0) {}
DBEvent(void* s) :subscript(NULL), self(s), index(0) {}
~DBEvent() {destroy();}
void create(dbEventCtx ctx, dbChannel *ch, EVENTFUNC *fn, unsigned mask)
{
subscript = db_add_event(ctx, ch, fn, this, mask);
if(!subscript)
throw std::runtime_error("Failed to subscribe to dbEvent");
chan = ch;
dbe_mask = mask;
}
void destroy() {
if(subscript) db_cancel_event(subscript);
}
bool operator!() const { return !subscript; }
private:
DBEvent(const DBEvent&);
DBEvent& operator=(const DBEvent&);
};
struct LocalFL
{
db_field_log *pfl;
bool ours;
LocalFL(db_field_log *pfl, dbChannel *pchan)
:pfl(pfl)
,ours(false)
{
if(!pfl && (ellCount(&pchan->pre_chain)!=0 || ellCount(&pchan->pre_chain)==0)) {
pfl = db_create_read_log(pchan);
if(pfl) {
ours = true;
pfl = dbChannelRunPreChain(pchan, pfl);
if(pfl) pfl = dbChannelRunPostChain(pchan, pfl);
}
}
}
~LocalFL() {
if(ours) db_delete_field_log(pfl);
}
};
struct DBScanLocker
{
dbCommon *prec;
DBScanLocker(dbChannel *chan) :prec(dbChannelRecord(chan))
{ dbScanLock(prec); }
DBScanLocker(dbCommon *prec) :prec(prec)
{ dbScanLock(prec); }
~DBScanLocker()
{ dbScanUnlock(prec); }
};
#ifdef USE_MULTILOCK
struct DBManyLock
{
dbLocker *plock;
DBManyLock() :plock(NULL) {}
DBManyLock(const std::vector<dbCommon*>& recs, unsigned flags=0)
:plock(dbLockerAlloc( (recs.size() > 0 ? (dbCommon**)&recs[0] : NULL), 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((dbCommon**)precs, nrecs, flags))
{
if(!plock) throw std::invalid_argument("Failed to create locker");
}
~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
{
dbLocker *plock;
DBManyLocker(dbLocker *L) :plock(L)
{
dbScanLockMany(plock);
}
~DBManyLocker()
{
dbScanUnlockMany(plock);
}
};
#endif
struct QSRV_API FieldName
{
struct Component {
std::string name;
epicsUInt32 index;
Component() :index((epicsUInt32)-1) {}
Component(const std::string& name, epicsUInt32 index = (epicsUInt32)-1)
:name(name), index(index)
{}
bool isArray() const { return index!=(epicsUInt32)-1; }
};
typedef std::vector<Component> parts_t;
parts_t parts;
FieldName() {}
explicit FieldName(const std::string&);
void swap(FieldName& o) {
parts.swap(o.parts);
}
bool empty() const { return parts.empty(); }
size_t size() const { return parts.size(); }
const Component& operator[](size_t i) const { return parts[i]; }
const Component& back() const { return parts.back(); }
// Apply field name(s) to given structure
// if ppenclose!=NULL then the address of the enclosing field (eg. structureArray)
// whose fieldOffset shoulbe by used, or NULL if no enclosing
epics::pvData::PVFieldPtr
lookup(const epics::pvData::PVStructurePtr& S, epics::pvData::PVField** ppenclose) const;
void show() const;
#if !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)
// Workaround needed for older GCC
private:
#endif
// Prevent default copy/assignment op's
FieldName(const FieldName&);
FieldName& operator=(const FieldName&);
};
struct QSRV_API PVIF {
PVIF(dbChannel *ch);
virtual ~PVIF() {}
dbChannel * const chan; // borrowed reference from PVIFBuilder
enum proc_t {
ProcPassive,
ProcInhibit,
ProcForce,
};
//! Copy from PDB record to pvalue (call dbChannelGet())
//! caller must lock record
virtual void put(epics::pvData::BitSet& mask, unsigned dbe, db_field_log *pfl) =0;
//! May copy from pvalue to PDB record (call dbChannelPut())
//! caller must lock record
virtual epics::pvData::Status get(const epics::pvData::BitSet& mask, proc_t proc=ProcInhibit, bool permit=true) =0;
//! Calculate DBE mask from changed bitset
virtual unsigned dbe(const epics::pvData::BitSet& mask) =0;
private:
PVIF(const PVIF&);
PVIF& operator=(const PVIF&);
};
struct QSRV_API PVIFBuilder {
virtual ~PVIFBuilder() {}
// fetch the structure description
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) =0;
virtual epics::pvData::FieldBuilderPtr dtype(epics::pvData::FieldBuilderPtr& builder,
const std::string& fld,
dbChannel *channel);
// Attach to a structure instance.
// must be of the type returned by dtype().
// must be the root structure
virtual PVIF* attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fld) =0;
static PVIFBuilder* create(const std::string& name);
protected:
PVIFBuilder() {}
private:
PVIFBuilder(const PVIFBuilder&);
PVIFBuilder& operator=(const PVIFBuilder&);
};
struct QSRV_API ScalarBuilder : public PVIFBuilder
{
virtual ~ScalarBuilder() {}
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL;
virtual PVIF* attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fld) OVERRIDE FINAL;
};
#endif // PVIF_H