ntndarray mapping

This commit is contained in:
Michael Davidsaver
2017-09-20 20:00:59 -05:00
parent 67bda75bcb
commit 0d4e99c1a2
7 changed files with 161 additions and 62 deletions

View File

@ -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"}
}
})
}

View File

@ -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>();

View File

@ -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;
}
}

View File

@ -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));

View File

@ -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)

View File

@ -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;

View File

@ -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);
}