// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only // Using OpenAPI licensed with Apache License 2.0 #include "JFJochBrokerHttp.h" #include #include #include #include #include "Helpers.h" #include "../common/GitInfo.h" #include "../preview/JFJochTIFF.h" #include "OpenAPIConvert.h" #include "gen/model/Error_message.h" using namespace org::openapitools::server::model; namespace { template bool fromStringValue(const std::string &s, T &out) { std::istringstream is(s); is >> out; return !is.fail() && is.eof(); } template<> bool fromStringValue(const std::string &s, std::string &out) { out = s; return true; } template<> bool fromStringValue(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 std::optional parse_query_value(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; } std::optional parse_query_string(const httplib::Request &req, const char *name) { if (!req.has_param(name)) return std::nullopt; return req.get_param_value(name); } 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"; if (path.ends_with(".json")) return "application/json"; if (path.ends_with(".cbor")) return "application/cbor"; if (path.ends_with(".jpeg") || path.ends_with(".jpg")) return "image/jpeg"; if (path.ends_with(".tiff") || path.ends_with(".tif")) return "image/tiff"; if (path.ends_with(".png")) return "image/png"; if (path.ends_with(".svg")) return "image/svg+xml"; if (path.ends_with(".bin")) return "application/octet-stream"; return "application/octet-stream"; } inline bool read_file_to_string(const std::string &path, std::string &out) { std::ifstream f(path, std::ios::binary); if (!f) return false; std::ostringstream ss; ss << f.rdbuf(); out = ss.str(); return true; } } JFJochBrokerHttp::JFJochBrokerHttp(const DiffractionExperiment &experiment, const SpotFindingSettings &spot_finding_settings) : state_machine(experiment, services, logger, spot_finding_settings) { } void JFJochBrokerHttp::AddDetectorSetup(const DetectorSetup &setup) { state_machine.AddDetectorSetup(setup); logger.Info("Added detector {}", setup.GetDescription()); } JFJochServices &JFJochBrokerHttp::Services() { return services; } JFJochBrokerHttp &JFJochBrokerHttp::FrontendDirectory(const std::string &directory) { frontend_directory = directory; return *this; } std::pair JFJochBrokerHttp::handleParsingException(const std::exception &ex) const noexcept { try { throw; } catch (const nlohmann::detail::exception &e) { return {400, e.what()}; } catch (const org::openapitools::server::helpers::ValidationException &e) { return {400, e.what()}; } catch (const std::exception &e) { return {500, e.what()}; } } std::pair JFJochBrokerHttp::handleOperationException(const std::exception &ex) const noexcept { try { throw; } catch (const WrongDAQStateException &) { Error_message msg; msg.setMsg(ex.what()); msg.setReason("WrongDAQState"); nlohmann::json j; to_json(j, msg); return {500, j.dump()}; } catch (const std::exception &) { Error_message msg; msg.setMsg(ex.what()); msg.setReason("Other"); nlohmann::json j; to_json(j, msg); return {500, j.dump()}; } } void JFJochBrokerHttp::attach(httplib::Server &server) { register_routes(server); server.set_error_handler([](const httplib::Request &, httplib::Response &res) { res.status = 404; res.set_content("The requested method does not exist", "text/plain"); }); } void JFJochBrokerHttp::register_routes(httplib::Server &server) { auto bind_noarg = [this](auto method) { return [this, method](const httplib::Request &, httplib::Response &res) { try { (this->*method)(res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } }; }; auto bind_json = [this](auto method, auto model_tag) { 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) { auto [c, s] = handleParsingException(e); send_plain(res, c, s); } }; }; auto bind_req = [this](auto method) { return [this, method](const httplib::Request &req, httplib::Response &res) { try { (this->*method)(req, res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } }; }; server.Post("/cancel", bind_noarg(&JFJochBrokerHttp::cancel_post)); server.Get("/config/azim_int", bind_noarg(&JFJochBrokerHttp::config_azim_int_get)); server.Put("/config/azim_int", bind_json(&JFJochBrokerHttp::config_azim_int_put, Azim_int_settings{})); server.Get("/config/dark_mask", bind_noarg(&JFJochBrokerHttp::config_dark_mask_get)); server.Put("/config/dark_mask", bind_json(&JFJochBrokerHttp::config_dark_mask_put, Dark_mask_settings{})); server.Get("/config/detector", bind_noarg(&JFJochBrokerHttp::config_detector_get)); server.Put("/config/detector", bind_json(&JFJochBrokerHttp::config_detector_put, Detector_settings{})); server.Get("/config/file_writer", bind_noarg(&JFJochBrokerHttp::config_file_writer_get)); server.Put("/config/file_writer", bind_json(&JFJochBrokerHttp::config_file_writer_put, File_writer_settings{})); server.Post("/config/image_format/conversion", bind_noarg(&JFJochBrokerHttp::config_image_format_conversion_post)); server.Get("/config/image_format", bind_noarg(&JFJochBrokerHttp::config_image_format_get)); server.Put("/config/image_format", bind_json(&JFJochBrokerHttp::config_image_format_put, Image_format_settings{})); server.Post("/config/image_format/raw", bind_noarg(&JFJochBrokerHttp::config_image_format_raw_post)); server.Get("/config/indexing", bind_noarg(&JFJochBrokerHttp::config_indexing_get)); 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", [this](const httplib::Request &req, httplib::Response &res) { try { config_internal_generator_image_put(parse_query_value(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(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{})); server.Get("/config/select_detector", bind_noarg(&JFJochBrokerHttp::config_select_detector_get)); server.Put("/config/select_detector", bind_json(&JFJochBrokerHttp::config_select_detector_put, Detector_selection{})); server.Get("/config/spot_finding", bind_noarg(&JFJochBrokerHttp::config_spot_finding_get)); server.Put("/config/spot_finding", bind_json(&JFJochBrokerHttp::config_spot_finding_put, Spot_finding_settings{})); server.Get("/config/user_mask", bind_noarg(&JFJochBrokerHttp::config_user_mask_get)); server.Put("/config/user_mask", bind_req(&JFJochBrokerHttp::config_user_mask_put)); server.Get("/config/user_mask.tiff", bind_noarg(&JFJochBrokerHttp::config_user_mask_tiff_get)); server.Put("/config/user_mask.tiff", bind_req(&JFJochBrokerHttp::config_user_mask_tiff_put)); server.Get("/config/zeromq_metadata", bind_noarg(&JFJochBrokerHttp::config_zeromq_metadata_get)); server.Put("/config/zeromq_metadata", bind_json(&JFJochBrokerHttp::config_zeromq_metadata_put, Zeromq_metadata_settings{})); server.Get("/config/zeromq_preview", bind_noarg(&JFJochBrokerHttp::config_zeromq_preview_get)); server.Put("/config/zeromq_preview", bind_json(&JFJochBrokerHttp::config_zeromq_preview_put, Zeromq_preview_settings{})); server.Post("/deactivate", bind_noarg(&JFJochBrokerHttp::deactivate_post)); 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) { try { image_buffer_image_cbor_get(parse_query_value(req, "id"), res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } }); server.Get("/image_buffer/image.jpeg", [this](const httplib::Request &req, httplib::Response &res) { try { image_buffer_image_jpeg_get( parse_query_value(req, "id"), parse_query_value(req, "show_user_mask"), parse_query_value(req, "show_roi"), parse_query_value(req, "show_spots"), parse_query_value(req, "show_beam_center"), parse_query_value(req, "saturation"), parse_query_value(req, "jpeg_quality"), parse_query_value(req, "show_res_ring"), parse_query_string(req, "color"), parse_query_value(req, "show_res_est"), res ); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } }); server.Get("/image_buffer/image.tiff", [this](const httplib::Request &req, httplib::Response &res) { try { image_buffer_image_tiff_get(parse_query_value(req, "id"), res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); 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) { try { preview_pedestal_tiff_get(parse_query_value(req, "gain_level"), parse_query_value(req, "sc"), res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } }); server.Get("/preview/plot.bin", [this](const httplib::Request &req, httplib::Response &res) { try { preview_plot_bin_get(parse_query_string(req, "type"), parse_query_string(req, "roi"), res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); send_plain(res, c, s); } }); server.Get("/preview/plot", [this](const httplib::Request &req, httplib::Response &res) { try { preview_plot_get(parse_query_string(req, "type"), parse_query_value(req, "binning"), parse_query_value(req, "fill"), parse_query_value(req, "experimental_coord"), parse_query_string(req, "azint_unit"), res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); 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) { try { statistics_get(res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); 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) { try { wait_till_done_post(parse_query_value(req, "timeout"), res); } catch (const std::exception &e) { auto [c, s] = handleOperationException(e); 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)); } void JFJochBrokerHttp::cancel_post(httplib::Response &response) { state_machine.Cancel(); response.status = 200; } void JFJochBrokerHttp::deactivate_post(httplib::Response &response) { state_machine.Deactivate(); response.status = 200; } void JFJochBrokerHttp::initialize_post(httplib::Response &response) { state_machine.Initialize(); response.status = 200; } void JFJochBrokerHttp::start_post(const Dataset_settings &datasetSettings, httplib::Response &response) { nlohmann::json j = datasetSettings; logger.Info("Start {}", j.dump()); state_machine.Start(Convert(datasetSettings)); response.status = 200; } void JFJochBrokerHttp::status_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetStatus()), response); } void JFJochBrokerHttp::wait_till_done_post(const std::optional &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(); else status = state_machine.WaitTillMeasurementDone(std::chrono::seconds(timeout.value())); switch (status.state) { case JFJochState::Idle: response.status = 200; break; case JFJochState::Inactive: response.status = 502; break; case JFJochState::Error: throw WrongDAQStateException(status.message.value_or("Unknown error")); case JFJochState::Measuring: case JFJochState::Busy: case JFJochState::Calibration: response.status = 504; break; } } void JFJochBrokerHttp::trigger_post(httplib::Response &response) { state_machine.Trigger(); response.status = 200; } void JFJochBrokerHttp::pedestal_post(httplib::Response &response) { state_machine.Pedestal(); response.status = 200; } void JFJochBrokerHttp::config_detector_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetDetectorSettings()), response); } void JFJochBrokerHttp::config_detector_put(const Detector_settings &detectorSettings, httplib::Response &response) { state_machine.LoadDetectorSettings(Convert(detectorSettings)); response.status = 200; } void JFJochBrokerHttp::config_azim_int_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetRadialIntegrationSettings()), response); } void JFJochBrokerHttp::config_azim_int_put(const Azim_int_settings &radIntSettings, httplib::Response &response) { state_machine.SetRadialIntegrationSettings(Convert(radIntSettings)); response.status = 200; } void JFJochBrokerHttp::config_select_detector_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetDetectorsList()), response); } void JFJochBrokerHttp::config_select_detector_put(const Detector_selection &detectorSelection, httplib::Response &response) { state_machine.SelectDetector(detectorSelection.getId()); response.status = 200; } void JFJochBrokerHttp::config_spot_finding_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetSpotFindingSettings()), response); } void JFJochBrokerHttp::config_spot_finding_put(const Spot_finding_settings &spotFindingSettings, httplib::Response &response) { state_machine.SetSpotFindingSettings(Convert(spotFindingSettings)); response.status = 200; } void JFJochBrokerHttp::statistics_calibration_get(httplib::Response &response) { nlohmann::json j; for (const auto &d: Convert(state_machine.GetCalibrationStatistics())) j.push_back(d); logger.Info("Calibration statistics: {}", j.dump()); response.set_content(j.dump(), "application/json"); response.status = 200; } void JFJochBrokerHttp::statistics_data_collection_get(httplib::Response &response) { auto stats = state_machine.GetMeasurementStatistics(); if (stats) ProcessOutput(Convert(stats.value()), response); else response.status = 404; } 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::config_image_format_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetImageFormatSettings()), response); } void JFJochBrokerHttp::config_image_format_put(const Image_format_settings &imageFormatSettings, httplib::Response &response) { state_machine.LoadImageFormatSettings(Convert(imageFormatSettings)); response.status = 200; } void JFJochBrokerHttp::config_image_format_conversion_post(httplib::Response &response) { state_machine.ConvImageFormatSettings(); response.status = 200; } void JFJochBrokerHttp::config_image_format_raw_post(httplib::Response &response) { state_machine.RawImageFormatSettings(); response.status = 200; } void JFJochBrokerHttp::fpga_status_get(httplib::Response &response) { nlohmann::json j; for (const auto &d: Convert(state_machine.GetDeviceStatus())) j.push_back(d); response.set_content(j.dump(), "application/json"); response.status = 200; } void JFJochBrokerHttp::statistics_get(httplib::Response &response) { Jfjoch_statistics statistics; auto data_collection_statistics = state_machine.GetMeasurementStatistics(); if (data_collection_statistics) statistics.setMeasurement(Convert(data_collection_statistics.value())); statistics.setFpga(Convert(state_machine.GetDeviceStatus())); statistics.setCalibration(Convert(state_machine.GetCalibrationStatistics())); statistics.setBroker(Convert(state_machine.GetStatus())); auto det_status = state_machine.GetDetectorStatus(); if (det_status.has_value()) statistics.setDetector(Convert(det_status.value())); statistics.setDetectorSettings(Convert(state_machine.GetDetectorSettings())); statistics.setDetectorList(Convert(state_machine.GetDetectorsList())); statistics.setDataProcessingSettings(Convert(state_machine.GetSpotFindingSettings())); statistics.setInstrumentMetadata(Convert(state_machine.GetInstrumentMetadata())); statistics.setImageFormatSettings(Convert(state_machine.GetImageFormatSettings())); statistics.setPixelMask(Convert(state_machine.GetPixelMaskStatistics())); statistics.setRoi(Convert(state_machine.GetROIDefintion())); statistics.setFileWriterSettings(Convert(state_machine.GetFileWriterSettings())); statistics.setAzInt(Convert(state_machine.GetRadialIntegrationSettings())); statistics.setBuffer(Convert(state_machine.GetImageBufferStatus())); statistics.setIndexing(Convert(state_machine.GetIndexingSettings())); statistics.setDarkMask(Convert(state_machine.GetDarkMaskSettings())); statistics.setImagePusher(Convert(state_machine.GetImagePusherStatus())); auto zeromq_prev = state_machine.GetPreviewSocketSettings(); if (!zeromq_prev.address.empty()) statistics.setZeromqPreview(Convert(zeromq_prev)); auto zeromq_metadata = state_machine.GetMetadataSocketSettings(); if (!zeromq_metadata.address.empty()) statistics.setZeromqMetadata(Convert(zeromq_metadata)); nlohmann::json j = statistics; response.set_content(j.dump(), "application/json"); response.status = 200; } void JFJochBrokerHttp::config_zeromq_preview_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetPreviewSocketSettings()), response); } 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_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetMetadataSocketSettings()), response); } 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_internal_generator_image_put(const std::optional &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 &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; } void JFJochBrokerHttp::image_buffer_image_cbor_get(const std::optional &imageNumber, httplib::Response &response) { std::vector tmp_vector; state_machine.GetImageFromBuffer(tmp_vector, imageNumber.value_or(-1)); std::string s(reinterpret_cast(tmp_vector.data()), tmp_vector.size()); if (!s.empty()) response.set_content(s, "application/cbor"); else response.status = 404; } void JFJochBrokerHttp::image_buffer_image_jpeg_get(const std::optional &id, const std::optional &showUserMask, const std::optional &showRoi, const std::optional &showSpots, const std::optional &showBeamCenter, const std::optional &saturation, const std::optional &jpegQuality, const std::optional &showResRing, const std::optional &color, const std::optional &showResEst, httplib::Response &response) { int64_t image_id = id.value_or(ImageBuffer::MaxImage); PreviewImageSettings settings{}; settings.show_user_mask = showUserMask.value_or(false); settings.show_roi = showRoi.value_or(false); settings.show_spots = showSpots.value_or(true); settings.saturation_value = saturation; settings.background_value = 0.0; settings.jpeg_quality = jpegQuality.value_or(100); if (showResEst.value_or(false)) { settings.show_res_est = true; } else { settings.show_res_est = false; settings.resolution_ring = showResRing; } settings.scale = ConvertColorScale(color); settings.show_beam_center = showBeamCenter.value_or(true); settings.format = PreviewImageFormat::JPEG; std::string s = state_machine.GetPreviewJPEG(settings, image_id); if (!s.empty()) response.set_content(s, "image/jpeg"); else response.status = 404; } void JFJochBrokerHttp::image_buffer_image_tiff_get(const std::optional &id, httplib::Response &response) { int64_t image_id = id.value_or(ImageBuffer::MaxImage); std::string s = state_machine.GetPreviewTIFF(image_id); if (!s.empty()) response.set_content(s, "image/tiff"); else response.status = 404; } void JFJochBrokerHttp::image_buffer_start_cbor_get(httplib::Response &response) { std::vector tmp_vector; state_machine.GetStartMessageFromBuffer(tmp_vector); std::string s(reinterpret_cast(tmp_vector.data()), tmp_vector.size()); if (!s.empty()) response.set_content(s, "application/cbor"); else response.status = 404; } void JFJochBrokerHttp::image_buffer_status_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetImageBufferStatus()), response); } void JFJochBrokerHttp::image_pusher_status_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetImagePusherStatus()), response); } void JFJochBrokerHttp::config_mask_tiff_get(httplib::Response &response) { auto s = state_machine.GetFullPixelMaskTIFF(); response.set_content(s, "image/tiff"); response.status = 200; } void JFJochBrokerHttp::config_user_mask_tiff_get(httplib::Response &response) { auto s = state_machine.GetUserPixelMaskTIFF(); response.set_content(s, "image/tiff"); response.status = 200; } void JFJochBrokerHttp::config_user_mask_tiff_put(const httplib::Request &request, httplib::Response &response) { uint32_t cols, lines; auto v = ReadTIFFFromString32(request.body, cols, lines); state_machine.SetUserPixelMask(v); response.status = 200; } void JFJochBrokerHttp::config_mask_get(httplib::Response &response) { const auto v = state_machine.GetFullPixelMask(); response.set_content(reinterpret_cast(v.data()), v.size() * sizeof(uint32_t), "application/octet-stream"); response.status = 200; } void JFJochBrokerHttp::config_user_mask_get(httplib::Response &response) { const auto v = state_machine.GetUserPixelMask(); response.set_content(reinterpret_cast(v.data()), v.size() * sizeof(uint32_t), "application/octet-stream"); response.status = 200; } void JFJochBrokerHttp::config_user_mask_put(const httplib::Request &request, httplib::Response &response) { if (request.body.empty()) { response.status = 400; response.set_content("Request body cannot be empty", "text/plain"); return; } if (request.body.size() % 4 != 0) { response.status = 400; response.set_content("Request has to be 32-bit", "text/plain"); return; } std::vector v(request.body.size() / 4); std::memcpy(v.data(), request.body.data(), request.body.size()); state_machine.SetUserPixelMask(v); response.status = 200; } void JFJochBrokerHttp::version_get(httplib::Response &response) { response.set_content(jfjoch_version(), "text/plain"); response.status = 200; } void JFJochBrokerHttp::config_instrument_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetInstrumentMetadata()), response); } void JFJochBrokerHttp::config_instrument_put(const Instrument_metadata &instrumentMetadata, httplib::Response &response) { state_machine.LoadInstrumentMetadata(Convert(instrumentMetadata)); response.status = 200; } void JFJochBrokerHttp::config_indexing_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetIndexingSettings()), response); } void JFJochBrokerHttp::config_indexing_put(const Indexing_settings &indexingSettings, httplib::Response &response) { state_machine.SetIndexingSettings(Convert(indexingSettings)); response.status = 200; } void JFJochBrokerHttp::result_scan_get(httplib::Response &response) { auto ret = state_machine.GetScanResult(); if (ret.has_value()) ProcessOutput(Convert(ret.value()), response); else response.status = 404; } void JFJochBrokerHttp::config_dark_mask_put(const Dark_mask_settings &darkMaskSettings, httplib::Response &response) { state_machine.SetDarkMaskSettings(Convert(darkMaskSettings)); response.status = 200; } void JFJochBrokerHttp::config_dark_mask_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetDarkMaskSettings()), response); } void JFJochBrokerHttp::detector_status_get(httplib::Response &response) { auto out = state_machine.GetDetectorStatus(); if (out) ProcessOutput(Convert(out.value()), response); else response.status = 404; } void JFJochBrokerHttp::preview_pedestal_tiff_get(const std::optional &gainLevel, const std::optional &sc, httplib::Response &response) { if (!gainLevel) { response.status = 400; response.set_content("gain_level is required", "text/plain"); return; } std::string s = state_machine.GetPedestalTIFF(gainLevel.value(), sc ? sc.value() : 0); if (!s.empty()) response.set_content(s, "image/tiff"); else response.status = 404; } void JFJochBrokerHttp::preview_plot_bin_get(const std::optional &type, const std::optional &roi, httplib::Response &response) { std::vector ret; state_machine.GetPlotRaw(ret, ConvertPlotType(type), roi.value_or("")); if (ret.empty()) { response.status = 404; return; } response.set_content(reinterpret_cast(ret.data()), ret.size() * sizeof(float), "application/octet-stream"); response.status = 200; } void JFJochBrokerHttp::preview_plot_get(const std::optional &type, const std::optional &binning, const std::optional &fill, const std::optional &experimentalCoord, const std::optional &azintUnit, httplib::Response &response) { PlotAzintUnit unit = PlotAzintUnit::Q_recipA; if (azintUnit.has_value()) { if (azintUnit == "Q_recipA" || azintUnit == "q_recipa") unit = PlotAzintUnit::Q_recipA; else if (azintUnit == "d_A" || azintUnit == "d_a") unit = PlotAzintUnit::D_A; else if (azintUnit == "two_theta_deg") unit = PlotAzintUnit::TwoTheta_deg; } PlotRequest req{ .type = ConvertPlotType(type), .binning = 1, .experimental_coord = experimentalCoord.value_or(false), .azint_unit = unit, .fill_value = fill }; if (binning) { if (binning.value() < 0) { response.status = 400; response.set_content("Binning cannot be negative", "text/plain"); return; } req.binning = binning.value(); } auto plot = state_machine.GetPlots(req); ProcessOutput(Convert(plot), response); } void JFJochBrokerHttp::config_roi_get(httplib::Response &response) { ProcessOutput(Convert(state_machine.GetROIDefintion()), response); } void JFJochBrokerHttp::config_roi_put(const Roi_definitions &roiDefinitions, httplib::Response &response) { state_machine.SetROIDefinition(Convert(roiDefinitions)); response.status = 200; } void JFJochBrokerHttp::xfel_event_code_get(httplib::Response &response) { auto array = state_machine.GetXFELEventCode(); if (array.empty()) { response.status = 404; return; } nlohmann::json j = array; response.set_content(j.dump(), "application/json"); response.status = 200; } void JFJochBrokerHttp::xfel_pulse_id_get(httplib::Response &response) { auto array = state_machine.GetXFELPulseID(); if (array.empty()) { response.status = 404; return; } nlohmann::json j = array; response.set_content(j.dump(), "application/json"); response.status = 200; }