pvd <-> dbf

Yet more type conversions.

This is a complement to pvif.h in that
PVIF calls into dbPut() which this code is
called by dbPut().
This commit is contained in:
Michael Davidsaver
2018-04-08 17:48:05 -07:00
parent 8b8e421a65
commit cd2c1762b7
6 changed files with 516 additions and 2 deletions

View File

@ -15,6 +15,7 @@ qsrv_SRCS += pvif.cpp
qsrv_SRCS += qsrv.cpp
qsrv_SRCS += pdb.cpp
qsrv_SRCS += pdbsingle.cpp
qsrv_SRCS += dbf_copy.cpp
#qsrv_SRCS += pvalink.cpp
qsrv_SRCS += tpool.cpp
qsrv_SRCS += demo.cpp

228
pdbApp/dbf_copy.cpp Normal file
View File

@ -0,0 +1,228 @@
#include <epicsStdio.h>
#include <dbAccess.h>
#include <dbChannel.h>
#include <dbStaticLib.h>
#include <dbLock.h>
#include <dbEvent.h>
#include <epicsString.h>
#include <epicsVersion.h>
#include <pv/status.h>
#include <pv/bitSet.h>
#include <pv/pvData.h>
#include <pv/anyscalar.h>
#define epicsExportSharedSymbols
#include "pvif.h"
namespace pvd = epics::pvData;
long copyPVD2DBF(const pvd::PVField::const_shared_pointer& inraw,
void *outbuf, short outdbf, long *outnReq)
{
long nreq = outnReq ? *outnReq : 1;
if(!inraw || nreq <= 0 || INVALID_DB_REQ(outdbf)) return S_db_errArg;
pvd::ScalarType outpvd = DBR2PVD(outdbf);
pvd::PVField::const_shared_pointer in(inraw);
if(outdbf != DBF_STRING && in->getField()->getType() == pvd::structure) {
// assume NTEnum.
// index to string not requested, so attempt to treat .index as plain integer
in = static_cast<const pvd::PVStructure*>(in.get())->getSubField("index");
if(!in) return S_db_errArg;
}
if(in->getField()->getType() == pvd::structure) {
assert(outdbf == DBF_STRING);
char *outsbuf = (char*)outbuf;
// maybe NTEnum
// try index -> string
const pvd::PVStructure* sin = static_cast<const pvd::PVStructure*>(in.get());
pvd::PVScalar::const_shared_pointer index(sin->getSubField<pvd::PVScalar>("index"));
if(!index) return S_db_badField; // Not NTEnum, don't know how to handle...
// we will have an answer.
if(outnReq)
*outnReq = 1;
pvd::uint16 ival = index->getAs<pvd::uint16>();
pvd::PVStringArray::const_shared_pointer choices(sin->getSubField<pvd::PVStringArray>("choices"));
if(choices) {
pvd::PVStringArray::const_svector strs(choices->view());
if(ival < strs.size()) {
// found it!
const std::string& sval = strs[ival];
size_t slen = std::min(sval.size(), size_t(MAX_STRING_SIZE-1));
memcpy(outbuf, sval.c_str(), slen);
outsbuf[slen] = '\0';
return 0;
}
}
// didn't find it. either no choices or index is out of range
// print numeric index
epicsSnprintf(outsbuf, MAX_STRING_SIZE, "%u", ival);
return 0;
} else if(in->getField()->getType() == pvd::scalarArray) {
const pvd::PVScalarArray* sarr = static_cast<const pvd::PVScalarArray*>(in.get());
pvd::shared_vector<const void> arr;
sarr->getAs(arr);
size_t elemsize = pvd::ScalarTypeFunc::elementSize(arr.original_type());
arr.slice(0, nreq*elemsize);
nreq = arr.size()/elemsize;
if(outdbf == DBF_STRING) {
char *outsbuf = (char*)outbuf;
// allocate a temp buffer of string[], ick...
pvd::shared_vector<std::string> strs(nreq); // alloc
pvd::castUnsafeV(nreq, pvd::pvString, strs.data(), arr.original_type(), arr.data());
for(long i =0; i<nreq; i++, outsbuf += MAX_STRING_SIZE) {
size_t slen = std::min(strs[i].size(), size_t(MAX_STRING_SIZE-1));
memcpy(outsbuf, strs[i].c_str(), slen);
outsbuf[slen] = '\0';
}
} else {
pvd::castUnsafeV(nreq, outpvd, outbuf, arr.original_type(), arr.data());
}
if(outnReq)
*outnReq = nreq;
return 0;
} else if(in->getField()->getType() == pvd::scalar) {
char *outsbuf = (char*)outbuf;
const pvd::PVScalar* sval = static_cast<const pvd::PVScalar*>(in.get());
pvd::AnyScalar val;
sval->getAs(val);
if(outdbf == DBF_STRING && val.type()==pvd::pvString) {
// std::string to char*
size_t len = std::min(val.as<std::string>().size(), size_t(MAX_STRING_SIZE-1));
memcpy(outbuf, val.as<std::string>().c_str(), len);
outsbuf[len] = '\0';
} else if(outdbf == DBF_STRING) {
// non-string to char*
std::string temp;
pvd::castUnsafeV(1, pvd::pvString, &temp, val.type(), val.unsafe());
size_t len = std::min(temp.size(), size_t(MAX_STRING_SIZE-1));
memcpy(outbuf, temp.c_str(), len);
outsbuf[len] = '\0';
} else {
// non-string to any
pvd::castUnsafeV(1, outpvd, outbuf, val.type(), val.unsafe());
}
if(outnReq)
*outnReq = 1;
return 0;
} else {
// struct array or other strangeness which I don't know how to handle
return S_dbLib_badField;
}
}
long copyDBF2PVD(const pvd::shared_vector<const void> &inbuf,
const pvd::PVField::shared_pointer& outraw,
pvd::BitSet& changed,
const pvd::PVStringArray::const_svector &choices)
{
pvd::ScalarType inpvd = inbuf.original_type();
size_t incnt = inbuf.size()/pvd::ScalarTypeFunc::elementSize(inpvd);
if(!outraw) return S_db_errArg;
pvd::PVField::shared_pointer out(outraw);
if(inpvd != pvd::pvString && out->getField()->getType() == pvd::structure) {
// assume NTEnum.
// string to index not requested, so attempt to treat .index as plain integer
out = static_cast<pvd::PVStructure*>(out.get())->getSubField("index");
if(!out) return S_db_errArg;
}
if(out->getField()->getType() == pvd::structure) {
assert(inpvd == pvd::pvString);
if(incnt==0)
return S_db_errArg; // Need at least one string
const pvd::shared_vector<const std::string> insbuf(pvd::static_shared_vector_cast<const std::string>(inbuf));
const std::string& instr(insbuf[0]);
// assume NTEnum
// try string to index, then parse
pvd::PVStructure* sout = static_cast<pvd::PVStructure*>(out.get());
pvd::PVScalar::shared_pointer index(sout->getSubField<pvd::PVScalar>("index"));
if(!index) return S_db_badField; // Not NTEnum, don't know how to handle...
pvd::uint16 result = pvd::uint16(-1);
bool match = false;
for(size_t i=0, N=std::min(size_t(0xffff), choices.size()); i<N; i++) {
if(choices[i] == instr) {
match = true;
result = pvd::uint16(i);
}
}
if(!match) {
// no choice string matched, so try to parse as integer
try{
result = pvd::castUnsafe<pvd::uint16>(instr);
}catch(std::exception&){
return S_db_errArg;
}
}
index->putFrom(result);
out = index;
} else if(out->getField()->getType() == pvd::scalarArray) {
pvd::PVScalarArray* sarr = static_cast<pvd::PVScalarArray*>(out.get());
sarr->putFrom(inbuf);
} else if(out->getField()->getType() == pvd::scalar) {
pvd::PVScalar* sval = static_cast<pvd::PVScalar*>(out.get());
if(incnt==0) return S_db_errArg;
pvd::AnyScalar val(inpvd, inbuf.data());
sval->putFrom(val);
} else {
// struct array or other strangeness which I don't know how to handle
return S_db_badField;
}
changed.set(out->getFieldOffset());
return 0;
}

View File

@ -627,12 +627,11 @@ pvd::ScalarType DBR2PVD(short dbr)
{
switch(dbr) {
#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case DBR_##DBFTYPE: return pvd::pv##PVACODE;
#define CASE_ENUM
#define CASE_SKIP_BOOL
#include "pv/typemap.h"
#undef CASE_ENUM
#undef CASE_SKIP_BOOL
#undef CASE
case DBF_ENUM: return pvd::pvUShort;
case DBF_STRING: return pvd::pvString;
}
throw std::invalid_argument("Unsupported DBR code");

View File

@ -32,6 +32,17 @@
epics::pvData::ScalarType DBR2PVD(short dbr);
short PVD2DBR(epics::pvData::ScalarType pvt);
// copy from PVField (.value sub-field) to DBF buffer
epicsShareExtern
long copyPVD2DBF(const epics::pvData::PVField::const_shared_pointer& in,
void *outbuf, short outdbf, long *outnReq);
// copy from DBF buffer to PVField (.value sub-field)
epicsShareExtern
long copyDBF2PVD(const epics::pvData::shared_vector<const void>& buf,
const epics::pvData::PVField::shared_pointer& out,
epics::pvData::BitSet &changed,
const epics::pvData::PVStringArray::const_svector& choices);
union dbrbuf {
epicsInt8 dbf_CHAR;
epicsUInt8 dbf_UCHAR;

View File

@ -54,6 +54,12 @@ TESTPROD_HOST += testgroupconfig
testgroupconfig_SRCS += testgroupconfig
testgroupconfig_LIBS += qsrv pvAccess pvData
testgroupconfig_LIBS += $(EPICS_BASE_IOC_LIBS)
TESTPROD_HOST += testdbf_copy
testdbf_copy_SRCS += testdbf_copy
testdbf_copy_LIBS += qsrv pvAccess pvData
testdbf_copy_LIBS += $(EPICS_BASE_IOC_LIBS)
TESTS += testdbf_copy
endif
TESTSCRIPTS_HOST += $(TESTS:%=%.t)

269
testApp/testdbf_copy.cpp Normal file
View File

@ -0,0 +1,269 @@
#include <stdexcept>
#include <pv/pvUnitTest.h>
#include <testMain.h>
#include <dbStaticLib.h>
#include <epicsTypes.h>
#include <pv/valueBuilder.h>
#include <pv/pvData.h>
#include "pvif.h"
namespace pvd = epics::pvData;
namespace {
template<pvd::ScalarType ENUM, typename DBF, typename E>
void testPVD2DBR_scalar(unsigned dbf, typename pvd::meta::arg_type<typename pvd::ScalarTypeTraits<ENUM>::type>::type V, E expect)
{
testDiag("testPVD2DBR_scalar(%s, %s)", pvd::ScalarTypeFunc::name(ENUM), dbGetFieldTypeString(dbf));
pvd::PVStructure::shared_pointer top(pvd::ValueBuilder()
.add<ENUM>("value", V)
.buildPVStructure());
DBF buf;
copyPVD2DBF(top->getSubFieldT("value"), &buf, dbf, NULL);
testEqual(buf, expect);
}
void testPVD2DBR_enum()
{
testDiag("testPVD2DBR_enum()");
pvd::shared_vector<std::string> choices(3);
choices[0] = "zero";
choices[1] = "one";
choices[2] = "two";
pvd::PVStructure::shared_pointer top(pvd::ValueBuilder()
.addNested("value")
.add<pvd::pvInt>("index", 1)
.add("choices", pvd::static_shared_vector_cast<const void>(pvd::freeze(choices)))
.endNested()
.buildPVStructure());
{
epicsEnum16 ival;
copyPVD2DBF(top->getSubFieldT("value"), &ival, DBF_ENUM, NULL);
testEqual(ival, 1);
ival = 0;
testOk1(!!top->getSubField("value"));
copyPVD2DBF(top->getSubFieldT("value"), &ival, DBF_USHORT, NULL);
testEqual(ival, 1);
}
{
epicsUInt32 ival;
copyPVD2DBF(top->getSubFieldT("value"), &ival, DBF_LONG, NULL);
testEqual(ival, 1u);
}
char sval[MAX_STRING_SIZE];
copyPVD2DBF(top->getSubFieldT("value"), sval, DBF_STRING, NULL);
testEqual(std::string(sval) , "one");
}
void testPVD2DBR_array()
{
testDiag("testPVD2DBR_array()");
pvd::shared_vector<const pvd::uint32> arr;
{
pvd::shared_vector<pvd::uint32> iarr(3);
iarr[0] = 1; iarr[1] = 2; iarr[2] = 3;
arr = pvd::freeze(iarr);
}
pvd::PVStructure::shared_pointer top(pvd::ValueBuilder()
.add("value", pvd::static_shared_vector_cast<const void>(arr))
.buildPVStructure());
{
epicsUInt16 sarr[5];
{
long nreq = 5;
copyPVD2DBF(top->getSubFieldT("value"), sarr, DBF_SHORT, &nreq);
testEqual(nreq, 3);
}
testEqual(sarr[0], arr[0]);
testEqual(sarr[1], arr[1]);
testEqual(sarr[2], arr[2]);
}
{
char sarr[MAX_STRING_SIZE*5];
{
long nreq = 5;
copyPVD2DBF(top->getSubFieldT("value"), sarr, DBF_STRING, &nreq);
testEqual(nreq, 3);
}
testEqual(sarr[0*MAX_STRING_SIZE+0], '1');
testEqual(int(sarr[0*MAX_STRING_SIZE+1]), int('\0'));
testEqual(sarr[1*MAX_STRING_SIZE+0], '2');
testEqual(int(sarr[1*MAX_STRING_SIZE+1]), int('\0'));
testEqual(sarr[2*MAX_STRING_SIZE+0], '3');
testEqual(int(sarr[2*MAX_STRING_SIZE+1]), int('\0'));
}
}
template<pvd::ScalarType IN, pvd::ScalarType OUT>
void testDBR2PVD_scalar(typename pvd::meta::arg_type<typename pvd::ScalarTypeTraits<IN>::type>::type input,
typename pvd::meta::arg_type<typename pvd::ScalarTypeTraits<OUT>::type>::type expect)
{
typedef typename pvd::ScalarTypeTraits<IN>::type input_t;
typedef typename pvd::ScalarTypeTraits<OUT>::type output_t;
testDiag("testDBR2PVD_scalar(%s, %s)", pvd::ScalarTypeFunc::name(IN), pvd::ScalarTypeFunc::name(OUT));
pvd::PVStructure::shared_pointer top(pvd::getPVDataCreate()->createPVStructure(pvd::getFieldCreate()->createFieldBuilder()
->add("value", OUT) // initially zero or ""
->createStructure()));
pvd::PVStringArray::const_svector choices;
pvd::BitSet changed;
pvd::shared_vector<input_t> buf(1);
buf[0] = input;
copyDBF2PVD(pvd::static_shared_vector_cast<const void>(pvd::freeze(buf)),
top->getSubFieldT("value"), changed, choices);
output_t actual = top->getSubFieldT<pvd::PVScalar>("value")->getAs<output_t>();
testEqual(actual, expect);
testOk1(changed.get(top->getSubFieldT("value")->getFieldOffset()));
}
template<typename input_t>
void testDBR2PVD_enum(const input_t& input, pvd::int32 expect)
{
testDiag("testDBR2PVD_enum()");
pvd::shared_vector<const std::string> choices;
{
pvd::shared_vector<std::string> temp(3);
temp[0] = "zero";
temp[1] = "one";
temp[2] = "two";
choices = pvd::freeze(temp);
}
pvd::PVStructure::shared_pointer top(pvd::ValueBuilder()
.addNested("value")
.add<pvd::pvInt>("index", 0)
.add("choices", pvd::static_shared_vector_cast<const void>(choices))
.endNested()
.buildPVStructure());
pvd::BitSet changed;
pvd::shared_vector<input_t> buf(1);
buf[0] = input;
copyDBF2PVD(pvd::static_shared_vector_cast<const void>(pvd::freeze(buf)),
top->getSubFieldT("value"), changed, choices);
pvd::int32 actual = top->getSubFieldT<pvd::PVScalar>("value.index")->getAs<pvd::int32>();
testShow()<<top;
testShow()<<changed;
testEqual(actual, expect);
testOk1(changed.get(top->getSubFieldT("value.index")->getFieldOffset()));
}
void testDBR2PVD_array()
{
testDiag("testDBR2PVD_array()");
pvd::PVStructure::shared_pointer top(pvd::getPVDataCreate()->createPVStructure(pvd::getFieldCreate()->createFieldBuilder()
->addArray("value", pvd::pvInt) // initially zero or ""
->createStructure()));
pvd::PVStringArray::const_svector choices;
pvd::BitSet changed;
{
pvd::shared_vector<pvd::uint32> buf(3);
buf[0] = 1; buf[1] = 2; buf[2] = 3;
copyDBF2PVD(pvd::static_shared_vector_cast<const void>(pvd::freeze(buf)),
top->getSubFieldT("value"), changed, choices);
pvd::PVIntArray::const_svector arr(top->getSubFieldT<pvd::PVIntArray>("value")->view());
testEqual(arr.size(), 3u);
testEqual(arr[0], 1);
testEqual(arr[1], 2);
testEqual(arr[2], 3);
testOk1(changed.get(top->getSubFieldT("value")->getFieldOffset()));
}
changed.clear();
{
pvd::shared_vector<std::string> buf(4);
buf[0] = "4";
buf[1] = "5";
buf[2] = "6";
buf[3] = "7";
copyDBF2PVD(pvd::static_shared_vector_cast<const void>(pvd::freeze(buf)),
top->getSubFieldT("value"), changed, choices);
pvd::PVIntArray::const_svector arr(top->getSubFieldT<pvd::PVIntArray>("value")->view());
testEqual(arr.size(), 4u);
testEqual(arr[0], 4);
testEqual(arr[1], 5);
testEqual(arr[2], 6);
testEqual(arr[3], 7);
testOk1(changed.get(top->getSubFieldT("value")->getFieldOffset()));
}
}
}
MAIN(testdbf_copy)
{
testPlan(51);
try{
testPVD2DBR_scalar<pvd::pvDouble, double>(DBF_DOUBLE, 42.2, 42.2);
testPVD2DBR_scalar<pvd::pvDouble, pvd::uint16>(DBF_USHORT, 42.2, 42u);
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::pvUShort, pvd::uint16>(DBF_USHORT, 41u, 41);
testPVD2DBR_scalar<pvd::pvByte, pvd::int8>(DBF_CHAR, 41, 41);
testPVD2DBR_scalar<pvd::pvString, char[MAX_STRING_SIZE]>(DBF_STRING, "hello", std::string("hello"));
testPVD2DBR_scalar<pvd::pvString, pvd::int32>(DBF_LONG, "-100", -100);
//testPVD2DBR_scalar<pvd::pvBoolean, pvd::int8>(DBF_CHAR, true, 1);
testPVD2DBR_enum();
testPVD2DBR_array();
testDBR2PVD_scalar<pvd::pvDouble, pvd::pvDouble>(42.2, 42.2);
testDBR2PVD_scalar<pvd::pvUShort, pvd::pvDouble>(42u, 42.0);
testDBR2PVD_scalar<pvd::pvInt, pvd::pvInt>(-41, -41);
testDBR2PVD_scalar<pvd::pvString, pvd::pvInt>("-41", -41);
testDBR2PVD_scalar<pvd::pvString, pvd::pvString>("hello", "hello");
testDBR2PVD_scalar<pvd::pvInt, pvd::pvString>(-42, "-42");
testDBR2PVD_enum<pvd::uint32>(2, 2);
testDBR2PVD_enum<std::string>("two", 2);
testDBR2PVD_array();
}catch(std::exception& e){
testFail("Unexpected exception: %s", e.what());
}
return testDone();
}