Browse Source

feat:增强 WebSocket 处理机制,新增高级设置管理功能

- 新增 WebSocket 就绪状态标识,确保仅在连接完全建立后发送消息。
- 实现消息队列功能,用于处理 WebSocket 就绪前发送的消息。
- 升级 AdvancedSettings 组件,通过自定义钩子函数管理设备配置,涵盖配置的获取与保存功能。
- 新增完善的 WebSocket 指令处理逻辑,支持设备编号、数据帧率、告警配置、过滤规则及 MQTT 配置的相关指令。
- 优化用户反馈体验,添加加载状态指示器及连接状态告警提示。
- 编写高级设置中 WebSocket 协议的完整使用文档。
- 将 package.json 文件中的版本号更新至 1.1.0。
master
qinjian 3 weeks ago
parent
commit
505aa3f199
  1. 113
      client/src/sections/wuyuanbiaoba/actions/websocket.jsx
  2. 206
      client/src/sections/wuyuanbiaoba/components/AdvancedSettings.jsx
  3. 1
      client/src/sections/wuyuanbiaoba/components/CameraView.jsx
  4. 3
      client/src/sections/wuyuanbiaoba/container/index.jsx
  5. 479
      client/src/sections/wuyuanbiaoba/hooks/useAdvancedSettings.js
  6. 246
      client/src/sections/wuyuanbiaoba/hooks/高级配置说明.md
  7. 2
      package.json
  8. 20
      server/tcpProxy/index.js

113
client/src/sections/wuyuanbiaoba/actions/websocket.jsx

@ -125,6 +125,7 @@ const WebSocketContext = createContext();
// WebSocket Provider
export const WebSocketProvider = ({ children }) => {
const [isConnected, setIsConnected] = useState(false);
const [isReady, setIsReady] = useState(false); //
const [connectionStatus, setConnectionStatus] = useState("disconnected");
const [lastMessage, setLastMessage] = useState(null); //
const [messageHistory, setMessageHistory] = useState([]); //
@ -132,9 +133,12 @@ export const WebSocketProvider = ({ children }) => {
const reconnectTimeoutRef = useRef(null);
const reconnectAttemptsRef = useRef(0);
const subscriptionsRef = useRef(new Map()); //
const messageQueueRef = useRef([]); //
const readyCheckTimeoutRef = useRef(null); //
const maxReconnectAttempts = 5;
const reconnectInterval = 3000; // 3
const maxHistoryLength = 100; //
const READY_TIMEOUT = 1500; // 1.5TCP
// WebSocket访
const getWebSocketUrl = () => {
@ -217,6 +221,24 @@ export const WebSocketProvider = ({ children }) => {
return;
}
//
if (parsedData._from === 'proxy' && parsedData.cmd === 'ready') {
console.log('🟢 收到TCP连接就绪信号');
//
if (readyCheckTimeoutRef.current) {
clearTimeout(readyCheckTimeoutRef.current);
readyCheckTimeoutRef.current = null;
}
//
setIsReady(true);
//
flushMessageQueue();
return;
}
// console.log(` [${parsedData._from}:${parsedData.cmd}]:`, parsedData);
} catch (error) {
console.error("解析WebSocket消息失败:", error, data);
@ -243,22 +265,60 @@ export const WebSocketProvider = ({ children }) => {
[notifySubscribers]
);
//
const flushMessageQueue = useCallback(() => {
if (messageQueueRef.current.length === 0) return;
console.log(`📤 开始发送队列中的 ${messageQueueRef.current.length} 条消息`);
const queue = [...messageQueueRef.current];
messageQueueRef.current = [];
queue.forEach((message, index) => {
setTimeout(() => {
if (socketRef.current?.readyState === WebSocket.OPEN) {
try {
socketRef.current.send(message);
console.log(`✅ 队列消息 ${index + 1}/${queue.length} 已发送`);
} catch (error) {
console.error(`❌ 发送队列消息 ${index + 1} 失败:`, error);
//
messageQueueRef.current.push(message);
}
}
}, index * 100); // 100ms
});
}, []);
//
const sendMessage = useCallback((message) => {
if (socketRef.current?.readyState === WebSocket.OPEN) {
//
if (socketRef.current?.readyState === WebSocket.OPEN && isReady) {
try {
socketRef.current.send(message);
// console.log('WebSocket:', message);
return true;
} catch (error) {
console.error("发送WebSocket消息失败:", error);
return false;
}
} else {
console.warn("WebSocket未连接,无法发送消息");
return false;
}
}, []);
//
else if (socketRef.current?.readyState === WebSocket.OPEN && !isReady) {
const cmdMatch = message.match(/"cmd"\s*:\s*"([^"]+)"/);
const cmd = cmdMatch ? cmdMatch[1] : 'unknown';
console.log(`⏳ 连接尚未完全就绪,消息 [${cmd}] 已加入队列`);
messageQueueRef.current.push(message);
return true;
}
//
else {
const cmdMatch = message.match(/"cmd"\s*:\s*"([^"]+)"/);
const cmd = cmdMatch ? cmdMatch[1] : 'unknown';
console.warn(`⚠️ WebSocket未连接,消息 [${cmd}] 已加入队列,等待连接建立`);
messageQueueRef.current.push(message);
return true;
}
}, [isReady]);
// WebSocket
const connect = useCallback(() => {
@ -267,23 +327,24 @@ export const WebSocketProvider = ({ children }) => {
}
setConnectionStatus("connecting");
setIsReady(false); //
console.log("尝试连接WebSocket:", websocketUrl);
try {
socketRef.current = new WebSocket(websocketUrl);
socketRef.current.onopen = () => {
console.log("WebSocket连接已建立");
console.log("WebSocket连接已建立");
setIsConnected(true);
setConnectionStatus("connected");
reconnectAttemptsRef.current = 0;
//
// sendMessage(JSON.stringify({
// _from: 'setup',
// cmd: 'init',
// values: { timestamp: Date.now() }
// }));
// TCP
// ready
readyCheckTimeoutRef.current = setTimeout(() => {
setIsReady(true);
flushMessageQueue();
}, READY_TIMEOUT);
};
socketRef.current.onmessage = (event) => {
@ -300,21 +361,28 @@ export const WebSocketProvider = ({ children }) => {
};
socketRef.current.onclose = (event) => {
console.log("WebSocket连接已关闭:", event.code, event.reason);
console.log("WebSocket连接已关闭:", event.code, event.reason);
setIsConnected(false);
setIsReady(false); //
setConnectionStatus("disconnected");
//
if (readyCheckTimeoutRef.current) {
clearTimeout(readyCheckTimeoutRef.current);
readyCheckTimeoutRef.current = null;
}
//
if (reconnectAttemptsRef.current < maxReconnectAttempts) {
reconnectAttemptsRef.current++;
console.log(
`尝试重连 ${reconnectAttemptsRef.current}/${maxReconnectAttempts}`
`🔄 尝试重连 ${reconnectAttemptsRef.current}/${maxReconnectAttempts}`
);
reconnectTimeoutRef.current = setTimeout(() => {
connect();
}, reconnectInterval);
} else {
console.log("达到最大重连次数,停止重连");
console.log("🛑 达到最大重连次数,停止重连");
setConnectionStatus("error");
}
};
@ -326,8 +394,9 @@ export const WebSocketProvider = ({ children }) => {
} catch (error) {
console.error("创建WebSocket连接失败:", error);
setConnectionStatus("error");
setIsReady(false);
}
}, [handleMessage, sendMessage]);
}, [handleMessage, flushMessageQueue]);
//
const disconnect = useCallback(() => {
@ -335,12 +404,18 @@ export const WebSocketProvider = ({ children }) => {
clearTimeout(reconnectTimeoutRef.current);
}
if (readyCheckTimeoutRef.current) {
clearTimeout(readyCheckTimeoutRef.current);
readyCheckTimeoutRef.current = null;
}
if (socketRef.current) {
socketRef.current.close(1000, "用户主动断开");
socketRef.current = null;
}
setIsConnected(false);
setIsReady(false);
setConnectionStatus("disconnected");
reconnectAttemptsRef.current = 0;
}, []);
@ -373,6 +448,9 @@ export const WebSocketProvider = ({ children }) => {
if (reconnectTimeoutRef.current) {
clearTimeout(reconnectTimeoutRef.current);
}
if (readyCheckTimeoutRef.current) {
clearTimeout(readyCheckTimeoutRef.current);
}
if (socketRef.current) {
socketRef.current.close();
}
@ -383,6 +461,7 @@ export const WebSocketProvider = ({ children }) => {
const value = {
//
isConnected,
isReady, //
connectionStatus,
//

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

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect } from "react";
import {
Card,
Input,
@ -12,6 +12,8 @@ import {
message,
Space,
Typography,
Spin,
Alert,
} from "antd";
import {
SettingOutlined,
@ -22,51 +24,166 @@ import {
BellOutlined,
FilterOutlined,
CloudUploadOutlined,
SyncOutlined,
} from "@ant-design/icons";
import useAdvancedSettings from "../hooks/useAdvancedSettings";
const { Option } = Select;
const { Title, Text } = Typography;
const AdvancedSettings = ({ onLogout }) => {
const [deviceId, setDeviceId] = useState("");
const [fps, setFps] = useState(5);
const [enableOfflineAlert, setEnableOfflineAlert] = useState(false);
const [offlineThreshold, setOfflineThreshold] = useState(60);
const [enableFiltering, setEnableFiltering] = useState(true);
const [filterMethod, setFilterMethod] = useState("median");
const [windowSize, setWindowSize] = useState(5);
const [filterThreshold, setFilterThreshold] = useState(0.1);
const [flowThreshold, setFlowThreshold] = useState(10.0);
const [enableMqtt, setEnableMqtt] = useState(true);
const [brokerAddress, setBrokerAddress] = useState("");
const [mqttPort, setMqttPort] = useState("");
const [mqttTopic, setMqttTopic] = useState("");
const [mqttClientId, setMqttClientId] = useState("");
const [mqttUsername, setMqttUsername] = useState("");
const [mqttPassword, setMqttPassword] = useState("");
// 使 Hook
const {
settings,
loading,
isConnected,
isReady,
fetchAllSettings,
saveAllSettings,
resetSettings,
updateLocalSettings,
} = useAdvancedSettings();
// 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 = Math.abs(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 handleSave = () => message.success("配置已保存");
const handleReset = () => {
setDeviceId("");
setFps(5);
setEnableOfflineAlert(false);
setOfflineThreshold(60);
setEnableFiltering(true);
setFilterMethod("median");
setWindowSize(5);
setFilterThreshold(0.1);
setFlowThreshold(10.0);
setEnableMqtt(true);
setBrokerAddress("");
setMqttPort("");
setMqttTopic("");
setMqttClientId("");
setMqttUsername("");
setMqttPassword("");
message.info("配置已重置");
fetchAllSettings();
};
return (
<Spin spinning={loading} tip="正在加载配置...">
<div
style={{
padding: "12px",
@ -74,6 +191,17 @@ const AdvancedSettings = ({ onLogout }) => {
minHeight: "calc(100vh - 92px)",
}}
>
{/* WebSocket 连接状态提示 */}
{!isConnected && (
<Alert
message="WebSocket 未连接"
description="当前未连接到设备,无法读取或保存配置。请检查网络连接。"
type="warning"
showIcon
style={{ marginBottom: 12, borderRadius: 8 }}
/>
)}
<Card
style={{
marginBottom: 12,
@ -107,23 +235,26 @@ const AdvancedSettings = ({ onLogout }) => {
>
针对下位机运行的参数配置修改后请及时保存(慎用)
</Text>
</Space>
</Col>
<Col>
<Space size="middle">
<Button
icon={<ReloadOutlined />}
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,
@ -487,6 +618,7 @@ const AdvancedSettings = ({ onLogout }) => {
</Col>
</Row>
</div>
</Spin>
);
};

1
client/src/sections/wuyuanbiaoba/components/CameraView.jsx

@ -59,7 +59,6 @@ const CameraView = ({
if (window.env && window.env.FS_FLAG === "localdev") {
streamUrl = `http://10.8.30.179:2240/video_flow`; //
}
console.log(streamUrl,'测试')
//
const applyTransform = () => {

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

@ -27,7 +27,7 @@ const { Title } = Typography;
// 使WebSocket hook
const WuyuanbiaobaContent = () => {
const { isConnected, sendMessage } = useWebSocket();
const { isConnected, isReady, sendMessage } = useWebSocket();
//
const realtimeDataSubscription = useWebSocketSubscription("dev", "data");
@ -513,6 +513,7 @@ const WuyuanbiaobaContent = () => {
>
<Title level={3} style={{ color: "#333", margin: 0 }}>
视觉位移计配置工具
</Title>
<Menu
mode="horizontal"

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

@ -0,0 +1,479 @@
/**
* useAdvancedSettings Hook
*
* 高级配置管理 Hook用于管理设备的高级参数配置
*
* 功能包括
* - 设备编码读取与设置 (getId/setId)
* - 数据帧率读取与设置 (getDataFps/setDataFps)
* - 异常超时监控读取与设置 (getAlert/setAlert)
* - 滤波配置读取与设置 (getWin/setWin)
* - MQTT 上报配置读取与设置 (getMqtt/setMqtt)
*
* 使用示例
* ```jsx
* const {
* settings,
* loading,
* fetchAllSettings,
* updateDeviceId,
* updateDataFps,
* updateAlertConfig,
* updateFilterConfig,
* updateMqttConfig,
* } = useAdvancedSettings();
* ```
*/
import { useState, useEffect, useCallback } from 'react';
import { message } from 'antd';
import { useWebSocket, useWebSocketSubscription } from '../actions/websocket.jsx';
/**
* 高级配置默认值
*/
const DEFAULT_SETTINGS = {
// 设备编码
deviceId: '',
// 数据帧率
dataFps: 10,
// 异常监控配置
alertConfig: {
enable: false,
intervalSec: 60
},
// 滤波配置
filterConfig: {
enable: true,
method: 'median',
size: 5,
threshold: -0.1,
imgThreshold: 10.0
},
// MQTT 配置
mqttConfig: {
enable: true,
mqtt: {
broker: '',
port: 1883,
topic: '',
username: '',
password: '',
client_id: ''
}
}
};
/**
* 高级配置管理 Hook
*/
const useAdvancedSettings = () => {
const { isConnected, isReady, sendMessage } = useWebSocket();
// 配置状态
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
const [loading, setLoading] = useState(false);
const [fetchStatus, setFetchStatus] = useState({});
// WebSocket 订阅 - 监听所有来自设备的响应
const { latest: getIdResponse } = useWebSocketSubscription('dev', 'getId');
const { latest: setIdResponse } = useWebSocketSubscription('dev', 'setId');
const { latest: getDataFpsResponse } = useWebSocketSubscription('dev', 'getDataFps');
const { latest: setDataFpsResponse } = useWebSocketSubscription('dev', 'setDataFps');
const { latest: getAlertResponse } = useWebSocketSubscription('dev', 'getAlert');
const { latest: setAlertResponse } = useWebSocketSubscription('dev', 'setAlert');
const { latest: getWinResponse } = useWebSocketSubscription('dev', 'getWin');
const { latest: setWinResponse } = useWebSocketSubscription('dev', 'setWin');
const { latest: getMqttResponse } = useWebSocketSubscription('dev', 'getMqtt');
const { latest: setMqttResponse } = useWebSocketSubscription('dev', 'setMqtt');
/**
* 处理设备编码读取响应
*/
useEffect(() => {
if (getIdResponse?.values?.id !== undefined) {
console.log('📥 收到设备编码:', getIdResponse.values.id);
setSettings(prev => ({
...prev,
deviceId: getIdResponse.values.id
}));
setFetchStatus(prev => ({ ...prev, deviceId: 'success' }));
}
}, [getIdResponse]);
/**
* 处理设备编码设置响应
*/
useEffect(() => {
if (setIdResponse?.values?.operate !== undefined) {
if (setIdResponse.values.operate) {
message.success('设备编码设置成功');
} else {
message.error('设备编码设置失败');
}
}
}, [setIdResponse]);
/**
* 处理数据帧率读取响应
*/
useEffect(() => {
if (getDataFpsResponse?.values?.dataFps !== undefined) {
console.log('📥 收到数据帧率:', getDataFpsResponse.values.dataFps);
setSettings(prev => ({
...prev,
dataFps: getDataFpsResponse.values.dataFps
}));
setFetchStatus(prev => ({ ...prev, dataFps: 'success' }));
}
}, [getDataFpsResponse]);
/**
* 处理数据帧率设置响应
*/
useEffect(() => {
if (setDataFpsResponse?.values?.operate !== undefined) {
if (setDataFpsResponse.values.operate) {
message.success('数据帧率设置成功');
} else {
message.error('数据帧率设置失败');
}
}
}, [setDataFpsResponse]);
/**
* 处理异常监控配置读取响应
*/
useEffect(() => {
if (getAlertResponse?.values) {
console.log('📥 收到异常监控配置:', getAlertResponse.values);
setSettings(prev => ({
...prev,
alertConfig: getAlertResponse.values
}));
setFetchStatus(prev => ({ ...prev, alert: 'success' }));
}
}, [getAlertResponse]);
/**
* 处理异常监控设置响应
*/
useEffect(() => {
if (setAlertResponse?.values?.operate !== undefined) {
if (setAlertResponse.values.operate) {
message.success('异常监控配置设置成功');
} else {
message.error('异常监控配置设置失败');
}
}
}, [setAlertResponse]);
/**
* 处理滤波配置读取响应
*/
useEffect(() => {
if (getWinResponse?.values) {
console.log('📥 收到滤波配置:', getWinResponse.values);
setSettings(prev => ({
...prev,
filterConfig: getWinResponse.values
}));
setFetchStatus(prev => ({ ...prev, filter: 'success' }));
}
}, [getWinResponse]);
/**
* 处理滤波配置设置响应
*/
useEffect(() => {
if (setWinResponse?.values?.operate !== undefined) {
if (setWinResponse.values.operate) {
message.success('滤波配置设置成功');
} else {
message.error('滤波配置设置失败');
}
}
}, [setWinResponse]);
/**
* 处理 MQTT 配置读取响应
*/
useEffect(() => {
if (getMqttResponse?.values) {
console.log('📥 收到 MQTT 配置:', getMqttResponse.values);
setSettings(prev => ({
...prev,
mqttConfig: getMqttResponse.values
}));
setFetchStatus(prev => ({ ...prev, mqtt: 'success' }));
}
}, [getMqttResponse]);
/**
* 处理 MQTT 配置设置响应
*/
useEffect(() => {
if (setMqttResponse?.values?.operate !== undefined) {
if (setMqttResponse.values.operate) {
message.success('MQTT 配置设置成功');
} else {
message.error('MQTT 配置设置失败');
}
}
}, [setMqttResponse]);
/**
* 发送 WebSocket 命令
*/
const sendCommand = useCallback((cmd, values = {}) => {
if (!isConnected) {
message.warning('WebSocket 未连接,请稍后重试');
return false;
}
const command = {
_from: 'setup',
cmd,
values
};
console.log('📤 发送命令:', command);
return sendMessage(JSON.stringify(command));
}, [isConnected, sendMessage]);
/**
* 获取设备编码
*/
const fetchDeviceId = useCallback(() => {
setFetchStatus(prev => ({ ...prev, deviceId: 'loading' }));
return sendCommand('getId', {});
}, [sendCommand]);
/**
* 更新设备编码
*/
const updateDeviceId = useCallback((id) => {
if (!id || !id.trim()) {
message.warning('请输入设备编码');
return false;
}
return sendCommand('setId', { id });
}, [sendCommand]);
/**
* 获取数据帧率
*/
const fetchDataFps = useCallback(() => {
setFetchStatus(prev => ({ ...prev, dataFps: 'loading' }));
return sendCommand('getDataFps', {});
}, [sendCommand]);
/**
* 更新数据帧率
*/
const updateDataFps = useCallback((dataFps) => {
if (dataFps < 1 || dataFps > 30) {
message.warning('数据帧率范围为 1-30 Hz');
return false;
}
return sendCommand('setDataFps', { dataFps });
}, [sendCommand]);
/**
* 获取异常监控配置
*/
const fetchAlertConfig = useCallback(() => {
setFetchStatus(prev => ({ ...prev, alert: 'loading' }));
return sendCommand('getAlert', {});
}, [sendCommand]);
/**
* 更新异常监控配置
*/
const updateAlertConfig = useCallback((alertConfig) => {
const { enable, intervalSec } = alertConfig;
return sendCommand('setAlert', { enable, intervalSec });
}, [sendCommand]);
/**
* 获取滤波配置
*/
const fetchFilterConfig = useCallback(() => {
setFetchStatus(prev => ({ ...prev, filter: 'loading' }));
return sendCommand('getWin', {});
}, [sendCommand]);
/**
* 更新滤波配置
*/
const updateFilterConfig = useCallback((filterConfig) => {
const { enable, method, size, threshold, imgThreshold } = filterConfig;
return sendCommand('setWin', {
enable,
method,
size,
threshold,
imgThreshold
});
}, [sendCommand]);
/**
* 获取 MQTT 配置
*/
const fetchMqttConfig = useCallback(() => {
setFetchStatus(prev => ({ ...prev, mqtt: 'loading' }));
return sendCommand('getMqtt', {});
}, [sendCommand]);
/**
* 更新 MQTT 配置
*/
const updateMqttConfig = useCallback((mqttConfig) => {
const { enable, mqtt } = mqttConfig;
// 验证必填字段
if (enable) {
if (!mqtt.broker || !mqtt.broker.trim()) {
message.warning('请输入 Broker 地址');
return false;
}
if (!mqtt.port) {
message.warning('请输入端口号');
return false;
}
if (!mqtt.topic || !mqtt.topic.trim()) {
message.warning('请输入 Topic');
return false;
}
if (!mqtt.client_id || !mqtt.client_id.trim()) {
message.warning('请输入 Client ID');
return false;
}
}
return sendCommand('setMqtt', { enable, mqtt });
}, [sendCommand]);
/**
* 获取所有配置
*/
const fetchAllSettings = useCallback(async () => {
if (!isConnected) {
message.warning('WebSocket 未连接,无法获取配置');
return;
}
if (!isReady) {
message.info('连接正在建立中,指令已加入队列...');
}
setLoading(true);
setFetchStatus({});
console.log('🔄 开始获取所有高级配置...');
// 依次发送所有读取命令
fetchDeviceId();
// 添加延迟避免命令过快
setTimeout(() => fetchDataFps(), 100);
setTimeout(() => fetchAlertConfig(), 200);
setTimeout(() => fetchFilterConfig(), 300);
setTimeout(() => fetchMqttConfig(), 400);
// 等待所有响应
setTimeout(() => {
setLoading(false);
console.log('✅ 配置获取完成');
}, 2000);
}, [isConnected, isReady, fetchDeviceId, fetchDataFps, fetchAlertConfig, fetchFilterConfig, fetchMqttConfig]);
/**
* 保存所有配置
*/
const saveAllSettings = useCallback(() => {
if (!isConnected) {
message.warning('WebSocket 未连接,无法保存配置');
return false;
}
if (!isReady) {
message.info('连接正在建立中,指令已加入队列...');
} else {
message.info('正在保存配置...');
}
// 依次发送所有设置命令
updateDeviceId(settings.deviceId);
setTimeout(() => updateDataFps(settings.dataFps), 100);
setTimeout(() => updateAlertConfig(settings.alertConfig), 200);
setTimeout(() => updateFilterConfig(settings.filterConfig), 300);
setTimeout(() => updateMqttConfig(settings.mqttConfig), 400);
return true;
}, [isConnected, isReady, settings, updateDeviceId, updateDataFps, updateAlertConfig, updateFilterConfig, updateMqttConfig]);
/**
* 重置配置到默认值
*/
const resetSettings = useCallback(() => {
setSettings(DEFAULT_SETTINGS);
message.info('配置已重置为默认值');
}, []);
/**
* 更新本地设置不发送到设备
*/
const updateLocalSettings = useCallback((updates) => {
setSettings(prev => ({
...prev,
...updates
}));
}, []);
/**
* WebSocket 连接建立后自动获取配置
*/
useEffect(() => {
if (isConnected) {
console.log('🔌 WebSocket 已连接,自动获取高级配置');
// 延迟获取,避免连接刚建立时发送命令
setTimeout(() => {
fetchAllSettings();
}, 500);
}
}, [isConnected, fetchAllSettings]);
return {
// 状态
settings,
loading,
fetchStatus,
isConnected,
isReady,
// 操作方法
fetchAllSettings,
saveAllSettings,
resetSettings,
updateLocalSettings,
// 单项操作
fetchDeviceId,
updateDeviceId,
fetchDataFps,
updateDataFps,
fetchAlertConfig,
updateAlertConfig,
fetchFilterConfig,
updateFilterConfig,
fetchMqttConfig,
updateMqttConfig,
};
};
export default useAdvancedSettings;

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

@ -0,0 +1,246 @@
# 高级配置 WebSocket 协议对接文档
1. 主机编码读取指令
```json
{
"_from": "setup",
"cmd": "getId",
"values": {}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "getId",
"values": {
"id": "uu1234"
}
}
```
2. 主机编码设置指令
```json
{
"_from": "setup",
"cmd": "setId",
"values": { "id": "uu1234" }
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "setId",
"values": { "operate": true }
}
```
3. 帧率读取指令
```json
{
"_from": "setup",
"cmd": "getDataFps",
"values": {}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "getDataFps",
"values": { "dataFps": 10 }
}
```
4. 帧率设置指令
```json
{
"_from": "setup",
"cmd": "setDataFps",
"values": { "dataFps": 10 }
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "setDataFps",
"values": { "operate": true }
}
```
5. 异常超时监控-读取指令
```json
{
"_from": "setup",
"cmd": "getAlert",
"values": {}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "getAlert",
"values": {
"enable": false,
"intervalSec": 6
}
}
```
6. 异常超时监控-设置指令
```json
{
"_from": "setup",
"cmd": "setAlert",
"values": {
"enable": false,
"intervalSec": 6
}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "setAlert",
"values": {
"operate": true
}
}
```
7. 滤波-读取
```json
{
"_from": "setup",
"cmd":"getWin",
"values": {}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "getWin",
"values": {
"enable": true,
"method": "median",
"size": 5,
"threshold": -0.1,
"imgThreshold": 10.0
}
}
```
8. 滤波-设置
```json
{
"_from": "setup",
"cmd":"setWin",
"values": {
"enable": true,
"method": "median",
"size": 5,
"threshold": -0.1,
"imgThreshold": 10.0
}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "setWin",
"values": {
"operate": true
}
}
```
9. mqtt 上报读取
```json
{
"_from": "setup",
"cmd":"getMqtt",
"values": {}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "getMqtt",
"values": {
"mqtt": {
"broker": "218.3.126.49",
"port": 1883,
"topic": "wybb/zj/mqtt110_debug",
"username": "",
"password": "",
"client_id": "wybb_debug"
},
"enable": true
}
}
```
10. mqtt上报设置
```json
{
"_from": "setup",
"cmd": "setMqtt",
"values": {
"mqtt": {
"broker": "218.3.126.49",
"port": 1883,
"topic": "wybb/zj/mqtt110_debug",
"username": "",
"password": "",
"client_id": "wybb_debug"
},
"enable": true
}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "setMqtt",
"values": {
"operate": true
}
}
```
10. 异常超时监控-读取
```json
{
"_from": "setup",
"cmd":"getAlert",
"values": {}
}
```
响应示例
```json
{
"_from": "dev",
"cmd": "getAlert",
"values": {
"enable": false,
"intervalSec": 6
}
}
```

2
package.json

@ -1,6 +1,6 @@
{
"name": "wuyuanbiaoba-web",
"version": "1.0.4",
"version": "1.1.0",
"main": "index.html",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",

20
server/tcpProxy/index.js

@ -27,11 +27,26 @@ function setupTcpProxy(conf) {
const tcpClient = new net.Socket();
// TCP数据缓冲区
let tcpDataBuffer = '';
let tcpConnected = false; // 新增:TCP连接状态标志
// TCP连接成功
tcpClient.connect(process.env.TCP_PORT || TCP_PORT, process.env.TCP_HOST || TCP_HOST, () => {
// console.log(process.env);
// console.log(`TCP连接已建立到 ${TCP_HOST}:${TCP_PORT}`);
tcpConnected = true;
// 向WebSocket客户端发送就绪信号
if (ws.readyState === WebSocket.OPEN) {
const readySignal = JSON.stringify({
_from: 'proxy',
cmd: 'ready',
values: {
message: 'TCP connection established',
timestamp: Date.now()
}
});
ws.send(readySignal);
}
});
// TCP接收数据,转发到WebSocket
@ -40,9 +55,8 @@ function setupTcpProxy(conf) {
// 尝试解析为文本
try {
textData = data.toString('utf8');
// console.log('收到TCP数据片段:', textData.length, '字节');
} catch (e) {
// console.log('TCP数据无法解析为文本');
console.log('TCP数据无法解析为文本');
return;
}
@ -94,7 +108,7 @@ function setupTcpProxy(conf) {
// 转发字符串数据到TCP服务器
if (tcpClient.writable) {
console.log('准备发送数据到TCP服务器:', messageStr);
console.log('发送数据到TCP服务器:', messageStr);
// 检查数据大小
const dataSize = Buffer.byteLength(messageStr, 'utf8');

Loading…
Cancel
Save