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
11 : QObject(parent)
12{
13 qCDebug(LogViewerControllerLog) << this;
14}
15
16LogViewerController::~LogViewerController()
17{
18 qCDebug(LogViewerControllerLog) << this;
19}
20
21void LogViewerController::clear()
22{
23 _plottableFields.clear();
24 _fieldRows.clear();
25 _selectedFields.clear();
26 _expandedGroups.clear();
27 emit fieldRowsChanged();
29 _setLog(SourceType::None, QString(), tr("No log loaded"));
30}
31
32void LogViewerController::openTLog(const QString &path)
33{
34 _setLog(SourceType::TLog, path, tr("Telemetry log loaded"));
35}
36
37void LogViewerController::openBinLog(const QString &path)
38{
39 _setLog(SourceType::Bin, path, tr("DataFlash log loaded"));
40}
41
42void LogViewerController::openULogFile(const QString &path)
43{
44 _setLog(SourceType::ULog, path, tr("ULog file loaded"));
45}
46
47void LogViewerController::setPlottableFields(const QStringList &fieldNames)
48{
49 _plottableFields = fieldNames;
50 std::sort(_plottableFields.begin(), _plottableFields.end());
51 _selectedFields.clear();
53 _rebuildFieldRows();
54}
55
56void LogViewerController::clearSelection()
57{
58 if (_selectedFields.isEmpty()) {
59 return;
60 }
61
62 _selectedFields.clear();
64}
65
66void LogViewerController::toggleGroupExpanded(const QString &groupName)
67{
68 if (_expandedGroups.contains(groupName)) {
69 _expandedGroups.remove(groupName);
70 } else {
71 _expandedGroups.insert(groupName);
72 }
73
74 _rebuildFieldRows();
75}
76
77bool LogViewerController::isGroupExpanded(const QString &groupName) const
78{
79 return _expandedGroups.contains(groupName);
80}
81
82void LogViewerController::setFieldSelected(const QString &fieldName, bool selected)
83{
84 const bool currentlySelected = _selectedFields.contains(fieldName);
85 if (currentlySelected == selected) {
86 return;
87 }
88
89 if (selected) {
90 _selectedFields.append(fieldName);
91 } else {
92 _selectedFields.removeAll(fieldName);
93 }
94
96}
97
98bool LogViewerController::isFieldSelected(const QString &fieldName) const
99{
100 return _selectedFields.contains(fieldName);
101}
102
103QString LogViewerController::fieldColor(const QString &fieldName) const
104{
105 return _assignColorForKey(fieldName);
106}
107
108QString LogViewerController::eventColor(const QString &eventType) const
109{
110 if (eventType == QStringLiteral("mode")) {
111 return _assignColorForKey(QStringLiteral("event-mode"));
112 }
113 if (eventType == QStringLiteral("error")) {
114 return _assignColorForKey(QStringLiteral("event-error"));
115 }
116 if (eventType == QStringLiteral("event")) {
117 return _assignColorForKey(QStringLiteral("event-generic"));
118 }
119 if (eventType == QStringLiteral("warning")) {
120 return _assignColorForKey(QStringLiteral("event-warning"));
121 }
122
123 return _assignColorForKey(QStringLiteral("event-other"));
124}
125
126QString LogViewerController::modeColor(const QString &modeName) const
127{
128 static const QStringList modePalette = {
129 QStringLiteral("#E53935"), // red
130 QStringLiteral("#FB8C00"), // orange
131 QStringLiteral("#FDD835"), // yellow
132 QStringLiteral("#43A047"), // green
133 QStringLiteral("#00897B"), // teal
134 QStringLiteral("#00ACC1"), // cyan
135 QStringLiteral("#1E88E5"), // blue
136 QStringLiteral("#5E35B1"), // indigo
137 QStringLiteral("#8E24AA"), // purple
138 QStringLiteral("#D81B60"), // pink
139 QStringLiteral("#6D4C41"), // brown
140 QStringLiteral("#546E7A"), // blue grey
141 };
142
143 quint32 hash = 0;
144 for (const QChar ch : modeName) {
145 hash = (hash * 31U) + ch.unicode();
146 }
147
148 const qsizetype idx = static_cast<qsizetype>(hash % static_cast<quint32>(modePalette.count()));
149 return modePalette[idx];
150}
151
152QStringList LogViewerController::modeLegendEntries(const QVariantList &modeSegments) const
153{
154 QStringList modes;
155 for (const QVariant &variant : modeSegments) {
156 const QVariantMap segment = variant.toMap();
157 const QString mode = segment.value(QStringLiteral("mode")).toString();
158 if (!mode.isEmpty() && !modes.contains(mode)) {
159 modes.append(mode);
160 }
161 }
162
163 return modes;
164}
165
166void LogViewerController::_setLog(SourceType sourceType, const QString &path, const QString &statusText)
167{
168 if (_sourceType != sourceType) {
169 _sourceType = sourceType;
170 emit sourceTypeChanged();
171 }
172
173 if (_currentLogPath != path) {
174 _currentLogPath = path;
176 }
177
178 if (_statusText != statusText) {
179 _statusText = statusText;
180 emit statusTextChanged();
181 }
182
183 qCDebug(LogViewerControllerLog) << "sourceType" << static_cast<int>(_sourceType) << "path" << _currentLogPath;
184}
185
186void LogViewerController::_rebuildFieldRows()
187{
188 QHash<QString, QStringList> groupedMap;
189 QStringList groups;
190
191 for (const QString &field : _plottableFields) {
192 const int splitIndex = field.indexOf('.');
193 const QString groupName = (splitIndex > 0) ? field.left(splitIndex) : tr("Other");
194 const QString shortName = (splitIndex > 0) ? field.mid(splitIndex + 1) : field;
195 if (!groupedMap.contains(groupName)) {
196 groups.append(groupName);
197 }
198 groupedMap[groupName].append(shortName);
199 }
200
201 std::sort(groups.begin(), groups.end());
202
203 QVariantList rows;
204 for (const QString &groupName : groups) {
205 QVariantMap groupRow;
206 groupRow[QStringLiteral("rowType")] = QStringLiteral("group");
207 groupRow[QStringLiteral("group")] = groupName;
208 rows.append(groupRow);
209
210 if (!_expandedGroups.contains(groupName)) {
211 continue;
212 }
213
214 QStringList fieldNames = groupedMap.value(groupName);
215 std::sort(fieldNames.begin(), fieldNames.end());
216 for (const QString &shortName : fieldNames) {
217 QVariantMap fieldRow;
218 fieldRow[QStringLiteral("rowType")] = QStringLiteral("field");
219 fieldRow[QStringLiteral("group")] = groupName;
220 fieldRow[QStringLiteral("shortName")] = shortName;
221 fieldRow[QStringLiteral("fullName")] = QStringLiteral("%1.%2").arg(groupName, shortName);
222 rows.append(fieldRow);
223 }
224 }
225
226 _fieldRows = rows;
227 emit fieldRowsChanged();
228}
229
230QString LogViewerController::_assignColorForKey(const QString &key) const
231{
232 static const QStringList palette = {
233 QStringLiteral("#3776D6"),
234 QStringLiteral("#D9534F"),
235 QStringLiteral("#3FA96B"),
236 QStringLiteral("#D98E04"),
237 QStringLiteral("#7B5CC9"),
238 QStringLiteral("#D64E8B"),
239 QStringLiteral("#2FA9A2"),
240 QStringLiteral("#D96A2D"),
241 QStringLiteral("#4A6CD4"),
242 QStringLiteral("#6EA827"),
243 };
244
245 quint32 hash = 0;
246 for (const QChar ch : key) {
247 hash = (hash * 31U) + ch.unicode();
248 }
249
250 const qsizetype idx = static_cast<qsizetype>(hash % static_cast<quint32>(palette.count()));
251 return palette[idx];
252}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void currentLogPathChanged()
void selectedFieldsChanged()