FactMetaData.cc 47.4 KB
Newer Older
1 2 3 4 5 6 7 8 9
/****************************************************************************
 *
 *   (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 *
 * 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
static const char* kDefaultCategory = QT_TRANSLATE_NOOP("FactMetaData", "Other");
static const char* kDefaultGroup    = QT_TRANSLATE_NOOP("FactMetaData", "Misc");
37

38 39
// Built in translations for all Facts
const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] = {
40 41 42 43
    { "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 },
44
};
Don Gagne's avatar
Don Gagne committed
45

46 47
// Translations driven by app settings
const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = {
Gus Grubba's avatar
Gus Grubba committed
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    { "m",      "m",        FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsMeters,         FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "meter",  "meter",    FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsMeters,         FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "meters", "meters",   FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsMeters,         FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "cm/px",  "cm/px",    FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsMeters,         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^2",      FactMetaData::UnitArea,        UnitsSettings::AreaUnitsSquareMeters,       FactMetaData::_defaultTranslator,                   FactMetaData::_defaultTranslator },
    { "m",      "ft",       FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsFeet,           FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "meter",  "ft",       FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsFeet,           FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "meters", "ft",       FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsFeet,           FactMetaData::_metersToFeet,                        FactMetaData::_feetToMeters },
    { "cm/px",  "in/px",    FactMetaData::UnitDistance,    UnitsSettings::DistanceUnitsFeet,           FactMetaData::_centimetersToInches,                 FactMetaData::_inchesToCentimeters },
    { "m^2",    "km^2",     FactMetaData::UnitArea,        UnitsSettings::AreaUnitsSquareKilometers,   FactMetaData::_squareMetersToSquareKilometers,      FactMetaData::_squareKilometersToSquareMeters },
    { "m^2",    "ha",       FactMetaData::UnitArea,        UnitsSettings::AreaUnitsHectares,           FactMetaData::_squareMetersToHectares,              FactMetaData::_hectaresToSquareMeters },
    { "m^2",    "ft^2",     FactMetaData::UnitArea,        UnitsSettings::AreaUnitsSquareFeet,         FactMetaData::_squareMetersToSquareFeet,            FactMetaData::_squareFeetToSquareMeters },
    { "m^2",    "ac",       FactMetaData::UnitArea,        UnitsSettings::AreaUnitsAcres,              FactMetaData::_squareMetersToAcres,                 FactMetaData::_acresToSquareMeters },
    { "m^2",    "mi^2",     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 },
69 70
};

71 72 73 74 75 76 77
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";
78
const char* FactMetaData::_mobileDefaultValueJsonKey =  "mobileDefaultValue";
79 80
const char* FactMetaData::_minJsonKey =                 "min";
const char* FactMetaData::_maxJsonKey =                 "max";
81
const char* FactMetaData::_incrementJsonKey =           "increment";
82
const char* FactMetaData::_hasControlJsonKey =          "control";
83

84
FactMetaData::FactMetaData(QObject* parent)
85 86 87 88
    : QObject               (parent)
    , _type                 (valueTypeInt32)
    , _decimalPlaces        (unknownDecimalPlaces)
    , _rawDefaultValue      (0)
89
    , _defaultValueAvailable(false)
90 91 92 93 94 95 96
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
    , _rebootRequired       (false)
97
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
98 99
    , _hasControl           (true)
    , _readOnly             (false)
100
    , _writeOnly            (false)
101
    , _volatile             (false)
Don Gagne's avatar
Don Gagne committed
102
{
103 104
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
Don Gagne's avatar
Don Gagne committed
105 106
}

107
FactMetaData::FactMetaData(ValueType_t type, QObject* parent)
108 109 110 111
    : QObject               (parent)
    , _type                 (type)
    , _decimalPlaces        (unknownDecimalPlaces)
    , _rawDefaultValue      (0)
112
    , _defaultValueAvailable(false)
113 114 115 116 117 118 119
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
    , _rebootRequired       (false)
120
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
121 122
    , _hasControl           (true)
    , _readOnly             (false)
123
    , _writeOnly            (false)
124
    , _volatile             (false)
125
{
126 127
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
128 129
}

Don Gagne's avatar
Don Gagne committed
130 131 132 133 134 135
FactMetaData::FactMetaData(const FactMetaData& other, QObject* parent)
    : QObject(parent)
{
    *this = other;
}

136
FactMetaData::FactMetaData(ValueType_t type, const QString name, QObject* parent)
137 138 139 140
    : QObject               (parent)
    , _type                 (type)
    , _decimalPlaces        (unknownDecimalPlaces)
    , _rawDefaultValue      (0)
141
    , _defaultValueAvailable(false)
142 143 144 145 146 147 148 149
    , _rawMax               (_maxForType())
    , _maxIsDefaultForType  (true)
    , _rawMin               (_minForType())
    , _minIsDefaultForType  (true)
    , _name                 (name)
    , _rawTranslator        (_defaultTranslator)
    , _cookedTranslator     (_defaultTranslator)
    , _rebootRequired       (false)
150
    , _rawIncrement         (std::numeric_limits<double>::quiet_NaN())
151 152
    , _hasControl           (true)
    , _readOnly             (false)
153
    , _writeOnly            (false)
154
    , _volatile             (false)
155
{
156 157
    _category   = kDefaultCategory;
    _group      = kDefaultGroup;
158 159
}

Don Gagne's avatar
Don Gagne committed
160 161
const FactMetaData& FactMetaData::operator=(const FactMetaData& other)
{
162
    _decimalPlaces          = other._decimalPlaces;
163
    _rawDefaultValue        = other._rawDefaultValue;
Don Gagne's avatar
Don Gagne committed
164
    _defaultValueAvailable  = other._defaultValueAvailable;
165 166
    _bitmaskStrings         = other._bitmaskStrings;
    _bitmaskValues          = other._bitmaskValues;
167 168
    _enumStrings            = other._enumStrings;
    _enumValues             = other._enumValues;
169
    _category               = other._category;
170 171
    _group                  = other._group;
    _longDescription        = other._longDescription;
172
    _rawMax                 = other._rawMax;
Don Gagne's avatar
Don Gagne committed
173
    _maxIsDefaultForType    = other._maxIsDefaultForType;
174
    _rawMin                 = other._rawMin;
175 176 177 178
    _minIsDefaultForType    = other._minIsDefaultForType;
    _name                   = other._name;
    _shortDescription       = other._shortDescription;
    _type                   = other._type;
179 180
    _rawUnits               = other._rawUnits;
    _cookedUnits            = other._cookedUnits;
181 182
    _rawTranslator          = other._rawTranslator;
    _cookedTranslator       = other._cookedTranslator;
183
    _rebootRequired         = other._rebootRequired;
184
    _rawIncrement           = other._rawIncrement;
185
    _hasControl             = other._hasControl;
186
    _readOnly               = other._readOnly;
187
    _writeOnly              = other._writeOnly;
188
    _volatile               = other._volatile;
Don Gagne's avatar
Don Gagne committed
189 190 191
    return *this;
}

192 193 194 195 196 197 198 199 200 201
const QString FactMetaData::defaultCategory()
{
    return QString(kDefaultCategory);
}

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

202
QVariant FactMetaData::rawDefaultValue(void) const
203 204
{
    if (_defaultValueAvailable) {
205
        return _rawDefaultValue;
206 207 208 209 210 211
    } else {
        qWarning() << "Attempt to access unavailable default value";
        return QVariant(0);
    }
}

212
void FactMetaData::setRawDefaultValue(const QVariant& rawDefaultValue)
213
{
Don Gagne's avatar
Don Gagne committed
214
    if (_type == valueTypeString || (_rawMin <= rawDefaultValue && rawDefaultValue <= _rawMax)) {
215
        _rawDefaultValue = rawDefaultValue;
216 217 218 219 220 221
        _defaultValueAvailable = true;
    } else {
        qWarning() << "Attempt to set default value which is outside min/max range";
    }
}

222
void FactMetaData::setRawMin(const QVariant& rawMin)
223
{
Don Gagne's avatar
Don Gagne committed
224
    if (rawMin >= _minForType()) {
225
        _rawMin = rawMin;
Don Gagne's avatar
Don Gagne committed
226
        _minIsDefaultForType = false;
227
    } else {
228
        qWarning() << "Attempt to set min below allowable value for fact: " << name()
229
                   << ", value attempted: " << rawMin
230
                   << ", type: " << type() << ", min for type: " << _minForType();
231
        _rawMin = _minForType();
232 233 234
    }
}

235
void FactMetaData::setRawMax(const QVariant& rawMax)
236
{
237
    if (rawMax > _maxForType()) {
238
        qWarning() << "Attempt to set max above allowable value";
239
        _rawMax = _maxForType();
240
    } else {
241
        _rawMax = rawMax;
Don Gagne's avatar
Don Gagne committed
242
        _maxIsDefaultForType = false;
243 244 245
    }
}

246
QVariant FactMetaData::_minForType(void) const
Don Gagne's avatar
Don Gagne committed
247
{
248
    switch (_type) {
249 250 251 252 253 254 255 256 257
    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:
258
        return QVariant(std::numeric_limits<uint32_t>::min());
259
    case valueTypeInt32:
260 261
        return QVariant(std::numeric_limits<int32_t>::min());
    case valueTypeUint64:
262
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::min());
263
    case valueTypeInt64:
264
        return QVariant((qlonglong)std::numeric_limits<int64_t>::min());
265 266 267 268
    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
269 270
    case valueTypeString:
        return QVariant();
271 272
    case valueTypeBool:
        return QVariant(0);
273 274
    case valueTypeElapsedTimeInSeconds:
        return QVariant(0.0);
275 276
    case valueTypeCustom:
        return QVariant();
277
    }
278

Don Gagne's avatar
Don Gagne committed
279 280
    // Make windows compiler happy, even switch is full cased
    return QVariant();
Don Gagne's avatar
Don Gagne committed
281
}
Don Gagne's avatar
Don Gagne committed
282

283
QVariant FactMetaData::_maxForType(void) const
Don Gagne's avatar
Don Gagne committed
284
{
285
    switch (_type) {
286 287 288 289 290 291 292 293 294
    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:
295
        return QVariant(std::numeric_limits<uint32_t>::max());
296
    case valueTypeInt32:
297 298
        return QVariant(std::numeric_limits<int32_t>::max());
    case valueTypeUint64:
299
        return QVariant((qulonglong)std::numeric_limits<uint64_t>::max());
300
    case valueTypeInt64:
301
        return QVariant((qlonglong)std::numeric_limits<int64_t>::max());
302 303
    case valueTypeFloat:
        return QVariant(std::numeric_limits<float>::max());
304
    case valueTypeElapsedTimeInSeconds:
305 306
    case valueTypeDouble:
        return QVariant(std::numeric_limits<double>::max());
Don Gagne's avatar
Don Gagne committed
307 308
    case valueTypeString:
        return QVariant();
309 310
    case valueTypeBool:
        return QVariant(1);
311 312
    case valueTypeCustom:
        return QVariant();
313
    }
314

Don Gagne's avatar
Don Gagne committed
315 316
    // Make windows compiler happy, even switch is full cased
    return QVariant();
Don Gagne's avatar
Don Gagne committed
317
}
Don Gagne's avatar
Don Gagne committed
318

319
bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertOnly, QVariant& typedValue, QString& errorString)
Don Gagne's avatar
Don Gagne committed
320
{
321
    bool convertOk = false;
322

Don Gagne's avatar
Don Gagne committed
323
    errorString.clear();
324

Don Gagne's avatar
Don Gagne committed
325
    switch (type()) {
326 327 328 329 330
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(rawValue.toInt(&convertOk));
        if (!convertOnly && convertOk) {
331
            if (typedValue < rawMin() || typedValue > rawMax()) {
332
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
Don Gagne's avatar
Don Gagne committed
333
            }
334 335
        }
        break;
336 337 338 339 340 341 342 343
    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;
344 345 346 347 348
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(rawValue.toUInt(&convertOk));
        if (!convertOnly && convertOk) {
349
            if (typedValue < rawMin() || typedValue > rawMax()) {
350
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
Don Gagne's avatar
Don Gagne committed
351
            }
352 353
        }
        break;
354 355 356 357 358 359 360 361
    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;
362 363 364
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(rawValue.toFloat(&convertOk));
        if (!convertOnly && convertOk) {
365
            if (typedValue < rawMin() || typedValue > rawMax()) {
366
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toFloat()).arg(rawMax().toFloat());
Don Gagne's avatar
Don Gagne committed
367
            }
368 369
        }
        break;
370
    case FactMetaData::valueTypeElapsedTimeInSeconds:
371 372 373
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(rawValue.toDouble(&convertOk));
        if (!convertOnly && convertOk) {
374
            if (typedValue < rawMin() || typedValue > rawMax()) {
375
                errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
Don Gagne's avatar
Don Gagne committed
376
            }
377 378
        }
        break;
Don Gagne's avatar
Don Gagne committed
379 380 381 382
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(rawValue.toString());
        break;
383 384 385 386
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(rawValue.toBool());
        break;
387 388 389 390
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(rawValue.toByteArray());
        break;
Don Gagne's avatar
Don Gagne committed
391
    }
392

Don Gagne's avatar
Don Gagne committed
393
    if (!convertOk) {
394
        errorString += tr("Invalid number");
Don Gagne's avatar
Don Gagne committed
395
    }
396

Don Gagne's avatar
Don Gagne committed
397 398
    return convertOk && errorString.isEmpty();
}
399

400 401 402 403 404 405 406
bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool convertOnly, QVariant& typedValue, QString& errorString)
{
    bool convertOk = false;

    errorString.clear();

    switch (type()) {
407 408 409 410 411 412
    case FactMetaData::valueTypeInt8:
    case FactMetaData::valueTypeInt16:
    case FactMetaData::valueTypeInt32:
        typedValue = QVariant(cookedValue.toInt(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
413
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
414
            }
415 416
        }
        break;
417 418 419 420 421 422 423 424
    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;
425 426 427 428 429 430
    case FactMetaData::valueTypeUint8:
    case FactMetaData::valueTypeUint16:
    case FactMetaData::valueTypeUint32:
        typedValue = QVariant(cookedValue.toUInt(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
431
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
432
            }
433 434
        }
        break;
435 436 437 438 439 440 441 442
    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;
443 444 445 446
    case FactMetaData::valueTypeFloat:
        typedValue = QVariant(cookedValue.toFloat(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
447
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
448
            }
449 450
        }
        break;
451
    case FactMetaData::valueTypeElapsedTimeInSeconds:
452 453 454 455
    case FactMetaData::valueTypeDouble:
        typedValue = QVariant(cookedValue.toDouble(&convertOk));
        if (!convertOnly && convertOk) {
            if (cookedMin() > typedValue || typedValue > cookedMax()) {
456
                errorString = tr("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
457
            }
458 459
        }
        break;
Don Gagne's avatar
Don Gagne committed
460 461 462 463
    case FactMetaData::valueTypeString:
        convertOk = true;
        typedValue = QVariant(cookedValue.toString());
        break;
464 465 466 467
    case FactMetaData::valueTypeBool:
        convertOk = true;
        typedValue = QVariant(cookedValue.toBool());
        break;
468 469 470 471
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
472 473 474
    }

    if (!convertOk) {
475
        errorString += tr("Invalid number");
476 477 478 479 480
    }

    return convertOk && errorString.isEmpty();
}

481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
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;
497 498 499 500 501 502 503 504 505 506
    case FactMetaData::valueTypeInt64:
        typedValue = QVariant(cookedValue.toLongLong(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
507 508 509 510 511 512 513 514 515 516 517 518
    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;
519 520 521 522 523 524 525 526 527 528
    case FactMetaData::valueTypeUint64:
        typedValue = QVariant(cookedValue.toULongLong(&convertOk));
        if (convertOk) {
            if (cookedMin() > typedValue) {
                typedValue = cookedMin();
            } else if(typedValue > cookedMax()) {
                typedValue = cookedMax();
            }
        }
        break;
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557
    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;
558 559 560 561
    case FactMetaData::valueTypeCustom:
        convertOk = true;
        typedValue = QVariant(cookedValue.toByteArray());
        break;
562 563 564 565
    }
    return convertOk;
}

566 567 568 569 570 571 572 573 574
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;
575
    setBuiltInTranslator();
576 577 578 579 580 581 582 583
}

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

584 585 586 587 588 589 590 591 592
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;
593
    setBuiltInTranslator();
594 595
}

596 597 598 599 600 601
void FactMetaData::addEnumInfo(const QString& name, const QVariant& value)
{
    _enumStrings << name;
    _enumValues << value;
}

602 603 604 605 606
void FactMetaData::setTranslators(Translator rawTranslator, Translator cookedTranslator)
{
    _rawTranslator = rawTranslator;
    _cookedTranslator = cookedTranslator;
}
607

608
void FactMetaData::setBuiltInTranslator(void)
609 610 611 612 613
{
    if (_enumStrings.count()) {
        // No translation if enum
        setTranslators(_defaultTranslator, _defaultTranslator);
        _cookedUnits = _rawUnits;
614
        return;
615
    } else {
616 617
        for (size_t i=0; i<sizeof(_rgBuiltInTranslations)/sizeof(_rgBuiltInTranslations[0]); i++) {
            const BuiltInTranslation_s* pBuiltInTranslation = &_rgBuiltInTranslations[i];
618

619
            if (pBuiltInTranslation->rawUnits.toLower() == _rawUnits.toLower()) {
620 621
                _cookedUnits = pBuiltInTranslation->cookedUnits;
                setTranslators(pBuiltInTranslation->rawTranslator, pBuiltInTranslation->cookedTranslator);
622
                return;
623 624 625
            }
        }
    }
626 627 628

    // Translator not yet set, try app settings translators
    _setAppSettingsTranslators();
629 630 631 632
}

QVariant FactMetaData::_degreesToRadians(const QVariant& degrees)
{
633
    return QVariant(qDegreesToRadians(degrees.toDouble()));
634 635 636 637
}

QVariant FactMetaData::_radiansToDegrees(const QVariant& radians)
{
638
    return QVariant(qRadiansToDegrees(radians.toDouble()));
639 640 641 642
}

QVariant FactMetaData::_centiDegreesToDegrees(const QVariant& centiDegrees)
{
643
    return QVariant(centiDegrees.toReal() / 100.0);
644 645 646 647
}

QVariant FactMetaData::_degreesToCentiDegrees(const QVariant& degrees)
{
648
    return QVariant(qRound(degrees.toReal() * 100.0));
649 650
}

651 652 653 654 655 656 657 658 659 660 661 662 663 664
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;
}

665 666
QVariant FactMetaData::_metersToFeet(const QVariant& meters)
{
667
    return QVariant(meters.toDouble() * 1.0/constants.feetToMeters);
668 669 670 671
}

QVariant FactMetaData::_feetToMeters(const QVariant& feet)
{
672
    return QVariant(feet.toDouble() * constants.feetToMeters);
673 674
}

675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 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
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);
}

725 726
QVariant FactMetaData::_metersPerSecondToMilesPerHour(const QVariant& metersPerSecond)
{
727
    return QVariant((metersPerSecond.toDouble() * 1.0/constants.milesToMeters) * constants.secondsPerHour);
728 729 730 731
}

QVariant FactMetaData::_milesPerHourToMetersPerSecond(const QVariant& milesPerHour)
{
732
    return QVariant((milesPerHour.toDouble() * constants.milesToMeters) / constants.secondsPerHour);
733 734 735 736
}

QVariant FactMetaData::_metersPerSecondToKilometersPerHour(const QVariant& metersPerSecond)
{
737
    return QVariant((metersPerSecond.toDouble() / 1000.0) * constants.secondsPerHour);
738 739 740 741
}

QVariant FactMetaData::_kilometersPerHourToMetersPerSecond(const QVariant& kilometersPerHour)
{
742
    return QVariant((kilometersPerHour.toDouble() * 1000.0) / constants.secondsPerHour);
743 744 745 746
}

QVariant FactMetaData::_metersPerSecondToKnots(const QVariant& metersPerSecond)
{
747
    return QVariant(metersPerSecond.toDouble() * constants.secondsPerHour / (1000.0 * constants.knotsToKPH));
748 749 750 751
}

QVariant FactMetaData::_knotsToMetersPerSecond(const QVariant& knots)
{
752
    return QVariant(knots.toDouble() * (1000.0 * constants.knotsToKPH / constants.secondsPerHour));
753 754
}

755 756 757 758 759 760 761 762 763 764
QVariant FactMetaData::_percentToNorm(const QVariant& percent)
{
    return QVariant(percent.toDouble() / 100.0);
}

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

765 766 767 768 769 770 771 772 773 774
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);
}

775 776 777 778 779 780 781 782 783 784
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));
}

785 786 787 788 789
void FactMetaData::setRawUnits(const QString& rawUnits)
{
    _rawUnits = rawUnits;
    _cookedUnits = rawUnits;

790
    setBuiltInTranslator();
791
}
Don Gagne's avatar
Don Gagne committed
792 793 794 795 796 797 798 799 800

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

    unknownType = false;

    knownTypeStrings << QStringLiteral("Uint8")
801 802 803 804 805
                     << QStringLiteral("Int8")
                     << QStringLiteral("Uint16")
                     << QStringLiteral("Int16")
                     << QStringLiteral("Uint32")
                     << QStringLiteral("Int32")
806 807
                     << QStringLiteral("Uint64")
                     << QStringLiteral("Int64")
808
                     << QStringLiteral("Float")
Don Gagne's avatar
Don Gagne committed
809
                     << QStringLiteral("Double")
810
                     << QStringLiteral("String")
811
                     << QStringLiteral("Bool")
812 813
                     << QStringLiteral("ElapsedSeconds")
                     << QStringLiteral("Custom");
Don Gagne's avatar
Don Gagne committed
814 815

    knownTypes << valueTypeUint8
816 817 818 819 820
               << valueTypeInt8
               << valueTypeUint16
               << valueTypeInt16
               << valueTypeUint32
               << valueTypeInt32
821 822
               << valueTypeUint64
               << valueTypeInt64
823
               << valueTypeFloat
Don Gagne's avatar
Don Gagne committed
824
               << valueTypeDouble
825
               << valueTypeString
826
               << valueTypeBool
827 828
               << valueTypeElapsedTimeInSeconds
               << valueTypeCustom;
Don Gagne's avatar
Don Gagne committed
829 830 831 832 833 834 835 836 837 838 839

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

    unknownType = true;

    return valueTypeDouble;
}
840 841 842 843

size_t FactMetaData::typeToSize(ValueType_t type)
{
    switch (type) {
844 845 846
    case valueTypeUint8:
    case valueTypeInt8:
        return 1;
847

848 849 850
    case valueTypeUint16:
    case valueTypeInt16:
        return 2;
851

852 853 854 855
    case valueTypeUint32:
    case valueTypeInt32:
    case valueTypeFloat:
        return 4;
856

857 858
    case valueTypeUint64:
    case valueTypeInt64:
859 860
    case valueTypeDouble:
        return 8;
861

862 863 864
    case valueTypeCustom:
        return MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN;

865 866
    default:
        qWarning() << "Unsupported fact value type" << type;
Don Gagne's avatar
Don Gagne committed
867
        return 0;
868 869
    }
}
870

871 872
/// Set translators according to app settings
void FactMetaData::_setAppSettingsTranslators(void)
873
{
874 875
    // We can only translate between real numbers
    if (!_enumStrings.count() && (type() == valueTypeDouble || type() == valueTypeFloat)) {
876 877
        for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
            const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904

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

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

            switch (pAppSettingsTranslation->unitType) {
            case UnitDistance:
                settingsUnits = settings->distanceUnits()->rawValue().toUInt();
                break;
            case UnitSpeed:
                settingsUnits = settings->speedUnits()->rawValue().toUInt();
                break;
            case UnitArea:
                settingsUnits = settings->areaUnits()->rawValue().toUInt();
                break;
            case UnitTemperature:
                settingsUnits = settings->temperatureUnits()->rawValue().toUInt();
                break;
            default:
                break;
            }

            if (settingsUnits == pAppSettingsTranslation->unitOption) {
                 _cookedUnits = pAppSettingsTranslation->cookedUnits;
905 906 907 908 909 910
                setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator);
                return;
            }
        }
    }
}
911 912 913 914 915

const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDistanceUnitsTranslation(const QString& rawUnits)
{
    for (size_t i=0; i<sizeof(_rgAppSettingsTranslations)/sizeof(_rgAppSettingsTranslations[0]); i++) {
        const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
916 917 918 919 920

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

921
        uint settingsUnits = qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt();
922 923 924

        if (pAppSettingsTranslation->unitType == UnitDistance
                && pAppSettingsTranslation->unitOption == settingsUnits) {
925 926 927 928 929 930
            return pAppSettingsTranslation;
        }
    }
    return NULL;
}

931 932 933 934
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];
935 936 937 938 939 940 941 942 943

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

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

        if (pAppSettingsTranslation->unitType == UnitArea
                && pAppSettingsTranslation->unitOption == settingsUnits) {
944 945 946 947 948 949 950
            return pAppSettingsTranslation;
        }
    }

    return NULL;
}

951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979
QVariant FactMetaData::metersToAppSettingsDistanceUnits(const QVariant& meters)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsDistanceUnitsTranslation("m");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->rawTranslator(meters);
    } else {
        return meters;
    }
}

QVariant FactMetaData::appSettingsDistanceUnitsToMeters(const QVariant& distance)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsDistanceUnitsTranslation("m");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(distance);
    } else {
        return distance;
    }
}

QString FactMetaData::appSettingsDistanceUnitsString(void)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsDistanceUnitsTranslation("m");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m");
    }
}
980

981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
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");
    }
}

1011 1012
double FactMetaData::cookedIncrement(void) const
{
1013
    return _rawTranslator(this->rawIncrement()).toDouble();
1014 1015
}

1016 1017 1018 1019 1020 1021
int FactMetaData::decimalPlaces(void) const
{
    int actualDecimalPlaces = defaultDecimalPlaces;
    int incrementDecimalPlaces = unknownDecimalPlaces;

    // First determine decimal places from increment
1022
    double increment = _rawTranslator(this->rawIncrement()).toDouble();
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
    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));
        }
    }

    // Correct decimal places is the larger of the two, increment or meta data value
    if (incrementDecimalPlaces != unknownDecimalPlaces && _decimalPlaces == unknownDecimalPlaces) {
        actualDecimalPlaces = incrementDecimalPlaces;
    } else {
1040 1041
        // Adjust decimal places for cooked translation
        int settingsDecimalPlaces = _decimalPlaces == unknownDecimalPlaces ? defaultDecimalPlaces : _decimalPlaces;
1042 1043 1044 1045 1046 1047 1048 1049
        double ctest = _rawTranslator(1.0).toDouble();

        settingsDecimalPlaces += -log10(ctest);

        settingsDecimalPlaces = qMin(25, settingsDecimalPlaces);
        settingsDecimalPlaces = qMax(0, settingsDecimalPlaces);

        actualDecimalPlaces = qMax(settingsDecimalPlaces, incrementDecimalPlaces);
1050 1051 1052 1053
    }

    return actualDecimalPlaces;
}
1054

1055
FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QObject* metaDataParent)
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066
{
    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);
    }

1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078
    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
        { _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 },
    };
    if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) {
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094
        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;
1095
    if (JsonHelper::parseEnum(json, enumStrings, enumValues, errorString, metaData->name())) {
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119
        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());
    }
1120 1121

    QString defaultValueJsonKey;
1122 1123
#ifdef __mobile__
    if (json.contains(_mobileDefaultValueJsonKey)) {
Don Gagne's avatar
Don Gagne committed
1124
        defaultValueJsonKey = _mobileDefaultValueJsonKey;
1125
    }
1126
#endif
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143
    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;
        }
    }

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

1158
    if (json.contains(_minJsonKey)) {
1159 1160
        QVariant typedValue;
        QString errorString;
1161 1162 1163 1164 1165 1166 1167 1168 1169
        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;
        }
1170
    }
1171

1172
    if (json.contains(_maxJsonKey)) {
1173 1174
        QVariant typedValue;
        QString errorString;
1175 1176 1177 1178 1179 1180 1181 1182 1183
        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;
        }
1184
    }
1185

1186 1187 1188 1189 1190
    if (json.contains(_hasControlJsonKey)) {
        metaData->setHasControl(json[_hasControlJsonKey].toBool());
    } else {
        metaData->setHasControl(true);
    }
1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219

    return metaData;
}

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) {
        qWarning() <<  "Unable to parse json document" << jsonFilename << jsonParseError.errorString();
        return metaDataMap;
    }

    if (!doc.isArray()) {
        qWarning() << "json document is not array";
        return metaDataMap;
    }

    QJsonArray jsonArray = doc.array();
1220 1221
    return createMapFromJsonArray(jsonArray, metaDataParent);
}
1222

1223 1224 1225 1226 1227
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray jsonArray, QObject* metaDataParent)
{
    QMap<QString, FactMetaData*> metaDataMap;
    for (int i=0; i<jsonArray.count(); i++) {
        QJsonValue jsonValue = jsonArray.at(i);
1228 1229 1230 1231 1232
        if (!jsonValue.isObject()) {
            qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
            continue;
        }
        QJsonObject jsonObject = jsonValue.toObject();
1233
        FactMetaData* metaData = createFromJsonObject(jsonObject, metaDataParent);
1234 1235
        if (metaDataMap.contains(metaData->name())) {
            qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
1236
            delete metaData;
1237 1238 1239 1240 1241 1242
        } else {
            metaDataMap[metaData->name()] = metaData;
        }
    }
    return metaDataMap;
}
1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268

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.
    QVariant cookedMax = _rawTranslator(_rawMax);
    QVariant cookedMin = _rawTranslator(_rawMin);
    if (cookedMax < cookedMin) {
        // We need to flip
        return cookedMin;
    } else {
        return cookedMax;
    }
}

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.
    QVariant cookedMax = _rawTranslator(_rawMax);
    QVariant cookedMin = _rawTranslator(_rawMin);
    if (cookedMax < cookedMin) {
        // We need to flip
        return cookedMax;
    } else {
        return cookedMin;
    }
}
1269 1270 1271 1272 1273 1274 1275 1276

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