Commit 74ac7502 authored by Don Gagne's avatar Don Gagne

Create meta data from json support

parent ee22373c
...@@ -15,9 +15,12 @@ ...@@ -15,9 +15,12 @@
#include "FactMetaData.h" #include "FactMetaData.h"
#include "QGroundControlQmlGlobal.h" #include "QGroundControlQmlGlobal.h"
#include "JsonHelper.h"
#include <QDebug> #include <QDebug>
#include <QtMath> #include <QtMath>
#include <QJsonParseError>
#include <QJsonArray>
#include <limits> #include <limits>
#include <cmath> #include <cmath>
...@@ -59,6 +62,16 @@ const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTransla ...@@ -59,6 +62,16 @@ const FactMetaData::AppSettingsTranslation_s FactMetaData::_rgAppSettingsTransla
{ "m/s", "kn", true, QGroundControlQmlGlobal::SpeedUnitsKnots, FactMetaData::_metersPerSecondToKnots, FactMetaData::_knotsToMetersPerSecond }, { "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) FactMetaData::FactMetaData(QObject* parent)
: QObject(parent) : QObject(parent)
, _type(valueTypeInt32) , _type(valueTypeInt32)
...@@ -178,22 +191,22 @@ void FactMetaData::setRawMax(const QVariant& rawMax) ...@@ -178,22 +191,22 @@ void FactMetaData::setRawMax(const QVariant& rawMax)
QVariant FactMetaData::_minForType(void) const QVariant FactMetaData::_minForType(void) const
{ {
switch (_type) { switch (_type) {
case valueTypeUint8: case valueTypeUint8:
return QVariant(std::numeric_limits<unsigned char>::min()); return QVariant(std::numeric_limits<unsigned char>::min());
case valueTypeInt8: case valueTypeInt8:
return QVariant(std::numeric_limits<signed char>::min()); return QVariant(std::numeric_limits<signed char>::min());
case valueTypeUint16: case valueTypeUint16:
return QVariant(std::numeric_limits<unsigned short int>::min()); return QVariant(std::numeric_limits<unsigned short int>::min());
case valueTypeInt16: case valueTypeInt16:
return QVariant(std::numeric_limits<short int>::min()); return QVariant(std::numeric_limits<short int>::min());
case valueTypeUint32: case valueTypeUint32:
return QVariant(std::numeric_limits<unsigned int>::min()); return QVariant(std::numeric_limits<unsigned int>::min());
case valueTypeInt32: case valueTypeInt32:
return QVariant(std::numeric_limits<int>::min()); return QVariant(std::numeric_limits<int>::min());
case valueTypeFloat: case valueTypeFloat:
return QVariant(-std::numeric_limits<float>::max()); return QVariant(-std::numeric_limits<float>::max());
case valueTypeDouble: case valueTypeDouble:
return QVariant(-std::numeric_limits<double>::max()); return QVariant(-std::numeric_limits<double>::max());
} }
// Make windows compiler happy, even switch is full cased // Make windows compiler happy, even switch is full cased
...@@ -203,22 +216,22 @@ QVariant FactMetaData::_minForType(void) const ...@@ -203,22 +216,22 @@ QVariant FactMetaData::_minForType(void) const
QVariant FactMetaData::_maxForType(void) const QVariant FactMetaData::_maxForType(void) const
{ {
switch (_type) { switch (_type) {
case valueTypeUint8: case valueTypeUint8:
return QVariant(std::numeric_limits<unsigned char>::max()); return QVariant(std::numeric_limits<unsigned char>::max());
case valueTypeInt8: case valueTypeInt8:
return QVariant(std::numeric_limits<signed char>::max()); return QVariant(std::numeric_limits<signed char>::max());
case valueTypeUint16: case valueTypeUint16:
return QVariant(std::numeric_limits<unsigned short int>::max()); return QVariant(std::numeric_limits<unsigned short int>::max());
case valueTypeInt16: case valueTypeInt16:
return QVariant(std::numeric_limits<short int>::max()); return QVariant(std::numeric_limits<short int>::max());
case valueTypeUint32: case valueTypeUint32:
return QVariant(std::numeric_limits<unsigned int>::max()); return QVariant(std::numeric_limits<unsigned int>::max());
case valueTypeInt32: case valueTypeInt32:
return QVariant(std::numeric_limits<int>::max()); return QVariant(std::numeric_limits<int>::max());
case valueTypeFloat: case valueTypeFloat:
return QVariant(std::numeric_limits<float>::max()); return QVariant(std::numeric_limits<float>::max());
case valueTypeDouble: case valueTypeDouble:
return QVariant(std::numeric_limits<double>::max()); return QVariant(std::numeric_limits<double>::max());
} }
// Make windows compiler happy, even switch is full cased // Make windows compiler happy, even switch is full cased
...@@ -232,45 +245,45 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO ...@@ -232,45 +245,45 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO
errorString.clear(); errorString.clear();
switch (type()) { switch (type()) {
case FactMetaData::valueTypeInt8: case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16: case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32: case FactMetaData::valueTypeInt32:
typedValue = QVariant(rawValue.toInt(&convertOk)); typedValue = QVariant(rawValue.toInt(&convertOk));
if (!convertOnly && convertOk) { if (!convertOnly && convertOk) {
if (rawMin() > typedValue || typedValue > rawMax()) { if (rawMin() > typedValue || typedValue > rawMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt()); errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
}
} }
break; }
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint32: case FactMetaData::valueTypeUint16:
typedValue = QVariant(rawValue.toUInt(&convertOk)); case FactMetaData::valueTypeUint32:
if (!convertOnly && convertOk) { typedValue = QVariant(rawValue.toUInt(&convertOk));
if (rawMin() > typedValue || typedValue > rawMax()) { if (!convertOnly && convertOk) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt()); if (rawMin() > typedValue || typedValue > rawMax()) {
} errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
} }
break; }
break;
case FactMetaData::valueTypeFloat:
typedValue = QVariant(rawValue.toFloat(&convertOk)); case FactMetaData::valueTypeFloat:
if (!convertOnly && convertOk) { typedValue = QVariant(rawValue.toFloat(&convertOk));
if (rawMin() > typedValue || typedValue > rawMax()) { if (!convertOnly && convertOk) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat()); if (rawMin() > typedValue || typedValue > rawMax()) {
} errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
} }
break; }
break;
case FactMetaData::valueTypeDouble:
typedValue = QVariant(rawValue.toDouble(&convertOk)); case FactMetaData::valueTypeDouble:
if (!convertOnly && convertOk) { typedValue = QVariant(rawValue.toDouble(&convertOk));
if (rawMin() > typedValue || typedValue > rawMax()) { if (!convertOnly && convertOk) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble()); if (rawMin() > typedValue || typedValue > rawMax()) {
} errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
} }
break; }
break;
} }
if (!convertOk) { if (!convertOk) {
...@@ -287,45 +300,45 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co ...@@ -287,45 +300,45 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co
errorString.clear(); errorString.clear();
switch (type()) { switch (type()) {
case FactMetaData::valueTypeInt8: case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16: case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32: case FactMetaData::valueTypeInt32:
typedValue = QVariant(cookedValue.toInt(&convertOk)); typedValue = QVariant(cookedValue.toInt(&convertOk));
if (!convertOnly && convertOk) { if (!convertOnly && convertOk) {
if (cookedMin() > typedValue || typedValue > cookedMax()) { if (cookedMin() > typedValue || typedValue > cookedMax()) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt()); errorString = QString("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt());
}
} }
break; }
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint32: case FactMetaData::valueTypeUint16:
typedValue = QVariant(cookedValue.toUInt(&convertOk)); case FactMetaData::valueTypeUint32:
if (!convertOnly && convertOk) { typedValue = QVariant(cookedValue.toUInt(&convertOk));
if (cookedMin() > typedValue || typedValue > cookedMax()) { if (!convertOnly && convertOk) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt()); if (cookedMin() > typedValue || typedValue > cookedMax()) {
} errorString = QString("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt());
} }
break; }
break;
case FactMetaData::valueTypeFloat:
typedValue = QVariant(cookedValue.toFloat(&convertOk)); case FactMetaData::valueTypeFloat:
if (!convertOnly && convertOk) { typedValue = QVariant(cookedValue.toFloat(&convertOk));
if (cookedMin() > typedValue || typedValue > cookedMax()) { if (!convertOnly && convertOk) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat()); if (cookedMin() > typedValue || typedValue > cookedMax()) {
} errorString = QString("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat());
} }
break; }
break;
case FactMetaData::valueTypeDouble:
typedValue = QVariant(cookedValue.toDouble(&convertOk)); case FactMetaData::valueTypeDouble:
if (!convertOnly && convertOk) { typedValue = QVariant(cookedValue.toDouble(&convertOk));
if (cookedMin() > typedValue || typedValue > cookedMax()) { if (!convertOnly && convertOk) {
errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble()); if (cookedMin() > typedValue || typedValue > cookedMax()) {
} errorString = QString("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble());
} }
break; }
break;
} }
if (!convertOk) { if (!convertOk) {
...@@ -536,22 +549,22 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, ...@@ -536,22 +549,22 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString,
unknownType = false; unknownType = false;
knownTypeStrings << QStringLiteral("Uint8") knownTypeStrings << QStringLiteral("Uint8")
<< QStringLiteral("Int8") << QStringLiteral("Int8")
<< QStringLiteral("Uint16") << QStringLiteral("Uint16")
<< QStringLiteral("Int16") << QStringLiteral("Int16")
<< QStringLiteral("Uint32") << QStringLiteral("Uint32")
<< QStringLiteral("Int32") << QStringLiteral("Int32")
<< QStringLiteral("Float") << QStringLiteral("Float")
<< QStringLiteral("Double"); << QStringLiteral("Double");
knownTypes << valueTypeUint8 knownTypes << valueTypeUint8
<< valueTypeInt8 << valueTypeInt8
<< valueTypeUint16 << valueTypeUint16
<< valueTypeInt16 << valueTypeInt16
<< valueTypeUint32 << valueTypeUint32
<< valueTypeInt32 << valueTypeInt32
<< valueTypeFloat << valueTypeFloat
<< valueTypeDouble; << valueTypeDouble;
for (int i=0; i<knownTypeStrings.count(); i++) { for (int i=0; i<knownTypeStrings.count(); i++) {
if (knownTypeStrings[i].compare(typeString, Qt::CaseInsensitive) == 0) { if (knownTypeStrings[i].compare(typeString, Qt::CaseInsensitive) == 0) {
...@@ -567,25 +580,25 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, ...@@ -567,25 +580,25 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString,
size_t FactMetaData::typeToSize(ValueType_t type) size_t FactMetaData::typeToSize(ValueType_t type)
{ {
switch (type) { switch (type) {
case valueTypeUint8: case valueTypeUint8:
case valueTypeInt8: case valueTypeInt8:
return 1; return 1;
case valueTypeUint16: case valueTypeUint16:
case valueTypeInt16: case valueTypeInt16:
return 2; return 2;
case valueTypeUint32: case valueTypeUint32:
case valueTypeInt32: case valueTypeInt32:
case valueTypeFloat: case valueTypeFloat:
return 4; return 4;
case valueTypeDouble: case valueTypeDouble:
return 8; return 8;
default: default:
qWarning() << "Unsupported fact value type" << type; qWarning() << "Unsupported fact value type" << type;
return 4; return 4;
} }
} }
...@@ -615,7 +628,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist ...@@ -615,7 +628,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist
const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
if (pAppSettingsTranslation->rawUnits == rawUnits && if (pAppSettingsTranslation->rawUnits == rawUnits &&
(!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::distanceUnits()->rawValue().toUInt())) { (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::distanceUnits()->rawValue().toUInt())) {
return pAppSettingsTranslation; return pAppSettingsTranslation;
} }
} }
...@@ -629,7 +642,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea ...@@ -629,7 +642,7 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea
const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i]; const AppSettingsTranslation_s* pAppSettingsTranslation = &_rgAppSettingsTranslations[i];
if (pAppSettingsTranslation->rawUnits == rawUnits && if (pAppSettingsTranslation->rawUnits == rawUnits &&
(!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::areaUnits()->rawValue().toUInt()) (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == QGroundControlQmlGlobal::areaUnits()->rawValue().toUInt())
) { ) {
return pAppSettingsTranslation; return pAppSettingsTranslation;
} }
...@@ -736,3 +749,120 @@ int FactMetaData::decimalPlaces(void) const ...@@ -736,3 +749,120 @@ int FactMetaData::decimalPlaces(void) const
return actualDecimalPlaces; 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 @@ ...@@ -17,7 +17,7 @@
#include <QObject> #include <QObject>
#include <QString> #include <QString>
#include <QVariant> #include <QVariant>
#include <QJsonObject>
/// Holds the meta data associated with a Fact. /// Holds the meta data associated with a Fact.
/// ///
...@@ -46,6 +46,8 @@ public: ...@@ -46,6 +46,8 @@ public:
FactMetaData(ValueType_t type, QObject* parent = NULL); FactMetaData(ValueType_t type, QObject* parent = NULL);
FactMetaData(const FactMetaData& other, 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); const FactMetaData& operator=(const FactMetaData& other);
/// Converts from meters to the user specified distance unit /// Converts from meters to the user specified distance unit
...@@ -142,6 +144,7 @@ private: ...@@ -142,6 +144,7 @@ private:
QVariant _minForType(void) const; QVariant _minForType(void) const;
QVariant _maxForType(void) const; QVariant _maxForType(void) const;
void _setAppSettingsTranslators(void); void _setAppSettingsTranslators(void);
static FactMetaData* _createFromJsonObject(const QJsonObject& json, QObject* metaDataParent);
// Built in translators // Built in translators
static QVariant _defaultTranslator(const QVariant& from) { return from; } static QVariant _defaultTranslator(const QVariant& from) { return from; }
...@@ -225,6 +228,16 @@ private: ...@@ -225,6 +228,16 @@ private:
static const BuiltInTranslation_s _rgBuiltInTranslations[]; static const BuiltInTranslation_s _rgBuiltInTranslations[];
static const AppSettingsTranslation_s _rgAppSettingsTranslations[]; 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 #endif
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