Skip to content
FactMetaData.cc 49.1 KiB
Newer Older
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsAreaUnitsTranslation("m^2");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedTranslator(area);
    } else {
        return area;
    }
}

QString FactMetaData::appSettingsAreaUnitsString(void)
{
    const AppSettingsTranslation_s* pAppSettingsTranslation = _findAppSettingsAreaUnitsTranslation("m^2");
    if (pAppSettingsTranslation) {
        return pAppSettingsTranslation->cookedUnits;
    } else {
        return QStringLiteral("m^2");
    }
}

double FactMetaData::cookedIncrement(void) const
{
    return _rawTranslator(this->rawIncrement()).toDouble();
int FactMetaData::decimalPlaces(void) const
{
    int actualDecimalPlaces = defaultDecimalPlaces;
    int incrementDecimalPlaces = unknownDecimalPlaces;

    // First determine decimal places from increment
    double increment = _rawTranslator(this->rawIncrement()).toDouble();
    if (!qIsNaN(increment)) {
        double integralPart;

        // Get the fractional part only
        increment = fabs(modf(increment, &integralPart));
        if (increment == 0.0) {
            // No fractional part, so no decimal places
            incrementDecimalPlaces = 0;
        } else {
            incrementDecimalPlaces = -ceil(log10(increment));
        }
    }

    // Correct decimal places is the larger of the two, increment or meta data value
    if (incrementDecimalPlaces != unknownDecimalPlaces && _decimalPlaces == unknownDecimalPlaces) {
        actualDecimalPlaces = incrementDecimalPlaces;
    } else {
        // Adjust decimal places for cooked translation
        int settingsDecimalPlaces = _decimalPlaces == unknownDecimalPlaces ? defaultDecimalPlaces : _decimalPlaces;
        double ctest = _rawTranslator(1.0).toDouble();

        settingsDecimalPlaces += -log10(ctest);

        settingsDecimalPlaces = qMin(25, settingsDecimalPlaces);
        settingsDecimalPlaces = qMax(0, settingsDecimalPlaces);

        actualDecimalPlaces = qMax(settingsDecimalPlaces, incrementDecimalPlaces);
    }

    return actualDecimalPlaces;
}
Don Gagne's avatar
 
Don Gagne committed
FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QMap<QString, QString>& defineMap, 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);
    }

    QList<JsonHelper::KeyValidateInfo> keyInfoList = {
Don Gagne's avatar
 
Don Gagne committed
        { _nameJsonKey,                 QJsonValue::String, true },
        { _typeJsonKey,                 QJsonValue::String, true },
        { _shortDescriptionJsonKey,     QJsonValue::String, false },
        { _longDescriptionJsonKey,      QJsonValue::String, false },
        { _unitsJsonKey,                QJsonValue::String, false },
        { _decimalPlacesJsonKey,        QJsonValue::Double, false },
        { _minJsonKey,                  QJsonValue::Double, false },
        { _maxJsonKey,                  QJsonValue::Double, false },
        { _hasControlJsonKey,           QJsonValue::Bool,   false },
        { _qgcRebootRequiredJsonKey,    QJsonValue::Bool,   false },
    };
    if (!JsonHelper::validateKeys(json, keyInfoList, 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;
Don Gagne's avatar
 
Don Gagne committed
    if (JsonHelper::parseEnum(json, defineMap, enumStrings, enumValues, errorString, metaData->name())) {
        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());
    }
#ifdef __mobile__
    if (json.contains(_mobileDefaultValueJsonKey)) {
Don Gagne's avatar
Don Gagne committed
        defaultValueJsonKey = _mobileDefaultValueJsonKey;
    if (defaultValueJsonKey.isEmpty() && json.contains(_defaultValueJsonKey)) {
        defaultValueJsonKey = _defaultValueJsonKey;
    }
    if (!defaultValueJsonKey.isEmpty()) {
        QVariant typedValue;
        QString errorString;
        QVariant initialValue = json[defaultValueJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawDefaultValue(typedValue);
        } else {
            qWarning() << "Invalid default value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
    }

    if (json.contains(_incrementJsonKey)) {
        QVariant typedValue;
        QString errorString;
        QVariant initialValue = json[_incrementJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawIncrement(typedValue.toDouble());
        } else {
            qWarning() << "Invalid increment value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
    }

    if (json.contains(_minJsonKey)) {
        QVariant typedValue;
        QString errorString;
        QVariant initialValue = json[_minJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawMin(typedValue);
        } else {
            qWarning() << "Invalid min value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
    if (json.contains(_maxJsonKey)) {
        QVariant typedValue;
        QString errorString;
        QVariant initialValue = json[_maxJsonKey].toVariant();
        if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) {
            metaData->setRawMax(typedValue);
        } else {
            qWarning() << "Invalid max value, name:" << metaData->name()
                       << " type:" << metaData->type()
                       << " value:" << initialValue
                       << " error:" << errorString;
        }
    if (json.contains(_hasControlJsonKey)) {
        metaData->setHasControl(json[_hasControlJsonKey].toBool());
    } else {
        metaData->setHasControl(true);
    }
Don Gagne's avatar
 
Don Gagne committed
    if (json.contains(_qgcRebootRequiredJsonKey)) {
        metaData->setQGCRebootRequired(json[_qgcRebootRequiredJsonKey].toBool());
    } else {
        metaData->setQGCRebootRequired(false);
    }

    return metaData;
}

Don Gagne's avatar
 
Don Gagne committed
void FactMetaData::_loadJsonDefines(const QJsonObject& jsonDefinesObject, QMap<QString, QString>& defineMap)
{
    for (const QString& defineName: jsonDefinesObject.keys()) {
        QString mapKey = _jsonMetaDataDefinesName + QString(".") + defineName;
        defineMap[mapKey] = jsonDefinesObject[defineName].toString();
    }
}

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) {
Don Gagne's avatar
Don Gagne committed
        qWarning() <<  "Unable to parse json document filename:error:offset" << jsonFilename << jsonParseError.errorString() << jsonParseError.offset;
        return metaDataMap;
    }

Don Gagne's avatar
 
Don Gagne committed
    QJsonArray factArray;
    QMap<QString /* define name */, QString /* define value */> defineMap;

    if (doc.isObject()) {
        // Check for Defines/Facts format
        QString errorString;
        QList<JsonHelper::KeyValidateInfo> keyInfoList = {
            { FactMetaData::_jsonMetaDataDefinesName,   QJsonValue::Object, true },
            { FactMetaData::_jsonMetaDataFactsName,     QJsonValue::Array, true },
        };
        if (!JsonHelper::validateKeys(doc.object(), keyInfoList, errorString)) {
            qWarning() << "Json document incorrect format:" << errorString;
            return metaDataMap;
        }

        _loadJsonDefines(doc.object()[FactMetaData::_jsonMetaDataDefinesName].toObject(), defineMap);
        factArray = doc.object()[FactMetaData::_jsonMetaDataFactsName].toArray();
    } else if (doc.isArray()) {
        factArray = doc.array();
    } else {
        qWarning() << "Json document is neither array nor object";
        return metaDataMap;
    }

Don Gagne's avatar
 
Don Gagne committed
    return createMapFromJsonArray(factArray, defineMap, metaDataParent);
Don Gagne's avatar
 
Don Gagne committed
QMap<QString, FactMetaData*> FactMetaData::createMapFromJsonArray(const QJsonArray jsonArray, QMap<QString, QString>& defineMap, QObject* metaDataParent)
{
    QMap<QString, FactMetaData*> metaDataMap;
    for (int i=0; i<jsonArray.count(); i++) {
        QJsonValue jsonValue = jsonArray.at(i);
        if (!jsonValue.isObject()) {
            qWarning() << QStringLiteral("JsonValue at index %1 not an object").arg(i);
            continue;
        }
        QJsonObject jsonObject = jsonValue.toObject();
Don Gagne's avatar
 
Don Gagne committed
        FactMetaData* metaData = createFromJsonObject(jsonObject, defineMap, metaDataParent);
        if (metaDataMap.contains(metaData->name())) {
            qWarning() << QStringLiteral("Duplicate fact name:") << metaData->name();
            delete metaData;
        } else {
            metaDataMap[metaData->name()] = metaData;
        }
    }
    return metaDataMap;
}

QVariant FactMetaData::cookedMax(void) const
{
    // We have to be careful with cooked min/max. Running the raw values through the translator could flip min and max.
    return qMax(_rawTranslator(_rawMax), _rawTranslator(_rawMin));
}

QVariant FactMetaData::cookedMin(void) const
{
    // We have to be careful with cooked min/max. Running the raw values through the translator could flip min and max.
    return qMin(_rawTranslator(_rawMax), _rawTranslator(_rawMin));

void FactMetaData::setVolatileValue(bool bValue)
{
    _volatile = bValue;
    if (_volatile) {
        _readOnly = true;
    }
}