QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
FactMetaData.cc
Go to the documentation of this file.
1#include "FactMetaData.h"
2#include "JsonParsing.h"
3#include "MAVLinkLib.h"
5#include "SettingsManager.h"
6#include "UnitsSettings.h"
7
8#include <QtCore/QJsonArray>
9#include <QtCore/QJsonObject>
10#include <QtCore/QtMath>
11
12#include <limits>
13
14QGC_LOGGING_CATEGORY(FactMetaDataLog, "FactSystem.FactMetaData")
15
16// Bitmask values are built with `1u << index`, so valid indices are bounded by the
17// width of unsigned int. Keep this in sync with the shift in createFromJsonObject.
18static constexpr int kMaxBitmaskIndex = std::numeric_limits<unsigned int>::digits - 1;
19
20// Built in translations for all Facts
21const FactMetaData::BuiltInTranslation_s FactMetaData::_rgBuiltInTranslations[] = {
22 { "centi-degrees", "deg", FactMetaData::_centiDegreesToDegrees, FactMetaData::_degreesToCentiDegrees },
23 { "radians", "deg", FactMetaData::_radiansToDegrees, FactMetaData::_degreesToRadians },
24 { "rad", "deg", FactMetaData::_radiansToDegrees, FactMetaData::_degreesToRadians },
25 { "gimbal-degrees", "deg", FactMetaData::_mavlinkGimbalDegreesToUserGimbalDegrees, FactMetaData::_userGimbalDegreesToMavlinkGimbalDegrees },
26 { "norm", "%", FactMetaData::_normToPercent, FactMetaData::_percentToNorm },
27 { "centi-celsius", "C", FactMetaData::_centiCelsiusToCelsius, FactMetaData::_celsiusToCentiCelsius },
28};
29
30// Translations driven by app settings
31const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTranslations[] = {
32 { "m", "m", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
33 { "meter", "meter", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
34 { "meters", "meters", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
35 // 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
36 { "vertical m", "m", FactMetaData::UnitVerticalDistance, UnitsSettings::VerticalDistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
37 { "cm/px", "cm/px", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
38 { "m/s", "m/s", FactMetaData::UnitSpeed, UnitsSettings::SpeedUnitsMetersPerSecond, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
39 { "C", "C", FactMetaData::UnitTemperature, UnitsSettings::TemperatureUnitsCelsius, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
40 { "m^2", "m^2", FactMetaData::UnitArea, UnitsSettings::AreaUnitsSquareMeters, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
41 { "m", "ft", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters },
42 { "meter", "ft", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters },
43 { "meters", "ft", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters },
44 { "vertical m", "ft", FactMetaData::UnitVerticalDistance, UnitsSettings::VerticalDistanceUnitsFeet, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters },
45 { "cm/px", "in/px", FactMetaData::UnitHorizontalDistance, UnitsSettings::HorizontalDistanceUnitsFeet, FactMetaData::_centimetersToInches, FactMetaData::_inchesToCentimeters },
46 { "m^2", "km^2", FactMetaData::UnitArea, UnitsSettings::AreaUnitsSquareKilometers, FactMetaData::_squareMetersToSquareKilometers, FactMetaData::_squareKilometersToSquareMeters },
47 { "m^2", "ha", FactMetaData::UnitArea, UnitsSettings::AreaUnitsHectares, FactMetaData::_squareMetersToHectares, FactMetaData::_hectaresToSquareMeters },
48 { "m^2", "ft^2", FactMetaData::UnitArea, UnitsSettings::AreaUnitsSquareFeet, FactMetaData::_squareMetersToSquareFeet, FactMetaData::_squareFeetToSquareMeters },
49 { "m^2", "ac", FactMetaData::UnitArea, UnitsSettings::AreaUnitsAcres, FactMetaData::_squareMetersToAcres, FactMetaData::_acresToSquareMeters },
50 { "m^2", "mi^2", FactMetaData::UnitArea, UnitsSettings::AreaUnitsSquareMiles, FactMetaData::_squareMetersToSquareMiles, FactMetaData::_squareMilesToSquareMeters },
51 { "m/s", "ft/s", FactMetaData::UnitSpeed, UnitsSettings::SpeedUnitsFeetPerSecond, FactMetaData::_metersToFeet, FactMetaData::_feetToMeters },
52 { "m/s", "mph", FactMetaData::UnitSpeed, UnitsSettings::SpeedUnitsMilesPerHour, FactMetaData::_metersPerSecondToMilesPerHour, FactMetaData::_milesPerHourToMetersPerSecond },
53 { "m/s", "km/h", FactMetaData::UnitSpeed, UnitsSettings::SpeedUnitsKilometersPerHour, FactMetaData::_metersPerSecondToKilometersPerHour, FactMetaData::_kilometersPerHourToMetersPerSecond },
54 { "m/s", "kn", FactMetaData::UnitSpeed, UnitsSettings::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond },
55 { "C", "F", FactMetaData::UnitTemperature, UnitsSettings::TemperatureUnitsFarenheit, FactMetaData::_celsiusToFarenheit, FactMetaData::_farenheitToCelsius },
56 { "g", "g", FactMetaData::UnitWeight, UnitsSettings::WeightUnitsGrams, FactMetaData::_defaultTranslator, FactMetaData::_defaultTranslator },
57 { "g", "kg", FactMetaData::UnitWeight, UnitsSettings::WeightUnitsKg, FactMetaData::_gramsToKilograms, FactMetaData::_kilogramsToGrams },
58 { "g", "oz", FactMetaData::UnitWeight, UnitsSettings::WeightUnitsOz, FactMetaData::_gramsToOunces, FactMetaData::_ouncesToGrams },
59 { "g", "lbs", FactMetaData::UnitWeight, UnitsSettings::WeightUnitsLbs, FactMetaData::_gramsToPunds, FactMetaData::_poundsToGrams },
60};
61
63 : QObject(parent)
64{
65 // qCDebug(FactMetaDataLog) << Q_FUNC_INFO << this;
66}
67
69 : QObject(parent)
70 , _type(type)
71{
72 // qCDebug(FactMetaDataLog) << Q_FUNC_INFO << this;
73}
74
75FactMetaData::FactMetaData(const FactMetaData &other, QObject *parent)
76 : QObject(parent)
77{
78 // qCDebug(FactMetaDataLog) << Q_FUNC_INFO << this;
79 *this = other;
80}
81
82FactMetaData::FactMetaData(ValueType_t type, const QString &name, QObject *parent)
83 : QObject(parent)
84 , _type(type)
85 , _name(name)
86{
87 // qCDebug(FactMetaDataLog) << Q_FUNC_INFO << this;
88}
89
91{
92 // qCDebug(FactMetaDataLog) << Q_FUNC_INFO << this;
93}
94
96{
97 _decimalPlaces = other._decimalPlaces;
98 _maxStringLength = other._maxStringLength;
99 _rawDefaultValue = other._rawDefaultValue;
100 _defaultValueAvailable = other._defaultValueAvailable;
101 _bitmaskStrings = other._bitmaskStrings;
102 _bitmaskValues = other._bitmaskValues;
103 _enumStrings = other._enumStrings;
104 _enumValues = other._enumValues;
105 _category = other._category;
106 _group = other._group;
107 _longDescription = other._longDescription;
108 _rawMax = other._rawMax;
109 _rawMin = other._rawMin;
110 _rawUserMin = other._rawUserMin;
111 _rawUserMax = other._rawUserMax;
112 _name = other._name;
113 _shortDescription = other._shortDescription;
114 _type = other._type;
115 _rawUnits = other._rawUnits;
116 _cookedUnits = other._cookedUnits;
117 _rawTranslator = other._rawTranslator;
118 _cookedTranslator = other._cookedTranslator;
119 _vehicleRebootRequired = other._vehicleRebootRequired;
120 _qgcRebootRequired = other._qgcRebootRequired;
121 _rawIncrement = other._rawIncrement;
122 _hasControl = other._hasControl;
123 _readOnly = other._readOnly;
124 _writeOnly = other._writeOnly;
125 _volatile = other._volatile;
126
127 return *this;
128}
129
130void FactMetaData::setMaxStringLength(int maxStringLength)
131{
132 if (maxStringLength < 0) {
133 qCWarning(FactMetaDataLog) << "Invalid maxStringLength, must be >= 0,"
134 << "name:" << name()
135 << "value:" << maxStringLength;
136 maxStringLength = 0;
137 }
138 _maxStringLength = maxStringLength;
139}
140
142{
143 if (_defaultValueAvailable) {
144 return _rawDefaultValue;
145 } else {
146 qCWarning(FactMetaDataLog) << "Attempt to access unavailable default value";
147 return QVariant(0);
148 }
149}
150
151void FactMetaData::setRawDefaultValue(const QVariant &rawDefaultValue)
152{
153 if ((_type == valueTypeString) || (isInRawMinLimit(rawDefaultValue) && isInRawMaxLimit(rawDefaultValue))) {
154 _rawDefaultValue = rawDefaultValue;
155 _defaultValueAvailable = true;
156 } else {
157 qCWarning(FactMetaDataLog) << "Attempt to set default value which is outside min/max range. Name:" << name()
158 << ", attempted value:" << rawDefaultValue
159 << ", type:" << type()
160 << ", min:" << _rawMin
161 << ", max:" << _rawMax;
162 }
163}
164
165void FactMetaData::setRawDefaultValueFirmwareForce(const QVariant &rawDefaultValue)
166{
167 _rawDefaultValue = rawDefaultValue;
168 _defaultValueAvailable = true;
169}
170
171void FactMetaData::setRawMin(const QVariant &rawMin)
172{
173 if (isInRawMinLimit(rawMin)) {
174 _rawMin = rawMin;
175 if (_rawUserMin.isValid() && !isInRawMinLimit(_rawUserMin)) {
176 _rawUserMin = _rawMin;
177 }
178 } else {
179 qCWarning(FactMetaDataLog) << "Attempt to set min below allowable value for fact:" << name()
180 << ", attempted value:" << rawMin
181 << ", type:" << type()
182 << ", min for type:" << _minForType();
183 _rawMin = _minForType();
184 _rawUserMin = _rawMin;
185 }
186}
187
188void FactMetaData::setRawMax(const QVariant &rawMax)
189{
190 if (isInRawMaxLimit(rawMax)) {
191 _rawMax = rawMax;
192 if (_rawUserMax.isValid() && !isInRawMaxLimit(_rawUserMax)) {
193 _rawUserMax = _rawMax;
194 }
195 } else {
196 qCWarning(FactMetaDataLog) << "Attempt to set max above allowable value for fact:" << name()
197 << ", attempted value:" << rawMax
198 << ", type:" << type()
199 << ", max for type:" << _maxForType();
200 _rawMax = _maxForType();
201 _rawUserMax = _rawMax;
202 }
203}
204
205void FactMetaData::setRawUserMin(const QVariant &rawUserMin)
206{
207 if (isInRawMinLimit(rawUserMin)) {
208 _rawUserMin = rawUserMin;
209 } else {
210 qCWarning(FactMetaDataLog) << "Attempt to set user min below allowable value for fact:" << name()
211 << ", attempted value:" << rawUserMin
212 << ", type:" << type()
213 << ", min:" << _rawMin;
214 _rawUserMin = _rawMin;
215 }
216}
217
218void FactMetaData::setRawUserMax(const QVariant &rawUserMax)
219{
220 if (isInRawMaxLimit(rawUserMax)) {
221 _rawUserMax = rawUserMax;
222 } else {
223 qCWarning(FactMetaDataLog) << "Attempt to set user max above allowable value for fact:" << name()
224 << ", attempted value:" << rawUserMax
225 << ", type:" << type()
226 << ", max:" << _rawMax;
227 _rawUserMax = _rawMax;
228 }
229}
230
231bool FactMetaData::isInRawMinLimit(const QVariant &variantValue) const
232{
233 switch (_type) {
234 case valueTypeUint8:
235 return (_rawMin.value<unsigned char>() <= variantValue.value<unsigned char>());
236 case valueTypeInt8:
237 return (_rawMin.value<signed char>() <= variantValue.value<signed char>());
238 case valueTypeUint16:
239 return (_rawMin.value<unsigned short int>() <= variantValue.value<unsigned short int>());
240 case valueTypeInt16:
241 return (_rawMin.value<short int>() <= variantValue.value<short int>());
242 case valueTypeUint32:
243 return (_rawMin.value<uint32_t>() <= variantValue.value<uint32_t>());
244 case valueTypeInt32:
245 return (_rawMin.value<int32_t>() <= variantValue.value<int32_t>());
246 case valueTypeUint64:
247 return (_rawMin.value<uint64_t>() <= variantValue.value<uint64_t>());
248 case valueTypeInt64:
249 return (_rawMin.value<int64_t>() <= variantValue.value<int64_t>());
250 case valueTypeFloat:
251 return ((qIsNaN(variantValue.toFloat())) || (_rawMin.value<float>() <= variantValue.value<float>()));
252 case valueTypeDouble:
253 return ((qIsNaN(variantValue.toDouble())) || (_rawMin.value<double>() <= variantValue.value<double>()));
254 default:
255 return true;
256 }
257}
258
259bool FactMetaData::isInRawMaxLimit(const QVariant &variantValue) const
260{
261 switch (_type) {
262 case valueTypeUint8:
263 return (_rawMax.value<unsigned char>() >= variantValue.value<unsigned char>());
264 case valueTypeInt8:
265 return (_rawMax.value<signed char>() >= variantValue.value<signed char>());
266 case valueTypeUint16:
267 return (_rawMax.value<unsigned short int>() >= variantValue.value<unsigned short int>());
268 case valueTypeInt16:
269 return (_rawMax.value<short int>() >= variantValue.value<short int>());
270 case valueTypeUint32:
271 return (_rawMax.value<uint32_t>() >= variantValue.value<uint32_t>());
272 case valueTypeInt32:
273 return (_rawMax.value<int32_t>() >= variantValue.value<int32_t>());
274 case valueTypeUint64:
275 return (_rawMax.value<uint64_t>() >= variantValue.value<uint64_t>());
276 case valueTypeInt64:
277 return (_rawMax.value<int64_t>() >= variantValue.value<int64_t>());
278 case valueTypeFloat:
279 return (qIsNaN(variantValue.toFloat()) || (_rawMax.value<float>() >= variantValue.value<float>()));
280 case valueTypeDouble:
281 return (qIsNaN(variantValue.toDouble()) || (_rawMax.value<double>() >= variantValue.value<double>()));
282 default:
283 return true;
284 }
285}
286
288{
289 switch (type) {
290 case valueTypeUint8:
291 return QVariant(std::numeric_limits<unsigned char>::min());
292 case valueTypeInt8:
293 return QVariant(std::numeric_limits<signed char>::min());
294 case valueTypeUint16:
295 return QVariant(std::numeric_limits<unsigned short int>::min());
296 case valueTypeInt16:
297 return QVariant(std::numeric_limits<short int>::min());
298 case valueTypeUint32:
299 return QVariant(std::numeric_limits<uint32_t>::min());
300 case valueTypeInt32:
301 return QVariant(std::numeric_limits<int32_t>::min());
302 case valueTypeUint64:
303 return QVariant((qulonglong)std::numeric_limits<uint64_t>::min());
304 case valueTypeInt64:
305 return QVariant((qlonglong)std::numeric_limits<int64_t>::min());
306 case valueTypeFloat:
307 return QVariant(-std::numeric_limits<float>::max());
308 case valueTypeDouble:
309 return QVariant(-std::numeric_limits<double>::max());
310 case valueTypeString:
311 return QVariant();
312 case valueTypeBool:
313 return QVariant(0);
315 return QVariant(0.0);
316 case valueTypeCustom:
317 default:
318 return QVariant();
319 }
320}
321
323{
324 switch (type) {
325 case valueTypeUint8:
326 return QVariant(std::numeric_limits<unsigned char>::max());
327 case valueTypeInt8:
328 return QVariant(std::numeric_limits<signed char>::max());
329 case valueTypeUint16:
330 return QVariant(std::numeric_limits<unsigned short int>::max());
331 case valueTypeInt16:
332 return QVariant(std::numeric_limits<short int>::max());
333 case valueTypeUint32:
334 return QVariant(std::numeric_limits<uint32_t>::max());
335 case valueTypeInt32:
336 return QVariant(std::numeric_limits<int32_t>::max());
337 case valueTypeUint64:
338 return QVariant((qulonglong)std::numeric_limits<uint64_t>::max());
339 case valueTypeInt64:
340 return QVariant((qlonglong)std::numeric_limits<int64_t>::max());
341 case valueTypeFloat:
342 return QVariant(std::numeric_limits<float>::max());
344 case valueTypeDouble:
345 return QVariant(std::numeric_limits<double>::max());
346 case valueTypeString:
347 return QVariant();
348 case valueTypeBool:
349 return QVariant(1);
350 case valueTypeCustom:
351 default:
352 return QVariant();
353 }
354}
355
356bool FactMetaData::convertAndValidateRaw(const QVariant &rawValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
357{
358 bool convertOk = false;
359
360 errorString.clear();
361
362 switch (type()) {
366 typedValue = QVariant(rawValue.toInt(&convertOk));
367 if (!convertOnly && convertOk) {
368 if (!isInRawLimit<int32_t>(typedValue)) {
369 errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
370 }
371 }
372 break;
374 typedValue = QVariant(rawValue.toLongLong(&convertOk));
375 if (!convertOnly && convertOk) {
376 if (!isInRawLimit<int64_t>(typedValue)) {
377 errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt());
378 }
379 }
380 break;
384 typedValue = QVariant(rawValue.toUInt(&convertOk));
385 if (!convertOnly && convertOk) {
386 if (!isInRawLimit<uint32_t>(typedValue)) {
387 errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
388 }
389 }
390 break;
392 typedValue = QVariant(rawValue.toULongLong(&convertOk));
393 if (!convertOnly && convertOk) {
394 if (!isInRawLimit<uint64_t>(typedValue)) {
395 errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt());
396 }
397 }
398 break;
400 typedValue = QVariant(rawValue.toFloat(&convertOk));
401 if (!convertOnly && convertOk) {
402 if (!isInRawLimit<float>(typedValue)) {
403 errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
404 }
405 }
406 break;
409 typedValue = QVariant(rawValue.toDouble(&convertOk));
410 if (!convertOnly && convertOk) {
411 if (!isInRawLimit<double>(typedValue)) {
412 errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble());
413 }
414 }
415 break;
417 convertOk = true;
418 typedValue = QVariant(rawValue.toString());
419 break;
421 convertOk = true;
422 typedValue = QVariant(rawValue.toBool());
423 break;
425 convertOk = true;
426 typedValue = QVariant(rawValue.toByteArray());
427 break;
428 }
429
430 if (!convertOk) {
431 errorString += tr("Invalid number");
432 }
433
434 return (convertOk && errorString.isEmpty());
435}
436
437bool FactMetaData::convertAndValidateCooked(const QVariant &cookedValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
438{
439 bool convertOk = false;
440
441 errorString.clear();
442
443 if (!convertOnly && _customCookedValidator) {
444 errorString = _customCookedValidator(cookedValue);
445 if (!errorString.isEmpty()) {
446 return false;
447 }
448 }
449
450 switch (type()) {
454 typedValue = QVariant(cookedValue.toInt(&convertOk));
455 if (!convertOnly && convertOk) {
456 if (!isInCookedLimit<int32_t>(typedValue)) {
457 errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
458 }
459 }
460 break;
462 typedValue = QVariant(cookedValue.toLongLong(&convertOk));
463 if (!convertOnly && convertOk) {
464 if (!isInCookedLimit<int64_t>(typedValue)) {
465 errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
466 }
467 }
468 break;
472 typedValue = QVariant(cookedValue.toUInt(&convertOk));
473 if (!convertOnly && convertOk) {
474 if (!isInCookedLimit<uint32_t>(typedValue)) {
475 errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
476 }
477 }
478 break;
480 typedValue = QVariant(cookedValue.toULongLong(&convertOk));
481 if (!convertOnly && convertOk) {
482 if (!isInCookedLimit<uint64_t>(typedValue)) {
483 errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
484 }
485 }
486 break;
488 typedValue = QVariant(cookedValue.toFloat(&convertOk));
489 if (!convertOnly && convertOk) {
490 if (!isInCookedLimit<float>(typedValue)) {
491 errorString = tr("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
492 }
493 }
494 break;
497 typedValue = QVariant(cookedValue.toDouble(&convertOk));
498 if (!convertOnly && convertOk) {
499 if (!isInCookedLimit<double>(typedValue)) {
500 errorString = tr("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
501 }
502 }
503 break;
505 convertOk = true;
506 typedValue = QVariant(cookedValue.toString());
507 if (!convertOnly && (_maxStringLength > 0) && (typedValue.toString().length() > _maxStringLength)) {
508 errorString = tr("Value must be %1 characters or less").arg(_maxStringLength);
509 }
510 break;
512 convertOk = true;
513 typedValue = QVariant(cookedValue.toBool());
514 break;
516 convertOk = true;
517 typedValue = QVariant(cookedValue.toByteArray());
518 break;
519 }
520
521 if (!convertOk) {
522 errorString += tr("Invalid number");
523 }
524
525 return (convertOk && errorString.isEmpty());
526}
527
528bool FactMetaData::clampValue(const QVariant &cookedValue, QVariant &typedValue) const
529{
530 bool convertOk = false;
531
532 switch (type()) {
536 typedValue = QVariant(cookedValue.toInt(&convertOk));
537 if (convertOk) {
538 clamp<int32_t>(typedValue);
539 }
540 break;
542 typedValue = QVariant(cookedValue.toLongLong(&convertOk));
543 if (convertOk) {
544 clamp<int64_t>(typedValue);
545 }
546 break;
550 typedValue = QVariant(cookedValue.toUInt(&convertOk));
551 if (convertOk) {
552 clamp<uint32_t>(typedValue);
553 }
554 break;
556 typedValue = QVariant(cookedValue.toULongLong(&convertOk));
557 if (convertOk) {
558 clamp<uint64_t>(typedValue);
559 }
560 break;
562 typedValue = QVariant(cookedValue.toFloat(&convertOk));
563 if (convertOk) {
564 clamp<float>(typedValue);
565 }
566 break;
569 typedValue = QVariant(cookedValue.toDouble(&convertOk));
570 if (convertOk) {
571 clamp<double>(typedValue);
572 }
573 break;
575 convertOk = true;
576 typedValue = QVariant(cookedValue.toString());
577 break;
579 convertOk = true;
580 typedValue = QVariant(cookedValue.toBool());
581 break;
583 convertOk = true;
584 typedValue = QVariant(cookedValue.toByteArray());
585 break;
586 }
587
588 return convertOk;
589}
590
591void FactMetaData::setBitmaskInfo(const QStringList &strings, const QVariantList &values)
592{
593 if (strings.count() != values.count()) {
594 qCWarning(FactMetaDataLog) << "Count mismatch strings:values" << strings.count() << values.count();
595 return;
596 }
597
598 _bitmaskStrings = strings;
599 _bitmaskValues = values;
601}
602
603void FactMetaData::addBitmaskInfo(const QString &name, const QVariant &value)
604{
605 _bitmaskStrings << name;
606 _bitmaskValues << value;
607}
608
609void FactMetaData::setEnumInfo(const QStringList &strings, const QVariantList &values)
610{
611 if (strings.count() != values.count()) {
612 qCWarning(FactMetaDataLog) << "Count mismatch strings:values" << strings.count() << values.count();
613 return;
614 }
615
616 _enumStrings = strings;
617 _enumValues = values;
619}
620
621void FactMetaData::addEnumInfo(const QString &name, const QVariant &value)
622{
623 _enumStrings << name;
624 _enumValues << value;
625}
626
627void FactMetaData::removeEnumInfo(const QVariant &value)
628{
629 const int index = _enumValues.indexOf(value);
630 if (index < 0) {
631 qCWarning(FactMetaDataLog) << "Value does not exist in fact:" << value;
632 return;
633 }
634
635 _enumValues.removeAt(index);
636 _enumStrings.removeAt(index);
637}
638
639void FactMetaData::setTranslators(Translator rawTranslator_, Translator cookedTranslator_)
640{
641 _rawTranslator = rawTranslator_;
642 _cookedTranslator = cookedTranslator_;
643}
644
646{
647 if (_enumStrings.count() || _bitmaskStrings.count()) {
648 // No translation if enum
649 setTranslators(_defaultTranslator, _defaultTranslator);
650 _cookedUnits = _rawUnits;
651 return;
652 } else {
653 for (size_t i = 0; i < std::size(_rgBuiltInTranslations); i++) {
654 const BuiltInTranslation_s *pBuiltInTranslation = &_rgBuiltInTranslations[i];
655
656 if (pBuiltInTranslation->rawUnits.toLower() == _rawUnits.toLower()) {
657 _cookedUnits = pBuiltInTranslation->cookedUnits;
658 setTranslators(pBuiltInTranslation->rawTranslator, pBuiltInTranslation->cookedTranslator);
659 return;
660 }
661 }
662 }
663
664 // Translator not yet set, try app settings translators
665 _setAppSettingsTranslators();
666}
667
668QVariant FactMetaData::_degreesToRadians(const QVariant &degrees)
669{
670 return QVariant(qDegreesToRadians(degrees.toDouble()));
671}
672
673QVariant FactMetaData::_radiansToDegrees(const QVariant &radians)
674{
675 return QVariant(qRadiansToDegrees(radians.toDouble()));
676}
677
678QVariant FactMetaData::_centiDegreesToDegrees(const QVariant &centiDegrees)
679{
680 return QVariant(centiDegrees.toReal() / 100.0);
681}
682
683QVariant FactMetaData::_degreesToCentiDegrees(const QVariant &degrees)
684{
685 return QVariant(qRound(degrees.toReal() * 100.0));
686}
687
688QVariant FactMetaData::_centiCelsiusToCelsius(const QVariant &centiCelsius)
689{
690 return QVariant(centiCelsius.toReal() / 100.0);
691}
692
693QVariant FactMetaData::_celsiusToCentiCelsius(const QVariant &celsius)
694{
695 return QVariant(qRound(celsius.toReal() * 100.0));
696}
697
698QVariant FactMetaData::_userGimbalDegreesToMavlinkGimbalDegrees(const QVariant &userGimbalDegrees)
699{
700 // User facing gimbal degree values are from 0 (level) to 90 (straight down)
701 // Mavlink gimbal degree values are from 0 (level) to -90 (straight down)
702 return (userGimbalDegrees.toDouble() * -1.0);
703}
704
705QVariant FactMetaData::_mavlinkGimbalDegreesToUserGimbalDegrees(const QVariant& mavlinkGimbalDegrees)
706{
707 // User facing gimbal degree values are from 0 (level) to 90 (straight down)
708 // Mavlink gimbal degree values are from 0 (level) to -90 (straight down)
709 return (mavlinkGimbalDegrees.toDouble() * -1.0);
710}
711
712QVariant FactMetaData::_metersToFeet(const QVariant &meters)
713{
714 return QVariant((meters.toDouble() * 1.0) / constants.feetToMeters);
715}
716
717QVariant FactMetaData::_feetToMeters(const QVariant &feet)
718{
719 return QVariant(feet.toDouble() * constants.feetToMeters);
720}
721
722QVariant FactMetaData::_squareMetersToSquareKilometers(const QVariant &squareMeters)
723{
724 return QVariant(squareMeters.toDouble() * 0.000001);
725}
726
727QVariant FactMetaData::_squareKilometersToSquareMeters(const QVariant &squareKilometers)
728{
729 return QVariant(squareKilometers.toDouble() * 1000000.0);
730}
731
732QVariant FactMetaData::_squareMetersToHectares(const QVariant &squareMeters)
733{
734 return QVariant(squareMeters.toDouble() * 0.0001);
735}
736
737QVariant FactMetaData::_hectaresToSquareMeters(const QVariant &hectares)
738{
739 return QVariant(hectares.toDouble() * 1000.0);
740}
741
742QVariant FactMetaData::_squareMetersToSquareFeet(const QVariant &squareMeters)
743{
744 return QVariant(squareMeters.toDouble() * constants.squareMetersToSquareFeet);
745}
746
747QVariant FactMetaData::_squareFeetToSquareMeters(const QVariant &squareFeet)
748{
749 return QVariant(squareFeet.toDouble() * constants.feetToSquareMeters);
750}
751
752QVariant FactMetaData::_squareMetersToAcres(const QVariant &squareMeters)
753{
754 return QVariant(squareMeters.toDouble() * constants.squareMetersToAcres);
755}
756
757QVariant FactMetaData::_acresToSquareMeters(const QVariant &acres)
758{
759 return QVariant(acres.toDouble() * constants.acresToSquareMeters);
760}
761
762QVariant FactMetaData::_squareMetersToSquareMiles(const QVariant &squareMeters)
763{
764 return QVariant(squareMeters.toDouble() * constants.squareMetersToSquareMiles);
765}
766
767QVariant FactMetaData::_squareMilesToSquareMeters(const QVariant &squareMiles)
768{
769 return QVariant(squareMiles.toDouble() * constants.squareMilesToSquareMeters);
770}
771
772QVariant FactMetaData::_metersPerSecondToMilesPerHour(const QVariant &metersPerSecond)
773{
774 return QVariant(((metersPerSecond.toDouble() * 1.0) / constants.milesToMeters) * constants.secondsPerHour);
775}
776
777QVariant FactMetaData::_milesPerHourToMetersPerSecond(const QVariant &milesPerHour)
778{
779 return QVariant((milesPerHour.toDouble() * constants.milesToMeters) / constants.secondsPerHour);
780}
781
782QVariant FactMetaData::_metersPerSecondToKilometersPerHour(const QVariant &metersPerSecond)
783{
784 return QVariant((metersPerSecond.toDouble() / 1000.0) * constants.secondsPerHour);
785}
786
787QVariant FactMetaData::_kilometersPerHourToMetersPerSecond(const QVariant &kilometersPerHour)
788{
789 return QVariant((kilometersPerHour.toDouble() * 1000.0) / constants.secondsPerHour);
790}
791
792QVariant FactMetaData::_metersPerSecondToKnots(const QVariant &metersPerSecond)
793{
794 return QVariant((metersPerSecond.toDouble() * constants.secondsPerHour) / (1000.0 * constants.knotsToKPH));
795}
796
797QVariant FactMetaData::_knotsToMetersPerSecond(const QVariant& knots)
798{
799 return QVariant(knots.toDouble() * (1000.0 * constants.knotsToKPH / constants.secondsPerHour));
800}
801
802QVariant FactMetaData::_percentToNorm(const QVariant &percent)
803{
804 return QVariant(percent.toDouble() / 100.0);
805}
806
807QVariant FactMetaData::_normToPercent(const QVariant &normalized)
808{
809 return QVariant(normalized.toDouble() * 100.0);
810}
811
812QVariant FactMetaData::_centimetersToInches(const QVariant &centimeters)
813{
814 return QVariant((centimeters.toDouble() * 1.0) / constants.inchesToCentimeters);
815}
816
817QVariant FactMetaData::_inchesToCentimeters(const QVariant &inches)
818{
819 return QVariant(inches.toDouble() * constants.inchesToCentimeters);
820}
821
822QVariant FactMetaData::_celsiusToFarenheit(const QVariant &celsius)
823{
824 return QVariant((celsius.toDouble() * (9.0 / 5.0)) + 32);
825}
826
827QVariant FactMetaData::_farenheitToCelsius(const QVariant &farenheit)
828{
829 return QVariant((farenheit.toDouble() - 32) * (5.0 / 9.0));
830}
831
832QVariant FactMetaData::_kilogramsToGrams(const QVariant &kg)
833{
834 return QVariant(kg.toDouble() * 1000);
835}
836
837QVariant FactMetaData::_ouncesToGrams(const QVariant &oz)
838{
839 return QVariant(oz.toDouble() * constants.ouncesToGrams);
840}
841
842QVariant FactMetaData::_poundsToGrams(const QVariant &lbs)
843{
844 return QVariant(lbs.toDouble() * constants.poundsToGrams);
845}
846
847QVariant FactMetaData::_gramsToKilograms(const QVariant &g)
848{
849 return QVariant(g.toDouble() / 1000);
850}
851
852QVariant FactMetaData::_gramsToOunces(const QVariant &g)
853{
854 return QVariant(g.toDouble() / constants.ouncesToGrams);
855}
856
857QVariant FactMetaData::_gramsToPunds(const QVariant &g)
858{
859 return QVariant(g.toDouble() / constants.poundsToGrams);
860}
861
862void FactMetaData::setRawUnits(const QString &rawUnits)
863{
864 _rawUnits = rawUnits;
865 _cookedUnits = rawUnits;
866
868}
869
870FactMetaData::ValueType_t FactMetaData::stringToType(const QString &typeString, bool &unknownType)
871{
872 unknownType = false;
873
874 for (size_t i = 0; i < std::size(_rgKnownTypeStrings); i++) {
875 if (typeString.compare(_rgKnownTypeStrings[i], Qt::CaseInsensitive) == 0) {
876 return _rgKnownValueTypes[i];
877 }
878 }
879
880 unknownType = true;
881
882 return valueTypeDouble;
883}
884
886{
887 for (size_t i = 0; i < std::size(_rgKnownTypeStrings); i++) {
888 if (type == _rgKnownValueTypes[i]) {
889 return _rgKnownTypeStrings[i];
890 }
891 }
892
893 return QStringLiteral("UnknownType%1").arg(type);
894}
895
897{
898 switch (type) {
899 case valueTypeUint8:
900 case valueTypeInt8:
901 return 1;
902 case valueTypeUint16:
903 case valueTypeInt16:
904 return 2;
905 case valueTypeUint32:
906 case valueTypeInt32:
907 case valueTypeFloat:
908 return 4;
909 case valueTypeUint64:
910 case valueTypeInt64:
911 case valueTypeDouble:
912 return 8;
913 case valueTypeCustom:
915 default:
916 qCWarning(FactMetaDataLog) << "Unsupported fact value type" << type;
917 return 0;
918 }
919}
920
921void FactMetaData::_setAppSettingsTranslators()
922{
923 // We can only translate between real numbers
924 if (_enumStrings.isEmpty() && ((type() == valueTypeDouble) || (type() == valueTypeFloat))) {
925 for (size_t i = 0; i < std::size(_rgAppSettingsTranslations); i++) {
926 const AppSettingsTranslation_s *pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
927
928 if (_rawUnits.toLower() != pAppSettingsTranslation->rawUnits.toLower()) {
929 continue;
930 }
931
933 uint settingsUnits = 0;
934
935 switch (pAppSettingsTranslation->unitType) {
936 case UnitHorizontalDistance:
937 settingsUnits = settings->horizontalDistanceUnits()->rawValue().toUInt();
938 break;
939 case UnitVerticalDistance:
940 settingsUnits = settings->verticalDistanceUnits()->rawValue().toUInt();
941 break;
942 case UnitSpeed:
943 settingsUnits = settings->speedUnits()->rawValue().toUInt();
944 break;
945 case UnitArea:
946 settingsUnits = settings->areaUnits()->rawValue().toUInt();
947 break;
948 case UnitTemperature:
949 settingsUnits = settings->temperatureUnits()->rawValue().toUInt();
950 break;
951 case UnitWeight:
952 settingsUnits = settings->weightUnits()->rawValue().toUInt();
953 break;
954 default:
955 break;
956 }
957
958 if (settingsUnits == pAppSettingsTranslation->unitOption) {
959 _cookedUnits = pAppSettingsTranslation->cookedUnits;
960 setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator);
961 return;
962 }
963 }
964 }
965
966 // "vertical m" is an artificial lookup unit - never show it to the user. Checking _cookedUnits
967 // rather than _rawUnits ensures a successful translation above is never overwritten.
968 if (_cookedUnits.compare(QStringLiteral("vertical m"), Qt::CaseInsensitive) == 0) {
969 _cookedUnits = QStringLiteral("m");
970 }
971}
972
973const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsUnitsTranslation(const QString &rawUnits, UnitTypes type)
974{
975 for (size_t i = 0; i < std::size(_rgAppSettingsTranslations); i++) {
976 const AppSettingsTranslation_s *const pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
977
978 if (rawUnits.toLower() != pAppSettingsTranslation->rawUnits.toLower()) {
979 continue;
980 }
981
982 uint unitOption = 0;
984 switch (type) {
985 case UnitHorizontalDistance:
986 unitOption = unitsSettings->horizontalDistanceUnits()->rawValue().toUInt();
987 break;
988 case UnitVerticalDistance:
989 unitOption = unitsSettings->verticalDistanceUnits()->rawValue().toUInt();
990 break;
991 case UnitArea:
992 unitOption = unitsSettings->areaUnits()->rawValue().toUInt();
993 break;
994 case UnitSpeed:
995 unitOption = unitsSettings->speedUnits()->rawValue().toUInt();
996 break;
997 case UnitTemperature:
998 unitOption = unitsSettings->temperatureUnits()->rawValue().toUInt();
999 break;
1000 case UnitWeight:
1001 unitOption = unitsSettings->weightUnits()->rawValue().toUInt();
1002 break;
1003 }
1004
1005 if ((pAppSettingsTranslation->unitType == type) && (pAppSettingsTranslation->unitOption == unitOption)) {
1006 return pAppSettingsTranslation;
1007 }
1008 }
1009
1010 return nullptr;
1011}
1012
1014{
1015 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m", UnitHorizontalDistance);
1016 if (pAppSettingsTranslation) {
1017 return pAppSettingsTranslation->rawTranslator(meters);
1018 } else {
1019 return meters;
1020 }
1021}
1022
1024{
1025 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("vertical m", UnitVerticalDistance);
1026 if (pAppSettingsTranslation) {
1027 return pAppSettingsTranslation->rawTranslator(meters);
1028 } else {
1029 return meters;
1030 }
1031}
1032
1034{
1035 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m", UnitHorizontalDistance);
1036 if (pAppSettingsTranslation) {
1037 return pAppSettingsTranslation->cookedTranslator(distance);
1038 } else {
1039 return distance;
1040 }
1041}
1042
1044{
1045 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("vertical m", UnitVerticalDistance);
1046 if (pAppSettingsTranslation) {
1047 return pAppSettingsTranslation->cookedTranslator(distance);
1048 } else {
1049 return distance;
1050 }
1051}
1052
1054{
1055 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m", UnitHorizontalDistance);
1056 if (pAppSettingsTranslation) {
1057 return pAppSettingsTranslation->cookedUnits;
1058 } else {
1059 return QStringLiteral("m");
1060 }
1061}
1062
1064{
1065 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("vertical m", UnitVerticalDistance);
1066 if (pAppSettingsTranslation) {
1067 return pAppSettingsTranslation->cookedUnits;
1068 } else {
1069 return QStringLiteral("m");
1070 }
1071}
1072
1074{
1075 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("g", UnitWeight);
1076 if (pAppSettingsTranslation) {
1077 return pAppSettingsTranslation->cookedUnits;
1078 } else {
1079 return QStringLiteral("g");
1080 }
1081}
1082
1083QVariant FactMetaData::squareMetersToAppSettingsAreaUnits(const QVariant &squareMeters)
1084{
1085 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m^2", UnitArea);
1086 if (pAppSettingsTranslation) {
1087 return pAppSettingsTranslation->rawTranslator(squareMeters);
1088 } else {
1089 return squareMeters;
1090 }
1091}
1092
1094{
1095 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m^2", UnitArea);
1096 if (pAppSettingsTranslation) {
1097 return pAppSettingsTranslation->cookedTranslator(area);
1098 } else {
1099 return area;
1100 }
1101}
1102
1104{
1105 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m^2", UnitArea);
1106 if (pAppSettingsTranslation) {
1107 return pAppSettingsTranslation->cookedUnits;
1108 } else {
1109 return QStringLiteral("m^2");
1110 }
1111}
1112
1113QVariant FactMetaData::gramsToAppSettingsWeightUnits(const QVariant &grams) {
1114 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("g", UnitWeight);
1115 if (pAppSettingsTranslation) {
1116 return pAppSettingsTranslation->rawTranslator(grams);
1117 } else {
1118 return grams;
1119 }
1120}
1121
1122QVariant FactMetaData::appSettingsWeightUnitsToGrams(const QVariant &weight) {
1123 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("g", UnitWeight);
1124 if (pAppSettingsTranslation) {
1125 return pAppSettingsTranslation->cookedTranslator(weight);
1126 } else {
1127 return weight;
1128 }
1129}
1130
1131QVariant FactMetaData::metersSecondToAppSettingsSpeedUnits(const QVariant &metersSecond)
1132{
1133 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m/s", UnitSpeed);
1134 if (pAppSettingsTranslation) {
1135 return pAppSettingsTranslation->rawTranslator(metersSecond);
1136 } else {
1137 return metersSecond;
1138 }
1139}
1140
1142{
1143 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m/s", UnitSpeed);
1144 if (pAppSettingsTranslation) {
1145 return pAppSettingsTranslation->cookedTranslator(speed);
1146 } else {
1147 return speed;
1148 }
1149}
1150
1152{
1153 const AppSettingsTranslation_s *const pAppSettingsTranslation = _findAppSettingsUnitsTranslation("m/s", UnitSpeed);
1154 if (pAppSettingsTranslation) {
1155 return pAppSettingsTranslation->cookedUnits;
1156 } else {
1157 return QStringLiteral("m/s");
1158 }
1159}
1160
1162{
1163 return _rawTranslator(this->rawIncrement()).toDouble();
1164}
1165
1167{
1168 int actualDecimalPlaces = kDefaultDecimalPlaces;
1169 int incrementDecimalPlaces = kUnknownDecimalPlaces;
1170
1171 // First determine decimal places from increment
1172 double increment = _rawTranslator(this->rawIncrement()).toDouble();
1173 if (!qIsNaN(increment)) {
1174 double integralPart;
1175
1176 // Get the fractional part only
1177 increment = fabs(modf(increment, &integralPart));
1178 if (increment == 0.0) {
1179 // No fractional part, so no decimal places
1180 incrementDecimalPlaces = 0;
1181 } else {
1182 incrementDecimalPlaces = -ceil(log10(increment));
1183 }
1184 }
1185
1186 if (_decimalPlaces == kUnknownDecimalPlaces) {
1187 if (incrementDecimalPlaces != kUnknownDecimalPlaces) {
1188 actualDecimalPlaces = incrementDecimalPlaces;
1189 } else {
1190 // Adjust decimal places for cooked translation
1191 int settingsDecimalPlaces = (_decimalPlaces == kUnknownDecimalPlaces) ? kDefaultDecimalPlaces : _decimalPlaces;
1192 const double ctest = _rawTranslator(1.0).toDouble();
1193
1194 settingsDecimalPlaces += -log10(ctest);
1195
1196 settingsDecimalPlaces = qMin(25, settingsDecimalPlaces);
1197 settingsDecimalPlaces = qMax(0, settingsDecimalPlaces);
1198
1199 actualDecimalPlaces = settingsDecimalPlaces;
1200 }
1201 } else {
1202 actualDecimalPlaces = _decimalPlaces;
1203 }
1204
1205 return actualDecimalPlaces;
1206}
1207
1208FactMetaData *FactMetaData::createFromJsonObject(const QJsonObject &json, const QMap<QString, QString> &defineMap, QObject *metaDataParent)
1209{
1210 QString errorString;
1211
1212 static const QList<JsonParsing::KeyValidateInfo> keyInfoList = {
1213 { _nameJsonKey, QJsonValue::String, true },
1214 { _labelJsonKey, QJsonValue::String, false },
1215 { _typeJsonKey, QJsonValue::String, true },
1216 { _commentJsonKey, QJsonValue::String, false }, // Developer notes only, never parsed
1217 { _keywordsJsonKey, QJsonValue::String, false }, // Consumed by the settings page generator, not C++
1218 { _shortDescriptionJsonKey, QJsonValue::String, false },
1219 { _longDescriptionJsonKey, QJsonValue::String, false },
1220 { _unitsJsonKey, QJsonValue::String, false },
1221 { _decimalPlacesJsonKey, QJsonValue::Double, false },
1222 { _maxStringLengthJsonKey, QJsonValue::Double, false },
1223 { _defaultValueJsonKey, QJsonValue::Undefined, false }, // Undefined: type follows the fact type
1224 { _mobileDefaultValueJsonKey, QJsonValue::Undefined, false }, // Undefined: type follows the fact type
1225 { _incrementJsonKey, QJsonValue::Double, false },
1226 { _minJsonKey, QJsonValue::Double, false },
1227 { _maxJsonKey, QJsonValue::Double, false },
1228 { _userMinJsonKey, QJsonValue::Double, false },
1229 { _userMaxJsonKey, QJsonValue::Double, false },
1230 { _hasControlJsonKey, QJsonValue::Bool, false },
1231 { _qgcRebootRequiredJsonKey, QJsonValue::Bool, false },
1232 { _rebootRequiredJsonKey, QJsonValue::Bool, false },
1233 { _categoryJsonKey, QJsonValue::String, false },
1234 { _groupJsonKey, QJsonValue::String, false },
1235 { _volatileJsonKey, QJsonValue::Bool, false },
1236 { _readOnlyJsonKey, QJsonValue::Bool, false },
1237 { _enumBitmaskArrayJsonKey, QJsonValue::Array, false },
1238 { _enumValuesArrayJsonKey, QJsonValue::Array, false },
1239 { _enumValuesJsonKey, QJsonValue::String, false },
1240 { _enumStringsJsonKey, QJsonValue::String, false },
1241 };
1242
1243 if (!JsonParsing::validateKeysStrict(json, keyInfoList, errorString)) {
1244 qCWarning(FactMetaDataLog) << errorString;
1245 return new FactMetaData(valueTypeUint32, metaDataParent);
1246 }
1247
1248 bool unknownType;
1249 const FactMetaData::ValueType_t type = FactMetaData::stringToType(json[_typeJsonKey].toString(), unknownType);
1250 if (unknownType) {
1251 qCWarning(FactMetaDataLog) << "Unknown type" << json[_typeJsonKey].toString();
1252 return new FactMetaData(valueTypeUint32, metaDataParent);
1253 }
1254
1255 FactMetaData *const metaData = new FactMetaData(type, metaDataParent);
1256
1257 metaData->_name = json[_nameJsonKey].toString();
1258
1259 QStringList rgDescriptions;
1260 QList<double> rgDoubleValues;
1261 QList<int> rgIntValues;
1262 QStringList rgStringValues;
1263
1264 bool foundBitmask = false;
1265 if (!_parseValuesArray(json, rgDescriptions, rgDoubleValues, errorString)) {
1266 qCWarning(FactMetaDataLog) << QStringLiteral("FactMetaData::createFromJsonObject _parseValueDescriptionArray for '%1' failed. %2").arg(metaData->name(), errorString);
1267 }
1268 if (rgDescriptions.isEmpty()) {
1269 if (!_parseBitmaskArray(json, rgDescriptions, rgIntValues, errorString)) {
1270 qCWarning(FactMetaDataLog) << QStringLiteral("FactMetaData::createFromJsonObject _parseBitmaskArray for '%1' failed. %2").arg(metaData->name(), errorString);
1271 }
1272 foundBitmask = rgDescriptions.count() != 0;
1273 }
1274 if (rgDescriptions.isEmpty()) {
1275 if (!_parseEnum(metaData->_name, json, defineMap, rgDescriptions, rgStringValues, errorString)) {
1276 qCWarning(FactMetaDataLog) << QStringLiteral("FactMetaData::createFromJsonObject _parseEnum for '%1' failed. %2").arg(metaData->name(), errorString);
1277 }
1278 }
1279
1280 if (errorString.isEmpty() && !rgDescriptions.isEmpty()) {
1281 for (qsizetype i = 0; i < rgDescriptions.count(); i++) {
1282 if (foundBitmask) {
1283 metaData->addBitmaskInfo(rgDescriptions[i], 1u << rgIntValues[i]);
1284 } else {
1285 const QVariant rawValueVariant = !rgDoubleValues.isEmpty() ? QVariant(rgDoubleValues[i]) : QVariant(rgStringValues[i]);
1286 QVariant convertedValueVariant;
1287 QString enumErrorString;
1288 if (metaData->convertAndValidateRaw(rawValueVariant, false /* validate */, convertedValueVariant, enumErrorString)) {
1289 metaData->addEnumInfo(rgDescriptions[i], convertedValueVariant);
1290 } else {
1291 qCWarning(FactMetaDataLog) << QStringLiteral("FactMetaData::createFromJsonObject convertAndValidateRaw on enum value for %1 failed.").arg(metaData->name())
1292 << "type:" << metaData->type()
1293 << "value:" << rawValueVariant
1294 << "error:" << enumErrorString;
1295 }
1296 }
1297 }
1298 }
1299
1300 metaData->setDecimalPlaces(json[_decimalPlacesJsonKey].toInt(kUnknownDecimalPlaces));
1301 metaData->setMaxStringLength(json[_maxStringLengthJsonKey].toInt(0));
1302
1303 metaData->setShortDescription(json[_shortDescriptionJsonKey].toString());
1304 metaData->setLabel(json[_labelJsonKey].toString());
1305 metaData->setLongDescription(json[_longDescriptionJsonKey].toString());
1306
1307 if (json.contains(_unitsJsonKey)) {
1308 metaData->setRawUnits(json[_unitsJsonKey].toString());
1309 }
1310
1311 QString defaultValueJsonKey = _defaultValueJsonKey;
1312#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
1313 if (json.contains(_mobileDefaultValueJsonKey)) {
1314 defaultValueJsonKey = _mobileDefaultValueJsonKey;
1315 }
1316#endif
1317
1318 if (json.contains(defaultValueJsonKey)) {
1319 const QJsonValue jsonValue = json[defaultValueJsonKey];
1320
1321 if ((jsonValue.type() == QJsonValue::Null) && (type == valueTypeFloat || type == valueTypeDouble)) {
1322 metaData->setRawDefaultValue((type == valueTypeFloat) ? std::numeric_limits<float>::quiet_NaN() : std::numeric_limits<double>::quiet_NaN());
1323 } else {
1324 QVariant typedValue;
1325 QString defaultValueErrorString;
1326 const QVariant initialValue = jsonValue.toVariant();
1327
1328 if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, defaultValueErrorString)) {
1329 metaData->setRawDefaultValue(typedValue);
1330 } else {
1331 qCWarning(FactMetaDataLog) << "Invalid default value,"
1332 << "name:" << metaData->name()
1333 << "type:" << metaData->type()
1334 << "value:" << initialValue
1335 << "error:" << defaultValueErrorString;
1336 }
1337 }
1338 }
1339
1340 if (json.contains(_incrementJsonKey)) {
1341 QVariant typedValue;
1342 QString incrementErrorString;
1343 const QVariant initialValue = json[_incrementJsonKey].toVariant();
1344 if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, incrementErrorString)) {
1345 metaData->setRawIncrement(typedValue.toDouble());
1346 } else {
1347 qCWarning(FactMetaDataLog) << "Invalid increment value,"
1348 << "name:" << metaData->name()
1349 << "type:" << metaData->type()
1350 << "value:" << initialValue
1351 << "error:" << incrementErrorString;
1352 }
1353 }
1354
1355 if (json.contains(_minJsonKey)) {
1356 QVariant typedValue;
1357 QString minErrorString;
1358 const QVariant initialValue = json[_minJsonKey].toVariant();
1359 if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, minErrorString)) {
1360 metaData->setRawMin(typedValue);
1361 } else {
1362 qCWarning(FactMetaDataLog) << "Invalid min value,"
1363 << "name:" << metaData->name()
1364 << "type:" << metaData->type()
1365 << "value:" << initialValue
1366 << "error:" << minErrorString;
1367 }
1368 }
1369
1370 if (json.contains(_maxJsonKey)) {
1371 QVariant typedValue;
1372 QString maxErrorString;
1373 const QVariant initialValue = json[_maxJsonKey].toVariant();
1374 if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, maxErrorString)) {
1375 metaData->setRawMax(typedValue);
1376 } else {
1377 qCWarning(FactMetaDataLog) << "Invalid max value,"
1378 << "name:" << metaData->name()
1379 << "type:" << metaData->type()
1380 << "value:" << initialValue
1381 << "error:" << maxErrorString;
1382 }
1383 }
1384
1385 // If userMin/Max not specified, we leave at invalid so the ui can decide what to do
1386
1387 if (json.contains(_userMinJsonKey)) {
1388 QVariant typedValue;
1389 QString userMinErrorString;
1390 const QVariant initialValue = json[_userMinJsonKey].toVariant();
1391 if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, userMinErrorString)) {
1392 metaData->setRawUserMin(typedValue);
1393 } else {
1394 qCWarning(FactMetaDataLog) << "Invalid userMin value,"
1395 << "name:" << metaData->name()
1396 << "type:" << metaData->type()
1397 << "value:" << initialValue
1398 << "error:" << userMinErrorString;
1399 }
1400 }
1401
1402 if (json.contains(_userMaxJsonKey)) {
1403 QVariant typedValue;
1404 QString userMaxErrorString;
1405 const QVariant initialValue = json[_userMaxJsonKey].toVariant();
1406 if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, userMaxErrorString)) {
1407 metaData->setRawUserMax(typedValue);
1408 } else {
1409 qCWarning(FactMetaDataLog) << "Invalid userMax value,"
1410 << "name:" << metaData->name()
1411 << "type:" << metaData->type()
1412 << "value:" << initialValue
1413 << "error:" << userMaxErrorString;
1414 }
1415 }
1416
1417 bool hasControlJsonKey = true;
1418 if (json.contains(_hasControlJsonKey)) {
1419 hasControlJsonKey = json[_hasControlJsonKey].toBool();
1420 }
1421 metaData->setHasControl(hasControlJsonKey);
1422
1423 bool qgcRebootRequired = false;
1424 if (json.contains(_qgcRebootRequiredJsonKey)) {
1425 qgcRebootRequired = json[_qgcRebootRequiredJsonKey].toBool();
1426 }
1428
1429 bool rebootRequired = false;
1430 if (json.contains(_rebootRequiredJsonKey)) {
1431 rebootRequired = json[_rebootRequiredJsonKey].toBool();
1432 }
1433 metaData->setVehicleRebootRequired(rebootRequired);
1434
1435 bool readOnly = false;
1436 if (json.contains(_readOnlyJsonKey)) {
1437 readOnly = json[_readOnlyJsonKey].toBool();
1438 }
1439 metaData->setReadOnly(readOnly);
1440
1441 bool volatileValue = false;
1442 if (json.contains(_volatileJsonKey)) {
1443 volatileValue = json[_volatileJsonKey].toBool();
1444 }
1446 if (json.contains(_groupJsonKey)) {
1447 metaData->setGroup(json[_groupJsonKey].toString());
1448 }
1449
1450 if (json.contains(_categoryJsonKey)) {
1451 metaData->setCategory(json[_categoryJsonKey].toString());
1452 }
1453
1454 return metaData;
1455}
1456
1457void FactMetaData::_loadJsonDefines(const QJsonObject &jsonDefinesObject, QMap<QString, QString> &defineMap)
1458{
1459 for (const QString &defineName: jsonDefinesObject.keys()) {
1460 const QString mapKey = _jsonMetaDataDefinesName + QStringLiteral(".") + defineName;
1461 defineMap[mapKey] = jsonDefinesObject[defineName].toString();
1462 }
1463}
1464
1465QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonFile(const QString &jsonFilename, QObject *metaDataParent)
1466{
1467 QMap<QString, FactMetaData*> metaDataMap;
1468
1469 QString errorString;
1470 int version;
1471 const QJsonObject jsonObject = JsonParsing::openInternalQGCJsonFile(
1472 jsonFilename, qgcFileType, 1, 1, version, errorString,
1473 QStringList{"label", "shortDesc", "longDesc", "enumStrings"},
1474 QStringList{"name"});
1475 if (!errorString.isEmpty()) {
1476 qCWarning(FactMetaDataLog) << "Internal Error:" << errorString;
1477 return metaDataMap;
1478 }
1479
1480 static const QList<JsonParsing::KeyValidateInfo> keyInfoList = {
1481 { FactMetaData::_jsonMetaDataDefinesName, QJsonValue::Object, false },
1482 { FactMetaData::_jsonMetaDataFactsName, QJsonValue::Array, true },
1483 };
1484 if (!JsonParsing::validateKeys(jsonObject, keyInfoList, errorString)) {
1485 qCWarning(FactMetaDataLog) << "Json document incorrect format:" << errorString;
1486 return metaDataMap;
1487 }
1488
1489 QMap<QString /* define name */, QString /* define value */> defineMap;
1490 _loadJsonDefines(jsonObject[FactMetaData::_jsonMetaDataDefinesName].toObject(), defineMap);
1491 const QJsonArray factArray = jsonObject[FactMetaData::_jsonMetaDataFactsName].toArray();
1492
1493 return createMapFromJsonArray(factArray, defineMap, metaDataParent);
1494}
1495
1496QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray &jsonArray, const QMap<QString, QString> &defineMap, QObject *metaDataParent)
1497{
1498 QMap<QString, FactMetaData*> metaDataMap;
1499 for (const QJsonValue &jsonValue : jsonArray) {
1500 if (!jsonValue.isObject()) {
1501 qCWarning(FactMetaDataLog) << "JsonValue is not an object";
1502 continue;
1503 }
1504
1505 const QJsonObject jsonObject = jsonValue.toObject();
1506 FactMetaData *const metaData = createFromJsonObject(jsonObject, defineMap, metaDataParent);
1507 if (metaDataMap.contains(metaData->name())) {
1508 qCWarning(FactMetaDataLog) << "Duplicate fact name:" << metaData->name();
1509 delete metaData;
1510 } else {
1511 metaDataMap[metaData->name()] = metaData;
1512 }
1513 }
1514
1515 return metaDataMap;
1516}
1517
1519{
1520 // We have to be careful with cooked min/max. Running the raw values through the translator could flip min and max.
1521 return qMax(_rawTranslator(_rawMax).toDouble(), _rawTranslator(_rawMin).toDouble());
1522}
1523
1525{
1526 // We have to be careful with cooked min/max. Running the raw values through the translator could flip min and max.
1527 return qMin(_rawTranslator(_rawMax).toDouble(), _rawTranslator(_rawMin).toDouble());
1528}
1529
1531{
1532 _volatile = bValue;
1533}
1534
1535QStringList FactMetaData::splitTranslatedList(const QString &translatedList)
1536{
1537 const QRegularExpression splitRegex("[,,、]"); // Note chinese commas for translations which have modified the english comma
1538 QStringList valueList = translatedList.split(splitRegex, Qt::SkipEmptyParts);
1539 for (QString &value: valueList) {
1540 value = value.trimmed();
1541 }
1542 return valueList;
1543}
1544
1545bool FactMetaData::_parseEnum(const QString& name, const QJsonObject &jsonObject, const DefineMap_t &defineMap, QStringList &rgDescriptions, QStringList &rgValues, QString &errorString)
1546{
1547 rgDescriptions.clear();
1548 rgValues.clear();
1549 errorString.clear();
1550
1551 if (!jsonObject.contains(_enumStringsJsonKey)) {
1552 return true;
1553 }
1554
1555 const QString jsonStrings = jsonObject.value(_enumStringsJsonKey).toString();
1556 const QString defineMapStrings = defineMap.value(jsonStrings, jsonStrings);
1557 rgDescriptions = splitTranslatedList(defineMapStrings);
1558
1559 const QString jsonValues = jsonObject.value(_enumValuesJsonKey).toString();
1560 const QString defineMapValues = defineMap.value(jsonValues, jsonValues);
1561 rgValues = splitTranslatedList(defineMapValues); // Never translated but still useful to use common string splitting code
1562
1563 if (rgDescriptions.count() != rgValues.count()) {
1564 errorString = QStringLiteral("Enum strings/values count mismatch - name: '%1' strings: '%2'[%3] values: '%4'[%5]").arg(name).arg(defineMapStrings).arg(rgDescriptions.count()).arg(defineMapValues).arg(rgValues.count());
1565 return false;
1566 }
1567
1568 return true;
1569}
1570
1571bool FactMetaData::_parseValuesArray(const QJsonObject &jsonObject, QStringList &rgDescriptions, QList<double> &rgValues, QString &errorString)
1572{
1573 rgDescriptions.clear();
1574 rgValues.clear();
1575 errorString.clear();
1576
1577 if (!jsonObject.contains(_enumValuesArrayJsonKey)) {
1578 return true;
1579 }
1580
1581 static const QList<JsonParsing::KeyValidateInfo> keyInfoList = {
1582 { _enumValuesArrayDescriptionJsonKey, QJsonValue::String, true },
1583 { _enumValuesArrayValueJsonKey, QJsonValue::Double, true },
1584 };
1585
1586 const QJsonArray &rgValueDescription = jsonObject[_enumValuesArrayJsonKey].toArray();
1587 for (const QJsonValue& jsonValue : rgValueDescription) {
1588 if (jsonValue.type() != QJsonValue::Object) {
1589 errorString = QStringLiteral("Value in \"values\" array is not an object.");
1590 return false;
1591 }
1592
1593 const QJsonObject &valueDescriptionObject = jsonValue.toObject();
1594 if (!JsonParsing::validateKeys(valueDescriptionObject, keyInfoList, errorString)) {
1595 errorString = QStringLiteral("Object in \"values\" array failed validation '%2'.").arg(errorString);
1596 return false;
1597 }
1598
1599 rgDescriptions.append(valueDescriptionObject[_enumValuesArrayDescriptionJsonKey].toString());
1600 rgValues.append(valueDescriptionObject[_enumValuesArrayValueJsonKey].toDouble());
1601 }
1602
1603 return true;
1604}
1605
1606bool FactMetaData::_parseBitmaskArray(const QJsonObject &jsonObject, QStringList &rgDescriptions, QList<int> &rgValues, QString &errorString)
1607{
1608 rgDescriptions.clear();
1609 rgValues.clear();
1610 errorString.clear();
1611
1612 if (!jsonObject.contains(_enumBitmaskArrayJsonKey)) {
1613 return true;
1614 }
1615
1616 static const QList<JsonParsing::KeyValidateInfo> keyInfoList = {
1617 { _enumBitmaskArrayDescriptionJsonKey, QJsonValue::String, true },
1618 { _enumBitmaskArrayIndexJsonKey, QJsonValue::Double, true },
1619 };
1620
1621 const QJsonArray &rgValueDescription = jsonObject[_enumBitmaskArrayJsonKey].toArray();
1622 for (const QJsonValue &jsonValue : rgValueDescription) {
1623 if (jsonValue.type() != QJsonValue::Object) {
1624 errorString = QStringLiteral("Value in \"values\" array is not an object.");
1625 return false;
1626 }
1627
1628 const QJsonObject &valueDescriptionObject = jsonValue.toObject();
1629 if (!JsonParsing::validateKeys(valueDescriptionObject, keyInfoList, errorString)) {
1630 errorString = QStringLiteral("Object in \"values\" array failed validation '%2'.").arg(errorString);
1631 return false;
1632 }
1633
1634 const QString description = valueDescriptionObject[_enumBitmaskArrayDescriptionJsonKey].toString();
1635 const int index = valueDescriptionObject[_enumBitmaskArrayIndexJsonKey].toInt();
1636 if (index < 0 || index > kMaxBitmaskIndex) {
1637 qCDebug(FactMetaDataLog) << "Ignoring out-of-range bitmask index" << index << "for" << description;
1638 continue;
1639 }
1640
1641 rgDescriptions.append(description);
1642 rgValues.append(index);
1643 }
1644
1645 return true;
1646}
1647
1649{
1650 if (!_rawUserMin.isValid()) {
1651 return QVariant();
1652 }
1653
1654 return _rawTranslator(_rawUserMin);
1655}
1656
1658{
1659 if (!_rawUserMax.isValid()) {
1660 return QVariant();
1661 }
1662
1663 return _rawTranslator(_rawUserMax);
1664}
static constexpr int kMaxBitmaskIndex
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
#define MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN
Holds the meta data associated with a Fact.
static FactMetaData * createFromJsonObject(const QJsonObject &json, const QMap< QString, QString > &defineMap, QObject *metaDataParent)
void setRawUnits(const QString &rawUnits)
void setDecimalPlaces(int decimalPlaces)
void setHasControl(bool bValue)
static constexpr const char * qgcFileType
void setRawUserMin(const QVariant &rawUserMin)
static QVariant appSettingsAreaUnitsToSquareMeters(const QVariant &area)
Converts from user specified distance unit to meters.
static QVariant metersToAppSettingsVerticalDistanceUnits(const QVariant &meters)
Converts from meters to the user specified vertical distance unit.
void setShortDescription(const QString &shortDescription)
void setRawDefaultValueFirmwareForce(const QVariant &rawDefaultValue)
QVariant cookedMin() const
static QVariant metersToAppSettingsHorizontalDistanceUnits(const QVariant &meters)
Converts from meters to the user specified horizontal distance unit.
static QVariant metersSecondToAppSettingsSpeedUnits(const QVariant &metersSecond)
Converts from meters/second to the user specified speed unit.
bool clampValue(const QVariant &cookedValue, QVariant &typedValue) const
bool qgcRebootRequired() const
bool convertAndValidateCooked(const QVariant &cookedValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
Same as convertAndValidateRaw except for cookedValue input.
QVariant cookedUserMax() const
void setQGCRebootRequired(bool rebootRequired)
static size_t typeToSize(ValueType_t type)
QVariant cookedMax() const
static QStringList splitTranslatedList(const QString &translatedList)
static QString typeToString(ValueType_t type)
static QMap< QString, FactMetaData * > createMapFromJsonFile(const QString &jsonFilename, QObject *metaDataParent)
QVariant rawMin() const
@ valueTypeElapsedTimeInSeconds
void addBitmaskInfo(const QString &name, const QVariant &value)
Used to add new values to the bitmask lists after the meta data has been loaded.
void setLongDescription(const QString &longDescription)
static QMap< QString, FactMetaData * > createMapFromJsonArray(const QJsonArray &jsonArray, const DefineMap_t &defineMap, QObject *metaDataParent)
static QString appSettingsVerticalDistanceUnitsString()
Returns the string for vertical distance units which has configued by user.
void setRawUserMax(const QVariant &rawUserMax)
void setLabel(const QString &label)
static constexpr int kUnknownDecimalPlaces
Number of decimal places to specify is not known.
void setMaxStringLength(int maxStringLength)
Negative values are invalid (they would silently disable length validation): warns and clamps to 0.
int decimalPlaces() const
void setVehicleRebootRequired(bool rebootRequired)
static constexpr int kDefaultDecimalPlaces
Default value for decimal places if not specified/known.
static QString appSettingsWeightUnitsString()
Returns the string for weight units which has configued by user.
void setTranslators(Translator rawTranslator_, Translator cookedTranslator_)
void setRawMin(const QVariant &rawMin)
QVariant cookedUserMin() const
QVariant rawUserMin() const
void setCategory(const QString &category)
bool convertAndValidateRaw(const QVariant &rawValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
QString rawUnits() const
QVariant rawUserMax() const
FactMetaData(QObject *parent=nullptr)
static QString appSettingsAreaUnitsString()
Returns the string for distance units which has configued by user.
static QVariant maxForType(ValueType_t type)
void setRawDefaultValue(const QVariant &rawDefaultValue)
static QVariant gramsToAppSettingsWeightUnits(const QVariant &grams)
Converts from grams to the user specified weight unit.
static QString appSettingsHorizontalDistanceUnitsString()
Returns the string for horizontal distance units which has configued by user.
double rawIncrement() const
int maxStringLength() const
Maximum string length for valueTypeString facts. 0 means no limit.
void setEnumInfo(const QStringList &strings, const QVariantList &values)
void setBitmaskInfo(const QStringList &strings, const QVariantList &values)
void setRawMax(const QVariant &rawMax)
bool readOnly() const
static QVariant squareMetersToAppSettingsAreaUnits(const QVariant &squareMeters)
Converts from meters to the user specified distance unit.
void removeEnumInfo(const QVariant &value)
Used to remove values from the enum lists after the meta data has been loaded.
static QVariant minForType(ValueType_t type)
static QVariant appSettingsVerticalDistanceUnitsToMeters(const QVariant &distance)
Converts from user specified vertical distance unit to meters.
void setRawIncrement(double increment)
void addEnumInfo(const QString &name, const QVariant &value)
Used to add new values to the enum lists after the meta data has been loaded.
void setVolatileValue(bool bValue)
QString name() const
static QVariant appSettingsHorizontalDistanceUnitsToMeters(const QVariant &distance)
Converts from user specified horizontal distance unit to meters.
const FactMetaData & operator=(const FactMetaData &other)
void setGroup(const QString &group)
QVariant rawDefaultValue() const
ValueType_t type() const
static ValueType_t stringToType(const QString &typeString, bool &unknownType)
static QVariant appSettingsSpeedUnitsToMetersSecond(const QVariant &speed)
Converts from user specified speed unit to meters/second.
static QVariant appSettingsWeightUnitsToGrams(const QVariant &weight)
Converts from user specified weight unit to grams.
bool volatileValue() const
QVariant rawMax() const
double cookedIncrement() const
void setBuiltInTranslator()
Set the translators to the standard built in versions.
static QString appSettingsSpeedUnitsString()
Returns the string for speed units which has configued by user.
void setReadOnly(bool bValue)
UnitsSettings * unitsSettings() const
static SettingsManager * instance()
@ VerticalDistanceUnitsMeters
@ SpeedUnitsKilometersPerHour
@ HorizontalDistanceUnitsFeet
@ HorizontalDistanceUnitsMeters
bool validateKeys(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates that all required keys are present and that listed keys have the expected type.
QJsonObject openInternalQGCJsonFile(const QString &jsonFilename, const QString &expectedFileType, int minSupportedVersion, int maxSupportedVersion, int &version, QString &errorString, const QStringList &defaultTranslateKeys, const QStringList &defaultArrayIDKeys)
bool validateKeysStrict(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates keys like validateKeys but also rejects any keys not listed in keyInfo.