receiver: a rotation run the image buffer cannot hold gets the per-image analysis

Live rugnux reads the images back from the image buffer, so a rotation
collection with more images than slots was only forwarded: no spots, no
scan result, no pixel sum. JFJochReceiverService now sees this when the
run starts and picks JFJochReceiverLite instead. The rugnux live status
is Skipped with the reason, and the receiver output carries the warning
for the broker's status, as before. The slot count is from the
experiment as set up; the rotation receiver keeps its own check for a
stream whose start message changes the image size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVeDQsU5a8AJUdJPgWevAK
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-11-08 11:15:25 +01:00
1 parent 72310c2ee1
commit 2978a9f928
3 files changed
+58 -3

No files matched your search

+21 -3
View File
@@ -101,6 +101,7 @@ void JFJochReceiverService::Start(const DiffractionExperiment &experiment,
try {
// First clean-up old measurement
receiver.reset();
analysis_warning.clear();
if (fpga_puller) {
image_puller.reset();
fpga_puller.reset();
@@ -152,6 +153,19 @@ void JFJochReceiverService::Start(const DiffractionExperiment &experiment,
else
image_puller = std::make_shared<ZMQImagePuller>(experiment.GetDetectorSetup().GetDECTRISStream2Addr());
bool rugnux_rotation = rugnux_enable && (experiment.GetDetectorMode() == DetectorMode::Standard)
&& RugnuxRotationData(experiment);
// Rugnux reads the images back from the buffer: a collection that does not fit gets the per-image analysis instead
const int64_t slots = image_buffer.GetBufferSize() / experiment.GetImageBufferLocationSize();
if (rugnux_rotation && (slots < experiment.GetImageNum())) {
analysis_warning = fmt::format("no rugnux analysis: the image buffer holds {} images, the collection "
"has {}", slots, experiment.GetImageNum());
logger.Warning("{}", analysis_warning);
rugnux_status.Start(experiment.GetImageNum(), {}, std::chrono::steady_clock::now());
rugnux_status.Finish(RugnuxLiveState::Skipped, "the image buffer cannot hold the whole collection");
rugnux_rotation = false;
}
// The dark frames of the mask sequence have a receiver of their own, with rugnux on as well.
if (experiment.GetDetectorMode() == DetectorMode::DarkMask)
receiver = std::make_unique<JFJochReceiverDarkMask>(experiment,
@@ -164,13 +178,14 @@ void JFJochReceiverService::Start(const DiffractionExperiment &experiment,
receiver_status,
plots,
image_buffer);
else if (rugnux_enable && (experiment.GetDetectorMode() == DetectorMode::Standard) && RugnuxRotationData(experiment))
else if (rugnux_rotation)
receiver = std::make_unique<JFJochReceiverRugnuxRotation>(
experiment, pixel_mask, *image_puller, image_pusher, logger, nthreads,
std::max<int>(1, static_cast<int>(AvailableCpus()) - rugnux_reserved_cpus),
rugnux_model, rugnux_deadline_s, rugnux_status, spot_finding_settings, receiver_status, plots,
image_buffer, zmq_preview_socket.get(), zmq_metadata_socket.get());
else if (rugnux_enable && (experiment.GetDetectorMode() == DetectorMode::Standard))
else if (rugnux_enable && (experiment.GetDetectorMode() == DetectorMode::Standard)
&& !RugnuxRotationData(experiment))
receiver = std::make_unique<JFJochReceiverRugnuxStills>(
experiment, pixel_mask, *image_puller, image_pusher, logger, nthreads,
rugnux_model, rugnux_deadline_s, rugnux_status, spot_finding_settings, receiver_status, plots,
@@ -235,7 +250,10 @@ JFJochReceiverOutput JFJochReceiverService::Stop() {
logger.Warning("Request to stop while receiver not running");
throw JFJochException(JFJochExceptionCategory::WrongReceiverState, "Receiver idle, cannot stop");
}
return receiver->GetFinalStatistics();
auto ret = receiver->GetFinalStatistics();
if (!analysis_warning.empty())
ret.analysis_warning = analysis_warning;
return ret;
}
void JFJochReceiverService::SetSpotFindingSettings(const SpotFindingSettings &settings) {
+1
View File
@@ -35,6 +35,7 @@ class JFJochReceiverService {
int rugnux_reserved_cpus = 4; // left to the forward threads and the pusher
double rugnux_deadline_s = 60; // the analysis is cut this long after the last image (0 = never)
RugnuxLiveStatus rugnux_status; // of the last collection, kept after it ends
std::string analysis_warning; // why the collection was not analysed as asked, for the broker's status
// A PSI detector's frames go through FPGAImagePuller and the stream receivers. Off unless asked for.
bool fpga_image_puller = false;
+36
View File
@@ -619,6 +619,42 @@ TEST_CASE("JFJochReceiverRugnux_Rotation", "[JFJochReceiverRugnux]") {
CHECK(live.state != RugnuxLiveState::Running);
}
TEST_CASE("JFJochReceiverRugnux_Rotation_DoesNotFit", "[JFJochReceiverRugnux]") {
// A scan the image buffer cannot hold is analysed image by image, and the live status says why rugnux did not run.
Logger logger(Catch::getResultCapture().getCurrentTestName());
RegisterHDF5Filter();
const int64_t images = 5;
DiffractionExperiment x = StillsExperiment(images);
x.Goniometer(GoniometerAxis("omega", 0.0f, 0.1f, {1, 0, 0}, {}));
PixelMask mask(x);
Stream stream(x, images);
stream.Frames(0, images);
stream.End();
// Three slots
const size_t buffer_MiB = (3 * x.GetImageBufferLocationSize()) / (1024 * 1024) + 1;
REQUIRE(buffer_MiB * 1024 * 1024 / x.GetImageBufferLocationSize() < images);
RecordingPusher pusher;
AcquisitionDeviceGroup group;
JFJochReceiverService service(group, logger, pusher, buffer_MiB);
service.NumThreads(4);
service.Indexing(x.GetIndexingSettings());
service.RugnuxLive(true, nullptr, 4, 60);
service.Start(x, mask, nullptr, stream.puller);
const auto out = service.Stop();
CHECK(out.status.images_collected == images);
CHECK(out.scan_result.images.size() == images);
CHECK(pusher.Events() == std::vector<std::string>{"start", "image", "end"});
CHECK(out.analysis_warning.find("image buffer") != std::string::npos);
const auto live = service.GetRugnuxStatus();
CHECK(live.state == RugnuxLiveState::Skipped);
CHECK(live.message.find("image buffer") != std::string::npos);
}
TEST_CASE("JFJochReceiverRugnux_Rotation_Cancel", "[JFJochReceiverRugnux]") {
Logger logger(Catch::getResultCapture().getCurrentTestName());
RegisterHDF5Filter();