diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index e143d817a0d46ba9c7a2a3eed6766e45765bc790..855caa34b54f2c9811b01c459681f42c9d24b01d 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -15,9 +15,12 @@ #include "FactMetaData.h" #include "QGroundControlQmlGlobal.h" +#include "JsonHelper.h" #include #include +#include +#include #include #include @@ -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::min()); - case valueTypeInt8: - return QVariant(std::numeric_limits::min()); - case valueTypeUint16: - return QVariant(std::numeric_limits::min()); - case valueTypeInt16: - return QVariant(std::numeric_limits::min()); - case valueTypeUint32: - return QVariant(std::numeric_limits::min()); - case valueTypeInt32: - return QVariant(std::numeric_limits::min()); - case valueTypeFloat: - return QVariant(-std::numeric_limits::max()); - case valueTypeDouble: - return QVariant(-std::numeric_limits::max()); + case valueTypeUint8: + return QVariant(std::numeric_limits::min()); + case valueTypeInt8: + return QVariant(std::numeric_limits::min()); + case valueTypeUint16: + return QVariant(std::numeric_limits::min()); + case valueTypeInt16: + return QVariant(std::numeric_limits::min()); + case valueTypeUint32: + return QVariant(std::numeric_limits::min()); + case valueTypeInt32: + return QVariant(std::numeric_limits::min()); + case valueTypeFloat: + return QVariant(-std::numeric_limits::max()); + case valueTypeDouble: + return QVariant(-std::numeric_limits::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::max()); - case valueTypeInt8: - return QVariant(std::numeric_limits::max()); - case valueTypeUint16: - return QVariant(std::numeric_limits::max()); - case valueTypeInt16: - return QVariant(std::numeric_limits::max()); - case valueTypeUint32: - return QVariant(std::numeric_limits::max()); - case valueTypeInt32: - return QVariant(std::numeric_limits::max()); - case valueTypeFloat: - return QVariant(std::numeric_limits::max()); - case valueTypeDouble: - return QVariant(std::numeric_limits::max()); + case valueTypeUint8: + return QVariant(std::numeric_limits::max()); + case valueTypeInt8: + return QVariant(std::numeric_limits::max()); + case valueTypeUint16: + return QVariant(std::numeric_limits::max()); + case valueTypeInt16: + return QVariant(std::numeric_limits::max()); + case valueTypeUint32: + return QVariant(std::numeric_limits::max()); + case valueTypeInt32: + return QVariant(std::numeric_limits::max()); + case valueTypeFloat: + return QVariant(std::numeric_limits::max()); + case valueTypeDouble: + return QVariant(std::numeric_limits::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; irawUnits == 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 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; iconvertAndValidateRaw(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 FactMetaData::createMapFromJsonFile(const QString& jsonFilename, QObject* metaDataParent) +{ + QMap 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; iname())) { + qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name(); + } else { + metaDataMap[metaData->name()] = metaData; + } + } + + return metaDataMap; +} diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index 37b54825edf5f18dcfd3372044d0af5684db5e3a..2765f0c1912b61584e449a793a2a1b5dfb1f076b 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -17,7 +17,7 @@ #include #include #include - +#include /// 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 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