From 52519fb45cea4f9932a7eb33e5df19a232527b51 Mon Sep 17 00:00:00 2001 From: leonarski_f Date: Tue, 30 Jan 2024 20:34:30 +0100 Subject: [PATCH] jfjoch_writer improvement --- CMakeLists.txt | 4 +- frame_serialize/CBORStream2Deserializer.cpp | 2 + frame_serialize/CBORStream2Deserializer.h | 4 +- tests/StreamWriterTest.cpp | 5 +- writer/CMakeLists.txt | 8 +- writer/JFJochWriterHttp.cpp | 59 +++----- writer/JFJochWriterHttp.h | 7 +- writer/REAMDE.md | 32 +++++ writer/StreamWriter.cpp | 129 +++++++++++------- writer/StreamWriter.h | 31 +++-- writer/ZMQImagePuller.cpp | 50 ++----- writer/ZMQImagePuller.h | 17 +-- writer/gen/api/DefaultApi.cpp | 48 +------ writer/gen/api/DefaultApi.h | 23 +--- writer/gen/model/Writer_statistics.cpp | 68 ++++++++- writer/gen/model/Writer_statistics.h | 25 +++- .../_wait_till_done_get_500_response.cpp | 91 ------------ .../model/_wait_till_done_get_500_response.h | 77 ----------- writer/jfjoch_writer.cpp | 78 +++++++++-- writer/jfjoch_writer_http.cpp | 75 ---------- writer/redoc-static.html | 58 ++------ writer/writer_api.yaml | 68 ++------- 22 files changed, 369 insertions(+), 590 deletions(-) create mode 100644 writer/REAMDE.md delete mode 100644 writer/gen/model/_wait_till_done_get_500_response.cpp delete mode 100644 writer/gen/model/_wait_till_done_get_500_response.h delete mode 100644 writer/jfjoch_writer_http.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e21724ce..da465437 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ ADD_SUBDIRECTORY(broker/pistache) IF (JFJOCH_WRITER_ONLY) MESSAGE(STATUS "Compiling HDF5 writer only") - SET(jfjoch_executables jfjoch_writer jfjoch_writer_http) + SET(jfjoch_executables jfjoch_writer) ELSE() ADD_SUBDIRECTORY(broker) ADD_SUBDIRECTORY(fpga) @@ -55,7 +55,7 @@ ELSE() ADD_SUBDIRECTORY(tests) ADD_SUBDIRECTORY(tools) # ADD_SUBDIRECTORY(export_images) - SET(jfjoch_executables jfjoch_broker jfjoch_writer jfjoch_writer_http CatchTest CompressionBenchmark HDF5DatasetWriteTest jfjoch_udp_simulator sls_detector_put sls_detector_get) + SET(jfjoch_executables jfjoch_broker jfjoch_writer CatchTest CompressionBenchmark HDF5DatasetWriteTest jfjoch_udp_simulator sls_detector_put sls_detector_get) ENDIF() ADD_CUSTOM_COMMAND(OUTPUT frontend_ui/build/index.html diff --git a/frame_serialize/CBORStream2Deserializer.cpp b/frame_serialize/CBORStream2Deserializer.cpp index a46c46ee..2c0afe7a 100644 --- a/frame_serialize/CBORStream2Deserializer.cpp +++ b/frame_serialize/CBORStream2Deserializer.cpp @@ -795,6 +795,8 @@ std::unique_lock ul(m); end_message = EndMessage{}; while (ProcessEndMessageElement(map_value)); break; + case Type::NONE: + break; } cborErr(cbor_value_leave_container(&value, &map_value)); diff --git a/frame_serialize/CBORStream2Deserializer.h b/frame_serialize/CBORStream2Deserializer.h index 3212b60e..336c0f69 100644 --- a/frame_serialize/CBORStream2Deserializer.h +++ b/frame_serialize/CBORStream2Deserializer.h @@ -15,11 +15,11 @@ class CBORStream2Deserializer { public: - enum class Type {START, END, IMAGE}; + enum class Type {START, END, IMAGE, NONE}; private: mutable std::mutex m; - Type msg_type; + Type msg_type = Type::NONE; DataMessage data_message; diff --git a/tests/StreamWriterTest.cpp b/tests/StreamWriterTest.cpp index c534e592..ba30f9c0 100644 --- a/tests/StreamWriterTest.cpp +++ b/tests/StreamWriterTest.cpp @@ -33,13 +33,16 @@ TEST_CASE("StreamWriterTest_ZMQ","[JFJochWriter]") { auto pusher_addr = pusher.GetAddress(); REQUIRE(pusher_addr.size() == 1); REQUIRE_NOTHROW(writer = std::make_unique(context, logger, pusher_addr[0])); - + CHECK (writer->GetStatistics().state == StreamWriterState::Idle); REQUIRE_NOTHROW(fpga_receiver_service.Start(x, nullptr)); REQUIRE_NOTHROW(writer->Run()); REQUIRE_NOTHROW(receiver_output = fpga_receiver_service.Stop()); CHECK(receiver_output.status.images_sent == 5); + CHECK(writer->GetStatistics().state == StreamWriterState::Idle); + CHECK(writer->GetStatistics().processed_images == 5); + CHECK(writer->GetStatistics().file_prefix == x.GetFilePrefix()); // HDF5 file can be opened std::unique_ptr file; diff --git a/writer/CMakeLists.txt b/writer/CMakeLists.txt index 32f60e7e..8020d3c1 100644 --- a/writer/CMakeLists.txt +++ b/writer/CMakeLists.txt @@ -22,10 +22,6 @@ ADD_LIBRARY(WriterAPI STATIC ${MODEL_SOURCES} gen/api/DefaultApi.cpp gen/api/Def TARGET_LINK_LIBRARIES(WriterAPI JFJochWriter pistache_static) TARGET_INCLUDE_DIRECTORIES(WriterAPI PUBLIC gen/model gen/api) -ADD_EXECUTABLE(jfjoch_writer jfjoch_writer.cpp) -TARGET_LINK_LIBRARIES(jfjoch_writer JFJochWriter) +ADD_EXECUTABLE(jfjoch_writer jfjoch_writer.cpp JFJochWriterHttp.h JFJochWriterHttp.cpp) +TARGET_LINK_LIBRARIES(jfjoch_writer JFJochWriter WriterAPI) INSTALL(TARGETS jfjoch_writer RUNTIME) - -ADD_EXECUTABLE(jfjoch_writer_http jfjoch_writer_http.cpp JFJochWriterHttp.h JFJochWriterHttp.cpp) -TARGET_LINK_LIBRARIES(jfjoch_writer_http JFJochWriter WriterAPI) -INSTALL(TARGETS jfjoch_writer_http RUNTIME) diff --git a/writer/JFJochWriterHttp.cpp b/writer/JFJochWriterHttp.cpp index c34f1deb..048bdfbd 100644 --- a/writer/JFJochWriterHttp.cpp +++ b/writer/JFJochWriterHttp.cpp @@ -2,52 +2,35 @@ #include "JFJochWriterHttp.h" -void JFJochWriterHttp::start_post(Pistache::Http::ResponseWriter &response) { - if (writer_future.valid()) - response.send(Pistache::Http::Code::Internal_Server_Error); - writer_future = writer.RunFuture(); - response.send(Pistache::Http::Code::Ok); -} +void JFJochWriterHttp::status_get(Pistache::Http::ResponseWriter &response) { + auto stat = writer.GetStatistics(); + org::openapitools::server::model::Writer_statistics resp_struct; + resp_struct.setNimages(stat.processed_images); + resp_struct.setPerformanceMBs(stat.performance_MBs); + resp_struct.setPerformanceHz(stat.performance_Hz); + resp_struct.setFilePrefix(stat.file_prefix); + switch (stat.state) { + case StreamWriterState::Idle: + resp_struct.setState("idle"); + break; + case StreamWriterState::Started: + resp_struct.setState("started"); + break; + case StreamWriterState::Receiving: + resp_struct.setState("receiving"); + break; + } -void JFJochWriterHttp::wait_till_done_get(Pistache::Http::ResponseWriter &response) { - wait_till_done(std::chrono::seconds(5), response); -} - -void JFJochWriterHttp::check_if_done_get(Pistache::Http::ResponseWriter &response) { - wait_till_done(std::chrono::seconds(0), response); + nlohmann::json j = resp_struct; + response.send(Pistache::Http::Code::Ok, j.dump(), MIME(Application, Json)); } void JFJochWriterHttp::cancel_post(Pistache::Http::ResponseWriter &response) { writer.Cancel(); + response.send(Pistache::Http::Code::Ok); } JFJochWriterHttp::JFJochWriterHttp(StreamWriter &in_writer, std::shared_ptr &rtr) : DefaultApi(rtr), writer(in_writer){ init(); } - -void JFJochWriterHttp::wait_till_done(const std::chrono::seconds &timeout, Pistache::Http::ResponseWriter &response) { - if (!writer_future.valid()) { - response.send(Pistache::Http::Code::Not_Found); - } else { - auto wait_resp = writer_future.wait_for(timeout); - if (wait_resp == std::future_status::timeout) { - response.send(Pistache::Http::Code::Gateway_Timeout); - } else { - try { - auto val = writer_future.get(); - org::openapitools::server::model::Writer_statistics resp_struct; - resp_struct.setNimages(val.image_puller_stats.processed_images); - resp_struct.setPerformanceMBs(val.image_puller_stats.performance_MBs); - resp_struct.setPerformanceHz(val.image_puller_stats.performance_Hz); - nlohmann::json j = resp_struct; - response.send(Pistache::Http::Code::Ok, j.dump(), MIME(Application, Json)); - } catch (const std::exception &e) { - org::openapitools::server::model::_wait_till_done_get_500_response resp_struct; - resp_struct.setMsg(e.what()); - nlohmann::json j = resp_struct; - response.send(Pistache::Http::Code::Internal_Server_Error, j.dump(), MIME(Application, Json)); - } - } - } -} diff --git a/writer/JFJochWriterHttp.h b/writer/JFJochWriterHttp.h index 75a90371..5224cc02 100644 --- a/writer/JFJochWriterHttp.h +++ b/writer/JFJochWriterHttp.h @@ -13,12 +13,7 @@ class JFJochWriterHttp : public org::openapitools::server::api::DefaultApi { StreamWriter& writer; - std::future writer_future; - - void wait_till_done(const std::chrono::seconds& timeout, Pistache::Http::ResponseWriter &response); - void start_post(Pistache::Http::ResponseWriter &response) override; - void wait_till_done_get(Pistache::Http::ResponseWriter &response) override; - void check_if_done_get(Pistache::Http::ResponseWriter &response) override; + void status_get(Pistache::Http::ResponseWriter &response) override; void cancel_post(Pistache::Http::ResponseWriter &response) override; public: JFJochWriterHttp(StreamWriter& writer, std::shared_ptr &rtr); diff --git a/writer/REAMDE.md b/writer/REAMDE.md new file mode 100644 index 00000000..92612ebf --- /dev/null +++ b/writer/REAMDE.md @@ -0,0 +1,32 @@ +# NXmx compliant writer + +## Acknowledgements +* Zdenek Matej (MAX IV) +* Felix Engelmann (MAX IV) +for testing and multiple improvement suggestions. + +## Running directory +Writer needs to be running in base directory for writing files - `file_prefix` will be always relative in regard to writer running directory. +Writer detects and protects for basic security issues, like `file_prefix` starting with a slash, or starting with `../`, or containing `/../`. + +## Usage +Writer needs to be started as a background service, with the following command: +``` +jfjoch_writer_service
{
} +``` +for example: +``` +jfjoch_writer_service tcp://dcu-address:5400 5232 tcp://0.0.0.0:3456 +``` +## HTTP interface +Writer has dedicated status interface via HTTP. It allows for two operations: +* ***check state of the writer*** to check if the writer is properly synchronized with DCU (e.g., that `file_prefix` agrees with what was set on the DCU) and monitor progress. +* ***cancel writing*** this will close all the HDF5 files being written and restart writer - the option should be used only if DCU process was terminated or disconnected, it SHOULD NOT be used as standard cancellation procedure (when DCU received cancel command it should properly finish writing as well) +## Republish +Republish creates a PULL socket on the writer, where all the messages are republished for further use by data analysis pipeline. +Republish is non-blocking, so if there is no receiver on other end or the sending queue is full - images won't be republished. +In case of START/END messages republishing will attempt sending for 100 ms, but if send times out it won't be retried. + +Republish address is optional, if omitted this functionality is not enabled. +## NXmx extensions +There are custom extension to NXmx format. These will be documented in the future. \ No newline at end of file diff --git a/writer/StreamWriter.cpp b/writer/StreamWriter.cpp index ff2182bb..c59d7467 100644 --- a/writer/StreamWriter.cpp +++ b/writer/StreamWriter.cpp @@ -7,61 +7,75 @@ #include "MakeDirectory.h" StreamWriter::StreamWriter(ZMQContext &context, Logger &in_logger, const std::string &zmq_addr, const std::string& repub_address) -: image_puller(context, repub_address), logger(in_logger), running(false) { +: image_puller(context, repub_address), logger(in_logger) { image_puller.Connect(zmq_addr); logger.Info("Connected via ZMQ to {}", zmq_addr); } -void StreamWriter::StartDataCollection() { - image_puller.WaitForImage(); - while (image_puller.GetFrameType() != CBORStream2Deserializer::Type::START) { - logger.Error("Expected START image"); - image_puller.WaitForImage(); - } - start_message = image_puller.GetStartMessage(); - logger.Info("Starting writing for dataset {} of {} images", start_message.file_prefix, - start_message.number_of_images); - - MakeDirectory(start_message.file_prefix); -} - void StreamWriter::CollectImages(std::vector &v) { + bool run = true; + while (run && (image_puller.GetFrameType() != CBORStream2Deserializer::Type::START)) + run = image_puller.WaitForImage(); + + if (!run) + return; + + StartMessage start_message = image_puller.GetStartMessage(); + logger.Info("Starting writing for dataset {} of {} images", start_message.file_prefix, start_message.number_of_images); + state = StreamWriterState::Started; + processed_images = 0; + processed_image_size = 0; + file_prefix = start_message.file_prefix; + + CheckPath(start_message.file_prefix); + MakeDirectory(start_message.file_prefix); HDF5Writer writer(start_message); - image_puller.WaitForImage(); - while (image_puller.GetFrameType() == CBORStream2Deserializer::Type::IMAGE) { + + bool first_image = true; + run = image_puller.WaitForImage(); + while (run && (image_puller.GetFrameType() == CBORStream2Deserializer::Type::IMAGE)) { + if (first_image) { + state = StreamWriterState::Receiving; + start_time = std::chrono::system_clock::now(); + first_image = false; + } auto image_array = image_puller.GetDataMessage(); writer.Write(image_array); - image_puller.WaitForImage(); + processed_images++; + processed_image_size += image_array.image.size; + run = image_puller.WaitForImage(); } + + if (image_puller.GetFrameType() == CBORStream2Deserializer::Type::END) { + EndMessage end_message = image_puller.GetEndMessage(); + end_time = std::chrono::system_clock::now(); + if (end_message.write_master_file) + HDF5Metadata::NXmx(start_message, end_message); + state = StreamWriterState::Idle; + } + writer.GetStatistics(v); } -void StreamWriter::EndDataCollection() { - while (image_puller.GetFrameType() != CBORStream2Deserializer::Type::END) { - logger.Error("Expected END image"); - image_puller.WaitForImage(); - } - EndMessage end_message = image_puller.GetEndMessage(); - if (end_message.write_master_file) - HDF5Metadata::NXmx(start_message, end_message); -} - - void StreamWriter::Cancel() { + logger.Info("Cancel requested"); image_puller.Abort(); } -StreamWriterStatistics StreamWriter::Run() { - { - std::unique_lock ul(m); - if (running) - throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Writer is busy"); - running = true; - } +void StreamWriter::CheckPath(const std::string &s) { + if (s.front() == '/') + throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, + "Path cannot start with slash"); + if (s.substr(0,3) == "../") + throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, + "Path cannot start with ../"); + if (s.find("/../") != std::string::npos) + throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, + "Path cannot contain /../"); +} - StreamWriterStatistics ret; - start_message = StartMessage(); - StartDataCollection(); +StreamWriterOutput StreamWriter::Run() { + StreamWriterOutput ret; try { CollectImages(ret.data_file_stats); } catch (JFJochException &e) { @@ -69,23 +83,34 @@ StreamWriterStatistics StreamWriter::Run() { // End data collection will consume all images till the end logger.ErrorException(e); } - EndDataCollection(); - ret.image_puller_stats = image_puller.GetStatistics(); + ret.image_puller_stats = GetStatistics(); logger.Info("Write task done. Images = {} Throughput = {:.0f} MB/s Frame rate = {:.0f} Hz", ret.image_puller_stats.processed_images, ret.image_puller_stats.performance_MBs, ret.image_puller_stats.performance_Hz); - - { - std::unique_lock ul(m); - running = false; - } - return ret; } -std::future StreamWriter::RunFuture() { - std::unique_lock ul(m); - if (running) - throw JFJochException(JFJochExceptionCategory::WrongDAQState, "Writer is busy"); - return std::async(std::launch::async, &StreamWriter::Run, this); -} \ No newline at end of file +StreamWriterStatistics StreamWriter::GetStatistics() const { + float perf_MBs = 0.0f, perf_Hz = 0.0f; + + + if ((state != StreamWriterState::Started) && (processed_images > 0)) { + int64_t time_us; + if (state == StreamWriterState::Idle) + time_us = std::chrono::duration_cast(end_time - start_time).count(); + else + time_us = std::chrono::duration_cast(std::chrono::system_clock::now() - start_time).count(); + + // MByte/s ==> Byte/us + perf_MBs = static_cast(processed_image_size) / static_cast(time_us); + perf_Hz = static_cast(processed_images) * 1e6f / static_cast(time_us); + } + + return { + .processed_images = processed_images, + .performance_MBs = perf_MBs, + .performance_Hz = perf_Hz, + .file_prefix = file_prefix, + .state = state + }; +} diff --git a/writer/StreamWriter.h b/writer/StreamWriter.h index d1f1a428..a4dd327e 100644 --- a/writer/StreamWriter.h +++ b/writer/StreamWriter.h @@ -8,27 +8,40 @@ #include "ZMQImagePuller.h" #include "HDF5DataFile.h" +enum class StreamWriterState {Idle, Started, Receiving}; + struct StreamWriterStatistics { - ZMQImagePullerStatistics image_puller_stats; + uint64_t processed_images; + float performance_MBs; + float performance_Hz; + std::string file_prefix; + StreamWriterState state; +}; + +struct StreamWriterOutput { + StreamWriterStatistics image_puller_stats; std::vector data_file_stats; }; class StreamWriter { - bool running; - std::mutex m; + StreamWriterState state = StreamWriterState::Idle; + + std::atomic processed_images; + std::atomic processed_image_size; + std::chrono::time_point start_time; + std::chrono::time_point end_time; + std::string file_prefix; ZMQImagePuller image_puller; Logger &logger; - StartMessage start_message; - - void StartDataCollection(); + void CheckPath(const std::string& s); void CollectImages(std::vector &v); - void EndDataCollection(); public: StreamWriter(ZMQContext& context, Logger &logger, const std::string& zmq_addr, const std::string& repub_address = ""); - StreamWriterStatistics Run(); - std::future RunFuture(); + StreamWriterOutput Run(); void Cancel(); + + StreamWriterStatistics GetStatistics() const; }; diff --git a/writer/ZMQImagePuller.cpp b/writer/ZMQImagePuller.cpp index b21bb65a..9f43f31d 100644 --- a/writer/ZMQImagePuller.cpp +++ b/writer/ZMQImagePuller.cpp @@ -12,6 +12,7 @@ ZMQImagePuller::ZMQImagePuller(ZMQContext &context, const std::string &repub_add if (!repub_address.empty()) { repub_socket = std::make_unique(context, ZMQSocketType::Pull); repub_socket->SendWaterMark(100); + repub_socket->SendTimeout(std::chrono::milliseconds(100)); repub_socket->Bind(repub_address); } } @@ -34,37 +35,33 @@ void ZMQImagePuller::Abort() { abort = 1; } -void ZMQImagePuller::WaitForImage() { +bool ZMQImagePuller::WaitForImage() { int64_t msg_size = -1; while ((msg_size < 0) && (!abort)) msg_size = socket.Receive(zmq_recv_buffer, true, true); if (msg_size > 0) { - // Republishing is always non-blocking - if (repub_socket) - repub_socket->Send(zmq_recv_buffer.data(), zmq_recv_buffer.size(), false); deserializer.Process(zmq_recv_buffer); if (deserializer.GetType() == CBORStream2Deserializer::Type::START) { start_message = std::make_unique(deserializer.GetStartMessage()); end_message.reset(); - processed_images = 0; - processed_size = 0; - first_image = true; - } else if (deserializer.GetType() == CBORStream2Deserializer::Type::END) { + } else if (deserializer.GetType() == CBORStream2Deserializer::Type::END) end_message = std::make_unique(deserializer.GetEndMessage()); - end_time = std::chrono::system_clock::now(); - } else if (deserializer.GetType() == CBORStream2Deserializer::Type::IMAGE) { - if (first_image) { - start_time = std::chrono::system_clock::now(); - first_image = false; - } - processed_images++; + else if (deserializer.GetType() == CBORStream2Deserializer::Type::IMAGE) deserialized_image_message = std::make_unique(deserializer.GetDataMessage()); - processed_size += deserialized_image_message->image.size; + + if (repub_socket) { + // Republishing is non-blocking for images + // and blocking (with 100ms timeout) for START/END + repub_socket->Send(zmq_recv_buffer.data(), zmq_recv_buffer.size(), + deserializer.GetType() != CBORStream2Deserializer::Type::IMAGE); } - } + + return true; + } else + return false; // This is all kinds of error } const DataMessage &ZMQImagePuller::GetDataMessage() const { @@ -76,28 +73,11 @@ const DataMessage &ZMQImagePuller::GetDataMessage() const { CBORStream2Deserializer::Type ZMQImagePuller::GetFrameType() const { if (abort) - return CBORStream2Deserializer::Type::END; + return CBORStream2Deserializer::Type::NONE; else return deserializer.GetType(); } -ZMQImagePullerStatistics ZMQImagePuller::GetStatistics() { - float perf_MBs = 0.0f, perf_Hz = 0.0f; - - if (processed_images > 0) { - auto time_us = std::chrono::duration_cast(end_time - start_time); - // MByte/s ==> Byte/us - perf_MBs = static_cast(processed_size) / static_cast(time_us.count()); - perf_Hz = static_cast(processed_images) * 1e6f / static_cast(time_us.count()); - } - - return { - .processed_images = processed_images, - .performance_MBs = perf_MBs, - .performance_Hz = perf_Hz - }; -} - StartMessage ZMQImagePuller::GetStartMessage() const { if (start_message) return *start_message; diff --git a/writer/ZMQImagePuller.h b/writer/ZMQImagePuller.h index bcf4126c..a28789b0 100644 --- a/writer/ZMQImagePuller.h +++ b/writer/ZMQImagePuller.h @@ -10,25 +10,15 @@ #include "../common/SpotToSave.h" #include "../frame_serialize/CBORStream2Deserializer.h" -struct ZMQImagePullerStatistics { - uint64_t processed_images; - float performance_MBs; - float performance_Hz; -}; - class ZMQImagePuller { std::vector zmq_recv_buffer; CBORStream2Deserializer deserializer; - constexpr const static uint32_t ReceiverWaterMark = 100; + constexpr const static uint32_t ReceiverWaterMark = 500; // ZeroMQ receive timeout allows to check for abort value from time to time constexpr const static auto ReceiveTimeout = std::chrono::milliseconds(100); - size_t processed_size = 0; - size_t processed_images = 0; - bool first_image = false; - std::chrono::time_point start_time; - std::chrono::time_point end_time; + ZMQSocket socket; std::string addr; volatile int abort = 0; @@ -44,10 +34,9 @@ public: void Disconnect(); void Abort(); - void WaitForImage(); + [[nodiscard]] bool WaitForImage(); const DataMessage &GetDataMessage() const; [[nodiscard]] CBORStream2Deserializer::Type GetFrameType() const; - [[nodiscard]] ZMQImagePullerStatistics GetStatistics(); [[nodiscard]] StartMessage GetStartMessage() const; [[nodiscard]] EndMessage GetEndMessage() const; }; diff --git a/writer/gen/api/DefaultApi.cpp b/writer/gen/api/DefaultApi.cpp index 2d2f4c31..4d65932b 100644 --- a/writer/gen/api/DefaultApi.cpp +++ b/writer/gen/api/DefaultApi.cpp @@ -34,9 +34,7 @@ void DefaultApi::setupRoutes() { using namespace Pistache::Rest; Routes::Post(*router, base + "/cancel", Routes::bind(&DefaultApi::cancel_post_handler, this)); - Routes::Get(*router, base + "/check_if_done", Routes::bind(&DefaultApi::check_if_done_get_handler, this)); - Routes::Post(*router, base + "/start", Routes::bind(&DefaultApi::start_post_handler, this)); - Routes::Get(*router, base + "/wait_till_done", Routes::bind(&DefaultApi::wait_till_done_get_handler, this)); + Routes::Get(*router, base + "/status", Routes::bind(&DefaultApi::status_get_handler, this)); // Default handler, called when a route is not found router->addCustomHandler(Routes::bind(&DefaultApi::default_api_default_handler, this)); @@ -80,52 +78,12 @@ void DefaultApi::cancel_post_handler(const Pistache::Rest::Request &, Pistache:: } } -void DefaultApi::check_if_done_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { +void DefaultApi::status_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { try { try { - this->check_if_done_get(response); - } catch (Pistache::Http::HttpError &e) { - response.send(static_cast(e.code()), e.what()); - return; - } catch (std::exception &e) { - const std::pair errorInfo = this->handleOperationException(e); - response.send(errorInfo.first, errorInfo.second); - return; - } - - } catch (std::exception &e) { - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); - } - -} -void DefaultApi::start_post_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { - try { - - - try { - this->start_post(response); - } catch (Pistache::Http::HttpError &e) { - response.send(static_cast(e.code()), e.what()); - return; - } catch (std::exception &e) { - const std::pair errorInfo = this->handleOperationException(e); - response.send(errorInfo.first, errorInfo.second); - return; - } - - } catch (std::exception &e) { - response.send(Pistache::Http::Code::Internal_Server_Error, e.what()); - } - -} -void DefaultApi::wait_till_done_get_handler(const Pistache::Rest::Request &, Pistache::Http::ResponseWriter response) { - try { - - - try { - this->wait_till_done_get(response); + this->status_get(response); } catch (Pistache::Http::HttpError &e) { response.send(static_cast(e.code()), e.what()); return; diff --git a/writer/gen/api/DefaultApi.h b/writer/gen/api/DefaultApi.h index dc61da02..348d0aaa 100644 --- a/writer/gen/api/DefaultApi.h +++ b/writer/gen/api/DefaultApi.h @@ -27,7 +27,6 @@ #include #include "Writer_statistics.h" -#include "_wait_till_done_get_500_response.h" namespace org::openapitools::server::api { @@ -44,9 +43,7 @@ private: void setupRoutes(); void cancel_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - void check_if_done_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - void start_post_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); - void wait_till_done_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); + void status_get_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); void default_api_default_handler(const Pistache::Rest::Request &request, Pistache::Http::ResponseWriter response); const std::shared_ptr router; @@ -73,26 +70,12 @@ private: /// virtual void cancel_post(Pistache::Http::ResponseWriter &response) = 0; /// - /// - /// - /// - /// Check if detector is done (similar to wait_till_done, but no timeout) - /// - virtual void check_if_done_get(Pistache::Http::ResponseWriter &response) = 0; - /// - /// Start writer + /// Get writer status /// /// /// /// - virtual void start_post(Pistache::Http::ResponseWriter &response) = 0; - /// - /// Will timeout after 5 seconds If multiple parallel calls are made - only one will result in success - /// - /// - /// Wait for writing done - /// - virtual void wait_till_done_get(Pistache::Http::ResponseWriter &response) = 0; + virtual void status_get(Pistache::Http::ResponseWriter &response) = 0; }; diff --git a/writer/gen/model/Writer_statistics.cpp b/writer/gen/model/Writer_statistics.cpp index aabbf847..e9edc7a7 100644 --- a/writer/gen/model/Writer_statistics.cpp +++ b/writer/gen/model/Writer_statistics.cpp @@ -21,12 +21,16 @@ namespace org::openapitools::server::model Writer_statistics::Writer_statistics() { - m_Nimages = 0; + m_Nimages = 0L; m_NimagesIsSet = false; m_Performance_MBs = 0.0f; m_Performance_MBsIsSet = false; m_Performance_Hz = 0.0f; m_Performance_HzIsSet = false; + m_File_prefix = ""; + m_File_prefixIsSet = false; + m_State = ""; + m_StateIsSet = false; } @@ -49,7 +53,7 @@ bool Writer_statistics::validate(std::stringstream& msg, const std::string& path bool success = true; const std::string _pathPrefix = pathPrefix.empty() ? "Writer_statistics" : pathPrefix; - + return success; } @@ -65,7 +69,13 @@ bool Writer_statistics::operator==(const Writer_statistics& rhs) const ((!performanceMBsIsSet() && !rhs.performanceMBsIsSet()) || (performanceMBsIsSet() && rhs.performanceMBsIsSet() && getPerformanceMBs() == rhs.getPerformanceMBs())) && - ((!performanceHzIsSet() && !rhs.performanceHzIsSet()) || (performanceHzIsSet() && rhs.performanceHzIsSet() && getPerformanceHz() == rhs.getPerformanceHz())) + ((!performanceHzIsSet() && !rhs.performanceHzIsSet()) || (performanceHzIsSet() && rhs.performanceHzIsSet() && getPerformanceHz() == rhs.getPerformanceHz())) && + + + ((!filePrefixIsSet() && !rhs.filePrefixIsSet()) || (filePrefixIsSet() && rhs.filePrefixIsSet() && getFilePrefix() == rhs.getFilePrefix())) && + + + ((!stateIsSet() && !rhs.stateIsSet()) || (stateIsSet() && rhs.stateIsSet() && getState() == rhs.getState())) ; } @@ -84,6 +94,10 @@ void to_json(nlohmann::json& j, const Writer_statistics& o) j["performance_MBs"] = o.m_Performance_MBs; if(o.performanceHzIsSet()) j["performance_Hz"] = o.m_Performance_Hz; + if(o.filePrefixIsSet()) + j["file_prefix"] = o.m_File_prefix; + if(o.stateIsSet()) + j["state"] = o.m_State; } @@ -104,14 +118,24 @@ void from_json(const nlohmann::json& j, Writer_statistics& o) j.at("performance_Hz").get_to(o.m_Performance_Hz); o.m_Performance_HzIsSet = true; } + if(j.find("file_prefix") != j.end()) + { + j.at("file_prefix").get_to(o.m_File_prefix); + o.m_File_prefixIsSet = true; + } + if(j.find("state") != j.end()) + { + j.at("state").get_to(o.m_State); + o.m_StateIsSet = true; + } } -int32_t Writer_statistics::getNimages() const +int64_t Writer_statistics::getNimages() const { return m_Nimages; } -void Writer_statistics::setNimages(int32_t const value) +void Writer_statistics::setNimages(int64_t const value) { m_Nimages = value; m_NimagesIsSet = true; @@ -158,6 +182,40 @@ void Writer_statistics::unsetPerformance_Hz() { m_Performance_HzIsSet = false; } +std::string Writer_statistics::getFilePrefix() const +{ + return m_File_prefix; +} +void Writer_statistics::setFilePrefix(std::string const& value) +{ + m_File_prefix = value; + m_File_prefixIsSet = true; +} +bool Writer_statistics::filePrefixIsSet() const +{ + return m_File_prefixIsSet; +} +void Writer_statistics::unsetFile_prefix() +{ + m_File_prefixIsSet = false; +} +std::string Writer_statistics::getState() const +{ + return m_State; +} +void Writer_statistics::setState(std::string const& value) +{ + m_State = value; + m_StateIsSet = true; +} +bool Writer_statistics::stateIsSet() const +{ + return m_StateIsSet; +} +void Writer_statistics::unsetState() +{ + m_StateIsSet = false; +} } // namespace org::openapitools::server::model diff --git a/writer/gen/model/Writer_statistics.h b/writer/gen/model/Writer_statistics.h index 5c8326e2..ad3ed75a 100644 --- a/writer/gen/model/Writer_statistics.h +++ b/writer/gen/model/Writer_statistics.h @@ -19,6 +19,7 @@ #define Writer_statistics_H_ +#include #include namespace org::openapitools::server::model @@ -60,8 +61,8 @@ public: /// /// Number of images written /// - int32_t getNimages() const; - void setNimages(int32_t const value); + int64_t getNimages() const; + void setNimages(int64_t const value); bool nimagesIsSet() const; void unsetNimages(); /// @@ -78,16 +79,34 @@ public: void setPerformanceHz(float const value); bool performanceHzIsSet() const; void unsetPerformance_Hz(); + /// + /// File prefix for the last written dataset + /// + std::string getFilePrefix() const; + void setFilePrefix(std::string const& value); + bool filePrefixIsSet() const; + void unsetFile_prefix(); + /// + /// + /// + std::string getState() const; + void setState(std::string const& value); + bool stateIsSet() const; + void unsetState(); friend void to_json(nlohmann::json& j, const Writer_statistics& o); friend void from_json(const nlohmann::json& j, Writer_statistics& o); protected: - int32_t m_Nimages; + int64_t m_Nimages; bool m_NimagesIsSet; float m_Performance_MBs; bool m_Performance_MBsIsSet; float m_Performance_Hz; bool m_Performance_HzIsSet; + std::string m_File_prefix; + bool m_File_prefixIsSet; + std::string m_State; + bool m_StateIsSet; }; diff --git a/writer/gen/model/_wait_till_done_get_500_response.cpp b/writer/gen/model/_wait_till_done_get_500_response.cpp deleted file mode 100644 index 92e00483..00000000 --- a/writer/gen/model/_wait_till_done_get_500_response.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/** -* Jungfraujoch writer -* Jungfraujoch Writer Web API -* -* The version of the OpenAPI document: 1.0.0 -* -* -* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). -* https://openapi-generator.tech -* Do not edit the class manually. -*/ - - -#include "_wait_till_done_get_500_response.h" -#include "Helpers.h" - -#include - -namespace org::openapitools::server::model -{ - -_wait_till_done_get_500_response::_wait_till_done_get_500_response() -{ - m_Msg = ""; - -} - -void _wait_till_done_get_500_response::validate() const -{ - std::stringstream msg; - if (!validate(msg)) - { - throw org::openapitools::server::helpers::ValidationException(msg.str()); - } -} - -bool _wait_till_done_get_500_response::validate(std::stringstream& msg) const -{ - return validate(msg, ""); -} - -bool _wait_till_done_get_500_response::validate(std::stringstream& msg, const std::string& pathPrefix) const -{ - bool success = true; - const std::string _pathPrefix = pathPrefix.empty() ? "_wait_till_done_get_500_response" : pathPrefix; - - - return success; -} - -bool _wait_till_done_get_500_response::operator==(const _wait_till_done_get_500_response& rhs) const -{ - return - - - (getMsg() == rhs.getMsg()) - - - ; -} - -bool _wait_till_done_get_500_response::operator!=(const _wait_till_done_get_500_response& rhs) const -{ - return !(*this == rhs); -} - -void to_json(nlohmann::json& j, const _wait_till_done_get_500_response& o) -{ - j = nlohmann::json(); - j["msg"] = o.m_Msg; - -} - -void from_json(const nlohmann::json& j, _wait_till_done_get_500_response& o) -{ - j.at("msg").get_to(o.m_Msg); - -} - -std::string _wait_till_done_get_500_response::getMsg() const -{ - return m_Msg; -} -void _wait_till_done_get_500_response::setMsg(std::string const& value) -{ - m_Msg = value; -} - - -} // namespace org::openapitools::server::model - diff --git a/writer/gen/model/_wait_till_done_get_500_response.h b/writer/gen/model/_wait_till_done_get_500_response.h deleted file mode 100644 index d5d4ce62..00000000 --- a/writer/gen/model/_wait_till_done_get_500_response.h +++ /dev/null @@ -1,77 +0,0 @@ -/** -* Jungfraujoch writer -* Jungfraujoch Writer Web API -* -* The version of the OpenAPI document: 1.0.0 -* -* -* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). -* https://openapi-generator.tech -* Do not edit the class manually. -*/ -/* - * _wait_till_done_get_500_response.h - * - * - */ - -#ifndef _wait_till_done_get_500_response_H_ -#define _wait_till_done_get_500_response_H_ - - -#include -#include - -namespace org::openapitools::server::model -{ - -/// -/// -/// -class _wait_till_done_get_500_response -{ -public: - _wait_till_done_get_500_response(); - virtual ~_wait_till_done_get_500_response() = default; - - - /// - /// Validate the current data in the model. Throws a ValidationException on failure. - /// - void validate() const; - - /// - /// Validate the current data in the model. Returns false on error and writes an error - /// message into the given stringstream. - /// - bool validate(std::stringstream& msg) const; - - /// - /// Helper overload for validate. Used when one model stores another model and calls it's validate. - /// Not meant to be called outside that case. - /// - bool validate(std::stringstream& msg, const std::string& pathPrefix) const; - - bool operator==(const _wait_till_done_get_500_response& rhs) const; - bool operator!=(const _wait_till_done_get_500_response& rhs) const; - - ///////////////////////////////////////////// - /// _wait_till_done_get_500_response members - - /// - /// Error message - /// - std::string getMsg() const; - void setMsg(std::string const& value); - - friend void to_json(nlohmann::json& j, const _wait_till_done_get_500_response& o); - friend void from_json(const nlohmann::json& j, _wait_till_done_get_500_response& o); -protected: - std::string m_Msg; - - -}; - -} // namespace org::openapitools::server::model - -#endif /* _wait_till_done_get_500_response_H_ */ diff --git a/writer/jfjoch_writer.cpp b/writer/jfjoch_writer.cpp index e2828df3..6b543199 100644 --- a/writer/jfjoch_writer.cpp +++ b/writer/jfjoch_writer.cpp @@ -1,23 +1,83 @@ // Copyright (2019-2023) Paul Scherrer Institute +#include #include "../common/Logger.h" +#include "JFJochWriterHttp.h" #include "StreamWriter.h" -volatile bool quitok = false; +static Pistache::Http::Endpoint *httpEndpoint; +static StreamWriter *writer; +volatile static bool quitok = false; + +static void sigHandler (int sig){ + switch(sig){ + case SIGINT: + case SIGQUIT: + case SIGTERM: + case SIGHUP: + default: + httpEndpoint->shutdown(); + quitok = true; + writer->Cancel(); + break; + } +} + +static void setUpUnixSignals(std::vector quitSignals) { + sigset_t blocking_mask; + sigemptyset(&blocking_mask); + for (auto sig : quitSignals) + sigaddset(&blocking_mask, sig); + + struct sigaction sa; + sa.sa_handler = sigHandler; + sa.sa_mask = blocking_mask; + sa.sa_flags = 0; + + for (auto sig : quitSignals) + sigaction(sig, &sa, nullptr); +} int main(int argc, char **argv) { RegisterHDF5Filter(); - Logger logger("jfjoch_writer"); + static Logger logger("jfjoch_writer_http"); - if (argc < 2) { - logger.Error("Usage ./jfjoch_writer "); + if ((argc != 3) && (argc != 4)) { + logger.Error("Usage ./jfjoch_writer_http {}"); exit(EXIT_FAILURE); } - ZMQContext context; - StreamWriter writer(context, logger, argv[1]); + uint16_t http_port = atoi(argv[2]); + std::string repub_address; + if (argc == 4) + repub_address = argv[3]; - while (!quitok) - writer.Run(); -} \ No newline at end of file + ZMQContext context; + Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(http_port)); + + writer = new StreamWriter(context, logger, argv[1], repub_address); + httpEndpoint = new Pistache::Http::Endpoint(addr); + + auto router = std::make_shared(); + + auto opts = Pistache::Http::Endpoint::options().threads(8); + opts.flags(Pistache::Tcp::Options::ReuseAddr); + httpEndpoint->init(opts); + + std::vector sigs{SIGQUIT, SIGINT, SIGTERM, SIGHUP}; + setUpUnixSignals(sigs); + + std::thread writer_thread([] { + while (!quitok) + writer->Run(); + }); + + JFJochWriterHttp writer_http(*writer, router); + + httpEndpoint->setHandler(router->handler()); + httpEndpoint->serve(); + writer_thread.join(); + logger.Info("Clean stop"); + exit(EXIT_SUCCESS); +} diff --git a/writer/jfjoch_writer_http.cpp b/writer/jfjoch_writer_http.cpp deleted file mode 100644 index 538a3e09..00000000 --- a/writer/jfjoch_writer_http.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (2019-2023) Paul Scherrer Institute - -#include -#include "../common/Logger.h" -#include "JFJochWriterHttp.h" -#include "StreamWriter.h" - -static Pistache::Http::Endpoint *httpEndpoint; - -static void sigHandler [[noreturn]] (int sig){ - switch(sig){ - case SIGINT: - case SIGQUIT: - case SIGTERM: - case SIGHUP: - default: - httpEndpoint->shutdown(); - break; - } - exit(0); -} - -static void setUpUnixSignals(std::vector quitSignals) { - sigset_t blocking_mask; - sigemptyset(&blocking_mask); - for (auto sig : quitSignals) - sigaddset(&blocking_mask, sig); - - struct sigaction sa; - sa.sa_handler = sigHandler; - sa.sa_mask = blocking_mask; - sa.sa_flags = 0; - - for (auto sig : quitSignals) - sigaction(sig, &sa, nullptr); -} - -int main(int argc, char **argv) { - RegisterHDF5Filter(); - - Logger logger("jfjoch_writer_http"); - - if ((argc != 3) && (argc != 4)) { - logger.Error("Usage ./jfjoch_writer_http {}"); - exit(EXIT_FAILURE); - } - - uint16_t http_port = atoi(argv[2]); - std::string repub_address; - if (argc == 4) - repub_address = argv[3]; - - ZMQContext context; - StreamWriter writer(context, logger, argv[1], repub_address); - - Pistache::Address addr(Pistache::Ipv4::any(), Pistache::Port(http_port)); - - httpEndpoint = new Pistache::Http::Endpoint((addr)); - - auto router = std::make_shared(); - - auto opts = Pistache::Http::Endpoint::options().threads(8); - opts.flags(Pistache::Tcp::Options::ReuseAddr); - httpEndpoint->init(opts); - - std::vector sigs{SIGQUIT, SIGINT, SIGTERM, SIGHUP}; - setUpUnixSignals(sigs); - - JFJochWriterHttp writer_http(writer, router); - - httpEndpoint->setHandler(router->handler()); - httpEndpoint->serve(); - - httpEndpoint->shutdown(); -} \ No newline at end of file diff --git a/writer/redoc-static.html b/writer/redoc-static.html index 46c9f9dc..5df2545b 100644 --- a/writer/redoc-static.html +++ b/writer/redoc-static.html @@ -43,13 +43,11 @@ data-styled.g12[id="sc-kEjbdu"]{content:"jmifsN,"}/*!sc*/ .jGBpue:before{content:'';width:15px;height:15px;background-size:contain;background-image:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEuMSIgeD0iMCIgeT0iMCIgd2lkdGg9IjUxMiIgaGVpZ2h0PSI1MTIiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBlbmFibGUtYmFja2dyb3VuZD0ibmV3IDAgMCA1MTIgNTEyIiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBmaWxsPSIjMDEwMTAxIiBkPSJNNDU5LjcgMjMzLjRsLTkwLjUgOTAuNWMtNTAgNTAtMTMxIDUwLTE4MSAwIC03LjktNy44LTE0LTE2LjctMTkuNC0yNS44bDQyLjEtNDIuMWMyLTIgNC41LTMuMiA2LjgtNC41IDIuOSA5LjkgOCAxOS4zIDE1LjggMjcuMiAyNSAyNSA2NS42IDI0LjkgOTAuNSAwbDkwLjUtOTAuNWMyNS0yNSAyNS02NS42IDAtOTAuNSAtMjQuOS0yNS02NS41LTI1LTkwLjUgMGwtMzIuMiAzMi4yYy0yNi4xLTEwLjItNTQuMi0xMi45LTgxLjYtOC45bDY4LjYtNjguNmM1MC01MCAxMzEtNTAgMTgxIDBDNTA5LjYgMTAyLjMgNTA5LjYgMTgzLjQgNDU5LjcgMjMzLjR6TTIyMC4zIDM4Mi4ybC0zMi4yIDMyLjJjLTI1IDI0LjktNjUuNiAyNC45LTkwLjUgMCAtMjUtMjUtMjUtNjUuNiAwLTkwLjVsOTAuNS05MC41YzI1LTI1IDY1LjUtMjUgOTAuNSAwIDcuOCA3LjggMTIuOSAxNy4yIDE1LjggMjcuMSAyLjQtMS40IDQuOC0yLjUgNi44LTQuNWw0Mi4xLTQyYy01LjQtOS4yLTExLjYtMTgtMTkuNC0yNS44IC01MC01MC0xMzEtNTAtMTgxIDBsLTkwLjUgOTAuNWMtNTAgNTAtNTAgMTMxIDAgMTgxIDUwIDUwIDEzMSA1MCAxODEgMGw2OC42LTY4LjZDMjc0LjYgMzk1LjEgMjQ2LjQgMzkyLjMgMjIwLjMgMzgyLjJ6Ii8+PC9zdmc+Cg==');opacity:0.5;visibility:hidden;display:inline-block;vertical-align:middle;}/*!sc*/ h1:hover>.jGBpue::before,h2:hover>.jGBpue::before,.jGBpue:hover::before{visibility:visible;}/*!sc*/ data-styled.g14[id="sc-crrtmM"]{content:"jGBpue,"}/*!sc*/ -.hHbBnT{height:20px;width:20px;min-width:20px;vertical-align:middle;float:right;transition:transform 0.2s ease-out;transform:rotateZ(0);}/*!sc*/ -.hHbBnT polygon{fill:white;}/*!sc*/ .iHKCKT{height:1.5em;width:1.5em;min-width:1.5em;vertical-align:middle;float:left;transition:transform 0.2s ease-out;transform:rotateZ(-90deg);}/*!sc*/ .iHKCKT polygon{fill:#1d8127;}/*!sc*/ -.bbipPn{height:1.5em;width:1.5em;min-width:1.5em;vertical-align:middle;float:left;transition:transform 0.2s ease-out;transform:rotateZ(-90deg);}/*!sc*/ -.bbipPn polygon{fill:#d41f1c;}/*!sc*/ -data-styled.g15[id="sc-dQpIV"]{content:"hHbBnT,iHKCKT,bbipPn,"}/*!sc*/ +.hHbBnT{height:20px;width:20px;min-width:20px;vertical-align:middle;float:right;transition:transform 0.2s ease-out;transform:rotateZ(0);}/*!sc*/ +.hHbBnT polygon{fill:white;}/*!sc*/ +data-styled.g15[id="sc-dQpIV"]{content:"iHKCKT,hHbBnT,"}/*!sc*/ .jVRsAZ >ul{list-style:none;padding:0;margin:0;margin:0 -5px;}/*!sc*/ .jVRsAZ >ul >li{padding:5px 10px;display:inline-block;background-color:#11171a;border-bottom:1px solid rgba(0, 0, 0, 0.5);cursor:pointer;text-align:center;outline:none;color:#ccc;margin:0 5px 5px 5px;border:1px solid #07090b;border-radius:5px;min-width:60px;font-size:0.9em;font-weight:bold;}/*!sc*/ .jVRsAZ >ul >li.react-tabs__tab--selected{color:#333333;background:#ffffff;}/*!sc*/ @@ -214,9 +212,9 @@ data-styled.g111[id="sc-fXoxOd"]{content:"jzevEV,"}/*!sc*/ .jlriCd ..sc-fXoxOd{color:#ffffff;}/*!sc*/ .jlriCd:focus{box-shadow:inset 0 2px 2px rgba(0, 0, 0, 0.45),0 2px 0 rgba(128, 128, 128, 0.25);}/*!sc*/ data-styled.g112[id="sc-FyfbU"]{content:"jlriCd,"}/*!sc*/ -.coiChI{font-size:0.929em;line-height:20px;background-color:#186FAF;color:#ffffff;padding:3px 10px;text-transform:uppercase;font-family:Montserrat,sans-serif;margin:0;}/*!sc*/ .gDPfwK{font-size:0.929em;line-height:20px;background-color:#2F8132;color:#ffffff;padding:3px 10px;text-transform:uppercase;font-family:Montserrat,sans-serif;margin:0;}/*!sc*/ -data-styled.g113[id="sc-jXkspL"]{content:"coiChI,gDPfwK,"}/*!sc*/ +.coiChI{font-size:0.929em;line-height:20px;background-color:#186FAF;color:#ffffff;padding:3px 10px;text-transform:uppercase;font-family:Montserrat,sans-serif;margin:0;}/*!sc*/ +data-styled.g113[id="sc-jXkspL"]{content:"gDPfwK,coiChI,"}/*!sc*/ .ciRGmD{position:absolute;width:100%;z-index:100;background:#fafafa;color:#263238;box-sizing:border-box;box-shadow:0 0 6px rgba(0, 0, 0, 0.33);overflow:hidden;border-bottom-left-radius:4px;border-bottom-right-radius:4px;transition:all 0.25s ease;visibility:hidden;transform:translateY(-50%) scaleY(0);}/*!sc*/ data-styled.g114[id="sc-eFtZDC"]{content:"ciRGmD,"}/*!sc*/ .buTZbL{padding:10px;}/*!sc*/ @@ -224,19 +222,13 @@ data-styled.g115[id="sc-fmlKft"]{content:"buTZbL,"}/*!sc*/ .uePtT{padding:5px;border:1px solid #ccc;background:#fff;word-break:break-all;color:#32329f;}/*!sc*/ .uePtT >span{color:#333333;}/*!sc*/ data-styled.g116[id="sc-ljRaSg"]{content:"uePtT,"}/*!sc*/ +.iePdRD{display:block;border:0;width:100%;text-align:left;padding:10px;border-radius:2px;margin-bottom:4px;line-height:1.5em;cursor:pointer;color:#1d8127;background-color:rgba(29,129,39,0.07);}/*!sc*/ +.iePdRD:focus{outline:auto #1d8127;}/*!sc*/ .eEqsZl{display:block;border:0;width:100%;text-align:left;padding:10px;border-radius:2px;margin-bottom:4px;line-height:1.5em;cursor:pointer;color:#1d8127;background-color:rgba(29,129,39,0.07);cursor:default;}/*!sc*/ .eEqsZl:focus{outline:auto #1d8127;}/*!sc*/ .eEqsZl::before{content:"—";font-weight:bold;width:1.5em;text-align:center;display:inline-block;vertical-align:top;}/*!sc*/ .eEqsZl:focus{outline:0;}/*!sc*/ -.iePdRD{display:block;border:0;width:100%;text-align:left;padding:10px;border-radius:2px;margin-bottom:4px;line-height:1.5em;cursor:pointer;color:#1d8127;background-color:rgba(29,129,39,0.07);}/*!sc*/ -.iePdRD:focus{outline:auto #1d8127;}/*!sc*/ -.jifJmL{display:block;border:0;width:100%;text-align:left;padding:10px;border-radius:2px;margin-bottom:4px;line-height:1.5em;cursor:pointer;color:#d41f1c;background-color:rgba(212,31,28,0.07);cursor:default;}/*!sc*/ -.jifJmL:focus{outline:auto #d41f1c;}/*!sc*/ -.jifJmL::before{content:"—";font-weight:bold;width:1.5em;text-align:center;display:inline-block;vertical-align:top;}/*!sc*/ -.jifJmL:focus{outline:0;}/*!sc*/ -.jEuzUJ{display:block;border:0;width:100%;text-align:left;padding:10px;border-radius:2px;margin-bottom:4px;line-height:1.5em;cursor:pointer;color:#d41f1c;background-color:rgba(212,31,28,0.07);}/*!sc*/ -.jEuzUJ:focus{outline:auto #d41f1c;}/*!sc*/ -data-styled.g119[id="sc-httZfN"]{content:"eEqsZl,iePdRD,jifJmL,jEuzUJ,"}/*!sc*/ +data-styled.g119[id="sc-httZfN"]{content:"iePdRD,eEqsZl,"}/*!sc*/ .goojcW{vertical-align:top;}/*!sc*/ data-styled.g122[id="sc-gVgoeb"]{content:"goojcW,"}/*!sc*/ .fCsSHf{font-size:1.3em;padding:0.2em 0;margin:3em 0 1.1em;color:#333333;font-weight:normal;}/*!sc*/ @@ -276,9 +268,7 @@ data-styled.g137[id="sc-cKZHtR"]{content:"lcoFQc,"}/*!sc*/ -

Jungfraujoch writer (1.0.0)

Download OpenAPI specification:Download

Jungfraujoch Writer Web API

-

Start writer

Responses

Will timeout after 5 seconds -If multiple parallel calls are made - only one will result in success -

Wait for writing done

-

Responses

Response samples

Content type
application/json
{
  • "nimages": 0,
  • "performance_MBs": 0,
  • "performance_Hz": 0
}

Cancel running data collection

Response samples

Content type
application/json
{
  • "nimages": 0,
  • "performance_MBs": 0,
  • "performance_Hz": 0,
  • "file_prefix": "string",
  • "state": "idle"
}

Cancel running data collection

It only instructs writer to cancel, but doesn't wait for cancellation actually happening. It still requires to call /wait_till_done

Responses

Check if detector is done (similar to wait_till_do

Check if detector is done (similar to wait_till_done, but no timeout)

-

Responses

Response samples

Content type
application/json
{
  • "nimages": 0,
  • "performance_MBs": 0,
  • "performance_Hz": 0
}
+