FactMetaData.cc 55.4 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 44 45
const char* FactMetaData::_jsonMetaDataDefinesName =    "QGC.MetaData.Defines";
const char* FactMetaData::_jsonMetaDataFactsName =      "QGC.MetaData.Facts";

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

54 55
// Translations driven by app settings
const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = {
Remek Zajac's avatar
Remek Zajac committed
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
    { "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 },
    { "alt m",  "ft",       FactMetaData::UnitVerticalDistance,      UnitsSettings::VerticalDistanceUnitsFeet,     FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "cm/px",  "in/px",    FactMetaData::UnitHorizontalDistance,    UnitsSettings::HorizontalDistanceUnitsFeet,   FactMetaData::_centimetersToInches,                 FactMetaData::_inchesToCentimeters },
    { "m^2",    "km\u00B2", FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareKilometers,     FactMetaData::_squareMetersToSquareKilometers,      FactMetaData::_squareKilometersToSquareMeters },
    { "m^2",    "ha",       FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsHectares,             FactMetaData::_squareMetersToHectares,              FactMetaData::_hectaresToSquareMeters },
    { "m^2",    "ft\u00B2", FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareFeet,           FactMetaData::_squareMetersToSquareFeet,            FactMetaData::_squareFeetToSquareMeters },
    { "m^2",    "ac",       FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsAcres,                FactMetaData::_squareMetersToAcres,                 FactMetaData::_acresToSquareMeters },
    { "m^2",    "mi\u00B2", FactMetaData::UnitArea,                  UnitsSettings::AreaUnitsSquareMiles,          FactMetaData::_squareMetersToSquareMiles,           FactMetaData::_squareMilesToSquareMeters },
    { "m/s",    "ft/s",     FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsFeetPerSecond,       FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "m/s",    "mph",      FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsMilesPerHour,        FactMetaData::_metersPerSecondToMilesPerHour,       FactMetaData::_milesPerHourToMetersPerSecond },
    { "m/s",    "km/h",     FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsKilometersPerHour,   FactMetaData::_metersPerSecondToKilometersPerHour,  FactMetaData::_kilometersPerHourToMetersPerSecond },
    { "m/s",    "kn",       FactMetaData::UnitSpeed,                 UnitsSettings::SpeedUnitsKnots,               FactMetaData::_metersPerSecondToKnots,              FactMetaData::_knotsToMetersPerSecond },
    { "C",      "F",        FactMetaData::UnitTemperature,           UnitsSettings::TemperatureUnitsFarenheit,     FactMetaData::_celsiusToFarenheit,                  FactMetaData::_farenheitToCelsius },
    { "g",      "g",        FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsGrams,              FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "g",      "kg",       FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsKg,                 FactMetaData::_gramsToKilograms,                    FactMetaData::_kilogramsToGrams },
    { "g",      "oz",       FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsOz,                 FactMetaData::_gramsToOunces,                       FactMetaData::_ouncesToGrams },
    { "g",      "lbs",      FactMetaData::UnitWeight,                UnitsSettings::WeightUnitsLbs,                FactMetaData::_gramsToPunds,                        FactMetaData::_poundsToGrams },
84 85
};

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    errorString.clear();

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

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

    return convertOk && errorString.isEmpty();
}

501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
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;
517 518 519 520 521 522 523 524 525 526
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(cookedValue.toLongLong(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
527 528 529 530 531 532 533 534 535 536 537 538
    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;
539 540 541 542 543 544 545 546 547 548
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(cookedValue.toULongLong(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
549 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
    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;
578 579 580 581
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
582 583 584 585
    }
    return convertOk;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

QVariant FactMetaData::_feetToMeters(const QVariant& feet)
{
692
    return QVariant(feet.toDouble() * constants.feetToMeters);
693 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
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);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    unknownType = false;

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

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

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

    unknownType = true;

    return valueTypeDouble;
}
884 885 886 887

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

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

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

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

906 907 908
    case valueTypeCustom:
        return MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN;

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

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

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

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

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

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

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

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

Remek Zajac's avatar
Remek Zajac committed
981
const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsVerticalDistanceUnitsTranslation(const QString& rawUnits)
982 983 984 985 986 987 988 989
{
    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
990
        uint settingsUnits = qgcApp()->toolbox()->settingsManager()->unitsSettings()->verticalDistanceUnits()->rawValue().toUInt();
991

Remek Zajac's avatar
Remek Zajac committed
992
        if (pAppSettingsTranslation->unitType == UnitVerticalDistance
993 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
                && 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;
}

1019 1020 1021 1022
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];
1023 1024 1025 1026 1027 1028 1029 1030 1031

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

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

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

1036
    return nullptr;
1037 1038
}

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

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

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

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

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

Remek Zajac's avatar
Remek Zajac committed
1089
QString FactMetaData::appSettingsVerticalDistanceUnitsString(void)
1090
{
Remek Zajac's avatar
Remek Zajac committed
1091
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsVerticalDistanceUnitsTranslation("alt m");
1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
    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");
    }
}

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

1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157
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;
    }
}


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

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

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

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

1191
            settingsDecimalPlaces += -log10(ctest);
1192

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

    return actualDecimalPlaces;
}
1202

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

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

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

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

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

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

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

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

1347 1348 1349
    return metaData;
}

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

1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent)
{
    QMap<QString, FactMetaData*> metaDataMap;

    QFile jsonFile(jsonFilename);
    if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "Unable to open file" << jsonFilename << jsonFile.errorString();
        return metaDataMap;
    }

    QByteArray bytes = jsonFile.readAll();
    jsonFile.close();
    QJsonParseError jsonParseError;
    QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
    if (jsonParseError.error != QJsonParseError::NoError) {
Don Gagne's avatar
Don Gagne committed
1373
        qWarning() <<  "Unable to parse json document filename:error:offset" << jsonFilename << jsonParseError.errorString() << jsonParseError.offset;
1374 1375 1376
        return metaDataMap;
    }

1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397
    QJsonArray factArray;
    QMap<QString /* define name */, QString /* define value */> defineMap;

    if (doc.isObject()) {
        // Check for Defines/Facts format
        QString errorString;
        QList<JsonHelper::KeyValidateInfo> keyInfoList = {
            { FactMetaData::_jsonMetaDataDefinesName,   QJsonValue::Object, true },
            { FactMetaData::_jsonMetaDataFactsName,     QJsonValue::Array, true },
        };
        if (!JsonHelper::validateKeys(doc.object(), keyInfoList, errorString)) {
            qWarning() << "Json document incorrect format:" << errorString;
            return metaDataMap;
        }

        _loadJsonDefines(doc.object()[FactMetaData::_jsonMetaDataDefinesName].toObject(), defineMap);
        factArray = doc.object()[FactMetaData::_jsonMetaDataFactsName].toArray();
    } else if (doc.isArray()) {
        factArray = doc.array();
    } else {
        qWarning() << "Json document is neither array nor object";
1398 1399 1400
        return metaDataMap;
    }

1401
    return createMapFromJsonArray(factArray, defineMap, metaDataParent);
1402
}
1403

1404
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray jsonArray, QMap<QString, QString>& defineMap, QObject* metaDataParent)
1405 1406 1407 1408
{
    QMap<QString, FactMetaData*> metaDataMap;
    for (int i=0; i<jsonArray.count(); i++) {
        QJsonValue jsonValue = jsonArray.at(i);
1409 1410 1411 1412 1413
        if (!jsonValue.isObject()) {
            qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
            continue;
        }
        QJsonObject jsonObject = jsonValue.toObject();
1414
        FactMetaData* metaData = createFromJsonObject(jsonObject, defineMap, metaDataParent);
1415 1416
        if (metaDataMap.contains(metaData->name())) {
            qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
1417
            delete metaData;
1418 1419 1420 1421 1422 1423
        } else {
            metaDataMap[metaData->name()] = metaData;
        }
    }
    return metaDataMap;
}
1424 1425 1426 1427

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.
1428
    return qMax(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
1429 1430 1431 1432 1433
}

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.
1434
    return qMin(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
1435
}
1436 1437 1438 1439 1440 1441 1442 1443

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