1)NDArray头文件:
#ifndef NDArray_H
#define NDArray_H#include <set>#include <epicsMutex.h>
#include <epicsTime.h>
#include <ellLib.h>#include "NDAttribute.h"
#include "NDAttributeList.h"
#include "Codec.h"/** NDArray中维度的最大数目 */
#define ND_ARRAY_MAX_DIMS 10/** NDArray属性"colorMode"的颜色模式枚举 */
typedef enum
{NDColorModeMono, /**< 单色 */NDColorModeBayer, /**< Bayer模式图, 每个像素1个值,但在探测器上带有颜色过滤 */NDColorModeRGB1, /**< 像素颜色交错的RGB图像, 数据数组是[3, NX, NY] */NDColorModeRGB2, /**< 行颜色交错的RGB图像 , 数据数组是[NX, 3, NY] */NDColorModeRGB3, /**< 面颜色交错的RGB图像 , 数据数组是[NX, NY, 3] */NDColorModeYUV444, /**< YUV 图像, 3字节编码1个RGB像素 */NDColorModeYUV422, /**< YUV 图像, 4字节编码2个RGB像素 */NDColorModeYUV411 /**< YUV 图像, 6字节编码4个RGB像素 */
} NDColorMode_t;/** NDArray属性"bayerPattern"的Bayer颜色枚举. 这个值仅对colorMode是NDColorModeBayer才有意义,* 因为在读出一个芯片子集时,Bayer模式将变化,所以需要这个值,例如如果X或Y偏移值不是偶数。*/
typedef enum
{NDBayerRGGB = 0, /**< 第一行RGRG, 第二行GBGB... */NDBayerGBRG = 1, /**< 第一行GBGB, 第二行RGRG... */NDBayerGRBG = 2, /**< 第一行GRGR, 第二行BGBG... */NDBayerBGGR = 3 /**< 第一行BGBG, 第二行GRGR... */
} NDBayerPattern_t;/** 定义一个NDArray维度的结构体 */
typedef struct NDDimension {size_t size; /**< 数组的这个维度中元素数目 */size_t offset; /**< 相对于原始的数据来源(例如,探测器)的原点的偏移量。* 如果探测器的一个选取区域被读取,则这个值可以大于0.* 在unbinned像素中和默认方向,非反向中指定这个偏移值,* 偏移值是累积的,因此如果一个诸如NDPluginROI的插件进一步选取了一个子区域,* 这个偏移量是相对于探测器中第一个元素,而不是相对于传递给NDPluginROI的区域的第一个元素。*/int binning; /**< binning(像素求和,1=no binning)相对于原始的数据来源(例如,探测器)* 偏移值是累积的,因而如果一个诸如NDPluginROI的插件执行binning,* 相对于探测器中像素而不是相对于传递给NDPluginROI的可能的binned像素表达这个binning。*/int reverse; /**< 相对于原先的数据来源(例如,探测器)的方向(0=normal,1=reversed)* 这个值是累积的,因此如果一个诸如NDPluginROI的插件反向这些数据,值必须反映相对于原先探测器的方向,* 而不是相对于传递给NDPluginROI的可能的反向数据。*/
} NDDimension_t;/** 由NDArray::getInfo返回的结构体 */
typedef struct NDArrayInfo {size_t nElements; /**< 数组中元素的总数 */int bytesPerElement; /**< 数组中每个元素的字节数*/size_t totalBytes; /**< 保存这个数组所需的字节总数;这可以小于NDArray::dataSize *//**< 以下对彩色图(RGB1,RGB2,RGB3)最有用 */NDColorMode_t colorMode; /**< 颜色模式 */int xDim; /**< 数组索引,它是X维度 */int yDim; /**< 数组索引,它是Y维度 */int colorDim; /**< 数组索引,它是颜色维度 */size_t xSize; /**< 这个数组的X尺寸 */size_t ySize; /**< 这个数组的Y尺寸 */size_t colorSize; /**< 这个数组的颜色尺寸 */size_t xStride; /**< X值之间数组元素的数目 */size_t yStride; /**< Y值之间数组元素的数目*/size_t colorStride; /**< 颜色值间数组元素的数目 */
} NDArrayInfo_t;/** * N维度数组类;每个数组有一个维度,数据类型,数据指针以及可选的属性的集合。* 一个数组也有一个标识自身的uniqueId和timeStamp。NDArray对象可以由一个NDArrayPool对象分配,* 出于内存管理,NDArrayPool对象维护一个NDArrays的空闲列表。* */
class ADCORE_API NDArray {
public:/* 方向 */NDArray();NDArray(int ndims, size_t *dims, NDDataType_t dataType, size_t dataSize, void *pData);virtual ~NDArray();int initDimension (NDDimension_t *pDimension, size_t size);static int computeArrayInfo(int ndims, size_t *dims, NDDataType_t dataType, NDArrayInfo *pInfo);int getInfo (NDArrayInfo_t *pInfo);int reserve();int release();int getReferenceCount() const {return referenceCount;}int report(FILE *fp, int details);friend class NDArrayPool;private:ELLNODE node; /**< 这必须首先出现,因为ELLNODE必须与NDArray对象有相同地址 */int referenceCount; /**< 这个NDArray的引用计数,等于正在使用它的客户端数目 */public:class NDArrayPool *pNDArrayPool; /**< 创建这个数组的NDArrayPool对象 */class asynNDArrayDriver *pDriver; /**< 创建这个数组的asynNDArrayDriver*/int uniqueId; /**< 在一个驱动启动后,由它产生的所有NDArrays中一个必须唯一的编号。*/double timeStamp; /**< 这个数组以秒为单位的时间戳;推荐从EPICS纪元(00:00:00 UTC, January 1, 1990)的秒数但某些驱动可以使用一个不同的起始的时间。*/epicsTimeStamp epicsTS; /**< epicsTimeStamp,用pasynManager->updateTimeStamp()设置这个变量,并且可以来自一个用户定义的时间戳源。 */int ndims; /**< 这个数组中维度数目,最小=1 */NDDimension_t dims[ND_ARRAY_MAX_DIMS]; /**< 这个数组的维度尺寸的数组;前ndims个值是有意义 */NDDataType_t dataType; /**< 这个数组的数据类型 */size_t dataSize; /**< 这个数组的数据尺寸;为*pData分配的实际内存量,可以多于保存这个数组所需的内存量 */void *pData; /**< 指向数组数据的指针,认为按dims[0]变化最快,dims[ndims-1]变化最慢的顺序存储这些数据。 */NDAttributeList *pAttributeList; /**< 属性的链表 */Codec_t codec; /**< 用于压缩这些数据的codec定义. */size_t compressedSize; /**< 压缩数据的尺寸。如果pData未被压缩,应该与dataSize相等。*/
};/*这个类定义了一个被包含在std::multilist中的对象,std::multilist用于排序这个freeList_中的NDArrays它定义了一个<操作符,用于使用NDArray::dataSize作为排序键。我们想要在NDArrayPool.cpp中隐藏这个类,并且只是在这里先引用了它。//class sortedListElement;*/
class freeListElement {public:freeListElement(NDArray *pArray, size_t dataSize) {pArray_ = pArray;dataSize_ = dataSize;}friend bool operator<(const freeListElement& lhs, const freeListElement& rhs) {return (lhs.dataSize_ < rhs.dataSize_);}NDArray *pArray_;size_t dataSize_;private:freeListElement(); // 默认构造器是私有的,因而对象不能被无参构建
};/** * NDArrayPool对象维护一个NDArrays的空闲列表(池)。驱动程序从这个池分配NDArray对象,并且传递这些对象给插件。* 当插件放置这个NDArray对象到它们的队列时,它们增加对这个对象的索引,而在它们处理完这个数组后,* 减少对这个对象的索引。当索引计数再次到达0时,这个NDArray对象被放回到空闲列表。* 这种机制使得在插件中数组的复制最小化。*/
class ADCORE_API NDArrayPool {
public:NDArrayPool (class asynNDArrayDriver *pDriver, size_t maxMemory);virtual ~NDArrayPool() {}NDArray* alloc(int ndims, size_t *dims, NDDataType_t dataType, size_t dataSize, void *pData);NDArray* copy(NDArray *pIn, NDArray *pOut, bool copyData, bool copyDimensions=true, bool copyDataType=true);int reserve(NDArray *pArray);int release(NDArray *pArray);int convert(NDArray *pIn,NDArray **ppOut,NDDataType_t dataTypeOut,NDDimension_t *outDims);int convert(NDArray *pIn,NDArray **ppOut,NDDataType_t dataTypeOut);int report(FILE *fp, int details);int getNumBuffers();size_t getMaxMemory();size_t getMemorySize();int getNumFree();void emptyFreeList();protected:/** * 应该由管理派生自NDArray类的对象的池类,实现以下方法。*/virtual NDArray* createArray();virtual void onAllocateArray(NDArray *pArray);virtual void onReserveArray(NDArray *pArray);virtual void onReleaseArray(NDArray *pArray);private:std::multiset<freeListElement> freeList_;epicsMutexId listLock_; /**< 保护这个空闲列表 */int numBuffers_;size_t maxMemory_; /**< 允许这个对象分配的最大内存字节量; -1=无限*/size_t memorySize_; /**< 这个对象当前已经分配的内存字节量 */class asynNDArrayDriver *pDriver_; /**< 创建这个对象的asynNDArrayDriver */
};#endif
2)NDArray的实现源文件:
#include <string.h>
#include <stdio.h>
#include <ellLib.h>
#include <vector>#include <epicsTypes.h>
#include <cantProceed.h>#include "NDArray.h"/** NDArray constructor, no parameters. NDArray构造器,不带参数。* 初始化所有字段为0,创建属性链表和链表互斥锁。*/
NDArray::NDArray() // referenceCount,pNDArrayPool,pDriver, uniqueId,timeStamp,ndims, dataType(NDInt8),pData: referenceCount(0), pNDArrayPool(0), pDriver(0),uniqueId(0), timeStamp(0.0), ndims(0), dataType(NDInt8),dataSize(0), pData(0)
{this->epicsTS.secPastEpoch = 0;this->epicsTS.nsec = 0;memset(this->dims, 0, sizeof(this->dims)); // dims这个数组中元素全部填0memset(&this->node, 0, sizeof(this->node)); // ELLNODE node成员结构体填0 this->pAttributeList = new NDAttributeList(); // 一个空的属性链表
}NDArray::NDArray(int nDims, size_t *dims, NDDataType_t dataType, size_t dataSize, void *pData): referenceCount(0), pNDArrayPool(0), pDriver(0),uniqueId(0), timeStamp(0.0), ndims(nDims), dataType(dataType), // 设置维度数目,每个维度上元素数目,数据类型,数据尺寸未传入值dataSize(dataSize), pData(0)
{static const char *functionName = "NDArray::NDArray";this->epicsTS.secPastEpoch = 0;this->epicsTS.nsec = 0;this->pAttributeList = new NDAttributeList();this->referenceCount = 1;memset(this->dims, 0, sizeof(this->dims));for (int i=0; i<ndims && i<ND_ARRAY_MAX_DIMS; i++) {this->dims[i].size = dims[i]; // 每一个维度上元素数目this->dims[i].offset = 0;this->dims[i].binning = 1;this->dims[i].reverse = 0;}NDArrayInfo arrayInfo;this->getInfo(&arrayInfo); // 获取数组信息//如果传入dataSize为0,则根据维度数目,每一维度上的元素数目,以及元素类型计算数据尺寸if (dataSize == 0) dataSize = arrayInfo.totalBytes; // if (arrayInfo.totalBytes > dataSize) {printf("%s: ERROR: required size=%d passed size=%d is too small\n",functionName, (int)arrayInfo.totalBytes, (int)dataSize);}/* 如果调用这传递了一个有效的缓存,使用这个缓存,相信它的尺寸是正确的 */if (pData) {this->pData = pData;} else {// 如果传递了一个NULL,则分配这个内存空间this->pData = malloc(dataSize);this->dataSize = dataSize;}
}/** NDArray析构器* 释放这个数据数组,删除所有属性,释放属性列表并且销毁互斥量 */
NDArray::~NDArray()
{if (this->pData) free(this->pData);delete this->pAttributeList;
}/* * 便捷方法计算数组中总字节数typedef struct NDArrayInfo {size_t nElements; //总元素数目 int bytesPerElement; //每个元素所用字节size_t totalBytes; //总字节数目NDColorMode_t colorMode;int xDim; int yDim; int colorDim; size_t xSize; size_t ySize; size_t colorSize; size_t xStride; size_t yStride; size_t colorStride; } NDArrayInfo_t;// 根据元素数据类型,维度数目和每一个维度上元素数目// 获取每个元素字节数,元素总数以及总字节数信息,存入NDArrayInfo结构体*/
int NDArray::computeArrayInfo(int ndims, size_t *dims, NDDataType_t dataType, NDArrayInfo *pInfo)
{switch(dataType) // 根据指定的元素数据类型,计算每个元素需要的字节数case NDInt8:pInfo->bytesPerElement = sizeof(epicsInt8);break;case NDUInt8:pInfo->bytesPerElement = sizeof(epicsUInt8);break;case NDInt16:pInfo->bytesPerElement = sizeof(epicsInt16);break;case NDUInt16:pInfo->bytesPerElement = sizeof(epicsUInt16);break;case NDInt32:pInfo->bytesPerElement = sizeof(epicsInt32);break;case NDUInt32:pInfo->bytesPerElement = sizeof(epicsUInt32);break;case NDInt64:pInfo->bytesPerElement = sizeof(epicsInt64);break;case NDUInt64:pInfo->bytesPerElement = sizeof(epicsUInt64);break;case NDFloat32:pInfo->bytesPerElement = sizeof(epicsFloat32);break;case NDFloat64:pInfo->bytesPerElement = sizeof(epicsFloat64);break;default:return(ND_ERROR);break;}pInfo->nElements = 1;for (int i=0; i<ndims; i++) pInfo->nElements *= dims[i]; // 根据维度数目,每个维度上元素数目,计算总元素数目pInfo->totalBytes = pInfo->nElements * pInfo->bytesPerElement; // 根据每个元素所用字节数和总元素数目计算总字节数目return ND_SUCCESS;
}
/** 便捷方法:返回有关NDArray的信息;包括这个数组中元素数目,每个元素的字节数目,以及字节总数。[out] pInfo : 指向一个NDArrayInfo_t结构体的指针,必须已经由调用者分配 . */
int NDArray::getInfo(NDArrayInfo_t *pInfo)
{int i;NDAttribute *pAttribute;size_t *dims_t = new size_t[this->ndims]; // 根据维度数目分配一个数组for (i=0; i<this->ndims; i++) dims_t[i] = this->dims[i].size; // 数组中每个元素获取这个NDArray对象中每个维度的尺寸// 获取每个元素字节数,元素总数以及总字节数信息,存入NDArrayInfo结构体int status = NDArray::computeArrayInfo(this->ndims, dims_t, this->dataType, pInfo);delete[] dims_t;if (status != ND_SUCCESS) return status;pInfo->colorMode = NDColorModeMono; // 设置颜色模式为单色pAttribute = this->pAttributeList->find("ColorMode"); // 属性列表中能够找到颜色模式,则设置为获取到的颜色模式if (pAttribute) pAttribute->getValue(NDAttrInt32, &pInfo->colorMode);pInfo->xDim = 0;pInfo->yDim = 0;pInfo->colorDim = 0;pInfo->xSize = 0;pInfo->ySize = 0;pInfo->colorSize = 0;pInfo->xStride = 0;pInfo->yStride = 0;pInfo->colorStride = 0;if (this->ndims > 0) { pInfo->xStride = 1;// X方向是变化最快的维度pInfo->xSize = this->dims[0].size;}if (this->ndims > 1) {pInfo->yDim = 1; // Y方向是变化其次的维度pInfo->yStride = pInfo->xSize;pInfo->ySize = this->dims[1].size;}if (this->ndims == 3) {switch (pInfo->colorMode) {case NDColorModeRGB1:pInfo->xDim = 1;pInfo->yDim = 2;pInfo->colorDim = 0;pInfo->xStride = this->dims[0].size;pInfo->yStride = this->dims[0].size * this->dims[1].size;pInfo->colorStride = 1;break;case NDColorModeRGB2:pInfo->xDim = 0;pInfo->yDim = 2;pInfo->colorDim = 1;pInfo->xStride = 1;pInfo->yStride = this->dims[0].size * this->dims[1].size;pInfo->colorStride = this->dims[0].size;break;case NDColorModeRGB3:pInfo->xDim = 0;pInfo->yDim = 1;pInfo->colorDim = 2;pInfo->xStride = 1;pInfo->yStride = this->dims[0].size;pInfo->colorStride = this->dims[0].size * this->dims[1].size;break;default:// This is a 3-D array, but is not RGBpInfo->xDim = 0;pInfo->yDim = 1;pInfo->colorDim = 2;pInfo->xStride = 1;pInfo->yStride = this->dims[0].size;pInfo->colorStride = this->dims[0].size * this->dims[1].size;break;}pInfo->xSize = this->dims[pInfo->xDim].size; //xDim为0pInfo->ySize = this->dims[pInfo->yDim].size; //yDim为1pInfo->colorSize = this->dims[pInfo->colorDim].size;}return(ND_SUCCESS);
}/*初始化维度结构体NDDimension_t为size=size,binning=1,reverse=0,offset=0。[in] pDimension : 指向一个NDDimension_t结构体的指针,必须已经由调用者分配.
*/
int NDArray::initDimension(NDDimension_t *pDimension, size_t size)
{pDimension->size=size;pDimension->binning = 1;pDimension->offset = 0;pDimension->reverse = 0;return ND_SUCCESS;
}/** 调用这个NDArray对象的NDArrayPool::reserve() ; 为这个数组增加引用计数 . */
int NDArray::reserve()
{const char *functionName = "NDArray::reserve";if (!pNDArrayPool) {printf("%s: WARNING, no owner\n", functionName);return(ND_ERROR);}return(pNDArrayPool->reserve(this));
}/** 调用这个NDArray对象的NDArrayPool::reverse(); 减小这个数组的引用计数 . */
int NDArray::release()
{const char *functionName = "NDArray::release";if (!pNDArrayPool) {printf("%s: WARNING, no owner\n", functionName);return(ND_ERROR);}return(pNDArrayPool->release(this));
}/** Reports on the properties of the array. 报告这个数组的性质。* [in] fp : 用于报告输出的文件指针.* [in] details: 所需报告详细程度;如果 >5,调用NDAttributeList::report().*/
int NDArray::report(FILE *fp, int details)
{int dim;fprintf(fp, "\n");fprintf(fp, "NDArray Array address=%p:\n", this); // NDArray对象的地址fprintf(fp, " ndims=%d dims=[", this->ndims); // NDArray对象的维度数目for (dim=0; dim<this->ndims; dim++) fprintf(fp, "%d ", (int)this->dims[dim].size); // NDArray对象每个维度的元素数目fprintf(fp, "]\n");fprintf(fp, " dataType=%d, dataSize=%d, pData=%p\n", // 数据类型,数据尺寸,数据地址this->dataType, (int)this->dataSize, this->pData); fprintf(fp, " uniqueId=%d, timeStamp=%f, epicsTS.secPastEpoch=%d, epicsTS.nsec=%d\n", // 唯一id,时间戳this->uniqueId, this->timeStamp, this->epicsTS.secPastEpoch, this->epicsTS.nsec);fprintf(fp, " referenceCount=%d\n", this->referenceCount); // 引用计数fprintf(fp, " number of attributes=%d\n", this->pAttributeList->count()); // 属性数目if (details > 5) {this->pAttributeList->report(fp, details); // 每个属性信息}return ND_SUCCESS;
}
3)测试代码:
#include <string.h>
#include <stdio.h>
#include <ellLib.h>
#include <vector>
#include <stdlib.h>#include <epicsTypes.h>
#include <epicsEvent.h>
#include <cantProceed.h>
#include <epicsTime.h>
#include <asynDriver.h>#include "NDArray.h"static asynUser * pasynUser = NULL;int main()
{int nDims = 2;size_t dims[2];size_t dataSize = 0;epicsTimeStamp currentTime;//double timeStamp;dims[0] = 3; dims[1] = 5;epicsInt32 * pArrs = NULL;if (!pasynUser){pasynUser = pasynManager->createAsynUser(0,0);}//NDArray * pNDArray = new NDArray(int ndims, size_t *dims, NDDataType_t dataType, size_t dataSize, void *pData);NDArray * pNDArray = new NDArray(nDims, dims, NDInt32, dataSize, (void *)pArrs) ;// 获取数组数据的存储地址pArrs = (epicsInt32 *)pNDArray->pData;size_t i;size_t total = dims[0] * dims[1];// 对NDArray对象的数组赋值for (i = 0; i < total; i++){pArrs[i] = i;}// 更新timestamp时间戳pasynManager->updateTimeStamp(pasynUser);pNDArray->epicsTS = pasynUser->timestamp;// 更新以秒计的时间戳epicsTimeGetCurrent(¤tTime);pNDArray->timeStamp = currentTime.secPastEpoch + currentTime.nsec / 1.e9;// 调用NDArray对象的report方法 输出这个NDArray对象的信息pNDArray->report(stdout, 10);// 输出NDArray数组中的信息:size_t j;printf("array data Information:\n");for (i = 0; i < dims[1]; i++ ){for (j = 0; j < dims[0]; j++){printf("%u\t", pArrs[i * dims[0] + j]);}printf("\n");}NDArrayInfo_t info;pNDArray->getInfo(&info);printf("\n");printf("NDArrayInfo for the NDArray object:\n");/** size_t nElements; //总元素数目int bytesPerElement; //每个元素所用字节size_t totalBytes; //总字节数目NDColorMode_t colorMode;int xDim;int yDim;int colorDim;size_t xSize;size_t ySize;size_t colorSize;size_t xStride;size_t yStride;size_t colorStride;* */printf("nElements: %lu,\tbytesPerElement:%d\ttotalBytes: %lu\n", info.nElements, info.bytesPerElement, info.totalBytes);printf("colorMode: %d\n", info.colorMode);printf("xDim:%d,\tyDim:%d,\tcolorDim:%d\n", info.xDim, info.yDim, info.colorDim);printf("xSize:%lu,\tySize:%lu,\tcolorSize:%lu\n", info.xSize, info.ySize, info.colorSize);printf("xStride:%lu\tyStride:%lu,\tcolorStride:%lu\n", info.xStride, info.yStride, info.colorStride);delete pNDArray;return 0;
}
编译以上源文件进行测试,结果如下:
root@orangepi5:/home/orangepi/C_program/host_program/hostApp# O.linux-aarch64/testNDArrayNDArray Array address=0x55902dda30:ndims=2 dims=[3 5 ]dataType=4, dataSize=60, pData=0x55902ddc90uniqueId=0, timeStamp=1065510588.289565, epicsTS.secPastEpoch=0, epicsTS.nsec=0referenceCount=1number of attributes=0NDAttributeList: address=0x55902ddbd0:number of attributes=0
array data Information:
0 1 2
3 4 5
6 7 8
9 10 11
12 13 14NDArrayInfo for the NDArray object:
nElements: 15, bytesPerElement:4 totalBytes: 60
colorMode: 0
xDim:0, yDim:1, colorDim:0
xSize:3, ySize:5, colorSize:0
xStride:1 yStride:3, colorStride:0