FactMetaData.cc 55.8 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

Don Gagne's avatar
Don Gagne committed
10
#include "FactMetaData.h"
11
#include "SettingsManager.h"
12
#include "JsonHelper.h"
13
#include "QGCApplication.h"
Don Gagne's avatar
Don Gagne committed
14

15
#include <QDebug>
16
#include <QtMath>
17 18
#include <QJsonParseError>
#include <QJsonArray>
19

Don Gagne's avatar
Don Gagne committed
20
#include <limits>
21 22
#include <cmath>

23 24 25 26 27 28 29 30
// Conversion Constants
// Time
const qreal FactMetaData::UnitConsts_s::secondsPerHour = 3600.0;

// Velocity
const qreal FactMetaData::UnitConsts_s::knotsToKPH = 1.852; // exact, hence weird base for knotsToMetersPerSecond

// Length
31 32 33
const qreal FactMetaData::UnitConsts_s::milesToMeters =         1609.344;
const qreal FactMetaData::UnitConsts_s::feetToMeters =          0.3048;
const qreal FactMetaData::UnitConsts_s::inchesToCentimeters =   2.54;
34

35 36 37 38
//Weight
const qreal FactMetaData::UnitConsts_s::ouncesToGrams =         28.3495;
const qreal FactMetaData::UnitConsts_s::poundsToGrams =         453.592;

39 40
const char* FactMetaData::kDefaultCategory = QT_TRANSLATE_NOOP("FactMetaData", "Other");
const char* FactMetaData::kDefaultGroup    = QT_TRANSLATE_NOOP("FactMetaData", "Misc");
41

42
const char* FactMetaData::qgcFileType =                 "FactMetaData";
43 44 45
const char* FactMetaData::_jsonMetaDataDefinesName =    "QGC.MetaData.Defines";
const char* FactMetaData::_jsonMetaDataFactsName =      "QGC.MetaData.Facts";

46 47
// Built in translations for all Facts
const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] = {
48 49 50 51
    { "centi-degrees",  "deg",  FactMetaData::_centiDegreesToDegrees,                   FactMetaData::_degreesToCentiDegrees },
    { "radians",        "deg",  FactMetaData::_radiansToDegrees,                        FactMetaData::_degreesToRadians },
    { "gimbal-degrees", "deg",  FactMetaData::_mavlinkGimbalDegreesToUserGimbalDegrees, FactMetaData::_userGimbalDegreesToMavlinkGimbalDegrees },
    { "norm",           "%",    FactMetaData::_normToPercent,                           FactMetaData::_percentToNorm },
52
};
Don Gagne's avatar
Don Gagne committed
53

54 55
// Translations driven by app settings
const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = {
Remek Zajac's avatar
Remek Zajac committed
56 57 58 59 60 61 62 63 64 65 66 67
    { "m",      "m",        FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "meter",  "meter",    FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "meters", "meters",   FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    //NOTE: we've coined an artificial "raw unit" of "vertical metre" to separate it from the horizontal metre - a bit awkward but this is all the design permits
    { "vertical m",  "m",   FactMetaData::UnitVerticalDistance,      UnitsSettings::VerticalDistanceUnitsMeters,   FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "cm/px",  "cm/px",    FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "m/s",    "m/s",      FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsMetersPerSecond,     FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "C",      "C",        FactMetaData::UnitTemperature,           UnitsSettings::TemperatureUnitsCelsius,       FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "m^2",    "m\u00B2",  FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareMeters,         FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "m",      "ft",       FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsFeet,   FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "meter",  "ft",       FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsFeet,   FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "meters", "ft",       FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsFeet,   FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
Remek Zajac's avatar
Remek Zajac committed
68
    { "vertical m",  "ft",  FactMetaData::UnitVerticalDistance,      UnitsSettings::VerticalDistanceUnitsFeet,     FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
Remek Zajac's avatar
Remek Zajac committed
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    { "cm/px",  "in/px",    FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsFeet,   FactMetaData::_centimetersToInches,                 FactMetaData::_inchesToCentimeters },
    { "m^2",    "km\u00B2", FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareKilometers,     FactMetaData::_squareMetersToSquareKilometers,      FactMetaData::_squareKilometersToSquareMeters },
    { "m^2",    "ha",       FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsHectares,             FactMetaData::_squareMetersToHectares,              FactMetaData::_hectaresToSquareMeters },
    { "m^2",    "ft\u00B2", FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareFeet,           FactMetaData::_squareMetersToSquareFeet,            FactMetaData::_squareFeetToSquareMeters },
    { "m^2",    "ac",       FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsAcres,                FactMetaData::_squareMetersToAcres,                 FactMetaData::_acresToSquareMeters },
    { "m^2",    "mi\u00B2", FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareMiles,          FactMetaData::_squareMetersToSquareMiles,           FactMetaData::_squareMilesToSquareMeters },
    { "m/s",    "ft/s",     FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsFeetPerSecond,       FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "m/s",    "mph",      FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsMilesPerHour,        FactMetaData::_metersPerSecondToMilesPerHour,       FactMetaData::_milesPerHourToMetersPerSecond },
    { "m/s",    "km/h",     FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsKilometersPerHour,   FactMetaData::_metersPerSecondToKilometersPerHour,  FactMetaData::_kilometersPerHourToMetersPerSecond },
    { "m/s",    "kn",       FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsKnots,               FactMetaData::_metersPerSecondToKnots,              FactMetaData::_knotsToMetersPerSecond },
    { "C",      "F",        FactMetaData::UnitTemperature,           UnitsSettings::TemperatureUnitsFarenheit,     FactMetaData::_celsiusToFarenheit,                  FactMetaData::_farenheitToCelsius },
    { "g",      "g",        FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsGrams,              FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "g",      "kg",       FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsKg,                 FactMetaData::_gramsToKilograms,                    FactMetaData::_kilogramsToGrams },
    { "g",      "oz",       FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsOz,                 FactMetaData::_gramsToOunces,                       FactMetaData::_ouncesToGrams },
    { "g",      "lbs",      FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsLbs,                FactMetaData::_gramsToPunds,                        FactMetaData::_poundsToGrams },
84 85
};

DonLakeFlyer's avatar
DonLakeFlyer committed
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
const char* FactMetaData::_rgKnownTypeStrings[] = {
    "Uint8",
    "Int8",
    "Uint16",
    "Int16",
    "Uint32",
    "Int32",
    "Uint64",
    "Int64",
    "Float",
    "Double",
    "String",
    "Bool",
    "ElapsedSeconds",
    "Custom",
};

const  FactMetaData::ValueType_t FactMetaData::_rgKnownValueTypes[] = {
    valueTypeUint8,
    valueTypeInt8,
    valueTypeUint16,
    valueTypeInt16,
    valueTypeUint32,
    valueTypeInt32,
    valueTypeUint64,
    valueTypeInt64,
    valueTypeFloat,
    valueTypeDouble,
    valueTypeString,
    valueTypeBool,
    valueTypeElapsedTimeInSeconds,
    valueTypeCustom,
};

120 121 122
const char* FactMetaData::_decimalPlacesJsonKey =       "decimalPlaces";
const char* FactMetaData::_nameJsonKey =                "name";
const char* FactMetaData::_typeJsonKey =                "type";
123 124
const char* FactMetaData::_shortDescriptionJsonKey =    "shortDesc";
const char* FactMetaData::_longDescriptionJsonKey =     "longDesc";
125
const char* FactMetaData::_unitsJsonKey =               "units";
126 127
const char* FactMetaData::_defaultValueJsonKey =        "default";
const char* FactMetaData::_mobileDefaultValueJsonKey =  "mobileDefault";
128 129
const char* FactMetaData::_minJsonKey =                 "min";
const char* FactMetaData::_maxJsonKey =                 "max";
130
const char* FactMetaData::_incrementJsonKey =           "increment";
131
const char* FactMetaData::_hasControlJsonKey =          "control";
132
const char* FactMetaData::_qgcRebootRequiredJsonKey =   "qgcRebootRequired";
133 134 135
const char* FactMetaData::_categoryJsonKey =            "category";
const char* FactMetaData::_groupJsonKey =               "group";
const char* FactMetaData::_volatileJsonKey =            "volatile";
136

137
FactMetaData::FactMetaData(QObject* parent)
138 139
    : QObject               (parent)
    , _type                 (valueTypeInt32)
140
    , _decimalPlaces        (kUnknownDecimalPlaces)
141
    , _rawDefaultValue      (0)
142
    , _defaultValueAvailable(false)
143 144 145 146 147 148
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
149 150
    , _vehicleRebootRequired(false)
    , _qgcRebootRequired    (false)
151
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
152 153
    , _hasControl           (true)
    , _readOnly             (false)
154
    , _writeOnly            (false)
155
    , _volatile             (false)
Don Gagne's avatar
Don Gagne committed
156
{
157 158
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
Don Gagne's avatar
Don Gagne committed
159 160
}

161
FactMetaData::FactMetaData(ValueType_t type, QObject* parent)
162 163
    : QObject               (parent)
    , _type                 (type)
164
    , _decimalPlaces        (kUnknownDecimalPlaces)
165
    , _rawDefaultValue      (0)
166
    , _defaultValueAvailable(false)
167 168 169 170 171 172
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
173 174
    , _vehicleRebootRequired(false)
    , _qgcRebootRequired    (false)
175
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
176 177
    , _hasControl           (true)
    , _readOnly             (false)
178
    , _writeOnly            (false)
179
    , _volatile             (false)
180
{
181 182
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
183 184
}

Don Gagne's avatar
Don Gagne committed
185 186 187 188 189 190
FactMetaData::FactMetaData(const FactMetaData& other, QObject* parent)
    : QObject(parent)
{
    *this = other;
}

191
FactMetaData::FactMetaData(ValueType_t type, const QString name, QObject* parent)
192 193
    : QObject               (parent)
    , _type                 (type)
194
    , _decimalPlaces        (kUnknownDecimalPlaces)
195
    , _rawDefaultValue      (0)
196
    , _defaultValueAvailable(false)
197 198 199 200 201 202 203
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _name                 (name)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
204 205
    , _vehicleRebootRequired(false)
    , _qgcRebootRequired    (false)
206
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
207 208
    , _hasControl           (true)
    , _readOnly             (false)
209
    , _writeOnly            (false)
210
    , _volatile             (false)
211
{
212 213
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
214 215
}

Don Gagne's avatar
Don Gagne committed
216 217
const FactMetaData& FactMetaData::operator=(const FactMetaData& other)
{
218
    _decimalPlaces          = other._decimalPlaces;
219
    _rawDefaultValue        = other._rawDefaultValue;
Don Gagne's avatar
Don Gagne committed
220
    _defaultValueAvailable  = other._defaultValueAvailable;
221 222
    _bitmaskStrings         = other._bitmaskStrings;
    _bitmaskValues          = other._bitmaskValues;
223 224
    _enumStrings            = other._enumStrings;
    _enumValues             = other._enumValues;
225
    _category               = other._category;
226 227
    _group                  = other._group;
    _longDescription        = other._longDescription;
228
    _rawMax                 = other._rawMax;
Don Gagne's avatar
Don Gagne committed
229
    _maxIsDefaultForType    = other._maxIsDefaultForType;
230
    _rawMin                 = other._rawMin;
231 232 233 234
    _minIsDefaultForType    = other._minIsDefaultForType;
    _name                   = other._name;
    _shortDescription       = other._shortDescription;
    _type                   = other._type;
235 236
    _rawUnits               = other._rawUnits;
    _cookedUnits            = other._cookedUnits;
237 238
    _rawTranslator          = other._rawTranslator;
    _cookedTranslator       = other._cookedTranslator;
239 240
    _vehicleRebootRequired  = other._vehicleRebootRequired;
    _qgcRebootRequired      = other._qgcRebootRequired;
241
    _rawIncrement           = other._rawIncrement;
242
    _hasControl             = other._hasControl;
243
    _readOnly               = other._readOnly;
244
    _writeOnly              = other._writeOnly;
245
    _volatile               = other._volatile;
Don Gagne's avatar
Don Gagne committed
246 247 248
    return *this;
}

249 250 251 252 253 254 255 256 257 258
const QString FactMetaData::defaultCategory()
{
    return QString(kDefaultCategory);
}

const QString FactMetaData::defaultGroup()
{
    return QString(kDefaultGroup);
}

259
QVariant FactMetaData::rawDefaultValue(void) const
260 261
{
    if (_defaultValueAvailable) {
262
        return _rawDefaultValue;
263 264 265 266 267 268
    } else {
        qWarning() << "Attempt to access unavailable default value";
        return QVariant(0);
    }
}

269
void FactMetaData::setRawDefaultValue(const QVariant& rawDefaultValue)
270
{
271
    if (_type == valueTypeString || (isInRawMinLimit(rawDefaultValue) && isInRawMaxLimit(rawDefaultValue))) {
272
        _rawDefaultValue = rawDefaultValue;
273 274 275 276 277 278
        _defaultValueAvailable = true;
    } else {
        qWarning() << "Attempt to set default value which is outside min/max range";
    }
}

279
void FactMetaData::setRawMin(const QVariant& rawMin)
280
{
281
    if (isInRawMinLimit(rawMin)) {
282
        _rawMin = rawMin;
Don Gagne's avatar
Don Gagne committed
283
        _minIsDefaultForType = false;
284
    } else {
285
        qWarning() << "Attempt to set min below allowable value for fact: " << name()
286
                   << ", value attempted: " << rawMin
287
                   << ", type: " << type() << ", min for type: " << _minForType();
288
        _rawMin = _minForType();
289 290 291
    }
}

292
void FactMetaData::setRawMax(const QVariant& rawMax)
293
{
294
    if (isInRawMaxLimit(rawMax)) {
295
        _rawMax = rawMax;
Don Gagne's avatar
Don Gagne committed
296
        _maxIsDefaultForType = false;
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
    } else {
        qWarning() << "Attempt to set max above allowable value";
        _rawMax = _maxForType();
    }
}

bool FactMetaData::isInRawMinLimit(const QVariant& variantValue) const
{
    switch (_type) {
    case valueTypeUint8:
        return _rawMin.value<unsigned char>() <= variantValue.value<unsigned char>();
    case valueTypeInt8:
        return _rawMin.value<signed char>() <= variantValue.value<signed char>();
    case valueTypeUint16:
        return _rawMin.value<unsigned short int>() <= variantValue.value<unsigned short int>();
    case valueTypeInt16:
        return _rawMin.value<short int>() <= variantValue.value<short int>();
    case valueTypeUint32:
        return _rawMin.value<uint32_t>() <= variantValue.value<uint32_t>();
    case valueTypeInt32:
        return _rawMin.value<int32_t>() <= variantValue.value<int32_t>();
    case valueTypeUint64:
        return _rawMin.value<uint64_t>() <= variantValue.value<uint64_t>();
    case valueTypeInt64:
        return _rawMin.value<int64_t>() <= variantValue.value<int64_t>();
    case valueTypeFloat:
323
        return qIsNaN(variantValue.toFloat()) || _rawMin.value<float>() <= variantValue.value<float>();
324
    case valueTypeDouble:
325
        return qIsNaN(variantValue.toDouble()) || _rawMin.value<double>() <= variantValue.value<double>();
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    default:
        return true;
    }

    return true;
}

bool FactMetaData::isInRawMaxLimit(const QVariant& variantValue) const
{
    switch (_type) {
    case valueTypeUint8:
        return _rawMax.value<unsigned char>() >= variantValue.value<unsigned char>();
    case valueTypeInt8:
        return _rawMax.value<signed char>() >= variantValue.value<signed char>();
    case valueTypeUint16:
        return _rawMax.value<unsigned short int>() >= variantValue.value<unsigned short int>();
    case valueTypeInt16:
        return _rawMax.value<short int>() >= variantValue.value<short int>();
    case valueTypeUint32:
        return _rawMax.value<uint32_t>() >= variantValue.value<uint32_t>();
    case valueTypeInt32:
        return _rawMax.value<int32_t>() >= variantValue.value<int32_t>();
    case valueTypeUint64:
        return _rawMax.value<uint64_t>() >= variantValue.value<uint64_t>();
    case valueTypeInt64:
        return _rawMax.value<int64_t>() >= variantValue.value<int64_t>();
    case valueTypeFloat:
353
        return qIsNaN(variantValue.toFloat()) || _rawMax.value<float>() >= variantValue.value<float>();
354
    case valueTypeDouble:
355
        return qIsNaN(variantValue.toDouble()) || _rawMax.value<double>() >= variantValue.value<double>();
356 357
    default:
        return true;
358
    }
359 360

    return true;
361 362
}

363
QVariant FactMetaData::_minForType(void) const
Don Gagne's avatar
Don Gagne committed
364
{
365
    switch (_type) {
366 367 368 369 370 371 372 373 374
    case valueTypeUint8:
        return QVariant(std::numeric_limits<unsigned char>::min());
    case valueTypeInt8:
        return QVariant(std::numeric_limits<signed char>::min());
    case valueTypeUint16:
        return QVariant(std::numeric_limits<unsigned short int>::min());
    case valueTypeInt16:
        return QVariant(std::numeric_limits<short int>::min());
    case valueTypeUint32:
375
        return QVariant(std::numeric_limits<uint32_t>::min());
376
    case valueTypeInt32:
377 378
        return QVariant(std::numeric_limits<int32_t>::min());
    case valueTypeUint64:
379
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::min());
380
    case valueTypeInt64:
381
        return QVariant((qlonglong)std::numeric_limits<int64_t>::min());
382 383 384 385
    case valueTypeFloat:
        return QVariant(-std::numeric_limits<float>::max());
    case valueTypeDouble:
        return QVariant(-std::numeric_limits<double>::max());
Don Gagne's avatar
Don Gagne committed
386 387
    case valueTypeString:
        return QVariant();
388 389
    case valueTypeBool:
        return QVariant(0);
390 391
    case valueTypeElapsedTimeInSeconds:
        return QVariant(0.0);
392 393
    case valueTypeCustom:
        return QVariant();
394
    }
395

Don Gagne's avatar
Don Gagne committed
396 397
    // Make windows compiler happy, even switch is full cased
    return QVariant();
Don Gagne's avatar
Don Gagne committed
398
}
Don Gagne's avatar
Don Gagne committed
399

400
QVariant FactMetaData::_maxForType(void) const
Don Gagne's avatar
Don Gagne committed
401
{
402
    switch (_type) {
403 404 405 406 407 408 409 410 411
    case valueTypeUint8:
        return QVariant(std::numeric_limits<unsigned char>::max());
    case valueTypeInt8:
        return QVariant(std::numeric_limits<signed char>::max());
    case valueTypeUint16:
        return QVariant(std::numeric_limits<unsigned short int>::max());
    case valueTypeInt16:
        return QVariant(std::numeric_limits<short int>::max());
    case valueTypeUint32:
412
        return QVariant(std::numeric_limits<uint32_t>::max());
413
    case valueTypeInt32:
414 415
        return QVariant(std::numeric_limits<int32_t>::max());
    case valueTypeUint64:
416
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::max());
417
    case valueTypeInt64:
418
        return QVariant((qlonglong)std::numeric_limits<int64_t>::max());
419 420
    case valueTypeFloat:
        return QVariant(std::numeric_limits<float>::max());
421
    case valueTypeElapsedTimeInSeconds:
422 423
    case valueTypeDouble:
        return QVariant(std::numeric_limits<double>::max());
Don Gagne's avatar
Don Gagne committed
424 425
    case valueTypeString:
        return QVariant();
426 427
    case valueTypeBool:
        return QVariant(1);
428 429
    case valueTypeCustom:
        return QVariant();
430
    }
431

Don Gagne's avatar
Don Gagne committed
432 433
    // Make windows compiler happy, even switch is full cased
    return QVariant();
Don Gagne's avatar
Don Gagne committed
434
}
Don Gagne's avatar
Don Gagne committed
435

436
bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertOnly, QVariant& typedValue, QString& errorString)
Don Gagne's avatar
Don Gagne committed
437
{
438
    bool convertOk = false;
439

Don Gagne's avatar
Don Gagne committed
440
    errorString.clear();
441

Don Gagne's avatar
Don Gagne committed
442
    switch (type()) {
443 444 445 446 447
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(rawValue.toInt(&convertOk));
        if (!convertOnly && convertOk) {
448
            if (!isInRawLimit<int32_t>(typedValue)) {
449
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
Don Gagne's avatar
Don Gagne committed
450
            }
451 452
        }
        break;
453 454 455
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(rawValue.toLongLong(&convertOk));
        if (!convertOnly && convertOk) {
456
            if (!isInRawLimit<int64_t>(typedValue)) {
457 458 459 460
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
            }
        }
        break;
461 462 463 464 465
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(rawValue.toUInt(&convertOk));
        if (!convertOnly && convertOk) {
466
            if (!isInRawLimit<uint32_t>(typedValue)) {
467
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
Don Gagne's avatar
Don Gagne committed
468
            }
469 470
        }
        break;
471 472 473
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(rawValue.toULongLong(&convertOk));
        if (!convertOnly && convertOk) {
474
            if (!isInRawLimit<uint64_t>(typedValue)) {
475 476 477 478
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
            }
        }
        break;
479 480 481
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(rawValue.toFloat(&convertOk));
        if (!convertOnly && convertOk) {
482
            if (!isInRawLimit<float>(typedValue)) {
483
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
Don Gagne's avatar
Don Gagne committed
484
            }
485 486
        }
        break;
487
    case FactMetaData::valueTypeElapsedTimeInSeconds:
488 489 490
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(rawValue.toDouble(&convertOk));
        if (!convertOnly && convertOk) {
491
            if (!isInRawLimit<double>(typedValue)) {
492
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
Don Gagne's avatar
Don Gagne committed
493
            }
494 495
        }
        break;
Don Gagne's avatar
Don Gagne committed
496 497 498 499
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(rawValue.toString());
        break;
500 501 502 503
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(rawValue.toBool());
        break;
504 505 506 507
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(rawValue.toByteArray());
        break;
Don Gagne's avatar
Don Gagne committed
508
    }
509

Don Gagne's avatar
Don Gagne committed
510
    if (!convertOk) {
511
        errorString += tr("Invalid number");
Don Gagne's avatar
Don Gagne committed
512
    }
513

Don Gagne's avatar
Don Gagne committed
514 515
    return convertOk && errorString.isEmpty();
}
516

517 518 519 520 521 522
bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool convertOnly, QVariant& typedValue, QString& errorString)
{
    bool convertOk = false;

    errorString.clear();

523 524 525 526 527 528 529
    if (!convertOnly && _customCookedValidator) {
        errorString = _customCookedValidator(cookedValue);
        if (!errorString.isEmpty()) {
            return false;
        }
    }

530
    switch (type()) {
531 532 533 534 535
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(cookedValue.toInt(&convertOk));
        if (!convertOnly && convertOk) {
536
            if (!isInCookedLimit<int32_t>(typedValue)) {
537
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
538
            }
539 540
        }
        break;
541 542 543
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(cookedValue.toLongLong(&convertOk));
        if (!convertOnly && convertOk) {
544
            if (!isInCookedLimit<int64_t>(typedValue)) {
545 546 547 548
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
            }
        }
        break;
549 550 551 552 553
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(cookedValue.toUInt(&convertOk));
        if (!convertOnly && convertOk) {
554
            if (!isInCookedLimit<uint32_t>(typedValue)) {
555
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
556
            }
557 558
        }
        break;
559 560 561
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(cookedValue.toULongLong(&convertOk));
        if (!convertOnly && convertOk) {
562
            if (!isInCookedLimit<uint64_t>(typedValue)) {
563 564 565 566
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
            }
        }
        break;
567 568 569
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(cookedValue.toFloat(&convertOk));
        if (!convertOnly && convertOk) {
570
            if (!isInCookedLimit<float>(typedValue)) {
571
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
572
            }
573 574
        }
        break;
575
    case FactMetaData::valueTypeElapsedTimeInSeconds:
576 577 578
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(cookedValue.toDouble(&convertOk));
        if (!convertOnly && convertOk) {
579
            if (!isInCookedLimit<double>(typedValue)) {
580
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
581
            }
582 583
        }
        break;
Don Gagne's avatar
Don Gagne committed
584 585 586 587
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(cookedValue.toString());
        break;
588 589 590 591
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(cookedValue.toBool());
        break;
592 593 594 595
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
596 597 598
    }

    if (!convertOk) {
599
        errorString += tr("Invalid number");
600 601 602 603 604
    }

    return convertOk && errorString.isEmpty();
}

605 606 607 608 609 610 611 612 613
bool FactMetaData::clampValue(const QVariant& cookedValue, QVariant& typedValue)
{
    bool convertOk = false;
    switch (type()) {
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(cookedValue.toInt(&convertOk));
        if (convertOk) {
614
            clamp<int32_t>(typedValue);
615 616
        }
        break;
617 618 619
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(cookedValue.toLongLong(&convertOk));
        if (convertOk) {
620
            clamp<int64_t>(typedValue);
621 622
        }
        break;
623 624 625 626 627
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(cookedValue.toUInt(&convertOk));
        if (convertOk) {
628
            clamp<uint32_t>(typedValue);
629 630
        }
        break;
631 632 633
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(cookedValue.toULongLong(&convertOk));
        if (convertOk) {
634
            clamp<uint64_t>(typedValue);
635 636
        }
        break;
637 638 639
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(cookedValue.toFloat(&convertOk));
        if (convertOk) {
640
            clamp<float>(typedValue);
641 642 643 644 645 646
        }
        break;
    case FactMetaData::valueTypeElapsedTimeInSeconds:
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(cookedValue.toDouble(&convertOk));
        if (convertOk) {
647
            clamp<double>(typedValue);
648 649 650 651 652 653 654 655 656 657
        }
        break;
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(cookedValue.toString());
        break;
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(cookedValue.toBool());
        break;
658 659 660 661
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
662 663 664 665
    }
    return convertOk;
}

666 667 668 669 670 671 672 673 674
void FactMetaData::setBitmaskInfo(const QStringList& strings, const QVariantList& values)
{
    if (strings.count() != values.count()) {
        qWarning() << "Count mismatch strings:values" << strings.count() << values.count();
        return;
    }

    _bitmaskStrings = strings;
    _bitmaskValues = values;
675
    setBuiltInTranslator();
676 677 678 679 680 681 682 683
}

void FactMetaData::addBitmaskInfo(const QString& name, const QVariant& value)
{
    _bitmaskStrings << name;
    _bitmaskValues << value;
}

684 685 686 687 688 689 690 691 692
void FactMetaData::setEnumInfo(const QStringList& strings, const QVariantList& values)
{
    if (strings.count() != values.count()) {
        qWarning() << "Count mismatch strings:values" << strings.count() << values.count();
        return;
    }

    _enumStrings = strings;
    _enumValues = values;
693
    setBuiltInTranslator();
694 695
}

696 697 698 699 700 701
void FactMetaData::addEnumInfo(const QString& name, const QVariant& value)
{
    _enumStrings << name;
    _enumValues << value;
}

702 703 704 705 706
void FactMetaData::setTranslators(Translator rawTranslator, Translator cookedTranslator)
{
    _rawTranslator = rawTranslator;
    _cookedTranslator = cookedTranslator;
}
707

708
void FactMetaData::setBuiltInTranslator(void)
709 710 711 712 713
{
    if (_enumStrings.count()) {
        // No translation if enum
        setTranslators(_defaultTranslator, _defaultTranslator);
        _cookedUnits = _rawUnits;
714
        return;
715
    } else {
716 717
        for (size_t i=0; i<sizeof(_rgBuiltInTranslations)/sizeof(_rgBuiltInTranslations[0]); i++) {
            const BuiltInTranslation_s* pBuiltInTranslation = &_rgBuiltInTranslations[i];
718

719
            if (pBuiltInTranslation->rawUnits.toLower() == _rawUnits.toLower()) {
720 721
                _cookedUnits = pBuiltInTranslation->cookedUnits;
                setTranslators(pBuiltInTranslation->rawTranslator, pBuiltInTranslation->cookedTranslator);
722
                return;
723 724 725
            }
        }
    }
726 727 728

    // Translator not yet set, try app settings translators
    _setAppSettingsTranslators();
729 730 731 732
}

QVariant FactMetaData::_degreesToRadians(const QVariant& degrees)
{
733
    return QVariant(qDegreesToRadians(degrees.toDouble()));
734 735 736 737
}

QVariant FactMetaData::_radiansToDegrees(const QVariant& radians)
{
738
    return QVariant(qRadiansToDegrees(radians.toDouble()));
739 740 741 742
}

QVariant FactMetaData::_centiDegreesToDegrees(const QVariant& centiDegrees)
{
743
    return QVariant(centiDegrees.toReal() / 100.0);
744 745 746 747
}

QVariant FactMetaData::_degreesToCentiDegrees(const QVariant& degrees)
{
748
    return QVariant(qRound(degrees.toReal() * 100.0));
749 750
}

751 752 753 754 755 756 757 758 759 760 761 762 763 764
QVariant FactMetaData::_userGimbalDegreesToMavlinkGimbalDegrees(const QVariant& userGimbalDegrees)
{
    // User facing gimbal degree values are from 0 (level) to 90 (straight down)
    // Mavlink gimbal degree values are from 0 (level) to -90 (straight down)
    return userGimbalDegrees.toDouble() * -1.0;
}

QVariant FactMetaData::_mavlinkGimbalDegreesToUserGimbalDegrees(const QVariant& mavlinkGimbalDegrees)
{
    // User facing gimbal degree values are from 0 (level) to 90 (straight down)
    // Mavlink gimbal degree values are from 0 (level) to -90 (straight down)
    return mavlinkGimbalDegrees.toDouble() * -1.0;
}

765 766
QVariant FactMetaData::_metersToFeet(const QVariant& meters)
{
767
    return QVariant(meters.toDouble() * 1.0/constants.feetToMeters);
768 769 770 771
}

QVariant FactMetaData::_feetToMeters(const QVariant& feet)
{
772
    return QVariant(feet.toDouble() * constants.feetToMeters);
773 774
}

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
QVariant FactMetaData::_squareMetersToSquareKilometers(const QVariant& squareMeters)
{
    return QVariant(squareMeters.toDouble() * 0.000001);
}

QVariant FactMetaData::_squareKilometersToSquareMeters(const QVariant& squareKilometers)
{
    return QVariant(squareKilometers.toDouble() * 1000000.0);
}

QVariant FactMetaData::_squareMetersToHectares(const QVariant& squareMeters)
{
    return QVariant(squareMeters.toDouble() * 0.0001);
}

QVariant FactMetaData::_hectaresToSquareMeters(const QVariant& hectares)
{
    return QVariant(hectares.toDouble() * 1000.0);
}

QVariant FactMetaData::_squareMetersToSquareFeet(const QVariant& squareMeters)
{
    return QVariant(squareMeters.toDouble() * 10.7639);
}

QVariant FactMetaData::_squareFeetToSquareMeters(const QVariant& squareFeet)
{
    return QVariant(squareFeet.toDouble() * 0.0929);
}

QVariant FactMetaData::_squareMetersToAcres(const QVariant& squareMeters)
{
    return QVariant(squareMeters.toDouble() * 0.000247105);
}

QVariant FactMetaData::_acresToSquareMeters(const QVariant& acres)
{
    return QVariant(acres.toDouble() * 4046.86);
}

QVariant FactMetaData::_squareMetersToSquareMiles(const QVariant& squareMeters)
{
    return QVariant(squareMeters.toDouble() * 3.86102e-7);
}

QVariant FactMetaData::_squareMilesToSquareMeters(const QVariant& squareMiles)
{
    return QVariant(squareMiles.toDouble() * 258999039.98855);
}

825 826
QVariant FactMetaData::_metersPerSecondToMilesPerHour(const QVariant& metersPerSecond)
{
827
    return QVariant((metersPerSecond.toDouble() * 1.0/constants.milesToMeters) * constants.secondsPerHour);
828 829 830 831
}

QVariant FactMetaData::_milesPerHourToMetersPerSecond(const QVariant& milesPerHour)
{
832
    return QVariant((milesPerHour.toDouble() * constants.milesToMeters) / constants.secondsPerHour);
833 834 835 836
}

QVariant FactMetaData::_metersPerSecondToKilometersPerHour(const QVariant& metersPerSecond)
{
837
    return QVariant((metersPerSecond.toDouble() / 1000.0) * constants.secondsPerHour);
838 839 840 841
}

QVariant FactMetaData::_kilometersPerHourToMetersPerSecond(const QVariant& kilometersPerHour)
{
842
    return QVariant((kilometersPerHour.toDouble() * 1000.0) / constants.secondsPerHour);
843 844 845 846
}

QVariant FactMetaData::_metersPerSecondToKnots(const QVariant& metersPerSecond)
{
847
    return QVariant(metersPerSecond.toDouble() * constants.secondsPerHour / (1000.0 * constants.knotsToKPH));
848 849 850 851
}

QVariant FactMetaData::_knotsToMetersPerSecond(const QVariant& knots)
{
852
    return QVariant(knots.toDouble() * (1000.0 * constants.knotsToKPH / constants.secondsPerHour));
853 854
}

855 856 857 858 859 860 861 862 863 864
QVariant FactMetaData::_percentToNorm(const QVariant& percent)
{
    return QVariant(percent.toDouble() / 100.0);
}

QVariant FactMetaData::_normToPercent(const QVariant& normalized)
{
    return QVariant(normalized.toDouble() * 100.0);
}

865 866 867 868 869 870 871 872 873 874
QVariant FactMetaData::_centimetersToInches(const QVariant& centimeters)
{
    return QVariant(centimeters.toDouble() * 1.0/constants.inchesToCentimeters);
}

QVariant FactMetaData::_inchesToCentimeters(const QVariant& inches)
{
    return QVariant(inches.toDouble() * constants.inchesToCentimeters);
}

875 876 877 878 879 880 881 882 883 884
QVariant FactMetaData::_celsiusToFarenheit(const QVariant& celsius)
{
    return QVariant(celsius.toDouble() * (9.0 / 5.0) + 32);
}

QVariant FactMetaData::_farenheitToCelsius(const QVariant& farenheit)
{
    return QVariant((farenheit.toDouble() - 32) * (5.0 / 9.0));
}

885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
QVariant FactMetaData::_kilogramsToGrams(const QVariant& kg) {
    return QVariant(kg.toDouble() * 1000);
}

QVariant FactMetaData::_ouncesToGrams(const QVariant& oz) {
    return QVariant(oz.toDouble() * constants.ouncesToGrams);
}

QVariant FactMetaData::_poundsToGrams(const QVariant& lbs) {
    return QVariant(lbs.toDouble() * constants.poundsToGrams);
}

QVariant FactMetaData::_gramsToKilograms(const QVariant& g) {
    return QVariant(g.toDouble() / 1000);
}

QVariant FactMetaData::_gramsToOunces(const QVariant& g) {
    return QVariant(g.toDouble() / constants.ouncesToGrams);
}

QVariant FactMetaData::_gramsToPunds(const QVariant& g) {
    return QVariant(g.toDouble() / constants.poundsToGrams);
}

909 910 911 912 913
void FactMetaData::setRawUnits(const QString& rawUnits)
{
    _rawUnits = rawUnits;
    _cookedUnits = rawUnits;

914
    setBuiltInTranslator();
915
}
Don Gagne's avatar
Don Gagne committed
916 917 918 919 920

FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, bool& unknownType)
{
    unknownType = false;

DonLakeFlyer's avatar
DonLakeFlyer committed
921 922 923
    for (size_t i=0; i<sizeof(_rgKnownTypeStrings)/sizeof(_rgKnownTypeStrings[0]); i++) {
        if (typeString.compare(_rgKnownTypeStrings[i], Qt::CaseInsensitive) == 0) {
            return _rgKnownValueTypes[i];
Don Gagne's avatar
Don Gagne committed
924 925 926 927 928 929 930
        }
    }

    unknownType = true;

    return valueTypeDouble;
}
931

DonLakeFlyer's avatar
DonLakeFlyer committed
932 933 934 935 936 937 938 939 940 941 942
QString FactMetaData::typeToString(ValueType_t type)
{
    for (size_t i=0; i<sizeof(_rgKnownTypeStrings)/sizeof(_rgKnownTypeStrings[0]); i++) {
        if (type == _rgKnownValueTypes[i]) {
            return _rgKnownTypeStrings[i];
        }
    }

    return QStringLiteral("UnknownType%1").arg(type);
}

943 944 945
size_t FactMetaData::typeToSize(ValueType_t type)
{
    switch (type) {
946 947 948
    case valueTypeUint8:
    case valueTypeInt8:
        return 1;
949

950 951 952
    case valueTypeUint16:
    case valueTypeInt16:
        return 2;
953

954 955 956 957
    case valueTypeUint32:
    case valueTypeInt32:
    case valueTypeFloat:
        return 4;
958

959 960
    case valueTypeUint64:
    case valueTypeInt64:
961 962
    case valueTypeDouble:
        return 8;
963

964 965 966
    case valueTypeCustom:
        return MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN;

967 968
    default:
        qWarning() << "Unsupported fact value type" << type;
Don Gagne's avatar
Don Gagne committed
969
        return 0;
970 971
    }
}
972

973 974
/// Set translators according to app settings
void FactMetaData::_setAppSettingsTranslators(void)
975
{
976 977
    // We can only translate between real numbers
    if (!_enumStrings.count() && (type() == valueTypeDouble || type() == valueTypeFloat)) {
978 979
        for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
            const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
980 981 982 983 984 985 986 987 988

            if (_rawUnits.toLower() != pAppSettingsTranslation->rawUnits.toLower()) {
                continue;
            }

            UnitsSettings* settings = qgcApp()->toolbox()->settingsManager()->unitsSettings();
            uint settingsUnits = 0;

            switch (pAppSettingsTranslation->unitType) {
Remek Zajac's avatar
Remek Zajac committed
989 990
            case UnitHorizontalDistance:
                settingsUnits = settings->horizontalDistanceUnits()->rawValue().toUInt();
991
                break;
Remek Zajac's avatar
Remek Zajac committed
992 993
            case UnitVerticalDistance:
                settingsUnits = settings->verticalDistanceUnits()->rawValue().toUInt();
994
                break;
995 996 997 998 999 1000 1001 1002 1003
            case UnitSpeed:
                settingsUnits = settings->speedUnits()->rawValue().toUInt();
                break;
            case UnitArea:
                settingsUnits = settings->areaUnits()->rawValue().toUInt();
                break;
            case UnitTemperature:
                settingsUnits = settings->temperatureUnits()->rawValue().toUInt();
                break;
1004 1005 1006
            case UnitWeight:
                settingsUnits = settings->weightUnits()->rawValue().toUInt();
                break;
1007 1008 1009 1010 1011 1012
            default:
                break;
            }

            if (settingsUnits == pAppSettingsTranslation->unitOption) {
                 _cookedUnits = pAppSettingsTranslation->cookedUnits;
1013 1014 1015 1016 1017 1018
                setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator);
                return;
            }
        }
    }
}
1019

1020
const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsUnitsTranslation(const QString& rawUnits, UnitTypes type)
1021 1022 1023
{
    for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
        const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
1024 1025 1026 1027 1028

        if (rawUnits.toLower() != pAppSettingsTranslation->rawUnits.toLower()) {
            continue;
        }

1029 1030
        uint unitOption = 0;
        auto unitsSettings = qgcApp()->toolbox()->settingsManager()->unitsSettings();
1031 1032
        switch (type) {
        case UnitHorizontalDistance:
1033
            unitOption = unitsSettings->horizontalDistanceUnits()->rawValue().toUInt();
1034 1035
            break;
        case UnitVerticalDistance:
1036
            unitOption = unitsSettings->verticalDistanceUnits()->rawValue().toUInt();
1037 1038
            break;
        case UnitArea:
1039
            unitOption = unitsSettings->areaUnits()->rawValue().toUInt();
1040 1041
            break;
        case UnitSpeed:
1042
            unitOption = unitsSettings->speedUnits()->rawValue().toUInt();
1043 1044
            break;
        case UnitTemperature:
1045
            unitOption = unitsSettings->temperatureUnits()->rawValue().toUInt();
1046 1047
            break;
        case UnitWeight:
1048
            unitOption = unitsSettings->weightUnits()->rawValue().toUInt();
1049
            break;
1050 1051
        }

1052
        if (pAppSettingsTranslation->unitType == type
1053
                && pAppSettingsTranslation->unitOption == unitOption) {
1054 1055 1056 1057 1058 1059 1060
            return pAppSettingsTranslation;
        }
    }

    return nullptr;
}

Remek Zajac's avatar
Remek Zajac committed
1061
QVariant FactMetaData::metersToAppSettingsHorizontalDistanceUnits(const QVariant& meters)
1062
{
1063
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m", UnitHorizontalDistance);
1064 1065 1066 1067 1068 1069 1070
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(meters);
    } else {
        return meters;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1071
QVariant FactMetaData::metersToAppSettingsVerticalDistanceUnits(const QVariant& meters)
1072
{
1073
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("vertical m", UnitVerticalDistance);
1074 1075 1076 1077 1078 1079 1080
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(meters);
    } else {
        return meters;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1081
QVariant FactMetaData::appSettingsHorizontalDistanceUnitsToMeters(const QVariant& distance)
1082
{
1083
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m", UnitHorizontalDistance);
1084 1085 1086 1087 1088 1089 1090
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(distance);
    } else {
        return distance;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1091
QVariant FactMetaData::appSettingsVerticalDistanceUnitsToMeters(const QVariant& distance)
1092
{
1093
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("vertical m", UnitVerticalDistance);
1094 1095 1096 1097 1098 1099 1100
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(distance);
    } else {
        return distance;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1101
QString FactMetaData::appSettingsHorizontalDistanceUnitsString(void)
1102
{
1103
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m", UnitHorizontalDistance);
1104 1105 1106 1107 1108 1109
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m");
    }
}
1110

Remek Zajac's avatar
Remek Zajac committed
1111
QString FactMetaData::appSettingsVerticalDistanceUnitsString(void)
1112
{
1113
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("vertical m", UnitVerticalDistance);
1114 1115 1116 1117 1118 1119 1120 1121 1122
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m");
    }
}

QString FactMetaData::appSettingsWeightUnitsString(void)
{
1123
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("g", UnitWeight);
1124 1125 1126 1127 1128 1129 1130
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("g");
    }
}

1131 1132
QVariant FactMetaData::squareMetersToAppSettingsAreaUnits(const QVariant& squareMeters)
{
1133
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m^2", UnitArea);
1134 1135 1136 1137 1138 1139 1140 1141 1142
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(squareMeters);
    } else {
        return squareMeters;
    }
}

QVariant FactMetaData::appSettingsAreaUnitsToSquareMeters(const QVariant& area)
{
1143
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m^2", UnitArea);
1144 1145 1146 1147 1148 1149 1150 1151 1152
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(area);
    } else {
        return area;
    }
}

QString FactMetaData::appSettingsAreaUnitsString(void)
{
1153
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m^2", UnitArea);
1154 1155 1156 1157 1158 1159 1160
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m^2");
    }
}

1161
QVariant FactMetaData::gramsToAppSettingsWeightUnits(const QVariant& grams) {
1162
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("g", UnitWeight);
1163 1164 1165 1166 1167 1168 1169 1170
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(grams);
    } else {
        return grams;
    }
}

QVariant FactMetaData::appSettingsWeightUnitsToGrams(const QVariant& weight) {
1171
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("g", UnitWeight);
1172 1173 1174 1175 1176 1177 1178
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(weight);
    } else {
        return weight;
    }
}

1179 1180
QString FactMetaData::appSettingsSpeedUnitsString()
{
1181
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m/s", UnitSpeed);
1182 1183 1184 1185 1186 1187
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m/s");
    }
}
1188

1189 1190
double FactMetaData::cookedIncrement(void) const
{
1191
    return _rawTranslator(this->rawIncrement()).toDouble();
1192 1193
}

1194 1195
int FactMetaData::decimalPlaces(void) const
{
1196 1197
    int actualDecimalPlaces = kDefaultDecimalPlaces;
    int incrementDecimalPlaces = kUnknownDecimalPlaces;
1198 1199

    // First determine decimal places from increment
1200
    double increment = _rawTranslator(this->rawIncrement()).toDouble();
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213
    if (!qIsNaN(increment)) {
        double integralPart;

        // Get the fractional part only
        increment = fabs(modf(increment, &integralPart));
        if (increment == 0.0) {
            // No fractional part, so no decimal places
            incrementDecimalPlaces = 0;
        } else {
            incrementDecimalPlaces = -ceil(log10(increment));
        }
    }

1214 1215 1216 1217 1218 1219 1220
    if (_decimalPlaces == kUnknownDecimalPlaces) {
        if (incrementDecimalPlaces != kUnknownDecimalPlaces) {
            actualDecimalPlaces = incrementDecimalPlaces;
        } else {
            // Adjust decimal places for cooked translation
            int settingsDecimalPlaces = _decimalPlaces == kUnknownDecimalPlaces ? kDefaultDecimalPlaces : _decimalPlaces;
            double ctest = _rawTranslator(1.0).toDouble();
1221

1222
            settingsDecimalPlaces += -log10(ctest);
1223

1224 1225 1226 1227 1228
            settingsDecimalPlaces = qMin(25, settingsDecimalPlaces);
            settingsDecimalPlaces = qMax(0, settingsDecimalPlaces);
        }
    } else {
        actualDecimalPlaces = _decimalPlaces;
1229 1230 1231 1232
    }

    return actualDecimalPlaces;
}
1233

1234
FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1235 1236 1237
{
    QString         errorString;

1238
    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
        { _nameJsonKey,                 QJsonValue::String, true },
        { _typeJsonKey,                 QJsonValue::String, true },
        { _shortDescriptionJsonKey,     QJsonValue::String, false },
        { _longDescriptionJsonKey,      QJsonValue::String, false },
        { _unitsJsonKey,                QJsonValue::String, false },
        { _decimalPlacesJsonKey,        QJsonValue::Double, false },
        { _minJsonKey,                  QJsonValue::Double, false },
        { _maxJsonKey,                  QJsonValue::Double, false },
        { _hasControlJsonKey,           QJsonValue::Bool,   false },
        { _qgcRebootRequiredJsonKey,    QJsonValue::Bool,   false },
1249 1250 1251
        { _categoryJsonKey,             QJsonValue::String, false },
        { _groupJsonKey,                QJsonValue::String, false },
        { _volatileJsonKey,             QJsonValue::Bool,   false },
1252 1253
    };
    if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
        qWarning() << errorString;
        return new FactMetaData(valueTypeUint32, metaDataParent);
    }

    bool unknownType;
    FactMetaData::ValueType_t type = FactMetaData::stringToType(json[_typeJsonKey].toString(), unknownType);
    if (unknownType) {
        qWarning() << "Unknown type" << json[_typeJsonKey].toString();
        return new FactMetaData(valueTypeUint32, metaDataParent);
    }

    FactMetaData*   metaData = new FactMetaData(type, metaDataParent);

    metaData->_name = json[_nameJsonKey].toString();

    QStringList enumValues, enumStrings;
1270
    if (JsonHelper::parseEnum(json, defineMap, enumStrings, enumValues, errorString, metaData->name())) {
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294
        for (int i=0; i<enumValues.count(); i++) {
            QVariant    enumVariant;
            QString     errorString;

            if (metaData->convertAndValidateRaw(enumValues[i], false /* validate */, enumVariant, errorString)) {
                metaData->addEnumInfo(enumStrings[i], enumVariant);
            } else {
                qWarning() << "Invalid enum value, name:" << metaData->name()
                           << " type:" << metaData->type()
                           << " value:" << enumValues[i]
                           << " error:" << errorString;
            }
        }
    } else {
        qWarning() << errorString;
    }

    metaData->setDecimalPlaces(json[_decimalPlacesJsonKey].toInt(0));
    metaData->setShortDescription(json[_shortDescriptionJsonKey].toString());
    metaData->setLongDescription(json[_longDescriptionJsonKey].toString());

    if (json.contains(_unitsJsonKey)) {
        metaData->setRawUnits(json[_unitsJsonKey].toString());
    }
1295

1296
    QString defaultValueJsonKey = _defaultValueJsonKey;
1297 1298
#ifdef __mobile__
    if (json.contains(_mobileDefaultValueJsonKey)) {
Don Gagne's avatar
Don Gagne committed
1299
        defaultValueJsonKey = _mobileDefaultValueJsonKey;
1300
    }
1301
#endif
1302 1303 1304 1305 1306 1307

    if (json.contains(defaultValueJsonKey)) {
        const QJsonValue jsonValue = json[defaultValueJsonKey];

        if (jsonValue.type() == QJsonValue::Null && (type == valueTypeFloat || type == valueTypeDouble)) {
            metaData->setRawDefaultValue(type == valueTypeFloat ? std::numeric_limits<float>::quiet_NaN() : std::numeric_limits<double>::quiet_NaN());
1308
        } else {
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320
            QVariant typedValue;
            QString errorString;
            QVariant initialValue = jsonValue.toVariant();

            if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
                metaData->setRawDefaultValue(typedValue);
            } else {
                qWarning() << "Invalid default value, name:" << metaData->name()
                           << " type:" << metaData->type()
                           << " value:" << initialValue
                           << " error:" << errorString;
            }
1321 1322 1323
        }
    }

1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
    if (json.contains(_incrementJsonKey)) {
        QVariant typedValue;
        QString errorString;
        QVariant initialValue = json[_incrementJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawIncrement(typedValue.toDouble());
        } else {
            qWarning() << "Invalid increment value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
    }

1338
    if (json.contains(_minJsonKey)) {
1339 1340
        QVariant typedValue;
        QString errorString;
1341 1342 1343 1344 1345 1346 1347 1348 1349
        QVariant initialValue = json[_minJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawMin(typedValue);
        } else {
            qWarning() << "Invalid min value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
1350
    }
1351

1352
    if (json.contains(_maxJsonKey)) {
1353 1354
        QVariant typedValue;
        QString errorString;
1355 1356 1357 1358 1359 1360 1361 1362 1363
        QVariant initialValue = json[_maxJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawMax(typedValue);
        } else {
            qWarning() << "Invalid max value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
1364
    }
1365

1366
    bool hasControlJsonKey = true;
1367
    if (json.contains(_hasControlJsonKey)) {
1368
        hasControlJsonKey = json[_hasControlJsonKey].toBool();
1369
    }
1370
    metaData->setHasControl(hasControlJsonKey);
1371

1372
    bool qgcRebootRequired = false;
1373
    if (json.contains(_qgcRebootRequiredJsonKey)) {
1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
        qgcRebootRequired = json[_qgcRebootRequiredJsonKey].toBool();
    }
    metaData->setQGCRebootRequired(qgcRebootRequired);

    bool volatileValue = false;
    if (json.contains(_volatileJsonKey)) {
        volatileValue = json[_volatileJsonKey].toBool();
    }
    metaData->setVolatileValue(volatileValue);

    if (json.contains(_groupJsonKey)) {
        metaData->setGroup(json[_groupJsonKey].toString());
    }

    if (json.contains(_categoryJsonKey)) {
        metaData->setCategory(json[_categoryJsonKey].toString());
1390 1391
    }

1392 1393 1394
    return metaData;
}

1395 1396 1397 1398 1399 1400 1401 1402
void FactMetaData::_loadJsonDefines(const QJsonObject& jsonDefinesObject, QMap<QString, QString>& defineMap)
{
    for (const QString& defineName: jsonDefinesObject.keys()) {
        QString mapKey = _jsonMetaDataDefinesName + QString(".") + defineName;
        defineMap[mapKey] = jsonDefinesObject[defineName].toString();
    }
}

1403 1404 1405 1406
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent)
{
    QMap<QString, FactMetaData*> metaDataMap;

1407 1408 1409 1410 1411
    QString errorString;
    int version;
    QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(jsonFilename, qgcFileType, 1, 1, version, errorString);
    if (!errorString.isEmpty()) {
        qWarning() << "Internal Error: " << errorString;
1412 1413 1414
        return metaDataMap;
    }

1415 1416 1417
    QJsonArray factArray;
    QMap<QString /* define name */, QString /* define value */> defineMap;

1418 1419 1420 1421 1422 1423
    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
        { FactMetaData::_jsonMetaDataDefinesName,   QJsonValue::Object, false },
        { FactMetaData::_jsonMetaDataFactsName,     QJsonValue::Array,  true },
    };
    if (!JsonHelper::validateKeys(jsonObject, keyInfoList, errorString)) {
        qWarning() << "Json document incorrect format:" << errorString;
1424 1425 1426
        return metaDataMap;
    }

1427 1428 1429
    _loadJsonDefines(jsonObject[FactMetaData::_jsonMetaDataDefinesName].toObject(), defineMap);
    factArray = jsonObject[FactMetaData::_jsonMetaDataFactsName].toArray();

1430
    return createMapFromJsonArray(factArray, defineMap, metaDataParent);
1431
}
1432

1433
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray jsonArray, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1434 1435 1436 1437
{
    QMap<QString, FactMetaData*> metaDataMap;
    for (int i=0; i<jsonArray.count(); i++) {
        QJsonValue jsonValue = jsonArray.at(i);
1438 1439 1440 1441 1442
        if (!jsonValue.isObject()) {
            qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
            continue;
        }
        QJsonObject jsonObject = jsonValue.toObject();
1443
        FactMetaData* metaData = createFromJsonObject(jsonObject, defineMap, metaDataParent);
1444 1445
        if (metaDataMap.contains(metaData->name())) {
            qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
1446
            delete metaData;
1447 1448 1449 1450 1451 1452
        } else {
            metaDataMap[metaData->name()] = metaData;
        }
    }
    return metaDataMap;
}
1453 1454 1455 1456

QVariant FactMetaData::cookedMax(void) const
{
    // We have to be careful with cooked min/max. Running the raw values through the translator could flip min and max.
DonLakeFlyer's avatar
DonLakeFlyer committed
1457
    return qMax(_rawTranslator(_rawMax).toDouble(), _rawTranslator(_rawMin).toDouble());
1458 1459 1460 1461 1462
}

QVariant FactMetaData::cookedMin(void) const
{
    // We have to be careful with cooked min/max. Running the raw values through the translator could flip min and max.
DonLakeFlyer's avatar
DonLakeFlyer committed
1463
    return qMin(_rawTranslator(_rawMax).toDouble(), _rawTranslator(_rawMin).toDouble());
1464
}
1465 1466 1467 1468 1469 1470 1471 1472

void FactMetaData::setVolatileValue(bool bValue)
{
    _volatile = bValue;
    if (_volatile) {
        _readOnly = true;
    }
}