From 171e08c2fd95e46c870e184d798c184b56b50f54 Mon Sep 17 00:00:00 2001 From: Max Paperno Date: Sun, 26 Feb 2017 07:00:49 -0500 Subject: [PATCH] [Companion][ModelEdit] Fix/improve module custom failsafe value settings (fixes #4443): (#4492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Properly set limits based on model's Extended Limits setting; * Fix rounding issues between percentile display and underlying value in μs; * Add ability to display values in μs as well as percent. * Channels numbering & count is responsive to module's defined start/end channels. --- companion/src/modeledit/setup.cpp | 357 +++++++++++++++++------- companion/src/modeledit/setup.h | 39 ++- companion/src/modeledit/setup_module.ui | 63 ++++- 3 files changed, 349 insertions(+), 110 deletions(-) diff --git a/companion/src/modeledit/setup.cpp b/companion/src/modeledit/setup.cpp index cd67f0a9d..b2498e83e 100644 --- a/companion/src/modeledit/setup.cpp +++ b/companion/src/modeledit/setup.cpp @@ -148,6 +148,18 @@ void TimerPanel::on_name_editingFinished() #define FAILSAFE_CHANNEL_HOLD 2000 #define FAILSAFE_CHANNEL_NOPULSE 2001 +#define MASK_PROTOCOL 1 +#define MASK_CHANNELS_COUNT 2 +#define MASK_RX_NUMBER 4 +#define MASK_CHANNELS_RANGE 8 +#define MASK_PPM_FIELDS 16 +#define MASK_FAILSAFES 32 +#define MASK_OPEN_DRAIN 64 +#define MASK_MULTIMODULE 128 +#define MASK_ANTENNA 256 + +quint8 ModulePanel::failsafesValueDisplayType = ModulePanel::FAILSAFE_DISPLAY_PERCENT; + ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx): ModelPanel(parent, model, generalSettings, firmware), module(module), @@ -210,36 +222,17 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul ui->multiProtocol->addItem(ModelPrinter::printMultiRfProtocol(i, false), (QVariant) i); } - if (firmware->getCapability(HasFailsafe)) { - for (int i=0; isetText(QString::number(i+1)); - QComboBox * combo = new QComboBox(this); - combo->setProperty("index", i); - combo->addItem(tr("Value"), 0); - combo->addItem(tr("Hold"), FAILSAFE_CHANNEL_HOLD); - combo->addItem(tr("No Pulse"), FAILSAFE_CHANNEL_NOPULSE); - QDoubleSpinBox * spinbox = new QDoubleSpinBox(this); - spinbox->setMinimumSize(QSize(20, 0)); - spinbox->setRange(-150, 150); - spinbox->setSingleStep(0.1); - spinbox->setDecimals(1); - label->setProperty("index", i); - spinbox->setProperty("index", i); - ui->failsafesLayout->addWidget(label, 3*(i/8), i%8, Qt::AlignHCenter); - ui->failsafesLayout->addWidget(combo, 1+3*(i/8), i%8, Qt::AlignHCenter); - ui->failsafesLayout->addWidget(spinbox, 2+3*(i/8), i%8, Qt::AlignHCenter); - failsafeGroups[i].combo = combo; - failsafeGroups[i].spinbox = spinbox; - failsafeGroups[i].label = label; - updateFailsafe(i); - connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFailsafeComboIndexChanged(int))); - connect(spinbox, SIGNAL(valueChanged(double)), this, SLOT(onFailsafeSpinChanged(double))); - } - } + ui->btnGrpValueType->setId(ui->optPercent, FAILSAFE_DISPLAY_PERCENT); + ui->btnGrpValueType->setId(ui->optUs, FAILSAFE_DISPLAY_USEC); + ui->btnGrpValueType->button(failsafesValueDisplayType)->setChecked(true); + + setupFailsafes(); disableMouseScrolling(); + connect(this, &ModulePanel::channelsRangeChanged, this, &ModulePanel::setupFailsafes); + connect(ui->btnGrpValueType, static_cast(&QButtonGroup::buttonClicked), this, &ModulePanel::onFailsafesDisplayValueTypeChanged); + lock = false; // a change will not register if only one item was added @@ -253,15 +246,102 @@ ModulePanel::~ModulePanel() delete ui; } -#define MASK_PROTOCOL 1 -#define MASK_CHANNELS_COUNT 2 -#define MASK_RX_NUMBER 4 -#define MASK_CHANNELS_RANGE 8 -#define MASK_PPM_FIELDS 16 -#define MASK_FAILSAFES 32 -#define MASK_OPEN_DRAIN 64 -#define MASK_MULTIMODULE 128 -#define MASK_ANTENNA 256 +bool ModulePanel::moduleHasFailsafes() +{ + return ((PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_XJT_X16 && firmware->getCapability(HasFailsafe));; +} + +void ModulePanel::setupFailsafes() +{ + ChannelFailsafeWidgetsGroup grp; + const int start = module.channelsStart; + const int end = start + module.channelsCount; + const bool hasFailsafe = moduleHasFailsafes(); + + lock = true; + + QMutableMapIterator i(failsafeGroupsMap); + while (i.hasNext()) { + i.next(); + grp = i.value(); + ui->failsafesLayout->removeWidget(grp.label); + ui->failsafesLayout->removeWidget(grp.combo); + ui->failsafesLayout->removeWidget(grp.sbPercent); + ui->failsafesLayout->removeWidget(grp.sbUsec); + if (i.key() < start || i.key() >= end || !hasFailsafe) { + grp.label->deleteLater(); + grp.combo->deleteLater(); + grp.sbPercent->deleteLater(); + grp.sbUsec->deleteLater(); + i.remove(); + } + } + + if (!hasFailsafe) + return; + + int row = 0; + int col = 0; + int channelMax = model->getChannelsMax(); + int channelMaxUs = 512 * channelMax / 100 * 2; + + for (int i = start; i < end; ++i) { + if (failsafeGroupsMap.contains(i)) { + grp = failsafeGroupsMap.value(i); + } + else { + QLabel * label = new QLabel(this); + label->setProperty("index", i); + label->setText(QString::number(i+1)); + + QComboBox * combo = new QComboBox(this); + combo->setProperty("index", i); + combo->addItem(tr("Value"), 0); + combo->addItem(tr("Hold"), FAILSAFE_CHANNEL_HOLD); + combo->addItem(tr("No Pulse"), FAILSAFE_CHANNEL_NOPULSE); + + QDoubleSpinBox * sbDbl = new QDoubleSpinBox(this); + sbDbl->setProperty("index", i); + sbDbl->setMinimumSize(QSize(20, 0)); + sbDbl->setRange(-channelMax, channelMax); + sbDbl->setSingleStep(0.1); + sbDbl->setDecimals(1); + + QSpinBox * sbInt = new QSpinBox(this); + sbInt->setProperty("index", i); + sbInt->setMinimumSize(QSize(20, 0)); + sbInt->setRange(-channelMaxUs, channelMaxUs); + sbInt->setSingleStep(1); + + grp = ChannelFailsafeWidgetsGroup(); + grp.combo = combo; + grp.sbPercent = sbDbl; + grp.sbUsec = sbInt; + grp.label = label; + failsafeGroupsMap.insert(i, grp); + + connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(onFailsafeComboIndexChanged(int))); + connect(sbInt, SIGNAL(valueChanged(int)), this, SLOT(onFailsafeUsecChanged(int))); + connect(sbDbl, SIGNAL(valueChanged(double)), this, SLOT(onFailsafePercentChanged(double))); + } + + ui->failsafesLayout->addWidget(grp.label, row, col, Qt::AlignHCenter); + ui->failsafesLayout->addWidget(grp.combo, row + 1, col, Qt::AlignHCenter); + ui->failsafesLayout->addWidget(grp.sbPercent, row + 2, col, Qt::AlignHCenter); + ui->failsafesLayout->addWidget(grp.sbUsec, row + 3, col, Qt::AlignHCenter); + grp.sbPercent->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_PERCENT); + grp.sbUsec->setVisible(failsafesValueDisplayType == FAILSAFE_DISPLAY_USEC); + + updateFailsafe(i); + + if (++col > 7) { + row += 4; + col = 0; + } + } + + lock = false; +} void ModulePanel::update() { @@ -277,9 +357,10 @@ void ModulePanel::update() case PULSES_PXX_XJT_LR12: case PULSES_PXX_DJT: mask |= MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; - if (protocol==PULSES_PXX_XJT_X16) mask |= MASK_FAILSAFES | MASK_RX_NUMBER; - if (protocol==PULSES_PXX_XJT_LR12) mask |= MASK_RX_NUMBER; - if (IS_HORUS(firmware->getBoard()) && moduleIdx==0) mask |= MASK_ANTENNA; + if (protocol==PULSES_PXX_XJT_X16 || protocol==PULSES_PXX_XJT_LR12) + mask |= MASK_RX_NUMBER; + if (IS_HORUS(firmware->getBoard()) && moduleIdx==0) + mask |= MASK_ANTENNA; break; case PULSES_LP45: case PULSES_DSM2: @@ -316,6 +397,9 @@ void ModulePanel::update() mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; } + if (moduleHasFailsafes()) + mask |= MASK_FAILSAFES; + ui->label_protocol->setVisible(mask & MASK_PROTOCOL); ui->protocol->setVisible(mask & MASK_PROTOCOL); ui->label_rxNumber->setVisible(mask & MASK_RX_NUMBER); @@ -380,28 +464,37 @@ void ModulePanel::update() ui->lowPower->setChecked(module.multi.lowPowerMode ? Qt::Checked : Qt::Unchecked); - if (firmware->getCapability(HasFailsafe)) { - ui->label_failsafeMode->setVisible(mask & MASK_FAILSAFES); - ui->failsafeMode->setVisible(mask & MASK_FAILSAFES); - ui->failsafeMode->setCurrentIndex(module.failsafeMode); - ui->failsafesGroupBox->setEnabled(module.failsafeMode == FAILSAFE_CUSTOM); - if (firmware->getCapability(ChannelsName) > 0) { - for(int i=0; ilimitData[i+module.channelsStart].name).trimmed(); - if (!name.isEmpty()) { - failsafeGroups[i].label->setText(name); - } - else { - failsafeGroups[i].label->setText(QString::number(i+1)); - } - } + ui->label_failsafeMode->setVisible(mask & MASK_FAILSAFES); + ui->failsafeMode->setVisible(mask & MASK_FAILSAFES); + + if ((mask & MASK_FAILSAFES) && module.failsafeMode == FAILSAFE_CUSTOM) { + if (ui->failsafesGroupBox->isHidden()) { + setupFailsafes(); + ui->failsafesGroupBox->setVisible(true); } } else { - mask = 0; + ui->failsafesGroupBox->setVisible(false); } - ui->failsafesGroupBox->setVisible((mask & MASK_FAILSAFES) && module.failsafeMode == FAILSAFE_CUSTOM); + if (mask & MASK_FAILSAFES) { + ui->failsafeMode->setCurrentIndex(module.failsafeMode); + + if (firmware->getCapability(ChannelsName)) { + int chan; + QString name; + QMapIterator i(failsafeGroupsMap); + while (i.hasNext()) { + i.next(); + chan = i.key(); + name = QString(model->limitData[chan + module.channelsStart].name).trimmed(); + if (name.isEmpty()) + i.value().label->setText(QString::number(chan + 1)); + else + i.value().label->setText(name); + } + } + } if (mask & MASK_CHANNELS_RANGE) { ui->channelsStart->setMaximum(33 - ui->channelsCount->value()); @@ -450,6 +543,7 @@ void ModulePanel::on_channelsCount_editingFinished() if (!lock) { module.channelsCount = ui->channelsCount->value(); update(); + emit channelsRangeChanged(); emit modified(); } } @@ -459,6 +553,7 @@ void ModulePanel::on_channelsStart_editingFinished() if (!lock) { module.channelsStart = ui->channelsStart->value() - 1; update(); + emit channelsRangeChanged(); emit modified(); } } @@ -493,27 +588,6 @@ void ModulePanel::on_failsafeMode_currentIndexChanged(int value) } } -void ModulePanel::onFailsafeSpinChanged(double value) -{ - if (!lock) { - int channel = sender()->property("index").toInt(); - module.failsafeChannels[channel] = (value*1024)/100; - emit modified(); - } -} - -void ModulePanel::onFailsafeComboIndexChanged(int index) -{ - if (!lock) { - lock = true; - int channel = sender()->property("index").toInt(); - module.failsafeChannels[channel] = ((QComboBox *)sender())->itemData(index).toInt(); - updateFailsafe(channel); - emit modified(); - lock = false; - } -} - void ModulePanel::on_multiProtocol_currentIndexChanged(int index) { if (!lock) { @@ -549,26 +623,118 @@ void ModulePanel::on_lowPower_stateChanged(int state) module.multi.lowPowerMode = (state == Qt::Checked); } +// updtSb (update spin box(es)): 0=none or bitmask of FailsafeValueDisplayTypes +void ModulePanel::setChannelFailsafeValue(const int channel, const int value, quint8 updtSb) +{ + if (channel < 0 || channel >= CPN_MAX_CHNOUT) + return; + + module.failsafeChannels[channel] = value; + double pctVal = divRoundClosest(value * 1000, 1024) / 10.0; + // qDebug() << value << pctVal; + + if (failsafeGroupsMap.contains(channel)) { + const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); + if ((updtSb & FAILSAFE_DISPLAY_PERCENT) && grp.sbPercent) { + grp.sbPercent->blockSignals(true); + grp.sbPercent->setValue(pctVal); + grp.sbPercent->blockSignals(false); + } + if ((updtSb & FAILSAFE_DISPLAY_USEC) && grp.sbUsec) { + grp.sbUsec->blockSignals(true); + grp.sbUsec->setValue(value); + grp.sbUsec->blockSignals(false); + } + } + if (!lock) + emit modified(); +} + +void ModulePanel::onFailsafeUsecChanged(int value) +{ + if (!sender()) + return; + + bool ok = false; + int channel = sender()->property("index").toInt(&ok); + if (ok) + setChannelFailsafeValue(channel, value, FAILSAFE_DISPLAY_PERCENT); +} + +void ModulePanel::onFailsafePercentChanged(double value) +{ + if (!sender()) + return; + + bool ok = false; + int channel = sender()->property("index").toInt(&ok); + if (ok) + setChannelFailsafeValue(channel, divRoundClosest(int(value * 1024), 100), FAILSAFE_DISPLAY_USEC); +} + +void ModulePanel::onFailsafeComboIndexChanged(int index) +{ + if (!sender()) + return; + + QComboBox * cb = qobject_cast(sender()); + + if (cb && !lock) { + lock = true; + bool ok = false; + int channel = sender()->property("index").toInt(&ok); + if (ok) { + module.failsafeChannels[channel] = cb->itemData(index).toInt(); + updateFailsafe(channel); + emit modified(); + } + lock = false; + } +} + +void ModulePanel::onFailsafesDisplayValueTypeChanged(int type) +{ + if (failsafesValueDisplayType != type) { + failsafesValueDisplayType = type; + foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { + if (grp.sbPercent) + grp.sbPercent->setVisible(type == FAILSAFE_DISPLAY_PERCENT); + if (grp.sbUsec) + grp.sbUsec->setVisible(type == FAILSAFE_DISPLAY_USEC); + } + } +} + +void ModulePanel::onExtendedLimitsToggled() +{ + double channelMaxPct = double(model->getChannelsMax()); + int channelMaxUs = 512 * channelMaxPct / 100 * 2; + foreach (ChannelFailsafeWidgetsGroup grp, failsafeGroupsMap) { + if (grp.sbPercent) + grp.sbPercent->setRange(-channelMaxPct, channelMaxPct); + if (grp.sbUsec) + grp.sbUsec->setRange(-channelMaxUs, channelMaxUs); + } +} + void ModulePanel::updateFailsafe(int channel) { - int failsafeValue = module.failsafeChannels[channel]; - QComboBox * combo = failsafeGroups[channel].combo; - QDoubleSpinBox * spinbox = failsafeGroups[channel].spinbox; - if (failsafeValue == FAILSAFE_CHANNEL_HOLD) { - combo->setCurrentIndex(1); - spinbox->setEnabled(false); - spinbox->setValue(0); - } - else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { - combo->setCurrentIndex(2); - spinbox->setEnabled(false); - spinbox->setValue(0); - } - else { - combo->setCurrentIndex(0); - spinbox->setEnabled(true); - spinbox->setValue(((double)failsafeValue*100)/1024); - } + if (channel >= CPN_MAX_CHNOUT || !failsafeGroupsMap.contains(channel)) + return; + + const int failsafeValue = module.failsafeChannels[channel]; + const ChannelFailsafeWidgetsGroup & grp = failsafeGroupsMap.value(channel); + const bool valDisable = (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE); + const int cbIdx = (failsafeValue == FAILSAFE_CHANNEL_HOLD ? 1 : failsafeValue == FAILSAFE_CHANNEL_NOPULSE ? 2 : 0); + + if (grp.combo) + grp.combo->setCurrentIndex(cbIdx); + if (grp.sbPercent) + grp.sbPercent->setDisabled(valDisable); + if (grp.sbUsec) + grp.sbUsec->setDisabled(valDisable); + + setChannelFailsafeValue(channel, (valDisable ? 0 : failsafeValue), (FAILSAFE_DISPLAY_PERCENT | FAILSAFE_DISPLAY_USEC)); } /******************************************************************************/ @@ -796,6 +962,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge modules[i] = new ModulePanel(this, model, model.moduleData[i], generalSettings, firmware, i); ui->modulesLayout->addWidget(modules[i]); connect(modules[i], SIGNAL(modified()), this, SLOT(onChildModified())); + connect(this, &SetupPanel::extendedLimitsToggled, modules[i], &ModulePanel::onExtendedLimitsToggled); } if (firmware->getCapability(ModelTrainerEnable)) { diff --git a/companion/src/modeledit/setup.h b/companion/src/modeledit/setup.h index 72e744049..18a8acf41 100644 --- a/companion/src/modeledit/setup.h +++ b/companion/src/modeledit/setup.h @@ -54,25 +54,22 @@ class TimerPanel : public ModelPanel class ModulePanel : public ModelPanel { - static const int maxChannels = 16; - - struct ChannelFailsafeWidgetsGroup { - QComboBox * combo; - QDoubleSpinBox * spinbox; - QLabel * label; - }; - - Q_OBJECT + Q_OBJECT public: ModulePanel(QWidget *parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx); virtual ~ModulePanel(); virtual void update(); + bool moduleHasFailsafes(); - protected: - void updateFailsafe(int channel); + public slots: + void onExtendedLimitsToggled(); + + signals: + void channelsRangeChanged(); private slots: + void setupFailsafes(); void on_trainerMode_currentIndexChanged(int index); void on_protocol_currentIndexChanged(int index); void on_ppmDelay_editingFinished(); @@ -84,18 +81,32 @@ class ModulePanel : public ModelPanel void on_antennaMode_currentIndexChanged(int index); void on_rxNumber_editingFinished(); void on_failsafeMode_currentIndexChanged(int value); - void onFailsafeComboIndexChanged(int index); - void onFailsafeSpinChanged(double value); void on_multiProtocol_currentIndexChanged(int index); void on_multiSubType_currentIndexChanged(int index); void on_autoBind_stateChanged(int state); void on_lowPower_stateChanged(int state); + void setChannelFailsafeValue(const int channel, const int value, quint8 updtSb = 0); + void onFailsafeComboIndexChanged(int index); + void onFailsafeUsecChanged(int value); + void onFailsafePercentChanged(double value); + void onFailsafesDisplayValueTypeChanged(int type); + void updateFailsafe(int channel); private: + enum FailsafeValueDisplayTypes { FAILSAFE_DISPLAY_PERCENT = 1, FAILSAFE_DISPLAY_USEC = 2 }; + + struct ChannelFailsafeWidgetsGroup { + QLabel * label; + QComboBox * combo; + QSpinBox * sbUsec; + QDoubleSpinBox * sbPercent; + }; + ModuleData & module; int moduleIdx; Ui::Module *ui; - ChannelFailsafeWidgetsGroup failsafeGroups[maxChannels]; + QMap failsafeGroupsMap; + static quint8 failsafesValueDisplayType; // FailsafeValueDisplayTypes }; class SetupPanel : public ModelPanel diff --git a/companion/src/modeledit/setup_module.ui b/companion/src/modeledit/setup_module.ui index d5a9c8e3d..6677b468b 100644 --- a/companion/src/modeledit/setup_module.ui +++ b/companion/src/modeledit/setup_module.ui @@ -7,7 +7,7 @@ 0 0 674 - 205 + 229 @@ -657,6 +657,10 @@ + + QGroupBox {padding-top: -10px; margin-top: 5px;} +QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding-left: 10px; padding-right: 10px; } + Failsafe Positions @@ -676,6 +680,60 @@ 4 + + + + true + + + + 5 + + + 12 + + + 0 + + + 12 + + + 2 + + + + + Show values in: + + + + + + + % + + + true + + + btnGrpValueType + + + + + + + μs + + + btnGrpValueType + + + + + + @@ -725,4 +783,7 @@ + + +