QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
SettingsManager.cc
Go to the documentation of this file.
1#include "SettingsManager.h"
2
3#include <QtCore/QApplicationStatic>
4#include <QtCore/QRegularExpression>
5
8#include "AppMessages.h"
9#include "AppSettings.h"
10#include "AutoConnectSettings.h"
13#include "FlightMapSettings.h"
14#include "FlightModeSettings.h"
15#include "FlyViewSettings.h"
19#include "JsonParsing.h"
20#include "LogManagerSettings.h"
21#include "LogViewerSettings.h"
22#include "MapsSettings.h"
24#include "MavlinkSettings.h"
25#include "NTRIPSettings.h"
26#include "OfflineMapsSettings.h"
27#include "PlanViewSettings.h"
28#include "QGCCorePlugin.h"
29#include "QGCLoggingCategory.h"
30#include "RTKSettings.h"
31#include "RemoteIDSettings.h"
32#include "SettingsGroup.h"
33#include "UnitsSettings.h"
34#include "VideoSettings.h"
35#include "Viewer3DSettings.h"
36
37QGC_LOGGING_CATEGORY(SettingsManagerLog, "Utilities.SettingsManager")
38
39Q_APPLICATION_STATIC(SettingsManager, _settingsManagerInstance);
40
42 : QQmlPropertyMap(this, parent)
43{
44 qCDebug(SettingsManagerLog) << this;
45}
46
48{
49 qCDebug(SettingsManagerLog) << this;
50}
51
53{
54 return _settingsManagerInstance();
55}
56
58{
59 _unitsSettings = new UnitsSettings(this); // Must be first since AppSettings references it
60
61 _appSettings = new AppSettings(this);
62 _loadSettingsFiles();
63
64 _autoConnectSettings = new AutoConnectSettings(this);
65 _batteryIndicatorSettings = new BatteryIndicatorSettings(this);
66 _mavlinkActionsSettings = new MavlinkActionsSettings(this);
67 _firmwareUpgradeSettings = new FirmwareUpgradeSettings(this);
68 _flightMapSettings = new FlightMapSettings(this);
69 _flightModeSettings = new FlightModeSettings(this);
70 _flyViewSettings = new FlyViewSettings(this);
71 _gimbalControllerSettings = new GimbalControllerSettings(this);
72 _mapsSettings = new MapsSettings(this);
73 _offlineMapsSettings = new OfflineMapsSettings(this);
74 _planViewSettings = new PlanViewSettings(this);
75 _remoteIDSettings = new RemoteIDSettings(this);
76 _rtkSettings = new RTKSettings(this);
77 _gpsCorrectionSettings = new GPSCorrectionSettings(this);
78 _ntripSettings = new NTRIPSettings(this);
79 _videoSettings = new VideoSettings(this);
80 _mavlinkSettings = new MavlinkSettings(this);
81 _joystickManagerSettings = new JoystickManagerSettings(this);
82 _logManagerSettings = new LogManagerSettings(this);
83 _logViewerSettings = new LogViewerSettings(this);
84 _viewer3DSettings = new Viewer3DSettings(this);
85 _adsbVehicleManagerSettings = new ADSBVehicleManagerSettings(this);
86 _apmMavlinkStreamRateSettings = new APMMavlinkStreamRateSettings(this);
87
89}
90
91void SettingsManager::registerCustomSettingsGroup(const QString &accessorName, SettingsGroup *group)
92{
93 // Must be a valid QML identifier or generated pages can't resolve the group via dot notation
94 static const QRegularExpression validAccessorRe(QStringLiteral("^[a-z_][A-Za-z0-9_]*$"));
95 if (!validAccessorRe.match(accessorName).hasMatch() || !group) {
96 qCWarning(SettingsManagerLog) << "registerCustomSettingsGroup: invalid accessor name or null group" << accessorName;
97 delete group;
98 return;
99 }
100 if (contains(accessorName)) {
101 qCWarning(SettingsManagerLog) << "registerCustomSettingsGroup: accessor already registered" << accessorName;
102 // Re-registering the stored group itself must not destroy it
103 if (group != value(accessorName).value<QObject*>()) {
104 delete group;
105 }
106 return;
107 }
108 if (staticMetaObject.indexOfProperty(accessorName.toUtf8().constData()) != -1) {
109 qCWarning(SettingsManagerLog) << "registerCustomSettingsGroup: accessor collides with a built-in settings group" << accessorName;
110 delete group;
111 return;
112 }
113
114 group->setParent(this);
115 insert(accessorName, QVariant::fromValue<QObject*>(group));
116 qCDebug(SettingsManagerLog) << "Registered custom settings group" << accessorName;
117}
118
119ADSBVehicleManagerSettings *SettingsManager::adsbVehicleManagerSettings() const { return _adsbVehicleManagerSettings; }
120APMMavlinkStreamRateSettings *SettingsManager::apmMavlinkStreamRateSettings() const { return _apmMavlinkStreamRateSettings; }
121AppSettings *SettingsManager::appSettings() const { return _appSettings; }
122AutoConnectSettings *SettingsManager::autoConnectSettings() const { return _autoConnectSettings; }
123BatteryIndicatorSettings *SettingsManager::batteryIndicatorSettings() const { return _batteryIndicatorSettings; }
124MavlinkActionsSettings *SettingsManager::mavlinkActionsSettings() const { return _mavlinkActionsSettings; }
125FirmwareUpgradeSettings *SettingsManager::firmwareUpgradeSettings() const { return _firmwareUpgradeSettings; }
126FlightMapSettings *SettingsManager::flightMapSettings() const { return _flightMapSettings; }
127FlightModeSettings *SettingsManager::flightModeSettings() const { return _flightModeSettings; }
128FlyViewSettings *SettingsManager::flyViewSettings() const { return _flyViewSettings; }
129GimbalControllerSettings *SettingsManager::gimbalControllerSettings() const { return _gimbalControllerSettings; }
130MapsSettings *SettingsManager::mapsSettings() const { return _mapsSettings; }
131OfflineMapsSettings *SettingsManager::offlineMapsSettings() const { return _offlineMapsSettings; }
132PlanViewSettings *SettingsManager::planViewSettings() const { return _planViewSettings; }
133RemoteIDSettings *SettingsManager::remoteIDSettings() const { return _remoteIDSettings; }
134RTKSettings *SettingsManager::rtkSettings() const { return _rtkSettings; }
135UnitsSettings *SettingsManager::unitsSettings() const { return _unitsSettings; }
136NTRIPSettings *SettingsManager::ntripSettings() const { return _ntripSettings; }
137
139{
140 return _gpsCorrectionSettings;
141}
142
143VideoSettings *SettingsManager::videoSettings() const { return _videoSettings; }
144MavlinkSettings *SettingsManager::mavlinkSettings() const { return _mavlinkSettings; }
145JoystickManagerSettings *SettingsManager::joystickManagerSettings() const { return _joystickManagerSettings; }
146LogManagerSettings *SettingsManager::logManagerSettings() const { return _logManagerSettings; }
147LogViewerSettings *SettingsManager::logViewerSettings() const { return _logViewerSettings; }
148Viewer3DSettings *SettingsManager::viewer3DSettings() const { return _viewer3DSettings; }
149
150void SettingsManager::_loadSettingsFiles()
151{
152 // Settings files can be found in the settingsSavePath() directory
153 // Settings files are json files which end in the settingsFileExtension extension
154 // The format for a settings file is:
155 // {
156 // "version": 1,
157 // "fileType": "Settings",
158 // "groups": {
159 // "groupName": {
160 // "settingName": {
161 // "forceRawValue": <value>, // Forces the rawValue for this setting to the specific value
162 // any FactMetaData json keys except for name and type,
163 // ...
164 // },
165 // "groupName": {
166 // ...
167 // }
168 // }
169 // }
170
171 QDir settingsDir(_appSettings->settingsSavePath());
172 if (!settingsDir.exists()) {
173 qCWarning(SettingsManagerLog) << "Settings directory does not exist:" << settingsDir.absolutePath();
174 return;
175 }
176
177 QStringList settingsFiles = settingsDir.entryList(QStringList() << QString("*.%1").arg(_appSettings->settingsFileExtension), QDir::Files);
178 for (const QString &fileName : settingsFiles) {
179 QFileInfo fileInfo(settingsDir, fileName);
180 if (!fileInfo.isFile()) continue;
181
182 // Load the settings file
183 qCDebug(SettingsManagerLog) << "Loading settings file:" << fileInfo.absoluteFilePath();
184
185 QJsonDocument jsonDoc;
186 QString errorString;
187 if (!JsonParsing::isJsonFile(fileInfo.absoluteFilePath(), jsonDoc, errorString)) {
188 qCWarning(SettingsManagerLog) << "Failed to load settings file:" << fileInfo.absoluteFilePath() << errorString;
189 continue;
190 }
191
192 QJsonObject jsonObject = jsonDoc.object();
193
194 // Validate the settings file
195 int version;
196 if (!JsonParsing::validateInternalQGCJsonFile(jsonObject, "Settings", 1, 1, version, errorString)) {
197 qCWarning(SettingsManagerLog) << "Settings file failed validation:" << fileInfo.absoluteFilePath() << errorString;
198 continue;
199 }
200
201 // Validate the remainder of the file
202
203 // groups key is an object
204 static const QList<JsonParsing::KeyValidateInfo> keyInfoList = {
205 { kJsonGroupsObjectKey, QJsonValue::Object, true },
206 };
207 if (!JsonParsing::validateKeys(jsonObject, keyInfoList, errorString)) {
208 qCWarning(SettingsManagerLog) << "Settings file incorrect format:" << fileInfo.absoluteFilePath() << errorString;
209 continue;
210 }
211
212 auto groupsObject = jsonObject[kJsonGroupsObjectKey].toObject();
213 for (const QString &groupName : groupsObject.keys()) {
214 qCDebug(SettingsManagerLog) << " Loading settings group:" << groupName;
215
216 const QJsonValue &groupValue = groupsObject[groupName];
217 if (!groupValue.isObject()) {
218 qCWarning(SettingsManagerLog) << "Settings file incorrect format, group is not an object:" << fileInfo.absoluteFilePath()
219 << groupName;
220 continue;
221 }
222
223 auto groupObject = groupValue.toObject();
224 for (const QString &settingName : groupObject.keys()) {
225 qCDebug(SettingsManagerLog) << " Loading settings:" << groupName << settingName;
226
227 if (!groupObject[settingName].isObject()) {
228 qCWarning(SettingsManagerLog) << "Settings file incorrect format, setting is not an object:" << fileInfo.absoluteFilePath()
229 << groupName << settingName;
230 continue;
231 }
232
233 // Store the setting overrides. Note that last one wins if there are multiple settings files with the same setting.
234 QJsonObject metaDataObject = groupObject[settingName].toObject();
235 _settingsFileOverrides[groupName][settingName] = metaDataObject;
236 }
237 }
238 }
239}
240
241void SettingsManager::adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData, bool &userVisible)
242{
243 userVisible = true; // By default all settings are visible
244
245 SettingsManager *settingsManager = SettingsManager::instance();
246 if (!settingsManager) {
247 qCWarning(SettingsManagerLog) << "SettingsManager instance not available";
248 return;
249 }
250
251 if (!QGC::runningUnitTests()) {
252 // Apply settings file overrides
253 const auto &groupOverrides = settingsManager->_settingsFileOverrides;
254 if (groupOverrides.contains(settingsGroup) && groupOverrides[settingsGroup].contains(metaData.name())) {
255 QJsonObject settingOverrideJsonObject = groupOverrides[settingsGroup][metaData.name()];
256
257 // We need to stuff in name and type so settingOverrideJsonObject can parse properly
258 settingOverrideJsonObject["name"] = metaData.name();
259 settingOverrideJsonObject["type"] = FactMetaData::typeToString(metaData.type());
260
261 qCDebug(SettingsManagerLog) << "Applying settings file override for" << settingsGroup << metaData.name();
262
263 // Strip SettingsManager-level keys which are not valid Fact metadata keys. Otherwise strict key
264 // validation in createFromJsonObject fails and returns default metadata, corrupting the overrides below.
265 QJsonObject factMetaDataJsonObject = settingOverrideJsonObject;
266 factMetaDataJsonObject.remove(kJsonVisibleKey);
267 factMetaDataJsonObject.remove(kJsonForceRawValueKey);
268
269 QScopedPointer<FactMetaData> overrideMetaData(FactMetaData::createFromJsonObject(factMetaDataJsonObject, {}, nullptr));
270
271 if (settingOverrideJsonObject.contains(kJsonForceRawValueKey) && settingOverrideJsonObject.contains(kJsonVisibleKey)) {
272 // A visible setting reads its value from QSettings which defeats forceRawValue
273 qCWarning(SettingsManagerLog) << "Ignoring settings file override which combines 'forceRawValue' and 'visible' keys:" << settingsGroup << metaData.name();
274 } else if (overrideMetaData->name() != metaData.name()) {
275 // createFromJsonObject failed validation (it already logged why) and returned default metadata.
276 // Don't apply any part of a bad override.
277 qCWarning(SettingsManagerLog) << "Ignoring settings file override which failed validation:" << settingsGroup << metaData.name();
278 } else {
279 // Apply Fact metadata overrides
280 for (const QString &metaDataName : factMetaDataJsonObject.keys()) {
281 if (metaDataName == FactMetaData::_defaultValueJsonKey) {
282 qCDebug(SettingsManagerLog) << " Setting default to" << overrideMetaData->rawDefaultValue();
283 metaData.setRawDefaultValue(overrideMetaData->rawDefaultValue());
284 } else if (metaDataName == FactMetaData::_minJsonKey) {
285 qCDebug(SettingsManagerLog) << " Setting min to" << overrideMetaData->rawMin();
286 metaData.setRawMin(overrideMetaData->rawMin());
287 } else if (metaDataName == FactMetaData::_maxJsonKey) {
288 qCDebug(SettingsManagerLog) << " Setting max to" << overrideMetaData->rawMax();
289 metaData.setRawMax(overrideMetaData->rawMax());
290 } else if (metaDataName == FactMetaData::_decimalPlacesJsonKey) {
291 qCDebug(SettingsManagerLog) << " Setting decimalPlaces to" << overrideMetaData->decimalPlaces();
292 metaData.setDecimalPlaces(overrideMetaData->decimalPlaces());
293 } else if (metaDataName == FactMetaData::_enumValuesJsonKey) {
294 qCDebug(SettingsManagerLog) << " Setting enumInfo to" << overrideMetaData->enumValues() << overrideMetaData->enumStrings();
295 metaData.setEnumInfo(overrideMetaData->enumStrings(), overrideMetaData->enumValues());
296 } else if (metaDataName == FactMetaData::_enumBitmaskArrayJsonKey) {
297 qCDebug(SettingsManagerLog) << " Setting bitmaskInfo to" << overrideMetaData->bitmaskValues() << overrideMetaData->bitmaskStrings();
298 metaData.setBitmaskInfo(overrideMetaData->bitmaskStrings(), overrideMetaData->bitmaskValues());
299 } else if (metaDataName == FactMetaData::_longDescriptionJsonKey) {
300 qCDebug(SettingsManagerLog) << " Setting longDesc to" << overrideMetaData->longDescription();
301 metaData.setLongDescription(overrideMetaData->longDescription());
302 } else if (metaDataName == FactMetaData::_shortDescriptionJsonKey) {
303 qCDebug(SettingsManagerLog) << " Setting shortDesc to" << overrideMetaData->shortDescription();
304 metaData.setShortDescription(overrideMetaData->shortDescription());
305 }
306 }
307
308 // Apply SettingsManager-level overrides last so forceRawValue wins over a default override
309 if (settingOverrideJsonObject.contains(kJsonForceRawValueKey)) {
310 qCDebug(SettingsManagerLog) << " Setting forceRawValue to" << settingOverrideJsonObject[kJsonForceRawValueKey];
311 metaData.setRawDefaultValue(settingOverrideJsonObject[kJsonForceRawValueKey].toVariant());
312 userVisible = false;
313 }
314 if (settingOverrideJsonObject.contains(kJsonVisibleKey)) {
315 qCDebug(SettingsManagerLog) << " Setting visibility to" << settingOverrideJsonObject[kJsonVisibleKey].toBool();
316 userVisible = settingOverrideJsonObject[kJsonVisibleKey].toBool();
317 }
318 }
319 }
320 }
321
322 // Give QGCCorePlugin a whack at it too
323 QGCCorePlugin::instance()->adjustSettingMetaData(settingsGroup, metaData, userVisible);
324}
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Q_APPLICATION_STATIC(SettingsManager, _settingsManagerInstance)
std::array< MsgOutKey, 4 > keys
Definition UBXPlan.cc:13
Application Settings.
Definition AppSettings.h:10
QString settingsSavePath()
static constexpr const char * settingsFileExtension
Auto connect settings.
Holds the meta data associated with a Fact.
static FactMetaData * createFromJsonObject(const QJsonObject &json, const QMap< QString, QString > &defineMap, QObject *metaDataParent)
void setDecimalPlaces(int decimalPlaces)
void setShortDescription(const QString &shortDescription)
static QString typeToString(ValueType_t type)
void setLongDescription(const QString &longDescription)
void setRawMin(const QVariant &rawMin)
void setRawDefaultValue(const QVariant &rawDefaultValue)
void setEnumInfo(const QStringList &strings, const QVariantList &values)
void setBitmaskInfo(const QStringList &strings, const QVariantList &values)
void setRawMax(const QVariant &rawMax)
QString name() const
ValueType_t type() const
Application Settings.
virtual void registerCustomSettings(SettingsManager *settingsManager)
virtual void adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData, bool &userVisible)
static QGCCorePlugin * instance()
Provides access to group of settings.
Provides access to all app settings.
AutoConnectSettings * autoConnectSettings() const
void registerCustomSettingsGroup(const QString &accessorName, SettingsGroup *group)
Viewer3DSettings * viewer3DSettings() const
JoystickManagerSettings * joystickManagerSettings() const
MapsSettings * mapsSettings() const
FlightMapSettings * flightMapSettings() const
ADSBVehicleManagerSettings * adsbVehicleManagerSettings() const
PlanViewSettings * planViewSettings() const
UnitsSettings * unitsSettings() const
RTKSettings * rtkSettings() const
LogViewerSettings * logViewerSettings() const
BatteryIndicatorSettings * batteryIndicatorSettings() const
FirmwareUpgradeSettings * firmwareUpgradeSettings() const
FlightModeSettings * flightModeSettings() const
static SettingsManager * instance()
APMMavlinkStreamRateSettings * apmMavlinkStreamRateSettings() const
LogManagerSettings * logManagerSettings() const
VideoSettings * videoSettings() const
static void adjustSettingMetaData(const QString &settingsGroup, FactMetaData &metaData, bool &userVisible)
AppSettings * appSettings() const
OfflineMapsSettings * offlineMapsSettings() const
GPSCorrectionSettings * gpsCorrectionSettings() const
RemoteIDSettings * remoteIDSettings() const
MavlinkActionsSettings * mavlinkActionsSettings() const
FlyViewSettings * flyViewSettings() const
GimbalControllerSettings * gimbalControllerSettings() const
MavlinkSettings * mavlinkSettings() const
NTRIPSettings * ntripSettings() const
bool validateInternalQGCJsonFile(const QJsonObject &jsonObject, const QString &expectedFileType, int minSupportedVersion, int maxSupportedVersion, int &version, QString &errorString)
bool validateKeys(const QJsonObject &jsonObject, const QList< KeyValidateInfo > &keyInfo, QString &errorString)
Validates that all required keys are present and that listed keys have the expected type.
bool isJsonFile(const QByteArray &bytes, QJsonDocument &jsonDoc, QString &errorString)
Determines whether an in-memory byte buffer contains parseable JSON content.
bool runningUnitTests()