QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MAVLinkMessage.cc
Go to the documentation of this file.
1#include "MAVLinkMessage.h"
2#include "MAVLinkInstanceFields.h"
3#include "MAVLinkLib.h"
7
8#include <QtCore/QTimeZone>
9
10QGC_LOGGING_CATEGORY(MAVLinkMessageLog, "AnalyzeView.MAVLinkMessage")
11
12QGCMAVLinkMessage::QGCMAVLinkMessage(const mavlink_message_t &message, const QString &instanceValue, QObject *parent)
13 : QObject(parent)
14 , _message(message)
15 , _fields(new QmlObjectListModel(this))
16 , _instanceValue(instanceValue)
17{
18 qCDebug(MAVLinkMessageLog) << this;
19
20 const mavlink_message_info_t *const msgInfo = mavlink_get_message_info(&message);
21 if (!msgInfo) {
22 qCWarning(MAVLinkMessageLog) << QStringLiteral("QGCMAVLinkMessage NULL msgInfo msgid(%1)").arg(message.msgid);
23 return;
24 }
25
26 _name = QString(msgInfo->name);
27 if (!_instanceValue.isEmpty()) {
28 _name += QStringLiteral(" [%1]").arg(_instanceValue);
29 }
30 qCDebug(MAVLinkMessageLog) << "New Message:" << _name;
31
32 for (unsigned int i = 0; i < msgInfo->num_fields; ++i) {
33 QString type = QStringLiteral("?");
34 switch (msgInfo->fields[i].type) {
35 case MAVLINK_TYPE_CHAR: type = QString("char"); break;
36 case MAVLINK_TYPE_UINT8_T: type = QString("uint8_t"); break;
37 case MAVLINK_TYPE_INT8_T: type = QString("int8_t"); break;
38 case MAVLINK_TYPE_UINT16_T: type = QString("uint16_t"); break;
39 case MAVLINK_TYPE_INT16_T: type = QString("int16_t"); break;
40 case MAVLINK_TYPE_UINT32_T: type = QString("uint32_t"); break;
41 case MAVLINK_TYPE_INT32_T: type = QString("int32_t"); break;
42 case MAVLINK_TYPE_FLOAT: type = QString("float"); break;
43 case MAVLINK_TYPE_DOUBLE: type = QString("double"); break;
44 case MAVLINK_TYPE_UINT64_T: type = QString("uint64_t"); break;
45 case MAVLINK_TYPE_INT64_T: type = QString("int64_t"); break;
46 default:
47 qCWarning(MAVLinkMessageLog) << "Unknown MAVLink field type:" << msgInfo->fields[i].type;
48 break;
49 }
50
51 const unsigned int array_length = msgInfo->fields[i].array_length;
52 const QString fieldName = QString(msgInfo->fields[i].name);
53
54 if (array_length > 0 && msgInfo->fields[i].type != MAVLINK_TYPE_CHAR) {
55 // Expand numeric arrays into individual element fields
56 for (unsigned int j = 0; j < array_length; ++j) {
57 const QString elementName = QStringLiteral("%1[%2]").arg(fieldName).arg(j);
58 QGCMAVLinkMessageField *const field = new QGCMAVLinkMessageField(elementName, type, this);
59 _fields->append(field);
60 _fieldMappings.append({i, static_cast<int>(j)});
61 }
62 } else {
63 QGCMAVLinkMessageField *const field = new QGCMAVLinkMessageField(fieldName, type, this);
64 _fields->append(field);
65 _fieldMappings.append({i, -1});
66 }
67 }
68}
69
71{
72 _fields->clearAndDeleteContents();
73
74 qCDebug(MAVLinkMessageLog) << this;
75}
76
78{
79 const QString instanceFieldName = mavlinkInstanceFields().value(message.msgid);
80 if (instanceFieldName.isEmpty()) {
81 // Special-case debug messages that use name/ind as implicit instance discriminator
82 return _extractDebugInstanceValue(message);
83 }
84
85 const mavlink_message_info_t *const msgInfo = mavlink_get_message_info(&message);
86 if (!msgInfo) {
87 return QString();
88 }
89
90 const uint8_t *const payload = reinterpret_cast<const uint8_t*>(&message.payload64[0]);
91
92 for (unsigned int i = 0; i < msgInfo->num_fields; ++i) {
93 if (instanceFieldName != QLatin1String(msgInfo->fields[i].name)) {
94 continue;
95 }
96
97 const unsigned int offset = msgInfo->fields[i].wire_offset;
98 const unsigned int array_length = msgInfo->fields[i].array_length;
99
100 switch (msgInfo->fields[i].type) {
101 case MAVLINK_TYPE_CHAR:
102 if (array_length > 0) {
103 char buf[MAVLINK_MAX_PAYLOAD_LEN + 1]{};
104 memcpy(buf, payload + offset, array_length);
105 buf[array_length] = '\0';
106 return QString::fromLatin1(buf).trimmed();
107 } else {
108 return QString(QChar::fromLatin1(static_cast<char>(*(payload + offset))));
109 }
110 case MAVLINK_TYPE_UINT8_T:
111 return QString::number(*(payload + offset));
112 case MAVLINK_TYPE_INT8_T:
113 return QString::number(*reinterpret_cast<const int8_t*>(payload + offset));
114 case MAVLINK_TYPE_UINT16_T: {
115 uint16_t val = 0;
116 memcpy(&val, payload + offset, sizeof(val));
117 return QString::number(val);
118 }
119 case MAVLINK_TYPE_INT16_T: {
120 int16_t val = 0;
121 memcpy(&val, payload + offset, sizeof(val));
122 return QString::number(val);
123 }
124 case MAVLINK_TYPE_UINT32_T: {
125 uint32_t val = 0;
126 memcpy(&val, payload + offset, sizeof(val));
127 return QString::number(val);
128 }
129 case MAVLINK_TYPE_INT32_T: {
130 int32_t val = 0;
131 memcpy(&val, payload + offset, sizeof(val));
132 return QString::number(val);
133 }
134 case MAVLINK_TYPE_UINT64_T: {
135 uint64_t val = 0;
136 memcpy(&val, payload + offset, sizeof(val));
137 return QString::number(val);
138 }
139 case MAVLINK_TYPE_INT64_T: {
140 int64_t val = 0;
141 memcpy(&val, payload + offset, sizeof(val));
142 return QString::number(val);
143 }
144 default:
145 return QString();
146 }
147 }
148
149 return QString();
150}
151
152QString QGCMAVLinkMessage::_extractDebugInstanceValue(const mavlink_message_t &message)
153{
154 switch (message.msgid) {
155 case MAVLINK_MSG_ID_NAMED_VALUE_FLOAT: {
156 char name[MAVLINK_MSG_NAMED_VALUE_FLOAT_FIELD_NAME_LEN + 1]{};
157 mavlink_msg_named_value_float_get_name(&message, name);
158 name[MAVLINK_MSG_NAMED_VALUE_FLOAT_FIELD_NAME_LEN] = '\0';
159 return QString::fromLatin1(name).trimmed();
160 }
161 case MAVLINK_MSG_ID_NAMED_VALUE_INT: {
162 char name[MAVLINK_MSG_NAMED_VALUE_INT_FIELD_NAME_LEN + 1]{};
163 mavlink_msg_named_value_int_get_name(&message, name);
164 name[MAVLINK_MSG_NAMED_VALUE_INT_FIELD_NAME_LEN] = '\0';
165 return QString::fromLatin1(name).trimmed();
166 }
167 case MAVLINK_MSG_ID_DEBUG_VECT: {
168 char name[MAVLINK_MSG_DEBUG_VECT_FIELD_NAME_LEN + 1]{};
169 mavlink_msg_debug_vect_get_name(&message, name);
170 name[MAVLINK_MSG_DEBUG_VECT_FIELD_NAME_LEN] = '\0';
171 return QString::fromLatin1(name).trimmed();
172 }
173 case MAVLINK_MSG_ID_DEBUG: {
174 return QString::number(mavlink_msg_debug_get_ind(&message));
175 }
176 default:
177 return QString();
178 }
179}
180
182{
183 bool sel = false;
184
185 for (int i = 0; i < _fields->count(); ++i) {
186 const QGCMAVLinkMessageField *const field = qobject_cast<const QGCMAVLinkMessageField*>(_fields->get(i));
187 if (field && field->selected()) {
188 sel = true;
189 break;
190 }
191 }
192
193 if (sel != _fieldSelected) {
194 _fieldSelected = sel;
196 }
197}
198
200{
201 const quint64 msgCount = _count - _lastCount;
202 const qreal lastRateHz = _actualRateHz;
203 _actualRateHz = (0.2 * _actualRateHz) + (0.8 * msgCount);
204 _lastCount = _count;
205 if (_actualRateHz != lastRateHz) {
206 emit actualRateHzChanged();
207 }
208}
209
211{
212 if (sel != _selected) {
213 _selected = sel;
214 _updateFields();
215 emit selectedChanged();
216 }
217}
218
220{
221 if (rate != _targetRateHz) {
222 _targetRateHz = rate;
223 emit targetRateHzChanged();
224 }
225}
226
228{
229 _count++;
230 _message = message;
231
232 if (_selected || _fieldSelected) {
233 // Don't update field info unless selected to reduce perf hit of message processing
234 _updateFields();
235 }
236 emit countChanged();
237}
238
239void QGCMAVLinkMessage::_updateFields()
240{
241 const mavlink_message_info_t *msgInfo = mavlink_get_message_info(&_message);
242 if (!msgInfo) {
243 qCWarning(MAVLinkMessageLog) << "QGCMAVLinkMessage::update NULL msgInfo msgid" << _message.msgid;
244 return;
245 }
246
247 if (_fields->count() != _fieldMappings.count()) {
248 qCWarning(MAVLinkMessageLog) << "QGCMAVLinkMessage::update field mapping count mismatch msgid" << _message.msgid;
249 return;
250 }
251
252 uint8_t *const msg = reinterpret_cast<uint8_t*>(&_message.payload64[0]);
253
254 for (int idx = 0; idx < _fieldMappings.count(); ++idx) {
255 QGCMAVLinkMessageField *const field = qobject_cast<QGCMAVLinkMessageField*>(_fields->get(idx));
256 if (!field) {
257 continue;
258 }
259
260 const FieldMapping &mapping = _fieldMappings.at(idx);
261 const unsigned int i = mapping.fieldIndex;
262 const int element = mapping.arrayElement;
263 const unsigned int offset = msgInfo->fields[i].wire_offset;
264
265 switch (msgInfo->fields[i].type) {
266 case MAVLINK_TYPE_CHAR:
267 field->setSelectable(false);
268 if (msgInfo->fields[i].array_length > 0) {
269 char *const str = reinterpret_cast<char*>(msg + offset);
270 str[msgInfo->fields[i].array_length - 1] = '\0';
271 const QString v(str);
272 field->updateValue(v, 0);
273 } else {
274 char b = *(reinterpret_cast<char*>(msg + offset));
275 const QString v(b);
276 field->updateValue(v, 0);
277 }
278 break;
279 case MAVLINK_TYPE_UINT8_T:
280 if (element >= 0) {
281 const uint8_t u = *(msg + offset + element);
282 field->updateValue(QString::number(u), static_cast<qreal>(u));
283 } else {
284 const uint8_t u = *(msg + offset);
285 field->updateValue(QString::number(u), static_cast<qreal>(u));
286 }
287 break;
288 case MAVLINK_TYPE_INT8_T:
289 if (element >= 0) {
290 const int8_t n = *(reinterpret_cast<int8_t*>(msg + offset) + element);
291 field->updateValue(QString::number(n), static_cast<qreal>(n));
292 } else {
293 const int8_t n = *(reinterpret_cast<int8_t*>(msg + offset));
294 field->updateValue(QString::number(n), static_cast<qreal>(n));
295 }
296 break;
297 case MAVLINK_TYPE_UINT16_T: {
298 uint16_t n = 0;
299 if (element >= 0) {
300 (void) memcpy(&n, msg + offset + element * static_cast<int>(sizeof(uint16_t)),
301 sizeof(uint16_t));
302 } else {
303 (void) memcpy(&n, msg + offset, sizeof(uint16_t));
304 }
305 field->updateValue(QString::number(n), static_cast<qreal>(n));
306 break;
307 }
308 case MAVLINK_TYPE_INT16_T: {
309 int16_t n = 0;
310 if (element >= 0) {
311 (void) memcpy(&n, msg + offset + element * static_cast<int>(sizeof(int16_t)),
312 sizeof(int16_t));
313 } else {
314 (void) memcpy(&n, msg + offset, sizeof(int16_t));
315 }
316 field->updateValue(QString::number(n), static_cast<qreal>(n));
317 break;
318 }
319 case MAVLINK_TYPE_UINT32_T: {
320 uint32_t n = 0;
321 if (element >= 0) {
322 (void) memcpy(&n, msg + offset + element * static_cast<int>(sizeof(uint32_t)),
323 sizeof(uint32_t));
324 } else {
325 (void) memcpy(&n, msg + offset, sizeof(uint32_t));
326 }
327 if (_message.msgid == MAVLINK_MSG_ID_SYSTEM_TIME && element < 0) {
328 const QDateTime d = QDateTime::fromMSecsSinceEpoch(static_cast<qint64>(n), QTimeZone::utc());
329 field->updateValue(d.toString("HH:mm:ss"), static_cast<qreal>(n));
330 } else {
331 field->updateValue(QString::number(n), static_cast<qreal>(n));
332 }
333 break;
334 }
335 case MAVLINK_TYPE_INT32_T: {
336 int32_t n = 0;
337 if (element >= 0) {
338 (void) memcpy(&n, msg + offset + element * static_cast<int>(sizeof(int32_t)),
339 sizeof(int32_t));
340 } else {
341 (void) memcpy(&n, msg + offset, sizeof(int32_t));
342 }
343 field->updateValue(QString::number(n), static_cast<qreal>(n));
344 break;
345 }
346 case MAVLINK_TYPE_FLOAT: {
347 float fv = 0;
348 if (element >= 0) {
349 (void) memcpy(&fv, msg + offset + element * static_cast<int>(sizeof(float)),
350 sizeof(float));
351 } else {
352 (void) memcpy(&fv, msg + offset, sizeof(float));
353 }
354 field->updateValue(QString::number(static_cast<double>(fv), 'g', 10), static_cast<qreal>(fv));
355 break;
356 }
357 case MAVLINK_TYPE_DOUBLE: {
358 double d = 0;
359 if (element >= 0) {
360 (void) memcpy(&d, msg + offset + element * static_cast<int>(sizeof(double)),
361 sizeof(double));
362 } else {
363 (void) memcpy(&d, msg + offset, sizeof(double));
364 }
365 field->updateValue(QString::number(d, 'g', 15), static_cast<qreal>(d));
366 break;
367 }
368 case MAVLINK_TYPE_UINT64_T: {
369 uint64_t n = 0;
370 if (element >= 0) {
371 (void) memcpy(&n, msg + offset + element * static_cast<int>(sizeof(uint64_t)),
372 sizeof(uint64_t));
373 } else {
374 (void) memcpy(&n, msg + offset, sizeof(uint64_t));
375 }
376 if (_message.msgid == MAVLINK_MSG_ID_SYSTEM_TIME && element < 0) {
377 const QDateTime d = QDateTime::fromMSecsSinceEpoch(n / 1000, QTimeZone::utc());
378 field->updateValue(d.toString("yyyy MM dd HH:mm:ss"), static_cast<qreal>(n));
379 } else {
380 field->updateValue(QString::number(n), static_cast<qreal>(n));
381 }
382 break;
383 }
384 case MAVLINK_TYPE_INT64_T: {
385 int64_t n = 0;
386 if (element >= 0) {
387 (void) memcpy(&n, msg + offset + element * static_cast<int>(sizeof(int64_t)),
388 sizeof(int64_t));
389 } else {
390 (void) memcpy(&n, msg + offset, sizeof(int64_t));
391 }
392 field->updateValue(QString::number(n), static_cast<qreal>(n));
393 break;
394 }
395 default:
396 qCWarning(MAVLinkMessageLog) << "Unknown MAVLink field type:" << msgInfo->fields[i].type;
397 break;
398 }
399 }
400}
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void updateValue(const QString &newValue, qreal v)
void setTargetRateHz(int32_t rate)
void targetRateHzChanged()
void setSelected(bool sel)
void update(const mavlink_message_t &message)
void actualRateHzChanged()
void fieldSelectedChanged()
QString name() const
static QString extractInstanceValue(const mavlink_message_t &message)
Extract the instance field value from a raw mavlink message, or empty string if none.
Q_INVOKABLE QObject * get(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.