Clean-up (but fully AI generated, so need to carefully check)
Some checks failed
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Failing after 6m42s
Build Packages / build:rpm (ubuntu2204) (push) Failing after 3m11s
Build Packages / build:rpm (rocky8_nocuda) (push) Failing after 10m13s
Build Packages / build:rpm (rocky9_nocuda) (push) Failing after 10m12s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Failing after 10m9s
Build Packages / Build documentation (push) Successful in 27s
Build Packages / Generate python client (push) Successful in 29s
Build Packages / Create release (push) Has been skipped
Build Packages / build:rpm (rocky9) (push) Failing after 13m14s
Build Packages / build:rpm (rocky8) (push) Failing after 14m7s
Build Packages / build:rpm (rocky8_sls9) (push) Failing after 14m34s
Build Packages / build:rpm (rocky9_sls9) (push) Failing after 14m38s
Build Packages / build:rpm (ubuntu2404) (push) Failing after 7m28s
Build Packages / Unit tests (push) Has been cancelled

This commit is contained in:
2026-03-23 22:15:02 +01:00
parent 75ed1f954e
commit 2d191b08d5
2 changed files with 320 additions and 401 deletions

View File

@@ -4,57 +4,20 @@
#include "JFJochBrokerHttp.h"
#include <algorithm>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <vector>
#include "gen/model/Error_message.h"
#include "../common/GitInfo.h"
#include "OpenAPIConvert.h"
#include "../preview/JFJochTIFF.h"
#include "OpenAPIConvert.h"
#include "gen/model/Error_message.h"
using namespace org::openapitools::server::model;
namespace {
template <class T>
static bool fromStringValue(const std::string& s, T& out) {
std::istringstream is(s);
is >> out;
return !is.fail() && is.eof();
}
template <>
static bool fromStringValue<std::string>(const std::string& s, std::string& out) {
out = s;
return true;
}
template <>
static bool fromStringValue<bool>(const std::string& s, bool& out) {
if (s == "true" || s == "1") { out = true; return true; }
if (s == "false" || s == "0") { out = false; return true; }
return false;
}
template <class T>
std::optional<T> query_optional(const httplib::Request& req, const char* name) {
if (!req.has_param(name))
return std::nullopt;
T v{};
if (fromStringValue(req.get_param_value(name), v))
return v;
return std::nullopt;
}
inline std::optional<std::string> query_optional_string(const httplib::Request& req, const char* name) {
if (!req.has_param(name))
return std::nullopt;
return req.get_param_value(name);
}
inline std::string mime_from_path(const std::string& path) {
inline std::string mime_from_path(const std::string &path) {
if (path.ends_with(".js")) return "text/javascript";
if (path.ends_with(".css")) return "text/css";
if (path.ends_with(".html")) return "text/html";
@@ -68,7 +31,7 @@ namespace {
return "application/octet-stream";
}
inline bool read_file_to_string(const std::string& path, std::string& out) {
inline bool read_file_to_string(const std::string &path, std::string &out) {
std::ifstream f(path, std::ios::binary);
if (!f)
return false;
@@ -80,8 +43,8 @@ namespace {
}
JFJochBrokerHttp::JFJochBrokerHttp(const DiffractionExperiment &experiment,
const SpotFindingSettings& spot_finding_settings)
: state_machine(experiment, services, logger, spot_finding_settings) {}
const SpotFindingSettings &spot_finding_settings)
: state_machine(experiment, services, logger, spot_finding_settings) {}
void JFJochBrokerHttp::AddDetectorSetup(const DetectorSetup &setup) {
state_machine.AddDetectorSetup(setup);
@@ -100,9 +63,9 @@ JFJochBrokerHttp &JFJochBrokerHttp::FrontendDirectory(const std::string &directo
std::pair<int, std::string> JFJochBrokerHttp::handleParsingException(const std::exception &ex) const noexcept {
try {
throw;
} catch (nlohmann::detail::exception &e) {
} catch (const nlohmann::detail::exception &e) {
return {400, e.what()};
} catch (std::exception &e) {
} catch (const std::exception &e) {
return {500, e.what()};
}
}
@@ -127,7 +90,7 @@ std::pair<int, std::string> JFJochBrokerHttp::handleOperationException(const std
}
}
void JFJochBrokerHttp::attach(httplib::Server& server) {
void JFJochBrokerHttp::attach(httplib::Server &server) {
register_routes(server);
server.set_error_handler([](const httplib::Request &, httplib::Response &res) {
res.status = 404;
@@ -135,43 +98,40 @@ void JFJochBrokerHttp::attach(httplib::Server& server) {
});
}
void JFJochBrokerHttp::register_routes(httplib::Server& server) {
void JFJochBrokerHttp::register_routes(httplib::Server &server) {
auto bind_noarg = [this](auto method) {
return [this, method](const httplib::Request&, httplib::Response& res) {
return [this, method](const httplib::Request &, httplib::Response &res) {
try {
(this->*method)(res);
} catch (const std::exception& e) {
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
};
};
auto bind_json = [this](auto method, auto model_tag) {
return [this, method](const httplib::Request& req, httplib::Response& res) {
return [this, method](const httplib::Request &req, httplib::Response &res) {
using Model = decltype(model_tag);
try {
Model v;
nlohmann::json::parse(req.body).get_to(v);
v.validate();
(this->*method)(v, res);
} catch (const std::exception& e) {
} catch (const std::exception &e) {
auto [c, s] = handleParsingException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
};
};
auto bind_req = [this](auto method) {
return [this, method](const httplib::Request& req, httplib::Response& res) {
return [this, method](const httplib::Request &req, httplib::Response &res) {
try {
(this->*method)(req, res);
} catch (const std::exception& e) {
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
};
};
@@ -193,9 +153,22 @@ void JFJochBrokerHttp::register_routes(httplib::Server& server) {
server.Put("/config/indexing", bind_json(&JFJochBrokerHttp::config_indexing_put, Indexing_settings{}));
server.Get("/config/instrument", bind_noarg(&JFJochBrokerHttp::config_instrument_get));
server.Put("/config/instrument", bind_json(&JFJochBrokerHttp::config_instrument_put, Instrument_metadata{}));
server.Put("/config/internal_generator_image", bind_req(&JFJochBrokerHttp::config_internal_generator_image_put));
server.Put("/config/internal_generator_image.tiff", bind_req(&JFJochBrokerHttp::config_internal_generator_image_tiff_put));
server.Get("/config/mask", bind_noarg(&JFJochBrokerHttp::config_mask_get));
server.Put("/config/internal_generator_image", [this](const httplib::Request &req, httplib::Response &res) {
try {
config_internal_generator_image_put(parse_query_value<int64_t>(req, "id"), req, res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
send_plain(res, c, s);
}
});
server.Put("/config/internal_generator_image.tiff", [this](const httplib::Request &req, httplib::Response &res) {
try {
config_internal_generator_image_tiff_put(parse_query_value<int64_t>(req, "id"), req, res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
send_plain(res, c, s);
}
}); server.Get("/config/mask", bind_noarg(&JFJochBrokerHttp::config_mask_get));
server.Get("/config/mask.tiff", bind_noarg(&JFJochBrokerHttp::config_mask_tiff_get));
server.Get("/config/roi", bind_noarg(&JFJochBrokerHttp::config_roi_get));
server.Put("/config/roi", bind_json(&JFJochBrokerHttp::config_roi_put, Roi_definitions{}));
@@ -215,118 +188,123 @@ void JFJochBrokerHttp::register_routes(httplib::Server& server) {
server.Get("/detector/status", bind_noarg(&JFJochBrokerHttp::detector_status_get));
server.Get("/fpga_status", bind_noarg(&JFJochBrokerHttp::fpga_status_get));
server.Post("/image_buffer/clear", bind_noarg(&JFJochBrokerHttp::image_buffer_clear_post));
server.Get("/image_buffer/image.cbor", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/image_buffer/image.cbor", [this](const httplib::Request &req, httplib::Response &res) {
try {
image_buffer_image_cbor_get(query_optional<int64_t>(req, "id"), res);
} catch (const std::exception& e) {
image_buffer_image_cbor_get(parse_query_value<int64_t>(req, "id"), res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/image_buffer/image.jpeg", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/image_buffer/image.jpeg", [this](const httplib::Request &req, httplib::Response &res) {
try {
image_buffer_image_jpeg_get(
query_optional<int64_t>(req, "id"),
query_optional<bool>(req, "show_user_mask"),
query_optional<bool>(req, "show_roi"),
query_optional<bool>(req, "show_spots"),
query_optional<bool>(req, "show_beam_center"),
query_optional<float>(req, "saturation"),
query_optional<int64_t>(req, "jpeg_quality"),
query_optional<float>(req, "show_res_ring"),
query_optional_string(req, "color"),
query_optional<bool>(req, "show_res_est"),
parse_query_value<int64_t>(req, "id"),
parse_query_value<bool>(req, "show_user_mask"),
parse_query_value<bool>(req, "show_roi"),
parse_query_value<bool>(req, "show_spots"),
parse_query_value<bool>(req, "show_beam_center"),
parse_query_value<float>(req, "saturation"),
parse_query_value<int64_t>(req, "jpeg_quality"),
parse_query_value<float>(req, "show_res_ring"),
parse_query_string(req, "color"),
parse_query_value<bool>(req, "show_res_est"),
res
);
} catch (const std::exception& e) {
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/image_buffer/image.tiff", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/image_buffer/image.tiff", [this](const httplib::Request &req, httplib::Response &res) {
try {
image_buffer_image_tiff_get(query_optional<int64_t>(req, "id"), res);
} catch (const std::exception& e) {
image_buffer_image_tiff_get(parse_query_value<int64_t>(req, "id"), res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/image_buffer/start.cbor", bind_noarg(&JFJochBrokerHttp::image_buffer_start_cbor_get));
server.Get("/image_buffer/status", bind_noarg(&JFJochBrokerHttp::image_buffer_status_get));
server.Get("/image_pusher/status", bind_noarg(&JFJochBrokerHttp::image_pusher_status_get));
server.Post("/initialize", bind_noarg(&JFJochBrokerHttp::initialize_post));
server.Post("/pedestal", bind_noarg(&JFJochBrokerHttp::pedestal_post));
server.Get("/preview/pedestal.tiff", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/preview/pedestal.tiff", [this](const httplib::Request &req, httplib::Response &res) {
try {
preview_pedestal_tiff_get(query_optional<int32_t>(req, "gain_level"),
query_optional<int32_t>(req, "sc"), res);
} catch (const std::exception& e) {
preview_pedestal_tiff_get(parse_query_value<int32_t>(req, "gain_level"),
parse_query_value<int32_t>(req, "sc"),
res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/preview/plot.bin", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/preview/plot.bin", [this](const httplib::Request &req, httplib::Response &res) {
try {
preview_plot_bin_get(query_optional_string(req, "type"),
query_optional_string(req, "roi"),
preview_plot_bin_get(parse_query_string(req, "type"),
parse_query_string(req, "roi"),
res);
} catch (const std::exception& e) {
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/preview/plot", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/preview/plot", [this](const httplib::Request &req, httplib::Response &res) {
try {
preview_plot_get(query_optional_string(req, "type"),
query_optional<int32_t>(req, "binning"),
query_optional<bool>(req, "compression"),
query_optional<float>(req, "fill"),
query_optional<bool>(req, "experimental_coord"),
query_optional_string(req, "azint_unit"),
preview_plot_get(parse_query_string(req, "type"),
parse_query_value<int32_t>(req, "binning"),
parse_query_value<bool>(req, "compression"),
parse_query_value<float>(req, "fill"),
parse_query_value<bool>(req, "experimental_coord"),
parse_query_string(req, "azint_unit"),
res);
} catch (const std::exception& e) {
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/result/scan", bind_noarg(&JFJochBrokerHttp::result_scan_get));
server.Post("/start", bind_json(&JFJochBrokerHttp::start_post, Dataset_settings{}));
server.Get("/statistics/calibration", bind_noarg(&JFJochBrokerHttp::statistics_calibration_get));
server.Get("/statistics/data_collection", bind_noarg(&JFJochBrokerHttp::statistics_data_collection_get));
server.Get("/statistics", [this](const httplib::Request& req, httplib::Response& res) {
server.Get("/statistics", [this](const httplib::Request &req, httplib::Response &res) {
try {
statistics_get(query_optional<bool>(req, "compression"), res);
} catch (const std::exception& e) {
statistics_get(parse_query_value<bool>(req, "compression"), res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/status", bind_noarg(&JFJochBrokerHttp::status_get));
server.Post("/trigger", bind_noarg(&JFJochBrokerHttp::trigger_post));
server.Get("/version", bind_noarg(&JFJochBrokerHttp::version_get));
server.Post("/wait_till_done", [this](const httplib::Request& req, httplib::Response& res) {
server.Post("/wait_till_done", [this](const httplib::Request &req, httplib::Response &res) {
try {
wait_till_done_post(query_optional<int32_t>(req, "timeout"), res);
} catch (const std::exception& e) {
wait_till_done_post(parse_query_value<int32_t>(req, "timeout"), res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
res.status = c;
res.set_content(s, "text/plain");
send_plain(res, c, s);
}
});
server.Get("/xfel/event_code", bind_noarg(&JFJochBrokerHttp::xfel_event_code_get));
server.Get("/xfel/pulse_id", bind_noarg(&JFJochBrokerHttp::xfel_pulse_id_get));
server.Get("/", [this](const httplib::Request& req, httplib::Response& res) { GetStaticFile(req, res); });
server.Get("/frontend", [this](const httplib::Request& req, httplib::Response& res) { GetStaticFile(req, res); });
server.Get("/frontend/*", [this](const httplib::Request& req, httplib::Response& res) { GetStaticFile(req, res); });
server.Get("/frontend/assets/*", [this](const httplib::Request& req, httplib::Response& res) { GetStaticFile(req, res); });
server.Get("/", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); });
server.Get("/frontend", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); });
server.Get("/frontend/*", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); });
server.Get("/frontend/assets/*", [this](const httplib::Request &req, httplib::Response &res) { GetStaticFile(req, res); });
}
void JFJochBrokerHttp::cancel_post(httplib::Response &response) {
@@ -355,13 +333,13 @@ void JFJochBrokerHttp::status_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetStatus()), response);
}
void JFJochBrokerHttp::wait_till_done_post(const std::optional<int32_t> &timeout,
httplib::Response &response) {
void JFJochBrokerHttp::wait_till_done_post(const std::optional<int32_t> &timeout, httplib::Response &response) {
BrokerStatus status;
if (!timeout)
status = state_machine.WaitTillMeasurementDone(std::chrono::minutes(1));
else if ((timeout.value() > 3600) || (timeout.value() < 0)) {
response.status = 400;
response.set_content("timeout must be in range 0..3600", "text/plain");
return;
} else if (timeout.value() == 0)
status = state_machine.GetStatus();
@@ -400,7 +378,7 @@ void JFJochBrokerHttp::config_detector_get(httplib::Response &response) {
}
void JFJochBrokerHttp::config_detector_put(const Detector_settings &detectorSettings,
httplib::Response &response) {
httplib::Response &response) {
state_machine.LoadDetectorSettings(Convert(detectorSettings));
response.status = 200;
}
@@ -410,7 +388,7 @@ void JFJochBrokerHttp::config_azim_int_get(httplib::Response &response) {
}
void JFJochBrokerHttp::config_azim_int_put(const Azim_int_settings &radIntSettings,
httplib::Response &response) {
httplib::Response &response) {
state_machine.SetRadialIntegrationSettings(Convert(radIntSettings));
response.status = 200;
}
@@ -437,7 +415,7 @@ void JFJochBrokerHttp::config_spot_finding_put(const Spot_finding_settings &spot
void JFJochBrokerHttp::statistics_calibration_get(httplib::Response &response) {
nlohmann::json j;
for (const auto &d: Convert(state_machine.GetCalibrationStatistics()))
for (const auto &d : Convert(state_machine.GetCalibrationStatistics()))
j.push_back(d);
response.set_content(j.dump(), "application/json");
response.status = 200;
@@ -445,11 +423,10 @@ void JFJochBrokerHttp::statistics_calibration_get(httplib::Response &response) {
void JFJochBrokerHttp::statistics_data_collection_get(httplib::Response &response) {
auto stats = state_machine.GetMeasurementStatistics();
if (stats) {
if (stats)
ProcessOutput(Convert(stats.value()), response);
} else {
else
response.status = 404;
}
}
void JFJochBrokerHttp::config_file_writer_get(httplib::Response &response) {
@@ -484,7 +461,7 @@ void JFJochBrokerHttp::config_image_format_raw_post(httplib::Response &response)
void JFJochBrokerHttp::fpga_status_get(httplib::Response &response) {
nlohmann::json j;
for (const auto &d: Convert(state_machine.GetDeviceStatus()))
for (const auto &d : Convert(state_machine.GetDeviceStatus()))
j.push_back(d);
response.set_content(j.dump(), "application/json");
response.status = 200;
@@ -555,6 +532,37 @@ void JFJochBrokerHttp::config_zeromq_metadata_put(const Zeromq_metadata_settings
response.status = 200;
}
void JFJochBrokerHttp::config_internal_generator_image_put(const std::optional<int64_t> &id,
const httplib::Request &request,
httplib::Response &response) {
int64_t image_number = id.value_or(0);
if ((image_number < 0) || (image_number > 127)) {
response.status = 400;
response.set_content("id must be in range 0-127", "text/plain");
return;
}
state_machine.LoadInternalGeneratorImage(request.body.data(), request.body.size(), image_number);
logger.Info("Internal generator image #{} loaded", image_number);
response.status = 200;
}
void JFJochBrokerHttp::config_internal_generator_image_tiff_put(const std::optional<int64_t> &id,
const httplib::Request &request,
httplib::Response &response) {
int64_t image_number = id.value_or(0);
if ((image_number < 0) || (image_number > 127)) {
response.status = 400;
response.set_content("id must be in range 0-127", "text/plain");
return;
}
state_machine.LoadInternalGeneratorImageTIFF(request.body, image_number);
response.status = 200;
}
void JFJochBrokerHttp::image_buffer_clear_post(httplib::Response &response) {
state_machine.ClearImageBuffer();
response.status = 200;
@@ -564,7 +572,7 @@ void JFJochBrokerHttp::image_buffer_image_cbor_get(const std::optional<int64_t>
httplib::Response &response) {
std::vector<uint8_t> tmp_vector;
state_machine.GetImageFromBuffer(tmp_vector, imageNumber.value_or(-1));
std::string s(reinterpret_cast<char*>(tmp_vector.data()), tmp_vector.size());
std::string s(reinterpret_cast<char *>(tmp_vector.data()), tmp_vector.size());
if (!s.empty())
response.set_content(s, "application/cbor");
@@ -582,7 +590,7 @@ void JFJochBrokerHttp::image_buffer_image_jpeg_get(const std::optional<int64_t>
const std::optional<float> &showResRing,
const std::optional<std::string> &color,
const std::optional<bool> &showResEst,
httplib::Response &response) {
httplib::Response &response) {
int64_t image_id = id.value_or(ImageBuffer::MaxImage);
PreviewImageSettings settings{};
@@ -592,9 +600,9 @@ void JFJochBrokerHttp::image_buffer_image_jpeg_get(const std::optional<int64_t>
settings.saturation_value = saturation;
settings.background_value = 0.0;
settings.jpeg_quality = jpegQuality.value_or(100);
if (showResEst.value_or(false))
if (showResEst.value_or(false)) {
settings.show_res_est = true;
else {
} else {
settings.show_res_est = false;
settings.resolution_ring = showResRing;
}
@@ -611,9 +619,7 @@ void JFJochBrokerHttp::image_buffer_image_jpeg_get(const std::optional<int64_t>
void JFJochBrokerHttp::image_buffer_image_tiff_get(const std::optional<int64_t> &id,
httplib::Response &response) {
int64_t image_id = ImageBuffer::MaxImage;
if (id.has_value())
image_id = id.value();
int64_t image_id = id.value_or(ImageBuffer::MaxImage);
std::string s = state_machine.GetPreviewTIFF(image_id);
if (!s.empty())
@@ -753,6 +759,7 @@ void JFJochBrokerHttp::preview_pedestal_tiff_get(const std::optional<int32_t> &g
httplib::Response &response) {
if (!gainLevel) {
response.status = 400;
response.set_content("gain_level is required", "text/plain");
return;
}
@@ -769,12 +776,14 @@ void JFJochBrokerHttp::preview_plot_bin_get(const std::optional<std::string> &ty
std::vector<float> ret;
state_machine.GetPlotRaw(ret, ConvertPlotType(type), roi.value_or(""));
if (ret.empty())
if (ret.empty()) {
response.status = 200;
else
response.set_content(reinterpret_cast<const char *>(ret.data()),
ret.size() * sizeof(float),
"application/octet-stream");
return;
}
response.set_content(reinterpret_cast<const char *>(ret.data()),
ret.size() * sizeof(float),
"application/octet-stream");
response.status = 200;
}
@@ -846,68 +855,26 @@ void JFJochBrokerHttp::xfel_pulse_id_get(httplib::Response &response) {
response.status = 200;
}
void JFJochBrokerHttp::config_zeromq_preview_put(const Zeromq_preview_settings &zeromqPreviewSettings,
httplib::Response &response) {
state_machine.SetPreviewSocketSettings(Convert(zeromqPreviewSettings));
response.status = 200;
}
void JFJochBrokerHttp::config_zeromq_metadata_put(const Zeromq_metadata_settings &zeromqMetadataSettings,
httplib::Response &response) {
state_machine.SetMetadataSocketSettings(Convert(zeromqMetadataSettings));
response.status = 200;
}
void JFJochBrokerHttp::config_zeromq_preview_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetPreviewSocketSettings()), response);
}
void JFJochBrokerHttp::config_zeromq_metadata_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetMetadataSocketSettings()), response);
}
void JFJochBrokerHttp::image_buffer_clear_post(httplib::Response &response) {
state_machine.ClearImageBuffer();
response.status = 200;
}
void JFJochBrokerHttp::config_file_writer_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetFileWriterSettings()), response);
}
void JFJochBrokerHttp::config_file_writer_put(const File_writer_settings &fileWriterSettings,
httplib::Response &response) {
state_machine.LoadFileWriterSettings(Convert(fileWriterSettings));
response.status = 200;
}
void JFJochBrokerHttp::image_pusher_status_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetImagePusherStatus()), response);
}
void JFJochBrokerHttp::image_buffer_status_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetImageBufferStatus()), response);
}
void JFJochBrokerHttp::GetStaticFile(const httplib::Request &request, httplib::Response &response) {
if (frontend_directory.empty()) {
response.status = 404;
return;
}
logger.Info("Requesting static resource {} from {}", request.path, frontend_directory);
const std::string path = request.path;
logger.Info("Requesting static resource {} from {}", path, frontend_directory);
if (request.path.find("../") != std::string::npos) {
if (path.find("../") != std::string::npos) {
response.status = 403;
return;
}
std::string rel;
if (request.path == "/" || request.path == "/frontend" || request.path == "/frontend/")
if (path == "/" || path == "/frontend" || path == "/frontend/") {
rel = "index.html";
else if (request.path.starts_with("/frontend/"))
rel = request.path.substr(std::string("/frontend/").size());
else {
} else if (path.starts_with("/frontend/")) {
rel = path.substr(std::string("/frontend/").size());
} else {
response.status = 404;
return;
}

View File

@@ -1,44 +1,36 @@
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
// Using OpenAPI licensed with Apache License 2.0
#pragma once
#include <httplib.h>
#include <nlohmann/json.hpp>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include "../common/Logger.h"
#include "JFJochStateMachine.h"
#include "JFJochServices.h"
#include "JFJochStateMachine.h"
#include "OpenAPIConvert.h"
#include "gen/model/Azim_int_settings.h"
#include "gen/model/Broker_status.h"
#include "gen/model/Calibration_statistics_inner.h"
#include "gen/model/Dark_mask_settings.h"
#include "gen/model/Dataset_settings.h"
#include "gen/model/Detector_list.h"
#include "gen/model/Detector_selection.h"
#include "gen/model/Detector_settings.h"
#include "gen/model/Detector_status.h"
#include "gen/model/Error_message.h"
#include "gen/model/File_writer_settings.h"
#include "gen/model/Fpga_status_inner.h"
#include "gen/model/Image_buffer_status.h"
#include "gen/model/Image_format_settings.h"
#include "gen/model/Image_pusher_status.h"
#include "gen/model/Indexing_settings.h"
#include "gen/model/Instrument_metadata.h"
#include "gen/model/Jfjoch_statistics.h"
#include "gen/model/Measurement_statistics.h"
#include "gen/model/Plots.h"
#include "gen/model/Roi_definitions.h"
#include "gen/model/Scan_result.h"
@@ -46,7 +38,6 @@
#include "gen/model/Zeromq_metadata_settings.h"
#include "gen/model/Zeromq_preview_settings.h"
class JFJochBrokerHttp {
Logger logger{"JFJochBroker"};
JFJochServices services{logger};
@@ -54,209 +45,52 @@ class JFJochBrokerHttp {
std::string frontend_directory;
template<class T>
static void send_plain(httplib::Response &res, int code, const T &body) {
res.status = code;
res.set_content(body, "text/plain");
static bool fromStringValue(const std::string &s, T &out) {
std::istringstream is(s);
is >> out;
return !is.fail() && is.eof();
}
template<class T>
static void send_json(httplib::Response &res, int code, const T &body) {
res.status = code;
res.set_content(body, "application/json");
template<>
static bool fromStringValue<std::string>(const std::string &s, std::string &out) {
out = s;
return true;
}
template<>
static bool fromStringValue<bool>(const std::string &s, bool &out) {
if (s == "true" || s == "1") {
out = true;
return true;
}
if (s == "false" || s == "0") {
out = false;
return true;
}
return false;
}
template<class T>
static std::optional<T> parse_query_value(const httplib::Request &req, const char *name) {
if (!req.has_param(name)) return std::nullopt;
if (!req.has_param(name))
return std::nullopt;
T v{};
if (fromStringValue(req.get_param_value(name), v)) return v;
if (fromStringValue(req.get_param_value(name), v))
return v;
return std::nullopt;
}
template<class Model, class Fn>
void with_json_body(const httplib::Request &req, httplib::Response &res, Fn &&fn) {
try {
Model v;
nlohmann::json::parse(req.body).get_to(v);
v.validate();
std::forward<Fn>(fn)(v, res);
} catch (const std::exception &e) {
auto [c, s] = handleParsingException(e);
send_plain(res, c, s);
}
static std::optional<std::string> parse_query_string(const httplib::Request &req, const char *name) {
if (!req.has_param(name))
return std::nullopt;
return req.get_param_value(name);
}
template<class Fn>
void with_no_body(const httplib::Request &, httplib::Response &res, Fn &&fn) {
try {
std::forward<Fn>(fn)(res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
send_plain(res, c, s);
}
static void send_plain(httplib::Response &res, int code, const std::string &body) {
res.status = code;
res.set_content(body, "text/plain");
}
template<class Fn>
void with_request(const httplib::Request &req, httplib::Response &res, Fn &&fn) {
try {
std::forward<Fn>(fn)(req, res);
} catch (const std::exception &e) {
auto [c, s] = handleOperationException(e);
send_plain(res, c, s);
}
}
std::pair<int, std::string> handleParsingException(const std::exception &ex) const noexcept;
std::pair<int, std::string> handleOperationException(const std::exception &ex) const noexcept;
void register_routes(httplib::Server &server);
void cancel_post(httplib::Response &response);
void deactivate_post(httplib::Response &response);
void initialize_post(httplib::Response &response);
void start_post(const org::openapitools::server::model::Dataset_settings &datasetSettings,
httplib::Response &response);
void status_get(httplib::Response &response);
void wait_till_done_post(const std::optional<int32_t> &timeout, httplib::Response &response);
void trigger_post(httplib::Response &response);
void pedestal_post(httplib::Response &response);
void config_detector_get(httplib::Response &response);
void config_detector_put(const org::openapitools::server::model::Detector_settings &detectorSettings,
httplib::Response &response);
void config_azim_int_get(httplib::Response &response);
void config_azim_int_put(const org::openapitools::server::model::Azim_int_settings &azimIntSettings,
httplib::Response &response);
void config_file_writer_get(httplib::Response &response);
void config_file_writer_put(const org::openapitools::server::model::File_writer_settings &fileWriterSettings,
httplib::Response &response);
void config_image_format_get(httplib::Response &response);
void config_image_format_put(const org::openapitools::server::model::Image_format_settings &imageFormatSettings,
httplib::Response &response);
void config_image_format_conversion_post(httplib::Response &response);
void config_image_format_raw_post(httplib::Response &response);
void config_indexing_get(httplib::Response &response);
void config_indexing_put(const org::openapitools::server::model::Indexing_settings &indexingSettings,
httplib::Response &response);
void config_instrument_get(httplib::Response &response);
void config_instrument_put(const org::openapitools::server::model::Instrument_metadata &instrumentMetadata,
httplib::Response &response);
void config_select_detector_get(httplib::Response &response);
void config_select_detector_put(const org::openapitools::server::model::Detector_selection &detectorSelection,
httplib::Response &response);
void config_spot_finding_get(httplib::Response &response);
void config_spot_finding_put(const org::openapitools::server::model::Spot_finding_settings &spotFindingSettings,
httplib::Response &response);
void config_dark_mask_get(httplib::Response &response);
void config_dark_mask_put(const org::openapitools::server::model::Dark_mask_settings &darkMaskSettings,
httplib::Response &response);
void config_zeromq_metadata_get(httplib::Response &response);
void config_zeromq_metadata_put(
const org::openapitools::server::model::Zeromq_metadata_settings &zeromqMetadataSettings,
httplib::Response &response);
void config_zeromq_preview_get(httplib::Response &response);
void config_zeromq_preview_put(
const org::openapitools::server::model::Zeromq_preview_settings &zeromqPreviewSettings,
httplib::Response &response);
void config_roi_get(httplib::Response &response);
void config_roi_put(const org::openapitools::server::model::Roi_definitions &roiDefinitions,
httplib::Response &response);
void config_mask_get(httplib::Response &response);
void config_mask_tiff_get(httplib::Response &response);
void config_user_mask_get(httplib::Response &response);
void config_user_mask_put(const httplib::Request &request, httplib::Response &response);
void config_user_mask_tiff_get(httplib::Response &response);
void config_user_mask_tiff_put(const httplib::Request &request, httplib::Response &response);
void image_buffer_clear_post(httplib::Response &response);
void image_buffer_image_cbor_get(const std::optional<int64_t> &id, httplib::Response &response);
void image_buffer_image_jpeg_get(const std::optional<int64_t> &id, const std::optional<bool> &showUserMask,
const std::optional<bool> &showRoi, const std::optional<bool> &showSpots,
const std::optional<bool> &showBeamCenter, const std::optional<float> &saturation,
const std::optional<int64_t> &jpegQuality, const std::optional<float> &showResRing,
const std::optional<std::string> &color, const std::optional<bool> &showResEst,
httplib::Response &response);
void image_buffer_image_tiff_get(const std::optional<int64_t> &id, httplib::Response &response);
void image_buffer_start_cbor_get(httplib::Response &response);
void image_buffer_status_get(httplib::Response &response);
void image_pusher_status_get(httplib::Response &response);
void detector_status_get(httplib::Response &response);
void fpga_status_get(httplib::Response &response);
void statistics_calibration_get(httplib::Response &response);
void statistics_data_collection_get(httplib::Response &response);
void statistics_get(const std::optional<bool> &compression, httplib::Response &response);
void result_scan_get(httplib::Response &response);
void version_get(httplib::Response &response);
void preview_pedestal_tiff_get(const std::optional<int32_t> &gainLevel, const std::optional<int32_t> &sc,
httplib::Response &response);
void preview_plot_bin_get(const std::optional<std::string> &type, const std::optional<std::string> &roi,
httplib::Response &response);
void preview_plot_get(const std::optional<std::string> &type, const std::optional<int32_t> &binning,
const std::optional<bool> &compression, const std::optional<float> &fill,
const std::optional<bool> &experimentalCoord, const std::optional<std::string> &azintUnit,
httplib::Response &response);
void xfel_event_code_get(httplib::Response &response);
void xfel_pulse_id_get(httplib::Response &response);
void GetStaticFile(const httplib::Request &request, httplib::Response &response);
template<class T>
void ProcessOutput(const T &output, httplib::Response &response, bool compression = false) {
std::stringstream s;
@@ -264,25 +98,143 @@ class JFJochBrokerHttp {
logger.Error(s.str());
response.status = 500;
response.set_content(s.str(), "text/plain");
} else {
nlohmann::json j = output;
if (compression)
response.set_header("Content-Encoding", "deflate");
response.status = 200;
response.set_content(j.dump(), "application/json");
return;
}
nlohmann::json j = output;
if (compression)
response.set_header("Content-Encoding", "deflate");
response.status = 200;
response.set_content(j.dump(), "application/json");
}
std::pair<int, std::string> handleParsingException(const std::exception &ex) const noexcept;
std::pair<int, std::string> handleOperationException(const std::exception &ex) const noexcept;
void register_routes(httplib::Server &server);
void cancel_post(httplib::Response &response);
void deactivate_post(httplib::Response &response);
void initialize_post(httplib::Response &response);
void start_post(const org::openapitools::server::model::Dataset_settings &datasetSettings,
httplib::Response &response);
void status_get(httplib::Response &response);
void wait_till_done_post(const std::optional<int32_t> &timeout, httplib::Response &response);
void trigger_post(httplib::Response &response);
void pedestal_post(httplib::Response &response);
void config_detector_get(httplib::Response &response);
void config_detector_put(const org::openapitools::server::model::Detector_settings &detectorSettings,
httplib::Response &response);
void config_azim_int_get(httplib::Response &response);
void config_azim_int_put(const org::openapitools::server::model::Azim_int_settings &azimIntSettings,
httplib::Response &response);
void config_select_detector_get(httplib::Response &response);
void config_select_detector_put(const org::openapitools::server::model::Detector_selection &detectorSelection,
httplib::Response &response);
void config_spot_finding_get(httplib::Response &response);
void config_spot_finding_put(const org::openapitools::server::model::Spot_finding_settings &spotFindingSettings,
httplib::Response &response);
void statistics_calibration_get(httplib::Response &response);
void statistics_data_collection_get(httplib::Response &response);
void config_file_writer_get(httplib::Response &response);
void config_file_writer_put(const org::openapitools::server::model::File_writer_settings &fileWriterSettings,
httplib::Response &response);
void config_image_format_get(httplib::Response &response);
void config_image_format_put(const org::openapitools::server::model::Image_format_settings &imageFormatSettings,
httplib::Response &response);
void config_image_format_conversion_post(httplib::Response &response);
void config_image_format_raw_post(httplib::Response &response);
void fpga_status_get(httplib::Response &response);
void statistics_get(const std::optional<bool> &compression, httplib::Response &response);
void config_zeromq_preview_get(httplib::Response &response);
void config_zeromq_preview_put(const org::openapitools::server::model::Zeromq_preview_settings &zeromqPreviewSettings,
httplib::Response &response);
void config_zeromq_metadata_get(httplib::Response &response);
void config_zeromq_metadata_put(const org::openapitools::server::model::Zeromq_metadata_settings &zeromqMetadataSettings,
httplib::Response &response);
void config_internal_generator_image_put(const std::optional<int64_t> &id,
const httplib::Request &request,
httplib::Response &response);
void config_internal_generator_image_tiff_put(const std::optional<int64_t> &id,
const httplib::Request &request,
httplib::Response &response);
void image_buffer_clear_post(httplib::Response &response);
void image_buffer_image_cbor_get(const std::optional<int64_t> &id, httplib::Response &response);
void image_buffer_image_jpeg_get(const std::optional<int64_t> &id, const std::optional<bool> &showUserMask,
const std::optional<bool> &showRoi, const std::optional<bool> &showSpots,
const std::optional<bool> &showBeamCenter, const std::optional<float> &saturation,
const std::optional<int64_t> &jpegQuality, const std::optional<float> &showResRing,
const std::optional<std::string> &color, const std::optional<bool> &showResEst,
httplib::Response &response);
void image_buffer_image_tiff_get(const std::optional<int64_t> &id, httplib::Response &response);
void image_buffer_start_cbor_get(httplib::Response &response);
void image_buffer_status_get(httplib::Response &response);
void detector_status_get(httplib::Response &response);
void image_pusher_status_get(httplib::Response &response);
void preview_pedestal_tiff_get(const std::optional<int32_t> &gainLevel, const std::optional<int32_t> &sc,
httplib::Response &response);
void preview_plot_bin_get(const std::optional<std::string> &type, const std::optional<std::string> &roi,
httplib::Response &response);
void preview_plot_get(const std::optional<std::string> &type, const std::optional<int32_t> &binning,
const std::optional<bool> &compression, const std::optional<float> &fill,
const std::optional<bool> &experimentalCoord, const std::optional<std::string> &azintUnit,
httplib::Response &response);
void config_mask_get(httplib::Response &response);
void config_mask_tiff_get(httplib::Response &response);
void config_user_mask_get(httplib::Response &response);
void config_user_mask_put(const httplib::Request &request, httplib::Response &response);
void config_user_mask_tiff_get(httplib::Response &response);
void config_user_mask_tiff_put(const httplib::Request &request, httplib::Response &response);
void config_roi_get(httplib::Response &response);
void config_roi_put(const org::openapitools::server::model::Roi_definitions &roiDefinitions,
httplib::Response &response);
void result_scan_get(httplib::Response &response);
void version_get(httplib::Response &response);
void config_instrument_get(httplib::Response &response);
void config_instrument_put(const org::openapitools::server::model::Instrument_metadata &instrumentMetadata,
httplib::Response &response);
void config_indexing_get(httplib::Response &response);
void config_indexing_put(const org::openapitools::server::model::Indexing_settings &indexingSettings,
httplib::Response &response);
void config_dark_mask_get(httplib::Response &response);
void config_dark_mask_put(const org::openapitools::server::model::Dark_mask_settings &darkMaskSettings,
httplib::Response &response);
void xfel_event_code_get(httplib::Response &response);
void xfel_pulse_id_get(httplib::Response &response);
void GetStaticFile(const httplib::Request &request, httplib::Response &response);
public:
JFJochBrokerHttp(const DiffractionExperiment &experiment,
const SpotFindingSettings &spot_finding_settings);
void AddDetectorSetup(const DetectorSetup &setup);
JFJochServices &Services();
JFJochBrokerHttp &FrontendDirectory(const std::string &directory);
void attach(httplib::Server &server);
~JFJochBrokerHttp() = default;