Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 33 additions & 12 deletions xspdApp/src/ADXSPD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,13 @@ asynStatus ADXSPD::acquireStart() {
setIntegerParam(ADNumImagesCounter, 0);

try {
int sizeX = this->pDetector->GetActiveDataPort()->GetVar<int>("frame_width");
int sizeY = this->pDetector->GetActiveDataPort()->GetVar<int>("frame_height");
setIntegerParam(ADSizeX, sizeX);
setIntegerParam(ADSizeY, sizeY);
// Frame geometry is cached at init and refreshed on roi_rows writes, so no need to
// re-read it from the detector on every acquisition.
setIntegerParam(ADStatus, ADStatusAcquire);

callParamCallbacks();

this->pDetector->ExecCommand("start");
this->pDetector->StartAcquisition();

INFO_TO_STATUS("Acquisition started");

Expand All @@ -163,6 +161,26 @@ asynStatus ADXSPD::acquireStart() {
return asynSuccess;
}

/**
* @brief Reads the data port frame geometry and caches it in the size parameters
*
* @return asynStatus asynSuccess on success, asynError if the geometry could not be read
*/
asynStatus ADXSPD::refreshFrameSize() {
try {
int sizeX = this->pDetector->GetActiveDataPort()->GetVar<int>("frame_width");
int sizeY = this->pDetector->GetActiveDataPort()->GetVar<int>("frame_height");
setIntegerParam(ADMaxSizeX, sizeX);
setIntegerParam(ADMaxSizeY, sizeY);
setIntegerParam(ADSizeX, sizeX);
setIntegerParam(ADSizeY, sizeY);
} catch (std::exception& e) {
ERR_ARGS("Failed to refresh frame size: %s", e.what());
return asynError;
}
return asynSuccess;
}

/**
* @brief stops acquisition by aborting exposure and joinging acq thread
*
Expand All @@ -171,7 +189,7 @@ asynStatus ADXSPD::acquireStart() {
asynStatus ADXSPD::acquireStop() {
setIntegerParam(ADAcquire, 0);
try {
this->pDetector->ExecCommand("stop");
this->pDetector->StopAcquisition();
setIntegerParam(ADStatus, ADStatusIdle);
callParamCallbacks();
} catch (std::exception& e) {
Expand Down Expand Up @@ -400,10 +418,12 @@ void ADXSPD::acquisitionThread() {
// shuffle record). The XSPD::ShuffleMode enum has been set up with the same
// values for ease of translation, so we can just static_cast it here. If ADCore
// gets an enumeration for this in the future, it should be used here.
XSPD::ShuffleMode shuffleMode =
this->pDetector->GetVar<XSPD::ShuffleMode>("shuffle_mode");
// Read from the cached parameter rather than the API to avoid a per-frame
// request.
int shuffleMode;
getIntegerParam(ADXSPD_ShuffleMode, &shuffleMode);
// TODO: Handle auto shuffle mode correctly.
pArray->codec.shuffle = static_cast<int>(shuffleMode);
pArray->codec.shuffle = shuffleMode;
}

if (!readoutOk) {
Expand Down Expand Up @@ -615,8 +635,7 @@ asynStatus ADXSPD::getInitialDetState() {
}

// Retrieve all remaining initial parameters.
status |= this->getDataPortVar<int>(ADMaxSizeX, "frame_width");
status |= this->getDataPortVar<int>(ADMaxSizeY, "frame_height");
status |= this->refreshFrameSize();
status |= this->getDetVar<int>(ADXSPD_SummedFrames, "summed_frames");
status |= this->getDetVar<int>(ADXSPD_CompressLevel, "compression_level");
status |= this->getDetVar<XSPD::Compressor>(ADXSPD_Compressor, "compressor");
Expand Down Expand Up @@ -698,7 +717,7 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) {
} else if (function == ADNumImages) {
int maxNumImages = INT_MAX;
for (auto& module : this->modules) {
int moduleMax = module->getMaxNumImages();
int moduleMax = module->getCachedMaxNumImages();
if (moduleMax < maxNumImages) {
maxNumImages = moduleMax;
}
Expand Down Expand Up @@ -730,6 +749,8 @@ asynStatus ADXSPD::writeInt32(asynUser* pasynUser, epicsInt32 value) {
actualValue = this->pDetector->SetVar<int>("summed_frames", value);
} else if (function == ADXSPD_RoiRows) {
actualValue = static_cast<int>(this->pDetector->SetVar<int>("roi_rows", value));
// ROI readout may change the data port frame geometry; refresh cached size.
this->refreshFrameSize();
for (auto& module : this->modules) {
module->getMaxNumImages();
}
Expand Down
1 change: 1 addition & 0 deletions xspdApp/src/ADXSPD.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class ADXSPD : ADDriver {
ADXSPDLogLevel getLogLevel() { return this->logLevel; }

asynStatus getInitialDetState();
asynStatus refreshFrameSize();
asynStatus acquireStart();
asynStatus acquireStop();

Expand Down
8 changes: 8 additions & 0 deletions xspdApp/src/ADXSPDModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ int ADXSPDModule::getMaxNumImages() {
return maxFrames;
}

// Returns the last known max_frames without querying the API. Only changes with bit_depth,
// roi_rows, and counter_mode, all of which refresh the cached value on write.
int ADXSPDModule::getCachedMaxNumImages() {
int maxFrames;
getIntegerParam(ADXSPDModule_MaxFrames, &maxFrames);
return maxFrames;
}

void ADXSPDModule::getInitialModuleState() {
this->checkStatus();

Expand Down
1 change: 1 addition & 0 deletions xspdApp/src/ADXSPDModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class ADXSPDModule : public asynPortDriver {
void getInitialModuleState();
void getFlatfieldState();
int getMaxNumImages();
int getCachedMaxNumImages();

protected:
// Module parameters
Expand Down
41 changes: 10 additions & 31 deletions xspdApp/src/XSPDAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ XSPD::Detector* XSPD::API::Initialize(string deviceId) {
"Detector information is missing 'detector-id' or 'modules' field for device ID " +
this->deviceId);

// Cache available commands on init to avoid repeated API calls during acq
json availableCommands = Get("devices/" + this->deviceId + "/commands");
for (auto& cmd : availableCommands) {
if (cmd.contains("path")) {
this->availableCommands.push_back(cmd["path"].get<string>());
}
}

this->detector = make_unique<Detector>(this, detectorInfo["detector-id"].get<string>());
for (auto& moduleJson : detectorInfo["modules"]) {
int numChips = moduleJson["chips"].get<int>();
Expand Down Expand Up @@ -344,16 +352,8 @@ json XSPD::API::Put(string endpoint) {
* @param command The command to execute
*/
void XSPD::API::ExecCommand(string command) {
json availableCommands = Get("devices/" + this->deviceId + "/commands");
bool commandFound = false;
for (auto& cmd : availableCommands) {
if (cmd["path"] == command) {
commandFound = true;
break;
}
}

if (!commandFound)
if (std::find(this->availableCommands.begin(), this->availableCommands.end(), command) ==
this->availableCommands.end())
throw invalid_argument("Command '" + command + "' not found for device ID " +
this->deviceId);

Expand Down Expand Up @@ -436,24 +436,3 @@ string XSPD::Detector::GetSerialNumber() {
if (sn.empty()) sn = this->GetAPI()->GetSystemId();
return sn;
}

// XSPD::CompressionSettings XSPD::Detector::GetCompressionSettings() {
// string fullCompressor = this->GetVar<string>("compressor");
// auto slashPos = fullCompressor.find('/');
// Compressor baseCompressor = magic_enum::enum_cast<Compressor>((slashPos != string::npos) ?
// fullCompressor.substr(0, slashPos) : fullCompressor).value(); int compressionLevel =
// this->GetVar<int>("compression_level"); if (baseCompressor == Compressor::BLOSC) {
// BloscCompressionSettings settings;
// settings.compressor = baseCompressor;
// settings.compressionLevel = compressionLevel;

// settings.bloscCompressor =
// magic_enum::enum_cast<BloscCompressor>(fullCompressor.substr(slashPos + 1)).value();
// settings.shuffleMode = this->GetVar<ShuffleMode>("shuffle_mode");
// return settings;
// }
// CompressionSettings settings;
// settings.compressor = baseCompressor;
// settings.compressionLevel = compressionLevel;
// return settings;
// }
18 changes: 7 additions & 11 deletions xspdApp/src/XSPDAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <cpr/cpr.h>

#include <algorithm>
#include <iostream>
#include <magic_enum/magic_enum.hpp>
#include <map>
Expand Down Expand Up @@ -87,17 +88,6 @@ enum class APIState {
INITIALIZED = 4,
};

// struct CompressionSettings {
// Compressor compressor;
// int compressionLevel;
// virtual ~CompressionSettings() = default;
// };

// struct BloscCompressionSettings : public CompressionSettings {
// BloscCompressor bloscCompressor;
// ShuffleMode shuffleMode;
// };

// Default port number for XSPD API
constexpr int DEFAULT_PORT = 8008;

Expand All @@ -123,6 +113,7 @@ class API {
string GetSystemId();

bool DeviceExists(string deviceId);
bool IsCommandAvailable(string command);
string GetDeviceAtIndex(int deviceIndex);
virtual json SubmitRequest(string uri, RequestType reqType);

Expand Down Expand Up @@ -241,6 +232,7 @@ class API {
private:
mutex apiMutex; // Mutex to protect API calls and internal state
string baseUri, apiVersion, xspdVersion, libxspVersion, deviceId, systemId;
vector<string> availableCommands;
unique_ptr<Detector> detector;
};

Expand Down Expand Up @@ -440,6 +432,10 @@ class Detector : public APIComponent {
DataPort* GetActiveDataPort() { return this->activeDataPort; }

void ExecCommand(string command) { this->GetAPI()->ExecCommand(this->GetId() + "/" + command); }

// Convenience methods for starting and stopping acquisition
void StartAcquisition() { this->ExecCommand("start"); }
void StopAcquisition() { this->ExecCommand("stop"); }
// CompressionSettings GetCompressionSettings();

private:
Expand Down
1 change: 1 addition & 0 deletions xspdApp/tests/MockXSPDAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ void MockXSPDAPI::MockInitializationSeq(std::string deviceId) {
this->MockAPIVersionCheck();
this->MockGetRequest("devices");
this->MockGetRequest("devices/" + deviceId + "/variables?path=info");
this->MockGetRequest("devices/" + deviceId + "/commands");
this->MockGetRequest("devices/" + deviceId);
}

Expand Down
2 changes: 2 additions & 0 deletions xspdApp/tests/TestXSPDAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ TEST_F(TestXSPDAPI, TestAPIInitNoDataPorts) {
this->mapi->MockAPIVersionCheck();
this->mapi->MockGetRequest("devices");
this->mapi->MockGetVarRequest("info");
this->mapi->MockGetRequest("devices/lambda01/commands");
this->mapi->MockGetRequest("devices/lambda01", &modifiedDeviceInfo);
EXPECT_THAT([&]() { this->mapi->Initialize("lambda01"); },
testing::ThrowsMessage<std::runtime_error>(
Expand All @@ -196,6 +197,7 @@ TEST_F(TestXSPDAPI, TestAPIInitMissingDataPortInfo) {
this->mapi->MockAPIVersionCheck();
this->mapi->MockGetRequest("devices");
this->mapi->MockGetVarRequest("info");
this->mapi->MockGetRequest("devices/lambda01/commands");
this->mapi->MockGetRequest("devices/lambda01", &modifiedDeviceInfo);
EXPECT_THAT([&]() { this->mapi->Initialize("lambda01"); },
testing::ThrowsMessage<std::runtime_error>(
Expand Down
Loading