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