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