Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Switch, Route } from "react-router-dom";
import { BeamlineI24 } from "./routes/BeamlineI24";
import { FixedTarget } from "./routes/FixedTarget";
import { SerialNavBar } from "./components/SerialNavBar";
import { Extruder } from "./routes/Extruder";

function App() {
const theme = useTheme();
Expand All @@ -27,6 +28,9 @@ function App() {
<Route path="/fixed-target">
<FixedTarget />
</Route>
<Route path="/extruder">
<Extruder />
</Route>
</Switch>
<Footer
logo={theme.logos?.short}
Expand Down
40 changes: 1 addition & 39 deletions src/blueapi/BlueapiComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import {
Tooltip,
Typography,
} from "@mui/material";
import { forceString, ReadPvRawValue } from "../pv/util";
import { RawValue } from "../pv/types";
import { parseInstrumentSession, readVisitFromPv } from "./visit";

type SeverityLevel = "success" | "info" | "warning" | "error";
type VariantChoice = "outlined" | "contained";
Expand Down Expand Up @@ -37,43 +36,6 @@ type RunPlanButtonProps = {
// This will be another PR
// See https://github.com/DiamondLightSource/mx-daq-ui/issues/71

/**
* Read the full visit path from the visit PV set by the beamline staff.
* @returns {string} the full visit pV /dls/i24/data/{year}/{visit}
*/
export function readVisitFromPv(): string {
const fullVisitPath: RawValue = ReadPvRawValue({
label: "visit",
pv: "ca://BL24I-MO-IOC-13:GP100",
});
const visitString: string = forceString(fullVisitPath);
return visitString;
}

/**
* Parse the full visit path and return only the instrument session.
* An error will be raised if the instrument session value is undefined or
* if the PV is not connected.
* @param {string} visit The full visit path
* @returns {string} Only the instrument session part of the visit path
*/
export function parseInstrumentSession(visit: string): string {
let instrumentSession: string | undefined;
if (visit === "not connected" || visit === "undefined") {
const msg =
"Unable to run plan as instrument session not set. Please check visit PV.";
throw new Error(msg);
} else {
instrumentSession = visit.split("/").filter(Boolean).at(-1);
if (!instrumentSession) {
throw new Error(
"Unable to run plan as something appears to be wrong with visit path"
);
}
}
return instrumentSession;
}

export function RunPlanButton(props: RunPlanButtonProps) {
const [openSnackbar, setOpenSnackbar] = React.useState<boolean>(false);
const [msg, setMsg] = React.useState<string>("Running plan...");
Expand Down
39 changes: 39 additions & 0 deletions src/blueapi/visit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { RawValue } from "../pv/types";
import { forceString, ReadPvRawValue } from "../pv/util";

/**
* Read the full visit path from the visit PV set by the beamline staff.
* @returns {string} the full visit pV /dls/i24/data/{year}/{visit}
*/
export function readVisitFromPv(): string {
const fullVisitPath: RawValue = ReadPvRawValue({
label: "visit",
pv: "ca://BL24I-MO-IOC-13:GP100",
});
const visitString: string = forceString(fullVisitPath);
return visitString;
}

/**
* Parse the full visit path and return only the instrument session.
* An error will be raised if the instrument session value is undefined or
* if the PV is not connected.
* @param {string} visit The full visit path
* @returns {string} Only the instrument session part of the visit path
*/
export function parseInstrumentSession(visit: string): string {
let instrumentSession: string | undefined;
if (visit === "not connected" || visit === "undefined") {
const msg =
"Unable to run plan as instrument session not set. Please check visit PV.";
throw new Error(msg);
} else {
instrumentSession = visit.split("/").filter(Boolean).at(-1);
if (!instrumentSession) {
throw new Error(
"Unable to run plan as something appears to be wrong with visit path"
);
}
}
return instrumentSession;
}
123 changes: 123 additions & 0 deletions src/components/Extruder/CollectionSetupEx.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { Box, Grid2, Stack } from "@mui/material";
import React from "react";
import { ParameterInput } from "../ParameterInputs";
import {
LaserCheckButtons,
PumpProbeSelection,
PumpProbeSetup,
} from "./PumpProbeSelection";
import { AbortButton, RunPlanButton } from "../../blueapi/BlueapiComponents";

/**Main collection input window for the extruderpanel. */
export function CollectionSetupEx() {
const [subDir, setSubDir] = React.useState<string>("path/to/dir");
const [fileName, setFileName] = React.useState<string>("test");
const [expTime, setExpTime] = React.useState<number>(0.01);
const [numImages, setNumImages] = React.useState<number>(1);
const [trans, setTrans] = React.useState<number>(0.3);
const [detDist, setDetDist] = React.useState<number>(1350);
const [pumpProbe, setPumpProbe] = React.useState<boolean>(false);
const [laserDwell, setLaserDwell] = React.useState<number>(0);
const [laserDelay, setLaserDelay] = React.useState<number>(0);

return (
<Box sx={{ flexGrow: 1, marginRight: 10, marginLeft: 10 }}>
<Grid2 container spacing={2}>
<Grid2 size={4.5}>
<Stack direction={"column"} spacing={1} alignItems={"center"}>
<ParameterInput
value={subDir}
onSet={setSubDir}
label="Sub-directory"
tooltip="Location inside visit directory to save data"
/>
<ParameterInput
value={fileName}
onSet={setFileName}
label="File Name"
tooltip="Filename prefix."
/>
<ParameterInput
value={numImages}
onSet={setNumImages}
label="Number of images"
tooltip="How many images should be collected"
/>
<ParameterInput
value={expTime}
onSet={setExpTime}
label="Exposure Time (s)"
tooltip="Exposure time for each window, in seconds"
/>
<ParameterInput
value={trans}
onSet={setTrans}
label="Transmission (fraction)"
tooltip="Request transmission for collection, expressed as a fraction"
/>
<ParameterInput
value={detDist}
onSet={setDetDist}
label="Detector Distance (mm)"
tooltip="Distance to move the detector y stage to, in millimeters"
/>
</Stack>
</Grid2>
<Grid2 size={3}>
<Stack direction={"column"} spacing={1} alignItems={"center"}>
<PumpProbeSelection
pumpProbe={pumpProbe}
setPumpProbe={setPumpProbe}
/>
<PumpProbeSetup
dwell={laserDwell}
delay={laserDelay}
render={pumpProbe}
setDwell={setLaserDwell}
setDelay={setLaserDelay}
/>
<LaserCheckButtons />
</Stack>
</Grid2>
<Grid2 size={4.5}>
<Stack direction={"column"} spacing={1} alignItems={"center"}>
<RunPlanButton
btnLabel="Initialise on Start"
planName="initialise_extruder"
title="Initialise parameters for extruder"
btnSize="large"
/>
<RunPlanButton
btnLabel="Enter hutch"
planName="enter_hutch"
title="Move detector stage before entering hutch"
btnSize="large"
/>
</Stack>
</Grid2>
<Grid2 size={12}>
<Stack direction={"row"} spacing={8} justifyContent={"center"}>
<RunPlanButton
btnLabel="Start!"
planName="gui_run_extruder_collection"
planParams={{
sub_dir: subDir,
file_name: fileName,
exp_time: expTime,
det_dist: detDist,
transmission: trans,
num_images: numImages,
pump_probe: pumpProbe,
laser_dwell: laserDwell,
laser_delay: laserDelay,
}}
title="Start extruder collection"
btnSize="large"
/>
<AbortButton />
</Stack>
</Grid2>
</Grid2>
</Box>
);
}
101 changes: 101 additions & 0 deletions src/components/Extruder/PumpProbeSelection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {
FormControl,
InputLabel,
MenuItem,
Select,
Tooltip,
} from "@mui/material";
import { ParameterInput } from "../ParameterInputs";
import React from "react";
import { RunPlanButton } from "../../blueapi/BlueapiComponents";

export function PumpProbeSelection({
pumpProbe,
setPumpProbe,
}: {
pumpProbe: boolean;
setPumpProbe: React.Dispatch<React.SetStateAction<boolean>>;
}) {
const handleChange = (choice: string) => {
let newValue: boolean;
if (choice.toLowerCase() === "true") {
newValue = true;
} else {
newValue = false;
}
setPumpProbe(newValue);
};
return (
<Tooltip title="Is this a pump probe experiment?" placement="right">
<FormControl size="small" style={{ width: 180 }}>
<InputLabel id="pp-ex">Pump Probe</InputLabel>
<Select
labelId="pp-ex"
id="pp"
value={String(pumpProbe)}
label="Pump Probe"
onChange={(e) => handleChange(e.target.value)}
>
<MenuItem value={"true"}>True</MenuItem>
<MenuItem value={"false"}>False</MenuItem>
</Select>
</FormControl>
</Tooltip>
);
}

export function PumpProbeSetup({
dwell,
delay,
render,
setDwell,
setDelay,
}: {
dwell: number;
delay: number;
render: boolean;
setDwell: React.Dispatch<React.SetStateAction<number>>;
setDelay: React.Dispatch<React.SetStateAction<number>>;
}): JSX.Element | null {
if (render === false) {
return null;
} else {
return (
<React.Fragment>
<ParameterInput
value={dwell}
onSet={setDwell}
label="Laser Dwell (s)"
tooltip="Exposure time for the laser pump, in seconds"
/>
<ParameterInput
value={delay}
onSet={setDelay}
label="Laser Delay (s)"
tooltip="Delay time between the laser pump and the collection, in seconds"
/>
</React.Fragment>
);
}
}

export function LaserCheckButtons() {
return (
<React.Fragment>
<RunPlanButton
btnLabel="Laser ON"
planName="laser_check"
planParams={{ mode: "laseron" }}
title="Open shutter and check laser beam"
btnSize="small"
/>
<RunPlanButton
btnLabel="Laser OFF"
planName="laser_check"
planParams={{ mode: "laseroff" }}
title="Switch off laser beam"
btnSize="small"
/>
</React.Fragment>
);
}
Loading