QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
APMSubMotorComponent.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4
5import QGroundControl
6import QGroundControl.Controls
7
8SetupPage {
9 id: motorPage
10 pageComponent: pageComponent
11 enabled: true
12
13 readonly property int _barHeight: 10
14 readonly property int _barWidth: 5
15 readonly property int _sliderHeight: 10
16
17 property int neutralValue: 50;
18 property int _lastIndex: 0;
19 property bool canRunManualTest: controller.vehicle.flightMode !== controller.vehicle.motorDetectionFlightMode && controller.vehicle.armed && motorPage.visible && setupView.visible
20 property var shouldRunManualTest: false // Does the operator intend to run the motor test?
21
22 APMSubMotorComponentController {
23 id: controller
24 }
25
26 function setMotorDirection(num, reversed) {
27 var fact = controller.getParameterFact(-1, "MOT_" + num + "_DIRECTION")
28 fact.value = reversed ? -1 : 1;
29 }
30
31 Component.onCompleted: controller.vehicle.armed = false
32
33 Component {
34 id: pageComponent
35
36 Column {
37 spacing: 10
38
39 Row {
40 id: motorSliders
41 enabled: canRunManualTest && shouldRunManualTest
42 spacing: ScreenTools.defaultFontPixelWidth * 4
43
44 Column {
45 spacing: ScreenTools.defaultFontPixelWidth * 2
46
47 Row {
48 id: sliderRow
49 spacing: ScreenTools.defaultFontPixelWidth * 4
50
51 Repeater {
52 id: sliderRepeater
53 model: controller.vehicle.motorCount == -1 ? 8 : controller.vehicle.motorCount
54
55 Column {
56 property alias motorSlider: slider
57 spacing: ScreenTools.defaultFontPixelWidth
58
59 QGCLabel {
60 anchors.horizontalCenter: parent.horizontalCenter
61 text: index + 1
62 }
63
64 QGCSlider {
65 id: slider
66 height: ScreenTools.defaultFontPixelHeight * _sliderHeight
67 orientation: Qt.Vertical
68 to: 100
69 value: neutralValue
70
71 // Give slider 'center sprung' behavior
72 onPressedChanged: {
73 if (!slider.pressed) {
74 slider.value = neutralValue
75 }
76 _lastIndex = index
77 }
78 // Disable mouse scroll
79 MouseArea {
80 anchors.fill: parent
81 onWheel: (wheel) => {
82 // do nothing
83 wheel.accepted = true;
84 }
85 onPressed: (mouse) => {
86 // propogate/accept
87 mouse.accepted = false;
88 }
89 onReleased: (mouse) => {
90 // propogate/accept
91 mouse.accepted = false;
92 }
93 }
94 }
95 } // Column
96 } // Repeater
97 } // Row
98
99 QGCLabel {
100 width: parent.width
101 anchors.left: parent.left
102 anchors.right: parent.right
103 wrapMode: Text.WordWrap
104 text: qsTr("Reverse Motor Direction")
105 horizontalAlignment: Text.AlignHCenter
106 verticalAlignment: Text.AlignBottom
107 }
108 Rectangle {
109 anchors.margins: ScreenTools.defaultFontPixelWidth * 3
110 width: parent.width
111 height: 1
112 color: qgcPal.text
113 }
114
115 Row {
116 anchors.margins: ScreenTools.defaultFontPixelWidth
117
118 Repeater {
119 id: cbRepeater
120 model: controller.vehicle.motorCount == -1 ? 8 : controller.vehicle.motorCount
121
122 Column {
123 spacing: ScreenTools.defaultFontPixelWidth
124
125 QGCCheckBox {
126 width: sliderRow.width / (controller.vehicle.motorCount - 0.5)
127 checked: controller.getParameterFact(-1, "MOT_" + (index + 1) + "_DIRECTION").value == -1
128 onClicked: {
129 sliderRepeater.itemAt(index).motorSlider.value = neutralValue
130 setMotorDirection(index + 1, checked)
131 }
132 }
133 } // Column
134 } // Repeater
135 } // Row
136 } // Column
137
138 // Display the frame currently in use with motor numbers
139 APMSubMotorDisplay {
140 anchors.top: parent.top
141 anchors.bottom: parent.bottom
142 width: height
143 frameType: controller.getParameterFact(-1, "FRAME_CONFIG").value
144 }
145 } // Row
146
147 QGCLabel {
148 anchors.left: parent.left
149 anchors.right: parent.right
150 wrapMode: Text.WordWrap
151 text: qsTr("Moving the sliders will cause the motors to spin. Make sure the motors and propellers are clear from obstructions! The direction of the motor rotation is dependent on how the three phases of the motor are physically connected to the ESCs (if any two wires are swapped, the direction of rotation will flip). Because we cannot guarantee what order the phases are connected, the motor directions must be configured in software. When a slider is moved DOWN, the thruster should push air/water TOWARD the cable entering the housing. Click the checkbox to reverse the direction of the corresponding thruster.\n\n"
152 + "Blue Robotics thrusters are lubricated by water and are not designed to be run in air. Testing the thrusters in air is ok at low speeds for short periods of time. Extended operation of Blue Robotics in air may lead to overheating and permanent damage. Without water lubrication, Blue Robotics thrusters may also make some unpleasant noises when operated in air; this is normal.")
153 }
154
155 Row {
156 spacing: ScreenTools.defaultFontPixelWidth
157 Switch {
158 id: safetySwitch
159 onToggled: {
160 if (controller.vehicle.armed) {
161 shouldRunManualTest = false
162 enabled = false
163 coolDownTimer.start()
164 }
165
166 controller.vehicle.armed = checked
167 checked = controller.vehicle.armed // Makes the switch stay off if it's not possible to arm
168 }
169 }
170
171 // Make sure external changes to Armed are reflected on the switch
172 Connections {
173 target: controller.vehicle
174 onArmedChanged:
175 {
176 safetySwitch.checked = armed
177 if (!armed) {
178 shouldRunManualTest = false
179 safetySwitch.enabled = false
180 coolDownTimer.start()
181 } else {
182 shouldRunManualTest = true
183 }
184 for (var sliderIndex=0; sliderIndex<sliderRepeater.count; sliderIndex++) {
185 sliderRepeater.itemAt(sliderIndex).motorSlider.value = neutralValue
186 }
187 }
188 }
189
190 QGCLabel {
191 anchors.verticalCenter: safetySwitch.verticalCenter
192 color: qgcPal.warningText
193 text: coolDownTimer.running
194 ? qsTr("A 10 second coooldown is required before testing again, please stand by...")
195 : qsTr("Slide this switch to arm the vehicle and enable the motor test (CAUTION!)")
196 }
197 } // Row
198
199 QGCLabel {
200 visible: controller.vehicle.versionCompare(4, 0, 0) >= 0
201 width: parent.width
202 anchors.left: parent.left
203 anchors.right: parent.right
204 font.pointSize: ScreenTools.largeFontPointSize
205 text: qsTr("Automatic Motor Direction Detection")
206 }
207
208 QGCLabel {
209 visible: controller.vehicle.versionCompare(4, 0, 0) >= 0
210 anchors.left: parent.left
211 anchors.right: parent.right
212 wrapMode: Text.WordWrap
213 text: qsTr("This will attempt to automatically detect the direction (normal/reversed) of your thrusters.\n"
214 + "Please place your vehicle in water, click the button, and wait. Note that the thrusters still need "
215 + "to be connected to the correct outputs (thrusters 2 and 3 can't be swapped, for example).")
216 }
217
218 Row {
219 visible: controller.vehicle.versionCompare(4, 0, 0) >= 0
220 spacing: ScreenTools.defaultFontPixelWidth
221
222 Column {
223 spacing: ScreenTools.defaultFontPixelWidth * 2
224
225 QGCButton {
226 id: startAutoDetection
227 text: "Auto-Detect Directions"
228 enabled: controller.vehicle.flightMode !== controller.vehicle.motorDetectionFlightMode
229
230 onClicked: function() {
231 controller.vehicle.flightMode = controller.vehicle.motorDetectionFlightMode
232 controller.vehicle.armed = true
233 }
234 }
235 }
236 Column {
237 spacing: ScreenTools.defaultFontPixelWidth * 2
238
239 Flickable {
240 id: flickable
241 width: 500
242 height: Math.min(contentHeight, 200)
243 contentWidth: width
244 contentHeight: textArea.implicitHeight
245 clip: true
246
247 TextArea {
248 id: textArea
249 anchors.fill: parent
250 color: qgcPal.text
251 text: controller.motorDetectionMessages
252 wrapMode: Text.WordWrap
253 background: Rectangle {
254 color: qgcPal.window
255 }
256 onTextChanged: function() {
257 flickable.flick(0, -300)
258 }
259 }
260 ScrollBar.vertical: ScrollBar {}
261 }
262 }
263 }
264
265 // Repeats the command signal and updates the checkbox every 50 ms
266 Timer {
267 id: timer
268 interval: 50
269 repeat: true
270 running: canRunManualTest && shouldRunManualTest
271
272 onTriggered: {
273 if (controller.vehicle.armed) {
274 var slider = sliderRepeater.itemAt(_lastIndex)
275
276 var reversed = controller.getParameterFact(-1, "MOT_" + (_lastIndex + 1) + "_DIRECTION").value == -1
277
278 if (reversed) {
279 controller.vehicle.motorTest(_lastIndex, 100 - slider.motorSlider.value, 0, false)
280 } else {
281 controller.vehicle.motorTest(_lastIndex, slider.motorSlider.value, 0, false)
282 }
283 }
284 }
285 }
286 Timer {
287 id: coolDownTimer
288 interval: 11000
289 repeat: false
290
291 onTriggered: {
292 safetySwitch.enabled = true
293 }
294 }
295 } // Column
296 } // Component
297} // SetupPage