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