From c67fdafb436f9dabebb12bccd40f1b13b9b967b2 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 14 Jul 2018 13:12:01 -0700 Subject: [PATCH] add pvRequest -> bitmask processing --- src/copy/Makefile | 1 + src/copy/pv/createRequest.h | 21 ++++++ src/copy/requestmask.cpp | 85 ++++++++++++++++++++++++ testApp/copy/testCreateRequest.cpp | 100 ++++++++++++++++++++++++++++- 4 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 src/copy/requestmask.cpp diff --git a/src/copy/Makefile b/src/copy/Makefile index cfd73f6..4bb65a0 100644 --- a/src/copy/Makefile +++ b/src/copy/Makefile @@ -6,4 +6,5 @@ INC += pv/createRequest.h INC += pv/pvCopy.h LIBSRCS += createRequest.cpp +LIBSRCS += requestmask.cpp LIBSRCS += pvCopy.cpp diff --git a/src/copy/pv/createRequest.h b/src/copy/pv/createRequest.h index 0d47f53..f3b9aff 100644 --- a/src/copy/pv/createRequest.h +++ b/src/copy/pv/createRequest.h @@ -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("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 */ diff --git a/src/copy/requestmask.cpp b/src/copy/requestmask.cpp new file mode 100644 index 0000000..1ad3176 --- /dev/null +++ b/src/copy/requestmask.cpp @@ -0,0 +1,85 @@ + +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include +#include + +namespace epics{namespace pvData { + +static +void setStruct(BitSet& ret, const PVStructure& S) +{ + for(size_t i=S.getFieldOffset(), L=S.getNextFieldOffset(); igetFieldNames(); + + 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(); igetSubField(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(subtype.get()), + static_cast(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(*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 diff --git a/testApp/copy/testCreateRequest.cpp b/testApp/copy/testCreateRequest.cpp index c86dd5e..eaf8ec4 100644 --- a/testApp/copy/testCreateRequest.cpp +++ b/testApp/copy/testCreateRequest.cpp @@ -15,7 +15,9 @@ #include #include +#include #include +#include 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("field"), expand)); + + testEqual(actual, expected)<<" request=\""<createPVStructure(maskingType)); + testShow()<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(); }