From 0d66034092b21b7a5dd2adf69fe34948353d9c1e Mon Sep 17 00:00:00 2001 From: Dave Hickin Date: Thu, 20 Aug 2015 08:01:29 +0100 Subject: [PATCH] Add NTUnion including builder and unit tests --- src/Makefile | 2 + src/nt/nt.h | 1 + src/nt/ntunion.cpp | 193 +++++++++++++++++++++++++++++++++++++++ src/nt/ntunion.h | 223 +++++++++++++++++++++++++++++++++++++++++++++ test/nt/Makefile | 4 + 5 files changed, 423 insertions(+) create mode 100644 src/nt/ntunion.cpp create mode 100644 src/nt/ntunion.h diff --git a/src/Makefile b/src/Makefile index 3871918..35b58db 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,6 +17,7 @@ INC += ntscalarMultiChannel.h INC += ntndarray.h INC += ntmatrix.h INC += ntenum.h +INC += ntunion.h LIBSRCS += ntutils.cpp LIBSRCS += ntfield.cpp @@ -29,6 +30,7 @@ LIBSRCS += ntscalarMultiChannel.cpp LIBSRCS += ntndarray.cpp LIBSRCS += ntmatrix.cpp LIBSRCS += ntenum.cpp +LIBSRCS += ntunion.cpp LIBRARY = nt diff --git a/src/nt/nt.h b/src/nt/nt.h index 8dc4f52..d21d8da 100644 --- a/src/nt/nt.h +++ b/src/nt/nt.h @@ -20,6 +20,7 @@ #include #include #include +#include #endif /* NT_H */ diff --git a/src/nt/ntunion.cpp b/src/nt/ntunion.cpp new file mode 100644 index 0000000..f911f71 --- /dev/null +++ b/src/nt/ntunion.cpp @@ -0,0 +1,193 @@ +/* ntunion.cpp */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ + +#define epicsExportSharedSymbols +#include +#include + +using namespace std; +using namespace epics::pvData; + +namespace epics { namespace nt { + +static NTFieldPtr ntField = NTField::get(); + +namespace detail { + +static NTFieldPtr ntField = NTField::get(); + + +StructureConstPtr NTUnionBuilder::createStructure() +{ + FieldBuilderPtr builder = + getFieldCreate()->createFieldBuilder()-> + setId(NTUnion::URI)-> + add("value", valueType); + + if (descriptor) + builder->add("descriptor", pvString); + + if (alarm) + builder->add("alarm", ntField->createAlarm()); + + if (timeStamp) + builder->add("timeStamp", ntField->createTimeStamp()); + + size_t extraCount = extraFieldNames.size(); + for (size_t i = 0; i< extraCount; i++) + builder->add(extraFieldNames[i], extraFields[i]); + + + StructureConstPtr s = builder->createStructure(); + + reset(); + return s; +} + +NTUnionBuilder::shared_pointer NTUnionBuilder::addDescriptor() +{ + descriptor = true; + return shared_from_this(); +} + +NTUnionBuilder::shared_pointer NTUnionBuilder::addAlarm() +{ + alarm = true; + return shared_from_this(); +} + +NTUnionBuilder::shared_pointer NTUnionBuilder::addTimeStamp() +{ + timeStamp = true; + return shared_from_this(); +} + +PVStructurePtr NTUnionBuilder::createPVStructure() +{ + return getPVDataCreate()->createPVStructure(createStructure()); +} + +NTUnionPtr NTUnionBuilder::create() +{ + return NTUnionPtr(new NTUnion(createPVStructure())); +} + +NTUnionBuilder::NTUnionBuilder() +{ + reset(); +} + +void NTUnionBuilder::reset() +{ + valueType = getFieldCreate()->createVariantUnion(); + descriptor = false; + alarm = false; + timeStamp = false; + extraFieldNames.clear(); + extraFields.clear(); +} + +NTUnionBuilder::shared_pointer NTUnionBuilder::add(string const & name, FieldConstPtr const & field) +{ + extraFields.push_back(field); extraFieldNames.push_back(name); + return shared_from_this(); +} + + +} + +const std::string NTUnion::URI("epics:nt/NTUnion:1.0"); + +NTUnion::shared_pointer NTUnion::wrap(PVStructurePtr const & structure) +{ + if(!isCompatible(structure)) return shared_pointer(); + return wrapUnsafe(structure); +} + +NTUnion::shared_pointer NTUnion::wrapUnsafe(PVStructurePtr const & structure) +{ + return shared_pointer(new NTUnion(structure)); +} + +bool NTUnion::is_a(StructureConstPtr const & structure) +{ + return NTUtils::is_a(structure->getID(), URI); +} + +bool NTUnion::isCompatible(PVStructurePtr const & pvStructure) +{ + if(!pvStructure) return false; + + PVUnionPtr pvValue = pvStructure->getSubField("value"); + if(!pvValue) return false; + + PVFieldPtr pvField = pvStructure->getSubField("descriptor"); + if(pvField && !pvStructure->getSubField("descriptor")) return false; + + pvField = pvStructure->getSubField("alarm"); + if(pvField && !ntField->isAlarm(pvField->getField())) return false; + + pvField = pvStructure->getSubField("timeStamp"); + if(pvField && !ntField->isTimeStamp(pvField->getField())) return false; + + return true; +} + +NTUnionBuilderPtr NTUnion::createBuilder() +{ + return NTUnionBuilderPtr(new detail::NTUnionBuilder()); +} + +bool NTUnion::attachTimeStamp(PVTimeStamp &pvTimeStamp) const +{ + PVStructurePtr ts = getTimeStamp(); + if (ts) + return pvTimeStamp.attach(ts); + else + return false; +} + +bool NTUnion::attachAlarm(PVAlarm &pvAlarm) const +{ + PVStructurePtr al = getAlarm(); + if (al) + return pvAlarm.attach(al); + else + return false; +} + +PVStructurePtr NTUnion::getPVStructure() const +{ + return pvNTUnion; +} + +PVStringPtr NTUnion::getDescriptor() const +{ + return pvNTUnion->getSubField("descriptor"); +} + +PVStructurePtr NTUnion::getTimeStamp() const +{ + return pvNTUnion->getSubField("timeStamp"); +} + +PVStructurePtr NTUnion::getAlarm() const +{ + return pvNTUnion->getSubField("alarm"); +} + +PVUnionPtr NTUnion::getValue() const +{ + return pvValue; +} + +NTUnion::NTUnion(PVStructurePtr const & pvStructure) : + pvNTUnion(pvStructure), pvValue(pvNTUnion->getSubField("value")) +{} + + +}} diff --git a/src/nt/ntunion.h b/src/nt/ntunion.h new file mode 100644 index 0000000..f080f84 --- /dev/null +++ b/src/nt/ntunion.h @@ -0,0 +1,223 @@ +/* ntunion.h */ +/** + * Copyright - See the COPYRIGHT that is included with this distribution. + * EPICS pvDataCPP is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + */ +#ifndef NTUNION_H +#define NTUNION_H + +#ifdef epicsExportSharedSymbols +# define ntunionEpicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#ifdef ntunionEpicsExportSharedSymbols +# define epicsExportSharedSymbols +# undef ntunionEpicsExportSharedSymbols +#endif + +#include + +#include + +namespace epics { namespace nt { + +class NTUnion; +typedef std::tr1::shared_ptr NTUnionPtr; + +namespace detail { + + /** + * @brief Interface for in-line creating of NTUnion. + * + * One instance can be used to create multiple instances. + * An instance of this object must not be used concurrently (an object has a state). + * @author dgh + */ + class epicsShareClass NTUnionBuilder : + public std::tr1::enable_shared_from_this + { + public: + POINTER_DEFINITIONS(NTUnionBuilder); + + /** + * Add descriptor field to the NTUnion. + * @return this instance of NTUnionBuilder. + */ + shared_pointer addDescriptor(); + + /** + * Add alarm structure to the NTUnion. + * @return this instance of NTUnionBuilder. + */ + shared_pointer addAlarm(); + + /** + * Add timeStamp structure to the NTUnion. + * @return this instance of NTUnionBuilder. + */ + shared_pointer addTimeStamp(); + + /** + * Create a Structure that represents NTUnion. + * This resets this instance state and allows new instance to be created. + * @return a new instance of a Structure. + */ + epics::pvData::StructureConstPtr createStructure(); + + /** + * Create a PVStructure that represents NTUnion. + * This resets this instance state and allows new instance to be created. + * @return a new instance of a PVStructure. + */ + epics::pvData::PVStructurePtr createPVStructure(); + + /** + * Create a NTUnion instance. + * This resets this instance state and allows new instance to be created. + * @return a new instance of a NTUnion. + */ + NTUnionPtr create(); + /** + * Add extra Field to the type. + * @param name name of the field. + * @param field a field to add. + * @return this instance of NTUnionBuilder. + */ + shared_pointer add(std::string const & name, epics::pvData::FieldConstPtr const & field); + + private: + NTUnionBuilder(); + + epics::pvData::UnionConstPtr valueType; + + void reset(); + + bool descriptor; + bool alarm; + bool timeStamp; + + // NOTE: this preserves order, however it does not handle duplicates + epics::pvData::StringArray extraFieldNames; + epics::pvData::FieldConstPtrArray extraFields; + + friend class ::epics::nt::NTUnion; + }; + +} + +typedef std::tr1::shared_ptr NTUnionBuilderPtr; + + + +/** + * @brief Convenience Class for NTUnion + * + * @author dgh + */ +class epicsShareClass NTUnion +{ +public: + POINTER_DEFINITIONS(NTUnion); + + static const std::string URI; + + /** + * Wrap (aka dynamic cast, or wrap) the structure to NTUnion. + * First isCompatible is called. + * This method will nullptr if the structure is is not compatible. + * This method will nullptr if the structure is nullptr. + * @param structure The structure to wrap-ed (dynamic cast, wrapped) to NTUnion. + * @return NTUnion instance on success, nullptr otherwise. + */ + static shared_pointer wrap(epics::pvData::PVStructurePtr const & structure); + + /** + * Wrap (aka dynamic cast, or wrap) the structure to NTMultiChannel without checking for isCompatible + * @param structure The structure to wrap-ed (dynamic cast, wrapped) to NTUnion. + * @return NTUnion instance. + */ + static shared_pointer wrapUnsafe(epics::pvData::PVStructurePtr const & structure); + + /** + * Is the structure an NTUnion. + * @param structure The structure to test. + * @return (false,true) if (is not, is) an NTUnion. + */ + static bool is_a(epics::pvData::StructureConstPtr const & structure); + /** + * Is the pvStructure compatible with NTUnion. + * This method introspects the fields to see if they are compatible. + * @param pvStructure The pvStructure to test. + * @return (false,true) if (is not, is) an NTMultiChannel. + */ + static bool isCompatible( + epics::pvData::PVStructurePtr const &pvStructure); + /** + * Create a NTUnion builder instance. + * @return builder instance. + */ + static NTUnionBuilderPtr createBuilder(); + + /** + * Destructor. + */ + ~NTUnion() {} + + /** + * Attach a pvTimeStamp. + * @param pvTimeStamp The pvTimeStamp that will be attached. + * Does nothing if no timeStamp. + * @return true if the operation was successfull (i.e. this instance has a timeStamp field), otherwise false. + */ + bool attachTimeStamp(epics::pvData::PVTimeStamp &pvTimeStamp) const; + + /** + * Attach an pvAlarm. + * @param pvAlarm The pvAlarm that will be attached. + * Does nothing if no alarm. + * @return true if the operation was successfull (i.e. this instance has a timeStamp field), otherwise false. + */ + bool attachAlarm(epics::pvData::PVAlarm &pvAlarm) const; + + /** + * Get the pvStructure. + * @return PVStructurePtr. + */ + epics::pvData::PVStructurePtr getPVStructure() const; + + /** + * Get the descriptor field. + * @return The pvString or null if no function field. + */ + epics::pvData::PVStringPtr getDescriptor() const; + + /** + * Get the timeStamp. + * @return PVStructurePtr which may be null. + */ + epics::pvData::PVStructurePtr getTimeStamp() const; + + /** + * Get the alarm. + * @return PVStructurePtr which may be null. + */ + epics::pvData::PVStructurePtr getAlarm() const; + + /** + * Get the value field. + * @return The PVUnion for the values. + */ + epics::pvData::PVUnionPtr getValue() const; + +private: + NTUnion(epics::pvData::PVStructurePtr const & pvStructure); + epics::pvData::PVStructurePtr pvNTUnion; + epics::pvData::PVUnionPtr pvValue; + + friend class detail::NTUnionBuilder; +}; + +}} +#endif /* NTUNION_H */ diff --git a/test/nt/Makefile b/test/nt/Makefile index 828e311..bc704dc 100644 --- a/test/nt/Makefile +++ b/test/nt/Makefile @@ -44,6 +44,10 @@ TESTPROD_HOST += ntenumTest ntenumTest_SRCS = ntenumTest.cpp TESTS += ntenumTest +TESTPROD_HOST += ntunionTest +ntunionTest_SRCS = ntunionTest.cpp +TESTS += ntunionTest + TESTPROD_HOST += ntutilsTest ntutilsTest_SRCS = ntutilsTest.cpp TESTS += ntutilsTest