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