예제 #1
0
void NDPluginCircularBuff::flushPreBuffer()
{
    if (NULL == preBuffer_) return;
    if (preBuffer_->size() > 0) {
      doCallbacksGenericPointer(preBuffer_->readFromStart(), NDArrayData, 0);
      while (preBuffer_->hasNext()) {
        doCallbacksGenericPointer(preBuffer_->readNext(), NDArrayData, 0);
      }
      preBuffer_->clear();
    }
}
예제 #2
0
void mar345::getImageData()
{
    char fullFileName[MAX_FILENAME_LEN];
    size_t dims[2];
    int itemp;
    int imageCounter;
    NDArray *pImage;
    char statusMessage[MAX_MESSAGE_SIZE];
    char errorBuffer[MAX_MESSAGE_SIZE];
    FILE *input;
    const char *functionName = "getImageData";

    /* Inquire about the image dimensions */
    getStringParam(NDFullFileName, MAX_FILENAME_LEN, fullFileName);
    getIntegerParam(NDArraySizeX, &itemp); dims[0] = itemp;
    getIntegerParam(NDArraySizeY, &itemp); dims[1] = itemp;
    getIntegerParam(NDArrayCounter, &imageCounter);
    pImage = this->pNDArrayPool->alloc(2, dims, NDUInt16, 0, NULL);

    epicsSnprintf(statusMessage, sizeof(statusMessage), "Reading mar345 file %s", fullFileName);
    setStringParam(ADStatusMessage, statusMessage);
    callParamCallbacks();
    input = fopen(fullFileName, "rb");
    if (input == NULL) {
        (void) strerror_r(errno, errorBuffer, sizeof(errorBuffer));
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
            "%s%s: unable to open input file %s, error=%s\n",
            driverName, functionName, fullFileName, errorBuffer);
        return;
    }
    get_pck(input, (epicsInt16 *)pImage->pData);
    fclose(input);

    /* Put the frame number and time stamp into the buffer */
    pImage->uniqueId = imageCounter;
    pImage->timeStamp = this->acqStartTime.secPastEpoch + this->acqStartTime.nsec / 1.e9;

    /* Get any attributes that have been defined for this driver */        
    this->getAttributes(pImage->pAttributeList);

    /* 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 */
    this->unlock();
    asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
         "%s:%s: calling NDArray callback\n", driverName, functionName);
    doCallbacksGenericPointer(pImage, NDArrayData, 0);
    this->lock();

    /* Free the image buffer */
    pImage->release();
}
    /** Callback function that is called by the NDArray driver with new NDArray data.
      * Grabs the current NDArray and applies the selected transforms to the data.  Apply the transforms in order.
      * \param[in] pArray  The NDArray from the callback.
      */
    void NDPluginDLL::processCallbacks(NDArray *pArray)
    {
        NDDimension_t dimsIn[ND_ARRAY_MAX_DIMS];
        NDArray *transformedArray;
        int ii;
        int colorMode;
        int k;
        const char* functionName = "processCallbacks";

        /* Call the base class method */
        NDPluginDriver::processCallbacks(pArray);


	if (controllers[0]->getPtrParam(genCamControllerEnum::gpu_ret_new_img) )
	{
        /* Previous version of the array was held in memory.  Release it and reserve a new one. */
        if (this->pArrays[0]) {
            this->pArrays[0]->release();
            this->pArrays[0] = NULL;
        }

		     pArrayOut = this->pNDArrayPool->copy(pArray, NULL, 1);
			 controllers[0]->putPtrParam(genCamControllerEnum::image_mem_ptr2,pArrayOut->pData);
	}

	//for (int k=0;k<num_controllers;k++)
	//{
		k=0;

		controllers[k]->putPtrParam(genCamControllerEnum::image_mem_ptr,pArray->pData);
		

		controllers[k]->putIntParam(genCamControllerEnum::gpu_img_sizex,pArray->dims[0].size);
		controllers[k]->putIntParam(genCamControllerEnum::gpu_img_sizey,pArray->dims[1].size);

		controllers[k]->processCallback(0);
	//}


		if (controllers[0]->getPtrParam(genCamControllerEnum::gpu_ret_new_img) )
		{

			this->pArrays[0] = pArrayOut;



	        doCallbacksGenericPointer(pArrayOut, NDArrayData,0);
		}

        callParamCallbacks();
    }
예제 #4
0
/** Base method for reading a file
  * Creates the file name with NDPluginBase::createFileName, then calls the pure virtual functions openFile,
  * readFile and closeFile in the derived class.  Does callbacks with the NDArray that was read in. */
asynStatus NDPluginFile::readFileBase(void)
{
    asynStatus status = asynSuccess;
    char fullFileName[MAX_FILENAME_LEN];
    int dataType=0;
    NDArray *pArray=NULL;
    static const char* functionName = "readFileBase";

    status = (asynStatus)createFileName(MAX_FILENAME_LEN, fullFileName);
    if (status) { 
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
              "%s:%s error creating full file name, fullFileName=%s, status=%d\n", 
              driverName, functionName, fullFileName, status);
        return(status);
    }
    
    /* Call the readFile method in the derived class */
    /* Do this with the main lock released since it is slow */
    this->unlock();
    epicsMutexLock(this->fileMutexId);
    status = this->openFile(fullFileName, NDFileModeRead, pArray);
    status = this->readFile(&pArray);
    status = this->closeFile();
    epicsMutexUnlock(this->fileMutexId);
    this->lock();
    
    /* If we got an error then return */
    if (status) return(status);
    
    /* Update the new values of dimensions and the array data */
    setIntegerParam(NDDataType, dataType);
    
    /* Call any registered clients */
    doCallbacksGenericPointer(pArray, NDArrayData, 0);

    /* Set the last array to be this one */
    this->pArrays[0]->release();
    this->pArrays[0] = pArray;    
    
    return(status);
}
예제 #5
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Grabs the current NDArray and applies the selected transforms to the data.  Apply the transforms in order.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginTransform::processCallbacks(NDArray *pArray){
  NDArray *transformedArray;
  NDArrayInfo_t arrayInfo;
  static const char* functionName = "processCallbacks";

  /* Call the base class method */
  NDPluginDriver::processCallbacks(pArray);

  /** Create a pointer to a structure of type NDArrayInfo_t and use it to get information about
    the input array.
  */
  pArray->getInfo(&arrayInfo);

  this->userDims_[0] = arrayInfo.xDim;
  this->userDims_[1] = arrayInfo.yDim;
  this->userDims_[2] = arrayInfo.colorDim;

  /* Previous version of the array was held in memory.  Release it and reserve a new one. */
  if (this->pArrays[0]) {
    this->pArrays[0]->release();
    this->pArrays[0] = NULL;
  }

  /* Release the lock; this is computationally intensive and does not access any shared data */
  this->unlock();
  /* Copy the information from the current array */
  this->pArrays[0] = this->pNDArrayPool->copy(pArray, NULL, 1);
  transformedArray = this->pArrays[0];

  if ( pArray->ndims <=3 )
    this->transformImage(pArray, transformedArray, &arrayInfo);
  else {
    asynPrint( this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s, this method is meant to transform 2Dimages when the number of dimensions is <= 3\n",
          pluginName, functionName);
  }
  this->lock();

  this->getAttributes(transformedArray->pAttributeList);
  doCallbacksGenericPointer(transformedArray, NDArrayData,0);
  callParamCallbacks();
}
예제 #6
0
void NDPluginFile::doNDArrayCallbacks(NDArray *pArray)
{
  int arrayCallbacks = 0;
  static const char *functionName = "doNDArrayCallbacks";

  getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
  if (arrayCallbacks == 1) {
    NDArray *pArrayOut = this->pNDArrayPool->copy(pArray, NULL, 1);
    if (pArrayOut != NULL) {
      this->getAttributes(pArrayOut->pAttributeList);
      this->unlock();
      doCallbacksGenericPointer(pArrayOut, NDArrayData, 0);
      this->lock();
      pArrayOut->release();
    }
    else {
      asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
        "%s: Couldn't allocate output array. Callbacks failed.\n", 
        functionName);
    }
  }
}
예제 #7
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Draws overlays on top of the array.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginFastCCD::processCallbacks(NDArray *pArray)
{
    /* This function does the actual operations
     * It is called with the mutex already locked.  It unlocks it during long calculations when private
     * structures don't need to be protected.
     */

    NDArray *pOutput;

    /* Call the base class method */
    NDPluginDriver::processCallbacks(pArray);

    /* We always keep the last array so read() can use it.
     * Release previous one. */
    if (this->pArrays[0]) {
        this->pArrays[0]->release();
    }

    /* Copy the input array format but not the data */
    this->pArrays[0] = this->pNDArrayPool->copy(pArray, NULL, 1);
    pOutput = this->pArrays[0];
    
    /* Get information about the array needed later */
    pOutput->getInfo(&this->arrayInfo);

    /* Get paraemeters which we need for processing the image */
    int offset0, offset1, offset2, enabled, dpval, dataen;
    getIntegerParam(NDPluginFastCCDOffset0, &offset0);
    getIntegerParam(NDPluginFastCCDOffset1, &offset1);
    getIntegerParam(NDPluginFastCCDOffset2, &offset2);
    getIntegerParam(NDPluginFastCCDEnable, &enabled);
    getIntegerParam(NDPluginFastCCDEnableData, &dataen);
    getIntegerParam(NDPluginFastCCDDPVal, &dpval);

    /* This function is called with the lock taken, and it must be set when we exit.
     * The following code can be exected without the mutex because we are not accessing memory
     * that other threads can access. */
    this->unlock();

    if(((pOutput->dataType == NDUInt16) || (pOutput->dataType == NDInt16)) && enabled){
      // We can only process NDUnit16 images just pass if not. 

      // Get the data pointer
      epicsInt16 *data = (epicsInt16 *)pOutput->pData;
      epicsInt16 ctrl, sign;

      for(size_t i=0; i<pOutput->dims[1].size; i++){
        for(size_t j=0; j<pOutput->dims[0].size; j++){
          ctrl = *data & CIN_DATA_CTRL_MASK;
          if(dataen){
            *data = *data & CIN_DATA_DATA_MASK;
            if((ctrl & CIN_DATA_GAIN_8) == CIN_DATA_GAIN_8){
              // Minimum Gain just left shift 3 times
              *data = (*data << 3) - (epicsInt16)(offset2 << 3);
            } else if ((ctrl & CIN_DATA_GAIN_4) == CIN_DATA_GAIN_4) {
              // Gain
              *data = (*data << 2) - (epicsInt16)(offset1 << 2);
            } else if ((ctrl & CIN_DATA_DROPPED_PACKET_VAL) == CIN_DATA_DROPPED_PACKET_VAL) {
              // Dropped Packet
              *data = (epicsUInt16)dpval;
            } else {
              // Maximum gain
              *data = *data - (epicsInt16)offset0;
            } 
          } else {
            *data = ctrl;
          }
          data++; // Advance the pointer
        }
      }
    }

    this->lock();

    /* Get the attributes for this driver */
    this->getAttributes(this->pArrays[0]->pAttributeList);
    /* Call any clients who have registered for NDArray callbacks */
    this->unlock();
    doCallbacksGenericPointer(this->pArrays[0], NDArrayData, 0);
    this->lock();
    callParamCallbacks();
}
예제 #8
0
void FastCCD::processImage(cin_data_frame_t *frame)
{
  const char* functionName = "processImage";
  this->lock();

  // Set the unique ID
  pImage->uniqueId = frame->number;
  pImage->dims[0].size = frame->size_x;
  pImage->dims[0].offset = 0;
  pImage->dims[0].binning = 1;
  pImage->dims[1].size = frame->size_y;
  pImage->dims[1].offset = 0;
  pImage->dims[1].binning = 1;

  // Process Timestamps.
  
  // Frame timestamp is a timespec always trust driver becuase it is correct!
  pImage->timeStamp = frame->timestamp.tv_sec + 1e-9 * frame->timestamp.tv_nsec;
  pImage->epicsTS.secPastEpoch = frame->timestamp.tv_sec;
  pImage->epicsTS.nsec = frame->timestamp.tv_nsec;
  updateTimeStamp(&pImage->epicsTS);
  
  // Get any attributes for the driver
  this->getAttributes(pImage->pAttributeList);
       
  int arrayCallbacks;
  getIntegerParam(NDArrayCallbacks, &arrayCallbacks);

  if (arrayCallbacks) {
    /* 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 */
    this->unlock();
    doCallbacksGenericPointer(pImage, NDArrayData, 0);
    this->lock();
  }

  if (this->framesRemaining > 0) this->framesRemaining--;
  if (this->framesRemaining == 0) {
    cin_ctl_int_trigger_stop(&cin_ctl_port);
    cin_ctl_ext_trigger_stop(&cin_ctl_port);
    setIntegerParam(ADAcquire, 0);
    setIntegerParam(ADStatus, ADStatusIdle);
  }

  /* Update the frame counter */
  int imageCounter;
  getIntegerParam(NDArrayCounter, &imageCounter);
  imageCounter++;
  setIntegerParam(NDArrayCounter, imageCounter);
 
  asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
              "%s:%s: frameId=%d\n",
              driverName, functionName, frame->number);

  // Release the frame as we are done with it!
  pImage->release();

  /* Update any changed parameters */
  callParamCallbacks();

  this->unlock();
  return;
}
예제 #9
0
/**
 * Readout thread function
 */
void ADSBIG::readoutTask(void)
{
  epicsEventWaitStatus eventStatus;
  epicsFloat64 timeout = 0.001;
  bool error = false;
  size_t dims[2];
  int nDims = 2;
  epicsInt32 sizeX = 0;
  epicsInt32 sizeY = 0;
  epicsInt32 minX = 0;
  epicsInt32 minY = 0;
  NDDataType_t dataType;
  epicsInt32 iDataType = 0;
  epicsUInt32 dataSize = 0;
  epicsTimeStamp nowTime;
  NDArray *pArray = NULL;
  epicsInt32 numImagesCounter = 0;
  epicsInt32 imageCounter = 0;
  PAR_ERROR cam_err = CE_NO_ERROR;

  const char* functionName = "ADSBIG::readoutTask";
  asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s Started Readout Thread.\n", functionName);

  while (1) {

    //Wait for a stop event, with a short timeout, to catch any that were done after last one.
    eventStatus = epicsEventWaitWithTimeout(m_stopEvent, timeout);          
    if (eventStatus == epicsEventWaitOK) {
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s Got Stop Event Before Start Event.\n", functionName);
    }

    lock();
    if (!error) {
      setStringParam(ADStatusMessage, "Idle");
    }
    callParamCallbacks();
    unlock();

    eventStatus = epicsEventWait(m_startEvent);          
    if (eventStatus == epicsEventWaitOK) {
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s Got Start Event.\n", functionName);
      error = false;
      setStringParam(ADStatusMessage, " ");
      lock();
      setIntegerParam(ADNumImagesCounter, 0);
      setIntegerParam(ADNumExposuresCounter, 0);

      //Sanity checks
      if ((p_Cam == NULL) || (p_Img == NULL)) {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s NULL pointer.\n", functionName);
        break;
      }

      //printf("%s Time before acqusition: ", functionName);
      //epicsTime::getCurrent().show(0);

      //Read the frame sizes 
      getIntegerParam(ADMinX, &minX);
      getIntegerParam(ADMinY, &minY);
      getIntegerParam(ADSizeX, &sizeX);
      getIntegerParam(ADSizeY, &sizeY);
      p_Cam->SetSubFrame(minX, minY, sizeX, sizeY);

      //Read what type of image we want - light field or dark field?
      int darkField = 0;
      getIntegerParam(ADSBIGDarkFieldParam, &darkField);

      if (darkField > 0) {
        cam_err = p_Cam->GrabSetup(p_Img, SBDF_DARK_ONLY);
      } else {
        cam_err = p_Cam->GrabSetup(p_Img, SBDF_LIGHT_ONLY);
      }
      if (cam_err != CE_NO_ERROR) {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
                "%s. CSBIGCam::GrabSetup returned an error. %s\n", 
              functionName, p_Cam->GetErrorString(cam_err).c_str());
        error = true;
        setStringParam(ADStatusMessage, p_Cam->GetErrorString(cam_err).c_str());
      } 

      unsigned short binX = 0;
      unsigned short binY = 0;
      p_Img->GetBinning(binX, binY);
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " binX: %d\n", binY);
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " binY: %d\n", binX);
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " PixelHeight: %f\n", p_Img->GetPixelHeight());
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " PixelWidth: %f\n", p_Img->GetPixelWidth());
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " Height: %d\n", p_Img->GetHeight());
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " Width: %d\n", p_Img->GetWidth());
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " Readout Mode: %d\n", p_Cam->GetReadoutMode());
      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, " Dark Field: %d\n", darkField);

      if (!error) {

        //Do exposure
        callParamCallbacks();
        unlock();
        if (darkField > 0) {
          cam_err = p_Cam->GrabMain(p_Img, SBDF_DARK_ONLY);
        } else {
          cam_err = p_Cam->GrabMain(p_Img, SBDF_LIGHT_ONLY);
        }
        lock();
        if (cam_err != CE_NO_ERROR) {
          asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
                    "%s. CSBIGCam::GrabMain returned an error. %s\n", 
                    functionName, p_Cam->GetErrorString(cam_err).c_str());
          error = true;
          setStringParam(ADStatusMessage, p_Cam->GetErrorString(cam_err).c_str());
        }

        setDoubleParam(ADSBIGPercentCompleteParam, 100.0);

        if (!m_aborted) { 
        
        unsigned short *pData = p_Img->GetImagePointer();
        
        //printf("%s Time after acqusition: ", functionName);
        //epicsTime::getCurrent().show(0);

        //Update counters
        getIntegerParam(NDArrayCounter, &imageCounter);
        imageCounter++;
        setIntegerParam(NDArrayCounter, imageCounter);
        getIntegerParam(ADNumImagesCounter, &numImagesCounter);
        numImagesCounter++;
        setIntegerParam(ADNumImagesCounter, numImagesCounter);

        //NDArray callbacks
        int arrayCallbacks = 0;
        getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
        getIntegerParam(NDDataType, &iDataType);
        dataType = static_cast<NDDataType_t>(iDataType);
        if (dataType == NDUInt8) {
          dataSize = sizeX*sizeY*sizeof(epicsUInt8);
        } else if (dataType == NDUInt16) {
          dataSize = sizeX*sizeY*sizeof(epicsUInt16);
        } else if (dataType == NDUInt32) {
          dataSize = sizeX*sizeY*sizeof(epicsUInt32);
        } else {
          asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
                    "%s. ERROR: We can't handle this data type. dataType: %d\n", 
                    functionName, dataType);
          error = true;
          dataSize = 0;
        }
        setIntegerParam(NDArraySize, dataSize);
        
        if (!error) {
          
          if (arrayCallbacks) {
            //Allocate an NDArray
            dims[0] = sizeX;
            dims[1] = sizeY;
            if ((pArray = this->pNDArrayPool->alloc(nDims, dims, dataType, 0, NULL)) == NULL) {
              asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
                        "%s. ERROR: pArray is NULL.\n", 
                        functionName);
            } else {
              epicsTimeGetCurrent(&nowTime);
              pArray->uniqueId = imageCounter;
              pArray->timeStamp = nowTime.secPastEpoch + nowTime.nsec / 1.e9;
              updateTimeStamp(&pArray->epicsTS);
              //Get any attributes that have been defined for this driver
              this->getAttributes(pArray->pAttributeList);
              //We copy data because the SBIG class library holds onto the original buffer until the next acqusition
              asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                        "%s: Copying data. dataSize: %d\n", functionName, dataSize);
              memcpy(pArray->pData, pData, dataSize);
                
              unlock();
              asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s: Calling NDArray callback\n", functionName);
              doCallbacksGenericPointer(pArray, NDArrayData, 0);
              lock();
              pArray->release();
            }
            
          }
          
          setIntegerParam(ADStatus, ADStatusIdle);
          
        } else {
          setIntegerParam(ADStatus, ADStatusError);
        }

        } else { //end if (!m_aborted)
          setIntegerParam(ADStatus, ADStatusAborted);
          m_aborted = false;
        }
        
      }
      
      callParamCallbacks();
      //Complete Acquire callback
      setIntegerParam(ADAcquire, 0);
      callParamCallbacks();
      unlock();

      asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                "%s Completed acqusition.\n", functionName);

    } //end of start event

  } //end of while(1)

  asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
            "%s: ERROR: Exiting ADSBIGReadoutTask main loop.\n", functionName);

}
예제 #10
0
/** 
 * Callback function that is called by the NDArray driver with new NDArray data.
 * Computes statistics on the ROIs if NDPluginROIStatUse is 1.
 * If NDPluginROIStatNDArrayCallbacks is 1 then it also does NDArray callbacks.
 * \param[in] pArray The NDArray from the callback.
 */
void NDPluginROIStat::processCallbacks(NDArray *pArray)
{

  //This function is called with the mutex already locked.  
  //It unlocks it during long calculations when private structures don't need to be protected.
  
  int use = 0;
  int itemp = 0;
  int dim = 0;
  asynStatus status = asynSuccess;
  NDROI *pROI;
  int TSAcquiring;
  const char* functionName = "NDPluginROIStat::processCallbacks";

  /* Call the base class method */
  NDPluginDriver::processCallbacks(pArray);

  // This plugin only works with 1-D or 2-D arrays
  if ((pArray->ndims < 1) || (pArray->ndims > 2)) {
      asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
        "%s: error, number of array dimensions must be 1 or 2\n",
        functionName);
  }

  //Set NDArraySize params to the input pArray, because this plugin doesn't change them
  if (pArray->ndims > 0) setIntegerParam(NDArraySizeX, (int)pArray->dims[0].size);
  if (pArray->ndims > 1) setIntegerParam(NDArraySizeY, (int)pArray->dims[1].size);

  getIntegerParam(NDPluginROIStatTSAcquiring,        &TSAcquiring);

  /* Loop over the ROIs in this driver */
  for (int roi=0; roi<maxROIs_; ++roi) {
    
    pROI = &pROIs_[roi];
    getIntegerParam(roi, NDPluginROIStatUse, &use);
    if (!use) {
      continue;
    }

    /* Need to fetch all of these parameters while we still have the mutex */
    getIntegerParam(roi, NDPluginROIStatDim0Min,      &itemp); pROI->offset[0] = itemp;
    getIntegerParam(roi, NDPluginROIStatDim1Min,      &itemp); pROI->offset[1] = itemp;
    getIntegerParam(roi, NDPluginROIStatDim0Size,     &itemp); pROI->size[0] = itemp;
    getIntegerParam(roi, NDPluginROIStatDim1Size,     &itemp); pROI->size[1] = itemp;
    getIntegerParam(roi, NDPluginROIStatBgdWidth,     &itemp); pROI->bgdWidth = itemp;
    
    for (dim=0; dim<pArray->ndims; dim++) {
      pROI->offset[dim]  = MAX(pROI->offset[dim], 0);
      pROI->offset[dim]  = MIN(pROI->offset[dim], pArray->dims[dim].size-1);
      pROI->size[dim]    = MAX(pROI->size[dim], 1);
      pROI->size[dim]    = MIN(pROI->size[dim], pArray->dims[dim].size - pROI->offset[dim]);
      pROI->arraySize[dim] = (int)pArray->dims[dim].size;
    }
    
    /* Update the parameters that may have changed */
    setIntegerParam(roi, NDPluginROIStatDim0MaxSize, 0);
    setIntegerParam(roi, NDPluginROIStatDim1MaxSize, 0);
    if (pArray->ndims > 0) {
      setIntegerParam(roi, NDPluginROIStatDim0MaxSize, (int)pArray->dims[0].size);
      setIntegerParam(roi, NDPluginROIStatDim0Min,  (int)pROI->offset[0]);
      setIntegerParam(roi, NDPluginROIStatDim0Size, (int)pROI->size[0]);
    }
    if (pArray->ndims > 1) {
      setIntegerParam(roi, NDPluginROIStatDim1MaxSize, (int)pArray->dims[1].size);
      setIntegerParam(roi, NDPluginROIStatDim1Min,  (int)pROI->offset[1]);
      setIntegerParam(roi, NDPluginROIStatDim1Size, (int)pROI->size[1]);
    }
        
    /* This function is called with the lock taken, and it must be set when we exit.
     * The following code can be exected without the mutex because we are not accessing elements of
     * pPvt that other threads can access. */
    this->unlock();
    
    status = doComputeStatistics(pArray, pROI);
    if (status != asynSuccess) {
      asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
        "%s: doComputeStatistics failed. status=%d\n", 
        functionName, status);
    }

    if (TSAcquiring) {
      double *pData = timeSeries_ + (roi * MAX_TIME_SERIES_TYPES * numTSPoints_);
      pData[TSMinValue*numTSPoints_ + currentTSPoint_]  = pROI->min;
      pData[TSMaxValue*numTSPoints_ + currentTSPoint_]  = pROI->max;
      pData[TSMeanValue*numTSPoints_ + currentTSPoint_] = pROI->mean;
      pData[TSTotal*numTSPoints_ + currentTSPoint_]     = pROI->total;
      pData[TSNet*numTSPoints_ + currentTSPoint_]       = pROI->net;
      pData[TSTimestamp*numTSPoints_ + currentTSPoint_] = pArray->timeStamp;
    }

    /* We must enter the loop and exit with the mutex locked */
    this->lock();
    setDoubleParam(roi, NDPluginROIStatMinValue,    pROI->min);
    setDoubleParam(roi, NDPluginROIStatMaxValue,    pROI->max);
    setDoubleParam(roi, NDPluginROIStatMeanValue,   pROI->mean);
    setDoubleParam(roi, NDPluginROIStatTotal,       pROI->total);
    setDoubleParam(roi, NDPluginROIStatNet,         pROI->net);
    asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
          "%s ROI=%d, min=%f, max=%f, mean=%f, total=%f, net=%f\n",
          functionName, roi, pROI->min, pROI->max, pROI->mean, pROI->total, pROI->net);

    callParamCallbacks(roi);
  }

  if (TSAcquiring) {
    currentTSPoint_++;
    setIntegerParam(NDPluginROIStatTSCurrentPoint, currentTSPoint_);
    if (currentTSPoint_ >= numTSPoints_) {
      setIntegerParam(NDPluginROIStatTSAcquiring, 0);
      doTimeSeriesCallbacks();
    }
  }

  int arrayCallbacks = 0;
  getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
  if (arrayCallbacks == 1) {
    NDArray *pArrayOut = this->pNDArrayPool->copy(pArray, NULL, 1);
    if (pArrayOut != NULL) {
      this->getAttributes(pArrayOut->pAttributeList);
      this->unlock();
      doCallbacksGenericPointer(pArrayOut, NDArrayData, 0);
      this->lock();
      pArrayOut->release();
    }
    else {
      asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
        "%s: Couldn't allocate output array. Callbacks failed.\n", 
        functionName);
    }
  }
  callParamCallbacks();
}
예제 #11
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Stores the number of pre-trigger images prior to the trigger in a ring buffer.
  * Once the trigger has been received stores the number of post-trigger buffers
  * and then exposes the buffers.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginCircularBuff::processCallbacks(NDArray *pArray)
{
    /* 
     * It is called with the mutex already locked.  It unlocks it during long calculations when private
     * structures don't need to be protected.
     */
    int scopeControl, preCount, postCount, currentImage, currentPostCount, softTrigger;
    int presetTriggerCount, actualTriggerCount;
    NDArray *pArrayCpy = NULL;
    NDArrayInfo arrayInfo;
    int triggered = 0;

    //const char* functionName = "processCallbacks";

    /* Call the base class method */
    NDPluginDriver::beginProcessCallbacks(pArray);
    
    pArray->getInfo(&arrayInfo);

    // Retrieve the running state
    getIntegerParam(NDCircBuffControl,            &scopeControl);
    getIntegerParam(NDCircBuffPreTrigger,         &preCount);
    getIntegerParam(NDCircBuffPostTrigger,        &postCount);
    getIntegerParam(NDCircBuffCurrentImage,       &currentImage);
    getIntegerParam(NDCircBuffPostCount,          &currentPostCount);
    getIntegerParam(NDCircBuffSoftTrigger,        &softTrigger);
    getIntegerParam(NDCircBuffPresetTriggerCount, &presetTriggerCount);
    getIntegerParam(NDCircBuffPresetTriggerCount, &presetTriggerCount);
    getIntegerParam(NDCircBuffActualTriggerCount, &actualTriggerCount);

    // Are we running?
    if (scopeControl) {

      // Check for a soft trigger
      if (softTrigger){
        triggered = 1;
        setIntegerParam(NDCircBuffTriggered, triggered);
      } else {
        getIntegerParam(NDCircBuffTriggered, &triggered);
        if (!triggered) { 
          // Check for the trigger based on meta-data in the NDArray and the trigger calculation
          calculateTrigger(pArray, &triggered);
          setIntegerParam(NDCircBuffTriggered, triggered);
        }
      }

      // First copy the buffer into our buffer pool so we can release the resource on the driver
      pArrayCpy = this->pNDArrayPool->copy(pArray, NULL, 1);

      if (pArrayCpy){

        // Have we detected a trigger event yet?
        if (!triggered){
          // No trigger so add the NDArray to the pre-trigger ring
          pOldArray_ = preBuffer_->addToEnd(pArrayCpy);
          // If we overwrote an existing array in the ring, release it here
          if (pOldArray_){
            pOldArray_->release();
            pOldArray_ = NULL;
          }
          // Set the size
          setIntegerParam(NDCircBuffCurrentImage,  preBuffer_->size());
          if (preBuffer_->size() == preCount){
            setStringParam(NDCircBuffStatus, "Buffer Wrapping");
          }
        } else {
          // Trigger detected
          // Start making frames available if trigger has occured
          setStringParam(NDCircBuffStatus, "Flushing");

          // Has the trigger occured on this frame?
          if (previousTrigger_ == 0){
            previousTrigger_ = 1;
            // Yes, so flush the ring first

            if (preBuffer_->size() > 0){
              doCallbacksGenericPointer(preBuffer_->readFromStart(), NDArrayData, 0);
              while (preBuffer_->hasNext()) {
                doCallbacksGenericPointer(preBuffer_->readNext(), NDArrayData, 0);
              }
            }
          }
      
          currentPostCount++;
          setIntegerParam(NDCircBuffPostCount,  currentPostCount);

          doCallbacksGenericPointer(pArrayCpy, NDArrayData, 0);
          if (pArrayCpy){
            pArrayCpy->release();
          }
        }

        // Stop recording once we have reached the post-trigger count, wait for a restart
        if (currentPostCount >= postCount){
          actualTriggerCount++;
          setIntegerParam(NDCircBuffActualTriggerCount, actualTriggerCount);
          if ((presetTriggerCount == 0) ||
              ((presetTriggerCount > 0) && (actualTriggerCount < presetTriggerCount)))
          {
            previousTrigger_ = 0;
            // Set the status to buffer filling
            setIntegerParam(NDCircBuffControl, 1);
            setIntegerParam(NDCircBuffSoftTrigger, 0);
            setIntegerParam(NDCircBuffTriggered, 0);
            setIntegerParam(NDCircBuffPostCount, 0);
            setStringParam(NDCircBuffStatus, "Buffer filling");
          } else {
            setIntegerParam(NDCircBuffTriggered, 0);
            setIntegerParam(NDCircBuffControl, 0);
            setStringParam(NDCircBuffStatus, "Acquisition Completed");
          }
        }
      } else {
        //printf("pArray NULL, failed to copy the data\n");
      }
    } else {
      // Currently do nothing
    }
            
    callParamCallbacks();
}
예제 #12
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Does image statistics.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginStats::processCallbacks(NDArray *pArray)
{
    /* This function does array statistics.
     * It is called with the mutex already locked.  It unlocks it during long calculations when private
     * structures don't need to be protected.
     */
    NDDimension_t bgdDims[ND_ARRAY_MAX_DIMS], *pDim;
    size_t bgdPixels;
    int bgdWidth;
    int dim;
    NDStats_t stats, *pStats=&stats, statsTemp, *pStatsTemp=&statsTemp;
    double bgdCounts, avgBgd;
    NDArray *pBgdArray=NULL;
    int computeStatistics, computeCentroid, computeProfiles, computeHistogram;
    size_t sizeX=0, sizeY=0;
    int i;
    int itemp;
    int numTSPoints, currentTSPoint, TSAcquiring;
    NDArrayInfo arrayInfo;
    static const char* functionName = "processCallbacks";

    /* Call the base class method */
    NDPluginDriver::processCallbacks(pArray);
    
    pArray->getInfo(&arrayInfo);
    getIntegerParam(NDPluginStatsComputeStatistics,  &computeStatistics);
    getIntegerParam(NDPluginStatsComputeCentroid,    &computeCentroid);
    getIntegerParam(NDPluginStatsComputeProfiles,    &computeProfiles);
    getIntegerParam(NDPluginStatsComputeHistogram,   &computeHistogram);
    
    if (pArray->ndims > 0) sizeX = pArray->dims[0].size;
    if (pArray->ndims == 1) sizeY = 1;
    if (pArray->ndims > 1)  sizeY = pArray->dims[1].size;

    if (sizeX != this->profileSizeX) {
        this->profileSizeX = sizeX;
        setIntegerParam(NDPluginStatsProfileSizeX,  (int)this->profileSizeX);
        for (i=0; i<MAX_PROFILE_TYPES; i++) {
            if (this->profileX[i]) free(this->profileX[i]);
            this->profileX[i] = (double *)malloc(this->profileSizeX * sizeof(double));
        }
    }
    if (sizeY != this->profileSizeY) {
        this->profileSizeY = sizeY;
        setIntegerParam(NDPluginStatsProfileSizeY, (int)this->profileSizeY);
        for (i=0; i<MAX_PROFILE_TYPES; i++) {
            if (this->profileY[i]) free(this->profileY[i]);
            this->profileY[i] = (double *)malloc(this->profileSizeY * sizeof(double));
        }
    }

    if (computeStatistics) {
        getIntegerParam(NDPluginStatsBgdWidth, &bgdWidth);
        doComputeStatistics(pArray, pStats);
        /* If there is a non-zero background width then compute the background counts */
        // Note that the following algorithm is general in N-dimensions but does have a slight inaccuracy.
        // It computes the background region such that the pixels at the corners are counted twice.
        // The normalization correctly accounts for this when computing the average background per pixel,
        // but these pixels are given extra weight in the calculation.
        if (bgdWidth > 0) {
            bgdPixels = 0;
            bgdCounts = 0.;
            /* Initialize the dimensions of the background array */
            for (dim=0; dim<pArray->ndims; dim++) {
                pArray->initDimension(&bgdDims[dim], pArray->dims[dim].size);
            }
            for (dim=0; dim<pArray->ndims; dim++) {
                pDim = &bgdDims[dim];
                pDim->offset = 0;
                pDim->size = MIN((size_t)bgdWidth, pDim->size);
                this->pNDArrayPool->convert(pArray, &pBgdArray, pArray->dataType, bgdDims);
                pDim->size = pArray->dims[dim].size;
                if (!pBgdArray) {
                    asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                        "%s::%s, error allocating array buffer in convert\n",
                        driverName, functionName);
                    continue;
                }
                doComputeStatistics(pBgdArray, pStatsTemp);
                pBgdArray->release();
                bgdPixels += pStatsTemp->nElements;
                bgdCounts += pStatsTemp->total;
                pDim->offset = MAX(0, (int)(pDim->size - bgdWidth));
                pDim->size = MIN((size_t)bgdWidth, pArray->dims[dim].size - pDim->offset);
                this->pNDArrayPool->convert(pArray, &pBgdArray, pArray->dataType, bgdDims);
                pDim->offset = 0;
                pDim->size = pArray->dims[dim].size;
                if (!pBgdArray) {
                    asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                        "%s::%s, error allocating array buffer in convert\n",
                        driverName, functionName);
                    continue;
                }
                doComputeStatistics(pBgdArray, pStatsTemp);
                pBgdArray->release();
                bgdPixels += pStatsTemp->nElements;
                bgdCounts += pStatsTemp->total;
            }
            if (bgdPixels < 1) bgdPixels = 1;
            avgBgd = bgdCounts / bgdPixels;
            pStats->net = pStats->total - avgBgd*pStats->nElements;
        }
        setDoubleParam(NDPluginStatsMinValue,    pStats->min);
        setDoubleParam(NDPluginStatsMinX,        (double)pStats->minX);
        setDoubleParam(NDPluginStatsMinY,        (double)pStats->minY);                        
        setDoubleParam(NDPluginStatsMaxValue,    pStats->max);
        setDoubleParam(NDPluginStatsMaxX,        (double)pStats->maxX);
        setDoubleParam(NDPluginStatsMaxY,        (double)pStats->maxY);                                
        setDoubleParam(NDPluginStatsMeanValue,   pStats->mean);
        setDoubleParam(NDPluginStatsSigmaValue,  pStats->sigma);
        setDoubleParam(NDPluginStatsTotal,       pStats->total);
        setDoubleParam(NDPluginStatsNet,         pStats->net);
        asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
            (char *)pArray->pData, arrayInfo.totalBytes,
            "%s:%s min=%f, max=%f, mean=%f, total=%f, net=%f",
            driverName, functionName, pStats->min, pStats->max, pStats->mean, pStats->total, pStats->net);
    }

    if (computeCentroid) {
        doComputeCentroid(pArray);
        setDoubleParam(NDPluginStatsCentroidX,   this->centroidX);
        setDoubleParam(NDPluginStatsCentroidY,   this->centroidY);
        setDoubleParam(NDPluginStatsSigmaX,      this->sigmaX);
        setDoubleParam(NDPluginStatsSigmaY,      this->sigmaY);
        setDoubleParam(NDPluginStatsSigmaXY,     this->sigmaXY);
    }
         
    if (computeProfiles) {
        doComputeProfiles(pArray);
    }
    
    if (computeHistogram) {
        getIntegerParam(NDPluginStatsHistSize, &itemp); this->histSizeNew = itemp;
        getDoubleParam (NDPluginStatsHistMin,  &this->histMin);
        getDoubleParam (NDPluginStatsHistMax,  &this->histMax);
        doComputeHistogram(pArray);
        setDoubleParam(NDPluginStatsHistEntropy, this->histEntropy);
        doCallbacksFloat64Array(this->histogram, this->histogramSize, NDPluginStatsHistArray, 0);
    }
    
    getIntegerParam(NDPluginStatsTSCurrentPoint,     &currentTSPoint);
    getIntegerParam(NDPluginStatsTSNumPoints,        &numTSPoints);
    getIntegerParam(NDPluginStatsTSAcquiring,        &TSAcquiring);
    if (TSAcquiring) {
        timeSeries[TSMinValue][currentTSPoint]    = pStats->min;
        timeSeries[TSMinX][currentTSPoint]        = (double)pStats->minX;
        timeSeries[TSMinY][currentTSPoint]        = (double)pStats->minY;                        
        timeSeries[TSMaxValue][currentTSPoint]    = pStats->max;
        timeSeries[TSMaxX][currentTSPoint]        = (double)pStats->maxX;
        timeSeries[TSMaxY][currentTSPoint]        = (double)pStats->maxY;                                
        timeSeries[TSMeanValue][currentTSPoint]   = pStats->mean;
        timeSeries[TSSigmaValue][currentTSPoint]  = pStats->sigma;
        timeSeries[TSTotal][currentTSPoint]       = pStats->total;
        timeSeries[TSNet][currentTSPoint]         = pStats->net;
        timeSeries[TSCentroidX][currentTSPoint] = this->centroidX;
        timeSeries[TSCentroidY][currentTSPoint] = this->centroidY;
        timeSeries[TSSigmaX][currentTSPoint]    = this->sigmaX;
        timeSeries[TSSigmaY][currentTSPoint]    = this->sigmaY;
        timeSeries[TSSigmaXY][currentTSPoint]   = this->sigmaXY;
        currentTSPoint++;
        setIntegerParam(NDPluginStatsTSCurrentPoint, currentTSPoint);
        if (currentTSPoint >= numTSPoints) {
            setIntegerParam(NDPluginStatsTSAcquiring, 0);
            doTimeSeriesCallbacks();
        }
    }

    NDArray *pArrayOut = this->pNDArrayPool->copy(pArray, NULL, 1);
    if (NULL != pArrayOut) {
        this->getAttributes(pArrayOut->pAttributeList);
        this->unlock();
        doCallbacksGenericPointer(pArrayOut, NDArrayData, 0);
        this->lock();
        /* Save a copy of this array for calculations when cursor is moved or threshold is changed */
        if (this->pArrays[0]) this->pArrays[0]->release();
        this->pArrays[0] = pArrayOut;
    }
    else {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
            "%s::%s: Couldn't allocate output array. Further processing terminated.\n", 
            driverName, functionName);
    }

    callParamCallbacks();
}
예제 #13
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Draws overlays on top of the array.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginOverlay::processCallbacks(NDArray *pArray)
{
    /* This function draws overlays
     * It is called with the mutex already locked.  It unlocks it during long calculations when private
     * structures don't need to be protected.
     */

    int use;
    int itemp;
    int overlay;
    NDArray *pOutput;
    //static const char* functionName = "processCallbacks";

    /* Call the base class method */
    NDPluginDriver::processCallbacks(pArray);

    /* We always keep the last array so read() can use it.
     * Release previous one. */
    if (this->pArrays[0]) {
        this->pArrays[0]->release();
    }
    /* Copy the input array so we can modify it. */
    this->pArrays[0] = this->pNDArrayPool->copy(pArray, NULL, 1);
    pOutput = this->pArrays[0];
    
    /* Get information about the array needed later */
    pOutput->getInfo(&this->arrayInfo);
    setIntegerParam(NDPluginOverlayMaxSizeX, (int)arrayInfo.xSize);
    setIntegerParam(NDPluginOverlayMaxSizeY, (int)arrayInfo.ySize);
   
    /* Loop over the overlays in this driver */
    for (overlay=0; overlay<this->maxOverlays; overlay++) {
        pOverlay = &this->pOverlays[overlay];
        getIntegerParam(overlay, NDPluginOverlayUse, &use);
        asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER,
            "NDPluginOverlay::processCallbacks, overlay=%d, use=%d\n",
            overlay, use);
        if (!use) continue;
        /* Need to fetch all of these parameters while we still have the mutex */
        getIntegerParam(overlay, NDPluginOverlayPositionX,  &itemp); pOverlay->PositionX = itemp;
        pOverlay->PositionX = MAX(pOverlay->PositionX, 0);
        pOverlay->PositionX = MIN(pOverlay->PositionX, this->arrayInfo.xSize-1);
        getIntegerParam(overlay, NDPluginOverlayPositionY,  &itemp); pOverlay->PositionY = itemp;
        pOverlay->PositionY = MAX(pOverlay->PositionY, 0);
        pOverlay->PositionY = MIN(pOverlay->PositionY, this->arrayInfo.ySize-1);
        getIntegerParam(overlay, NDPluginOverlaySizeX,      &itemp); pOverlay->SizeX = itemp;
        getIntegerParam(overlay, NDPluginOverlaySizeY,      &itemp); pOverlay->SizeY = itemp;
        getIntegerParam(overlay, NDPluginOverlayWidthX,     &itemp); pOverlay->WidthX = itemp;
        getIntegerParam(overlay, NDPluginOverlayWidthY,     &itemp); pOverlay->WidthY = itemp;
        getIntegerParam(overlay, NDPluginOverlayShape,      &itemp); pOverlay->shape = (NDOverlayShape_t)itemp;
        getIntegerParam(overlay, NDPluginOverlayDrawMode,   &itemp); pOverlay->drawMode = (NDOverlayDrawMode_t)itemp;
        getIntegerParam(overlay, NDPluginOverlayRed,        &pOverlay->red);
        getIntegerParam(overlay, NDPluginOverlayGreen,      &pOverlay->green);
        getIntegerParam(overlay, NDPluginOverlayBlue,       &pOverlay->blue);
        getStringParam( overlay, NDPluginOverlayTimeStampFormat, sizeof(pOverlay->TimeStampFormat), pOverlay->TimeStampFormat);
        getIntegerParam(overlay, NDPluginOverlayFont,       &pOverlay->Font);
        getStringParam( overlay, NDPluginOverlayDisplayText, sizeof(pOverlay->DisplayText), pOverlay->DisplayText);

        pOverlay->DisplayText[sizeof(pOverlay->DisplayText)-1] = 0;

        /* This function is called with the lock taken, and it must be set when we exit.
         * The following code can be exected without the mutex because we are not accessing memory
         * that other threads can access. */
        this->unlock();
        this->doOverlay(pOutput, pOverlay);
        this->lock();
    }
    /* Get the attributes for this driver */
    this->getAttributes(this->pArrays[0]->pAttributeList);
    /* Call any clients who have registered for NDArray callbacks */
    this->unlock();
    doCallbacksGenericPointer(this->pArrays[0], NDArrayData, 0);
    this->lock();
    callParamCallbacks();
}
예제 #14
0
epicsInt32 mythen::dataCallback(epicsInt32 *pData)
{
    NDArray *pImage; 
    int ndims = 1;
    size_t dims[2];
    int totalBytes; 
    int arrayCallbacks;
    int imageCounter;
    epicsTimeStamp timeStamp; 
    epicsInt32 colorMode = NDColorModeMono;

    // printf ("pData[0] = %X\n",pData[0]);

    if (pData == NULL || pData[0] < 0) return(0); 

    dims[0] = MAX_DIMS;
    dims[1] = 1;
    totalBytes = this->nmodules*MAX_DIMS*sizeof(epicsInt32);

    /* Get the current time */
    epicsTimeGetCurrent(&timeStamp); 

    /* Allocate a new image buffer */
    pImage = this->pNDArrayPool->alloc(ndims, dims, NDInt32, totalBytes, NULL);
    if (readmode_==0)
      decodeRawReadout(this->nmodules, this->nbits_, pData, (int *)pImage->pData);
    else
      decodeRawReadout(this->nmodules, 24, pData, (int *)pImage->pData);
    //    memcpy(pImage->pData,  pData, totalBytes); 
    pImage->dataType = NDUInt32;
    pImage->ndims = ndims; 
    pImage->dims[0].size = dims[0]; 
    pImage->dims[0].offset = 0; 
    pImage->dims[0].binning = 1; 
    pImage->dims[1].size = dims[1]; 
    pImage->dims[1].offset = 0; 
    pImage->dims[1].binning = 1; 

    pImage->pAttributeList->add("ColorMode", "Color Mode", NDAttrInt32, &colorMode);

    /* Increase image counter */
    getIntegerParam(NDArrayCounter, &imageCounter);
    imageCounter++;
    setIntegerParam(NDArrayCounter, imageCounter);

    /* Set the uniqueId and time stamp */
    pImage->uniqueId = imageCounter; 
    pImage->timeStamp = timeStamp.secPastEpoch + timeStamp.nsec / 1e9; 

    /* Get any attributes that have been defined for this driver */        
    this->getAttributes(pImage->pAttributeList);

    getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
    if (arrayCallbacks) {
        /* 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 */
        this->unlock();
        doCallbacksGenericPointer(pImage, NDArrayData, 0);
        this->lock();
    }

    /* We save the most recent good image buffer so it can be used in the
     * readADImage function.  Now release it. */
    if (this->pArrays[0]) this->pArrays[0]->release();
    this->pArrays[0] = pImage;

    /* Update any changed parameters */
    callParamCallbacks();

    return(1);
}
예제 #15
0
/** This thread computes new image data and does the callbacks to send it to higher layers */
void roper::roperTask()
{
    int status = asynSuccess;
    int imageCounter;
    int numAcquisitions, numAcquisitionsCounter;
    int imageMode;
    int arrayCallbacks;
    int acquire, autoSave;
    NDArray *pImage;
    double acquireTime, acquirePeriod, delay;
    epicsTimeStamp startTime, endTime;
    double elapsedTime;
    const char *functionName = "roperTask";
    VARIANT varArg;
    IDispatch *pDocFileDispatch;
    HRESULT hr;

    /* Initialize the COM system for this thread */
    hr = INITIALIZE_COM;
    if (hr == S_FALSE) {
        /* COM was already initialized for this thread */
        CoUninitialize();
    } else if (hr != S_OK) {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
            "%s:%s: error initializing COM\n",
            driverName, functionName);
    }
    VariantInit(&varArg);

    this->lock();
    /* Loop forever */
    while (1) {
        /* Is acquisition active? */
        getIntegerParam(ADAcquire, &acquire);
        
        /* If we are not acquiring then wait for a semaphore that is given when acquisition is started */
        if (!acquire) {
            setIntegerParam(ADStatus, ADStatusIdle);
            callParamCallbacks();
            /* Release the lock while we wait for an event that says acquire has started, then lock again */
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                "%s:%s: waiting for acquire to start\n", driverName, functionName);
            this->unlock();
            status = epicsEventWait(this->startEventId);
            this->lock();
            getIntegerParam(ADAcquire, &acquire);
            setIntegerParam(RoperNumAcquisitionsCounter, 0);
        }
        
        /* We are acquiring. */
        /* Get the current time */
        epicsTimeGetCurrent(&startTime);
        
        /* Get the exposure parameters */
        getDoubleParam(ADAcquireTime, &acquireTime);
        getDoubleParam(ADAcquirePeriod, &acquirePeriod);
        getIntegerParam(ADImageMode, &imageMode);
        getIntegerParam(RoperNumAcquisitions, &numAcquisitions);
        
        setIntegerParam(ADStatus, ADStatusAcquire);
        
        /* Open the shutter */
        setShutter(ADShutterOpen);

        /* Call the callbacks to update any changes */
        callParamCallbacks();

        try {
            /* Collect the frame(s) */
            /* Stop current exposure, if any */
            this->pExpSetup->Stop();
            this->pDocFile->Close();
            switch (imageMode) {
                case RoperImageNormal:
                case RoperImageContinuous:
                    pDocFileDispatch = pExpSetup->Start2(&varArg);
                    break;
                case RoperImageFocus:
                    pDocFileDispatch = pExpSetup->StartFocus2(&varArg);
                    break;
            }
            pDocFile->AttachDispatch(pDocFileDispatch);

            /* Wait for acquisition to complete, but allow acquire stop events to be handled */
            while (1) {
                this->unlock();
                status = epicsEventWaitWithTimeout(this->stopEventId, ROPER_POLL_TIME);
                this->lock();
                if (status == epicsEventWaitOK) {
                    /* We got a stop event, abort acquisition */
                    this->pExpSetup->Stop();
                    acquire = 0;
                } else {
                    acquire = this->getAcquireStatus();
                }
                if (!acquire) {
                    /* Close the shutter */
                    setShutter(ADShutterClosed);
                    break;
                }
            }
        }
        catch(CException *pEx) {
            pEx->GetErrorMessage(this->errorMessage, sizeof(this->errorMessage));
            asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                "%s:%s: exception = %s\n", 
                driverName, functionName, this->errorMessage);
            pEx->Delete();
        }
        
        /* Get the current parameters */
        getIntegerParam(NDAutoSave,         &autoSave);
        getIntegerParam(NDArrayCounter,     &imageCounter);
        getIntegerParam(RoperNumAcquisitionsCounter, &numAcquisitionsCounter);
        getIntegerParam(NDArrayCallbacks,   &arrayCallbacks);
        imageCounter++;
        numAcquisitionsCounter++;
        setIntegerParam(NDArrayCounter, imageCounter);
        setIntegerParam(RoperNumAcquisitionsCounter, numAcquisitionsCounter);
        
        if (arrayCallbacks) {
            /* Get the data from the DocFile */
            pImage = this->getData();
            if (pImage)  {
                /* Put the frame number and time stamp into the buffer */
                pImage->uniqueId = imageCounter;
                pImage->timeStamp = startTime.secPastEpoch + startTime.nsec / 1.e9;
                /* Get any attributes that have been defined for this driver */        
                this->getAttributes(pImage->pAttributeList);
                /* 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 */
                this->unlock();
                asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                     "%s:%s: calling imageData callback\n", driverName, functionName);
                doCallbacksGenericPointer(pImage, NDArrayData, 0);
                this->lock();
                pImage->release();
            }
        }
        
        /* See if we should save the file */
        if ((imageMode != RoperImageFocus) && autoSave) {
            setIntegerParam(ADStatus, ADStatusSaving);
            callParamCallbacks();
            this->saveFile();
            callParamCallbacks();
        }
        
        /* See if acquisition is done */
        if ((imageMode == RoperImageFocus) ||
            ((imageMode == RoperImageNormal) && 
             (numAcquisitionsCounter >= numAcquisitions))) {
            setIntegerParam(ADAcquire, 0);
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                  "%s:%s: acquisition completed\n", driverName, functionName);
        }
        
        /* Call the callbacks to update any changes */
        callParamCallbacks();
        getIntegerParam(ADAcquire, &acquire);
        
        /* If we are acquiring then sleep for the acquire period minus elapsed time. */
        if (acquire) {
            epicsTimeGetCurrent(&endTime);
            elapsedTime = epicsTimeDiffInSeconds(&endTime, &startTime);
            delay = acquirePeriod - elapsedTime;
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                     "%s:%s: delay=%f\n",
                      driverName, functionName, delay);            
            if (delay >= 0.0) {
                /* We set the status to indicate we are in the period delay */
                setIntegerParam(ADStatus, ADStatusWaiting);
                callParamCallbacks();
                this->unlock();
                status = epicsEventWaitWithTimeout(this->stopEventId, delay);
                this->lock();
            }
        }
    }
}
예제 #16
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Extracts the NthrDArray data into each of the ROIs that are being used.
  * Computes statistics on the ROI if NDPluginROIComputeStatistics is 1.
  * Computes the histogram of ROI values if NDPluginROIComputeHistogram is 1.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginROI::processCallbacks(NDArray *pArray)
{
    /* This function computes the ROIs.
     * It is called with the mutex already locked.  It unlocks it during long calculations when private
     * structures don't need to be protected.
     */

    int dataType;
    int dim;
    NDDimension_t dims[ND_ARRAY_MAX_DIMS], tempDim, *pDim;
    size_t userDims[ND_ARRAY_MAX_DIMS];
    NDArrayInfo arrayInfo, scratchInfo;
    NDArray *pScratch, *pOutput;
    NDColorMode_t colorMode;
    double *pData;
    int enableScale, enableDim[3], autoSize[3];
    size_t i;
    double scale;
    //static const char* functionName = "processCallbacks";
    
    memset(dims, 0, sizeof(NDDimension_t) * ND_ARRAY_MAX_DIMS);

    /* Get all parameters while we have the mutex */
    getIntegerParam(NDPluginROIDim0Bin,      &dims[0].binning);
    getIntegerParam(NDPluginROIDim1Bin,      &dims[1].binning);
    getIntegerParam(NDPluginROIDim2Bin,      &dims[2].binning);
    getIntegerParam(NDPluginROIDim0Reverse,  &dims[0].reverse);
    getIntegerParam(NDPluginROIDim1Reverse,  &dims[1].reverse);
    getIntegerParam(NDPluginROIDim2Reverse,  &dims[2].reverse);
    getIntegerParam(NDPluginROIDim0Enable,   &enableDim[0]);
    getIntegerParam(NDPluginROIDim1Enable,   &enableDim[1]);
    getIntegerParam(NDPluginROIDim2Enable,   &enableDim[2]);
    getIntegerParam(NDPluginROIDim0AutoSize, &autoSize[0]);
    getIntegerParam(NDPluginROIDim1AutoSize, &autoSize[1]);
    getIntegerParam(NDPluginROIDim2AutoSize, &autoSize[2]);
    getIntegerParam(NDPluginROIDataType,     &dataType);
    getIntegerParam(NDPluginROIEnableScale,  &enableScale);
    getDoubleParam(NDPluginROIScale, &scale);

    /* Call the base class method */
    NDPluginDriver::processCallbacks(pArray);

    /* We always keep the last array so read() can use it.
     * Release previous one. Reserve new one below. */
    if (this->pArrays[0]) {
        this->pArrays[0]->release();
        this->pArrays[0] = NULL;
    }
    
    /* Get information about the array */
    pArray->getInfo(&arrayInfo);
    
    userDims[0] = arrayInfo.xDim;
    userDims[1] = arrayInfo.yDim;
    userDims[2] = arrayInfo.colorDim;

    /* Make sure dimensions are valid, fix them if they are not */
    for (dim=0; dim<pArray->ndims; dim++) {
        pDim = &dims[dim];
        if (enableDim[dim]) {
            size_t newDimSize = pArray->dims[userDims[dim]].size;
            pDim->offset  = requestedOffset_[dim];
            pDim->size    = requestedSize_[dim];
            pDim->offset  = MAX(pDim->offset,  0);
            pDim->offset  = MIN(pDim->offset,  newDimSize-1);
            if (autoSize[dim]) pDim->size = newDimSize;
            pDim->size    = MAX(pDim->size,    1);
            pDim->size    = MIN(pDim->size,    newDimSize - pDim->offset);
            pDim->binning = MAX(pDim->binning, 1);
            pDim->binning = MIN(pDim->binning, (int)pDim->size);
        } else {
            pDim->offset  = 0;
            pDim->size    = pArray->dims[userDims[dim]].size;
            pDim->binning = 1;
        }
    }

    /* Update the parameters that may have changed */
    setIntegerParam(NDPluginROIDim0MaxSize, 0);
    setIntegerParam(NDPluginROIDim1MaxSize, 0);
    setIntegerParam(NDPluginROIDim2MaxSize, 0);
    if (pArray->ndims > 0) {
        pDim = &dims[0];
        setIntegerParam(NDPluginROIDim0MaxSize, (int)pArray->dims[userDims[0]].size);
        if (enableDim[0]) {
            setIntegerParam(NDPluginROIDim0Min,  (int)pDim->offset);
            setIntegerParam(NDPluginROIDim0Size, (int)pDim->size);
            setIntegerParam(NDPluginROIDim0Bin,  pDim->binning);
        }
    }
    if (pArray->ndims > 1) {
        pDim = &dims[1];
        setIntegerParam(NDPluginROIDim1MaxSize, (int)pArray->dims[userDims[1]].size);
        if (enableDim[1]) {
            setIntegerParam(NDPluginROIDim1Min,  (int)pDim->offset);
            setIntegerParam(NDPluginROIDim1Size, (int)pDim->size);
            setIntegerParam(NDPluginROIDim1Bin,  pDim->binning);
        }
    }
    if (pArray->ndims > 2) {
        pDim = &dims[2];
        setIntegerParam(NDPluginROIDim2MaxSize, (int)pArray->dims[userDims[2]].size);
        if (enableDim[2]) {
            setIntegerParam(NDPluginROIDim2Min,  (int)pDim->offset);
            setIntegerParam(NDPluginROIDim2Size, (int)pDim->size);
            setIntegerParam(NDPluginROIDim2Bin,  pDim->binning);
        }
    }

    /* This function is called with the lock taken, and it must be set when we exit.
     * The following code can be exected without the mutex because we are not accessing memory
     * that other threads can access. */
    this->unlock();

    /* Extract this ROI from the input array.  The convert() function allocates
     * a new array and it is reserved (reference count = 1) */
    if (dataType == -1) dataType = (int)pArray->dataType;
    /* We treat the case of RGB1 data specially, so that NX and NY are the X and Y dimensions of the
     * image, not the first 2 dimensions.  This makes it much easier to switch back and forth between
     * RGB1 and mono mode when using an ROI. */
    if (arrayInfo.colorMode == NDColorModeRGB1) {
        tempDim = dims[0];
        dims[0] = dims[2];
        dims[2] = dims[1];
        dims[1] = tempDim;
    }
    else if (arrayInfo.colorMode == NDColorModeRGB2) {
        tempDim = dims[1];
        dims[1] = dims[2];
        dims[2] = tempDim;
    }
    
    if (enableScale && (scale != 0) && (scale != 1)) {
        /* This is tricky.  We want to do the operation to avoid errors due to integer truncation.
         * For example, if an image with all pixels=1 is binned 3x3 with scale=9 (divide by 9), then
         * the output should also have all pixels=1. 
         * We do this by extracting the ROI and converting to double, do the scaling, then convert
         * to the desired data type. */
        this->pNDArrayPool->convert(pArray, &pScratch, NDFloat64, dims);
        pScratch->getInfo(&scratchInfo);
        pData = (double *)pScratch->pData;
        for (i=0; i<scratchInfo.nElements; i++) pData[i] = pData[i]/scale;
        this->pNDArrayPool->convert(pScratch, &this->pArrays[0], (NDDataType_t)dataType);
        pScratch->release();
    } 
    else {        
        this->pNDArrayPool->convert(pArray, &this->pArrays[0], (NDDataType_t)dataType, dims);
    }
    pOutput = this->pArrays[0];

    /* If we selected just one color from the array, then we need to change the
     * dimensions and the color mode */
    colorMode = NDColorModeMono;
    if ((pOutput->ndims == 3) && 
        (arrayInfo.colorMode == NDColorModeRGB1) && 
        (pOutput->dims[0].size == 1)) 
    {
        pOutput->ndims = 2;
        pOutput->dims[0] = pOutput->dims[1];
        pOutput->dims[1] = pOutput->dims[2];
        pOutput->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode);
    }
    else if ((pOutput->ndims == 3) && 
        (arrayInfo.colorMode == NDColorModeRGB2) && 
        (pOutput->dims[1].size == 1)) 
    {
        pOutput->ndims = 2;
        pOutput->dims[1] = pOutput->dims[2];
        pOutput->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode);
    }
    else if ((pOutput->ndims == 3) && 
        (pOutput->dims[2].size == 1)) 
    {
        pOutput->ndims = 2;
        pOutput->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32, &colorMode);
    }
    this->lock();

    /* Set the image size of the ROI image data */
    setIntegerParam(NDArraySizeX, 0);
    setIntegerParam(NDArraySizeY, 0);
    setIntegerParam(NDArraySizeZ, 0);
    if (pOutput->ndims > 0) setIntegerParam(NDArraySizeX, (int)this->pArrays[0]->dims[userDims[0]].size);
    if (pOutput->ndims > 1) setIntegerParam(NDArraySizeY, (int)this->pArrays[0]->dims[userDims[1]].size);
    if (pOutput->ndims > 2) setIntegerParam(NDArraySizeZ, (int)this->pArrays[0]->dims[userDims[2]].size);

    /* Get the attributes for this driver */
    this->getAttributes(this->pArrays[0]->pAttributeList);
    /* Call any clients who have registered for NDArray callbacks */
    this->unlock();
    doCallbacksGenericPointer(this->pArrays[0], NDArrayData, 0);
    /* We must enter the loop and exit with the mutex locked */
    this->lock();
    callParamCallbacks();

}
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Does image processing.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginEdge::processCallbacks(NDArray *pArray)
{
  /* This function does array processing.
   * It is called with the mutex already locked.  It unlocks it during long calculations when private
   * structures don't need to be protected.
   */
  NDArray *pScratch=NULL;
  NDArrayInfo arrayInfo;

  int i, j;
  unsigned int numRows, rowSize;
  unsigned char *inData, *outData;
  int edge1;
  int edge1Found;
  int edge2;
  int edge2Found;
  double lowThreshold;
  double thresholdRatio;

  static const char* functionName = "processCallbacks";


  /* Call the base class method */
  NDPluginDriver::processCallbacks(pArray);

  getDoubleParam( NDPluginEdgeLowThreshold,   &lowThreshold);
  getDoubleParam( NDPluginEdgeThresholdRatio, &thresholdRatio);

  /* Do the computationally expensive code with the lock released */
  this->unlock();
  
  /* make the array something we understand */
  this->pNDArrayPool->convert( pArray, &pScratch, NDUInt8);

  pScratch->getInfo(&arrayInfo);

  rowSize = pScratch->dims[arrayInfo.xDim].size;
  numRows = pScratch->dims[arrayInfo.yDim].size;

  cv::Mat img = cv::Mat( numRows, rowSize, CV_8UC1);

  cv::Mat detected_edges;


  // Initialize the output data array
  //
  inData  = (unsigned char *)pScratch->pData;
  outData = (unsigned char *)img.data;
  memcpy( outData, inData, arrayInfo.nElements * sizeof( *inData));

  try {
    // As suggested in the openCV examples, first slightly blur the image
    //
    cv::blur( img, detected_edges, cv::Size(3,3));
  }
  catch( cv::Exception &e) {
    const char* err_msg = e.what();

    asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s cv::blur exception:  %s\n", 
        driverName, functionName, err_msg);
    this->lock();
    return;
  }

  try {
    //
    // Here is the edge detection routine.
    //
    cv::Canny( detected_edges, detected_edges, lowThreshold, thresholdRatio * lowThreshold, 3);
  }
  catch( cv::Exception &e) {
    const char* err_msg = e.what();

    asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "%s::%s cv::Canny exception:  %s\n", 
        driverName, functionName, err_msg);
    this->lock();
    return;
  }

  // Take the lock again since we are accessing the parameter library and 
  // these calculations are not time consuming
  this->lock();
  
  // Try to find the top pixel
  j = rowSize/2;
  edge1Found = 0;
  edge2Found = 0;
  outData = (unsigned char *)detected_edges.data;
  for( i=0; (unsigned int)i<numRows; i++) {
    if( *(outData + i*rowSize + j) != 0) {
      edge1Found = 1;
      break;
    }
  }
  edge1 = i;

  setIntegerParam( NDPluginEdgeTopEdgeFound, edge1Found);
  if( edge1Found)
    setIntegerParam( NDPluginEdgeTopPixel, edge1);

  // Maybe find the bottom pixel
  for( i=numRows - 1; i>=0; i--) {
    if( *(outData + i*rowSize + j) != 0) {
      edge2Found = 1;
      break;
    }
  }

  edge2 = i;
  setIntegerParam( NDPluginEdgeBottomEdgeFound, edge2Found);
  if( edge2Found)
    setIntegerParam( NDPluginEdgeBottomPixel, edge2);


  if( edge1Found && edge2Found && edge1 < edge2) {
    // Both edges found and they are not the same
    //
    setIntegerParam( NDPluginEdgeVerticalFound, 1);
    setDoubleParam( NDPluginEdgeVerticalCenter, (edge1 + edge2)/2.0);
    setIntegerParam(    NDPluginEdgeVerticalSize, edge2 - edge1);
  } else {
    // no edge found
    setIntegerParam( NDPluginEdgeVerticalFound, 0);
  }

  // Find Left pixel
  i = numRows/2;
  edge1Found = 0;
  edge2Found = 0;
  for( j=0; (unsigned int)j<rowSize; j++) {
    if( *(outData + i*rowSize + j) != 0) {
      edge1Found = 1;
      break;
    }
  }
  edge1 = j;

  setIntegerParam( NDPluginEdgeLeftEdgeFound, edge1Found);
  if( edge1Found)
    setIntegerParam( NDPluginEdgeLeftPixel, edge1);


  // Maybe find right pixel
  for( j=rowSize-1; j>=0; j--) {
    if( *(outData + i*rowSize + j) != 0) {
      edge2Found = 1;
      break;
    }
  }
  edge2 = j;

  setIntegerParam( NDPluginEdgeRightEdgeFound, edge2Found);
  if( edge2Found)
    setIntegerParam( NDPluginEdgeRightPixel, edge2);

  if( edge1Found && edge2Found && edge1 < edge2) {
    setIntegerParam( NDPluginEdgeHorizontalFound, 1);
    setDoubleParam( NDPluginEdgeHorizontalCenter, (edge1 + edge2)/2.0);
    setIntegerParam( NDPluginEdgeHorizontalSize, edge2 - edge1);
  } else {
    // no edge found
    setIntegerParam( NDPluginEdgeHorizontalFound, 0);
  }

  int arrayCallbacks = 0;
  getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
  if (arrayCallbacks == 1) {
    inData  = (unsigned char *)detected_edges.data;
    outData = (unsigned char *)pScratch->pData;
    memcpy(outData, inData, arrayInfo.nElements * sizeof( *inData));
    this->getAttributes(pScratch->pAttributeList);
    this->unlock();
    doCallbacksGenericPointer(pScratch, NDArrayData, 0);
    this->lock();
  }

  if (NULL != pScratch)
    pScratch->release();

  callParamCallbacks();
}
예제 #18
0
/** This thread controls acquisition, reads image files to get the image data,
  * and does the callbacks to send it to higher layers */
void hdf5Driver::hdf5Task (void)
{
    const char *functionName = "hdf5Task";
    int status = asynSuccess;
    epicsTimeStamp startTime, endTime;
    int imageMode, currentFrame, colorMode;
    double acquirePeriod, elapsedTime, delay;

    this->lock();

    for(;;)
    {
        int acquire;
        getIntegerParam(ADAcquire, &acquire);

        if (!acquire)
        {
            this->unlock(); // Wait for semaphore unlocked

            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
                    "%s:%s: waiting for acquire to start\n",
                    driverName, functionName);

            status = epicsEventWait(this->mStartEventId);

            this->lock();

            acquire = 1;
            setStringParam(ADStatusMessage, "Acquiring data");
            setIntegerParam(ADNumImagesCounter, 0);
        }

        // Are there datasets loaded?
        if(!mDatasetsCount)
        {
            setStringParam(ADStatusMessage, "No datasets loaded");
            goto error;
        }

        // Get acquisition parameters
        epicsTimeGetCurrent(&startTime);
        getIntegerParam(ADImageMode, &imageMode);
        getDoubleParam(ADAcquirePeriod, &acquirePeriod);
        getIntegerParam(HDF5CurrentFrame, &currentFrame);
        setIntegerParam(ADStatus, ADStatusAcquire);
        callParamCallbacks();

        // Get information to allocate NDArray
        size_t dims[2];
        NDDataType_t dataType;
        if(getFrameInfo(currentFrame, dims, &dataType))
        {
            setStringParam(ADStatusMessage, "Failed to get frame info");
            goto error;
        }

        // Allocate NDArray
        NDArray *pImage;
        if(!(pImage = pNDArrayPool->alloc(2, dims, dataType, 0, NULL)))
        {
            setStringParam(ADStatusMessage, "Failed to allocate frame");
            goto error;
        }

        // Copy data into NDArray
        if(getFrameData(currentFrame, pImage->pData))
        {
            setStringParam(ADStatusMessage, "Failed to read frame data");
            goto error;
        }

        // Set ColorMode
        colorMode = NDColorModeMono;
        pImage->pAttributeList->add("ColorMode", "Color mode", NDAttrInt32,
                &colorMode);

        // Call plugins callbacks
        int arrayCallbacks;
        getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
        if (arrayCallbacks)
        {
          this->unlock();
          asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
                    "%s:%s: calling imageData callback\n",
                    driverName, functionName);
          doCallbacksGenericPointer(pImage, NDArrayData, 0);
          this->lock();
        }
        pImage->release();

        // Get the current parameters
        int lastFrame, imageCounter, numImages, numImagesCounter;
        getIntegerParam(HDF5LastFrame,      &lastFrame);
        getIntegerParam(NDArrayCounter,     &imageCounter);
        getIntegerParam(ADNumImages,        &numImages);
        getIntegerParam(ADNumImagesCounter, &numImagesCounter);

        setIntegerParam(NDArrayCounter,     ++imageCounter);
        setIntegerParam(ADNumImagesCounter, ++numImagesCounter);
        setIntegerParam(HDF5CurrentFrame,   ++currentFrame);

        // Put the frame number and time stamp into the buffer
        pImage->uniqueId = imageCounter;
        pImage->timeStamp = startTime.secPastEpoch + startTime.nsec / 1.e9;
        updateTimeStamp(&pImage->epicsTS);

        // Prepare loop if necessary
        int loop;
        getIntegerParam(HDF5Loop, &loop);

        if (loop && currentFrame > lastFrame)
        {
            getIntegerParam(HDF5FirstFrame,   &currentFrame);
            setIntegerParam(HDF5CurrentFrame, currentFrame);
        }

        // See if acquisition is done
        if (imageMode == ADImageSingle || currentFrame > lastFrame ||
            (imageMode == ADImageMultiple && numImagesCounter >= numImages))
        {
          // First do callback on ADStatus
          setStringParam(ADStatusMessage, "Waiting for acquisition");
          setIntegerParam(ADStatus, ADStatusIdle);

          acquire = 0;
          setIntegerParam(ADAcquire, acquire);

          asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
                  "%s:%s: acquisition completed\n",
                  driverName, functionName);
        }

        callParamCallbacks();

        // Delay next acquisition and check if received STOP signal
        if(acquire)
        {
            epicsTimeGetCurrent(&endTime);
            elapsedTime = epicsTimeDiffInSeconds(&endTime, &startTime);
            delay = acquirePeriod - elapsedTime;
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
                      "%s:%s: delay=%f\n",
                      driverName, functionName, delay);
            if(delay > 0.0)
            {
                // Set the status to waiting to indicate we are in the delay
                setIntegerParam(ADStatus, ADStatusWaiting);
                callParamCallbacks();
                this->unlock();
                status = epicsEventWaitWithTimeout(mStopEventId, delay);
                this->lock();

                if (status == epicsEventWaitOK)
                {
                    acquire = 0;
                    if (imageMode == ADImageContinuous)
                        setIntegerParam(ADStatus, ADStatusIdle);
                    else
                        setIntegerParam(ADStatus, ADStatusAborted);

                  callParamCallbacks();
                }
            }
        }
        continue;

error:
        setIntegerParam(ADAcquire, 0);
        setIntegerParam(ADStatus, ADStatusError);
        callParamCallbacks();
        continue;
    }
}
예제 #19
0
asynStatus drvQuadEM::doDataCallbacks()
{
    epicsFloat64 doubleData[QE_MAX_DATA];
    epicsFloat64 *pIn, *pOut;
    int numAverage;
    int numAveraged;
    int sampleSize = sizeof(doubleData);
    int count;
    epicsTimeStamp now;
    epicsFloat64 timeStamp;
    int arrayCounter;
    int i, j;
    size_t dims[2];
    NDArray *pArrayAll, *pArraySingle;
    static const char *functionName = "doDataCallbacks";
    
    getIntegerParam(P_NumAverage, &numAverage);
    while (1) {
        numAveraged = epicsRingBytesUsedBytes(ringBuffer_) / sampleSize;
        if (numAverage > 0) {
            if (numAveraged < numAverage) break;
            numAveraged = numAverage;
            setIntegerParam(P_NumAveraged, numAveraged);
        } else {
            setIntegerParam(P_NumAveraged, numAveraged);
            if (numAveraged < 1) break;
        }

        dims[0] = QE_MAX_DATA;
        dims[1] = numAveraged;

        epicsTimeGetCurrent(&now);
        getIntegerParam(NDArrayCounter, &arrayCounter);
        arrayCounter++;
        setIntegerParam(NDArrayCounter, arrayCounter);

        pArrayAll = pNDArrayPool->alloc(2, dims, NDFloat64, 0, 0);
        pArrayAll->uniqueId = arrayCounter;
        timeStamp = now.secPastEpoch + now.nsec / 1.e9;
        pArrayAll->timeStamp = timeStamp;
        getAttributes(pArrayAll->pAttributeList);

        count = epicsRingBytesGet(ringBuffer_, (char *)pArrayAll->pData, numAveraged * sampleSize);
        if (count != numAveraged * sampleSize) {
            asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
                "%s:%s: ring read failed\n",
                driverName, functionName);
            return asynError;
        }
        ringCount_ -= numAveraged;
        unlock();
        doCallbacksGenericPointer(pArrayAll, NDArrayData, QE_MAX_DATA);
        lock();
        // Copy data to arrays for each type of data, do callbacks on that.
        dims[0] = numAveraged;
        for (i=0; i<QE_MAX_DATA; i++) {
            pArraySingle = pNDArrayPool->alloc(1, dims, NDFloat64, 0, 0);
            pArraySingle->uniqueId = arrayCounter;
            pArraySingle->timeStamp = timeStamp;
            getAttributes(pArraySingle->pAttributeList);
            pIn = (epicsFloat64 *)pArrayAll->pData;
            pOut = (epicsFloat64 *)pArraySingle->pData;
            for (j=0; j<numAveraged; j++) {
                pOut[j] = pIn[i];
                pIn += QE_MAX_DATA;
            }
            unlock();
            doCallbacksGenericPointer(pArraySingle, NDArrayData, i);
            lock();
            pArraySingle->release();
        }   
        pArrayAll->release();
        callParamCallbacks();
        // We only loop once if numAverage==0
        if (numAverage == 0) break;
    }    
    setIntegerParam(P_RingOverflows, 0);
    callParamCallbacks();
    return asynSuccess;
}
예제 #20
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * If the plugin is running then it attaches position data to the NDArray as NDAttributes
  * and then passes the array on.  If the plugin is not running then NDArrays are not
  * passed through to the next plugin(s) in the chain.
  * \param[in] pArray  The NDArray from the callback.
  */ 
void NDPosPlugin::processCallbacks(NDArray *pArray)
{
  int index = 0;
  int running = NDPOS_IDLE;
  int skip = 0;
  int mode = 0;
  int size = 0;
  int duplicates = 0;
  int dropped = 0;
  int expectedID = 0;
  int IDDifference = 0;
  epicsInt32 IDValue = 0;
  char IDName[MAX_STRING_SIZE];
  static const char *functionName = "NDPosPlugin::processCallbacks";

  // Call the base class method
  NDPluginDriver::processCallbacks(pArray);
  getIntegerParam(NDPos_Running, &running);
  // We must maintain the size of the list ourselves, as calling
  // size() on the list has a complexity of O(n) which causes a problem
  // once we get into tens of thousands of items.
  getIntegerParam(NDPos_CurrentQty, &size);
  // Only attach the position data to the array if we are running
  if (running == NDPOS_RUNNING){
    getIntegerParam(NDPos_CurrentIndex, &index);
    if (index >= size){
      // We've reached the end of our positions, stop to make sure we don't overflow
      setIntegerParam(NDPos_Running, NDPOS_IDLE);
      running = NDPOS_IDLE;
    } else {
      // Read the ID parameter from the NDArray.  If it cannot be found then abort
      getStringParam(NDPos_IDName, MAX_STRING_SIZE, IDName);
      // Check for IDName, if it is empty the we use the unique ID of the array
      if (strcmp(IDName, "") == 0){
        IDValue = pArray->uniqueId;
      } else {
        NDAttribute *IDAtt = pArray->pAttributeList->find(IDName);
        if (IDAtt){
          epicsInt32 IDValue;
          if (IDAtt->getValue(NDAttrInt32, &IDValue, sizeof(epicsInt32)) == ND_ERROR){
            // Error, unable to get the value from the ID attribute
            asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                      "%s::%s ERROR: could not retrieve expected ID from attribute [%s]\n",
                      driverName, functionName, IDName);
            setIntegerParam(NDPos_Running, NDPOS_IDLE);
            running = NDPOS_IDLE;
          }
        } else {
          // Error, unable to find the named ID attribute
          asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                    "%s::%s ERROR: could not find attribute [%s]\n",
                    driverName, functionName, IDName);
          setIntegerParam(NDPos_Running, NDPOS_IDLE);
          running = NDPOS_IDLE;
        }
      }
      if (running == NDPOS_RUNNING){
        // Check the ID is the same as the expected index
        getIntegerParam(NDPos_IDDifference, &IDDifference);
        getIntegerParam(NDPos_ExpectedID, &expectedID);
        if (expectedID < IDValue){
          asynPrint(this->pasynUserSelf, ASYN_TRACE_WARNING,
                    "%s::%s WARNING: possible frame drop detected: expected ID [%d] received ID [%d]\n",
                    driverName, functionName, expectedID, IDValue);
          // If expected is less than ID throw away positions and record dropped events
          getIntegerParam(NDPos_MissingFrames, &dropped);
          getIntegerParam(NDPos_Mode, &mode);
          if (mode == MODE_DISCARD){
            while ((expectedID < IDValue) && (size > 0)){
              // The index will stay the same, and we need to pop the value out of the position array
              positionArray.erase(positionArray.begin());
              size--;
              expectedID += IDDifference;
              dropped++;
            }
            // If the size has dropped to zero then we've run out of positions, abort
            if (size == 0){
              setIntegerParam(NDPos_Running, NDPOS_IDLE);
              running = NDPOS_IDLE;
            }
            setIntegerParam(NDPos_CurrentQty, size);
          } else if (mode == MODE_KEEP){
            while (expectedID < IDValue && (index < size)){
              index++;
              expectedID += IDDifference;
              dropped++;
            }
            // If the index has reached the size of the array then we've run out of positions, abort
            if (index == size){
              setIntegerParam(NDPos_Running, NDPOS_IDLE);
              running = NDPOS_IDLE;
            }
            setIntegerParam(NDPos_CurrentIndex, index);
          }
          setIntegerParam(NDPos_ExpectedID, expectedID);
          setIntegerParam(NDPos_MissingFrames, dropped);
        } else if (expectedID > IDValue){
          asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                    "%s::%s ERROR: dropping frame! possible duplicate detected: expected ID [%d] received ID [%d]\n",
                    driverName, functionName, expectedID, IDValue);
          // If expected is greater than ID then ignore the frame and record duplicate event
          getIntegerParam(NDPos_DuplicateFrames, &duplicates);
          duplicates++;
          setIntegerParam(NDPos_DuplicateFrames, duplicates);
          skip = 1;
        }
      }

      // Only perform the actual setting of positions if we aren't skipping
      if (skip == 0 && running == NDPOS_RUNNING){
        // We always keep the last array so read() can use it.
        // Release previous one. Reserve new one below during the copy.
        if (this->pArrays[0]){
          this->pArrays[0]->release();
          this->pArrays[0] = NULL;
        }
        // We must make a copy of the array as we are going to alter it
        this->pArrays[0] = this->pNDArrayPool->copy(pArray, this->pArrays[0], 1);
        if (this->pArrays[0]){
          std::list<std::map<std::string, double> >::iterator it = positionArray.begin();
          std::advance(it, index);
          //std::map<std::string, double> pos = positionArray[index];
          std::map<std::string, double> pos = *it;
          std::stringstream sspos;
          sspos << "[";
          bool firstTime = true;
          std::map<std::string, double>::iterator iter;
          for (iter = pos.begin(); iter != pos.end(); iter++){
            if (firstTime){
              firstTime = false;
            } else {
              sspos << ",";
            }
            sspos << iter->first << "=" << iter->second;
            // Create the NDAttribute with the position data
            NDAttribute *pAtt = new NDAttribute(iter->first.c_str(), "Position of NDArray", NDAttrSourceDriver, driverName, NDAttrFloat64, &(iter->second));
            // Add the NDAttribute to the NDArray
            this->pArrays[0]->pAttributeList->add(pAtt);
          }
          sspos << "]";
          setStringParam(NDPos_CurrentPos, sspos.str().c_str());

        } else {
          // We were unable to allocate the required buffer (memory or qty exceeded).
          // This results in us dropping a frame, note it and print an error
          asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
                    "%s::%s ERROR: dropped frame! Could not allocate the required buffer\n",
                    driverName, functionName);
          // Note the frame drop
          getIntegerParam(NDPluginDriverDroppedArrays, &dropped);
          dropped++;
          setIntegerParam(NDPluginDriverDroppedArrays, dropped);
          skip = 1;
        }

        // Check the mode
        getIntegerParam(NDPos_Mode, &mode);
        if (mode == MODE_DISCARD){
          // The index will stay the same, and we need to pop the value out of the position array
          positionArray.erase(positionArray.begin());
          size--;
          setIntegerParam(NDPos_CurrentQty, size);
        } else if (mode == MODE_KEEP){
          index++;
          setIntegerParam(NDPos_CurrentIndex, index);
        }
        // Increment the expectedID by the difference
        expectedID += IDDifference;
        setIntegerParam(NDPos_ExpectedID, expectedID);
      }
    }
    // If the size has dropped to zero then we've run out of positions, abort
    if (size == 0){
      setIntegerParam(NDPos_Running, NDPOS_IDLE);
    }
    callParamCallbacks();
    if (skip == 0 && running == NDPOS_RUNNING){
      this->unlock();
      doCallbacksGenericPointer(this->pArrays[0], NDArrayData, 0);
      this->lock();
    }
  }
}
예제 #21
0
/** Callback function that is called by the NDArray driver with new NDArray data.
  * Does image processing.
  * \param[in] pArray  The NDArray from the callback.
  */
void NDPluginProcess::processCallbacks(NDArray *pArray)
{
    /* This function does array processing.
     * It is called with the mutex already locked.  It unlocks it during long calculations when private
     * structures don't need to be protected.
     */
    size_t i;
    NDArray *pScratch=NULL;
    double  *data, newData, newFilter;
    NDArrayInfo arrayInfo;
    double  *background=NULL, *flatField=NULL, *filter=NULL;
    double  value;
    size_t  nElements;
    int     saveBackground, enableBackground, validBackground;
    int     saveFlatField,  enableFlatField,  validFlatField;
    double  scaleFlatField;
    int     enableOffsetScale, autoOffsetScale;
    double  offset, scale, minValue, maxValue;
    double  lowClip=0, highClip=0;
    int     enableLowClip, enableHighClip;
    int     resetFilter, autoResetFilter, filterCallbacks, doCallbacks=1;
    int     enableFilter, numFilter;
    int     dataType;
    int     anyProcess;
    double  oOffset, fOffset, rOffset, oScale, fScale;
    double  oc1, oc2, oc3, oc4;
    double  fc1, fc2, fc3, fc4;
    double  rc1, rc2;
    double  F1, F2, O1, O2;

    NDArray *pArrayOut = NULL;
    static const char* functionName = "processCallbacks";

    /* Call the base class method */
    NDPluginDriver::processCallbacks(pArray);

    /* Need to fetch all of these parameters while we still have the mutex */
    getIntegerParam(NDPluginProcessDataType,            &dataType);
    getIntegerParam(NDPluginProcessSaveBackground,      &saveBackground);
    getIntegerParam(NDPluginProcessEnableBackground,    &enableBackground);
    getIntegerParam(NDPluginProcessSaveFlatField,       &saveFlatField);
    getIntegerParam(NDPluginProcessEnableFlatField,     &enableFlatField);
    getDoubleParam (NDPluginProcessScaleFlatField,      &scaleFlatField);
    getIntegerParam(NDPluginProcessEnableOffsetScale,   &enableOffsetScale);
    getIntegerParam(NDPluginProcessAutoOffsetScale,     &autoOffsetScale);
    getIntegerParam(NDPluginProcessEnableLowClip,       &enableLowClip);
    getIntegerParam(NDPluginProcessEnableHighClip,      &enableHighClip);
    getIntegerParam(NDPluginProcessEnableFilter,        &enableFilter);
    getIntegerParam(NDPluginProcessResetFilter,         &resetFilter);
    getIntegerParam(NDPluginProcessAutoResetFilter,     &autoResetFilter);
    getIntegerParam(NDPluginProcessFilterCallbacks,     &filterCallbacks);

    if (enableOffsetScale) {
        getDoubleParam (NDPluginProcessScale,           &scale);
        getDoubleParam (NDPluginProcessOffset,          &offset);
    }
    if (enableLowClip) 
        getDoubleParam (NDPluginProcessLowClip,         &lowClip);
    if (enableHighClip) 
        getDoubleParam (NDPluginProcessHighClip,        &highClip);
    if (resetFilter) 
        setIntegerParam(NDPluginProcessResetFilter, 0);
    if (enableFilter) {
        getIntegerParam(NDPluginProcessNumFilter,       &numFilter);
        getDoubleParam (NDPluginProcessOOffset,         &oOffset);
        getDoubleParam (NDPluginProcessOScale,          &oScale);
        getDoubleParam (NDPluginProcessOC1,             &oc1);
        getDoubleParam (NDPluginProcessOC2,             &oc2);
        getDoubleParam (NDPluginProcessOC3,             &oc3);
        getDoubleParam (NDPluginProcessOC4,             &oc4);
        getDoubleParam (NDPluginProcessFOffset,         &fOffset);
        getDoubleParam (NDPluginProcessFScale,          &fScale);
        getDoubleParam (NDPluginProcessFC1,             &fc1);
        getDoubleParam (NDPluginProcessFC2,             &fc2);
        getDoubleParam (NDPluginProcessFC3,             &fc3);
        getDoubleParam (NDPluginProcessFC4,             &fc4);
        getDoubleParam (NDPluginProcessROffset,         &rOffset);
        getDoubleParam (NDPluginProcessRC1,             &rc1);
        getDoubleParam (NDPluginProcessRC2,             &rc2);
    }

    /* Release the lock now that we are only doing things that don't involve memory other thread
     * cannot access */
    this->unlock();
    /* Special case for automatic data type */
    if (dataType == -1) dataType = (int)pArray->dataType;
    
    pArray->getInfo(&arrayInfo);
    nElements = arrayInfo.nElements;

    validBackground = 0;
    if (this->pBackground && (nElements == this->nBackgroundElements)) validBackground = 1;
    setIntegerParam(NDPluginProcessValidBackground, validBackground);
    validFlatField = 0;
    if (this->pFlatField && (nElements == this->nFlatFieldElements)) validFlatField = 1;
    setIntegerParam(NDPluginProcessValidFlatField, validFlatField);

    if (validBackground && enableBackground)
        background = (double *)this->pBackground->pData;
    if (validFlatField && enableFlatField)
        flatField = (double *)this->pFlatField->pData;

    anyProcess = ((enableBackground && validBackground) ||
                  (enableFlatField && validFlatField)   ||
                   enableOffsetScale                    ||
                   autoOffsetScale                      ||
                   enableHighClip                       || 
                   enableLowClip                        ||
                   enableFilter);
    /* If no processing is to be done just convert the input array and do callbacks */
    if (!anyProcess) {
        /* Convert the array to the desired output data type */
        this->pNDArrayPool->convert(pArray, &pArrayOut, (NDDataType_t)dataType);
        goto doCallbacks;
    }
    
    /* Make a copy of the array converted to double, because we cannot modify the input array */
    this->pNDArrayPool->convert(pArray, &pScratch, NDFloat64);
    if (NULL == pScratch) {
        asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
            "%s:%s Processing aborted; cannot allocate an NDArray for storage of temporary data.\n", 
            driverName, functionName);
        goto doCallbacks;
    }
    data = (double *)pScratch->pData;

    if (nElements > 0) {
        minValue = data[0];
        maxValue = data[0];
    } else {
        minValue = 0;
        maxValue = 1;
    }
    for (i=0; i<nElements; i++) {
        value = data[i];
        if (autoOffsetScale) {
            if (data[i] < minValue) minValue = data[i];
            if (data[i] > maxValue) maxValue = data[i];
        }
        if (background) value -= background[i];
        if (flatField) {
            if (flatField[i] != 0.) 
                value *= scaleFlatField / flatField[i];
            else
                value = scaleFlatField;
        }
        if (enableOffsetScale) value = (value + offset)*scale;
        if (enableHighClip && (value > highClip)) value = highClip;
        if (enableLowClip  && (value < lowClip))  value = lowClip;
        data[i] = value;
    }
    
    if (enableFilter) {
        if (this->pFilter) {
            this->pFilter->getInfo(&arrayInfo);
            if (nElements != arrayInfo.nElements) {
                this->pFilter->release();
                this->pFilter = NULL;
            }
        }
        if (!this->pFilter) {
            /* There is not a current filter array */
            /* Make a copy of the current array, converted to double type */
            this->pNDArrayPool->convert(pScratch, &this->pFilter, NDFloat64);
            if (NULL == this->pFilter) {
                asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, 
                    "%s:%s Processing aborted; cannot allocate an NDArray to store the filter.\n", 
                    driverName,functionName);
                goto doCallbacks;
            }
            resetFilter = 1;
        }
        if ((this->numFiltered >= numFilter) && autoResetFilter)
          resetFilter = 1;
        if (resetFilter) {
            filter = (double *)this->pFilter->pData;
            for (i=0; i<nElements; i++) {
                newFilter = rOffset;
                if (rc1) newFilter += rc1*filter[i];
                if (rc2) newFilter += rc2*data[i];
                filter[i] = newFilter;
            }           
            this->numFiltered = 0;
        }
        /* Do the filtering */
        if (this->numFiltered < numFilter) this->numFiltered++;
        filter = (double *)this->pFilter->pData;
        O1 = oScale * (oc1 + oc2/this->numFiltered);
        O2 = oScale * (oc3 + oc4/this->numFiltered);
        F1 = fScale * (fc1 + fc2/this->numFiltered);
        F2 = fScale * (fc3 + fc4/this->numFiltered);
        for (i=0; i<nElements; i++) {
            newData   = oOffset;
            if (O1) newData += O1 * filter[i];
            if (O2) newData += O2 * data[i];
            newFilter = fOffset;
            if (F1) newFilter += F1 * filter[i];
            if (F2) newFilter += F2 * data[i];
            data[i] = newData;
            filter[i] = newFilter;
        }
        if ((this->numFiltered != numFilter) && filterCallbacks)
          doCallbacks = 0;
    }

    if (doCallbacks) {
      /* Convert the array to the desired output data type */
      this->pNDArrayPool->convert(pScratch, &pArrayOut, (NDDataType_t)dataType);
    }

    if (autoOffsetScale && (NULL != pArrayOut)) {
        pArrayOut->getInfo(&arrayInfo);
        double maxScale = pow(2., arrayInfo.bytesPerElement*8) - 1;
        scale = maxScale /(maxValue-minValue);
        offset = -minValue;
        setDoubleParam (NDPluginProcessScale,             scale);
        setDoubleParam (NDPluginProcessOffset,            offset);
        setDoubleParam (NDPluginProcessLowClip,           0);
        setDoubleParam (NDPluginProcessHighClip,          maxScale);
        setIntegerParam(NDPluginProcessEnableOffsetScale, 1);
        setIntegerParam(NDPluginProcessEnableLowClip,     1);
        setIntegerParam(NDPluginProcessEnableHighClip,    1);
    }

    doCallbacks:    
    /* We must exit with the mutex locked */
    this->lock();
    if (doCallbacks && (NULL != pArrayOut)) {
        /* Get the attributes from this driver */
        this->getAttributes(pArrayOut->pAttributeList);
        /* Call any clients who have registered for NDArray callbacks */
        this->unlock();
        doCallbacksGenericPointer( pArrayOut, NDArrayData, 0);
        this->lock();
        if (NULL != this->pArrays[0]) this->pArrays[0]->release();
        this->pArrays[0] = pArrayOut;
    }

    if (NULL != pScratch) pScratch->release();

    setIntegerParam(NDPluginProcessNumFiltered, this->numFiltered);
    callParamCallbacks();
    if (autoOffsetScale && this->pArrays[0] != NULL) {
        setIntegerParam(NDPluginProcessAutoOffsetScale, 0);
        callParamCallbacks();
    }
}
/** This thread controls acquisition, reads SFRM files to get the image data, and
  * does the callbacks to send it to higher layers */
void BISDetector::BISTask()
{
    int status = asynSuccess;
    int imageCounter;
        int numImages, numImagesCounter;
    int imageMode;
    int acquire;
    NDArray *pImage;
    double acquireTime, timeRemaining;
    ADShutterMode_t shutterMode;
    int frameType;
    int numDarks;
    double readSFRMTimeout;
    epicsTimeStamp startTime, currentTime;
    const char *functionName = "BISTask";
    char fullFileName[MAX_FILENAME_LEN];
    char statusMessage[MAX_MESSAGE_SIZE];
    size_t dims[2];
    int itemp;
    int arrayCallbacks;
    
    this->lock();

    /* Loop forever */
    while (1) {
        /* Is acquisition active? */
        getIntegerParam(ADAcquire, &acquire);
        
        /* If we are not acquiring then wait for a semaphore that is given when acquisition is started */
        if (!acquire) {
            setStringParam(ADStatusMessage, "Waiting for acquire command");
            setIntegerParam(ADStatus, ADStatusIdle);
            callParamCallbacks();
            /* Release the lock while we wait for an event that says acquire has started, then lock again */
            this->unlock();
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                "%s:%s: waiting for acquire to start\n", driverName, functionName);
            status = epicsEventWait(this->startEventId);
            this->lock();
            setIntegerParam(ADNumImagesCounter, 0);
        }
        
        /* Get current values of some parameters */
        getIntegerParam(ADFrameType, &frameType);
        /* Get the exposure parameters */
        getDoubleParam(ADAcquireTime, &acquireTime);
        getIntegerParam(ADShutterMode, &itemp);  shutterMode = (ADShutterMode_t)itemp;
        getDoubleParam(BISSFRMTimeout, &readSFRMTimeout);
        
        setIntegerParam(ADStatus, ADStatusAcquire);

        /* Create the full filename */
        createFileName(sizeof(fullFileName), fullFileName);
        
        setStringParam(ADStatusMessage, "Starting exposure");
        /* Call the callbacks to update any changes */
        setStringParam(NDFullFileName, fullFileName);
        callParamCallbacks();
        switch (frameType) {
            case BISFrameNormal:
                epicsSnprintf(this->toBIS, sizeof(this->toBIS), 
                    "[Scan /Filename=%s /scantime=%f /Rescan=0]", fullFileName, acquireTime);
                break;
            case BISFrameDark:
                getIntegerParam(BISNumDarks, &numDarks);
                epicsSnprintf(this->toBIS, sizeof(this->toBIS), 
                    "[Dark /AddTime=%f /Repetitions=%d]", acquireTime, numDarks);
                break;
            case BISFrameRaw:
                epicsSnprintf(this->toBIS, sizeof(this->toBIS), 
                    "[Scan /Filename=%s /scantime=%f /Rescan=0 /DarkFlood=0]", fullFileName, acquireTime);
                break;
            case BISFrameDoubleCorrelation:
                epicsSnprintf(this->toBIS, sizeof(this->toBIS), 
                    "[Scan /Filename=%s /scantime=%f /Rescan=1]", fullFileName, acquireTime);
                break;
        }
        /* Send the acquire command to BIS */
        writeBIS(2.0);

        setStringParam(ADStatusMessage, "Waiting for Acquisition");
        callParamCallbacks();
        /* Set the the start time for the TimeRemaining counter */
        epicsTimeGetCurrent(&startTime);
        timeRemaining = acquireTime;

        /* BIS will control the shutter if we are using the hardware shutter signal.
         * If we are using the EPICS shutter then tell it to open */
        if (shutterMode == ADShutterModeEPICS) ADDriver::setShutter(1);

        /* Wait for the exposure time using epicsEventWaitWithTimeout, 
         * so we can abort. */
        epicsTimerStartDelay(this->timerId, acquireTime);
        while(1) {
            this->unlock();
            status = epicsEventWaitWithTimeout(this->stopEventId, BIS_POLL_DELAY);
            this->lock();
            if (status == epicsEventWaitOK) {
                /* The acquisition was stopped before the time was complete */
                epicsTimerCancel(this->timerId);
                break;
            }
            epicsTimeGetCurrent(&currentTime);
            timeRemaining = acquireTime -  epicsTimeDiffInSeconds(&currentTime, &startTime);
            if (timeRemaining < 0.) timeRemaining = 0.;
            setDoubleParam(ADTimeRemaining, timeRemaining);
            callParamCallbacks();
        }
        if (shutterMode == ADShutterModeEPICS) ADDriver::setShutter(0);
        setDoubleParam(ADTimeRemaining, 0.0);
        callParamCallbacks();
        this->unlock();
        status = epicsEventWaitWithTimeout(this->readoutEventId, 5.0);
        this->lock();
        /* If there was an error jump to bottom of loop */
        if (status != epicsEventWaitOK) {
            setIntegerParam(ADAcquire, 0);
            asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
                "%s:%s: error waiting for readout to complete\n",
                driverName, functionName);
            goto done;
        }
        getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
        getIntegerParam(NDArrayCounter, &imageCounter);
        imageCounter++;
        setIntegerParam(NDArrayCounter, imageCounter);
        getIntegerParam(ADNumImagesCounter, &numImagesCounter);
        numImagesCounter++;
        setIntegerParam(ADNumImagesCounter, numImagesCounter);
        callParamCallbacks();

        if (arrayCallbacks && frameType != BISFrameDark) {
            /* Get an image buffer from the pool */
            getIntegerParam(ADSizeX, &itemp); dims[0] = itemp;
            getIntegerParam(ADSizeY, &itemp); dims[1] = itemp;
            pImage = this->pNDArrayPool->alloc(2, dims, NDInt32, 0, NULL);
            epicsSnprintf(statusMessage, sizeof(statusMessage), "Reading from File %s", fullFileName);
            setStringParam(ADStatusMessage, statusMessage);
            callParamCallbacks();
            status = readSFRM(fullFileName, &startTime, acquireTime + readSFRMTimeout, pImage); 
            /* If there was an error jump to bottom of loop */
            if (status) {
                setIntegerParam(ADAcquire, 0);
                pImage->release();
                goto done;
            } 

            /* Put the frame number and time stamp into the buffer */
            pImage->uniqueId = imageCounter;
            pImage->timeStamp = startTime.secPastEpoch + startTime.nsec / 1.e9;
            updateTimeStamp(&pImage->epicsTS);

            /* Get any attributes that have been defined for this driver */        
            this->getAttributes(pImage->pAttributeList);

            /* 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 */
            this->unlock();
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, 
                 "%s:%s: calling NDArray callback\n", driverName, functionName);
            doCallbacksGenericPointer(pImage, NDArrayData, 0);
            this->lock();
            /* Free the image buffer */
            pImage->release();
        }
        getIntegerParam(ADImageMode, &imageMode);
        if (imageMode == ADImageMultiple) {
            getIntegerParam(ADNumImages, &numImages);
            if (numImagesCounter >= numImages) setIntegerParam(ADAcquire, 0);
        }    
        if (imageMode == ADImageSingle) setIntegerParam(ADAcquire, 0);
        done:
        callParamCallbacks();
    }
}