add pvRequest -> bitmask processing

This commit is contained in:
Michael Davidsaver
2018-07-14 13:12:01 -07:00
parent f66d277918
commit c67fdafb43
4 changed files with 206 additions and 1 deletions

View File

@@ -6,4 +6,5 @@ INC += pv/createRequest.h
INC += pv/pvCopy.h
LIBSRCS += createRequest.cpp
LIBSRCS += requestmask.cpp
LIBSRCS += pvCopy.cpp

View File

@@ -16,6 +16,8 @@
namespace epics { namespace pvData {
class BitSet;
/**
* @brief Create pvRequest structure for Channel methods.
*
@@ -61,6 +63,25 @@ protected:
epicsShareExtern
PVStructure::shared_pointer createRequest(std::string const & request);
/** Extract a bit mask of fields from a field selection mask.
*
@param type The Structure to which the mask will be applied
@param pvRequestMask The 'field' sub-structure of a pvRequest. May be NULL
@param expand If true, expand any "compressed" sub-structure bits
@returns A bit mask, where the bits are field offset in 'type'.
*
@code
PVStructure::const_shared_pointer value(...), // some Structure with .value
pvRequest(createRequest("field(value)"));
BitSet fieldMask(extractRequestMask(value, pvRequest->getSubField<PVStructure>("field"));
assert(fieldMask == BitSet().set(value->getSubFieldT("value")->getFieldOffset());
@endcode
*/
epicsShareExtern
BitSet extractRequestMask(const PVStructure::const_shared_pointer& type,
const PVStructure::const_shared_pointer& pvRequestMask,
bool expand = true);
}}
#endif /* CREATEREQUEST_H */

85
src/copy/requestmask.cpp Normal file
View File

@@ -0,0 +1,85 @@
#include <epicsTypes.h>
#include <epicsVersion.h>
#include <epicsConvert.h>
#include <epicsAssert.h>
#include <epicsMutex.h>
#include <epicsGuard.h>
#define epicsExportSharedSymbols
#include <pv/createRequest.h>
#include <pv/bitSet.h>
namespace epics{namespace pvData {
static
void setStruct(BitSet& ret, const PVStructure& S)
{
for(size_t i=S.getFieldOffset(), L=S.getNextFieldOffset(); i<L; i++) {
ret.set(i);
}
}
static
void _buildMask(BitSet& ret, const PVStructure* type, const Structure* pvRequestMask, bool expand, unsigned depth)
{
const StringArray& reqNames = pvRequestMask->getFieldNames();
if(reqNames.empty()) {
// empty sub-structure selects all members
ret.set(type->getFieldOffset());
if(expand) setStruct(ret, *type);
} else {
// iterate through request fields
for(size_t i=0, N=reqNames.size(); i<N; i++)
{
// does the requested field actually exist in the target Structure?
PVField::const_shared_pointer subtype(type->getSubField(reqNames[i]));
const FieldConstPtr& subReq = pvRequestMask->getFields()[i];
if(!subtype || subReq->getType()!=structure)
continue; // TODO: warn/error on invalid selection?
if(subtype->getField()->getType()==structure && depth<5) {
// requested field is a Structure, recurse if below arbitrary limit
_buildMask(ret,
static_cast<const PVStructure*>(subtype.get()),
static_cast<const Structure*>(subReq.get()),
expand,
depth+1);
} else {
// requested field so not a structure, or at recursion limit, so just select it and move on
ret.set(subtype->getFieldOffset());
if(expand && subtype->getField()->getType()==structure) setStruct(ret, static_cast<const PVStructure&>(*subtype));
// TODO: error if subReq has sub-structure?
}
}
}
}
BitSet extractRequestMask(const PVStructure::const_shared_pointer& type,
const PVStructure::const_shared_pointer& pvRequestMask,
bool expand)
{
BitSet ret;
if(!type)
throw std::invalid_argument("NULL type not allowed");
if(!pvRequestMask) {
// we treat no sub-struct as wildcard. (totally empty selection is useless)
ret.set(0);
if(expand) setStruct(ret, *type);
} else {
_buildMask(ret, type.get(), pvRequestMask->getStructure().get(), expand, 0);
}
return ret;
}
}} //namespace epics::pvData

View File

@@ -15,7 +15,9 @@
#include <pv/pvUnitTest.h>
#include <testMain.h>
#include <pv/current_function.h>
#include <pv/createRequest.h>
#include <pv/bitSet.h>
namespace {
@@ -335,15 +337,111 @@ static void testBadRequest()
testOk1(!!C->createRequest("field(value)"));
testOk1(C->getMessage().empty());
// duplicate fieldName C
// correct is: "field(A,C{D,E.F})"
testThrows(std::invalid_argument, createRequest("field(A,C.D,C.E.F)"));
}
static
StructureConstPtr maskingType = getFieldCreate()->createFieldBuilder()
->add("A", pvInt)
->add("B", pvInt)
->addNestedStructure("C")
->add("D", pvInt)
->addNestedStructure("E")
->add("F", pvInt)
->endNested()
->endNested()
->createStructure();
static
void checkMask(bool expand,
const std::string& request,
const BitSet& expected)
{
PVStructurePtr pvRequest(createRequest(request));
PVStructurePtr value(getPVDataCreate()->createPVStructure(maskingType));
BitSet actual(extractRequestMask(value, pvRequest->getSubField<PVStructure>("field"), expand));
testEqual(actual, expected)<<" request=\""<<request<<"\"";
}
static void testMask()
{
testDiag("===== %s =====", CURRENT_FUNCTION);
PVStructurePtr V(getPVDataCreate()->createPVStructure(maskingType));
testShow()<<V;
checkMask(false, "", BitSet().set(0));
checkMask(true, "", BitSet().set(0)
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("B")->getFieldOffset())
.set(V->getSubField("C")->getFieldOffset())
.set(V->getSubField("C.D")->getFieldOffset())
.set(V->getSubField("C.E")->getFieldOffset())
.set(V->getSubField("C.E.F")->getFieldOffset()));
checkMask(false, "field()", BitSet().set(0));
checkMask(true, "field()", BitSet().set(0)
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("B")->getFieldOffset())
.set(V->getSubField("C")->getFieldOffset())
.set(V->getSubField("C.D")->getFieldOffset())
.set(V->getSubField("C.E")->getFieldOffset())
.set(V->getSubField("C.E.F")->getFieldOffset()));
checkMask(false, "field(A)", BitSet()
.set(V->getSubField("A")->getFieldOffset()));
checkMask(true, "field(A)", BitSet()
.set(V->getSubField("A")->getFieldOffset()));
checkMask(false, "field(A,B)", BitSet()
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("B")->getFieldOffset()));
checkMask(false, "field(A,C)", BitSet()
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("C")->getFieldOffset()));
checkMask(true, "field(A,C)", BitSet()
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("C")->getFieldOffset())
.set(V->getSubField("C.D")->getFieldOffset())
.set(V->getSubField("C.E")->getFieldOffset())
.set(V->getSubField("C.E.F")->getFieldOffset()));
checkMask(false, "field(C.D)", BitSet()
.set(V->getSubField("C.D")->getFieldOffset()));
checkMask(true, "field(C.D)", BitSet()
.set(V->getSubField("C.D")->getFieldOffset()));
checkMask(false, "field(A,C{D,E.F})", BitSet()
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("C.D")->getFieldOffset())
.set(V->getSubField("C.E.F")->getFieldOffset()));
checkMask(true, "field(A,C{D,E.F})", BitSet()
.set(V->getSubField("A")->getFieldOffset())
.set(V->getSubField("C.D")->getFieldOffset())
.set(V->getSubField("C.E.F")->getFieldOffset()));
// request for non-existant field is silently ignored
checkMask(false, "field(A,foo)", BitSet()
.set(V->getSubField("A")->getFieldOffset()));
}
} // namespace
MAIN(testCreateRequest)
{
testPlan(126);
testPlan(141);
testCreateRequestInternal();
testBadRequest();
testMask();
return testDone();
}