QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
LogViewerController.cc
Go to the documentation of this file.
2
4
5#include <algorithm>
6#include <cmath>
7
8QGC_LOGGING_CATEGORY(LogViewerControllerLog, "AnalyzeView.LogViewerController")
9
10namespace {
11
12// Compares digit runs by numeric value so "control[2]" sorts before "control[10]"
13bool _naturalLess(const QString& a, const QString& b)
14{
15 const auto isDigit = [](QChar c) { return (c >= QLatin1Char('0')) && (c <= QLatin1Char('9')); };
16
17 qsizetype i = 0;
18 qsizetype j = 0;
19 while ((i < a.size()) && (j < b.size())) {
20 if (isDigit(a[i]) && isDigit(b[j])) {
21 const qsizetype aStart = i;
22 const qsizetype bStart = j;
23 while ((i < a.size()) && isDigit(a[i])) {
24 i++;
25 }
26 while ((j < b.size()) && isDigit(b[j])) {
27 j++;
28 }
29 const QStringView aNumber = QStringView(a).sliced(aStart, i - aStart);
30 const QStringView bNumber = QStringView(b).sliced(bStart, j - bStart);
31 if (aNumber.size() != bNumber.size()) {
32 return aNumber.size() < bNumber.size();
33 }
34 const int numberCompare = aNumber.compare(bNumber);
35 if (numberCompare != 0) {
36 return numberCompare < 0;
37 }
38 continue;
39 }
40 if (a[i] != b[j]) {
41 return a[i] < b[j];
42 }
43 i++;
44 j++;
45 }
46
47 return (a.size() - i) < (b.size() - j);
48}
49
50} // namespace
51
53 : QObject(parent)
54{
55 qCDebug(LogViewerControllerLog) << this;
56}
57
59{
60 qCDebug(LogViewerControllerLog) << this;
61}
62
64{
65 _plottableFields.clear();
66 _fieldRows.clear();
67 _selectedFields.clear();
68 _expandedGroups.clear();
70 emit fieldRowsChanged();
72 _setLog(SourceType::None, QString());
73}
74
75void LogViewerController::openTLog(const QString &path)
76{
77 _setLog(SourceType::TLog, path);
78}
79
80void LogViewerController::openBinLog(const QString &path)
81{
82 _setLog(SourceType::Bin, path);
83}
84
85void LogViewerController::openULogFile(const QString &path)
86{
87 _setLog(SourceType::ULog, path);
88}
89
90void LogViewerController::setPlottableFields(const QStringList &fieldNames)
91{
92 _plottableFields = fieldNames;
93 std::sort(_plottableFields.begin(), _plottableFields.end(), _naturalLess);
95 _selectedFields.clear();
97 _rebuildFieldRows();
98}
99
101{
102 if (_selectedFields.isEmpty()) {
103 return;
104 }
105
106 _selectedFields.clear();
108}
109
110void LogViewerController::toggleGroupExpanded(const QString &groupName)
111{
112 if (_expandedGroups.contains(groupName)) {
113 _expandedGroups.remove(groupName);
114 } else {
115 _expandedGroups.insert(groupName);
116 }
117
118 _rebuildFieldRows();
119}
120
121bool LogViewerController::isGroupExpanded(const QString &groupName) const
122{
123 return _expandedGroups.contains(groupName);
124}
125
126void LogViewerController::setFieldSelected(const QString &fieldName, bool selected)
127{
128 const bool currentlySelected = _selectedFields.contains(fieldName);
129 if (currentlySelected == selected) {
130 return;
131 }
132
133 if (selected) {
134 _selectedFields.append(fieldName);
135 } else {
136 _selectedFields.removeAll(fieldName);
137 }
138
140}
141
142bool LogViewerController::isFieldSelected(const QString &fieldName) const
143{
144 return _selectedFields.contains(fieldName);
145}
146
147QString LogViewerController::fieldColor(const QString &fieldName) const
148{
149 return _assignColorForKey(fieldName);
150}
151
152QString LogViewerController::eventColor(const QString &eventType) const
153{
154 if (eventType == QStringLiteral("mode")) {
155 return _assignColorForKey(QStringLiteral("event-mode"));
156 }
157 if (eventType == QStringLiteral("error")) {
158 return _assignColorForKey(QStringLiteral("event-error"));
159 }
160 if (eventType == QStringLiteral("event")) {
161 return _assignColorForKey(QStringLiteral("event-generic"));
162 }
163 if (eventType == QStringLiteral("warning")) {
164 return _assignColorForKey(QStringLiteral("event-warning"));
165 }
166
167 return _assignColorForKey(QStringLiteral("event-other"));
168}
169
170void LogViewerController::_setLog(SourceType sourceType, const QString &path)
171{
172 if (_sourceType != sourceType) {
173 _sourceType = sourceType;
174 emit sourceTypeChanged();
175 }
176
177 if (_currentLogPath != path) {
178 _currentLogPath = path;
180 }
181
182 qCDebug(LogViewerControllerLog) << "sourceType" << static_cast<int>(_sourceType) << "path" << _currentLogPath;
183}
184
185void LogViewerController::_rebuildFieldRows()
186{
187 QHash<QString, QStringList> groupedMap;
188 QStringList groups;
189
190 for (const QString &field : _plottableFields) {
191 const int splitIndex = field.indexOf('.');
192 const QString groupName = (splitIndex > 0) ? field.left(splitIndex) : tr("Other");
193 const QString shortName = (splitIndex > 0) ? field.mid(splitIndex + 1) : field;
194 if (!groupedMap.contains(groupName)) {
195 groups.append(groupName);
196 }
197 groupedMap[groupName].append(shortName);
198 }
199
200 std::sort(groups.begin(), groups.end(), _naturalLess);
201
202 QVariantList rows;
203 for (const QString &groupName : groups) {
204 QVariantMap groupRow;
205 groupRow[QStringLiteral("rowType")] = QStringLiteral("group");
206 groupRow[QStringLiteral("group")] = groupName;
207 rows.append(groupRow);
208
209 if (!_expandedGroups.contains(groupName)) {
210 continue;
211 }
212
213 QStringList fieldNames = groupedMap.value(groupName);
214 std::sort(fieldNames.begin(), fieldNames.end(), _naturalLess);
215 for (const QString &shortName : fieldNames) {
216 QVariantMap fieldRow;
217 fieldRow[QStringLiteral("rowType")] = QStringLiteral("field");
218 fieldRow[QStringLiteral("group")] = groupName;
219 fieldRow[QStringLiteral("shortName")] = shortName;
220 fieldRow[QStringLiteral("fullName")] = QStringLiteral("%1.%2").arg(groupName, shortName);
221 rows.append(fieldRow);
222 }
223 }
224
225 _fieldRows = rows;
226 emit fieldRowsChanged();
227}
228
229QString LogViewerController::_assignColorForKey(const QString &key) const
230{
231 static const QStringList palette = {
232 QStringLiteral("#3776D6"),
233 QStringLiteral("#D9534F"),
234 QStringLiteral("#3FA96B"),
235 QStringLiteral("#D98E04"),
236 QStringLiteral("#7B5CC9"),
237 QStringLiteral("#D64E8B"),
238 QStringLiteral("#2FA9A2"),
239 QStringLiteral("#D96A2D"),
240 QStringLiteral("#4A6CD4"),
241 QStringLiteral("#6EA827"),
242 };
243
244 quint32 hash = 0;
245 for (const QChar ch : key) {
246 hash = (hash * 31U) + ch.unicode();
247 }
248
249 const qsizetype idx = static_cast<qsizetype>(hash % static_cast<quint32>(palette.count()));
250 return palette[idx];
251}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Q_INVOKABLE bool isGroupExpanded(const QString &groupName) const
void plottableFieldsChanged()
Q_INVOKABLE void openTLog(const QString &path)
void currentLogPathChanged()
Q_INVOKABLE void openBinLog(const QString &path)
Q_INVOKABLE void clear()
SourceType sourceType() const
Q_INVOKABLE void setPlottableFields(const QStringList &fieldNames)
Q_INVOKABLE bool isFieldSelected(const QString &fieldName) const
Q_INVOKABLE QString fieldColor(const QString &fieldName) const
Q_INVOKABLE void setFieldSelected(const QString &fieldName, bool selected)
Q_INVOKABLE void clearSelection()
Q_INVOKABLE void openULogFile(const QString &path)
Q_INVOKABLE void toggleGroupExpanded(const QString &groupName)
Q_INVOKABLE QString eventColor(const QString &eventType) const
void selectedFieldsChanged()
LogViewerController(QObject *parent=nullptr)