QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
CompInfoParam.cc
Go to the documentation of this file.
1#include "CompInfoParam.h"
2#include "JsonHelper.h"
3#include "JsonParsing.h"
4#include "FactMetaData.h"
5#include "FirmwarePlugin.h"
7#include "QGCApplication.h"
9#include "Vehicle.h"
10
11#include <QtCore/QJsonDocument>
12#include <QtCore/QJsonArray>
13#include <QtCore/QRegularExpression>
14#include <QtCore/QRegularExpressionMatch>
15#include <QtCore/QDir>
16
17QGC_LOGGING_CATEGORY(CompInfoParamLog, "ComponentInformation.CompInfoParam")
18
19CompInfoParam::CompInfoParam(uint8_t compId_, Vehicle* vehicle_, QObject* parent)
20 : CompInfo(COMP_METADATA_TYPE_PARAMETER, compId_, vehicle_, parent)
21{
22
23}
24
25void CompInfoParam::setJson(const QString& metadataJsonFileName)
26{
27 qCDebug(CompInfoParamLog) << "setJson: metadataJsonFileName" << metadataJsonFileName;
28
29 if (metadataJsonFileName.isEmpty()) {
30 // This will fall back to using the old FirmwarePlugin mechanism for parameter meta data.
31 // In this case paramter metadata is loaded through the _parameterMajorVersionKnown call which happens after parameter are downloaded
32 return;
33 }
34
35 QString errorString;
36 QJsonDocument jsonDoc;
37
38 _noJsonMetadata = false;
39
40 if (!JsonParsing::isJsonFile(metadataJsonFileName, jsonDoc, errorString)) {
41 qCWarning(CompInfoParamLog) << "Metadata json file open failed: compid:" << compId << errorString;
42 return;
43 }
44 QJsonObject jsonObj = jsonDoc.object();
45
46 QList<JsonHelper::KeyValidateInfo> keyInfoList = {
47 { JsonHelper::jsonVersionKey, QJsonValue::Double, true },
48 { _jsonParametersKey, QJsonValue::Array, true },
49 };
50 if (!JsonHelper::validateKeys(jsonObj, keyInfoList, errorString)) {
51 qCWarning(CompInfoParamLog) << "Metadata json validation failed: compid:" << compId << errorString;
52 return;
53 }
54
55 int version = jsonObj[JsonHelper::jsonVersionKey].toInt();
56 if (version != 1) {
57 qCWarning(CompInfoParamLog) << "Metadata json unsupported version" << version;
58 return;
59 }
60
61 QJsonArray rgParameters = jsonObj[_jsonParametersKey].toArray();
62 for (QJsonValue parameterValue: rgParameters) {
63 QMap<QString, QString> emptyDefineMap;
64
65 if (!parameterValue.isObject()) {
66 qCWarning(CompInfoParamLog) << "Metadata json read failed: compid:" << compId << "parameters array contains non-object";
67 return;
68 }
69
70 FactMetaData* newMetaData = FactMetaData::createFromJsonObject(parameterValue.toObject(), emptyDefineMap, this);
71
72 if (newMetaData->name().contains(_indexedNameTag)) {
73 _indexedNameMetaDataList.append(RegexFactMetaDataPair_t(newMetaData->name(), newMetaData));
74 } else {
75 _nameToMetaDataMap[newMetaData->name()] = newMetaData;
76 }
77 }
78}
79
81{
82 FactMetaData* factMetaData = nullptr;
83
84 if (_noJsonMetadata) {
85 QObject* opaqueMetaData = _getOpaqueParameterMetaData();
86 if (opaqueMetaData) {
87 factMetaData = vehicle->firmwarePlugin()->_getMetaDataForFact(opaqueMetaData, name, valueType, vehicle->vehicleType());
88 }
89 }
90
91 if (!factMetaData) {
92 if (_nameToMetaDataMap.contains(name)) {
93 factMetaData = _nameToMetaDataMap[name];
94 } else {
95 // We didn't get any direct matches. Try an indexed name.
96 for (int i=0; i<_indexedNameMetaDataList.count(); i++) {
97 const RegexFactMetaDataPair_t& pair = _indexedNameMetaDataList[i];
98
99 QString indexedName = pair.first;
100 const QString indexedRegex("(\\d+)");
101 indexedName.replace(_indexedNameTag, indexedRegex);
102
103 const QRegularExpression regex(indexedName);
104 const QRegularExpressionMatch match = regex.match(name);
105
106 const QStringList captured = match.capturedTexts();
107 if (captured.count() == 2) {
108 factMetaData = new FactMetaData(*pair.second, this);
109 factMetaData->setName(name);
110
111 QString shortDescription = factMetaData->shortDescription();
112 shortDescription.replace(_indexedNameTag, captured[1]);
113 factMetaData->setShortDescription(shortDescription);
114 QString longDescription = factMetaData->shortDescription();
115 longDescription.replace(_indexedNameTag, captured[1]);
116 factMetaData->setLongDescription(longDescription);
117 }
118 }
119
120 if (!factMetaData) {
121 factMetaData = new FactMetaData(valueType, this);
122 int i = name.indexOf("_");
123 if (i > 0) {
124 factMetaData->setGroup(name.left(i));
125 }
126 if (compId != MAV_COMP_ID_AUTOPILOT1) {
127 factMetaData->setCategory(tr("Component %1").arg(compId));
128 }
129 }
130 _nameToMetaDataMap[name] = factMetaData;
131 }
132 }
133
134 return factMetaData;
135}
136
137FirmwarePlugin* CompInfoParam::_anyVehicleTypeFirmwarePlugin(MAV_AUTOPILOT firmwareType)
138{
139 const QGCMAVLink::FirmwareClass_t firmwareClass = QGCMAVLink::firmwareClass(firmwareType);
140 const QList<QGCMAVLink::VehicleClass_t> supportedClasses = FirmwarePluginManager::instance()->supportedVehicleClasses(firmwareClass);
141 MAV_TYPE anySupportedVehicleType;
142 if (!supportedClasses.isEmpty()) {
143 anySupportedVehicleType = QGCMAVLink::vehicleClassToMavType(supportedClasses[0]);
144 } else {
145 anySupportedVehicleType = MAV_TYPE_GENERIC;
146 }
147 return FirmwarePluginManager::instance()->firmwarePluginForAutopilot(firmwareType, anySupportedVehicleType);
148}
149
150QString CompInfoParam::_parameterMetaDataFile(Vehicle* vehicle, MAV_AUTOPILOT firmwareType, int& majorVersion, int& minorVersion)
151{
152 bool cacheHit = false;
153 int wantedMajorVersion = 1;
154 FirmwarePlugin* fwPlugin = _anyVehicleTypeFirmwarePlugin(firmwareType);
155
156 if (firmwareType != MAV_AUTOPILOT_PX4) {
157 return fwPlugin->_internalParameterMetaDataFile(vehicle);
158 } else {
159 // Only PX4 support the old style cached metadata
160 QSettings settings;
161 QDir cacheDir = QFileInfo(settings.fileName()).dir();
162
163 // First look for a direct cache hit
164 int cacheMinorVersion = 0;
165 int cacheMajorVersion = 0;
166 QFile cacheFile(cacheDir.filePath(QString("%1.%2.%3.xml").arg(_cachedMetaDataFilePrefix).arg(firmwareType).arg(wantedMajorVersion)));
167 if (cacheFile.exists()) {
168 fwPlugin->_getParameterMetaDataVersionInfo(cacheFile.fileName(), cacheMajorVersion, cacheMinorVersion);
169 if (wantedMajorVersion != cacheMajorVersion) {
170 qWarning() << "Parameter meta data cache corruption:" << cacheFile.fileName() << "major version does not match file name" << "actual:excepted" << cacheMajorVersion << wantedMajorVersion;
171 } else {
172 qCDebug(CompInfoParamLog) << "Direct cache hit on file:major:minor" << cacheFile.fileName() << cacheMajorVersion << cacheMinorVersion;
173 cacheHit = true;
174 }
175 }
176
177 if (!cacheHit) {
178 // No direct hit, look for lower param set version
179 const QString wildcard = QString("%1.%2.*.xml").arg(_cachedMetaDataFilePrefix).arg(firmwareType);
180 const QStringList cacheHits = cacheDir.entryList(QStringList(wildcard), QDir::Files, QDir::Name);
181
182 // Find the highest major version number which is below the vehicles major version number
183 int cacheHitIndex = -1;
184 cacheMajorVersion = -1;
185
186 const QString pattern = QString("%1\\.%2\\.(\\d*)\\.xml").arg(_cachedMetaDataFilePrefix).arg(firmwareType);
187 const QRegularExpression regex(pattern);
188 for (int i = 0; i < cacheHits.count(); i++) {
189 const QRegularExpressionMatch match = regex.match(cacheHits[i]);
190 if (match.hasMatch() && match.lastCapturedIndex() == 0) {
191 const int matchedMajorVersion = match.captured(1).toInt();
192 if ((matchedMajorVersion > cacheMajorVersion) && (matchedMajorVersion < wantedMajorVersion)) {
193 cacheMajorVersion = matchedMajorVersion;
194 cacheHitIndex = i;
195 }
196 }
197 }
198
199 if (cacheHitIndex != -1) {
200 // We have a cache hit on a lower major version, read minor version as well
201 int cacheFileMajorVersion;
202 cacheFile.setFileName(cacheDir.filePath(cacheHits[cacheHitIndex]));
203 fwPlugin->_getParameterMetaDataVersionInfo(cacheFile.fileName(), cacheFileMajorVersion, cacheMinorVersion);
204 if (cacheFileMajorVersion != cacheMajorVersion) {
205 qWarning() << "Parameter meta data cache corruption:" << cacheFile.fileName() << "major version does not match file name" << "actual:excepted" << cacheFileMajorVersion << cacheMajorVersion;
206 cacheHit = false;
207 } else {
208 qCDebug(CompInfoParamLog) << "Indirect cache hit on file:major:minor:want" << cacheFile.fileName() << cacheMajorVersion << cacheMinorVersion << wantedMajorVersion;
209 cacheHit = true;
210 }
211 }
212 }
213
214 int internalMinorVersion, internalMajorVersion;
215 QString internalMetaDataFile = fwPlugin->_internalParameterMetaDataFile(vehicle);
216 fwPlugin->_getParameterMetaDataVersionInfo(internalMetaDataFile, internalMajorVersion, internalMinorVersion);
217 qCDebug(CompInfoParamLog) << "Internal metadata file:major:minor" << internalMetaDataFile << internalMajorVersion << internalMinorVersion;
218 if (cacheHit) {
219 // Cache hit is available, we need to check if internal meta data is a better match, if so use internal version
220 if (internalMajorVersion == wantedMajorVersion) {
221 if (cacheMajorVersion == wantedMajorVersion) {
222 // Both internal and cache are direct hit on major version, Use higher minor version number
223 cacheHit = cacheMinorVersion > internalMinorVersion;
224 } else {
225 // Direct internal hit, but not direct hit in cache, use internal
226 cacheHit = false;
227 }
228 } else {
229 if (cacheMajorVersion == wantedMajorVersion) {
230 // Direct hit on cache, no direct hit on internal, use cache
231 cacheHit = true;
232 } else {
233 // No direct hit anywhere, use internal
234 cacheHit = false;
235 }
236 }
237 }
238
239 QString metaDataFile;
240 if (cacheHit && !qgcApp()->runningUnitTests()) {
241 majorVersion = cacheMajorVersion;
242 minorVersion = cacheMinorVersion;
243 metaDataFile = cacheFile.fileName();
244 } else {
245 majorVersion = internalMajorVersion;
246 minorVersion = internalMinorVersion;
247 metaDataFile = internalMetaDataFile;
248 }
249 qCDebug(CompInfoParamLog) << "_parameterMetaDataFile returning file:major:minor" << metaDataFile << majorVersion << minorVersion;
250
251 return metaDataFile;
252 }
253}
254
255void CompInfoParam::_cachePX4MetaDataFile(const QString& metaDataFile)
256{
257 FirmwarePlugin* plugin = _anyVehicleTypeFirmwarePlugin(MAV_AUTOPILOT_PX4);
258
259 int newMajorVersion, newMinorVersion;
260 plugin->_getParameterMetaDataVersionInfo(metaDataFile, newMajorVersion, newMinorVersion);
261 if (newMajorVersion != 1) {
262 newMajorVersion = 1;
263 qgcApp()->showAppMessage(tr("Internal Error: Parameter MetaData major must be 1"));
264 }
265 qCDebug(CompInfoParamLog) << "ParameterManager::cacheMetaDataFile file:major;minor" << metaDataFile << newMajorVersion << newMinorVersion;
266
267 // Find the cache hit closest to this new file
268 int cacheMajorVersion, cacheMinorVersion;
269 QString cacheHit = _parameterMetaDataFile(nullptr, MAV_AUTOPILOT_PX4, cacheMajorVersion, cacheMinorVersion);
270 qCDebug(CompInfoParamLog) << "ParameterManager::cacheMetaDataFile cacheHit file:firmware:major;minor" << cacheHit << cacheMajorVersion << cacheMinorVersion;
271
272 bool cacheNewFile = false;
273 if (cacheHit.isEmpty()) {
274 // No cache hits, store the new file
275 cacheNewFile = true;
276 } else if (cacheMajorVersion == newMajorVersion) {
277 // Direct hit on major version in cache:
278 // Cache new file if newer/equal minor version. We cache if equal to allow flashing test builds with new parameter metadata.
279 // Also delete older cache file.
280 if (newMinorVersion >= cacheMinorVersion) {
281 cacheNewFile = true;
282 QFile::remove(cacheHit);
283 }
284 } else {
285 // Indirect hit in cache, store new file
286 cacheNewFile = true;
287 }
288
289 if (cacheNewFile) {
290 // Cached files are stored in settings location. Copy from current file to cache naming.
291
292 QSettings settings;
293 QDir cacheDir = QFileInfo(settings.fileName()).dir();
294 QFile cacheFile(cacheDir.filePath(QString("%1.%2.%3.xml").arg(_cachedMetaDataFilePrefix).arg(MAV_AUTOPILOT_PX4).arg(newMajorVersion)));
295 qCDebug(CompInfoParamLog) << "ParameterManager::cacheMetaDataFile caching file:" << cacheFile.fileName();
296 QFile newFile(metaDataFile);
297 newFile.copy(cacheFile.fileName());
298 }
299}
300
301QObject* CompInfoParam::_getOpaqueParameterMetaData(void)
302{
303 if (!_noJsonMetadata) {
304 qWarning() << "CompInfoParam::_getOpaqueParameterMetaData _noJsonMetadata == false";
305 }
306
307 if (!_opaqueParameterMetaData && compId == MAV_COMP_ID_AUTOPILOT1) {
308 // Load best parameter meta data set
309 int majorVersion, minorVersion;
310 QString metaDataFile = _parameterMetaDataFile(vehicle, vehicle->firmwareType(), majorVersion, minorVersion);
311 qCDebug(CompInfoParamLog) << "Loading meta data the old way file" << metaDataFile;
312 _opaqueParameterMetaData = vehicle->firmwarePlugin()->_loadParameterMetaData(metaDataFile);
313 }
314
315 return _opaqueParameterMetaData;
316}
#define qgcApp()
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void setJson(const QString &metadataJsonFileName) override
static void _cachePX4MetaDataFile(const QString &metaDataFile)
FactMetaData * factMetaDataForName(const QString &name, FactMetaData::ValueType_t valueType)
Base class for all CompInfo types.
Definition CompInfo.h:14
Vehicle *const vehicle
Definition CompInfo.h:36
const uint8_t compId
Definition CompInfo.h:37
static FactMetaData * createFromJsonObject(const QJsonObject &json, const QMap< QString, QString > &defineMap, QObject *metaDataParent)
void setShortDescription(const QString &shortDescription)
void setName(const QString &name)
void setLongDescription(const QString &longDescription)
QString shortDescription() const
void setCategory(const QString &category)
QString name() const
void setGroup(const QString &group)
FirmwarePlugin * firmwarePluginForAutopilot(MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType)
QList< QGCMAVLink::VehicleClass_t > supportedVehicleClasses(QGCMAVLink::FirmwareClass_t firmwareClass)
Returns the list of supported vehicle types for the specified firmware.
static FirmwarePluginManager * instance()
virtual QString _internalParameterMetaDataFile(const Vehicle *) const
virtual FactMetaData * _getMetaDataForFact(QObject *, const QString &, FactMetaData::ValueType_t, MAV_TYPE) const
virtual void _getParameterMetaDataVersionInfo(const QString &metaDataFile, int &majorVersion, int &minorVersion) const
virtual QObject * _loadParameterMetaData(const QString &)
MAV_TYPE vehicleType() const
Definition Vehicle.h:428
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
Definition Vehicle.h:444
MAV_AUTOPILOT firmwareType() const
Definition Vehicle.h:427
constexpr const char * jsonVersionKey
Definition JsonHelper.h:109
bool validateKeys(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
bool isJsonFile(const QByteArray &bytes, QJsonDocument &jsonDoc, QString &errorString)
Determines whether an in-memory byte buffer contains parseable JSON content.