Companion 2.5 (#555)

* Introduce yaml in Companion

* EdgeTX Companion
Replace some OpenTX refs
Data conversion to v220
Update Timers ui to cater for mode field split

* Fix some simulators crashing when started from Companion

* Disable firmware and app update checking and downloads from companion

* Some more rebranding

* Disable manual download actions and another rebrand

* Fix cmake warning

* Fix mode ON conversion gtests and enable switch in model edit

* Stop update message when updates turned off

* OpenTX #8580 Fixes missing negative sign for telemetry sources

* OpenTX #8604 fix curves tooltip colors

* OpenTX #8616 Fix release notes url references

* OpenTX #8548 Align file sync folder references

* OpenTX #8548 Link field once

* OpenTX #8550 Confirmation prompt before write models and settings to radio

* OpenTX #8550 Translate strings

* Title and splash images

* Change DEB package maintainer

* Start NV14

* NV14 continued

* NV14 sim

* Timer countdownStart data conversion and ui fixes

* Fix screen width

* Merge OpenTX PR 8675

* NV14 sim add computer keys and mouse to activate some functions

* Only allow supported protocols for each module on conversion

* Clear mismatched modules on conversion

* Replace colorlcd theme and layout blobs with detailed data structs in companion

* NV14 has 2 topbar zones

* Fix layout init

* Fix layout init again

* Reset to eepromimportexport.h to main branch

* Import/export does not like enum fields

* Model edit add custom screens tab for color and bw telemetry

* Free memory
This commit is contained in:
Neil Horne
2021-09-08 10:56:39 +10:00
committed by GitHub
parent 5ce08bb396
commit a9812688b0
70 changed files with 2656 additions and 739 deletions
+6 -6
View File
@@ -19,7 +19,7 @@ endif()
# This the name that the user will see in the generated DMG and what the application
# will be called under /Applications. We include the version string to make installing
# different versions for different eeproms easier, i.e. without manually renaming
set(COMPANION_OSX_APP_BUNDLE_NAME "OpenTX Companion ${VERSION_MAJOR}.${VERSION_MINOR}")
set(COMPANION_OSX_APP_BUNDLE_NAME "EdgeTX Companion ${VERSION_MAJOR}.${VERSION_MINOR}")
# On OS X we copy dfu-util to the application bundle. This the path from which we copy
# the binary (default homebrew path)
@@ -494,7 +494,7 @@ if(WIN32)
endif()
set(CPACK_PACKAGE_NAME "companion${C9X_NAME_SUFFIX}")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Models and settings editor for the OpenTX open source firmware")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Models and settings editor for the EdgeTX open source firmware")
string(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
# The file stripping is deliberately disabled, with the stripped file we get
@@ -515,8 +515,8 @@ IF(APPLE)
# Use a non standard Info.plist that adds Retina support flags
set_target_properties(${COMPANION_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/companion/targets/mac/MacOSXBundleInfo.plist.in)
set_target_properties(${SIMULATOR_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${PROJECT_SOURCE_DIR}/companion/targets/mac/MacOSXBundleInfo.plist.in)
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.opentx.companion")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.edgetx.companion")
INSTALL(TARGETS ${COMPANION_NAME}
BUNDLE DESTINATION . COMPONENT Runtime
@@ -584,7 +584,7 @@ if(DPKG_PROGRAM)
OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Bertrand Songis <bsongis-nosp@m-gmail.com>")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Raphael Coeffic")
set(CPACK_DEBIAN_PACKAGE_VERSION ${VERSION})
set(CPACK_DEBIAN_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR})
set(CPACK_DEBIAN_PACKAGE_VERSION_MINOR ${VERSION_MINOR})
@@ -596,7 +596,7 @@ elseif(APPLE)
# set(CPACK_GENERATOR "TGZ") # for quick testing
set(CPACK_BINARY_DRAGNDROP ON)
set(CPACK_DMG_BACKGROUND_IMAGE ${COMPANION_SRC_DIRECTORY}/images/splash_dmg.png)
set(CPACK_DMG_VOLUME_NAME "OpenTX Companion")
set(CPACK_DMG_VOLUME_NAME "EdgeTX Companion")
set(CPACK_DMG_DS_STORE ${PROJECT_SOURCE_DIR}/companion/targets/mac/DS_Store)
set(CPACK_PACKAGE_FILE_NAME "opentx-${CPACK_PACKAGE_NAME_LOWERCASE}-${VERSION}")
else(DPKG_PROGRAM)
+19
View File
@@ -243,6 +243,10 @@
<file>images/simulator/TX16S/left_tele.png</file>
<file>images/simulator/TX16S/right_ent.png</file>
<file>images/simulator/TX16S/right_mdl.png</file>
<file>images/simulator/NV14/left.png</file>
<file>images/simulator/NV14/right.png</file>
<file>images/simulator/NV14/top.png</file>
<file>images/simulator/NV14/bottom.png</file>
<file>images/wizard/ailerons.png</file>
<file>images/wizard/airbrakes.png</file>
<file>images/wizard/elevons.png</file>
@@ -1224,4 +1228,19 @@
<file>themes/yerico/48/write_eeprom.png</file>
<file>themes/yerico/48/sdsync.png</file>
</qresource>
<qresource prefix="/layouts/">
<file alias="mask_layout1p2.png">../../radio/src/gui/colorlcd/layouts/mask_layout1+2.png</file>
<file alias="mask_layout1p3.png">../../radio/src/gui/colorlcd/layouts/mask_layout1+3.png</file>
<file alias="mask_layout1x1.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x1.png</file>
<file alias="mask_layout1x2.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x2.png</file>
<file alias="mask_layout1x3.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x3.png</file>
<file alias="mask_layout1x4.png">../../radio/src/gui/colorlcd/layouts/mask_layout1x4.png</file>
<file alias="mask_layout2p1.png">../../radio/src/gui/colorlcd/layouts/mask_layout2+1.png</file>
<file alias="mask_layout2p3.png">../../radio/src/gui/colorlcd/layouts/mask_layout2+3.png</file>
<file alias="mask_layout2x1.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x1.png</file>
<file alias="mask_layout2x2.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x2.png</file>
<file alias="mask_layout2x3.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x3.png</file>
<file alias="mask_layout2x4.png">../../radio/src/gui/colorlcd/layouts/mask_layout2x4.png</file>
<file alias="mask_layout4p2.png">../../radio/src/gui/colorlcd/layouts/mask_layout4+2.png</file>
</qresource>
</RCC>
+5 -2
View File
@@ -47,7 +47,7 @@
#define CPN_MAX_GYRO_ANALOGS 2
#define CPN_MAX_ANALOGS (CPN_MAX_STICKS + CPN_MAX_POTS + CPN_MAX_MOUSE_ANALOGS + CPN_MAX_GYRO_ANALOGS)
#define CPN_STR_APP_NAME QCoreApplication::translate("Companion", "OpenTX Companion")
#define CPN_STR_APP_NAME QCoreApplication::translate("Companion", "EdgeTX Companion")
#define CPN_STR_TTL_INFO QCoreApplication::translate("Companion", "Information") // shared Title Case words, eg. for a window title or section heading
#define CPN_STR_TTL_WARNING QCoreApplication::translate("Companion", "Warning")
#define CPN_STR_TTL_ERROR QCoreApplication::translate("Companion", "Error")
@@ -59,7 +59,8 @@
#define DFU_FILES_FILTER "DFU " % CPN_STR_FILES % " (*.dfu);;"
#define EEPE_FILES_FILTER "EEPE " % CPN_STR_FILES % " (*.eepe);;"
#define OTX_FILES_FILTER "OpenTX " % CPN_STR_FILES % " (*.otx);;"
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.otx *.eepe *.bin *.hex);;" % OTX_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER
#define YML_FILES_FILTER "YAML " % CPN_STR_FILES % " (*.yml);;"
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.otx *.yml *.eepe *.bin *.hex);;" % OTX_FILES_FILTER % YML_FILES_FILTER % EEPE_FILES_FILTER % BIN_FILES_FILTER % HEX_FILES_FILTER
#define FLASH_FILES_FILTER "FLASH " % CPN_STR_FILES % " (*.bin *.hex *.dfu);;" % BIN_FILES_FILTER % HEX_FILES_FILTER % DFU_FILES_FILTER
#define EXTERNAL_EEPROM_FILES_FILTER "EEPROM " % CPN_STR_FILES % " (*.bin *.hex);;" % BIN_FILES_FILTER % HEX_FILES_FILTER
#define ER9X_EEPROM_FILE_TYPE "ER9X_EEPROM_FILE"
@@ -78,4 +79,6 @@
#define CPN_STR_SW_INDICATOR_NEUT QCoreApplication::translate("RawSwitch", "-") // Switch neutral (middle) position indicator.
#define CPN_STR_SW_INDICATOR_REV QCoreApplication::translate("RawSwitch", "!") // Switch reversed logic (NOT) indicator.
#define OPENTX_HOME_PAGE_URL "http://www.edge-tx.org"
#endif // _CONSTANTS_H_
@@ -176,7 +176,6 @@ RawSwitchItemModel::RawSwitchItemModel(const GeneralSettings * const generalSett
addItems(SWITCH_TYPE_SWITCH, -board->getCapability(Board::SwitchPositions));
// Ascending switch direction (including zero)
addItems(SWITCH_TYPE_TIMER_MODE, 5);
addItems(SWITCH_TYPE_NONE, 1);
addItems(SWITCH_TYPE_SWITCH, board->getCapability(Board::SwitchPositions));
addItems(SWITCH_TYPE_MULTIPOS_POT, board->getCapability(Board::MultiposPots) * board->getCapability(Board::MultiposPotsPositions));
@@ -214,14 +213,6 @@ void RawSwitchItemModel::addItems(const RawSwitchType & type, int count)
context &= ~RawSwitch::GlobalFunctionsContext;
break;
case SWITCH_TYPE_TIMER_MODE:
context = RawSwitch::TimersContext;
break;
case SWITCH_TYPE_NONE:
context &= ~RawSwitch::TimersContext;
break;
case SWITCH_TYPE_ON:
case SWITCH_TYPE_ONE:
context = RawSwitch::SpecialFunctionsContext | RawSwitch::GlobalFunctionsContext;
+49 -37
View File
@@ -123,31 +123,28 @@ static QWidget * folderSelectorWidget(QLineEdit * le, QWidget * parent)
void FileSyncDialog::setupUi()
{
const QString srcArw = CPN_STR_SW_INDICATOR_UP % " ";
const QString dstArw = CPN_STR_SW_INDICATOR_DN % " ";
m_folderNameA = tr("Folder A");
m_folderNameB = tr("Folder B");
int row = 0;
CompanionIcon playIcon("play.png");
playIcon.addImage("stop.png", QIcon::Normal, QIcon::On);
QLabel * lblSrc = new QLabel(tr("Source Folder:"), this);
AutoLineEdit * leSrc = new AutoLineEdit(this, true);
leSrc->setField(m_syncOptions.folderA);
leSrc->setText(QDir::toNativeSeparators(m_syncOptions.folderA));
QWidget * wdgSrc = folderSelectorWidget(leSrc, this);
QLabel * lblFolderA = new QLabel(m_folderNameA % ":", this);
AutoLineEdit * leFolderA = new AutoLineEdit(this, true);
leFolderA->setField(m_syncOptions.folderA);
leFolderA->setText(QDir::toNativeSeparators(m_syncOptions.folderA));
QWidget * wdgFolderA = folderSelectorWidget(leFolderA, this);
QLabel * lblDst = new QLabel(tr("Destination Folder:"), this);
AutoLineEdit * leDst = new AutoLineEdit(this, true);
leDst->setField(m_syncOptions.folderB);
leDst->setText(QDir::toNativeSeparators(m_syncOptions.folderB));
QWidget * wdgDst = folderSelectorWidget(leDst, this);
QLabel * lblFolderB = new QLabel(m_folderNameB % ":", this);
AutoLineEdit * leFolderB = new AutoLineEdit(this, true);
leFolderB->setField(m_syncOptions.folderB);
leFolderB->setText(QDir::toNativeSeparators(m_syncOptions.folderB));
QWidget * wdgFolderB = folderSelectorWidget(leFolderB, this);
AutoComboBox * syncDir = new AutoComboBox(this);
syncDir->addItem(tr("%1%2 Both directions, to destination folder first").arg(dstArw, srcArw), SyncProcess::SYNC_A2B_B2A);
syncDir->addItem(tr("%1%2 Both directions, to source folder first").arg(srcArw, dstArw), SyncProcess::SYNC_B2A_A2B);
syncDir->addItem(tr(" %1 Only from source folder to destination folder").arg(dstArw), SyncProcess::SYNC_A2B);
syncDir->addItem(tr(" %1 Only from destination folder to source folder").arg(srcArw), SyncProcess::SYNC_B2A);
syncDir->setCurrentIndex(-1); // we set the default option later
ui_syncDir = new AutoComboBox(this);
setSyncDirList();
ui_syncDir->setField(m_syncOptions.direction);
AutoComboBox * copyMode = new AutoComboBox(this);
copyMode->setToolTip(tr("How to handle overwriting files which already exist in the destination folder."));
@@ -274,12 +271,12 @@ void FileSyncDialog::setupUi()
ui_optionsPanel = new QGroupBox(tr("Options"), this);
QGridLayout * optsLay = new QGridLayout(ui_optionsPanel);
row = 0;
optsLay->addWidget(lblSrc, row, 0);
optsLay->addWidget(wdgSrc, row++, 1);
optsLay->addWidget(lblDst, row, 0);
optsLay->addWidget(wdgDst, row++, 1);
optsLay->addWidget(lblFolderA, row, 0);
optsLay->addWidget(wdgFolderA, row++, 1);
optsLay->addWidget(lblFolderB, row, 0);
optsLay->addWidget(wdgFolderB, row++, 1);
optsLay->addWidget(new QLabel(tr("Sync. Direction:"), this), row, 0);
optsLay->addWidget(syncDir, row++, 1);
optsLay->addWidget(ui_syncDir, row++, 1);
optsLay->addWidget(new QLabel(tr("Existing Files:"), this), row, 0);
optsLay->addWidget(copyMode, row++, 1);
optsLay->addWidget(new QLabel(tr("Max. File Size:"), this), row, 0);
@@ -299,13 +296,13 @@ void FileSyncDialog::setupUi()
// Connect all signal handlers
// source path
connect(leSrc, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
// destination path
connect(leDst, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
// folder A path
connect(leFolderA, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
// folder B path
connect(leFolderB, &AutoLineEdit::textChanged, this, &FileSyncDialog::validate);
// function to dis/enable the OVERWR_ALWAYS option depending on sync direction
connect(syncDir, &AutoComboBox::currentDataChanged, [=](int dir) {
connect(ui_syncDir, &AutoComboBox::currentDataChanged, [=](int dir) {
int idx = copyMode->findData(SyncProcess::OVERWR_ALWAYS);
int flg = (dir == SyncProcess::SYNC_A2B || dir == SyncProcess::SYNC_B2A) ? 33 : 0;
if (!flg && idx == copyMode->currentIndex())
@@ -342,7 +339,7 @@ void FileSyncDialog::setupUi()
});
// we set these after connections are made because we want the signal processors to run
syncDir->setField(m_syncOptions.direction);
ui_syncDir->setField(m_syncOptions.direction);
maxSize->setValue(m_syncOptions.maxFileSize / 1024);
// connect Reset action to all fields
@@ -354,14 +351,16 @@ void FileSyncDialog::setupUi()
connect(this, &FileSyncDialog::optionsWereReset, a, &AutoBitsetCheckBox::updateValue);
connect(this, &FileSyncDialog::optionsWereReset, [=]() { maxSize->setValue(m_syncOptions.maxFileSize / 1024); });
// React to name changes for source and destination path labels
// React to name changes for folder path labels
connect(this, &FileSyncDialog::folderNameAChanged, [=](const QString &text) {
lblSrc->setText(text % ":");
wdgSrc->setProperty("fileDialogTitle", text);
lblFolderA->setText(text % ":");
wdgFolderA->setProperty("fileDialogTitle", text);
setSyncDirList();
});
connect(this, &FileSyncDialog::folderNameBChanged, [=](const QString &text) {
lblDst->setText(text % ":");
wdgDst->setProperty("fileDialogTitle", text);
lblFolderB->setText(text % ":");
wdgFolderB->setProperty("fileDialogTitle", text);
setSyncDirList();
});
// button actions
@@ -487,11 +486,11 @@ bool FileSyncDialog::validate()
{
QString msg;
if (m_syncOptions.folderA.isEmpty() || !QFileInfo(m_syncOptions.folderA).exists())
msg = tr("Source folder not found.");
msg = tr("%1 not found.").arg(m_folderNameA);
else if (m_syncOptions.folderB.isEmpty() || !QFileInfo(m_syncOptions.folderB).exists())
msg = tr("Destination folder not found.");
msg = tr("%1 not found.").arg(m_folderNameB);
else if (QFileInfo(m_syncOptions.folderA).canonicalFilePath() == QFileInfo(m_syncOptions.folderB).canonicalFilePath())
msg = tr("Source and destination folders are the same.");
msg = tr("Folders are the same.");
setStatusText(msg, QtCriticalMsg);
ui_btnStartStop->setEnabled(msg.isEmpty());
@@ -502,3 +501,16 @@ void FileSyncDialog::adjustSizeDelayed()
{
QTimer::singleShot(0, [=]() { adjustSize(); });
}
void FileSyncDialog::setSyncDirList()
{
const QString upArw = CPN_STR_SW_INDICATOR_UP % " ";
const QString downArw = CPN_STR_SW_INDICATOR_DN % " ";
ui_syncDir->clear();
ui_syncDir->addItem(tr("%1%2 Both directions, to %3 first").arg(downArw, upArw, m_folderNameB), SyncProcess::SYNC_A2B_B2A);
ui_syncDir->addItem(tr("%1%2 Both directions, to %3 first").arg(upArw, downArw, m_folderNameA), SyncProcess::SYNC_B2A_A2B);
ui_syncDir->addItem(tr(" %1 Only from %2 to %3").arg(downArw, m_folderNameA, m_folderNameB), SyncProcess::SYNC_A2B);
ui_syncDir->addItem(tr(" %1 Only from %2 to %3").arg(upArw, m_folderNameB, m_folderNameA), SyncProcess::SYNC_B2A);
ui_syncDir->updateValue();
}
+4 -4
View File
@@ -18,8 +18,7 @@
* GNU General Public License for more details.
*/
#ifndef FILESYNCDIALOG_H
#define FILESYNCDIALOG_H
#pragma once
#include "process_sync.h"
@@ -30,6 +29,7 @@
#include <QToolButton>
class ProgressWidget;
class AutoComboBox;
class FileSyncDialog : public QDialog
{
@@ -76,6 +76,7 @@ class FileSyncDialog : public QDialog
void updateRunningState();
void onStatusUpdate(const SyncProcess::SyncStatus & stat);
void adjustSizeDelayed();
void setSyncDirList();
private:
SyncProcess::SyncOptions m_syncOptions;
@@ -91,6 +92,5 @@ class FileSyncDialog : public QDialog
QToolButton * ui_btnStartStop;
QToolButton * ui_btnClose;
QLabel * ui_statusLabel;
AutoComboBox * ui_syncDir;
};
#endif // FILESYNCDIALOG_H
+1
View File
@@ -37,6 +37,7 @@ string(REPLACE ".cpp" ".h" firmwares_HDRS "${firmwares_SRCS}")
list(APPEND firmwares_HDRS
eepromimportexport.h
moduledata.h
customisation_data.h
)
set(firmwares_QT
+37 -2
View File
@@ -92,6 +92,8 @@ uint32_t Boards::getFourCC(Type board)
return 0x4178746F;
case BOARD_RADIOMASTER_T8:
return 0x4378746F;
case BOARD_FLYSKY_NV14:
return 0x3A78746F;
default:
return 0;
}
@@ -128,6 +130,7 @@ int Boards::getEEpromSize(Board::Type board)
case BOARD_JUMPER_T16:
case BOARD_JUMPER_T18:
case BOARD_RADIOMASTER_TX16S:
case BOARD_FLYSKY_NV14:
return 0;
default:
return 0;
@@ -163,6 +166,7 @@ int Boards::getFlashSize(Type board)
case BOARD_JUMPER_T16:
case BOARD_JUMPER_T18:
case BOARD_RADIOMASTER_TX16S:
case BOARD_FLYSKY_NV14:
return FSIZE_HORUS;
case BOARD_UNKNOWN:
return FSIZE_MAX;
@@ -261,6 +265,20 @@ SwitchInfo Boards::getSwitchInfo(Board::Type board, int index)
if (index < DIM(switches))
return switches[index];
}
else if (IS_FLYSKY_NV14(board)) {
const Board::SwitchInfo switches[] = {
{SWITCH_2POS, "SA"},
{SWITCH_3POS, "SB"},
{SWITCH_TOGGLE, "SC"},
{SWITCH_2POS, "SD"},
{SWITCH_TOGGLE, "SE"},
{SWITCH_3POS, "SF"},
{SWITCH_3POS, "SG"},
{SWITCH_TOGGLE, "SH"}
};
if (index < DIM(switches))
return switches[index];
}
else if (IS_FAMILY_HORUS_OR_T16(board)) {
const Board::SwitchInfo switches[] = {
{SWITCH_3POS, "SA"},
@@ -337,6 +355,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
return 5;
else if (IS_HORUS_X12S(board))
return 3;
else if (IS_FLYSKY_NV14(board))
return 2;
else
return 3;
@@ -388,6 +408,8 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
return 8;
else if (board == BOARD_JUMPER_TLITE)
return 4;
else if (board == BOARD_FLYSKY_NV14)
return 8;
else if (IS_FAMILY_T12(board))
return 8;
else if (IS_TARANIS_XLITE(board))
@@ -414,13 +436,13 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
return getCapability(board, Board::Switches);
case SwitchPositions:
if (IS_HORUS_OR_TARANIS(board))
if (IS_HORUS_OR_TARANIS(board) || IS_FLYSKY_NV14(board))
return getCapability(board, Board::Switches) * 3;
else
return 9;
case NumTrims:
if (IS_FAMILY_HORUS_OR_T16(board))
if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board))
return 6;
else
return 4;
@@ -431,6 +453,9 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
case HasRTC:
return IS_STM32(board) ? true : false;
case HasColorLcd:
return IS_FAMILY_HORUS_OR_T16(board);
default:
return 0;
}
@@ -528,6 +553,14 @@ QString Boards::getAnalogInputName(Board::Type board, int index)
if (index < DIM(pots))
return pots[index];
}
else if (IS_FLYSKY_NV14(board)) {
const QString pots[] = {
"VRA",
"VRB",
};
if (index < DIM(pots))
return pots[index];
}
else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) {
const QString pots[] = {
"S1",
@@ -599,6 +632,8 @@ QString Boards::getBoardName(Board::Type board)
return "Radiomaster TX12";
case BOARD_RADIOMASTER_T8:
return "Radiomaster T8";
case BOARD_FLYSKY_NV14:
return "FlySky NV14";
default:
return CPN_STR_UNKNOWN_ITEM;
}
+15 -3
View File
@@ -61,6 +61,7 @@ namespace Board {
BOARD_RADIOMASTER_TX12,
BOARD_RADIOMASTER_T8,
BOARD_JUMPER_TLITE,
BOARD_FLYSKY_NV14,
BOARD_TYPE_COUNT,
BOARD_TYPE_MAX = BOARD_TYPE_COUNT - 1
};
@@ -140,7 +141,8 @@ namespace Board {
FactoryInstalledSwitches,
NumTrims,
NumTrimSwitches,
HasRTC
HasRTC,
HasColorLcd
};
struct SwitchInfo
@@ -269,6 +271,11 @@ inline bool IS_FAMILY_T12(Board::Type board)
return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12 || board == Board::BOARD_RADIOMASTER_T8 || board == Board::BOARD_JUMPER_TLITE;
}
inline bool IS_FLYSKY_NV14(Board::Type board)
{
return (board == Board::BOARD_FLYSKY_NV14);
}
inline bool IS_TARANIS_XLITE(Board::Type board)
{
return board == Board::BOARD_TARANIS_XLITE || board == Board::BOARD_TARANIS_XLITES;
@@ -346,7 +353,7 @@ inline bool IS_FAMILY_HORUS(Board::Type board)
inline bool IS_FAMILY_HORUS_OR_T16(Board::Type board)
{
return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board);
return IS_FAMILY_HORUS(board) || IS_FAMILY_T16(board) || IS_FLYSKY_NV14(board)/*generally*/;
}
inline bool IS_HORUS_OR_TARANIS(Board::Type board)
@@ -356,7 +363,7 @@ inline bool IS_HORUS_OR_TARANIS(Board::Type board)
inline bool IS_STM32(Board::Type board)
{
return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board);
return IS_TARANIS(board) || IS_FAMILY_HORUS_OR_T16(board) || IS_FLYSKY_NV14(board);
}
inline bool IS_ARM(Board::Type board)
@@ -384,3 +391,8 @@ inline bool IS_ACCESS_RADIO(Board::Type board, const QString & id)
return (IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board) || board == Board::BOARD_TARANIS_X9DP_2019 || board == Board::BOARD_X10_EXPRESS || IS_TARANIS_X7_ACCESS(board) ||
(IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess")));
}
inline bool HAS_EEPROM_YAML(Board::Type board)
{
return IS_FAMILY_HORUS_OR_T16(board);
}
@@ -0,0 +1,236 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "constants.h"
#include <QtCore>
/*
Note: this code is consolidated from various radio/src files and modified if necessary to run on desktop
*/
constexpr int MAX_CUSTOM_SCREENS {5};
constexpr int THEME_NAME_LEN {8};
constexpr int MAX_THEME_OPTIONS {5};
constexpr int LEN_ZONE_OPTION_STRING {8};
constexpr int MAX_LAYOUT_ZONES {10};
constexpr int MAX_LAYOUT_OPTIONS {10};
constexpr int WIDGET_NAME_LEN {10};
constexpr int MAX_WIDGET_OPTIONS {5};
constexpr int MAX_TOPBAR_ZONES {4};
constexpr int MAX_TOPBAR_OPTIONS {1};
constexpr int LAYOUT_ID_LEN {10};
// Common 'ZoneOptionValue's among all layouts
enum {
LAYOUT_OPTION_TOPBAR = 0,
LAYOUT_OPTION_FM,
LAYOUT_OPTION_SLIDERS,
LAYOUT_OPTION_TRIMS,
LAYOUT_OPTION_MIRRORED,
LAYOUT_OPTION_LAST_DEFAULT=LAYOUT_OPTION_MIRRORED
};
struct ZoneOptionValue // union in radio/src/datastructs.h
{
unsigned int unsignedValue;
int signedValue;
unsigned int boolValue;
char stringValue[LEN_ZONE_OPTION_STRING + 1];
};
enum ZoneOptionValueEnum {
ZOV_Unsigned,
ZOV_Signed,
ZOV_Bool,
ZOV_String,
ZOV_LAST = ZOV_String
};
inline const char * zoneOptionValueEnumToString(ZoneOptionValueEnum zovenum) {
switch (zovenum) {
case ZOV_Unsigned:
return "unsigned";
case ZOV_Signed:
return "signed";
case ZOV_Bool:
return "bool";
case ZOV_String:
return "string";
default:
return "unknown";
}
}
struct ZoneOption
{
enum Type {
Integer,
Source,
Bool,
String,
File,
TextSize,
Timer,
Switch,
Color
};
const char * name;
Type type;
ZoneOptionValue deflt;
ZoneOptionValue min;
ZoneOptionValue max;
};
struct ZoneOptionValueTyped
{
ZoneOptionValueEnum type;
ZoneOptionValue value;
};
inline ZoneOptionValueEnum zoneValueEnumFromType(ZoneOption::Type type)
{
switch(type) {
case ZoneOption::File:
case ZoneOption::String:
return ZOV_String;
case ZoneOption::Integer:
return ZOV_Signed;
case ZoneOption::Bool:
return ZOV_Bool;
case ZoneOption::Color:
case ZoneOption::Timer:
case ZoneOption::Switch:
case ZoneOption::Source:
case ZoneOption::TextSize:
default:
return ZOV_Unsigned;
}
}
struct WidgetPersistentData {
ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS];
};
struct ZonePersistentData {
char widgetName[WIDGET_NAME_LEN + 1];
WidgetPersistentData widgetData;
};
template<int N, int O>
struct WidgetsContainerPersistentData {
ZonePersistentData zones[N];
ZoneOptionValueTyped options[O];
};
typedef WidgetsContainerPersistentData<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS> LayoutPersistentData;
typedef WidgetsContainerPersistentData<MAX_TOPBAR_ZONES, MAX_TOPBAR_OPTIONS> TopBarPersistentData;
inline void setZoneOptionValue(ZoneOptionValue & zov, bool value) { zov.boolValue = value; }
inline void setZoneOptionValue(ZoneOptionValue & zov, int value) { zov.signedValue = value; }
inline void setZoneOptionValue(ZoneOptionValue & zov, char value) { memset(&zov.stringValue, value, LEN_ZONE_OPTION_STRING); }
inline void setZoneOptionValue(ZoneOptionValue & zov, unsigned int value) { zov.unsignedValue = value; }
// cannot use QColor so use formula from libopenui_defines.h
#define RGB(r, g, b) (uint16_t)((((r) & 0xF8) << 8) + (((g) & 0xFC) << 3) + (((b) & 0xF8) >> 3))
#define WHITE RGB(0xFF, 0xFF, 0xFF)
#define RED RGB(229, 32, 30)
class RadioTheme
{
Q_DECLARE_TR_FUNCTIONS(RadioTheme)
public:
struct PersistentData {
ZoneOptionValueTyped options[MAX_THEME_OPTIONS];
};
struct ThemeData {
char themeName[THEME_NAME_LEN + 1];
PersistentData themePersistentData;
};
static void init(const char * themeName, ThemeData & themeData)
{
memset(&themeData, 0, sizeof(ThemeData));
memcpy(&themeData.themeName, themeName, THEME_NAME_LEN);
PersistentData & persistentData = themeData.themePersistentData;
persistentData.options[0].type = zoneValueEnumFromType(ZoneOption::Type::Color);
setZoneOptionValue(persistentData.options[0].value, (unsigned int)WHITE);
persistentData.options[1].type = zoneValueEnumFromType(ZoneOption::Type::Color);
setZoneOptionValue(persistentData.options[1].value, (unsigned int)RED);
}
};
class RadioLayout
{
Q_DECLARE_TR_FUNCTIONS(RadioLayout)
public:
struct CustomScreenData {
char layoutId[LAYOUT_ID_LEN + 1];
LayoutPersistentData layoutPersistentData;
};
struct CustomScreens {
CustomScreenData customScreenData[MAX_CUSTOM_SCREENS];
};
static void init(const char * layoutId, CustomScreens & customScreens)
{
memset(&customScreens, 0, sizeof(CustomScreens));
for (int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
if (i == 0)
memcpy(&customScreens.customScreenData[i].layoutId, layoutId, LAYOUT_ID_LEN);
LayoutPersistentData & persistentData = customScreens.customScreenData[i].layoutPersistentData;
int j = 0;
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
setZoneOptionValue(persistentData.options[j++].value, (bool)true);
persistentData.options[j].type = zoneValueEnumFromType(ZoneOption::Type::Bool);
setZoneOptionValue(persistentData.options[j++].value, (bool)false);
}
}
};
+1 -1
View File
@@ -588,7 +588,7 @@ class UnionField: public DataField {
virtual bool select(const selectorT& attr) const = 0;
virtual DataField* getField() = 0;
};
class TransformedMember: public UnionMember, public TransformedField {
public:
TransformedMember(DataField* parent, DataField& field):
+2 -1
View File
@@ -158,7 +158,8 @@ enum Capability {
HasBluetooth,
HasAntennaChoice,
HasADCJitterFilter,
HasTelemetryBaudrate
HasTelemetryBaudrate,
TopBarZones
};
class EEPROMInterface
@@ -90,9 +90,9 @@ t_Ersky9xGeneral::t_Ersky9xGeneral()
Ersky9xGeneral::operator GeneralSettings ()
{
GeneralSettings result;
result.version = myVers;
for (int i=0; i<CPN_MAX_STICKSnPOTS; i++) {
result.calibMid[i] = calibMid[i];
result.calibSpanNeg[i] = calibSpanNeg[i];
@@ -280,7 +280,7 @@ t_Ersky9xMixData_v11::operator MixData ()
}
else if (srcRaw > 45) {
c9x.srcRaw = RawSource(SOURCE_TYPE_GVAR, srcRaw-46);
}
}
else if (srcRaw <= 12) {
c9x.srcRaw = RawSource(SOURCE_TYPE_CYC, srcRaw-10);
}
@@ -504,7 +504,7 @@ t_Ersky9xModelData_v10::operator ModelData ()
c9x.used = true;
getEEPROMString(c9x.name, name, sizeof(name));
for (int i=0; i<2; i++) {
c9x.timers[i].mode = getErSky9xTimerMode(timer[i].tmrModeA);
c9x.timers[i].mode = timer[i].tmrModeA < 4 ? timer[i].tmrModeA : 0;
c9x.timers[i].val = timer[i].tmrVal;
}
switch(protocol) {
@@ -619,7 +619,7 @@ t_Ersky9xModelData_v11::operator ModelData ()
c9x.used = true;
getEEPROMString(c9x.name, name, sizeof(name));
for (int i=0; i<2; i++) {
c9x.timers[i].mode = getErSky9xTimerMode(timer[i].tmrModeA);
c9x.timers[i].mode = timer[i].tmrModeA < 4 ? timer[i].tmrModeA : 0;
c9x.timers[i].val = timer[i].tmrVal;
}
switch(protocol) {
+11 -10
View File
@@ -121,12 +121,12 @@ GeneralSettings::GeneralSettings()
if (IS_JUMPER_T16(board))
strcpy(bluetoothName, "t16");
else if (IS_FAMILY_HORUS_OR_T16(board)) {
else if (IS_FLYSKY_NV14(board))
strcpy(bluetoothName, "nv14");
else if (IS_FAMILY_HORUS_OR_T16(board))
strcpy(bluetoothName, "horus");
}
else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board)) {
else if (IS_TARANIS_X9E(board) || IS_TARANIS_SMALL(board))
strcpy(bluetoothName, "taranis");
}
for (uint8_t i = 0; i < 4; i++) {
trainer.mix[i].mode = TrainerMix::TRN_MIX_MODE_SUBST;
@@ -233,11 +233,8 @@ GeneralSettings::GeneralSettings()
}
}
strcpy(themeName, "default");
ThemeOptionData option1 = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 };
memcpy(&themeOptionValue[0], option1, sizeof(ThemeOptionData));
ThemeOptionData option2 = { 0x03, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0 };
memcpy(&themeOptionValue[1], option2, sizeof(ThemeOptionData));
const char * themeName = IS_FLYSKY_NV14(board) ? "FlySky" : "EdgeTX";
RadioTheme::init(themeName, themeData);
}
void GeneralSettings::setDefaultControlTypes(Board::Type board)
@@ -251,11 +248,15 @@ void GeneralSettings::setDefaultControlTypes(Board::Type board)
return;
// TODO: move to Boards, like with switches
if (IS_FAMILY_HORUS_OR_T16(board)) {
if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) {
potConfig[0] = Board::POT_WITH_DETENT;
potConfig[1] = Board::POT_MULTIPOS_SWITCH;
potConfig[2] = Board::POT_WITH_DETENT;
}
else if (IS_FLYSKY_NV14(board)) {
potConfig[0] = Board::POT_WITHOUT_DETENT;
potConfig[1] = Board::POT_WITHOUT_DETENT;
}
else if (IS_TARANIS_XLITE(board)) {
potConfig[0] = Board::POT_WITHOUT_DETENT;
potConfig[1] = Board::POT_WITHOUT_DETENT;
+3 -4
View File
@@ -25,6 +25,7 @@
#include "customfunctiondata.h"
#include "rawsource.h"
#include "datahelpers.h"
#include "customisation_data.h"
#include <QtCore>
@@ -94,7 +95,6 @@ constexpr int OWNER_NAME_LEN {10};
constexpr int BLUETOOTH_NAME_LEN {10};
constexpr int TTS_LANGUAGE_LEN {2};
constexpr int HARDWARE_NAME_LEN {3};
constexpr int THEME_NAME_LEN {8};
constexpr int REGISTRATION_ID_LEN {8};
class GeneralSettings {
@@ -236,13 +236,12 @@ class GeneralSettings {
char sliderName[CPN_MAX_SLIDERS][HARDWARE_NAME_LEN + 1];
unsigned int sliderConfig[CPN_MAX_SLIDERS];
char themeName[THEME_NAME_LEN + 1];
typedef uint8_t ThemeOptionData[8 + 1];
ThemeOptionData themeOptionValue[5];
RadioTheme::ThemeData themeData;
char registrationId[REGISTRATION_ID_LEN + 1];
int gyroMax;
int gyroOffset;
int uartSampleMode;
int pwrOnSpeed;
int pwrOffSpeed;
+7 -7
View File
@@ -176,8 +176,8 @@ void ModelData::clear()
for (unsigned i = 0; i < CPN_MAX_SENSORS; i++)
sensorData[i].clear();
static const uint8_t blob[] = { 0x4c, 0x61, 0x79, 0x6f, 0x75, 0x74, 0x32, 0x50, 0x31, 0x00, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x42, 0x6d, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
memcpy(customScreenData[0], blob, sizeof(blob));
const char * layoutId = "Layout2P1"; // currently all using same default though might change for NV14
RadioLayout::init(layoutId, customScreens);
}
bool ModelData::isEmpty() const
@@ -407,6 +407,10 @@ void ModelData::convert(RadioDataConversionState & cstate)
thrTraceSrc = RawSource(SOURCE_TYPE_STICK, (int)thrTraceSrc + 3).convert(cstate).index - 3;
}
for (int i = 0; i < CPN_MAX_MODULES; i++) {
moduleData[i].convert(cstate.withComponentIndex(i));
}
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
timers[i].convert(cstate.withComponentIndex(i));
}
@@ -430,10 +434,6 @@ void ModelData::convert(RadioDataConversionState & cstate)
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
flightModeData[i].convert(cstate.withComponentIndex(i));
}
for (int i = 0; i < CPN_MAX_MODULES; i++) {
moduleData[i].convert(cstate.withComponentIndex(i));
}
}
#define MAX_REF_UPDATES 100
@@ -575,7 +575,7 @@ int ModelData::updateReference()
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
TimerData *td = &timers[i];
if (!td->isModeOff()) {
updateTimerMode(td->mode);
updateTimerMode(td->swtch);
if (td->isModeOff())
appendUpdateReferenceParams(REF_UPD_TYPE_TIMER, REF_UPD_ACT_CLEAR, i);
}
+5 -32
View File
@@ -34,6 +34,7 @@
#include "sensordata.h"
#include "telem_data.h"
#include "timerdata.h"
#include "customisation_data.h"
#include <QtCore>
@@ -67,34 +68,6 @@ class ScriptData {
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(ScriptData)); }
};
/*
* TODO ...
*/
#if 0
class CustomScreenOptionData {
public:
};
class CustomScreenZoneData {
public:
char widgetName[10+1];
WidgetOptionData widgetOptions[5];
};
class CustomScreenData {
public:
CustomScreenData();
char layoutName[10+1];
CustomScreenZoneData zones[];
CustomScreenOptionData options[];
};
#else
typedef char CustomScreenData[610+1];
typedef char TopbarData[216+1];
#endif
enum TrainerMode {
TRAINER_MODE_MASTER_TRAINER_JACK,
TRAINER_MODE_SLAVE,
@@ -176,7 +149,7 @@ class ModelData {
unsigned int trainerMode; // TrainerMode
ModuleData moduleData[CPN_MAX_MODULES+1/*trainer*/];
ModuleData moduleData[CPN_MAX_MODULES + 1/*trainer*/];
ScriptData scriptData[CPN_MAX_SCRIPTS];
@@ -184,9 +157,9 @@ class ModelData {
unsigned int toplcdTimer;
CustomScreenData customScreenData[5];
TopbarData topbarData;
RadioLayout::CustomScreens customScreens;
TopBarPersistentData topBarData;
unsigned int view;
char registrationId[8+1];
+23 -1
View File
@@ -26,7 +26,29 @@
void ModuleData::convert(RadioDataConversionState & cstate)
{
if (protocol == PULSES_PXX_R9M && (IS_TARANIS_XLITE(cstate.fromType) || IS_TARANIS_XLITE(cstate.toType))) {
Firmware *fw = getCurrentFirmware();
cstate.setSubComp(indexToString(cstate.subCompIdx, fw));
cstate.setItemType("Protocol", 1);
RadioDataConversionState::EventType evt = RadioDataConversionState::EVT_NONE;
RadioDataConversionState::LogField oldData(protocol, protocolToString(protocol));
if (fw->getBoard() == cstate.toType) {
if (cstate.subCompIdx < fw->getCapability(NumFirstUsableModule) || cstate.subCompIdx > fw->getCapability(NumModules) - 1) {
if ((PulsesProtocol) protocol != PULSES_OFF)
evt = RadioDataConversionState::EVT_INV;
}
else if (!fw->isAvailable((PulsesProtocol) protocol, cstate.subCompIdx)) {
evt = RadioDataConversionState::EVT_INV;
}
}
else {
evt = RadioDataConversionState::EVT_INV;
qDebug() << "Error - current firmware board does not match conversion to board!";
}
if (evt == RadioDataConversionState::EVT_INV) {
cstate.setInvalid(oldData);
clear();
}
}
+444 -107
View File
@@ -103,7 +103,7 @@ inline int MAX_POTS_SOURCES(Board::Type board, int version)
inline int MAX_SLIDERS_STORAGE(Board::Type board, int version)
{
if (version >= 219 && IS_FAMILY_HORUS_OR_T16(board))
if (version >= 219 && (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)))
return 4;
return Boards::getCapability(board, Board::Sliders);
}
@@ -139,7 +139,7 @@ inline int SWITCHES_CONFIG_SIZE(Board::Type board, int version)
inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version)
{
if (IS_FAMILY_HORUS_OR_T16(board))
if (IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board))
return 2;
else
return 0;
@@ -160,7 +160,7 @@ inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version)
#define MAX_TELEMETRY_SENSORS(board, version) (version <= 218 ? 32 : ((IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board)) ? 60 : 40))
#define NUM_PPM_INPUTS(board, version) 16
#define ROTENC_COUNT(board, version) ((IS_STM32(board) && version >= 218) ? 0 : 1)
#define MAX_AUX_TRIMS(board) (IS_FAMILY_HORUS_OR_T16(board) ? 2 : 0)
#define MAX_AUX_TRIMS(board) ((IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board)) ? 2 : 0)
inline int switchIndex(int i, Board::Type board, unsigned int version)
{
@@ -693,10 +693,18 @@ class FlightModeField: public TransformedField {
}
}
if (version >= 218) {
if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<10>(this, phase.name, "Flight mode name"));
else
internalField.Append(new ZCharField<6>(this, phase.name, "Flight mode name"));
if (version >= 220) {
if (HAS_LARGE_LCD(board))
internalField.Append(new CharField<10>(this, phase.name, "Flight mode name"));
else
internalField.Append(new CharField<6>(this, phase.name, "Flight mode name"));
}
else {
if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<10>(this, phase.name, "Flight mode name"));
else
internalField.Append(new ZCharField<6>(this, phase.name, "Flight mode name"));
}
internalField.Append(new SwitchField<9>(this, phase.swtch, board, version));
internalField.Append(new SpareBitsField<7>(this));
}
@@ -819,12 +827,16 @@ class MixField: public TransformedField {
internalField.Append(new UnsignedField<8>(this, mix.delayDown));
internalField.Append(new UnsignedField<8>(this, mix.speedUp));
internalField.Append(new UnsignedField<8>(this, mix.speedDown));
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new ZCharField<6>(this, mix.name));
else if (HAS_LARGE_LCD(board) && version <= 218)
internalField.Append(new ZCharField<8>(this, mix.name));
else
internalField.Append(new ZCharField<6>(this, mix.name));
if (version >= 220)
internalField.Append(new CharField<6>(this, mix.name));
else {
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new ZCharField<6>(this, mix.name));
else if (HAS_LARGE_LCD(board) && version <= 218)
internalField.Append(new ZCharField<8>(this, mix.name));
else
internalField.Append(new ZCharField<6>(this, mix.name));
}
}
else if (IS_TARANIS(board)) {
internalField.Append(new UnsignedField<8>(this, _destCh));
@@ -955,12 +967,16 @@ class InputField: public TransformedField {
internalField.Append(new UnsignedField<9>(this, expo.flightModes));
internalField.Append(new SignedField<8>(this, _weight, "Weight"));
internalField.Append(new SpareBitsField<1>(this));
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new ZCharField<6>(this, expo.name));
else if (HAS_LARGE_LCD(board) && version <= 218)
internalField.Append(new ZCharField<8>(this, expo.name));
else
internalField.Append(new ZCharField<6>(this, expo.name));
if (version >= 220)
internalField.Append(new CharField<6>(this, expo.name));
else {
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new ZCharField<6>(this, expo.name));
else if (HAS_LARGE_LCD(board) && version <= 218)
internalField.Append(new ZCharField<8>(this, expo.name));
else
internalField.Append(new ZCharField<6>(this, expo.name));
}
internalField.Append(new SignedField<8>(this, _offset, "Offset"));
internalField.Append(new CurveReferenceField(this, expo.curve, board, version));
}
@@ -1091,10 +1107,18 @@ class LimitField: public StructField {
Append(new BoolField<1>(this, limit.revert));
Append(new SpareBitsField<3>(this));
Append(new SignedField<8>(this, limit.curve.value));
if (HAS_LARGE_LCD(board))
Append(new ZCharField<6>(this, limit.name));
else
Append(new ZCharField<4>(this, limit.name));
if (version >= 220) {
if (HAS_LARGE_LCD(board))
Append(new CharField<6>(this, limit.name));
else
Append(new CharField<4>(this, limit.name));
}
else {
if (HAS_LARGE_LCD(board))
Append(new ZCharField<6>(this, limit.name));
else
Append(new ZCharField<4>(this, limit.name));
}
}
else if (IS_TARANIS(board)) {
Append(new ConversionField< SignedField<11> >(this, limit.min, exportLimitValue<1000, 1024>, importLimitValue<1000, 1024>));
@@ -1137,7 +1161,10 @@ class CurvesField: public TransformedField {
internalField.Append(new UnsignedField<1>(this, (unsigned int &)curves[i].type));
internalField.Append(new BoolField<1>(this, curves[i].smooth));
internalField.Append(new ConversionField< SignedField<6> >(this, curves[i].count, -5));
internalField.Append(new ZCharField<3>(this, curves[i].name));
if (version >= 220)
internalField.Append(new CharField<3>(this, curves[i].name));
else
internalField.Append(new ZCharField<3>(this, curves[i].name));
}
else if (IS_TARANIS(board)) {
internalField.Append(new UnsignedField<3>(this, (unsigned int &)curves[i].type));
@@ -1892,7 +1919,10 @@ class SensorField: public TransformedField {
{
internalField.Append(new UnsignedField<16>(this, _id, "id/persistentValue"));
internalField.Append(new UnsignedField<8>(this, _instance, "instance/formula"));
internalField.Append(new ZCharField<4>(this, sensor.label));
if (version>= 220)
internalField.Append(new CharField<4>(this, sensor.label));
else
internalField.Append(new ZCharField<4>(this, sensor.label));
if (version >= 219) {
internalField.Append(new UnsignedField<8>(this, _subid, "subid"));
internalField.Append(new UnsignedField<2>(this, sensor.type, "type"));
@@ -2275,6 +2305,257 @@ class ModuleField: public TransformedField {
ProtocolsConversionTable protocolsConversionTable;
};
class TimerField: public TransformedField {
public:
TimerField(DataField * parent, TimerData & timer, Board::Type board, unsigned int version):
TransformedField(parent, internalField),
internalField(this, "Timer"),
timer(timer),
board(board),
version(version),
switchesConversionTable(SwitchesConversionTable::getInstance(board, version))
{
if (version >= 220) {
internalField.Append(new UnsignedField<22>(this, timer.val));
internalField.Append(new SwitchField<10>(this, _swtch, board, version));
internalField.Append(new SignedField<22>(this, timer.pvalue));
internalField.Append(new UnsignedField<3>(this, _mode));
internalField.Append(new UnsignedField<2>(this, timer.countdownBeep));
internalField.Append(new BoolField<1>(this, timer.minuteBeep));
internalField.Append(new UnsignedField<2>(this, timer.persistent));
internalField.Append(new SignedField<2>(this, timer.countdownStart));
if (HAS_LARGE_LCD(board))
internalField.Append(new CharField<8>(this, timer.name, "Timer name"));
else
internalField.Append(new CharField<3>(this, timer.name, "Timer name"));
}
else if (version >= 218) {
internalField.Append(new SignedField<9>(this, _mode_pre220));
internalField.Append(new UnsignedField<23>(this, timer.val));
internalField.Append(new SignedField<24>(this, timer.pvalue));
internalField.Append(new UnsignedField<2>(this, timer.countdownBeep));
internalField.Append(new BoolField<1>(this, timer.minuteBeep));
internalField.Append(new UnsignedField<2>(this, timer.persistent));
internalField.Append(new SignedField<2>(this, timer.countdownStart));
internalField.Append(new SpareBitsField<1>(this)); // direction
if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<8>(this, timer.name, "Timer name"));
else
internalField.Append(new ZCharField<3>(this, timer.name, "Timer name"));
}
else {
internalField.Append(new SignedField<8>(this, _mode_pre220));
internalField.Append(new UnsignedField<24>(this, timer.val));
internalField.Append(new SignedField<24>(this, timer.pvalue));
internalField.Append(new UnsignedField<2>(this, timer.countdownBeep));
internalField.Append(new BoolField<1>(this, timer.minuteBeep));
internalField.Append(new UnsignedField<2>(this, timer.persistent));
internalField.Append(new SpareBitsField<3>(this));
if (IS_TARANIS(board))
internalField.Append(new ZCharField<8>(this, timer.name, "Timer name"));
else
internalField.Append(new ZCharField<3>(this, timer.name, "Timer name"));
}
}
void beforeExport() override
{
_swtch = timer.swtch;
_mode = timer.mode;
}
void afterImport() override
{
if (version >= 220) {
timer.swtch = _swtch;
timer.mode = _mode;
}
else {
if (_mode_pre220 >= 0 and _mode_pre220 <= 4) {
timer.swtch = _swtch;
timer.mode = _mode_pre220;
if (timer.mode >= TimerData::TIMERMODE_START)
timer.mode += 1;
}
else {
timer.mode = TimerData::TIMERMODE_ON;
if (_mode_pre220 > 0)
_mode_pre220 -= 4;
switchesConversionTable->importValue(_mode_pre220, _sw1);
timer.swtch = RawSwitch(_sw1);
}
timer.countdownStart = (timer.countdownStart * -1) - 1;
}
}
protected:
StructField internalField;
TimerData & timer;
Board::Type board;
unsigned int version;
SwitchesConversionTable * switchesConversionTable;
int _mode_pre220 = 0;
unsigned int _mode = 0;
RawSwitch _swtch = RawSwitch(SWITCH_TYPE_NONE, 0);
int _sw1;
};
class ZoneOptionValueUnionField: public UnionField<ZoneOptionValueEnum> {
class ZOVUnsignedField: public UnionField::UnionMember, public StructField {
public:
ZOVUnsignedField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
StructField(this, "ZOV unsigned")
{
Append(new UnsignedField<32>(this, option.value.unsignedValue));
}
bool select(const ZoneOptionValueEnum & attr) const override {
return attr == ZOV_Unsigned;
}
DataField * getField() override
{
return this;
}
};
class ZOVSignedField: public UnionField::UnionMember, public StructField {
public:
ZOVSignedField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
StructField(this, "ZOV signed")
{
Append(new SignedField<32>(this, option.value.signedValue));
}
bool select(const ZoneOptionValueEnum & attr) const override {
return attr == ZOV_Signed;
}
DataField * getField() override
{
return this;
}
};
class ZOVBoolField: public UnionField::UnionMember, public StructField {
public:
ZOVBoolField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
StructField(this, "ZOV bool")
{
Append(new UnsignedField<32>(this, option.value.boolValue));
}
bool select(const ZoneOptionValueEnum & attr) const override {
return attr == ZOV_Bool;
}
DataField * getField() override
{
return this;
}
};
class ZOVStringField: public UnionField::UnionMember, public StructField {
public:
ZOVStringField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
StructField(this, "ZOV string")
{
Append(new CharField<LEN_ZONE_OPTION_STRING>(this, option.value.stringValue));
}
bool select(const ZoneOptionValueEnum & attr) const override {
return attr == ZOV_String;
}
DataField * getField() override
{
return this;
}
};
public:
ZoneOptionValueUnionField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
UnionField<ZoneOptionValueEnum>(parent, option.type, "Zone Option Value Union")
{
Append(new ZOVUnsignedField(parent, option, board, version));
Append(new ZOVSignedField(parent, option, board, version));
Append(new ZOVBoolField(parent, option, board, version));
Append(new ZOVStringField(parent, option, board, version));
}
};
class ZoneOptionValueTypedField: public StructField {
public:
ZoneOptionValueTypedField(DataField * parent, ZoneOptionValueTyped & option, Board::Type board, unsigned int version):
StructField(this, "Zone Option Value Typed")
{
Append(new SignedField<32>(this, (int &)option.type, "Zone Option Value Enum"));
Append(new ZoneOptionValueUnionField(this, option, board, version));
}
};
class WidgetPersistentDataField: public StructField {
public:
WidgetPersistentDataField(DataField * parent, WidgetPersistentData & persistentData, Board::Type board, unsigned int version):
StructField(this, "Widget Persistent")
{
for (int i = 0; i < MAX_WIDGET_OPTIONS; i++) {
Append(new ZoneOptionValueTypedField(this, persistentData.options[i], board, version));
}
}
};
class ZonePersistentDataField: public StructField {
public:
ZonePersistentDataField(DataField * parent, ZonePersistentData & persistentData, Board::Type board, unsigned int version):
StructField(this, "Zone Persistent")
{
Append(new CharField<WIDGET_NAME_LEN>(this, persistentData.widgetName, "Widget name"));
Append(new SpareBitsField<16>(this)); // pad to word boundary
Append(new WidgetPersistentDataField(this, persistentData.widgetData, board, version));
}
};
template <class container>
class WidgetsContainerPersistentField: public StructField {
public:
WidgetsContainerPersistentField(DataField * parent, container & persistentData, int zonecnt, int optioncnt, Board::Type board, unsigned int version):
StructField(this, "Widgets Container Persistent")
{
for (int i = 0; i < zonecnt; i++) {
Append(new ZonePersistentDataField(this, persistentData.zones[i], board, version));
}
for (int i = 0; i < optioncnt; i++) {
Append(new ZoneOptionValueTypedField(this, persistentData.options[i], board, version));
}
}
};
class CustomScreenField: public StructField {
public:
CustomScreenField(DataField * parent, RadioLayout::CustomScreenData & customScreen, Board::Type board, unsigned int version):
StructField(this, "Custom Screen")
{
Append(new CharField<10>(this, customScreen.layoutId, "Layout id"));
//Append(new SpareBitsField<16>(this)); // pad to word boundary not required in this case
Append(new WidgetsContainerPersistentField<LayoutPersistentData>(this, customScreen.layoutPersistentData, MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS, board, version));
//dump();
}
};
class TopBarField: public StructField {
public:
TopBarField(DataField * parent, TopBarPersistentData & topBar, Board::Type board, unsigned int version):
StructField(parent, "Top Bar")
{
Append(new WidgetsContainerPersistentField<TopBarPersistentData>(this, topBar, IS_FLYSKY_NV14(board) ? 2 : 4, MAX_TOPBAR_OPTIONS, board, version));
//dump();
}
};
OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsigned int version, unsigned int variant):
TransformedField(nullptr, internalField),
internalField(this, "ModelData"),
@@ -2287,12 +2568,22 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
qDebug() << QString("OpenTxModelData::OpenTxModelData(name: %1, board: %2, ver: %3, var: %4)").arg(name).arg(board).arg(version).arg(variant);
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new ZCharField<15>(this, modelData.name, "Model name"));
else if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<12>(this, modelData.name, "Model name"));
else
internalField.Append(new ZCharField<10>(this, modelData.name, "Model name"));
if (version >= 220) {
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new CharField<15>(this, modelData.name, "Model name"));
else if (HAS_LARGE_LCD(board))
internalField.Append(new CharField<12>(this, modelData.name, "Model name"));
else
internalField.Append(new CharField<10>(this, modelData.name, "Model name"));
}
else {
if (IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new ZCharField<15>(this, modelData.name, "Model name"));
else if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<12>(this, modelData.name, "Model name"));
else
internalField.Append(new ZCharField<10>(this, modelData.name, "Model name"));
}
internalField.Append(new UnsignedField<8>(this, modelData.moduleData[0].modelId));
internalField.Append(new UnsignedField<8>(this, modelData.moduleData[1].modelId));
@@ -2304,34 +2595,8 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
internalField.Append(new CharField<10>(this, modelData.bitmap, true, "Model bitmap"));
}
for (int i=0; i<MAX_TIMERS(board, version); i++) {
if (version >= 218) {
internalField.Append(new SwitchField<9>(this, modelData.timers[i].mode, board, version, true));
internalField.Append(new UnsignedField<23>(this, modelData.timers[i].val));
internalField.Append(new SignedField<24>(this, modelData.timers[i].pvalue));
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].countdownBeep));
internalField.Append(new BoolField<1>(this, modelData.timers[i].minuteBeep));
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].persistent));
internalField.Append(new SignedField<2>(this, modelData.timers[i].countdownStart));
internalField.Append(new UnsignedField<1>(this, modelData.timers[i].direction));
if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<8>(this, modelData.timers[i].name, "Timer name"));
else
internalField.Append(new ZCharField<3>(this, modelData.timers[i].name, "Timer name"));
}
else {
internalField.Append(new SwitchField<8>(this, modelData.timers[i].mode, board, version, true));
internalField.Append(new UnsignedField<24>(this, modelData.timers[i].val));
internalField.Append(new SignedField<24>(this, modelData.timers[i].pvalue));
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].countdownBeep));
internalField.Append(new BoolField<1>(this, modelData.timers[i].minuteBeep));
internalField.Append(new UnsignedField<2>(this, modelData.timers[i].persistent));
internalField.Append(new SpareBitsField<3>(this));
if (IS_TARANIS(board))
internalField.Append(new ZCharField<8>(this, modelData.timers[i].name, "Timer name"));
else
internalField.Append(new ZCharField<3>(this, modelData.timers[i].name, "Timer name"));
}
for (int i = 0; i < MAX_TIMERS(board, version); i++) {
internalField.Append(new TimerField(this, modelData.timers[i], board, version));
}
internalField.Append(new UnsignedField<3>(this, modelData.telemetryProtocol));
@@ -2385,9 +2650,12 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
else if (!IS_FAMILY_HORUS_OR_T16(board))
internalField.Append(new UnsignedField<8>(this, modelData.switchWarningEnable));
for (int i=0; i<MAX_GVARS(board, version); i++) {
for (int i = 0; i < MAX_GVARS(board, version); i++) {
if (version >= 218) {
internalField.Append(new ZCharField<3>(this, modelData.gvarData[i].name, "GVar name"));
if (version >= 220)
internalField.Append(new CharField<3>(this, modelData.gvarData[i].name, "GVar name"));
else
internalField.Append(new ZCharField<3>(this, modelData.gvarData[i].name, "GVar name"));
internalField.Append(new UnsignedField<12>(this, (unsigned &)modelData.gvarData[i].min));
internalField.Append(new UnsignedField<12>(this, (unsigned &)modelData.gvarData[i].max));
internalField.Append(new BoolField<1>(this, modelData.gvarData[i].popup));
@@ -2457,24 +2725,27 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
}
if (IS_TARANIS(board) && version < 218) {
for (int i=0; i<MAX_CURVES(board, version); i++) {
for (int i = 0; i < MAX_CURVES(board, version); i++) {
internalField.Append(new ZCharField<6>(this, modelData.curves[i].name, "Curve name"));
}
}
if (IS_STM32(board)) {
if (version >= 218) {
for (int i=0; i<MAX_SCRIPTS(board); i++) {
for (int i = 0; i < MAX_SCRIPTS(board); i++) {
ScriptData & script = modelData.scriptData[i];
internalField.Append(new CharField<6>(this, script.filename, true, "Script filename"));
internalField.Append(new ZCharField<6>(this, script.name, "Script name"));
for (int j=0; j<6; j++) {
if (version >= 220)
internalField.Append(new CharField<6>(this, script.name, "Script name"));
else
internalField.Append(new ZCharField<6>(this, script.name, "Script name"));
for (int j = 0; j < 6; j++) {
internalField.Append(new SignedField<16>(this, script.inputs[j]));
}
}
}
else {
for (int i=0; i<7; i++) {
for (int i = 0; i < 7; i++) {
ScriptData & script = modelData.scriptData[i];
internalField.Append(new CharField<8>(this, script.filename, true, "Script filename"));
internalField.Append(new ZCharField<8>(this, script.name, "Script name"));
@@ -2485,21 +2756,29 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
}
}
for (int i=0; i<32; i++) {
if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<4>(this, modelData.inputNames[i], "Input name"));
else
internalField.Append(new ZCharField<3>(this, modelData.inputNames[i], "Input name"));
for (int i = 0; i < 32; i++) {
if (version >= 220) {
if (HAS_LARGE_LCD(board))
internalField.Append(new CharField<4>(this, modelData.inputNames[i], "Input name"));
else
internalField.Append(new CharField<3>(this, modelData.inputNames[i], "Input name"));
}
else {
if (HAS_LARGE_LCD(board))
internalField.Append(new ZCharField<4>(this, modelData.inputNames[i], "Input name"));
else
internalField.Append(new ZCharField<3>(this, modelData.inputNames[i], "Input name"));
}
}
for (int i=0; i<8; i++) {
for (int i = 0; i < 8; i++) {
if (i < Boards::getCapability(board, Board::Pots) + Boards::getCapability(board, Board::Sliders))
internalField.Append(new BoolField<1>(this, modelData.potsWarnEnabled[i]));
else
internalField.Append(new SpareBitsField<1>(this));
}
for (int i=0; i < MAX_POTS_STORAGE(board, version) + MAX_SLIDERS_STORAGE(board, version); i++) {
for (int i = 0; i < MAX_POTS_STORAGE(board, version) + MAX_SLIDERS_STORAGE(board, version); i++) {
internalField.Append(new SignedField<8>(this, modelData.potsWarnPosition[i]));
}
@@ -2512,7 +2791,7 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
internalField.Append(new SpareBitsField<8>(this));
}
for (int i=0; i<MAX_TELEMETRY_SENSORS(board, version); ++i) {
for (int i = 0; i < MAX_TELEMETRY_SENSORS(board, version); ++i) {
internalField.Append(new SensorField(this, modelData, modelData.sensorData[i], board, version));
}
@@ -2521,11 +2800,13 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
}
if (IS_FAMILY_HORUS_OR_T16(board)) {
for (int i = 0; i < 5; i++) {
internalField.Append(new CharField<610>(this, modelData.customScreenData[i], false, "Custom screen blob"));
if (version >= 220) { // data from earlier versions cannot be converted so fields initialised in afterImport
for (int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
internalField.Append(new CustomScreenField(this, modelData.customScreens.customScreenData[i], board, version));
}
internalField.Append(new TopBarField(this, modelData.topBarData, board, version));
internalField.Append(new UnsignedField<8>(this, modelData.view));
}
internalField.Append(new CharField<216>(this, modelData.topbarData, false, "Top bar blob"));
internalField.Append(new SpareBitsField<8>(this)); // current view
}
else if (version >= 219) {
for (int i = 0; i < 4; i++) {
@@ -2537,7 +2818,10 @@ OpenTxModelData::OpenTxModelData(ModelData & modelData, Board::Type board, unsig
internalField.Append(new SpareBitsField<8>(this)); // current view
}
if (version >= 219) {
if (version >= 220) {
internalField.Append(new CharField<8>(this, modelData.registrationId, "ACCESS Registration ID"));
}
else if (version >= 219) {
internalField.Append(new ZCharField<8>(this, modelData.registrationId, "ACCESS Registration ID"));
}
}
@@ -2572,6 +2856,12 @@ void OpenTxModelData::afterImport()
newSwitchWarningStates |= ((value & 0x03) - 1) << (2*i);
}
modelData.switchWarningStates = newSwitchWarningStates;
if (version < 220) { // re-initialise as no conversion possible
const char * layoutId = "Layout2P1"; // currently all using same default though might change for NV14
RadioLayout::init(layoutId, modelData.customScreens);
memset(&modelData.topBarData, 0, sizeof(TopBarPersistentData));
}
}
if (version <= 218 && IS_HORUS_X10(board) && modelData.thrTraceSrc > 3) {
@@ -2610,7 +2900,7 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
internalField.Append(new UnsignedField<16>(this, chkSum));
if (!IS_FAMILY_HORUS_OR_T16(board)) {
if (!IS_FAMILY_HORUS_OR_T16(board) || (IS_FLYSKY_NV14(board))) {
internalField.Append(new UnsignedField<8>(this, generalData.currModelIndex));
internalField.Append(new UnsignedField<8>(this, generalData.contrast));
}
@@ -2676,11 +2966,11 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
internalField.Append(new SignedField<8>(this, generalData.PPM_Multiplier));
internalField.Append(new SignedField<8>(this, generalData.hapticLength));
if (version < 218 || (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board))) {
if (version < 218 || (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board)) {
internalField.Append(new UnsignedField<8>(this, generalData.reNavigation));
}
if (!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) {
if ((!IS_TARANIS(board) && !IS_FAMILY_HORUS_OR_T16(board)) || IS_FLYSKY_NV14(board)) {
internalField.Append(new UnsignedField<8>(this, generalData.stickReverse));
}
@@ -2832,23 +3122,35 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
internalField.Append(new SpareBitsField<16>(this)); // switchUnlockStates
if (version == 217) {
for (int i=0; i<MAX_CUSTOM_FUNCTIONS(board, version); i++) {
for (int i = 0; i < MAX_CUSTOM_FUNCTIONS(board, version); i++) {
internalField.Append(new ArmCustomFunctionField(this, generalData.customFn[i], board, version, variant));
}
}
if (IS_FAMILY_HORUS_OR_T16(board)) {
for (int i=0; i<MAX_SWITCHES(board, version); ++i) {
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
for (int i = 0; i < MAX_SWITCHES(board, version); ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.switchName[i], "Switch name"));
else
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
}
for (int i=0; i<CPN_MAX_STICKS; ++i) {
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
for (int i = 0; i < CPN_MAX_STICKS; ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.stickName[i], "Stick name"));
else
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
}
for (int i=0; i<MAX_POTS_STORAGE(board, version); ++i) {
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
for (int i = 0; i < MAX_POTS_STORAGE(board, version); ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.potName[i], "Pot name"));
else
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
}
for (int i=0; i<MAX_SLIDERS_STORAGE(board, version); ++i) {
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
for (int i = 0; i < MAX_SLIDERS_STORAGE(board, version); ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.sliderName[i], "Slider name"));
else
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
}
if (version <= 218 && IS_HORUS_X10(board)) {
internalField.Append(new SpareBitsField<48>(this)); // DUMMY_ANAS
@@ -2856,44 +3158,67 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
internalField.Append(new CharField<17>(this, generalData.currModelFilename, true, "Current model filename"));
}
else if (IS_TARANIS(board)) {
for (int i=0; i<SWITCHES_CONFIG_SIZE(board, version) / 2; i++) {
for (int i = 0; i < SWITCHES_CONFIG_SIZE(board, version) / 2; i++) {
if (i < MAX_SWITCHES(board, version))
internalField.Append(new UnsignedField<2>(this, generalData.switchConfig[i]));
else
internalField.Append(new SpareBitsField<2>(this));
}
for (int i=0; i<MAX_SWITCHES(board, version); ++i) {
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
for (int i = 0; i < MAX_SWITCHES(board, version); ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.switchName[i], "Switch name"));
else
internalField.Append(new ZCharField<3>(this, generalData.switchName[i], "Switch name"));
}
for (int i=0; i<CPN_MAX_STICKS; ++i) {
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
for (int i = 0; i < CPN_MAX_STICKS; ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.stickName[i], "Stick name"));
else
internalField.Append(new ZCharField<3>(this, generalData.stickName[i], "Stick name"));
}
for (int i=0; i<Boards::getCapability(board, Board::Pots); ++i) {
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
for (int i = 0; i < Boards::getCapability(board, Board::Pots); ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.potName[i], "Pot name"));
else
internalField.Append(new ZCharField<3>(this, generalData.potName[i], "Pot name"));
}
for (int i=0; i<Boards::getCapability(board, Board::Sliders); ++i) {
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
for (int i = 0; i < Boards::getCapability(board, Board::Sliders); ++i) {
if (version >= 220)
internalField.Append(new CharField<3>(this, generalData.sliderName[i], "Slider name"));
else
internalField.Append(new ZCharField<3>(this, generalData.sliderName[i], "Slider name"));
}
}
if (IS_FAMILY_HORUS_OR_T16(board)) {
internalField.Append(new SpareBitsField<1>(this));
internalField.Append(new UnsignedField<7>(this, generalData.backlightOffBright));
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
if (version >= 220)
internalField.Append(new CharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
else
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
}
else if (IS_TARANIS_X9E(board) || (version >= 219 && (IS_TARANIS_X7(board) || IS_TARANIS_X9D(board) || IS_TARANIS_XLITE(board)))) {
internalField.Append(new SpareBitsField<8>(this));
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
if (version >= 220)
internalField.Append(new CharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
else
internalField.Append(new ZCharField<10>(this, generalData.bluetoothName, "Bluetooth name"));
}
if (IS_FAMILY_HORUS_OR_T16(board)) {
internalField.Append(new CharField<8>(this, generalData.themeName, true, "Theme name"));
for (int i=0; i<5; i++) {
internalField.Append(new CharField<8>(this, (char *)generalData.themeOptionValue[i], true, "Theme blob"));
if (version >= 220) { // data from earlier versions cannot be converted so fields initialised in afterImport
internalField.Append(new CharField<8>(this, generalData.themeData.themeName, true, "Theme name"));
for (int i = 0; i < MAX_THEME_OPTIONS; i++) {
internalField.Append(new ZoneOptionValueTypedField(this, generalData.themeData.themePersistentData.options[i], board, version));
}
}
}
if (version >= 219) {
if (version >= 220) {
internalField.Append(new CharField<8>(this, generalData.registrationId, "ACCESS Registration ID"));
}
else if (version >= 219) {
internalField.Append(new ZCharField<8>(this, generalData.registrationId, "ACCESS Registration ID"));
}
@@ -2901,6 +3226,11 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
internalField.Append(new SignedField<8>(this, generalData.gyroMax, "Gyro full scale"));
internalField.Append(new SignedField<8>(this, generalData.gyroOffset, "Gyro Offset"));
}
if (version >= 220) {
internalField.Append(new SignedField<2>(this, generalData.uartSampleMode, "Uart Sample Mode"));
//internalField.Append(new SpareBitsField<6>(this)); // may need padding to end of 8 byte boundary
}
}
void OpenTxGeneralData::beforeExport()
@@ -2908,7 +3238,7 @@ void OpenTxGeneralData::beforeExport()
uint16_t sum = 0;
int count = 0;
for (int i=0; i<inputsCount; i++) {
for (int i = 0; i < inputsCount; i++) {
sum += generalData.calibMid[i];
if (++count == 12) break;
sum += generalData.calibSpanNeg[i];
@@ -2922,4 +3252,11 @@ void OpenTxGeneralData::beforeExport()
void OpenTxGeneralData::afterImport()
{
if (IS_FAMILY_HORUS_OR_T16(board)) {
if (version < 220) { // re-initialise as no conversion possible
const char * themeName = IS_FLYSKY_NV14(board) ? "FlySky" : "EdgeTX";
RadioTheme::init(themeName, generalData.themeData);
}
}
}
@@ -104,6 +104,8 @@ const char * OpenTxEepromInterface::getName()
return "OpenTX for FrSky X10";
case BOARD_X10_EXPRESS:
return "OpenTX for FrSky X10 Express";
case BOARD_FLYSKY_NV14:
return "OpenTX for FlySky NV14";
default:
return "OpenTX for an unknown board";
}
@@ -280,7 +282,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
uint8_t OpenTxEepromInterface::getLastDataVersion(Board::Type board)
{
return 219;
return 220;
}
void OpenTxEepromInterface::showErrors(const QString & title, const QStringList & errors)
@@ -627,7 +629,9 @@ int OpenTxFirmware::getCapability(::Capability capability)
case HasSDLogs:
return true;
case LcdWidth:
if (IS_FAMILY_HORUS_OR_T16(board))
if (IS_FLYSKY_NV14(board))
return 320;
else if (IS_FAMILY_HORUS_OR_T16(board))
return 480;
else if (IS_TARANIS_SMALL(board))
return 128;
@@ -636,7 +640,9 @@ int OpenTxFirmware::getCapability(::Capability capability)
else
return 128;
case LcdHeight:
if (IS_FAMILY_HORUS_OR_T16(board))
if (IS_FLYSKY_NV14(board))
return 480;
else if (IS_FAMILY_HORUS_OR_T16(board))
return 272;
else
return 64;
@@ -726,16 +732,17 @@ int OpenTxFirmware::getCapability(::Capability capability)
case HasAuxSerialMode:
return (IS_FAMILY_HORUS_OR_T16(board) && !IS_TARANIS_SMALL(board)) ? true : false;
case HasAux2SerialMode:
return (IS_FAMILY_HORUS_OR_T16(board) && !IS_TARANIS_SMALL(board)) ? true : false;
return (IS_FAMILY_HORUS_OR_T16(board) && !IS_TARANIS_SMALL(board) && !IS_FLYSKY_NV14(board)) ? true : false;
case HasBluetooth:
return (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_TARANIS_X9DP_2019(board)) ? true : false;
return (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X7(board) || IS_TARANIS_XLITE(board)|| IS_TARANIS_X9E(board) || IS_TARANIS_X9DP_2019(board) || IS_FLYSKY_NV14(board)) ? true : false;
case HasAntennaChoice:
return ((IS_FAMILY_HORUS_OR_T16(board) && board != Board::BOARD_X10_EXPRESS) || (IS_TARANIS_XLITE(board) && !IS_TARANIS_XLITES(board))) ? true : false;
case HasADCJitterFilter:
return IS_HORUS_OR_TARANIS(board) ? true : false;
case HasTelemetryBaudrate:
return IS_HORUS_OR_TARANIS(board) ? true : false;
case TopBarZones:
return getCapability(LcdWidth) > getCapability(LcdHeight) ? 4 : 2;
default:
return 0;
}
@@ -764,14 +771,16 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
return true;
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_LR12:
return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board);
return !IS_ACCESS_RADIO(board, id) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
case PULSES_PXX_XJT_D8:
return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board);
return !(IS_ACCESS_RADIO(board, id) || id.contains("eu")) && !IS_FAMILY_T16(board) && !IS_FAMILY_T12(board) && !IS_FLYSKY_NV14(board);
case PULSES_ACCESS_ISRM:
case PULSES_ACCST_ISRM_D16:
return IS_ACCESS_RADIO(board, id);
case PULSES_MULTIMODULE:
return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board) || IS_JUMPER_T18(board) || IS_RADIOMASTER_TX12(board) || IS_JUMPER_TLITE(board);
case PULSES_AFHDS3:
return IS_FLYSKY_NV14(board);
default:
return false;
}
@@ -927,6 +936,24 @@ EepromLoadErrors OpenTxEepromInterface::checkVersion(unsigned int version)
return OLD_VERSION;
case 219:
// 60 (Horus / X9) / 40 (others) telemetry sensors instead of 32
// ALL: ReceiverData array added
// ALL: registrationId added
// ALL: failsafeChannels moved from ModuleData to ModelData
// ALL: ModuleData / TrainerModuleData modified
// PCBX9 : 6 chars for expos / mixes names instead of 8
// on X7: 2 additional switches
// on X9D / X9D+: 1 additional switch
// on xlite : 2 more storage switches
// on X10: 2 additional pots => 12 multipos switches
return OLD_VERSION;
case 220:
// Refer radio/src/storage/conversions/conversions_219_220.cpp
// ALL: TimerData split mode into separate mode and switch and change modes
// ALL: TimerData countdownStart invert value
// ALL: convert more fields from zchar to normal string
// COLORLCD: change CustomScreenData and TopBarPersistentData
break;
default:
@@ -1112,7 +1139,7 @@ QString OpenTxFirmware::getFirmwareUrl()
QString OpenTxFirmware::getReleaseNotesUrl()
{
return getFirmwareBaseUrl() % QStringLiteral("releasenotes.txt");
return QString("%1/downloads").arg(OPENTX_HOME_PAGE_URL);
}
QString OpenTxFirmware::getStampUrl()
@@ -1368,24 +1395,31 @@ void registerOpenTxFirmwares()
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FlySky NV14 board */
firmware = new OpenTxFirmware("opentx-nv14", QCoreApplication::translate("Firmware", "FlySky NV14"), BOARD_FLYSKY_NV14);
addOpenTxFrskyOptions(firmware);
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
addOpenTxRfOptions(firmware, FLEX + AFHDS3);
registerOpenTxFirmware(firmware);
/* 9XR-Pro */
firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO);
addOpenTxArm9xOptions(firmware, false);
registerOpenTxFirmware(firmware);
registerOpenTxFirmware(firmware, true);
/* ar9x board */
firmware = new OpenTxFirmware("opentx-ar9x", Firmware::tr("9X with AR9X board"), BOARD_AR9X);
addOpenTxArm9xOptions(firmware);
addOpenTxArm9xOptions(firmware, true);
//firmware->addOption("rtc", Firmware::tr("Optional RTC added"));
//firmware->addOption("volume", Firmware::tr("i2c volume control added"));
registerOpenTxFirmware(firmware);
registerOpenTxFirmware(firmware, true);
/* Sky9x board */
firmware = new OpenTxFirmware("opentx-sky9x", Firmware::tr("9X with Sky9x board"), BOARD_SKY9X);
addOpenTxArm9xOptions(firmware);
registerOpenTxFirmware(firmware);
registerOpenTxFirmware(firmware, true);
Firmware::setDefaultVariant(Firmware::getFirmwareForId("opentx-x9d+"));
Firmware::setDefaultVariant(Firmware::getFirmwareForId("opentx-tx16s"));
Firmware::setCurrentVariant(Firmware::getDefaultVariant());
}
+4 -4
View File
@@ -50,15 +50,15 @@ QString RawSwitch::toString(Board::Type board, const GeneralSettings * const gen
tr("TrmV Down"), tr("TrmV Up")
};
static const QString rotaryEncoders[] = {
tr("REa"), tr("REb")
};
static const QString timerModes[] = {
tr("OFF"), tr("ON"),
tr("THs"), tr("TH%"), tr("THt")
};
static const QString rotaryEncoders[] = {
tr("REa"), tr("REb")
};
static const QStringList directionIndicators = QStringList()
<< CPN_STR_SW_INDICATOR_UP
<< CPN_STR_SW_INDICATOR_NEUT
+1 -4
View File
@@ -18,8 +18,7 @@
* GNU General Public License for more details.
*/
#ifndef RAWSWITCH_H
#define RAWSWITCH_H
#pragma once
#include "boards.h"
#include "constants.h"
@@ -102,5 +101,3 @@ class RawSwitch {
RawSwitchType type;
int index;
};
#endif // RAWSWITCH_H
+2 -2
View File
@@ -322,10 +322,10 @@ QString SensorData::precToString(const int value)
}
// static
QString SensorData::sourceToString(const ModelData * model, const int index, const bool sign)
QString SensorData::sourceToString(const ModelData * model, const int index, const bool positivesign)
{
if (model) {
const QString prfx = sign ? index < 0 ? "-" : "+" : "";
const QString prfx = index < 0 ? "-" : positivesign ? "+" : "";
if (abs(index) > 0) {
const SensorData &sd = model->sensorData[abs(index) - 1];
+1 -1
View File
@@ -197,7 +197,7 @@ class SensorData {
void formulaChanged();
void unitChanged();
static QString sourceToString(const ModelData * model, const int index, const bool sign = false);
static QString sourceToString(const ModelData * model, const int index, const bool positivesign = false);
static bool isSourceAvailable(const ModelData * model, const int index);
static QString idToString(const int value);
static QString typeToString(const int value);
+51 -5
View File
@@ -26,23 +26,23 @@ void TimerData::convert(RadioDataConversionState & cstate)
{
cstate.setComponent(tr("TMR"), 1);
cstate.setSubComp(tr("Timer %1").arg(cstate.subCompIdx + 1));
mode.convert(cstate);
swtch.convert(cstate);
}
void TimerData::clear()
{
memset(reinterpret_cast<void *>(this), 0, sizeof(TimerData));
mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0);
swtch = RawSwitch(SWITCH_TYPE_NONE);
}
bool TimerData::isEmpty()
{
return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWNBEEP_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/);
return (swtch == RawSwitch(SWITCH_TYPE_NONE, 0) && mode == TIMERMODE_OFF && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWNBEEP_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/);
}
bool TimerData::isModeOff()
{
return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0);
return swtch == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0);
}
QString TimerData::nameToString(int index) const
@@ -78,12 +78,23 @@ QString TimerData::valToString() const
return valToString(val);
}
QString TimerData::modeToString() const
{
return modeToString(mode);
}
void TimerData::countdownBeepChanged()
{
if (countdownBeep == COUNTDOWNBEEP_SILENT)
countdownStart = 0;
}
void TimerData::modeChanged()
{
if (mode != TIMERMODE_START)
swtch = RawSwitch(SWITCH_TYPE_NONE);
}
// static
QString TimerData::countdownBeepToString(const int value)
{
@@ -145,6 +156,27 @@ QString TimerData::valToString(const int value)
return DataHelpers::timeToString(value, TIMESTR_MASK_HRSMINS | TIMESTR_MASK_ZEROHRS);
}
// static
QString TimerData::modeToString(const int value)
{
switch(value) {
case TIMERMODE_OFF:
return tr("OFF");
case TIMERMODE_ON:
return tr("ON");
case TIMERMODE_START:
return tr("Start");
case TIMERMODE_THR:
return tr("THs");
case TIMERMODE_THR_REL:
return tr("TH%");
case TIMERMODE_THR_START:
return tr("THt");
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
// static
AbstractStaticItemModel * TimerData::countdownBeepItemModel()
{
@@ -165,7 +197,7 @@ AbstractStaticItemModel * TimerData::countdownStartItemModel()
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName(AIM_TIMER_COUNTDOWNSTART);
for (int i = COUNTDOWNSTART_LAST; i >= COUNTDOWNSTART_FIRST; i--) {
for (int i = COUNTDOWNSTART_FIRST; i <= COUNTDOWNSTART_LAST; i++) {
mdl->appendToItemList(countdownStartToString(i), i);
}
@@ -186,3 +218,17 @@ AbstractStaticItemModel * TimerData::persistentItemModel()
mdl->loadItemList();
return mdl;
}
// static
AbstractStaticItemModel * TimerData::modeItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName(AIM_TIMER_MODE);
for (int i = 0; i < TIMERMODE_COUNT; i++) {
mdl->appendToItemList(modeToString(i), i);
}
mdl->loadItemList();
return mdl;
}
+23 -7
View File
@@ -31,6 +31,7 @@ class AbstractStaticItemModel;
constexpr char AIM_TIMER_COUNTDOWNBEEP[] {"timerdata.countdownBeep"};
constexpr char AIM_TIMER_COUNTDOWNSTART[] {"timerdata.countdownStart"};
constexpr char AIM_TIMER_PERSISTENT[] {"timerdata.persistent"};
constexpr char AIM_TIMER_MODE[] {"timerdata.mode"};
constexpr int TIMER_NAME_LEN {8};
@@ -47,12 +48,12 @@ class TimerData {
};
enum CountDownStart {
COUNTDOWNSTART_30 = -2,
COUNTDOWNSTART_FIRST = COUNTDOWNSTART_30,
COUNTDOWNSTART_20,
COUNTDOWNSTART_5 = -2,
COUNTDOWNSTART_FIRST = COUNTDOWNSTART_5,
COUNTDOWNSTART_10,
COUNTDOWNSTART_5,
COUNTDOWNSTART_LAST = COUNTDOWNSTART_5
COUNTDOWNSTART_20,
COUNTDOWNSTART_30,
COUNTDOWNSTART_LAST = COUNTDOWNSTART_30
};
enum PersistentType {
@@ -62,16 +63,27 @@ class TimerData {
PERSISTENT_COUNT
};
enum TimerMode {
TIMERMODE_OFF,
TIMERMODE_ON,
TIMERMODE_START,
TIMERMODE_THR,
TIMERMODE_THR_REL,
TIMERMODE_THR_START,
TIMERMODE_COUNT,
TIMERMODE_MAX = TIMERMODE_COUNT - 1
};
TimerData() { clear(); }
RawSwitch mode;
RawSwitch swtch;
unsigned int mode;
char name[TIMER_NAME_LEN + 1];
bool minuteBeep;
unsigned int countdownBeep;
unsigned int val;
unsigned int persistent;
int countdownStart;
unsigned int direction;
int pvalue;
void convert(RadioDataConversionState & cstate);
@@ -84,15 +96,19 @@ class TimerData {
QString persistentToString(const bool verbose = true) const;
QString pvalueToString() const;
QString valToString() const;
QString modeToString() const;
void countdownBeepChanged();
void modeChanged();
static QString countdownBeepToString(const int value);
static QString countdownStartToString(const int value);
static QString persistentToString(const int value, const bool verbose = true);
static QString pvalueToString(const int value);
static QString valToString(const int value);
static QString modeToString(const int value);
static AbstractStaticItemModel * countdownBeepItemModel();
static AbstractStaticItemModel * countdownStartItemModel();
static AbstractStaticItemModel * persistentItemModel();
static AbstractStaticItemModel * modeItemModel();
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 48 KiB

+40 -66
View File
@@ -48,6 +48,7 @@
#include "dialogs/filesyncdialog.h"
#include "profilechooser.h"
#include "constants.h"
#include <QtGui>
#include <QFileInfo>
@@ -64,8 +65,8 @@
#define INTERACTIVE_DOWNLOAD 4
#define AUTOMATIC_DOWNLOAD 8
#define OPENTX_DOWNLOADS_PAGE_URL QStringLiteral("http://www.open-tx.org/downloads")
#define DONATE_STR QStringLiteral("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QUZ48K4SEXDP2")
#define OPENTX_DOWNLOADS_PAGE_URL QStringLiteral("http://www.edge-tx.org/downloads")
#define DONATE_STR QStringLiteral("https://www.edge-tx.org")
#ifdef Q_OS_MACOS
#define COMPANION_STAMP QStringLiteral("companion-macosx.stamp")
@@ -129,7 +130,7 @@ MainWindow::MainWindow():
else {
if (!g.previousVersion().isEmpty())
g.warningId(g.warningId() | AppMessages::MSG_UPGRADED);
if (g.promptProfile()) {
chooseProfile();
}
@@ -263,6 +264,9 @@ void MainWindow::checkForUpdates()
return;
}
QMessageBox::information(this, CPN_STR_APP_NAME, tr("Updates via Companion currently unavailable. Please go to the EdgeTX <a href='%1'>website</a> for installation instructions. Update the application settings to disable this message.").arg("https://github.com/EdgeTX/edgetx.github.io/wiki/EdgeTX-Installation-Guide"));
return;
if (networkManager)
disconnect(networkManager, 0, this, 0);
else
@@ -378,7 +382,7 @@ void MainWindow::checkForCompanionUpdateFinished(QNetworkReply * reply)
}
}
#else
QMessageBox::warning(this, tr("New release available"), tr("A new release of Companion is available, please check the <a href='%1'>OpenTX website!</a>").arg(OPENTX_DOWNLOADS_PAGE_URL));
QMessageBox::warning(this, tr("New release available"), tr("A new release of Companion is available, please check the <a href='%1'>EdgeTX website!</a>").arg(OPENTX_DOWNLOADS_PAGE_URL));
#endif
}
else {
@@ -521,17 +525,26 @@ void MainWindow::checkForFirmwareUpdateFinished(QNetworkReply * reply)
int currentVersion = g.fwRev.get(Firmware::getCurrentVariant()->getId());
QString currentVersionString = index2version(currentVersion);
QMessageBox msgBox;
msgBox.setWindowTitle(CPN_STR_APP_NAME);
QSpacerItem * horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
QGridLayout * layout = (QGridLayout*)msgBox.layout();
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
QString msgText;
if (currentVersion == 0)
msgText = tr("Firmware %1 does not seem to have ever been downloaded.\nVersion %2 is available.\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
.arg(Firmware::getCurrentVariant()->getId()).arg(fullVersionString);
else if (version > currentVersion)
msgText = tr("A new version of %1 firmware is available:\n - current is %2\n - newer is %3\n\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
.arg(Firmware::getCurrentVariant()->getId()).arg(currentVersionString).arg(fullVersionString);
else if (checkForUpdatesState == INTERACTIVE_DOWNLOAD)
QMessageBox::information(this, CPN_STR_APP_NAME, tr("No updates available at this time."));
if (currentVersion == 0 || version > currentVersion) {
QMessageBox msgBox;
msgBox.setWindowTitle(CPN_STR_APP_NAME);
QSpacerItem * horizontalSpacer = new QSpacerItem(500, 0, QSizePolicy::Minimum, QSizePolicy::Expanding);
QGridLayout * layout = (QGridLayout*)msgBox.layout();
layout->addItem(horizontalSpacer, layout->rowCount(), 0, 1, layout->columnCount());
if (currentVersion == 0) {
QString rn = getCurrentFirmware()->getReleaseNotesUrl();
QAbstractButton *rnButton = nullptr;
msgBox.setText(tr("Firmware %1 does not seem to have ever been downloaded.\nVersion %2 is available.\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
.arg(Firmware::getCurrentVariant()->getId()).arg(fullVersionString));
msgBox.setText(msgText);
QAbstractButton *YesButton = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
msgBox.addButton(tr("No"), QMessageBox::NoRole);
if (!rn.isEmpty()) {
@@ -541,57 +554,17 @@ void MainWindow::checkForFirmwareUpdateFinished(QNetworkReply * reply)
msgBox.resize(0, 0);
msgBox.exec();
if (msgBox.clickedButton() == rnButton) {
ReleaseNotesFirmwareDialog * dialog = new ReleaseNotesFirmwareDialog(this, rn);
dialog->exec();
QDesktopServices::openUrl(QUrl(rn));
int ret2 = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to download version %1 now ?").arg(fullVersionString), QMessageBox::Yes | QMessageBox::No);
if (ret2 == QMessageBox::Yes)
download = true;
else
ignore = true;
}
else if (msgBox.clickedButton() == YesButton ) {
else if (msgBox.clickedButton() == YesButton )
download = true;
}
else {
else
ignore = true;
}
}
else if (version > currentVersion) {
QString rn = getCurrentFirmware()->getReleaseNotesUrl();
QAbstractButton *rnButton = nullptr;
msgBox.setText(tr("A new version of %1 firmware is available:\n - current is %2\n - newer is %3\n\nDo you want to download it now?\n\nWe recommend you view the release notes using the button below to learn about any changes that may be important to you.")
.arg(Firmware::getCurrentVariant()->getId()).arg(currentVersionString).arg(fullVersionString));
QAbstractButton *YesButton = msgBox.addButton(tr("Yes"), QMessageBox::YesRole);
msgBox.addButton(tr("No"), QMessageBox::NoRole);
if (!rn.isEmpty()) {
rnButton = msgBox.addButton(tr("Release Notes"), QMessageBox::ActionRole);
}
msgBox.setIcon(QMessageBox::Question);
msgBox.resize(0,0);
msgBox.exec();
if( msgBox.clickedButton() == rnButton ) {
ReleaseNotesFirmwareDialog * dialog = new ReleaseNotesFirmwareDialog(this, rn);
dialog->exec();
int ret2 = QMessageBox::question(this, CPN_STR_APP_NAME, tr("Do you want to download version %1 now ?").arg(fullVersionString),
QMessageBox::Yes | QMessageBox::No);
if (ret2 == QMessageBox::Yes) {
download = true;
}
else {
ignore = true;
}
}
else if (msgBox.clickedButton() == YesButton ) {
download = true;
}
else {
ignore = true;
}
}
else {
if (checkForUpdatesState == INTERACTIVE_DOWNLOAD) {
QMessageBox::information(this, CPN_STR_APP_NAME, tr("No updates available at this time."));
}
}
}
@@ -737,7 +710,7 @@ void MainWindow::newFile()
void MainWindow::openDocURL()
{
QString link = "http://www.open-tx.org/documents.html";
QString link = "http://www.edge-tx.org/";
QDesktopServices::openUrl(QUrl(link));
}
@@ -906,7 +879,7 @@ void MainWindow::sdsync()
void MainWindow::changelog()
{
QString link = "http://www.open-tx.org";
QString link = "http://www.edge-tx.org";
QDesktopServices::openUrl(QUrl(link));
}
@@ -1037,15 +1010,15 @@ void MainWindow::logFile()
void MainWindow::about()
{
QString aboutStr = "<center><img src=\":/images/companion-title.png\"></center><br/>";
aboutStr.append(tr("OpenTX Home Page: <a href='%1'>%1</a>").arg("http://www.open-tx.org"));
aboutStr.append(tr("EdgeTX Home Page: <a href='%1'>%1</a>").arg("http://www.edge-tx.org"));
aboutStr.append("<br/><br/>");
aboutStr.append(tr("The OpenTX Companion project was originally forked from <a href='%1'>eePe</a>").arg("http://code.google.com/p/eepe"));
aboutStr.append(tr("The EdgeTX Companion project was originally forked from <a href='%1'>OpenTX</a>").arg("https://github.com/opentx/opentx"));
aboutStr.append("<br/><br/>");
aboutStr.append(tr("If you've found this program useful, please support by <a href='%1'>donating</a>").arg(DONATE_STR));
aboutStr.append("<br/><br/>");
aboutStr.append(QString("Version %1, %2").arg(VERSION).arg(__DATE__));
aboutStr.append("<br/><br/>");
aboutStr.append(tr("Copyright OpenTX Team") + QString("<br/>&copy; 2011-%1<br/>").arg(QString(__DATE__).right(4)));
aboutStr.append(tr("Copyright EdgeTX Team") + QString("<br/>&copy; 2011-%1<br/>").arg(QString(__DATE__).right(4)));
QMessageBox msgBox(this);
msgBox.setWindowIcon(CompanionIcon("information.png"));
msgBox.setWindowTitle(tr("About Companion"));
@@ -1126,7 +1099,7 @@ void MainWindow::updateMenus()
updateRecentFileActions();
updateProfilesActions();
setWindowTitle(tr("OpenTX Companion %1 - Radio: %2 - Profile: %3").arg(VERSION).arg(getCurrentFirmware()->getName()).arg(g.profile[g.id()].name()));
setWindowTitle(tr("EdgeTX Companion %1 - Radio: %2 - Profile: %3").arg(VERSION).arg(getCurrentFirmware()->getName()).arg(g.profile[g.id()].name()));
}
MdiChild * MainWindow::createMdiChild()
@@ -1208,7 +1181,7 @@ void MainWindow::retranslateUi(bool showMsg)
trAct(logsAct, tr("View Log File..."), tr("Open and view log file"));
trAct(appPrefsAct, tr("Settings..."), tr("Edit Settings"));
trAct(fwPrefsAct, tr("Download..."), tr("Download firmware and voice files"));
trAct(checkForUpdatesAct, tr("Check for Updates..."), tr("Check OpenTX and Companion updates"));
trAct(checkForUpdatesAct, tr("Check for Updates..."), tr("Check EdgeTX and Companion updates"));
trAct(changelogAct, tr("Release notes..."), tr("Show release notes"));
trAct(compareAct, tr("Compare Models..."), tr("Compare models"));
trAct(editSplashAct, tr("Edit Radio Splash Image..."), tr("Edit the splash image of your Radio"));
@@ -1216,13 +1189,13 @@ void MainWindow::retranslateUi(bool showMsg)
trAct(writeFlashAct, tr("Write Firmware to Radio"), tr("Write firmware to Radio"));
trAct(sdsyncAct, tr("Synchronize SD"), tr("SD card synchronization"));
trAct(openDocURLAct, tr("Manuals and other Documents"), tr("Open the OpenTX document page in a web browser"));
trAct(openDocURLAct, tr("Manuals and other Documents"), tr("Open the EdgeTX document page in a web browser"));
trAct(writeEepromAct, tr("Write Models and Settings To Radio"), tr("Write Models and Settings to Radio"));
trAct(readEepromAct, tr("Read Models and Settings From Radio"), tr("Read Models and Settings from Radio"));
trAct(burnConfigAct, tr("Configure Communications..."), tr("Configure software for communicating with the Radio"));
trAct(writeBUToRadioAct, tr("Write Backup to Radio"), tr("Write Backup from file to Radio"));
trAct(readBUToFileAct, tr("Backup Radio to File"), tr("Save a complete backup file of all settings and model data in the Radio"));
trAct(createProfileAct, tr("Add Radio Profile"), tr("Create a new Radio Settings Profile"));
trAct(copyProfileAct, tr("Copy Current Radio Profile"), tr("Duplicate current Radio Settings Profile"));
trAct(deleteProfileAct, tr("Delete Current Radio Profile..."), tr("Delete the current Radio Settings Profile"));
@@ -1297,7 +1270,7 @@ void MainWindow::createActions()
aboutAct = addAct("information.png", SLOT(about()));
openDocURLAct = addAct("changelog.png", SLOT(openDocURL()));
changelogAct = addAct("changelog.png", SLOT(changelog()));
// these two get assigned menus in createMenus()
recentFilesAct = addAct("recentdocument.png");
profilesMenuAct = addAct("profiles.png");
@@ -1311,6 +1284,7 @@ void MainWindow::createActions()
actTabbedWindows->setCheckable(true);
compareAct->setEnabled(false);
fwPrefsAct->setEnabled(false);
}
void MainWindow::createMenus()
@@ -1391,7 +1365,7 @@ void MainWindow::createMenus()
helpMenu->addSeparator();
helpMenu->addAction(changelogAct);
helpMenu->addSeparator();
recentFilesMenu = new QMenu(this);
recentFilesMenu->setToolTipsVisible(true);
for ( int i = 0; i < g.historySize(); ++i) {
+18 -2
View File
@@ -1375,10 +1375,10 @@ bool MdiChild::saveAs(bool isNew)
#ifdef __APPLE__
QString filter;
#else
QString filter(OTX_FILES_FILTER);
QString filter(OTX_FILES_FILTER % YML_FILES_FILTER);
#endif
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" +fi.fileName(), filter);
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" + fi.fileName(), filter);
if (fileName.isEmpty())
return false;
g.eepromDir( QFileInfo(fileName).dir().absolutePath() );
@@ -1530,6 +1530,22 @@ int MdiChild::askQuestion(const QString & msg, QMessageBox::StandardButtons butt
void MdiChild::writeEeprom() // write to Tx
{
if (g.confirmWriteModelsAndSettings()) {
QMessageBox msgbox;
msgbox.setText(tr("You are about to overwrite ALL models on the Radio."));
msgbox.setInformativeText(tr("Do you want to continue?"));
msgbox.setIcon(QMessageBox::Icon::Question);
msgbox.setStandardButtons(QMessageBox::Yes | QMessageBox::Abort);
msgbox.setDefaultButton(QMessageBox::Abort);
QCheckBox *cb = new QCheckBox(tr("Don't show this message again"));
msgbox.setCheckBox(cb);
connect(cb, &QCheckBox::stateChanged, [=](const int &state){ g.confirmWriteModelsAndSettings(!state); });
if (msgbox.exec() == QMessageBox::Abort)
return;
}
Board::Type board = getCurrentBoard();
if (IS_FAMILY_HORUS_OR_T16(board)) {
QString radioPath = findMassstoragePath("RADIO", true);
+4
View File
@@ -25,6 +25,8 @@ set(modeledit_SRCS
mixerslistwidget.cpp
# node.cpp ## node and edge are built in common lib because also used by simulator
# edge.cpp ## commenting them here avoids a "duplicate target" warning eg. from ninja
colorcustomscreens.cpp
telemetry_customscreens.cpp
)
set(modeledit_HDRS
@@ -36,6 +38,8 @@ set(modeledit_HDRS
customfunctions.h
mixerslistwidget.h
# node.h
colorcustomscreens.h
telemetry_customscreens.h
)
set(modeledit_UIS
@@ -0,0 +1,456 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "colorcustomscreens.h"
#include "customisation_data.h"
#include "appdata.h"
#include <QImage>
#include <QFile>
// used to screen out raw data that cannot be displayed in a user meaningful format at this time TODO remove
#define SHOW_RAW_INFO (1==0)
UserInterfacePanel::UserInterfacePanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
ModelPanel(parent, model, generalSettings, firmware)
{
RadioTheme::ThemeData & td = generalSettings.themeData;
grid = new QGridLayout(this);
int col = 0;
int row = 0;
addGridBlankRow(grid, row);
addGridLabel(grid, tr("Theme"), row, col++);
addGridLabel(grid, td.themeName, row++, col++);
col = 0;
addGridBlankRow(grid, row);
addGridLabel(grid, tr("Background"), row, col++);
QLabel * img = new QLabel(this);
img->setText(tr("background image"));
QString path = QString(g.profile[g.id()].sdPath()).trimmed();
if (!path.isEmpty()) {
path.append(QString("/THEMES/%1/background.png").arg(td.themeName));
QFile f(path);
if (f.exists()) {
QImage image(path);
if (!image.isNull()) {
img->setText("");
img->setFixedSize(QSize(firmware->getCapability(LcdWidth) / 2, firmware->getCapability(LcdHeight) / 2));
img->setPixmap(QPixmap::fromImage(image.scaled(firmware->getCapability(LcdWidth) / 2, firmware->getCapability(LcdHeight) / 2)));
}
}
}
grid->addWidget(img, row++, col++);
col = 0;
addGridLabel(grid, tr("Top Bar"), row, col++);
int widgetdetailsrow = row;
int widgetdetailscol = col + 1;
int firstenabledbtn = -1;
QGridLayout * tbgrid = new QGridLayout();
for (int i = 0; i < firmware->getCapability(TopBarZones); i++) {
ZonePersistentData & zpd = model.topBarData.zones[i];
QPushButton * btn = new QPushButton(zpd.widgetName, this);
btn->setProperty("index", i);
btn->setFixedSize(QSize(90, 30));
widgetbtns.append(btn);
if (zpd.widgetName[0] != '\0' && SHOW_RAW_INFO) {
if (firstenabledbtn < 0)
firstenabledbtn = i;
QFrame * wgt = new QFrame();
wgt->setFrameStyle(QFrame::StyledPanel);
wgt->setFrameShadow(QFrame::Plain);
wgt->setVisible(false);
optswidgets.append(wgt);
QGridLayout * layout = new QGridLayout(wgt); // must be owned by QWidget so visibility can be set
layout->addLayout(addOptionsLayout<WidgetPersistentData>(zpd.widgetData, MAX_WIDGET_OPTIONS, zpd.widgetName), 0, 0);
optsgrids.append(layout);
connect(btn, &QPushButton::clicked, this, [&]() {
bool ok;
int idx = sender()->property("index").toInt(&ok);
if (ok) {
if (currindex >= 0) {
optswidgets.at(currindex)->setVisible(false);
}
optswidgets.at(idx)->setVisible(true);
currindex = idx;
}
});
}
else if (SHOW_RAW_INFO) {
btn->setEnabled(false);
}
div_t posn = div(i, MAX_TOPBAR_ZONES);
tbgrid->addWidget(btn, posn.quot, posn.rem);
}
grid->addLayout(tbgrid, row++, col++, Qt::AlignTop);
addGridBlankRow(grid, row);
col = 0;
if (SHOW_RAW_INFO) {
addGridLabel(grid, tr("Theme"), row, col++);
grid->addLayout(addOptionsLayout<RadioTheme::PersistentData>(td.themePersistentData, MAX_THEME_OPTIONS), row, col);
}
// the grid must be fully built for the rowspan to work as required
foreach (QWidget * wgt, optswidgets) {
grid->addWidget(wgt, widgetdetailsrow, widgetdetailscol, 3, Qt::AlignTop);
}
addHSpring(grid, grid->columnCount(), 0);
addVSpring(grid, 0, grid->rowCount());
disableMouseScrolling();
if (firstenabledbtn >= 0)
widgetbtns.at(firstenabledbtn)->click();
}
UserInterfacePanel::~UserInterfacePanel()
{
foreach (QGridLayout * grid, optsgrids) {
delete grid;
}
foreach (QFrame * wgt, optswidgets) {
delete wgt;
}
foreach (QPushButton * btn, widgetbtns) {
delete btn;
}
}
/*
CustomScreenPanel
*/
CustomScreenPanel::CustomScreenPanel(QWidget * parent, ModelData & model, int index, GeneralSettings & generalSettings, Firmware * firmware):
ModelPanel(parent, model, generalSettings, firmware)
{
RadioLayout::CustomScreens & scrns = model.customScreens;
grid = new QGridLayout(this);
int col = 0;
int row = 0;
addGridBlankRow(grid, row);
addGridLabel(grid, tr("Layout:"), row, col++);
// currently no point continuing if no layout but will change if editing becomes possible
if (model.customScreens.customScreenData[index].layoutId[0] == '\0') {
addGridLabel(grid, tr("None"), row, col);
addHSpring(grid, grid->columnCount(), 0);
addVSpring(grid, 0, grid->rowCount());
return;
}
QLabel * img = new QLabel(this);
QString path(QString(":/layouts/mask_%1.png").arg(QString(scrns.customScreenData[index].layoutId).toLower()));
QFile f(path);
if (f.exists()) {
QImage image(path);
if (!image.isNull()) {
img->setFixedSize(QSize(firmware->getCapability(LcdWidth) / 5, firmware->getCapability(LcdHeight) / 5));
img->setPixmap(QPixmap::fromImage(image.scaled(firmware->getCapability(LcdWidth) / 5, firmware->getCapability(LcdHeight) / 5)));
}
}
grid->addWidget(img, row++, col++);
LayoutPersistentData & lpd = scrns.customScreenData[index].layoutPersistentData;
addGridBlankRow(grid, row);
col = 0;
addGridLabel(grid, tr("Widgets:"), row, col++);
int widgetdetailsrow = row;
int widgetdetailscol = col + 1;
int firstenabledbtn = -1;
QGridLayout * tbgrid = new QGridLayout();
for (int i = 0; i < MAX_LAYOUT_ZONES; i++) {
ZonePersistentData & zpd = lpd.zones[i];
QPushButton * btn = new QPushButton(zpd.widgetName, this);
btn->setProperty("index", i);
btn->setFixedSize(QSize(90, 30));
widgetbtns.append(btn);
if (zpd.widgetName[0] != '\0' && SHOW_RAW_INFO) {
if (firstenabledbtn < 0)
firstenabledbtn = i;
QFrame * wgt = new QFrame();
wgt->setFrameStyle(QFrame::StyledPanel);
wgt->setFrameShadow(QFrame::Plain);
wgt->setVisible(false);
optswidgets.append(wgt);
QGridLayout * layout = new QGridLayout(wgt); // must be owned by QWidget so isibility can be set
layout->addLayout(addOptionsLayout<WidgetPersistentData>(zpd.widgetData, MAX_WIDGET_OPTIONS, zpd.widgetName), 0, 0, Qt::AlignTop);
optsgrids.append(layout);
connect(btn, &QPushButton::clicked, this, [&]() {
bool ok;
int idx = sender()->property("index").toInt(&ok);
if (ok) {
if (currindex >= 0) {
optswidgets.at(currindex)->setVisible(false);
}
optswidgets.at(idx)->setVisible(true);
currindex = idx;
}
});
}
else if (SHOW_RAW_INFO) {
btn->setEnabled(false);
}
div_t posn = div(i, MAX_LAYOUT_ZONES / 2);
tbgrid->addWidget(btn, posn.quot, posn.rem);
}
grid->addLayout(tbgrid, row++, col++, Qt::AlignTop);
addGridBlankRow(grid, row);
// Note: options are defined by each layout but we do not have access to the definitions yet!
// We assumed the first five to be the common options and the description of any extras is unknown
int cnt = SHOW_RAW_INFO ? MAX_LAYOUT_OPTIONS : LAYOUT_OPTION_LAST_DEFAULT + 1;
for (int i = 0; i < cnt; i++) {
QString str;
switch(i) {
case LAYOUT_OPTION_TOPBAR:
str = tr("Top bar");
break;
case LAYOUT_OPTION_FM:
str = tr("Flight mode");
break;
case LAYOUT_OPTION_SLIDERS:
str = tr("Sliders");
break;
case LAYOUT_OPTION_TRIMS:
str = tr("Trims");
break;
case LAYOUT_OPTION_MIRRORED:
str = tr("Mirror");
break;
default:
str = tr("Option #%1").arg(i + 1);
}
col = 0;
addGridLabel(grid, str, row, col++);
QCheckBox *chk = nullptr;
QLabel *lbl = nullptr;
switch (lpd.options[i].type) {
case ZOV_Bool:
chk = new QCheckBox(this);
chk->setChecked(lpd.options[i].value.boolValue);
break;
case ZOV_Unsigned:
lbl = new QLabel(this);
lbl->setText(QString("%1").arg(lpd.options[i].value.unsignedValue));
break;
case ZOV_Signed:
lbl = new QLabel(this);
lbl->setText(QString("%1").arg(lpd.options[i].value.signedValue));
break;
case ZOV_String:
lbl = new QLabel(this);
lbl->setText(lpd.options[i].value.stringValue);
break;
default:
lbl = new QLabel(this);
lbl->setText(QString("%1").arg(lpd.options[i].value.unsignedValue));
}
if (chk) {
chk->setEnabled(false);
grid->addWidget(chk, row++, col++);
}
else {
grid->addWidget(lbl, row++, col++);
}
}
// the grid must be fully built so the rowspan works
foreach (QWidget * wgt, optswidgets) {
grid->addWidget(wgt, widgetdetailsrow, widgetdetailscol, MAX_WIDGET_OPTIONS + 2/*title and column headings*/, Qt::AlignTop);
}
addHSpring(grid, grid->columnCount(), 0);
addVSpring(grid, 0, grid->rowCount());
disableMouseScrolling();
if (firstenabledbtn >= 0)
widgetbtns.at(firstenabledbtn)->click();
}
CustomScreenPanel::~CustomScreenPanel()
{
foreach (QGridLayout * grid, optsgrids) {
delete grid;
}
foreach (QFrame * wgt, optswidgets) {
delete wgt;
}
foreach (QPushButton * btn, widgetbtns) {
delete btn;
}
}
/*
ColorCustomScreensPanel
*/
ColorCustomScreensPanel::ColorCustomScreensPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
CompoundItemModelFactory * sharedItemModels):
ModelPanel(parent, model, generalSettings, firmware)
{
grid = new QGridLayout(this);
tabWidget = new QTabWidget(this);
UserInterfacePanel * tab = new UserInterfacePanel(tabWidget, model, generalSettings, firmware);
tab->setProperty("index", 0);
tabWidget->addTab(tab, getTabName(0));
panels << tab;
for (int i = 0; i < MAX_CUSTOM_SCREENS; i++) {
CustomScreenPanel * tab = new CustomScreenPanel(tabWidget, model, i, generalSettings, firmware);
tab->setProperty("index", i + 1);
tabWidget->addTab(tab, getTabName(i + 1));
panels << tab;
}
grid->addWidget(tabWidget, 0, 0, 1, 1);
}
ColorCustomScreensPanel::~ColorCustomScreensPanel()
{
foreach (GenericPanel * pnl, panels) {
delete pnl;
}
}
QString ColorCustomScreensPanel::getTabName(int index)
{
if (index == 0)
return tr("User Interface");
else
return tr("Main View %1").arg(index);
}
void addGridLine(QGridLayout * grid, int row, int col, int colspan)
{
QFrame *line = new QFrame();
line->setFrameShape(QFrame::HLine);
line->setFrameShadow(QFrame::Sunken);
line->setLineWidth(1);
line->setMidLineWidth(0);
grid->addWidget(line, row, col, 1, colspan);
}
void addGridLabel(QGridLayout * grid, QString text, int row, int col, int colspan)
{
QLabel * label = new QLabel();
label->setText(text);
grid->addWidget(label, row, col, 1, colspan, Qt::AlignTop);
}
void addGridBlankRow(QGridLayout * grid, int & row)
{
addGridLabel(grid, QString(), row++, 0);
}
template <class T>
QGridLayout * addOptionsLayout(T & persistentData, int optioncnt, QString title)
{
QGridLayout * grid = new QGridLayout();
int row = 0;
int col = 0;
if (!title.isEmpty()) {
QLabel * label = new QLabel();
label->setFrameShape(QFrame::Panel);
label->setFrameShadow(QFrame::Plain);
label->setAlignment(Qt::AlignCenter);
label->setMinimumHeight(30);
label->setMaximumHeight(30);
label->setText(title);
grid->addWidget(label, row++, col, 1, 2, Qt::AlignTop);
}
addGridLabel(grid, "Option", row, col++);
//addGridLabel(grid, "Type", row, col++);
addGridLabel(grid, "Value", row++, col++);
for (int i = 0; i < optioncnt; i++) {
ZoneOptionValueTyped & zovt = persistentData.options[i];
int col = 0;
addGridLabel(grid, QString("#%1").arg(i + 1), row, col++);
//addGridLabel(grid, QString("%1:%2").arg(zovt.type).arg(zoneOptionValueEnumToString(zovt.type)), row, col++);
QString val;
switch (zovt.type) {
case ZOV_Bool:
val = QString("%1").arg(zovt.value.boolValue);
break;
case ZOV_Unsigned:
val = QString("%1").arg(zovt.value.unsignedValue);
break;
case ZOV_Signed:
val = QString("%1").arg(zovt.value.signedValue);
break;
case ZOV_String:
val = QString("%1").arg(zovt.value.stringValue);
break;
default:
val = QString("%1").arg(zovt.value.unsignedValue);
}
addGridLabel(grid, QString("%1").arg(val), row++, col++);
}
return grid;
}
@@ -0,0 +1,82 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "modeledit.h"
#include "eeprominterface.h"
#include <QtCore>
class UserInterfacePanel : public ModelPanel
{
Q_OBJECT
public:
UserInterfacePanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware);
virtual ~UserInterfacePanel();
private:
QGridLayout * grid;
int currindex = -1;
QVector<QPushButton *> widgetbtns;
QVector<QFrame *> optswidgets;
QVector<QGridLayout *> optsgrids;
};
class CustomScreenPanel : public ModelPanel
{
Q_OBJECT
public:
CustomScreenPanel(QWidget *parent, ModelData & model, int index, GeneralSettings & generalSettings, Firmware * firmware);
virtual ~CustomScreenPanel();
private:
QGridLayout * grid;
int currindex = -1;
QVector<QPushButton *> widgetbtns;
QVector<QFrame *> optswidgets;
QVector<QGridLayout *> optsgrids;
};
class ColorCustomScreensPanel : public ModelPanel
{
Q_OBJECT
public:
ColorCustomScreensPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
CompoundItemModelFactory * sharedItemModels);
virtual ~ColorCustomScreensPanel();
private:
QTabWidget *tabWidget;
QGridLayout * grid;
QVector<GenericPanel *> panels;
QString getTabName(int index);
};
void addGridLabel(QGridLayout * grid, QString text, int row, int col, int colspan = 1);
void addGridBlankRow(QGridLayout * grid, int & row);
void addGridLine(QGridLayout * grid, int row, int col, int colspan);
template <class T>
QGridLayout * addOptionsLayout(T & persistentData, int optioncnt, QString title = "");
+7 -2
View File
@@ -149,13 +149,18 @@ CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings &
QPushButton * edit = new QPushButton(this);
edit->setProperty("index", i);
QString style = QString(
"QPushButton {"
#ifdef __GNUC__
// creates more compact and likely consistent buttons across OS flavors
edit->setStyleSheet(QString("background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;").arg(colors[i].name()));
"background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;"
#else
edit->setStyleSheet(QString("background-color: %1; color: white;").arg(colors[i].name()));
"background-color: %1; color: white;"
#endif
"}"
"QToolTip { background: white; color: black; }");
edit->setStyleSheet(style.arg(colors[i].name()));
edit->setText(tr("Curve %1").arg(i + 1));
edit->setContextMenuPolicy(Qt::CustomContextMenu);
edit->setToolTip(tr("Popup menu available"));
+11
View File
@@ -32,6 +32,8 @@
#include "logicalswitches.h"
#include "customfunctions.h"
#include "telemetry.h"
#include "colorcustomscreens.h"
#include "telemetry_customscreens.h"
#include "appdata.h"
#include "compounditemmodels.h"
@@ -104,6 +106,15 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw
s1.report("Telemetry");
}
if (Boards::getCapability(firmware->getBoard(), Board::HasColorLcd)) {
addTab(new ColorCustomScreensPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Custom Screens"));
s1.report("ColorLcd Custom Screens");
}
else if (firmware->getCapability(TelemetryCustomScreens)) {
addTab(new TelemetryCustomScreensPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Custom Screens"));
s1.report("Telemetry Custom Screens");
}
connect(setupPanel, &SetupPanel::extendedLimitsToggled, channelsPanel, &ChannelsPanel::refreshExtendedLimits);
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &ModelEdit::onTabIndexChanged);
connect(ui->pushButton, &QPushButton::clicked, this, &ModelEdit::launchSimulation);
+19 -2
View File
@@ -57,8 +57,12 @@ TimerPanel::TimerPanel(QWidget * parent, ModelData & model, TimerData & timer, G
ui->value->setField(timer.val, this);
ui->value->setMaximumTime(firmware->getMaxTimerStart());
ui->mode->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH));
ui->mode->setModel(panelItemModels->getItemModel(AIM_TIMER_MODE));
ui->mode->setField(timer.mode, this);
connect(ui->mode, SIGNAL(currentDataChanged(int)), this, SLOT(onModeChanged(int)));
ui->swtch->setModel(panelFilteredModels->getItemModel(FIM_TIMERSWITCH));
ui->swtch->setField(timer.swtch, this);
ui->countdownBeep->setModel(panelItemModels->getItemModel(AIM_TIMER_COUNTDOWNBEEP));
ui->countdownBeep->setField(timer.countdownBeep, this);
@@ -102,11 +106,17 @@ void TimerPanel::update()
ui->name->updateValue();
ui->mode->updateValue();
ui->swtch->updateValue();
ui->value->updateValue();
ui->countdownBeep->updateValue();
ui->minuteBeep->updateValue();
ui->countdownStart->updateValue();
if (timer.mode == TimerData::TIMERMODE_ON || timer.mode == TimerData::TIMERMODE_START)
ui->swtch->setEnabled(true);
else
ui->swtch->setEnabled(false);
if (timer.countdownBeep == TimerData::COUNTDOWNBEEP_SILENT) {
ui->countdownStartLabel->setEnabled(false);
ui->countdownStart->setEnabled(false);
@@ -161,6 +171,12 @@ void TimerPanel::onCountdownBeepChanged(int index)
update();
}
void TimerPanel::onModeChanged(int index)
{
timer.modeChanged();
update();
}
/******************************************************************************/
#define FAILSAFE_CHANNEL_HOLD 2000
@@ -998,7 +1014,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
panelFilteredModels = new FilteredItemModelFactory();
panelFilteredModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSwitch),
RawSwitch::TimersContext),
RawSwitch::AllSwitchContexts),
FIM_TIMERSWITCH);
connectItemModelEvents(panelFilteredModels->getItemModel(FIM_TIMERSWITCH));
@@ -1010,6 +1026,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
panelItemModels->registerItemModel(TimerData::countdownBeepItemModel());
panelItemModels->registerItemModel(TimerData::countdownStartItemModel());
panelItemModels->registerItemModel(TimerData::persistentItemModel());
panelItemModels->registerItemModel(TimerData::modeItemModel());
Board::Type board = firmware->getBoard();
+1
View File
@@ -50,6 +50,7 @@ class TimerPanel : public ModelPanel
void onItemModelAboutToBeUpdated();
void onItemModelUpdateComplete();
void onCountdownBeepChanged(int index);
void onModeChanged(int index);
signals:
void nameChanged();
+4 -1
View File
@@ -9,7 +9,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>874</width>
<width>1047</width>
<height>33</height>
</rect>
</property>
@@ -58,6 +58,9 @@
<item>
<widget class="AutoComboBox" name="mode"/>
</item>
<item>
<widget class="AutoComboBox" name="swtch"/>
</item>
<item>
<widget class="QLabel" name="countdownBeepLabel">
<property name="text">
-305
View File
@@ -20,13 +20,10 @@
#include "telemetry.h"
#include "ui_telemetry.h"
#include "ui_telemetry_customscreen.h"
#include "ui_telemetry_sensor.h"
#include "helpers.h"
#include "appdata.h"
#include <TimerEdit>
constexpr char FIM_RAWSOURCE[] {"Raw Source"};
constexpr char FIM_TELEALLSRC[] {"Tele All Source"};
constexpr char FIM_TELEPOSSRC[] {"Tele Pos Source"};
@@ -37,294 +34,6 @@ constexpr char FIM_SENSORUNIT[] {"Sensor.Unit"};
constexpr char FIM_SENSORPRECISION[] {"Sensor.Precision"};
constexpr char FIM_RSSISOURCE[] {"Rssi Source"};
TelemetryCustomScreen::TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings,
Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels):
ModelPanel(parent, model, generalSettings, firmware),
ui(new Ui::TelemetryCustomScreen),
screen(screen),
modelsUpdateCnt(0),
parentLock(parentLock)
{
ui->setupUi(this);
FilteredItemModel * rawSourceFilteredModel = panelFilteredItemModels->getItemModel(FIM_RAWSOURCE);
connectItemModelEvents(rawSourceFilteredModel);
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
fieldsCB[l][c] = new QComboBox(this);
fieldsCB[l][c]->setProperty("index", c + (l << 8));
fieldsCB[l][c]->setModel(rawSourceFilteredModel);
ui->screenNumsLayout->addWidget(fieldsCB[l][c], l, c, 1, 1);
connect(fieldsCB[l][c], SIGNAL(activated(int)), this, SLOT(customFieldChanged(int)));
}
}
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
barsCB[l] = new QComboBox(this);
barsCB[l]->setProperty("index", l);
barsCB[l]->setModel(rawSourceFilteredModel);
connect(barsCB[l], SIGNAL(activated(int)), this, SLOT(barSourceChanged(int)));
ui->screenBarsLayout->addWidget(barsCB[l], l, 0, 1, 1);
minSB[l] = new QDoubleSpinBox(this);
minSB[l]->setProperty("index", l);
connect(minSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMinChanged(double)));
ui->screenBarsLayout->addWidget(minSB[l], l, 1, 1, 1);
minTime[l] = new TimerEdit(this);
minTime[l]->setProperty("index", l);
minTime[l]->setProperty("type", "min");
connect(minTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
ui->screenBarsLayout->addWidget(minTime[l], l, 1, 1, 1);
minTime[l]->hide();
QLabel * label = new QLabel(this);
label->setAutoFillBackground(false);
label->setStyleSheet(QString::fromUtf8("Background:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 128, 255), stop:0.339795 rgba(0, 0, 128, 255), stop:0.339799 rgba(255, 255, 255, 255), stop:0.662444 rgba(255, 255, 255, 255),)\n"""));
label->setFrameShape(QFrame::Panel);
label->setFrameShadow(QFrame::Raised);
label->setAlignment(Qt::AlignCenter);
ui->screenBarsLayout->addWidget(label, l, 2, 1, 1);
maxSB[l] = new QDoubleSpinBox(this);
maxSB[l]->setProperty("index", l);
connect(maxSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMaxChanged(double)));
ui->screenBarsLayout->addWidget(maxSB[l], l, 3, 1, 1);
maxTime[l] = new TimerEdit(this);
maxTime[l]->setProperty("index", l);
maxTime[l]->setProperty("type", "max");
connect(maxTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
ui->screenBarsLayout->addWidget(maxTime[l], l, 3, 1, 1);
maxTime[l]->hide();
}
disableMouseScrolling();
lock = true;
ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE);
ui->screenType->addItem(tr("Numbers"), TELEMETRY_SCREEN_NUMBERS);
ui->screenType->addItem(tr("Bars"), TELEMETRY_SCREEN_BARS);
if (IS_TARANIS(firmware->getBoard()))
ui->screenType->addItem(tr("Script"), TELEMETRY_SCREEN_SCRIPT);
ui->screenType->setField(screen.type, this);
lock = false;
if (IS_TARANIS(firmware->getBoard())) {
QSet<QString> scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/TELEMETRY", QStringList() << "*.lua", 6);
Helpers::populateFileComboBox(ui->scriptName, scriptsSet, screen.body.script.filename);
connect(ui->scriptName, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptNameEdited()));
connect(ui->scriptName, SIGNAL(editTextChanged ( const QString)), this, SLOT(scriptNameEdited()));
}
update();
}
TelemetryCustomScreen::~TelemetryCustomScreen()
{
delete ui;
}
void TelemetryCustomScreen::update()
{
lock = true;
ui->scriptName->setVisible(screen.type == TELEMETRY_SCREEN_SCRIPT);
ui->screenNums->setVisible(screen.type == TELEMETRY_SCREEN_NUMBERS);
ui->screenBars->setVisible(screen.type == TELEMETRY_SCREEN_BARS);
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
fieldsCB[l][c]->setCurrentIndex(fieldsCB[l][c]->findData(screen.body.lines[l].source[c].toValue()));
}
}
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
barsCB[l]->setCurrentIndex(barsCB[l]->findData(screen.body.bars[l].source.toValue()));
}
if (screen.type == TELEMETRY_SCREEN_BARS) {
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreensBars); i++) {
updateBar(i);
}
}
lock = false;
}
void TelemetryCustomScreen::updateBar(int line)
{
lock = true;
RawSource source = screen.body.bars[line].source;
minTime[line]->setVisible(false);
maxTime[line]->setVisible(false);
if (source.type != SOURCE_TYPE_NONE) {
RawSourceRange range = source.getRange(model, generalSettings);
float minVal = range.getValue(screen.body.bars[line].barMin);
float maxVal = screen.body.bars[line].barMax;
maxVal = range.getValue(maxVal);
if (source.isTimeBased()) {
minTime[line]->setVisible(true);
minTime[line]->setTimeRange(range.min, range.max);
minTime[line]->setSingleStep(range.step);
minTime[line]->setPageStep(range.step * 60);
minTime[line]->setShowSeconds(range.step != 60);
minTime[line]->setTime((int)minVal);
maxTime[line]->setVisible(true);
maxTime[line]->setTimeRange(range.min, range.max);
maxTime[line]->setSingleStep(range.step);
maxTime[line]->setPageStep(range.step * 60);
maxTime[line]->setShowSeconds(range.step != 60);
maxTime[line]->setTime((int)maxVal);
minSB[line]->setVisible(false);
maxSB[line]->setVisible(false);
}
else {
minSB[line]->setVisible(true);
minSB[line]->setEnabled(true);
minSB[line]->setDecimals(range.decimals);
minSB[line]->setMinimum(range.min);
minSB[line]->setMaximum(range.max);
minSB[line]->setSingleStep(range.step);
minSB[line]->setSuffix(" " + range.unit);
minSB[line]->setValue(minVal);
maxSB[line]->setVisible(true);
maxSB[line]->setEnabled(true);
maxSB[line]->setDecimals(range.decimals);
maxSB[line]->setMinimum(range.min);
maxSB[line]->setMaximum(range.max);
maxSB[line]->setSingleStep(range.step);
maxSB[line]->setSuffix(" " + range.unit);
maxSB[line]->setValue(maxVal);
}
}
else {
minSB[line]->setDisabled(true);
maxSB[line]->setDisabled(true);
}
lock = false;
}
void TelemetryCustomScreen::on_screenType_currentIndexChanged(int index)
{
if (!isLocked()) {
memset(reinterpret_cast<void *>(&screen.body), 0, sizeof(screen.body));
update();
emit modified();
}
}
void TelemetryCustomScreen::scriptNameEdited()
{
if (!isLocked()) {
lock = true;
Helpers::getFileComboBoxValue(ui->scriptName, screen.body.script.filename, 8);
emit modified();
lock = false;
}
}
void TelemetryCustomScreen::customFieldChanged(int value)
{
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
return;
bool ok;
const int index = sender()->property("index").toInt(&ok),
i = index / 256,
m = index % 256;
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
if (ok && screen.body.lines[i].source[m].toValue() != src.toValue()) {
screen.body.lines[i].source[m] = src;
emit modified();
}
}
void TelemetryCustomScreen::barSourceChanged(int value)
{
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
return;
bool ok;
const int index = sender()->property("index").toInt(&ok);
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
if (!ok || screen.body.bars[index].source.toValue() == src.toValue())
return;
screen.body.bars[index].source = src;
screen.body.bars[index].barMin = 0;
screen.body.bars[index].barMax = 0;
updateBar(index);
emit modified();
}
void TelemetryCustomScreen::barMinChanged(double value)
{
if (!isLocked()) {
int line = sender()->property("index").toInt();
screen.body.bars[line].barMin = round(value / minSB[line]->singleStep());
// TODO set min (maxSB)
emit modified();
}
}
void TelemetryCustomScreen::barMaxChanged(double value)
{
if (!isLocked()) {
int line = sender()->property("index").toInt();
screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep());
// TODO set max (minSB)
emit modified();
}
}
void TelemetryCustomScreen::barTimeChanged()
{
if (!isLocked()) {
int line = sender()->property("index").toInt();
int & valRef = (sender()->property("type").toString() == "min" ? screen.body.bars[line].barMin : screen.body.bars[line].barMax);
TimerEdit * te = qobject_cast<TimerEdit *>(sender());
if (!te)
return;
valRef = round(te->timeInSeconds() / te->singleStep());
emit modified();
}
}
void TelemetryCustomScreen::connectItemModelEvents(const FilteredItemModel * itemModel)
{
connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryCustomScreen::onItemModelAboutToBeUpdated);
connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryCustomScreen::onItemModelUpdateComplete);
}
void TelemetryCustomScreen::onItemModelAboutToBeUpdated()
{
lock = true;
modelsUpdateCnt++;
}
void TelemetryCustomScreen::onItemModelUpdateComplete()
{
modelsUpdateCnt--;
if (modelsUpdateCnt < 1) {
// leave updating to parent
lock = false;
}
}
/******************************************************/
TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, int sensorCapability, ModelData & model,
GeneralSettings & generalSettings, Firmware * firmware, const bool & parentLock,
FilteredItemModelFactory * panelFilteredItemModels) :
@@ -755,14 +464,6 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin
ui->topbarGB->hide();
}
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock,
panelFilteredItemModels);
ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i + 1));
telemetryCustomScreens[i] = tab;
connect(tab, &TelemetryCustomScreen::modified, this, &TelemetryPanel::onModified);
}
disableMouseScrolling();
setup();
@@ -798,10 +499,6 @@ void TelemetryPanel::update()
sensorPanels[i]->update();
}
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
telemetryCustomScreens[i]->update();
}
lock = false;
}
@@ -901,8 +598,6 @@ void TelemetryPanel::on_frskyProtoCB_currentIndexChanged(int index)
{
if (!isLocked()) {
model->frsky.usrProto = index;
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++)
telemetryCustomScreens[i]->update();
emit modified();
}
}
-40
View File
@@ -30,49 +30,10 @@ class AutoComboBox;
class TimerEdit;
namespace Ui {
class TelemetryCustomScreen;
class TelemetrySensor;
class Telemetry;
}
class TelemetryCustomScreen: public ModelPanel
{
Q_OBJECT
public:
TelemetryCustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware,
const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels);
~TelemetryCustomScreen();
void update();
private slots:
void on_screenType_currentIndexChanged(int index);
void scriptNameEdited();
void customFieldChanged(int index);
void barSourceChanged(int index);
void barMinChanged(double value);
void barMaxChanged(double value);
void barTimeChanged();
void onItemModelAboutToBeUpdated();
void onItemModelUpdateComplete();
private:
void updateBar(int line);
Ui::TelemetryCustomScreen * ui;
FrSkyScreenData & screen;
QComboBox * fieldsCB[4][3];
QComboBox * barsCB[4];
QDoubleSpinBox * minSB[4];
QDoubleSpinBox * maxSB[4];
TimerEdit * minTime[4];
TimerEdit * maxTime[4];
int modelsUpdateCnt;
const bool &parentLock;
inline bool isLocked() { return lock | parentLock; }
void connectItemModelEvents(const FilteredItemModel * itemModel);
};
class TelemetrySensorPanel: public ModelPanel
{
Q_OBJECT
@@ -165,7 +126,6 @@ class TelemetryPanel : public ModelPanel
private:
Ui::Telemetry *ui;
TelemetryCustomScreen *telemetryCustomScreens[4];
TelemetrySensorPanel *sensorPanels[CPN_MAX_SENSORS];
int sensorCapability;
CompoundItemModelFactory *sharedItemModels;
+2 -16
View File
@@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>861</width>
<height>737</height>
<height>742</height>
</rect>
</property>
<property name="windowTitle">
<string/>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0,0">
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0">
<property name="spacing">
<number>4</number>
</property>
@@ -999,19 +999,6 @@
</layout>
</widget>
</item>
<item>
<widget class="QTabWidget" name="customScreens">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
@@ -1059,7 +1046,6 @@
<tabstop>frskyVoltCB</tabstop>
<tabstop>frskyCurrentCB</tabstop>
<tabstop>bladesCount</tabstop>
<tabstop>customScreens</tabstop>
</tabstops>
<resources/>
<connections/>
@@ -31,7 +31,7 @@
</size>
</property>
<property name="text">
<string>Custom Screen Type</string>
<string>Type</string>
</property>
</widget>
</item>
@@ -124,6 +124,19 @@
<layout class="QGridLayout" name="screenNumsLayout"/>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
@@ -0,0 +1,405 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "telemetry_customscreens.h"
#include "ui_telemetry_customscreen.h"
#include "helpers.h"
#include "appdata.h"
#include <TimerEdit>
constexpr char FIM_RAWSOURCE[] {"Raw Source"};
CustomScreen::CustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings,
Firmware * firmware, const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels):
ModelPanel(parent, model, generalSettings, firmware),
ui(new Ui::TelemetryCustomScreen),
screen(screen),
modelsUpdateCnt(0),
parentLock(parentLock)
{
ui->setupUi(this);
FilteredItemModel * rawSourceFilteredModel = panelFilteredItemModels->getItemModel(FIM_RAWSOURCE);
connectItemModelEvents(rawSourceFilteredModel);
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
fieldsCB[l][c] = new QComboBox(this);
fieldsCB[l][c]->setProperty("index", c + (l << 8));
fieldsCB[l][c]->setModel(rawSourceFilteredModel);
ui->screenNumsLayout->addWidget(fieldsCB[l][c], l, c, 1, 1);
connect(fieldsCB[l][c], SIGNAL(activated(int)), this, SLOT(customFieldChanged(int)));
}
}
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
barsCB[l] = new QComboBox(this);
barsCB[l]->setProperty("index", l);
barsCB[l]->setModel(rawSourceFilteredModel);
connect(barsCB[l], SIGNAL(activated(int)), this, SLOT(barSourceChanged(int)));
ui->screenBarsLayout->addWidget(barsCB[l], l+1, 0, 1, 1);
minSB[l] = new QDoubleSpinBox(this);
minSB[l]->setProperty("index", l);
connect(minSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMinChanged(double)));
ui->screenBarsLayout->addWidget(minSB[l], l+1, 1, 1, 1);
minTime[l] = new TimerEdit(this);
minTime[l]->setProperty("index", l);
minTime[l]->setProperty("type", "min");
connect(minTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
ui->screenBarsLayout->addWidget(minTime[l], l+1, 1, 1, 1);
minTime[l]->hide();
QLabel * label = new QLabel(this);
label->setAutoFillBackground(false);
label->setStyleSheet(QString::fromUtf8("Background:qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(0, 0, 128, 255), stop:0.339795 rgba(0, 0, 128, 255), stop:0.339799 rgba(255, 255, 255, 255), stop:0.662444 rgba(255, 255, 255, 255),)\n"""));
label->setFrameShape(QFrame::Panel);
label->setFrameShadow(QFrame::Raised);
label->setAlignment(Qt::AlignCenter);
ui->screenBarsLayout->addWidget(label, l+1, 2, 1, 1);
maxSB[l] = new QDoubleSpinBox(this);
maxSB[l]->setProperty("index", l);
connect(maxSB[l], SIGNAL(valueChanged(double)), this, SLOT(barMaxChanged(double)));
ui->screenBarsLayout->addWidget(maxSB[l], l+1, 3, 1, 1);
maxTime[l] = new TimerEdit(this);
maxTime[l]->setProperty("index", l);
maxTime[l]->setProperty("type", "max");
connect(maxTime[l], SIGNAL(editingFinished()), this, SLOT(barTimeChanged()));
ui->screenBarsLayout->addWidget(maxTime[l], l+1, 3, 1, 1);
maxTime[l]->hide();
}
disableMouseScrolling();
lock = true;
ui->screenType->addItem(tr("None"), TELEMETRY_SCREEN_NONE);
ui->screenType->addItem(tr("Numbers"), TELEMETRY_SCREEN_NUMBERS);
ui->screenType->addItem(tr("Bars"), TELEMETRY_SCREEN_BARS);
if (IS_TARANIS(firmware->getBoard()))
ui->screenType->addItem(tr("Script"), TELEMETRY_SCREEN_SCRIPT);
ui->screenType->setField(screen.type, this);
lock = false;
if (IS_TARANIS(firmware->getBoard())) {
QSet<QString> scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/TELEMETRY", QStringList() << "*.lua", 6);
Helpers::populateFileComboBox(ui->scriptName, scriptsSet, screen.body.script.filename);
connect(ui->scriptName, SIGNAL(currentIndexChanged(int)), this, SLOT(scriptNameEdited()));
connect(ui->scriptName, SIGNAL(editTextChanged ( const QString)), this, SLOT(scriptNameEdited()));
}
update();
}
CustomScreen::~CustomScreen()
{
delete ui;
}
void CustomScreen::update()
{
lock = true;
ui->scriptName->setVisible(screen.type == TELEMETRY_SCREEN_SCRIPT);
ui->screenNums->setVisible(screen.type == TELEMETRY_SCREEN_NUMBERS);
ui->screenBars->setVisible(screen.type == TELEMETRY_SCREEN_BARS);
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensLines); l++) {
for (int c = 0; c < firmware->getCapability(TelemetryCustomScreensFieldsPerLine); c++) {
fieldsCB[l][c]->setCurrentIndex(fieldsCB[l][c]->findData(screen.body.lines[l].source[c].toValue()));
}
}
for (int l = 0; l < firmware->getCapability(TelemetryCustomScreensBars); l++) {
barsCB[l]->setCurrentIndex(barsCB[l]->findData(screen.body.bars[l].source.toValue()));
}
if (screen.type == TELEMETRY_SCREEN_BARS) {
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreensBars); i++) {
updateBar(i);
}
}
lock = false;
}
void CustomScreen::updateBar(int line)
{
lock = true;
RawSource source = screen.body.bars[line].source;
minTime[line]->setVisible(false);
maxTime[line]->setVisible(false);
if (source.type != SOURCE_TYPE_NONE) {
RawSourceRange range = source.getRange(model, generalSettings);
float minVal = range.getValue(screen.body.bars[line].barMin);
float maxVal = screen.body.bars[line].barMax;
maxVal = range.getValue(maxVal);
if (source.isTimeBased()) {
minTime[line]->setVisible(true);
minTime[line]->setTimeRange(range.min, range.max);
minTime[line]->setSingleStep(range.step);
minTime[line]->setPageStep(range.step * 60);
minTime[line]->setShowSeconds(range.step != 60);
minTime[line]->setTime((int)minVal);
maxTime[line]->setVisible(true);
maxTime[line]->setTimeRange(range.min, range.max);
maxTime[line]->setSingleStep(range.step);
maxTime[line]->setPageStep(range.step * 60);
maxTime[line]->setShowSeconds(range.step != 60);
maxTime[line]->setTime((int)maxVal);
minSB[line]->setVisible(false);
maxSB[line]->setVisible(false);
}
else {
minSB[line]->setVisible(true);
minSB[line]->setEnabled(true);
minSB[line]->setDecimals(range.decimals);
minSB[line]->setMinimum(range.min);
minSB[line]->setMaximum(range.max);
minSB[line]->setSingleStep(range.step);
minSB[line]->setSuffix(" " + range.unit);
minSB[line]->setValue(minVal);
maxSB[line]->setVisible(true);
maxSB[line]->setEnabled(true);
maxSB[line]->setDecimals(range.decimals);
maxSB[line]->setMinimum(range.min);
maxSB[line]->setMaximum(range.max);
maxSB[line]->setSingleStep(range.step);
maxSB[line]->setSuffix(" " + range.unit);
maxSB[line]->setValue(maxVal);
}
}
else {
minSB[line]->setDisabled(true);
maxSB[line]->setDisabled(true);
}
lock = false;
}
void CustomScreen::on_screenType_currentIndexChanged(int index)
{
if (!isLocked()) {
memset(reinterpret_cast<void *>(&screen.body), 0, sizeof(screen.body));
update();
emit modified();
}
}
void CustomScreen::scriptNameEdited()
{
if (!isLocked()) {
lock = true;
Helpers::getFileComboBoxValue(ui->scriptName, screen.body.script.filename, 8);
emit modified();
lock = false;
}
}
void CustomScreen::customFieldChanged(int value)
{
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
return;
bool ok;
const int index = sender()->property("index").toInt(&ok),
i = index / 256,
m = index % 256;
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
if (ok && screen.body.lines[i].source[m].toValue() != src.toValue()) {
screen.body.lines[i].source[m] = src;
emit modified();
}
}
void CustomScreen::barSourceChanged(int value)
{
if (isLocked() || !sender() || !qobject_cast<QComboBox *>(sender()))
return;
bool ok;
const int index = sender()->property("index").toInt(&ok);
const RawSource src(qobject_cast<QComboBox *>(sender())->itemData(value).toInt());
if (!ok || screen.body.bars[index].source.toValue() == src.toValue())
return;
screen.body.bars[index].source = src;
screen.body.bars[index].barMin = 0;
screen.body.bars[index].barMax = 0;
updateBar(index);
emit modified();
}
void CustomScreen::barMinChanged(double value)
{
if (!isLocked()) {
int line = sender()->property("index").toInt();
screen.body.bars[line].barMin = round(value / minSB[line]->singleStep());
// TODO set min (maxSB)
emit modified();
}
}
void CustomScreen::barMaxChanged(double value)
{
if (!isLocked()) {
int line = sender()->property("index").toInt();
screen.body.bars[line].barMax = round((value) / maxSB[line]->singleStep());
// TODO set max (minSB)
emit modified();
}
}
void CustomScreen::barTimeChanged()
{
if (!isLocked()) {
int line = sender()->property("index").toInt();
int & valRef = (sender()->property("type").toString() == "min" ? screen.body.bars[line].barMin : screen.body.bars[line].barMax);
TimerEdit * te = qobject_cast<TimerEdit *>(sender());
if (!te)
return;
valRef = round(te->timeInSeconds() / te->singleStep());
emit modified();
}
}
void CustomScreen::connectItemModelEvents(const FilteredItemModel * itemModel)
{
connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &CustomScreen::onItemModelAboutToBeUpdated);
connect(itemModel, &FilteredItemModel::updateComplete, this, &CustomScreen::onItemModelUpdateComplete);
}
void CustomScreen::onItemModelAboutToBeUpdated()
{
lock = true;
modelsUpdateCnt++;
}
void CustomScreen::onItemModelUpdateComplete()
{
modelsUpdateCnt--;
if (modelsUpdateCnt < 1) {
// leave updating to parent
lock = false;
}
}
/******************************************************/
TelemetryCustomScreensPanel::TelemetryCustomScreensPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
CompoundItemModelFactory * sharedItemModels):
ModelPanel(parent, model, generalSettings, firmware),
sharedItemModels(sharedItemModels),
modelsUpdateCnt(0)
{
panelFilteredItemModels = new FilteredItemModelFactory();
int id;
id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(sharedItemModels->getItemModel(AbstractItemModel::IMID_RawSource)),
FIM_RAWSOURCE);
connectItemModelEvents(id);
grid = new QGridLayout(this);
tabWidget = new QTabWidget(this);
for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
CustomScreen * tab = new CustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, lock,
panelFilteredItemModels);
tab->setProperty("index", i + 1);
tabWidget->addTab(tab, getTabName(i + 1));
panels << tab;
connect(tab, &CustomScreen::modified, this, &TelemetryCustomScreensPanel::onModified);
}
grid->addWidget(tabWidget, 0, 0, 1, 1);
addHSpring(grid, grid->columnCount(), 0);
addVSpring(grid, 0, grid->rowCount());
disableMouseScrolling();
}
TelemetryCustomScreensPanel::~TelemetryCustomScreensPanel()
{
delete panelFilteredItemModels;
foreach(GenericPanel * gp, panels) {
delete gp;
}
}
QString TelemetryCustomScreensPanel::getTabName(int index)
{
return tr("Telemetry screen %1").arg(index);
}
void TelemetryCustomScreensPanel::update()
{
lock = true;
foreach(GenericPanel * pnl, panels) {
pnl->update();
}
lock = false;
}
void TelemetryCustomScreensPanel::onModified()
{
emit modified();
}
void TelemetryCustomScreensPanel::on_dataModifiedSensor()
{
sharedItemModels->update(AbstractItemModel::IMUE_TeleSensors);
emit modified();
}
void TelemetryCustomScreensPanel::connectItemModelEvents(const int id)
{
FilteredItemModel * itemModel = panelFilteredItemModels->getItemModel(id);
connect(itemModel, &FilteredItemModel::aboutToBeUpdated, this, &TelemetryCustomScreensPanel::onItemModelAboutToBeUpdated);
connect(itemModel, &FilteredItemModel::updateComplete, this, &TelemetryCustomScreensPanel::onItemModelUpdateComplete);
}
void TelemetryCustomScreensPanel::onItemModelAboutToBeUpdated()
{
lock = true;
modelsUpdateCnt++;
}
void TelemetryCustomScreensPanel::onItemModelUpdateComplete()
{
modelsUpdateCnt--;
if (modelsUpdateCnt < 1) {
update();
lock = false;
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "modeledit.h"
#include "eeprominterface.h"
#include "filtereditemmodels.h"
class AutoComboBox;
class TimerEdit;
namespace Ui {
class TelemetryCustomScreen;
}
class CustomScreen: public ModelPanel
{
Q_OBJECT
public:
CustomScreen(QWidget *parent, ModelData & model, FrSkyScreenData & screen, GeneralSettings & generalSettings, Firmware * firmware,
const bool & parentLock, FilteredItemModelFactory * panelFilteredItemModels);
~CustomScreen();
void update();
private slots:
void on_screenType_currentIndexChanged(int index);
void scriptNameEdited();
void customFieldChanged(int index);
void barSourceChanged(int index);
void barMinChanged(double value);
void barMaxChanged(double value);
void barTimeChanged();
void onItemModelAboutToBeUpdated();
void onItemModelUpdateComplete();
private:
void updateBar(int line);
Ui::TelemetryCustomScreen * ui;
FrSkyScreenData & screen;
QComboBox * fieldsCB[4][3];
QComboBox * barsCB[4];
QDoubleSpinBox * minSB[4];
QDoubleSpinBox * maxSB[4];
TimerEdit * minTime[4];
TimerEdit * maxTime[4];
int modelsUpdateCnt;
const bool &parentLock;
inline bool isLocked() { return lock | parentLock; }
void connectItemModelEvents(const FilteredItemModel * itemModel);
};
class TelemetryCustomScreensPanel : public ModelPanel
{
Q_OBJECT
public:
TelemetryCustomScreensPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware,
CompoundItemModelFactory * sharedItemModels);
virtual ~TelemetryCustomScreensPanel();
virtual void update();
signals:
void updated();
private slots:
void onModified();
void on_dataModifiedSensor();
void onItemModelAboutToBeUpdated();
void onItemModelUpdateComplete();
private:
CompoundItemModelFactory *sharedItemModels;
FilteredItemModelFactory *panelFilteredItemModels;
int modelsUpdateCnt;
QTabWidget *tabWidget;
QGridLayout *grid;
QVector<GenericPanel *> panels;
QString getTabName(int index);
void setup();
void telBarUpdate();
void connectItemModelEvents(const int id);
inline bool isLocked() { return lock; }
};
+4 -3
View File
@@ -334,7 +334,7 @@ QString MultiModelPrinter::printTimers()
QString str;
MultiColumns columns(modelPrinterMap.size());
columns.appendSectionTableStart();
columns.appendRowHeader(QStringList() << tr("Timers") << tr("Time") << tr("Switch") << tr("Countdown") << tr("Start") << tr("Min.call") << tr("Persist"));
columns.appendRowHeader(QStringList() << tr("Timers") << tr("Time") << tr("Mode") << tr("Switch") << tr("Countdown") << tr("Start") << tr("Min.call") << tr("Persist"));
for (int i=0; i<firmware->getCapability(Timers); i++) {
columns.appendRowStart();
@@ -342,11 +342,12 @@ QString MultiModelPrinter::printTimers()
COMPARE(model->timers[i].nameToString(i));
columns.appendCellEnd(true);
COMPARECELLWIDTH(model->timers[i].valToString(), 10);
COMPARECELLWIDTH(model->timers[i].mode.toString(), 10);
COMPARECELLWIDTH(model->timers[i].modeToString(), 10);
COMPARECELLWIDTH(modelPrinter->printFlightModeSwitch(model->timers[i].swtch), 5);
COMPARECELLWIDTH(model->timers[i].countdownBeepToString(), 10);
COMPARECELLWIDTH(model->timers[i].countdownStartToString(), 10);
COMPARECELLWIDTH(DataHelpers::boolToString(model->timers[i].minuteBeep, DataHelpers::BOOL_FMT_YESNO), 10);
COMPARECELLWIDTH(model->timers[i].persistentToString(false), 15);
COMPARECELLWIDTH(model->timers[i].persistentToString(false), 10);
columns.appendRowEnd();
}
columns.appendTableEnd();
+2 -1
View File
@@ -112,10 +112,11 @@ void AutoBitsetCheckBox::setInverted(bool invert)
void AutoBitsetCheckBox::updateValue()
{
if (m_field)
if (m_field) {
setLock(true);
setChecked((m_invert && !m_toggleMask) != bool(*m_field & m_bitmask));
setLock(false);
}
}
void AutoBitsetCheckBox::onToggled(bool checked)
+1 -1
View File
@@ -29,7 +29,7 @@ AutoComboBox::AutoComboBox(QWidget * parent):
m_rawSource(nullptr),
m_rawSwitch(nullptr)
{
connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onCurrentIndexChanged(int)));
connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AutoComboBox::onCurrentIndexChanged);
}
AutoComboBox::~AutoComboBox()
+1 -1
View File
@@ -26,7 +26,7 @@ AutoDoubleSpinBox::AutoDoubleSpinBox(QWidget * parent):
m_field(nullptr),
m_offset(0)
{
connect(this, SIGNAL(valueChanged(double)), this, SLOT(onValueChanged(double)));
connect(this, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &AutoDoubleSpinBox::onValueChanged);
}
AutoDoubleSpinBox::~AutoDoubleSpinBox()
+2
View File
@@ -19,6 +19,7 @@ set(simulation_SRCS
simulateduiwidgetTX12.cpp
simulateduiwidgetT8.cpp
simulateduiwidgetTX16S.cpp
simulateduiwidgetNV14.cpp
simulatorinterface.cpp
simulatormainwindow.cpp
simulatorstartupdialog.cpp
@@ -48,6 +49,7 @@ set(simulation_UIS
simulateduiwidgetTX12.ui
simulateduiwidgetT8.ui
simulateduiwidgetTX16S.ui
simulateduiwidgetNV14.ui
simulatormainwindow.ui
simulatorstartupdialog.ui
simulatorwidget.ui
@@ -118,6 +118,7 @@ namespace Ui {
class SimulatedUIWidgetTX16S;
class SimulatedUIWidgetTX12;
class SimulatedUIWidgetT8;
class SimulatedUIWidgetNV14;
}
class SimulatedUIWidget9X: public SimulatedUIWidget
@@ -316,5 +317,16 @@ class SimulatedUIWidgetTX16S: public SimulatedUIWidget
Ui::SimulatedUIWidgetTX16S * ui;
};
class SimulatedUIWidgetNV14: public SimulatedUIWidget
{
Q_OBJECT
public:
explicit SimulatedUIWidgetNV14(SimulatorInterface * simulator, QWidget * parent = nullptr);
virtual ~SimulatedUIWidgetNV14();
private:
Ui::SimulatedUIWidgetNV14 * ui;
};
#endif // SIMULATEDUIWIDGET_H
@@ -0,0 +1,76 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
// NOTE: RadioUiAction(NUMBER,...): NUMBER relates to enum EnumKeys in the specific board.h
#include "simulateduiwidget.h"
#include "ui_simulateduiwidgetNV14.h"
SimulatedUIWidgetNV14::SimulatedUIWidgetNV14(SimulatorInterface *simulator, QWidget * parent):
SimulatedUIWidget(simulator, parent),
ui(new Ui::SimulatedUIWidgetNV14)
{
RadioUiAction * act;
ui->setupUi(this);
// add actions in order of appearance on the help menu
// Note: the NV14 has no physical buttons though at some point the trim joystick is repurposed
// allow for colorlcd key events and see what works
// the mouse click areas do not map to visual buttons on the background images
act = new RadioUiAction(3, QList<int>() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL);
addRadioWidget(ui->rightbuttons->addArea(QRect(10, 1, 80, 35), "NV14/left.png", act));
m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ROT_DN);
addRadioWidget(ui->rightbuttons->addArea(QRect(10, 40, 80, 35), "NV14/left.png", m_mouseMidClickAction));
act = new RadioUiAction(6, QList<int>() << Qt::Key_Left, SIMU_STR_HLP_KEY_LFT, SIMU_STR_HLP_ACT_SYS);
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 80, 80, 35), "NV14/left.png", act));
act = new RadioUiAction(5, QList<int>() << Qt::Key_Right, SIMU_STR_HLP_KEY_RGT, SIMU_STR_HLP_ACT_TELE);
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 120, 80, 35), "NV14/left.png", act));
act = new RadioUiAction(1, QList<int>() << Qt::Key_PageDown, SIMU_STR_HLP_KEY_PGDN, SIMU_STR_HLP_ACT_PGDN);
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 160, 80, 35), "NV14/left.png", act));
act = new RadioUiAction(0, QList<int>() << Qt::Key_PageUp, SIMU_STR_HLP_KEY_PGUP, SIMU_STR_HLP_ACT_PGUP);
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 200, 80, 35), "NV14/left.png", act));
act = new RadioUiAction(4, QList<int>() << Qt::Key_Down << Qt::Key_Delete << Qt::Key_Escape << Qt::Key_Backspace,
SIMU_STR_HLP_KEY_DN % "<br>" % SIMU_STR_HLP_KEYS_EXIT, SIMU_STR_HLP_ACT_RTN);
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 240, 80, 35), "NV14/left.png", act));
m_scrollUpAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Minus, SIMU_STR_HLP_KEY_MIN % "|" % SIMU_STR_HLP_MOUSE_UP, SIMU_STR_HLP_ACT_ROT_LFT);
m_scrollDnAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Equal, SIMU_STR_HLP_KEY_PLS % "|" % SIMU_STR_HLP_MOUSE_DN, SIMU_STR_HLP_ACT_ROT_RGT);
connectScrollActions();
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 280, 30, 30), "NV14/left.png", m_screenshotAction));
m_backlightColors << QColor(47, 123, 227);
setLcd(ui->lcd);
}
SimulatedUIWidgetNV14::~SimulatedUIWidgetNV14()
{
delete ui;
}
@@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SimulatedUIWidgetNV14</class>
<widget class="QWidget" name="SimulatedUIWidgetNV14">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>520</width>
<height>500</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>520</width>
<height>500</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>520</width>
<height>500</height>
</size>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="2" rowspan="3">
<widget class="ButtonsWidget" name="rightbuttons" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>500</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>500</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background:url(:/images/simulator/NV14/right.png)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="LcdWidget" name="lcd" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>480</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>320</width>
<height>480</height>
</size>
</property>
<property name="font">
<font>
<pointsize>5</pointsize>
</font>
</property>
</widget>
</item>
<item row="0" column="0" rowspan="3">
<widget class="ButtonsWidget" name="leftbuttons" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>100</width>
<height>500</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>100</width>
<height>500</height>
</size>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="styleSheet">
<string notr="true">background:url(:/images/simulator/NV14/left.png);</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QWidget" name="top" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>10</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>320</width>
<height>10</height>
</size>
</property>
<property name="font">
<font>
<pointsize>5</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">background:url(:/images/simulator/NV14/top.png)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QWidget" name="bottom" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>10</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>320</width>
<height>10</height>
</size>
</property>
<property name="font">
<font>
<pointsize>5</pointsize>
<kerning>false</kerning>
</font>
</property>
<property name="styleSheet">
<string notr="true">background:url(:/images/simulator/NV14/bottom.png)</string>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>LcdWidget</class>
<extends>QWidget</extends>
<header>lcdwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonsWidget</class>
<extends>QWidget</extends>
<header>buttonswidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
@@ -86,7 +86,7 @@ void SimulatorStartupDialog::changeEvent(QEvent *e)
// FIXME : need a better way to check for this
bool SimulatorStartupDialog::usesCategorizedStorage(const QString & name)
{
return name.contains(QRegExp("(x12|x10|horus|16|18)", Qt::CaseInsensitive));
return name.contains(QRegExp("(x12|x10|horus|16|18|nv14)", Qt::CaseInsensitive));
}
bool SimulatorStartupDialog::usesCategorizedStorage()
@@ -109,6 +109,9 @@ SimulatorWidget::SimulatorWidget(QWidget * parent, SimulatorInterface * simulato
case Board::BOARD_RADIOMASTER_TX16S:
radioUiWidget = new SimulatedUIWidgetTX16S(simulator, this);
break;
case Board::BOARD_FLYSKY_NV14:
radioUiWidget = new SimulatedUIWidgetNV14(simulator, this);
break;
default:
radioUiWidget = new SimulatedUIWidget9X(simulator, this);
break;
+1
View File
@@ -13,6 +13,7 @@ set(storage_NAMES
categorized
sdcard
otx
yaml
)
set(storage_SRCS
+10 -7
View File
@@ -45,11 +45,11 @@
//! CPN_SETTINGS_VERSION is used for settings data version tracking.
#define CPN_SETTINGS_VERSION ((VERSION_NUMBER << 8) | CPN_SETTINGS_REVISION)
#define COMPANY QStringLiteral("OpenTX")
#define COMPANY_DOMAIN QStringLiteral("open-tx.org")
#define COMPANY QStringLiteral("EdgeTX")
#define COMPANY_DOMAIN QStringLiteral("edge-tx.org")
#define PRODUCT QStringLiteral("Companion " QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR))
#define APP_COMPANION QStringLiteral("OpenTX Companion")
#define APP_SIMULATOR QStringLiteral("OpenTX Simulator")
#define APP_COMPANION QStringLiteral("EdgeTX Companion")
#define APP_SIMULATOR QStringLiteral("EdgeTX Simulator")
//! Default location for OpenTX-related user documents (settigns, logs, etc)
#define CPN_DOCUMENTS_LOCATION QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) % "/" % COMPANY
@@ -58,7 +58,7 @@
#define CPN_SETTINGS_INI_FILE QString(PRODUCT % " " % QCoreApplication::translate("Companion", "settings") % " %1.ini")
#define CPN_SETTINGS_INI_PATH QString(CPN_SETTINGS_BACKUP_DIR % "/" % CPN_SETTINGS_INI_FILE)
#define CPN_URL_DOWNLOAD "https://downloads.open-tx.org/"
#define CPN_URL_DOWNLOAD "https://downloads.edge-tx.org/"
#define CPN_URL_DOWNLOAD_CUR_VERS CPN_URL_DOWNLOAD QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR) "/"
#define CPN_URL_DOWNLOAD_CUR_REL CPN_URL_DOWNLOAD_CUR_VERS "release/"
#define CPN_URL_DOWNLOAD_CUR_RC CPN_URL_DOWNLOAD_CUR_VERS "rc/"
@@ -573,8 +573,8 @@ class AppData: public CompStoreObj
PROPERTY4(bool, jsSupport, "js_support", false)
PROPERTY4(bool, showSplash, "show_splash", true)
PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false)
PROPERTY4(bool, autoCheckApp, "startup_check_companion", true)
PROPERTY4(bool, autoCheckFw, "startup_check_fw", true)
PROPERTY4(bool, autoCheckApp, "startup_check_companion", false)
PROPERTY4(bool, autoCheckFw, "startup_check_fw", false)
PROPERTY4(bool, promptProfile, "startup_prompt_profile", false)
PROPERTY(bool, enableBackup, false)
@@ -593,6 +593,9 @@ class AppData: public CompStoreObj
PROPERTY(int, simuLastProfId, -1)
PROPERTY(bool, simuSW, true)
// Message box confirmations
PROPERTY(bool, confirmWriteModelsAndSettings, true)
bool firstUse;
QString upgradeFromVersion;
+4
View File
@@ -22,6 +22,7 @@
#include "eepe.h"
#include "otx.h"
#include "sdcard.h"
#include "yaml.h"
#include "firmwareinterface.h"
#include "eeprominterface.h"
#include <QFileInfo>
@@ -41,6 +42,8 @@ StorageType getStorageType(const QString & filename)
return STORAGE_TYPE_XML;
else if (suffix == "OTX")
return STORAGE_TYPE_OTX;
else if (suffix == "YML")
return STORAGE_TYPE_YML;
else
return STORAGE_TYPE_UNKNOWN;
}
@@ -61,6 +64,7 @@ void registerStorageFactories()
registerStorageFactory(new DefaultStorageFactory<EepeFormat>("eepe"));
registerStorageFactory(new DefaultStorageFactory<HexEepromFormat>("hex"));
registerStorageFactory(new DefaultStorageFactory<OtxFormat>("otx"));
registerStorageFactory(new DefaultStorageFactory<YamlFormat>("yml"));
registerStorageFactory(new SdcardStorageFactory());
}
+2 -1
View File
@@ -36,7 +36,8 @@ enum StorageType
STORAGE_TYPE_EEPM,
STORAGE_TYPE_XML,
STORAGE_TYPE_SDCARD,
STORAGE_TYPE_OTX
STORAGE_TYPE_OTX,
STORAGE_TYPE_YML
};
StorageType getStorageType(const QString & filename);
+84
View File
@@ -0,0 +1,84 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "yaml.h"
#include "../../../radio/src/storage/yaml/yaml_tree_walker.h" // just to confirm valid compiler path
#include <QFile>
#include <QDir>
bool YamlFormat::loadFile(QByteArray & filedata, const QString & filename)
{
QString path = this->filename + "/" + filename;
QFile file(path);
if (!file.open(QFile::ReadOnly)) {
setError(tr("Error opening file %1:\n%2.").arg(path).arg(file.errorString()));
return false;
}
filedata = file.readAll();
qDebug() << "File" << path << "read, size:" << filedata.size();
file.close();
return true;
}
bool YamlFormat::writeFile(const QByteArray & filedata, const QString & filename)
{
QString path = this->filename + "/" + filename;
QFile file(path);
if (!file.open(QFile::WriteOnly)) {
setError(tr("Error opening file %1 in write mode:\n%2.").arg(path).arg(file.errorString()));
return false;
}
file.write(filedata.data(), filedata.size());
file.close();
qDebug() << "File" << path << "written, size:" << filedata.size();
return true;
}
bool YamlFormat::load(RadioData & radioData)
{
QByteArray radioSettingsBuffer;
if (!loadFile(radioSettingsBuffer, "RADIO/radio.yml")) {
setError(tr("Can't extract RADIO/radio.yml"));
return false;
}
qDebug() << "Warning: format ignored - under development";
return false; // force failure until fully developed
}
bool YamlFormat::write(const RadioData & radioData)
{
/* Need this test in case of a new sdcard
Board::Type board = getCurrentBoard();
if (!HAS_EEPROM_YAML(board)) {
qDebug() << "Board does not support YAML format";
return false;
}
// ensure directories exist on sd card
QDir dir(filename);
dir.mkdir("RADIO");
dir.mkdir("MODELS");
*/
qDebug() << "Warning: format ignored - under development";
return false; // force failure until fully developed
}
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#pragma once
#include "storage.h"
#include <QtCore>
class YamlFormat : public StorageFormat
{
Q_DECLARE_TR_FUNCTIONS(YamlFormat)
public:
YamlFormat(const QString & filename):
StorageFormat(filename)
{
}
virtual QString name() { return "yml"; }
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
protected:
bool loadFile(QByteArray & fileData, const QString & fileName);
bool writeFile(const QByteArray & fileData, const QString & fileName);
};
+1 -1
View File
@@ -22,7 +22,7 @@ if(GTEST_INCDIR AND GTEST_SRCDIR AND Qt5Widgets_FOUND)
use_cxx11() # ensure gnu++11 in CXX_FLAGS with CMake < 3.1
add_executable(gtests-companion EXCLUDE_FROM_ALL ${TEST_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/location.h)
add_executable(gtests-companion EXCLUDE_FROM_ALL ${TEST_SRC_FILES} ${CMAKE_CURRENT_SOURCE_DIR}/location.h.in)
add_dependencies(gtests-companion gtests-companion-lib)
target_link_libraries(gtests-companion gtests-companion-lib simulation firmwares storage common)
message(STATUS "Added optional gtests-companion target")
+12 -9
View File
@@ -19,10 +19,11 @@ TEST(Conversions, ConversionX9DPFrom22)
EXPECT_STREQ("Tes", settings.switchName[0]);
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
const ModelData& model = radioData.models[0];
EXPECT_STREQ("Test", model.name);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model.timers[0].mode);
EXPECT_EQ(TimerData::TIMERMODE_ON, model.timers[0].mode);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model.timers[0].swtch);
EXPECT_EQ(80, model.mixData[0].weight);
EXPECT_EQ(900, model.limitData[0].max); // -100
EXPECT_EQ(80, model.expoData[0].weight);
@@ -64,7 +65,8 @@ TEST(Conversions, ConversionX9DPFrom23)
const ModelData & model1 = radioData.models[0];
EXPECT_STREQ("Test", model1.name);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model1.timers[0].mode);
EXPECT_EQ(TimerData::TIMERMODE_ON, model1.timers[0].mode);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model1.timers[0].swtch);
EXPECT_EQ(80, model1.mixData[0].weight);
EXPECT_EQ(900, model1.limitData[0].max); // -100
EXPECT_EQ(80, model1.expoData[0].weight);
@@ -82,7 +84,8 @@ TEST(Conversions, ConversionX9DPFrom23)
const ModelData & model2 = radioData.models[1];
EXPECT_STREQ("Test", model2.name);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model2.timers[0].mode);
EXPECT_EQ(TimerData::TIMERMODE_ON, model2.timers[0].mode);
EXPECT_EQ(RawSwitch(SWITCH_TYPE_SWITCH, SWITCH_SA0), model2.timers[0].swtch);
EXPECT_EQ(80, model2.mixData[0].weight);
EXPECT_EQ(900, model2.limitData[0].max); // -100
EXPECT_EQ(80, model2.expoData[0].weight);
@@ -114,7 +117,7 @@ TEST(Conversions, ConversionX7From22)
EXPECT_STREQ("Tes", settings.switchName[0]);
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
const ModelData& model = radioData.models[0];
EXPECT_STREQ("Test", model.name);
EXPECT_EQ(PULSES_PXX_R9M, model.moduleData[1].protocol);
@@ -216,7 +219,7 @@ TEST(Conversions, ConversionX10From22)
const GeneralSettings& settings = radio.generalSettings;
const ModelData& model = radio.models[0];
#else
#else
ASSERT_EQ(true, loadFile(byteBuffer, RADIO_TESTS_PATH "/model_22_x10/RADIO/radio.bin"));
GeneralSettings settings;
@@ -248,7 +251,7 @@ TEST(Conversions, ConversionX10From22)
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
EXPECT_STREQ("BT_X10", settings.bluetoothName);
EXPECT_STREQ("Default", settings.themeName);
EXPECT_STREQ("EdgeTX", settings.themeData.themeName);
#if !defined(USE_OTX)
byteBuffer.clear();
@@ -257,7 +260,7 @@ TEST(Conversions, ConversionX10From22)
ModelData model;
ASSERT_NE(nullptr, loadModelFromByteArray(model, byteBuffer));
#endif
EXPECT_STREQ("Test", model.name);
EXPECT_EQ(0, model.noGlobalFunctions);
EXPECT_EQ(0, model.beepANACenter);
@@ -325,7 +328,7 @@ TEST(Conversions, ConversionX12SFrom22)
EXPECT_EQ(Board::SWITCH_3POS, settings.switchConfig[0]);
EXPECT_STREQ("BT", settings.bluetoothName);
EXPECT_STREQ("Default", settings.themeName);
EXPECT_STREQ("EdgeTX", settings.themeData.themeName);
#if !defined(USE_OTX)
byteBuffer.clear();
+6 -6
View File
@@ -28,19 +28,19 @@
#include <QMessageBox>
#define CPN_STR_MSG_WELCOME QCoreApplication::translate("Companion", \
"<p><b>Welcome to OpenTX %1.</b></p>" \
"<p><b>Welcome to EdgeTX %1.</b></p>" \
"<p>As the first step, please configure the initial Radio Profile by selecting your Radio Type, Menu Language, and Build Options.</p>" \
"<p>You may also want to take this time to review the other available options in the displayed Settings dialog.</p>" \
"<p>After saving your settings, we recommend you download the latest firmware for your radio by using the <i>File -&gt; Download</i> menu option.</p>" \
"<p>Please visit <a href='http://www.open-tx.org'>open-tx.org</a> for latest news, updates and documentation. Thank you for choosing OpenTX!</p>" \
"- The OpenTX Team.")
"<p>Please visit <a href='http://www.edge-tx.org'>edge-tx.org</a> for latest news, updates and documentation. Thank you for choosing EdgeTX!</p>" \
"- The EdgeTX Team.")
#define CPN_STR_MSG_UPGRADED QCoreApplication::translate("Companion", \
"<p><b>Thank you for upgrading to OpenTX %1.</b></p>" \
"<p><b>Thank you for upgrading to EdgeTX %1.</b></p>" \
"<p>This is a major upgrade that adds and modifies a lot of things, so please make sure that you read release notes carefully" \
" to learn about the changes, and thoroughly check each of your models for proper function.</p>" \
"<p>Please visit <a href='http://www.open-tx.org'>open-tx.org</a> for release notes and other documentation.</p>" \
"- The OpenTX Team.")
"<p>Please visit <a href='http://www.edge-tx.org'>edge-tx.org</a> for release notes and other documentation.</p>" \
"- The EdgeTX Team.")
#define CPN_STR_MSG_NO_RADIO_TYPE QCoreApplication::translate("Companion", \
"<p>The radio type in the selected profile does not exist. Using the default type instead.</p> <p><b>Please update your profile settings!</b></p>")
+2 -4
View File
@@ -133,8 +133,7 @@ WizMix::operator ModelData()
if (options[FLIGHT_TIMER_OPTION] && throttleChannel >= 0){
memset(model.timers[timerIndex].name, 0, sizeof(model.timers[timerIndex].name));
strncpy(model.timers[timerIndex].name, "Flt", sizeof(model.timers[timerIndex].name)-1);
model.timers[timerIndex].mode.type = SWITCH_TYPE_TIMER_MODE;
model.timers[timerIndex].mode.index = TMRMODE_THR_TRG;
model.timers[timerIndex].mode = TimerData::TIMERMODE_START;
timerIndex++;
}
@@ -142,8 +141,7 @@ WizMix::operator ModelData()
if (options[THROTTLE_TIMER_OPTION] && throttleChannel >= 0){
memset(model.timers[timerIndex].name, 0, sizeof(model.timers[timerIndex].name));
strncpy(model.timers[timerIndex].name, "Thr", sizeof(model.timers[timerIndex].name)-1);
model.timers[timerIndex].mode.type = SWITCH_TYPE_TIMER_MODE;
model.timers[timerIndex].mode.index = TMRMODE_THR;
model.timers[timerIndex].mode = TimerData::TIMERMODE_THR;
timerIndex++;
}
+8 -1
View File
@@ -44,6 +44,13 @@ if(Qt5Widgets_FOUND)
add_dependencies(${SIMULATOR_TARGET} ${RADIO_DEPENDENCIES})
target_compile_definitions(${SIMULATOR_TARGET} PUBLIC ${APP_COMMON_DEFINES}) # set in top-level CMakeLists
target_link_libraries(${SIMULATOR_TARGET} ${SDL_LIBRARY} Qt5::Core)
# Introduced at 2.5 - TODO replace with a code change
# When compiled shared simulators using libopenui and Companion have classes named MainWindow
# When the simulator is launched from Companion the runtime dynamic linking can result in Companion overiding the simulator
# and crashing the simulator under some OSes
# The flag forces the runtime linker to always look for references in the simulator first
target_link_options(${SIMULATOR_TARGET} PRIVATE "LINKER:-Bsymbolic")
#
add_custom_target(libsimulator DEPENDS ${SIMULATOR_TARGET})
# Prepare the "all-simu-libs" target to build simulator libraries for *every* supported PCB type (PCB_TYPES list)
@@ -101,7 +108,7 @@ if(FOX_FOUND)
endif()
add_executable(simu WIN32 ${SIMU_SRC} ${RADIO_SRC_DIR}/simu.cpp)
add_dependencies(simu ${RADIO_DEPENDENCIES})
target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} )
target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL_LIBRARY})
target_compile_definitions(simu PUBLIC -DSIMU)
@@ -603,6 +603,8 @@ class OpenTxSimulatorFactory: public SimulatorFactory
return Board::BOARD_TARANIS_X9LITES;
#elif defined(PCBX9LITE)
return Board::BOARD_TARANIS_X9LITE;
#elif defined(PCBNV14)
return Board::BOARD_FLYSKY_NV14;
#else
return Board::BOARD_TARANIS_X9D;
#endif