Some checks failed
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 8m22s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 8m37s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 8m15s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 9m39s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 9m17s
Build Packages / Generate python client (push) Successful in 21s
Build Packages / Build documentation (push) Successful in 43s
Build Packages / Create release (push) Has been skipped
Build Packages / build:rpm (rocky8) (push) Successful in 7m51s
Build Packages / build:rpm (rocky9) (push) Successful in 8m45s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 7m55s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 8m22s
Build Packages / Unit tests (push) Failing after 1h5m51s
This is an UNSTABLE release and not recommended for production use (please use rc.111 instead). * jfjoch_viewer: Fix issue when HTTP sync silently disconnected when it was enabled when the broker was starting measurement. * jfjoch_broker: Add protections on time of geometry optimization and reduce rotation recalculations Reviewed-on: #25 Co-authored-by: Filip Leonarski <filip.leonarski@psi.ch> Co-committed-by: Filip Leonarski <filip.leonarski@psi.ch>
240 lines
9.3 KiB
C++
240 lines
9.3 KiB
C++
// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include "JFJochHttpReader.h"
|
|
|
|
#include <httplib/httplib.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include "../frame_serialize/CBORStream2Deserializer.h"
|
|
#include "../broker/gen/model/Image_buffer_status.h"
|
|
#include "../broker/gen/model/Plots.h"
|
|
#include "../image_analysis/bragg_integration/CalcISigma.h"
|
|
|
|
void JFJochHttpReader::Close() {
|
|
std::unique_lock ul(http_mutex);
|
|
addr = "";
|
|
SetStartMessage({});
|
|
}
|
|
|
|
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<JFJochReaderDataset> 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
|
|
&& res->status != httplib::StatusCode::NotFound_404))
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Could not get image buffer status");
|
|
|
|
if (res->status == httplib::StatusCode::NotFound_404)
|
|
return {};
|
|
if (res->body.empty())
|
|
return {};
|
|
|
|
try {
|
|
auto msg = CBORStream2Deserialize(res->body);
|
|
|
|
if (msg->msg_type != CBORImageType::START)
|
|
return {};
|
|
|
|
auto dataset = std::make_shared<JFJochReaderDataset>();
|
|
dataset->arm_date = msg->start_message->arm_date;
|
|
|
|
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->experiment.PoniRot1_rad(msg->start_message->poni_rot1.value_or(0.0));
|
|
dataset->experiment.PoniRot2_rad(msg->start_message->poni_rot2.value_or(0.0));
|
|
dataset->experiment.PoniRot3_rad(msg->start_message->poni_rot3.value_or(0.0));
|
|
|
|
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->experiment.FluorescenceSpectrum(msg->start_message->fluorescence_spectrum);
|
|
|
|
dataset->bkg_estimate = GetPlot_i("bkg_estimate");
|
|
dataset->spot_count = GetPlot_i("spot_count");
|
|
dataset->spot_count_ice_rings = GetPlot_i("spot_count_ice");
|
|
dataset->spot_count_low_res = GetPlot_i("spot_count_low_res");
|
|
dataset->spot_count_indexed = GetPlot_i("spot_count_indexed");
|
|
|
|
dataset->indexing_result = GetPlot_i("indexing_rate");
|
|
dataset->profile_radius = GetPlot_i("profile_radius");
|
|
dataset->b_factor = GetPlot_i("b_factor");
|
|
dataset->resolution_estimate = GetPlot_i("resolution_estimate");
|
|
dataset->efficiency = GetPlot_i("image_collection_efficiency");
|
|
|
|
if (msg->start_message->goniometer)
|
|
dataset->experiment.Goniometer(msg->start_message->goniometer);
|
|
else if (msg->start_message->grid_scan)
|
|
dataset->experiment.GridScan(msg->start_message->grid_scan);
|
|
|
|
return dataset;
|
|
} catch (std::exception &e) {
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
std::string("Could not load dataset: ") + std::string(e.what()));
|
|
}
|
|
}
|
|
|
|
void JFJochHttpReader::ReadURL(const std::string &url) {
|
|
std::unique_lock ul(http_mutex);
|
|
addr = url;
|
|
if (url.empty())
|
|
SetStartMessage({});
|
|
else
|
|
SetStartMessage(UpdateDataset_i());
|
|
}
|
|
|
|
bool JFJochHttpReader::LoadImage_i(std::shared_ptr<JFJochReaderDataset> &dataset,
|
|
DataMessage &message,
|
|
std::vector<uint8_t> &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();
|
|
if (!dataset)
|
|
return false;
|
|
|
|
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;
|
|
|
|
CalcISigma(message);
|
|
CalcWilsonBFactor(message);
|
|
|
|
return true;
|
|
} catch (std::exception &e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
std::vector<float> 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");
|
|
}
|
|
}
|
|
|
|
void JFJochHttpReader::UploadUserMask(const std::vector<uint32_t>& mask) {
|
|
std::unique_lock ul(http_mutex);
|
|
|
|
if (addr.empty())
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"HTTP address not set. Call ReadURL() first.");
|
|
|
|
if (mask.empty())
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"User mask is empty.");
|
|
|
|
httplib::Client cli_cmd(addr);
|
|
|
|
const char* data_ptr = reinterpret_cast<const char*>(mask.data());
|
|
const size_t byte_size = mask.size() * sizeof(uint32_t);
|
|
|
|
auto res = cli_cmd.Put("/config/user_mask",
|
|
data_ptr,
|
|
byte_size,
|
|
"application/octet-stream");
|
|
|
|
if (!res)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Failed to connect to server to upload user mask");
|
|
|
|
if (res->status != httplib::StatusCode::OK_200)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"Server rejected user mask upload");
|
|
}
|