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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UqXYdJKPUFLoeAchjzv3x4
This commit is contained in:
1 parent
4a54e0c6f5
commit
c78e6b99be
2 files changed
+107
-5
No files matched your search
@@ -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)"/></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>;
|
||||
text="No rugnux run yet - start a collection with live processing enabled"/></Paper>;
|
||||
else
|
||||
live = (
|
||||
<Stack spacing={2}>
|
||||
<Header s={s}/>
|
||||
<RugnuxHero s={s} modelLoaded={settings?.model_loaded === true}/>
|
||||
{s.stills
|
||||
? <Section title="Serial · pre-pass and GPU analysis"><RugnuxStills st={s.stills}/></Section>
|
||||
: <RugnuxHero s={s} modelLoaded={settings?.model_loaded === true}/>}
|
||||
<CutAlert s={s}/>
|
||||
<ProbeAlert s={s} large/>
|
||||
<Section title="Timeline">
|
||||
<RugnuxTimeline s={s} deadline_s={settings?.deadline_s ?? 0}/>
|
||||
</Section>
|
||||
{s.model && s.state !== 'Running' && <ModelSection s={s}/>}
|
||||
<Box sx={{display: "grid", gridTemplateColumns: {xs: "1fr", lg: "minmax(400px, 1fr) 2fr"}, gap: 2,
|
||||
<Box sx={{display: "grid", gridTemplateColumns: {xs: "1fr", lg: s.stills ? "1fr" : "minmax(400px, 1fr) 2fr"}, gap: 2,
|
||||
alignItems: "start"}}>
|
||||
<Section title="Processing">
|
||||
<CurrentStep s={s}/>
|
||||
<StepTable steps={s.steps}/>
|
||||
</Section>
|
||||
<Section title="Result">
|
||||
{!s.stills && <Section title="Result">
|
||||
{s.verdict
|
||||
? <VerdictAlert s={s}/>
|
||||
: <Typography color="text.secondary">
|
||||
@@ -180,7 +183,7 @@ function RugnuxPage({measuring}: { measuring: boolean }) {
|
||||
<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>
|
||||
</Section>}
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<Box sx={{p: 1.5, borderRadius: 2, bgcolor: indigo[50], border: "1px solid", borderColor: indigo[100]}}>
|
||||
<Typography sx={{fontSize: "0.75rem", fontWeight: 700, letterSpacing: ".06em", textTransform: "uppercase",
|
||||
color: "primary.main"}}>
|
||||
{label}
|
||||
</Typography>
|
||||
<Typography sx={{fontSize: "1.2rem", fontWeight: 700, color: "primary.dark"}}>{value}</Typography>
|
||||
{sub && <Typography sx={{fontSize: "0.85rem", color: "text.secondary"}}>{sub}</Typography>}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Stack spacing={2}>
|
||||
<Stack direction="row" spacing={2} alignItems="center" flexWrap="wrap" useFlexGap>
|
||||
<Typography sx={{fontWeight: 700, fontSize: "1.25rem"}}>Pre-pass</Typography>
|
||||
<Chip label={st.prepass} color={PREPASS_COLOUR[st.prepass]}/>
|
||||
<Typography color="text.secondary">
|
||||
{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"}
|
||||
</Typography>
|
||||
</Stack>
|
||||
{st.prepass === "Holding" &&
|
||||
<LinearProgress variant="determinate" value={100 * st.images_held / Math.max(1, st.images_needed)}
|
||||
sx={{height: 10, borderRadius: 5}}/>}
|
||||
{st.prepass === "Running" && <LinearProgress sx={{height: 10, borderRadius: 5}}/>}
|
||||
|
||||
<Table size="small">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell/>
|
||||
<TableCell align="right">Stated</TableCell>
|
||||
<TableCell align="right">Pre-pass</TableCell>
|
||||
<TableCell align="right">Shift</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">Beam centre (px)</TableCell>
|
||||
<TableCell align="right">{st.file_beam_x_pxl.toFixed(2)}, {st.file_beam_y_pxl.toFixed(2)}</TableCell>
|
||||
<TableCell align="right" sx={{fontWeight: 700}}>
|
||||
{decided ? `${st.beam_x_pxl!.toFixed(2)}, ${st.beam_y_pxl!.toFixed(2)}` : "—"}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{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)` : "—"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell component="th" scope="row">Detector distance (mm)</TableCell>
|
||||
<TableCell align="right">{st.file_distance_mm.toFixed(2)}</TableCell>
|
||||
<TableCell align="right" sx={{fontWeight: 700}}>
|
||||
{st.distance_mm !== undefined ? st.distance_mm.toFixed(2) : "—"}
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
{st.distance_mm !== undefined ? `${signed(st.distance_mm - st.file_distance_mm, 2)} mm` : "—"}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
|
||||
<Box sx={{display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 1.5}}>
|
||||
<Tile label="Beam-stop shadow" value={`${fmt(st.shadow_pixels)} px`}/>
|
||||
<Tile label="Bad pixels" value={`${fmt(st.bad_pixels)} px`} sub="hot, dead, error value"/>
|
||||
<Tile label="Indexed" value={indexed !== undefined ? `${indexed.toFixed(1)} %` : "—"}
|
||||
sub={`${st.images_indexed.toLocaleString()} of ${st.images_analysed.toLocaleString()} images`}/>
|
||||
<Tile label="GPU analysis" value={st.analysis_rate_hz !== undefined ? `${st.analysis_rate_hz.toFixed(0)} Hz` : "—"}
|
||||
sub="since the pre-pass"/>
|
||||
</Box>
|
||||
<Typography sx={{fontSize: "0.85rem", color: grey[600]}}>
|
||||
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.
|
||||
</Typography>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
export default RugnuxStills;
|
||||
Reference in new issue
Block a user