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