Commit e6aaf9c6 authored by Don Gagne's avatar Don Gagne

Merge pull request #1075 from DonLakeFlyer/FlightModeSetup

Flight mode setup
parents a709b2f9 9991c153
......@@ -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
[Rules]
*Log=false
/*=====================================================================
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
This diff is collapsed.
......@@ -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
......
This diff is collapsed.
/*=====================================================================
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
/*=====================================================================
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 Subclass of QComboBox. Mainly used for unit test so you can simulate a user selection
/// with correct signalling.
///
/// @author Don Gagne <don@thegagnes.com>
#include "QGCComboBox.h"
QGCComboBox::QGCComboBox(QWidget* parent) :
QComboBox(parent)
{
}
void QGCComboBox::simulateUserSetCurrentIndex(int index)
{
Q_ASSERT(index >=0 && index < count());
// This will signal currentIndexChanged
setCurrentIndex(index);
// We have to manually signal activated
emit activated(index);
emit activated(itemText(index));
}
/*=====================================================================
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 QGCComboBox_H
#define QGCComboBox_H
#include <QComboBox>
/// @file
/// @brief Subclass of QComboBox. Mainly used for unit test so you can simulate a user selection
/// with correct signalling.
///
/// @author Don Gagne <don@thegagnes.com>
class QGCComboBox : public QComboBox {
Q_OBJECT
public:
QGCComboBox(QWidget* parent = NULL);
/// @brief Sets the current index on the combo. Signals activated, as well as currentIndexChanged.
void simulateUserSetCurrentIndex(int index);
};
#endif
......@@ -22,12 +22,16 @@
======================================================================*/
#include "MockQGCUASParamManager.h"
#include "mavlink.h"
#include <QTest>
#include <QDebug>
Q_LOGGING_CATEGORY(MockQGCUASParamManagerLog, "MockQGCUASParamManagerLog")
MockQGCUASParamManager::MockQGCUASParamManager(void)
{
_loadParams();
}
bool MockQGCUASParamManager::getParameterValue(int component, const QString& parameter, QVariant& value) const
......@@ -36,13 +40,65 @@ bool MockQGCUASParamManager::getParameterValue(int component, const QString& par
if (_mapParams.contains(parameter)) {
value = _mapParams[parameter];
return true;
}
qCDebug(MockQGCUASParamManagerLog) << QString("getParameterValue: parameter not found %1").arg(parameter);
return false;
}
void MockQGCUASParamManager::setParameter(int component, QString parameterName, QVariant value)
{
Q_UNUSED(component);
qCDebug(MockQGCUASParamManagerLog) << QString("setParameter: component(%1) parameter(%2) value(%3)").arg(component).arg(parameterName).arg(value.toString());
_mapParams[parameterName] = value;
emit parameterUpdated(_defaultComponentId, parameterName, value);
}
_mapParamsSet[parameterName] = value;
void MockQGCUASParamManager::_loadParams(void)
{
QFile paramFile(":/unittest/MockLink.param");
bool success = paramFile.open(QFile::ReadOnly);
Q_UNUSED(success);
Q_ASSERT(success);
QTextStream paramStream(&paramFile);
while (!paramStream.atEnd()) {
QString line = paramStream.readLine();
if (line.startsWith("#")) {
continue;
}
QStringList paramData = line.split("\t");
Q_ASSERT(paramData.count() == 5);
QString paramName = paramData.at(2);
QString valStr = paramData.at(3);
uint paramType = paramData.at(4).toUInt();
QVariant paramValue;
switch (paramType) {
case MAV_PARAM_TYPE_REAL32:
paramValue = QVariant(valStr.toFloat());
break;
case MAV_PARAM_TYPE_UINT32:
paramValue = QVariant(valStr.toUInt());
break;
case MAV_PARAM_TYPE_INT32:
paramValue = QVariant(valStr.toInt());
break;
case MAV_PARAM_TYPE_INT8:
paramValue = QVariant((unsigned char)valStr.toUInt());
break;
default:
Q_ASSERT(false);
break;
}
Q_ASSERT(!_mapParams.contains(paramName));
_mapParams[paramName] = paramValue;
}
}
......@@ -24,8 +24,12 @@
#ifndef MOCKQGCUASPARAMMANAGER_H
#define MOCKQGCUASPARAMMANAGER_H
#include <QLoggingCategory>