QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ParameterMetaData.cc
Go to the documentation of this file.
1#include "ParameterMetaData.h"
2#include "JsonParsing.h"
3#include "QGCFileHelper.h"
5
6#include <QtCore/QJsonDocument>
7#include <QtCore/QJsonObject>
8#include <QtCore/QRegularExpression>
9#include <QtCore/QThread>
10
11QGC_LOGGING_CATEGORY(ParameterMetaDataLog, "FirmwarePlugin.ParameterMetaData")
12
13const FactMetaData::DefineMap_t ParameterMetaData::kEmptyDefines;
14
16 : QObject(parent)
17{
18 qCDebug(ParameterMetaDataLog) << this;
19}
20
22{
23 qCDebug(ParameterMetaDataLog) << this;
24}
25
26void ParameterMetaData::loadParameterFactMetaDataFile(const QString &metaDataFile)
27{
28 Q_ASSERT(QThread::currentThread() == thread());
29 if (Q_UNLIKELY(QThread::currentThread() != thread())) {
30 qCCritical(ParameterMetaDataLog) << "loadParameterFactMetaDataFile called from wrong thread";
31 return;
32 }
33
35 return;
36 }
37
38 qCDebug(ParameterMetaDataLog) << "Loading parameter meta data:" << metaDataFile;
39
40 QJsonDocument doc;
41 QString errorString;
42 if (!JsonParsing::isJsonFile(metaDataFile, doc, errorString)) {
43 qCWarning(ParameterMetaDataLog) << "Unable to open parameter meta data file:" << metaDataFile << errorString;
44 return;
45 }
46 if (!doc.isObject()) {
47 qCWarning(ParameterMetaDataLog) << "JSON root is not an object:" << metaDataFile;
48 return;
49 }
50
52 parseParameterJson(doc.object());
53}
54
56{
57 Q_ASSERT(QThread::currentThread() == thread());
58 if (Q_UNLIKELY(QThread::currentThread() != thread())) {
59 qCCritical(ParameterMetaDataLog) << "getMetaDataForFact called from wrong thread";
60 return nullptr;
61 }
62
63 if (FactMetaData *cached = _cachedMetaData.value(name)) {
64 return cached;
65 }
66
67 FactMetaData *metaData = _lookupMetaData(name, type);
68 if (!metaData) {
69 metaData = _createDefaultMetaData(name, type);
70 }
71
72 _postProcessMetaData(name, metaData);
73 _cachedMetaData[name] = metaData;
74 return metaData;
75}
76
77QVersionNumber ParameterMetaData::versionFromJsonData(const QByteArray &jsonData)
78{
79 return versionFromJsonData(jsonData, nullptr);
80}
81
82QVersionNumber ParameterMetaData::versionFromJsonData(const QByteArray &jsonData, bool *validJson)
83{
84 QJsonDocument doc;
85 QString errorString;
86 const bool parsed = JsonParsing::isJsonFile(jsonData, doc, errorString);
87
88 if (validJson) {
89 *validJson = parsed;
90 }
91
92 if (!parsed) {
93 qCDebug(ParameterMetaDataLog) << "JSON parse error extracting version:" << errorString;
94 return {};
95 }
96
97 const QJsonObject root = doc.object();
98
99 // Only honour explicit parameter-catalog version stamps.
100 // The top-level "version" key is a schema version (e.g. PX4 JSON
101 // always has "version":1) and must NOT be conflated with the
102 // parameter-set version used for cache selection.
103 const int major = root.value(u"parameter_version_major").toInt(-1);
104 const int minor = root.value(u"parameter_version_minor").toInt(0);
105 if (major >= 0) {
106 return QVersionNumber(major, minor);
107 }
108
109 return {};
110}
111
112QVersionNumber ParameterMetaData::versionFromMetaDataFile(const QString &metaDataFile)
113{
114 QString errorString;
115 const QByteArray data = QGCFileHelper::readFile(metaDataFile, &errorString);
116 if (data.isEmpty()) {
117 qCWarning(ParameterMetaDataLog) << "Failed to read parameter meta data file:" << metaDataFile << errorString;
118 return {};
119 }
120
121 return versionFromJsonData(data);
122}
123
124QVersionNumber ParameterMetaData::versionFromFileName(const QString &fileName)
125{
126 static const QRegularExpression regex(QStringLiteral("\\.(\\d+)\\.(\\d+)\\.json$"));
127 const QRegularExpressionMatch match = regex.match(fileName);
128 if (match.hasMatch()) {
129 return QVersionNumber(match.captured(1).toInt(), match.captured(2).toInt());
130 }
131 return {};
132}
133
135{
136 Q_UNUSED(name)
137 Q_UNUSED(type)
138 return nullptr;
139}
140
142{
143 Q_UNUSED(name)
144 return new FactMetaData(type, this);
145}
146
147void ParameterMetaData::_postProcessMetaData(const QString &name, FactMetaData *metaData)
148{
149 Q_UNUSED(name)
150 Q_UNUSED(metaData)
151}
152
153bool ParameterMetaData::setRawConvertedValue(FactMetaData *metaData, const QString &rawText, void (FactMetaData::*setter)(const QVariant &))
154{
155 QVariant converted;
156 QString errorString;
157 if (metaData->convertAndValidateRaw(rawText, false, converted, errorString)) {
158 (metaData->*setter)(converted);
159 return true;
160 }
161 qCDebug(ParameterMetaDataLog) << "Invalid value for" << metaData->name() << "raw:" << rawText << "error:" << errorString;
162 return false;
163}
164
165void ParameterMetaData::setEnumFromPairs(FactMetaData *metaData, const QList<ValueDescPair> &pairs)
166{
167 QStringList enumStrings;
168 QVariantList enumValues;
169
170 for (const auto &[code, description] : pairs) {
171 QVariant enumValue;
172 QString errorString;
173 if (metaData->convertAndValidateRaw(code, false, enumValue, errorString)) {
174 enumValues << enumValue;
175 enumStrings << description;
176 } else {
177 qCWarning(ParameterMetaDataLog) << "Skipping invalid enum value for" << metaData->name() << "code:" << code << "error:" << errorString;
178 }
179 }
180
181 if (!enumStrings.isEmpty()) {
182 metaData->setEnumInfo(enumStrings, enumValues);
183 }
184}
185
186void ParameterMetaData::setBitmaskFromPairs(FactMetaData *metaData, const QList<ValueDescPair> &pairs)
187{
188 QStringList bitmaskStrings;
189 QVariantList bitmaskValues;
190
191 for (const auto &[bitIndexStr, description] : pairs) {
192 bool ok = false;
193 const uint bitIndex = bitIndexStr.toUInt(&ok);
194 if (!ok) {
195 qCWarning(ParameterMetaDataLog) << "Skipping invalid bitmask index for" << metaData->name() << "value:" << bitIndexStr;
196 continue;
197 }
198 if (bitIndex >= 64) {
199 qCWarning(ParameterMetaDataLog) << "Skipping out-of-range bitmask index for" << metaData->name() << "bit:" << bitIndex;
200 continue;
201 }
202
203 const QVariant rawValue = QVariant::fromValue(static_cast<quint64>(1ull << bitIndex));
204 QVariant bitmaskValue;
205 QString errorString;
206 if (metaData->convertAndValidateRaw(rawValue, false, bitmaskValue, errorString)) {
207 bitmaskValues << bitmaskValue;
208 bitmaskStrings << description;
209 } else {
210 qCWarning(ParameterMetaDataLog) << "Skipping invalid bitmask value for" << metaData->name() << "bit:" << bitIndex << "error:" << errorString;
211 }
212 }
213
214 if (!bitmaskStrings.isEmpty()) {
215 metaData->setBitmaskInfo(bitmaskStrings, bitmaskValues);
216 }
217}
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool convertAndValidateRaw(const QVariant &rawValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
void setEnumInfo(const QStringList &strings, const QVariantList &values)
void setBitmaskInfo(const QStringList &strings, const QVariantList &values)
QString name() const
virtual FactMetaData * _createDefaultMetaData(const QString &name, FactMetaData::ValueType_t type)
virtual void parseParameterJson(const QJsonObject &json)=0
static void setBitmaskFromPairs(FactMetaData *metaData, const QList< ValueDescPair > &pairs)
static QVersionNumber versionFromMetaDataFile(const QString &metaDataFile)
static QVersionNumber versionFromJsonData(const QByteArray &jsonData)
static bool setRawConvertedValue(FactMetaData *metaData, const QString &rawText, void(FactMetaData::*setter)(const QVariant &))
FactMetaData::NameToMetaDataMap_t _cachedMetaData
FactMetaData * getMetaDataForFact(const QString &name, FactMetaData::ValueType_t type)
static void setEnumFromPairs(FactMetaData *metaData, const QList< ValueDescPair > &pairs)
void loadParameterFactMetaDataFile(const QString &metaDataFile)
virtual FactMetaData * _lookupMetaData(const QString &name, FactMetaData::ValueType_t type)
virtual void _postProcessMetaData(const QString &name, FactMetaData *metaData)
static QVersionNumber versionFromFileName(const QString &fileName)
~ParameterMetaData() override
bool isJsonFile(const QByteArray &bytes, QJsonDocument &jsonDoc, QString &errorString)
Determines whether an in-memory byte buffer contains parseable JSON content.
QByteArray readFile(const QString &filePath, QString *errorString, qint64 maxBytes)