KinematicUnitGuiPlugin.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
19 * @author
20 * @date
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
25
26#include <SimoxUtility/algorithm/string.h>
27#include <SimoxUtility/json.h>
28#include <VirtualRobot/Nodes/RobotNode.h>
29#include <VirtualRobot/Robot.h>
30#include <VirtualRobot/RobotNodeSet.h>
31#include <VirtualRobot/Visualization/CoinVisualization/CoinVisualization.h>
32#include <VirtualRobot/Visualization/VisualizationFactory.h>
33#include <VirtualRobot/XML/RobotIO.h>
34
47
48#include <RobotAPI/gui-plugins/KinematicUnitPlugin/ui_KinematicUnitConfigDialog.h>
49#include <RobotAPI/interface/core/NameValueMap.h>
50#include <RobotAPI/interface/units/KinematicUnitInterface.h>
51
53
54// Qt headers
55#include <QCheckBox>
56#include <QClipboard>
57#include <QInputDialog>
58#include <QPushButton>
59#include <QSlider>
60#include <QSpinBox>
61#include <QStringList>
62#include <QTableView>
63#include <QTableWidget>
64#include <Qt>
65#include <QtGlobal>
66#include <qtimer.h>
67
69
70#include <Inventor/Qt/SoQt.h>
71#include <Inventor/SoDB.h>
72
73// System
74#include <cmath>
75#include <cstddef>
76#include <cstdio>
77#include <cstdlib>
78#include <filesystem>
79#include <iostream>
80#include <memory>
81#include <optional>
82#include <stdexcept>
83#include <string>
84
85
86//#define KINEMATIC_UNIT_FILE_DEFAULT std::string("RobotAPI/robots/Armar3/ArmarIII.xml")
87//#define KINEMATIC_UNIT_FILE_DEFAULT_PACKAGE std::string("RobotAPI")
88#define KINEMATIC_UNIT_NAME_DEFAULT "Robot"
89//#define TOPIC_NAME_DEFAULT "RobotState"
90
91constexpr float SLIDER_POS_DEG_MULTIPLIER = 5;
92constexpr float SLIDER_POS_RAD_MULTIPLIER = 100;
93constexpr float SLIDER_POS_HEMI_MULTIPLIER = 100;
94
95namespace armarx
96{
97
99 {
100
101 qRegisterMetaType<DebugInfo>("DebugInfo");
102
104 }
105
107 kinematicUnitNode(nullptr),
108 enableValueValidator(true),
109 historyTime(100000), // 1/10 s
110 currentValueMax(5.0f)
111 {
112 rootVisu = NULL;
113 debugLayerVisu = NULL;
114
115 // init gui
116 ui.setupUi(getWidget());
117 getWidget()->setEnabled(false);
118
119 ui.tableJointList->setItemDelegateForColumn(eTabelColumnAngleProgressbar, &delegate);
120
121 ui.radioButtonUnknown->setHidden(true);
122 }
123
124 void
126 {
128 verbose = true;
129
130
131 rootVisu = new SoSeparator;
132 rootVisu->ref();
133 robotVisu = new SoSeparator;
134 robotVisu->ref();
135 rootVisu->addChild(robotVisu);
136
137 // create the debugdrawer component
138 std::string debugDrawerComponentName = "KinemticUnitGUIDebugDrawer_" + getName();
139 ARMARX_INFO << "Creating component " << debugDrawerComponentName;
142 showVisuLayers(false);
143
144 if (mutex3D)
145 {
146 //ARMARX_IMPORTANT << "mutex3d:" << mutex3D.get();
147 debugDrawer->setMutex(mutex3D);
148 }
149 else
150 {
151 ARMARX_ERROR << " No 3d mutex available...";
152 }
153
155 m->addObject(debugDrawer, false);
156
157
158 {
159 std::unique_lock lock(*mutex3D);
160 debugLayerVisu = new SoSeparator();
161 debugLayerVisu->ref();
162 debugLayerVisu->addChild(debugDrawer->getVisualization());
163 rootVisu->addChild(debugLayerVisu);
164 }
165
166 connectSlots();
167
169 }
170
171 void
173 {
174 // ARMARX_INFO << "Kinematic Unit Gui :: onConnectComponent()";
175 jointCurrentHistory.clear();
176 jointCurrentHistory.set_capacity(5);
177
178 // jointAnglesUpdateFrequency = new filters::MedianFilter(100);
180
181 lastJointAngleUpdateTimestamp = Clock::Now();
182 robotVisu->removeAllChildren();
183
184 robot.reset();
185
186 std::string rfile;
187 Ice::StringSeq includePaths;
188
189 // Get robot filename
190 try
191 {
192 Ice::StringSeq packages = kinematicUnitInterfacePrx->getArmarXPackages();
193 packages.push_back(Application::GetProjectName());
194 ARMARX_VERBOSE << "ArmarX packages " << packages;
195
196 for (const std::string& projectName : packages)
197 {
198 if (projectName.empty())
199 {
200 continue;
201 }
202
203 CMakePackageFinder project(projectName);
204 auto pathsString = project.getDataDir();
205 ARMARX_VERBOSE << "Data paths of ArmarX package " << projectName << ": "
206 << pathsString;
207 Ice::StringSeq projectIncludePaths = Split(pathsString, ";,", true, true);
208 ARMARX_VERBOSE << "Result: Data paths of ArmarX package " << projectName << ": "
209 << projectIncludePaths;
210 includePaths.insert(
211 includePaths.end(), projectIncludePaths.begin(), projectIncludePaths.end());
212 }
213
214 rfile = kinematicUnitInterfacePrx->getRobotFilename();
215 ARMARX_VERBOSE << "Relative robot file " << rfile;
216 ArmarXDataPath::getAbsolutePath(rfile, rfile, includePaths);
217 ARMARX_VERBOSE << "Absolute robot file " << rfile;
218
219 robotNodeSetName = kinematicUnitInterfacePrx->getRobotNodeSetName();
220 }
221 catch (...)
222 {
223 ARMARX_ERROR << "Unable to retrieve robot filename.";
224 }
225
226 try
227 {
228 ARMARX_INFO << "Loading robot from file " << rfile;
229 robot = loadRobotFile(rfile);
230 }
231 catch (const std::exception& e)
232 {
233 ARMARX_ERROR << "Failed to init robot: " << e.what();
234 }
235 catch (...)
236 {
237 ARMARX_ERROR << "Failed to init robot";
238 }
239
240 if (!robot || !robot->hasRobotNodeSet(robotNodeSetName))
241 {
242 getObjectScheduler()->terminate();
243 if (getWidget()->parentWidget())
244 {
245 getWidget()->parentWidget()->close();
246 }
247 return;
248 }
249
250 // Check robot name and disable setZero Button if necessary
251 if (not simox::alg::starts_with(robot->getName(), "Armar3"))
252 {
253 ARMARX_VERBOSE << "Disable the SetZero button because the robot name is '"
254 << robot->getName() << "'.";
255 ui.pushButtonKinematicUnitPos1->setDisabled(true);
256 }
257
258 kinematicUnitFile = rfile;
259 robotNodeSet = robot->getRobotNodeSet(robotNodeSetName);
260
261 kinematicUnitVisualization = getCoinVisualization(robot);
262 kinematicUnitNode = kinematicUnitVisualization->getCoinVisualization();
263 robotVisu->addChild(kinematicUnitNode);
264
265 // Fetch the current joint angles.
267
268 initGUIComboBox(robotNodeSet); // init the pull down menu (QT: ComboBox)
269 initGUIJointListTable(robotNodeSet);
270
271 const auto initialDebugInfo = kinematicUnitInterfacePrx->getDebugInfo();
272
273 initializeUi(initialDebugInfo);
274
275 QMetaObject::invokeMethod(this, "resetSlider");
277
280 updateTask->start();
281 }
282
283 void
285 {
286 Metronome metronome(Frequency::Hertz(10));
287
289 {
290 fetchData();
291 metronome.waitForNextTick();
292 }
293
294 ARMARX_INFO << "Connection to kinemetic unit lost. Update task terminates.";
295 }
296
297 void
299 {
301
302 if (updateTask)
303 {
304 updateTask->stop();
305 updateTask->join();
306 updateTask = nullptr;
307 }
308
309 // killTimer(updateTimerId);
311
312 {
313 std::unique_lock lock(mutexNodeSet);
314 robot.reset();
315 robotNodeSet.reset();
316 currentNode.reset();
317 }
318
319 {
320 std::unique_lock lock(*mutex3D);
321 robotVisu->removeAllChildren();
322 debugLayerVisu->removeAllChildren();
323 }
324 }
325
326 void
328 {
330
331 if (updateTask)
332 {
333 updateTask->stop();
334 updateTask->join();
335 updateTask = nullptr;
336 }
337
339
340 {
341 std::unique_lock lock(*mutex3D);
342
343 if (robotVisu)
344 {
345 robotVisu->removeAllChildren();
346 robotVisu->unref();
347 robotVisu = NULL;
348 }
349
350 if (debugLayerVisu)
351 {
352 debugLayerVisu->removeAllChildren();
353 debugLayerVisu->unref();
354 debugLayerVisu = NULL;
355 }
356
357 if (rootVisu)
358 {
359 rootVisu->removeAllChildren();
360 rootVisu->unref();
361 rootVisu = NULL;
362 }
363 }
364
365 /*
366 if (debugDrawer && debugDrawer->getObjectScheduler())
367 {
368 ARMARX_INFO << "Removing DebugDrawer component...";
369 debugDrawer->getObjectScheduler()->terminate();
370 ARMARX_INFO << "Removing DebugDrawer component...done";
371 }
372 */
373 }
374
375 QPointer<QDialog>
377 {
378 if (!dialog)
379 {
380 dialog = new KinematicUnitConfigDialog(parent);
381 dialog->setName(dialog->getDefaultName());
382 }
383
384 return qobject_cast<KinematicUnitConfigDialog*>(dialog);
385 }
386
387 void
389 {
390 ARMARX_VERBOSE << "KinematicUnitWidget::configured()";
391 kinematicUnitName = dialog->proxyFinder->getSelectedProxyName().toStdString();
392 enableValueValidator = dialog->ui->checkBox->isChecked();
393 viewerEnabled = dialog->ui->checkBox3DViewerEnabled->isChecked();
394 historyTime = dialog->ui->spinBoxHistory->value() * 1000;
395 currentValueMax = dialog->ui->doubleSpinBoxMaxMinCurrent->value();
396 }
397
398 void
400 {
401 kinematicUnitName = settings->value("kinematicUnitName", KINEMATIC_UNIT_NAME_DEFAULT)
402 .toString()
403 .toStdString();
404 enableValueValidator = settings->value("enableValueValidator", true).toBool();
405 viewerEnabled = settings->value("viewerEnabled", true).toBool();
406 historyTime = settings->value("historyTime", 100).toInt() * 1000;
407 currentValueMax = settings->value("currentValueMax", 5.0).toFloat();
408 }
409
410 void
412 {
413 settings->setValue("kinematicUnitName", QString::fromStdString(kinematicUnitName));
414 settings->setValue("enableValueValidator", enableValueValidator);
415 settings->setValue("viewerEnabled", viewerEnabled);
416 assert(historyTime % 1000 == 0);
417 settings->setValue("historyTime", static_cast<int>(historyTime / 1000));
418 settings->setValue("currentValueMax", currentValueMax);
419 }
420
421 void
423 {
424 if (debugDrawer)
425 {
426 if (show)
427 {
428 debugDrawer->enableAllLayers();
429 }
430 else
431 {
432 debugDrawer->disableAllLayers();
433 }
434 }
435 }
436
437 void
439 {
440 NameValueMap values;
441 {
442 std::unique_lock lock(mutexNodeSet);
443
445 const auto debugInfo = kinematicUnitInterfacePrx->getDebugInfo();
446
447 const auto selectedControlMode = getSelectedControlMode();
448
449 if (selectedControlMode == ePositionControl)
450 {
451 values = debugInfo.jointAngles;
452 }
453 else if (selectedControlMode == eVelocityControl)
454 {
455 values = debugInfo.jointVelocities;
456 }
457 }
458
459 JSONObjectPtr serializer = new JSONObject();
460 for (auto& kv : values)
461 {
462 serializer->setFloat(kv.first, kv.second);
463 }
464 const QString json = QString::fromStdString(serializer->asString(true));
465 QClipboard* clipboard = QApplication::clipboard();
466 clipboard->setText(json);
467 QApplication::processEvents();
468 }
469
470 void
472 {
473 // modelUpdateCB();
474 }
475
476 void
480
481 void
485
486 SoNode*
488 {
489 if (viewerEnabled)
490 {
491 ARMARX_INFO << "Returning scene ";
492 return rootVisu;
493 }
494 else
495 {
496 ARMARX_INFO << "viewer disabled - returning null scene";
497 return NULL;
498 }
499 }
500
501 void
503 {
504 connect(ui.pushButtonKinematicUnitPos1,
505 SIGNAL(clicked()),
506 this,
507 SLOT(kinematicUnitZeroPosition()));
508
509 connect(
510 ui.nodeListComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(selectJoint(int)));
511 connect(ui.horizontalSliderKinematicUnitPos,
512 SIGNAL(valueChanged(int)),
513 this,
514 SLOT(sliderValueChanged(int)));
515
516 connect(ui.horizontalSliderKinematicUnitPos,
517 SIGNAL(sliderReleased()),
518 this,
520
521 connect(ui.radioButtonPositionControl,
522 SIGNAL(clicked(bool)),
523 this,
524 SLOT(setControlModePosition()));
525 connect(ui.radioButtonVelocityControl,
526 SIGNAL(clicked(bool)),
527 this,
528 SLOT(setControlModeVelocity()));
529 connect(
530 ui.radioButtonTorqueControl, SIGNAL(clicked(bool)), this, SLOT(setControlModeTorque()));
531 connect(
532 ui.pushButtonFromJson, SIGNAL(clicked()), this, SLOT(on_pushButtonFromJson_clicked()));
533
534 connect(ui.copyToClipboard, SIGNAL(clicked()), this, SLOT(copyToClipboard()));
535 connect(ui.showDebugLayer,
536 SIGNAL(toggled(bool)),
537 this,
538 SLOT(showVisuLayers(bool)),
539 Qt::QueuedConnection);
540
541 connect(this,
542 SIGNAL(jointAnglesReported()),
543 this,
545 Qt::QueuedConnection);
546 connect(this,
547 SIGNAL(jointVelocitiesReported()),
548 this,
550 Qt::QueuedConnection);
551 connect(this,
552 SIGNAL(jointTorquesReported()),
553 this,
555 Qt::QueuedConnection);
556 connect(this,
557 SIGNAL(jointCurrentsReported()),
558 this,
560 Qt::QueuedConnection);
561 connect(this,
563 this,
565 Qt::QueuedConnection);
566 connect(this,
568 this,
570 Qt::QueuedConnection);
571 connect(this,
572 SIGNAL(jointStatusesReported()),
573 this,
575 Qt::QueuedConnection);
576
577 connect(ui.tableJointList,
578 SIGNAL(cellDoubleClicked(int, int)),
579 this,
580 SLOT(selectJointFromTableWidget(int, int)),
581 Qt::QueuedConnection);
582
583 connect(ui.checkBoxUseDegree,
584 SIGNAL(clicked()),
585 this,
586 SLOT(resetSlider()),
587 Qt::QueuedConnection);
588 connect(ui.checkBoxUseDegree, SIGNAL(clicked()), this, SLOT(setControlModePosition()));
589 connect(ui.checkBoxUseDegree, SIGNAL(clicked()), this, SLOT(setControlModeVelocity()));
590 connect(ui.checkBoxUseDegree, SIGNAL(clicked()), this, SLOT(setControlModeTorque()));
591
592 connect(this,
593 SIGNAL(onDebugInfoReceived(const DebugInfo&)),
594 this,
595 SLOT(debugInfoReceived(const DebugInfo&)));
596 }
597
598 void
600 {
601 //signal clicked is not emitted if you call setDown(), setChecked() or toggle().
602
603 // there is no default control mode
604 setControlModeRadioButtonGroup(ControlMode::eUnknown);
605
606 ui.widgetSliderFactor->setVisible(false);
607
608 fetchData();
609 }
610
611 void
613 {
614 if (!robotNodeSet)
615 {
616 return;
617 }
618
619 std::unique_lock lock(mutexNodeSet);
620 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
621 NameValueMap vels;
622 NameControlModeMap jointModes;
623
624 for (unsigned int i = 0; i < rn.size(); i++)
625 {
626 jointModes[rn[i]->getName()] = eVelocityControl;
627 vels[rn[i]->getName()] = 0.0f;
628 }
629
630 try
631 {
632 kinematicUnitInterfacePrx->switchControlMode(jointModes);
633 kinematicUnitInterfacePrx->setJointVelocities(vels);
634 }
635 catch (...)
636 {
637 }
638
639 const auto selectedControlMode = getSelectedControlMode();
640 if (selectedControlMode == eVelocityControl)
641 {
642 ui.horizontalSliderKinematicUnitPos->setSliderPosition(SLIDER_ZERO_POSITION);
643 }
644 }
645
646 void
648 {
649 const auto selectedControlMode = getSelectedControlMode();
650
651 if (selectedControlMode == eVelocityControl || selectedControlMode == eTorqueControl)
652 {
654 }
655 else if (selectedControlMode == ePositionControl)
656 {
657 if (currentNode)
658 {
659 if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or
660 currentNode->isFourBarJoint())
661 {
662 const bool isDeg = ui.checkBoxUseDegree->isChecked();
663 const auto factor =
665 const float conversionFactor = isDeg ? 180.0 / M_PI : 1.0f;
666 const float pos = currentNode->getJointValue() * conversionFactor;
667
668 ui.lcdNumberKinematicUnitJointValue->display((int)pos);
669 ui.horizontalSliderKinematicUnitPos->setSliderPosition((int)(pos * factor));
670 }
671
672 if (currentNode->isTranslationalJoint())
673 {
674 const auto factor = SLIDER_POS_DEG_MULTIPLIER;
675 const float pos = currentNode->getJointValue();
676
677 ui.lcdNumberKinematicUnitJointValue->display((int)pos);
678 ui.horizontalSliderKinematicUnitPos->setSliderPosition((int)(pos * factor));
679 }
680 }
681 }
682 }
683
684 void
686 {
687 const auto selectedControlMode = getSelectedControlMode();
688
689 if (selectedControlMode == eVelocityControl || selectedControlMode == eTorqueControl)
690 {
691 ui.horizontalSliderKinematicUnitPos->setSliderPosition(SLIDER_ZERO_POSITION);
692 ui.lcdNumberKinematicUnitJointValue->display(SLIDER_ZERO_POSITION);
693 }
694 }
695
696 void
698 {
699 ARMARX_VERBOSE << "Setting control mode of radio button group to " << controlMode;
700
701 switch (controlMode)
702 {
703 case eDisabled:
704 case eUnknown:
705 case ePositionVelocityControl:
706 ui.radioButtonUnknown->setChecked(true);
707 break;
708 case ePositionControl:
709 ui.radioButtonPositionControl->setChecked(true);
710 break;
711 case eVelocityControl:
712 ui.radioButtonVelocityControl->setChecked(true);
713 break;
714 case eTorqueControl:
715 ui.radioButtonTorqueControl->setChecked(true);
716 break;
717 }
718 }
719
720 void
722 {
723 if (!ui.radioButtonPositionControl->isChecked())
724 {
725 return;
726 }
727 NameControlModeMap jointModes;
728 // selectedControlMode = ePositionControl;
729 ui.widgetSliderFactor->setVisible(false);
730
731 // FIXME currentNode should be passed to this function!
732
733 if (currentNode)
734 {
735 const QString unit = [&]() -> QString
736 {
737 if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or
738 currentNode->isFourBarJoint())
739 {
740 if (ui.checkBoxUseDegree->isChecked())
741 {
742 return "deg";
743 }
744
745 return "rad";
746 }
747
748 if (currentNode->isTranslationalJoint())
749 {
750 return "mm";
751 }
752
753 throw std::invalid_argument("unknown/unsupported joint type");
754 }();
755
756 ui.labelUnit->setText(unit);
757
758
759 const auto [factor, conversionFactor] = [&]() -> std::pair<float, float>
760 {
761 if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or
762 currentNode->isFourBarJoint())
763 {
764 const bool isDeg = ui.checkBoxUseDegree->isChecked();
765 if (isDeg)
766 {
767 return {SLIDER_POS_DEG_MULTIPLIER, 180.0 / M_PI};
768 }
769 return {SLIDER_POS_RAD_MULTIPLIER, 1};
770 }
771
772 if (currentNode->isTranslationalJoint())
773 {
774 return {SLIDER_POS_DEG_MULTIPLIER, 1};
775 }
776
777 throw std::invalid_argument("unknown/unsupported joint type");
778 }();
779
780 jointModes[currentNode->getName()] = ePositionControl;
781
783 {
784 kinematicUnitInterfacePrx->switchControlMode(jointModes);
785 }
786
787 const float lo = currentNode->getJointLimitLo() * conversionFactor;
788 const float hi = currentNode->getJointLimitHi() * conversionFactor;
789
790 if (hi - lo <= 0.0f)
791 {
792 return;
793 }
794
795 {
796 // currentNode->getJointValue() can we wrong after we re-connected to the robot unit.
797 // E.g., it can be 0 although the torso joint was at -365 before the unit disconnected.
798 // Therefore, we first have to fetch the actual joint values and use that one.
799 // However, this should actually not be necessary, as the robot model should be updated
800 // via the topics.
802 }
803
804 const float pos = currentNode->getJointValue() * conversionFactor;
805 ARMARX_INFO << "Setting position control for current node "
806 << "(name '" << currentNode->getName() << "' with current value " << pos
807 << ")";
808
809 // Setting the slider position to pos will set the position to the slider tick closest to pos
810 // This will initially send a position target with a small delta to the joint.
811 ui.horizontalSliderKinematicUnitPos->blockSignals(true);
812
813 const float sliderMax = hi * factor;
814 const float sliderMin = lo * factor;
815
816 ui.horizontalSliderKinematicUnitPos->setMaximum(sliderMax);
817 ui.horizontalSliderKinematicUnitPos->setMinimum(sliderMin);
818
819 const std::size_t desiredNumberOfTicks = 1'000;
820
821 const float tickInterval = (sliderMax - sliderMin) / desiredNumberOfTicks;
822 ARMARX_INFO << VAROUT(tickInterval);
823
824 ui.horizontalSliderKinematicUnitPos->setTickInterval(tickInterval);
825 ui.lcdNumberKinematicUnitJointValue->display(pos);
826
827 ui.horizontalSliderKinematicUnitPos->blockSignals(false);
828 resetSlider();
829 }
830 }
831
832 void
834 {
835 if (!ui.radioButtonVelocityControl->isChecked())
836 {
837 return;
838 }
839 NameControlModeMap jointModes;
840 NameValueMap jointVelocities;
841
842 if (currentNode)
843 {
844 jointModes[currentNode->getName()] = eVelocityControl;
845
846 // set the velocity to zero to stop any previous controller (e.g. torque controller)
847 jointVelocities[currentNode->getName()] = 0;
848
849
850 const QString unit = [&]() -> QString
851 {
852 if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or
853 currentNode->isFourBarJoint())
854 {
855 if (ui.checkBoxUseDegree->isChecked())
856 {
857 return "deg/s";
858 }
859
860 return "rad/(100*s)";
861 }
862
863 if (currentNode->isTranslationalJoint())
864 {
865 return "mm/s";
866 }
867
868 throw std::invalid_argument("unknown/unsupported joint type");
869 }();
870
871
872 ui.labelUnit->setText(unit);
873 ARMARX_INFO << "setting velocity control for current Node Name: "
874 << currentNode->getName() << flush;
875
876 const bool isDeg = ui.checkBoxUseDegree->isChecked();
877 const bool isRot = currentNode->isRotationalJoint() or
878 currentNode->isHemisphereJoint() or currentNode->isFourBarJoint();
879
880 const float lo = isRot ? (isDeg ? -90 : -M_PI * 100) : -1000;
881 const float hi = isRot ? (isDeg ? +90 : +M_PI * 100) : 1000;
882
883 try
884 {
886 {
887 kinematicUnitInterfacePrx->switchControlMode(jointModes);
888 kinematicUnitInterfacePrx->setJointVelocities(jointVelocities);
889 }
890 }
891 catch (...)
892 {
893 }
894
895 ui.widgetSliderFactor->setVisible(true);
896
897 ui.horizontalSliderKinematicUnitPos->blockSignals(true);
898 ui.horizontalSliderKinematicUnitPos->setMaximum(hi);
899 ui.horizontalSliderKinematicUnitPos->setMinimum(lo);
900 ui.horizontalSliderKinematicUnitPos->blockSignals(false);
901 resetSlider();
902 }
903 }
904
905 ControlMode
907 {
908 if (ui.radioButtonPositionControl->isChecked())
909 {
910 return ControlMode::ePositionControl;
911 }
912
913 if (ui.radioButtonVelocityControl->isChecked())
914 {
915 return ControlMode::eVelocityControl;
916 }
917
918 if (ui.radioButtonTorqueControl->isChecked())
919 {
920 return ControlMode::eTorqueControl;
921 }
922
923 // if no button is checked, then the joint is likely initialized but no controller has been loaded yet
924 // (well, the no movement controller should be active)
925 return ControlMode::eUnknown;
926 }
927
928 void
930 {
931 if (!ui.radioButtonTorqueControl->isChecked())
932 {
933 return;
934 }
935 NameControlModeMap jointModes;
936
937 if (currentNode)
938 {
939 jointModes[currentNode->getName()] = eTorqueControl;
940 ui.labelUnit->setText("Ncm");
941 ARMARX_INFO << "setting torque control for current Node Name: "
942 << currentNode->getName() << flush;
943
945 {
946 try
947 {
948 kinematicUnitInterfacePrx->switchControlMode(jointModes);
949 }
950 catch (...)
951 {
952 }
953 }
954
955 ui.horizontalSliderKinematicUnitPos->blockSignals(true);
956 ui.horizontalSliderKinematicUnitPos->setMaximum(20000.0);
957 ui.horizontalSliderKinematicUnitPos->setMinimum(-20000.0);
958
959 ui.widgetSliderFactor->setVisible(true);
960
961 ui.horizontalSliderKinematicUnitPos->blockSignals(false);
962 resetSlider();
963 }
964 }
965
967 KinematicUnitWidgetController::loadRobotFile(std::string fileName)
968 {
970
971 if (verbose)
972 {
973 ARMARX_INFO << "Loading KinematicUnit " << kinematicUnitName << " from "
974 << kinematicUnitFile << " ..." << flush;
975 }
976
977 if (!ArmarXDataPath::getAbsolutePath(fileName, fileName))
978 {
979 ARMARX_INFO << "Could not find Robot XML file with name " << fileName << flush;
980 }
981
982 robot = VirtualRobot::RobotIO::loadRobot(fileName);
983
984 if (!robot)
985 {
986 ARMARX_INFO << "Could not find Robot XML file with name " << fileName << "("
987 << kinematicUnitName << ")" << flush;
988 }
989
990 return robot;
991 }
992
993 VirtualRobot::CoinVisualizationPtr
994 KinematicUnitWidgetController::getCoinVisualization(VirtualRobot::RobotPtr robot)
995 {
996 VirtualRobot::CoinVisualizationPtr coinVisualization;
997
998 if (robot != NULL)
999 {
1000 ARMARX_VERBOSE << "getting coin visualization" << flush;
1001 coinVisualization = robot->getVisualization();
1002
1003 if (!coinVisualization || !coinVisualization->getCoinVisualization())
1004 {
1005 ARMARX_INFO << "could not get coin visualization" << flush;
1006 }
1007 }
1008
1009 return coinVisualization;
1010 }
1011
1012 VirtualRobot::RobotNodeSetPtr
1013 KinematicUnitWidgetController::getRobotNodeSet(VirtualRobot::RobotPtr robot,
1014 std::string nodeSetName)
1015 {
1016 VirtualRobot::RobotNodeSetPtr nodeSetPtr;
1017
1018 if (robot)
1019 {
1020 nodeSetPtr = robot->getRobotNodeSet(nodeSetName);
1021
1022 if (!nodeSetPtr)
1023 {
1024 ARMARX_INFO << "RobotNodeSet with name " << nodeSetName << " is not defined"
1025 << flush;
1026 }
1027 }
1028
1029 return nodeSetPtr;
1030 }
1031
1032 bool
1033 KinematicUnitWidgetController::initGUIComboBox(VirtualRobot::RobotNodeSetPtr robotNodeSet)
1034 {
1035 ui.nodeListComboBox->clear();
1036
1037 if (robotNodeSet)
1038 {
1039 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1040
1041 for (unsigned int i = 0; i < rn.size(); i++)
1042 {
1043 // ARMARX_INFO << "adding item to joint combo box" << rn[i]->getName() << flush;
1044 QString name(rn[i]->getName().c_str());
1045 ui.nodeListComboBox->addItem(name);
1046 }
1047 ui.nodeListComboBox->setCurrentIndex(-1);
1048 return true;
1049 }
1050 return false;
1051 }
1052
1053 bool
1054 KinematicUnitWidgetController::initGUIJointListTable(VirtualRobot::RobotNodeSetPtr robotNodeSet)
1055 {
1056 uint numberOfColumns = 10;
1057
1058 //dont use clear! It is not required here and somehow causes the tabel to have
1059 //numberOfColumns additional empty columns and rn.size() additional empty rows.
1060 //Somehow columncount (rowcount) stay at numberOfColumns (rn.size())
1061 //ui.tableJointList->clear();
1062
1063 if (robotNodeSet)
1064 {
1065 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1066
1067 //set dimension of table
1068 //ui.tableJointList->setColumnWidth(0,110);
1069
1070 //ui.tableJointList->horizontalHeader()->setResizeMode(QHeaderView::ResizeToContents);
1071 ui.tableJointList->setRowCount(rn.size());
1072 ui.tableJointList->setColumnCount(eTabelColumnCount);
1073
1074
1075 //ui.tableJointList->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
1076
1077 // set table header
1078 // if the order is changed dont forget to update the order in the enum JointTabelColumnIndex
1079 // in theheader file
1080 QStringList s;
1081 s << "Joint Name"
1082 << "Control Mode"
1083 << "Angle [deg]/Position [mm]"
1084 << "Velocity [deg/s]/[mm/s]"
1085 << "Torque [Nm] / PWM"
1086 << "Current [A]"
1087 << "Temperature [C]"
1088 << "Operation"
1089 << "Error"
1090 << "Enabled"
1091 << "Emergency Stop";
1092 ui.tableJointList->setHorizontalHeaderLabels(s);
1093 ARMARX_CHECK_EXPRESSION(ui.tableJointList->columnCount() == eTabelColumnCount)
1094 << "Current table size: " << ui.tableJointList->columnCount();
1095
1096
1097 // fill in joint names
1098 for (unsigned int i = 0; i < rn.size(); i++)
1099 {
1100 // ARMARX_INFO << "adding item to joint table" << rn[i]->getName() << flush;
1101 QString name(rn[i]->getName().c_str());
1102
1103 QTableWidgetItem* newItem = new QTableWidgetItem(name);
1104 ui.tableJointList->setItem(i, eTabelColumnName, newItem);
1105 }
1106
1107 // init missing table fields with default values
1108 for (unsigned int i = 0; i < rn.size(); i++)
1109 {
1110 for (unsigned int j = 1; j < numberOfColumns; j++)
1111 {
1112 QString state = "--";
1113 QTableWidgetItem* newItem = new QTableWidgetItem(state);
1114 ui.tableJointList->setItem(i, j, newItem);
1115 }
1116 }
1117
1118 //hide columns Operation, Error, Enabled and Emergency Stop
1119 //they will be shown when changes occur
1120 ui.tableJointList->setColumnHidden(eTabelColumnTemperature, true);
1121 ui.tableJointList->setColumnHidden(eTabelColumnOperation, true);
1122 ui.tableJointList->setColumnHidden(eTabelColumnError, true);
1123 ui.tableJointList->setColumnHidden(eTabelColumnEnabled, true);
1124 ui.tableJointList->setColumnHidden(eTabelColumnEmergencyStop, true);
1125
1126 return true;
1127 }
1128
1129 return false;
1130 }
1131
1132 void
1134 {
1135 std::unique_lock lock(mutexNodeSet);
1136
1137 ARMARX_INFO << "Selected index: " << ui.nodeListComboBox->currentIndex();
1138
1139 if (!robotNodeSet || i < 0 || i >= static_cast<int>(robotNodeSet->getSize()))
1140 {
1141 return;
1142 }
1143
1144 currentNode = robotNodeSet->getAllRobotNodes()[i];
1145 ARMARX_IMPORTANT << "Selected joint is `" << currentNode->getName() << "`.";
1146
1147 const auto controlModes = kinematicUnitInterfacePrx->getControlModes();
1148 if (controlModes.count(currentNode->getName()) == 0)
1149 {
1150 ARMARX_ERROR << "Could not retrieve control mode for joint `" << currentNode->getName()
1151 << "` from kinematic unit!";
1152 return;
1153 }
1154
1155 const auto controlMode = controlModes.at(currentNode->getName());
1156 setControlModeRadioButtonGroup(controlMode);
1157
1158 if (controlMode == ePositionControl)
1159 {
1161 }
1162 else if (controlMode == eVelocityControl)
1163 {
1165 ui.horizontalSliderKinematicUnitPos->setSliderPosition(SLIDER_ZERO_POSITION);
1166 }
1167 else if (controlMode == eTorqueControl)
1168 {
1170 ui.horizontalSliderKinematicUnitPos->setSliderPosition(SLIDER_ZERO_POSITION);
1171 }
1172 }
1173
1174 void
1176 {
1177 if (column == eTabelColumnName)
1178 {
1179 ui.nodeListComboBox->setCurrentIndex(row);
1180 // selectJoint(row);
1181 }
1182 }
1183
1184 void
1186 {
1187 std::unique_lock lock(mutexNodeSet);
1188
1189 if (!currentNode)
1190 {
1191 return;
1192 }
1193
1194 const float value = static_cast<float>(ui.horizontalSliderKinematicUnitPos->value());
1195
1196 const ControlMode currentControlMode = getSelectedControlMode();
1197
1198 const bool isDeg = ui.checkBoxUseDegree->isChecked();
1199 const bool isRot = currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or
1200 currentNode->isFourBarJoint();
1201
1202 if (currentControlMode == ePositionControl)
1203 {
1204 const auto factor =
1206 float conversionFactor = isRot && isDeg ? 180.0 / M_PI : 1.0f;
1207
1208 NameValueMap jointAngles;
1209
1210 jointAngles[currentNode->getName()] = value / conversionFactor / factor;
1211 ui.lcdNumberKinematicUnitJointValue->display(value / factor);
1213 {
1214 try
1215 {
1216 kinematicUnitInterfacePrx->setJointAngles(jointAngles);
1217 }
1218 catch (...)
1219 {
1220 }
1221 }
1222 }
1223 else if (currentControlMode == eVelocityControl)
1224 {
1225 float conversionFactor = isRot ? (isDeg ? 180.0 / M_PI : 100.f) : 1.0f;
1226 NameValueMap jointVelocities;
1227 jointVelocities[currentNode->getName()] =
1228 value / conversionFactor *
1229 static_cast<float>(ui.doubleSpinBoxKinematicUnitPosFactor->value());
1230 ui.lcdNumberKinematicUnitJointValue->display(value);
1231
1233 {
1234 try
1235 {
1236 kinematicUnitInterfacePrx->setJointVelocities(jointVelocities);
1237 }
1238 catch (...)
1239 {
1240 }
1241 }
1242 }
1243 else if (currentControlMode == eTorqueControl)
1244 {
1245 NameValueMap jointTorques;
1246 float torqueTargetValue =
1247 value / 100.0f *
1248 static_cast<float>(ui.doubleSpinBoxKinematicUnitPosFactor->value());
1249 jointTorques[currentNode->getName()] = torqueTargetValue;
1250 ui.lcdNumberKinematicUnitJointValue->display(torqueTargetValue);
1251
1253 {
1254 try
1255 {
1256 kinematicUnitInterfacePrx->setJointTorques(jointTorques);
1257 }
1258 catch (...)
1259 {
1260 }
1261 }
1262 }
1263 else
1264 {
1265 ARMARX_INFO << "current ControlModes unknown" << flush;
1266 }
1267 }
1268
1269 void
1271 const NameControlModeMap& reportedJointControlModes)
1272 {
1273 if (!getWidget() || !robotNodeSet)
1274 {
1275 return;
1276 }
1277
1278 std::unique_lock lock(mutexNodeSet);
1279 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1280
1281 for (unsigned int i = 0; i < rn.size(); i++)
1282 {
1283 NameControlModeMap::const_iterator it;
1284 it = reportedJointControlModes.find(rn[i]->getName());
1285 QString state;
1286
1287 if (it == reportedJointControlModes.end())
1288 {
1289 state = "unknown";
1290 }
1291 else
1292 {
1293 ControlMode currentMode = it->second;
1294
1295
1296 switch (currentMode)
1297 {
1298 /*case eNoMode:
1299 state = "None";
1300 break;
1301
1302 case eUnknownMode:
1303 state = "Unknown";
1304 break;
1305 */
1306 case eDisabled:
1307 state = "Disabled";
1308 break;
1309
1310 case eUnknown:
1311 state = "Unknown";
1312 break;
1313
1314 case ePositionControl:
1315 state = "Position";
1316 break;
1317
1318 case eVelocityControl:
1319 state = "Velocity";
1320 break;
1321
1322 case eTorqueControl:
1323 state = "Torque";
1324 break;
1325
1326
1327 case ePositionVelocityControl:
1328 state = "Position + Velocity";
1329 break;
1330
1331 default:
1332 //show the value of the mode so it can be implemented
1333 state = QString("<nyi Mode: %1>").arg(static_cast<int>(currentMode));
1334 break;
1335 }
1336 }
1337
1338 QTableWidgetItem* newItem = new QTableWidgetItem(state);
1339 ui.tableJointList->setItem(i, eTabelColumnControlMode, newItem);
1340 }
1341 }
1342
1343 void
1345 const NameStatusMap& reportedJointStatuses)
1346 {
1347 if (!getWidget() || !robotNodeSet || reportedJointStatuses.empty())
1348 {
1349 return;
1350 }
1351
1352 std::unique_lock lock(mutexNodeSet);
1353 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1354
1355 for (unsigned int i = 0; i < rn.size(); i++)
1356 {
1357
1358 auto it = reportedJointStatuses.find(rn[i]->getName());
1359 if (it == reportedJointStatuses.end())
1360 {
1361 ARMARX_VERBOSE << deactivateSpam(5, rn[i]->getName()) << "Joint Status for "
1362 << rn[i]->getName() << " was not reported!";
1363 continue;
1364 }
1365 JointStatus currentStatus = it->second;
1366
1367 QString state = translateStatus(currentStatus.operation);
1368 QTableWidgetItem* newItem = new QTableWidgetItem(state);
1369 ui.tableJointList->setItem(i, eTabelColumnOperation, newItem);
1370
1371 state = translateStatus(currentStatus.error);
1372 newItem = new QTableWidgetItem(state);
1373 ui.tableJointList->setItem(i, eTabelColumnError, newItem);
1374
1375 state = currentStatus.enabled ? "yes" : "no";
1376 newItem = new QTableWidgetItem(state);
1377 ui.tableJointList->setItem(i, eTabelColumnEnabled, newItem);
1378
1379 state = currentStatus.emergencyStop ? "yes" : "no";
1380 newItem = new QTableWidgetItem(state);
1381 ui.tableJointList->setItem(i, eTabelColumnEmergencyStop, newItem);
1382 }
1383
1384 //show columns
1385 ui.tableJointList->setColumnHidden(eTabelColumnOperation, false);
1386 ui.tableJointList->setColumnHidden(eTabelColumnError, false);
1387 ui.tableJointList->setColumnHidden(eTabelColumnEnabled, false);
1388 ui.tableJointList->setColumnHidden(eTabelColumnEmergencyStop, false);
1389 }
1390
1391 QString
1393 {
1394 switch (status)
1395 {
1396 case eOffline:
1397 return "Offline";
1398
1399 case eOnline:
1400 return "Online";
1401
1402 case eInitialized:
1403 return "Initialized";
1404
1405 default:
1406 return "?";
1407 }
1408 }
1409
1410 QString
1412 {
1413 switch (status)
1414 {
1415 case eOk:
1416 return "Ok";
1417
1418 case eWarning:
1419 return "Wr";
1420
1421 case eError:
1422 return "Er";
1423
1424 default:
1425 return "?";
1426 }
1427 }
1428
1429 void
1430 KinematicUnitWidgetController::updateJointAnglesTable(const NameValueMap& reportedJointAngles)
1431 {
1432 std::unique_lock lock(mutexNodeSet);
1433
1434 if (!robotNodeSet)
1435 {
1436 return;
1437 }
1438 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1439
1440
1441 for (unsigned int i = 0; i < rn.size(); i++)
1442 {
1443 NameValueMap::const_iterator it;
1444 VirtualRobot::RobotNodePtr node = rn[i];
1445 it = reportedJointAngles.find(node->getName());
1446
1447 if (it == reportedJointAngles.end())
1448 {
1449 continue;
1450 }
1451
1452 const float currentValue = it->second;
1453
1454 QModelIndex index = ui.tableJointList->model()->index(i, eTabelColumnAngleProgressbar);
1455 float conversionFactor = ui.checkBoxUseDegree->isChecked() &&
1456 (node->isRotationalJoint() or
1457 node->isHemisphereJoint() or node->isFourBarJoint())
1458 ? 180.0 / M_PI
1459 : 1;
1460 ui.tableJointList->model()->setData(
1461 index,
1462 (int)(cutJitter(currentValue * conversionFactor) * 100) / 100.0f,
1464 ui.tableJointList->model()->setData(
1465 index, node->getJointLimitHigh() * conversionFactor, eJointHiRole);
1466 ui.tableJointList->model()->setData(
1467 index, node->getJointLimitLow() * conversionFactor, eJointLoRole);
1468 }
1469 }
1470
1471 void
1473 const NameValueMap& reportedJointVelocities)
1474 {
1475 if (!getWidget())
1476 {
1477 return;
1478 }
1479
1480 std::unique_lock lock(mutexNodeSet);
1481 if (!robotNodeSet)
1482 {
1483 return;
1484 }
1485 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1486 QTableWidgetItem* newItem;
1487
1488 for (unsigned int i = 0; i < rn.size(); i++)
1489 {
1490 NameValueMap::const_iterator it;
1491 it = reportedJointVelocities.find(rn[i]->getName());
1492
1493 if (it == reportedJointVelocities.end())
1494 {
1495 continue;
1496 }
1497
1498 float currentValue = it->second;
1499 if (ui.checkBoxUseDegree->isChecked() &&
1500 (rn[i]->isRotationalJoint() or rn[i]->isHemisphereJoint() or
1501 rn[i]->isFourBarJoint()))
1502 {
1503 currentValue *= 180.0 / M_PI;
1504 }
1505 const QString Text = QString::number(cutJitter(currentValue), 'g', 2);
1506 newItem = new QTableWidgetItem(Text);
1507 ui.tableJointList->setItem(i, eTabelColumnVelocity, newItem);
1508 }
1509 }
1510
1511 void
1512 KinematicUnitWidgetController::updateJointTorquesTable(const NameValueMap& reportedJointTorques)
1513 {
1514
1515
1516 std::unique_lock lock(mutexNodeSet);
1517 if (!getWidget() || !robotNodeSet)
1518 {
1519 return;
1520 }
1521 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1522 QTableWidgetItem* newItem;
1523 NameValueMap::const_iterator it;
1524
1525 for (unsigned int i = 0; i < rn.size(); i++)
1526 {
1527 it = reportedJointTorques.find(rn[i]->getName());
1528
1529 if (it == reportedJointTorques.end())
1530 {
1531 continue;
1532 }
1533
1534 const float currentValue = it->second;
1535 newItem = new QTableWidgetItem(QString::number(cutJitter(currentValue)));
1536 ui.tableJointList->setItem(i, eTabelColumnTorque, newItem);
1537 }
1538 }
1539
1540 void
1542 const NameValueMap& reportedJointCurrents,
1543 const NameStatusMap& reportedJointStatuses)
1544 {
1545
1546
1547 std::unique_lock lock(mutexNodeSet);
1548 if (!getWidget() || !robotNodeSet || jointCurrentHistory.size() == 0)
1549 {
1550 return;
1551 }
1552 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1553 QTableWidgetItem* newItem;
1554
1555 // FIXME history!
1556 // NameValueMap reportedJointCurrents = jointCurrentHistory.back().second;
1557 NameValueMap::const_iterator it;
1558
1559 for (unsigned int i = 0; i < rn.size(); i++)
1560 {
1561 it = reportedJointCurrents.find(rn[i]->getName());
1562
1563 if (it == reportedJointCurrents.end())
1564 {
1565 continue;
1566 }
1567
1568 const float currentValue = it->second;
1569 newItem = new QTableWidgetItem(QString::number(cutJitter(currentValue)));
1570 ui.tableJointList->setItem(i, eTabelColumnCurrent, newItem);
1571 }
1572
1573 highlightCriticalValues(reportedJointStatuses);
1574 }
1575
1576 void
1578 const NameValueMap& reportedJointTemperatures)
1579 {
1580
1581
1582 std::unique_lock lock(mutexNodeSet);
1583 if (!getWidget() || !robotNodeSet || reportedJointTemperatures.empty())
1584 {
1585 return;
1586 }
1587 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1588 QTableWidgetItem* newItem;
1589 NameValueMap::const_iterator it;
1590
1591 for (unsigned int i = 0; i < rn.size(); i++)
1592 {
1593 it = reportedJointTemperatures.find(rn[i]->getName());
1594
1595 if (it == reportedJointTemperatures.end())
1596 {
1597 continue;
1598 }
1599
1600 const float currentValue = it->second;
1601 newItem = new QTableWidgetItem(QString::number(cutJitter(currentValue)));
1602 ui.tableJointList->setItem(i, eTabelColumnTemperature, newItem);
1603 }
1604 ui.tableJointList->setColumnHidden(eTabelColumnTemperature, false);
1605 }
1606
1607 void
1608 KinematicUnitWidgetController::updateModel(const NameValueMap& reportedJointAngles)
1609 {
1610 // ARMARX_INFO << "updateModel()" << flush;
1611 std::unique_lock lock(mutexNodeSet);
1612 if (!robotNodeSet)
1613 {
1614 return;
1615 }
1616 robot->setJointValues(reportedJointAngles);
1617 }
1618
1619 std::optional<float>
1620 mean(const boost::circular_buffer<NameValueMap>& buffer, const std::string& key)
1621 {
1622 float sum = 0;
1623 std::size_t count = 0;
1624
1625 for (const auto& element : buffer)
1626 {
1627 if (element.count(key) > 0)
1628 {
1629 sum += element.at(key);
1630 }
1631 }
1632
1633 if (count == 0)
1634 {
1635 return std::nullopt;
1636 }
1637
1638 return sum / static_cast<float>(count);
1639 }
1640
1641 void
1643 const NameStatusMap& reportedJointStatuses)
1644 {
1645 if (!enableValueValidator)
1646 {
1647 return;
1648 }
1649
1650 std::unique_lock lock(mutexNodeSet);
1651
1652 std::vector<VirtualRobot::RobotNodePtr> rn = robotNodeSet->getAllRobotNodes();
1653
1654 // get standard line colors
1655 static std::vector<QBrush> standardColors;
1656 if (standardColors.size() == 0)
1657 {
1658 for (unsigned int i = 0; i < rn.size(); i++)
1659 {
1660 // all cells of a row have the same color
1661 standardColors.push_back(
1662 ui.tableJointList->item(i, eTabelColumnCurrent)->background());
1663 }
1664 }
1665
1666 // check robot current value of nodes
1667 for (unsigned int i = 0; i < rn.size(); i++)
1668 {
1669 const auto& jointName = rn[i]->getName();
1670
1671 const auto currentSmoothValOpt = mean(jointCurrentHistory, jointName);
1672 if (not currentSmoothValOpt.has_value())
1673 {
1674 continue;
1675 }
1676
1677 const float smoothValue = std::fabs(currentSmoothValOpt.value());
1678
1679 if (jointCurrentHistory.front().count(jointName) == 0)
1680 {
1681 continue;
1682 }
1683
1684 const float startValue = jointCurrentHistory.front().at(jointName);
1685 const bool isStatic = (smoothValue == startValue);
1686
1687 NameStatusMap::const_iterator it;
1688 it = reportedJointStatuses.find(rn[i]->getName());
1689 JointStatus currentStatus = it->second;
1690
1691 if (isStatic)
1692 {
1693 if (currentStatus.operation != eOffline)
1694 {
1695 // current value is zero, but joint is not offline
1696 ui.tableJointList->item(i, eTabelColumnCurrent)->setBackground(Qt::yellow);
1697 }
1698 }
1699 else if (std::abs(smoothValue) > currentValueMax)
1700 {
1701 // current value is too high
1702 ui.tableJointList->item(i, eTabelColumnCurrent)->setBackground(Qt::red);
1703 }
1704 else
1705 {
1706 // everything seems to work as expected
1707 ui.tableJointList->item(i, eTabelColumnCurrent)->setBackground(standardColors[i]);
1708 }
1709 }
1710 }
1711
1712 void
1714 {
1715 this->mutex3D = mutex3D;
1716
1717 if (debugDrawer)
1718 {
1719 debugDrawer->setMutex(mutex3D);
1720 }
1721 }
1722
1723 QPointer<QWidget>
1725 {
1726 if (customToolbar)
1727 {
1728 customToolbar->setParent(parent);
1729 }
1730 else
1731 {
1732 customToolbar = new QToolBar(parent);
1733 customToolbar->addAction("ZeroVelocity", this, SLOT(kinematicUnitZeroVelocity()));
1734 }
1735 return customToolbar.data();
1736 }
1737
1738 float
1739 KinematicUnitWidgetController::cutJitter(float value)
1740 {
1741 return (abs(value) < static_cast<float>(ui.jitterThresholdSpinBox->value())) ? 0 : value;
1742 }
1743
1744 void
1746 {
1747 ARMARX_DEBUG << "updateGui";
1748
1750 {
1751 ARMARX_WARNING << "KinematicUnit is not available!";
1752 return;
1753 }
1754
1755 const auto debugInfo = kinematicUnitInterfacePrx->getDebugInfo();
1756
1757 emit onDebugInfoReceived(debugInfo);
1758 }
1759
1760 void
1762 {
1763 ARMARX_DEBUG << "debug info received";
1764
1765 updateModel(debugInfo.jointAngles);
1766
1767 updateJointAnglesTable(debugInfo.jointAngles);
1768 updateJointVelocitiesTable(debugInfo.jointVelocities);
1769 updateJointTorquesTable(debugInfo.jointTorques);
1770 updateJointCurrentsTable(debugInfo.jointCurrents, debugInfo.jointStatus);
1771 updateControlModesTable(debugInfo.jointModes);
1772 updateJointStatusesTable(debugInfo.jointStatus);
1773 updateMotorTemperaturesTable(debugInfo.jointMotorTemperatures);
1774 }
1775
1776 void
1777 RangeValueDelegate::paint(QPainter* painter,
1778 const QStyleOptionViewItem& option,
1779 const QModelIndex& index) const
1780 {
1782 {
1783 float jointValue = index.data(KinematicUnitWidgetController::eJointAngleRole).toFloat();
1784 float loDeg = index.data(KinematicUnitWidgetController::eJointLoRole).toFloat();
1785 float hiDeg = index.data(KinematicUnitWidgetController::eJointHiRole).toFloat();
1786
1787 if (hiDeg - loDeg <= 0)
1788 {
1789 QStyledItemDelegate::paint(painter, option, index);
1790 return;
1791 }
1792
1793 QStyleOptionProgressBar progressBarOption;
1794 progressBarOption.rect = option.rect;
1795 progressBarOption.minimum = loDeg;
1796 progressBarOption.maximum = hiDeg;
1797 progressBarOption.progress = jointValue;
1798 progressBarOption.text = QString::number(jointValue);
1799 progressBarOption.textVisible = true;
1800 QPalette pal;
1801 pal.setColor(QPalette::Background, Qt::red);
1802 progressBarOption.palette = pal;
1803 QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter);
1804 }
1805 else
1806 {
1807 QStyledItemDelegate::paint(painter, option, index);
1808 }
1809 }
1810
1812 {
1813 kinematicUnitInterfacePrx = nullptr;
1814
1815 if (updateTask)
1816 {
1817 updateTask->stop();
1818 updateTask->join();
1819 updateTask = nullptr;
1820 }
1821 }
1822
1823 void
1825 {
1826 bool ok;
1827 const auto text = QInputDialog::getMultiLineText(
1828 __widget, tr("JSON Joint values"), tr("Json:"), "{\n}", &ok)
1829 .toStdString();
1830
1831 if (!ok || text.empty())
1832 {
1833 return;
1834 }
1835
1836 NameValueMap jointAngles;
1837 try
1838 {
1839 jointAngles = simox::json::json2NameValueMap(text);
1840 }
1841 catch (...)
1842 {
1843 ARMARX_ERROR << "invalid json";
1844 }
1845
1846 NameControlModeMap jointModes;
1847 for (const auto& [key, _] : jointAngles)
1848 {
1849 jointModes[key] = ePositionControl;
1850 }
1851
1852 try
1853 {
1854 kinematicUnitInterfacePrx->switchControlMode(jointModes);
1855 kinematicUnitInterfacePrx->setJointAngles(jointAngles);
1856 }
1857 catch (...)
1858 {
1859 ARMARX_ERROR << "failed to switch mode or set angles";
1860 }
1861 }
1862
1863 void
1865 {
1866 const NameValueMap currentJointAngles = kinematicUnitInterfacePrx->getJointAngles();
1867 robot->setJointValues(currentJointAngles);
1868 }
1869
1870} // namespace armarx
#define lo(x)
#define hi(x)
uint8_t index
#define option(type, fn)
constexpr float SLIDER_POS_RAD_MULTIPLIER
#define KINEMATIC_UNIT_NAME_DEFAULT
constexpr float SLIDER_POS_DEG_MULTIPLIER
constexpr float SLIDER_POS_HEMI_MULTIPLIER
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define M_PI
Definition MathTools.h:17
#define VAROUT(x)
static const std::string & GetProjectName()
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
std::enable_if<!HasGetWidgetName< ArmarXWidgetType >::value >::type addWidget()
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
std::shared_ptr< RecursiveMutex > RecursiveMutexPtr
std::shared_ptr< std::recursive_mutex > mutex3D
void enableMainWidgetAsync(bool enable)
This function enables/disables the main widget asynchronously (if called from a non qt thread).
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
static DateTime Now()
Current time on the virtual clock.
Definition Clock.cpp:93
static TPtr create(Ice::PropertiesPtr properties=Ice::createProperties(), const std::string &configName="", const std::string &configDomain="ArmarX")
Factory method for a component.
Definition Component.h:116
static Frequency Hertz(std::int64_t hertz)
Definition Frequency.cpp:20
The JSONObject class is used to represent and (de)serialize JSON objects.
Definition JSONObject.h:44
void initializeUi(const DebugInfo &debugInfo)
void highlightCriticalValues(const NameStatusMap &reportedJointStatuses)
void onInitComponent() override
Pure virtual hook for the subclass.
void updateJointTorquesTable(const NameValueMap &reportedJointTorques)
armarx::RunningTask< KinematicUnitWidgetController >::pointer_type updateTask
void updateJointVelocitiesTable(const NameValueMap &reportedJointVelocities)
QPointer< QWidget > getCustomTitlebarWidget(QWidget *parent) override
getTitleToolbar returns a pointer to the a toolbar widget of this controller.
void onDisconnectComponent() override
Hook for subclass.
void loadSettings(QSettings *settings) override
Implement to load the settings that are part of the GUI configuration.
void updateModel(const NameValueMap &jointAngles)
void saveSettings(QSettings *settings) override
Implement to save the settings as part of the GUI configuration.
SoNode * getScene() override
Reimplementing this function and returning a SoNode* will show this SoNode in the 3DViewerWidget,...
void setMutex3D(RecursiveMutexPtr const &mutex3D) override
This mutex is used to protect 3d scene updates. Usually called by the ArmarXGui main window on creati...
armarx::DebugDrawerComponentPtr debugDrawer
void updateJointCurrentsTable(const NameValueMap &reportedJointCurrents, const NameStatusMap &reportedJointStatuses)
QString translateStatus(OperationStatus status)
void updateControlModesTable(const NameControlModeMap &reportedJointControlModes)
VirtualRobot::CoinVisualizationPtr kinematicUnitVisualization
QPointer< QDialog > getConfigDialog(QWidget *parent=0) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
void setControlModeRadioButtonGroup(const ControlMode &controlMode)
VirtualRobot::RobotNodeSetPtr robotNodeSet
void updateJointStatusesTable(const NameStatusMap &reportedJointStatuses)
void onConnectComponent() override
Pure virtual hook for the subclass.
void updateJointAnglesTable(const NameValueMap &reportedJointAngles)
void resetSlider()
Sets the Slider ui.horizontalSliderKinematicUnitPos to 0 if this->selectedControlMode is eVelocityCon...
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
void onExitComponent() override
Hook for subclass.
void onDebugInfoReceived(const DebugInfo &debugInfo)
KinematicUnitInterfacePrx kinematicUnitInterfacePrx
void debugInfoReceived(const DebugInfo &debugInfo)
void updateMotorTemperaturesTable(const NameValueMap &reportedMotorTemperatures)
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
ArmarXObjectSchedulerPtr getObjectScheduler() const
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
ArmarXManagerPtr getArmarXManager() const
Returns the ArmarX manager used to add and remove components.
Ice::PropertiesPtr getIceProperties() const
Returns the set of Ice properties.
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition Metronome.h:57
Duration waitForNextTick() const
Wait and block until the target period is met.
Definition Metronome.cpp:27
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#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
std::shared_ptr< class Robot > RobotPtr
Definition Bus.h:19
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< ArmarXManager > ArmarXManagerPtr
std::optional< float > mean(const boost::circular_buffer< NameValueMap > &buffer, const std::string &key)
std::vector< std::string > Split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
std::vector< T > abs(const std::vector< T > &v)
IceInternal::Handle< JSONObject > JSONObjectPtr
Definition JSONObject.h:34
const LogSender::manipulator flush
Definition LogSender.h:251