39 #include <QStringList>
42 #include <QVBoxLayout>
43 #include <QPushButton>
44 #include <QMessageBox>
45 #include <QDesktopServices>
47 #include "mixersettings.h"
48 #include "actuatorcommand.h"
49 #include "actuatorsettings.h"
50 #include "systemalarms.h"
51 #include "systemsettings.h"
60 m_config =
new Ui_OutputWidget();
61 m_config->setupUi(
this);
71 &ConfigOutputWidget::stopTests);
81 &ConfigOutputWidget::stopTests);
87 for (
unsigned int i = 0;
i < ActuatorCommand::CHANNEL_NUMELEM;
i++) {
89 m_config->channelLayout->addWidget(outputForm);
91 connect(m_config->channelOutTest, &QAbstractButton::toggled, outputForm,
94 &ConfigOutputWidget::sendChannelTest);
97 &ConfigOutputWidget::do_SetDirty);
100 connect(m_config->channelOutTest, &QAbstractButton::toggled,
this,
101 &ConfigOutputWidget::runChannelTests);
102 connect(m_config->calibrateESC, &QAbstractButton::clicked,
this,
103 &ConfigOutputWidget::startESCCalibration);
107 lblList << m_config->chBank1 << m_config->chBank2 << m_config->chBank3 << m_config->chBank4
108 << m_config->chBank5 << m_config->chBank6;
110 rateList << m_config->cb_outputRate1 << m_config->cb_outputRate2 << m_config->cb_outputRate3
111 << m_config->cb_outputRate4 << m_config->cb_outputRate5 << m_config->cb_outputRate6;
114 for (rateIter = rateList.begin(); rateIter != rateList.end(); rateIter++) {
115 QComboBox *rate = *rateIter;
117 connect(rate, QOverload<int>::of(&QComboBox::currentIndexChanged),
this,
118 &ConfigOutputWidget::refreshWidgetRanges);
124 this->setEnabled(
false);
126 &ConfigOutputWidget::assignOutputChannels);
129 refreshWidgetsValues();
148 m_config->channelOutTest->setChecked(
false);
149 m_config->channelOutTest->setEnabled(enable);
150 m_config->calibrateESC->setEnabled(enable);
151 m_config->motorCurveFit->setEnabled(enable);
152 m_config->motorPowerGain->setEnabled(enable);
166 void ConfigOutputWidget::runChannelTests(
bool state)
168 SystemAlarms *systemAlarmsObj = SystemAlarms::GetInstance(
getObjectManager());
169 SystemAlarms::DataFields systemAlarms = systemAlarmsObj->getData();
171 if (state && ((systemAlarms.Alarm[SystemAlarms::ALARM_ACTUATOR] == SystemAlarms::ALARM_ERROR)
172 || (systemAlarms.Alarm[SystemAlarms::ALARM_ACTUATOR]
173 == SystemAlarms::ALARM_CRITICAL))) {
174 (
void) QMessageBox::critical(
this, tr(
"Actuator error"),
175 tr(
"The actuator module is in an error state. "
176 "Please fix these before testing outputs."));
179 accInitialData = ActuatorCommand::GetInstance(
getObjectManager())->getMetadata();
181 m_config->channelOutTest->setChecked(
false);
187 int retval = QMessageBox::warning(
this, tr(
"Actuator test"),
188 tr(
"This option will start your motors by the amount selected on the "
189 "sliders regardless of transmitter. It is recommended to remove "
190 "any blades from motors. Are you sure you want to do this?"),
191 QMessageBox::Yes | QMessageBox::No);
193 if (retval != QMessageBox::Yes) {
195 m_config->channelOutTest->setChecked(
false);
201 UAVObject::Metadata mdata = obj->getMetadata();
203 accInitialData = mdata;
208 mdata.gcsTelemetryUpdatePeriod = 100;
210 mdata = accInitialData;
212 obj->setMetadata(mdata);
216 OutputChannelForm *ConfigOutputWidget::getOutputChannelForm(
const int index)
const
220 if (outputChannelForm->
index() == index)
221 return outputChannelForm;
233 void ConfigOutputWidget::assignOutputChannels(
UAVObject *obj)
238 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(
getObjectManager());
239 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
250 quint32 minValue = actuatorSettingsData.ChannelMin[outputChannelForm->
index()];
251 quint32 maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->
index()];
252 outputChannelForm->
setMinmax(minValue, maxValue);
254 quint32 neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->
index()];
258 refreshWidgetRanges();
265 void ConfigOutputWidget::sendChannelTest(
int index,
int value)
267 if (!m_config->channelOutTest->isChecked())
270 if (index < 0 || (
unsigned)index >= ActuatorCommand::CHANNEL_NUMELEM)
273 ActuatorCommand *actuatorCommand = ActuatorCommand::GetInstance(
getObjectManager());
274 Q_ASSERT(actuatorCommand);
275 ActuatorCommand::DataFields actuatorCommandFields = actuatorCommand->getData();
276 actuatorCommandFields.Channel[index] = value;
277 actuatorCommand->setData(actuatorCommandFields);
288 QCheckBox *checkBoxes[ActuatorCommand::CHANNEL_NUMELEM];
289 QLabel infoLabel(
"Select output channels to calibrate: ");
290 layout.addWidget(&infoLabel);
291 for (
unsigned int i = 0;
i < ActuatorCommand::CHANNEL_NUMELEM;
i++) {
292 checkBoxes[
i] =
new QCheckBox();
293 checkBoxes[
i]->setText(QString(
"Channel ") + QString::number(
i + 1) + QString(
" (")
294 + ChannelDesc[
i] + QString(
")"));
295 checkBoxes[
i]->setChecked(
false);
296 layout.addWidget(checkBoxes[
i]);
299 QHBoxLayout horizontalLayout;
300 QPushButton buttonOk(
"Ok");
301 QPushButton buttonCancel(
"Cancel");
302 horizontalLayout.addWidget(&buttonOk);
303 horizontalLayout.addWidget(&buttonCancel);
304 layout.addLayout(&horizontalLayout);
307 dialog.connect(&buttonOk, &QAbstractButton::clicked, &dialog, &QDialog::accept);
308 dialog.connect(&buttonCancel, &QAbstractButton::clicked, &dialog, &QDialog::reject);
311 dialog.setLayout(&layout);
312 int retCode = dialog.exec();
313 if (retCode == dialog.Accepted) {
314 for (
unsigned int i = 0;
i < ActuatorCommand::CHANNEL_NUMELEM;
i++)
315 selectedChannels[
i] = checkBoxes[
i]->isChecked();
324 void ConfigOutputWidget::startESCCalibration()
326 bool channelsToCalibrate[ActuatorCommand::CHANNEL_NUMELEM];
330 QMessageBox mbox(
this);
331 mbox.setText(QString(tr(
"Starting ESC calibration.<br/><b>Please remove all propellers and "
332 "disconnect battery from ESCs.</b>")));
333 mbox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
334 mbox.setDefaultButton(QMessageBox::Cancel);
336 if (mbox.exec() != QMessageBox::Ok)
340 ActuatorCommand *actuatorCommand = ActuatorCommand::GetInstance(
getObjectManager());
341 Q_ASSERT(actuatorCommand);
342 ActuatorCommand::DataFields actuatorCommandFields = actuatorCommand->getData();
343 UAVObject::Metadata mdata = actuatorCommand->getMetadata();
345 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(
getObjectManager());
346 Q_ASSERT(actuatorSettings);
347 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
350 accInitialData = mdata;
357 mdata.gcsTelemetryUpdatePeriod = 100;
358 actuatorCommand->setMetadata(mdata);
359 actuatorCommand->updated();
361 mbox.setText(QString(tr(
"Please wait...")));
366 for (
unsigned int i = 0;
i < ActuatorCommand::CHANNEL_NUMELEM;
i++) {
368 if (!channelsToCalibrate[
i])
371 actuatorCommandFields.Channel[
i] = 0;
374 actuatorCommand->setData(actuatorCommandFields);
379 timeout.setSingleShot(
true);
380 connect(&timeout, &QTimer::timeout, &loop, &QEventLoop::quit);
385 for (
unsigned int i = 0;
i < ActuatorCommand::CHANNEL_NUMELEM;
i++) {
387 if (!channelsToCalibrate[
i])
390 actuatorCommandFields.Channel[
i] = actuatorSettingsData.ChannelMax[
i];
393 actuatorCommand->setData(actuatorCommandFields);
395 mbox.setText(QString(tr(
"Motors outputs were increased to maximum. "
396 "Reconnect the battery and wait for notification from ESCs that they "
397 "recognized high throttle position.<br/>"
398 "<b>Immediately after that proceed to next step.</b>")));
399 if (mbox.exec() == QMessageBox::Ok) {
401 for (
unsigned int i = 0;
i < ActuatorCommand::CHANNEL_NUMELEM;
i++) {
403 if (!channelsToCalibrate[
i])
410 double range = actuatorSettingsData.ChannelMax[
i] - actuatorSettingsData.ChannelMin[
i];
411 actuatorCommandFields.Channel[
i] = actuatorSettingsData.ChannelMin[
i]
412 + std::copysign(std::min(5.0, std::abs(range) / 40.0 + 0.5), range);
414 actuatorCommand->setData(actuatorCommandFields);
416 mbox.setStandardButtons(QMessageBox::Ok);
417 mbox.setDefaultButton(QMessageBox::Ok);
418 mbox.setText(QString(tr(
"Motors outputs were decreased to minimum.<br/>Wait for "
419 "notification from ESCs that calibration is finished.")));
424 actuatorCommand->setMetadata(accInitialData);
425 actuatorCommand->updated();
436 void ConfigOutputWidget::refreshWidgetsValues(
UAVObject *obj)
441 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(
getObjectManager());
442 Q_ASSERT(actuatorSettings);
443 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
446 assignOutputChannels(actuatorSettings);
449 m_config->spinningArmed->setChecked(actuatorSettingsData.MotorsSpinWhileArmed
450 == ActuatorSettings::MOTORSSPINWHILEARMED_TRUE);
463 for (
int i = 0; i < 6; i++) {
464 lblList.at(i)->setText(
"-");
465 rateList.at(i)->setEnabled(
false);
469 for (
int i = 0; i < banks.length(); i++) {
470 lblList.at(i)->setText(banks.at(i));
471 QComboBox *ccmb = rateList.at(i);
472 ccmb->setEnabled(
true);
474 QString setting = timerFreqToString(actuatorSettingsData.TimerUpdateFreq[i]);
475 if (ccmb->findText(setting) == -1) {
476 ccmb->addItem(setting);
478 ccmb->setCurrentIndex(ccmb->findText(setting));
483 m_config->motorCurveFit->setValue(actuatorSettingsData.MotorInputOutputCurveFit);
484 m_config->motorPowerGain->setValue(actuatorSettingsData.MotorInputOutputGain * 100);
489 quint32 minValue = actuatorSettingsData.ChannelMin[outputChannelForm->
index()];
490 quint32 maxValue = actuatorSettingsData.ChannelMax[outputChannelForm->
index()];
492 outputChannelForm->
setMinmax(minValue, maxValue);
494 quint32 neutral = actuatorSettingsData.ChannelNeutral[outputChannelForm->
index()];
498 refreshWidgetRanges();
501 void ConfigOutputWidget::refreshWidgetRanges()
514 for (
int i = 0; i < banks.size(); i++) {
515 QVector<int> bank = banks[
i];
520 QComboBox *ccmb = rateList.at(i);
521 quint32 timerFreq = timerStringToFreq(ccmb->currentText());
524 double maxPulseWidth, timerPeriodUs = UINT16_MAX;
525 int minPulseWidth = 0;
526 bool digital =
false;
531 maxPulseWidth = timerPeriodUs;
539 maxPulseWidth = 2047;
544 maxPulseWidth = 1000000 / timerFreq;
548 if (maxPulseWidth > timerPeriodUs)
549 maxPulseWidth = timerPeriodUs;
554 for (
auto channel : bank) {
556 for (
auto outputChannelForm : outputChannelForms) {
558 if (outputChannelForm->
index() != (channel - 1))
561 minPulseWidth, static_cast<int>(maxPulseWidth), digital);
573 void ConfigOutputWidget::updateObjectsFromWidgets()
577 ActuatorSettings *actuatorSettings = ActuatorSettings::GetInstance(
getObjectManager());
578 Q_ASSERT(actuatorSettings);
579 if (actuatorSettings) {
580 ActuatorSettings::DataFields actuatorSettingsData = actuatorSettings->getData();
584 actuatorSettingsData.ChannelMax[outputChannelForm->
index()] = outputChannelForm->
max();
585 actuatorSettingsData.ChannelMin[outputChannelForm->
index()] = outputChannelForm->
min();
586 actuatorSettingsData.ChannelNeutral[outputChannelForm->
index()] =
591 actuatorSettingsData.TimerUpdateFreq[0] =
592 timerStringToFreq(m_config->cb_outputRate1->currentText());
593 actuatorSettingsData.TimerUpdateFreq[1] =
594 timerStringToFreq(m_config->cb_outputRate2->currentText());
595 actuatorSettingsData.TimerUpdateFreq[2] =
596 timerStringToFreq(m_config->cb_outputRate3->currentText());
597 actuatorSettingsData.TimerUpdateFreq[3] =
598 timerStringToFreq(m_config->cb_outputRate4->currentText());
599 actuatorSettingsData.TimerUpdateFreq[4] =
600 timerStringToFreq(m_config->cb_outputRate5->currentText());
601 actuatorSettingsData.TimerUpdateFreq[5] =
602 timerStringToFreq(m_config->cb_outputRate6->currentText());
604 actuatorSettingsData.MotorInputOutputCurveFit = m_config->motorCurveFit->value();
605 actuatorSettingsData.MotorInputOutputGain = m_config->motorPowerGain->value() * 0.01;
607 if (m_config->spinningArmed->isChecked() ==
true)
608 actuatorSettingsData.MotorsSpinWhileArmed = ActuatorSettings::MOTORSSPINWHILEARMED_TRUE;
610 actuatorSettingsData.MotorsSpinWhileArmed =
611 ActuatorSettings::MOTORSSPINWHILEARMED_FALSE;
614 actuatorSettings->setData(actuatorSettingsData);
617 refreshWidgetRanges();
620 QString ConfigOutputWidget::timerFreqToString(quint32 freq)
const
622 static const QMap<quint32, QString> mapping{
623 { RATE_SYNCPWM, tr(
"SyncPWM") },
624 { RATE_DSHOT300, tr(
"Dshot300") },
625 { RATE_DSHOT600, tr(
"Dshot600") },
626 { RATE_DSHOT1200, tr(
"Dshot1200") },
628 return mapping.value(freq, QString::number(freq));
631 quint32 ConfigOutputWidget::timerStringToFreq(QString str)
const
633 static const QMap<QString, quint32> mapping{
634 { tr(
"SyncPWM"), RATE_SYNCPWM },
635 { tr(
"Dshot300"), RATE_DSHOT300 },
636 { tr(
"Dshot600"), RATE_DSHOT600 },
637 { tr(
"Dshot1200"), RATE_DSHOT1200 },
639 return mapping.value(str, str.toUInt());
642 void ConfigOutputWidget::tabSwitchingAway()
647 void ConfigOutputWidget::stopTests()
649 if (m_config->channelOutTest->isChecked()) {
650 qDebug() <<
"Output testing stopped by signal of incompatible mode";
653 m_config->channelOutTest->setChecked(
false);
659 void ConfigOutputWidget::do_SetDirty()
static ModeManager * instance()
virtual QVector< QVector< int > > getChannelBanks()
Get banks of output PWM channels banks on the board.
static void SetFlightAccess(Metadata &meta, AccessMode mode)
void currentModeAboutToChange(Core::IMode *mode)
Core plugin system that manages the plugins, their life cycle and their registered objects...
void channelChanged(int index, int value)
void setMinmax(int minimum, int maximum)
void setAssignment(const QString &assignment)
static void SetGcsTelemetryAcked(Metadata &meta, quint8 val)
static UpdateMode GetGcsTelemetryUpdateMode(const Metadata &meta)
virtual QStringList queryChannelBanks()
Query number & names of output PWM channels banks on the board.
void objectUpdated(UAVObject *obj)
Signal sent whenever any field of the object is updated.
void enableControls(bool enable)
static void SetFlightTelemetryUpdateMode(Metadata &meta, UpdateMode val)
ConfigOutputWidget(QWidget *parent=nullptr)
void enableChannelTest(bool state)
void updateChannelLimits(int minPulse, int maxPulse, bool digitalProtocol=false)
bool showOutputChannelSelectWindow(bool(&selectedChannels)[ActuatorCommand::CHANNEL_NUMELEM])
void setNeutral(int value)
static void SetGcsTelemetryUpdateMode(Metadata &meta, UpdateMode val)
void currentModeChanged(Core::IMode *mode)
UAVObject * getObject(const QString &name, quint32 instId=0)
Core::IBoardType * getBoardType()
Get the IBoardType corresponding to the connected board.
void importAboutToBegin()
static QStringList getChannelDescriptions()