diff --git a/.gitmodules b/.gitmodules index 1da1a8370..ec08cff60 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index d160ff674..c3e2928f1 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -197,6 +197,7 @@ add_subdirectory(simulation) add_subdirectory(storage) add_subdirectory(thirdparty/qcustomplot) add_subdirectory(thirdparty/maxlibqt/src/widgets) +add_subdirectory(thirdparty/yaml-cpp) ############# Companion ############### diff --git a/companion/src/firmwares/CMakeLists.txt b/companion/src/firmwares/CMakeLists.txt index 9a8593b99..2185e3676 100644 --- a/companion/src/firmwares/CMakeLists.txt +++ b/companion/src/firmwares/CMakeLists.txt @@ -29,6 +29,7 @@ set(firmwares_SRCS ersky9x/ersky9xinterface.cpp opentx/opentxeeprom.cpp opentx/opentxinterface.cpp + edgetx/edgetxinterface.cpp datahelpers.cpp customisation_data.cpp ) @@ -48,14 +49,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" ) diff --git a/companion/src/firmwares/edgetx/edgetxinterface.cpp b/companion/src/firmwares/edgetx/edgetxinterface.cpp new file mode 100644 index 000000000..b38f0fbc0 --- /dev/null +++ b/companion/src/firmwares/edgetx/edgetxinterface.cpp @@ -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 "edgetxinterface.h" + +#include "yaml_ops.h" +#include "yaml_generalsettings.h" + +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); +} + +bool loadModelFromYaml(ModelData& model, const QByteArray& data) +{ + try { + YAML::Node node = loadYamlFromByteArray(data); + //model << node; + } catch (const std::runtime_error& e) { + qDebug() << "YAML::ParserException: " << e.what(); + return false; + } + + return true; +} + +bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data) +{ + try { + YAML::Node node = loadYamlFromByteArray(data); + node >> settings; + } catch (const std::runtime_error& e) { + qDebug() << "YAML::ParserException: " << e.what(); + return false; + } + + return true; +} + +bool writeModelToYaml(const ModelData& model, QByteArray& data) +{ + return false; +} + +bool writeRadioSettingsToYaml(const GeneralSettings& settings, QByteArray& data) +{ + return false; +} diff --git a/companion/src/firmwares/edgetx/edgetxinterface.h b/companion/src/firmwares/edgetx/edgetxinterface.h new file mode 100644 index 000000000..bdfe01bf7 --- /dev/null +++ b/companion/src/firmwares/edgetx/edgetxinterface.h @@ -0,0 +1,30 @@ +/* + * 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 + +bool loadModelFromYaml(ModelData& model, const QByteArray& data); +bool loadRadioSettingsFromYaml(GeneralSettings& settings, const QByteArray& data); + +bool writeModelToByteArray(const ModelData& model, QByteArray& data); +bool writeRadioSettingsToByteArray(const GeneralSettings& settings, QByteArray& data); diff --git a/companion/src/firmwares/edgetx/yaml_generalsettings.h b/companion/src/firmwares/edgetx/yaml_generalsettings.h new file mode 100644 index 000000000..1cda57076 --- /dev/null +++ b/companion/src/firmwares/edgetx/yaml_generalsettings.h @@ -0,0 +1,123 @@ +/* + * 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" +#include "yaml_trainerdata.h" + +namespace YAML { + + template<> + struct convert { + + // static YAML::Node encode(const GeneralSettings& rhs) { + // Node node; + // // TODO: convert struct into Node + // return node; + // } + + static bool decode(const YAML::Node& node, GeneralSettings& rhs) { + + node["version"] >> rhs.version; + node["versionBla"] >> rhs.version; + // // calib + node["currModel"] >> rhs.currModelIndex; + // // filename + node["contrast"] >> rhs.contrast; + node["vBatWarn"] >> rhs.vBatWarn; + node["txVoltageCalibration"] >> rhs.txVoltageCalibration; + node["vBatMin"] >> rhs.vBatMin; + node["vBatMax"] >> rhs.vBatMax; + + // TODO: lookup table + //rhs.backlightMode = node["backlightMode"].as(); // backlight_mode_all + + node["trainer"] >> rhs.trainer; + node["view"] >> rhs.view; + node["fai"] >> rhs.fai; + node["disableMemoryWarning"] >> rhs.disableMemoryWarning; + //node["beepMode"] >> rhs.beeperMode; // TODO: string mapping + node["disableAlarmWarning"] >> rhs.disableAlarmWarning; + node["disableRssiPoweroffAlarm"] >> rhs.disableRssiPoweroffAlarm; + node["USBMode"] >> rhs.usbMode; + node["jackMode"] >> rhs.jackMode; + //node["hapticMode"] >> rhs.hapticMode; // TODO: strin mapping + node["stickMode"] >> rhs.stickMode; + node["timezone"] >> rhs.timezone; + node["adjustRTC"] >> rhs.adjustRTC; + node["inactivityTimer"] >> rhs.inactivityTimer; + node["telemetryBaudrate"] >> rhs.telemetryBaudrate; // TODO: conversion??? + node["splashMode"] >> rhs.splashMode; // TODO: B&W only + node["templateSetup"] >> rhs.templateSetup; + node["hapticLength"] >> rhs.hapticLength; // TODO: conversion??? + node["speakerPitch"] >> rhs.speakerPitch; // TODO: conversion??? + node["hapticStrength"] >> rhs.hapticStrength; // TODO: conversion??? + node["beepLength"] >> rhs.beeperLength; // TODO: conversion??? + node["gpsFormat"] >> rhs.gpsFormat; + node["speakerVolume"] >> rhs.speakerVolume; // TODO: conversion??? + node["backlightBright"] >> rhs.backlightBright; + node["switchesDelay"] >> rhs.switchesDelay; + node["globalTimer"] >> rhs.globalTimer; + node["bluetoothName"] >> rhs.bluetoothName; + node["bluetoothBaudrate"] >> rhs.bluetoothBaudrate; + node["bluetoothMode"] >> 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"] >> rhs.beepVolume; // TODO: conversion??? + node["wavVolume"] >> rhs.wavVolume; + node["varioVolume"] >> rhs.varioVolume; + node["varioPitch"] >> rhs.varioPitch; + node["varioRange"] >> rhs.varioRange; + node["varioRepeat"] >> rhs.varioRepeat; + node["backgroundVolume"] >> rhs.backgroundVolume; + //node["auxSerialMode"] >> rhs.auxSerialMode; + //node["aux2SerialMode"] >> rhs.aux2SerialMode; + //node["antennaMode"] >> rhs.antennaMode; + node["backlightColor"] >> rhs.backlightColor; + + // if (node["customFn"]) { + // const YAML::Node& cfn = node["customFn"]; + // for (int i = 0; i < CPN_MAX_SPECIAL_FUNCTIONS; i++) { + // cfn[i] >> rhs.customFn[i]; + // } + // } + + // TODO: + // 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 sliderName[CPN_MAX_SLIDERS][HARDWARE_NAME_LEN + 1]; + // unsigned int sliderConfig[CPN_MAX_SLIDERS]; + // RadioTheme::ThemeData themeData; + + node["ownerRegistrationID"] >> rhs.registrationId; + + return true; + } + }; +} + + diff --git a/companion/src/firmwares/edgetx/yaml_ops.h b/companion/src/firmwares/edgetx/yaml_ops.h new file mode 100644 index 000000000..4dc4d6608 --- /dev/null +++ b/companion/src/firmwares/edgetx/yaml_ops.h @@ -0,0 +1,41 @@ +/* + * 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 + +template +void operator>>(const YAML::Node& node, T& value) +{ + if (node) { + value = node.as(); + } +} + +template +void operator>>(const YAML::Node& node, char (&value)[N]) +{ + if (node) { + auto str = node.as(); + str.copy(value, N-1); + value[std::min(str.size(), N-1)] = '\0'; + } +} diff --git a/companion/src/firmwares/edgetx/yaml_trainerdata.h b/companion/src/firmwares/edgetx/yaml_trainerdata.h new file mode 100644 index 000000000..766c0930b --- /dev/null +++ b/companion/src/firmwares/edgetx/yaml_trainerdata.h @@ -0,0 +1,71 @@ +/* + * 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 { + + // static YAML::Node encode(const TrainerMix& rhs) { + // Node node; + // // TODO: convert struct into Node + // return node; + // } + + static bool decode(const YAML::Node& node, TrainerMix& rhs) + { + node["srcChn"] >> rhs.src; + //node["mode"] >> rhs.mode; // TODO: string mapping + node["studWeight"] >> rhs.weight; + return true; + } + }; + + template<> + struct convert { + + // static YAML::Node encode(const TrainerData& rhs) { + // Node node; + // // TODO: convert struct into Node + // return node; + // } + + static bool decode(const YAML::Node& node, TrainerData& rhs) + { + if (node["calib"]) { + const YAML::Node& calib = node["calib"]; + for (int i = 0; i < 4; i++) { + calib[i] >> rhs.calib[i]; + } + } + + if (node["mix"]) { + const YAML::Node& mix = node["mix"]; + for (int i = 0; i < 4; i++) { + mix[i] >> rhs.mix[i]; + } + } + return true; + } + }; +} diff --git a/companion/src/storage/categorized.cpp b/companion/src/storage/categorized.cpp index 78ffd9c95..984c01842 100644 --- a/companion/src/storage/categorized.cpp +++ b/companion/src/storage/categorized.cpp @@ -20,6 +20,7 @@ #include "categorized.h" #include "firmwares/opentx/opentxinterface.h" +#include "firmwares/edgetx/edgetxinterface.h" #include "miniz.c" // Can only be included once! bool CategorizedStorageFormat::load(RadioData & radioData) @@ -217,8 +218,8 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData) qDebug() << tr("Found %1").arg(filename); } - setError(tr("Error: Yaml format ignored - under development")); - return false; // force failure until fully developed + // setError(tr("Error: Yaml format ignored - under development")); + // return false; // force failure until fully developed /* This code a copy of loadBin with yaml file extensions ONLY @@ -229,88 +230,15 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData) setError(tr("Can't extract RADIO/radio.yml")); return false; } - - OpenTxEepromInterface * loadInterface = loadRadioSettingsFromByteArray(radioData.generalSettings, radioSettingsBuffer); - if (!loadInterface) { + + if (!loadRadioSettingsFromYaml(radioData.generalSettings, radioSettingsBuffer)) { + setError(tr("Can't load RADIO/radio.yml")); return false; } - board = loadInterface->getBoard(); + // TODO: provide a simple text mapping + board = Board::BOARD_RADIOMASTER_TX16S; - QByteArray modelsListBuffer; - if (!loadFile(modelsListBuffer, "MODELS/models.yml")) { - setError(tr("Can't extract MODELS/models.yml")); - return false; - } - - QList lines = modelsListBuffer.split('\n'); - int modelIndex = 0; - int categoryIndex = -1; - foreach (const QByteArray & lineArray, lines) { - QString line = QString(lineArray).trimmed(); - if (line.isEmpty()) continue; - // qDebug() << "parsing line" << line; - - if (line.startsWith('[') && line.endsWith(']')) { - // ignore categories for boards that do not support them - if (getCurrentFirmware()->getCapability(HasModelCategories)) { - QString name = line.mid(1, line.size() - 2); - CategoryData category(qPrintable(name)); - radioData.categories.push_back(category); - categoryIndex++; - qDebug() << "added category" << name; - } - continue; - } - - // determine if we have a model number - QStringList parts = line.split(QRegExp("\\s+"), QString::SkipEmptyParts); - if (parts.size() == 2) { - // parse model number - int modelNumber = parts[0].toInt(); - if (modelNumber > 0 && modelNumber > modelIndex && modelNumber < getCurrentFirmware()->getCapability(Models)) { - modelIndex = modelNumber; - qDebug() << "advanced model number to" << modelIndex; - } - else { - if (modelNumber != modelIndex) qDebug() << "Invalid model number" << parts[0]; - } - parts.removeFirst(); - } - if (parts.size() == 1) { - // parse model file name and load - QString fileName = parts[0]; - qDebug() << "Loading model from file" << fileName << "into slot" << modelIndex; - QByteArray modelBuffer; - if (!loadFile(modelBuffer, QString("MODELS/%1").arg(fileName))) { - setError(tr("Can't extract %1").arg(fileName)); - return false; - } - if ((int)radioData.models.size() <= modelIndex) { - radioData.models.resize(modelIndex + 1); - } - if (!loadModelFromByteArray(radioData.models[modelIndex], modelBuffer)) { - setError(tr("Error loading models")); - return false; - } - strncpy(radioData.models[modelIndex].filename, qPrintable(fileName), sizeof(radioData.models[modelIndex].filename)); - if (IS_FAMILY_HORUS_OR_T16(board) && !strcmp(radioData.generalSettings.currModelFilename, qPrintable(fileName))) { - radioData.generalSettings.currModelIndex = modelIndex; - qDebug() << "currModelIndex =" << modelIndex; - } - if (getCurrentFirmware()->getCapability(HasModelCategories)) { - radioData.models[modelIndex].category = categoryIndex; - } - radioData.models[modelIndex].used = true; - modelIndex++; - continue; - } - - // invalid line - // TODO add to parsing report - qDebug() << "Invalid line" <> sortedModels(numCategories); - - writeRadioSettingsToByteArray(radioData.generalSettings, radioSettingsData); - if (!writeFile(radioSettingsData, "RADIO/radio.yml")) { - return false; - } - - for (size_t m=0; mgetCapability(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)); - } else { - sortedModels[model.category].push_back(QString("%1\n").arg(model.filename)); - } - } - - if (getCurrentFirmware()->getCapability(HasModelCategories)) { - for (size_t c=0; c