ntndarray mapping
This commit is contained in:
@ -31,10 +31,42 @@ record(aSub, "$(N):ArrayData_") {
|
||||
|
||||
record(waveform, "$(N):ArrayData") {
|
||||
field(FTVL, "USHORT")
|
||||
field(NELM, "256")
|
||||
field(NELM, "262144")
|
||||
info(Q:group, {
|
||||
"$(N):Array":{
|
||||
"value":{+type:"NTNDArray", +channel:"VAL", +trigger:"*"}
|
||||
+id:"epics:nt/NTNDArray:1.0",
|
||||
"value":{+type:"any", +channel:"VAL", +trigger:"*"},
|
||||
"alarm.severity":{+type:"plain", +channel:"SEVR"},
|
||||
"alarm.status":{+type:"plain", +channel:"STAT"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
record(stringin, "$(N):ColorMode_") {
|
||||
field(VAL, "ColorMode")
|
||||
field(PINI, "YES")
|
||||
info(Q:group, {
|
||||
"$(N):Array":{
|
||||
"attribute[0].name":{+type:"plain", +channel:"VAL"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
record(mbbi, "$(N):ColorMode") {
|
||||
field(ZRST, "Mono")
|
||||
field(ONST, "Bayer")
|
||||
field(TWST, "RGB1")
|
||||
field(THST, "RGB2")
|
||||
field(FRST, "RGB3")
|
||||
field(FVST, "YUV444")
|
||||
field(SXST, "YUV422")
|
||||
field(SVST, "YUV411")
|
||||
field(VAL, "0") # Gray
|
||||
field(PINI, "YES")
|
||||
info(Q:group, {
|
||||
"$(N):Array":{
|
||||
"attribute[0].value":{+type:"any", +channel:"VAL"}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -39,6 +39,9 @@ struct context {
|
||||
grp.atomic = value.as<pvd::boolean>();
|
||||
grp.atomic_set = true;
|
||||
|
||||
} else if(field=="+id") {
|
||||
grp.id = value.as<std::string>();
|
||||
|
||||
} else {
|
||||
conf.warning += "Unknown group option ";
|
||||
conf.warning += field;
|
||||
@ -53,8 +56,9 @@ struct context {
|
||||
|
||||
} else if(key=="+channel") {
|
||||
fld.channel = value.ref<std::string>();
|
||||
if(fld.channel.empty())
|
||||
throw std::runtime_error("+channel can't be empty string");
|
||||
|
||||
} else if(key=="+id") {
|
||||
fld.id = value.ref<std::string>();
|
||||
|
||||
} else if(key=="+trigger") {
|
||||
fld.trigger = value.ref<std::string>();
|
||||
|
@ -21,11 +21,10 @@
|
||||
static
|
||||
long QSRV_image_demo(aSubRecord *prec)
|
||||
{
|
||||
epicsUInt32 W = *(epicsUInt32*)prec->a,
|
||||
H = *(epicsUInt32*)prec->b;
|
||||
epicsUInt32 H = *(epicsUInt32*)prec->a,
|
||||
W = *(epicsUInt32*)prec->b;
|
||||
epicsUInt16 *I = (epicsUInt16*)prec->vala;
|
||||
epicsUInt32 i, j;
|
||||
epicsUInt16 phase;
|
||||
|
||||
if(W*H>prec->nova) {
|
||||
(void)recGblSetSevr(prec, READ_ALARM, INVALID_ALARM);
|
||||
@ -33,13 +32,11 @@ long QSRV_image_demo(aSubRecord *prec)
|
||||
}
|
||||
|
||||
for(i=0; i<W; i++) {
|
||||
phase = 0;
|
||||
for(j=0; j<H; j++) {
|
||||
if(i%50==49 || j%50==49)
|
||||
I[i*H+j] = 65535;
|
||||
else
|
||||
I[i*H+j] = phase;
|
||||
phase += 65535/W;
|
||||
I[i*H+j] = ((epicsUInt32)j)*65535/H;
|
||||
}
|
||||
}
|
||||
|
||||
|
136
pdbApp/pdb.cpp
136
pdbApp/pdb.cpp
@ -51,8 +51,8 @@ struct GroupMemberInfo {
|
||||
|
||||
std::string pvname, // aka. name passed to dbChannelOpen()
|
||||
pvfldname; // PVStructure sub-field
|
||||
std::set<size_t> triggers; // indices in GroupInfo::members which are post()d on events from pvfldname
|
||||
size_t index; // index in GroupInfo::members
|
||||
std::string structID; // ID to assign to sub-field
|
||||
std::set<std::string> triggers; // names in GroupInfo::members_names which are post()d on events from pvfldname
|
||||
p2p::auto_ptr<PVIFBuilder> builder;
|
||||
|
||||
bool operator<(const GroupMemberInfo& o) const {
|
||||
@ -67,7 +67,7 @@ struct GroupMemberInfo {
|
||||
|
||||
struct GroupInfo {
|
||||
GroupInfo(const std::string& name) : name(name),atomic(Unset),hastriggers(false) {}
|
||||
std::string name;
|
||||
std::string name, structID;
|
||||
|
||||
typedef std::vector<GroupMemberInfo> members_t;
|
||||
members_t members;
|
||||
@ -120,7 +120,9 @@ struct PDBProcessor
|
||||
|
||||
if(target=="*") {
|
||||
for(size_t i=0; i<info.members.size(); i++) {
|
||||
srcmem.triggers.insert(i);
|
||||
if(info.members[i].pvname.empty())
|
||||
continue;
|
||||
srcmem.triggers.insert(info.members[i].pvfldname);
|
||||
if(PDBProviderDebug>2)
|
||||
fprintf(stderr, "%s, ", info.members[i].pvfldname.c_str());
|
||||
}
|
||||
@ -135,10 +137,16 @@ struct PDBProcessor
|
||||
}
|
||||
const GroupMemberInfo& targetmem = info.members[it3x->second];
|
||||
|
||||
// and finally, update source BitSet
|
||||
srcmem.triggers.insert(targetmem.index);
|
||||
if(PDBProviderDebug>2)
|
||||
fprintf(stderr, "%s, ", info.members[targetmem.index].pvfldname.c_str());
|
||||
if(targetmem.pvname.empty()) {
|
||||
if(PDBProviderDebug>2)
|
||||
fprintf(stderr, "<ignore: %s>, ", targetmem.pvfldname.c_str());
|
||||
|
||||
} else {
|
||||
// and finally, update source BitSet
|
||||
srcmem.triggers.insert(targetmem.pvfldname);
|
||||
if(PDBProviderDebug>2)
|
||||
fprintf(stderr, "%s, ", targetmem.pvfldname.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,8 +157,10 @@ struct PDBProcessor
|
||||
|
||||
FOREACH(it2, end2, info.members) {
|
||||
GroupMemberInfo& mem = *it2;
|
||||
if(mem.pvname.empty())
|
||||
continue;
|
||||
|
||||
mem.triggers.insert(mem.index); // default is self trigger
|
||||
mem.triggers.insert(mem.pvfldname); // default is self trigger
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,6 +172,9 @@ struct PDBProcessor
|
||||
{
|
||||
const char *json = rec.info("Q:group");
|
||||
if(!json) continue;
|
||||
if(PDBProviderDebug>2) {
|
||||
fprintf(stderr, "%s: info(Q:Group, ...\n", rec.name());
|
||||
}
|
||||
|
||||
try {
|
||||
GroupConfig conf;
|
||||
@ -190,6 +203,8 @@ struct PDBProcessor
|
||||
it = ins.first;
|
||||
}
|
||||
curgroup = &it->second;
|
||||
if(!grp.id.empty())
|
||||
curgroup->structID = grp.id;
|
||||
|
||||
for(GroupConfig::Group::fields_t::const_iterator fit=grp.fields.begin(), fend=grp.fields.end();
|
||||
fit!=fend; ++fit)
|
||||
@ -197,10 +212,7 @@ struct PDBProcessor
|
||||
const std::string& fldname = fit->first;
|
||||
const GroupConfig::Field& fld = fit->second;
|
||||
|
||||
if(fld.channel.empty())
|
||||
throw std::runtime_error("Missing required +channel");
|
||||
|
||||
GroupInfo::members_map_t::const_iterator oldgrp = curgroup->members_map.find(fldname);
|
||||
GroupInfo::members_map_t::const_iterator oldgrp(curgroup->members_map.find(fldname));
|
||||
if(oldgrp!=curgroup->members_map.end()) {
|
||||
const GroupMemberInfo& other = curgroup->members[oldgrp->second];
|
||||
fprintf(stderr, "%s.%s ignoring duplicate mapping %s%s and %s\n",
|
||||
@ -212,7 +224,8 @@ struct PDBProcessor
|
||||
|
||||
p2p::auto_ptr<PVIFBuilder> builder(PVIFBuilder::create(fld.type));
|
||||
|
||||
curgroup->members.push_back(GroupMemberInfo(recbase + fld.channel, fldname, builder));
|
||||
curgroup->members.push_back(GroupMemberInfo(fld.channel.empty() ? fld.channel : recbase + fld.channel, fldname, builder));
|
||||
curgroup->members.back().structID = fld.id;
|
||||
curgroup->members_map[fldname] = (size_t)-1; // placeholder see below
|
||||
|
||||
if(PDBProviderDebug>2) {
|
||||
@ -273,7 +286,6 @@ struct PDBProcessor
|
||||
|
||||
for(size_t i=0, N=info.members.size(); i<N; i++)
|
||||
{
|
||||
info.members[i].index = i;
|
||||
info.members_map[info.members[i].pvfldname] = i;
|
||||
}
|
||||
}
|
||||
@ -288,6 +300,12 @@ size_t PDBProvider::num_instances;
|
||||
|
||||
PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &)
|
||||
{
|
||||
/* Long view
|
||||
* 1. PDBProcessor collects info() tags and builds config of groups and group fields
|
||||
* (including those w/o a dbChannel)
|
||||
* 2. Build pvd::Structure and discard those w/o dbChannel
|
||||
* 3. Build the lockers for the triggers of each group field
|
||||
*/
|
||||
PDBProcessor proc;
|
||||
pvd::FieldCreatePtr fcreate(pvd::getFieldCreate());
|
||||
pvd::PVDataCreatePtr pvbuilder(pvd::getPVDataCreate());
|
||||
@ -308,8 +326,6 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &)
|
||||
if(persist_pv_map.find(info.name)!=persist_pv_map.end())
|
||||
throw std::runtime_error("name already in used");
|
||||
|
||||
const size_t nchans = info.members.size();
|
||||
|
||||
PDBGroupPV::shared_pointer pv(new PDBGroupPV());
|
||||
pv->weakself = pv;
|
||||
pv->name = info.name;
|
||||
@ -317,23 +333,30 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &)
|
||||
pv->pgatomic = info.atomic!=GroupInfo::False; // default true if Unset
|
||||
pv->monatomic = info.hastriggers;
|
||||
|
||||
pvd::shared_vector<PDBGroupPV::Info> members(nchans);
|
||||
// some gymnastics because Info isn't copyable
|
||||
pvd::shared_vector<PDBGroupPV::Info> members;
|
||||
typedef std::map<std::string, size_t> members_map_t;
|
||||
members_map_t members_map;
|
||||
{
|
||||
size_t nchans = 0;
|
||||
for(size_t i=0, N=info.members.size(); i<N; i++)
|
||||
if(!info.members[i].pvname.empty())
|
||||
nchans++;
|
||||
pvd::shared_vector<PDBGroupPV::Info> temp(nchans);
|
||||
members.swap(temp);
|
||||
}
|
||||
|
||||
std::vector<dbCommon*> records(nchans);
|
||||
std::vector<dbCommon*> records(members.size());
|
||||
|
||||
pvd::FieldBuilderPtr builder(fcreate->createFieldBuilder());
|
||||
builder->add("record", _options);
|
||||
builder = builder->add("record", _options);
|
||||
|
||||
for(size_t i=0; i<nchans; i++)
|
||||
if(!info.structID.empty())
|
||||
builder = builder->setId(info.structID);
|
||||
|
||||
for(size_t i=0, J=0, N=info.members.size(); i<N; i++)
|
||||
{
|
||||
GroupMemberInfo &mem = info.members[i];
|
||||
PDBGroupPV::Info& info = members[i];
|
||||
|
||||
DBCH chan(mem.pvname);
|
||||
|
||||
info.builder = PTRMOVE(mem.builder);
|
||||
assert(info.builder.get());
|
||||
|
||||
|
||||
// parse down attachment point to build/traverse structure
|
||||
FieldName parts(mem.pvfldname);
|
||||
@ -346,21 +369,37 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &)
|
||||
builder = builder->addNestedStructure(parts[j].name);
|
||||
}
|
||||
|
||||
builder->add(parts.back().name, info.builder->dtype(chan));
|
||||
if(!mem.structID.empty())
|
||||
builder = builder->setId(mem.structID);
|
||||
|
||||
DBCH chan;
|
||||
if(!mem.pvname.empty()) {
|
||||
DBCH temp(mem.pvname);
|
||||
chan.swap(temp);
|
||||
}
|
||||
|
||||
builder->add(parts.back().name, mem.builder->dtype(chan));
|
||||
|
||||
for(size_t j=0; j<parts.size()-1; j++)
|
||||
builder = builder->endNested();
|
||||
|
||||
info.attachment.swap(parts);
|
||||
info.chan.swap(chan);
|
||||
if(!mem.pvname.empty()) {
|
||||
members_map[mem.pvfldname] = J;
|
||||
PDBGroupPV::Info& info = members[J];
|
||||
|
||||
info.triggers.reserve(mem.triggers.size());
|
||||
FOREACH(idx, endidx, mem.triggers) {
|
||||
info.triggers.push_back(*idx);
|
||||
info.builder = PTRMOVE(mem.builder);
|
||||
assert(info.builder.get());
|
||||
|
||||
info.attachment.swap(parts);
|
||||
info.chan.swap(chan);
|
||||
|
||||
// info.triggers populated below
|
||||
|
||||
assert(info.chan);
|
||||
records[J] = dbChannelRecord(info.chan);
|
||||
|
||||
J++;
|
||||
}
|
||||
|
||||
assert(info.chan);
|
||||
records[i] = dbChannelRecord(info.chan);
|
||||
}
|
||||
pv->members.swap(members);
|
||||
|
||||
@ -372,15 +411,25 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &)
|
||||
DBManyLock L(&records[0], records.size(), 0);
|
||||
pv->locker.swap(L);
|
||||
|
||||
for(size_t i=0; i<nchans; i++)
|
||||
// construct locker for records triggered by each member
|
||||
for(size_t i=0, J=0, N=info.members.size(); i<N; i++)
|
||||
{
|
||||
PDBGroupPV::Info& info = pv->members[i];
|
||||
GroupMemberInfo &mem = info.members[i];
|
||||
if(mem.pvname.empty()) continue;
|
||||
PDBGroupPV::Info& info = pv->members[J++];
|
||||
|
||||
if(info.triggers.empty()) continue;
|
||||
if(mem.triggers.empty()) continue;
|
||||
|
||||
std::vector<dbCommon*> trig_records(info.triggers.size());
|
||||
for(size_t idx=0; idx<trig_records.size(); idx++) {
|
||||
trig_records[idx] = records[info.triggers[idx]];
|
||||
std::vector<dbCommon*> trig_records;
|
||||
trig_records.reserve(mem.triggers.size());
|
||||
|
||||
FOREACH(it, end, mem.triggers) {
|
||||
members_map_t::const_iterator imap(members_map.find(*it));
|
||||
if(imap==members_map.end())
|
||||
throw std::logic_error("trigger resolution missed map to non-dbChannel");
|
||||
|
||||
info.triggers.push_back(imap->second);
|
||||
trig_records.push_back(records[imap->second]);
|
||||
}
|
||||
|
||||
DBManyLock L(&trig_records[0], trig_records.size(), 0);
|
||||
@ -427,6 +476,7 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &)
|
||||
PDBGroupPV::Info& info = *it2;
|
||||
info.evt_VALUE.index = info.evt_PROPERTY.index = i++;
|
||||
info.evt_VALUE.self = info.evt_PROPERTY.self = pv;
|
||||
assert(info.chan);
|
||||
|
||||
info.pvif.reset(info.builder->attach(info.chan, pv->complete, info.attachment));
|
||||
|
||||
|
@ -48,6 +48,7 @@ void pdb_group_event(void *user_arg, struct dbChannel *chan,
|
||||
self->members[idx].pvif->put(self->scratch, evt->dbe_mask, pfl);
|
||||
|
||||
} else {
|
||||
// we ignore 'pfl' (and the dbEvent queue) when collecting an atomic snapshot
|
||||
|
||||
DBManyLocker L(info.locker); // lock only those records in the triggers list
|
||||
FOREACH(it, end, info.triggers)
|
||||
|
@ -22,7 +22,7 @@
|
||||
struct epicsShareClass GroupConfig
|
||||
{
|
||||
struct epicsShareClass Field {
|
||||
std::string type, channel, trigger;
|
||||
std::string type, channel, trigger, id;
|
||||
int putorder;
|
||||
|
||||
Field() :putorder(std::numeric_limits<int>::min()) {}
|
||||
@ -32,6 +32,7 @@ struct epicsShareClass GroupConfig
|
||||
std::swap(channel, o.channel);
|
||||
std::swap(trigger, o.trigger);
|
||||
std::swap(putorder, o.putorder);
|
||||
std::swap(id, o.id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -39,6 +40,7 @@ struct epicsShareClass GroupConfig
|
||||
typedef std::map<std::string, Field> fields_t;
|
||||
fields_t fields;
|
||||
bool atomic, atomic_set;
|
||||
std::string id;
|
||||
|
||||
Group() :atomic(true), atomic_set(false) {}
|
||||
|
||||
@ -46,6 +48,7 @@ struct epicsShareClass GroupConfig
|
||||
std::swap(fields, o.fields);
|
||||
std::swap(atomic, o.atomic);
|
||||
std::swap(atomic_set, o.atomic_set);
|
||||
std::swap(id, o.id);
|
||||
}
|
||||
};
|
||||
|
||||
@ -84,7 +87,7 @@ struct epicsShareClass PDBGroupPV : public PDBPV
|
||||
DBCH chan;
|
||||
p2p::auto_ptr<PVIFBuilder> builder;
|
||||
FieldName attachment;
|
||||
std::vector<size_t> triggers;
|
||||
std::vector<size_t> triggers; // index in PDBGroupPV::members
|
||||
DBManyLock locker; // lock only those channels being triggered
|
||||
p2p::auto_ptr<PVIF> pvif;
|
||||
DBEvent evt_VALUE, evt_PROPERTY;
|
||||
|
@ -537,6 +537,8 @@ ScalarBuilder::dtype(dbChannel *channel)
|
||||
PVIF*
|
||||
ScalarBuilder::attach(dbChannel *channel, const epics::pvData::PVStructurePtr& root, const FieldName& fldname)
|
||||
{
|
||||
if(!channel)
|
||||
throw std::runtime_error("+type:\"scalar\" requires +channel:");
|
||||
pvd::PVField *enclosing = 0;
|
||||
pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing));
|
||||
|
||||
@ -650,6 +652,8 @@ struct PlainBuilder : public PVIFBuilder
|
||||
const epics::pvData::PVStructurePtr& root,
|
||||
const FieldName& fldname) OVERRIDE FINAL
|
||||
{
|
||||
if(!channel)
|
||||
throw std::runtime_error("+type:\"plain\" requires +channel:");
|
||||
const long maxelem = dbChannelFinalElements(channel);
|
||||
|
||||
pvd::PVField *enclosing = 0;
|
||||
@ -662,9 +666,9 @@ struct PlainBuilder : public PVIFBuilder
|
||||
}
|
||||
};
|
||||
|
||||
struct NTNDArrayBuilder : public PVIFBuilder
|
||||
struct AnyScalarBuilder : public PVIFBuilder
|
||||
{
|
||||
virtual ~NTNDArrayBuilder() {}
|
||||
virtual ~AnyScalarBuilder() {}
|
||||
|
||||
// fetch the structure description
|
||||
virtual epics::pvData::FieldConstPtr dtype(dbChannel *channel) OVERRIDE FINAL {
|
||||
@ -679,8 +683,11 @@ struct NTNDArrayBuilder : public PVIFBuilder
|
||||
const epics::pvData::PVStructurePtr& root,
|
||||
const FieldName& fldname) OVERRIDE FINAL
|
||||
{
|
||||
if(!channel)
|
||||
throw std::runtime_error("+type:\"any\" requires +channel:");
|
||||
pvd::PVDataCreatePtr create(pvd::getPVDataCreate());
|
||||
const short dbr = dbChannelFinalFieldType(channel);
|
||||
const long maxelem = dbChannelFinalElements(channel);
|
||||
const pvd::ScalarType pvt = DBR2PVD(dbr);
|
||||
|
||||
pvd::PVField *enclosing = 0;
|
||||
@ -692,16 +699,21 @@ struct NTNDArrayBuilder : public PVIFBuilder
|
||||
|
||||
pvd::PVFieldPtr arr(value->get());
|
||||
if(!arr) {
|
||||
arr = create->createPVScalarArray(pvt);
|
||||
if(maxelem==1)
|
||||
arr = create->createPVScalar(pvt);
|
||||
else
|
||||
arr = create->createPVScalarArray(pvt);
|
||||
value->set(arr);
|
||||
}
|
||||
|
||||
return new PVIFPlain<pvd::PVScalarArray>(channel, arr, enclosing ? enclosing : arr.get());
|
||||
if(maxelem==1)
|
||||
return new PVIFPlain<pvd::PVScalar>(channel, arr, enclosing ? enclosing : arr.get());
|
||||
else
|
||||
return new PVIFPlain<pvd::PVScalarArray>(channel, arr, enclosing ? enclosing : arr.get());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
}//namespace
|
||||
|
||||
|
||||
@ -711,8 +723,8 @@ PVIFBuilder* PVIFBuilder::create(const std::string& type)
|
||||
return new ScalarBuilder;
|
||||
else if(type=="plain")
|
||||
return new PlainBuilder;
|
||||
else if(type=="NTNDArray" || type=="NTNDArray:1.0")
|
||||
return new NTNDArrayBuilder;
|
||||
else if(type=="any")
|
||||
return new AnyScalarBuilder;
|
||||
else
|
||||
throw std::runtime_error(std::string("Unknown +type=")+type);
|
||||
}
|
||||
|
Reference in New Issue
Block a user