WiP: start parsing YaML

This commit is contained in:
Raphael Coeffic
2021-12-31 10:53:08 +01:00
parent 0f6db9c5cb
commit 5a8a5ab743
10 changed files with 351 additions and 139 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
+1
View File
@@ -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 ###############
+6
View File
@@ -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"
)
@@ -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;
}
@@ -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 <QByteArray>
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);
@@ -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<GeneralSettings> {
// 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<std::string>(); // 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;
}
};
}
+41
View File
@@ -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 <yaml-cpp/yaml.h>
template <typename T>
void operator>>(const YAML::Node& node, T& value)
{
if (node) {
value = node.as<T>();
}
}
template <size_t N>
void operator>>(const YAML::Node& node, char (&value)[N])
{
if (node) {
auto str = node.as<std::string>();
str.copy(value, N-1);
value[std::min(str.size(), N-1)] = '\0';
}
}
@@ -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<TrainerMix> {
// 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<TrainerData> {
// 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;
}
};
}
+8 -139
View File
@@ -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<QByteArray> 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" <<line;
continue;
}
return true;
}
@@ -318,63 +246,4 @@ bool CategorizedStorageFormat::writeYaml(const RadioData & radioData)
{
setError("Error: Yaml format ignored - under development");
return false; // force failure until fully developed
/*
This is a copy of writeBin with changed file yml extensions ONLY
*/
QByteArray modelsList; // models.txt
QByteArray radioSettingsData; // radio.bin
size_t numModels = radioData.models.size();
size_t numCategories = radioData.categories.size();
std::vector<std::vector<QString>> sortedModels(numCategories);
writeRadioSettingsToByteArray(radioData.generalSettings, radioSettingsData);
if (!writeFile(radioSettingsData, "RADIO/radio.yml")) {
return false;
}
for (size_t m=0; m<numModels; m++) {
const ModelData & model = radioData.models[m];
if (model.isEmpty()) continue;
QString modelFilename = QString("MODELS/%1").arg(model.filename);
QByteArray modelData;
writeModelToByteArray(model, modelData);
if (!writeFile(modelData, modelFilename)) {
return false;
}
// For firmware that doesn't support categories, we can just construct
// models.txt as we iterate thru the models vector. For firmware that does
// support categories, we have a bit of work to do in order to sort the
// models first by category index, so we use the sortedModels data
// structure to do that.
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));
} else {
sortedModels[model.category].push_back(QString("%1\n").arg(model.filename));
}
}
if (getCurrentFirmware()->getCapability(HasModelCategories)) {
for (size_t c=0; c<numCategories; c++) {
modelsList.append(QString().sprintf("[%s]\n", radioData.categories[c].name));
numModels = sortedModels[c].size();
for (size_t m=0; m<numModels; m++) {
modelsList.append(sortedModels[c][m]);
}
}
}
if (!writeFile(modelsList, "MODELS/models.yml")) {
return false;
}
return true;
}