change display.format, add display.form and .precision

Replace printf-style format string with enum + integer.

ScalarBuilder explode scalar()/scalarArray()
This commit is contained in:
Michael Davidsaver
2018-10-30 17:45:11 -07:00
parent 7bc5cbf957
commit f3d7d7cd8f
5 changed files with 133 additions and 98 deletions

View File

@ -11,6 +11,7 @@
#include <alarm.h>
#include <errSymTbl.h>
#include <epicsVersion.h>
#include <errlog.h>
#include <pv/status.h>
#include <pv/bitSet.h>
@ -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<const std::string> buildFormats()
{
pvd::shared_vector<std::string> 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<const std::string> 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<pvd::PVStructure>("display.form"));
if(fmt) {
fmt->getSubFieldT<pvd::PVStringArray>("choices")->replace(displayForms);
}
}
attachTime(pvm, pv);
#define FMAP(MNAME, PVT, FNAME, DBE) pvm.MNAME = pv->getSubField<pvd::PVT>(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<pvd::uint32>(std::string(&UT[9]));
}catch(std::exception& e){
pvmeta.nsecMask = 0;
std::cerr<<dbChannelRecord(chan)->name<<" : Q:time:tag nsec:lsb: requires a number not '"<<UT[9]<<"'\n";
std::cerr<<info.name()<<" : Q:time:tag nsec:lsb: requires a number not '"<<UT[9]<<"'\n";
}
}
if(pvmeta.nsecMask>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<pvd::PVScalar>("display.form.index"));
if(fmt) {
bool found = false;
for(size_t i=0; !found && i<displayForms.size(); i++) {
if((found=(displayForms[i]==FMT)))
fmt->putFrom<pvd::uint32>(i);
}
if(!found) {
fmt->putFrom<pvd::uint32>(0); // Default
}
}
}
}
template<typename PVX, typename META>
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);
}

View File

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

View File

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

View File

@ -107,6 +107,8 @@ void testScalar()
p2p::auto_ptr<PVIF> pvif_ai_rval(builder.attach(chan_ai_rval, root, FieldName("ai_rval")));
p2p::auto_ptr<PVIF> pvif_mbbi(builder.attach(chan_mbbi, root, FieldName("mbbi")));
testShow()<<"Entire structure\n"<<root;
pvd::BitSet mask;
dbScanLock((dbCommon*)prec_li);
@ -123,18 +125,19 @@ void testScalar()
.set(OFF("li.alarm.message"))
.set(OFF("li.timeStamp.secondsPastEpoch"))
.set(OFF("li.timeStamp.nanoseconds"))
//.set(OFF("li.timeStamp.userTag"))
.set(OFF("li.display.limitHigh"))
.set(OFF("li.display.limitLow"))
.set(OFF("li.display.description"))
.set(OFF("li.display.format"))
.set(OFF("li.display.units"))
.set(OFF("li.display.precision"))
.set(OFF("li.display.form.index"))
.set(OFF("li.control.limitHigh"))
.set(OFF("li.control.limitLow"))
.set(OFF("li.valueAlarm.highWarningLimit"))
.set(OFF("li.valueAlarm.lowWarningLimit"))
.set(OFF("li.valueAlarm.highAlarmLimit"))
.set(OFF("li.valueAlarm.lowAlarmLimit")));
.set(OFF("li.valueAlarm.lowAlarmLimit")))
<<" li changes\n"<<root->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"<<root->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"<<root->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"<<root->stream().show(mask);
#undef OFF
mask.clear();
@ -272,6 +282,8 @@ void testScalar()
testFieldEqual<pvd::PVDouble>(root, "li.display.limitHigh", 100.0);
testFieldEqual<pvd::PVDouble>(root, "li.display.limitLow", 10.0);
testFieldEqual<pvd::PVString>(root, "li.display.units", "arb");
testFieldEqual<pvd::PVInt>(root, "li.display.precision", 0);
testFieldEqual<pvd::PVInt>(root, "li.display.form.index", 4); // "Hex"
#ifdef USE_INT64
testFieldEqual<pvd::PVLong>(root, "i64.value", 0x7fffffffffffffffLL);
@ -284,6 +296,7 @@ void testScalar()
testTodoBegin("Bug in int64inRecord get_units()");
testFieldEqual<pvd::PVString>(root, "i64.display.units", "arb");
testTodoEnd();
testFieldEqual<pvd::PVInt>(root, "i64.display.precision", 0);
#endif
testFieldEqual<pvd::PVString>(root, "si.value", "hello");
@ -298,8 +311,9 @@ void testScalar()
testFieldEqual<pvd::PVInt>(root, "ai.timeStamp.nanoseconds", 12345678);
testFieldEqual<pvd::PVDouble>(root, "ai.display.limitHigh", 200.0);
testFieldEqual<pvd::PVDouble>(root, "ai.display.limitLow", 20.0);
testFieldEqual<pvd::PVString>(root, "ai.display.format", "");
testFieldEqual<pvd::PVInt>(root, "ai.display.precision", 2);
testFieldEqual<pvd::PVString>(root, "ai.display.units", "foo");
testFieldEqual<pvd::PVInt>(root, "ai.display.form.index", 0);
testFieldEqual<pvd::PVInt>(root, "ai_rval.value", 1234);
testFieldEqual<pvd::PVInt>(root, "ai_rval.alarm.severity", 2);
@ -308,8 +322,9 @@ void testScalar()
testFieldEqual<pvd::PVInt>(root, "ai_rval.timeStamp.userTag", 0);
testFieldEqual<pvd::PVDouble>(root, "ai_rval.display.limitHigh", 2147483647.0);
testFieldEqual<pvd::PVDouble>(root, "ai_rval.display.limitLow", -2147483648.0);
testFieldEqual<pvd::PVString>(root, "ai_rval.display.format", "");
testFieldEqual<pvd::PVInt>(root, "ai_rval.display.precision", 0);
testFieldEqual<pvd::PVString>(root, "ai_rval.display.units", "");
testFieldEqual<pvd::PVInt>(root, "ai_rval.display.form.index", 0);
testFieldEqual<pvd::PVInt>(root, "mbbi.value.index", 1);
testFieldEqual<pvd::PVInt>(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

View File

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