dataload_zcm.cpp
Go to the documentation of this file.
1 #include "dataload_zcm.h"
2 
3 #include <QDebug>
4 #include <QFile>
5 #include <QInputDialog>
6 #include <QMainWindow>
7 #include <QMessageBox>
8 #include <QProgressDialog>
9 #include <QPushButton>
10 #include <QSettings>
11 #include <QTextStream>
12 #include <QWidget>
13 #include <QFileDialog>
14 
15 #include <iostream>
16 
17 #include <zcm/zcm-cpp.hpp>
18 #include <zcm/tools/Introspection.hpp>
19 
20 using namespace std;
21 
22 static bool verbose = false;
23 
25 {
26  _dialog = new QDialog();
27  _ui = new Ui::DialogZcm();
28  _ui->setupUi(_dialog);
29 
30  _config_widget = new ConfigZCM("DataLoadZcm", _dialog);
31  _ui->mainLayout->insertWidget(0, _config_widget, 1);
32 
33  _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
34 
35  _ui->listWidgetChannels->setSelectionMode(QAbstractItemView::ExtendedSelection);
36 
37  connect(_ui->listWidgetChannels, &QListWidget::itemSelectionChanged, this, [this]() {
38  auto selected = _ui->listWidgetChannels->selectionModel()->selectedIndexes();
39  bool box_enabled = selected.size() > 0;
40  _ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(box_enabled);
41  });
42 }
43 
45 {
46  delete _dialog;
47 }
48 
49 const char* DataLoadZcm::name() const
50 {
51  return "DataLoad Zcm";
52 }
53 
54 const vector<const char*>& DataLoadZcm::compatibleFileExtensions() const
55 {
56  static vector<const char*> extensions = { "zcmlog" };
57  return extensions;
58 }
59 
60 static int processInputLog(const string& logpath,
61  function<void(const zcm::LogEvent* evt)> processEvent)
62 {
63  zcm::LogFile inlog(logpath, "r");
64  if (!inlog.good())
65  {
66  cerr << "Unable to open input zcm log: " << logpath << endl;
67  return 1;
68  }
69 
70  auto processLog = [&inlog](function<void(const zcm::LogEvent* evt)> processEvent) {
71  const zcm::LogEvent* evt;
72  off64_t offset;
73  static int lastPrintPercent = 0;
74 
75  fseeko(inlog.getFilePtr(), 0, SEEK_END);
76  off64_t logSize = ftello(inlog.getFilePtr());
77  fseeko(inlog.getFilePtr(), 0, SEEK_SET);
78 
79  QProgressDialog progress_dialog;
80  progress_dialog.setLabelText("Loading... please wait");
81  progress_dialog.setWindowModality(Qt::ApplicationModal);
82  progress_dialog.setRange(0, 100);
83  progress_dialog.setAutoClose(true);
84  progress_dialog.setAutoReset(true);
85  progress_dialog.show();
86 
87  bool interrupted = false;
88 
89  while (1)
90  {
91  offset = ftello(inlog.getFilePtr());
92 
93  int percent = 100.0 * offset / (logSize == 0 ? 1 : logSize);
94  if (percent != lastPrintPercent)
95  {
96  if (verbose)
97  {
98  cout << "\r"
99  << "Percent Complete: " << percent << flush;
100  }
101  lastPrintPercent = percent;
102 
103  progress_dialog.setValue(percent);
104  if (progress_dialog.wasCanceled())
105  {
106  interrupted = true;
107  break;
108  }
109  }
110 
111  evt = inlog.readNextEvent();
112  if (evt == nullptr)
113  break;
114 
115  processEvent(evt);
116  }
117  if (verbose)
118  {
119  if (lastPrintPercent != 100 && !interrupted)
120  cout << "\r"
121  << "Percent Complete: 100" << flush;
122  cout << endl;
123  }
124 
125  if (interrupted)
126  progress_dialog.cancel();
127  };
128 
129  processLog(processEvent);
130 
131  inlog.close();
132 
133  return 0;
134 }
135 
136 bool DataLoadZcm::refreshChannels(const string& filepath)
137 {
138  _all_channels.clear();
139  _all_channels_filepath = filepath;
140 
141  auto processEvent = [&](const zcm::LogEvent* evt) {
142  _all_channels.insert(evt->channel);
143  };
144 
145  return processInputLog(filepath, processEvent) == 0;
146 }
147 
148 bool DataLoadZcm::launchDialog(const string& filepath)
149 {
150  QSettings settings;
151  _dialog->restoreGeometry(settings.value("DataLoadZcm.geometry").toByteArray());
152 
153  _ui->listWidgetChannels->clear();
154  for (auto& c : _all_channels)
155  {
156  auto chan = QString::fromStdString(c);
157  _ui->listWidgetChannels->addItem(chan);
158  }
159  _ui->listWidgetChannels->sortItems();
160 
161  auto selected_channels = settings.value("DataLoadZcm.selected_channels").toStringList();
162  for (int row = 0; row < _ui->listWidgetChannels->count(); row++)
163  {
164  auto item = _ui->listWidgetChannels->item(row);
165  if (selected_channels.contains(item->text()))
166  {
167  item->setSelected(true);
168  }
169  }
170  selected_channels.clear();
171 
172  int res = _dialog->exec();
173  settings.setValue("DataLoadZcm.geometry", _dialog->saveGeometry());
174 
175  if (res == QDialog::Rejected)
176  {
177  return false;
178  }
179 
180  _selected_channels.clear();
181 
182  QModelIndexList indexes = _ui->listWidgetChannels->selectionModel()->selectedRows();
183  for (auto& i : indexes)
184  {
185  auto item = _ui->listWidgetChannels->item(i.row());
186  _selected_channels.insert(item->text().toStdString());
187  selected_channels.push_back(item->text());
188  }
189 
190  settings.setValue("DataLoadZcm.selected_channels", selected_channels);
191 
192  return !indexes.empty();
193 }
194 
195 template <typename T>
196 double toDouble(const void* data)
197 {
198  return static_cast<double>(*reinterpret_cast<const T*>(data));
199 }
200 
202 {
203  vector<pair<string, double>>& numerics;
204  vector<pair<string, string>>& strings;
205 };
206 static void processData(const string& name, zcm_field_type_t type, const void* data,
207  void* usr)
208 {
209  ProcessUsr* v = (ProcessUsr*)usr;
210  switch (type)
211  {
212  case ZCM_FIELD_INT8_T:
213  v->numerics.emplace_back(name, toDouble<int8_t>(data));
214  break;
215  case ZCM_FIELD_INT16_T:
216  v->numerics.emplace_back(name, toDouble<int16_t>(data));
217  break;
218  case ZCM_FIELD_INT32_T:
219  v->numerics.emplace_back(name, toDouble<int32_t>(data));
220  break;
221  case ZCM_FIELD_INT64_T:
222  v->numerics.emplace_back(name, toDouble<int64_t>(data));
223  break;
224  case ZCM_FIELD_BYTE:
225  v->numerics.emplace_back(name, toDouble<uint8_t>(data));
226  break;
227  case ZCM_FIELD_FLOAT:
228  v->numerics.emplace_back(name, toDouble<float>(data));
229  break;
230  case ZCM_FIELD_DOUBLE:
231  v->numerics.emplace_back(name, toDouble<double>(data));
232  break;
233  case ZCM_FIELD_BOOLEAN:
234  v->numerics.emplace_back(name, toDouble<bool>(data));
235  break;
236  case ZCM_FIELD_STRING:
237  v->strings.emplace_back(name, string((const char*)data));
238  break;
239  case ZCM_FIELD_USER_TYPE:
240  assert(false && "Should not be possble");
241  }
242 };
243 
245 {
246  string filepath = info->filename.toStdString();
247 
248  if (info->plugin_config.hasChildNodes())
249  {
250  xmlLoadState(info->plugin_config.firstChildElement());
251  }
252  if (filepath != _all_channels_filepath)
253  {
254  refreshChannels(filepath);
255  }
256  if (!launchDialog(filepath))
257  {
258  return false;
259  }
260 
261  zcm::TypeDb types(_config_widget->getLibraries().toStdString());
262  if (!types.good())
263  {
264  QMessageBox::warning(nullptr, "Error", "Failed to load zcmtypes");
265  return false;
266  }
267 
268  vector<pair<string, double>> numerics;
269  vector<pair<string, string>> strings;
270  ProcessUsr usr = { numerics, strings };
271 
272  auto processEvent = [&](const zcm::LogEvent* evt) {
273  if (_selected_channels.find(evt->channel) == _selected_channels.end())
274  {
275  return;
276  }
277 
278  if (evt->datalen == 0)
279  {
280  auto itr = plot_data.numeric.find(evt->channel);
281  if (itr == plot_data.numeric.end())
282  itr = plot_data.addNumeric(evt->channel);
283  itr->second.pushBack({ (double)evt->timestamp / 1e6, 0 });
284  return;
285  }
286 
287  zcm::Introspection::processEncodedType(evt->channel, evt->data, evt->datalen, "/",
288  types, processData, &usr);
289  for (auto& n : usr.numerics)
290  {
291  auto itr = plot_data.numeric.find(n.first);
292  if (itr == plot_data.numeric.end())
293  itr = plot_data.addNumeric(n.first);
294  itr->second.pushBack({ (double)evt->timestamp / 1e6, n.second });
295  }
296  for (auto& s : usr.strings)
297  {
298  auto itr = plot_data.strings.find(s.first);
299  if (itr == plot_data.strings.end())
300  itr = plot_data.addStringSeries(s.first);
301  itr->second.pushBack({ (double)evt->timestamp / 1e6, s.second });
302  }
303 
304  usr.numerics.clear();
305  usr.strings.clear();
306  };
307 
308  if (processInputLog(filepath, processEvent) != 0)
309  return false;
310 
311  return true;
312 }
313 
314 static QDomElement serialize(const unordered_set<string>& input, QDomDocument& doc,
315  const string& name)
316 {
317  QDomElement ret = doc.createElement(QString::fromStdString(name));
318 
319  for (const auto& item : input)
320  {
321  QDomElement stringElement = doc.createElement("String");
322  QDomText textNode = doc.createTextNode(QString::fromStdString(item));
323  stringElement.appendChild(textNode);
324  ret.appendChild(stringElement);
325  }
326 
327  return ret;
328 }
329 
330 static unordered_set<string> deserialize(const QDomElement& elt)
331 {
332  unordered_set<string> ret;
333 
334  QDomNodeList childNodes = elt.childNodes();
335  for (int i = 0; i < childNodes.size(); ++i)
336  {
337  QDomNode node = childNodes.item(i);
338  if (node.isElement())
339  {
340  QDomElement element = node.toElement();
341  if (element.tagName() == "String")
342  {
343  QString stringValue = element.text();
344  ret.insert(stringValue.toStdString());
345  }
346  }
347  }
348 
349  return ret;
350 }
351 
352 bool DataLoadZcm::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
353 {
354  parent_element.appendChild(serialize(_selected_channels, doc, "selected_channels"));
355  auto all_channels = serialize(_all_channels, doc, "all_channels");
356  all_channels.setAttribute("filepath", QString::fromStdString(_all_channels_filepath));
357  parent_element.appendChild(all_channels);
358  return true;
359 }
360 
361 bool DataLoadZcm::xmlLoadState(const QDomElement& parent_element)
362 {
363  QDomElement all_channels = parent_element.firstChildElement("all_channels");
364  if (!all_channels.isNull())
365  {
366  _all_channels = deserialize(all_channels);
367  if (all_channels.hasAttribute("filepath"))
368  {
369  _all_channels_filepath = all_channels.attribute("filepath").toStdString();
370  }
371  }
372 
373  QDomElement selected_channels = parent_element.firstChildElement("selected_channels");
374  if (!selected_channels.isNull())
375  {
376  _selected_channels = deserialize(selected_channels);
377 
378  QStringList selected_channels;
379  for (auto& c : _selected_channels)
380  selected_channels.push_back(QString::fromStdString(c));
381 
382  QSettings settings;
383  settings.setValue("DataLoadZcm.selected_channels", selected_channels);
384  }
385 
386  return true;
387 }
dataload_zcm.h
DataLoadZcm::refreshChannels
bool refreshChannels(const std::string &filepath)
Definition: dataload_zcm.cpp:136
DataLoadZcm::launchDialog
bool launchDialog(const std::string &filepath)
Definition: dataload_zcm.cpp:148
PJ::PlotDataMapRef::addStringSeries
StringSeriesMap::iterator addStringSeries(const std::string &name, PlotGroup::Ptr group={})
Definition: plotdata.cpp:63
DataLoadZcm::~DataLoadZcm
virtual ~DataLoadZcm()
Definition: dataload_zcm.cpp:44
PJ::FileLoadInfo
Definition: dataloader_base.h:18
ConfigZCM
Definition: config_zcm.h:10
s
XmlRpcServer s
PJ::PlotDataMapRef::addNumeric
TimeseriesMap::iterator addNumeric(const std::string &name, PlotGroup::Ptr group={})
Definition: plotdata.cpp:51
PJ::FileLoadInfo::plugin_config
QDomDocument plugin_config
Saved configuration from a previous run or a Layout file.
Definition: dataloader_base.h:25
deserialize
static unordered_set< string > deserialize(const QDomElement &elt)
Definition: dataload_zcm.cpp:330
processInputLog
static int processInputLog(const string &logpath, function< void(const zcm::LogEvent *evt)> processEvent)
Definition: dataload_zcm.cpp:60
PJ::PlotDataMapRef::numeric
TimeseriesMap numeric
Numerical timeseries.
Definition: plotdata.h:39
ProcessUsr::strings
vector< pair< string, string > > & strings
Definition: dataload_zcm.cpp:204
DataLoadZcm::readDataFromFile
bool readDataFromFile(PJ::FileLoadInfo *fileload_info, PlotDataMapRef &destination) override
Definition: dataload_zcm.cpp:244
processData
static void processData(const string &name, zcm_field_type_t type, const void *data, void *usr)
Definition: dataload_zcm.cpp:206
nlohmann::detail::void
j template void())
Definition: json.hpp:4061
assert
#define assert(condition)
Definition: lz4.c:271
DataLoadZcm::DataLoadZcm
DataLoadZcm()
Definition: dataload_zcm.cpp:24
DataLoadZcm::name
const char * name() const override
Name of the plugin type, NOT the particular instance.
Definition: dataload_zcm.cpp:49
toDouble
double toDouble(const void *data)
Definition: dataload_zcm.cpp:196
DataLoadZcm::xmlSaveState
bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const override
Override this method to save the status of the plugin to XML.
Definition: dataload_zcm.cpp:352
DataLoadZcm::xmlLoadState
bool xmlLoadState(const QDomElement &parent_element) override
Override this method to load the status of the plugin from XML.
Definition: dataload_zcm.cpp:361
std
ProcessUsr::numerics
vector< pair< string, double > > & numerics
Definition: dataload_zcm.cpp:203
mqtt_test.data
dictionary data
Definition: mqtt_test.py:22
ProcessUsr
Definition: dataload_zcm.cpp:201
PJ::PlotDataMapRef
Definition: plotdata.h:34
verbose
static bool verbose
Definition: dataload_zcm.cpp:22
serialize
static QDomElement serialize(const unordered_set< string > &input, QDomDocument &doc, const string &name)
Definition: dataload_zcm.cpp:314
PJ::PlotDataMapRef::strings
StringSeriesMap strings
Series of strings.
Definition: plotdata.h:46
mqtt_test.ret
ret
Definition: mqtt_test.py:30
DataLoadZcm::compatibleFileExtensions
const std::vector< const char * > & compatibleFileExtensions() const override
Provide a list of file extensions that this plugin can open.
Definition: dataload_zcm.cpp:54
PJ::FileLoadInfo::filename
QString filename
name of the file to open
Definition: dataloader_base.h:21


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Nov 11 2024 03:23:44