QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
StatusTextHandler.cc
Go to the documentation of this file.
1#include "StatusTextHandler.h"
2#include "MAVLinkLib.h"
4
5#include <QtCore/QTimer>
6#include <QtCore/QDateTime>
7
8QGC_LOGGING_CATEGORY(StatusTextHandlerLog, "MAVLink.StatusTextHandler")
9
10StatusText::StatusText(MAV_COMPONENT componentid, MAV_SEVERITY severity, const QString &text)
11 : m_compId(componentid)
12 , m_severity(severity)
13 , m_text(text)
14{
15 // qCDebug(StatusTextHandlerLog) << Q_FUNC_INFO << this;
16}
17
19{
20 switch (m_severity) {
21 case MAV_SEVERITY_EMERGENCY:
22 case MAV_SEVERITY_ALERT:
23 case MAV_SEVERITY_CRITICAL:
24 case MAV_SEVERITY_ERROR:
25 return true;
26
27 default:
28 return false;
29 }
30}
31
33 : QObject(parent)
34 , m_chunkedStatusTextTimer(new QTimer(this))
35{
36 // qCDebug(StatusTextHandlerLog) << Q_FUNC_INFO << this;
37
38 m_chunkedStatusTextTimer->setSingleShot(true);
39 m_chunkedStatusTextTimer->setInterval(1000);
40 (void) connect(m_chunkedStatusTextTimer, &QTimer::timeout, this, &StatusTextHandler::_chunkedStatusTextTimeout);
41}
42
44{
46
47 // qCDebug(StatusTextHandlerLog) << Q_FUNC_INFO << this;
48}
49
51{
52 // Warning: There is a bug in mavlink which causes mavlink_msg_statustext_get_text to work incorrect.
53 // It ends up copying crap off the end of the buffer, so don't use it for now.
54
55 mavlink_statustext_t statusText;
56 mavlink_msg_statustext_decode(&message, &statusText);
57
58 char buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN + 1];
59 memcpy(buffer, statusText.text, MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN);
60 buffer[MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN] = '\0';
61
62 return QString(buffer);
63}
64
66{
67 QString result;
68 for (const StatusText *message: messages()) {
69 (void) result.prepend(message->getFormattedText());
70 }
71
72 return result;
73}
74
76{
77 qDeleteAll(m_messages);
78 m_messages.clear();
79
80 m_errorCount = 0;
81 m_warningCount = 0;
82 m_normalCount = 0;
83
84 _handleTextMessage(0);
85}
86
88{
89 const uint32_t count = messageCount();
90 const MessageType type = m_messageType;
91
92 m_errorCount = 0;
93 m_warningCount = 0;
94 m_normalCount = 0;
95 m_messageCount = 0;
96 m_messageType = MessageType::MessageNone;
97
98 if (count != messageCount()) {
99 emit messageCountChanged(0);
100 }
101
102 if (type != m_messageType) {
103 emit messageTypeChanged();
104 }
105}
106
108{
109 const uint32_t prevMessageCount = messageCount();
110 const MessageType prevMessagetype = m_messageType;
111
112 m_messageCount -= getErrorCount();
113 m_errorCount = 0;
114
115 if (getWarningCount() > 0) {
116 m_messageType = MessageType::MessageWarning;
117 } else if (getNormalCount() > 0) {
118 m_messageType = MessageType::MessageNormal;
119 } else {
120 m_messageType = MessageType::MessageNone;
121 }
122
123 if (prevMessageCount != messageCount()) {
125 }
126
127 if (prevMessagetype != m_messageType) {
128 emit messageTypeChanged();
129 }
130}
131
132void StatusTextHandler::handleHTMLEscapedTextMessage(MAV_COMPONENT compId, MAV_SEVERITY severity, const QString &text, const QString &description)
133{
134 QString htmlText(text);
135
136 (void) htmlText.replace("\n", "<br/>");
137
138 // TODO: handle text + description separately in the UI
139 if (!description.isEmpty()) {
140 QString htmlDescription(description);
141 (void) htmlDescription.replace("\n", "<br/>");
142 (void) htmlText.append(QStringLiteral("<br/><small><small>"));
143 (void) htmlText.append(htmlDescription);
144 (void) htmlText.append(QStringLiteral("</small></small>"));
145 }
146
147 if (m_activeComponent == MAV_COMPONENT::MAV_COMPONENT_ENUM_END) {
148 m_activeComponent = compId;
149 }
150
151 if (compId != m_activeComponent) {
152 m_multiComp = true;
153 }
154
155 MessageType messageType = MessageType::MessageNone;
156
157 // Color the output depending on the message severity. We have 3 distinct cases:
158 // 1: If we have an ERROR or worse, make it bigger, bolder, and highlight it red.
159 // 2: If we have a warning or notice, just make it bold and color it orange.
160 // 3: Otherwise color it the standard color, white.
161 QString style;
162 switch (severity) {
163 case MAV_SEVERITY_EMERGENCY:
164 case MAV_SEVERITY_ALERT:
165 case MAV_SEVERITY_CRITICAL:
166 case MAV_SEVERITY_ERROR:
167 style = QStringLiteral("<#E>");
168 messageType = MessageType::MessageError;
169 break;
170
171 case MAV_SEVERITY_NOTICE:
172 case MAV_SEVERITY_WARNING:
173 style = QStringLiteral("<#I>");
174 messageType = MessageType::MessageWarning;
175 break;
176
177 default:
178 style = QStringLiteral("<#N>");
179 messageType = MessageType::MessageNormal;
180 break;
181 }
182
183 QString severityText;
184 switch (severity) {
185 case MAV_SEVERITY_EMERGENCY:
186 severityText = tr("EMERGENCY");
187 break;
188
189 case MAV_SEVERITY_ALERT:
190 severityText = tr("ALERT");
191 break;
192
193 case MAV_SEVERITY_CRITICAL:
194 severityText = tr("Critical");
195 break;
196
197 case MAV_SEVERITY_ERROR:
198 severityText = tr("Error");
199 break;
200
201 case MAV_SEVERITY_WARNING:
202 severityText = tr("Warning");
203 break;
204
205 case MAV_SEVERITY_NOTICE:
206 severityText = tr("Notice");
207 break;
208
209 case MAV_SEVERITY_INFO:
210 severityText = tr("Info");
211 break;
212
213 case MAV_SEVERITY_DEBUG:
214 severityText = tr("Debug");
215 break;
216
217 default:
218 qCWarning(StatusTextHandlerLog) << Q_FUNC_INFO << "Invalid MAV_SEVERITY";
219 break;
220 }
221
222 QString compString;
223 if (m_multiComp) {
224 compString = QString("COMP:%1").arg(compId);
225 }
226
227 const QString dateString = QDateTime::currentDateTime().toString("hh:mm:ss.zzz");
228
229 const QString formatText = QString("<font style=\"%1\">[%2 %3] %4: %5</font><br/>").arg(style, dateString, compString, severityText, htmlText);
230
231 StatusText* const message = new StatusText(compId, severity, text);
232 message->setFormatedText(formatText);
233
234 emit newFormattedMessage(formatText);
235
236 (void) m_messages.append(message);
237 const uint32_t count = m_messages.count();
238
239 _handleTextMessage(count, messageType);
240
241 if (message->severityIsError()) {
242 emit newErrorMessage(message->getText());
243 }
244}
245
247{
248 if (message.msgid != MAVLINK_MSG_ID_STATUSTEXT) {
249 return;
250 }
251
252 _handleStatusText(message);
253}
254
255void StatusTextHandler::_handleStatusText(const mavlink_message_t &message)
256{
257 mavlink_statustext_t statustext;
258 mavlink_msg_statustext_decode(&message, &statustext);
259
260 const QString messageText = getMessageText(message);
261 const bool includesNullTerminator = messageText.length() < MAVLINK_MSG_STATUSTEXT_FIELD_TEXT_LEN;
262
263 const MAV_COMPONENT compId = static_cast<MAV_COMPONENT>(message.compid);
264 if (m_chunkedStatusTextInfoMap.contains(compId) && (m_chunkedStatusTextInfoMap.value(compId).chunkId != statustext.id)) {
265 // We have an incomplete chunked status still pending
266 (void) m_chunkedStatusTextInfoMap.value(compId).rgMessageChunks.append(QString());
267 _chunkedStatusTextCompleted(compId);
268 }
269
270 if (statustext.id == 0) {
271 // Non-chunked status text. We still use common chunked text output mechanism.
272 ChunkedStatusTextInfo_t chunkedInfo;
273 chunkedInfo.chunkId = 0;
274 chunkedInfo.severity = static_cast<MAV_SEVERITY>(statustext.severity);
275 (void) chunkedInfo.rgMessageChunks.append(messageText);
276 (void) m_chunkedStatusTextInfoMap.insert(compId, chunkedInfo);
277 } else {
278 if (m_chunkedStatusTextInfoMap.contains(compId)) {
279 // A chunk sequence is in progress
280 QStringList& chunks = m_chunkedStatusTextInfoMap[compId].rgMessageChunks;
281 if (statustext.chunk_seq > chunks.size()) {
282 // We are missing some chunks in between, fill them in as missing
283 for (size_t i = chunks.size(); i < statustext.chunk_seq; i++) {
284 (void) chunks.append(QString());
285 }
286 }
287
288 (void) chunks.append(messageText);
289 } else {
290 // Starting a new chunk sequence
291 ChunkedStatusTextInfo_t chunkedInfo;
292 chunkedInfo.chunkId = statustext.id;
293 chunkedInfo.severity = static_cast<MAV_SEVERITY>(statustext.severity);
294 (void) chunkedInfo.rgMessageChunks.append(messageText);
295 (void) m_chunkedStatusTextInfoMap.insert(compId, chunkedInfo);
296 }
297
298 m_chunkedStatusTextTimer->start();
299 }
300
301 if ((statustext.id == 0) || includesNullTerminator) {
302 m_chunkedStatusTextTimer->stop();
303 _chunkedStatusTextCompleted(compId);
304 }
305}
306
307void StatusTextHandler::_chunkedStatusTextTimeout()
308{
309 for (auto compId : m_chunkedStatusTextInfoMap.keys()) {
310 auto& chunkedInfo = m_chunkedStatusTextInfoMap[compId];
311 (void) chunkedInfo.rgMessageChunks.append(QString());
312 _chunkedStatusTextCompleted(compId);
313 }
314}
315
316void StatusTextHandler::_chunkedStatusTextCompleted(MAV_COMPONENT compId)
317{
318 const ChunkedStatusTextInfo_t& chunkedInfo = m_chunkedStatusTextInfoMap.value(compId);
319 const MAV_SEVERITY severity = chunkedInfo.severity;
320
321 QString messageText;
322 for (const QString& chunk : std::as_const(chunkedInfo.rgMessageChunks)) {
323 if (chunk.isEmpty()) {
324 (void) messageText.append(tr(" ... ", "Indicates missing chunk from chunked STATUS_TEXT"));
325 } else {
326 (void) messageText.append(chunk);
327 }
328 }
329
330 (void) m_chunkedStatusTextInfoMap.remove(compId);
331
332 emit textMessageReceived(compId, severity, messageText, "");
333}
334
335void StatusTextHandler::_handleTextMessage(uint32_t newCount, MessageType messageType)
336{
337 if (newCount == 0) {
339 return;
340 }
341
342 switch (messageType) {
343 case MessageType::MessageNormal:
344 m_normalCount++;
345 break;
346
347 case MessageType::MessageWarning:
348 m_warningCount++;
349 break;
350
351 case MessageType::MessageError:
352 m_errorCount++;
353 m_errorCountTotal++;
354 break;
355
356 case MessageType::MessageNone:
357 default:
358 qCWarning(StatusTextHandlerLog) << Q_FUNC_INFO << "Invalid MessageType";
359 break;
360 }
361
362 const uint32_t count = getErrorCount() + getWarningCount() + getNormalCount();
363 if (count != messageCount()) {
364 m_messageCount = count;
366 }
367
368 // messageType represents the worst message which hasn't been viewed yet
369 MessageType newMessageType = MessageType::MessageNone;
370 if (getErrorCount() > 0) {
371 newMessageType = MessageType::MessageError;
372 } else if (getWarningCount() > 0) {
373 newMessageType = MessageType::MessageWarning;
374 } else if (getNormalCount() > 0) {
375 newMessageType = MessageType::MessageNormal;
376 }
377
378 if (newMessageType != m_messageType) {
379 m_messageType = newMessageType;
380 emit messageTypeChanged();
381 }
382}
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
StatusTextHandler(QObject *parent=nullptr)
void handleHTMLEscapedTextMessage(MAV_COMPONENT componentid, MAV_SEVERITY severity, const QString &text, const QString &description)
const QList< StatusText * > & messages() const
void newErrorMessage(QString message)
static QString getMessageText(const mavlink_message_t &message)
uint32_t getErrorCount() const
void mavlinkMessageReceived(const mavlink_message_t &message)
void messageCountChanged(uint32_t newCount)
void textMessageReceived(MAV_COMPONENT componentid, MAV_SEVERITY severity, QString text, QString description)
uint32_t messageCount() const
uint32_t getWarningCount() const
uint32_t getNormalCount() const
void newFormattedMessage(QString message)
QString formattedMessages() const
void messageTypeChanged()
bool severityIsError() const
void setFormatedText(const QString &formatedText)
QString getText() const