Build Packages / Unit tests (push) Successful in 1h31m59s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 8m43s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 10m5s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 9m27s
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 8m56s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 9m24s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 10m27s
Build Packages / build:rpm (rocky8) (push) Successful in 9m20s
Build Packages / build:rpm (rocky9) (push) Successful in 10m50s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 9m54s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 8m38s
Build Packages / DIALS test (push) Successful in 12m13s
Build Packages / XDS test (durin plugin) (push) Successful in 7m8s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 7m8s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m50s
Build Packages / Generate python client (push) Successful in 16s
Build Packages / Build documentation (push) Successful in 50s
Build Packages / Create release (push) Skipped
This is an UNSTABLE release. It includes many experimental features, as well as many AI generated fixes. We recommend using rc.152 for production use. * jfjoch_broker: Add EXPERIMENTAL pixelrefine mode for image processing * jfjoch_broker: Allow to load user mask from 8-bit and 16-bit TIFF files * jfjoch_broker: Add ROI calculation in non-FPGA workflow * jfjoch_broker: Fixes to TCP image pusher * jfjoch_broker: Remove NUMA bindings * jfjoch_broker: Improvements to indexing * jfjoch_broker: For PSI EIGER, trimming energies are taken from the detector configuration (now compulsory) instead of hardcoded values * jfjoch_writer: Save ROI definitions and the per-pixel ROI bitmap in the master file; azimuthal ROIs support phi (angular) sectors * jfjoch_viewer: Major redesign with dockable panels and saved layouts, plus on-canvas creation/move/resize of box, circle and azimuthal ROIs * jfjoch_viewer: Run jfjoch_process reprocessing jobs from inside the GUI and overlay per-run results Reviewed-on: #63
271 lines
10 KiB
TypeScript
271 lines
10 KiB
TypeScript
import {ChangeEvent, memo, useState} from 'react';
|
|
|
|
import Paper from '@mui/material/Paper';
|
|
import {Stack, TextField, Radio, RadioGroup, FormControlLabel, FormControl, Checkbox} from "@mui/material";
|
|
import NumberTextField from "./NumberTextField";
|
|
import {dataset_settings, grid_scan, rotation_axis} from "../client";
|
|
import ButtonWithSnackbar from "./ButtonWithSnackbar";
|
|
|
|
type MyProps = {
|
|
frame_time_us: number
|
|
};
|
|
|
|
function DataCollection({frame_time_us}: MyProps) {
|
|
const [s, setS] = useState<dataset_settings>(() => ({
|
|
beam_x_pxl: 0,
|
|
beam_y_pxl: 0,
|
|
detector_distance_mm: 100,
|
|
incident_energy_keV: 12.398,
|
|
images_per_trigger: 1,
|
|
image_time_us: frame_time_us,
|
|
ntrigger: 1,
|
|
images_per_file: 1000
|
|
}));
|
|
const [goniometer, setGoniometer] = useState<rotation_axis>({
|
|
start: 0,
|
|
step: 0.1,
|
|
name: "omega",
|
|
vector: [0,1,0]
|
|
});
|
|
const [grid, setGrid] = useState<grid_scan>({
|
|
n_fast: 10,
|
|
step_x_um: 10.0,
|
|
step_y_um: 10.0,
|
|
vertical: false,
|
|
snake: false
|
|
});
|
|
const [beamXPxlErr, setBeamXPxlErr] = useState(false);
|
|
const [beamYPxlErr, setBeamYPxlErr] = useState(false);
|
|
const [detectorDistanceMmErr, setDetectorDistanceMmErr] = useState(false);
|
|
const [incidentEnergyKevErr, setIncidentEnergyKevErr] = useState(false);
|
|
const [imagesPerTriggerErr, setImagesPerTriggerErr] = useState(false);
|
|
const [imageTimeUsErr, setImageTimeUsErr] = useState(false);
|
|
const [ntriggerErr, setNtriggerErr] = useState(false);
|
|
const [imagesPerFileErr, setImagesPerFileErr] = useState(false);
|
|
const [mode, setMode] = useState<'still' | 'rotation' | 'grid'>('still');
|
|
|
|
const error = () : boolean =>
|
|
beamXPxlErr
|
|
|| beamYPxlErr
|
|
|| detectorDistanceMmErr
|
|
|| incidentEnergyKevErr
|
|
|| imageTimeUsErr
|
|
|| imagesPerTriggerErr
|
|
|| imagesPerFileErr
|
|
|| ntriggerErr;
|
|
|
|
const getDatasetSettings = () : dataset_settings => {
|
|
let d = {...s};
|
|
if (mode === 'rotation')
|
|
d.goniometer = goniometer;
|
|
else if (mode === 'grid')
|
|
d.grid_scan = grid;
|
|
return d;
|
|
};
|
|
|
|
return <Paper style={{textAlign: 'center'}} sx={{ width: '100%'}}>
|
|
<br/>
|
|
<Stack spacing={5} sx={{
|
|
justifyContent: "center",
|
|
alignItems: "center",
|
|
}}>
|
|
<TextField label={"File prefix"}
|
|
variant="outlined"
|
|
onChange={
|
|
(event: ChangeEvent<HTMLInputElement>) => {
|
|
setS(prev => ({...prev, file_prefix: event.target.value}));
|
|
}
|
|
}
|
|
value={s.file_prefix}
|
|
sx={{width:"90%"}}
|
|
/>
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label={"Number of images per trigger"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, images_per_trigger: val}));
|
|
setImagesPerTriggerErr(err);
|
|
}}
|
|
min={1}
|
|
default={s.images_per_trigger}
|
|
/>
|
|
<NumberTextField
|
|
label={"Number of triggers"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, ntrigger: val}));
|
|
setNtriggerErr(err);
|
|
}}
|
|
min={1}
|
|
default={s.ntrigger}
|
|
/>
|
|
<NumberTextField
|
|
label={"Image time"}
|
|
callback={(val: number, err: boolean) => {
|
|
let image_time_us = Math.round(val * 1000.0);
|
|
setS(prev => ({...prev, image_time_us: image_time_us}));
|
|
setImageTimeUsErr(err);
|
|
}}
|
|
units={"ms"}
|
|
float={true}
|
|
min={0.01}
|
|
default={(s.image_time_us ?? 500) / 1000.0}
|
|
/>
|
|
|
|
<NumberTextField
|
|
label={"Images per file"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, images_per_file: val}));
|
|
setImagesPerFileErr(err);
|
|
}}
|
|
float={false}
|
|
min={1}
|
|
default={s.images_per_file}
|
|
/>
|
|
</Stack>
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label={"Beam X [pxl]"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, beam_x_pxl: val}));
|
|
setBeamXPxlErr(err);
|
|
}}
|
|
units={"pxl"}
|
|
float={true}
|
|
default={s.beam_x_pxl}
|
|
/>
|
|
<NumberTextField
|
|
label={"Beam Y"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, beam_y_pxl: val}));
|
|
setBeamYPxlErr(err);
|
|
}}
|
|
units={"pxl"}
|
|
float={true}
|
|
default={s.beam_y_pxl}
|
|
/>
|
|
<NumberTextField
|
|
label={"Detector distance"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, detector_distance_mm: val}));
|
|
setDetectorDistanceMmErr(err);
|
|
}}
|
|
units={"mm"}
|
|
min={0.1}
|
|
float={true}
|
|
default={s.detector_distance_mm}
|
|
/>
|
|
<NumberTextField
|
|
label={"Energy"}
|
|
callback={(val: number, err: boolean) => {
|
|
setS(prev => ({...prev, incident_energy_keV: val}));
|
|
setIncidentEnergyKevErr(err);
|
|
}}
|
|
min={0.1}
|
|
max={500.0}
|
|
units={"keV"}
|
|
float={true}
|
|
default={s.incident_energy_keV}
|
|
/>
|
|
|
|
|
|
|
|
</Stack>
|
|
<FormControl sx={{width: '90%'}}>
|
|
<RadioGroup
|
|
row
|
|
value={mode}
|
|
onChange={(event) => setMode(event.target.value as 'still' | 'rotation' | 'grid')}>
|
|
<FormControlLabel value="still" control={<Radio/>} label="Still"/>
|
|
<FormControlLabel value="rotation" control={<Radio/>} label="Rotation"/>
|
|
<FormControlLabel value="grid" control={<Radio/>} label="Grid scan"/>
|
|
</RadioGroup>
|
|
</FormControl>
|
|
{mode === 'rotation' && (
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label="Start angle"
|
|
callback={(val: number) => setGoniometer(prev => ({...prev, start: val}))}
|
|
units="°"
|
|
float={true}
|
|
default={goniometer.start}
|
|
/>
|
|
<NumberTextField
|
|
label="Step"
|
|
callback={(val: number) => setGoniometer(prev => ({...prev, step: val}))}
|
|
units="°"
|
|
float={true}
|
|
min={0.001}
|
|
default={goniometer.step}
|
|
/>
|
|
<TextField
|
|
label="Axis name"
|
|
value={goniometer.name}
|
|
onChange={(e) => setGoniometer(prev => ({...prev, name: e.target.value}))}
|
|
/>
|
|
</Stack>
|
|
)}
|
|
{mode === 'grid' && (
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label="Grid elements (fast dim.)"
|
|
callback={(val: number) => setGrid(prev => ({...prev, n_fast: val}))}
|
|
min={1}
|
|
default={grid.n_fast}
|
|
/>
|
|
<NumberTextField
|
|
label="X step"
|
|
callback={(val: number) => setGrid(prev => ({...prev, step_x_um: val}))}
|
|
units="μm"
|
|
float={true}
|
|
default={grid.step_x_um}
|
|
/>
|
|
<NumberTextField
|
|
label="Y step"
|
|
callback={(val: number) => setGrid(prev => ({...prev, step_y_um: val}))}
|
|
units="μm"
|
|
float={true}
|
|
default={grid.step_y_um}
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={grid.snake}
|
|
onChange={(e) => setGrid(prev => ({...prev, snake: e.target.checked}))}
|
|
/>
|
|
}
|
|
label="Snake scan"
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={grid.vertical}
|
|
onChange={(e) => setGrid(prev => ({...prev, vertical: e.target.checked}))}
|
|
/>
|
|
}
|
|
label="Vertical scan"
|
|
/>
|
|
</Stack>
|
|
)}
|
|
|
|
<Stack direction="row" spacing={2}>
|
|
<ButtonWithSnackbar
|
|
path={"/start"}
|
|
text={"start"}
|
|
disabled={error()}
|
|
input={JSON.stringify(getDatasetSettings())}
|
|
color={"primary"}
|
|
/>
|
|
<ButtonWithSnackbar
|
|
path={"/trigger"}
|
|
text={"soft trigger"}
|
|
disabled={error()}
|
|
color={"primary"}
|
|
/>
|
|
</Stack>
|
|
</Stack>
|
|
<br/>
|
|
</Paper>
|
|
}
|
|
|
|
export default memo(DataCollection);
|