Commit 9d20690a authored by Don Gagne's avatar Don Gagne

FlightModeConfig plus unit test

parent e7a44abb
......@@ -282,7 +282,7 @@ FORMS += \
src/ui/designer/QGCParamSlider.ui \
src/ui/designer/QGCActionButton.ui \
src/ui/designer/QGCCommandButton.ui \
src/ui/designer/QGCComboBox.ui \
src/ui/designer/QGCToolWidgetComboBox.ui \
src/ui/designer/QGCTextLabel.ui \
src/ui/designer/QGCXYPlot.ui \
src/ui/QGCMAVLinkLogPlayer.ui \
......@@ -391,7 +391,7 @@ HEADERS += \
src/ui/designer/QGCParamSlider.h \
src/ui/designer/QGCCommandButton.h \
src/ui/designer/QGCToolWidgetItem.h \
src/ui/designer/QGCComboBox.h \
src/ui/designer/QGCToolWidgetComboBox.h \
src/ui/designer/QGCTextLabel.h \
src/ui/designer/QGCRadioChannelDisplay.h \
src/ui/designer/QGCXYPlot.h \
......@@ -477,7 +477,8 @@ HEADERS += \
src/CmdLineOptParser.h \
src/uas/QGXPX4UAS.h \
src/QGCFileDialog.h \
src/QGCMessageBox.h
src/QGCMessageBox.h \
src/QGCComboBox.h
SOURCES += \
src/main.cc \
......@@ -533,7 +534,7 @@ SOURCES += \
src/ui/designer/QGCParamSlider.cc \
src/ui/designer/QGCCommandButton.cc \
src/ui/designer/QGCToolWidgetItem.cc \
src/ui/designer/QGCComboBox.cc \
src/ui/designer/QGCToolWidgetComboBox.cc \
src/ui/designer/QGCTextLabel.cc \
src/ui/designer/QGCRadioChannelDisplay.cpp \
src/ui/designer/QGCXYPlot.cc \
......@@ -614,8 +615,8 @@ SOURCES += \
src/uas/QGCUASWorker.cc \
src/CmdLineOptParser.cc \
src/uas/QGXPX4UAS.cc \
src/QGCFileDialog.cc
src/QGCFileDialog.cc \
src/QGCComboBox.cc
#
# Unit Test specific configuration goes here
......@@ -652,7 +653,8 @@ HEADERS += \
src/qgcunittest/QGCUASFileManagerTest.h \
src/qgcunittest/PX4RCCalibrationTest.h \
src/qgcunittest/LinkManagerTest.h \
src/qgcunittest/MainWindowTest.h
src/qgcunittest/MainWindowTest.h \
src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h
SOURCES += \
src/qgcunittest/UnitTest.cc \
......@@ -672,7 +674,8 @@ SOURCES += \
src/qgcunittest/QGCUASFileManagerTest.cc \
src/qgcunittest/PX4RCCalibrationTest.cc \
src/qgcunittest/LinkManagerTest.cc \
src/qgcunittest/MainWindowTest.cc
src/qgcunittest/MainWindowTest.cc \
src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc
}
#
......@@ -682,7 +685,8 @@ FORMS += \
src/VehicleSetup/SetupView.ui \
src/VehicleSetup/SummaryPage.ui \
src/VehicleSetup/ParameterEditor.ui \
src/ui/QGCPX4VehicleConfig.ui
src/ui/QGCPX4VehicleConfig.ui \
src/AutoPilotPlugins/PX4/FlightModeConfig.ui
HEADERS+= \
src/VehicleSetup/SetupView.h \
......@@ -698,6 +702,7 @@ HEADERS+= \
src/AutoPilotPlugins/PX4/PX4Component.h \
src/AutoPilotPlugins/PX4/RadioComponent.h \
src/AutoPilotPlugins/PX4/FlightModesComponent.h \
src/AutoPilotPlugins/PX4/FlightModeConfig.h \
src/AutoPilotPlugins/PX4/AirframeComponent.h \
src/AutoPilotPlugins/PX4/SensorsComponent.h
......@@ -712,5 +717,6 @@ SOURCES += \
src/AutoPilotPlugins/PX4/PX4Component.cc \
src/AutoPilotPlugins/PX4/RadioComponent.cc \
src/AutoPilotPlugins/PX4/FlightModesComponent.cc \
src/AutoPilotPlugins/PX4/FlightModeConfig.cc \
src/AutoPilotPlugins/PX4/AirframeComponent.cc \
src/AutoPilotPlugins/PX4/SensorsComponent.cc
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2014 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
/// @brief Flight Mode Configuration
/// @author Don Gagne <don@thegagnes.com
#include "FlightModeConfig.h"
#include "UASManager.h"
#include "QGCMessageBox.h"
const char* FlightModeConfig::_modeSwitchParam = "RC_MAP_MODE_SW";
const char* FlightModeConfig::_manualSwitchParam = "RC_MAP_ACRO_SW";
const char* FlightModeConfig::_assistSwitchParam = "RC_MAP_POSCTL_SW";
const char* FlightModeConfig::_returnSwitchParam = "RC_MAP_RETURN_SW";
const char* FlightModeConfig::_loiterSwitchParam = "RC_MAP_LOITER_SW";
FlightModeConfig::FlightModeConfig(QWidget *parent) :
QWidget(parent),
_mav(NULL),
_paramMgr(NULL),
_ui(new Ui::FlightModeConfig)
{
Q_CHECK_PTR(_ui);
_ui->setupUi(this);
// Expectation is that we have an active uas, it is connected and the parameters are ready
_mav = UASManager::instance()->getActiveUAS();
Q_ASSERT(_mav);
_paramMgr = _mav->getParamManager();
Q_ASSERT(_paramMgr);
Q_ASSERT(_paramMgr->parametersReady());
_componentId = _paramMgr->getDefaultComponentId();
connect(_paramMgr, SIGNAL(parameterUpdated(int, QString, QVariant)), this, SLOT(_parameterUpdated(int, QString, QVariant)));
_initUi();
}
/// @brief Intialize the UI elements
void FlightModeConfig::_initUi(void) {
struct Combo2Param {
QGCComboBox* combo;
QButtonGroup* buttonGroup;
const char* param;
const char* description;
};
struct Combo2Param rgCombo2ParamMap[] = {
{ _ui->modeSwitchChannel, _ui->modeSwitchGroup, _modeSwitchParam, "Mode Switch" },
{ _ui->manualSwitchChannel, _ui->manualSwitchGroup, _manualSwitchParam, "Manual Switch" },
{ _ui->assistSwitchChannel, _ui->assistSwitchGroup, _assistSwitchParam, "Assist Switch" },
{ _ui->returnSwitchChannel, _ui->returnSwitchGroup, _returnSwitchParam, "Return Switch" },
{ _ui->loiterSwitchChannel, _ui->loiterSwitchGroup, _loiterSwitchParam, "Loiter Switch" },
{ NULL, NULL, "RC_MAP_THROTTLE", "Throttle" },
{ NULL, NULL, "RC_MAP_YAW", "Rudder" },
{ NULL, NULL, "RC_MAP_ROLL", "Aileron" },
{ NULL, NULL, "RC_MAP_PITCH", "Elevator" },
{ NULL, NULL, "RC_MAP_AUX1", "Aux1" },
{ NULL, NULL, "RC_MAP_AUX2", "Aux2" },
{ NULL, NULL, "RC_MAP_AUX3", "Aux3" }
};
// Setup up maps
for (size_t i=0; i<sizeof(rgCombo2ParamMap)/sizeof(rgCombo2ParamMap[0]); i++) {
struct Combo2Param* map = &rgCombo2ParamMap[i];
if (map->combo) {
Q_ASSERT(!_mapChannelCombo2Param.contains(map->combo));
_mapChannelCombo2Param[map->combo] = map->param;
Q_ASSERT(!_mapChannelCombo2ButtonGroup.contains(map->combo));
_mapChannelCombo2ButtonGroup[map->combo] = map->buttonGroup;
// We connect to activated because we only want signals from user initiated changes
connect(map->combo, SIGNAL(activated(int)), this, SLOT(_channelSelected(int)));
}
Q_ASSERT(!_mapParam2FunctionInfo.contains(map->param));
_mapParam2FunctionInfo[map->param] = map->description;
}
_updateAllSwitches();
// Finally if RC Calibration has not been performed disable the entire widget and inform user
if (_getChannelMapForParam(_modeSwitchParam) == 0) {
// FIXME: Do something more than disable
setEnabled(false);
}
}
void FlightModeConfig::_updateAllSwitches(void)
{
foreach(QGCComboBox* combo, _mapChannelCombo2Param.keys()) {
_updateSwitchUiGroup(combo);
}
}
/// @brief Selects the channel in the combo for the given parameter
void FlightModeConfig::_selectChannelForParam(QGCComboBox* combo, const QString& parameter)
{
int channel = _getChannelMapForParam(parameter);
int index = combo->findData(channel);
Q_ASSERT(index != -1);
combo->setCurrentIndex(index);
}
/// @brief Fills a channel selection combo box with channel values
void FlightModeConfig::_updateSwitchUiGroup(QGCComboBox* combo)
{
QMap<int, QString> mapChannelUsed2Description;
// Make a map of all channels currently mapped
foreach(QString paramId, _mapParam2FunctionInfo.keys()) {
int channel = _getChannelMapForParam(paramId);
if (channel != 0) {
mapChannelUsed2Description[channel] = _mapParam2FunctionInfo[paramId];
}
}
combo->clear();
combo->addItem("Disabled", 0);
for (int i=1; i<=_maxChannels; i++) {
QString comboText = QString("Channel %1").arg(i);
if (mapChannelUsed2Description.contains(i)) {
comboText += QString(" (%1)").arg(mapChannelUsed2Description[i]);
}
combo->addItem(comboText, i);
}
_selectChannelForParam(combo, _mapChannelCombo2Param[combo]);
// Enable/Disable radio buttons appropriately
int channelMap = _getChannelMapForParam(_mapChannelCombo2Param[combo]);
Q_ASSERT(_mapChannelCombo2ButtonGroup.contains(combo));
Q_ASSERT(_mapChannelCombo2ButtonGroup[combo]->buttons().count() > 0);
foreach(QAbstractButton* button, _mapChannelCombo2ButtonGroup[combo]->buttons()) {
QRadioButton* radio = qobject_cast<QRadioButton*>(button);
Q_ASSERT(radio);
radio->setEnabled(channelMap != 0);
}
}
/// @brief Returns channel mapping for the specified parameters
int FlightModeConfig::_getChannelMapForParam(const QString& paramId)
{
QVariant value;
bool found = _paramMgr->getParameterValue(_componentId, paramId, value);
Q_UNUSED(found);
Q_ASSERT(found);
bool conversionOk;
int channel = value.toInt(&conversionOk);
Q_UNUSED(conversionOk);
Q_ASSERT(conversionOk);
return channel;
}
/// @brief Called when a selection occurs in one of the channel combo boxes
void FlightModeConfig::_channelSelected(int requestedMapping)
{
QGCComboBox* combo = qobject_cast<QGCComboBox*>(sender());
Q_ASSERT(combo);
Q_ASSERT(_mapChannelCombo2Param.contains(combo));
// Check for no-op
int currentMapping = _getChannelMapForParam(_mapChannelCombo2Param[combo]);
if (currentMapping == requestedMapping) {
return;
}
// Make sure that channel is not already mapped
if (requestedMapping == 0) {
// Channel was disabled
if (combo == _ui->modeSwitchChannel) {
// Mode switch is not allowed to be disabled.
QGCMessageBox::warning(tr("Flight Mode"), "Mode Switch is a required switch and cannot be disabled.");
_selectChannelForParam(combo, _mapChannelCombo2Param[combo]);
return;
}
} else {
// Channel was remapped to a non-disabled channel
foreach(QString paramId, _mapParam2FunctionInfo.keys()) {
int usedChannelMap = _getChannelMapForParam(paramId);
if (requestedMapping == usedChannelMap) {
QGCMessageBox::warning(tr("Flight Mode"), "You cannot use a channel which is already mapped.");
_selectChannelForParam(combo, _mapChannelCombo2Param[combo]);
return;
}
}
}
// Save the new setting to the parameter
_paramMgr->setParameter(_componentId, _mapChannelCombo2Param[combo], requestedMapping);
_paramMgr->sendPendingParameters(true, false);
}
void FlightModeConfig::_parameterUpdated(int componentId, QString parameter, QVariant value)
{
Q_UNUSED(componentId);
Q_UNUSED(value);
// If this is a function mapping parameter update the combos
if (_mapParam2FunctionInfo.contains(parameter)) {
_updateAllSwitches();
}
// Finally if RC Calibration suddenly needs to be re-done disable the entire widget and inform user
if (parameter == _modeSwitchParam) {
// FIXME: Do something more than disable
if (isEnabled() == false && value.toInt() != 0) {
setEnabled(true);
} else if (isEnabled() == true && value.toInt() == 0) {
setEnabled(false);
}
}
}
\ No newline at end of file
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009, 2014 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
/// @brief Flight Mode Configuration
/// @author Don Gagne <don@thegagnes.com
#ifndef FlightModeConfig_H
#define FlightModeConfig_H
#include <QWidget>
#include "UASInterface.h"
#include "ui_FlightModeConfig.h"
namespace Ui {
class FlightModeConfig;
}
class FlightModeConfig : public QWidget
{
Q_OBJECT
friend class FlightModeConfigTest; ///< This allows our unit test to access internal information needed.
public:
explicit FlightModeConfig(QWidget *parent = 0);
private slots:
void _channelSelected(int index);
void _parameterUpdated(int componentId, QString parameter, QVariant value);
private:
void _initUi(void);
void _updateSwitchUiGroup(QGCComboBox* combo);
void _selectChannelForParam(QGCComboBox* combo, const QString& parameter);
void _updateAllSwitches(void);
int _getChannelMapForParam(const QString& parameter);
UASInterface* _mav; ///< The current MAV
int _componentId; ///< Default component id
QGCUASParamManagerInterface* _paramMgr;
static const int _maxChannels = 18;
QMap<QGCComboBox*, QString> _mapChannelCombo2Param;
QMap<QGCComboBox*, QButtonGroup*> _mapChannelCombo2ButtonGroup;
QMap<QString, QString> _mapParam2FunctionInfo;
static const char* _modeSwitchParam;
static const char* _manualSwitchParam;
static const char* _assistSwitchParam;
static const char* _returnSwitchParam;
static const char* _loiterSwitchParam;
Ui::FlightModeConfig* _ui;
};
#endif
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>FlightModeConfig</class>
<widget class="QWidget" name="FlightModeConfig">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>831</width>
<height>1286</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label_8">
<property name="font">
<font>
<pointsize>22</pointsize>
</font>
</property>
<property name="text">
<string>This implementation is a work in progress. Visuals are meant to be functional only. Active display of switch positions is not yet implemented. If this entire screen is disabled, you must do a Radio Calibration before settings Flight Modes.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="modeSelectLayout">
<item>
<widget class="QRadioButton" name="multiChannelMode">
<property name="text">
<string>Flight Modes on multiple channels</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">modeSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="singleChannelMode">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Flight Modes on single channel (coming soon)</string>
</property>
<attribute name="buttonGroup">
<string notr="true">modeSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
</property>
<widget class="QWidget" name="singleChannelWidget"/>
<widget class="QWidget" name="multiChannelWidget">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="label_10">
<property name="text">
<string>Main Mode Switch</string>
</property>
</widget>
</item>
<item>
<widget class="QGCComboBox" name="modeSwitchChannel"/>
</item>
<item>
<widget class="QLabel" name="label_11">
<property name="text">
<string>Current switch position:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="manualMode">
<property name="text">
<string>Manual Mode</string>
</property>
<attribute name="buttonGroup">
<string notr="true">mainSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="assistMode">
<property name="text">
<string>Assist Mode</string>
</property>
<attribute name="buttonGroup">
<string notr="true">mainSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="missionMode">
<property name="text">
<string>Mission Mode</string>
</property>
<attribute name="buttonGroup">
<string notr="true">mainSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>Manual Mode Switch</string>
</property>
</widget>
</item>
<item>
<widget class="QGCComboBox" name="manualSwitchChannel"/>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="text">
<string>Current switch position:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="manualAcro">
<property name="text">
<string>Acro</string>
</property>
<attribute name="buttonGroup">
<string notr="true">manualSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="manualManual">
<property name="text">
<string>Manual</string>
</property>
<attribute name="buttonGroup">
<string notr="true">manualSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="manualStabilize">
<property name="text">
<string>Stabilize</string>
</property>
<attribute name="buttonGroup">
<string notr="true">manualSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QLabel" name="label_7">
<property name="text">
<string>Assist Mode Switch</string>
</property>
</widget>
</item>
<item>
<widget class="QGCComboBox" name="assistSwitchChannel"/>
</item>
<item>
<widget class="QRadioButton" name="altitudeAssist">
<property name="text">
<string>Altitude Control</string>
</property>
<attribute name="buttonGroup">
<string notr="true">assistSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="positionAssist">
<property name="text">
<string>Position Control</string>
</property>
<attribute name="buttonGroup">
<string notr="true">assistSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="loiterReturnLayout">
<item>
<layout class="QVBoxLayout" name="loiterLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Loiter Switch</string>
</property>
</widget>
</item>
<item>
<widget class="QGCComboBox" name="loiterSwitchChannel"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Current switch position</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="loiterOn">
<property name="text">
<string>Loiter</string>
</property>
<attribute name="buttonGroup">
<string notr="true">loiterSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="loiterOff">
<property name="text">
<string>Off</string>
</property>
<attribute name="buttonGroup">
<string notr="true">loiterSwitchGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QVBoxLayout" name="returnLayout">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Return to Home Switch</string>
</property>
</widget>
</item>
<item>
<widget class="QGCComboBox" name="returnSwitchChannel"/>
</item>
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Current switch position</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="returnOn">
<property name="text">
<string>Return to Home</string>
</property>
<attribute name="buttonGroup">
<string notr="true">returnSwitchGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="returnOff">
<property name="text">
<string>Off</string>
</property>
<attribute name="buttonGroup">
<string notr="true">returnSwitchGroup</string>
</attribute>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QGCComboBox</class>
<extends>QComboBox</extends>
<header>QGCComboBox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="returnSwitchGroup"/>
<buttongroup name="modeSwitchGroup"/>
<buttongroup name="mainSwitchGroup"/>
<buttongroup name="loiterSwitchGroup"/>
<buttongroup name="manualSwitchGroup"/>
<buttongroup name="assistSwitchGroup"/>
</buttongroups>
</ui>
......@@ -25,9 +25,7 @@
/// @author Don Gagne <don@thegagnes.com>
#include "FlightModesComponent.h"
#include <QVBoxLayout>
#include <QLabel>
#include "FlightModeConfig.h"
/// @brief Parameters which signal a change in setupComplete state
static const char* triggerParams[] = { "RC_MAP_MODE_SW", NULL };
......@@ -115,13 +113,7 @@ QStringList FlightModesComponent::paramFilterList(void) const
QWidget* FlightModesComponent::setupWidget(void) const
{
QWidget* widget = new QWidget;
QVBoxLayout* layout = new QVBoxLayout(widget);
QLabel* label = new QLabel("Coming Soon\nUse Radio setup for Main Mode Switch setup\n(Use Flight Modes Parameters for everything else)", widget);
label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
layout->addWidget(label);
return widget;
return new FlightModeConfig();
}
QList<QStringList> FlightModesComponent::summaryItems(void) const
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 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/>.
======================================================================*/
#include "FlightModeConfigTest.h"
#include "UASManager.h"
#include "MockQGCUASParamManager.h"
#include "QGCMessageBox.h"
/// @file
/// @brief FlightModeConfig Widget unit test
///
/// @author Don Gagne <don@thegagnes.com>
UT_REGISTER_TEST(FlightModeConfigTest)
FlightModeConfigTest::FlightModeConfigTest(void) :
_mockUASManager(NULL),
_paramMgr(NULL),
_configWidget(NULL),
_defaultComponentId(0)
{
}
void FlightModeConfigTest::initTestCase(void)
{
}
void FlightModeConfigTest::init(void)
{
UnitTest::init();
_mockUASManager = new MockUASManager();
Q_ASSERT(_mockUASManager);
UASManager::setMockUASManager(_mockUASManager);
_mockUAS = new MockUAS();
Q_CHECK_PTR(_mockUAS);
_paramMgr = _mockUAS->getParamManager();
_defaultComponentId = _paramMgr->getDefaultComponentId();
_mockUASManager->setMockActiveUAS(_mockUAS);
struct SetupParamInfo {
const char* param;
int mappedChannel;
};
struct SetupParamInfo rgSetupParamInfo[] = {
{ "RC_MAP_MODE_SW", 5 },
{ "RC_MAP_ACRO_SW", 0 },
{ "RC_MAP_POSCTL_SW", 0 },
{ "RC_MAP_RETURN_SW", 0 },
{ "RC_MAP_LOITER_SW", 0 },
{ "RC_MAP_THROTTLE", 1 },
{ "RC_MAP_YAW", 2 },
{ "RC_MAP_ROLL", 3 },
{ "RC_MAP_PITCH", 4 },
{ "RC_MAP_AUX1", 0 },
{ "RC_MAP_AUX2", 0 },
{ "RC_MAP_AUX3", 0 }
};
size_t crgSetupParamInfo = sizeof(rgSetupParamInfo) / sizeof(rgSetupParamInfo[0]);
// Set initial parameter values
for (size_t i=0; i<crgSetupParamInfo; i++) {
struct SetupParamInfo* info = &rgSetupParamInfo[i];
_paramMgr->setParameter(_defaultComponentId, info->param, info->mappedChannel);
}
_configWidget = new FlightModeConfig;
Q_CHECK_PTR(_configWidget);
_configWidget->setVisible(true);
// Setup combo maps
struct SetupComboInfo {
QGCComboBox* combo;
QButtonGroup* buttonGroup;
const char* param;
};
struct SetupComboInfo rgSetupComboInfo[] = {
{ _configWidget->_ui->modeSwitchChannel, _configWidget->_ui->modeSwitchGroup, "RC_MAP_MODE_SW" },
{ _configWidget->_ui->manualSwitchChannel, _configWidget->_ui->manualSwitchGroup, "RC_MAP_ACRO_SW" },
{ _configWidget->_ui->assistSwitchChannel, _configWidget->_ui->assistSwitchGroup, "RC_MAP_POSCTL_SW" },
{ _configWidget->_ui->returnSwitchChannel, _configWidget->_ui->returnSwitchGroup, "RC_MAP_RETURN_SW" },
{ _configWidget->_ui->loiterSwitchChannel, _configWidget->_ui->loiterSwitchGroup, "RC_MAP_LOITER_SW" }
};
size_t crgSetupComboInfo = sizeof(rgSetupComboInfo) / sizeof(rgSetupComboInfo[0]);
Q_ASSERT(_mapChannelCombo2Param.count() == 0);
for (size_t i=0; i<crgSetupComboInfo; i++) {
struct SetupComboInfo* info = &rgSetupComboInfo[i];
Q_ASSERT(!_mapChannelCombo2Param.contains(info->combo));
_mapChannelCombo2Param[info->combo] = info->param;
Q_ASSERT(!_mapChannelCombo2ButtonGroup.contains(info->combo));
_mapChannelCombo2ButtonGroup[info->combo] = info->buttonGroup;
}
}
void FlightModeConfigTest::cleanup(void)
{
UnitTest::cleanup();
Q_ASSERT(_configWidget);
delete _configWidget;
Q_ASSERT(_mockUAS);
delete _mockUAS;
UASManager::setMockUASManager(NULL);
Q_ASSERT(_mockUASManager);
delete _mockUASManager;
_mapChannelCombo2Param.clear();
}
/// @brief Returns channel mapping for the specified parameters
int FlightModeConfigTest::_getChannelMapForParam(const QString& paramId)
{
QVariant value;
bool found = _paramMgr->getParameterValue(_defaultComponentId, paramId, value);
Q_UNUSED(found);
Q_ASSERT(found);
bool conversionOk;
int channel = value.toInt(&conversionOk);
Q_UNUSED(conversionOk);
Q_ASSERT(conversionOk);
return channel;
}
void FlightModeConfigTest::_create_test(void)
{
// This just test widget creation which is handled in init
}
void FlightModeConfigTest::_validateInitialState_test(void)
{
// Make sure that the combo boxes are set to the correct selections
foreach(QGCComboBox* combo, _mapChannelCombo2Param.keys()) {
QVariant value;
Q_ASSERT(_mapChannelCombo2Param.contains(combo));
int expectedMappedChannel = _getChannelMapForParam(_mapChannelCombo2Param[combo]);
int currentIndex = combo->currentIndex();
QVERIFY(currentIndex != -1);
int comboMappedChannel = combo->itemData(currentIndex).toInt();
QCOMPARE(comboMappedChannel, expectedMappedChannel);
}
QGCComboBox* combo = _configWidget->_ui->modeSwitchChannel;
// combo should have entry for each channel plus disabled
QCOMPARE(combo->count(), _availableChannels + 1);
for (int i=0; i<combo->count(); i++) {
// Combo data equates to channel mapping value. These are one-base channel values with
// 0 meaning disabled. The channel should match the index.
QCOMPARE(i, combo->itemData(i).toInt());
// Channels which are mapped are displayed after the channel number in the form "(name)".
// Check for the ending parentheses to indicate mapped channels
QString text = combo->itemText(i);
if (text.endsWith(")")) {
// Channels 1-5 are mapped
QVERIFY(i >= 1 && i <= 5);
} else {
QVERIFY(!(i >= 1 && i <= 5));
}
}
}
void FlightModeConfigTest::_validateRCCalCheck_test(void)
{
// Mode switch mapped is used to signal RC Calibration not complete. You must complete RC Cal before
// doing Flight Mode Config.
// Set mode switch mapping to not set
Q_ASSERT(_mapChannelCombo2Param.contains(_configWidget->_ui->modeSwitchChannel));
_paramMgr->setParameter(_defaultComponentId, _mapChannelCombo2Param[_configWidget->_ui->modeSwitchChannel], 0);
// Widget should disable
QCOMPARE(_configWidget->isEnabled(), false);
// Set mode switch mapping to mapped
Q_ASSERT(_mapChannelCombo2Param.contains(_configWidget->_ui->modeSwitchChannel));
_paramMgr->setParameter(_defaultComponentId, _mapChannelCombo2Param[_configWidget->_ui->modeSwitchChannel], 5);
// Widget should re-enable
QCOMPARE(_configWidget->isEnabled(), true);
}
void FlightModeConfigTest::_attempChannelReuse_test(void)
{
// Attempt to select a channel that is already in use for a switch mapping
foreach(QGCComboBox* combo, _mapChannelCombo2Param.keys()) {
Q_ASSERT(_mapChannelCombo2Param.contains(combo));
int beforeMapping = _getChannelMapForParam(_mapChannelCombo2Param[combo]);
setExpectedMessageBox(QMessageBox::Ok);
// Channel 1 mapped to throttle. This should pop a message box and the parameter value should
// not change.
combo->simulateUserSetCurrentIndex(1);
checkExpectedMessageBox();
int afterMapping = _getChannelMapForParam(_mapChannelCombo2Param[combo]);
QCOMPARE(beforeMapping, afterMapping);
}
}
void FlightModeConfigTest::_validateRadioButtonEnableDisable_test(void)
{
// Radio button should enable/disable according to selection
foreach(QGCComboBox* combo, _mapChannelCombo2Param.keys()) {
// Mode switch can't be disabled
if (combo == _configWidget->_ui->modeSwitchChannel) {
continue;
}
// Index 0 is disabled
combo->simulateUserSetCurrentIndex(0);
Q_ASSERT(_mapChannelCombo2ButtonGroup.contains(combo));
QButtonGroup* buttonGroup = _mapChannelCombo2ButtonGroup[combo];
foreach(QAbstractButton* button, buttonGroup->buttons()) {
QRadioButton* radio = qobject_cast<QRadioButton*>(button);
Q_ASSERT(radio);
QCOMPARE(radio->isEnabled(), false);
}
// Index 6 should be a valid mapping
combo->simulateUserSetCurrentIndex(6);
foreach(QAbstractButton* button, buttonGroup->buttons()) {
QRadioButton* radio = qobject_cast<QRadioButton*>(button);
Q_ASSERT(radio);
QCOMPARE(radio->isEnabled(), true);
}
// Set back to disabled for next iteration through loop. Otherwise we'll error out
// with the channel already in use
combo->simulateUserSetCurrentIndex(0);
}
}
void FlightModeConfigTest::_validateModeSwitchMustBeEnabled_test(void)
{
QGCComboBox* modeCombo = _configWidget->_ui->modeSwitchChannel;
Q_ASSERT(_mapChannelCombo2Param.contains(modeCombo));
int beforeMapping = _getChannelMapForParam(_mapChannelCombo2Param[modeCombo]);
setExpectedMessageBox(QMessageBox::Ok);
// Index 0 is disabled. This should pop a message box and the parameter value should
// not change.
modeCombo->simulateUserSetCurrentIndex(0);
checkExpectedMessageBox();
int afterMapping = _getChannelMapForParam(_mapChannelCombo2Param[modeCombo]);
QCOMPARE(beforeMapping, afterMapping);
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 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/>.
======================================================================*/
#ifndef FlightModeConfigTest_H
#define FlightModeConfigTest_H
#include "UnitTest.h"
#include "MockUASManager.h"
#include "MockUAS.h"
#include "AutoPilotPlugins/PX4/FlightModeConfig.h"
/// @file
/// @brief FlightModeConfig Widget unit test
///
/// @author Don Gagne <don@thegagnes.com>
class FlightModeConfigTest : public UnitTest
{
Q_OBJECT
public:
FlightModeConfigTest(void);
private slots:
void initTestCase(void);
void init(void);
void cleanup(void);
void _create_test(void);
void _validateInitialState_test(void);
void _validateRCCalCheck_test(void);
void _attempChannelReuse_test(void);
void _validateRadioButtonEnableDisable_test(void);
void _validateModeSwitchMustBeEnabled_test(void);
private:
int _getChannelMapForParam(const QString& parameter);
MockUASManager* _mockUASManager;
MockUAS* _mockUAS;
QGCUASParamManagerInterface* _paramMgr;
FlightModeConfig* _configWidget;
int _defaultComponentId;
QMap<QGCComboBox*, QString> _mapChannelCombo2Param;
QMap<QGCComboBox*, QButtonGroup*> _mapChannelCombo2ButtonGroup;
static const int _availableChannels = 18; ///< Simulate 18 channel RC Transmitter
};
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment