11 Commits

Author SHA1 Message Date
Michael Davidsaver 521154fd52 1.2.0 2019-03-18 11:44:01 -07:00
Andrew Johnson 6d1cbd87fd Make a local copy of softIocExit.db for standalone builds 2019-03-12 10:52:50 -05:00
Michael Davidsaver 68904852b9 update release notes 2019-03-11 14:51:39 -07:00
Michael Davidsaver a3dc712174 avoid unnecessary copy
dev. leftovers...
2019-03-11 14:47:42 -07:00
Michael Davidsaver 1ce449d40d parse unknown format as number
forward compatibility
2019-01-15 19:15:21 -08:00
Michael Davidsaver 67b384bb6c doc Q:form 2019-01-15 19:15:21 -08:00
Michael Davidsaver f3d7d7cd8f change display.format, add display.form and .precision
Replace printf-style format string with enum + integer.

ScalarBuilder explode scalar()/scalarArray()
2019-01-15 18:48:18 -08:00
Michael Davidsaver 7bc5cbf957 fix int64 in pva link 2019-01-08 10:27:57 -08:00
Michael Davidsaver 0ab4f937a7 test 64-bit integer field access 2019-01-08 09:31:51 -08:00
Michael Davidsaver 3c5a5c805c fix 64-bit integer field access 2019-01-08 09:10:10 -08:00
Michael Davidsaver c79074e7fa doc 2018-12-23 09:26:28 -08:00
19 changed files with 332 additions and 165 deletions
+2 -4
View File
@@ -109,9 +109,7 @@ At present each pva2pva process can act as a uni-directional proxy,
presenting a pvAccess server on one interface,
and a client on other(s).
The file [example.cmd](example.cmd) provides a starting point.
Adjust *EPICS_PVAS_INTF_ADDR_LIST* and *EPICS_PVA_ADDR_LIST*
according to the host computer's network configuration.
The file [loopback.conf](loopback.conf) provides a starting point.
At present there are no safe guard against creating loops
where a gateway client side connects to its own server side.
@@ -121,5 +119,5 @@ the interface used for the server (either directly, or included in a broadcast d
```
cd pva2pva
./bin/linux-x86_64/pva2pva example.cmd
./bin/linux-x86_64/pva2pva loopback.conf
```
+3 -3
View File
@@ -1,8 +1,8 @@
# Module (source) version
EPICS_QSRV_MAJOR_VERSION = 1
EPICS_QSRV_MINOR_VERSION = 1
EPICS_QSRV_MAINTENANCE_VERSION = 1
EPICS_QSRV_DEVELOPMENT_FLAG = 1
EPICS_QSRV_MINOR_VERSION = 2
EPICS_QSRV_MAINTENANCE_VERSION = 0
EPICS_QSRV_DEVELOPMENT_FLAG = 0
# ABI version
EPICS_QSRV_ABI_MAJOR_VERSION = 1
+21
View File
@@ -141,6 +141,27 @@ record(ai, "...") {
}
@endcode
@subsection qsrv_form QSRV Display Form Option
The value of the OPI display form hint ('display.form') can be set set with the "Q:form" info() tag.
This hint, along with 'display.precision', is used by some OPI clients to control how values are rendered.
The text value of the tag must be one of the following choices.
@li Default
@li String
@li Binary
@li Decimal
@li Hex
@li Exponential
@li Engineering
@code
record(ai, "...") {
info(Q:form, "Default") # implied default
}
@endcode
@subsection qsrv_link PVAccess Links
When built against Base >= 3.16.1, support is enabled for PVAccess links,
+12
View File
@@ -2,6 +2,18 @@
@page release_notes Release Notes
Release 1.2.0 (Mar 2019)
========================
- Incompatible changes
- The field 'display.format' is replaced with 'display.form' and 'display.precision'.
https://github.com/epics-base/pva2pva/issues/19
- Additions
- Use @code info(Q:form, "...") @endcode to set 'display.form'. See @ref qsrv_form
- Fixes
- Correct handling of 64-bit integer fields.
- Install a copy of softIocExit.db for standalone builds
Release 1.1.0 (Nov 2018)
==========================
-26
View File
@@ -1,26 +0,0 @@
# Bind gateway server side to this interface
epicsEnvSet("EPICS_PVAS_INTF_ADDR_LIST","10.0.1.200")
# Gateway client side searches here. Must not include gateway server side interface
epicsEnvSet("EPICS_PVA_ADDR_LIST", "10.1.1.255")
# Prevent gateway client from automatically populating the address list,
# which would include the gateway server side interface
epicsEnvSet("EPICS_PVA_AUTO_ADDR_LIST","NO")
gwstart()
# PVA variables
#
# Server side
#
# EPICS_PVAS_INTF_ADDR_LIST - Bind to this interface for both UDP and TCP
# EPICS_PVAS_SERVER_PORT - default TCP port
# EPICS_PVAS_BROADCAST_PORT - Listen for searches on this port
#
# EPICS_PVA_SERVER_PORT - Unused if EPICS_PVAS_SERVER_PORT set
#
# Client side
#
# EPICS_PVA_BROADCAST_PORT - Default search port for *ADDR_LIST
# EPICS_PVA_ADDR_LIST - Space seperated list of search endpoints (bcast or unicast)
# EPICS_PVA_AUTO_ADDR_LIST - YES/NO whether to populate ADDR_LIST with all local interface bcast addrs
+1 -7
View File
@@ -64,19 +64,13 @@ GWServerChannelProvider::createChannel(std::string const & channelName,
short priority, std::string const & addressx)
{
GWChannel::shared_pointer ret;
std::string newName;
std::string address = channelRequester->getRequesterName();
if(!channelName.empty())
{
// rewrite name
newName = channelName;
//newName[0] = 'y';
Guard G(cache.cacheLock);
ChannelCacheEntry::shared_pointer ent(cache.lookup(newName)); // recursively locks cacheLock
ChannelCacheEntry::shared_pointer ent(cache.lookup(channelName)); // recursively locks cacheLock
if(ent)
{
+5
View File
@@ -73,6 +73,11 @@ softIocPVA_LIBS += $(EPICS_BASE_IOC_LIBS)
DBD += softIocPVA.dbd
DBD += qsrv.dbd
ifneq ($(FINAL_LOCATION),$(EPICS_BASE))
# Copy from Base if we won't be installed there
DB_INSTALLS += $(EPICS_BASE)/db/softIocExit.db
endif
softIocPVA_DBD += softIoc.dbd
softIocPVA_DBD += PVAServerRegister.dbd
softIocPVA_DBD += qsrv.dbd
+2
View File
@@ -25,8 +25,10 @@ pvd::ScalarType DBR2PVD(short dbr)
switch(dbr) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
#define CASE_SKIP_BOOL
#define CASE_REAL_INT64
#include "pv/typemap.h"
#undef CASE_SKIP_BOOL
#undef CASE_REAL_INT64
#undef CASE
case DBF_ENUM: return pvd::pvUShort;
case DBF_STRING: return pvd::pvString;
+2
View File
@@ -389,8 +389,10 @@ pvd::ScalarType DBR2PVD(short dbr)
switch(dbr) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
#define CASE_SKIP_BOOL
#define CASE_REAL_INT64
#include "pv/typemap.h"
#undef CASE_SKIP_BOOL
#undef CASE_REAL_INT64
#undef CASE
case DBF_ENUM: return pvd::pvUShort;
case DBF_STRING: return pvd::pvString;
+110 -84
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>
@@ -27,13 +28,11 @@
#ifdef EPICS_VERSION_INT
# if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
# define USE_INT64
// effects all uses of pv/typemap.h
# define CASE_REAL_INT64
# endif
#endif
extern "C" {
int qsrvDisableFormat = 1;
}
namespace pvd = epics::pvData;
DBCH::DBCH(dbChannel *ch) :chan(ch)
@@ -92,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;
@@ -173,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())
@@ -185,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);
@@ -441,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);
@@ -526,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) {
@@ -551,6 +525,29 @@ 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) {
try {
fmt->putFrom(std::string(FMT)); // attempt to parse as number
}catch(std::exception& e){
errlogPrintf("%s: info(Q:form, \"%s\") is not known format: %s\n", info.name(), FMT, e.what());
}
}
}
}
}
template<typename PVX, typename META>
struct PVIFScalarNumeric : public PVIF
{
@@ -581,7 +578,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() {}
@@ -651,15 +650,11 @@ short PVD2DBR(pvd::ScalarType pvt)
{
switch(pvt) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pvd::pv##PVACODE: return DBR_##DBFTYPE;
#ifdef USE_INT64
# define CASE_REAL_INT64
#else
#ifndef USE_INT64
# define CASE_SQUEEZE_INT64
#endif
#include "pv/typemap.h"
#ifdef USE_INT64
# undef CASE_REAL_INT64
#else
#ifndef USE_INT64
# undef CASE_SQUEEZE_INT64
#endif
#undef CASE
@@ -681,19 +676,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*
@@ -706,7 +727,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) {
@@ -716,6 +736,10 @@ ScalarBuilder::attach(dbChannel *channel, const epics::pvData::PVStructurePtr& r
case DBR_USHORT:
case DBR_LONG:
case DBR_ULONG:
#ifdef USE_INT64
case DBR_INT64:
case DBR_UINT64:
#endif
return new PVIFScalarNumeric<pvScalar, metaDOUBLE>(channel, fld, enclosing);
case DBR_FLOAT:
case DBR_DOUBLE:
@@ -736,6 +760,10 @@ ScalarBuilder::attach(dbChannel *channel, const epics::pvData::PVStructurePtr& r
case DBR_ULONG:
case DBR_STRING:
case DBR_FLOAT:
#ifdef USE_INT64
case DBR_INT64:
case DBR_UINT64:
#endif
case DBR_DOUBLE:
return new PVIFScalarNumeric<pvArray, metaDOUBLE>(channel, fld, enclosing);
}
@@ -898,8 +926,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());
@@ -1088,7 +1118,3 @@ PVIFBuilder* PVIFBuilder::create(const std::string& type)
else
throw std::runtime_error(std::string("Unknown +type=")+type);
}
extern "C" {
epicsExportAddress(int, qsrvDisableFormat);
}
+7
View File
@@ -52,6 +52,13 @@ union dbrbuf {
epicsUInt32 dbf_ULONG;
epicsFloat32 dbf_FLOAT;
epicsFloat64 dbf_DOUBLE;
#ifdef EPICS_VERSION_INT
# if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
epicsInt64 dbf_INT64;
epicsUInt64 dbf_UINT64;
# endif
#endif
char dbf_STRING[MAX_STRING_SIZE];
};
-3
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)
-3
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)
+4 -1
View File
@@ -230,7 +230,7 @@ void testDBR2PVD_array()
MAIN(testdbf_copy)
{
testPlan(51);
testPlan(53);
try{
testPVD2DBR_scalar<pvd::pvDouble, double>(DBF_DOUBLE, 42.2, 42.2);
testPVD2DBR_scalar<pvd::pvDouble, pvd::uint16>(DBF_USHORT, 42.2, 42u);
@@ -238,6 +238,9 @@ MAIN(testdbf_copy)
testPVD2DBR_scalar<pvd::pvInt, pvd::int32>(DBF_LONG, 42, 42);
testPVD2DBR_scalar<pvd::pvInt, char[MAX_STRING_SIZE]>(DBF_STRING, 42, std::string("42"));
testPVD2DBR_scalar<pvd::pvLong, pvd::int64>(DBF_INT64, 42, 42);
testPVD2DBR_scalar<pvd::pvLong, char[MAX_STRING_SIZE]>(DBF_STRING, 42, std::string("42"));
testPVD2DBR_scalar<pvd::pvUShort, pvd::uint16>(DBF_USHORT, 41u, 41);
testPVD2DBR_scalar<pvd::pvByte, pvd::int8>(DBF_CHAR, 41, 41);
+16 -16
View File
@@ -15,31 +15,31 @@ void testGet()
{
testDiag("==== testGet ====");
longinRecord *li1 = (longinRecord*)testdbRecordPtr("src:li1");
longinRecord *li1 = (longinRecord*)testdbRecordPtr("src:i1");
while(!dbIsLinkConnected(&li1->inp))
testqsrvWaitForLinkEvent(&li1->inp);
testdbGetFieldEqual("target:li.VAL", DBF_LONG, 42);
testdbGetFieldEqual("target:i.VAL", DBF_INT64, 42);
testdbGetFieldEqual("src:li1.VAL", DBF_LONG, 0); // value before first process
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 0); // value before first process
testdbGetFieldEqual("src:li1.INP", DBF_STRING, "{\"pva\":\"target:li\"}");
testdbGetFieldEqual("src:i1.INP", DBF_STRING, "{\"pva\":\"target:i\"}");
testdbPutFieldOk("src:li1.PROC", DBF_LONG, 1);
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1);
testdbGetFieldEqual("src:li1.VAL", DBF_LONG, 42);
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42);
testdbPutFieldOk("src:li1.INP", DBF_STRING, "{\"pva\":\"target:ai\"}");
testdbPutFieldOk("src:i1.INP", DBF_STRING, "{\"pva\":\"target:ai\"}");
while(!dbIsLinkConnected(&li1->inp))
testqsrvWaitForLinkEvent(&li1->inp);
testdbGetFieldEqual("src:li1.VAL", DBF_LONG, 42); // changing link doesn't automatically process
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 42); // changing link doesn't automatically process
testdbPutFieldOk("src:li1.PROC", DBF_LONG, 1);
testdbPutFieldOk("src:i1.PROC", DBF_INT64, 1);
testdbGetFieldEqual("src:li1.VAL", DBF_LONG, 4); // now it's changed
testdbGetFieldEqual("src:i1.VAL", DBF_INT64, 4); // now it's changed
}
void testPut()
@@ -51,14 +51,14 @@ void testPut()
while(!dbIsLinkConnected(&lo2->out))
testqsrvWaitForLinkEvent(&lo2->out);
testdbGetFieldEqual("target:li2.VAL", DBF_LONG, 43);
testdbGetFieldEqual("src:lo2.VAL", DBF_LONG, 0);
testdbGetFieldEqual("src:lo2.OUT", DBF_STRING, "{\"pva\":\"target:li2\"}");
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 43);
testdbGetFieldEqual("src:lo2.VAL", DBF_INT64, 0);
testdbGetFieldEqual("src:lo2.OUT", DBF_STRING, "{\"pva\":\"target:i2\"}");
testdbPutFieldOk("src:lo2.VAL", DBF_LONG, 14);
testdbPutFieldOk("src:lo2.VAL", DBF_INT64, 14);
testdbGetFieldEqual("target:li2.VAL", DBF_LONG, 14);
testdbGetFieldEqual("src:lo2.VAL", DBF_LONG, 14);
testdbGetFieldEqual("target:i2.VAL", DBF_INT64, 14);
testdbGetFieldEqual("src:lo2.VAL", DBF_INT64, 14);
}
} // namespace
+6 -6
View File
@@ -1,21 +1,21 @@
# used by testGet()
record(longin, "target:li") {
record(int64in, "target:i") {
field(VAL, "42")
}
record(ai, "target:ai") {
field(VAL, "4.0")
}
record(longin, "src:li1") {
field(INP, {pva:"target:li"})
record(int64in, "src:i1") {
field(INP, {pva:"target:i"})
}
# used by testPut()
record(longin, "target:li2") {
record(int64in, "target:i2") {
field(VAL, "43")
}
record(longout, "src:lo2") {
field(OUT, {pva:"target:li2"})
record(int64out, "src:lo2") {
field(OUT, {pva:"target:i2"})
}
+132 -12
View File
@@ -7,6 +7,14 @@
#include <aiRecord.h>
#include <mbbiRecord.h>
#include <stringinRecord.h>
#include <epicsVersion.h>
#ifdef EPICS_VERSION_INT
# if EPICS_VERSION_INT>=VERSION_INT(3,16,1,0)
# define USE_INT64
# include <int64inRecord.h>
# endif
#endif
#include "helper.h"
#include "pvif.h"
@@ -28,11 +36,17 @@ void testScalar()
testdbReadDatabase("p2pTestIoc.dbd", NULL, NULL);
p2pTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("testpvif.db", NULL, NULL);
#ifdef USE_INT64
testdbReadDatabase("testpvif64.db", NULL, NULL);
#endif
longinRecord *prec_li = (longinRecord*)testdbRecordPtr("test:li");
stringinRecord *prec_si = (stringinRecord*)testdbRecordPtr("test:si");
aiRecord *prec_ai = (aiRecord*)testdbRecordPtr("test:ai");
mbbiRecord *prec_mbbi = (mbbiRecord*)testdbRecordPtr("test:mbbi");
#ifdef USE_INT64
int64inRecord *prec_i64 = (int64inRecord*)testdbRecordPtr("test:i64");
#endif
IOC.init();
@@ -44,6 +58,9 @@ void testScalar()
DBCH chan_ai("test:ai");
DBCH chan_ai_rval("test:ai.RVAL");
DBCH chan_mbbi("test:mbbi");
#ifdef USE_INT64
DBCH chan_i64("test:i64");
#endif
testEqual(dbChannelFieldType(chan_li), DBR_LONG);
testEqual(dbChannelFieldType(chan_si), DBR_STRING);
testEqual(dbChannelFieldType(chan_ai), DBR_DOUBLE);
@@ -53,10 +70,16 @@ void testScalar()
testEqual(dbChannelFinalFieldType(chan_ai), DBR_DOUBLE);
testEqual(dbChannelFinalFieldType(chan_ai_rval), DBR_LONG);
testEqual(dbChannelFinalFieldType(chan_mbbi), DBR_ENUM);
#ifdef USE_INT64
testEqual(dbChannelFinalFieldType(chan_i64), DBR_INT64);
#endif
ScalarBuilder builder;
pvd::FieldConstPtr dtype_li(builder.dtype(chan_li));
#ifdef USE_INT64
pvd::FieldConstPtr dtype_i64(builder.dtype(chan_i64));
#endif
pvd::FieldConstPtr dtype_si(builder.dtype(chan_si));
pvd::FieldConstPtr dtype_ai(builder.dtype(chan_ai));
pvd::FieldConstPtr dtype_ai_rval(builder.dtype(chan_ai_rval));
@@ -64,6 +87,9 @@ void testScalar()
pvd::StructureConstPtr dtype_root(pvd::getFieldCreate()->createFieldBuilder()
->add("li", dtype_li)
#ifdef USE_INT64
->add("i64", dtype_i64)
#endif
->add("si", dtype_si)
->add("ai", dtype_ai)
->add("ai_rval", dtype_ai_rval)
@@ -73,11 +99,16 @@ void testScalar()
pvd::PVStructurePtr root(pvd::getPVDataCreate()->createPVStructure(dtype_root));
p2p::auto_ptr<PVIF> pvif_li(builder.attach(chan_li, root, FieldName("li")));
#ifdef USE_INT64
p2p::auto_ptr<PVIF> pvif_i64(builder.attach(chan_i64, root, FieldName("i64")));
#endif
p2p::auto_ptr<PVIF> pvif_si(builder.attach(chan_si, root, FieldName("si")));
p2p::auto_ptr<PVIF> pvif_ai(builder.attach(chan_ai, root, FieldName("ai")));
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);
@@ -94,21 +125,56 @@ 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();
#ifdef USE_INT64
dbScanLock((dbCommon*)prec_i64);
prec_i64->time.secPastEpoch = 0x12345678;
prec_i64->time.nsec = 12345678;
pvif_i64->put(mask, DBE_VALUE|DBE_ALARM|DBE_PROPERTY, NULL);
dbScanUnlock((dbCommon*)prec_i64);
#define OFF(NAME) (epicsUInt32)root->getSubFieldT(NAME)->getFieldOffset()
testEqual(mask, pvd::BitSet()
.set(OFF("i64.value"))
.set(OFF("i64.alarm.severity"))
.set(OFF("i64.alarm.status"))
.set(OFF("i64.alarm.message"))
.set(OFF("i64.timeStamp.secondsPastEpoch"))
.set(OFF("i64.timeStamp.nanoseconds"))
.set(OFF("i64.display.limitHigh"))
.set(OFF("i64.display.limitLow"))
.set(OFF("i64.display.description"))
.set(OFF("i64.display.units"))
.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"))
.set(OFF("i64.valueAlarm.lowWarningLimit"))
.set(OFF("i64.valueAlarm.highAlarmLimit"))
.set(OFF("i64.valueAlarm.lowAlarmLimit")))
<<" i64 changes\n"<<root->stream().show(mask);
#undef OFF
mask.clear();
#endif
dbScanLock((dbCommon*)prec_si);
prec_si->time.secPastEpoch = 0x12345678;
prec_si->time.nsec = 12345678;
@@ -127,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();
@@ -153,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"))
@@ -171,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();
@@ -198,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();
@@ -210,6 +282,22 @@ 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);
testFieldEqual<pvd::PVInt>(root, "i64.alarm.severity", 1);
testFieldEqual<pvd::PVInt>(root, "i64.alarm.status", 1);
testFieldEqual<pvd::PVLong>(root, "i64.timeStamp.secondsPastEpoch", 0x12345678+POSIX_TIME_AT_EPICS_EPOCH);
testFieldEqual<pvd::PVInt>(root, "i64.timeStamp.nanoseconds", 12345678);
testFieldEqual<pvd::PVDouble>(root, "i64.display.limitHigh", 100.0);
testFieldEqual<pvd::PVDouble>(root, "i64.display.limitLow", 10.0);
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");
testFieldEqual<pvd::PVInt>(root, "si.alarm.severity", 0);
@@ -223,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);
@@ -233,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);
@@ -250,6 +340,9 @@ void testScalar()
}
root->getSubFieldT<pvd::PVInt>("li.value")->put(102043);
#ifdef USE_INT64
root->getSubFieldT<pvd::PVLong>("i64.value")->put(-0x8000000000000000LL);
#endif
root->getSubFieldT<pvd::PVString>("si.value")->put("world");
root->getSubFieldT<pvd::PVDouble>("ai.value")->put(44.4);
root->getSubFieldT<pvd::PVInt>("ai_rval.value")->put(2143);
@@ -262,6 +355,24 @@ 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();
mask.set(root->getSubFieldT("i64.value")->getFieldOffset());
pvif_i64->get(mask);
testEqual(prec_i64->val, epicsInt64(-0x8000000000000000LL));
dbScanUnlock((dbCommon*)prec_i64);
#endif
dbScanLock((dbCommon*)prec_si);
mask.clear();
mask.set(root->getSubFieldT("si.value")->getFieldOffset());
@@ -414,7 +525,16 @@ void testPlain()
MAIN(testpvif)
{
testPlan(71);
testPlan(75
#ifdef USE_INT64
+13
#endif
);
#ifdef USE_INT64
testDiag("Testing of 64-bit field access");
#else
testDiag("64-bit field access not supported");
#endif
testScalar();
testPlain();
return testDone();
+1
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")
+8
View File
@@ -0,0 +1,8 @@
record(int64in, "test:i64") {
field(VAL, "0x7fffffffffffffff")
field(SEVR, "1")
field(STAT, "1")
field(EGU, "arb")
field(HOPR, "100")
field(LOPR, "10")
}