diff --git a/src/Makefile b/src/Makefile index 35b58db..3e8c1e6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -18,6 +18,7 @@ INC += ntndarray.h INC += ntmatrix.h INC += ntenum.h INC += ntunion.h +INC += ntaggregate.h LIBSRCS += ntutils.cpp LIBSRCS += ntfield.cpp @@ -31,6 +32,7 @@ LIBSRCS += ntndarray.cpp LIBSRCS += ntmatrix.cpp LIBSRCS += ntenum.cpp LIBSRCS += ntunion.cpp +LIBSRCS += ntaggregate.cpp LIBRARY = nt diff --git a/src/nt/nt.h b/src/nt/nt.h index d21d8da..f4ef74a 100644 --- a/src/nt/nt.h +++ b/src/nt/nt.h @@ -21,6 +21,7 @@ #include #include #include +#include #endif /* NT_H */ diff --git a/src/nt/ntaggregate.cpp b/src/nt/ntaggregate.cpp new file mode 100644 index 0000000..243c9ac --- /dev/null +++ b/src/nt/ntaggregate.cpp @@ -0,0 +1,311 @@ +/* ntaggregate.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 NTAggregateBuilder::createStructure() +{ + FieldBuilderPtr builder = + getFieldCreate()->createFieldBuilder()-> + setId(NTAggregate::URI)-> + add("value", pvDouble)-> + add("N", pvLong); + + if (dispersion) + builder->add("dispersion", pvDouble); + + if (first) + builder->add("first", pvDouble); + + if (firstTimeStamp) + builder->add("firstTimeStamp", ntField->createTimeStamp()); + + if (last) + builder->add("last" , pvDouble); + + if (lastTimeStamp) + builder->add("lastTimeStamp", ntField->createTimeStamp()); + + if (max) + builder->add("max", pvDouble); + + if (min) + builder->add("min", pvDouble); + + 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; +} + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addDispersion() +{ + dispersion = true; + return shared_from_this(); +} + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addFirst() +{ + first = true; + return shared_from_this(); +} + + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addFirstTimeStamp() +{ + firstTimeStamp = true; + return shared_from_this(); +} + + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addLast() +{ + last = true; + return shared_from_this(); +} + + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addLastTimeStamp() +{ + lastTimeStamp = true; + return shared_from_this(); +} + + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addMax() +{ + max = true; + return shared_from_this(); +} + + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addMin() +{ + min = true; + return shared_from_this(); +} + + + + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addDescriptor() +{ + descriptor = true; + return shared_from_this(); +} + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addAlarm() +{ + alarm = true; + return shared_from_this(); +} + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::addTimeStamp() +{ + timeStamp = true; + return shared_from_this(); +} + +PVStructurePtr NTAggregateBuilder::createPVStructure() +{ + return getPVDataCreate()->createPVStructure(createStructure()); +} + +NTAggregatePtr NTAggregateBuilder::create() +{ + return NTAggregatePtr(new NTAggregate(createPVStructure())); +} + +NTAggregateBuilder::NTAggregateBuilder() +{ + reset(); +} + +void NTAggregateBuilder::reset() +{ + dispersion = false; + first = false; + firstTimeStamp = false; + last = false; + lastTimeStamp = false; + max = false; + min = false; + + descriptor = false; + alarm = false; + timeStamp = false; + + extraFieldNames.clear(); + extraFields.clear(); +} + +NTAggregateBuilder::shared_pointer NTAggregateBuilder::add(string const & name, FieldConstPtr const & field) +{ + extraFields.push_back(field); extraFieldNames.push_back(name); + return shared_from_this(); +} + + +} + +const std::string NTAggregate::URI("epics:nt/NTAggregate:1.0"); + +NTAggregate::shared_pointer NTAggregate::wrap(PVStructurePtr const & structure) +{ + if(!isCompatible(structure)) return shared_pointer(); + return wrapUnsafe(structure); +} + +NTAggregate::shared_pointer NTAggregate::wrapUnsafe(PVStructurePtr const & structure) +{ + return shared_pointer(new NTAggregate(structure)); +} + +bool NTAggregate::is_a(StructureConstPtr const & structure) +{ + return NTUtils::is_a(structure->getID(), URI); +} + +bool NTAggregate::isCompatible(PVStructurePtr const & pvStructure) +{ + if(!pvStructure) return false; + + PVDoublePtr 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; +} + +NTAggregateBuilderPtr NTAggregate::createBuilder() +{ + return NTAggregateBuilderPtr(new detail::NTAggregateBuilder()); +} + +bool NTAggregate::attachTimeStamp(PVTimeStamp &pvTimeStamp) const +{ + PVStructurePtr ts = getTimeStamp(); + if (ts) + return pvTimeStamp.attach(ts); + else + return false; +} + +bool NTAggregate::attachAlarm(PVAlarm &pvAlarm) const +{ + PVStructurePtr al = getAlarm(); + if (al) + return pvAlarm.attach(al); + else + return false; +} + +PVStructurePtr NTAggregate::getPVStructure() const +{ + return pvNTAggregate; +} + +PVStringPtr NTAggregate::getDescriptor() const +{ + return pvNTAggregate->getSubField("descriptor"); +} + +PVStructurePtr NTAggregate::getTimeStamp() const +{ + return pvNTAggregate->getSubField("timeStamp"); +} + +PVStructurePtr NTAggregate::getAlarm() const +{ + return pvNTAggregate->getSubField("alarm"); +} + +PVDoublePtr NTAggregate::getValue() const +{ + return pvValue; +} + +PVLongPtr NTAggregate::getN() const +{ + return pvNTAggregate->getSubField("N"); +} + +PVDoublePtr NTAggregate::getDispersion() const +{ + return pvNTAggregate->getSubField("dispersion"); +} + +PVDoublePtr NTAggregate::getFirst() const +{ + return pvNTAggregate->getSubField("first"); +} + +PVStructurePtr NTAggregate::getFirstTimeStamp() const +{ + return pvNTAggregate->getSubField("firstTimeStamp"); +} + +PVDoublePtr NTAggregate::getLast() const +{ + return pvNTAggregate->getSubField("last"); +} + +PVStructurePtr NTAggregate::getLastTimeStamp() const +{ + return pvNTAggregate->getSubField("lastTimeStamp"); +} + +PVDoublePtr NTAggregate::getMax() const +{ + return pvNTAggregate->getSubField("max"); +} + +PVDoublePtr NTAggregate::getMin() const +{ + return pvNTAggregate->getSubField("min"); +} + +NTAggregate::NTAggregate(PVStructurePtr const & pvStructure) : + pvNTAggregate(pvStructure), pvValue(pvNTAggregate->getSubField("value")) +{} + + +}} diff --git a/src/nt/ntaggregate.h b/src/nt/ntaggregate.h new file mode 100644 index 0000000..0b231df --- /dev/null +++ b/src/nt/ntaggregate.h @@ -0,0 +1,318 @@ +/* ntaggregate.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 NTAGGREGATE_H +#define NTAGGREGATE_H + +#ifdef epicsExportSharedSymbols +# define ntaggregateEpicsExportSharedSymbols +# undef epicsExportSharedSymbols +#endif + +#ifdef ntaggregateEpicsExportSharedSymbols +# define epicsExportSharedSymbols +# undef ntaggregateEpicsExportSharedSymbols +#endif + +#include + +#include + +namespace epics { namespace nt { + +class NTAggregate; +typedef std::tr1::shared_ptr NTAggregatePtr; + +namespace detail { + + /** + * @brief Interface for in-line creating of NTAggregate. + * + * 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 NTAggregateBuilder : + public std::tr1::enable_shared_from_this + { + public: + POINTER_DEFINITIONS(NTAggregateBuilder); + + /** + * Add dispersion field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addDispersion(); + + /** + * Add first field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addFirst(); + + /** + * Add firstTimeStamp field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addFirstTimeStamp(); + + /** + * Add last field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addLast(); + + /** + * Add lastTimeStamp field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addLastTimeStamp(); + + /** + * Add max field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addMax(); + + /** + * Add min field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addMin(); + + /** + * Add descriptor field to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addDescriptor(); + + /** + * Add alarm structure to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addAlarm(); + + /** + * Add timeStamp structure to the NTAggregate. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer addTimeStamp(); + + /** + * Create a Structure that represents NTAggregate. + * 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 NTAggregate. + * 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 NTAggregate instance. + * This resets this instance state and allows new instance to be created. + * @return a new instance of a NTAggregate. + */ + NTAggregatePtr create(); + /** + * Add extra Field to the type. + * @param name name of the field. + * @param field a field to add. + * @return this instance of NTAggregateBuilder. + */ + shared_pointer add(std::string const & name, epics::pvData::FieldConstPtr const & field); + + private: + NTAggregateBuilder(); + + void reset(); + + bool dispersion; + bool first; + bool firstTimeStamp; + bool last; + bool lastTimeStamp; + bool max; + bool min; + + 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::NTAggregate; + }; + +} + +typedef std::tr1::shared_ptr NTAggregateBuilderPtr; + + +/** + * @brief Convenience Class for NTAggregate + * + * @author dgh + */ +class epicsShareClass NTAggregate +{ +public: + POINTER_DEFINITIONS(NTAggregate); + + static const std::string URI; + + /** + * Wrap (aka dynamic cast, or wrap) the structure to NTAggregate. + * 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 NTAggregate. + * @return NTAggregate 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 NTAggregate. + * @return NTAggregate instance. + */ + static shared_pointer wrapUnsafe(epics::pvData::PVStructurePtr const & structure); + + /** + * Is the structure an NTAggregate. + * @param structure The structure to test. + * @return (false,true) if (is not, is) an NTAggregate. + */ + static bool is_a(epics::pvData::StructureConstPtr const & structure); + /** + * Is the pvStructure compatible with NTAggregate. + * 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 NTAggregate builder instance. + * @return builder instance. + */ + static NTAggregateBuilderPtr createBuilder(); + + /** + * Destructor. + */ + ~NTAggregate() {} + + /** + * 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 PVDouble for the value. + */ + epics::pvData::PVDoublePtr getValue() const; + + /** + * Get the N field. + * @return The PVLong for the N field. + */ + epics::pvData::PVLongPtr getN() const; + + /** + * Get the dispersion field. + * @return The PVDouble for the dispersion which may be null + */ + epics::pvData::PVDoublePtr getDispersion() const; + + /** + * Get the first field. + * @return The PVDouble for the first field which may be null + */ + epics::pvData::PVDoublePtr getFirst() const; + + /** + * Get the firstTimeStamp field. + * @return PVStructurePtr which may be null. + */ + epics::pvData::PVStructurePtr getFirstTimeStamp() const; + + /** + * Get the last field. + * @return The PVDouble for the last field which may be null + */ + epics::pvData::PVDoublePtr getLast() const; + + /** + * Get the lastTimeStamp field. + * @return PVStructurePtr which may be null. + */ + epics::pvData::PVStructurePtr getLastTimeStamp() const; + + /** + * Get the max field. + * @return The PVDouble for the max field which may be null + */ + epics::pvData::PVDoublePtr getMax() const; + + /** + * Get the min field. + * @return The PVDouble for the max field which may be null + */ + epics::pvData::PVDoublePtr getMin() const; + +private: + NTAggregate(epics::pvData::PVStructurePtr const & pvStructure); + epics::pvData::PVStructurePtr pvNTAggregate; + epics::pvData::PVDoublePtr pvValue; + + friend class detail::NTAggregateBuilder; +}; + +}} +#endif /* NTAGGREGATE_H */ diff --git a/test/nt/Makefile b/test/nt/Makefile index bc704dc..aab1e22 100644 --- a/test/nt/Makefile +++ b/test/nt/Makefile @@ -48,6 +48,10 @@ TESTPROD_HOST += ntunionTest ntunionTest_SRCS = ntunionTest.cpp TESTS += ntunionTest +TESTPROD_HOST += ntaggregateTest +ntaggregateTest_SRCS = ntaggregateTest.cpp +TESTS += ntaggregateTest + TESTPROD_HOST += ntutilsTest ntutilsTest_SRCS = ntutilsTest.cpp TESTS += ntutilsTest diff --git a/test/nt/ntaggregateTest.cpp b/test/nt/ntaggregateTest.cpp new file mode 100644 index 0000000..3d961a6 --- /dev/null +++ b/test/nt/ntaggregateTest.cpp @@ -0,0 +1,175 @@ +/** + * 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(); +static StandardFieldPtr standardField = getStandardField(); +static NTFieldPtr ntField = NTField::get(); + +void test_builder() +{ + testDiag("test_builder"); + + NTAggregateBuilderPtr builder = NTAggregate::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + StructureConstPtr structure = builder-> + addDescriptor()-> + addAlarm()-> + addTimeStamp()-> + add("valueAlarm",standardField->doubleAlarm()) -> + add("extra",fieldCreate->createScalarArray(pvString)) -> + createStructure(); + testOk1(structure.get() != 0); + if (!structure) + return; + + testOk1(NTAggregate::is_a(structure)); + testOk1(structure->getID() == NTAggregate::URI); + testOk1(structure->getNumberFields() == 7); + testOk1(structure->getField("value").get() != 0); + testOk1(structure->getField("descriptor").get() != 0); + testOk1(structure->getField("alarm").get() != 0); + testOk1(structure->getField("timeStamp").get() != 0); + + ScalarConstPtr valueField = structure->getField("value"); + testOk(valueField.get() != 0, "value is scalar"); + + std::cout << *structure << std::endl; + +} + +void test_ntaggregate() +{ + testDiag("test_ntaggregate"); + + NTAggregateBuilderPtr builder = NTAggregate::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + NTAggregatePtr ntAggregate = builder-> + addDescriptor()-> + addAlarm()-> + addTimeStamp()-> + add("valueAlarm",standardField->intAlarm()) -> + create(); + testOk1(ntAggregate.get() != 0); + + testOk1(ntAggregate->getPVStructure().get() != 0); + testOk1(ntAggregate->getValue().get() != 0); + testOk1(ntAggregate->getDescriptor().get() != 0); + testOk1(ntAggregate->getAlarm().get() != 0); + testOk1(ntAggregate->getTimeStamp().get() != 0); + + // + // example how to set a value + // + ntAggregate->getValue()->put(1.0); + + // + // example how to get a value + // + double value = ntAggregate->getValue()->get(); + testOk1(value == 1.0); + + // + // timeStamp ops + // + PVTimeStamp pvTimeStamp; + if (ntAggregate->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 (ntAggregate->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"); + + // + // set descriptor + // + ntAggregate->getDescriptor()->put("This is a test NTAggregate"); + + // dump ntAggregate + std::cout << *ntAggregate->getPVStructure() << std::endl; + +} + +void test_wrap() +{ + testDiag("test_wrap"); + + NTAggregatePtr nullPtr = NTAggregate::wrap(PVStructurePtr()); + testOk(nullPtr.get() == 0, "nullptr wrap"); + + nullPtr = NTAggregate::wrap( + getPVDataCreate()->createPVStructure( + NTField::get()->createTimeStamp() + ) + ); + testOk(nullPtr.get() == 0, "wrong type wrap"); + + + NTAggregateBuilderPtr builder = NTAggregate::createBuilder(); + testOk(builder.get() != 0, "Got builder"); + + PVStructurePtr pvStructure = builder-> + createPVStructure(); + testOk1(pvStructure.get() != 0); + if (!pvStructure) + return; + + testOk1(NTAggregate::isCompatible(pvStructure)==true); + NTAggregatePtr ptr = NTAggregate::wrap(pvStructure); + testOk(ptr.get() != 0, "wrap OK"); + + ptr = NTAggregate::wrapUnsafe(pvStructure); + testOk(ptr.get() != 0, "wrapUnsafe OK"); +} + +MAIN(testNTAggregate) { + testPlan(28); + test_builder(); + test_ntaggregate(); + test_wrap(); + return testDone(); +} + +