diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 00000000000..e69de29bb2d diff --git a/qgcimages.qrc b/qgcimages.qrc index 716acb55ab5..34b07fe572f 100644 --- a/qgcimages.qrc +++ b/qgcimages.qrc @@ -214,5 +214,11 @@ src/FlightMap/Images/ZoomMinus.svg src/FlightMap/Images/ZoomPlus.svg src/Viewer3D/Images/city_3d_map_icon.svg + resources/EcamIcon.svg + src/Engine/images/EngineIcon.svg + src/Engine/images/ElectronicIcon.svg + src/Engine/images/FuelIcon.svg + src/Engine/images/FctlIcon.svg + src/Engine/images/CursieIcon.svg diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index e74bb3f8837..ef9e976c738 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -358,6 +358,8 @@ src/UI/preferences/DebugWindow.qml src/UI/preferences/MockLink.qml src/UI/preferences/MockLinkSettings.qml + src/Engine/EngineStatus.qml + src/Engine/EngineSummary.qml src/FirstRunPromptDialogs/UnitsFirstRunPrompt.qml diff --git a/resources/EcamIcon.svg b/resources/EcamIcon.svg new file mode 100644 index 00000000000..1143796e88b --- /dev/null +++ b/resources/EcamIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/API/QGCCorePlugin.cc b/src/API/QGCCorePlugin.cc index 44ed383734d..9065bfbb126 100644 --- a/src/API/QGCCorePlugin.cc +++ b/src/API/QGCCorePlugin.cc @@ -108,6 +108,34 @@ const QVariantList &QGCCorePlugin::analyzePages() return analyzeList; } +//Create ECAM Pages List +const QVariantList &QGCCorePlugin::ecamPages() +{ + static const QVariantList ecamList = { + QVariant::fromValue(new QmlComponentInfo( + tr("Engine"), + QUrl::fromUserInput(QStringLiteral("qrc:/qml/EngineSummary.qml")), + QUrl::fromUserInput(QStringLiteral("qrc:/qmlimages/EngineIcon.svg")))), + QVariant::fromValue(new QmlComponentInfo( + tr("Electronic"), + QUrl::fromUserInput(QStringLiteral("qrc:/qml/LogDownloadPage.qml")), + QUrl::fromUserInput(QStringLiteral("qrc:/qmlimages/ElectronicIcon.svg")))), + QVariant::fromValue(new QmlComponentInfo( + tr("Fuel"), + QUrl::fromUserInput(QStringLiteral("qrc:/qml/LogDownloadPage.qml")), + QUrl::fromUserInput(QStringLiteral("qrc:/qmlimages/FuelIcon.svg")))), + QVariant::fromValue(new QmlComponentInfo + (tr("F/Ctl"), + QUrl::fromUserInput(QStringLiteral("qrc:/qml/LogDownloadPage.qml")), + QUrl::fromUserInput(QStringLiteral("qrc:/qmlimages/FctlIcon.svg")))), + QVariant::fromValue(new QmlComponentInfo + (tr("Crusie"), + QUrl::fromUserInput(QStringLiteral("qrc:/qml/LogDownloadPage.qml")), + QUrl::fromUserInput(QStringLiteral("qrc:/qmlimages/CursieIcon.svg")))), + }; + return ecamList; +} + QGCOptions *QGCCorePlugin::options() { return _defaultOptions; diff --git a/src/API/QGCCorePlugin.h b/src/API/QGCCorePlugin.h index 25635fb783f..dbfcbfe6b79 100644 --- a/src/API/QGCCorePlugin.h +++ b/src/API/QGCCorePlugin.h @@ -49,6 +49,8 @@ class QGCCorePlugin : public QObject Q_PROPERTY(QString showAdvancedUIMessage READ showAdvancedUIMessage CONSTANT) Q_PROPERTY(QVariantList analyzePages READ analyzePages CONSTANT) Q_PROPERTY(QVariantList toolBarIndicators READ toolBarIndicators CONSTANT) + //Register of ECAM Page List + Q_PROPERTY(QVariantList ecamPages READ ecamPages CONSTANT) public: explicit QGCCorePlugin(QObject *parent = nullptr); @@ -63,6 +65,10 @@ class QGCCorePlugin : public QObject /// @return A list of QmlPageInfo virtual const QVariantList &analyzePages(); + ///The list of pages/buttons under the ECAM Menu + ///@return A list of QmlPageInfo + virtual const QVariantList &ecamPages(); + /// The default settings panel to show /// @return The settings index virtual int defaultSettings() { return 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 166b20546b6..3fa6e2be7d6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,5 +78,6 @@ add_subdirectory(UTMSP) add_subdirectory(Vehicle) add_subdirectory(VideoManager) add_subdirectory(Viewer3D) +add_subdirectory(Engine) add_subdirectory(QtLocationPlugin) diff --git a/src/Engine/CMakeLists.txt b/src/Engine/CMakeLists.txt new file mode 100644 index 00000000000..54969c9e315 --- /dev/null +++ b/src/Engine/CMakeLists.txt @@ -0,0 +1,21 @@ +# =================================================================== +# Engine Status Subsystem +# =================================================================== +target_sources(${CMAKE_PROJECT_NAME} + PRIVATE + Engine.cc + Engine.h +) +target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + +QT_ADD_LIBRARY(EngineStatusModule STATIC) + +QT_ADD_QML_MODULE(EngineStatusModule + URI QGroundControl.EngineStatus + VERSION 1.0 + RESOURCE_PREFIX /qml + QML_FILES + EngineStatus.qml + EngineSummary.qml + NO_PLUGIN +) diff --git a/src/Engine/Engine.cc b/src/Engine/Engine.cc new file mode 100644 index 00000000000..8fdf2543e96 --- /dev/null +++ b/src/Engine/Engine.cc @@ -0,0 +1 @@ +#include "Engine.h" diff --git a/src/Engine/Engine.h b/src/Engine/Engine.h new file mode 100644 index 00000000000..6f70f09beec --- /dev/null +++ b/src/Engine/Engine.h @@ -0,0 +1 @@ +#pragma once diff --git a/src/Engine/EngineStatus.qml b/src/Engine/EngineStatus.qml new file mode 100644 index 00000000000..ef7729d9626 --- /dev/null +++ b/src/Engine/EngineStatus.qml @@ -0,0 +1,127 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Window +import QtQuick.Controls + +import QGroundControl +import QGroundControl.Palette +import QGroundControl.Controls +import QGroundControl.Controllers +import QGroundControl.ScreenTools + +Rectangle { + id: _root + color: qgcPal.window + z: QGroundControl.zOrderTopMost + + signal popout() + + readonly property real _defaultTextHeight: ScreenTools.defaultFontPixelHeight + readonly property real _defaultTextWidth: ScreenTools.defaultFontPixelWidth + readonly property real _horizontalMargin: _defaultTextWidth / 2 + readonly property real _verticalMargin: _defaultTextHeight / 2 + readonly property real _buttonWidth: _defaultTextWidth * 18 + + GeoTagController { + id: geoController + } + + QGCFlickable { + id: buttonScroll + width: buttonColumn.width + anchors.topMargin: _defaultTextHeight / 2 + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: _horizontalMargin + anchors.left: parent.left + contentHeight: buttonColumn.height + flickableDirection: Flickable.VerticalFlick + clip: true + + Column { + id: buttonColumn + width: _maxButtonWidth + spacing: _defaultTextHeight / 2 + + property real _maxButtonWidth: 0 + + Component.onCompleted: reflowWidths() + + // I don't know why this does not work + Connections { + target: QGroundControl.settingsManager.appSettings.appFontPointSize + onValueChanged: buttonColumn.reflowWidths() + } + + function reflowWidths() { + buttonColumn._maxButtonWidth = 0 + for (var i = 0; i < children.length; i++) { + buttonColumn._maxButtonWidth = Math.max(buttonColumn._maxButtonWidth, children[i].width) + } + for (var j = 0; j < children.length; j++) { + children[j].width = buttonColumn._maxButtonWidth + } + } + + Repeater { + id: buttonRepeater + model: QGroundControl.corePlugin ? QGroundControl.corePlugin.ecamPages : [] + + Component.onCompleted: itemAt(0).checked = true + + SubMenuButton { + id: subMenu + imageResource: modelData.icon + autoExclusive: true + text: modelData.title + + onClicked: { + panelLoader.source = modelData.url + panelLoader.title = modelData.title + checked = true + } + } + } + } + } + + Rectangle { + id: divider + anchors.topMargin: _verticalMargin + anchors.bottomMargin: _verticalMargin + anchors.leftMargin: _horizontalMargin + anchors.left: buttonScroll.right + anchors.top: parent.top + anchors.bottom: parent.bottom + width: 1 + color: qgcPal.windowShade + } + + Loader { + id: panelLoader + anchors.topMargin: _verticalMargin + anchors.bottomMargin: _verticalMargin + anchors.leftMargin: _horizontalMargin + anchors.rightMargin: _horizontalMargin + anchors.left: divider.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + source: "qrc:/qml/EngineSummary.qml" + + property string title + + Connections { + target: panelLoader.item + onPopout: mainWindow.createrWindowedAnalyzePage(panelLoader.title, panelLoader.source) + } + } +} diff --git a/src/Engine/EngineSummary.qml b/src/Engine/EngineSummary.qml new file mode 100644 index 00000000000..4f57f7cf4e1 --- /dev/null +++ b/src/Engine/EngineSummary.qml @@ -0,0 +1,281 @@ +import QtQuick +import QtQuick.Shapes +import QtQuick.Layouts + +Item { + id: root + anchors.fill: parent + + component ToggleSwitchBlue: Rectangle { + id: toggleBlue + width: 180 + height: 60 + radius: 6 + color: checked ? "#42A5F5" : "#424242" + border.color: "#666" + border.width: 1 + + property string text: "" + property bool checked: false + + Text { + anchors.centerIn: parent + text: toggleBlue.text + color: "white" + font.bold: true + font.pixelSize: 15 + } + + MouseArea { + anchors.fill: parent + onClicked: toggleBlue.checked = !toggleBlue.checked + } + } + + component ToggleSwitchGreen: Rectangle { + id: toggleGreen + width: 180 + height: 60 + radius: 6 + color: checked ? "#4CAF50" : "#424242" + border.color: "#666" + border.width: 1 + + property string text: "" + property bool checked: false + + Text { + anchors.centerIn: parent + text: toggleGreen.text + color: "white" + font.bold: true + font.pixelSize: 15 + } + + MouseArea { + anchors.fill: parent + onClicked: toggleGreen.checked = !toggleGreen.checked + } + } + + component ToggleSwitchRed: Rectangle { + id: toggleRed + width: 180 + height: 60 + radius: 6 + color: checked ? "#424242" : "#af504c" + border.color: "#666" + border.width: 1 + + property string text: "" + property bool checked: false + + Text { + anchors.centerIn: parent + text: toggleRed.text + color: "white" + font.bold: true + font.pixelSize: 15 + } + + MouseArea { + anchors.fill: parent + onClicked: toggleRed.checked = !toggleRed.checked + } + } + component ToggleSwitchAmber: Rectangle { + id: toggleAmber + width: 180 + height: 60 + radius: 6 + color: checked ? "#ffbf00" : "#424242" + border.color: "#666" + border.width: 1 + + property string text: "" + property bool checked: false + + Text { + anchors.centerIn: parent + text: toggleAmber.text + color: "white" + font.bold: true + font.pixelSize: 15 + } + + MouseArea { + anchors.fill: parent + onClicked: toggleAmber.checked = !toggleAmber.checked + } + } + + + component BarGauge: Rectangle { + id: gauge + width: 70 + height: 240 + color: "#2a2a2a" + border.color: "#555" + border.width: 1 + radius: 4 + + property string label: "" + property real value: 0 + property real maxValue: 100 + property string unit: "" + property color barColor: "#2196F3" + property real percentage: Math.min(Math.max(value / maxValue, 0), 1) + + Column { + anchors.fill: parent + anchors.margins: 6 + spacing: 6 + + Text { + width: parent.width + text: gauge.value.toFixed(0) + color: "white" + font.bold: true + font.pixelSize: 14 + horizontalAlignment: Text.AlignHCenter + height: 16 + } + + Text { + width: parent.width + text: gauge.unit + color: "#aaa" + font.pixelSize: 10 + horizontalAlignment: Text.AlignHCenter + height: 12 + } + + Item { + width: parent.width + height: parent.height - 76 + anchors.horizontalCenter: parent.horizontalCenter + + Rectangle { + anchors.fill: parent + color: "#1a1a1a" + radius: 2 + } + + Rectangle { + width: parent.width - 10 + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + height: parent.height * gauge.percentage + radius: 2 + color: gauge.percentage > 0.8 ? "#f44336" : gauge.barColor + } + + Repeater { + model: 5 + Rectangle { + width: parent.width + height: 1 + color: "#444" + anchors.bottom: parent.bottom + anchors.bottomMargin: parent.height / 4 * index + opacity: 0.5 + } + } + } + + Text { + width: parent.width + height: 40 + text: gauge.label + color: "white" + font.bold: true + font.pixelSize: 11 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.Wrap + lineHeight: 1.2 + } + } + } + + // 左上角开关布局 + Column { + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 20 + spacing: 20 + + ToggleSwitchAmber { text: "Fuel Pump 1" } + ToggleSwitchAmber { text: "Fuel Pump 2"} + ToggleSwitchBlue { text: "Water Cooler Fan 1"} + ToggleSwitchBlue { text: "Water Cooler Fan 2" } + ToggleSwitchBlue { text: "Inter Cooler Fan" } + ToggleSwitchGreen { text: "Engine Start" } + ToggleSwitchRed { text: "Emergency STOP"} + } + + // 右侧横向仪表布局 + Row { + anchors.top: parent.top + anchors.right: parent.right + anchors.margins: 20 + spacing: 20 + + BarGauge { + id: rpmGauge + label: "Engine\nRPM" + value: 2500 + maxValue: 6000 + unit: "rpm" + barColor: "#42A5F5" + } + + BarGauge { + id: intakeGauge + label: "Intake\nTemp" + value: 45 + maxValue: 100 + unit: "°C" + barColor: "#FFA726" + } + + BarGauge { + id: exhaustGauge + label: "Exhaust\nTemp" + value: 720 + maxValue: 900 + unit: "°C" + barColor: "#EF5350" + } + + BarGauge { + id: oilGauge + label: "Oil\nTemp" + value: 85 + maxValue: 120 + unit: "°C" + barColor: "#66BB6A" + } + } + + // 数据更新定时器 + Timer { + interval: 800 // 每800毫秒更新一次 + running: true + repeat: true + + onTriggered: { + // Engine RPM: 1000-2800 之间波动(模拟引擎转速) + rpmGauge.value = 4000 + Math.random() * 1800 + + // Intake Temp: 30-80 度之间缓慢变化 + intakeGauge.value = 30 + Math.random() * 50 + + // Exhaust Temp: 600-850 度之间变化(较高温度) + exhaustGauge.value = 600 + Math.random() * 250 + + // Oil Temp: 70-110 度之间变化 + oilGauge.value = 70 + Math.random() * 40 + } + } +} diff --git a/src/Engine/images/CursieIcon.svg b/src/Engine/images/CursieIcon.svg new file mode 100644 index 00000000000..9f484e9b888 --- /dev/null +++ b/src/Engine/images/CursieIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Engine/images/ElectronicIcon.svg b/src/Engine/images/ElectronicIcon.svg new file mode 100644 index 00000000000..7d9b27e3777 --- /dev/null +++ b/src/Engine/images/ElectronicIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Engine/images/EngineIcon.svg b/src/Engine/images/EngineIcon.svg new file mode 100644 index 00000000000..b0c363d6434 --- /dev/null +++ b/src/Engine/images/EngineIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Engine/images/FctlIcon.svg b/src/Engine/images/FctlIcon.svg new file mode 100644 index 00000000000..f3fbc264a93 --- /dev/null +++ b/src/Engine/images/FctlIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/Engine/images/FuelIcon.svg b/src/Engine/images/FuelIcon.svg new file mode 100644 index 00000000000..c4b24f46f61 --- /dev/null +++ b/src/Engine/images/FuelIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/GPS/CMakeLists.txt b/src/GPS/CMakeLists.txt index 4b75ad46c95..17b8e4b0ddd 100644 --- a/src/GPS/CMakeLists.txt +++ b/src/GPS/CMakeLists.txt @@ -28,7 +28,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_ CPMAddPackage( NAME px4-gpsdrivers GITHUB_REPOSITORY PX4/PX4-GPSDrivers - GIT_TAG main + GIT_TAG caf5158061bd10e79c9f042abb62c86bc6f3e7a7 SOURCE_SUBDIR src ) diff --git a/src/UI/MainRootWindow.qml b/src/UI/MainRootWindow.qml index a2d1fa02a66..9f65ecc7cd8 100644 --- a/src/UI/MainRootWindow.qml +++ b/src/UI/MainRootWindow.qml @@ -167,6 +167,11 @@ ApplicationWindow { } } + //Add Engine EngineStatus icon + function showEngineStatus(){ + showTool(qsTr("ECAM"), "qrc:/qml/src/Engine/EngineStatus.qml", "/qmlimages/EcamIcon.svg") + } + //------------------------------------------------------------------------- //-- Global simple message dialog @@ -359,6 +364,19 @@ ApplicationWindow { } } } + //Add engineButton + SubMenuButton{ + id: engineButton + height: toolSelectDialog._toolButtonHeight + Layout.fillWidth: true + text: qsTr("ECAM") + imageResource: "/qmlimages/EcamIcon.svg" + visible: QGroundControl.corePlugin.showAdvancedUI + onClicked:{ + if(mainWindow.allowViewSwitch()){ + mainWindow.closeIndicatorDrawer() + mainWindow.showEngineStatus() + } SubMenuButton { id: analyzeButton