1)NDArray头文件:
#ifndef NDArray_H
#define NDArray_H
#include
#include
#include
#include
#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 freeList_;
epicsMutexId listLock_; /**< 保护这个空闲列表 */
int numBuffers_;
size_t maxMemory_; /**< 允许这个对象分配的最大内存字节量; -1=无限*/
size_t memorySize_; /**< 这个对象当前已经分配的内存字节量 */
class asynNDArrayDriver *pDriver_; /**< 创建这个对象的asynNDArrayDriver */
};
#endif
2)NDArray的实现源文件:
#include
#include
#include
#include
#include
#include
#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这个数组中元素全部填0
memset(&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; idims[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; inElements *= 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; indims; 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 RGB
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;
}
pInfo->xSize = this->dims[pInfo->xDim].size; //xDim为0
pInfo->ySize = this->dims[pInfo->yDim].size; //yDim为1
pInfo->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; dimndims; 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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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/testNDArray
NDArray Array address=0x55902dda30:
ndims=2 dims=[3 5 ]
dataType=4, dataSize=60, pData=0x55902ddc90
uniqueId=0, timeStamp=1065510588.289565, epicsTS.secPastEpoch=0, epicsTS.nsec=0
referenceCount=1
number of attributes=0
NDAttributeList: address=0x55902ddbd0:
number of attributes=0
array data Information:
0 1 2
3 4 5
6 7 8
9 10 11
12 13 14
NDArrayInfo 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