# areaDetector Plugins

## August 16, 2009

## Mark Rivers

## University of Chicago

## Contents

- Overview
- NDPluginDriver
- Guidelines and rules for plugins
- NDPluginStdArrays
- NDPluginFile
- NDPluginROI
- NDPluginColorConvert

## Overview

A powerful feature of the areaDetector module is the concept of plugins. A plugin is code that is called by a driver that passes NDArray data in a callback. Plugins can be used to process array data in real time. Existing plugins convert data to standard asyn arrays (NDPluginStdArrays), save data to disk (NDPluginFile), select regions-of-interest (NDPluginROI), and convert color modes (NDPluginColorConvert). New plugins could be written to perform functions like finding the centroid of a beam, etc. Once a plugin is written it will work with any areaDetector driver. Plugins have the the following properties:

- They can execute either in a blocking mode or a non-blocking mode. In the blocking mode the callback is executed by the driver callback thread. In this mode the callback is guaranteed to execute for each NDArray callback. However, it can slow down the driver, and does not utilize the multi-core capability of modern CPUs. In the non-blocking mode the driver callback simply places the NDArray data in a queue that is part of the plugin. The plugin then executes the callback code in it own thread. It removes NDArray data from the queue, processes it, and releases the data back to the NDArrayPool when it is done. In the non-blocking mode some additional memory is required for the NDArray objects that are in the queue. It is also possible to drop NDArray data if the queue is full when a callback occurs, i.e. some callback data will not be processed. The non-blocking mode can utilize the multi-core capabilities of modern CPUs because each plugin is executing in its own thread. The operation of the queue and the NDArrayPool class means that data never needs to be copied, each plugin has a pointer to the data which will continue to be valid until the last plugin is done with it.
- They can be enabled or disabled at run time.
- They can be throttled to only execute at a limited rate. This means, for example, that a detector can be saving data to disk at full speed, but images can be posted to EPICS at a reduced rate.
- They can be unplugged from one driver, and plugged into another driver at run time. For example, the NDPluginROI driver is itself a source of NDArray data callbacks, so a file saving plugin could be unplugged from a detector driver (where it would be saving the entire detector), and plugged into a particular ROI, where it would just save a portion of the detector. Similarly the NDPluginColorConvert plugin is also a source of NDArray data callbacks. A pipeline of plugins can be constructed, for example NDPluginColorConvert->NDPluginROI->NDPluginStdArrays. Each stage of this pipeline can be executing in its own thread, and on modern multi-core processors each can be executing on its own core.

## NDPluginDriver

NDPluginDriver inherits from asynNDArrayDriver. NDPluginDriver is the class from which actual plugins are directly derived. The NDPluginDriver class handles most of the details of processing NDArray callbacks from the driver. Plugins derived from this class typically need to implement the processCallbacks method, and one or more of the write(Int32, Float64, Octet) methods. The NDPluginDriver class documentation describes this class in detail.

NDPluginDriver defines parameters that all plugin drivers should implement if possible. These parameters are defined by strings (drvInfo strings in asyn) with an associated asyn interface, and access (read-only or read-write). The EPICS database NDPluginBase.template provides access to these standard plugin parameters, listed in the following table. Note that to reduce the width of this table the parameter index variable names have been split into 2 lines, but these are just a single name, for example `NDPluginDriverArrayPort`.

Parameter Definitions in NDPluginDriver.h and EPICS Record Definitions in NDPluginBase.template
Parameter index variable | asyn interface | Access | Description | drvInfo string | EPICS record name | EPICS record type
--- | --- | --- | --- | --- | --- | ---
Information about this plugin
NDPortNameSelf | asynOctet | r/o | asyn port name | PORT_NAME_SELF | $(P)$(R)PortName_RBV | stringin
NDPluginDriver PluginType | asynOctet | r/o | A string describing the plugin type. | PLUGIN_TYPE | $(P)$(R)PluginType_RBV | stringin
asyn NDArray driver doing callbacks
NDPluginDriver ArrayPort | asynOctet | r/w | asyn port name for NDArray driver that will make callbacks to this plugin. This port can be changed at run time, connecting the plugin to a different NDArray driver. | NDARRAY_PORT | $(P)$(R)NDArrayPort (P)$(R)NDArrayPort_RBV | stringout stringin
NDPluginDriver ArrayAddr | asynInt32 | r/w | asyn port address for NDArray driver that will make callbacks to this plugin. This address can be changed at run time, connecting the plugin to a different address in the NDArray driver. | NDARRAY_ADDR | $(P)$(R)NDArrayAddress $(P)$(R)NDArrayAddress_RBV | longout longin
Callback enable, minimum time, and statistics
NDPluginDriver EnableCallbacks | asynInt32 | r/w | Enable (1) or disable (0) callbacks from the driver to this plugin. If callbacks are disabled then the plugin will normally be idle and consume no CPU resources. | ENABLE_CALLBACKS | $(P)$(R)EnableCallbacks $(P)$(R)EnableCallbacks_RBV | bo bi
NDPluginDriver BlockingCallbacks | asynInt32 | r/w | 0 = callbacks from the driver do not block; the NDArray data is put on a queue and the callback processes in its own thread. 1 = callbacks from the driver block; the callback processes in the driver callback thread. | BLOCKING_CALLBACKS | $(P)$(R)BlockingCallbacks $(P)$(R)BlockingCallbacks_RBV | bo bi
NDPluginDriver MinCallbackTime | asynFloat64 | r/w | The minimum time in seconds between calls to processCallbacks. Any callbacks occuring before this minimum time has elapsed will be ignored. 0 means no minimum time, i.e. process all callbacks. | MIN_CALLBACK_TIME | $(P)$(R)MinCallbackTime $(P)$(R)MinCallbackTime_RBV | ao ai
NDPluginDriver ArrayCounter | asynInt32 | r/w | Counter that increments by 1 each time an NDArray callback is processed | ARRAY_COUNTER | $(P)$(R)ArrayCounter $(P)$(R)ArrayCounter_RBV | longout longin
N/A | N/A | r/o | Rate (Hz) at which ArrayCounter is incrementing. Computed in database. | N/A | $(P)$(R)ArrayRate_RBV | calc
NDPluginDriver DroppedArrays | asynInt32 | r/w | Counter that increments by 1 each time an NDArray callback occurs when NDPluginDriverBlockingCallbacks=0 and the plugin driver queue is full, so the callback cannot be processed. | DROPPED_ARRAYS | $(P)$(R)DroppedArrays $(P)$(R)DroppedArrays_RBV | longout longin
Information about last NDArray callback data
NDPluginDriver NDimensions | asynInt32 | r/o | Number of dimensions in last NDArray callback data | ARRAY_NDIMENSIONS | $(P)$(R)NDimensions_RBV | longin
NDPluginDriver Dimensions | asynInt32Array | r/o | Dimensions in last NDArray callback data | ARRAY_DIMENSIONS | $(P)$(R)Dimensions_RBV | waveform
N/A | N/A | r/o | First dimension of NDArray callback data | N/A | $(P)$(R)ArraySize0_RBV | longin
N/A | N/A | r/o | Second dimension of NDArray callback data | N/A | $(P)$(R)ArraySize1_RBV | longin
N/A | N/A | r/o | Third dimension of NDArray callback data | N/A | $(P)$(R)ArraySize2_RBV | longin
NDPluginDriver DataType | asynInt32 | r/o | Data type of last NDArray callback data (NDDataType_t). | DATA_TYPE | $(P)$(R)DataType_RBV | mbbi
NDPluginDriver ColorMode | asynInt32 | r/o | Color mode of last NDArray callback data (NDColorMode_t). | COLOR_MODE | $(P)$(R)ColorMode_RBV | mbbi
NDPluginDriver BayerPattern | asynInt32 | r/o | BayerPattern of last NDArray callback data (NDBayerPattern_t). | BAYER_PATTERN | $(P)$(R)BayerPattern_RBV | mbbi
NDPluginDriver UniqueId | asynInt32 | r/o | Unique ID number of last NDArray callback data | UNIQUE_ID | $(P)$(R)UniqueId_RBV | longin
NDPluginDriver TimeStamp | asynFloat64 | r/o | Time stamp number of last NDArray callback data | TIME_STAMP | $(P)$(R)TimeStamp_RBV | ai
Array attributes
NDAttributesFile | asynOctet | r/w | The name of an XML file defining the PVAttributes and paramAttributes to be added to each NDArray by this plugin. The format of the XML file is described in the documentation for asynNDArrayDriver::readNDAttributesFile(). | ND_ATTRIBUTES_FILE | $(P)$(R)NDAttributesFile | waveform
Debugging control
N/A | N/A | N/A | asyn record to control debugging (asynTrace) | N/A | $(P)$(R)AsynIO | asyn

## Guidelines and rules for plugins

The following are guidelines and rules for writing plugins

- Plugins will almost always implement the processCallbacks() function. This function will be called with an NDArray pointer each time an NDArray callback occurs. This function will normally call the NDPluginDriver::processCallbacks() base class function, which handles tasks common to all plugins, including callbacks with information about the array, etc.
- Plugins will generally implement one or more of the writeInt32(), writeFloat64() or writeOctet() functions if they need to act immediately on a new value of a parameter. For many parameters it is normally sufficient to simply have them written to the parameter library, and not to handle them in the writeXXX() functions. The parameters are then retrieved from the parameter library with the getIntParam(), getDoubleParam(), or getStringParam() function calls when they are needed.
- If the writeInt32(), writeFloat64() or writeOctet() functions are implemented they must call the base class function for parameters that they do not handle and whose parameter index value is less than the first parameter of this class, i.e. parameters that belong to a base class.
- Plugins will need to call the createParam() function in their constructor if they have additional parameters beyond those in the asynPortDriver or NDPluginDriver base classes.
- Plugins may never modify the NDArray that they receive in the processCallbacks() function. The reason is that other plugins may be concurrently operating on the same NDArray, since each is passed the same pointer. This means also that when getting the attributes for this plugin that asynNDArrayDriver::getAttributes(pArray->pAttributeList) must not be called with the NDArray passed to processCallbacks(), because that will modify the NDArray attribute list, and hence the NDArray that other plugins are operating on. Plugins such as NDPluginROI and NDPluginColorConvert create new NDArrays via NDArrayPool::copy() or NDArrayPool::convert() (which copy the attributes to the new array) and then call getAttributes(pArray->pAttributeList) with the new array. The NDPluginFile makes a copy of the attribute list from the original NDArray before calling getAttributes(), but does not need to make a copy of the NDArray because it does not modify it.
- Plugins must release their mutex by calling this->unlock() when they do time-consuming operations. If they do not then they will not be able to queue new NDArrays callbacks or obtain new parameter values. Obviously they must mot access memory locations that other threads could modify during this time, so they should only access local variables, not class variables (which includes the parameter library).
- If plugins generate new or modified NDArrays then they must call doCallbacksGenericPointer() so that registered clients can get the values of the new arrays.