FactMetaData.cc 54.9 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 39
//Weight
const qreal FactMetaData::UnitConsts_s::ouncesToGrams =         28.3495;
const qreal FactMetaData::UnitConsts_s::poundsToGrams =         453.592;


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

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

47 48
// Built in translations for all Facts
const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] = {
49 50 51 52
    { "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 },
53
};
Don Gagne's avatar
Don Gagne committed
54

55 56
// Translations driven by app settings
const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = {
Remek Zajac's avatar
Remek Zajac committed
57 58 59 60 61 62 63 64 65 66 67 68
    { "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
69
    { "vertical m",  "ft",  FactMetaData::UnitVerticalDistance,      UnitsSettings::VerticalDistanceUnitsFeet,     FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
Remek Zajac's avatar
Remek Zajac committed
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
    { "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 },
85 86
};

DonLakeFlyer's avatar
DonLakeFlyer committed
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 120
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,
};

121 122 123 124 125 126 127
const char* FactMetaData::_decimalPlacesJsonKey =       "decimalPlaces";
const char* FactMetaData::_nameJsonKey =                "name";
const char* FactMetaData::_typeJsonKey =                "type";
const char* FactMetaData::_shortDescriptionJsonKey =    "shortDescription";
const char* FactMetaData::_longDescriptionJsonKey =     "longDescription";
const char* FactMetaData::_unitsJsonKey =               "units";
const char* FactMetaData::_defaultValueJsonKey =        "defaultValue";
128
const char* FactMetaData::_mobileDefaultValueJsonKey =  "mobileDefaultValue";
129 130
const char* FactMetaData::_minJsonKey =                 "min";
const char* FactMetaData::_maxJsonKey =                 "max";
131
const char* FactMetaData::_incrementJsonKey =           "increment";
132
const char* FactMetaData::_hasControlJsonKey =          "control";
133
const char* FactMetaData::_qgcRebootRequiredJsonKey =   "qgcRebootRequired";
134

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

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

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

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

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

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

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

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

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

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

290
void FactMetaData::setRawMax(const QVariant& rawMax)
291
{
292
    if (isInRawMaxLimit(rawMax)) {
293
        _rawMax = rawMax;
Don Gagne's avatar
Don Gagne committed
294
        _maxIsDefaultForType = false;
295 296 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 323 324 325 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 353 354 355
    } 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:
        return _rawMin.value<float>() <= variantValue.value<float>();
    case valueTypeDouble:
        return _rawMin.value<double>() <= variantValue.value<double>();
    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:
        return _rawMax.value<float>() >= variantValue.value<float>();
    case valueTypeDouble:
        return _rawMax.value<double>() >= variantValue.value<double>();
    default:
        return true;
356
    }
357 358

    return true;
359 360
}

361
QVariant FactMetaData::_minForType(void) const
Don Gagne's avatar
Don Gagne committed
362
{
363
    switch (_type) {
364 365 366 367 368 369 370 371 372
    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:
373
        return QVariant(std::numeric_limits<uint32_t>::min());
374
    case valueTypeInt32:
375 376
        return QVariant(std::numeric_limits<int32_t>::min());
    case valueTypeUint64:
377
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::min());
378
    case valueTypeInt64:
379
        return QVariant((qlonglong)std::numeric_limits<int64_t>::min());
380 381 382 383
    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
384 385
    case valueTypeString:
        return QVariant();
386 387
    case valueTypeBool:
        return QVariant(0);
388 389
    case valueTypeElapsedTimeInSeconds:
        return QVariant(0.0);
390 391
    case valueTypeCustom:
        return QVariant();
392
    }
393

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

398
QVariant FactMetaData::_maxForType(void) const
Don Gagne's avatar
Don Gagne committed
399
{
400
    switch (_type) {
401 402 403 404 405 406 407 408 409
    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:
410
        return QVariant(std::numeric_limits<uint32_t>::max());
411
    case valueTypeInt32:
412 413
        return QVariant(std::numeric_limits<int32_t>::max());
    case valueTypeUint64:
414
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::max());
415
    case valueTypeInt64:
416
        return QVariant((qlonglong)std::numeric_limits<int64_t>::max());
417 418
    case valueTypeFloat:
        return QVariant(std::numeric_limits<float>::max());
419
    case valueTypeElapsedTimeInSeconds:
420 421
    case valueTypeDouble:
        return QVariant(std::numeric_limits<double>::max());
Don Gagne's avatar
Don Gagne committed
422 423
    case valueTypeString:
        return QVariant();
424 425
    case valueTypeBool:
        return QVariant(1);
426 427
    case valueTypeCustom:
        return QVariant();
428
    }
429

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

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

Don Gagne's avatar
Don Gagne committed
438
    errorString.clear();
439

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

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

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

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

    errorString.clear();

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

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

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

    return convertOk && errorString.isEmpty();
}

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

664 665 666 667 668 669 670 671 672
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;
673
    setBuiltInTranslator();
674 675 676 677 678 679 680 681
}

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

682 683 684 685 686 687 688 689 690
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;
691
    setBuiltInTranslator();
692 693
}

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

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

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

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

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

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

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

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

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

749 750 751 752 753 754 755 756 757 758 759 760 761 762
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;
}

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

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

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
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);
}

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

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

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

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

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

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

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

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

863 864 865 866 867 868 869 870 871 872
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);
}

873 874 875 876 877 878 879 880 881 882
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));
}

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
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);
}

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

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

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

DonLakeFlyer's avatar
DonLakeFlyer committed
919 920 921
    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
922 923 924 925 926 927 928
        }
    }

    unknownType = true;

    return valueTypeDouble;
}
929

DonLakeFlyer's avatar
DonLakeFlyer committed
930 931 932 933 934 935 936 937 938 939 940
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);
}

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

948 949 950
    case valueTypeUint16:
    case valueTypeInt16:
        return 2;
951

952 953 954 955
    case valueTypeUint32:
    case valueTypeInt32:
    case valueTypeFloat:
        return 4;
956

957 958
    case valueTypeUint64:
    case valueTypeInt64:
959 960
    case valueTypeDouble:
        return 8;
961

962 963 964
    case valueTypeCustom:
        return MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN;

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

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

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

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

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

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

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

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

    return nullptr;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    // First determine decimal places from increment
1198
    double increment = _rawTranslator(this->rawIncrement()).toDouble();
1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211
    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));
        }
    }

1212 1213 1214 1215 1216 1217 1218
    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();
1219

1220
            settingsDecimalPlaces += -log10(ctest);
1221

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

    return actualDecimalPlaces;
}
1231

1232
FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243
{
    QString         errorString;

    // Make sure we have the required keys
    QStringList requiredKeys;
    requiredKeys << _nameJsonKey << _typeJsonKey;
    if (!JsonHelper::validateRequiredKeys(json, requiredKeys, errorString)) {
        qWarning() << errorString;
        return new FactMetaData(valueTypeUint32, metaDataParent);
    }

1244
    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
1245 1246 1247 1248 1249 1250 1251 1252 1253 1254
        { _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 },
1255 1256
    };
    if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272
        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;
1273
    if (JsonHelper::parseEnum(json, defineMap, enumStrings, enumValues, errorString, metaData->name())) {
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297
        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());
    }
1298 1299

    QString defaultValueJsonKey;
1300 1301
#ifdef __mobile__
    if (json.contains(_mobileDefaultValueJsonKey)) {
Don Gagne's avatar
Don Gagne committed
1302
        defaultValueJsonKey = _mobileDefaultValueJsonKey;
1303
    }
1304
#endif
1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321
    if (defaultValueJsonKey.isEmpty() && json.contains(_defaultValueJsonKey)) {
        defaultValueJsonKey = _defaultValueJsonKey;
    }
    if (!defaultValueJsonKey.isEmpty()) {
        QVariant typedValue;
        QString errorString;
        QVariant initialValue = json[defaultValueJsonKey].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;
        }
    }

1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335
    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;
        }
    }

1336
    if (json.contains(_minJsonKey)) {
1337 1338
        QVariant typedValue;
        QString errorString;
1339 1340 1341 1342 1343 1344 1345 1346 1347
        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;
        }
1348
    }
1349

1350
    if (json.contains(_maxJsonKey)) {
1351 1352
        QVariant typedValue;
        QString errorString;
1353 1354 1355 1356 1357 1358 1359 1360 1361
        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;
        }
1362
    }
1363

1364 1365 1366 1367 1368
    if (json.contains(_hasControlJsonKey)) {
        metaData->setHasControl(json[_hasControlJsonKey].toBool());
    } else {
        metaData->setHasControl(true);
    }
1369

1370 1371 1372 1373 1374 1375
    if (json.contains(_qgcRebootRequiredJsonKey)) {
        metaData->setQGCRebootRequired(json[_qgcRebootRequiredJsonKey].toBool());
    } else {
        metaData->setQGCRebootRequired(false);
    }

1376 1377 1378
    return metaData;
}

1379 1380 1381 1382 1383 1384 1385 1386
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();
    }
}

1387 1388 1389 1390
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent)
{
    QMap<QString, FactMetaData*> metaDataMap;

1391 1392 1393 1394 1395
    QString errorString;
    int version;
    QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(jsonFilename, qgcFileType, 1, 1, version, errorString);
    if (!errorString.isEmpty()) {
        qWarning() << "Internal Error: " << errorString;
1396 1397 1398
        return metaDataMap;
    }

1399 1400 1401
    QJsonArray factArray;
    QMap<QString /* define name */, QString /* define value */> defineMap;

1402 1403 1404 1405 1406 1407
    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;
1408 1409 1410
        return metaDataMap;
    }

1411 1412 1413
    _loadJsonDefines(jsonObject[FactMetaData::_jsonMetaDataDefinesName].toObject(), defineMap);
    factArray = jsonObject[FactMetaData::_jsonMetaDataFactsName].toArray();

1414
    return createMapFromJsonArray(factArray, defineMap, metaDataParent);
1415
}
1416

1417
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray jsonArray, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1418 1419 1420 1421
{
    QMap<QString, FactMetaData*> metaDataMap;
    for (int i=0; i<jsonArray.count(); i++) {
        QJsonValue jsonValue = jsonArray.at(i);
1422 1423 1424 1425 1426
        if (!jsonValue.isObject()) {
            qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
            continue;
        }
        QJsonObject jsonObject = jsonValue.toObject();
1427
        FactMetaData* metaData = createFromJsonObject(jsonObject, defineMap, metaDataParent);
1428 1429
        if (metaDataMap.contains(metaData->name())) {
            qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
1430
            delete metaData;
1431 1432 1433 1434 1435 1436
        } else {
            metaDataMap[metaData->name()] = metaData;
        }
    }
    return metaDataMap;
}
1437 1438 1439 1440

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.
1441
    return qMax(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
1442 1443 1444 1445 1446
}

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.
1447
    return qMin(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
1448
}
1449 1450 1451 1452 1453 1454 1455 1456

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