// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "JFJochReceiver.h" #include "../common/CUDAWrapper.h" #include "../common/time_utc.h" #include "JFJochCompressor.h" JFJochReceiver::JFJochReceiver(const DiffractionExperiment &in_experiment, ImageBuffer &in_image_buffer, ImagePusher &in_image_pusher, PreviewImage &in_preview_image, JFJochReceiverCurrentStatus &in_current_status, JFJochReceiverPlots &in_plots, const SpotFindingSettings &spot_finding_settings, Logger &logger, const NUMAHWPolicy &in_numa_policy, const PixelMask &in_pixel_mask, ZMQPreviewSocket *in_zmq_preview_socket, ZMQMetadataSocket *in_zmq_metadata_socket, IndexerThreadPool *indexing_thread_pool) : experiment(in_experiment), image_buffer(in_image_buffer), image_pusher(in_image_pusher), preview_image(in_preview_image), current_status(in_current_status), plots(in_plots), spot_finding_settings(spot_finding_settings), logger(logger), numa_policy(in_numa_policy), zmq_preview_socket(in_zmq_preview_socket), zmq_metadata_socket(in_zmq_metadata_socket), serialmx_filter(in_experiment), pixel_mask(in_pixel_mask), az_int_mapping(experiment, pixel_mask), scan_result(in_experiment), indexer_thread_pool(indexing_thread_pool) { image_buffer.StartMeasurement(experiment); current_status.SetProgress(0); current_status.SetEfficiency({}); current_status.SetStatus(JFJochReceiverStatus{}); // GetStatus() is virtual function and cannot be called yet! plots.Setup(experiment, az_int_mapping); push_images_to_writer = (experiment.GetImageNum() > 0) && (!experiment.GetFilePrefix().empty()); preview_image.Configure(experiment, pixel_mask); } JFJochReceiver::~JFJochReceiver() = default; SpotFindingSettings JFJochReceiver::GetSpotFindingSettings() { std::unique_lock ul(spot_finding_settings_mutex); return spot_finding_settings; } void JFJochReceiver::UpdateMaxImageSent(int64_t image_number) { std::unique_lock ul(max_image_number_sent_mutex); if (image_number + 1 > max_image_number_sent) max_image_number_sent = image_number + 1; } void JFJochReceiver::UpdateMaxImageReceived(int64_t image_number) { std::unique_lock ul(max_image_number_received_mutex); if (image_number + 1 > max_image_number_received) max_image_number_received = image_number + 1; } void JFJochReceiver::UpdateMaxDelay(uint64_t delay) { std::unique_lock ul(max_delay_mutex); if (!max_delay || (delay > max_delay)) max_delay = delay; } JFJochReceiverStatus JFJochReceiver::GetStatus() const { JFJochReceiverStatus ret; ret.indexing_rate = plots.GetIndexingRate(); ret.bkg_estimate = plots.GetBkgEstimate(); if ((experiment.GetImageNum() > 0) && (compressed_size > 0)) { ret.compressed_ratio = static_cast(uncompressed_size) / static_cast(compressed_size); } ret.saturated_pixels = saturated_pixels.Read(); ret.error_pixels = error_pixels.Read(); ret.roi_beam_npixel = roi_beam_npixel.Read(); ret.roi_beam_sum = roi_beam_sum.Read(); ret.compressed_size = compressed_size; ret.max_receive_delay = max_delay; ret.max_image_number_sent = max_image_number_sent; ret.images_collected = images_collected; ret.images_sent = images_sent; ret.images_skipped = images_skipped; ret.cancelled = cancelled; ret.efficiency = GetEfficiency(); return ret; } void JFJochReceiver::SendStartMessage() { StartMessage message{}; experiment.FillMessage(message); message.arm_date = time_UTC(std::chrono::system_clock::now()); message.az_int_q_bin_count = az_int_mapping.GetQBinCount(); message.az_int_bin_to_q = az_int_mapping.GetBinToQ(); message.az_int_bin_to_two_theta = az_int_mapping.GetBinToTwoTheta(); message.az_int_phi_bin_count = az_int_mapping.GetAzimuthalBinCount(); if (az_int_mapping.GetAzimuthalBinCount() > 1) message.az_int_bin_to_phi = az_int_mapping.GetBinToPhi(); message.writer_notification_zmq_addr = image_pusher.GetWriterNotificationSocketAddress(); message.rois = experiment.ROI().ExportMetadata(); message.max_spot_count = experiment.GetMaxSpotCount(); std::vector nexus_mask; message.pixel_mask["default"] = pixel_mask.GetMask(experiment); SaveStartMessageToImageBuffer(message); if (push_images_to_writer) image_pusher.StartDataCollection(message); if (zmq_preview_socket != nullptr) zmq_preview_socket->StartDataCollection(message); if (zmq_metadata_socket != nullptr) zmq_metadata_socket->StartDataCollection(message); } void JFJochReceiver::SaveStartMessageToImageBuffer(const StartMessage &msg) { std::vector buffer(MESSAGE_SIZE_FOR_START_END); CBORStream2Serializer serializer(buffer.data(), buffer.size()); serializer.SerializeSequenceStart(msg); buffer.resize(serializer.GetBufferSize()); image_buffer.SaveStartMessage(buffer); } void JFJochReceiver::SendEndMessage() { EndMessage message{}; message.max_image_number = max_image_number_sent; message.images_collected_count = images_collected; message.images_sent_to_write_count = images_sent; message.max_receiver_delay = max_delay; message.efficiency = GetEfficiency(); message.end_date = time_UTC(std::chrono::system_clock::now()); message.run_number = experiment.GetRunNumber(); message.run_name = experiment.GetRunName(); message.bkg_estimate = plots.GetBkgEstimate(); message.indexing_rate = plots.GetIndexingRate(); message.az_int_result["dataset"] = plots.GetAzIntProfile(); for (int i = 0; i < adu_histogram_module.size(); i++) message.adu_histogram["module" + std::to_string(i)] = adu_histogram_module[i]->GetHistogram(); if (push_images_to_writer) { if (!image_pusher.EndDataCollection(message)) logger.Error("End message not sent via ZeroMQ (time-out)"); logger.Info("Disconnected from writers"); } if (zmq_metadata_socket != nullptr) zmq_metadata_socket->EndDataCollection(message); if (zmq_preview_socket != nullptr) zmq_preview_socket->EndDataCollection(message); } JFJochReceiverOutput JFJochReceiver::GetFinalStatistics() const { JFJochReceiverOutput ret; ret.efficiency = GetEfficiency(); ret.start_time_ms = std::chrono::duration_cast(start_time.time_since_epoch()).count(); ret.end_time_ms = std::chrono::duration_cast(end_time.time_since_epoch()).count(); ret.writer_queue_full_warning = writer_queue_full; ret.status = GetStatus(); ret.writer_err = writer_error; ret.scan_result = scan_result.GetResult(); return ret; } void JFJochReceiver::Cancel(bool silent) { if (!silent) { // Remote abort: This tells FPGAs to stop but doesn't do anything to CPU code logger.Warning("Cancelling on request"); cancelled = true; } } void JFJochReceiver::Cancel(const JFJochException &e) { logger.Error("Cancelling data collection due to exception"); logger.ErrorException(e); // Error abort: This tells FPGAs to stop and also prevents deadlock in CPU code by setting abort to 1 cancelled = true; }