WiP: Add model loading

This commit is contained in:
Raphael Coeffic
2021-12-31 10:53:09 +01:00
parent ac60ad87f8
commit d0293013ec
8 changed files with 315 additions and 10 deletions
+1
View File
@@ -34,6 +34,7 @@ set(firmwares_SRCS
edgetx/yaml_generalsettings.cpp
edgetx/yaml_calibdata.cpp
edgetx/yaml_switchconfig.cpp
edgetx/yaml_modeldata.cpp
datahelpers.cpp
customisation_data.cpp
)
@@ -22,6 +22,7 @@
#include "yaml_ops.h"
#include "yaml_generalsettings.h"
#include "yaml_modeldata.h"
static YAML::Node loadYamlFromByteArray(const QByteArray& data)
{
@@ -30,11 +31,45 @@ static YAML::Node loadYamlFromByteArray(const QByteArray& data)
return YAML::Load(data_istream);
}
bool loadModelsListFromYaml(std::vector<CategoryData> categories,
EtxModelfiles& modelFiles,
const QByteArray& data)
{
try {
YAML::Node node = loadYamlFromByteArray(data);
if (!node.IsSequence()) return false;
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;
model["filename"] >> filename;
modelFiles.push_back({ filename, categories.size()-1 });
}
}
}
} catch (const std::runtime_error& e) {
qDebug() << "YAML::ParserException: " << e.what();
return false;
}
return true;
}
bool loadModelFromYaml(ModelData& model, const QByteArray& data)
{
try {
YAML::Node node = loadYamlFromByteArray(data);
//model << node;
node >> model;
} catch (const std::runtime_error& e) {
qDebug() << "YAML::ParserException: " << e.what();
return false;
@@ -23,6 +23,17 @@
#include "radiodata.h"
#include <QByteArray>
#include <string>
#include <utility>
#include <vector>
typedef std::pair<std::string, int> EtxModelfileCategoryPair;
typedef std::list<EtxModelfileCategoryPair> 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);
@@ -47,16 +47,24 @@ namespace YAML {
static bool decode(const Node& node, GeneralSettings& rhs) {
if (!node.IsMap()) return false;
// for (const auto& n : node) {
// qDebug() << QString::fromStdString(n.first.Scalar());
// }
// TODO: check board string and fetch Board instance
std::string board;
node["board"] >> board;
// TODO: we should be able to fetch the board based on 'board'
auto fw = getCurrentFirmware();
if (fw && fw->getId().toStdString() != ("edgetx-" + board)) {
throw std::runtime_error("Wrong board");
}
// auto fw = getCurrentFirmware();
// if (fw) {
// qDebug() << "FW Id: " << fw->getId();
// if (fw->getId() != ("edgetx-" + radioData.generalSettings.board)) {
// throw std::runtime_error("Wrong board");
// }
// }
node["version"] >> rhs.version;
// Radio calib
@@ -0,0 +1,43 @@
/*
* 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_modeldata.h"
#include "modeldata.h"
const YamlLookupTable timerModeLut = {
{TimerData::TIMERMODE_OFF, "OFF"},
{TimerData::TIMERMODE_ON, "ON"},
{TimerData::TIMERMODE_START, "START"},
{TimerData::TIMERMODE_THR, "THR"},
{TimerData::TIMERMODE_THR_REL, "THR_REL"},
{TimerData::TIMERMODE_THR_START, "THR_START"},
};
const YamlLookupTable trainerModeLut = {
{ TRAINER_MODE_OFF, "OFF" },
{ TRAINER_MODE_MASTER_JACK, "MASTER_TRAINER_JACK" },
{ TRAINER_MODE_SLAVE_JACK, "SLAVE" },
{ TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE, "MASTER_SBUS_EXT" },
{ TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE, "MASTER_CPPM_EXT" },
{ TRAINER_MODE_MASTER_BATTERY_COMPARTMENT, "MASTER_BATT_COMP" },
{ TRAINER_MODE_MASTER_BLUETOOTH, "MASTER_BT" },
{ TRAINER_MODE_SLAVE_BLUETOOTH, "SLAVE_BT" },
{ TRAINER_MODE_MULTI, "MASTER_MULTI" },
};
@@ -0,0 +1,140 @@
/*
* 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"
#include <string>
extern const YamlLookupTable timerModeLut;
extern const YamlLookupTable trainerModeLut;
// extern const YamlLookupTable backlightModeLut;
// extern const YamlLookupTable bluetoothModeLut;
// extern const YamlLookupTable uartModeLut;
// extern const YamlLookupTable antennaModeLut;
namespace YAML {
// ENUM_CONVERTER(GeneralSettings::BeeperMode, beeperModeLut);
template <>
struct convert<TimerData> {
// static YAML::Node encode(const GeneralSettings& rhs) {
// Node node;
// // TODO: convert struct into Node
// return node;
// }
static bool decode(const Node& node, TimerData& rhs)
{
// TODO: node["swtch"] >> rhs.swtch;
node["mode"] >> timerModeLut >> rhs.mode;
node["name"] >> rhs.name;
node["minuteBeep"] >> rhs.minuteBeep;
node["countdownBeep"] >> rhs.countdownBeep;
node["start"] >> rhs.val;
node["persistent"] >> rhs.persistent;
node["countdownStart"] >> rhs.countdownStart;
node["value"] >> rhs.pvalue;
return true;
}
};
template <>
struct convert<ModelData> {
// static YAML::Node encode(const GeneralSettings& rhs) {
// Node node;
// // TODO: convert struct into Node
// return node;
// }
static bool decode(const Node& node, ModelData& rhs)
{
if (!node.IsMap()) return false;
if (node["header"]) {
const auto& header = node["header"];
if (!header.IsMap()) return false;
header["name"] >> rhs.name;
header["bitmap"] >> rhs.bitmap;
// TODO: modelId
}
if (node["timers"]) {
const auto& timers = node["timers"];
if (!timers.IsMap()) return false;
timers >> rhs.timers;
}
node["noGlobalFunctions"] >> rhs.noGlobalFunctions;
node["thrTrim"] >> rhs.thrTrim;
node["trimInc"] >> rhs.trimInc;
node["displayTrims"] >> rhs.trimsDisplay;
node["disableThrottleWarning"] >> rhs.disableThrottleWarning;
//node[] >> rhs.beepANACenter;
node["extendedLimits"] >> rhs.extendedLimits;
node["extendedTrims"] >> rhs.extendedTrims;
node["throttleReversed"] >> rhs.throttleReversed;
//flightModeData[]
//mixData[]
//limitData[]
//inputNames[]
//expoData[]
//curves[]
//logicalSw[]
//customFn[]
//swashRingData
//thrTraceSrc
//switchWarningStates
//thrTrimSwitch
//potsWarningMode
//potsWarnEnabled[]
//potsWarnPosition[]
node["displayChecklist"] >> rhs.displayChecklist;
//gvarData[]
//mavlink ???
//telemetryProtocol ???
//frsky
//rssiSource
//rssiAlarms
node["trainerMode"] >> trainerModeLut >> rhs.trainerMode;
//moduleData[]
//scriptData[]
//sensorData[]
node["toplcdTimer"] >> rhs.toplcdTimer;
//customScreens
//topBarData
node["view"] >> rhs.view;
node["modelRegistrationID"] >> rhs.registrationId;
return true;
}
};
} // namespace YAML
+20
View File
@@ -21,6 +21,7 @@
#pragma once
#include <yaml-cpp/yaml.h>
#include <QString>
typedef std::pair<int, std::string> YamlLookupTableElmt;
typedef std::vector<YamlLookupTableElmt> YamlLookupTable;
@@ -91,6 +92,25 @@ void operator >> (const YAML::Node& node, char (&value)[N])
}
}
template <typename T, size_t N>
void operator>>(const YAML::Node& node, T (&value)[N])
{
if (!node.IsMap()) return;// false;
for (const auto& elmt : node) {
try {
int idx = std::stoi(elmt.first.Scalar());
if (idx < 0 || idx >= N) return;// false;
elmt.second >> value[idx];
} catch (...) {
//return false;
return;
}
}
//return true;
}
#define ENUM_CONVERTER(enum_type, lut) \
template <> \
struct convert<enum_type> { \
+50 -3
View File
@@ -175,7 +175,7 @@ bool CategorizedStorageFormat::writeBin(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));
}
@@ -183,10 +183,12 @@ bool CategorizedStorageFormat::writeBin(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());
}
}
}
@@ -239,6 +241,51 @@ bool CategorizedStorageFormat::loadYaml(RadioData & radioData)
// TODO: provide a simple text mapping
board = Board::BOARD_RADIOMASTER_TX16S;
// get models
QByteArray modelslistBuffer;
if (loadFile(modelslistBuffer, "MODELS/models.yml")) {
// List< { filename, category index } >
EtxModelfiles modelFiles;
if (!loadModelsListFromYaml(radioData.categories, modelFiles, modelslistBuffer)) {
setError(tr("Can't load MODELS/models.yml"));
return false;
}
radioData.models.reserve(modelFiles.size());
int modelIdx = 0;
for (const auto& mc : modelFiles) {
qDebug() << "Filename: " << mc.first.c_str() << " / Category: " << mc.second;
QByteArray modelBuffer;
QString filename = "MODELS/" + QString::fromStdString(mc.first);
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[modelIdx];
model.category = mc.second;
model.modelIndex = modelIdx;
strncpy(model.filename, mc.first.c_str(), sizeof(model.filename)-1);
if (!loadModelFromYaml(radioData.models[modelIdx],modelBuffer)) {
setError(tr("Can't load ") + filename);
return false;
}
modelIdx++;
}
} else {
// fetch "MODELS/modelXX.yml"
}
return true;
}