QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GCSControlIndicator.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Layouts
3
4import QGroundControl
5import QGroundControl.Controls
6import QGroundControl.FactControls
7
8Item {
9 id: control
10 width: controlIndicatorIconGCS.width * 1.1
11 anchors.top: parent.top
12 anchors.bottom: parent.bottom
13
14 property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
15 property bool showIndicator: activeVehicle && activeVehicle.firstControlStatusReceived
16 property var sysidInControl: activeVehicle ? activeVehicle.sysidInControl : 0
17 property bool gcsControlStatusFlags_SystemManager: activeVehicle ? activeVehicle.gcsControlStatusFlags_SystemManager : false
18 property bool gcsControlStatusFlags_TakeoverAllowed: activeVehicle ? activeVehicle.gcsControlStatusFlags_TakeoverAllowed : false
19 property Fact requestControlAllowTakeoverFact: QGroundControl.settingsManager.flyViewSettings.requestControlAllowTakeover
20 property bool requestControlAllowTakeover: requestControlAllowTakeoverFact.rawValue
21 property bool isThisGCSinControl: sysidInControl == QGroundControl.settingsManager.mavlinkSettings.gcsMavlinkSystemID.rawValue
22 property bool sendControlRequestAllowed: activeVehicle ? activeVehicle.sendControlRequestAllowed : false
23
24 property var margins: ScreenTools.defaultFontPixelWidth
25 property var panelRadius: ScreenTools.defaultFontPixelWidth * 0.5
26 property var buttonHeight: height * 1.6
27 property var squareButtonPadding: ScreenTools.defaultFontPixelWidth
28 property var separatorHeight: buttonHeight * 0.9
29 property var settingsPanelVisible: false
30 property bool outdoorPalette: qgcPal.globalTheme === QGCPalette.Light
31
32 // Used by control request popup, when other GCS ask us for control
33 property var receivedRequestTimeoutMs: QGroundControl.settingsManager.flyViewSettings.requestControlTimeout.defaultValue // Use this as default in case something goes wrong. Usually it will be overriden on onRequestOperatorControlReceived
34 property var requestSysIdRequestingControl: 0
35 property var requestAllowTakeover: false
36
37 signal triggerAnimations // Used to trigger animation inside the popup component
38
39 Connections {
40 target: activeVehicle
41 // Popup prompting user to accept control from other GCS
42 function onRequestOperatorControlReceived(sysIdRequestingControl, allowTakeover, requestTimeoutSecs) {
43 // If we don't have the indicator visible ( not receiving CONTROL_STATUS ) don't proceed
44 if (!control.showIndicator) {
45 return
46 }
47 requestSysIdRequestingControl = sysIdRequestingControl
48 requestAllowTakeover = allowTakeover
49 // If request came without request timeout, use our default one
50 receivedRequestTimeoutMs = requestTimeoutSecs !== 0 ? requestTimeoutSecs * 1000 : QGroundControl.settingsManager.flyViewSettings.requestControlTimeout.defaultValue
51 // First hide current popup, in case the normal control panel is visible
52 mainWindow.closeIndicatorDrawer()
53 // When showing the popup, the component will automatically start the count down in controlRequestPopup
54 mainWindow.showIndicatorDrawer(controlRequestPopup, control)
55 }
56 // Animation to blink indicator when any related info changes
57 function onGcsControlStatusChanged() {
58 backgroundRectangle.doOpacityAnimation()
59 triggerAnimations() // Needed for animation inside the popup component
60 }
61 }
62
63 // Background to have animation when current system in control changes
64 Rectangle {
65 id: backgroundRectangle
66 anchors.centerIn: parent
67 width: height
68 height: parent.height * 1.4
69 color: isThisGCSinControl ? qgcPal.colorGreen : qgcPal.text
70 radius: margins
71 opacity: 0.0
72
73 function doOpacityAnimation() { opacityAnimation.restart() }
74 SequentialAnimation on opacity {
75 id: opacityAnimation
76 running: false
77 loops: 1
78 PropertyAnimation { to: 0.4; duration: 150 }
79 PropertyAnimation { to: 0.5; duration: 100 }
80 PropertyAnimation { to: 0.0; duration: 150 }
81 }
82 }
83
84 // Control request popup. Appears when other GCS requests control, so operator on this one can accept or deny it
85 Component {
86 id: controlRequestPopup
87 ToolIndicatorPage {
88
89 TimedProgressTracker {
90 id: requestProgressTracker
91 timeoutSeconds: receivedRequestTimeoutMs * 0.001
92 onTimeout: mainWindow.closeIndicatorDrawer()
93 }
94
95 Component.onCompleted: {
96 requestProgressTracker.start()
97 }
98
99 contentComponent: GridLayout {
100 id: mainLayout
101 columns: 3
102
103 // Action label
104 QGCLabel {
105 font.pointSize: ScreenTools.defaultFontPointSize * 1.1
106 text: qsTr("GCS ") + requestSysIdRequestingControl + qsTr(" is requesting control")
107 font.bold: true
108 Layout.columnSpan: 2
109 }
110 QGCButton {
111 text: qsTr("Allow <br> takeover")
112 Layout.rowSpan: 2
113 Layout.leftMargin: margins * 2
114 Layout.alignment: Qt.AlignBottom
115 Layout.fillHeight: true
116 onClicked: {
117 control.activeVehicle.requestOperatorControl(true) // Allow takeover
118 mainWindow.closeIndicatorDrawer()
119 // After allowing takeover, if other GCS does not take control within 10 seconds
120 // takeover will be set to not allowed again. Notify user about this
121 control.activeVehicle.startTimerRevertAllowTakeover()
122 mainWindow.showIndicatorDrawer(allowTakeoverExpirationPopup, control)
123 }
124 }
125 // Action label
126 QGCLabel {
127 font.pointSize: ScreenTools.defaultFontPointSize * 1.1
128 text: qsTr("Ignoring automatically in ") + requestProgressTracker.progressLabel + qsTr(" seconds")
129 }
130 QGCButton {
131 id: ignoreButton
132 text: qsTr("Ignore")
133 onClicked: mainWindow.closeIndicatorDrawer()
134 Layout.alignment: Qt.AlignHCenter
135 }
136 // Actual progress bar
137 Rectangle {
138 id: overlayRectangle
139 height: ScreenTools.defaultFontPixelWidth
140 width: parent.width * requestProgressTracker.progress
141 color: qgcPal.buttonHighlight
142 Layout.columnSpan: 2
143 }
144 }
145 }
146 }
147
148 // Allow takeover expiration time popup. When a request is received and takeover was allowed, this popup alerts
149 // that after vehicle::REQUEST_OPERATOR_CONTROL_ALLOW_TAKEOVER_TIMEOUT_MSECS seconds, this GCS will change back to takeover not allowed, as per mavlink specs
150 Component {
151 id: allowTakeoverExpirationPopup
152
153 ToolIndicatorPage {
154 // Allow takeover expiration time popup. When a request is received and takeover was allowed, this popup alerts
155 // that after vehicle::REQUEST_OPERATOR_CONTROL_ALLOW_TAKEOVER_TIMEOUT_MSECS seconds, this GCS will change back to takeover not allowed, as per mavlink specs
156 TimedProgressTracker {
157 id: revertTakeoverProgressTracker
158 timeoutSeconds: control.activeVehicle.operatorControlTakeoverTimeoutMsecs * 0.001
159 onTimeout: {
160 mainWindow.closeIndicatorDrawer()
161 }
162 }
163 // After accepting allow takeover after a request, we show the 10 seconds countdown after which takeover will be set again to not allowed.
164 // If during this time another GCS takes control, which is what we are expecting, remove this popup
165 property var isThisGCSinControlLocal: control.isThisGCSinControl
166 onIsThisGCSinControlLocalChanged: {
167 if (visible && !isThisGCSinControlLocal) {
168 mainWindow.closeIndicatorDrawer()
169 }
170 }
171
172 Component.onCompleted: {
173 revertTakeoverProgressTracker.start()
174 }
175
176 contentComponent: GridLayout {
177 id: mainLayout
178 columns: 3
179
180 // Action label
181 QGCLabel {
182 font.pointSize: ScreenTools.defaultFontPointSize * 1.1
183 text: qsTr("Reverting back to takeover not allowed if GCS ") + requestSysIdRequestingControl +
184 qsTr(" doesn't take control in ") + revertTakeoverProgressTracker.progressLabel +
185 qsTr(" seconds ...")
186 }
187 QGCButton {
188 id: ignoreButton
189 text: qsTr("Ignore")
190 onClicked: mainWindow.closeIndicatorDrawer()
191 Layout.alignment: Qt.AlignHCenter
192 }
193 }
194 }
195 }
196
197 // Popup panel, appears when clicking top control indicator
198 Component {
199 id: controlPopup
200
201 ToolIndicatorPage {
202
203 TimedProgressTracker {
204 id: sendRequestProgressTracker
205 timeoutSeconds: QGroundControl.settingsManager.flyViewSettings.requestControlTimeout.rawValue
206 }
207 // If a request was sent, and we get feedback that takeover has been allowed, stop the progress tracker as the request has been granted
208 property var takeoverAllowedLocal: control.gcsControlStatusFlags_TakeoverAllowed
209 onTakeoverAllowedLocalChanged: {
210 if (takeoverAllowedLocal && sendRequestProgressTracker.running) {
211 sendRequestProgressTracker.stop()
212 }
213 }
214
215 Component.onCompleted: {
216 // If send control request is not allowed it means we recently sent a request, closed the popup, and opened again
217 // before the other request timeout expired. This way we can keep track of the time remaining and update UI accordingly
218 if (!sendControlRequestAllowed) {
219 // vehicle.requestOperatorControlRemainingMsecs holds the time remaining for the current request
220 startProgressTracker(control.activeVehicle.requestOperatorControlRemainingMsecs * 0.001)
221 }
222 }
223
224 function startProgressTracker(timeoutSeconds) {
225 sendRequestProgressTracker.timeoutSeconds = timeoutSeconds
226 sendRequestProgressTracker.start()
227 }
228
229 contentComponent: GridLayout {
230 id: mainLayout
231 columns: 2
232
233 QGCLabel {
234 text: qsTr("System in control: ")
235 font.bold: true
236 }
237 QGCLabel {
238 text: isThisGCSinControl ? (qsTr("This GCS") + " (" + sysidInControl + ")" ) : sysidInControl
239 font.bold: isThisGCSinControl
240 color: isThisGCSinControl ? qgcPal.colorGreen : qgcPal.text
241 Layout.alignment: Qt.AlignRight
242 Layout.fillWidth: true
243 horizontalAlignment: Text.AlignRight
244 }
245 QGCLabel {
246 text: gcsControlStatusFlags_TakeoverAllowed ? qsTr("Takeover allowed") : qsTr("Takeover NOT allowed")
247 Layout.columnSpan: 2
248 Layout.alignment: Qt.AlignRight
249 Layout.fillWidth: true
250 horizontalAlignment: Text.AlignRight
251 color: gcsControlStatusFlags_TakeoverAllowed ? qgcPal.colorGreen : qgcPal.text
252 }
253 // Separator
254 Rectangle {
255 Layout.columnSpan: 2
256 Layout.preferredWidth: parent.width
257 Layout.alignment: Qt.AlignHCenter
258 color: qgcPal.windowShade
259 height: outdoorPalette ? 1 : 2
260 }
261 QGCLabel {
262 text: qsTr("Send Control Request:")
263 Layout.columnSpan: 2
264 visible: !isThisGCSinControl
265 }
266 QGCLabel {
267 text: qsTr("Change takeover condition:")
268 Layout.columnSpan: 2
269 visible: isThisGCSinControl
270 }
271 QGCLabel {
272 id: requestSentTimeoutLabel
273 text: qsTr("Request sent: ") + sendRequestProgressTracker.progressLabel
274 Layout.columnSpan: 2
275 visible: sendRequestProgressTracker.running
276 }
277 FactCheckBox {
278 text: qsTr("Allow takeover")
279 fact: requestControlAllowTakeoverFact
280 enabled: gcsControlStatusFlags_TakeoverAllowed || isThisGCSinControl
281 }
282 QGCButton {
283 text: gcsControlStatusFlags_TakeoverAllowed ? qsTr("Adquire Control") : qsTr("Send Request")
284 onClicked: {
285 var timeout = gcsControlStatusFlags_TakeoverAllowed ? 0 : QGroundControl.settingsManager.flyViewSettings.requestControlTimeout.rawValue
286 control.activeVehicle.requestOperatorControl(requestControlAllowTakeoverFact.rawValue, timeout)
287 if (timeout > 0) {
288 // Start UI timeout animation
289 startProgressTracker(timeout)
290 }
291 }
292 Layout.alignment: Qt.AlignRight
293 visible: !isThisGCSinControl
294 enabled: !sendRequestProgressTracker.running
295 }
296 QGCLabel {
297 text: qsTr("Request Timeout (sec):")
298 visible: !isThisGCSinControl && !gcsControlStatusFlags_TakeoverAllowed
299 }
300 FactTextField {
301 fact: QGroundControl.settingsManager.flyViewSettings.requestControlTimeout
302 visible: !isThisGCSinControl && !gcsControlStatusFlags_TakeoverAllowed
303 Layout.alignment: Qt.AlignRight
304 Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 7
305 }
306 QGCButton {
307 text: qsTr("Change")
308 onClicked: control.activeVehicle.requestOperatorControl(requestControlAllowTakeoverFact.rawValue)
309 visible: isThisGCSinControl
310 Layout.alignment: Qt.AlignRight
311 enabled: gcsControlStatusFlags_TakeoverAllowed != requestControlAllowTakeoverFact.rawValue
312 }
313 // Separator
314 Rectangle {
315 Layout.columnSpan: 2
316 Layout.preferredWidth: parent.width
317 Layout.alignment: Qt.AlignHCenter
318 color: qgcPal.windowShade
319 height: outdoorPalette ? 1 : 2
320 }
321 LabelledFactTextField {
322 Layout.fillWidth: true
323 Layout.columnSpan: 2
324 label: qsTr("This GCS Mavlink System ID: ")
325 fact: QGroundControl.settingsManager.mavlinkSettings.gcsMavlinkSystemID
326 }
327 }
328 }
329 }
330
331 // Actual top toolbar indicator
332 QGCColoredImage {
333 id: controlIndicatorIconLine
334 width: height
335 anchors.top: parent.top
336 anchors.bottom: parent.bottom
337 source: "/gcscontrolIndicator/gcscontrol_line.svg"
338 fillMode: Image.PreserveAspectFit
339 sourceSize.height: height
340 color: isThisGCSinControl ? qgcPal.colorGreen : qgcPal.text
341 }
342 QGCColoredImage {
343 id: controlIndicatorIconAircraft
344 width: height
345 anchors.top: parent.top
346 anchors.bottom: parent.bottom
347 source: "/gcscontrolIndicator/gcscontrol_device.svg"
348 fillMode: Image.PreserveAspectFit
349 sourceSize.height: height
350 color: (isThisGCSinControl || gcsControlStatusFlags_TakeoverAllowed) ? qgcPal.colorGreen : qgcPal.text
351 }
352 QGCColoredImage {
353 id: controlIndicatorIconGCS
354 width: height
355 anchors.top: parent.top
356 anchors.bottom: parent.bottom
357 source: "/gcscontrolIndicator/gcscontrol_gcs.svg"
358 fillMode: Image.PreserveAspectFit
359 sourceSize.height: height
360 color: qgcPal.text
361
362 // Current GCS in control indicator
363 QGCLabel {
364 id: gcsInControlIndicator
365 text: sysidInControl
366 font.bold: true
367 font.pointSize: ScreenTools.smallFontPointSize * 1.1
368 color: isThisGCSinControl ? qgcPal.colorGreen : qgcPal.text
369 anchors.bottom: parent.bottom
370 anchors.bottomMargin: -margins * 0.7
371 anchors.right: parent.right
372 anchors.rightMargin: -margins * 0.1
373 }
374 }
375
376 MouseArea {
377 anchors.fill: parent
378 onClicked: {
379 mainWindow.showIndicatorDrawer(controlPopup, control)
380 }
381 }
382}