PX4ParameterMetaData.cc 16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*=====================================================================
 
 QGroundControl Open Source Ground Control Station
 
 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
 
 This file is part of the QGROUNDCONTROL project
 
 QGROUNDCONTROL is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 QGROUNDCONTROL is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
 
 ======================================================================*/

/// @file
///     @author Don Gagne <don@thegagnes.com>

27
#include "PX4ParameterMetaData.h"
28 29 30 31 32 33 34 35
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"

#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>

36
QGC_LOGGING_CATEGORY(PX4ParameterMetaDataLog, "PX4ParameterMetaDataLog")
37

38 39
bool                            PX4ParameterMetaData::_parameterMetaDataLoaded = false;
QMap<QString, FactMetaData*>    PX4ParameterMetaData::_mapParameterName2FactMetaData;
40

41 42
PX4ParameterMetaData::PX4ParameterMetaData(QObject* parent) :
    QObject(parent)
43
{
44

45 46 47 48 49 50 51
}

/// Converts a string to a typed QVariant
///     @param string String to convert
///     @param type Type for Fact which dictates the QVariant type as well
///     @param convertOk Returned: true: conversion success, false: conversion failure
/// @return Returns the correctly type QVariant
52
QVariant PX4ParameterMetaData::_stringToTypedVariant(const QString& string, FactMetaData::ValueType_t type, bool* convertOk)
53 54 55
{
    QVariant var(string);

Don Gagne's avatar
Don Gagne committed
56
    int convertTo = QVariant::Int; // keep compiler warning happy
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
    switch (type) {
        case FactMetaData::valueTypeUint8:
        case FactMetaData::valueTypeUint16:
        case FactMetaData::valueTypeUint32:
            convertTo = QVariant::UInt;
            break;
        case FactMetaData::valueTypeInt8:
        case FactMetaData::valueTypeInt16:
        case FactMetaData::valueTypeInt32:
            convertTo = QVariant::Int;
            break;
        case FactMetaData::valueTypeFloat:
            convertTo = QMetaType::Float;
            break;
        case FactMetaData::valueTypeDouble:
            convertTo = QVariant::Double;
            break;
    }
    
    *convertOk = var.convert(convertTo);
    
    return var;
}

81 82 83 84 85 86 87
QString PX4ParameterMetaData::parameterMetaDataFile(void)
{
    QSettings settings;
    QDir parameterDir = QFileInfo(settings.fileName()).dir();
    return parameterDir.filePath("PX4ParameterFactMetaData.xml");
}

88 89 90
/// Load Parameter Fact meta data
///
/// The meta data comes from firmware parameters.xml file.
91
void PX4ParameterMetaData::_loadParameterFactMetaData(void)
92 93 94 95 96 97
{
    if (_parameterMetaDataLoaded) {
        return;
    }
    _parameterMetaDataLoaded = true;
    
98
    qCDebug(PX4ParameterMetaDataLog) << "Loading PX4 parameter fact meta data";
99 100 101

    Q_ASSERT(_mapParameterName2FactMetaData.count() == 0);

102 103 104 105 106
    QString parameterFilename;
    
    // We want unit test builds to always use the resource based meta data to provide repeatable results
    if (!qgcApp()->runningUnitTests()) {
        // First look for meta data that comes from a firmware download. Fall back to resource if not there.
107
        parameterFilename = parameterMetaDataFile();
108 109
    }
	if (parameterFilename.isEmpty() || !QFile(parameterFilename).exists()) {
110 111 112
		parameterFilename = ":/AutoPilotPlugins/PX4/ParameterFactMetaData.xml";
	}
	
113
    qCDebug(PX4ParameterMetaDataLog) << "Loading parameter meta data:" << parameterFilename;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

    QFile xmlFile(parameterFilename);
    Q_ASSERT(xmlFile.exists());
    
    bool success = xmlFile.open(QIODevice::ReadOnly);
    Q_UNUSED(success);
    Q_ASSERT(success);
    
    QXmlStreamReader xml(xmlFile.readAll());
    xmlFile.close();
    if (xml.hasError()) {
        qWarning() << "Badly formed XML" << xml.errorString();
        return;
    }
    
129 130 131 132 133
    QString         factGroup;
    QString         errorString;
    FactMetaData*   metaData = NULL;
    int             xmlState = XmlStateNone;
    bool            badMetaData = true;
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
    
    while (!xml.atEnd()) {
        if (xml.isStartElement()) {
            QString elementName = xml.name().toString();
            
            if (elementName == "parameters") {
                if (xmlState != XmlStateNone) {
                    qWarning() << "Badly formed XML";
                    return;
                }
                xmlState = XmlStateFoundParameters;
                
            } else if (elementName == "version") {
                if (xmlState != XmlStateFoundParameters) {
                    qWarning() << "Badly formed XML";
                    return;
                }
                xmlState = XmlStateFoundVersion;
                
                bool convertOk;
                QString strVersion = xml.readElementText();
                int intVersion = strVersion.toInt(&convertOk);
                if (!convertOk) {
                    qWarning() << "Badly formed XML";
                    return;
                }
160
                if (intVersion <= 2) {
161
                    // We can't read these old files
162
                    qDebug() << "Parameter version stamp too old, skipping load. Found:" << intVersion << "Want: 3 File:" << parameterFilename;
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
                    return;
                }
                
                
            } else if (elementName == "group") {
                if (xmlState != XmlStateFoundVersion) {
                    // We didn't get a version stamp, assume older version we can't read
                    qDebug() << "Parameter version stamp not found, skipping load" << parameterFilename;
                    return;
                }
                xmlState = XmlStateFoundGroup;
                
                if (!xml.attributes().hasAttribute("name")) {
                    qWarning() << "Badly formed XML";
                    return;
                }
                factGroup = xml.attributes().value("name").toString();
180
                qCDebug(PX4ParameterMetaDataLog) << "Found group: " << factGroup;
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
                
            } else if (elementName == "parameter") {
                if (xmlState != XmlStateFoundGroup) {
                    qWarning() << "Badly formed XML";
                    return;
                }
                xmlState = XmlStateFoundParameter;
                
                if (!xml.attributes().hasAttribute("name") || !xml.attributes().hasAttribute("type")) {
                    qWarning() << "Badly formed XML";
                    return;
                }
                
                QString name = xml.attributes().value("name").toString();
                QString type = xml.attributes().value("type").toString();
196
                QString strDefault =    xml.attributes().value("default").toString();
197
                
198
                qCDebug(PX4ParameterMetaDataLog) << "Found parameter name:" << name << " type:" << type << " default:" << strDefault;
199 200

                // Convert type from string to FactMetaData::ValueType_t
Don Gagne's avatar
Don Gagne committed
201 202 203
                bool unknownType;
                FactMetaData::ValueType_t foundType = FactMetaData::stringToType(type, unknownType);
                if (unknownType) {
204 205 206 207 208
                    qWarning() << "Parameter meta data with bad type:" << type << " name:" << name;
                    return;
                }
                
                // Now that we know type we can create meta data object and add it to the system
209
                
210 211
                metaData = new FactMetaData(foundType);
                Q_CHECK_PTR(metaData);
212
                if (_mapParameterName2FactMetaData.contains(name)) {
213
                    // We can't trust the meta dafa since we have dups
214
                    qCWarning(PX4ParameterMetaDataLog) << "Duplicate parameter found:" << name;
215 216 217
                    badMetaData = true;
                    // Reset to default meta data
                    _mapParameterName2FactMetaData[name] = metaData;
218
                } else {
219
                    _mapParameterName2FactMetaData[name] = metaData;
220
                    metaData->setName(name);
221
                    metaData->setGroup(factGroup);
222
                    
223 224 225
                    if (xml.attributes().hasAttribute("default") && !strDefault.isEmpty()) {
                        QVariant varDefault;
                        
226 227
                        if (metaData->convertAndValidateRaw(strDefault, false, varDefault, errorString)) {
                            metaData->setRawDefaultValue(varDefault);
228
                        } else {
229
                            qCWarning(PX4ParameterMetaDataLog) << "Invalid default value, name:" << name << " type:" << type << " default:" << strDefault << " error:" << errorString;
230
                        }
231 232 233 234 235 236 237 238 239 240
                    }
                }
                
            } else {
                // We should be getting meta data now
                if (xmlState != XmlStateFoundParameter) {
                    qWarning() << "Badly formed XML";
                    return;
                }

241 242 243 244
                if (!badMetaData) {
                    if (elementName == "short_desc") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
245
                        text = text.replace("\n", " ");
246
                        qCDebug(PX4ParameterMetaDataLog) << "Short description:" << text;
247
                        metaData->setShortDescription(text);
248

249 250 251
                    } else if (elementName == "long_desc") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
252
                        text = text.replace("\n", " ");
253
                        qCDebug(PX4ParameterMetaDataLog) << "Long description:" << text;
254 255 256 257 258
                        metaData->setLongDescription(text);
                        
                    } else if (elementName == "min") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
259
                        qCDebug(PX4ParameterMetaDataLog) << "Min:" << text;
260 261
                        
                        QVariant varMin;
262 263
                        if (metaData->convertAndValidateRaw(text, true /* convertOnly */, varMin, errorString)) {
                            metaData->setRawMin(varMin);
264
                        } else {
265
                            qCWarning(PX4ParameterMetaDataLog) << "Invalid min value, name:" << metaData->name() << " type:" << metaData->type() << " min:" << text << " error:" << errorString;
266 267 268 269 270
                        }
                        
                    } else if (elementName == "max") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
271
                        qCDebug(PX4ParameterMetaDataLog) << "Max:" << text;
272 273
                        
                        QVariant varMax;
274 275
                        if (metaData->convertAndValidateRaw(text, true /* convertOnly */, varMax, errorString)) {
                            metaData->setRawMax(varMax);
276
                        } else {
277
                            qCWarning(PX4ParameterMetaDataLog) << "Invalid max value, name:" << metaData->name() << " type:" << metaData->type() << " max:" << text << " error:" << errorString;
278 279 280 281 282
                        }
                        
                    } else if (elementName == "unit") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
283
                        qCDebug(PX4ParameterMetaDataLog) << "Unit:" << text;
284
                        metaData->setRawUnits(text);
285
                        
286 287 288
                    } else if (elementName == "decimal") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
289
                        qCDebug(PX4ParameterMetaDataLog) << "Decimal:" << text;
290 291 292 293 294 295

                        bool convertOk;
                        QVariant varDecimals = QVariant(text).toUInt(&convertOk);
                        if (convertOk) {
                            metaData->setDecimalPlaces(varDecimals.toInt());
                        } else {
296
                            qCWarning(PX4ParameterMetaDataLog) << "Invalid decimals value, name:" << metaData->name() << " type:" << metaData->type() << " decimals:" << text << " error: invalid number";
297 298
                        }

299 300 301 302 303 304 305 306
                    } else if (elementName == "reboot_required") {
                        Q_ASSERT(metaData);
                        QString text = xml.readElementText();
                        qCDebug(PX4ParameterMetaDataLog) << "RebootRequired:" << text;
                        if (text.compare("true", Qt::CaseInsensitive) == 0) {
                            metaData->setRebootRequired(true);
                        }

307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
                    } else if (elementName == "values") {
                        // doing nothing individual value will follow anyway. May be used for sanity checking.

                    } else if (elementName == "value") {
                        QString enumValueStr = xml.attributes().value("code").toString();
                        QString enumString = xml.readElementText();
                        qCDebug(PX4ParameterMetaDataLog) << "parameter value:"
                                                         << "value desc:" << enumString << "code:" << enumValueStr;

                        QVariant    enumValue;
                        QString     errorString;
                        if (metaData->convertAndValidateRaw(enumValueStr, false /* validate */, enumValue, errorString)) {
                            metaData->addEnumInfo(enumString, enumValue);
                        } else {
                            qCDebug(PX4ParameterMetaDataLog) << "Invalid enum value, name:" << metaData->name()
                                                             << " type:" << metaData->type() << " value:" << enumValueStr
                                                             << " error:" << errorString;
                        }
325
                    } else {
326
                        qDebug() << "Unknown element in XML: " << elementName;
327 328 329 330 331 332 333
                    }
                }
            }
        } else if (xml.isEndElement()) {
            QString elementName = xml.name().toString();

            if (elementName == "parameter") {
334 335 336 337
                // Done loading this parameter, validate default value
                if (metaData->defaultValueAvailable()) {
                    QVariant var;
                    
338 339
                    if (!metaData->convertAndValidateRaw(metaData->rawDefaultValue(), false /* convertOnly */, var, errorString)) {
                        qCWarning(PX4ParameterMetaDataLog) << "Invalid default value, name:" << metaData->name() << " type:" << metaData->type() << " default:" << metaData->rawDefaultValue() << " error:" << errorString;
340 341 342 343
                    }
                }
                
                // Reset for next parameter
344
                metaData = NULL;
345
                badMetaData = false;
346 347 348 349 350 351 352 353 354 355 356 357
                xmlState = XmlStateFoundGroup;
            } else if (elementName == "group") {
                xmlState = XmlStateFoundVersion;
            } else if (elementName == "parameters") {
                xmlState = XmlStateFoundParameters;
            }
        }
        xml.readNext();
    }
}

/// Override from FactLoad which connects the meta data to the fact
358
void PX4ParameterMetaData::addMetaDataToFact(Fact* fact, MAV_TYPE vehicleType)
359
{
360 361
    Q_UNUSED(vehicleType)

Don Gagne's avatar
Don Gagne committed
362
    _loadParameterFactMetaData();
363 364 365 366
    if (_mapParameterName2FactMetaData.contains(fact->name())) {
        fact->setMetaData(_mapParameterName2FactMetaData[fact->name()]);
    } else {
        // Use generic meta data
367 368
        FactMetaData* metaData = new FactMetaData(fact->type(), fact);
        fact->setMetaData(metaData);
369 370
    }
}