From f3d7d7cd8f30a9c90f2570001c35c821b3b1e337 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 30 Oct 2018 17:45:11 -0700 Subject: [PATCH] change display.format, add display.form and .precision Replace printf-style format string with enum + integer. ScalarBuilder explode scalar()/scalarArray() --- pdbApp/pvif.cpp | 172 +++++++++++++++++++++++-------------------- pdbApp/qsrv-new.dbd | 3 - pdbApp/qsrv-old.dbd | 3 - testApp/testpvif.cpp | 52 +++++++++---- testApp/testpvif.db | 1 + 5 files changed, 133 insertions(+), 98 deletions(-) diff --git a/pdbApp/pvif.cpp b/pdbApp/pvif.cpp index d0d3fab..3cde6f9 100644 --- a/pdbApp/pvif.cpp +++ b/pdbApp/pvif.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -32,10 +33,6 @@ # endif #endif -extern "C" { -int qsrvDisableFormat = 1; -} - namespace pvd = epics::pvData; DBCH::DBCH(dbChannel *ch) :chan(ch) @@ -94,7 +91,8 @@ struct pvCommon : public pvTimeAlarm { pvd::BitSet maskVALUE, maskPROPERTY, maskVALUEPut; pvd::PVDoublePtr displayLow, displayHigh, controlLow, controlHigh; - pvd::PVStringPtr egu, desc, prec; + pvd::PVStringPtr egu, desc; + pvd::PVIntPtr fmt, prec; pvd::PVScalarPtr warnLow, warnHigh, alarmLow, alarmHigh; @@ -175,9 +173,32 @@ void attachTime(pvTimeAlarm& pvm, const pvd::PVStructurePtr& pv) #undef FMAP } +static +pvd::shared_vector buildFormats() +{ + pvd::shared_vector fmt; + fmt.push_back("Default"); + fmt.push_back("String"); + fmt.push_back("Binary"); + fmt.push_back("Decimal"); + fmt.push_back("Hex"); + fmt.push_back("Exponential"); + fmt.push_back("Engineering"); + return pvd::freeze(fmt); +} + +static const +pvd::shared_vector displayForms(buildFormats()); + // lookup fields and populate pvCommon. Non-existant fields will be NULL. void attachMeta(pvCommon& pvm, const pvd::PVStructurePtr& pv) { + { + pvd::PVStructurePtr fmt(pv->getSubField("display.form")); + if(fmt) { + fmt->getSubFieldT("choices")->replace(displayForms); + } + } attachTime(pvm, pv); #define FMAP(MNAME, PVT, FNAME, DBE) pvm.MNAME = pv->getSubField(FNAME); \ if(pvm.MNAME) pvm.mask ## DBE.set(pvm.MNAME->getFieldOffset()) @@ -187,7 +208,8 @@ void attachMeta(pvCommon& pvm, const pvd::PVStructurePtr& pv) FMAP(controlLow, PVDouble, "control.limitLow", PROPERTY); FMAP(egu, PVString, "display.units", PROPERTY); FMAP(desc, PVString, "display.description", PROPERTY); - FMAP(prec, PVString, "display.format", PROPERTY); + FMAP(prec, PVInt, "display.precision", PROPERTY); + FMAP(fmt, PVInt, "display.form.index", PROPERTY); FMAP(warnHigh, PVScalar, "valueAlarm.highWarningLimit", PROPERTY); FMAP(warnLow, PVScalar, "valueAlarm.lowWarningLimit", PROPERTY); FMAP(alarmHigh, PVScalar, "valueAlarm.highAlarmLimit", PROPERTY); @@ -443,61 +465,12 @@ void putMeta(const pvCommon& pv, unsigned dbe, db_field_log *pfl) FMAP(DBR_CTRL_DOUBLE, controlLow, lower_ctrl_limit); FMAP(DBR_GR_DOUBLE, egu, units); #undef FMAP - if(META::mask&DBR_PRECISION && pv.prec && !qsrvDisableFormat) { - // construct printf() style format. - // Widths based on epicsTypes.h - char buf[8]; - char *pos = &buf[8]; // build string in reverse order - bool ok = true; - - *--pos = '\0'; // buf[7] = '\0' - - switch(dbChannelFinalFieldType(pv.chan)) { -#ifdef USE_INT64 - case DBF_UINT64: - *--pos = 'l'; - *--pos = 'l'; -#endif - case DBF_UCHAR: - case DBF_USHORT: - case DBF_ULONG: - *--pos = 'u'; - break; -#ifdef USE_INT64 - case DBF_INT64: - *--pos = 'l'; - *--pos = 'l'; -#endif - case DBF_CHAR: - case DBF_SHORT: - case DBF_LONG: - *--pos = 'd'; - break; - case DBF_DOUBLE: - case DBF_FLOAT: - *--pos = 'g'; // either decimal or scientific - break; - case DBF_STRING: - *--pos = 's'; - break; - default: - ok = false; - } - - if(ok) { - long dp = meta.precision.dp; - if(dp<0) dp = 0; - else if(dp>=99) dp = 99; - for(;dp;dp = dp/10u) { - *--pos = '0'+(dp%10u); - } - *--pos = '%'; - } - pv.prec->put(pos); + if(META::mask&DBR_PRECISION && pv.prec) { + pv.prec->put(pvd::int32(meta.precision.dp)); } #define FMAP(MASK, MNAME, FNAME) if(META::mask&(MASK) && pv.MNAME) pv.MNAME->putFrom(meta.FNAME) // not handling precision until I get a better idea of what 'format' is supposed to be... - //FMAP(prec, PVScalar, "display.format", PROPERTY); + //FMAP(prec, PVScalar, "display.form", PROPERTY); FMAP(DBR_AL_DOUBLE, warnHigh, upper_warning_limit); FMAP(DBR_AL_DOUBLE, warnLow, lower_warning_limit); FMAP(DBR_AL_DOUBLE, alarmHigh, upper_alarm_limit); @@ -528,16 +501,15 @@ void putAll(const PVC &pv, unsigned dbe, db_field_log *pfl) } } -void findNSMask(pvTimeAlarm& pvmeta, dbChannel *chan, const epics::pvData::PVStructurePtr& pvalue) +void findNSMask(pvTimeAlarm& pvmeta, pdbRecordIterator& info, const epics::pvData::PVStructurePtr& pvalue) { - pdbRecordIterator info(chan); const char *UT = info.info("Q:time:tag"); if(UT && strncmp(UT, "nsec:lsb:", 9)==0) { try{ pvmeta.nsecMask = epics::pvData::castUnsafe(std::string(&UT[9])); }catch(std::exception& e){ pvmeta.nsecMask = 0; - std::cerr<name<<" : Q:time:tag nsec:lsb: requires a number not '"<0 && pvmeta.nsecMask<=32) { @@ -553,6 +525,25 @@ void findNSMask(pvTimeAlarm& pvmeta, dbChannel *chan, const epics::pvData::PVStr pvmeta.nsecMask = 0; } +void findFormat(pvTimeAlarm& pvmeta, pdbRecordIterator& info, const epics::pvData::PVStructurePtr& pvalue) +{ + const char *FMT = info.info("Q:form"); + if(FMT) { + pvd::PVScalarPtr fmt(pvalue->getSubField("display.form.index")); + if(fmt) { + bool found = false; + for(size_t i=0; !found && iputFrom(i); + } + if(!found) { + fmt->putFrom(0); // Default + } + } + } +} + + template struct PVIFScalarNumeric : public PVIF { @@ -583,7 +574,9 @@ struct PVIFScalarNumeric : public PVIF pvmeta.maskVALUEPut.set(0); pvmeta.maskVALUEPut.set(bit); } - findNSMask(pvmeta, chan, pvalue); + pdbRecordIterator info(chan); + findNSMask(pvmeta, info, pvalue); + findFormat(pvmeta, info, pvalue); } virtual ~PVIFScalarNumeric() {} @@ -679,19 +672,45 @@ ScalarBuilder::dtype(dbChannel *channel) if(maxelem!=1 && dbr==DBR_ENUM) dbr = DBF_SHORT; + pvd::FieldBuilderPtr builder(pvd::getFieldCreate()->createFieldBuilder()); + pvd::StandardFieldPtr standard(pvd::getStandardField()); + if(dbr==DBR_ENUM) - return pvd::getStandardField()->enumerated("alarm,timeStamp"); - - std::string options; - if(dbr!=DBR_STRING) - options = "alarm,timeStamp,display,control,valueAlarm"; + builder = builder->setId("epics:nt/NTEnum:1.0") + ->addNestedStructure("value") + ->add("index", pvd::pvInt) + ->addArray("choices", pvd::pvString) + ->endNested(); + else if(maxelem==1) + builder = builder->setId("epics:nt/NTScalar:1.0") + ->add("value", pvt); else - options = "alarm,timeStamp,display,control"; + builder = builder->setId("epics:nt/NTScalarArray:1.0") + ->addArray("value", pvt); - if(maxelem==1) - return pvd::getStandardField()->scalar(pvt, options); - else - return pvd::getStandardField()->scalarArray(pvt, options); + builder = builder->add("alarm", standard->alarm()) + ->add("timeStamp", standard->timeStamp()); + + if(dbr!=DBR_ENUM) { + builder = builder->addNestedStructure("display") + ->add("limitLow", pvd::pvDouble) + ->add("limitHigh", pvd::pvDouble) + ->add("description", pvd::pvString) + ->add("units", pvd::pvString) + ->add("precision", pvd::pvInt) + ->addNestedStructure("form") + ->setId("enum_t") + ->add("index", pvd::pvInt) + ->addArray("choices", pvd::pvString) + ->endNested() + ->endNested() + ->add("control", standard->control()); + + if(dbr!=DBR_STRING) + builder = builder->add("valueAlarm", standard->doubleAlarm()); + } + + return builder->createStructure(); } PVIF* @@ -704,7 +723,6 @@ ScalarBuilder::attach(dbChannel *channel, const epics::pvData::PVStructurePtr& r const short dbr = dbChannelFinalFieldType(channel); const long maxelem = dbChannelFinalElements(channel); - //const pvd::ScalarType pvt = DBR2PVD(dbr); if(maxelem==1) { switch(dbr) { @@ -904,8 +922,10 @@ struct PVIFMeta : public PVIF if(!field) throw std::logic_error("PVIFMeta attached type mis-match"); meta.chan = channel; + pdbRecordIterator info(chan); attachTime(meta, field); - findNSMask(meta, channel, field); + findNSMask(meta, info, field); + findFormat(meta, info, field); if(enclosing) { meta.maskALWAYS.clear(); meta.maskALWAYS.set(enclosing->getFieldOffset()); @@ -1094,7 +1114,3 @@ PVIFBuilder* PVIFBuilder::create(const std::string& type) else throw std::runtime_error(std::string("Unknown +type=")+type); } - -extern "C" { - epicsExportAddress(int, qsrvDisableFormat); -} diff --git a/pdbApp/qsrv-new.dbd b/pdbApp/qsrv-new.dbd index 409942f..cf9f69d 100644 --- a/pdbApp/qsrv-new.dbd +++ b/pdbApp/qsrv-new.dbd @@ -9,9 +9,6 @@ link("pva", "lsetPVA") device(waveform, CONSTANT, devWfPDBDemo, "QSRV Demo") # from imagedemo.c function(QSRV_image_demo) -# from pvif.cpp -# Disable mapping of display.format -variable(qsrvDisableFormat, int) # from pdb.cpp # Extra debug info when parsing group definitions variable(PDBProviderDebug, int) diff --git a/pdbApp/qsrv-old.dbd b/pdbApp/qsrv-old.dbd index 4f3c3af..99a37db 100644 --- a/pdbApp/qsrv-old.dbd +++ b/pdbApp/qsrv-old.dbd @@ -8,9 +8,6 @@ registrar(installPVAAddLinkHook) device(waveform, CONSTANT, devWfPDBDemo, "QSRV Demo") # from imagedemo.c function(QSRV_image_demo) -# from pvif.cpp -# Disable mapping of display.format -variable(qsrvDisableFormat, int) # from pdb.cpp # Extra debug info when parsing group definitions variable(PDBProviderDebug, int) diff --git a/testApp/testpvif.cpp b/testApp/testpvif.cpp index a8361df..7002510 100644 --- a/testApp/testpvif.cpp +++ b/testApp/testpvif.cpp @@ -107,6 +107,8 @@ void testScalar() p2p::auto_ptr pvif_ai_rval(builder.attach(chan_ai_rval, root, FieldName("ai_rval"))); p2p::auto_ptr pvif_mbbi(builder.attach(chan_mbbi, root, FieldName("mbbi"))); + testShow()<<"Entire structure\n"<stream().show(mask); #undef OFF mask.clear(); @@ -158,7 +161,8 @@ void testScalar() .set(OFF("i64.display.limitLow")) .set(OFF("i64.display.description")) .set(OFF("i64.display.units")) - .set(OFF("i64.display.format")) + .set(OFF("i64.display.precision")) + .set(OFF("i64.display.form.index")) .set(OFF("i64.control.limitHigh")) .set(OFF("i64.control.limitLow")) .set(OFF("i64.valueAlarm.highWarningLimit")) @@ -189,10 +193,12 @@ void testScalar() .set(OFF("si.display.limitHigh")) .set(OFF("si.display.limitLow")) .set(OFF("si.display.description")) - .set(OFF("si.display.format")) .set(OFF("si.display.units")) + .set(OFF("si.display.precision")) + .set(OFF("si.display.form.index")) .set(OFF("si.control.limitHigh")) - .set(OFF("si.control.limitLow"))); + .set(OFF("si.control.limitLow"))) + <<" si changes\n"<stream().show(mask); #undef OFF mask.clear(); @@ -215,7 +221,8 @@ void testScalar() .set(OFF("ai.display.limitHigh")) .set(OFF("ai.display.limitLow")) .set(OFF("ai.display.description")) - .set(OFF("ai.display.format")) + .set(OFF("ai.display.precision")) + .set(OFF("ai.display.form.index")) .set(OFF("ai.display.units")) .set(OFF("ai.control.limitHigh")) .set(OFF("ai.control.limitLow")) @@ -233,15 +240,17 @@ void testScalar() .set(OFF("ai_rval.display.limitHigh")) .set(OFF("ai_rval.display.limitLow")) .set(OFF("ai_rval.display.description")) - .set(OFF("ai_rval.display.format")) .set(OFF("ai_rval.display.units")) + .set(OFF("ai_rval.display.precision")) + .set(OFF("ai_rval.display.form.index")) .set(OFF("ai_rval.control.limitHigh")) .set(OFF("ai_rval.control.limitLow")) .set(OFF("ai_rval.valueAlarm.highWarningLimit")) .set(OFF("ai_rval.valueAlarm.lowWarningLimit")) .set(OFF("ai_rval.valueAlarm.highAlarmLimit")) .set(OFF("ai_rval.valueAlarm.lowAlarmLimit")) - ); + ) + <<" ai changes\n"<stream().show(mask); #undef OFF mask.clear(); @@ -260,7 +269,8 @@ void testScalar() .set(OFF("mbbi.alarm.message")) .set(OFF("mbbi.timeStamp.secondsPastEpoch")) .set(OFF("mbbi.timeStamp.nanoseconds")) - .set(OFF("mbbi.timeStamp.userTag"))); + .set(OFF("mbbi.timeStamp.userTag"))) + <<" mbbi changes\n"<stream().show(mask); #undef OFF mask.clear(); @@ -272,6 +282,8 @@ void testScalar() testFieldEqual(root, "li.display.limitHigh", 100.0); testFieldEqual(root, "li.display.limitLow", 10.0); testFieldEqual(root, "li.display.units", "arb"); + testFieldEqual(root, "li.display.precision", 0); + testFieldEqual(root, "li.display.form.index", 4); // "Hex" #ifdef USE_INT64 testFieldEqual(root, "i64.value", 0x7fffffffffffffffLL); @@ -284,6 +296,7 @@ void testScalar() testTodoBegin("Bug in int64inRecord get_units()"); testFieldEqual(root, "i64.display.units", "arb"); testTodoEnd(); + testFieldEqual(root, "i64.display.precision", 0); #endif testFieldEqual(root, "si.value", "hello"); @@ -298,8 +311,9 @@ void testScalar() testFieldEqual(root, "ai.timeStamp.nanoseconds", 12345678); testFieldEqual(root, "ai.display.limitHigh", 200.0); testFieldEqual(root, "ai.display.limitLow", 20.0); - testFieldEqual(root, "ai.display.format", ""); + testFieldEqual(root, "ai.display.precision", 2); testFieldEqual(root, "ai.display.units", "foo"); + testFieldEqual(root, "ai.display.form.index", 0); testFieldEqual(root, "ai_rval.value", 1234); testFieldEqual(root, "ai_rval.alarm.severity", 2); @@ -308,8 +322,9 @@ void testScalar() testFieldEqual(root, "ai_rval.timeStamp.userTag", 0); testFieldEqual(root, "ai_rval.display.limitHigh", 2147483647.0); testFieldEqual(root, "ai_rval.display.limitLow", -2147483648.0); - testFieldEqual(root, "ai_rval.display.format", ""); + testFieldEqual(root, "ai_rval.display.precision", 0); testFieldEqual(root, "ai_rval.display.units", ""); + testFieldEqual(root, "ai_rval.display.form.index", 0); testFieldEqual(root, "mbbi.value.index", 1); testFieldEqual(root, "mbbi.alarm.severity", 0); @@ -340,6 +355,15 @@ void testScalar() testEqual(prec_li->val, 102043); dbScanUnlock((dbCommon*)prec_li); +#ifdef USE_INT64 + dbScanLock((dbCommon*)prec_i64); + mask.clear(); + mask.set(root->getSubFieldT("i64.value")->getFieldOffset()); + pvif_i64->get(mask); + testEqual(prec_i64->val, epicsInt64(-0x8000000000000000LL)); + dbScanUnlock((dbCommon*)prec_i64); +#endif + #ifdef USE_INT64 dbScanLock((dbCommon*)prec_i64); mask.clear(); @@ -501,9 +525,9 @@ void testPlain() MAIN(testpvif) { - testPlan(71 + testPlan(75 #ifdef USE_INT64 - +11 + +13 #endif ); #ifdef USE_INT64 diff --git a/testApp/testpvif.db b/testApp/testpvif.db index 78011b2..82abd5f 100644 --- a/testApp/testpvif.db +++ b/testApp/testpvif.db @@ -5,6 +5,7 @@ record(longin, "test:li") { field(EGU, "arb") field(HOPR, "100") field(LOPR, "10") + info(Q:form, "Hex") } record(ai, "test:ai") { field(VAL, "42.2")