LP-311 Remove basic/advanced stabilization tab auto-switch (autotune/txpid lock issues)
[librepilot.git] / ground / gcs / src / plugins / config / configoutputwidget.cpp
blob82ac86891cbfab1afd10990fa71fa9718de9be36
1 /**
2 ******************************************************************************
4 * @file configoutputwidget.cpp
5 * @author The LibrePilot Project, http://www.librepilot.org Copyright (C) 2015.
6 * E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
7 * @addtogroup GCSPlugins GCS Plugins
8 * @{
9 * @addtogroup ConfigPlugin Config Plugin
10 * @{
11 * @brief Servo output configuration panel for the config gadget
12 *****************************************************************************/
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with this program; if not, write to the Free Software Foundation, Inc.,
26 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "configoutputwidget.h"
31 #include "ui_output.h"
32 #include "ui_outputchannelform.h"
34 #include "outputchannelform.h"
35 #include "configvehicletypewidget.h"
37 #include "uavsettingsimportexport/uavsettingsimportexportfactory.h"
38 #include <extensionsystem/pluginmanager.h>
39 #include <coreplugin/generalsettings.h>
41 #include "mixersettings.h"
42 #include "actuatorcommand.h"
43 #include "actuatorsettings.h"
44 #include "flightmodesettings.h"
45 #include "flightstatus.h"
46 #include "systemsettings.h"
48 #include <QDebug>
49 #include <QStringList>
50 #include <QWidget>
51 #include <QTextEdit>
52 #include <QVBoxLayout>
53 #include <QPushButton>
54 #include <QMessageBox>
55 #include <QDesktopServices>
56 #include <QUrl>
58 ConfigOutputWidget::ConfigOutputWidget(QWidget *parent) : ConfigTaskWidget(parent)
60 m_ui = new Ui_OutputWidget();
61 m_ui->setupUi(this);
63 m_ui->gvFrame->setVisible(false);
65 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
66 Core::Internal::GeneralSettings *settings = pm->getObject<Core::Internal::GeneralSettings>();
67 if (!settings->useExpertMode()) {
68 m_ui->saveRCOutputToRAM->setVisible(false);
71 UAVSettingsImportExportFactory *importexportplugin = pm->getObject<UAVSettingsImportExportFactory>();
72 connect(importexportplugin, SIGNAL(importAboutToBegin()), this, SLOT(stopTests()));
74 connect(m_ui->channelOutTest, SIGNAL(clicked(bool)), this, SLOT(runChannelTests(bool)));
76 // Configure the task widget
77 // Connect the help button
78 connect(m_ui->outputHelp, SIGNAL(clicked()), this, SLOT(openHelp()));
80 addApplySaveButtons(m_ui->saveRCOutputToRAM, m_ui->saveRCOutputToSD);
82 // Track the ActuatorSettings object
83 addUAVObject("ActuatorSettings");
85 // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10.
86 // Register for ActuatorSettings changes:
87 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
88 OutputChannelForm *form = new OutputChannelForm(i, this);
89 form->moveTo(*(m_ui->channelLayout));
91 connect(m_ui->channelOutTest, SIGNAL(toggled(bool)), form, SLOT(enableChannelTest(bool)));
92 connect(form, SIGNAL(channelChanged(int, int)), this, SLOT(sendChannelTest(int, int)));
94 addWidget(form->ui->actuatorMin);
95 addWidget(form->ui->actuatorNeutral);
96 addWidget(form->ui->actuatorMax);
97 addWidget(form->ui->actuatorRev);
98 addWidget(form->ui->actuatorLink);
102 // Associate the buttons with their UAVO fields
103 addWidget(m_ui->spinningArmed);
104 connect(m_ui->spinningArmed, SIGNAL(clicked(bool)), this, SLOT(updateSpinStabilizeCheckComboBoxes()));
106 addUAVObject("FlightModeSettings");
107 addWidgetBinding("FlightModeSettings", "AlwaysStabilizeWhenArmedSwitch", m_ui->alwaysStabilizedSwitch);
109 connect(FlightStatus::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateAlwaysStabilizeStatus()));
111 MixerSettings *mixer = MixerSettings::GetInstance(getObjectManager());
112 Q_ASSERT(mixer);
113 m_banks << OutputBankControls(mixer, m_ui->chBank1, QColor("#C6ECAE"), m_ui->cb_outputRate1, m_ui->cb_outputMode1);
114 m_banks << OutputBankControls(mixer, m_ui->chBank2, QColor("#91E5D3"), m_ui->cb_outputRate2, m_ui->cb_outputMode2);
115 m_banks << OutputBankControls(mixer, m_ui->chBank3, QColor("#FCEC52"), m_ui->cb_outputRate3, m_ui->cb_outputMode3);
116 m_banks << OutputBankControls(mixer, m_ui->chBank4, QColor("#C3A8FF"), m_ui->cb_outputRate4, m_ui->cb_outputMode4);
117 m_banks << OutputBankControls(mixer, m_ui->chBank5, QColor("#F7F7F2"), m_ui->cb_outputRate5, m_ui->cb_outputMode5);
118 m_banks << OutputBankControls(mixer, m_ui->chBank6, QColor("#FF9F51"), m_ui->cb_outputRate6, m_ui->cb_outputMode6);
120 QList<int> rates;
121 rates << 50 << 60 << 125 << 165 << 270 << 330 << 400 << 490;
122 int i = 0;
123 foreach(OutputBankControls controls, m_banks) {
124 addWidget(controls.rateCombo());
126 controls.rateCombo()->addItem(tr("-"), QVariant(0));
127 controls.rateCombo()->model()->setData(controls.rateCombo()->model()->index(0, 0), QVariant(0), Qt::UserRole - 1);
128 foreach(int rate, rates) {
129 controls.rateCombo()->addItem(tr("%1 Hz").arg(rate), rate);
132 addWidgetBinding("ActuatorSettings", "BankMode", controls.modeCombo(), i++, 0, true);
133 connect(controls.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
136 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
137 connect(systemAlarmsObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateWarnings(UAVObject *)));
139 disconnect(this, SLOT(refreshWidgetsValues(UAVObject *)));
141 populateWidgets();
142 refreshWidgetsValues();
144 updateEnableControls();
147 ConfigOutputWidget::~ConfigOutputWidget()
149 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
151 disconnect(systemAlarmsObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateWarnings(UAVObject *)));
152 foreach(OutputBankControls controls, m_banks) {
153 disconnect(controls.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
157 void ConfigOutputWidget::enableControls(bool enable)
159 ConfigTaskWidget::enableControls(enable);
161 if (!enable) {
162 m_ui->channelOutTest->setChecked(false);
164 m_ui->channelOutTest->setEnabled(enable);
168 Force update all channels with the values in the OutputChannelForms.
170 void ConfigOutputWidget::sendAllChannelTests()
172 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
173 OutputChannelForm *form = getOutputChannelForm(i);
174 sendChannelTest(i, form->neutral());
179 Toggles the channel testing mode by making the GCS take over
180 the ActuatorCommand objects
182 void ConfigOutputWidget::runChannelTests(bool state)
184 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
185 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
187 if (state && systemAlarms.Alarm[SystemAlarms::ALARM_ACTUATOR] != SystemAlarms::ALARM_OK) {
188 QMessageBox mbox;
189 mbox.setText(QString(tr("The actuator module is in an error state. This can also occur because there are no inputs. "
190 "Please fix these before testing outputs.")));
191 mbox.setStandardButtons(QMessageBox::Ok);
192 mbox.exec();
194 // Unfortunately must cache this since callback will reoccur
195 m_accInitialData = ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
197 m_ui->channelOutTest->setChecked(false);
198 return;
201 // Confirm this is definitely what they want
202 if (state) {
203 QMessageBox mbox;
204 mbox.setText(QString(tr("This option will start your motors by the amount selected on the sliders regardless of transmitter."
205 "It is recommended to remove any blades from motors. Are you sure you want to do this?")));
206 mbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
207 int retval = mbox.exec();
208 if (retval != QMessageBox::Yes) {
209 state = false;
210 qDebug() << "Cancelled";
211 m_ui->channelOutTest->setChecked(false);
212 return;
216 ActuatorCommand *obj = ActuatorCommand::GetInstance(getObjectManager());
217 UAVObject::Metadata mdata = obj->getMetadata();
218 if (state) {
219 m_accInitialData = mdata;
220 UAVObject::SetFlightAccess(mdata, UAVObject::ACCESS_READONLY);
221 UAVObject::SetFlightTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_ONCHANGE);
222 UAVObject::SetGcsTelemetryAcked(mdata, false);
223 UAVObject::SetGcsTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_ONCHANGE);
224 mdata.gcsTelemetryUpdatePeriod = 100;
225 } else {
226 mdata = m_accInitialData; // Restore metadata
228 obj->setMetadata(mdata);
229 obj->updated();
231 // Setup the correct initial channel values when the channel testing mode is turned on.
232 if (state) {
233 sendAllChannelTests();
236 // Add info at end
237 if (!state && isDirty()) {
238 QMessageBox mbox;
239 mbox.setText(QString(tr("You may want to save your neutral settings.")));
240 mbox.setStandardButtons(QMessageBox::Ok);
241 mbox.setIcon(QMessageBox::Information);
242 mbox.exec();
246 OutputChannelForm *ConfigOutputWidget::getOutputChannelForm(const int index) const
248 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
249 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
250 if (outputChannelForm->index() == index) {
251 return outputChannelForm;
255 // no OutputChannelForm found with given index
256 return NULL;
260 * Set the label for a channel output assignement
262 void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString &str)
264 // FIXME: use signal/ slot approach
265 UAVObjectField *field = obj->getField(str);
266 QStringList options = field->getOptions();
267 int index = options.indexOf(field->getValue().toString());
269 OutputChannelForm *outputChannelForm = getOutputChannelForm(index);
271 if (outputChannelForm) {
272 outputChannelForm->setName(str);
277 Sends the channel value to the UAV to move the servo.
278 Returns immediately if we are not in testing mode
280 void ConfigOutputWidget::sendChannelTest(int index, int value)
282 if (!m_ui->channelOutTest->isChecked()) {
283 return;
286 if (index < 0 || (unsigned)index >= ActuatorCommand::CHANNEL_NUMELEM) {
287 return;
290 ActuatorCommand *actuatorCommand = ActuatorCommand::GetInstance(getObjectManager());
291 Q_ASSERT(actuatorCommand);
292 ActuatorCommand::DataFields actuatorCommandFields = actuatorCommand->getData();
293 actuatorCommandFields.Channel[index] = value;
294 actuatorCommand->setData(actuatorCommandFields);
297 void ConfigOutputWidget::setColor(QWidget *widget, const QColor color)
299 QPalette p(palette());
301 p.setColor(QPalette::Background, color);
302 p.setColor(QPalette::Base, color);
303 p.setColor(QPalette::Active, QPalette::Button, color);
304 p.setColor(QPalette::Inactive, QPalette::Button, color);
305 widget->setAutoFillBackground(true);
306 widget->setPalette(p);
309 /********************************
310 * Output settings
311 *******************************/
314 Request the current config from the board (RC Output)
316 void ConfigOutputWidget::refreshWidgetsValues(UAVObject *obj)
318 bool dirty = isDirty();
320 ConfigTaskWidget::refreshWidgetsValues(obj);
322 // Get Actuator Settings
323 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(getObjectManager());
325 Q_ASSERT(actuatorSettings);
326 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
328 // Get channel descriptions
329 QStringList channelDesc = ConfigVehicleTypeWidget::getChannelDescriptions();
331 // Initialize output forms
332 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
333 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
334 outputChannelForm->setName(channelDesc[outputChannelForm->index()]);
336 // init min,max,neutral
337 int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()];
338 int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()];
339 outputChannelForm->setRange(minValue, maxValue);
341 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
342 outputChannelForm->setNeutral(neutral);
345 // Get the SpinWhileArmed setting
346 m_ui->spinningArmed->setChecked(actuatorSettingsData.MotorsSpinWhileArmed == ActuatorSettings::MOTORSSPINWHILEARMED_TRUE);
348 for (int i = 0; i < m_banks.count(); i++) {
349 OutputBankControls controls = m_banks.at(i);
350 // Reset to all disabled
351 controls.label()->setText("-");
353 controls.rateCombo()->setEnabled(false);
354 setColor(controls.rateCombo(), palette().color(QPalette::Background));
355 controls.rateCombo()->setCurrentIndex(0);
357 controls.modeCombo()->setEnabled(false);
358 setColor(controls.modeCombo(), palette().color(QPalette::Background));
361 // Get connected board model
362 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
363 Q_ASSERT(pm);
364 UAVObjectUtilManager *utilMngr = pm->getObject<UAVObjectUtilManager>();
365 Q_ASSERT(utilMngr);
366 QStringList bankLabels;
367 QList<int> channelBanks;
369 if (utilMngr) {
370 int board = utilMngr->getBoardModel();
371 // Setup labels and combos for banks according to board type
372 if ((board & 0xff00) == 0x0400) {
373 // Coptercontrol family of boards 4 timer banks
374 bankLabels << "1 (1-3)" << "2 (4)" << "3 (5,7-8)" << "4 (6,9-10)";
375 channelBanks << 1 << 1 << 1 << 2 << 3 << 4 << 3 << 3 << 4 << 4;
376 } else if (board == 0x0903) {
377 // Revolution family of boards 6 timer banks
378 bankLabels << "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7,12)" << "6 (8-11)";
379 channelBanks << 1 << 1 << 2 << 3 << 4 << 4 << 5 << 6 << 6 << 6 << 6 << 5;
380 } else if (board == 0x0905) {
381 // Revolution Nano
382 bankLabels << "1 (1)" << "2 (2,7,11)" << "3 (3)" << "4 (4)" << "5 (5-6)" << "6 (8-10,12)";
383 channelBanks << 1 << 2 << 3 << 4 << 5 << 5 << 2 << 6 << 6 << 6 << 2 << 6;
387 int i = 0;
388 foreach(QString banklabel, bankLabels) {
389 OutputBankControls controls = m_banks.at(i);
391 controls.label()->setText(banklabel);
392 int index = controls.rateCombo()->findData(actuatorSettingsData.BankUpdateFreq[i]);
393 if (index == -1) {
394 controls.rateCombo()->addItem(tr("%1 Hz").arg(actuatorSettingsData.BankUpdateFreq[i]), actuatorSettingsData.BankUpdateFreq[i]);
396 controls.rateCombo()->setCurrentIndex(index);
397 controls.rateCombo()->setEnabled(controls.modeCombo()->currentIndex() == ActuatorSettings::BANKMODE_PWM);
398 setColor(controls.rateCombo(), controls.color());
399 controls.modeCombo()->setEnabled(true);
400 setColor(controls.modeCombo(), controls.color());
401 i++;
404 // Get Channel ranges:
405 i = 0;
406 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
407 int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()];
408 int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()];
410 outputChannelForm->setRange(minValue, maxValue);
411 if (channelBanks.count() > i) {
412 outputChannelForm->setBank(QString("%1").arg(channelBanks.at(i)));
413 outputChannelForm->setColor(m_banks.at(channelBanks.at(i++) - 1).color());
415 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
416 outputChannelForm->setNeutral(neutral);
419 updateSpinStabilizeCheckComboBoxes();
421 setDirty(dirty);
425 * Sends the config to the board, without saving to the SD card (RC Output)
427 void ConfigOutputWidget::updateObjectsFromWidgets()
429 ConfigTaskWidget::updateObjectsFromWidgets();
431 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(getObjectManager());
433 Q_ASSERT(actuatorSettings);
434 if (actuatorSettings) {
435 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
437 // Set channel ranges
438 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
439 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
440 actuatorSettingsData.ChannelMax[outputChannelForm->index()] = outputChannelForm->max();
441 actuatorSettingsData.ChannelMin[outputChannelForm->index()] = outputChannelForm->min();
442 actuatorSettingsData.ChannelNeutral[outputChannelForm->index()] = outputChannelForm->neutral();
445 // Set update rates
446 actuatorSettingsData.BankUpdateFreq[0] = m_ui->cb_outputRate1->currentData().toUInt();
447 actuatorSettingsData.BankUpdateFreq[1] = m_ui->cb_outputRate2->currentData().toUInt();
448 actuatorSettingsData.BankUpdateFreq[2] = m_ui->cb_outputRate3->currentData().toUInt();
449 actuatorSettingsData.BankUpdateFreq[3] = m_ui->cb_outputRate4->currentData().toUInt();
450 actuatorSettingsData.BankUpdateFreq[4] = m_ui->cb_outputRate5->currentData().toUInt();
451 actuatorSettingsData.BankUpdateFreq[5] = m_ui->cb_outputRate6->currentData().toUInt();
453 actuatorSettingsData.MotorsSpinWhileArmed = m_ui->spinningArmed->isChecked() ?
454 ActuatorSettings::MOTORSSPINWHILEARMED_TRUE :
455 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE;
457 // Apply settings
458 actuatorSettings->setData(actuatorSettingsData);
461 FlightModeSettings *flightModeSettings = FlightModeSettings::GetInstance(getObjectManager());
462 Q_ASSERT(flightModeSettings);
464 if (flightModeSettings) {
465 FlightModeSettings::DataFields flightModeSettingsData = flightModeSettings->getData();
466 flightModeSettingsData.AlwaysStabilizeWhenArmedSwitch = m_ui->alwaysStabilizedSwitch->currentIndex();
468 // Apply settings
469 flightModeSettings->setData(flightModeSettingsData);
473 void ConfigOutputWidget::updateSpinStabilizeCheckComboBoxes()
475 m_ui->alwayStabilizedLabel1->setEnabled(m_ui->spinningArmed->isChecked());
476 m_ui->alwayStabilizedLabel2->setEnabled(m_ui->spinningArmed->isChecked());
477 m_ui->alwaysStabilizedSwitch->setEnabled(m_ui->spinningArmed->isChecked());
479 if (!m_ui->spinningArmed->isChecked()) {
480 m_ui->alwaysStabilizedSwitch->setCurrentIndex(FlightModeSettings::ALWAYSSTABILIZEWHENARMEDSWITCH_DISABLED);
484 void ConfigOutputWidget::updateAlwaysStabilizeStatus()
486 FlightStatus *flightStatusObj = FlightStatus::GetInstance(getObjectManager());
487 FlightStatus::DataFields flightStatus = flightStatusObj->getData();
489 if (flightStatus.AlwaysStabilizeWhenArmed == FlightStatus::ALWAYSSTABILIZEWHENARMED_TRUE) {
490 m_ui->alwayStabilizedLabel2->setText("AlwaysStabilizeWhenArmed is <b>ACTIVE</b>. This prevents arming!.");
491 } else {
492 m_ui->alwayStabilizedLabel2->setText("(Really be careful!).");
496 void ConfigOutputWidget::openHelp()
498 QDesktopServices::openUrl(QUrl(QString(WIKI_URL_ROOT) + QString("Output+Configuration"),
499 QUrl::StrictMode));
502 void ConfigOutputWidget::onBankTypeChange()
504 QComboBox *bankModeCombo = qobject_cast<QComboBox *>(sender());
506 if (bankModeCombo != NULL) {
507 foreach(OutputBankControls controls, m_banks) {
508 if (controls.modeCombo() == bankModeCombo) {
509 bool enabled = bankModeCombo->currentIndex() == ActuatorSettings::BANKMODE_PWM;
510 controls.rateCombo()->setEnabled(enabled);
511 controls.rateCombo()->setCurrentIndex(enabled ? 1 : 0);
512 break;
518 void ConfigOutputWidget::stopTests()
520 m_ui->channelOutTest->setChecked(false);
523 void ConfigOutputWidget::updateWarnings(UAVObject *)
525 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
526 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
528 if (systemAlarms.Alarm[SystemAlarms::ALARM_SYSTEMCONFIGURATION] > SystemAlarms::ALARM_WARNING) {
529 switch (systemAlarms.ExtendedAlarmStatus[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION]) {
530 case SystemAlarms::EXTENDEDALARMSTATUS_UNSUPPORTEDCONFIG_ONESHOT:
531 setWarning(tr("OneShot and PWMSync output only works with Receiver Port settings marked with '+OneShot'<br>"
532 "When using Receiver Port setting 'PPM_PIN8+OneShot' "
533 "<b><font color='%1'>Bank %2</font></b> must be set to PWM")
534 .arg(m_banks.at(3).color().name()).arg(m_banks.at(3).label()->text()));
535 return;
538 setWarning(NULL);
541 void ConfigOutputWidget::setWarning(QString message)
543 m_ui->gvFrame->setVisible(!message.isNull());
544 m_ui->picWarning->setPixmap(message.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
545 m_ui->txtWarning->setText(message);
549 OutputBankControls::OutputBankControls(MixerSettings *mixer, QLabel *label, QColor color, QComboBox *rateCombo, QComboBox *modeCombo) :
550 m_mixer(mixer), m_label(label), m_color(color), m_rateCombo(rateCombo), m_modeCombo(modeCombo)
553 OutputBankControls::~OutputBankControls()