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(); } }
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(); }
/** 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); }
/** 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(); }
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); } } }
/** 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(); }
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; }
/** * 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); }
/** * 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(); }
/** 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, ¤tImage); getIntegerParam(NDCircBuffPostCount, ¤tPostCount); 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(); }
/** 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, ¤tTSPoint); 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(); }
/** 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(); }
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); }
/** 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(); } } } }
/** 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(); }
/** 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, ¤tFrame); 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, ¤tFrame); 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; } }
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; }
/** 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(); } } }
/** 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(¤tTime); timeRemaining = acquireTime - epicsTimeDiffInSeconds(¤tTime, &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(); } }