PlotterController.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5  *
6  * ArmarX is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * ArmarX is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * @package ArmarX
19  * @author Mirko Waechter( mirko.waechter at kit dot edu)
20  * @date 2016
21  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22  * GNU General Public License
23  */
24 
25 #include "PlotterController.h"
26 
27 #include <chrono>
28 #include <cmath>
29 
30 #include <qwt_legend_label.h>
31 
33 
34 namespace armarx
35 {
37  QObject(parent),
38  plotter(new QwtPlot())
39  {
40 
42  Qt::green,
43  Qt::blue,
44  Qt::cyan,
45  Qt::magenta,
46  Qt::darkRed,
47  Qt::darkGreen,
48  Qt::darkBlue,
49  Qt::darkCyan,
50  Qt::darkMagenta
51  };
52 
53  qRegisterMetaType<std::map<std::string, VariantPtr>>("std::map<std::string, VariantPtr>");
54  widget = new QWidget();
55  widget->setMinimumHeight(150);
56  // widget->setMaximumWidth(600);
57  // widget->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::MinimumExpanding));
58  ////////////////
59  // Setup Plotter
60  ///////////////
61  // panning with the left mouse button
62  (void) new QwtPlotPanner(plotter->canvas());
63 
64  // zoom in/out with the wheel
65  QwtPlotMagnifier* magnifier = new QwtPlotMagnifier(plotter->canvas());
66  magnifier->setAxisEnabled(QwtPlot::xBottom, false);
67 
68  dynamic_cast<QwtPlotCanvas&>(*plotter->canvas()).setPaintAttribute(QwtPlotCanvas::BackingStore, false); //increases performance for incremental drawing
69 
70  QwtLegend* legend = new QwtLegend;
71  legend->setDefaultItemMode(QwtLegendData::Mode::Checkable);
72  plotter->insertLegend(legend, QwtPlot::BottomLegend);
73 
74 
75  plotter->setAxisTitle(QwtPlot::xBottom, "Time (in sec)");
76  plotter->enableAxis(QwtPlot::yLeft, false);
77 
78  plotter->enableAxis(QwtPlot::yRight, true);
79  plotter->setAxisAutoScale(QwtPlot::yRight, true);
80  // plotter->setAutoReplot();
81 
82 
83  // plotter->setCanvasBackground(* new QBrush(Qt::white));
84 
85  connect(legend, SIGNAL(checked(const QVariant&, bool, int)),
86  SLOT(legendChecked(const QVariant&, bool)));
87 
88  connect(&timer, SIGNAL(timeout()), this, SLOT(updateGraph()));
89  stackedLayout = new QStackedLayout(widget);
90  stackedLayout->addWidget(plotter);
91 
92  barlayout = new QHBoxLayout;
93  QWidget* barsWidget = new QWidget();
94  barsWidget->setLayout(barlayout);
95  barsWidget->setAccessibleName("barsWidget");
96  stackedLayout->addWidget(barsWidget);
97  }
98 
100  {
101  ARMARX_DEBUG << "~PlotterController";
102  if (!widget.isNull())
103  {
104  widget->deleteLater();
105  }
106  timer.stop();
107  if (pollingTask)
108  {
109  pollingTask->stop();
110  }
111  }
112 
114  {
115  return widget;
116  }
117 
119  {
120  std::map<std::string, VariantPtr> newData = getData(selectedDatafields);
121 
122 
123  emit newDataAvailable(TimeUtil::GetTime().toMicroSeconds(), newData);
124  }
125 
127  {
128  if (this->__plottingPaused)
129  {
130  return;
131  }
132  std::lock_guard lock(dataMutex);
133 
134  IceUtil::Time curTime = TimeUtil::GetTime();
135  // erase old markers
136  for (auto& markerChannel : markers)
137  {
138  for (auto it = markerChannel.second.begin(); it != markerChannel.second.end();)
139  {
140  if ((curTime - it->first).toSecondsDouble() > shownInterval)
141  {
142  for (auto elem : it->second)
143  {
144  elem.second->detach();
145  }
146  it = markerChannel.second.erase(it);
147  }
148  else
149  {
150  it++;
151  }
152  }
153  }
154 
155  GraphDataMap::iterator it = dataMap.begin();
156 
157  auto addMarker = [this](IceUtil::Time & age, VariantPtr & var, std::string & datafieldId)
158  {
159  std::map < std::string, QwtPlotMarkerPtr>& markerSubMap = markers[datafieldId][age];
160 
161  std::string value = var->getString();
162  auto it2 = markerSubMap.find(value);
163  if (it2 != markerSubMap.end())
164  {
165  QwtPlotMarkerPtr marker = it2->second;
166  marker->setXValue(0.001 * age.toMilliSecondsDouble());
167  }
168  else
169  {
170  QwtPlotMarkerPtr marker(new QwtPlotMarker());
171  marker->setValue(0.001 * age.toMilliSecondsDouble(), 0);
172  marker->setLabel(QwtText(QString::fromStdString(value)));
173  marker->setLineStyle(QwtPlotMarker::VLine);
174  marker->setLabelAlignment(Qt::AlignBottom | Qt::AlignLeft);
175  int i = 0;
176  for (auto& markerSubMap : markers)
177  {
178  if (markerSubMap.first == datafieldId)
179  {
180  break;
181  }
182  i++;
183  }
184  marker->setLinePen(QColor(Qt::GlobalColor((i) % 5 + 13)));
185  marker->attach(plotter);
186  markerSubMap[value] = marker;
187  }
188 
189  };
190 
191  for (; it != dataMap.end(); ++it)
192  {
193 
194  QVector<QPointF> pointList;
195  pointList.clear();
196  auto datafieldId = it->first;
197  std::vector<TimeData>& dataVec = it->second;
198  // int newSize = min(size,(int)dataVec.size());
199  pointList.reserve(dataVec.size());
200 
201 
202  try
203  {
204  int j = 0;
205 
206  for (int i = dataVec.size() - 1; i >= 0 ; --i)
207  {
208  TimeData& data = dataVec[i];
209  IceUtil::Time age = (data.time - curTime);
210 
211  if (age.toSecondsDouble() <= shownInterval /*&& age.toMicroSeconds() > 0*/)
212  {
213  VariantPtr var = VariantPtr::dynamicCast(data.data);
214 
215  if (var->getInitialized())
216  {
217  float xValue = 0.001 * age.toMilliSecondsDouble();
218  auto type = var->getType();
219  double yValue = 0;
220  if (type == VariantType::Float && var->getFloat() != std::numeric_limits<float>::infinity())
221  {
222  yValue = (var->getFloat());
223  }
224  else if (type == VariantType::Double && var->getDouble() != std::numeric_limits<double>::infinity())
225  {
226  yValue = (var->getDouble());
227  }
228  else if (type == VariantType::Int && var->getInt() != std::numeric_limits<int>::infinity())
229  {
230  yValue = (var->getInt());
231  }
232  else if (type == VariantType::Long && var->getLong() != std::numeric_limits<long>::infinity())
233  {
234  yValue = static_cast<double>(var->getLong());
235  }
236  else if (type == VariantType::Bool)
237  {
238  yValue = (var->getBool() ? 1 : -1);
239  }
240  else if (type == VariantType::String)
241  {
242  addMarker(age, var, datafieldId);
243  }
244  else
245  {
246  continue;
247  }
248 
249  pointList.push_back(QPointF(xValue, yValue));
250 
251  j++;
252  }
253  else
254  {
255  ARMARX_WARNING << deactivateSpam(3) << "uninitialized field: " << it->first;
256  }
257 
258  }
259  else
260  {
261  break; // data too old from now
262  }
263  }
264  }
265  catch (...)
266  {
268  }
269 
270  QwtSeriesData<QPointF>* pointSeries = new QwtPointSeriesData(pointList);
271 
272  if (pointList.size() > 0 && curves.find(it->first) != curves.end() && bars.find(it->first) != bars.end())
273  {
274  QwtPlotCurve* curve = curves[it->first];
275  curve->setData(pointSeries);
276  curve->setYAxis(QwtPlot::yRight);
277  QwtThermo* bar = bars[it->first];
278  bar->setValue(pointList.first().y());
279  float range = curve->maxYValue() - curve->minYValue();
280  bar->setLowerBound(curve->minYValue() - 0.05 * range);
281  bar->setUpperBound((curve->maxYValue()) + range * 0.05);
282  }
283  }
284 
285  plotter->setAxisAutoScale(QwtPlot::yRight, autoScalePlot);
286 
287  // plotter->setAxisScale( QwtPlot::yLeft, -1, 1);
288 
289  plotter->replot();
290  }
291 
292  void PlotterController::showCurve(QwtPlotItem* item, bool on)
293  {
294  item->setVisible(on);
295  QwtLegend* lgd = qobject_cast<QwtLegend*>(plotter->legend());
296 
297  QList<QWidget*> legendWidgets =
298  lgd->legendWidgets(plotter->itemToInfo(item));
299 
300  if (legendWidgets.size() == 1)
301  {
302  QwtLegendLabel* legendLabel =
303  qobject_cast<QwtLegendLabel*>(legendWidgets[0]);
304 
305  if (legendLabel)
306  {
307  legendLabel->setChecked(on);
308  }
309  else
310  {
311  ARMARX_WARNING << "legendLabel is no QwtLegendLabel";
312  }
313  }
314  else
315  {
316  ARMARX_WARNING << "found " << legendWidgets.size() << " items for the given curve";
317  }
318  plotter->replot();
319  }
320 
321  void PlotterController::autoScale(bool toggled)
322  {
323  ARMARX_VERBOSE << "clicked autoscale" << flush;
324 
325  plotter->setAxisAutoScale(QwtPlot::yRight, toggled);
326  plotter->replot();
327  autoScalePlot = toggled;
328  }
329 
331  {
332  __plottingPaused = toggled;
333 
334  // if (pollingTask)
335  // {
336  // pollingTask->stop();
337  // }
338 
339  if (__plottingPaused)
340  {
341  timer.stop();
342  }
343  else
344  {
345  // pollingTask = new PeriodicTask<PlotterController>(this, &PlotterController::pollingExec, pollingInterval, false, "DataPollingTask", false);
346  // pollingTask->start();
347  timer.start(updateInterval);
348  }
349  }
350 
351  std::map<std::string, VariantPtr> PlotterController::getData(const QStringList& channels)
352  {
353  using clock_t = std::chrono::high_resolution_clock;
354  const auto now = [] {return clock_t::now();};
355  const auto dt_ms = [](auto t0, auto t1) -> float
356  {
357  return std::chrono::duration_cast<std::chrono::nanoseconds>(t1 - t0).count() / 1e6f;
358  };
359  if (iceManager->isShutdown())
360  {
361  return {};
362  }
363  //create Data field ids from channel names
364  std::map< std::string, DataFieldIdentifierBaseList > channelsSplittedByObserver;
365  for (QString const& channel : channels)
366  {
367  DataFieldIdentifierPtr identifier = new DataFieldIdentifier(channel.toStdString());
368  channelsSplittedByObserver[identifier->getObserverName()].push_back(identifier);
369  // ARMARX_INFO << identifier;
370  }
371  std::map<std::string, VariantPtr> newData;
372  const auto time_clear_start = now();
373  {
374  std::lock_guard lock(dataMutex);
375 
376  // first clear to old entries
377  auto now = TimeUtil::GetTime();
378  GraphDataMap::iterator itmap = dataMap.begin();
379 
380  for (; itmap != dataMap.end(); ++itmap)
381  {
382  std::vector<TimeData>& dataVec = itmap->second;
383  int stepSize = std::max<int>(1, dataVec.size() * 0.01);
384  int thresholdIndex = -1;
385 
386  for (unsigned int i = 0; i < dataVec.size(); i += stepSize)
387  {
388  // only delete if entries are older than 2*showninterval
389  // and delete then all entries that are older than showninterval.
390  // otherwise it would delete items on every call, which would be very slow
391 
392  if ((now - dataVec[i].time).toSecondsDouble() > shownInterval * 2
393  || (thresholdIndex != -1 && (now - dataVec[i].time).toSecondsDouble() > shownInterval)
394  )
395  {
396  thresholdIndex = i;
397  }
398  else
399  {
400  break;
401  }
402  }
403 
404  if (thresholdIndex != -1)
405  {
406  unsigned int offset = std::min((int)dataVec.size(), thresholdIndex);
407 
408  // ARMARX_IMPORTANT << "Erasing " << offset << " fields";
409  if (offset > dataVec.size())
410  {
411  dataVec.clear();
412  }
413  else
414  {
415  dataVec.erase(dataVec.begin(), dataVec.begin() + offset);
416  }
417  }
418 
419  // ARMARX_IMPORTANT << deactivateSpam(5) << "size: " << dataVec.size();
420  }
421  }
422  const auto time_clear_end = now();
423 
424  // now get new data
426  std::map<std::string, DataFieldIdentifierBaseList >::iterator it = channelsSplittedByObserver.begin();
427  const auto time_get_data_start = now();
428  float time_get_data_get_variants = 0;
429  float time_get_data_find_obs = 0;
430  float time_get_data_lock = 0;
431  float time_get_data_process = 0;
432  std::map<std::string, float> time_get_data_get_variants_per_obs;
433  try
434  {
435  for (; it != channelsSplittedByObserver.end(); ++it)
436  {
437  const std::string& observerName = it->first;
438 
439  const auto time_get_data_find_obs_start = now();
440  if (proxyMap.find(observerName) == proxyMap.end())
441  {
442  if (!iceManager)
443  {
444  continue;
445  time_get_data_find_obs += dt_ms(time_get_data_find_obs_start, now());
446  }
447  proxyMap[observerName] = iceManager->getProxy<ObserverInterfacePrx>(observerName);
448  }
449  time_get_data_find_obs += dt_ms(time_get_data_find_obs_start, now());
450 
451  // QDateTime time(QDateTime::currentDateTime());
452  const auto time_get_data_get_variants_start = now();
453  TimedVariantBaseList variants = proxyMap[observerName]->getDataFields(it->second);
454  const auto time_get_data_get_variants_dt = dt_ms(time_get_data_get_variants_start, now());
455  time_get_data_get_variants_per_obs[observerName] = time_get_data_get_variants_dt;
456  time_get_data_get_variants += time_get_data_get_variants_dt;
457 
458  const auto time_get_data_lock_start = now();
459  std::lock_guard lock(dataMutex);
460  time_get_data_lock += dt_ms(time_get_data_lock_start, now());
461  // # ARMARX_IMPORTANT << "data from observer: " << observerName;
462  const auto time_get_data_process_start = now();
463  for (unsigned int i = 0; i < variants.size(); ++i)
464  {
465  // ARMARX_IMPORTANT << "Variant: " << VariantPtr::dynamicCast(variants[i]);
466  VariantPtr var = VariantPtr::dynamicCast(variants[i]);
467  std::string id = DataFieldIdentifierPtr::dynamicCast(it->second[i])->getIdentifierStr();
468  if (!var->getInitialized())
469  {
470  //dataMap[id] = {};
471  continue;
472  }
473  auto type = var->getType();
474  if (type == VariantType::String)
475  {
476  if (dataMap[id].size() == 0 || dataMap[id].rbegin()->data->getString() != var->getString())
477  {
478  // only insert if changed
479  dataMap[id].push_back(TimeData(time, var));
480  newData[id] = var;
481  }
482  }
483  else if (type == VariantType::Float)
484  {
485  if (std::isfinite(var->getFloat()))
486  {
487  dataMap[id].push_back(TimeData(time, var));
488  }
489  newData[id] = var;
490  }
491  else if (type == VariantType::Double)
492  {
493  if (std::isfinite(var->getDouble()))
494  {
495  dataMap[id].push_back(TimeData(time, var));
496  }
497  newData[id] = var;
498  }
499  else if (VariantType::IsBasicType(type))
500  {
501  dataMap[id].push_back(TimeData(time, var));
502  newData[id] = var;
503  }
504  else
505  {
506  auto dict = JSONObject::ConvertToBasicVariantMap(json, var);
507 
508  for (const auto& e : dict)
509  {
510  if (e.first == "timestamp") // TimedVariants always contain a timestamp field which is irrelevant
511  {
512  continue;
513  }
514  std::string key = id + "." + e.first;
515  // ARMARX_INFO << key << ": " << *VariantPtr::dynamicCast(e.second);
516  VariantPtr var = VariantPtr::dynamicCast(e.second);
517  auto type = var->getType();
518  if (type == VariantType::String)
519  {
520  // complex contain additional strings often, which cannot be selected right now -> disable strings from complex types
521  // if (dataMap[id].size() == 0 || dataMap[id].rbegin()->data->getString() != var->getString())
522  // {
523  // // only insert if changed
524  // dataMap[id].push_back(TimeData(time, var));
525  // newData[key] = var;
526  // }
527  }
528  else
529  {
530  dataMap[key].push_back(TimeData(time, var));
531  newData[key] = var;
532  }
533  }
534  }
535  }
536  time_get_data_process += dt_ms(time_get_data_process_start, now());
537  }
538  }
539  catch (Ice::NotRegisteredException& e)
540  {
541  ARMARX_WARNING << deactivateSpam(3) << "Caught Ice::NotRegisteredException: " << e.what();
542  }
544  {
545  ARMARX_WARNING << deactivateSpam(5) << "Caught InvalidDataFieldException: " << e.what();
546  }
548  {
549  ARMARX_WARNING << deactivateSpam(5) << "Caught InvalidChannelException: " << e.what();
550  }
551  catch (armarx::UserException& e)
552  {
553  ARMARX_WARNING << deactivateSpam(5) << "Caught UserException: " << e.what() << "\nReason: " << e.reason;
554  }
555  catch (...)
556  {
558  }
559  const auto time_get_data_end = now();
561  {
562  const auto dt_get_data = dt_ms(time_get_data_start, time_get_data_end);
563  const auto dt_clear = dt_ms(time_clear_start, time_clear_end);
564  out << "timings PlotterController::getData"
565  << "\ndt_clear " << dt_clear << " ms"
566  << "\ndt_get_data " << dt_get_data << " ms"
567  << "\n time_get_data_find_obs " << time_get_data_find_obs << " ms"
568  << "\n time_get_data_get_variants " << time_get_data_get_variants << " ms";
569  for (const auto& [k, v] : time_get_data_get_variants_per_obs)
570  {
571  out << "\n " << k << "\t" << v;
572  }
573  out << "\n time_get_data_lock " << time_get_data_lock << " ms"
574  << "\n time_get_data_process " << time_get_data_process << " ms";
575  };
576  return newData;
577  }
578 
579  void PlotterController::setupCurves(int samplingIntervalMs)
580  {
581  {
582  if (samplingIntervalMs < 0)
583  {
584  samplingIntervalMs = pollingInterval;
585  }
586  std::lock_guard lock(dataMutex);
587  clear();
588  auto now = TimeUtil::GetTime();
589  std::map<std::string, TimedVariantBaseList> histories;
590  markers.clear();
591  // for(auto marker : markers)
592  // marker->detach();
593  plotter->detachItems();
594  for (int i = 0; i < selectedDatafields.size(); i++)
595  {
596  try
597  {
598  ARMARX_VERBOSE << "Channel: " << selectedDatafields.at(i).toStdString() << flush;
599  DataFieldIdentifierPtr identifier = new DataFieldIdentifier(selectedDatafields.at(i).toStdString());
600  auto prx = iceManager->getProxy<ObserverInterfacePrx>(identifier->observerName);
601 
602 
603  // get past data of that datafield
604  auto id = identifier->getIdentifierStr();
605  auto historiesIt = histories.find(id);
606 
607  if (historiesIt == histories.end())
608  {
609  auto start = IceUtil::Time::now();
610  histories[id] = prx->getPartialDatafieldHistory(identifier->channelName, identifier->datafieldName,
611  (now - IceUtil::Time::seconds(shownInterval)).toMicroSeconds(), now.toMicroSeconds(), samplingIntervalMs);
612  ARMARX_DEBUG << "history data polling took : " << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " got " << histories[identifier->channelName].size() << " entries";
613  historiesIt = histories.find(id);
614  }
615 
616  long lastTimestamp = 0;
617 
618  VariantPtr var = VariantPtr::dynamicCast(prx->getDataField(identifier));
619  auto type = var->getType();
620  if (type == VariantType::String)
621  {
622  // do nothing for now
623  }
624  else if (VariantType::IsBasicType(type))
625  {
626  QwtPlotCurve* curve = createCurve(selectedDatafields.at(i));
627  curves[selectedDatafields.at(i).toStdString()] = curve;
628 
629  QwtThermo* bar = createBar(selectedDatafields.at(i));
630  bars[selectedDatafields.at(i).toStdString()] = bar;
631 
632  for (TimedVariantBasePtr& entry : historiesIt->second)
633  {
634  if (lastTimestamp + pollingInterval < entry->getTimestamp())
635  {
636  dataMap[id].push_back(TimeData(IceUtil::Time::microSeconds(entry->getTimestamp()), VariantPtr::dynamicCast(entry)));
637  lastTimestamp = entry->getTimestamp();
638  }
639  }
640  }
641  else
642  {
643  auto id = identifier->getIdentifierStr();
644  ARMARX_VERBOSE << id;
645  auto dict = JSONObject::ConvertToBasicVariantMap(json, var);
646 
647  for (auto e : dict)
648  {
649  if (e.first == "timestamp") // TimedVariants always contain a timestamp field which is irrelevant
650  {
651  continue;
652  }
653  VariantTypeId type = e.second->getType();
654 
655  if (type == VariantType::Double
656  || type == VariantType::Float
657  || type == VariantType::Int
658  || type == VariantType::Long)
659  {
660  std::string key = id + "." + e.first;
661  ARMARX_VERBOSE << key << ": " << *VariantPtr::dynamicCast(e.second);
662  QwtPlotCurve* curve = createCurve(QString::fromStdString(key));
663  curves[key] = curve;
664 
665  QwtThermo* bar = createBar(QString::fromStdString(key));
666  bars[key] = bar;
667  }
668  }
669  for (auto& entry : historiesIt->second)
670  {
671  if (lastTimestamp + pollingInterval < entry->getTimestamp())
672  {
673  auto dict = JSONObject::ConvertToBasicVariantMap(json, entry);
674  for (const auto& e : dict)
675  {
676  if (e.first == "timestamp") // TimedVariants always contain a timestamp field which is irrelevant
677  {
678  continue;
679  }
680  std::string key = id + "." + e.first;
681  // ARMARX_INFO << key << ": " << *VariantPtr::dynamicCast(e.second);
682  VariantPtr var = VariantPtr::dynamicCast(e.second);
683  auto type = var->getType();
684  if (type == VariantType::String)
685  {
686  // complex contain additional strings often, which cannot be selected right now -> disable strings from complex types
687  // if (dataMap[id].size() == 0 || dataMap[id].rbegin()->data->getString() != var->getString())
688  // {
689  // // only insert if changed
690  // dataMap[id].push_back(TimeData(time, var));
691  // newData[key] = var;
692  // }
693  }
694  else
695  {
696  dataMap[key].push_back(TimeData(IceUtil::Time::microSeconds(entry->getTimestamp()), var));
697  }
698  }
699  }
700  }
701 
702 
703  }
704 
705  }
706  catch (...)
707  {
709  }
710  }
711 
712  plotter->replot();
713 
714  }
715  plotter->setAxisScale(QwtPlot::xBottom, shownInterval * -1, 0.f);
716 
717  timer.start(updateInterval);
718 
719  if (pollingTask)
720  {
721  pollingTask->stop();
722  }
723 
724  pollingTask = new PeriodicTask<PlotterController>(this, & PlotterController::pollingExec, pollingInterval, false, "DataPollingTask", false);
725  pollingTask->start();
726  }
727 
728  void PlotterController::legendChecked(const QVariant& itemInfo, bool on)
729  {
730  QwtPlotItem* plotItem = plotter->infoToItem(itemInfo);
731  if (plotItem)
732  {
733  showCurve(plotItem, on);
734  }
735  else
736  {
737  ARMARX_WARNING << "nonexistent legend item toggled to " << on;
738  }
739  }
740 
741  QwtPlotCurve* PlotterController::createCurve(const QString& label)
742  {
743  QwtPlotCurve* curve = new QwtPlotCurve(label);
744  curve->setRenderHint(QwtPlotItem::RenderAntialiased);
745  curve->setPen(QColor(curveColors.at(curves.size() % curveColors.size())));
746  curve->setStyle(QwtPlotCurve::Lines);
747 
748  curve->setPaintAttribute(QwtPlotCurve::ClipPolygons, true);
749 
750  curve->attach(plotter);
751  showCurve(curve, true);
752 
753  return curve;
754  }
755 
756  QwtThermo* PlotterController::createBar(const QString& label)
757  {
758  //creates a widget containing a bar chart and a label
759  //with the fill color of chart same as its curve
760 
761  QwtThermo* bar = new QwtThermo(this->widget);
762  bar->setAccessibleName(label);
763  bar->setFillBrush(QBrush(QColor(Qt::GlobalColor((bars.size() + 7) % 15))));
764  bar->setStyleSheet("* { background-color: rgb(240, 240, 240); }"); //neccessary because white is a picked fill color
765 
766  QTextEdit* lab = new QTextEdit(label, this->widget);
767  lab->setStyleSheet("* { background-color: rgb(240, 240, 240); }");
768  lab->setLineWrapMode(QTextEdit::WidgetWidth);
769  lab->setTextInteractionFlags(nullptr);
770  lab->setFrameStyle(0);
771  int linecount = lab->document()->blockCount();
772  lab->setMaximumHeight(70 * linecount);
773  lab->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
774  QVBoxLayout* layout = new QVBoxLayout();
775  layout->addWidget(bar);
776  layout->addWidget(lab);
777 
778  QWidget* widget = new QWidget(layout->widget());
779  widget->setLayout(layout);
780  barlayout->addWidget(widget);
781 
782  return bar;
783  }
784 
786  {
787  std::lock_guard lock(dataMutex);
788  return selectedDatafields;
789  }
790 
791 
792 
793  std::vector<std::string> PlotterController::getSelectedDatafieldsKeys() const
794  {
795 
796  std::vector<std::string> keys;
797 
798  std::lock_guard lock(dataMutex);
799 
800  for (int i = 0; i < selectedDatafields.size(); i++)
801  {
802  try
803  {
804  DataFieldIdentifierPtr identifier = new DataFieldIdentifier(selectedDatafields.at(i).toStdString());
805  ObserverInterfacePrx observer = iceManager->getProxy<ObserverInterfacePrx>(identifier->observerName);
806 
807  VariantPtr var = VariantPtr::dynamicCast(observer->getDataField(identifier));
808  VariantTypeId type = var->getType();
809  if (type == VariantType::String || VariantType::IsBasicType(type))
810  {
811  keys.push_back(selectedDatafields.at(i).toStdString());
812  }
813  else
814  {
815  // TODO auto load libraries see ObserverItemModel.cpp:108
816 
817  auto dict = JSONObject::ConvertToBasicVariantMap(json, var);
818  for (auto e : dict)
819  {
820  ARMARX_LOG_S << e.first;
821  if (e.first == "timestamp")
822  {
823  continue;
824  }
825  VariantTypeId type = e.second->getType();
826 
827  if (type == VariantType::Double
828  || type == VariantType::Float
829  || type == VariantType::Int
830  || type == VariantType::Long)
831  {
832  keys.push_back(identifier->getIdentifierStr() + "." + e.first);
833  }
834 
835  }
836  }
837  }
838  catch (...)
839  {
841  }
842 
843  }
844  return keys;
845  }
846 
847 
848  void PlotterController::setSelectedDatafields(const QStringList& value, int samplingIntervalMs)
849  {
850  {
851  std::lock_guard lock(dataMutex);
852  if (selectedDatafields == value)
853  {
854  return;
855  }
857  }
858  setupCurves(samplingIntervalMs);
859  }
860 
862  {
863  return pollingInterval;
864  }
865 
867  {
869  if (pollingTask)
870  {
871  pollingTask->changeInterval(value);
872  }
873  }
874 
876  {
877  return updateInterval;
878  }
879 
881  {
883  timer.start(value);
884  }
885 
887  {
888  iceManager = value;
889  json = new JSONObject(iceManager->getCommunicator());
890 
891  }
892 
894  {
895  //maybe comparing new and old selected list better than doing a new one every time?
896  QLayoutItem* child;
897  int containedItems = barlayout->count() - 1;
898 
899  for (int i = containedItems; i >= 0 ; i--) //must be done in this order due to internal implementation of layout item numbering
900  {
901  child = barlayout->itemAt(i);
902  barlayout->removeItem(child);
903  child->widget()->deleteLater();
904  }
905 
906  bars.clear();
907  }
908 
910  {
911  std::lock_guard g(dataMutex);
912  plotter->detachItems();
913  markers.clear();
914  curves.clear();
915  dataMap.clear();
916  clearBarList();
917 
918  }
919 
921  {
922  std::lock_guard g(dataMutex);
923  dataMap.clear();
924  }
925 
927  {
928  return shownInterval;
929  }
930 
932  {
933  ARMARX_VERBOSE << "Setting shown interval to " << shownInterval;
935  plotter->setAxisScale(QwtPlot::xBottom, shownInterval * -1, 0.f);
936 
937  }
938 
940  {
941  return autoScalePlot;
942  }
943 
945  {
947  }
948 
950  {
951  return graphStyle;
952  }
953 
954  void PlotterController::setGraphStyle(const std::string& style)
955  {
956  if (graphStyle != style)
957  {
958  graphStyle = style;
959  if (style == "Bar chart")
960  {
961  stackedLayout->setCurrentIndex(1);
962  }
963  else
964  {
965  stackedLayout->setCurrentIndex(0);
966  QwtPlotCurve::CurveStyle st = QwtPlotCurve::Lines;
967  if (style == "Curve (line, steps)")
968  {
969  st = QwtPlotCurve::Steps;
970  }
971  else if (style == "Curve (dots)")
972  {
973  st = QwtPlotCurve::Dots;
974  }
975  for (const auto& [_, curve] : curves)
976  {
977  curve->setStyle(st);
978  }
979  plotter->replot();
980  }
981  }
982  }
983 
984  std::vector<Qt::GlobalColor> PlotterController::getCurveColors() const
985  {
986  return curveColors;
987  }
988 
989  void PlotterController::setCurveColors(const std::vector<Qt::GlobalColor>& value)
990  {
991  ARMARX_CHECK_GREATER(value.size(), 0);
992  curveColors = value;
993  }
994 }
armarx::PlotterController::setPollingInterval
void setPollingInterval(int value)
Definition: PlotterController.cpp:866
armarx::PlotterController::setIceManager
void setIceManager(const IceManagerPtr &value)
Definition: PlotterController.cpp:886
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::PlotterController::selectedDatafields
QStringList selectedDatafields
Definition: PlotterController.h:158
armarx::PlotterController::updateGraph
void updateGraph()
Definition: PlotterController.cpp:126
armarx::PlotterController::bars
std::map< std::string, QwtThermo * > bars
Definition: PlotterController.h:168
armarx::VariantType::Float
const VariantTypeId Float
Definition: Variant.h:918
armarx::PlotterController::proxyMap
ProxyMap proxyMap
Definition: PlotterController.h:165
armarx::PlotterController::widget
QPointer< QWidget > widget
Definition: PlotterController.h:156
armarx::PlotterController::dataMutex
std::recursive_mutex dataMutex
Definition: PlotterController.h:163
armarx::PlotterController::getPlotterWidget
QWidget * getPlotterWidget()
Definition: PlotterController.cpp:113
armarx::PlotterController::showCurve
void showCurve(QwtPlotItem *item, bool on)
Definition: PlotterController.cpp:292
armarx::PlotterController::setAutoScale
void setAutoScale(bool value)
Definition: PlotterController.cpp:944
armarx::JSONObject::ConvertToBasicVariantMap
static StringVariantBaseMap ConvertToBasicVariantMap(const JSONObjectPtr &serializer, const VariantBasePtr &variant)
Definition: JSONObject.cpp:523
armarx::PlotterController::pollingInterval
int pollingInterval
Definition: PlotterController.h:150
armarx::JSONObject
The JSONObject class is used to represent and (de)serialize JSON objects.
Definition: JSONObject.h:43
armarx::PlotterController::curveColors
std::vector< Qt::GlobalColor > curveColors
Definition: PlotterController.h:167
ARMARX_CHECK_GREATER
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
Definition: ExpressionException.h:116
armarx::PlotterController::timer
QTimer timer
Definition: PlotterController.h:162
armarx::PlotterController::newDataAvailable
void newDataAvailable(long timestamp, const std::map< std::string, VariantPtr > &newData)
armarx::PlotterController::updateInterval
int updateInterval
Definition: PlotterController.h:151
armarx::PlotterController::getPollingInterval
int getPollingInterval() const
Definition: PlotterController.cpp:861
armarx::PlotterController::createBar
QwtThermo * createBar(const QString &label)
Definition: PlotterController.cpp:756
armarx::VariantType::Bool
const VariantTypeId Bool
Definition: Variant.h:915
armarx::PlotterController::barlayout
QHBoxLayout * barlayout
Definition: PlotterController.h:173
armarx::PlotterController::json
JSONObjectPtr json
Definition: PlotterController.h:159
armarx::PlotterController::autoScale
void autoScale(bool toggled)
Definition: PlotterController.cpp:321
armarx::PlotterController::clearBarList
void clearBarList()
Definition: PlotterController.cpp:893
armarx::PlotterController::getAutoScale
bool getAutoScale() const
Definition: PlotterController.cpp:939
armarx::PlotterController::iceManager
IceManagerPtr iceManager
Definition: PlotterController.h:160
armarx::PlotterController::~PlotterController
~PlotterController() override
Definition: PlotterController.cpp:99
IceInternal::Handle< Variant >
std::isfinite
bool isfinite(const std::vector< T, Ts... > &v)
Definition: algorithm.h:327
armarx::VariantType::Double
const VariantTypeId Double
Definition: Variant.h:919
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:72
armarx::GetHandledExceptionString
std::string GetHandledExceptionString()
Definition: Exception.cpp:147
armarx::PlotterController::getSelectedDatafields
QStringList getSelectedDatafields() const
Definition: PlotterController.cpp:785
armarx::PlotterController::dataMap
GraphDataMap dataMap
Definition: PlotterController.h:164
armarx::PlotterController::__plottingPaused
bool __plottingPaused
Definition: PlotterController.h:155
armarx::QwtPlotMarkerPtr
QwtPlotMarker * QwtPlotMarkerPtr
Definition: PlotterController.h:60
cxxopts::value
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:926
armarx::exceptions::local::InvalidDataFieldException
Definition: InvalidDataFieldException.h:33
armarx::PlotterController::curves
std::map< std::string, QwtPlotCurve * > curves
Definition: PlotterController.h:166
armarx::PlotterController::setupCurves
void setupCurves(int samplingIntervalMs=-1)
Definition: PlotterController.cpp:579
armarx::flush
const LogSender::manipulator flush
Definition: LogSender.h:251
armarx::PlotterController::setUpdateInterval
void setUpdateInterval(int value)
Definition: PlotterController.cpp:880
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
armarx::PlotterController::stackedLayout
QStackedLayout * stackedLayout
Definition: PlotterController.h:172
armarx::VariantType::Long
const VariantTypeId Long
Definition: Variant.h:917
armarx::PlotterController::legendChecked
void legendChecked(const QVariant &itemInfo, bool on)
Required for Qt5.
Definition: PlotterController.cpp:728
PlotterController.h
armarx::PlotterController::plottingPaused
void plottingPaused(bool toggled)
Definition: PlotterController.cpp:330
armarx::PlotterController::getGraphStyle
std::string getGraphStyle() const
Definition: PlotterController.cpp:949
armarx::exceptions::local::InvalidChannelException
Definition: InvalidChannelException.h:33
armarx::PlotterController::clear
void clear()
Definition: PlotterController.cpp:909
armarx::VariantTypeId
Ice::Int VariantTypeId
Definition: Variant.h:44
armarx::PlotterController::clearHistory
void clearHistory()
Definition: PlotterController.cpp:920
armarx::VariantType::IsBasicType
bool IsBasicType(VariantTypeId id)
Definition: Variant.cpp:772
ARMARX_LOG_S
#define ARMARX_LOG_S
Definition: Logging.h:145
armarx::PlotterController::graphStyle
std::string graphStyle
Definition: PlotterController.h:153
armarx::armem::Time
armarx::core::time::DateTime Time
Definition: forward_declarations.h:13
armarx::PlotterController::pollingTask
PeriodicTask< PlotterController >::pointer_type pollingTask
Definition: PlotterController.h:161
armarx::red
QColor red()
Definition: StyleSheets.h:76
armarx::PlotterController::getShownInterval
int getShownInterval() const
Definition: PlotterController.cpp:926
armarx::TimeUtil::GetTime
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition: TimeUtil.cpp:42
armarx::PlotterController::createCurve
QwtPlotCurve * createCurve(const QString &label)
Definition: PlotterController.cpp:741
ExpressionException.h
armarx::PlotterController::pollingExec
void pollingExec()
Definition: PlotterController.cpp:118
armarx::PlotterController::PlotterController
PlotterController(QObject *parent)
Definition: PlotterController.cpp:36
armarx::PlotterController::getData
std::map< std::string, VariantPtr > getData(const QStringList &channels)
Definition: PlotterController.cpp:351
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
armarx::PlotterController::shownInterval
int shownInterval
Definition: PlotterController.h:152
armarx::PlotterController::setGraphStyle
void setGraphStyle(const std::string &style)
Definition: PlotterController.cpp:954
IceUtil::Handle< IceManager >
armarx::PlotterController::plotter
QPointer< QwtPlot > plotter
Definition: PlotterController.h:157
armarx::VariantType::Int
const VariantTypeId Int
Definition: Variant.h:916
armarx::PlotterController::getUpdateInterval
int getUpdateInterval() const
Definition: PlotterController.cpp:875
armarx::PlotterController::autoScalePlot
bool autoScalePlot
Definition: PlotterController.h:154
armarx::PlotterController::markers
std::map< std::string, std::map< IceUtil::Time, std::map< std::string, QwtPlotMarkerPtr > > > markers
Definition: PlotterController.h:170
armarx::PlotterController::getCurveColors
std::vector< Qt::GlobalColor > getCurveColors() const
Definition: PlotterController.cpp:984
armarx::PeriodicTask
Definition: ArmarXManager.h:70
armarx::PlotterController::getSelectedDatafieldsKeys
std::vector< std::string > getSelectedDatafieldsKeys() const
Definition: PlotterController.cpp:793
min
T min(T t1, T t2)
Definition: gdiam.h:42
armarx::handleExceptions
void handleExceptions()
Definition: Exception.cpp:141
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::VariantType::String
const VariantTypeId String
Definition: Variant.h:920
armarx::PlotterController::setShownInterval
void setShownInterval(int value)
Definition: PlotterController.cpp:931
armarx::PlotterController::setCurveColors
void setCurveColors(const std::vector< Qt::GlobalColor > &value)
Definition: PlotterController.cpp:989
armarx::TimeData
Definition: PlotterController.h:62
armarx::PlotterController::setSelectedDatafields
void setSelectedDatafields(const QStringList &value, int samplingIntervalMs=-1)
Changes the datafields that are plotted.
Definition: PlotterController.cpp:848
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
armarx::DataFieldIdentifier
DataFieldIdentifier provide the basis to identify data field within a distributed ArmarX scenario.
Definition: DataFieldIdentifier.h:48
armarx::green
QColor green()
Definition: StyleSheets.h:72
ARMARX_STREAM_PRINTER
#define ARMARX_STREAM_PRINTER
use this macro to write output code that is executed when printed and thus not executed if the debug ...
Definition: Logging.h:304