341 lines
14 KiB
TypeScript
341 lines
14 KiB
TypeScript
import React from 'react';
|
|
|
|
import Paper from '@mui/material/Paper';
|
|
import {Stack, TextField, Radio, RadioGroup, FormControlLabel, FormControl, FormLabel, Checkbox} from "@mui/material";
|
|
import NumberTextField from "./NumberTextField";
|
|
import {dataset_settings, grid_scan, rotation_axis} from "../openapi";
|
|
import ButtonWithSnackbar from "./ButtonWithSnackbar";
|
|
|
|
type MyProps = {
|
|
frame_time_us: number
|
|
};
|
|
|
|
type MyState = {
|
|
s: dataset_settings,
|
|
beam_x_pxl_err: boolean,
|
|
beam_y_pxl_err: boolean,
|
|
detector_distance_mm_err: boolean,
|
|
incident_energy_kev_err: boolean,
|
|
images_per_trigger_err: boolean,
|
|
images_per_file_err: boolean,
|
|
image_time_us_err: boolean,
|
|
ntrigger_err: boolean,
|
|
mode: 'still' | 'rotation' | 'grid',
|
|
goniometer: rotation_axis,
|
|
grid: grid_scan,
|
|
};
|
|
|
|
class DataCollection extends React.Component<MyProps, MyState> {
|
|
state : MyState = {
|
|
s : {
|
|
beam_x_pxl: 0,
|
|
beam_y_pxl: 0,
|
|
detector_distance_mm: 100,
|
|
incident_energy_keV: 12.398,
|
|
images_per_trigger: 1,
|
|
image_time_us: this.props.frame_time_us,
|
|
ntrigger: 1,
|
|
images_per_file: 1000
|
|
},
|
|
goniometer : {
|
|
start: 0,
|
|
step: 0.1,
|
|
name: "omega",
|
|
vector: [0,1,0]
|
|
},
|
|
grid: {
|
|
n_fast: 10,
|
|
step_x_um: 10.0,
|
|
step_y_um: 10.0,
|
|
vertical: false,
|
|
snake: false
|
|
},
|
|
beam_x_pxl_err: false,
|
|
beam_y_pxl_err: false,
|
|
detector_distance_mm_err: false,
|
|
incident_energy_kev_err: false,
|
|
images_per_trigger_err: false,
|
|
image_time_us_err: false,
|
|
ntrigger_err: false,
|
|
images_per_file_err: false,
|
|
mode: 'still',
|
|
}
|
|
|
|
error() : boolean {
|
|
return this.state.beam_x_pxl_err
|
|
|| this.state.beam_y_pxl_err
|
|
|| this.state.detector_distance_mm_err
|
|
|| this.state.incident_energy_kev_err
|
|
|| this.state.image_time_us_err
|
|
|| this.state.images_per_trigger_err
|
|
|| this.state.images_per_file_err
|
|
|| this.state.ntrigger_err;
|
|
}
|
|
|
|
getDatasetSettings = () => {
|
|
let d = this.state.s;
|
|
if (this.state.mode === 'rotation')
|
|
d.goniometer = this.state.goniometer;
|
|
else if (this.state.mode === 'grid')
|
|
d.grid_scan = this.state.grid;
|
|
return d;
|
|
}
|
|
|
|
render() {
|
|
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: React.ChangeEvent<HTMLInputElement>) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, file_prefix: event.target.value}
|
|
}));
|
|
}
|
|
}
|
|
value={this.state.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) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, images_per_trigger: val},
|
|
images_per_trigger_err: err
|
|
}
|
|
));
|
|
}}
|
|
min={1}
|
|
default={this.state.s.images_per_trigger}
|
|
/>
|
|
<NumberTextField
|
|
label={"Number of triggers"}
|
|
callback={(val: number, err: boolean) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, ntrigger: val},
|
|
ntrigger_err: err
|
|
}
|
|
));
|
|
}}
|
|
min={1}
|
|
default={this.state.s.ntrigger}
|
|
/>
|
|
<NumberTextField
|
|
label={"Image time"}
|
|
callback={(val: number, err: boolean) => {
|
|
let image_time_us = Math.round(val * 1000.0);
|
|
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s,
|
|
image_time_us: image_time_us
|
|
},
|
|
image_time_us_err: err
|
|
}
|
|
));
|
|
}}
|
|
units={"ms"}
|
|
float={true}
|
|
min={0.01}
|
|
default={(this.state.s.image_time_us ?? 500) / 1000.0}
|
|
/>
|
|
|
|
<NumberTextField
|
|
label={"Images per file"}
|
|
callback={(val: number, err: boolean) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s,
|
|
images_per_file: val
|
|
},
|
|
images_per_file_err: err
|
|
}
|
|
));
|
|
}}
|
|
float={false}
|
|
min={1}
|
|
default={this.state.s.images_per_file}
|
|
/>
|
|
</Stack>
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label={"Beam X [pxl]"}
|
|
callback={(val: number, err: boolean) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, beam_x_pxl: val},
|
|
beam_x_pxl_err: err
|
|
}
|
|
));
|
|
}}
|
|
units={"pxl"}
|
|
float={true}
|
|
default={this.state.s.beam_x_pxl}
|
|
/>
|
|
<NumberTextField
|
|
label={"Beam Y"}
|
|
callback={(val: number, err: boolean) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, beam_y_pxl: val},
|
|
beam_y_pxl_err: err
|
|
}
|
|
));
|
|
}}
|
|
units={"pxl"}
|
|
float={true}
|
|
default={this.state.s.beam_y_pxl}
|
|
/>
|
|
<NumberTextField
|
|
label={"Detector distance"}
|
|
callback={(val: number, err: boolean) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, detector_distance_mm: val},
|
|
detector_distance_mm_err: err
|
|
}
|
|
));
|
|
}}
|
|
units={"mm"}
|
|
min={0.1}
|
|
float={true}
|
|
default={this.state.s.detector_distance_mm}
|
|
/>
|
|
<NumberTextField
|
|
label={"Energy"}
|
|
callback={(val: number, err: boolean) => {
|
|
this.setState(prevState => ({
|
|
s: {...prevState.s, incident_energy_keV: val},
|
|
incident_energy_kev_err: err
|
|
}
|
|
));
|
|
}}
|
|
min={0.1}
|
|
max={500.0}
|
|
units={"keV"}
|
|
float={true}
|
|
default={this.state.s.incident_energy_keV}
|
|
/>
|
|
|
|
|
|
|
|
</Stack>
|
|
<FormControl sx={{width: '90%'}}>
|
|
<RadioGroup
|
|
row
|
|
value={this.state.mode}
|
|
onChange={(event) => this.setState({mode: 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>
|
|
{this.state.mode === 'rotation' && (
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label="Start angle"
|
|
callback={(val: number) => this.setState(prevState => (
|
|
{goniometer : {...prevState.goniometer, start: val}}
|
|
))}
|
|
units="°"
|
|
float={true}
|
|
default={this.state.goniometer.start}
|
|
/>
|
|
<NumberTextField
|
|
label="Step"
|
|
callback={(val: number) => this.setState(prevState => (
|
|
{goniometer : {...prevState.goniometer, step: val}}
|
|
))}
|
|
units="°"
|
|
float={true}
|
|
min={0.001}
|
|
default={this.state.goniometer.step}
|
|
/>
|
|
<TextField
|
|
label="Axis name"
|
|
value={this.state.goniometer.name}
|
|
onChange={(e) => this.setState(prevState => (
|
|
{goniometer : {...prevState.goniometer, name: e.target.value}}
|
|
))}
|
|
/>
|
|
</Stack>
|
|
)}
|
|
{this.state.mode === 'grid' && (
|
|
<Stack direction="row" spacing={2} sx={{width: '90%'}}>
|
|
<NumberTextField
|
|
label="Grid elements (fast dim.)"
|
|
callback={(val: number) => this.setState((e) => this.setState(
|
|
prevState => (
|
|
{grid : {...prevState.grid, n_fast: val}}
|
|
)))}
|
|
min={1}
|
|
default={this.state.grid.n_fast}
|
|
/>
|
|
<NumberTextField
|
|
label="X step"
|
|
callback={(val: number) => this.setState((e) => this.setState(
|
|
prevState => (
|
|
{grid : {...prevState.grid, step_x_um: val}}
|
|
)))}
|
|
units="μm"
|
|
float={true}
|
|
default={this.state.grid.step_x_um}
|
|
/>
|
|
<NumberTextField
|
|
label="Y step"
|
|
callback={(val: number) => this.setState((e) => this.setState(
|
|
prevState => (
|
|
{grid : {...prevState.grid, step_y_um: val}}
|
|
)))}
|
|
units="μm"
|
|
float={true}
|
|
default={this.state.grid.step_y_um}
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={this.state.grid.snake}
|
|
onChange={(e) => this.setState(prevState => (
|
|
{grid : {...prevState.grid, snake: e.target.checked}}
|
|
))}
|
|
/>
|
|
}
|
|
label="Snake scan"
|
|
/>
|
|
<FormControlLabel
|
|
control={
|
|
<Checkbox
|
|
checked={this.state.grid.vertical}
|
|
onChange={(e) => this.setState(prevState => (
|
|
{grid : {...prevState.grid, vertical: e.target.checked}}
|
|
))}
|
|
/>
|
|
}
|
|
label="Vertical scan"
|
|
/>
|
|
</Stack>
|
|
)}
|
|
|
|
<Stack direction="row" spacing={2}>
|
|
<ButtonWithSnackbar
|
|
path={"/start"}
|
|
text={"start"}
|
|
disabled={this.error()}
|
|
input={JSON.stringify(this.getDatasetSettings())}
|
|
color={"primary"}
|
|
/>
|
|
<ButtonWithSnackbar
|
|
path={"/trigger"}
|
|
text={"soft trigger"}
|
|
disabled={this.error()}
|
|
color={"primary"}
|
|
/>
|
|
</Stack>
|
|
</Stack>
|
|
<br/>
|
|
</Paper>
|
|
}
|
|
}
|
|
|
|
export default DataCollection;
|