diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 90180708d5dadb91f3d3e64b75d09ab7ad0b5ad8..98f357275b12d54f673c1649e9d33e6c6e0a14cc 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -93,6 +93,7 @@ src/QmlControls/QGCViewMessage.qml src/QmlControls/ParameterEditor.qml src/QmlControls/ParameterEditorDialog.qml + src/QmlControls/ModeSwitchDisplay.qml src/ViewWidgets/ParameterEditorWidget.qml src/ViewWidgets/CustomCommandWidget.qml src/VehicleSetup/SetupView.qml diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.qml b/src/AutoPilotPlugins/PX4/FlightModesComponent.qml index 56f9b380297f2295bf5719e8133e99d690c0fba6..72828d202095ffb6b1007868d91613d4a7269c52 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponent.qml +++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.qml @@ -21,974 +21,377 @@ ======================================================================*/ -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 -import QGroundControl.ScreenTools 1.0 - -Item { - Loader { - id: loader - anchors.fill: parent - sourceComponent: controller.validConfiguration ? validComponent : invalidComponent - - property FlightModesComponentController controller: FlightModesComponentController { factPanel: loader.item } - property QGCPalette qgcPal: QGCPalette { colorGroupEnabled: true } - property bool loading: true - - onLoaded: loading = false +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.1 + +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 +import QGroundControl.ScreenTools 1.0 + +QGCView { + id: rootQGCView + viewPanel: panel + + readonly property int monitorThresholdCharWidth: 8 // Character width of Monitor and Threshold labels + + // User visible strings + + readonly property string topHelpText: "Assign Flight Modes to radio control channels and adjust the thresholds for triggering them. " + + "You can assign multiple flight modes to a single channel. " + + "Turn your radio control on to test switch settings." + + + readonly property string fwManualModeName: "Manual/Main" + readonly property string mrManualModeName: "Stabilized/Main" + readonly property string fwManualModeDescription: "The pilot has full control of the aircraft, no assistance is provided. " + + "The Main mode switch must always be assigned to a channel in order to fly" + readonly property string mrManualModeDescription: "Centering roll/pitch stick will return the multirotor to a level attitude, but it will continue drifting in the direction it was previously sent. " + + "Altitude is controlled fully by pilot using the Throttle stick. " + + "The Main mode switch must always be assigned to a channel in order to fly" + + readonly property string assistModeName: "Assist" + readonly property string assistModeDescription: "If Position Control is placed on a seperate channel from the Main mode channel, an additional 'Assist' mode is added to the Main switch. " + + "In order for the Attitude Control/Position Control switch to be active, the Main switch must be in Assist mode." + + readonly property string autoModeName: "Auto" + readonly property string autoModeDescription: "If Loiter is placed on a seperate channel from the Main mode channel, an additional 'Auto' mode is added to the Main switch. " + + "In order for the Mission/Loiter switch to be active, the Main switch must be in Auto mode." + + readonly property string fwAcroModeName: "Stabilized" + readonly property string mrAcroModeName: "Acro" + readonly property string fwAcroModeDescription: "Need Stablized description" + readonly property string mrAcroModeDescription: "Need Acro mode description" + + readonly property string fwAltCtlModeName: "Attitude Control" + readonly property string mrAltCtlModeName: "Altitude Control" + readonly property string fwAltCtlModeDescription: "Aileron and Elevator sticks affect roll and pitch according to the Attitude Control settings. " + + "Altitude is not maintained automatically. " + + "Rudder is controlled fully by the pilot." + readonly property string mrAltCtlModeDescription: "Same as Stablized mode except that Throttle controls climb/sink rate. Centered throttle holds altitude steady." + + readonly property string posCtlModeName: "Position Control" + readonly property string fwPosCtlModeDescription: "Throttle controls speed, pitch controls climb/sink rate, roll controls yaw rate. " + + "Roll and Pitch centered gives level, straight-line flight. " + + "Position Control and Attitude Control must always be on the same channel." + readonly property string mrPosCtlModeDescription: "Roll and Pitch control left-right and front-back speed over ground respectively. " + + "When roll and pitch are centered, the multirotor will hold position. " + + "Yaw controls yaw rate as in Stablized mode. " + + "Throttle controls climb/sink rate. Centered throttle holds altitude steady. " + + "Position Control and Attitude Control must always be on the same channel." + + readonly property string missionModeName: "Mission" + readonly property string missionModeDescription: "The aircraft obeys the programmed mission sent by QGroundControl. " + + "If no mission was sent, aircraft will loiter at current position instead." + + readonly property string loiterModeName: "Loiter" + readonly property string fwLoiterModeDescription: "The aircraft flies in a circle around the current position at the current altitude. " + + "Loiter and Mission must always be on the same channel." + readonly property string mrLoiterModeDescription: "The multirotor hovers in a fixed position at the current position and altitude. " + + "Loiter and Mission must always be on the same channel." + + readonly property string returnModeName: "Return" + readonly property string fwReturnModeDescription: "The aircraft returns to the home position and loiters above it." + + "The settings which control this sequence can be found under Setup - Safety." + readonly property string mrReturnModeDescription: "The multirotor returns to the home position, loiters and then lands. " + + "The settings which control this sequence can be found under Setup - Safety." + + readonly property string offboardModeName: "Offboard" + readonly property string offboardModeDescription: "Offboard description" + + readonly property real modeSpacing: ScreenTools.defaultFontPixelHeight / 3 + + QGCPalette { id: qgcPal; colorGroupEnabled: panel.enabled } + + FlightModesComponentController { + id: controller + factPanel: panel + + onModeRowsChanged: recalcModePositions() } - Component { - id: validComponent + Timer { + interval: 200 + running: true - FactPanel { - property Fact rc_map_throttle: controller.getParameterFact(-1, "RC_MAP_THROTTLE") - property Fact rc_map_yaw: controller.getParameterFact(-1, "RC_MAP_YAW") - property Fact rc_map_pitch: controller.getParameterFact(-1, "RC_MAP_PITCH") - property Fact rc_map_roll: controller.getParameterFact(-1, "RC_MAP_ROLL") - property Fact rc_map_flaps: controller.getParameterFact(-1, "RC_MAP_FLAPS") - property Fact rc_map_aux1: controller.getParameterFact(-1, "RC_MAP_AUX1") - property Fact rc_map_aux2: controller.getParameterFact(-1, "RC_MAP_AUX2") - - property Fact rc_map_mode_sw: controller.getParameterFact(-1, "RC_MAP_MODE_SW") - property Fact rc_map_posctl_sw: controller.getParameterFact(-1, "RC_MAP_POSCTL_SW") - property Fact rc_map_return_sw: controller.getParameterFact(-1, "RC_MAP_RETURN_SW") - property Fact rc_map_offboard_sw: controller.getParameterFact(-1, "RC_MAP_OFFB_SW") - property Fact rc_map_loiter_sw: controller.getParameterFact(-1, "RC_MAP_LOITER_SW") - property Fact rc_map_acro_sw: controller.getParameterFact(-1, "RC_MAP_ACRO_SW") - - property Fact rc_assist_th: controller.getParameterFact(-1, "RC_ASSIST_TH") - property Fact rc_posctl_th: controller.getParameterFact(-1, "RC_POSCTL_TH") - property Fact rc_auto_th: controller.getParameterFact(-1, "RC_AUTO_TH") - property Fact rc_loiter_th: controller.getParameterFact(-1, "RC_LOITER_TH") - property Fact rc_return_th: controller.getParameterFact(-1, "RC_RETURN_TH") - property Fact rc_offboard_th: controller.getParameterFact(-1, "RC_OFFB_TH") - property Fact rc_acro_th: controller.getParameterFact(-1, "RC_ACRO_TH") - - property Fact rc_th_user: controller.getParameterFact(-1, "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 offboardChannel: rc_map_offboard_sw.value - property int loiterChannel: rc_map_loiter_sw.value - property int acroChannel: rc_map_acro_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 + onTriggered: recalcModePositions() + } - Component { - id: dragHandle + function recalcModePositions() { + var spacing = ScreenTools.defaultFontPixelHeight / 2 + var nextY = manualMode.y + manualMode.height + spacing - 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 - } - } - } + for (var index = 0; index < 9; index++) { + if (controller.assistModeRow == index) { + if (controller.assistModeVisible) { + assistMode.y = nextY + assistMode.z = 9 - index + nextY += assistMode.height + spacing } - } - - // 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 { - property Fact fact: controller.getParameterFact(-1, 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) { - if (!singleSwitchRequired || parent.Drag.target.unassignedChannel) { - 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 - property bool unassignedChannel: 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: nullFact } - property Fact fact: tileDragEnabled ? controller.getParameterFact(-1, tileParam) : nullFact - - 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) { - if (!singleSwitchRequired || parent.Drag.target.unassignedChannel) { - fact.value = parent.Drag.target.channel - } - } - } - } + } else if (controller.autoModeRow == index) { + if (controller.autoModeVisible) { + autoMode.y = nextY + autoMode.z = 9 - index + nextY += autoMode.height + spacing } + } else if (controller.acroModeRow == index) { + acroMode.y = nextY + acroMode.z = 9 - index + nextY += acroMode.height + spacing + } else if (controller.altCtlModeRow == index) { + altCtlMode.y = nextY + altCtlMode.z = 9 - index + nextY += altCtlMode.height + spacing + } else if (controller.posCtlModeRow == index) { + posCtlMode.y = nextY + posCtlMode.z = 9 - index + nextY += posCtlMode.height + spacing + } else if (controller.loiterModeRow == index) { + loiterMode.y = nextY + loiterMode.z = 9 - index + nextY += loiterMode.height + spacing + } else if (controller.missionModeRow == index) { + missionMode.y = nextY + missionMode.z = 9 - index + nextY += missionMode.height + spacing + } else if (controller.returnModeRow == index) { + returnMode.y = nextY + returnMode.z = 9 - index + nextY += returnMode.height + spacing + } else if (controller.offboardModeRow == index) { + offboardMode.y = nextY + offboardMode.z = 9 - index + nextY += offboardMode.height + spacing } + } - onModeChannelChanged: if (!inRedistribution) redistributeThresholds() - onReturnChannelChanged: if (!inRedistribution) redistributeThresholds() - onOffboardChannelChanged: if (!inRedistribution) redistributeThresholds() - onLoiterChannelChanged: if (!inRedistribution) redistributeThresholds() - onPosCtlChannelChanged: if (!inRedistribution) redistributeThresholds() - onAcroChannelChanged: 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 loiterOnModeSwitch = modeChannel == loiterChannel - var posCtlOnModeSwitch = modeChannel == posCtlChannel - var acroOnModeSwitch = modeChannel == acroChannel - - positions += loiterOnModeSwitch ? 1 : 0 - positions += posCtlOnModeSwitch ? 1 : 0 - positions += acroOnModeSwitch ? 1 : 0 - - var increment = 1.0 / positions - var currentThreshold = 0.0 - - // Make sure we don't re-enter - inRedistribution = true + scrollItem.height = nextY + } - if (acroOnModeSwitch) { - currentThreshold += increment - rc_acro_th.value = currentThreshold - } + QGCViewPanel { + id: panel + anchors.fill: parent - currentThreshold += increment - rc_assist_th.value = currentThreshold - if (posCtlOnModeSwitch) { - currentThreshold += increment - rc_posctl_th.value = currentThreshold - } + ScrollView { + id: scroll + anchors.fill: parent + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff - currentThreshold += increment - rc_auto_th.value = currentThreshold - if (loiterOnModeSwitch) { - currentThreshold += increment - rc_loiter_th.value = currentThreshold - } + Item { + id: scrollItem + width: scroll.viewport.width - inRedistribution = false + QGCLabel { + id: header + width: parent.width + font.pixelSize: ScreenTools.largeFontPixelSize + text: "FLIGHT MODES CONFIG" } - if (returnChannel != 0) { - inRedistribution = true - - // 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 - rc_return_th.value = 0.25 - - inRedistribution = false + Item { + id: headingSpacer + anchors.top: header.bottom + height: 20 + width: 20 } - if (offboardChannel != 0) { - inRedistribution = true - - // 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 - rc_offboard_th.value = 0.25 - - inRedistribution = false + QGCLabel { + anchors.top: headingSpacer.bottom + anchors.left: parent.left + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + anchors.right: applyButton.left + text: topHelpText + wrapMode: Text.WordWrap } - if (loiterChannel != 0 && loiterChannel != modeChannel) { - inRedistribution = true + QGCButton { + id: applyButton + anchors.top: headingSpacer.bottom + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + anchors.right: parent.right + text: "Generate Thresholds" - // 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 - rc_loiter_th.value = 0.25 - - inRedistribution = false + onClicked: controller.generateThresholds() } - if (posCtlChannel != 0 & posCtlChannel != modeChannel) { - inRedistribution = true - - // 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 - rc_posctl_th.value = 0.25 - - inRedistribution = false + Item { + id: lastSpacer + anchors.top: applyButton.bottom + height: 20 + width: 10 } - if (acroChannel != 0 & acroChannel != modeChannel) { - inRedistribution = true - - // 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 - rc_acro_th.value = 0.25 - - inRedistribution = false + ModeSwitchDisplay { + id: manualMode + anchors.top: lastSpacer.bottom + flightModeName: controller.fixedWing ? fwManualModeName : mrManualModeName + flightModeDescription: controller.fixedWing ? fwManualModeDescription : mrManualModeDescription + rcValue: controller.manualModeRcValue + modeChannelIndex: controller.manualModeChannelIndex + modeChannelEnabled: true + modeSelected: controller.manualModeSelected + thresholdValue: controller.manualModeThreshold + thresholdDragEnabled: false + + onModeChannelIndexChanged: controller.manualModeChannelIndex = modeChannelIndex } - } - Column { - anchors.fill: parent - - QGCLabel { - text: "FLIGHT MODES CONFIG" - font.pixelSize: ScreenTools.largeFontPixelSize + ModeSwitchDisplay { + id: assistMode + visible: controller.assistModeVisible + flightModeName: assistModeName + flightModeDescription: assistModeDescription + rcValue: controller.assistModeRcValue + modeChannelIndex: controller.assistModeChannelIndex + modeChannelEnabled: false + modeSelected: controller.assistModeSelected + thresholdValue: controller.assistModeThreshold + thresholdDragEnabled: true + + onThresholdValueChanged: controller.assistModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - Item { height: 20; width: 10 } // spacer - - QGCLabel { - width: parent.width - text: "The Main Mode, Loiter, PostCtl and Acro switches can be assigned to any channel which is not currently being used for attitude control. The Return and Offboard switches must be assigned to their seperate channel. " + - "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 + ModeSwitchDisplay { + id: autoMode + visible: controller.autoModeVisible + flightModeName: autoModeName + flightModeDescription: autoModeDescription + rcValue: controller.autoModeRcValue + modeChannelIndex: controller.autoModeChannelIndex + modeChannelEnabled: false + modeSelected: controller.autoModeSelected + thresholdValue: controller.autoModeThreshold + thresholdDragEnabled: true + + onThresholdValueChanged: controller.autoModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - 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 offboardMapped: channel == offboardChannel - property bool loiterMapped: channel == loiterChannel - property bool acroMapped: channel == acroChannel - - property bool nonFlightModeMapping: throttleMapped | yawMapped | pitchMapped | rollMapped | flapsMapped | aux1Mapped | aux2Mapped - property bool unassignedMapping: !(nonFlightModeMapping | modeMapped | posCtlMapped | returnMapped | offboardMapped | loiterMapped | acroMapped) - property bool singleSwitchMapping: returnMapped | offboardMapped - - id: channelTarget - width: tileWidth - height: channelCol.implicitHeight - - color: qgcPal.windowShadeDark - - states: [ - State { - when: dropArea.containsDrag && dropArea.dropAllowed && (!dropArea.drag.source.singleSwitchRequired || dropArea.unassignedChannel) - 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" - property bool singleSwitchRequired: false - - visible: modeMapped - sourceComponent: assignedModeTileComponent - } - Loader { - property string tileLabel: "Return" - property bool tileVisible: visible - property bool tileDragEnabled: true - property string tileParam: "RC_MAP_RETURN_SW" - property bool singleSwitchRequired: true - - visible: returnMapped - sourceComponent: assignedModeTileComponent - } - Loader { - property string tileLabel: "Offboard" - property bool tileVisible: visible - property bool tileDragEnabled: true - property string tileParam: "RC_MAP_OFFB_SW" - property bool singleSwitchRequired: true - - visible: offboardMapped - sourceComponent: assignedModeTileComponent - } - Loader { - property string tileLabel: "Loiter" - property bool tileVisible: visible - property bool tileDragEnabled: true - property string tileParam: "RC_MAP_LOITER_SW" - property bool singleSwitchRequired: false - - visible: loiterMapped - sourceComponent: assignedModeTileComponent - } - Loader { - property string tileLabel: "PosCtl" - property bool tileVisible: visible - property bool tileDragEnabled: true - property string tileParam: "RC_MAP_POSCTL_SW" - property bool singleSwitchRequired: false - - visible: posCtlMapped - sourceComponent: assignedModeTileComponent - } - Loader { - property string tileLabel: "Acro/Stabilize" - property bool tileVisible: visible - property bool tileDragEnabled: true - property string tileParam: "RC_MAP_ACRO_SW" - property bool singleSwitchRequired: false - - visible: acroMapped - sourceComponent: assignedModeTileComponent - } - } - - DropArea { - // Drops are not allowed on channels which are mapped to non-flight mode switches - property bool dropAllowed: !nonFlightModeMapping && !singleSwitchMapping - property bool unassignedChannel: unassignedMapping - property int channel: parent.channel - - id: dropArea - width: parent.width - height: parent.height - - keys: [ "unassigned", "assigned" ] - } - } - } + ModeSwitchDisplay { + id: acroMode + flightModeName: controller.fixedWing ? fwAcroModeName : mrAcroModeName + flightModeDescription: controller.fixedWing ? fwAcroModeDescription : mrAcroModeDescription + rcValue: controller.acroModeRcValue + modeChannelIndex: controller.acroModeChannelIndex + modeChannelEnabled: true + modeSelected: controller.acroModeSelected + thresholdValue: controller.acroModeThreshold + thresholdDragEnabled: true + + onModeChannelIndexChanged: controller.acroModeChannelIndex = modeChannelIndex + onThresholdValueChanged: controller.acroModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - Item { height: 20; width: 10 } // spacer + ModeSwitchDisplay { + id: altCtlMode + flightModeName: controller.fixedWing ? fwAltCtlModeName : mrAltCtlModeName + flightModeDescription: controller.fixedWing ? fwAltCtlModeDescription : mrAltCtlModeDescription + rcValue: controller.altCtlModeRcValue + modeChannelIndex: controller.altCtlModeChannelIndex + modeChannelEnabled: false + modeSelected: controller.altCtlModeSelected + thresholdValue: controller.altCtlModeThreshold + thresholdDragEnabled: !controller.assistModeVisible - Row { - spacing: 5 + onThresholdValueChanged: controller.altCtlModeThreshold = thresholdValue - QGCLabel { - text: "Flight Modes" - } - QGCLabel { - text: "(Mode Switch must be assigned to channel before flight is allowed)" - visible: rc_map_mode_sw.value == 0 - } + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - Flow { - width: parent.width - spacing: 5 - - Loader { - property string tileLabel: "Main Mode" - property string tileParam: "RC_MAP_MODE_SW" - property bool singleSwitchRequired: false - sourceComponent: unassignedModeTileComponent - } - Loader { - property string tileLabel: "Loiter" - property string tileParam: "RC_MAP_LOITER_SW" - property bool singleSwitchRequired: false - sourceComponent: unassignedModeTileComponent - } - Loader { - property string tileLabel: "PosCtl" - property string tileParam: "RC_MAP_POSCTL_SW" - property bool singleSwitchRequired: false - sourceComponent: unassignedModeTileComponent - } - Loader { - property string tileLabel: "Acro/Stabilize" - property string tileParam: "RC_MAP_ACRO_SW" - property bool singleSwitchRequired: false - sourceComponent: unassignedModeTileComponent - } - Loader { - property string tileLabel: "Return" - property string tileParam: "RC_MAP_RETURN_SW" - property bool singleSwitchRequired: true - sourceComponent: unassignedModeTileComponent - } - Loader { - property string tileLabel: "Offboard" - property string tileParam: "RC_MAP_OFFB_SW" - property bool singleSwitchRequired: true - 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)" + ModeSwitchDisplay { + id: posCtlMode + flightModeName: posCtlModeName + flightModeDescription: controller.fixedWing ? fwPosCtlModeDescription : mrPosCtlModeDescription + rcValue: controller.posCtlModeRcValue + modeChannelIndex: controller.posCtlModeChannelIndex + modeChannelEnabled: true + modeSelected: controller.posCtlModeSelected + thresholdValue: controller.posCtlModeThreshold + thresholdDragEnabled: true + + onModeChannelIndexChanged: controller.posCtlModeChannelIndex = modeChannelIndex + onThresholdValueChanged: controller.posCtlModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - Item { height: 20; width: 10 } // spacer + ModeSwitchDisplay { + id: missionMode + flightModeName: missionModeName + flightModeDescription: missionModeDescription + rcValue: controller.missionModeRcValue + modeChannelIndex: controller.missionModeChannelIndex + modeChannelEnabled: false + modeSelected: controller.missionModeSelected + thresholdValue: controller.missionModeThreshold + thresholdDragEnabled: !controller.autoModeVisible - Row { - spacing: 20 + onThresholdValueChanged: controller.missionModeThreshold = thresholdValue - QGCLabel { - text: "Switch Display" - } - QGCCheckBox { - checked: controller.sendLiveRCSwitchRanges - text: "Show live RC display" - - onClicked: { - controller.sendLiveRCSwitchRanges = checked - } - } + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - Item { height: 20; width: 10 } // spacer - - Row { - property bool modeSwitchVisible: modeChannel != 0 - property bool loiterSwitchVisible: loiterChannel != 0 && loiterChannel != modeChannel && loiterChannel != returnChannel - property bool posCtlSwitchVisible: posCtlChannel != 0 && posCtlChannel != modeChannel - property bool acroSwitchVisible: acroChannel != 0 && acroChannel != modeChannel - property bool returnSwitchVisible: returnChannel != 0 - property bool offboardSwitchVisible: offboardChannel != 0 - - 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_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 * (1.0 - rc_acro_th.value)) - (implicitHeight / 2) - visible: modeChannel == acroChannel - horizontalAlignment: Text.AlignRight - text: "Acro/Stabilize" - } - - 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.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 - } - } - } - - Column { - visible: parent.acroSwitchVisible - - QGCLabel { text: "Acro/Stabilize Switch" } - - Row { - Item { - height: progressBarHeight - width: 150 - - QGCLabel { - width: parent.width - y: (parent.height * (1.0 - rc_acro_th.value)) - (implicitHeight / 2) - horizontalAlignment: Text.AlignRight - text: "Acro/Stabilize" - } - - QGCLabel { - width: parent.width - y: parent.height - (implicitHeight / 2) - horizontalAlignment: Text.AlignRight - text: "Manual" - } - } - - ProgressBar { - height: progressBarHeight - orientation: Qt.Vertical - value: controller.acroSwitchLiveRange - } - } - } - - 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: "Return" - } - - QGCLabel { - width: parent.width - y: parent.height - (implicitHeight / 2) - horizontalAlignment: Text.AlignRight - text: "Return Off" - } - } - - ProgressBar { - height: progressBarHeight - orientation: Qt.Vertical - value: controller.returnSwitchLiveRange - } - } - } - - Column { - visible: parent.offboardSwitchVisible - - QGCLabel { text: "Offboard 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: "Offboard" - } - - QGCLabel { - width: parent.width - y: parent.height - (implicitHeight / 2) - visible: returnChannel != loiterChannel - horizontalAlignment: Text.AlignRight - text: "Offboard Off" - } - } - - ProgressBar { - height: progressBarHeight - orientation: Qt.Vertical - value: controller.offboardSwitchLiveRange - } - } - } + ModeSwitchDisplay { + id: loiterMode + flightModeName: loiterModeName + flightModeDescription: controller.fixedWing ? fwLoiterModeDescription : mrLoiterModeDescription + rcValue: controller.loiterModeRcValue + modeChannelIndex: controller.loiterModeChannelIndex + modeChannelEnabled: true + modeSelected: controller.loiterModeSelected + thresholdValue: controller.loiterModeThreshold + thresholdDragEnabled: true + + onModeChannelIndexChanged: controller.loiterModeChannelIndex = modeChannelIndex + onThresholdValueChanged: controller.loiterModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - } - } - } - - Component { - id: invalidComponent - FactPanel { - anchors.fill: parent - color: qgcPal.window - - Column { - width: parent.width - spacing: 20 - - QGCLabel { - text: "FLIGHT MODES CONFIG" - font.pixelSize: ScreenTools.largeFontPixelSize - } - - 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." + ModeSwitchDisplay { + id: returnMode + flightModeName: returnModeName + flightModeDescription: controller.fixedWing ? fwReturnModeDescription : mrReturnModeDescription + rcValue: controller.returnModeRcValue + modeChannelIndex: controller.returnModeChannelIndex + modeChannelEnabled: true + modeSelected: controller.returnModeSelected + thresholdValue: controller.returnModeThreshold + thresholdDragEnabled: true + + onModeChannelIndexChanged: controller.returnModeChannelIndex = modeChannelIndex + onThresholdValueChanged: controller.returnModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: controller.configurationErrors + ModeSwitchDisplay { + id: offboardMode + flightModeName: offboardModeName + flightModeDescription: offboardModeDescription + rcValue: controller.offboardModeRcValue + modeChannelIndex: controller.offboardModeChannelIndex + modeChannelEnabled: true + modeSelected: controller.offboardModeSelected + thresholdValue: controller.offboardModeThreshold + thresholdDragEnabled: true + + onModeChannelIndexChanged: controller.offboardModeChannelIndex = modeChannelIndex + onThresholdValueChanged: controller.offboardModeThreshold = thresholdValue + + Behavior on y { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } - } - } - } -} + } // Item + } // Scroll View + } // QGCViewPanel +} // QGCView diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc b/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc index ef71cdc26bfe7de0c9d669f9a79b0583a706f8ec..41c8d622b857ceecccf46d6feac406954498c1c6 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc +++ b/src/AutoPilotPlugins/PX4/FlightModesComponentController.cc @@ -33,31 +33,115 @@ #include FlightModesComponentController::FlightModesComponentController(void) : - _liveRCValues(false), _validConfiguration(false), - _channelCount(18) + _channelCount(18), + _manualModeSelected(false), + _assistModeSelected(false), + _autoModeSelected(false), + _acroModeSelected(false), + _altCtlModeSelected(false), + _posCtlModeSelected(false), + _missionModeSelected(false), + _loiterModeSelected(false), + _returnModeSelected(false), + _offboardModeSelected(false) { QStringList usedParams; - usedParams << "RC_MAP_THROTTLE" << "RC_MAP_YAW" << "RC_MAP_PITCH" << "RC_MAP_ROLL" << "RC_MAP_FLAPS" << "RC_MAP_AUX1" << "RC_MAP_AUX2" << "RC_MAP_ACRO_SW"; + usedParams << "RC_MAP_THROTTLE" << "RC_MAP_YAW" << "RC_MAP_PITCH" << "RC_MAP_ROLL" << "RC_MAP_FLAPS" << "RC_MAP_AUX1" << "RC_MAP_AUX2" << + "RC_MAP_MODE_SW" << "RC_MAP_RETURN_SW" << "RC_MAP_LOITER_SW" << "RC_MAP_POSCTL_SW" << "RC_MAP_OFFB_SW" << "RC_MAP_ACRO_SW"; if (!_allParametersExists(FactSystem::defaultComponentId, usedParams)) { return; } - - _initRcValues(); + + _init(); _validateConfiguration(); + + connect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged); } FlightModesComponentController::~FlightModesComponentController() { - setSendLiveRCSwitchRanges(false); + disconnect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged); } - -void FlightModesComponentController::_initRcValues(void) +void FlightModesComponentController::_init(void) { - for (int i=0; i<_chanMax; i++) { - _rcValues << 1.0; + // FIXME: What about VTOL? That confuses the whole Flight Mode naming scheme + _fixedWing = _uas->getSystemType() == MAV_TYPE_FIXED_WING; + + // We need to know min and max for channel in order to calculate percentage range + for (int channel=0; channel<_chanMax; channel++) { + QString rcMinParam, rcMaxParam, rcRevParam; + + rcMinParam = QString("RC%1_MIN").arg(channel+1); + rcMaxParam = QString("RC%1_MAX").arg(channel+1); + rcRevParam = QString("RC%1_REV").arg(channel+1); + + QVariant value; + + _rgRCMin[channel] = getParameterFact(FactSystem::defaultComponentId, rcMinParam)->value().toInt(); + _rgRCMax[channel] = getParameterFact(FactSystem::defaultComponentId, rcMaxParam)->value().toInt(); + + float floatReversed = getParameterFact(-1, rcRevParam)->value().toFloat(); + _rgRCReversed[channel] = floatReversed == -1.0f; + + _rcValues[channel] = 0.0; } + + // RC_CHAN_CNT parameter is set by Radio Cal to specify the number of radio channels. + if (parameterExists(FactSystem::defaultComponentId, "RC_CHAN_CNT")) { + _channelCount = getParameterFact(FactSystem::defaultComponentId, "RC_CHAN_CNT")->value().toInt(); + } else { + _channelCount =_chanMax; + } + if (_channelCount <= 0 || _channelCount > _chanMax) { + // Parameter exists, but has not yet been set or is invalid. Use default + _channelCount = _chanMax; + } + + int modeChannel = getParameterFact(-1, "RC_MAP_MODE_SW")->value().toInt(); + int posCtlChannel = getParameterFact(-1, "RC_MAP_POSCTL_SW")->value().toInt(); + int loiterChannel = getParameterFact(-1, "RC_MAP_LOITER_SW")->value().toInt(); + + if (posCtlChannel == 0) { + // PosCtl disabled so AltCtl must move back to main Mode switch + _assistModeVisible = false; + } else { + // Assist mode is visible if AltCtl/PosCtl are on seperate channel from main Mode switch + _assistModeVisible = posCtlChannel != modeChannel; + } + + if (loiterChannel == 0) { + // Loiter disabled so Mission must move back to main Mode switch + _autoModeVisible = false; + } else { + // Auto mode is visible if Mission/Loiter are on seperate channel from main Mode switch + _autoModeVisible = loiterChannel != modeChannel; + } + + // Setup the channel combobox model + + QList usedChannels; + QStringList attitudeParams; + + attitudeParams << "RC_MAP_THROTTLE" << "RC_MAP_YAW" << "RC_MAP_PITCH" << "RC_MAP_ROLL" << "RC_MAP_FLAPS" << "RC_MAP_AUX1" << "RC_MAP_AUX2"; + foreach(QString attitudeParam, attitudeParams) { + int channel = getParameterFact(-1, attitudeParam)->value().toInt(); + if (channel != 0) { + usedChannels << channel; + } + } + + _channelListModel << "Disabled"; + _channelListModelChannel << 0; + for (int channel=1; channel<_channelCount; channel++) { + if (!usedChannels.contains(channel)) { + _channelListModel << QString("Channel %1").arg(channel); + _channelListModelChannel << channel; + } + } + + _recalcModeRows(); } /// This will look for parameter settings which would cause the config to not run correctly. @@ -66,19 +150,12 @@ void FlightModesComponentController::_validateConfiguration(void) { _validConfiguration = true; - _channelCount = parameterExists(FactSystem::defaultComponentId, "RC_CHAN_CNT") ? getParameterFact(FactSystem::defaultComponentId, "RC_CHAN_CNT")->value().toInt() : _chanMax; - if (_channelCount <= 0 || _channelCount > _chanMax) { - // Parameter exists, but has not yet been set or is invalid. Use default - _channelCount = _chanMax; - } - // Make sure switches are valid and within channel range - QStringList switchParams, switchNames; + QStringList switchParams; QList switchMappings; - switchParams << "RC_MAP_MODE_SW" << "RC_MAP_RETURN_SW" << "RC_MAP_LOITER_SW" << "RC_MAP_POSCTL_SW" << "RC_MAP_OFFB_SW" << "RC_MAP_ACRO_SW"; - switchNames << "Mode Switch" << "Return Switch" << "Loiter Switch" << "PosCtl Switch" << "Offboard Switch" << "Acro Switch"; + switchParams << "RC_MAP_MODE_SW" << "RC_MAP_ACRO_SW" << "RC_MAP_POSCTL_SW" << "RC_MAP_LOITER_SW" << "RC_MAP_RETURN_SW" << "RC_MAP_OFFB_SW"; for(int i=0; ivalue().toInt(); @@ -86,16 +163,15 @@ void FlightModesComponentController::_validateConfiguration(void) 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); + _configurationErrors += QString("%1 is set to %2. Mapping must between 0 and %3 (inclusive).\n").arg(switchParams[i]).arg(map).arg(_channelCount); } } - // Make sure switches are not double-mapped + // Make sure mode switches are not double-mapped - QStringList attitudeParams, attitudeNames; + QStringList attitudeParams; 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; ivalue().toInt(); @@ -103,58 +179,23 @@ void FlightModesComponentController::_validateConfiguration(void) for (int j=0; jvalue().toInt(); - - for (int j=0; jvalue().toInt(); - _rgRCMax[i] = getParameterFact(FactSystem::defaultComponentId, rcMaxParam)->value().toInt(); - - float floatReversed = getParameterFact(-1, rcRevParam)->value().toFloat(); - _rgRCReversed[i] = floatReversed == -1.0f; + foreach(QString thresholdParam, thresholdParams) { + float threshold = getParameterFact(-1, thresholdParam)->value().toFloat(); + if (threshold < 0.0f || threshold > 1.0f) { + _validConfiguration = false; + _configurationErrors += QString("%1 is set to %2. Threshold must between 0.0 and 1.0 (inclusive).\n").arg(thresholdParam).arg(threshold); } - - _uas->startCalibration(UASInterface::StartCalibrationRadio); - connect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged); - } else { - disconnect(_uas, &UASInterface::remoteControlChannelRawChanged, this, &FlightModesComponentController::_remoteControlChannelRawChanged); - _uas->stopCalibration(); - _initRcValues(); - emit switchLiveRangeChanged(); } } @@ -178,6 +219,9 @@ void FlightModesComponentController::_remoteControlChannelRawChanged(int chan, f } _rcValues[chan] = percentRange; + + _recalcModeSelections(); + emit switchLiveRangeChanged(); } @@ -187,38 +231,582 @@ double FlightModesComponentController::_switchLiveRange(const QString& param) int channel = getParameterFact(-1, param)->value().toInt(); if (channel == 0) { - return 1.0; + return 0.0; } else { return _rcValues[channel - 1]; } } -double FlightModesComponentController::modeSwitchLiveRange(void) +double FlightModesComponentController::manualModeRcValue(void) { return _switchLiveRange("RC_MAP_MODE_SW"); } -double FlightModesComponentController::returnSwitchLiveRange(void) +double FlightModesComponentController::assistModeRcValue(void) +{ + return manualModeRcValue(); +} + +double FlightModesComponentController::autoModeRcValue(void) +{ + return manualModeRcValue(); +} + +double FlightModesComponentController::acroModeRcValue(void) +{ + return _switchLiveRange("RC_MAP_ACRO_SW"); +} + +double FlightModesComponentController::altCtlModeRcValue(void) +{ + int posCtlSwitchChannel = getParameterFact(-1, "RC_MAP_POSCTL_SW")->value().toInt(); + + if (posCtlSwitchChannel == 0) { + return _switchLiveRange("RC_MAP_MODE_SW"); + } else { + return _switchLiveRange("RC_MAP_POSCTL_SW"); + } +} + +double FlightModesComponentController::posCtlModeRcValue(void) +{ + return _switchLiveRange("RC_MAP_POSCTL_SW"); +} + +double FlightModesComponentController::missionModeRcValue(void) +{ + int returnSwitchChannel = getParameterFact(-1, "RC_MAP_RETURN_SW")->value().toInt(); + int loiterSwitchChannel = getParameterFact(-1, "RC_MAP_LOITER_SW")->value().toInt(); + + const char* switchChannelParam = "RC_MAP_MODE_SW"; + + if (returnSwitchChannel == 0) { + if (loiterSwitchChannel != 0) { + switchChannelParam = "RC_MAP_LOITER_SW"; + } + } else { + if (loiterSwitchChannel == 0) { + switchChannelParam = "RC_MAP_RETURN_SW"; + } else { + switchChannelParam = "RC_MAP_LOITER_SW"; + } + } + + return _switchLiveRange(switchChannelParam); +} + +double FlightModesComponentController::loiterModeRcValue(void) +{ + return _switchLiveRange("RC_MAP_LOITER_SW"); +} + +double FlightModesComponentController::returnModeRcValue(void) { return _switchLiveRange("RC_MAP_RETURN_SW"); } -double FlightModesComponentController::offboardSwitchLiveRange(void) +double FlightModesComponentController::offboardModeRcValue(void) { return _switchLiveRange("RC_MAP_OFFB_SW"); } -double FlightModesComponentController::loiterSwitchLiveRange(void) +void FlightModesComponentController::_recalcModeSelections(void) { - return _switchLiveRange("RC_MAP_LOITER_SW"); + _manualModeSelected = false; + _assistModeSelected = false; + _autoModeSelected = false; + _acroModeSelected = false; + _altCtlModeSelected = false; + _posCtlModeSelected = false; + _missionModeSelected = false; + _loiterModeSelected = false; + _returnModeSelected = false; + _offboardModeSelected = false; + + // Convert channels to 0-based, -1 signals not mapped + int modeSwitchChannel = getParameterFact(-1, "RC_MAP_MODE_SW")->value().toInt() - 1; + int acroSwitchChannel = getParameterFact(-1, "RC_MAP_ACRO_SW")->value().toInt() - 1; + int posCtlSwitchChannel = getParameterFact(-1, "RC_MAP_POSCTL_SW")->value().toInt() - 1; + int loiterSwitchChannel = getParameterFact(-1, "RC_MAP_LOITER_SW")->value().toInt() - 1; + int returnSwitchChannel = getParameterFact(-1, "RC_MAP_RETURN_SW")->value().toInt() - 1; + int offboardSwitchChannel = getParameterFact(-1, "RC_MAP_OFFB_SW")->value().toInt() - 1; + + double autoThreshold = getParameterFact(-1, "RC_AUTO_TH")->value().toDouble(); + double assistThreshold = getParameterFact(-1, "RC_ASSIST_TH")->value().toDouble(); + double acroThreshold = getParameterFact(-1, "RC_ACRO_TH")->value().toDouble(); + double posCtlThreshold = getParameterFact(-1, "RC_POSCTL_TH")->value().toDouble(); + double loiterThreshold = getParameterFact(-1, "RC_LOITER_TH")->value().toDouble(); + double returnThreshold = getParameterFact(-1, "RC_RETURN_TH")->value().toDouble(); + double offboardThreshold = getParameterFact(-1, "RC_OFFB_TH")->value().toDouble(); + + if (modeSwitchChannel >= 0) { + if (offboardSwitchChannel >= 0 && _rcValues[offboardSwitchChannel] >= offboardThreshold) { + _offboardModeSelected = true; + } else if (returnSwitchChannel >= 0 && _rcValues[returnSwitchChannel] >= returnThreshold) { + _returnModeSelected = true; + } else { + if (_rcValues[modeSwitchChannel] >= autoThreshold) { + _autoModeSelected = true; + if (loiterSwitchChannel >= 0 && _rcValues[loiterSwitchChannel] >= loiterThreshold) { + _loiterModeSelected = true; + } else { + _missionModeSelected = true; + } + } else if (_rcValues[modeSwitchChannel] >= assistThreshold) { + _assistModeSelected = true; + if (posCtlSwitchChannel >= 0 && _rcValues[posCtlSwitchChannel] >= posCtlThreshold) { + _posCtlModeSelected = true; + } else { + _altCtlModeSelected = true; + } + } else if (acroSwitchChannel >= 0 && _rcValues[acroSwitchChannel] >= acroThreshold) { + _acroModeSelected = true; + } else { + _manualModeSelected = true; + } + } + } + + emit modesSelectedChanged(); } -double FlightModesComponentController::posCtlSwitchLiveRange(void) +void FlightModesComponentController::_recalcModeRows(void) { - return _switchLiveRange("RC_MAP_POSCTL_SW"); + int modeSwitchChannel = getParameterFact(-1, "RC_MAP_MODE_SW")->value().toInt(); + int acroSwitchChannel = getParameterFact(-1, "RC_MAP_ACRO_SW")->value().toInt(); + int posCtlSwitchChannel = getParameterFact(-1, "RC_MAP_POSCTL_SW")->value().toInt(); + int loiterSwitchChannel = getParameterFact(-1, "RC_MAP_LOITER_SW")->value().toInt(); + int returnSwitchChannel = getParameterFact(-1, "RC_MAP_RETURN_SW")->value().toInt(); + int offboardSwitchChannel = getParameterFact(-1, "RC_MAP_OFFB_SW")->value().toInt(); + + if (modeSwitchChannel == 0) { + _acroModeRow = 0; + _assistModeRow = 1; + _autoModeRow = 2; + _altCtlModeRow = 3; + _posCtlModeRow = 4; + _loiterModeRow = 5; + _missionModeRow = 6; + _returnModeRow = 7; + _offboardModeRow = 8; + } else { + int row = 0; + + // First set is all switches on main mode channel + + if (acroSwitchChannel == modeSwitchChannel) { + _acroModeRow = row++; + } + _assistModeRow = row++; + if (posCtlSwitchChannel == modeSwitchChannel) { + _altCtlModeRow = row++; + _posCtlModeRow = row++; + } else if (posCtlSwitchChannel == 0) { + _altCtlModeRow = row++; + } + _autoModeRow = row++; + if (loiterSwitchChannel == modeSwitchChannel) { + _missionModeRow = row++; + _loiterModeRow = row++; + } else if (loiterSwitchChannel == 0) { + _missionModeRow = row++; + } + if (returnSwitchChannel == modeSwitchChannel) { + _returnModeRow = row++; + } + if (offboardSwitchChannel == modeSwitchChannel) { + _offboardModeRow = row++; + } + + // Now individual enabled switches not on main mode channel + + if (acroSwitchChannel != 0 && acroSwitchChannel != modeSwitchChannel) { + _acroModeRow = row++; + } + if (posCtlSwitchChannel != 0 && posCtlSwitchChannel != modeSwitchChannel) { + _altCtlModeRow = row++; + _posCtlModeRow = row++; + } + if (loiterSwitchChannel != 0 && loiterSwitchChannel != modeSwitchChannel) { + _missionModeRow = row++; + _loiterModeRow = row++; + } + if (returnSwitchChannel != 0 && returnSwitchChannel != modeSwitchChannel) { + _returnModeRow = row++; + } + if (offboardSwitchChannel != 0 && offboardSwitchChannel != modeSwitchChannel) { + _offboardModeRow = row++; + } + + // Now disabled switches + + if (acroSwitchChannel == 0) { + _acroModeRow = row++; + } + if (posCtlSwitchChannel == 0) { + _posCtlModeRow = row++; + } + if (loiterSwitchChannel == 0) { + _loiterModeRow = row++; + } + if (returnSwitchChannel == 0) { + _returnModeRow = row++; + } + if (offboardSwitchChannel == 0) { + _offboardModeRow = row++; + } + } + + emit modeRowsChanged(); } -double FlightModesComponentController::acroSwitchLiveRange(void) +double FlightModesComponentController::manualModeThreshold(void) { - return _switchLiveRange("RC_MAP_ACRO_SW"); + return 0.0; +} + +double FlightModesComponentController::assistModeThreshold(void) +{ + return getParameterFact(-1, "RC_ASSIST_TH")->value().toDouble(); +} + +double FlightModesComponentController::autoModeThreshold(void) +{ + return getParameterFact(-1, "RC_AUTO_TH")->value().toDouble(); +} + +double FlightModesComponentController::acroModeThreshold(void) +{ + return getParameterFact(-1, "RC_ACRO_TH")->value().toDouble(); +} + +double FlightModesComponentController::altCtlModeThreshold(void) +{ + return _assistModeVisible ? 0.0 : getParameterFact(-1, "RC_ASSIST_TH")->value().toDouble(); +} + +double FlightModesComponentController::posCtlModeThreshold(void) +{ + return getParameterFact(-1, "RC_POSCTL_TH")->value().toDouble(); +} + +double FlightModesComponentController::missionModeThreshold(void) +{ + return _autoModeVisible ? 0.0 : getParameterFact(-1, "RC_AUTO_TH")->value().toDouble(); +} + + +double FlightModesComponentController::loiterModeThreshold(void) +{ + return getParameterFact(-1, "RC_LOITER_TH")->value().toDouble(); +} + +double FlightModesComponentController::returnModeThreshold(void) +{ + return getParameterFact(-1, "RC_RETURN_TH")->value().toDouble(); +} + +double FlightModesComponentController::offboardModeThreshold(void) +{ + return getParameterFact(-1, "RC_OFFB_TH")->value().toDouble(); +} + +void FlightModesComponentController::setAssistModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_ASSIST_TH")->setValue(threshold); + _recalcModeSelections(); +} + +void FlightModesComponentController::setAutoModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_AUTO_TH")->setValue(threshold); + _recalcModeSelections(); +} + +void FlightModesComponentController::setAcroModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_ACRO_TH")->setValue(threshold); + _recalcModeSelections(); +} + +void FlightModesComponentController::setAltCtlModeThreshold(double threshold) +{ + setAssistModeThreshold(threshold); +} + +void FlightModesComponentController::setPosCtlModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_POSCTL_TH")->setValue(threshold); + _recalcModeSelections(); +} + +void FlightModesComponentController::setMissionModeThreshold(double threshold) +{ + setAutoModeThreshold(threshold); +} + +void FlightModesComponentController::setLoiterModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_LOITER_TH")->setValue(threshold); + _recalcModeSelections(); +} + +void FlightModesComponentController::setReturnModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_RETURN_TH")->setValue(threshold); + _recalcModeSelections(); +} + +void FlightModesComponentController::setOffboardModeThreshold(double threshold) +{ + getParameterFact(-1, "RC_OFFB_TH")->setValue(threshold); + _recalcModeSelections(); +} + +int FlightModesComponentController::_channelToChannelIndex(int channel) +{ + return _channelListModelChannel.lastIndexOf(channel); +} + +int FlightModesComponentController::_channelToChannelIndex(const QString& channelParam) +{ + return _channelToChannelIndex(getParameterFact(-1, channelParam)->value().toInt()); +} + +int FlightModesComponentController::manualModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_MODE_SW"); +} + +int FlightModesComponentController::assistModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_MODE_SW"); +} + +int FlightModesComponentController::autoModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_MODE_SW"); +} + +int FlightModesComponentController::acroModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_ACRO_SW"); +} + +int FlightModesComponentController::altCtlModeChannelIndex(void) +{ + int posCtlSwitchChannel = getParameterFact(-1, "RC_MAP_POSCTL_SW")->value().toInt(); + + if (posCtlSwitchChannel == 0) { + return _channelToChannelIndex("RC_MAP_MODE_SW"); + } else { + return _channelToChannelIndex(posCtlSwitchChannel); + } +} + +int FlightModesComponentController::posCtlModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_POSCTL_SW"); +} + +int FlightModesComponentController::loiterModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_LOITER_SW"); +} + +int FlightModesComponentController::missionModeChannelIndex(void) +{ + int loiterSwitchChannel = getParameterFact(-1, "RC_MAP_LOITER_SW")->value().toInt(); + + if (loiterSwitchChannel == 0) { + return _channelToChannelIndex("RC_MAP_MODE_SW"); + } else { + return _channelToChannelIndex(loiterSwitchChannel); + } +} + +int FlightModesComponentController::returnModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_RETURN_SW"); +} + +int FlightModesComponentController::offboardModeChannelIndex(void) +{ + return _channelToChannelIndex("RC_MAP_OFFB_SW"); +} + +int FlightModesComponentController::_channelIndexToChannel(int index) +{ + return _channelListModelChannel[index]; +} + +void FlightModesComponentController::setManualModeChannelIndex(int index) +{ + getParameterFact(-1, "RC_MAP_MODE_SW")->setValue(_channelIndexToChannel(index)); + + _recalcModeSelections(); + _recalcModeRows(); +} + +void FlightModesComponentController::setAcroModeChannelIndex(int index) +{ + getParameterFact(-1, "RC_MAP_ACRO_SW")->setValue(_channelIndexToChannel(index)); + + _recalcModeSelections(); + _recalcModeRows(); +} + +void FlightModesComponentController::setPosCtlModeChannelIndex(int index) +{ + int channel = _channelIndexToChannel(index); + + getParameterFact(-1, "RC_MAP_POSCTL_SW")->setValue(channel); + + if (channel == 0) { + // PosCtl disabled so AltCtl must move back to main Mode switch + _assistModeVisible = false; + } else { + // Assist mode is visible if AltCtl/PosCtl are on seperate channel from main Mode switch + _assistModeVisible = channel != getParameterFact(-1, "RC_MAP_MODE_SW")->value().toInt(); + } + + emit altCtlModeChannelIndexChanged(index); + emit modesVisibleChanged(); + + _recalcModeSelections(); + _recalcModeRows(); +} + +void FlightModesComponentController::setLoiterModeChannelIndex(int index) +{ + int channel = _channelIndexToChannel(index); + + getParameterFact(-1, "RC_MAP_LOITER_SW")->setValue(channel); + + if (channel == 0) { + // Loiter disabled so Mission must move back to main Mode switch + _autoModeVisible = false; + } else { + // Auto mode is visible if Mission/Loiter are on seperate channel from main Mode switch + _autoModeVisible = channel != getParameterFact(-1, "RC_MAP_MODE_SW")->value().toInt(); + } + + emit missionModeChannelIndexChanged(index); + emit modesVisibleChanged(); + + _recalcModeSelections(); + _recalcModeRows(); +} + +void FlightModesComponentController::setReturnModeChannelIndex(int index) +{ + getParameterFact(-1, "RC_MAP_RETURN_SW")->setValue(_channelIndexToChannel(index)); + _recalcModeSelections(); + _recalcModeRows(); +} + +void FlightModesComponentController::setOffboardModeChannelIndex(int index) +{ + getParameterFact(-1, "RC_MAP_OFFB_SW")->setValue(_channelIndexToChannel(index)); + _recalcModeSelections(); + _recalcModeRows(); +} + +void FlightModesComponentController::generateThresholds(void) +{ + // Reset all thresholds to 0.0 + + QStringList thresholdParams; + + thresholdParams << "RC_ASSIST_TH" << "RC_AUTO_TH" << "RC_ACRO_TH" << "RC_POSCTL_TH" << "RC_LOITER_TH" << "RC_RETURN_TH" << "RC_OFFB_TH"; + + foreach(QString thresholdParam, thresholdParams) { + getParameterFact(-1, thresholdParam)->setValue(0.0f); + } + + // Redistribute + + int modeChannel = getParameterFact(-1, "RC_MAP_MODE_SW")->value().toInt(); + int acroChannel = getParameterFact(-1, "RC_MAP_ACRO_SW")->value().toInt(); + int posCtlChannel = getParameterFact(-1, "RC_MAP_POSCTL_SW")->value().toInt(); + int loiterChannel = getParameterFact(-1, "RC_MAP_LOITER_SW")->value().toInt(); + int returnChannel = getParameterFact(-1, "RC_MAP_RETURN_SW")->value().toInt(); + int offboardChannel = getParameterFact(-1, "RC_MAP_OFFB_SW")->value().toInt(); + + if (modeChannel != 0) { + int positions = 3; // Manual/Assist/Auto always exist + + bool acroOnModeSwitch = modeChannel == acroChannel; + bool posCtlOnModeSwitch = modeChannel == posCtlChannel; + bool loiterOnModeSwitch = modeChannel == loiterChannel; + bool returnOnModeSwitch = modeChannel == returnChannel; + bool offboardOnModeSwitch = modeChannel == offboardChannel; + + positions += acroOnModeSwitch ? 1 : 0; + positions += posCtlOnModeSwitch ? 1 : 0; + positions += loiterOnModeSwitch ? 1 : 0; + positions += returnOnModeSwitch ? 1 : 0; + positions += offboardOnModeSwitch ? 1 : 0; + + float increment = 1.0f / positions; + float currentThreshold = 0.0f; + + if (acroOnModeSwitch) { + currentThreshold += increment; + getParameterFact(-1, "RC_ACRO_TH")->setValue(currentThreshold); + acroChannel = 0; + } + + currentThreshold += increment; + getParameterFact(-1, "RC_ASSIST_TH")->setValue(currentThreshold); + if (posCtlOnModeSwitch) { + currentThreshold += increment; + getParameterFact(-1, "RC_POSCTL_TH")->setValue(currentThreshold); + posCtlChannel = 0; + } + + currentThreshold += increment; + getParameterFact(-1, "RC_AUTO_TH")->setValue(currentThreshold); + if (loiterOnModeSwitch) { + currentThreshold += increment; + getParameterFact(-1, "RC_LOITER_TH")->setValue(currentThreshold); + loiterChannel = 0; + } + + if (returnOnModeSwitch) { + currentThreshold += increment; + getParameterFact(-1, "RC_RETURN_TH")->setValue(currentThreshold); + returnChannel = 0; + } + + if (offboardOnModeSwitch) { + currentThreshold += increment; + getParameterFact(-1, "RC_OFFB_TH")->setValue(currentThreshold); + offboardChannel = 0; + } + } + + if (acroChannel != 0) { + // If only two positions don't set threshold at midrange. Setting to 0.25 + // allows for this channel to work with either a two or three position switch + getParameterFact(-1, "RC_ACRO_TH")->setValue(0.25f); + } + + if (posCtlChannel != 0) { + getParameterFact(-1, "RC_POSCTL_TH")->setValue(0.25f); + } + + if (loiterChannel != 0) { + getParameterFact(-1, "RC_LOITER_TH")->setValue(0.25f); + } + + if (returnChannel != 0) { + getParameterFact(-1, "RC_RETURN_TH")->setValue(0.25f); + } + + if (offboardChannel != 0) { + getParameterFact(-1, "RC_OFFB_TH")->setValue(0.25f); + } + + emit thresholdsChanged(); } diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentController.h b/src/AutoPilotPlugins/PX4/FlightModesComponentController.h index b1b100cdf4a8287f40b9698a46e6a34ed163dbd9..4f7257289754a15158423766ddeeca8681cbfd3d 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponentController.h +++ b/src/AutoPilotPlugins/PX4/FlightModesComponentController.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "UASInterface.h" #include "AutoPilotPlugin.h" @@ -48,49 +49,198 @@ public: Q_PROPERTY(QString configurationErrors MEMBER _configurationErrors CONSTANT) Q_PROPERTY(int channelCount MEMBER _channelCount CONSTANT) + Q_PROPERTY(bool fixedWing MEMBER _fixedWing CONSTANT) - Q_PROPERTY(double modeSwitchLiveRange READ modeSwitchLiveRange NOTIFY switchLiveRangeChanged) - Q_PROPERTY(double loiterSwitchLiveRange READ loiterSwitchLiveRange NOTIFY switchLiveRangeChanged) - Q_PROPERTY(double posCtlSwitchLiveRange READ posCtlSwitchLiveRange NOTIFY switchLiveRangeChanged) - Q_PROPERTY(double returnSwitchLiveRange READ returnSwitchLiveRange NOTIFY switchLiveRangeChanged) - Q_PROPERTY(double offboardSwitchLiveRange READ offboardSwitchLiveRange NOTIFY switchLiveRangeChanged) - Q_PROPERTY(double acroSwitchLiveRange READ acroSwitchLiveRange NOTIFY switchLiveRangeChanged) + Q_PROPERTY(int assistModeRow MEMBER _assistModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int autoModeRow MEMBER _autoModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int acroModeRow MEMBER _acroModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int altCtlModeRow MEMBER _altCtlModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int posCtlModeRow MEMBER _posCtlModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int loiterModeRow MEMBER _loiterModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int missionModeRow MEMBER _missionModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int returnModeRow MEMBER _returnModeRow NOTIFY modeRowsChanged) + Q_PROPERTY(int offboardModeRow MEMBER _offboardModeRow NOTIFY modeRowsChanged) - Q_PROPERTY(bool sendLiveRCSwitchRanges READ sendLiveRCSwitchRanges WRITE setSendLiveRCSwitchRanges NOTIFY liveRCSwitchRangesChanged) + Q_PROPERTY(int manualModeChannelIndex READ manualModeChannelIndex WRITE setManualModeChannelIndex NOTIFY manualModeChannelIndexChanged) + Q_PROPERTY(int assistModeChannelIndex READ assistModeChannelIndex NOTIFY assistModeChannelIndexChanged) + Q_PROPERTY(int autoModeChannelIndex READ autoModeChannelIndex NOTIFY autoModeChannelIndexChanged) + Q_PROPERTY(int acroModeChannelIndex READ acroModeChannelIndex WRITE setAcroModeChannelIndex NOTIFY acroModeChannelIndexChanged) + Q_PROPERTY(int altCtlModeChannelIndex READ altCtlModeChannelIndex NOTIFY altCtlModeChannelIndexChanged) + Q_PROPERTY(int posCtlModeChannelIndex READ posCtlModeChannelIndex WRITE setPosCtlModeChannelIndex NOTIFY posCtlModeChannelIndexChanged) + Q_PROPERTY(int loiterModeChannelIndex READ loiterModeChannelIndex WRITE setLoiterModeChannelIndex NOTIFY loiterModeChannelIndexChanged) + Q_PROPERTY(int missionModeChannelIndex READ missionModeChannelIndex NOTIFY missionModeChannelIndexChanged) + Q_PROPERTY(int returnModeChannelIndex READ returnModeChannelIndex WRITE setReturnModeChannelIndex NOTIFY returnModeChannelIndexChanged) + Q_PROPERTY(int offboardModeChannelIndex READ offboardModeChannelIndex WRITE setOffboardModeChannelIndex NOTIFY offboardModeChannelIndexChanged) - double modeSwitchLiveRange(void); - double loiterSwitchLiveRange(void); - double posCtlSwitchLiveRange(void); - double returnSwitchLiveRange(void); - double offboardSwitchLiveRange(void); - double acroSwitchLiveRange(void); + Q_PROPERTY(double manualModeRcValue READ manualModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double assistModeRcValue READ assistModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double autoModeRcValue READ autoModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double acroModeRcValue READ acroModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double altCtlModeRcValue READ altCtlModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double posCtlModeRcValue READ posCtlModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double loiterModeRcValue READ loiterModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double missionModeRcValue READ missionModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double returnModeRcValue READ returnModeRcValue NOTIFY switchLiveRangeChanged) + Q_PROPERTY(double offboardModeRcValue READ offboardModeRcValue NOTIFY switchLiveRangeChanged) - bool sendLiveRCSwitchRanges(void) { return _liveRCValues; } - void setSendLiveRCSwitchRanges(bool start); + Q_PROPERTY(double manualModeThreshold READ manualModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double assistModeThreshold READ assistModeThreshold WRITE setAssistModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double autoModeThreshold READ autoModeThreshold WRITE setAutoModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double acroModeThreshold READ acroModeThreshold WRITE setAcroModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double altCtlModeThreshold READ altCtlModeThreshold WRITE setAltCtlModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double posCtlModeThreshold READ posCtlModeThreshold WRITE setPosCtlModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double loiterModeThreshold READ loiterModeThreshold WRITE setLoiterModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double missionModeThreshold READ missionModeThreshold WRITE setMissionModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double returnModeThreshold READ returnModeThreshold WRITE setReturnModeThreshold NOTIFY thresholdsChanged) + Q_PROPERTY(double offboardModeThreshold READ offboardModeThreshold WRITE setOffboardModeThreshold NOTIFY thresholdsChanged) + + Q_PROPERTY(bool assistModeVisible MEMBER _assistModeVisible NOTIFY modesVisibleChanged) + Q_PROPERTY(bool autoModeVisible MEMBER _autoModeVisible NOTIFY modesVisibleChanged) + + Q_PROPERTY(bool manualModeSelected MEMBER _manualModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool assistModeSelected MEMBER _assistModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool autoModeSelected MEMBER _autoModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool acroModeSelected MEMBER _acroModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool altCtlModeSelected MEMBER _altCtlModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool posCtlModeSelected MEMBER _posCtlModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool missionModeSelected MEMBER _missionModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool loiterModeSelected MEMBER _loiterModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool returnModeSelected MEMBER _returnModeSelected NOTIFY modesSelectedChanged) + Q_PROPERTY(bool offboardModeSelected MEMBER _offboardModeSelected NOTIFY modesSelectedChanged) + + Q_PROPERTY(QStringList channelListModel MEMBER _channelListModel CONSTANT) + + Q_INVOKABLE void generateThresholds(void); + + int assistModeRow(void); + int autoModeRow(void); + int acroModeRow(void); + int altCtlModeRow(void); + int posCtlModeRow(void); + int loiterModeRow(void); + int missionModeRow(void); + int returnModeRow(void); + int offboardModeRow(void); + + int manualModeChannelIndex(void); + int assistModeChannelIndex(void); + int autoModeChannelIndex(void); + int acroModeChannelIndex(void); + int altCtlModeChannelIndex(void); + int posCtlModeChannelIndex(void); + int loiterModeChannelIndex(void); + int missionModeChannelIndex(void); + int returnModeChannelIndex(void); + int offboardModeChannelIndex(void); + + void setManualModeChannelIndex(int index); + void setAcroModeChannelIndex(int index); + void setPosCtlModeChannelIndex(int index); + void setLoiterModeChannelIndex(int index); + void setReturnModeChannelIndex(int index); + void setOffboardModeChannelIndex(int index); + + double manualModeRcValue(void); + double assistModeRcValue(void); + double autoModeRcValue(void); + double acroModeRcValue(void); + double altCtlModeRcValue(void); + double posCtlModeRcValue(void); + double missionModeRcValue(void); + double loiterModeRcValue(void); + double returnModeRcValue(void); + double offboardModeRcValue(void); + + double manualModeThreshold(void); + double assistModeThreshold(void); + double autoModeThreshold(void); + double acroModeThreshold(void); + double altCtlModeThreshold(void); + double posCtlModeThreshold(void); + double missionModeThreshold(void); + double loiterModeThreshold(void); + double returnModeThreshold(void); + double offboardModeThreshold(void); + + void setAssistModeThreshold(double threshold); + void setAutoModeThreshold(double threshold); + void setAcroModeThreshold(double threshold); + void setAltCtlModeThreshold(double threshold); + void setPosCtlModeThreshold(double threshold); + void setMissionModeThreshold(double threshold); + void setLoiterModeThreshold(double threshold); + void setReturnModeThreshold(double threshold); + void setOffboardModeThreshold(double threshold); signals: void switchLiveRangeChanged(void); - void liveRCSwitchRangesChanged(void); + void thresholdsChanged(void); + void modesSelectedChanged(void); + void modesVisibleChanged(void); + void manualModeChannelIndexChanged(int index); + void assistModeChannelIndexChanged(int index); + void autoModeChannelIndexChanged(int index); + void acroModeChannelIndexChanged(int index); + void altCtlModeChannelIndexChanged(int index); + void posCtlModeChannelIndexChanged(int index); + void loiterModeChannelIndexChanged(int index); + void missionModeChannelIndexChanged(int index); + void returnModeChannelIndexChanged(int index); + void offboardModeChannelIndexChanged(int index); + void modeRowsChanged(void); private slots: void _remoteControlChannelRawChanged(int chan, float fval); private: double _switchLiveRange(const QString& param); - void _initRcValues(void); + void _init(void); void _validateConfiguration(void); + void _recalcModeSelections(void); + void _recalcModeRows(void); + int _channelToChannelIndex(int channel); + int _channelToChannelIndex(const QString& channelParam); + int _channelIndexToChannel(int index); static const int _chanMax = 18; - QList _rcValues; - bool _liveRCValues; - int _rgRCMin[_chanMax]; - int _rgRCMax[_chanMax]; - bool _rgRCReversed[_chanMax]; + bool _fixedWing; + + double _rcValues[_chanMax]; + int _rgRCMin[_chanMax]; + int _rgRCMax[_chanMax]; + bool _rgRCReversed[_chanMax]; bool _validConfiguration; QString _configurationErrors; int _channelCount; + + int _assistModeRow; + int _autoModeRow; + int _acroModeRow; + int _altCtlModeRow; + int _posCtlModeRow; + int _loiterModeRow; + int _missionModeRow; + int _returnModeRow; + int _offboardModeRow; + + bool _manualModeSelected; + bool _assistModeSelected; + bool _autoModeSelected; + bool _acroModeSelected; + bool _altCtlModeSelected; + bool _posCtlModeSelected; + bool _missionModeSelected; + bool _loiterModeSelected; + bool _returnModeSelected; + bool _offboardModeSelected; + + bool _assistModeVisible; + bool _autoModeVisible; + + QStringList _channelListModel; + QList _channelListModelChannel; }; #endif diff --git a/src/QmlControls/ModeSwitchDisplay.qml b/src/QmlControls/ModeSwitchDisplay.qml new file mode 100644 index 0000000000000000000000000000000000000000..6cc406ee19d79d1a84e58cec0916bb9dce09828a --- /dev/null +++ b/src/QmlControls/ModeSwitchDisplay.qml @@ -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 . + + ======================================================================*/ + +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.2 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Rectangle { + property string flightModeName ///< User visible name for this flight mode + property string flightModeDescription + property real rcValue ///< Current rcValue to show in Monitor display, range: 0.0 - 1.0 + property int modeChannelIndex ///< Index into channel list for this mode + property bool modeChannelEnabled ///< true: Channel combo box is enabled + property bool modeSelected ///< true: This mode is currently selected + property real thresholdValue ///< Treshold setting for this mode, show in Threshold display, range 0.0 - 1.0 + property bool thresholdDragEnabled ///< true: Threshold value indicator can be dragged to modify value + + anchors.leftMargin: ScreenTools.defaultFontPixelWidth + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + anchors.left: parent.left + anchors.right: parent.right + height: column.height + (ScreenTools.defaultFontPixelWidth * 2) + color: _qgcPal.windowShade + + QGCPalette { id: _qgcPal; colorGroupEnabled: enabled } + + Item { + anchors.margins: ScreenTools.defaultFontPixelWidth + anchors.fill: parent + + Column { + id: column + width: parent.width + spacing: ScreenTools.defaultFontPixelHeight / 4 + + Row { + width: parent.width + spacing: ScreenTools.defaultFontPixelWidth + + Rectangle { + width: modeLabel.width + height: modeLabel.contentHeight + color: modeSelected ? _qgcPal.buttonHighlight : _qgcPal.windowShade + + QGCLabel { + id: modeLabel + width: ScreenTools.defaultFontPixelWidth * 18 + color: modeSelected ? _qgcPal.buttonHighlightText : _qgcPal.text + horizontalAlignment: Text.AlignHCenter + font.pixelSize: ScreenTools.mediumFontPixelSize + text: flightModeName + } + } + + QGCComboBox { + id: channelCombo + width: ScreenTools.defaultFontPixelWidth * 15 + model: controller.channelListModel + currentIndex: modeChannelIndex + enabled: modeChannelEnabled + + onActivated: modeChannelIndex = index + } + + QGCLabel { + width: parent.width - x + wrapMode: Text.WordWrap + text: flightModeDescription + } + } + + Row { + width: parent.width + spacing: ScreenTools.defaultFontPixelWidth * 2 + + QGCLabel { + width: ScreenTools.defaultFontPixelWidth * monitorThresholdCharWidth + height: ScreenTools.defaultFontPixelHeight + verticalAlignment: Text.AlignVCenter + text: "Monitor:" + } + + Item { + height: ScreenTools.defaultFontPixelHeight + width: parent.width - x + + // Won't be able to pull these properties, need to reference parent. + property int __lastRcValue: 1500 + readonly property int __rcValueMaxJitter: 2 + + // Bar + Rectangle { + id: bar + anchors.verticalCenter: parent.verticalCenter + width: parent.width + height: parent.height / 2 + color: _qgcPal.windowShadeDark + } + + // RC Indicator + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: parent.height * 0.75 + height: width + x: (parent.width * rcValue) - (width / 2) + radius: width / 2 + color: _qgcPal.text + } + } // Item + } + + Row { + width: parent.width + spacing: ScreenTools.defaultFontPixelWidth * 2 + + QGCLabel { + width: ScreenTools.defaultFontPixelWidth * monitorThresholdCharWidth + height: ScreenTools.defaultFontPixelHeight + verticalAlignment: Text.AlignVCenter + text: "Threshold:" + } + + + Item { + id: thresholdContainer + height: ScreenTools.defaultFontPixelHeight + width: parent.width - x + + // Bar + Rectangle { + anchors.verticalCenter: parent.verticalCenter + width: parent.width + height: parent.height / 2 + color: _qgcPal.windowShadeDark + } + + // Threshold Indicator + Rectangle { + id: thresholdIndicator + anchors.verticalCenter: parent.verticalCenter + width: parent.height * 0.75 + height: width + x: (parent.width * thresholdValue) - (width / 2) + radius: width / 2 + color: thresholdDragEnabled ? _qgcPal.buttonHighlight : _qgcPal.text + + Drag.active: thresholdDrag.drag.active + Drag.hotSpot.x: width / 2 + Drag.hotSpot.y: height / 2 + + MouseArea { + id: thresholdDrag + anchors.fill: parent + cursorShape: Qt.SizeHorCursor + drag.target: thresholdDragEnabled ? parent : null + drag.minimumX: - (width / 2) + drag.maximumX: thresholdContainer.width - (width / 2) + drag.axis: Drag.XAxis + + property bool dragActive: drag.active + + onDragActiveChanged: { + if (!drag.active) { + thresholdValue = (thresholdIndicator.x + (thresholdIndicator.width / 2)) / thresholdContainer.width + } + } + } + } + } // Item + } // Row + } // Column + } // Item +} // Rectangle diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index 6f58994b62a2fe7f9b64b46667a0eae73a590ed0..3b627337c5dbb9d6fb5fd8d70779df20ea086c84 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -19,6 +19,8 @@ ViewWidget 1.0 ViewWidget.qml ParameterEditor 1.0 ParameterEditor.qml ParameterEditorDialog 1.0 ParameterEditorDialog.qml +ModeSwitchDisplay 1.0 ModeSwitchDisplay.qml + QGCView 1.0 QGCView.qml QGCViewPanel 1.0 QGCViewPanel.qml QGCViewDialog 1.0 QGCViewDialog.qml