Commit 99b298c6 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #4060 from DonLakeFlyer/GlobalFactMetaData

Global fact meta data setup
parents 9b5d673e 5466f496
......@@ -189,5 +189,6 @@
<file alias="Vehicle/GPSFact.json">src/Vehicle/GPSFact.json</file>
<file alias="Vehicle/WindFact.json">src/Vehicle/WindFact.json</file>
<file alias="Vehicle/VibrationFact.json">src/Vehicle/VibrationFact.json</file>
<file alias="QGroundControlQmlGlobal.json">src/QmlControls/QGroundControlQmlGlobal.json</file>
</qresource>
</RCC>
......@@ -20,17 +20,6 @@
QGC_LOGGING_CATEGORY(FactGroupLog, "FactGroupLog")
const char* FactGroup::_decimalPlacesJsonKey = "decimalPlaces";
const char* FactGroup::_nameJsonKey = "name";
const char* FactGroup::_propertiesJsonKey = "properties";
const char* FactGroup::_versionJsonKey = "version";
const char* FactGroup::_typeJsonKey = "type";
const char* FactGroup::_shortDescriptionJsonKey = "shortDescription";
const char* FactGroup::_unitsJsonKey = "units";
const char* FactGroup::_defaultValueJsonKey = "defaultValue";
const char* FactGroup::_minJsonKey = "min";
const char* FactGroup::_maxJsonKey = "max";
FactGroup::FactGroup(int updateRateMsecs, const QString& metaDataFile, QObject* parent)
: QObject(parent)
, _updateRateMSecs(updateRateMsecs)
......@@ -121,119 +110,5 @@ void FactGroup::_updateAllValues(void)
void FactGroup::_loadMetaData(const QString& jsonFilename)
{
if (jsonFilename.isEmpty()) {
return;
}
qCDebug(FactGroupLog) << "Loading" << jsonFilename;
QFile jsonFile(jsonFilename);
if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Unable to open file" << jsonFilename << jsonFile.errorString();
return;
}
QByteArray bytes = jsonFile.readAll();
jsonFile.close();
QJsonParseError jsonParseError;
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Unable to open json document" << jsonFilename << jsonParseError.errorString();
return;
}
QJsonObject json = doc.object();
int version = json.value(_versionJsonKey).toInt();
if (version != 1) {
qWarning() << "Invalid version" << version;
return;
}
QJsonValue jsonValue = json.value(_propertiesJsonKey);
if (!jsonValue.isArray()) {
qWarning() << "properties object not array";
return;
}
QJsonArray jsonArray = jsonValue.toArray();
foreach(QJsonValue property, jsonArray) {
if (!property.isObject()) {
qWarning() << "properties object should contain only objects";
return;
}
QJsonObject jsonObject = property.toObject();
// Make sure we have the required keys
QString errorString;
QStringList requiredKeys;
requiredKeys << _nameJsonKey << _typeJsonKey << _shortDescriptionJsonKey;
if (!JsonHelper::validateRequiredKeys(jsonObject, requiredKeys, errorString)) {
qWarning() << errorString;
return;
}
// Validate key types
QStringList keys;
QList<QJsonValue::Type> types;
keys << _nameJsonKey << _decimalPlacesJsonKey << _typeJsonKey << _shortDescriptionJsonKey << _unitsJsonKey << _defaultValueJsonKey << _minJsonKey << _maxJsonKey;
types << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Double << QJsonValue::Double;
if (!JsonHelper::validateKeyTypes(jsonObject, keys, types, errorString)) {
qWarning() << errorString;
return;
}
QString name = jsonObject.value(_nameJsonKey).toString();
if (_nameToFactMetaDataMap.contains(name)) {
qWarning() << "Duplicate property name" << name;
continue;
}
bool unknownType;
FactMetaData::ValueType_t type = FactMetaData::stringToType(jsonObject.value(_typeJsonKey).toString(), unknownType);
if (unknownType) {
qWarning() << "Unknown type" << jsonObject.value(_typeJsonKey).toString();
return;
}
QStringList enumValues, enumStrings;
if (!JsonHelper::parseEnum(jsonObject, enumStrings, enumValues, errorString)) {
qWarning() << errorString;
return;
}
FactMetaData* metaData = new FactMetaData(type, this);
metaData->setDecimalPlaces(jsonObject.value(_decimalPlacesJsonKey).toInt(0));
metaData->setShortDescription(jsonObject.value(_shortDescriptionJsonKey).toString());
metaData->setRawUnits(jsonObject.value(_unitsJsonKey).toString());
if (jsonObject.contains(_defaultValueJsonKey)) {
metaData->setRawDefaultValue(jsonObject.value(_defaultValueJsonKey).toDouble());
}
if (jsonObject.contains(_minJsonKey)) {
metaData->setRawMin(jsonObject.value(_minJsonKey).toDouble());
}
if (jsonObject.contains(_maxJsonKey)) {
metaData->setRawMax(jsonObject.value(_maxJsonKey).toDouble());
}
for (int i=0; i<enumValues.count(); i++) {
QVariant enumVariant;
QString errorString;
if (metaData->convertAndValidateRaw(enumValues[i], false /* validate */, enumVariant, errorString)) {
metaData->addEnumInfo(enumStrings[i], enumVariant);
} else {
qWarning() << "Invalid enum value, name:" << metaData->name()
<< " type:" << metaData->type() << " value:" << enumValues[i]
<< " error:" << errorString;
delete metaData;
return;
}
}
_nameToFactMetaDataMap[name] = metaData;
}
_nameToFactMetaDataMap = FactMetaData::createMapFromJsonFile(jsonFilename, this);
}
......@@ -57,17 +57,6 @@ private:
QMap<QString, FactMetaData*> _nameToFactMetaDataMap;
QTimer _updateTimer;
static const char* _propertiesJsonKey;
static const char* _nameJsonKey;
static const char* _decimalPlacesJsonKey;
static const char* _typeJsonKey;
static const char* _versionJsonKey;
static const char* _shortDescriptionJsonKey;
static const char* _unitsJsonKey;
static const char* _defaultValueJsonKey;
static const char* _minJsonKey;
static const char* _maxJsonKey;
};
#endif
......@@ -15,9 +15,12 @@
#include "FactMetaData.h"
#include "QGroundControlQmlGlobal.h"
#include "JsonHelper.h"
#include <QDebug>
#include <QtMath>
#include <QJsonParseError>
#include <QJsonArray>
#include <limits>
#include <cmath>
......@@ -59,6 +62,16 @@ const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTransla
{ "m/s", "kn", true, QGroundControlQmlGlobal::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond },
};
const char* FactMetaData::_decimalPlacesJsonKey = "decimalPlaces";
const char* FactMetaData::_nameJsonKey = "name";
const char* FactMetaData::_typeJsonKey = "type";
const char* FactMetaData::_shortDescriptionJsonKey = "shortDescription";
const char* FactMetaData::_longDescriptionJsonKey = "longDescription";
const char* FactMetaData::_unitsJsonKey = "units";
const char* FactMetaData::_defaultValueJsonKey = "defaultValue";
const char* FactMetaData::_minJsonKey = "min";
const char* FactMetaData::_maxJsonKey = "max";
FactMetaData::FactMetaData(QObject* parent)
: QObject(parent)
, _type(valueTypeInt32)
......@@ -178,22 +191,22 @@ void FactMetaData::setRawMax(const QVariant& rawMax)
QVariant FactMetaData::_minForType(void) const
{
switch (_type) {
case valueTypeUint8:
return QVariant(std::numeric_limits<unsigned char>::min());
case valueTypeInt8:
return QVariant(std::numeric_limits<signed char>::min());
case valueTypeUint16:
return QVariant(std::numeric_limits<unsigned short int>::min());
case valueTypeInt16:
return QVariant(std::numeric_limits<short int>::min());
case valueTypeUint32:
return QVariant(std::numeric_limits<unsigned int>::min());
case valueTypeInt32:
return QVariant(std::numeric_limits<int>::min());
case valueTypeFloat:
return QVariant(-std::numeric_limits<float>::max());
case valueTypeDouble:
return QVariant(-std::numeric_limits<double>::max());
case valueTypeUint8:
return QVariant(std::numeric_limits<unsigned char>::min());
case valueTypeInt8:
return QVariant(std::numeric_limits<signed char>::min());
case valueTypeUint16:
return QVariant(std::numeric_limits<unsigned short int>::min());
case valueTypeInt16:
return QVariant(std::numeric_limits<short int>::min());
case valueTypeUint32:
return QVariant(std::numeric_limits<unsigned int>::min());
case valueTypeInt32:
return QVariant(std::numeric_limits<int>::min());
case valueTypeFloat:
return QVariant(-std::numeric_limits<float>::max());
case valueTypeDouble:
return QVariant(-std::numeric_limits<double>::max());
}
// Make windows compiler happy, even switch is full cased
......@@ -203,22 +216,22 @@ QVariant FactMetaData::_minForType(void) const
QVariant FactMetaData::_maxForType(void) const
{
switch (_type) {
case valueTypeUint8:
return QVariant(std::numeric_limits<unsigned char>::max());
case valueTypeInt8:
return QVariant(std::numeric_limits<signed char>::max());
case valueTypeUint16:
return QVariant(std::numeric_limits<unsigned short int>::max());
case valueTypeInt16:
return QVariant(std::numeric_limits<short int>::max());
case valueTypeUint32:
return QVariant(std::numeric_limits<unsigned int>::max());
case valueTypeInt32:
return QVariant(std::numeric_limits<int>::max());
case valueTypeFloat:
return QVariant(std::numeric_limits<float>::max());
case valueTypeDouble:
return QVariant(std::numeric_limits<double>::max());
case valueTypeUint8:
return QVariant(std::numeric_limits<unsigned char>::max());
case valueTypeInt8:
return QVariant(std::numeric_limits<signed char>::max());
case valueTypeUint16:
return QVariant(std::numeric_limits<unsigned short int>::max());
case valueTypeInt16:
return QVariant(std::numeric_limits<short int>::max());
case valueTypeUint32:
return QVariant(std::numeric_limits<unsigned int>::max());
case valueTypeInt32:
return QVariant(std::numeric_limits<int>::max());
case valueTypeFloat:
return QVariant(std::numeric_limits<float>::max());
case valueTypeDouble:
return QVariant(std::numeric_limits<double>::max());
}
// Make windows compiler happy, even switch is full cased
......@@ -232,45 +245,45 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO
errorString.clear();
switch (type()) {
case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32:
typedValue = QVariant(rawValue.toInt(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
}
case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32:
typedValue = QVariant(rawValue.toInt(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
}
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16:
case FactMetaData::valueTypeUint32:
typedValue = QVariant(rawValue.toUInt(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
}
}
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16:
case FactMetaData::valueTypeUint32:
typedValue = QVariant(rawValue.toUInt(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
}
break;
case FactMetaData::valueTypeFloat:
typedValue = QVariant(rawValue.toFloat(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
}
}
break;
case FactMetaData::valueTypeFloat:
typedValue = QVariant(rawValue.toFloat(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
}
break;
case FactMetaData::valueTypeDouble:
typedValue = QVariant(rawValue.toDouble(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
}
}
break;
case FactMetaData::valueTypeDouble:
typedValue = QVariant(rawValue.toDouble(&convertOk));
if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
}
break;
}
break;
}
if (!convertOk) {
......@@ -287,45 +300,45 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co
errorString.clear();
switch (type()) {
case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32:
typedValue = QVariant(cookedValue.toInt(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
}
case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32:
typedValue = QVariant(cookedValue.toInt(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
}
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16:
case FactMetaData::valueTypeUint32:
typedValue = QVariant(cookedValue.toUInt(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
}
}
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16:
case FactMetaData::valueTypeUint32:
typedValue = QVariant(cookedValue.toUInt(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
}
break;
case FactMetaData::valueTypeFloat:
typedValue = QVariant(cookedValue.toFloat(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
}
}
break;
case FactMetaData::valueTypeFloat:
typedValue = QVariant(cookedValue.toFloat(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
}
break;
case FactMetaData::valueTypeDouble:
typedValue = QVariant(cookedValue.toDouble(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
}
}
break;
case FactMetaData::valueTypeDouble:
typedValue = QVariant(cookedValue.toDouble(&convertOk));
if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
}
break;
}
break;
}
if (!convertOk) {
......@@ -536,22 +549,22 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString,
unknownType = false;
knownTypeStrings << QStringLiteral("Uint8")
<< QStringLiteral("Int8")
<< QStringLiteral("Uint16")
<< QStringLiteral("Int16")
<< QStringLiteral("Uint32")
<< QStringLiteral("Int32")
<< QStringLiteral("Float")
<< QStringLiteral("Double");
<< QStringLiteral("Int8")
<< QStringLiteral("Uint16")
<< QStringLiteral("Int16")
<< QStringLiteral("Uint32")
<< QStringLiteral("Int32")
<< QStringLiteral("Float")
<< QStringLiteral("Double");
knownTypes << valueTypeUint8
<< valueTypeInt8
<< valueTypeUint16
<< valueTypeInt16
<< valueTypeUint32
<< valueTypeInt32
<< valueTypeFloat
<< valueTypeDouble;
<< valueTypeInt8
<< valueTypeUint16
<< valueTypeInt16
<< valueTypeUint32
<< valueTypeInt32
<< valueTypeFloat
<< valueTypeDouble;
for (int i=0; i<knownTypeStrings.count(); i++) {
if (knownTypeStrings[i].compare(typeString, Qt::CaseInsensitive) == 0) {
......@@ -567,25 +580,25 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString,
size_t FactMetaData::typeToSize(ValueType_t type)
{
switch (type) {
case valueTypeUint8:
case valueTypeInt8:
return 1;
case valueTypeUint8:
case valueTypeInt8:
return 1;
case valueTypeUint16:
case valueTypeInt16:
return 2;
case valueTypeUint16:
case valueTypeInt16:
return 2;
case valueTypeUint32:
case valueTypeInt32:
case valueTypeFloat:
return 4;
case valueTypeUint32:
case valueTypeInt32:
case valueTypeFloat:
return 4;
case valueTypeDouble:
return 8;
case valueTypeDouble:
return 8;
default:
qWarning() << "Unsupported fact value type" << type;
return 4;
default:
qWarning() << "Unsupported fact value type" << type;
return 4;
}
}
......@@ -615,7 +628,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist
const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
if (pAppSettingsTranslation->rawUnits == rawUnits &&
(!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::distanceUnits()->rawValue().toUInt())) {
(!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::distanceUnits()->rawValue().toUInt())) {
return pAppSettingsTranslation;
}
}
......@@ -629,7 +642,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea
const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
if (pAppSettingsTranslation->rawUnits == rawUnits &&
(!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::areaUnits()->rawValue().toUInt())
(!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::areaUnits()->rawValue().toUInt())
) {
return pAppSettingsTranslation;
}
......@@ -736,3 +749,120 @@ int FactMetaData::decimalPlaces(void) const
return actualDecimalPlaces;
}
FactMetaData* FactMetaData::_createFromJsonObject(const QJsonObject& json, QObject* metaDataParent)
{
QString errorString;
// Make sure we have the required keys
QStringList requiredKeys;
requiredKeys << _nameJsonKey << _typeJsonKey;
if (!JsonHelper::validateRequiredKeys(json, requiredKeys, errorString)) {
qWarning() << errorString;
return new FactMetaData(valueTypeUint32, metaDataParent);
}
// Validate key types
QStringList keys;
QList<QJsonValue::Type> types;
keys << _nameJsonKey << _decimalPlacesJsonKey << _typeJsonKey << _shortDescriptionJsonKey << _longDescriptionJsonKey << _unitsJsonKey << _defaultValueJsonKey << _minJsonKey << _maxJsonKey;
types << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Double << QJsonValue::Double;
if (!JsonHelper::validateKeyTypes(json, keys, types, errorString)) {
qWarning() << errorString;
return new FactMetaData(valueTypeUint32, metaDataParent);
}
bool unknownType;
FactMetaData::ValueType_t type = FactMetaData::stringToType(json[_typeJsonKey].toString(), unknownType);
if (unknownType) {
qWarning() << "Unknown type" << json[_typeJsonKey].toString();
return new FactMetaData(valueTypeUint32, metaDataParent);
}
FactMetaData* metaData = new FactMetaData(type, metaDataParent);
metaData->_name = json[_nameJsonKey].toString();
QStringList enumValues, enumStrings;
if (JsonHelper::parseEnum(json, enumStrings, enumValues, errorString)) {
for (int i=0; i<enumValues.count(); i++) {
QVariant enumVariant;
QString errorString;
if (metaData->convertAndValidateRaw(enumValues[i], false /* validate */, enumVariant, errorString)) {
metaData->addEnumInfo(enumStrings[i], enumVariant);
} else {
qWarning() << "Invalid enum value, name:" << metaData->name()
<< " type:" << metaData->type()
<< " value:" << enumValues[i]
<< " error:" << errorString;
}
}
} else {
qWarning() << errorString;
}
metaData->setDecimalPlaces(json[_decimalPlacesJsonKey].toInt(0));
metaData->setShortDescription(json[_shortDescriptionJsonKey].toString());
metaData->setLongDescription(json[_longDescriptionJsonKey].toString());
if (json.contains(_unitsJsonKey)) {
metaData->setRawUnits(json[_unitsJsonKey].toString());
}
if (json.contains(_defaultValueJsonKey)) {
metaData->setRawDefaultValue(json[_defaultValueJsonKey].toDouble());
}
if (json.contains(_minJsonKey)) {
metaData->setRawMin(json[_minJsonKey].toDouble());
}
if (json.contains(_maxJsonKey)) {
metaData->setRawMax(json[_maxJsonKey].toDouble());
}
return metaData;
}
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent)
{
QMap<QString, FactMetaData*> metaDataMap;
QFile jsonFile(jsonFilename);
if (!jsonFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "Unable to open file" << jsonFilename << jsonFile.errorString();
return metaDataMap;
}
QByteArray bytes = jsonFile.readAll();
jsonFile.close();
QJsonParseError jsonParseError;
QJsonDocument doc = QJsonDocument::fromJson(bytes, &jsonParseError);
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Unable to parse json document" << jsonFilename << jsonParseError.errorString();
return metaDataMap;
}
if (!doc.isArray()) {
qWarning() << "json document is not array";
return metaDataMap;
}
QJsonArray jsonArray = doc.array();
for (int i=0; i<jsonArray.count(); i++) {
QJsonValueRef jsonValue = jsonArray[i];
if (!jsonValue.isObject()) {
qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
continue;
}
QJsonObject jsonObject = jsonValue.toObject();
FactMetaData* metaData = _createFromJsonObject(jsonObject, metaDataParent);
if (metaDataMap.contains(metaData->name())) {
qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
} else {
metaDataMap[metaData->name()] = metaData;
}
}
return metaDataMap;
}
......@@ -17,7 +17,7 @@
#include <QObject>
#include <QString>
#include <QVariant>
#include <QJsonObject>
/// Holds the meta data associated with a Fact.
///
......@@ -46,6 +46,8 @@ public:
FactMetaData(ValueType_t type, QObject* parent = NULL);
FactMetaData(const FactMetaData& other, QObject* parent = NULL);
static QMap<QString, FactMetaData*> createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent);
const FactMetaData& operator=(const FactMetaData& other);
/// Converts from meters to the user specified distance unit
......@@ -142,6 +144,7 @@ private:
QVariant _minForType(void) const;
QVariant _maxForType(void) const;
void _setAppSettingsTranslators(void);
static FactMetaData* _createFromJsonObject(const QJsonObject& json, QObject* metaDataParent);
// Built in translators
static QVariant _defaultTranslator(const QVariant& from) { return from; }
......@@ -225,6 +228,16 @@ private:
static const BuiltInTranslation_s _rgBuiltInTranslations[];
static const AppSettingsTranslation_s _rgAppSettingsTranslations[];
static const char* _nameJsonKey;
static const char* _decimalPlacesJsonKey;
static const char* _typeJsonKey;
static const char* _shortDescriptionJsonKey;
static const char* _longDescriptionJsonKey;
static const char* _unitsJsonKey;
static const char* _defaultValueJsonKey;
static const char* _minJsonKey;
static const char* _maxJsonKey;
};
#endif
......@@ -30,7 +30,7 @@ SettingsFact::SettingsFact(QString settingGroup, QString settingName, FactMetaDa
_rawValue = settings.value(_name, defaultValue);
connect(this, &Fact::valueChanged, this, &SettingsFact::_valueChanged);
connect(this, &Fact::rawValueChanged, this, &SettingsFact::_rawValueChanged);
}
SettingsFact::SettingsFact(const SettingsFact& other, QObject* parent)
......@@ -48,7 +48,7 @@ const SettingsFact& SettingsFact::operator=(const SettingsFact& other)
return *this;
}
void SettingsFact::_valueChanged(QVariant value)
void SettingsFact::_rawValueChanged(QVariant value)
{
QSettings settings;
......
......@@ -29,7 +29,7 @@ public:
const SettingsFact& operator=(const SettingsFact& other);
private slots:
void _valueChanged(QVariant value);
void _rawValueChanged(QVariant value);
private:
QString _settingGroup;
......
......@@ -101,13 +101,13 @@ bool JsonHelper::validateKeyTypes(const QJsonObject& jsonObject, const QStringLi
return true;
}
bool JsonHelper::parseEnum(QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString)
bool JsonHelper::parseEnum(const QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString)
{
enumStrings = jsonObject.value(_enumStringsJsonKey).toString().split(",", QString::SkipEmptyParts);
enumValues = jsonObject.value(_enumValuesJsonKey).toString().split(",", QString::SkipEmptyParts);
if (enumStrings.count() != enumValues.count()) {
errorString = QString("enum strings/values count mismatch: %1");
errorString = QString("enum strings/values count mismatch strings:values %1:%2").arg(enumStrings.count()).arg(enumValues.count());
return false;
}
......
......@@ -24,7 +24,7 @@ public:
static bool validateRequiredKeys(const QJsonObject& jsonObject, const QStringList& keys, QString& errorString);
static bool validateKeyTypes(const QJsonObject& jsonObject, const QStringList& keys, const QList<QJsonValue::Type>& types, QString& errorString);
static bool toQGeoCoordinate(const QJsonValue& jsonValue, QGeoCoordinate& coordinate, bool altitudeRequired, QString& errorString);
static bool parseEnum(QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString);
static bool parseEnum(const QJsonObject& jsonObject, QStringList& enumStrings, QStringList& enumValues, QString& errorString);
static void writeQGeoCoordinate(QJsonValue& jsonValue, const QGeoCoordinate& coordinate, bool writeAltitude);
......
......@@ -19,20 +19,17 @@
static const char* kQmlGlobalKeyName = "QGCQml";
SettingsFact* QGroundControlQmlGlobal::_offlineEditingFirmwareTypeFact = NULL;
FactMetaData* QGroundControlQmlGlobal::_offlineEditingFirmwareTypeMetaData = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingVehicleTypeFact = NULL;
FactMetaData* QGroundControlQmlGlobal::_offlineEditingVehicleTypeMetaData = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingCruiseSpeedFact = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingHoverSpeedFact = NULL;
SettingsFact* QGroundControlQmlGlobal::_distanceUnitsFact = NULL;
FactMetaData* QGroundControlQmlGlobal::_distanceUnitsMetaData = NULL;
SettingsFact* QGroundControlQmlGlobal::_areaUnitsFact = NULL;
FactMetaData* QGroundControlQmlGlobal::_areaUnitsMetaData = NULL;
SettingsFact* QGroundControlQmlGlobal::_speedUnitsFact = NULL;
FactMetaData* QGroundControlQmlGlobal::_speedUnitsMetaData = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingFirmwareTypeFact = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingVehicleTypeFact = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingCruiseSpeedFact = NULL;
SettingsFact* QGroundControlQmlGlobal::_offlineEditingHoverSpeedFact = NULL;
SettingsFact* QGroundControlQmlGlobal::_batteryPercentRemainingAnnounceFact = NULL;
FactMetaData* QGroundControlQmlGlobal::_batteryPercentRemainingAnnounceMetaData = NULL;
const char* QGroundControlQmlGlobal::_virtualTabletJoystickKey = "VirtualTabletJoystick";
const char* QGroundControlQmlGlobal::_baseFontPointSizeKey = "BaseDeviceFontPointSize";
......@@ -229,20 +226,21 @@ void QGroundControlQmlGlobal::setBaseFontPointSize(qreal size)
}
}
Fact* QGroundControlQmlGlobal::offlineEditingFirmwareType(void)
SettingsFact* QGroundControlQmlGlobal::_createSettingsFact(const QString& name)
{
if (!_offlineEditingFirmwareTypeFact) {
QStringList enumStrings;
QVariantList enumValues;
SettingsFact* fact;
FactMetaData* metaData = nameToMetaDataMap()[name];
_offlineEditingFirmwareTypeFact = new SettingsFact(QString(), "OfflineEditingFirmwareType", FactMetaData::valueTypeUint32, (uint32_t)MAV_AUTOPILOT_ARDUPILOTMEGA);
_offlineEditingFirmwareTypeMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
fact = new SettingsFact(QString(), name, metaData->type(), metaData->rawDefaultValue());
fact->setMetaData(metaData);
enumStrings << tr("ArduPilot Firmware") << tr("PX4 Pro Firmware") << tr("Mavlink Generic Firmware");
enumValues << QVariant::fromValue((uint32_t)MAV_AUTOPILOT_ARDUPILOTMEGA) << QVariant::fromValue((uint32_t)MAV_AUTOPILOT_PX4) << QVariant::fromValue((uint32_t)MAV_AUTOPILOT_GENERIC);
return fact;
}
_offlineEditingFirmwareTypeMetaData->setEnumInfo(enumStrings, enumValues);
_offlineEditingFirmwareTypeFact->setMetaData(_offlineEditingFirmwareTypeMetaData);
Fact* QGroundControlQmlGlobal::offlineEditingFirmwareType(void)
{
if (!_offlineEditingFirmwareTypeFact) {
_offlineEditingFirmwareTypeFact = _createSettingsFact(QStringLiteral("OfflineEditingFirmwareType"));
}
return _offlineEditingFirmwareTypeFact;
......@@ -251,19 +249,7 @@ Fact* QGroundControlQmlGlobal::offlineEditingFirmwareType(void)
Fact* QGroundControlQmlGlobal::offlineEditingVehicleType(void)
{
if (!_offlineEditingVehicleTypeFact) {
QStringList enumStrings;
QVariantList enumValues;
_offlineEditingVehicleTypeFact = new SettingsFact(QString(), "OfflineEditingVehicleType", FactMetaData::valueTypeUint32, (uint32_t)MAV_TYPE_FIXED_WING);
_offlineEditingVehicleTypeMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
enumStrings << tr("Fixedwing") << tr("Multicopter") << tr("VTOL") << tr("Rover") << tr("Sub");
enumValues << QVariant::fromValue((uint32_t)MAV_TYPE_FIXED_WING) << QVariant::fromValue((uint32_t)MAV_TYPE_QUADROTOR)
<< QVariant::fromValue((uint32_t)MAV_TYPE_VTOL_DUOROTOR) << QVariant::fromValue((uint32_t)MAV_TYPE_GROUND_ROVER)
<< QVariant::fromValue((uint32_t)MAV_TYPE_SUBMARINE);
_offlineEditingVehicleTypeMetaData->setEnumInfo(enumStrings, enumValues);
_offlineEditingVehicleTypeFact->setMetaData(_offlineEditingVehicleTypeMetaData);
_offlineEditingVehicleTypeFact = _createSettingsFact(QStringLiteral("OfflineEditingVehicleType"));
}
return _offlineEditingVehicleTypeFact;
......@@ -272,7 +258,7 @@ Fact* QGroundControlQmlGlobal::offlineEditingVehicleType(void)
Fact* QGroundControlQmlGlobal::offlineEditingCruiseSpeed(void)
{
if (!_offlineEditingCruiseSpeedFact) {
_offlineEditingCruiseSpeedFact = new SettingsFact(QString(), "OfflineEditingCruiseSpeed", FactMetaData::valueTypeDouble, 16.0);
_offlineEditingCruiseSpeedFact = _createSettingsFact(QStringLiteral("OfflineEditingCruiseSpeed"));
}
return _offlineEditingCruiseSpeedFact;
}
......@@ -280,7 +266,7 @@ Fact* QGroundControlQmlGlobal::offlineEditingCruiseSpeed(void)
Fact* QGroundControlQmlGlobal::offlineEditingHoverSpeed(void)
{
if (!_offlineEditingHoverSpeedFact) {
_offlineEditingHoverSpeedFact = new SettingsFact(QString(), "OfflineEditingHoverSpeed", FactMetaData::valueTypeDouble, 4.0);
_offlineEditingHoverSpeedFact = _createSettingsFact(QStringLiteral("OfflineEditingHoverSpeed"));
}
return _offlineEditingHoverSpeedFact;
}
......@@ -288,6 +274,7 @@ Fact* QGroundControlQmlGlobal::offlineEditingHoverSpeed(void)
Fact* QGroundControlQmlGlobal::distanceUnits(void)
{
if (!_distanceUnitsFact) {
// Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading.
QStringList enumStrings;
QVariantList enumValues;
......@@ -308,6 +295,7 @@ Fact* QGroundControlQmlGlobal::distanceUnits(void)
Fact* QGroundControlQmlGlobal::areaUnits(void)
{
if (!_areaUnitsFact) {
// Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading.
QStringList enumStrings;
QVariantList enumValues;
......@@ -328,6 +316,7 @@ Fact* QGroundControlQmlGlobal::areaUnits(void)
Fact* QGroundControlQmlGlobal::speedUnits(void)
{
if (!_speedUnitsFact) {
// Distance/Area/Speed units settings can't be loaded from json since it creates an infinite loop of meta data loading.
QStringList enumStrings;
QVariantList enumValues;
......@@ -347,19 +336,7 @@ Fact* QGroundControlQmlGlobal::speedUnits(void)
Fact* QGroundControlQmlGlobal::batteryPercentRemainingAnnounce(void)
{
if (!_batteryPercentRemainingAnnounceFact) {
QStringList enumStrings;
QVariantList enumValues;
_batteryPercentRemainingAnnounceFact = new SettingsFact(QString(), "batteryPercentRemainingAnnounce", FactMetaData::valueTypeUint32, 30);
_batteryPercentRemainingAnnounceMetaData = new FactMetaData(FactMetaData::valueTypeUint32);
_batteryPercentRemainingAnnounceMetaData->setDecimalPlaces(0);
_batteryPercentRemainingAnnounceMetaData->setShortDescription(tr("Percent announce"));
_batteryPercentRemainingAnnounceMetaData->setRawUnits("%");
_batteryPercentRemainingAnnounceMetaData->setRawMin(0);
_batteryPercentRemainingAnnounceMetaData->setRawMax(100);
_batteryPercentRemainingAnnounceFact->setMetaData(_batteryPercentRemainingAnnounceMetaData);
_batteryPercentRemainingAnnounceFact = _createSettingsFact(QStringLiteral("batteryPercentRemainingAnnounce"));
}
return _batteryPercentRemainingAnnounceFact;
......@@ -372,3 +349,13 @@ bool QGroundControlQmlGlobal::linesIntersect(QPointF line1A, QPointF line1B, QPo
return QLineF(line1A, line1B).intersect(QLineF(line2A, line2B), &intersectPoint) == QLineF::BoundedIntersection &&
intersectPoint != line1A && intersectPoint != line1B;
}
QMap<QString, FactMetaData*>& QGroundControlQmlGlobal::nameToMetaDataMap(void) {
static QMap<QString, FactMetaData*> map;
if (map.isEmpty()) {
map = FactMetaData::createMapFromJsonFile(":/json/QGroundControlQmlGlobal.json", NULL);
}
return map;
}
......@@ -229,6 +229,9 @@ signals:
void flightMapZoomChanged (double flightMapZoom);
private:
static SettingsFact* _createSettingsFact(const QString& name);
static QMap<QString, FactMetaData*>& nameToMetaDataMap(void);
FlightMapSettings* _flightMapSettings;
HomePositionManager* _homePositionManager;
LinkManager* _linkManager;
......@@ -245,9 +248,7 @@ private:
// These are static so they are available to C++ code as well as Qml
static SettingsFact* _offlineEditingFirmwareTypeFact;
static FactMetaData* _offlineEditingFirmwareTypeMetaData;
static SettingsFact* _offlineEditingVehicleTypeFact;
static FactMetaData* _offlineEditingVehicleTypeMetaData;
static SettingsFact* _offlineEditingCruiseSpeedFact;
static SettingsFact* _offlineEditingHoverSpeedFact;
static SettingsFact* _distanceUnitsFact;
......@@ -257,7 +258,6 @@ private:
static SettingsFact* _speedUnitsFact;
static FactMetaData* _speedUnitsMetaData;
static SettingsFact* _batteryPercentRemainingAnnounceFact;
static FactMetaData* _batteryPercentRemainingAnnounceMetaData;
static const char* _virtualTabletJoystickKey;
static const char* _baseFontPointSizeKey;
......
[
{
"name": "OfflineEditingFirmwareType",
"shortDescription": "Offline editing firmware type",
"type": "uint32",
"enumStrings": "ArduPilot Firmware,PX4 Pro Firmware,Mavlink Generic Firmware",
"enumValues": "3,12,0",
"defaultValue": 3
},
{
"name": "OfflineEditingVehicleType",
"shortDescription": "Offline editing vehicle type",
"type": "uint32",
"enumStrings": "Fixedwing,Multicopter,VTOL,Rover,Sub",
"enumValues": "1,2,19,10,12",
"defaultValue": 1
},
{
"name": "OfflineEditingCruiseSpeed",
"shortDescription": "Offline editing cruise speed",
"longDescription": "This value defines the cruising speed for forward flight vehicles for use in calculating mission duration when not connected to a vehicle.",
"type": "double",
"defaultValue": 16.0,
"min": 1.0,
"units": "m/s",
"decimalPlaces": 2
},
{
"name": "OfflineEditingHoverSpeed",
"shortDescription": "Offline editing hover speed",
"longDescription": "This value defines the cruising speed for multi-rotor vehicles for use in calculating mission duration when not connected to a vehicle.",
"type": "double",
"defaultValue": 4.0,
"min": 1.0,
"units": "m/s",
"decimalPlaces": 2
},
{
"name": "batteryPercentRemainingAnnounce",
"shortDescription": "Announce battery remaining percent",
"longDescription": "QGroundControl will announce the remaining battery percent when it falls below the specified percentage.",
"type": "uint32",
"defaultValue": 30,
"units": "%",
"min": 0,
"max": 100
}
]
[
{
"version": 1,
"properties": [
{
"name": "voltage",
"shortDescription": "Voltage",
"type": "double",
"decimalPlaces": 2,
"units": "v"
},
{
"name": "percentRemaining",
"shortDescription": "Percent",
"type": "int32",
"decimalPlaces": 0,
"units": "%"
},
{
"name": "mahConsumed",
"shortDescription": "Consumed",
"type": "int32",
"decimalPlaces": 0,
"units": "mAh"
},
{
"name": "current",
"shortDescription": "Current",
"type": "int32",
"decimalPlaces": 0,
"units": "mA"
},
{
"name": "temperature",
"shortDescription": "Temperature",
"type": "int32",
"decimalPlaces": 2,
"units": "C"
},
{
"name": "cellCount",
"shortDescription": "Cell Count",
"type": "int32",
"decimalPlaces": 0
}
]
"name": "voltage",
"shortDescription": "Voltage",
"type": "double",
"decimalPlaces": 2,
"units": "v"
},
{
"name": "percentRemaining",
"shortDescription": "Percent",
"type": "int32",
"decimalPlaces": 0,
"units": "%"
},
{
"name": "mahConsumed",
"shortDescription": "Consumed",
"type": "int32",
"decimalPlaces": 0,
"units": "mAh"
},
{
"name": "current",
"shortDescription": "Current",
"type": "int32",
"decimalPlaces": 0,
"units": "mA"
},
{
"name": "temperature",
"shortDescription": "Temperature",
"type": "int32",
"decimalPlaces": 2,
"units": "C"
},
{
"name": "cellCount",
"shortDescription": "Cell Count",
"type": "int32",
"decimalPlaces": 0
}
]
[
{
"version": 1,
"properties": [
{
"name": "hdop",
"shortDescription": "HDOP",
"type": "double",
"decimalPlaces": 1
},
{
"name": "vdop",
"shortDescription": "VDOP",
"type": "double",
"decimalPlaces": 1
},
{
"name": "courseOverGround",
"shortDescription": "Course Over Ground",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "lock",
"shortDescription": "GPS Lock",
"type": "uint32",
"enumStrings": "None,None,2D Lock,3D Lock,3D DGPS Lock,3D RTK GPS Lock (float),3D RTK GPS Lock (fixed)",
"enumValues": "0,1,2,3,4,5,6",
"decimalPlaces": 0
},
{
"name": "count",
"shortDescription": "Sat Count",
"type": "double",
"decimalPlaces": 0
}
]
"name": "hdop",
"shortDescription": "HDOP",
"type": "double",
"decimalPlaces": 1
},
{
"name": "vdop",
"shortDescription": "VDOP",
"type": "double",
"decimalPlaces": 1
},
{
"name": "courseOverGround",
"shortDescription": "Course Over Ground",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "lock",
"shortDescription": "GPS Lock",
"type": "uint32",
"enumStrings": "None,None,2D Lock,3D Lock,3D DGPS Lock,3D RTK GPS Lock (float),3D RTK GPS Lock (fixed)",
"enumValues": "0,1,2,3,4,5,6",
"decimalPlaces": 0
},
{
"name": "count",
"shortDescription": "Sat Count",
"type": "double",
"decimalPlaces": 0
}
]
[
{
"version": 1,
"properties": [
{
"name": "roll",
"shortDescription": "Roll",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "pitch",
"shortDescription": "Pitch",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "heading",
"shortDescription": "Heading",
"type": "double",
"decimalPlaces": 0,
"units": "deg"
},
{
"name": "groundSpeed",
"shortDescription": "Ground Speed",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "airSpeed",
"shortDescription": "Air Speed",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "climbRate",
"shortDescription": "Climb Rate",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "altitudeRelative",
"shortDescription": "Altitude-rel",
"type": "double",
"decimalPlaces": 1,
"units": "m"
},
{
"name": "altitudeAMSL",
"shortDescription": "Altitude",
"type": "double",
"decimalPlaces": 1,
"units": "m"
}
]
"name": "roll",
"shortDescription": "Roll",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "pitch",
"shortDescription": "Pitch",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "heading",
"shortDescription": "Heading",
"type": "double",
"decimalPlaces": 0,
"units": "deg"
},
{
"name": "groundSpeed",
"shortDescription": "Ground Speed",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "airSpeed",
"shortDescription": "Air Speed",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "climbRate",
"shortDescription": "Climb Rate",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "altitudeRelative",
"shortDescription": "Altitude-rel",
"type": "double",
"decimalPlaces": 1,
"units": "m"
},
{
"name": "altitudeAMSL",
"shortDescription": "Altitude",
"type": "double",
"decimalPlaces": 1,
"units": "m"
}
]
[
{
"version": 1,
"properties": [
{
"name": "xAxis",
"shortDescription": "Vibe xAxis",
"type": "double",
"decimalPlaces": 1
},
{
"name": "yAxis",
"shortDescription": "Vibe yAxis",
"type": "double",
"decimalPlaces": 1
},
{
"name": "zAxis",
"shortDescription": "Vibe zAxis",
"type": "double",
"decimalPlaces": 1
},
{
"name": "clipCount1",
"shortDescription": "Clip Count (1)",
"type": "uint32"
},
{
"name": "clipCount2",
"shortDescription": "Clip Count (2)",
"type": "uint32"
},
{
"name": "clipCount3",
"shortDescription": "Clip Count (3)",
"type": "uint32"
}
]
"name": "xAxis",
"shortDescription": "Vibe xAxis",
"type": "double",
"decimalPlaces": 1
},
{
"name": "yAxis",
"shortDescription": "Vibe yAxis",
"type": "double",
"decimalPlaces": 1
},
{
"name": "zAxis",
"shortDescription": "Vibe zAxis",
"type": "double",
"decimalPlaces": 1
},
{
"name": "clipCount1",
"shortDescription": "Clip Count (1)",
"type": "uint32"
},
{
"name": "clipCount2",
"shortDescription": "Clip Count (2)",
"type": "uint32"
},
{
"name": "clipCount3",
"shortDescription": "Clip Count (3)",
"type": "uint32"
}
]
[
{
"version": 1,
"properties": [
{
"name": "direction",
"shortDescription": "Wind Direction",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "speed",
"shortDescription": "Wind Spd",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "verticalSpeed",
"shortDescription": "Wind Spd (vert)",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
}
]
"name": "direction",
"shortDescription": "Wind Direction",
"type": "double",
"decimalPlaces": 1,
"units": "deg"
},
{
"name": "speed",
"shortDescription": "Wind Spd",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
},
{
"name": "verticalSpeed",
"shortDescription": "Wind Spd (vert)",
"type": "double",
"decimalPlaces": 1,
"units": "m/s"
}
]
......@@ -408,7 +408,7 @@ QGCView {
spacing: ScreenTools.defaultFontPixelWidth
QGCLabel {
anchors.baseline: paletteCombo.baseline
text: qsTr("Style:")
text: qsTr("UI Style:")
width: _labelWidth
}
QGCComboBox {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment