QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MainStatusIndicator.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Layouts
3
4import QGroundControl
5import QGroundControl.Controls
6
7RowLayout {
8 id: control
9 spacing: ScreenTools.defaultFontPixelWidth
10
11 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
12 property var _guidedController: globals.guidedControllerFlyView
13 property bool _armed: _activeVehicle ? _activeVehicle.armed : false
14 property real _margins: ScreenTools.defaultFontPixelWidth
15 property real _spacing: ScreenTools.defaultFontPixelWidth / 2
16 property bool _healthAndArmingChecksSupported: _activeVehicle ? _activeVehicle.healthAndArmingCheckReport.supported : false
17 property var _vehicleInAir: _activeVehicle ? _activeVehicle.airborne || _activeVehicle.landing : false
18 property bool _vtolInFWDFlight: _activeVehicle ? _activeVehicle.vtolInFwdFlight : false
19 property bool _rebootRequired: _activeVehicle ? _activeVehicle.rebootRequired : false
20
21 function dropMainStatusIndicator() {
22 let overallStatusComponent = _activeVehicle ? overallStatusIndicatorPage : overallStatusOfflineIndicatorPage
23 mainWindow.showIndicatorDrawer(overallStatusComponent, control)
24 }
25
26 QGCPalette { id: qgcPal }
27
28 QGCLabel {
29 id: mainStatusLabel
30 Layout.fillHeight: true
31 Layout.preferredWidth: Math.max(contentWidth,
32 criticalMessageBadge.width + rebootRequiredIcon.width +
33 firmwareUpdateBadge.width + ScreenTools.defaultFontPixelWidth * 0.5)
34 verticalAlignment: Text.AlignVCenter
35 text: mainStatusText()
36 color: qgcPal.text
37 font.pointSize: ScreenTools.largeFontPointSize
38
39 property string _commLostText: qsTr("Comms Lost")
40 property string _readyToFlyText: qsTr("Ready")
41 property string _notReadyToFlyText: qsTr("Not Ready")
42 property string _disconnectedText: qsTr("Disconnected - Click to manually connect")
43 property string _armedText: qsTr("Armed")
44 property string _flyingText: qsTr("Flying")
45 property string _landingText: qsTr("Landing")
46
47 function mainStatusText() {
48 var statusText
49 if (_activeVehicle) {
50 if (_communicationLost) {
51 _mainStatusBGColor = "red"
52 return mainStatusLabel._commLostText
53 }
54 if (_activeVehicle.armed) {
55 _mainStatusBGColor = "green"
56
57 if (_healthAndArmingChecksSupported) {
58 if (_activeVehicle.healthAndArmingCheckReport.canArm) {
59 if (_activeVehicle.healthAndArmingCheckReport.hasWarningsOrErrors) {
60 _mainStatusBGColor = "yellow"
61 }
62 } else {
63 _mainStatusBGColor = "red"
64 }
65 }
66
67 if (_activeVehicle.underway) {
68 return mainStatusLabel._flyingText
69 } else if (_activeVehicle.landing) {
70 return mainStatusLabel._landingText
71 } else {
72 return mainStatusLabel._armedText
73 }
74 } else {
75 if (_healthAndArmingChecksSupported) {
76 if (_activeVehicle.healthAndArmingCheckReport.canArm) {
77 if (_activeVehicle.healthAndArmingCheckReport.hasWarningsOrErrors) {
78 _mainStatusBGColor = "yellow"
79 } else {
80 _mainStatusBGColor = "green"
81 }
82 return mainStatusLabel._readyToFlyText
83 } else {
84 _mainStatusBGColor = "red"
85 return mainStatusLabel._notReadyToFlyText
86 }
87 } else if (_activeVehicle.readyToFlyAvailable) {
88 if (_activeVehicle.readyToFly) {
89 _mainStatusBGColor = "green"
90 return mainStatusLabel._readyToFlyText
91 } else {
92 _mainStatusBGColor = "yellow"
93 return mainStatusLabel._notReadyToFlyText
94 }
95 } else {
96 // Best we can do is determine readiness based on AutoPilot component setup and health indicators from SYS_STATUS
97 if (_activeVehicle.allSensorsHealthy && _activeVehicle.autopilotPlugin.setupComplete) {
98 _mainStatusBGColor = "green"
99 return mainStatusLabel._readyToFlyText
100 } else {
101 _mainStatusBGColor = "yellow"
102 return mainStatusLabel._notReadyToFlyText
103 }
104 }
105 }
106 } else {
107 _mainStatusBGColor = qgcPal.brandPrimary
108 return mainStatusLabel._disconnectedText
109 }
110 }
111
112 Rectangle {
113 id: criticalMessageBadge
114 anchors.top: parent.top
115 anchors.topMargin: ScreenTools.defaultFontPixelHeight * 0.25
116 anchors.right: parent.right
117 width: ScreenTools.defaultFontPixelHeight
118 height: width
119 radius: width / 2
120 color: qgcPal.colorRed
121 visible: _criticalMessageCount > 0
122
123 property int _criticalMessageCount: _activeVehicle ? _activeVehicle.criticalMessageCount : 0
124
125 QGCLabel {
126 anchors.centerIn: parent
127 text: criticalMessageBadge._criticalMessageCount > 9 ? "!" : criticalMessageBadge._criticalMessageCount.toString()
128 color: qgcPal.buttonHighlightText
129 font.pointSize: ScreenTools.smallFontPointSize
130 font.bold: true
131 }
132 }
133
134 QGCColoredImage {
135 id: rebootRequiredIcon
136 objectName: "mainStatusRebootRequiredIcon"
137 anchors.verticalCenter: criticalMessageBadge.verticalCenter
138 anchors.right: criticalMessageBadge.visible ? criticalMessageBadge.left : parent.right
139 anchors.rightMargin: criticalMessageBadge.visible ? ScreenTools.defaultFontPixelWidth * 0.25 : 0
140 width: criticalMessageBadge.width * 0.9
141 height: width
142 source: "/res/PowerButton.svg"
143 color: qgcPal.colorOrange
144 visible: _rebootRequired
145 }
146
147 Rectangle {
148 id: firmwareUpdateBadge
149 objectName: "mainStatusFirmwareUpdateBadge"
150 anchors.verticalCenter: criticalMessageBadge.verticalCenter
151 anchors.right: rebootRequiredIcon.visible ? rebootRequiredIcon.left : (criticalMessageBadge.visible ? criticalMessageBadge.left : parent.right)
152 anchors.rightMargin: (rebootRequiredIcon.visible || criticalMessageBadge.visible) ? ScreenTools.defaultFontPixelWidth * 0.25 : 0
153 width: criticalMessageBadge.width
154 height: width
155 radius: width / 2
156 color: Qt.rgba(1, 1, 1, 0.75)
157 visible: _activeVehicle ? _activeVehicle.newStableFirmwareVersion !== "" && !_activeVehicle.newStableFirmwareVersionAcknowledged : false
158
159 QGCColoredImage {
160 anchors.fill: parent
161 source: "/res/UpdateAvailable.svg"
162 color: qgcPal.colorBlue
163 }
164 }
165
166 QGCMouseArea {
167 anchors.fill: parent
168 onClicked: dropMainStatusIndicator()
169 }
170 }
171
172 QGCLabel {
173 id: vtolModeLabel
174 Layout.fillHeight: true
175 verticalAlignment: Text.AlignVCenter
176 text: _vtolInFWDFlight ? qsTr("FW(vtol)") : qsTr("MR(vtol)")
177 color: qgcPal.text
178 font.pointSize: _vehicleInAir ? ScreenTools.largeFontPointSize : ScreenTools.defaultFontPointSize
179 visible: _activeVehicle && _activeVehicle.vtol
180
181 QGCMouseArea {
182 anchors.fill: parent
183 onClicked: {
184 if (_vehicleInAir) {
185 mainWindow.showIndicatorDrawer(vtolTransitionIndicatorPage)
186 }
187 }
188 }
189 }
190
191 Component {
192 id: overallStatusOfflineIndicatorPage
193
194 MainStatusIndicatorOfflinePage {
195 Component.onCompleted: mainWindow.suppressCriticalVehicleMessages = true
196 Component.onDestruction: mainWindow.suppressCriticalVehicleMessages = false
197 }
198 }
199
200 Component {
201 id: overallStatusIndicatorPage
202
203 ToolIndicatorPage {
204 waitForParameters: false
205 fillWindow: true
206 contentComponent: mainStatusContentComponent
207
208 Component.onCompleted: mainWindow.suppressCriticalVehicleMessages = true
209 Component.onDestruction: mainWindow.suppressCriticalVehicleMessages = false
210 }
211 }
212
213 Component {
214 id: mainStatusContentComponent
215
216 ColumnLayout {
217 id: mainLayout
218 spacing: _spacing
219
220 property bool parametersReady: QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable
221
222 RowLayout {
223 Layout.fillWidth: true
224 spacing: ScreenTools.defaultFontPixelWidth
225 // Stop controls must stay reachable without parameters
226 visible: parametersReady || _guidedController.showEmergencyStop || _guidedController.showDisarm || _rebootRequired
227
228 QGCDelayButton {
229 objectName: "mainStatusArmButton"
230 enabled: _armed || _guidedController.showArm
231 text: _armed ? qsTr("Disarm") : qsTr("Arm")
232 visible: _armed ? _guidedController.showDisarm : parametersReady
233
234 onActivated: {
235 _activeVehicle.armed = !_armed
236 mainWindow.closeIndicatorDrawer()
237 }
238 }
239
240 QGCButton {
241 objectName: "mainStatusEmergencyStopButton"
242 text: qsTr("Emergency Stop")
243 backgroundColor: qgcPal.colorRed
244 textColor: qgcPal.buttonHighlightText
245 fontWeight: Font.Bold
246 visible: _guidedController.showEmergencyStop
247
248 onClicked: {
249 _guidedController.confirmAction(_guidedController.actionEmergencyStop)
250 mainWindow.closeIndicatorDrawer()
251 }
252 }
253
254 QGCDelayButton {
255 text: qsTr("Force Arm")
256 visible: parametersReady && _guidedController.showForceArm
257
258 onActivated: {
259 _activeVehicle.forceArm()
260 mainWindow.closeIndicatorDrawer()
261 }
262 }
263
264 LabelledComboBox {
265 id: primaryLinkCombo
266 Layout.alignment: Qt.AlignTop
267 label: qsTr("Primary Link")
268 alternateText: _primaryLinkName
269 visible: parametersReady && _activeVehicle && _activeVehicle.vehicleLinkManager.linkNames.length > 1
270
271 property var _rgLinkNames: _activeVehicle ? _activeVehicle.vehicleLinkManager.linkNames : [ ]
272 property var _rgLinkStatus: _activeVehicle ? _activeVehicle.vehicleLinkManager.linkStatuses : [ ]
273 property string _primaryLinkName: _activeVehicle ? _activeVehicle.vehicleLinkManager.primaryLinkName : ""
274
275 function updateComboModel() {
276 let linkModel = []
277 for (let i = 0; i < _rgLinkNames.length; i++) {
278 let linkStatus = _rgLinkStatus[i]
279 linkModel.push(_rgLinkNames[i] + (linkStatus === "" ? "" : " " + _rgLinkStatus[i]))
280 }
281 primaryLinkCombo.model = linkModel
282 primaryLinkCombo.currentIndex = -1
283 }
284
285 Component.onCompleted: updateComboModel()
286 on_RgLinkNamesChanged: updateComboModel()
287 on_RgLinkStatusChanged: updateComboModel()
288
289 onActivated: (index) => {
290 _activeVehicle.vehicleLinkManager.primaryLinkName = _rgLinkNames[index]; currentIndex = -1
291 mainWindow.closeIndicatorDrawer()
292 }
293 }
294
295 Item { Layout.fillWidth: true }
296
297 QGCLabel {
298 Layout.maximumWidth: ScreenTools.defaultFontPixelWidth * 16
299 Layout.maximumHeight: rebootButton.height
300 text: qsTr("Reboot required for changes to take effect")
301 font.pointSize: ScreenTools.smallFontPointSize
302 wrapMode: Text.WordWrap
303 elide: Text.ElideRight
304 horizontalAlignment: Text.AlignRight
305 visible: _rebootRequired && !_armed
306 }
307
308 QGCLabel {
309 objectName: "mainStatusRebootDisarmLabel"
310 Layout.maximumWidth: ScreenTools.defaultFontPixelWidth * 16
311 Layout.maximumHeight: rebootButton.height
312 text: qsTr("Reboot required. Disarm vehicle first.")
313 font.pointSize: ScreenTools.smallFontPointSize
314 wrapMode: Text.WordWrap
315 elide: Text.ElideRight
316 horizontalAlignment: Text.AlignRight
317 visible: _rebootRequired && _armed
318 }
319
320 QGCDelayButton {
321 id: rebootButton
322 objectName: "mainStatusRebootButton"
323 text: qsTr("Reboot Vehicle")
324 visible: _rebootRequired && !_armed
325
326 onActivated: {
327 _activeVehicle.rebootVehicle()
328 mainWindow.closeIndicatorDrawer()
329 }
330 }
331 }
332
333 RowLayout {
334 Layout.fillWidth: true
335 spacing: ScreenTools.defaultFontPixelWidth
336 visible: _activeVehicle && _activeVehicle.newStableFirmwareVersion !== ""
337
338 QGCLabel {
339 objectName: "mainStatusFirmwareUpdateLabel"
340 text: _activeVehicle ? qsTr("Firmware update available: %1.%2.%3 → %4")
341 .arg(_activeVehicle.firmwareMajorVersion)
342 .arg(_activeVehicle.firmwareMinorVersion)
343 .arg(_activeVehicle.firmwarePatchVersion)
344 .arg(_activeVehicle.newStableFirmwareVersion) : ""
345 }
346
347 QGCButton {
348 objectName: "mainStatusFirmwareUpdateAckButton"
349 implicitHeight: contentItem.implicitHeight + topPadding + bottomPadding
350 heightFactor: 0.2
351 leftPadding: ScreenTools.defaultFontPixelWidth
352 rightPadding: leftPadding
353 text: qsTr("Acknowledge and Hide")
354 pointSize: ScreenTools.smallFontPointSize
355 visible: _activeVehicle && !_activeVehicle.newStableFirmwareVersionAcknowledged
356 onClicked: _activeVehicle.acknowledgeNewStableFirmwareVersion()
357 }
358 }
359
360 SettingsGroupLayout {
361 Layout.fillWidth: true
362 heading: qsTr("Vehicle Messages")
363
364 VehicleMessageList {
365 id: vehicleMessageList
366 Layout.fillWidth: true
367 visible: !noMessages
368 }
369
370 QGCLabel {
371 text: qsTr("No new vehicle messages")
372 visible: vehicleMessageList.noMessages
373 }
374 }
375
376 SettingsGroupLayout {
377 Layout.fillWidth: true
378 heading: qsTr("Sensor Status")
379 visible: parametersReady && !_healthAndArmingChecksSupported
380
381 GridLayout {
382 rowSpacing: _spacing
383 columnSpacing: _spacing
384 rows: _activeVehicle.sysStatusSensorInfo.sensorNames.length
385 flow: GridLayout.TopToBottom
386
387 Repeater {
388 model: _activeVehicle.sysStatusSensorInfo.sensorNames
389 QGCLabel { text: modelData }
390 }
391
392 Repeater {
393 model: _activeVehicle.sysStatusSensorInfo.sensorStatus
394 QGCLabel { text: modelData }
395 }
396 }
397 }
398
399 SettingsGroupLayout {
400 Layout.fillWidth: true
401 heading: qsTr("Overall Status")
402 visible: parametersReady && _healthAndArmingChecksSupported && _activeVehicle.healthAndArmingCheckReport.problemsForCurrentMode.count > 0
403
404 // List health and arming checks
405 Repeater {
406 model: _activeVehicle ? _activeVehicle.healthAndArmingCheckReport.problemsForCurrentMode : null
407 delegate: listdelegate
408 }
409 }
410
411 Component {
412 id: listdelegate
413
414 ColumnLayout {
415 Layout.fillWidth: true
416 spacing: 0
417
418 RowLayout {
419 Layout.fillWidth: true
420 spacing: ScreenTools.defaultFontPixelHeight
421
422 QGCLabel {
423 id: message
424 Layout.fillWidth: true
425 // Rounded up: the layout's whole-pixel width would otherwise wrap a short message
426 Layout.maximumWidth: Math.ceil(implicitWidth)
427 text: object.message
428 textFormat: TextEdit.RichText
429 wrapMode: Text.WordWrap
430 color: object.severity == 'error' ? qgcPal.colorRed : object.severity == 'warning' ? qgcPal.colorOrange : qgcPal.text
431 MouseArea {
432 anchors.fill: parent
433 onClicked: {
434 if (object.description != "")
435 object.expanded = !object.expanded
436 }
437 }
438 }
439
440 QGCColoredImage {
441 id: arrowDownIndicator
442 Layout.alignment: Qt.AlignVCenter
443 Layout.preferredHeight: 1.5 * ScreenTools.defaultFontPixelWidth
444 Layout.preferredWidth: Layout.preferredHeight
445 source: "/qmlimages/arrow-down.png"
446 color: qgcPal.text
447 visible: object.description != ""
448 MouseArea {
449 anchors.fill: parent
450 onClicked: object.expanded = !object.expanded
451 }
452 }
453 }
454
455 QGCLabel {
456 id: description
457 Layout.fillWidth: true
458 text: object.description
459 textFormat: TextEdit.RichText
460 wrapMode: Text.WordWrap
461 clip: true
462 visible: object.expanded
463
464 property var fact: null
465
466 onLinkActivated: (link) => {
467 if (link.startsWith('param://')) {
468 var paramName = link.substr(8);
469 fact = controller.getParameterFact(-1, paramName, true)
470 if (fact != null) {
471 paramEditorDialogFactory.open()
472 }
473 } else {
474 Qt.openUrlExternally(link);
475 }
476 }
477
478 FactPanelController {
479 id: controller
480 }
481
482 QGCPopupDialogFactory {
483 id: paramEditorDialogFactory
484
485 dialogComponent: paramEditorDialogComponent
486 }
487
488 Component {
489 id: paramEditorDialogComponent
490
491 ParameterEditorDialog {
492 title: qsTr("Edit Parameter")
493 fact: description.fact
494 destroyOnClose: true
495 }
496 }
497 }
498 }
499 }
500 }
501 }
502}