Commit 5053bebc authored by Don Gagne's avatar Don Gagne

Merge pull request #1471 from DonLakeFlyer/MoreMetaData

Full meta data support in fact system
parents 6042ff09 6760bc95
......@@ -613,7 +613,7 @@ HEADERS+= \
src/AutoPilotPlugins/PX4/SensorsComponentController.h \
src/AutoPilotPlugins/PX4/SafetyComponent.h \
src/AutoPilotPlugins/PX4/PowerComponent.h \
src/AutoPilotPlugins/PX4/PX4ParameterFacts.h \
src/AutoPilotPlugins/PX4/PX4ParameterLoader.h \
SOURCES += \
src/VehicleSetup/SetupView.cc \
......@@ -637,7 +637,7 @@ SOURCES += \
src/AutoPilotPlugins/PX4/SensorsComponentController.cc \
src/AutoPilotPlugins/PX4/SafetyComponent.cc \
src/AutoPilotPlugins/PX4/PowerComponent.cc \
src/AutoPilotPlugins/PX4/PX4ParameterFacts.cc \
src/AutoPilotPlugins/PX4/PX4ParameterLoader.cc \
# Fact System code
......
......@@ -25,7 +25,7 @@
#include "AutoPilotPluginManager.h"
#include "UASManager.h"
#include "QGCUASParamManagerInterface.h"
#include "PX4ParameterFacts.h"
#include "PX4ParameterLoader.h"
#include "FlightModesComponentController.h"
#include "AirframeComponentController.h"
#include "QGCMessageBox.h"
......@@ -79,12 +79,12 @@ PX4AutoPilotPlugin::PX4AutoPilotPlugin(UASInterface* uas, QObject* parent) :
qmlRegisterType<FlightModesComponentController>("QGroundControl.Controllers", 1, 0, "FlightModesComponentController");
qmlRegisterType<AirframeComponentController>("QGroundControl.Controllers", 1, 0, "AirframeComponentController");
_parameterFacts = new PX4ParameterFacts(uas, this);
_parameterFacts = new PX4ParameterLoader(uas, this);
Q_CHECK_PTR(_parameterFacts);
connect(_parameterFacts, &PX4ParameterFacts::parametersReady, this, &PX4AutoPilotPlugin::_pluginReadyPreChecks);
connect(_parameterFacts, &PX4ParameterLoader::parametersReady, this, &PX4AutoPilotPlugin::_pluginReadyPreChecks);
PX4ParameterFacts::loadParameterFactMetaData();
PX4ParameterLoader::loadParameterFactMetaData();
}
PX4AutoPilotPlugin::~PX4AutoPilotPlugin()
......@@ -187,7 +187,7 @@ QString PX4AutoPilotPlugin::getShortModeText(uint8_t baseMode, uint32_t customMo
void PX4AutoPilotPlugin::clearStaticData(void)
{
PX4ParameterFacts::clearStaticData();
PX4ParameterLoader::clearStaticData();
}
const QVariantList& PX4AutoPilotPlugin::vehicleComponents(void)
......
......@@ -27,7 +27,7 @@
#include "AutoPilotPlugin.h"
#include "AutoPilotPluginManager.h"
#include "UASInterface.h"
#include "PX4ParameterFacts.h"
#include "PX4ParameterLoader.h"
#include "AirframeComponent.h"
#include "RadioComponent.h"
#include "FlightModesComponent.h"
......@@ -71,7 +71,7 @@ private:
// Overrides from AutoPilotPlugin
virtual ParameterLoader* _getParameterLoader(void) { return _parameterFacts; }
PX4ParameterFacts* _parameterFacts;
PX4ParameterLoader* _parameterFacts;
QVariantList _components;
AirframeComponent* _airframeComponent;
RadioComponent* _radioComponent;
......
/*=====================================================================
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>
#include "PX4ParameterFacts.h"
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
QGC_LOGGING_CATEGORY(PX4ParameterFactsMetaDataLog, "PX4ParameterFactsMetaDataLog")
bool PX4ParameterFacts::_parameterMetaDataLoaded = false;
QMap<QString, FactMetaData*> PX4ParameterFacts::_mapParameterName2FactMetaData;
PX4ParameterFacts::PX4ParameterFacts(UASInterface* uas, QObject* parent) :
ParameterLoader(uas, parent)
{
Q_ASSERT(uas);
}
/// This will fill in missing meta data such as range info
void PX4ParameterFacts::_initMetaData(FactMetaData* metaData)
{
switch (metaData->type) {
case FactMetaData::valueTypeUint8:
metaData->min = QVariant(std::numeric_limits<quint8>::min());
metaData->max = QVariant(std::numeric_limits<quint8>::max());
break;
case FactMetaData::valueTypeInt8:
metaData->min = QVariant(std::numeric_limits<qint8>::min());
metaData->max = QVariant(std::numeric_limits<qint8>::max());
break;
case FactMetaData::valueTypeUint16:
metaData->min = QVariant(std::numeric_limits<quint16>::min());
metaData->max = QVariant(std::numeric_limits<quint16>::max());
break;
case FactMetaData::valueTypeInt16:
metaData->min = QVariant(std::numeric_limits<qint16>::min());
metaData->max = QVariant(std::numeric_limits<qint16>::max());
break;
case FactMetaData::valueTypeUint32:
metaData->min = QVariant(std::numeric_limits<quint32>::min());
metaData->max = QVariant(std::numeric_limits<quint32>::max());
break;
case FactMetaData::valueTypeInt32:
metaData->min = QVariant(std::numeric_limits<qint32>::min());
metaData->max = QVariant(std::numeric_limits<qint32>::max());
break;
case FactMetaData::valueTypeFloat:
metaData->min = QVariant(std::numeric_limits<float>::min());
metaData->max = QVariant(std::numeric_limits<float>::max());
break;
case FactMetaData::valueTypeDouble:
metaData->min = QVariant(std::numeric_limits<double>::min());
metaData->max = QVariant(std::numeric_limits<double>::max());
break;
}
}
/// Converts a string to a typed QVariant
/// @param type Type for Fact which dictates the QVariant type as well
/// @param failOk - true: Don't assert if convert fails
/// @return Returns the correctly type QVariant
QVariant PX4ParameterFacts::_stringToTypedVariant(const QString& string, FactMetaData::ValueType_t type, bool failOk)
{
QVariant var(string);
bool convertOk;
Q_UNUSED(convertOk);
switch (type) {
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16:
case FactMetaData::valueTypeUint32:
convertOk = var.convert(QVariant::UInt);
if (!failOk) {
Q_ASSERT(convertOk);
}
break;
case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32:
convertOk = var.convert(QVariant::Int);
if (!failOk) {
qDebug() << string;
Q_ASSERT(convertOk);
}
break;
case FactMetaData::valueTypeFloat:
case FactMetaData::valueTypeDouble:
convertOk = var.convert(QVariant::Double);
if (!failOk) {
qDebug() << string;
Q_ASSERT(convertOk);
}
break;
}
return var;
}
/// Load Parameter Fact meta data
///
/// The meta data comes from firmware parameters.xml file.
void PX4ParameterFacts::loadParameterFactMetaData(void)
{
if (_parameterMetaDataLoaded) {
return;
}
_parameterMetaDataLoaded = true;
qCDebug(PX4ParameterFactsMetaDataLog) << "Loading PX4 parameter fact meta data";
Q_ASSERT(_mapParameterName2FactMetaData.count() == 0);
// First look for meta data that comes from a firmware download. Fall back to resource if not there.
QSettings settings;
QDir parameterDir = QFileInfo(settings.fileName()).dir();
QString parameterFilename = parameterDir.filePath("PX4ParameterFactMetaData.xml");
if (!QFile(parameterFilename).exists()) {
parameterFilename = ":/AutoPilotPlugins/PX4/ParameterFactMetaData.xml";
}
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();
QString factGroup;
FactMetaData* metaData = NULL;
while (!xml.atEnd()) {
if (xml.isStartElement()) {
QString elementName = xml.name().toString();
if (elementName == "parameters") {
// Just move to next state
} else if (elementName == "group") {
factGroup = xml.attributes().value("name").toString();
qCDebug(PX4ParameterFactsMetaDataLog) << "Found group: " << factGroup;
} else if (elementName == "parameter") {
metaData = new FactMetaData();
Q_CHECK_PTR(metaData);
metaData->group = factGroup;
} else if (elementName == "code") {
Q_ASSERT(metaData);
QString name = xml.readElementText();
qCDebug(PX4ParameterFactsMetaDataLog) << "Found parameter: " << name;
// FIXME: Change the parameter build scheme in Firmware to get rid of ifdef dup problem
if (!_mapParameterName2FactMetaData.contains(name)) {
_mapParameterName2FactMetaData[name] = metaData;
}
} else if (elementName == "type") {
Q_ASSERT(metaData);
QString type = xml.readElementText();
// Convert type from string to FactMetaData::ValueType_t
struct String2Type {
const char* strType;
FactMetaData::ValueType_t type;
};
static const struct String2Type rgString2Type[] = {
{ "FLOAT", FactMetaData::valueTypeFloat },
{ "INT32", FactMetaData::valueTypeInt32 },
};
static const size_t crgString2Type = sizeof(rgString2Type) / sizeof(rgString2Type[0]);
bool found = false;
for (size_t i=0; i<crgString2Type; i++) {
const struct String2Type* info = &rgString2Type[i];
if (type == info->strType) {
found = true;
metaData->type = info->type;
break;
}
}
Q_UNUSED(found);
Q_ASSERT(found);
qCDebug(PX4ParameterFactsMetaDataLog) << "Type:" << type << metaData->type;
_initMetaData(metaData);
} else if (elementName == "short_desc") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
metaData->shortDescription = text;
} else if (elementName == "long_desc") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
metaData->longDescription = text;
} else if (elementName == "default") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
// FIXME: failOk=true Is a hack to handle enum values in default value. Need
// to implement enums in the meta data.
metaData->defaultValue = _stringToTypedVariant(text, metaData->type, true /* failOk */);
} else if (elementName == "min") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
metaData->min = _stringToTypedVariant(text, metaData->type);
} else if (elementName == "max") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
metaData->max = _stringToTypedVariant(text, metaData->type);
metaData->shortDescription = text;
} else if (elementName == "unit") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
metaData->units = text;
}
} else if (xml.isEndElement() && xml.name() == "parameter") {
metaData = NULL;
}
xml.readNext();
}
}
void PX4ParameterFacts::clearStaticData(void)
{
foreach(QString parameterName, _mapParameterName2FactMetaData.keys()) {
delete _mapParameterName2FactMetaData[parameterName];
}
_mapParameterName2FactMetaData.clear();
_parameterMetaDataLoaded = false;
}
/// Override from FactLoad which connects the meta data to the fact
void PX4ParameterFacts::_addMetaDataToFact(Fact* fact)
{
if (_mapParameterName2FactMetaData.contains(fact->name())) {
fact->setMetaData(_mapParameterName2FactMetaData[fact->name()]);
} else {
// Use generic meta data
ParameterLoader::_addMetaDataToFact(fact);
}
}
/*=====================================================================
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>
#include "PX4ParameterLoader.h"
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
QGC_LOGGING_CATEGORY(PX4ParameterLoaderLog, "PX4ParameterLoaderLog")
bool PX4ParameterLoader::_parameterMetaDataLoaded = false;
QMap<QString, FactMetaData*> PX4ParameterLoader::_mapParameterName2FactMetaData;
PX4ParameterLoader::PX4ParameterLoader(UASInterface* uas, QObject* parent) :
ParameterLoader(uas, parent)
{
Q_ASSERT(uas);
}
/// 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
QVariant PX4ParameterLoader::_stringToTypedVariant(const QString& string, FactMetaData::ValueType_t type, bool* convertOk)
{
QVariant var(string);
int convertTo = QVariant::Int; // keep compiler warning happy
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;
}
/// Load Parameter Fact meta data
///
/// The meta data comes from firmware parameters.xml file.
void PX4ParameterLoader::loadParameterFactMetaData(void)
{
if (_parameterMetaDataLoaded) {
return;
}
_parameterMetaDataLoaded = true;
qCDebug(PX4ParameterLoaderLog) << "Loading PX4 parameter fact meta data";
Q_ASSERT(_mapParameterName2FactMetaData.count() == 0);
// First look for meta data that comes from a firmware download. Fall back to resource if not there.
QSettings settings;
QDir parameterDir = QFileInfo(settings.fileName()).dir();
QString parameterFilename = parameterDir.filePath("PX4ParameterFactMetaData.xml");
if (!QFile(parameterFilename).exists()) {
parameterFilename = ":/AutoPilotPlugins/PX4/ParameterFactMetaData.xml";
}
qCDebug(PX4ParameterLoaderLog) << "Loading parameter meta data:" << parameterFilename;
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;
}
QString factGroup;
FactMetaData* metaData = NULL;
int xmlState = XmlStateNone;
bool badMetaData = true;
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;
}
if (intVersion <= 2) {
// We can't read these old files
qDebug() << "Parameter version stamp too old, skipping load" << parameterFilename;
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();
qCDebug(PX4ParameterLoaderLog) << "Found group: " << factGroup;
} 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();
QString strDefault = xml.attributes().value("default").toString();
qCDebug(PX4ParameterLoaderLog) << "Found parameter name:" << name << " type:" << type << " default:" << strDefault;
// Convert type from string to FactMetaData::ValueType_t
struct String2Type {
const char* strType;
FactMetaData::ValueType_t type;
};
static const struct String2Type rgString2Type[] = {
{ "FLOAT", FactMetaData::valueTypeFloat },
{ "INT32", FactMetaData::valueTypeInt32 },
};
static const size_t crgString2Type = sizeof(rgString2Type) / sizeof(rgString2Type[0]);
bool found = false;
FactMetaData::ValueType_t foundType;
for (size_t i=0; i<crgString2Type; i++) {
const struct String2Type* info = &rgString2Type[i];
if (type == info->strType) {
found = true;
foundType = info->type;
break;
}
}
if (!found) {
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
metaData = new FactMetaData(foundType);
Q_CHECK_PTR(metaData);
if (_mapParameterName2FactMetaData.contains(name)) {
// We can't trust the meta dafa since we have dups
qDebug() << "Duplicate parameter found:" << name;
badMetaData = true;
// Reset to default meta data
_mapParameterName2FactMetaData[name] = metaData;
} else {
_mapParameterName2FactMetaData[name] = metaData;
metaData->setGroup(factGroup);
if (xml.attributes().hasAttribute("default")) {
bool convertOk;
QVariant varDefault = _stringToTypedVariant(strDefault, metaData->type(), &convertOk);
if (convertOk) {
metaData->setDefaultValue(varDefault);
} else {
// Non-fatal
qDebug() << "Parameter meta data with bad default value, name:" << name << " type:" << type << " default:" << strDefault;
}
}
}
} else {
// We should be getting meta data now
if (xmlState != XmlStateFoundParameter) {
qWarning() << "Badly formed XML";
return;
}
if (!badMetaData) {
if (elementName == "short_desc") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
qCDebug(PX4ParameterLoaderLog) << "Short description:" << text;
metaData->setShortDescription(text);
} else if (elementName == "long_desc") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
qCDebug(PX4ParameterLoaderLog) << "Long description:" << text;
metaData->setLongDescription(text);
} else if (elementName == "min") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
qCDebug(PX4ParameterLoaderLog) << "Min:" << text;
bool convertOk;
QVariant varMin = _stringToTypedVariant(text, metaData->type(), &convertOk);
if (convertOk) {
metaData->setMin(varMin);
} else {
// Non-fatal
qDebug() << "Parameter meta data with bad min value:" << text;
}
} else if (elementName == "max") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
qCDebug(PX4ParameterLoaderLog) << "Max:" << text;
bool convertOk;
QVariant varMax = _stringToTypedVariant(text, metaData->type(), &convertOk);
if (convertOk) {
metaData->setMax(varMax);
} else {
// Non-fatal
qDebug() << "Parameter meta data with bad max value:" << text;
}
} else if (elementName == "unit") {
Q_ASSERT(metaData);
QString text = xml.readElementText();
qCDebug(PX4ParameterLoaderLog) << "Unit:" << text;
metaData->setUnits(text);
} else {
qDebug() << "Unknown element in XML: " << elementName;
}
}
}
} else if (xml.isEndElement()) {
QString elementName = xml.name().toString();
if (elementName == "parameter") {
// Done loading this one parameter
metaData = NULL;
badMetaData = false;
xmlState = XmlStateFoundGroup;
} else if (elementName == "group") {
xmlState = XmlStateFoundVersion;
} else if (elementName == "parameters") {
xmlState = XmlStateFoundParameters;
}
}
xml.readNext();
}
}
void PX4ParameterLoader::clearStaticData(void)
{
foreach(QString parameterName, _mapParameterName2FactMetaData.keys()) {
delete _mapParameterName2FactMetaData[parameterName];
}
_mapParameterName2FactMetaData.clear();
_parameterMetaDataLoaded = false;
}
/// Override from FactLoad which connects the meta data to the fact
void PX4ParameterLoader::_addMetaDataToFact(Fact* fact)
{
if (_mapParameterName2FactMetaData.contains(fact->name())) {
fact->setMetaData(_mapParameterName2FactMetaData[fact->name()]);
} else {
// Use generic meta data
ParameterLoader::_addMetaDataToFact(fact);
}
}
......@@ -21,8 +21,8 @@
======================================================================*/
#ifndef PX4PARAMETERFACTS_H
#define PX4PARAMETERFACTS_H
#ifndef PX4PARAMETERLOADER_H
#define PX4PARAMETERLOADER_H
#include <QObject>
#include <QMap>
......@@ -36,17 +36,17 @@
/// @file
/// @author Don Gagne <don@thegagnes.com>
Q_DECLARE_LOGGING_CATEGORY(PX4ParameterFactsMetaDataLog)
Q_DECLARE_LOGGING_CATEGORY(PX4ParameterLoaderLog)
/// Collection of Parameter Facts for PX4 AutoPilot
class PX4ParameterFacts : public ParameterLoader
class PX4ParameterLoader : public ParameterLoader
{
Q_OBJECT
public:
/// @param uas Uas which this set of facts is associated with