add pvRequest -> bitmask processing
This commit is contained in:
@@ -6,4 +6,5 @@ INC += pv/createRequest.h
|
||||
INC += pv/pvCopy.h
|
||||
|
||||
LIBSRCS += createRequest.cpp
|
||||
LIBSRCS += requestmask.cpp
|
||||
LIBSRCS += pvCopy.cpp
|
||||
|
||||
@@ -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
85
src/copy/requestmask.cpp
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user