From ec3c5bf1880d8af70fd5994832d18cbaa5647b16 Mon Sep 17 00:00:00 2001
From: Neil Horne <15316949+elecpower@users.noreply.github.com>
Date: Thu, 6 Feb 2025 13:05:31 +1100
Subject: [PATCH] fix(cpn): inputs and mixes must have source (#5798)
---
companion/src/companion.qrc | 3 +
companion/src/firmwares/modeldata.cpp | 60 ++++++++++++++
companion/src/firmwares/modeldata.h | 8 ++
companion/src/firmwares/radiodata.cpp | 16 ++++
companion/src/firmwares/radiodata.h | 2 +
.../src/images/originals/circle-green.svg | 48 +++++++++++
.../src/images/originals/circle-orange.svg | 48 +++++++++++
companion/src/images/originals/circle-red.svg | 48 +++++++++++
companion/src/images/svg/circle-green.svg | 48 +++++++++++
companion/src/images/svg/circle-orange.svg | 48 +++++++++++
companion/src/images/svg/circle-red.svg | 48 +++++++++++
companion/src/mainwindow.cpp | 6 +-
companion/src/mdichild.cpp | 83 +++++++++++++++++--
companion/src/mdichild.h | 16 +++-
companion/src/mdichild.ui | 3 +
companion/src/modeledit/modeledit.cpp | 3 +-
companion/src/modeledit/modeledit.h | 6 +-
companion/src/modelslist.cpp | 6 ++
radio/src/thirdparty/FreeRTOS | 2 +-
19 files changed, 481 insertions(+), 21 deletions(-)
create mode 100644 companion/src/images/originals/circle-green.svg
create mode 100644 companion/src/images/originals/circle-orange.svg
create mode 100644 companion/src/images/originals/circle-red.svg
create mode 100644 companion/src/images/svg/circle-green.svg
create mode 100644 companion/src/images/svg/circle-orange.svg
create mode 100644 companion/src/images/svg/circle-red.svg
diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc
index bd395a44389..8712699d382 100644
--- a/companion/src/companion.qrc
+++ b/companion/src/companion.qrc
@@ -10,6 +10,9 @@
images/track.png
images/track0.png
images/taranison.png
+ images/svg/circle-green.svg
+ images/svg/circle-orange.svg
+ images/svg/circle-red.svg
images/simulator/icons/svg/arrow_click.svg
images/simulator/icons/svg/camera.svg
images/simulator/icons/svg/camera-active.svg
diff --git a/companion/src/firmwares/modeldata.cpp b/companion/src/firmwares/modeldata.cpp
index 5f8ee4fc280..8bfac936a8e 100644
--- a/companion/src/firmwares/modeldata.cpp
+++ b/companion/src/firmwares/modeldata.cpp
@@ -29,6 +29,8 @@
#include "adjustmentreference.h"
#include "compounditemmodels.h"
+#include
+
ModelData::ModelData()
{
clear();
@@ -1924,3 +1926,61 @@ int ModelData::getCustomScreensCount() const
return cnt;
}
+
+void ModelData::validate()
+{
+ modelErrors = false;
+
+ for (int i = 0; i < CPN_MAX_INPUTS; i++) {
+ if (!expoData[i].isEmpty() && expoData[i].srcRaw == SOURCE_TYPE_NONE) {
+ modelErrors = true;
+ return;
+ }
+ }
+
+ for (int i = 0; i < CPN_MAX_MIXERS; i++) {
+ if (!mixData[i].isEmpty() && mixData[i].srcRaw == SOURCE_TYPE_NONE) {
+ modelErrors = true;
+ return;
+ }
+ }
+}
+
+QStringList ModelData::errorsList()
+{
+ QStringList list;
+
+ for (int i = 0; i < CPN_MAX_INPUTS; i++) {
+ if (!expoData[i].isEmpty() && expoData[i].srcRaw == SOURCE_TYPE_NONE)
+ list.append(tr("Error - Input %1 Line %2 %3").arg(expoData[i].chn + 1).arg(getInputLine(i)).arg(tr("has no source")));
+ }
+
+ for (int i = 0; i < CPN_MAX_MIXERS; i++) {
+ if (!mixData[i].isEmpty() && mixData[i].srcRaw == SOURCE_TYPE_NONE)
+ list.append(tr("Error - Mix %1 Line %2 %3").arg(mixData[i].destCh).arg(getMixLine(i)).arg(tr("has no source")));
+ }
+
+ return list;
+}
+
+int ModelData::getMixLine(int index) const
+{
+ int cnt = 1;
+
+ for (int i = index - 1; i >= 0 && mixData[i].destCh == mixData[index].destCh; i--)
+ cnt++;
+
+ return cnt;
+}
+
+int ModelData::getInputLine(int index) const
+{
+ int cnt = 1;
+
+ for (int i = 0; i < index; i++) {
+ if (expoData[i].chn == expoData[index].chn)
+ cnt++;
+ }
+
+ return cnt;
+}
diff --git a/companion/src/firmwares/modeldata.h b/companion/src/firmwares/modeldata.h
index 6ad033398a9..d4af9c1e1a8 100644
--- a/companion/src/firmwares/modeldata.h
+++ b/companion/src/firmwares/modeldata.h
@@ -140,6 +140,7 @@ class ModelData {
char labels[100];
int modelIndex; // Companion only, temporary index position managed by data model.
bool modelUpdated; // Companion only, used to highlight if changed in models list
+ bool modelErrors; // Companion only, used to highlight if data errors in models list
TimerData timers[CPN_MAX_TIMERS];
bool noGlobalFunctions;
@@ -366,11 +367,18 @@ class ModelData {
static AbstractStaticItemModel * funcSwitchStartItemModel();
int getCustomScreensCount() const;
+ bool hasErrors() { return modelErrors; }
+ bool isValid() { return !hasErrors(); }
+ void validate();
+ QStringList errorsList();
protected:
void removeGlobalVar(int & var);
private:
+ int getMixLine(int index) const;
+ int getInputLine(int index) const;
+
QVector *updRefList = nullptr;
struct UpdateReferenceInfo
diff --git a/companion/src/firmwares/radiodata.cpp b/companion/src/firmwares/radiodata.cpp
index be7f3eeb074..f5e10958346 100644
--- a/companion/src/firmwares/radiodata.cpp
+++ b/companion/src/firmwares/radiodata.cpp
@@ -343,3 +343,19 @@ AbstractStaticItemModel * RadioData::modelSortOrderItemModel()
mdl->loadItemList();
return mdl;
}
+
+void RadioData::validateModels()
+{
+ for(auto &model: models)
+ model.validate();
+}
+
+int RadioData::invalidModels()
+{
+ int cnt = 0;
+
+ for(auto &model: models)
+ cnt += model.isValid() ? 0 : 1;
+
+ return cnt;
+}
diff --git a/companion/src/firmwares/radiodata.h b/companion/src/firmwares/radiodata.h
index d8612b602f5..4263092b16b 100644
--- a/companion/src/firmwares/radiodata.h
+++ b/companion/src/firmwares/radiodata.h
@@ -66,6 +66,8 @@ class RadioData {
void setCurrentModel(unsigned int index);
void fixModelFilenames();
QString getNextModelFilename();
+ void validateModels();
+ int invalidModels();
static QString modelSortOrderToString(int value);
static AbstractStaticItemModel * modelSortOrderItemModel();
diff --git a/companion/src/images/originals/circle-green.svg b/companion/src/images/originals/circle-green.svg
new file mode 100644
index 00000000000..e8e8c790ae7
--- /dev/null
+++ b/companion/src/images/originals/circle-green.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/companion/src/images/originals/circle-orange.svg b/companion/src/images/originals/circle-orange.svg
new file mode 100644
index 00000000000..f23aaa15c54
--- /dev/null
+++ b/companion/src/images/originals/circle-orange.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/companion/src/images/originals/circle-red.svg b/companion/src/images/originals/circle-red.svg
new file mode 100644
index 00000000000..7080708e0ad
--- /dev/null
+++ b/companion/src/images/originals/circle-red.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/companion/src/images/svg/circle-green.svg b/companion/src/images/svg/circle-green.svg
new file mode 100644
index 00000000000..e8e8c790ae7
--- /dev/null
+++ b/companion/src/images/svg/circle-green.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/companion/src/images/svg/circle-orange.svg b/companion/src/images/svg/circle-orange.svg
new file mode 100644
index 00000000000..f23aaa15c54
--- /dev/null
+++ b/companion/src/images/svg/circle-orange.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/companion/src/images/svg/circle-red.svg b/companion/src/images/svg/circle-red.svg
new file mode 100644
index 00000000000..7080708e0ad
--- /dev/null
+++ b/companion/src/images/svg/circle-red.svg
@@ -0,0 +1,48 @@
+
+
+
+
diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp
index bad1760abf1..28729a362d4 100644
--- a/companion/src/mainwindow.cpp
+++ b/companion/src/mainwindow.cpp
@@ -676,9 +676,9 @@ void MainWindow::updateMenus()
saveAsAct->setEnabled(activeChild);
closeAct->setEnabled(activeChild);
compareAct->setEnabled(activeChild);
- writeSettingsAct->setEnabled(activeChild);
+ writeSettingsAct->setEnabled(activeChild && !activeMdiChild()->invalidModels());
readSettingsAct->setEnabled(true);
- writeSettingsSDPathAct->setEnabled(activeChild && isSDPathValid());
+ writeSettingsSDPathAct->setEnabled(activeChild && isSDPathValid() && !activeMdiChild()->invalidModels());
readSettingsSDPathAct->setEnabled(isSDPathValid());
writeBUToRadioAct->setEnabled(false);
readBUToFileAct->setEnabled(false);
@@ -889,10 +889,8 @@ void MainWindow::createActions()
// assigned menus in createMenus()
recentFilesAct = addAct("recentdocument.png");
closeAct = addAct("clear.png", SLOT(closeFile()) /*, QKeySequence::Close*/); // setting/showing this shortcut interferes with the system one (Ctrl+W/Ctrl-F4)
- // TODO change to more appropriate icon sets and uncomment toolbar actions
writeSettingsSDPathAct = addAct("folder-tree-write.png", SLOT(writeSettingsSDPath()));
readSettingsSDPathAct = addAct("folder-tree-read.png", SLOT(readSettingsSDPath()));
- // end TODO
exitAct = addAct("exit.png", SLOT(closeAllWindows()), QKeySequence::Quit, qApp);
editAppSettingsAct = addAct("apppreferences.png", SLOT(editAppSettings()), QKeySequence::Preferences);
diff --git a/companion/src/mdichild.cpp b/companion/src/mdichild.cpp
index ab13be5d3e0..28464f5b458 100644
--- a/companion/src/mdichild.cpp
+++ b/companion/src/mdichild.cpp
@@ -63,6 +63,7 @@ MdiChild::MdiChild(QWidget * parent, QWidget * parentWin, Qt::WindowFlags f):
if (parentWindow)
parentWindow->setWindowIcon(windowIcon());
+ setupStatusBar();
setupNavigation();
initModelsList();
@@ -227,11 +228,12 @@ void MdiChild::setupNavigation()
addAct(ACT_MDL_SIM, "simulate.png", SLOT(modelSimulate()), tr("Alt+S"));
addAct(ACT_MDL_DUP, "duplicate.png", SLOT(modelDuplicate()), QKeySequence::Underline);
- addAct(ACT_MDL_CUT, "cut.png", SLOT(cut()), QKeySequence::Cut);
- addAct(ACT_MDL_CPY, "copy.png", SLOT(copy()), QKeySequence::Copy);
- addAct(ACT_MDL_PST, "paste.png", SLOT(paste()), QKeySequence::Paste);
- addAct(ACT_MDL_INS, "list.png", SLOT(insert()), QKeySequence::Italic);
- addAct(ACT_MDL_EXP, "save.png", SLOT(modelExport()), tr("Ctrl+Alt+S"));
+ addAct(ACT_MDL_CUT, "cut.png", SLOT(cut()), QKeySequence::Cut);
+ addAct(ACT_MDL_CPY, "copy.png", SLOT(copy()), QKeySequence::Copy);
+ addAct(ACT_MDL_PST, "paste.png", SLOT(paste()), QKeySequence::Paste);
+ addAct(ACT_MDL_INS, "list.png", SLOT(insert()), QKeySequence::Italic);
+ addAct(ACT_MDL_EXP, "save.png", SLOT(modelExport()), tr("Ctrl+Alt+S"));
+ addAct(ACT_MDL_ERR, "information.png", SLOT(modelShowErrors()), tr("Ctrl+Alt+E"));
addAct(ACT_MDL_MOV, "arrow-right.png");
QMenu * catsMenu = new QMenu(this);
@@ -349,6 +351,7 @@ void MdiChild::updateNavigation()
cboModelSortOrder->setCurrentIndex(radioData.sortOrder);
cboModelSortOrder->blockSignals(false);
}
+ action[ACT_GEN_SIM]->setEnabled(!invalidModels());
action[ACT_GEN_SRT]->setVisible(hasLabels);
action[ACT_MDL_DEL]->setEnabled(modelsSelected);
@@ -369,7 +372,8 @@ void MdiChild::updateNavigation()
action[ACT_MDL_WIZ]->setEnabled(singleModelSelected);
action[ACT_MDL_DFT]->setEnabled(singleModelSelected && getCurrentModel() != (int)radioData.generalSettings.currModelIndex);
action[ACT_MDL_PRT]->setEnabled(singleModelSelected);
- action[ACT_MDL_SIM]->setEnabled(singleModelSelected);
+ action[ACT_MDL_SIM]->setEnabled(singleModelSelected && !invalidModels());
+ action[ACT_MDL_ERR]->setEnabled(singleModelSelected && radioData.models[getCurrentModel()].modelErrors);
}
void MdiChild::retranslateUi()
@@ -399,6 +403,7 @@ void MdiChild::retranslateUi()
action[ACT_MDL_PRT]->setText(tr("Print Model"));
action[ACT_MDL_SIM]->setText(tr("Simulate Model"));
action[ACT_MDL_DUP]->setText(tr("Duplicate Model"));
+ action[ACT_MDL_ERR]->setText(tr("Show Model Errors"));
radioToolbar->setWindowTitle(tr("Show Radio Actions Toolbar"));
modelsToolbar->setWindowTitle(tr("Show Model Actions Toolbar"));
@@ -444,6 +449,7 @@ QList MdiChild::getModelActions()
//actGrp.append(getAction(ACT_MDL_RTR));
actGrp.append(getAction(ACT_MDL_PRT));
actGrp.append(getAction(ACT_MDL_SIM));
+ actGrp.append(getAction(ACT_MDL_ERR));
return actGrp;
}
@@ -603,6 +609,7 @@ void MdiChild::refresh()
}
updateNavigation();
updateTitle();
+ updateStatusBar();
}
void MdiChild::onItemActivated(const QModelIndex index)
@@ -1204,6 +1211,7 @@ void MdiChild::openModelEditWindow(int row)
gStopwatch.report("ModelEdit created");
t->setWindowTitle(tr("Editing model %1: ").arg(row+1) + QString(model.name) + QString(" (%1)").arg(userFriendlyCurrentFile()));
connect(t, &ModelEdit::modified, this, &MdiChild::setCurrentModelModified);
+ connect(t, &ModelEdit::closed, this, &MdiChild::onModelEditClosed);
gStopwatch.report("STARTING MODEL EDIT");
t->show();
QApplication::restoreOverrideCursor();
@@ -1298,6 +1306,9 @@ bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
refresh();
}
+ radioData.validateModels();
+ updateStatusBar();
+
return true;
}
@@ -1344,8 +1355,10 @@ bool MdiChild::saveFile(const QString & filename, bool setCurrent)
g.eepromDir(QFileInfo(filename).dir().absolutePath());
for (int i = 0; i < (int)radioData.models.size(); i++) {
- if (!radioData.models[i].isEmpty())
+ if (!radioData.models[i].isEmpty()) {
radioData.models[i].modelUpdated = false;
+ radioData.models[i].validate();
+ }
}
refresh();
@@ -1486,6 +1499,13 @@ int MdiChild::askQuestion(const QString & msg, QMessageBox::StandardButtons butt
void MdiChild::writeSettings(StatusDialog * status, bool toRadio) // write to Tx
{
+ // safeguard as the menu actions should be disabled
+ int cnt = radioData.invalidModels();
+ if (cnt) {
+ QMessageBox::critical(this, tr("Write Models and Settings"), tr("Operation aborted as %1 models have significant errors that may affect model operation.").arg(cnt));
+ return;
+ }
+
if (g.confirmWriteModelsAndSettings()) {
QMessageBox msgbox;
msgbox.setText(tr("You are about to overwrite ALL models."));
@@ -1854,3 +1874,52 @@ QAction * MdiChild::actionsSeparator()
act->setSeparator(true);
return act;
}
+
+bool MdiChild::invalidModels()
+{
+ return (bool)radioData.invalidModels();
+}
+
+void MdiChild::modelShowErrors()
+{
+ ModelData &mdl = radioData.models[getCurrentModel()];
+ QMessageBox::critical(this, QString("%1").arg(mdl.name), mdl.errorsList().join("\n"));
+}
+
+void MdiChild::onModelEditClosed(int id)
+{
+ radioData.models[id].validate();
+ refresh();
+}
+
+void MdiChild::setupStatusBar()
+{
+ statusBar = new QStatusBar();
+ ui->statusBarLayout->addWidget(statusBar);
+ QLabel *lbl = new QLabel(tr("Models status"));
+ statusBar->addPermanentWidget(lbl);
+ statusBarIcon = new QLabel();
+ statusBar->addPermanentWidget(statusBarIcon);
+ statusBarCount = new QLabel();
+ statusBar->addPermanentWidget(statusBarCount);
+}
+
+void MdiChild::updateStatusBar()
+{
+ QPixmap p;
+ QLabel cnt;
+ int invalid = radioData.invalidModels();
+
+ if (!invalidModels()) {
+ statusBarIcon->setToolTip(tr("No errors"));
+ p.load(":/images/svg/circle-green.svg");
+ }
+ else {
+ statusBarIcon->setToolTip(tr("Errors"));
+ p.load(":/images/svg/circle-red.svg");
+ cnt.setText(QString::number(invalid));
+ }
+
+ statusBarIcon->setPixmap(p.scaled(QSize(16, 16)));
+ statusBarCount->setText(cnt.text());
+}
diff --git a/companion/src/mdichild.h b/companion/src/mdichild.h
index 49f4cbc346c..65e4ec9761b 100644
--- a/companion/src/mdichild.h
+++ b/companion/src/mdichild.h
@@ -19,8 +19,7 @@
* GNU General Public License for more details.
*/
-#ifndef _MDICHILD_H_
-#define _MDICHILD_H_
+#pragma once
#include "eeprominterface.h"
#include "modelslist.h"
@@ -33,6 +32,7 @@
#include
#include
#include
+#include
class QToolBar;
class StatusDialog;
@@ -70,6 +70,7 @@ class MdiChild : public QWidget
ACT_MDL_DFT, // set as DeFaulT
ACT_MDL_PRT, // print
ACT_MDL_SIM,
+ ACT_MDL_ERR,
ACT_LBL_ADD, // label actions..
ACT_LBL_DEL,
ACT_LBL_MVU, // Move up
@@ -89,6 +90,7 @@ class MdiChild : public QWidget
QList getModelActions();
QList getLabelsActions();
QAction * getAction(const Actions type);
+ bool invalidModels();
public slots:
void newFile(bool createDefaults = true);
@@ -130,6 +132,7 @@ class MdiChild : public QWidget
void onCurrentItemChanged(const QModelIndex &, const QModelIndex &);
void onDataChanged(const QModelIndex & index);
void onInternalModuleChanged();
+ void onModelEditClosed(int id);
void generalEdit();
void copyGeneralSettings();
@@ -153,6 +156,7 @@ class MdiChild : public QWidget
void labelsFault(QString msg);
void wizardEdit();
void modelDuplicate();
+ void modelShowErrors();
void openModelWizard(int row = -1);
void openModelEditWindow(int row = -1);
@@ -201,6 +205,7 @@ class MdiChild : public QWidget
bool convertStorage(Board::Type from, Board::Type to, bool newFile = false);
void showWarning(const QString & msg);
int askQuestion(const QString & msg, QMessageBox::StandardButtons buttons = (QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::No);
+
QDialog * getChildDialog(QRegularExpression & regexp);
QDialog * getModelEditDialog(int row);
QList * getChildrenDialogsList(QRegularExpression & regexp);
@@ -219,6 +224,9 @@ class MdiChild : public QWidget
QToolBar * modelsToolbar;
QToolBar * labelsToolbar;
QLabel *lblLabels;
+ QStatusBar *statusBar;
+ QLabel *statusBarIcon;
+ QLabel *statusBarCount;
Firmware * firmware;
RadioData radioData;
@@ -232,6 +240,8 @@ class MdiChild : public QWidget
QComboBox* cboModelSortOrder;
void setModelModified(const int modelIndex, bool cascade = true);
QAction * actionsSeparator();
+ void setupStatusBar();
+ void updateStatusBar();
};
// This will draw the drop indicator across all columns of a model View (vs. in just one column), and lets us make the indicator more obvious.
@@ -262,5 +272,3 @@ class ItemViewProxyStyle: public QProxyStyle
}
}
};
-
-#endif // _MDICHILD_H_
diff --git a/companion/src/mdichild.ui b/companion/src/mdichild.ui
index 7ba95757fa0..439caa168b8 100644
--- a/companion/src/mdichild.ui
+++ b/companion/src/mdichild.ui
@@ -51,6 +51,9 @@
+ -
+
+
diff --git a/companion/src/modeledit/modeledit.cpp b/companion/src/modeledit/modeledit.cpp
index deb753d45dc..49a73d300e7 100644
--- a/companion/src/modeledit/modeledit.cpp
+++ b/companion/src/modeledit/modeledit.cpp
@@ -140,7 +140,8 @@ ModelEdit::~ModelEdit()
void ModelEdit::closeEvent(QCloseEvent *event)
{
- g.modelEditGeo( saveGeometry() );
+ g.modelEditGeo(saveGeometry());
+ emit closed(modelId);
}
void ModelEdit::addTab(GenericPanel *panel, QString text)
diff --git a/companion/src/modeledit/modeledit.h b/companion/src/modeledit/modeledit.h
index 13c27dce586..f3211bd4a54 100644
--- a/companion/src/modeledit/modeledit.h
+++ b/companion/src/modeledit/modeledit.h
@@ -19,8 +19,7 @@
* GNU General Public License for more details.
*/
-#ifndef _MODELEDIT_H_
-#define _MODELEDIT_H_
+#pragma once
#include
#include "genericpanel.h"
@@ -56,6 +55,7 @@ class ModelEdit : public QDialog
signals:
void modified();
+ void closed(int id);
private slots:
void onTabIndexChanged(int index);
@@ -72,5 +72,3 @@ class ModelEdit : public QDialog
void launchSimulation();
};
-
-#endif // _MODELEDIT_H_
diff --git a/companion/src/modelslist.cpp b/companion/src/modelslist.cpp
index 5bb14f5ce56..bea7df78a62 100644
--- a/companion/src/modelslist.cpp
+++ b/companion/src/modelslist.cpp
@@ -186,6 +186,12 @@ QVariant ModelsListModel::data(const QModelIndex & index, int role) const
}
if (role == Qt::ForegroundRole && item->isModel()) {
+ if (index.column() == (hasLabels ? 0 : 1) && radioData->models[item->getModelIndex()].modelErrors) {
+ QBrush brush;
+ brush.setColor(Qt::red);
+ return brush;
+ }
+
int col = item->columnCount() - 1;
if(hasLabels)
col --;
diff --git a/radio/src/thirdparty/FreeRTOS b/radio/src/thirdparty/FreeRTOS
index dbf70559b27..a4b28e35103 160000
--- a/radio/src/thirdparty/FreeRTOS
+++ b/radio/src/thirdparty/FreeRTOS
@@ -1 +1 @@
-Subproject commit dbf70559b27d39c1fdb68dfb9a32140b6a6777a0
+Subproject commit a4b28e35103d699edf074dfff4835921b481b301