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  cerr << "Unable to open input zcm log: " << logpath << endl;
66  return 1;
67  }
68 
69  auto processLog = [&inlog](function<void(const zcm::LogEvent* evt)> processEvent) {
70  const zcm::LogEvent* evt;
71  off64_t offset;
72  static int lastPrintPercent = 0;
73 
74  fseeko(inlog.getFilePtr(), 0, SEEK_END);
75  off64_t logSize = ftello(inlog.getFilePtr());
76  fseeko(inlog.getFilePtr(), 0, SEEK_SET);
77 
78  QProgressDialog progress_dialog;
79  progress_dialog.setLabelText("Loading... please wait");
80  progress_dialog.setWindowModality(Qt::ApplicationModal);
81  progress_dialog.setRange(0, 100);
82  progress_dialog.setAutoClose(true);
83  progress_dialog.setAutoReset(true);
84  progress_dialog.show();
85 
86  bool interrupted = false;
87 
88  while (1) {
89  offset = ftello(inlog.getFilePtr());
90 
91  int percent = 100.0 * offset / (logSize == 0 ? 1 : logSize);
92  if (percent != lastPrintPercent) {
93  if (verbose) {
94  cout << "\r" << "Percent Complete: " << percent << flush;
95  }
96  lastPrintPercent = percent;
97 
98  progress_dialog.setValue(percent);
99  if (progress_dialog.wasCanceled()) {
100  interrupted = true;
101  break;
102  }
103  }
104 
105  evt = inlog.readNextEvent();
106  if (evt == nullptr) break;
107 
108  processEvent(evt);
109  }
110  if (verbose) {
111  if (lastPrintPercent != 100 && !interrupted)
112  cout << "\r" << "Percent Complete: 100" << flush;
113  cout << endl;
114  }
115 
116  if (interrupted) progress_dialog.cancel();
117  };
118 
119  processLog(processEvent);
120 
121  inlog.close();
122 
123  return 0;
124 }
125 
126 bool DataLoadZcm::refreshChannels(const string& filepath)
127 {
128  _all_channels.clear();
129  _all_channels_filepath = filepath;
130 
131  auto processEvent = [&](const zcm::LogEvent* evt){
132  _all_channels.insert(evt->channel);
133  };
134 
135  return processInputLog(filepath, processEvent) == 0;
136 }
137 
138 bool DataLoadZcm::launchDialog(const string& filepath)
139 {
140  QSettings settings;
141  _dialog->restoreGeometry(settings.value("DataLoadZcm.geometry").toByteArray());
142 
143  _ui->listWidgetChannels->clear();
144  for (auto& c : _all_channels) {
145  auto chan = QString::fromStdString(c);
146  _ui->listWidgetChannels->addItem(chan);
147  }
148  _ui->listWidgetChannels->sortItems();
149 
150  auto selected_channels = settings.value("DataLoadZcm.selected_channels").toStringList();
151  for (int row = 0; row<_ui->listWidgetChannels->count(); row++) {
152  auto item = _ui->listWidgetChannels->item(row);
153  if (selected_channels.contains(item->text())) {
154  item->setSelected(true);
155  }
156  }
157  selected_channels.clear();
158 
159  int res = _dialog->exec();
160  settings.setValue("DataLoadZcm.geometry", _dialog->saveGeometry());
161 
162  if (res == QDialog::Rejected) {
163  return false;
164  }
165 
166  _selected_channels.clear();
167 
168  QModelIndexList indexes = _ui->listWidgetChannels->selectionModel()->selectedRows();
169  for (auto& i : indexes) {
170  auto item = _ui->listWidgetChannels->item(i.row());
171  _selected_channels.insert(item->text().toStdString());
172  selected_channels.push_back(item->text());
173  }
174 
175  settings.setValue("DataLoadZcm.selected_channels", selected_channels);
176 
177  return !indexes.empty();
178 }
179 
180 template <typename T>
181 double toDouble(const void* data) {
182  return static_cast<double>(*reinterpret_cast<const T*>(data));
183 }
184 
186 {
187  vector<pair<string, double>>& numerics;
188  vector<pair<string, string>>& strings;
189 };
190 static void processData(const string& name, zcm_field_type_t type,
191  const void* data, void* usr)
192 {
193  ProcessUsr* v = (ProcessUsr*)usr;
194  switch (type) {
195  case ZCM_FIELD_INT8_T: v->numerics.emplace_back(name, toDouble<int8_t>(data)); break;
196  case ZCM_FIELD_INT16_T: v->numerics.emplace_back(name, toDouble<int16_t>(data)); break;
197  case ZCM_FIELD_INT32_T: v->numerics.emplace_back(name, toDouble<int32_t>(data)); break;
198  case ZCM_FIELD_INT64_T: v->numerics.emplace_back(name, toDouble<int64_t>(data)); break;
199  case ZCM_FIELD_BYTE: v->numerics.emplace_back(name, toDouble<uint8_t>(data)); break;
200  case ZCM_FIELD_FLOAT: v->numerics.emplace_back(name, toDouble<float>(data)); break;
201  case ZCM_FIELD_DOUBLE: v->numerics.emplace_back(name, toDouble<double>(data)); break;
202  case ZCM_FIELD_BOOLEAN: v->numerics.emplace_back(name, toDouble<bool>(data)); break;
203  case ZCM_FIELD_STRING: v->strings.emplace_back(name, string((const char*)data)); break;
204  case ZCM_FIELD_USER_TYPE: assert(false && "Should not be possble");
205  }
206 };
207 
209 {
210  string filepath = info->filename.toStdString();
211 
212  if (info->plugin_config.hasChildNodes()) {
213  xmlLoadState(info->plugin_config.firstChildElement());
214  }
215  if (filepath != _all_channels_filepath) {
216  refreshChannels(filepath);
217  }
218  if (!launchDialog(filepath)) {
219  return false;
220  }
221 
222  zcm::TypeDb types(_config_widget->getLibraries().toStdString());
223  if(!types.good())
224  {
225  QMessageBox::warning(nullptr, "Error", "Failed to load zcmtypes");
226  return false;
227  }
228 
229  vector<pair<string, double>> numerics;
230  vector<pair<string, string>> strings;
231  ProcessUsr usr = { numerics, strings };
232 
233  auto processEvent = [&](const zcm::LogEvent* evt){
234  if (_selected_channels.find(evt->channel) == _selected_channels.end()) return;
235  zcm::Introspection::processEncodedType(evt->channel,
236  evt->data, evt->datalen,
237  "/",
238  types, processData, &usr);
239  for (auto& n : usr.numerics) {
240  auto itr = plot_data.numeric.find(n.first);
241  if (itr == plot_data.numeric.end()) itr = plot_data.addNumeric(n.first);
242  itr->second.pushBack({ (double)evt->timestamp / 1e6, n.second });
243  }
244  for (auto& s : usr.strings) {
245  auto itr = plot_data.strings.find(s.first);
246  if (itr == plot_data.strings.end()) itr = plot_data.addStringSeries(s.first);
247  itr->second.pushBack({ (double)evt->timestamp / 1e6, s.second });
248  }
249 
250  usr.numerics.clear();
251  usr.strings.clear();
252  };
253 
254  if (processInputLog(filepath, processEvent) != 0)
255  return false;
256 
257  return true;
258 }
259 
260 static QDomElement serialize(const unordered_set<string>& input,
261  QDomDocument& doc, const string& name)
262 {
263  QDomElement ret = doc.createElement(QString::fromStdString(name));
264 
265  for (const auto& item : input) {
266  QDomElement stringElement = doc.createElement("String");
267  QDomText textNode = doc.createTextNode(QString::fromStdString(item));
268  stringElement.appendChild(textNode);
269  ret.appendChild(stringElement);
270  }
271 
272  return ret;
273 }
274 
275 static unordered_set<string> deserialize(const QDomElement& elt)
276 {
277  unordered_set<string> ret;
278 
279  QDomNodeList childNodes = elt.childNodes();
280  for (int i = 0; i < childNodes.size(); ++i) {
281  QDomNode node = childNodes.item(i);
282  if (node.isElement()) {
283  QDomElement element = node.toElement();
284  if (element.tagName() == "String") {
285  QString stringValue = element.text();
286  ret.insert(stringValue.toStdString());
287  }
288  }
289  }
290 
291  return ret;
292 }
293 
294 
295 bool DataLoadZcm::xmlSaveState(QDomDocument& doc, QDomElement& parent_element) const
296 {
297  parent_element.appendChild(serialize(_selected_channels, doc, "selected_channels"));
298  auto all_channels = serialize(_all_channels, doc, "all_channels");
299  all_channels.setAttribute("filepath", QString::fromStdString(_all_channels_filepath));
300  parent_element.appendChild(all_channels);
301  return true;
302 }
303 
304 bool DataLoadZcm::xmlLoadState(const QDomElement& parent_element)
305 {
306  QDomElement all_channels = parent_element.firstChildElement("all_channels");
307  if (!all_channels.isNull()) {
308  _all_channels = deserialize(all_channels);
309  if (all_channels.hasAttribute("filepath")) {
310  _all_channels_filepath = all_channels.attribute("filepath").toStdString();
311  }
312  }
313 
314  QDomElement selected_channels = parent_element.firstChildElement("selected_channels");
315  if (!selected_channels.isNull()) {
316  _selected_channels = deserialize(selected_channels);
317 
318  QStringList selected_channels;
319  for (auto& c : _selected_channels)
320  selected_channels.push_back(QString::fromStdString(c));
321 
322  QSettings settings;
323  settings.setValue("DataLoadZcm.selected_channels", selected_channels);
324  }
325 
326  return true;
327 }
bool launchDialog(const std::string &filepath)
const std::vector< const char * > & compatibleFileExtensions() const override
Provide a list of file extensions that this plugin can open.
virtual ~DataLoadZcm()
static bool verbose
XmlRpcServer s
QString filename
name of the file to open
bool readDataFromFile(PJ::FileLoadInfo *fileload_info, PlotDataMapRef &destination) override
type
Definition: core.h:1059
double toDouble(const void *data)
TimeseriesMap numeric
Numerical timeseries.
Definition: plotdata.h:38
#define assert(condition)
Definition: lz4.c:245
StringSeriesMap strings
Series of strings.
Definition: plotdata.h:45
TimeseriesMap::iterator addNumeric(const std::string &name, PlotGroup::Ptr group={})
Definition: plotdata.cpp:51
vector< pair< string, string > > & strings
vector< pair< string, double > > & numerics
StringSeriesMap::iterator addStringSeries(const std::string &name, PlotGroup::Ptr group={})
Definition: plotdata.cpp:63
const char * name() const override
Name of the plugin type, NOT the particular instance.
static int processInputLog(const string &logpath, function< void(const zcm::LogEvent *evt)> processEvent)
bool xmlLoadState(const QDomElement &parent_element) override
Override this method to load the status of the plugin from XML.
static unordered_set< string > deserialize(const QDomElement &elt)
bool xmlSaveState(QDomDocument &doc, QDomElement &parent_element) const override
Override this method to save the status of the plugin to XML.
QDomDocument plugin_config
Saved configuration from a previous run or a Layout file.
dictionary data
Definition: mqtt_test.py:22
bool refreshChannels(const std::string &filepath)
Definition: format.h:895
static void processData(const string &name, zcm_field_type_t type, const void *data, void *usr)
static QDomElement serialize(const unordered_set< string > &input, QDomDocument &doc, const string &name)


plotjuggler
Author(s): Davide Faconti
autogenerated on Mon Jun 19 2023 03:01:01