diff --git a/documentation/areaDetectorDoc.html b/documentation/areaDetectorDoc.html index 5e072ca..e88be26 100755 --- a/documentation/areaDetectorDoc.html +++ b/documentation/areaDetectorDoc.html @@ -21,20 +21,22 @@ Overview
  • Architecture -
  • - asynParamBase +
  • + asynPortDriver
  • NDArray -
  • - asynNDArrayBase -
  • - ADDriverBase +
  • + NDArrayPool +
  • + asynNDArrayDriver +
  • + ADDriver
  • simDetector
  • Prosilica driver -
  • - NDPluginBase +
  • + NDPluginDriver
  • NDPluginStdArrays
  • @@ -129,7 +131,7 @@ From the bottom to the top this architecture consists of the following:

    of a socket protocol to a driver, a Microsoft COM interface, etc.
  • Layer 2. This is the driver that is written for the area detector application to control a particular detector. It is normally written in C++ and inherits from the - ADDriverBase class. It uses the standard asyn interfaces for control and status + ADDriver class. It uses the standard asyn interfaces for control and status information. Each time it receives a new data array it passes it as an NDArray object to all Layer 3 clients that have registered for callbacks. This is the only code that needs to be written to implement a new detector. Existing drivers range from @@ -139,7 +141,7 @@ From the bottom to the top this architecture consists of the following:

    file saving (NDPluginFile), region-of-interest (ROI) calculations (NDPluginROI), and conversion of detector data to standard EPICS array types for use by Channel Access clients (NDPluginStdArrays). Plugins are normally written in C++ and - inherit from NDPluginBase. Existing plugins range from 280 to 550 lines + inherit from NDPluginDriver. Existing plugins range from 280 to 550 lines of code.
  • Layer 4. This is standard asyn device support that comes with the EPICS asyn module.
  • Layer 5. These are standard EPICS records, and EPICS database (template) files that @@ -158,7 +160,7 @@ code.
  • asyn. asyn is a module that provides interthread messaging services, including queueing and callbacks. -In particular it is possible to eliminates layers 4-6 in the architecture shown in Figure 1, providing +In particular it is possible to eliminate layers 4-6 in the architecture shown in Figure 1, providing there is a programs such as the high-performance GUI shown in Layer 3. This means that it is not necessary to run an EPICS IOC or to use EPICS Channel Access when using the drivers and plugins at Layers 2 and 3.

    @@ -178,17 +180,17 @@ the vendor API has the ability to save the data to a file. What follows is a detailed description of the software, working from the bottom up. Most of the code is object oriented, and written in C++. The parts of the code that depend on anything from EPICS except libCom and asyn have been kept in in separate C files, so that -it is easy to build applications that do not run as part of an EPICS IOC. +it should be easy to build applications that do not run as part of an EPICS IOC.

     

    -

    - asynParamBase

    +

    + asynPortDriver

    The areaDetector module depends heavily on asyn. It is the software that is used for interthread communication, using the standard asyn interfaces (e.g. asynInt32, asynOctet, etc.), and callbacks. Detector drivers and plugins are asyn port drivers, meaning that they implement one or more of the standard asyn interfaces. They register themselves as interrupt sources, so that they do callbacks to registered asyn clients when values change. -asynParamBase handles all of the details of registering the port driver, registering the supported interfaces, +asynPortDriver handles all of the details of registering the port driver, registering the supported interfaces, and registering the required interrupt sources.

    Drivers and plugins each need to support a number of parameters that control their operation and provide @@ -203,21 +205,35 @@ new value (e.g. image size in the X direction). The sequence of operations in t

  • For each parameter whose value changes set a flag noting that it changed.
  • When operation is complete, call the registered callbacks for each changed parameter. -asynParamBase provides methods to simplify the above sequence, which must be -implemented for the many parameters that the driver supports. Each parameter is assigned +asynPortDriver provides methods to simplify the above sequence, which must be +implemented for each of the many parameters that the driver supports. Each parameter is assigned a number, which is the value in the pasynUser-> reason field that asyn clients pass to -the driver when reading or writing that parameter. asynParamBase maintains a table +the driver when reading or writing that parameter. asynPortDriver maintains a table of parameter values, associating each parameter number with a data type (integer, double, or string), caching the current value, and maintaining a flag indicating if a value has changed. -Drivers use asynParamBase methods to read the current value +Drivers use asynPortDriver methods to read the current value from the table, and to set new values in the table. There is a method to call all registered callbacks for values that have changed since callbacks were last done. -

    The following is the definition of the asynParamBase class: +

    The following are the public definitions in the asynPortDriver class:

    -class asynParamBase {
    +#define asynCommonMask          0x00000001
    +#define asynDrvUserMask         0x00000002
    +#define asynOptionMask          0x00000004
    +#define asynInt32Mask           0x00000008
    +#define asyUInt32DigitalMask    0x00000010
    +#define asynFloat64Mask         0x00000020
    +#define asynOctetMask           0x00000040
    +#define asynInt8ArrayMask       0x00000080
    +#define asynInt16ArrayMask      0x00000100
    +#define asynInt32ArrayMask      0x00000200
    +#define asynFloat32ArrayMask    0x00000400
    +#define asynFloat64ArrayMask    0x00000800
    +#define asynGenericPointerMask  0x00001000
    +
    +class asynPortDriver {
     public:
    -    asynParamBase(const char *portName, int maxAddr, int paramTableSize, int interfaceMask, int interruptMask);
    +    asynPortDriver(const char *portName, int maxAddr, int paramTableSize, int interfaceMask, int interruptMask);
         virtual asynStatus getAddress(asynUser *pasynUser, const char *functionName, int *address); 
         virtual asynStatus findParam(asynParamString_t *paramTable, int numParams, const char *paramName, int *param);
         virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
    @@ -259,9 +275,9 @@ public:
                                             size_t nElements);
         virtual asynStatus doCallbacksFloat64Array(epicsFloat64 *value,
                                             size_t nElements, int reason, int addr);
    -    virtual asynStatus readHandle(asynUser *pasynUser, void *handle);
    -    virtual asynStatus writeHandle(asynUser *pasynUser, void *handle);
    -    virtual asynStatus doCallbacksHandle(void *handle, int reason, int addr);
    +    virtual asynStatus readGenericPointer(asynUser *pasynUser, void *pointer);
    +    virtual asynStatus writeGenericPointer(asynUser *pasynUser, void *pointer);
    +    virtual asynStatus doCallbacksGenericPointer(void *pointer, int reason, int addr);
         virtual asynStatus drvUserCreate(asynUser *pasynUser, const char *drvInfo, 
                                          const char **pptypeName, size_t *psize);
         virtual asynStatus drvUserGetType(asynUser *pasynUser,
    @@ -287,14 +303,6 @@ public:
         virtual asynStatus callParamCallbacks(int list, int addr);
         virtual void reportParams();
     
    -    char *portName;
    -    int maxAddr;
    -    paramList **params;
    -    epicsMutexId mutexId;
    -
    -    /* The asyn interfaces this driver implements */
    -    asynStandardInterfaces asynStdInterfaces;
    -    
         /* asynUser connected to ourselves for asynTrace */
         asynUser *pasynUser;
     };
    @@ -305,12 +313,12 @@ example driver (simDetector) and plugins provided with areaDetector for examples
     class is used.
     
     
    -    asynParamBase(const char *portName, int maxAddr, int paramTableSize, int interfaceMask, int interruptMask);
    +    asynPortDriver(const char *portName, int maxAddr, int paramTableSize, int interfaceMask, int interruptMask);
     
    This is the constructor for the class.
    @@ -347,11 +355,11 @@ Derived classed typically do not need to implement these methods.
         virtual asynStatus writeOctet(asynUser *pasynUser, const char *value, size_t maxChars,
                               size_t *nActual);
     
    -These methods are called by asynClients to set the new value of a parameter. These -methods only have stub methods that return an error in asynParamBase, so they -must be implemented in the derived classes if the corresponding interface is -used. They are not pure virtual functions so that the derived class need not implement -the interface if it is not used. +These methods are called by asynClients to set the new value of a parameter. The +implementation of these methods in asynPortDriver copies the parameter into a cached +location for use by the asynRead(Int32, Float64, and Octet) methods. Most drivers +will provide their own implementations of these methods to do driver-dependent operations +when there is a new value of the parameter.
         
    @@ -361,15 +369,15 @@ the interface if it is not used.
                                             size_t nElements);
         virtual asynStatus doCallbacksXXXArray(epicsInt8 *value,
                                             size_t nElements, int reason, int addr);
    -    virtual asynStatus readHandle(asynUser *pasynUser, void *handle);
    -    virtual asynStatus writeHandle(asynUser *pasynUser, void *handle);
    -    virtual asynStatus doCallbacksHandle(void *handle, int reason, int addr);
    +    virtual asynStatus readGenericPointer(asynUser *pasynUser, void *handle);
    +    virtual asynStatus writeGenericPointer(asynUser *pasynUser, void *handle);
    +    virtual asynStatus doCallbacksGenericPointer(void *handle, int reason, int addr);
     
    where XXX=(Int8, Int16, Int32, Float32, or Float64). -The readXXX and writeXXX methods only have stub methods that return an error in asynParamBase, so they +The readXXX and writeXXX methods only have stub methods that return an error in asynPortDriver, so they must be implemented in the derived classes if the corresponding interface is used. They are not pure virtual functions so that the derived class need not implement -the interface if it is not used. The doCallbacksXXX methods in asynParamBase +the interface if it is not used. The doCallbacksXXX methods in asynPortDriver call any registered asyn clients on the corresponding interface if the reason and addr values match. It typically does not need to be implemented in derived classes. @@ -382,7 +390,7 @@ and addr values match. It typically does not need to be implemente const char **pptypeName, size_t *psize); virtual asynStatus drvUserDestroy(asynUser *pasynUser);
    -drvUserCreate must be implemented in derived classes that use the parameter facilities of asynParamBase. +drvUserCreate must be implemented in derived classes that use the parameter facilities of asynPortDriver. The findParam method is a convenience function that searches an array of {enum, string} structures and returns the enum (parameter number) matching the string. This is typically used in the implementation of drvUserCreate in derived classes. drvUserGetType and @@ -440,6 +448,7 @@ to plugins. The NDArray class is defined as follows:
     #define ND_ARRAY_MAX_DIMS 10
     
    +/* Enumeration of array data types */
     typedef enum
     {
         NDInt8,
    @@ -452,6 +461,7 @@ typedef enum
         NDFloat64
     } NDDataType_t;
     
    +
     typedef struct NDDimension {
         int size;
         int offset;
    @@ -486,14 +496,12 @@ public:
         NDArray();
         int          initDimension   (NDDimension_t *pDimension, int size);
         int          getInfo         (NDArrayInfo_t *pInfo);
    -    int          convertDimension(NDArray *pOut,
    -                                  void *pDataIn,
    -                                  void *pDataOut,
    -                                  int dim);
         int          copy            (NDArray *pOut);
         int          reserve(); 
         int          release();
     };
    +
    +
     
    An NDArray is a general purpose class for handling array data. An NDArray object is self-describing, meaning it contains enough information to describe the data itself. It is not intended to @@ -549,21 +557,255 @@ The remaining data fields are as follows: The methods of the NDArray class are: +

    + NDArrayPool

    + +The NDArrayPool class manages a free list (pool) of NDArray objects (described above). Drivers allocate NDArray objects +from the pool, and pass these objects to plugins. Plugins increase the reference count on the object when they +place the object on their queue, and decrease the reference count when they are done processing the array. When +the reference count reaches 0 again the NDArray object is placed back on the free list. This mechanism minimizes the +copying of array data in plugins. +The public interface of the NDArrayPool class is defined as follows: + +
    +class NDArrayPool {
    +public:
    +                 NDArrayPool   (int maxBuffers, size_t maxMemory);
    +    NDArray*     alloc         (int ndims, int *dims, NDDataType_t dataType, int dataSize, void *pData);
    +    int          reserve       (NDArray *pArray); 
    +    int          release       (NDArray *pArray);
    +    int          convert       (NDArray *pIn, 
    +                                NDArray **ppOut,
    +                                NDDataType_t dataTypeOut,
    +                                NDDimension_t *outDims);
    +    int          report        (int details);     
    +
    + +The methods of the NDArrayPool class are: + +

    + asynNDArrayDriver

    + +asynNDArrayDriver inherits from asynPortDriver. It implements the asynGenericPointer functions, assuming that +these reference NDArray objects. This is the class from which both plugins and area detector drivers are +indirectly derived. +Its public interface is defined as follows: +
    +class asynNDArrayDriver : public asynPortDriver {
    +public:
    +    asynNDArrayDriver(const char *portName, int maxAddr, int paramTableSize, int maxBuffers, size_t maxMemory,
    +                      int interfaceMask, int interruptMask);
    +    virtual asynStatus readGenericPointer(asynUser *pasynUser, void *genericPointer);
    +    virtual asynStatus writeGenericPointer(asynUser *pasynUser, void *genericPointer);
    +    virtual void report(FILE *fp, int details);
     
    +};
    +
    +The methods of the asynNDArrayDriver class are: + +

    + ADDriver

    + +ADDriver inherits from asynNDArrayDriver. This is the class from which area detector drivers are directly derived. +Its public interface is defined as follows: +
    +class ADDriver : public asynNDArrayDriver {
    +public:
    +    ADDriver(const char *portName, int maxAddr, int paramTableSize, int maxBuffers, size_t maxMemory,
    +             int interfaceMask, int interruptMask);
    +                 
    +    /* These are the methods that we override from asynPortDriver */
    +     virtual asynStatus drvUserCreate(asynUser *pasynUser, const char *drvInfo, 
    +                                     const char **pptypeName, size_t *psize);
    +                                     
    +    /* These are the methods that are new to this class */
    +    int createFileName(int maxChars, char *fullFileName);
    +
    +The methods of the ADDriver class are: + +

    + simDetector

    + +simDetector is a driver for a simulated area detector. It inherits from ADDriver. The simulation detector implements +nearly all of the parameters defined in ADStdDriverParams.h. It also implements a few parameters that are specific +to the simulation detector. The simulation detector is useful as a model for writing real detector drivers. It is +also very useful for testing plugins and channel access clients. +This is part of the definition of the simDetector class: +
    +class simDetector : public ADDriver {
    +public:
    +    simDetector(const char *portName, int maxSizeX, int maxSizeY, NDDataType_t dataType,
    +                int maxBuffers, size_t maxMemory);
    +                 
    +    /* These are the methods that we override from ADDriver */
    +    virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
    +    virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
    +    virtual asynStatus drvUserCreate(asynUser *pasynUser, const char *drvInfo, 
    +                                     const char **pptypeName, size_t *psize);
    +    void report(FILE *fp, int details);
    +
    +The portName, maxBuffers, and maxMemory arguments are passed to the base class constructors. The maxSizeX, maxSizeY, and +dataType arguments are specific to the simulation driver, controlling the maximum image size and initial data type of the +computed images. The writeInt32 and writeFloat64 methods override those in the base class. The driver takes action +when new parameters are passed via those interfaces. For example, the ADAcquire parameter (on the asynInt32 interface) is +used to turn acquisition (i.e. computing new images) on and off. +

    +The simulation driver initially sets the image[i, j] = i*gainX + j*gainY * gain * exposureTime * 1000. Thus the +image is a linear ramp in the X and Y directions, with the gains in each direction being detector-specific parameters. +Each subsquent acquisition increments each pixel value by gain*exposureTime*1000. Thus if gain=1 and exposureTime=.001 +second then the pixels are incremented by 1. If the array is an unsigned 8 or 16 bit integer then the pixels +will overflow and wrap around to 0 after some period of time. This gives the appearance of bands that appear to move +with time. The slope of the bands and their periodicity can be adjusted by changing the gains and exposure times. +

    +The driver creates a thread that waits for a signal to start acquisition. When acquisition is started that thread +computes new images and then calls back any registered plugins as follows: +

    +        /* Put the frame number and time stamp into the buffer */
    +        pImage->uniqueId = imageCounter;
    +        pImage->timeStamp = startTime.secPastEpoch + startTime.nsec / 1.e9;
    +        
    +        /* Call the NDArray callback */
    +        /* Must release the lock here, or we can get into a deadlock, because we can
    +         * block on the plugin lock, and the plugin can be calling us */
    +        epicsMutexUnlock(this->mutexId);
    +        asynPrint(this->pasynUser, ASYN_TRACE_FLOW, 
    +             "%s:%s: calling imageData callback\n", driverName, functionName);
    +        doCallbacksGenericPointer(pImage, NDArrayData, addr);
    +        epicsMutexLock(this->mutexId);
    +
    +The 3 driver-specific parameters are defined in the driver as follows: +
    +/* If we have any private driver parameters they begin with ADFirstDriverParam and should end
    +   with ADLastDriverParam, which is used for setting the size of the parameter library table */
    +typedef enum {
    +    SimGainX 
    +        = ADFirstDriverParam,
    +    SimGainY,
    +    SimResetImage,
    +    ADLastDriverParam
    +} SimDetParam_t;
     
    +static asynParamString_t SimDetParamString[] = {
    +    {SimGainX,          "SIM_GAINX"},  
    +    {SimGainY,          "SIM_GAINY"},  
    +    {SimResetImage,     "RESET_IMAGE"},  
    +};
     
    +#define NUM_SIM_DET_PARAMS (sizeof(SimDetParamString)/sizeof(SimDetParamString[0]))
    +
    +The drvUserCreate function first checks to see if the parameter is one of these +3 parameters that are unique to the simulation driver. If not it checks to +see if it is a standard parameter by calling the ADDriver base class method. +
    +asynStatus simDetector::drvUserCreate(asynUser *pasynUser,
    +                                      const char *drvInfo, 
    +                                      const char **pptypeName, size_t *psize)
    +{
    +    asynStatus status;
    +    int param;
    +    const char *functionName = "drvUserCreate";
     
    +    /* See if this is one of our standard parameters */
    +    status = findParam(SimDetParamString, NUM_SIM_DET_PARAMS, 
    +                       drvInfo, ¶m);
    +                                
    +    if (status == asynSuccess) {
    +        pasynUser->reason = param;
    +        if (pptypeName) {
    +            *pptypeName = epicsStrDup(drvInfo);
    +        }
    +        if (psize) {
    +            *psize = sizeof(param);
    +        }
    +        asynPrint(pasynUser, ASYN_TRACE_FLOW,
    +                  "%s:%s: drvInfo=%s, param=%d\n", 
    +                  driverName, functionName, drvInfo, param);
    +        return(asynSuccess);
    +    }
    +    
    +    /* If not, then see if it is a base class parameter */
    +    status = ADDriver::drvUserCreate(pasynUser, drvInfo, pptypeName, psize);
    +    return(status);  
    +}
    +
    + +

    + Prosilica Driver

    + +This is a driver for Gigabit Ethernet and Firewire cameras from Prosilica. It inherits from ADDriver. +It also implements a number of parameters that are specific +to the Prosilica cameras. The vendor library provided by Prosilica does callbacks to a user-supplied +function each time there is a new frame. Thus, it is not necessary to create a thread for callbacks in this driver.