Browse Source

feat: 增加批量上报和补全算法设置;适配批量上报数据格式

master
liujiangyong 2 weeks ago
parent
commit
a6c1bc9834
  1. 71
      client/src/sections/wuyuanbiaoba/components/AdvancedSettings.jsx
  2. 27
      client/src/sections/wuyuanbiaoba/container/index.jsx
  3. 23
      client/src/sections/wuyuanbiaoba/hooks/useAdvancedSettings.js
  4. 43
      client/src/sections/wuyuanbiaoba/hooks/高级配置说明.md

71
client/src/sections/wuyuanbiaoba/components/AdvancedSettings.jsx

@ -94,6 +94,8 @@ const AdvancedSettings = ({ onLogout, locationConfig }) => {
const mqttClientId = settings.mqttConfig?.mqtt?.client_id ?? '';
const mqttUsername = settings.mqttConfig?.mqtt?.username ?? '';
const mqttPassword = settings.mqttConfig?.mqtt?.password ?? '';
const enableBatch = settings.mqttConfig?.batch ?? false;
const batchInterpolation = settings.mqttConfig?.interpolation ?? 'copy';
// 更新本地状态的辅助函数
const setDeviceId = (value) => updateLocalSettings({ deviceId: value });
@ -207,6 +209,20 @@ const AdvancedSettings = ({ onLogout, locationConfig }) => {
},
},
});
const setEnableBatch = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
batch: value
},
});
const setBatchInterpolation = (value) =>
updateLocalSettings({
mqttConfig: {
...settings.mqttConfig,
interpolation: value
},
});
const [saving, setSaving] = React.useState(false);
const [captureEditable, setCaptureEditable] = React.useState(false);
@ -497,16 +513,16 @@ const AdvancedSettings = ({ onLogout, locationConfig }) => {
<Col span={18}>
<Slider
min={1}
max={30}
max={50}
value={fps}
onChange={setFps}
marks={{ 1: "1 Hz", 30: "30 Hz" }}
marks={{ 1: "1 Hz", 50: "50 Hz" }}
/>
</Col>
<Col span={6}>
<InputNumber
min={1}
max={30}
max={50}
value={fps}
onChange={setFps}
style={{ width: "100%", borderRadius: 8 }}
@ -515,7 +531,7 @@ const AdvancedSettings = ({ onLogout, locationConfig }) => {
</Col>
</Row>
<Text type="secondary" style={{ fontSize: 12 }}>
有效范围 1~30 Hz
有效范围 1~50 Hz
</Text>
</Card>
@ -807,6 +823,53 @@ const AdvancedSettings = ({ onLogout, locationConfig }) => {
/>
</Col>
</Row>
{/* 每秒批量上报 */}
<div
style={{
background:
"linear-gradient(135deg, #f5f7fa 0%, #e8eef5 100%)",
padding: 20,
borderRadius: 12,
marginTop: 16,
}}
>
<Row justify="space-between" align="middle">
<Col>
<Text strong>每秒批量上报</Text>
<br />
<Text type="secondary" style={{ fontSize: 12 }}>
开启后,每秒将多条数据合并为一个数组发送。
</Text>
</Col>
<Col>
<Switch
checked={enableBatch}
onChange={setEnableBatch}
checkedChildren="开启"
unCheckedChildren="关闭"
disabled={!enableMqtt}
/>
</Col>
</Row>
</div>
{/* 批量补全算法 */}
<div style={{ marginTop: 16 }}>
<div style={{ marginBottom: 4, fontWeight: 500 }}>
批量补全算法
</div>
<Select
value={batchInterpolation}
onChange={setBatchInterpolation}
size="large"
disabled={!enableMqtt || !enableBatch}
style={{ width: "50%", borderRadius: 8 }}
>
<Option value="copy">分段复制</Option>
<Option value="lowpass">低通滤波</Option>
</Select>
</div>
</Card>
</Col>
</Row>

27
client/src/sections/wuyuanbiaoba/container/index.jsx

@ -122,6 +122,17 @@ const WuyuanbiaobaContent = () => {
}));
};
// 归一化设备上报数据:兼容批量格式(values 为数组)与单条格式(values 为对象)
const normalizeDataGroups = (values) => {
if (Array.isArray(values)) {
return values.filter((g) => g && Array.isArray(g.data));
}
if (values && Array.isArray(values.data)) {
return [values];
}
return [];
};
// 初始化数据
useEffect(() => {
// 初始化空的实时数据表格
@ -172,10 +183,20 @@ const WuyuanbiaobaContent = () => {
realtimeDataSubscription.latest &&
realtimeDataSubscription.latest.values
) {
const newRealtimeData = processRealtimeData(
// 兼容批量上报(values 为数组)与单条上报(values 为对象)
const dataGroups = normalizeDataGroups(
realtimeDataSubscription.latest.values
);
if (dataGroups.length > 0) {
// 维护两分钟的缓冲区(批量模式下保留批量内全部数据点,保证导出全分辨率)
dataGroups.forEach((group) => pushToRealtimeBuffer(group));
setLastUpdateTime(new Date());
// 展示采样:取最新一条数据(批量模式下取批量内最后一条)
const latestGroup = dataGroups[dataGroups.length - 1];
const newRealtimeData = processRealtimeData(latestGroup);
if (newRealtimeData.length > 0) {
const currentTime = Date.now();
const currentSecond = Math.floor(currentTime / 1000);
@ -209,9 +230,7 @@ const WuyuanbiaobaContent = () => {
}))
);
}
//维护两分钟的缓冲区
pushToRealtimeBuffer(realtimeDataSubscription.latest.values);
setLastUpdateTime(new Date());
}
}
}
}, [realtimeDataSubscription.latest]);

23
client/src/sections/wuyuanbiaoba/hooks/useAdvancedSettings.js

@ -61,6 +61,8 @@ const DEFAULT_SETTINGS = {
// MQTT 配置
mqttConfig: {
enable: true,
batch: false,
interpolation: 'copy',
mqtt: {
broker: '',
port: 1883,
@ -343,8 +345,8 @@ const useAdvancedSettings = () => {
* 更新数据帧率
*/
const updateDataFps = useCallback((dataFps) => {
if (dataFps < 1 || dataFps > 30) {
message.warning('数据帧率范围为 1-30 Hz');
if (dataFps < 1 || dataFps > 50) {
message.warning('数据帧率范围为 1-50 Hz');
return false;
}
return sendCommand('setDataFps', { dataFps });
@ -400,7 +402,7 @@ const useAdvancedSettings = () => {
* 更新 MQTT 配置
*/
const updateMqttConfig = useCallback((mqttConfig) => {
const { enable, mqtt } = mqttConfig;
const { enable, mqtt, batch, interpolation } = mqttConfig;
// 验证必填字段
if (enable) {
@ -422,7 +424,12 @@ const useAdvancedSettings = () => {
}
}
return sendCommand('setMqtt', { enable, mqtt });
return sendCommand('setMqtt', {
enable,
mqtt,
batch: batch ?? false,
interpolation: interpolation ?? 'copy'
});
}, [sendCommand]);
const sendAndWaitOperate = useCallback((cmd, values = {}, timeout = SAVE_RESPONSE_TIMEOUT) => {
@ -484,8 +491,8 @@ const useAdvancedSettings = () => {
return false;
}
if (settings.dataFps < 1 || settings.dataFps > 30) {
message.warning('数据帧率范围为 1-30 Hz');
if (settings.dataFps < 1 || settings.dataFps > 50) {
message.warning('数据帧率范围为 1-50 Hz');
return false;
}
@ -561,7 +568,9 @@ const useAdvancedSettings = () => {
cmd: 'setMqtt',
values: {
enable: settings.mqttConfig.enable,
mqtt: settings.mqttConfig.mqtt
mqtt: settings.mqttConfig.mqtt,
batch: settings.mqttConfig.batch ?? false,
interpolation: settings.mqttConfig.interpolation ?? 'copy'
}
}
];

43
client/src/sections/wuyuanbiaoba/hooks/高级配置说明.md

@ -230,11 +230,18 @@
"password": "",
"client_id": "wybb_debug"
},
"enable": true
"enable": true,
"batch": true,
"interpolation": "lowpass"
}
}
```
字段说明:
- `enable`:MQTT 上报总开关
- `batch`:每秒批量上报开关。开启后,每秒将多条数据合并为一个数组发送;关闭时数据格式不变(一秒内上报多次)
- `interpolation`:批量补全算法,`copy`=分段复制(现有算法,数据缺失时仍复制该条),`lowpass`=低通滤波插值(3Hz FIR 滤波器插值补全缺失数据)
12. mqtt上报设置
```json
{
@ -249,7 +256,9 @@
"password": "",
"client_id": "wybb_debug"
},
"enable": true
"enable": true,
"batch": true,
"interpolation": "lowpass"
}
}
```
@ -263,3 +272,33 @@
}
}
```
13. 批量上报数据格式(batch 开启时)
`batch` 开启后,设备每秒上报一次,`values` 为数组,每项为该秒内的一条数据(含时间戳):
```json
{
"_from": "dev",
"cmd": "data",
"values": [
{
"data": [{"pos": "0", "desc": "bb_0", "x": 0.12, "y": 0.34}],
"time": "2026-10-27 10:00:00.000"
},
{
"data": [{"pos": "0", "desc": "bb_0", "x": 0.13, "y": 0.35}],
"time": "2026-10-27 10:00:00.020"
}
]
}
```
`batch` 关闭时,`values` 仍为单条对象格式:
```json
{
"_from": "dev",
"cmd": "data",
"values": {
"data": [{"pos": "0", "desc": "bb_0", "x": 0.12, "y": 0.34}],
"time": "2026-10-27 10:00:00.000"
}
}
```

Loading…
Cancel
Save