2 ******************************************************************************
4 * @file configoutputwidget.cpp
5 * @author E. Lafargue & The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
6 * @addtogroup GCSPlugins GCS Plugins
8 * @addtogroup ConfigPlugin Config Plugin
10 * @brief Servo output configuration panel for the config gadget
11 *****************************************************************************/
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "configoutputwidget.h"
29 #include "outputchannelform.h"
30 #include "configvehicletypewidget.h"
32 #include "mixersettings.h"
33 #include "actuatorcommand.h"
34 #include "actuatorsettings.h"
35 #include "systemsettings.h"
36 #include "uavsettingsimportexport/uavsettingsimportexportfactory.h"
37 #include <extensionsystem/pluginmanager.h>
38 #include <coreplugin/generalsettings.h>
41 #include <QStringList>
44 #include <QVBoxLayout>
45 #include <QPushButton>
46 #include <QMessageBox>
47 #include <QDesktopServices>
50 ConfigOutputWidget::ConfigOutputWidget(QWidget
*parent
) : ConfigTaskWidget(parent
)
52 m_ui
= new Ui_OutputWidget();
55 m_ui
->gvFrame
->setVisible(false);
57 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
58 Core::Internal::GeneralSettings
*settings
= pm
->getObject
<Core::Internal::GeneralSettings
>();
59 if (!settings
->useExpertMode()) {
60 m_ui
->saveRCOutputToRAM
->setVisible(false);
63 UAVSettingsImportExportFactory
*importexportplugin
= pm
->getObject
<UAVSettingsImportExportFactory
>();
64 connect(importexportplugin
, SIGNAL(importAboutToBegin()), this, SLOT(stopTests()));
66 connect(m_ui
->channelOutTest
, SIGNAL(clicked(bool)), this, SLOT(runChannelTests(bool)));
68 // Configure the task widget
69 // Connect the help button
70 connect(m_ui
->outputHelp
, SIGNAL(clicked()), this, SLOT(openHelp()));
72 addApplySaveButtons(m_ui
->saveRCOutputToRAM
, m_ui
->saveRCOutputToSD
);
74 // Track the ActuatorSettings object
75 addUAVObject("ActuatorSettings");
77 // NOTE: we have channel indices from 0 to 9, but the convention for OP is Channel 1 to Channel 10.
78 // Register for ActuatorSettings changes:
79 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
80 OutputChannelForm
*form
= new OutputChannelForm(i
, this);
81 form
->moveTo(*(m_ui
->channelLayout
));
83 connect(m_ui
->channelOutTest
, SIGNAL(toggled(bool)), form
, SLOT(enableChannelTest(bool)));
84 connect(form
, SIGNAL(channelChanged(int, int)), this, SLOT(sendChannelTest(int, int)));
86 addWidget(form
->ui
.actuatorMin
);
87 addWidget(form
->ui
.actuatorNeutral
);
88 addWidget(form
->ui
.actuatorMax
);
89 addWidget(form
->ui
.actuatorRev
);
90 addWidget(form
->ui
.actuatorLink
);
94 // Associate the buttons with their UAVO fields
95 addWidget(m_ui
->spinningArmed
);
96 MixerSettings
*mixer
= MixerSettings::GetInstance(getObjectManager());
98 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank1
, QColor("#C6ECAE"), m_ui
->cb_outputRate1
, m_ui
->cb_outputMode1
);
99 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank2
, QColor("#91E5D3"), m_ui
->cb_outputRate2
, m_ui
->cb_outputMode2
);
100 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank3
, QColor("#FCEC52"), m_ui
->cb_outputRate3
, m_ui
->cb_outputMode3
);
101 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank4
, QColor("#C3A8FF"), m_ui
->cb_outputRate4
, m_ui
->cb_outputMode4
);
102 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank5
, QColor("#F7F7F2"), m_ui
->cb_outputRate5
, m_ui
->cb_outputMode5
);
103 m_banks
<< OutputBankControls(mixer
, m_ui
->chBank6
, QColor("#FF9F51"), m_ui
->cb_outputRate6
, m_ui
->cb_outputMode6
);
106 rates
<< 50 << 60 << 125 << 165 << 270 << 330 << 400 << 490;
108 foreach(OutputBankControls controls
, m_banks
) {
109 addWidget(controls
.rateCombo());
111 controls
.rateCombo()->addItem(tr("-"), QVariant(0));
112 controls
.rateCombo()->model()->setData(controls
.rateCombo()->model()->index(0, 0), QVariant(0), Qt::UserRole
- 1);
113 foreach(int rate
, rates
) {
114 controls
.rateCombo()->addItem(tr("%1 Hz").arg(rate
), rate
);
117 addWidgetBinding("ActuatorSettings", "BankMode", controls
.modeCombo(), i
++, 0, true);
118 connect(controls
.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
121 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
122 connect(systemAlarmsObj
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateWarnings(UAVObject
*)));
124 disconnect(this, SLOT(refreshWidgetsValues(UAVObject
*)));
127 refreshWidgetsValues();
129 updateEnableControls();
132 ConfigOutputWidget::~ConfigOutputWidget()
134 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
136 disconnect(systemAlarmsObj
, SIGNAL(objectUpdated(UAVObject
*)), this, SLOT(updateWarnings(UAVObject
*)));
137 foreach(OutputBankControls controls
, m_banks
) {
138 disconnect(controls
.modeCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(onBankTypeChange()));
142 void ConfigOutputWidget::enableControls(bool enable
)
144 ConfigTaskWidget::enableControls(enable
);
147 m_ui
->channelOutTest
->setChecked(false);
149 m_ui
->channelOutTest
->setEnabled(enable
);
153 Force update all channels with the values in the OutputChannelForms.
155 void ConfigOutputWidget::sendAllChannelTests()
157 for (unsigned int i
= 0; i
< ActuatorCommand::CHANNEL_NUMELEM
; i
++) {
158 OutputChannelForm
*form
= getOutputChannelForm(i
);
159 sendChannelTest(i
, form
->neutral());
164 Toggles the channel testing mode by making the GCS take over
165 the ActuatorCommand objects
167 void ConfigOutputWidget::runChannelTests(bool state
)
169 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
170 SystemAlarms::DataFields systemAlarms
= systemAlarmsObj
->getData();
172 if (state
&& systemAlarms
.Alarm
[SystemAlarms::ALARM_ACTUATOR
] != SystemAlarms::ALARM_OK
) {
174 mbox
.setText(QString(tr("The actuator module is in an error state. This can also occur because there are no inputs. "
175 "Please fix these before testing outputs.")));
176 mbox
.setStandardButtons(QMessageBox::Ok
);
179 // Unfortunately must cache this since callback will reoccur
180 m_accInitialData
= ActuatorCommand::GetInstance(getObjectManager())->getMetadata();
182 m_ui
->channelOutTest
->setChecked(false);
186 // Confirm this is definitely what they want
189 mbox
.setText(QString(tr("This option will start your motors by the amount selected on the sliders regardless of transmitter."
190 "It is recommended to remove any blades from motors. Are you sure you want to do this?")));
191 mbox
.setStandardButtons(QMessageBox::Yes
| QMessageBox::No
);
192 int retval
= mbox
.exec();
193 if (retval
!= QMessageBox::Yes
) {
195 qDebug() << "Cancelled";
196 m_ui
->channelOutTest
->setChecked(false);
201 ActuatorCommand
*obj
= ActuatorCommand::GetInstance(getObjectManager());
202 UAVObject::Metadata mdata
= obj
->getMetadata();
204 m_accInitialData
= mdata
;
205 UAVObject::SetFlightAccess(mdata
, UAVObject::ACCESS_READONLY
);
206 UAVObject::SetFlightTelemetryUpdateMode(mdata
, UAVObject::UPDATEMODE_ONCHANGE
);
207 UAVObject::SetGcsTelemetryAcked(mdata
, false);
208 UAVObject::SetGcsTelemetryUpdateMode(mdata
, UAVObject::UPDATEMODE_ONCHANGE
);
209 mdata
.gcsTelemetryUpdatePeriod
= 100;
211 mdata
= m_accInitialData
; // Restore metadata
213 obj
->setMetadata(mdata
);
216 // Setup the correct initial channel values when the channel testing mode is turned on.
218 sendAllChannelTests();
222 if (!state
&& isDirty()) {
224 mbox
.setText(QString(tr("You may want to save your neutral settings.")));
225 mbox
.setStandardButtons(QMessageBox::Ok
);
226 mbox
.setIcon(QMessageBox::Information
);
231 OutputChannelForm
*ConfigOutputWidget::getOutputChannelForm(const int index
) const
233 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
234 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
235 if (outputChannelForm
->index() == index
) {
236 return outputChannelForm
;
240 // no OutputChannelForm found with given index
245 * Set the label for a channel output assignement
247 void ConfigOutputWidget::assignOutputChannel(UAVDataObject
*obj
, QString
&str
)
249 // FIXME: use signal/ slot approach
250 UAVObjectField
*field
= obj
->getField(str
);
251 QStringList options
= field
->getOptions();
252 int index
= options
.indexOf(field
->getValue().toString());
254 OutputChannelForm
*outputChannelForm
= getOutputChannelForm(index
);
256 if (outputChannelForm
) {
257 outputChannelForm
->setName(str
);
262 Sends the channel value to the UAV to move the servo.
263 Returns immediately if we are not in testing mode
265 void ConfigOutputWidget::sendChannelTest(int index
, int value
)
267 if (!m_ui
->channelOutTest
->isChecked()) {
271 if (index
< 0 || (unsigned)index
>= ActuatorCommand::CHANNEL_NUMELEM
) {
275 ActuatorCommand
*actuatorCommand
= ActuatorCommand::GetInstance(getObjectManager());
276 Q_ASSERT(actuatorCommand
);
277 ActuatorCommand::DataFields actuatorCommandFields
= actuatorCommand
->getData();
278 actuatorCommandFields
.Channel
[index
] = value
;
279 actuatorCommand
->setData(actuatorCommandFields
);
282 void ConfigOutputWidget::setColor(QWidget
*widget
, const QColor color
)
284 QPalette
p(palette());
286 p
.setColor(QPalette::Background
, color
);
287 p
.setColor(QPalette::Base
, color
);
288 p
.setColor(QPalette::Active
, QPalette::Button
, color
);
289 p
.setColor(QPalette::Inactive
, QPalette::Button
, color
);
290 widget
->setAutoFillBackground(true);
291 widget
->setPalette(p
);
294 /********************************
296 *******************************/
299 Request the current config from the board (RC Output)
301 void ConfigOutputWidget::refreshWidgetsValues(UAVObject
*obj
)
303 bool dirty
= isDirty();
305 ConfigTaskWidget::refreshWidgetsValues(obj
);
307 // Get Actuator Settings
308 ActuatorSettings
*actuatorSettings
= ActuatorSettings::GetInstance(getObjectManager());
310 Q_ASSERT(actuatorSettings
);
311 ActuatorSettings::DataFields actuatorSettingsData
= actuatorSettings
->getData();
313 // Get channel descriptions
314 QStringList channelDesc
= ConfigVehicleTypeWidget::getChannelDescriptions();
316 // Initialize output forms
317 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
318 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
319 outputChannelForm
->setName(channelDesc
[outputChannelForm
->index()]);
321 // init min,max,neutral
322 int minValue
= actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()];
323 int maxValue
= actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()];
324 outputChannelForm
->setRange(minValue
, maxValue
);
326 int neutral
= actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()];
327 outputChannelForm
->setNeutral(neutral
);
330 // Get the SpinWhileArmed setting
331 m_ui
->spinningArmed
->setChecked(actuatorSettingsData
.MotorsSpinWhileArmed
== ActuatorSettings::MOTORSSPINWHILEARMED_TRUE
);
333 for (int i
= 0; i
< m_banks
.count(); i
++) {
334 OutputBankControls controls
= m_banks
.at(i
);
335 // Reset to all disabled
336 controls
.label()->setText("-");
338 controls
.rateCombo()->setEnabled(false);
339 setColor(controls
.rateCombo(), palette().color(QPalette::Background
));
340 controls
.rateCombo()->setCurrentIndex(0);
342 controls
.modeCombo()->setEnabled(false);
343 setColor(controls
.modeCombo(), palette().color(QPalette::Background
));
346 // Get connected board model
347 ExtensionSystem::PluginManager
*pm
= ExtensionSystem::PluginManager::instance();
349 UAVObjectUtilManager
*utilMngr
= pm
->getObject
<UAVObjectUtilManager
>();
351 QStringList bankLabels
;
352 QList
<int> channelBanks
;
355 int board
= utilMngr
->getBoardModel();
356 // Setup labels and combos for banks according to board type
357 if ((board
& 0xff00) == 0x0400) {
358 // Coptercontrol family of boards 4 timer banks
359 bankLabels
<< "1 (1-3)" << "2 (4)" << "3 (5,7-8)" << "4 (6,9-10)";
360 channelBanks
<< 1 << 1 << 1 << 2 << 3 << 4 << 3 << 3 << 4 << 4;
361 } else if (board
== 0x0903) {
362 // Revolution family of boards 6 timer banks
363 bankLabels
<< "1 (1-2)" << "2 (3)" << "3 (4)" << "4 (5-6)" << "5 (7,12)" << "6 (8-11)";
364 channelBanks
<< 1 << 1 << 2 << 3 << 4 << 4 << 5 << 6 << 6 << 6 << 6 << 5;
365 } else if (board
== 0x0905) {
367 bankLabels
<< "1 (1)" << "2 (2,7,11)" << "3 (3)" << "4 (4)" << "5 (5-6)" << "6 (8-10,12)";
368 channelBanks
<< 1 << 2 << 3 << 4 << 5 << 5 << 2 << 6 << 6 << 6 << 2 << 6;
373 foreach(QString banklabel
, bankLabels
) {
374 OutputBankControls controls
= m_banks
.at(i
);
376 controls
.label()->setText(banklabel
);
377 int index
= controls
.rateCombo()->findData(actuatorSettingsData
.BankUpdateFreq
[i
]);
379 controls
.rateCombo()->addItem(tr("%1 Hz").arg(actuatorSettingsData
.BankUpdateFreq
[i
]), actuatorSettingsData
.BankUpdateFreq
[i
]);
381 controls
.rateCombo()->setCurrentIndex(index
);
382 controls
.rateCombo()->setEnabled(controls
.modeCombo()->currentIndex() == ActuatorSettings::BANKMODE_PWM
);
383 setColor(controls
.rateCombo(), controls
.color());
384 controls
.modeCombo()->setEnabled(true);
385 setColor(controls
.modeCombo(), controls
.color());
389 // Get Channel ranges:
391 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
392 int minValue
= actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()];
393 int maxValue
= actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()];
395 outputChannelForm
->setRange(minValue
, maxValue
);
396 if (channelBanks
.count() > i
) {
397 outputChannelForm
->setBank(QString("%1").arg(channelBanks
.at(i
)));
398 outputChannelForm
->setColor(m_banks
.at(channelBanks
.at(i
++) - 1).color());
400 int neutral
= actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()];
401 outputChannelForm
->setNeutral(neutral
);
408 * Sends the config to the board, without saving to the SD card (RC Output)
410 void ConfigOutputWidget::updateObjectsFromWidgets()
412 ConfigTaskWidget::updateObjectsFromWidgets();
414 ActuatorSettings
*actuatorSettings
= ActuatorSettings::GetInstance(getObjectManager());
416 Q_ASSERT(actuatorSettings
);
417 if (actuatorSettings
) {
418 ActuatorSettings::DataFields actuatorSettingsData
= actuatorSettings
->getData();
420 // Set channel ranges
421 QList
<OutputChannelForm
*> outputChannelForms
= findChildren
<OutputChannelForm
*>();
422 foreach(OutputChannelForm
* outputChannelForm
, outputChannelForms
) {
423 actuatorSettingsData
.ChannelMax
[outputChannelForm
->index()] = outputChannelForm
->max();
424 actuatorSettingsData
.ChannelMin
[outputChannelForm
->index()] = outputChannelForm
->min();
425 actuatorSettingsData
.ChannelNeutral
[outputChannelForm
->index()] = outputChannelForm
->neutral();
429 actuatorSettingsData
.BankUpdateFreq
[0] = m_ui
->cb_outputRate1
->currentData().toUInt();
430 actuatorSettingsData
.BankUpdateFreq
[1] = m_ui
->cb_outputRate2
->currentData().toUInt();
431 actuatorSettingsData
.BankUpdateFreq
[2] = m_ui
->cb_outputRate3
->currentData().toUInt();
432 actuatorSettingsData
.BankUpdateFreq
[3] = m_ui
->cb_outputRate4
->currentData().toUInt();
433 actuatorSettingsData
.BankUpdateFreq
[4] = m_ui
->cb_outputRate5
->currentData().toUInt();
434 actuatorSettingsData
.BankUpdateFreq
[5] = m_ui
->cb_outputRate6
->currentData().toUInt();
436 actuatorSettingsData
.MotorsSpinWhileArmed
= m_ui
->spinningArmed
->isChecked() ?
437 ActuatorSettings::MOTORSSPINWHILEARMED_TRUE
:
438 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE
;
441 actuatorSettings
->setData(actuatorSettingsData
);
445 void ConfigOutputWidget::openHelp()
447 QDesktopServices::openUrl(QUrl(tr("http://wiki.openpilot.org/x/WIGf"), QUrl::StrictMode
));
450 void ConfigOutputWidget::onBankTypeChange()
452 QComboBox
*bankModeCombo
= qobject_cast
<QComboBox
*>(sender());
454 if (bankModeCombo
!= NULL
) {
455 foreach(OutputBankControls controls
, m_banks
) {
456 if (controls
.modeCombo() == bankModeCombo
) {
457 bool enabled
= bankModeCombo
->currentIndex() == ActuatorSettings::BANKMODE_PWM
;
458 controls
.rateCombo()->setEnabled(enabled
);
459 controls
.rateCombo()->setCurrentIndex(enabled
? 1 : 0);
466 void ConfigOutputWidget::stopTests()
468 m_ui
->channelOutTest
->setChecked(false);
471 void ConfigOutputWidget::updateWarnings(UAVObject
*)
473 SystemAlarms
*systemAlarmsObj
= SystemAlarms::GetInstance(getObjectManager());
474 SystemAlarms::DataFields systemAlarms
= systemAlarmsObj
->getData();
476 if (systemAlarms
.Alarm
[SystemAlarms::ALARM_SYSTEMCONFIGURATION
] > SystemAlarms::ALARM_WARNING
) {
477 switch (systemAlarms
.ExtendedAlarmStatus
[SystemAlarms::EXTENDEDALARMSTATUS_SYSTEMCONFIGURATION
]) {
478 case SystemAlarms::EXTENDEDALARMSTATUS_UNSUPPORTEDCONFIG_ONESHOT
:
479 setWarning(tr("OneShot and PWMSync output only works with Receiver Port settings marked with '+OneShot'<br>"
480 "When using Receiver Port setting 'PPM_PIN8+OneShot' "
481 "<b><font color='%1'>Bank %2</font></b> must be set to PWM")
482 .arg(m_banks
.at(3).color().name()).arg(m_banks
.at(3).label()->text()));
489 void ConfigOutputWidget::setWarning(QString message
)
491 m_ui
->gvFrame
->setVisible(!message
.isNull());
492 m_ui
->picWarning
->setPixmap(message
.isNull() ? QPixmap() : QPixmap(":/configgadget/images/error.svg"));
493 m_ui
->txtWarning
->setText(message
);
497 OutputBankControls::OutputBankControls(MixerSettings
*mixer
, QLabel
*label
, QColor color
, QComboBox
*rateCombo
, QComboBox
*modeCombo
) :
498 m_mixer(mixer
), m_label(label
), m_color(color
), m_rateCombo(rateCombo
), m_modeCombo(modeCombo
)
501 OutputBankControls::~OutputBankControls()