From d440649547f3545138816dc36643281cce61dfac Mon Sep 17 00:00:00 2001 From: Jakub Wlodek Date: Thu, 23 Apr 2026 10:18:36 -0400 Subject: [PATCH 1/2] Correctly handle case where summed frames is greater than 1 --- sim/xspdSimulator.py | 4 ++-- xspdApp/src/ADXSPD.cpp | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/sim/xspdSimulator.py b/sim/xspdSimulator.py index f95a7e3..4df9185 100755 --- a/sim/xspdSimulator.py +++ b/sim/xspdSimulator.py @@ -3,8 +3,8 @@ """ FastAPI + ZMQ based simulator for the ADXSPD driver. -Loads initial state from a JSON dump file (same format as -xspdApp/tests/samples/xspd_sample_responses.json) and serves +Loads initial state from a JSON dump file created with the +generate_sample_response_json.py script and serves the REST API expected by the ADXSPD EPICS driver while streaming simulated image data over ZMQ. diff --git a/xspdApp/src/ADXSPD.cpp b/xspdApp/src/ADXSPD.cpp index 3323c44..90d61aa 100644 --- a/xspdApp/src/ADXSPD.cpp +++ b/xspdApp/src/ADXSPD.cpp @@ -225,7 +225,7 @@ void ADXSPD::acquisitionThread() { // void* prevFrameBuffer = nullptr; int arrayCallbacks, decompress; - int collectedImages; + int collectedImages, summedFrames; void* zmqSubscriber = zmq_socket(this->zmqContext, ZMQ_SUB); int rc = zmq_connect(zmqSubscriber, this->pDetector->GetActiveDataPort()->GetURI().c_str()); @@ -718,12 +718,25 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) { string endpoint; if (function == ADXSPD_BitDepth) { actualValue = this->pDetector->SetVar("bit_depth", value); - setIntegerParam(NDDataType, static_cast(getDataTypeForBitDepth(actualValue))); + int summedFrames = this->pDetector->GetVar("summed_frames"); + if (summedFrames > 1) { + // If summed frames > 1, libxsp will always return uint32 data regardless of bit depth, so set that here to avoid confusion + setIntegerParam(NDDataType, NDUInt32); + } else { + setIntegerParam(NDDataType, static_cast(getDataTypeForBitDepth(actualValue))); + } for (auto& module : this->modules) { module->getMaxNumImages(); } } else if (function == ADXSPD_SummedFrames) { actualValue = this->pDetector->SetVar("summed_frames", value); + if (actualValue > 1) { + // If summed frames > 1, libxsp will always return uint32 data regardless of bit depth, so set that here to avoid confusion + setIntegerParam(NDDataType, NDUInt32); + } else { + int bitDepth = static_cast(this->pDetector->GetVar("bit_depth")); + setIntegerParam(NDDataType, static_cast(getDataTypeForBitDepth(bitDepth))); + } } else if (function == ADXSPD_RoiRows) { actualValue = static_cast(this->pDetector->SetVar("roi_rows", value)); for (auto& module : this->modules) { @@ -782,7 +795,7 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) { callParamCallbacks(); if (status) { - ERR_TO_STATUS_ARGS("status=%d, parameter=%s, value=%d", status, paramName, value); + ERR_ARGS("status=%d, parameter=%s, value=%d", status, paramName, value); return asynError; } else { DEBUG_ARGS("parameter=%s value=%d", paramName, value); From e981a71274f80d4fbc775944119168e000fa6f7b Mon Sep 17 00:00:00 2001 From: Jakub Wlodek Date: Thu, 23 Apr 2026 10:34:47 -0400 Subject: [PATCH 2/2] Summed Frames > 1 not supported in dual counter mode --- xspdApp/src/ADXSPD.cpp | 49 +++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/xspdApp/src/ADXSPD.cpp b/xspdApp/src/ADXSPD.cpp index 90d61aa..4b2092f 100644 --- a/xspdApp/src/ADXSPD.cpp +++ b/xspdApp/src/ADXSPD.cpp @@ -729,13 +729,20 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) { module->getMaxNumImages(); } } else if (function == ADXSPD_SummedFrames) { - actualValue = this->pDetector->SetVar("summed_frames", value); - if (actualValue > 1) { - // If summed frames > 1, libxsp will always return uint32 data regardless of bit depth, so set that here to avoid confusion - setIntegerParam(NDDataType, NDUInt32); + XSPD::CounterMode counterMode; + getIntegerParam(ADXSPD_CounterMode, (int*) &counterMode); + if (counterMode == XSPD::CounterMode::DUAL && value > 1) { + ERR_TO_STATUS("Dual counter mode not supporeted with summed frames > 1"); + status = asynError; } else { - int bitDepth = static_cast(this->pDetector->GetVar("bit_depth")); - setIntegerParam(NDDataType, static_cast(getDataTypeForBitDepth(bitDepth))); + actualValue = this->pDetector->SetVar("summed_frames", value); + if (actualValue > 1) { + // If summed frames > 1, libxsp will always return uint32 data regardless of bit depth, so set that here to avoid confusion + setIntegerParam(NDDataType, NDUInt32); + } else { + int bitDepth = static_cast(this->pDetector->GetVar("bit_depth")); + setIntegerParam(NDDataType, static_cast(getDataTypeForBitDepth(bitDepth))); + } } } else if (function == ADXSPD_RoiRows) { actualValue = static_cast(this->pDetector->SetVar("roi_rows", value)); @@ -758,11 +765,17 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) { actualValue = static_cast(this->pDetector->SetVar( "countrate_correction", static_cast(value))); } else if (function == ADXSPD_CounterMode) { - actualValue = static_cast(this->pDetector->SetVar( - "counter_mode", static_cast(value))); - for (auto& module : this->modules) { - module->getMaxNumImages(); - module->getFlatfieldState(); // FF is different for each counter mode + int framesSummed; + getIntegerParam(ADXSPD_SummedFrames, &framesSummed); + if (framesSummed > 1) { + ERR_TO_STATUS("Dual counter mode not supporeted with summed frames > 1"); + } else { + actualValue = static_cast(this->pDetector->SetVar( + "counter_mode", static_cast(value))); + for (auto& module : this->modules) { + module->getMaxNumImages(); + module->getFlatfieldState(); // FF is different for each counter mode + } } } else if (function == ADXSPD_SaturationFlag) { actualValue = static_cast(this->pDetector->SetVar( @@ -776,13 +789,15 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) { } } - setIntegerParam(function, actualValue); - if (actualValue != value) { - WARN_ARGS("Requested value %d for parameter %s, but set value is %d", value, - paramName, actualValue); - status = asynError; + if (status != asynError) { + setIntegerParam(function, actualValue); + if (actualValue != value) { + WARN_ARGS("Requested value %d for parameter %s, but set value is %d", value, + paramName, actualValue); + status = asynError; + } + INFO_TO_STATUS_ARGS("Set %s to %d", formatParamName(paramName).c_str(), actualValue); } - INFO_TO_STATUS_ARGS("Set %s to %d", formatParamName(paramName).c_str(), actualValue); } catch (std::invalid_argument& e) { ERR_TO_STATUS_ARGS("Invalid argument when setting parameter %s: %s", paramName, e.what());