Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 521154fd52 | |||
| 6d1cbd87fd | |||
| 68904852b9 | |||
| a3dc712174 | |||
| 1ce449d40d | |||
| 67b384bb6c | |||
| f3d7d7cd8f | |||
| 7bc5cbf957 | |||
| 0ab4f937a7 | |||
| 3c5a5c805c | |||
| c79074e7fa |
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
Reference in New Issue
Block a user