// SPDX-FileCopyrightText: 2025 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #pragma once #include "../reader/JFJochReader.h" #include "../common/BrokerStatus.h" #include "../common/ImageBuffer.h" #include "../common/ROIDefinition.h" // libcurl's easy handle, kept opaque here so this widely-included header does NOT pull in // . On Windows curl.h drags in , whose min/max/ERROR/LoadImage macros // would then clobber every viewer translation unit that includes this header (breaking std::min / // std::max in gemmi, the `ERROR` enum, the LoadImage() calls, ...). CURL is `typedef void` in // libcurl; is included only in JFJochHttpReader.cpp. using CURL = void; class JFJochHttpReader : public JFJochReader { mutable std::mutex http_mutex; std::string addr; // Persistent libcurl handle: created lazily on the first request (i.e. as soon as an address // is selected) and then reused for every subsequent request so the TCP/TLS connection is kept // alive. libcurl transparently reopens the connection if it was dropped (idle timeout, // server-side session limit, ...). curl_mutex serialises access because a single easy handle // must never be used from two threads at once; it is a leaf lock (nothing else is taken while // it is held), so it cannot deadlock with http_mutex. mutable std::mutex curl_mutex; mutable CURL *curl_handle = nullptr; std::optional last_image_buffer_counter; bool last_op_http_sync = false; // Cache of the (constant-per-acquisition) pixel mask, keyed on arm date, so the per-refresh // dataset rebuild reuses one shared mask instead of reconstructing it every tick. std::shared_ptr cached_pixel_mask; std::string cached_pixel_mask_arm_date; // Minimal HTTP result: whether a response arrived (transport-level success), the HTTP status // code, and the (possibly binary) response body. struct HttpResult { bool ok = false; long status = 0; std::string body; }; // Single libcurl request helper behind every endpoint call. method is "GET" or "PUT"; for PUT // the body is sent with the given content_type. HttpResult Request(const std::string &method, const std::string &path, const std::string &body = {}, const std::string &content_type = {}) const; // Tear down the persistent connection (on disconnect, when a new address is selected, and in // the destructor); the next Request transparently re-opens it. void ResetConnection() const; ImageBufferStatus GetImageBufferStatus() const; bool LoadImage_i(std::shared_ptr &dataset, DataMessage& message, std::vector &buffer, int64_t image_number, bool update_dataset) override; std::shared_ptr UpdateDataset_i(); std::vector GetPlot_i(const std::string &plot_type, float fill_value = 0.0) const; public: ~JFJochHttpReader() override; void ReadURL(const std::string& url); uint64_t GetNumberOfImages() const override; void Close() override; // Refresh dataset/plots if the image buffer changed since the last poll (returns nullptr if // unchanged). Always writes the current number of images to num_images_out. Does not load an image. std::shared_ptr RefreshDatasetIfChanged(int64_t &num_images_out); void UploadUserMask(const std::vector& mask); [[nodiscard]] ROIDefinition GetROIDefinitions() const; // GET /config/roi void UploadROIDefinitions(const ROIDefinition &rois) const; // PUT /config/roi BrokerStatus GetBrokerStatus() const; std::shared_ptr GetRawImage(int64_t image_number) override; std::vector ReadSpots(int64_t image) const override; };