v1.0.0-rc.36
This commit is contained in:
293
detector_control/DectrisSimplonClient.cpp
Normal file
293
detector_control/DectrisSimplonClient.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
#include "DectrisSimplonClient.h"
|
||||
|
||||
std::string to_string(SimplonState state) {
|
||||
switch (state) {
|
||||
case SimplonState::Na: return "Na";
|
||||
case SimplonState::Ready: return "Ready";
|
||||
case SimplonState::Initialize: return "Initialize";
|
||||
case SimplonState::Configure: return "Configure";
|
||||
case SimplonState::Acquire: return "Acquire";
|
||||
case SimplonState::Idle: return "Idle";
|
||||
case SimplonState::Test: return "Test";
|
||||
case SimplonState::Error: return "Error";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
DectrisSimplonClient::DectrisSimplonClient(const std::string &addr, uint16_t port)
|
||||
: cli(addr, port) {
|
||||
cli.set_connection_timeout(std::chrono::seconds(1));
|
||||
cli.set_read_timeout(std::chrono::minutes(20)); // Initialize or disarm might take a lot of time
|
||||
cli.set_write_timeout(std::chrono::minutes(20)); // Initialize or disarm might take a lot of time
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::SendDetectorCommand(SimplonDetectorCommand cmd) {
|
||||
std::string key;
|
||||
switch (cmd) {
|
||||
case SimplonDetectorCommand::Initialize:
|
||||
key = "initialize";
|
||||
break;
|
||||
case SimplonDetectorCommand::Arm:
|
||||
key = "arm";
|
||||
break;
|
||||
case SimplonDetectorCommand::Disarm:
|
||||
key = "disarm";
|
||||
break;
|
||||
case SimplonDetectorCommand::Trigger:
|
||||
key = "trigger";
|
||||
break;
|
||||
case SimplonDetectorCommand::Cancel:
|
||||
key = "cancel";
|
||||
break;
|
||||
}
|
||||
auto addr = GenAddr(SimplonModule::Detector, SimplonTask::Command, key);
|
||||
auto res = cli.Post(addr);
|
||||
if (!res || res->status != httplib::StatusCode::OK_200) {
|
||||
std::string err_msg = "Command failed " + key + "; HTTP " + std::to_string(res->status);
|
||||
if (res && !res->body.empty())
|
||||
err_msg += "; " + res->body;
|
||||
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::SetConfig(SimplonModule element, const std::string &key, const nlohmann::json &value) {
|
||||
std::string addr = GenAddr(element, SimplonTask::Config, key);
|
||||
|
||||
nlohmann::json j;
|
||||
j["value"] = value;
|
||||
|
||||
auto res = cli.Post(addr, j.dump(), "application/json");
|
||||
if (!res || res->status != httplib::StatusCode::OK_200) {
|
||||
std::string err_msg = "Configure failed " + key + "; HTTP " + std::to_string(res->status);
|
||||
if (res && !res->body.empty())
|
||||
err_msg += "; " + res->body;
|
||||
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, err_msg);
|
||||
}
|
||||
}
|
||||
|
||||
std::string DectrisSimplonClient::GenAddr(DectrisSimplonClient::SimplonModule element,
|
||||
DectrisSimplonClient::SimplonTask task,
|
||||
const std::string& key) {
|
||||
std::string addr;
|
||||
switch (element) {
|
||||
case SimplonModule::Detector:
|
||||
addr += "/detector/";
|
||||
break;
|
||||
case SimplonModule::Stream:
|
||||
addr += "/stream/";
|
||||
break;
|
||||
case SimplonModule::Filewriter:
|
||||
addr += "/filewriter/";
|
||||
break;
|
||||
case SimplonModule::Monitor:
|
||||
addr += "/monitor/";
|
||||
break;
|
||||
}
|
||||
addr += "api/";
|
||||
addr += api_version;
|
||||
switch (task) {
|
||||
case SimplonTask::Config:
|
||||
addr += "/config/";
|
||||
break;
|
||||
case SimplonTask::Command:
|
||||
addr += "/command/";
|
||||
break;
|
||||
case SimplonTask::Status:
|
||||
addr += "/status/";
|
||||
break;
|
||||
}
|
||||
addr += key;
|
||||
return addr;
|
||||
}
|
||||
|
||||
SimplonConfig
|
||||
DectrisSimplonClient::GetConfig(DectrisSimplonClient::SimplonModule element, DectrisSimplonClient::SimplonTask task,
|
||||
const std::string &key) {
|
||||
auto res = cli.Get(GenAddr(element, task, key));
|
||||
|
||||
if (!res) {
|
||||
throw JFJochException(JFJochExceptionCategory::SimplonError,
|
||||
"Get key " + key + " timeout/error: " + to_string(res.error()));
|
||||
}
|
||||
if (res->status != httplib::StatusCode::OK_200) {
|
||||
throw JFJochException(JFJochExceptionCategory::SimplonError,
|
||||
"Get key " + key + " failed: " + to_string(res.error()));
|
||||
}
|
||||
|
||||
try {
|
||||
auto j = nlohmann::json::parse(res->body);
|
||||
SimplonConfig ret{};
|
||||
ret.val = j["value"];
|
||||
if (j.contains("unit"))
|
||||
ret.unit = j["unit"];
|
||||
if (j.contains("min"))
|
||||
ret.min = j["min"];
|
||||
if (j.contains("max"))
|
||||
ret.max = j["max"];
|
||||
return ret;
|
||||
} catch (const std::exception &e) {
|
||||
throw JFJochException(JFJochExceptionCategory::JSON, e.what());
|
||||
}
|
||||
}
|
||||
|
||||
SimplonConfig DectrisSimplonClient::GetConfig(SimplonModule element, const std::string &key) {
|
||||
return GetConfig(element, SimplonTask::Config, key);
|
||||
}
|
||||
|
||||
SimplonConfig DectrisSimplonClient::GetDetCfg(const std::string &key) {
|
||||
return GetConfig(SimplonModule::Detector, key);
|
||||
}
|
||||
|
||||
nlohmann::json DectrisSimplonClient::GetStatus(DectrisSimplonClient::SimplonModule element, const std::string &key) {
|
||||
return GetConfig(element, SimplonTask::Status, key).val;
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::ReadDetectorConfig(DetectorSetup &setup) {
|
||||
// Need to initialize to read configuration information
|
||||
|
||||
if ((GetState() == SimplonState::Na)
|
||||
|| (GetState() == SimplonState::Error))
|
||||
throw JFJochException(JFJochExceptionCategory::Detector,
|
||||
"Detector state Na or Error - need to reinitialize before reading configuration");
|
||||
|
||||
setup.Description(GetDetCfg( "description").val);
|
||||
setup.PixelSize_um(std::round(GetDetCfg( "x_pixel_size").val.get<float>() * 1e6f));
|
||||
setup.SerialNumber(GetDetCfg( "detector_number").val);
|
||||
setup.SensorMaterial(GetDetCfg( "sensor_material").val);
|
||||
setup.SensorThickness_um(std::round(GetDetCfg( "sensor_thickness").val.get<float>() * 1e6f));
|
||||
|
||||
setup.BitDepthReadout(GetDetCfg("bit_depth_readout").val.get<int64_t>());
|
||||
setup.BitDepthImage(GetDetCfg("bit_depth_image").val.get<int64_t>());
|
||||
setup.MinFrameTime(std::chrono::microseconds(std::lround(GetDetCfg("frame_time").min.get<float>() * 1e6f)));
|
||||
setup.MinCountTime(std::chrono::microseconds(std::lround(GetDetCfg("count_time").min.get<float>() * 1e6f)));
|
||||
setup.ReadOutTime(std::chrono::microseconds(std::lround(GetDetCfg("detector_readout_time").val.get<float>() * 1e6f)));
|
||||
setup.MinThreshold_keV(GetDetCfg("threshold_energy").min.get<float>() / 1000.0f);
|
||||
|
||||
int64_t width = GetDetCfg("x_pixels_in_detector").val.get<int64_t>();
|
||||
int64_t height = GetDetCfg( "y_pixels_in_detector").val.get<int64_t>();
|
||||
setup.Geometry(DetectorGeometryFixed(width, height));
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::InitializeDetector(DetectorSetup& setup) {
|
||||
auto state = GetState();
|
||||
|
||||
if ((state == SimplonState::Na) || (state == SimplonState::Error))
|
||||
SendDetectorCommand(SimplonDetectorCommand::Initialize);
|
||||
|
||||
if (GetState() != SimplonState::Idle)
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Initialize unsuccessful");
|
||||
|
||||
SetConfig(SimplonModule::Detector, "roi_mode", setup.GetDECTRISROI());
|
||||
int64_t width = GetDetCfg("x_pixels_in_detector").val.get<int64_t>();
|
||||
int64_t height = GetDetCfg( "y_pixels_in_detector").val.get<int64_t>();
|
||||
|
||||
setup.Geometry(DetectorGeometryFixed(width, height));
|
||||
setup.PixelSize_um(std::round(GetDetCfg( "x_pixel_size").val.get<float>() * 1e6f));
|
||||
setup.SerialNumber(GetDetCfg( "detector_number").val);
|
||||
setup.SensorMaterial(GetDetCfg( "sensor_material").val);
|
||||
setup.SensorThickness_um(std::round(GetDetCfg( "sensor_thickness").val.get<float>() * 1e6f));
|
||||
setup.MinFrameTime(std::chrono::microseconds(std::lround(GetDetCfg("frame_time").min.get<float>() * 1e6f)));
|
||||
setup.MinCountTime(std::chrono::microseconds(std::lround(GetDetCfg("count_time").min.get<float>() * 1e6f)));
|
||||
setup.ReadOutTime(std::chrono::microseconds(std::lround(GetDetCfg("detector_readout_time").val.get<float>() * 1e6f)));
|
||||
setup.MinThreshold_keV(GetDetCfg("threshold_energy").min.get<float>() / 1000.0f);
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::TriggerAcquisition() {
|
||||
SendDetectorCommand(SimplonDetectorCommand::Trigger);
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::CancelAcquisition() {
|
||||
SendDetectorCommand(SimplonDetectorCommand::Cancel);
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::EndAcquisitionFinished() {
|
||||
SendDetectorCommand(SimplonDetectorCommand::Disarm);
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::StartAcquisition() {
|
||||
SendDetectorCommand(SimplonDetectorCommand::Arm);
|
||||
}
|
||||
|
||||
void DectrisSimplonClient::ConfigureDetector(const DiffractionExperiment &experiment) {
|
||||
SetConfig(SimplonModule::Stream, "format", "cbor");
|
||||
SetConfig(SimplonModule::Stream, "mode", "enabled");
|
||||
SetConfig(SimplonModule::Filewriter, "mode", "disabled");
|
||||
SetConfig(SimplonModule::Monitor, "mode", "disabled");
|
||||
|
||||
SetConfig(SimplonModule::Detector, "photon_energy", experiment.GetIncidentEnergy_keV() * 1e3f);
|
||||
auto thr = experiment.GetEigerThreshold_keV();
|
||||
SetConfig(SimplonModule::Detector, "threshold_energy", thr * 1e3f);
|
||||
|
||||
switch (experiment.GetDetectorTiming()) {
|
||||
case DetectorTiming::Auto:
|
||||
SetConfig(SimplonModule::Detector, "trigger_mode", "ints");
|
||||
break;
|
||||
case DetectorTiming::Trigger:
|
||||
SetConfig(SimplonModule::Detector, "trigger_mode", "exts");
|
||||
break;
|
||||
case DetectorTiming::Gated:
|
||||
SetConfig(SimplonModule::Detector, "trigger_mode", "extg");
|
||||
break;
|
||||
case DetectorTiming::Burst:
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"Burst mode not supported with DECTRIS sytems");
|
||||
}
|
||||
|
||||
// For DECTRIS detectors we assume frame_time == count_time
|
||||
if (experiment.GetFrameCountTimeAuto())
|
||||
SetConfig(SimplonModule::Detector, "count_time", experiment.GetFrameTime().count() / 1e6f);
|
||||
else
|
||||
SetConfig(SimplonModule::Detector, "count_time", experiment.GetFrameCountTime().count() / 1e6f);
|
||||
|
||||
SetConfig(SimplonModule::Detector, "frame_time", experiment.GetFrameTime().count() / 1e6f);
|
||||
SetConfig(SimplonModule::Detector, "nimages", experiment.GetFrameNumPerTrigger());
|
||||
SetConfig(SimplonModule::Detector, "ntrigger", experiment.GetNumTriggers());
|
||||
|
||||
SetConfig(SimplonModule::Detector, "beam_center_x", experiment.GetBeamX_pxl());
|
||||
SetConfig(SimplonModule::Detector, "beam_center_y", experiment.GetBeamY_pxl());
|
||||
SetConfig(SimplonModule::Detector, "detector_distance", experiment.GetDetectorDistance_mm() / 1e3f);
|
||||
SetConfig(SimplonModule::Detector, "trigger_start_delay", experiment.GetDetectorDelay().count() / 1e9);
|
||||
}
|
||||
|
||||
SimplonState DectrisSimplonClient::GetState() {
|
||||
auto c = GetStatus(SimplonModule::Detector, "state");
|
||||
if (!c.is_string())
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"value must be string");
|
||||
|
||||
std::string res = c.get<std::string>();
|
||||
|
||||
if (res == "na")
|
||||
return SimplonState::Na;
|
||||
else if (res == "ready")
|
||||
return SimplonState::Ready;
|
||||
else if (res == "idle")
|
||||
return SimplonState::Idle;
|
||||
else if (res == "acquire")
|
||||
return SimplonState::Acquire;
|
||||
else if (res == "error")
|
||||
return SimplonState::Error;
|
||||
else if (res == "initialize")
|
||||
return SimplonState::Initialize;
|
||||
else if (res == "configure")
|
||||
return SimplonState::Configure;
|
||||
else if (res == "test")
|
||||
return SimplonState::Test;
|
||||
|
||||
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
||||
"State unknown " + res);
|
||||
}
|
||||
|
||||
float DectrisSimplonClient::GetHumidity() {
|
||||
return GetStatus(SimplonModule::Detector, "humidity");
|
||||
}
|
||||
|
||||
float DectrisSimplonClient::GetTemperature() {
|
||||
return GetStatus(SimplonModule::Detector, "temperature");
|
||||
}
|
||||
Reference in New Issue
Block a user