NDArray源码解析及测试代码

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

你可能感兴趣的:(EPICS教程,Linux,C,EPICS,C语言,areaDetector)