From 3ec47bdc720becbad410caf3e2429ce5c60a77b9 Mon Sep 17 00:00:00 2001 From: jinchao <383321154@qq.com> Date: Mon, 28 Apr 2025 16:41:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0UDP=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Release/Scenario/NewConfig.xml | 22 - Release/Scenario/NewConfig1.xml | 22 - Release/Scenario/NewConfig2.sce | 12 - Release/Scenario/NewConfig3.xml | 14 - Release/Scenario/OnlyFlightModel.xml | 4 +- ...ghtModel1.xml => OnlyFlightModel_60Hz.xml} | 4 +- Release/Scenario/OnlyFlightModel_test.xml | 16 + Release/Services/123.scfg | 12 - Release/Services/XNUDPService.scfg | 28 +- Release/Services/XNUDPService2.scfg | 15 - Release/Services/XNUDPTestService.scfg | 17 + Release/shell/XNCore.conf | 6 +- Release/shell/XNCore.sh | 2 +- .../XNATA04DataProcessor.cpp | 467 ++++++++++--- .../XNATA04DataProcessor.h | 2 + XNServices/XNUDPService/XNUDPService.cpp | 53 +- .../.vscode/c_cpp_properties.json | 9 + .../XNUDPTestService/.vscode/settings.json | 5 + XNServices/XNUDPTestService/CMakeLists.txt | 65 ++ .../XNUDPTestService/CMakeLists.txt.user | 424 ++++++++++++ .../XNUDPTestService/XNUDPTestService.cpp | 135 ++++ .../XNUDPTestService/XNUDPTestService.h | 29 + .../XNUDPTestService/XNUDPTestService.scfg | 17 + .../XNUDPTestService_global.h | 12 + .../XNUDPTestService/XNUDPTestService_p.h | 17 + XNSimHtml/components/data-monitor.js | 637 +++++++++++++++++- XNSimHtml/routes/udp-monitor.js | 257 +++++++ XNSimHtml/server.js | 2 + 28 files changed, 2081 insertions(+), 224 deletions(-) delete mode 100644 Release/Scenario/NewConfig.xml delete mode 100644 Release/Scenario/NewConfig1.xml delete mode 100644 Release/Scenario/NewConfig2.sce delete mode 100644 Release/Scenario/NewConfig3.xml rename Release/Scenario/{OnlyFlightModel1.xml => OnlyFlightModel_60Hz.xml} (89%) create mode 100755 Release/Scenario/OnlyFlightModel_test.xml delete mode 100644 Release/Services/123.scfg mode change 100755 => 100644 Release/Services/XNUDPService.scfg delete mode 100644 Release/Services/XNUDPService2.scfg create mode 100644 Release/Services/XNUDPTestService.scfg create mode 100755 XNServices/XNUDPTestService/.vscode/c_cpp_properties.json create mode 100644 XNServices/XNUDPTestService/.vscode/settings.json create mode 100755 XNServices/XNUDPTestService/CMakeLists.txt create mode 100755 XNServices/XNUDPTestService/CMakeLists.txt.user create mode 100755 XNServices/XNUDPTestService/XNUDPTestService.cpp create mode 100755 XNServices/XNUDPTestService/XNUDPTestService.h create mode 100755 XNServices/XNUDPTestService/XNUDPTestService.scfg create mode 100755 XNServices/XNUDPTestService/XNUDPTestService_global.h create mode 100755 XNServices/XNUDPTestService/XNUDPTestService_p.h create mode 100644 XNSimHtml/routes/udp-monitor.js diff --git a/Release/Scenario/NewConfig.xml b/Release/Scenario/NewConfig.xml deleted file mode 100644 index c08e06f..0000000 --- a/Release/Scenario/NewConfig.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Release/Scenario/NewConfig1.xml b/Release/Scenario/NewConfig1.xml deleted file mode 100644 index c08e06f..0000000 --- a/Release/Scenario/NewConfig1.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Release/Scenario/NewConfig2.sce b/Release/Scenario/NewConfig2.sce deleted file mode 100644 index 53107fa..0000000 --- a/Release/Scenario/NewConfig2.sce +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Release/Scenario/NewConfig3.xml b/Release/Scenario/NewConfig3.xml deleted file mode 100644 index c5be7d7..0000000 --- a/Release/Scenario/NewConfig3.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Release/Scenario/OnlyFlightModel.xml b/Release/Scenario/OnlyFlightModel.xml index 32c6a7a..95a7d15 100755 --- a/Release/Scenario/OnlyFlightModel.xml +++ b/Release/Scenario/OnlyFlightModel.xml @@ -1,13 +1,13 @@ - + - + diff --git a/Release/Scenario/OnlyFlightModel1.xml b/Release/Scenario/OnlyFlightModel_60Hz.xml similarity index 89% rename from Release/Scenario/OnlyFlightModel1.xml rename to Release/Scenario/OnlyFlightModel_60Hz.xml index 38c823e..ff7ea7d 100755 --- a/Release/Scenario/OnlyFlightModel1.xml +++ b/Release/Scenario/OnlyFlightModel_60Hz.xml @@ -5,8 +5,8 @@ Version="11" RTXVersion="preempt-rt" CPUAffinity="0,1" - BaseFrequency="120" - WorkPath="/home/jin/Myprj/XNSim/Release/" + BaseFrequency="60" + WorkPath="/home/jin/MyCode/XNSim/Release/" ModelsPath="Models/" ServicesPath="Services/" DomainID="10" diff --git a/Release/Scenario/OnlyFlightModel_test.xml b/Release/Scenario/OnlyFlightModel_test.xml new file mode 100755 index 0000000..d8b9960 --- /dev/null +++ b/Release/Scenario/OnlyFlightModel_test.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Release/Services/123.scfg b/Release/Services/123.scfg deleted file mode 100644 index ceb35da..0000000 --- a/Release/Services/123.scfg +++ /dev/null @@ -1,12 +0,0 @@ - - - 123 - 服务描述 - sdgsdf - 1.0.0 - 2025-04-21 16:16:14 - 2025-04-21 16:16:46 - - - - diff --git a/Release/Services/XNUDPService.scfg b/Release/Services/XNUDPService.scfg old mode 100755 new mode 100644 index b44206f..8fa1279 --- a/Release/Services/XNUDPService.scfg +++ b/Release/Services/XNUDPService.scfg @@ -1,17 +1,17 @@ - XNUDPService - UDP通信服务 - Jin - 1.0.0 - 2025-02-04 10:00:00 - 2025-02-04 10:00:00 - - - - - 12345 - 127.0.0.1 - 54321 - + XNUDPService + UDP通信服务 + Jin + 1.0.0 + 2025-02-04 10:00:00 + 2025-02-04 10:00:00 + + + + + 12345 + 127.0.0.1 + 54321 + diff --git a/Release/Services/XNUDPService2.scfg b/Release/Services/XNUDPService2.scfg deleted file mode 100644 index 0c7a440..0000000 --- a/Release/Services/XNUDPService2.scfg +++ /dev/null @@ -1,15 +0,0 @@ - - - XNUDPService - UDP通信服务 - Jin - 1.0.0 - 2025-02-04 10:00:00 - 2025-02-04 10:00:00 - - - 12345 - 127.0.0.1 - 54321 - - diff --git a/Release/Services/XNUDPTestService.scfg b/Release/Services/XNUDPTestService.scfg new file mode 100644 index 0000000..d80e723 --- /dev/null +++ b/Release/Services/XNUDPTestService.scfg @@ -0,0 +1,17 @@ + + + XNUDPTestService + UDP通信服务 + Jin + 1.0.0 + 2025-02-04 10:00:00 + 2025-02-04 10:00:00 + + + + + 54321 + 127.0.0.1 + 12345 + + diff --git a/Release/shell/XNCore.conf b/Release/shell/XNCore.conf index 473bcbd..bf0c420 100644 --- a/Release/shell/XNCore.conf +++ b/Release/shell/XNCore.conf @@ -1,3 +1,3 @@ -/home/jin/Myprj/XNSim/Release/lib -/home/jin/Myprj/XNSim/Release/Models -/home/jin/Myprj/XNSim/Release/Services +/home/jin/MyCode/XNSim/Release/lib +/home/jin/MyCode/XNSim/Release/Models +/home/jin/MyCode/XNSim/Release/Services diff --git a/Release/shell/XNCore.sh b/Release/shell/XNCore.sh index 2e82b00..6f105a2 100755 --- a/Release/shell/XNCore.sh +++ b/Release/shell/XNCore.sh @@ -1,4 +1,4 @@ -export XNCore=/home/jin/Myprj/XNSim/Release +export XNCore=/home/jin/MyCode/XNSim/Release export PATH=$XNCore:$PATH export LD_LIBRARY_PATH=$XNCore/lib:$LD_LIBRARY_PATH export QT_QPA_PLATFORM=xcb diff --git a/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.cpp b/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.cpp index d2cc865..0a46310 100755 --- a/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.cpp +++ b/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.cpp @@ -54,6 +54,7 @@ void XNATA04DataProcessor::StepUpdate() Q_D(XNATA04DataProcessor); XNModelObject::StepUpdate(); SendUdpData(); + SendUdpTestData(); } void XNATA04DataProcessor::SendUdpData() @@ -981,97 +982,124 @@ void XNATA04DataProcessor::OnAeroInput(const QVariant &data) { Q_D(XNATA04DataProcessor); QByteArray inputData = data.toByteArray(); - if (inputData.size() <= 5 || inputData[0] != 0x0b || inputData[1] != 0x04 - || inputData[2] != 0x00 || inputData[3] != 0x00 || inputData[4] != inputData.size()) { - LOG_WARNING("ATA04DataProcessor::OnAeroInput: invalid input data"); + + // 基本验证:长度和包头前四个字节 + if (inputData.size() <= 5) { + LOG_WARNING("ATA04DataProcessor::OnAeroInput: data too short, size=%d", inputData.size()); return; } + + // 现在我们知道数据包确实以0b 04 00 00开头 + if (inputData[0] != 0x0b || inputData[1] != 0x04 || inputData[2] != 0x00 + || inputData[3] != 0x00) { + LOG_WARNING("ATA04DataProcessor::OnAeroInput: invalid header, expected 0b 04 00 00, got " + "%02x %02x %02x %02x", + (quint8)inputData[0], (quint8)inputData[1], (quint8)inputData[2], + (quint8)inputData[3]); + return; + } + + // 使用与发送方相同版本的QDataStream读取数据 QDataStream inputStream(&inputData, QIODevice::ReadOnly); inputStream.setByteOrder(QDataStream::LittleEndian); + inputStream.setVersion(QDataStream::Qt_6_0); + + // 跳过5字节的头 for (int i = 0; i < 5; i++) { char tmp; inputStream >> tmp; } + QMutexLocker locker(&d->_aeroInputMutex); - double tmp; - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_alpha_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_alpdot_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_beta_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_press_alt_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_tas_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_mach_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_nx_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_ny_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_nz_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_p_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_q_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_r_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_qbar_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_blcg_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_bscg_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_wlcg_f8(tmp); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_stab_f8(tmp); - std::array tmpArray; - for (int i = 0; i < 10; i++) { - inputStream >> tmpArray[i]; + + // 尝试读取数据,并添加异常处理 + try { + double tmp; + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_alpha_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_alpdot_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_beta_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_press_alt_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_tas_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_mach_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_nx_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_ny_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_nz_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_p_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_q_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_r_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_qbar_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_blcg_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_bscg_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_wlcg_f8(tmp); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_stab_f8(tmp); + std::array tmpArray; + for (int i = 0; i < 10; i++) { + inputStream >> tmpArray[i]; + } + d->_aeroInput.l_04_i_aerocomac_ail_f8(tmpArray); + std::array tmpArray2; + for (int i = 0; i < 4; i++) { + inputStream >> tmpArray2[i]; + } + d->_aeroInput.l_04_i_aerocomac_elv_f8(tmpArray2); + std::array tmpArray3; + for (int i = 0; i < 2; i++) { + inputStream >> tmpArray3[i]; + } + d->_aeroInput.l_04_i_aerocomac_rud_f8(tmpArray3); + std::array tmpArray4; + for (int i = 0; i < 7; i++) { + inputStream >> tmpArray4[i]; + } + d->_aeroInput.l_04_i_aerocomac_gear_f8(tmpArray4); + for (int i = 0; i < 10; i++) { + inputStream >> tmpArray[i]; + } + d->_aeroInput.l_04_i_aerocomac_flap_f8(tmpArray); + std::array tmpArray5; + for (int i = 0; i < 20; i++) { + inputStream >> tmpArray5[i]; + } + d->_aeroInput.l_04_i_aerocomac_slat_f8(tmpArray5); + for (int i = 0; i < 20; i++) { + inputStream >> tmpArray5[i]; + } + d->_aeroInput.l_04_i_aerocomac_spl_f8(tmpArray5); + for (int i = 0; i < 4; i++) { + inputStream >> tmpArray2[i]; + } + d->_aeroInput.l_04_i_aerocomac_tnet_f8(tmpArray2); + for (int i = 0; i < 20; i++) { + inputStream >> tmpArray5[i]; + } + d->_aeroInput.l_04_i_aerocomac_kice_f8(tmpArray5); + inputStream >> tmp; + d->_aeroInput.l_04_i_aerocomac_alt_agl_f8(tmp); + d->_dataWriters["XNSim::ATA04::Aerodynamics_input"]->write(&d->_aeroInput); + } catch (const std::exception &e) { + LOG_WARNING("ATA04DataProcessor::OnAeroInput: exception during data parsing: %s", e.what()); + return; + } catch (...) { + LOG_WARNING("ATA04DataProcessor::OnAeroInput: unknown exception during data parsing"); + return; } - d->_aeroInput.l_04_i_aerocomac_ail_f8(tmpArray); - std::array tmpArray2; - for (int i = 0; i < 4; i++) { - inputStream >> tmpArray2[i]; - } - d->_aeroInput.l_04_i_aerocomac_elv_f8(tmpArray2); - std::array tmpArray3; - for (int i = 0; i < 2; i++) { - inputStream >> tmpArray3[i]; - } - d->_aeroInput.l_04_i_aerocomac_rud_f8(tmpArray3); - std::array tmpArray4; - for (int i = 0; i < 7; i++) { - inputStream >> tmpArray4[i]; - } - d->_aeroInput.l_04_i_aerocomac_gear_f8(tmpArray4); - for (int i = 0; i < 10; i++) { - inputStream >> tmpArray[i]; - } - d->_aeroInput.l_04_i_aerocomac_flap_f8(tmpArray); - std::array tmpArray5; - for (int i = 0; i < 20; i++) { - inputStream >> tmpArray5[i]; - } - d->_aeroInput.l_04_i_aerocomac_slat_f8(tmpArray5); - for (int i = 0; i < 20; i++) { - inputStream >> tmpArray5[i]; - } - d->_aeroInput.l_04_i_aerocomac_spl_f8(tmpArray5); - for (int i = 0; i < 4; i++) { - inputStream >> tmpArray2[i]; - } - d->_aeroInput.l_04_i_aerocomac_tnet_f8(tmpArray2); - for (int i = 0; i < 20; i++) { - inputStream >> tmpArray5[i]; - } - d->_aeroInput.l_04_i_aerocomac_kice_f8(tmpArray5); - inputStream >> tmp; - d->_aeroInput.l_04_i_aerocomac_alt_agl_f8(tmp); - d->_dataWriters["XNSim::ATA04::Aerodynamics_input"]->write(&d->_aeroInput); } void XNATA04DataProcessor::OnWbOutput(const XNSim::ATA04::WeightBalance_output &input) @@ -1115,3 +1143,280 @@ void XNATA04DataProcessor::OnGhHeartbeat(const XNSim::ATA04::GroundHandling_hear QMutexLocker locker(&d->_ghHeartbeatMutex); d->_ghHeartbeat = input; } + +void XNATA04DataProcessor::SendUdpTestData() +{ + Q_D(XNATA04DataProcessor); + + // 创建气动输入(AeroInput)数据包 + QByteArray aeroData; + QDataStream aeroStream(&aeroData, QIODevice::WriteOnly); + aeroStream.setByteOrder(QDataStream::LittleEndian); + aeroStream.setVersion(QDataStream::Qt_6_0); + + // 气动输入数据包头 (0x0b=从外部输入, 0x04=ATA04, 0x00=气动模型, 0x00=输入数据) + quint8 aeroHeader[5] = {0x0b, 0x04, 0x00, 0x00, 0x00}; + aeroStream << aeroHeader[0] << aeroHeader[1] << aeroHeader[2] << aeroHeader[3] << aeroHeader[4]; + + // 气动数据字段 + aeroStream << (double)5.0; // alpha + aeroStream << (double)0.5; // alpdot + aeroStream << (double)1.0; // beta + aeroStream << (double)10000.0; // press_alt + aeroStream << (double)250.0; // tas + aeroStream << (double)0.8; // mach + aeroStream << (double)0.2; // nx + aeroStream << (double)0.0; // ny + aeroStream << (double)1.0; // nz + aeroStream << (double)0.0; // p + aeroStream << (double)0.0; // q + aeroStream << (double)0.0; // r + aeroStream << (double)12000.0; // qbar + aeroStream << (double)0.0; // blcg + aeroStream << (double)0.0; // bscg + aeroStream << (double)0.0; // wlcg + aeroStream << (double)0.0; // stab + + // ail_f8 数组,需要10个double + for (int i = 0; i < 10; i++) { + aeroStream << (double)0.0; + } + + // elv_f8 数组,需要4个double + for (int i = 0; i < 4; i++) { + aeroStream << (double)0.0; + } + + // rud_f8 数组,需要2个double + for (int i = 0; i < 2; i++) { + aeroStream << (double)0.0; + } + + // gear_f8 数组,需要7个double + for (int i = 0; i < 7; i++) { + aeroStream << (double)0.0; + } + + // flap_f8 数组,需要10个double + for (int i = 0; i < 10; i++) { + aeroStream << (double)0.0; + } + + // slat_f8 数组,需要20个double + for (int i = 0; i < 20; i++) { + aeroStream << (double)0.0; + } + + // spl_f8 数组,需要20个double + for (int i = 0; i < 20; i++) { + aeroStream << (double)0.0; + } + + // tnet_f8 数组,需要4个double + for (int i = 0; i < 4; i++) { + aeroStream << (double)20000.0; + } + + // kice_f8 数组,需要20个double + for (int i = 0; i < 20; i++) { + aeroStream << (double)0.0; + } + + // alt_agl + aeroStream << (double)1000.0; + + // 更新数据包大小,确保不会溢出 + if (aeroData.size() <= 255) { + aeroData[4] = aeroData.size(); + } else { + LOG_WARNING("ATA04DataProcessor::SendUdpTestData: aeroData size exceeds byte limit: %d", + aeroData.size()); + aeroData[4] = 255; // 设置为最大值 + } + + // 创建地面操作输入(GhInput)数据包 + QByteArray ghData; + QDataStream ghStream(&ghData, QIODevice::WriteOnly); + ghStream.setByteOrder(QDataStream::LittleEndian); + ghStream.setVersion(QDataStream::Qt_6_0); + // 地面操作输入数据包头 (0x0b=从外部输入, 0x04=ATA04, 0x01=地面操作模型, 0x00=输入数据) + quint8 ghHeader[5] = {0x0b, 0x04, 0x01, 0x00, 0x00}; + ghStream << ghHeader[0] << ghHeader[1] << ghHeader[2] << ghHeader[3] << ghHeader[4]; + + // 地面操作数据字段 + ghStream << (quint8)0; // frz_l1 + ghStream << (quint8)0; // chocks_l1 + ghStream << (double)100.0; // alt_agl_f8 + ghStream << (quint8)0; // frzflt_l1 + ghStream << (double)0.0; // p_f8 + ghStream << (double)0.0; // q_f8 + ghStream << (double)0.0; // r_f8 + ghStream << (double)0.0; // ug_f8 + ghStream << (double)0.0; // vg_f8 + ghStream << (double)0.0; // wg_f8 + ghStream << (double)0.0; // blcg_f8 + ghStream << (double)0.0; // bscg_f8 + ghStream << (double)0.0; // wlcg_f8 + ghStream << (quint8)0; // pb_active_l1 + ghStream << (double)0.0; // pb_towforce_f8 + + // brake_torq_f8 数组 3x2 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ghStream << (double)0.0; + } + } + + // gear_f8 数组,需要3个double + for (int i = 0; i < 3; i++) { + ghStream << (double)0.0; + } + + // 另一个3个double的数组 + for (int i = 0; i < 3; i++) { + ghStream << (double)0.0; + } + + // gsteer_f8 数组,需要10个double + for (int i = 0; i < 10; i++) { + ghStream << (double)0.0; + } + + // tire_pres_f8 数组 3x2 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ghStream << (double)30.0; // 30 psi的胎压 + } + } + + // 4个double数组 + for (int i = 0; i < 4; i++) { + ghStream << (double)0.0; + } + + ghStream << (quint8)0; // onjax_l1 + + // contdep_f8 数组,需要7个double + for (int i = 0; i < 7; i++) { + ghStream << (double)0.0; + } + + ghStream << (double)0.0; // thetag_f8 + ghStream << (double)0.0; // phig_f8 + ghStream << (qint32)0; // rwyrgh_i2 + ghStream << (double)0.0; // rwyhdg_f8 + ghStream << (quint8)0; // reset_braketemp_l1 + ghStream << (quint8)0; // reset_tirepress_l1 + ghStream << (double)20.0; // temp_c_f8 + + // brake_temp_f8 数组 3x2 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ghStream << (double)50.0; // 50度的刹车温度 + } + } + + // tire_tburst_l1 数组 3x2 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ghStream << (char)0; + } + } + + // tire_tflat_l1 数组 3x2 + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + ghStream << (char)0; + } + } + + ghStream << (quint8)0; // brk_reset_tpres_l1 + + // rcon_ci_f8 数组,需要14个double + for (int i = 0; i < 14; i++) { + ghStream << (double)0.0; + } + + ghStream << (qint32)0; // gsteer_state_i4 + ghStream << (quint8)0; // trim_active_l1 + ghStream << (double)0.0; // phi_deg_f8 + ghStream << (double)0.0; // theta_deg_f8 + ghStream << (double)0.0; // psi_deg_f8 + ghStream << (quint8)0; // resetint_l1 + + // 更新数据包大小,确保不会溢出 + if (ghData.size() <= 255) { + ghData[4] = ghData.size(); + } else { + LOG_WARNING("ATA04DataProcessor::SendUdpTestData: ghData size exceeds byte limit: %d", + ghData.size()); + ghData[4] = 255; // 设置为最大值 + } + + // 创建重量平衡输入(WbInput)数据包 + QByteArray wbData; + QDataStream wbStream(&wbData, QIODevice::WriteOnly); + wbStream.setByteOrder(QDataStream::LittleEndian); + wbStream.setVersion(QDataStream::Qt_6_0); + // 重量平衡输入数据包头 (0x0b=从外部输入, 0x04=ATA04, 0x02=重量平衡模型, 0x00=输入数据) + quint8 wbHeader[5] = {0x0b, 0x04, 0x02, 0x00, 0x00}; + wbStream << wbHeader[0] << wbHeader[1] << wbHeader[2] << wbHeader[3] << wbHeader[4]; + + // 重量平衡数据字段 + wbStream << (double)0.0; // theta_deg_f8 + wbStream << (double)0.0; // phi_deg_f8 + wbStream << (double)0.0; // psi_deg_f8 + wbStream << (quint8)0; // gear_mode_l1 + wbStream << (double)60000.0; // acset_gw_f8 (60000kg飞机重量) + wbStream << (double)25.0; // acset_cg_f8 (25%MAC重心位置) + + // acset_tankfuel_f4 数组,需要20个float + for (int i = 0; i < 20; i++) { + wbStream << (float)(i < 10 ? 1000.0 : 0.0); // 前10个油箱各1000kg + } + + wbStream << (double)10000.0; // acset_totfuel_f8 (总油量10000kg) + wbStream << (double)50000.0; // acset_zfw_f8 (零油重50000kg) + wbStream << (double)24.0; // acset_zfwcg_f8 (零油重重心24%MAC) + + // eng_efsep_l1 数组,需要4个char + for (int i = 0; i < 4; i++) { + wbStream << (char)0; + } + + // fuel_f8 数组,需要20个double + for (int i = 0; i < 20; i++) { + wbStream << (double)(i < 10 ? 1000.0 : 0.0); // 前10个油箱各1000kg + } + + wbStream << (double)0.0; // gear_avg_f8 + + // kice_f8 数组,需要20个double + for (int i = 0; i < 20; i++) { + wbStream << (double)0.0; + } + + wbStream << (quint8)0; // bycglim_l1 + wbStream << (quint8)0; // bygwlim_l1 + wbStream << (quint8)0; // frz_l1 + wbStream << (quint8)0; // zcgfrz_l1 + wbStream << (quint8)0; // zcgfrz_grfx_l1 + wbStream << (quint8)0; // ycgfrz_l1 + wbStream << (quint8)0; // inertfrz_l1 + wbStream << (double)60000.0; // potreq_gw_f8 + wbStream << (double)25.0; // potreq_gwcg_f8 + + // 更新数据包大小,确保不会溢出 + if (wbData.size() <= 255) { + wbData[4] = wbData.size(); + } else { + LOG_WARNING("ATA04DataProcessor::SendUdpTestData: wbData size exceeds byte limit: %d", + wbData.size()); + wbData[4] = 255; // 设置为最大值 + } + + // 发送测试数据包 + TriggerRTEvent("SendTestUDPData", QVariant::fromValue(aeroData)); + TriggerRTEvent("SendTestUDPData", QVariant::fromValue(ghData)); + TriggerRTEvent("SendTestUDPData", QVariant::fromValue(wbData)); +} diff --git a/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.h b/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.h index d73b15e..2455ac5 100755 --- a/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.h +++ b/XNModels/XNATA04DataProcessor/XNATA04DataProcessor.h @@ -59,6 +59,8 @@ private: void SendWbHeartbeat(); void SendGhHeartbeat(); + + void SendUdpTestData(); }; Q_DECLARE_METATYPE(XNATA04DataProcessor) diff --git a/XNServices/XNUDPService/XNUDPService.cpp b/XNServices/XNUDPService/XNUDPService.cpp index 9cf5fa5..628ea0a 100755 --- a/XNServices/XNUDPService/XNUDPService.cpp +++ b/XNServices/XNUDPService/XNUDPService.cpp @@ -89,25 +89,56 @@ void XNUDPService::HandleIncomingData() d->udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); - // 处理接收到的数据 - if (datagram.size() <= 5 || datagram[0] != 0x0b || datagram[4] != datagram.size()) { - LOG_WARNING("Invalid UDP datagram received"); + // 数据包头在第9个位置,直接跳过前9个字节 + if (datagram.size() <= 9) { + LOG_WARNING("Invalid size of UDP datagram received, size:%1", datagram.size()); continue; } - if (datagram[1] == 0x04) { - if (datagram[2] == 0x00 && datagram[3] == 0x00) { - TriggerRTEvent("ATA04AeroInput", datagram); + + // 创建一个新的数据包,去除前面的额外头信息 + QByteArray actualData = datagram.mid(9); + + // 使用处理后的数据 + quint8 header = (quint8)actualData[0]; + quint8 type = (quint8)actualData[1]; + quint8 subType1 = (quint8)actualData[2]; + quint8 subType2 = (quint8)actualData[3]; + quint8 size = (quint8)actualData[4]; + + // 处理接收到的数据 + if (actualData.size() <= 5) { + LOG_WARNING("Invalid size of UDP datagram received, size:%1", actualData.size()); + continue; + } + + if (header != 0x0b) { + LOG_WARNING("Invalid header of UDP datagram received, header:%1", header); + continue; + } + + //如果数据包大小大于size,则截取size大小的数据 + if (actualData.size() > size) { + actualData = actualData.mid(0, size); + } else if (actualData.size() < size) { + LOG_WARNING("Invalid size of UDP datagram received, size:%1, expected:%2", + actualData.size(), size); + continue; + } + + if (type == 0x04) { + if (subType1 == 0x00 && subType2 == 0x00) { + TriggerRTEvent("ATA04AeroInput", actualData); continue; - } else if (datagram[2] == 0x01 && datagram[3] == 0x00) { - TriggerRTEvent("ATA04GhInput", datagram); + } else if (subType1 == 0x01 && subType2 == 0x00) { + TriggerRTEvent("ATA04GhInput", actualData); continue; - } else if (datagram[2] == 0x02 && datagram[3] == 0x00) { - TriggerRTEvent("ATA04WbInput", datagram); + } else if (subType1 == 0x02 && subType2 == 0x00) { + TriggerRTEvent("ATA04WbInput", actualData); continue; } } // TODO: 根据具体需求处理其它数据 - TriggerRTEvent("ReceiveUDPData", datagram); + TriggerRTEvent("ReceiveUDPData", actualData); } } diff --git a/XNServices/XNUDPTestService/.vscode/c_cpp_properties.json b/XNServices/XNUDPTestService/.vscode/c_cpp_properties.json new file mode 100755 index 0000000..0b3cbd5 --- /dev/null +++ b/XNServices/XNUDPTestService/.vscode/c_cpp_properties.json @@ -0,0 +1,9 @@ +{ + "configurations": [ + { + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/XNServices/XNUDPTestService/.vscode/settings.json b/XNServices/XNUDPTestService/.vscode/settings.json new file mode 100644 index 0000000..1507357 --- /dev/null +++ b/XNServices/XNUDPTestService/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "functional": "cpp" + } +} \ No newline at end of file diff --git a/XNServices/XNUDPTestService/CMakeLists.txt b/XNServices/XNUDPTestService/CMakeLists.txt new file mode 100755 index 0000000..61ba363 --- /dev/null +++ b/XNServices/XNUDPTestService/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.16) + +project(XNUDPTestService LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# 获取环境变量 +if(DEFINED ENV{XNCore}) + set(XNCore_PATH $ENV{XNCore}) +else() + message(FATAL_ERROR "Environment variable XNCore is not set.") +endif() + +# 添加 XNCore_PATH 下的 include 目录为包含目录 +include_directories(${XNCore_PATH}/include) + +find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core Network Xml) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core Network Xml) + +add_library(XNUDPTestService SHARED + XNUDPTestService_global.h + XNUDPTestService.cpp + XNUDPTestService.h + XNUDPTestService_p.h +) + +target_link_libraries(XNUDPTestService PRIVATE + Qt${QT_VERSION_MAJOR}::Core + Qt${QT_VERSION_MAJOR}::Network + Qt${QT_VERSION_MAJOR}::Xml + ${XNCore_PATH}/lib/libXNCore.so + ) + +# 获取Qt库的安装路径 +get_target_property(QT_LIB_DIR Qt${QT_VERSION_MAJOR}::Core LOCATION) +get_filename_component(QT_LIB_DIR ${QT_LIB_DIR} DIRECTORY) + +# 设置rpath +set_target_properties(XNUDPTestService PROPERTIES + BUILD_WITH_INSTALL_RPATH TRUE + INSTALL_RPATH "${QT_LIB_DIR}" +) + +target_compile_definitions(XNUDPTestService PRIVATE XNUDPSERVICE_LIBRARY) + +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${XNCore_PATH}/Services" CACHE PATH "Install path prefix" FORCE) +endif() + +include(GNUInstallDirs) +install(TARGETS XNUDPTestService + BUNDLE DESTINATION . + LIBRARY DESTINATION . + RUNTIME DESTINATION . +) + +# 添加自定义命令和目标以拷贝配置文件 +file(GLOB CONFIG_FILE "*.scfg") + +# 使用 install 命令在安装时拷贝配置文件 +install(FILES ${CONFIG_FILE} DESTINATION ${CMAKE_INSTALL_PREFIX}) diff --git a/XNServices/XNUDPTestService/CMakeLists.txt.user b/XNServices/XNUDPTestService/CMakeLists.txt.user new file mode 100755 index 0000000..3432be7 --- /dev/null +++ b/XNServices/XNUDPTestService/CMakeLists.txt.user @@ -0,0 +1,424 @@ + + + + + + EnvironmentId + {5b958118-2d32-49ab-8eab-9018ac74c7d6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 2 + true + true + 0 + 8 + true + false + 1 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + false + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 5 + true + + + + true + + + true + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 6.7.2 + Desktop Qt 6.7.2 + qt.qt6.672.linux_gcc_64_kit + 0 + 0 + 0 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_BUILD_TYPE:STRING=Debug + 0 + /media/jin/E/MyCode/xnsim/XNModels/XNAerodynamics/build/Desktop_Qt_6_7_2-Debug + + + + + all + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_BUILD_TYPE:STRING=Release + /media/jin/E/MyCode/xnsim/XNModels/XNAerodynamics/build/Desktop_Qt_6_7_2-Release + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo + /media/jin/E/MyCode/xnsim/XNModels/XNAerodynamics/build/Desktop_Qt_6_7_2-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo + 0 + /media/jin/E/MyCode/xnsim/XNModels/XNAerodynamics/build/Desktop_Qt_6_7_2-Profile + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DCMAKE_GENERATOR:STRING=Unix Makefiles +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel + /media/jin/E/MyCode/xnsim/XNModels/XNAerodynamics/build/Desktop_Qt_6_7_2-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + -e cpu-cycles --call-graph dwarf,4096 -F 250 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/XNServices/XNUDPTestService/XNUDPTestService.cpp b/XNServices/XNUDPTestService/XNUDPTestService.cpp new file mode 100755 index 0000000..8bad458 --- /dev/null +++ b/XNServices/XNUDPTestService/XNUDPTestService.cpp @@ -0,0 +1,135 @@ +#include "XNUDPTestService.h" +#include "XNUDPTestService_p.h" +#include +#include +#include +#include + +XN_DLL_INITIALIZE(XNUDPTestService) + +XN_REGISTER_SERVICE_BEGIN_SERVICE(XNUDPTestService) +XN_REGISTER_SERVICE_END_SERVICE(XNUDPTestService) + +XNUDPTestService::XNUDPTestService(QObject *parent) + : XNServiceObject(*new XNUDPTestServicePrivate(this), parent) +{ +} + +XNUDPTestService::~XNUDPTestService() +{ +} + +XNUDPTestService::XNUDPTestService(XNUDPTestServicePrivate &dd, QObject *parent) + : XNServiceObject(dd, parent) +{ +} + +void XNUDPTestService::OnInitialize() +{ + Q_D(XNUDPTestService); + XNServiceObject::OnInitialize(); + + // 读取配置文件 + QFile file(GetXmlPath()); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + LOG_WARNING("Failed to open config file:%1", GetXmlPath()); + return; + } + + QDomDocument doc; + if (!doc.setContent(&file)) { + file.close(); + LOG_WARNING("Failed to parse config file:%1", GetXmlPath()); + return; + } + file.close(); + + // 读取UDP配置 + QDomElement udpElement = doc.documentElement().firstChildElement("UDP"); + if (!udpElement.isNull()) { + d->localPort = udpElement.firstChildElement("LocalPort").text().toUInt(); + d->targetHost = udpElement.firstChildElement("TargetHost").text(); + d->targetPort = udpElement.firstChildElement("TargetPort").text().toUInt(); + } else { + LOG_WARNING("UDP configuration not found, using default values"); + d->localPort = 12345; + d->targetHost = "127.0.0.1"; + d->targetPort = 54321; + } +} + +void XNUDPTestService::OnPrepareForExecute() +{ + Q_D(XNUDPTestService); + XNServiceObject::OnPrepareForExecute(); + + // 初始化UDP socket + d->udpSocket = new QUdpSocket(this); + + // 绑定本地端口 + if (!d->udpSocket->bind(QHostAddress::Any, d->localPort)) { + LOG_WARNING("UDP socket bind failed on port:%1", d->localPort); + return; + } + + // 连接UDP socket的信号 + connect(d->udpSocket, &QUdpSocket::readyRead, this, &XNUDPTestService::HandleIncomingData); + + RegisterRTEventHandler("SendTestUDPData", + std::bind(&XNUDPTestService::SendData, this, std::placeholders::_1)); +} + +void XNUDPTestService::HandleIncomingData() +{ + Q_D(XNUDPTestService); + return; + while (d->udpSocket->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(d->udpSocket->pendingDatagramSize()); + QHostAddress sender; + quint16 senderPort; + + d->udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort); + + // 处理接收到的数据 + if (datagram.size() <= 5 || datagram[0] != 0x0b || datagram[4] != datagram.size()) { + LOG_WARNING("Invalid UDP datagram received"); + continue; + } + if (datagram[1] == 0x04) { + if (datagram[2] == 0x00 && datagram[3] == 0x00) { + TriggerRTEvent("ATA04AeroInput", datagram); + continue; + } else if (datagram[2] == 0x01 && datagram[3] == 0x00) { + TriggerRTEvent("ATA04GhInput", datagram); + continue; + } else if (datagram[2] == 0x02 && datagram[3] == 0x00) { + TriggerRTEvent("ATA04WbInput", datagram); + continue; + } + } + // TODO: 根据具体需求处理其它数据 + //TriggerRTEvent("ReceiveUDPData", datagram); + } +} + +void XNUDPTestService::SendData(const QVariant &data) +{ + Q_D(XNUDPTestService); + if (!d->udpSocket) { + LOG_WARNING("UDP socket not initialized"); + return; + } + + // 将QVariant转换为字节数组 + QByteArray datagram; + QDataStream stream(&datagram, QIODevice::WriteOnly); + stream << data; + + // 发送数据 + qint64 bytesSent = + d->udpSocket->writeDatagram(datagram, QHostAddress(d->targetHost), d->targetPort); + if (bytesSent == -1) { + LOG_WARNING("Failed to send UDP datagram:%1", d->udpSocket->errorString()); + } +} diff --git a/XNServices/XNUDPTestService/XNUDPTestService.h b/XNServices/XNUDPTestService/XNUDPTestService.h new file mode 100755 index 0000000..0ca8f22 --- /dev/null +++ b/XNServices/XNUDPTestService/XNUDPTestService.h @@ -0,0 +1,29 @@ +#pragma once +#include "XNUDPTestService_global.h" +#include +#include +class XNUDPTestServicePrivate; + +class XNUDPTESTSERVICE_EXPORT XNUDPTestService : public XNServiceObject +{ + Q_OBJECT + Q_DISABLE_COPY(XNUDPTestService) + Q_DECLARE_PRIVATE(XNUDPTestService) + XN_DECLARE_DDS_SERVICE() +public: + explicit XNUDPTestService(QObject *parent = nullptr); + virtual ~XNUDPTestService(); + +protected: + XNUDPTestService(XNUDPTestServicePrivate &dd, QObject *parent = nullptr); + +public slots: + virtual void OnInitialize() override; + virtual void OnPrepareForExecute() override; + void HandleIncomingData(); + +public: + void SendData(const QVariant &data); +}; + +Q_DECLARE_METATYPE(XNUDPTestService) diff --git a/XNServices/XNUDPTestService/XNUDPTestService.scfg b/XNServices/XNUDPTestService/XNUDPTestService.scfg new file mode 100755 index 0000000..d80e723 --- /dev/null +++ b/XNServices/XNUDPTestService/XNUDPTestService.scfg @@ -0,0 +1,17 @@ + + + XNUDPTestService + UDP通信服务 + Jin + 1.0.0 + 2025-02-04 10:00:00 + 2025-02-04 10:00:00 + + + + + 54321 + 127.0.0.1 + 12345 + + diff --git a/XNServices/XNUDPTestService/XNUDPTestService_global.h b/XNServices/XNUDPTestService/XNUDPTestService_global.h new file mode 100755 index 0000000..ad16e20 --- /dev/null +++ b/XNServices/XNUDPTestService/XNUDPTestService_global.h @@ -0,0 +1,12 @@ +#ifndef XNUDPTESTSERVICE_GLOBAL_H +#define XNUDPTESTSERVICE_GLOBAL_H + +#include + +#if defined(XNUDPTESTSERVICE_LIBRARY) +# define XNUDPTESTSERVICE_EXPORT Q_DECL_EXPORT +#else +# define XNUDPTESTSERVICE_EXPORT Q_DECL_IMPORT +#endif + +#endif // XNUDPTESTSERVICE_GLOBAL_H diff --git a/XNServices/XNUDPTestService/XNUDPTestService_p.h b/XNServices/XNUDPTestService/XNUDPTestService_p.h new file mode 100755 index 0000000..636b0bb --- /dev/null +++ b/XNServices/XNUDPTestService/XNUDPTestService_p.h @@ -0,0 +1,17 @@ +#pragma once +#include +#include +#include + +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.currentMode === 'udp' + ? `
UDP数据监控
+
+ +
+
UDP数据监控
+ +
+
+ + +
+
+ + +
+ + +
+ + ${this.isMonitoring ? '监控中' : '未监控'} + +
+
+ +
+ +
+
+
+
+ + +
+ + +
+
UDP数据注入
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ +
+ +
+
+
` + : `
FastDDS数据监控
+
FastDDS数据监控内容(待实现)
`}
-
数据监控组件内容(待实现)
`; + + // 添加切换模式的事件监听 + 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) => {