From c78e6b99be6b6502d4027bc026afdee2569c0c86 Mon Sep 17 00:00:00 2001 From: Filip Leonarski Date: Mon, 2 Nov 2026 20:22:34 +0100 Subject: [PATCH] Rugnux live page: serial pre-pass and GPU analysis Where the run is a serial one analysed on the GPU, the page shows its pre-pass (state, images held, duration), the beam centre and distance stated and decided with the shift, the masked shadow and bad pixels, and the indexed fraction and analysis rate, in place of the rotation headline and result. Rotation runs render as before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4 --- frontend/src/components/RugnuxPage.tsx | 13 ++-- frontend/src/components/RugnuxStills.tsx | 99 ++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/RugnuxStills.tsx diff --git a/frontend/src/components/RugnuxPage.tsx b/frontend/src/components/RugnuxPage.tsx index 4230cfa2d..abf3d164a 100644 --- a/frontend/src/components/RugnuxPage.tsx +++ b/frontend/src/components/RugnuxPage.tsx @@ -8,6 +8,7 @@ import EmptyState from "./EmptyState"; import RugnuxTimeline from "./RugnuxTimeline"; import RugnuxSettings from "./RugnuxSettings"; import RugnuxHero from "./RugnuxHero"; +import RugnuxStills from "./RugnuxStills"; import {CutAlert, lastImageTime, ModelScores, ProbeAlert, RunMessage, StateChip, StepTable, VerdictAlert} from "./RugnuxParts"; import BoltIcon from "@mui/icons-material/Bolt"; import {lazy, Suspense} from "react"; @@ -152,25 +153,27 @@ function RugnuxPage({measuring}: { measuring: boolean }) { text="Dataset token required (key button in the top bar)"/>; else if (isError || s === undefined || s.state === 'Idle') live = } - text="No rugnux run yet - start a rotation collection with live processing enabled"/>; + text="No rugnux run yet - start a collection with live processing enabled"/>; else live = (
- + {s.stills + ?
+ : }
{s.model && s.state !== 'Running' && } -
-
+ {!s.stills &&
{s.verdict ? : @@ -180,7 +183,7 @@ function RugnuxPage({measuring}: { measuring: boolean }) { 0 ? 2 : 0}}> {s.warnings.map((w, i) => {w})} -
+
}
); diff --git a/frontend/src/components/RugnuxStills.tsx b/frontend/src/components/RugnuxStills.tsx new file mode 100644 index 000000000..9d36fcdc4 --- /dev/null +++ b/frontend/src/components/RugnuxStills.tsx @@ -0,0 +1,99 @@ +import {Box, Chip, LinearProgress, Stack, Table, TableBody, TableCell, TableHead, TableRow, Typography} from "@mui/material"; +import {grey, indigo} from "@mui/material/colors"; +import {rugnux_live_stills} from "../client"; + +// Serial frames converted by the FPGA and analysed on the GPU: the pre-pass on the first images - the beam +// centre, distance and mask the whole collection is analysed with - and the analysis since. + +const PREPASS_COLOUR = {Holding: "default", Running: "primary", Done: "success", Failed: "error"} as const; + +function Tile({label, value, sub}: { label: string, value: string, sub?: string }) { + return ( + + + {label} + + {value} + {sub && {sub}} + + ); +} + +function signed(x: number, digits: number) { + return (x >= 0 ? "+" : "") + x.toFixed(digits); +} + +function RugnuxStills({st}: { st: rugnux_live_stills }) { + const decided = st.beam_x_pxl !== undefined && st.beam_y_pxl !== undefined; + const shift = decided ? Math.hypot(st.beam_x_pxl! - st.file_beam_x_pxl, st.beam_y_pxl! - st.file_beam_y_pxl) : undefined; + const indexed = st.images_analysed > 0 ? 100 * st.images_indexed / st.images_analysed : undefined; + const fmt = (n?: number) => n === undefined ? "—" : n.toLocaleString(); + + return ( + + + Pre-pass + + + {st.prepass === "Holding" && `holding ${st.images_held} / ${st.images_needed} images`} + {st.prepass === "Running" && `on the first ${st.images_held} images`} + {st.prepass_s !== undefined && `on ${st.images_held} images in ${st.prepass_s.toFixed(1)} s`} + {st.prepass === "Failed" && " · the stated beam centre and mask are used"} + + + {st.prepass === "Holding" && + } + {st.prepass === "Running" && } + + + + + + Stated + Pre-pass + Shift + + + + + Beam centre (px) + {st.file_beam_x_pxl.toFixed(2)}, {st.file_beam_y_pxl.toFixed(2)} + + {decided ? `${st.beam_x_pxl!.toFixed(2)}, ${st.beam_y_pxl!.toFixed(2)}` : "—"} + + + {decided ? `${signed(st.beam_x_pxl! - st.file_beam_x_pxl, 2)}, ${signed(st.beam_y_pxl! - st.file_beam_y_pxl, 2)} (${shift!.toFixed(2)} px)` : "—"} + + + + Detector distance (mm) + {st.file_distance_mm.toFixed(2)} + + {st.distance_mm !== undefined ? st.distance_mm.toFixed(2) : "—"} + + + {st.distance_mm !== undefined ? `${signed(st.distance_mm - st.file_distance_mm, 2)} mm` : "—"} + + + +
+ + + + + + + + + Every image is analysed with the pre-pass's beam centre, distance and mask; images arriving + during the pre-pass are held and analysed after it. + +
+ ); +} + +export default RugnuxStills;