LP-551 Changes from review, change names and fix typos.
[librepilot.git] / ground / gcs / src / plugins / config / configoutputwidget.cpp
bloba306881216917dbab7ddd46e0be3e0e209b613ce
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);
172 channelTestsStarted = false;
174 m_ui->channelOutTest->setEnabled(enable);
178 Force update all channels with the values in the OutputChannelForms.
180 void ConfigOutputWidget::sendAllChannelTests()
182 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
183 OutputChannelForm *form = getOutputChannelForm(i);
184 sendChannelTest(i, form->neutral());
189 Toggles the channel testing mode by making the GCS take over
190 the ActuatorCommand objects
192 void ConfigOutputWidget::runChannelTests(bool state)
194 if (!checkOutputConfig()) {
195 m_ui->channelOutTest->setChecked(false);
196 QMessageBox::warning(this, tr("Warning"), tr("There is something wrong in the current config."
197 "<p>Please fix the issue before starting testing outputs.</p>"), QMessageBox::Ok);
198 return;
201 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
202 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
204 if (state && systemAlarms.Alarm[SystemAlarms::ALARM_ACTUATOR] != SystemAlarms::ALARM_OK) {
205 QMessageBox mbox;
206 mbox.setText(QString(tr("The actuator module is in an error state. This can also occur because there are no inputs. "
207 "Please fix these before testing outputs.")));
208 mbox.setStandardButtons(QMessageBox::Ok);
209 mbox.exec();
211 // Unfortunately must cache this since callback will reoccur
212 m_accInitialData = ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
214 m_ui->channelOutTest->setChecked(false);
215 return;
218 // Confirm this is definitely what they want
219 if (state) {
220 QMessageBox mbox;
221 mbox.setText(QString(tr("This option will start your motors by the amount selected on the sliders regardless of transmitter."
222 "It is recommended to remove any blades from motors. Are you sure you want to do this?")));
223 mbox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
224 int retval = mbox.exec();
225 if (retval != QMessageBox::Yes) {
226 state = false;
227 qDebug() << "Cancelled";
228 m_ui->channelOutTest->setChecked(false);
229 return;
233 channelTestsStarted = state;
235 // Emit signal to be received by Input tab
236 emit outputConfigSafeChanged(!state);
238 m_ui->spinningArmed->setEnabled(!state);
239 m_ui->alwaysStabilizedSwitch->setEnabled((m_ui->spinningArmed->isChecked()) && !state);
240 m_ui->alwayStabilizedLabel1->setEnabled((m_ui->spinningArmed->isChecked()) && !state);
241 m_ui->alwayStabilizedLabel2->setEnabled((m_ui->spinningArmed->isChecked()) && !state);
242 setBanksEnabled(!state);
244 ActuatorCommand *obj = ActuatorCommand::GetInstance(getObjectManager());
245 UAVObject::Metadata mdata = obj->getMetadata();
246 if (state) {
247 m_accInitialData = mdata;
248 UAVObject::SetFlightAccess(mdata, UAVObject::ACCESS_READONLY);
249 UAVObject::SetFlightTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_ONCHANGE);
250 UAVObject::SetGcsTelemetryAcked(mdata, false);
251 UAVObject::SetGcsTelemetryUpdateMode(mdata, UAVObject::UPDATEMODE_ONCHANGE);
252 mdata.gcsTelemetryUpdatePeriod = 100;
253 } else {
254 mdata = m_accInitialData; // Restore metadata
256 obj->setMetadata(mdata);
257 obj->updated();
259 // Setup the correct initial channel values when the channel testing mode is turned on.
260 if (state) {
261 sendAllChannelTests();
264 // Add info at end
265 if (!state && isDirty()) {
266 QMessageBox mbox;
267 mbox.setText(QString(tr("You may want to save your neutral settings.")));
268 mbox.setStandardButtons(QMessageBox::Ok);
269 mbox.setIcon(QMessageBox::Information);
270 mbox.exec();
274 OutputChannelForm *ConfigOutputWidget::getOutputChannelForm(const int index) const
276 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
277 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
278 if (outputChannelForm->index() == index) {
279 return outputChannelForm;
283 // no OutputChannelForm found with given index
284 return NULL;
288 * Set the label for a channel output assignement
290 void ConfigOutputWidget::assignOutputChannel(UAVDataObject *obj, QString &str)
292 // FIXME: use signal/ slot approach
293 UAVObjectField *field = obj->getField(str);
294 QStringList options = field->getOptions();
295 int index = options.indexOf(field->getValue().toString());
297 OutputChannelForm *outputChannelForm = getOutputChannelForm(index);
299 if (outputChannelForm) {
300 outputChannelForm->setName(str);
305 Sends the channel value to the UAV to move the servo.
306 Returns immediately if we are not in testing mode
308 void ConfigOutputWidget::sendChannelTest(int index, int value)
310 if (!m_ui->channelOutTest->isChecked()) {
311 return;
314 if (index < 0 || (unsigned)index >= ActuatorCommand::CHANNEL_NUMELEM) {
315 return;
318 ActuatorCommand *actuatorCommand = ActuatorCommand::GetInstance(getObjectManager());
319 Q_ASSERT(actuatorCommand);
320 ActuatorCommand::DataFields actuatorCommandFields = actuatorCommand->getData();
321 actuatorCommandFields.Channel[index] = value;
322 actuatorCommand->setData(actuatorCommandFields);
325 void ConfigOutputWidget::setColor(QWidget *widget, const QColor color)
327 QPalette p(palette());
329 p.setColor(QPalette::Background, color);
330 p.setColor(QPalette::Base, color);
331 p.setColor(QPalette::Active, QPalette::Button, color);
332 p.setColor(QPalette::Inactive, QPalette::Button, color);
333 widget->setAutoFillBackground(true);
334 widget->setPalette(p);
337 /********************************
338 * Output settings
339 *******************************/
342 Request the current config from the board (RC Output)
344 void ConfigOutputWidget::refreshWidgetsValuesImpl(UAVObject *obj)
346 Q_UNUSED(obj);
348 // Get Actuator Settings
349 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(getObjectManager());
351 Q_ASSERT(actuatorSettings);
352 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
354 // Get channel descriptions
355 QStringList channelDesc = ConfigVehicleTypeWidget::getChannelDescriptions();
357 // Initialize output forms
358 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
359 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
360 outputChannelForm->setName(channelDesc[outputChannelForm->index()]);
362 // init min,max,neutral
363 int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()];
364 int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()];
365 outputChannelForm->setRange(minValue, maxValue);
367 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
368 outputChannelForm->setNeutral(neutral);
371 // Get the SpinWhileArmed setting
372 m_ui->spinningArmed->setChecked(actuatorSettingsData.MotorsSpinWhileArmed == ActuatorSettings::MOTORSSPINWHILEARMED_TRUE);
374 for (int i = 0; i < m_banks.count(); i++) {
375 OutputBankControls controls = m_banks.at(i);
376 // Reset to all disabled
377 controls.label()->setText("-");
379 controls.rateCombo()->setEnabled(false);
380 setColor(controls.rateCombo(), palette().color(QPalette::Background));
381 controls.rateCombo()->setCurrentIndex(0);
383 controls.modeCombo()->setEnabled(false);
384 setColor(controls.modeCombo(), palette().color(QPalette::Background));
387 // Get connected board model
388 ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
389 Q_ASSERT(pm);
390 UAVObjectUtilManager *utilMngr = pm->getObject<UAVObjectUtilManager>();
391 Q_ASSERT(utilMngr);
392 QStringList bankLabels;
393 QList<int> channelBanks;
395 if (utilMngr) {
396 int board = utilMngr->getBoardModel();
397 // Setup labels and combos for banks according to board type
398 if ((board & 0xff00) == 0x0400) {
399 // Coptercontrol family of boards 4 timer banks
400 bankLabels << "1 (1-3)" << "2 (4)" << "3 (5,7-8)" << "4 (6,9-10)";
401 channelBanks << 1 << 1 << 1 << 2 << 3 << 4 << 3 << 3 << 4 << 4;
402 } else if (board == 0x0903) {
403 // Revolution family of boards 6 timer banks
404 bankLabels << "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7,12)" << "6 (8-11)";
405 channelBanks << 1 << 1 << 2 << 3 << 4 << 4 << 5 << 6 << 6 << 6 << 6 << 5;
406 } else if (board == 0x0905) {
407 // Revolution Nano
408 bankLabels << "1 (1)" << "2 (2,7,11)" << "3 (3)" << "4 (4)" << "5 (5-6)" << "6 (8-10,12)";
409 channelBanks << 1 << 2 << 3 << 4 << 5 << 5 << 2 << 6 << 6 << 6 << 2 << 6;
410 } else if (board == 0x9201) {
411 // Sparky2
412 bankLabels << "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7-8)" << "6 (9-10)";
413 channelBanks << 1 << 1 << 2 << 3 << 4 << 4 << 5 << 5 << 6 << 6;
414 } else if (board == 0x1001) {
415 // SPRacingF3
416 bankLabels << "1 (1-3,7)" << "2 (4,8)" << "3 (5)" << "4 (6)";
417 channelBanks << 1 << 1 << 1 << 2 << 3 << 4 << 1 << 2;
418 } else if (board == 0x1002 || board == 0x1003) {
419 // SPRacingF3_EVO, NucleoF303RE
420 bankLabels << "1 (1-3)" << "2 (4)" << "3 (5-8)";
421 channelBanks << 1 << 1 << 1 << 2 << 3 << 3 << 3 << 3;
422 } else if (board == 0x1005) { // PikoBLX
423 bankLabels << "1 (1-4)" << "2 (5-6)" << "3 (7)" << "4 (8)";
424 channelBanks << 1 << 1 << 1 << 1 << 2 << 2 << 3 << 4;
425 } else if (board == 0x1006) { // tinyFISH
426 bankLabels << "1 (1)" << "2 (2)" << "3 (3-4)" << "4 (5-6)";
427 channelBanks << 1 << 2 << 3 << 3 << 4 << 4;
431 // Store how many banks are active according to the board
432 activeBanksCount = bankLabels.count();
434 int i = 0;
435 foreach(QString banklabel, bankLabels) {
436 OutputBankControls controls = m_banks.at(i);
438 controls.label()->setText(banklabel);
439 int index = controls.rateCombo()->findData(actuatorSettingsData.BankUpdateFreq[i]);
440 if (index == -1) {
441 controls.rateCombo()->addItem(tr("%1 Hz").arg(actuatorSettingsData.BankUpdateFreq[i]), actuatorSettingsData.BankUpdateFreq[i]);
443 bool isPWM = (controls.modeCombo()->currentIndex() == ActuatorSettings::BANKMODE_PWM);
444 controls.rateCombo()->setCurrentIndex(index);
445 controls.rateCombo()->setEnabled(!inputCalibrationStarted && !channelTestsStarted && isPWM);
446 setColor(controls.rateCombo(), controls.color());
447 controls.modeCombo()->setEnabled(!inputCalibrationStarted && !channelTestsStarted);
448 setColor(controls.modeCombo(), controls.color());
449 i++;
452 // Get Channel ranges:
453 i = 0;
454 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
455 int minValue = actuatorSettingsData.ChannelMin[outputChannelForm->index()];
456 int maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->index()];
458 if (channelBanks.count() > i) {
459 int bankNumber = channelBanks.at(i);
460 OutputBankControls bankControls = m_banks.at(bankNumber - 1);
462 setChannelLimits(outputChannelForm, &bankControls);
464 outputChannelForm->setBank(QString::number(bankNumber));
465 outputChannelForm->setColor(bankControls.color());
467 i++;
469 outputChannelForm->setRange(minValue, maxValue);
470 int neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->index()];
471 outputChannelForm->setNeutral(neutral);
474 updateSpinStabilizeCheckComboBoxes();
475 checkOutputConfig();
479 * Sends the config to the board, without saving to the SD card (RC Output)
481 void ConfigOutputWidget::updateObjectsFromWidgetsImpl()
483 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(getObjectManager());
485 Q_ASSERT(actuatorSettings);
486 if (actuatorSettings) {
487 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
489 // Set channel ranges
490 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
491 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
492 actuatorSettingsData.ChannelMax[outputChannelForm->index()] = outputChannelForm->max();
493 actuatorSettingsData.ChannelMin[outputChannelForm->index()] = outputChannelForm->min();
494 actuatorSettingsData.ChannelNeutral[outputChannelForm->index()] = outputChannelForm->neutral();
497 // Set update rates
498 actuatorSettingsData.BankUpdateFreq[0] = m_ui->cb_outputRate1->currentData().toUInt();
499 actuatorSettingsData.BankUpdateFreq[1] = m_ui->cb_outputRate2->currentData().toUInt();
500 actuatorSettingsData.BankUpdateFreq[2] = m_ui->cb_outputRate3->currentData().toUInt();
501 actuatorSettingsData.BankUpdateFreq[3] = m_ui->cb_outputRate4->currentData().toUInt();
502 actuatorSettingsData.BankUpdateFreq[4] = m_ui->cb_outputRate5->currentData().toUInt();
503 actuatorSettingsData.BankUpdateFreq[5] = m_ui->cb_outputRate6->currentData().toUInt();
505 actuatorSettingsData.MotorsSpinWhileArmed = m_ui->spinningArmed->isChecked() ?
506 ActuatorSettings::MOTORSSPINWHILEARMED_TRUE :
507 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE;
509 // Apply settings
510 UAVObjectUpdaterHelper updateHelper;
511 actuatorSettings->setData(actuatorSettingsData, false);
512 updateHelper.doObjectAndWait(actuatorSettings);
515 FlightModeSettings *flightModeSettings = FlightModeSettings::GetInstance(getObjectManager());
516 Q_ASSERT(flightModeSettings);
518 if (flightModeSettings) {
519 FlightModeSettings::DataFields flightModeSettingsData = flightModeSettings->getData();
520 flightModeSettingsData.AlwaysStabilizeWhenArmedSwitch = m_ui->alwaysStabilizedSwitch->currentIndex();
522 // Apply settings
523 flightModeSettings->setData(flightModeSettingsData);
527 void ConfigOutputWidget::updateSpinStabilizeCheckComboBoxes()
529 m_ui->alwayStabilizedLabel1->setEnabled((m_ui->spinningArmed->isChecked()) && (m_ui->spinningArmed->isEnabled()));
530 m_ui->alwayStabilizedLabel2->setEnabled((m_ui->spinningArmed->isChecked()) && (m_ui->spinningArmed->isEnabled()));
531 m_ui->alwaysStabilizedSwitch->setEnabled((m_ui->spinningArmed->isChecked()) && (m_ui->spinningArmed->isEnabled()));
533 if (!m_ui->spinningArmed->isChecked()) {
534 m_ui->alwaysStabilizedSwitch->setCurrentIndex(FlightModeSettings::ALWAYSSTABILIZEWHENARMEDSWITCH_DISABLED);
538 void ConfigOutputWidget::updateAlwaysStabilizeStatus()
540 FlightStatus *flightStatusObj = FlightStatus::GetInstance(getObjectManager());
541 FlightStatus::DataFields flightStatus = flightStatusObj->getData();
543 if (flightStatus.AlwaysStabilizeWhenArmed == FlightStatus::ALWAYSSTABILIZEWHENARMED_TRUE) {
544 m_ui->alwayStabilizedLabel2->setText(tr("AlwaysStabilizeWhenArmed is <b>ACTIVE</b>. This prevents arming!."));
545 } else {
546 m_ui->alwayStabilizedLabel2->setText(tr("(Really be careful!)."));
550 void ConfigOutputWidget::setChannelLimits(OutputChannelForm *channelForm, OutputBankControls *bankControls)
552 // Set UI limits according to the bankmode and destination
553 switch (bankControls->modeCombo()->currentIndex()) {
554 case ActuatorSettings::BANKMODE_DSHOT:
555 // 0 - 2000 UI limits, DShot min value is fixed to zero
556 if (channelForm->isServoOutput()) {
557 // Driving a servo using DShot doest not make sense so break
558 break;
560 channelForm->setLimits(DSHOT_MINTOUTPUT_RANGE, DSHOT_MINTOUTPUT_RANGE, DSHOT_MINTOUTPUT_RANGE, DSHOT_MAXOUTPUT_RANGE);
561 channelForm->setRange(DSHOT_MINTOUTPUT_RANGE, DSHOT_MAXOUTPUT_RANGE);
562 channelForm->setNeutral(DSHOT_MINTOUTPUT_RANGE);
563 break;
564 case ActuatorSettings::BANKMODE_PWMSYNC:
565 // 900 - 1900 UI limits
566 // Default values 1000 - 1900
567 channelForm->setLimits(DEFAULT_MINOUTPUT_RANGE, PWMSYNC_MAXOUTPUT_RANGE, DEFAULT_MINOUTPUT_RANGE, PWMSYNC_MAXOUTPUT_RANGE);
568 channelForm->setRange(DEFAULT_MINOUTPUT_VALUE, PWMSYNC_MAXOUTPUT_RANGE);
569 channelForm->setNeutral(DEFAULT_MINOUTPUT_VALUE);
570 if (channelForm->isServoOutput()) {
571 // Servo: Some of them can handle PWMSync, 500 - 1900 UI limits
572 // Default values 1000 - 1900 + neutral 1500
573 channelForm->setRange(SERVO_MINOUTPUT_VALUE, PWMSYNC_MAXOUTPUT_RANGE);
574 channelForm->setNeutral(SERVO_NEUTRAL_VALUE);
576 break;
577 case ActuatorSettings::BANKMODE_PWM:
578 if (channelForm->isServoOutput()) {
579 // Servo: 500 - 2500 UI limits
580 // Default values 1000 - 2000 + neutral 1500
581 channelForm->setLimits(SERVO_MINOUTPUT_RANGE, SERVO_MAXOUTPUT_RANGE, SERVO_MINOUTPUT_RANGE, SERVO_MAXOUTPUT_RANGE);
582 channelForm->setRange(SERVO_MINOUTPUT_VALUE, SERVO_MAXOUTPUT_VALUE);
583 channelForm->setNeutral(SERVO_NEUTRAL_VALUE);
584 break;
586 // PWM motor outputs fall to default
587 case ActuatorSettings::BANKMODE_ONESHOT125:
588 case ActuatorSettings::BANKMODE_ONESHOT42:
589 case ActuatorSettings::BANKMODE_MULTISHOT:
590 if (channelForm->isServoOutput()) {
591 // Driving a servo using this mode does not make sense so break
592 break;
594 default:
595 // Motors 900 - 2000 UI limits
596 // Default values 1000 - 2000, neutral set to min
597 // This settings are used for PWM, OneShot125, OneShot42 and MultiShot
598 channelForm->setLimits(DEFAULT_MINOUTPUT_RANGE, DEFAULT_MAXOUTPUT_RANGE, DEFAULT_MINOUTPUT_RANGE, DEFAULT_MAXOUTPUT_RANGE);
599 channelForm->setRange(DEFAULT_MINOUTPUT_VALUE, DEFAULT_MAXOUTPUT_RANGE);
600 channelForm->setNeutral(DEFAULT_MINOUTPUT_VALUE);
601 break;
605 ConfigOutputWidget::ChannelConfigWarning ConfigOutputWidget::checkChannelConfig(OutputChannelForm *channelForm, OutputBankControls *bankControls)
607 ChannelConfigWarning warning = None;
608 int currentNeutralValue = channelForm->neutralValue();
610 // Check if RevMotor has neutral value around center
611 if (channelForm->isReversibleMotorOutput()) {
612 warning = IsReversibleMotorCheckNeutral;
613 int neutralDiff = qAbs(REVMOTOR_NEUTRAL_TARGET_VALUE - currentNeutralValue);
614 if (neutralDiff < REVMOTOR_NEUTRAL_DIFF_VALUE) {
615 // Reset warning
616 warning = None;
620 // Check if NormalMotor neutral is not too high
621 if (channelForm->isNormalMotorOutput()) {
622 warning = IsNormalMotorCheckNeutral;
623 int neutralDiff = currentNeutralValue - DEFAULT_MINOUTPUT_VALUE;
624 if (neutralDiff < MOTOR_NEUTRAL_DIFF_VALUE) {
625 // Reset warning
626 warning = None;
630 switch (bankControls->modeCombo()->currentIndex()) {
631 case ActuatorSettings::BANKMODE_DSHOT:
632 if (channelForm->isServoOutput()) {
633 // Driving a servo using DShot doest not make sense
634 warning = CannotDriveServo;
635 } else if (channelForm->isReversibleMotorOutput()) {
636 // Bi-directional DShot not yet supported
637 warning = BiDirectionalDShotNotSupported;
639 break;
640 case ActuatorSettings::BANKMODE_PWMSYNC:
641 case ActuatorSettings::BANKMODE_PWM:
642 break;
643 case ActuatorSettings::BANKMODE_ONESHOT125:
644 case ActuatorSettings::BANKMODE_ONESHOT42:
645 case ActuatorSettings::BANKMODE_MULTISHOT:
646 if (channelForm->isServoOutput()) {
647 warning = CannotDriveServo;
648 // Driving a servo using this mode does not make sense so break
650 default:
651 break;
654 return warning;
658 void ConfigOutputWidget::onBankTypeChange()
660 QComboBox *bankModeCombo = qobject_cast<QComboBox *>(sender());
662 ChannelConfigWarning new_warning = None;
664 if (bankModeCombo != NULL) {
665 int bankNumber = 1;
666 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
667 foreach(OutputBankControls controls, m_banks) {
668 if (controls.modeCombo() == bankModeCombo) {
669 bool enabled = bankModeCombo->currentIndex() == ActuatorSettings::BANKMODE_PWM;
670 controls.rateCombo()->setEnabled(enabled);
671 controls.rateCombo()->setCurrentIndex(enabled ? 1 : 0);
672 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
673 if (outputChannelForm->bank().toInt() == bankNumber) {
674 setChannelLimits(outputChannelForm, &controls);
675 ChannelConfigWarning warning = checkChannelConfig(outputChannelForm, &controls);
676 if (warning > None) {
677 new_warning = warning;
681 break;
684 bankNumber++;
688 updateChannelConfigWarning(new_warning);
691 bool ConfigOutputWidget::checkOutputConfig()
693 ChannelConfigWarning new_warning = None;
695 int bankNumber = 1;
697 QList<OutputChannelForm *> outputChannelForms = findChildren<OutputChannelForm *>();
699 foreach(OutputBankControls controls, m_banks) {
700 foreach(OutputChannelForm * outputChannelForm, outputChannelForms) {
701 if (!outputChannelForm->isDisabledOutput() && (outputChannelForm->bank().toInt() == bankNumber)) {
702 ChannelConfigWarning warning = checkChannelConfig(outputChannelForm, &controls);
703 if (warning > None) {
704 new_warning = warning;
709 bankNumber++;
712 updateChannelConfigWarning(new_warning);
714 // Emit signal to be received by Input tab
715 emit outputConfigSafeChanged(new_warning == None);
717 return new_warning == None;
720 void ConfigOutputWidget::stopTests()
722 m_ui->channelOutTest->setChecked(false);
723 channelTestsStarted = false;
726 void ConfigOutputWidget::updateBoardWarnings(UAVObject *)
728 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(getObjectManager());
729 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
731 if (systemAlarms.Alarm[SystemAlarms::ALARM_SYSTEMCONFIGURATION] > SystemAlarms::ALARM_WARNING) {
732 switch (systemAlarms.ExtendedAlarmStatus[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION]) {
733 case SystemAlarms::EXTENDEDALARMSTATUS_UNSUPPORTEDCONFIG_ONESHOT:
734 setBoardWarning(tr("OneShot and PWMSync output only works with Receiver Port settings marked with '+OneShot'<br>"
735 "When using Receiver Port setting 'PPM_PIN8+OneShot' "
736 "<b><font color='%1'>Bank %2</font></b> must be set to PWM")
737 .arg(m_banks.at(3).color().name()).arg(m_banks.at(3).label()->text()));
738 return;
741 setBoardWarning(NULL);
744 void ConfigOutputWidget::updateChannelConfigWarning(ChannelConfigWarning warning)
746 QString warning_str;
748 if (warning == BiDirectionalDShotNotSupported) {
749 // TODO: Implement bi-directional DShot
750 warning_str = "There is <b>one reversible motor</b> using DShot is configured.<br>"
751 "Bi-directional DShot is currently not supported. Please use PWM, OneShotXXX or MultiShot.";
754 if (warning == IsNormalMotorCheckNeutral) {
755 warning_str = "There is at least one pretty <b>high neutral value</b> set in your configuration.<br>"
756 "Make sure all ESCs are calibrated and no mechanical stress in all motors.";
759 if (warning == IsReversibleMotorCheckNeutral) {
760 warning_str = "A least one <b>reversible motor</b> is configured.<br>"
761 "Make sure a appropriate neutral value is set before saving and applying power to the vehicule.";
764 if (warning == CannotDriveServo) {
765 warning_str = "One bank cannot drive a <b>servo output</b>!<br>"
766 "You must use PWM for this bank or move the servo output to another compatible bank.";
769 setConfigWarning(warning_str);
772 void ConfigOutputWidget::setBanksEnabled(bool state)
774 // Disable/Enable banks
775 for (int i = 0; i < m_banks.count(); i++) {
776 OutputBankControls controls = m_banks.at(i);
777 if (i < activeBanksCount) {
778 controls.modeCombo()->setEnabled(state);
779 controls.rateCombo()->setEnabled(state);
780 } else {
781 controls.modeCombo()->setEnabled(false);
782 controls.rateCombo()->setEnabled(false);
787 void ConfigOutputWidget::setBoardWarning(QString message)
789 m_ui->boardWarningFrame->setVisible(!message.isNull());
790 m_ui->boardWarningPic->setPixmap(message.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
791 m_ui->boardWarningTxt->setText(message);
794 void ConfigOutputWidget::setConfigWarning(QString message)
796 m_ui->configWarningFrame->setVisible(!message.isNull());
797 m_ui->configWarningPic->setPixmap(message.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
798 m_ui->configWarningTxt->setText(message);
801 void ConfigOutputWidget::setInputCalibrationState(bool started)
803 inputCalibrationStarted = started;
805 // Disable UI when a input calibration is started
806 // so user cannot manipulate settings.
807 enableControls(!started);
808 setBanksEnabled(!started);
809 // Disable ASWA
810 m_ui->spinningArmed->setEnabled(!started);
811 m_ui->alwaysStabilizedSwitch->setEnabled((m_ui->spinningArmed->isChecked()) && !started);
812 m_ui->alwayStabilizedLabel1->setEnabled((m_ui->spinningArmed->isChecked()) && !started);
813 m_ui->alwayStabilizedLabel2->setEnabled((m_ui->spinningArmed->isChecked()) && !started);
815 // Disable every channel form when needed
816 for (unsigned int i = 0; i < ActuatorCommand::CHANNEL_NUMELEM; i++) {
817 OutputChannelForm *form = getOutputChannelForm(i);
818 form->ui->actuatorRev->setChecked(false);
819 form->ui->actuatorLink->setChecked(false);
820 form->setChannelRangeEnabled(!started);
821 form->setControlsEnabled(!started);
825 OutputBankControls::OutputBankControls(MixerSettings *mixer, QLabel *label, QColor color, QComboBox *rateCombo, QComboBox *modeCombo) :
826 m_mixer(mixer), m_label(label), m_color(color), m_rateCombo(rateCombo), m_modeCombo(modeCombo)
829 OutputBankControls::~OutputBankControls()