+
+class XNUDPTestServicePrivate : public XNServiceObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(XNUDPTestService)
+
+ XNUDPTestServicePrivate(XNUDPTestService *q) : XNServiceObjectPrivate(q) {}
+
+ QUdpSocket *udpSocket{nullptr};
+ QString targetHost;
+ quint16 targetPort{0};
+ quint16 localPort{0};
+};
diff --git a/XNSimHtml/components/data-monitor.js b/XNSimHtml/components/data-monitor.js
index 0eaad88..eaa208c 100644
--- a/XNSimHtml/components/data-monitor.js
+++ b/XNSimHtml/components/data-monitor.js
@@ -2,12 +2,225 @@ class DataMonitor extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
+ this.currentMode = 'udp'; // 默认显示UDP模式
+ this.udpPort = 54321; // 默认UDP端口
+ this.udpIp = '127.0.0.1'; // 默认监听所有接口
+ this.isMonitoring = false; // 监控状态
+ this.udpData = []; // 存储接收到的UDP数据
+
+ // UDP数据注入默认值
+ this.injectIp = '127.0.0.1';
+ this.injectPort = 12345;
+ this.injectData = '{"message": "测试数据"}';
}
connectedCallback() {
this.render();
}
+ switchMode(mode) {
+ this.currentMode = mode;
+ this.render();
+ }
+
+ async startMonitoring() {
+ if (this.isMonitoring) return;
+
+ try {
+ const response = await fetch('/api/udp-monitor/start', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ port: this.udpPort,
+ ip: this.udpIp
+ }),
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ this.isMonitoring = true;
+ this.updateMonitoringStatus();
+ this.setupDataFetch();
+ } else {
+ this.showError(data.error || '启动监控失败');
+ }
+ } catch (error) {
+ this.showError('无法连接到服务器');
+ console.error('启动UDP监控失败:', error);
+ }
+ }
+
+ async stopMonitoring() {
+ if (!this.isMonitoring) return;
+
+ try {
+ const response = await fetch('/api/udp-monitor/stop', {
+ method: 'POST',
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ this.isMonitoring = false;
+ this.updateMonitoringStatus();
+ if (this.dataFetchInterval) {
+ clearInterval(this.dataFetchInterval);
+ this.dataFetchInterval = null;
+ }
+ } else {
+ this.showError(data.error || '停止监控失败');
+ }
+ } catch (error) {
+ this.showError('无法连接到服务器');
+ console.error('停止UDP监控失败:', error);
+ }
+ }
+
+ async injectUdpData() {
+ try {
+ // 验证数据格式
+ let parsedData;
+ try {
+ parsedData = JSON.parse(this.injectData);
+ } catch (e) {
+ this.showError('数据格式无效,请输入有效的JSON');
+ return;
+ }
+
+ const response = await fetch('/api/udp-monitor/inject', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ targetIp: this.injectIp,
+ targetPort: this.injectPort,
+ data: parsedData
+ }),
+ });
+
+ const data = await response.json();
+
+ if (data.success) {
+ this.showSuccess('数据已成功发送');
+ } else {
+ this.showError(data.error || '发送数据失败');
+ }
+ } catch (error) {
+ this.showError('无法连接到服务器');
+ console.error('发送UDP数据失败:', error);
+ }
+ }
+
+ setupDataFetch() {
+ // 清除可能存在的之前的定时器
+ if (this.dataFetchInterval) {
+ clearInterval(this.dataFetchInterval);
+ }
+
+ // 每秒拉取一次新数据
+ this.dataFetchInterval = setInterval(async () => {
+ try {
+ const response = await fetch('/api/udp-monitor/data');
+ const data = await response.json();
+
+ if (data.success && data.data) {
+ this.updateDataDisplay(data.data);
+ }
+ } catch (error) {
+ console.error('获取UDP数据失败:', error);
+ }
+ }, 1000);
+ }
+
+ updateDataDisplay(newData) {
+ if (!newData || newData.length === 0) return;
+
+ // 更新数据并限制显示的数据条数
+ this.udpData = [...this.udpData, ...newData].slice(-100);
+
+ const dataContainer = this.shadowRoot.querySelector('.data-container');
+ if (!dataContainer) return;
+
+ dataContainer.innerHTML = this.udpData.map(item => {
+ return `
+ ${new Date(item.timestamp).toLocaleTimeString()}
+ ${item.source}
+ ${this.formatData(item.data)}
+
`;
+ }).join('');
+
+ // 自动滚动到底部
+ dataContainer.scrollTop = dataContainer.scrollHeight;
+ }
+
+ formatData(data) {
+ // 简单显示为字符串,真实实现可能更复杂
+ if (typeof data === 'object') {
+ return JSON.stringify(data);
+ }
+ return data;
+ }
+
+ updateMonitoringStatus() {
+ const statusLabel = this.shadowRoot.querySelector('.status-label');
+ const startButton = this.shadowRoot.querySelector('#start-monitoring');
+ const stopButton = this.shadowRoot.querySelector('#stop-monitoring');
+ const portInput = this.shadowRoot.querySelector('#udp-port');
+ const ipInput = this.shadowRoot.querySelector('#udp-ip');
+
+ if (statusLabel) {
+ statusLabel.textContent = this.isMonitoring ? '监控中' : '未监控';
+ statusLabel.className = `status-label ${this.isMonitoring ? 'active' : 'inactive'}`;
+ }
+
+ if (startButton) {
+ startButton.disabled = this.isMonitoring;
+ }
+
+ if (stopButton) {
+ stopButton.disabled = !this.isMonitoring;
+ }
+
+ if (portInput) {
+ portInput.disabled = this.isMonitoring;
+ }
+
+ if (ipInput) {
+ ipInput.disabled = this.isMonitoring;
+ }
+ }
+
+ showError(message) {
+ const errorElement = this.shadowRoot.querySelector('.error-message');
+ if (errorElement) {
+ errorElement.textContent = message;
+ errorElement.style.display = 'block';
+ errorElement.className = 'message-box error-message';
+
+ // 3秒后自动隐藏错误消息
+ setTimeout(() => {
+ errorElement.style.display = 'none';
+ }, 3000);
+ }
+ }
+
+ showSuccess(message) {
+ const successElement = this.shadowRoot.querySelector('.success-message');
+ if (successElement) {
+ successElement.textContent = message;
+ successElement.style.display = 'block';
+
+ // 3秒后自动隐藏成功消息
+ setTimeout(() => {
+ successElement.style.display = 'none';
+ }, 3000);
+ }
+ }
+
render() {
this.shadowRoot.innerHTML = `
-
`;
+
+ // 添加切换模式的事件监听
+ this.shadowRoot.getElementById('udp-mode').addEventListener('click', () => this.switchMode('udp'));
+ this.shadowRoot.getElementById('fastdds-mode').addEventListener('click', () => this.switchMode('fastdds'));
+
+ // 如果是UDP模式,添加控制按钮的事件监听
+ if (this.currentMode === 'udp') {
+ // 监控部分事件监听
+ // IP输入事件
+ const ipInput = this.shadowRoot.getElementById('udp-ip');
+ if (ipInput) {
+ ipInput.addEventListener('change', (e) => {
+ const value = e.target.value.trim();
+ // 简单的IP地址验证,可以接受0.0.0.0或具体IP
+ if (value === '0.0.0.0' || /^(\d{1,3}\.){3}\d{1,3}$/.test(value)) {
+ this.udpIp = value;
+ } else {
+ e.target.value = this.udpIp;
+ this.showError('请输入有效的IP地址');
+ }
+ });
+ }
+
+ // 端口输入事件
+ const portInput = this.shadowRoot.getElementById('udp-port');
+ if (portInput) {
+ portInput.addEventListener('change', (e) => {
+ const value = parseInt(e.target.value, 10);
+ if (value >= 1024 && value <= 65535) {
+ this.udpPort = value;
+ } else {
+ e.target.value = this.udpPort;
+ this.showError('端口号必须在1024-65535之间');
+ }
+ });
+ }
+
+ // 开始监控按钮
+ const startButton = this.shadowRoot.getElementById('start-monitoring');
+ if (startButton) {
+ startButton.addEventListener('click', () => this.startMonitoring());
+ }
+
+ // 停止监控按钮
+ const stopButton = this.shadowRoot.getElementById('stop-monitoring');
+ if (stopButton) {
+ stopButton.addEventListener('click', () => this.stopMonitoring());
+ }
+
+ // 数据注入部分事件监听
+ // 注入IP输入事件
+ const injectIpInput = this.shadowRoot.getElementById('inject-ip');
+ if (injectIpInput) {
+ injectIpInput.addEventListener('change', (e) => {
+ const value = e.target.value.trim();
+ if (/^(\d{1,3}\.){3}\d{1,3}$/.test(value)) {
+ this.injectIp = value;
+ } else {
+ e.target.value = this.injectIp;
+ this.showError('请输入有效的IP地址');
+ }
+ });
+ }
+
+ // 注入端口输入事件
+ const injectPortInput = this.shadowRoot.getElementById('inject-port');
+ if (injectPortInput) {
+ injectPortInput.addEventListener('change', (e) => {
+ const value = parseInt(e.target.value, 10);
+ if (value >= 1024 && value <= 65535) {
+ this.injectPort = value;
+ } else {
+ e.target.value = this.injectPort;
+ this.showError('端口号必须在1024-65535之间');
+ }
+ });
+ }
+
+ // 注入数据输入事件
+ const injectDataInput = this.shadowRoot.getElementById('inject-data');
+ if (injectDataInput) {
+ injectDataInput.addEventListener('change', (e) => {
+ this.injectData = e.target.value;
+ });
+ }
+
+ // 发送数据按钮
+ const injectButton = this.shadowRoot.getElementById('inject-data-btn');
+ if (injectButton) {
+ injectButton.addEventListener('click', () => this.injectUdpData());
+ }
+ }
}
}
diff --git a/XNSimHtml/routes/udp-monitor.js b/XNSimHtml/routes/udp-monitor.js
new file mode 100644
index 0000000..dc749e9
--- /dev/null
+++ b/XNSimHtml/routes/udp-monitor.js
@@ -0,0 +1,257 @@
+const express = require('express');
+const router = express.Router();
+const dgram = require('dgram');
+
+// 全局变量存储UDP服务器实例和接收到的数据
+let udpServer = null;
+let udpData = [];
+let isMonitoring = false;
+let currentPort = null;
+let currentIp = null;
+
+// 开始UDP监控
+router.post('/start', (req, res) => {
+ try {
+ const { port, ip } = req.body;
+
+ // 验证端口参数
+ if (!port || port < 1024 || port > 65535) {
+ return res.status(400).json({
+ success: false,
+ error: '端口号必须在1024-65535之间'
+ });
+ }
+
+ // 验证IP参数
+ if (!ip) {
+ return res.status(400).json({
+ success: false,
+ error: 'IP地址不能为空'
+ });
+ }
+
+ // 如果已经在监控,先停止之前的监控
+ if (isMonitoring) {
+ stopUdpServer();
+ }
+
+ // 创建UDP服务器
+ udpServer = dgram.createSocket('udp4');
+ currentPort = port;
+ currentIp = ip;
+
+ // 清空之前的数据
+ udpData = [];
+
+ // 监听错误事件
+ udpServer.on('error', (err) => {
+ console.error(`UDP服务器错误:`, err);
+ isMonitoring = false;
+ udpServer.close();
+ udpServer = null;
+ });
+
+ // 监听消息事件
+ udpServer.on('message', (msg, rinfo) => {
+ // 将收到的数据添加到数据队列
+ try {
+ let processedData;
+
+ // 尝试将数据解析为JSON
+ try {
+ processedData = JSON.parse(msg.toString());
+ } catch (e) {
+ // 如果不是JSON,则保存为字符串
+ processedData = msg.toString();
+ }
+
+ // 将数据添加到队列
+ udpData.push({
+ timestamp: Date.now(),
+ source: `${rinfo.address}:${rinfo.port}`,
+ data: processedData
+ });
+
+ // 限制数据队列大小,最多保存1000条记录
+ if (udpData.length > 1000) {
+ udpData = udpData.slice(-1000);
+ }
+
+ } catch (error) {
+ console.error('处理UDP数据时出错:', error);
+ }
+ });
+
+ // 监听监听事件
+ udpServer.on('listening', () => {
+ const address = udpServer.address();
+ console.log(`UDP数据监控已启动在 ${address.address}:${address.port}`);
+ isMonitoring = true;
+ });
+
+ // 绑定IP和端口
+ udpServer.bind(port, ip);
+
+ res.json({
+ success: true,
+ message: `UDP监控已在 ${ip}:${port} 上启动`
+ });
+
+ } catch (error) {
+ console.error('启动UDP监控失败:', error);
+ res.status(500).json({
+ success: false,
+ error: '启动UDP监控失败: ' + error.message
+ });
+ }
+});
+
+// 停止UDP监控
+router.post('/stop', (req, res) => {
+ try {
+ if (!isMonitoring) {
+ return res.json({
+ success: true,
+ message: '没有正在运行的UDP监控'
+ });
+ }
+
+ stopUdpServer();
+
+ res.json({
+ success: true,
+ message: 'UDP监控已停止'
+ });
+
+ } catch (error) {
+ console.error('停止UDP监控失败:', error);
+ res.status(500).json({
+ success: false,
+ error: '停止UDP监控失败: ' + error.message
+ });
+ }
+});
+
+// 注入UDP数据
+router.post('/inject', (req, res) => {
+ try {
+ const { targetIp, targetPort, data } = req.body;
+
+ // 验证参数
+ if (!targetIp) {
+ return res.status(400).json({
+ success: false,
+ error: '目标IP地址不能为空'
+ });
+ }
+
+ if (!targetPort || targetPort < 1024 || targetPort > 65535) {
+ return res.status(400).json({
+ success: false,
+ error: '目标端口号必须在1024-65535之间'
+ });
+ }
+
+ if (!data) {
+ return res.status(400).json({
+ success: false,
+ error: '数据内容不能为空'
+ });
+ }
+
+ // 创建临时UDP客户端
+ const client = dgram.createSocket('udp4');
+
+ // 将数据对象转换为JSON字符串
+ const message = JSON.stringify(data);
+
+ // 发送数据
+ client.send(message, targetPort, targetIp, (err) => {
+ // 关闭客户端
+ client.close();
+
+ if (err) {
+ console.error('发送UDP数据失败:', err);
+ return res.status(500).json({
+ success: false,
+ error: '发送UDP数据失败: ' + err.message
+ });
+ }
+
+ console.log(`已发送UDP数据到 ${targetIp}:${targetPort}`);
+ res.json({
+ success: true,
+ message: `数据已成功发送到 ${targetIp}:${targetPort}`
+ });
+ });
+
+ } catch (error) {
+ console.error('发送UDP数据失败:', error);
+ res.status(500).json({
+ success: false,
+ error: '发送UDP数据失败: ' + error.message
+ });
+ }
+});
+
+// 获取UDP监控数据
+router.get('/data', (req, res) => {
+ try {
+ // 创建一个新数组来保存返回的数据
+ const dataToSend = [...udpData];
+
+ // 清空数据队列
+ udpData = [];
+
+ res.json({
+ success: true,
+ data: dataToSend,
+ isMonitoring,
+ port: currentPort,
+ ip: currentIp
+ });
+
+ } catch (error) {
+ console.error('获取UDP监控数据失败:', error);
+ res.status(500).json({
+ success: false,
+ error: '获取UDP监控数据失败: ' + error.message
+ });
+ }
+});
+
+// 获取当前监控状态
+router.get('/status', (req, res) => {
+ res.json({
+ success: true,
+ isMonitoring,
+ port: currentPort,
+ ip: currentIp
+ });
+});
+
+// 辅助函数:停止UDP服务器
+function stopUdpServer() {
+ if (udpServer) {
+ try {
+ udpServer.close();
+ console.log('UDP监控已停止');
+ } catch (error) {
+ console.error('关闭UDP服务器时出错:', error);
+ }
+
+ udpServer = null;
+ isMonitoring = false;
+ currentPort = null;
+ currentIp = null;
+ }
+}
+
+// 当应用程序关闭时,确保UDP服务器也关闭
+process.on('exit', stopUdpServer);
+process.on('SIGINT', () => {
+ stopUdpServer();
+ process.exit(0);
+});
+
+module.exports = router;
\ No newline at end of file
diff --git a/XNSimHtml/server.js b/XNSimHtml/server.js
index ebd3526..27b4577 100644
--- a/XNSimHtml/server.js
+++ b/XNSimHtml/server.js
@@ -17,6 +17,7 @@ const idlRoutes = require('./routes/idl');
const projectModelRoutes = require('./routes/project-model');
const ataChaptersRoutes = require('./routes/model-dev');
const simulationRoutes = require('./routes/run-simulation');
+const udpMonitorRoutes = require('./routes/udp-monitor');
const app = express();
const PORT = process.env.PORT || 3000;
@@ -64,6 +65,7 @@ app.use('/api/idl', idlRoutes);
app.use('/api', projectModelRoutes);
app.use('/api', ataChaptersRoutes);
app.use('/api', simulationRoutes);
+app.use('/api/udp-monitor', udpMonitorRoutes);
// 主页路由
app.get('/', (req, res) => {