Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 11m26s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 12m45s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 14m0s
Build Packages / build:rpm (rocky8) (push) Successful in 14m3s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 14m16s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 14m46s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 15m46s
Build Packages / XDS test (durin plugin) (push) Successful in 8m3s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 9m51s
Build Packages / Generate python client (push) Successful in 22s
Build Packages / Create release (push) Skipped
Build Packages / build:rpm (rocky9) (push) Successful in 11m30s
Build Packages / Build documentation (push) Successful in 41s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m49s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m53s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 9m54s
Build Packages / DIALS test (push) Successful in 11m46s
Build Packages / Unit tests (push) Successful in 59m17s
Match the unsaved-changes dot: the Upload button uses the warning color while the panel has unedited changes, primary otherwise. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
446 lines
20 KiB
TypeScript
446 lines
20 KiB
TypeScript
import {ChangeEvent, memo, useEffect, useState} from 'react';
|
|
|
|
import {Checkbox, FormControlLabel, FormGroup, Grid, List, ListItem, Switch, Tooltip} from "@mui/material";
|
|
import Paper from "@mui/material/Paper";
|
|
import FormControl from "@mui/material/FormControl";
|
|
import InputLabel from "@mui/material/InputLabel";
|
|
import Select, {SelectChangeEvent} from "@mui/material/Select";
|
|
import MenuItem from "@mui/material/MenuItem";
|
|
import {detector_settings, detector_timing} from "../client";
|
|
import {putConfigDetectorMutation} from "../client/@tanstack/react-query.gen";
|
|
import NumberTextField from "./NumberTextField";
|
|
import _ from "lodash";
|
|
import ButtonWithSnackbar from "./ButtonWithSnackbar";
|
|
import Button from "@mui/material/Button";
|
|
import {PanelTitle, UploadSnackbarView} from "./SettingsPanel";
|
|
import {useUpload} from "./useUpload";
|
|
|
|
type MyProps = {
|
|
s?: detector_settings
|
|
}
|
|
|
|
function extractDepthForSelection(input: detector_settings) : string {
|
|
if (input.eiger_bit_depth === undefined)
|
|
return 'a';
|
|
else if (input.eiger_bit_depth === 16)
|
|
return '16';
|
|
else if (input.eiger_bit_depth === 32)
|
|
return '32';
|
|
else if (input.eiger_bit_depth === 8)
|
|
return '8';
|
|
else
|
|
return "";
|
|
}
|
|
|
|
function DetectorSettings({s: serverS}: MyProps) {
|
|
const [s, setS] = useState<detector_settings>({ frame_time_us: 1000 });
|
|
const [lastDownloadedS, setLastDownloadedS] = useState<detector_settings>({ frame_time_us: 1000 });
|
|
const [bitWidthSelection, setBitWidthSelection] = useState("a");
|
|
const [storageCellListValue, setStorageCellListValue] = useState("1");
|
|
const [timingModeListValue, setTimingModeListValue] = useState<detector_timing>(detector_timing.TRIGGER);
|
|
const [frameTimeErr, setFrameTimeErr] = useState(false);
|
|
const [countTimeErr, setCountTimeErr] = useState(false);
|
|
const [storageCellDelayErr, setStorageCellDelayErr] = useState(false);
|
|
const [detectorTriggerDelayErr, setDetectorTriggerDelayErr] = useState(false);
|
|
const [internalFrameGeneratorImagesErr, setInternalFrameGeneratorImagesErr] = useState(false);
|
|
const [pedestalG0FramesErr, setPedestalG0FramesErr] = useState(false);
|
|
const [pedestalG1FramesErr, setPedestalG1FramesErr] = useState(false);
|
|
const [pedestalG2FramesErr, setPedestalG2FramesErr] = useState(false);
|
|
const [pedestalMinImageCountErr, setPedestalMinImageCountErr] = useState(false);
|
|
const [eigerThresholdErr, setEigerThresholdErr] = useState(false);
|
|
const [eigerThresholdKeVOld, setEigerThresholdKeVOld] = useState(6.0);
|
|
const [downloadCounter, setDownloadCounter] = useState(0);
|
|
const { submit, pending, snackbar } = useUpload(putConfigDetectorMutation());
|
|
|
|
useEffect(() => {
|
|
if ((serverS !== undefined) && !_.isEqual(serverS, lastDownloadedS)) {
|
|
setDownloadCounter(c => c + 1);
|
|
setS(serverS);
|
|
setBitWidthSelection(extractDepthForSelection(serverS));
|
|
setLastDownloadedS(serverS);
|
|
setStorageCellListValue(String(serverS.jungfrau_storage_cell_count ?? "1"));
|
|
setTimingModeListValue(serverS.timing ?? detector_timing.TRIGGER);
|
|
setPedestalG0FramesErr(false);
|
|
setPedestalG1FramesErr(false);
|
|
setPedestalG2FramesErr(false);
|
|
setPedestalMinImageCountErr(false);
|
|
setStorageCellDelayErr(false);
|
|
setDetectorTriggerDelayErr(false);
|
|
setFrameTimeErr(false);
|
|
setCountTimeErr(false);
|
|
setInternalFrameGeneratorImagesErr(false);
|
|
setEigerThresholdErr(false);
|
|
setEigerThresholdKeVOld(6.0);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [serverS]);
|
|
|
|
const eigerThresholdToggle = (event: ChangeEvent<HTMLInputElement>) => {
|
|
if (event.target.checked)
|
|
setS(prev => ({...prev, eiger_threshold_keV: eigerThresholdKeVOld}));
|
|
else
|
|
setS(prev => ({...prev, eiger_threshold_keV: undefined}));
|
|
};
|
|
|
|
const countTimeToggle = (event: ChangeEvent<HTMLInputElement>) => {
|
|
if (event.target.checked)
|
|
setS(prev => ({...prev, count_time_us: prev.frame_time_us - 20}));
|
|
else
|
|
setS(prev => ({...prev, count_time_us: undefined}));
|
|
};
|
|
|
|
const internalFrameGeneratorToggle = (event: ChangeEvent<HTMLInputElement>) => {
|
|
setS(prev => ({...prev, internal_frame_generator: event.target.checked}));
|
|
};
|
|
|
|
const useGainHG0Toggle = (event: ChangeEvent<HTMLInputElement>) => {
|
|
setS(prev => ({...prev, jungfrau_use_gain_hg0: event.target.checked}));
|
|
};
|
|
|
|
const fixedGainG1Toggle = (event: ChangeEvent<HTMLInputElement>) => {
|
|
setS(prev => ({...prev, jungfrau_fixed_gain_g1: event.target.checked}));
|
|
};
|
|
|
|
const handleBitWidthChange = (event: SelectChangeEvent) => {
|
|
let val: detector_settings['eiger_bit_depth'] = undefined;
|
|
|
|
if (event.target.value === "a")
|
|
val = undefined;
|
|
else if (event.target.value === "8")
|
|
val = 8;
|
|
else if (event.target.value === "16")
|
|
val = 16;
|
|
else if (event.target.value === "32")
|
|
val = 32;
|
|
|
|
setBitWidthSelection(event.target.value);
|
|
setS(prev => ({...prev, eiger_bit_depth: val}));
|
|
};
|
|
|
|
const handleTimingChange = (event : SelectChangeEvent<detector_timing>) => {
|
|
let d : detector_timing = detector_timing.TRIGGER;
|
|
|
|
switch (event.target.value) {
|
|
case "trigger":
|
|
d = detector_timing.TRIGGER;
|
|
break;
|
|
case "auto":
|
|
d = detector_timing.AUTO;
|
|
break;
|
|
case "burst":
|
|
d = detector_timing.BURST;
|
|
break;
|
|
case "gated":
|
|
d = detector_timing.GATED;
|
|
break;
|
|
}
|
|
|
|
setTimingModeListValue(d);
|
|
setS(prev => ({...prev, timing: d}));
|
|
};
|
|
|
|
const handleChange = (event : SelectChangeEvent<String>) => {
|
|
setStorageCellListValue(String(event.target.value).toString());
|
|
setS(prev => ({...prev, jungfrau_storage_cell_count: Number(event.target.value)}));
|
|
};
|
|
|
|
const dirty = !_.isEqual(s, lastDownloadedS);
|
|
|
|
return <Paper style={{textAlign: 'center'}} sx={{ height: 1150, width: '100%' }}>
|
|
|
|
<Grid container>
|
|
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<br/><PanelTitle title="Detector settings" dirty={dirty}/>
|
|
<br/><br/><br/>
|
|
<NumberTextField start_val={s.frame_time_us}
|
|
label={"Internal frame time"}
|
|
units={"us"}
|
|
min={450}
|
|
fullWidth
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setFrameTimeErr(err);
|
|
setS(prev => ({...prev, frame_time_us: val}));
|
|
}}/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={2}/>
|
|
<Grid item xs={8}>
|
|
<List>
|
|
<ListItem>
|
|
<Switch onChange={countTimeToggle}
|
|
checked={s.count_time_us !== undefined}/>
|
|
<NumberTextField
|
|
start_val={s.count_time_us}
|
|
disabled={s.count_time_us === undefined}
|
|
label={"Count time"}
|
|
units={"us"}
|
|
min={0}
|
|
max={1980}
|
|
fullWidth
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setCountTimeErr(err);
|
|
setS(prev => ({...prev, count_time_us: val}));
|
|
}}/>
|
|
</ListItem>
|
|
</List>
|
|
</Grid>
|
|
<Grid item xs={2}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<NumberTextField start_val={s.detector_trigger_delay_ns}
|
|
label={"Detector delay"}
|
|
units={"ns"}
|
|
default={0}
|
|
min={0}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setDetectorTriggerDelayErr(err);
|
|
setS(prev => ({...prev, detector_trigger_delay_ns: val}));
|
|
}}
|
|
sx={{width: 120}}
|
|
/>
|
|
<FormControl>
|
|
<InputLabel id="timing-select-label">Timing mode</InputLabel>
|
|
<Select
|
|
sx={{width: 120}}
|
|
labelId="timing-select-label"
|
|
id="timing-select"
|
|
value={timingModeListValue}
|
|
label="Storage cell count"
|
|
onChange={handleTimingChange}
|
|
>
|
|
<MenuItem value={"trigger"}>Trigger</MenuItem>
|
|
<MenuItem value={"auto"}>Auto</MenuItem>
|
|
<MenuItem value={"burst"}>Burst (EIGER)</MenuItem>
|
|
<MenuItem value={"gated"}>Gated (EIGER)</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
<br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<b>Internal image generator (FPGA) </b><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={3}/>
|
|
<Grid item xs={6}>
|
|
<br/>
|
|
<List>
|
|
<Tooltip title="Internal generator allows to test Jungfraujoch without having detector connected.">
|
|
<ListItem>
|
|
<Checkbox onChange={internalFrameGeneratorToggle}
|
|
checked={s.internal_frame_generator === true}/>
|
|
<FormControl fullWidth>
|
|
<NumberTextField start_val={s.internal_frame_generator_images}
|
|
label={"Images"}
|
|
min={1}
|
|
default={1}
|
|
max={128}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setInternalFrameGeneratorImagesErr(err);
|
|
setS(prev => ({...prev, internal_frame_generator_images: val}));
|
|
}}
|
|
disabled={!s.internal_frame_generator}
|
|
fullWidth/>
|
|
</FormControl>
|
|
</ListItem>
|
|
</Tooltip>
|
|
</List>
|
|
</Grid>
|
|
<Grid item xs={3}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<b>JUNGFRAU settings </b><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={3}/>
|
|
<Grid item xs={6}>
|
|
<FormGroup>
|
|
<FormControlLabel control={
|
|
<Checkbox onChange={useGainHG0Toggle} checked={s.jungfrau_use_gain_hg0 === true}/>
|
|
} label="HG0"/>
|
|
<FormControlLabel control={
|
|
<Checkbox onChange={fixedGainG1Toggle} checked={s.jungfrau_fixed_gain_g1 === true}/>
|
|
} label="Fixed G1"/>
|
|
</FormGroup><br/>
|
|
</Grid>
|
|
<Grid item xs={3}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<b>JUNGFRAU storage cells </b><br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<FormControl>
|
|
<InputLabel id="demo-simple-select-label">Count</InputLabel>
|
|
<Select
|
|
sx={{width: 120}}
|
|
labelId="demo-simple-select-label"
|
|
id="demo-simple-select"
|
|
value={storageCellListValue}
|
|
label="Storage cell count"
|
|
onChange={handleChange}
|
|
>
|
|
<MenuItem value={"1"}>1</MenuItem>
|
|
<MenuItem value={"2"}>2</MenuItem>
|
|
<MenuItem value={"4"}>4</MenuItem>
|
|
<MenuItem value={"8"}>8</MenuItem>
|
|
<MenuItem value={"15"}>15</MenuItem>
|
|
<MenuItem value={"16"}>16</MenuItem>
|
|
</Select>
|
|
</FormControl>
|
|
<NumberTextField start_val={s.jungfrau_storage_cell_delay_ns}
|
|
label={"Delay"}
|
|
units={"ns"}
|
|
min={2100}
|
|
default={2100}
|
|
counter={downloadCounter}
|
|
sx={{ width: "140px" }}
|
|
callback={(val: number, err: boolean) => {
|
|
setStorageCellDelayErr(err);
|
|
setS(prev => ({...prev, jungfrau_storage_cell_delay_ns: val}));
|
|
}}
|
|
disabled={storageCellListValue === "1"}
|
|
fullWidth/>
|
|
<br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<b>JUNGFRAU Pedestal</b><br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<NumberTextField start_val={s.jungfrau_pedestal_g0_frames}
|
|
label={"G0"}
|
|
min={0}
|
|
default={2000}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setPedestalG0FramesErr(err);
|
|
setS(prev => ({...prev, jungfrau_pedestal_g0_frames: val}));
|
|
}}
|
|
sx={{width: "80px"}}/>
|
|
<NumberTextField start_val={s.jungfrau_pedestal_g1_frames}
|
|
label={"G1"}
|
|
min={0}
|
|
default={300}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setPedestalG1FramesErr(err);
|
|
setS(prev => ({...prev, jungfrau_pedestal_g1_frames: val}));
|
|
}}
|
|
sx={{width: "80px"}}/>
|
|
<NumberTextField start_val={s.jungfrau_pedestal_g2_frames}
|
|
label={"G2"}
|
|
min={0}
|
|
default={300}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setPedestalG2FramesErr(err);
|
|
setS(prev => ({...prev, jungfrau_pedestal_g2_frames: val}));
|
|
}}
|
|
sx={{width: "80px"}}/>
|
|
<br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
|
|
<Grid item xs={10}>
|
|
<NumberTextField start_val={s.jungfrau_pedestal_min_image_count}
|
|
label={"Min. image count"}
|
|
min={0}
|
|
default={128}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
setPedestalMinImageCountErr(err);
|
|
setS(prev => ({...prev, jungfrau_pedestal_min_image_count: val}));
|
|
}}
|
|
sx={{width: "120px"}}/><br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<b>EIGER settings </b><br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<ListItem>
|
|
<Checkbox onChange={eigerThresholdToggle}
|
|
checked={s.eiger_threshold_keV !== undefined}/>
|
|
<FormControl fullWidth>
|
|
<NumberTextField
|
|
default={eigerThresholdKeVOld}
|
|
start_val={s.eiger_threshold_keV}
|
|
label={"Energy threshold"}
|
|
min={1.0}
|
|
max={100.0}
|
|
units={"keV"}
|
|
float={true}
|
|
counter={downloadCounter}
|
|
callback={(val: number, err: boolean) => {
|
|
if (!err)
|
|
setEigerThresholdKeVOld(val);
|
|
setEigerThresholdErr(err);
|
|
setS(prev => ({...prev, eiger_threshold_keV: val}));
|
|
}}
|
|
disabled={s.eiger_threshold_keV === undefined}
|
|
/>
|
|
</FormControl>
|
|
<FormControl>
|
|
<InputLabel id="demo-simple-select-label">Read-out</InputLabel>
|
|
<Select
|
|
labelId="demo-simple-select-label"
|
|
id="demo-simple-select"
|
|
label="Detector"
|
|
value={bitWidthSelection}
|
|
onChange={handleBitWidthChange}
|
|
sx={{minWidth:106}}
|
|
>
|
|
<MenuItem value={"a"}>Auto</MenuItem>
|
|
<MenuItem value={"8"}>8-bit</MenuItem>
|
|
<MenuItem value={"16"}>16-bit</MenuItem>
|
|
<MenuItem value={"32"}>32-bit</MenuItem>
|
|
</Select>
|
|
</FormControl><br/><br/>
|
|
</ListItem><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={1}/>
|
|
<Grid item xs={10}>
|
|
<Button color={dirty ? "warning" : "primary"} variant="contained" disableElevation
|
|
onClick={() => submit(s)}
|
|
disabled={pending
|
|
|| countTimeErr
|
|
|| frameTimeErr
|
|
|| internalFrameGeneratorImagesErr
|
|
|| detectorTriggerDelayErr
|
|
|| storageCellDelayErr
|
|
|| pedestalG0FramesErr
|
|
|| pedestalG1FramesErr
|
|
|| pedestalG2FramesErr
|
|
|| pedestalMinImageCountErr}>Upload</Button>
|
|
<ButtonWithSnackbar
|
|
color={"primary"}
|
|
path={"/deactivate"}
|
|
text={"Deactivate"}
|
|
/>
|
|
<br/><br/>
|
|
</Grid>
|
|
<Grid item xs={1}/>
|
|
</Grid>
|
|
<UploadSnackbarView snackbar={snackbar}/>
|
|
</Paper>
|
|
}
|
|
|
|
export default memo(DetectorSettings);
|