Skip to content

Commit 8eca618

Browse files
committed
Merge branch 'main' into 67-navbar
2 parents c6953bb + 74a3e06 commit 8eca618

File tree

6 files changed

+309
-254
lines changed

6 files changed

+309
-254
lines changed

src/blueapi/BlueapiComponents.tsx

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React from "react";
2+
import { abortCurrentPlan, submitAndRunPlanImmediately } from "./blueapi";
3+
import {
4+
Alert,
5+
Button,
6+
Snackbar,
7+
SnackbarCloseReason,
8+
styled,
9+
Tooltip,
10+
Typography,
11+
} from "@mui/material";
12+
13+
type SeverityLevel = "success" | "info" | "warning" | "error";
14+
type VariantChoice = "outlined" | "contained";
15+
type ButtonSize = "small" | "medium" | "large";
16+
17+
type RunPlanButtonProps = {
18+
btnLabel: string;
19+
planName: string;
20+
planParams?: object;
21+
title?: string;
22+
btnVariant?: VariantChoice;
23+
btnSize?: ButtonSize;
24+
};
25+
26+
// @{todo} Ideally we should be able to set up the stylings for
27+
// a custom button in the proper way by doing something like:
28+
// const CustomRunButton = styled(Button)({
29+
// width: "100%",
30+
// height: "85%",
31+
// color: "var(--color)",
32+
// backgroundColor: "var(--backgroundColor)",
33+
// padding: "var(--padding)",
34+
// margin: "var(--margin)",
35+
// });
36+
// This will be another PR
37+
// See https://github.com/DiamondLightSource/mx-daq-ui/issues/71
38+
39+
export function RunPlanButton(props: RunPlanButtonProps) {
40+
const [openSnackbar, setOpenSnackbar] = React.useState<boolean>(false);
41+
const [msg, setMsg] = React.useState<string>("Running plan...");
42+
const [severity, setSeverity] = React.useState<SeverityLevel>("info");
43+
44+
const params = props.planParams ? props.planParams : {};
45+
const variant = props.btnVariant ? props.btnVariant : "outlined";
46+
const size = props.btnSize ? props.btnSize : "medium";
47+
48+
const handleClick = () => {
49+
setOpenSnackbar(true);
50+
submitAndRunPlanImmediately({
51+
planName: props.planName,
52+
planParams: params,
53+
}).catch((error) => {
54+
setSeverity("error");
55+
setMsg(
56+
`Failed to run plan ${props.planName}, see console and logs for full error`
57+
);
58+
console.log(`${msg}. Reason: ${error}`);
59+
});
60+
};
61+
62+
const handleSnackbarClose = (
63+
_event: React.SyntheticEvent | Event,
64+
reason?: SnackbarCloseReason
65+
) => {
66+
if (reason === "clickaway") {
67+
return;
68+
}
69+
70+
setOpenSnackbar(false);
71+
};
72+
73+
return (
74+
<div>
75+
<Tooltip title={props.title ? props.title : ""} placement="bottom">
76+
<Button
77+
variant={variant}
78+
color="custom"
79+
size={size}
80+
onClick={handleClick}
81+
>
82+
<Typography
83+
variant="button"
84+
fontWeight="fontWeightBold"
85+
sx={{ display: "block" }}
86+
>
87+
{props.btnLabel}
88+
</Typography>
89+
</Button>
90+
</Tooltip>
91+
<Snackbar
92+
open={openSnackbar}
93+
autoHideDuration={5000}
94+
onClose={handleSnackbarClose}
95+
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
96+
>
97+
<Alert onClose={handleSnackbarClose} severity={severity}>
98+
{msg}
99+
</Alert>
100+
</Snackbar>
101+
</div>
102+
);
103+
}
104+
105+
export function AbortButton() {
106+
const [openMsg, setOpenMsg] = React.useState<boolean>(false);
107+
108+
const handleClick = () => {
109+
setOpenMsg(true);
110+
abortCurrentPlan();
111+
};
112+
113+
const handleMsgClose = (
114+
_event: React.SyntheticEvent | Event,
115+
reason?: SnackbarCloseReason
116+
) => {
117+
if (reason === "clickaway") {
118+
return;
119+
}
120+
121+
setOpenMsg(false);
122+
};
123+
124+
return (
125+
<div>
126+
<Tooltip title="Abort current blueapi operation" placement="bottom">
127+
<Button
128+
color="custom"
129+
variant="outlined"
130+
size="large"
131+
onClick={handleClick}
132+
>
133+
<Typography
134+
variant="button"
135+
fontWeight="fontWeightBold"
136+
sx={{ display: "block" }}
137+
>
138+
Abort!
139+
</Typography>
140+
</Button>
141+
</Tooltip>
142+
<Snackbar
143+
open={openMsg}
144+
autoHideDuration={5000}
145+
onClose={handleMsgClose}
146+
anchorOrigin={{ vertical: "bottom", horizontal: "right" }}
147+
>
148+
<Alert onClose={handleMsgClose} severity="warning">
149+
Aborting plan ...
150+
</Alert>
151+
</Snackbar>
152+
</div>
153+
);
154+
}

src/blueapi/blueapi.ts

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ const BLUEAPI_SOCKET: string = import.meta.env.VITE_BLUEAPI_SOCKET;
55
type BlueApiRequestBody = {
66
planName: string;
77
planParams: object;
8+
// instrumentSession: string;
89
};
10+
// Update to latest blueapi (> 1.0.0), See https://github.com/DiamondLightSource/mx-daq-ui/issues/72
911
// @todo check if blueapi request still works if planParams optional (since some times there's none)
1012

1113
export type BlueApiWorkerState =
@@ -44,13 +46,12 @@ export function useBlueApiCall(
4446
pollRateMillis?: number,
4547
queryKey?: string
4648
) {
47-
return useQuery(
48-
queryKey ?? "BlueApiCall",
49-
async () => await blueApiCall(endpoint, method, body),
50-
{
51-
refetchInterval: pollRateMillis ?? 500,
52-
}
53-
);
49+
const fetchCall = async () => {
50+
return await blueApiCall(endpoint, method, body);
51+
};
52+
return useQuery(queryKey ?? "BlueApiCall", fetchCall, {
53+
refetchInterval: pollRateMillis ?? 500,
54+
});
5455
}
5556

5657
export function processUseBlueApiCall(
@@ -81,7 +82,7 @@ function submitTask(request: BlueApiRequestBody): Promise<string | void> {
8182
}).then((res) => {
8283
if (!res.ok) {
8384
throw new Error(
84-
`Unable to POST request, response error ${res.statusText}`
85+
`Unable to POST request, response error ${res.status} ${res.statusText}`
8586
);
8687
}
8788
res.json().then((res) => res["task_id"]);
@@ -91,7 +92,9 @@ function submitTask(request: BlueApiRequestBody): Promise<string | void> {
9192
function runTask(taskId: string): Promise<string | void> {
9293
return blueApiCall("/worker/task", "PUT", { task_id: taskId }).then((res) => {
9394
if (!res.ok) {
94-
throw new Error(`Unable to run task, response error ${res.statusText}`);
95+
throw new Error(
96+
`Unable to run task, response error ${res.status} ${res.statusText}`
97+
);
9598
}
9699
res.json().then((res) => res["task_id"]);
97100
});
@@ -100,13 +103,11 @@ function runTask(taskId: string): Promise<string | void> {
100103
export function submitAndRunPlanImmediately(
101104
request: BlueApiRequestBody
102105
): Promise<string | void> {
103-
return submitTask(request)
104-
.then((res) => {
105-
if (res) {
106-
runTask(res);
107-
}
108-
})
109-
.catch((error) => console.log(error));
106+
return submitTask(request).then((res) => {
107+
if (res) {
108+
runTask(res);
109+
}
110+
});
110111
}
111112

112113
export function abortCurrentPlan(): Promise<BlueApiWorkerState> {

src/components/WorkerStatus.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,28 @@
1-
import { Box, Grid2, Stack } from "@mui/material";
1+
import { Grid2, Stack, Typography } from "@mui/material";
22

33
import { processUseBlueApiCall, useBlueApiCall } from "../blueapi/blueapi";
44
import { useState } from "react";
55

66
export function WorkerStatus() {
77
const [currentState, setCurrentState] = useState<string>("UNKNOWN");
88
const workerStateInfo = useBlueApiCall("/worker/state");
9+
10+
const readCurrentState = () => {
11+
return processUseBlueApiCall(workerStateInfo, (res) => {
12+
if (!res.bodyUsed) {
13+
res.json().then((text) => setCurrentState(text));
14+
}
15+
return currentState;
16+
});
17+
};
18+
919
return (
1020
<Grid2 size={12}>
1121
<Stack direction={"row"} spacing={1} justifyContent={"center"}>
12-
<b>BlueAPI worker status: </b>
13-
<Box>
14-
{processUseBlueApiCall(workerStateInfo, (res) => {
15-
if (!res.bodyUsed) {
16-
res.json().then((text) => setCurrentState(text));
17-
}
18-
return currentState;
19-
})}
20-
</Box>
22+
<Typography variant="body1" fontWeight={"bold"}>
23+
BlueAPI worker status:
24+
</Typography>
25+
<Typography variant="body1">{readCurrentState()}</Typography>
2126
</Stack>
2227
</Grid2>
2328
);

src/screens/CollectionPanel.tsx

Lines changed: 24 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
Box,
3-
Button,
43
FormControl,
54
Grid2,
65
InputLabel,
@@ -9,17 +8,13 @@ import {
98
Stack,
109
TextField,
1110
Tooltip,
12-
Typography,
1311
} from "@mui/material";
1412
import { PvComponent } from "../pv/PvComponent";
1513
import React from "react";
1614
import { MapView, PumpProbeOptions } from "../components/CollectionComponents";
17-
import {
18-
abortCurrentPlan,
19-
submitAndRunPlanImmediately,
20-
} from "../blueapi/blueapi";
2115
import { chipTypes, MapTypes, pumpProbeMode } from "../components/params";
2216
import { forceString } from "../pv/util";
17+
import { AbortButton, RunPlanButton } from "../blueapi/BlueapiComponents";
2318

2419
/**
2520
* A couple of read-only boxes showing what the visit and detector in use are.
@@ -66,60 +61,29 @@ function RunButtons(props: ParametersProps) {
6661
return (
6762
<Grid2 size={12}>
6863
<Stack direction={"row"} spacing={8} justifyContent={"center"}>
69-
{/* See
70-
https://github.com/DiamondLightSource/mx-daq-ui/issues/3?issue=DiamondLightSource%7Cmx-daq-ui%7C18 */}
71-
<Tooltip title="Start fixed target collection" placement="bottom">
72-
<Button
73-
variant="outlined"
74-
color="custom"
75-
size="large"
76-
onClick={() =>
77-
submitAndRunPlanImmediately({
78-
planName: "gui_run_chip_collection",
79-
planParams: {
80-
sub_dir: props.subDir,
81-
chip_name: props.chipName,
82-
exp_time: props.expTime,
83-
det_dist: props.detDist,
84-
transmission: props.transFract,
85-
n_shots: props.nShots,
86-
chip_type: props.chipType,
87-
map_type: props.mapType,
88-
chip_format: props.chipFormat,
89-
checker_pattern: props.checkerPattern,
90-
pump_probe: props.pumpProbe,
91-
laser_dwell: props.pumpInputs[0],
92-
laser_delay: props.pumpInputs[1],
93-
pre_pump: props.pumpInputs[2],
94-
},
95-
})
96-
}
97-
>
98-
<Typography
99-
variant="button"
100-
fontWeight="fontWeightBold"
101-
sx={{ display: "block" }}
102-
>
103-
Start!
104-
</Typography>
105-
</Button>
106-
</Tooltip>
107-
<Tooltip title="Abort current operation" placement="bottom">
108-
<Button
109-
color="custom"
110-
variant="outlined"
111-
size="large"
112-
onClick={() => abortCurrentPlan()}
113-
>
114-
<Typography
115-
variant="button"
116-
fontWeight="fontWeightBold"
117-
sx={{ display: "block" }}
118-
>
119-
Abort!
120-
</Typography>
121-
</Button>
122-
</Tooltip>
64+
<RunPlanButton
65+
btnLabel="Start!"
66+
planName="gui_run_chip_collection"
67+
planParams={{
68+
sub_dir: props.subDir,
69+
chip_name: props.chipName,
70+
exp_time: props.expTime,
71+
det_dist: props.detDist,
72+
transmission: props.transFract,
73+
n_shots: props.nShots,
74+
chip_type: props.chipType,
75+
map_type: props.mapType,
76+
chip_format: props.chipFormat,
77+
checker_pattern: props.checkerPattern,
78+
pump_probe: props.pumpProbe,
79+
laser_dwell: props.pumpInputs[0],
80+
laser_delay: props.pumpInputs[1],
81+
pre_pump: props.pumpInputs[2],
82+
}}
83+
title="Start fixed target collection"
84+
btnSize="large"
85+
/>
86+
<AbortButton />
12387
</Stack>
12488
</Grid2>
12589
);

0 commit comments

Comments
 (0)