// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include "RugnuxLiveStatus.h" double RugnuxLiveStatus::Since(std::chrono::steady_clock::time_point t) const { return std::chrono::duration(t - start).count(); } double RugnuxLiveStatus::Now() const { return Since(std::chrono::steady_clock::now()); } void RugnuxLiveStatus::CloseStep() { if (!s.steps.empty() && !s.steps.back().duration_s) s.steps.back().duration_s = Now() - s.steps.back().started_s; } void RugnuxLiveStatus::Start(int64_t images_expected, std::optional expected_collection_s, std::chrono::steady_clock::time_point start_received) { std::unique_lock ul(m); s = RugnuxLiveSnapshot{}; s.state = RugnuxLiveState::Running; s.images_expected = images_expected; s.expected_collection_s = expected_collection_s; start = start_received; last_image.reset(); } void RugnuxLiveStatus::ImageReceived(std::chrono::steady_clock::time_point received) { std::unique_lock ul(m); ++s.images_received; // Several forward threads report, so the images need not come here in the order they arrived. const double t = Since(received); if (!s.start_to_first_image_s || t < s.start_to_first_image_s.value()) s.start_to_first_image_s = t; if (!last_image || received > last_image.value()) last_image = received; } void RugnuxLiveStatus::Step(const std::string &title) { std::unique_lock ul(m); CloseStep(); s.steps.push_back(RugnuxLiveStep{.title = title, .started_s = Now()}); s.step_progress.reset(); } void RugnuxLiveStatus::Note(const std::string &text) { std::unique_lock ul(m); if (s.steps.empty()) s.steps.push_back(RugnuxLiveStep{.title = "", .started_s = Now()}); s.steps.back().notes.push_back(text); } void RugnuxLiveStatus::Progress(uint64_t done, uint64_t total) { std::unique_lock ul(m); if (total > 0) s.step_progress = static_cast(done) / static_cast(total); } void RugnuxLiveStatus::Finish(RugnuxLiveState state, const std::string &message) { std::unique_lock ul(m); CloseStep(); s.elapsed_s = Now(); s.state = state; s.message = message; s.step_progress.reset(); } void RugnuxLiveStatus::Result(const std::string &verdict, const std::string &verdict_text, const std::vector> &summary, const std::vector &warnings, bool anomalous_signal) { std::unique_lock ul(m); s.verdict = verdict; s.verdict_text = verdict_text; s.summary = summary; s.warnings = warnings; s.anomalous_signal = anomalous_signal; } void RugnuxLiveStatus::Model(const RugnuxLiveModel &model) { std::unique_lock ul(m); s.model = model; } void RugnuxLiveStatus::Headline(const std::string &space_group, std::optional d_min, std::optional isa) { std::unique_lock ul(m); s.space_group = space_group; s.d_min = d_min; s.isa = isa; } void RugnuxLiveStatus::Latency(double last_image_to_written_s) { std::unique_lock ul(m); s.last_image_to_written_s = last_image_to_written_s; } std::optional RugnuxLiveStatus::LastImage() { std::unique_lock ul(m); if (last_image) s.last_image_s = Since(last_image.value()); return last_image; } void RugnuxLiveStatus::Cut(const std::string &reason) { std::unique_lock ul(m); s.cut = reason; s.cut_s = Now(); } void RugnuxLiveStatus::Probe(const std::string &text) { std::unique_lock ul(m); s.probe = text; s.probe_s = Now(); } void RugnuxLiveStatus::Warning(const std::string &text) { std::unique_lock ul(m); s.warnings.push_back(text); } void RugnuxLiveStatus::Structure(std::shared_ptr structure, std::shared_ptr refined_structure) { std::unique_lock ul(m); s.structure = std::move(structure); s.refined_structure = std::move(refined_structure); } void RugnuxLiveStatus::Clear() { std::unique_lock ul(m); s = RugnuxLiveSnapshot{}; last_image.reset(); } RugnuxLiveSnapshot RugnuxLiveStatus::Get() const { std::unique_lock ul(m); RugnuxLiveSnapshot ret = s; if (s.state == RugnuxLiveState::Running) ret.elapsed_s = Now(); // Held since START, reported since the first image - or, before it has come, since now. const double zero = s.start_to_first_image_s.value_or(s.state == RugnuxLiveState::Running ? ret.elapsed_s : 0.0); ret.elapsed_s -= zero; for (auto &step: ret.steps) step.started_s -= zero; if (ret.probe_s) *ret.probe_s -= zero; if (ret.last_image_s) *ret.last_image_s -= zero; if (ret.cut_s) *ret.cut_s -= zero; return ret; }