地面站终端 App
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

315 lines
14 KiB

/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "CompInfoParam.h"
#include "JsonHelper.h"
#include "FactMetaData.h"
#include "FirmwarePlugin.h"
#include "FirmwarePluginManager.h"
#include "QGCApplication.h"
#include <QStandardPaths>
#include <QJsonDocument>
#include <QJsonArray>
QGC_LOGGING_CATEGORY(CompInfoParamLog, "CompInfoParamLog")
const char* CompInfoParam::_jsonParametersKey = "parameters";
const char* CompInfoParam::_cachedMetaDataFilePrefix = "ParameterFactMetaData";
const char* CompInfoParam::_indexedNameTag = "{n}";
CompInfoParam::CompInfoParam(uint8_t compId, Vehicle* vehicle, QObject* parent)
: CompInfo(COMP_METADATA_TYPE_PARAMETER, compId, vehicle, parent)
{
}
void CompInfoParam::setJson(const QString& metadataJsonFileName, const QString& translationJsonFileName)
{
qCDebug(CompInfoParamLog) << "setJson: metadataJsonFileName:translationJsonFileName" << metadataJsonFileName << translationJsonFileName;
if (metadataJsonFileName.isEmpty()) {
// This will fall back to using the old FirmwarePlugin mechanism for parameter meta data.
// In this case paramter metadata is loaded through the _parameterMajorVersionKnown call which happens after parameter are downloaded
return;
}
QString errorString;
QJsonDocument jsonDoc;
_noJsonMetadata = false;
if (!JsonHelper::isJsonFile(metadataJsonFileName, jsonDoc, errorString)) {
qCWarning(CompInfoParamLog) << "Metadata json file open failed: compid:" << compId << errorString;
return;
}
QJsonObject jsonObj = jsonDoc.object();
QList<JsonHelper::KeyValidateInfo> keyInfoList = {
{ JsonHelper::jsonVersionKey, QJsonValue::Double, true },
{ _jsonParametersKey, QJsonValue::Array, true },
};
if (!JsonHelper::validateKeys(jsonObj, keyInfoList, errorString)) {
qCWarning(CompInfoParamLog) << "Metadata json validation failed: compid:" << compId << errorString;
return;
}
int version = jsonObj[JsonHelper::jsonVersionKey].toInt();
if (version != 1) {
qCWarning(CompInfoParamLog) << "Metadata json unsupported version" << version;
return;
}
QJsonArray rgParameters = jsonObj[_jsonParametersKey].toArray();
for (QJsonValue parameterValue: rgParameters) {
QMap<QString, QString> emptyDefineMap;
if (!parameterValue.isObject()) {
qCWarning(CompInfoParamLog) << "Metadata json read failed: compid:" << compId << "parameters array contains non-object";
return;
}
FactMetaData* newMetaData = FactMetaData::createFromJsonObject(parameterValue.toObject(), emptyDefineMap, this);
if (newMetaData->name().contains(_indexedNameTag)) {
_indexedNameMetaDataList.append(RegexFactMetaDataPair_t(newMetaData->name(), newMetaData));
} else {
_nameToMetaDataMap[newMetaData->name()] = newMetaData;
}
}
}
FactMetaData* CompInfoParam::factMetaDataForName(const QString& name, FactMetaData::ValueType_t type)
{
FactMetaData* factMetaData = nullptr;
if (_noJsonMetadata) {
QObject* opaqueMetaData = _getOpaqueParameterMetaData();
if (opaqueMetaData) {
factMetaData = vehicle->firmwarePlugin()->_getMetaDataForFact(opaqueMetaData, name, type, vehicle->vehicleType());
}
}
if (!factMetaData) {
if (_nameToMetaDataMap.contains(name)) {
factMetaData = _nameToMetaDataMap[name];
} else {
// We didn't get any direct matches. Try an indexed name.
for (int i=0; i<_indexedNameMetaDataList.count(); i++) {
const RegexFactMetaDataPair_t& pair = _indexedNameMetaDataList[i];
QString indexedName = pair.first;
QString indexedRegex("(\\d+)");
indexedName.replace(_indexedNameTag, indexedRegex);
QRegularExpression regex(indexedName);
QRegularExpressionMatch match = regex.match(name);
QStringList captured = match.capturedTexts();
if (captured.count() == 2) {
factMetaData = new FactMetaData(*pair.second, this);
factMetaData->setName(name);
QString shortDescription = factMetaData->shortDescription();
shortDescription.replace(_indexedNameTag, captured[1]);
factMetaData->setShortDescription(shortDescription);
QString longDescription = factMetaData->shortDescription();
longDescription.replace(_indexedNameTag, captured[1]);
factMetaData->setLongDescription(longDescription);
}
}
if (!factMetaData) {
factMetaData = new FactMetaData(type, this);
int i = name.indexOf("_");
if (i > 0) {
factMetaData->setGroup(name.left(i));
}
if (compId != MAV_COMP_ID_AUTOPILOT1) {
factMetaData->setCategory(tr("Component %1").arg(compId));
}
}
_nameToMetaDataMap[name] = factMetaData;
}
}
return factMetaData;
}
FirmwarePlugin* CompInfoParam::_anyVehicleTypeFirmwarePlugin(MAV_AUTOPILOT firmwareType)
{
FirmwarePluginManager* pluginMgr = qgcApp()->toolbox()->firmwarePluginManager();
MAV_TYPE anySupportedVehicleType = QGCMAVLink::vehicleClassToMavType(pluginMgr->supportedVehicleClasses(QGCMAVLink::firmwareClass(firmwareType))[0]);
return pluginMgr->firmwarePluginForAutopilot(firmwareType, anySupportedVehicleType);
}
QString CompInfoParam::_parameterMetaDataFile(Vehicle* vehicle, MAV_AUTOPILOT firmwareType, int& majorVersion, int& minorVersion)
{
bool cacheHit = false;
int wantedMajorVersion = 1;
FirmwarePlugin* fwPlugin = _anyVehicleTypeFirmwarePlugin(firmwareType);
if (firmwareType != MAV_AUTOPILOT_PX4) {
return fwPlugin->_internalParameterMetaDataFile(vehicle);
} else {
// Only PX4 support the old style cached metadata
QSettings settings;
QDir cacheDir = QFileInfo(settings.fileName()).dir();
// First look for a direct cache hit
int cacheMinorVersion, cacheMajorVersion;
QFile cacheFile(cacheDir.filePath(QString("%1.%2.%3.xml").arg(_cachedMetaDataFilePrefix).arg(firmwareType).arg(wantedMajorVersion)));
if (cacheFile.exists()) {
fwPlugin->_getParameterMetaDataVersionInfo(cacheFile.fileName(), cacheMajorVersion, cacheMinorVersion);
if (wantedMajorVersion != cacheMajorVersion) {
qWarning() << "Parameter meta data cache corruption:" << cacheFile.fileName() << "major version does not match file name" << "actual:excepted" << cacheMajorVersion << wantedMajorVersion;
} else {
qCDebug(CompInfoParamLog) << "Direct cache hit on file:major:minor" << cacheFile.fileName() << cacheMajorVersion << cacheMinorVersion;
cacheHit = true;
}
}
if (!cacheHit) {
// No direct hit, look for lower param set version
QString wildcard = QString("%1.%2.*.xml").arg(_cachedMetaDataFilePrefix).arg(firmwareType);
QStringList cacheHits = cacheDir.entryList(QStringList(wildcard), QDir::Files, QDir::Name);
// Find the highest major version number which is below the vehicles major version number
int cacheHitIndex = -1;
cacheMajorVersion = -1;
QRegExp regExp(QString("%1\\.%2\\.(\\d*)\\.xml").arg(_cachedMetaDataFilePrefix).arg(firmwareType));
for (int i=0; i< cacheHits.count(); i++) {
if (regExp.exactMatch(cacheHits[i]) && regExp.captureCount() == 1) {
int majorVersion = regExp.capturedTexts()[0].toInt();
if (majorVersion > cacheMajorVersion && majorVersion < wantedMajorVersion) {
cacheMajorVersion = majorVersion;
cacheHitIndex = i;
}
}
}
if (cacheHitIndex != -1) {
// We have a cache hit on a lower major version, read minor version as well
int majorVersion;
cacheFile.setFileName(cacheDir.filePath(cacheHits[cacheHitIndex]));
fwPlugin->_getParameterMetaDataVersionInfo(cacheFile.fileName(), majorVersion, cacheMinorVersion);
if (majorVersion != cacheMajorVersion) {
qWarning() << "Parameter meta data cache corruption:" << cacheFile.fileName() << "major version does not match file name" << "actual:excepted" << majorVersion << cacheMajorVersion;
cacheHit = false;
} else {
qCDebug(CompInfoParamLog) << "Indirect cache hit on file:major:minor:want" << cacheFile.fileName() << cacheMajorVersion << cacheMinorVersion << wantedMajorVersion;
cacheHit = true;
}
}
}
int internalMinorVersion, internalMajorVersion;
QString internalMetaDataFile = fwPlugin->_internalParameterMetaDataFile(vehicle);
fwPlugin->_getParameterMetaDataVersionInfo(internalMetaDataFile, internalMajorVersion, internalMinorVersion);
qCDebug(CompInfoParamLog) << "Internal metadata file:major:minor" << internalMetaDataFile << internalMajorVersion << internalMinorVersion;
if (cacheHit) {
// Cache hit is available, we need to check if internal meta data is a better match, if so use internal version
if (internalMajorVersion == wantedMajorVersion) {
if (cacheMajorVersion == wantedMajorVersion) {
// Both internal and cache are direct hit on major version, Use higher minor version number
cacheHit = cacheMinorVersion > internalMinorVersion;
} else {
// Direct internal hit, but not direct hit in cache, use internal
cacheHit = false;
}
} else {
if (cacheMajorVersion == wantedMajorVersion) {
// Direct hit on cache, no direct hit on internal, use cache
cacheHit = true;
} else {
// No direct hit anywhere, use internal
cacheHit = false;
}
}
}
QString metaDataFile;
if (cacheHit && !qgcApp()->runningUnitTests()) {
majorVersion = cacheMajorVersion;
minorVersion = cacheMinorVersion;
metaDataFile = cacheFile.fileName();
} else {
majorVersion = internalMajorVersion;
minorVersion = internalMinorVersion;
metaDataFile = internalMetaDataFile;
}
qCDebug(CompInfoParamLog) << "_parameterMetaDataFile returning file:major:minor" << metaDataFile << majorVersion << minorVersion;
return metaDataFile;
}
}
void CompInfoParam::_cachePX4MetaDataFile(const QString& metaDataFile)
{
FirmwarePlugin* plugin = _anyVehicleTypeFirmwarePlugin(MAV_AUTOPILOT_PX4);
int newMajorVersion, newMinorVersion;
plugin->_getParameterMetaDataVersionInfo(metaDataFile, newMajorVersion, newMinorVersion);
if (newMajorVersion != 1) {
newMajorVersion = 1;
qgcApp()->showAppMessage(tr("Internal Error: Parameter MetaData major must be 1"));
}
qCDebug(CompInfoParamLog) << "ParameterManager::cacheMetaDataFile file:major;minor" << metaDataFile << newMajorVersion << newMinorVersion;
// Find the cache hit closest to this new file
int cacheMajorVersion, cacheMinorVersion;
QString cacheHit = _parameterMetaDataFile(nullptr, MAV_AUTOPILOT_PX4, cacheMajorVersion, cacheMinorVersion);
qCDebug(CompInfoParamLog) << "ParameterManager::cacheMetaDataFile cacheHit file:firmware:major;minor" << cacheHit << cacheMajorVersion << cacheMinorVersion;
bool cacheNewFile = false;
if (cacheHit.isEmpty()) {
// No cache hits, store the new file
cacheNewFile = true;
} else if (cacheMajorVersion == newMajorVersion) {
// Direct hit on major version in cache:
// Cache new file if newer/equal minor version. We cache if equal to allow flashing test builds with new parameter metadata.
// Also delete older cache file.
if (newMinorVersion >= cacheMinorVersion) {
cacheNewFile = true;
QFile::remove(cacheHit);
}
} else {
// Indirect hit in cache, store new file
cacheNewFile = true;
}
if (cacheNewFile) {
// Cached files are stored in settings location. Copy from current file to cache naming.
QSettings settings;
QDir cacheDir = QFileInfo(settings.fileName()).dir();
QFile cacheFile(cacheDir.filePath(QString("%1.%2.%3.xml").arg(_cachedMetaDataFilePrefix).arg(MAV_AUTOPILOT_PX4).arg(newMajorVersion)));
qCDebug(CompInfoParamLog) << "ParameterManager::cacheMetaDataFile caching file:" << cacheFile.fileName();
QFile newFile(metaDataFile);
newFile.copy(cacheFile.fileName());
}
}
QObject* CompInfoParam::_getOpaqueParameterMetaData(void)
{
if (!_noJsonMetadata) {
qWarning() << "CompInfoParam::_getOpaqueParameterMetaData _noJsonMetadata == false";
}
if (!_opaqueParameterMetaData && compId == MAV_COMP_ID_AUTOPILOT1) {
// Load best parameter meta data set
int majorVersion, minorVersion;
QString metaDataFile = _parameterMetaDataFile(vehicle, vehicle->firmwareType(), majorVersion, minorVersion);
qCDebug(CompInfoParamLog) << "Loading meta data the old way file" << metaDataFile;
_opaqueParameterMetaData = vehicle->firmwarePlugin()->_loadParameterMetaData(metaDataFile);
}
return _opaqueParameterMetaData;
}