broker: rugnux live - rugnux runs inside the broker while a DECTRIS rotation collection arrives (demo prototype)

A DECTRIS rotation collection, with the mode switched on (PUT /config/rugnux), goes to
JFJochReceiverRugnux instead of JFJochReceiverLite: the images are forwarded as before, without the
per-image analysis, and one rugnux thread reads them back from the image buffer
(JFJochImageBufferReader) as they arrive. END waits for rugnux and carries its lattice, refined
geometry and per-image results. A small second run on the first 60 degrees reports early whether
there is a lattice. The live state, steps and result are at GET /rugnux/status and on a new
"Rugnux live" frontend page.

rugnux: ProcessConfig::fast_live takes every decision where it is first made and does nothing again
once the sweep is in (near-tie re-checks and the run made again, rotation-scale walk, metric-symmetry
arms, geometry walk, the supercell and quality guards, the doubled cell), so the run ends a fixed
time after the last image. A cut (/cancel, or deadline_s after the last image) hands over what is
established. Off by default: the CLI and the battery are unchanged (--fast-live is hidden, with
--simulate-live).

With the mode off the broker is unchanged. Not built yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-17 12:20:38 +02:00
1 parent 5d8c6f4504
commit 3a44779d79
68 files changed
+5295 -100

No files matched your search

+56 -1
View File
@@ -7,6 +7,7 @@
#include <set>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <vector>
@@ -152,7 +153,10 @@ static const std::set<std::string> PROTECTED_PATHS{
"/image_buffer/image.tiff",
"/preview/plot",
"/preview/plot.bin",
"/live/events"
"/live/events",
"/config/rugnux",
"/config/rugnux/model",
"/rugnux/status"
};
std::optional<std::string> JFJochBrokerHttp::AuthorizationHeader(const httplib::Request &req) {
@@ -251,6 +255,11 @@ void JFJochBrokerHttp::register_routes(httplib::Server &server) {
server.Post("/config/image_format/raw", bind_noarg(&JFJochBrokerHttp::config_image_format_raw_post));
server.Get("/config/indexing", bind_noarg(&JFJochBrokerHttp::config_indexing_get));
server.Put("/config/indexing", bind_json(&JFJochBrokerHttp::config_indexing_put, Indexing_settings{}));
server.Get("/config/rugnux", bind_noarg(&JFJochBrokerHttp::config_rugnux_get));
server.Put("/config/rugnux", bind_json(&JFJochBrokerHttp::config_rugnux_put, Rugnux_settings{}));
server.Put("/config/rugnux/model", bind_req(&JFJochBrokerHttp::config_rugnux_model_put));
server.Delete("/config/rugnux/model", bind_noarg(&JFJochBrokerHttp::config_rugnux_model_delete));
server.Get("/rugnux/status", bind_noarg(&JFJochBrokerHttp::rugnux_status_get));
server.Get("/config/bragg_integration", bind_noarg(&JFJochBrokerHttp::config_bragg_integration_get));
server.Put("/config/bragg_integration", bind_json(&JFJochBrokerHttp::config_bragg_integration_put, Bragg_integration_settings{}));
server.Get("/config/instrument", bind_noarg(&JFJochBrokerHttp::config_instrument_get));
@@ -963,6 +972,52 @@ void JFJochBrokerHttp::config_indexing_put(const Indexing_settings &indexingSett
response.status = 200;
}
void JFJochBrokerHttp::config_rugnux_get(httplib::Response &response) {
const RugnuxLiveSettings s = state_machine.GetRugnuxLive();
Rugnux_status ret;
ret.setEnable(s.enable);
ret.setReservedCpus(s.reserved_cpus);
ret.setDeadlineS(s.deadline_s);
ret.setModelLoaded(!s.model_path.empty());
if (!s.model_path.empty())
ret.setModelSizeBytes(static_cast<int64_t>(std::filesystem::file_size(s.model_path)));
ProcessOutput(ret, response);
}
void JFJochBrokerHttp::config_rugnux_put(const Rugnux_settings &rugnuxSettings,
httplib::Response &response) {
state_machine.SetRugnuxLive(rugnuxSettings.isEnable(), rugnuxSettings.getReservedCpus(),
rugnuxSettings.getDeadlineS());
response.status = 200;
}
void JFJochBrokerHttp::config_rugnux_model_put(const httplib::Request &request,
httplib::Response &response) {
if (request.body.empty()) {
response.status = 400;
response.set_content("Request body cannot be empty", "text/plain");
return;
}
if (request.body.size() > 64 * 1024 * 1024) {
response.status = 413;
response.set_content("Model cannot be larger than 64 MiB", "text/plain");
return;
}
state_machine.SetRugnuxModel(request.body);
logger.Info("Rugnux model uploaded ({} bytes)", request.body.size());
response.status = 200;
}
void JFJochBrokerHttp::config_rugnux_model_delete(httplib::Response &response) {
state_machine.SetRugnuxModel("");
logger.Info("Rugnux model removed");
response.status = 200;
}
void JFJochBrokerHttp::rugnux_status_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetRugnuxStatus()), response);
}
void JFJochBrokerHttp::config_bragg_integration_get(httplib::Response &response) {
ProcessOutput(Convert(state_machine.GetBraggIntegrationSettings()), response);
}
+8
View File
@@ -34,6 +34,8 @@
#include "gen/model/Jfjoch_statistics.h"
#include "gen/model/Plots.h"
#include "gen/model/Roi_definitions.h"
#include "gen/model/Rugnux_settings.h"
#include "gen/model/Rugnux_status.h"
#include "gen/model/Scan_result.h"
#include "gen/model/Spot_finding_settings.h"
#include "gen/model/Zeromq_metadata_settings.h"
@@ -198,6 +200,12 @@ class JFJochBrokerHttp {
void config_indexing_get(httplib::Response &response);
void config_indexing_put(const org::openapitools::server::model::Indexing_settings &indexingSettings,
httplib::Response &response);
void config_rugnux_get(httplib::Response &response);
void config_rugnux_put(const org::openapitools::server::model::Rugnux_settings &rugnuxSettings,
httplib::Response &response);
void config_rugnux_model_put(const httplib::Request &request, httplib::Response &response);
void config_rugnux_model_delete(httplib::Response &response);
void rugnux_status_get(httplib::Response &response);
void config_bragg_integration_get(httplib::Response &response);
void config_bragg_integration_put(const org::openapitools::server::model::Bragg_integration_settings &braggIntegrationSettings,
httplib::Response &response);
+9
View File
@@ -276,6 +276,15 @@ void ParseReceiverSettings(const org::openapitools::server::model::Jfjoch_settin
}
}
void ParseRugnuxSettings(const org::openapitools::server::model::Jfjoch_settings &input, JFJochServices &services) {
if (input.rugnuxIsSet())
services.SetRugnuxLive(RugnuxLiveSettings{
.enable = input.getRugnux().isEnable(),
.reserved_cpus = input.getRugnux().getReservedCpus(),
.deadline_s = input.getRugnux().getDeadlineS()
});
}
SpotFindingSettings ParseSpotFindingSettings(const org::openapitools::server::model::Jfjoch_settings &input) {
if (input.spotFindingIsSet())
return Convert(input.getSpotFinding());
+2
View File
@@ -7,6 +7,7 @@
#include "../acquisition_device/AcquisitionDeviceGroup.h"
#include "../image_pusher/ImagePusher.h"
#include "../receiver/JFJochReceiverService.h"
#include "JFJochServices.h"
#include "gen/model/Jfjoch_settings.h"
DetectorGeometryModular ParseStandardDetectorGeometry(const org::openapitools::server::model::Detector &j);
@@ -20,4 +21,5 @@ std::unique_ptr<ImagePusher> ParseImagePusher(const org::openapitools::server::m
void ParseAcquisitionDeviceGroup(const org::openapitools::server::model::Jfjoch_settings &input, AcquisitionDeviceGroup &aq_devices);
void ParseReceiverSettings(const org::openapitools::server::model::Jfjoch_settings &input, JFJochReceiverService &service);
void ParseRugnuxSettings(const org::openapitools::server::model::Jfjoch_settings &input, JFJochServices &services);
SpotFindingSettings ParseSpotFindingSettings(const org::openapitools::server::model::Jfjoch_settings &input);
+49
View File
@@ -2,6 +2,10 @@
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochServices.h"
#include <filesystem>
#include <fstream>
#include <cstdlib>
#include "../common/JFJochException.h"
#include "../detector_control/SLSDetectorWrapper.h"
#include "../detector_control/DectrisDetectorWrapper.h"
@@ -370,6 +374,51 @@ void JFJochServices::SetupIndexing(const IndexingSettings &input) {
receiver->Indexing(input);
}
void JFJochServices::SetRugnuxLive(const RugnuxLiveSettings &input) {
if (receiver)
receiver->RugnuxLive(input.enable, input.model_path, input.reserved_cpus, input.deadline_s);
rugnux = input;
}
RugnuxLiveSettings JFJochServices::GetRugnuxLive() const {
return rugnux;
}
RugnuxLiveSnapshot JFJochServices::GetRugnuxStatus() const {
if (receiver)
return receiver->GetRugnuxStatus();
return {};
}
void JFJochServices::SetRugnuxModel(const std::string &model) {
RugnuxLiveSettings s = rugnux;
if (!s.model_path.empty())
std::filesystem::remove(s.model_path);
s.model_path.clear();
if (!model.empty()) {
if (rugnux_model_dir.empty()) {
char dir[] = "/tmp/jfjoch_rugnux_XXXXXX"; // mkdtemp creates it 0700
if (mkdtemp(dir) == nullptr)
throw JFJochException(JFJochExceptionCategory::FileWriteError,
"Cannot create directory for the rugnux model");
rugnux_model_dir = dir;
}
// gemmi detects PDB/mmCIF from the content, but gzip only from the extension
const bool gzipped = model.size() >= 2 && model[0] == '\x1f' && model[1] == '\x8b';
s.model_path = rugnux_model_dir + (gzipped ? "/model.gz" : "/model");
{
std::ofstream f(s.model_path, std::ios::binary);
f.write(model.data(), static_cast<std::streamsize>(model.size()));
if (!f)
throw JFJochException(JFJochExceptionCategory::FileWriteError, "Cannot write the rugnux model");
}
std::filesystem::permissions(s.model_path,
std::filesystem::perms::owner_read | std::filesystem::perms::owner_write);
}
SetRugnuxLive(s);
}
LiveCollection *JFJochServices::GetLiveCollection() {
return receiver ? receiver->GetLiveCollection() : nullptr;
}
+16
View File
@@ -14,6 +14,13 @@ struct JFJochServicesOutput {
JFJochReceiverOutput receiver_output;
};
struct RugnuxLiveSettings {
bool enable = false;
int64_t reserved_cpus = 4;
double deadline_s = 0; // the analysis is cut this long after the last image (0 = never)
std::string model_path; // empty = no model
};
class JFJochServices {
mutable std::shared_timed_mutex detector_mutex;
@@ -22,6 +29,9 @@ class JFJochServices {
std::shared_ptr<ZMQImagePuller> image_puller;
std::atomic<bool> cannot_stop_detector = false;
RugnuxLiveSettings rugnux;
std::string rugnux_model_dir; // private directory, created at the first model upload
Logger &logger;
public:
explicit JFJochServices(Logger &in_logger);
@@ -76,6 +86,12 @@ public:
void SetupIndexing(const IndexingSettings& input);
void SetRugnuxLive(const RugnuxLiveSettings &input);
RugnuxLiveSettings GetRugnuxLive() const;
// The model file is written 0600 into a private directory; an empty model removes it
void SetRugnuxModel(const std::string &model);
RugnuxLiveSnapshot GetRugnuxStatus() const;
ImagePusherStatus GetImagePusherStatus() const;
LiveCollection *GetLiveCollection();
};
+29
View File
@@ -1209,6 +1209,35 @@ IndexingSettings JFJochStateMachine::GetIndexingSettings() const {
return experiment.GetIndexingSettings();
}
void JFJochStateMachine::SetRugnuxLive(bool enable, int64_t reserved_cpus, double deadline_s) {
std::unique_lock ul(m);
if (IsRunning())
throw WrongDAQStateException("Cannot change rugnux settings during data collection");
std::unique_lock ul2(rugnux_mutex);
RugnuxLiveSettings s = services.GetRugnuxLive();
s.enable = enable;
s.reserved_cpus = reserved_cpus;
s.deadline_s = deadline_s;
services.SetRugnuxLive(s);
}
void JFJochStateMachine::SetRugnuxModel(const std::string &model) {
std::unique_lock ul(m);
if (IsRunning())
throw WrongDAQStateException("Cannot change rugnux model during data collection");
std::unique_lock ul2(rugnux_mutex);
services.SetRugnuxModel(model);
}
RugnuxLiveSettings JFJochStateMachine::GetRugnuxLive() const {
std::unique_lock ul(rugnux_mutex);
return services.GetRugnuxLive();
}
RugnuxLiveSnapshot JFJochStateMachine::GetRugnuxStatus() const {
return services.GetRugnuxStatus();
}
void JFJochStateMachine::SetIndexingSettings(const IndexingSettings &input) {
std::unique_lock ul(m);
if (IsRunning())
+6
View File
@@ -90,6 +90,7 @@ class JFJochStateMachine {
mutable std::mutex experiment_file_writer_settings_mutex;
mutable std::mutex experiment_indexing_settings_mutex;
mutable std::mutex experiment_dark_mask_settings_mutex;
mutable std::mutex rugnux_mutex;
DiffractionExperiment experiment;
// mutex m is protecting:
@@ -247,6 +248,11 @@ public:
void SetIndexingSettings(const IndexingSettings &input);
IndexingSettings GetIndexingSettings() const;
void SetRugnuxLive(bool enable, int64_t reserved_cpus, double deadline_s);
void SetRugnuxModel(const std::string &model); // empty = remove the model
RugnuxLiveSettings GetRugnuxLive() const;
RugnuxLiveSnapshot GetRugnuxStatus() const;
void SetBraggIntegrationSettings(const BraggIntegrationSettings &input);
BraggIntegrationSettings GetBraggIntegrationSettings() const;
PixelMaskStatistics GetPixelMaskStatistics() const;
+54
View File
@@ -1221,6 +1221,60 @@ DarkMaskSettings Convert(const org::openapitools::server::model::Dark_mask_setti
return ret;
}
org::openapitools::server::model::Rugnux_live_status Convert(const RugnuxLiveSnapshot& input) {
org::openapitools::server::model::Rugnux_live_status ret;
switch (input.state) {
case RugnuxLiveState::Idle: ret.setState("Idle"); break;
case RugnuxLiveState::Running: ret.setState("Running"); break;
case RugnuxLiveState::Done: ret.setState("Done"); break;
case RugnuxLiveState::Failed: ret.setState("Failed"); break;
case RugnuxLiveState::Cancelled: ret.setState("Cancelled"); break;
case RugnuxLiveState::Skipped: ret.setState("Skipped"); break;
}
ret.setMessage(input.message);
ret.setImagesExpected(input.images_expected);
ret.setImagesReceived(input.images_received);
ret.setElapsedS(input.elapsed_s);
ret.setProbe(input.probe);
if (input.probe_s)
ret.setProbeS(input.probe_s.value());
if (input.step_progress)
ret.setStepProgress(input.step_progress.value());
std::vector<org::openapitools::server::model::Rugnux_live_step> steps;
for (const auto &s: input.steps) {
org::openapitools::server::model::Rugnux_live_step step;
step.setTitle(s.title);
step.setStartedS(s.started_s);
if (s.duration_s)
step.setDurationS(s.duration_s.value());
step.setNotes(s.notes);
steps.push_back(step);
}
ret.setSteps(steps);
if (input.last_image_s)
ret.setLastImageS(input.last_image_s.value());
ret.setCut(input.cut);
if (input.cut_s)
ret.setCutS(input.cut_s.value());
if (input.last_image_to_written_s)
ret.setLastImageToWrittenS(input.last_image_to_written_s.value());
ret.setVerdict(input.verdict);
ret.setVerdictText(input.verdict_text);
std::vector<org::openapitools::server::model::Rugnux_summary_row> summary;
for (const auto &[label, value]: input.summary) {
org::openapitools::server::model::Rugnux_summary_row row;
row.setLabel(label);
row.setValue(value);
summary.push_back(row);
}
ret.setSummary(summary);
ret.setWarnings(input.warnings);
return ret;
}
org::openapitools::server::model::Image_pusher_status Convert(const ImagePusherStatus& input) {
org::openapitools::server::model::Image_pusher_status ret;
ret.setAddr(input.address);
+3
View File
@@ -30,6 +30,7 @@
#include "gen/model/Indexing_settings.h"
#include "gen/model/Bragg_integration_settings.h"
#include "gen/model/Scan_result.h"
#include "gen/model/Rugnux_live_status.h"
#include "../common/JFJochMessages.h"
#include "../common/DatasetSettings.h"
@@ -41,6 +42,7 @@
#include "../common/InstrumentMetadata.h"
#include "../common/ScanResult.h"
#include "../image_pusher/ImagePusher.h"
#include "../receiver/RugnuxLiveStatus.h"
SpotFindingSettings Convert(const org::openapitools::server::model::Spot_finding_settings &input);
org::openapitools::server::model::Spot_finding_settings Convert(const SpotFindingSettings &input);
@@ -99,4 +101,5 @@ org::openapitools::server::model::Dark_mask_settings Convert(const DarkMaskSetti
DarkMaskSettings Convert(const org::openapitools::server::model::Dark_mask_settings& input);
org::openapitools::server::model::Image_pusher_status Convert(const ImagePusherStatus& input);
org::openapitools::server::model::Rugnux_live_status Convert(const RugnuxLiveSnapshot& input);
+30 -2
View File
@@ -44,6 +44,7 @@ Jfjoch_settings::Jfjoch_settings()
m_Zeromq_previewIsSet = false;
m_Zeromq_metadataIsSet = false;
m_Dark_maskIsSet = false;
m_RugnuxIsSet = false;
}
@@ -141,7 +142,7 @@ bool Jfjoch_settings::validate(std::stringstream& msg, const std::string& pathPr
}
}
return success;
}
@@ -211,7 +212,10 @@ bool Jfjoch_settings::operator==(const Jfjoch_settings& rhs) const
((!zeromqMetadataIsSet() && !rhs.zeromqMetadataIsSet()) || (zeromqMetadataIsSet() && rhs.zeromqMetadataIsSet() && getZeromqMetadata() == rhs.getZeromqMetadata())) &&
((!darkMaskIsSet() && !rhs.darkMaskIsSet()) || (darkMaskIsSet() && rhs.darkMaskIsSet() && getDarkMask() == rhs.getDarkMask()))
((!darkMaskIsSet() && !rhs.darkMaskIsSet()) || (darkMaskIsSet() && rhs.darkMaskIsSet() && getDarkMask() == rhs.getDarkMask())) &&
((!rugnuxIsSet() && !rhs.rugnuxIsSet()) || (rugnuxIsSet() && rhs.rugnuxIsSet() && getRugnux() == rhs.getRugnux()))
;
}
@@ -263,6 +267,8 @@ void to_json(nlohmann::json& j, const Jfjoch_settings& o)
j["zeromq_metadata"] = o.m_Zeromq_metadata;
if(o.darkMaskIsSet())
j["dark_mask"] = o.m_Dark_mask;
if(o.rugnuxIsSet())
j["rugnux"] = o.m_Rugnux;
}
@@ -361,6 +367,11 @@ void from_json(const nlohmann::json& j, Jfjoch_settings& o)
j.at("dark_mask").get_to(o.m_Dark_mask);
o.m_Dark_maskIsSet = true;
}
if(j.find("rugnux") != j.end())
{
j.at("rugnux").get_to(o.m_Rugnux);
o.m_RugnuxIsSet = true;
}
}
@@ -694,6 +705,23 @@ void Jfjoch_settings::unsetDark_mask()
{
m_Dark_maskIsSet = false;
}
org::openapitools::server::model::Rugnux_settings Jfjoch_settings::getRugnux() const
{
return m_Rugnux;
}
void Jfjoch_settings::setRugnux(org::openapitools::server::model::Rugnux_settings const& value)
{
m_Rugnux = value;
m_RugnuxIsSet = true;
}
bool Jfjoch_settings::rugnuxIsSet() const
{
return m_RugnuxIsSet;
}
void Jfjoch_settings::unsetRugnux()
{
m_RugnuxIsSet = false;
}
} // namespace org::openapitools::server::model
+10
View File
@@ -31,6 +31,7 @@
#include "Image_format_settings.h"
#include "Zeromq_metadata_settings.h"
#include "Detector_settings.h"
#include "Rugnux_settings.h"
#include <vector>
#include "Indexing_settings.h"
#include "Detector.h"
@@ -216,6 +217,13 @@ public:
void setDarkMask(org::openapitools::server::model::Dark_mask_settings const& value);
bool darkMaskIsSet() const;
void unsetDark_mask();
/// <summary>
///
/// </summary>
org::openapitools::server::model::Rugnux_settings getRugnux() const;
void setRugnux(org::openapitools::server::model::Rugnux_settings const& value);
bool rugnuxIsSet() const;
void unsetRugnux();
friend void to_json(nlohmann::json& j, const Jfjoch_settings& o);
friend void from_json(const nlohmann::json& j, Jfjoch_settings& o);
@@ -262,6 +270,8 @@ protected:
bool m_Zeromq_metadataIsSet;
org::openapitools::server::model::Dark_mask_settings m_Dark_mask;
bool m_Dark_maskIsSet;
org::openapitools::server::model::Rugnux_settings m_Rugnux;
bool m_RugnuxIsSet;
};
+484
View File
@@ -0,0 +1,484 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
#include "Rugnux_live_status.h"
#include "Helpers.h"
#include <sstream>
namespace org::openapitools::server::model
{
Rugnux_live_status::Rugnux_live_status()
{
m_State = "";
m_Probe = "";
m_Probe_s = 0.0;
m_Probe_sIsSet = false;
m_Message = "";
m_Images_expected = 0L;
m_Images_received = 0L;
m_Elapsed_s = 0.0;
m_Step_progress = 0.0f;
m_Step_progressIsSet = false;
m_Last_image_s = 0.0;
m_Last_image_sIsSet = false;
m_Cut = "";
m_CutIsSet = false;
m_Cut_s = 0.0;
m_Cut_sIsSet = false;
m_Last_image_to_written_s = 0.0;
m_Last_image_to_written_sIsSet = false;
m_Verdict = "";
m_Verdict_text = "";
}
void Rugnux_live_status::validate() const
{
std::stringstream msg;
if (!validate(msg))
{
throw org::openapitools::server::helpers::ValidationException(msg.str());
}
}
bool Rugnux_live_status::validate(std::stringstream& msg) const
{
return validate(msg, "");
}
bool Rugnux_live_status::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Rugnux_live_status" : pathPrefix;
if (stepProgressIsSet())
{
const float& value = m_Step_progress;
const std::string currentValuePath = _pathPrefix + ".stepProgress";
if (value < static_cast<float>(0.0))
{
success = false;
msg << currentValuePath << ": must be greater than or equal to 0.0;";
}
if (value > static_cast<float>(1.0))
{
success = false;
msg << currentValuePath << ": must be less than or equal to 1.0;";
}
}
/* Steps */ {
const std::vector<org::openapitools::server::model::Rugnux_live_step>& value = m_Steps;
const std::string currentValuePath = _pathPrefix + ".steps";
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const org::openapitools::server::model::Rugnux_live_step& value : value)
{
const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]";
success = value.validate(msg, currentValuePath + ".steps") && success;
i++;
}
}
}
/* Summary */ {
const std::vector<org::openapitools::server::model::Rugnux_summary_row>& value = m_Summary;
const std::string currentValuePath = _pathPrefix + ".summary";
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const org::openapitools::server::model::Rugnux_summary_row& value : value)
{
const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]";
success = value.validate(msg, currentValuePath + ".summary") && success;
i++;
}
}
}
/* Warnings */ {
const std::vector<std::string>& value = m_Warnings;
const std::string currentValuePath = _pathPrefix + ".warnings";
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const std::string& value : value)
{
const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]";
i++;
}
}
}
return success;
}
bool Rugnux_live_status::operator==(const Rugnux_live_status& rhs) const
{
return
(getState() == rhs.getState())
&&
(getProbe() == rhs.getProbe())
&&
((!probeSIsSet() && !rhs.probeSIsSet()) || (probeSIsSet() && rhs.probeSIsSet() && getProbeS() == rhs.getProbeS())) &&
(getMessage() == rhs.getMessage())
&&
(getImagesExpected() == rhs.getImagesExpected())
&&
(getImagesReceived() == rhs.getImagesReceived())
&&
(getElapsedS() == rhs.getElapsedS())
&&
((!stepProgressIsSet() && !rhs.stepProgressIsSet()) || (stepProgressIsSet() && rhs.stepProgressIsSet() && getStepProgress() == rhs.getStepProgress())) &&
(getSteps() == rhs.getSteps())
&&
((!lastImageSIsSet() && !rhs.lastImageSIsSet()) || (lastImageSIsSet() && rhs.lastImageSIsSet() && getLastImageS() == rhs.getLastImageS())) &&
((!cutIsSet() && !rhs.cutIsSet()) || (cutIsSet() && rhs.cutIsSet() && getCut() == rhs.getCut())) &&
((!cutSIsSet() && !rhs.cutSIsSet()) || (cutSIsSet() && rhs.cutSIsSet() && getCutS() == rhs.getCutS())) &&
((!lastImageToWrittenSIsSet() && !rhs.lastImageToWrittenSIsSet()) || (lastImageToWrittenSIsSet() && rhs.lastImageToWrittenSIsSet() && getLastImageToWrittenS() == rhs.getLastImageToWrittenS())) &&
(getVerdict() == rhs.getVerdict())
&&
(getVerdictText() == rhs.getVerdictText())
&&
(getSummary() == rhs.getSummary())
&&
(getWarnings() == rhs.getWarnings())
;
}
bool Rugnux_live_status::operator!=(const Rugnux_live_status& rhs) const
{
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const Rugnux_live_status& o)
{
j = nlohmann::json::object();
j["state"] = o.m_State;
j["probe"] = o.m_Probe;
if(o.probeSIsSet())
j["probe_s"] = o.m_Probe_s;
j["message"] = o.m_Message;
j["images_expected"] = o.m_Images_expected;
j["images_received"] = o.m_Images_received;
j["elapsed_s"] = o.m_Elapsed_s;
if(o.stepProgressIsSet())
j["step_progress"] = o.m_Step_progress;
j["steps"] = o.m_Steps;
if(o.lastImageSIsSet())
j["last_image_s"] = o.m_Last_image_s;
if(o.cutIsSet())
j["cut"] = o.m_Cut;
if(o.cutSIsSet())
j["cut_s"] = o.m_Cut_s;
if(o.lastImageToWrittenSIsSet())
j["last_image_to_written_s"] = o.m_Last_image_to_written_s;
j["verdict"] = o.m_Verdict;
j["verdict_text"] = o.m_Verdict_text;
j["summary"] = o.m_Summary;
j["warnings"] = o.m_Warnings;
}
void from_json(const nlohmann::json& j, Rugnux_live_status& o)
{
j.at("state").get_to(o.m_State);
j.at("probe").get_to(o.m_Probe);
if(j.find("probe_s") != j.end())
{
j.at("probe_s").get_to(o.m_Probe_s);
o.m_Probe_sIsSet = true;
}
j.at("message").get_to(o.m_Message);
j.at("images_expected").get_to(o.m_Images_expected);
j.at("images_received").get_to(o.m_Images_received);
j.at("elapsed_s").get_to(o.m_Elapsed_s);
if(j.find("step_progress") != j.end())
{
j.at("step_progress").get_to(o.m_Step_progress);
o.m_Step_progressIsSet = true;
}
j.at("steps").get_to(o.m_Steps);
if(j.find("last_image_s") != j.end())
{
j.at("last_image_s").get_to(o.m_Last_image_s);
o.m_Last_image_sIsSet = true;
}
if(j.find("cut") != j.end())
{
j.at("cut").get_to(o.m_Cut);
o.m_CutIsSet = true;
}
if(j.find("cut_s") != j.end())
{
j.at("cut_s").get_to(o.m_Cut_s);
o.m_Cut_sIsSet = true;
}
if(j.find("last_image_to_written_s") != j.end())
{
j.at("last_image_to_written_s").get_to(o.m_Last_image_to_written_s);
o.m_Last_image_to_written_sIsSet = true;
}
j.at("verdict").get_to(o.m_Verdict);
j.at("verdict_text").get_to(o.m_Verdict_text);
j.at("summary").get_to(o.m_Summary);
j.at("warnings").get_to(o.m_Warnings);
}
std::string Rugnux_live_status::getState() const
{
return m_State;
}
void Rugnux_live_status::setState(std::string const& value)
{
m_State = value;
}
std::string Rugnux_live_status::getProbe() const
{
return m_Probe;
}
void Rugnux_live_status::setProbe(std::string const& value)
{
m_Probe = value;
}
double Rugnux_live_status::getProbeS() const
{
return m_Probe_s;
}
void Rugnux_live_status::setProbeS(double const value)
{
m_Probe_s = value;
m_Probe_sIsSet = true;
}
bool Rugnux_live_status::probeSIsSet() const
{
return m_Probe_sIsSet;
}
void Rugnux_live_status::unsetProbe_s()
{
m_Probe_sIsSet = false;
}
std::string Rugnux_live_status::getMessage() const
{
return m_Message;
}
void Rugnux_live_status::setMessage(std::string const& value)
{
m_Message = value;
}
int64_t Rugnux_live_status::getImagesExpected() const
{
return m_Images_expected;
}
void Rugnux_live_status::setImagesExpected(int64_t const value)
{
m_Images_expected = value;
}
int64_t Rugnux_live_status::getImagesReceived() const
{
return m_Images_received;
}
void Rugnux_live_status::setImagesReceived(int64_t const value)
{
m_Images_received = value;
}
double Rugnux_live_status::getElapsedS() const
{
return m_Elapsed_s;
}
void Rugnux_live_status::setElapsedS(double const value)
{
m_Elapsed_s = value;
}
float Rugnux_live_status::getStepProgress() const
{
return m_Step_progress;
}
void Rugnux_live_status::setStepProgress(float const value)
{
m_Step_progress = value;
m_Step_progressIsSet = true;
}
bool Rugnux_live_status::stepProgressIsSet() const
{
return m_Step_progressIsSet;
}
void Rugnux_live_status::unsetStep_progress()
{
m_Step_progressIsSet = false;
}
std::vector<org::openapitools::server::model::Rugnux_live_step> Rugnux_live_status::getSteps() const
{
return m_Steps;
}
void Rugnux_live_status::setSteps(std::vector<org::openapitools::server::model::Rugnux_live_step> const& value)
{
m_Steps = value;
}
double Rugnux_live_status::getLastImageS() const
{
return m_Last_image_s;
}
void Rugnux_live_status::setLastImageS(double const value)
{
m_Last_image_s = value;
m_Last_image_sIsSet = true;
}
bool Rugnux_live_status::lastImageSIsSet() const
{
return m_Last_image_sIsSet;
}
void Rugnux_live_status::unsetLast_image_s()
{
m_Last_image_sIsSet = false;
}
std::string Rugnux_live_status::getCut() const
{
return m_Cut;
}
void Rugnux_live_status::setCut(std::string const& value)
{
m_Cut = value;
m_CutIsSet = true;
}
bool Rugnux_live_status::cutIsSet() const
{
return m_CutIsSet;
}
void Rugnux_live_status::unsetCut()
{
m_CutIsSet = false;
}
double Rugnux_live_status::getCutS() const
{
return m_Cut_s;
}
void Rugnux_live_status::setCutS(double const value)
{
m_Cut_s = value;
m_Cut_sIsSet = true;
}
bool Rugnux_live_status::cutSIsSet() const
{
return m_Cut_sIsSet;
}
void Rugnux_live_status::unsetCut_s()
{
m_Cut_sIsSet = false;
}
double Rugnux_live_status::getLastImageToWrittenS() const
{
return m_Last_image_to_written_s;
}
void Rugnux_live_status::setLastImageToWrittenS(double const value)
{
m_Last_image_to_written_s = value;
m_Last_image_to_written_sIsSet = true;
}
bool Rugnux_live_status::lastImageToWrittenSIsSet() const
{
return m_Last_image_to_written_sIsSet;
}
void Rugnux_live_status::unsetLast_image_to_written_s()
{
m_Last_image_to_written_sIsSet = false;
}
std::string Rugnux_live_status::getVerdict() const
{
return m_Verdict;
}
void Rugnux_live_status::setVerdict(std::string const& value)
{
m_Verdict = value;
}
std::string Rugnux_live_status::getVerdictText() const
{
return m_Verdict_text;
}
void Rugnux_live_status::setVerdictText(std::string const& value)
{
m_Verdict_text = value;
}
std::vector<org::openapitools::server::model::Rugnux_summary_row> Rugnux_live_status::getSummary() const
{
return m_Summary;
}
void Rugnux_live_status::setSummary(std::vector<org::openapitools::server::model::Rugnux_summary_row> const& value)
{
m_Summary = value;
}
std::vector<std::string> Rugnux_live_status::getWarnings() const
{
return m_Warnings;
}
void Rugnux_live_status::setWarnings(std::vector<std::string> const& value)
{
m_Warnings = value;
}
} // namespace org::openapitools::server::model
+204
View File
@@ -0,0 +1,204 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/*
* Rugnux_live_status.h
*
* Live rugnux processing of the last collection; kept until the next collection starts
*/
#ifndef Rugnux_live_status_H_
#define Rugnux_live_status_H_
#include "Rugnux_summary_row.h"
#include <string>
#include "Rugnux_live_step.h"
#include <vector>
#include <nlohmann/json.hpp>
namespace org::openapitools::server::model
{
/// <summary>
/// Live rugnux processing of the last collection; kept until the next collection starts
/// </summary>
class Rugnux_live_status
{
public:
Rugnux_live_status();
virtual ~Rugnux_live_status() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes an error
/// message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and calls it's validate.
/// Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const Rugnux_live_status& rhs) const;
bool operator!=(const Rugnux_live_status& rhs) const;
/////////////////////////////////////////////
/// Rugnux_live_status members
/// <summary>
///
/// </summary>
std::string getState() const;
void setState(std::string const& value);
/// <summary>
/// Answer of the early lattice probe on the first 60 degrees; empty until it answers
/// </summary>
std::string getProbe() const;
void setProbe(std::string const& value);
/// <summary>
/// When the probe answered, since the collection started
/// </summary>
double getProbeS() const;
void setProbeS(double const value);
bool probeSIsSet() const;
void unsetProbe_s();
/// <summary>
/// Why the run failed or was skipped
/// </summary>
std::string getMessage() const;
void setMessage(std::string const& value);
/// <summary>
///
/// </summary>
int64_t getImagesExpected() const;
void setImagesExpected(int64_t const value);
/// <summary>
///
/// </summary>
int64_t getImagesReceived() const;
void setImagesReceived(int64_t const value);
/// <summary>
///
/// </summary>
double getElapsedS() const;
void setElapsedS(double const value);
/// <summary>
/// Fraction of the current pass&#39;s images
/// </summary>
float getStepProgress() const;
void setStepProgress(float const value);
bool stepProgressIsSet() const;
void unsetStep_progress();
/// <summary>
///
/// </summary>
std::vector<org::openapitools::server::model::Rugnux_live_step> getSteps() const;
void setSteps(std::vector<org::openapitools::server::model::Rugnux_live_step> const& value);
/// <summary>
/// When the last image arrived (and the cutoff timer started), since the collection started
/// </summary>
double getLastImageS() const;
void setLastImageS(double const value);
bool lastImageSIsSet() const;
void unsetLast_image_s();
/// <summary>
/// Empty, or why the analysis was cut: &#x60;deadline&#x60; (&#x60;deadline_s&#x60; after the last image) or &#x60;cancel&#x60; (&#x60;POST /cancel&#x60;). The end message then carried what was established at the cut.
/// </summary>
std::string getCut() const;
void setCut(std::string const& value);
bool cutIsSet() const;
void unsetCut();
/// <summary>
/// When the analysis was cut, since the collection started
/// </summary>
double getCutS() const;
void setCutS(double const value);
bool cutSIsSet() const;
void unsetCut_s();
/// <summary>
/// From the last image received to all results written
/// </summary>
double getLastImageToWrittenS() const;
void setLastImageToWrittenS(double const value);
bool lastImageToWrittenSIsSet() const;
void unsetLast_image_to_written_s();
/// <summary>
/// OK, WARNINGS, UNUSABLE or FAILED; empty until the run is over
/// </summary>
std::string getVerdict() const;
void setVerdict(std::string const& value);
/// <summary>
///
/// </summary>
std::string getVerdictText() const;
void setVerdictText(std::string const& value);
/// <summary>
///
/// </summary>
std::vector<org::openapitools::server::model::Rugnux_summary_row> getSummary() const;
void setSummary(std::vector<org::openapitools::server::model::Rugnux_summary_row> const& value);
/// <summary>
///
/// </summary>
std::vector<std::string> getWarnings() const;
void setWarnings(std::vector<std::string> const& value);
friend void to_json(nlohmann::json& j, const Rugnux_live_status& o);
friend void from_json(const nlohmann::json& j, Rugnux_live_status& o);
protected:
std::string m_State;
std::string m_Probe;
double m_Probe_s;
bool m_Probe_sIsSet;
std::string m_Message;
int64_t m_Images_expected;
int64_t m_Images_received;
double m_Elapsed_s;
float m_Step_progress;
bool m_Step_progressIsSet;
std::vector<org::openapitools::server::model::Rugnux_live_step> m_Steps;
double m_Last_image_s;
bool m_Last_image_sIsSet;
std::string m_Cut;
bool m_CutIsSet;
double m_Cut_s;
bool m_Cut_sIsSet;
double m_Last_image_to_written_s;
bool m_Last_image_to_written_sIsSet;
std::string m_Verdict;
std::string m_Verdict_text;
std::vector<org::openapitools::server::model::Rugnux_summary_row> m_Summary;
std::vector<std::string> m_Warnings;
};
} // namespace org::openapitools::server::model
#endif /* Rugnux_live_status_H_ */
+168
View File
@@ -0,0 +1,168 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
#include "Rugnux_live_step.h"
#include "Helpers.h"
#include <sstream>
namespace org::openapitools::server::model
{
Rugnux_live_step::Rugnux_live_step()
{
m_Title = "";
m_Started_s = 0.0;
m_Duration_s = 0.0;
m_Duration_sIsSet = false;
}
void Rugnux_live_step::validate() const
{
std::stringstream msg;
if (!validate(msg))
{
throw org::openapitools::server::helpers::ValidationException(msg.str());
}
}
bool Rugnux_live_step::validate(std::stringstream& msg) const
{
return validate(msg, "");
}
bool Rugnux_live_step::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Rugnux_live_step" : pathPrefix;
/* Notes */ {
const std::vector<std::string>& value = m_Notes;
const std::string currentValuePath = _pathPrefix + ".notes";
{ // Recursive validation of array elements
const std::string oldValuePath = currentValuePath;
int i = 0;
for (const std::string& value : value)
{
const std::string currentValuePath = oldValuePath + "[" + std::to_string(i) + "]";
i++;
}
}
}
return success;
}
bool Rugnux_live_step::operator==(const Rugnux_live_step& rhs) const
{
return
(getTitle() == rhs.getTitle())
&&
(getStartedS() == rhs.getStartedS())
&&
((!durationSIsSet() && !rhs.durationSIsSet()) || (durationSIsSet() && rhs.durationSIsSet() && getDurationS() == rhs.getDurationS())) &&
(getNotes() == rhs.getNotes())
;
}
bool Rugnux_live_step::operator!=(const Rugnux_live_step& rhs) const
{
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const Rugnux_live_step& o)
{
j = nlohmann::json::object();
j["title"] = o.m_Title;
j["started_s"] = o.m_Started_s;
if(o.durationSIsSet())
j["duration_s"] = o.m_Duration_s;
j["notes"] = o.m_Notes;
}
void from_json(const nlohmann::json& j, Rugnux_live_step& o)
{
j.at("title").get_to(o.m_Title);
j.at("started_s").get_to(o.m_Started_s);
if(j.find("duration_s") != j.end())
{
j.at("duration_s").get_to(o.m_Duration_s);
o.m_Duration_sIsSet = true;
}
j.at("notes").get_to(o.m_Notes);
}
std::string Rugnux_live_step::getTitle() const
{
return m_Title;
}
void Rugnux_live_step::setTitle(std::string const& value)
{
m_Title = value;
}
double Rugnux_live_step::getStartedS() const
{
return m_Started_s;
}
void Rugnux_live_step::setStartedS(double const value)
{
m_Started_s = value;
}
double Rugnux_live_step::getDurationS() const
{
return m_Duration_s;
}
void Rugnux_live_step::setDurationS(double const value)
{
m_Duration_s = value;
m_Duration_sIsSet = true;
}
bool Rugnux_live_step::durationSIsSet() const
{
return m_Duration_sIsSet;
}
void Rugnux_live_step::unsetDuration_s()
{
m_Duration_sIsSet = false;
}
std::vector<std::string> Rugnux_live_step::getNotes() const
{
return m_Notes;
}
void Rugnux_live_step::setNotes(std::vector<std::string> const& value)
{
m_Notes = value;
}
} // namespace org::openapitools::server::model
+101
View File
@@ -0,0 +1,101 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/*
* Rugnux_live_step.h
*
*
*/
#ifndef Rugnux_live_step_H_
#define Rugnux_live_step_H_
#include <string>
#include <vector>
#include <nlohmann/json.hpp>
namespace org::openapitools::server::model
{
/// <summary>
///
/// </summary>
class Rugnux_live_step
{
public:
Rugnux_live_step();
virtual ~Rugnux_live_step() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes an error
/// message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and calls it's validate.
/// Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const Rugnux_live_step& rhs) const;
bool operator!=(const Rugnux_live_step& rhs) const;
/////////////////////////////////////////////
/// Rugnux_live_step members
/// <summary>
///
/// </summary>
std::string getTitle() const;
void setTitle(std::string const& value);
/// <summary>
/// Start of the step, since the collection started
/// </summary>
double getStartedS() const;
void setStartedS(double const value);
/// <summary>
/// Set once the next step starts or the run ends
/// </summary>
double getDurationS() const;
void setDurationS(double const value);
bool durationSIsSet() const;
void unsetDuration_s();
/// <summary>
/// What the step found (e.g. lattice, space group)
/// </summary>
std::vector<std::string> getNotes() const;
void setNotes(std::vector<std::string> const& value);
friend void to_json(nlohmann::json& j, const Rugnux_live_step& o);
friend void from_json(const nlohmann::json& j, Rugnux_live_step& o);
protected:
std::string m_Title;
double m_Started_s;
double m_Duration_s;
bool m_Duration_sIsSet;
std::vector<std::string> m_Notes;
};
} // namespace org::openapitools::server::model
#endif /* Rugnux_live_step_H_ */
+192
View File
@@ -0,0 +1,192 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
#include "Rugnux_settings.h"
#include "Helpers.h"
#include <sstream>
namespace org::openapitools::server::model
{
Rugnux_settings::Rugnux_settings()
{
m_Enable = false;
m_EnableIsSet = false;
m_Reserved_cpus = 4L;
m_Reserved_cpusIsSet = false;
m_Deadline_s = 0.0;
m_Deadline_sIsSet = false;
}
void Rugnux_settings::validate() const
{
std::stringstream msg;
if (!validate(msg))
{
throw org::openapitools::server::helpers::ValidationException(msg.str());
}
}
bool Rugnux_settings::validate(std::stringstream& msg) const
{
return validate(msg, "");
}
bool Rugnux_settings::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Rugnux_settings" : pathPrefix;
if (reservedCpusIsSet())
{
const int64_t& value = m_Reserved_cpus;
const std::string currentValuePath = _pathPrefix + ".reservedCpus";
if (value < 0ll)
{
success = false;
msg << currentValuePath << ": must be greater than or equal to 0;";
}
}
if (deadlineSIsSet())
{
const double& value = m_Deadline_s;
const std::string currentValuePath = _pathPrefix + ".deadlineS";
if (value < 0.0)
{
success = false;
msg << currentValuePath << ": must be greater than or equal to 0.0;";
}
}
return success;
}
bool Rugnux_settings::operator==(const Rugnux_settings& rhs) const
{
return
((!enableIsSet() && !rhs.enableIsSet()) || (enableIsSet() && rhs.enableIsSet() && isEnable() == rhs.isEnable())) &&
((!reservedCpusIsSet() && !rhs.reservedCpusIsSet()) || (reservedCpusIsSet() && rhs.reservedCpusIsSet() && getReservedCpus() == rhs.getReservedCpus())) &&
((!deadlineSIsSet() && !rhs.deadlineSIsSet()) || (deadlineSIsSet() && rhs.deadlineSIsSet() && getDeadlineS() == rhs.getDeadlineS()))
;
}
bool Rugnux_settings::operator!=(const Rugnux_settings& rhs) const
{
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const Rugnux_settings& o)
{
j = nlohmann::json::object();
if(o.enableIsSet())
j["enable"] = o.m_Enable;
if(o.reservedCpusIsSet())
j["reserved_cpus"] = o.m_Reserved_cpus;
if(o.deadlineSIsSet())
j["deadline_s"] = o.m_Deadline_s;
}
void from_json(const nlohmann::json& j, Rugnux_settings& o)
{
if(j.find("enable") != j.end())
{
j.at("enable").get_to(o.m_Enable);
o.m_EnableIsSet = true;
}
if(j.find("reserved_cpus") != j.end())
{
j.at("reserved_cpus").get_to(o.m_Reserved_cpus);
o.m_Reserved_cpusIsSet = true;
}
if(j.find("deadline_s") != j.end())
{
j.at("deadline_s").get_to(o.m_Deadline_s);
o.m_Deadline_sIsSet = true;
}
}
bool Rugnux_settings::isEnable() const
{
return m_Enable;
}
void Rugnux_settings::setEnable(bool const value)
{
m_Enable = value;
m_EnableIsSet = true;
}
bool Rugnux_settings::enableIsSet() const
{
return m_EnableIsSet;
}
void Rugnux_settings::unsetEnable()
{
m_EnableIsSet = false;
}
int64_t Rugnux_settings::getReservedCpus() const
{
return m_Reserved_cpus;
}
void Rugnux_settings::setReservedCpus(int64_t const value)
{
m_Reserved_cpus = value;
m_Reserved_cpusIsSet = true;
}
bool Rugnux_settings::reservedCpusIsSet() const
{
return m_Reserved_cpusIsSet;
}
void Rugnux_settings::unsetReserved_cpus()
{
m_Reserved_cpusIsSet = false;
}
double Rugnux_settings::getDeadlineS() const
{
return m_Deadline_s;
}
void Rugnux_settings::setDeadlineS(double const value)
{
m_Deadline_s = value;
m_Deadline_sIsSet = true;
}
bool Rugnux_settings::deadlineSIsSet() const
{
return m_Deadline_sIsSet;
}
void Rugnux_settings::unsetDeadline_s()
{
m_Deadline_sIsSet = false;
}
} // namespace org::openapitools::server::model
+96
View File
@@ -0,0 +1,96 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/*
* Rugnux_settings.h
*
* Rugnux live mode: rotation collections from a DECTRIS detector are processed by rugnux inside the broker while they arrive, and the merged result is sent to the writer.
*/
#ifndef Rugnux_settings_H_
#define Rugnux_settings_H_
#include <nlohmann/json.hpp>
namespace org::openapitools::server::model
{
/// <summary>
/// Rugnux live mode: rotation collections from a DECTRIS detector are processed by rugnux inside the broker while they arrive, and the merged result is sent to the writer.
/// </summary>
class Rugnux_settings
{
public:
Rugnux_settings();
virtual ~Rugnux_settings() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes an error
/// message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and calls it's validate.
/// Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const Rugnux_settings& rhs) const;
bool operator!=(const Rugnux_settings& rhs) const;
/////////////////////////////////////////////
/// Rugnux_settings members
/// <summary>
/// Process DECTRIS rotation collections with rugnux live
/// </summary>
bool isEnable() const;
void setEnable(bool const value);
bool enableIsSet() const;
void unsetEnable();
/// <summary>
/// CPU threads left to the receiver and the image pusher while rugnux runs
/// </summary>
int64_t getReservedCpus() const;
void setReservedCpus(int64_t const value);
bool reservedCpusIsSet() const;
void unsetReserved_cpus();
/// <summary>
/// Fixed cutoff: this long after the last image the analysis is cut, and the collection ends with what it has established (as &#x60;POST /cancel&#x60; after the last image does). 0 means no cutoff.
/// </summary>
double getDeadlineS() const;
void setDeadlineS(double const value);
bool deadlineSIsSet() const;
void unsetDeadline_s();
friend void to_json(nlohmann::json& j, const Rugnux_settings& o);
friend void from_json(const nlohmann::json& j, Rugnux_settings& o);
protected:
bool m_Enable;
bool m_EnableIsSet;
int64_t m_Reserved_cpus;
bool m_Reserved_cpusIsSet;
double m_Deadline_s;
bool m_Deadline_sIsSet;
};
} // namespace org::openapitools::server::model
#endif /* Rugnux_settings_H_ */
+177
View File
@@ -0,0 +1,177 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
#include "Rugnux_status.h"
#include "Helpers.h"
#include <sstream>
namespace org::openapitools::server::model
{
Rugnux_status::Rugnux_status()
{
m_Enable = false;
m_Reserved_cpus = 0L;
m_Deadline_s = 0.0;
m_Deadline_sIsSet = false;
m_Model_loaded = false;
m_Model_size_bytes = 0L;
m_Model_size_bytesIsSet = false;
}
void Rugnux_status::validate() const
{
std::stringstream msg;
if (!validate(msg))
{
throw org::openapitools::server::helpers::ValidationException(msg.str());
}
}
bool Rugnux_status::validate(std::stringstream& msg) const
{
return validate(msg, "");
}
bool Rugnux_status::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Rugnux_status" : pathPrefix;
return success;
}
bool Rugnux_status::operator==(const Rugnux_status& rhs) const
{
return
(isEnable() == rhs.isEnable())
&&
(getReservedCpus() == rhs.getReservedCpus())
&&
((!deadlineSIsSet() && !rhs.deadlineSIsSet()) || (deadlineSIsSet() && rhs.deadlineSIsSet() && getDeadlineS() == rhs.getDeadlineS())) &&
(isModelLoaded() == rhs.isModelLoaded())
&&
((!modelSizeBytesIsSet() && !rhs.modelSizeBytesIsSet()) || (modelSizeBytesIsSet() && rhs.modelSizeBytesIsSet() && getModelSizeBytes() == rhs.getModelSizeBytes()))
;
}
bool Rugnux_status::operator!=(const Rugnux_status& rhs) const
{
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const Rugnux_status& o)
{
j = nlohmann::json::object();
j["enable"] = o.m_Enable;
j["reserved_cpus"] = o.m_Reserved_cpus;
if(o.deadlineSIsSet())
j["deadline_s"] = o.m_Deadline_s;
j["model_loaded"] = o.m_Model_loaded;
if(o.modelSizeBytesIsSet())
j["model_size_bytes"] = o.m_Model_size_bytes;
}
void from_json(const nlohmann::json& j, Rugnux_status& o)
{
j.at("enable").get_to(o.m_Enable);
j.at("reserved_cpus").get_to(o.m_Reserved_cpus);
if(j.find("deadline_s") != j.end())
{
j.at("deadline_s").get_to(o.m_Deadline_s);
o.m_Deadline_sIsSet = true;
}
j.at("model_loaded").get_to(o.m_Model_loaded);
if(j.find("model_size_bytes") != j.end())
{
j.at("model_size_bytes").get_to(o.m_Model_size_bytes);
o.m_Model_size_bytesIsSet = true;
}
}
bool Rugnux_status::isEnable() const
{
return m_Enable;
}
void Rugnux_status::setEnable(bool const value)
{
m_Enable = value;
}
int64_t Rugnux_status::getReservedCpus() const
{
return m_Reserved_cpus;
}
void Rugnux_status::setReservedCpus(int64_t const value)
{
m_Reserved_cpus = value;
}
double Rugnux_status::getDeadlineS() const
{
return m_Deadline_s;
}
void Rugnux_status::setDeadlineS(double const value)
{
m_Deadline_s = value;
m_Deadline_sIsSet = true;
}
bool Rugnux_status::deadlineSIsSet() const
{
return m_Deadline_sIsSet;
}
void Rugnux_status::unsetDeadline_s()
{
m_Deadline_sIsSet = false;
}
bool Rugnux_status::isModelLoaded() const
{
return m_Model_loaded;
}
void Rugnux_status::setModelLoaded(bool const value)
{
m_Model_loaded = value;
}
int64_t Rugnux_status::getModelSizeBytes() const
{
return m_Model_size_bytes;
}
void Rugnux_status::setModelSizeBytes(int64_t const value)
{
m_Model_size_bytes = value;
m_Model_size_bytesIsSet = true;
}
bool Rugnux_status::modelSizeBytesIsSet() const
{
return m_Model_size_bytesIsSet;
}
void Rugnux_status::unsetModel_size_bytes()
{
m_Model_size_bytesIsSet = false;
}
} // namespace org::openapitools::server::model
+108
View File
@@ -0,0 +1,108 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/*
* Rugnux_status.h
*
*
*/
#ifndef Rugnux_status_H_
#define Rugnux_status_H_
#include <nlohmann/json.hpp>
namespace org::openapitools::server::model
{
/// <summary>
///
/// </summary>
class Rugnux_status
{
public:
Rugnux_status();
virtual ~Rugnux_status() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes an error
/// message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and calls it's validate.
/// Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const Rugnux_status& rhs) const;
bool operator!=(const Rugnux_status& rhs) const;
/////////////////////////////////////////////
/// Rugnux_status members
/// <summary>
/// Rugnux live mode is enabled
/// </summary>
bool isEnable() const;
void setEnable(bool const value);
/// <summary>
/// CPU threads left to the receiver and the image pusher while rugnux runs
/// </summary>
int64_t getReservedCpus() const;
void setReservedCpus(int64_t const value);
/// <summary>
/// Seconds after the last image at which the analysis is cut; 0 means no cutoff
/// </summary>
double getDeadlineS() const;
void setDeadlineS(double const value);
bool deadlineSIsSet() const;
void unsetDeadline_s();
/// <summary>
/// A model was uploaded with &#x60;PUT /config/rugnux/model&#x60;
/// </summary>
bool isModelLoaded() const;
void setModelLoaded(bool const value);
/// <summary>
/// Size of the uploaded model file
/// </summary>
int64_t getModelSizeBytes() const;
void setModelSizeBytes(int64_t const value);
bool modelSizeBytesIsSet() const;
void unsetModel_size_bytes();
friend void to_json(nlohmann::json& j, const Rugnux_status& o);
friend void from_json(const nlohmann::json& j, Rugnux_status& o);
protected:
bool m_Enable;
int64_t m_Reserved_cpus;
double m_Deadline_s;
bool m_Deadline_sIsSet;
bool m_Model_loaded;
int64_t m_Model_size_bytes;
bool m_Model_size_bytesIsSet;
};
} // namespace org::openapitools::server::model
#endif /* Rugnux_status_H_ */
+105
View File
@@ -0,0 +1,105 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
#include "Rugnux_summary_row.h"
#include "Helpers.h"
#include <sstream>
namespace org::openapitools::server::model
{
Rugnux_summary_row::Rugnux_summary_row()
{
m_Label = "";
m_Value = "";
}
void Rugnux_summary_row::validate() const
{
std::stringstream msg;
if (!validate(msg))
{
throw org::openapitools::server::helpers::ValidationException(msg.str());
}
}
bool Rugnux_summary_row::validate(std::stringstream& msg) const
{
return validate(msg, "");
}
bool Rugnux_summary_row::validate(std::stringstream& msg, const std::string& pathPrefix) const
{
bool success = true;
const std::string _pathPrefix = pathPrefix.empty() ? "Rugnux_summary_row" : pathPrefix;
return success;
}
bool Rugnux_summary_row::operator==(const Rugnux_summary_row& rhs) const
{
return
(getLabel() == rhs.getLabel())
&&
(getValue() == rhs.getValue())
;
}
bool Rugnux_summary_row::operator!=(const Rugnux_summary_row& rhs) const
{
return !(*this == rhs);
}
void to_json(nlohmann::json& j, const Rugnux_summary_row& o)
{
j = nlohmann::json::object();
j["label"] = o.m_Label;
j["value"] = o.m_Value;
}
void from_json(const nlohmann::json& j, Rugnux_summary_row& o)
{
j.at("label").get_to(o.m_Label);
j.at("value").get_to(o.m_Value);
}
std::string Rugnux_summary_row::getLabel() const
{
return m_Label;
}
void Rugnux_summary_row::setLabel(std::string const& value)
{
m_Label = value;
}
std::string Rugnux_summary_row::getValue() const
{
return m_Value;
}
void Rugnux_summary_row::setValue(std::string const& value)
{
m_Value = value;
}
} // namespace org::openapitools::server::model
+84
View File
@@ -0,0 +1,84 @@
/**
* Jungfraujoch
* API to control Jungfraujoch developed by the Paul Scherrer Institute (Switzerland). Jungfraujoch is a data acquisition and analysis system for pixel array detectors, primarly PSI JUNGFRAU. Jungfraujoch uses FPGA boards to acquire data at high data rates. # License Clarification While this API definition is licensed under GPL-3.0, **the GPL copyleft provisions do not apply** when this file is used solely to generate OpenAPI clients or when implementing applications that interact with the API. Generated client code and applications using this API definition are not subject to the GPL license requirements and may be distributed under terms of your choosing. This exception is similar in spirit to the Linux Kernel's approach to userspace API headers and the GCC Runtime Library Exception. The Linux Kernel developers have explicitly stated that user programs that merely use the kernel interfaces (syscalls, ioctl definitions, etc.) are not derivative works of the kernel and are not subject to the terms of the GPL. This exception is intended to allow wider use of this API specification without imposing GPL requirements on applications that merely interact with the API, regardless of whether they communicate through network calls or other mechanisms.
*
* The version of the OpenAPI document: 1.0.0-rc.175
* Contact: filip.leonarski@psi.ch
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/*
* Rugnux_summary_row.h
*
*
*/
#ifndef Rugnux_summary_row_H_
#define Rugnux_summary_row_H_
#include <string>
#include <nlohmann/json.hpp>
namespace org::openapitools::server::model
{
/// <summary>
///
/// </summary>
class Rugnux_summary_row
{
public:
Rugnux_summary_row();
virtual ~Rugnux_summary_row() = default;
/// <summary>
/// Validate the current data in the model. Throws a ValidationException on failure.
/// </summary>
void validate() const;
/// <summary>
/// Validate the current data in the model. Returns false on error and writes an error
/// message into the given stringstream.
/// </summary>
bool validate(std::stringstream& msg) const;
/// <summary>
/// Helper overload for validate. Used when one model stores another model and calls it's validate.
/// Not meant to be called outside that case.
/// </summary>
bool validate(std::stringstream& msg, const std::string& pathPrefix) const;
bool operator==(const Rugnux_summary_row& rhs) const;
bool operator!=(const Rugnux_summary_row& rhs) const;
/////////////////////////////////////////////
/// Rugnux_summary_row members
/// <summary>
///
/// </summary>
std::string getLabel() const;
void setLabel(std::string const& value);
/// <summary>
///
/// </summary>
std::string getValue() const;
void setValue(std::string const& value);
friend void to_json(nlohmann::json& j, const Rugnux_summary_row& o);
friend void from_json(const nlohmann::json& j, Rugnux_summary_row& o);
protected:
std::string m_Label;
std::string m_Value;
};
} // namespace org::openapitools::server::model
#endif /* Rugnux_summary_row_H_ */
+293
View File
@@ -2684,6 +2684,167 @@ components:
$ref: '#/components/schemas/zeromq_metadata_settings'
dark_mask:
$ref: '#/components/schemas/dark_mask_settings'
rugnux:
$ref: '#/components/schemas/rugnux_settings'
rugnux_settings:
type: object
description: |
Rugnux live mode: rotation collections from a DECTRIS detector are processed by rugnux
inside the broker while they arrive, and the merged result is sent to the writer.
properties:
enable:
type: boolean
default: false
description: Process DECTRIS rotation collections with rugnux live
reserved_cpus:
type: integer
format: int64
minimum: 0
default: 4
description: CPU threads left to the receiver and the image pusher while rugnux runs
deadline_s:
type: number
format: double
minimum: 0.0
default: 0.0
description: |
Fixed cutoff: this long after the last image the analysis is cut, and the collection ends
with what it has established (as `POST /cancel` after the last image does). 0 means no cutoff.
rugnux_status:
type: object
required:
- enable
- reserved_cpus
- model_loaded
properties:
enable:
type: boolean
description: Rugnux live mode is enabled
reserved_cpus:
type: integer
format: int64
description: CPU threads left to the receiver and the image pusher while rugnux runs
deadline_s:
type: number
format: double
description: Seconds after the last image at which the analysis is cut; 0 means no cutoff
model_loaded:
type: boolean
description: A model was uploaded with `PUT /config/rugnux/model`
model_size_bytes:
type: integer
format: int64
description: Size of the uploaded model file
rugnux_live_step:
type: object
required:
- title
- started_s
- notes
properties:
title:
type: string
started_s:
type: number
format: double
description: Start of the step, since the collection started
duration_s:
type: number
format: double
description: Set once the next step starts or the run ends
notes:
type: array
description: What the step found (e.g. lattice, space group)
items:
type: string
rugnux_summary_row:
type: object
required:
- label
- value
properties:
label:
type: string
value:
type: string
rugnux_live_status:
type: object
description: Live rugnux processing of the last collection; kept until the next collection starts
required:
- state
- message
- images_expected
- images_received
- elapsed_s
- steps
- verdict
- verdict_text
- summary
- warnings
- probe
properties:
state:
type: string
enum: [Idle, Running, Done, Failed, Cancelled, Skipped]
probe:
type: string
description: Answer of the early lattice probe on the first 60 degrees; empty until it answers
probe_s:
type: number
format: double
description: When the probe answered, since the collection started
message:
type: string
description: Why the run failed or was skipped
images_expected:
type: integer
format: int64
images_received:
type: integer
format: int64
elapsed_s:
type: number
format: double
step_progress:
type: number
format: float
minimum: 0.0
maximum: 1.0
description: Fraction of the current pass's images
steps:
type: array
items:
$ref: '#/components/schemas/rugnux_live_step'
last_image_s:
type: number
format: double
description: When the last image arrived (and the cutoff timer started), since the collection started
cut:
type: string
description: |
Empty, or why the analysis was cut: `deadline` (`deadline_s` after the last image) or `cancel`
(`POST /cancel`). The end message then carried what was established at the cut.
cut_s:
type: number
format: double
description: When the analysis was cut, since the collection started
last_image_to_written_s:
type: number
format: double
description: From the last image received to all results written
verdict:
type: string
description: OK, WARNINGS, UNUSABLE or FAILED; empty until the run is over
verdict_text:
type: string
summary:
type: array
items:
$ref: '#/components/schemas/rugnux_summary_row'
warnings:
type: array
items:
type: string
zeromq_metadata_settings:
type: object
required:
@@ -2895,6 +3056,11 @@ paths:
Any frame that is currently being processed by CPU will be finished and sent to writer.
Given the command is making sure to gracefully stop data acquisition and detector, it might take some time to switch back after command finished to `Idle` state.
With rugnux live mode (`/config/rugnux`) the command also cuts the rugnux analysis, during the collection
or after its last image alike: the collection ends with what the analysis has established
(see `cut` in `/rugnux/status`). After the last image the collection itself is complete and is not
marked as cancelled.
If data collection is not running, the command has no effect.
responses:
"200":
@@ -3331,6 +3497,133 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/detector_list'
/config/rugnux:
put:
summary: Change rugnux live mode settings
description: |
This can only be done when detector is `Idle`, `Error` or `Inactive` states.
security:
- bearerAuth: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/rugnux_settings'
responses:
"401":
description: The current dataset is protected and no valid bearer token was presented.
content:
text/plain:
schema:
type: string
"200":
description: Everything OK
"400":
description: Input parsing or validation error
content:
text/plain:
schema:
type: string
description: Exception error
"500":
description: Error within Jungfraujoch code - see output message.
content:
application/json:
schema:
$ref: '#/components/schemas/error_message'
get:
summary: Get rugnux live mode settings and model status
description: Can be done anytime
security:
- bearerAuth: []
responses:
"401":
description: The current dataset is protected and no valid bearer token was presented.
content:
text/plain:
schema:
type: string
"200":
description: Everything OK
content:
application/json:
schema:
$ref: '#/components/schemas/rugnux_status'
/config/rugnux/model:
put:
summary: Upload the model used by rugnux live mode
description: |
This can only be done when detector is `Idle`, `Error` or `Inactive` states.
The model (PDB or mmCIF, optionally gzipped; at most 64 MiB) is used for molecular replacement
and validation of every following rugnux live collection, until it is removed with
`DELETE /config/rugnux/model` or replaced by another upload.
security:
- bearerAuth: []
requestBody:
content:
application/octet-stream:
schema:
type: string
format: binary
responses:
"401":
description: The current dataset is protected and no valid bearer token was presented.
content:
text/plain:
schema:
type: string
"200":
description: All good
"400":
description: Empty body
"413":
description: Model larger than 64 MiB
"500":
description: Error within Jungfraujoch code - see output message.
content:
application/json:
schema:
$ref: '#/components/schemas/error_message'
delete:
summary: Remove the model used by rugnux live mode
description: |
This can only be done when detector is `Idle`, `Error` or `Inactive` states.
security:
- bearerAuth: []
responses:
"401":
description: The current dataset is protected and no valid bearer token was presented.
content:
text/plain:
schema:
type: string
"200":
description: All good
"500":
description: Error within Jungfraujoch code - see output message.
content:
application/json:
schema:
$ref: '#/components/schemas/error_message'
/rugnux/status:
get:
summary: Get live rugnux processing status of the last collection
description: Can be done anytime
security:
- bearerAuth: []
responses:
"401":
description: The current dataset is protected and no valid bearer token was presented.
content:
text/plain:
schema:
type: string
"200":
description: Everything OK
content:
application/json:
schema:
$ref: '#/components/schemas/rugnux_live_status'
/config/zeromq_preview:
put:
summary: Set ZeroMQ preview settings
+1
View File
@@ -116,6 +116,7 @@ int main(int argc, char **argv) {
if (receiver)
broker.Services().Receiver(receiver.get());
ParseRugnuxSettings(settings, broker.Services());
httplib::Server server;
httpServer = &server;
+27
View File
@@ -16,6 +16,12 @@
#include "ThreadAffinity.h"
#ifdef __linux__
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>
#endif
// Two shapes of "run this over a range on several threads", used by the analysis code. Both take the
// worker count from the caller rather than asking the hardware, so a run that was told how many
// threads to use keeps to it.
@@ -48,6 +54,20 @@ namespace parallel_detail {
size_t WorkerCount() const { return workers.size(); }
// The scheduling priority of every pool thread (Linux; elsewhere nothing). The pool is made once
// per process, so a thread that lowers its own priority does not lower the work it hands to the
// pool: the broker running rugnux beside its receiving threads sets it here as well. Raising
// it back needs the privilege to do so; returns false where that was refused.
bool SetNice(int nice) {
bool ok = true;
#ifdef __linux__
std::lock_guard lock(m);
for (const int tid: tids)
ok = (setpriority(PRIO_PROCESS, static_cast<id_t>(tid), nice) == 0) && ok;
#endif
return ok;
}
void Submit(std::function<void()> job) {
{
std::lock_guard lock(m);
@@ -66,6 +86,12 @@ namespace parallel_detail {
// It inherited whatever CPUs the thread that first used the pool was kept to.
RestoreThreadAffinity();
InWorkerFlag() = true;
#ifdef __linux__
{
std::lock_guard lock(m);
tids.push_back(static_cast<int>(syscall(SYS_gettid)));
}
#endif
Loop();
});
} catch (...) {
@@ -106,6 +132,7 @@ namespace parallel_detail {
std::condition_variable cv;
std::deque<std::function<void()> > queue;
std::vector<std::thread> workers;
std::vector<int> tids; // the workers' kernel thread ids, for SetNice
bool stop = false;
// A function-local thread_local rather than an inline static member: Apple's linker rejects the
+21
View File
@@ -170,6 +170,27 @@ The writers report each closed data file to the broker (TCP protocol version 5,
for `FILE_CLOSED`); the in-process HDF5 writer reports to it directly. The ZeroMQ writer has no way
back to the broker, so with it the route answers 409.
## Rugnux live mode
With rugnux live mode on, rotation collections from a DECTRIS detector are processed by rugnux inside
the broker while they arrive. It is off by default; the configuration file switches it on with
`"rugnux": {"enable": true, "reserved_cpus": 4}`, where `reserved_cpus` is the number of CPU threads
left to the receiver and the image pusher.
The same settings are read with `GET /config/rugnux` and changed with `PUT /config/rugnux`. A model
for molecular replacement and validation (PDB or mmCIF, optionally gzipped, at most 64 MiB) is
uploaded as the raw file with `PUT /config/rugnux/model` (`application/octet-stream`; a larger body
gets `413`). The broker keeps it, readable only by its own user, for every following collection until
`DELETE /config/rugnux/model` removes it or another upload replaces it. `GET /config/rugnux` reports
whether a model is loaded and its size, never its contents. Changes are refused during a collection,
and while the current dataset was started with tokens, all of these routes need its bearer token.
`GET /rugnux/status` reports the live run of the last collection, kept until the next one starts: its
state, the images received, the step it is on and how far into it, the steps so far with what each
found, and once it is over the verdict, the summary, warnings and the time from the last image to
every result written. The web frontend shows it on the dashboard. It needs the dataset's bearer token
like the routes above.
## Setting up a local test for Jungfraujoch
For development, it is possible to set up a local installation of Jungfraujoch.
This will work without FPGA installed in the computer and allows testing the Jungfraujoch software layer, including
+11
View File
@@ -126,6 +126,10 @@ Class | Method | HTTP request | Description
*DefaultApi* | [**config_mask_tiff_get**](docs/DefaultApi.md#config_mask_tiff_get) | **GET** /config/mask.tiff | Get mask of the detector (TIFF)
*DefaultApi* | [**config_roi_get**](docs/DefaultApi.md#config_roi_get) | **GET** /config/roi | Get ROI definitions
*DefaultApi* | [**config_roi_put**](docs/DefaultApi.md#config_roi_put) | **PUT** /config/roi | Upload ROI definitions
*DefaultApi* | [**config_rugnux_get**](docs/DefaultApi.md#config_rugnux_get) | **GET** /config/rugnux | Get rugnux live mode settings and model status
*DefaultApi* | [**config_rugnux_model_delete**](docs/DefaultApi.md#config_rugnux_model_delete) | **DELETE** /config/rugnux/model | Remove the model used by rugnux live mode
*DefaultApi* | [**config_rugnux_model_put**](docs/DefaultApi.md#config_rugnux_model_put) | **PUT** /config/rugnux/model | Upload the model used by rugnux live mode
*DefaultApi* | [**config_rugnux_put**](docs/DefaultApi.md#config_rugnux_put) | **PUT** /config/rugnux | Change rugnux live mode settings
*DefaultApi* | [**config_select_detector_get**](docs/DefaultApi.md#config_select_detector_get) | **GET** /config/select_detector | List available detectors
*DefaultApi* | [**config_select_detector_put**](docs/DefaultApi.md#config_select_detector_put) | **PUT** /config/select_detector | Select detector
*DefaultApi* | [**config_spot_finding_get**](docs/DefaultApi.md#config_spot_finding_get) | **GET** /config/spot_finding | Get data processing configuration
@@ -149,11 +153,13 @@ Class | Method | HTTP request | Description
*DefaultApi* | [**image_buffer_status_get**](docs/DefaultApi.md#image_buffer_status_get) | **GET** /image_buffer/status | Get status of the image buffers
*DefaultApi* | [**image_pusher_status_get**](docs/DefaultApi.md#image_pusher_status_get) | **GET** /image_pusher/status | Get status of image pusher
*DefaultApi* | [**initialize_post**](docs/DefaultApi.md#initialize_post) | **POST** /initialize | Initialize detector and data acquisition
*DefaultApi* | [**live_events_get**](docs/DefaultApi.md#live_events_get) | **GET** /live/events | Event stream of the collection, for a reader following it
*DefaultApi* | [**pedestal_post**](docs/DefaultApi.md#pedestal_post) | **POST** /pedestal | Collect dark current for the detector
*DefaultApi* | [**preview_pedestal_tiff_get**](docs/DefaultApi.md#preview_pedestal_tiff_get) | **GET** /preview/pedestal.tiff | Get pedestal in TIFF format
*DefaultApi* | [**preview_plot_bin_get**](docs/DefaultApi.md#preview_plot_bin_get) | **GET** /preview/plot.bin | Generate 1D plot from Jungfraujoch and send in raw binary format. Data are provided as (32-bit) float binary array. This format doesn&#39;t transmit information about X-axis, only values, so it is of limited use for azimuthal integration.
*DefaultApi* | [**preview_plot_get**](docs/DefaultApi.md#preview_plot_get) | **GET** /preview/plot | Generate 1D plot from Jungfraujoch
*DefaultApi* | [**result_scan_get**](docs/DefaultApi.md#result_scan_get) | **GET** /result/scan | Get full scan result
*DefaultApi* | [**rugnux_status_get**](docs/DefaultApi.md#rugnux_status_get) | **GET** /rugnux/status | Get live rugnux processing status of the last collection
*DefaultApi* | [**start_post**](docs/DefaultApi.md#start_post) | **POST** /start | Start detector
*DefaultApi* | [**statistics_calibration_get**](docs/DefaultApi.md#statistics_calibration_get) | **GET** /statistics/calibration | Get calibration statistics
*DefaultApi* | [**statistics_data_collection_get**](docs/DefaultApi.md#statistics_data_collection_get) | **GET** /statistics/data_collection | Get data collection statistics
@@ -223,6 +229,11 @@ Class | Method | HTTP request | Description
- [RoiCircleList](docs/RoiCircleList.md)
- [RoiDefinitions](docs/RoiDefinitions.md)
- [RotationAxis](docs/RotationAxis.md)
- [RugnuxLiveStatus](docs/RugnuxLiveStatus.md)
- [RugnuxLiveStep](docs/RugnuxLiveStep.md)
- [RugnuxSettings](docs/RugnuxSettings.md)
- [RugnuxStatus](docs/RugnuxStatus.md)
- [RugnuxSummaryRow](docs/RugnuxSummaryRow.md)
- [ScanResult](docs/ScanResult.md)
- [ScanResultImagesInner](docs/ScanResultImagesInner.md)
- [SpotFindingSettings](docs/SpotFindingSettings.md)
+483
View File
@@ -29,6 +29,10 @@ Method | HTTP request | Description
[**config_mask_tiff_get**](DefaultApi.md#config_mask_tiff_get) | **GET** /config/mask.tiff | Get mask of the detector (TIFF)
[**config_roi_get**](DefaultApi.md#config_roi_get) | **GET** /config/roi | Get ROI definitions
[**config_roi_put**](DefaultApi.md#config_roi_put) | **PUT** /config/roi | Upload ROI definitions
[**config_rugnux_get**](DefaultApi.md#config_rugnux_get) | **GET** /config/rugnux | Get rugnux live mode settings and model status
[**config_rugnux_model_delete**](DefaultApi.md#config_rugnux_model_delete) | **DELETE** /config/rugnux/model | Remove the model used by rugnux live mode
[**config_rugnux_model_put**](DefaultApi.md#config_rugnux_model_put) | **PUT** /config/rugnux/model | Upload the model used by rugnux live mode
[**config_rugnux_put**](DefaultApi.md#config_rugnux_put) | **PUT** /config/rugnux | Change rugnux live mode settings
[**config_select_detector_get**](DefaultApi.md#config_select_detector_get) | **GET** /config/select_detector | List available detectors
[**config_select_detector_put**](DefaultApi.md#config_select_detector_put) | **PUT** /config/select_detector | Select detector
[**config_spot_finding_get**](DefaultApi.md#config_spot_finding_get) | **GET** /config/spot_finding | Get data processing configuration
@@ -52,11 +56,13 @@ Method | HTTP request | Description
[**image_buffer_status_get**](DefaultApi.md#image_buffer_status_get) | **GET** /image_buffer/status | Get status of the image buffers
[**image_pusher_status_get**](DefaultApi.md#image_pusher_status_get) | **GET** /image_pusher/status | Get status of image pusher
[**initialize_post**](DefaultApi.md#initialize_post) | **POST** /initialize | Initialize detector and data acquisition
[**live_events_get**](DefaultApi.md#live_events_get) | **GET** /live/events | Event stream of the collection, for a reader following it
[**pedestal_post**](DefaultApi.md#pedestal_post) | **POST** /pedestal | Collect dark current for the detector
[**preview_pedestal_tiff_get**](DefaultApi.md#preview_pedestal_tiff_get) | **GET** /preview/pedestal.tiff | Get pedestal in TIFF format
[**preview_plot_bin_get**](DefaultApi.md#preview_plot_bin_get) | **GET** /preview/plot.bin | Generate 1D plot from Jungfraujoch and send in raw binary format. Data are provided as (32-bit) float binary array. This format doesn&#39;t transmit information about X-axis, only values, so it is of limited use for azimuthal integration.
[**preview_plot_get**](DefaultApi.md#preview_plot_get) | **GET** /preview/plot | Generate 1D plot from Jungfraujoch
[**result_scan_get**](DefaultApi.md#result_scan_get) | **GET** /result/scan | Get full scan result
[**rugnux_status_get**](DefaultApi.md#rugnux_status_get) | **GET** /rugnux/status | Get live rugnux processing status of the last collection
[**start_post**](DefaultApi.md#start_post) | **POST** /start | Start detector
[**statistics_calibration_get**](DefaultApi.md#statistics_calibration_get) | **GET** /statistics/calibration | Get calibration statistics
[**statistics_data_collection_get**](DefaultApi.md#statistics_data_collection_get) | **GET** /statistics/data_collection | Get data collection statistics
@@ -1729,6 +1735,318 @@ No authorization required
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **config_rugnux_get**
> RugnuxStatus config_rugnux_get()
Get rugnux live mode settings and model status
Can be done anytime
### Example
* Bearer Authentication (bearerAuth):
```python
import jfjoch_client
from jfjoch_client.models.rugnux_status import RugnuxStatus
from jfjoch_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost:5232
# See configuration.py for a list of all supported configuration parameters.
configuration = jfjoch_client.Configuration(
host = "http://localhost:5232"
)
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
# Configure Bearer authorization: bearerAuth
configuration = jfjoch_client.Configuration(
access_token = os.environ["BEARER_TOKEN"]
)
# Enter a context with an instance of the API client
with jfjoch_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = jfjoch_client.DefaultApi(api_client)
try:
# Get rugnux live mode settings and model status
api_response = api_instance.config_rugnux_get()
print("The response of DefaultApi->config_rugnux_get:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DefaultApi->config_rugnux_get: %s\n" % e)
```
### Parameters
This endpoint does not need any parameter.
### Return type
[**RugnuxStatus**](RugnuxStatus.md)
### Authorization
[bearerAuth](../README.md#bearerAuth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: text/plain, application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**401** | The current dataset is protected and no valid bearer token was presented. | - |
**200** | Everything OK | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **config_rugnux_model_delete**
> config_rugnux_model_delete()
Remove the model used by rugnux live mode
This can only be done when detector is `Idle`, `Error` or `Inactive` states.
### Example
* Bearer Authentication (bearerAuth):
```python
import jfjoch_client
from jfjoch_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost:5232
# See configuration.py for a list of all supported configuration parameters.
configuration = jfjoch_client.Configuration(
host = "http://localhost:5232"
)
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
# Configure Bearer authorization: bearerAuth
configuration = jfjoch_client.Configuration(
access_token = os.environ["BEARER_TOKEN"]
)
# Enter a context with an instance of the API client
with jfjoch_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = jfjoch_client.DefaultApi(api_client)
try:
# Remove the model used by rugnux live mode
api_instance.config_rugnux_model_delete()
except Exception as e:
print("Exception when calling DefaultApi->config_rugnux_model_delete: %s\n" % e)
```
### Parameters
This endpoint does not need any parameter.
### Return type
void (empty response body)
### Authorization
[bearerAuth](../README.md#bearerAuth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: text/plain, application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**401** | The current dataset is protected and no valid bearer token was presented. | - |
**200** | All good | - |
**500** | Error within Jungfraujoch code - see output message. | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **config_rugnux_model_put**
> config_rugnux_model_put(body=body)
Upload the model used by rugnux live mode
This can only be done when detector is `Idle`, `Error` or `Inactive` states.
The model (PDB or mmCIF, optionally gzipped; at most 64 MiB) is used for molecular replacement
and validation of every following rugnux live collection, until it is removed with
`DELETE /config/rugnux/model` or replaced by another upload.
### Example
* Bearer Authentication (bearerAuth):
```python
import jfjoch_client
from jfjoch_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost:5232
# See configuration.py for a list of all supported configuration parameters.
configuration = jfjoch_client.Configuration(
host = "http://localhost:5232"
)
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
# Configure Bearer authorization: bearerAuth
configuration = jfjoch_client.Configuration(
access_token = os.environ["BEARER_TOKEN"]
)
# Enter a context with an instance of the API client
with jfjoch_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = jfjoch_client.DefaultApi(api_client)
body = None # bytearray | (optional)
try:
# Upload the model used by rugnux live mode
api_instance.config_rugnux_model_put(body=body)
except Exception as e:
print("Exception when calling DefaultApi->config_rugnux_model_put: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**body** | **bytearray**| | [optional]
### Return type
void (empty response body)
### Authorization
[bearerAuth](../README.md#bearerAuth)
### HTTP request headers
- **Content-Type**: application/octet-stream
- **Accept**: text/plain, application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**401** | The current dataset is protected and no valid bearer token was presented. | - |
**200** | All good | - |
**400** | Empty body | - |
**413** | Model larger than 64 MiB | - |
**500** | Error within Jungfraujoch code - see output message. | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **config_rugnux_put**
> config_rugnux_put(rugnux_settings=rugnux_settings)
Change rugnux live mode settings
This can only be done when detector is `Idle`, `Error` or `Inactive` states.
### Example
* Bearer Authentication (bearerAuth):
```python
import jfjoch_client
from jfjoch_client.models.rugnux_settings import RugnuxSettings
from jfjoch_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost:5232
# See configuration.py for a list of all supported configuration parameters.
configuration = jfjoch_client.Configuration(
host = "http://localhost:5232"
)
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
# Configure Bearer authorization: bearerAuth
configuration = jfjoch_client.Configuration(
access_token = os.environ["BEARER_TOKEN"]
)
# Enter a context with an instance of the API client
with jfjoch_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = jfjoch_client.DefaultApi(api_client)
rugnux_settings = jfjoch_client.RugnuxSettings() # RugnuxSettings | (optional)
try:
# Change rugnux live mode settings
api_instance.config_rugnux_put(rugnux_settings=rugnux_settings)
except Exception as e:
print("Exception when calling DefaultApi->config_rugnux_put: %s\n" % e)
```
### Parameters
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**rugnux_settings** | [**RugnuxSettings**](RugnuxSettings.md)| | [optional]
### Return type
void (empty response body)
### Authorization
[bearerAuth](../README.md#bearerAuth)
### HTTP request headers
- **Content-Type**: application/json
- **Accept**: text/plain, application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**401** | The current dataset is protected and no valid bearer token was presented. | - |
**200** | Everything OK | - |
**400** | Input parsing or validation error | - |
**500** | Error within Jungfraujoch code - see output message. | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **config_select_detector_get**
> DetectorList config_select_detector_get()
@@ -3326,6 +3644,96 @@ No authorization required
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **live_events_get**
> str live_events_get()
Event stream of the collection, for a reader following it
Server-sent events (text/event-stream) for the current collection, from the moment /start has
accepted it: first its whole history, then every change, until it ends or a new collection
begins. Only one reader at a time. Events, each with a JSON `data` line:
- `start` {run_number, run_name, file_prefix, number_of_images, images_per_file, cbor} - first,
once the start message is known: at once for a JUNGFRAU, when the detector sends its own for
a DECTRIS. `cbor` is the start message the writer of the master file received, CBOR in base64;
a reader builds the dataset from it;
- `file` {file_number, file, first_image, image_count} - a data file closed and renamed into
place; `file` is relative to the collection's root, as the writer named it;
- `end` {images, error?} - every writer acknowledged the end: the data files and the master
file are in place; `images` is the number of images collected. Also sent, with `error`, when
the measurement ends any other way - before the start message, for one. The stream then closes;
- `superseded` {run_number} - a new collection began. The stream then closes.
A comment line (`: keepalive`) every 10 s when nothing happens.
### Example
* Bearer Authentication (bearerAuth):
```python
import jfjoch_client
from jfjoch_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost:5232
# See configuration.py for a list of all supported configuration parameters.
configuration = jfjoch_client.Configuration(
host = "http://localhost:5232"
)
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
# Configure Bearer authorization: bearerAuth
configuration = jfjoch_client.Configuration(
access_token = os.environ["BEARER_TOKEN"]
)
# Enter a context with an instance of the API client
with jfjoch_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = jfjoch_client.DefaultApi(api_client)
try:
# Event stream of the collection, for a reader following it
api_response = api_instance.live_events_get()
print("The response of DefaultApi->live_events_get:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DefaultApi->live_events_get: %s\n" % e)
```
### Parameters
This endpoint does not need any parameter.
### Return type
**str**
### Authorization
[bearerAuth](../README.md#bearerAuth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: text/event-stream, text/plain
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**200** | Event stream | - |
**401** | The current dataset is protected and no valid bearer token was presented. | - |
**404** | No collection has started | - |
**409** | Another reader is following the collection, or the image pusher does not report closed data files | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **pedestal_post**
> pedestal_post()
@@ -3702,6 +4110,81 @@ This endpoint does not need any parameter.
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **rugnux_status_get**
> RugnuxLiveStatus rugnux_status_get()
Get live rugnux processing status of the last collection
Can be done anytime
### Example
* Bearer Authentication (bearerAuth):
```python
import jfjoch_client
from jfjoch_client.models.rugnux_live_status import RugnuxLiveStatus
from jfjoch_client.rest import ApiException
from pprint import pprint
# Defining the host is optional and defaults to http://localhost:5232
# See configuration.py for a list of all supported configuration parameters.
configuration = jfjoch_client.Configuration(
host = "http://localhost:5232"
)
# The client must configure the authentication and authorization parameters
# in accordance with the API server security policy.
# Examples for each auth method are provided below, use the example that
# satisfies your auth use case.
# Configure Bearer authorization: bearerAuth
configuration = jfjoch_client.Configuration(
access_token = os.environ["BEARER_TOKEN"]
)
# Enter a context with an instance of the API client
with jfjoch_client.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = jfjoch_client.DefaultApi(api_client)
try:
# Get live rugnux processing status of the last collection
api_response = api_instance.rugnux_status_get()
print("The response of DefaultApi->rugnux_status_get:\n")
pprint(api_response)
except Exception as e:
print("Exception when calling DefaultApi->rugnux_status_get: %s\n" % e)
```
### Parameters
This endpoint does not need any parameter.
### Return type
[**RugnuxLiveStatus**](RugnuxLiveStatus.md)
### Authorization
[bearerAuth](../README.md#bearerAuth)
### HTTP request headers
- **Content-Type**: Not defined
- **Accept**: text/plain, application/json
### HTTP response details
| Status code | Description | Response headers |
|-------------|-------------|------------------|
**401** | The current dataset is protected and no valid bearer token was presented. | - |
**200** | Everything OK | - |
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **start_post**
> start_post(dataset_settings=dataset_settings)
@@ -27,6 +27,7 @@ Name | Type | Description | Notes
**zeromq_preview** | [**ZeromqPreviewSettings**](ZeromqPreviewSettings.md) | | [optional]
**zeromq_metadata** | [**ZeromqMetadataSettings**](ZeromqMetadataSettings.md) | | [optional]
**dark_mask** | [**DarkMaskSettings**](DarkMaskSettings.md) | | [optional]
**rugnux** | [**RugnuxSettings**](RugnuxSettings.md) | | [optional]
## Example
@@ -0,0 +1,43 @@
# RugnuxLiveStatus
Live rugnux processing of the last collection; kept until the next collection starts
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**state** | **str** | |
**probe** | **str** | Answer of the early lattice probe on the first 60 degrees; empty until it answers |
**probe_s** | **float** | When the probe answered, since the collection started | [optional]
**message** | **str** | Why the run failed or was skipped |
**images_expected** | **int** | |
**images_received** | **int** | |
**elapsed_s** | **float** | |
**step_progress** | **float** | Fraction of the current pass&#39;s images | [optional]
**steps** | [**List[RugnuxLiveStep]**](RugnuxLiveStep.md) | |
**last_image_to_written_s** | **float** | From the last image received to all results written | [optional]
**verdict** | **str** | OK, WARNINGS, UNUSABLE or FAILED; empty until the run is over |
**verdict_text** | **str** | |
**summary** | [**List[RugnuxSummaryRow]**](RugnuxSummaryRow.md) | |
**warnings** | **List[str]** | |
## Example
```python
from jfjoch_client.models.rugnux_live_status import RugnuxLiveStatus
# TODO update the JSON string below
json = "{}"
# create an instance of RugnuxLiveStatus from a JSON string
rugnux_live_status_instance = RugnuxLiveStatus.from_json(json)
# print the JSON string representation of the object
print(RugnuxLiveStatus.to_json())
# convert the object into a dict
rugnux_live_status_dict = rugnux_live_status_instance.to_dict()
# create an instance of RugnuxLiveStatus from a dict
rugnux_live_status_from_dict = RugnuxLiveStatus.from_dict(rugnux_live_status_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+32
View File
@@ -0,0 +1,32 @@
# RugnuxLiveStep
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**title** | **str** | |
**started_s** | **float** | Start of the step, since the collection started |
**duration_s** | **float** | Set once the next step starts or the run ends | [optional]
**notes** | **List[str]** | What the step found (e.g. lattice, space group) |
## Example
```python
from jfjoch_client.models.rugnux_live_step import RugnuxLiveStep
# TODO update the JSON string below
json = "{}"
# create an instance of RugnuxLiveStep from a JSON string
rugnux_live_step_instance = RugnuxLiveStep.from_json(json)
# print the JSON string representation of the object
print(RugnuxLiveStep.to_json())
# convert the object into a dict
rugnux_live_step_dict = rugnux_live_step_instance.to_dict()
# create an instance of RugnuxLiveStep from a dict
rugnux_live_step_from_dict = RugnuxLiveStep.from_dict(rugnux_live_step_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+31
View File
@@ -0,0 +1,31 @@
# RugnuxSettings
Rugnux live mode: rotation collections from a DECTRIS detector are processed by rugnux inside the broker while they arrive, and the merged result is sent to the writer.
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**enable** | **bool** | Process DECTRIS rotation collections with rugnux live | [optional] [default to False]
**reserved_cpus** | **int** | CPU threads left to the receiver and the image pusher while rugnux runs | [optional] [default to 4]
## Example
```python
from jfjoch_client.models.rugnux_settings import RugnuxSettings
# TODO update the JSON string below
json = "{}"
# create an instance of RugnuxSettings from a JSON string
rugnux_settings_instance = RugnuxSettings.from_json(json)
# print the JSON string representation of the object
print(RugnuxSettings.to_json())
# convert the object into a dict
rugnux_settings_dict = rugnux_settings_instance.to_dict()
# create an instance of RugnuxSettings from a dict
rugnux_settings_from_dict = RugnuxSettings.from_dict(rugnux_settings_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+32
View File
@@ -0,0 +1,32 @@
# RugnuxStatus
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**enable** | **bool** | Rugnux live mode is enabled |
**reserved_cpus** | **int** | CPU threads left to the receiver and the image pusher while rugnux runs |
**model_loaded** | **bool** | A model was uploaded with &#x60;PUT /config/rugnux/model&#x60; |
**model_size_bytes** | **int** | Size of the uploaded model file | [optional]
## Example
```python
from jfjoch_client.models.rugnux_status import RugnuxStatus
# TODO update the JSON string below
json = "{}"
# create an instance of RugnuxStatus from a JSON string
rugnux_status_instance = RugnuxStatus.from_json(json)
# print the JSON string representation of the object
print(RugnuxStatus.to_json())
# convert the object into a dict
rugnux_status_dict = rugnux_status_instance.to_dict()
# create an instance of RugnuxStatus from a dict
rugnux_status_from_dict = RugnuxStatus.from_dict(rugnux_status_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
@@ -0,0 +1,30 @@
# RugnuxSummaryRow
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**label** | **str** | |
**value** | **str** | |
## Example
```python
from jfjoch_client.models.rugnux_summary_row import RugnuxSummaryRow
# TODO update the JSON string below
json = "{}"
# create an instance of RugnuxSummaryRow from a JSON string
rugnux_summary_row_instance = RugnuxSummaryRow.from_json(json)
# print the JSON string representation of the object
print(RugnuxSummaryRow.to_json())
# convert the object into a dict
rugnux_summary_row_dict = rugnux_summary_row_instance.to_dict()
# create an instance of RugnuxSummaryRow from a dict
rugnux_summary_row_from_dict = RugnuxSummaryRow.from_dict(rugnux_summary_row_dict)
```
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
+10 -1
View File
@@ -24,6 +24,7 @@ import MenuBookIcon from "@mui/icons-material/MenuBook";
import HistoryIcon from "@mui/icons-material/History";
import CodeIcon from "@mui/icons-material/Code";
import GavelIcon from "@mui/icons-material/Gavel";
import AutoGraphIcon from "@mui/icons-material/AutoGraph";
import _ from "lodash";
import Markdown from "react-markdown";
import remarkGfm from "remark-gfm";
@@ -32,6 +33,8 @@ import StatusBar from "./components/StatusBar";
import ErrorMessage from "./components/ErrorMessage";
import DataProcessingPlots from "./components/DataProcessingPlots";
import MeasurementStatistics from "./components/MeasurementStatistics";
import RugnuxStatus from "./components/RugnuxStatus";
import RugnuxPage from "./components/RugnuxPage";
import DataProcessingSettings from "./components/DataProcessingSettings";
import PreviewImage from "./components/PreviewImage";
import DataCollection from "./components/DataCollection";
@@ -168,7 +171,10 @@ function App() {
<DataProcessingPlots height={550} type={plot_type.BKG_ESTIMATE}/>
</Grid>
<Grid item xs={12} lg={4}>
<MeasurementStatistics s={s.measurement ?? {}}/>
<Stack spacing={3}>
<MeasurementStatistics s={s.measurement ?? {}}/>
<RugnuxStatus/>
</Stack>
</Grid>
<Grid item xs={12} lg={8}>
<DataProcessingPlots height={800} type={plot_type.INDEXING_RATE}/>
@@ -188,6 +194,9 @@ function App() {
{ id: 'data-collection', label: 'Data collection', icon: <VideocamIcon/>, render: () => (
<DataCollection frame_time_us={s.detector_settings?.frame_time_us ?? 500}/>
)},
{ id: 'rugnux', label: 'Rugnux live', icon: <AutoGraphIcon/>, render: () => (
<RugnuxPage measuring={isMeasuring || s.broker?.state === 'Busy'}/>
)},
]},
{ subheader: 'Expert', items: [
{ id: 'detector', label: 'Detector', icon: <PhotoCameraIcon/>, render: () => (
@@ -3,8 +3,8 @@
import { type DefaultError, queryOptions, type UseMutationOptions } from '@tanstack/react-query';
import { client } from '../client.gen';
import { getConfigAzimInt, getConfigBraggIntegration, getConfigDarkMask, getConfigDetector, getConfigFileWriter, getConfigImageFormat, getConfigIndexing, getConfigInstrument, getConfigMask, getConfigMaskTiff, getConfigRoi, getConfigSelectDetector, getConfigSpotFinding, getConfigUserMask, getConfigUserMaskTiff, getConfigZeromqMetadata, getConfigZeromqPreview, getDetectorStatus, getFpgaStatus, getImageBufferImageCbor, getImageBufferImageJpeg, getImageBufferImageTiff, getImageBufferStartCbor, getImageBufferStatus, getImagePusherStatus, getPreviewPedestalTiff, getPreviewPlot, getPreviewPlotBin, getResultScan, getStatistics, getStatisticsCalibration, getStatisticsDataCollection, getStatus, getVersion, getXfelEventCode, getXfelPulseId, type Options, postCancel, postConfigImageFormatConversion, postConfigImageFormatRaw, postDeactivate, postImageBufferClear, postInitialize, postPedestal, postStart, postTrigger, postWaitTillDone, postWaitUntilRunning, putConfigAzimInt, putConfigBraggIntegration, putConfigDarkMask, putConfigDetector, putConfigFileWriter, putConfigImageFormat, putConfigIndexing, putConfigInstrument, putConfigInternalGeneratorImage, putConfigInternalGeneratorImageTiff, putConfigRoi, putConfigSelectDetector, putConfigSpotFinding, putConfigUserMask, putConfigUserMaskTiff, putConfigZeromqMetadata, putConfigZeromqPreview } from '../sdk.gen';
import type { getConfigAzimIntData, getConfigAzimIntResponse, getConfigBraggIntegrationData, getConfigBraggIntegrationResponse, getConfigDarkMaskData, getConfigDarkMaskResponse, getConfigDetectorData, getConfigDetectorResponse, getConfigFileWriterData, getConfigFileWriterResponse, getConfigImageFormatData, getConfigImageFormatResponse, getConfigIndexingData, getConfigIndexingResponse, getConfigInstrumentData, getConfigInstrumentResponse, getConfigMaskData, getConfigMaskResponse, getConfigMaskTiffData, getConfigMaskTiffResponse, getConfigRoiData, getConfigRoiResponse, getConfigSelectDetectorData, getConfigSelectDetectorResponse, getConfigSpotFindingData, getConfigSpotFindingResponse, getConfigUserMaskData, getConfigUserMaskResponse, getConfigUserMaskTiffData, getConfigUserMaskTiffResponse, getConfigZeromqMetadataData, getConfigZeromqMetadataResponse, getConfigZeromqPreviewData, getConfigZeromqPreviewResponse, getDetectorStatusData, getDetectorStatusError, getDetectorStatusResponse, getFpgaStatusData, getFpgaStatusResponse, getImageBufferImageCborData, getImageBufferImageCborError, getImageBufferImageCborResponse, getImageBufferImageJpegData, getImageBufferImageJpegError, getImageBufferImageJpegResponse, getImageBufferImageTiffData, getImageBufferImageTiffError, getImageBufferImageTiffResponse, getImageBufferStartCborData, getImageBufferStartCborError, getImageBufferStartCborResponse, getImageBufferStatusData, getImageBufferStatusError, getImageBufferStatusResponse, getImagePusherStatusData, getImagePusherStatusError, getImagePusherStatusResponse, getPreviewPedestalTiffData, getPreviewPedestalTiffResponse, getPreviewPlotBinData, getPreviewPlotBinError, getPreviewPlotBinResponse, getPreviewPlotData, getPreviewPlotError, getPreviewPlotResponse, getResultScanData, getResultScanError, getResultScanResponse, getStatisticsCalibrationData, getStatisticsCalibrationResponse, getStatisticsData, getStatisticsDataCollectionData, getStatisticsDataCollectionError, getStatisticsDataCollectionResponse, getStatisticsResponse, getStatusData, getStatusResponse, getVersionData, getVersionResponse, getXfelEventCodeData, getXfelEventCodeResponse, getXfelPulseIdData, getXfelPulseIdResponse, postCancelData, postConfigImageFormatConversionData, postConfigImageFormatConversionError, postConfigImageFormatRawData, postConfigImageFormatRawError, postDeactivateData, postDeactivateError, postImageBufferClearData, postImageBufferClearError, postInitializeData, postInitializeError, postPedestalData, postPedestalError, postStartData, postStartError, postTriggerData, postWaitTillDoneData, postWaitTillDoneError, postWaitUntilRunningData, postWaitUntilRunningError, putConfigAzimIntData, putConfigAzimIntError, putConfigBraggIntegrationData, putConfigBraggIntegrationError, putConfigDarkMaskData, putConfigDarkMaskError, putConfigDetectorData, putConfigDetectorError, putConfigFileWriterData, putConfigFileWriterError, putConfigImageFormatData, putConfigImageFormatError, putConfigIndexingData, putConfigIndexingError, putConfigInstrumentData, putConfigInstrumentError, putConfigInternalGeneratorImageData, putConfigInternalGeneratorImageError, putConfigInternalGeneratorImageTiffData, putConfigInternalGeneratorImageTiffError, putConfigRoiData, putConfigRoiError, putConfigSelectDetectorData, putConfigSelectDetectorError, putConfigSpotFindingData, putConfigSpotFindingError, putConfigUserMaskData, putConfigUserMaskError, putConfigUserMaskTiffData, putConfigUserMaskTiffError, putConfigZeromqMetadataData, putConfigZeromqMetadataError, putConfigZeromqPreviewData, putConfigZeromqPreviewError } from '../types.gen';
import { deleteConfigRugnuxModel, getConfigAzimInt, getConfigBraggIntegration, getConfigDarkMask, getConfigDetector, getConfigFileWriter, getConfigImageFormat, getConfigIndexing, getConfigInstrument, getConfigMask, getConfigMaskTiff, getConfigRoi, getConfigRugnux, getConfigSelectDetector, getConfigSpotFinding, getConfigUserMask, getConfigUserMaskTiff, getConfigZeromqMetadata, getConfigZeromqPreview, getDetectorStatus, getFpgaStatus, getImageBufferImageCbor, getImageBufferImageJpeg, getImageBufferImageTiff, getImageBufferStartCbor, getImageBufferStatus, getImagePusherStatus, getPreviewPedestalTiff, getPreviewPlot, getPreviewPlotBin, getResultScan, getRugnuxStatus, getStatistics, getStatisticsCalibration, getStatisticsDataCollection, getStatus, getVersion, getXfelEventCode, getXfelPulseId, type Options, postCancel, postConfigImageFormatConversion, postConfigImageFormatRaw, postDeactivate, postImageBufferClear, postInitialize, postPedestal, postStart, postTrigger, postWaitTillDone, postWaitUntilRunning, putConfigAzimInt, putConfigBraggIntegration, putConfigDarkMask, putConfigDetector, putConfigFileWriter, putConfigImageFormat, putConfigIndexing, putConfigInstrument, putConfigInternalGeneratorImage, putConfigInternalGeneratorImageTiff, putConfigRoi, putConfigRugnux, putConfigRugnuxModel, putConfigSelectDetector, putConfigSpotFinding, putConfigUserMask, putConfigUserMaskTiff, putConfigZeromqMetadata, putConfigZeromqPreview } from '../sdk.gen';
import type { deleteConfigRugnuxModelData, deleteConfigRugnuxModelError, getConfigAzimIntData, getConfigAzimIntResponse, getConfigBraggIntegrationData, getConfigBraggIntegrationResponse, getConfigDarkMaskData, getConfigDarkMaskResponse, getConfigDetectorData, getConfigDetectorResponse, getConfigFileWriterData, getConfigFileWriterResponse, getConfigImageFormatData, getConfigImageFormatResponse, getConfigIndexingData, getConfigIndexingResponse, getConfigInstrumentData, getConfigInstrumentResponse, getConfigMaskData, getConfigMaskResponse, getConfigMaskTiffData, getConfigMaskTiffResponse, getConfigRoiData, getConfigRoiResponse, getConfigRugnuxData, getConfigRugnuxError, getConfigRugnuxResponse, getConfigSelectDetectorData, getConfigSelectDetectorResponse, getConfigSpotFindingData, getConfigSpotFindingResponse, getConfigUserMaskData, getConfigUserMaskResponse, getConfigUserMaskTiffData, getConfigUserMaskTiffResponse, getConfigZeromqMetadataData, getConfigZeromqMetadataResponse, getConfigZeromqPreviewData, getConfigZeromqPreviewResponse, getDetectorStatusData, getDetectorStatusError, getDetectorStatusResponse, getFpgaStatusData, getFpgaStatusResponse, getImageBufferImageCborData, getImageBufferImageCborError, getImageBufferImageCborResponse, getImageBufferImageJpegData, getImageBufferImageJpegError, getImageBufferImageJpegResponse, getImageBufferImageTiffData, getImageBufferImageTiffError, getImageBufferImageTiffResponse, getImageBufferStartCborData, getImageBufferStartCborError, getImageBufferStartCborResponse, getImageBufferStatusData, getImageBufferStatusError, getImageBufferStatusResponse, getImagePusherStatusData, getImagePusherStatusError, getImagePusherStatusResponse, getPreviewPedestalTiffData, getPreviewPedestalTiffResponse, getPreviewPlotBinData, getPreviewPlotBinError, getPreviewPlotBinResponse, getPreviewPlotData, getPreviewPlotError, getPreviewPlotResponse, getResultScanData, getResultScanError, getResultScanResponse, getRugnuxStatusData, getRugnuxStatusError, getRugnuxStatusResponse, getStatisticsCalibrationData, getStatisticsCalibrationResponse, getStatisticsData, getStatisticsDataCollectionData, getStatisticsDataCollectionError, getStatisticsDataCollectionResponse, getStatisticsResponse, getStatusData, getStatusResponse, getVersionData, getVersionResponse, getXfelEventCodeData, getXfelEventCodeResponse, getXfelPulseIdData, getXfelPulseIdResponse, postCancelData, postConfigImageFormatConversionData, postConfigImageFormatConversionError, postConfigImageFormatRawData, postConfigImageFormatRawError, postDeactivateData, postDeactivateError, postImageBufferClearData, postImageBufferClearError, postInitializeData, postInitializeError, postPedestalData, postPedestalError, postStartData, postStartError, postTriggerData, postWaitTillDoneData, postWaitTillDoneError, postWaitUntilRunningData, postWaitUntilRunningError, putConfigAzimIntData, putConfigAzimIntError, putConfigBraggIntegrationData, putConfigBraggIntegrationError, putConfigDarkMaskData, putConfigDarkMaskError, putConfigDetectorData, putConfigDetectorError, putConfigFileWriterData, putConfigFileWriterError, putConfigImageFormatData, putConfigImageFormatError, putConfigIndexingData, putConfigIndexingError, putConfigInstrumentData, putConfigInstrumentError, putConfigInternalGeneratorImageData, putConfigInternalGeneratorImageError, putConfigInternalGeneratorImageTiffData, putConfigInternalGeneratorImageTiffError, putConfigRoiData, putConfigRoiError, putConfigRugnuxData, putConfigRugnuxError, putConfigRugnuxModelData, putConfigRugnuxModelError, putConfigSelectDetectorData, putConfigSelectDetectorError, putConfigSpotFindingData, putConfigSpotFindingError, putConfigUserMaskData, putConfigUserMaskError, putConfigUserMaskTiffData, putConfigUserMaskTiffError, putConfigZeromqMetadataData, putConfigZeromqMetadataError, putConfigZeromqPreviewData, putConfigZeromqPreviewError } from '../types.gen';
/**
* Initialize detector and data acquisition
@@ -156,6 +156,11 @@ export const postTriggerMutation = (options?: Partial<Options<postTriggerData>>)
* Any frame that is currently being processed by CPU will be finished and sent to writer.
* Given the command is making sure to gracefully stop data acquisition and detector, it might take some time to switch back after command finished to `Idle` state.
*
* With rugnux live mode (`/config/rugnux`) the command also cuts the rugnux analysis, during the collection
* or after its last image alike: the collection ends with what the analysis has established
* (see `cut` in `/rugnux/status`). After the last image the collection itself is complete and is not
* marked as cancelled.
*
* If data collection is not running, the command has no effect.
*
*/
@@ -675,6 +680,109 @@ export const putConfigSelectDetectorMutation = (options?: Partial<Options<putCon
return mutationOptions;
};
export const getConfigRugnuxQueryKey = (options?: Options<getConfigRugnuxData>) => createQueryKey('getConfigRugnux', options);
/**
* Get rugnux live mode settings and model status
*
* Can be done anytime
*/
export const getConfigRugnuxOptions = (options?: Options<getConfigRugnuxData>) => queryOptions<getConfigRugnuxResponse, getConfigRugnuxError, getConfigRugnuxResponse, ReturnType<typeof getConfigRugnuxQueryKey>>({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getConfigRugnux({
...options,
...queryKey[0],
signal,
throwOnError: true
});
return data;
},
queryKey: getConfigRugnuxQueryKey(options)
});
/**
* Change rugnux live mode settings
*
* This can only be done when detector is `Idle`, `Error` or `Inactive` states.
*
*/
export const putConfigRugnuxMutation = (options?: Partial<Options<putConfigRugnuxData>>): UseMutationOptions<unknown, putConfigRugnuxError, Options<putConfigRugnuxData>> => {
const mutationOptions: UseMutationOptions<unknown, putConfigRugnuxError, Options<putConfigRugnuxData>> = {
mutationFn: async (fnOptions) => {
const { data } = await putConfigRugnux({
...options,
...fnOptions,
throwOnError: true
});
return data;
}
};
return mutationOptions;
};
/**
* Remove the model used by rugnux live mode
*
* This can only be done when detector is `Idle`, `Error` or `Inactive` states.
*
*/
export const deleteConfigRugnuxModelMutation = (options?: Partial<Options<deleteConfigRugnuxModelData>>): UseMutationOptions<unknown, deleteConfigRugnuxModelError, Options<deleteConfigRugnuxModelData>> => {
const mutationOptions: UseMutationOptions<unknown, deleteConfigRugnuxModelError, Options<deleteConfigRugnuxModelData>> = {
mutationFn: async (fnOptions) => {
const { data } = await deleteConfigRugnuxModel({
...options,
...fnOptions,
throwOnError: true
});
return data;
}
};
return mutationOptions;
};
/**
* Upload the model used by rugnux live mode
*
* This can only be done when detector is `Idle`, `Error` or `Inactive` states.
* The model (PDB or mmCIF, optionally gzipped; at most 64 MiB) is used for molecular replacement
* and validation of every following rugnux live collection, until it is removed with
* `DELETE /config/rugnux/model` or replaced by another upload.
*
*/
export const putConfigRugnuxModelMutation = (options?: Partial<Options<putConfigRugnuxModelData>>): UseMutationOptions<unknown, putConfigRugnuxModelError, Options<putConfigRugnuxModelData>> => {
const mutationOptions: UseMutationOptions<unknown, putConfigRugnuxModelError, Options<putConfigRugnuxModelData>> = {
mutationFn: async (fnOptions) => {
const { data } = await putConfigRugnuxModel({
...options,
...fnOptions,
throwOnError: true
});
return data;
}
};
return mutationOptions;
};
export const getRugnuxStatusQueryKey = (options?: Options<getRugnuxStatusData>) => createQueryKey('getRugnuxStatus', options);
/**
* Get live rugnux processing status of the last collection
*
* Can be done anytime
*/
export const getRugnuxStatusOptions = (options?: Options<getRugnuxStatusData>) => queryOptions<getRugnuxStatusResponse, getRugnuxStatusError, getRugnuxStatusResponse, ReturnType<typeof getRugnuxStatusQueryKey>>({
queryFn: async ({ queryKey, signal }) => {
const { data } = await getRugnuxStatus({
...options,
...queryKey[0],
signal,
throwOnError: true
});
return data;
},
queryKey: getRugnuxStatusQueryKey(options)
});
export const getConfigZeromqPreviewQueryKey = (options?: Options<getConfigZeromqPreviewData>) => createQueryKey('getConfigZeromqPreview', options);
/**
+2 -2
View File
@@ -1,4 +1,4 @@
// This file is auto-generated by @hey-api/openapi-ts
export { getConfigAzimInt, getConfigBraggIntegration, getConfigDarkMask, getConfigDetector, getConfigFileWriter, getConfigImageFormat, getConfigIndexing, getConfigInstrument, getConfigMask, getConfigMaskTiff, getConfigRoi, getConfigSelectDetector, getConfigSpotFinding, getConfigUserMask, getConfigUserMaskTiff, getConfigZeromqMetadata, getConfigZeromqPreview, getDetectorStatus, getFpgaStatus, getImageBufferImageCbor, getImageBufferImageJpeg, getImageBufferImageTiff, getImageBufferStartCbor, getImageBufferStatus, getImagePusherStatus, getPreviewPedestalTiff, getPreviewPlot, getPreviewPlotBin, getResultScan, getStatistics, getStatisticsCalibration, getStatisticsDataCollection, getStatus, getVersion, getXfelEventCode, getXfelPulseId, type Options, postCancel, postConfigImageFormatConversion, postConfigImageFormatRaw, postDeactivate, postImageBufferClear, postInitialize, postPedestal, postStart, postTrigger, postWaitTillDone, postWaitUntilRunning, putConfigAzimInt, putConfigBraggIntegration, putConfigDarkMask, putConfigDetector, putConfigFileWriter, putConfigImageFormat, putConfigIndexing, putConfigInstrument, putConfigInternalGeneratorImage, putConfigInternalGeneratorImageTiff, putConfigRoi, putConfigSelectDetector, putConfigSpotFinding, putConfigUserMask, putConfigUserMaskTiff, putConfigZeromqMetadata, putConfigZeromqPreview } from './sdk.gen';
export { type azim_int_settings, azint_unit, type background, type binning, type bragg_integration_settings, type broker_status, type calibration_statistics, type ClientOptions, color_scale, type crystal_lattice, type dark_mask_settings, type dataset_settings, type dataset_settingsWritable, type detector, type detector_list, type detector_list_element, type detector_module, detector_module_direction, detector_power_state, type detector_selection, type detector_settings, detector_state, type detector_status, detector_timing, detector_type, type error_message, type experimental_coord, file_writer_format, type file_writer_settings, type fill_value, type fpga_status, geom_refinement_algorithm, type getConfigAzimIntData, type getConfigAzimIntResponse, type getConfigAzimIntResponses, type getConfigBraggIntegrationData, type getConfigBraggIntegrationResponse, type getConfigBraggIntegrationResponses, type getConfigDarkMaskData, type getConfigDarkMaskResponse, type getConfigDarkMaskResponses, type getConfigDetectorData, type getConfigDetectorResponse, type getConfigDetectorResponses, type getConfigFileWriterData, type getConfigFileWriterResponse, type getConfigFileWriterResponses, type getConfigImageFormatData, type getConfigImageFormatResponse, type getConfigImageFormatResponses, type getConfigIndexingData, type getConfigIndexingResponse, type getConfigIndexingResponses, type getConfigInstrumentData, type getConfigInstrumentResponse, type getConfigInstrumentResponses, type getConfigMaskData, type getConfigMaskResponse, type getConfigMaskResponses, type getConfigMaskTiffData, type getConfigMaskTiffResponse, type getConfigMaskTiffResponses, type getConfigRoiData, type getConfigRoiResponse, type getConfigRoiResponses, type getConfigSelectDetectorData, type getConfigSelectDetectorResponse, type getConfigSelectDetectorResponses, type getConfigSpotFindingData, type getConfigSpotFindingResponse, type getConfigSpotFindingResponses, type getConfigUserMaskData, type getConfigUserMaskResponse, type getConfigUserMaskResponses, type getConfigUserMaskTiffData, type getConfigUserMaskTiffResponse, type getConfigUserMaskTiffResponses, type getConfigZeromqMetadataData, type getConfigZeromqMetadataResponse, type getConfigZeromqMetadataResponses, type getConfigZeromqPreviewData, type getConfigZeromqPreviewResponse, type getConfigZeromqPreviewResponses, type getDetectorStatusData, type getDetectorStatusError, type getDetectorStatusErrors, type getDetectorStatusResponse, type getDetectorStatusResponses, type getFpgaStatusData, type getFpgaStatusResponse, type getFpgaStatusResponses, type getImageBufferImageCborData, type getImageBufferImageCborError, type getImageBufferImageCborErrors, type getImageBufferImageCborResponse, type getImageBufferImageCborResponses, type getImageBufferImageJpegData, type getImageBufferImageJpegError, type getImageBufferImageJpegErrors, type getImageBufferImageJpegResponse, type getImageBufferImageJpegResponses, type getImageBufferImageTiffData, type getImageBufferImageTiffError, type getImageBufferImageTiffErrors, type getImageBufferImageTiffResponse, type getImageBufferImageTiffResponses, type getImageBufferStartCborData, type getImageBufferStartCborError, type getImageBufferStartCborErrors, type getImageBufferStartCborResponse, type getImageBufferStartCborResponses, type getImageBufferStatusData, type getImageBufferStatusError, type getImageBufferStatusErrors, type getImageBufferStatusResponse, type getImageBufferStatusResponses, type getImagePusherStatusData, type getImagePusherStatusError, type getImagePusherStatusErrors, type getImagePusherStatusResponse, type getImagePusherStatusResponses, type getPreviewPedestalTiffData, type getPreviewPedestalTiffErrors, type getPreviewPedestalTiffResponse, type getPreviewPedestalTiffResponses, type getPreviewPlotBinData, type getPreviewPlotBinError, type getPreviewPlotBinErrors, type getPreviewPlotBinResponse, type getPreviewPlotBinResponses, type getPreviewPlotData, type getPreviewPlotError, type getPreviewPlotErrors, type getPreviewPlotResponse, type getPreviewPlotResponses, type getResultScanData, type getResultScanError, type getResultScanErrors, type getResultScanResponse, type getResultScanResponses, type getStatisticsCalibrationData, type getStatisticsCalibrationResponse, type getStatisticsCalibrationResponses, type getStatisticsData, type getStatisticsDataCollectionData, type getStatisticsDataCollectionError, type getStatisticsDataCollectionErrors, type getStatisticsDataCollectionResponse, type getStatisticsDataCollectionResponses, type getStatisticsResponse, type getStatisticsResponses, type getStatusData, type getStatusResponse, type getStatusResponses, type getVersionData, type getVersionResponse, type getVersionResponses, type getXfelEventCodeData, type getXfelEventCodeErrors, type getXfelEventCodeResponse, type getXfelEventCodeResponses, type getXfelPulseIdData, type getXfelPulseIdErrors, type getXfelPulseIdResponse, type getXfelPulseIdResponses, type Line truncated
export { deleteConfigRugnuxModel, getConfigAzimInt, getConfigBraggIntegration, getConfigDarkMask, getConfigDetector, getConfigFileWriter, getConfigImageFormat, getConfigIndexing, getConfigInstrument, getConfigMask, getConfigMaskTiff, getConfigRoi, getConfigRugnux, getConfigSelectDetector, getConfigSpotFinding, getConfigUserMask, getConfigUserMaskTiff, getConfigZeromqMetadata, getConfigZeromqPreview, getDetectorStatus, getFpgaStatus, getImageBufferImageCbor, getImageBufferImageJpeg, getImageBufferImageTiff, getImageBufferStartCbor, getImageBufferStatus, getImagePusherStatus, getLiveEvents, getPreviewPedestalTiff, getPreviewPlot, getPreviewPlotBin, getResultScan, getRugnuxStatus, getStatistics, getStatisticsCalibration, getStatisticsDataCollection, getStatus, getVersion, getXfelEventCode, getXfelPulseId, type Options, postCancel, postConfigImageFormatConversion, postConfigImageFormatRaw, postDeactivate, postImageBufferClear, postInitialize, postPedestal, postStart, postTrigger, postWaitTillDone, postWaitUntilRunning, putConfigAzimInt, putConfigBraggIntegration, putConfigDarkMask, putConfigDetector, putConfigFileWriter, putConfigImageFormat, putConfigIndexing, putConfigInstrument, putConfigInternalGeneratorImage, putConfigInternalGeneratorImageTiff, putConfigRoi, putConfigRugnux, putConfigRugnuxModel, putConfigSelectDetector, putConfigSpotFinding, putConfigUserMask, putConfigUserMaskTiff, putConfigZeromqMetadata, putConfigZeromqPreview } from './sdk.gen';
export { type azim_int_settings, azint_unit, type background, type binning, type bragg_integration_settings, type broker_status, type calibration_statistics, type ClientOptions, color_scale, type crystal_lattice, type dark_mask_settings, type dataset_settings, type dataset_settingsWritable, type deleteConfigRugnuxModelData, type deleteConfigRugnuxModelError, type deleteConfigRugnuxModelErrors, type deleteConfigRugnuxModelResponses, type detector, type detector_list, type detector_list_element, type detector_module, detector_module_direction, detector_power_state, type detector_selection, type detector_settings, detector_state, type detector_status, detector_timing, detector_type, type error_message, type experimental_coord, file_writer_format, type file_writer_settings, type fill_value, type fpga_status, geom_refinement_algorithm, type getConfigAzimIntData, type getConfigAzimIntResponse, type getConfigAzimIntResponses, type getConfigBraggIntegrationData, type getConfigBraggIntegrationResponse, type getConfigBraggIntegrationResponses, type getConfigDarkMaskData, type getConfigDarkMaskResponse, type getConfigDarkMaskResponses, type getConfigDetectorData, type getConfigDetectorResponse, type getConfigDetectorResponses, type getConfigFileWriterData, type getConfigFileWriterResponse, type getConfigFileWriterResponses, type getConfigImageFormatData, type getConfigImageFormatResponse, type getConfigImageFormatResponses, type getConfigIndexingData, type getConfigIndexingResponse, type getConfigIndexingResponses, type getConfigInstrumentData, type getConfigInstrumentResponse, type getConfigInstrumentResponses, type getConfigMaskData, type getConfigMaskResponse, type getConfigMaskResponses, type getConfigMaskTiffData, type getConfigMaskTiffResponse, type getConfigMaskTiffResponses, type getConfigRoiData, type getConfigRoiResponse, type getConfigRoiResponses, type getConfigRugnuxData, type getConfigRugnuxError, type getConfigRugnuxErrors, type getConfigRugnuxResponse, type getConfigRugnuxResponses, type getConfigSelectDetectorData, type getConfigSelectDetectorResponse, type getConfigSelectDetectorResponses, type getConfigSpotFindingData, type getConfigSpotFindingResponse, type getConfigSpotFindingResponses, type getConfigUserMaskData, type getConfigUserMaskResponse, type getConfigUserMaskResponses, type getConfigUserMaskTiffData, type getConfigUserMaskTiffResponse, type getConfigUserMaskTiffResponses, type getConfigZeromqMetadataData, type getConfigZeromqMetadataResponse, type getConfigZeromqMetadataResponses, type getConfigZeromqPreviewData, type getConfigZeromqPreviewResponse, type getConfigZeromqPreviewResponses, type getDetectorStatusData, type getDetectorStatusError, type getDetectorStatusErrors, type getDetectorStatusResponse, type getDetectorStatusResponses, type getFpgaStatusData, type getFpgaStatusResponse, type getFpgaStatusResponses, type getImageBufferImageCborData, type getImageBufferImageCborError, type getImageBufferImageCborErrors, type getImageBufferImageCborResponse, type getImageBufferImageCborResponses, type getImageBufferImageJpegData, type getImageBufferImageJpegError, type getImageBufferImageJpegErrors, type getImageBufferImageJpegResponse, type getImageBufferImageJpegResponses, type getImageBufferImageTiffData, type getImageBufferImageTiffError, type getImageBufferImageTiffErrors, type getImageBufferImageTiffResponse, type getImageBufferImageTiffResponses, type getImageBufferStartCborData, type getImageBufferStartCborError, type getImageBufferStartCborErrors, type getImageBufferStartCborResponse, type getImageBufferStartCborResponses, type getImageBufferStatusData, type getImageBufferStatusError, type getImageBufferStatusErrors, type getImageBufferStatusResponse, type getImageBufferStatusResponses, type getImagePusherStatusData, type getImagePusherStatusError, type getImagePusherStatusErrors, type getImagePusherStatusResponse, type getImagePusherStatusResponses, type getLiveEventsData, type getLiveEventsError, type getLiveEventsErrors, type getLiveEventsResponse, type getLiveEventsResponses, type getPreviewPedestalTiffData, type getPreviewPedestalTiffErrors, type getPreviewPedestalTiffResponse, type getPreviewPedestalTiffResponses, type getPreviewPlotBinData, type getPreviewPlotBinError, type getPreviewPlotBinErrors, type getPreviewPlotBinResponse, type getPreviewPlotBinResponses, type getPreviewPlotData, type getPreviewPlotError, type getPreviewPlotErrors, type getPreviewPlotResponse, type getPreviewPlotResponses, type getResultScanData, type getResultScanError, type getResultScanErrors, type getResultScanResponse, type getResultScanResponses, type getRugnuxStatusData, type getRugnuxStatusError, type getRugnuxStatusErrors, type getRugnuxStatusResponse, type getRugnuxStatusResponses, type getStatisticsCalibrationData, type getStatisticsCalibrationResponse, type getStatisticsCalibrationResponses, type getStatisticsData, type getStatisticsDataCollectionData, type getStatisticsDataCollectionErrorLine truncated
+102 -2
View File
@@ -1,8 +1,8 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Client, ClientMeta, Options as Options2, RequestResult, TDataShape } from './client';
import type { Client, ClientMeta, Options as Options2, RequestResult, ServerSentEventsResult, TDataShape } from './client';
import { client } from './client.gen';
import type { getConfigAzimIntData, getConfigAzimIntResponses, getConfigBraggIntegrationData, getConfigBraggIntegrationResponses, getConfigDarkMaskData, getConfigDarkMaskResponses, getConfigDetectorData, getConfigDetectorResponses, getConfigFileWriterData, getConfigFileWriterResponses, getConfigImageFormatData, getConfigImageFormatResponses, getConfigIndexingData, getConfigIndexingResponses, getConfigInstrumentData, getConfigInstrumentResponses, getConfigMaskData, getConfigMaskResponses, getConfigMaskTiffData, getConfigMaskTiffResponses, getConfigRoiData, getConfigRoiResponses, getConfigSelectDetectorData, getConfigSelectDetectorResponses, getConfigSpotFindingData, getConfigSpotFindingResponses, getConfigUserMaskData, getConfigUserMaskResponses, getConfigUserMaskTiffData, getConfigUserMaskTiffResponses, getConfigZeromqMetadataData, getConfigZeromqMetadataResponses, getConfigZeromqPreviewData, getConfigZeromqPreviewResponses, getDetectorStatusData, getDetectorStatusErrors, getDetectorStatusResponses, getFpgaStatusData, getFpgaStatusResponses, getImageBufferImageCborData, getImageBufferImageCborErrors, getImageBufferImageCborResponses, getImageBufferImageJpegData, getImageBufferImageJpegErrors, getImageBufferImageJpegResponses, getImageBufferImageTiffData, getImageBufferImageTiffErrors, getImageBufferImageTiffResponses, getImageBufferStartCborData, getImageBufferStartCborErrors, getImageBufferStartCborResponses, getImageBufferStatusData, getImageBufferStatusErrors, getImageBufferStatusResponses, getImagePusherStatusData, getImagePusherStatusErrors, getImagePusherStatusResponses, getPreviewPedestalTiffData, getPreviewPedestalTiffErrors, getPreviewPedestalTiffResponses, getPreviewPlotBinData, getPreviewPlotBinErrors, getPreviewPlotBinResponses, getPreviewPlotData, getPreviewPlotErrors, getPreviewPlotResponses, getResultScanData, getResultScanErrors, getResultScanResponses, getStatisticsCalibrationData, getStatisticsCalibrationResponses, getStatisticsData, getStatisticsDataCollectionData, getStatisticsDataCollectionErrors, getStatisticsDataCollectionResponses, getStatisticsResponses, getStatusData, getStatusResponses, getVersionData, getVersionResponses, getXfelEventCodeData, getXfelEventCodeErrors, getXfelEventCodeResponses, getXfelPulseIdData, getXfelPulseIdErrors, getXfelPulseIdResponses, postCancelData, postCancelResponses, postConfigImageFormatConversionData, postConfigImageFormatConversionErrors, postConfigImageFormatConversionResponses, postConfigImageFormatRawData, postConfigImageFormatRawErrors, postConfigImageFormatRawResponses, postDeactivateData, postDeactivateErrors, postDeactivateResponses, postImageBufferClearData, postImageBufferClearErrors, postImageBufferClearResponses, postInitializeData, postInitializeErrors, postInitializeResponses, postPedestalData, postPedestalErrors, postPedestalResponses, postStartData, postStartErrors, postStartResponses, postTriggerData, postTriggerResponses, postWaitTillDoneData, postWaitTillDoneErrors, postWaitTillDoneResponses, postWaitUntilRunningData, postWaitUntilRunningErrors, postWaitUntilRunningResponses, putConfigAzimIntData, putConfigAzimIntErrors, putConfigAzimIntResponses, putConfigBraggIntegrationData, putConfigBraggIntegrationErrors, putConfigBraggIntegrationResponses, putConfigDarkMaskData, putConfigDarkMaskErrors, putConfigDarkMaskResponses, putConfigDetectorData, putConfigDetectorErrors, putConfigDetectorResponses, putConfigFileWriterData, putConfigFileWriterErrors, putConfigFileWriterResponses, putConfigImageFormatData, putConfigImageFormatErrors, putConfigImageFormatResponses, putConfigIndexingData, putConfigIndexingErrors, putConfigIndexingResponses, putConfigInstrumentData, putConfigInstrumentErrors, putConfigInstrumentResponses, putConfigInternalGeneratorImageData, putConfigInternalGeneratorImageErrors, putConfigInternalGeneratorImageResponses, putConfigInternalGeneratorImageTiffData, putConfigInternalGeneratorImageTiffErrors, putConfigInternalGeneratorImageTiffResponses, putConfigRoiData, putConfigRoiErrors, putConfigRoiResponses, putConfigSelectDetectorData, putConfigSelectDetectorErrors, putConfigSelectDetectorResponses, putConfigSpotFindingData, putConfigSpotFindingErrors, putConfigSpotFindingResponses, putConfigUserMaskData, putConfigUserMaskErrors, putConfigUserMaskResponses, putConfigUserMaskTiffData, putConfigUserMaskTiffErrors, putConfigUserMaskTiffResponses, putConfigZeromqMetadataData, putConfigZeromqMetadataErrors, putConfigZeromqMetadataResponses, putConfigZeromqPreviewData, putConfigZeromqPreviewErrors, putConfigZeromqPreviewResponses } from './types.gen';
import type { deleteConfigRugnuxModelData, deleteConfigRugnuxModelErrors, deleteConfigRugnuxModelResponses, getConfigAzimIntData, getConfigAzimIntResponses, getConfigBraggIntegrationData, getConfigBraggIntegrationResponses, getConfigDarkMaskData, getConfigDarkMaskResponses, getConfigDetectorData, getConfigDetectorResponses, getConfigFileWriterData, getConfigFileWriterResponses, getConfigImageFormatData, getConfigImageFormatResponses, getConfigIndexingData, getConfigIndexingResponses, getConfigInstrumentData, getConfigInstrumentResponses, getConfigMaskData, getConfigMaskResponses, getConfigMaskTiffData, getConfigMaskTiffResponses, getConfigRoiData, getConfigRoiResponses, getConfigRugnuxData, getConfigRugnuxErrors, getConfigRugnuxResponses, getConfigSelectDetectorData, getConfigSelectDetectorResponses, getConfigSpotFindingData, getConfigSpotFindingResponses, getConfigUserMaskData, getConfigUserMaskResponses, getConfigUserMaskTiffData, getConfigUserMaskTiffResponses, getConfigZeromqMetadataData, getConfigZeromqMetadataResponses, getConfigZeromqPreviewData, getConfigZeromqPreviewResponses, getDetectorStatusData, getDetectorStatusErrors, getDetectorStatusResponses, getFpgaStatusData, getFpgaStatusResponses, getImageBufferImageCborData, getImageBufferImageCborErrors, getImageBufferImageCborResponses, getImageBufferImageJpegData, getImageBufferImageJpegErrors, getImageBufferImageJpegResponses, getImageBufferImageTiffData, getImageBufferImageTiffErrors, getImageBufferImageTiffResponses, getImageBufferStartCborData, getImageBufferStartCborErrors, getImageBufferStartCborResponses, getImageBufferStatusData, getImageBufferStatusErrors, getImageBufferStatusResponses, getImagePusherStatusData, getImagePusherStatusErrors, getImagePusherStatusResponses, getLiveEventsData, getLiveEventsErrors, getLiveEventsResponse, getLiveEventsResponses, getPreviewPedestalTiffData, getPreviewPedestalTiffErrors, getPreviewPedestalTiffResponses, getPreviewPlotBinData, getPreviewPlotBinErrors, getPreviewPlotBinResponses, getPreviewPlotData, getPreviewPlotErrors, getPreviewPlotResponses, getResultScanData, getResultScanErrors, getResultScanResponses, getRugnuxStatusData, getRugnuxStatusErrors, getRugnuxStatusResponses, getStatisticsCalibrationData, getStatisticsCalibrationResponses, getStatisticsData, getStatisticsDataCollectionData, getStatisticsDataCollectionErrors, getStatisticsDataCollectionResponses, getStatisticsResponses, getStatusData, getStatusResponses, getVersionData, getVersionResponses, getXfelEventCodeData, getXfelEventCodeErrors, getXfelEventCodeResponses, getXfelPulseIdData, getXfelPulseIdErrors, getXfelPulseIdResponses, postCancelData, postCancelResponses, postConfigImageFormatConversionData, postConfigImageFormatConversionErrors, postConfigImageFormatConversionResponses, postConfigImageFormatRawData, postConfigImageFormatRawErrors, postConfigImageFormatRawResponses, postDeactivateData, postDeactivateErrors, postDeactivateResponses, postImageBufferClearData, postImageBufferClearErrors, postImageBufferClearResponses, postInitializeData, postInitializeErrors, postInitializeResponses, postPedestalData, postPedestalErrors, postPedestalResponses, postStartData, postStartErrors, postStartResponses, postTriggerData, postTriggerResponses, postWaitTillDoneData, postWaitTillDoneErrors, postWaitTillDoneResponses, postWaitUntilRunningData, postWaitUntilRunningErrors, postWaitUntilRunningResponses, putConfigAzimIntData, putConfigAzimIntErrors, putConfigAzimIntResponses, putConfigBraggIntegrationData, putConfigBraggIntegrationErrors, putConfigBraggIntegrationResponses, putConfigDarkMaskData, putConfigDarkMaskErrors, putConfigDarkMaskResponses, putConfigDetectorData, putConfigDetectorErrors, putConfigDetectorResponses, putConfigFileWriterData, putConfigFileWriterErrors, putConfigFileWriterResponses, putConfigImageFormatData, putConfigImageFormatErrors, putConfigImageFormatResponses, putConfigIndexingData, putConfigIndexingErrors, putConfigIndexingResponses, putConfigInstrumentData, putConfigInstrumentErrors, putConfigInstrumentResponses, putConfigInternalGeneratorImageData, putConfigInternalGeneratorImageErrors, putConfigInternalGeneratorImageResponses, putConfigInternalGeneratorImageTiffData, putConfigInternalGeneratorImageTiffErrors, putConfigInternalGeneratorImageTiffResponses, putConfigRoiData, putConfigRoiErrors, putConfigRoiResponses, putConfigRugnuxData, putConfigRugnuxErrors, putConfigRugnuxModelData, putConfigRugnuxModelErrors, putConfigRugnuxModelResponses, putConfigRugnuxResponses, putConfigSelectDetectorData, putConfigSelectDetectorErrors, putConfigSelectDetectorResponses, putConfigSpotFindingData, putConfigSpotFindingErrors, putConfigSpotFindingResponses, putConfigUserMaskData, putConfigUserMaskErrors, putConfigUserMaskResponses, putConfigUserMaskTiffData, putConfigUserMaskTiffErrors, putConfigUserMaskTiffResponses, putConfigZeromqMetadataData, putConfigZeromqMetadataErrors, putConfigZeromqMetadataResponses, putConfigZeromqPreLine truncated
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean, TResponse = unknown> = Options2<TData, ThrowOnError, TResponse> & {
/**
@@ -103,6 +103,11 @@ export const postTrigger = <ThrowOnError extends boolean = false>(options?: Opti
* Any frame that is currently being processed by CPU will be finished and sent to writer.
* Given the command is making sure to gracefully stop data acquisition and detector, it might take some time to switch back after command finished to `Idle` state.
*
* With rugnux live mode (`/config/rugnux`) the command also cuts the rugnux analysis, during the collection
* or after its last image alike: the collection ends with what the analysis has established
* (see `cut` in `/rugnux/status`). After the last image the collection itself is complete and is not
* marked as cancelled.
*
* If data collection is not running, the command has no effect.
*
*/
@@ -371,6 +376,76 @@ export const putConfigSelectDetector = <ThrowOnError extends boolean = false>(op
}
});
/**
* Get rugnux live mode settings and model status
*
* Can be done anytime
*/
export const getConfigRugnux = <ThrowOnError extends boolean = false>(options?: Options<getConfigRugnuxData, ThrowOnError>): RequestResult<getConfigRugnuxResponses, getConfigRugnuxErrors, ThrowOnError> => (options?.client ?? client).get<getConfigRugnuxResponses, getConfigRugnuxErrors, ThrowOnError>({
security: [{ scheme: 'bearer', type: 'http' }],
url: '/config/rugnux',
...options
});
/**
* Change rugnux live mode settings
*
* This can only be done when detector is `Idle`, `Error` or `Inactive` states.
*
*/
export const putConfigRugnux = <ThrowOnError extends boolean = false>(options?: Options<putConfigRugnuxData, ThrowOnError>): RequestResult<putConfigRugnuxResponses, putConfigRugnuxErrors, ThrowOnError> => (options?.client ?? client).put<putConfigRugnuxResponses, putConfigRugnuxErrors, ThrowOnError>({
security: [{ scheme: 'bearer', type: 'http' }],
url: '/config/rugnux',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
/**
* Remove the model used by rugnux live mode
*
* This can only be done when detector is `Idle`, `Error` or `Inactive` states.
*
*/
export const deleteConfigRugnuxModel = <ThrowOnError extends boolean = false>(options?: Options<deleteConfigRugnuxModelData, ThrowOnError>): RequestResult<deleteConfigRugnuxModelResponses, deleteConfigRugnuxModelErrors, ThrowOnError> => (options?.client ?? client).delete<deleteConfigRugnuxModelResponses, deleteConfigRugnuxModelErrors, ThrowOnError>({
security: [{ scheme: 'bearer', type: 'http' }],
url: '/config/rugnux/model',
...options
});
/**
* Upload the model used by rugnux live mode
*
* This can only be done when detector is `Idle`, `Error` or `Inactive` states.
* The model (PDB or mmCIF, optionally gzipped; at most 64 MiB) is used for molecular replacement
* and validation of every following rugnux live collection, until it is removed with
* `DELETE /config/rugnux/model` or replaced by another upload.
*
*/
export const putConfigRugnuxModel = <ThrowOnError extends boolean = false>(options?: Options<putConfigRugnuxModelData, ThrowOnError>): RequestResult<putConfigRugnuxModelResponses, putConfigRugnuxModelErrors, ThrowOnError> => (options?.client ?? client).put<putConfigRugnuxModelResponses, putConfigRugnuxModelErrors, ThrowOnError>({
bodySerializer: null,
security: [{ scheme: 'bearer', type: 'http' }],
url: '/config/rugnux/model',
...options,
headers: {
'Content-Type': 'application/octet-stream',
...options?.headers
}
});
/**
* Get live rugnux processing status of the last collection
*
* Can be done anytime
*/
export const getRugnuxStatus = <ThrowOnError extends boolean = false>(options?: Options<getRugnuxStatusData, ThrowOnError>): RequestResult<getRugnuxStatusResponses, getRugnuxStatusErrors, ThrowOnError> => (options?.client ?? client).get<getRugnuxStatusResponses, getRugnuxStatusErrors, ThrowOnError>({
security: [{ scheme: 'bearer', type: 'http' }],
url: '/rugnux/status',
...options
});
/**
* Get ZeroMQ preview settings
*/
@@ -648,6 +723,31 @@ export const getImageBufferStartCbor = <ThrowOnError extends boolean = false>(op
...options
});
/**
* Event stream of the collection, for a reader following it
*
* Server-sent events (text/event-stream) for the current collection, from the moment /start has
* accepted it: first its whole history, then every change, until it ends or a new collection
* begins. Only one reader at a time. Events, each with a JSON `data` line:
* - `start` {run_number, run_name, file_prefix, number_of_images, images_per_file, cbor} - first,
* once the start message is known: at once for a JUNGFRAU, when the detector sends its own for
* a DECTRIS. `cbor` is the start message the writer of the master file received, CBOR in base64;
* a reader builds the dataset from it;
* - `file` {file_number, file, first_image, image_count} - a data file closed and renamed into
* place; `file` is relative to the collection's root, as the writer named it;
* - `end` {images, error?} - every writer acknowledged the end: the data files and the master
* file are in place; `images` is the number of images collected. Also sent, with `error`, when
* the measurement ends any other way - before the start message, for one. The stream then closes;
* - `superseded` {run_number} - a new collection began. The stream then closes.
* A comment line (`: keepalive`) every 10 s when nothing happens.
*
*/
export const getLiveEvents = <ThrowOnError extends boolean = false>(options?: Options<getLiveEventsData, ThrowOnError, getLiveEventsResponse>): Promise<ServerSentEventsResult<getLiveEventsResponses>> => (options?.client ?? client).sse.get<getLiveEventsResponses, getLiveEventsErrors, ThrowOnError>({
security: [{ scheme: 'bearer', type: 'http' }],
url: '/live/events',
...options
});
/**
* Get image message in CBOR format
*
+295
View File
@@ -1873,6 +1873,125 @@ export type jfjoch_settings = {
zeromq_preview?: zeromq_preview_settings;
zeromq_metadata?: zeromq_metadata_settings;
dark_mask?: dark_mask_settings;
rugnux?: rugnux_settings;
};
/**
* Rugnux live mode: rotation collections from a DECTRIS detector are processed by rugnux
* inside the broker while they arrive, and the merged result is sent to the writer.
*
*/
export type rugnux_settings = {
/**
* Process DECTRIS rotation collections with rugnux live
*/
enable?: boolean;
/**
* CPU threads left to the receiver and the image pusher while rugnux runs
*/
reserved_cpus?: number;
/**
* Fixed cutoff: this long after the last image the analysis is cut, and the collection ends
* with what it has established (as `POST /cancel` after the last image does). 0 means no cutoff.
*
*/
deadline_s?: number;
};
export type rugnux_status = {
/**
* Rugnux live mode is enabled
*/
enable: boolean;
/**
* CPU threads left to the receiver and the image pusher while rugnux runs
*/
reserved_cpus: number;
/**
* Seconds after the last image at which the analysis is cut; 0 means no cutoff
*/
deadline_s?: number;
/**
* A model was uploaded with `PUT /config/rugnux/model`
*/
model_loaded: boolean;
/**
* Size of the uploaded model file
*/
model_size_bytes?: number;
};
export type rugnux_live_step = {
title: string;
/**
* Start of the step, since the collection started
*/
started_s: number;
/**
* Set once the next step starts or the run ends
*/
duration_s?: number;
/**
* What the step found (e.g. lattice, space group)
*/
notes: Array<string>;
};
export type rugnux_summary_row = {
label: string;
value: string;
};
/**
* Live rugnux processing of the last collection; kept until the next collection starts
*/
export type rugnux_live_status = {
state: 'Idle' | 'Running' | 'Done' | 'Failed' | 'Cancelled' | 'Skipped';
/**
* Answer of the early lattice probe on the first 60 degrees; empty until it answers
*/
probe: string;
/**
* When the probe answered, since the collection started
*/
probe_s?: number;
/**
* Why the run failed or was skipped
*/
message: string;
images_expected: number;
images_received: number;
elapsed_s: number;
/**
* Fraction of the current pass's images
*/
step_progress?: number;
steps: Array<rugnux_live_step>;
/**
* When the last image arrived (and the cutoff timer started), since the collection started
*/
last_image_s?: number;
/**
* Empty, or why the analysis was cut: `deadline` (`deadline_s` after the last image) or `cancel`
* (`POST /cancel`). The end message then carried what was established at the cut.
*
*/
cut?: string;
/**
* When the analysis was cut, since the collection started
*/
cut_s?: number;
/**
* From the last image received to all results written
*/
last_image_to_written_s?: number;
/**
* OK, WARNINGS, UNUSABLE or FAILED; empty until the run is over
*/
verdict: string;
verdict_text: string;
summary: Array<rugnux_summary_row>;
warnings: Array<string>;
};
export type zeromq_metadata_settings = {
@@ -3060,6 +3179,149 @@ export type putConfigSelectDetectorResponses = {
200: unknown;
};
export type getConfigRugnuxData = {
body?: never;
path?: never;
query?: never;
url: '/config/rugnux';
};
export type getConfigRugnuxErrors = {
/**
* The current dataset is protected and no valid bearer token was presented.
*/
401: string;
};
export type getConfigRugnuxError = getConfigRugnuxErrors[keyof getConfigRugnuxErrors];
export type getConfigRugnuxResponses = {
/**
* Everything OK
*/
200: rugnux_status;
};
export type getConfigRugnuxResponse = getConfigRugnuxResponses[keyof getConfigRugnuxResponses];
export type putConfigRugnuxData = {
body?: rugnux_settings;
path?: never;
query?: never;
url: '/config/rugnux';
};
export type putConfigRugnuxErrors = {
/**
* Exception error
*/
400: string;
/**
* The current dataset is protected and no valid bearer token was presented.
*/
401: string;
/**
* Error within Jungfraujoch code - see output message.
*/
500: error_message;
};
export type putConfigRugnuxError = putConfigRugnuxErrors[keyof putConfigRugnuxErrors];
export type putConfigRugnuxResponses = {
/**
* Everything OK
*/
200: unknown;
};
export type deleteConfigRugnuxModelData = {
body?: never;
path?: never;
query?: never;
url: '/config/rugnux/model';
};
export type deleteConfigRugnuxModelErrors = {
/**
* The current dataset is protected and no valid bearer token was presented.
*/
401: string;
/**
* Error within Jungfraujoch code - see output message.
*/
500: error_message;
};
export type deleteConfigRugnuxModelError = deleteConfigRugnuxModelErrors[keyof deleteConfigRugnuxModelErrors];
export type deleteConfigRugnuxModelResponses = {
/**
* All good
*/
200: unknown;
};
export type putConfigRugnuxModelData = {
body?: Blob | File;
path?: never;
query?: never;
url: '/config/rugnux/model';
};
export type putConfigRugnuxModelErrors = {
/**
* Empty body
*/
400: unknown;
/**
* The current dataset is protected and no valid bearer token was presented.
*/
401: string;
/**
* Model larger than 64 MiB
*/
413: unknown;
/**
* Error within Jungfraujoch code - see output message.
*/
500: error_message;
};
export type putConfigRugnuxModelError = putConfigRugnuxModelErrors[keyof putConfigRugnuxModelErrors];
export type putConfigRugnuxModelResponses = {
/**
* All good
*/
200: unknown;
};
export type getRugnuxStatusData = {
body?: never;
path?: never;
query?: never;
url: '/rugnux/status';
};
export type getRugnuxStatusErrors = {
/**
* The current dataset is protected and no valid bearer token was presented.
*/
401: string;
};
export type getRugnuxStatusError = getRugnuxStatusErrors[keyof getRugnuxStatusErrors];
export type getRugnuxStatusResponses = {
/**
* Everything OK
*/
200: rugnux_live_status;
};
export type getRugnuxStatusResponse = getRugnuxStatusResponses[keyof getRugnuxStatusResponses];
export type getConfigZeromqPreviewData = {
body?: never;
path?: never;
@@ -3738,6 +4000,39 @@ export type getImageBufferStartCborResponses = {
export type getImageBufferStartCborResponse = getImageBufferStartCborResponses[keyof getImageBufferStartCborResponses];
export type getLiveEventsData = {
body?: never;
path?: never;
query?: never;
url: '/live/events';
};
export type getLiveEventsErrors = {
/**
* The current dataset is protected and no valid bearer token was presented.
*/
401: string;
/**
* No collection has started
*/
404: unknown;
/**
* Another reader is following the collection, or the image pusher does not report closed data files
*/
409: unknown;
};
export type getLiveEventsError = getLiveEventsErrors[keyof getLiveEventsErrors];
export type getLiveEventsResponses = {
/**
* Event stream
*/
200: string;
};
export type getLiveEventsResponse = getLiveEventsResponses[keyof getLiveEventsResponses];
export type getImageBufferImageCborData = {
body?: never;
path?: never;
+82 -1
View File
@@ -792,6 +792,67 @@ export const zBraggIntegrationSettings = z.object({
max_hkl: z.int().gte(1).lte(511).optional().default(100)
});
/**
* Rugnux live mode: rotation collections from a DECTRIS detector are processed by rugnux
* inside the broker while they arrive, and the merged result is sent to the writer.
*
*/
export const zRugnuxSettings = z.object({
enable: z.boolean().optional().default(false),
reserved_cpus: z.coerce.bigint().gte(BigInt(0)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional().default(BigInt(4)),
deadline_s: z.number().gte(0).optional().default(0)
});
export const zRugnuxStatus = z.object({
enable: z.boolean(),
reserved_cpus: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
deadline_s: z.number().optional(),
model_loaded: z.boolean(),
model_size_bytes: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional()
});
export const zRugnuxLiveStep = z.object({
title: z.string(),
started_s: z.number(),
duration_s: z.number().optional(),
notes: z.array(z.string())
});
export const zRugnuxSummaryRow = z.object({
label: z.string(),
value: z.string()
});
/**
* Live rugnux processing of the last collection; kept until the next collection starts
*/
export const zRugnuxLiveStatus = z.object({
state: z.enum([
'Idle',
'Running',
'Done',
'Failed',
'Cancelled',
'Skipped'
]),
probe: z.string(),
probe_s: z.number().optional(),
message: z.string(),
images_expected: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
images_received: z.coerce.bigint().min(BigInt('-9223372036854775808'), { error: 'Invalid value: Expected int64 to be >= -9223372036854775808' }).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }),
elapsed_s: z.number(),
step_progress: z.number().gte(0).lte(1).optional(),
steps: z.array(zRugnuxLiveStep),
last_image_s: z.number().optional(),
cut: z.string().optional(),
cut_s: z.number().optional(),
last_image_to_written_s: z.number().optional(),
verdict: z.string(),
verdict_text: z.string(),
summary: z.array(zRugnuxSummaryRow),
warnings: z.array(z.string())
});
export const zZeromqMetadataSettings = z.object({
enabled: z.boolean().default(true),
period_ms: z.coerce.bigint().gte(BigInt(1)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).default(BigInt(1000)),
@@ -857,7 +918,8 @@ export const zJfjochSettings = z.object({
image_pusher: zImagePusherType,
zeromq_preview: zZeromqPreviewSettings.optional(),
zeromq_metadata: zZeromqMetadataSettings.optional(),
dark_mask: zDarkMaskSettings.optional()
dark_mask: zDarkMaskSettings.optional(),
rugnux: zRugnuxSettings.optional()
});
export const zDatasetSettingsWritable = z.object({
@@ -1186,6 +1248,20 @@ export const zGetConfigSelectDetectorResponse = zDetectorList;
export const zPutConfigSelectDetectorBody = zDetectorSelection;
/**
* Everything OK
*/
export const zGetConfigRugnuxResponse = zRugnuxStatus;
export const zPutConfigRugnuxBody = zRugnuxSettings;
export const zPutConfigRugnuxModelBody = z.string();
/**
* Everything OK
*/
export const zGetRugnuxStatusResponse = zRugnuxLiveStatus;
/**
* Everything OK
*/
@@ -1410,6 +1486,11 @@ export const zGetResultScanResponse = zScanResult;
*/
export const zGetImageBufferStartCborResponse = z.string();
/**
* Event stream
*/
export const zGetLiveEventsResponse = z.string();
export const zGetImageBufferImageCborQuery = z.object({
id: z.coerce.bigint().gte(BigInt(-2)).max(BigInt('9223372036854775807'), { error: 'Invalid value: Expected int64 to be <= 9223372036854775807' }).optional().default(BigInt(-1))
});
+166
View File
@@ -0,0 +1,166 @@
import {Alert, Box, Chip, LinearProgress, Paper, Stack, Typography} from "@mui/material";
import {indigo, lime, deepOrange, grey} from "@mui/material/colors";
import {useQuery} from "@tanstack/react-query";
import {getConfigRugnuxOptions, getRugnuxStatusOptions} from "../client/@tanstack/react-query.gen";
import {rugnux_live_status} from "../client";
import {useTokenRequired} from "../auth";
import EmptyState from "./EmptyState";
import RugnuxTimeline from "./RugnuxTimeline";
import RugnuxSettings from "./RugnuxSettings";
import {CutAlert, lastImageTime, ProbeAlert, StepTable, VerdictAlert} from "./RugnuxParts";
import BoltIcon from "@mui/icons-material/Bolt";
const STATE_COLOUR: Record<string, { bg: string, fg: string }> = {
Running: {bg: indigo[500], fg: "#fff"},
Done: {bg: lime[600], fg: "rgba(0,0,0,.87)"},
Failed: {bg: deepOrange[700], fg: "#fff"},
Cancelled: {bg: grey[500], fg: "#fff"},
Skipped: {bg: grey[400], fg: "rgba(0,0,0,.87)"},
};
// The summary rows rugnux writes; the ones a crystallographer looks at first get a stronger tile.
const KEY_ROW = /cell|space group|resolution|cc1\/2|r-?free|model/i;
function Section({title, children}: { title: string, children: React.ReactNode }) {
return (
<Paper sx={{p: 3, height: "100%"}}>
<Typography sx={{fontSize: "0.8rem", fontWeight: 700, letterSpacing: ".08em", textTransform: "uppercase",
color: "primary.dark", mb: 2}}>
{title}
</Typography>
{children}
</Paper>
);
}
function Header({s}: { s: rugnux_live_status }) {
const colour = STATE_COLOUR[s.state] ?? STATE_COLOUR.Skipped;
const latency = s.last_image_to_written_s;
const lastImage = lastImageTime(s);
return (
<Paper sx={{p: 3, display: "flex", alignItems: "center", gap: 3, flexWrap: "wrap",
background: `linear-gradient(90deg, ${indigo[50]} 0%, #fff 70%)`}}>
<Box sx={{flexGrow: 1, minWidth: 280}}>
<Stack direction="row" spacing={2} alignItems="center">
<Typography variant="h4" sx={{fontWeight: 700, color: "primary.dark"}}>Rugnux live</Typography>
<Chip label={s.state} sx={{bgcolor: colour.bg, color: colour.fg, fontWeight: 700,
fontSize: "1rem", height: 34, px: 1}}/>
</Stack>
<Typography sx={{mt: 1, color: "text.secondary", fontSize: "1.1rem"}}>
{s.images_received} / {s.images_expected} images
{lastImage !== undefined && ` · collected in ${lastImage.toFixed(1)} s`}
{` · ${s.elapsed_s.toFixed(1)} s since the first image`}
</Typography>
{s.message && <Typography sx={{mt: 0.5, color: "error.main"}}>{s.message}</Typography>}
</Box>
<Box sx={{textAlign: "right"}}>
<Typography sx={{fontSize: "0.85rem", fontWeight: 700, letterSpacing: ".08em",
textTransform: "uppercase", color: "text.secondary"}}>
{latency !== undefined ? "Last image → all results written" : "Processing for"}
</Typography>
<Typography sx={{fontSize: "4.5rem", lineHeight: 1.05, fontWeight: 800,
color: latency === undefined ? indigo[300] : s.state === 'Done' ? lime[900] : indigo[700]}}>
{(latency ?? s.elapsed_s).toFixed(1)} s
</Typography>
</Box>
</Paper>
);
}
function CurrentStep({s}: { s: rugnux_live_status }) {
const current = s.state === 'Running' && s.steps.length > 0 ? s.steps[s.steps.length - 1] : undefined;
if (!current)
return null;
return (
<Box sx={{mb: 2}}>
<Stack direction="row" justifyContent="space-between" alignItems="baseline">
<Typography sx={{fontWeight: 700, fontSize: "1.25rem"}}>{current.title}</Typography>
{s.step_progress !== undefined &&
<Typography sx={{fontWeight: 700, color: "primary.main"}}>{(s.step_progress * 100).toFixed(0)} %</Typography>}
</Stack>
<LinearProgress variant={s.step_progress !== undefined ? "determinate" : "indeterminate"}
value={(s.step_progress ?? 0) * 100} sx={{height: 10, borderRadius: 5, mt: 1}}/>
</Box>
);
}
function SummaryTiles({s}: { s: rugnux_live_status }) {
if (s.summary.length === 0)
return null;
return (
<Box sx={{display: "grid", gridTemplateColumns: {xs: "1fr", sm: "1fr 1fr"}, gap: 1.5, mt: 2}}>
{s.summary.map((row, i) => {
const key = KEY_ROW.test(row.label) || KEY_ROW.test(row.value);
const short = row.value.length <= 24;
return (
<Box key={i} sx={{p: 1.5, borderRadius: 2, gridColumn: key ? "auto" : "1 / -1",
bgcolor: key ? indigo[50] : grey[50],
border: "1px solid", borderColor: key ? indigo[100] : grey[200]}}>
<Typography sx={{fontSize: "0.75rem", fontWeight: 700, letterSpacing: ".06em",
textTransform: "uppercase", color: key ? "primary.main" : "text.secondary"}}>
{row.label}
</Typography>
<Typography sx={{fontSize: key && short ? "1.2rem" : "0.95rem", fontWeight: key ? 700 : 400,
color: key ? "primary.dark" : "text.primary", wordBreak: "break-word"}}>
{row.value}
</Typography>
</Box>
);
})}
</Box>
);
}
function RugnuxPage({measuring}: { measuring: boolean }) {
const {data: s, isError} = useQuery({
...getRugnuxStatusOptions(),
refetchInterval: 1000,
});
const {data: settings} = useQuery({...getConfigRugnuxOptions(), refetchInterval: 2000});
const tokenRequired = useTokenRequired();
let live;
if (isError && tokenRequired)
live = <Paper><EmptyState icon={<BoltIcon fontSize="inherit"/>}
text="Dataset token required (key button in the top bar)"/></Paper>;
else if (isError || s === undefined || s.state === 'Idle')
live = <Paper><EmptyState icon={<BoltIcon fontSize="inherit"/>}
text="No rugnux run yet - start a rotation collection with live processing enabled"/></Paper>;
else
live = (
<Stack spacing={2}>
<Header s={s}/>
<CutAlert s={s}/>
<ProbeAlert s={s} large/>
<Section title="Timeline">
<RugnuxTimeline s={s} deadline_s={settings?.deadline_s ?? 0}/>
</Section>
<Box sx={{display: "grid", gridTemplateColumns: {xs: "1fr", lg: "1fr 1fr"}, gap: 2}}>
<Section title="Processing">
<CurrentStep s={s}/>
<StepTable steps={s.steps}/>
</Section>
<Section title="Result">
{s.verdict
? <VerdictAlert s={s}/>
: <Typography color="text.secondary">
{s.state === 'Running' ? "Results appear when the run is over." : "No result."}
</Typography>}
<SummaryTiles s={s}/>
<Stack spacing={1} sx={{mt: s.warnings.length > 0 ? 2 : 0}}>
{s.warnings.map((w, i) => <Alert key={i} severity="warning">{w}</Alert>)}
</Stack>
</Section>
</Box>
</Stack>
);
return (
<Stack spacing={2}>
{live}
<RugnuxSettings measuring={measuring}/>
</Stack>
);
}
export default RugnuxPage;
+88
View File
@@ -0,0 +1,88 @@
import {Alert, AlertTitle, Box, Paper, Table, TableBody, TableCell, TableContainer, TableRow, Typography} from "@mui/material";
import {rugnux_live_status, rugnux_live_step} from "../client";
// Pieces of the rugnux live status shared by the dashboard card and the Rugnux page.
export const verdictSeverity = (verdict: string) => {
switch (verdict) {
case "OK":
return "success";
case "WARNINGS":
return "warning";
default:
return "error";
}
};
// When the last image arrived, in seconds since the collection started. Older brokers do not report
// it; there it follows from the latency (every result written `latency` after the last image).
export const lastImageTime = (s: rugnux_live_status) =>
s.last_image_s ?? (s.last_image_to_written_s !== undefined ? s.elapsed_s - s.last_image_to_written_s : undefined);
export function CutAlert({s}: { s: rugnux_live_status }) {
if (!s.cut)
return null;
return (
<Alert severity="warning" variant="filled" sx={{width: "100%", textAlign: "left", fontSize: "1.05rem"}}>
Analysis cut ({s.cut}){s.cut_s !== undefined && ` at ${s.cut_s.toFixed(1)} s`} - results as established
</Alert>
);
}
export function ProbeAlert({s, large}: { s: rugnux_live_status, large?: boolean }) {
if (!s.probe)
return null;
const noLattice = s.probe.startsWith("no lattice");
return (
<Alert severity={noLattice ? "warning" : "info"} variant={large ? "filled" : "standard"}
sx={{width: "100%", textAlign: "left", ...(large && {fontSize: "1.15rem", alignItems: "center"})}}>
{large && <AlertTitle sx={{mb: 0, fontSize: "0.8rem", textTransform: "uppercase", letterSpacing: ".06em"}}>
Lattice probe{s.probe_s !== undefined && ` · ${s.probe_s.toFixed(1)} s after the first image`}
</AlertTitle>}
{s.probe}{!large && s.probe_s !== undefined && ` (at ${s.probe_s.toFixed(1)} s)`}
</Alert>
);
}
export function VerdictAlert({s}: { s: rugnux_live_status }) {
if (!s.verdict)
return null;
return (
<Alert severity={verdictSeverity(s.verdict)} sx={{width: "100%", textAlign: "left"}}>
<AlertTitle sx={{fontWeight: 700}}>{s.verdict}</AlertTitle>
{s.verdict_text}
</Alert>
);
}
const stepDuration = (step: rugnux_live_step) =>
step.duration_s !== undefined ? step.duration_s.toFixed(1) + " s" : "running";
export function StepTable({steps}: { steps: rugnux_live_step[] }) {
if (steps.length === 0)
return null;
return (
<TableContainer component={Paper} variant="outlined">
<Table size="small">
<TableBody>
{steps.map((step, i) => (
<TableRow key={i} sx={step.duration_s === undefined ? {bgcolor: "action.hover"} : undefined}>
<TableCell sx={{verticalAlign: "top", color: "text.secondary", width: 72, whiteSpace: "nowrap"}}>
{step.started_s.toFixed(1)} s
</TableCell>
<TableCell sx={{verticalAlign: "top"}}>
<Typography variant="body2" sx={{fontWeight: 600}}>{step.title}</Typography>
{step.notes.map((note, j) =>
<Box key={j} sx={{color: "text.secondary", fontSize: "0.85rem"}}>{note}</Box>)}
</TableCell>
<TableCell align="right" sx={{verticalAlign: "top", whiteSpace: "nowrap",
fontWeight: step.duration_s === undefined ? 700 : 400}}>
{stepDuration(step)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
);
}
@@ -0,0 +1,94 @@
import {ChangeEvent, useEffect, useState} from 'react';
import {Button, FormControlLabel, Stack, Switch, Typography} from "@mui/material";
import UploadFileIcon from "@mui/icons-material/UploadFile";
import DeleteIcon from "@mui/icons-material/Delete";
import {useQuery, useQueryClient} from "@tanstack/react-query";
import {
deleteConfigRugnuxModelMutation, getConfigRugnuxOptions, getConfigRugnuxQueryKey,
putConfigRugnuxModelMutation, putConfigRugnuxMutation
} from "../client/@tanstack/react-query.gen";
import NumberTextField from "./NumberTextField";
import SettingsPanel, {UploadSnackbarView} from "./SettingsPanel";
import {useUpload} from "./useUpload";
const sizeText = (bytes?: number) =>
bytes === undefined ? "" : bytes >= 1024 * 1024 ? `${(bytes / 1024 / 1024).toFixed(1)} MiB` : `${(bytes / 1024).toFixed(0)} KiB`;
// Rugnux live mode (enable, reserved CPUs) and the model used for MR, as two panels side by side.
function RugnuxSettings({measuring}: { measuring: boolean }) {
const queryClient = useQueryClient();
const {data: s} = useQuery({...getConfigRugnuxOptions(), refetchInterval: 2000});
const refresh = {onSuccess: () => queryClient.invalidateQueries({queryKey: getConfigRugnuxQueryKey()})};
const settings = useUpload({...putConfigRugnuxMutation(), ...refresh});
const upload = useUpload({...putConfigRugnuxModelMutation(), ...refresh});
const remove = useUpload({...deleteConfigRugnuxModelMutation(), ...refresh});
const [enable, setEnable] = useState(false);
const [cpus, setCpus] = useState(4);
const [cpusError, setCpusError] = useState(false);
const [deadline, setDeadline] = useState(0);
const [deadlineError, setDeadlineError] = useState(false);
const [counter, setCounter] = useState(0);
const [file, setFile] = useState<File>();
useEffect(() => {
if (s !== undefined) {
setEnable(s.enable);
setCpus(s.reserved_cpus);
setDeadline(s.deadline_s ?? 0);
setCounter(c => c + 1);
}
}, [s?.enable, s?.reserved_cpus, s?.deadline_s]);
const dirty = s !== undefined && (enable !== s.enable || cpus !== s.reserved_cpus || deadline !== (s.deadline_s ?? 0));
return (
<Stack direction={{xs: "column", md: "row"}} spacing={2} alignItems="stretch">
<SettingsPanel title="Live processing" dirty={dirty} spacing={2}
onUpload={() => settings.submit({enable, reserved_cpus: cpus, deadline_s: deadline})}
uploadDisabled={measuring || cpusError || deadlineError || settings.pending}
snackbar={settings.snackbar}>
<FormControlLabel label="Process DECTRIS rotation collections with rugnux"
control={<Switch checked={enable} disabled={measuring}
onChange={e => setEnable(e.target.checked)}/>}/>
<NumberTextField label="Reserved CPU threads" start_val={cpus} min={0} max={512}
counter={counter} disabled={measuring}
callback={(v, err) => { setCpus(v); setCpusError(err); }}/>
<NumberTextField label="Cut the analysis this long after the last image (0 = never)" units="s"
start_val={deadline} min={0} float decimals={1} counter={counter} disabled={measuring}
sx={{width: "100%"}} callback={(v, err) => { setDeadline(v); setDeadlineError(err); }}/>
</SettingsPanel>
<SettingsPanel title="Model for molecular replacement" spacing={2}>
<Typography sx={{fontWeight: 600}}>
{s?.model_loaded ? `Model loaded · ${sizeText(s.model_size_bytes)}` : "No model loaded"}
</Typography>
<Typography variant="body2" color="text.secondary">
PDB or mmCIF, optionally gzipped, up to 64 MiB. Kept until removed.
</Typography>
<Stack direction="row" spacing={1} alignItems="center">
<Button component="label" variant="outlined" startIcon={<UploadFileIcon/>} disabled={measuring}>
{file ? file.name : "Choose file"}
<input hidden type="file" accept=".pdb,.cif,.ent,.gz"
onChange={(e: ChangeEvent<HTMLInputElement>) => setFile(e.target.files?.[0])}/>
</Button>
<Button variant="contained" disableElevation disabled={!file || measuring || upload.pending}
onClick={() => file && upload.submit(file)}>
Upload
</Button>
<Button color="error" startIcon={<DeleteIcon/>}
disabled={!s?.model_loaded || measuring || remove.pending}
onClick={() => remove.submit(undefined)}>
Remove
</Button>
</Stack>
<UploadSnackbarView snackbar={upload.snackbar}/>
<UploadSnackbarView snackbar={remove.snackbar}/>
</SettingsPanel>
</Stack>
);
}
export default RugnuxSettings;
+62
View File
@@ -0,0 +1,62 @@
import {memo} from 'react';
import {Alert, Box, LinearProgress, Typography} from "@mui/material";
import {useQuery} from "@tanstack/react-query";
import {getRugnuxStatusOptions} from "../client/@tanstack/react-query.gen";
import {useTokenRequired} from "../auth";
import SettingsPanel from "./SettingsPanel";
import {CutAlert, ProbeAlert, StepTable, VerdictAlert} from "./RugnuxParts";
// Live rugnux processing of the last collection, as a dashboard card: the step it is on, the steps
// behind it with what they found, and once it is over the verdict. Nothing while rugnux has not run.
function RugnuxStatus() {
const {data: s, isError} = useQuery({
...getRugnuxStatusOptions(),
refetchInterval: 1000,
});
const tokenRequired = useTokenRequired();
if (isError && tokenRequired)
return (
<SettingsPanel title="Rugnux">
<Typography>Dataset token required (key button in the top bar)</Typography>
</SettingsPanel>
);
if (isError || s === undefined || s.state === 'Idle')
return null;
const current = s.state === 'Running' && s.steps.length > 0 ? s.steps[s.steps.length - 1] : undefined;
return (
<SettingsPanel title="Rugnux" spacing={2}>
<Typography variant="h6">
{s.state} · {s.images_received} / {s.images_expected} images · {s.elapsed_s.toFixed(0)} s
</Typography>
{s.message && <Typography color="text.secondary">{s.message}</Typography>}
<CutAlert s={s}/>
<ProbeAlert s={s}/>
{current && (
<Box sx={{width: "100%"}}>
<Typography>{current.title}</Typography>
<LinearProgress variant={s.step_progress !== undefined ? "determinate" : "indeterminate"}
value={(s.step_progress ?? 0) * 100}/>
</Box>
)}
{s.last_image_to_written_s !== undefined && (
<Typography variant="h5">
Last image → all written: {s.last_image_to_written_s.toFixed(1)} s
</Typography>
)}
<VerdictAlert s={s}/>
<StepTable steps={s.steps}/>
{s.warnings.map((w, i) => (
<Alert key={i} severity="warning" sx={{width: "100%", textAlign: "left"}}>{w}</Alert>
))}
</SettingsPanel>
);
}
export default memo(RugnuxStatus);
+174
View File
@@ -0,0 +1,174 @@
import {Box, Tooltip, Typography} from "@mui/material";
import {keyframes} from "@mui/system";
import {indigo, lime, lightBlue, orange, grey, deepOrange} from "@mui/material/colors";
import {rugnux_live_status} from "../client";
import {lastImageTime} from "./RugnuxParts";
// The overlap of collection and processing on one time axis (seconds since the first image): the
// detector lane fills while images arrive, the rugnux lane shows its steps where they ran, and the
// gap between "last image" and "all written" is the headline latency.
const stripes = keyframes`
from { background-position: 0 0; }
to { background-position: 40px 0; }
`;
const runningSx = {
backgroundImage: "linear-gradient(135deg, rgba(255,255,255,.28) 25%, transparent 25%, transparent 50%, "
+ "rgba(255,255,255,.28) 50%, rgba(255,255,255,.28) 75%, transparent 75%, transparent)",
backgroundSize: "40px 40px",
animation: `${stripes} 1s linear infinite`,
};
const STEP_COLOURS = [indigo[500], indigo[300], indigo[700], lightBlue[700], indigo[400], lightBlue[900]];
const LABEL_WIDTH = 120;
const LANE_HEIGHT = 64;
const AXIS_HEIGHT = 28;
function niceStep(span: number) {
for (const s of [1, 2, 5, 10, 20, 30, 60, 120])
if (span / s <= 12)
return s;
return 300;
}
function Marker({x, colour, label, top, dashed}: { x: number, colour: string, label: string, top: number, dashed?: boolean }) {
return (
<Box sx={{position: "absolute", left: `${x}%`, top, bottom: AXIS_HEIGHT, pointerEvents: "none"}}>
<Box sx={{position: "absolute", top: 0, bottom: 0, left: -1.5, width: 0, zIndex: 1,
borderLeft: `3px ${dashed ? "dashed" : "solid"} ${colour}`}}/>
<Box sx={{position: "absolute", top: 0, left: 0, transform: "translate(-50%, -100%)", zIndex: 4,
bgcolor: colour, color: "#fff", px: 1, py: 0.25, borderRadius: 1, whiteSpace: "nowrap",
fontSize: "0.85rem", fontWeight: 700, boxShadow: 1}}>
{label}
</Box>
</Box>
);
}
// deadline_s: the configured cutoff after the last image, 0 = none
function RugnuxTimeline({s, deadline_s}: { s: rugnux_live_status, deadline_s: number }) {
const lastImage = lastImageTime(s);
const collecting = lastImage === undefined && s.state === 'Running';
const collectionEnd = lastImage ?? s.elapsed_s;
const rate = collectionEnd > 0 ? s.images_received / collectionEnd : 0;
const expectedEnd = rate > 0 ? s.images_expected / rate : 0;
const deadline = lastImage !== undefined && deadline_s > 0 ? lastImage + deadline_s : undefined;
const span = Math.max(s.elapsed_s, expectedEnd, deadline ?? 0, 10) * 1.06;
const pct = (t: number) => 100 * Math.min(Math.max(t, 0), span) / span;
const tick = niceStep(span);
const ticks = Array.from({length: Math.floor(span / tick) + 1}, (_, i) => i * tick);
const done = s.state !== 'Running';
const latency = s.last_image_to_written_s;
return (
<Box sx={{display: "flex", pt: 6, pb: latency !== undefined ? 6 : 1}}>
<Box sx={{width: LABEL_WIDTH, flexShrink: 0}}>
{["Detector", "Rugnux"].map((l, i) => (
<Box key={l} sx={{height: LANE_HEIGHT, mb: i === 0 ? 1.5 : 0, display: "flex", alignItems: "center"}}>
<Typography sx={{fontWeight: 700, fontSize: "1.1rem", color: "primary.dark"}}>{l}</Typography>
</Box>
))}
</Box>
<Box sx={{position: "relative", flexGrow: 1, minWidth: 0}}>
{/* grid lines */}
{ticks.map(t => (
<Box key={t} sx={{position: "absolute", left: `${pct(t)}%`, top: 0,
height: 2 * LANE_HEIGHT + 12, borderLeft: `1px solid ${grey[200]}`}}/>
))}
{/* detector lane */}
<Box sx={{position: "relative", height: LANE_HEIGHT, mb: 1.5, bgcolor: grey[100], borderRadius: 2}}>
<Box sx={{position: "absolute", left: 0, top: 0, bottom: 0, width: `${pct(collectionEnd)}%`,
bgcolor: lime[600], borderRadius: 2, display: "flex", alignItems: "center", px: 2,
overflow: "hidden", ...(collecting && runningSx)}}>
<Typography sx={{position: "relative", zIndex: 2, fontWeight: 700, fontSize: "1.1rem",
color: "rgba(0,0,0,.85)", whiteSpace: "nowrap", bgcolor: "rgba(255,255,255,.55)",
px: 1, borderRadius: 1}}>
{s.images_received} / {s.images_expected} images{rate > 0 && ` · ${rate.toFixed(0)} Hz`}
</Typography>
</Box>
{collecting && expectedEnd > collectionEnd && (
<Box sx={{position: "absolute", left: `${pct(collectionEnd)}%`, top: 0, bottom: 0,
width: `${pct(expectedEnd) - pct(collectionEnd)}%`,
border: `2px dashed ${lime[700]}`, borderLeft: "none", borderRadius: "0 8px 8px 0"}}/>
)}
</Box>
{/* rugnux lane */}
<Box sx={{position: "relative", height: LANE_HEIGHT, bgcolor: grey[100], borderRadius: 2}}>
{s.steps.map((step, i) => {
const open = step.duration_s === undefined;
const end = open ? s.elapsed_s : step.started_s + step.duration_s!;
const width = Math.max(pct(end) - pct(step.started_s), 0.4);
return (
<Tooltip key={i} arrow title={
<Box>
<b>{step.title}</b> · {open ? "running" : `${step.duration_s!.toFixed(1)} s`}
{step.notes.map((n, j) => <div key={j}>{n}</div>)}
</Box>}>
<Box sx={{position: "absolute", left: `${pct(step.started_s)}%`, width: `${width}%`,
top: 0, bottom: 0, bgcolor: STEP_COLOURS[i % STEP_COLOURS.length],
borderRight: "2px solid #fff", display: "flex", alignItems: "center",
justifyContent: "center", overflow: "hidden", px: 0.5,
borderRadius: i === 0 ? "8px 0 0 8px" : 0,
...(open && !done && runningSx),
...(i === s.steps.length - 1 && {borderTopRightRadius: 8, borderBottomRightRadius: 8})}}>
<Typography sx={{position: "relative", zIndex: 2, color: "#fff", fontWeight: 600, fontSize: "0.9rem",
whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis"}}>
{step.title}
</Typography>
</Box>
</Tooltip>
);
})}
</Box>
{/* axis */}
<Box sx={{position: "relative", height: AXIS_HEIGHT}}>
{ticks.map(t => (
<Typography key={t} sx={{position: "absolute", left: `${pct(t)}%`, transform: "translateX(-50%)",
fontSize: "0.85rem", color: "text.secondary"}}>
{t} s
</Typography>
))}
</Box>
{/* markers */}
{s.probe_s !== undefined && (
<Marker x={pct(s.probe_s)} top={-8} label={`probe ${s.probe_s.toFixed(1)} s`}
colour={s.probe.startsWith("no lattice") ? orange[800] : lightBlue[800]}/>
)}
{lastImage !== undefined && (
<Marker x={pct(lastImage)} top={-8} dashed colour={grey[800]} label="last image"/>
)}
{deadline !== undefined && s.cut !== 'deadline' && (
<Marker x={pct(deadline)} top={-8} dashed colour={deepOrange[700]} label={`deadline ${deadline.toFixed(1)} s`}/>
)}
{s.cut && s.cut_s !== undefined && (
<Marker x={pct(s.cut_s)} top={-8} colour={deepOrange[900]} label={`${s.cut} cut ${s.cut_s.toFixed(1)} s`}/>
)}
{!done && <Marker x={pct(s.elapsed_s)} top={-8} colour="#d32f2f" label="now"/>}
{/* latency bracket */}
{latency !== undefined && lastImage !== undefined && (
<Box sx={{position: "absolute", left: `${pct(lastImage)}%`, width: `${pct(lastImage + latency) - pct(lastImage)}%`,
top: 2 * LANE_HEIGHT + 12 + AXIS_HEIGHT, height: 14,
borderBottom: `4px solid ${lime[700]}`, borderLeft: `4px solid ${lime[700]}`,
borderRight: `4px solid ${lime[700]}`, borderRadius: "0 0 8px 8px"}}>
<Typography sx={{position: "absolute", right: 0, top: "100%", mt: 0.5, fontWeight: 800,
fontSize: "1.4rem", color: lime[900], whiteSpace: "nowrap"}}>
+{latency.toFixed(1)} s → all written
</Typography>
</Box>
)}
</Box>
</Box>
);
}
export default RugnuxTimeline;
+2
View File
@@ -19,6 +19,8 @@ ADD_LIBRARY(JFJochReader STATIC
HDF5ImageLocator.cpp
JFJochStartMessageReader.cpp
JFJochStartMessageReader.h
JFJochImageBufferReader.cpp
JFJochImageBufferReader.h
HDF5ImageLocator.h
HDF5ImageSource.cpp
HDF5ImageSource.h
+69
View File
@@ -0,0 +1,69 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochImageBufferReader.h"
#include <cstring>
#include <thread>
#include "JFJochStartMessageReader.h"
#include "../common/JFJochException.h"
#include "../frame_serialize/CBORStream2Deserializer.h"
JFJochImageBufferReader::JFJochImageBufferReader(ImageBuffer &buffer) : buffer_(buffer) {}
void JFJochImageBufferReader::Open(const StartMessage &start) {
number_of_images_ = start.number_of_images;
ended_ = false;
SetStartMessage(DatasetFromStartMessage(start, default_experiment));
}
void JFJochImageBufferReader::Ended() {
ended_ = true;
}
uint64_t JFJochImageBufferReader::GetNumberOfImages() const {
return number_of_images_;
}
void JFJochImageBufferReader::Close() {
SetStartMessage({});
}
bool JFJochImageBufferReader::WaitForMessage(int64_t image_number, std::vector<uint8_t> &cbor) {
if (image_number < 0 || static_cast<uint64_t>(image_number) >= number_of_images_)
throw JFJochException(JFJochExceptionCategory::ArrayOutOfBounds, "Image out of bounds");
// The buffer has no notification of its own; at 100 Hz a millisecond's poll costs nothing.
while (!buffer_.GetImage(cbor, image_number)) {
if (ended_)
return buffer_.GetImage(cbor, image_number); // one that came in just before the end
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return true;
}
bool JFJochImageBufferReader::ReadRawImage(int64_t image_number, JFJochReaderRawImage &ret) {
if (!WaitForMessage(image_number, ret.read_buffer))
return false;
const auto msg = CBORStream2Deserialize(ret.read_buffer);
if (msg->msg_type != CBORImageType::IMAGE || !msg->data_message)
return false;
// Copied out of the message: read_buffer is scratch a caller may hand to the next read.
const CompressedImage &image = msg->data_message->image;
ret.image_buffer.resize(image.GetCompressedSize());
memcpy(ret.image_buffer.data(), image.GetCompressed(), image.GetCompressedSize());
ret.image = CompressedImage(ret.image_buffer.data(), ret.image_buffer.size(), image.GetWidth(),
image.GetHeight(), image.GetMode(), image.GetCompressionAlgorithm());
return true;
}
bool JFJochImageBufferReader::LoadImage_i(std::shared_ptr<JFJochReaderDataset> &dataset, DataMessage &message,
std::vector<uint8_t> &buffer, int64_t image_number, bool) {
if (!dataset || !WaitForMessage(image_number, buffer))
return false;
const auto msg = CBORStream2Deserialize(buffer);
if (msg->msg_type != CBORImageType::IMAGE || !msg->data_message)
return false;
message = *msg->data_message;
return true;
}
+40
View File
@@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <atomic>
#include <string>
#include <vector>
#include "JFJochReader.h"
#include "../common/ImageBuffer.h"
// Reads a collection inside the broker while it arrives: the dataset comes from its start message,
// the images from the broker's image buffer, where the receiver puts each one as it forwards it. A
// read of an image not there yet waits for it; after Ended it returns false for an image that never
// came. The buffer must hold the whole collection (slots >= images) - a slot taken again for a later
// image would hand out that image instead.
class JFJochImageBufferReader : public JFJochReader {
public:
explicit JFJochImageBufferReader(ImageBuffer &buffer);
void Open(const StartMessage &start);
// No image comes after this: the collection is over, or abandoned.
void Ended();
[[nodiscard]] uint64_t GetNumberOfImages() const override;
void Close() override;
bool ReadRawImage(int64_t image_number, JFJochReaderRawImage &image) override;
std::vector<SpotToSave> ReadSpots(int64_t image) const override { return {}; }
private:
bool LoadImage_i(std::shared_ptr<JFJochReaderDataset> &dataset, DataMessage &message,
std::vector<uint8_t> &buffer, int64_t image_number, bool update_dataset) override;
// The image's CBOR message, as the receiver put it in the buffer; false when it never came.
bool WaitForMessage(int64_t image_number, std::vector<uint8_t> &cbor);
ImageBuffer &buffer_;
uint64_t number_of_images_ = 0;
std::atomic<bool> ended_ = false;
};
+65 -66
View File
@@ -15,78 +15,77 @@ namespace {
const std::string prefix = std::filesystem::path(start.file_prefix).filename().string();
return fmt::format("{:s}_data_{:06d}.h5", prefix, file_number + 1);
}
}
// The dataset as the start message describes it - the same quantities an HDF5 master states, taken
// from the message the writer wrote it from.
std::shared_ptr<JFJochReaderDataset> Dataset(const StartMessage &start, const DiffractionExperiment &defaults) {
auto dataset = std::make_shared<JFJochReaderDataset>();
dataset->arm_date = start.arm_date;
dataset->jfjoch_release = start.jfjoch_release;
dataset->error_value = start.error_value;
dataset->file_detect_ice_rings = start.detect_ice_rings;
std::shared_ptr<JFJochReaderDataset> DatasetFromStartMessage(const StartMessage &start,
const DiffractionExperiment &defaults) {
auto dataset = std::make_shared<JFJochReaderDataset>();
dataset->arm_date = start.arm_date;
dataset->jfjoch_release = start.jfjoch_release;
dataset->error_value = start.error_value;
dataset->file_detect_ice_rings = start.detect_ice_rings;
auto &x = dataset->experiment;
x = defaults;
// The reader hands every image out as signed 32-bit, as JFJochHDF5Reader does.
x.BitDepthImage(32);
x.PixelSigned(true);
x.FilePrefix(std::filesystem::path(start.file_prefix).filename().string());
x.BeamX_pxl(start.beam_center_x);
x.BeamY_pxl(start.beam_center_y);
x.DetectorDistance_mm(start.detector_distance * 1000.0f);
x.PoniRot1_rad(start.poni_rot1.value_or(0.0f));
x.PoniRot2_rad(start.poni_rot2.value_or(0.0f));
x.PoniRot3_rad(start.poni_rot3.value_or(0.0f));
x.IncidentEnergy_keV(WVL_1A_IN_KEV / start.incident_wavelength);
if (start.incident_wavelength_spread)
x.BandwidthFWHM(start.incident_wavelength_spread.value() / start.incident_wavelength);
x.DetectIceRings(start.detect_ice_rings.value_or(false));
auto &x = dataset->experiment;
x = defaults;
// The reader hands every image out as signed 32-bit, as JFJochHDF5Reader does.
x.BitDepthImage(32);
x.PixelSigned(true);
x.FilePrefix(std::filesystem::path(start.file_prefix).filename().string());
x.BeamX_pxl(start.beam_center_x);
x.BeamY_pxl(start.beam_center_y);
x.DetectorDistance_mm(start.detector_distance * 1000.0f);
x.PoniRot1_rad(start.poni_rot1.value_or(0.0f));
x.PoniRot2_rad(start.poni_rot2.value_or(0.0f));
x.PoniRot3_rad(start.poni_rot3.value_or(0.0f));
x.IncidentEnergy_keV(WVL_1A_IN_KEV / start.incident_wavelength);
if (start.incident_wavelength_spread)
x.BandwidthFWHM(start.incident_wavelength_spread.value() / start.incident_wavelength);
x.DetectIceRings(start.detect_ice_rings.value_or(false));
DetectorSetup detector = DetDECTRIS(start.image_size_x, start.image_size_y, start.detector_description, {});
detector.PixelSize_um(start.pixel_size_x * 1e6f);
detector.MirrorY(start.mirror_y);
detector.ImageOrientation(DetectorOrientation(start.detector_orientation_mirror_y,
start.detector_orientation_quarter_turns));
detector.SensorThickness_um(start.sensor_thickness * 1e6f);
detector.SensorMaterial(start.sensor_material);
detector.SaturationLimit(SaturationLimitFromValue(start.saturation_value));
detector.BitDepthImage(32);
detector.MinFrameTime(std::chrono::microseconds(0));
detector.MinCountTime(std::chrono::microseconds(0));
detector.ReadOutTime(std::chrono::nanoseconds(0));
x.Detector(detector);
DetectorSetup detector = DetDECTRIS(start.image_size_x, start.image_size_y, start.detector_description, {});
detector.PixelSize_um(start.pixel_size_x * 1e6f);
detector.MirrorY(start.mirror_y);
detector.ImageOrientation(DetectorOrientation(start.detector_orientation_mirror_y,
start.detector_orientation_quarter_turns));
detector.SensorThickness_um(start.sensor_thickness * 1e6f);
detector.SensorMaterial(start.sensor_material);
detector.SaturationLimit(SaturationLimitFromValue(start.saturation_value));
detector.BitDepthImage(32);
detector.MinFrameTime(std::chrono::microseconds(0));
detector.MinCountTime(std::chrono::microseconds(0));
detector.ReadOutTime(std::chrono::nanoseconds(0));
x.Detector(detector);
x.FrameTime(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<float>(start.frame_time)),
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<float>(start.count_time)));
x.FrameTime(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<float>(start.frame_time)),
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::duration<float>(start.count_time)));
x.Goniometer(start.goniometer);
x.GridScan(start.grid_scan);
x.Smargon(start.smargon_position);
x.NumTriggers(1);
x.ImagesPerTrigger(static_cast<int64_t>(start.number_of_images));
x.Goniometer(start.goniometer);
x.GridScan(start.grid_scan);
x.Smargon(start.smargon_position);
x.NumTriggers(1);
x.ImagesPerTrigger(static_cast<int64_t>(start.number_of_images));
InstrumentMetadata instrument;
instrument.SourceName(start.source_name);
instrument.SourceType(start.source_type);
instrument.InstrumentName(start.instrument_name);
x.ImportInstrumentMetadata(instrument);
x.SampleName(start.sample_name);
x.SampleTemperature_K(start.sample_temperature_K);
x.RingCurrent_mA(start.ring_current_mA);
x.TotalFlux(start.total_flux);
x.AttenuatorTransmission(start.attenuator_transmission);
if (start.beam_size_x && start.beam_size_y) {
x.BeamSizeX_um(start.beam_size_x.value() * 1e6f);
x.BeamSizeY_um(start.beam_size_y.value() * 1e6f);
}
if (!start.pixel_mask.empty())
dataset->pixel_mask = std::make_shared<const PixelMask>(start.pixel_mask.begin()->second);
else
dataset->pixel_mask = std::make_shared<const PixelMask>(
std::vector<uint32_t>(start.image_size_x * start.image_size_y));
return dataset;
InstrumentMetadata instrument;
instrument.SourceName(start.source_name);
instrument.SourceType(start.source_type);
instrument.InstrumentName(start.instrument_name);
x.ImportInstrumentMetadata(instrument);
x.SampleName(start.sample_name);
x.SampleTemperature_K(start.sample_temperature_K);
x.RingCurrent_mA(start.ring_current_mA);
x.TotalFlux(start.total_flux);
x.AttenuatorTransmission(start.attenuator_transmission);
if (start.beam_size_x && start.beam_size_y) {
x.BeamSizeX_um(start.beam_size_x.value() * 1e6f);
x.BeamSizeY_um(start.beam_size_y.value() * 1e6f);
}
if (!start.pixel_mask.empty())
dataset->pixel_mask = std::make_shared<const PixelMask>(start.pixel_mask.begin()->second);
else
dataset->pixel_mask = std::make_shared<const PixelMask>(
std::vector<uint32_t>(start.image_size_x * start.image_size_y));
return dataset;
}
void JFJochStartMessageReader::Open(const StartMessage &start, const std::string &master_path) {
@@ -114,7 +113,7 @@ void JFJochStartMessageReader::Open(const StartMessage &start, const std::string
ended_ = false;
lost_.clear();
}
SetStartMessage(Dataset(start, default_experiment));
SetStartMessage(DatasetFromStartMessage(start, default_experiment));
}
void JFJochStartMessageReader::FileClosed(uint64_t file_number, uint64_t image_count) {
+5
View File
@@ -19,6 +19,11 @@ public:
explicit LiveCollectionLost(const std::string &what) : std::runtime_error(what) {}
};
// The dataset as the start message describes it - the same quantities an HDF5 master states, taken
// from the message the writer wrote it from.
std::shared_ptr<JFJochReaderDataset> DatasetFromStartMessage(const StartMessage &start,
const DiffractionExperiment &defaults);
// Reads a collection while it is being written: the dataset comes from its start message, as the
// broker sends it, and the images straight from the data files - /entry/data/data in each - once the
// broker reports a file closed and renamed into place (FileClosed). A read of an image whose file has
+3 -1
View File
@@ -11,7 +11,9 @@ ADD_LIBRARY(JFJochReceiver STATIC
JFJochReceiverCurrentStatus.h
JFJochReceiverOutput.h
JFJochReceiverLite.h JFJochReceiverLite.cpp
JFJochReceiverRugnux.h JFJochReceiverRugnux.cpp
RugnuxLiveStatus.h RugnuxLiveStatus.cpp
JFJochReceiver.cpp
JFJochReceiver.h)
TARGET_LINK_LIBRARIES(JFJochReceiver ImagePusher JFJochImagePuller JFJochImageAnalysis JFJochAcquisitionDevice JFJochCommon JFJochHLSSimulation JFJochPreview)
TARGET_LINK_LIBRARIES(JFJochReceiver ImagePusher JFJochImagePuller JFJochImageAnalysis JFJochAcquisitionDevice JFJochCommon JFJochHLSSimulation JFJochPreview JFJochRugnux)
+1
View File
@@ -197,6 +197,7 @@ void JFJochReceiver::SendEndMessage() {
message.adu_histogram["module" + std::to_string(i)] = adu_histogram_module[i]->GetHistogram();
scan_result.FillEndMessage(message);
AddToEndMessage(message);
if (push_images_to_writer) {
if (!image_pusher.EndDataCollection(message))
+3
View File
@@ -96,6 +96,9 @@ protected:
void SendStartMessage();
void SendEndMessage();
void SaveStartMessageToImageBuffer(const StartMessage &msg);
// The last word on the end message before it is sent: a receiver whose analysis is not the
// per-image one fills in what it found.
virtual void AddToEndMessage(EndMessage &message) {}
virtual void Cancel(const JFJochException &e);
public:
JFJochReceiver(const DiffractionExperiment& experiment,
+482
View File
@@ -0,0 +1,482 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochReceiverRugnux.h"
#include <cmath>
#include <filesystem>
#include <sys/resource.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "spdlog/fmt/fmt.h"
#include "JFJochReceiverLite.h"
#include "../common/ParallelFor.h"
#include "../frame_serialize/CBORStream2Deserializer.h"
#include "../rugnux/RugnuxLive.h"
#include "../rugnux/ResultReport.h"
using namespace std::chrono_literals;
namespace {
int64_t SteadyNowNs() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch()).count();
}
double SecondsSince(int64_t ns) {
return static_cast<double>(SteadyNowNs() - ns) / 1e9;
}
// A run reachable for Cut while it exists: registered in `slot` (under `m`, cancelled at once where the
// cut came first) and taken off again before it is destroyed, an exception included.
class Reachable {
std::mutex &m;
Rugnux *&slot;
public:
Reachable(std::mutex &m, Rugnux *&slot, Rugnux &run, const bool &cut, const std::atomic<bool> &cancelled)
: m(m), slot(slot) {
std::unique_lock ul(m);
slot = &run;
if (cut || cancelled)
run.Cancel();
}
~Reachable() {
std::unique_lock ul(m);
slot = nullptr;
}
};
// What rugnux reports as it goes: its per-image results into the receiver's plots and scan
// result, which a later pass over the same image overwrites - so the canonical pass, the last to
// read every image, is what stays - and its steps into the log.
class LiveObserver : public RugnuxObserver {
JFJochReceiverPlots &plots;
ScanResultGenerator &scan_result;
const AzimuthalIntegrationMapping &mapping;
RugnuxLiveStatus &status;
Logger &logger;
public:
LiveObserver(JFJochReceiverPlots &plots, ScanResultGenerator &scan_result,
const AzimuthalIntegrationMapping &mapping, RugnuxLiveStatus &status, Logger &logger)
: plots(plots), scan_result(scan_result), mapping(mapping), status(status), logger(logger) {}
void OnImageProcessed(const DataMessage &msg) override {
plots.Add(msg, AzimuthalIntegrationProfile(mapping));
scan_result.Add(msg);
}
void OnProgress(uint64_t done, uint64_t total) override { status.Progress(done, total); }
void OnStep(const std::string &title) override {
status.Step(title);
logger.Info("rugnux: {}", title);
}
void OnNote(const std::string &text) override {
status.Note(text);
logger.Info("rugnux: {}", text);
}
};
}
JFJochReceiverRugnux::JFJochReceiverRugnux(const DiffractionExperiment &in_experiment,
const PixelMask &in_pixel_mask,
ImagePuller &in_image_puller,
ImagePusher &in_image_pusher,
Logger &in_logger,
int64_t in_forward_nthreads,
int in_rugnux_nthreads,
const std::string &in_model_path,
double in_deadline_s,
RugnuxLiveStatus &in_live_status,
const SpotFindingSettings &in_spot_finding_settings,
JFJochReceiverCurrentStatus &in_current_status,
JFJochReceiverPlots &in_plots,
ImageBuffer &in_image_buffer,
ZMQPreviewSocket *in_zmq_preview_socket,
ZMQMetadataSocket *in_zmq_metadata_socket)
: JFJochReceiver(in_experiment, in_image_buffer, in_image_pusher, in_current_status, in_plots,
in_spot_finding_settings, in_logger, in_pixel_mask, in_zmq_preview_socket,
in_zmq_metadata_socket, nullptr),
image_puller(in_image_puller),
reader(in_image_buffer),
forward_nthreads(JFJochReceiverLite::NumberOfDataAnalysisThreads(in_forward_nthreads, in_experiment)),
rugnux_nthreads(in_rugnux_nthreads),
model_path(in_model_path),
deadline_s(in_deadline_s),
live_status(in_live_status),
measurement_started(1),
forward_started(forward_nthreads) {
logger.Info("Starting {} forward threads; rugnux runs on {} threads while the images arrive",
forward_nthreads, rugnux_nthreads);
for (int i = 0; i < forward_nthreads; i++)
forward_futures.emplace_back(std::async(std::launch::async, &JFJochReceiverRugnux::ForwardThread, this, i));
measurement = std::async(std::launch::async, &JFJochReceiverRugnux::MeasurementThread, this);
forward_started.wait();
}
JFJochReceiverRugnux::~JFJochReceiverRugnux() {
cancelled = true;
reader.Ended();
{
std::unique_lock ul(rugnux_m);
if (rugnux != nullptr)
rugnux->Cancel();
if (probe != nullptr)
probe->Cancel();
}
try {
if (measurement.valid())
measurement.get();
} catch (const std::exception &e) {
logger.Error("ReceiverRugnux teardown failed: {}", e.what());
}
}
void JFJochReceiverRugnux::MeasurementThread() {
std::future<void> rugnux_done, probe_done;
try {
// Waiting for the start message, as JFJochReceiverLite::MeasurementThread.
std::optional<std::chrono::steady_clock::time_point> give_up_at;
auto msg = image_puller.PollImage();
while (!cancelled && (!msg.has_value() || !msg->cbor || !msg->cbor->start_message)) {
if (detector_finished && !give_up_at)
give_up_at = std::chrono::steady_clock::now() + StartMessageGrace;
if (give_up_at && (std::chrono::steady_clock::now() > *give_up_at)) {
logger.Error("Detector finished, but no start message arrived - abandoning measurement");
cancelled = true;
break;
}
msg = image_puller.PollImage();
}
if (cancelled) {
current_status.SetProgress({});
current_status.SetStatus(GetStatus());
measurement_started.count_down();
return;
}
Configure(msg->cbor->start_message.value());
image_buffer.StartMeasurement(experiment);
start_time = std::chrono::system_clock::now();
SendStartMessage();
// Rugnux reads the images back from the buffer, so it needs one slot per image: a slot taken
// again would hand it a later image. Without that the collection is only forwarded.
const auto slots = image_buffer.GetStatus().total_slots;
live_status.Start(experiment.GetImageNum());
if (slots < experiment.GetImageNum()) {
logger.Error("Image buffer holds {} images, the collection has {}: no rugnux analysis for it",
slots, experiment.GetImageNum());
live_status.Finish(RugnuxLiveState::Skipped, "the image buffer cannot hold the whole collection");
} else {
// The start message as the writer gets it, so rugnux sees the dataset an HDF5 master states.
std::vector<uint8_t> start_cbor;
image_buffer.GetStartMessage(start_cbor);
reader.Open(CBORStream2Deserialize(start_cbor)->start_message.value());
char dir_template[] = "/tmp/jfjoch_rugnux_XXXXXX";
if (mkdtemp(dir_template) == nullptr)
throw JFJochException(JFJochExceptionCategory::FileWriteError, "Cannot create rugnux scratch directory");
scratch_dir = dir_template;
rugnux_done = std::async(std::launch::async, &JFJochReceiverRugnux::RugnuxThread, this);
probe_done = std::async(std::launch::async, &JFJochReceiverRugnux::ProbeThread, this);
}
measurement_started.count_down();
} catch (const JFJochException &e) {
logger.ErrorException(e);
Cancel(e);
measurement_started.count_down();
throw;
}
logger.Info("Receiving started");
for (auto &f: forward_futures)
f.get();
reader.Ended();
images_done = true;
live_status.LastImage();
const int64_t last_image = last_image_ns.load();
logger.Info("Receiving finished, {} images", images_collected.load());
if (rugnux_done.valid()) {
// The fixed cutoff: the analysis gets deadline_s after the last image, then gives what it has.
if (deadline_s > 0 && rugnux_done.wait_for(std::chrono::duration<double>(deadline_s))
== std::future_status::timeout)
Cut("deadline");
rugnux_done.wait();
logger.Info("rugnux live: done {:.2f} s after the last image", SecondsSince(last_image));
}
if (probe_done.valid())
probe_done.wait();
current_status.SetProgress(1.0);
current_status.SetStatus(GetStatus());
SendEndMessage();
const double end_sent_s = SecondsSince(last_image);
if (!image_buffer.CheckIfBufferReturned(SendFinalizeTimeout)) {
logger.Error("Send commands not finalized in {} seconds", SendFinalizeTimeout.count());
throw JFJochException(JFJochExceptionCategory::ZeroMQ, "Send commands not finalized in "
+ std::to_string(SendFinalizeTimeout.count()) + " seconds");
}
if (push_images_to_writer)
writer_error = image_pusher.Finalize();
if (last_image > 0 && rugnux_done.valid()) {
const double written_s = SecondsSince(last_image);
live_status.Latency(written_s);
logger.Info("rugnux live: last image -> all written {:.2f} s (end message sent at {:.2f} s)",
written_s, end_sent_s);
}
if (!scratch_dir.empty()) {
std::error_code ec;
std::filesystem::remove_all(scratch_dir, ec);
}
end_time = std::chrono::system_clock::now();
current_status.SetProgress({});
current_status.SetStatus(GetStatus());
}
void JFJochReceiverRugnux::RugnuxThread() {
// Below the forward threads: every thread rugnux starts inherits this, so an image arriving finds a
// core however busy rugnux keeps the machine.
// The process-wide ParallelFor pool is not started by this thread, so it is lowered separately.
setpriority(PRIO_PROCESS, static_cast<id_t>(syscall(SYS_gettid)), 10);
parallel_detail::WorkerPool::Instance().SetNice(10);
try {
const auto dataset = reader.GetDataset();
const RugnuxLiveSetup setup = MakeRugnuxLiveSetup(*dataset, rugnux_nthreads, scratch_dir + "/p", model_path);
LiveObserver observer(plots, scan_result, *az_int_mapping, live_status, logger);
ProcessResult result;
{
Rugnux process(reader, setup.experiment, *dataset->pixel_mask, setup.config);
Reachable reachable(rugnux_m, rugnux, process, cut, cancelled);
result = process.Run(&observer);
}
// The verdict and summary of the report a `rugnux` run writes, without writing it.
const ReportDocument doc = BuildReportDocument(scratch_dir + "/p", experiment.GetFilePrefix(),
setup.experiment, result);
std::vector<std::string> warnings;
for (const auto &w: doc.warnings)
if (!w.developer_only)
warnings.push_back(w.text);
live_status.Result(doc.verdict, doc.verdict_text, doc.summary_rows, warnings);
live_status.Finish(result.cancelled ? RugnuxLiveState::Cancelled : RugnuxLiveState::Done);
std::unique_lock ul(rugnux_m);
rugnux_result = std::move(result);
} catch (const std::exception &e) {
logger.Error("rugnux live: {}", e.what());
live_status.Finish(RugnuxLiveState::Failed, e.what());
}
if (!parallel_detail::WorkerPool::Instance().SetNice(0))
logger.Warning("rugnux live: the ParallelFor pool stays at nice 10 - raising it back needs CAP_SYS_NICE");
}
void JFJochReceiverRugnux::ProbeThread() {
// Rugnux waits for half the sweep before it decides on a lattice. In the meantime the machine is
// idle, so a small run of its own indexes the first 60 degrees and says at once whether there is
// a crystal - quick feedback for the person at the beamline, above all where there is none. It
// decides nothing for the run, which goes on as it would without it.
constexpr double PROBE_DEG = 60.0;
setpriority(PRIO_PROCESS, static_cast<id_t>(syscall(SYS_gettid)), 10);
try {
const auto dataset = reader.GetDataset();
const auto gonio = dataset->experiment.GetGoniometer();
if (!gonio || gonio->GetIncrement_deg() == 0.0f)
return;
const int64_t frames = std::lround(PROBE_DEG / std::abs(gonio->GetIncrement_deg()));
if (2 * frames > static_cast<int64_t>(reader.GetNumberOfImages()))
return; // a sweep this short is decided on soon enough by the run itself
RugnuxLiveSetup setup = MakeRugnuxLiveSetup(*dataset, rugnux_nthreads, "", "");
setup.config.end_image = static_cast<int>(frames);
setup.config.rotation_postrefine_geometry = false;
setup.config.run_scaling = false;
ProcessResult r;
{
Rugnux probe_run(reader, setup.experiment, *dataset->pixel_mask, setup.config);
Reachable reachable(rugnux_m, probe, probe_run, cut, cancelled);
r = probe_run.Run(nullptr);
}
if (r.consensus_cell && r.rotation_lattice_type) {
const auto &c = *r.consensus_cell;
live_status.Probe(fmt::format("lattice on the first {:.0f} deg: {}-centred {}, {:.1f} {:.1f} {:.1f} "
"{:.1f} {:.1f} {:.1f}, {:.0f}% of the images indexed", PROBE_DEG,
r.rotation_lattice_type->centering,
gemmi::crystal_system_str(r.rotation_lattice_type->crystal_system),
c.a, c.b, c.c, c.alpha, c.beta, c.gamma,
100.0 * r.indexing_rate.value_or(0.0f)));
} else
live_status.Probe(fmt::format("no lattice on the first {:.0f} deg", PROBE_DEG));
} catch (const NoLatticeFound &e) {
live_status.Probe(fmt::format("no lattice on the first {:.0f} deg: {}", PROBE_DEG, e.reason));
} catch (const std::exception &e) {
logger.Warning("rugnux live: the lattice probe failed: {}", e.what());
}
logger.Info("rugnux live: lattice probe - {}", live_status.Get().probe);
}
void JFJochReceiverRugnux::AddToEndMessage(EndMessage &message) {
std::unique_lock ul(rugnux_m);
if (!rugnux_result)
return;
const EndMessage &r = rugnux_result->end_message;
message.indexing_rate = r.indexing_rate;
message.bkg_estimate = r.bkg_estimate;
message.spindle_blind_fraction = r.spindle_blind_fraction;
message.ice_ring_score_mean = r.ice_ring_score_mean;
message.az_int_result = r.az_int_result;
message.rotation_lattice = r.rotation_lattice;
message.rotation_lattice_type = r.rotation_lattice_type;
message.rotation_extra_lattices = r.rotation_extra_lattices;
message.unit_cell = r.unit_cell;
message.space_group_name = r.space_group_name;
message.space_group_number = r.space_group_number;
message.reindex_matrix = r.reindex_matrix;
message.refined_beam_center_x = r.refined_beam_center_x;
message.refined_beam_center_y = r.refined_beam_center_y;
message.refined_poni_rot1 = r.refined_poni_rot1;
message.refined_poni_rot2 = r.refined_poni_rot2;
message.refined_poni_rot3 = r.refined_poni_rot3;
message.refined_rotation_axis = r.refined_rotation_axis;
// TODO: merged data, map coefficients and placed model from scratch_dir (writer branch). Only here,
// with rugnux_result set: every file in scratch_dir is then whole - rugnux writes synchronously and a
// cut never stops a write half-way - while a run that threw may have left one half-written.
}
void JFJochReceiverRugnux::Configure(const StartMessage &msg) {
// As JFJochReceiverLite::Configure.
if ((experiment.GetXPixelsNum() != msg.image_size_x)
|| (experiment.GetYPixelsNum() != msg.image_size_y))
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in detector size");
if (fabs(experiment.GetPixelSize_mm() - msg.pixel_size_x * 1e3) > 1e-7)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in pixel size");
experiment.Detector().Description(msg.detector_description);
experiment.Detector().SerialNumber(msg.detector_serial_number);
experiment.Detector().SensorMaterial(msg.sensor_material);
experiment.Detector().SensorThickness_um(msg.sensor_thickness * 1e6);
experiment.Detector().SaturationLimit(SaturationLimitFromValue(msg.saturation_value));
experiment.Detector().CountRateCorrectionApplied(msg.countrate_correction_enabled);
experiment.Detector().CountRateCorrectionLookupTable(msg.countrate_correction_lookup_table);
experiment.Detector().FlatfieldApplied(msg.flatfield_enabled);
experiment.Detector().VirtualPixelInterpolationApplied(msg.virtual_pixel_interpolation_enabled);
experiment.ApplyPixelMask(false);
experiment.Detector().BitDepthImage(msg.bit_depth_image);
experiment.PixelSigned(msg.pixel_signed);
}
void JFJochReceiverRugnux::ForwardThread(uint32_t id) {
forward_started.count_down();
measurement_started.wait();
while (!cancelled && !end_message_received) {
try {
auto msg = image_puller.PollImage(std::chrono::milliseconds(5));
if (!msg.has_value() || !msg->cbor)
continue;
if (msg->cbor->end_message) {
end_message_received = true;
} else if (msg->cbor->calibration) {
if (push_images_to_writer)
image_pusher.SendCalibration(msg->cbor->calibration.value());
} else if (msg->cbor->data_message) {
last_image_ns = SteadyNowNs();
live_status.ImageReceived();
DataMessage data_msg = msg->cbor->data_message.value();
++images_collected;
const auto compressed_size_img = data_msg.image.GetCompressedSize();
const auto uncompressed_size_img = data_msg.image.GetUncompressedSize();
compressed_size += compressed_size_img;
uncompressed_size += uncompressed_size_img;
UpdateMaxImageReceived(data_msg.number);
data_msg.original_number = data_msg.number;
data_msg.user_data = experiment.GetImageAppendix();
data_msg.run_number = experiment.GetRunNumber();
data_msg.run_name = experiment.GetRunName();
data_msg.receiver_buf_available = image_buffer.GetAvailSlots();
data_msg.receiver_aq_dev_delay = image_puller.GetCurrentFifoUtilization();
if (compressed_size_img > 0)
data_msg.compression_ratio = static_cast<float>(uncompressed_size_img)
/ static_cast<float>(compressed_size_img);
auto loc = image_buffer.GetImageSlot();
if (loc == nullptr)
writer_queue_full = true;
else {
auto writer_buffer = (uint8_t *) loc->GetImage();
CBORStream2Serializer serializer(writer_buffer, experiment.GetImageBufferLocationSize());
serializer.SerializeImage(data_msg);
loc->SetImageNumber(data_msg.number);
loc->SetImageSize(serializer.GetBufferSize());
loc->SetIndexed(false);
loc->ReadyToSend();
if (zmq_preview_socket != nullptr)
zmq_preview_socket->SendImage(writer_buffer, serializer.GetBufferSize());
if (zmq_metadata_socket != nullptr)
zmq_metadata_socket->AddDataMessage(data_msg);
if (push_images_to_writer) {
if (image_pusher.SendImage(*loc))
++images_sent;
} else
loc->release();
UpdateMaxImageSent(data_msg.number);
}
}
UpdateMaxDelay(image_puller.GetCurrentFifoUtilization());
current_status.SetProgress(GetProgress());
current_status.SetStatus(GetStatus());
} catch (const JFJochException &e) {
logger.ErrorException(e);
Cancel(e);
}
}
}
void JFJochReceiverRugnux::Cancel(bool silent) {
detector_finished = true;
// The silent cancel is the detector reading idle at the end of every collection: rugnux goes on.
if (silent)
return;
// /cancel is the one way in for the beamline, which cannot tell the collection from its analysis: it
// ends the collection while the images still arrive, as JFJochReceiverLite does, and in every case
// cuts the analysis, so the end message goes out at once with what rugnux has established.
if (!images_done)
JFJochReceiver::Cancel(silent);
Cut("cancel");
}
void JFJochReceiverRugnux::Cut(const std::string &reason) {
std::unique_lock ul(rugnux_m);
if (cut || rugnux_result)
return;
cut = true;
reader.Ended(); // no image is waited for any more
if (rugnux != nullptr)
rugnux->Cancel();
if (probe != nullptr)
probe->Cancel();
live_status.Cut(reason);
logger.Info("rugnux live: analysis cut ({}) - the end message carries what is established", reason);
}
void JFJochReceiverRugnux::StopReceiver() {
if (measurement.valid()) {
measurement.get();
logger.Info("Receiver stopped");
}
}
float JFJochReceiverRugnux::GetEfficiency() const {
if (experiment.GetFrameNum() == 0)
return 0;
return static_cast<float>(images_collected) / static_cast<float>(experiment.GetFrameNum());
}
float JFJochReceiverRugnux::GetProgress() const {
if (experiment.GetFrameNum() == 0)
return 0.0;
return static_cast<float>(max_image_number_received) / static_cast<float>(experiment.GetFrameNum());
}
+87
View File
@@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <future>
#include <latch>
#include "JFJochReceiver.h"
#include "../image_puller/ImagePuller.h"
#include "../reader/JFJochImageBufferReader.h"
#include "../rugnux/Rugnux.h"
#include "RugnuxLiveStatus.h"
// A DECTRIS rotation collection forwarded to the writer as JFJochReceiverLite does, but analysed by
// rugnux while it arrives instead of image by image: the forward threads put each image into the
// image buffer and send it on, and one rugnux thread reads the collection back from that buffer. The
// end message waits for rugnux, and carries what it found - the lattice, refined geometry, per-image
// results and its merged data, map coefficients and placed model (see AddToEndMessage).
class JFJochReceiverRugnux : public JFJochReceiver {
using JFJochReceiver::Cancel;
ImagePuller &image_puller;
JFJochImageBufferReader reader;
std::atomic<bool> end_message_received{false};
std::atomic<bool> detector_finished{false}; // see JFJochReceiverLite
constexpr static auto StartMessageGrace = std::chrono::seconds(5);
const int64_t forward_nthreads;
const int rugnux_nthreads;
const std::string model_path;
const double deadline_s; // after the last image, the analysis is cut (0 = never)
std::string scratch_dir; // rugnux's own output, read back into the end message
RugnuxLiveStatus &live_status;
std::vector<std::future<void>> forward_futures;
std::future<void> measurement;
std::latch measurement_started;
std::latch forward_started;
std::mutex rugnux_m;
Rugnux *rugnux = nullptr; // while it runs, for Cut
Rugnux *probe = nullptr; // the same for the lattice probe
std::optional<ProcessResult> rugnux_result;
bool cut = false;
std::atomic<bool> images_done{false}; // the forward threads have ended
// The latency of the demonstration: from the last image in to every result in place.
std::atomic<int64_t> last_image_ns{0};
void MeasurementThread();
void ForwardThread(uint32_t id);
void RugnuxThread();
void ProbeThread();
void Configure(const StartMessage &message);
// Stops the analysis where it is: rugnux returns what it has established (see Rugnux::Cancel), and
// the end message goes out with that. Reason: "deadline" or "cancel".
void Cut(const std::string &reason);
void AddToEndMessage(EndMessage &message) override;
public:
JFJochReceiverRugnux(const DiffractionExperiment &experiment,
const PixelMask &pixel_mask,
ImagePuller &image_puller,
ImagePusher &image_pusher,
Logger &logger,
int64_t forward_nthreads,
int rugnux_nthreads,
const std::string &model_path,
double deadline_s,
RugnuxLiveStatus &live_status,
const SpotFindingSettings &spot_finding_settings,
JFJochReceiverCurrentStatus &current_status,
JFJochReceiverPlots &plots,
ImageBuffer &image_buffer,
ZMQPreviewSocket *zmq_preview_socket = nullptr,
ZMQMetadataSocket *zmq_metadata_socket = nullptr);
~JFJochReceiverRugnux() override;
void StopReceiver() override;
void Cancel(bool silent) override;
float GetEfficiency() const override;
float GetProgress() const override;
void SetSpotFindingSettings(const SpotFindingSettings &spot_finding_settings) override {}
};
+28
View File
@@ -2,6 +2,9 @@
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochReceiverService.h"
#include "JFJochReceiverRugnux.h"
#include "../common/ThreadAffinity.h"
#include "../rugnux/RugnuxDefaults.h"
#include "JFJochReceiverFPGA.h"
#include "JFJochReceiverLite.h"
#include "../preview/JFJochJPEG.h"
@@ -17,6 +20,23 @@ JFJochReceiverService::JFJochReceiverService(AcquisitionDeviceGroup &in_aq_devic
spot_finding_settings(DiffractionExperiment::DefaultDataProcessingSettings()) {
}
JFJochReceiverService &JFJochReceiverService::RugnuxLive(bool enable, const std::string &model_path,
int reserved_cpus, double deadline_s) {
std::unique_lock ul_state(state_mutex);
if (state != ReceiverState::Idle)
throw JFJochException(JFJochExceptionCategory::WrongDAQState,
"Cannot change rugnux settings during data collection");
rugnux_enable = enable;
rugnux_model_path = model_path;
rugnux_reserved_cpus = reserved_cpus;
rugnux_deadline_s = deadline_s;
return *this;
}
RugnuxLiveSnapshot JFJochReceiverService::GetRugnuxStatus() const {
return rugnux_status.Get();
}
JFJochReceiverService &JFJochReceiverService::NumThreads(int64_t input) {
if (input <= 0)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Thread number must be above zero");
@@ -103,6 +123,14 @@ void JFJochReceiverService::Start(const DiffractionExperiment &experiment,
image_puller = std::make_shared<ZMQImagePuller>(
experiment.GetDetectorSetup().GetDECTRISStream2Addr());
}
if (rugnux_enable && RugnuxRotationData(experiment)) {
receiver = std::make_unique<JFJochReceiverRugnux>(
experiment, pixel_mask, *image_puller, image_pusher, logger, nthreads_local,
std::max<int>(1, static_cast<int>(AvailableCpus()) - rugnux_reserved_cpus),
rugnux_model_path, rugnux_deadline_s, rugnux_status, spot_finding_settings, receiver_status, plots, image_buffer,
zmq_preview_socket.get(), zmq_metadata_socket.get());
break;
}
receiver = std::make_unique<JFJochReceiverLite>(experiment,
pixel_mask,
*image_puller,
+13
View File
@@ -3,6 +3,8 @@
#pragma once
#include "RugnuxLiveStatus.h"
#include <mutex>
#include <memory>
@@ -21,6 +23,14 @@ class JFJochReceiverService {
std::shared_ptr<ImagePuller> image_puller;
int64_t nthreads = 8;
// Rotation collections from a DECTRIS detector analysed by rugnux while they arrive
// (JFJochReceiverRugnux), instead of image by image. Off unless asked for.
bool rugnux_enable = false;
std::string rugnux_model_path;
int rugnux_reserved_cpus = 4; // left to the forward threads and the pusher
double rugnux_deadline_s = 0; // the analysis is cut this long after the last image (0 = never)
RugnuxLiveStatus rugnux_status; // of the last collection, kept after it ends
enum class ReceiverState {Idle, Running};
ReceiverState state = ReceiverState::Idle;
mutable std::mutex state_mutex;
@@ -43,6 +53,9 @@ public:
ImagePusher &pusher,
size_t send_buffer_size_MiB = 512);
JFJochReceiverService& NumThreads(int64_t input);
JFJochReceiverService& RugnuxLive(bool enable, const std::string &model_path, int reserved_cpus,
double deadline_s);
RugnuxLiveSnapshot GetRugnuxStatus() const;
JFJochReceiverService& PreviewSocket(const std::string &addr, const std::optional<int32_t> &watermark = {});
JFJochReceiverService& MetadataSocket(const std::string &addr);
+95
View File
@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "RugnuxLiveStatus.h"
double RugnuxLiveStatus::Now() const {
return std::chrono::duration<double>(std::chrono::steady_clock::now() - start).count();
}
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::unique_lock ul(m);
s = RugnuxLiveSnapshot{};
s.state = RugnuxLiveState::Running;
s.images_expected = images_expected;
start = std::chrono::steady_clock::now();
}
void RugnuxLiveStatus::ImageReceived() {
std::unique_lock ul(m);
++s.images_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) {
std::unique_lock ul(m);
s.verdict = verdict;
s.verdict_text = verdict_text;
s.summary = summary;
s.warnings = warnings;
}
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;
}
void RugnuxLiveStatus::LastImage() {
std::unique_lock ul(m);
s.last_image_s = Now();
}
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();
}
RugnuxLiveSnapshot RugnuxLiveStatus::Get() const {
std::unique_lock ul(m);
RugnuxLiveSnapshot ret = s;
if (s.state == RugnuxLiveState::Running)
ret.elapsed_s = Now();
return ret;
}
+71
View File
@@ -0,0 +1,71 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <chrono>
#include <mutex>
#include <optional>
#include <string>
#include <utility>
#include <vector>
// Where the live rugnux run of the last collection is, for the broker's status route: the steps it has
// taken with what each found ("lattice found", "space group P 21 21 21"), the step it is on and how far
// into its images, and - once it is over - its verdict and summary as the report states them, and the
// latency from the last image to every result written.
enum class RugnuxLiveState { Idle, Running, Done, Failed, Cancelled, Skipped };
struct RugnuxLiveStep {
std::string title;
double started_s = 0.0; // since the collection started
std::optional<double> duration_s; // set once the next step starts or the run ends
std::vector<std::string> notes;
};
struct RugnuxLiveSnapshot {
RugnuxLiveState state = RugnuxLiveState::Idle;
std::string message; // why it failed or was skipped
int64_t images_expected = 0;
int64_t images_received = 0;
double elapsed_s = 0.0;
std::optional<float> step_progress; // fraction of the current pass's images
std::vector<RugnuxLiveStep> steps;
// The lattice probe on the first 60 degrees (JFJochReceiverRugnux::ProbeThread): an early answer
// to "is there a crystal", seconds into the collection; the run's own answer comes later.
std::string probe;
std::optional<double> probe_s; // when it answered, since the collection started
std::optional<double> last_image_s; // when the last image arrived, since the collection started
// The analysis was cut - "deadline" (the broker's deadline_s after the last image) or "cancel" - at
// cut_s since the collection started; what it had established went out, the steps after it were left out.
std::string cut;
std::optional<double> cut_s;
std::optional<double> last_image_to_written_s;
std::string verdict; // OK | WARNINGS | UNUSABLE | FAILED
std::string verdict_text;
std::vector<std::pair<std::string, std::string>> summary;
std::vector<std::string> warnings;
};
class RugnuxLiveStatus {
mutable std::mutex m;
RugnuxLiveSnapshot s;
std::chrono::steady_clock::time_point start;
double Now() const;
void CloseStep();
public:
void Start(int64_t images_expected);
void ImageReceived();
void Step(const std::string &title);
void Note(const std::string &text);
void Progress(uint64_t done, uint64_t total);
void Finish(RugnuxLiveState state, const std::string &message = "");
void 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);
void Latency(double last_image_to_written_s);
void LastImage();
void Cut(const std::string &reason);
void Probe(const std::string &text);
RugnuxLiveSnapshot Get() const;
};
+2
View File
@@ -17,6 +17,8 @@ ADD_LIBRARY(JFJochRugnux STATIC
RugnuxScaleMerge.cpp
RugnuxDefaults.cpp
RugnuxDefaults.h
RugnuxLive.cpp
RugnuxLive.h
Rugnux.h
RugnuxCommandLine.cpp
RugnuxCommandLine.h
+13 -4
View File
@@ -419,11 +419,11 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
result = RunAllPasses(observer);
} catch (const NoLatticeFound &) {
// A run that found no lattice after deciding a near tie early is made again deciding late.
if (!near_tie_rechecks_->carried)
if (!near_tie_rechecks_->carried || config_.fast_live)
throw;
lost = true;
}
if (lost || (!result.cancelled && NearTieOverturned())) {
if (lost || (!result.cancelled && !config_.fast_live && NearTieOverturned())) {
JoinBeamCenterCapture();
Logger logger("Rugnux");
logger.Info("A near tie decided early ends elsewhere on the rest of the sweep - the run is made again, "
@@ -431,6 +431,12 @@ ProcessResult Rugnux::Run(RugnuxObserver *observer) {
result = again->RunAllPasses(observer);
run = again.get();
}
// Cut short in fast_live: a pass whose images were not all read has no lattice and no merge, so the
// run's answer is the last pass that ran to its end.
if (config_.fast_live && result.cancelled && established_) {
result = *established_;
result.cancelled = true;
}
result.total_time_s = std::chrono::duration<double>(std::chrono::steady_clock::now() - run_start).count();
// For the report's beam-centre warning: what the background measured, and what this crystal asks
// of the centre - at the file's geometry, as the pre-scan's check asks it, but on the cell the run
@@ -707,7 +713,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
// skip the search that is the whole point of running again.
// The run as this pass finds it, for the re-check of a near tie its first pass may decide early.
std::shared_ptr<Rugnux> recheck;
if (!decide_late_ && config_.mode == ProcessMode::FullAnalysis && config_.rotation_indexing
if (!decide_late_ && !config_.fast_live && config_.mode == ProcessMode::FullAnalysis && config_.rotation_indexing
&& config_.two_pass_rotation && !force_rotation_result_.has_value()
&& !config_.forced_rotation_lattice.has_value() && experiment_.IsRotationIndexing())
recheck = std::make_shared<Rugnux>(*this);
@@ -1072,7 +1078,9 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
image_pass_mark.Stop();
// Scaling and merging (full analysis only).
if (full && !cancelled_ && (result.indexing_rate.value_or(0.0f) > 0 || result.rotation_lattice_found)
// As ImagePass's stopped(): in fast_live a pass that read all its images merges whatever comes after.
const bool images_cut = config_.fast_live ? result.cancelled : static_cast<bool>(cancelled_);
if (full && !images_cut && (result.indexing_rate.value_or(0.0f) > 0 || result.rotation_lattice_found)
&& (config_.run_scaling || !config_.reference_data.empty())) {
TimingMark mark(logger, "scale, merge and symmetry");
std::optional<GPUMerge> gpu_merge; // what the speculative probe waits for on a small card
@@ -1190,6 +1198,7 @@ ProcessResult Rugnux::RunPipeline(RugnuxObserver *observer, bool write_output, b
writer->Finalize();
result.written_master_path = config_.output_prefix + "_process.h5";
}
result.end_message = end_msg;
output_mark.Stop();
+15
View File
@@ -110,6 +110,13 @@ struct ProcessConfig {
// are decided (Rugnux::decide_late_).
bool live = false;
// Live in the broker, where the run has to end a fixed time after the last frame: every decision
// stands where it was first taken, and nothing the run would otherwise do again once the sweep is
// in - the near-tie re-checks and the run made again, the rotation-scale walk, the metric-symmetry
// arms, the geometry walk, the supercell and quality guards, the doubled cell - is done. A
// difficult crystal can come out worse; an easy one comes out the same, sooner.
bool fast_live = false;
// Rotation indexing (FullAnalysis)
bool rotation_indexing = false;
bool two_pass_rotation = true;
@@ -326,6 +333,9 @@ struct ProcessResult {
// beam-centre pre-scan that runs before the first one. processing_time_s is the last pass alone
// and so understates a two-pass run by more than a factor of two.
double total_time_s = 0.0;
// The end message of this pass, as the _process.h5 records it: the lattice, refined geometry,
// reindexing and the run means - what the broker puts into its own when it runs rugnux live.
EndMessage end_message;
// Workers the per-image loop actually ran, which is not config.nthreads once the loop is capped.
int image_loop_threads = 0;
double frame_rate_hz = 0.0;
@@ -813,6 +823,9 @@ class Rugnux {
CancelFlag Own() const { CancelFlag f; f.run = flag; return f; }
};
CancelFlag cancelled_;
// fast_live: the last pass that ran to its end. Where a cancel cuts the pass after it short, Run()
// returns this one, so the broker sends what is established (lattice, geometry) rather than nothing.
std::optional<ProcessResult> established_;
// Where the steps and notes of the pass now running go: the observer on a pass that makes the
// result, nullptr on a probe pass and on a copy run beside the main one. Set by RunPipeline.
@@ -1357,6 +1370,8 @@ public:
const PowderRings &GetPowderRings() const { return powder_; }
// Request cancellation; safe to call from any thread (the worker loop checks between images).
// fast_live makes it a cut: a pass that is past its images still merges and writes its files,
// only the model is left out, and a pass cut in its images gives way to the last complete one.
void Cancel() { cancelled_ = true; }
bool IsCancelled() const { return cancelled_; }
};
+10 -6
View File
@@ -409,6 +409,10 @@ void Rugnux::ImagePass(PipelineLocals &p) {
std::chrono::steady_clock::now() - image_loop_start).count();
result.cancelled = cancelled_;
// Whether the pass stops here. In fast_live a cut that comes after the last image read lets the pass
// finish - its lattice, its merge and its files are what the run has established - and only the model
// after them is left out (ScaleMergeAndSymmetry).
const auto stopped = [&] { return config_.fast_live ? result.cancelled : static_cast<bool>(cancelled_); };
result.images_processed = finished_count.load();
Note(fmt::format("{} images, {:.0f} per second", result.images_processed,
result.images_processed / std::max(1e-6, result.image_loop_time_s)));
@@ -424,7 +428,7 @@ void Rugnux::ImagePass(PipelineLocals &p) {
// The footprint of the reflection rather than of the spot: the pre-scan's widths plus where the
// spots sit against their predictions (SpotFootprint.h). Like the widths it acts only where the
// reflection outgrows the r1 disk, and like them it is held back for the canonical pass.
if (measure_offsets && !cancelled_) {
if (measure_offsets && !stopped()) {
const SpotFootprint fp = FootprintWithOffsets(*prescan_footprint_, footprint_offsets);
const BraggIntegrationSettings canonical = bragg_adaptive_.value_or(experiment_.GetBraggIntegrationSettings());
bool outgrows = false;
@@ -446,7 +450,7 @@ void Rugnux::ImagePass(PipelineLocals &p) {
}
}
if (full && indexer && experiment_.IsRotationIndexing() && !cancelled_) {
if (full && indexer && experiment_.IsRotationIndexing() && !stopped()) {
const SupercellProbe probe = indexer->GetSupercellProbe();
if (probe[0][0].n > 0 && probe[0][1].n > 0) {
std::string table;
@@ -594,7 +598,7 @@ void Rugnux::ImagePass(PipelineLocals &p) {
predicted += p_i;
crowded += c_i;
}
if (predicted > 0 && !cancelled_) {
if (predicted > 0 && !stopped()) {
const double f = static_cast<double>(crowded) / static_cast<double>(predicted);
const double n = static_cast<double>(crowding_frames.size());
double ss = 0.0;
@@ -611,7 +615,7 @@ void Rugnux::ImagePass(PipelineLocals &p) {
// Every image failing is a total failure, not a run that produced nothing: it used to be reported
// only as per-image log lines while the process still exited 0 with no output file.
if (!cancelled_ && images_to_process > 0 && result.images_processed == 0)
if (!stopped() && images_to_process > 0 && result.images_processed == 0)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"No image could be analyzed - see the per-image errors above");
@@ -621,7 +625,7 @@ void Rugnux::ImagePass(PipelineLocals &p) {
// Powder calibration: the whole run has now been accumulated - the summed (q x azimuth) profile in
// plots, or the pooled spot list - so fit the detector geometry to it. The caller reports it and
// writes the PONI file; nothing here is stored in the _process.h5.
if (calibration && !cancelled_)
if (calibration && !stopped())
FitCalibration(p, calibration_spot_list);
// End message (also written to the file).
@@ -642,7 +646,7 @@ void Rugnux::ImagePass(PipelineLocals &p) {
end_msg.az_int_result["dataset"] = plots.GetAzIntProfile();
end_msg.indexing_rate = result.indexing_rate;
if (full && !cancelled_) {
if (full && !stopped()) {
const auto rot = indexer->FinalizeRotationIndexing();
indexer->RethrowIndexerError();
if (rot.has_value()) {
+57
View File
@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "RugnuxLive.h"
#include "RugnuxDefaults.h"
RugnuxLiveSetup MakeRugnuxLiveSetup(const JFJochReaderDataset &dataset, int nthreads,
const std::string &output_prefix, const std::string &model_path) {
RugnuxLiveSetup s;
// The experiment as rugnux_cli.cpp sets it up for a rotation sweep with no options given.
DiffractionExperiment &x = s.experiment;
x = dataset.experiment;
ApplyRugnuxExperimentDefaults(x);
x.NumTriggers(1);
ClearStoredCrystal(x);
x.MaxSpotCount(RUGNUX_MAX_SPOT_COUNT);
IndexingSettings indexing;
indexing.Algorithm(IndexingAlgorithmEnum::Auto);
indexing.RotationIndexing(true);
indexing.GeomRefinementAlgorithm(GeomRefinementAlgorithmEnum::BeamCenter);
indexing.IndexIceRings(false);
x.ImportIndexingSettings(indexing);
x.DetectIceRings(RugnuxDetectIceRings(dataset.file_detect_ice_rings, true));
ScalingSettings scaling = RugnuxDefaultScalingSettings(true);
scaling.ScaleFulls(true);
scaling.RelativeBDegrees(0.0);
scaling.MergeFriedel(true);
scaling.MinPartiality(0.02);
scaling.MinCCForImage(0.0);
x.ImportScalingSettings(scaling);
ProcessConfig &c = s.config;
c.mode = ProcessMode::FullAnalysis;
c.nthreads = nthreads;
c.output_prefix = output_prefix;
c.write_process_h5 = false;
c.export_unmerged = false;
c.spot_finding = RugnuxDefaultSpotFinding(true);
c.rotation_indexing = true;
c.two_pass_rotation = true;
c.rotation_postrefine_geometry = true;
c.rotation_indexing_image_count = RUGNUX_ROTATION_INDEXING_IMAGES;
c.detect_beam_stop = RUGNUX_DETECT_BEAM_STOP_FRAMES;
c.beam_center_check = true;
c.measure_spot_budget = true;
c.run_scaling = true;
c.model_path = model_path;
c.live = true;
c.fast_live = true;
// Decided late, on the whole sweep, as it re-checks its radius there - which fast_live does not.
c.adaptive_integration_radius = false;
return s;
}
+21
View File
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2026 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#pragma once
#include <string>
#include "Rugnux.h"
#include "../reader/JFJochReaderDataset.h"
// A rotation collection processed inside the broker while its frames arrive: what a bare `rugnux` on
// the same file would set, with the run made fast_live. The output goes to output_prefix - a place
// private to the broker, from which it builds the END message - and the model, where one is given,
// is placed and validated as `rugnux --model` does.
struct RugnuxLiveSetup {
DiffractionExperiment experiment;
ProcessConfig config;
};
RugnuxLiveSetup MakeRugnuxLiveSetup(const JFJochReaderDataset &dataset, int nthreads,
const std::string &output_prefix, const std::string &model_path);
+13 -10
View File
@@ -387,6 +387,8 @@ ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) {
pass1.pass_number = 1;
pass1.pass_count = 2;
if (cancelled_) { config_.output_prefix = base_prefix; return pass1; }
if (config_.fast_live)
established_ = pass1;
// Beam centre as a LATTICE hypothesis, decided on the merge. The first pass indexed both
// centres, found the same cell with a different metric symmetry read off it, and measured that
@@ -658,7 +660,7 @@ ProcessResult Rugnux::RunAllPasses(RugnuxObserver *observer) {
// pass off and throws it away, so the walk runs first, on its own, and the refined passes
// after it. Either way the run ends where the sequential walk leaves it.
constexpr double ROTATION_SCALE_STEP = 0.01;
if (!cancelled_ && gonio_snapshot && pass1.post_refine && !config_.rotation_scale) {
if (!cancelled_ && !config_.fast_live && gonio_snapshot && pass1.post_refine && !config_.rotation_scale) {
const auto check = std::make_shared<RotationScaleCheck>();
RotationScaleWalk walk;
if (std::fabs(pass1.post_refine->rotation_scale - 1.0) >= ROTATION_SCALE_STEP) {
@@ -940,7 +942,8 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
// Let every refined pass from here on ask the quality guard's question of itself, so one that
// is going to be thrown away can skip the report and the files it would have superseded.
quality_guard_pass1_ = pass1;
if (!config_.fast_live)
quality_guard_pass1_ = pass1;
config_.output_prefix = base_prefix; // the refined pass is the canonical result (no _02 suffix)
// The canonical pass measures the post-refinement again where the pre-pass moved the geometry
// (it applies nothing), so that the walk below has something to decide on.
@@ -997,7 +1000,7 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
double split = 0.0;
std::optional<ProcessResult> demoted_arm; // run where the data resolve the equality
std::shared_ptr<Rugnux> demoted_recheck; // the demoted arm again on all the frames, for a near tie
if (!cancelled_ && (length_arm || angle_arm) && pass1.rotation_lattice_type) {
if (!cancelled_ && !config_.fast_live && (length_arm || angle_arm) && pass1.rotation_lattice_type) {
split = length_arm ? *pass1.lattice_length_split : *pass1.lattice_angle_departure_deg * PI / 180.0;
const double dx = std::max(experiment_.GetBeamX_pxl(),
static_cast<float>(experiment_.GetXPixelsNum()) - experiment_.GetBeamX_pxl());
@@ -1101,8 +1104,8 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
logger.Info("Rotation two-pass geometry post-refinement: second pass (refined geometry, canonical) -> {}_*",
base_prefix);
speculate_geometry_probe_ = true; // a walk that follows starts with an indexing probe
rerun_if_starved_ = true;
speculate_geometry_probe_ = !config_.fast_live; // a walk that follows starts with an indexing probe
rerun_if_starved_ = !config_.fast_live;
const DiffractionExperiment before_pass2 = experiment_;
auto pass2 = RunPipeline(observer, /*write_output=*/true, /*geometry_prepass=*/false);
if (radius_recheck_) {
@@ -1296,7 +1299,7 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
// from here, not from where its own probe left the run.
DiffractionExperiment last_round_start = experiment_;
bool walk_shown = false;
while (!cancelled_ && prepass_detector_geometry_
while (!cancelled_ && !config_.fast_live && prepass_detector_geometry_
&& pass2.post_refine && pass2.post_refine->detector_refined) {
if (!walk_shown) {
step("Geometry check");
@@ -1437,7 +1440,7 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
// RunPipeline) - so each is followed by it. After the first re-run the radius is the fixed one
// and this never fires again.
const auto rerun_if_starved = [&](ProcessResult &pass, const std::string decision) {
if (cancelled_ || !bragg_adaptive_ || !bkg_starved_fraction_
if (cancelled_ || config_.fast_live || !bragg_adaptive_ || !bkg_starved_fraction_
|| *bkg_starved_fraction_ <= spot_width::BKG_STARVED_MAX_FRACTION)
return;
logger.Info("Two-pass: at the widened integration radius r1={:.1f} the neighbouring "
@@ -1487,7 +1490,7 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
};
const double v1 = primitive_volume(*pass1.consensus_cell, pass1.consensus_centering);
const double v2 = primitive_volume(*pass2.consensus_cell, pass2.consensus_centering);
if (v1 > 0.0 && v2 > 1.5 * v1 && !supercell_settled_) {
if (v1 > 0.0 && v2 > 1.5 * v1 && !supercell_settled_ && !config_.fast_live) {
logger.Info("Two-pass: second-pass primitive cell volume {:.0f} A^3 is {:.2f}x pass-1's "
"({:.0f} A^3) - a spurious supercell; re-running the second pass with "
"pass-1's result forced",
@@ -1519,7 +1522,7 @@ std::optional<ProcessResult> Rugnux::RefinedPasses(RugnuxObserver *observer, Pro
// but it is not guaranteed to be, and the guard that decides is RefinedPassIsWorse above. It
// has already been asked once, inside the pass itself, which is how a pass that is about to be
// thrown away knows not to write a report; this is where the answer is acted on.
if (!cancelled_ && pass1.has_merge_statistics && pass2.has_merge_statistics) {
if (!cancelled_ && !config_.fast_live && pass1.has_merge_statistics && pass2.has_merge_statistics) {
logger.Info("Two-pass quality guard: the refined pass holds {} reflections at I/sigma >= 2 and "
"{} at <= -2 of {} in a {:.0f} A^3 primitive cell, the header-geometry pass {} and {} "
"of {} in {:.0f} A^3; low-order axial {} against {}",
@@ -1800,7 +1803,7 @@ namespace {
ProcessResult Rugnux::DoubledCellHypothesis(RugnuxObserver *observer, ProcessResult half,
const std::string &base_prefix) {
if (cancelled_ || user_cell_ || user_fixed_sg_ || !half.supercell || !last_rotation_result_
if (cancelled_ || config_.fast_live || user_cell_ || user_fixed_sg_ || !half.supercell || !last_rotation_result_
|| !half.has_merge_statistics || !half.consensus_cell || !DoubledCellWorthMerging(*half.supercell))
return half;
Logger logger("Rugnux");
+10 -2
View File
@@ -3992,8 +3992,16 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) {
gemmi::SpaceGroup space_group;
};
std::optional<PlacedModelFile> placed_model_out;
// fast_live, cut (the broker's deadline or a cancel): the model is left out, and the files are written
// as a run without one writes them. Asked again before the search below.
const auto model_cut = [&] {
if (!config_.fast_live || !cancelled_)
return false;
Note("cut: the model is left out");
return true;
};
const bool model_run = result.consensus_cell && write_files && !config_.model_path.empty() && !geometry_prepass
&& !superseded;
&& !superseded && !model_cut();
// The twin hypothesis the model is asked about: its own subgroup's merge where it has one, the data's
// otherwise - and whether that merge was made only for the model, so is written only where it chooses it.
std::optional<TwinFile> &model_twin = model_twin_merge ? model_twin_file : twin_file;
@@ -4307,7 +4315,7 @@ bool Rugnux::ScaleMergeAndSymmetry(PipelineLocals &p) {
// veto - and nothing else, never an indexing: a different group or the subgroup it chooses goes to the
// files of its own, <prefix>_mr, from the merge it was chosen on. It never makes the rigid body's
// verdict (MODEL_FIT) either; its own null is what placed it.
if (model_run && (model_predicted || config_.force_mr || !placed_by_rigid_body)) {
if (model_run && (model_predicted || config_.force_mr || !placed_by_rigid_body) && !model_cut()) {
phase("Molecular replacement");
Step("Molecular replacement");
TimingMark mark(logger, "model prior");
+9
View File
@@ -318,6 +318,7 @@ enum {
OPT_WRITE_PROCESS_H5,
OPT_FINALIST_LEDGER,
OPT_SIMULATE_LIVE,
OPT_FAST_LIVE,
OPT_DEVELOPER,
OPT_NO_GPU,
OPT_FORCE_STILL,
@@ -376,6 +377,7 @@ static option long_options[] = {
{"write-process-h5", no_argument, nullptr, OPT_WRITE_PROCESS_H5},
{"finalist-ledger", no_argument, nullptr, OPT_FINALIST_LEDGER},
{"simulate-live", required_argument, nullptr, OPT_SIMULATE_LIVE},
{"fast-live", no_argument, nullptr, OPT_FAST_LIVE}, // undocumented: the broker's live run (ProcessConfig::fast_live)
{"developer", no_argument, nullptr, OPT_DEVELOPER},
{"no-gpu", no_argument, nullptr, OPT_NO_GPU},
{"smooth-g", optional_argument, nullptr, OPT_SMOOTH_G},
@@ -942,6 +944,7 @@ static int RunRugnux(int argc, char **argv, bool no_gpu) {
std::optional<bool> scale_fulls_arg; // --scale-fulls / --no-scale-fulls; default on for rot3d
bool write_process_h5_flag = false; // --write-process-h5; also write _process.h5 when merging
bool simulate_live = false; // --simulate-live
bool fast_live = false; // --fast-live, with --simulate-live
bool finalist_ledger_flag = false; // --finalist-ledger; report-only symmetry evidence table
std::optional<bool> detect_ice_rings; // --detect-ice-rings[=on|off]; unset => use the dataset (file) value
bool index_ice_rings = false; // --index-ice-rings[=on|off]; index on the ice-band spots too
@@ -1367,6 +1370,9 @@ static int RunRugnux(int argc, char **argv, bool no_gpu) {
case OPT_FINALIST_LEDGER:
finalist_ledger_flag = true;
break;
case OPT_FAST_LIVE:
fast_live = true;
break;
case OPT_SIMULATE_LIVE:
simulate_live = true;
simulated_frame_time = std::chrono::microseconds(
@@ -3136,6 +3142,9 @@ static int RunRugnux(int argc, char **argv, bool no_gpu) {
config.write_process_h5 = run_scaling ? write_process_h5_flag : true;
config.finalist_ledger = finalist_ledger_flag;
config.live = broker_feed || simulate_live;
config.fast_live = fast_live && config.live;
if (config.fast_live)
config.adaptive_integration_radius = false; // as MakeRugnuxLiveSetup
if (simulate_live)
logger.Info("Simulated live collection: image i readable (i+1) x {:.1f} ms after the run starts",
simulated_frame_time.count() / 1000.0);