camera-slider-web/src/App.js

373 lines
9.7 KiB
JavaScript

import { ArrowLeftOutlined, ArrowRightOutlined } from "@ant-design/icons";
import {
Button,
Card,
Col,
ConfigProvider,
Divider,
Form,
Grid,
InputNumber,
Progress,
Row,
Select,
Slider,
Space,
} from "antd";
import { useForm } from "antd/es/form/Form";
import useMessage from "antd/es/message/useMessage";
import { useEffect, useRef, useState } from "react";
import { myFetch } from "./utils";
const { useBreakpoint } = Grid;
const Constants = {
DISTANCE: {
MIN: 0,
MAX: 100,
},
ACCELERATION: {
MIN: 0,
MAX: 100,
},
DURATION: {
MIN: 1,
MAX: 60,
},
};
function App() {
const screens = useBreakpoint();
const [form] = useForm();
const [messageApi, messageContextHolder] = useMessage();
const [distance, setDistance] = useState([20, 50]);
const [acceleration, setAcceleration] = useState(0);
const [direction, setDirection] = useState(0);
const [durationUnit, setDurationUnit] = useState("seconds");
const remainingTimeRef = useRef(0);
const [remainingTime, setRemainingTime] = useState(0);
const [procesStatus, setProcessStatus] = useState("");
const intervalRef = useRef(null);
const setRemainingTimeProgress = (time) => {
remainingTimeRef.current = time;
setRemainingTime(time);
};
const showErrorMessage = () => {
messageApi.error({
type: "error",
content: "Ein Fehler ist aufgetreten",
});
};
useEffect(() => {
myFetch({
url: "/status",
method: "GET",
showNotification: messageApi,
})
.then((response) => {
console.log(response);
setRemainingTimeProgress(response.runningUntil);
setProcessStatus(response.status);
})
.catch((error) => {
console.error(error);
showErrorMessage();
});
}, []);
useEffect(() => {
intervalRef.current = setInterval(() => {
if (remainingTimeRef.current <= 0) {
clearInterval(intervalRef.current);
return;
}
setRemainingTime((prev) => {
const newTime = prev - 1000;
if (newTime <= 0) {
remainingTimeRef.current = 0;
clearInterval(intervalRef.current);
}
return newTime;
});
}, 1000);
return () => {
clearInterval(intervalRef.current);
};
}, [remainingTimeRef.current]);
const sendControlRequest = (urlPath) => {
form
.validateFields()
.then((values) => {
myFetch({
url: urlPath,
method: "POST",
body: {
distance: distance,
acceleration: acceleration,
direction: direction,
duration: values.duration * (durationUnit === "seconds" ? 1 : 60),
},
showNotification: messageApi,
})
.then((response) => {
console.log(response);
setRemainingTimeProgress(response.runningUntil);
setProcessStatus(response.status);
})
.catch((error) => {
console.error(error);
showErrorMessage();
setProcessStatus("");
setRemainingTimeProgress(0);
});
})
.catch((err) => console.error(err));
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
overflow: "auto",
marginTop: 60,
marginBottom: 60,
}}
>
{messageContextHolder}
<Card
title="Kamera Slider"
style={{
width: "90%",
maxWidth: 800,
}}
>
<Form
form={form}
layout="vertical"
onFinish={() => sendControlRequest("/start")}
>
<Form.Item
label={`Status${procesStatus !== "" ? `: ${procesStatus}` : ""}`}
>
<Progress
percent={
100 -
Math.round((remainingTime / remainingTimeRef.current) * 100)
}
type="line"
status="active"
strokeColor={{
from: "#108ee9",
to: "#87d068",
}}
/>
</Form.Item>
<Divider />
<Form.Item label="Distanz">
<Row>
<Col xs={24} md={4}>
<InputNumber
min={Constants.DISTANCE.MIN}
max={Constants.DISTANCE.MAX}
value={distance[0]}
step={1}
onChange={(value) =>
setDistance((newDistance) => {
const newData = [...newDistance];
newData[0] = value;
return newData;
})
}
/>
</Col>
<Col xs={24} md={16}>
<Slider
range
defaultValue={[20, 50]}
marks={{ 0: "0", 100: "100" }}
min={Constants.DISTANCE.MIN}
max={Constants.DISTANCE.MAX}
value={distance}
onChange={(values) => setDistance(values)}
/>
</Col>
<Col xs={24} md={4}>
<InputNumber
min={Constants.DISTANCE.MIN}
max={Constants.DISTANCE.MAX}
style={{
margin: screens.md ? "0 26px" : "",
}}
value={distance[1]}
step={1}
onChange={(value) =>
setDistance((newDistance) => {
const newData = [...newDistance];
newData[1] = value;
return newData;
})
}
/>
</Col>
</Row>
</Form.Item>
<Form.Item name="duration" label="Dauer" initialValue={5}>
<InputNumber
step={1}
min={Constants.DURATION.MIN}
max={Constants.DURATION.MAX}
addonAfter={
<Select
style={{
width: 120,
}}
value={durationUnit}
onChange={(value) => setDurationUnit(value)}
>
<Select.Option value="seconds">Sekunden</Select.Option>
<Select.Option value="minutes">Minuten</Select.Option>
</Select>
}
/>
</Form.Item>
<Form.Item label="Beschleunigung">
<Row>
<Col xs={24} md={20}>
<Slider
defaultValue={20}
marks={{ 0: "0", 100: "100" }}
min={Constants.ACCELERATION.MIN}
max={Constants.ACCELERATION.MAX}
value={acceleration}
onChange={(value) => setAcceleration(value)}
/>
</Col>
<Col xs={24} md={4}>
<InputNumber
min={Constants.ACCELERATION.MIN}
max={Constants.ACCELERATION.MAX}
style={{
margin: screens.md ? "0 26px" : "",
}}
value={acceleration}
onChange={(value) => setAcceleration(value)}
/>
</Col>
</Row>
</Form.Item>
<Form.Item label="Richtung">
<Space>
<Button
type={direction === 0 ? "primary" : "default"}
shape="round"
icon={<ArrowLeftOutlined />}
onClick={() => setDirection(0)}
>
Links
</Button>
<Button
type={direction === 1 ? "primary" : "default"}
shape="round"
icon={<ArrowRightOutlined />}
iconPosition="end"
onClick={() => setDirection(1)}
>
Rechts
</Button>
</Space>
</Form.Item>
<Form.Item>
<Row gutter={[16, 16]}>
<Col span={12}>
<Button
type="primary"
block
danger
onClick={() => {
myFetch({
url: "/stop",
method: "POST",
showNotification: messageApi,
})
.then((response) => {
console.log(response);
setRemainingTimeProgress(0);
setProcessStatus("Angehalten");
})
.catch((error) => {
console.error(error);
showErrorMessage();
setProcessStatus("");
setRemainingTimeProgress(0);
});
}}
>
Stop
</Button>
</Col>
<Col span={12}>
<ConfigProvider
theme={{
token: {
colorPrimary: "#27ae60",
},
}}
>
<Button type="primary" htmlType="submit" block>
Start
</Button>
</ConfigProvider>
</Col>
</Row>
</Form.Item>
</Form>
<Divider />
<Button
type="primary"
block
onClick={() => sendControlRequest("/moveNextPoint")}
>
Fahre zum nächsten Punkt
</Button>
</Card>
</div>
);
}
export default App;