FactMetaData.cc 54.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 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
};

87 88 89 90 91 92 93
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";
94
const char* FactMetaData::_mobileDefaultValueJsonKey =  "mobileDefaultValue";
95 96
const char* FactMetaData::_minJsonKey =                 "min";
const char* FactMetaData::_maxJsonKey =                 "max";
97
const char* FactMetaData::_incrementJsonKey =           "increment";
98
const char* FactMetaData::_hasControlJsonKey =          "control";
99
const char* FactMetaData::_qgcRebootRequiredJsonKey =   "qgcRebootRequired";
100

101
FactMetaData::FactMetaData(QObject* parent)
102 103
    : QObject               (parent)
    , _type                 (valueTypeInt32)
104
    , _decimalPlaces        (kUnknownDecimalPlaces)
105
    , _rawDefaultValue      (0)
106
    , _defaultValueAvailable(false)
107 108 109 110 111 112
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
113 114
    , _vehicleRebootRequired(false)
    , _qgcRebootRequired    (false)
115
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
116 117
    , _hasControl           (true)
    , _readOnly             (false)
118
    , _writeOnly            (false)
119
    , _volatile             (false)
Don Gagne's avatar
Don Gagne committed
120
{
121 122
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
Don Gagne's avatar
Don Gagne committed
123 124
}

125
FactMetaData::FactMetaData(ValueType_t type, QObject* parent)
126 127
    : QObject               (parent)
    , _type                 (type)
128
    , _decimalPlaces        (kUnknownDecimalPlaces)
129
    , _rawDefaultValue      (0)
130
    , _defaultValueAvailable(false)
131 132 133 134 135 136
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
137 138
    , _vehicleRebootRequired(false)
    , _qgcRebootRequired    (false)
139
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
140 141
    , _hasControl           (true)
    , _readOnly             (false)
142
    , _writeOnly            (false)
143
    , _volatile             (false)
144
{
145 146
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
147 148
}

Don Gagne's avatar
Don Gagne committed
149 150 151 152 153 154
FactMetaData::FactMetaData(const FactMetaData& other, QObject* parent)
    : QObject(parent)
{
    *this = other;
}

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

Don Gagne's avatar
Don Gagne committed
180 181
const FactMetaData& FactMetaData::operator=(const FactMetaData& other)
{
182
    _decimalPlaces          = other._decimalPlaces;
183
    _rawDefaultValue        = other._rawDefaultValue;
Don Gagne's avatar
Don Gagne committed
184
    _defaultValueAvailable  = other._defaultValueAvailable;
185 186
    _bitmaskStrings         = other._bitmaskStrings;
    _bitmaskValues          = other._bitmaskValues;
187 188
    _enumStrings            = other._enumStrings;
    _enumValues             = other._enumValues;
189
    _category               = other._category;
190 191
    _group                  = other._group;
    _longDescription        = other._longDescription;
192
    _rawMax                 = other._rawMax;
Don Gagne's avatar
Don Gagne committed
193
    _maxIsDefaultForType    = other._maxIsDefaultForType;
194
    _rawMin                 = other._rawMin;
195 196 197 198
    _minIsDefaultForType    = other._minIsDefaultForType;
    _name                   = other._name;
    _shortDescription       = other._shortDescription;
    _type                   = other._type;
199 200
    _rawUnits               = other._rawUnits;
    _cookedUnits            = other._cookedUnits;
201 202
    _rawTranslator          = other._rawTranslator;
    _cookedTranslator       = other._cookedTranslator;
203 204
    _vehicleRebootRequired  = other._vehicleRebootRequired;
    _qgcRebootRequired      = other._qgcRebootRequired;
205
    _rawIncrement           = other._rawIncrement;
206
    _hasControl             = other._hasControl;
207
    _readOnly               = other._readOnly;
208
    _writeOnly              = other._writeOnly;
209
    _volatile               = other._volatile;
Don Gagne's avatar
Don Gagne committed
210 211 212
    return *this;
}

213 214 215 216 217 218 219 220 221 222
const QString FactMetaData::defaultCategory()
{
    return QString(kDefaultCategory);
}

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

223
QVariant FactMetaData::rawDefaultValue(void) const
224 225
{
    if (_defaultValueAvailable) {
226
        return _rawDefaultValue;
227 228 229 230 231 232
    } else {
        qWarning() << "Attempt to access unavailable default value";
        return QVariant(0);
    }
}

233
void FactMetaData::setRawDefaultValue(const QVariant& rawDefaultValue)
234
{
Don Gagne's avatar
Don Gagne committed
235
    if (_type == valueTypeString || (_rawMin <= rawDefaultValue && rawDefaultValue <= _rawMax)) {
236
        _rawDefaultValue = rawDefaultValue;
237 238 239 240 241 242
        _defaultValueAvailable = true;
    } else {
        qWarning() << "Attempt to set default value which is outside min/max range";
    }
}

243
void FactMetaData::setRawMin(const QVariant& rawMin)
244
{
Don Gagne's avatar
Don Gagne committed
245
    if (rawMin >= _minForType()) {
246
        _rawMin = rawMin;
Don Gagne's avatar
Don Gagne committed
247
        _minIsDefaultForType = false;
248
    } else {
249
        qWarning() << "Attempt to set min below allowable value for fact: " << name()
250
                   << ", value attempted: " << rawMin
251
                   << ", type: " << type() << ", min for type: " << _minForType();
252
        _rawMin = _minForType();
253 254 255
    }
}

256
void FactMetaData::setRawMax(const QVariant& rawMax)
257
{
258
    if (rawMax > _maxForType()) {
259
        qWarning() << "Attempt to set max above allowable value";
260
        _rawMax = _maxForType();
261
    } else {
262
        _rawMax = rawMax;
Don Gagne's avatar
Don Gagne committed
263
        _maxIsDefaultForType = false;
264 265 266
    }
}

267
QVariant FactMetaData::_minForType(void) const
Don Gagne's avatar
Don Gagne committed
268
{
269
    switch (_type) {
270 271 272 273 274 275 276 277 278
    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:
279
        return QVariant(std::numeric_limits<uint32_t>::min());
280
    case valueTypeInt32:
281 282
        return QVariant(std::numeric_limits<int32_t>::min());
    case valueTypeUint64:
283
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::min());
284
    case valueTypeInt64:
285
        return QVariant((qlonglong)std::numeric_limits<int64_t>::min());
286 287 288 289
    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
290 291
    case valueTypeString:
        return QVariant();
292 293
    case valueTypeBool:
        return QVariant(0);
294 295
    case valueTypeElapsedTimeInSeconds:
        return QVariant(0.0);
296 297
    case valueTypeCustom:
        return QVariant();
298
    }
299

Don Gagne's avatar
Don Gagne committed
300 301
    // Make windows compiler happy, even switch is full cased
    return QVariant();
Don Gagne's avatar
Don Gagne committed
302
}
Don Gagne's avatar
Don Gagne committed
303

304
QVariant FactMetaData::_maxForType(void) const
Don Gagne's avatar
Don Gagne committed
305
{
306
    switch (_type) {
307 308 309 310 311 312 313 314 315
    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:
316
        return QVariant(std::numeric_limits<uint32_t>::max());
317
    case valueTypeInt32:
318 319
        return QVariant(std::numeric_limits<int32_t>::max());
    case valueTypeUint64:
320
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::max());
321
    case valueTypeInt64:
322
        return QVariant((qlonglong)std::numeric_limits<int64_t>::max());
323 324
    case valueTypeFloat:
        return QVariant(std::numeric_limits<float>::max());
325
    case valueTypeElapsedTimeInSeconds:
326 327
    case valueTypeDouble:
        return QVariant(std::numeric_limits<double>::max());
Don Gagne's avatar
Don Gagne committed
328 329
    case valueTypeString:
        return QVariant();
330 331
    case valueTypeBool:
        return QVariant(1);
332 333
    case valueTypeCustom:
        return QVariant();
334
    }
335

Don Gagne's avatar
Don Gagne committed
336 337
    // Make windows compiler happy, even switch is full cased
    return QVariant();
Don Gagne's avatar
Don Gagne committed
338
}
Don Gagne's avatar
Don Gagne committed
339

340
bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertOnly, QVariant& typedValue, QString& errorString)
Don Gagne's avatar
Don Gagne committed
341
{
342
    bool convertOk = false;
343

Don Gagne's avatar
Don Gagne committed
344
    errorString.clear();
345

Don Gagne's avatar
Don Gagne committed
346
    switch (type()) {
347 348 349 350 351
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(rawValue.toInt(&convertOk));
        if (!convertOnly && convertOk) {
352
            if (typedValue < rawMin() || typedValue > rawMax()) {
353
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
Don Gagne's avatar
Don Gagne committed
354
            }
355 356
        }
        break;
357 358 359 360 361 362 363 364
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(rawValue.toLongLong(&convertOk));
        if (!convertOnly && convertOk) {
            if (typedValue < rawMin() || typedValue > rawMax()) {
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
            }
        }
        break;
365 366 367 368 369
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(rawValue.toUInt(&convertOk));
        if (!convertOnly && convertOk) {
370
            if (typedValue < rawMin() || typedValue > rawMax()) {
371
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
Don Gagne's avatar
Don Gagne committed
372
            }
373 374
        }
        break;
375 376 377 378 379 380 381 382
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(rawValue.toULongLong(&convertOk));
        if (!convertOnly && convertOk) {
            if (typedValue < rawMin() || typedValue > rawMax()) {
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
            }
        }
        break;
383 384 385
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(rawValue.toFloat(&convertOk));
        if (!convertOnly && convertOk) {
386
            if (typedValue < rawMin() || typedValue > rawMax()) {
387
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
Don Gagne's avatar
Don Gagne committed
388
            }
389 390
        }
        break;
391
    case FactMetaData::valueTypeElapsedTimeInSeconds:
392 393 394
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(rawValue.toDouble(&convertOk));
        if (!convertOnly && convertOk) {
395
            if (typedValue < rawMin() || typedValue > rawMax()) {
396
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
Don Gagne's avatar
Don Gagne committed
397
            }
398 399
        }
        break;
Don Gagne's avatar
Don Gagne committed
400 401 402 403
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(rawValue.toString());
        break;
404 405 406 407
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(rawValue.toBool());
        break;
408 409 410 411
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(rawValue.toByteArray());
        break;
Don Gagne's avatar
Don Gagne committed
412
    }
413

Don Gagne's avatar
Don Gagne committed
414
    if (!convertOk) {
415
        errorString += tr("Invalid number");
Don Gagne's avatar
Don Gagne committed
416
    }
417

Don Gagne's avatar
Don Gagne committed
418 419
    return convertOk && errorString.isEmpty();
}
420

421 422 423 424 425 426 427
bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool convertOnly, QVariant& typedValue, QString& errorString)
{
    bool convertOk = false;

    errorString.clear();

    switch (type()) {
428 429 430 431 432 433
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(cookedValue.toInt(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
434
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
435
            }
436 437
        }
        break;
438 439 440 441 442 443 444 445
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(cookedValue.toLongLong(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
            }
        }
        break;
446 447 448 449 450 451
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(cookedValue.toUInt(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
452
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
453
            }
454 455
        }
        break;
456 457 458 459 460 461 462 463
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(cookedValue.toULongLong(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
            }
        }
        break;
464 465 466 467
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(cookedValue.toFloat(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
468
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
469
            }
470 471
        }
        break;
472
    case FactMetaData::valueTypeElapsedTimeInSeconds:
473 474 475 476
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(cookedValue.toDouble(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
477
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
478
            }
479 480
        }
        break;
Don Gagne's avatar
Don Gagne committed
481 482 483 484
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(cookedValue.toString());
        break;
485 486 487 488
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(cookedValue.toBool());
        break;
489 490 491 492
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
493 494 495
    }

    if (!convertOk) {
496
        errorString += tr("Invalid number");
497 498 499 500 501
    }

    return convertOk && errorString.isEmpty();
}

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
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) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
518 519 520 521 522 523 524 525 526 527
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(cookedValue.toLongLong(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
528 529 530 531 532 533 534 535 536 537 538 539
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(cookedValue.toUInt(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
540 541 542 543 544 545 546 547 548 549
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(cookedValue.toULongLong(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(cookedValue.toFloat(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
    case FactMetaData::valueTypeElapsedTimeInSeconds:
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(cookedValue.toDouble(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(cookedValue.toString());
        break;
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(cookedValue.toBool());
        break;
579 580 581 582
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
583 584 585 586
    }
    return convertOk;
}

587 588 589 590 591 592 593 594 595
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;
596
    setBuiltInTranslator();
597 598 599 600 601 602 603 604
}

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

605 606 607 608 609 610 611 612 613
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;
614
    setBuiltInTranslator();
615 616
}

617 618 619 620 621 622
void FactMetaData::addEnumInfo(const QString& name, const QVariant& value)
{
    _enumStrings << name;
    _enumValues << value;
}

623 624 625 626 627
void FactMetaData::setTranslators(Translator rawTranslator, Translator cookedTranslator)
{
    _rawTranslator = rawTranslator;
    _cookedTranslator = cookedTranslator;
}
628

629
void FactMetaData::setBuiltInTranslator(void)
630 631 632 633 634
{
    if (_enumStrings.count()) {
        // No translation if enum
        setTranslators(_defaultTranslator, _defaultTranslator);
        _cookedUnits = _rawUnits;
635
        return;
636
    } else {
637 638
        for (size_t i=0; i<sizeof(_rgBuiltInTranslations)/sizeof(_rgBuiltInTranslations[0]); i++) {
            const BuiltInTranslation_s* pBuiltInTranslation = &_rgBuiltInTranslations[i];
639

640
            if (pBuiltInTranslation->rawUnits.toLower() == _rawUnits.toLower()) {
641 642
                _cookedUnits = pBuiltInTranslation->cookedUnits;
                setTranslators(pBuiltInTranslation->rawTranslator, pBuiltInTranslation->cookedTranslator);
643
                return;
644 645 646
            }
        }
    }
647 648 649

    // Translator not yet set, try app settings translators
    _setAppSettingsTranslators();
650 651 652 653
}

QVariant FactMetaData::_degreesToRadians(const QVariant& degrees)
{
654
    return QVariant(qDegreesToRadians(degrees.toDouble()));
655 656 657 658
}

QVariant FactMetaData::_radiansToDegrees(const QVariant& radians)
{
659
    return QVariant(qRadiansToDegrees(radians.toDouble()));
660 661 662 663
}

QVariant FactMetaData::_centiDegreesToDegrees(const QVariant& centiDegrees)
{
664
    return QVariant(centiDegrees.toReal() / 100.0);
665 666 667 668
}

QVariant FactMetaData::_degreesToCentiDegrees(const QVariant& degrees)
{
669
    return QVariant(qRound(degrees.toReal() * 100.0));
670 671
}

672 673 674 675 676 677 678 679 680 681 682 683 684 685
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;
}

686 687
QVariant FactMetaData::_metersToFeet(const QVariant& meters)
{
688
    return QVariant(meters.toDouble() * 1.0/constants.feetToMeters);
689 690 691 692
}

QVariant FactMetaData::_feetToMeters(const QVariant& feet)
{
693
    return QVariant(feet.toDouble() * constants.feetToMeters);
694 695
}

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
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);
}

746 747
QVariant FactMetaData::_metersPerSecondToMilesPerHour(const QVariant& metersPerSecond)
{
748
    return QVariant((metersPerSecond.toDouble() * 1.0/constants.milesToMeters) * constants.secondsPerHour);
749 750 751 752
}

QVariant FactMetaData::_milesPerHourToMetersPerSecond(const QVariant& milesPerHour)
{
753
    return QVariant((milesPerHour.toDouble() * constants.milesToMeters) / constants.secondsPerHour);
754 755 756 757
}

QVariant FactMetaData::_metersPerSecondToKilometersPerHour(const QVariant& metersPerSecond)
{
758
    return QVariant((metersPerSecond.toDouble() / 1000.0) * constants.secondsPerHour);
759 760 761 762
}

QVariant FactMetaData::_kilometersPerHourToMetersPerSecond(const QVariant& kilometersPerHour)
{
763
    return QVariant((kilometersPerHour.toDouble() * 1000.0) / constants.secondsPerHour);
764 765 766 767
}

QVariant FactMetaData::_metersPerSecondToKnots(const QVariant& metersPerSecond)
{
768
    return QVariant(metersPerSecond.toDouble() * constants.secondsPerHour / (1000.0 * constants.knotsToKPH));
769 770 771 772
}

QVariant FactMetaData::_knotsToMetersPerSecond(const QVariant& knots)
{
773
    return QVariant(knots.toDouble() * (1000.0 * constants.knotsToKPH / constants.secondsPerHour));
774 775
}

776 777 778 779 780 781 782 783 784 785
QVariant FactMetaData::_percentToNorm(const QVariant& percent)
{
    return QVariant(percent.toDouble() / 100.0);
}

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

786 787 788 789 790 791 792 793 794 795
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);
}

796 797 798 799 800 801 802 803 804 805
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));
}

806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829
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);
}

830 831 832 833 834
void FactMetaData::setRawUnits(const QString& rawUnits)
{
    _rawUnits = rawUnits;
    _cookedUnits = rawUnits;

835
    setBuiltInTranslator();
836
}
Don Gagne's avatar
Don Gagne committed
837 838 839 840 841 842 843 844 845

FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, bool& unknownType)
{
    QStringList         knownTypeStrings;
    QList<ValueType_t>  knownTypes;

    unknownType = false;

    knownTypeStrings << QStringLiteral("Uint8")
846 847 848 849 850
                     << QStringLiteral("Int8")
                     << QStringLiteral("Uint16")
                     << QStringLiteral("Int16")
                     << QStringLiteral("Uint32")
                     << QStringLiteral("Int32")
851 852
                     << QStringLiteral("Uint64")
                     << QStringLiteral("Int64")
853
                     << QStringLiteral("Float")
Don Gagne's avatar
Don Gagne committed
854
                     << QStringLiteral("Double")
855
                     << QStringLiteral("String")
856
                     << QStringLiteral("Bool")
857 858
                     << QStringLiteral("ElapsedSeconds")
                     << QStringLiteral("Custom");
Don Gagne's avatar
Don Gagne committed
859 860

    knownTypes << valueTypeUint8
861 862 863 864 865
               << valueTypeInt8
               << valueTypeUint16
               << valueTypeInt16
               << valueTypeUint32
               << valueTypeInt32
866 867
               << valueTypeUint64
               << valueTypeInt64
868
               << valueTypeFloat
Don Gagne's avatar
Don Gagne committed
869
               << valueTypeDouble
870
               << valueTypeString
871
               << valueTypeBool
872 873
               << valueTypeElapsedTimeInSeconds
               << valueTypeCustom;
Don Gagne's avatar
Don Gagne committed
874 875 876 877 878 879 880 881 882 883 884

    for (int i=0; i<knownTypeStrings.count(); i++) {
        if (knownTypeStrings[i].compare(typeString, Qt::CaseInsensitive) == 0) {
            return knownTypes[i];
        }
    }

    unknownType = true;

    return valueTypeDouble;
}
885 886 887 888

size_t FactMetaData::typeToSize(ValueType_t type)
{
    switch (type) {
889 890 891
    case valueTypeUint8:
    case valueTypeInt8:
        return 1;
892

893 894 895
    case valueTypeUint16:
    case valueTypeInt16:
        return 2;
896

897 898 899 900
    case valueTypeUint32:
    case valueTypeInt32:
    case valueTypeFloat:
        return 4;
901

902 903
    case valueTypeUint64:
    case valueTypeInt64:
904 905
    case valueTypeDouble:
        return 8;
906

907 908 909
    case valueTypeCustom:
        return MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN;

910 911
    default:
        qWarning() << "Unsupported fact value type" << type;
Don Gagne's avatar
Don Gagne committed
912
        return 0;
913 914
    }
}
915

916 917
/// Set translators according to app settings
void FactMetaData::_setAppSettingsTranslators(void)
918
{
919 920
    // We can only translate between real numbers
    if (!_enumStrings.count() && (type() == valueTypeDouble || type() == valueTypeFloat)) {
921 922
        for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
            const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
923 924 925 926 927 928 929 930 931

            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
932 933
            case UnitHorizontalDistance:
                settingsUnits = settings->horizontalDistanceUnits()->rawValue().toUInt();
934
                break;
Remek Zajac's avatar
Remek Zajac committed
935 936
            case UnitVerticalDistance:
                settingsUnits = settings->verticalDistanceUnits()->rawValue().toUInt();
937
                break;
938 939 940 941 942 943 944 945 946
            case UnitSpeed:
                settingsUnits = settings->speedUnits()->rawValue().toUInt();
                break;
            case UnitArea:
                settingsUnits = settings->areaUnits()->rawValue().toUInt();
                break;
            case UnitTemperature:
                settingsUnits = settings->temperatureUnits()->rawValue().toUInt();
                break;
947 948 949
            case UnitWeight:
                settingsUnits = settings->weightUnits()->rawValue().toUInt();
                break;
950 951 952 953 954 955
            default:
                break;
            }

            if (settingsUnits == pAppSettingsTranslation->unitOption) {
                 _cookedUnits = pAppSettingsTranslation->cookedUnits;
956 957 958 959 960 961
                setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator);
                return;
            }
        }
    }
}
962

Remek Zajac's avatar
Remek Zajac committed
963
const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsHorizontalDistanceUnitsTranslation(const QString& rawUnits)
964 965 966
{
    for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
        const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
967 968 969 970 971

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

Remek Zajac's avatar
Remek Zajac committed
972
        uint settingsUnits = qgcApp()->toolbox()->settingsManager()->unitsSettings()->horizontalDistanceUnits()->rawValue().toUInt();
973

Remek Zajac's avatar
Remek Zajac committed
974
        if (pAppSettingsTranslation->unitType == UnitHorizontalDistance
975
                && pAppSettingsTranslation->unitOption == settingsUnits) {
976 977 978
            return pAppSettingsTranslation;
        }
    }
979
    return nullptr;
980 981
}

Remek Zajac's avatar
Remek Zajac committed
982
const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsVerticalDistanceUnitsTranslation(const QString& rawUnits)
983 984 985 986 987 988 989 990
{
    for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
        const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];

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

Remek Zajac's avatar
Remek Zajac committed
991
        uint settingsUnits = qgcApp()->toolbox()->settingsManager()->unitsSettings()->verticalDistanceUnits()->rawValue().toUInt();
992

Remek Zajac's avatar
Remek Zajac committed
993
        if (pAppSettingsTranslation->unitType == UnitVerticalDistance
994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
                && pAppSettingsTranslation->unitOption == settingsUnits) {
            return pAppSettingsTranslation;
        }
    }
    return nullptr;
}

const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsWeightUnitsTranslation(const QString& rawUnits)
{
    for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
        const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];

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

        uint settingsUnits = qgcApp()->toolbox()->settingsManager()->unitsSettings()->weightUnits()->rawValue().toUInt();

        if (pAppSettingsTranslation->unitType == UnitWeight
                && pAppSettingsTranslation->unitOption == settingsUnits) {
            return pAppSettingsTranslation;
        }
    }
    return nullptr;
}

1020 1021 1022 1023
const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsAreaUnitsTranslation(const QString& rawUnits)
{
    for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
        const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
1024 1025 1026 1027 1028 1029 1030 1031 1032

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

        uint settingsUnits = qgcApp()->toolbox()->settingsManager()->unitsSettings()->areaUnits()->rawValue().toUInt();

        if (pAppSettingsTranslation->unitType == UnitArea
                && pAppSettingsTranslation->unitOption == settingsUnits) {
1033 1034 1035 1036
            return pAppSettingsTranslation;
        }
    }

1037
    return nullptr;
1038 1039
}

Remek Zajac's avatar
Remek Zajac committed
1040
QVariant FactMetaData::metersToAppSettingsHorizontalDistanceUnits(const QVariant& meters)
1041
{
Remek Zajac's avatar
Remek Zajac committed
1042
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsHorizontalDistanceUnitsTranslation("m");
1043 1044 1045 1046 1047 1048 1049
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(meters);
    } else {
        return meters;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1050
QVariant FactMetaData::metersToAppSettingsVerticalDistanceUnits(const QVariant& meters)
1051
{
Remek Zajac's avatar
Remek Zajac committed
1052
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsVerticalDistanceUnitsTranslation("vertical m");
1053 1054 1055 1056 1057 1058 1059
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(meters);
    } else {
        return meters;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1060
QVariant FactMetaData::appSettingsHorizontalDistanceUnitsToMeters(const QVariant& distance)
1061
{
Remek Zajac's avatar
Remek Zajac committed
1062
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsHorizontalDistanceUnitsTranslation("m");
1063 1064 1065 1066 1067 1068 1069
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(distance);
    } else {
        return distance;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1070
QVariant FactMetaData::appSettingsVerticalDistanceUnitsToMeters(const QVariant& distance)
1071
{
Remek Zajac's avatar
Remek Zajac committed
1072
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsVerticalDistanceUnitsTranslation("alt m");
1073 1074 1075 1076 1077 1078 1079
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(distance);
    } else {
        return distance;
    }
}

Remek Zajac's avatar
Remek Zajac committed
1080
QString FactMetaData::appSettingsHorizontalDistanceUnitsString(void)
1081
{
Remek Zajac's avatar
Remek Zajac committed
1082
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsHorizontalDistanceUnitsTranslation("m");
1083 1084 1085 1086 1087 1088
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m");
    }
}
1089

Remek Zajac's avatar
Remek Zajac committed
1090
QString FactMetaData::appSettingsVerticalDistanceUnitsString(void)
1091
{
Remek Zajac's avatar
Remek Zajac committed
1092
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsVerticalDistanceUnitsTranslation("alt m");
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m");
    }
}

QString FactMetaData::appSettingsWeightUnitsString(void)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsWeightUnitsTranslation("g");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("g");
    }
}

1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139
QVariant FactMetaData::squareMetersToAppSettingsAreaUnits(const QVariant& squareMeters)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsAreaUnitsTranslation("m^2");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(squareMeters);
    } else {
        return squareMeters;
    }
}

QVariant FactMetaData::appSettingsAreaUnitsToSquareMeters(const QVariant& area)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsAreaUnitsTranslation("m^2");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(area);
    } else {
        return area;
    }
}

QString FactMetaData::appSettingsAreaUnitsString(void)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsAreaUnitsTranslation("m^2");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m^2");
    }
}

1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158
QVariant FactMetaData::gramsToAppSettingsWeightUnits(const QVariant& grams) {
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsWeightUnitsTranslation("g");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(grams);
    } else {
        return grams;
    }
}

QVariant FactMetaData::appSettingsWeightUnitsToGrams(const QVariant& weight) {
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsWeightUnitsTranslation("g");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(weight);
    } else {
        return weight;
    }
}


1159 1160
double FactMetaData::cookedIncrement(void) const
{
1161
    return _rawTranslator(this->rawIncrement()).toDouble();
1162 1163
}

1164 1165
int FactMetaData::decimalPlaces(void) const
{
1166 1167
    int actualDecimalPlaces = kDefaultDecimalPlaces;
    int incrementDecimalPlaces = kUnknownDecimalPlaces;
1168 1169

    // First determine decimal places from increment
1170
    double increment = _rawTranslator(this->rawIncrement()).toDouble();
1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183
    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));
        }
    }

1184 1185 1186 1187 1188 1189 1190
    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();
1191

1192
            settingsDecimalPlaces += -log10(ctest);
1193

1194 1195 1196 1197 1198
            settingsDecimalPlaces = qMin(25, settingsDecimalPlaces);
            settingsDecimalPlaces = qMax(0, settingsDecimalPlaces);
        }
    } else {
        actualDecimalPlaces = _decimalPlaces;
1199 1200 1201 1202
    }

    return actualDecimalPlaces;
}
1203

1204
FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215
{
    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);
    }

1216
    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226
        { _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 },
1227 1228
    };
    if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244
        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;
1245
    if (JsonHelper::parseEnum(json, defineMap, enumStrings, enumValues, errorString, metaData->name())) {
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
        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());
    }
1270 1271

    QString defaultValueJsonKey;
1272 1273
#ifdef __mobile__
    if (json.contains(_mobileDefaultValueJsonKey)) {
Don Gagne's avatar
Don Gagne committed
1274
        defaultValueJsonKey = _mobileDefaultValueJsonKey;
1275
    }
1276
#endif
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293
    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;
        }
    }

1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
    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;
        }
    }

1308
    if (json.contains(_minJsonKey)) {
1309 1310
        QVariant typedValue;
        QString errorString;
1311 1312 1313 1314 1315 1316 1317 1318 1319
        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;
        }
1320
    }
1321

1322
    if (json.contains(_maxJsonKey)) {
1323 1324
        QVariant typedValue;
        QString errorString;
1325 1326 1327 1328 1329 1330 1331 1332 1333
        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;
        }
1334
    }
1335

1336 1337 1338 1339 1340
    if (json.contains(_hasControlJsonKey)) {
        metaData->setHasControl(json[_hasControlJsonKey].toBool());
    } else {
        metaData->setHasControl(true);
    }
1341

1342 1343 1344 1345 1346 1347
    if (json.contains(_qgcRebootRequiredJsonKey)) {
        metaData->setQGCRebootRequired(json[_qgcRebootRequiredJsonKey].toBool());
    } else {
        metaData->setQGCRebootRequired(false);
    }

1348 1349 1350
    return metaData;
}

1351 1352 1353 1354 1355 1356 1357 1358
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();
    }
}

1359 1360 1361 1362
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent)
{
    QMap<QString, FactMetaData*> metaDataMap;

1363 1364 1365 1366 1367
    QString errorString;
    int version;
    QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(jsonFilename, qgcFileType, 1, 1, version, errorString);
    if (!errorString.isEmpty()) {
        qWarning() << "Internal Error: " << errorString;
1368 1369 1370
        return metaDataMap;
    }

1371 1372 1373
    QJsonArray factArray;
    QMap<QString /* define name */, QString /* define value */> defineMap;

1374 1375 1376 1377 1378 1379
    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;
1380 1381 1382
        return metaDataMap;
    }

1383 1384 1385
    _loadJsonDefines(jsonObject[FactMetaData::_jsonMetaDataDefinesName].toObject(), defineMap);
    factArray = jsonObject[FactMetaData::_jsonMetaDataFactsName].toArray();

1386
    return createMapFromJsonArray(factArray, defineMap, metaDataParent);
1387
}
1388

1389
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray jsonArray, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1390 1391 1392 1393
{
    QMap<QString, FactMetaData*> metaDataMap;
    for (int i=0; i<jsonArray.count(); i++) {
        QJsonValue jsonValue = jsonArray.at(i);
1394 1395 1396 1397 1398
        if (!jsonValue.isObject()) {
            qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
            continue;
        }
        QJsonObject jsonObject = jsonValue.toObject();
1399
        FactMetaData* metaData = createFromJsonObject(jsonObject, defineMap, metaDataParent);
1400 1401
        if (metaDataMap.contains(metaData->name())) {
            qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
1402
            delete metaData;
1403 1404 1405 1406 1407 1408
        } else {
            metaDataMap[metaData->name()] = metaData;
        }
    }
    return metaDataMap;
}
1409 1410 1411 1412

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.
1413
    return qMax(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
1414 1415 1416 1417 1418
}

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.
1419
    return qMin(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
1420
}
1421 1422 1423 1424 1425 1426 1427 1428

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