From bf07eb3eeec06f64fa02a7841dbb4239295e876c Mon Sep 17 00:00:00 2001 From: James Souter Date: Mon, 28 Apr 2025 10:09:34 +0100 Subject: [PATCH 01/23] Add outline for switching between WITH_PVXS and WITH_PVA builds --- ADApp/Makefile | 4 + ADApp/commonDriverMakefile | 6 + ADApp/commonLibraryMakefile | 5 + ADApp/ntndArrayConverterSrc/Makefile | 23 +- .../ntndArrayConverterPvxs.cpp | 595 ++++++++++++++++++ ADApp/pluginSrc/Makefile | 6 + ADApp/pluginSrc/NDPluginPvxs.cpp | 228 +++++++ 7 files changed, 859 insertions(+), 8 deletions(-) create mode 100644 ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp create mode 100644 ADApp/pluginSrc/NDPluginPvxs.cpp diff --git a/ADApp/Makefile b/ADApp/Makefile index 0b2f54e2b..f4987ea54 100644 --- a/ADApp/Makefile +++ b/ADApp/Makefile @@ -16,6 +16,10 @@ ifeq ($(WITH_PVA), YES) DIRS += ntndArrayConverterSrc ntndArrayConverterSrc_DEPEND_DIRS += ADSrc pluginSrc_DEPEND_DIRS += ntndArrayConverterSrc +else ifeq ($(WITH_PVXS), YES) + DIRS += ntndArrayConverterSrc + ntndArrayConverterSrc_DEPEND_DIRS += ADSrc + pluginSrc_DEPEND_DIRS += ntndArrayConverterSrc endif DIRS += op diff --git a/ADApp/commonDriverMakefile b/ADApp/commonDriverMakefile index 1347f01e5..872aa2edd 100644 --- a/ADApp/commonDriverMakefile +++ b/ADApp/commonDriverMakefile @@ -37,6 +37,12 @@ ifeq ($(WITH_PVA),YES) endif endif +ifeq ($(WITH_PVXS),YES) + $(DBD_NAME)_DBD += NDPluginPvxs.dbd + PROD_LIBS += pvxs + PROD_LIBS += ntndArrayConverterPvxs +endif + ifeq ($(WITH_NETCDF),YES) $(DBD_NAME)_DBD += NDFileNetCDF.dbd ifeq ($(NETCDF_EXTERNAL),NO) diff --git a/ADApp/commonLibraryMakefile b/ADApp/commonLibraryMakefile index a159db671..c96974758 100644 --- a/ADApp/commonLibraryMakefile +++ b/ADApp/commonLibraryMakefile @@ -11,6 +11,11 @@ ifeq ($(WITH_PVA),YES) LIB_LIBS += pvData endif +ifeq ($(WITH_PVXS),YES) + LIB_LIBS += ntndArrayConverterPvxs + LIB_LIBS += pvxs +endif + ifeq ($(WITH_NETCDF),YES) ifeq ($(NETCDF_EXTERNAL),NO) LIB_LIBS += netCDF diff --git a/ADApp/ntndArrayConverterSrc/Makefile b/ADApp/ntndArrayConverterSrc/Makefile index 662520807..07695450d 100644 --- a/ADApp/ntndArrayConverterSrc/Makefile +++ b/ADApp/ntndArrayConverterSrc/Makefile @@ -4,19 +4,26 @@ include $(TOP)/configure/CONFIG # ADD MACRO DEFINITIONS AFTER THIS LINE #============================= -LIBRARY_IOC += ntndArrayConverter -INC += ntndArrayConverterAPI.h -INC += ntndArrayConverter.h -LIB_SRCS += ntndArrayConverter.cpp +ifeq ($(WITH_PVA), YES) + LIBRARY_IOC += ntndArrayConverter + INC += ntndArrayConverter.h + LIB_SRCS += ntndArrayConverter.cpp + LIB_LIBS += pvData + LIB_LIBS += nt +endif -USR_CPPFLAGS += -DBUILDING_ntndArrayConverter_API +ifeq ($(WITH_PVXS), YES) + LIBRARY_IOC += ntndArrayConverterPvxs + INC += ntndArrayConverterPvxs.h + LIB_SRCS += ntndArrayConverterPvxs.cpp + LIB_LIBS += pvxs +endif +INC += ntndArrayConverterAPI.h +USR_CPPFLAGS += -DBUILDING_ntndArrayConverter_API LIB_LIBS += ADBase -LIB_LIBS += pvData -LIB_LIBS += nt LIB_LIBS += asyn LIB_LIBS += $(EPICS_BASE_IOC_LIBS) - #============================= include $(TOP)/configure/RULES diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp new file mode 100644 index 000000000..d0673b32a --- /dev/null +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -0,0 +1,595 @@ +#include "ntndArrayConverter.h" + +using namespace std; +using namespace epics::nt; +using namespace epics::pvData; +using tr1::static_pointer_cast; + +// Maps ScalarType to NDAttrDataType_t +static const NDAttrDataType_t scalarToNDAttrDataType[pvString+1] = { + NDAttrInt8, // 0: pvBoolean (not supported) + NDAttrInt8, // 1: pvByte + NDAttrInt16, // 2: pvShort + NDAttrInt32, // 3: pvInt + NDAttrInt64, // 4: pvLong + NDAttrUInt8, // 5: pvUByte + NDAttrUInt16, // 6: pvUShort + NDAttrUInt32, // 7: pvUInt + NDAttrUInt64, // 8: pvULong + NDAttrFloat32, // 9: pvFloat + NDAttrFloat64, // 10: pvDouble + NDAttrString, // 11: pvString +}; + +// Maps NDDataType to ScalarType +static const ScalarType NDDataTypeToScalar[NDFloat64 + 1] = { + pvByte, // 0: NDInt8 + pvUByte, // 1: NDUInt8 + pvShort, // 2: NDInt16 + pvUShort, // 3: NDUInt16 + pvInt, // 4: NDInt32 + pvUInt, // 5: NDUInt32 + pvLong, // 6: NDInt32 + pvULong, // 7: NDUInt32 + pvFloat, // 8: NDFloat32 + pvDouble, // 9: NDFloat64 +}; + +static const PVDataCreatePtr PVDC = getPVDataCreate(); + +template +struct freeNDArray { + NDArray *array; + freeNDArray(NDArray *array) : array(array) {}; + void operator()(dataType *data) { array->release(); } +}; + +NTNDArrayConverter::NTNDArrayConverter (NTNDArrayPtr array) : m_array(array) {} + +ScalarType NTNDArrayConverter::getValueType (void) +{ + string fieldName(m_array->getValue()->getSelectedFieldName()); + + /* + * Check if union field selected. It happens when the driver is run before + * the producer. There is a monitor update that is sent on the + * initialization of a PVRecord with no real data. + */ + if(fieldName.empty()) + throw std::runtime_error("no union field selected"); + + string typeName(fieldName.substr(0,fieldName.find("Value"))); + return ScalarTypeFunc::getScalarType(typeName); +} + +NDColorMode_t NTNDArrayConverter::getColorMode (void) +{ + NDColorMode_t colorMode = NDColorModeMono; + PVStructureArray::const_svector attrs(m_array->getAttribute()->view()); + + for(PVStructureArray::const_svector::iterator it(attrs.cbegin()); + it != attrs.cend(); ++it) + { + PVStringPtr nameFld((*it)->getSubFieldT("name")); + if(nameFld->get() == "ColorMode") + { + PVUnionPtr valueUnion((*it)->getSubFieldT("value")); + PVScalar::shared_pointer valueFld(valueUnion->get()); + if(valueFld) { + int cm = valueFld->getAs(); + colorMode = (NDColorMode_t) cm; + } else + throw std::runtime_error("Error accessing attribute ColorMode"); + } + } + + return colorMode; +} + +NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) +{ + NTNDArrayInfo_t info = {0}; + + PVStructureArray::const_svector dims(m_array->getDimension()->view()); + + info.ndims = (int) dims.size(); + info.nElements = 1; + + for(int i = 0; i < info.ndims; ++i) + { + info.dims[i] = (size_t) dims[i]->getSubField("size")->get(); + info.nElements *= info.dims[i]; + } + + PVStructurePtr codec(m_array->getCodec()); + + info.codec = codec->getSubField("name")->get(); + + ScalarType dataType; + + if (info.codec.empty()) + dataType = getValueType(); + else { + // Read uncompressed data type + PVIntPtr udt(codec->getSubField("parameters")->get()); + dataType = static_cast(udt->get()); + } + + NDDataType_t dt; + int bpe; + switch(dataType) + { + case pvByte: dt = NDInt8; bpe = sizeof(epicsInt8); break; + case pvUByte: dt = NDUInt8; bpe = sizeof(epicsUInt8); break; + case pvShort: dt = NDInt16; bpe = sizeof(epicsInt16); break; + case pvUShort: dt = NDUInt16; bpe = sizeof(epicsUInt16); break; + case pvInt: dt = NDInt32; bpe = sizeof(epicsInt32); break; + case pvUInt: dt = NDUInt32; bpe = sizeof(epicsUInt32); break; + case pvLong: dt = NDInt64; bpe = sizeof(epicsInt64); break; + case pvULong: dt = NDUInt64; bpe = sizeof(epicsUInt64); break; + case pvFloat: dt = NDFloat32; bpe = sizeof(epicsFloat32); break; + case pvDouble: dt = NDFloat64; bpe = sizeof(epicsFloat64); break; + case pvBoolean: + case pvString: + default: + throw std::runtime_error("invalid value data type"); + break; + } + + info.dataType = dt; + info.bytesPerElement = bpe; + info.totalBytes = info.nElements*info.bytesPerElement; + info.colorMode = getColorMode(); + + if(info.ndims > 0) + { + info.x.dim = 0; + info.x.stride = 1; + info.x.size = info.dims[0]; + } + + if(info.ndims > 1) + { + info.y.dim = 1; + info.y.stride = 1; + info.y.size = info.dims[1]; + } + + if(info.ndims == 3) + { + switch(info.colorMode) + { + case NDColorModeRGB1: + info.x.dim = 1; + info.y.dim = 2; + info.color.dim = 0; + info.x.stride = info.dims[0]; + info.y.stride = info.dims[0]*info.dims[1]; + info.color.stride = 1; + break; + + case NDColorModeRGB2: + info.x.dim = 0; + info.y.dim = 2; + info.color.dim = 1; + info.x.stride = 1; + info.y.stride = info.dims[0]*info.dims[1]; + info.color.stride = info.dims[0]; + break; + + case NDColorModeRGB3: + info.x.dim = 1; + info.y.dim = 2; + info.color.dim = 0; + info.x.stride = info.dims[0]; + info.y.stride = info.dims[0]*info.dims[1]; + info.color.stride = 1; + break; + + default: + info.x.dim = 0; + info.y.dim = 1; + info.color.dim = 2; + info.x.stride = 1; + info.y.stride = info.dims[0]; + info.color.stride = info.dims[0]*info.dims[1]; + break; + } + + info.x.size = info.dims[info.x.dim]; + info.y.size = info.dims[info.y.dim]; + info.color.size = info.dims[info.color.dim]; + } + + return info; +} + +void NTNDArrayConverter::toArray (NDArray *dest) +{ + toValue(dest); + toDimensions(dest); + toTimeStamp(dest); + toDataTimeStamp(dest); + toAttributes(dest); + + // getUniqueId not implemented yet + // dest->uniqueId = m_array->getUniqueId()->get(); + PVIntPtr uniqueId(m_array->getPVStructure()->getSubField("uniqueId")); + dest->uniqueId = uniqueId->get(); +} + +void NTNDArrayConverter::fromArray (NDArray *src) +{ + fromValue(src); + fromDimensions(src); + fromTimeStamp(src); + fromDataTimeStamp(src); + fromAttributes(src); + + // getUniqueId not implemented yet + // m_array->getUniqueId()->put(src->uniqueId); + PVIntPtr uniqueId(m_array->getPVStructure()->getSubField("uniqueId")); + uniqueId->put(src->uniqueId); +} + +template +void NTNDArrayConverter::toValue (NDArray *dest) +{ + typedef typename arrayType::value_type arrayValType; + typedef typename arrayType::const_svector arrayVecType; + + PVUnionPtr src(m_array->getValue()); + arrayVecType srcVec(src->get()->view()); + memcpy(dest->pData, srcVec.data(), srcVec.size()*sizeof(arrayValType)); + + NTNDArrayInfo_t info = getInfo(); + dest->codec.name = info.codec; + dest->dataType = info.dataType; + + if (!info.codec.empty()) + dest->compressedSize = srcVec.size()*sizeof(arrayValType); +} + +void NTNDArrayConverter::toValue (NDArray *dest) +{ + switch(getValueType()) + { + case pvByte: toValue (dest); break; + case pvUByte: toValue (dest); break; + case pvShort: toValue (dest); break; + case pvUShort: toValue(dest); break; + case pvInt: toValue (dest); break; + case pvUInt: toValue (dest); break; + case pvFloat: toValue (dest); break; + case pvDouble: toValue(dest); break; + case pvBoolean: + case pvLong: + case pvULong: + case pvString: + default: + throw std::runtime_error("invalid value data type"); + break; + } + +} + +void NTNDArrayConverter::toDimensions (NDArray *dest) +{ + PVStructureArrayPtr src(m_array->getDimension()); + PVStructureArray::const_svector srcVec(src->view()); + + dest->ndims = (int)srcVec.size(); + + for(size_t i = 0; i < srcVec.size(); ++i) + { + NDDimension_t *d = &dest->dims[i]; + d->size = srcVec[i]->getSubField("size")->get(); + d->offset = srcVec[i]->getSubField("offset")->get(); + d->binning = srcVec[i]->getSubField("binning")->get(); + d->reverse = srcVec[i]->getSubField("reverse")->get(); + } +} + +void NTNDArrayConverter::toTimeStamp (NDArray *dest) +{ + PVStructurePtr src(m_array->getTimeStamp()); + + if(!src.get()) + return; + + PVTimeStamp pvSrc; + pvSrc.attach(src); + + TimeStamp ts; + pvSrc.get(ts); + + // NDArray uses EPICS time, pvAccess uses Posix time, need to convert + dest->epicsTS.secPastEpoch = (epicsUInt32)ts.getSecondsPastEpoch() - POSIX_TIME_AT_EPICS_EPOCH; + dest->epicsTS.nsec = ts.getNanoseconds(); +} + +void NTNDArrayConverter::toDataTimeStamp (NDArray *dest) +{ + PVStructurePtr src(m_array->getDataTimeStamp()); + PVTimeStamp pvSrc; + pvSrc.attach(src); + + TimeStamp ts; + pvSrc.get(ts); + + // NDArray uses EPICS time, pvAccess uses Posix time, need to convert + dest->timeStamp = ts.toSeconds() - POSIX_TIME_AT_EPICS_EPOCH; +} + +template +void NTNDArrayConverter::toAttribute (NDArray *dest, PVStructurePtr src) +{ + const char *name = src->getSubField("name")->get().c_str(); + const char *desc = src->getSubField("descriptor")->get().c_str(); + NDAttrSource_t sourceType = (NDAttrSource_t)src->getSubField("sourceType")->get(); + const char *source = src->getSubField("source")->get().c_str(); + NDAttrDataType_t dataType = scalarToNDAttrDataType[pvAttrType::typeCode]; + valueType value = src->getSubField("value")->get()->get(); + + NDAttribute *attr = new NDAttribute(name, desc, sourceType, source, dataType, (void*)&value); + dest->pAttributeList->add(attr); +} + +void NTNDArrayConverter::toStringAttribute (NDArray *dest, PVStructurePtr src) +{ + const char *name = src->getSubField("name")->get().c_str(); + const char *desc = src->getSubField("descriptor")->get().c_str(); + NDAttrSource_t sourceType = (NDAttrSource_t)src->getSubField("sourceType")->get(); + const char *source = src->getSubField("source")->get().c_str(); + const char *value = src->getSubField("value")->get()->get().c_str(); + + NDAttribute *attr = new NDAttribute(name, desc, sourceType, source, NDAttrString, (void*)value); + dest->pAttributeList->add(attr); +} + +void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, PVStructurePtr src) +{ + const char *name = src->getSubField("name")->get().c_str(); + const char *desc = src->getSubField("descriptor")->get().c_str(); + NDAttrSource_t sourceType = (NDAttrSource_t)src->getSubField("sourceType")->get(); + const char *source = src->getSubField("source")->get().c_str(); + + NDAttribute *attr = new NDAttribute(name, desc, sourceType, source, NDAttrUndefined, NULL); + dest->pAttributeList->add(attr); +} + +void NTNDArrayConverter::toAttributes (NDArray *dest) +{ + typedef PVStructureArray::const_svector::const_iterator VecIt; + + PVStructureArray::const_svector srcVec(m_array->getAttribute()->view()); + + for(VecIt it = srcVec.cbegin(); it != srcVec.cend(); ++it) + { + PVScalarPtr srcScalar((*it)->getSubField("value")->get()); + + if(!srcScalar) + toUndefinedAttribute(dest, *it); + else + { + switch(srcScalar->getScalar()->getScalarType()) + { + case pvByte: toAttribute (dest, *it); break; + case pvUByte: toAttribute (dest, *it); break; + case pvShort: toAttribute (dest, *it); break; + case pvUShort: toAttribute(dest, *it); break; + case pvInt: toAttribute (dest, *it); break; + case pvUInt: toAttribute(dest, *it); break; + case pvLong: toAttribute (dest, *it); break; + case pvULong: toAttribute(dest, *it); break; + case pvFloat: toAttribute (dest, *it); break; + case pvDouble: toAttribute (dest, *it); break; + case pvString: toStringAttribute (dest, *it); break; + case pvBoolean: + default: + break; // ignore invalid types + } + } + } +} + +template +void NTNDArrayConverter::fromValue (NDArray *src) +{ + typedef typename arrayType::value_type arrayValType; + + string unionField(string(ScalarTypeFunc::name(arrayType::typeCode)) + + string("Value")); + + NDArrayInfo_t arrayInfo; + src->getInfo(&arrayInfo); + + int64 compressedSize = src->compressedSize; + int64 uncompressedSize = arrayInfo.totalBytes; + + m_array->getCompressedDataSize()->put(compressedSize); + m_array->getUncompressedDataSize()->put(uncompressedSize); + + // The uncompressed data type would be lost when converting to NTNDArray, + // so we must store it somewhere. codec.parameters seems like a good place. + PVScalarPtr uncompressedType(PVDC->createPVScalar(pvInt)); + uncompressedType->putFrom(NDDataTypeToScalar[src->dataType]); + + PVStructurePtr codec(m_array->getCodec()); + codec->getSubField("parameters")->set(uncompressedType); + codec->getSubField("name")->put(src->codec.name); + + size_t count = src->codec.empty() ? arrayInfo.nElements : (size_t)compressedSize; + + src->reserve(); + shared_vector temp((srcDataType*)src->pData, + freeNDArray(src), 0, count); + + PVUnionPtr dest = m_array->getValue(); + dest->select(unionField)->replace(freeze(temp)); + dest->postPut(); +} + +void NTNDArrayConverter::fromValue (NDArray *src) +{ + // Uncompressed + if (src->codec.empty()) { + switch(src->dataType) + { + case NDInt8: fromValue (src); break; + case NDUInt8: fromValue (src); break; + case NDInt16: fromValue (src); break; + case NDUInt16: fromValue (src); break; + case NDInt32: fromValue (src); break; + case NDUInt32: fromValue (src); break; + case NDInt64: fromValue (src); break; + case NDUInt64: fromValue (src); break; + case NDFloat32: fromValue (src); break; + case NDFloat64: fromValue (src); break; + } + // Compressed + } else { + fromValue(src); + } +} + +void NTNDArrayConverter::fromDimensions (NDArray *src) +{ + PVStructureArrayPtr dest(m_array->getDimension()); + PVStructureArray::svector destVec(dest->reuse()); + StructureConstPtr dimStructure(dest->getStructureArray()->getStructure()); + + destVec.resize(src->ndims); + for (int i = 0; i < src->ndims; i++) + { + if (!destVec[i] || !destVec[i].unique()) + destVec[i] = PVDC->createPVStructure(dimStructure); + + destVec[i]->getSubField("size")->put((int)src->dims[i].size); + destVec[i]->getSubField("offset")->put((int)src->dims[i].offset); + destVec[i]->getSubField("fullSize")->put((int)src->dims[i].size); + destVec[i]->getSubField("binning")->put(src->dims[i].binning); + destVec[i]->getSubField("reverse")->put(src->dims[i].reverse); + } + dest->replace(freeze(destVec)); +} + +void NTNDArrayConverter::fromDataTimeStamp (NDArray *src) +{ + PVStructurePtr dest(m_array->getDataTimeStamp()); + + double seconds = floor(src->timeStamp); + double nanoseconds = (src->timeStamp - seconds)*1e9; + // pvAccess uses Posix time, NDArray uses EPICS time, need to convert + seconds += POSIX_TIME_AT_EPICS_EPOCH; + + PVTimeStamp pvDest; + pvDest.attach(dest); + + TimeStamp ts((int64_t)seconds, (int32_t)nanoseconds); + pvDest.set(ts); +} + +void NTNDArrayConverter::fromTimeStamp (NDArray *src) +{ + PVStructurePtr dest(m_array->getTimeStamp()); + + PVTimeStamp pvDest; + pvDest.attach(dest); + + // pvAccess uses Posix time, NDArray uses EPICS time, need to convert + TimeStamp ts(src->epicsTS.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH, src->epicsTS.nsec); + pvDest.set(ts); +} + +template +void NTNDArrayConverter::fromAttribute (PVStructurePtr dest, NDAttribute *src) +{ + valueType value; + src->getValue(src->getDataType(), (void*)&value); + + PVUnionPtr destUnion(dest->getSubFieldT("value")); + typename pvAttrType::shared_pointer valueFld(destUnion->get()); + if(!valueFld) { + valueFld = PVDC->createPVScalar(); + destUnion->set(valueFld); + } + valueFld->put(value); +} + +void NTNDArrayConverter::fromStringAttribute (PVStructurePtr dest, NDAttribute *src) +{ + NDAttrDataType_t attrDataType; + size_t attrDataSize; + + src->getValueInfo(&attrDataType, &attrDataSize); + std::vector value(attrDataSize); + src->getValue(attrDataType, &value[0], attrDataSize); + + PVUnionPtr destUnion(dest->getSubFieldT("value")); + PVStringPtr valueFld(destUnion->get()); + if(!valueFld) { + valueFld = PVDC->createPVScalar(); + destUnion->set(valueFld); + } + valueFld->put(&value[0]); +} + +void NTNDArrayConverter::fromUndefinedAttribute (PVStructurePtr dest) +{ + PVFieldPtr nullPtr; + dest->getSubField("value")->set(nullPtr); +} + +void NTNDArrayConverter::fromAttributes (NDArray *src) +{ + PVStructureArrayPtr dest(m_array->getAttribute()); + NDAttributeList *srcList = src->pAttributeList; + NDAttribute *attr = NULL; + StructureConstPtr structure(dest->getStructureArray()->getStructure()); + PVStructureArray::svector destVec(dest->reuse()); + + destVec.resize(srcList->count()); + + size_t i = 0; + while((attr = srcList->next(attr))) + { + if(!destVec[i].get() || !destVec[i].unique()) + destVec[i] = PVDC->createPVStructure(structure); + + PVStructurePtr pvAttr(destVec[i]); + + pvAttr->getSubField("name")->put(attr->getName()); + pvAttr->getSubField("descriptor")->put(attr->getDescription()); + pvAttr->getSubField("source")->put(attr->getSource()); + + NDAttrSource_t sourceType; + attr->getSourceInfo(&sourceType); + pvAttr->getSubField("sourceType")->put(sourceType); + + switch(attr->getDataType()) + { + case NDAttrInt8: fromAttribute (pvAttr, attr); break; + case NDAttrUInt8: fromAttribute (pvAttr, attr); break; + case NDAttrInt16: fromAttribute (pvAttr, attr); break; + case NDAttrUInt16: fromAttribute (pvAttr, attr); break; + case NDAttrInt32: fromAttribute (pvAttr, attr); break; + case NDAttrUInt32: fromAttribute (pvAttr, attr); break; + case NDAttrInt64: fromAttribute (pvAttr, attr); break; + case NDAttrUInt64: fromAttribute (pvAttr, attr); break; + case NDAttrFloat32: fromAttribute (pvAttr, attr); break; + case NDAttrFloat64: fromAttribute (pvAttr, attr); break; + case NDAttrString: fromStringAttribute(pvAttr, attr); break; + case NDAttrUndefined: fromUndefinedAttribute(pvAttr); break; + default: throw std::runtime_error("invalid attribute data type"); + } + + ++i; + } + + dest->replace(freeze(destVec)); +} + + + + diff --git a/ADApp/pluginSrc/Makefile b/ADApp/pluginSrc/Makefile index a6d95710e..a00a81d00 100644 --- a/ADApp/pluginSrc/Makefile +++ b/ADApp/pluginSrc/Makefile @@ -196,6 +196,12 @@ ifeq ($(WITH_PVA), YES) LIB_SRCS += NDPluginPva.cpp endif +ifeq ($(WITH_PVXS), YES) + DBD += NDPluginPvxs.dbd + INC += NDPluginPvxs.h + LIB_SRCS += NDPluginPvxs.cpp +endif + ifeq ($(WITH_BLOSC), YES) USR_CXXFLAGS += -DHAVE_BLOSC endif diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp new file mode 100644 index 000000000..0377cd810 --- /dev/null +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -0,0 +1,228 @@ +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include "NDPluginPva.h" + +#include + +static const char *driverName="NDPluginPva"; + +using namespace epics; +using namespace epics::pvData; +using namespace epics::pvAccess; +using namespace epics::pvDatabase; +using namespace epics::nt; +using namespace std; + +class NDPLUGIN_API NTNDArrayRecord : + public PVRecord +{ + +private: + NTNDArrayRecord(string const & name, PVStructurePtr const & pvStructure) + :PVRecord(name, pvStructure) {} + + NTNDArrayPtr m_ntndArray; + NTNDArrayConverterPtr m_converter; + +public: + POINTER_DEFINITIONS(NTNDArrayRecord); + + virtual ~NTNDArrayRecord () {} + static NTNDArrayRecordPtr create (string const & name); + virtual bool init (); + virtual void process () {} + void update (NDArray *pArray); +}; + +NTNDArrayRecordPtr NTNDArrayRecord::create (string const & name) +{ + NTNDArrayBuilderPtr builder = NTNDArray::createBuilder(); + builder->addDescriptor()->addTimeStamp()->addAlarm()->addDisplay(); + + NTNDArrayRecordPtr pvRecord(new NTNDArrayRecord(name, + builder->createPVStructure())); + + if(!pvRecord->init()) + pvRecord.reset(); + + return pvRecord; +} + +bool NTNDArrayRecord::init () +{ + initPVRecord(); + m_ntndArray = NTNDArray::wrap(getPVStructure()); + m_converter.reset(new NTNDArrayConverter(m_ntndArray)); + return true; +} + +void NTNDArrayRecord::update(NDArray *pArray) +{ + lock(); + + try + { + beginGroupPut(); + m_converter->fromArray(pArray); + endGroupPut(); + } + catch(...) + { + endGroupPut(); + unlock(); + throw; + } + unlock(); +} + +/** Callback function that is called by the NDArray driver with new NDArray + * data. + * \param[in] pArray The NDArray from the callback. + */ +void NDPluginPva::processCallbacks(NDArray *pArray) +{ + static const char *functionName = "processCallbacks"; + + NDPluginDriver::beginProcessCallbacks(pArray); // Base class method + + // Most plugins can rely on endProcessCallbacks() to check for throttling, but this one cannot + // because the output is not an NDArray but a pvAccess server. Need to check here. + if (throttled(pArray)) { + int droppedOutputArrays; + int arrayCounter; + getIntegerParam(NDPluginDriverDroppedOutputArrays, &droppedOutputArrays); + asynPrint(pasynUserSelf, ASYN_TRACE_WARNING, + "%s::%s maximum byte rate exceeded, dropped array uniqueId=%d\n", + driverName, functionName, pArray->uniqueId); + droppedOutputArrays++; + setIntegerParam(NDPluginDriverDroppedOutputArrays, droppedOutputArrays); + // Since this plugin has done no useful work we also decrement ArrayCounter + getIntegerParam(NDArrayCounter, &arrayCounter); + arrayCounter--; + setIntegerParam(NDArrayCounter, arrayCounter); + } else { + this->unlock(); // Function called with the lock taken + m_record->update(pArray); + this->lock(); // Must return locked + } + + // Do NDArray callbacks. We need to copy the array and get the attributes + NDPluginDriver::endProcessCallbacks(pArray, true, true); + + callParamCallbacks(); +} + +/** Constructor for NDPluginPva + * This plugin cannot block (ASYN_CANBLOCK=0) and is not multi-device (ASYN_MULTIDEVICE=0). + * \param[in] portName The name of the asyn port driver to be created. + * \param[in] queueSize The number of NDArrays that the input queue for this + * plugin can hold when NDPluginDriverBlockingCallbacks=0. + * Larger queues can decrease the number of dropped arrays, at the + * expense of more NDArray buffers being allocated from the + * underlying driver's NDArrayPool. + * \param[in] blockingCallbacks Initial setting for the + * NDPluginDriverBlockingCallbacks flag. 0=callbacks are queued and + * executed by the callback thread; 1 callbacks execute in the + * thread of the driver doing the callbacks. + * \param[in] NDArrayPort Name of asyn port driver for initial source of + * NDArray callbacks. + * \param[in] NDArrayAddr asyn port driver address for initial source of + * NDArray callbacks. + * \param[in] pvName Name of the PV that will be served by the EPICSv4 server. + * \param[in] maxBuffers The maximum number of NDArray buffers that the NDArrayPool for this driver is + * allowed to allocate. Set this to 0 to allow an unlimited number of buffers. + * \param[in] maxMemory The maximum amount of memory that the NDArrayPool for this driver is + * allowed to allocate. Set this to 0 to allow an unlimited amount of memory. + * \param[in] priority The thread priority for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags. + * This value should also be used for any other threads this object creates. + * \param[in] stackSize The stack size for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags. + * This value should also be used for any other threads this object creates. + */ +NDPluginPva::NDPluginPva(const char *portName, int queueSize, + int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, + const char *pvName, int maxBuffers, size_t maxMemory, int priority, int stackSize) + /* Invoke the base class constructor */ + : NDPluginDriver(portName, queueSize, blockingCallbacks, + NDArrayPort, NDArrayAddr, 1, maxBuffers, maxMemory, 0, 0, + 0, 1, priority, stackSize, 1, true), + m_record(NTNDArrayRecord::create(pvName)) +{ + createParam(NDPluginPvaPvNameString, asynParamOctet, &NDPluginPvaPvName); + + if(!m_record.get()) + throw runtime_error("failed to create NTNDArrayRecord"); + + /* Set the plugin type string */ + setStringParam(NDPluginDriverPluginType, "NDPluginPva"); + + /* Set PvName */ + setStringParam(NDPluginPvaPvName, pvName); + + /* Try to connect to the NDArray port */ + connectToArrayPort(); + + PVDatabasePtr master = PVDatabase::getMaster(); + ChannelProviderLocalPtr channelProvider = getChannelProviderLocal(); + + if(!master->addRecord(m_record)) + throw runtime_error("couldn't add record to master database"); +} + +/* Configuration routine. Called directly, or from the iocsh function */ +extern "C" int NDPvaConfigure(const char *portName, int queueSize, + int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, + const char *pvName, int maxBuffers, size_t maxMemory, int priority, int stackSize) +{ + NDPluginPva *pPlugin = new NDPluginPva(portName, queueSize, blockingCallbacks, NDArrayPort, + NDArrayAddr, pvName, maxBuffers, maxMemory, priority, stackSize); + return pPlugin->start(); +} + +/* EPICS iocsh shell commands */ +static const iocshArg initArg0 = { "portName",iocshArgString}; +static const iocshArg initArg1 = { "frame queue size",iocshArgInt}; +static const iocshArg initArg2 = { "blocking callbacks",iocshArgInt}; +static const iocshArg initArg3 = { "NDArrayPort",iocshArgString}; +static const iocshArg initArg4 = { "NDArrayAddr",iocshArgInt}; +static const iocshArg initArg5 = { "pvName",iocshArgString}; +static const iocshArg initArg6 = { "maxBuffers",iocshArgInt}; +static const iocshArg initArg7 = { "maxMemory",iocshArgInt}; +static const iocshArg initArg8 = { "priority",iocshArgInt}; +static const iocshArg initArg9 = { "stack size",iocshArgInt}; +static const iocshArg * const initArgs[] = {&initArg0, + &initArg1, + &initArg2, + &initArg3, + &initArg4, + &initArg5, + &initArg6, + &initArg7, + &initArg8, + &initArg9,}; +static const iocshFuncDef initFuncDef = {"NDPvaConfigure",10,initArgs}; +static void initCallFunc(const iocshArgBuf *args) +{ + NDPvaConfigure(args[0].sval, args[1].ival, args[2].ival, + args[3].sval, args[4].ival, args[5].sval, + args[6].ival, args[7].ival, args[8].ival, + args[9].ival); +} + +extern "C" void NDPvaRegister(void) +{ + iocshRegister(&initFuncDef,initCallFunc); +} + +extern "C" { +epicsExportRegistrar(NDPvaRegister); +} From cda8282440bfbe2d03cc3e11d761808f704e1677 Mon Sep 17 00:00:00 2001 From: James Souter Date: Mon, 28 Apr 2025 08:42:34 +0100 Subject: [PATCH 02/23] update NDPluginPvxs.cpp to use PVXS library --- ADApp/pluginSrc/NDPluginPvxs.cpp | 87 ++++++++++++-------------------- ADApp/pluginSrc/NDPluginPvxs.h | 32 ++++++++++++ 2 files changed, 63 insertions(+), 56 deletions(-) create mode 100644 ADApp/pluginSrc/NDPluginPvxs.h diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp index 0377cd810..e62eb2303 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.cpp +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -1,10 +1,13 @@ #include #include #include +#include -#include -#include -#include +#include +#include +#include +#include +#include #include @@ -16,28 +19,20 @@ static const char *driverName="NDPluginPva"; -using namespace epics; -using namespace epics::pvData; -using namespace epics::pvAccess; -using namespace epics::pvDatabase; -using namespace epics::nt; using namespace std; -class NDPLUGIN_API NTNDArrayRecord : - public PVRecord -{ +class NDPLUGIN_API NTNDArrayRecord { private: - NTNDArrayRecord(string const & name, PVStructurePtr const & pvStructure) - :PVRecord(name, pvStructure) {} - - NTNDArrayPtr m_ntndArray; + NTNDArrayRecord(string const & name, pvxs::Value value) : m_name(name), m_value(value) {}; NTNDArrayConverterPtr m_converter; + pvxs::server::Server m_server; + string m_name; + pvxs::Value m_value; + pvxs::server::SharedPV m_pv; public: - POINTER_DEFINITIONS(NTNDArrayRecord); - - virtual ~NTNDArrayRecord () {} + virtual ~NTNDArrayRecord (); static NTNDArrayRecordPtr create (string const & name); virtual bool init (); virtual void process () {} @@ -46,43 +41,35 @@ class NDPLUGIN_API NTNDArrayRecord : NTNDArrayRecordPtr NTNDArrayRecord::create (string const & name) { - NTNDArrayBuilderPtr builder = NTNDArray::createBuilder(); - builder->addDescriptor()->addTimeStamp()->addAlarm()->addDisplay(); - - NTNDArrayRecordPtr pvRecord(new NTNDArrayRecord(name, - builder->createPVStructure())); - + pvxs::Value value = pvxs::nt::NTNDArray{}.build().create(); + NTNDArrayRecordPtr pvRecord(new NTNDArrayRecord(name, value)); + if(!pvRecord->init()) pvRecord.reset(); - + return pvRecord; } bool NTNDArrayRecord::init () { - initPVRecord(); - m_ntndArray = NTNDArray::wrap(getPVStructure()); - m_converter.reset(new NTNDArrayConverter(m_ntndArray)); + m_pv = pvxs::server::SharedPV(pvxs::server::SharedPV::buildMailbox()); + m_pv.open(m_value); + m_server = pvxs::server::Server::fromEnv(); + m_server.addPV(m_name, m_pv); + m_server.start(); // start is not blocking + m_converter.reset(new NTNDArrayConverter(m_value)); return true; } +NTNDArrayRecord::~NTNDArrayRecord () { + m_server.stop(); +} + + void NTNDArrayRecord::update(NDArray *pArray) { - lock(); - - try - { - beginGroupPut(); - m_converter->fromArray(pArray); - endGroupPut(); - } - catch(...) - { - endGroupPut(); - unlock(); - throw; - } - unlock(); + m_converter->fromArray(pArray); + m_pv.post(m_value); } /** Callback function that is called by the NDArray driver with new NDArray @@ -110,11 +97,8 @@ void NDPluginPva::processCallbacks(NDArray *pArray) getIntegerParam(NDArrayCounter, &arrayCounter); arrayCounter--; setIntegerParam(NDArrayCounter, arrayCounter); - } else { - this->unlock(); // Function called with the lock taken - m_record->update(pArray); - this->lock(); // Must return locked } + m_record->update(pArray); // Do NDArray callbacks. We need to copy the array and get the attributes NDPluginDriver::endProcessCallbacks(pArray, true, true); @@ -159,9 +143,6 @@ NDPluginPva::NDPluginPva(const char *portName, int queueSize, { createParam(NDPluginPvaPvNameString, asynParamOctet, &NDPluginPvaPvName); - if(!m_record.get()) - throw runtime_error("failed to create NTNDArrayRecord"); - /* Set the plugin type string */ setStringParam(NDPluginDriverPluginType, "NDPluginPva"); @@ -170,12 +151,6 @@ NDPluginPva::NDPluginPva(const char *portName, int queueSize, /* Try to connect to the NDArray port */ connectToArrayPort(); - - PVDatabasePtr master = PVDatabase::getMaster(); - ChannelProviderLocalPtr channelProvider = getChannelProviderLocal(); - - if(!master->addRecord(m_record)) - throw runtime_error("couldn't add record to master database"); } /* Configuration routine. Called directly, or from the iocsh function */ diff --git a/ADApp/pluginSrc/NDPluginPvxs.h b/ADApp/pluginSrc/NDPluginPvxs.h new file mode 100644 index 000000000..f97587acc --- /dev/null +++ b/ADApp/pluginSrc/NDPluginPvxs.h @@ -0,0 +1,32 @@ +#ifndef NDPluginPva_H +#define NDPluginPva_H + +#include "NDPluginDriver.h" +#include + +#define NDPluginPvaPvNameString "PV_NAME" + +class NTNDArrayRecord; +typedef std::shared_ptr NTNDArrayRecordPtr; + +/** Converts NDArray callback data into EPICS V4 NTNDArray data and exposes it + * as an EPICS V4 PV */ +class NDPLUGIN_API NDPluginPva : public NDPluginDriver, + public std::enable_shared_from_this +{ +public: + NDPluginPva(const char *portName, int queueSize, int blockingCallbacks, + const char *NDArrayPort, int NDArrayAddr, const char *pvName, + int maxBuffers, size_t maxMemory, int priority, int stackSize); + + /* These methods override the virtual methods in the base class */ + void processCallbacks(NDArray *pArray); + +protected: + int NDPluginPvaPvName; + +private: + NTNDArrayRecordPtr m_record; +}; + +#endif From e889f8c5128697039870d33004ef2ed0f258f823 Mon Sep 17 00:00:00 2001 From: James Souter Date: Mon, 28 Apr 2025 10:24:30 +0100 Subject: [PATCH 03/23] First draft of PVXS array converter --- .../ntndArrayConverterPvxs.cpp | 558 ++++++------------ .../ntndArrayConverterPvxs.h | 66 +++ 2 files changed, 247 insertions(+), 377 deletions(-) create mode 100644 ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index d0673b32a..63b31f79c 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -1,41 +1,8 @@ #include "ntndArrayConverter.h" - +#include +#include +#include using namespace std; -using namespace epics::nt; -using namespace epics::pvData; -using tr1::static_pointer_cast; - -// Maps ScalarType to NDAttrDataType_t -static const NDAttrDataType_t scalarToNDAttrDataType[pvString+1] = { - NDAttrInt8, // 0: pvBoolean (not supported) - NDAttrInt8, // 1: pvByte - NDAttrInt16, // 2: pvShort - NDAttrInt32, // 3: pvInt - NDAttrInt64, // 4: pvLong - NDAttrUInt8, // 5: pvUByte - NDAttrUInt16, // 6: pvUShort - NDAttrUInt32, // 7: pvUInt - NDAttrUInt64, // 8: pvULong - NDAttrFloat32, // 9: pvFloat - NDAttrFloat64, // 10: pvDouble - NDAttrString, // 11: pvString -}; - -// Maps NDDataType to ScalarType -static const ScalarType NDDataTypeToScalar[NDFloat64 + 1] = { - pvByte, // 0: NDInt8 - pvUByte, // 1: NDUInt8 - pvShort, // 2: NDInt16 - pvUShort, // 3: NDUInt16 - pvInt, // 4: NDInt32 - pvUInt, // 5: NDUInt32 - pvLong, // 6: NDInt32 - pvULong, // 7: NDUInt32 - pvFloat, // 8: NDFloat32 - pvDouble, // 9: NDFloat64 -}; - -static const PVDataCreatePtr PVDC = getPVDataCreate(); template struct freeNDArray { @@ -44,45 +11,19 @@ struct freeNDArray { void operator()(dataType *data) { array->release(); } }; -NTNDArrayConverter::NTNDArrayConverter (NTNDArrayPtr array) : m_array(array) {} - -ScalarType NTNDArrayConverter::getValueType (void) -{ - string fieldName(m_array->getValue()->getSelectedFieldName()); - - /* - * Check if union field selected. It happens when the driver is run before - * the producer. There is a monitor update that is sent on the - * initialization of a PVRecord with no real data. - */ - if(fieldName.empty()) - throw std::runtime_error("no union field selected"); - - string typeName(fieldName.substr(0,fieldName.find("Value"))); - return ScalarTypeFunc::getScalarType(typeName); -} +NTNDArrayConverter::NTNDArrayConverter (pvxs::Value value) : m_value(value) {} NDColorMode_t NTNDArrayConverter::getColorMode (void) { - NDColorMode_t colorMode = NDColorModeMono; - PVStructureArray::const_svector attrs(m_array->getAttribute()->view()); - - for(PVStructureArray::const_svector::iterator it(attrs.cbegin()); - it != attrs.cend(); ++it) - { - PVStringPtr nameFld((*it)->getSubFieldT("name")); - if(nameFld->get() == "ColorMode") - { - PVUnionPtr valueUnion((*it)->getSubFieldT("value")); - PVScalar::shared_pointer valueFld(valueUnion->get()); - if(valueFld) { - int cm = valueFld->getAs(); - colorMode = (NDColorMode_t) cm; - } else - throw std::runtime_error("Error accessing attribute ColorMode"); + auto attributes = m_value["attribute"].as>(); + NDColorMode_t colorMode = NDColorMode_t::NDColorModeMono; + for (int i=0; i() == "ColorMode") { + colorMode = (NDColorMode_t) attribute["value"].as(); + break; } } - return colorMode; } @@ -90,50 +31,40 @@ NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) { NTNDArrayInfo_t info = {0}; - PVStructureArray::const_svector dims(m_array->getDimension()->view()); - - info.ndims = (int) dims.size(); + auto dims = m_value["dimension"].as>(); + info.ndims = (int) dims.size(); info.nElements = 1; for(int i = 0; i < info.ndims; ++i) { - info.dims[i] = (size_t) dims[i]->getSubField("size")->get(); + info.dims[i] = dims[i]["size"].as(); info.nElements *= info.dims[i]; } - PVStructurePtr codec(m_array->getCodec()); - - info.codec = codec->getSubField("name")->get(); - - ScalarType dataType; - - if (info.codec.empty()) - dataType = getValueType(); - else { - // Read uncompressed data type - PVIntPtr udt(codec->getSubField("parameters")->get()); - dataType = static_cast(udt->get()); - } + info.codec = m_value["codec.name"].as(); NDDataType_t dt; int bpe; - switch(dataType) - { - case pvByte: dt = NDInt8; bpe = sizeof(epicsInt8); break; - case pvUByte: dt = NDUInt8; bpe = sizeof(epicsUInt8); break; - case pvShort: dt = NDInt16; bpe = sizeof(epicsInt16); break; - case pvUShort: dt = NDUInt16; bpe = sizeof(epicsUInt16); break; - case pvInt: dt = NDInt32; bpe = sizeof(epicsInt32); break; - case pvUInt: dt = NDUInt32; bpe = sizeof(epicsUInt32); break; - case pvLong: dt = NDInt64; bpe = sizeof(epicsInt64); break; - case pvULong: dt = NDUInt64; bpe = sizeof(epicsUInt64); break; - case pvFloat: dt = NDFloat32; bpe = sizeof(epicsFloat32); break; - case pvDouble: dt = NDFloat64; bpe = sizeof(epicsFloat64); break; - case pvBoolean: - case pvString: - default: - throw std::runtime_error("invalid value data type"); - break; + + if (info.codec.empty()) { + // TODO would be nicer as a switch statement + std::string fieldName = m_value["value"].nameOf(m_value["value"]["->"]); + std::string typeName(fieldName.substr(0, fieldName.find("Value"))); + if (typeName == "byte") {dt = NDDataType_t::NDInt8; bpe = sizeof(int8_t);} + else if (typeName == "ubyte") {dt = NDDataType_t::NDUInt8; bpe = sizeof(uint8_t);} + else if (typeName == "short") {dt = NDDataType_t::NDInt16; bpe = sizeof(int16_t);} + else if (typeName == "ushort") {dt = NDDataType_t::NDUInt16; bpe = sizeof(uint16_t);} + else if (typeName == "int") {dt = NDDataType_t::NDInt32; bpe = sizeof(int32_t);} + else if (typeName == "uint") {dt = NDDataType_t::NDUInt32; bpe = sizeof(uint32_t);} + else if (typeName == "long") {dt = NDDataType_t::NDInt64; bpe = sizeof(int64_t);} + else if (typeName == "ulong") {dt = NDDataType_t::NDUInt64; bpe = sizeof(uint64_t);} + else if (typeName == "float") {dt = NDDataType_t::NDFloat32; bpe = sizeof(float_t);} + else if (typeName == "double") {dt = NDDataType_t::NDFloat64; bpe = sizeof(double_t);} + else throw std::runtime_error("invalid value data type"); + // TODO get datatype + } else { + throw std::runtime_error("Have not implemeted parsing from known codec type yet"); + // get datatype from codec.parameters... } info.dataType = dt; @@ -212,10 +143,7 @@ void NTNDArrayConverter::toArray (NDArray *dest) toDataTimeStamp(dest); toAttributes(dest); - // getUniqueId not implemented yet - // dest->uniqueId = m_array->getUniqueId()->get(); - PVIntPtr uniqueId(m_array->getPVStructure()->getSubField("uniqueId")); - dest->uniqueId = uniqueId->get(); + dest->uniqueId = m_value["uniqueId"].as(); } void NTNDArrayConverter::fromArray (NDArray *src) @@ -226,368 +154,244 @@ void NTNDArrayConverter::fromArray (NDArray *src) fromDataTimeStamp(src); fromAttributes(src); - // getUniqueId not implemented yet - // m_array->getUniqueId()->put(src->uniqueId); - PVIntPtr uniqueId(m_array->getPVStructure()->getSubField("uniqueId")); - uniqueId->put(src->uniqueId); + m_value["uniqueId"] = src->uniqueId; } template -void NTNDArrayConverter::toValue (NDArray *dest) +void NTNDArrayConverter::toValue (NDArray *dest, std::string fieldName) { - typedef typename arrayType::value_type arrayValType; - typedef typename arrayType::const_svector arrayVecType; - - PVUnionPtr src(m_array->getValue()); - arrayVecType srcVec(src->get()->view()); - memcpy(dest->pData, srcVec.data(), srcVec.size()*sizeof(arrayValType)); - NTNDArrayInfo_t info = getInfo(); dest->codec.name = info.codec; dest->dataType = info.dataType; + auto value = m_value[fieldName].as>(); + memcpy(dest->pData, value.data(), info.totalBytes); + if (!info.codec.empty()) - dest->compressedSize = srcVec.size()*sizeof(arrayValType); + dest->compressedSize = info.totalBytes; } void NTNDArrayConverter::toValue (NDArray *dest) { - switch(getValueType()) - { - case pvByte: toValue (dest); break; - case pvUByte: toValue (dest); break; - case pvShort: toValue (dest); break; - case pvUShort: toValue(dest); break; - case pvInt: toValue (dest); break; - case pvUInt: toValue (dest); break; - case pvFloat: toValue (dest); break; - case pvDouble: toValue(dest); break; - case pvBoolean: - case pvLong: - case pvULong: - case pvString: - default: - throw std::runtime_error("invalid value data type"); - break; - } - + std::string fieldName = m_value["value"].nameOf(m_value["value"]["->"]); + std::string typeName(fieldName.substr(0, fieldName.find("Value"))); + if (typeName == "byte") {toValue(dest, std::string("value->byteValue"));} + else if (typeName == "ubyte") {toValue(dest, std::string("value->ubyteValue"));} + else if (typeName == "short") {toValue(dest, std::string("value->shortValue"));} + else if (typeName == "ushort") {toValue(dest, std::string("value->ushortValue"));} + else if (typeName == "int") {toValue(dest, std::string("value->intValue"));} + else if (typeName == "uint") {toValue(dest, std::string("value->uintValue"));} + else if (typeName == "long") {toValue(dest, std::string("value->longValue"));} + else if (typeName == "ulong") {toValue(dest, std::string("value->ulongValue"));} + else if (typeName == "float") {toValue(dest, std::string("value->floatValue"));} + else if (typeName == "double") {toValue(dest, std::string("value->doubleValue"));} + else throw std::runtime_error("invalid value data type"); } void NTNDArrayConverter::toDimensions (NDArray *dest) { - PVStructureArrayPtr src(m_array->getDimension()); - PVStructureArray::const_svector srcVec(src->view()); + auto dims = m_value["dimension"].as>(); + dest->ndims = (int)dims.size(); - dest->ndims = (int)srcVec.size(); - - for(size_t i = 0; i < srcVec.size(); ++i) + for(size_t i = 0; i < dest->ndims; ++i) { NDDimension_t *d = &dest->dims[i]; - d->size = srcVec[i]->getSubField("size")->get(); - d->offset = srcVec[i]->getSubField("offset")->get(); - d->binning = srcVec[i]->getSubField("binning")->get(); - d->reverse = srcVec[i]->getSubField("reverse")->get(); + d->size = dims[i]["size"].as(); + d->offset = dims[i]["offset"].as(); + d->binning = dims[i]["binning"].as(); + d->reverse = dims[i]["reverse"].as(); } } void NTNDArrayConverter::toTimeStamp (NDArray *dest) { - PVStructurePtr src(m_array->getTimeStamp()); - - if(!src.get()) - return; - - PVTimeStamp pvSrc; - pvSrc.attach(src); - - TimeStamp ts; - pvSrc.get(ts); - // NDArray uses EPICS time, pvAccess uses Posix time, need to convert - dest->epicsTS.secPastEpoch = (epicsUInt32)ts.getSecondsPastEpoch() - POSIX_TIME_AT_EPICS_EPOCH; - dest->epicsTS.nsec = ts.getNanoseconds(); + dest->epicsTS.secPastEpoch = (epicsUInt32) + m_value["timeStamp.secondsPastEpoch"].as() - POSIX_TIME_AT_EPICS_EPOCH; + dest->epicsTS.nsec = (epicsUInt32) + m_value["timeStamp.nanoseconds"].as(); } void NTNDArrayConverter::toDataTimeStamp (NDArray *dest) { - PVStructurePtr src(m_array->getDataTimeStamp()); - PVTimeStamp pvSrc; - pvSrc.attach(src); - - TimeStamp ts; - pvSrc.get(ts); - // NDArray uses EPICS time, pvAccess uses Posix time, need to convert - dest->timeStamp = ts.toSeconds() - POSIX_TIME_AT_EPICS_EPOCH; + dest->timeStamp = (epicsFloat64) + (m_value["dataTimeStamp.nanoseconds"].as() / 1e9) + + m_value["dataTimeStamp.secondsPastEpoch"].as() + - POSIX_TIME_AT_EPICS_EPOCH; } -template -void NTNDArrayConverter::toAttribute (NDArray *dest, PVStructurePtr src) +template +void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute, NDAttrDataType_t dataType) { - const char *name = src->getSubField("name")->get().c_str(); - const char *desc = src->getSubField("descriptor")->get().c_str(); - NDAttrSource_t sourceType = (NDAttrSource_t)src->getSubField("sourceType")->get(); - const char *source = src->getSubField("source")->get().c_str(); - NDAttrDataType_t dataType = scalarToNDAttrDataType[pvAttrType::typeCode]; - valueType value = src->getSubField("value")->get()->get(); - - NDAttribute *attr = new NDAttribute(name, desc, sourceType, source, dataType, (void*)&value); + // TODO, can we make dataType a template parameter? + std::string name = attribute["name"].as(); + std::string desc = attribute["descriptor"].as(); + std::string source = attribute["source"].as(); + NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); + valueType value = attribute["value"].as(); + + NDAttribute *attr = new NDAttribute(name.c_str(), desc.c_str(), sourceType, source.c_str(), dataType, (void*)&value); dest->pAttributeList->add(attr); } -void NTNDArrayConverter::toStringAttribute (NDArray *dest, PVStructurePtr src) +void NTNDArrayConverter::toStringAttribute (NDArray *dest, pvxs::Value attribute) { - const char *name = src->getSubField("name")->get().c_str(); - const char *desc = src->getSubField("descriptor")->get().c_str(); - NDAttrSource_t sourceType = (NDAttrSource_t)src->getSubField("sourceType")->get(); - const char *source = src->getSubField("source")->get().c_str(); - const char *value = src->getSubField("value")->get()->get().c_str(); + std::string name = attribute["name"].as(); + std::string desc = attribute["descriptor"].as(); + std::string source = attribute["source"].as(); + NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); + std::string value = attribute["value"].as(); - NDAttribute *attr = new NDAttribute(name, desc, sourceType, source, NDAttrString, (void*)value); + NDAttribute *attr = new NDAttribute(name.c_str(), desc.c_str(), sourceType, source.c_str(), NDAttrDataType_t::NDAttrString, (void*)value.c_str()); dest->pAttributeList->add(attr); } -void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, PVStructurePtr src) -{ - const char *name = src->getSubField("name")->get().c_str(); - const char *desc = src->getSubField("descriptor")->get().c_str(); - NDAttrSource_t sourceType = (NDAttrSource_t)src->getSubField("sourceType")->get(); - const char *source = src->getSubField("source")->get().c_str(); - - NDAttribute *attr = new NDAttribute(name, desc, sourceType, source, NDAttrUndefined, NULL); - dest->pAttributeList->add(attr); -} +// void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, pvxs::value attribute) +// { +// } void NTNDArrayConverter::toAttributes (NDArray *dest) { - typedef PVStructureArray::const_svector::const_iterator VecIt; - - PVStructureArray::const_svector srcVec(m_array->getAttribute()->view()); - - for(VecIt it = srcVec.cbegin(); it != srcVec.cend(); ++it) - { - PVScalarPtr srcScalar((*it)->getSubField("value")->get()); - - if(!srcScalar) - toUndefinedAttribute(dest, *it); - else - { - switch(srcScalar->getScalar()->getScalarType()) - { - case pvByte: toAttribute (dest, *it); break; - case pvUByte: toAttribute (dest, *it); break; - case pvShort: toAttribute (dest, *it); break; - case pvUShort: toAttribute(dest, *it); break; - case pvInt: toAttribute (dest, *it); break; - case pvUInt: toAttribute(dest, *it); break; - case pvLong: toAttribute (dest, *it); break; - case pvULong: toAttribute(dest, *it); break; - case pvFloat: toAttribute (dest, *it); break; - case pvDouble: toAttribute (dest, *it); break; - case pvString: toStringAttribute (dest, *it); break; - case pvBoolean: - default: - break; // ignore invalid types - } + // TODO, handle undefined attributes? + auto attributes = m_value["attribute"].as>(); + for (int i=0; i"].type().code) { + // use indirection on Any container to get specified type + case pvxs::TypeCode::Int8: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt8); break; + case pvxs::TypeCode::UInt8: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt8); break; + case pvxs::TypeCode::Int16: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt16); break; + case pvxs::TypeCode::UInt16: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt16); break; + case pvxs::TypeCode::Int32: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt32); break; + case pvxs::TypeCode::UInt32: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt32); break; + case pvxs::TypeCode::Int64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt64); break; + case pvxs::TypeCode::UInt64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt64); break; + case pvxs::TypeCode::Float32: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrFloat32); break; + case pvxs::TypeCode::Float64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrFloat64); break; + case pvxs::TypeCode::String: toStringAttribute (dest, attributes[i]); break; + default: throw std::runtime_error("invalid value data type"); } } } -template -void NTNDArrayConverter::fromValue (NDArray *src) -{ - typedef typename arrayType::value_type arrayValType; - - string unionField(string(ScalarTypeFunc::name(arrayType::typeCode)) + - string("Value")); - +void NTNDArrayConverter::fromValue (NDArray *src) { NDArrayInfo_t arrayInfo; src->getInfo(&arrayInfo); - int64 compressedSize = src->compressedSize; - int64 uncompressedSize = arrayInfo.totalBytes; - - m_array->getCompressedDataSize()->put(compressedSize); - m_array->getUncompressedDataSize()->put(uncompressedSize); - + m_value["compressedSize"] = src->compressedSize; + m_value["uncompressedSize"] = arrayInfo.totalBytes; + std::string field_name; + pvxs::ArrayType arrayType; + switch(src->dataType) { + case NDDataType_t::NDInt8: {arrayType = pvxs::ArrayType::Int8; field_name = std::string("value->byteValue"); break;}; + case NDDataType_t::NDUInt8: {arrayType = pvxs::ArrayType::UInt8; field_name = std::string("value->ubyteValue"); break;}; + case NDDataType_t::NDInt16: {arrayType = pvxs::ArrayType::Int16; field_name = std::string("value->shortValue"); break;}; + case NDDataType_t::NDInt32: {arrayType = pvxs::ArrayType::Int32; field_name = std::string("value->intValue"); break;}; + case NDDataType_t::NDUInt32: {arrayType = pvxs::ArrayType::UInt32; field_name = std::string("value->uintValue"); break;}; + case NDDataType_t::NDInt64: {arrayType = pvxs::ArrayType::Int64; field_name = std::string("value->longValue"); break;}; + case NDDataType_t::NDUInt64: {arrayType = pvxs::ArrayType::UInt64; field_name = std::string("value->ulongValue"); break;}; + case NDDataType_t::NDFloat32: {arrayType = pvxs::ArrayType::Float32; field_name = std::string("value->floatValue"); break;}; + case NDDataType_t::NDFloat64: {arrayType = pvxs::ArrayType::Float64; field_name = std::string("value->doubleValue"); break;}; + default: { + throw std::runtime_error("invalid value data type"); + break; + } + } + const auto val = pvxs::detail::copyAs( + arrayType, arrayType, (const void*) src->pData, arrayInfo.nElements).freeze(); + m_value[field_name] = val; + m_value["codec.name"] = src->codec.name; // compression codec // The uncompressed data type would be lost when converting to NTNDArray, // so we must store it somewhere. codec.parameters seems like a good place. - PVScalarPtr uncompressedType(PVDC->createPVScalar(pvInt)); - uncompressedType->putFrom(NDDataTypeToScalar[src->dataType]); - - PVStructurePtr codec(m_array->getCodec()); - codec->getSubField("parameters")->set(uncompressedType); - codec->getSubField("name")->put(src->codec.name); - - size_t count = src->codec.empty() ? arrayInfo.nElements : (size_t)compressedSize; - - src->reserve(); - shared_vector temp((srcDataType*)src->pData, - freeNDArray(src), 0, count); - - PVUnionPtr dest = m_array->getValue(); - dest->select(unionField)->replace(freeze(temp)); - dest->postPut(); + m_value["codec.parameters"] = (int32_t) src->dataType; } -void NTNDArrayConverter::fromValue (NDArray *src) -{ - // Uncompressed - if (src->codec.empty()) { - switch(src->dataType) - { - case NDInt8: fromValue (src); break; - case NDUInt8: fromValue (src); break; - case NDInt16: fromValue (src); break; - case NDUInt16: fromValue (src); break; - case NDInt32: fromValue (src); break; - case NDUInt32: fromValue (src); break; - case NDInt64: fromValue (src); break; - case NDUInt64: fromValue (src); break; - case NDFloat32: fromValue (src); break; - case NDFloat64: fromValue (src); break; - } - // Compressed - } else { - fromValue(src); +void NTNDArrayConverter::fromDimensions (NDArray *src) { + pvxs::shared_array dims; + dims.resize(src->ndims); + + for (int i = 0; i < src->ndims; i++) { + dims[i] = m_value["dimension"].allocMember(); + dims[i].update("size", src->dims[i].size); + dims[i].update("offset", src->dims[i].offset); + dims[i].update("fullSize", src->dims[i].size); + dims[i].update("binning", src->dims[i].binning); + dims[i].update("reverse", src->dims[i].reverse); } + m_value["dimension"] = dims.freeze(); } -void NTNDArrayConverter::fromDimensions (NDArray *src) -{ - PVStructureArrayPtr dest(m_array->getDimension()); - PVStructureArray::svector destVec(dest->reuse()); - StructureConstPtr dimStructure(dest->getStructureArray()->getStructure()); - - destVec.resize(src->ndims); - for (int i = 0; i < src->ndims; i++) - { - if (!destVec[i] || !destVec[i].unique()) - destVec[i] = PVDC->createPVStructure(dimStructure); - - destVec[i]->getSubField("size")->put((int)src->dims[i].size); - destVec[i]->getSubField("offset")->put((int)src->dims[i].offset); - destVec[i]->getSubField("fullSize")->put((int)src->dims[i].size); - destVec[i]->getSubField("binning")->put(src->dims[i].binning); - destVec[i]->getSubField("reverse")->put(src->dims[i].reverse); - } - dest->replace(freeze(destVec)); -} - -void NTNDArrayConverter::fromDataTimeStamp (NDArray *src) -{ - PVStructurePtr dest(m_array->getDataTimeStamp()); - +void NTNDArrayConverter::fromDataTimeStamp (NDArray *src) { double seconds = floor(src->timeStamp); double nanoseconds = (src->timeStamp - seconds)*1e9; // pvAccess uses Posix time, NDArray uses EPICS time, need to convert seconds += POSIX_TIME_AT_EPICS_EPOCH; - - PVTimeStamp pvDest; - pvDest.attach(dest); - - TimeStamp ts((int64_t)seconds, (int32_t)nanoseconds); - pvDest.set(ts); + m_value["dataTimeStamp.secondsPastEpoch"] = seconds; + m_value["dataTimeStamp.nanoseconds"] = nanoseconds; } -void NTNDArrayConverter::fromTimeStamp (NDArray *src) -{ - PVStructurePtr dest(m_array->getTimeStamp()); - - PVTimeStamp pvDest; - pvDest.attach(dest); - +void NTNDArrayConverter::fromTimeStamp (NDArray *src) { // pvAccess uses Posix time, NDArray uses EPICS time, need to convert - TimeStamp ts(src->epicsTS.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH, src->epicsTS.nsec); - pvDest.set(ts); + m_value["timeStamp.secondsPastEpoch"] = src->epicsTS.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH; + m_value["timeStamp.nanoseconds"] = src->epicsTS.nsec; } -template -void NTNDArrayConverter::fromAttribute (PVStructurePtr dest, NDAttribute *src) +template +void NTNDArrayConverter::fromAttribute (pvxs::Value dest_value, NDAttribute *src) { valueType value; src->getValue(src->getDataType(), (void*)&value); - - PVUnionPtr destUnion(dest->getSubFieldT("value")); - typename pvAttrType::shared_pointer valueFld(destUnion->get()); - if(!valueFld) { - valueFld = PVDC->createPVScalar(); - destUnion->set(valueFld); - } - valueFld->put(value); + dest_value["value"] = value; } -void NTNDArrayConverter::fromStringAttribute (PVStructurePtr dest, NDAttribute *src) +void NTNDArrayConverter::fromStringAttribute (pvxs::Value dest_value, NDAttribute *src) { - NDAttrDataType_t attrDataType; - size_t attrDataSize; - - src->getValueInfo(&attrDataType, &attrDataSize); - std::vector value(attrDataSize); - src->getValue(attrDataType, &value[0], attrDataSize); - - PVUnionPtr destUnion(dest->getSubFieldT("value")); - PVStringPtr valueFld(destUnion->get()); - if(!valueFld) { - valueFld = PVDC->createPVScalar(); - destUnion->set(valueFld); - } - valueFld->put(&value[0]); + const char *value; + src->getValue(src->getDataType(), (void*)&value); + dest_value["value"] = std::string(value); } -void NTNDArrayConverter::fromUndefinedAttribute (PVStructurePtr dest) -{ - PVFieldPtr nullPtr; - dest->getSubField("value")->set(nullPtr); -} +// void NTNDArrayConverter::fromUndefinedAttribute (PVStructurePtr dest, NDAttribute *src) +// { +// } void NTNDArrayConverter::fromAttributes (NDArray *src) { - PVStructureArrayPtr dest(m_array->getAttribute()); NDAttributeList *srcList = src->pAttributeList; NDAttribute *attr = NULL; - StructureConstPtr structure(dest->getStructureArray()->getStructure()); - PVStructureArray::svector destVec(dest->reuse()); - - destVec.resize(srcList->count()); - size_t i = 0; + pvxs::shared_array attrs; + attrs.resize(src->pAttributeList->count()); while((attr = srcList->next(attr))) { - if(!destVec[i].get() || !destVec[i].unique()) - destVec[i] = PVDC->createPVStructure(structure); - - PVStructurePtr pvAttr(destVec[i]); - - pvAttr->getSubField("name")->put(attr->getName()); - pvAttr->getSubField("descriptor")->put(attr->getDescription()); - pvAttr->getSubField("source")->put(attr->getSource()); - NDAttrSource_t sourceType; attr->getSourceInfo(&sourceType); - pvAttr->getSubField("sourceType")->put(sourceType); + attrs[i] = m_value["attribute"].allocMember(); + attrs[i].update("name", attr->getName()); + attrs[i].update("descriptor", attr->getDescription()); + attrs[i].update("source", attr->getSource()); + attrs[i].update("sourceType", sourceType); switch(attr->getDataType()) { - case NDAttrInt8: fromAttribute (pvAttr, attr); break; - case NDAttrUInt8: fromAttribute (pvAttr, attr); break; - case NDAttrInt16: fromAttribute (pvAttr, attr); break; - case NDAttrUInt16: fromAttribute (pvAttr, attr); break; - case NDAttrInt32: fromAttribute (pvAttr, attr); break; - case NDAttrUInt32: fromAttribute (pvAttr, attr); break; - case NDAttrInt64: fromAttribute (pvAttr, attr); break; - case NDAttrUInt64: fromAttribute (pvAttr, attr); break; - case NDAttrFloat32: fromAttribute (pvAttr, attr); break; - case NDAttrFloat64: fromAttribute (pvAttr, attr); break; - case NDAttrString: fromStringAttribute(pvAttr, attr); break; - case NDAttrUndefined: fromUndefinedAttribute(pvAttr); break; + case NDAttrInt8: fromAttribute(attrs[i], attr); break; + case NDAttrUInt8: fromAttribute(attrs[i], attr); break; + case NDAttrInt16: fromAttribute(attrs[i], attr); break; + case NDAttrUInt16: fromAttribute(attrs[i], attr); break; + case NDAttrInt32: fromAttribute(attrs[i], attr); break; + case NDAttrUInt32: fromAttribute(attrs[i], attr); break; + case NDAttrInt64: fromAttribute(attrs[i], attr); break; + case NDAttrUInt64: fromAttribute(attrs[i], attr); break; + case NDAttrFloat32: fromAttribute(attrs[i], attr); break; + case NDAttrFloat64: fromAttribute(attrs[i], attr); break; + case NDAttrString: fromStringAttribute(attrs[i], attr); break; + // case NDAttrUndefined: fromUndefinedAttribute(attrs[i]); break; default: throw std::runtime_error("invalid attribute data type"); } - ++i; } - - dest->replace(freeze(destVec)); + m_value["attribute"] = attrs.freeze(); } diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h new file mode 100644 index 000000000..35197d4f1 --- /dev/null +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -0,0 +1,66 @@ +#include + +#include +#include +#include +#include + +typedef struct NTNDArrayInfo +{ + int ndims; + size_t dims[ND_ARRAY_MAX_DIMS]; + size_t nElements, totalBytes; + int bytesPerElement; + NDColorMode_t colorMode; + NDDataType_t dataType; + std::string codec; + + struct + { + int dim; + size_t size, stride; + }x, y, color; +}NTNDArrayInfo_t; + +class NTNDARRAYCONVERTER_API NTNDArrayConverter +{ +public: + NTNDArrayConverter(pvxs::Value value); + NTNDArrayInfo_t getInfo (void); + void toArray (NDArray *dest); + void fromArray (NDArray *src); + +private: + pvxs::Value m_value; + NDColorMode_t getColorMode (void); + + template + void toValue (NDArray *dest, std::string fieldName); + void toValue (NDArray *dest); + + void toDimensions (NDArray *dest); + void toTimeStamp (NDArray *dest); + void toDataTimeStamp (NDArray *dest); + + template + void toAttribute (NDArray *dest, pvxs::Value attribute, NDAttrDataType_t dataType); + void toStringAttribute (NDArray *dest, pvxs::Value attribute); + // void toUndefinedAttribute (NDArray *dest, epics::pvData::PVStructurePtr src); + void toAttributes (NDArray *dest); + + // template + // void fromValue (NDArray *src, const char* field_name); + void fromValue (NDArray *src); + + void fromDimensions (NDArray *src); + void fromTimeStamp (NDArray *src); + void fromDataTimeStamp (NDArray *src); + + template + void fromAttribute (pvxs::Value dest_value, NDAttribute *src); + void fromStringAttribute (pvxs::Value dest_value, NDAttribute *src); + // void fromUndefinedAttribute (epics::pvData::PVStructurePtr dest); + void fromAttributes (NDArray *src); +}; + +typedef std::shared_ptr NTNDArrayConverterPtr; From e0b19306a3da33e0d33e04c0a94a5f2aafcd77da Mon Sep 17 00:00:00 2001 From: James Souter Date: Mon, 28 Apr 2025 14:27:40 +0100 Subject: [PATCH 04/23] add toUndefinedAttribute back to PVXS converter --- .../ntndArrayConverterPvxs.cpp | 16 ++++++++++++---- .../ntndArrayConverterPvxs.h | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 63b31f79c..3e465deb6 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -247,13 +247,19 @@ void NTNDArrayConverter::toStringAttribute (NDArray *dest, pvxs::Value attribute dest->pAttributeList->add(attr); } -// void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, pvxs::value attribute) -// { -// } +void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, pvxs::Value attribute) +{ + std::string name = attribute["name"].as(); + std::string desc = attribute["descriptor"].as(); + std::string source = attribute["source"].as(); + NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); + + NDAttribute *attr = new NDAttribute(name.c_str(), desc.c_str(), sourceType, source.c_str(), NDAttrDataType_t::NDAttrUndefined, NULL); + dest->pAttributeList->add(attr); +} void NTNDArrayConverter::toAttributes (NDArray *dest) { - // TODO, handle undefined attributes? auto attributes = m_value["attribute"].as>(); for (int i=0; i (dest, attributes[i], NDAttrDataType_t::NDAttrFloat32); break; case pvxs::TypeCode::Float64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrFloat64); break; case pvxs::TypeCode::String: toStringAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::Null: toUndefinedAttribute (dest, attributes[i]); break; default: throw std::runtime_error("invalid value data type"); } } @@ -352,6 +359,7 @@ void NTNDArrayConverter::fromStringAttribute (pvxs::Value dest_value, NDAttribut dest_value["value"] = std::string(value); } +// TODO reimplement if required // void NTNDArrayConverter::fromUndefinedAttribute (PVStructurePtr dest, NDAttribute *src) // { // } diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index 35197d4f1..ffbcfbb10 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -45,7 +45,7 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverter template void toAttribute (NDArray *dest, pvxs::Value attribute, NDAttrDataType_t dataType); void toStringAttribute (NDArray *dest, pvxs::Value attribute); - // void toUndefinedAttribute (NDArray *dest, epics::pvData::PVStructurePtr src); + void toUndefinedAttribute (NDArray *dest, pvxs::Value attribute); void toAttributes (NDArray *dest); // template From 0525e37937a2dcf33762faf602b6c45a9c369419 Mon Sep 17 00:00:00 2001 From: James Souter Date: Tue, 29 Apr 2025 11:02:55 +0100 Subject: [PATCH 05/23] use auto when using pvxs::Value::as() --- .../ntndArrayConverterPvxs.cpp | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 3e465deb6..dccad04da 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -225,9 +225,9 @@ template void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute, NDAttrDataType_t dataType) { // TODO, can we make dataType a template parameter? - std::string name = attribute["name"].as(); - std::string desc = attribute["descriptor"].as(); - std::string source = attribute["source"].as(); + auto name = attribute["name"].as(); + auto desc = attribute["descriptor"].as(); + auto source = attribute["source"].as(); NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); valueType value = attribute["value"].as(); @@ -237,11 +237,11 @@ void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute, NDAt void NTNDArrayConverter::toStringAttribute (NDArray *dest, pvxs::Value attribute) { - std::string name = attribute["name"].as(); - std::string desc = attribute["descriptor"].as(); - std::string source = attribute["source"].as(); + auto name = attribute["name"].as(); + auto desc = attribute["descriptor"].as(); + auto source = attribute["source"].as(); NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); - std::string value = attribute["value"].as(); + auto value = attribute["value"].as(); NDAttribute *attr = new NDAttribute(name.c_str(), desc.c_str(), sourceType, source.c_str(), NDAttrDataType_t::NDAttrString, (void*)value.c_str()); dest->pAttributeList->add(attr); @@ -249,9 +249,9 @@ void NTNDArrayConverter::toStringAttribute (NDArray *dest, pvxs::Value attribute void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, pvxs::Value attribute) { - std::string name = attribute["name"].as(); - std::string desc = attribute["descriptor"].as(); - std::string source = attribute["source"].as(); + auto name = attribute["name"].as(); + auto desc = attribute["descriptor"].as(); + auto source = attribute["source"].as(); NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); NDAttribute *attr = new NDAttribute(name.c_str(), desc.c_str(), sourceType, source.c_str(), NDAttrDataType_t::NDAttrUndefined, NULL); From 3455ce4ffe5f4a7beab8d0d430d81ffacec6870f Mon Sep 17 00:00:00 2001 From: James Souter Date: Tue, 29 Apr 2025 14:40:48 +0000 Subject: [PATCH 06/23] remove unneeded includes --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h | 1 - ADApp/pluginSrc/NDPluginPvxs.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index ffbcfbb10..189b79764 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -3,7 +3,6 @@ #include #include #include -#include typedef struct NTNDArrayInfo { diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp index e62eb2303..de2b76743 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.cpp +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -3,11 +3,9 @@ #include #include -#include #include #include #include -#include #include From 77961ddb469543fa1a5555253bb70f1bdf50805d Mon Sep 17 00:00:00 2001 From: James Souter Date: Tue, 29 Apr 2025 14:48:21 +0000 Subject: [PATCH 07/23] Handle undefined attributes in pvxs converter fromAttribute --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 7 +------ ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index dccad04da..9a58749fc 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -359,11 +359,6 @@ void NTNDArrayConverter::fromStringAttribute (pvxs::Value dest_value, NDAttribut dest_value["value"] = std::string(value); } -// TODO reimplement if required -// void NTNDArrayConverter::fromUndefinedAttribute (PVStructurePtr dest, NDAttribute *src) -// { -// } - void NTNDArrayConverter::fromAttributes (NDArray *src) { NDAttributeList *srcList = src->pAttributeList; @@ -394,7 +389,7 @@ void NTNDArrayConverter::fromAttributes (NDArray *src) case NDAttrFloat32: fromAttribute(attrs[i], attr); break; case NDAttrFloat64: fromAttribute(attrs[i], attr); break; case NDAttrString: fromStringAttribute(attrs[i], attr); break; - // case NDAttrUndefined: fromUndefinedAttribute(attrs[i]); break; + case NDAttrUndefined: break; // No need to assign value, leave as undefined default: throw std::runtime_error("invalid attribute data type"); } ++i; diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index 189b79764..ea08160cd 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -58,7 +58,6 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverter template void fromAttribute (pvxs::Value dest_value, NDAttribute *src); void fromStringAttribute (pvxs::Value dest_value, NDAttribute *src); - // void fromUndefinedAttribute (epics::pvData::PVStructurePtr dest); void fromAttributes (NDArray *src); }; From 3ac18c83c3f68eeda405af7117c4fabed636aeec Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 30 Apr 2025 07:45:11 +0000 Subject: [PATCH 08/23] use switch statements on value type code in pvxs converter --- .../ntndArrayConverterPvxs.cpp | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 9a58749fc..3259d32f8 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -47,20 +47,19 @@ NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) int bpe; if (info.codec.empty()) { - // TODO would be nicer as a switch statement - std::string fieldName = m_value["value"].nameOf(m_value["value"]["->"]); - std::string typeName(fieldName.substr(0, fieldName.find("Value"))); - if (typeName == "byte") {dt = NDDataType_t::NDInt8; bpe = sizeof(int8_t);} - else if (typeName == "ubyte") {dt = NDDataType_t::NDUInt8; bpe = sizeof(uint8_t);} - else if (typeName == "short") {dt = NDDataType_t::NDInt16; bpe = sizeof(int16_t);} - else if (typeName == "ushort") {dt = NDDataType_t::NDUInt16; bpe = sizeof(uint16_t);} - else if (typeName == "int") {dt = NDDataType_t::NDInt32; bpe = sizeof(int32_t);} - else if (typeName == "uint") {dt = NDDataType_t::NDUInt32; bpe = sizeof(uint32_t);} - else if (typeName == "long") {dt = NDDataType_t::NDInt64; bpe = sizeof(int64_t);} - else if (typeName == "ulong") {dt = NDDataType_t::NDUInt64; bpe = sizeof(uint64_t);} - else if (typeName == "float") {dt = NDDataType_t::NDFloat32; bpe = sizeof(float_t);} - else if (typeName == "double") {dt = NDDataType_t::NDFloat64; bpe = sizeof(double_t);} - else throw std::runtime_error("invalid value data type"); + switch (m_value["value->"].type().code) { + case pvxs::TypeCode::Int8A: {dt = NDDataType_t::NDInt8; bpe = sizeof(int8_t); break;} + case pvxs::TypeCode::UInt8A: {dt = NDDataType_t::NDUInt8; bpe = sizeof(uint8_t); break;} + case pvxs::TypeCode::Int16A: {dt = NDDataType_t::NDInt16; bpe = sizeof(int16_t); break;} + case pvxs::TypeCode::UInt16A: {dt = NDDataType_t::NDUInt16; bpe = sizeof(uint16_t); break;} + case pvxs::TypeCode::Int32A: {dt = NDDataType_t::NDInt32; bpe = sizeof(int32_t); break;} + case pvxs::TypeCode::UInt32A: {dt = NDDataType_t::NDUInt32; bpe = sizeof(uint32_t); break;} + case pvxs::TypeCode::Int64A: {dt = NDDataType_t::NDInt64; bpe = sizeof(int64_t); break;} + case pvxs::TypeCode::UInt64A: {dt = NDDataType_t::NDUInt64; bpe = sizeof(uint64_t); break;} + case pvxs::TypeCode::Float32A: {dt = NDDataType_t::NDFloat32; bpe = sizeof(float_t); break;} + case pvxs::TypeCode::Float64A: {dt = NDDataType_t::NDFloat64; bpe = sizeof(double_t); break;} + default: throw std::runtime_error("invalid value data type"); + } // TODO get datatype } else { throw std::runtime_error("Have not implemeted parsing from known codec type yet"); @@ -173,19 +172,19 @@ void NTNDArrayConverter::toValue (NDArray *dest, std::string fieldName) void NTNDArrayConverter::toValue (NDArray *dest) { - std::string fieldName = m_value["value"].nameOf(m_value["value"]["->"]); - std::string typeName(fieldName.substr(0, fieldName.find("Value"))); - if (typeName == "byte") {toValue(dest, std::string("value->byteValue"));} - else if (typeName == "ubyte") {toValue(dest, std::string("value->ubyteValue"));} - else if (typeName == "short") {toValue(dest, std::string("value->shortValue"));} - else if (typeName == "ushort") {toValue(dest, std::string("value->ushortValue"));} - else if (typeName == "int") {toValue(dest, std::string("value->intValue"));} - else if (typeName == "uint") {toValue(dest, std::string("value->uintValue"));} - else if (typeName == "long") {toValue(dest, std::string("value->longValue"));} - else if (typeName == "ulong") {toValue(dest, std::string("value->ulongValue"));} - else if (typeName == "float") {toValue(dest, std::string("value->floatValue"));} - else if (typeName == "double") {toValue(dest, std::string("value->doubleValue"));} - else throw std::runtime_error("invalid value data type"); + switch (m_value["value->"].type().code) { + case pvxs::TypeCode::Int8A: {toValue(dest, std::string("value->byteValue")); break;} + case pvxs::TypeCode::UInt8A: {toValue(dest, std::string("value->ubyteValue")); break;} + case pvxs::TypeCode::Int16A: {toValue(dest, std::string("value->shortValue")); break;} + case pvxs::TypeCode::UInt16A: {toValue(dest, std::string("value->ushortValue")); break;} + case pvxs::TypeCode::Int32A: {toValue(dest, std::string("value->intValue")); break;} + case pvxs::TypeCode::UInt32A: {toValue(dest, std::string("value->uintValue")); break;} + case pvxs::TypeCode::Int64A: {toValue(dest, std::string("value->longValue")); break;} + case pvxs::TypeCode::UInt64A: {toValue(dest, std::string("value->ulongValue")); break;} + case pvxs::TypeCode::Float32A: {toValue(dest, std::string("value->floatValue")); break;} + case pvxs::TypeCode::Float64A: {toValue(dest, std::string("value->doubleValue")); break;} + default: throw std::runtime_error("invalid value data type"); + } } void NTNDArrayConverter::toDimensions (NDArray *dest) From f5233aca63218932d33bd34358ce57bc8d11bcda Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 30 Apr 2025 08:30:07 +0000 Subject: [PATCH 09/23] Use unordered maps to simplify templated methods in pvxs converter --- .../ntndArrayConverterPvxs.cpp | 152 ++++++++++++------ .../ntndArrayConverterPvxs.h | 14 +- 2 files changed, 109 insertions(+), 57 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 3259d32f8..21c40b825 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -11,7 +11,46 @@ struct freeNDArray { void operator()(dataType *data) { array->release(); } }; -NTNDArrayConverter::NTNDArrayConverter (pvxs::Value value) : m_value(value) {} +NTNDArrayConverter::NTNDArrayConverter (pvxs::Value value) : m_value(value) { + m_typeMap = { + {typeid(int8_t), NDAttrDataType_t::NDAttrInt8}, + {typeid(uint8_t), NDAttrDataType_t::NDAttrUInt8}, + {typeid(int16_t), NDAttrDataType_t::NDAttrInt16}, + {typeid(uint16_t), NDAttrDataType_t::NDAttrUInt16}, + {typeid(int32_t), NDAttrDataType_t::NDAttrInt32}, + {typeid(uint32_t), NDAttrDataType_t::NDAttrUInt32}, + {typeid(int64_t), NDAttrDataType_t::NDAttrInt64}, + {typeid(uint64_t), NDAttrDataType_t::NDAttrUInt64}, + {typeid(float_t), NDAttrDataType_t::NDAttrFloat32}, + {typeid(double_t), NDAttrDataType_t::NDAttrFloat64} + }; + + m_fieldNameMap = { + {typeid(int8_t), "value->byteValue"}, + {typeid(uint8_t), "value->ubyteValue"}, + {typeid(int16_t), "value->shortValue"}, + {typeid(uint16_t), "value->ushortValue"}, + {typeid(int32_t), "value->intValue"}, + {typeid(uint32_t), "value->uintValue"}, + {typeid(int64_t), "value->longValue"}, + {typeid(uint64_t), "value->ulongValue"}, + {typeid(float_t), "value->floatValue"}, + {typeid(double_t), "value->doubleValue"} + }; + + m_arrayTypeMap = { + {typeid(int8_t), pvxs::ArrayType::Int8}, + {typeid(uint8_t), pvxs::ArrayType::UInt8}, + {typeid(int16_t), pvxs::ArrayType::Int16}, + {typeid(int32_t), pvxs::ArrayType::Int32}, + {typeid(uint32_t), pvxs::ArrayType::UInt32}, + {typeid(int64_t), pvxs::ArrayType::Int64}, + {typeid(uint64_t), pvxs::ArrayType::UInt64}, + {typeid(float_t), pvxs::ArrayType::Float32}, + {typeid(double_t), pvxs::ArrayType::Float64} + }; + +} NDColorMode_t NTNDArrayConverter::getColorMode (void) { @@ -48,16 +87,16 @@ NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) if (info.codec.empty()) { switch (m_value["value->"].type().code) { - case pvxs::TypeCode::Int8A: {dt = NDDataType_t::NDInt8; bpe = sizeof(int8_t); break;} - case pvxs::TypeCode::UInt8A: {dt = NDDataType_t::NDUInt8; bpe = sizeof(uint8_t); break;} - case pvxs::TypeCode::Int16A: {dt = NDDataType_t::NDInt16; bpe = sizeof(int16_t); break;} - case pvxs::TypeCode::UInt16A: {dt = NDDataType_t::NDUInt16; bpe = sizeof(uint16_t); break;} - case pvxs::TypeCode::Int32A: {dt = NDDataType_t::NDInt32; bpe = sizeof(int32_t); break;} - case pvxs::TypeCode::UInt32A: {dt = NDDataType_t::NDUInt32; bpe = sizeof(uint32_t); break;} - case pvxs::TypeCode::Int64A: {dt = NDDataType_t::NDInt64; bpe = sizeof(int64_t); break;} - case pvxs::TypeCode::UInt64A: {dt = NDDataType_t::NDUInt64; bpe = sizeof(uint64_t); break;} - case pvxs::TypeCode::Float32A: {dt = NDDataType_t::NDFloat32; bpe = sizeof(float_t); break;} - case pvxs::TypeCode::Float64A: {dt = NDDataType_t::NDFloat64; bpe = sizeof(double_t); break;} + case pvxs::TypeCode::Int8A: {dt = NDInt8; bpe = sizeof(int8_t); break;} + case pvxs::TypeCode::UInt8A: {dt = NDUInt8; bpe = sizeof(uint8_t); break;} + case pvxs::TypeCode::Int16A: {dt = NDInt16; bpe = sizeof(int16_t); break;} + case pvxs::TypeCode::UInt16A: {dt = NDUInt16; bpe = sizeof(uint16_t); break;} + case pvxs::TypeCode::Int32A: {dt = NDInt32; bpe = sizeof(int32_t); break;} + case pvxs::TypeCode::UInt32A: {dt = NDUInt32; bpe = sizeof(uint32_t); break;} + case pvxs::TypeCode::Int64A: {dt = NDInt64; bpe = sizeof(int64_t); break;} + case pvxs::TypeCode::UInt64A: {dt = NDUInt64; bpe = sizeof(uint64_t); break;} + case pvxs::TypeCode::Float32A: {dt = NDFloat32; bpe = sizeof(float_t); break;} + case pvxs::TypeCode::Float64A: {dt = NDFloat64; bpe = sizeof(double_t); break;} default: throw std::runtime_error("invalid value data type"); } // TODO get datatype @@ -157,12 +196,15 @@ void NTNDArrayConverter::fromArray (NDArray *src) } template -void NTNDArrayConverter::toValue (NDArray *dest, std::string fieldName) +void NTNDArrayConverter::toValue (NDArray *dest) { NTNDArrayInfo_t info = getInfo(); dest->codec.name = info.codec; dest->dataType = info.dataType; + + std::string fieldName = m_fieldNameMap[typeid(arrayType)]; + auto value = m_value[fieldName].as>(); memcpy(dest->pData, value.data(), info.totalBytes); @@ -173,16 +215,16 @@ void NTNDArrayConverter::toValue (NDArray *dest, std::string fieldName) void NTNDArrayConverter::toValue (NDArray *dest) { switch (m_value["value->"].type().code) { - case pvxs::TypeCode::Int8A: {toValue(dest, std::string("value->byteValue")); break;} - case pvxs::TypeCode::UInt8A: {toValue(dest, std::string("value->ubyteValue")); break;} - case pvxs::TypeCode::Int16A: {toValue(dest, std::string("value->shortValue")); break;} - case pvxs::TypeCode::UInt16A: {toValue(dest, std::string("value->ushortValue")); break;} - case pvxs::TypeCode::Int32A: {toValue(dest, std::string("value->intValue")); break;} - case pvxs::TypeCode::UInt32A: {toValue(dest, std::string("value->uintValue")); break;} - case pvxs::TypeCode::Int64A: {toValue(dest, std::string("value->longValue")); break;} - case pvxs::TypeCode::UInt64A: {toValue(dest, std::string("value->ulongValue")); break;} - case pvxs::TypeCode::Float32A: {toValue(dest, std::string("value->floatValue")); break;} - case pvxs::TypeCode::Float64A: {toValue(dest, std::string("value->doubleValue")); break;} + case pvxs::TypeCode::Int8A: {toValue(dest); break;} + case pvxs::TypeCode::UInt8A: {toValue(dest); break;} + case pvxs::TypeCode::Int16A: {toValue(dest); break;} + case pvxs::TypeCode::UInt16A: {toValue(dest); break;} + case pvxs::TypeCode::Int32A: {toValue(dest); break;} + case pvxs::TypeCode::UInt32A: {toValue(dest); break;} + case pvxs::TypeCode::Int64A: {toValue(dest); break;} + case pvxs::TypeCode::UInt64A: {toValue(dest); break;} + case pvxs::TypeCode::Float32A: {toValue(dest); break;} + case pvxs::TypeCode::Float64A: {toValue(dest); break;} default: throw std::runtime_error("invalid value data type"); } } @@ -221,14 +263,14 @@ void NTNDArrayConverter::toDataTimeStamp (NDArray *dest) } template -void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute, NDAttrDataType_t dataType) +void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute) { - // TODO, can we make dataType a template parameter? auto name = attribute["name"].as(); auto desc = attribute["descriptor"].as(); auto source = attribute["source"].as(); NDAttrSource_t sourceType = (NDAttrSource_t) attribute["sourceType"].as(); valueType value = attribute["value"].as(); + NDAttrDataType_t dataType = m_typeMap[typeid(valueType)]; NDAttribute *attr = new NDAttribute(name.c_str(), desc.c_str(), sourceType, source.c_str(), dataType, (void*)&value); dest->pAttributeList->add(attr); @@ -264,16 +306,16 @@ void NTNDArrayConverter::toAttributes (NDArray *dest) pvxs::Value value = attributes[i]["value"]; switch (attributes[i]["value->"].type().code) { // use indirection on Any container to get specified type - case pvxs::TypeCode::Int8: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt8); break; - case pvxs::TypeCode::UInt8: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt8); break; - case pvxs::TypeCode::Int16: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt16); break; - case pvxs::TypeCode::UInt16: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt16); break; - case pvxs::TypeCode::Int32: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt32); break; - case pvxs::TypeCode::UInt32: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt32); break; - case pvxs::TypeCode::Int64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrInt64); break; - case pvxs::TypeCode::UInt64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrUInt64); break; - case pvxs::TypeCode::Float32: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrFloat32); break; - case pvxs::TypeCode::Float64: toAttribute (dest, attributes[i], NDAttrDataType_t::NDAttrFloat64); break; + case pvxs::TypeCode::Int8: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::UInt8: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::Int16: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::UInt16: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::Int32: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::UInt32: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::Int64: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::UInt64: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::Float32: toAttribute (dest, attributes[i]); break; + case pvxs::TypeCode::Float64: toAttribute (dest, attributes[i]); break; case pvxs::TypeCode::String: toStringAttribute (dest, attributes[i]); break; case pvxs::TypeCode::Null: toUndefinedAttribute (dest, attributes[i]); break; default: throw std::runtime_error("invalid value data type"); @@ -281,36 +323,40 @@ void NTNDArrayConverter::toAttributes (NDArray *dest) } } -void NTNDArrayConverter::fromValue (NDArray *src) { +template +void NTNDArrayConverter::fromValue(NDArray *src) { NDArrayInfo_t arrayInfo; src->getInfo(&arrayInfo); m_value["compressedSize"] = src->compressedSize; m_value["uncompressedSize"] = arrayInfo.totalBytes; - std::string field_name; - pvxs::ArrayType arrayType; + std::string fieldName = m_fieldNameMap[typeid(dataType)]; + auto arrayType = m_arrayTypeMap[typeid(dataType)]; + const auto val = pvxs::detail::copyAs( + arrayType, arrayType, (const void*) src->pData, arrayInfo.nElements).freeze(); + m_value[fieldName] = val; + m_value["codec.name"] = src->codec.name; // compression codec + // The uncompressed data type would be lost when converting to NTNDArray, + // so we must store it somewhere. codec.parameters seems like a good place. + m_value["codec.parameters"] = (int32_t) src->dataType; +} + +void NTNDArrayConverter::fromValue (NDArray *src) { switch(src->dataType) { - case NDDataType_t::NDInt8: {arrayType = pvxs::ArrayType::Int8; field_name = std::string("value->byteValue"); break;}; - case NDDataType_t::NDUInt8: {arrayType = pvxs::ArrayType::UInt8; field_name = std::string("value->ubyteValue"); break;}; - case NDDataType_t::NDInt16: {arrayType = pvxs::ArrayType::Int16; field_name = std::string("value->shortValue"); break;}; - case NDDataType_t::NDInt32: {arrayType = pvxs::ArrayType::Int32; field_name = std::string("value->intValue"); break;}; - case NDDataType_t::NDUInt32: {arrayType = pvxs::ArrayType::UInt32; field_name = std::string("value->uintValue"); break;}; - case NDDataType_t::NDInt64: {arrayType = pvxs::ArrayType::Int64; field_name = std::string("value->longValue"); break;}; - case NDDataType_t::NDUInt64: {arrayType = pvxs::ArrayType::UInt64; field_name = std::string("value->ulongValue"); break;}; - case NDDataType_t::NDFloat32: {arrayType = pvxs::ArrayType::Float32; field_name = std::string("value->floatValue"); break;}; - case NDDataType_t::NDFloat64: {arrayType = pvxs::ArrayType::Float64; field_name = std::string("value->doubleValue"); break;}; + case NDDataType_t::NDInt8: {fromValue(src); break;}; + case NDDataType_t::NDUInt8: {fromValue(src); break;}; + case NDDataType_t::NDInt16: {fromValue(src); break;}; + case NDDataType_t::NDInt32: {fromValue(src); break;}; + case NDDataType_t::NDUInt32: {fromValue(src); break;}; + case NDDataType_t::NDInt64: {fromValue(src); break;}; + case NDDataType_t::NDUInt64: {fromValue(src); break;}; + case NDDataType_t::NDFloat32: {fromValue(src); break;}; + case NDDataType_t::NDFloat64: {fromValue(src); break;}; default: { throw std::runtime_error("invalid value data type"); break; } } - const auto val = pvxs::detail::copyAs( - arrayType, arrayType, (const void*) src->pData, arrayInfo.nElements).freeze(); - m_value[field_name] = val; - m_value["codec.name"] = src->codec.name; // compression codec - // The uncompressed data type would be lost when converting to NTNDArray, - // so we must store it somewhere. codec.parameters seems like a good place. - m_value["codec.parameters"] = (int32_t) src->dataType; } void NTNDArrayConverter::fromDimensions (NDArray *src) { diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index ea08160cd..8fca0bcb1 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include typedef struct NTNDArrayInfo { @@ -31,10 +34,13 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverter private: pvxs::Value m_value; + std::unordered_map m_typeMap; + std::unordered_map m_fieldNameMap; + std::unordered_map m_arrayTypeMap; NDColorMode_t getColorMode (void); template - void toValue (NDArray *dest, std::string fieldName); + void toValue (NDArray *dest); void toValue (NDArray *dest); void toDimensions (NDArray *dest); @@ -42,13 +48,13 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverter void toDataTimeStamp (NDArray *dest); template - void toAttribute (NDArray *dest, pvxs::Value attribute, NDAttrDataType_t dataType); + void toAttribute (NDArray *dest, pvxs::Value attribute); void toStringAttribute (NDArray *dest, pvxs::Value attribute); void toUndefinedAttribute (NDArray *dest, pvxs::Value attribute); void toAttributes (NDArray *dest); - // template - // void fromValue (NDArray *src, const char* field_name); + template + void fromValue (NDArray *src); void fromValue (NDArray *src); void fromDimensions (NDArray *src); From f93d5784123ffc2e19226ee59fd45a5de224c6b8 Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 30 Apr 2025 14:41:43 +0000 Subject: [PATCH 10/23] get datatype from codec if set in pvxs converter remove unused freeNDArray struct --- .../ntndArrayConverterPvxs.cpp | 64 ++++++++++--------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 21c40b825..86b20d0b7 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -4,13 +4,6 @@ #include using namespace std; -template -struct freeNDArray { - NDArray *array; - freeNDArray(NDArray *array) : array(array) {}; - void operator()(dataType *data) { array->release(); } -}; - NTNDArrayConverter::NTNDArrayConverter (pvxs::Value value) : m_value(value) { m_typeMap = { {typeid(int8_t), NDAttrDataType_t::NDAttrInt8}, @@ -87,22 +80,31 @@ NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) if (info.codec.empty()) { switch (m_value["value->"].type().code) { - case pvxs::TypeCode::Int8A: {dt = NDInt8; bpe = sizeof(int8_t); break;} - case pvxs::TypeCode::UInt8A: {dt = NDUInt8; bpe = sizeof(uint8_t); break;} - case pvxs::TypeCode::Int16A: {dt = NDInt16; bpe = sizeof(int16_t); break;} - case pvxs::TypeCode::UInt16A: {dt = NDUInt16; bpe = sizeof(uint16_t); break;} - case pvxs::TypeCode::Int32A: {dt = NDInt32; bpe = sizeof(int32_t); break;} - case pvxs::TypeCode::UInt32A: {dt = NDUInt32; bpe = sizeof(uint32_t); break;} - case pvxs::TypeCode::Int64A: {dt = NDInt64; bpe = sizeof(int64_t); break;} - case pvxs::TypeCode::UInt64A: {dt = NDUInt64; bpe = sizeof(uint64_t); break;} - case pvxs::TypeCode::Float32A: {dt = NDFloat32; bpe = sizeof(float_t); break;} - case pvxs::TypeCode::Float64A: {dt = NDFloat64; bpe = sizeof(double_t); break;} - default: throw std::runtime_error("invalid value data type"); + case pvxs::TypeCode::Int8A: {dt = NDInt8; break;} + case pvxs::TypeCode::UInt8A: {dt = NDUInt8; break;} + case pvxs::TypeCode::Int16A: {dt = NDInt16; break;} + case pvxs::TypeCode::UInt16A: {dt = NDUInt16; break;} + case pvxs::TypeCode::Int32A: {dt = NDInt32; break;} + case pvxs::TypeCode::UInt32A: {dt = NDUInt32; break;} + case pvxs::TypeCode::Int64A: {dt = NDInt64; break;} + case pvxs::TypeCode::UInt64A: {dt = NDUInt64; break;} + case pvxs::TypeCode::Float32A: {dt = NDFloat32; break;} + case pvxs::TypeCode::Float64A: {dt = NDFloat64; break;} + default: throw std::runtime_error("invalid value data type"); } - // TODO get datatype - } else { - throw std::runtime_error("Have not implemeted parsing from known codec type yet"); - // get datatype from codec.parameters... + } else dt = (NDDataType_t) m_value["codec.parameters"].as(); + switch (dt) { + case NDInt8: {bpe = sizeof(int8_t); break;} + case NDUInt8: {bpe = sizeof(uint8_t); break;} + case NDInt16: {bpe = sizeof(int16_t); break;} + case NDUInt16: {bpe = sizeof(uint16_t); break;} + case NDInt32: {bpe = sizeof(int32_t); break;} + case NDUInt32: {bpe = sizeof(uint32_t); break;} + case NDInt64: {bpe = sizeof(int64_t); break;} + case NDUInt64: {bpe = sizeof(uint64_t); break;} + case NDFloat32: {bpe = sizeof(float_t); break;} + case NDFloat64: {bpe = sizeof(double_t); break;} + default: throw std::runtime_error("Could not determine element size."); } info.dataType = dt; @@ -343,15 +345,15 @@ void NTNDArrayConverter::fromValue(NDArray *src) { void NTNDArrayConverter::fromValue (NDArray *src) { switch(src->dataType) { - case NDDataType_t::NDInt8: {fromValue(src); break;}; - case NDDataType_t::NDUInt8: {fromValue(src); break;}; - case NDDataType_t::NDInt16: {fromValue(src); break;}; - case NDDataType_t::NDInt32: {fromValue(src); break;}; - case NDDataType_t::NDUInt32: {fromValue(src); break;}; - case NDDataType_t::NDInt64: {fromValue(src); break;}; - case NDDataType_t::NDUInt64: {fromValue(src); break;}; - case NDDataType_t::NDFloat32: {fromValue(src); break;}; - case NDDataType_t::NDFloat64: {fromValue(src); break;}; + case NDInt8: {fromValue(src); break;}; + case NDUInt8: {fromValue(src); break;}; + case NDInt16: {fromValue(src); break;}; + case NDInt32: {fromValue(src); break;}; + case NDUInt32: {fromValue(src); break;}; + case NDInt64: {fromValue(src); break;}; + case NDUInt64: {fromValue(src); break;}; + case NDFloat32: {fromValue(src); break;}; + case NDFloat64: {fromValue(src); break;}; default: { throw std::runtime_error("invalid value data type"); break; From f7c02f979a5c46456302e6450498850da586ef1d Mon Sep 17 00:00:00 2001 From: James Souter Date: Thu, 1 May 2025 07:20:01 +0000 Subject: [PATCH 11/23] use c++11 style loop, chain update calls in pvxs converter --- .../ntndArrayConverterPvxs.cpp | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 86b20d0b7..2a14ad1ee 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -49,8 +49,7 @@ NDColorMode_t NTNDArrayConverter::getColorMode (void) { auto attributes = m_value["attribute"].as>(); NDColorMode_t colorMode = NDColorMode_t::NDColorModeMono; - for (int i=0; i() == "ColorMode") { colorMode = (NDColorMode_t) attribute["value"].as(); break; @@ -366,12 +365,12 @@ void NTNDArrayConverter::fromDimensions (NDArray *src) { dims.resize(src->ndims); for (int i = 0; i < src->ndims; i++) { - dims[i] = m_value["dimension"].allocMember(); - dims[i].update("size", src->dims[i].size); - dims[i].update("offset", src->dims[i].offset); - dims[i].update("fullSize", src->dims[i].size); - dims[i].update("binning", src->dims[i].binning); - dims[i].update("reverse", src->dims[i].reverse); + dims[i] = m_value["dimension"].allocMember() + .update("size", src->dims[i].size) + .update("offset", src->dims[i].offset) + .update("fullSize", src->dims[i].size) + .update("binning", src->dims[i].binning) + .update("reverse", src->dims[i].reverse); } m_value["dimension"] = dims.freeze(); } @@ -417,11 +416,11 @@ void NTNDArrayConverter::fromAttributes (NDArray *src) { NDAttrSource_t sourceType; attr->getSourceInfo(&sourceType); - attrs[i] = m_value["attribute"].allocMember(); - attrs[i].update("name", attr->getName()); - attrs[i].update("descriptor", attr->getDescription()); - attrs[i].update("source", attr->getSource()); - attrs[i].update("sourceType", sourceType); + attrs[i] = m_value["attribute"].allocMember() + .update("name", attr->getName()) + .update("descriptor", attr->getDescription()) + .update("source", attr->getSource()) + .update("sourceType", sourceType); switch(attr->getDataType()) { From 13544344ca45ea9ebe9faff557ae5ac5b3add9ca Mon Sep 17 00:00:00 2001 From: James Souter Date: Thu, 1 May 2025 07:53:38 +0000 Subject: [PATCH 12/23] use buildReadonly() in pvxs plugin SharedPV replace some snake_case with camelCase --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 8 ++++---- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h | 4 ++-- ADApp/pluginSrc/NDPluginPvxs.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 2a14ad1ee..1717fe3c5 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -391,18 +391,18 @@ void NTNDArrayConverter::fromTimeStamp (NDArray *src) { } template -void NTNDArrayConverter::fromAttribute (pvxs::Value dest_value, NDAttribute *src) +void NTNDArrayConverter::fromAttribute (pvxs::Value destValue, NDAttribute *src) { valueType value; src->getValue(src->getDataType(), (void*)&value); - dest_value["value"] = value; + destValue["value"] = value; } -void NTNDArrayConverter::fromStringAttribute (pvxs::Value dest_value, NDAttribute *src) +void NTNDArrayConverter::fromStringAttribute (pvxs::Value destValue, NDAttribute *src) { const char *value; src->getValue(src->getDataType(), (void*)&value); - dest_value["value"] = std::string(value); + destValue["value"] = std::string(value); } void NTNDArrayConverter::fromAttributes (NDArray *src) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index 8fca0bcb1..a3daad4f6 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -62,8 +62,8 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverter void fromDataTimeStamp (NDArray *src); template - void fromAttribute (pvxs::Value dest_value, NDAttribute *src); - void fromStringAttribute (pvxs::Value dest_value, NDAttribute *src); + void fromAttribute (pvxs::Value destValue, NDAttribute *src); + void fromStringAttribute (pvxs::Value destValue, NDAttribute *src); void fromAttributes (NDArray *src); }; diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp index de2b76743..f157d3db3 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.cpp +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -50,7 +50,7 @@ NTNDArrayRecordPtr NTNDArrayRecord::create (string const & name) bool NTNDArrayRecord::init () { - m_pv = pvxs::server::SharedPV(pvxs::server::SharedPV::buildMailbox()); + m_pv = pvxs::server::SharedPV(pvxs::server::SharedPV::buildReadonly()); m_pv.open(m_value); m_server = pvxs::server::Server::fromEnv(); m_server.addPV(m_name, m_pv); From 6ce6b25db71ca66976c92f20eda6752b9f127647 Mon Sep 17 00:00:00 2001 From: James Souter Date: Thu, 1 May 2025 08:47:17 +0000 Subject: [PATCH 13/23] use memcpy in fromValue to avoid using pvxs::detail method --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 1717fe3c5..f8c3c95ed 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -331,11 +331,13 @@ void NTNDArrayConverter::fromValue(NDArray *src) { m_value["compressedSize"] = src->compressedSize; m_value["uncompressedSize"] = arrayInfo.totalBytes; + std::string fieldName = m_fieldNameMap[typeid(dataType)]; auto arrayType = m_arrayTypeMap[typeid(dataType)]; - const auto val = pvxs::detail::copyAs( - arrayType, arrayType, (const void*) src->pData, arrayInfo.nElements).freeze(); - m_value[fieldName] = val; + auto val = pvxs::allocArray(arrayType, arrayInfo.nElements); + memcpy(val.data(), src->pData, arrayInfo.totalBytes); + m_value[fieldName] = val.freeze(); + m_value["codec.name"] = src->codec.name; // compression codec // The uncompressed data type would be lost when converting to NTNDArray, // so we must store it somewhere. codec.parameters seems like a good place. From 3c167517308c2104fbe51473924ad26588dbbec8 Mon Sep 17 00:00:00 2001 From: James Souter Date: Fri, 9 May 2025 13:08:56 +0100 Subject: [PATCH 14/23] Rename ntndArrayConverterPvxs to prevent name collision --- .../ntndArrayConverterPvxs.cpp | 46 +++++++++---------- .../ntndArrayConverterPvxs.h | 6 +-- ADApp/pluginSrc/NDPluginPvxs.cpp | 34 +++++++------- ADApp/pluginSrc/NDPluginPvxs.dbd | 1 + ADApp/pluginSrc/NDPluginPvxs.h | 15 +++--- 5 files changed, 52 insertions(+), 50 deletions(-) create mode 100644 ADApp/pluginSrc/NDPluginPvxs.dbd diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index f8c3c95ed..4f5d5f1ef 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -1,10 +1,10 @@ -#include "ntndArrayConverter.h" +#include "ntndArrayConverterPvxs.h" #include #include #include using namespace std; -NTNDArrayConverter::NTNDArrayConverter (pvxs::Value value) : m_value(value) { +NTNDArrayConverterPvxs::NTNDArrayConverterPvxs (pvxs::Value value) : m_value(value) { m_typeMap = { {typeid(int8_t), NDAttrDataType_t::NDAttrInt8}, {typeid(uint8_t), NDAttrDataType_t::NDAttrUInt8}, @@ -45,7 +45,7 @@ NTNDArrayConverter::NTNDArrayConverter (pvxs::Value value) : m_value(value) { } -NDColorMode_t NTNDArrayConverter::getColorMode (void) +NDColorMode_t NTNDArrayConverterPvxs::getColorMode (void) { auto attributes = m_value["attribute"].as>(); NDColorMode_t colorMode = NDColorMode_t::NDColorModeMono; @@ -58,7 +58,7 @@ NDColorMode_t NTNDArrayConverter::getColorMode (void) return colorMode; } -NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) +NTNDArrayInfo_t NTNDArrayConverterPvxs::getInfo (void) { NTNDArrayInfo_t info = {0}; @@ -174,7 +174,7 @@ NTNDArrayInfo_t NTNDArrayConverter::getInfo (void) return info; } -void NTNDArrayConverter::toArray (NDArray *dest) +void NTNDArrayConverterPvxs::toArray (NDArray *dest) { toValue(dest); toDimensions(dest); @@ -185,7 +185,7 @@ void NTNDArrayConverter::toArray (NDArray *dest) dest->uniqueId = m_value["uniqueId"].as(); } -void NTNDArrayConverter::fromArray (NDArray *src) +void NTNDArrayConverterPvxs::fromArray (NDArray *src) { fromValue(src); fromDimensions(src); @@ -197,7 +197,7 @@ void NTNDArrayConverter::fromArray (NDArray *src) } template -void NTNDArrayConverter::toValue (NDArray *dest) +void NTNDArrayConverterPvxs::toValue (NDArray *dest) { NTNDArrayInfo_t info = getInfo(); dest->codec.name = info.codec; @@ -213,7 +213,7 @@ void NTNDArrayConverter::toValue (NDArray *dest) dest->compressedSize = info.totalBytes; } -void NTNDArrayConverter::toValue (NDArray *dest) +void NTNDArrayConverterPvxs::toValue (NDArray *dest) { switch (m_value["value->"].type().code) { case pvxs::TypeCode::Int8A: {toValue(dest); break;} @@ -230,7 +230,7 @@ void NTNDArrayConverter::toValue (NDArray *dest) } } -void NTNDArrayConverter::toDimensions (NDArray *dest) +void NTNDArrayConverterPvxs::toDimensions (NDArray *dest) { auto dims = m_value["dimension"].as>(); dest->ndims = (int)dims.size(); @@ -245,7 +245,7 @@ void NTNDArrayConverter::toDimensions (NDArray *dest) } } -void NTNDArrayConverter::toTimeStamp (NDArray *dest) +void NTNDArrayConverterPvxs::toTimeStamp (NDArray *dest) { // NDArray uses EPICS time, pvAccess uses Posix time, need to convert dest->epicsTS.secPastEpoch = (epicsUInt32) @@ -254,7 +254,7 @@ void NTNDArrayConverter::toTimeStamp (NDArray *dest) m_value["timeStamp.nanoseconds"].as(); } -void NTNDArrayConverter::toDataTimeStamp (NDArray *dest) +void NTNDArrayConverterPvxs::toDataTimeStamp (NDArray *dest) { // NDArray uses EPICS time, pvAccess uses Posix time, need to convert dest->timeStamp = (epicsFloat64) @@ -264,7 +264,7 @@ void NTNDArrayConverter::toDataTimeStamp (NDArray *dest) } template -void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute) +void NTNDArrayConverterPvxs::toAttribute (NDArray *dest, pvxs::Value attribute) { auto name = attribute["name"].as(); auto desc = attribute["descriptor"].as(); @@ -277,7 +277,7 @@ void NTNDArrayConverter::toAttribute (NDArray *dest, pvxs::Value attribute) dest->pAttributeList->add(attr); } -void NTNDArrayConverter::toStringAttribute (NDArray *dest, pvxs::Value attribute) +void NTNDArrayConverterPvxs::toStringAttribute (NDArray *dest, pvxs::Value attribute) { auto name = attribute["name"].as(); auto desc = attribute["descriptor"].as(); @@ -289,7 +289,7 @@ void NTNDArrayConverter::toStringAttribute (NDArray *dest, pvxs::Value attribute dest->pAttributeList->add(attr); } -void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, pvxs::Value attribute) +void NTNDArrayConverterPvxs::toUndefinedAttribute (NDArray *dest, pvxs::Value attribute) { auto name = attribute["name"].as(); auto desc = attribute["descriptor"].as(); @@ -300,7 +300,7 @@ void NTNDArrayConverter::toUndefinedAttribute (NDArray *dest, pvxs::Value attrib dest->pAttributeList->add(attr); } -void NTNDArrayConverter::toAttributes (NDArray *dest) +void NTNDArrayConverterPvxs::toAttributes (NDArray *dest) { auto attributes = m_value["attribute"].as>(); for (int i=0; i -void NTNDArrayConverter::fromValue(NDArray *src) { +void NTNDArrayConverterPvxs::fromValue(NDArray *src) { NDArrayInfo_t arrayInfo; src->getInfo(&arrayInfo); @@ -344,7 +344,7 @@ void NTNDArrayConverter::fromValue(NDArray *src) { m_value["codec.parameters"] = (int32_t) src->dataType; } -void NTNDArrayConverter::fromValue (NDArray *src) { +void NTNDArrayConverterPvxs::fromValue (NDArray *src) { switch(src->dataType) { case NDInt8: {fromValue(src); break;}; case NDUInt8: {fromValue(src); break;}; @@ -362,7 +362,7 @@ void NTNDArrayConverter::fromValue (NDArray *src) { } } -void NTNDArrayConverter::fromDimensions (NDArray *src) { +void NTNDArrayConverterPvxs::fromDimensions (NDArray *src) { pvxs::shared_array dims; dims.resize(src->ndims); @@ -377,7 +377,7 @@ void NTNDArrayConverter::fromDimensions (NDArray *src) { m_value["dimension"] = dims.freeze(); } -void NTNDArrayConverter::fromDataTimeStamp (NDArray *src) { +void NTNDArrayConverterPvxs::fromDataTimeStamp (NDArray *src) { double seconds = floor(src->timeStamp); double nanoseconds = (src->timeStamp - seconds)*1e9; // pvAccess uses Posix time, NDArray uses EPICS time, need to convert @@ -386,28 +386,28 @@ void NTNDArrayConverter::fromDataTimeStamp (NDArray *src) { m_value["dataTimeStamp.nanoseconds"] = nanoseconds; } -void NTNDArrayConverter::fromTimeStamp (NDArray *src) { +void NTNDArrayConverterPvxs::fromTimeStamp (NDArray *src) { // pvAccess uses Posix time, NDArray uses EPICS time, need to convert m_value["timeStamp.secondsPastEpoch"] = src->epicsTS.secPastEpoch + POSIX_TIME_AT_EPICS_EPOCH; m_value["timeStamp.nanoseconds"] = src->epicsTS.nsec; } template -void NTNDArrayConverter::fromAttribute (pvxs::Value destValue, NDAttribute *src) +void NTNDArrayConverterPvxs::fromAttribute (pvxs::Value destValue, NDAttribute *src) { valueType value; src->getValue(src->getDataType(), (void*)&value); destValue["value"] = value; } -void NTNDArrayConverter::fromStringAttribute (pvxs::Value destValue, NDAttribute *src) +void NTNDArrayConverterPvxs::fromStringAttribute (pvxs::Value destValue, NDAttribute *src) { const char *value; src->getValue(src->getDataType(), (void*)&value); destValue["value"] = std::string(value); } -void NTNDArrayConverter::fromAttributes (NDArray *src) +void NTNDArrayConverterPvxs::fromAttributes (NDArray *src) { NDAttributeList *srcList = src->pAttributeList; NDAttribute *attr = NULL; diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index a3daad4f6..688224317 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -24,10 +24,10 @@ typedef struct NTNDArrayInfo }x, y, color; }NTNDArrayInfo_t; -class NTNDARRAYCONVERTER_API NTNDArrayConverter +class NTNDARRAYCONVERTER_API NTNDArrayConverterPvxs { public: - NTNDArrayConverter(pvxs::Value value); + NTNDArrayConverterPvxs(pvxs::Value value); NTNDArrayInfo_t getInfo (void); void toArray (NDArray *dest); void fromArray (NDArray *src); @@ -67,4 +67,4 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverter void fromAttributes (NDArray *src); }; -typedef std::shared_ptr NTNDArrayConverterPtr; +typedef std::shared_ptr NTNDArrayConverterPvxsPtr; diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp index f157d3db3..36346c21f 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.cpp +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -9,13 +9,13 @@ #include -#include +#include -#include "NDPluginPva.h" +#include "NDPluginPvxs.h" #include -static const char *driverName="NDPluginPva"; +static const char *driverName="NDPluginPvxs"; using namespace std; @@ -23,7 +23,7 @@ class NDPLUGIN_API NTNDArrayRecord { private: NTNDArrayRecord(string const & name, pvxs::Value value) : m_name(name), m_value(value) {}; - NTNDArrayConverterPtr m_converter; + NTNDArrayConverterPvxsPtr m_converter; pvxs::server::Server m_server; string m_name; pvxs::Value m_value; @@ -55,7 +55,7 @@ bool NTNDArrayRecord::init () m_server = pvxs::server::Server::fromEnv(); m_server.addPV(m_name, m_pv); m_server.start(); // start is not blocking - m_converter.reset(new NTNDArrayConverter(m_value)); + m_converter.reset(new NTNDArrayConverterPvxs(m_value)); return true; } @@ -74,7 +74,7 @@ void NTNDArrayRecord::update(NDArray *pArray) * data. * \param[in] pArray The NDArray from the callback. */ -void NDPluginPva::processCallbacks(NDArray *pArray) +void NDPluginPvxs::processCallbacks(NDArray *pArray) { static const char *functionName = "processCallbacks"; @@ -104,7 +104,7 @@ void NDPluginPva::processCallbacks(NDArray *pArray) callParamCallbacks(); } -/** Constructor for NDPluginPva +/** Constructor for NDPluginPvxs * This plugin cannot block (ASYN_CANBLOCK=0) and is not multi-device (ASYN_MULTIDEVICE=0). * \param[in] portName The name of the asyn port driver to be created. * \param[in] queueSize The number of NDArrays that the input queue for this @@ -130,7 +130,7 @@ void NDPluginPva::processCallbacks(NDArray *pArray) * \param[in] stackSize The stack size for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags. * This value should also be used for any other threads this object creates. */ -NDPluginPva::NDPluginPva(const char *portName, int queueSize, +NDPluginPvxs::NDPluginPvxs(const char *portName, int queueSize, int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, const char *pvName, int maxBuffers, size_t maxMemory, int priority, int stackSize) /* Invoke the base class constructor */ @@ -139,24 +139,24 @@ NDPluginPva::NDPluginPva(const char *portName, int queueSize, 0, 1, priority, stackSize, 1, true), m_record(NTNDArrayRecord::create(pvName)) { - createParam(NDPluginPvaPvNameString, asynParamOctet, &NDPluginPvaPvName); + createParam(NDPluginPvxsPvNameString, asynParamOctet, &NDPluginPvxsPvName); /* Set the plugin type string */ - setStringParam(NDPluginDriverPluginType, "NDPluginPva"); + setStringParam(NDPluginDriverPluginType, "NDPluginPvxs"); /* Set PvName */ - setStringParam(NDPluginPvaPvName, pvName); + setStringParam(NDPluginPvxsPvName, pvName); /* Try to connect to the NDArray port */ connectToArrayPort(); } /* Configuration routine. Called directly, or from the iocsh function */ -extern "C" int NDPvaConfigure(const char *portName, int queueSize, +extern "C" int NDPvxsConfigure(const char *portName, int queueSize, int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, const char *pvName, int maxBuffers, size_t maxMemory, int priority, int stackSize) { - NDPluginPva *pPlugin = new NDPluginPva(portName, queueSize, blockingCallbacks, NDArrayPort, + NDPluginPvxs *pPlugin = new NDPluginPvxs(portName, queueSize, blockingCallbacks, NDArrayPort, NDArrayAddr, pvName, maxBuffers, maxMemory, priority, stackSize); return pPlugin->start(); } @@ -182,20 +182,20 @@ static const iocshArg * const initArgs[] = {&initArg0, &initArg7, &initArg8, &initArg9,}; -static const iocshFuncDef initFuncDef = {"NDPvaConfigure",10,initArgs}; +static const iocshFuncDef initFuncDef = {"NDPvxsConfigure",10,initArgs}; static void initCallFunc(const iocshArgBuf *args) { - NDPvaConfigure(args[0].sval, args[1].ival, args[2].ival, + NDPvxsConfigure(args[0].sval, args[1].ival, args[2].ival, args[3].sval, args[4].ival, args[5].sval, args[6].ival, args[7].ival, args[8].ival, args[9].ival); } -extern "C" void NDPvaRegister(void) +extern "C" void NDPvxsRegister(void) { iocshRegister(&initFuncDef,initCallFunc); } extern "C" { -epicsExportRegistrar(NDPvaRegister); +epicsExportRegistrar(NDPvxsRegister); } diff --git a/ADApp/pluginSrc/NDPluginPvxs.dbd b/ADApp/pluginSrc/NDPluginPvxs.dbd new file mode 100644 index 000000000..8fe934c1b --- /dev/null +++ b/ADApp/pluginSrc/NDPluginPvxs.dbd @@ -0,0 +1 @@ +registrar("NDPvxsRegister") diff --git a/ADApp/pluginSrc/NDPluginPvxs.h b/ADApp/pluginSrc/NDPluginPvxs.h index f97587acc..3923d4645 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.h +++ b/ADApp/pluginSrc/NDPluginPvxs.h @@ -1,21 +1,22 @@ -#ifndef NDPluginPva_H -#define NDPluginPva_H +#ifndef NDPluginPvxs_H +#define NDPluginPvxs_H + #include "NDPluginDriver.h" #include -#define NDPluginPvaPvNameString "PV_NAME" +#define NDPluginPvxsPvNameString "PV_NAME" class NTNDArrayRecord; typedef std::shared_ptr NTNDArrayRecordPtr; /** Converts NDArray callback data into EPICS V4 NTNDArray data and exposes it * as an EPICS V4 PV */ -class NDPLUGIN_API NDPluginPva : public NDPluginDriver, - public std::enable_shared_from_this +class NDPLUGIN_API NDPluginPvxs : public NDPluginDriver, + public std::enable_shared_from_this { public: - NDPluginPva(const char *portName, int queueSize, int blockingCallbacks, + NDPluginPvxs(const char *portName, int queueSize, int blockingCallbacks, const char *NDArrayPort, int NDArrayAddr, const char *pvName, int maxBuffers, size_t maxMemory, int priority, int stackSize); @@ -23,7 +24,7 @@ class NDPLUGIN_API NDPluginPva : public NDPluginDriver, void processCallbacks(NDArray *pArray); protected: - int NDPluginPvaPvName; + int NDPluginPvxsPvName; private: NTNDArrayRecordPtr m_record; From 1c76cc8e1f812b9870f47d6ef86e43ff440bd2bc Mon Sep 17 00:00:00 2001 From: James Souter Date: Fri, 9 May 2025 13:12:49 +0000 Subject: [PATCH 15/23] linting --- ADApp/pluginSrc/NDPluginPvxs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp index 36346c21f..44e2bec38 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.cpp +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -137,7 +137,7 @@ NDPluginPvxs::NDPluginPvxs(const char *portName, int queueSize, : NDPluginDriver(portName, queueSize, blockingCallbacks, NDArrayPort, NDArrayAddr, 1, maxBuffers, maxMemory, 0, 0, 0, 1, priority, stackSize, 1, true), - m_record(NTNDArrayRecord::create(pvName)) + m_record(NTNDArrayRecord::create(pvName)) { createParam(NDPluginPvxsPvNameString, asynParamOctet, &NDPluginPvxsPvName); From 056382f6047149a81c5c1e1ea3c183ed21bdfeb4 Mon Sep 17 00:00:00 2001 From: James Souter Date: Mon, 19 May 2025 09:03:45 +0000 Subject: [PATCH 16/23] Use PVXS singleton server in NDPluginPvxs --- ADApp/pluginSrc/NDPluginPvxs.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/ADApp/pluginSrc/NDPluginPvxs.cpp b/ADApp/pluginSrc/NDPluginPvxs.cpp index 44e2bec38..d4fed9662 100644 --- a/ADApp/pluginSrc/NDPluginPvxs.cpp +++ b/ADApp/pluginSrc/NDPluginPvxs.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -24,13 +25,12 @@ class NDPLUGIN_API NTNDArrayRecord { private: NTNDArrayRecord(string const & name, pvxs::Value value) : m_name(name), m_value(value) {}; NTNDArrayConverterPvxsPtr m_converter; - pvxs::server::Server m_server; string m_name; pvxs::Value m_value; pvxs::server::SharedPV m_pv; public: - virtual ~NTNDArrayRecord (); + virtual ~NTNDArrayRecord () {}; static NTNDArrayRecordPtr create (string const & name); virtual bool init (); virtual void process () {} @@ -52,18 +52,12 @@ bool NTNDArrayRecord::init () { m_pv = pvxs::server::SharedPV(pvxs::server::SharedPV::buildReadonly()); m_pv.open(m_value); - m_server = pvxs::server::Server::fromEnv(); - m_server.addPV(m_name, m_pv); - m_server.start(); // start is not blocking + // use singleton pvxs server + pvxs::ioc::server().addPV(m_name, m_pv); m_converter.reset(new NTNDArrayConverterPvxs(m_value)); return true; } -NTNDArrayRecord::~NTNDArrayRecord () { - m_server.stop(); -} - - void NTNDArrayRecord::update(NDArray *pArray) { m_converter->fromArray(pArray); From 5af4c34635b845554daddacd85046ff71d621809 Mon Sep 17 00:00:00 2001 From: James Souter Date: Tue, 10 Jun 2025 09:14:14 +0000 Subject: [PATCH 17/23] Don't throw in pvxs converter if codec.name missing --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 4f5d5f1ef..69a1679b7 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -72,7 +72,8 @@ NTNDArrayInfo_t NTNDArrayConverterPvxs::getInfo (void) info.nElements *= info.dims[i]; } - info.codec = m_value["codec.name"].as(); + // does not update info.codec if codec.name not found in Value + m_value["codec.name"].as(info.codec); NDDataType_t dt; int bpe; From 50e3b8613ef6d7a9c6c1c1f745fbd57e52732a41 Mon Sep 17 00:00:00 2001 From: James Souter Date: Tue, 10 Jun 2025 09:45:49 +0000 Subject: [PATCH 18/23] use trivial deletor to avoid memcpy in pvxs converter --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 69a1679b7..28fe0f7fe 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -335,8 +335,11 @@ void NTNDArrayConverterPvxs::fromValue(NDArray *src) { std::string fieldName = m_fieldNameMap[typeid(dataType)]; auto arrayType = m_arrayTypeMap[typeid(dataType)]; - auto val = pvxs::allocArray(arrayType, arrayInfo.nElements); - memcpy(val.data(), src->pData, arrayInfo.totalBytes); + auto val = pvxs::shared_array( + (dataType*)src->pData, + // trivial deletor that does nothing when shared_array goes out of scope + [] (dataType *data) {}, + arrayInfo.nElements); m_value[fieldName] = val.freeze(); m_value["codec.name"] = src->codec.name; // compression codec From 27912b032555baf2fa83096f3bcc8f939036cc12 Mon Sep 17 00:00:00 2001 From: James Souter Date: Tue, 10 Jun 2025 12:14:27 +0000 Subject: [PATCH 19/23] remove now unused array type unordered_map --- .../ntndArrayConverterPvxs.cpp | 14 -------------- .../ntndArrayConverterSrc/ntndArrayConverterPvxs.h | 1 - 2 files changed, 15 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 28fe0f7fe..e499274a2 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -30,19 +30,6 @@ NTNDArrayConverterPvxs::NTNDArrayConverterPvxs (pvxs::Value value) : m_value(val {typeid(float_t), "value->floatValue"}, {typeid(double_t), "value->doubleValue"} }; - - m_arrayTypeMap = { - {typeid(int8_t), pvxs::ArrayType::Int8}, - {typeid(uint8_t), pvxs::ArrayType::UInt8}, - {typeid(int16_t), pvxs::ArrayType::Int16}, - {typeid(int32_t), pvxs::ArrayType::Int32}, - {typeid(uint32_t), pvxs::ArrayType::UInt32}, - {typeid(int64_t), pvxs::ArrayType::Int64}, - {typeid(uint64_t), pvxs::ArrayType::UInt64}, - {typeid(float_t), pvxs::ArrayType::Float32}, - {typeid(double_t), pvxs::ArrayType::Float64} - }; - } NDColorMode_t NTNDArrayConverterPvxs::getColorMode (void) @@ -334,7 +321,6 @@ void NTNDArrayConverterPvxs::fromValue(NDArray *src) { m_value["uncompressedSize"] = arrayInfo.totalBytes; std::string fieldName = m_fieldNameMap[typeid(dataType)]; - auto arrayType = m_arrayTypeMap[typeid(dataType)]; auto val = pvxs::shared_array( (dataType*)src->pData, // trivial deletor that does nothing when shared_array goes out of scope diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h index 688224317..a69dded5a 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.h @@ -36,7 +36,6 @@ class NTNDARRAYCONVERTER_API NTNDArrayConverterPvxs pvxs::Value m_value; std::unordered_map m_typeMap; std::unordered_map m_fieldNameMap; - std::unordered_map m_arrayTypeMap; NDColorMode_t getColorMode (void); template From 2ea85d2fc21c16947e1a615de561bda8f0d800f8 Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 11 Jun 2025 09:28:56 +0000 Subject: [PATCH 20/23] don't throw in fromValue with NDUInt16 --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index e499274a2..c9648b7e1 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -339,6 +339,7 @@ void NTNDArrayConverterPvxs::fromValue (NDArray *src) { case NDInt8: {fromValue(src); break;}; case NDUInt8: {fromValue(src); break;}; case NDInt16: {fromValue(src); break;}; + case NDUInt16: {fromValue(src); break;}; case NDInt32: {fromValue(src); break;}; case NDUInt32: {fromValue(src); break;}; case NDInt64: {fromValue(src); break;}; From b9a03929455256a55179f0a619d97c48b651c86a Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 11 Jun 2025 09:48:22 +0000 Subject: [PATCH 21/23] reserve pvxs converter array, release in destructor --- .../ntndArrayConverterPvxs.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index c9648b7e1..8e324ca4e 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -312,6 +312,17 @@ void NTNDArrayConverterPvxs::toAttributes (NDArray *dest) } } +template +struct freeNDArray { + NDArray *array; + // increase reference count of array, release on destructor + freeNDArray(NDArray *array) : array(array) { array->reserve(); } + void operator()(dataType *data) { + assert (array->pData == data); + array->release(); + } +}; + template void NTNDArrayConverterPvxs::fromValue(NDArray *src) { NDArrayInfo_t arrayInfo; @@ -323,8 +334,8 @@ void NTNDArrayConverterPvxs::fromValue(NDArray *src) { std::string fieldName = m_fieldNameMap[typeid(dataType)]; auto val = pvxs::shared_array( (dataType*)src->pData, - // trivial deletor that does nothing when shared_array goes out of scope - [] (dataType *data) {}, + // custom deletor + freeNDArray(src), arrayInfo.nElements); m_value[fieldName] = val.freeze(); From 1a7864665053d65739fc0aa20c15237488b24675 Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 11 Jun 2025 10:27:58 +0000 Subject: [PATCH 22/23] Remove redundancy in ADApp/Makefile --- ADApp/Makefile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ADApp/Makefile b/ADApp/Makefile index f4987ea54..32f440b0a 100644 --- a/ADApp/Makefile +++ b/ADApp/Makefile @@ -12,11 +12,8 @@ pluginSrc_DEPEND_DIRS += ADSrc DIRS += pluginTests pluginTests_DEPEND_DIRS += pluginSrc -ifeq ($(WITH_PVA), YES) - DIRS += ntndArrayConverterSrc - ntndArrayConverterSrc_DEPEND_DIRS += ADSrc - pluginSrc_DEPEND_DIRS += ntndArrayConverterSrc -else ifeq ($(WITH_PVXS), YES) +# if WITH_PVA or WITH_PVA = YES +ifeq ($(findstring YES,$(WITH_PVA) $(WITH_PVXS)), YES) DIRS += ntndArrayConverterSrc ntndArrayConverterSrc_DEPEND_DIRS += ADSrc pluginSrc_DEPEND_DIRS += ntndArrayConverterSrc From d493f02ddcabd9d7075d9acddf67a76e96cb3850 Mon Sep 17 00:00:00 2001 From: James Souter Date: Wed, 11 Jun 2025 10:48:58 +0000 Subject: [PATCH 23/23] Fix sign-compare warnings in pvxs converter --- ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp index 8e324ca4e..37a33921a 100644 --- a/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp +++ b/ADApp/ntndArrayConverterSrc/ntndArrayConverterPvxs.cpp @@ -223,7 +223,7 @@ void NTNDArrayConverterPvxs::toDimensions (NDArray *dest) auto dims = m_value["dimension"].as>(); dest->ndims = (int)dims.size(); - for(size_t i = 0; i < dest->ndims; ++i) + for(int i = 0; i < dest->ndims; ++i) { NDDimension_t *d = &dest->dims[i]; d->size = dims[i]["size"].as(); @@ -291,7 +291,7 @@ void NTNDArrayConverterPvxs::toUndefinedAttribute (NDArray *dest, pvxs::Value at void NTNDArrayConverterPvxs::toAttributes (NDArray *dest) { auto attributes = m_value["attribute"].as>(); - for (int i=0; i"].type().code) { // use indirection on Any container to get specified type