LP-551 Add enableBanks function - Refine bank freeze/unfreeze - Remove unused bankMod...
[librepilot.git] / ground / gcs / src / plugins / config / configoutputwidget.cpp
blob5a51df0944cbba83214b5d78c81639bb7ed2fd65
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 <uavobjecthelper.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 <QMessageBox>
54 // Motor settings
55 #define DSHOT_MAXOUTPUT_RANGE 2000
56 #define DSHOT_MINTOUTPUT_RANGE 0
57 #define PWMSYNC_MAXOUTPUT_RANGE 1900
58 #define DEFAULT_MAXOUTPUT_RANGE 2000
59 #define DEFAULT_MINOUTPUT_RANGE 900
60 #define DEFAULT_MINOUTPUT_VALUE 1000
61 #define REVMOTOR_NEUTRAL_TARGET_VALUE 1500
62 #define REVMOTOR_NEUTRAL_DIFF_VALUE 150
63 #define MOTOR_NEUTRAL_DIFF_VALUE 200
65 // Servo settings
66 #define SERVO_MAXOUTPUT_RANGE 2500
67 #define SERVO_MINOUTPUT_RANGE 500
68 #define SERVO_MAXOUTPUT_VALUE 2000
69 #define SERVO_MINOUTPUT_VALUE 1000
70 #define SERVO_NEUTRAL_VALUE 1500
72 ConfigOutputWidget::ConfigOutputWidget(QWidget *parent) : ConfigTaskWidget(parent)
74 m_ui = new Ui_OutputWidget();
75 m_ui->setupUi(this);
77 // must be done before auto binding !
78 setWikiURL("Output+Configuration");
80 addAutoBindings();
82 m_ui->boardWarningFrame->setVisible(false);
83 m_ui->configWarningFrame->setVisible(false);
85 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
86 UAVSettingsImportExportFactory *importexportplugin = pm->getObject<UAVSettingsImportExportFactory>();
87 connect(importexportplugin, SIGNAL(importAboutToBegin()), this, SLOT(stopTests()));
89 connect(m_ui->channelOutTest, SIGNAL(clicked(bool)), this, SLOT(runChannelTests(bool)));
91 // Configure the task widget
93 // Track the ActuatorSettings object
94 addUAVObject("ActuatorSettings");
96 // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10.
97 // Register for ActuatorSettings changes:
98 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
99 OutputChannelForm *form = new OutputChannelForm(i, this);
100 form->moveTo(*(m_ui->channelLayout));
102 connect(m_ui->channelOutTest, SIGNAL(toggled(bool)), form, SLOT(enableChannelTest(bool)));
103 connect(form, SIGNAL(channelChanged(int, int)), this, SLOT(sendChannelTest(int, int)));
105 addWidget(form->ui->actuatorMin);
106 addWidget(form->ui->actuatorNeutral);
107 addWidget(form->ui->actuatorMax);
108 addWidget(form->ui->actuatorRev);
109 addWidget(form->ui->actuatorLink);
112 // Associate the buttons with their UAVO fields
113 addWidget(m_ui->spinningArmed);
114 connect(m_ui->spinningArmed, SIGNAL(clicked(bool)), this, SLOT(updateSpinStabilizeCheckComboBoxes()));
116 addUAVObject("FlightModeSettings");
117 addWidgetBinding("FlightModeSettings", "AlwaysStabilizeWhenArmedSwitch", m_ui->alwaysStabilizedSwitch);
119 connect(FlightStatus::GetInstance(getObjectManager()), SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateAlwaysStabilizeStatus()));
121 MixerSettings *mixer = MixerSettings::GetInstance(getObjectManager());
122 Q_ASSERT(mixer);
123 m_banks << OutputBankControls(mixer, m_ui->chBank1, QColor("#C6ECAE"), m_ui->cb_outputRate1, m_ui->cb_outputMode1);
124 m_banks << OutputBankControls(mixer, m_ui->chBank2, QColor("#91E5D3"), m_ui->cb_outputRate2, m_ui->cb_outputMode2);
125 m_banks << OutputBankControls(mixer, m_ui->chBank3, QColor("#FCEC52"), m_ui->cb_outputRate3, m_ui->cb_outputMode3);
126 m_banks << OutputBankControls(mixer, m_ui->chBank4, QColor("#C3A8FF"), m_ui->cb_outputRate4, m_ui->cb_outputMode4);
127 m_banks << OutputBankControls(mixer, m_ui->chBank5, QColor("#F7F7F2"), m_ui->cb_outputRate5, m_ui->cb_outputMode5);
128 m_banks << OutputBankControls(mixer, m_ui->chBank6, QColor("#FF9F51"), m_ui->cb_outputRate6, m_ui->cb_outputMode6);
130 QList<int> rates;
131 rates << 50 << 60 << 125 << 165 << 270 << 330 << 400 << 490;
132 int i = 0;
133 foreach(OutputBankControls controls, m_banks) {
134 addWidget(controls.rateCombo());
136 controls.rateCombo()->addItem(tr("-"), QVariant(0));
137 controls.rateCombo()->model()->setData(controls.rateCombo()->model()->index(0, 0), QVariant(0), Qt::UserRole - 1);
138 foreach(int rate, rates) {
139 controls.rateCombo()->addItem(tr("%1 Hz").arg(rate), rate);
142 addWidgetBinding("ActuatorSettings", "BankMode", controls.modeCombo(), i++, 0, true);
143 connect(controls.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
146 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
147 connect(systemAlarmsObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateBoardWarnings(UAVObject *)));
149 inputCalibrationStarted = false;
150 channelTestsStarted = false;
152 // TODO why do we do that ?
153 disconnect(this, SLOT(refreshWidgetsValues(UAVObject *)));
156 ConfigOutputWidget::~ConfigOutputWidget()
158 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
160 disconnect(systemAlarmsObj, SIGNAL(objectUpdated(UAVObject *)), this, SLOT(updateBoardWarnings(UAVObject *)));
161 foreach(OutputBankControls controls, m_banks) {
162 disconnect(controls.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
166 void ConfigOutputWidget::enableControls(bool enable)
168 ConfigTaskWidget::enableControls(enable);
170 if (!enable) {
171 m_ui->channelOutTest->setChecked(false);
173 m_ui->channelOutTest->setEnabled(enable);
177 Force update all channels with the values in the OutputChannelForms.
179 void ConfigOutputWidget::sendAllChannelTests()
181 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
182 OutputChannelForm *form = getOutputChannelForm(i);
183 sendChannelTest(i, form->neutral());
188 Toggles the channel testing mode by making the GCS take over
189 the ActuatorCommand objects
191 void ConfigOutputWidget::runChannelTests(bool state)
193 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
194 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
196 if (state && systemAlarms.Alarm[SystemAlarms::ALARM_ACTUATOR] != SystemAlarms::ALARM_OK) {
197 QMessageBox mbox;
198 mbox.setText(QString(tr("The actuator module is in an error state. This can also occur because there are no inputs. "
199 "Please fix these before testing outputs.")));
200 mbox.setStandardButtons(QMessageBox::Ok);
201 mbox.exec();
203 // Unfortunately must cache this since callback will reoccur
204 m_accInitialData = ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
206 m_ui->channelOutTest->setChecked(false);
207 return;
210 // Confirm this is definitely what they want
211 if (state) {
212 QMessageBox mbox;
213 mbox.setText(QString(tr("This option will start your motors by the amount selected on the sliders regardless of transmitter."
214 "It is recommended to remove any blades from motors. Are you sure you want to do this?")));
215 mbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
216 int retval = mbox.exec();
217 if (retval != QMessageBox::Yes) {
218 state = false;
219 qDebug() << "Cancelled";
220 m_ui->channelOutTest->setChecked(false);
221 return;
225 channelTestsStarted = state;
227 enableBanks(!state);
229 ActuatorCommand *obj = ActuatorCommand::GetInstance(getObjectManager());
230 UAVObject::Metadata mdata = obj->getMetadata();
231 if (state) {
232 m_accInitialData = mdata;
233 UAVObject::SetFlightAccess(mdata, UAVObject::ACCESS_READONLY);
234 UAVObject::SetFlightTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_ONCHANGE);
235 UAVObject::SetGcsTelemetryAcked(mdata, false);
236 UAVObject::SetGcsTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_ONCHANGE);
237 mdata.gcsTelemetryUpdatePeriod = 100;
238 } else {
239 mdata = m_accInitialData; // Restore metadata
241 obj->setMetadata(mdata);
242 obj->updated();
244 // Setup the correct initial channel values when the channel testing mode is turned on.
245 if (state) {
246 sendAllChannelTests();
249 // Add info at end
250 if (!state && isDirty()) {
251 QMessageBox mbox;
252 mbox.setText(QString(tr("You may want to save your neutral settings.")));
253 mbox.setStandardButtons(QMessageBox::Ok);
254 mbox.setIcon(QMessageBox::Information);
255 mbox.exec();
259 OutputChannelForm *ConfigOutputWidget::getOutputChannelForm(const int index) const
261 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
262 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
263 if (outputChannelForm->index() == index) {
264 return outputChannelForm;
268 // no OutputChannelForm found with given index
269 return NULL;
273 * Set the label for a channel output assignement
275 void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString &str)
277 // FIXME: use signal/ slot approach
278 UAVObjectField *field = obj->getField(str);
279 QStringList options = field->getOptions();
280 int index = options.indexOf(field->getValue().toString());
282 OutputChannelForm *outputChannelForm = getOutputChannelForm(index);
284 if (outputChannelForm) {
285 outputChannelForm->setName(str);
290 Sends the channel value to the UAV to move the servo.
291 Returns immediately if we are not in testing mode
293 void ConfigOutputWidget::sendChannelTest(int index, int value)
295 if (!m_ui->channelOutTest->isChecked()) {
296 return;
299 if (index < 0 || (unsigned)index >= ActuatorCommand::CHANNEL_NUMELEM) {
300 return;
303 ActuatorCommand *actuatorCommand = ActuatorCommand::GetInstance(getObjectManager());
304 Q_ASSERT(actuatorCommand);
305 ActuatorCommand::DataFields actuatorCommandFields = actuatorCommand->getData();
306 actuatorCommandFields.Channel[index] = value;
307 actuatorCommand->setData(actuatorCommandFields);
310 void ConfigOutputWidget::setColor(QWidget *widget, const QColor color)
312 QPalette p(palette());
314 p.setColor(QPalette::Background, color);
315 p.setColor(QPalette::Base, color);
316 p.setColor(QPalette::Active, QPalette::Button, color);
317 p.setColor(QPalette::Inactive, QPalette::Button, color);
318 widget->setAutoFillBackground(true);
319 widget->setPalette(p);
322 /********************************
323 * Output settings
324 *******************************/
327 Request the current config from the board (RC Output)
329 void ConfigOutputWidget::refreshWidgetsValuesImpl(UAVObject *obj)
331 Q_UNUSED(obj);
333 // Get Actuator Settings
334 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(getObjectManager());
336 Q_ASSERT(actuatorSettings);
337 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
339 // Get channel descriptions
340 QStringList channelDesc = ConfigVehicleTypeWidget::getChannelDescriptions();
342 // Initialize output forms
343 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
344 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
345 outputChannelForm->setName(channelDesc[outputChannelForm->index()]);
347 // init min,max,neutral
348 int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()];
349 int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()];
350 outputChannelForm->setRange(minValue, maxValue);
352 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
353 outputChannelForm->setNeutral(neutral);
356 // Get the SpinWhileArmed setting
357 m_ui->spinningArmed->setChecked(actuatorSettingsData.MotorsSpinWhileArmed == ActuatorSettings::MOTORSSPINWHILEARMED_TRUE);
359 for (int i = 0; i < m_banks.count(); i++) {
360 OutputBankControls controls = m_banks.at(i);
361 // Reset to all disabled
362 controls.label()->setText("-");
364 controls.rateCombo()->setEnabled(false);
365 setColor(controls.rateCombo(), palette().color(QPalette::Background));
366 controls.rateCombo()->setCurrentIndex(0);
368 controls.modeCombo()->setEnabled(false);
369 setColor(controls.modeCombo(), palette().color(QPalette::Background));
372 // Get connected board model
373 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
374 Q_ASSERT(pm);
375 UAVObjectUtilManager *utilMngr = pm->getObject<UAVObjectUtilManager>();
376 Q_ASSERT(utilMngr);
377 QStringList bankLabels;
378 QList<int> channelBanks;
380 if (utilMngr) {
381 int board = utilMngr->getBoardModel();
382 // Setup labels and combos for banks according to board type
383 if ((board & 0xff00) == 0x0400) {
384 // Coptercontrol family of boards 4 timer banks
385 bankLabels << "1 (1-3)" << "2 (4)" << "3 (5,7-8)" << "4 (6,9-10)";
386 channelBanks << 1 << 1 << 1 << 2 << 3 << 4 << 3 << 3 << 4 << 4;
387 } else if (board == 0x0903) {
388 // Revolution family of boards 6 timer banks
389 bankLabels << "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7,12)" << "6 (8-11)";
390 channelBanks << 1 << 1 << 2 << 3 << 4 << 4 << 5 << 6 << 6 << 6 << 6 << 5;
391 } else if (board == 0x0905) {
392 // Revolution Nano
393 bankLabels << "1 (1)" << "2 (2,7,11)" << "3 (3)" << "4 (4)" << "5 (5-6)" << "6 (8-10,12)";
394 channelBanks << 1 << 2 << 3 << 4 << 5 << 5 << 2 << 6 << 6 << 6 << 2 << 6;
395 } else if (board == 0x9201) {
396 // Sparky2
397 bankLabels << "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7-8)" << "6 (9-10)";
398 channelBanks << 1 << 1 << 2 << 3 << 4 << 4 << 5 << 5 << 6 << 6;
399 } else if (board == 0x1001) {
400 // SPRacingF3
401 bankLabels << "1 (1-3,7)" << "2 (4,8)" << "3 (5)" << "4 (6)";
402 channelBanks << 1 << 1 << 1 << 2 << 3 << 4 << 1 << 2;
403 } else if (board == 0x1002 || board == 0x1003) {
404 // SPRacingF3_EVO, NucleoF303RE
405 bankLabels << "1 (1-3)" << "2 (4)" << "3 (5-8)";
406 channelBanks << 1 << 1 << 1 << 2 << 3 << 3 << 3 << 3;
407 } else if (board == 0x1005) { // PikoBLX
408 bankLabels << "1 (1-4)" << "2 (5-6)" << "3 (7)" << "4 (8)";
409 channelBanks << 1 << 1 << 1 << 1 << 2 << 2 << 3 << 4;
410 } else if (board == 0x1006) { // tinyFISH
411 bankLabels << "1 (1)" << "2 (2)" << "3 (3-4)" << "4 (5-6)";
412 channelBanks << 1 << 2 << 3 << 3 << 4 << 4;
416 // Store how many banks are active according to the board
417 activeBanksCount = bankLabels.count();
419 int i = 0;
420 foreach(QString banklabel, bankLabels) {
421 OutputBankControls controls = m_banks.at(i);
423 controls.label()->setText(banklabel);
424 int index = controls.rateCombo()->findData(actuatorSettingsData.BankUpdateFreq[i]);
425 if (index == -1) {
426 controls.rateCombo()->addItem(tr("%1 Hz").arg(actuatorSettingsData.BankUpdateFreq[i]), actuatorSettingsData.BankUpdateFreq[i]);
428 bool isPWM = (controls.modeCombo()->currentIndex() == ActuatorSettings::BANKMODE_PWM);
429 controls.rateCombo()->setCurrentIndex(index);
430 controls.rateCombo()->setEnabled(!inputCalibrationStarted && !channelTestsStarted && isPWM);
431 setColor(controls.rateCombo(), controls.color());
432 controls.modeCombo()->setEnabled(!inputCalibrationStarted && !channelTestsStarted);
433 setColor(controls.modeCombo(), controls.color());
434 i++;
437 // Get Channel ranges:
438 i = 0;
439 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
440 int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()];
441 int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()];
443 if (channelBanks.count() > i) {
444 int bankNumber = channelBanks.at(i);
445 OutputBankControls bankControls = m_banks.at(bankNumber - 1);
447 setChannelLimits(outputChannelForm, &bankControls);
449 outputChannelForm->setBank(QString::number(bankNumber));
450 outputChannelForm->setColor(bankControls.color());
452 i++;
454 outputChannelForm->setRange(minValue, maxValue);
455 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
456 outputChannelForm->setNeutral(neutral);
459 updateSpinStabilizeCheckComboBoxes();
460 checkOutputConfig();
464 * Sends the config to the board, without saving to the SD card (RC Output)
466 void ConfigOutputWidget::updateObjectsFromWidgetsImpl()
468 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(getObjectManager());
470 Q_ASSERT(actuatorSettings);
471 if (actuatorSettings) {
472 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
474 // Set channel ranges
475 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
476 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
477 actuatorSettingsData.ChannelMax[outputChannelForm->index()] = outputChannelForm->max();
478 actuatorSettingsData.ChannelMin[outputChannelForm->index()] = outputChannelForm->min();
479 actuatorSettingsData.ChannelNeutral[outputChannelForm->index()] = outputChannelForm->neutral();
482 // Set update rates
483 actuatorSettingsData.BankUpdateFreq[0] = m_ui->cb_outputRate1->currentData().toUInt();
484 actuatorSettingsData.BankUpdateFreq[1] = m_ui->cb_outputRate2->currentData().toUInt();
485 actuatorSettingsData.BankUpdateFreq[2] = m_ui->cb_outputRate3->currentData().toUInt();
486 actuatorSettingsData.BankUpdateFreq[3] = m_ui->cb_outputRate4->currentData().toUInt();
487 actuatorSettingsData.BankUpdateFreq[4] = m_ui->cb_outputRate5->currentData().toUInt();
488 actuatorSettingsData.BankUpdateFreq[5] = m_ui->cb_outputRate6->currentData().toUInt();
490 actuatorSettingsData.MotorsSpinWhileArmed = m_ui->spinningArmed->isChecked() ?
491 ActuatorSettings::MOTORSSPINWHILEARMED_TRUE :
492 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE;
494 // Apply settings
495 UAVObjectUpdaterHelper updateHelper;
496 actuatorSettings->setData(actuatorSettingsData, false);
497 updateHelper.doObjectAndWait(actuatorSettings);
500 FlightModeSettings *flightModeSettings = FlightModeSettings::GetInstance(getObjectManager());
501 Q_ASSERT(flightModeSettings);
503 if (flightModeSettings) {
504 FlightModeSettings::DataFields flightModeSettingsData = flightModeSettings->getData();
505 flightModeSettingsData.AlwaysStabilizeWhenArmedSwitch = m_ui->alwaysStabilizedSwitch->currentIndex();
507 // Apply settings
508 flightModeSettings->setData(flightModeSettingsData);
512 void ConfigOutputWidget::updateSpinStabilizeCheckComboBoxes()
514 m_ui->alwayStabilizedLabel1->setEnabled(m_ui->spinningArmed->isChecked());
515 m_ui->alwayStabilizedLabel2->setEnabled(m_ui->spinningArmed->isChecked());
516 m_ui->alwaysStabilizedSwitch->setEnabled(m_ui->spinningArmed->isChecked());
518 if (!m_ui->spinningArmed->isChecked()) {
519 m_ui->alwaysStabilizedSwitch->setCurrentIndex(FlightModeSettings::ALWAYSSTABILIZEWHENARMEDSWITCH_DISABLED);
523 void ConfigOutputWidget::updateAlwaysStabilizeStatus()
525 FlightStatus *flightStatusObj = FlightStatus::GetInstance(getObjectManager());
526 FlightStatus::DataFields flightStatus = flightStatusObj->getData();
528 if (flightStatus.AlwaysStabilizeWhenArmed == FlightStatus::ALWAYSSTABILIZEWHENARMED_TRUE) {
529 m_ui->alwayStabilizedLabel2->setText(tr("AlwaysStabilizeWhenArmed is <b>ACTIVE</b>. This prevents arming!."));
530 } else {
531 m_ui->alwayStabilizedLabel2->setText(tr("(Really be careful!)."));
535 void ConfigOutputWidget::setChannelLimits(OutputChannelForm *channelForm, OutputBankControls *bankControls)
537 // Set UI limits according to the bankmode and destination
538 switch (bankControls->modeCombo()->currentIndex()) {
539 case ActuatorSettings::BANKMODE_DSHOT:
540 // 0 - 2000 UI limits, DShot min value is fixed to zero
541 if (channelForm->isServoOutput()) {
542 // Driving a servo using DShot doest not make sense so break
543 break;
545 channelForm->setLimits(DSHOT_MINTOUTPUT_RANGE, DSHOT_MINTOUTPUT_RANGE, DSHOT_MINTOUTPUT_RANGE, DSHOT_MAXOUTPUT_RANGE);
546 channelForm->setRange(DSHOT_MINTOUTPUT_RANGE, DSHOT_MAXOUTPUT_RANGE);
547 channelForm->setNeutral(DSHOT_MINTOUTPUT_RANGE);
548 break;
549 case ActuatorSettings::BANKMODE_PWMSYNC:
550 // 900 - 1900 UI limits
551 // Default values 1000 - 1900
552 channelForm->setLimits(DEFAULT_MINOUTPUT_RANGE, PWMSYNC_MAXOUTPUT_RANGE, DEFAULT_MINOUTPUT_RANGE, PWMSYNC_MAXOUTPUT_RANGE);
553 channelForm->setRange(DEFAULT_MINOUTPUT_VALUE, PWMSYNC_MAXOUTPUT_RANGE);
554 channelForm->setNeutral(DEFAULT_MINOUTPUT_VALUE);
555 if (channelForm->isServoOutput()) {
556 // Servo: Some of them can handle PWMSync, 500 - 1900 UI limits
557 // Default values 1000 - 1900 + neutral 1500
558 channelForm->setRange(SERVO_MINOUTPUT_VALUE, PWMSYNC_MAXOUTPUT_RANGE);
559 channelForm->setNeutral(SERVO_NEUTRAL_VALUE);
561 break;
562 case ActuatorSettings::BANKMODE_PWM:
563 if (channelForm->isServoOutput()) {
564 // Servo: 500 - 2500 UI limits
565 // Default values 1000 - 2000 + neutral 1500
566 channelForm->setLimits(SERVO_MINOUTPUT_RANGE, SERVO_MAXOUTPUT_RANGE, SERVO_MINOUTPUT_RANGE, SERVO_MAXOUTPUT_RANGE);
567 channelForm->setRange(SERVO_MINOUTPUT_VALUE, SERVO_MAXOUTPUT_VALUE);
568 channelForm->setNeutral(SERVO_NEUTRAL_VALUE);
569 break;
571 // PWM motor outputs fall to default
572 case ActuatorSettings::BANKMODE_ONESHOT125:
573 case ActuatorSettings::BANKMODE_ONESHOT42:
574 case ActuatorSettings::BANKMODE_MULTISHOT:
575 if (channelForm->isServoOutput()) {
576 // Driving a servo using this mode does not make sense so break
577 break;
579 default:
580 // Motors 900 - 2000 UI limits
581 // Default values 1000 - 2000, neutral set to min
582 // This settings are used for PWM, OneShot125, OneShot42 and MultiShot
583 channelForm->setLimits(DEFAULT_MINOUTPUT_RANGE, DEFAULT_MAXOUTPUT_RANGE, DEFAULT_MINOUTPUT_RANGE, DEFAULT_MAXOUTPUT_RANGE);
584 channelForm->setRange(DEFAULT_MINOUTPUT_VALUE, DEFAULT_MAXOUTPUT_RANGE);
585 channelForm->setNeutral(DEFAULT_MINOUTPUT_VALUE);
586 break;
590 ConfigOutputWidget::ChannelConfigWarning ConfigOutputWidget::checkChannelConfig(OutputChannelForm *channelForm, OutputBankControls *bankControls)
592 ChannelConfigWarning warning = None;
593 int currentNeutralValue = channelForm->neutralValue();
595 // Check if RevMotor has neutral value around center
596 if (channelForm->isReversableMotor()) {
597 warning = IsReversibleMotorCheckNeutral;
598 int neutralDiff = qAbs(REVMOTOR_NEUTRAL_TARGET_VALUE - currentNeutralValue);
599 if (neutralDiff < REVMOTOR_NEUTRAL_DIFF_VALUE) {
600 // Reset warning
601 warning = None;
605 // Check if NormalMotor neutral is not too high
606 if (channelForm->isNormalMotor()) {
607 warning = IsNormalMotorCheckNeutral;
608 int neutralDiff = currentNeutralValue - DEFAULT_MINOUTPUT_VALUE;
609 if (neutralDiff < MOTOR_NEUTRAL_DIFF_VALUE) {
610 // Reset warning
611 warning = None;
615 switch (bankControls->modeCombo()->currentIndex()) {
616 case ActuatorSettings::BANKMODE_DSHOT:
617 if (channelForm->isServoOutput()) {
618 warning = CannotDriveServo;
619 // Driving a servo using DShot doest not make sense so break
620 break;
622 if (channelForm->isReversableMotor()) {
623 // Bi-directional DShot not yet supported
624 warning = BiDirectionalDShotNotSupported;
626 break;
627 case ActuatorSettings::BANKMODE_PWMSYNC:
628 break;
629 case ActuatorSettings::BANKMODE_PWM:
630 break;
631 case ActuatorSettings::BANKMODE_ONESHOT125:
632 case ActuatorSettings::BANKMODE_ONESHOT42:
633 case ActuatorSettings::BANKMODE_MULTISHOT:
634 if (channelForm->isServoOutput()) {
635 warning = CannotDriveServo;
636 // Driving a servo using this mode does not make sense so break
637 break;
639 default:
640 break;
643 return warning;
647 void ConfigOutputWidget::onBankTypeChange()
649 QComboBox *bankModeCombo = qobject_cast<QComboBox *>(sender());
651 ChannelConfigWarning current_warning = None;
652 ChannelConfigWarning warning_found = None;
654 if (bankModeCombo != NULL) {
655 int bankNumber = 1;
656 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
657 foreach(OutputBankControls controls, m_banks) {
658 if (controls.modeCombo() == bankModeCombo) {
659 bool enabled = bankModeCombo->currentIndex() == ActuatorSettings::BANKMODE_PWM;
660 controls.rateCombo()->setEnabled(enabled);
661 controls.rateCombo()->setCurrentIndex(enabled ? 1 : 0);
662 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
663 if (outputChannelForm->bank().toInt() == bankNumber) {
664 setChannelLimits(outputChannelForm, &controls);
665 current_warning = checkChannelConfig(outputChannelForm, &controls);
666 if (current_warning > None) {
667 warning_found = current_warning;
671 break;
674 bankNumber++;
678 updateChannelConfigWarning(warning_found);
681 void ConfigOutputWidget::checkOutputConfig()
683 ChannelConfigWarning current_warning = None;
684 ChannelConfigWarning warning_found = None;
686 int bankNumber = 1;
688 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
690 foreach(OutputBankControls controls, m_banks) {
691 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
692 if (!outputChannelForm->isDisabledOutput() && (outputChannelForm->bank().toInt() == bankNumber)) {
693 current_warning = checkChannelConfig(outputChannelForm, &controls);
694 if (current_warning > None) {
695 warning_found = current_warning;
700 bankNumber++;
703 updateChannelConfigWarning(warning_found);
705 // Emit signal to be received by Input tab
706 emit outputConfigSafe(warning_found == None);
709 void ConfigOutputWidget::stopTests()
711 m_ui->channelOutTest->setChecked(false);
712 channelTestsStarted = false;
715 void ConfigOutputWidget::updateBoardWarnings(UAVObject *)
717 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
718 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
720 if (systemAlarms.Alarm[SystemAlarms::ALARM_SYSTEMCONFIGURATION] > SystemAlarms::ALARM_WARNING) {
721 switch (systemAlarms.ExtendedAlarmStatus[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION]) {
722 case SystemAlarms::EXTENDEDALARMSTATUS_UNSUPPORTEDCONFIG_ONESHOT:
723 setBoardWarning(tr("OneShot and PWMSync output only works with Receiver Port settings marked with '+OneShot'<br>"
724 "When using Receiver Port setting 'PPM_PIN8+OneShot' "
725 "<b><font color='%1'>Bank %2</font></b> must be set to PWM")
726 .arg(m_banks.at(3).color().name()).arg(m_banks.at(3).label()->text()));
727 return;
730 setBoardWarning(NULL);
733 void ConfigOutputWidget::updateChannelConfigWarning(ChannelConfigWarning warning)
735 QString warning_str;
737 if (warning == BiDirectionalDShotNotSupported) {
738 // TODO: Implement bi-directional DShot
739 warning_str = "There is at least <b>one reversable motor using DShot</b> in your configuration.<br>"
740 "Bi-directional DShot is not currently supported, you should use PWM, OneShotXXX or MultiShot.";
743 if (warning == IsNormalMotorCheckNeutral) {
744 warning_str = "Seems there is at least one pretty <b>high neutral value</b> set in your configuration.<br>"
745 "Be sure all Esc are calibrated and no mechanical stress in all motors.";
748 if (warning == IsReversibleMotorCheckNeutral) {
749 warning_str = "There is at least one <b>reversable motor</b> in your configuration.<br>"
750 "Be sure you set a appropriate neutral value before saving and applying power to the vehicule.";
753 if (warning == CannotDriveServo) {
754 warning_str = "One Bank cannot drive a <b>servo output!</b><br>"
755 "You must use PWM for this bank or move the servo output to another compatible bank.";
758 setConfigWarning(warning_str);
761 void ConfigOutputWidget::enableBanks(bool state)
763 // Disable/Enable banks
764 for (int i = 0; i < m_banks.count(); i++) {
765 OutputBankControls controls = m_banks.at(i);
766 if (i < activeBanksCount) {
767 controls.modeCombo()->setEnabled(state);
768 controls.rateCombo()->setEnabled(state);
769 } else {
770 controls.modeCombo()->setEnabled(false);
771 controls.rateCombo()->setEnabled(false);
776 void ConfigOutputWidget::setBoardWarning(QString message)
778 m_ui->boardWarningFrame->setVisible(!message.isNull());
779 m_ui->boardWarningPic->setPixmap(message.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
780 m_ui->boardWarningTxt->setText(message);
783 void ConfigOutputWidget::setConfigWarning(QString message)
785 m_ui->configWarningFrame->setVisible(!message.isNull());
786 m_ui->configWarningPic->setPixmap(message.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
787 m_ui->configWarningTxt->setText(message);
790 void ConfigOutputWidget::inputCalibrationStatus(bool started)
792 inputCalibrationStarted = started;
794 // Disable UI when a input calibration is started
795 // so user cannot manipulate settings.
796 enableControls(!started);
797 enableBanks(!started);
799 // Disable every channel form when needed
800 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
801 OutputChannelForm *form = getOutputChannelForm(i);
802 form->ui->actuatorRev->setChecked(false);
803 form->ui->actuatorLink->setChecked(false);
804 form->inputCalibrationStatus(started);
805 form->enableControls(!started);
809 OutputBankControls::OutputBankControls(MixerSettings *mixer, QLabel *label, QColor color, QComboBox *rateCombo, QComboBox *modeCombo) :
810 m_mixer(mixer), m_label(label), m_color(color), m_rateCombo(rateCombo), m_modeCombo(modeCombo)
813 OutputBankControls::~OutputBankControls()