33 #include <QHBoxLayout>
37 #include <QMessageBox>
38 #include <QMouseEvent>
40 #include <QPushButton>
41 #include <QSignalBlocker>
42 #include <qboxlayout.h>
43 #include <qhashfunctions.h>
46 #include <qmessagebox.h>
48 #include <qobjectdefs.h>
51 #include <qsettings.h>
62 #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h>
72 #include <RobotAPI/libraries/armem_locations/aron/Location.aron.generated.h>
77 #include <armarx/navigation/components/navigation_memory/ComponentInterface.h>
80 #include <armarx/navigation/core/aron/Graph.aron.generated.h>
86 #include <armarx/navigation/gui-plugins/LocationGraphEditor/ui_LocationGraphEditorWidget.h>
96 #include <SemanticObjectRelations/Shapes/Shape.h>
99 static const QString SETTING_LAST_SCENE =
"lastScene";
107 return "Navigation.LocationGraphEditor";
113 return QIcon{
"://icons/location_graph_editor.svg"};
118 widget.setupUi(getWidget());
120 loadAutomaticSettings();
122 widget.loadGraphButton->setEnabled(
false);
125 view.vertexData->setEnabled(
false);
126 widget.locationDataGroupBox->layout()->addWidget(view.vertexData);
127 if (QBoxLayout* layout =
dynamic_cast<QBoxLayout*
>(widget.locationDataGroupBox->layout()))
129 layout->addStretch();
132 view.vertexTable =
new VertexTableWidget();
133 widget.locationsTableGroupBox->layout()->addWidget(view.vertexTable);
135 view.edgeTable =
new EdgeTableWidget();
136 widget.edgesTableGroupBox->layout()->addWidget(view.edgeTable);
138 view.robotVisu =
new RobotVisuWidget(*
this);
139 widget.robotVisuGroupBox->layout()->addWidget(view.robotVisu);
141 view.objectPoses =
new ObjectPoseClientWidget(*
this);
142 widget.objectPoseClientGroupBox->layout()->addWidget(view.objectPoses);
145 connect(
this, &This::connected,
this, &This::queryGraphs);
146 connect(
this, &This::connected,
this, &This::graphChanged);
148 connect(widget.createGraphButton, &QPushButton::pressed,
this, &This::createGraphDialog);
150 connect(widget.queryGraphsButton, &QPushButton::pressed,
this, &This::queryGraphs);
152 connect(
this, &This::locationMemoryChanged,
this, &This::memoryChanged);
153 connect(
this, &This::graphMemoryChanged,
this, &This::memoryChanged);
155 connect(
this, &This::graphMemoryChanged,
this, &This::updateGraphList);
157 connect(widget.loadGraphButton, &QPushButton::pressed,
this, &This::loadGraph);
158 connect(widget.commitGraphButton, &QPushButton::pressed,
this, &This::commit);
161 connect(
this, &This::graphChanged,
this, &This::updateGraphView);
165 connect(view.robotVisu,
168 [
this]() { view.vertexData->setRobotConnection(&view.robotVisu->connection()); });
171 connect(view.objectPoses,
175 { view.vertexData->setObjectPoseConnection(&view.objectPoses->connection()); });
178 connect(view.vertexTable,
179 &VertexTableWidget::currentItemChanged,
180 [
this](QTableWidgetItem* current, QTableWidgetItem* previous)
183 this->selectVertex(current);
186 connect(view.vertexTable,
187 &VertexTableWidget::itemSelectionChanged,
189 &This::updateVertexHighlighting);
190 connect(view.edgeTable,
191 &EdgeTableWidget::itemSelectionChanged,
193 &This::updateEdgeHighlighting);
196 connect(view.vertexTable,
199 &This::createVertexDialog);
201 connect(view.vertexTable,
204 &This::removeEdgesOfVertex);
209 connect(widget.exportButton, &QPushButton::clicked,
this, &This::exportLocationGraph);
213 WidgetController::exportLocationGraph()
217 auto navigationMemoryPrx =
218 memory::NavigationMemoryInterfacePrx::uncheckedCast(remote.locationReader.readingPrx);
219 if (navigationMemoryPrx)
221 const ::armarx::PackagePath packagePath(
222 widget.packageNameEdit->text().toStdString(),
223 widget.packageDirectoryEdit->text().toStdString());
225 navigationMemoryPrx->storeLocationGraph(packagePath.serialize());
226 labelText =
"Exported locations and graph.";
230 labelText =
"Export failed: The connected navigation memory does not implement the "
231 "NavigationMemoryInterface.";
234 widget.exportLabel->setText(labelText);
245 if (not configDialog)
248 configDialog->addProxyFinder<armem::mns::MemoryNameSystemInterfacePrx>(
249 "MemoryNameSystem",
"Memory Name System", remote.memoryNameSystemName);
251 return qobject_cast<SimpleConfigDialog*>(configDialog);
257 remote.memoryNameSystemName = configDialog->getProxyName(
"MemoryNameSystem");
263 remote.memoryNameSystemName =
265 ->value(
"memoryNameSystemName", QString::fromStdString(remote.memoryNameSystemName))
273 settings->setValue(
"memoryNameSystemName",
274 QString::fromStdString(remote.memoryNameSystemName));
280 lastSelectedSceneName =
281 settings.value(SETTING_LAST_SCENE, lastSelectedSceneName).toString();
287 settings.setValue(SETTING_LAST_SCENE, lastSelectedSceneName);
299 remote.connect(*
this);
301 std::stringstream ss;
302 ss <<
"Navigation Graphs (Entities from Core Segment "
304 widget.graphGroupBox->setTitle(QString::fromStdString(ss.str()));
305 widget.graphGroupBox->setEnabled(
true);
312 WidgetController::Remote::connect(
Component& parent)
315 parent.
getProxy<armem::mns::MemoryNameSystemInterfacePrx>(memoryNameSystemName);
328 WidgetController::queryMemory()
336 std::stringstream ss;
345 widget.statusLabel->setText(
status.join(
"\n"));
349 armem::client::QueryResult
350 WidgetController::queryLocations()
352 armem::client::QueryResult result =
356 model.locationsMemory = std::move(result.memory);
362 armem::client::QueryResult
363 WidgetController::queryGraphs()
365 armem::client::QueryResult result =
369 model.graphMemory = std::move(result.memory);
376 WidgetController::updateGraphList()
378 QString previousText = widget.graphsComboBox->currentText();
379 widget.graphsComboBox->clear();
382 int previousIndex = -1;
383 model.graphMemory.forEachEntity(
384 [&](
const armem::wm::Entity& entity)
387 (entity.id() == model.graphEntityID ? model.graph.hasChanged() :
false);
388 QString text = getGraphDisplayName(entity.id(), hasChanged);
389 QString
id = QString::fromStdString(entity.id().str());
391 widget.graphsComboBox->addItem(text,
id);
392 if (previousIndex < 0 and text == previousText)
398 if (previousIndex >= 0)
400 widget.graphsComboBox->setCurrentIndex(previousIndex);
402 widget.loadGraphButton->setEnabled(widget.graphsComboBox->count() > 0);
406 WidgetController::loadGraph()
408 if (model.graph.hasChanged())
410 if (not loadGraphDialog())
417 widget.graphsComboBox->currentData().toString().toStdString());
425 widget.statusLabel->setText(QString::fromStdString(
426 "Loaded snapshot " + instance->id().getEntitySnapshotID().str()));
430 std::stringstream ss;
431 ss <<
"No latest instance of entity " << entityID <<
" in memory.";
432 widget.statusLabel->setText(QString::fromStdString(ss.str()));
440 dto.fromAron(instance->data());
446 std::vector<semrel::ShapeID> remove;
447 for (navigation::core::Graph::Vertex vertex : nav.vertices())
449 if (not vertex.attrib().hasPose())
451 remove.push_back(vertex.objectID());
454 if (not remove.empty())
456 for (semrel::ShapeID vertexID : remove)
458 nav.removeVertex(nav.vertex(vertexID));
460 std::stringstream ss;
461 ss <<
"Dropped " << remove.size() <<
" locations which could not be resolved.";
462 widget.statusLabel->setText(QString::fromStdString(ss.str()));
467 setGraph(entityID, nav);
471 WidgetController::loadGraphDialog()
476 msgBox.setText(
"The current graph and/or locations have uncommitted changes. ");
477 msgBox.setInformativeText(
"Do you want to discard them?");
478 QStringList detailLines;
479 if (model.graph.attrib().edgesChanged)
481 detailLines.append(
"Graph edges have changed.");
483 for (
auto vertex : model.graph.vertices())
485 if (vertex.attrib().changed)
487 detailLines.append(
"Location " + QString::fromStdString(vertex.attrib().getName()) +
491 msgBox.setDetailedText(detailLines.join(
"\n"));
492 msgBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
493 msgBox.setDefaultButton(QMessageBox::Cancel);
495 switch (msgBox.exec())
497 case QMessageBox::Discard:
499 case QMessageBox::Cancel:
507 WidgetController::commit()
513 std::stringstream ss;
516 ss <<
"Committed " << locResults.
results.size() <<
" location snapshot";
517 if (locResults.
results.size() != 1)
523 ss <<
" and 1 graph snapshot " << graphResult.
snapshotID;
527 ss <<
" but failed to commit graph: \n" << graphResult.
errorMessage;
532 int numLocs =
static_cast<int>(locResults.
results.size());
534 for (
const auto& r : locResults.
results)
536 numSuccess += int(r.success);
538 int numFailed = numLocs - numSuccess;
542 ss <<
"Committed 1 graph snapshot " << graphResult.
snapshotID;
543 ss <<
" and " << numSuccess <<
" locations, but failed to commit " << numFailed
549 ss <<
"Failed to commit graph and " << numFailed <<
" of " << numLocs
556 widget.statusLabel->setText(QString::fromStdString(ss.str()));
564 WidgetController::commitLocations()
568 for (
auto vertex : model.graph.vertices())
570 if (vertex.attrib().changed)
576 navigation::location::arondto::Location dto;
577 toAron(dto.framedPose, vertex.attrib().getPose());
578 update.instancesData = {dto.toAron()};
583 auto it = result.
results.begin();
584 for (
auto vertex : model.graph.vertices())
586 if (vertex.attrib().changed)
592 vertex.attrib().changed =
false;
601 WidgetController::commitGraph()
610 update.entityID = model.graphEntityID;
612 update.instancesData = {dto.toAron()};
618 model.graph.attrib().edgesChanged =
false;
623 template <
class GraphT>
627 while (graph.hasVertex(vertexID))
637 model.graphEntityID = entityID;
641 QSignalBlocker blocker(
this);
644 model.graph.attrib().edgesChanged =
false;
646 std::set<armem::MemoryID> coveredLocationIDs;
647 for (
auto vertex : nav.vertices())
650 addVertex(vertex.objectID(), {vertex.attrib()});
653 aron::fromAron<armem::MemoryID>(vertex.attrib().aron.locationID).
getEntityID();
654 if (coveredLocationIDs.count(
id) > 0)
660 coveredLocationIDs.insert(
id);
666 semrel::ShapeID vertexID{0};
667 model.locationsMemory.forEachInstance(
670 if (coveredLocationIDs.count(instance.id().getEntityID()) == 0)
672 vertexID = findNextFreeVertexID(model.graph, vertexID);
673 addVertex(vertexID, instance);
679 for (
auto edge : nav.edges())
681 addEdge(model.graph.vertex(edge.sourceObjectID()),
682 model.graph.vertex(edge.targetObjectID()),
694 model.graphEntityID = entityID;
697 QSignalBlocker blocker(
this);
700 semrel::ShapeID
id{0};
702 model.locationsMemory.forEachInstance(
705 addVertex(
id, instance);
711 model.graph.attrib().edgesChanged =
true;
717 WidgetController::addVertex(semrel::ShapeID vertexID,
const VertexData& defaultAttribs)
721 GuiGraph::Vertex vertex = model.graph.addVertex(vertexID, defaultAttribs);
722 vertex.attrib().tableWidgetItem = view.vertexTable->addVertex();
730 WidgetController::addVertex(semrel::ShapeID vertexID,
733 navigation::location::arondto::Location location;
734 location.fromAron(locationInstance.data());
737 toAron(attrib.aron.locationID, locationInstance.id().getEntityID());
739 fromAron(location.framedPose, pose);
740 attrib.setPose(pose);
741 return addVertex(vertexID, attrib);
745 WidgetController::addEdge(GuiGraph::ConstVertex
source,
746 GuiGraph::ConstVertex
target,
747 const EdgeData& defaultAttribs)
750 <<
"Edge must not exist before being added: '" <<
source.attrib().getName() <<
"' -> '"
751 <<
target.attrib().getName() <<
"'";
753 GuiGraph::Edge edge = model.graph.addEdge(
source,
target, defaultAttribs);
754 edge.attrib().tableWidgetItem = view.edgeTable->addEdge(edge);
762 WidgetController::updateGraphView()
764 for (
auto vertex : model.graph.vertices())
766 updateVertexView(vertex);
768 for (
auto edge : model.graph.edges())
770 updateEdgeView(edge);
774 widget.graphsComboBox->findData(QString::fromStdString(model.graphEntityID.str()));
777 widget.graphsComboBox->setItemText(
778 index, getGraphDisplayName(model.graphEntityID, model.graph.hasChanged()));
785 WidgetController::updateVertexView(GuiGraph::Vertex vertex)
787 view.vertexTable->updateVertex(vertex);
792 std::set<semrel::ShapeID> highlightedVertices;
793 for (
auto v : model.graph.vertices())
795 if (
v.attrib().highlighted)
797 highlightedVertices.insert(
v.objectID());
801 for (
auto edge : model.graph.edges())
803 bool verticesHighlighted = highlightedVertices.count(edge.sourceObjectID()) and
804 highlightedVertices.count(edge.targetObjectID());
808 if (edge.attrib().highlighted != verticesHighlighted)
810 edge.attrib().highlighted = verticesHighlighted;
817 WidgetController::updateEdgeView(GuiGraph::Edge edge)
819 view.edgeTable->updateEdge(edge);
823 WidgetController::updateArViz()
828 std::vector<ObjectInfo> objectInfo;
829 graph::visu::ObjectParserInfo info{objectMap, objectInfo};
832 if (view.objectPoses->isConnected())
834 objectMap = view.objectPoses->connection().getObjectPoseMap();
835 objectInfo = view.objectPoses->connection().getObjectInfo();
838 std::vector<viz::Layer> layers;
840 viz::Layer& vertices = layers.emplace_back(remote.arviz->layer(
"Locations"));
841 applyVisu(vertices, model.visu.vertex, model.graph.vertices(), info);
844 viz::Layer& edges = layers.emplace_back(remote.arviz->layer(
"Graph Edges"));
845 applyVisu(edges, model.visu.edge, model.graph.edges(), info);
848 viz::Layer& robot = layers.emplace_back(remote.arviz->layer(
"Robot"));
849 if (view.vertexData->vertex().has_value() and view.robotVisu->isEnabled())
852 objectMap, objectInfo, view.vertexData->vertex()->attrib().getPose());
853 if (res.pose.has_value())
856 view.robotVisu->connection().vizRobot(
"robot").pose(res.pose.value()));
860 remote.arviz->commit(layers);
866 updateElementHighlighting(QList<QTableWidgetItem*> selectedItems,
867 std::map<QTableWidgetItem*, T>&& itemToElementMap)
869 for (QTableWidgetItem* selected : selectedItems)
871 if (
auto it = itemToElementMap.find(selected); it != itemToElementMap.end())
873 it->second.attrib().highlighted =
true;
874 itemToElementMap.erase(it);
877 for (
auto& [_, unselected] : itemToElementMap)
879 unselected.attrib().highlighted =
false;
884 WidgetController::updateVertexHighlighting()
886 updateElementHighlighting(view.vertexTable->selectedVertexItems(),
887 model.graph.getTableItemToVertexMap());
892 WidgetController::updateEdgeHighlighting()
894 updateElementHighlighting(view.edgeTable->selectedEdgeItems(),
895 model.graph.getTableItemToEdgeMap());
900 WidgetController::selectVertex(QTableWidgetItem* vertexItem)
902 if (vertexItem ==
nullptr)
904 view.vertexData->clearVertex();
906 if (
auto vertex = model.graph.getVertexFromTableItem(vertexItem))
908 selectVertex(vertex.value());
913 WidgetController::selectVertex(GuiGraph::Vertex vertex)
915 view.vertexData->setVertex(vertex);
919 WidgetController::clearGraph()
922 QSignalBlocker blocker(
this);
935 WidgetController::clearEdges()
938 std::vector<GuiGraph::Edge> edges{model.graph.edges().begin(), model.graph.edges().end()};
940 QSignalBlocker blocker(
this);
941 for (
auto it = edges.rbegin(); it != edges.rend(); ++it)
943 GuiGraph::Edge edge = *it;
955 WidgetController::clearVertices()
958 <<
"The graph may not have any edges when clearing the vertices.";
961 std::vector<GuiGraph::Vertex> vertices{model.graph.vertices().begin(),
962 model.graph.vertices().end()};
964 QSignalBlocker blocker(
this);
965 for (
auto it = vertices.rbegin(); it != vertices.rend(); ++it)
967 GuiGraph::Vertex vertex = *it;
968 removeVertex(vertex);
979 WidgetController::removeEdge(GuiGraph::Edge& edge)
981 ARMARX_CHECK(model.graph.hasEdge(edge.sourceDescriptor(), edge.targetDescriptor()))
982 <<
"Cannot remove edge that does not exist.";
986 QSignalBlocker blocker(view.edgeTable);
987 view.edgeTable->removeEdge(edge);
991 model.graph.removeEdge(edge);
993 ARMARX_CHECK(not model.graph.hasEdge(edge.sourceDescriptor(), edge.targetDescriptor()))
994 << edge.sourceDescriptor() <<
" -> " << edge.targetDescriptor();
1000 WidgetController::removeVertex(GuiGraph::Vertex& vertex)
1003 <<
"A vertex must not have any edges before being removed. "
1004 << vertex.attrib().getName();
1006 <<
"A vertex must not have any edges before being removed. "
1007 << vertex.attrib().getName();
1011 QSignalBlocker blocker(view.vertexTable);
1012 view.vertexTable->removeVertex(vertex);
1014 if (view.vertexData->vertex().has_value() and view.vertexData->vertex().value() == vertex)
1016 view.vertexData->clearVertex();
1020 model.graph.removeVertex(vertex);
1026 WidgetController::createVertexDialog()
1029 switch (dialog.exec())
1031 case QDialog::Accepted:
1034 case QDialog::Rejected:
1044 toAron(attribs.aron.locationID, dialog.entityID());
1046 attribs.changed =
true;
1048 addVertex(vertexID, attribs);
1052 WidgetController::addEdges(QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems)
1054 const auto itemToVertexMap = model.graph.getTableItemToVertexMap();
1056 QSignalBlocker blocker(
this);
1058 for (
const auto& [sourceItem, targetItem] : vertexItems)
1060 GuiGraph::Vertex
source = itemToVertexMap.at(sourceItem);
1061 GuiGraph::Vertex
target = itemToVertexMap.at(targetItem);
1069 model.graph.attrib().edgesChanged =
true;
1074 WidgetController::removeEdges(QList<QTableWidgetItem*> edgeItems)
1076 const auto itemToEdgeMap = model.graph.getTableItemToEdgeMap();
1078 QSignalBlocker blocker(
this);
1080 for (
const auto& edgeItem : edgeItems)
1082 GuiGraph::Edge edge = itemToEdgeMap.at(edgeItem);
1087 model.graph.attrib().edgesChanged =
true;
1092 WidgetController::removeEdgesOfVertex(QList<QTableWidgetItem*> vertexItems,
1095 auto removeEdges = [
this](
auto&& range)
1097 for (
auto edge : range)
1103 QSignalBlocker blocker(
this);
1105 const auto itemToVertexMap = model.graph.getTableItemToVertexMap();
1106 for (
const auto& vertexItem : vertexItems)
1108 GuiGraph::Vertex vertex = itemToVertexMap.at(vertexItem);
1112 removeEdges(vertex.inEdges());
1116 removeEdges(vertex.outEdges());
1120 removeEdges(vertex.inEdges());
1121 removeEdges(vertex.outEdges());
1127 model.graph.attrib().edgesChanged =
true;
1132 WidgetController::createGraphDialog()
1135 switch (dialog.exec())
1137 case QDialog::Accepted:
1139 case QDialog::Rejected:
1145 const bool hasChanged =
true;
1146 QString text = getGraphDisplayName(entityID, hasChanged);
1147 QString
data = QString::fromStdString(entityID.
str());
1148 widget.graphsComboBox->addItem(text,
data);
1149 widget.graphsComboBox->setCurrentIndex(widget.graphsComboBox->count() - 1);
1151 setEmptyGraph(entityID);
1155 WidgetController::getGraphDisplayName(
const armem::MemoryID& entityID,
bool changed)
const