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