WiP: Add model loading
This commit is contained in:
@@ -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
|
||||
@@ -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> { \
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user