[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)) {
+25 -14
View File
@@ -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<int, ChannelFailsafeWidgetsGroup> failsafeGroupsMap;
static quint8 failsafesValueDisplayType; // FailsafeValueDisplayTypes
};
class SetupPanel : public ModelPanel
+62 -1
View File
@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>674</width>
<height>205</height>
<height>229</height>
</rect>
</property>
<property name="windowTitle">
@@ -657,6 +657,10 @@
</item>
<item row="1" column="0" colspan="3">
<widget class="QGroupBox" name="failsafesGroupBox">
<property name="styleSheet">
<string notr="true">QGroupBox {padding-top: -10px; margin-top: 5px;}
QGroupBox::title { subcontrol-origin: margin; subcontrol-position: top left; padding-left: 10px; padding-right: 10px; }</string>
</property>
<property name="title">
<string>Failsafe Positions</string>
</property>
@@ -676,6 +680,60 @@
<property name="bottomMargin">
<number>4</number>
</property>
<item alignment="Qt::AlignRight">
<widget class="QWidget" name="widgetValueType" native="true">
<property name="autoFillBackground">
<bool>true</bool>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>5</number>
</property>
<property name="leftMargin">
<number>12</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>12</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Show values in:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="optPercent">
<property name="text">
<string extracomment="abbreviation for percent">%</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
<attribute name="buttonGroup">
<string notr="true">btnGrpValueType</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="optUs">
<property name="text">
<string extracomment="abbreviation for microseconds">μs</string>
</property>
<attribute name="buttonGroup">
<string notr="true">btnGrpValueType</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="failsafesLayout">
<property name="spacing">
@@ -725,4 +783,7 @@
</tabstops>
<resources/>
<connections/>
<buttongroups>
<buttongroup name="btnGrpValueType"/>
</buttongroups>
</ui>