diff --git a/frontend/src/components/RugnuxPage.tsx b/frontend/src/components/RugnuxPage.tsx
index 8af057b73..463fa019f 100644
--- a/frontend/src/components/RugnuxPage.tsx
+++ b/frontend/src/components/RugnuxPage.tsx
@@ -3,7 +3,7 @@ import {green, indigo, lime, orange, red, grey} from "@mui/material/colors";
import {keyframes} from "@mui/system";
import {useQuery} from "@tanstack/react-query";
import {getConfigRugnuxOptions, getRugnuxStatusOptions} from "../client/@tanstack/react-query.gen";
-import {rugnux_live_status, rugnux_live_step} from "../client";
+import {rugnux_live_status} from "../client";
import {useTokenRequired} from "../auth";
import EmptyState from "./EmptyState";
import RugnuxTriage from "./RugnuxTriage";
@@ -16,7 +16,6 @@ import RugnuxProgress from "./RugnuxProgress";
import {lastImageTime, ModelScores, StepTable, VerdictAlert} from "./RugnuxParts";
import BoltIcon from "@mui/icons-material/Bolt";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import CheckIcon from "@mui/icons-material/Check";
import {lazy, Suspense, useEffect, useRef, useState} from "react";
// The live page as one control-room screen: a dark stage the height of the window, edged in the colour of the
@@ -110,34 +109,21 @@ function Later({text}: { text: string }) {
return {text};
}
-// The run's steps as a console - the running one large with its progress, the ones behind it as a list with
-// their durations - filling the centre until there is a model to show. Their notes are in the Details fold.
+// The step rugnux is on, large with its progress, filling the centre until there is a model to show; the
+// steps behind it are on the strip under the bars.
function RunConsole({s}: { s: rugnux_live_status }) {
const running = s.state === "Running";
const current = running && s.steps.length > 0 && s.steps[s.steps.length - 1].duration_s === undefined
? s.steps[s.steps.length - 1] : undefined;
- const done = current ? s.steps.slice(0, -1) : s.steps;
- const row = (step: rugnux_live_step, i: number) => (
-
-
- {step.title}
-
- {step.duration_s !== undefined ? `${step.duration_s.toFixed(1)} s` : ""}
-
-
- );
+ if (!current)
+ return ;
return (
-
- {current &&
- now
-
- {current.title}{s.step_progress !== undefined && {(100 * s.step_progress).toFixed(0)} %}
-
- }
- {s.steps.length === 0 && }
- {done.map(row)}
+
+ now
+
+ {current.title}{s.step_progress !== undefined && {(100 * s.step_progress).toFixed(0)} %}
+
);
}
diff --git a/frontend/src/components/RugnuxProgress.tsx b/frontend/src/components/RugnuxProgress.tsx
index 827974b36..2daddc425 100644
--- a/frontend/src/components/RugnuxProgress.tsx
+++ b/frontend/src/components/RugnuxProgress.tsx
@@ -1,15 +1,16 @@
import {useEffect, useRef, useState} from "react";
import {Box, Tooltip, Typography} from "@mui/material";
import {keyframes} from "@mui/system";
-import {deepOrange, grey, indigo, lightBlue, lime} from "@mui/material/colors";
-import {rugnux_live_status} from "../client";
+import {deepOrange, grey, indigo, lime} from "@mui/material/colors";
+import {rugnux_live_status, rugnux_live_step} from "../client";
import {lastImageTime} from "./RugnuxParts";
// The detector's and rugnux's progress one under the other on one time axis (seconds since the first image):
// the detector's bar fills as images arrive, up to the collection's expected end; rugnux's bar is its steps,
// one segment each, as wide as the step took - the running one growing to "now" - so what takes how long is
-// read off the bar, and the strip under the bars names every step with its duration. The last image and the
-// deadline are lines across both bars.
+// read off the bar. Each kind of step has a tag of a few letters and a colour of its own, the same on every
+// pass and on the strip under the bars that gives each kind its time; the full titles are on hover. The last
+// image and the deadline are lines across both bars.
const stripes = keyframes`
from { background-position: 0 0; }
@@ -28,7 +29,38 @@ const runningSx = {
"@media (prefers-reduced-motion: reduce)": {animation: "none"},
};
-const STEP_COLOURS = [indigo[400], lightBlue[700], indigo[600], lightBlue[900], indigo[300], lightBlue[500]];
+// The step's tag from its title; a title no row knows is its first word.
+const TAGS: [RegExp, string][] = [
+ [/^pre-?(scan|pass)/i, "Pre"], [/spot/i, "Spots"], [/index/i, "Idx"], [/integrat/i, "Int"], [/gpu analysis/i, "GPU"],
+ [/scaling/i, "Scale"], [/^merge/i, "Merge"], [/molecular replacement/i, "MR"], [/geometry refinement/i, "Geom"],
+ [/refinement/i, "Ref"], [/validation/i, "Val"], [/analys/i, "Chk"], [/writ/i, "Out"], [/distance/i, "Dist"],
+ [/doubled-cell/i, "Cell"], [/calibration/i, "Cal"], [/reading/i, "Read"],
+];
+const tagOf = (title: string) => TAGS.find(([re]) => re.test(title))?.[1] ?? title.split(/[\s,]/)[0].slice(0, 6);
+
+// One colour per kind of step - Okabe-Ito, which keeps its kinds apart for colour-blind readers too, with
+// its blue lightened for the dark stage; a kind not in the table takes the next free one.
+const KIND_COLOURS: Record = {
+ Pre: "#56B4E9", Spots: "#E69F00", Idx: "#F0E442", Int: "#3C8DD6", GPU: "#3C8DD6", Scale: "#009E73",
+ Merge: "#009E73", MR: "#CC79A7", Ref: "#D55E00", Out: "#9E9E9E",
+};
+const SPARE_COLOURS = ["#8DD3C7", "#BEBADA", "#FB8072", "#FDB462", "#B3DE69"];
+function kindColours(steps: rugnux_live_step[]) {
+ const colours: Record = {...KIND_COLOURS};
+ let spare = 0;
+ for (const st of steps) {
+ const tag = tagOf(st.title);
+ if (!(tag in colours))
+ colours[tag] = SPARE_COLOURS[spare++ % SPARE_COLOURS.length];
+ }
+ return colours;
+}
+// dark text on the light colours
+const inkOn = (hex: string) => {
+ const [r, g, b] = [1, 3, 5].map(i => parseInt(hex.slice(i, i + 2), 16));
+ return 0.299 * r + 0.587 * g + 0.114 * b > 150 ? "rgba(0,0,0,.85)" : "#fff";
+};
+
const BAR = 20;
const GAP = 6;
const AXIS = 16;
@@ -85,6 +117,21 @@ function RugnuxProgress({s, deadline_s}: { s: rugnux_live_status, deadline_s: nu
: s.cut ? `cut (${s.cut})` : `${s.steps.length} steps · ${seconds(s.elapsed_s)}`;
const stepEnd = (i: number) => s.steps[i].duration_s === undefined ? s.elapsed_s : s.steps[i].started_s + s.steps[i].duration_s!;
+ const colours = kindColours(s.steps);
+ const tags = s.steps.map(st => tagOf(st.title));
+ // consecutive steps of one kind (the passes) as one chip, with their time together
+ const kinds: { tag: string, titles: string[], seconds: number, open: boolean }[] = [];
+ s.steps.forEach((st, i) => {
+ const last = kinds[kinds.length - 1];
+ const open = st.duration_s === undefined;
+ const duration = open ? s.elapsed_s - st.started_s : st.duration_s!;
+ if (last && last.tag === tags[i]) {
+ last.titles.push(st.title);
+ last.seconds += duration;
+ last.open = open;
+ } else
+ kinds.push({tag: tags[i], titles: [st.title], seconds: duration, open});
+ });
return (
@@ -107,15 +154,18 @@ function RugnuxProgress({s, deadline_s}: { s: rugnux_live_status, deadline_s: nu
{s.steps.map((st, i) => {
const open = st.duration_s === undefined;
const width = Math.max(pct(stepEnd(i)) - pct(st.started_s), 0.3);
- const showTitle = width / 100 * laneWidth >= Math.min(st.title.length * 6.5 + 10, 60);
+ const colour = colours[tags[i]];
+ // a pass after a pass of the same kind: a thin divider, the tag only on the first
+ const samePass = i > 0 && tags[i - 1] === tags[i];
+ const showTag = !samePass && width / 100 * laneWidth >= tags[i].length * 8 + 14;
return (
- {showTitle &&
- {st.title}
+ {showTag &&
+ {tags[i]}
}
@@ -154,25 +204,24 @@ function RugnuxProgress({s, deadline_s}: { s: rugnux_live_status, deadline_s: nu
- {/* the steps, each with its duration */}
- {s.steps.length > 0 &&
+ {/* the kinds of step, each with its time */}
+ {kinds.length > 0 &&
- {s.steps.map((st, i) => {
- const open = st.duration_s === undefined;
- return (
- (
+
+
-
- {st.title}
+
+ {k.tag}
- {!open ? seconds(st.duration_s!)
- : running && i === s.steps.length - 1 && s.step_progress !== undefined ? `${(100 * s.step_progress).toFixed(0)} %` : ""}
+ {k.open && running && i === kinds.length - 1 && s.step_progress !== undefined
+ ? `${(100 * s.step_progress).toFixed(0)} %` : seconds(k.seconds)}
- );
- })}
+
+ ))}
}
);