Files
edgetx-gx12/companion/src/firmwares/edgetx/yaml_modeldata.cpp
T

499 lines
13 KiB
C++

/*
* 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 "yaml_rawswitch.h"
#include "yaml_mixdata.h"
#include "yaml_rawsource.h"
#include "yaml_expodata.h"
#include "yaml_curvedata.h"
#include "yaml_moduledata.h"
#include "yaml_logicalswitchdata.h"
#include "yaml_customfunctiondata.h"
#include "modeldata.h"
#include "output_data.h"
#include "eeprominterface.h"
#include <string>
static 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"},
};
static 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" },
};
static const YamlLookupTable swashTypeLut = {
{ 0, "TYPE_NONE" },
{ 1, "TYPE_120" },
{ 2, "TYPE_120X" },
{ 3, "TYPE_140" },
{ 4, "TYPE_90" },
};
struct YamlTrim {
int mode = 0;
int ref = 0;
int value = 0;
YamlTrim() = default;
YamlTrim(int mode, int ref, int value):
mode(mode), ref(ref), value(value)
{}
};
struct YamlThrTrace {
RawSource src;
YamlThrTrace() = default;
YamlThrTrace(unsigned int cpn_value)
{
if (cpn_value == 0) {
src = RawSource(SOURCE_TYPE_STICK, 2/* throttle */);
return;
}
cpn_value--;
Boards board(getCurrentBoard());
int pots = board.getCapability(Board::Pots);
int sliders = board.getCapability(Board::Sliders);
if (cpn_value < (unsigned int)(pots + sliders)) {
src = RawSource(SOURCE_TYPE_STICK, 4/* sticks */ + cpn_value);
}
else {
cpn_value -= pots + sliders;
src = RawSource(SOURCE_TYPE_CH, cpn_value);
}
}
unsigned int toCpn()
{
switch (src.type) {
case SOURCE_TYPE_STICK:
if (src.index == 2 /* throttle */) {
return 0;
} else {
return src.index - 4/* sticks */ + 1;
}
break;
case SOURCE_TYPE_CH: {
Boards board(getCurrentBoard());
int pots = board.getCapability(Board::Pots);
int sliders = board.getCapability(Board::Sliders);
return pots + sliders + src.index;
} break;
default:
break;
}
return 0;
}
};
namespace YAML
{
Node convert<TimerData>::encode(const TimerData& rhs)
{
Node node;
node["swtch"] = rhs.swtch;
node["mode"] = timerModeLut << rhs.mode;
node["name"] = rhs.name;
node["minuteBeep"] = (int)rhs.minuteBeep;
node["countdownBeep"] = rhs.countdownBeep;
node["start"] = rhs.val;
node["persistent"] = rhs.persistent;
node["countdownStart"] = rhs.countdownStart;
node["value"] = rhs.pvalue;
return node;
}
bool convert<TimerData>::decode(const Node& node, TimerData& rhs)
{
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<LimitData> {
static Node encode(const LimitData& rhs)
{
Node node;
node["min"] = rhs.min;
node["max"] = rhs.max;
node["revert"] = rhs.revert;
node["offset"] = rhs.offset;
node["ppmCenter"] = rhs.ppmCenter;
node["symetrical"] = rhs.symetrical;
node["failsafe"] = rhs.failsafe;
node["name"] = rhs.name;
node["curve"] = rhs.curve;
return node;
}
static bool decode(const Node& node, LimitData& rhs)
{
node["min"] >> rhs.min;
node["max"] >> rhs.max;
node["revert"] >> rhs.revert;
node["offset"] >> rhs.offset;
node["ppmCenter"] >> rhs.ppmCenter;
node["symetrical"] >> rhs.symetrical;
node["failsafe"] >> rhs.failsafe;
node["name"] >> rhs.name;
node["curve"] >> rhs.curve;
return true;
}
};
template <>
struct convert<YamlTrim> {
static Node encode(const YamlTrim& rhs)
{
Node node;
node["value"] = rhs.value;
if (rhs.mode < 0) {
node["mode"] = -1;
} else {
node["mode"] = 2 * rhs.ref + rhs.mode;
}
return node;
}
static bool decode(const Node& node, YamlTrim& rhs)
{
node["value"] >> rhs.value;
int trimMode = 0;
node["mode"] >> trimMode;
if (trimMode < 0) {
rhs.mode = trimMode;
} else {
rhs.mode = trimMode % 2;
rhs.ref = trimMode / 2;
}
return true;
}
};
template <>
struct convert<FlightModeData> {
static Node encode(const FlightModeData& rhs)
{
Node node;
// TODO: use max number of trims for radio instead
YamlTrim yt[CPN_MAX_TRIMS];
for (int i=0; i<CPN_MAX_TRIMS; i++) {
yt[i] = { rhs.trimMode[i], rhs.trimRef[i], rhs.trim[i] };
}
node["trim"] = convert_array<YamlTrim,CPN_MAX_TRIMS>::encode(yt);
node["swtch"] = rhs.swtch;
node["name"] = rhs.name;
node["fadeIn"] = rhs.fadeIn;
node["fadeOut"] = rhs.fadeOut;
node["gvars"] = convert_array<int,CPN_MAX_GVARS>::encode(rhs.gvars);
return node;
}
static bool decode(const Node& node, FlightModeData& rhs)
{
YamlTrim trims[CPN_MAX_TRIMS];
node["trim"] >> trims;
for (size_t i=0; i<CPN_MAX_TRIMS; i++) {
rhs.trimMode[i] = trims[i].mode;
rhs.trimRef[i] = trims[i].ref;
rhs.trim[i] = trims[i].value;
}
node["swtch"] >> rhs.swtch;
node["name"] >> rhs.name;
node["fadeIn"] >> rhs.fadeIn;
node["fadeOut"] >> rhs.fadeOut;
node["gvars"] >> rhs.gvars;
return true;
}
};
template <>
struct convert<SwashRingData> {
static Node encode(const SwashRingData& rhs)
{
Node node;
node["type"] = swashTypeLut << rhs.type;
node["value"] = rhs.value;
node["collectiveSource"] = rhs.collectiveSource;
node["aileronSource"] = rhs.aileronSource;
node["elevatorSource"] = rhs.elevatorSource;
node["collectiveWeight"] = rhs.collectiveWeight;
node["aileronWeight"] = rhs.aileronWeight;
node["elevatorWeight"] = rhs.elevatorWeight;
return node;
}
static bool decode(const Node& node, SwashRingData& rhs)
{
node["type"] >> swashTypeLut >> rhs.type;
node["value"] >> rhs.value;
node["collectiveSource"] >> rhs.collectiveSource;
node["aileronSource"] >> rhs.aileronSource;
node["elevatorSource"] >> rhs.elevatorSource;
node["collectiveWeight"] >> rhs.collectiveWeight;
node["aileronWeight"] >> rhs.aileronWeight;
node["elevatorWeight"] >> rhs.elevatorWeight;
return true;
}
};
Node convert<ModelData>::encode(const ModelData& rhs)
{
Node node;
node["header"]["name"] = rhs.name;
node["header"]["bitmap"] = rhs.bitmap;
// TODO: modelId
for (int i=0; i<CPN_MAX_TIMERS; i++) {
Node timer;
timer = rhs.timers[i];
node["timers"][std::to_string(i)] = timer;
}
node["noGlobalFunctions"] = (int)rhs.noGlobalFunctions;
node["thrTrim"] = (int)rhs.thrTrim;
node["trimInc"] = rhs.trimInc;
node["displayTrims"] = rhs.trimsDisplay;
node["disableThrottleWarning"] = (int)rhs.disableThrottleWarning;
node["beepANACenter"] = rhs.beepANACenter;
node["extendedLimits"] = (int)rhs.extendedLimits;
node["extendedTrims"] = (int)rhs.extendedTrims;
node["throttleReversed"] = (int)rhs.throttleReversed;
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
Node fmNode;
fmNode = rhs.flightModeData[i];
node["flightModeData"].push_back(fmNode);
}
for (int i = 0; i < CPN_MAX_MIXERS; i++) {
const MixData& mix = rhs.mixData[i];
if (!mix.isEmpty()) {
Node mixNode;
mixNode = mix;
node["mixData"].push_back(mixNode);
}
}
for (int i = 0; i < CPN_MAX_CHNOUT; i++) {
const LimitData& limit = rhs.limitData[i];
if (!limit.isEmpty()) {
Node limitNode;
limitNode = limit;
node["limitData"].push_back(limitNode);
}
}
std::set<int> inputs;
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
const ExpoData& expo = rhs.expoData[i];
if (!expo.isEmpty()) {
Node expoNode;
expoNode = expo;
node["expoData"].push_back(expoNode);
inputs.insert(expo.chn);
}
}
for (auto input : inputs) {
node["inputNames"][std::to_string(input)]["val"] = rhs.inputNames[input];
}
for (int i = 0; i < CPN_MAX_CURVES; i++) {
const CurveData& curve = rhs.curves[i];
if (!curve.isEmpty()) {
node["curves"][std::to_string(i)] = curve;
}
}
YAML::Node points;
YamlWriteCurvePoints(points, rhs.curves);
if (points && (points.IsMap() || points.IsSequence())) {
node["points"] = points;
}
for (int i = 0; i < CPN_MAX_LOGICAL_SWITCHES; i++) {
const LogicalSwitchData& ls = rhs.logicalSw[i];
if (!ls.isEmpty()) {
node["logicalSw"][std::to_string(i)] = ls;
}
}
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;
}
}
if (getCurrentFirmware()->getCapability(Heli))
node["swashR"] = rhs.swashRingData;
YamlThrTrace thrTrace(rhs.thrTraceSrc);
node["thrTraceSrc"] = thrTrace.src;
// switchWarningStates
node["thrTrimSw"] = rhs.thrTrimSwitch;
// potsWarningMode
// potsWarnEnabled[]
// potsWarnPosition[]
node["displayChecklist"] = (int)rhs.displayChecklist;
// gvarData[]
// mavlink ???
// telemetryProtocol ???
// frsky
// rssiSource
// rssiAlarms
node["trainerMode"] = trainerModeLut << rhs.trainerMode;
for (int i=0; i<CPN_MAX_MODULES; i++) {
if (rhs.moduleData[i].protocol != PULSES_OFF) {
node["moduleData"][std::to_string(i)] = rhs.moduleData[i];
}
}
// scriptData[]
// sensorData[]
node["toplcdTimer"] = rhs.toplcdTimer;
// B&W only
// customScreens
// topBarData
node["view"] = rhs.view;
node["modelRegistrationID"] = rhs.registrationId;
return node;
}
bool convert<ModelData>::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["beepANACenter"] >> rhs.beepANACenter;
node["extendedLimits"] >> rhs.extendedLimits;
node["extendedTrims"] >> rhs.extendedTrims;
node["throttleReversed"] >> rhs.throttleReversed;
node["flightModeData"] >> rhs.flightModeData;
node["mixData"] >> rhs.mixData;
node["limitData"] >> rhs.limitData;
node["inputNames"] >> rhs.inputNames;
node["expoData"] >> rhs.expoData;
node["curves"] >> rhs.curves;
YamlReadCurvePoints(node["points"], rhs.curves);
node["logicalSw"] >> rhs.logicalSw;
node["customFn"] >> rhs.customFn;
if (node["swashR"]) {
const auto& swashR = node["swashR"];
if (!swashR.IsMap()) return false;
swashR >> rhs.swashRingData;
}
YamlThrTrace thrTrace;
node["thrTraceSrc"] >> thrTrace.src;
rhs.thrTraceSrc = thrTrace.toCpn();
// switchWarningStates
node["thrTrimSw"] >> rhs.thrTrimSwitch;
// potsWarningMode
// potsWarnEnabled[]
// potsWarnPosition[]
node["displayChecklist"] >> rhs.displayChecklist;
// gvarData[]
// mavlink ???
// telemetryProtocol ???
// frsky
// rssiSource
// rssiAlarms
node["trainerMode"] >> trainerModeLut >> rhs.trainerMode;
node["moduleData"] >> rhs.moduleData;
// scriptData[]
// sensorData[]
node["toplcdTimer"] >> rhs.toplcdTimer;
// customScreens
// topBarData
node["view"] >> rhs.view;
node["modelRegistrationID"] >> rhs.registrationId;
return true;
}
} // namespace YAML