Merge pull request #1186 from EdgeTX/cpn-storage-fmts

Companion YaML support
This commit is contained in:
Raphael Coeffic
2022-01-02 13:06:01 +01:00
committed by GitHub
157 changed files with 7365 additions and 1217 deletions
+3
View File
@@ -5,3 +5,6 @@
[submodule "radio/src/thirdparty/AccessDenied"]
path = radio/src/thirdparty/AccessDenied
url = https://github.com/raphaelcoeffic/AccessDenied.git
[submodule "companion/src/thirdparty/yaml-cpp"]
path = companion/src/thirdparty/yaml-cpp
url = https://github.com/jbeder/yaml-cpp.git
+11 -5
View File
@@ -198,6 +198,12 @@ add_subdirectory(storage)
add_subdirectory(thirdparty/qcustomplot)
add_subdirectory(thirdparty/maxlibqt/src/widgets)
set(YAML_CPP_BUILD_TESTS OFF CACHE BOOL "Enable testing" FORCE)
set(YAML_CPP_BUILD_TOOLS OFF CACHE BOOL "Enable parse tools" FORCE)
set(YAML_CPP_BUILD_CONTRIB OFF CACHE BOOL "Enable contrib stuff in library" FORCE)
set(YAML_CPP_INSTALL OFF CACHE BOOL "Enable generation of install target" FORCE)
add_subdirectory(thirdparty/yaml-cpp)
############# Companion ###############
set(companion_SRCS
@@ -313,7 +319,7 @@ add_subdirectory(tests)
############# Install ####################
# Generate list of simulator plugins, used by all platforms
file(GLOB simulator_plugins "${CMAKE_BINARY_DIR}/*opentx-*simulator${CMAKE_SHARED_LIBRARY_SUFFIX}")
file(GLOB simulator_plugins "${CMAKE_BINARY_DIR}/*edgetx-*simulator${CMAKE_SHARED_LIBRARY_SUFFIX}")
# the current flavour is not automatically added if build in the current cmake iteration, so always
# add its library name to be sure
@@ -349,7 +355,7 @@ if(POLICY CMP0026)
endif()
if(SIMU_TARGET)
get_property(current_plugin TARGET opentx-${FLAVOUR}-simulator PROPERTY LOCATION)
get_property(current_plugin TARGET edgetx-${FLAVOUR}-simulator PROPERTY LOCATION)
list(APPEND simulator_plugins "${current_plugin}")
endif()
@@ -529,7 +535,7 @@ IF(APPLE)
# technically a hack (that is already is in the Bundle library ...)
install(CODE "
include(BundleUtilities)
file(GLOB bundle_simulator_libs \"\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/libopentx-*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
file(GLOB bundle_simulator_libs \"\${CMAKE_INSTALL_PREFIX}/${companion_res_dir}/libedgetx-*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
set(BU_CHMOD_BUNDLE_ITEMS on)
fixup_bundle(\"${APPS}\" \"\${bundle_simulator_libs};${bundle_qt_libs};${bundle_tools_path}\" \"${QT_LIB_DIR}\")
file(RENAME \"\${CMAKE_INSTALL_PREFIX}/${COMPANION_NAME}.app\" \"\${CMAKE_INSTALL_PREFIX}/${COMPANION_OSX_APP_BUNDLE_NAME}.app\")
@@ -543,9 +549,9 @@ if(APPLE)
set(CPACK_DMG_BACKGROUND_IMAGE ${COMPANION_SRC_DIRECTORY}/images/splash_dmg.png)
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}")
set(CPACK_PACKAGE_FILE_NAME "edgetx-${CPACK_PACKAGE_NAME_LOWERCASE}-${VERSION}")
endif(APPLE)
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
get_filename_component(_qt_bin_dir "${QT_QMAKE_EXECUTABLE}" DIRECTORY)
find_program(LINUXDEPLOY_EXECUTABLE linuxdeployqt HINTS "${_qt_bin_dir}")
+13
View File
@@ -28,6 +28,10 @@
#include "joystick.h"
#include "joystickdialog.h"
#endif
#include "moduledata.h"
#include "compounditemmodels.h"
#include <QAbstractItemModel>
AppPreferencesDialog::AppPreferencesDialog(QWidget * parent) :
QDialog(parent),
@@ -97,6 +101,7 @@ void AppPreferencesDialog::accept()
g.jsSupport(false);
g.jsCtrl(0);
}
profile.defaultInternalModule(ui->defaultInternalModuleCB->currentData().toInt());
profile.channelOrder(ui->channelorderCB->currentIndex());
profile.defaultMode(ui->stickmodeCB->currentIndex());
profile.renameFwFiles(ui->renameFirmware->isChecked());
@@ -256,6 +261,8 @@ void AppPreferencesDialog::initSettings()
}
#endif
// Profile Tab Inits
ui->defaultInternalModuleCB->setModel(ModuleData::internalModuleItemModel());
ui->defaultInternalModuleCB->setCurrentIndex(ui->defaultInternalModuleCB->findData(profile.defaultInternalModule()));
ui->channelorderCB->setCurrentIndex(profile.channelOrder());
ui->stickmodeCB->setCurrentIndex(profile.defaultMode());
ui->renameFirmware->setChecked(profile.renameFwFiles());
@@ -453,6 +460,12 @@ void AppPreferencesDialog::on_clearImageButton_clicked()
void AppPreferencesDialog::onBaseFirmwareChanged()
{
populateFirmwareOptions(getBaseFirmware());
Firmware *newfw = getFirmwareVariant();
Profile & profile = g.currentProfile();
profile.defaultInternalModule(Boards::getDefaultInternalModules(newfw->getBoard()));
ui->defaultInternalModuleCB->setModel(ModuleData::internalModuleItemModel(newfw->getBoard()));
ui->defaultInternalModuleCB->setCurrentIndex(ui->defaultInternalModuleCB->findData(profile.defaultInternalModule()));
}
Firmware *AppPreferencesDialog::getBaseFirmware() const
+373 -363
View File
@@ -51,178 +51,21 @@
<attribute name="title">
<string>Radio Profile</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0,0,0">
<item row="5" column="1">
<widget class="QWidget" name="optionsWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0,0,0,0">
<item row="14" column="0">
<widget class="QLabel" name="label_14">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="optionsLayout">
<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="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
</layout>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="QWidget" name="widget_splashImage" native="true">
<layout class="QGridLayout" name="gridLayout_5">
<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>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="splashLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Splash Screen</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="SplashFileName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="SplashSelect">
<property name="text">
<string>Select Image</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="imageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>212</width>
<height>64</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="clearImageButton">
<property name="text">
<string>Clear Image</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="Line" name="line_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="16" column="1">
<widget class="QCheckBox" name="burnFirmware">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Offer to write FW to Tx after download</string>
<string>Default Stick Mode</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="4">
<widget class="Line" name="line_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="14" column="1">
<item row="15" column="2">
<widget class="QComboBox" name="channelorderCB">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@@ -358,88 +201,27 @@
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="boardCB">
<property name="enabled">
<bool>true</bool>
<item row="10" column="2">
<widget class="QLineEdit" name="profilebackupPath">
<property name="toolTip">
<string>If set it will override the application general setting</string>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Build Options</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="15" column="1">
<widget class="QCheckBox" name="renameFirmware">
<item row="4" column="2">
<widget class="QComboBox" name="langCombo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Append version number to FW file name</string>
</property>
</widget>
</item>
<item row="9" column="2" colspan="2">
<widget class="QPushButton" name="sdPathButton">
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Profile Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="profileNameLE"/>
</item>
<item row="10" column="0">
<widget class="QLabel" name="profilebackupPathLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The profile specific folder, if set, will override general Backup folder</string>
</property>
<property name="text">
<string>Backup folder</string>
</property>
</widget>
</item>
<item row="12" column="1">
<item row="12" column="2">
<widget class="QLabel" name="lblGeneralSettings">
<property name="enabled">
<bool>true</bool>
@@ -455,39 +237,17 @@
</property>
</widget>
</item>
<item row="8" column="0" colspan="4">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
<item row="10" column="3" colspan="2">
<widget class="QPushButton" name="ProfilebackupPathButton">
<property name="toolTip">
<string>The profile specific folder, if set, will override general Backup folder</string>
</property>
<property name="text">
<string>Other Settings</string>
<string>Select Folder</string>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_14">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Default Stick Mode</string>
</property>
</widget>
</item>
<item row="13" column="1">
<item row="14" column="2">
<widget class="QComboBox" name="stickmodeCB">
<property name="maximumSize">
<size>
@@ -541,61 +301,61 @@ Mode 4:
</item>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_13">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<item row="9" column="2">
<widget class="QLineEdit" name="sdPath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Default Channel Order</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="pbackupEnable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>if set, will override general backup enable</string>
</property>
<property name="text">
<string>Enable automatic backup before writing firmware</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="4">
<widget class="Line" name="splashSeparator">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="10" column="1">
<widget class="QLineEdit" name="profilebackupPath">
<property name="toolTip">
<string>If set it will override the application general setting</string>
<string/>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="profileNameLE"/>
</item>
<item row="2" column="2">
<widget class="QComboBox" name="boardCB">
<property name="enabled">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Profile Name</string>
</property>
</widget>
</item>
<item row="16" column="2">
<widget class="QCheckBox" name="renameFirmware">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Append version number to FW file name</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="sdPathLabel">
<property name="sizePolicy">
@@ -612,16 +372,6 @@ Mode 4:
</property>
</widget>
</item>
<item row="10" column="2" colspan="2">
<widget class="QPushButton" name="ProfilebackupPathButton">
<property name="toolTip">
<string>The profile specific folder, if set, will override general Backup folder</string>
</property>
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
<item row="12" column="0">
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
@@ -635,6 +385,136 @@ Mode 4:
</property>
</widget>
</item>
<item row="10" column="0">
<widget class="QLabel" name="profilebackupPathLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The profile specific folder, if set, will override general Backup folder</string>
</property>
<property name="text">
<string>Backup folder</string>
</property>
</widget>
</item>
<item row="15" column="0">
<widget class="QLabel" name="label_13">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Default Channel Order</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Default Int. Module</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="5">
<widget class="Line" name="line_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="17" column="2">
<widget class="QCheckBox" name="burnFirmware">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Offer to write FW to Tx after download</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Build Options</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="langLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Menu Language</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="9" column="3" colspan="2">
<widget class="QPushButton" name="sdPathButton">
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
<item row="18" column="2">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item row="6" column="0" colspan="5">
<widget class="Line" name="splashSeparator">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="sizePolicy">
@@ -658,58 +538,188 @@ Mode 4:
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="langLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Menu Language</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLineEdit" name="sdPath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string/>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item row="17" column="1">
<spacer name="verticalSpacer_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QComboBox" name="langCombo">
<item row="11" column="2">
<widget class="QCheckBox" name="pbackupEnable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>if set, will override general backup enable</string>
</property>
<property name="text">
<string>Enable automatic backup before writing firmware</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="5">
<widget class="QWidget" name="widget_splashImage" native="true">
<layout class="QGridLayout" name="gridLayout_5">
<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>6</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="splashLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Splash Screen</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="SplashFileName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QPushButton" name="SplashSelect">
<property name="text">
<string>Select Image</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLabel" name="imageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>212</width>
<height>64</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>1</number>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="clearImageButton">
<property name="text">
<string>Clear Image</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="Line" name="line_6">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="2">
<widget class="QWidget" name="optionsWidget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QGridLayout" name="optionsLayout">
<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="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
</layout>
</widget>
</item>
<item row="8" column="0" colspan="5">
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>Other Settings</string>
</property>
</widget>
</item>
<item row="13" column="2">
<widget class="QComboBox" name="defaultInternalModuleCB"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="applicationTab">
+8 -14
View File
@@ -69,28 +69,22 @@ void checkSettingsImport(bool force = false)
if (!found && !force)
return;
const QString impFileBtn = QCoreApplication::translate("Companion", "Import from File");
const QString impPrevBtn = QCoreApplication::translate("Companion", "Import from v%1").arg(previousVersion);
const QString impNoneBtn = QCoreApplication::translate("Companion", "Do not import");
QString msg;
if (previousVersion.isEmpty()) {
const QString impFileBtn = QCoreApplication::translate("Companion", "Import from File");
const QString impNoneBtn = QCoreApplication::translate("Companion", "Do not import");
if (found)
msg = QCoreApplication::translate("Companion", "We have found possible Companion settings backup file(s).\nDo you want to import settings from a file?");
else
msg = QCoreApplication::translate("Companion", "Import settings from a file, or start with current values.");
const int ret = QMessageBox::question(nullptr, CPN_STR_APP_NAME, msg, impNoneBtn, impFileBtn, 0, 0);
if (!ret)
return;
}
else {
msg = QCoreApplication::translate("Companion", "We have found existing settings for Companion version: %1.\nDo you want to import them?\n\n" \
"If you have a settings backup file, you may import that instead.").arg(previousVersion);
}
const int ret = QMessageBox::question(nullptr, CPN_STR_APP_NAME, msg, impNoneBtn, impFileBtn, (previousVersion.isEmpty() ? QString() : impPrevBtn), 0, 0);
if (!ret)
return;
// Import from previous version
if (ret == 2) {
if (!g.importSettings(previousVersion)) {
// very unlikely, but just in case of unexpected error, restart the import
importError();
+8 -5
View File
@@ -38,15 +38,17 @@
#define CPN_MAX_MODULES 2
#define CPN_MAX_STICKS Board::STICK_AXIS_COUNT
#define CPN_MAX_TRIMS Board::TRIM_AXIS_COUNT
#define CPN_MAX_KNOBS 8
#define CPN_MAX_POTS 8
#define CPN_MAX_SLIDERS 4
#define CPN_MAX_POTS (CPN_MAX_KNOBS + CPN_MAX_SLIDERS)
#define CPN_MAX_CYC 3
#define CPN_MAX_SWITCHES 32
#define CPN_MAX_FUNCTION_SWITCHES 6
#define CPN_MAX_MOUSE_ANALOGS 2
#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_MAX_ANALOGS (CPN_MAX_STICKS + CPN_MAX_POTS + CPN_MAX_SLIDERS + CPN_MAX_MOUSE_ANALOGS + CPN_MAX_GYRO_ANALOGS)
#define CPN_MAX_SENSORS 60
#define CPN_MAX_SCRIPTS 9
#define CPN_MAX_SCRIPT_INPUTS 10
#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
@@ -60,8 +62,9 @@
#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 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 ETX_FILES_FILTER "EdgeTX " % CPN_STR_FILES % " (*.etx);;"
#define YML_FILES_FILTER "EdgeTX YAML " % CPN_STR_FILES % " (*.yml);;"
#define EEPROM_FILES_FILTER CPN_STR_RAD_MOD_SETTINGS % " " % CPN_STR_FILES % " (*.etx *.yml *.otx *.eepe *.bin *.hex);;" % ETX_FILES_FILTER % YML_FILES_FILTER % OTX_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"
@@ -118,7 +118,7 @@ RawSourceItemModel::RawSourceItemModel(const GeneralSettings * const generalSett
addItems(SOURCE_TYPE_CYC, RawSource::SourcesGroup, CPN_MAX_CYC);
addItems(SOURCE_TYPE_PPM, RawSource::SourcesGroup, firmware->getCapability(TrainerInputs));
addItems(SOURCE_TYPE_CH, RawSource::SourcesGroup, firmware->getCapability(Outputs));
addItems(SOURCE_TYPE_SPECIAL, RawSource::TelemGroup, 5);
addItems(SOURCE_TYPE_SPECIAL, RawSource::TelemGroup, SOURCE_TYPE_SPECIAL_COUNT);
addItems(SOURCE_TYPE_TELEMETRY, RawSource::TelemGroup, firmware->getCapability(Sensors) * 3);
addItems(SOURCE_TYPE_GVAR, RawSource::GVarsGroup, firmware->getCapability(Gvars));
}
+22
View File
@@ -29,6 +29,23 @@ set(firmwares_SRCS
ersky9x/ersky9xinterface.cpp
opentx/opentxeeprom.cpp
opentx/opentxinterface.cpp
edgetx/edgetxinterface.cpp
edgetx/yaml_ops.cpp
edgetx/yaml_generalsettings.cpp
edgetx/yaml_calibdata.cpp
edgetx/yaml_trainerdata.cpp
edgetx/yaml_switchconfig.cpp
edgetx/yaml_modeldata.cpp
edgetx/yaml_rawswitch.cpp
edgetx/yaml_rawsource.cpp
edgetx/yaml_mixdata.cpp
edgetx/yaml_expodata.cpp
edgetx/yaml_curvedata.cpp
edgetx/yaml_moduledata.cpp
edgetx/yaml_logicalswitchdata.cpp
edgetx/yaml_customfunctiondata.cpp
edgetx/yaml_sensordata.cpp
edgetx/yaml_screendata.cpp
datahelpers.cpp
customisation_data.cpp
)
@@ -48,14 +65,19 @@ set(firmwares_QT
qt5_wrap_cpp(firmwares_SRCS ${firmwares_QT})
add_library(firmwares ${firmwares_SRCS} ${firmwares_HDRS})
target_link_libraries(firmwares ${CPN_COMMON_LIB} datamodels Qt5::Core Qt5::Xml Qt5::Widgets)
target_link_libraries(firmwares yaml-cpp)
target_include_directories(firmwares
PRIVATE
${CMAKE_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
${COMPANION_SRC_DIRECTORY}/thirdparty/yaml-cpp/include
PUBLIC
"${CMAKE_CURRENT_LIST_DIR}"
"${CMAKE_CURRENT_LIST_DIR}/er9x"
"${CMAKE_CURRENT_LIST_DIR}/ersky9x"
"${CMAKE_CURRENT_LIST_DIR}/opentx"
"${CMAKE_CURRENT_LIST_DIR}/edgetx"
)
+200 -92
View File
@@ -21,6 +21,7 @@
#include "boards.h"
#include "macros.h"
#include "compounditemmodels.h"
#include "moduledata.h"
// TODO remove all those constants
// Update: These are now all only used within this class.
@@ -374,7 +375,7 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
else if (IS_TARANIS_X9E(board))
return 4;
else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board))
return 5;
return 7;
else if (IS_HORUS_X12S(board))
return 3;
else if (IS_FLYSKY_NV14(board))
@@ -486,6 +487,12 @@ int Boards::getCapability(Board::Type board, Board::Capability capability)
case NumFunctionSwitches:
return IS_JUMPER_TPRO(board) ? 6 : 0;
case HasSDCard:
return IS_STM32(board);
case HasInternalModuleSupport:
return (IS_STM32(board) && !IS_TARANIS_X9(board));
default:
return 0;
}
@@ -507,104 +514,82 @@ QString Boards::getAxisName(int index)
return CPN_STR_UNKNOWN_ITEM;
}
QString Boards::getAnalogInputName(Board::Type board, int index)
StringTagMappingTable Boards::getAnalogNamesLookupTable(Board::Type board)
{
if (index < 0)
return CPN_STR_UNKNOWN_ITEM;
StringTagMappingTable tbl;
if (index < getBoardCapability(board, Board::Sticks)) {
const QString sticks[] = {
tr("Rud"),
tr("Ele"),
tr("Thr"),
tr("Ail")
};
return sticks[index];
if (getBoardCapability(board, Board::Sticks)) {
tbl.insert(tbl.end(), {"Rud", "Ele", "Thr", "Ail"});
}
index -= getCapability(board, Board::Sticks);
if (IS_SKY9X(board)) {
const QString pots[] = {
"P1",
"P2",
"P3"
};
if (index < DIM(pots))
return pots[index];
}
else if (IS_TARANIS_X9E(board)) {
const QString pots[] = {
"F1",
"F2",
"F3",
"F4",
"S1",
"S2",
"LS",
"RS"
};
if (index < DIM(pots))
return pots[index];
}
else if (IS_TARANIS_XLITE(board)) {
const QString pots[] = {
"S1",
"S2",
"GyrX",
"GyrY"
};
if (index < DIM(pots))
return pots[index];
}
else if (IS_TARANIS(board)) {
const QString pots[] = {
"S1",
"S2",
"S3",
"LS",
"RS"
};
if (index < DIM(pots))
return pots[index];
}
else if (IS_HORUS_X12S(board)) {
const QString pots[] = {
"S1",
"6P",
"S2",
"L1",
"L2",
"LS",
"RS",
"JSx",
"JSy"
};
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",
"6P",
"S2",
"EX1",
"EX2",
"LS",
"RS"
};
if (index < DIM(pots))
return pots[index];
tbl.insert(tbl.end(), {"P1", "P2", "P3"});
} else if (IS_TARANIS_X9E(board)) {
tbl.insert(tbl.end(), {
{"F1", "POT1"},
{"F2", "POT2"},
{"F3", "POT3"},
{"F4", "POT4"},
{"S1", "SLIDER1"},
{"S2", "SLIDER2"},
{"LS", "SLIDER3"},
{"RS", "SLIDER4"},
});
} else if (IS_TARANIS_XLITE(board)) {
tbl.insert(tbl.end(), {
{"S1", "POT1"},
{"S2", "POT2"},
{"GyrX", "GYRO1"},
{"GyrY", "GYRO2"},
});
} else if (IS_TARANIS(board)) {
tbl.insert(tbl.end(), {
{"S1", "POT1"},
{"S2", "POT2"},
{"S3", "POT3"},
{"LS", "SLIDER1"},
{"RS", "SLIDER2"},
});
} else if (IS_HORUS_X12S(board)) {
tbl.insert(tbl.end(), {
{"S1", "S1"},
{"6P", "6POS"},
{"S2", "S2"},
{"L1", "S3"},
{"L2", "S4"},
{"LS", "LS"},
{"RS", "RS"},
{"JSx", "MOUSE1"},
{"JSy", "MOUSE2"},
});
} else if (IS_FLYSKY_NV14(board)) {
tbl.insert(tbl.end(), {
{"VRA", "POT1"},
{"VRB", "POT2"},
});
} else if (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) {
tbl.insert(tbl.end(), {
{"S1", "S1"},
{"6P", "6POS"},
{"S2", "S2"},
{"EX1", "EXT1"},
{"EX2", "EXT2"},
{"EX3", "EXT3"},
{"EX4", "EXT4"},
{"LS", "LS"},
{"RS", "RS"},
});
}
return tbl;
}
QString Boards::getAnalogInputName(Board::Type board, int index)
{
const StringTagMappingTable& lut = getAnalogNamesLookupTable(board);
if (index < (int)lut.size())
return QString::fromStdString(lut[index].name);
return CPN_STR_UNKNOWN_ITEM;
}
@@ -762,3 +747,126 @@ AbstractStaticItemModel * Boards::switchTypeItemModel()
mdl->loadItemList();
return mdl;
}
// static
StringTagMappingTable Boards::getSwitchesLookupTable(Board::Type board)
{
StringTagMappingTable tbl;
for (int i = 0; i < Boards::getCapability(board, Board::Switches); i++) {
SwitchInfo si = Boards::getSwitchInfo(board, i);
if (si.config != SWITCH_NOT_AVAILABLE) // safety check in case Switches does not align
tbl.insert(tbl.end(), si.name.toStdString());
}
return tbl;
}
// static
StringTagMappingTable Boards::getTrimSwitchesLookupTable(Board::Type board)
{
StringTagMappingTable tbl;
tbl.insert(tbl.end(), {
{std::to_string(TRIM_SW_LH_DEC), "TrimRudLeft"},
{std::to_string(TRIM_SW_LH_INC), "TrimRudRight"},
{std::to_string(TRIM_SW_LV_DEC), "TrimEleDown"},
{std::to_string(TRIM_SW_LV_INC), "TrimEleUp"},
{std::to_string(TRIM_SW_RV_DEC), "TrimThrDown"},
{std::to_string(TRIM_SW_RV_INC), "TrimThrUp"},
{std::to_string(TRIM_SW_RH_DEC), "TrimAilLeft"},
{std::to_string(TRIM_SW_RH_INC), "TrimAilRight"},
});
if (getCapability(board, Board::NumTrims) > 4)
tbl.insert(tbl.end(), {
{std::to_string(TRIM_SW_T5_DEC), "TrimT5Down"},
{std::to_string(TRIM_SW_T5_INC), "TrimT5Up"},
{std::to_string(TRIM_SW_T6_DEC), "TrimT6Down"},
{std::to_string(TRIM_SW_T6_INC), "TrimT6Up"},
});
return tbl;
}
// static
StringTagMappingTable Boards::getTrimSourcesLookupTable(Board::Type board)
{
StringTagMappingTable tbl;
tbl.insert(tbl.end(), {
{std::to_string(TRIM_AXIS_LH), "TrimRud"},
{std::to_string(TRIM_AXIS_LV), "TrimEle"},
{std::to_string(TRIM_AXIS_RV), "TrimThr"},
{std::to_string(TRIM_AXIS_RH), "TrimAil"},
});
if (getCapability(board, Board::NumTrims) > 4)
tbl.insert(tbl.end(), {
{std::to_string(TRIM_AXIS_T5), "TrimT5"},
{std::to_string(TRIM_AXIS_T6), "TrimT6"},
});
return tbl;
}
QList<ModuleType> Boards::getSupportedInternalModules(Board::Type board)
{
QList<ModuleType> modules;
if (IS_TARANIS_X9DP_2019(board) || IS_TARANIS_X7_ACCESS(board)) {
modules = {MODULE_TYPE_ISRM_PXX2};
} else if (IS_FLYSKY_NV14(board)) {
modules = {MODULE_TYPE_FLYSKY};
} else if (IS_FAMILY_HORUS_OR_T16(board) || IS_FAMILY_T12(board)
|| (IS_TARANIS_SMALL(board) && IS_ACCESS_RADIO(board))) {
modules.append({
MODULE_TYPE_XJT_PXX1,
MODULE_TYPE_ISRM_PXX2,
MODULE_TYPE_CROSSFIRE,
MODULE_TYPE_MULTIMODULE,
});
} else if (IS_TARANIS(board)) {
modules = {MODULE_TYPE_XJT_PXX1};
}
return modules;
}
ModuleType Boards::getDefaultInternalModules(Board::Type board)
{
switch(board) {
case BOARD_TARANIS_X7:
case BOARD_TARANIS_X9D:
case BOARD_TARANIS_X9DP:
case BOARD_TARANIS_X9E:
case BOARD_HORUS_X12S:
case BOARD_X10:
case BOARD_TARANIS_XLITE:
return MODULE_TYPE_XJT_PXX1;
case BOARD_TARANIS_X7_ACCESS:
case BOARD_TARANIS_X9DP_2019:
case BOARD_X10_EXPRESS:
case BOARD_TARANIS_XLITES:
case BOARD_TARANIS_X9LITE:
case BOARD_TARANIS_X9LITES:
return MODULE_TYPE_ISRM_PXX2;
case BOARD_JUMPER_T12:
case BOARD_JUMPER_T16:
case BOARD_RADIOMASTER_TX16S:
case BOARD_JUMPER_T18:
case BOARD_RADIOMASTER_TX12:
case BOARD_RADIOMASTER_T8:
case BOARD_JUMPER_TLITE:
case BOARD_RADIOMASTER_ZORRO:
case BOARD_JUMPER_TPRO:
return MODULE_TYPE_MULTIMODULE;
case BOARD_FLYSKY_NV14:
return MODULE_TYPE_FLYSKY;
default:
return MODULE_TYPE_NONE;
}
}
+29 -5
View File
@@ -19,6 +19,8 @@
*/
#pragma once
#include "datahelpers.h"
#include "moduledata.h"
#include <QtCore>
#include <QObject>
@@ -145,7 +147,9 @@ namespace Board {
NumTrimSwitches,
HasRTC,
HasColorLcd,
NumFunctionSwitches
NumFunctionSwitches,
HasSDCard,
HasInternalModuleSupport
};
struct SwitchInfo
@@ -199,18 +203,26 @@ class Boards
static int getEEpromSize(Board::Type board);
static int getFlashSize(Board::Type board);
static Board::SwitchInfo getSwitchInfo(Board::Type board, int index);
static StringTagMappingTable getSwitchesLookupTable(Board::Type board);
static int getCapability(Board::Type board, Board::Capability capability);
static QString getAxisName(int index);
static StringTagMappingTable getAnalogNamesLookupTable(Board::Type board);
static QString getAnalogInputName(Board::Type board, int index);
static bool isBoardCompatible(Board::Type board1, Board::Type board2);
static QString getBoardName(Board::Type board);
static QString potTypeToString(int value);
static QString sliderTypeToString(int value);
static QString switchTypeToString(int value);
static QString intModuleTypeToString(int value);
static AbstractStaticItemModel * potTypeItemModel();
static AbstractStaticItemModel * sliderTypeItemModel();
static AbstractStaticItemModel * switchTypeItemModel();
static AbstractStaticItemModel * intModuleTypeItemModel();
static StringTagMappingTable getTrimSwitchesLookupTable(Board::Type board);
static StringTagMappingTable getTrimSourcesLookupTable(Board::Type board);
static QList<ModuleType> getSupportedInternalModules(Board::Type board);
static ModuleType getDefaultInternalModules(Board::Type board);
protected:
Board::Type m_boardType;
@@ -282,7 +294,12 @@ inline bool IS_FAMILY_T16(Board::Type board)
inline bool IS_FAMILY_T12(Board::Type board)
{
return board == Board::BOARD_JUMPER_T12 || board == Board::BOARD_RADIOMASTER_TX12 || board == Board::BOARD_RADIOMASTER_ZORRO || board == Board::BOARD_RADIOMASTER_T8 || board == Board::BOARD_JUMPER_TLITE;
return board == Board::BOARD_JUMPER_T12 ||
board == Board::BOARD_RADIOMASTER_TX12 ||
board == Board::BOARD_RADIOMASTER_ZORRO ||
board == Board::BOARD_RADIOMASTER_T8 ||
board == Board::BOARD_JUMPER_TLITE ||
board == Board::BOARD_JUMPER_TPRO;
}
inline bool IS_FLYSKY_NV14(Board::Type board)
@@ -400,10 +417,17 @@ inline bool IS_TARANIS_X9DP_2019(Board::Type board)
return (board == Board::BOARD_TARANIS_X9DP_2019);
}
inline bool IS_ACCESS_RADIO(Board::Type board)
{
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);
}
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")));
return IS_ACCESS_RADIO(board) ||
(IS_FAMILY_HORUS_OR_T16(board) && id.contains("internalaccess"));
}
inline bool HAS_EEPROM_YAML(Board::Type board)
-12
View File
@@ -30,18 +30,6 @@ void CurveData::clear(int count)
{
memset(reinterpret_cast<void *>(this), 0, sizeof(CurveData));
this->count = count;
int incr = 200 / (count - 1);
// points[0].x must be -100
for (int i = 0; i < count; i++) {
if (i < count - 1)
points[i].x = -100 + incr * i;
else
points[i].x = 100; // points[last].x must be +100 do not risk a rounding issue using incr
points[i].y = points[i].x;
}
}
bool CurveData::isEmpty() const
@@ -84,7 +84,7 @@ QString CustomFunctionData::funcToString(const AssignFunc func, const ModelData
else if (func == FuncReset)
return tr("Reset");
else if (func >= FuncSetTimer1 && func <= FuncSetTimerLast)
return tr("Set %1").arg(RawSource(SOURCE_TYPE_SPECIAL, SOURCE_TYPE_SPECIAL_TIMER1_IDX + func - FuncSetTimer1).toString(model));
return tr("Set %1").arg(RawSource(SOURCE_TYPE_SPECIAL, SOURCE_TYPE_SPECIAL_FIRST_TIMER + func - FuncSetTimer1).toString(model));
else if (func == FuncVario)
return tr("Vario");
else if (func == FuncPlayPrompt)
@@ -249,7 +249,7 @@ QString CustomFunctionData::resetToString(const int value, const ModelData * mod
if (value < step) {
if (value < firmware->getCapability(Timers))
return RawSource(SOURCE_TYPE_SPECIAL, value + SOURCE_TYPE_SPECIAL_TIMER1_IDX).toString(model);
return RawSource(SOURCE_TYPE_SPECIAL, value + SOURCE_TYPE_SPECIAL_FIRST_TIMER).toString(model);
else
return QString(CPN_STR_UNKNOWN_ITEM);
}
+47 -1
View File
@@ -27,6 +27,26 @@
#define WHITE RADIO_RGB(0xFF, 0xFF, 0xFF)
#define RED RADIO_RGB(229, 32, 30)
ZoneOptionValue::ZoneOptionValue()
{
memset((void*)this, 0, sizeof(ZoneOptionValue));
}
ZoneOptionValueTyped::ZoneOptionValueTyped()
{
memset((void*)this, 0, sizeof(ZoneOptionValueTyped));
}
WidgetPersistentData::WidgetPersistentData()
{
memset((void*)this, 0, sizeof(WidgetPersistentData));
}
ZonePersistentData::ZonePersistentData()
{
memset((void*)this, 0, sizeof(ZonePersistentData));
}
inline void setZoneOptionValue(ZoneOptionValue& zov, bool value)
{
zov.boolValue = value;
@@ -61,9 +81,13 @@ inline ZoneOptionValueEnum zoneValueEnumFromType(ZoneOption::Type type)
return ZOV_Bool;
case ZoneOption::Color:
return ZOV_Color;
case ZoneOption::Source:
return ZOV_Source;
case ZoneOption::Timer:
case ZoneOption::Switch:
case ZoneOption::Source:
case ZoneOption::TextSize:
default:
return ZOV_Unsigned;
@@ -85,6 +109,28 @@ inline const char * zoneOptionValueEnumToString(ZoneOptionValueEnum zovenum) {
}
}
static const ZoneOptionValueTyped zero_widget_option = {};
bool ZoneOptionValueTyped::isEmpty() const
{
return !memcmp((void*)this, &zero_widget_option, sizeof(zero_widget_option));
}
bool ZonePersistentData::isEmpty() const
{
return strlen(widgetName) == 0;
}
RadioLayout::CustomScreenData::CustomScreenData()
{
memset((void*)this, 0, sizeof(RadioLayout::CustomScreenData));
}
bool RadioLayout::CustomScreenData::isEmpty() const
{
return strlen(layoutId) == 0;
}
void RadioTheme::init(const char* themeName, ThemeData& themeData)
{
memset(&themeData, 0, sizeof(ThemeData));
+23 -1
View File
@@ -20,6 +20,7 @@
#pragma once
#include "constants.h"
#include "rawsource.h"
#include <QtCore>
@@ -58,6 +59,10 @@ struct ZoneOptionValue // union in radio/src/datastructs.h
int signedValue;
unsigned int boolValue;
char stringValue[LEN_ZONE_OPTION_STRING + 1];
RawSource sourceValue;
unsigned int colorValue;
ZoneOptionValue();
};
enum ZoneOptionValueEnum {
@@ -65,7 +70,9 @@ enum ZoneOptionValueEnum {
ZOV_Signed,
ZOV_Bool,
ZOV_String,
ZOV_LAST = ZOV_String
ZOV_Source,
ZOV_Color,
ZOV_LAST = ZOV_Color
};
struct ZoneOption
@@ -93,21 +100,33 @@ struct ZoneOptionValueTyped
{
ZoneOptionValueEnum type;
ZoneOptionValue value;
ZoneOptionValueTyped();
bool isEmpty() const;
};
struct WidgetPersistentData {
ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS];
WidgetPersistentData();
};
struct ZonePersistentData {
char widgetName[WIDGET_NAME_LEN + 1];
WidgetPersistentData widgetData;
ZonePersistentData();
bool isEmpty() const;
};
template<int N, int O>
struct WidgetsContainerPersistentData {
ZonePersistentData zones[N];
ZoneOptionValueTyped options[O];
WidgetsContainerPersistentData() {
memset((void*)this, 0, sizeof(WidgetsContainerPersistentData));
}
};
typedef WidgetsContainerPersistentData<MAX_LAYOUT_ZONES, MAX_LAYOUT_OPTIONS>
@@ -141,6 +160,9 @@ class RadioLayout
struct CustomScreenData {
char layoutId[LAYOUT_ID_LEN + 1];
LayoutPersistentData layoutPersistentData;
CustomScreenData();
bool isEmpty() const;
};
struct CustomScreens {
+23
View File
@@ -68,3 +68,26 @@ QString DataHelpers::timeToString(const int value, const unsigned int mask)
result.append(QString("%1:%2").arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')));
return result;
}
int DataHelpers::getStringTagMappingIndex(const StringTagMappingTable& lut, const char * tag)
{
auto it =
find_if(lut.begin(), lut.end(), [=](const StringTagMapping& elmt) {
if (elmt.tag == tag) return true;
return false;
});
if (it != lut.end()) {
return it - lut.begin();
}
return -1;
}
std::string DataHelpers::getStringTagMappingTag(const StringTagMappingTable& lut, unsigned int index)
{
if (index < lut.size())
return lut[index].tag;
return std::string();
}
+42 -1
View File
@@ -21,6 +21,46 @@
#pragma once
#include <QtCore>
#include <string>
#include <vector>
struct StringTagMapping {
std::string name;
std::string tag;
StringTagMapping() = default;
StringTagMapping(const char* name) :
name(name), tag(name)
{
}
StringTagMapping(const std::string& name) :
name(name), tag(name)
{
}
StringTagMapping(const char* name, const char* tag) :
name(name), tag(tag)
{
}
StringTagMapping(const std::string& name, const std::string& tag) :
name(name), tag(tag)
{
}
};
typedef std::vector<StringTagMapping> StringTagMappingTable;
#define STRINGTAGMAPPINGFUNCS_HELPER(tbl, name, index, tag) \
inline int name##index (const char * tag) \
{ \
return DataHelpers::getStringTagMappingIndex(tbl, tag); \
} \
\
inline std::string name##tag (unsigned int index) \
{ \
return DataHelpers::getStringTagMappingTag(tbl, index); \
}
#define STRINGTAGMAPPINGFUNCS(tbl, name) STRINGTAGMAPPINGFUNCS_HELPER(tbl, get##name, Index, Tag)
class FieldRange
{
@@ -65,5 +105,6 @@ namespace DataHelpers
QString boolToString(const bool value, const BoolFormat format);
QString getElementName(const QString & prefix, const unsigned int index, const char * name = 0, const bool padding = false);
QString timeToString(const int value, const unsigned int mask);
int getStringTagMappingIndex(const StringTagMappingTable& lut, const char * tag);
std::string getStringTagMappingTag(const StringTagMappingTable& lut, unsigned int index);
}
@@ -0,0 +1,169 @@
/*
* 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 "edgetxinterface.h"
#include "yaml_ops.h"
#include "yaml_generalsettings.h"
#include "yaml_modeldata.h"
#include <QMessageBox>
static YAML::Node loadYamlFromByteArray(const QByteArray& data)
{
// TODO: use real streaming to avoid memory copies
std::istringstream data_istream(data.toStdString());
return YAML::Load(data_istream);
}
static void writeYamlToByteArray(const YAML::Node& node, QByteArray& data)
{
// TODO: use real streaming to avoid memory copies
std::stringstream data_ostream;
data_ostream << node;
data = QByteArray::fromStdString(data_ostream.str());
qDebug() << "Saving YAML:";
qDebug() << data_ostream.str().c_str();
}
bool loadModelsListFromYaml(std::vector<CategoryData>& categories,
EtxModelfiles& modelFiles,
const QByteArray& data)
{
if (data.size() == 0)
return true;
try {
YAML::Node node = loadYamlFromByteArray(data);
if (!node.IsSequence()) return false;
int modelIdx = 0;
for (const auto& cat : node) {
if (!cat.IsMap()) continue;
for (const auto& cat_map : cat) {
categories.push_back(cat_map.first.Scalar().c_str());
const auto& models = cat_map.second;
if (!models.IsSequence()) continue;
for (const auto& model : models) {
std::string filename, name;
model["filename"] >> filename;
model["name"] >> name;
modelFiles.push_back(
{filename, name, (int)categories.size() - 1, modelIdx++});
}
}
}
} catch (const std::runtime_error& e) {
qDebug() << "YAML::ParserException: " << e.what();
return false;
}
return true;
}
bool loadModelFromYaml(ModelData& model, const QByteArray& data)
{
YAML::Node node = loadYamlFromByteArray(data);
node >> model;
return true;
}
bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data)
{
YAML::Node node = loadYamlFromByteArray(data);
node >> settings;
if (settings.version < CPN_CURRENT_SETTINGS_VERSION) {
qDebug() << "Older version of settings detected:" << settings.version;
}
else if (settings.version > CPN_CURRENT_SETTINGS_VERSION) {
qDebug() << "Newer version of settings detected:" << settings.version;
QMessageBox::critical(NULL, CPN_STR_APP_NAME, QCoreApplication::translate("EdgeTXInterface", "Companion does not support settings version %1!").arg(settings.version));
return false;
}
return true;
}
// TODO:
// 'modelFiles' should be ordered by Category index to avoid
// turning the sequence into a map.
//
bool writeModelsListToYaml(const std::vector<CategoryData>& categories,
const EtxModelfiles& modelFiles,
QByteArray& data)
{
YAML::Node node;
for (const auto& modelFile: modelFiles) {
YAML::Node cat_attrs;
cat_attrs["filename"] = modelFile.filename;
cat_attrs["name"] = modelFile.name;
if (modelFile.category >= (int)categories.size()) {
return false;
}
const std::string cat_name = categories[modelFile.category].name;
node[modelFile.category][cat_name].push_back(cat_attrs);
}
writeYamlToByteArray(node, data);
return true;
}
bool writeModelToYaml(const ModelData& model, QByteArray& data)
{
YAML::Node node;
node = model;
writeYamlToByteArray(node, data);
return true;
}
bool writeRadioSettingsToYaml(const GeneralSettings& settings, QByteArray& data)
{
YAML::Node node;
node = settings;
writeYamlToByteArray(node, data);
return true;
}
std::string patchFilenameToYaml(const std::string& str)
{
constexpr const char MODELS_EXT[] = ".bin";
constexpr const char YAML_EXT[] = ".yml";
constexpr unsigned bin_len = sizeof(MODELS_EXT) - 1;
// patch file extension
auto const dot_pos = str.find_last_of('.');
if ((dot_pos != std::string::npos) && (str.substr(dot_pos) == MODELS_EXT)) {
std::string tmp = str;
return tmp.replace(dot_pos, bin_len, YAML_EXT);
}
return str;
}
@@ -0,0 +1,55 @@
/*
* 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 "radiodata.h"
#include <QByteArray>
#include <string>
#include <utility>
#include <vector>
constexpr unsigned int CPN_CURRENT_SETTINGS_VERSION = { 221 };
struct EtxModelMetadata {
std::string filename;
std::string name;
int category;
int modelIdx;
};
typedef std::list<EtxModelMetadata> EtxModelfiles;
bool loadModelsListFromYaml(std::vector<CategoryData>& categories,
EtxModelfiles& modelFiles,
const QByteArray& data);
bool loadModelFromYaml(ModelData& model, const QByteArray& data);
bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data);
bool writeModelsListToYaml(const std::vector<CategoryData>& categories,
const EtxModelfiles& modelFiles,
QByteArray& data);
bool writeModelToYaml(const ModelData& model, QByteArray& data);
bool writeRadioSettingsToYaml(const GeneralSettings& settings, QByteArray& data);
std::string patchFilenameToYaml(const std::string& str);
@@ -0,0 +1,93 @@
/*
* 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_calibdata.h"
#include "eeprominterface.h"
YamlCalibData::YamlCalibData() { memset(calib, 0, sizeof(calib)); }
YamlCalibData::YamlCalibData(const int* calibMid, const int* calibSpanNeg,
const int* calibSpanPos)
{
for (int i = 0; i < CPN_MAX_ANALOGS; i++) {
calib[i].mid = calibMid[i];
calib[i].spanNeg = calibSpanNeg[i];
calib[i].spanPos = calibSpanPos[i];
}
}
void YamlCalibData::copy(int* calibMid, int* calibSpanNeg,
int* calibSpanPos) const
{
for (int i = 0; i < CPN_MAX_ANALOGS; i++) {
calibMid[i] = calib[i].mid;
calibSpanNeg[i] = calib[i].spanNeg;
calibSpanPos[i] = calib[i].spanPos;
}
}
namespace YAML
{
Node convert<CalibData>::encode(const CalibData& rhs)
{
Node node;
node["mid"] = rhs.mid;
node["spanNeg"] = rhs.spanNeg;
node["spanPos"] = rhs.spanPos;
return node;
}
bool convert<CalibData>::decode(const Node& node, CalibData& rhs)
{
if (!node.IsMap()) return false;
node["mid"] >> rhs.mid;
node["spanNeg"] >> rhs.spanNeg;
node["spanPos"] >> rhs.spanPos;
return true;
}
Node convert<YamlCalibData>::encode(const YamlCalibData& rhs)
{
Node node;
int idx = 0;
const auto* calibIdxLut = getCurrentFirmware()->getAnalogIndexNamesLookupTable();
for (const auto& kv : *calibIdxLut) {
node[kv.tag] = rhs.calib[idx++];
}
return node;
}
bool convert<YamlCalibData>::decode(const Node& node, YamlCalibData& rhs)
{
if (!node.IsMap()) return false;
for (const auto& kv : node) {
std::string tag;
kv.first >> tag;
int idx = getCurrentFirmware()->getAnalogInputIndex(tag.c_str());
if (idx >= 0) {
kv.second >> rhs.calib[idx];
}
}
return true;
}
} // namespace YAML
@@ -0,0 +1,51 @@
/*
* 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_ops.h"
#include "generalsettings.h"
struct CalibData {
int16_t mid;
int16_t spanNeg;
int16_t spanPos;
};
struct YamlCalibData {
CalibData calib[CPN_MAX_ANALOGS];
YamlCalibData();
YamlCalibData(const int* calibMid, const int* calibSpanNeg, const int* calibSpanPos);
void copy(int* calibMid, int* calibSpanNeg, int* calibSpanPos) const;
};
namespace YAML
{
template <>
struct convert<CalibData> {
static Node encode(const CalibData& rhs);
static bool decode(const Node& node, CalibData& rhs);
};
template <>
struct convert<YamlCalibData> {
static Node encode(const YamlCalibData& rhs);
static bool decode(const Node& node, YamlCalibData& rhs);
};
} // namespace YAML
@@ -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.
*/
#include "yaml_curvedata.h"
// CurveHeader curves[MAX_CURVES];
// int8_t points[MAX_CURVE_POINTS];
void YamlReadCurvePoints(const YAML::Node& node, CurveData* curves)
{
int pidx = 0;
for (int i = 0; i < CPN_MAX_CURVES; i++) {
CurveData& curve = curves[i];
for (int j = 0; j < curve.count; j++) {
std::string pidx_str = std::to_string(pidx++);
if (node[pidx_str]) {
int y = 0;
node[pidx_str]["val"] >> y;
curve.points[j].y = (int8_t)y;
}
}
if (curve.type == CurveData::CURVE_TYPE_CUSTOM) {
// Custom curve: X points are trailing Y points
curve.points[0].x = -100;
for (int k = 1; k < curve.count - 1; k++) {
std::string pidx_str = std::to_string(pidx++);
if (node[pidx_str]) {
int x = 0;
node[pidx_str]["val"] >> x;
curve.points[k].x = (int8_t)x;
}
}
curve.points[curve.count - 1].x = +100;
} else {
// Standard curve: X points are generated
for (int k = 0; k < curve.count; k++) {
curve.points[k].x = -100 + (200 * k) / (curve.count - 1);
}
}
}
}
void YamlWriteCurvePoints(YAML::Node& node, const CurveData* curves)
{
int pidx = 0;
for (int i = 0; i < CPN_MAX_CURVES; i++) {
//TODO: assert max number of points
// and raise exception
const CurveData& curve = curves[i];
if (!curve.isEmpty()) {
for (int k = 0; k < curve.count; k++) {
node[std::to_string(pidx++)]["val"] = (int)curve.points[k].y;
}
if (curve.type == CurveData::CURVE_TYPE_CUSTOM) {
for (int k = 1; k < curve.count - 1; k++) {
node[std::to_string(pidx++)]["val"] = (int)curve.points[k].x;
}
}
} else {
pidx += 5;
}
}
}
namespace YAML
{
Node convert<CurveData>::encode(const CurveData& rhs)
{
Node node;
node["type"] = (int)rhs.type;
node["smooth"] = (int)rhs.smooth;
node["points"] = rhs.count - 5;
node["name"] = rhs.name;
return node;
}
bool convert<CurveData>::decode(const Node& node, CurveData& rhs)
{
int curve_type = 0;
node["type"] >> curve_type;
rhs.type = (CurveData::CurveType)curve_type;
node["smooth"] >> rhs.smooth;
node["points"] >> ioffset_int(rhs.count, -5);
node["name"] >> rhs.name;
return true;
}
}
@@ -0,0 +1,34 @@
/*
* 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_ops.h"
#include "curvedata.h"
void YamlReadCurvePoints(const YAML::Node& node, CurveData* curves);
void YamlWriteCurvePoints(YAML::Node& node, const CurveData* curves);
namespace YAML
{
template <>
struct convert<CurveData> {
static Node encode(const CurveData& rhs);
static bool decode(const Node& node, CurveData& rhs);
};
} // namespace YAML
@@ -0,0 +1,365 @@
/*
* 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_customfunctiondata.h"
#include "yaml_rawswitch.h"
#include "yaml_rawsource.h"
static bool fnHasEnable(AssignFunc fn)
{
return (fn <= FuncInstantTrim)
|| (fn >= FuncReset && fn <= FuncSetTimerLast)
|| (fn >= FuncAdjustGV1 && fn <= FuncBindExternalModule)
|| (fn == FuncVolume);
}
static bool fnHasRepeat(AssignFunc fn)
{
return (fn == FuncPlayPrompt)
|| (fn == FuncPlayValue)
|| (fn == FuncPlayHaptic)
|| (fn == FuncPlaySound);
}
static const YamlLookupTable customFnLut = {
{ FuncOverrideCH1, "OVERRIDE_CHANNEL" },
{ FuncTrainer, "TRAINER" },
{ FuncInstantTrim, "INSTANT_TRIM" },
{ FuncReset, "RESET" },
{ FuncSetTimer1, "SET_TIMER" },
{ FuncAdjustGV1, "ADJUST_GVAR" },
{ FuncVolume, "VOLUME" },
{ FuncSetFailsafe, "SET_FAILSAFE" },
{ FuncRangeCheckInternalModule, "RANGECHECK" },
{ FuncBindInternalModule, "BIND" },
{ FuncPlaySound, "PLAY_SOUND" },
{ FuncPlayPrompt, "PLAY_TRACK" },
{ FuncPlayValue, "PLAY_VALUE" },
{ FuncPlayScript, "PLAY_SCRIPT" },
{ FuncBackgroundMusic, "BACKGND_MUSIC" },
{ FuncBackgroundMusicPause, "BACKGND_MUSIC_PAUSE" },
{ FuncVario, "VARIO" },
{ FuncPlayHaptic, "HAPTIC" },
{ FuncLogs, "LOGS" },
{ FuncBacklight, "BACKLIGHT" },
{ FuncScreenshot, "SCREENSHOT" },
{ FuncRacingMode, "RACING_MODE" },
{ FuncDisableTouch, "DISABLE_TOUCH" },
};
static const YamlLookupTable trainerLut = {
{ 0, "sticks" },
{ 1, "Rud" },
{ 2, "Ele" },
{ 3, "Thr" },
{ 4, "Ail" },
{ 5, "chans" },
};
static const YamlLookupTable soundLut = {
{ 0, "Bp1" },
{ 1, "Bp2" },
{ 2, "Bp3" },
{ 3, "Wrn1" },
{ 4, "Wrn2" },
{ 5, "Chee" },
{ 6, "Rata" },
{ 7, "Tick" },
{ 8, "Sirn" },
{ 9, "Ring" },
{ 10, "SciF" },
{ 11, "Robt" },
{ 12, "Chrp" },
{ 13, "Tada" },
{ 14, "Crck" },
{ 15, "Alrm" },
};
static const YamlLookupTable resetLut = {
{ 0, "Tmr1" },
{ 1, "Tmr2" },
{ 2, "Tmr3" },
{ 3, "All" },
{ 4, "Tele" },
};
static const YamlLookupTable gvarModeLut = {
{ FUNC_ADJUST_GVAR_CONSTANT, "Cst" },
{ FUNC_ADJUST_GVAR_SOURCE, "Src" },
{ FUNC_ADJUST_GVAR_GVAR, "GVar" },
{ FUNC_ADJUST_GVAR_INCDEC, "IncDec" },
};
static const YamlLookupTable moduleLut = {
{ 0, "Int" },
{ 1, "Ext" },
};
namespace YAML
{
Node convert<CustomFunctionData>::encode(const CustomFunctionData& rhs)
{
Node node;
node["swtch"] = rhs.swtch;
int fn = rhs.func;
int p1 = 0;
if(fn >= FuncOverrideCH1 && fn <= FuncOverrideCHLast) {
p1 = fn - (int)FuncOverrideCH1;
fn = (int)FuncOverrideCH1;
} else if (fn >= FuncTrainer && fn <= FuncTrainerChannels) {
p1 = fn - (int)FuncTrainer;
fn = (int)FuncTrainer;
} else if (fn >= FuncSetTimer1 && fn <= FuncSetTimerLast) {
p1 = fn - (int)FuncSetTimer1;
fn = (int)FuncSetTimer1;
} else if (fn >= FuncAdjustGV1 && fn <= FuncAdjustGVLast) {
p1 = fn - (int)FuncAdjustGV1;
fn = (int)FuncAdjustGV1;
} else if (fn >= FuncRangeCheckInternalModule && fn <= FuncRangeCheckExternalModule) {
p1 = fn - (int)FuncRangeCheckInternalModule;
fn = (int)FuncRangeCheckInternalModule;
} else if (fn >= FuncBindInternalModule && fn <= FuncBindExternalModule) {
p1 = fn - (int)FuncBindInternalModule;
fn = (int)FuncBindInternalModule;
}
node["func"] = LookupValue(customFnLut, fn);
bool add_comma = true;
std::string def;
switch(fn) {
case FuncOverrideCH1:
def += std::to_string(p1);
def += ",";
def += std::to_string(rhs.param);
break;
case FuncTrainer:
def += LookupValue(trainerLut, p1);
break;
case FuncPlaySound:
def += LookupValue(soundLut, rhs.param);
break;
case FuncPlayPrompt:
case FuncPlayScript:
case FuncBackgroundMusic:
def += std::string(rhs.paramarm);
break;
case FuncReset:
def += LookupValue(resetLut, rhs.param);
if (def.empty()) {
def += std::to_string(rhs.param - resetLut.size());
}
break;
case FuncSetTimer1: {
def += LookupValue(resetLut, p1);
def += ",";
def += std::to_string(rhs.param);
} break;
case FuncPlayValue:
case FuncVolume:
case FuncBacklight: {
def += YamlRawSourceEncode(RawSource(rhs.param));
} break;
case FuncAdjustGV1: {
// + GV #
def += std::to_string(p1);
def += ",";
def += LookupValue(gvarModeLut, rhs.adjustMode);
def += ",";
switch(rhs.adjustMode) {
case FUNC_ADJUST_GVAR_CONSTANT:
case FUNC_ADJUST_GVAR_INCDEC:
def += std::to_string(rhs.param);
break;
case FUNC_ADJUST_GVAR_GVAR:
case FUNC_ADJUST_GVAR_SOURCE: {
def += YamlRawSourceEncode(RawSource(rhs.param));
} break;
}
} break;
case FuncRangeCheckInternalModule:
case FuncBindInternalModule: {
def += LookupValue(moduleLut, p1);
} break;
case FuncLogs:
def += std::to_string(rhs.param);
break;
default:
add_comma = false;
break;
}
if (fnHasEnable(rhs.func)) {
if (add_comma) {
def += ",";
}
def += std::to_string((int)rhs.enabled);
} else if(fnHasRepeat(rhs.func)) {
if (add_comma) {
def += ",";
}
if (rhs.repeatParam == 0) {
def += "1x";
} else if (rhs.repeatParam == -1) {
def += "!1x";
} else {
def += std::to_string(rhs.repeatParam);
}
}
if (!def.empty()) {
node["def"] = def;
}
return node;
}
bool convert<CustomFunctionData>::decode(const Node& node,
CustomFunctionData& rhs)
{
node["swtch"] >> rhs.swtch;
int func = 0;
node["func"] >> customFnLut >> func;
rhs.func = (AssignFunc)func;
std::string def_str;
node["def"] >> def_str;
std::stringstream def(def_str);
switch(rhs.func) {
case FuncOverrideCH1: {
int ch=0;
def >> ch;
rhs.func = (AssignFunc)((int)rhs.func + ch);
def.ignore();
def >> rhs.param;
} break;
case FuncTrainer: {
std::string value_str;
getline(def, value_str, ',');
int value=0;
Node(value_str) >> trainerLut >> value;
rhs.func = (AssignFunc)((int)rhs.func + value);
} break;
case FuncPlaySound: {
std::string snd;
getline(def, snd, ',');
Node(snd) >> soundLut >> rhs.param;
} break;
case FuncPlayPrompt:
case FuncPlayScript:
case FuncBackgroundMusic: {
std::string file_str;
getline(def, file_str, ',');
strncpy(rhs.paramarm, file_str.c_str(), sizeof(rhs.paramarm)-1);
} break;
case FuncReset: {
std::string rst_str;
getline(def, rst_str, ',');
try {
rhs.param = std::stoi(rst_str);
rhs.param += resetLut.size();
break;
} catch(...) {}
Node(rst_str.c_str()) >> resetLut >> rhs.param;
} break;
case FuncSetTimer1: {
// + timer #
std::string tmr_str;
getline(def, tmr_str, ',');
int tmr = 0;
Node(tmr_str) >> resetLut >> tmr;
rhs.func = (AssignFunc)((int)rhs.func + tmr);
def >> rhs.param;
} break;
case FuncPlayValue:
case FuncVolume:
case FuncBacklight: {
std::string src_str;
getline(def, src_str, ',');
rhs.param = YamlRawSourceDecode(src_str).toValue();
} break;
case FuncAdjustGV1: {
// + GV #
int gvar_idx=0;
def >> gvar_idx;
rhs.func = (AssignFunc)((int)rhs.func + gvar_idx);
def.ignore();
std::string mode;
getline(def, mode, ',');
Node(mode) >> gvarModeLut >> rhs.adjustMode;
switch(rhs.adjustMode) {
case FUNC_ADJUST_GVAR_CONSTANT:
case FUNC_ADJUST_GVAR_INCDEC:
def >> rhs.param;
break;
case FUNC_ADJUST_GVAR_GVAR:
case FUNC_ADJUST_GVAR_SOURCE: {
std::string src_str;
getline(def, src_str, ',');
RawSource src;
Node(src_str) >> src;
rhs.param = src.toValue();
} break;
}
} break;
case FuncRangeCheckInternalModule:
case FuncBindInternalModule: {
// DANGEROUS_FUNCTIONS...
// + module # ???
std::string mod;
getline(def, mod, ',');
int module=0;
Node(mod) >> moduleLut >> module;
rhs.func = (AssignFunc)((int)rhs.func + module);
} break;
case FuncLogs: {
int param = 0;
def >> param;
rhs.param = param;
} break;
default:
break;
}
if (fnHasEnable(rhs.func)) {
int en = 0;
def >> en;
rhs.enabled = en;
} else if(fnHasRepeat(rhs.func)) {
std::string repeat;
getline(def, repeat);
if (repeat == "1x") {
rhs.repeatParam = 0;
} else if (repeat == "!1x") {
rhs.repeatParam = -1;
} else {
try {
rhs.repeatParam = std::stoi(repeat);
} catch(...) {}
}
}
return true;
}
} // namespace YAML
@@ -0,0 +1,31 @@
/*
* 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_ops.h"
#include "customfunctiondata.h"
namespace YAML
{
template <>
struct convert<CustomFunctionData> {
static Node encode(const CustomFunctionData& rhs);
static bool decode(const Node& node, CustomFunctionData& rhs);
};
} // namespace YAML
@@ -0,0 +1,67 @@
/*
* 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_expodata.h"
#include "yaml_mixdata.h"
#include "yaml_rawswitch.h"
#include "yaml_rawsource.h"
#include "curvereference.h"
namespace YAML
{
Node convert<ExpoData>::encode(const ExpoData& rhs)
{
Node node;
node["srcRaw"] = rhs.srcRaw;
node["scale"] = rhs.scale;
node["mode"] = rhs.mode; // InputMode
node["chn"] = rhs.chn;
node["swtch"] = rhs.swtch;
node["flightModes"] = YamlWriteFlightModes(rhs.flightModes);
node["weight"] = YamlWriteMixWeight(rhs.weight);
node["offset"] = rhs.offset; // YamlWriteMixWeight
node["curve"] = rhs.curve;
node["carryTrim"] = rhs.carryTrim;
node["name"] = rhs.name;
return node;
}
bool convert<ExpoData>::decode(const Node& node, ExpoData& rhs)
{
node["srcRaw"] >> rhs.srcRaw;
node["scale"] >> rhs.scale;
node["mode"] >> rhs.mode; // InputMode
node["chn"] >> rhs.chn;
node["swtch"] >> rhs.swtch;
if (node["flightModes"]) {
rhs.flightModes = YamlReadFlightModes(node["flightModes"]);
}
if (node["weight"]) {
rhs.weight = YamlReadMixWeight(node["weight"]);
}
if (node["offset"]) {
rhs.offset = YamlReadMixWeight(node["offset"]);
}
node["curve"] >> rhs.curve;
node["carryTrim"] >> rhs.carryTrim;
node["name"] >> rhs.name;
return true;
}
} // namespace YAML
@@ -0,0 +1,31 @@
/*
* 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_ops.h"
#include "input_data.h"
namespace YAML
{
template <>
struct convert<ExpoData> {
static Node encode(const ExpoData& rhs);
static bool decode(const Node& node, ExpoData& rhs);
};
} // namespace YAML
@@ -0,0 +1,361 @@
/*
* 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_generalsettings.h"
#include "yaml_customfunctiondata.h"
#include "yaml_trainerdata.h"
#include "yaml_calibdata.h"
#include "yaml_switchconfig.h"
#include "yaml_moduledata.h"
#include "eeprominterface.h"
#include "edgetxinterface.h"
#include <QMessageBox>
const YamlLookupTable beeperModeLut = {
{ GeneralSettings::BEEPER_QUIET, "mode_quiet" },
{ GeneralSettings::BEEPER_ALARMS_ONLY, "mode_alarms" },
{ GeneralSettings::BEEPER_NOKEYS, "mode_nokeys" },
{ GeneralSettings::BEEPER_ALL, "mode_all" },
};
enum BacklightMode {
e_backlight_mode_off = 0,
e_backlight_mode_keys = 1,
e_backlight_mode_sticks = 2,
e_backlight_mode_all = e_backlight_mode_keys+e_backlight_mode_sticks,
e_backlight_mode_on
};
const YamlLookupTable backlightModeLut = {
{ e_backlight_mode_off, "backlight_mode_off" },
{ e_backlight_mode_keys, "backlight_mode_keys" },
{ e_backlight_mode_sticks, "backlight_mode_sticks" },
{ e_backlight_mode_all, "backlight_mode_all" },
{ e_backlight_mode_on, "backlight_mode_on" },
};
const YamlLookupTable bluetoothModeLut = {
{ GeneralSettings::BLUETOOTH_MODE_OFF, "OFF" },
{ GeneralSettings::BLUETOOTH_MODE_TELEMETRY, "TELEMETRY" },
{ GeneralSettings::BLUETOOTH_MODE_TRAINER, "TRAINER" },
};
const YamlLookupTable uartModeLut = {
{ GeneralSettings::AUX_SERIAL_OFF, "MODE_NONE" },
{ GeneralSettings::AUX_SERIAL_TELE_MIRROR, "MODE_TELEMETRY_MIRROR" },
{ GeneralSettings::AUX_SERIAL_TELE_IN, "MODE_TELEMETRY" },
{ GeneralSettings::AUX_SERIAL_SBUS_TRAINER, "MODE_SBUS_TRAINER" },
{ GeneralSettings::AUX_SERIAL_LUA, "MODE_LUA" },
};
const YamlLookupTable antennaModeLut = {
{ GeneralSettings::ANTENNA_MODE_INTERNAL, "MODE_INTERNAL" },
{ GeneralSettings::ANTENNA_MODE_ASK, "MODE_ASK" },
{ GeneralSettings::ANTENNA_MODE_PER_MODEL, "MODE_PER_MODEL" },
{ GeneralSettings::ANTENNA_MODE_EXTERNAL, "MODE_EXTERNAL" },
{ GeneralSettings::ANTENNA_MODE_INTERNAL_EXTERNAL, "MODE_INTERNAL_EXTERNAL" },
};
const YamlLookupTable internalModuleLut = {
{ MODULE_TYPE_NONE, "TYPE_NONE" },
{ MODULE_TYPE_PPM, "TYPE_PPM" },
{ MODULE_TYPE_XJT_PXX1, "TYPE_XJT_PXX1" },
{ MODULE_TYPE_ISRM_PXX2, "TYPE_ISRM_PXX2" },
{ MODULE_TYPE_DSM2, "TYPE_DSM2" },
{ MODULE_TYPE_CROSSFIRE, "TYPE_CROSSFIRE" },
{ MODULE_TYPE_MULTIMODULE, "TYPE_MULTIMODULE" },
{ MODULE_TYPE_R9M_PXX1, "TYPE_R9M_PXX1" },
{ MODULE_TYPE_R9M_PXX2, "TYPE_R9M_PXX2" },
{ MODULE_TYPE_R9M_LITE_PXX1, "TYPE_R9M_LITE_PXX1" },
{ MODULE_TYPE_R9M_LITE_PXX2, "TYPE_R9M_LITE_PXX2" },
{ MODULE_TYPE_GHOST, "TYPE_GHOST" },
{ MODULE_TYPE_R9M_LITE_PRO_PXX2, "TYPE_R9M_LITE_PRO_PXX2" },
{ MODULE_TYPE_SBUS, "TYPE_SBUS" },
{ MODULE_TYPE_XJT_LITE_PXX2, "TYPE_XJT_LITE_PXX2" },
{ MODULE_TYPE_FLYSKY, "TYPE_FLYSKY" },
};
namespace YAML
{
ENUM_CONVERTER(GeneralSettings::BeeperMode, beeperModeLut);
Node convert<GeneralSettings>::encode(const GeneralSettings& rhs)
{
Node node;
std::string board = getCurrentFirmware()->getFlavour().toStdString();
node["board"] = board;
node["version"] = CPN_CURRENT_SETTINGS_VERSION;
YamlCalibData calib(rhs.calibMid, rhs.calibSpanNeg, rhs.calibSpanPos);
node["calib"] = calib;
node["currModel"] = rhs.currModelIndex;
node["currModelFilename"] = patchFilenameToYaml(rhs.currModelFilename);
node["contrast"] = rhs.contrast;
node["vBatWarn"] = rhs.vBatWarn;
node["txVoltageCalibration"] = rhs.txVoltageCalibration;
node["vBatMin"] = rhs.vBatMin + 90;
node["vBatMax"] = rhs.vBatMax + 120;
node["backlightMode"] = backlightModeLut << std::abs(rhs.backlightMode);
node["trainer"] = rhs.trainer;
node["view"] = rhs.view;
node["fai"] = (int)rhs.fai;
node["disableMemoryWarning"] = (int)rhs.disableMemoryWarning;
node["beepMode"] = rhs.beeperMode;
node["disableAlarmWarning"] = (int)rhs.disableAlarmWarning;
node["disableRssiPoweroffAlarm"] = (int)rhs.disableRssiPoweroffAlarm;
node["USBMode"] = rhs.usbMode;
node["jackMode"] = rhs.jackMode;
node["hapticMode"] = rhs.hapticMode;
node["stickMode"] = rhs.stickMode;
node["timezone"] = rhs.timezone;
node["adjustRTC"] = (int)rhs.adjustRTC;
node["inactivityTimer"] = rhs.inactivityTimer;
node["telemetryBaudrate"] = rhs.telemetryBaudrate; // TODO: conversion???
node["internalModule"] = LookupValue(internalModuleLut, rhs.internalModule);
node["splashMode"] = rhs.splashMode; // TODO: B&W only
node["lightAutoOff"] = rhs.backlightDelay;
node["templateSetup"] = rhs.templateSetup;
node["hapticLength"] = rhs.hapticLength + 2;
node["speakerPitch"] = rhs.speakerPitch * 15;
node["hapticStrength"] = rhs.hapticStrength + 2;
node["beepLength"] = rhs.beeperLength + 2;
node["gpsFormat"] = rhs.gpsFormat;
node["speakerVolume"] = rhs.speakerVolume + 12;
node["backlightBright"] = rhs.backlightBright;
node["switchesDelay"] = rhs.switchesDelay;
node["globalTimer"] = rhs.globalTimer;
node["bluetoothName"] = rhs.bluetoothName;
node["bluetoothBaudrate"] = rhs.bluetoothBaudrate;
node["bluetoothMode"] = bluetoothModeLut << rhs.bluetoothMode;
node["countryCode"] = rhs.countryCode;
node["jitterFilter"] = (int)rhs.jitterFilter;
node["disableRtcWarning"] = (int)rhs.rtcCheckDisable; // TODO: verify
node["keysBacklight"] = (int)rhs.keysBacklight;
node["imperial"] = rhs.imperial;
node["ttsLanguage"] = rhs.ttsLanguage;
node["beepVolume"] = rhs.beepVolume + 2;
node["wavVolume"] = rhs.wavVolume + 2;
node["varioVolume"] = rhs.varioVolume + 2;
node["varioPitch"] = rhs.varioPitch * 10;
node["varioRange"] = rhs.varioRange * 15;
node["varioRepeat"] = rhs.varioRepeat;
node["backgroundVolume"] = rhs.backgroundVolume + 2;
node["auxSerialMode"] = uartModeLut << rhs.auxSerialMode;
node["aux2SerialMode"] = uartModeLut << rhs.aux2SerialMode;
node["antennaMode"] = antennaModeLut << rhs.antennaMode;
node["backlightColor"] = rhs.backlightColor;
for (int i = 0; i < CPN_MAX_SPECIAL_FUNCTIONS; i++) {
const CustomFunctionData& fn = rhs.customFn[i];
if (!fn.isEmpty()) {
node["customFn"][std::to_string(i)] = fn;
}
}
Node sticksConfig;
sticksConfig = YamlStickConfig(rhs.stickName);
if (sticksConfig && sticksConfig.IsMap()) {
node["sticksConfig"] = sticksConfig;
}
Node switchConfig;
switchConfig = YamlSwitchConfig(rhs.switchName, rhs.switchConfig);
if (switchConfig && switchConfig.IsMap()) {
node["switchConfig"] = switchConfig;
}
Node potsConfig;
potsConfig = YamlPotConfig(rhs.potName, rhs.potConfig);
if (potsConfig && potsConfig.IsMap()) {
node["potsConfig"] = potsConfig;
}
Node slidersConfig;
slidersConfig = YamlSliderConfig(rhs.sliderName, rhs.sliderConfig);
if (slidersConfig && slidersConfig.IsMap()) {
node["slidersConfig"] = slidersConfig;
}
// Color lcd theme settings are not used in EdgeTx
// RadioTheme::ThemeData themeData;
node["ownerRegistrationID"] = rhs.registrationId;
// Gyro (for now only xlites)
node["gyroMax"] = rhs.gyroMax;
node["gyroOffset"] = rhs.gyroOffset;
// OneBit sampling (X9D only?)
node["uartSampleMode"] = rhs.uartSampleMode;
return node;
}
bool convert<GeneralSettings>::decode(const Node& node, GeneralSettings& rhs)
{
if (!node.IsMap()) return false;
// for (const auto& n : node) {
// qDebug() << QString::fromStdString(n.first.Scalar());
// }
node["version"] >> rhs.version;
// Decoding uses profile firmare therefore all conversions are performed on the fly
// So set board to firmware board
// However we should alert users that conversions may have occured
// If conversion should rename file with _converted suffix as done for bin
// Need to pass back messages and flags like converted so they can be handled in a more suitable section
rhs.variant = Board::BOARD_UNKNOWN;
std::string flavour;
node["board"] >> flavour;
auto fw = getCurrentFirmware();
auto msfw = Firmware::getFirmwareForFlavour(QString(flavour.c_str()));
if (flavour.empty()) {
qDebug() << "Warning: Settings file does not contain board flavour! Default firmware board will be used";
flavour = msfw->getFlavour().toStdString();
}
qDebug() << "Settings version:" << rhs.version << "File flavour:" << flavour.c_str() << "Firmware flavour:" << fw->getFlavour();
if (fw->getFlavour().toStdString() != flavour) {
QString prmpt = QCoreApplication::translate("YamlGeneralSettings", "File board (%1) does not match current profile board (%2), models and settings will be converted where possible.\n\nDo you wish to continue?").arg(Boards::getBoardName(msfw->getBoard())).arg(Boards::getBoardName(fw->getBoard()));
if (QMessageBox::question(NULL, CPN_STR_APP_NAME, prmpt, (QMessageBox::Yes | QMessageBox::No), QMessageBox::No) != QMessageBox::Yes) {
// TODO: this triggers an error in the calling code so we need a graceful way to handle
return false;
}
}
rhs.variant = fw->getBoard();
YamlCalibData calib;
node["calib"] >> calib;
calib.copy(rhs.calibMid, rhs.calibSpanNeg, rhs.calibSpanPos);
node["currModel"] >> rhs.currModelIndex;
node["currModelFilename"] >> rhs.currModelFilename;
node["contrast"] >> rhs.contrast;
node["vBatWarn"] >> rhs.vBatWarn;
node["txVoltageCalibration"] >> rhs.txVoltageCalibration;
node["vBatMin"] >> ioffset_int(rhs.vBatMin, 90);
node["vBatMax"] >> ioffset_int(rhs.vBatMax, 120);
node["backlightMode"] >> backlightModeLut >> rhs.backlightMode;
node["trainer"] >> rhs.trainer;
node["view"] >> rhs.view;
node["fai"] >> rhs.fai;
node["disableMemoryWarning"] >> rhs.disableMemoryWarning;
node["beepMode"] >> rhs.beeperMode;
node["disableAlarmWarning"] >> rhs.disableAlarmWarning;
node["disableRssiPoweroffAlarm"] >> rhs.disableRssiPoweroffAlarm;
node["USBMode"] >> rhs.usbMode;
node["jackMode"] >> rhs.jackMode;
node["hapticMode"] >> rhs.hapticMode;
node["stickMode"] >> rhs.stickMode;
node["timezone"] >> rhs.timezone;
node["adjustRTC"] >> rhs.adjustRTC;
node["inactivityTimer"] >> rhs.inactivityTimer;
node["telemetryBaudrate"] >> rhs.telemetryBaudrate; // TODO: conversion???
if (node["internalModule"]) {
node["internalModule"] >> internalModuleLut >> rhs.internalModule;
} else {
rhs.internalModule = Boards::getDefaultInternalModules(fw->getBoard());
}
node["splashMode"] >> rhs.splashMode; // TODO: B&W only
node["lightAutoOff"] >> rhs.backlightDelay;
node["templateSetup"] >> rhs.templateSetup;
node["hapticLength"] >> ioffset_int(rhs.hapticLength, 2);
node["speakerPitch"] >> ifactor<unsigned int>(rhs.speakerPitch, 15);
node["hapticStrength"] >> ioffset_int(rhs.hapticStrength, 2);
node["beepLength"] >> ioffset_int(rhs.beeperLength, 2);
node["gpsFormat"] >> rhs.gpsFormat;
node["speakerVolume"] >> ioffset_int(rhs.speakerVolume, 12);
node["backlightBright"] >> rhs.backlightBright;
node["switchesDelay"] >> rhs.switchesDelay;
node["globalTimer"] >> rhs.globalTimer;
node["bluetoothName"] >> rhs.bluetoothName;
node["bluetoothBaudrate"] >> rhs.bluetoothBaudrate;
node["bluetoothMode"] >> bluetoothModeLut >> rhs.bluetoothMode;
node["countryCode"] >> rhs.countryCode;
node["jitterFilter"] >> rhs.jitterFilter;
node["disableRtcWarning"] >> rhs.rtcCheckDisable; // TODO: verify
node["keysBacklight"] >> rhs.keysBacklight;
node["imperial"] >> rhs.imperial;
node["ttsLanguage"] >> rhs.ttsLanguage;
node["beepVolume"] >> ioffset_int(rhs.beepVolume, 2);
node["wavVolume"] >> ioffset_int(rhs.wavVolume, 2);
node["varioVolume"] >> ioffset_int(rhs.varioVolume, 2);
node["varioPitch"] >> ifactor<int>(rhs.varioPitch, 10);
node["varioRange"] >> ifactor<int>(rhs.varioRange, 15);
node["varioRepeat"] >> rhs.varioRepeat;
node["backgroundVolume"] >> ioffset_int(rhs.backgroundVolume, 2);
node["auxSerialMode"] >> uartModeLut >> rhs.auxSerialMode;
node["aux2SerialMode"] >> uartModeLut >> rhs.aux2SerialMode;
node["antennaMode"] >> antennaModeLut >> rhs.antennaMode;
node["backlightColor"] >> rhs.backlightColor;
node["customFn"] >> rhs.customFn;
YamlStickConfig stickConfig;
node["sticksConfig"] >> stickConfig;
stickConfig.copy(rhs.stickName);
YamlSwitchConfig switchConfig;
node["switchConfig"] >> switchConfig;
switchConfig.copy(rhs.switchName, rhs.switchConfig);
YamlPotConfig potsConfig;
node["potsConfig"] >> potsConfig;
potsConfig.copy(rhs.potName, rhs.potConfig);
YamlSliderConfig slidersConfig;
node["slidersConfig"] >> slidersConfig;
slidersConfig.copy(rhs.sliderName, rhs.sliderConfig);
// Color lcd theme settings are not used in EdgeTx
// RadioTheme::ThemeData themeData;
node["ownerRegistrationID"] >> rhs.registrationId;
// Gyro (for now only xlites)
node["gyroMax"] >> rhs.gyroMax;
node["gyroOffset"] >> rhs.gyroOffset;
// OneBit sampling (X9D only?)
node["uartSampleMode"] >> rhs.uartSampleMode;
return true;
}
} // namespace YAML
@@ -0,0 +1,34 @@
/*
* 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_ops.h"
#include "generalsettings.h"
namespace YAML {
template<>
struct convert<GeneralSettings> {
static Node encode(const GeneralSettings& rhs);
static bool decode(const Node& node, GeneralSettings& rhs);
};
}
@@ -0,0 +1,188 @@
/*
* 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_logicalswitchdata.h"
#include "yaml_rawswitch.h"
#include "yaml_rawsource.h"
static const YamlLookupTable funcLut = {
{LS_FN_OFF, "FUNC_NONE"},
{LS_FN_VEQUAL, "FUNC_VEQUAL"},
{LS_FN_VALMOSTEQUAL, "FUNC_VALMOSTEQUAL"},
{LS_FN_VPOS, "FUNC_VPOS"},
{LS_FN_VNEG, "FUNC_VNEG"},
{LS_FN_APOS, "FUNC_APOS"},
{LS_FN_ANEG, "FUNC_ANEG"},
{LS_FN_AND, "FUNC_AND"},
{LS_FN_OR, "FUNC_OR"},
{LS_FN_XOR, "FUNC_XOR"},
{LS_FN_EDGE, "FUNC_EDGE"},
{LS_FN_EQUAL, "FUNC_EQUAL"},
{LS_FN_GREATER, "FUNC_GREATER"},
{LS_FN_LESS, "FUNC_LESS"},
{LS_FN_DPOS, "FUNC_DIFFEGREATER"},
{LS_FN_DAPOS, "FUNC_ADIFFEGREATER"},
{LS_FN_TIMER, "FUNC_TIMER"},
{LS_FN_STICKY, "FUNC_STICKY"},
};
static int timerValue2lsw(uint32_t t)
{
if (t < 20) {
return t - 129;
} else if (600) {
return t / 5 - 113;
} else {
return t / 10 - 53;
}
}
// val = [-129,-110] => [0,19] (step 1)
// val = [-109,6] => [20,595] (step 5)
// val = [7,122] => [600,1750] (step 10)
//
static int lswTimerValue(int val)
{
return (val < -109 ? 129+val : (val < 7 ? (113+val)*5 : (53+val)*10));
}
namespace YAML
{
Node convert<LogicalSwitchData>::encode(const LogicalSwitchData& rhs)
{
Node node;
node["func"] = funcLut << rhs.func;
std::string def;
switch (rhs.getFunctionFamily()) {
case LS_FAMILY_VBOOL:
case LS_FAMILY_STICKY: {
def += YamlRawSwitchEncode(RawSwitch(rhs.val1));
def += ",";
def += YamlRawSwitchEncode(RawSwitch(rhs.val2));
} break;
case LS_FAMILY_EDGE: {
def += YamlRawSwitchEncode(RawSwitch(rhs.val1));
def += ",";
def += std::to_string(lswTimerValue(rhs.val2));
def += ",";
def += std::to_string(lswTimerValue(rhs.val3));
} break;
case LS_FAMILY_VCOMP: {
def += YamlRawSourceEncode(RawSource(rhs.val1));
def += ",";
def += YamlRawSourceEncode(RawSource(rhs.val2));
} break;
case LS_FAMILY_TIMER: {
def += std::to_string(lswTimerValue(rhs.val1));
def += ",";
def += std::to_string(lswTimerValue(rhs.val2));
} break;
default: {
def += YamlRawSourceEncode(RawSource(rhs.val1));
def += ",";
def += std::to_string(rhs.val2);
} break;
}
node["def"] = def;
node["delay"] = rhs.delay;
node["duration"] = rhs.duration;
node["andsw"] = YamlRawSwitchEncode(RawSwitch(rhs.andsw));
return node;
}
bool convert<LogicalSwitchData>::decode(const Node& node,
LogicalSwitchData& rhs)
{
node["func"] >> funcLut >> rhs.func;
std::string def_str;
node["def"] >> def_str;
std::stringstream def(def_str);
switch (rhs.getFunctionFamily()) {
case LS_FAMILY_VBOOL:
case LS_FAMILY_STICKY: {
std::string sw_str;
getline(def, sw_str, ',');
rhs.val1 = YamlRawSwitchDecode(sw_str).toValue();
getline(def, sw_str);
rhs.val2 = YamlRawSwitchDecode(sw_str).toValue();
} break;
case LS_FAMILY_EDGE: {
std::string sw_str;
getline(def, sw_str, ',');
rhs.val1 = YamlRawSwitchDecode(sw_str).toValue();
int v2=0, v3=0;
def >> v2;
rhs.val2 = timerValue2lsw(v2);
def.ignore();
if (def.peek() == '<') {
rhs.val3 = -1;
} else if (def.peek() == '-') {
rhs.val3 = 0;
} else {
def >> v3;
rhs.val3 = timerValue2lsw(v3);
}
} break;
case LS_FAMILY_VCOMP: {
std::string src_str;
getline(def, src_str, ',');
rhs.val1 = YamlRawSourceDecode(src_str).toValue();
getline(def, src_str);
rhs.val2 = YamlRawSourceDecode(src_str).toValue();
} break;
case LS_FAMILY_TIMER: {
int v1=0, v2=0;
def >> v1;
rhs.val1 = timerValue2lsw(v1);
def.ignore();
def >> v2;
rhs.val2 = timerValue2lsw(v2);
} break;
default: {
std::string src_str;
getline(def, src_str, ',');
rhs.val1 = YamlRawSourceDecode(src_str).toValue();
def >> rhs.val2;
} break;
}
node["delay"] >> rhs.delay;
node["duration"] >> rhs.duration;
RawSwitch andsw;
node["andsw"] >> andsw;
rhs.andsw = andsw.toValue();
return true;
}
} // namespace YAML
@@ -0,0 +1,31 @@
/*
* 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_ops.h"
#include "logicalswitchdata.h"
namespace YAML
{
template <>
struct convert<LogicalSwitchData> {
static Node encode(const LogicalSwitchData& rhs);
static bool decode(const Node& node, LogicalSwitchData& rhs);
};
} // namespace YAML
@@ -0,0 +1,173 @@
/*
* 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_mixdata.h"
#include "yaml_rawswitch.h"
#include "yaml_rawsource.h"
#include "curvereference.h"
static const YamlLookupTable mixMultiplexLut = {
{ MLTPX_ADD, "ADD" },
{ MLTPX_MUL, "MUL" },
{ MLTPX_REP, "REPL" },
};
#define GVAR_SMALL 128
#define CPN_GV1 1024
int32_t YamlReadMixWeight(const YAML::Node& node)
{
std::string val = node.as<std::string>();
if ((val.size() >= 4)
&& (val[0] == '-')
&& (val[1] == 'G')
&& (val[2] == 'V')
&& (val[3] >= '1')
&& (val[3] <= '9')) {
return -10000 - std::stoi(val.substr(3));
}
if ((val.size() >= 3)
&& (val[0] == 'G')
&& (val[1] == 'V')
&& (val[2] >= '1')
&& (val[2] <= '9')) {
return 10000 + std::stoi(val.substr(2));
}
try {
return std::stoi(val);
} catch(...) {
throw YAML::TypedBadConversion<int>(node.Mark());
}
}
std::string YamlWriteMixWeight(int32_t sval)
{
if (sval < -10000) {
int n = -sval - 10000;
return std::string("-GV") + std::to_string(n);
} else if (sval > 10000) {
int n = sval - 10000;
return std::string("GV") + std::to_string(n);
}
return std::to_string(sval);
}
uint32_t YamlReadFlightModes(const YAML::Node& node)
{
std::string fm_str = node.as<std::string>();
uint32_t bits = 0;
uint32_t mask = 1;
for (uint32_t i = 0; i < fm_str.size(); i++) {
if (fm_str[i] == '1') bits |= mask;
mask <<= 1;
}
return bits;
}
std::string YamlWriteFlightModes(uint32_t val)
{
std::string fm_str;
// TODO: constant for number of flight modes
for (int i=0; i<9; i++) {
uint32_t bit = (val >> i) & 1;
fm_str += bit ? "1" : "0";
}
return fm_str;
}
static const YamlLookupTable dummy = {};
namespace YAML
{
ENUM_CONVERTER(MltpxValue, mixMultiplexLut);
Node convert<CurveReference>::encode(const CurveReference& rhs)
{
Node node;
node["type"] = (int)rhs.type;
node["value"] = YamlWriteMixWeight(rhs.value);
return node;
}
bool convert<CurveReference>::decode(const Node& node, CurveReference& rhs)
{
// no symbolic value defined in radio
int type = 0;
node["type"] >> type;
rhs.type = (CurveReference::CurveRefType)type;
if (node["value"]) {
rhs.value = YamlReadMixWeight(node["value"]);
}
return true;
}
Node convert<MixData>::encode(const MixData& rhs)
{
Node node;
node["destCh"] = rhs.destCh - 1;
node["srcRaw"] = rhs.srcRaw;
node["weight"] = YamlWriteMixWeight(rhs.weight);
node["swtch"] = rhs.swtch;
node["curve"] = rhs.curve;
node["delayUp"] = rhs.delayUp;
node["delayDown"] = rhs.delayDown;
node["speedUp"] = rhs.speedUp;
node["speedDown"] = rhs.speedDown;
node["carryTrim"] = rhs.carryTrim;
node["mltpx"] = rhs.mltpx;
node["mixWarn"] = rhs.mixWarn;
node["flightModes"] = YamlWriteFlightModes(rhs.flightModes);
node["offset"] = YamlWriteMixWeight(rhs.sOffset);
node["name"] = rhs.name;
return node;
}
bool convert<MixData>::decode(const Node& node, MixData& rhs)
{
node["destCh"] >> ioffset_int((int&)rhs.destCh, -1);
node["srcRaw"] >> rhs.srcRaw;
if (node["weight"]) {
rhs.weight = YamlReadMixWeight(node["weight"]);
}
node["swtch"] >> rhs.swtch;
node["curve"] >> rhs.curve;
node["delayUp"] >> rhs.delayUp;
node["delayDown"] >> rhs.delayDown;
node["speedUp"] >> rhs.speedUp;
node["speedDown"] >> rhs.speedDown;
node["carryTrim"] >> rhs.carryTrim;
node["mltpx"] >> rhs.mltpx;
node["mixWarn"] >> rhs.mixWarn;
if (node["flightModes"]) {
rhs.flightModes = YamlReadFlightModes(node["flightModes"]);
}
if (node["offset"]) {
rhs.sOffset = YamlReadMixWeight(node["offset"]);
}
node["name"] >> rhs.name;
return true;
}
}
@@ -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.
*/
#include "yaml_ops.h"
#include "mixdata.h"
int32_t YamlReadMixWeight(const YAML::Node& node);
std::string YamlWriteMixWeight(int32_t sval);
uint32_t YamlReadFlightModes(const YAML::Node& node);
std::string YamlWriteFlightModes(uint32_t val);
namespace YAML
{
template <>
struct convert<CurveReference> {
static Node encode(const CurveReference& rhs);
static bool decode(const Node& node, CurveReference& rhs);
};
template <>
struct convert<MixData> {
static Node encode(const MixData& rhs);
static bool decode(const Node& node, MixData& rhs);
};
} // namespace YAML
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
/*
* 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_ops.h"
#include "modeldata.h"
namespace YAML {
template <>
struct convert<TimerData> {
static Node encode(const TimerData& rhs);
static bool decode(const Node& node, TimerData& rhs);
};
template <>
struct convert<FlightModeData> {
static bool decode(const Node& node, FlightModeData& rhs);
};
template <>
struct convert<ModelData> {
static Node encode(const ModelData& rhs);
static bool decode(const Node& node, ModelData& rhs);
};
} // namespace YAML
@@ -0,0 +1,396 @@
/*
* 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_moduledata.h"
#include "moduledata.h"
#include "rawsource.h"
// type: TYPE_MULTIMODULE
// subType: 15,2
// channelsStart: 0
// channelsCount: 12
// failsafeMode: NOT_SET
// mod:
// multi:
// disableTelemetry: 0
// disableMapping: 0
// autoBindMode: 0
// lowPowerMode: 0
// optionValue: 0
// receiverTelemetryOff: 0
// receiverHigherChannels: 0
static const YamlLookupTable protocolLut = {
{ PULSES_OFF, "TYPE_NONE" },
{ PULSES_PPM, "TYPE_PPM" },
{ PULSES_PXX_XJT_X16, "TYPE_XJT_PXX1" },
{ PULSES_ACCESS_ISRM, "TYPE_ISRM_PXX2" },
{ PULSES_LP45, "TYPE_DSM2" },
{ PULSES_CROSSFIRE, "TYPE_CROSSFIRE" },
{ PULSES_MULTIMODULE, "TYPE_MULTIMODULE" },
{ PULSES_PXX_R9M, "TYPE_R9M_PXX1" },
{ PULSES_ACCESS_R9M, "TYPE_R9M_PXX2" },
{ PULSES_PXX_R9M_LITE, "TYPE_R9M_LITE_PXX1" },
{ PULSES_ACCESS_R9M_LITE, "TYPE_R9M_LITE_PXX2" },
{ PULSES_GHOST, "TYPE_GHOST" },
{ PULSES_ACCESS_R9M_LITE_PRO, "TYPE_R9M_LITE_PRO_PXX2" },
{ PULSES_SBUS, "TYPE_SBUS" },
{ PULSES_XJT_LITE_X16, "TYPE_XJT_LITE_PXX2" },
{ PULSES_AFHDS3, "TYPE_FLYSKY" },
};
static const YamlLookupTable xjtLut = {
{ 0, "D16" },
{ 1, "D8" },
{ 2, "LR12" },
};
static const YamlLookupTable isrmLut = {
{ 0, "ACCESS" },
{ 1, "D16" },
};
static const YamlLookupTable r9mLut = {
{ MODULE_SUBTYPE_R9M_FCC, "FCC" },
{ MODULE_SUBTYPE_R9M_EU, "EU" },
{ MODULE_SUBTYPE_R9M_EUPLUS, "EUPLUS" },
{ MODULE_SUBTYPE_R9M_AUPLUS, "AUPLUS" },
};
static const YamlLookupTable dsmLut = {
{ 0, "LP45" },
{ 1, "DSM2" },
{ 3, "DSMX" },
};
static const YamlLookupTable failsafeLut = {
{ FAILSAFE_NOT_SET, "NOT_SET" },
{ FAILSAFE_HOLD, "HOLD" },
{ FAILSAFE_CUSTOM, "CUSTOM" },
{ FAILSAFE_NOPULSES, "NOPULSES" },
{ FAILSAFE_RECEIVER, "RECEIVER" },
};
enum MMRFrskySubtypes {
MM_RF_FRSKY_SUBTYPE_D16,
MM_RF_FRSKY_SUBTYPE_D8,
MM_RF_FRSKY_SUBTYPE_D16_8CH,
MM_RF_FRSKY_SUBTYPE_V8,
MM_RF_FRSKY_SUBTYPE_D16_LBT,
MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH,
MM_RF_FRSKY_SUBTYPE_D8_CLONED,
MM_RF_FRSKY_SUBTYPE_D16_CLONED
};
// from radio/src/pulses/multi.cpp
static void convertMultiProtocolToEtx(int *protocol, int *subprotocol)
{
if (*protocol == 3 && *subprotocol == 0) {
*protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1;
*subprotocol = MM_RF_FRSKY_SUBTYPE_D8;
return;
}
if (*protocol == 3 && *subprotocol == 1) {
*protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1;
*subprotocol = MM_RF_FRSKY_SUBTYPE_D8_CLONED;
return;
}
if (*protocol == 25) {
*protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1;
*subprotocol = MM_RF_FRSKY_SUBTYPE_V8;
return;
}
if (*protocol == 15) {
*protocol = MODULE_SUBTYPE_MULTI_FRSKY + 1;
if (*subprotocol == 0)
*subprotocol = MM_RF_FRSKY_SUBTYPE_D16;
else if (*subprotocol == 1)
*subprotocol = MM_RF_FRSKY_SUBTYPE_D16_8CH;
else if (*subprotocol == 2)
*subprotocol = MM_RF_FRSKY_SUBTYPE_D16_LBT;
else if (*subprotocol == 3)
*subprotocol = MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH;
else if (*subprotocol == 4)
*subprotocol = MM_RF_FRSKY_SUBTYPE_D16_CLONED;
return;
}
if (*protocol >= 25)
*protocol -= 1;
if (*protocol >= 16)
*protocol -= 1;
}
void convertEtxProtocolToMulti(int *protocol, int *subprotocol)
{
// Special treatment for the FrSky entry...
if (*protocol == MODULE_SUBTYPE_MULTI_FRSKY + 1) {
if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D8) {
//D8
*protocol = 3;
*subprotocol = 0;
}
else if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D8_CLONED) {
//D8
*protocol = 3;
*subprotocol = 1;
}
else if (*subprotocol == MM_RF_FRSKY_SUBTYPE_V8) {
//V8
*protocol = 25;
*subprotocol = 0;
}
else {
*protocol = 15;
if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D16_8CH)
*subprotocol = 1;
else if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D16)
*subprotocol = 0; // D16
else if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D16_LBT)
*subprotocol = 2;
else if (*subprotocol == MM_RF_FRSKY_SUBTYPE_D16_LBT_8CH)
*subprotocol = 3;
else
*subprotocol = 4; // D16_CLONED
}
}
else {
// 15 for Multimodule is FrskyX or D16 which we map as a protocol of 3 (FrSky)
// all protos > frskyx are therefore also off by one
if (*protocol >= 15)
*protocol += 1;
// 25 is again a FrSky *protocol (FrskyV) so shift again
if (*protocol >= 25)
*protocol += 1;
}
}
static int exportPpmDelay(int delay) { return (delay - 300) / 50; }
static int importPpmDelay(int delay) { return 300 + 50 * delay; }
namespace YAML
{
Node convert<ModuleData>::encode(const ModuleData& rhs)
{
Node node;
int protocol = rhs.protocol;
int subtype = rhs.subType;
if (rhs.protocol >= PULSES_LP45 && rhs.protocol <= PULSES_DSMX) {
protocol = PULSES_LP45;
subtype = rhs.protocol - PULSES_LP45;
} else if (rhs.protocol == PULSES_ACCST_ISRM_D16 || rhs.protocol == PULSES_ACCESS_ISRM) {
protocol = PULSES_ACCESS_ISRM;
subtype = rhs.protocol - PULSES_ACCESS_ISRM;
} else if (rhs.protocol >= PULSES_PXX_XJT_X16 && rhs.protocol <= PULSES_PXX_XJT_LR12) {
protocol = PULSES_PXX_XJT_X16;
subtype = rhs.protocol - PULSES_PXX_XJT_X16;
} else if (rhs.protocol >= PULSES_XJT_LITE_X16 && rhs.protocol <= PULSES_XJT_LITE_LR12) {
protocol = PULSES_XJT_LITE_X16;
subtype = rhs.protocol - PULSES_XJT_LITE_X16;
}
node["type"] = LookupValue(protocolLut, protocol);
switch (protocol) {
case PULSES_PXX_XJT_X16:
case PULSES_XJT_LITE_X16:
node["subType"] = LookupValue(xjtLut, subtype);
break;
case PULSES_ACCESS_ISRM:
node["subType"] = LookupValue(isrmLut, subtype);
break;
case PULSES_PXX_R9M:
case PULSES_PXX_R9M_LITE:
node["subType"] = LookupValue(r9mLut, subtype);
break;
case PULSES_LP45:
node["subType"] = LookupValue(dsmLut, subtype);
break;
case PULSES_MULTIMODULE: {
int rfProtocol = rhs.multi.rfProtocol + 1;
int subType = rhs.subType;
convertEtxProtocolToMulti(&rfProtocol, &subType);
std::string st_str = std::to_string(rfProtocol);
st_str += ",";
st_str += std::to_string(subType);
node["subType"] = st_str;
} break;
}
node["channelsStart"] = rhs.channelsStart;
node["channelsCount"] = rhs.channelsCount;
node["failsafeMode"] = LookupValue(failsafeLut, rhs.failsafeMode);
Node mod;
switch (protocol) {
case PULSES_PXX_XJT_X16:
case PULSES_PXX_R9M:
case PULSES_PXX_R9M_LITE: {
Node pxx;
pxx["power"] = rhs.pxx.power;
// pxx["receiverTelemetryOff"] = rhs.pxx.receiverTelemetryOff;
// pxx["receiverHigherChannels"] = rhs.pxx.receiverHigherChannels;
pxx["antennaMode"] = rhs.pxx.antennaMode;
mod["pxx"] = pxx;
} break;
case PULSES_ACCESS_ISRM:
case PULSES_ACCESS_R9M:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
case PULSES_XJT_LITE_X16: {
Node pxx2;
pxx2["receivers"] = rhs.access.receivers;
pxx2["racingMode"] = rhs.access.racingMode;
for (int i=0; i<(int)rhs.access.receivers; i++) {
pxx2["receiverName"][std::to_string(i)] = rhs.access.receiverName[i];
}
mod["pxx2"] = pxx2;
} break;
case PULSES_MULTIMODULE: {
Node multi;;
multi["disableTelemetry"] = (int)rhs.multi.disableTelemetry;
multi["disableMapping"] = (int)rhs.multi.disableMapping;
multi["autoBindMode"] = (int)rhs.multi.autoBindMode;
multi["lowPowerMode"] = (int)rhs.multi.lowPowerMode;
multi["optionValue"] = rhs.multi.optionValue;
// multi["receiverTelemetryOff"] = (int)rhs.pxx.receiverTelemetryOff;
// multi["receiverHigherChannels"] = (int)rhs.pxx.receiverHigherChannels;
mod["multi"] = multi;
} break;
case PULSES_SBUS: {
Node sbus;
sbus["refreshRate"] = rhs.ppm.frameLength;
mod["sbus"] = sbus;
} break;
// TODO: afhds3, flysky
default: {
Node ppm;
ppm["delay"] = exportPpmDelay(rhs.ppm.delay);
ppm["pulsePol"] = (int)rhs.ppm.pulsePol;
ppm["outputType"] = (int)rhs.ppm.outputType;
ppm["frameLength"] = rhs.ppm.frameLength;
mod["ppm"] = ppm;
} break;
}
if (mod && mod.IsMap())
node["mod"] = mod;
return node;
}
bool convert<ModuleData>::decode(const Node& node, ModuleData& rhs)
{
node["type"] >> protocolLut >> rhs.protocol;
Node subType;
node["subType"] >> subType;
switch (rhs.protocol) {
case PULSES_PXX_XJT_X16:
case PULSES_XJT_LITE_X16: {
int subProto = 0;
subType >> xjtLut >> subProto;
rhs.protocol += subProto;
} break;
case PULSES_ACCESS_ISRM: {
int subProto = 0;
subType >> isrmLut >> subProto;
rhs.protocol += subProto;
} break;
case PULSES_PXX_R9M:
case PULSES_PXX_R9M_LITE: {
subType >> r9mLut >> rhs.subType;
} break;
case PULSES_LP45: {
int subProto = 0;
subType >> dsmLut >> subProto;
rhs.protocol += subProto;
} break;
case PULSES_MULTIMODULE: {
std::string st_str;
subType >> st_str;
if (!st_str.empty()) {
size_t pos = 0;
try {
int rfProtocol = std::stoi(st_str, &pos);
st_str = st_str.substr(pos + 1);
int rfSubType = std::stoi(st_str);
convertMultiProtocolToEtx(&rfProtocol, &rfSubType);
if (rfProtocol > 0) {
rhs.multi.rfProtocol = rfProtocol - 1;
rhs.subType = rfSubType;
}
} catch(...) {}
}
} break;
}
node["channelsStart"] >> rhs.channelsStart;
node["channelsCount"] >> rhs.channelsCount;
node["failsafeMode"] >> failsafeLut >> rhs.failsafeMode;
if (node["mod"]) {
const Node& mod = node["mod"];
if (mod["ppm"]) {
const Node& ppm = mod["ppm"];
ppm["delay"] >> rhs.ppm.delay;
rhs.ppm.delay = importPpmDelay(rhs.ppm.delay);
ppm["pulsePol"] >> rhs.ppm.pulsePol;
ppm["outputType"] >> rhs.ppm.outputType;
ppm["frameLength"] >> rhs.ppm.frameLength;
} else if (mod["multi"]) {
Node multi = mod["multi"];
multi["disableTelemetry"] >> rhs.multi.disableTelemetry;
multi["disableMapping"] >> rhs.multi.disableMapping;
multi["autoBindMode"] >> rhs.multi.autoBindMode;
multi["lowPowerMode"] >> rhs.multi.lowPowerMode;
multi["optionValue"] >> rhs.multi.optionValue;
// multi["receiverTelemetryOff"] >> rhs.pxx.receiverTelemetryOff;
// multi["receiverHigherChannels"] >> rhs.pxx.receiverHigherChannels;
} else if (mod["pxx"]) {
Node pxx = mod["pxx"];
pxx["power"] >> rhs.pxx.power;
// pxx["receiverTelemetryOff"] >> rhs.pxx.receiverTelemetryOff;
// pxx["receiverHigherChannels"] >> rhs.pxx.receiverHigherChannels;
pxx["antennaMode"] >> rhs.pxx.antennaMode;
} else if (mod["sbus"]) {
mod["sbus"]["refreshRate"] >> rhs.ppm.frameLength;
} else if (mod["pxx2"]) {
Node pxx2 = mod["pxx2"];
pxx2["receivers"] >> rhs.access.receivers;
pxx2["racingMode"] >> rhs.access.racingMode;
for (const auto& rx : pxx2["receiverName"]) {
rx >> rhs.access.receiverName;
}
} else if (mod["flysky"]) {
//TODO
} else if (mod["afhds3"]) {
//TODO
}
}
return true;
}
}
@@ -0,0 +1,31 @@
/*
* 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_ops.h"
#include "moduledata.h"
namespace YAML
{
template <>
struct convert<ModuleData> {
static Node encode(const ModuleData& rhs);
static bool decode(const Node& node, ModuleData& rhs);
};
} // namespace YAML
@@ -0,0 +1,83 @@
/*
* 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_ops.h"
YAML::Node operator >> (const YAML::Node& node, const YamlLookupTable& lut)
{
if (node && node.IsScalar()) {
std::string str = node.as<std::string>();
const auto& it =
find_if(lut.begin(), lut.end(), [=](const YamlLookupTableElmt& elmt) {
if (elmt.second == str) return true;
return false;
});
if (it != lut.end()) {
return YAML::Node(it->first);
}
return YAML::Node();
}
return node;
}
void operator >> (const YAML::Node& node, const ioffset_int& value)
{
if (node && node.IsScalar()) {
value.assign(node.as<int>());
}
}
template <>
void operator >> (const YAML::Node& node, bool& value)
{
if (node && node.IsScalar()) {
value = node.as<int>();
}
}
namespace YAML {
std::string LookupValue(const YamlLookupTable& lut, const int& value)
{
const auto& it =
find_if(lut.begin(), lut.end(), [=](const YamlLookupTableElmt& elmt) {
if (elmt.first == value) return true;
return false;
});
if (it != lut.end()) {
return it->second;
}
return std::string();
}
Node operator << (const YamlLookupTable& lut, const int& value)
{
std::string str = LookupValue(lut, value);
if (!str.empty()) {
return Node(str);
}
return Node();
}
}
+173
View File
@@ -0,0 +1,173 @@
/*
* 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 <yaml-cpp/yaml.h>
#include <algorithm>
#include <QString>
typedef std::pair<int, std::string> YamlLookupTableElmt;
typedef std::vector<YamlLookupTableElmt> YamlLookupTable;
class ioffset_int {
public:
ioffset_int(int& val, int offset)
: value(val), offset(offset)
{}
void assign(int newValue) const {
value = newValue - offset;
}
private:
int& value;
int offset;
};
template<typename T>
class ifactor {
public:
ifactor(T& val, int factor)
: value(val), factor(factor)
{}
void assign(T newValue) const {
value = newValue / factor;
}
private:
T& value;
T factor;
};
YAML::Node operator >> (const YAML::Node& node, const YamlLookupTable& lut);
void operator >> (const YAML::Node& node, const ioffset_int& value);
template<typename T>
void operator >> (const YAML::Node& node, const ifactor<T>& value)
{
if (node && node.IsScalar()) {
value.assign(node.as<T>());
}
}
template <typename T>
void operator >> (const YAML::Node& node, T& value)
{
if (node && !node.IsNull()) {
value = node.as<T>();
}
}
// type specialisation
template <> void operator >> (const YAML::Node& node, bool& value);
template <size_t N>
void operator >> (const YAML::Node& node, char (&value)[N])
{
if (node && node.IsScalar()) {
auto str = node.as<std::string>();
str.copy(value, N-1);
value[std::min(str.size(), N-1)] = '\0';
}
}
template <typename T, size_t N>
void operator>>(const YAML::Node& node, T (&value)[N])
{
if (!node) return;
if (node.IsMap()) {
for (const auto& elmt : node) {
int idx = -1;
try {
idx = std::stoi(elmt.first.Scalar());
} catch (...) {
}
if (idx < 0 || idx >= (int)N) continue;
if (elmt.second.IsMap() && (elmt.second.size() == 1) &&
elmt.second["val"]) {
elmt.second["val"] >> value[idx];
} else {
elmt.second >> value[idx];
}
}
} else if (node.IsSequence()) {
int idx = 0;
for (const auto& elmt : node) {
elmt >> value[idx];
idx++;
}
}
}
namespace YAML {
std::string LookupValue(const YamlLookupTable& lut, const int& value);
Node operator << (const YamlLookupTable& lut, const int& value);
template <typename T, size_t N>
Node encode_array(const T (&rhs)[N])
{
Node node;
for (size_t i = 0; i < N; i++) {
Node elmt;
elmt = rhs[i];
node.push_back(elmt);
}
return node;
}
template <typename T>
struct convert_enum
{
static YAML::Node encode(const YamlLookupTable& lut, const T& rhs)
{
return lut << rhs;
}
static bool decode(const YAML::Node& node, const YamlLookupTable& lut, T& rhs)
{
if (node) {
YAML::Node conv = node >> lut;
if (conv.IsScalar()) {
rhs = (T)conv.as<int>();
}
}
return true;
}
};
}
#define ENUM_CONVERTER(enum_type, lut) \
template <> \
struct convert<enum_type> { \
static YAML::Node encode(const enum_type& rhs) \
{ \
return convert_enum<enum_type>::encode(lut,rhs); \
} \
static bool decode(const YAML::Node& node, enum_type& rhs) \
{ \
return convert_enum<enum_type>::decode(node, lut, rhs); \
} \
}
@@ -0,0 +1,254 @@
/*
* 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_rawsource.h"
#include "eeprominterface.h"
std::string YamlRawSourceEncode(const RawSource& rhs)
{
std::string src_str;
switch (rhs.type) {
case SOURCE_TYPE_VIRTUAL_INPUT:
src_str += "I" + std::to_string(rhs.index);
break;
case SOURCE_TYPE_LUA_OUTPUT:
src_str += "lua(";
src_str += std::to_string(rhs.index / 16);
src_str += ",";
src_str += std::to_string(rhs.index % 16);
src_str += ")";
break;
case SOURCE_TYPE_STICK:
src_str = getCurrentFirmware()->getAnalogInputTag(rhs.index);
break;
case SOURCE_TYPE_TRIM:
src_str = getCurrentFirmware()->getTrimSourcesTag(rhs.index);
break;
case SOURCE_TYPE_MAX:
src_str += "MAX";
break;
case SOURCE_TYPE_SWITCH:
src_str += "S";
src_str += (rhs.index + 'A');
break;
case SOURCE_TYPE_CUSTOM_SWITCH:
src_str += "ls(";
src_str += std::to_string(rhs.index);
src_str += ")";
break;
case SOURCE_TYPE_CYC:
src_str = getCurrentFirmware()->getRawSourceCyclicTag(rhs.index);
break;
case SOURCE_TYPE_PPM:
src_str += "tr(";
src_str += std::to_string(rhs.index);
src_str += ")";
break;
case SOURCE_TYPE_CH:
src_str += "ch(";
src_str += std::to_string(rhs.index);
src_str += ")";
break;
case SOURCE_TYPE_GVAR:
src_str += "gv(";
src_str += std::to_string(rhs.index);
src_str += ")";
break;
case SOURCE_TYPE_SPECIAL:
src_str = getCurrentFirmware()->getRawSourceSpecialTypesTag(rhs.index);
break;
case SOURCE_TYPE_TELEMETRY:
src_str = "tele(";
switch (rhs.index % 3) {
case 0:
break;
case 1:
src_str += '-';
break;
case 2:
src_str += '+';
break;
}
src_str += std::to_string(rhs.index / 3);
src_str += ")";
break;
default:
src_str = "NONE";
break;
}
return src_str;
}
RawSource YamlRawSourceDecode(const std::string& src_str)
{
RawSource rhs;
const char* val = src_str.data();
size_t val_len = src_str.size();
if (val_len > 1 && val[0] == 'I'
&& (val[1] >= '0') && (val[1] <= '9')) {
int idx = std::stoi(src_str.substr(1));
if (idx < CPN_MAX_INPUTS) {
rhs = RawSource(SOURCE_TYPE_VIRTUAL_INPUT, idx);
}
} else if (val_len >= 2
&& val[0] == 'S'
&& val[1] >= 'A'
&& val[1] <= 'Z') {
int idx = val[1] - 'A';
if (idx < CPN_MAX_SWITCHES)
rhs = RawSource(SOURCE_TYPE_SWITCH, idx);
} else if (val_len > 4 &&
val[0] == 'l' &&
val[1] == 'u' &&
val[2] == 'a' &&
val[3] == '(') {
std::stringstream src(src_str.substr(4));
int script = 0, output = 0; // TODO: check rename outputs to inputs???
src >> script;
src.ignore();
src >> output;
if (script < CPN_MAX_SCRIPTS && output < CPN_MAX_SCRIPT_INPUTS)
rhs = RawSource(SOURCE_TYPE_LUA_OUTPUT, script * 16 + output);
} else if (val_len > 3 &&
val[0] == 'l' &&
val[1] == 's' &&
val[2] == '(') {
std::stringstream src(src_str.substr(3));
int ls = 0;
src >> ls;
if (ls < CPN_MAX_LOGICAL_SWITCHES)
rhs = RawSource(SOURCE_TYPE_CUSTOM_SWITCH, ls);
} else if (val_len > 3 &&
val[0] == 't' &&
val[1] == 'r' &&
val[2] == '(') {
std::stringstream src(src_str.substr(3));
int tr = 0;
src >> tr;
if (tr < getCurrentFirmware()->getCapability(TrainerInputs))
rhs = RawSource(SOURCE_TYPE_PPM, tr);
} else if (val_len > 3 &&
val[0] == 'c' &&
val[1] == 'h' &&
val[2] == '(') {
std::stringstream src(src_str.substr(3));
int ch = 0;
src >> ch;
if (ch < CPN_MAX_CHNOUT)
rhs = RawSource(SOURCE_TYPE_CH, ch);
} else if (val_len > 3 &&
val[0] == 'g' &&
val[1] == 'v' &&
val[2] == '(') {
std::stringstream src(src_str.substr(3));
int gv = 0;
src >> gv;
if (gv < CPN_MAX_GVARS)
rhs = RawSource(SOURCE_TYPE_GVAR, gv);
} else if (val_len > 5 &&
val[0] == 't' &&
val[1] == 'e' &&
val[2] == 'l' &&
val[3] == 'e' &&
val[4] == '(') {
std::stringstream src(src_str.substr(5));
// parse sign
uint8_t sign = 0;
char c = src.peek();
if (c == '-') {
sign = 1;
src.ignore();
} else if (c == '+') {
sign = 2;
src.ignore();
}
int sensor = 0;
src >> sensor;
if (sensor < CPN_MAX_SENSORS)
rhs = RawSource(SOURCE_TYPE_TELEMETRY, sensor * 3 + sign);
} else {
YAML::Node node(src_str);
std::string ana_str;
node >> ana_str;
int ana_idx = getCurrentFirmware()->getAnalogInputIndex(ana_str.c_str());
if (ana_idx >= 0) {
rhs.type = SOURCE_TYPE_STICK;
rhs.index = ana_idx;
}
int trm_idx = getCurrentFirmware()->getTrimSourcesIndex(src_str.c_str());
if (trm_idx >= 0) {
rhs.type = SOURCE_TYPE_TRIM;
rhs.index = trm_idx;
}
int cyc_idx = getCurrentFirmware()->getRawSourceCyclicIndex(src_str.c_str());
if (cyc_idx >= 0) {
rhs.type = SOURCE_TYPE_CYC;
rhs.index = cyc_idx;
}
int sp_idx = getCurrentFirmware()->getRawSourceSpecialTypesIndex(src_str.c_str());
if (sp_idx >= 0) {
rhs.type = SOURCE_TYPE_SPECIAL;
rhs.index = sp_idx;
}
if (node.IsScalar() && node.as<std::string>() == "MAX") {
rhs.type = SOURCE_TYPE_MAX;
rhs.index = 0;
}
}
return rhs;
}
namespace YAML {
Node convert<RawSource>::encode(const RawSource& rhs)
{
return Node(YamlRawSourceEncode(rhs));
}
bool convert<RawSource>::decode(const Node& node, RawSource& rhs)
{
std::string src_str = node.Scalar();
rhs = YamlRawSourceDecode(src_str);
return true;
}
} // namespace YAML
@@ -0,0 +1,33 @@
/*
* 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_ops.h"
#include "rawsource.h"
std::string YamlRawSourceEncode(const RawSource& rhs);
RawSource YamlRawSourceDecode(const std::string& src_str);
namespace YAML {
template <>
struct convert<RawSource> {
static Node encode(const RawSource& rhs);
static bool decode(const Node& node, RawSource& rhs);
};
}
@@ -0,0 +1,178 @@
/*
* 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_rawswitch.h"
#include "eeprominterface.h"
std::string YamlRawSwitchEncode(const RawSwitch& rhs)
{
std::string sw_str;
int32_t sval = rhs.index;
if (rhs.index < 0) {
sval = -sval;
sw_str += "!";
}
int multiposcnt = Boards::getCapability(getCurrentBoard(), Board::MultiposPotsPositions);
switch (rhs.type) {
case SWITCH_TYPE_SWITCH:
sw_str += getCurrentFirmware()->getSwitchesTag((sval - 1) / 3);
sw_str += std::to_string((sval - 1) % 3);
break;
case SWITCH_TYPE_VIRTUAL:
sw_str += "L";
sw_str += std::to_string(sval);
break;
case SWITCH_TYPE_MULTIPOS_POT:
sw_str += "6P";
sw_str += std::to_string(sval / multiposcnt);
sw_str += std::to_string(sval % multiposcnt);
break;
case SWITCH_TYPE_TRIM:
sw_str += getCurrentFirmware()->getTrimSwitchesTag(sval);
break;
case SWITCH_TYPE_FLIGHT_MODE:
sw_str += "FM";
sw_str += std::to_string(sval - 1);
break;
case SWITCH_TYPE_SENSOR:
sw_str += "T";
sw_str += std::to_string(sval + 1);
break;
case SWITCH_TYPE_TIMER_MODE:
// TODO: check as it appears to be depreciated as not in RawSwitch data model and not in radio yaml export
break;
default:
sw_str += getCurrentFirmware()->getRawSwitchTypesTag(rhs.type);
break;
}
return sw_str;
}
RawSwitch YamlRawSwitchDecode(const std::string& sw_str)
{
RawSwitch rhs; // constructor sets to SWITCH_TYPE_NONE
const char* val = sw_str.data();
size_t val_len = sw_str.size();
std::string sw_str_tmp = sw_str;
// yaml-cpp does not escape this
if (val_len > 0 && val[0] == '\\') {
val++; val_len--;
sw_str_tmp = sw_str_tmp.substr(1);
}
bool neg = false;
if (val_len > 0 && val[0] == '!') {
neg = true;
val++;
val_len--;
sw_str_tmp = sw_str_tmp.substr(1);
}
int multiposcnt = Boards::getCapability(getCurrentBoard(), Board::MultiposPotsPositions);
// TODO: validate all expected numeric chars are numeric not just first
if (val_len >= 2 && val[0] == 'L' && (val[1] >= '0' && val[1] <= '9')) {
int sw_idx = std::stoi(sw_str_tmp.substr(1, val_len - 1));
if (sw_idx < CPN_MAX_LOGICAL_SWITCHES) {
rhs = RawSwitch(SWITCH_TYPE_VIRTUAL, sw_idx);
}
} else if (val_len > 3 && val[0] == '6' && val[1] == 'P' &&
(val[2] >= '0' && val[2] <= '9') &&
(val[3] >= '0' && val[3] < (multiposcnt + '0'))) {
rhs = RawSwitch(SWITCH_TYPE_MULTIPOS_POT,
(val[2] - '0') * multiposcnt + (val[3] - '0'));
} else if (val_len == 3 && val[0] == 'F' && val[1] == 'M' &&
(val[2] >= '0' && val[2] <= '9')) {
rhs = RawSwitch(SWITCH_TYPE_FLIGHT_MODE, val[2] - '0' + 1);
} else if (val_len >= 2 && val[0] == 'T' &&
(val[1] >= '0' && val[1] <= '9')) {
// starts at T1
int sensor_idx = std::stoi(sw_str_tmp.substr(1, val_len - 1));
if ((sensor_idx > 0) && (sensor_idx < CPN_MAX_SENSORS)) {
rhs = RawSwitch(SWITCH_TYPE_SENSOR, sensor_idx - 1);
}
} else if (sw_str_tmp.substr(0, 4) == std::string("Trim")) {
int tsw_idx = getCurrentFirmware()->getTrimSwitchesIndex(sw_str_tmp.c_str());
if (tsw_idx >= 0) {
rhs.type = SWITCH_TYPE_TRIM;
rhs.index = tsw_idx;
}
} else if (val_len >= 3 && val[0] == 'S' &&
(val[1] >= 'A' && val[1] <= 'Z') &&
(val[2] >= '0' && val[2] <= '2')) {
int sw_idx = getCurrentFirmware()->getSwitchesIndex(sw_str_tmp.substr(0, 2).c_str());
if (sw_idx >= 0) {
rhs.type = SWITCH_TYPE_SWITCH;
rhs.index = sw_idx * 3 + (val[2] - '0' + 1);
}
// TODO: SWITCH_TYPE_TIMER_MODE
// check as it appears to be depreciated as not in RawSwitch data model and not in radio yaml export
} else {
// types which do not use index
int sw_type = getCurrentFirmware()->getRawSwitchTypesIndex(sw_str_tmp.c_str());
if (sw_type >= 0) {
rhs.type = (RawSwitchType)sw_type;
rhs.index = 0;
}
}
if (neg) {
rhs.index = -rhs.index;
}
return rhs;
}
namespace YAML {
Node convert<RawSwitch>::encode(const RawSwitch& rhs)
{
return Node(YamlRawSwitchEncode(rhs));
}
bool convert<RawSwitch>::decode(const Node& node, RawSwitch& rhs)
{
rhs = YamlRawSwitchDecode(node.Scalar());
return true;
}
} // namespace YAML
@@ -0,0 +1,35 @@
/*
* 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_ops.h"
#include "rawswitch.h"
std::string YamlRawSwitchEncode(const RawSwitch& rhs);
RawSwitch YamlRawSwitchDecode(const std::string& sw_str);
namespace YAML {
template <>
struct convert<RawSwitch> {
static Node encode(const RawSwitch& rhs);
static bool decode(const Node& node, RawSwitch& rhs);
};
}
@@ -0,0 +1,145 @@
/*
* 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_screendata.h"
#include "yaml_rawsource.h"
#include "yaml_ops.h"
#include <sstream>
#include <iomanip>
static const YamlLookupTable zoneOptionValueEnum = {
{ZOV_Unsigned, "Unsigned"}, {ZOV_Signed, "Signed"}, {ZOV_Bool, "Bool"},
{ZOV_String, "String"}, {ZOV_Source, "Source"}, {ZOV_Color, "Color"},
};
std::string color_to_hex( unsigned int i )
{
std::stringstream stream;
stream << "0x"
<< std::setfill ('0') << std::setw(6)
<< std::hex << i;
return stream.str();
}
namespace YAML
{
ENUM_CONVERTER(ZoneOptionValueEnum, zoneOptionValueEnum);
Node convert<ZoneOptionValueTyped>::encode(const ZoneOptionValueTyped& rhs)
{
Node node, value;
node["type"] = rhs.type;
switch (rhs.type) {
case ZOV_Unsigned:
value["unsignedValue"] = rhs.value.unsignedValue;
break;
case ZOV_Signed:
value["signedValue"] = rhs.value.signedValue;
break;
case ZOV_Bool:
value["boolValue"] = rhs.value.boolValue;
break;
case ZOV_Source:
value["source"] = rhs.value.sourceValue;
break;
case ZOV_Color:
value["color"] = color_to_hex(rhs.value.colorValue);
break;
default:
break;
}
node["value"] = value;
return node;
}
bool convert<ZoneOptionValueTyped>::decode(const Node& node,
ZoneOptionValueTyped& rhs)
{
node["type"] >> rhs.type;
if (node["value"]) {
Node value = node["value"];
if (value.IsMap()) {
value["unsignedValue"] >> rhs.value.unsignedValue;
value["signedValue"] >> rhs.value.signedValue;
value["boolValue"] >> rhs.value.boolValue;
value["stringValue"] >> rhs.value.stringValue;
value["source"] >> rhs.value.sourceValue;
value["color"] >> rhs.value.colorValue;
}
}
return true;
}
template <>
struct convert<WidgetPersistentData> {
static Node encode(const WidgetPersistentData& rhs)
{
Node node;
for (int i=0; i<MAX_WIDGET_OPTIONS; i++) {
if (!rhs.options[i].isEmpty()) {
Node options;
options = rhs.options[i];
if (options && options.IsMap()) {
node["options"][std::to_string(i)] = options;
}
}
}
return node;
}
static bool decode(const Node& node, WidgetPersistentData& rhs)
{
node["options"] >> rhs.options;
return true;
}
};
Node convert<ZonePersistentData>::encode(const ZonePersistentData& rhs)
{
Node node;
node["widgetName"] = rhs.widgetName;
node["widgetData"] = rhs.widgetData;
return node;
}
bool convert<ZonePersistentData>::decode(const Node& node, ZonePersistentData& rhs)
{
node["widgetName"] >> rhs.widgetName;
node["widgetData"] >> rhs.widgetData;
return true;
}
Node convert<ScreenData>::encode(const ScreenData& rhs)
{
Node node;
node["LayoutId"] = rhs.layoutId;
node["layoutData"] = rhs.layoutPersistentData;
return node;
}
bool convert<ScreenData>::decode(const Node& node, ScreenData& rhs)
{
node["LayoutId"] >> rhs.layoutId;
node["layoutData"] >> rhs.layoutPersistentData;
return true;
}
} // namespace YAML
@@ -0,0 +1,72 @@
/*
* 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_ops.h"
#include "customisation_data.h"
namespace YAML
{
template <>
struct convert<ZonePersistentData> {
static Node encode(const ZonePersistentData& rhs);
static bool decode(const Node& node, ZonePersistentData& rhs);
};
template <>
struct convert<ZoneOptionValueTyped> {
static Node encode(const ZoneOptionValueTyped& rhs);
static bool decode(const Node& node, ZoneOptionValueTyped& rhs);
};
template <int N, int O>
struct convert<WidgetsContainerPersistentData<N,O> > {
typedef WidgetsContainerPersistentData<N,O> PersistentData;
static Node encode(const PersistentData& rhs)
{
Node node;
for (int i=0; i<N; i++) {
if (strlen(rhs.zones[i].widgetName) > 0) {
node["zones"][std::to_string(i)] = rhs.zones[i];
}
}
for(int i=0; i<O; i++) {
if (!rhs.options[i].isEmpty()) {
node["options"][std::to_string(i)] = rhs.options[i];
}
}
return node;
}
static bool decode(const Node& node, PersistentData& rhs)
{
node["zones"] >> rhs.zones;
node["options"] >> rhs.options;
return true;
}
};
typedef RadioLayout::CustomScreenData ScreenData;
template <>
struct convert<ScreenData> {
static Node encode(const ScreenData& rhs);
static bool decode(const Node& node, ScreenData& rhs);
};
} // namespace YAML
@@ -0,0 +1,184 @@
/*
* 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_sensordata.h"
static const YamlLookupTable sensorType = {
{SensorData::TELEM_TYPE_CUSTOM, "TYPE_CUSTOM"},
{SensorData::TELEM_TYPE_CALCULATED, "TYPE_CALCULATED"},
};
static const YamlLookupTable sensorFormula = {
{SensorData::TELEM_FORMULA_ADD, "FORMULA_ADD"},
{SensorData::TELEM_FORMULA_AVERAGE, "FORMULA_AVERAGE"},
{SensorData::TELEM_FORMULA_MIN, "FORMULA_MIN"},
{SensorData::TELEM_FORMULA_MAX, "FORMULA_MAX"},
{SensorData::TELEM_FORMULA_MULTIPLY, "FORMULA_MULTIPLY"},
{SensorData::TELEM_FORMULA_TOTALIZE, "FORMULA_TOTALIZE"},
{SensorData::TELEM_FORMULA_CELL, "FORMULA_CELL"},
{SensorData::TELEM_FORMULA_CONSUMPTION, "FORMULA_CONSUMPTION"},
{SensorData::TELEM_FORMULA_DIST, "FORMULA_DIST"},
};
namespace YAML
{
Node convert<SensorData>::encode(const SensorData& rhs)
{
Node node;
node["type"] = LookupValue(sensorType, rhs.type);
Node cfg;
if (rhs.type == SensorData::TELEM_TYPE_CUSTOM) {
node["id1"]["id"] = rhs.id;
node["subid"] = rhs.subid;
unsigned int instance = rhs.instance;
if (instance != 0) {
instance =
((instance - 1) & 0x1F) | (rhs.rxIdx << 5) | (rhs.moduleIdx << 7);
}
node["id2"]["instance"] = instance;
cfg["custom"]["ratio"] = rhs.ratio;
cfg["custom"]["offset"] = rhs.offset;
} else {
if (rhs.persistent) {
node["persistent"] = 1;
node["id1"]["persistentValue"] = rhs.persistentValue;
}
node["id2"]["formula"] = LookupValue(sensorFormula, rhs.formula);
switch (rhs.formula) {
case SensorData::TELEM_FORMULA_CELL:
cfg["cell"]["source"] = rhs.source;
cfg["cell"]["index"] = rhs.index;
break;
case SensorData::TELEM_FORMULA_ADD:
case SensorData::TELEM_FORMULA_AVERAGE:
case SensorData::TELEM_FORMULA_MULTIPLY:
case SensorData::TELEM_FORMULA_MIN:
case SensorData::TELEM_FORMULA_MAX: {
Node sources;
for (int i = 0; i < 4; i++) {
if (rhs.sources[i]) {
sources[std::to_string(i)]["val"] = rhs.sources[i];
}
}
cfg["calc"]["sources"] = sources;
} break;
case SensorData::TELEM_FORMULA_DIST:
if (rhs.gps) {
cfg["dist"]["gps"] = rhs.gps;
}
if (rhs.alt) {
cfg["dist"]["alt"] = rhs.alt;
}
break;
case SensorData::TELEM_FORMULA_CONSUMPTION:
case SensorData::TELEM_FORMULA_TOTALIZE:
cfg["consumption"]["source"] = rhs.amps;
default:
break;
}
}
node["label"] = rhs.label;
node["unit"] = rhs.unit;
node["prec"] = rhs.prec;
node["autoOffset"] = (int)rhs.autoOffset;
node["filter"] = (int)rhs.filter;
node["logs"] = (int)rhs.logs;
node["persistent"] = (int)rhs.persistent;
node["onlyPositive"] = (int)rhs.onlyPositive;
if (cfg && cfg.IsMap()) {
node["cfg"] = cfg;
}
return node;
}
bool convert<SensorData>::decode(const Node& node, SensorData& rhs)
{
node["type"] >> sensorType >> rhs.type;
if (node["id1"]) {
Node id1 = node["id1"];
id1["id"] >> rhs.id;
id1["persistentValue"] >> rhs.persistentValue;
}
node["subid"] >> rhs.subid;
if (node["id2"]) {
Node id2 = node["id2"];
id2["instance"] >> rhs.instance;
id2["formula"] >> sensorFormula >> rhs.formula;
}
if (rhs.type == SensorData::TELEM_TYPE_CUSTOM) {
rhs.rxIdx = (rhs.instance >> 5) & 0x03; // 2 bits Rx idx
rhs.moduleIdx = (rhs.instance >> 7) & 0x1; // 1 bit module idx
rhs.instance = (rhs.instance & 0x1F) + 1; // 5 bits instance
}
node["label"] >> rhs.label;
node["unit"] >> rhs.unit;
node["prec"] >> rhs.prec;
node["autoOffset"] >> rhs.autoOffset;
node["filter"] >> rhs.filter;
node["logs"] >> rhs.logs;
node["persistent"] >> rhs.persistent;
node["onlyPositive"] >> rhs.onlyPositive;
if (node["cfg"]) {
Node cfg = node["cfg"];
if (cfg.IsMap()) {
if (cfg["custom"]) {
Node custom = cfg["custom"];
if (custom.IsMap()) {
custom["ratio"] >> rhs.ratio;
custom["offset"] >> rhs.offset;
}
} else if (cfg["cell"]) {
Node cell = cfg["cell"];
if (cell.IsMap()) {
cell["source"] >> rhs.source;
cell["index"] >> rhs.index;
}
} else if (cfg["calc"]) {
Node calc = cfg["calc"];
if (calc.IsMap()) {
calc["sources"] >> rhs.sources;
}
} else if (cfg["consumption"]) {
Node consumption = cfg["consumption"];
if (consumption.IsMap()) {
consumption["source"] >> rhs.amps;
}
} else if (cfg["dist"]) {
Node dist = cfg["dist"];
if (dist.IsMap()) {
dist["gps"] >> rhs.gps;
dist["alt"] >> rhs.alt;
}
}
}
}
return true;
}
} // namespace YAML
@@ -0,0 +1,31 @@
/*
* 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_ops.h"
#include "sensordata.h"
namespace YAML
{
template <>
struct convert<SensorData> {
static Node encode(const SensorData& rhs);
static bool decode(const Node& node, SensorData& rhs);
};
} // namespace YAML
@@ -0,0 +1,145 @@
/*
* 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_switchconfig.h"
#include "boards.h"
#include "eeprominterface.h"
const YamlLookupTable switchConfigLut = {
{Board::SWITCH_NOT_AVAILABLE, "none"},
{Board::SWITCH_TOGGLE, "toggle"},
{Board::SWITCH_2POS, "2pos"},
{Board::SWITCH_3POS, "3pos"},
};
const YamlLookupTable potConfigLut = {
{Board::POT_NONE, "none"},
{Board::POT_WITH_DETENT, "with_detent"},
{Board::POT_MULTIPOS_SWITCH, "multipos_switch"},
{Board::POT_WITHOUT_DETENT, "without_detent"},
};
const YamlLookupTable slidersLut = {
{0, "LS"},
{1, "RS"},
};
const YamlLookupTable sliderConfigLut = {
{Board::SLIDER_NONE, "none"},
{Board::SLIDER_WITH_DETENT, "with_detent"},
};
int YamlStickLookup::name2idx(const std::string& name)
{
try {
int idx = std::stoi(name);
if (idx < 4) {
return idx;
}
} catch(...) {}
return -1;
}
std::string YamlStickLookup::idx2name(unsigned int idx)
{
if (idx < 4) {
return std::to_string(idx);
}
return std::string();
}
int YamlSwitchLookup::name2idx(const std::string& name)
{
auto fw = getCurrentFirmware();
int idx = fw->getSwitchesIndex(name.c_str());
if (idx < 0) return -1;
return idx;
}
std::string YamlSwitchLookup::idx2name(unsigned int idx)
{
auto fw = getCurrentFirmware();
unsigned int switches = Boards::getCapability(fw->getBoard(), Board::Switches);
if (idx >= switches) return std::string();
return fw->getSwitchesTag(idx);
}
int YamlPotLookup::name2idx(const std::string& name)
{
auto fw = getCurrentFirmware();
int idx = fw->getAnalogInputIndex(name.c_str());
if (idx < 0) return idx;
int sticks = Boards::getCapability(fw->getBoard(), Board::Sticks);
if (idx < sticks) return -1;
idx -= sticks;
int pots = Boards::getCapability(fw->getBoard(), Board::Pots);
if (idx >= pots) return -1;
return idx;
}
std::string YamlPotLookup::idx2name(unsigned int idx)
{
auto fw = getCurrentFirmware();
unsigned int pots = Boards::getCapability(fw->getBoard(), Board::Pots);
if (idx >= pots) return std::string();
unsigned int sticks = Boards::getCapability(fw->getBoard(), Board::Sticks);
return fw->getAnalogInputTag(idx + sticks);
}
int YamlSliderLookup::name2idx(const std::string& name)
{
auto fw = getCurrentFirmware();
int idx = fw->getAnalogInputIndex(name.c_str());
if (idx < 0) return idx;
int sticks = Boards::getCapability(fw->getBoard(), Board::Sticks);
if (idx < sticks) return -1;
idx -= sticks;
int pots = Boards::getCapability(fw->getBoard(), Board::Pots);
if (idx < pots) return -1;
idx -= pots;
int sliders = Boards::getCapability(fw->getBoard(), Board::Sliders);
if (idx >= sliders) return -1;
return idx;
}
std::string YamlSliderLookup::idx2name(unsigned int idx)
{
auto fw = getCurrentFirmware();
unsigned int sliders = Boards::getCapability(fw->getBoard(), Board::Sliders);
if (idx >= sliders) return std::string();
unsigned int sticks = Boards::getCapability(fw->getBoard(), Board::Sticks);
unsigned int pots = Boards::getCapability(fw->getBoard(), Board::Pots);
return fw->getAnalogInputTag(idx + pots + sticks);
}
@@ -0,0 +1,162 @@
/*
* 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 "yaml_ops.h"
#include "generalsettings.h"
#include <string.h>
struct YamlStickLookup {
static int name2idx(const std::string& name);
static std::string idx2name(unsigned int idx);
};
template <unsigned int N, class name_lookup>
struct YamlNameConfig {
char name[N][HARDWARE_NAME_LEN + 1];
YamlNameConfig() { memset(name, 0, sizeof(name)); }
YamlNameConfig(const char in_names[N][HARDWARE_NAME_LEN + 1])
{
memcpy(name, in_names, sizeof(name));
}
void copy(char out_names[N][HARDWARE_NAME_LEN + 1])
{
memcpy(out_names, name, sizeof(name));
}
};
typedef YamlNameConfig<CPN_MAX_SWITCHES, YamlStickLookup> YamlStickConfig;
template <unsigned int N, class name_lookup, const YamlLookupTable& cfg_lut>
struct YamlKnobConfig : public YamlNameConfig<N, name_lookup> {
unsigned int config[N];
YamlKnobConfig() { memset(config, 0, sizeof(config)); }
YamlKnobConfig(const char in_names[N][HARDWARE_NAME_LEN + 1],
const unsigned int in_configs[N]) :
YamlNameConfig<N, name_lookup>(in_names)
{
memcpy(config, in_configs, sizeof(config));
}
void copy(char out_names[N][HARDWARE_NAME_LEN + 1],
unsigned int out_configs[N])
{
YamlNameConfig<N, name_lookup>::copy(out_names);
memcpy(out_configs, config, sizeof(config));
}
};
// SA:
// type: 3pos
// name:
struct YamlSwitchLookup {
static int name2idx(const std::string& name);
static std::string idx2name(unsigned int idx);
};
extern const YamlLookupTable switchConfigLut;
typedef YamlKnobConfig<CPN_MAX_SWITCHES, YamlSwitchLookup, switchConfigLut>
YamlSwitchConfig;
// S1:
// type: with_detent
// name:
struct YamlPotLookup {
static int name2idx(const std::string& name);
static std::string idx2name(unsigned int idx);
};
extern const YamlLookupTable potConfigLut;
typedef YamlKnobConfig<CPN_MAX_POTS, YamlPotLookup, potConfigLut> YamlPotConfig;
// S1:
// type: with_detent
// name:
struct YamlSliderLookup {
static int name2idx(const std::string& name);
static std::string idx2name(unsigned int idx);
};
extern const YamlLookupTable sliderConfigLut;
typedef YamlKnobConfig<CPN_MAX_SLIDERS, YamlSliderLookup, sliderConfigLut>
YamlSliderConfig;
namespace YAML
{
template <unsigned int N, class name_lookup>
struct convert<YamlNameConfig<N, name_lookup> > {
static Node encode(const YamlNameConfig<N, name_lookup>& rhs)
{
Node node;
for (unsigned int i=0; i<N; i++) {
std::string name = name_lookup::idx2name(i);
if (!name.empty() && rhs.name[i][0]) {
node[name]["name"] = rhs.name[i];
}
}
return node;
}
static bool decode(const Node& node, YamlNameConfig<N, name_lookup>& rhs)
{
if (!node.IsMap()) return false;
int idx = 0;
for (const auto& kv : node) {
idx = name_lookup::name2idx(kv.first.Scalar());
if (idx >= 0) {
kv.second["name"] >> rhs.name[idx];
}
}
return true;
}
};
template <unsigned int N, class name_lookup, const YamlLookupTable& cfg_lut>
struct convert<YamlKnobConfig<N, name_lookup, cfg_lut> > {
static Node encode(const YamlKnobConfig<N, name_lookup, cfg_lut>& rhs)
{
Node node;
for (unsigned int i=0; i<N; i++) {
std::string name = name_lookup::idx2name(i);
if (!name.empty() && (rhs.config[i] || rhs.name[i][0])) {
node[name]["type"] = cfg_lut << rhs.config[i];
node[name]["name"] = rhs.name[i];
}
}
return node;
}
static bool decode(const Node& node,
YamlKnobConfig<N, name_lookup, cfg_lut>& rhs)
{
if (!node.IsMap()) return false;
int idx = 0;
for (const auto& kv : node) {
idx = name_lookup::name2idx(kv.first.Scalar());
if (idx >= 0) {
kv.second["type"] >> cfg_lut >> rhs.config[idx];
kv.second["name"] >> rhs.name[idx];
}
}
return true;
}
};
} // namespace YAML
@@ -0,0 +1,80 @@
/*
* 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_trainerdata.h"
#include "generalsettings.h"
const YamlLookupTable trainerMixMode = {
{TrainerMix::TRN_MIX_MODE_OFF, "OFF"},
{TrainerMix::TRN_MIX_MODE_ADD, "ADD"},
{TrainerMix::TRN_MIX_MODE_SUBST, "REPL"},
};
namespace YAML
{
Node convert<TrainerMix>::encode(const TrainerMix& rhs)
{
Node node;
node["srcChn"] = rhs.src;
node["mode"] = trainerMixMode << rhs.mode;
node["studWeight"] = rhs.weight;
return node;
}
bool convert<TrainerMix>::decode(const Node& node, TrainerMix& rhs)
{
node["srcChn"] >> rhs.src;
node["mode"] >> trainerMixMode >> rhs.mode;
node["studWeight"] >> rhs.weight;
return true;
}
Node convert<TrainerData>::encode(const TrainerData& rhs)
{
Node node;
for (int i = 0; i < 4; i++) {
node["calib"][std::to_string(i)]["val"] = rhs.calib[i];
}
for (int i = 0; i < 4; i++) {
node["mix"][std::to_string(i)] = rhs.mix[i];
}
return node;
}
bool convert<TrainerData>::decode(const Node& node, TrainerData& rhs)
{
if (node["calib"]) {
const Node& calib = node["calib"];
for (int i = 0; i < 4; i++) {
calib[std::to_string(i)]["val"] >> rhs.calib[i];
}
}
if (node["mix"]) {
const Node& mix = node["mix"];
for (int i = 0; i < 4; i++) {
mix[std::to_string(i)] >> rhs.mix[i];
}
}
return true;
}
} // namespace YAML
@@ -0,0 +1,39 @@
/*
* 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_ops.h"
#include "generalsettings.h"
extern const YamlLookupTable trainerMixMode;
namespace YAML
{
template <>
struct convert<TrainerMix> {
static Node encode(const TrainerMix& rhs);
static bool decode(const Node& node, TrainerMix& rhs);
};
template <>
struct convert<TrainerData> {
static Node encode(const TrainerData& rhs);
static bool decode(const Node& node, TrainerData& rhs);
};
} // namespace YAML
@@ -178,3 +178,9 @@ void Firmware::addOptionsGroup(const OptionsGroup & options)
{
this->opts.append(options);
}
QString Firmware::getFlavour()
{
QStringList strl = id.split("-");
return strl.count() >= 2 ? strl.at(1) : QString();
}
+48 -3
View File
@@ -26,6 +26,7 @@
#include "radiodata.h"
#include "../../radio/src/definitions.h"
#include "simulatorinterface.h"
#include "datahelpers.h"
#include <QtCore>
#include <QStringList>
@@ -33,6 +34,7 @@
#include <QDebug>
#include <iostream>
#include <string>
const uint8_t modn12x3[4][4]= {
{1, 2, 3, 4},
@@ -163,7 +165,9 @@ enum Capability {
HasADCJitterFilter,
HasTelemetryBaudrate,
TopBarZones,
FunctionSwitches
FunctionSwitches,
HasModelsList,
HasFlySkyGimbals
};
class EEPROMInterface
@@ -258,6 +262,8 @@ enum EepromLoadErrors {
NUM_ERRORS
};
constexpr char FIRMWARE_ID_PREFIX[] = { "edgetx-" };
class Firmware
{
Q_DECLARE_TR_FUNCTIONS(Firmware)
@@ -285,8 +291,16 @@ class Firmware
board(board),
variantBase(0),
base(base),
eepromInterface(nullptr)
{ }
eepromInterface(nullptr),
analogInputNamesLookupTable(Boards::getAnalogNamesLookupTable(board)),
switchesLookupTable(Boards::getSwitchesLookupTable(board)),
trimSwitchesLookupTable(Boards::getTrimSwitchesLookupTable(board)),
trimSourcesLookupTable(Boards::getTrimSourcesLookupTable(board)),
rawSwitchTypesLookupTable(RawSwitch::getRawSwitchTypesLookupTable()),
rawSourceSpecialTypesLookupTable(RawSource::getSpecialTypesLookupTable()),
rawSourceCyclicLookupTable(RawSource::getCyclicLookupTable())
{
}
virtual ~Firmware() { }
@@ -393,6 +407,26 @@ class Firmware
currentVariant = value;
}
QString getFlavour();
static Firmware * getFirmwareForFlavour(const QString & flavour)
{
return getFirmwareForId(FIRMWARE_ID_PREFIX + flavour);
}
const StringTagMappingTable* getAnalogIndexNamesLookupTable()
{
return &analogInputNamesLookupTable;
}
STRINGTAGMAPPINGFUNCS(analogInputNamesLookupTable, AnalogInput);
STRINGTAGMAPPINGFUNCS(switchesLookupTable, Switches);
STRINGTAGMAPPINGFUNCS(trimSwitchesLookupTable, TrimSwitches);
STRINGTAGMAPPINGFUNCS(trimSourcesLookupTable, TrimSources);
STRINGTAGMAPPINGFUNCS(rawSwitchTypesLookupTable, RawSwitchTypes);
STRINGTAGMAPPINGFUNCS(rawSourceSpecialTypesLookupTable, RawSourceSpecialTypes);
STRINGTAGMAPPINGFUNCS(rawSourceCyclicLookupTable, RawSourceCyclic);
protected:
QString id;
QString name;
@@ -400,6 +434,16 @@ class Firmware
unsigned int variantBase;
Firmware * base;
EEPROMInterface * eepromInterface;
// used by YAML encode and decode
const StringTagMappingTable analogInputNamesLookupTable;
const StringTagMappingTable switchesLookupTable;
const StringTagMappingTable trimSwitchesLookupTable;
const StringTagMappingTable trimSourcesLookupTable;
const StringTagMappingTable rawSwitchTypesLookupTable;
const StringTagMappingTable rawSourceSpecialTypesLookupTable;
const StringTagMappingTable rawSourceCyclicLookupTable;
QList<const char *> languages;
//QList<const char *> ttslanguages;
OptionsList opts;
@@ -407,6 +451,7 @@ class Firmware
static QVector<Firmware *> registeredFirmwares;
static Firmware * defaultVariant;
static Firmware * currentVariant;
};
inline Firmware * getCurrentFirmware()
+8 -2
View File
@@ -54,14 +54,18 @@ bool GeneralSettings::switchSourceAllowedTaranis(int index) const
bool GeneralSettings::isPotAvailable(int index) const
{
if (index < 0 || index > Boards::getCapability(getCurrentBoard(), Board::Pots))
int numPots = Boards::getCapability(getCurrentBoard(), Board::Pots);
if (getCurrentFirmware()->getCapability(HasFlySkyGimbals))
numPots -= 2;
if (index < 0 || index >= numPots)
return false;
return potConfig[index] != Board::POT_NONE;
}
bool GeneralSettings::isSliderAvailable(int index) const
{
if (index < 0 || index > Boards::getCapability(getCurrentBoard(), Board::Sliders))
if (index < 0 || index >= Boards::getCapability(getCurrentBoard(), Board::Sliders))
return false;
return sliderConfig[index] != Board::SLIDER_NONE;
}
@@ -241,6 +245,8 @@ GeneralSettings::GeneralSettings()
}
}
internalModule = g.profile[g.sessionId()].defaultInternalModule();
const char * themeName = IS_FLYSKY_NV14(board) ? "FlySky" : "EdgeTX";
RadioTheme::init(themeName, themeData);
}
+3 -2
View File
@@ -161,6 +161,7 @@ class GeneralSettings {
int vBatMin;
int vBatMax;
int backlightMode;
unsigned int internalModule; // Introducted in EdgeTX 2.6 yaml only
TrainerData trainer;
unsigned int view; // main screen view // TODO enum
bool disableThrottleWarning;
@@ -231,8 +232,8 @@ class GeneralSettings {
char switchName[CPN_MAX_SWITCHES][HARDWARE_NAME_LEN + 1];
unsigned int switchConfig[CPN_MAX_SWITCHES];
char stickName[CPN_MAX_STICKS][HARDWARE_NAME_LEN + 1];
char potName[CPN_MAX_KNOBS][HARDWARE_NAME_LEN + 1];
unsigned int potConfig[CPN_MAX_KNOBS];
char potName[CPN_MAX_POTS][HARDWARE_NAME_LEN + 1];
unsigned int potConfig[CPN_MAX_POTS];
char sliderName[CPN_MAX_SLIDERS][HARDWARE_NAME_LEN + 1];
unsigned int sliderConfig[CPN_MAX_SLIDERS];
+2 -4
View File
@@ -63,8 +63,6 @@ class RSSIAlarmData {
}
};
#define CPN_MAX_SCRIPTS 9
#define CPN_MAX_SCRIPT_INPUTS 10
class ScriptData {
public:
ScriptData() { clear(); }
@@ -145,8 +143,8 @@ class ModelData {
unsigned int switchWarningEnable;
unsigned int thrTrimSwitch;
unsigned int potsWarningMode;
bool potsWarnEnabled[CPN_MAX_POTS];
int potsWarnPosition[CPN_MAX_POTS];
bool potsWarnEnabled[CPN_MAX_POTS + CPN_MAX_SLIDERS];
int potsWarnPosition[CPN_MAX_POTS + CPN_MAX_SLIDERS];
bool displayChecklist;
GVarData gvarData[CPN_MAX_GVARS];
+238 -4
View File
@@ -23,6 +23,11 @@
#include "multiprotocols.h"
#include "afhds3.h"
#include "radiodataconversionstate.h"
#include "compounditemmodels.h"
#include "generalsettings.h"
#include <QPair>
#include <QVector>
void ModuleData::convert(RadioDataConversionState & cstate)
{
@@ -149,7 +154,7 @@ QString ModuleData::indexToString(int index, Firmware * fw)
}
// static
QString ModuleData::protocolToString(unsigned protocol)
QString ModuleData::protocolToString(unsigned int protocol)
{
static const char * strings[] = {
"OFF",
@@ -159,8 +164,8 @@ QString ModuleData::protocolToString(unsigned protocol)
"LP45", "DSM2", "DSMX",
"PPM16", "PPMsim",
"FrSky XJT (D16)", "FrSky XJT (D8)", "FrSky XJT (LR12)", "FrSky DJT",
"TBS Crossfire",
"DIY Multiprotocol Module",
"Crossfire",
"Multi",
"FrSky R9M",
"FrSky R9M Lite",
"FrSky R9M Lite Pro",
@@ -171,7 +176,7 @@ QString ModuleData::protocolToString(unsigned protocol)
"FrSky ACCESS R9M Lite Pro",
"FrSky XJT lite (D16)", "FrSky XJT lite (D8)", "FrSky XJT lite (LR12)",
"AFHDS3",
"ImmersionRC Ghost"
"Ghost"
};
return CHECK_IN_ARRAY(strings, protocol);
@@ -262,3 +267,232 @@ int ModuleData::getMaxChannelCount()
}
return 8;
}
/*
If Companion did not combine the /radio/src/datastructs type and sub-type fields
this would be so much cleaner and, modules and protocols generally easier to maintain
Another case for refactoring ModuleData as a whole!
*/
// static
int ModuleData::getTypeFromProtocol(unsigned int protocol)
{
// must be kept in sync with opentxeeprom.h ProtocolsConversionTable
const QVector<QPair<int, int>>ProtocolTypeTable = {
{ PULSES_OFF, MODULE_TYPE_NONE },
{ PULSES_PPM, MODULE_TYPE_PPM },
{ PULSES_PXX_XJT_X16, MODULE_TYPE_XJT_PXX1 },
{ PULSES_PXX_XJT_D8, MODULE_TYPE_XJT_PXX1 },
{ PULSES_PXX_XJT_LR12, MODULE_TYPE_XJT_PXX1 },
{ PULSES_ACCESS_ISRM, MODULE_TYPE_ISRM_PXX2 },
{ PULSES_ACCST_ISRM_D16, MODULE_TYPE_ISRM_PXX2 },
{ PULSES_LP45, MODULE_TYPE_DSM2 },
{ PULSES_DSM2, MODULE_TYPE_DSM2 },
{ PULSES_DSMX, MODULE_TYPE_DSM2 },
{ PULSES_CROSSFIRE, MODULE_TYPE_CROSSFIRE },
{ PULSES_MULTIMODULE, MODULE_TYPE_MULTIMODULE },
{ PULSES_PXX_R9M, MODULE_TYPE_R9M_PXX1 },
{ PULSES_ACCESS_R9M, MODULE_TYPE_R9M_PXX2 },
{ PULSES_PXX_R9M_LITE, MODULE_TYPE_R9M_LITE_PXX1 },
{ PULSES_ACCESS_R9M_LITE, MODULE_TYPE_R9M_LITE_PXX2 },
{ PULSES_GHOST, MODULE_TYPE_GHOST },
{ PULSES_ACCESS_R9M_LITE_PRO, MODULE_TYPE_R9M_LITE_PRO_PXX2 },
{ PULSES_SBUS, MODULE_TYPE_SBUS },
{ PULSES_XJT_LITE_X16, MODULE_TYPE_XJT_LITE_PXX2 },
{ PULSES_XJT_LITE_D8, MODULE_TYPE_XJT_LITE_PXX2 },
{ PULSES_XJT_LITE_LR12, MODULE_TYPE_XJT_LITE_PXX2 },
{ PULSES_AFHDS3, MODULE_TYPE_FLYSKY }
};
QPair<int, int>elmt;
foreach (elmt, ProtocolTypeTable) {
if (elmt.first == (int)protocol)
return elmt.second;
}
return -1;
}
// static
int ModuleData::getSubTypeFromProtocol(unsigned int protocol)
{
if (protocol == PULSES_ACCST_ISRM_D16 || protocol == PULSES_ACCESS_ISRM)
return protocol - PULSES_ACCESS_ISRM;
else if (protocol >= PULSES_PXX_XJT_X16 && protocol <= PULSES_PXX_XJT_LR12)
return protocol - PULSES_PXX_XJT_X16;
else
return 0;
}
// static
QString ModuleData::typeToString(int type)
{
static const char * strings[] = {
"OFF",
"PPM",
"XJT",
"ISRM",
"DSM2",
"CRSF",
"MULTI",
"R9M",
"R9M ACCESS",
"R9MLite",
"R9ML ACCESS",
"GHST",
"R9MLP ACCESS",
"SBUS",
"XJT Lite",
"FLYSKY"
};
return CHECK_IN_ARRAY(strings, type);
}
AbstractStaticItemModel * ModuleData::internalModuleItemModel(int board)
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("moduledata.internalmodule");
auto modules = Boards::getSupportedInternalModules(board == Board::BOARD_UNKNOWN ? getCurrentBoard() : (Board::Type)board);
for(auto mod : modules) {
mdl->appendToItemList(typeToString(mod), mod);
}
mdl->loadItemList();
return mdl;
}
// static
bool ModuleData::isProtocolAvailable(int moduleidx, unsigned int protocol, GeneralSettings & settings)
{
if (protocol == PULSES_OFF)
return true;
Firmware *fw = getCurrentFirmware();
Board::Type board = fw->getBoard();
if (moduleidx == 0 && Boards::getCapability(board, Board::HasInternalModuleSupport))
return (int)settings.internalModule == getTypeFromProtocol(protocol);
QString id = fw->getId();
if (IS_HORUS_OR_TARANIS(board)) {
switch (moduleidx) {
case 1:
switch (protocol) {
case PULSES_OFF:
case PULSES_PPM:
return true;
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_D8:
case PULSES_PXX_XJT_LR12:
return !(IS_TARANIS_XLITES(board) || IS_TARANIS_X9LITE(board));
case PULSES_PXX_R9M:
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
case PULSES_SBUS:
case PULSES_MULTIMODULE:
case PULSES_CROSSFIRE:
case PULSES_AFHDS3:
case PULSES_GHOST:
return true;
case PULSES_ACCESS_R9M:
return IS_ACCESS_RADIO(board, id) || (IS_FAMILY_HORUS_OR_T16(board) && id.contains("externalaccessmod"));
case PULSES_PXX_R9M_LITE:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
case PULSES_XJT_LITE_X16:
case PULSES_XJT_LITE_D8:
case PULSES_XJT_LITE_LR12:
return (IS_TARANIS_XLITE(board) || IS_TARANIS_X9LITE(board) || IS_JUMPER_TLITE(board));
default:
return false;
}
case -1:
switch (protocol) {
case PULSES_PPM:
return true;
default:
return false;
}
default:
return false;
}
}
else if (IS_SKY9X(board)) {
switch (moduleidx) {
case 0:
switch (protocol) {
case PULSES_PPM:
case PULSES_PXX_XJT_X16:
case PULSES_PXX_XJT_D8:
case PULSES_PXX_XJT_LR12:
case PULSES_PXX_R9M:
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
case PULSES_SBUS:
case PULSES_MULTIMODULE:
return true;
default:
return false;
}
break;
case 1:
switch (protocol) {
case PULSES_PPM:
return true;
default:
return false;
}
break;
default:
return false;
}
}
else {
switch (protocol) {
case PULSES_PPM:
case PULSES_DSMX:
case PULSES_LP45:
case PULSES_DSM2:
// case PULSES_PXX_DJT: // Unavailable for now
case PULSES_PPM16:
case PULSES_PPMSIM:
return true;
default:
return false;
}
}
return false; // to avoid compiler warning
}
// static
AbstractStaticItemModel * ModuleData::protocolItemModel(GeneralSettings & settings)
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("moduledata.protocol");
for (int i = 0; i <= 1; i++) {
for (unsigned int j = 0; j < PULSES_PROTOCOL_LAST; j++) {
mdl->appendToItemList(protocolToString(j), j, isProtocolAvailable(i, j, settings), 0, i + 1/*flag cannot be 0*/);
}
}
mdl->loadItemList();
return mdl;
}
+32 -5
View File
@@ -18,8 +18,7 @@
* GNU General Public License for more details.
*/
#ifndef MODULEDATA_H
#define MODULEDATA_H
#pragma once
#include "constants.h"
@@ -27,6 +26,30 @@
class Firmware;
class RadioDataConversionState;
class AbstractStaticItemModel;
class GeneralSettings;
// from radio/src/pulses/module_constants.h
enum ModuleType {
MODULE_TYPE_NONE = 0,
MODULE_TYPE_PPM,
MODULE_TYPE_XJT_PXX1,
MODULE_TYPE_ISRM_PXX2,
MODULE_TYPE_DSM2,
MODULE_TYPE_CROSSFIRE,
MODULE_TYPE_MULTIMODULE,
MODULE_TYPE_R9M_PXX1, // R9M
MODULE_TYPE_R9M_PXX2, // R9M ACCESS
MODULE_TYPE_R9M_LITE_PXX1, //R9MLite
MODULE_TYPE_R9M_LITE_PXX2, //R9MLP
MODULE_TYPE_GHOST, // Replaces MODULE_TYPE_R9M_LITE_PRO_PXX1 which doesn't exist
MODULE_TYPE_R9M_LITE_PRO_PXX2,
MODULE_TYPE_SBUS,
MODULE_TYPE_XJT_LITE_PXX2,
MODULE_TYPE_FLYSKY, //no more protocols possible because of 4 bits value
MODULE_TYPE_COUNT,
MODULE_TYPE_MAX = MODULE_TYPE_COUNT - 1
};
enum PulsesProtocol {
PULSES_OFF,
@@ -219,10 +242,14 @@ class ModuleData {
QString subTypeToString(int type = -1) const;
QString powerValueToString(Firmware * fw) const;
static QString indexToString(int index, Firmware * fw);
static QString protocolToString(unsigned protocol);
static QString protocolToString(unsigned int protocol);
static QStringList powerValueStrings(enum PulsesProtocol protocol, int subType, Firmware * fw);
bool hasFailsafes(Firmware * fw) const;
int getMaxChannelCount();
static int getTypeFromProtocol(unsigned int protocol);
static int getSubTypeFromProtocol(unsigned int protocol);
static QString typeToString(int type);
static AbstractStaticItemModel * internalModuleItemModel(int board = -1);
static bool isProtocolAvailable(int moduleidx, unsigned int protocol, GeneralSettings & settings);
static AbstractStaticItemModel * protocolItemModel(GeneralSettings & settings);
};
#endif // MODULEDATA_H
+15 -11
View File
@@ -79,6 +79,8 @@ inline int MAX_POTS(Board::Type board, int version)
{
if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board))
return 3;
if (version <= 220 && (IS_HORUS_X10(board) || IS_FAMILY_T16(board)))
return 5;
if (IS_FAMILY_T12(board))
return 2;
return Boards::getCapability(board, Board::Pots);
@@ -95,7 +97,7 @@ inline int MAX_POTS_STORAGE(Board::Type board, int version)
{
if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board))
return 3;
if (version >= 219 && IS_FAMILY_HORUS_OR_T16(board))
if (version <= 220 && IS_FAMILY_HORUS_OR_T16(board) && !IS_FLYSKY_NV14(board))
return 5;
if (IS_FAMILY_T12(board))
return 2;
@@ -105,6 +107,8 @@ inline int MAX_POTS_STORAGE(Board::Type board, int version)
inline int MAX_POTS_SOURCES(Board::Type board, int version)
{
if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board))
return 3;
if (version <= 220 && (IS_HORUS_X10(board) || IS_FAMILY_T16(board)))
return 5;
if (IS_FAMILY_T12(board))
return 2;
@@ -115,6 +119,8 @@ inline int MAX_XPOTS(Board::Type board, int version)
{
if (version <= 218 && IS_FAMILY_HORUS_OR_T16(board))
return 3;
if (version <= 220 && (IS_HORUS_X10(board) || IS_FAMILY_T16(board)))
return 5;
if (IS_FAMILY_T12(board))
return 2;
return Boards::getCapability(board, Board::MultiposPots);
@@ -180,6 +186,7 @@ inline int MAX_MOUSE_ANALOG_SOURCES(Board::Type board, int version)
#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) && !IS_FLYSKY_NV14(board)) ? 2 : 0)
#define MAX_SOURCE_TYPE_SPECIAL(board, version) SOURCE_TYPE_SPECIAL_COUNT
inline int switchIndex(int i, Board::Type board, unsigned int version)
{
@@ -340,6 +347,8 @@ class SourcesConversionTable: public ConversionTable {
int offset = 0;
if (version <= 218 && IS_HORUS_X10(board) && i >= CPN_MAX_STICKS + MAX_POTS_STORAGE(board, version))
offset += 2;
if (version <= 220 && (IS_HORUS_X10(board) || IS_FAMILY_T16(board)) && i >= CPN_MAX_STICKS + MAX_POTS_STORAGE(board, version))
offset += 2;
addConversion(RawSource(SOURCE_TYPE_STICK, i + offset), val++);
}
@@ -376,12 +385,8 @@ class SourcesConversionTable: public ConversionTable {
if (!(flags & FLAG_NOTELEMETRY)) {
for (int i=0; i<MAX_GVARS(board, version); i++)
addConversion(RawSource(SOURCE_TYPE_GVAR, i), val++);
addConversion(RawSource(SOURCE_TYPE_SPECIAL, 0), val++); // BATT
addConversion(RawSource(SOURCE_TYPE_SPECIAL, 1), val++); // TIME
val += 5;
addConversion(RawSource(SOURCE_TYPE_SPECIAL, 2), val++); // Timer1
addConversion(RawSource(SOURCE_TYPE_SPECIAL, 3), val++); // Timer2
addConversion(RawSource(SOURCE_TYPE_SPECIAL, 4), val++); // Timer3
for (int i=0; i<MAX_SOURCE_TYPE_SPECIAL(board, version); i++)
addConversion(RawSource(SOURCE_TYPE_SPECIAL, i), val++);
for (int i=0; i<MAX_TELEMETRY_SENSORS(board, version)*3; ++i) {
addConversion(RawSource(SOURCE_TYPE_TELEMETRY, i), val++);
}
@@ -1275,10 +1280,6 @@ class CurvesField: public TransformedField {
curve->points[j].x = *cur++;
curve->points[curve->count-1].x = +100;
}
else {
for (int j=0; j<curve->count; j++)
curve->points[j].x = -100 + (200*i) / (curve->count-1);
}
qCDebug(eepromImport) << QString("imported curve: %3 points").arg(curve->count);
}
}
@@ -2947,6 +2948,9 @@ OpenTxGeneralData::OpenTxGeneralData(GeneralSettings & generalData, Board::Type
if (version <= 218 && IS_HORUS_X10(board) && (i == CPN_MAX_STICKS + 3)) {
input += 2;
}
if (version <= 220 && IS_FAMILY_HORUS_OR_T16(board) && (i == CPN_MAX_STICKS + 5)) { // make room for EXT3 and EXT4
input += 2;
}
internalField.Append(new SignedField<16>(this, generalData.calibMid[input]));
internalField.Append(new SignedField<16>(this, generalData.calibSpanNeg[input]));
internalField.Append(new SignedField<16>(this, generalData.calibSpanPos[input]));
@@ -59,59 +59,59 @@ const char * OpenTxEepromInterface::getName()
{
switch (board) {
case BOARD_JUMPER_T12:
return "OpenTX for Jumper T12";
return "EdgeTX for Jumper T12";
case BOARD_JUMPER_TLITE:
return "OpenTX for Jumper T-Lite";
return "EdgeTX for Jumper T-Lite";
case BOARD_JUMPER_TPRO:
return "OpenTX for Jumper T-Pro";
return "EdgeTX for Jumper T-Pro";
case BOARD_JUMPER_T16:
return "OpenTX for Jumper T16";
return "EdgeTX for Jumper T16";
case BOARD_JUMPER_T18:
return "OpenTX for Jumper T18";
return "EdgeTX for Jumper T18";
case BOARD_RADIOMASTER_TX16S:
return "OpenTX for Radiomaster TX16S";
return "EdgeTX for Radiomaster TX16S";
case BOARD_RADIOMASTER_TX12:
return "OpenTX for Radiomaster TX12";
return "EdgeTX for Radiomaster TX12";
case BOARD_RADIOMASTER_ZORRO:
return "OpenTX for Radiomaster Zorro";
return "EdgeTX for Radiomaster Zorro";
case BOARD_RADIOMASTER_T8:
return "OpenTX for Radiomaster T8";
return "EdgeTX for Radiomaster T8";
case BOARD_TARANIS_X9D:
return "OpenTX for FrSky Taranis X9D";
return "EdgeTX for FrSky Taranis X9D";
case BOARD_TARANIS_X9DP:
return "OpenTX for FrSky Taranis X9D+";
return "EdgeTX for FrSky Taranis X9D+";
case BOARD_TARANIS_X9DP_2019:
return "OpenTX for FrSky Taranis X9D+ 2019";
return "EdgeTX for FrSky Taranis X9D+ 2019";
case BOARD_TARANIS_X9E:
return "OpenTX for FrSky Taranis X9E";
return "EdgeTX for FrSky Taranis X9E";
case BOARD_TARANIS_X7:
return "OpenTX for FrSky Taranis X7";
return "EdgeTX for FrSky Taranis X7";
case BOARD_TARANIS_X7_ACCESS:
return "OpenTX for FrSky Taranis X7 Access";
return "EdgeTX for FrSky Taranis X7 Access";
case BOARD_TARANIS_X9LITES:
return "OpenTX for FrSky Taranis X9-Lite S";
return "EdgeTX for FrSky Taranis X9-Lite S";
case BOARD_TARANIS_X9LITE:
return "OpenTX for FrSky Taranis X9-Lite";
return "EdgeTX for FrSky Taranis X9-Lite";
case BOARD_TARANIS_XLITE:
return "OpenTX for FrSky Taranis X-Lite";
return "EdgeTX for FrSky Taranis X-Lite";
case BOARD_TARANIS_XLITES:
return "OpenTX for FrSky Taranis X-Lite S/Pro";
return "EdgeTX for FrSky Taranis X-Lite S/Pro";
case BOARD_SKY9X:
return "OpenTX for Sky9x board / 9X";
return "EdgeTX for Sky9x board / 9X";
case BOARD_9XRPRO:
return "OpenTX for 9XR-PRO";
return "EdgeTX for 9XR-PRO";
case BOARD_AR9X:
return "OpenTX for ar9x board / 9X";
return "EdgeTX for ar9x board / 9X";
case BOARD_HORUS_X12S:
return "OpenTX for FrSky Horus";
return "EdgeTX for FrSky Horus";
case BOARD_X10:
return "OpenTX for FrSky X10";
return "EdgeTX for FrSky X10";
case BOARD_X10_EXPRESS:
return "OpenTX for FrSky X10 Express";
return "EdgeTX for FrSky X10 Express";
case BOARD_FLYSKY_NV14:
return "OpenTX for FlySky NV14";
return "EdgeTX for FlySky NV14";
default:
return "OpenTX for an unknown board";
return "Board is unknown to EdgeTX";
}
}
@@ -254,7 +254,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
errors.set(HAS_WARNINGS);
}
else if (version_error == NOT_OPENTX) {
dbg << " not open9x";
dbg << " not OpenTX";
errors.set(version_error);
return errors.to_ulong();
}
@@ -286,7 +286,7 @@ unsigned long OpenTxEepromInterface::load(RadioData &radioData, const uint8_t *
uint8_t OpenTxEepromInterface::getLastDataVersion(Board::Type board)
{
return 220;
return 220; // Note: this is the last bin format before the switch to YAML introduced in Companion version 2.6
}
void OpenTxEepromInterface::showErrors(const QString & title, const QStringList & errors)
@@ -752,14 +752,17 @@ int OpenTxFirmware::getCapability(::Capability capability)
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;
return IS_HORUS_OR_TARANIS(board);
case HasTelemetryBaudrate:
return IS_HORUS_OR_TARANIS(board) ? true : false;
return IS_HORUS_OR_TARANIS(board);
case TopBarZones:
return getCapability(LcdWidth) > getCapability(LcdHeight) ? 4 : 2;
case FunctionSwitches:
return IS_JUMPER_TPRO(board) ? 6 : 0;
case HasModelsList:
return IS_FAMILY_HORUS_OR_T16(board);
case HasFlySkyGimbals:
return (IS_RADIOMASTER_TX16S(board) && id.contains("flyskygimbals"));
default:
return 0;
}
@@ -981,6 +984,13 @@ EepromLoadErrors OpenTxEepromInterface::checkVersion(unsigned int version)
// ALL: TimerData countdownStart invert value
// ALL: convert more fields from zchar to normal string
// COLORLCD: change CustomScreenData and TopBarPersistentData
return OLD_VERSION;
case 221:
// ALL: YAML storage
// X10PCB: add pots EXT3 and EXT4 (conversion required as shifts slider settings in multiple fields)
// RM TX16S: add Flysky gimbals firmware build option and repurpose EXT3 and EXT4 if selected
// ??: add source TX GPS
break;
default:
@@ -1270,25 +1280,28 @@ void addOpenTxArm9xOptions(OpenTxFirmware * firmware, bool dblkeys = true)
addOpenTxRfOptions(firmware, FLEX);
}
// TODO: refactoring OpenTxFirmware constructor - need to check if used elsewhere and how
#define FIRMWAREID(flavour) std::string(FIRMWARE_ID_PREFIX).append(flavour).c_str()
void registerOpenTxFirmwares()
{
OpenTxFirmware * firmware;
/* FrSky Taranis X9D+ board */
firmware = new OpenTxFirmware("opentx-x9d+", Firmware::tr("FrSky Taranis X9D+"), BOARD_TARANIS_X9DP);
firmware = new OpenTxFirmware(FIRMWAREID("x9d+"), Firmware::tr("FrSky Taranis X9D+"), BOARD_TARANIS_X9DP);
firmware->addOption("noras", Firmware::tr("Disable RAS (SWR)"));
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3);
/* FrSky Taranis X9D+ 2019 board */
firmware = new OpenTxFirmware("opentx-x9d+2019", Firmware::tr("FrSky Taranis X9D+ 2019"), BOARD_TARANIS_X9DP_2019);
firmware = new OpenTxFirmware(FIRMWAREID("x9d+2019"), Firmware::tr("FrSky Taranis X9D+ 2019"), BOARD_TARANIS_X9DP_2019);
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FrSky Taranis X9D board */
firmware = new OpenTxFirmware("opentx-x9d", Firmware::tr("FrSky Taranis X9D"), BOARD_TARANIS_X9D);
firmware = new OpenTxFirmware(FIRMWAREID("x9d"), Firmware::tr("FrSky Taranis X9D"), BOARD_TARANIS_X9D);
firmware->addOption("noras", Firmware::tr("Disable RAS (SWR)"));
firmware->addOption("haptic", Firmware::tr("Haptic module installed"));
addOpenTxTaranisOptions(firmware);
@@ -1296,7 +1309,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, EU + FLEX + AFHDS3);
/* FrSky Taranis X9E board */
firmware = new OpenTxFirmware("opentx-x9e", Firmware::tr("FrSky Taranis X9E"), BOARD_TARANIS_X9E);
firmware = new OpenTxFirmware(FIRMWAREID("x9e"), Firmware::tr("FrSky Taranis X9E"), BOARD_TARANIS_X9E);
firmware->addOption("shutdownconfirm", Firmware::tr("Confirmation before radio shutdown"));
firmware->addOption("horussticks", Firmware::tr("Horus gimbals installed (Hall sensors)"));
addOpenTxTaranisOptions(firmware);
@@ -1304,44 +1317,44 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, EU + FLEX);
/* FrSky X9-Lite board */
firmware = new OpenTxFirmware("opentx-x9lite", Firmware::tr("FrSky Taranis X9-Lite"), BOARD_TARANIS_X9LITE);
firmware = new OpenTxFirmware(FIRMWAREID("x9lite"), Firmware::tr("FrSky Taranis X9-Lite"), BOARD_TARANIS_X9LITE);
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FrSky X9-LiteS board */
firmware = new OpenTxFirmware("opentx-x9lites", Firmware::tr("FrSky Taranis X9-Lite S"), BOARD_TARANIS_X9LITES);
firmware = new OpenTxFirmware(FIRMWAREID("x9lites"), Firmware::tr("FrSky Taranis X9-Lite S"), BOARD_TARANIS_X9LITES);
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FrSky X7 board */
firmware = new OpenTxFirmware("opentx-x7", Firmware::tr("FrSky Taranis X7 / X7S"), BOARD_TARANIS_X7);
firmware = new OpenTxFirmware(FIRMWAREID("x7"), Firmware::tr("FrSky Taranis X7 / X7S"), BOARD_TARANIS_X7);
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, EU + FLEX);
/* FrSky X7 Access board */
firmware = new OpenTxFirmware("opentx-x7access", Firmware::tr("FrSky Taranis X7 / X7S Access"), BOARD_TARANIS_X7_ACCESS);
firmware = new OpenTxFirmware(FIRMWAREID("x7access"), Firmware::tr("FrSky Taranis X7 / X7S Access"), BOARD_TARANIS_X7_ACCESS);
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FrSky X-Lite S/PRO board */
firmware = new OpenTxFirmware("opentx-xlites", Firmware::tr("FrSky Taranis X-Lite S/PRO"), BOARD_TARANIS_XLITES);
firmware = new OpenTxFirmware(FIRMWAREID("xlites"), Firmware::tr("FrSky Taranis X-Lite S/PRO"), BOARD_TARANIS_XLITES);
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FrSky X-Lite board */
firmware = new OpenTxFirmware("opentx-xlite", Firmware::tr("FrSky Taranis X-Lite"), BOARD_TARANIS_XLITE);
firmware = new OpenTxFirmware(FIRMWAREID("xlite"), Firmware::tr("FrSky Taranis X-Lite"), BOARD_TARANIS_XLITE);
// firmware->addOption("stdr9m", Firmware::tr("Use JR-sized R9M module"));
addOpenTxTaranisOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, EU + FLEX);
/* FrSky X10 board */
firmware = new OpenTxFirmware("opentx-x10", Firmware::tr("FrSky Horus X10 / X10S"), BOARD_X10);
firmware = new OpenTxFirmware(FIRMWAREID("x10"), Firmware::tr("FrSky Horus X10 / X10S"), BOARD_X10);
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
@@ -1349,13 +1362,13 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, EU + FLEX);
/* FrSky X10 Express board */
firmware = new OpenTxFirmware("opentx-x10express", Firmware::tr("FrSky Horus X10 Express / X10S Express"), BOARD_X10_EXPRESS);
firmware = new OpenTxFirmware(FIRMWAREID("x10express"), Firmware::tr("FrSky Horus X10 Express / X10S Express"), BOARD_X10_EXPRESS);
addOpenTxFrskyOptions(firmware);
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
/* FrSky X12 (Horus) board */
firmware = new OpenTxFirmware("opentx-x12s", Firmware::tr("FrSky Horus X12S"), BOARD_HORUS_X12S);
firmware = new OpenTxFirmware(FIRMWAREID("x12s"), Firmware::tr("FrSky Horus X12S"), BOARD_HORUS_X12S);
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalaccess", Firmware::tr("Support for ACCESS internal module replacement"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
@@ -1364,7 +1377,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, EU + FLEX);
/* Jumper T12 board */
firmware = new OpenTxFirmware("opentx-t12", QCoreApplication::translate("Firmware", "Jumper T12 / T12 Pro"), BOARD_JUMPER_T12);
firmware = new OpenTxFirmware(FIRMWAREID("t12"), QCoreApplication::translate("Firmware", "Jumper T12 / T12 Pro"), BOARD_JUMPER_T12);
addOpenTxCommonOptions(firmware);
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
@@ -1375,7 +1388,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, FLEX);
/* Jumper T-Lite board */
firmware = new OpenTxFirmware("opentx-tlite", QCoreApplication::translate("Firmware", "Jumper T-Lite"), BOARD_JUMPER_TLITE);
firmware = new OpenTxFirmware(FIRMWAREID("tlite"), QCoreApplication::translate("Firmware", "Jumper T-Lite"), BOARD_JUMPER_TLITE);
addOpenTxCommonOptions(firmware);
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
@@ -1385,7 +1398,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, FLEX);
/* Jumper T-Pro board */
firmware = new OpenTxFirmware("opentx-tpro", QCoreApplication::translate("Firmware", "Jumper T-Pro"), BOARD_JUMPER_TPRO);
firmware = new OpenTxFirmware(FIRMWAREID("tpro"), QCoreApplication::translate("Firmware", "Jumper T-Pro"), BOARD_JUMPER_TPRO);
addOpenTxCommonOptions(firmware);
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
@@ -1395,7 +1408,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, FLEX);
/* Jumper T16 board */
firmware = new OpenTxFirmware("opentx-t16", Firmware::tr("Jumper T16 / T16+ / T16 Pro"), BOARD_JUMPER_T16);
firmware = new OpenTxFirmware(FIRMWAREID("t16"), Firmware::tr("Jumper T16 / T16+ / T16 Pro"), BOARD_JUMPER_T16);
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module"));
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
@@ -1404,7 +1417,7 @@ void registerOpenTxFirmwares()
registerOpenTxFirmware(firmware);
/* Radiomaster TX12 board */
firmware = new OpenTxFirmware("opentx-tx12", QCoreApplication::translate("Firmware", "Radiomaster TX12"), BOARD_RADIOMASTER_TX12);
firmware = new OpenTxFirmware(FIRMWAREID("tx12"), QCoreApplication::translate("Firmware", "Radiomaster TX12"), BOARD_RADIOMASTER_TX12);
addOpenTxCommonOptions(firmware);
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
@@ -1414,7 +1427,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, FLEX);
/* Radiomaster Zorro board */
firmware = new OpenTxFirmware("opentx-zorro", QCoreApplication::translate("Firmware", "Radiomaster Zorro"), Board::BOARD_RADIOMASTER_ZORRO);
firmware = new OpenTxFirmware(FIRMWAREID("zorro"), QCoreApplication::translate("Firmware", "Radiomaster Zorro"), Board::BOARD_RADIOMASTER_ZORRO);
addOpenTxCommonOptions(firmware);
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
@@ -1425,7 +1438,7 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, FLEX + AFHDS3);
/* Radiomaster T8 board */
firmware = new OpenTxFirmware("opentx-t8", QCoreApplication::translate("Firmware", "Radiomaster T8"), BOARD_RADIOMASTER_T8);
firmware = new OpenTxFirmware(FIRMWAREID("t8"), QCoreApplication::translate("Firmware", "Radiomaster T8"), BOARD_RADIOMASTER_T8);
addOpenTxCommonOptions(firmware);
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
@@ -1436,17 +1449,18 @@ void registerOpenTxFirmwares()
firmware->addOption("bindkey", Firmware::tr("Allow bind using bind key"));
/* Radiomaster TX16S board */
firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16S / SE / Hall / Masterfire"), BOARD_RADIOMASTER_TX16S);
firmware = new OpenTxFirmware(FIRMWAREID("tx16s"), Firmware::tr("Radiomaster TX16S / SE / Hall / Masterfire"), BOARD_RADIOMASTER_TX16S);
addOpenTxFrskyOptions(firmware);
addOpenTxRfOptions(firmware, FLEX);
static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module"));
static const Firmware::Option opt_internal_gps("internalgps", Firmware::tr("Support internal GPS"));
firmware->addOptionsGroup({opt_bt, opt_internal_gps});
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
firmware->addOption("flyskygimbals", Firmware::tr("Support hardware mod: FlySky Paladin EV Gimbals"));
registerOpenTxFirmware(firmware);
/* Jumper T18 board */
firmware = new OpenTxFirmware("opentx-t18", Firmware::tr("Jumper T18"), BOARD_JUMPER_T18);
firmware = new OpenTxFirmware(FIRMWAREID("t18"), Firmware::tr("Jumper T18"), BOARD_JUMPER_T18);
addOpenTxFrskyOptions(firmware);
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
firmware->addOption("externalaccessmod", Firmware::tr("Support hardware mod: R9M ACCESS"));
@@ -1454,30 +1468,30 @@ void registerOpenTxFirmwares()
addOpenTxRfOptions(firmware, FLEX);
/* FlySky NV14 board */
firmware = new OpenTxFirmware("opentx-nv14", QCoreApplication::translate("Firmware", "FlySky NV14"), BOARD_FLYSKY_NV14);
firmware = new OpenTxFirmware(FIRMWAREID("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);
firmware = new OpenTxFirmware(FIRMWAREID("9xrpro"), Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO);
addOpenTxArm9xOptions(firmware, false);
registerOpenTxFirmware(firmware, true);
/* ar9x board */
firmware = new OpenTxFirmware("opentx-ar9x", Firmware::tr("9X with AR9X board"), BOARD_AR9X);
firmware = new OpenTxFirmware(FIRMWAREID("ar9x"), Firmware::tr("9X with AR9X board"), BOARD_AR9X);
addOpenTxArm9xOptions(firmware, true);
//firmware->addOption("rtc", Firmware::tr("Optional RTC added"));
//firmware->addOption("volume", Firmware::tr("i2c volume control added"));
registerOpenTxFirmware(firmware, true);
/* Sky9x board */
firmware = new OpenTxFirmware("opentx-sky9x", Firmware::tr("9X with Sky9x board"), BOARD_SKY9X);
firmware = new OpenTxFirmware(FIRMWAREID("sky9x"), Firmware::tr("9X with Sky9x board"), BOARD_SKY9X);
addOpenTxArm9xOptions(firmware);
registerOpenTxFirmware(firmware, true);
Firmware::setDefaultVariant(Firmware::getFirmwareForId("opentx-tx16s"));
Firmware::setDefaultVariant(Firmware::getFirmwareForFlavour("tx16s"));
Firmware::setCurrentVariant(Firmware::getDefaultVariant());
}
+5 -3
View File
@@ -39,7 +39,8 @@ void RadioData::fixModelFilename(unsigned int index)
{
ModelData & model = models[index];
QString filename(model.filename);
bool ok = filename.endsWith(".bin");
const bool hasSDCard = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::HasSDCard);
bool ok = hasSDCard ? filename.endsWith(".yml") : filename.endsWith(".bin");
if (ok) {
if (filename.startsWith("model") && filename.mid(5, filename.length()-9).toInt() > 0) {
ok = false;
@@ -54,7 +55,7 @@ void RadioData::fixModelFilename(unsigned int index)
}
}
if (!ok) {
sprintf(model.filename, "model%d.bin", index+1);
sprintf(model.filename, "model%d.%s", index + 1, hasSDCard ? "yml" : "bin");
}
}
@@ -68,11 +69,12 @@ void RadioData::fixModelFilenames()
QString RadioData::getNextModelFilename()
{
const bool hasSDCard = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::HasSDCard);
char filename[sizeof(ModelData::filename)];
int index = 0;
bool found = true;
while (found) {
sprintf(filename, "model%d.bin", ++index);
sprintf(filename, "model%d.%s", ++index, hasSDCard ? "yml" : "bin");
found = false;
for (unsigned int i=0; i<models.size(); i++) {
if (strcmp(filename, models[i].filename) == 0) {
+2 -1
View File
@@ -12,7 +12,8 @@ class RadioDataConversionState;
class CategoryData {
public:
CategoryData(const char * name) {
strncpy(this->name, name, sizeof(CategoryData::name));
memset(this->name, 0, sizeof(CategoryData::name));
strncpy(this->name, name, sizeof(CategoryData::name)-1);
}
char name[15+1];
};
+47 -5
View File
@@ -96,6 +96,10 @@ RawSourceRange RawSource::getRange(const ModelData * model, const GeneralSetting
result.max = 24 * 60 * result.step - 60; // 23:59:00 with 1-minute resolution
result.unit = tr("s");
}
else if (index == 2) { //GPS
result.max = 30000;
result.min = -result.max;
}
else { // Timers 1 - 3
result.step = 1;
result.max = 9 * 60 * 60 - 1; // 8:59:59 (to match firmware)
@@ -137,7 +141,7 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
};
static const QString special[] = {
tr("Batt"), tr("Time"), tr("Timer1"), tr("Timer2"), tr("Timer3"),
tr("Batt"), tr("Time"), tr("GPS"), tr("Reserved1"), tr("Reserved2"), tr("Reserved3"), tr("Reserved4")
};
static const QString rotary[] = { tr("REa"), tr("REb") };
@@ -208,11 +212,11 @@ QString RawSource::toString(const ModelData * model, const GeneralSettings * con
return LimitData().nameToString(index);
case SOURCE_TYPE_SPECIAL:
if (index >= SOURCE_TYPE_SPECIAL_TIMER1_IDX && index <= SOURCE_TYPE_SPECIAL_TIMER1_IDX + CPN_MAX_TIMERS - 1) {
if (index >= SOURCE_TYPE_SPECIAL_FIRST_TIMER && index <= SOURCE_TYPE_SPECIAL_LAST_TIMER) {
if (model)
result = model->timers[index - SOURCE_TYPE_SPECIAL_TIMER1_IDX].nameToString(index - SOURCE_TYPE_SPECIAL_TIMER1_IDX);
result = model->timers[index - SOURCE_TYPE_SPECIAL_FIRST_TIMER].nameToString(index - SOURCE_TYPE_SPECIAL_FIRST_TIMER);
else
result = TimerData().nameToString(index - SOURCE_TYPE_SPECIAL_TIMER1_IDX);
result = TimerData().nameToString(index - SOURCE_TYPE_SPECIAL_FIRST_TIMER);
}
else
result = CHECK_IN_ARRAY(special, index);
@@ -289,7 +293,7 @@ bool RawSource::isSlider(int * sliderIndex, Board::Type board) const
bool RawSource::isTimeBased(Board::Type board) const
{
return (type == SOURCE_TYPE_SPECIAL && index > 0);
return (type == SOURCE_TYPE_SPECIAL && index >= SOURCE_TYPE_SPECIAL_FIRST_TIMER && index <= SOURCE_TYPE_SPECIAL_LAST_TIMER);
}
bool RawSource::isAvailable(const ModelData * const model, const GeneralSettings * const gs, Board::Type board) const
@@ -305,6 +309,9 @@ bool RawSource::isAvailable(const ModelData * const model, const GeneralSettings
if (type == SOURCE_TYPE_SWITCH && index >= b.getCapability(Board::Switches))
return false;
if (type == SOURCE_TYPE_SPECIAL && index >= SOURCE_TYPE_SPECIAL_FIRST_RESERVED && index <= SOURCE_TYPE_SPECIAL_LAST_RESERVED)
return false;
if (model) {
if (type == SOURCE_TYPE_VIRTUAL_INPUT && !model->isInputValid(index))
return false;
@@ -411,3 +418,38 @@ QStringList RawSource::getSwitchList(Boards board) const
}
return ret;
}
// static
StringTagMappingTable RawSource::getSpecialTypesLookupTable()
{
StringTagMappingTable tbl;
tbl.insert(tbl.end(), {
{std::to_string(SOURCE_TYPE_SPECIAL_TX_BATT), "TX_VOLTAGE"},
{std::to_string(SOURCE_TYPE_SPECIAL_TX_TIME), "TX_TIME"},
{std::to_string(SOURCE_TYPE_SPECIAL_TX_GPS), "TX_GPS"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED1), "RESERVED1"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED2), "RESERVED2"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED3), "RESERVED3"},
{std::to_string(SOURCE_TYPE_SPECIAL_RESERVED4), "RESERVED4"},
{std::to_string(SOURCE_TYPE_SPECIAL_TIMER1), "TIMER1"},
{std::to_string(SOURCE_TYPE_SPECIAL_TIMER2), "TIMER2"},
{std::to_string(SOURCE_TYPE_SPECIAL_TIMER3), "TIMER3"},
});
return tbl;
}
// static
StringTagMappingTable RawSource::getCyclicLookupTable()
{
StringTagMappingTable tbl;
tbl.insert(tbl.end(), {
{"0", "CYC1"},
{"1", "CYC2"},
{"2", "CYC3"},
});
return tbl;
}
+21 -1
View File
@@ -23,6 +23,7 @@
#include "boards.h"
#include "constants.h"
#include "datahelpers.h"
#include <QtCore>
@@ -178,8 +179,25 @@ enum RawSourceType {
MAX_SOURCE_TYPE
};
enum RawSourceTypeSpecial {
SOURCE_TYPE_SPECIAL_TX_BATT,
SOURCE_TYPE_SPECIAL_TX_TIME,
SOURCE_TYPE_SPECIAL_TX_GPS,
SOURCE_TYPE_SPECIAL_FIRST_RESERVED,
SOURCE_TYPE_SPECIAL_RESERVED1 = SOURCE_TYPE_SPECIAL_FIRST_RESERVED,
SOURCE_TYPE_SPECIAL_RESERVED2,
SOURCE_TYPE_SPECIAL_RESERVED3,
SOURCE_TYPE_SPECIAL_RESERVED4,
SOURCE_TYPE_SPECIAL_LAST_RESERVED = SOURCE_TYPE_SPECIAL_RESERVED4,
SOURCE_TYPE_SPECIAL_FIRST_TIMER,
SOURCE_TYPE_SPECIAL_TIMER1 = SOURCE_TYPE_SPECIAL_FIRST_TIMER,
SOURCE_TYPE_SPECIAL_TIMER2,
SOURCE_TYPE_SPECIAL_TIMER3,
SOURCE_TYPE_SPECIAL_LAST_TIMER = SOURCE_TYPE_SPECIAL_TIMER3,
SOURCE_TYPE_SPECIAL_COUNT
};
constexpr int SOURCE_TYPE_STICK_THR_IDX { 3 }; // TODO is there a function to determine index?
constexpr int SOURCE_TYPE_SPECIAL_TIMER1_IDX { 2 }; // TODO temp const until Timers own source type
class RawSourceRange
{
@@ -257,6 +275,8 @@ class RawSource {
void clear() { type = SOURCE_TYPE_NONE; index = 0; }
QStringList getStickList(Boards board) const;
QStringList getSwitchList(Boards board) const;
static StringTagMappingTable getSpecialTypesLookupTable();
static StringTagMappingTable getCyclicLookupTable();
bool operator == ( const RawSource & other) const {
return (this->type == other.type) && (this->index == other.index);
+25
View File
@@ -241,3 +241,28 @@ QStringList RawSwitch::getSwitchList(Boards board) const
}
return ret;
}
// static
StringTagMappingTable RawSwitch::getRawSwitchTypesLookupTable()
{
StringTagMappingTable tbl;
tbl.insert(tbl.end(), {
{std::to_string(SWITCH_TYPE_NONE), "NONE"},
{std::to_string(SWITCH_TYPE_SWITCH), "Sxn"},
{std::to_string(SWITCH_TYPE_VIRTUAL), "Ln"},
{std::to_string(SWITCH_TYPE_MULTIPOS_POT), "6Pn"},
{std::to_string(SWITCH_TYPE_TRIM), "Trim"},
{std::to_string(SWITCH_TYPE_ROTARY_ENCODER), "RE"},
{std::to_string(SWITCH_TYPE_ON), "ON"},
{std::to_string(SWITCH_TYPE_OFF), "OFF"},
{std::to_string(SWITCH_TYPE_ONE), "ONE"},
{std::to_string(SWITCH_TYPE_FLIGHT_MODE), "FMn"},
{std::to_string(SWITCH_TYPE_TIMER_MODE), "Timer"},
{std::to_string(SWITCH_TYPE_TELEMETRY), "TELEMETRY_STREAMING"},
{std::to_string(SWITCH_TYPE_SENSOR), "Tnn"},
{std::to_string(SWITCH_TYPE_ACT), "RADIO_ACTIVITY"},
});
return tbl;
}
+2
View File
@@ -22,6 +22,7 @@
#include "boards.h"
#include "constants.h"
#include "datahelpers.h"
#include <QtCore>
@@ -89,6 +90,7 @@ class RawSwitch {
bool isSet() const { return type != SWITCH_TYPE_NONE || index != 0; }
void clear() { type = SWITCH_TYPE_NONE; index = 0; }
QStringList getSwitchList(Boards board) const;
static StringTagMappingTable getRawSwitchTypesLookupTable();
bool operator== ( const RawSwitch& other) const {
return (this->type == other.type) && (this->index == other.index);
-1
View File
@@ -24,7 +24,6 @@
#include <QtCore>
constexpr int CPN_MAX_SENSORS { 60 };
constexpr int SENSOR_LABEL_LEN { 4 };
constexpr int SENSOR_ISCONFIGURABLE { 1 << 1 };
+1 -1
View File
@@ -65,7 +65,7 @@ void FrSkyData::clear()
fasOffset = 0;
for (int i=0; i<4; i++)
screens[i].clear();
varioSource = 2/*VARIO*/;
varioSource = 0;
blades = 2;
}
+5 -2
View File
@@ -35,9 +35,12 @@ void TimerData::clear()
swtch = RawSwitch(SWITCH_TYPE_NONE);
}
bool TimerData::isEmpty()
bool TimerData::isEmpty() const
{
return (swtch == RawSwitch(SWITCH_TYPE_NONE, 0) && mode == TIMERMODE_OFF && 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()
+1 -1
View File
@@ -88,7 +88,7 @@ class TimerData {
void convert(RadioDataConversionState & cstate);
void clear();
bool isEmpty();
bool isEmpty() const;
bool isModeOff();
QString nameToString(int index) const;
QString countdownBeepToString() const;
+2 -2
View File
@@ -266,7 +266,7 @@ void FlashEEpromDialog::on_burnButton_clicked()
backupFilename = generateProcessUniqueTempFileName("eeprom.bin");
}
if (!backupFilename.isEmpty()) {
if (!readEeprom(backupFilename, progressDialog.progress())) {
if (!readSettings(backupFilename, progressDialog.progress())) {
return;
}
}
@@ -297,7 +297,7 @@ void FlashEEpromDialog::on_burnButton_clicked()
}
// and write...
bool result = writeEeprom(filename, progressDialog.progress());
bool result = writeSettings(filename, progressDialog.progress());
if (!result && !progressDialog.isEmpty()) {
progressDialog.exec();
}
+3 -3
View File
@@ -307,7 +307,7 @@ void FlashFirmwareDialog::startFlash(const QString &filename)
backupPath=g.backupDir();
}
backupFilename = backupPath + "/backup-" + QDateTime().currentDateTime().toString("yyyy-MM-dd-HHmmss") + ".bin";
result = readEeprom(backupFilename, progressDialog.progress());
result = readSettings(backupFilename, progressDialog.progress());
sleep(2);
}
@@ -322,14 +322,14 @@ void FlashFirmwareDialog::startFlash(const QString &filename)
QMessageBox::warning(this, tr("Conversion failed"), tr("Cannot convert Models and Settings for use with this firmware, original data will be used"));
restoreFilename = backupFilename;
}
if (!writeEeprom(restoreFilename, progressDialog.progress())) {
if (!writeSettings(restoreFilename, progressDialog.progress())) {
QMessageBox::warning(this, tr("Restore failed"), tr("Could not restore Models and Settings to Radio. The models and settings data file can be found at: %1").arg(backupFilename));
}
}
progressDialog.progress()->setInfo(tr("Flashing done"));
}
progressDialog.exec();
if (isTempFileName(filename)) {
+9 -2
View File
@@ -25,15 +25,22 @@
CalibrationPanel::CalibrationPanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware):
GeneralPanel(parent, generalSettings, firmware)
{
int rows = Boards::getCapability(getCurrentBoard(), Board::MaxAnalogs);
Board::Type board = getCurrentBoard();
int rows = Boards::getCapability(board, Board::MaxAnalogs);
QStringList headerLabels;
headerLabels << "" << tr("Negative span") << tr("Mid value") << tr("Positive span");
TableLayout * tableLayout = new TableLayout(this, rows, headerLabels);
for (int i = 0; i < rows; i++) {
for (int i = 0, row = 0; i < rows; i++) {
int col = 0;
if (firmware->getCapability(HasFlySkyGimbals) &&
i >= (Boards::getCapability(board, Board::Sticks) + 5) &&
i < (Boards::getCapability(board, Board::Sticks) + 7))
continue;
row++;
QLabel * label = new QLabel(this);
label->setText(firmware->getAnalogInputName(i));
tableLayout->addWidget(i, col++, label);
+26 -9
View File
@@ -33,6 +33,7 @@
constexpr char FIM_SWITCHTYPE2POS[] {"Switch Type 2 Pos"};
constexpr char FIM_SWITCHTYPE3POS[] {"Switch Type 3 Pos"};
constexpr char FIM_INTERNALMODULES[] {"Internal Modules"};
HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels):
GeneralPanel(parent, generalSettings, firmware),
@@ -52,6 +53,9 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
int auxmodelid = editorItemModels->registerItemModel(GeneralSettings::auxSerialModeItemModel());
int baudmodelid = editorItemModels->registerItemModel(GeneralSettings::telemetryBaudrateItemModel());
id = editorItemModels->registerItemModel(ModuleData::internalModuleItemModel());
tabFilteredModels->registerItemModel(new FilteredItemModel(editorItemModels->getItemModel(id)), FIM_INTERNALMODULES);
grid = new QGridLayout(this);
int count;
int row = 0;
@@ -64,7 +68,8 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
}
count = Boards::getCapability(board, Board::Pots);
if (count) {
count -= firmware->getCapability(HasFlySkyGimbals) ? 2 : 0;
if (count > 0) {
for (int i = 0; i < count; i++) {
addPot(i, row);
}
@@ -126,6 +131,26 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
addParams(row, antennaMode);
}
if (Boards::getCapability(board, Board::HasInternalModuleSupport)) {
addLabel(tr("Internal module"), row, 0);
AutoComboBox *internalModule = new AutoComboBox(this);
internalModule->setModel(tabFilteredModels->getItemModel(FIM_INTERNALMODULES));
internalModule->setField(generalSettings.internalModule, this);
addParams(row, internalModule);
connect(internalModule, &AutoComboBox::currentDataChanged, this, [=] () {
QMessageBox::warning(this, CPN_STR_APP_NAME,
tr("ALERT: Check each model's internal module protocol to ensure it is compatible!"));
});
}
if (firmware->getCapability(HasTelemetryBaudrate)) {
addLabel(tr("Maximum Baud"), row, 0);
AutoComboBox *maxBaudRate = new AutoComboBox(this);
maxBaudRate->setModel(editorItemModels->getItemModel(baudmodelid));
maxBaudRate->setField(generalSettings.telemetryBaudrate, this);
addParams(row, maxBaudRate);
}
if (firmware->getCapability(HasAuxSerialMode)) {
QString lbl = "Serial Port";
if (IS_RADIOMASTER_TX16S(board))
@@ -162,14 +187,6 @@ HardwarePanel::HardwarePanel(QWidget * parent, GeneralSettings & generalSettings
addParams(row, sportPower);
}
if (firmware->getCapability(HasTelemetryBaudrate)) {
addLabel(tr("Maximum Baud"), row, 0);
AutoComboBox *maxBaudRate = new AutoComboBox(this);
maxBaudRate->setModel(editorItemModels->getItemModel(baudmodelid));
maxBaudRate->setField(generalSettings.telemetryBaudrate, this);
addParams(row, maxBaudRate);
}
if (firmware->getCapability(HastxCurrentCalibration)) {
addLabel(tr("Current Offset"), row, 0);
AutoSpinBox *txCurrentCalibration = new AutoSpinBox(this);
+25 -23
View File
@@ -157,7 +157,7 @@ MainWindow::MainWindow():
if (!str.isEmpty()) {
int fileType = getStorageType(str);
if (fileType==STORAGE_TYPE_EEPE || fileType==STORAGE_TYPE_EEPM || fileType==STORAGE_TYPE_BIN || fileType==STORAGE_TYPE_OTX) {
if (fileType==STORAGE_TYPE_EEPE || fileType==STORAGE_TYPE_EEPM || fileType==STORAGE_TYPE_BIN || fileType==STORAGE_TYPE_ETX) {
MdiChild * child = createMdiChild();
if (child->loadFile(str)) {
if (!(printing && model >= 0 && (getCurrentFirmware()->getCapability(Models) == 0 || model<getCurrentFirmware()->getCapability(Models)) && !printfilename.isEmpty())) {
@@ -897,24 +897,24 @@ void MainWindow::customizeSplash()
dialog->deleteLater();
}
void MainWindow::writeEeprom()
void MainWindow::writeSettings()
{
if (activeMdiChild())
activeMdiChild()->writeEeprom();
activeMdiChild()->writeSettings();
}
void MainWindow::readEeprom()
void MainWindow::readSettings()
{
Board::Type board = getCurrentBoard();
QString tempFile;
if (IS_FAMILY_HORUS_OR_T16(board))
tempFile = generateProcessUniqueTempFileName("temp.otx");
if (Boards::getCapability(board, Board::HasSDCard))
tempFile = generateProcessUniqueTempFileName("temp.etx");
else
tempFile = generateProcessUniqueTempFileName("temp.bin");
qDebug() << "MainWindow::readEeprom(): using temp file: " << tempFile;
qDebug() << "Reading models and settings into temp file: " << tempFile;
if (readEepromFromRadio(tempFile)) {
if (readSettingsFromRadio(tempFile)) {
MdiChild * child = createMdiChild();
child->newFile(false);
child->loadFile(tempFile, false);
@@ -933,10 +933,10 @@ bool MainWindow::readFirmwareFromRadio(const QString & filename)
return result;
}
bool MainWindow::readEepromFromRadio(const QString & filename)
bool MainWindow::readSettingsFromRadio(const QString & filename)
{
ProgressDialog progressDialog(this, tr("Read Models and Settings from Radio"), CompanionIcon("read_eeprom.png"));
bool result = ::readEeprom(filename, progressDialog.progress());
bool result = ::readSettings(filename, progressDialog.progress());
if (!result) {
if (!progressDialog.isEmpty()) {
progressDialog.exec();
@@ -974,7 +974,7 @@ void MainWindow::readBackup()
}
QString fileName = QFileDialog::getSaveFileName(this, tr("Save Radio Backup to File"), g.eepromDir(), EXTERNAL_EEPROM_FILES_FILTER);
if (!fileName.isEmpty()) {
if (!readEepromFromRadio(fileName))
if (!readSettingsFromRadio(fileName))
return;
}
}
@@ -1045,8 +1045,8 @@ void MainWindow::updateMenus()
saveAsAct->setEnabled(activeChild);
closeAct->setEnabled(activeChild);
compareAct->setEnabled(activeChild);
writeEepromAct->setEnabled(activeChild);
readEepromAct->setEnabled(true);
writeSettingsAct->setEnabled(activeChild);
readSettingsAct->setEnabled(true);
if (IS_FAMILY_HORUS_OR_T16(getCurrentBoard())) {
writeBUToRadioAct->setEnabled(false);
readBUToFileAct->setEnabled(false);
@@ -1199,8 +1199,8 @@ void MainWindow::retranslateUi(bool showMsg)
trAct(sdsyncAct, tr("Synchronize SD"), tr("SD card synchronization"));
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(writeSettingsAct, tr("Write Models and Settings To Radio"), tr("Write Models and Settings to Radio"));
trAct(readSettingsAct, 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"));
@@ -1256,8 +1256,8 @@ void MainWindow::createActions()
burnListAct = addAct("list.png", SLOT(burnList()));
readFlashAct = addAct("read_flash.png", SLOT(readFlash()));
writeFlashAct = addAct("write_flash.png", SLOT(writeFlash()));
writeEepromAct = addAct("write_eeprom.png", SLOT(writeEeprom()));
readEepromAct = addAct("read_eeprom.png", SLOT(readEeprom()));
writeSettingsAct = addAct("write_eeprom.png", SLOT(writeSettings()));
readSettingsAct = addAct("read_eeprom.png", SLOT(readSettings()));
burnConfigAct = addAct("configure.png", SLOT(burnConfig()));
writeBUToRadioAct = addAct("write_eeprom_file.png", SLOT(writeBackup()));
@@ -1347,8 +1347,8 @@ void MainWindow::createMenus()
settingsMenu->addAction(importSettingsAct);
burnMenu = menuBar()->addMenu("");
burnMenu->addAction(writeEepromAct);
burnMenu->addAction(readEepromAct);
burnMenu->addAction(writeSettingsAct);
burnMenu->addAction(readSettingsAct);
burnMenu->addSeparator();
burnMenu->addAction(writeBUToRadioAct);
burnMenu->addAction(readBUToFileAct);
@@ -1435,8 +1435,8 @@ void MainWindow::createToolBars()
burnToolBar = new QToolBar(this);
addToolBar( Qt::LeftToolBarArea, burnToolBar );
burnToolBar->setObjectName("Write");
burnToolBar->addAction(writeEepromAct);
burnToolBar->addAction(readEepromAct);
burnToolBar->addAction(writeSettingsAct);
burnToolBar->addAction(readSettingsAct);
burnToolBar->addSeparator();
burnToolBar->addAction(writeBUToRadioAct);
burnToolBar->addAction(readBUToFileAct);
@@ -1626,9 +1626,11 @@ int MainWindow::newProfile(bool loadProfile)
if (i == MAX_PROFILES) //Failed to find free slot
return -1;
Firmware *newfw = Firmware::getDefaultVariant();
g.profile[i].init();
g.profile[i].name(tr("New Radio"));
g.profile[i].fwType(Firmware::getDefaultVariant()->getId());
g.profile[i].name("New Radio");
g.profile[i].fwType(newfw->getId());
g.profile[i].defaultInternalModule(Boards::getDefaultInternalModules(newfw->getBoard()));
if (loadProfile) {
if (loadProfileId(i))
+5 -5
View File
@@ -103,8 +103,8 @@ class MainWindow : public QMainWindow
bool loadProfileId(const unsigned pid);
void loadProfile();
void logFile();
void writeEeprom();
void readEeprom();
void writeSettings();
void readSettings();
void writeFlash(QString fileToFlash="");
void readFlash();
void writeBackup();
@@ -156,7 +156,7 @@ class MainWindow : public QMainWindow
QMdiSubWindow * findMdiChild(const QString & fileName);
bool anyChildrenDirty();
bool readEepromFromRadio(const QString & filename);
bool readSettingsFromRadio(const QString & filename);
bool readFirmwareFromRadio(const QString & filename);
bool checkProfileRadioExists(int profId);
@@ -203,8 +203,8 @@ class MainWindow : public QMainWindow
QAction *changelogAct;
QAction *compareAct;
QAction *editSplashAct;
QAction *writeEepromAct;
QAction *readEepromAct;
QAction *writeSettingsAct;
QAction *readSettingsAct;
QAction *burnConfigAct;
QAction *burnListAct;
QAction *writeFlashAct;
+11 -8
View File
@@ -1320,7 +1320,7 @@ void MdiChild::newFile(bool createDefaults)
{
static int sequenceNumber = 1;
isUntitled = true;
curFile = QString("document%1.otx").arg(sequenceNumber++);
curFile = QString("document%1.etx").arg(sequenceNumber++);
updateTitle();
if (createDefaults && firmware->getCapability(Capability::HasModelCategories)) {
@@ -1345,6 +1345,7 @@ bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
setCurrentFile(filename);
}
// For etx files this will never be true as any conversion occurs when parsing file
if (!Boards::isBoardCompatible(storage.getBoard(), getCurrentBoard())) {
if (!convertStorage(storage.getBoard(), getCurrentBoard(), true))
return false;
@@ -1360,7 +1361,7 @@ bool MdiChild::loadFile(const QString & filename, bool resetCurrentFile)
bool MdiChild::save()
{
QFileInfo fi(curFile);
if (isUntitled || !fi.isWritable()) {
if (isUntitled || !fi.isWritable() || fi.suffix().toLower() != "etx") {
return saveAs(true);
}
else {
@@ -1375,7 +1376,7 @@ bool MdiChild::saveAs(bool isNew)
#ifdef __APPLE__
QString filter;
#else
QString filter(OTX_FILES_FILTER % YML_FILES_FILTER);
QString filter(ETX_FILES_FILTER % YML_FILES_FILTER);
#endif
QString fileName = QFileDialog::getSaveFileName(this, tr("Save As"), g.eepromDir() + "/" + fi.fileName(), filter);
@@ -1455,7 +1456,7 @@ void MdiChild::setCurrentFile(const QString & fileName)
void MdiChild::forceNewFilename(const QString & suffix, const QString & ext)
{
curFile.replace(QRegExp("\\.(eepe|bin|hex|otx)$"), suffix + "." + ext);
curFile.replace(QRegExp("\\.(eepe|bin|hex|otx|etx)$"), suffix + "." + ext);
}
bool MdiChild::convertStorage(Board::Type from, Board::Type to, bool newFile)
@@ -1528,7 +1529,7 @@ int MdiChild::askQuestion(const QString & msg, QMessageBox::StandardButtons butt
return QMessageBox::question(this, CPN_STR_APP_NAME, msg, buttons, defaultButton);
}
void MdiChild::writeEeprom() // write to Tx
void MdiChild::writeSettings() // write to Tx
{
if (g.confirmWriteModelsAndSettings()) {
QMessageBox msgbox;
@@ -1547,19 +1548,21 @@ void MdiChild::writeEeprom() // write to Tx
}
Board::Type board = getCurrentBoard();
if (IS_FAMILY_HORUS_OR_T16(board)) {
if (Boards::getCapability(board, Board::HasSDCard)) {
QString radioPath = findMassstoragePath("RADIO", true);
qDebug() << "Searching for SD card, found" << radioPath;
if (radioPath.isEmpty()) {
qDebug() << "MdiChild::writeEeprom(): Horus radio not found";
qDebug() << "Radio SD card not found";
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Unable to find radio SD card!"));
return;
}
if (saveFile(radioPath, false)) {
emit newStatusMessage(tr("Models and Settings written"), 2000);
QMessageBox::information(this, CPN_STR_TTL_INFO, tr("Saved models and settings to radio"));
}
else {
qDebug() << "MdiChild::writeEeprom(): saveFile error";
QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Error saving models and settings to radio!"));
}
}
else {
+2 -2
View File
@@ -87,7 +87,7 @@ class MdiChild : public QWidget
bool saveAs(bool isNew=false);
bool saveFile(const QString & fileName, bool setCurrent=true);
void closeFile(bool force = false);
void writeEeprom();
void writeSettings();
void print(int model=-1, const QString & filename="");
void onFirmwareChanged();
@@ -179,7 +179,7 @@ class MdiChild : public QWidget
bool maybeSave();
void setCurrentFile(const QString & fileName);
void forceNewFilename(const QString & suffix = "", const QString & ext = "otx");
void forceNewFilename(const QString & suffix = "", const QString & ext = "etx");
bool convertStorage(Board::Type from, Board::Type to, bool newFile = false);
void showWarning(const QString & msg);
int askQuestion(const QString & msg, QMessageBox::StandardButtons buttons = (QMessageBox::Yes | QMessageBox::No), QMessageBox::StandardButton defaultButton = QMessageBox::No);
+3 -2
View File
@@ -106,11 +106,12 @@ ModelEdit::ModelEdit(QWidget * parent, RadioData & radioData, int modelId, Firmw
s1.report("Telemetry");
}
if (Boards::getCapability(firmware->getBoard(), Board::HasColorLcd)) {
// temporary hide
/*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)) {
else */if (firmware->getCapability(TelemetryCustomScreens)) {
addTab(new TelemetryCustomScreensPanel(this, model, generalSettings, firmware, sharedItemModels), tr("Custom Screens"));
s1.report("Telemetry Custom Screens");
}
+10 -11
View File
@@ -237,14 +237,12 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul
ui->trainerMode->hide();
}
// The protocols available on this board
for (unsigned int i = 0; i < PULSES_PROTOCOL_LAST; i++) {
if (firmware->isAvailable((PulsesProtocol) i, moduleIdx)) {
ui->protocol->addItem(ModuleData::protocolToString(i), i);
if (i == module.protocol)
ui->protocol->setCurrentIndex(ui->protocol->count() - 1);
}
if (panelFilteredItemModels && moduleIdx >= 0) {
int id = panelFilteredItemModels->registerItemModel(new FilteredItemModel(ModuleData::protocolItemModel(generalSettings), moduleIdx + 1/*flag cannot be 0*/), QString("Module Protocol %1").arg(moduleIdx));
ui->protocol->setModel(panelFilteredItemModels->getItemModel(id));
ui->protocol->setField(module.protocol, this);
}
for (int i = 0; i <= MODULE_SUBTYPE_MULTI_LAST; i++) {
if (i == MODULE_SUBTYPE_MULTI_SCANNER)
continue;
@@ -266,7 +264,7 @@ ModulePanel::ModulePanel(QWidget * parent, ModelData & model, ModuleData & modul
update();
connect(ui->protocol, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ModulePanel::onProtocolChanged);
connect(ui->protocol, &AutoComboBox::currentDataChanged, this, &ModulePanel::onProtocolChanged);
connect(ui->multiSubType, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ModulePanel::onSubTypeChanged);
connect(ui->multiProtocol, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &ModulePanel::onMultiProtocolChanged);
connect(this, &ModulePanel::channelsRangeChanged, this, &ModulePanel::setupFailsafes);
@@ -662,8 +660,7 @@ void ModulePanel::update()
void ModulePanel::onProtocolChanged(int index)
{
if (!lock && module.protocol != ui->protocol->itemData(index).toUInt()) {
module.protocol = ui->protocol->itemData(index).toInt();
if (!lock) {
module.channelsCount = module.getMaxChannelCount();
update();
emit updateItemModels();
@@ -1171,6 +1168,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());
panelFilteredModels->registerItemModel(new FilteredItemModel(ModelData::trainerModeItemModel(generalSettings, firmware)), FIM_TRAINERMODE);
Board::Type board = firmware->getBoard();
@@ -1320,6 +1318,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
Board::SwitchInfo switchInfo = Boards::getSwitchInfo(board, i);
switchInfo.config = Board::SwitchType(generalSettings.switchConfig[i]);
if (switchInfo.config == Board::SWITCH_NOT_AVAILABLE || switchInfo.config == Board::SWITCH_TOGGLE) {
model.switchWarningEnable |= (1 << i);
continue;
}
RawSource src(RawSourceType::SOURCE_TYPE_SWITCH, i);
@@ -1389,7 +1388,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
// ui->functionSwitchesLayout->hide();
for (int i = firmware->getCapability(NumFirstUsableModule); i < firmware->getCapability(NumModules); i++) {
modules[i] = new ModulePanel(this, model, model.moduleData[i], generalSettings, firmware, i);
modules[i] = new ModulePanel(this, model, model.moduleData[i], generalSettings, firmware, i, panelFilteredModels);
ui->modulesLayout->addWidget(modules[i]);
connect(modules[i], &ModulePanel::modified, this, &SetupPanel::modified);
connect(modules[i], &ModulePanel::updateItemModels, this, &SetupPanel::onModuleUpdateItemModels);
+1 -1
View File
@@ -805,7 +805,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="protocol">
<widget class="AutoComboBox" name="protocol">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
+2 -2
View File
@@ -651,7 +651,7 @@ void TreeModel::refresh()
EEPROMInterface * eepromInterface = getCurrentEEpromInterface();
Board::Type board = eepromInterface->getBoard();
TreeItem * defaultCategoryItem = NULL;
bool hasEepromSizeData = (IS_FAMILY_HORUS_OR_T16(board) ? false : true);
bool hasEepromSizeData = false; // not necessary with move to YAML storage for all boards TODO: remove unused code
if (hasEepromSizeData) {
availableEEpromSize = Boards::getEEpromSize(board) - 64; // let's consider fat
@@ -753,7 +753,7 @@ bool TreeModel::isModelIdUnique(unsigned modelIdx, unsigned module, unsigned pro
int cnt = 0;
if (protocol== PULSES_PXX_XJT_D8)
return true;
for (auto const& model: radioData->models) {
if (!model.isEmpty()) {
const ModuleData& moduleData = model.moduleData[module];
+75 -120
View File
@@ -175,7 +175,7 @@ bool writeFirmware(const QString & filename, ProgressWidget * progress)
return flashProcess.run();
}
bool readEeprom(const QString & filename, ProgressWidget * progress)
bool readSettings(const QString & filename, ProgressWidget * progress)
{
Board::Type board = getCurrentBoard();
@@ -186,64 +186,84 @@ bool readEeprom(const QString & filename, ProgressWidget * progress)
return false;
}
if (IS_FAMILY_HORUS_OR_T16(board)) {
QString radioPath = findMassstoragePath("RADIO", true);
qDebug() << "Searching for SD card, found" << radioPath;
if (radioPath.isEmpty()) {
QMessageBox::critical(progress, CPN_STR_TTL_ERROR,
QCoreApplication::translate("RadioInterface", "Unable to find radio SD card!"));
return false;
}
RadioData radioData;
Storage inputStorage(radioPath);
if (!inputStorage.load(radioData)) {
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, inputStorage.error());
return false;
}
Storage outputStorage(filename);
if (!outputStorage.write(radioData)) {
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, outputStorage.error());
return false;
}
if (Boards::getCapability(board, Board::HasSDCard))
return readSettingsSDCard(filename, progress);
else
return readSettingsEeprom(filename, progress);
}
if (getCurrentBoard() == Board::BOARD_JUMPER_T16 && inputStorage.getBoard() == Board::BOARD_X10) {
if (displayT16ImportWarning() == false)
return false;
}
bool readSettingsSDCard(const QString & filename, ProgressWidget * progress)
{
QString radioPath = findMassstoragePath("RADIO", true);
qDebug() << "Searching for SD card, found" << radioPath;
if (radioPath.isEmpty()) {
QMessageBox::critical(progress, CPN_STR_TTL_ERROR,
QCoreApplication::translate("RadioInterface", "Unable to find radio SD card!"));
return false;
}
else {
QString path = findMassstoragePath("EEPROM.BIN");
if (path.isEmpty()) {
// On previous OpenTX we called the EEPROM file "TARANIS.BIN" :(
path = findMassstoragePath("TARANIS.BIN");
}
if (path.isEmpty()) {
// Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :(
path = findMassstoragePath("ERSKY9X.BIN");
}
if (path.isEmpty()) {
RadioNotFoundDialog dialog(progress);
dialog.exec();
// if radio.yml it is a converted radio
// if radio.bin unconverted pre 2.6
// if neither then unconverted eeprom or empty
if (!QFile::exists(radioPath % "/RADIO/radio.yml") && !QFile::exists(radioPath % "/RADIO/radio.bin"))
return readSettingsEeprom(filename, progress);
RadioData radioData;
Storage inputStorage(radioPath);
if (!inputStorage.load(radioData)) {
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QCoreApplication::translate("RadioInterface", "Failed to read Models and Settings from") % radioPath);
return false;
}
Storage outputStorage(filename);
if (!outputStorage.write(radioData)) {
QMessageBox::critical(progress, CPN_STR_TTL_ERROR, QCoreApplication::translate("RadioInterface", "Failed to write Models and Setting file") % filename);
return false;
}
if (getCurrentBoard() == Board::BOARD_JUMPER_T16 && inputStorage.getBoard() == Board::BOARD_X10) {
if (displayT16ImportWarning() == false)
return false;
}
CopyProcess copyProcess(path, filename, progress);
if (!copyProcess.run()) {
return false;
}
}
return QFileInfo(filename).exists();
}
if (!IS_STM32(board)) {
FlashProcess flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename), progress);
if (!flashProcess.run()) {
return false;
}
bool readSettingsEeprom(const QString & filename, ProgressWidget * progress)
{
Board::Type board = getCurrentBoard();
QString path = findMassstoragePath("EEPROM.BIN");
if (path.isEmpty()) {
// On previous OpenTX we called the EEPROM file "TARANIS.BIN" :(
path = findMassstoragePath("TARANIS.BIN");
}
if (path.isEmpty()) {
// Mike's bootloader calls the EEPROM file "ERSKY9X.BIN" :(
path = findMassstoragePath("ERSKY9X.BIN");
}
if (path.isEmpty()) {
RadioNotFoundDialog dialog(progress);
dialog.exec();
return false;
}
CopyProcess copyProcess(path, filename, progress);
if (!copyProcess.run()) {
return false;
}
if (!IS_STM32(board)) {
FlashProcess flashProcess(getRadioInterfaceCmd(), getReadEEpromCmd(filename), progress);
if (!flashProcess.run()) {
return false;
}
}
return QFileInfo(filename).exists();
}
bool writeEeprom(const QString & filename, ProgressWidget * progress)
bool writeSettings(const QString & filename, ProgressWidget * progress)
{
Board::Type board = getCurrentBoard();
@@ -272,94 +292,29 @@ bool writeEeprom(const QString & filename, ProgressWidget * progress)
return false;
}
#if (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
#if defined WIN32 || !defined __GNUC__
#include <windows.h>
bool isRemovableMedia(const QString & vol)
{
char szDosDeviceName[MAX_PATH];
QString volume = vol;
UINT driveType = GetDriveType(volume.replace("/", "\\").toLatin1());
if (driveType != DRIVE_REMOVABLE)
return false;
QueryDosDevice(volume.replace("/", "").toLatin1(), szDosDeviceName, MAX_PATH);
if (strstr(szDosDeviceName, "\\Floppy") != NULL) { // it's a floppy
return false;
}
return true;
}
#else
#include "mountlist.h"
#endif // defined WIN32 || !defined __GNUC__
#endif // (QT_VERSION < QT_VERSION_CHECK(5, 4, 0))
QString findMassstoragePath(const QString & filename, bool onlyPath)
{
QString temppath;
QString eepromfile;
QString probefile;
bool found = false;
QRegularExpression fstypeRe("^(v?fat|msdos)", QRegularExpression::CaseInsensitiveOption); // Linux: "vfat"; macOS: "msdos"; Win: "FAT32"
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
foreach(const QStorageInfo & si, QStorageInfo::mountedVolumes()) {
//qDebug() << si.rootPath() << si.name() << si.device() << si.displayName() << si.fileSystemType() << si.isReady();
if (!si.isReady() || !QString(si.fileSystemType()).contains(fstypeRe))
continue;
temppath = si.rootPath();
eepromfile = temppath % "/" % filename;
qDebug() << "Searching for" << eepromfile;
if (QFile::exists(eepromfile)) {
probefile = temppath % "/" % filename;
qDebug() << "Searching for" << probefile;
if (QFile::exists(probefile)) {
found = true;
break;
}
}
#elif defined WIN32 || !defined __GNUC__
static QStringList blacklist;
foreach(const QFileInfo & drive, QDir::drives()) {
temppath = drive.absolutePath();
if (blacklist.contains(temppath))
continue;
if (!isRemovableMedia(temppath)) {
blacklist.append(temppath);
continue;
}
WCHAR szVolumeName[256] ;
WCHAR szFileSystemName[256];
DWORD dwSerialNumber = 0;
DWORD dwMaxFileNameLength=256;
DWORD dwFileSystemFlags=0;
if (!GetVolumeInformationW( (WCHAR *) drive.absolutePath().utf16(),szVolumeName,256,&dwSerialNumber,&dwMaxFileNameLength,&dwFileSystemFlags,szFileSystemName,256))
continue;
eepromfile = temppath % "/" % filename;
qDebug() << "Searching for" << eepromfile;
if (QFile::exists(eepromfile)) {
found = true;
break;
}
}
#else
const static QStringList blacklist = QStringList() << "/" << "/net" << "/proc" << "/run";
QStringList drives;
struct mount_entry *entry;
struct mount_entry *firstEntry;
firstEntry = entry = read_file_system_list(true);
while (entry != NULL) {
temppath = entry->me_mountdir;
if (!drives.contains(entry->me_devname) && !blacklist.contains(temppath) && QString(entry->me_type).contains(fstypeRe)) {
drives.append(entry->me_devname);
eepromfile = temppath % "/" % filename;
qDebug() << "Searching for" << eepromfile;
if (QFile::exists(eepromfile)) {
found = true;
break;
}
}
entry = entry->me_next;
}
free_file_system_list(firstEntry);
#endif
if (found)
return onlyPath ? temppath : eepromfile;
return onlyPath ? temppath : probefile;
else
return QString();
}
+4 -2
View File
@@ -40,7 +40,9 @@ QStringList getWriteFirmwareArgs(const QString &filename);
bool readFirmware(const QString &filename, ProgressWidget *progress);
bool writeFirmware(const QString &filename, ProgressWidget *progress);
bool readEeprom(const QString &filename, ProgressWidget *progress);
bool writeEeprom(const QString &filename, ProgressWidget *progress);
bool readSettings(const QString &filename, ProgressWidget *progress);
bool readSettingsSDCard(const QString &filename, ProgressWidget *progress);
bool readSettingsEeprom(const QString &filename, ProgressWidget *progress);
bool writeSettings(const QString &filename, ProgressWidget *progress);
#endif // _RADIOINTERFACE_H_
+1 -1
View File
@@ -103,7 +103,7 @@ void joystickDialog::populateSourceCombo(QComboBox * cb)
for (i=0; i < Board::STICK_AXIS_COUNT; ++i) {
cb->addItem(Boards::getAxisName(i) % tr(" Stick"), i);
}
for (; i < Board::STICK_AXIS_COUNT + CPN_MAX_POTS; ++i) {
for (; i < Board::STICK_AXIS_COUNT + CPN_MAX_POTS + CPN_MAX_SLIDERS; ++i) {
cb->addItem(tr("Knob/Slider %1").arg(i - Board::STICK_AXIS_COUNT + 1), i);
}
}
@@ -120,7 +120,7 @@ QString SimulatorStartupDialog::radioEepromFileName(const QString & firmwareId,
if (pos > 0)
radioId = radioId.mid(pos+1);
if (usesCategorizedStorage(radioId))
ext = "otx";
ext = "etx";
eepromFileName = QString("eeprom-%1.%2").arg(radioId, ext);
eepromFileName = QDir(folder).filePath(eepromFileName.toLatin1());
+13 -10
View File
@@ -249,9 +249,9 @@ bool SimulatorWidget::setStartupData(const QByteArray & dataSource, bool fromFil
error = store.error();
}
else {
if (fileName.endsWith(".otx", Qt::CaseInsensitive)) {
// no radios can work with .otx files directly, so we load contents into either
// a temporary folder (Horus) or local data array (other radios) which we'll save back to .otx upon exit
if (fileName.endsWith(".etx", Qt::CaseInsensitive)) {
// no radios can work with .etx files directly, so we load contents into either
// a temporary folder (Horus) or local data array (other radios) which we'll save back to .etx upon exit
if ((ret = setRadioData(&simuData))) {
startupFromFile = false;
return true;
@@ -294,17 +294,20 @@ bool SimulatorWidget::setRadioData(RadioData * radioData)
saveTempRadioData = (flags & SIMULATOR_FLAGS_STANDALONE);
if (IS_FAMILY_HORUS_OR_T16(m_board))
// All radios use SD card data path from 2.6.0 on
bool hasSdCard = Boards::getCapability(m_board, Board::HasSDCard);
if (hasSdCard)
ret = useTempDataPath(true);
if (ret) {
if (radioDataPath.isEmpty()) {
if (!hasSdCard) {
startupData.fill(0, Boards::getEEpromSize(m_board));
if (firmware->getEEpromInterface()->save((uint8_t *)startupData.data(), *radioData, 0, firmware->getCapability(SimulatorVariant)) <= 0) {
if (firmware->getEEpromInterface()->save(
(uint8_t *)startupData.data(), *radioData, 0,
firmware->getCapability(SimulatorVariant)) <= 0) {
ret = false;
}
}
else {
} else {
ret = saveRadioData(radioData, radioDataPath);
}
}
@@ -365,7 +368,7 @@ bool SimulatorWidget::useTempDataPath(bool deleteOnClose)
if (deleteTempRadioData)
deleteTempData();
QTemporaryDir tmpDir(QDir::tempPath() + "/otx-XXXXXX");
QTemporaryDir tmpDir(QDir::tempPath() + "/etx-XXXXXX");
if (tmpDir.isValid()) {
setDataPath(tmpDir.path());
tmpDir.setAutoRemove(false);
@@ -379,7 +382,7 @@ bool SimulatorWidget::useTempDataPath(bool deleteOnClose)
}
}
// This will save radio data from temporary folder structure back into an .otx file, eg. for Horus.
// This will save radio data from temporary folder structure back into an .etx file, eg. for Horus.
bool SimulatorWidget::saveTempData()
{
bool ret = false;
+1 -1
View File
@@ -127,7 +127,7 @@ CommandLineParseResult cliOptions(SimulatorOptions * simOptions, int * profileId
QApplication::translate("SimulatorMain", "type"));
cliOptions.addPositionalArgument(QApplication::translate("SimulatorMain", "data-source"),
QApplication::translate("SimulatorMain", "Radio data (.bin/.eeprom/.otx) image file to use OR data folder path (for Horus-style radios).\n"
QApplication::translate("SimulatorMain", "Radio data (.bin/.eeprom/.etx) image file to use OR data folder path (for Horus-style radios).\n"
"NOTE: any existing EEPROM data incompatible with the selected radio type may be overwritten!"),
QApplication::translate("SimulatorMain", "[data-source]"));
+1
View File
@@ -12,6 +12,7 @@ set(storage_NAMES
hexeeprom
categorized
sdcard
etx
otx
yaml
)
+26 -49
View File
@@ -409,16 +409,10 @@ QMap<int, QString> AppData::getActiveProfiles() const
void AppData::convertSettings(QSettings & settings)
{
quint32 savedVer = settings.value(SETTINGS_VERSION_KEY, 0).toUInt();
#if (VERSION_NUMBER < QT_VERSION_CHECK(2, 3, 0))
// convert old 2.2.x version marker; TODO: remove once v2.3 is established (because we'll always convert and clean 2.2.x settings anyway)
if (savedVer == 220)
savedVer = 0x2020000;
#endif
if (savedVer == CPN_SETTINGS_VERSION)
return;
if (savedVer > CPN_SETTINGS_VERSION) {
qWarning().noquote() << "Saved settings version is newer than current, skippig conversions. Saved:" << fmtHex(savedVer) << "Current:" << fmtHex(CPN_SETTINGS_VERSION);
qWarning().noquote() << "Saved settings version is newer than current, skipping conversions. Saved:" << fmtHex(savedVer) << "Current:" << fmtHex(CPN_SETTINGS_VERSION);
return;
}
@@ -432,29 +426,24 @@ void AppData::convertSettings(QSettings & settings)
<< " from v" << fmtHex(savedVer) << " (" << fmtHex(savedMajMin) << ") to v"
<< fmtHex(CPN_SETTINGS_VERSION) << " (" << fmtHex(currMajMin) << "). Removing unused: " << removeUnused;
if (settings.contains("useWizard")) {
if (!settings.contains(newModelAction_key()))
settings.setValue(newModelAction_key(), (settings.value("useWizard").toBool() ? MODEL_ACT_WIZARD : MODEL_ACT_EDITOR));
if (removeUnused)
settings.remove("useWizard");
}
// meaning of warningId changed during v2.2 development but value of "7" indicates old setting, removing it will restore defaults
if (savedMajMin < 0x203 && settings.contains("warningId") && settings.value("warningId").toInt() == 7)
settings.remove("warningId");
// Convert joystick settings to new style of key names
static const QString jsCalBasePath = QStringLiteral("JsCalibration/%1");
if (settings.contains(jsCalBasePath.arg("stick0_axe"))) {
const QStringList vars({"axe", "inv", "max", "med", "min"});
for (int i=0; i < MAX_JOYSTICKS; ++i) {
for (const QString & var : vars) {
const QString oldPath = jsCalBasePath.arg(QString("stick%1_%2").arg(i).arg(var)); // old format "/stick#_var"
const QVariant val = settings.value(oldPath);
if (val.isValid())
settings.setValue(jsCalBasePath.arg(QString("%1/stick_%2").arg(i).arg(var)), val); // new format "/#/stick_var"
if (removeUnused)
settings.remove(oldPath);
// firmwares renamed from opentx-* to edgetx-* at 2.6
if (savedMajMin <= 0x207) { // Note: change merged post 2.6 rc 1 and version bumped to 2.7 the Nightly users also require upgrade
qInfo().noquote() << "Converting profiles";
static const QString profileFwTypePath = QStringLiteral("Profiles/profile%1/fwType");
for (int i = 0; i < MAX_PROFILES; i++) {
if (settings.contains(profileFwTypePath.arg(i))) {
const QVariant oldValue = settings.value(profileFwTypePath.arg(i));
if (oldValue.isValid()) {
const QString oldval = settings.value(profileFwTypePath.arg(i)).toString();
QString newval = oldval;
newval.replace("opentx-", "edgetx-");
if (oldval != newval) {
settings.setValue(profileFwTypePath.arg(i), newval);
qInfo().noquote().nospace() << "Converted entry " << profileFwTypePath.arg(i)
<< " from (" << oldval << ")"
<< " to (" << newval << ")";
}
}
}
}
}
@@ -481,7 +470,8 @@ void AppData::clearUnusedSettings(QSettings & settings)
bool AppData::findPreviousVersionSettings(QString * version) const
{
static const QStringList versList({QStringLiteral("2.2"), QStringLiteral("2.1"), QStringLiteral("2.0")});
// TODO need a more dynamic method since ETX moves so quickly
static const QStringList versList({QStringLiteral("2.6"), QStringLiteral("2.5"), QStringLiteral("2.4")});
for (const QString &ver : versList) {
const QString prod("Companion " % ver);
@@ -498,30 +488,15 @@ bool AppData::findPreviousVersionSettings(QString * version) const
}
}
QSettings settings(COMPANY, "OpenTX Companion");
if (settings.contains(SETTINGS_VERSION_KEY)) {
*version = QStringLiteral("1.x");
return true;
}
else {
settings.clear();
}
return false;
}
bool AppData::importSettings(const QString & fromVersion)
{
QString fromProduct;
QString fromProduct = PRODUCT_NO_VERS % " " % fromVersion;
//qDebug() << "From Product:" << fromProduct;
upgradeFromVersion.clear();
if (fromVersion.startsWith("2."))
fromProduct = "Companion " % fromVersion;
else if (fromVersion == "1.x")
fromProduct = "OpenTX Companion";
else
return false;
upgradeFromVersion = fromVersion;
QSettings fromSettings(COMPANY, fromProduct);
@@ -535,8 +510,10 @@ bool AppData::importSettings(QSettings * fromSettings)
// Create temporary settings because we may modify them before import.
QSettings tempSettings(COMPANY, PRODUCT % "_import");
for (const QString & key : fromSettings->allKeys())
for (const QString & key : fromSettings->allKeys()) {
tempSettings.setValue(key, fromSettings->value(key));
//qInfo().noquote() << "Import key(" << key << ") value(" << fromSettings->value(key) << ")";
}
// convert settings first to simplify import process
convertSettings(tempSettings);
+5 -3
View File
@@ -38,20 +38,21 @@
#include <QSettings>
#include <QStandardPaths>
//! CPN_SETTINGS_REVISION is used to track settings changes independently of OpenTX version. It should be reset to zero whenever settings are migrated to new COMPANY or PRODUCT.
//! CPN_SETTINGS_REVISION is used to track settings changes independently of EdgeTX version. It should be reset to zero whenever settings are migrated to new COMPANY or PRODUCT.
//! \note !! Increment this value if properties are removed or refactored. It will trigger a conversion/cleanup of any stored settings. \sa AppData::convertSettings()
#define CPN_SETTINGS_REVISION 0
#define CPN_SETTINGS_REVISION 1 // Note: bumped to ensure 2.7 Nightly version users also get upgraded
//! CPN_SETTINGS_VERSION is used for settings data version tracking.
#define CPN_SETTINGS_VERSION ((VERSION_NUMBER << 8) | CPN_SETTINGS_REVISION)
#define COMPANY QStringLiteral("EdgeTX")
#define COMPANY_DOMAIN QStringLiteral("edgetx.org")
#define PRODUCT_NO_VERS QStringLiteral("Companion")
#define PRODUCT QStringLiteral("Companion " QT_STRINGIFY(VERSION_MAJOR) "." QT_STRINGIFY(VERSION_MINOR))
#define APP_COMPANION QStringLiteral("EdgeTX Companion")
#define APP_SIMULATOR QStringLiteral("EdgeTX Simulator")
//! Default location for OpenTX-related user documents (settigns, logs, etc)
//! Default location for EdgeTX-related user documents (settigns, logs, etc)
#define CPN_DOCUMENTS_LOCATION QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) % "/" % COMPANY
//! Location for settings backup files. TODO: make option or remember last location.
#define CPN_SETTINGS_BACKUP_DIR CPN_DOCUMENTS_LOCATION % "/backup"
@@ -379,6 +380,7 @@ class Profile: public CompStoreObj
PROPERTYSTR(sdPath)
PROPERTYSTR(pBackupDir)
PROPERTY (int, defaultInternalModule, 0)
PROPERTY4(int, channelOrder, "default_channel_order", 0)
PROPERTY4(int, defaultMode, "default_mode", 1)
PROPERTY (int, volumeGain, 10)
+201 -4
View File
@@ -20,8 +20,49 @@
#include "categorized.h"
#include "firmwares/opentx/opentxinterface.h"
#include "firmwares/edgetx/edgetxinterface.h"
#include "miniz.c" // Can only be included once!
#include <regex>
bool CategorizedStorageFormat::load(RadioData & radioData)
{
StorageType st = getStorageType(filename);
if (st == STORAGE_TYPE_UNKNOWN) {
st = probeFormat();
}
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML) {
return loadYaml(radioData);
} else {
return loadBin(radioData);
}
}
bool CategorizedStorageFormat::write(const RadioData & radioData)
{
StorageType st = getStorageType(filename);
if (st == STORAGE_TYPE_UNKNOWN) {
st = probeFormat();
}
if (st == STORAGE_TYPE_ETX || st == STORAGE_TYPE_YML ||
st == STORAGE_TYPE_UNKNOWN) {
return writeYaml(radioData);
} else {
return writeBin(radioData);
}
}
StorageType CategorizedStorageFormat::probeFormat()
{
if (QFile(filename + "/RADIO/radio.yml").exists()) // converted
return getStorageType("radio.yml");
else if (QFile(filename + "/RADIO/radio.bin").exists()) // unconverted
return getStorageType("radio.bin");
else
return getStorageType(filename);
}
bool CategorizedStorageFormat::loadBin(RadioData & radioData)
{
QByteArray radioSettingsBuffer;
if (!loadFile(radioSettingsBuffer, "RADIO/radio.bin")) {
@@ -113,7 +154,7 @@ bool CategorizedStorageFormat::load(RadioData & radioData)
return true;
}
bool CategorizedStorageFormat::write(const RadioData & radioData)
bool CategorizedStorageFormat::writeBin(const RadioData & radioData)
{
QByteArray modelsList; // models.txt
QByteArray radioSettingsData; // radio.bin
@@ -145,7 +186,7 @@ bool CategorizedStorageFormat::write(const RadioData & radioData)
if (!getCurrentFirmware()->getCapability(HasModelCategories)) {
// Use format with model number and file name. This is needed because
// radios without category support can have unused model slots
modelsList.append(QString("%1 %2\n").arg(m).arg(model.filename));
modelsList.append(QString("%1 %2\n").arg(m).arg(model.filename).toLocal8Bit());
} else {
sortedModels[model.category].push_back(QString("%1\n").arg(model.filename));
}
@@ -153,10 +194,12 @@ bool CategorizedStorageFormat::write(const RadioData & radioData)
if (getCurrentFirmware()->getCapability(HasModelCategories)) {
for (size_t c=0; c<numCategories; c++) {
modelsList.append(QString().sprintf("[%s]\n", radioData.categories[c].name));
modelsList.append(QString()
.sprintf("[%s]\n", radioData.categories[c].name)
.toLocal8Bit());
numModels = sortedModels[c].size();
for (size_t m=0; m<numModels; m++) {
modelsList.append(sortedModels[c][m]);
modelsList.append(sortedModels[c][m].toLocal8Bit());
}
}
}
@@ -167,3 +210,157 @@ bool CategorizedStorageFormat::write(const RadioData & radioData)
return true;
}
bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
{
if (getStorageType(filename) == STORAGE_TYPE_UNKNOWN && probeFormat() == STORAGE_TYPE_ETX) {
if (!QFile(filename + "/" + "RADIO/radio.yml").exists())
qDebug() << tr("Can't find %1/RADIO/radio.yml").arg(filename);
else
qDebug() << tr("Found %1/RADIO/radio.yml").arg(filename);
if (!QFile(filename + "/" + "MODELS/models.yml").exists())
qDebug() << tr("Can't find %1/MODELS/models.yml").arg(filename);
else
qDebug() << tr("Found %1/MODELS/models.yml").arg(filename);
}
else {
if (!QFile(filename).exists())
qDebug() << tr("Can't find %1").arg(filename);
else
qDebug() << tr("Found %1").arg(filename);
}
QByteArray radioSettingsBuffer;
if (!loadFile(radioSettingsBuffer, "RADIO/radio.yml")) {
setError(tr("Can't extract RADIO/radio.yml"));
return false;
}
try {
if (!loadRadioSettingsFromYaml(radioData.generalSettings, radioSettingsBuffer)) {
setError(tr("Can't load RADIO/radio.yml"));
return false;
}
} catch(const std::runtime_error& e) {
setError(tr("Can't load RADIO/radio.yml") + ":\n" + QString(e.what()));
return false;
}
board = (Board::Type)radioData.generalSettings.variant;
// get models
EtxModelfiles modelFiles;
QByteArray modelslistBuffer;
if (loadFile(modelslistBuffer, "MODELS/models.yml")) {
if (!loadModelsListFromYaml(radioData.categories, modelFiles, modelslistBuffer)) {
setError(tr("Can't load MODELS/models.yml"));
return false;
}
} else {
// fetch "MODELS/modelXX.yml"
std::list<std::string> filelist;
if (!getFileList(filelist)) {
setError(tr("Can't list files"));
return false;
}
const std::regex yml_regex("MODELS/(model([0-9]+)\\.yml)");
for(const auto& f : filelist) {
std::smatch match;
if (std::regex_match(f, match, yml_regex)) {
if (match.size() == 3) {
std::ssub_match modelFile = match[1];
std::ssub_match modelIdx = match[2];
modelFiles.push_back({ modelFile.str(), "", 0, std::stoi(modelIdx.str()) });
}
}
}
}
radioData.models.resize(modelFiles.size());
for (const auto& mc : modelFiles) {
qDebug() << "Filename: " << mc.filename.c_str() << " / Category: " << mc.category;
QByteArray modelBuffer;
QString filename = "MODELS/" + QString::fromStdString(mc.filename);
if (!loadFile(modelBuffer, filename)) {
setError(tr("Can't extract ") + filename);
return false;
}
// Please note:
// ModelData() use memset to clear everything to 0
//
auto& model = radioData.models[mc.modelIdx];
try {
if (!loadModelFromYaml(model,modelBuffer)) {
setError(tr("Can't load ") + filename);
return false;
}
} catch(const std::runtime_error& e) {
setError(tr("Can't load ") + filename + ":\n" + QString(e.what()));
return false;
}
model.category = mc.category;
model.modelIndex = mc.modelIdx;
strncpy(model.filename, mc.filename.c_str(), sizeof(model.filename)-1);
model.used = true;
}
return true;
}
bool CategorizedStorageFormat::writeYaml(const RadioData & radioData)
{
QByteArray radioSettingsBuffer;
if (!writeRadioSettingsToYaml(radioData.generalSettings, radioSettingsBuffer)) {
return false;
}
if (!writeFile(radioSettingsBuffer, "RADIO/radio.yml")) {
return false;
}
bool hasCategories = getCurrentFirmware()->getCapability(HasModelCategories);
EtxModelfiles modelFiles;
for (const auto& model : radioData.models) {
if (model.isEmpty())
continue;
QString modelFilename;
if (hasCategories) {
std::string ymlFilename = patchFilenameToYaml(model.filename);
modelFilename =
QString("MODELS/%1").arg(QString::fromStdString(ymlFilename));
modelFiles.push_back(
{ymlFilename, std::string(model.name), model.category});
} else {
modelFilename = QString("MODELS/model%1.yml")
.arg(model.modelIndex, 2, 10, QLatin1Char('0'));
}
QByteArray modelData;
writeModelToYaml(model, modelData);
if (!writeFile(modelData, modelFilename)) {
return false;
}
}
if (hasCategories) {
// TODO: sort 'modelFiles' by category index
QByteArray modelslistBuffer;
if (!writeModelsListToYaml(radioData.categories, modelFiles, modelslistBuffer)
|| !writeFile(modelslistBuffer, "MODELS/models.yml")) {
return false;
}
}
return true;
}
+10
View File
@@ -24,6 +24,8 @@
#include "storage.h"
#include <QtCore>
#include <list>
#include <string>
class CategorizedStorageFormat : public StorageFormat
{
@@ -41,6 +43,14 @@ class CategorizedStorageFormat : public StorageFormat
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName) = 0;
virtual bool writeFile(const QByteArray & fileData, const QString & fileName) = 0;
virtual bool getFileList(std::list<std::string>& filelist) = 0;
virtual bool loadBin(RadioData & radioData);
virtual bool writeBin(const RadioData & radioData);
virtual bool loadYaml(RadioData & radioData);
virtual bool writeYaml(const RadioData & radioData);
StorageType probeFormat();
};
#endif // _CATEGORIZED_H_
+130
View File
@@ -0,0 +1,130 @@
/*
* 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 "etx.h"
#include <QFile>
#define MZ_ALLOCATION_SIZE (32*1024)
bool EtxFormat::load(RadioData & radioData)
{
QFile file(filename);
if (!file.open(QFile::ReadOnly)) {
setError(tr("Error opening file %1:\n%2.").arg(filename).arg(file.errorString()));
return false;
}
QByteArray archiveContents = file.readAll();
qDebug() << "File" << filename << "read, size:" << archiveContents.size();
// open zip file
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_reader_init_mem(&zip_archive, archiveContents.data(), archiveContents.size(), 0)) {
qDebug() << tr("Error opening EdgeTX archive %1").arg(filename);
return false;
}
bool result = CategorizedStorageFormat::load(radioData);
mz_zip_reader_end(&zip_archive);
return result;
}
bool EtxFormat::write(const RadioData & radioData)
{
qDebug() << "Saving to archive" << filename;
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_writer_init_heap(&zip_archive, 0, MZ_ALLOCATION_SIZE)) {
setError(tr("Error initializing EdgeTX archive writer"));
return false;
}
bool result = CategorizedStorageFormat::write(radioData);
if (result) {
// finalize archive and get contents
char * archiveContents;
size_t archiveSize;
if (mz_zip_writer_finalize_heap_archive(&zip_archive, (void **)&archiveContents, &archiveSize)) {
qDebug() << "Archive size" << archiveSize;
// write contents to file
QFile file(filename);
if (file.open(QIODevice::WriteOnly)) {
qint64 len = file.write(archiveContents, archiveSize);
if (len != (qint64)archiveSize) {
setError(tr("Error writing file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(tr("Error creating EdgeTX file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(tr("Error creating EdgeTX archive"));
result = false;
}
}
mz_zip_writer_end(&zip_archive);
return result;
}
bool EtxFormat::loadFile(QByteArray & filedata, const QString & filename)
{
size_t size;
void * data = mz_zip_reader_extract_file_to_heap(&zip_archive, qPrintable(filename), &size, 0);
if (!data) {
return false;
}
qDebug() << QString("Extracted file %1, size=%2").arg(filename).arg(size);
filedata.clear();
filedata.append((char *)data, size);
mz_free(data);
return true;
}
bool EtxFormat::writeFile(const QByteArray & filedata, const QString & filename)
{
if (!mz_zip_writer_add_mem(&zip_archive, filename.toStdString().c_str(), filedata.data(), filedata.size(), MZ_DEFAULT_LEVEL)) {
setError(tr("Error adding %1 to EdgeTX archive").arg(filename));
return false;
}
return true;
}
bool EtxFormat::getFileList(std::list<std::string>& filelist)
{
int count = (int)mz_zip_reader_get_num_files(&zip_archive);
if (count == 0) return false;
mz_zip_archive_file_stat file_stat;
for (int i=0; i<count; i++) {
if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) continue;
if (mz_zip_reader_is_file_a_directory(&zip_archive, i)) continue;
filelist.push_back(file_stat.m_filename);
}
return true;
}
+54
View File
@@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef _ETX_H_
#define _ETX_H_
#include "categorized.h"
#include <QtCore>
#define MINIZ_HEADER_FILE_ONLY
#include "miniz.c"
#undef MINIZ_HEADER_FILE_ONLY
class EtxFormat : public CategorizedStorageFormat
{
Q_DECLARE_TR_FUNCTIONS(EtxFormat)
public:
EtxFormat(const QString & filename):
CategorizedStorageFormat(filename)
{
}
virtual QString name() { return "etx"; }
virtual bool load(RadioData & radioData);
virtual bool write(const RadioData & radioData);
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName);
virtual bool writeFile(const QByteArray & fileData, const QString & fileName);
virtual bool getFileList(std::list<std::string>& filelist);
mz_zip_archive zip_archive;
};
#endif // _ETX_H_
+7 -7
View File
@@ -305,11 +305,11 @@ bool FirmwareInterface::seekSplash(QByteArray sps, QByteArray spe, int size)
return false;
}
#define OTX_SPS_9X "SPS\0\200\100"
#define OTX_SPS_TARANIS "SPS\0\324\100"
#define OTX_SPS_SIZE 6
#define OTX_SPE "SPE"
#define OTX_SPE_SIZE 4
#define ETX_SPS_9X "SPS\0\200\100"
#define ETX_SPS_TARANIS "SPS\0\324\100"
#define ETX_SPS_SIZE 6
#define ETX_SPE "SPE"
#define ETX_SPE_SIZE 4
void FirmwareInterface::seekSplash()
{
@@ -342,11 +342,11 @@ void FirmwareInterface::seekSplash()
return;
}
if (seekSplash(QByteArray(OTX_SPS_9X, OTX_SPS_SIZE), QByteArray(OTX_SPE, OTX_SPE_SIZE), 1024)) {
if (seekSplash(QByteArray(ETX_SPS_9X, ETX_SPS_SIZE), QByteArray(ETX_SPE, ETX_SPE_SIZE), 1024)) {
return;
}
if (seekSplash(QByteArray(OTX_SPS_TARANIS, OTX_SPS_SIZE), QByteArray(OTX_SPE, OTX_SPE_SIZE), RLE_SPLASH_MAX_SIZE)) {
if (seekSplash(QByteArray(ETX_SPS_TARANIS, ETX_SPS_SIZE), QByteArray(ETX_SPE, ETX_SPE_SIZE), RLE_SPLASH_MAX_SIZE)) {
splashWidth = SPLASHX9D_WIDTH;
splashHeight = SPLASHX9D_HEIGHT;
splash_format = QImage::Format_Indexed8;
+5 -6
View File
@@ -19,7 +19,6 @@
*/
#include "otx.h"
#include "miniz.c"
#include <QFile>
#define MZ_ALLOCATION_SIZE (32*1024)
@@ -40,7 +39,7 @@ bool OtxFormat::load(RadioData & radioData)
// open zip file
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_reader_init_mem(&zip_archive, archiveContents.data(), archiveContents.size(), 0)) {
qDebug() << tr("Error opening OTX archive %1").arg(filename);
qDebug() << tr("Error opening OpenTX archive %1").arg(filename);
return false;
}
@@ -55,7 +54,7 @@ bool OtxFormat::write(const RadioData & radioData)
memset(&zip_archive, 0, sizeof(zip_archive));
if (!mz_zip_writer_init_heap(&zip_archive, 0, MZ_ALLOCATION_SIZE)) {
setError(tr("Error initializing OTX archive writer"));
setError(tr("Error initializing OpenTX archive writer"));
return false;
}
@@ -76,12 +75,12 @@ bool OtxFormat::write(const RadioData & radioData)
}
}
else {
setError(tr("Error creating OTX file %1:\n%2.").arg(filename).arg(file.errorString()));
setError(tr("Error creating OpenTX file %1:\n%2.").arg(filename).arg(file.errorString()));
result = false;
}
}
else {
setError(tr("Error creating OTX archive"));
setError(tr("Error creating OpenTX archive"));
result = false;
}
}
@@ -108,7 +107,7 @@ bool OtxFormat::loadFile(QByteArray & filedata, const QString & filename)
bool OtxFormat::writeFile(const QByteArray & filedata, const QString & filename)
{
if (!mz_zip_writer_add_mem(&zip_archive, filename.toStdString().c_str(), filedata.data(), filedata.size(), MZ_DEFAULT_LEVEL)) {
setError(tr("Error adding %1 to OTX archive").arg(filename));
setError(tr("Error adding %1 to OpenTX archive").arg(filename));
return false;
}
+1
View File
@@ -46,6 +46,7 @@ class OtxFormat : public CategorizedStorageFormat
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName);
virtual bool writeFile(const QByteArray & fileData, const QString & fileName);
virtual bool getFileList(std::list<std::string>& filelist) { return false; }
mz_zip_archive zip_archive;
};
+12 -1
View File
@@ -57,8 +57,19 @@ bool SdcardFormat::writeFile(const QByteArray & data, const QString & filename)
return true;
}
bool SdcardFormat::getFileList(std::list<std::string>& filelist)
{
QDir dir(filename);
if (!dir.cd("MODELS")) return false;
QStringList ql = dir.entryList();
for (const auto& str : ql) {
filelist.push_back("MODELS/" + str.toStdString());
}
return true;
}
bool SdcardStorageFactory::probe(const QString & path)
{
return QDir(path).exists();
}
+1
View File
@@ -41,6 +41,7 @@ class SdcardFormat : public CategorizedStorageFormat
protected:
virtual bool loadFile(QByteArray & fileData, const QString & fileName);
virtual bool writeFile(const QByteArray & fileData, const QString & fileName);
virtual bool getFileList(std::list<std::string>& filelist);
};
class SdcardStorageFactory : public DefaultStorageFactory<SdcardFormat>
+17 -10
View File
@@ -21,6 +21,7 @@
#include "bineeprom.h"
#include "eepe.h"
#include "otx.h"
#include "etx.h"
#include "sdcard.h"
#include "yaml.h"
#include "firmwareinterface.h"
@@ -42,6 +43,8 @@ StorageType getStorageType(const QString & filename)
return STORAGE_TYPE_XML;
else if (suffix == "OTX")
return STORAGE_TYPE_OTX;
else if (suffix == "ETX")
return STORAGE_TYPE_ETX;
else if (suffix == "YML")
return STORAGE_TYPE_YML;
else
@@ -64,6 +67,7 @@ void registerStorageFactories()
registerStorageFactory(new DefaultStorageFactory<EepeFormat>("eepe"));
registerStorageFactory(new DefaultStorageFactory<HexEepromFormat>("hex"));
registerStorageFactory(new DefaultStorageFactory<OtxFormat>("otx"));
registerStorageFactory(new DefaultStorageFactory<EtxFormat>("etx"));
registerStorageFactory(new DefaultStorageFactory<YamlFormat>("yml"));
registerStorageFactory(new SdcardStorageFactory());
}
@@ -84,17 +88,20 @@ bool Storage::load(RadioData & radioData)
bool ret = false;
foreach(StorageFactory * factory, registeredStorageFactories) {
StorageFormat * format = factory->instance(filename);
if (format->load(radioData)) {
board = format->getBoard();
setWarning(format->warning());
ret = true;
break;
if (factory->probe(filename)) {
StorageFormat * format = factory->instance(filename);
if (format->load(radioData)) {
board = format->getBoard();
setWarning(format->warning());
ret = true;
delete format;
break;
}
else {
setError(format->error());
}
delete format;
}
else {
setError(format->error());
}
delete format;
}
return ret;

Some files were not shown because too many files have changed in this diff Show More