QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PX4ULogUtility.cc
Go to the documentation of this file.
1#include "PX4ULogUtility.h"
2
3#include <cstring>
4#include <ulog_cpp/reader.hpp>
5
6#include "LittleEndian.h"
8
9QGC_LOGGING_CATEGORY(PX4ULogUtilityLog, "Utilities.PX4ULogUtility")
10
12{
13
14// ============================================================================
15// Header Validation
16// ============================================================================
17
18bool isValidHeader(const char *data, qint64 size)
19{
20 if (size < kMagicSize) {
21 return false;
22 }
23 return memcmp(data, kMagicBytes, kMagicSize) == 0;
24}
25
26int getVersion(const char *data, qint64 size)
27{
28 if (size < 5) {
29 return -1;
30 }
31 if (!isValidHeader(data, size)) {
32 return -1;
33 }
34 return static_cast<uint8_t>(data[4]);
35}
36
37uint64_t getHeaderTimestamp(const char *data, qint64 size)
38{
39 if (size < kHeaderSize) {
40 return 0;
41 }
42 if (!isValidHeader(data, size)) {
43 return 0;
44 }
45
46 const std::span<const uint8_t> header{reinterpret_cast<const uint8_t*>(data), kHeaderSize};
47 return *LittleEndian::read<uint64_t>(header, 8);
48}
49
50// ============================================================================
51// MessageHandler Implementation
52// ============================================================================
53
54MessageHandler::MessageHandler(const std::string &messageName,
55 const MessageCallback &callback,
56 QString &errorMsg)
57 : _targetMessageName(messageName)
58 , _callback(callback)
59 , _errorMessage(errorMsg)
60{
61}
62
63void MessageHandler::error(const std::string &msg, bool is_recoverable)
64{
65 if (!is_recoverable) {
66 _hadFatalError = true;
67 }
68 if (!_errorMessage.isEmpty()) {
69 _errorMessage.append(QStringLiteral(", "));
70 }
71 _errorMessage.append(QString::fromStdString(msg));
72}
73
74void MessageHandler::messageFormat(const ulog_cpp::MessageFormat &message_format)
75{
76 if (message_format.name() == _targetMessageName) {
77 _messageFormat = std::make_shared<ulog_cpp::MessageFormat>(message_format);
78 }
79}
80
81void MessageHandler::addLoggedMessage(const ulog_cpp::AddLoggedMessage &add_logged_message)
82{
83 if (add_logged_message.messageName() == _targetMessageName && _messageFormat) {
84 _messageIds.insert(add_logged_message.msgId());
85 }
86}
87
89{
90 _headerComplete = true;
91 if (_messageFormat) {
92 _messageFormat->resolveDefinition({{_targetMessageName, _messageFormat}});
93 }
94}
95
96void MessageHandler::data(const ulog_cpp::Data &data)
97{
98 if (!_headerComplete || !_messageFormat) {
99 return;
100 }
101
102 if (!_messageIds.contains(data.msgId())) {
103 return;
104 }
105
106 try {
107 const ulog_cpp::TypedDataView typedData(data, *_messageFormat);
108 ++_messageCount;
109 if (!_callback(typedData)) {
110 // Callback returned false, stop processing
111 // Note: ulog_cpp doesn't support early termination, so we just skip remaining
112 }
113 } catch (const ulog_cpp::AccessException &exception) {
114 qCWarning(PX4ULogUtilityLog) << "Failed to parse" << QString::fromStdString(_targetMessageName)
115 << ":" << exception.what();
116 QStringList fields;
117 for (const std::string &name : _messageFormat->fieldNames()) {
118 fields.append(QString::fromStdString(name));
119 }
120 qCDebug(PX4ULogUtilityLog) << "Available fields:" << fields;
121 }
122}
123
124// ============================================================================
125// Message Iteration
126// ============================================================================
127
128bool iterateMessages(const char *data, qint64 size,
129 const std::string &messageName,
130 const MessageCallback &callback,
131 QString &errorMessage)
132{
133 errorMessage.clear();
134
135 auto handler = std::make_shared<MessageHandler>(messageName, callback, errorMessage);
136 ulog_cpp::Reader parser(handler);
137 parser.readChunk(reinterpret_cast<const uint8_t*>(data), static_cast<size_t>(size));
138
139 if (handler->hadFatalError()) {
140 errorMessage = QStringLiteral("Could not parse ULog");
141 return false;
142 }
143
144 if (!handler->isHeaderComplete()) {
145 errorMessage = QStringLiteral("Could not parse ULog header");
146 return false;
147 }
148
149 return true;
150}
151
152} // namespace PX4ULogUtility
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void messageFormat(const ulog_cpp::MessageFormat &message_format) override
void addLoggedMessage(const ulog_cpp::AddLoggedMessage &add_logged_message) override
void error(const std::string &msg, bool is_recoverable) override
void data(const ulog_cpp::Data &data) override
uint64_t getHeaderTimestamp(const char *data, qint64 size)
int getVersion(const char *data, qint64 size)
std::function< bool(const ulog_cpp::TypedDataView &sample)> MessageCallback
constexpr int kMagicSize
bool iterateMessages(const char *data, qint64 size, const std::string &messageName, const MessageCallback &callback, QString &errorMessage)
constexpr char kMagicBytes[]
ULog file magic bytes: "ULog" followed by 0x01 (version 1) followed by 0x12 (file compat)
constexpr int kHeaderSize