/rugnux/status gains `model` (rugnux_live_model) - placement (RIGID_BODY/MR/NOT_PLACED), R-work and R-free of the placed, unrefined model, CC(model, data), R-work over its random-orientation null, mean 2mFo-DFc at the atoms, the rigid body's move, molecular replacement's decision, group, copies, TFZ/RFZ/LLG-Z and search resolution, and the strongest anomalous site - taken from ProcessResult::model_validation and ::model_prior (LiveModel). `anomalous_signal` says the summary's "Anomalous" row reports usable signal (CC_anom > CC_ANOM_SIGNAL, now a named constant shared with the report), so the viewer can show the anomalous map by default only then. The step that placed the model (Molecular replacement, or Model validation) now carries notes: what MR placed and its TFZ/RFZ, R-work/R-free (unrefined) and CC(model, data), the rigid-body move. Generated code regenerated from jfjoch_api.yaml: broker/gen (cpp-pistache-server), 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 f602e931dc6afcccb3767b8c6df87650d9496033)
145 lines
4.5 KiB
C++
145 lines
4.5 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::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;
|
|
}
|