EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  <20222023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  <20222023  2024 
<== Date ==> <== Thread ==>

Subject: AreaDetector Teledyne driver question
From: David Vine via Tech-talk <tech-talk at aps.anl.gov>
To: tech-talk at aps.anl.gov
Date: Wed, 22 Jun 2022 17:04:22 -0700
Hi all,

I wrote a driver for the Teledyne Shadobox 6k flat panel detector.

The API has a function called GevWaitForFrame(timeout) which must be being executed by the driver when a frame arrives from the camera or the frame will be missed. Are there any examples of a driver that has a similar API?

The problem is that it creates a race condition so if I execute the code sequentially:

send trigger
GevWaitForFrame(1 second)

It's possible for the frame to arrive before I'm "waiting" for it. Right now I have two threads that receive epicsEventSignal - one to send the trigger and one to wait.

That works ok but since I can't abort the GevWaitForFrame the driver gets out of sync for longer exposures. Eg I have a 5 second exposure so I set a 7 second timeout but then abort after 1 second my wait thread is stuck for 6 seconds.

I haven't had a camera API work like this before so any examples would be really helpful.

Thanks,
David






This email and any files transmitted with it are confidential and intended solely for the use of the individual or entity to whom they are addressed. If you have received this email in error, please notify the system manager. This message contains confidential information and is intended only for the individual named. If you are not the named addressee, you should not disseminate, distribute or copy this email. Please notify the sender immediately by email if you have received this email by mistake and delete this email from your system. If you are not the intended recipient, you are notified that disclosing, copying, distributing or taking any action in reliance on the contents of this information is strictly prohibited
/* shaobox.cpp
 *
 * This is a driver for the Teledyn Shadobox camera.
 *
 * Author: David Vine
 * 		   Sigray
 *
 * Created:  Ausgust 6 2021
 *
 */

#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <iostream>
#include <chrono>
#include <thread>

#include <epicsTime.h>
#include <epicsThread.h>
#include <epicsEvent.h>
#include <epicsMutex.h>
#include <epicsString.h>
#include <epicsStdio.h>
#include <epicsMutex.h>
#include <epicsExit.h>
#include <cantProceed.h>
#include <iocsh.h>

#include "ADDriver.h"
#include <epicsExport.h>
#include "shadobox.h"

static const char *driverName = "shadobox";

void shadobox::imageGrabTask()
{
		static const char* functionName = "grabImage";
		size_t dims[2];
		uint16_t byteDepth;
		NDDataType_t dataType;
		NDColorMode_t colorMode;
		uint32_t numBytes;
		dataType = NDUInt16;
		colorMode = NDColorModeMono;
		int acquire;
		double acquireTime = 1.0;
		bool verbose  = true;

		while(!exiting_){

				GEV_BUFFER_OBJECT *img = NULL;
				GEV_STATUS status = 0;

				getIntegerParam(ADAcquire, &acquire);
				if (!acquire){
						if (verbose) printf("GrabTask: Waiting for start event\n");
						status = epicsEventWait(startGrabTaskEventId_);
						if (verbose) printf("GrabTask: Got start event\n");
						acquire = 1;
						getDoubleParam(ADAcquireTime, &acquireTime);
				}
				int waitTimeOut = (int)(2000*acquireTime+132);
				if (waitTimeOut<500) waitTimeOut = 500;
				if (verbose) printf("Entering GevWaitForNextFrame with timeout: %d\n", waitTimeOut);
				status = GevWaitForNextImage(telCamHandle_, &img, waitTimeOut);
				if ((img != NULL) && (status == GEVLIB_OK) ){
						if (img->status==0){
								lock();
								getIntegerParam(ADAcquire, &acquire);
								// If we're not acquiring don't do anything
								if (acquire==0) {
										printf("Got image but not acquiring\n");
										unlock();
										continue;
								}

								dims[0] = img->w;
								dims[1] = img->h;
								byteDepth = img->d;
								numBytes = dims[0]*dims[1]*byteDepth;
								setIntegerParam(ADStatus, ADStatusReadout);

								setIntegerParam(NDArraySizeX, dims[0]);
								setIntegerParam(NDArraySizeY, dims[1]);
								setIntegerParam(NDArraySize, numBytes);
								setIntegerParam(NDDataType, dataType);
								setIntegerParam(NDColorMode, colorMode);
								callParamCallbacks();
								//printf("Allocating pRaw_\n");
								pRaw_ = pNDArrayPool->alloc(2, dims, dataType, 0, NULL);
								if (!pRaw_){
										setIntegerParam(ADStatus, ADStatusAborting);
										callParamCallbacks();
										asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
														"%s:%s: Unable to allocate buffer\n",
														driverName, functionName);
										setIntegerParam(ADAcquire, 0);
								} else {
										memcpy(pRaw_->pData, img->address, numBytes);
								}

								setIntegerParam(NDArrayCounter, img->id);
								if (pRaw_){
										pRaw_->uniqueId = img->id;
										updateTimeStamp(&pRaw_->epicsTS);
										pRaw_->timeStamp = pRaw_->epicsTS.secPastEpoch+pRaw_->epicsTS.nsec/1e9;
										getAttributes(pRaw_->pAttributeList);
										pRaw_->pAttributeList->add("ColorMode", "Color Mode", NDAttrInt32, &colorMode);
								}

								callParamCallbacks();
								unlock();
								/* Signal main thread image has been received */
								epicsEventSignal(imageReceivedId_);
						}
				}
				if (img != NULL){
						//printf("Releasing buffer\n");
						GevReleaseImage(telCamHandle_, img);
				}
		}
}

void shadobox::shutdown(void){
		exiting_=1;
		disconnectCamera();
}

void shadobox::reconnectCamera(void){
		disconnectCamera();
		connectCamera();
}

asynStatus shadobox::disconnectCamera(void){
		int acquiring;
		asynStatus status;
		GEV_STATUS telStatus;

		// Check if acuiring
		status = getIntegerParam(ADAcquire, &acquiring);

		// If necessary stop acquisitiom
		if (status==asynSuccess && acquiring){
				telStatus = GevStopTransfer(telCamHandle_);
		}
		for (int i=0; i<numBuffers; i++){
			free(bufAddress[i]);
		}
		if (telCamHandle_){
				printf("Disconnecting camera... ");
				GevAbortTransfer(telCamHandle_);
				telStatus = GevFreeTransfer(telCamHandle_);
				GevCloseCamera(&telCamHandle_);
				GevApiUninitialize();
				_CloseSocketAPI();
				printf("done\n");
		}
		return asynSuccess;
}

static void c_shutdown(void *arg)
{
		shadobox *s = (shadobox *)arg;
		s->shutdown();
}

static void imageGrabTaskC(void *drvPvt)
{
    shadobox *pPvt = (shadobox *)drvPvt;

    pPvt->imageGrabTask();
}

static void tempReadTaskC(void *drvPvt)
{
    shadobox *pPvt = (shadobox *)drvPvt;

    pPvt->tempReadTask();
}

static void mainDataTaskC(void *drvPvt)
{
    shadobox *pPvt = (shadobox *)drvPvt;

    pPvt->mainDataTask();
}

void shadobox::tempReadTask(){
		int type;
		double temp;
		 while(!exiting_){
				GevGetFeatureValue(telCamHandle_, "DeviceTemperature", &type, sizeof(temp), &temp);
				lock();
				setDoubleParam(ADTemperature, temp);
				setDoubleParam(ADTemperatureActual, temp);
				callParamCallbacks();
				unlock();
				sleep(1); // seconds
		 }
}

/** This thread interacts with the camera API to start/stop acquiring.
  * It implements the logic for single, multiple or continuous acquisition. */
void shadobox::mainDataTask()
{
    int status = asynSuccess;
    int imageCounter;
    int numImages, numImagesCounter;
    int imageMode, triggerMode;
    int arrayCallbacks;
    int acquire=0;
    double acquireTime;
    epicsTimeStamp startTime;
    const char *functionName = "imageGrabTask";
	GEV_STATUS telStatus;
	epicsEventStatus eventStatus;
	int missedFrames = 0;
	int shadSynch = 0;
	int disconnectCycles = 0;
	bool verbose = true;

    this->lock();
    /* Loop forever */
    while (1) {
        /* 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();
			if (verbose) printf("Waiting for event\n");
            status = epicsEventWait(startEventId_);
			if (verbose) printf("Got start event\n");
            this->lock();
			missedFrames = 0;
			disconnectCycles = 0;
			getIntegerParam(ADNumImages, &numImages);
			getIntegerParam(ADImageMode, &imageMode);
			getIntegerParam(ADTriggerMode, &triggerMode);
			if (imageMode == 0){ // Single
					numImages = 1;
			} else if (imageMode==1){ // Multiple
					getIntegerParam(ADNumImages, &numImages);
			} else if (imageMode == 2){ // Continuous
					numImages = -1;
			}
			GevStartTransfer(telCamHandle_, numImages);
            acquire = 1;
            setIntegerParam(ADNumImagesCounter, 0);
        }

        /* We are acquiring. */
        /* Get the current time */
        epicsTimeGetCurrent(&startTime);
        setIntegerParam(ADStatus, ADStatusAcquire);
		getDoubleParam(ADAcquireTime, &acquireTime);

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

		/* If internal triggering send the trigger */
        setStringParam(ADStatusMessage, "Acquiring");
		if (triggerMode!=0){
            setStringParam(ADStatusMessage, "Wait for trigger");
		}
		getIntegerParam(ShadTrigMode, &shadSynch);
		if (shadSynch==0){ // Snapshot
			if (verbose) printf("Sending trigger\n");
			setCameraFeature("SoftwareTrigger", 1);
		}
		callParamCallbacks();

		/* Wait for signal from grabber thread that image was received */
		unlock();
		auto t_start = std::chrono::high_resolution_clock::now();
		double waitTimeOut = 2.0*acquireTime+0.132;
		if (waitTimeOut<0.5) waitTimeOut = 0.5;
		eventStatus = epicsEventWaitWithTimeout(imageReceivedId_, waitTimeOut);
		auto t_end = std::chrono::high_resolution_clock::now();

		double elapsed_exp = std::chrono::duration<double, std::milli>(t_end-t_start).count();
		printf("Elapsed exposure time: %f ms\n", elapsed_exp);
		lock();
		getIntegerParam(ADAcquire, &acquire);
        if (!acquire){ // Stop was pressed during continuous acquisition
			printf("Aborting\n");
            GevStopTransfer(telCamHandle_);
	    	continue;
		}

		if (eventStatus == epicsEventOK){
				setIntegerParam(ADStatus, ADStatusReadout);
				getIntegerParam(NDArrayCounter, &imageCounter);
				getIntegerParam(ADNumImages, &numImages);
				getIntegerParam(ADNumImagesCounter, &numImagesCounter);
				getIntegerParam(NDArrayCallbacks, &arrayCallbacks);
				imageCounter++;
				numImagesCounter++;
				setIntegerParam(NDArrayCounter, imageCounter);
				setIntegerParam(ADNumImagesCounter, numImagesCounter);
				/* Call the callbacks to update any changes */
				callParamCallbacks();


				if (arrayCallbacks) {
					/* Call the NDArray callback */
					asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
							  "%s:%s: calling imageData callback\n", driverName, functionName);
					doCallbacksGenericPointer(pRaw_, NDArrayData, 0);
				}
		} else if (eventStatus == epicsEventWaitTimeout){
				int numShadRetries = 10;
				//getIntegerParam(ShadRetries, &numShadRetries);
				if (missedFrames<numShadRetries){
						missedFrames++;
						printf("Missed frame from timeout\n");
						char buf[50];
						sprintf(buf, "Missed Frame %d, Cycle %d", missedFrames, disconnectCycles);
						setIntegerParam(ADStatus, ADStatusError);
						setStringParam(ADStatusMessage, buf);
						// Continue to send another trigger
						continue;
						/*
				} else if ((missedFrames>=numShadRetries)&&(disconnectCycles<4)){
						disconnectCycles++;
						missedFrames = 0;
						setStringParam(ADStatusMessage, "Reconnecting...");
						callParamCallbacks();
						reconnectCamera();
						setStringParam(ADStatusMessage, "Reconnecting...Done");
						callParamCallbacks();
						int imagesCompleted = 0;
						getIntegerParam(ADNumImagesCounter, &imagesCompleted);
						int imagesRemaining = numImages - imagesCompleted;
						GevStartTransfer(telCamHandle_, imagesRemaining);
						continue;
						*/
				} else {
						setStringParam(ADStatusMessage, "Aborting acquisition. Sorry.");
						setIntegerParam(ADStatus, ADStatusError);
						acquire = 0;
						setIntegerParam(ADAcquire, acquire);
						callParamCallbacks();
						continue;
				}
		}

        /* See if acquisition is done */
        if ((imageMode == ADImageSingle) ||
            ((imageMode == ADImageMultiple) &&
             (numImagesCounter >= numImages))) {

            /* First do callback on ADStatus. */
            setStringParam(ADStatusMessage, "Waiting for acquisition");
            setIntegerParam(ADStatus, ADStatusIdle);
            callParamCallbacks();

            acquire = 0;
            setIntegerParam(ADAcquire, acquire);
            asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
                      "%s:%s: acquisition completed\n", driverName, functionName);
        }
		if (pRaw_ != NULL){
				//printf("Releasing pRaw\n");
				pRaw_->release();
		}

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


/** Called when asyn clients call pasynInt32->write().
  * This function performs actions for some parameters, including ADAcquire, ADColorMode, etc.
  * For all parameters it sets the value in the parameter library and calls any registered callbacks..
  * \param[in] pasynUser pasynUser structure that encodes the reason and address.
  * \param[in] value Value to write. */
asynStatus shadobox::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
    int function = pasynUser->reason;
    int adstatus;
    int acquiring;
    int imageMode;
    asynStatus status = asynSuccess;

    /* Ensure that ADStatus is set correctly before we set ADAcquire.*/
    getIntegerParam(ADStatus, &adstatus);
    getIntegerParam(ADAcquire, &acquiring);
    getIntegerParam(ADImageMode, &imageMode);
    if (function == ADAcquire) {
      if (value && !acquiring) {
        setStringParam(ADStatusMessage, "Acquiring data");
      }
      if (!value && acquiring) {
        setStringParam(ADStatusMessage, "Acquisition stopped");
        if (imageMode == ADImageContinuous) {
          setIntegerParam(ADStatus, ADStatusIdle);
        } else {
          setIntegerParam(ADStatus, ADStatusAborted);
        }
        setIntegerParam(ADStatus, ADStatusAcquire);
      }
    }
    callParamCallbacks();

    /* 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 = setIntegerParam(function, value);

    /* For a real detector this is where the parameter is sent to the hardware */
    if (function == ADAcquire) {
        if (value && !acquiring) {
            /* Send an event to wake up the simulation task.
             * It won't actually start generating new images until we release the lock below */
            epicsEventSignal(startEventId_);
            epicsEventSignal(startGrabTaskEventId_);
        }
        if (!value && acquiring) {
            /* This was a command to stop acquisition */
            /* Send the stop event */
            epicsEventSignal(stopEventId_);
			/* Main data task is waiting for an image so stop it*/
		    epicsEventSignal(imageReceivedId_);
        }
	} else if ((function == ADBinX) ||
			   (function == ADBinY)){
			if (value < 1) value = 1;
			if (value > 2) value = 2;
			int binX, binY;
			std::string rmode;
			getIntegerParam(ADBinX, &binX);
			getIntegerParam(ADBinY, &binY);
			if (value!=binX){
					setIntegerParam(ADBinX, value);
			}
			if (value!=binY){
					setIntegerParam(ADBinY, value);
			}
			if (value==2){
					rmode = "Binning";
			} else {
					rmode = "FullResolution";
			}
			setCameraFeature("ReadOutMode", rmode);
			updateSensorSize();
	} else if (function == ADMinX){
			if (value<0) value = 0;
			if (value>ADMaxSizeX-2) value = ADMaxSizeX-2;
			int sizeX, maxSizeX;
			getIntegerParam(ADSizeX, &sizeX);
			getIntegerParam(ADMaxSizeX, &maxSizeX);
			setCameraFeature("ROIStartH", value);
			if (value+sizeX>maxSizeX-1){
					sizeX = maxSizeX-1-value;
					setIntegerParam(ADSizeX, sizeX);
			}
			uint16_t stopX = value+sizeX;
			printf("Setting startH=%d, stopH=%d\n", value, stopX);
			setCameraFeature("ROIStopH", stopX);
	} else if (function == ADMinY){
			if (value<0) value = 0;
			if (value>ADMaxSizeY-2) value = ADMaxSizeY-2;
			int sizeY, maxSizeY;
			getIntegerParam(ADSizeY, &sizeY);
			getIntegerParam(ADMaxSizeY, &maxSizeY);
			setCameraFeature("ROIStartV", value);
			if (value+sizeY>maxSizeY-1){
					sizeY = maxSizeY-1-value;
					setIntegerParam(ADSizeY, sizeY);
			}
			int stopY = value+sizeY;
			printf("Setting startY=%d, stopY=%d\n", value, stopY);
			setCameraFeature("ROIStopV", stopY);
	} else if (function == ADSizeX){
			int maxSizeX, minX;
			getIntegerParam(ADMaxSizeX, &maxSizeX);
			getIntegerParam(ADMinX, &minX);
			if (value<1) value = 1;
			if (value>=maxSizeX-minX) value = maxSizeX-minX-1;
			setIntegerParam(ADSizeX, value);
			printf("roi stopH=%d\n", value);
			setCameraFeature("ROIStopH", value);
	} else if (function == ADSizeY){	
			int maxSizeY, minY;
			getIntegerParam(ADMaxSizeY, &maxSizeY);
			getIntegerParam(ADMinY, &minY);
			if (value<1) value = 1;
			if (value>=maxSizeY-minY) value = maxSizeY-minY-1;
			setIntegerParam(ADSizeY, value);
			printf("roi stopY=%d\n", value);
			setCameraFeature("ROIStopV", value);
	} else if (function == ShadReadOutMode){
			// 0 = Full Resolution, 1 = Binning, 2 = ROI
			setCameraFeature("ReadOutMode", value);
	} else if (function == ShadFullWell){
			setCameraFeature("FullWell", value);
	} else if (function == ShadTrigMode){
			if (value==0){
					setCameraFeature("SynchronizationMode", "Snapshot");
					printf("Synch mode: Snapshot\n");
			} else if (value==1){
					setCameraFeature("SynchronizationMode", "FreeRunning");
					printf("Synch mode: Free Running\n");
			}
	} else if (function == ShadGain){
			setCameraFeature("DigitalGain", value);
	} else if (function == ShadFFCEnable){
			setCameraFeature("FFCEnable", value);
	} else if (function == ShadFFCActive){
			setCameraFeature("FFCActive", value);
	} else if (function == ShadDPCEnable){
			setCameraFeature("DPCEnable", value);
	} else if (function == ShadDPCActive){
			setCameraFeature("DPCActive", value);
	} else if (function == ShadTurbo){
			setCameraFeature("transferTurboMode", value);
    } else {
        /* If this parameter belongs to a base class call its method */
        if (function < FIRST_SHAD_PARAM) status = ADDriver::writeInt32(pasynUser, value);
    }

    /* Do callbacks so higher layers see any changes */
    callParamCallbacks();

    if (status)
        asynPrint(pasynUser, ASYN_TRACE_ERROR,
              "%s:writeInt32 error, status=%d function=%d, value=%d\n",
              driverName, status, function, value);
    else
        asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
              "%s:writeInt32: function=%d, value=%d\n",
              driverName, function, value);
    return status;
}

/** Called when asyn clients call pasynFloat64->write().
  * This function performs actions for some parameters, including ADAcquireTime, ADGain, etc.
  * For all parameters it sets the value in the parameter library and calls any registered callbacks..
  * \param[in] pasynUser pasynUser structure that encodes the reason and address.
  * \param[in] value Value to write. */
asynStatus shadobox::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
{
    int function = pasynUser->reason;
    asynStatus status = asynSuccess;

    /* 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);

    /* Changing any of the simulation parameters requires recomputing the base image */
	if (function == ADAcquireTime){
			if (value<10e-3) value = 10e-3;
			if (value>60.0) value = 60.0;
			setCameraFeature("FrameInterval", (int)(1e6*value));
			setCameraFeature("SoftwareTrigIntTime", (int)(1e6*value));
			status = setDoubleParam(ADAcquireTime, value);
			int type, frameInterval, trigIntTime;
			GevGetFeatureValue(telCamHandle_, "FrameInterval", &type, sizeof(frameInterval), &frameInterval);
			GevGetFeatureValue(telCamHandle_, "SoftwareTrigIntTime", &type, sizeof(trigIntTime), &trigIntTime);
			printf("Frame interval: %d\n", frameInterval);
			printf("Trig int time: %d\n", trigIntTime);
	} else {
        /* This parameter belongs to a base class call its method */
        status = ADDriver::writeFloat64(pasynUser, value);
    }

    /* Do callbacks so higher layers see any changes */
    callParamCallbacks();
    if (status)
        asynPrint(pasynUser, ASYN_TRACE_ERROR,
              "%s:writeFloat64 error, status=%d function=%d, value=%f\n",
              driverName, status, function, value);
    else
        asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
              "%s:writeFloat64: function=%d, value=%f\n",
              driverName, function, value);
    return status;
}

int shadobox::setCameraFeature(std::string feature, unsigned int value){
		int rbv, type, telStatus;
		telStatus = GevSetFeatureValue(telCamHandle_, feature.c_str(), sizeof(value), &value);
		if (telStatus!=GEVLIB_OK){
				printf("Coud not set %s (status=%d)\n", feature.c_str(), telStatus);
		}
	    GevGetFeatureValue(telCamHandle_, feature.c_str(), &type, sizeof(rbv), &rbv);
		if (rbv!=value){
				printf( "%s:setCameraFeature error, status=%d feature=%s value=%d rbv=%d\n",
					  driverName, telStatus, feature.c_str(), value, rbv);
				return asynError;
		}

		return asynSuccess;
}

int shadobox::setCameraFeature(std::string feature, int value){
		int rbv, type, telStatus;
		telStatus = GevSetFeatureValue(telCamHandle_, feature.c_str(), sizeof(value), &value);
		if (telStatus!=GEVLIB_OK){
				printf("Coud not set %s (status=%d)\n", feature.c_str(), telStatus);
		}
	    GevGetFeatureValue(telCamHandle_, feature.c_str(), &type, sizeof(rbv), &rbv);
		if (rbv!=value){
				printf( "%s:setCameraFeature error, status=%d feature=%s value=%d rbv=%d\n",
					  driverName, telStatus, feature.c_str(), value, rbv);
				return asynError;
		}

		return asynSuccess;
}

int shadobox::setCameraFeature(std::string feature, double value){
		int type, telStatus;
		double rbv;
		telStatus = GevSetFeatureValue(telCamHandle_, feature.c_str(), sizeof(value), &value);
		if (telStatus!=GEVLIB_OK){
				printf("Coud not set %s (status=%d)\n", feature.c_str(), telStatus);
		}
		GevGetFeatureValue(telCamHandle_, feature.c_str(), &type, sizeof(rbv), &rbv);
		if (std::abs(rbv-value)>1e-3){
				printf("%s:setCameraFeature error, status=%d feature=%s value=%f\n",
					  driverName, telStatus, feature.c_str(), value);
				return asynError;
		}
		return asynSuccess;
}

int shadobox::setCameraFeature(std::string feature, std::string value){
		int type, telStatus;
		std::string rbv;
		telStatus = GevSetFeatureValueAsString(telCamHandle_, feature.c_str(), value.c_str());
		if (telStatus!=GEVLIB_OK){
				printf("Could not set %s (status=%d)\n", feature.c_str(), telStatus);
		}
		GevGetFeatureValueAsString(telCamHandle_, feature.c_str(), &type, sizeof(rbv), &rbv[0]);
		if (strcmp(value.c_str(), rbv.c_str())!=0){
				printf("%s:setCameraFeature error, status=%d feature=%s value=%s rbv=%s\n",
					  driverName, telStatus, feature.c_str(), value.c_str(), rbv.c_str());
				return asynError;
		}
		return asynSuccess;
}

/** Report status of the driver.
  * Prints details about the driver if details>0.
  * It then calls the ADDriver::report() method.
  * \param[in] fp File pointed passed by caller where the output is written to.
  * \param[in] details If >0 then driver details are printed.
  */
void shadobox::report(FILE *fp, int details)
{

    fprintf(fp, "Simulation detector %s\n", this->portName);
    if (details > 0) {
        int nx, ny, dataType;
        getIntegerParam(ADSizeX, &nx);
        getIntegerParam(ADSizeY, &ny);
        getIntegerParam(NDDataType, &dataType);
        fprintf(fp, "  NX, NY:            %d  %d\n", nx, ny);
        fprintf(fp, "  Data type:         %d\n", dataType);
    }
    /* Invoke the base class method */
    ADDriver::report(fp, details);
}

int shadobox::connectCamera(){
	static const char* functionName = "connectCamera";
	GEV_STATUS telStatus;
	int numCameras = 0;
	int type, status;
	unsigned int height, width, format;
	unsigned long int size;
	char synchModeStr[64] = {0};
	char versionString[40] = {0};

	GevGetLibraryConfigOptions(&telLibOpt_);
	telLibOpt_.logLevel = GEV_LOG_LEVEL_NORMAL;
	GevSetLibraryConfigOptions(&telLibOpt_);

	telStatus = GevGetCameraList(telCamInt_, MAX_CAMERAS, &numCameras);

	printf("%s:%s: Discovered %d cameras on the network\n",
		driverName, functionName, numCameras);
	if (numCameras==0){
		std::this_thread::sleep_for(std::chrono::milliseconds(3000));
		printf("%s:%s: No cameras detected on network\n",
				driverName, functionName);
		return asynError;
	}

	telStatus = GevOpenCameraBySN(serial_number, GevControlMode, &telCamHandle_);

	if (telStatus==0){
		char xmlFileName[1024] = {0};
		telStatus = GevGetGenICamXML_FileName( telCamHandle_, (int)sizeof(xmlFileName), xmlFileName);
		if (telStatus==GEVLIB_OK){
			printf("XML stored as: %s\n", xmlFileName);
		}

		/* Set some default values for parameters */
		char stringFeature[32] = {0};
		GevGetFeatureValueAsString(telCamHandle_, "DeviceVendorName", &type,  sizeof(stringFeature), stringFeature);
		status =  setStringParam(ADManufacturer, stringFeature);
		memset(stringFeature, 0, sizeof(stringFeature));
		GevGetFeatureValueAsString(telCamHandle_, "DeviceModelName", &type, sizeof(stringFeature), stringFeature);
		status =  setStringParam(ADModel, stringFeature);
		setStringParam(ADSerialNumber, serial_number);
		memset(stringFeature, 0, sizeof(stringFeature));
		GevGetFeatureValueAsString(telCamHandle_, "DeviceVersion", &type, sizeof(stringFeature), stringFeature);
		setStringParam(ADFirmwareVersion, stringFeature);
		memset(stringFeature, 0, sizeof(stringFeature));
		epicsSnprintf(versionString, sizeof(versionString), "%d.%d.%d",
				  DRIVER_VERSION, DRIVER_REVISION, DRIVER_MODIFICATION);
		setStringParam(NDDriverVersion, versionString);
		GevGetFeatureValueAsString(telCamHandle_, "HardwareRevision", &type, sizeof(stringFeature), stringFeature);
		setStringParam(ADSDKVersion, stringFeature);

		uint32_t intFeature = 0;
		uint32_t width, height;
		GevGetFeatureValue(telCamHandle_, "Width", &type, sizeof(width), &width);
		status |= setIntegerParam(ADMaxSizeX, width);
		status |= setIntegerParam(ADSizeX, width);
		status |= setIntegerParam(NDArraySizeX, width);
		GevGetFeatureValue(telCamHandle_, "Height", &type, sizeof(height), &height);
		status |= setIntegerParam(ADMaxSizeY, height);
		status |= setIntegerParam(ADSizeY, height);
		status |= setIntegerParam(NDArraySizeY, height);
		status |= setIntegerParam(ADMinX, 0);
		status |= setIntegerParam(ADMinY, 0);
		status |= setIntegerParam(ADBinX, 1);
		status |= setIntegerParam(ADBinY, 1);
		status |= setIntegerParam(ADReverseX, 0);
		status |= setIntegerParam(ADReverseY, 0);
		status |= setIntegerParam(NDArraySize, 0);
		status |= setIntegerParam(NDDataType, NDInt16);
		status |= setIntegerParam(NDColorMode, NDColorModeMono);
		status |= setIntegerParam(NDArraySize, 2*width*height);
		callParamCallbacks();

		GevGetCameraInterfaceOptions(telCamHandle_, &telCamOpt_);
		telCamOpt_.heartbeat_timeout_ms = 5000;     // Disconnect detection (5 seconds)
		telCamOpt_.streamFrame_timeout_ms = 1001; // Internal timeout for frame reception
		telCamOpt_.streamNumFramesBuffered = 1;   // Buffer frames internally
		telCamOpt_.streamMemoryLimitMax = 64*2304*2940; // Adjust packet memory buffering limit
		telCamOpt_.streamPktSize = 9216;
		telCamOpt_.streamPktDelay = 10;

		int numCpus = _GetNumCpus();
		if (numCpus>1){
			telCamOpt_.streamThreadAffinity = numCpus-1;
			telCamOpt_.serverThreadAffinity = numCpus-2;
		}
		GevSetCameraInterfaceOptions(telCamHandle_, &telCamOpt_);
		bufSize = width*height*2;
		for (int i=0; i<numBuffers; i++){
			bufAddress[i] = (PUINT8)malloc(bufSize);
			memset(bufAddress[i], 0, bufSize);
		}
		telStatus = GevInitializeTransfer(telCamHandle_, SynchronousNextEmpty, bufSize, numBuffers, bufAddress);

	} else {
			asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
				"%s:%s: Could not open camera (Error: %d)\n",
				driverName, functionName, numCameras);
			this->disconnect(this->pasynUserSelf);
		return asynError;
	}

	callParamCallbacks();
	return asynSuccess;
}

void shadobox::updateSensorSize(void){
		int type, width, height;
		int status;
		GevGetFeatureValue(telCamHandle_, "Width", &type, sizeof(width), &width);
		status |= setIntegerParam(ADMaxSizeX, width);
		status |= setIntegerParam(ADSizeX, width);
		status |= setIntegerParam(NDArraySizeX, width);
		GevGetFeatureValue(telCamHandle_, "Height", &type, sizeof(height), &height);
		status |= setIntegerParam(ADMaxSizeY, height);
		status |= setIntegerParam(ADSizeY, height);
		status |= setIntegerParam(NDArraySizeY, height);
		status |= setIntegerParam(NDArraySize, 2*width*height);
		callParamCallbacks();
}

/** Constructor for shadobox; most parameters are simply passed to ADDriver::ADDriver.
  * After calling the base class constructor this method creates a thread to compute the simulated detector data,
  * and sets reasonable default values for parameters defined in this class, asynNDArrayDriver and ADDriver.
  * \param[in] portName The name of the asyn port driver to be created.
  * \param[in] serial_number The serial number used to connect to the shadobox
  * \param[in] maxBuffers The maximum number of NDArray buffers that the NDArrayPool for this driver is
  *            allowed to allocate. Set this to -1 to allow an unlimited number of buffers.
  * \param[in] maxMemory The maximum amount of memory that the NDArrayPool for this driver is
  *            allowed to allocate. Set this to -1 to allow an unlimited amount of memory.
  * \param[in] priority The thread priority for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
  * \param[in] stackSize The stack size for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
  */
shadobox::shadobox(const char *portName, char* serial_number, int maxBuffers, size_t maxMemory, int priority, int stackSize)

    : ADDriver(portName, 1, 0, maxBuffers, maxMemory,
               0, 0, /* No interfaces beyond those set in ADDriver.cpp */
               0, 1, /* ASYN_CANBLOCK=0, ASYN_MULTIDEVICE=0, autoConnect=1 */
               priority, stackSize),
	  pRaw_(NULL), serial_number(serial_number) 
{
    int status = asynSuccess;
    char versionString[20];
    const char *functionName = "shadobox";

    /* Create the epicsEvents for signaling to the simulate task when acquisition starts and stops */
    startEventId_ = epicsEventCreate(epicsEventEmpty);
    if (!startEventId_) {
        printf("%s:%s epicsEventCreate failure for start event\n",
            driverName, functionName);
        return;
    }
    startGrabTaskEventId_ = epicsEventCreate(epicsEventEmpty);
    if (!startGrabTaskEventId_) {
        printf("%s:%s epicsEventCreate failure for start grab task event\n",
            driverName, functionName);
        return;
    }
    stopEventId_ = epicsEventCreate(epicsEventEmpty);
    if (!stopEventId_) {
        printf("%s:%s epicsEventCreate failure for stop event\n",
            driverName, functionName);
        return;
    }
    imageReceivedId_ = epicsEventCreate(epicsEventEmpty);
    if (!imageReceivedId_) {
        printf("%s:%s epicsEventCreate failure for image received event\n",
            driverName, functionName);
        return;
    }

	createParam(ShadFullWellString, 		  asynParamInt32,	&ShadFullWell);
	createParam(ShadGainString,				  asynParamInt32,   &ShadGain);
	createParam(ShadTrigModeString,			  asynParamInt32,   &ShadTrigMode);
	createParam(ShadRetriesString,			  asynParamInt32,   &ShadRetries);
	createParam(ShadReadOutModeString,		  asynParamInt32,   &ShadReadOutMode);
	createParam(ShadTurboString,			  asynParamInt32,   &ShadTurbo);
	createParam(ShadFFCEnableString,		  asynParamInt32, 	&ShadFFCEnable);
	createParam(ShadFFCActiveString,		  asynParamInt32, 	&ShadFFCActive);
	createParam(ShadDPCEnableString,		  asynParamInt32,   &ShadDPCEnable);
	createParam(ShadDPCActiveString,		  asynParamInt32,   &ShadDPCActive);

	status = connectCamera();
	if (status){
			printf("Didn't connect to camera\n");
			return;
	}

    /* Create the thread that updates the images */
    status = (epicsThreadCreate("ShadoboxMainDataTask",
                                epicsThreadPriorityMedium,
                                epicsThreadGetStackSize(epicsThreadStackMedium),
                                (EPICSTHREADFUNC)mainDataTaskC,
                                this) == NULL);
    if (status) {
        printf("%s:%s epicsThreadCreate failure for image grab task\n",
            driverName, functionName);
        return;
    }

    /* Create the thread that updates the temperature*/
    status = (epicsThreadCreate("ShadoboxTempReadTask",
                                epicsThreadPriorityMedium,
                                epicsThreadGetStackSize(epicsThreadStackMedium),
                                (EPICSTHREADFUNC)tempReadTaskC,
                                this) == NULL);
    if (status) {
        printf("%s:%s epicsThreadCreate failure for temp read task\n",
            driverName, functionName);
        return;
    }

    /* Create the thread that grabs images from buffer */
    status = (epicsThreadCreate("ShadoboxImageGrabTask",
                                epicsThreadPriorityMedium,
                                epicsThreadGetStackSize(epicsThreadStackMedium),
                                (EPICSTHREADFUNC)imageGrabTaskC,
                                this) == NULL);
    if (status) {
        printf("%s:%s epicsThreadCreate failure for image grab task\n",
            driverName, functionName);
        return;
    }

	epicsAtExit(c_shutdown, this);
	return;
}

/** Configuration command, called directly or from iocsh */
extern "C" int shadoboxConfig(const char *portName, char* serialNumber,
                                 int maxBuffers, int maxMemory, int priority, int stackSize)
{
    new shadobox(portName, serialNumber, 
                    (maxBuffers < 0) ? 0 : maxBuffers,
                    (maxMemory < 0) ? 0 : maxMemory,
                    priority, stackSize);
    return(asynSuccess);
}

/** Code for iocsh registration */
static const iocshArg shadoboxConfigArg0 = {"Port name", iocshArgString};
static const iocshArg shadoboxConfigArg1 = {"Serial Number", iocshArgString};
static const iocshArg shadoboxConfigArg2 = {"maxBuffers", iocshArgInt};
static const iocshArg shadoboxConfigArg3 = {"maxMemory", iocshArgInt};
static const iocshArg shadoboxConfigArg4 = {"priority", iocshArgInt};
static const iocshArg shadoboxConfigArg5 = {"stackSize", iocshArgInt};
static const iocshArg * const shadoboxConfigArgs[] =  {&shadoboxConfigArg0,
                                                       &shadoboxConfigArg1,
                                                       &shadoboxConfigArg2,
                                                       &shadoboxConfigArg3,
                                                       &shadoboxConfigArg4,
                                                       &shadoboxConfigArg5};
static const iocshFuncDef configShadobox = {"shadoboxConfig", 6, shadoboxConfigArgs};
static void configShadoboxCallFunc(const iocshArgBuf *args)
{
    shadoboxConfig(args[0].sval, args[1].sval, args[2].ival, args[3].ival,
                      args[4].ival, args[5].ival);
}

static void shadoboxRegister(void)
{

    iocshRegister(&configShadobox, configShadoboxCallFunc);
}

extern "C" {
epicsExportRegistrar(shadoboxRegister);
}

Attachment: ShadoBox HS Command Reference_rev09.pdf
Description: Adobe PDF document


Replies:
Re: AreaDetector Teledyne driver question Mark Rivers via Tech-talk

Navigate by Date:
Prev: areaDetector build errors Jong Woo Kim via Tech-talk
Next: Re: AreaDetector Teledyne driver question Mark Rivers via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  <20222023  2024 
Navigate by Thread:
Prev: Re: areaDetector build errors Jong Woo Kim via Tech-talk
Next: Re: AreaDetector Teledyne driver question Mark Rivers via Tech-talk
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  <20222023  2024 
ANJ, 14 Sep 2022 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·