// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "JFJochHttpReader.h" #include #include #include "../frame_serialize/CBORStream2Deserializer.h" #include "../broker/gen/model/Image_buffer_status.h" #include "../broker/gen/model/Plots.h" void JFJochHttpReader::Close_i() { addr = ""; SetStartMessage({}); } void JFJochHttpReader::Close() { std::unique_lock ul(http_mutex); Close_i(); } uint64_t JFJochHttpReader::GetNumberOfImages() const { std::unique_lock ul(http_mutex); if (addr.empty()) return 0; httplib::Client cli_cmd(addr); auto res = cli_cmd.Get("/image_buffer/status"); if (!res || res->status != httplib::StatusCode::OK_200) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Could not get image buffer status"); try { org::openapitools::server::model::Image_buffer_status status = nlohmann::json::parse(res->body); return status.getMaxImageNumber() + 1; } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Could not parse image buffer status"); } } std::shared_ptr JFJochHttpReader::UpdateDataset_i() { httplib::Client cli_cmd(addr); auto res = cli_cmd.Get("/image_buffer/start.cbor"); if (!res || res->status != httplib::StatusCode::OK_200) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Could not get image buffer status"); try { auto msg = CBORStream2Deserialize(res->body); if (msg->msg_type != CBORImageType::START) return {}; auto dataset = std::make_shared(); dataset->experiment = default_experiment; // JFJochReader is always using int32_t dataset->experiment.BitDepthImage(32); dataset->experiment.PixelSigned(true); dataset->experiment.FilePrefix(msg->start_message->file_prefix); dataset->experiment.BeamX_pxl(msg->start_message->beam_center_x); dataset->experiment.BeamY_pxl(msg->start_message->beam_center_y); dataset->experiment.DetectorDistance_mm(msg->start_message->detector_distance * 1000.0); dataset->experiment.DetectIceRings(msg->start_message->detect_ice_rings.value_or(false)); dataset->az_int_bin_to_q = msg->start_message->az_int_bin_to_q; dataset->az_int_bin_to_phi = msg->start_message->az_int_bin_to_phi; dataset->q_bins = msg->start_message->az_int_q_bin_count.value_or(0); dataset->azimuthal_bins = msg->start_message->az_int_phi_bin_count.value_or(0); dataset->jfjoch_release = msg->start_message->jfjoch_release; DetectorSetup detector = DetDECTRIS(msg->start_message->image_size_x, msg->start_message->image_size_y, msg->start_message->detector_description, {}); detector.PixelSize_um(msg->start_message->pixel_size_x * 1e6); detector.SaturationLimit(msg->start_message->saturation_value); detector.MinFrameTime(std::chrono::microseconds(0)); detector.MinCountTime(std::chrono::microseconds(0)); detector.ReadOutTime(std::chrono::microseconds (0)); dataset->experiment.Detector(detector); dataset->experiment.FrameTime( std::chrono::microseconds(std::lround(msg->start_message->frame_time * 1e6)), std::chrono::microseconds(std::lround(msg->start_message->count_time * 1e6)) ); if (!msg->start_message->pixel_mask.empty()) dataset->pixel_mask = PixelMask(msg->start_message->pixel_mask.begin()->second); dataset->experiment.NumTriggers(1); dataset->experiment.ImagesPerTrigger(msg->start_message->number_of_images); dataset->experiment.SampleName(msg->start_message->sample_name); dataset->experiment.SampleTemperature_K(msg->start_message->sample_temperature_K); dataset->experiment.RingCurrent_mA(msg->start_message->ring_current_mA); dataset->experiment.IncidentEnergy_keV(msg->start_message->incident_energy / 1000.0); dataset->bkg_estimate = GetPlot_i("bkg_estimate"); dataset->spot_count = GetPlot_i("spot_count"); dataset->indexing_result = GetPlot_i("indexing_rate"); dataset->profile_radius = GetPlot_i("profile_radius"); dataset->b_factor = GetPlot_i("b_factor"); dataset->efficiency = GetPlot_i("image_collection_efficiency"); return dataset; } catch (std::exception &e) { Close_i(); throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Could not load dataset"); } } void JFJochHttpReader::ReadURL(const std::string &url) { std::unique_lock ul(http_mutex); addr = url; if (url.empty()) Close_i(); SetStartMessage(UpdateDataset_i()); } bool JFJochHttpReader::LoadImage_i(std::shared_ptr &dataset, DataMessage &message, std::vector &buffer, int64_t image_number, bool update_dataset) { std::unique_lock ul(http_mutex); if (addr.empty()) return false; httplib::Client cli_cmd(addr); // Always update dataset, as it might have changed from the last time if (update_dataset) dataset = UpdateDataset_i(); auto res = cli_cmd.Get("/image_buffer/image.cbor?id=" + std::to_string(image_number)); if (!res || res->status != httplib::StatusCode::OK_200) return false; try { buffer.resize(res->body.size()); memcpy(buffer.data(), res->body.data(), res->body.size()); auto msg = CBORStream2Deserialize(buffer); if (msg->msg_type != CBORImageType::IMAGE) return false; message = *msg->data_message; return true; } catch (std::exception &e) { return false; } } std::vector JFJochHttpReader::GetPlot_i(const std::string &plot_type, float fill_value) const { if (addr.empty()) return {}; httplib::Client cli_cmd(addr); auto res = cli_cmd.Get("/preview/plot?binning=1&experimental_coord=false&fill=" + std::to_string(fill_value) + "&type=" + plot_type); if (!res || res->status != httplib::StatusCode::OK_200) return {}; try { org::openapitools::server::model::Plots plots = nlohmann::json::parse(res->body); auto plot_v = plots.getPlot(); if (plot_v.size() == 1) return plot_v[0].getY(); else return {}; } catch (nlohmann::json::parse_error &e) { throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Could not parse image buffer status"); } }