[Companion][ModelEdit] Fix/improve module custom failsafe value settings (fixes #4443): (#4492)

* 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.
This commit is contained in:
Max Paperno
2017-02-26 13:00:49 +01:00
committed by Bertrand Songis
parent a1a58ad8f8
commit 171e08c2fd
3 changed files with 349 additions and 110 deletions
+262 -95
View File
@@ -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; i<maxChannels; i++) {
QLabel * label = new QLabel(this);
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 * 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<void(QButtonGroup::*)(int)>(&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<int, ChannelFailsafeWidgetsGroup> 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; i<maxChannels;i++) {
QString name = QString(model->limitData[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<int, ChannelFailsafeWidgetsGroup> 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<QComboBox *>(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)) {