From 9d20690a80a6dc773638c3e7323bdb48b8a6f3ea Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Thu, 4 Dec 2014 14:59:34 -0800 Subject: [PATCH] FlightModeConfig plus unit test --- qgroundcontrol.pro | 24 +- src/AutoPilotPlugins/PX4/FlightModeConfig.cc | 253 +++++++++++ src/AutoPilotPlugins/PX4/FlightModeConfig.h | 81 ++++ src/AutoPilotPlugins/PX4/FlightModeConfig.ui | 392 ++++++++++++++++++ .../PX4/FlightModesComponent.cc | 12 +- .../PX4/Tests/FlightModeConfigTest.cc | 303 ++++++++++++++ .../PX4/Tests/FlightModeConfigTest.h | 71 ++++ 7 files changed, 1117 insertions(+), 19 deletions(-) create mode 100644 src/AutoPilotPlugins/PX4/FlightModeConfig.cc create mode 100644 src/AutoPilotPlugins/PX4/FlightModeConfig.h create mode 100644 src/AutoPilotPlugins/PX4/FlightModeConfig.ui create mode 100644 src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc create mode 100644 src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index bcf3fd2c5..d63cc0f6e 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -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 diff --git a/src/AutoPilotPlugins/PX4/FlightModeConfig.cc b/src/AutoPilotPlugins/PX4/FlightModeConfig.cc new file mode 100644 index 000000000..78cd1fda4 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/FlightModeConfig.cc @@ -0,0 +1,253 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009, 2014 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +/// @file +/// @brief Flight Mode Configuration +/// @author Don Gagne 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; icombo) { + 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 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(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(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 diff --git a/src/AutoPilotPlugins/PX4/FlightModeConfig.h b/src/AutoPilotPlugins/PX4/FlightModeConfig.h new file mode 100644 index 000000000..891d5ce32 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/FlightModeConfig.h @@ -0,0 +1,81 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009, 2014 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + + +/// @file +/// @brief Flight Mode Configuration +/// @author Don Gagne + +#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 _mapChannelCombo2Param; + QMap _mapChannelCombo2ButtonGroup; + QMap _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 diff --git a/src/AutoPilotPlugins/PX4/FlightModeConfig.ui b/src/AutoPilotPlugins/PX4/FlightModeConfig.ui new file mode 100644 index 000000000..c8ad74899 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/FlightModeConfig.ui @@ -0,0 +1,392 @@ + + + FlightModeConfig + + + + 0 + 0 + 831 + 1286 + + + + + 0 + 0 + + + + Form + + + + + + + 22 + + + + 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. + + + true + + + + + + + + + Flight Modes on multiple channels + + + true + + + modeSwitchGroup + + + + + + + false + + + Flight Modes on single channel (coming soon) + + + modeSwitchGroup + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + 0 + 0 + + + + 1 + + + + + + + + + + Main Mode Switch + + + + + + + + + + Current switch position: + + + + + + + Manual Mode + + + mainSwitchGroup + + + + + + + Assist Mode + + + mainSwitchGroup + + + + + + + Mission Mode + + + mainSwitchGroup + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Manual Mode Switch + + + + + + + + + + Current switch position: + + + + + + + Acro + + + manualSwitchGroup + + + + + + + Manual + + + manualSwitchGroup + + + + + + + Stabilize + + + manualSwitchGroup + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Assist Mode Switch + + + + + + + + + + Altitude Control + + + assistSwitchGroup + + + + + + + Position Control + + + assistSwitchGroup + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + Loiter Switch + + + + + + + + + + Current switch position + + + + + + + Loiter + + + loiterSwitchGroup + + + + + + + Off + + + loiterSwitchGroup + + + + + + + + + + + Return to Home Switch + + + + + + + + + + Current switch position + + + + + + + Return to Home + + + returnSwitchGroup + + + + + + + Off + + + returnSwitchGroup + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + QGCComboBox + QComboBox +
QGCComboBox.h
+
+
+ + + + + + + + + + +
diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc index bba49c0c9..d7a857a47 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponent.cc +++ b/src/AutoPilotPlugins/PX4/FlightModesComponent.cc @@ -25,9 +25,7 @@ /// @author Don Gagne #include "FlightModesComponent.h" - -#include -#include +#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 FlightModesComponent::summaryItems(void) const diff --git a/src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc b/src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc new file mode 100644 index 000000000..c5ddb7093 --- /dev/null +++ b/src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.cc @@ -0,0 +1,303 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +#include "FlightModeConfigTest.h" +#include "UASManager.h" +#include "MockQGCUASParamManager.h" +#include "QGCMessageBox.h" + +/// @file +/// @brief FlightModeConfig Widget unit test +/// +/// @author Don Gagne + +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; isetParameter(_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; icombo)); + _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; icount(); 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(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(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); +} diff --git a/src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h b/src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h new file mode 100644 index 000000000..c6cb77eea --- /dev/null +++ b/src/AutoPilotPlugins/PX4/Tests/FlightModeConfigTest.h @@ -0,0 +1,71 @@ +/*===================================================================== + + QGroundControl Open Source Ground Control Station + + (c) 2009 - 2014 QGROUNDCONTROL PROJECT + + This file is part of the QGROUNDCONTROL project + + QGROUNDCONTROL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + QGROUNDCONTROL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QGROUNDCONTROL. If not, see . + + ======================================================================*/ + +#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 + +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 _mapChannelCombo2Param; + QMap _mapChannelCombo2ButtonGroup; + + static const int _availableChannels = 18; ///< Simulate 18 channel RC Transmitter +}; + +#endif -- 2.22.0