From 55c23329dcb37cad56c358d9bec0186d3bef21ee Mon Sep 17 00:00:00 2001 From: phesch <ulila@student.kit.edu> Date: Sat, 19 Mar 2022 10:37:11 +0100 Subject: [PATCH] Add extra utilities to aroncommon and armem/util --- .../RobotAPI/libraries/armem/CMakeLists.txt | 1 + source/RobotAPI/libraries/armem/util/util.cpp | 67 ++++++++- source/RobotAPI/libraries/armem/util/util.h | 34 +++++ .../libraries/aron/common/CMakeLists.txt | 2 + .../aron/common/util/object_finders.cpp | 60 ++++++++ .../aron/common/util/object_finders.h | 132 ++++++++++++++++++ .../data/visitor/variant/VariantVisitor.cpp | 25 ++++ .../data/visitor/variant/VariantVisitor.h | 6 + 8 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 source/RobotAPI/libraries/aron/common/util/object_finders.cpp create mode 100644 source/RobotAPI/libraries/aron/common/util/object_finders.h diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index 3e43604dd..6b105d8f6 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -9,6 +9,7 @@ set(LIBS ArmarXCore RemoteGui aron + aroncommon ) set(LIB_FILES diff --git a/source/RobotAPI/libraries/armem/util/util.cpp b/source/RobotAPI/libraries/armem/util/util.cpp index 34f0e2f56..fcaab64e0 100644 --- a/source/RobotAPI/libraries/armem/util/util.cpp +++ b/source/RobotAPI/libraries/armem/util/util.cpp @@ -1,3 +1,68 @@ #include "util.h" -// intentionally left blank \ No newline at end of file +#include <RobotAPI/libraries/armem/client.h> +#include <RobotAPI/libraries/armem/core/error/mns.h> + +namespace armarx::armem +{ + + std::optional<armarx::armem::wm::Memory> + resolveID(armarx::armem::client::MemoryNameSystem& mns, const armarx::armem::MemoryID& memoryID) + { + armarx::armem::client::Reader reader; + try + { + reader = mns.getReader(memoryID); + } + catch (const armarx::armem::error::CouldNotResolveMemoryServer& e) + { + ARMARX_WARNING << armarx::armem::error::CouldNotResolveMemoryServer::makeMsg(memoryID); + return {}; + } + + const armarx::armem::ClientQueryResult result = reader.queryMemoryIDs({memoryID}); + if (result.success) + { + return result.memory; + } + ARMARX_WARNING << "Could not query memory for ID " << memoryID << ", " + << result.errorMessage; + return {}; + } + + + std::optional<std::pair<armarx::aron::data::DictPtr, armarx::aron::type::ObjectPtr>> + extractDataAndType(const armarx::armem::wm::Memory& memory, + const armarx::armem::MemoryID& memoryID) + { + const auto* instance = memory.findLatestInstance(memoryID); + if (instance == nullptr) + { + return {}; + } + + armarx::aron::data::DictPtr aronData = instance->data(); + armarx::aron::type::ObjectPtr aronType; + const auto* providerSegment = memory.findProviderSegment(memoryID); + if (providerSegment == nullptr) + { + return {}; + } + + if (!providerSegment->hasAronType()) + { + const auto* coreSegment = memory.findCoreSegment(memoryID); + if (coreSegment == nullptr || !coreSegment->hasAronType()) + { + return {}; + } + aronType = coreSegment->aronType(); + } + else + { + aronType = providerSegment->aronType(); + } + + return {{aronData, aronType}}; + } +} // namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/util/util.h b/source/RobotAPI/libraries/armem/util/util.h index a337bfafb..7ffdfbfdc 100644 --- a/source/RobotAPI/libraries/armem/util/util.h +++ b/source/RobotAPI/libraries/armem/util/util.h @@ -26,6 +26,7 @@ #include <ArmarXCore/core/logging/Logging.h> +#include <RobotAPI/libraries/armem/client/MemoryNameSystem.h> #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/AronGeneratedClass.h> @@ -165,4 +166,37 @@ namespace armarx::armem return outV; } + /** + * @brief resolve a single MemoryID with the given MemoryNameSystem. + * + * Returns a Memory containing only the desired segment / entity if it was + * successfully queried. If the query was unsuccessful (the MNS could not + * resolve the memory server, the MemoryID does not exist, etc.), + * nothing is returned. + * + * @param mns the MemoryNameSystem to use for the query + * @param memoryID the MemoryID to query for + * @return memory containing the object referenced by the MemoryID if available + */ + std::optional<armarx::armem::wm::Memory> + resolveID(armarx::armem::client::MemoryNameSystem& mns, + const armarx::armem::MemoryID& memoryID); + + + /** + * @brief get the data and type of the given MemoryID in the given Memory. + * + * Returns the data and the type of the latest instance corresponding to + * the given MemoryID (whatever `memory.findLatestInstance(memoryID)` returns). + * If no such instance exists in the memory or no type for the instance is available, + * nothing is returned. + * + * @param memory the Memory to extract the data and type from + * @param memoryID the MemoryID of the instance to extract + * @return pair of data and type for the instance if available + */ + std::optional<std::pair<armarx::aron::data::DictPtr, armarx::aron::type::ObjectPtr>> + extractDataAndType(const armarx::armem::wm::Memory& memory, + const armarx::armem::MemoryID& memoryID); + } // namespace armarx::armem diff --git a/source/RobotAPI/libraries/aron/common/CMakeLists.txt b/source/RobotAPI/libraries/aron/common/CMakeLists.txt index ea66ba9db..3930f1917 100644 --- a/source/RobotAPI/libraries/aron/common/CMakeLists.txt +++ b/source/RobotAPI/libraries/aron/common/CMakeLists.txt @@ -18,6 +18,7 @@ armarx_add_library( aron_conversions/simox.h aron_conversions/stl.h aron_conversions/eigen.h + util/object_finders.h SOURCES aron_conversions/core.cpp @@ -25,6 +26,7 @@ armarx_add_library( aron_conversions/simox.cpp aron_conversions/stl.cpp aron_conversions/eigen.cpp + util/object_finders.cpp ) diff --git a/source/RobotAPI/libraries/aron/common/util/object_finders.cpp b/source/RobotAPI/libraries/aron/common/util/object_finders.cpp new file mode 100644 index 000000000..e6bba147d --- /dev/null +++ b/source/RobotAPI/libraries/aron/common/util/object_finders.cpp @@ -0,0 +1,60 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::libraries::aron + * @author Philip Scherer ( ulila@student.kit.edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "object_finders.h" + +namespace armarx::aron +{ + SubObjectFinder::SubObjectFinder(const std::string& typeNamePrefix) : + typeNamePrefix(typeNamePrefix) + { + } + + const std::map<std::string, std::pair<aron::data::VariantPtr, aron::type::VariantPtr>>& + SubObjectFinder::getFoundObjects() + { + return foundSubObjects; + } + + void + SubObjectFinder::visitObjectOnEnter(DataInput& elementData, TypeInput& elementType) + { + if (elementType && simox::alg::starts_with(elementType->getFullName(), typeNamePrefix)) + { + foundSubObjects.emplace(elementData->getPath().toString(), + std::make_pair(elementData, elementType)); + } + } + + void + SubObjectFinder::visitUnknown(DataInput& elementData, TypeInput& elementType) + { + // Ignore (the base class throws an exception here) + } + + SubObjectFinder::MapElements + SubObjectFinder::getObjectElements(DataInput& elementData, TypeInput& elementType) + { + return RecursiveConstTypedVariantVisitor::GetObjectElementsWithNullType(elementData, + elementType); + } +} // namespace armarx::aron diff --git a/source/RobotAPI/libraries/aron/common/util/object_finders.h b/source/RobotAPI/libraries/aron/common/util/object_finders.h new file mode 100644 index 000000000..b55423fa9 --- /dev/null +++ b/source/RobotAPI/libraries/aron/common/util/object_finders.h @@ -0,0 +1,132 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::libraries::aron + * @author Philip Scherer ( ulila@student.kit.edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <map> +#include <vector> + +#include <SimoxUtility/algorithm/string.h> + +#include <RobotAPI/libraries/aron/common/aron_conversions/core.h> +#include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> +#include <RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h> + +namespace armarx::aron +{ + /** + * Finds aron objects with a given type name prefix in aron variants. + * + * Construct it with the prefix you want to search for, + * then use `visitRecursive` to run the finder over the variant to search. + * Get a map of paths to data and type of the found objects using `getFoundObjects`. + */ + class SubObjectFinder : public armarx::aron::data::RecursiveConstTypedVariantVisitor + { + public: + SubObjectFinder(const std::string& typeNamePrefix); + + ~SubObjectFinder() override = default; + + /** + * Get the objects that have been found. + * + * The keys are strings instead of `aron::Path`s because those don't support comparisons. + * + * @return map of paths to pair of data and type variants + */ + const std::map<std::string, std::pair<aron::data::VariantPtr, aron::type::VariantPtr>>& + getFoundObjects(); + + void visitObjectOnEnter(DataInput& elementData, TypeInput& elementType) override; + + void visitUnknown(DataInput& elementData, TypeInput& elementType) override; + + MapElements getObjectElements(DataInput& elementData, TypeInput& elementType) override; + + private: + std::string typeNamePrefix; + std::map<std::string, std::pair<aron::data::VariantPtr, aron::type::VariantPtr>> + foundSubObjects; + }; + + /** + * Finds aron objects with a given type name prefix in aron variants and returns them as BOs. + * + * Construct it with the prefix you want to search for, + * then use `visitRecursive` to run the finder over the variant to search. + * Get a map of paths to your BOs using `getFoundObjects`. + * + * \tparam DTOType the _generated_ aron type of your data. + * \tparam BOType the type of your final BO. + */ + template <typename DTOType, typename BOType> + class BOSubObjectFinder : public armarx::aron::data::RecursiveConstTypedVariantVisitor + { + public: + BOSubObjectFinder() = default; + ~BOSubObjectFinder() override = default; + + /** + * Get the objects that have been found. + * + * The keys are strings instead of `aron::Path`s because those don't support comparisons. + * + * @return map of paths to BOs. + */ + const std::map<std::string, BOType>& + getFoundObjects() + { + return foundSubObjects; + } + + void + visitObjectOnEnter(DataInput& elementData, TypeInput& elementType) override + { + if (elementType->getFullName() == DTOType::ToAronType()->getFullName()) + { + auto dict = armarx::aron::data::Dict::DynamicCastAndCheck(elementData); + DTOType dto; + dto.fromAron(dict); + BOType boObj = armarx::aron::fromAron<BOType>(dto); + foundSubObjects.emplace(elementData->getPath().toString(), boObj); + } + } + + void + visitUnknown(DataInput& elementData, TypeInput& elementType) override + { + // Ignore (the base class throws an exception here) + } + + MapElements + getObjectElements(DataInput& elementData, TypeInput& elementType) override + { + return RecursiveConstTypedVariantVisitor::GetObjectElementsWithNullType(elementData, + elementType); + } + + private: + std::map<std::string, BOType> foundSubObjects; + }; + +} // namespace armarx::aron diff --git a/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.cpp b/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.cpp index a91f1ac11..0f6f90a19 100644 --- a/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.cpp +++ b/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.cpp @@ -536,6 +536,31 @@ namespace armarx::aron::data return ret; } + RecursiveConstTypedVariantVisitor::MapElements + RecursiveConstTypedVariantVisitor::GetObjectElementsWithNullType(DataInput& o, TypeInput& t) + { + std::map<std::string, std::pair<aron::data::VariantPtr, aron::type::VariantPtr>> ret; + auto data = aron::data::Dict::DynamicCastAndCheck(o); + auto type = aron::type::Object::DynamicCastAndCheck(t); + + if (data) + { + for (const auto& [key, e] : data->getElements()) + { + if (type && type->hasMemberType(key)) + { + auto memberType = type->getMemberType(key); + ret.insert({key, {e, memberType}}); + } + else + { + ret.insert({key, {e, nullptr}}); + } + } + } + return ret; + } + RecursiveConstTypedVariantVisitor::MapElements RecursiveConstTypedVariantVisitor::GetDictElements(DataInput& o, TypeInput& t) { diff --git a/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h b/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h index 8a8cc2a90..389db80a5 100644 --- a/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h +++ b/source/RobotAPI/libraries/aron/core/data/visitor/variant/VariantVisitor.h @@ -201,6 +201,12 @@ namespace armarx::aron::data { type::Descriptor getDescriptor(DataInput& o, TypeInput& n) override; static MapElements GetObjectElements(DataInput& o, TypeInput& t); + /* This override exists for visitors that need to handle untyped members in the hierarchy. + * Using it instead of `GetObjectElements` will allow your visitor to visit objects with + * a nullptr as their type. However, you will have to handle nullptr types in your + * visitor's methods. + */ + static MapElements GetObjectElementsWithNullType(DataInput& o, TypeInput& t); static MapElements GetDictElements(DataInput& o, TypeInput& t); static ListElements GetListElements(DataInput& o, TypeInput& t); static PairElements GetPairElements(DataInput& o, TypeInput& t); -- GitLab