在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平面探测器和很多别的探测器。
areaDetector模块的架构如下展示。
这个架构从下自上由以下构成:
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++编写。
探测器驱动和插件是asyn端口驱动,意味着它们实现了一个或多个标准的asyn接口。它们注册自己为中断源,因而当值变换时,它们对注册的asyn客户端进行回调。它们继承自在asyn模块中提供的基类C++类。那个基类处理注册这个端口驱动,注册支持的接口以及注册所需中断源的所有细节。它也提供了用于被此驱动中定义的整数值索引的int,double和string参数的参数库。参数库提供了写和读取参数值的方法,和在一个参数值变换时执行对注册客户端的回调。asynPortDriver类文档详细地描述了这个类asynPortDriver类。
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;
公共属性:
/*
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类挂你一个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类,一个属性有一个名称,描述,来源类型,来源字符串,数据类型和值 。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)
参数:
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实现了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()。