Begin work on general NT validator
This commit is contained in:

committed by
mdavidsaver

parent
2d186d40d5
commit
b76c91a885
@ -21,6 +21,7 @@ INC += pv/ntcontinuum.h
|
||||
INC += pv/nthistogram.h
|
||||
INC += pv/nturi.h
|
||||
INC += pv/ntndarrayAttribute.h
|
||||
INC += pv/ntvalidator.h
|
||||
|
||||
LIBSRCS += ntutils.cpp
|
||||
LIBSRCS += ntid.cpp
|
||||
@ -41,6 +42,7 @@ LIBSRCS += ntcontinuum.cpp
|
||||
LIBSRCS += nthistogram.cpp
|
||||
LIBSRCS += nturi.cpp
|
||||
LIBSRCS += ntndarrayAttribute.cpp
|
||||
LIBSRCS += ntvalidator.cpp
|
||||
|
||||
LIBRARY = nt
|
||||
|
||||
|
@ -6,135 +6,91 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <epicsThread.h>
|
||||
|
||||
#include <pv/lock.h>
|
||||
#include <pv/sharedPtr.h>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/ntndarray.h>
|
||||
#include <pv/ntndarrayAttribute.h>
|
||||
#include <pv/ntutils.h>
|
||||
#include <pv/ntvalidator.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace epics::pvData;
|
||||
|
||||
namespace epics { namespace nt {
|
||||
|
||||
static NTFieldPtr ntField = NTField::get();
|
||||
|
||||
|
||||
namespace detail {
|
||||
|
||||
static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
static StructureConstPtr *base_s;
|
||||
static epicsThreadOnceId base_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static Mutex mutex;
|
||||
void NTNDArrayBuilder::once(void *)
|
||||
{
|
||||
FieldBuilderPtr fb(FieldBuilder::begin());
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
|
||||
for (int i = pvBoolean; i < pvString; ++i) {
|
||||
ScalarType st = static_cast<ScalarType>(i);
|
||||
fb->addArray(std::string(ScalarTypeFunc::name(st)) + "Value", st);
|
||||
}
|
||||
|
||||
UnionConstPtr value(fb->createUnion());
|
||||
|
||||
StructureConstPtr codec(fb->setId("codec_t")->
|
||||
add("name", pvString)->
|
||||
add("parameters", getFieldCreate()->createVariantUnion())->
|
||||
createStructure());
|
||||
|
||||
StructureConstPtr dimension(fb->setId("dimension_t")->
|
||||
add("size", pvInt)->
|
||||
add("offset", pvInt)->
|
||||
add("fullSize", pvInt)->
|
||||
add("binning", pvInt)->
|
||||
add("reverse", pvBoolean)->
|
||||
createStructure());
|
||||
|
||||
StructureConstPtr attribute(NTNDArrayAttribute::createBuilder()->createStructure());
|
||||
|
||||
base_s = new StructureConstPtr;
|
||||
*base_s = fb->setId(NTNDArray::URI)->
|
||||
add("value", value)->
|
||||
add("codec", codec)->
|
||||
add("compressedSize", pvLong)->
|
||||
add("uncompressedSize", pvLong)->
|
||||
addArray("dimension", dimension)->
|
||||
add("uniqueId", pvInt)->
|
||||
add("dataTimeStamp", standardField->timeStamp())->
|
||||
addArray("attribute", attribute)->
|
||||
createStructure();
|
||||
}
|
||||
|
||||
StructureConstPtr NTNDArrayBuilder::createStructure()
|
||||
{
|
||||
enum
|
||||
{
|
||||
DISCRIPTOR_INDEX,
|
||||
TIMESTAMP_INDEX,
|
||||
ALARM_INDEX,
|
||||
DISPLAY_INDEX
|
||||
};
|
||||
epicsThreadOnce(&base_once, &NTNDArrayBuilder::once, 0);
|
||||
|
||||
const size_t NUMBER_OF_INDICES = DISPLAY_INDEX+1;
|
||||
const size_t NUMBER_OF_STRUCTURES = 1 << NUMBER_OF_INDICES;
|
||||
StandardFieldPtr standardField(getStandardField());
|
||||
FieldBuilderPtr fb(FieldBuilder::begin(*base_s));
|
||||
|
||||
Lock xx(mutex);
|
||||
if (descriptor)
|
||||
fb->add("descriptor", pvString);
|
||||
|
||||
static StructureConstPtr ntndarrayStruc[NUMBER_OF_STRUCTURES];
|
||||
static UnionConstPtr valueType;
|
||||
static StructureConstPtr codecStruc;
|
||||
static StructureConstPtr dimensionStruc;
|
||||
static StructureConstPtr attributeStruc;
|
||||
if (alarm)
|
||||
fb->add("alarm", standardField->alarm());
|
||||
|
||||
StructureConstPtr returnedStruc;
|
||||
if (timeStamp)
|
||||
fb->add("timeStamp", standardField->timeStamp());
|
||||
|
||||
size_t index = 0;
|
||||
if (descriptor) index |= 1 << DISCRIPTOR_INDEX;
|
||||
if (timeStamp) index |= 1 << TIMESTAMP_INDEX;
|
||||
if (alarm) index |= 1 << ALARM_INDEX;
|
||||
if (display) index |= 1 << DISPLAY_INDEX;
|
||||
if (display)
|
||||
fb->add("display", standardField->display());
|
||||
|
||||
bool isExtended = !extraFieldNames.empty();
|
||||
size_t extraCount = extraFieldNames.size();
|
||||
for (size_t i = 0; i< extraCount; i++)
|
||||
fb->add(extraFieldNames[i], extraFields[i]);
|
||||
|
||||
if (isExtended || !ntndarrayStruc[index])
|
||||
{
|
||||
StandardFieldPtr standardField = getStandardField();
|
||||
FieldBuilderPtr fb = fieldCreate->createFieldBuilder();
|
||||
|
||||
if (!valueType)
|
||||
{
|
||||
for (int i = pvBoolean; i < pvString; ++i)
|
||||
{
|
||||
ScalarType st = static_cast<ScalarType>(i);
|
||||
fb->addArray(std::string(ScalarTypeFunc::name(st)) + "Value", st);
|
||||
}
|
||||
valueType = fb->createUnion();
|
||||
}
|
||||
|
||||
if (!codecStruc)
|
||||
{
|
||||
codecStruc = fb->setId("codec_t")->
|
||||
add("name", pvString)->
|
||||
add("parameters", fieldCreate->createVariantUnion())->
|
||||
createStructure();
|
||||
}
|
||||
|
||||
if (!dimensionStruc)
|
||||
{
|
||||
dimensionStruc = fb->setId("dimension_t")->
|
||||
add("size", pvInt)->
|
||||
add("offset", pvInt)->
|
||||
add("fullSize", pvInt)->
|
||||
add("binning", pvInt)->
|
||||
add("reverse", pvBoolean)->
|
||||
createStructure();
|
||||
}
|
||||
|
||||
if (!attributeStruc)
|
||||
{
|
||||
attributeStruc = NTNDArrayAttribute::createBuilder()->createStructure();
|
||||
}
|
||||
|
||||
fb->setId(NTNDArray::URI)->
|
||||
add("value", valueType)->
|
||||
add("codec", codecStruc)->
|
||||
add("compressedSize", pvLong)->
|
||||
add("uncompressedSize", pvLong)->
|
||||
addArray("dimension", dimensionStruc)->
|
||||
add("uniqueId", pvInt)->
|
||||
add("dataTimeStamp", standardField->timeStamp())->
|
||||
addArray("attribute", attributeStruc);
|
||||
|
||||
if (descriptor)
|
||||
fb->add("descriptor", pvString);
|
||||
|
||||
if (alarm)
|
||||
fb->add("alarm", standardField->alarm());
|
||||
|
||||
if (timeStamp)
|
||||
fb->add("timeStamp", standardField->timeStamp());
|
||||
|
||||
if (display)
|
||||
fb->add("display", standardField->display());
|
||||
|
||||
size_t extraCount = extraFieldNames.size();
|
||||
for (size_t i = 0; i< extraCount; i++)
|
||||
fb->add(extraFieldNames[i], extraFields[i]);
|
||||
|
||||
returnedStruc = fb->createStructure();
|
||||
|
||||
if (!isExtended)
|
||||
ntndarrayStruc[index] = returnedStruc;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ntndarrayStruc[index];
|
||||
}
|
||||
|
||||
return returnedStruc;
|
||||
return fb->createStructure();
|
||||
}
|
||||
|
||||
NTNDArrayBuilder::shared_pointer NTNDArrayBuilder::addDescriptor()
|
||||
@ -192,95 +148,11 @@ NTNDArrayBuilder::shared_pointer NTNDArrayBuilder::add(string const & name, Fiel
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
const std::string NTNDArray::URI("epics:nt/NTNDArray:1.0");
|
||||
const std::string ntAttrStr("epics:nt/NTAttribute:1.0");
|
||||
|
||||
static FieldCreatePtr fieldCreate = getFieldCreate();
|
||||
static PVDataCreatePtr pvDataCreate = getPVDataCreate();
|
||||
|
||||
class NTValueType
|
||||
{
|
||||
public:
|
||||
static bool isCompatible(UnionConstPtr const &u)
|
||||
{
|
||||
if(!u.get()) return false;
|
||||
|
||||
if (u->getID() != Union::defaultId()) return false;
|
||||
if (u->isVariant()) return false;
|
||||
|
||||
for (int i = pvBoolean; i != pvString; ++i)
|
||||
{
|
||||
ScalarType scalarType = static_cast<ScalarType>(i);
|
||||
std::string name(ScalarTypeFunc::name(scalarType));
|
||||
name += "Value";
|
||||
ScalarArrayConstPtr scalarField = u->getField<ScalarArray>(name);
|
||||
if (scalarField.get() == 0 ||
|
||||
scalarField->getElementType() != scalarType)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class NTCodec
|
||||
{
|
||||
public:
|
||||
static bool isCompatible(StructureConstPtr const &structure)
|
||||
{
|
||||
if(!structure.get()) return false;
|
||||
|
||||
if (structure->getID() != "codec_t") return false;
|
||||
|
||||
ScalarConstPtr scalarField = structure->getField<Scalar>("name");
|
||||
if (scalarField.get() == 0 || scalarField->getScalarType() != pvString)
|
||||
return false;
|
||||
|
||||
UnionConstPtr paramField = structure->getField<Union>("parameters");
|
||||
if (paramField.get() == 0 || !paramField->isVariant())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class NTDimension
|
||||
{
|
||||
public:
|
||||
static bool isCompatible(StructureConstPtr const &structure)
|
||||
{
|
||||
if(!structure.get()) return false;
|
||||
|
||||
if (structure->getID() != "dimension_t") return false;
|
||||
|
||||
ScalarConstPtr scalarField = structure->getField<Scalar>("size");
|
||||
if (scalarField.get() == 0 || scalarField->getScalarType() != pvInt)
|
||||
return false;
|
||||
|
||||
scalarField = structure->getField<Scalar>("offset");
|
||||
if (scalarField.get() == 0 || scalarField->getScalarType() != pvInt)
|
||||
return false;
|
||||
|
||||
scalarField = structure->getField<Scalar>("fullSize");
|
||||
if (scalarField.get() == 0 || scalarField->getScalarType() != pvInt)
|
||||
return false;
|
||||
|
||||
scalarField = structure->getField<Scalar>("binning");
|
||||
if (scalarField.get() == 0 || scalarField->getScalarType() != pvInt)
|
||||
return false;
|
||||
|
||||
scalarField = structure->getField<Scalar>("reverse");
|
||||
if (scalarField.get() == 0 || scalarField->getScalarType() != pvBoolean)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
NTNDArray::shared_pointer NTNDArray::wrap(PVStructurePtr const & pvStructure)
|
||||
{
|
||||
if(!isCompatible(pvStructure)) return shared_pointer();
|
||||
@ -302,84 +174,47 @@ bool NTNDArray::is_a(PVStructurePtr const & pvStructure)
|
||||
return is_a(pvStructure->getStructure());
|
||||
}
|
||||
|
||||
static Validator::Definition* definition;
|
||||
static epicsThreadOnceId definition_once = EPICS_THREAD_ONCE_INIT;
|
||||
|
||||
static void definition_init(void *)
|
||||
{
|
||||
StructureConstPtr structure(
|
||||
NTNDArray::createBuilder()->
|
||||
addDescriptor()->
|
||||
addAlarm()->
|
||||
addDisplay()->
|
||||
addTimeStamp()->
|
||||
createStructure());
|
||||
|
||||
definition = new Validator::Definition;
|
||||
definition->structure = std::static_pointer_cast<const Field>(structure);
|
||||
|
||||
// TODO: these should be all getFieldT
|
||||
definition->optional.insert(structure->getField("descriptor").get());
|
||||
definition->optional.insert(structure->getField("alarm").get());
|
||||
definition->optional.insert(structure->getField("timeStamp").get());
|
||||
definition->optional.insert(structure->getField("display").get());
|
||||
|
||||
// TODO: the following should be a helper in NTNDArrayAttribute
|
||||
StructureConstPtr attribute(
|
||||
std::static_pointer_cast<const StructureArray>(
|
||||
structure->getField("attribute")
|
||||
)->getStructure()
|
||||
);
|
||||
|
||||
definition->optional.insert(attribute->getField("tags").get());
|
||||
definition->optional.insert(attribute->getField("alarm").get());
|
||||
definition->optional.insert(attribute->getField("timeStamp").get());
|
||||
}
|
||||
|
||||
bool NTNDArray::isCompatible(StructureConstPtr const &structure)
|
||||
{
|
||||
if(!structure.get()) return false;
|
||||
|
||||
UnionConstPtr valueField = structure->getField<Union>("value");
|
||||
if(!NTValueType::isCompatible(valueField)) return false;
|
||||
epicsThreadOnce(&definition_once, &definition_init, 0);
|
||||
|
||||
StructureConstPtr codecField = structure->getField<Structure>("codec");
|
||||
if(!NTCodec::isCompatible(codecField)) return false;
|
||||
|
||||
ScalarConstPtr compressedSizeField = structure->getField<Scalar>("compressedSize");
|
||||
if (compressedSizeField.get() == 0)
|
||||
return false;
|
||||
|
||||
if (compressedSizeField->getScalarType() != pvLong)
|
||||
return false;
|
||||
|
||||
|
||||
ScalarConstPtr uncompressedSizeField = structure->getField<Scalar>("uncompressedSize");
|
||||
if (uncompressedSizeField.get() == 0)
|
||||
return false;
|
||||
|
||||
if (uncompressedSizeField->getScalarType() != pvLong)
|
||||
return false;
|
||||
|
||||
StructureArrayConstPtr dimensionField = structure->getField<StructureArray>("dimension");
|
||||
if (dimensionField.get() == 0)
|
||||
return false;
|
||||
StructureConstPtr dimElementStruc = dimensionField->getStructure();
|
||||
|
||||
if(!NTDimension::isCompatible(dimElementStruc))
|
||||
return false;
|
||||
|
||||
NTFieldPtr ntField = NTField::get();
|
||||
|
||||
StructureConstPtr dataTimeStampField = structure->getField<Structure>(
|
||||
"dataTimeStamp");
|
||||
if (dataTimeStampField.get() == 0 || !ntField->isTimeStamp(dataTimeStampField))
|
||||
return false;
|
||||
|
||||
|
||||
ScalarConstPtr uniqueIdField = structure->getField<Scalar>("uniqueId");
|
||||
if (uniqueIdField.get() == 0)
|
||||
return false;
|
||||
|
||||
if (uniqueIdField->getScalarType() != pvInt)
|
||||
return false;
|
||||
|
||||
|
||||
StructureArrayConstPtr attributeField = structure->getField<StructureArray>( "attribute");
|
||||
if (!attributeField)
|
||||
return false;
|
||||
|
||||
if (!NTNDArrayAttribute::isCompatible(attributeField->getStructure()))
|
||||
return false;
|
||||
|
||||
|
||||
FieldConstPtr field = structure->getField("descriptor");
|
||||
if (field.get())
|
||||
{
|
||||
ScalarConstPtr descriptorField = structure->getField<Scalar>("descriptor");
|
||||
if (!descriptorField || descriptorField->getScalarType() != pvString)
|
||||
return false;
|
||||
}
|
||||
|
||||
field = structure->getField("alarm");
|
||||
if (field.get() && !ntField->isAlarm(field))
|
||||
return false;
|
||||
|
||||
field = structure->getField("timeStamp");
|
||||
if (field.get() && !ntField->isTimeStamp(field))
|
||||
return false;
|
||||
|
||||
field = structure->getField("display");
|
||||
if (field.get() && !ntField->isDisplay(field))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return Validator::isCompatible(*definition, *structure);
|
||||
}
|
||||
|
||||
|
||||
@ -390,7 +225,6 @@ bool NTNDArray::isCompatible(PVStructurePtr const & pvStructure)
|
||||
return isCompatible(pvStructure->getStructure());
|
||||
}
|
||||
|
||||
|
||||
bool NTNDArray::isValid()
|
||||
{
|
||||
int64 valueSize = getValueSize();
|
||||
|
231
src/ntvalidator.cpp
Normal file
231
src/ntvalidator.cpp
Normal file
@ -0,0 +1,231 @@
|
||||
/* ntvalidator.cpp */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include <pv/ntvalidator.h>
|
||||
#include <pv/bitSet.h>
|
||||
|
||||
namespace epics { namespace nt {
|
||||
|
||||
using epics::pvData::Type;
|
||||
using epics::pvData::Field;
|
||||
using epics::pvData::Union;
|
||||
using epics::pvData::UnionArray;
|
||||
using epics::pvData::Structure;
|
||||
using epics::pvData::StructureConstPtr;
|
||||
using epics::pvData::StructureArray;
|
||||
using epics::pvData::StringArray;
|
||||
using epics::pvData::FieldConstPtr;
|
||||
using epics::pvData::FieldConstPtrArray;
|
||||
using epics::pvData::BitSet;
|
||||
|
||||
using std::string;
|
||||
|
||||
struct Helper
|
||||
{
|
||||
Validator::Definition const & definition;
|
||||
std::deque<string> path;
|
||||
Validator::Result result;
|
||||
|
||||
Helper(Validator::Definition const & definition) : definition(definition) {}
|
||||
|
||||
bool isOptional(Field const & field) const
|
||||
{
|
||||
return definition.optional.find(&field) != definition.optional.end();
|
||||
}
|
||||
|
||||
string getCurrentPath(void) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
// TODO: is there a better string join?
|
||||
std::deque<string>::const_iterator it;
|
||||
for (it = path.cbegin(); it != path.cend(); ++it) {
|
||||
if (it != path.cbegin())
|
||||
os << ".";
|
||||
os << *it;
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
void appendError(Validator::ErrorType type)
|
||||
{
|
||||
result.errors.push_back(Validator::Error(getCurrentPath(), type));
|
||||
}
|
||||
|
||||
void appendError(Validator::ErrorType type, string const & field_name)
|
||||
{
|
||||
path.push_back(field_name);
|
||||
appendError(type);
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
bool validate(Union const & ref, Union const & un)
|
||||
{
|
||||
// TODO: Validator Errors for unions could be more granular
|
||||
if (&un == &ref)
|
||||
return true;
|
||||
|
||||
if (ref.isVariant() != un.isVariant()) {
|
||||
appendError(Validator::ErrorType::INCORRECT_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ref.isVariant())
|
||||
return true;
|
||||
|
||||
size_t numfields = ref.getNumberFields();
|
||||
|
||||
if (un.getNumberFields() != numfields) {
|
||||
appendError(Validator::ErrorType::INCORRECT_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
StringArray const & rnames = ref.getFieldNames();
|
||||
StringArray const & unames = un.getFieldNames();
|
||||
|
||||
if (!std::equal(rnames.cbegin(), rnames.cend(), unames.cend())) {
|
||||
appendError(Validator::ErrorType::INCORRECT_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
FieldConstPtrArray const & rfields = ref.getFields();
|
||||
FieldConstPtrArray const & ufields = un.getFields();
|
||||
|
||||
bool fields_ok = true;
|
||||
for (size_t i = 0; i < numfields; ++i) {
|
||||
path.push_back(rnames[i]);
|
||||
fields_ok = fields_ok && validate(*rfields[i], *ufields[i]);
|
||||
path.pop_back();
|
||||
}
|
||||
|
||||
return fields_ok;
|
||||
}
|
||||
|
||||
bool validate(Structure const & ref, Structure const & struc)
|
||||
{
|
||||
if (&struc == &ref)
|
||||
return true;
|
||||
|
||||
StringArray const & rnames = ref.getFieldNames();
|
||||
StringArray const & snames = struc.getFieldNames();
|
||||
|
||||
// TODO: This is naive O(N^2), replace with better algorithm
|
||||
// TODO: This assumes getField doesn't fail (there's no Field::getFieldT)
|
||||
bool fields_ok = true;
|
||||
StringArray::const_iterator ri;
|
||||
for (ri = rnames.cbegin(); ri != rnames.cend(); ++ri) {
|
||||
|
||||
FieldConstPtr rfield(ref.getField(*ri));
|
||||
|
||||
if (std::find(snames.cbegin(), snames.cend(), *ri) == snames.end()) {
|
||||
if (isOptional(*rfield))
|
||||
continue;
|
||||
|
||||
appendError(Validator::ErrorType::MISSING_FIELD, *ri);
|
||||
fields_ok = false;
|
||||
} else {
|
||||
path.push_back(*ri);
|
||||
|
||||
if (!validate(*rfield, *struc.getField(*ri))) {
|
||||
appendError(Validator::ErrorType::INCORRECT_TYPE, *ri);
|
||||
fields_ok = false;
|
||||
}
|
||||
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
return fields_ok;
|
||||
}
|
||||
|
||||
bool validate(Field const & reference, Field const & field)
|
||||
{
|
||||
Type referenceType = reference.getType();
|
||||
if (referenceType != field.getType())
|
||||
return false;
|
||||
|
||||
switch(referenceType) {
|
||||
case Type::scalar:
|
||||
case Type::scalarArray:
|
||||
return true;
|
||||
|
||||
case Type::structure:
|
||||
return validate(
|
||||
static_cast<Structure const &>(reference),
|
||||
static_cast<Structure const &>(field)
|
||||
);
|
||||
|
||||
case Type::structureArray:
|
||||
return validate(
|
||||
*static_cast<StructureArray const &>(reference).getStructure(),
|
||||
*static_cast<StructureArray const &>(field).getStructure()
|
||||
);
|
||||
|
||||
case Type::union_:
|
||||
return validate(
|
||||
static_cast<Union const &>(reference),
|
||||
static_cast<Union const &>(field)
|
||||
);
|
||||
|
||||
case Type::unionArray:
|
||||
return validate(
|
||||
*static_cast<UnionArray const &>(reference).getUnion(),
|
||||
*static_cast<UnionArray const &>(field).getUnion()
|
||||
);
|
||||
|
||||
default:
|
||||
throw std::logic_error("Unknown Field type");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate(Field const & field)
|
||||
{
|
||||
return validate(*definition.structure, field);
|
||||
}
|
||||
};
|
||||
|
||||
Validator::Result Validator::validate(Validator::Definition const & definition,
|
||||
Field const & field)
|
||||
{
|
||||
Helper helper(definition);
|
||||
helper.validate(field);
|
||||
return helper.result;
|
||||
}
|
||||
|
||||
bool Validator::isCompatible(Validator::Definition const & definition,
|
||||
Field const & field)
|
||||
{
|
||||
return Helper(definition).validate(field);
|
||||
}
|
||||
|
||||
bool Validator::isCompatible(Validator::Definition const & definition,
|
||||
StructureConstPtr const & structure)
|
||||
{
|
||||
FieldConstPtr field(std::static_pointer_cast<const Field>(structure));
|
||||
return Validator::isCompatible(definition, *field);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& o, const Validator::Error& error)
|
||||
{
|
||||
switch (error.type) {
|
||||
case Validator::ErrorType::MISSING_FIELD:
|
||||
return o << "Missing field '" << error.path << "'";
|
||||
case Validator::ErrorType::INCORRECT_TYPE:
|
||||
return o << "Field '" << error.path << "' has incorrect type";
|
||||
default:
|
||||
return o << "Unknown error " << error.type << " in field '"
|
||||
<< error.path << "'";
|
||||
}
|
||||
}
|
||||
|
||||
}}
|
@ -101,6 +101,7 @@ namespace detail {
|
||||
|
||||
private:
|
||||
NTNDArrayBuilder();
|
||||
static void once(void*);
|
||||
|
||||
void reset();
|
||||
|
||||
|
68
src/pv/ntvalidator.h
Normal file
68
src/pv/ntvalidator.h
Normal file
@ -0,0 +1,68 @@
|
||||
/* ntvalidator.h */
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
#ifndef NTVALIDATOR_H
|
||||
#define NTVALIDATOR_H
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
#include <shareLib.h>
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
|
||||
namespace epics { namespace nt {
|
||||
|
||||
/**
|
||||
* @brief Validation methods for NT types.
|
||||
*
|
||||
* @author bsm
|
||||
*/
|
||||
|
||||
class epicsShareClass Validator {
|
||||
public:
|
||||
struct Definition {
|
||||
epics::pvData::FieldConstPtr structure;
|
||||
std::set<epics::pvData::Field const *> optional;
|
||||
};
|
||||
|
||||
enum ErrorType { MISSING_FIELD, INCORRECT_TYPE };
|
||||
|
||||
struct Error {
|
||||
std::string path;
|
||||
ErrorType type;
|
||||
|
||||
Error(std::string const & path, ErrorType type) : path(path), type(type) {};
|
||||
};
|
||||
|
||||
struct Result {
|
||||
std::vector<Error> errors;
|
||||
bool valid(void) const { return errors.empty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether a Field is compatible with a NT definition.
|
||||
* @param definition the definition of the reference structure
|
||||
* @param field the Field being tested for compatibility
|
||||
* @return true if field is compatible, false otherwise
|
||||
*/
|
||||
static Result validate(Definition const & definition,
|
||||
epics::pvData::Field const & field);
|
||||
|
||||
static bool isCompatible(Definition const & definition,
|
||||
epics::pvData::Field const & field);
|
||||
|
||||
static bool isCompatible(Definition const & definition,
|
||||
epics::pvData::StructureConstPtr const & structure);
|
||||
|
||||
private:
|
||||
Validator();
|
||||
};
|
||||
|
||||
epicsShareExtern std::ostream& operator<<(std::ostream& o, const Validator::Error& error);
|
||||
|
||||
}}
|
||||
|
||||
#endif /* NTVALIDATOR_H */
|
@ -72,6 +72,10 @@ TESTPROD_HOST += ntutilsTest
|
||||
ntutilsTest_SRCS = ntutilsTest.cpp
|
||||
TESTS += ntutilsTest
|
||||
|
||||
TESTPROD_HOST += ntvalidatorTest
|
||||
ntutilsTest_SRCS = ntvalidatorTest.cpp
|
||||
TESTS += ntvalidatorTest
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
37
test/ntvalidatorTest.cpp
Normal file
37
test/ntvalidatorTest.cpp
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright information and license terms for this software can be
|
||||
* found in the file LICENSE that is included with the distribution
|
||||
*/
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include <pv/ntvalidator.h>
|
||||
#include <pv/ntndarray.h>
|
||||
|
||||
using namespace epics::nt;
|
||||
using epics::pvData::StructureConstPtr;
|
||||
using epics::pvData::Field;
|
||||
|
||||
void test_isCompatible()
|
||||
{
|
||||
testDiag("test_isCompatible");
|
||||
|
||||
StructureConstPtr ref(NTNDArray::createBuilder()->addAlarm()->createStructure());
|
||||
|
||||
Validator::Definition def;
|
||||
def.structure = std::static_pointer_cast<const Field>(ref);
|
||||
def.optional.insert(ref->getField("alarm").get());
|
||||
|
||||
StructureConstPtr struc(NTNDArray::createBuilder()->createStructure());
|
||||
|
||||
testOk1(Validator::isCompatible(def, struc));
|
||||
}
|
||||
|
||||
MAIN(testNTValidator) {
|
||||
testPlan(1);
|
||||
test_isCompatible();
|
||||
return testDone();
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user