ObjectPoseGuiWidgetController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * \package RobotAPI::gui-plugins::ObjectPoseGuiWidgetController
17 * \author Rainer Kartmann ( rainer dot kartmann at kit dot edu )
18 * \date 2020
19 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <RobotAPI/gui-plugins/ObjectPoseGui/ui_ObjectPoseGuiWidget.h>
26
27#include <string>
28
29#include <QMenu>
30#include <QTimer>
31
33
37
38namespace armarx
39{
40
41 static const int OBJECTS_COLUMN_ATTACHMENT = 6;
42
44 {
45 widget = std::make_unique<Ui::ObjectPoseGuiWidget>();
46 widget->setupUi(getWidget());
47
48 widget->objectsTree->clear();
49 widget->objectsTree->sortItems(0, Qt::SortOrder::AscendingOrder);
50 widget->objectsTree->setContextMenuPolicy(Qt::CustomContextMenu);
51
52 widget->requestTree->clear();
53 widget->requestTree->sortItems(0, Qt::SortOrder::AscendingOrder);
54
55
57
58 connect(widget->updateButton, &QPushButton::pressed, this, &This::updateTab);
59 connect(widget->tabWidget, &QTabWidget::currentChanged, this, &This::updateTab);
60 connect(widget->detchAllButton,
61 &QPushButton::pressed,
62 [this]() {
63 this->detachAllObjectsFromRobotNodes(
64 widget->detchAllCommitAttachedCheckBox->isChecked());
65 });
66
67 connect(widget->objectsTree,
68 &QTreeWidget::customContextMenuRequested,
69 this,
70 &This::prepareObjectContextMenu);
71
72 connect(widget->requestButton, &QPushButton::pressed, this, &This::requestSelectedObjects);
73
74 QTimer* timer = new QTimer(this);
75 timer->setInterval(500);
76 connect(timer, &QTimer::timeout, this, &This::updateTab);
77 connect(widget->autoUpdateCheckBox,
78 &QCheckBox::toggled,
79 this,
80 [timer](bool checked)
81 {
82 if (checked)
83 {
84 timer->start();
85 }
86 else
87 {
88 timer->stop();
89 }
90 });
91 }
92
96
97 void
99 {
100 (void)settings;
101 }
102
103 void
105 {
106 (void)settings;
107 }
108
109 QString
111 {
112 return "ArMem.ObjectPoseGui";
113 }
114
115 static const std::string CONFIG_KEY_OBJECT_POSE_STORAGE = "ObjectPoseStorage";
116
117 QPointer<QDialog>
119 {
120 if (!configDialog)
121 {
122 configDialog = new SimpleConfigDialog(parent);
123 configDialog->addProxyFinder<armarx::objpose::ObjectPoseStorageInterfacePrx>(
124 {CONFIG_KEY_OBJECT_POSE_STORAGE, "Object pose storage", "Object*"});
125 }
126 return qobject_cast<QDialog*>(configDialog);
127 }
128
129 void
131 {
132 if (configDialog)
133 {
134 objectPoseStorageName = configDialog->getProxyName(CONFIG_KEY_OBJECT_POSE_STORAGE);
135 if (objectPoseStorageName.empty())
136 {
137 objectPoseStorageName = "ObjectMemory";
138 }
139 }
140 }
141
142 void
144 {
145 if (!objectPoseStorageName.empty())
146 {
147 usingProxy(objectPoseStorageName);
148 }
149 }
150
151 void
153 {
154 if (!objectPoseStorageName.empty())
155 {
156 getProxy(ObjectPoseStorage, objectPoseStorageName);
157 }
158
159 this->attachableFrames = ObjectPoseStorage->getAttachableFrames();
160 std::sort(attachableFrames.begin(),
161 attachableFrames.end(),
162 [](const auto& lhs, const auto& rhs) { return lhs.agent < rhs.agent; });
163 for (objpose::AgentFrames& frames : attachableFrames)
164 {
165 std::sort(frames.frames.begin(), frames.frames.end());
166 }
167
168 // Update once.
169 updateTab();
170 }
171
172 void
174 {
175 ObjectPoseStorage = nullptr;
176 }
177
178 void
180 {
181 if (widget->tabWidget->currentWidget() == widget->tabObjects)
182 {
184 }
185 else if (widget->tabWidget->currentWidget() == widget->tabRequest)
186 {
188 }
189 }
190
191 void
193 {
194 if (!ObjectPoseStorage)
195 {
196 // Probably disconnected.
197 ARMARX_VERBOSE << "No object pose storage.";
198 return;
199 }
200
201 IceUtil::Time start = IceUtil::Time::now();
202 ARMARX_VERBOSE << "Getting object poses...";
203 const objpose::data::ObjectPoseSeq objectPosesIce = ObjectPoseStorage->getObjectPoses();
204 ARMARX_VERBOSE << "Got " << objectPosesIce.size() << " object poses. "
205 << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble()
206 << " ms.)";
207
208 const objpose::ObjectPoseSeq objectPoses = objpose::fromIce(objectPosesIce);
209
210 std::map<std::string, objpose::ObjectPoseSeq> objectPosesByProvider;
211 for (const auto& pose : objectPoses)
212 {
213 objectPosesByProvider[pose.providerName].push_back(pose);
214 }
215
216 start = IceUtil::Time::now();
217
218 QTreeWidget* tree = widget->objectsTree;
219
220 MapTreeWidgetBuilder builder(objectPosesByProvider);
221 builder.setMakeItemFn(
222 [](const std::string& provider, const objpose::ObjectPoseSeq&)
223 {
224 QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(provider)});
225 return item;
226 });
227 builder.setUpdateItemFn(
228 [](const std::string&, const objpose::ObjectPoseSeq& objectPoses, QTreeWidgetItem* item)
229 {
230 bool expand = item->childCount() == 0;
231
233 builder.setNameFn([](const objpose::ObjectPose& pose)
234 { return pose.objectID.str(); });
235 builder.setMakeItemFn(
236 [](const objpose::ObjectPose&)
237 {
238 QTreeWidgetItem* item = new QTreeWidgetItem(QStringList{});
239 return item;
240 });
241 builder.setUpdateItemFn(
242 [](const objpose::ObjectPose& pose, QTreeWidgetItem* item)
243 {
244 int col = 0;
245 item->setText(col++, QString::fromStdString(pose.objectID.str()));
246 item->setText(col++, QString::fromStdString(pose.providerName));
247 item->setText(col++,
248 QString::fromStdString(
249 objpose::ObjectTypeNames.to_name(pose.objectType)));
250
251 {
252 std::stringstream ss;
253 if (pose.localOOBB)
254 {
255 static const Eigen::IOFormat iof(5, 0, "", " x ", "", "", "", "");
256 ss << pose.localOOBB->dimensions().format(iof);
257 }
258 else
259 {
260 ss << "None";
261 }
262 item->setText(col++, QString::fromStdString(ss.str()));
263 }
264 item->setText(col++, QString::number(double(pose.confidence), 'g', 2));
265 item->setText(col++,
266 QString::fromStdString(pose.timestamp.toDateTimeString()));
267
268 {
269 std::stringstream ss;
270 if (pose.attachment)
271 {
272 ss << pose.attachment->frameName << " ("
273 << pose.attachment->agentName << ")";
274 }
275 item->setText(col++, QString::fromStdString(ss.str()));
276 }
277
278 return true;
279 });
280 builder.updateTreeWithContainer(item, objectPoses);
281
282 if (expand)
283 {
284 item->setExpanded(true);
285 }
286
287 return true;
288 });
289 builder.updateTree(tree, objectPosesByProvider);
290
291 ARMARX_VERBOSE << "Gui update took "
292 << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
293 }
294
295 void
297 {
298 if (!ObjectPoseStorage)
299 {
300 // Probably disconnected.
301 ARMARX_VERBOSE << "No object pose storage.";
302 return;
303 }
304
305 IceUtil::Time start = IceUtil::Time::now();
306 objpose::ProviderInfoMap availableProvidersInfo =
307 ObjectPoseStorage->getAvailableProvidersInfo();
308 ARMARX_VERBOSE << "Got infos of " << availableProvidersInfo.size()
309 << " object pose providers. "
310 << "(Took " << (IceUtil::Time::now() - start).toMilliSecondsDouble()
311 << " ms.)";
312
313
314 // Restructure data.
315 std::map<std::string, std::set<std::pair<std::string, std::string>>> data;
316 for (const auto& [providerName, info] : availableProvidersInfo)
317 {
318 for (const auto& id : info.supportedObjects)
319 {
320 data[id.dataset].insert(std::make_pair(id.className, providerName));
321 }
322 }
323
324 start = IceUtil::Time::now();
325
326 QTreeWidget* tree = widget->requestTree;
327
328 MapTreeWidgetBuilder builder(data);
329 builder.setMakeItemFn(
330 [](const std::string& dataset, const auto&)
331 {
332 QTreeWidgetItem* item = new QTreeWidgetItem({QString::fromStdString(dataset)});
333 return item;
334 });
335 builder.setUpdateItemFn(
336 [tree](
337 const std::string& dataset, const auto& datasetData, QTreeWidgetItem* datasetItem)
338 {
339 (void)dataset;
340
342 builder.setCompareFn(
343 [](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem* item)
344 {
345 auto rhs = std::make_pair(item->text(0).toStdString(),
346 item->text(1).toStdString());
347 if (lhs < rhs)
348 {
349 return -1;
350 }
351 return lhs == rhs ? 0 : 1;
352 });
353 builder.setMakeItemFn(
354 [](const std::pair<std::string, std::string>& element)
355 {
356 QTreeWidgetItem* item =
357 new QTreeWidgetItem({QString::fromStdString(element.first),
358 QString::fromStdString(element.second)});
359 return item;
360 });
361 builder.setUpdateItemFn(
362 [tree](const std::pair<std::string, std::string>& element,
363 QTreeWidgetItem* item)
364 {
365 (void)element;
366 if (!tree->itemWidget(item, 2))
367 {
368 QCheckBox* requestCheckBox = new QCheckBox();
369 tree->setItemWidget(item, 2, requestCheckBox);
370 }
371 return true;
372 });
373 builder.updateTreeWithContainer(datasetItem, datasetData);
374
375 return true;
376 });
377 builder.updateTree(tree, data);
378
379 ARMARX_VERBOSE << "Gui update took "
380 << (IceUtil::Time::now() - start).toMilliSecondsDouble() << " ms.";
381 }
382
383 void
385 {
386 QTreeWidget* tree = widget->objectsTree;
387 QTreeWidgetItem* item = tree->itemAt(pos);
388
389 if (item == nullptr || item->parent() == nullptr)
390 {
391 // No item or top level item => no context menu.
392 return;
393 }
394
395 QString providerName = item->parent()->text(0);
396 QString objectID = item->text(0);
397
398 QMenu* attachMenu = new QMenu("Attach to robot node", tree);
399 for (const objpose::AgentFrames& agentFrames : attachableFrames)
400 {
401 QMenu* agentMenu = new QMenu(QString::fromStdString(agentFrames.agent), tree);
402
403 for (const std::string& frame : agentFrames.frames)
404 {
405 QAction* attachAgentAction = new QAction(QString::fromStdString(frame), tree);
406 // attachAgentAction->setStatusTip(tr("Attach object rigidly to a robot node"));
407 connect(attachAgentAction,
408 &QAction::triggered,
409 [=, this]() {
411 providerName, objectID, agentFrames.agent, frame);
412 });
413 agentMenu->addAction(attachAgentAction);
414 }
415 attachMenu->addMenu(agentMenu);
416 }
417
418 QAction* detachAction = new QAction(tr("Detach from to robot node"), tree);
419 detachAction->setEnabled(!item->text(OBJECTS_COLUMN_ATTACHMENT).isEmpty());
420 connect(detachAction,
421 &QAction::triggered,
422 [=, this]() { this->detachObjectFromRobotNode(providerName, objectID); });
423
424 QMenu menu(tree);
425 menu.addMenu(attachMenu);
426 menu.addAction(detachAction);
427
428 menu.exec(tree->mapToGlobal(pos));
429 }
430
431 void
433 QString objectID,
434 const std::string& agentName,
435 const std::string& frameName)
436 {
437 ARMARX_VERBOSE << "Attaching " << objectID << " by '" << providerName << "' to robot node '"
438 << frameName << "' of agent '" << agentName << "'.";
439
440 objpose::AttachObjectToRobotNodeInput input;
441 input.providerName = providerName.toStdString();
442 input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString()));
443 input.agentName = agentName;
444 input.frameName = frameName;
445
446 try
447 {
448 objpose::AttachObjectToRobotNodeOutput output =
449 ObjectPoseStorage->attachObjectToRobotNode(input);
450 ARMARX_VERBOSE << "Success of attaching: " << output.success;
451 }
452 catch (const IceUtil::Exception& e)
453 {
454 ARMARX_WARNING << "Failed to attach object '" << input.objectID << "' to robot node '"
455 << input.frameName << "' of agent '" << input.agentName << "'."
456 << "\nReason: " << e.what();
457 }
458 }
459
460 void
461 ObjectPoseGuiWidgetController::detachObjectFromRobotNode(QString providerName, QString objectID)
462 {
463 ARMARX_VERBOSE << "Detaching " << objectID << " by '" << providerName
464 << "' from robot node.";
465
466 objpose::DetachObjectFromRobotNodeInput input;
467 input.providerName = providerName.toStdString();
468 input.objectID = armarx::toIce(armarx::ObjectID(objectID.toStdString()));
469
470 try
471 {
472 objpose::DetachObjectFromRobotNodeOutput output =
473 ObjectPoseStorage->detachObjectFromRobotNode(input);
474 ARMARX_VERBOSE << "Was attached: " << output.wasAttached;
475 }
476 catch (const IceUtil::Exception& e)
477 {
478 ARMARX_WARNING << "Failed to detach object '" << input.objectID
479 << "' from a robot node."
480 << "\nReason: " << e.what();
481 }
482 }
483
484 void
486 {
487
488 objpose::DetachAllObjectsFromRobotNodesInput input;
489 input.commitAttachedPose = commitAttachedPose;
490
491 try
492 {
493 objpose::DetachAllObjectsFromRobotNodesOutput output =
494 ObjectPoseStorage->detachAllObjectsFromRobotNodes(input);
495 ARMARX_VERBOSE << "Detached " << output.numDetached << " objects from robot nodes.";
496 }
497 catch (const IceUtil::Exception& e)
498 {
499 ARMARX_WARNING << "Failed to detach all objects from robot nodes.";
500 }
501 }
502
503 void
505 {
506 std::map<std::string, objpose::observer::RequestObjectsInput> requestsPerProvider;
507
508 QTreeWidget* tree = widget->requestTree;
509 for (int i = 0; i < tree->topLevelItemCount(); ++i)
510 {
511 QTreeWidgetItem* datasetItem = tree->topLevelItem(i);
512 for (int j = 0; j < datasetItem->childCount(); ++j)
513 {
514 QTreeWidgetItem* classItem = datasetItem->child(j);
515 QCheckBox* selected = dynamic_cast<QCheckBox*>(tree->itemWidget(classItem, 2));
516 ARMARX_CHECK_NOT_NULL(selected);
517 if (selected->isChecked())
518 {
519 std::string providerName = classItem->text(1).toStdString();
520 objpose::observer::RequestObjectsInput& requests =
521 requestsPerProvider[providerName];
522 data::ObjectID& id = requests.request.objectIDs.emplace_back();
523 id.dataset = datasetItem->text(0).toStdString();
524 id.className = classItem->text(0).toStdString();
525 }
526 }
527 }
528
529 long timeoutMS = -1;
530 if (!widget->requestInfiniteCheckBox->isChecked())
531 {
532 timeoutMS = long(widget->requestTimeoutSpinBox->value() * 1000);
533 }
534
535 for (auto& [providerName, request] : requestsPerProvider)
536 {
537 request.provider = providerName;
538 request.request.relativeTimeoutMS = timeoutMS;
539
540 ARMARX_INFO << "Requesting " << request.request.objectIDs.size() << " objects for "
541 << request.request.relativeTimeoutMS << " ms.";
542 objpose::observer::RequestObjectsOutput output =
543 ObjectPoseStorage->requestObjects(request);
544 int successful = 0;
545 for (const auto& [id, result] : output.results)
546 {
547 successful += int(!result.providerName.empty() && result.result.success);
548 }
549 ARMARX_INFO << successful << " of " << request.request.objectIDs.size()
550 << " object request successful.";
551 }
552 }
553
554} // namespace armarx
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
A known object ID of the form "Dataset/ClassName" or "Dataset/ClassName/InstanceName".
Definition ObjectID.h:11
std::string str() const
Return "dataset/className" or "dataset/className/instanceName".
Definition ObjectID.cpp:60
void updateTab()
Update the currently opened tab.
void loadSettings(QSettings *settings) override
static QString GetWidgetName()
Returns the Widget name displayed in the ArmarXGui to create an instance of this class.
void detachObjectFromRobotNode(QString providerName, QString objectID)
void detachAllObjectsFromRobotNodes(bool commitAttachedPose)
void attachObjectToRobotNode(QString providerName, QString objectID, const std::string &agentName, const std::string &frameName)
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
virtual ~ObjectPoseGuiWidgetController() override
Controller destructor.
QPointer< QDialog > getConfigDialog(QWidget *parent) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
A config-dialog containing one (or multiple) proxy finders.
std::string toDateTimeString() const
Definition DateTime.cpp:75
#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:179
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:185
std::vector< ObjectPose > ObjectPoseSeq
void fromIce(const Box &box, simox::OrientedBox< float > &oobb)
const simox::meta::EnumNames< objpose::ObjectType > ObjectTypeNames
This file offers overloads of toIce() and fromIce() functions for STL container types.
void toIce(std::map< IceKeyT, IceValueT > &iceMap, const boost::container::flat_map< CppKeyT, CppValueT > &cppMap)
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a m...
void setUpdateItemFn(UpdateItemFn updateItemFn)
void updateTree(ParentT *tree, const MapT &elements)
void setMakeItemFn(MakeItemFn makeItemFn)
A class to efficiently build and maintain sorted items of QTreeWidget or QTreeWidgetItem based on a s...
void setNameFn(NameFn nameFn, int nameColumn=0)
void setUpdateItemFn(UpdateItemFn updateItemFn)
void setMakeItemFn(MakeItemFn makeItemFn)
void updateTreeWithContainer(ParentT *parent, const ContainerT &elements)
Update the tree with the iterable container.
void setCompareFn(CompareFn compareFn)
An object pose as stored by the ObjectPoseStorage.
Definition ObjectPose.h:34
float confidence
Confidence in [0, 1] (1 = full, 0 = none).
Definition ObjectPose.h:96
armarx::ObjectID objectID
The object ID, i.e. dataset, class name and instance name.
Definition ObjectPose.h:56
std::optional< ObjectAttachmentInfo > attachment
Attachment information.
Definition ObjectPose.h:93
std::string providerName
Name of the providing component.
Definition ObjectPose.h:59
DateTime timestamp
Source timestamp.
Definition ObjectPose.h:98
ObjectType objectType
Known or unknown object.
Definition ObjectPose.h:61
std::optional< simox::OrientedBoxf > localOOBB
Object bounding box in object's local coordinate frame.
Definition ObjectPose.h:102