6#include <QtCore/QStringList>
7#include <QtCore/QTimeZone>
12#include <ulog_cpp/subscription.hpp>
21QString _px4NavStateName(
int state)
25 case 0:
return QStringLiteral(
"Manual");
26 case 1:
return QStringLiteral(
"Altitude");
27 case 2:
return QStringLiteral(
"Position");
28 case 3:
return QStringLiteral(
"Mission");
29 case 4:
return QStringLiteral(
"Hold");
30 case 5:
return QStringLiteral(
"Return");
31 case 6:
return QStringLiteral(
"Position Slow");
32 case 8:
return QStringLiteral(
"Altitude Cruise");
33 case 10:
return QStringLiteral(
"Acro");
34 case 12:
return QStringLiteral(
"Descend");
35 case 13:
return QStringLiteral(
"Termination");
36 case 14:
return QStringLiteral(
"Offboard");
37 case 15:
return QStringLiteral(
"Stabilized");
38 case 17:
return QStringLiteral(
"Takeoff");
39 case 18:
return QStringLiteral(
"Land");
40 case 19:
return QStringLiteral(
"Follow Target");
41 case 20:
return QStringLiteral(
"Precision Land");
42 case 21:
return QStringLiteral(
"Orbit");
43 case 22:
return QStringLiteral(
"VTOL Takeoff");
44 default:
return QStringLiteral(
"Mode %1").arg(state);
48bool _isNumericBasicType(ulog_cpp::Field::BasicType type)
50 using BT = ulog_cpp::Field::BasicType;
78 const QString errorMessage = QString::fromStdString(msg);
79 if (!is_recoverable) {
80 _hadFatalError =
true;
85 qCWarning(ULogFullHandlerLog) <<
"ULog parse error:" << errorMessage;
90 _logStartSecs =
static_cast<double>(header.header().timestamp) / 1e6;
95 _formats[message_format.name()] = std::make_shared<ulog_cpp::MessageFormat>(message_format);
100 const auto it = _formats.find(add_logged_message.messageName());
101 if (it != _formats.cend()) {
102 _subscriptions[add_logged_message.msgId()] = {
104 add_logged_message.multiId(),
105 add_logged_message.messageName()
112 _headerComplete =
true;
113 for (
auto &[name, fmt] : _formats) {
114 fmt->resolveDefinition(_formats);
120 if (!_headerComplete) {
124 const auto it = _subscriptions.find(
data.msgId());
125 if (it == _subscriptions.cend()) {
129 const SubscriptionInfo &sub = it->second;
135 const ulog_cpp::TypedDataView view(
data, *sub.format);
138 double timestampSecs = -1.0;
139 if (sub.format->fieldMap().count(
"timestamp") > 0) {
140 const uint64_t tsUs = view.at(
"timestamp").as<uint64_t>();
141 timestampSecs =
static_cast<double>(tsUs) / 1e6;
143 if (timestampSecs < _logStartSecs) {
144 timestampSecs = _logStartSecs;
147 _lastTimestampSecs = timestampSecs;
152#ifndef QGC_NO_LOG_START_TIME
153 if (_result.
startTime.isNull() && timestampSecs >= 0.0) {
154 if ((sub.topicName ==
"sensor_gps" || sub.topicName ==
"vehicle_gps_position")
155 && sub.format->fieldMap().count(
"time_utc_usec") > 0) {
156 const uint64_t utcUsec = view.at(
"time_utc_usec").as<uint64_t>();
157 const uint64_t tsUs = view.at(
"timestamp").as<uint64_t>();
158 if (utcUsec > 0 && utcUsec >= tsUs) {
159 const qint64 startMs =
static_cast<qint64
>((utcUsec - tsUs) / 1000);
160 _result.
startTime = QDateTime::fromMSecsSinceEpoch(startMs, QTimeZone::utc());
167 const QString prefix = (sub.multiId > 0)
168 ? QStringLiteral(
"%1[%2].").arg(QString::fromStdString(sub.topicName)).arg(sub.multiId)
169 : QString::fromStdString(sub.topicName) + QLatin1Char(
'.');
171 for (
const auto &field : sub.format->fields()) {
173 if (field->name().rfind(
"_padding", 0) == 0) {
176 if (field->name() ==
"timestamp") {
179 if (!field->definitionResolved()) {
183 const QString fieldName = prefix + QString::fromStdString(field->name());
184 _collectFieldSamples(fieldName, *field, view.at(field), timestampSecs);
189 if (timestampSecs >= 0.0) {
195 }
catch (
const std::exception &e) {
196 qCWarning(ULogFullHandlerLog) <<
"Failed to decode data message:" << e.what();
200void ULogFullHandler::_collectFieldSamples(
const QString& fieldName,
const ulog_cpp::Field& field,
201 const ulog_cpp::Value& value,
double timestampSecs)
203 if (field.arrayLength() < 0) {
204 _collectElementSamples(fieldName, field, value, timestampSecs);
209 if (field.type().type == ulog_cpp::Field::BasicType::CHAR) {
210 _fieldSet.insert(fieldName);
214 for (
int i = 0; i < field.arrayLength(); i++) {
215 _collectElementSamples(QStringLiteral(
"%1[%2]").arg(fieldName).arg(i), field, value[
static_cast<size_t>(i)],
220void ULogFullHandler::_collectElementSamples(
const QString& fieldName,
const ulog_cpp::Field& field,
221 const ulog_cpp::Value& value,
double timestampSecs)
223 if (field.type().type == ulog_cpp::Field::BasicType::NESTED) {
224 for (
const auto& nestedField : field.nestedFormat()->fields()) {
225 if (nestedField->name().rfind(
"_padding", 0) == 0) {
228 if (!nestedField->definitionResolved()) {
231 const QString nestedFieldName = fieldName + QLatin1Char(
'.') + QString::fromStdString(nestedField->name());
232 _collectFieldSamples(nestedFieldName, *nestedField, value[*nestedField], timestampSecs);
237 _fieldSet.insert(fieldName);
239 if (!_isNumericBasicType(field.type().type) || (timestampSecs < 0.0)) {
243 _result.
fieldSamples[fieldName].append(QPointF(timestampSecs, value.as<
double>()));
244 _plottableFieldSet.insert(fieldName);
249 const double timestampSecs =
static_cast<double>(
logging.timestamp()) / 1e6;
250 const QString text = QString::fromStdString(
logging.message());
252 if (text.isEmpty()) {
257 msgRow[QStringLiteral(
"time")] = timestampSecs;
258 msgRow[QStringLiteral(
"text")] = text;
261 using Level = ulog_cpp::Logging::Level;
262 if (
logging.logLevel() <= Level::Warning) {
263 const QString eventType = (
logging.logLevel() <= Level::Error)
264 ? QStringLiteral(
"error")
265 : QStringLiteral(
"warning");
267 if (timestampSecs >= 0.0) {
268 QVariantMap eventRow;
269 eventRow[QStringLiteral(
"time")] = timestampSecs;
270 eventRow[QStringLiteral(
"type")] = eventType;
271 eventRow[QStringLiteral(
"description")] = text;
272 _result.
events.append(eventRow);
279 const QString name = QString::fromStdString(param.field().name());
280 if (name.isEmpty()) {
285 bool isFloat =
false;
289 value = param.value().as<
double>();
290 isFloat = (param.field().type().type == ulog_cpp::Field::BasicType::FLOAT);
291 }
catch (
const std::exception &) {
296 row[QStringLiteral(
"name")] = name;
297 row[QStringLiteral(
"value")] = value;
298 row[QStringLiteral(
"isFloat")] = isFloat;
306 const QString name = QString::fromStdString(param_default.field().name());
307 if (name.isEmpty()) {
311 const double defaultVal = param_default.value().as<
double>();
314 using DT = ulog_cpp::ulog_parameter_default_type_t;
315 if ((
static_cast<uint8_t
>(param_default.defaultType()) &
316 static_cast<uint8_t
>(DT::system)) != 0) {
317 _paramDefaults[name] = defaultVal;
319 }
catch (
const std::exception &) {
326 if (_lastTimestampSecs < 0.0) {
330 const double start = _lastTimestampSecs;
331 const double end = start +
static_cast<double>(
dropout.durationMs()) / 1000.0;
333 row[QStringLiteral(
"start")] = start;
334 row[QStringLiteral(
"end")] = end;
340 if (_staleSampleCount > 0) {
341 qCDebug(ULogFullHandlerLog) <<
"Moved" << _staleSampleCount <<
"samples older than log start to"
342 << _logStartSecs <<
"s";
347 const auto vehicleTypeIt = _result.
fieldSamples.constFind(QStringLiteral(
"vehicle_status.vehicle_type"));
348 if (vehicleTypeIt != _result.
fieldSamples.cend() && !vehicleTypeIt->isEmpty()) {
349 const int vtype =
static_cast<int>(vehicleTypeIt->first().y());
361 const auto navStateIt = _result.
fieldSamples.constFind(QStringLiteral(
"vehicle_status.nav_state"));
363 const QVector<QPointF> &samples = navStateIt.value();
364 int lastNavState = -1;
365 double segmentStart = -1.0;
368 for (
const QPointF &pt : samples) {
369 const int navState =
static_cast<int>(pt.y());
370 if (navState != lastNavState) {
372 if (lastNavState >= 0 && segmentStart >= 0.0) {
374 seg[QStringLiteral(
"mode")] = segmentMode;
375 seg[QStringLiteral(
"start")] = segmentStart;
376 seg[QStringLiteral(
"end")] = pt.x();
379 lastNavState = navState;
380 segmentStart = pt.x();
381 segmentMode = _px4NavStateName(navState);
386 if (lastNavState >= 0 && segmentStart >= 0.0 && _result.
maxTimestamp >= segmentStart) {
388 seg[QStringLiteral(
"mode")] = segmentMode;
389 seg[QStringLiteral(
"start")] = segmentStart;
403 for (
int i = 0; i < _result.
parameters.size(); i++) {
404 QVariantMap row = _result.
parameters[i].toMap();
405 const QString name = row.value(QStringLiteral(
"name")).toString();
406 const auto it = _paramDefaults.constFind(name);
407 if (it != _paramDefaults.constEnd()) {
408 const double defaultVal = it.value();
409 const QVariant valueVariant = row.value(QStringLiteral(
"value"));
410 row[QStringLiteral(
"hasDefault")] =
true;
411 row[QStringLiteral(
"defaultValue")] = defaultVal;
415 bool isDefault =
false;
416 if (valueVariant.isValid() && valueVariant.canConvert<
double>()) {
417 const double currentVal = valueVariant.toDouble();
420 isDefault = (currentVal == defaultVal)
421 || (!qFuzzyIsNull(defaultVal) && qFuzzyCompare(currentVal, defaultVal));
423 row[QStringLiteral(
"isDefault")] = isDefault;
425 row[QStringLiteral(
"hasDefault")] =
false;
426 row[QStringLiteral(
"defaultValue")] = QVariant();
427 row[QStringLiteral(
"isDefault")] =
false;
std::function< void(float)> ProgressCallback
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void parameter(const ulog_cpp::Parameter ¶meter) override
void fileHeader(const ulog_cpp::FileHeader &header) override
ULogFullHandler(LogParseResult &result, const ProgressCallback &progressCallback=nullptr)
void addLoggedMessage(const ulog_cpp::AddLoggedMessage &add_logged_message) override
void error(const std::string &msg, bool is_recoverable) override
void logging(const ulog_cpp::Logging &logging) override
void messageFormat(const ulog_cpp::MessageFormat &message_format) override
void parameterDefault(const ulog_cpp::ParameterDefault ¶meter_default) override
void data(const ulog_cpp::Data &data) override
void dropout(const ulog_cpp::Dropout &dropout) override
void headerComplete() override
QString detectedVehicleType
QVariantList modeSegments
QStringList plottableFields
QStringList availableFields
QHash< QString, QVector< QPointF > > fieldSamples