11 changed files with 1198 additions and 24 deletions
@ -0,0 +1,870 @@
@@ -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 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,196 @@
@@ -0,0 +1,196 @@
|
||||
/*=====================================================================
|
||||
|
||||
QGroundControl Open Source Ground Control Station |
||||
|
||||
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
======================================================================*/ |
||||
|
||||
/// @file
|
||||
/// @author Don Gagne <don@thegagnes.com>
|
||||
|
||||
#include "FlightModesComponentController.h" |
||||
#include "QGCMAVLink.h" |
||||
#include "UASManager.h" |
||||
#include "AutoPilotPluginManager.h" |
||||
|
||||
#include <QVariant> |
||||
#include <QQmlProperty> |
||||
|
||||
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<int> 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; i<switchParams.count(); i++) { |
||||
int map = _autoPilotPlugin->getFact(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; i<attitudeParams.count(); i++) { |
||||
int map = _autoPilotPlugin->getFact(attitudeParams[i])->value().toInt(); |
||||
|
||||
for (int j=0; j<switchParams.count(); j++) { |
||||
if (map != 0 && map == switchMappings[j]) { |
||||
_validConfiguration = false; |
||||
_configurationErrors += QString("%1 is set to same channel as %2.\n").arg(switchNames[j]).arg(attitudeNames[i]); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void FlightModesComponentController::setSendLiveRCSwitchRanges(bool start) |
||||
{ |
||||
if (start) { |
||||
|
||||
// We need to know min and max for channel in order to calculate percentage range
|
||||
for (int i=0; i<_chanMax; i++) { |
||||
QString rcMinParam, rcMaxParam, rcRevParam; |
||||
|
||||
rcMinParam = QString("RC%1_MIN").arg(i+1); |
||||
rcMaxParam = QString("RC%1_MAX").arg(i+1); |
||||
rcRevParam = QString("RC%1_REV").arg(i+1); |
||||
|
||||
QVariant value; |
||||
|
||||
_rgRCMin[i] = _autoPilotPlugin->getFact(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"); |
||||
} |
||||
|
@ -0,0 +1,95 @@
@@ -0,0 +1,95 @@
|
||||
/*=====================================================================
|
||||
|
||||
QGroundControl Open Source Ground Control Station |
||||
|
||||
(c) 2009, 2015 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
======================================================================*/ |
||||
|
||||
/// @file
|
||||
/// @author Don Gagne <don@thegagnes.com>
|
||||
|
||||
#ifndef FLIGHTMODESCOMPONENTCONTROLLER_H |
||||
#define FLIGHTMODESCOMPONENTCONTROLLER_H |
||||
|
||||
#include <QObject> |
||||
#include <QQuickItem> |
||||
#include <QList> |
||||
|
||||
#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<double> _rcValues; |
||||
bool _liveRCValues; |
||||
int _rgRCMin[_chanMax]; |
||||
int _rgRCMax[_chanMax]; |
||||
bool _rgRCReversed[_chanMax]; |
||||
|
||||
bool _validConfiguration; |
||||
QString _configurationErrors; |
||||
int _channelCount; |
||||
|
||||
AutoPilotPlugin* _autoPilotPlugin; |
||||
}; |
||||
|
||||
#endif |
Loading…
Reference in new issue