diff --git a/ADApp/pluginSrc/NDPluginAttribute.cpp b/ADApp/pluginSrc/NDPluginAttribute.cpp index 1a168142..c8fa924f 100644 --- a/ADApp/pluginSrc/NDPluginAttribute.cpp +++ b/ADApp/pluginSrc/NDPluginAttribute.cpp @@ -175,7 +175,7 @@ NDPluginAttribute::NDPluginAttribute(const char *portName, int queueSize, int bl NDArrayPort, NDArrayAddr, std::max(maxAttributes,2), maxBuffers, maxMemory, asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynGenericPointerMask, asynInt32ArrayMask | asynFloat64Mask | asynFloat64ArrayMask | asynGenericPointerMask, - ASYN_MULTIDEVICE, 1, priority, stackSize, 1) + ASYN_MULTIDEVICE, 1, priority, stackSize, 1, true) { int i; // static const char *functionName = "NDPluginAttribute::NDPluginAttribute"; diff --git a/ADApp/pluginTests/AttributePluginWrapper.cpp b/ADApp/pluginTests/AttributePluginWrapper.cpp new file mode 100644 index 00000000..0be94562 --- /dev/null +++ b/ADApp/pluginTests/AttributePluginWrapper.cpp @@ -0,0 +1,22 @@ +/* + * AttributePluginWrapper.cpp + * + * Created on: 27 Apr 2026 + * Author: Jakub Wlodek + */ + +#include "AttributePluginWrapper.h" + +AttributePluginWrapper::AttributePluginWrapper(const std::string& port, + const std::string& detectorPort, + int maxAttributes) + : NDPluginAttribute(port.c_str(), 50, 0, detectorPort.c_str(), 0, + maxAttributes, 0, 0, 0, 0), + AsynPortClientContainer(port) +{ +} + +AttributePluginWrapper::~AttributePluginWrapper() +{ + cleanup(); +} diff --git a/ADApp/pluginTests/AttributePluginWrapper.h b/ADApp/pluginTests/AttributePluginWrapper.h new file mode 100644 index 00000000..f080d960 --- /dev/null +++ b/ADApp/pluginTests/AttributePluginWrapper.h @@ -0,0 +1,22 @@ +/* + * AttributePluginWrapper.h + * + * Created on: 27 Apr 2026 + * Author: Jakub Wlodek + */ + +#ifndef ADAPP_PLUGINTESTS_ATTRIBUTEPLUGINWRAPPER_H_ +#define ADAPP_PLUGINTESTS_ATTRIBUTEPLUGINWRAPPER_H_ + +#include +#include "AsynPortClientContainer.h" + +class AttributePluginWrapper : public NDPluginAttribute, public AsynPortClientContainer +{ +public: + AttributePluginWrapper(const std::string& port, const std::string& detectorPort, + int maxAttributes); + virtual ~AttributePluginWrapper(); +}; + +#endif /* ADAPP_PLUGINTESTS_ATTRIBUTEPLUGINWRAPPER_H_ */ diff --git a/ADApp/pluginTests/Makefile b/ADApp/pluginTests/Makefile index 44553717..df6076b1 100644 --- a/ADApp/pluginTests/Makefile +++ b/ADApp/pluginTests/Makefile @@ -40,6 +40,7 @@ ifeq ($(WITH_BOOST),YES) ADTestUtility_SRCS += ROIPluginWrapper.cpp ADTestUtility_SRCS += OverlayPluginWrapper.cpp ADTestUtility_SRCS += CodecPluginWrapper.cpp + ADTestUtility_SRCS += AttributePluginWrapper.cpp PROD_IOC_Linux += plugin-test PROD_IOC_Darwin += plugin-test @@ -55,6 +56,7 @@ ifeq ($(WITH_BOOST),YES) plugin-test_SRCS += test_NDPluginTimeSeries.cpp plugin-test_SRCS += test_NDPluginFFT.cpp plugin-test_SRCS += test_NDPluginCodec.cpp + plugin-test_SRCS += test_NDPluginAttribute.cpp plugin-test_SRCS += test_NDPluginAttrPlot.cpp plugin-test_SRCS += test_NDPluginROI.cpp plugin-test_SRCS += test_NDPluginOverlay.cpp diff --git a/ADApp/pluginTests/test_NDPluginAttribute.cpp b/ADApp/pluginTests/test_NDPluginAttribute.cpp new file mode 100644 index 00000000..cb32d1b1 --- /dev/null +++ b/ADApp/pluginTests/test_NDPluginAttribute.cpp @@ -0,0 +1,147 @@ +/* + * test_NDPluginAttribute.cpp + * + * Tests NDPluginAttribute, including with compressed arrays. + * + * Created on: 27 Apr 2026 + * Author: Jakub Wlodek + */ + +#include +#include + +#include "boost/test/unit_test.hpp" + +#include +#include +#include +#include +#include +#include + +#include "testingutilities.h" +#include "AttributePluginWrapper.h" + +#define TEST_XSIZE 8 +#define TEST_YSIZE 8 +#define TEST_NELEMENTS (TEST_XSIZE * TEST_YSIZE) + +struct AttributeTestFixture +{ + NDArrayPool *arrayPool; + asynNDArrayDriver *dummy_driver; + AttributePluginWrapper *attr; + + AttributeTestFixture() + { + std::string dummy_port("simAttr"), testport("testAttr"); + uniqueAsynPortName(dummy_port); + uniqueAsynPortName(testport); + + dummy_driver = new asynNDArrayDriver( + dummy_port.c_str(), 1, 0, 0, + asynGenericPointerMask, asynGenericPointerMask, 0, 0, 0, 0); + arrayPool = dummy_driver->pNDArrayPool; + + attr = new AttributePluginWrapper(testport.c_str(), dummy_port.c_str(), 4); + attr->start(); + + attr->write(NDPluginDriverEnableCallbacksString, 1); + attr->write(NDPluginDriverBlockingCallbacksString, 1); + } + + ~AttributeTestFixture() + { + delete attr; + delete dummy_driver; + } + + NDArray *createTestArray(bool compress = false) + { + size_t dims[2] = {TEST_XSIZE, TEST_YSIZE}; + NDArray *arr = arrayPool->alloc(2, dims, NDUInt16, 0, NULL); + epicsUInt16 *pData = (epicsUInt16 *)arr->pData; + for (int i = 0; i < TEST_NELEMENTS; i++) { + pData[i] = (epicsUInt16)(i % 256); + } +#ifdef HAVE_ZLIB + if (compress) { + NDCodecStatus_t status; + char errorMessage[256] = ""; + NDArray *compressed = compressZlib(arr, 6, &status, errorMessage); + arr->release(); + return compressed; + } +#else + (void)compress; +#endif + return arr; + } + + void processArray(NDArray *pArray) + { + attr->lock(); + attr->processCallbacks(pArray); + attr->unlock(); + } +}; + +BOOST_FIXTURE_TEST_SUITE(AttributePluginTests, AttributeTestFixture) + +/* Test that NDPluginAttribute reads attributes from an uncompressed array */ +BOOST_AUTO_TEST_CASE(test_attribute_uncompressed) +{ + NDArray *input = createTestArray(); + double testVal = 42.5; + input->pAttributeList->add("TestAttr", "", NDAttrFloat64, &testVal); + + attr->write(NDPluginAttributeAttrNameString, std::string("TestAttr"), 0); + + processArray(input); + + double readVal = attr->readDouble(NDPluginAttributeValString, 0); + BOOST_CHECK_CLOSE(readVal, 42.5, 0.001); + + input->release(); +} + +#ifdef HAVE_ZLIB +/* Test that NDPluginAttribute accepts a zlib-compressed array and still reads attributes */ +BOOST_AUTO_TEST_CASE(test_attribute_compressed_array) +{ + NDArray *input = createTestArray(true); + BOOST_REQUIRE(input != NULL); + BOOST_CHECK_EQUAL(input->codec.name, codecName[NDCODEC_ZLIB]); + + double testVal = 99.0; + input->pAttributeList->add("CompAttr", "", NDAttrFloat64, &testVal); + + attr->write(NDPluginAttributeAttrNameString, std::string("CompAttr"), 0); + + processArray(input); + + double readVal = attr->readDouble(NDPluginAttributeValString, 0); + BOOST_CHECK_CLOSE(readVal, 99.0, 0.001); + + input->release(); +} + +/* Test that uniqueId is readable from a compressed array */ +BOOST_AUTO_TEST_CASE(test_attribute_compressed_unique_id) +{ + NDArray *input = createTestArray(true); + BOOST_REQUIRE(input != NULL); + input->uniqueId = 12345; + + attr->write(NDPluginAttributeAttrNameString, std::string("NDArrayUniqueId"), 0); + + processArray(input); + + double readVal = attr->readDouble(NDPluginAttributeValString, 0); + BOOST_CHECK_CLOSE(readVal, 12345.0, 0.001); + + input->release(); +} +#endif /* HAVE_ZLIB */ + +BOOST_AUTO_TEST_SUITE_END()