diff --git a/iocBoot/iocimagedemo/image.db b/iocBoot/iocimagedemo/image.db index c225440..beb4975 100644 --- a/iocBoot/iocimagedemo/image.db +++ b/iocBoot/iocimagedemo/image.db @@ -36,8 +36,7 @@ record(waveform, "$(N):ArrayData") { "$(N):Array":{ +id:"epics:nt/NTNDArray:1.0", "value":{+type:"any", +channel:"VAL", +trigger:"*"}, - "alarm.severity":{+type:"plain", +channel:"SEVR"}, - "alarm.status":{+type:"plain", +channel:"STAT"} + "":{+type:"meta", +channel:"SEVR"} } }) } diff --git a/pdbApp/configparse.cpp b/pdbApp/configparse.cpp index ec5f464..36a3990 100644 --- a/pdbApp/configparse.cpp +++ b/pdbApp/configparse.cpp @@ -21,12 +21,19 @@ typedef std::map config_t; struct context { std::string msg; std::string group, field, key; + unsigned depth; // number of '{'s + // depth 0 - invalid + // depth 1 - top Object + // depth 2 - Group + // depth 3 - field + + context() :depth(0u) {} GroupConfig conf; void can_assign() { - if(group.empty() || field.empty()) + if(depth<2 || depth>3) throw std::runtime_error("Can't assign value in this context"); } @@ -34,7 +41,7 @@ struct context { can_assign(); GroupConfig::Group& grp = conf.groups[group]; - if(key.empty()) { + if(depth==2) { if(field=="+atomic") { grp.atomic = value.as(); grp.atomic_set = true; @@ -48,7 +55,7 @@ struct context { } field.clear(); - } else { + } else if(depth==3) { GroupConfig::Field& fld = grp.fields[field]; if(key=="+type") { @@ -125,8 +132,9 @@ int conf_string(void * ctx, const unsigned char * stringVal, int conf_start_map(void * ctx) { TRY { - if(!self->group.empty() && !self->field.empty() && !self->key.empty()) - throw std::runtime_error("Too deep"); + self->depth++; + if(self->depth>3) + throw std::runtime_error("Group field def. can't contain Object (too deep)"); return 1; }CATCH() } @@ -135,16 +143,16 @@ int conf_map_key(void * ctx, const unsigned char * key, unsigned int stringLen) { TRY { - if(stringLen==0) - throw std::runtime_error("empty key"); + if(stringLen==0 && self->depth!=2) + throw std::runtime_error("empty group or key name not allowed"); std::string name((const char*)key, stringLen); - if(self->group.empty()) + if(self->depth==1) self->group.swap(name); - else if(self->field.empty()) + else if(self->depth==2) self->field.swap(name); - else if(self->key.empty()) + else if(self->depth==3) self->key.swap(name); else throw std::logic_error("Too deep!!"); @@ -158,10 +166,15 @@ int conf_end_map(void * ctx) TRY { assert(self->key.empty()); // cleared in assign() - if(!self->field.empty()) + if(self->depth==3) + self->key.clear(); + else if(self->depth==2) self->field.clear(); - else if(!self->group.empty()) + else if(self->depth==1) self->group.clear(); + else + throw std::logic_error("Invalid depth"); + self->depth--; return 1; }CATCH() diff --git a/pdbApp/pdb.cpp b/pdbApp/pdb.cpp index 476fdae..75464eb 100644 --- a/pdbApp/pdb.cpp +++ b/pdbApp/pdb.cpp @@ -363,13 +363,14 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &) // parse down attachment point to build/traverse structure FieldName parts(mem.pvfldname); - assert(!parts.empty()); - for(size_t j=0; jaddNestedStructureArray(parts[j].name); - else - builder = builder->addNestedStructure(parts[j].name); + if(!parts.empty()) { + for(size_t j=0; jaddNestedStructureArray(parts[j].name); + else + builder = builder->addNestedStructure(parts[j].name); + } } if(!mem.structID.empty()) @@ -381,10 +382,15 @@ PDBProvider::PDBProvider(const epics::pvAccess::Configuration::shared_pointer &) chan.swap(temp); } - builder = mem.builder->dtype(builder, parts.back().name, chan); + if(!parts.empty()) + builder = mem.builder->dtype(builder, parts.back().name, chan); + else + builder = mem.builder->dtype(builder, "", chan); - for(size_t j=0; jendNested(); + if(!parts.empty()) { + for(size_t j=0; jendNested(); + } if(!mem.pvname.empty()) { members_map[mem.pvfldname] = J; @@ -611,6 +617,8 @@ PDBProvider::createChannel(std::string const & channelName, FieldName::FieldName(const std::string& pv) { + if(pv.empty()) + return; Splitter S(pv.c_str(), '.'); std::string part; while(S.snip(part)) { diff --git a/pdbApp/pvif.cpp b/pdbApp/pvif.cpp index c05d649..ded611d 100644 --- a/pdbApp/pvif.cpp +++ b/pdbApp/pvif.cpp @@ -400,7 +400,7 @@ void putAll(const PVC &pv, unsigned dbe, db_field_log *pfl) } } -void findNSMask(pvCommon& pvmeta, dbChannel *chan, const epics::pvData::PVStructurePtr& pvalue) +void findNSMask(pvTimeAlarm& pvmeta, dbChannel *chan, const epics::pvData::PVStructurePtr& pvalue) { pdbRecordIterator info(chan); const char *UT = info.info("Q:time:tag"); @@ -741,7 +741,9 @@ struct PVIFMeta : public PVIF pvd::PVStructurePtr field(std::dynamic_pointer_cast(fld)); if(!field) throw std::logic_error("PVIFMeta attached type mis-match"); + meta.chan = channel; attachTime(meta, field); + findNSMask(meta, channel, field); if(enclosing) { meta.maskALWAYS.clear(); meta.maskALWAYS.set(enclosing->getFieldOffset()); @@ -788,8 +790,15 @@ struct MetaBuilder : public PVIFBuilder dbChannel *channel) { pvd::StandardFieldPtr std(pvd::getStandardField()); - return builder->add("alarm", std->alarm()) - ->add("timeStamp", std->timeStamp()); + if(fld.empty()) { + return builder->add("alarm", std->alarm()) + ->add("timeStamp", std->timeStamp()); + } else { + return builder->addNestedStructure(fld) + ->add("alarm", std->alarm()) + ->add("timeStamp", std->timeStamp()) + ->endNested(); + } } // Attach to a structure instance. @@ -800,7 +809,7 @@ struct MetaBuilder : public PVIFBuilder const FieldName& fldname) OVERRIDE FINAL { if(!channel) - throw std::runtime_error("+type:\"any\" requires +channel:"); + throw std::runtime_error("+type:\"meta\" requires +channel:"); pvd::PVField *enclosing = 0; pvd::PVFieldPtr fld(fldname.lookup(root, &enclosing)); diff --git a/testApp/testgroupconfig.cpp b/testApp/testgroupconfig.cpp index c5d455b..27a77f7 100644 --- a/testApp/testgroupconfig.cpp +++ b/testApp/testgroupconfig.cpp @@ -19,9 +19,12 @@ void test_parse() " \"fld\":{\n" " \"+type\": \"simple\"," " \"+putorder\": -4" + " },\n" + " \"\":{\n" + " \"+type\": \"top\"" " }\n" " },\n" - " \"fld2\":{\n" + " \"grpb\":{\n" " }\n" "}"; @@ -36,12 +39,18 @@ void test_parse() testEqual(conf.groups["grpa"].fields["fld"].type, "simple"); testEqual(conf.groups["grpa"].fields["fld"].channel, ""); testEqual(conf.groups["grpa"].fields["fld"].putorder, -4); + + testEqual(conf.groups["grpa"].fields[""].type, "top"); } void test_fail() { testDiag("test_fail()"); + { + GroupConfig conf; + testThrows(std::runtime_error, GroupConfig::parse("{", conf)); + } { GroupConfig conf; testThrows(std::runtime_error, GroupConfig::parse("{\"G\":{\"F\":{\"K\":{}}}}", conf)); @@ -56,7 +65,7 @@ void test_fail() MAIN(testanyscalar) { - testPlan(8); + testPlan(10); try { test_parse(); test_fail();