From 7d0b6d86ea85c4fc3fe5deadeecfa3edeaf57aa3 Mon Sep 17 00:00:00 2001 From: Dave Hickin Date: Thu, 20 Aug 2015 07:56:20 +0100 Subject: [PATCH] Add NTMatrix including builder and unit tests --- src/Makefile | 2 + src/nt/nt.h | 1 + src/nt/ntmatrix.cpp | 228 ++++++++++++++++++++++++++++++++++ src/nt/ntmatrix.h | 260 +++++++++++++++++++++++++++++++++++++++ test/nt/Makefile | 4 + test/nt/ntmatrixTest.cpp | 211 +++++++++++++++++++++++++++++++ 6 files changed, 706 insertions(+) create mode 100644 src/nt/ntmatrix.cpp create mode 100644 src/nt/ntmatrix.h create mode 100644 test/nt/ntmatrixTest.cpp diff --git a/src/Makefile b/src/Makefile index 663c92b..357c7a9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,6 +15,7 @@ INC += nttable.h INC += ntmultiChannel.h INC += ntscalarMultiChannel.h INC += ntndarray.h +INC += ntmatrix.h LIBSRCS += ntutils.cpp LIBSRCS += ntfield.cpp @@ -25,6 +26,7 @@ LIBSRCS += nttable.cpp LIBSRCS += ntmultiChannel.cpp LIBSRCS += ntscalarMultiChannel.cpp LIBSRCS += ntndarray.cpp +LIBSRCS += ntmatrix.cpp LIBRARY = nt diff --git a/src/nt/nt.h b/src/nt/nt.h index 46ec733..2403c62 100644 --- a/src/nt/nt.h +++ b/src/nt/nt.h @@ -18,6 +18,7 @@ #include #include #include +#include #endif /* NT_H */ diff --git a/src/nt/ntmatrix.cpp b/src/nt/ntmatrix.cpp new file mode 100644 index 0000000..e7fdab0 --- /dev/null +++ b/src/nt/ntmatrix.cpp @@ -0,0 +1,228 @@ +/* ntmatrix.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 { + +StructureConstPtr NTMatrixBuilder::createStructure() +{ + FieldBuilderPtr builder = + getFieldCreate()->createFieldBuilder()-> + setId(NTMatrix::URI)-> + addArray("value", pvDouble); + + if (dim) + builder->addArray("dim", pvInt); + + if (descriptor) + builder->add("descriptor", pvString); + + if (alarm) + builder->add("alarm", ntField->createAlarm()); + + if (timeStamp) + builder->add("timeStamp", ntField->createTimeStamp()); + + if (display) + builder->add("display", ntField->createDisplay()); + + 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; +} + +NTMatrixBuilder::shared_pointer NTMatrixBuilder::addDim() +{ + dim = true; + return shared_from_this(); +} + +NTMatrixBuilder::shared_pointer NTMatrixBuilder::addDescriptor() +{ + descriptor = true; + return shared_from_this(); +} + +NTMatrixBuilder::shared_pointer NTMatrixBuilder::addAlarm() +{ + alarm = true; + return shared_from_this(); +} + +NTMatrixBuilder::shared_pointer NTMatrixBuilder::addTimeStamp() +{ + timeStamp = true; + return shared_from_this(); +} + +NTMatrixBuilder::shared_pointer NTMatrixBuilder::addDisplay() +{ + display = true; + return shared_from_this(); +} + +PVStructurePtr NTMatrixBuilder::createPVStructure() +{ + return getPVDataCreate()->createPVStructure(createStructure()); +} + +NTMatrixPtr NTMatrixBuilder::create() +{ + return NTMatrixPtr(new NTMatrix(createPVStructure())); +} + +NTMatrixBuilder::NTMatrixBuilder() +{ + reset(); +} + +void NTMatrixBuilder::reset() +{ + dim = false; + descriptor = false; + alarm = false; + timeStamp = false; + display = false; + extraFieldNames.clear(); + extraFields.clear(); +} + +NTMatrixBuilder::shared_pointer NTMatrixBuilder::add(string const & name, FieldConstPtr const & field) +{ + extraFields.push_back(field); extraFieldNames.push_back(name); + return shared_from_this(); +} + +} + +const std::string NTMatrix::URI("epics:nt/NTMatrix:1.0"); + +NTMatrix::shared_pointer NTMatrix::wrap(PVStructurePtr const & structure) +{ + if(!isCompatible(structure)) return shared_pointer(); + return wrapUnsafe(structure); +} + +NTMatrix::shared_pointer NTMatrix::wrapUnsafe(PVStructurePtr const & structure) +{ + return shared_pointer(new NTMatrix(structure)); +} + +bool NTMatrix::is_a(StructureConstPtr const & structure) +{ + return NTUtils::is_a(structure->getID(), URI); +} + +bool NTMatrix::isCompatible(PVStructurePtr const & pvStructure) +{ + if(!pvStructure) return false; + PVScalarArrayPtr 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; + pvField = pvStructure->getSubField("display"); + if(pvField && !ntField->isDisplay(pvField->getField())) return false; + pvField = pvStructure->getSubField("control"); + if(pvField && !ntField->isControl(pvField->getField())) return false; + return true; +} + + +NTMatrixBuilderPtr NTMatrix::createBuilder() +{ + return NTMatrixBuilderPtr(new detail::NTMatrixBuilder()); +} + +bool NTMatrix::attachTimeStamp(PVTimeStamp &pvTimeStamp) const +{ + PVStructurePtr ts = getTimeStamp(); + if (ts) + return pvTimeStamp.attach(ts); + else + return false; +} + +bool NTMatrix::attachAlarm(PVAlarm &pvAlarm) const +{ + PVStructurePtr al = getAlarm(); + if (al) + return pvAlarm.attach(al); + else + return false; +} + +bool NTMatrix::attachDisplay(PVDisplay &pvDisplay) const +{ + PVStructurePtr dp = getDisplay(); + if (dp) + return pvDisplay.attach(dp); + else + return false; +} + +PVStructurePtr NTMatrix::getPVStructure() const +{ + return pvNTMatrix; +} + +PVStringPtr NTMatrix::getDescriptor() const +{ + return pvNTMatrix->getSubField("descriptor"); +} + +PVStructurePtr NTMatrix::getTimeStamp() const +{ + return pvNTMatrix->getSubField("timeStamp"); +} + +PVStructurePtr NTMatrix::getAlarm() const +{ + return pvNTMatrix->getSubField("alarm"); +} + +PVStructurePtr NTMatrix::getDisplay() const +{ + return pvNTMatrix->getSubField("display"); +} + +PVDoubleArrayPtr NTMatrix::getValue() const +{ + return pvValue; +} + +PVIntArrayPtr NTMatrix::getDim() const +{ + return pvNTMatrix->getSubField("dim"); +} + +NTMatrix::NTMatrix(PVStructurePtr const & pvStructure) : + pvNTMatrix(pvStructure), + pvValue(pvNTMatrix->getSubField("value")) +{} + + +}} diff --git a/src/nt/ntmatrix.h b/src/nt/ntmatrix.h new file mode 100644 index 0000000..3eb0757 --- /dev/null +++ b/src/nt/ntmatrix.h @@ -0,0 +1,260 @@ +/* ntmatrix.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 NTMATRIX_H +#define NTMATRIX_H + +#ifdef epicsExportSharedSymbols +# define ntmatrixEpicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#include + +#ifdef ntmatrixEpicsExportSharedSymbols +# define epicsExportSharedSymbols +# undef ntmatrixEpicsExportSharedSymbols +#endif + +#include + +#include + + +namespace epics { namespace nt { + +class NTMatrix; +typedef std::tr1::shared_ptr NTMatrixPtr; + +namespace detail { + + /** + * @brief Interface for in-line creating of NTMatrix. + * + * 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 NTMatrixBuilder : + public std::tr1::enable_shared_from_this + { + public: + POINTER_DEFINITIONS(NTMatrixBuilder); + + /** + * Add dimensionfield to the NTMatrix. + * @return this instance of NTMatrixBuilder. + */ + shared_pointer addDim(); + + /** + * Add descriptor field to the NTMatrix. + * @return this instance of NTMatrixBuilder. + */ + shared_pointer addDescriptor(); + + /** + * Add alarm structure to the NTMatrix. + * @return this instance of NTMatrixBuilder. + */ + shared_pointer addAlarm(); + + /** + * Add timeStamp structure to the NTMatrix. + * @return this instance of NTMatrixBuilder. + */ + shared_pointer addTimeStamp(); + + /** + * Add display structure to the NTMatrix. + * @return this instance of NTMatrixBuilder. + */ + shared_pointer addDisplay(); + + /** + * Create a Structure that represents NTMatrix. + * This resets this instance state and allows new instance to be created. + * @return a new instance of Structure. + */ + epics::pvData::StructureConstPtr createStructure(); + + /** + * Create a PVStructure that represents NTMatrix. + * This resets this instance state and allows new instance to be created. + * @return a new instance of PVStructure. + */ + epics::pvData::PVStructurePtr createPVStructure(); + + /** + * Create a NTMatrix instance. + * This resets this instance state and allows new instance to be created. + * @return a new instance of NTMatrix. + */ + NTMatrixPtr create(); + /** + * Add extra Field to the type. + * @param name name of the field. + * @param field a field to add. + * @return this instance of NTMatrixBuilder. + */ + shared_pointer add(std::string const & name, epics::pvData::FieldConstPtr const & field); + + private: + NTMatrixBuilder(); + + void reset(); + + bool dim; + bool descriptor; + bool alarm; + bool timeStamp; + bool display; + + // NOTE: this preserves order, however it does not handle duplicates + epics::pvData::StringArray extraFieldNames; + epics::pvData::FieldConstPtrArray extraFields; + + friend class ::epics::nt::NTMatrix; + }; + +} + +typedef std::tr1::shared_ptr NTMatrixBuilderPtr; + + + +/** + * @brief Convenience Class for NTMatrix + * + * @author dgh + */ +class epicsShareClass NTMatrix +{ +public: + POINTER_DEFINITIONS(NTMatrix); + + static const std::string URI; + + /** + * Wrap (aka dynamic cast, or wrap) the structure to NTMatrix. + * 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 NTMatrix. + * @return NTMatrix 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 NTMatrix. + * @return NTMatrix instance. + */ + static shared_pointer wrapUnsafe(epics::pvData::PVStructurePtr const & structure); + + + /** + * Is the structure an NTMatrix. + * @param structure The structure to test. + * @return (false,true) if (is not, is) an NTMatrix. + */ + static bool is_a(epics::pvData::StructureConstPtr const & structure); + /** + * Is the pvStructure compatible with NTMatrix.. + * 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 NTMatrix builder instance. + * @return builder instance. + */ + static NTMatrixBuilderPtr createBuilder(); + + /** + * Destructor. + */ + ~NTMatrix() {} + + /** + * 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; + + /** + * Attach an pvDisplay. + * @param pvDisplay The pvDisplay that will be attached. + * Does nothing if no display. + * @return true if the operation was successfull (i.e. this instance has a display field), otherwise false. + */ + bool attachDisplay(epics::pvData::PVDisplay &pvDisplay) 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 display. + * @return PVStructurePtr which may be null. + */ + epics::pvData::PVStructurePtr getDisplay() const; + + /** + * Get the value field. + * @return The PVDoubleArray for the values. + */ + epics::pvData::PVDoubleArrayPtr getValue() const; + + /** + * Get the dim field. + * @return The PVIntArray for the dim which may be null. + */ + epics::pvData::PVIntArrayPtr getDim() const; + +private: + NTMatrix(epics::pvData::PVStructurePtr const & pvStructure); + epics::pvData::PVStructurePtr pvNTMatrix; + epics::pvData::PVDoubleArrayPtr pvValue; + + friend class detail::NTMatrixBuilder; +}; + +}} +#endif /* NTMATRIX_H */ diff --git a/test/nt/Makefile b/test/nt/Makefile index d948e0d..3334e0e 100644 --- a/test/nt/Makefile +++ b/test/nt/Makefile @@ -36,6 +36,10 @@ TESTPROD_HOST += ntndarrayTest ntndarrayTest_SRCS = ntndarrayTest.cpp TESTS += ntndarrayTest +TESTPROD_HOST += ntmatrixTest +ntmatrixTest_SRCS = ntmatrixTest.cpp +TESTS += ntmatrixTest + TESTPROD_HOST += ntutilsTest ntutilsTest_SRCS = ntutilsTest.cpp TESTS += ntutilsTest diff --git a/test/nt/ntmatrixTest.cpp b/test/nt/ntmatrixTest.cpp new file mode 100644 index 0000000..175cdda --- /dev/null +++ b/test/nt/ntmatrixTest.cpp @@ -0,0 +1,211 @@ +/** + * 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. + */ + +#include +#include + +#include + +using namespace epics::nt; +using namespace epics::pvData; +using std::tr1::dynamic_pointer_cast; + +static FieldCreatePtr fieldCreate = getFieldCreate(); + +void test_builder() +{ + testDiag("test_builder"); + + NTMatrixBuilderPtr builder = NTMatrix::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + StructureConstPtr structure = builder-> + //arrayValue(pvDouble)-> + addDim()-> + addDescriptor()-> + addAlarm()-> + addTimeStamp()-> + addDisplay()-> + add("extra1",fieldCreate->createScalar(pvString)) -> + add("extra2",fieldCreate->createScalarArray(pvString)) -> + createStructure(); + testOk1(structure.get() != 0); + if (!structure) + return; + + testOk1(NTMatrix::is_a(structure)); + testOk1(structure->getID() == NTMatrix::URI); + testOk1(structure->getNumberFields() == 8); + testOk1(structure->getField("value").get() != 0); + testOk1(structure->getField("dim").get() != 0); + testOk1(structure->getField("descriptor").get() != 0); + testOk1(structure->getField("alarm").get() != 0); + testOk1(structure->getField("timeStamp").get() != 0); + testOk1(structure->getField("display").get() != 0); + + testOk(dynamic_pointer_cast(structure->getField("value")).get() != 0 && + dynamic_pointer_cast(structure->getField("value"))->getElementType() == pvDouble, "value type"); + + std::cout << *structure << std::endl; +} + +void test_ntmatrix() +{ + testDiag("test_ntmatrix"); + + NTMatrixBuilderPtr builder = NTMatrix::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + NTMatrixPtr ntScalarArray = builder-> + //arrayValue(pvInt)-> + addDim()-> + addDescriptor()-> + addAlarm()-> + addTimeStamp()-> + addDisplay()-> + create(); + testOk1(ntScalarArray.get() != 0); + + testOk1(ntScalarArray->getPVStructure().get() != 0); + testOk1(ntScalarArray->getValue().get() != 0); + testOk1(ntScalarArray->getDim().get() != 0); + testOk1(ntScalarArray->getDescriptor().get() != 0); + testOk1(ntScalarArray->getAlarm().get() != 0); + testOk1(ntScalarArray->getTimeStamp().get() != 0); + testOk1(ntScalarArray->getDisplay().get() != 0); + + // + // example how to set values + // + PVDoubleArray::svector newValues; + newValues.push_back(1.0); + newValues.push_back(2.0); + newValues.push_back(8.0); + + PVDoubleArrayPtr pvValueField = ntScalarArray->getValue(); + pvValueField->replace(freeze(newValues)); + + // + // example how to get values + // + PVDoubleArray::const_svector values(pvValueField->view()); + + testOk1(values.size() == 3); + testOk1(values[0] == 1.0); + testOk1(values[1] == 2.0); + testOk1(values[2] == 8.0); + + // + // timeStamp ops + // + PVTimeStamp pvTimeStamp; + if (ntScalarArray->attachTimeStamp(pvTimeStamp)) + { + testPass("timeStamp attach"); + + // example how to set current time + TimeStamp ts; + ts.getCurrent(); + pvTimeStamp.set(ts); + + // example how to get EPICS time + TimeStamp ts2; + pvTimeStamp.get(ts2); + testOk1(ts2.getEpicsSecondsPastEpoch() != 0); + } + else + testFail("timeStamp attach fail"); + + // + // alarm ops + // + PVAlarm pvAlarm; + if (ntScalarArray->attachAlarm(pvAlarm)) + { + testPass("alarm attach"); + + // example how to set an alarm + Alarm alarm; + alarm.setStatus(deviceStatus); + alarm.setSeverity(minorAlarm); + alarm.setMessage("simulation alarm"); + pvAlarm.set(alarm); + } + else + testFail("alarm attach fail"); + + // + // display ops + // + PVDisplay pvDisplay; + if (ntScalarArray->attachDisplay(pvDisplay)) + { + testPass("display attach"); + + // example how to set an display + Display display; + display.setLow(-15); + display.setHigh(15); + display.setDescription("This is a test scalar array"); + display.setFormat("%d"); + display.setUnits("A"); + pvDisplay.set(display); + } + else + testFail("display attach fail"); + + // + // set descriptor + // + ntScalarArray->getDescriptor()->put("This is a test NTMatrix"); + + // dump ntScalarArray + std::cout << *ntScalarArray->getPVStructure() << std::endl; + +} + + +void test_wrap() +{ + testDiag("test_wrap"); + + NTMatrixPtr nullPtr = NTMatrix::wrap(PVStructurePtr()); + testOk(nullPtr.get() == 0, "nullptr wrap"); + + nullPtr = NTMatrix::wrap( + getPVDataCreate()->createPVStructure( + NTField::get()->createTimeStamp() + ) + ); + testOk(nullPtr.get() == 0, "wrong type wrap"); + + + NTMatrixBuilderPtr builder = NTMatrix::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + PVStructurePtr pvStructure = builder-> + createPVStructure(); + testOk1(pvStructure.get() != 0); + if (!pvStructure) + return; + + testOk1(NTMatrix::isCompatible(pvStructure)==true); + NTMatrixPtr ptr = NTMatrix::wrap(pvStructure); + testOk(ptr.get() != 0, "wrap OK"); + + ptr = NTMatrix::wrapUnsafe(pvStructure); + testOk(ptr.get() != 0, "wrapUnsafe OK"); +} + +MAIN(testNTMatrix) { + testPlan(36); + test_builder(); + test_ntmatrix(); + test_wrap(); + return testDone(); +} + +