无源标靶上位机
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

630 lines
22 KiB

import React, { useEffect } from "react";
import {
Card,
Input,
Slider,
InputNumber,
Switch,
Select,
Row,
Col,
Button,
message,
Space,
Typography,
Spin,
Alert,
} from "antd";
import {
SettingOutlined,
SaveOutlined,
DatabaseOutlined,
ClockCircleOutlined,
BellOutlined,
FilterOutlined,
CloudUploadOutlined,
SyncOutlined,
} from "@ant-design/icons";
import useAdvancedSettings from "../hooks/useAdvancedSettings";
const { Option } = Select;
const { Title, Text } = Typography;
const AdvancedSettings = ({ onLogout }) => {
// 使用高级配置 Hook
const {
settings,
loading,
isConnected,
isReady,
fetchAllSettings,
saveAllSettings,
resetSettings,
updateLocalSettings,
} = useAdvancedSettings();
// 只在首次 isReady 变为 true 时自动拉取配置
const hasFetchedRef = React.useRef(false);
useEffect(() => {
if (isReady && !hasFetchedRef.current) {
fetchAllSettings();
hasFetchedRef.current = true;
}
}, [isReady, fetchAllSettings]);
// 从 settings 中提取配置
const deviceId = settings.deviceId;
const fps = settings.dataFps;
const enableOfflineAlert = settings.alertConfig?.enable ?? false;
const offlineThreshold = settings.alertConfig?.intervalSec ?? 60;
const enableFiltering = settings.filterConfig?.enable ?? true;
const filterMethod = settings.filterConfig?.method ?? 'median';
const windowSize = settings.filterConfig?.size ?? 5;
const filterThreshold = settings.filterConfig?.threshold ?? 0.1;
const flowThreshold = settings.filterConfig?.imgThreshold ?? 10.0;
const enableMqtt = settings.mqttConfig?.enable ?? true;
const brokerAddress = settings.mqttConfig?.mqtt?.broker ?? '';
const mqttPort = settings.mqttConfig?.mqtt?.port ?? 1883;
const mqttTopic = settings.mqttConfig?.mqtt?.topic ?? '';
const mqttClientId = settings.mqttConfig?.mqtt?.client_id ?? '';
const mqttUsername = settings.mqttConfig?.mqtt?.username ?? '';
const mqttPassword = settings.mqttConfig?.mqtt?.password ?? '';
// 更新本地状态的辅助函数
const setDeviceId = (value) => updateLocalSettings({ deviceId: value });
const setFps = (value) => updateLocalSettings({ dataFps: value });
const setEnableOfflineAlert = (value) =>
updateLocalSettings({
alertConfig: {
enable: value,
intervalSec: settings.alertConfig?.intervalSec ?? 60
},
});
const setOfflineThreshold = (value) =>
updateLocalSettings({
alertConfig: {
enable: settings.alertConfig?.enable ?? false,
intervalSec: value
},
});
const setEnableFiltering = (value) =>
updateLocalSettings({
filterConfig: { ...settings.filterConfig, enable: value },
});
const setFilterMethod = (value) =>
updateLocalSettings({
filterConfig: { ...settings.filterConfig, method: value },
});
const setWindowSize = (value) =>
updateLocalSettings({
filterConfig: { ...settings.filterConfig, size: value },
});
const setFilterThreshold = (value) =>
updateLocalSettings({
filterConfig: { ...settings.filterConfig, threshold: -Math.abs(value) },
});
const setFlowThreshold = (value) =>
updateLocalSettings({
filterConfig: { ...settings.filterConfig, imgThreshold: value },
});
const setEnableMqtt = (value) =>
updateLocalSettings({
mqttConfig: {
enable: value,
mqtt: settings.mqttConfig?.mqtt ?? {
broker: '',
port: 1883,
topic: '',
username: '',
password: '',
client_id: ''
}
},
});
const setBrokerAddress = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
mqtt: {
...(settings.mqttConfig?.mqtt ?? {}),
broker: value
},
},
});
const setMqttPort = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
mqtt: {
...(settings.mqttConfig?.mqtt ?? {}),
port: value
},
},
});
const setMqttTopic = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
mqtt: {
...(settings.mqttConfig?.mqtt ?? {}),
topic: value
},
},
});
const setMqttClientId = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
mqtt: {
...(settings.mqttConfig?.mqtt ?? {}),
client_id: value
},
},
});
const setMqttUsername = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
mqtt: {
...(settings.mqttConfig?.mqtt ?? {}),
username: value
},
},
});
const setMqttPassword = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
mqtt: {
...(settings.mqttConfig?.mqtt ?? {}),
password: value
},
},
});
const handleSave = () => {
saveAllSettings();
};
const handleReset = () => {
fetchAllSettings();
};
return (
<Spin spinning={loading} tip="正在加载配置...">
<div
style={{
padding: "12px",
backgroundColor: "#f5f7fa",
minHeight: "calc(100vh - 92px)",
}}
>
{/* WebSocket 连接状态提示 */}
{!isConnected && (
<Alert
message="WebSocket 未连接"
description="当前未连接到设备,无法读取或保存配置。请检查网络连接。"
type="warning"
showIcon
style={{ marginBottom: 12, borderRadius: 8 }}
/>
)}
<Card
style={{
marginBottom: 12,
background:
"linear-gradient(135deg, #01152cff 0%, #063b77ff 100%)",
borderRadius: 12,
}}
styles={{ padding: 12 }}
>
<Row justify="space-between" align="middle">
<Col>
<Space direction="vertical" size={4}>
<Title
level={2}
style={{
margin: 0,
color: "white",
display: "flex",
alignItems: "center",
gap: 12,
}}
>
<SettingOutlined />
高级参数配置
</Title>
<Text
style={{
color: "rgba(255,255,255,0.85)",
fontSize: 14,
}}
>
针对下位机运行的参数配置修改后请及时保存(慎用)
</Text>
</Space>
</Col>
<Col>
<Space size="middle">
<Button
icon={<SyncOutlined spin={loading} />}
onClick={handleReset}
size="large"
disabled={!isConnected || loading}
style={{ height: 42, borderRadius: 8 }}
>
刷新配置
</Button>
<Button
type="primary"
icon={<SaveOutlined />}
onClick={handleSave}
size="large"
disabled={!isConnected || loading}
style={{
height: 42,
borderRadius: 8,
background: "white",
color: "#667eea",
borderColor: "white",
}}
>
保存更改
</Button>
</Space>
</Col>
</Row>
</Card>
<Row gutter={12}>
<Col xs={12} lg={12}>
<Card
title={
<Space>
<DatabaseOutlined style={{ color: "#667eea" }} />
<span>基础设备信息</span>
</Space>
}
style={{
marginBottom: 12,
borderRadius: 12,
paddingLeft: 24,
paddingRight: 24,
}}
>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
设备编码
</div>
<Input
value={deviceId}
onChange={(e) => setDeviceId(e.target.value)}
placeholder="请输入设备编码"
size="large"
style={{ borderRadius: 8 }}
/>
</Card>
<Card
title={
<Space>
<ClockCircleOutlined style={{ color: "#667eea" }} />
<span>数据帧率 (FPS)</span>
</Space>
}
style={{
marginBottom: 12,
borderRadius: 12,
paddingLeft: 24,
paddingRight: 24,
}}
>
<div style={{ marginBottom: 12, fontWeight: 500 }}>
数据帧率
</div>
<Row gutter={12} align="middle">
<Col span={18}>
<Slider
min={1}
max={30}
value={fps}
onChange={setFps}
marks={{ 1: "1 Hz", 30: "30 Hz" }}
/>
</Col>
<Col span={6}>
<InputNumber
min={1}
max={30}
value={fps}
onChange={setFps}
style={{ width: "100%", borderRadius: 8 }}
size="large"
/>
</Col>
</Row>
<Text type="secondary" style={{ fontSize: 12 }}>
有效范围 1~30 Hz
</Text>
</Card>
<Card
title={
<Space>
<BellOutlined style={{ color: "#667eea" }} />
<span>异常监控与报警</span>
</Space>
}
style={{
marginBottom: 12,
borderRadius: 12,
paddingLeft: 24,
paddingRight: 24,
}}
>
<div
style={{
background:
"linear-gradient(135deg, #f5f7fa 0%, #e8eef5 100%)",
padding: 20,
borderRadius: 12,
marginBottom: 12,
}}
>
<Row justify="space-between" align="middle">
<Col>
<Text strong>启用离线超时告警</Text>
<br />
<Text type="secondary" style={{ fontSize: 12 }}>
超过此时长未接收到新数据则触发报警
</Text>
</Col>
<Col>
<Switch
checked={enableOfflineAlert}
onChange={setEnableOfflineAlert}
/>
</Col>
</Row>
</div>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
超时阈值
</div>
<InputNumber
min={10}
max={300}
value={offlineThreshold}
onChange={setOfflineThreshold}
size="large"
addonAfter="秒"
style={{ width: "100%", borderRadius: 8 }}
/>
</Card>
</Col>
<Col xs={12} lg={12}>
<Card
title={
<Space>
<FilterOutlined style={{ color: "#667eea" }} />
<span>数据滤波与异常记录</span>
</Space>
}
style={{
marginBottom: 12,
borderRadius: 12,
paddingLeft: 24,
paddingRight: 24,
}}
>
<div
style={{
background:
"linear-gradient(135deg, #f5f7fa 0%, #e8eef5 100%)",
padding: 20,
borderRadius: 12,
marginBottom: 20,
}}
>
<Row justify="space-between" align="middle">
<Col>
<Text strong>滤波配置</Text>
<br />
<Text type="secondary" style={{ fontSize: 12 }}>
启用数据滤波算法
</Text>
</Col>
<Col>
<Switch
checked={enableFiltering}
onChange={setEnableFiltering}
checkedChildren="已启用"
unCheckedChildren="已停用"
/>
</Col>
</Row>
</div>
<Row gutter={[12, 12]}>
<Col span={12}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
滤波方法
</div>
<Select
value={filterMethod}
onChange={setFilterMethod}
size="large"
disabled={!enableFiltering}
style={{ width: "100%", borderRadius: 8 }}
>
<Option value="median">中值滤波</Option>
</Select>
</Col>
<Col span={12}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
窗口大小
</div>
<InputNumber
min={3}
max={21}
step={2}
value={windowSize}
onChange={setWindowSize}
size="large"
disabled={!enableFiltering}
style={{ width: "100%", borderRadius: 8 }}
/>
</Col>
<Col span={12}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
滤波阈值
</div>
<InputNumber
value={filterThreshold}
onChange={setFilterThreshold}
addonAfter="mm"
size="large"
disabled={!enableFiltering}
style={{ width: "100%", borderRadius: 8 }}
/>
</Col>
<Col span={12}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
波动阈值
</div>
<InputNumber
min={0}
max={100}
step={0.1}
value={flowThreshold}
onChange={setFlowThreshold}
addonAfter="mm"
size="large"
disabled={!enableFiltering}
style={{ width: "100%", borderRadius: 8 }}
/>
</Col>
</Row>
</Card>
<Card
title={
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
}}
>
<Space>
<CloudUploadOutlined style={{ color: "#667eea" }} />
<span>数据上报 (MQTT)</span>
</Space>
<Switch
checked={enableMqtt}
onChange={setEnableMqtt}
checkedChildren="启用"
unCheckedChildren="禁用"
/>
</div>
}
style={{
borderRadius: 12,
paddingLeft: 24,
paddingRight: 24,
}}
>
<Row gutter={[12, 12]}>
<Col span={12}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
Broker Address
</div>
<Input
value={brokerAddress}
onChange={(e) => setBrokerAddress(e.target.value)}
placeholder="例如: 218.3.126.49"
size="large"
disabled={!enableMqtt}
style={{ borderRadius: 8 }}
/>
</Col>
<Col span={8}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
Port
</div>
<Input
value={mqttPort}
onChange={(e) => setMqttPort(e.target.value)}
placeholder="1883"
size="large"
disabled={!enableMqtt}
style={{ borderRadius: 8 }}
/>
</Col>
<Col span={12}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
Topic
</div>
<Input
value={mqttTopic}
onChange={(e) => setMqttTopic(e.target.value)}
placeholder="例如: wybb/z/mqtt179"
size="large"
disabled={!enableMqtt}
style={{ borderRadius: 8 }}
/>
</Col>
<Col span={8}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
Client ID
</div>
<Input
value={mqttClientId}
onChange={(e) => setMqttClientId(e.target.value)}
placeholder="wybb_z1_123"
size="large"
disabled={!enableMqtt}
style={{ borderRadius: 8 }}
/>
</Col>
<Col span={8}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
Username
</div>
<Input
value={mqttUsername}
onChange={(e) => setMqttUsername(e.target.value)}
placeholder="用户名"
size="large"
disabled={!enableMqtt}
style={{ borderRadius: 8 }}
/>
</Col>
<Col span={8}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
Password
</div>
<Input.Password
value={mqttPassword}
onChange={(e) => setMqttPassword(e.target.value)}
placeholder="密码"
size="large"
disabled={!enableMqtt}
style={{ borderRadius: 8 }}
/>
</Col>
</Row>
</Card>
</Col>
</Row>
</div>
</Spin>
);
};
export default AdvancedSettings;