dataload_mcap.cpp
Go to the documentation of this file.
1 #include "dataload_mcap.h"
2 
5 
6 #include "mcap/reader.hpp"
7 #include "dialog_mcap.h"
8 
9 #include <QTextStream>
10 #include <QFile>
11 #include <QMessageBox>
12 #include <QDebug>
13 #include <QSettings>
14 #include <QProgressDialog>
15 #include <QDateTime>
16 #include <QInputDialog>
17 #include <QPushButton>
18 #include <QElapsedTimer>
19 #include <QStandardItemModel>
20 
21 #include <set>
22 
24 {
25 }
26 
28 {
29 }
30 
31 bool DataLoadMCAP::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
32 {
33  if (!_dialog_parameters)
34  {
35  return false;
36  }
37  QDomElement elem = doc.createElement("parameters");
38  const auto& params = *_dialog_parameters;
39  elem.setAttribute("use_timestamp", int(params.use_timestamp));
40  elem.setAttribute("use_mcap_log_time", int(params.use_mcap_log_time));
41  elem.setAttribute("clamp_large_arrays", int(params.clamp_large_arrays));
42  elem.setAttribute("max_array_size", params.max_array_size);
43  elem.setAttribute("selected_topics", params.selected_topics.join(';'));
44 
45  parent_element.appendChild(elem);
46  return true;
47 }
48 
49 bool DataLoadMCAP::xmlLoadState(const QDomElement& parent_element)
50 {
51  QDomElement elem = parent_element.firstChildElement("parameters");
52  if (elem.isNull())
53  {
55  return false;
56  }
57  mcap::LoadParams params;
58  params.use_timestamp = bool(elem.attribute("use_timestamp").toInt());
59  params.use_mcap_log_time = bool(elem.attribute("use_mcap_log_time").toInt());
60  params.clamp_large_arrays = bool(elem.attribute("clamp_large_arrays").toInt());
61  params.max_array_size = elem.attribute("max_array_size").toInt();
62  params.selected_topics = elem.attribute("selected_topics").split(';');
63  _dialog_parameters = params;
64  return true;
65 }
66 
67 const std::vector<const char*>& DataLoadMCAP::compatibleFileExtensions() const
68 {
69  static std::vector<const char*> ext = { "mcap", "MCAP" };
70  return ext;
71 }
72 
74 {
75  if (!parserFactories())
76  {
77  throw std::runtime_error("No parsing available");
78  }
79 
80  // open file
82  auto status = reader.open(info->filename.toStdString());
83  if (!status.ok())
84  {
85  QMessageBox::warning(nullptr, "Can't open file",
86  tr("Code: %0\n Message: %1")
87  .arg(int(status.code))
88  .arg(QString::fromStdString(status.message)));
89  return false;
90  }
91 
92  status = reader.readSummary(mcap::ReadSummaryMethod::NoFallbackScan);
93  if (!status.ok())
94  {
95  QMessageBox::warning(nullptr, "Can't open summary of the file",
96  tr("Code: %0\n Message: %1")
97  .arg(int(status.code))
98  .arg(QString::fromStdString(status.message)));
99  return false;
100  }
101  auto statistics = reader.statistics();
102 
103  std::unordered_map<int, mcap::SchemaPtr> mcap_schemas; // schema_id
104  std::unordered_map<int, mcap::ChannelPtr> channels; // channel_id
105  std::unordered_map<int, MessageParserPtr> parsers_by_channel; // channel_id
106 
107  std::unordered_map<int, DataTamerParser::Schema> dt_schames;
108  int total_dt_schemas = 0;
109 
110  std::unordered_set<mcap::ChannelId> channels_containing_datatamer_schema;
111  std::unordered_set<mcap::ChannelId> channels_containing_datatamer_data;
112 
113  for (const auto& [schema_id, schema_ptr] : reader.schemas())
114  {
115  mcap_schemas.insert({ schema_id, schema_ptr });
116  }
117 
118  std::set<QString> notified_encoding_problem;
119 
120  QElapsedTimer timer;
121  timer.start();
122 
123  for (const auto& [channel_id, channel_ptr] : reader.channels())
124  {
125  channels.insert({ channel_id, channel_ptr });
126  const auto& schema = mcap_schemas.at(channel_ptr->schemaId);
127  const auto& topic_name = channel_ptr->topic;
128  std::string definition(reinterpret_cast<const char*>(schema->data.data()),
129  schema->data.size());
130 
131  if (schema->name == "data_tamer_msgs/msg/Schemas")
132  {
133  channels_containing_datatamer_schema.insert(channel_id);
134  total_dt_schemas += statistics->channelMessageCounts.at(channel_id);
135  }
136  if (schema->name == "data_tamer_msgs/msg/Snapshot")
137  {
138  channels_containing_datatamer_data.insert(channel_id);
139  }
140 
141  QString channel_encoding = QString::fromStdString(channel_ptr->messageEncoding);
142  QString schema_encoding = QString::fromStdString(schema->encoding);
143 
144  auto it = parserFactories()->find(channel_encoding);
145 
146  if (it == parserFactories()->end())
147  {
148  it = parserFactories()->find(schema_encoding);
149  }
150 
151  if (it == parserFactories()->end())
152  {
153  // show message only once per encoding type
154  if (notified_encoding_problem.count(schema_encoding) == 0)
155  {
156  notified_encoding_problem.insert(schema_encoding);
157  auto msg = QString("No parser available for encoding [%0] nor [%1]")
158  .arg(channel_encoding)
159  .arg(schema_encoding);
160  QMessageBox::warning(nullptr, "Encoding problem", msg);
161  }
162  continue;
163  }
164 
165  auto& parser_factory = it->second;
166  auto parser =
167  parser_factory->createParser(topic_name, schema->name, definition, plot_data);
168  parsers_by_channel.insert({ channel_ptr->id, parser });
169  };
170 
171  if (!info->plugin_config.hasChildNodes())
172  {
174  }
175 
176  // don't show the dialog if we already loaded the parameters with xmlLoadState
177  if (!_dialog_parameters)
178  {
179  DialogMCAP dialog(channels, mcap_schemas, _dialog_parameters);
180  auto ret = dialog.exec();
181  if (ret != QDialog::Accepted)
182  {
183  return false;
184  }
185  _dialog_parameters = dialog.getParams();
186  }
187 
188  std::unordered_set<int> enabled_channels;
189 
190  for (const auto& [channel_id, parser] : parsers_by_channel)
191  {
192  parser->setLargeArraysPolicy(_dialog_parameters->clamp_large_arrays,
193  _dialog_parameters->max_array_size);
194  parser->enableEmbeddedTimestamp(_dialog_parameters->use_timestamp);
195 
196  QString topic_name = QString::fromStdString(channels[channel_id]->topic);
197  if (_dialog_parameters->selected_topics.contains(topic_name))
198  {
199  enabled_channels.insert(channel_id);
200  }
201  }
202 
203  //-------------------------------------------
204  //---------------- Parse messages -----------
205 
206  auto onProblem = [](const mcap::Status& problem) {
207  qDebug() << QString::fromStdString(problem.message);
208  };
209 
210  auto messages = reader.readMessages(onProblem);
211 
212  QProgressDialog progress_dialog("Loading... please wait", "Cancel", 0, 0, nullptr);
213  progress_dialog.setWindowTitle("Loading the MCAP file");
214  progress_dialog.setModal(true);
215  progress_dialog.setAutoClose(true);
216  progress_dialog.setAutoReset(true);
217  progress_dialog.setMinimumDuration(0);
218  progress_dialog.show();
219  progress_dialog.setValue(0);
220 
221  size_t msg_count = 0;
222 
223  for (const auto& msg_view : messages)
224  {
225  if (enabled_channels.count(msg_view.channel->id) == 0)
226  {
227  continue;
228  }
229 
230  // MCAP always represents publishTime in nanoseconds
231  double timestamp_sec = double(msg_view.message.publishTime) * 1e-9;
232  if (_dialog_parameters->use_mcap_log_time)
233  {
234  timestamp_sec = double(msg_view.message.logTime) * 1e-9;
235  }
236  auto parser_it = parsers_by_channel.find(msg_view.channel->id);
237  if (parser_it == parsers_by_channel.end())
238  {
239  qDebug() << "Skipping channeld id: " << msg_view.channel->id;
240  continue;
241  }
242 
243  auto parser = parser_it->second;
244  MessageRef msg(msg_view.message.data, msg_view.message.dataSize);
245  parser->parseMessage(msg, timestamp_sec);
246 
247  if (msg_count++ % 1000 == 0)
248  {
249  QApplication::processEvents();
250  if (progress_dialog.wasCanceled())
251  {
252  break;
253  }
254  }
255  }
256 
257  reader.close();
258  qDebug() << "Loaded file in " << timer.elapsed() << "milliseconds";
259  return true;
260 }
DialogMCAP
Definition: dialog_mcap.h:14
PJ::FileLoadInfo
Definition: dataloader_base.h:18
reader.hpp
mcap::ReadSummaryMethod::NoFallbackScan
@ NoFallbackScan
Parse the Summary section to produce seeking indexes and summary statistics. If the Summary section i...
mcap::LoadParams
Definition: dataload_params.h:8
definition
const char * definition()
DataLoadMCAP::~DataLoadMCAP
virtual ~DataLoadMCAP() override
Definition: dataload_mcap.cpp:27
dialog_mcap.h
mcap::Status
Wraps a status code and string message carrying additional context.
Definition: errors.hpp:35
PJ::FileLoadInfo::plugin_config
QDomDocument plugin_config
Saved configuration from a previous run or a Layout file.
Definition: dataloader_base.h:25
arg
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1875
mcap::McapReader
Provides a read interface to an MCAP file.
Definition: reader.hpp:271
mqtt_test_proto.msg
msg
Definition: mqtt_test_proto.py:43
dataload_mcap.h
mcap::LoadParams::max_array_size
unsigned max_array_size
Definition: dataload_params.h:11
mcap::LoadParams::clamp_large_arrays
bool clamp_large_arrays
Definition: dataload_params.h:12
start_test_publisher.topic
topic
Definition: start_test_publisher.py:22
data_tamer_parser.hpp
DataLoadMCAP::xmlLoadState
bool xmlLoadState(const QDomElement &parent_element) override
Override this method to load the status of the plugin from XML.
Definition: dataload_mcap.cpp:49
DialogMCAP::getParams
mcap::LoadParams getParams() const
Definition: dialog_mcap.cpp:90
DataLoadMCAP::xmlSaveState
bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const override
Override this method to save the status of the plugin to XML.
Definition: dataload_mcap.cpp:31
DataLoadMCAP::DataLoadMCAP
DataLoadMCAP()
Definition: dataload_mcap.cpp:23
udp_client.parser
parser
Definition: udp_client.py:9
DataLoadMCAP::compatibleFileExtensions
virtual const std::vector< const char * > & compatibleFileExtensions() const override
Provide a list of file extensions that this plugin can open.
Definition: dataload_mcap.cpp:67
mcap::LoadParams::selected_topics
QStringList selected_topics
Definition: dataload_params.h:10
reader
static const char * reader(lua_State *L, void *ud, size_t *size)
Definition: luac.c:126
PJ::MessageRef
Definition: messageparser_base.h:28
mcap::LoadParams::use_timestamp
bool use_timestamp
Definition: dataload_params.h:13
PJ::PlotDataMapRef
Definition: plotdata.h:34
DataLoadMCAP::readDataFromFile
virtual bool readDataFromFile(PJ::FileLoadInfo *fileload_info, PlotDataMapRef &destination) override
Definition: dataload_mcap.cpp:73
eprosima::fastcdr::nullopt
static constexpr nullopt_t nullopt
nullopt is a constant of type nullopt_t that is used to indicate optional type with uninitialized sta...
Definition: optional.hpp:40
messageparser_base.h
mqtt_test.ret
ret
Definition: mqtt_test.py:30
DataLoadMCAP::_dialog_parameters
std::optional< mcap::LoadParams > _dialog_parameters
Definition: dataload_mcap.h:38
PJ::DataLoader::parserFactories
const ParserFactories * parserFactories() const
Definition: dataloader_base.h:52
PJ::FileLoadInfo::filename
QString filename
name of the file to open
Definition: dataloader_base.h:21
mcap::LoadParams::use_mcap_log_time
bool use_mcap_log_time
Definition: dataload_params.h:14


plotjuggler
Author(s): Davide Faconti
autogenerated on Sun Jan 26 2025 03:23:23