QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ParameterEditorController.cc
Go to the documentation of this file.
3#include "AppMessages.h"
4#include "ParameterManager.h"
5#include "AppSettings.h"
6#include "SettingsManager.h"
7#include "Vehicle.h"
9
10QGC_LOGGING_CATEGORY(ParameterEditorControllerLog, "QMLControls.ParameterEditorController")
11
13 : QAbstractTableModel(parent)
14{
15
16}
17
22
23int ParameterTableModel::rowCount(const QModelIndex& /*parent*/) const
24{
25 return _tableData.count();
26}
27
28int ParameterTableModel::columnCount(const QModelIndex & /*parent*/) const
29{
30 return _tableViewColCount;
31}
32
33QVariant ParameterTableModel::data(const QModelIndex &index, int role) const
34{
35 if (!index.isValid()) {
36 return QVariant();
37 }
38
39 if (index.row() < 0 || index.row() >= _tableData.count()) {
40 return QVariant();
41 }
42 if (index.column() < 0 || index.column() >= _tableViewColCount) {
43 return QVariant();
44 }
45
46 switch (role) {
47 case Qt::DisplayRole:
48 return QVariant::fromValue(_tableData[index.row()][index.column()]);
49 case FactRole:
50 return QVariant::fromValue(_tableData[index.row()][ValueColumn]);
51 default:
52 return QVariant();
53 }
54}
55
56QVariant ParameterTableModel::headerData(int section, Qt::Orientation orientation, int role) const
57{
58 if (orientation != Qt::Horizontal || role != Qt::DisplayRole) {
59 return QVariant();
60 }
61
62 switch (section) {
63 case FavColumn: return tr("Fav");
64 case NameColumn: return tr("Name");
65 case ValueColumn: return tr("Value");
66 case DescriptionColumn: return tr("Description");
67 default: return QVariant();
68 }
69}
70
71QHash<int, QByteArray> ParameterTableModel::roleNames() const
72{
73 return {
74 {Qt::DisplayRole, "display"},
75 {FactRole, "fact"}
76 };
77}
78
80{
81 beginReset();
82 _tableData.clear();
83 endReset();
84}
85
87{
88 insert(rowCount(), fact);
89}
90
92{
93 if (row < 0 || row > rowCount()) {
94 qWarning() << "Invalid row row:rowCount" << row << rowCount() << Q_FUNC_INFO;
95 row = qMax(qMin(row, rowCount()), 0);
96 }
97
98 ColumnData colData(_tableViewColCount, QString());
99 colData[FavColumn] = QString();
100 colData[NameColumn] = fact->name();
101 colData[ValueColumn] = QVariant::fromValue(fact);
102 colData[DescriptionColumn] = fact->shortDescription();
103
104 if (!_isResetting()) {
105 beginInsertRows(QModelIndex(), row, row);
106 }
107 _tableData.insert(row, colData);
108 if (!_isResetting()) {
109 endInsertRows();
111 }
112}
113
115{
116 _resetNestingCount++;
117
118 if (_resetNestingCount == 1) {
119 beginResetModel();
120 }
121}
122
124{
125 if (_resetNestingCount == 0) {
126 qWarning() << "ParameterTableModel::endReset called without prior beginReset";
127 return;
128 }
129 _resetNestingCount--;
130 if (_resetNestingCount == 0) {
131 endResetModel();
133 }
134}
135
137{
138 if (row < 0 || row >= _tableData.count()) {
139 qWarning() << "Invalid row row:rowCount" << row << _tableData.count() << Q_FUNC_INFO;
140 return nullptr;
141 }
142
143 return _tableData[row][ValueColumn].value<Fact*>();
144}
145
146
148 : QObject(parent)
149{
150
151}
152
154 : FactPanelController(parent)
155 , _parameterMgr(_vehicle->parameterManager())
156{
157 // qCDebug(ParameterEditorControllerLog) << Q_FUNC_INFO << this;
158
159 _buildLists();
160
161 _searchTimer.setSingleShot(true);
162 _searchTimer.setInterval(300);
163
164 connect(this, &ParameterEditorController::currentCategoryChanged, this, &ParameterEditorController::_currentCategoryChanged);
165 connect(this, &ParameterEditorController::currentGroupChanged, this, &ParameterEditorController::_currentGroupChanged);
166 connect(this, &ParameterEditorController::searchTextChanged, this, &ParameterEditorController::_searchTextChanged);
167 connect(this, &ParameterEditorController::showModifiedOnlyChanged, this, &ParameterEditorController::_searchTextChanged);
168 connect(this, &ParameterEditorController::showFavoritesOnlyChanged, this, &ParameterEditorController::_searchTextChanged);
169 connect(this, &ParameterEditorController::hideReadOnlyChanged, this, &ParameterEditorController::_hideReadOnlyChanged);
170 connect(&_searchTimer, &QTimer::timeout, this, &ParameterEditorController::_performSearch);
171 connect(_parameterMgr, &ParameterManager::factAdded, this, &ParameterEditorController::_factAdded);
172
173 _loadFavorites();
174
175 ParameterEditorCategory* category = _categories.count() ? _categories.value<ParameterEditorCategory*>(0) : nullptr;
176 setCurrentCategory(category);
177}
178
180{
181 // qCDebug(ParameterEditorControllerLog) << Q_FUNC_INFO << this;
182}
183
184void ParameterEditorController::_buildListsForComponent(int compId)
185{
186 for (const QString& factName: _parameterMgr->parameterNames(compId)) {
187 Fact* fact = _parameterMgr->getParameter(compId, factName);
188
189 if (_hideReadOnly && fact->readOnly()) {
190 continue;
191 }
192
193 ParameterEditorCategory* category = nullptr;
194 if (_mapCategoryName2Category.contains(fact->category())) {
195 category = _mapCategoryName2Category[fact->category()];
196 } else {
197 category = new ParameterEditorCategory(this);
198 category->name = fact->category();
199 _mapCategoryName2Category[fact->category()] = category;
200 _categories.append(category);
201 }
202
203 ParameterEditorGroup* group = nullptr;
204 if (category->mapGroupName2Group.contains(fact->group())) {
205 group = category->mapGroupName2Group[fact->group()];
206 } else {
207 group = new ParameterEditorGroup(this);
208 group->componentId = compId;
209 group->name = fact->group();
210 category->mapGroupName2Group[fact->group()] = group;
211 category->groups.append(group);
212 }
213
214 group->facts.append(fact);
215 }
216}
217
218void ParameterEditorController::_buildLists(void)
219{
220 _currentCategory = nullptr;
221 _currentGroup = nullptr;
222 _parameters = nullptr;
223 _mapCategoryName2Category.clear();
224 _categories.clearAndDeleteContents();
225 emit parametersChanged();
226
227 // Autopilot component should always be first list
228 _buildListsForComponent(MAV_COMP_ID_AUTOPILOT1);
229
230 // "Standard" category should always be first
231 for (int i=0; i<_categories.count(); i++) {
232 ParameterEditorCategory* category = _categories.value<ParameterEditorCategory*>(i);
233 if (category->name == "Standard" && i != 0) {
234 _categories.removeAt(i);
235 _categories.insert(0, category);
236 break;
237 }
238 }
239
240 // Default category should always be last
241 for (int i=0; i<_categories.count(); i++) {
242 ParameterEditorCategory* category = _categories.value<ParameterEditorCategory*>(i);
243 if (category->name == FactMetaData::kDefaultCategory) {
244 if (i != _categories.count() - 1) {
245 _categories.removeAt(i);
246 _categories.append(category);
247 }
248 break;
249 }
250 }
251
252 // Now add other random components
253 for (int compId: _parameterMgr->componentIds()) {
254 if (compId != MAV_COMP_ID_AUTOPILOT1) {
255 _buildListsForComponent(compId);
256 }
257 }
258
259 // Default group should always be last
260 for (int i=0; i<_categories.count(); i++) {
261 ParameterEditorCategory* category = _categories.value<ParameterEditorCategory*>(i);
262 for (int j=0; j<category->groups.count(); j++) {
264 if (group->name == FactMetaData::kDefaultGroup) {
265 if (j != _categories.count() - 1) {
266 category->groups.removeAt(j);
267 category->groups.append(group);
268 }
269 break;
270 }
271 }
272 }
273}
274
275void ParameterEditorController::_factAdded(int compId, Fact* fact)
276{
277 if (_hideReadOnly && fact->readOnly()) {
278 return;
279 }
280
281 bool inserted = false;
282 ParameterEditorCategory* category = nullptr;
283
284 if (_mapCategoryName2Category.contains(fact->category())) {
285 category = _mapCategoryName2Category[fact->category()];
286 } else {
287 category = new ParameterEditorCategory(this);
288 category->name = fact->category();
289 _mapCategoryName2Category[fact->category()] = category;
290
291 // Insert in sorted order
292 inserted = false;
293 for (int i=0; i<_categories.count(); i++) {
294 if (_categories.value<ParameterEditorCategory*>(i)->name > category->name) {
295 _categories.insert(i, category);
296 inserted = true;
297 break;
298 }
299 }
300 if (!inserted) {
301 _categories.append(category);
302 }
303 }
304
305 ParameterEditorGroup* group = nullptr;
306 if (category->mapGroupName2Group.contains(fact->group())) {
307 group = category->mapGroupName2Group[fact->group()];
308 } else {
309 group = new ParameterEditorGroup(this);
310 group->componentId = compId;
311 group->name = fact->group();
312 category->mapGroupName2Group[fact->group()] = group;
313
314 // Insert in sorted order
315 QmlObjectListModel& groups = category->groups;
316 inserted = false;
317 for (int i=0; i<groups.count(); i++) {
318 if (groups.value<ParameterEditorGroup*>(i)->name > group->name) {
319 groups.insert(i, group);
320 inserted = true;
321 break;
322 }
323 }
324 if (!inserted) {
325 groups.append(group);
326 }
327 }
328
329 // Insert in sorted order
330 auto& facts = group->facts;
331 for (int i=0; i<facts.rowCount(); i++) {
332 if (facts.factAt(i)->name() > fact->name()) {
333 facts.insert(i, fact);
334 return;
335 }
336 }
337 facts.append(fact);
338}
339
340void ParameterEditorController::saveToFile(const QString& filename)
341{
342 if (!filename.isEmpty()) {
343 QString parameterFilename = filename;
344 if (!QFileInfo(filename).fileName().contains(".")) {
345 parameterFilename += QString(".%1").arg(AppSettings::parameterFileExtension);
346 }
347
348 QFile file(parameterFilename);
349
350 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
351 qCWarning(ParameterEditorControllerLog) << "saveToFile: unable to create file" << parameterFilename;
352 QGC::showAppMessage(tr("Unable to create file: %1").arg(parameterFilename));
353 return;
354 }
355
356 qCDebug(ParameterEditorControllerLog) << "saveToFile:" << parameterFilename;
357 QTextStream stream(&file);
358 _parameterMgr->writeParametersToStream(stream);
359 file.close();
360 }
361}
362
363template <typename T, typename SignalFn>
364void ParameterEditorController::_setDiffProperty(T& member, const T& value, SignalFn changedSignal)
365{
366 if (member == value) {
367 return;
368 }
369 member = value;
370 emit (this->*changedSignal)(member);
371}
372
374{
375 _diffList.clearAndDeleteContents();
376 _setDiffProperty(_diffOtherVehicle, false, &ParameterEditorController::diffOtherVehicleChanged);
377 _setDiffProperty(_diffMultipleComponents, false, &ParameterEditorController::diffMultipleComponentsChanged);
378 _setDiffProperty(_diffParsedCount, 0, &ParameterEditorController::diffParsedCountChanged);
379 _setDiffProperty(_diffUnchangedCount, 0, &ParameterEditorController::diffUnchangedCountChanged);
380 _setDiffProperty(_diffReadOnlyCount, 0, &ParameterEditorController::diffReadOnlyCountChanged);
381 _setDiffProperty(_diffNoVehicleCount, 0, &ParameterEditorController::diffNoVehicleCountChanged);
382 _setDiffProperty(_diffSendableCount, 0, &ParameterEditorController::diffSendableCountChanged);
383 _setDiffProperty(_diffSelectedCount, 0, &ParameterEditorController::diffSelectedCountChanged);
384 _setDiffProperty(_diffMissingParams, QStringList(), &ParameterEditorController::diffMissingParamsChanged);
385}
386
387// Recomputed whenever a diff row's load checkbox changes. Drives the dialog's Ok button enabled state.
388void ParameterEditorController::_updateDiffSelectedCount()
389{
390 int selectedCount = 0;
391 for (int i=0; i<_diffList.count(); i++) {
392 const ParameterEditorDiff* paramDiff = _diffList.value<ParameterEditorDiff*>(i);
393 if (paramDiff->load && !paramDiff->cannotSend) {
394 selectedCount++;
395 }
396 }
397 _setDiffProperty(_diffSelectedCount, selectedCount, &ParameterEditorController::diffSelectedCountChanged);
398}
399
401{
402 int sentCount = 0;
403 int uncheckedCount = 0;
404
405 for (int i=0; i<_diffList.count(); i++) {
406 ParameterEditorDiff* paramDiff = _diffList.value<ParameterEditorDiff*>(i);
407
408 if (paramDiff->cannotSend) {
409 qCDebug(ParameterEditorControllerLog) << "sendDiff: skipped (cannot send, not on vehicle) -" << paramDiff->name;
410 continue;
411 }
412
413 if (paramDiff->load) {
414 sentCount++;
415 if (paramDiff->noVehicleValue) {
416 qCDebug(ParameterEditorControllerLog) << "sendDiff: PARAM_SET new param -" << paramDiff->name
417 << "componentId:" << paramDiff->componentId << "value:" << paramDiff->fileValueVar;
418 _parameterMgr->_mavlinkParamSet(paramDiff->componentId, paramDiff->name, paramDiff->valueType, paramDiff->fileValueVar);
419 } else {
420 qCDebug(ParameterEditorControllerLog) << "sendDiff: fact write -" << paramDiff->name
421 << "componentId:" << paramDiff->componentId << "value:" << paramDiff->fileValueVar;
422 Fact* fact = _parameterMgr->getParameter(paramDiff->componentId, paramDiff->name);
423 fact->setRawValue(paramDiff->fileValueVar);
424 }
425 } else {
426 uncheckedCount++;
427 qCDebug(ParameterEditorControllerLog) << "sendDiff: skipped (unchecked) -" << paramDiff->name;
428 }
429 }
430
431 qCDebug(ParameterEditorControllerLog) << "sendDiff summary - sent:" << sentCount << "unchecked:" << uncheckedCount;
432}
433
435{
436 QFile file(filename);
437
438 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
439 qCWarning(ParameterEditorControllerLog) << "buildDiffFromFile: unable to open file" << filename;
440 QGC::showAppMessage(tr("Unable to open file: %1").arg(filename));
441 return false;
442 }
443
444 clearDiff();
445
446 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile:" << filename;
447
448 QTextStream stream(&file);
449
450 // Accumulate in locals; property setters at the end emit only for values that changed.
451 bool diffOtherVehicle = false;
452 bool diffMultipleComponents = false;
453 int diffParsedCount = 0;
454 int diffUnchangedCount = 0;
455 int diffReadOnlyCount = 0;
456 int diffNoVehicleCount = 0;
457 int diffSendableCount = 0;
458 QStringList diffMissingParams;
459
460 int firstComponentId = -1;
461 while (!stream.atEnd()) {
462 QString line = stream.readLine();
463 if (!line.startsWith("#") && !line.trimmed().isEmpty()) {
464 QStringList wpParams = line.trimmed().split(QRegularExpression("[\\t ,]+"));
465
466 int componentId = -1;
467 QString paramName;
468 QString fileValueStr;
469 int mavParamType = -1;
470 bool isMPFormat = false;
471
472 if (wpParams.size() == 5) {
473 // QGC tab-delimited: VehicleId ComponentId Name Value Type
474 int vehicleId = wpParams.at(0).toInt();
475 componentId = wpParams.at(1).toInt();
476 paramName = wpParams.at(2);
477 fileValueStr = wpParams.at(3);
478 mavParamType = wpParams.at(4).toInt();
479
480 if (_vehicle->id() != vehicleId) {
481 if (!diffOtherVehicle) {
482 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: file is from other vehicle - file vehicleId:" << vehicleId << "connected vehicleId:" << _vehicle->id();
483 }
484 diffOtherVehicle = true;
485 }
486 if (firstComponentId == -1) {
487 firstComponentId = componentId;
488 } else if (firstComponentId != componentId) {
490 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: file contains multiple componentIds:" << firstComponentId << componentId;
491 }
493 }
494 } else if (wpParams.size() == 2) {
495 // Mission Planner 2-column: Name Value
496 paramName = wpParams.at(0);
497 fileValueStr = wpParams.at(1);
499 isMPFormat = true;
500 } else {
501 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: skipping unparseable line:" << line.trimmed().left(80);
502 continue;
503 }
504
506
507 QString vehicleValueStr;
508 QString units;
509 QVariant fileValueVar = fileValueStr;
510 bool noVehicleValue = false;
511 bool readOnly = false;
512
513 if (_parameterMgr->parameterExists(componentId, paramName)) {
514 Fact* vehicleFact = _parameterMgr->getParameter(componentId, paramName);
515 FactMetaData* vehicleFactMetaData = vehicleFact->metaData();
516 Fact fileFact(vehicleFact->componentId(), vehicleFact->name(), vehicleFact->type());
517
518 if (mavParamType == -1) {
519 mavParamType = ParameterManager::factTypeToMavType(vehicleFact->type());
520 }
521
522 // Turn off reboot messaging before setting value in fileFact
523 bool vehicleRebootRequired = vehicleFactMetaData->vehicleRebootRequired();
524 vehicleFactMetaData->setVehicleRebootRequired(false);
525 fileFact.setMetaData(vehicleFact->metaData());
526 fileFact.setRawValue(fileValueStr);
527 vehicleFactMetaData->setVehicleRebootRequired(vehicleRebootRequired);
528 readOnly = vehicleFact->readOnly();
529
530 if (vehicleFact->rawValue() == fileFact.rawValue()) {
532 continue;
533 }
534 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: changed -" << paramName
535 << "vehicle:" << vehicleFact->rawValue() << "file:" << fileFact.rawValue();
536 fileValueStr = fileFact.enumOrValueString();
537 fileValueVar = fileFact.rawValue();
538 vehicleValueStr = vehicleFact->enumOrValueString();
539 units = vehicleFact->cookedUnits();
540 } else if (isMPFormat) {
541 // MP format: param not on vehicle and file carries no type info, so it can never
542 // be sent. Show it in the diff list as a disabled (cannot-send) row.
543 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: not on vehicle, cannot send (MP format) -" << paramName;
544 diffMissingParams.append(paramName);
545
546 ParameterEditorDiff* paramDiff = new ParameterEditorDiff(this);
547
548 paramDiff->componentId = componentId;
549 paramDiff->name = paramName;
550 paramDiff->valueType = FactMetaData::valueTypeFloat; // Unknown - never sent
551 paramDiff->fileValue = fileValueStr;
552 paramDiff->fileValueVar = fileValueVar;
553 paramDiff->cannotSend = true;
554 paramDiff->load = false;
555
556 _diffList.append(paramDiff);
557 continue;
558 } else {
559 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: not on vehicle, will send as new (QGC format) -" << paramName << "value:" << fileValueStr;
560 noVehicleValue = true;
561
562 // fileValueVar is still a QString variant. Convert it to the typed variant matching the
563 // file's param type, otherwise the PARAM_VALUE ack type check in _mavlinkParamSet fails
564 // and the send would retry/time out.
565 const FactMetaData metaData(ParameterManager::mavTypeToFactType(static_cast<MAV_PARAM_TYPE>(mavParamType)));
566 QVariant typedValue;
567 QString errorString;
568 if (metaData.convertAndValidateRaw(fileValueVar, true /* convertOnly */, typedValue, errorString)) {
569 fileValueVar = typedValue;
570 } else {
571 qCWarning(ParameterEditorControllerLog) << "buildDiffFromFile: value conversion failed, skipping -" << paramName
572 << "value:" << fileValueStr << "error:" << errorString;
574 continue;
575 }
576 }
577
578 if (!readOnly) {
579 ParameterEditorDiff* paramDiff = new ParameterEditorDiff(this);
580
581 paramDiff->componentId = componentId;
582 paramDiff->name = paramName;
583 paramDiff->valueType = ParameterManager::mavTypeToFactType(static_cast<MAV_PARAM_TYPE>(mavParamType));
584 paramDiff->fileValue = fileValueStr;
585 paramDiff->fileValueVar = fileValueVar;
586 paramDiff->vehicleValue = vehicleValueStr;
587 paramDiff->noVehicleValue = noVehicleValue;
588 paramDiff->units = units;
589
590 (void) connect(paramDiff, &ParameterEditorDiff::loadChanged, this, &ParameterEditorController::_updateDiffSelectedCount);
591
592 _diffList.append(paramDiff);
594
595 if (noVehicleValue) {
597 }
598 } else {
599 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile: skipping read-only param -" << paramName;
601 }
602 }
603 }
604
605 file.close();
606
607 if (diffParsedCount == 0) {
608 QGC::showAppMessage(tr("No valid parameters found in file. Check that the file is in QGC or Mission Planner format."));
609 return false;
610 }
611
612 qCDebug(ParameterEditorControllerLog) << "buildDiffFromFile summary -"
613 << "parsed:" << diffParsedCount
614 << "changed:" << diffSendableCount
615 << "unchanged:" << diffUnchangedCount
616 << "readOnly:" << diffReadOnlyCount
617 << "noVehicleValue:" << diffNoVehicleCount
618 << "missing:" << diffMissingParams.count()
619 << (diffMissingParams.isEmpty() ? QString() : diffMissingParams.join(QStringLiteral(", ")));
620
621 _setDiffProperty(_diffOtherVehicle, diffOtherVehicle, &ParameterEditorController::diffOtherVehicleChanged);
622 _setDiffProperty(_diffMultipleComponents, diffMultipleComponents, &ParameterEditorController::diffMultipleComponentsChanged);
623 _setDiffProperty(_diffParsedCount, diffParsedCount, &ParameterEditorController::diffParsedCountChanged);
624 _setDiffProperty(_diffUnchangedCount, diffUnchangedCount, &ParameterEditorController::diffUnchangedCountChanged);
625 _setDiffProperty(_diffReadOnlyCount, diffReadOnlyCount, &ParameterEditorController::diffReadOnlyCountChanged);
626 _setDiffProperty(_diffNoVehicleCount, diffNoVehicleCount, &ParameterEditorController::diffNoVehicleCountChanged);
627 _setDiffProperty(_diffSendableCount, diffSendableCount, &ParameterEditorController::diffSendableCountChanged);
628 _setDiffProperty(_diffSelectedCount, diffSendableCount, &ParameterEditorController::diffSelectedCountChanged); // All sendable rows start checked
629 _setDiffProperty(_diffMissingParams, diffMissingParams, &ParameterEditorController::diffMissingParamsChanged);
630
631 return true;
632}
633
635{
636 _parameterMgr->refreshAllParameters();
637}
638
644
650
651bool ParameterEditorController::_shouldShow(Fact* fact) const
652{
653 if (_hideReadOnly && fact->readOnly()) {
654 return false;
655 }
656 if (_showModifiedOnly) {
657 if (!fact->defaultValueAvailable() || fact->valueEqualsDefault()) {
658 return false;
659 }
660 }
661 if (_showFavoritesOnly) {
662 if (!_favoriteNames.contains(fact->name())) {
663 return false;
664 }
665 }
666 return true;
667}
668
669void ParameterEditorController::_searchTextChanged(void)
670{
671 _searchTimer.start();
672}
673
674void ParameterEditorController::_hideReadOnlyChanged(void)
675{
676 _buildLists();
677
678 ParameterEditorCategory* category = _categories.count() ? _categories.value<ParameterEditorCategory*>(0) : nullptr;
679 setCurrentCategory(category);
680
681 // Re-trigger search if active
682 if (!_searchText.isEmpty() || _showModifiedOnly) {
683 _performSearch();
684 }
685}
686
687void ParameterEditorController::_performSearch(void)
688{
689 QObjectList newParameterList;
690
691 QStringList rgSearchStrings = _searchText.split(' ', Qt::SkipEmptyParts);
692
693 if (rgSearchStrings.isEmpty() && !_showModifiedOnly && !_showFavoritesOnly) {
694 ParameterEditorCategory* category = _categories.count() ? _categories.value<ParameterEditorCategory*>(0) : nullptr;
695 setCurrentCategory(category);
696 _searchParameters.clear();
697 } else {
698 QVector<QRegularExpression> regexList;
699 regexList.reserve(rgSearchStrings.size());
700 for (const QString &searchItem : rgSearchStrings) {
701 QRegularExpression re(searchItem, QRegularExpression::CaseInsensitiveOption);
702 regexList.append(re.isValid() ? re : QRegularExpression());
703 }
704
705 _searchParameters.beginReset();
706 _searchParameters.clear();
707
708 for (int compId : _parameterMgr->componentIds()) {
709 for (const QString &paraName: _parameterMgr->parameterNames(compId)) {
710 Fact* fact = _parameterMgr->getParameter(compId, paraName);
711 bool matched = _shouldShow(fact);
712 // All of the search items must match in order for the parameter to be added to the list
713 if (matched) {
714 for (int i = 0; i < rgSearchStrings.size(); ++i) {
715 const QRegularExpression &re = regexList.at(i);
716 if (re.isValid()) {
717 if (!fact->name().contains(re) &&
718 !fact->shortDescription().contains(re) &&
719 !fact->longDescription().contains(re)) {
720 matched = false;
721 }
722 } else {
723 const QString &searchItem = rgSearchStrings.at(i);
724 if (!fact->name().contains(searchItem, Qt::CaseInsensitive) &&
725 !fact->shortDescription().contains(searchItem, Qt::CaseInsensitive) &&
726 !fact->longDescription().contains(searchItem, Qt::CaseInsensitive)) {
727 matched = false;
728 }
729 }
730 }
731 }
732 if (matched) {
733 _searchParameters.append(fact);
734 }
735 }
736 }
737
738 _searchParameters.endReset();
739
740 if (_parameters != &_searchParameters) {
741 _parameters = &_searchParameters;
742 emit parametersChanged();
743
744 _currentCategory = nullptr;
745 _currentGroup = nullptr;
746 }
747 }
748}
749
750void ParameterEditorController::_currentCategoryChanged(void)
751{
752 ParameterEditorGroup* group = nullptr;
753 if (_currentCategory) {
754 // Select first group when category changes
755 group = _currentCategory->groups.value<ParameterEditorGroup*>(0);
756 } else {
757 group = nullptr;
758 }
759 setCurrentGroup(group);
760}
761
762void ParameterEditorController::_currentGroupChanged(void)
763{
764 _parameters = _currentGroup ? &_currentGroup->facts : nullptr;
765 emit parametersChanged();
766}
767
769{
770 ParameterEditorCategory* category = qobject_cast<ParameterEditorCategory*>(currentCategory);
771 if (category != _currentCategory) {
772 _currentCategory = category;
774 }
775}
776
778{
779 ParameterEditorGroup* group = qobject_cast<ParameterEditorGroup*>(currentGroup);
780 if (group != _currentGroup) {
781 _currentGroup = group;
782 emit currentGroupChanged();
783 }
784}
785
787{
788 QStringList list(_favoriteNames.begin(), _favoriteNames.end());
789 list.sort();
790 return list;
791}
792
793void ParameterEditorController::toggleFavorite(const QString& paramName)
794{
795 if (_favoriteNames.contains(paramName)) {
796 _favoriteNames.remove(paramName);
797 } else {
798 _favoriteNames.insert(paramName);
799 }
800 _saveFavorites();
801 emit favoritesChanged();
802
803 if (_showFavoritesOnly) {
804 _performSearch();
805 }
806}
807
808bool ParameterEditorController::isFavorite(const QString& paramName) const
809{
810 return _favoriteNames.contains(paramName);
811}
812
814{
815 _favoriteNames.clear();
816 _saveFavorites();
817 emit favoritesChanged();
818
819 if (_showFavoritesOnly) {
820 _performSearch();
821 }
822}
823
824void ParameterEditorController::_loadFavorites()
825{
826 Fact* fact = SettingsManager::instance()->appSettings()->favoriteParameters();
827 const QStringList list = fact->rawValue().toString().split(",", Qt::SkipEmptyParts);
828 _favoriteNames = QSet<QString>(list.begin(), list.end());
829}
830
831void ParameterEditorController::_saveFavorites()
832{
833 QStringList list(_favoriteNames.begin(), _favoriteNames.end());
834 list.sort();
835 Fact* fact = SettingsManager::instance()->appSettings()->favoriteParameters();
836 fact->setRawValue(list.join(","));
837}
QString errorString
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static constexpr const char * parameterFileExtension
Holds the meta data associated with a Fact.
static constexpr const char * kDefaultGroup
static constexpr const char * kDefaultCategory
void setVehicleRebootRequired(bool rebootRequired)
bool convertAndValidateRaw(const QVariant &rawValue, bool convertOnly, QVariant &typedValue, QString &errorString) const
bool vehicleRebootRequired() const
Used for handling missing Facts from C++ code.
A Fact is used to hold a single value within the system.
Definition Fact.h:17
QString enumOrValueString()
Definition Fact.cc:851
void setMetaData(FactMetaData *metaData, bool setDefaultFromMetaData=false)
Definition Fact.cc:741
QString cookedUnits() const
Definition Fact.cc:571
QString longDescription() const
Definition Fact.cc:551
QString shortDescription() const
Definition Fact.cc:524
FactMetaData * metaData()
Definition Fact.h:177
bool valueEqualsDefault() const
Definition Fact.cc:750
bool readOnly() const
Definition Fact.cc:895
int componentId() const
Definition Fact.h:95
QString group() const
Definition Fact.cc:731
FactMetaData::ValueType_t type() const
Definition Fact.h:130
void setRawValue(const QVariant &value)
Definition Fact.cc:134
bool defaultValueAvailable() const
Definition Fact.cc:764
QString name() const
Definition Fact.h:127
QString category() const
Definition Fact.cc:721
QVariant rawValue() const
Definition Fact.h:90
QMap< QString, ParameterEditorGroup * > mapGroupName2Group
void diffMissingParamsChanged(const QStringList &diffMissingParams)
QStringList diffMissingParams(void) const
Q_INVOKABLE void resetAllToVehicleConfiguration(void)
void showFavoritesOnlyChanged(void)
Q_INVOKABLE bool buildDiffFromFile(const QString &filename)
void diffSelectedCountChanged(int diffSelectedCount)
void diffMultipleComponentsChanged(bool diffMultipleComponents)
Q_INVOKABLE void toggleFavorite(const QString &paramName)
void diffParsedCountChanged(int diffParsedCount)
void diffReadOnlyCountChanged(int diffReadOnlyCount)
Q_INVOKABLE void clearAllFavorites(void)
void diffNoVehicleCountChanged(int diffNoVehicleCount)
QStringList favoriteParameterNames(void) const
void setCurrentGroup(QObject *currentGroup)
Q_INVOKABLE void saveToFile(const QString &filename)
void diffUnchangedCountChanged(int diffUnchangedCount)
ParameterEditorController(QObject *parent=nullptr)
void searchTextChanged(QString searchText)
void currentCategoryChanged(void)
void diffOtherVehicleChanged(bool diffOtherVehicle)
void setCurrentCategory(QObject *currentCategory)
void diffSendableCountChanged(int diffSendableCount)
Q_INVOKABLE bool isFavorite(const QString &paramName) const
void showModifiedOnlyChanged(void)
Q_INVOKABLE void resetAllToDefaults(void)
bool cannotSend
Param not on vehicle and file has no type info (MP format) - shown but never sent.
void loadChanged(bool load)
FactMetaData::ValueType_t valueType
bool parameterExists(int componentId, const QString &paramName) const
Fact * getParameter(int componentId, const QString &paramName)
void factAdded(int componentId, Fact *fact)
static FactMetaData::ValueType_t mavTypeToFactType(MAV_PARAM_TYPE mavType)
static constexpr int defaultComponentId
void refreshAllParameters(uint8_t componentID)
Re-request the full set of parameters from the autopilot.
static MAV_PARAM_TYPE factTypeToMavType(FactMetaData::ValueType_t factType)
void resetAllToVehicleConfiguration()
void writeParametersToStream(QTextStream &stream) const
QVector< QVariant > ColumnData
void insert(int row, Fact *fact)
int rowCount(const QModelIndex &parent=QModelIndex()) const override
int columnCount(const QModelIndex &parent=QModelIndex()) const override
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const override
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
QHash< int, QByteArray > roleNames(void) const override
void rowCountChanged(int count)
void endReset()
Supports nesting - only outermost call has effect.
void beginReset()
Supports nesting - only outermost call has effect.
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
T value(int index) const
QObject * removeAt(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
void insert(int index, QObject *object)
static SettingsManager * instance()
AppSettings * appSettings() const
int id() const
Definition Vehicle.h:429
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9