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;