EPICS areaDetector用户指南一

1 概要

在EPICS中,areaDetector模块为面(2-D)探测器提供了多用途接口。它是为了和各种探测器和相机一起使用,范围从高帧率CCD和CMOS相机,像素阵列探测器,诸如Pilatus,和大型格式探测器,如Perkin Elmer平板面板。

这个模块的目标是:

1)使实现一个新探测器所需编写的代码量最少。

2)提供一个定义了一个探测器驱动必须支持的函数和参数的标准接口。

3)提供一组基本的EPICS记录,对于使用这个模块的每个探测器都存在。这使得能够使用通用的EPICS客户端来显示图像和控制相机和探测器。

4)除了标准参数外,还允许轻松扩展来使用探测器专用的特性。

5)高性能。可以编写程序来通过EPICS获取探测器图像数据,也有一个接口可用于为非常高性能获取更底层的探测器数据。

6)提供一种机制用于设备无关的实时数据分析,诸如感兴趣区域和统计。

7)提供同步辐射应用中常用探测器的探测器驱动。这些包括GigE视频相机,IEEE 1394相机,CCD x射线探测器,科学CCD和CMOS相机,在线成像板探测器,像素阵列探测器,CMOS平面探测器和很多别的探测器。

2、架构

areaDetector模块的架构如下展示。

EPICS areaDetector用户指南一_第1张图片

这个架构从下自上由以下构成:

1)Layer1: 这一层允许使用者编写的代码能够与硬件通信。它通常是由探测器厂家提供的。它可能包含一个库或者DLL组成,一个驱动的套接字协议组成,一个Microsoft COM接口等。

2)Layer2: 编写这个驱动是为了控制一个特定的探测的areaDetector程序。用C++编写它,并且从ADDriver类继承。它为控制和状态信息使用标准的asyn接口。每次它接收到一个新数据数组时,它能够以一个NDArray对象传递它给已经注册回调的所有Layer 3客户端。

3)Layer3:运行在这层的代码被称为一个"plug-in"。本代码向一个驱动注册了一个回调,当有一个新数据数组时,将用这个回调。已存在的插件实现了文件保存(MDPluginFile), 感兴趣区域(ROI)计算(NDPluginROI),统计计算(NDPluginStats, NDPluginROIStat), 图像处理(NDPluginProcess),几何变换(NDPluginTransform),缓存和触发(NDPluginCircularBuff),颜色模式转换(NDPluginColorConvert),图形和文字重叠(NDPluginOverlay),导出NDArray属性为标量和waveform记录(NDPluginAttribute),为了由通道访问客户端(NDPluginStdArrays),转换探测器数据为标准的EPICS数组类型,以及转换NDArrays为EPICS pvAccess(NDPluginPva)。插件用C++编写并且从NDPluginDriver继承。已经存在的插件范围大约从300到3000行代码。

4)Layer4:这是EPICS asyn模块带有的标准asyn设备支持。

5)Layer5:这些时标准的EPICS记录,以及EPICS数据库(模板)文件,它们定义了与Layer 2驱动和Layer 3插件通信的记录。

6)Layer6:    这些是EPICS通道访问客户端,诸如与Layer 5记录通信的MEDM。areaDetector包括了能够使用EPICS waveform显示图像的某些客户端程序以及与Layer 3 NDPluginStdArrays进行通信的其它记录。若干这些客户端是ImageJ插件,而另一些是自由运行的IDL程序。

在Layers 1-3中的代码实际上独立于EPICS。原则上,在这部分代码中只有2个EPICS依赖。

1)libCom:来自EPICS base的libCom为线程,互斥量等提供了操作系统无关的函数。

2)asyn:asyn是一个模块,它提供了线程间消息服务,包含队列和回调。

特别地,消除在上图中展示地架构中layers 4-6是可能的。这表示在使用在Layers 2和3的驱动和插件时,运行一个EPICS IOC或者使用EPICS通道访问不是必需的。在ADSimDetector中simDetectorNoIOC程序和在ADCore/ADApp/pluginTests中单元测试中展示了这点。

插件架构非常强大,因为能够为程序专用目的编写新插件。例如,例如,可以编写一个插件来分析图像和进行某些程序专用功能,并且这样一个插件将接着与任何探测器驱动一起运行。由于在运行时可以重新配置它们,所以它们也是强大的。例如,NDPluginStdArrays可以从一个探测器驱动获取数据切换到从一个NDPluginROI插件获取数据。它用此方式从显示整个探测器切换到这个ROI驱动选取的任何子区域。连接了这个NDPluginStdArrays驱动的任何通道访问客户端将自动切换成显示这个子区域。类似地,只要通过更改其输入源,NDPluginFile插件在运行时从保存整个图像切换成保存一个所选地ROI。插件可用于组成一个图像处理pipeline,例如,用探测器提供数据给一个颜色转换插件,这个颜色转换插件提供数据给一个ROI插件,这个ROI插件提供数据给一个文件保存插件。每隔插件可以运行在它自己的线程中,因而在现代多核CPU上在其自己的内核上。

插件的使用是可选的,并且它只是需要驱动用图像数据进行回调的插件。如果没有插件被使用,则EPICS可以用于仅控制这个探测器,而不访问数据自身。当厂家提供了能够保存数据到一个文件的API并且一个显示这些图像的程序时,这非常有用。

以下是这个软件的详细描述,从低向上运行。大部分代码是面向对象的,用C++编写。

asynPortDriver

探测器驱动和插件是asyn端口驱动,意味着它们实现了一个或多个标准的asyn接口。它们注册自己为中断源,因而当值变换时,它们对注册的asyn客户端进行回调。它们继承自在asyn模块中提供的基类C++类。那个基类处理注册这个端口驱动,注册支持的接口以及注册所需中断源的所有细节。它也提供了用于被此驱动中定义的整数值索引的int,double和string参数的参数库。参数库提供了写和读取参数值的方法,和在一个参数值变换时执行对注册客户端的回调。asynPortDriver类文档详细地描述了这个类asynPortDriver类。

NDArray

NDArray

NDArray(N维度数组)是用于从驱动向插件传递探测器数据的类。一个NDArray是一个用于处理数组数据的通用类。一个NDArray是自描述的,表示它包含了描述数据自身的足够信息。它能够可选地包含"属性"(类NDAttribute),它包含了描述如何采集这个数据的元数据等。

一个NDArray可以最多有ND_ARRAY_MAX_DIMS个维度,当前10。相较于无限维度数,一个固定的最大维度数用于显著地简化代码。数组的每隔维度由NDDimension描述。NDArray类文档详细地描述这个类。

/* 定义一个NDArray维度地结构体 */
typedef struct NDDimension {
     size_t size;    
     size_t offset;  
     int binning;    
     int reverse;    
 } NDDimension_t;

公共属性:

  • size_t size:在这个数组的这个维度上元素数目。
  • size_t offset:相对于原来数据源(例如,探测器)原点的偏移。如果探测器的一个所选区域正在被读取,则这个值必须大于0。在非合并像素以及咋默认取向,非反向中指定偏移值。偏移值是累积的,因此如果一个插件,诸如NDPluginROI进一步选取一个子区域,这个偏移相对于这个探测器中第一个元素,而非传递给NDPluginROI的区域的第一个元素。
  • int binning:相对于原来数据源(例如,探测器)的合并(像素求和,1=no binning)。偏移值是累积的,因此如果一个诸如NDPluginROI的插件执行合并,相对于探测器表达合并,而非传递给NDPluginROI的可能被合并像素。
  • int reverse:取向(0=正常,1=反向)相对于数据源(例如,探测器)。这个值是累计的,因此如果要给诸如NDPluginROI的插件反向了数据,这个值必须反映相对于原始探测器的取向,而不是传递给NDPluginROI的可能被反向的数据。
/*
    N维数组类;每个数组有一个维度,数据类型,指向数据指针和可选属性的集合
*/ 
 #include "NDAttribute.h"
 #include "NDAttributeList.h"
 #include "Codec.h"
  
 #define ND_ARRAY_MAX_DIMS 10
  
 typedef enum
 {
     NDColorModeMono,    
     NDColorModeBayer,   
     NDColorModeRGB1,    
     NDColorModeRGB2,    
     NDColorModeRGB3,    
     NDColorModeYUV444,  
     NDColorModeYUV422,  
     NDColorModeYUV411   
 } NDColorMode_t;
  
 typedef enum
 {
     NDBayerRGGB        = 0,    
     NDBayerGBRG        = 1,    
     NDBayerGRBG        = 2,    
     NDBayerBGGR        = 3     
 } NDBayerPattern_t;
  
 typedef struct NDDimension {
     size_t size;    
     size_t offset;  
     int binning;    
     int reverse;    
 } NDDimension_t;
  
 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;

/*
    N维数组类;每个数组有一个维度,数据类型,指向数据的指针和可选属性的集合。
    一个NDArray也有一个标识它的uniqueId和timeStamp。NDArray对象可以由NDArrayPool对象分配,它维护一个为了高效内存管理的NDArrays的空闲表。
*/
  
 class ADCORE_API NDArray {
 public:
     /* 方法 */
    
     NDArray();     /* 无参构造器,初始化所有字段为0.创建属性链表和链表互斥量 */
     NDArray(int ndims, size_t *dims, NDDataType_t dataType, size_t dataSize, void *pData);
     virtual ~NDArray();    /* NDArray析构器,释放数据数组,删除所有属性,释放属性表和销毁互斥锁 */
     int          initDimension   (NDDimension_t *pDimension, size_t size); // 初始化维度结构体为size=size, binning=1, reverse=0,offset=0。参数:[in]pDimension:指向一个NDDimension_t结构体的指针,必须已经由调用者分配;[in]size:这个维度的大小。
     static int   computeArrayInfo(int ndims, size_t *dims, NDDataType_t dataType, NDArrayInfo *pInfo);     // 计算数组中总字节数的便捷方法。
     int          getInfo         (NDArrayInfo_t *pInfo);  // 返回有关一个NDArray的信息的便捷方法,包括元素总数,每个元素的字节数,在数组中字节总数,参数:[输出]pInfo,指向一个NDArrayInfo的指针,必须已经由调用者分配。
     int          reserve();     //为这个对象调用NDArrayPool::reverse(),增加一次这个数组的引用次数
     int          release();     //为这个对象调用NDArrayPool::release(),减少一次这个数组的引用次数
     int          getReferenceCount() const {return referenceCount;}  // 返回引用次数
     int          report(FILE *fp, int details); // 报告这个数组的属性。参数:[in]fp:这个报告输出的文件指针;[in]details:所需的报告细节等级;如果>5, 调用NDAttributeList::report()
     friend class NDArrayPool;
  
 private:
     ELLNODE      node;              
     int          referenceCount;    
 public:
     class NDArrayPool *pNDArrayPool;         // 创建这个数组的NDArrayPool对象
     class asynNDArrayDriver *pDriver;        // 创建这个数组的asynNDPortDriver对象的指针
     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;                   // 指向这个数组数据的指针 
     NDAttributeList *pAttributeList;        // 属性的链表
     Codec_t codec;                          // 用于压缩这些数据的codec定义    
     size_t compressedSize;                  // 被压缩数据的尺寸
 };

NDArrayPool

NDArrayPool类挂你一个NDArray对象的空闲列表(池)。驱动从这个池分配NDArray对象,并且传递这些对象给插件。当插件防止这个对象在它们的队列时,它增加对这个对象的引用次数,而当它们结束处理这个数组时,减少这个引用次数。当引用次数再次达到0时,NDArray对象被放回空闲列表。这个机制使得在插件中数组数据的复制最小化。NDArrayPool类文档详细地描写了这个类。

//  这个类定义被包含在std::multilist中的对象,用于排序freeList_中的数组。
 // 它定义<操作符,来使用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(); // 默认构造器是私有的,因而不能无参数构造对象
 };
  
 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:
     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_;     
     size_t       memorySize_;    
     class asynNDArrayDriver *pDriver_; 
 };
NDArrayPool::NDArrayPool(class asynNDArrayDriver * pDriver, size_t maxMemory)

NDArrayPool构造器

参数:

[in]pDriver:指向创建这个对象地asynNDArrayDriver的指针。

[in]maxMemory: 允许这个池使用的最大内存字节数,所有NDArray对象之和;0无限制。

virtual NDArrayPool::~NDArrayPool()

析构函数。

NDArray * NDArrayPool::alloc(int ndims, size_t * dims, NDDataType_t dataType, size_t dataSize, void * pData)		

alloc()分配一个新的NDArray对象,需要前3个参数。

参数:

[in]ndims:NDArray中维度数目。

[in]dims:维度数组,其尺寸必须至少ndims。

[in]dataType:NDArray数据的数据类型。

[in]dataSize:为数组数据分配的字节数;如果0,则alloc()将从ndims,dims和dataType计算这个大小。

[in]pData:指向一个数据缓存的指针,如果NULL,则alloc将分配一个新的数组缓存;如果非NULL,则认为它指向一个有效缓存。

如果pData不是NULL,则dataSize必须包含在这个已有数组中实际字节数,并且这个数组必须足够大来保存这个数组数据。alloc()搜索它的空闲列表查找一个空闲的NDArray缓存。如果它不能找到一个,则它将分配一个新的并且添加到这个空闲列表。如果分配这个NDArray所需的内存使得为这个池分配的累计内存超过了maxMemory,则将返回一个错误。alloc()设置返回的NDArray的引用计数为1。

int NDArrayPool::convert(NDArray * 	pIn, NDArray ** ppOut, NDDataType_t dataTypeOut)

convert从一个输入NDArray创建一个新的输出NDArray,执行转换操作。这个函数格式是仅用于转换数据类型,非维度,保留维度。

参数:

[in]:输入数组,转换的源。

[ppOut]:输出数组,转换的结果。

[dataTypeOut]:输出数组的数据类型。

int NDArrayPool::convert(NDArray * 	pIn, NDArray ** ppOut, NDDataType_t dataTypeOut, NDDimension_t * dimsOut)		

convert()从一个输入NDArray转换为一个新的输出NDArray,执行转换操作。

如果dataTypeOut与pIn->dataType不同,这个转换可以转换数据类型。它也可以更改维度。对于每个它的维度,outDims可能有与输入数组维度(pIn->dims)不同的size,binning和reverse。

参数:

[in]pIn:输入数组,转换的源。

[out]ppOut:输出数组,转换的结果。

[in]dataTypeOut:输出数组的数据类型。

[in]dimsOut:输出数组的维度。

NDArray * NDArrayPool::copy(NDArray * pIn, NDArray * pOut, bool copyData, bool copyDimensions = true, bool copyDataType = true)

copy此方法对一个NDArray对象进行复制。

参数:

[in]pIn:要复制的输入数组。

[in]pOut:要被复制到的输出数组,可以是NULL,或者一个指向一个已有NDArray的指针。

[in]copyData:如果这个标记是true,则复制在这个数组数据中包含的所有东西;如果0,复制除了数据(包含属性)外的所有东西。

[in]copyDimensions:如果这个标记为真,即使pOut不为NULL,维度也被复制,默认为true。

[in]copyDataType:如果这个标记为真,即使pOut不为NULL,dataType也被复制;默认为true。

返回一个指向一个输出数组的指针。

如果pOut是NULL,则必须首先分配它。如果输出数组对象已经存在(pOut!=NULL),则它必须为它分配足够内存来保存数据。

NDArray * NDArrayPool::createArray()	

createArray()以下方法应该有一个池类实现,它管理从NDArray类派生的对象。

创建一个新的NDArray对象。这个方法应该被一个池类重写,它管理从NDArray类派生的对象。

void NDArrayPool::emptyFreeList()	

emptyFreeList() 删除在空闲表中所有NDArrays。

size_t NDArrayPool::getMemorySize()	

getMaxMemory() 返回允许这个对象分配的内存最大字节数;0=无限。

size_t NDArrayPool::getMemorySize()

getMemorySize()返回这个对象当前已经分配的内存字节数。

int NDArrayPool::getNumBuffers()	

getBunBuffers()返回这个对象当前已经分配的缓存数目。

int NDArrayPool::getNumFree()	

getNumFree()返回这个空闲列表中NDArray对象的数目。

void NDArrayPool::onAllocateArray(NDArray * pArray)	

onAllocateArray()管理从NDArray类派生的对象的池类的hook。在分配新数组后调用这个hook。

参数

[in]pArray指向已经分配的NDArray对象的指针。

void NDArrayPool::onReleaseArray(NDArray * pArray)	

onRelaseArray()管理从NDArray类派生的对象的池类的hook。在数组被释放后,调用这个hook。

[in]pArray指向已经释放NDArray对象的指针。

void NDArrayPool::onReserveArray(NDArray *	pArray)

onReserveArray()管理从NDArray类派生的对象的池类的hook。在这个数组被保留后,调用这个hook。

参数[in]pArray:指向被保留的NDArray对象的指针。

int NDArrayPool::release(NDArray * pArray)

release()这个方法减少NDArray对象的引用次数。

参数[in]pArray:减少这个数组引用次数。

当引用次数达到0时,NDArray被放回到空闲列表。当从队列移除一个NDArray并且对它的处理结束时,插件必须调用release()。

int NDArrayPool::report(FILE * fp, int details)		

report()报告NDArrayPool对象的空闲列表尺寸和其它属性。

参数:

[in]fp:报告输出的文件指针。

[in]details:所需报告的详细程度;目前什么也没有。

int NDArrayPool::reserve(NDArray * 	pArray)

reserve()这个方法增加这个NDArray对象的引用次数。

参数:

[in]pArray:要增加引用次数的数组。当一个NDArray被放入一个队列用于之后处理时,插件必须调用reserve()。

NDAttribute

NDAttribute类,一个属性有一个名称,描述,来源类型,来源字符串,数据类型和值 。lNDAttribute是一个用于链接元数据到一个NDArray的类。一个NDAttribute有一个名称,描述,数据类型,值,来源类型和来源信息。由属性的名称标识属性,它们是区分大小写的。有设置和获取一个属性的信息的方法。

定义属性名称的一些规则,因而插件或数据分析程序可以查找一个特定的属性。以下是在当前插件中使用的属性规则:

标准属性名称的规程

属性名 描述 数据类型
ColorMode NDArray的颜色模式。被很多插件使用来确定如何处理这个数组。

NDAttrInt32

(NDColorMode_t)

BayerPattern 由彩色相机用一个Bayer掩码采集的图像的Bayer模式。可用于转换成一个RGB颜色图像。此功能可以被添加到NDPluginColorConvert。

NDAttrInt32

(NDBayerPattern_t)

DriverFilename 由驱动原先采集的文件的名称。如果置位了DeleteDriverFile并且NDArray已经在另一个文件中被成功地保存了,这由这NDPluginFile使用来删除原来地驱动文件。 NDAttrString

FilePlugin

Destination

这是由NDPluginFile使用,确定是否处理这个NDArray。如果这个属性出现了并且是"all",或者这个插件地名称,则NDArray被处理,否则它被忽略。 NDAttrString

FilePlugin

FileName

这是由NDPluginFile使用,当保存这个NDArray时,设置这个文件名。 NDAttrString

FilePlugin

FileNumber

这是由NDPluginFile使用,当保存这个NDArray时,设置这个文件号。 NDAttrInt32

FilePlugin

FileClose

这是由NDPluginFile使用,在处理完这个NDArray后,关闭这个文件。 NDAttrInt32

[HDF dataset

name]

这是由NDFileHDF5使用,这个NDArray应该被写入到这个文件中哪个数据集。属性名是这个HDF5数据集的名称。 NDAttrString
[posName] 这是由NDFileHDF5使用,确定这个NDArray应该被写到这个数据集的哪个位置。属性名被包含在NDFileHDF5.template中定义的PosName记录中。设计这个来允许例如"sname scan"数据按正确的顺序被放置在一个HDF5文件中。 NDAttrInt32

属性名是区分大小写的。对于不在这个表格中的属性,一个好规则是使用开头不带ND或AD,以及名称的每个单词第一个字符大写开始的相应的驱动参数。例如,ADManufacturer的标准属性名应该是"Manufacturer",ADNumExposures应该是"NumExposures"等。NDAttribute类文档详细地描述了这个类。

#ifndef NDAttribute_H
#define NDAttribute_H

#include 
#include 

#include 
#include 
#include 
#include 

/** 成功返回码 */
#define ND_SUCCESS 0
/** 出错返回码  */
#define ND_ERROR -1


/** NDArray数据类型的枚举 */
typedef enum
{
    NDInt8,     /**< 有符号8位整数  */
    NDUInt8,    /**< 无有符号8位整数 */
    NDInt16,    /**< 有符号16位整数 */
    NDUInt16,   /**< 无有符号16位整数 */
    NDInt32,    /**< 有符号32位整数 */
    NDUInt32,   /**< 无符号32位整数 */
    NDInt64,    /**< 有符号64位整数 */
    NDUInt64,   /**< 无符号64位整数*/
    NDFloat32,  /**< 32位浮点 */
    NDFloat64   /**< 64位浮点 */
} NDDataType_t;

/** NDAttibute属性数据类型的枚举 */
typedef enum
{
    NDAttrInt8    = NDInt8,     /**< 有符号8位整数 */
    NDAttrUInt8   = NDUInt8,    /**< 无有符号8位整数 */
    NDAttrInt16   = NDInt16,    /**< 有符号16位整数 */
    NDAttrUInt16  = NDUInt16,   /**< 无有符号16位整数 */
    NDAttrInt32   = NDInt32,    /**< 有符号32位整数 */
    NDAttrUInt32  = NDUInt32,   /**< 无符号32位整数 */
    NDAttrInt64   = NDInt64,    /**< 有符号64位整数 */
    NDAttrUInt64  = NDUInt64,   /**< 无符号64位整数 */
    NDAttrFloat32 = NDFloat32,  /**< 32位浮点 */
    NDAttrFloat64 = NDFloat64,  /**< 64位浮点 */
    NDAttrString,               /**< 动态长度字符串 */
    NDAttrUndefined             /**< 未定义的数据类型 */
} NDAttrDataType_t;

/** NDAttibute源类型的枚举 */
typedef enum
{
    NDAttrSourceDriver,    /**< 直接从驱动获取的属性  */
    NDAttrSourceParam,     /**< 从参数库获取的数 */
    NDAttrSourceEPICSPV,   /**< 从EPICS PV获取的属性 */
    NDAttrSourceFunct,     /**< 从用户指定的函数获取的属性  */
    NDAttrSourceUndefined  /**< 属性源是未定义的  */
} NDAttrSource_t;

/** 在一个属性对象中定义这些值的联合体 */
typedef union {
    epicsInt8    i8;    /**< 有符号8位整数 */
    epicsUInt8   ui8;   /**< 无有符号8位整数 */
    epicsInt16   i16;   /**< 有符号16位整数 */
    epicsUInt16  ui16;  /**< 无有符号16位整数 */
    epicsInt32   i32;   /**< 有符号32位整数 */
    epicsUInt32  ui32;  /**< 无符号32位整数 */
    epicsInt64   i64;   /**< 有符号64位整数 */
    epicsUInt64  ui64;  /**< 无符号64位整数 */
    epicsFloat32 f32;   /**< 32位浮点 */
    epicsFloat64 f64;   /**< 64位浮点 */
} NDAttrValue;

/**  
  * EPICS ellLib库使用这个结构体用于C++对象的链表。
  * C++对象的ellLists需要这个
  */
typedef struct NDAttributeListNode {
    ELLNODE node;
    class NDAttribute *pNDAttribute;
} NDAttributeListNode;

/** NDAttribute类:一个属性有一个名称,描述,源类型,源字符串,数据类型和值。
  */
class ADCORE_API NDAttribute {
public:
    /* 方法 */
    NDAttribute(const char *pName, const char *pDescription,
                NDAttrSource_t sourceType, const char *pSource, NDAttrDataType_t dataType, void *pValue);
    NDAttribute(NDAttribute& attribute);
    static const char *attrSourceString(NDAttrSource_t type);
    virtual ~NDAttribute();
    virtual NDAttribute* copy(NDAttribute *pAttribute);
    virtual const char *getName();
    virtual const char *getDescription();
    virtual const char *getSource();
    virtual const char *getSourceInfo(NDAttrSource_t *pSourceType);
    virtual NDAttrDataType_t getDataType();
    virtual int getValueInfo(NDAttrDataType_t *pDataType, size_t *pDataSize);
    virtual int getValue(NDAttrDataType_t dataType, void *pValue, size_t dataSize=0);
    virtual int getValue(std::string& value);
    virtual int setDataType(NDAttrDataType_t dataType);
    virtual int setValue(const void *pValue);
    virtual int setValue(const std::string&);
    virtual int updateValue();
    virtual int report(FILE *fp, int details);
    friend class NDArray;
    friend class NDAttributeList;


private:
    template  int getValueT(void *pValue, size_t dataSize); // 获取值
    std::string name_;              /**< 名称字符串 */
    std::string description_;       /**< 描述字符串 */
    NDAttrDataType_t dataType_;     /**< 属性的数据类型 */
    NDAttrValue value_;             /**< 除了字符串外,属性的值 */
    std::string string_;            /**< 字符串属性的值 */
    std::string source_;            /**< 表示来源的源字符串 - EPICS PV名称 or DRV_INFO字符串 */
    NDAttrSource_t sourceType_;     /**< 属性来源类型:NDAttrSourceDriver/从参数库获取的数/... */
    std::string sourceTypeString_;  /**< 来源类型的字符串 */
    NDAttributeListNode listNode_;  /**< 用于NDAttributeList */
};

#endif
NDAttribute(const char * pName, const char * pDescription, NDAttrSource_t sourceType, 
const char * pSource, NDAttrDataType_t dataType, void *pValue)	

参数:

  • [in] pName:要被创建地属性名称。
  • [in] sourceType:这个属性的来源类型(NDAttrSource_t)
  • [in] pSource:这个属性的来源字符串。
  • [in] pDescription:属性的描述。
  • [in] dataType:属性的数据类型(NDAttrDataType_t)
  • [in] pValue:指向这个属性的值的指针。
NDAttribute(NDAttribute & attribute)	

NDAttribute拷贝构造器。

参数[in]attribute:从这个属性复制。

~NDAttribute()	

NDAttribute析构器。

const char * NDAttribute::attrSourceString(NDAttrSource_t type)

设置NDAttribute对象的sourceType成员。

NDAttribute * NDAttribute::copy	(NDAttribute * pOut)	

从this复制属性到pOut。

参数:

[in]pOut:一个指向输出属性的指针,如果NULL,将使用复制构造器创建这个输出属性。仅拷贝值,认为所有其它字段在pOut中已经相同。

返回:返回一个指向这个副本的指针。

在PVAttribte, paramAttribute和functAttribtue中被重新实现。

NDAttrDataType_t NDAttribute::getDataType()	

返回这个属性的数据类型。

const char * NDAttribute::getDescription()	

返回这个属性的描述。

const char * NDAttribute::getName()	

返回这个属性的名称。

const char * NDAttribute::getSource()	

返回这个属性的来源字符串。

const char * NDAttribute::getSourceInfo(NDAttrSource_t * pSourceType)	

返回这个属性的来源信息。

参数[out]:pSourceType这个属性的来源类型(NDAttrSource_t)。

返回:这个属性的来源类型字符串。

int NDAttribute::getValue(NDAttrDataType_t 	dataType, void * pValue, size_t dataSize = 0)	

返回这个属性的值。

参数:

[in] dataType:这个值的数据类型。

[out] pValue:指向返回这个值的位置的指针。

[in] dataSize:输入数据位置的大小,仅在dataType是NDAttrString时,才使用。

进行在数值数据类型之间的数据类型转换。

int NDAttribute::getValue(std::string & value)	

以一个std::string返回一个NDAttrString属性的值。

参数:

[out]value:返回这个值的位置。

在数值数据类型之间进行数据类型转换。

int NDAttribute::getValueInfo(NDAttrDataType_t * pDataType, size_t * pSize)

返回这个属性的数据类型和大小。

参数:

[out]pDataType:指向返回数据类型的位置的指针。

[out]pSize:指向返回数据尺寸的位置的指针;这是除了NDAttrString外所有数据类型的数据类型尺寸,在NDAttrString情况下,它是这个字符串包括0终止符的长度。

int NDAttribute::report(FILE * fp, int details)	

报告这个属性(attribute)的属性(properties)。

参数:

[in]fp:用于报告输出的文件指针。

[in]details:所需的报告详细程度;当前什么也不做。

在PVAttribute, paramAttribute和functAttribute中重新实现。

int NDAttribute::setDataType(NDAttrDataType_t type)	

设置这个属性的数据类型。这只能被调用一次。

int NDAttribute::setValue(const std::string & value)	

为这个属性设置值。

参数:

[in]value:这个属性的值。

int NDAttribute::setValue(const void * pValue)	

为这个属性设置值。

参数:

[in]pValue:指向这个值的指针。

int NDAttribute::updateValue()

更新这个属性的当前值。

这个基类不做任何事情,但派生类可以获取这个属性的当前值,例如从一个EPICS PV或者驱动参数库。在PVAttribute, paramAttribute和functAttribute中重新实现。

NDAttributeList

NDAttributeList实现了NDAttribute对象的链表。NDArray对象包含一个NDAttributeList,其是属性如何关联一个NDArray的方法。有添加,删除和搜索在一个NDAttributeList中NDAttribute对象的方法。在这个表中的每个属性必须有一个唯一名称,它是区分大小写的。

当用NDArrayPool方法复制NDArrays时,也复制了这个属性列表。

重要注释:当使用NDArrayPool::alloc()分配一个新NDArray时,获取自这个池的NDArray上任何已有属性列表的行为由全局变量eraseNDAtrriubtes的值确定。默认,这个值是0。这表示当从这个池分配一个新NDArray时,它的属性列表未被清除。这大大提高了在正常情况下的效率,在正常情况下,一个指定驱动的属性在初始化时一旦被定义,并且从不会被删除。(属性值当然可以变换)。它消除了每次从这个池分配一个数组时,分配和取消分配属性内存。添加新属性到这个数组也是可能的,但即使已有属性表面上被清除,它们将继续存在,例如asynNDArrayDriver::readNDAttributesFiles()被再次调用。如果每次分配一个新数组时,需要从NDArrays清除所有已有属性,则全局变量eraseNDAttributes应该设置为1。在iocsh提示中用以下命令做这件事:

var eraseNDAttributes 1

NDAttriubteList类文档详细地描述这个类。这是一个属性的链表。

#ifndef NDAttributeList_H
 #define NDAttributeList_H
  
 #include 
 #include 
 #include 
  
 #include "NDAttribute.h"
  
  
 class ADCORE_API NDAttributeList {
 public:
     NDAttributeList();
     ~NDAttributeList();
     int          add(NDAttribute *pAttribute);
     NDAttribute* add(const char *pName, const char *pDescription="",
                      NDAttrDataType_t dataType=NDAttrUndefined, void *pValue=NULL);
     NDAttribute* find(const char *pName);
     NDAttribute* next(NDAttribute *pAttribute);
     int          count();
     int          remove(const char *pName);
     int          clear();
     int          copy(NDAttributeList *pOut);
     int          updateValues();
     int          report(FILE *fp, int details);
  
 private:
     ELLLIST      list_;   
     epicsMutexId lock_;  
 };
  
 #endif
NDAttributeList::NDAttributeList()

NDAttributeList构造器。

NDAttribute * NDAttributeList::add(const char * pName, const char * 	pDescription = "", NDAttrDataType_t dataType = NDAttrUndefined, void * pValue = NULL)

添加一个属性到列表。这是一个用于添加属性到一个列表的快捷方式。它首先搜索这个列表查找是否有一个相同名称的已有属性。如果有,它只是更改已有属性(attributes)的属性(properties)。如果没有,它用指定的属性(properties)创建一个新属性(attribute)。重要:这个方法仅能够创建NDAttributes基类类型的属性,不能创建派生类属性。要添加一个派生类的属性到一个列表,必须使用NDAttributeList::add(NDAttribute*)。

参数:

[in]pName:要添加的属性的名称。

[in]pDescription:这个属性的描述。

[in]dataType:这个属性的数据类型。

[in]pValue:一个指向这个属性的值的指针。

int NDAttributeList::add(NDAttribute * 	pAttribute)

添加一个属性到这个列表。如果一个相同名称的属性已经存在,则已有属性被删除并且用这个新属性替代。

参数:

[in]pAttribute:一个指向要添加属性的指针。

int NDAttributeList::clear()

从这个列表删除所有属性。

int NDAttributeList::copy(NDAttributeList * pListOut)

从一个属性表复制所有属性到另一个属性表。它是高效的,使得如果属性(attribute)已经存在于输出表,它仅复制属性(properties),并且使得内存分配最少。属性被添加到在输出表中已经存在的任何已有属性。

参数

[out]pListOut:一个指向要复制到的输出属性表的指针。

int NDAttributeList::count()

返回属性列表中属性(attribute)总数。

返回:返回属性数目。

NDAttribute * NDAttributeList::find(const char * pName)

通过名称查找属性,搜索现在是区分大小写。

参数:

[in]pName:要被查找的属性名。

返回:如果找到返回一个指向这个属性的指针,没有找到,NULL。

NDAttribute * NDAttributeList::next	(NDAttribute * 	pAttributeIn)	

查找属性链表中下一个属性。

参数:

[in]pAttributeIn:一个指向链表中前个属性的指针;如果NULL,返回链表中第一个属性。

返回:如果有,返回指向下个属性的指针,如果在链表中没有更多属性,返回NULL。

int NDAttributeList::remove	(const char * pName)	

从链表输出一个属性。

参数:

[in]pName:要被删除的属性的名称。

返回:如果属性被找到并且被删除,返回ND_SUCCESS,如果没有找到这个属性,ND_ERROR。

int NDAttributeList::report(FILE * fp, int details)	

报告这个属性(attribute)链表的属性(properties)。

参数:

[in]fp:用于报告输出的文件指针。

[in]details:所需的报告详细程度。如果>10,对每个属性,调用NDAttribute::report()。

int NDAttributeList::updateValues()	

更新链表中所有属性值;为链表中每个属性调用NDAttribute::updateValue()。

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