/** * Area Detector driver for the Andor CCD. * * @author Matthew Pearson * @date June 2009 * * Updated Dec 2011 for Asyn 4-17 and areaDetector 1-7 * */ #include #include #include "andorCCD.h" #include #include using std::cout; using std::endl; using std::flush; using std::ofstream; //Definitions of static class data members const epicsUInt32 AndorCCD::AASingle = 1; const epicsUInt32 AndorCCD::AAAccumulate = 2; const epicsUInt32 AndorCCD::AAKinetics = 3; const epicsUInt32 AndorCCD::AAFastKinetics = 4; const epicsUInt32 AndorCCD::AARunTillAbort = 5; const epicsUInt32 AndorCCD::AATimeDelayedInt = 9; const epicsUInt32 AndorCCD::ATInternal = 0; const epicsUInt32 AndorCCD::ATExternal = 1; const epicsUInt32 AndorCCD::ATExternalStart = 6; const epicsUInt32 AndorCCD::ATExternalExposure = 7; const epicsUInt32 AndorCCD::ATExternalFVB = 9; const epicsUInt32 AndorCCD::ATSoftware = 10; const epicsUInt32 AndorCCD::ASIdle = DRV_IDLE; const epicsUInt32 AndorCCD::ASTempCycle = DRV_TEMPCYCLE; const epicsUInt32 AndorCCD::ASAcquiring = DRV_ACQUIRING; const epicsUInt32 AndorCCD::ASAccumTimeNotMet = DRV_ACCUM_TIME_NOT_MET; const epicsUInt32 AndorCCD::ASKineticTimeNotMet = DRV_KINETIC_TIME_NOT_MET; const epicsUInt32 AndorCCD::ASErrorAck = DRV_ERROR_ACK; const epicsUInt32 AndorCCD::ASAcqBuffer = DRV_ACQ_BUFFER; const epicsUInt32 AndorCCD::ASSpoolError = DRV_SPOOLERROR; const epicsInt32 AndorCCD::ARFullVerticalBinning = 0; const epicsInt32 AndorCCD::ARMultiTrack = 1; const epicsInt32 AndorCCD::ARRandomTrack = 2; const epicsInt32 AndorCCD::ARSingleTrack = 3; const epicsInt32 AndorCCD::ARImage = 4; const epicsInt32 AndorCCD::AShutterAuto = 0; const epicsInt32 AndorCCD::AShutterOpen = 1; const epicsInt32 AndorCCD::AShutterClose = 2; const epicsInt32 AndorCCD::AFFTIFF = 0; const epicsInt32 AndorCCD::AFFBMP = 1; const epicsInt32 AndorCCD::AFFSIF = 2; const epicsInt32 AndorCCD::AFFEDF = 3; const epicsInt32 AndorCCD::AFFRAW = 4; const epicsInt32 AndorCCD::AFFTEXT = 5; const std::string AndorCCD::INSTALL_PATH = "/usr/local/etc/andor"; //C Function prototypes to tie in with EPICS static void andorStatusTaskC(void *drvPvt); static void andorDataTaskC(void *drvPvt); /** * Constructor for AndorCCD::AndorCCD. * */ AndorCCD::AndorCCD(const char *portName, int maxBuffers, size_t maxMemory, int maxSizeX, int maxSizeY) : ADDriver(portName, 1, NUM_ANDOR_DET_PARAMS, maxBuffers, maxMemory, 0, 0, ASYN_CANBLOCK, 1, 0, 0) { int status = asynSuccess; at_32 param1 = 0; const char *functionName = "AndorCCD::AndorCCD"; cout << "Constructing AndorCCD driver..." << endl; /* Set some default values for parameters */ this->lock(); createParam(AndorCoolerParamString, asynParamInt32, &AndorCoolerParam); createParam(AndorShutdownParamString, asynParamInt32, &AndorShutdownParam); createParam(AndorStartupParamString, asynParamInt32, &AndorStartupParam); createParam(AndorImageModeAMultiParamString, asynParamInt32, &AndorImageModeAMultiParam); createParam(AndorACTInKineticsParamString, asynParamInt32, &AndorACTInKineticsParam); createParam(AndorANumInKineticsParamString, asynParamInt32, &AndorANumInKineticsParam); createParam(AndorFKHeightParamString, asynParamInt32, &AndorFKHeightParam); createParam(AndorFKHBinningParamString, asynParamInt32, &AndorFKHBinningParam); createParam(AndorFKVBinningParamString, asynParamInt32, &AndorFKVBinningParam); createParam(AndorFKOffsetParamString, asynParamInt32, &AndorFKOffsetParam); createParam(AndorTempStatusMessageString, asynParamOctet, &AndorTempStatusMessage); createParam(AndorMessageString, asynParamOctet, &AndorMessage); createParam(AndorShutterModeString, asynParamInt32, &AndorShutterMode); createParam(AndorShutterExTTLString, asynParamInt32, &AndorShutterExTTL); createParam(AndorPalFileNameString, asynParamOctet, &AndorPalFileName); createParam(AndorAdcSpeedString, asynParamInt32, &AndorAdcSpeed); this->unlock(); //Create the epicsEvent for signaling to the status task when parameters should have changed. //This will cause it to do a poll immediately, rather than wait for the poll time period. this->statusEvent = epicsEventMustCreate(epicsEventEmpty); if (!this->statusEvent) { printf("%s:%s epicsEventCreate failure for start event\n", driverName, functionName); return; } //Use this to signal the data acquisition task that acquisition has started. this->dataEvent = epicsEventMustCreate(epicsEventEmpty); if (!this->dataEvent) { printf("%s:%s epicsEventCreate failure for data event\n", driverName, functionName); return; } //The camera is not initialized yet. mRunning = 0; //Set default driver start parameters for binning and sub-area readout. //Use 1 based (which Andor uses. areaDetector is 0 based) mXBinning = 1; mYBinning = 1; mXStart = 1; mYStart = 1; mXEnd = maxSizeX; mYEnd = maxSizeY; mXSize = maxSizeX; mYSize = maxSizeY; mXMaxSize = maxSizeX; mYMaxSize = maxSizeY; mShutterExTTL = 1; //Use high TTL signal for external shutter mShutterMode = AShutterAuto; //Auto mode mShutterCloseTime = 0; //milliseconds mShutterOpenTime = 0; //milliseconds //Initialize camera initializeCCD(INSTALL_PATH); try { checkStatus(GetAvailableCameras(¶m1)); cout << "Number of cameras found: " << param1 << endl; } catch (const std::string &e) { cout << e << endl; } /* Set some default values for parameters */ this->lock(); status = setStringParam(ADManufacturer, "Andor"); status |= setStringParam(ADModel, "CCD"); status |= setIntegerParam(ADSizeX, maxSizeX-1); status |= setIntegerParam(ADSizeY, maxSizeY-1); status |= setIntegerParam(ADMaxSizeX, maxSizeX-1); status |= setIntegerParam(ADMaxSizeY, maxSizeY-1); status |= setIntegerParam(ADImageMode, ADImageSingle); status |= setIntegerParam(ADTriggerMode, AndorCCD::ATInternal); status |= setDoubleParam (ADAcquireTime, 1.0); status |= setDoubleParam (ADAcquirePeriod, 1.0); status |= setIntegerParam(ADNumImages, 1); status |= setIntegerParam(AndorFKHeightParam, 1); status |= setIntegerParam(AndorFKHBinningParam, 1); status |= setIntegerParam(AndorFKVBinningParam, 1); status |= setIntegerParam(AndorFKOffsetParam, 0); status |= setIntegerParam(NDArraySizeX, mXSize); status |= setIntegerParam(NDArraySizeY, mYSize); status |= setIntegerParam(NDArraySize, mXSize*mYSize*2); callParamCallbacks(); this->unlock(); AAModeCurrent = AASingle; AAModeMulti = AAAccumulate; mACTInKinetics = 0; mTriggerMode = ATInternal; if (status) { printf("%s: unable to set camera parameters\n", functionName); return; } //Define the polling periods for the status thread. mPollingPeriod = 0.2; //seconds mFastPollingPeriod = 0.05; //seconds mAcquiringData = 0; //Allocate space for data (single image) mDataSize = mXSize * mYSize; mData = NULL; mData = (at_32 *) calloc(mDataSize, sizeof(at_32)); if (mData == NULL) { cout << driverName << ":" << functionName << " ERROR: Could not allocate enough memory for data" << endl; return; } /* Create the thread that updates the detector status */ status = (epicsThreadCreate("AndorStatusTask", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), (EPICSTHREADFUNC)andorStatusTaskC, this) == NULL); if (status) { printf("%s:%s epicsThreadCreate failure for status task\n", driverName, functionName); return; } /* Create the thread that does data readout */ status = (epicsThreadCreate("AndorDataTask", epicsThreadPriorityMedium, epicsThreadGetStackSize(epicsThreadStackMedium), (EPICSTHREADFUNC)andorDataTaskC, this) == NULL); if (status) { printf("%s:%s epicsThreadCreate failure for data task\n", driverName, functionName); return; } } AndorCCD::~AndorCCD() { //NOTE this destructor doesn't seem to be called. printf("Destructing.\n"); } /** * Initialize the CCD camera using the built SDK Initialize(path) function. * @param path The path to the local install of the SDK config files (usually /usr/local/etc/andor) */ void AndorCCD::initializeCCD(const std::string &path) { //Initialize camera try { cout << "Initializing CCD...\n" << endl; checkStatus(Initialize(const_cast(path.c_str()))); this->lock(); setStringParam(AndorMessage, "Camera successfully initialized."); runAtInitialization(); mRunning = 1; callParamCallbacks(); this->unlock(); } catch (const std::string &e) { cout << e << endl; } } /** * Shutdown the CCD. */ void AndorCCD::shutdownCCD(void) { try { cout << "Shutdown and freeing up memory..." << endl; this->lock(); checkStatus(FreeInternalMemory()); checkStatus(ShutDown()); setStringParam(AndorMessage, "Camera successfully shutdown. Restart IOC."); //Free data memory free(mData); mRunning = 0; callParamCallbacks(); this->unlock(); } catch (const std::string &e) { cout << e << endl; } } /** * Run this after the SDK driver has been initilaized. * It reads some detector information and prints to stdout. */ asynStatus AndorCCD::runAtInitialization(void) { int param1 = 0; int xsize = 0; int ysize = 0; unsigned int uIntParam1 = 0; unsigned int uIntParam2 = 0; unsigned int uIntParam3 = 0; unsigned int uIntParam4 = 0; unsigned int uIntParam5 = 0; unsigned int uIntParam6 = 0; cout << "Andor CCD camera information:" << endl; try { checkStatus(GetCameraSerialNumber(¶m1)); cout << " serial number: " << param1 << endl; checkStatus(GetHardwareVersion(&uIntParam1,&uIntParam2,&uIntParam3,&uIntParam4,&uIntParam5,&uIntParam6)); cout << " PCB Version: " << uIntParam1 << endl; cout << " Flex File Version: " << uIntParam2 << endl; cout << " Firmware Version: " << uIntParam5 << endl; cout << " Firmware Build: " << uIntParam6 << endl; checkStatus(GetDetector(&xsize, &ysize)); cout << " xpixels: " << xsize << endl; cout << " ypixels: " << ysize << endl; checkStatus(GetNumberAmp(¶m1)); cout << " Number of amplifier channels: " << param1 << endl; checkStatus(GetNumberADChannels(¶m1)); cout << " Number of ADC channels: " << param1 << endl; //Code to find the ADC with the fastest sampling rate. //This is commented out because the ADC channel can be set by the database now. /*float maxSpeed = 0.0; int fastestChannel = 0; for (int i=0; i maxSpeed) { maxSpeed = temp; fastestChannel = i; } } cout << " Defaulting to the fastest ADC channel, channel: " << fastestChannel << " (" << maxSpeed << ")." << endl; SetADChannel(fastestChannel); SetHSSpeed(0, 0); setIntegerParam(AndorAdcSpeed, fastestChannel);*/ } catch (const std::string &e) { cout << e << endl; return(asynError); } //Set some detector size parameters in param lib //so that they can read by user //(areaDetector is 0 based, so I will set 0->(size-1)) mXEnd = xsize; mYEnd = ysize; mXSize = xsize; mYSize = ysize; mXMaxSize = xsize - 1; mYMaxSize = ysize - 1; setIntegerParam(ADSizeX, mXEnd-1); setIntegerParam(ADSizeY, mYEnd-1); setIntegerParam(ADMaxSizeX, mXEnd-1); setIntegerParam(ADMaxSizeY, mYEnd-1); setIntegerParam(ADMinX, mXStart-1); setIntegerParam(ADMinY, mYStart-1); setIntegerParam(ADBinX, mXBinning); setIntegerParam(ADBinY, mYBinning); cout << "Setting read mode to be Image, and readout to be full image." << endl; try { checkStatus(SetReadMode(ARImage)); cout << "SetImage("<reason; int adstatus = 0; int FKheight = 0; int FKseries = 0; double FKtime = 0; int FKmode = 4; int FKhbin = 0; int FKvbin = 0; int FKoffset = 0; asynStatus status = asynSuccess; const char *functionName = "AndorCCD::writeInt32"; if (function == ADAcquire) { getIntegerParam(ADStatus, &adstatus); if (value && (adstatus == ADStatusIdle)) { try { mAcquiringData = 1; //We send an event at the bottom of this function. asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Acquiring data...\n", functionName); } catch (const std::string &e) { cout << e << endl; return(asynError); } } if (!value && (adstatus != ADStatusIdle)) { try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, AbortAcquisition()\n", functionName); checkStatus(AbortAcquisition()); mAcquiringData = 0; asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, FreeInternalMemory()\n", functionName); checkStatus(FreeInternalMemory()); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, CancelWait()\n", functionName); checkStatus(CancelWait()); } catch (const std::string &e) { cout << e << endl; return(asynError); } } } else if (function == ADTriggerMode) { try { if (value == 0) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetTriggerMode(%d)\n", functionName, ATInternal); checkStatus(SetTriggerMode(ATInternal)); mTriggerMode = ATInternal; } else if (value == 1) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetTriggerMode(%d)\n", functionName, ATExternal); checkStatus(SetTriggerMode(ATExternal)); mTriggerMode = ATExternal; } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADImageMode) { try { if (value == 0) { //Single image mode asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting single image mode.\n", functionName); checkStatus(SetAcquisitionMode(AASingle)); setIntegerParam(ADNumImages, 1); AAModeCurrent = AASingle; } else if (value == 1) { //Multiple image mode (by default do accumulate mode. Use AndorImageModeAMultiParam to change to other types.) asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting multiple image (accumulate) mode.\n", functionName); checkStatus(SetAcquisitionMode(AAAccumulate)); AAModeCurrent = AAAccumulate; } else if (value == 2) { //Continues mode //Set Run Till Abort mode here. setStringParam(AndorMessage, "Continues mode not supported."); callParamCallbacks(); return asynError; } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorImageModeAMultiParam) { try { //If we are not already in Multiple Image mode, do nothing here. if (AAModeCurrent != AASingle) { if (value == 0) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting multiple image (accumulate) mode.\n", functionName); checkStatus(SetAcquisitionMode(AAAccumulate)); AAModeCurrent = AAAccumulate; } else if (value == 1) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting multiple image (kinetics) mode.\n", functionName); checkStatus(SetAcquisitionMode(AAKinetics)); AAModeCurrent = AAKinetics; } else if (value == 2) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting multiple image (fast kinetics) mode.\n", functionName); checkStatus(SetAcquisitionMode(AAFastKinetics)); //Also send the SetFastKineticsEx command here. AAModeCurrent = AAFastKinetics; } } else { setStringParam(AndorMessage, "Not in multiple image mode. No action taken."); callParamCallbacks(); return asynError; } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADNumImages) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting number of images....\n", functionName); try { if (AAModeCurrent == AAAccumulate) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetNumberAccumulations(%d).\n", functionName, value); checkStatus(SetNumberAccumulations(value)); } else if (AAModeCurrent == AAKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetNumberKinetics(%d).\n", functionName, value); checkStatus(SetNumberKinetics(value)); } else if (AAModeCurrent == AAFastKinetics) { getIntegerParam(AndorFKHeightParam, &FKheight); getIntegerParam(AndorFKHBinningParam, &FKhbin); getIntegerParam(AndorFKVBinningParam, &FKvbin); getIntegerParam(AndorFKOffsetParam, &FKoffset); getDoubleParam(ADAcquireTime, &FKtime); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetFastKineticsEx(%d,%d,%d,%d,%d,%d,%d).\n", functionName, FKheight, value, FKtime, FKmode, FKhbin, FKvbin, FKoffset); checkStatus(SetFastKineticsEx(FKheight, value, FKtime, FKmode, FKhbin, FKvbin, FKoffset)); } else { //Force user to set this after defining the multiple acquisition mode setStringParam(AndorMessage, "Not in multiple image mode. No action taken."); callParamCallbacks(); return(asynError); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADNumExposures) { } else if (function == AndorCoolerParam) { try { if (value == 0) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, CoolerOFF().\n", functionName); checkStatus(CoolerOFF()); } else if (value == 1) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, CoolerON().\n", functionName); checkStatus(CoolerON()); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorShutdownParam) { shutdownCCD(); } else if (function == AndorStartupParam) { initializeCCD(INSTALL_PATH); } else if (function == AndorANumInKineticsParam) { try { if (AAModeCurrent == AAKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting number of accumulations in kinetics mode...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetNumberAccumulations(%d).\n", functionName, value); checkStatus(SetNumberAccumulations(value)); } else { setStringParam(AndorMessage, "Not in kinetics mode. No action taken."); callParamCallbacks(); return(asynError); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorFKHeightParam) { if (AAModeCurrent == AAFastKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting FK Height Param...\n", functionName); try { getIntegerParam(AndorFKHBinningParam, &FKhbin); getIntegerParam(ADNumImages, &FKseries); getIntegerParam(AndorFKVBinningParam, &FKvbin); getIntegerParam(AndorFKOffsetParam, &FKoffset); getDoubleParam(ADAcquireTime, &FKtime); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetFastKineticsEx(%d,%d,%d,%d,%d,%d,%d).\n", functionName, value, FKseries, FKtime, FKmode, FKhbin, FKvbin, FKoffset); checkStatus(SetFastKineticsEx(value, FKseries, FKtime, FKmode, FKhbin, FKvbin, FKoffset)); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { setStringParam(AndorMessage, "Not in fast kinetics mode. No action taken."); callParamCallbacks(); return(asynError); } } else if (function == AndorFKHBinningParam) { if (AAModeCurrent == AAFastKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting FK Horizontal binning param...\n", functionName); try { getIntegerParam(AndorFKHeightParam, &FKheight); getIntegerParam(ADNumImages, &FKseries); getIntegerParam(AndorFKVBinningParam, &FKvbin); getIntegerParam(AndorFKOffsetParam, &FKoffset); getDoubleParam(ADAcquireTime, &FKtime); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetFastKineticsEx(%d,%d,%d,%d,%d,%d,%d).\n", functionName, FKheight, FKseries, FKtime, FKmode, value, FKvbin, FKoffset); checkStatus(SetFastKineticsEx(FKheight, FKseries, FKtime, FKmode, value, FKvbin, FKoffset)); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { setStringParam(AndorMessage, "Not in fast kinetics mode. No action taken."); callParamCallbacks(); return(asynError); } } else if (function == AndorFKVBinningParam) { if (AAModeCurrent == AAFastKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting FK Vertical binning param...\n", functionName); try { getIntegerParam(AndorFKHeightParam, &FKheight); getIntegerParam(ADNumImages, &FKseries); getIntegerParam(AndorFKHBinningParam, &FKhbin); getIntegerParam(AndorFKOffsetParam, &FKoffset); getDoubleParam(ADAcquireTime, &FKtime); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetFastKineticsEx(%d,%d,%d,%d,%d,%d,%d).\n", functionName, FKheight, FKseries, FKtime, FKmode, FKhbin, value, FKoffset); checkStatus(SetFastKineticsEx(FKheight, FKseries, FKtime, FKmode, FKhbin, value, FKoffset)); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { setStringParam(AndorMessage, "Not in fast kinetics mode. No action taken."); callParamCallbacks(); return(asynError); } } else if (function == AndorFKOffsetParam) { if (AAModeCurrent == AAFastKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting FK offset param...\n", functionName); try { getIntegerParam(AndorFKHeightParam, &FKheight); getIntegerParam(ADNumImages, &FKseries); getIntegerParam(AndorFKVBinningParam, &FKvbin); getIntegerParam(AndorFKHBinningParam, &FKhbin); getDoubleParam(ADAcquireTime, &FKtime); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetFastKineticsEx(%d,%d,%d,%d,%d,%d,%d).\n", functionName, FKheight, FKseries, FKtime, FKmode, FKhbin, FKvbin, value); checkStatus(SetFastKineticsEx(FKheight, FKseries, FKtime, FKmode, FKhbin, FKvbin, value)); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { setStringParam(AndorMessage, "Not in fast kinetics mode. No action taken."); callParamCallbacks(); return(asynError); } } else if (function == ADBinX) { try { //If AreaDetector tries to set 0 for binning, assume it means 1. if (value==0) { value++; } asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting BinX...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetImage(%d,%d,%d,%d,%d,%d).\n", functionName, value, mYBinning, mXStart, mXEnd, mYStart, mYEnd); checkStatus(SetImage(static_cast(value), mYBinning, mXStart, mXEnd, mYStart, mYEnd)); mXBinning = static_cast(value); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADBinY) { try { //If AreaDetector tries to set 0 for binning, assume it means 1. if (value==0) { value++; } asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting BinY...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetImage(%d,%d,%d,%d,%d,%d).\n", functionName, mXBinning, value, mXStart, mXEnd, mYStart, mYEnd); checkStatus(SetImage(mXBinning, static_cast(value), mXStart, mXEnd, mYStart, mYEnd)); mYBinning = static_cast(value); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADMinX) { try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting MinX...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetImage(%d,%d,%d,%d,%d,%d).\n", functionName, mXBinning, mYBinning, static_cast(value)+1, mXEnd+(static_cast(value)-mXStart)+1, mYStart, mYEnd); checkStatus(SetImage(mXBinning, mYBinning, static_cast(value)+1, mXEnd+(static_cast(value)-mXStart)+1, mYStart, mYEnd)); mXEnd = mXEnd+(static_cast(value)-mXStart)+1; mXStart = static_cast(value)+1; } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADMinY) { try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting MinY...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetImage(%d,%d,%d,%d,%d,%d).\n", functionName, mXBinning, mYBinning, mXStart, mXEnd, static_cast(value)+1, mYEnd+(static_cast(value)-mYStart)+1); checkStatus(SetImage(mXBinning, mYBinning, mXStart, mXEnd, static_cast(value)+1, mYEnd+(static_cast(value)-mYStart)+1)); mYEnd = mYEnd + (static_cast(value)-mYStart)+1; mYStart = static_cast(value)+1; } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADSizeX) { try { //If AreaDetector tries to set 0 for binning, assume it means 1. if (value==0) { value++; } asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting SizeX...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetImage(%d,%d,%d,%d,%d,%d).\n", functionName, mXBinning, mYBinning, mXStart, mXStart+static_cast(value), mYStart, mYEnd); checkStatus(SetImage(mXBinning, mYBinning, mXStart, mXStart+static_cast(value), mYStart, mYEnd)); mXEnd = mXStart + static_cast(value); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADSizeY) { try { //If AreaDetector tries to set 0 for binning, assume it means 1. if (value==0) { value++; } asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting SizeY...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetImage(%d,%d,%d,%d,%d,%d).\n", functionName, mXBinning, mYBinning, mXStart, mXEnd, mYStart, mYStart+static_cast(value)); checkStatus(SetImage(mXBinning, mYBinning, mXStart, mXEnd, mYStart, mYStart+static_cast(value))); mYEnd = mYStart + static_cast(value); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADShutterControl) { try { if (value == 0) { //Close shutter asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, AShutterClose, mShutterCloseTime, mShutterOpenTime); checkStatus(SetShutter(mShutterExTTL, AShutterClose, mShutterCloseTime, mShutterOpenTime)); mShutterMode = AShutterClose; } else { //Open shutter (check current value of AndorShutterMode) int aShutter = 999; getIntegerParam(AndorShutterMode, &aShutter); if (aShutter == AShutterAuto) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, AShutterAuto, mShutterCloseTime, mShutterOpenTime); checkStatus(SetShutter(mShutterExTTL, AShutterAuto, mShutterCloseTime, mShutterOpenTime)); mShutterMode = AShutterAuto; } else if (aShutter == AShutterOpen) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, AShutterOpen, mShutterCloseTime, mShutterOpenTime); checkStatus(SetShutter(mShutterExTTL, AShutterOpen, mShutterCloseTime, mShutterOpenTime)); mShutterMode = AShutterOpen; } } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorShutterMode) { try { checkStatus(SetShutter(mShutterExTTL, static_cast(value), mShutterCloseTime, mShutterOpenTime)); mShutterMode = static_cast(value); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, mShutterMode, mShutterCloseTime, mShutterOpenTime); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorShutterExTTL) { try { checkStatus(SetShutter(static_cast(value), mShutterMode, mShutterCloseTime, mShutterOpenTime)); mShutterExTTL = static_cast(value); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, mShutterMode, mShutterCloseTime, mShutterOpenTime); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorAdcSpeed) { try { checkStatus(SetADChannel(value)); //Set fastest HS speed. checkStatus(SetHSSpeed(0, 0)); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetADCChannel(%d).\n", functionName, value); setIntegerParam(AndorAdcSpeed, value); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { status = ADDriver::writeInt32(pasynUser, value); } //Set in param lib so the user sees a readback straight away. We might overwrite this in the //status task, depending on the parameter. status = setIntegerParam(function, value); //For a successful write, clear the error message. if (mRunning==1) { setStringParam(AndorMessage, " "); } /* Do callbacks so higher layers see any changes */ callParamCallbacks(); /* Send a signal to the poller task which will make it do a poll, and switch to the fast poll rate */ epicsEventSignal(statusEvent); if (mAcquiringData) { //Also signal the data readout thread epicsEventSignal(dataEvent); } if (status) asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s:%s: error, status=%d function=%d, value=%d\n", driverName, functionName, status, function, value); else asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, value=%d\n", driverName, functionName, function, value); return status; } asynStatus AndorCCD::readInt32(asynUser *pasynUser, epicsInt32 *value) { int function = pasynUser->reason; asynStatus status = asynSuccess; const char *functionName = "AndorCCD::readInt32"; int temp = 0; //Changing any of the following parameters requires recomputing the base image if (function == AndorCoolerParam) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Reading cooler status...\n", functionName); try { checkStatus(IsCoolerOn(&temp)); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { status = ADDriver::readInt32(pasynUser, value); } *value = static_cast(temp); status = setIntegerParam(function, *value); callParamCallbacks(); if (status) asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d function=%d, value=%f\n", driverName, functionName, status, function, value); else asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, value=%f\n", driverName, functionName, function, value); return status; } asynStatus AndorCCD::writeFloat64(asynUser *pasynUser, epicsFloat64 value) { int function = pasynUser->reason; asynStatus status = asynSuccess; const char *functionName = "AndorCCD::writeFloat64"; int minTemp = 0; int maxTemp = 0; int FKheight = 0; int FKseries = 0; int FKmode = 4; int FKhbin = 0; int FKvbin = 0; int FKoffset = 0; /* Changing any of the following parameters requires recomputing the base image */ if (function == ADGain) { } else if (function == ADAcquireTime) { try { if (AAModeCurrent == AAFastKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting exposure time for fast kinetics.\n", functionName); getIntegerParam(AndorFKHeightParam, &FKheight); getIntegerParam(AndorFKHBinningParam, &FKhbin); getIntegerParam(ADNumImages, &FKseries); getIntegerParam(AndorFKVBinningParam, &FKvbin); getIntegerParam(AndorFKOffsetParam, &FKoffset); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetFastKineticsEx(%d,%d,%f,%d,%d,%d,%d).\n", functionName, FKheight, FKseries, value, FKmode, FKhbin, FKvbin, FKoffset); checkStatus(SetFastKineticsEx(FKheight, FKseries, value, FKmode, FKhbin, FKvbin, FKoffset)); } else { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetExposureTime(%f).\n", functionName, value); checkStatus(SetExposureTime(value)); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADAcquirePeriod) { try { if (AAModeCurrent == AAAccumulate) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetAccumulationCycleTime(%f).\n", functionName, value); checkStatus(SetAccumulationCycleTime(value)); } else if (AAModeCurrent == AAKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetKineticCycleTime(%f).\n", functionName, value); checkStatus(SetKineticCycleTime(value)); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADTemperature) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting temperature value %f.\n", functionName, value); try { checkStatus(GetTemperatureRange(&minTemp, &maxTemp)); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, CCD Min Temp: %d, Max Temp %d.\n", functionName, minTemp, maxTemp); if ((static_cast(value) > minTemp) & (static_cast(value) < maxTemp)) { checkStatus(SetTemperature(static_cast(value))); } else { setStringParam(AndorMessage, "Temperature is out of range."); callParamCallbacks(); return(asynError); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == AndorACTInKineticsParam) { try { if (AAModeCurrent == AAKinetics) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting accumulated cycle time in kinetics mode...\n", functionName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetAccumulationCycleTime(%f).\n", functionName, value); checkStatus(SetAccumulationCycleTime(value)); } else { setStringParam(AndorMessage, "Not in kinetics mode. No action taken."); callParamCallbacks(); return(asynError); } } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADShutterOpenDelay) { try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting ADShutterOpenDelay to %f.\n", functionName, value); checkStatus(SetShutter(mShutterExTTL, mShutterMode, mShutterCloseTime, static_cast(value))); mShutterOpenTime = static_cast(value); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, mShutterMode, mShutterCloseTime, mShutterOpenTime); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADShutterCloseDelay) { try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Setting ADShutterCloseDelay to %f.\n", functionName, value); checkStatus(SetShutter(mShutterExTTL, mShutterMode, static_cast(value), mShutterOpenTime)); mShutterCloseTime = static_cast(value); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, SetShutter(%d,%d,%d,%d).\n", functionName, mShutterExTTL, mShutterMode, mShutterCloseTime, mShutterOpenTime); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { status = ADDriver::writeFloat64(pasynUser, value); } /* Set the parameter and readback in the parameter library. This may be overwritten when we read back the * status at the end, but that's OK */ status = setDoubleParam(function, value); //For a successful write, clear the error message. setStringParam(AndorMessage, " "); /* Do callbacks so higher layers see any changes */ callParamCallbacks(); if (status) asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d function=%d, value=%f\n", driverName, functionName, status, function, value); else asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, value=%f\n", driverName, functionName, function, value); return status; } asynStatus AndorCCD::readFloat64(asynUser *pasynUser, epicsFloat64 *value) { int function = pasynUser->reason; asynStatus status = asynSuccess; const char *functionName = "AndorCCD::readFloat64"; int temp = 0; float exposure = 0.0; float accumulate = 0.0; float kinetic = 0.0; //Changing any of the following parameters requires recomputing the base image if (function == ADGain) { } else if (function == ADAcquireTime) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Reading exposure timimgs...\n", functionName); try { checkStatus(GetAcquisitionTimings(&exposure, &accumulate, &kinetic)); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, exposure is %f.\n", functionName, exposure); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, accumulate is %f.\n", functionName, accumulate); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, kinetic is %f.\n", functionName, kinetic); *value = exposure; } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADAcquirePeriod) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Reading exposure timimgs...\n", functionName); try { checkStatus(GetAcquisitionTimings(&exposure, &accumulate, &kinetic)); if (AAModeCurrent == AAAccumulate) { *value = accumulate; } else if (AAModeCurrent == AAKinetics) { *value = kinetic; } asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, exposure is %f.\n", functionName, exposure); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, accumulate is %f.\n", functionName, accumulate); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, kinetic is %f.\n", functionName, kinetic); } catch (const std::string &e) { cout << e << endl; return(asynError); } } else if (function == ADTemperature) { try { checkStatus(GetTemperature(&temp)); *value = (epicsFloat64)temp; } catch (const std::string &e) { cout << e << endl; return(asynError); } } else { status = ADDriver::readFloat64(pasynUser, value); } status = setDoubleParam(function, *value); callParamCallbacks(); /* Send a signal to the poller task which will make it do a poll, and switch to the fast poll rate */ epicsEventSignal(statusEvent); if (status) asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s:%s error, status=%d function=%d, value=%f\n", driverName, functionName, status, function, value); else asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, value=%f\n", driverName, functionName, function, value); return status; } /** * Function to check the return status of Andor SDK library functions. * @param returnStatus The return status of the SDK function * @return 0=success. Does not return in case of failure. * @throw std::string An exception is thrown in case of failure. */ unsigned int AndorCCD::checkStatus(unsigned int returnStatus) throw(std::string) { if (returnStatus == DRV_SUCCESS) { return 0; } else if (returnStatus == DRV_NOT_INITIALIZED) { throw std::string("ERROR: Driver is not initialized."); } else if (returnStatus == DRV_ACQUIRING) { throw std::string("ERROR: Not allowed. Currently acquiring data."); } else if (returnStatus == DRV_P1INVALID) { throw std::string("ERROR: Parameter 1 not valid."); } else if (returnStatus == DRV_P2INVALID) { throw std::string("ERROR: Parameter 2 not valid."); } else if (returnStatus == DRV_P3INVALID) { throw std::string("ERROR: Parameter 3 not valid."); } else if (returnStatus == DRV_P4INVALID) { throw std::string("ERROR: Parameter 4 not valid."); } else if (returnStatus == DRV_P5INVALID) { throw std::string("ERROR: Parameter 5 not valid."); } else if (returnStatus == DRV_P6INVALID) { throw std::string("ERROR: Parameter 6 not valid."); } else if (returnStatus == DRV_P7INVALID) { throw std::string("ERROR: Parameter 7 not valid."); } else if (returnStatus == DRV_ERROR_ACK) { throw std::string("ERROR: Unable to communicate with card."); } else if (returnStatus == DRV_TEMP_OFF) { setStringParam(AndorTempStatusMessage, "Cooler is OFF"); return 0; } else if (returnStatus == DRV_TEMP_STABILIZED) { setStringParam(AndorTempStatusMessage, "Temperature has stabilized at set point."); return 0; } else if (returnStatus == DRV_TEMP_NOT_REACHED) { setStringParam(AndorTempStatusMessage, "Temperature has not reached setpoint."); return 0; } else if (returnStatus == DRV_TEMP_DRIFT) { setStringParam(AndorTempStatusMessage, "Temperature has stabilized but has since drifted."); return 0; } else if (returnStatus == DRV_TEMP_NOT_STABILIZED) { setStringParam(AndorTempStatusMessage, "Temperature has not stabilized."); return 0; } else if (returnStatus == DRV_VXDNOTINSTALLED) { throw std::string("ERROR: VxD not loaded."); } else if (returnStatus == DRV_INIERROR) { throw std::string("ERROR: Unable to load DETECTOR.INI."); } else if (returnStatus == DRV_COFERROR) { throw std::string("ERROR: Unable to load *.COF."); } else if (returnStatus == DRV_FLEXERROR) { throw std::string("ERROR: Unable to load *.RBF."); } else if (returnStatus == DRV_ERROR_FILELOAD) { throw std::string("ERROR: Unable to load *.COF or *.RBF files."); } else if (returnStatus == DRV_ERROR_PAGELOCK) { throw std::string("ERROR: Unable to acquire lock on requested memory."); } else if (returnStatus == DRV_USBERROR) { throw std::string("ERROR: Unable to detect USB device or not USB 2.0."); } else if (returnStatus == DRV_ERROR_NOCAMERA) { throw std::string("ERROR: No camera found."); } else if (returnStatus == DRV_GENERAL_ERRORS) { throw std::string("ERROR: An error occured while obtaining the number of available cameras."); } else if (returnStatus == DRV_INVALID_MODE) { throw std::string("ERROR: Invalid mode or mode not available."); } else if (returnStatus == DRV_ACQUISITION_ERRORS) { throw std::string("ERROR: Acquisition mode are invalid."); } else if (returnStatus == DRV_ERROR_PAGELOCK) { throw std::string("ERROR: Unable to allocate memory."); } else if (returnStatus == DRV_INVALID_FILTER) { throw std::string("ERROR: Filter not available for current acquisition."); } else if (returnStatus == DRV_IDLE) { throw std::string("ERROR: The system is not currently acquiring."); } else if (returnStatus == DRV_NO_NEW_DATA) { throw std::string("ERROR: No data to read, or CancelWait() called."); } else if (returnStatus == DRV_ERROR_CODES) { throw std::string("ERROR: Problem communicating with camera."); } else { throw std::string("ERROR: Unknown error code returned from Andor SDK."); } return 0; } /** * Update status of detector. Meant to be run in own thread. */ void AndorCCD::statusTask(void) { int value = 0; unsigned int uvalue = 0; unsigned int status = 0; float exposure = 0.0; float accumulate = 0.0; float kinetic = 0.0; float timeout = 0.0; unsigned int forcedFastPolls = 0; //const char *functionName = "AndorCCD::statusTask"; cout << "Status thread started..." << endl; while(1) { //Read timeout for polling freq. this->lock(); if (forcedFastPolls > 0) { timeout = mFastPollingPeriod; forcedFastPolls--; } else { timeout = mPollingPeriod; } this->unlock(); if (timeout != 0.0) { status = epicsEventWaitWithTimeout(statusEvent, timeout); } else { status = epicsEventWait(statusEvent); } if (status == epicsEventWaitOK) { //cout << "Got status event" << endl; //We got an event, rather than a timeout. This is because other software //knows that data has arrived, or device should have changed state (parameters changed, etc.). //Force a minimum number of fast polls, because the device status //might not have changed in the first few polls forcedFastPolls = 5; } this->lock(); if (mRunning) { //cout << " Status poll." << endl; //Only read these if we are not acquiring data if (!mAcquiringData) { //Read cooler status try { checkStatus(IsCoolerOn(&value)); status = setIntegerParam(AndorCoolerParam, value); } catch (const std::string &e) { cout << e << endl; setStringParam(AndorMessage, e.c_str()); } //Read temperature of CCD try { checkStatus(GetTemperature(&value)); status = setDoubleParam(ADTemperature, static_cast(value)); } catch (const std::string &e) { cout << e << endl; setStringParam(AndorMessage, e.c_str()); } //Read acquisition timings, and set ADAcquireTime and ADAcquirePeriod (depending on mode). try { if (AAModeCurrent == AAFastKinetics) { checkStatus(GetFKExposureTime(&exposure)); status |= setDoubleParam(ADAcquireTime, exposure); } else { checkStatus(GetAcquisitionTimings(&exposure, &accumulate, &kinetic)); if (AAModeCurrent == AASingle) { status |= setDoubleParam(ADAcquireTime, exposure); } else if (AAModeCurrent == AAAccumulate) { status |= setDoubleParam(ADAcquirePeriod, accumulate); } else if (AAModeCurrent == AAKinetics) { status |= setDoubleParam(ADAcquirePeriod, kinetic); } } } catch (const std::string &e) { cout << e << endl; setStringParam(AndorMessage, e.c_str()); } } //Read detector status (idle, acquiring, error, etc.) try { checkStatus(GetStatus(&value)); uvalue = static_cast(value); if (uvalue == ASIdle) { setIntegerParam(ADStatus, ADStatusIdle); setStringParam(ADStatusMessage, "IDLE. Waiting on instructions."); } else if (uvalue == ASTempCycle) { setIntegerParam(ADStatus, ADStatusWaiting); setStringParam(ADStatusMessage, "Executing temperature cycle."); } else if (uvalue == ASAcquiring) { setIntegerParam(ADStatus, ADStatusAcquire); setStringParam(ADStatusMessage, "Data acquisition in progress."); } else if (uvalue == ASAccumTimeNotMet) { setIntegerParam(ADStatus, ADStatusError); setStringParam(ADStatusMessage, "Unable to meet accumulate time."); } else if (uvalue == ASKineticTimeNotMet) { setIntegerParam(ADStatus, ADStatusError); setStringParam(ADStatusMessage, "Unable to meet kinetic cycle time."); } else if (uvalue == ASErrorAck) { setIntegerParam(ADStatus, ADStatusError); setStringParam(ADStatusMessage, "Unable to communicate with device."); } else if (uvalue == ASAcqBuffer) { setIntegerParam(ADStatus, ADStatusError); setStringParam(ADStatusMessage, "Computer unable to read data from device at required rate."); } else if (uvalue == ASSpoolError) { setIntegerParam(ADStatus, ADStatusError); setStringParam(ADStatusMessage, "Overflow of the spool buffer."); } } catch (const std::string &e) { cout << e << endl; setStringParam(AndorMessage, e.c_str()); } /* Call the callbacks to update any changes */ callParamCallbacks(); } this->unlock(); } //End of loop } /** * Do data readout from the detector. Meant to be run in own thread. */ void AndorCCD::dataTask(void) { epicsUInt32 status = 0; char *errorString = NULL; unsigned char dataValid = 0; // char filePath[256] = {0}; char palFilePath[256] = {0}; // char fileName[256] = {0}; char fullFileName[256] = {0}; //float fparam = 0.0; epicsInt32 numImages = 0; epicsUInt32 currentAcqMode = 0; //long *dP = NULL; const char *functionName = "AndorCCD::dataTask"; cout << "Data thread started..." << endl; while(1) { errorString = NULL; //Wait for event from main thread to signal that data acquisition has started. status = epicsEventWait(dataEvent); //if (status == epicsEventWaitOK) { // cout << "Got data event" << endl; //} this->lock(); if (mRunning) { //Sanity check that main thread thinks we are acquiring data if (mAcquiringData) { dataValid = 1; //Read current acqusition setting currentAcqMode = AAModeCurrent; //Read the number of images set getIntegerParam(ADNumImages, &numImages); } else { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Data thread is running but main thread thinks we are not acquiring.\n", functionName); } } this->unlock(); if (dataValid) { //Trigger and wait for data from SDK try { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, StartAcquisition().\n", functionName); checkStatus(StartAcquisition()); if (currentAcqMode == AASingle) { //Single image mode. Wait for acquisiton then save data. asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, WaitForAcquisition().\n", functionName); checkStatus(WaitForAcquisition()); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, WaitForAcquisition has returned.\n", functionName); //Save data this->saveDataFrame(fullFileName, palFilePath); } else if (currentAcqMode == AAAccumulate) { //Wait for all images and then readout the single accumulated image from the SDK for (int i=0; ipasynUserSelf, ASYN_TRACE_FLOW, "%s, accumulate image %d.\n", functionName, i); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, WaitForAcquisition().\n", functionName); checkStatus(WaitForAcquisition()); } //Save data this->saveDataFrame(fullFileName, palFilePath); } else if (currentAcqMode == AAKinetics) { //Wait for each image acquisition and then read out one by one as they acquired. Save each one. for (int i=0; ipasynUserSelf, ASYN_TRACE_FLOW, "%s, kinetic image %d.\n", functionName, i); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, WaitForAcquisition().\n", functionName); checkStatus(WaitForAcquisition()); //Save data this->saveDataFrame(fullFileName, palFilePath); } } else { this->lock(); setStringParam(AndorMessage, "Error in data thread. Acquisition mode not recognised."); callParamCallbacks(); this->unlock(); } } catch (const std::string &e) { cout << e << endl; errorString = const_cast(e.c_str()); } ///////////////////////////////////// this->lock(); //Now clear main thread flag mAcquiringData = 0; setIntegerParam(ADAcquire, 0); //setIntegerParam(ADStatus, 0); //Dont set this as the status thread sets it. /* Call the callbacks to update any changes */ callParamCallbacks(); this->unlock(); } } //End of loop } /** * Save a data frame using the Andor SDK file writing functions. * Also has the option to save data as plain text. */ void AndorCCD::saveDataFrame(char *fullFileName, char *palFilePath) { at_32 *dP = NULL; char *errorString = NULL; int fileFormat; const char *functionName = "AndorCCD::saveDataFrame"; //Update data this->lock(); // Fetch the file format getIntegerParam(NDFileFormat, &fileFormat); //////////////////////////////////// //Put data into waveforms, or save to file //Check we haven't cancelled data acquisition before trying to save file if (mAcquiringData) { if (fileFormat == AFFTIFF) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data in TIFF format.\n", functionName); } else if (fileFormat == AFFBMP) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data in BMP format.\n", functionName); } else if (fileFormat == AFFSIF) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data in SIF format.\n", functionName); } else if (fileFormat == AFFEDF) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data in EDF format.\n", functionName); } else if (fileFormat == AFFRAW) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data in RAW format.\n", functionName); } else if (fileFormat == AFFTEXT) { asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data in TEXT format.\n", functionName); } this->createFileName(255, fullFileName); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, file name is %s.\n", functionName, fullFileName); getStringParam(AndorPalFileName, 255, palFilePath); asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data...\n", functionName); try { if (fileFormat == AFFTIFF) { //checkStatus(SaveAsTiff(fullFileName, palFilePath, 1, 1)); //Didn't work checkStatus(SaveAsTiffEx(fullFileName, palFilePath, 1, 1, 1)); } else if (fileFormat == AFFBMP) { checkStatus(SaveAsBmp(fullFileName, palFilePath, 0, 0)); } else if (fileFormat == AFFSIF) { checkStatus(SaveAsSif(fullFileName)); } else if (fileFormat == AFFEDF) { checkStatus(SaveAsEDF(fullFileName, 0)); } else if (fileFormat == AFFRAW) { checkStatus(SaveAsRaw(fullFileName, 1)); } else if (fileFormat == AFFTEXT) { //Get data into buffer and dump to file checkStatus(GetMostRecentImage(mData, mDataSize)); ofstream datafile; datafile.open(fullFileName); if (datafile.is_open()) { datafile << fullFileName << endl; dP = mData; for (int d=0; d(e.c_str()); } asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s, Saving data.. Done!\n", functionName); } if (errorString != NULL) { setStringParam(AndorMessage, errorString); setIntegerParam(ADStatus, ADStatusError); } this->unlock(); } //C utility functions to tie in with EPICS static void andorStatusTaskC(void *drvPvt) { AndorCCD *pPvt = (AndorCCD *)drvPvt; pPvt->statusTask(); } static void andorDataTaskC(void *drvPvt) { AndorCCD *pPvt = (AndorCCD *)drvPvt; pPvt->dataTask(); } /** * Config function for IOC shell. * * @param portName The ASYN port. * @param maxBuffers The maximum number of data frame buffers for the ADDriver class. * @param maxMemory The maximum memory size allowed in the ADDriver class. * @param maxSizeX The maximum X dimension of the detector. * @param maxSizeY The maximum Y dimension of the detector. */ extern "C" { int andorCCDConfig(const char *portName, int maxBuffers, size_t maxMemory, int maxSizeX, int maxSizeY) { /*Instantiate class.*/ new AndorCCD(portName, maxBuffers, maxMemory, maxSizeX, maxSizeY); return(asynSuccess); } /* Code for iocsh registration */ /* andorCCDConfig */ static const iocshArg andorCCDConfigArg0 = {"Port name", iocshArgString}; static const iocshArg andorCCDConfigArg1 = {"maxBuffers", iocshArgInt}; static const iocshArg andorCCDConfigArg2 = {"maxMemory", iocshArgInt}; static const iocshArg andorCCDConfigArg3 = {"maxSizeX", iocshArgInt}; static const iocshArg andorCCDConfigArg4 = {"maxSizeY", iocshArgInt}; static const iocshArg * const andorCCDConfigArgs[] = {&andorCCDConfigArg0, &andorCCDConfigArg1, &andorCCDConfigArg2, &andorCCDConfigArg3, &andorCCDConfigArg4}; static const iocshFuncDef configAndorCCD = {"andorCCDConfig", 5, andorCCDConfigArgs}; static void configAndorCCDCallFunc(const iocshArgBuf *args) { andorCCDConfig(args[0].sval, args[1].ival, args[2].ival, args[3].ival, args[4].ival); } static void andorCCDRegister(void) { iocshRegister(&configAndorCCD, configAndorCCDCallFunc); } epicsExportRegistrar(andorCCDRegister); }