Under the header of the Rugnux live page, four big-number cards in the manner of jfjoch_viewer's merge statistics window: Resolution (d_min of the merge), ISa, Space group (short symbol, screw axes subscripted) and the model. The model card shows the fit over random orientations - R-work of the placed model below its random-orientation null, in sd - which is the test rugnux places a model by, both for molecular replacement (after the TFZ/LLG-Z gate) and for a model that fits where it is. The card is green where rugnux placed the model (placement MR or RIGID_BODY), red where it did not (with MR's reason), grey where no model was uploaded; no threshold of its own. "—" while the run is going, "searching…" during the MR step. /rugnux/status gains `space_group`, `d_min` and `isa` (set with the result; absent until then, isa also where the error model did not determine it), filled from ProcessResult in RugnuxThread with the values the summary's Space group, Resolution and Signal rows print. Generated code regenerated from jfjoch_api.yaml: broker/gen (cpp-pistache-server 7.20.0), the TypeScript client (npm run openapi) and broker/redoc-static.html. Python client not regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu (cherry picked from commit fcc019707751aab9d8c9f349309eeadcf364b9c2)
153 lines
4.8 KiB
C++
153 lines
4.8 KiB
C++
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// 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<double>(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<double> 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<float>(done) / static_cast<float>(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<std::pair<std::string, std::string>> &summary,
|
|
const std::vector<std::string> &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<double> d_min,
|
|
std::optional<double> 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<std::chrono::steady_clock::time_point> 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<const RugnuxStructure> structure) {
|
|
std::unique_lock ul(m);
|
|
s.structure = std::move(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;
|
|
}
|