broker: initialize and start clear the last collection's live rugnux status

/rugnux/status kept answering with the previous collection's rugnux run after POST /initialize,
and also after a /start whose collection does not run rugnux - the latter under the new run's
tokens. RugnuxLiveStatus gets a Clear(), called from JFJochReceiverService::ClearResults(), which
/start already uses to drop the previous run's status, plots and buffered images before the tokens
change. Initialize now calls ClearResults() instead of ClearImageBuffer(), so a fresh start also
drops the previous run's receiver status and plots.

Authentication: every rugnux endpoint (GET/PUT /config/rugnux, PUT/DELETE /config/rugnux/model,
GET /rugnux/status) was already in the broker's bearer-protected set and carries bearerAuth in
jfjoch_api.yaml; the HTTP auth test now checks all five.

The yaml descriptions of /initialize and rugnux_live_status say so; the C++ model and frontend
client are regenerated (comments only).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXc1zD1yxHqaR1DGdCTHL4
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-20 01:00:54 +02:00
1 parent 48c14b6d1b
commit 02e5bc387f
14 files changed
+58 -15

No files matched your search

+1 -1
View File
@@ -80,7 +80,7 @@ public:
bool GetImageFromBuffer(std::vector<uint8_t> &v, int64_t image_number = -1);
ImageBufferStatus GetImageBufferStatus() const;
void ClearImageBuffer() const;
void ClearResults() const; // previous run's status, plots and buffered images
void ClearResults() const; // previous run's status, plots, live rugnux status and buffered images
void LoadDetectorPixelMask(PixelMask &mask);
+3 -3
View File
@@ -329,9 +329,9 @@ void JFJochStateMachine::Initialize() {
ResetError(); // Clear error, we don't care what was it
start_exception = nullptr; // Re-initialising discards a pending start failure
// A fresh start: no images from before, and no live rugnux analysis or model left over from the
// previous user.
services.ClearImageBuffer();
// A fresh start: no images, plots or live rugnux status from before, and no live rugnux analysis
// or model left over from the previous user.
services.ClearResults();
{
std::unique_lock ul2(rugnux_mutex);
RugnuxLiveSettings s = services.GetRugnuxLive();
+2 -2
View File
@@ -12,7 +12,7 @@
/*
* Rugnux_live_status.h
*
* Live rugnux processing of the last collection; kept until the next collection starts. Times are taken when the image puller received a message, and are in seconds since the first image (before it arrives, since now). The frame rate is &#x60;(images_received - 1) / last_image_s&#x60;.
* Live rugnux processing of the last collection; kept until the next collection starts or &#x60;POST /initialize&#x60;. Times are taken when the image puller received a message, and are in seconds since the first image (before it arrives, since now). The frame rate is &#x60;(images_received - 1) / last_image_s&#x60;.
*/
#ifndef Rugnux_live_status_H_
@@ -29,7 +29,7 @@ namespace org::openapitools::server::model
{
/// <summary>
/// Live rugnux processing of the last collection; kept until the next collection starts. Times are taken when the image puller received a message, and are in seconds since the first image (before it arrives, since now). The frame rate is &#x60;(images_received - 1) / last_image_s&#x60;.
/// Live rugnux processing of the last collection; kept until the next collection starts or &#x60;POST /initialize&#x60;. Times are taken when the image puller received a message, and are in seconds since the first image (before it arrives, since now). The frame rate is &#x60;(images_received - 1) / last_image_s&#x60;.
/// </summary>
class Rugnux_live_status
{
+2 -2
View File
@@ -2770,7 +2770,7 @@ components:
rugnux_live_status:
type: object
description: |
Live rugnux processing of the last collection; kept until the next collection starts.
Live rugnux processing of the last collection; kept until the next collection starts or `POST /initialize`.
Times are taken when the image puller received a message, and are in seconds since the first image
(before it arrives, since now). The frame rate is `(images_received - 1) / last_image_s`.
required:
@@ -2917,7 +2917,7 @@ paths:
This operation will reconfigure network interface of the detector.
During operation of the detector it is recommended to use the `POST /pedestal` operation instead.
If storage cells are used, the execution time might be few minutes.
It also clears the image buffer, disables live rugnux analysis and removes the rugnux model.
It also clears the image buffer, the previous collection's plots and live rugnux status, disables live rugnux analysis and removes the rugnux model.
This is async function - one needs to use `POST /wait_till_done` to ensure operation is done.
responses:
@@ -16,7 +16,7 @@ import type { deleteConfigRugnuxModelData, deleteConfigRugnuxModelError, getConf
* This operation will reconfigure network interface of the detector.
* During operation of the detector it is recommended to use the `POST /pedestal` operation instead.
* If storage cells are used, the execution time might be few minutes.
* It also clears the image buffer, disables live rugnux analysis and removes the rugnux model.
* It also clears the image buffer, the previous collection's plots and live rugnux status, disables live rugnux analysis and removes the rugnux model.
*
* This is async function - one needs to use `POST /wait_till_done` to ensure operation is done.
*
+1 -1
View File
@@ -28,7 +28,7 @@ export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends
* This operation will reconfigure network interface of the detector.
* During operation of the detector it is recommended to use the `POST /pedestal` operation instead.
* If storage cells are used, the execution time might be few minutes.
* It also clears the image buffer, disables live rugnux analysis and removes the rugnux model.
* It also clears the image buffer, the previous collection's plots and live rugnux status, disables live rugnux analysis and removes the rugnux model.
*
* This is async function - one needs to use `POST /wait_till_done` to ensure operation is done.
*
+1 -1
View File
@@ -1943,7 +1943,7 @@ export type rugnux_summary_row = {
};
/**
* Live rugnux processing of the last collection; kept until the next collection starts.
* Live rugnux processing of the last collection; kept until the next collection starts or `POST /initialize`.
* Times are taken when the image puller received a message, and are in seconds since the first image
* (before it arrives, since now). The frame rate is `(images_received - 1) / last_image_s`.
*
+1 -1
View File
@@ -824,7 +824,7 @@ export const zRugnuxSummaryRow = z.object({
});
/**
* Live rugnux processing of the last collection; kept until the next collection starts.
* Live rugnux processing of the last collection; kept until the next collection starts or `POST /initialize`.
* Times are taken when the image puller received a message, and are in seconds since the first image
* (before it arrives, since now). The frame rate is `(images_received - 1) / last_image_s`.
*
+1
View File
@@ -355,6 +355,7 @@ void JFJochReceiverService::ClearResults() {
receiver_status.Clear();
receiver_status.SetProgress({});
plots.Clear();
rugnux_status.Clear();
image_buffer.Finalize(std::chrono::milliseconds(2500));
}
+2 -2
View File
@@ -101,8 +101,8 @@ public:
std::string GetJPEGFromBuffer(const PreviewImageSettings &settings, int64_t image_number = ImageBuffer::MaxImage);
ImageBufferStatus GetImageBufferStatus() const;
void ClearImageBuffer();
// Drop everything the previous run left readable - its status, plots and buffered images - so
// nothing of it is served under the next run's tokens. Idle only.
// Drop everything the previous run left readable - its status, plots, live rugnux status and
// buffered images - so nothing of it is served under the next run's tokens. Idle only.
void ClearResults();
ImagePusherStatus GetImagePusherStatus() const;
+6
View File
@@ -106,6 +106,12 @@ void RugnuxLiveStatus::Warning(const std::string &text) {
s.warnings.push_back(text);
}
void RugnuxLiveStatus::Clear() {
std::unique_lock ul(m);
s = RugnuxLiveSnapshot{};
last_image.reset();
}
RugnuxLiveSnapshot RugnuxLiveStatus::Get() const {
std::unique_lock ul(m);
RugnuxLiveSnapshot ret = s;
+2
View File
@@ -81,5 +81,7 @@ public:
void Cut(const std::string &reason);
void Probe(const std::string &text);
void Warning(const std::string &text);
// Back to Idle with nothing of the last run, as before any collection.
void Clear();
RugnuxLiveSnapshot Get() const;
};
+9 -1
View File
@@ -69,7 +69,8 @@ TEST_CASE("JFJochBrokerHttp_BearerTokens", "[broker]") {
for (const char *path: {"/statistics/data_collection", "/result/scan", "/image_buffer/start.cbor",
"/image_buffer/image.cbor?id=0", "/image_buffer/image.tiff?id=0",
"/image_buffer/image.jpeg?id=0", "/preview/plot?type=bkg_estimate",
"/preview/plot.bin?type=bkg_estimate", "/live/events"}) {
"/preview/plot.bin?type=bkg_estimate", "/live/events",
"/config/rugnux", "/rugnux/status"}) {
INFO(path);
auto res = client.Get(path);
REQUIRE(res);
@@ -78,6 +79,11 @@ TEST_CASE("JFJochBrokerHttp_BearerTokens", "[broker]") {
REQUIRE(res->body.find("protected_run") == std::string::npos);
REQUIRE(client.Get(path, wrong)->status == 401);
}
// Live rugnux settings and its model are the dataset's too: changed only with a token.
REQUIRE(client.Put("/config/rugnux", R"({"enable":false})", "application/json")->status == 401);
REQUIRE(client.Put("/config/rugnux/model", "", "application/octet-stream")->status == 401);
REQUIRE(client.Delete("/config/rugnux/model")->status == 401);
REQUIRE(client.Delete("/config/rugnux/model", wrong)->status == 401);
}
SECTION("either token opens them") {
@@ -86,6 +92,8 @@ TEST_CASE("JFJochBrokerHttp_BearerTokens", "[broker]") {
REQUIRE(res->body.find("protected_run") != std::string::npos);
REQUIRE(client.Get("/statistics/data_collection", other)->status == 200);
REQUIRE(client.Get("/preview/plot?type=bkg_estimate", good)->status == 200);
REQUIRE(client.Get("/rugnux/status", good)->status == 200);
REQUIRE(client.Get("/config/rugnux", other)->status == 200);
}
SECTION("the aggregate statistics drop the measurement block instead of refusing") {
+26
View File
@@ -46,6 +46,32 @@ TEST_CASE("RugnuxLiveStatus_BeforeFirstImage", "[RugnuxLive]") {
CHECK(!status.LastImage());
}
TEST_CASE("RugnuxLiveStatus_Clear", "[RugnuxLive]") {
RugnuxLiveStatus status;
status.Start(10, 1.0, std::chrono::steady_clock::now() - 2s);
status.ImageReceived(std::chrono::steady_clock::now() - 1s);
status.Step("indexing");
status.Probe("lattice found");
status.Result("OK", "fine", {{"Space group", "P 1"}}, {"a warning"});
status.Finish(RugnuxLiveState::Done);
REQUIRE(status.LastImage());
status.Clear();
const auto s = status.Get();
CHECK(s.state == RugnuxLiveState::Idle);
CHECK(s.images_expected == 0);
CHECK(s.images_received == 0);
CHECK(s.steps.empty());
CHECK(s.probe.empty());
CHECK(s.verdict.empty());
CHECK(s.summary.empty());
CHECK(s.warnings.empty());
CHECK(!s.start_to_first_image_s);
CHECK(!s.last_image_s);
CHECK(s.elapsed_s == 0.0);
CHECK(!status.LastImage());
}
TEST_CASE("TestImagePuller_SetsReceiveTime", "[RugnuxLive]") {
TestImagePuller puller;
const auto before = std::chrono::steady_clock::now();