Files
Jungfraujoch/broker/JFJochStateMachine.cpp
2024-03-14 20:26:03 +01:00

693 lines
24 KiB
C++

// Copyright (2019-2023) Paul Scherrer Institute
#include <thread>
#include "JFJochStateMachine.h"
void ApplyDetectorSettings(DiffractionExperiment& experiment, const DetectorSettings &settings) {
auto tmp = experiment;
try {
if (settings.count_time_us)
experiment.FrameTime(std::chrono::microseconds(settings.frame_time_us),
std::chrono::microseconds(settings.count_time_us.value()));
else
experiment.FrameTime(std::chrono::microseconds(settings.frame_time_us));
experiment.StorageCells(settings.storage_cell_count);
experiment.UseInternalPacketGenerator(settings.use_internal_packet_generator);
// If internal packet generator is used --> no conversion on FPGA
experiment.ConversionOnFPGA(!settings.use_internal_packet_generator);
if (settings.collect_raw_data)
experiment.Mode(DetectorMode::Raw);
else
experiment.Mode(DetectorMode::Conversion);
experiment.UsingGainHG0(settings.use_gain_hg0);
experiment.FixedGainG1(settings.fixed_gain_g1);
if (settings.pedestal_g0_frames)
experiment.PedestalG0Frames(settings.pedestal_g0_frames.value());
if (settings.pedestal_g1_frames)
experiment.PedestalG1Frames(settings.pedestal_g1_frames.value());
if (settings.pedestal_g2_frames)
experiment.PedestalG2Frames(settings.pedestal_g2_frames.value());
if (settings.storage_cell_delay_ns)
experiment.StorageCellDelay(std::chrono::nanoseconds(settings.storage_cell_delay_ns.value()));
if (settings.detector_delay_ns)
experiment.DetectorDelay(std::chrono::nanoseconds(settings.detector_delay_ns.value()));
if (settings.internal_packet_generator_images)
experiment.InternalPacketGeneratorImages(settings.internal_packet_generator_images.value());
} catch (...) {
experiment = tmp;
throw;
}
}
void ApplyRadialIntegrationSettings(DiffractionExperiment& experiment, const RadialIntegrationSettings &settings) {
auto tmp = experiment;
try {
if (settings.polarization_factor) {
experiment.ApplyPolarizationCorr(true);
experiment.PolarizationFactor(settings.polarization_factor.value());
} else
experiment.ApplyPolarizationCorr(false);
experiment.ApplySolidAngleCorr(settings.solid_angle_correction);
experiment.LowQForAzimInt_recipA(settings.low_q_recipA);
experiment.HighQForAzimInt_recipA(settings.high_q_recipA);
experiment.QSpacingForAzimInt_recipA(settings.q_spacing);
} catch (...) {
experiment = tmp;
throw;
}
}
JFJochStateMachine::JFJochStateMachine(JFJochServices &in_services, Logger &in_logger)
: services(in_services), logger(in_logger),
data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()) {
}
bool JFJochStateMachine::ImportPedestalG0(const JFJochReceiverOutput &receiver_output) {
if (receiver_output.pedestal_result.empty())
return false;
if (receiver_output.pedestal_result.size() != experiment.GetModulesNum() * experiment.GetStorageCellNumber())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in pedestal output");
size_t gain_level = experiment.IsFixedGainG1() ? 1 : 0;
for (int s = 0; s < experiment.GetStorageCellNumber(); s++) {
for (int module = 0; module < experiment.GetModulesNum(); module++)
calibration->Pedestal(module, gain_level, s)
= receiver_output.pedestal_result[module + s * experiment.GetModulesNum()];
}
SetCalibrationStatistics(calibration->GetModuleStatistics());
return true;
}
bool JFJochStateMachine::ImportPedestalG1G2(const JFJochReceiverOutput &receiver_output, size_t gain_level,
size_t storage_cell) {
if (receiver_output.pedestal_result.empty())
return false;
if (receiver_output.pedestal_result.size() != experiment.GetModulesNum())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in pedestal output");
for (int i = 0; i < receiver_output.pedestal_result.size(); i++)
calibration->Pedestal(i, gain_level, storage_cell) = receiver_output.pedestal_result[i];
SetCalibrationStatistics(calibration->GetModuleStatistics());
return true;
}
void JFJochStateMachine::TakePedestalInternalAll(std::unique_lock<std::mutex> &ul) {
calibration = std::make_unique<JFCalibration>(experiment);
if (!gain_calibration.empty()) {
if (gain_calibration.size() != experiment.GetModulesNum())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in gain files number");
for (int i = 0; i < gain_calibration.size(); i++)
calibration->GainCalibration(i) = gain_calibration[i];
}
cancel_sequence = false;
logger.Info("Pedestal sequence started");
try {
TakePedestalInternalG0(ul);
if (!experiment.IsFixedGainG1()) {
for (int i = 0; i < experiment.GetStorageCellNumber(); i++) {
TakePedestalInternalG1(ul, i);
TakePedestalInternalG2(ul, i);
}
}
services.ConfigureDetector(experiment);
} catch (const std::exception &e) {
logger.Error("Pedestal sequence error {}", e.what());
state = JFJochState::Error;
throw;
}
logger.Info("Pedestal sequence done");
}
void JFJochStateMachine::TakePedestalInternalG0(std::unique_lock<std::mutex> &ul) {
DiffractionExperiment local_experiment(experiment);
if (local_experiment.IsFixedGainG1())
local_experiment.Mode(DetectorMode::PedestalG1);
else
local_experiment.Mode(DetectorMode::PedestalG0);
if (local_experiment.GetStorageCellNumber() == 1)
local_experiment.StorageCellStart(15);
else
local_experiment.StorageCellStart(0);
if (cancel_sequence) {
state = JFJochState::Inactive;
return;
}
if (local_experiment.GetPedestalG0Frames() == 0) {
state = JFJochState::Idle;
return;
}
state = JFJochState::Pedestal;
services.ConfigureDetector(local_experiment);
services.Start(local_experiment, *calibration);
if (!experiment.IsPulsedSource())
services.Trigger();
ul.unlock();
// Allow to cancel/abort during the pedestal data collection
// Must ensure that while state is Pedestal, nothing can take lock for longer time, to avoid deadlock
auto pedestal_output = services.Stop();
ul.lock();
SetFullMeasurementOutput(pedestal_output);
if (ImportPedestalG0(pedestal_output.receiver_output))
state = JFJochState::Idle;
else
state = JFJochState::Inactive;
}
void JFJochStateMachine::TakePedestalInternalG1(std::unique_lock<std::mutex> &ul, int32_t storage_cell) {
DiffractionExperiment local_experiment(experiment);
local_experiment.Mode(DetectorMode::PedestalG1);
if (local_experiment.GetStorageCellNumber() == 2)
local_experiment.StorageCellStart((storage_cell + 15) % 16); // one previous
else
local_experiment.StorageCellStart(15);
if (cancel_sequence) {
state = JFJochState::Inactive;
return;
}
if (local_experiment.GetPedestalG1Frames() == 0) {
state = JFJochState::Idle;
return;
}
state = JFJochState::Pedestal;
services.ConfigureDetector(local_experiment);
services.Start(local_experiment, *calibration);
if (!experiment.IsPulsedSource())
services.Trigger();
ul.unlock();
// Allow to cancel/abort during the pedestal data collection
// Must ensure that while state is Pedestal, nothing can take lock for longer time, to avoid deadlock
auto pedestal_output = services.Stop();
ul.lock();
SetFullMeasurementOutput(pedestal_output);
if (ImportPedestalG1G2(pedestal_output.receiver_output, 1, storage_cell))
state = JFJochState::Idle;
else
state = JFJochState::Inactive;
}
void JFJochStateMachine::TakePedestalInternalG2(std::unique_lock<std::mutex> &ul, int32_t storage_cell) {
DiffractionExperiment local_experiment(experiment);
local_experiment.Mode(DetectorMode::PedestalG2);
if (local_experiment.GetStorageCellNumber() == 2)
local_experiment.StorageCellStart((storage_cell + 15) % 16); // one previous
else
local_experiment.StorageCellStart(15);
if (cancel_sequence) {
state = JFJochState::Inactive;
return;
}
if (local_experiment.GetPedestalG2Frames() == 0) {
state = JFJochState::Idle;
return;
}
state = JFJochState::Pedestal;
services.ConfigureDetector(local_experiment);
services.Start(local_experiment, *calibration);
if (!experiment.IsPulsedSource())
services.Trigger();
ul.unlock();
// Allow to cancel/abort during the pedestal data collection
// Must ensure that while state is Pedestal, nothing can take lock for longer time, to avoid deadlock
auto pedestal_output = services.Stop();
ul.lock();
SetFullMeasurementOutput(pedestal_output);
if (ImportPedestalG1G2(pedestal_output.receiver_output, 2, storage_cell))
state = JFJochState::Idle;
else
state = JFJochState::Inactive;
}
void JFJochStateMachine::Initialize() {
std::unique_lock<std::mutex> ul(m);
if (IsRunning())
throw WrongDAQStateException ("Cannot initialize during measurement");
if (detector_setup.empty())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Detector information not provided");
CheckError(); // Clear error, we don't care what was it
logger.Info("Initialize");
state = JFJochState::Busy;
ClearMeasurementStatistics();
measurement = std::async(std::launch::async, &JFJochStateMachine::InitializeThread, this, std::move(ul));
}
void JFJochStateMachine::Pedestal() {
std::unique_lock<std::mutex> ul(m);
if (state != JFJochState::Idle)
throw WrongDAQStateException ("Must be idle to take pedestal");
measurement = std::async(std::launch::async, &JFJochStateMachine::PedestalThread, this, std::move(ul));
}
void JFJochStateMachine::PedestalThread(std::unique_lock<std::mutex> ul) {
TakePedestalInternalAll(ul);
}
void JFJochStateMachine::InitializeThread(std::unique_lock<std::mutex> ul) {
try {
services.On(experiment);
} catch (...) {
state = JFJochState::Error;
throw;
}
TakePedestalInternalAll(ul);
}
void JFJochStateMachine::Trigger() {
std::unique_lock<std::mutex> ul(m);
if (state == JFJochState::Measuring)
services.Trigger();
}
void JFJochStateMachine::Start(const DatasetSettings& settings) {
std::unique_lock<std::mutex> ul(m);
if (state != JFJochState::Idle)
throw WrongDAQStateException ("Must be idle to start measurement");
if (measurement.valid())
measurement.get(); // In case measurement was running - clear thread
experiment.ImportDatasetSettings(settings);
ClearAndSetMeasurementStatistics();
cancel_sequence = false;
if (experiment.GetStorageCellNumber() == 1)
experiment.StorageCellStart(15);
else
experiment.StorageCellStart(0);
experiment.IncrementSeriesID();
try {
state = JFJochState::Busy;
services.SetSpotFindingSettings(GetSpotFindingSettings());
services.Start(experiment, *calibration);
state = JFJochState::Measuring;
measurement = std::async(std::launch::async, &JFJochStateMachine::MeasurementThread, this);
} catch (...) {
state = JFJochState::Error;
services.Cancel();
throw;
}
}
void JFJochStateMachine::WaitTillMeasurementDone() {
std::unique_lock<std::mutex> ul(m);
c.wait(ul, [&] { return !IsRunning(); });
}
void JFJochStateMachine::MeasurementThread() {
try {
auto tmp_output = services.Stop();
SetFullMeasurementOutput(tmp_output);
{
std::unique_lock<std::mutex> ul(m);
state = JFJochState::Idle;
}
} catch (...) {
std::unique_lock<std::mutex> ul(m);
state = JFJochState::Error;
}
c.notify_all();
}
void JFJochStateMachine::Cancel() {
// This is inconsistency in naming - need to solve later
std::unique_lock<std::mutex> ul(m);
if ((state == JFJochState::Pedestal) || (state == JFJochState::Measuring)) {
services.Cancel();
cancel_sequence = true;
}
}
void JFJochStateMachine::DebugOnly_SetState(JFJochState in_state) {
std::unique_lock<std::mutex> ul(m);
state = in_state;
}
void JFJochStateMachine::Deactivate() {
std::unique_lock<std::mutex> ul(m);
try {
if (measurement.valid())
measurement.get();
services.Off();
state = JFJochState::Inactive;
} catch (...) {
state = JFJochState::Error;
throw;
}
}
JFJochStateMachine::~JFJochStateMachine() {
try {
if (measurement.valid())
measurement.get();
} catch (...) {}
}
void JFJochStateMachine::SetFullMeasurementOutput(const JFJochServicesOutput &output) {
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
MeasurementStatistics tmp{}; // reset last measurement statistics
tmp.file_prefix = experiment.GetFilePrefix();
tmp.detector_width = experiment.GetXPixelsNum();
tmp.detector_height = experiment.GetYPixelsNum();
tmp.detector_pixel_depth = experiment.GetPixelDepth();
tmp.images_expected = experiment.GetImageNum();
tmp.compression_ratio = output.receiver_output.status.compressed_ratio;
tmp.collection_efficiency = output.receiver_output.efficiency;
tmp.images_collected = output.receiver_output.status.images_collected;
tmp.images_sent = output.receiver_output.status.images_sent;
tmp.cancelled = output.receiver_output.status.cancelled;
tmp.max_image_number_sent = output.receiver_output.status.max_image_number_sent;
tmp.max_receive_delay = output.receiver_output.status.max_receive_delay;
tmp.indexing_rate = output.receiver_output.status.indexing_rate;
tmp.bkg_estimate = output.receiver_output.status.bkg_estimate;
measurement_statistics = tmp;
}
void JFJochStateMachine::ClearAndSetMeasurementStatistics() {
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
MeasurementStatistics tmp{};
tmp.file_prefix = experiment.GetFilePrefix();
tmp.detector_height = experiment.GetXPixelsNum();
tmp.detector_width = experiment.GetYPixelsNum();
tmp.detector_pixel_depth = experiment.GetPixelDepth();
tmp.images_expected = experiment.GetImageNum();
measurement_statistics = tmp;
}
void JFJochStateMachine::ClearMeasurementStatistics() {
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
measurement_statistics.reset();
}
std::optional<MeasurementStatistics> JFJochStateMachine::GetMeasurementStatistics() const {
auto rcv_status = services.GetReceiverStatus();
if (rcv_status) {
MeasurementStatistics tmp;
tmp.file_prefix = experiment.GetFilePrefix();
tmp.detector_width = experiment.GetXPixelsNum();
tmp.detector_height = experiment.GetYPixelsNum();
tmp.detector_pixel_depth = experiment.GetPixelDepth();
tmp.images_expected = experiment.GetImageNum();
tmp.compression_ratio = rcv_status->compressed_ratio;
tmp.collection_efficiency = -1.0;
tmp.images_collected = rcv_status->images_collected;
tmp.images_sent = rcv_status->images_sent;
tmp.cancelled = rcv_status->cancelled;
tmp.max_image_number_sent = rcv_status->max_image_number_sent;
tmp.max_receive_delay = rcv_status->max_receive_delay;
tmp.indexing_rate = rcv_status->indexing_rate;
tmp.bkg_estimate = rcv_status->bkg_estimate;
return tmp;
} else {
std::unique_lock<std::mutex> ul(last_receiver_output_mutex);
return measurement_statistics;
}
}
std::vector<JFCalibrationModuleStatistics> JFJochStateMachine::GetCalibrationStatistics() const {
std::unique_lock<std::mutex> ul(calibration_statistics_mutex);
return calibration_statistics;
}
void JFJochStateMachine::SetCalibrationStatistics(const std::vector<JFCalibrationModuleStatistics> &input) {
std::unique_lock<std::mutex> ul(calibration_statistics_mutex);
calibration_statistics = input;
}
DetectorSettings JFJochStateMachine::GetDetectorSettings() const {
std::unique_lock<std::mutex> ul(m);
DetectorSettings ret;
ret.frame_time_us = experiment.GetFrameTime().count();
ret.count_time_us = experiment.GetFrameCountTime().count();
ret.collect_raw_data = (experiment.GetDetectorMode() != DetectorMode::Conversion);
ret.use_internal_packet_generator = experiment.IsUsingInternalPacketGen();
ret.storage_cell_count = experiment.GetStorageCellNumber();
ret.pedestal_g0_frames = experiment.GetPedestalG0Frames();
ret.pedestal_g1_frames = experiment.GetPedestalG1Frames();
ret.pedestal_g2_frames = experiment.GetPedestalG2Frames();
ret.storage_cell_delay_ns = experiment.GetStorageCellDelay().count();
ret.detector_delay_ns = experiment.GetDetectorDelay().count();
ret.fixed_gain_g1 = experiment.IsFixedGainG1();
ret.use_gain_hg0 = experiment.IsUsingGainHG0();
ret.internal_packet_generator_images = experiment.GetInternalPacketGeneratorImages();
return ret;
}
void JFJochStateMachine::LoadDetectorSettings(const DetectorSettings &settings) {
std::unique_lock<std::mutex> ul(m);
switch (state) {
case JFJochState::Inactive:
case JFJochState::Error:
ApplyDetectorSettings(experiment, settings);
break;
case JFJochState::Idle:
ApplyDetectorSettings(experiment, settings);
measurement = std::async(std::launch::async, &JFJochStateMachine::PedestalThread, this, std::move(ul));
break;
case JFJochState::Measuring:
case JFJochState::Busy:
case JFJochState::Pedestal:
throw WrongDAQStateException ("Cannot change detector settings during data collection");
}
}
DiffractionExperiment &JFJochStateMachine::NotThreadSafe_Experiment() {
return experiment;
}
BrokerStatus JFJochStateMachine::GetStatus() const {
BrokerStatus ret{};
ret.broker_state = state;
try {
auto rcv_status = services.GetReceiverStatus();
if (rcv_status) {
ret.progress = rcv_status.value().progress;
ret.indexing_rate = rcv_status.value().indexing_rate;
}
} catch (JFJochException &e) {} // ignore exception in getting receiver status (don't really care, e.g. if receiver is down)
return ret;
}
Plot JFJochStateMachine::GetPlots(const PlotRequest &request) const {
return services.GetPlots(request);
}
RadialIntegrationProfiles JFJochStateMachine::GetRadialIntegrationProfiles() const {
return services.GetRadialIntegrationProfiles();
}
void JFJochStateMachine::SetSpotFindingSettings(const SpotFindingSettings &settings) {
std::unique_lock<std::mutex> ul(data_processing_settings_mutex);
DiffractionExperiment::CheckDataProcessingSettings(settings);
data_processing_settings = settings;
services.SetSpotFindingSettings(settings);
}
SpotFindingSettings JFJochStateMachine::GetSpotFindingSettings() const {
std::unique_lock<std::mutex> ul(data_processing_settings_mutex);
return data_processing_settings;
}
JFJochState JFJochStateMachine::GetState() const {
return state;
}
void JFJochStateMachine::AddDetectorSetup(const DetectorSetup &setup) {
std::unique_lock<std::mutex> ul(m);
if (detector_setup.empty()) {
experiment.Detector(setup);
gain_calibration = setup.GetGainCalibration();
current_detector_setup = 0;
}
detector_setup.emplace_back(setup);
}
DetectorList JFJochStateMachine::GetDetectorsList() const {
std::unique_lock<std::mutex> ul(m);
DetectorList ret;
for (const auto & i : detector_setup) {
DetectorListElement tmp;
tmp.description = i.GetDescription();
tmp.nmodules = i.GetModulesNum();
tmp.width = i.GetGeometry().GetWidth();
tmp.height = i.GetGeometry().GetHeight();
ret.detector.emplace_back(std::move(tmp));
}
ret.current_id = current_detector_setup;
return ret;
}
std::optional<DetectorStatus> JFJochStateMachine::GetDetectorStatus() const {
return services.GetDetectorStatus();
}
void JFJochStateMachine::SelectDetector(int64_t id) {
std::unique_lock<std::mutex> ul(m);
if ((id < 0) || (id >= detector_setup.size()))
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Detector doesn't exist");
if (IsRunning())
throw WrongDAQStateException ("Cannot change detector during data collection");
try {
experiment.Detector(detector_setup[id]);
gain_calibration = detector_setup[id].GetGainCalibration();
state = JFJochState::Inactive;
current_detector_setup = id;
} catch (JFJochException &e) {
logger.ErrorException(e);
state = JFJochState::Inactive;
}
}
void JFJochStateMachine::SetRadialIntegrationSettings(const RadialIntegrationSettings &settings) {
std::unique_lock<std::mutex> ul(m);
if (IsRunning())
throw WrongDAQStateException ("Cannot change radial integration settings during data collection");
ApplyRadialIntegrationSettings(experiment, settings);
}
RadialIntegrationSettings JFJochStateMachine::GetRadialIntegrationSettings() const {
RadialIntegrationSettings ret;
if (experiment.GetApplyPolarizationCorr())
ret.polarization_factor = experiment.GetPolarizationFactor();
ret.solid_angle_correction = experiment.GetApplySolidAngleCorr();
ret.q_spacing = experiment.GetQSpacingForAzimInt_recipA();
ret.low_q_recipA = experiment.GetLowQForAzimInt_recipA();
ret.high_q_recipA = experiment.GetHighQForAzimInt_recipA();
return ret;
}
bool JFJochStateMachine::IsRunning() const {
switch (state) {
case JFJochState::Inactive:
case JFJochState::Error:
case JFJochState::Idle:
return false;
case JFJochState::Measuring:
case JFJochState::Busy:
case JFJochState::Pedestal:
return true;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "State unknown");
}
}
JFJochState JFJochStateMachine::WaitTillMeasurementDone(std::chrono::milliseconds timeout) {
std::unique_lock<std::mutex> ul(m);
c.wait_for(ul, timeout, [&] { return !IsRunning(); });
return state;
}
std::optional<std::string> JFJochStateMachine::CheckError() {
try {
if (measurement.valid())
measurement.get();
} catch (JFJochException &e) {
return e.what();
}
return {};
}
void JFJochStateMachine::SetPreviewJPEGSettings(const PreviewJPEGSettings &settings) {
preview_jpeg_settings = settings;
}
PreviewJPEGSettings JFJochStateMachine::GetPreviewJPEGSettings() const {
return preview_jpeg_settings;
}
std::string JFJochStateMachine::GetPreviewJPEG() const {
return services.GetPreviewJPEG(preview_jpeg_settings);
}
std::string JFJochStateMachine::GetPreviewTIFF(bool calibration) const {
return services.GetPreviewTIFF(calibration);
}
void JFJochStateMachine::LoadInternalGeneratorImage(const void *data, size_t size, uint64_t image_number) {
std::unique_lock<std::mutex> ul(m);
if (state != JFJochState::Idle)
throw WrongDAQStateException ("Can change internal generator image only when detector in Idle state");
if ((size != experiment.GetPixelsNum() * sizeof(uint16_t))
&& (size != experiment.GetModulesNum() * RAW_MODULE_SIZE * sizeof(uint16_t)))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image size doesn't match current detector");
if (image_number >= experiment.GetInternalPacketGeneratorImages())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Image for internal generator out of bounds");
std::vector<uint16_t> image(size / sizeof(uint16_t));
memcpy(image.data(), data, size);
services.LoadInternalGeneratorImage(experiment, image, image_number);
}