// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include "JFJochStateMachine.h" #include "../preview/JFJochTIFF.h" #include "pistache/net.h" #include "../common/CUDAWrapper.h" JFJochStateMachine::JFJochStateMachine(const DiffractionExperiment& in_experiment, JFJochServices &in_services, Logger &in_logger) : experiment(in_experiment), logger(in_logger), services(in_services), pixel_mask(experiment), current_detector_setup(0), data_processing_settings(DiffractionExperiment::DefaultDataProcessingSettings()), pixel_mask_statistics({0, 0, 0}), gpu_count(get_gpu_count()) { indexing_possible = (get_gpu_count() >= 0); if (!indexing_possible) data_processing_settings.indexing = false; SupressTIFFErrors(); } 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 &ul) { if (experiment.GetDetectorSetup().GetDetectorType() != DetectorType::JUNGFRAU) { try { calibration.reset(); logger.Info("EIGER configuration"); services.ConfigureDetector(experiment); logger.Info(" ... done "); SetState(JFJochState::Idle, "Detector configured", BrokerStatus::MessageSeverity::Success); return; } catch (const std::exception &e) { logger.Error("Configuration error {}", e.what()); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; } } calibration = std::make_unique(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); pixel_mask.LoadDetectorBadPixelMask(experiment, calibration.get()); UpdatePixelMaskStatistics(pixel_mask.GetStatistics()); SetState(JFJochState::Idle, "Pedestal sequence done", BrokerStatus::MessageSeverity::Success); } catch (const std::exception &e) { logger.Error("Pedestal sequence error {}", e.what()); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; } logger.Info("Pedestal sequence done"); } void JFJochStateMachine::TakePedestalInternalG0(std::unique_lock &ul) { DiffractionExperiment local_experiment(experiment); std::string message; if (local_experiment.IsFixedGainG1()) { local_experiment.Mode(DetectorMode::PedestalG1); message = "Pedestal G1"; } else { local_experiment.Mode(DetectorMode::PedestalG0); message = "Pedestal G0"; } if (local_experiment.GetStorageCellNumber() == 1) local_experiment.StorageCellStart(15); else local_experiment.StorageCellStart(0); if (cancel_sequence) { SetState(JFJochState::Inactive, "Pedestal sequence cancelled", BrokerStatus::MessageSeverity::Warning); return; } if (local_experiment.GetPedestalG0Frames() == 0) return; SetState(JFJochState::Pedestal, message, BrokerStatus::MessageSeverity::Info); services.ConfigureDetector(local_experiment); services.Start(local_experiment, pixel_mask, *calibration); 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(); if (ImportPedestalG0(pedestal_output.receiver_output)) SetState(JFJochState::Idle); else SetState(JFJochState::Error, "Pedestal not collected properly", BrokerStatus::MessageSeverity::Error); } void JFJochStateMachine::TakePedestalInternalG1(std::unique_lock &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) { SetState(JFJochState::Inactive, "Pedestal sequence cancelled", BrokerStatus::MessageSeverity::Warning); return; } if (local_experiment.GetPedestalG1Frames() == 0) return; SetState(JFJochState::Pedestal, "Pedestal G1 SC" + std::to_string(storage_cell), BrokerStatus::MessageSeverity::Info); services.ConfigureDetector(local_experiment); services.Start(local_experiment, pixel_mask, *calibration); 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(); if (!ImportPedestalG1G2(pedestal_output.receiver_output, 1, storage_cell)) SetState(JFJochState::Error, "Pedestal not collected properly", BrokerStatus::MessageSeverity::Error); } void JFJochStateMachine::TakePedestalInternalG2(std::unique_lock &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) { SetState(JFJochState::Inactive, "Pedestal sequence cancelled", BrokerStatus::MessageSeverity::Warning); return; } if (local_experiment.GetPedestalG2Frames() == 0) return; SetState(JFJochState::Pedestal, "Pedestal G2 SC" + std::to_string(storage_cell), BrokerStatus::MessageSeverity::Info); services.ConfigureDetector(local_experiment); services.Start(local_experiment, pixel_mask, *calibration); 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(); if (!ImportPedestalG1G2(pedestal_output.receiver_output, 2, storage_cell)) SetState(JFJochState::Error, "Pedestal not collected properly", BrokerStatus::MessageSeverity::Error); } void JFJochStateMachine::Initialize() { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot initialize during measurement"); if (detector_setup.empty()) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Detector information not provided"); ResetError(); // Clear error, we don't care what was it logger.Info("Initialize"); try { services.SetupIndexing(experiment.GetIndexingSettings()); } catch (const JFJochException &e) { SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; } SetState(JFJochState::Busy, "Configuring detector", BrokerStatus::MessageSeverity::Info); scan_result = {}; // Clear scan result measurement = std::async(std::launch::async, &JFJochStateMachine::InitializeThread, this, std::move(ul)); } void JFJochStateMachine::Pedestal() { std::unique_lock 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 ul) { TakePedestalInternalAll(ul); } void JFJochStateMachine::InitializeThread(std::unique_lock ul) { try { // services.On can potentially take a lot of time, so better to unlock main mutex // Since On might modify the experiment (reads DECTRIS configuration), one has to have a local copy for unlocked part DiffractionExperiment local_experiment(experiment); if (state != JFJochState::Busy) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "State must be busy for safe operation"); ul.unlock(); services.On(local_experiment); ul.lock(); experiment = local_experiment; detector_setup[current_detector_setup] = experiment.GetDetectorSetup(); pixel_mask = PixelMask(experiment); services.LoadDetectorPixelMask(pixel_mask); UpdatePixelMaskStatistics(pixel_mask.GetStatistics()); } catch (const std::exception &e) { logger.Error("Initialize error {}", e.what()); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; } TakePedestalInternalAll(ul); } void JFJochStateMachine::Trigger() { services.Trigger(); } void JFJochStateMachine::Start(const DatasetSettings &settings) { std::unique_lock 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); cancel_sequence = false; if (experiment.GetStorageCellNumber() == 1) experiment.StorageCellStart(15); else experiment.StorageCellStart(0); experiment.IncrementRunNumber(); try { SetState(JFJochState::Busy, "Preparing measurement", BrokerStatus::MessageSeverity::Info); services.SetSpotFindingSettings(GetSpotFindingSettings()); services.Start(experiment, pixel_mask, *calibration); SetState(JFJochState::Measuring, "Measuring ...", BrokerStatus::MessageSeverity::Info); measurement = std::async(std::launch::async, &JFJochStateMachine::MeasurementThread, this); } catch (const std::exception &e) { SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); services.Cancel(); throw; } } void JFJochStateMachine::UpdatePixelMaskStatistics(const PixelMaskStatistics &input) { std::unique_lock ul(pixel_mask_statistics_mutex); pixel_mask_statistics = input; } PixelMaskStatistics JFJochStateMachine::GetPixelMaskStatistics() const { std::unique_lock ul(pixel_mask_statistics_mutex); return pixel_mask_statistics; } void JFJochStateMachine::MeasurementThread() { try { auto tmp_output = services.Stop(); { std::unique_lock ul(m); scan_result = tmp_output.receiver_output.scan_result; if (tmp_output.receiver_output.writer_queue_full_warning) SetState(JFJochState::Idle, "Stream receiver (writer or downstream analysis) cannot cope with data; reduce frame rate", BrokerStatus::MessageSeverity::Warning); else if (tmp_output.receiver_output.status.cancelled) SetState(JFJochState::Idle, "Data collection cancelled", BrokerStatus::MessageSeverity::Info); else if (tmp_output.receiver_output.efficiency != 1.0) SetState(JFJochState::Idle, "Missing packets in data collection; reduce frame rate", BrokerStatus::MessageSeverity::Error); else if (!tmp_output.receiver_output.writer_err.empty()) SetState(JFJochState::Idle, tmp_output.receiver_output.writer_err, BrokerStatus::MessageSeverity::Error); else SetState(JFJochState::Idle, "Data collection without problems", BrokerStatus::MessageSeverity::Success); } } catch (const std::exception &e) { std::unique_lock ul(m); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); } c.notify_all(); } void JFJochStateMachine::Cancel() { // This is inconsistency in naming - need to solve later std::unique_lock ul(m); if ((state == JFJochState::Pedestal) || (state == JFJochState::Measuring)) { services.Cancel(); cancel_sequence = true; } } void JFJochStateMachine::DebugOnly_SetState(JFJochState in_state, const std::optional &message, BrokerStatus::MessageSeverity message_severity) { std::unique_lock ul(m); SetState(in_state, message, message_severity); } void JFJochStateMachine::Deactivate() { std::unique_lock ul(m); try { if (measurement.valid()) measurement.get(); services.Off(); SetState(JFJochState::Inactive, "Detector safe to turn off", BrokerStatus::MessageSeverity::Info); } catch (const std::exception &e) { SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; } } JFJochStateMachine::~JFJochStateMachine() { ResetError(); } std::optional JFJochStateMachine::GetMeasurementStatistics() const { MeasurementStatistics tmp{}; tmp.file_prefix = experiment.GetFilePrefix(); tmp.run_number = experiment.GetRunNumber(); tmp.experiment_group = experiment.GetExperimentGroup(); tmp.detector_width = experiment.GetXPixelsNum(); tmp.detector_height = experiment.GetYPixelsNum(); tmp.detector_pixel_depth = experiment.GetByteDepthImage(); tmp.images_expected = experiment.GetImageNum(); tmp.unit_cell = experiment.GetUnitCellString(); auto rcv_status = services.GetReceiverStatus(); if (rcv_status) { tmp.compression_ratio = rcv_status->compressed_ratio; tmp.images_collected = rcv_status->images_collected; tmp.images_sent = rcv_status->images_sent; tmp.images_skipped = rcv_status->images_skipped; 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; tmp.collection_efficiency = rcv_status->efficiency; tmp.error_pixels = rcv_status->error_pixels; tmp.saturated_pixels = rcv_status->saturated_pixels; tmp.roi_beam_sum = rcv_status->roi_beam_sum; tmp.roi_beam_npixel = rcv_status->roi_beam_npixel; } return tmp; } std::vector JFJochStateMachine::GetCalibrationStatistics() const { std::unique_lock ul(calibration_statistics_mutex); return calibration_statistics; } void JFJochStateMachine::SetCalibrationStatistics(const std::vector &input) { std::unique_lock ul(calibration_statistics_mutex); calibration_statistics = input; } DetectorSettings JFJochStateMachine::GetDetectorSettings() const { std::unique_lock ul(experiment_detector_settings_mutex); return experiment.GetDetectorSettings(); } bool JFJochStateMachine::ImportDetectorSettings(const DetectorSettings &input) { std::unique_lock ul(experiment_detector_settings_mutex); // For JUNGFRAU detector, if detector settings changes key parameters // need to recalibrate the detector bool recalib = input.NeedsJUNGFRAURecalibration(experiment.GetDetectorSettings()) && experiment.GetDetectorType() == DetectorType::JUNGFRAU; experiment.ImportDetectorSettings(input); return recalib; } void JFJochStateMachine::LoadDetectorSettings(const DetectorSettings &settings) { std::unique_lock ul(m); switch (state) { case JFJochState::Inactive: case JFJochState::Error: ImportDetectorSettings(settings); break; case JFJochState::Idle: if (ImportDetectorSettings(settings)) { SetState(JFJochState::Busy, "Loading settings", BrokerStatus::MessageSeverity::Info); measurement = std::async(std::launch::async, &JFJochStateMachine::PedestalThread, this, std::move(ul)); } else { try { SetState(JFJochState::Busy, "Configure detector", BrokerStatus::MessageSeverity::Info); services.ConfigureDetector(experiment); SetState(JFJochState::Idle, "Detector configured", BrokerStatus::MessageSeverity::Info); } catch (const std::exception &e) { logger.Error("Detector configuration error {}", e.what()); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); } } break; case JFJochState::Measuring: case JFJochState::Busy: case JFJochState::Pedestal: throw WrongDAQStateException("Cannot change detector settings during data collection"); } } DiffractionExperiment JFJochStateMachine::Experiment() { return experiment; } BrokerStatus JFJochStateMachine::GetStatus() const { std::unique_lock ul(broker_status_mutex); BrokerStatus ret = broker_status; ret.progress = services.GetReceiverProgress(); ret.gpu_count = gpu_count; return ret; } void JFJochStateMachine::SetState(JFJochState curr_state, const std::optional &message, BrokerStatus::MessageSeverity message_severity) { std::unique_lock ul(broker_status_mutex); state = curr_state; broker_status = BrokerStatus{ .state = curr_state, .message = message, .message_severity = message_severity }; } MultiLinePlot JFJochStateMachine::GetPlots(const PlotRequest &request) const { return services.GetPlots(request); } void JFJochStateMachine::SetSpotFindingSettings(const SpotFindingSettings &settings) { std::unique_lock ul(data_processing_settings_mutex); DiffractionExperiment::CheckDataProcessingSettings(settings); data_processing_settings = settings; // If there is no capability to use the features, make sure these are disabled if (!indexing_possible) data_processing_settings.indexing = false; services.SetSpotFindingSettings(data_processing_settings); } SpotFindingSettings JFJochStateMachine::GetSpotFindingSettings() const { std::unique_lock ul(data_processing_settings_mutex); return data_processing_settings; } void JFJochStateMachine::AddDetectorSetup(const DetectorSetup &setup) { // Not thread safe, only during setup if (detector_setup.empty()) { experiment.Detector(setup); UpdateROIDefinition(); gain_calibration = setup.GetGainCalibration(); current_detector_setup = 0; pixel_mask = PixelMask(experiment); } detector_setup.emplace_back(setup); } DetectorList JFJochStateMachine::GetDetectorsList() const { DetectorList ret; for (const auto &i: detector_setup) { DetectorListElement tmp; tmp.description = i.GetDescription(); tmp.nmodules = i.GetModulesNum(); tmp.width = i.GetGeometry().GetWidth(true); tmp.height = i.GetGeometry().GetHeight(true); tmp.serial_number = i.GetSerialNumber(); tmp.base_ipv4_addr = i.GetBaseIPv4Addr(); tmp.udp_interface_count = i.GetUDPInterfaceCount(); tmp.min_frame_time = i.GetMinFrameTime(); tmp.min_count_time = i.GetMinCountTime(); tmp.readout_time = i.GetReadOutTime(); tmp.detector_type = i.GetDetectorType(); tmp.pixel_size_mm = i.GetPixelSize_mm(); ret.detector.emplace_back(std::move(tmp)); } ret.current_id = current_detector_setup; return ret; } std::optional JFJochStateMachine::GetDetectorStatus() const { return services.GetDetectorStatus(); } void JFJochStateMachine::SelectDetector(int64_t id) { std::unique_lock 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"); // Do nothing if this is the same detector as currently used if (id == current_detector_setup) return; // Try to deactivate current detector (if actually running) if (state != JFJochState::Inactive) { try { SetState(JFJochState::Busy, "Deactivating existing detector"); ul.unlock(); services.Off(); ul.lock(); } catch (const std::exception &e) { logger.ErrorException(e); logger.Warning("Cannot turn off existing detector - proceeding anyway"); } } try { experiment.Detector(detector_setup[id]); UpdateROIDefinition(); gain_calibration = detector_setup[id].GetGainCalibration(); pixel_mask = PixelMask(experiment); SetState(JFJochState::Inactive, detector_setup[id].GetDescription() + " selected; please initialize"); current_detector_setup = id; } catch (const JFJochException &e) { logger.ErrorException(e); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; // re-throw the exception, so it is populated to caller } } void JFJochStateMachine::SetRadialIntegrationSettings(const AzimuthalIntegrationSettings &settings) { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change radial integration settings during data collection"); { std::unique_lock ul2(experiment_azimuthal_integration_settings_mutex); experiment.ImportAzimuthalIntegrationSettings(settings); } } AzimuthalIntegrationSettings JFJochStateMachine::GetRadialIntegrationSettings() const { std::unique_lock ul(experiment_azimuthal_integration_settings_mutex); return experiment.GetAzimuthalIntegrationSettings(); } 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"); } } BrokerStatus JFJochStateMachine::WaitTillMeasurementDone() { std::unique_lock ul(m); c.wait(ul, [&] { return !IsRunning(); }); return GetStatus(); } BrokerStatus JFJochStateMachine::WaitTillMeasurementDone(std::chrono::milliseconds timeout) { std::unique_lock ul(m); c.wait_for(ul, timeout, [&] { return !IsRunning(); }); return GetStatus(); } void JFJochStateMachine::ResetError() noexcept { try { if (measurement.valid()) measurement.get(); } catch (...) { } } std::string JFJochStateMachine::GetPreviewJPEG(const PreviewImageSettings &settings, int64_t image_number) const { return services.GetPreviewJPEG(settings, image_number); } std::string JFJochStateMachine::GetPreviewTIFF(int64_t image_number) const { return services.GetPreviewTIFF(image_number); } std::string JFJochStateMachine::GetPedestalTIFF(size_t gain_level, size_t sc) const { std::unique_lock ul(m); if (state != JFJochState::Idle) throw WrongDAQStateException("Pedestal can be only retrieved in Idle state"); if ((experiment.GetDetectorSetup().GetDetectorType() == DetectorType::JUNGFRAU) && calibration) { auto tmp = calibration->GetPedestal(gain_level, sc); CompressedImage image(tmp, RAW_MODULE_COLS, RAW_MODULE_LINES * experiment.GetModulesNum()); return WriteTIFFToString(image); } else return {}; } void JFJochStateMachine::LoadInternalGeneratorImage(const void *data, size_t size, uint64_t image_number) { std::unique_lock 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 image(size / sizeof(uint16_t)); memcpy(image.data(), data, size); services.LoadInternalGeneratorImage(experiment, image, image_number); } void JFJochStateMachine::LoadInternalGeneratorImageTIFF(const std::string &s, uint64_t image_number) { std::unique_lock ul(m); if (state != JFJochState::Idle) throw WrongDAQStateException("Can change internal generator image only when detector in Idle state"); uint32_t cols, lines; auto v = ReadTIFFFromString16(s, cols, lines); if (((cols == experiment.GetXPixelsNum()) && (lines == experiment.GetYPixelsNum())) || ((cols == RAW_MODULE_SIZE) && (lines == RAW_MODULE_LINES * experiment.GetModulesNum()))) services.LoadInternalGeneratorImage(experiment, v, image_number); else throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Image size doesn't match current detector"); } void JFJochStateMachine::UpdateROIDefinition() { std::unique_lock ul(roi_mutex); roi = experiment.ROI().GetROIDefinition(); } void JFJochStateMachine::SetROIDefinition(const ROIDefinition &input) { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("ROI can be modified only when detector is not running"); experiment.ROI().SetROI(input); UpdateROIDefinition(); } ROIDefinition JFJochStateMachine::GetROIDefintion() const { std::unique_lock ul(roi_mutex); return roi; } std::vector JFJochStateMachine::GetXFELPulseID() const { std::vector ret; services.GetXFELPulseID(ret); return ret; } std::vector JFJochStateMachine::GetXFELEventCode() const { std::vector ret; services.GetXFELEventCode(ret); return ret; } std::string JFJochStateMachine::GetFullPixelMaskTIFF() const { std::unique_lock ul(m); if (state == JFJochState::Inactive) return {}; std::vector v = pixel_mask.GetMask(experiment); CompressedImage mask_image(v, experiment.GetXPixelsNum(), experiment.GetYPixelsNum()); return WriteTIFFToString(mask_image); } std::string JFJochStateMachine::GetUserPixelMaskTIFF() const { std::unique_lock ul(m); if (state == JFJochState::Inactive) return {}; std::vector v = pixel_mask.GetUserMask(experiment); CompressedImage mask_image(v, experiment.GetXPixelsNum(), experiment.GetYPixelsNum()); return WriteTIFFToString(mask_image); } std::vector JFJochStateMachine::GetFullPixelMask() const { std::unique_lock ul(m); if (state == JFJochState::Inactive) return {}; return pixel_mask.GetMask(experiment); } std::vector JFJochStateMachine::GetUserPixelMask() const { std::unique_lock ul(m); if (state == JFJochState::Inactive) return {}; return pixel_mask.GetUserMask(experiment); } void JFJochStateMachine::SetUserPixelMask(const std::vector &v) { std::unique_lock ul(m); if (state != JFJochState::Idle) throw WrongDAQStateException("User mask can be only modified in Idle state"); try { pixel_mask.LoadUserMask(experiment, v); UpdatePixelMaskStatistics(pixel_mask.GetStatistics()); } catch (const JFJochException &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Problem handling user mask " + std::string(e.what())); } } InstrumentMetadata JFJochStateMachine::GetInstrumentMetadata() const { std::unique_lock ul(experiment_instrument_metadata_mutex); return experiment.GetInstrumentMetadata(); } void JFJochStateMachine::LoadInstrumentMetadata(const InstrumentMetadata &settings) { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change instrument metadata during data collection"); { std::unique_lock ul2(experiment_instrument_metadata_mutex); experiment.ImportInstrumentMetadata(settings); } } ImageFormatSettings JFJochStateMachine::GetImageFormatSettings() const { std::unique_lock ul(experiment_image_format_settings_mutex); return experiment.GetImageFormatSettings(); } void JFJochStateMachine::LoadImageFormatSettings(const ImageFormatSettings &settings) { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change image format settings during data collection"); bool recalc_mask = (experiment.GetPedestalG0RMSLimit() != settings.GetPedestalG0RMSLimit()); { std::unique_lock ul2(experiment_image_format_settings_mutex); experiment.ImportImageFormatSettings(settings); } if (recalc_mask) pixel_mask.LoadDetectorBadPixelMask(experiment, calibration.get()); else pixel_mask.CalcEdgePixels(experiment); UpdatePixelMaskStatistics(pixel_mask.GetStatistics()); } void JFJochStateMachine::RawImageFormatSettings() { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change instrument metadata during data collection"); experiment.Raw(); } void JFJochStateMachine::ConvImageFormatSettings() { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change instrument metadata during data collection"); experiment.Conversion(); } std::vector JFJochStateMachine::GetDeviceStatus() const { return services.GetDeviceStatus(); } void JFJochStateMachine::SetPreviewSocketSettings(const ZMQPreviewSettings &input) { services.SetPreviewSocketSettings(input); } ZMQPreviewSettings JFJochStateMachine::GetPreviewSocketSettings() { return services.GetPreviewSocketSettings(); } void JFJochStateMachine::SetMetadataSocketSettings(const ZMQMetadataSettings &input) { services.SetMetadataSocketSettings(input); } ZMQMetadataSettings JFJochStateMachine::GetMetadataSocketSettings() { return services.GetMetadataSocketSettings(); } void JFJochStateMachine::GetStartMessageFromBuffer(std::vector &v) { return services.GetStartMessageFromBuffer(v); } void JFJochStateMachine::GetImageFromBuffer(std::vector &v, int64_t image_number) { services.GetImageFromBuffer(v, image_number); } ImageBufferStatus JFJochStateMachine::GetImageBufferStatus() const { return services.GetImageBufferStatus(); } void JFJochStateMachine::ClearImageBuffer() const { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot clear image buffer during data collection"); services.ClearImageBuffer(); } FileWriterSettings JFJochStateMachine::GetFileWriterSettings() const { std::unique_lock ul(experiment_file_writer_settings_mutex); return experiment.GetFileWriterSettings(); } void JFJochStateMachine::LoadFileWriterSettings(const FileWriterSettings &settings) { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change instrument metadata during data collection"); { std::unique_lock ul2(experiment_file_writer_settings_mutex); experiment.ImportFileWriterSettings(settings); } } IndexingSettings JFJochStateMachine::GetIndexingSettings() const { std::unique_lock ul(experiment_indexing_settings_mutex); return experiment.GetIndexingSettings(); } void JFJochStateMachine::SetIndexingSettings(const IndexingSettings &input) { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot change instrument metadata during data collection"); { std::unique_lock ul2(experiment_indexing_settings_mutex); experiment.ImportIndexingSettings(input); try { services.SetupIndexing(input); } catch (const JFJochException &e) { logger.ErrorException(e); SetState(JFJochState::Error, e.what(), BrokerStatus::MessageSeverity::Error); throw; } } } std::optional JFJochStateMachine::GetScanResult() const { std::unique_lock ul(m); if (IsRunning()) throw WrongDAQStateException("Cannot check scan result, when running"); return scan_result; }