From b69a146e738151260d28463bf8e676a34dc918a9 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Sat, 7 Mar 2015 09:49:46 -0800 Subject: [PATCH] New Flight Mode config --- qgroundcontrol.pro | 7 +- qgroundcontrol.qrc | 19 +- .../PX4/FlightModesComponent.cc | 11 +- .../PX4/FlightModesComponent.qml | 870 ++++++++++++++++++ .../PX4/FlightModesComponentController.cc | 196 ++++ .../PX4/FlightModesComponentController.h | 95 ++ .../PX4/PX4AutoPilotPlugin.cc | 4 +- src/FactSystem/Fact.h | 2 +- src/FactSystem/FactLoader.cc | 8 +- src/FactSystem/FactLoader.h | 2 +- src/qgcunittest/MockLink.param | 8 +- 11 files changed, 1198 insertions(+), 24 deletions(-) create mode 100644 src/AutoPilotPlugins/PX4/FlightModesComponent.qml create mode 100644 src/AutoPilotPlugins/PX4/FlightModesComponentController.cc create mode 100644 src/AutoPilotPlugins/PX4/FlightModesComponentController.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index aae4cfebfc..ffbc0bf66f 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -676,7 +676,6 @@ HEADERS += \ src/qgcunittest/PX4RCCalibrationTest.h \ src/qgcunittest/LinkManagerTest.h \ src/qgcunittest/MainWindowTest.h \ - src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h \ src/qgcunittest/MavlinkLogTest.h \ src/FactSystem/FactSystemTestBase.h \ src/FactSystem/FactSystemTestPX4.h \ @@ -702,7 +701,6 @@ SOURCES += \ src/qgcunittest/PX4RCCalibrationTest.cc \ src/qgcunittest/LinkManagerTest.cc \ src/qgcunittest/MainWindowTest.cc \ - src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc \ src/qgcunittest/MavlinkLogTest.cc \ src/FactSystem/FactSystemTestBase.cc \ src/FactSystem/FactSystemTestPX4.cc \ @@ -722,7 +720,6 @@ INCLUDEPATH += \ FORMS += \ src/VehicleSetup/ParameterEditor.ui \ src/ui/QGCPX4VehicleConfig.ui \ - src/AutoPilotPlugins/PX4/FlightModeConfig.ui \ src/VehicleSetup/SetupView.ui \ HEADERS+= \ @@ -740,7 +737,7 @@ HEADERS+= \ src/AutoPilotPlugins/PX4/PX4Component.h \ src/AutoPilotPlugins/PX4/RadioComponent.h \ src/AutoPilotPlugins/PX4/FlightModesComponent.h \ - src/AutoPilotPlugins/PX4/FlightModeConfig.h \ + src/AutoPilotPlugins/PX4/FlightModesComponentController.h \ src/AutoPilotPlugins/PX4/AirframeComponent.h \ src/AutoPilotPlugins/PX4/SensorsComponent.h \ src/AutoPilotPlugins/PX4/SensorsComponentController.h \ @@ -762,7 +759,7 @@ SOURCES += \ src/AutoPilotPlugins/PX4/PX4Component.cc \ src/AutoPilotPlugins/PX4/RadioComponent.cc \ src/AutoPilotPlugins/PX4/FlightModesComponent.cc \ - src/AutoPilotPlugins/PX4/FlightModeConfig.cc \ + src/AutoPilotPlugins/PX4/FlightModesComponentController.cc \ src/AutoPilotPlugins/PX4/AirframeComponent.cc \ src/AutoPilotPlugins/PX4/SensorsComponent.cc \ src/AutoPilotPlugins/PX4/SensorsComponentController.cc \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index a75fe6658f..16459b2f42 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -247,15 +247,14 @@ src/test.qml src/QmlControls/QmlTest.qml + src/FactSystem/FactControls/qmldir src/FactSystem/FactControls/FactLabel.qml src/FactSystem/FactControls/FactTextField.qml src/FactSystem/FactControls/FactCheckBox.qml src/FactSystem/FactControls/FactComboBox.qml + src/QmlControls/qmldir - src/QmlControls/SubMenuButton.qml - src/QmlControls/IndicatorButton.qml - src/QmlControls/VehicleRotationCal.qml src/QmlControls/QGCButton.qml src/QmlControls/QGCRadioButton.qml src/QmlControls/QGCCheckBox.qml @@ -263,21 +262,31 @@ src/QmlControls/QGCTextField.qml src/QmlControls/QGCComboBox.qml src/QmlControls/QGCColoredImage.qml - src/QmlControls/arrow-down.png src/QmlControls/QGCToolBarButton.qml + + src/QmlControls/SubMenuButton.qml + src/QmlControls/IndicatorButton.qml + src/QmlControls/VehicleRotationCal.qml + + src/QmlControls/arrow-down.png + files/images/px4/airframes/octo_x.png files/images/px4/boards/px4fmu_2.x.png src/VehicleSetup/SetupViewButtons.qml src/VehicleSetup/VehicleSummary.qml src/VehicleSetup/FirmwareUpgrade.qml + src/AutoPilotPlugins/PX4/SafetyComponent.qml src/AutoPilotPlugins/PX4/SensorsComponent.qml + src/AutoPilotPlugins/PX4/FlightModesComponent.qml + src/AutoPilotPlugins/PX4/SafetyComponentSummary.qml src/AutoPilotPlugins/PX4/SensorsComponentSummary.qml src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml src/AutoPilotPlugins/PX4/RadioComponentSummary.qml src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml + src/AutoPilotPlugins/PX4/Images/SafetyComponentTree.png src/AutoPilotPlugins/PX4/Images/SafetyComponentHome.png src/AutoPilotPlugins/PX4/Images/SafetyComponentArrowDown.png @@ -288,6 +297,7 @@ src/AutoPilotPlugins/PX4/Images/VehicleRight.png src/AutoPilotPlugins/PX4/Images/VehicleNoseDown.png src/AutoPilotPlugins/PX4/Images/VehicleTailDown.png + files/Setup/cogwheels.png src/AutoPilotPlugins/PX4/Images/SensorsComponentIcon.png src/AutoPilotPlugins/PX4/Images/RadioComponentIcon.png @@ -296,6 +306,7 @@ src/AutoPilotPlugins/PX4/Images/SafetyComponentIcon.png src/VehicleSetup/FirmwareUpgradeIcon.png src/VehicleSetup/VehicleSummaryIcon.png + src/ui/toolbar/MainToolBar.qml diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc index 14b64315cf..e573a26c21 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc +++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc @@ -25,8 +25,8 @@ /// @author Don Gagne #include "FlightModesComponent.h" -#include "FlightModeConfig.h" #include "PX4AutoPilotPlugin.h" +#include "QGCQmlWidgetHolder.h" struct SwitchListItem { const char* param; @@ -111,7 +111,14 @@ QStringList FlightModesComponent::paramFilterList(void) const QWidget* FlightModesComponent::setupWidget(void) const { - return new FlightModeConfig(); + QGCQmlWidgetHolder* holder = new QGCQmlWidgetHolder(); + Q_CHECK_PTR(holder); + + holder->setAutoPilot(_autopilot); + + holder->setSource(QUrl::fromUserInput("qrc:/qml/FlightModesComponent.qml")); + + return holder; } QUrl FlightModesComponent::summaryQmlSource(void) const diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.qml b/src/AutoPilotPlugins/PX4/FlightModesComponent.qml new file mode 100644 index 0000000000..8aba6c3494 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.qml @@ -0,0 +1,870 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 + +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Controllers 1.0 + +Item { + Loader { + property FlightModesComponentController controller: FlightModesComponentController { } + property QGCPalette qgcPal: QGCPalette { colorGroupEnabled: true } + property bool loading: true + + anchors.fill: parent + sourceComponent: controller.validConfiguration ? validComponent : invalidComponent + + onLoaded: loading = false + } + + Component { + id: validComponent + + Rectangle { + Fact { id: rc_map_throttle; name: "RC_MAP_THROTTLE" } + Fact { id: rc_map_yaw; name: "RC_MAP_YAW" } + Fact { id: rc_map_pitch; name: "RC_MAP_PITCH" } + Fact { id: rc_map_roll; name: "RC_MAP_ROLL" } + Fact { id: rc_map_flaps; name: "RC_MAP_FLAPS" } + Fact { id: rc_map_aux1; name: "RC_MAP_AUX1" } + Fact { id: rc_map_aux2; name: "RC_MAP_AUX2" } + + Fact { id: rc_map_mode_sw; name: "RC_MAP_MODE_SW" } + Fact { id: rc_map_posctl_sw; name: "RC_MAP_POSCTL_SW" } + Fact { id: rc_map_return_sw; name: "RC_MAP_RETURN_SW" } + Fact { id: rc_map_loiter_sw; name: "RC_MAP_LOITER_SW" } + + Fact { id: rc_assist_th; name: "RC_ASSIST_TH" } + Fact { id: rc_posctl_th; name: "RC_POSCTL_TH" } + Fact { id: rc_auto_th; name: "RC_AUTO_TH" } + Fact { id: rc_loiter_th; name: "RC_LOITER_TH" } + Fact { id: rc_return_th; name: "RC_RETURN_TH" } + + Fact { id: rc_chan_cnt; name: "RC_CHAN_CNT" } + Fact { id: rc_th_user; name: "RC_TH_USER" } + + property int throttleChannel: rc_map_throttle.value + property int yawChannel: rc_map_yaw.value + property int pitchChannel: rc_map_pitch.value + property int rollChannel: rc_map_roll.value + property int flapsChannel: rc_map_flaps.value + property int aux1Channel: rc_map_aux1.value + property int aux2Channel: rc_map_aux2.value + + property int modeChannel: rc_map_mode_sw.value + property int posCtlChannel: rc_map_posctl_sw.value + property int returnChannel: rc_map_return_sw.value + property int loiterChannel: rc_map_loiter_sw.value + + property real rcThUserValue: rc_th_user.value + + readonly property int channelCount: controller.channelCount + + property bool inRedistribution: false + + readonly property int tileWidth: 150 + readonly property int tileHeight: 30 + + readonly property int progressBarHeight: 200 + + anchors.fill: parent + + + color: qgcPal.window + + Component { + id: dragHandle + + Item { + id: outerItem + width: parent.width + height: parent.height + + Column { + x: 4 + y: 4 + spacing: 3 + + Repeater { + model: (outerItem.height - 8) / 4 + Rectangle { + color: qgcPal.text + width: 15 + height: 1 + } + } + } + } + } + + // This component is used to create draggable tiles for unassigned mode switches. It also + // creates the drop area for dragging an assigned mode switch tile back to an unassigned state. + // The following properties must be set in the Loader: + // tileLabel - label for tile + // tileParam - parameter to load from + // tileDragEnabled - true: this tile can be dragged + Component { + id: unassignedModeTileComponent + + Rectangle { + Fact { id: fact; name: tileParam } + property bool dragEnabled: fact.value == 0 + + id: outerRect + width: tileWidth + height: tileHeight + color: qgcPal.windowShadeDark + border.width: dragEnabled ? 1 : 0 + border.color: qgcPal.text + + Drag.active: mouseArea.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + Drag.keys: [ "unassigned"] + + states: [ + State { + when: dropArea.containsDrag + PropertyChanges { + target: outerRect + color: "red" + } + } + ] + + Loader { + width: parent.width + height: parent.height + visible: dragEnabled + sourceComponent: dragHandle + } + + QGCLabel { + width: parent.width + height: parent.height + text: tileLabel + enabled: dragEnabled + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + MouseArea { + id: mouseArea + width: parent.width + height: parent.height + drag.target: dragEnabled ? parent : null + + onReleased: { + // Move tile back to original position + parent.x = 0; parent.y = 0; + + // If dropped over a channel target remap switch + if (parent.Drag.target && parent.Drag.target.dropAllowed) { + fact.value = parent.Drag.target.channel + } + } + } + + DropArea { + // This will cause to tile to go back to unassigned if dropped here + readonly property int channel: 0 + property bool dropAllowed: true + + id: dropArea + width: parent.width + height: parent.height + + keys: [ "assigned" ] + } + } + } + + // This component is used to create draggable tiles for currently assigned mode switches. The following + // properties must be set in the Loader: + // tileLabel - label for tile + // tileVisible - visibility for tile + // tileDragEnabled - true: this tile can be dragged + // tileParam - parameter to load from + Component { + id: assignedModeTileComponent + + Rectangle { + Fact { id: fact; name: tileDragEnabled ? tileParam : "" } + + width: tileWidth + height: tileHeight + color: qgcPal.windowShadeDark + border.width: tileDragEnabled ? 1 : 0 + border.color: qgcPal.text + visible: tileVisible + + Drag.active: mouseArea.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + Drag.keys: [ "assigned" ] + + Loader { + width: parent.width + height: parent.height + visible: tileDragEnabled + sourceComponent: dragHandle + } + + QGCLabel { + width: parent.width + height: parent.height + enabled: tileDragEnabled + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: tileLabel + } + + MouseArea { + id: mouseArea + width: parent.width + height: parent.height + drag.target: tileDragEnabled ? parent : null + + onReleased: { + // Move tile back to original position + parent.x = 0; parent.y = 0; + + // If dropped over a channel target remap switch + if (parent.Drag.target && parent.Drag.target.dropAllowed) { + fact.value = parent.Drag.target.channel + } + } + } + } + } + + onModeChannelChanged: if (!inRedistribution) redistributeThresholds() + onReturnChannelChanged: if (!inRedistribution) redistributeThresholds() + onLoiterChannelChanged: if (!inRedistribution) redistributeThresholds() + onPosCtlChannelChanged: if (!inRedistribution) redistributeThresholds() + onRcThUserValue: if (!inRedistribution) redistributeThresholds() + + function redistributeThresholds() { + if (loading || rcThUserValue != 0) { + // User is specifying thresholds, do not auto-calculate + return + } + + if (modeChannel != 0) { + var positions = 3 // Manual/Assist/Auto always exist + + var returnOnModeSwitch = modeChannel == returnChannel + var loiterOnModeSwitch = modeChannel == loiterChannel + var posCtlOnModeSwitch = modeChannel == posCtlChannel + + positions += returnOnModeSwitch ? 1 : 0 + positions += loiterOnModeSwitch ? 1 : 0 + positions += posCtlOnModeSwitch ? 1 : 0 + + var increment = 1.0 / positions + var currentThreshold = 0.0 + + // Make sure we don't re-enter + inRedistribution = true + + currentThreshold += increment + rc_assist_th.value = currentThreshold + if (posCtlOnModeSwitch) { + currentThreshold += increment + rc_posctl_th.value = currentThreshold + } + currentThreshold += increment + rc_auto_th.value = currentThreshold + if (loiterOnModeSwitch) { + currentThreshold += increment + rc_loiter_th.value = currentThreshold + } + if (returnOnModeSwitch) { + currentThreshold += increment + rc_return_th.value = currentThreshold + } + + inRedistribution = false + } + + if (returnChannel != 0 && returnChannel != modeChannel) { + var positions = 2 // On/off always exist + + var loiterOnReturnSwitch = returnChannel == loiterChannel + + positions += loiterOnReturnSwitch ? 1 : 0 + + var increment = 1.0 / positions + var currentThreshold = 0.0 + + if (positions == 2) { + // If only two positions don't set threshold at midrange. Setting to 0.25 + // allows for this channel to work with either two or three position switch + increment = 0.25 + } + + // Make sure we don't re-enter + inRedistribution = true + + if (loiterOnReturnSwitch) { + currentThreshold += increment + rc_loiter_th.value = currentThreshold + } + currentThreshold += increment + rc_return_th.value = currentThreshold + + inRedistribution = false + } + + if (loiterChannel != 0 && loiterChannel != modeChannel && loiterChannel != returnChannel) { + // If only two positions don't set threshold at midrange. Setting to 0.25 + // allows for this channel to work with either two or three position switch + var increment = 0.25 + + var currentThreshold = 0.0 + + // Make sure we don't re-enter + inRedistribution = true + + currentThreshold += increment + rc_loiter_th.value = currentThreshold + + inRedistribution = false + } + + if (posCtlChannel != 0 & posCtlChannel != modeChannel) { + // If only two positions don't set threshold at midrange. Setting to 0.25 + // allows for this channel to work with either two or three position switch + var increment = 0.25 + + var currentThreshold = 0.0 + + // Make sure we don't re-enter + inRedistribution = true + + currentThreshold += increment + rc_posctl_th.value = currentThreshold + + inRedistribution = false + } + + } + + Column { + anchors.fill: parent + + QGCLabel { + text: "FLIGHT MODES CONFIG" + font.pointSize: 20 + } + + Item { height: 20; width: 10 } // spacer + + QGCLabel { + width: parent.width + text: "Flight Mode switches can be assigned to any channel which is not currently being used for attitude control. All channels are displayed below. " + + "You can drag Flight Modes from the Flight Modes section below to a channel and drop it there. You can also drag switches assigned to a channel " + + "to another channel or back to the Unassigned Switches section. The Switch Display section at the very bottom will show you the results of your Flight Mode setup." + wrapMode: Text.WordWrap + } + + Item { height: 20; width: 10 } // spacer + + QGCLabel { + text: "Channel Assignments" + } + Flow { + width: parent.width + spacing: 5 + + Repeater { + model: channelCount + + Rectangle { + property int channel: modelData + 1 + property bool throttleMapped: channel == throttleChannel + property bool yawMapped: channel == yawChannel + property bool pitchMapped: channel == pitchChannel + property bool rollMapped: channel == rollChannel + property bool flapsMapped: channel == flapsChannel + property bool aux1Mapped: channel == aux1Channel + property bool aux2Mapped: channel == aux2Channel + + property bool modeMapped: channel == modeChannel + property bool posCtlMapped: channel == posCtlChannel + property bool returnMapped: channel == returnChannel + property bool loiterMapped: channel == loiterChannel + + property bool nonFlightModeMapping: throttleMapped | yawMapped | pitchMapped | rollMapped | flapsMapped | aux1Mapped | aux2Mapped + property bool unassignedMapping: !(nonFlightModeMapping | modeMapped | posCtlMapped | returnMapped | loiterMapped) + + id: channelTarget + width: tileWidth + height: channelCol.implicitHeight + + color: qgcPal.windowShadeDark + + states: [ + State { + when: dropArea.containsDrag && dropArea.dropAllowed + PropertyChanges { + target: channelHeader + color: "red" + } + } + ] + + Column { + id: channelCol + spacing: 3 + + Rectangle { + id: channelHeader + width: tileWidth + height: tileHeight + color: qgcPal.windowShade + + QGCLabel { + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: "Channel " + (modelData + 1) + (nonFlightModeMapping ? ": Unavailable" : "") + } + } + Loader { + property string tileLabel: "Available" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: unassignedMapping + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Throttle" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: throttleMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Yaw" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: yawMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Pitch" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: pitchMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Roll" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: rollMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Flaps Switch" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: flapsMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Aux1 Switch" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: aux1Mapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Aux2 Switch" + property bool tileVisible: visible + property bool tileDragEnabled: false + + visible: aux2Mapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Main Mode" + property bool tileVisible: visible + property bool tileDragEnabled: true + property string tileParam: "RC_MAP_MODE_SW" + + visible: modeMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Return" + property bool tileVisible: visible + property bool tileDragEnabled: true + property string tileParam: "RC_MAP_RETURN_SW" + + visible: returnMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "Loiter" + property bool tileVisible: visible + property bool tileDragEnabled: true + property string tileParam: "RC_MAP_LOITER_SW" + + visible: loiterMapped + sourceComponent: assignedModeTileComponent + } + Loader { + property string tileLabel: "PosCtl" + property bool tileVisible: visible + property bool tileDragEnabled: true + property string tileParam: "RC_MAP_POSCTL_SW" + + visible: posCtlMapped + sourceComponent: assignedModeTileComponent + } + } + + DropArea { + // Drops are not allowed on channels which are mapped to non-flight mode switches + property bool dropAllowed: !nonFlightModeMapping + property int channel: parent.channel + + id: dropArea + width: parent.width + height: parent.height + + keys: [ "unassigned", "assigned" ] + } + } + } + } + + Item { height: 20; width: 10 } // spacer + + Row { + spacing: 5 + + QGCLabel { + text: "Flight Modes" + } + QGCLabel { + text: "(Mode Switch must be assigned to channel before flight is allowed)" + visible: rc_map_mode_sw.value == 0 + } + } + Flow { + width: parent.width + spacing: 5 + + Loader { + property string tileLabel: "Main Mode" + property string tileParam: "RC_MAP_MODE_SW" + sourceComponent: unassignedModeTileComponent + } + Loader { + property string tileLabel: "Return" + property string tileParam: "RC_MAP_RETURN_SW" + sourceComponent: unassignedModeTileComponent + } + Loader { + property string tileLabel: "Loiter" + property string tileParam: "RC_MAP_LOITER_SW" + sourceComponent: unassignedModeTileComponent + } + Loader { + property string tileLabel: "PosCtl" + property string tileParam: "RC_MAP_POSCTL_SW" + sourceComponent: unassignedModeTileComponent + } + } + + Item { height: 20; width: 10 } // spacer + + FactCheckBox { + checkedValue: 0 + uncheckedValue: 1 + fact: rc_th_user + text: "Allow setup to generate the thresholds for the flight mode positions within a switch (recommended)" + } + + Item { height: 20; width: 10 } // spacer + + Row { + spacing: 20 + + QGCLabel { + text: "Switch Display" + } + QGCCheckBox { + checked: controller.sendLiveRCSwitchRanges + text: "Show live RC display" + onClicked: { + controller.sendLiveRCSwitchRanges = checked + } + } + } + + Item { height: 20; width: 10 } // spacer + + Row { + property bool modeSwitchVisible: modeChannel != 0 + property bool returnSwitchVisible: returnChannel != 0 && returnChannel != modeChannel + property bool loiterSwitchVisible: loiterChannel != 0 && loiterChannel != modeChannel && loiterChannel != returnChannel + property bool posCtlSwitchVisible: posCtlChannel != 0 && posCtlChannel != modeChannel + + width: parent.width + spacing: 20 + + Column { + visible: parent.modeSwitchVisible + + QGCLabel { text: "Mode Switch" } + + Row { + Item { + height: progressBarHeight + width: 150 + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_auto_th.value)) - (implicitHeight / 2) + visible: modeChannel != returnChannel && modeChannel != loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_return_th.value)) - (implicitHeight / 2) + visible: modeChannel == returnChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Return" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_loiter_th.value)) - (implicitHeight / 2) + visible: modeChannel == loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Loiter" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_auto_th.value)) - (implicitHeight / 2) + visible: modeChannel == loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Mission" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_auto_th.value)) - (implicitHeight / 2) + visible: modeChannel == returnChannel && modeChannel != loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Loiter/Mission" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_assist_th.value)) - (implicitHeight / 2) + visible: modeChannel != posCtlChannel + horizontalAlignment: Text.AlignRight + text: "Assist" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_posctl_th.value)) - (implicitHeight / 2) + visible: modeChannel == posCtlChannel + horizontalAlignment: Text.AlignRight + text: "Assist: PosCtl" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_assist_th.value)) - (implicitHeight / 2) + visible: modeChannel == posCtlChannel + horizontalAlignment: Text.AlignRight + text: "Assist: AltCtl" + } + + QGCLabel { + width: parent.width + y: parent.height - (implicitHeight / 2) + text: "Manual" + horizontalAlignment: Text.AlignRight + } + } + + ProgressBar { + height: progressBarHeight + orientation: Qt.Vertical + value: controller.modeSwitchLiveRange + } + } + } + + Column { + visible: parent.returnSwitchVisible + + QGCLabel { text: "Return Switch" } + + Row { + Item { + height: progressBarHeight + width: 150 + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_return_th.value)) - (implicitHeight / 2) + horizontalAlignment: Text.AlignRight + text: "Auto: Return" + } + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_loiter_th.value)) - (implicitHeight / 2) + visible: returnChannel == loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Loiter" + } + + QGCLabel { + width: parent.width + y: parent.height - (implicitHeight / 2) + visible: returnChannel == loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Mission" + } + + QGCLabel { + width: parent.width + y: parent.height - (implicitHeight / 2) + visible: returnChannel != loiterChannel + horizontalAlignment: Text.AlignRight + text: "Auto: Return Off" + } + } + + ProgressBar { + height: progressBarHeight + orientation: Qt.Vertical + value: controller.returnSwitchLiveRange + } + } + } + + Column { + visible: parent.loiterSwitchVisible + + QGCLabel { text: "Loiter Switch" } + + Row { + Item { + height: progressBarHeight + width: 150 + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_loiter_th.value)) - (implicitHeight / 2) + horizontalAlignment: Text.AlignRight + text: "Auto: Loiter" + } + + QGCLabel { + width: parent.width + y: parent.height - (implicitHeight / 2) + horizontalAlignment: Text.AlignRight + text: "Auto: Mission" + } + } + + ProgressBar { + height: progressBarHeight + orientation: Qt.Vertical + value: controller.loiterSwitchLiveRange + } + } + } + + Column { + visible: parent.posCtlSwitchVisible + + QGCLabel { text: "PosCtl Switch" } + + Row { + Item { + height: progressBarHeight + width: 150 + + QGCLabel { + width: parent.width + y: (parent.height * (1.0 - rc_posctl_th.value)) - (implicitHeight / 2) + horizontalAlignment: Text.AlignRight + text: "Assist: PosCtl" + } + + QGCLabel { + width: parent.width + y: parent.height - (implicitHeight / 2) + horizontalAlignment: Text.AlignRight + text: "Assist: AltCtl" + } + } + + ProgressBar { + height: progressBarHeight + orientation: Qt.Vertical + value: controller.posCtlSwitchLiveRange + } + } + } + } + } + } + } + + Component { + id: invalidComponent + + Rectangle { + anchors.fill: parent + color: qgcPal.window + + Column { + width: parent.width + spacing: 20 + + QGCLabel { + text: "FLIGHT MODES CONFIG" + font.pointSize: 20 + } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + text: "There are errors in your current configuration which will need to be fixed before you can used Flight Config setup. " + + "You will need to change Parameters directly using Parameters Setup to remove these errors." + } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + text: controller.configurationErrors + } + } + } + } +} diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc b/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc new file mode 100644 index 0000000000..ef5c346f8f --- /dev/null +++ b/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc @@ -0,0 +1,196 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009, 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @author Don Gagne + +#include "FlightModesComponentController.h" +#include "QGCMAVLink.h" +#include "UASManager.h" +#include "AutoPilotPluginManager.h" + +#include +#include + +FlightModesComponentController::FlightModesComponentController(QObject* parent) : + QObject(parent), + _liveRCValues(false), + _validConfiguration(false), + _channelCount(18), + _autoPilotPlugin(NULL) +{ + _uas = UASManager::instance()->getActiveUAS(); + Q_ASSERT(_uas); + + _autoPilotPlugin = AutoPilotPluginManager::instance()->getInstanceForAutoPilotPlugin(_uas); + Q_ASSERT(_autoPilotPlugin); + Q_ASSERT(_autoPilotPlugin->pluginIsReady()); + + _initRcValues(); + _validateConfiguration(); +} + +FlightModesComponentController::~FlightModesComponentController() +{ + setSendLiveRCSwitchRanges(false); +} + + +void FlightModesComponentController::_initRcValues(void) +{ + for (int i=0; i<_chanMax; i++) { + _rcValues << 1.0; + } +} + +/// This will look for parameter settings which would cause the config to not run correctly. +/// It will set _validConfiguration and _configurationErrors as needed. +void FlightModesComponentController::_validateConfiguration(void) +{ + _validConfiguration = true; + + _channelCount = _autoPilotPlugin->factExists("RC_CHAN_CNT") ? _autoPilotPlugin->getFact("RC_CHAN_CNT")->value().toInt() : 18; + + // Make sure switches are valid and within channel range + + QStringList switchParams, switchNames; + QList switchMappings; + + switchParams << "RC_MAP_MODE_SW" << "RC_MAP_RETURN_SW" << "RC_MAP_LOITER_SW" << "RC_MAP_POSCTL_SW"; + switchNames << "Mode Switch" << "Return Switch" << "Loiter Switch" << "PosCtl Switch"; + + for(int i=0; igetFact(switchParams[i])->value().toInt(); + switchMappings << map; + + if (map < 0 || map > _channelCount) { + _validConfiguration = false; + _configurationErrors += QString("%1 is set to %2. Mapping must between 0 and %3 (inclusive).\n").arg(switchNames[i]).arg(map).arg(_channelCount); + } + } + + // Make sure switches are not mapped to attitude control channels + + QStringList attitudeParams, attitudeNames; + + attitudeParams << "RC_MAP_THROTTLE" << "RC_MAP_YAW" << "RC_MAP_PITCH" << "RC_MAP_ROLL" << "RC_MAP_FLAPS" << "RC_MAP_AUX1" << "RC_MAP_AUX2"; + attitudeNames << "Throttle" << "Yaw" << "Pitch" << "Roll" << "Flaps" << "Aux1" << "Aux2"; + + for (int i=0; igetFact(attitudeParams[i])->value().toInt(); + + for (int j=0; jgetFact(rcMinParam)->value().toInt(); + _rgRCMax[i] = _autoPilotPlugin->getFact(rcMaxParam)->value().toInt(); + + float floatReversed = _autoPilotPlugin->getFact(rcRevParam)->value().toFloat(); + _rgRCReversed[i] = floatReversed == -1.0f; + } + + _uas->startRadioControlCalibration(); + connect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged); + } else { + disconnect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged); + _uas->endRadioControlCalibration(); + _initRcValues(); + emit switchLiveRangeChanged(); + } +} + +/// @brief This routine is called whenever a raw value for an RC channel changes. +/// @param chan RC channel on which signal is coming from (0-based) +/// @param fval Current value for channel +void FlightModesComponentController::_remoteControlChannelRawChanged(int chan, float fval) +{ + Q_ASSERT(chan >= 0 && chan <= _chanMax); + + if (fval < _rgRCMin[chan]) { + fval= _rgRCMin[chan]; + } + if (fval > _rgRCMax[chan]) { + fval= _rgRCMax[chan]; + } + + float percentRange = (fval - _rgRCMin[chan]) / (float)(_rgRCMax[chan] - _rgRCMin[chan]); + if (_rgRCReversed[chan]) { + percentRange = 1.0 - percentRange; + } + + _rcValues[chan] = percentRange; + emit switchLiveRangeChanged(); +} + +double FlightModesComponentController::_switchLiveRange(const QString& param) +{ + QVariant value; + + int channel = _autoPilotPlugin->getFact(param)->value().toInt(); + if (channel == 0) { + return 1.0; + } else { + return _rcValues[channel - 1]; + } +} + +double FlightModesComponentController::modeSwitchLiveRange(void) +{ + return _switchLiveRange("RC_MAP_MODE_SW"); +} + +double FlightModesComponentController::returnSwitchLiveRange(void) +{ + return _switchLiveRange("RC_MAP_RETURN_SW"); +} + +double FlightModesComponentController::loiterSwitchLiveRange(void) +{ + return _switchLiveRange("RC_MAP_LOITER_SW"); +} + +double FlightModesComponentController::posCtlSwitchLiveRange(void) +{ + return _switchLiveRange("RC_MAP_POSCTL_SW"); +} + diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentController.h b/src/AutoPilotPlugins/PX4/FlightModesComponentController.h new file mode 100644 index 0000000000..ccc27715fa --- /dev/null +++ b/src/AutoPilotPlugins/PX4/FlightModesComponentController.h @@ -0,0 +1,95 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009, 2015 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @author Don Gagne + +#ifndef FLIGHTMODESCOMPONENTCONTROLLER_H +#define FLIGHTMODESCOMPONENTCONTROLLER_H + +#include +#include +#include + +#include "UASInterface.h" +#include "AutoPilotPlugin.h" + +/// MVC Controller for FlightModesComponent.qml. +class FlightModesComponentController : public QObject +{ + Q_OBJECT + +public: + FlightModesComponentController(QObject* parent = NULL); + ~FlightModesComponentController(); + + Q_PROPERTY(bool validConfiguration MEMBER _validConfiguration CONSTANT) + Q_PROPERTY(QString configurationErrors MEMBER _configurationErrors CONSTANT) + + Q_PROPERTY(int channelCount MEMBER _channelCount CONSTANT) + + Q_PROPERTY(double modeSwitchLiveRange READ modeSwitchLiveRange NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double returnSwitchLiveRange READ returnSwitchLiveRange NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double loiterSwitchLiveRange READ loiterSwitchLiveRange NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double posCtlSwitchLiveRange READ posCtlSwitchLiveRange NOTIFY switchLiveRangeChanged) + + Q_PROPERTY(bool sendLiveRCSwitchRanges READ sendLiveRCSwitchRanges WRITE setSendLiveRCSwitchRanges NOTIFY liveRCSwitchRangesChanged) + + double modeSwitchLiveRange(void); + double returnSwitchLiveRange(void); + double loiterSwitchLiveRange(void); + double posCtlSwitchLiveRange(void); + + bool sendLiveRCSwitchRanges(void) { return _liveRCValues; } + void setSendLiveRCSwitchRanges(bool start); + +signals: + void switchLiveRangeChanged(void); + void liveRCSwitchRangesChanged(void); + +private slots: + void _remoteControlChannelRawChanged(int chan, float fval); + +private: + double _switchLiveRange(const QString& param); + void _initRcValues(void); + void _validateConfiguration(void); + + static const int _chanMax = 18; + + UASInterface* _uas; + + QList _rcValues; + bool _liveRCValues; + int _rgRCMin[_chanMax]; + int _rgRCMax[_chanMax]; + bool _rgRCReversed[_chanMax]; + + bool _validConfiguration; + QString _configurationErrors; + int _channelCount; + + AutoPilotPlugin* _autoPilotPlugin; +}; + +#endif diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc index c5aded6dea..e9e553efe2 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc @@ -26,7 +26,7 @@ #include "UASManager.h" #include "QGCUASParamManagerInterface.h" #include "PX4ParameterFacts.h" -#include "SensorsComponentController.h" +#include "FlightModesComponentController.h" #include "QGCMessageBox.h" /// @file @@ -74,6 +74,8 @@ PX4AutoPilotPlugin::PX4AutoPilotPlugin(UASInterface* uas, QObject* parent) : { Q_ASSERT(uas); + qmlRegisterType("QGroundControl.Controllers", 1, 0, "FlightModesComponentController"); + _parameterFacts = new PX4ParameterFacts(uas, this); Q_CHECK_PTR(_parameterFacts); diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index 7d1f6943be..2398a98b5e 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -108,7 +108,7 @@ signals: /// Signalled when property has been changed by a call to the property write accessor /// /// This signal is meant for use by Fact container implementations. - void _containerValueChanged(QVariant& value); + void _containerValueChanged(const QVariant& value); private: QString _name; diff --git a/src/FactSystem/FactLoader.cc b/src/FactSystem/FactLoader.cc index 903fb44a90..394d7fb374 100644 --- a/src/FactSystem/FactLoader.cc +++ b/src/FactSystem/FactLoader.cc @@ -57,11 +57,7 @@ FactLoader::FactLoader(UASInterface* uas, QObject* parent) : FactLoader::~FactLoader() { - foreach(Fact* fact, _mapFact2ParameterName.keys()) { - delete fact; - } - _mapParameterName2Variant.clear(); - _mapFact2ParameterName.clear(); + } /// Called whenever a parameter is updated or first seen. @@ -141,7 +137,7 @@ void FactLoader::_parameterUpdate(int uas, int component, QString parameterName, /// Connected to Fact::valueUpdated /// /// Sets the new value into the Parameter Manager. Parameter is persisted after send. -void FactLoader::_valueUpdated(QVariant value) +void FactLoader::_valueUpdated(const QVariant& value) { Fact* fact = qobject_cast(sender()); Q_ASSERT(fact); diff --git a/src/FactSystem/FactLoader.h b/src/FactSystem/FactLoader.h index 98a64634d0..a61b147543 100644 --- a/src/FactSystem/FactLoader.h +++ b/src/FactSystem/FactLoader.h @@ -65,7 +65,7 @@ protected: private slots: void _parameterUpdate(int uas, int component, QString parameterName, int mavType, QVariant value); - void _valueUpdated(QVariant value); + void _valueUpdated(const QVariant& value); void _paramMgrParameterListUpToDate(void); private: diff --git a/src/qgcunittest/MockLink.param b/src/qgcunittest/MockLink.param index 349d7d42c4..6fc11ee57c 100644 --- a/src/qgcunittest/MockLink.param +++ b/src/qgcunittest/MockLink.param @@ -389,22 +389,22 @@ 1 50 RC1_MAX 2000 9 1 50 RC1_MIN 1000 9 1 50 RC1_REV 1 9 -1 50 RC1_TRIM 1500 9 +1 50 RC1_TRIM 1501 9 1 50 RC2_DZ 10 9 1 50 RC2_MAX 2000 9 1 50 RC2_MIN 1000 9 1 50 RC2_REV 1 9 -1 50 RC2_TRIM 1500 9 +1 50 RC2_TRIM 1501 9 1 50 RC3_DZ 10 9 1 50 RC3_MAX 2000 9 1 50 RC3_MIN 1000 9 1 50 RC3_REV 1 9 -1 50 RC3_TRIM 1500 9 +1 50 RC3_TRIM 1501 9 1 50 RC4_DZ 10 9 1 50 RC4_MAX 2000 9 1 50 RC4_MIN 1000 9 1 50 RC4_REV 1 9 -1 50 RC4_TRIM 1500 9 +1 50 RC4_TRIM 1501 9 1 50 RC5_DZ 10 9 1 50 RC5_MAX 2000 9 1 50 RC5_MIN 1000 9 -- GitLab