diff --git a/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp b/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp index 8412e2aeb339d16fd256b81ea85470c060a91026..879809064b9cdb1429b49925e54ba5765cb534cf 100644 --- a/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp +++ b/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp @@ -60,8 +60,7 @@ namespace armarx void ExampleMemory::onInitComponent() { - workingMemory.name() = p.memoryName; - longtermMemory.name() = p.memoryName; + this->setMemoryName(p.memoryName); // Usually, the memory server will specify a number of core segments with a specific aron type. workingMemory.addCoreSegment("ExampleData", armem::example::ExampleData::toAronType()); diff --git a/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp b/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp index 350810558ce9d2200e61bda865dd1633da15c128..15253dffb3eb8c7e49c114d2a7659627cd797071 100644 --- a/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp +++ b/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp @@ -55,8 +55,7 @@ namespace armarx void MotionMemory::onInitComponent() { - workingMemory.name() = memoryName; - longtermMemory.name() = memoryName; + this->setMemoryName(memoryName); mdbMotions.onInit(); } diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp index 4dde0d39cfc4d248188e9af772af470914c73290..582cf20586d119f1ef232cb32c706bbd1eef1e4e 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp @@ -68,8 +68,7 @@ namespace armarx::armem::server::robot_state const std::string prefix = "mem."; - workingMemory.name() = "RobotState"; - longtermMemory.name() = "RobotState"; + this->setMemoryName("RobotState"); defs->optional(workingMemory.name(), prefix + "MemoryName", "Name of this memory server."); const std::string robotUnitPrefix{sensorValuePrefix}; diff --git a/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice b/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice index bb1e7f221856b4505184fdeb380683c6f8cb917b..68a3ff2ef6fc06114eaff88dcb3e0393002b1160 100644 --- a/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice +++ b/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice @@ -11,7 +11,7 @@ module armarx { interface LoadingMemoryInterface { - armem::query::data::Result load(armem::query::data::Input query); + }; }; }; diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index 2427d51f1b9c3184add7ffaa4817a75085c9d3bc..1050253cf3728945f245c1a6496b749cc0b0d7c9 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -17,6 +17,9 @@ set(LIBS ArmarXCoreInterfaces ArmarXCore RemoteGui aron + + # Needed for LTM + RobotAPI::aron::converter::json ${LIBMONGOCXX_LIBRARIES} ${LIBBSONCXX_LIBRARIES} ) @@ -48,29 +51,12 @@ set(LIB_FILES core/base/ice_conversions.cpp core/wm/memory_definitions.cpp - core/wm/ice_conversions.cpp - core/wm/json_conversions.cpp core/wm/aron_conversions.cpp + core/wm/ice_conversions.cpp core/wm/detail/data_lookup_mixins.cpp core/wm/visitor/Visitor.cpp core/wm/visitor/FunctionalVisitor.cpp - core/longtermmemory/CoreSegment.cpp - core/longtermmemory/Entity.cpp - core/longtermmemory/EntityInstance.cpp - core/longtermmemory/EntitySnapshot.cpp - core/longtermmemory/Memory.cpp - core/longtermmemory/ProviderSegment.cpp - core/longtermmemory/mongodb/MongoDBConnectionManager.cpp - - core/diskmemory/TypeIO.cpp - core/diskmemory/CoreSegment.cpp - core/diskmemory/Entity.cpp - core/diskmemory/EntityInstance.cpp - core/diskmemory/EntitySnapshot.cpp - core/diskmemory/Memory.cpp - core/diskmemory/ProviderSegment.cpp - core/error/ArMemError.cpp core/error/mns.cpp @@ -95,6 +81,11 @@ set(LIB_FILES server/MemoryRemoteGui.cpp server/RemoteGuiAronDataVisitor.cpp + server/ltm/LongtermMemoryBase.cpp + server/ltm/disk/MemoryManager.cpp + server/ltm/mongodb/MemoryManager.cpp + server/ltm/mongodb/ConnectionManager.cpp + server/wm/memory_definitions.cpp server/wm/ice_conversions.cpp server/wm/detail/MaxHistorySize.cpp @@ -108,7 +99,6 @@ set(LIB_FILES server/query_proc/base/CoreSegmentQueryProcessorBase.cpp server/query_proc/base/MemoryQueryProcessorBase.cpp - server/query_proc/diskmemory.cpp server/query_proc/ltm.cpp server/query_proc/wm.cpp @@ -130,8 +120,8 @@ set(LIB_HEADERS core/SuccessHeader.h core/Time.h core/aron_conversions.h - core/json_conversions.h core/ice_conversions.h + core/json_conversions.h core/ice_conversions_boost_templates.h core/ice_conversions_templates.h @@ -159,30 +149,10 @@ set(LIB_HEADERS core/wm/memory_definitions.h core/wm/aron_conversions.h core/wm/ice_conversions.h - core/wm/json_conversions.h core/wm/detail/data_lookup_mixins.h core/wm/visitor/Visitor.h core/wm/visitor/FunctionalVisitor.h - core/longtermmemory/CoreSegment.h - core/longtermmemory/Entity.h - core/longtermmemory/EntityInstance.h - core/longtermmemory/EntitySnapshot.h - core/longtermmemory/Memory.h - core/longtermmemory/ProviderSegment.h - core/longtermmemory/mongodb/MongoDBConnectionManager.h - - core/diskmemory/TypeIO.h - core/diskmemory/CoreSegment.h - core/diskmemory/Entity.h - core/diskmemory/EntityInstance.h - core/diskmemory/EntitySnapshot.h - core/diskmemory/Memory.h - core/diskmemory/ProviderSegment.h - - core/ice_conversions_templates.h - core/ice_conversions.h - client.h client/ComponentPlugin.h client/MemoryNameSystem.h @@ -212,6 +182,11 @@ set(LIB_HEADERS server/MemoryRemoteGui.h server/RemoteGuiAronDataVisitor.h + server/ltm/LongtermMemoryBase.h + server/ltm/disk/MemoryManager.h + server/ltm/mongodb/MemoryManager.h + server/ltm/mongodb/ConnectionManager.h + server/wm/memory_definitions.h server/wm/ice_conversions.h server/wm/detail/MaxHistorySize.h @@ -228,7 +203,6 @@ set(LIB_HEADERS server/query_proc/base/CoreSegmentQueryProcessorBase.h server/query_proc/base/MemoryQueryProcessorBase.h - server/query_proc/diskmemory.h server/query_proc/ltm.h server/query_proc/wm.h diff --git a/source/RobotAPI/libraries/armem/client/Reader.cpp b/source/RobotAPI/libraries/armem/client/Reader.cpp index 5052a37b8e80ce14028e2cf07cfaa9494c1ea666..2662f86e0d7f36827f9d5cff67f963ac29613de9 100644 --- a/source/RobotAPI/libraries/armem/client/Reader.cpp +++ b/source/RobotAPI/libraries/armem/client/Reader.cpp @@ -5,7 +5,6 @@ #include <ArmarXCore/core/logging/Logging.h> #include <RobotAPI/libraries/armem/core/MemoryID_operators.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem/util/util.h> diff --git a/source/RobotAPI/libraries/armem/core/MemoryID.cpp b/source/RobotAPI/libraries/armem/core/MemoryID.cpp index f8d7c832401a4ebf335347c67745c1ce82af4e03..f3b8c662ddef099b18dc09e03a81f21bd7de2bde 100644 --- a/source/RobotAPI/libraries/armem/core/MemoryID.cpp +++ b/source/RobotAPI/libraries/armem/core/MemoryID.cpp @@ -245,6 +245,31 @@ namespace armarx::armem return id; } + MemoryID MemoryID::removeLeafItem() const + { + if (instanceIndex != -1) + { + return getEntitySnapshotID(); + } + if (timestamp != Time::microSeconds(-1)) + { + return getEntityID(); + } + if (!entityName.empty()) + { + return getProviderSegmentID(); + } + if (!providerSegmentName.empty()) + { + return getCoreSegmentID(); + } + if (!coreSegmentName.empty()) + { + return getMemoryID(); + } + return {}; // return empty if already empty. Client needs to check (Avoids using optional as additional include) + } + void MemoryID::setMemoryID(const MemoryID& id) { memoryName = id.memoryName; diff --git a/source/RobotAPI/libraries/armem/core/MemoryID.h b/source/RobotAPI/libraries/armem/core/MemoryID.h index f9d3d5b58ca4670a8abb4333e417a9a2109843ae..d583e752258d69e8271e30be2a2d091493aa18cc 100644 --- a/source/RobotAPI/libraries/armem/core/MemoryID.h +++ b/source/RobotAPI/libraries/armem/core/MemoryID.h @@ -138,7 +138,11 @@ namespace armarx::armem MemoryID getEntitySnapshotID() const; MemoryID getEntityInstanceID() const; - // Slice setters: Set upper part of the ID. + // Slice getter: remove the last set name + MemoryID removeLeafItem() const; + + + // Slice setters: Set part of the ID. void setMemoryID(const MemoryID& id); void setCoreSegmentID(const MemoryID& id); diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.cpp deleted file mode 100644 index 204aa8f86e20d99df852abbdc79be3d1a07ef39f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.cpp +++ /dev/null @@ -1,106 +0,0 @@ -#include "CoreSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "TypeIO.h" - - -namespace armarx::armem::d_ltm -{ - - std::filesystem::path CoreSegment::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path CoreSegment::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName; - } - - wm::CoreSegment CoreSegment::convert() const - { - wm::CoreSegment m(id()); - for (const auto& [_, s] : _container) - { - m.addProviderSegment(s.convert(_aronType)); - } - return m; - } - - void CoreSegment::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = p_ptr; - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Assuming an empty container."; - } - else - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - auto wms = _container.emplace(k, id().withProviderSegmentName(k)); - wms.first->second.reload(p_ptr); - } - - if (d.is_regular_file()) - { - if (auto type = TypeIO::readAronType(d.path())) - { - _aronType = type; - } - } - } - } - } - - void CoreSegment::append(const wm::CoreSegment& m) - { - std::filesystem::create_directories(_fullPath()); - TypeIO::writeAronType(_aronType, _fullPath()); - - m.forEachProviderSegment([this](const wm::ProviderSegment & s) - { - if (auto it = _container.find(s.name()); it != _container.end()) - { - it->second.append(s); - } - else - { - try - { - std::filesystem::create_directory(_fullPath() / s.name()); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return false; - } - - auto wms = _container.emplace(s.name(), id().withProviderSegmentName(s.name())); - wms.first->second.path = path; - wms.first->second.append(s); - } - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.h b/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.h deleted file mode 100644 index 2d8bd8f29e7027ea63b797d0a19f4b96e116898e..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/armem/core/base/CoreSegmentBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include "ProviderSegment.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of a core segment containing multiple provider segments. - */ - class CoreSegment : - public base::CoreSegmentBase<ProviderSegment, CoreSegment> - { - using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; - - public: - - using Base::CoreSegmentBase; - - - // Conversion - wm::CoreSegment convert() const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void append(const wm::CoreSegment&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/Entity.cpp deleted file mode 100644 index d4d0ac233f1fac1e910869f56cc12541928798ce..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "Entity.h" - -#include <ArmarXCore/core/logging/Logging.h> - - -namespace armarx::armem::d_ltm -{ - std::filesystem::path Entity::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path Entity::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName / id().entityName; - } - - wm::Entity Entity::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - wm::Entity m(id()); - for (const auto& [_, s] : _container) - { - m.addSnapshot(s.convert(expectedStructure)); - } - return m; - } - - void Entity::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = p_ptr; - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Assuming an empty container."; - } - else - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - armem::Time t = armem::Time::microSeconds(std::stol(k)); - auto wms = _container.emplace(std::make_pair(t, id().withTimestamp(t))); - wms.first->second.reload(p_ptr); - } - } - } - } - - void Entity::append(const wm::Entity& m) - { - std::filesystem::create_directories(_fullPath()); - m.forEachSnapshot([this](const wm::EntitySnapshot & s) - { - if (auto it = _container.find(s.time()); it != _container.end()) - { - // timestamp already exists - // We assume that a snapshot does not change, so ignore - } - else - { - std::filesystem::create_directory(_fullPath() / std::to_string(s.time().toMicroSeconds())); - auto wms = _container.emplace(std::make_pair(s.time(), id().withTimestamp(s.time()))); - wms.first->second.path = path; - wms.first->second.setTo(s); - } - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.h b/source/RobotAPI/libraries/armem/core/diskmemory/Entity.h deleted file mode 100644 index 63011ceff58a0ec7ff4b77bf2afeaa0252bbcec0..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/armem/core/base/EntityBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include "EntitySnapshot.h" - - - -namespace armarx::armem::d_ltm -{ - /** - * @brief An entity over a period of time. - * - * An entity should be a physical thing or abstract concept existing - * (and potentially evolving) over some time. - * - * Examples are: - * - objects (the green box) - * - agents (robot, human) - * - locations (frige, sink) - * - grasp affordances (general, or for a specific object) - * - images - * - point clouds - * - other sensory values - * - * At each point in time (`EntitySnapshot`), the entity can have a - * (potentially variable) number of instances (`EntityInstance`), - * each containing a single `AronData` object of a specific `AronType`. - */ - class Entity : - public base::EntityBase<EntitySnapshot, Entity> - { - using Base = base::EntityBase<EntitySnapshot, Entity>; - - public: - - using Base::EntityBase; - - - // Conversion - wm::Entity convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void append(const wm::Entity&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.cpp deleted file mode 100644 index 56786756655453bb97952a206ff642b9f65174cb..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "EntityInstance.h" - -#include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/wm/json_conversions.h> - -#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <SimoxUtility/json/json.hpp> - -#include <iostream> -#include <fstream> - - -namespace armarx::armem::d_ltm -{ - - bool EntityInstance::equalsDeep(const EntityInstance& other) const - { - return id() == other.id(); - } - - - - void EntityInstance::update(const EntityUpdate& update) - { - ARMARX_CHECK_FITS_SIZE(this->index(), update.instancesData.size()); - } - - - std::filesystem::path EntityInstance::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - ARMARX_WARNING << "The path of the disk memory instance with id '" << id().str() << "' is not set. This may lead to errors."; - return std::filesystem::path(); - } - - std::filesystem::path EntityInstance::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName / id().entityName / std::to_string(id().timestamp.toMicroSeconds()) / std::to_string(id().instanceIndex); - } - - wm::EntityInstance EntityInstance::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - std::filesystem::path p = _fullPath(); // here we assume that "reload" has been called first - std::filesystem::path d = p / (std::string(DATA_FILENAME) + ".json"); - - if (std::filesystem::is_regular_file(d)) - { - std::ifstream ifs(d); - std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); - - nlohmann::json j = nlohmann::json::parse(file_content); - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, j, expectedStructure); - wm::EntityInstance e(id()); - from_aron(aron, e); - return e; - } - else - { - throw error::ArMemError("An diskMemory EntityInstance is not leading to a regular file. The path was: " + d.string()); - } - } - - void EntityInstance::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered path is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (!std::filesystem::is_directory(p)) - { - ARMARX_ERROR << "The entered path is not leading to a file! This is an error since if the folder for an EntityInstance exists there must be a data file in it (containing at least the metadata)."; - } - else - { - path = p_ptr; - } - } - - void EntityInstance::setTo(const wm::EntityInstance& m) - { - std::filesystem::path p = _fullPath(); - - try - { - std::filesystem::create_directories(p); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - std::filesystem::path d = p / (std::string(DATA_FILENAME) + ".json"); - - if (std::filesystem::is_regular_file(d)) - { - std::filesystem::remove(d); - } - - std::ofstream ofs; - ofs.open(d); - - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, m); - nlohmann::json j; - from_aron(aron, j); - - ofs << j.dump(2); - ofs.close(); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.h b/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.h deleted file mode 100644 index c56a944f264c08e1f26b35df1ecb622dff92c0e6..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.h +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/armem/core/base/EntityInstanceBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> - - -namespace armarx::armem::d_ltm -{ - /** - * @brief Data of a single entity instance. - */ - class EntityInstance : - public base::EntityInstanceBase<base::NoData, base::NoData> - { - using Base = base::EntityInstanceBase<base::NoData, base::NoData>; - - public: - - using Base::EntityInstanceBase; - - - /** - * @brief Fill `*this` with the update's values. - * @param update The update. - * @param index The instances index. - */ - void update(const EntityUpdate& update); - - bool equalsDeep(const EntityInstance& other) const; - - - // Conversion - wm::EntityInstance convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void setTo(const wm::EntityInstance&); - - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - - private: - static const constexpr char* DATA_FILENAME = "data"; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.cpp deleted file mode 100644 index 9354ab7d52e0dcbd058aec022892132b753b51ab..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "EntitySnapshot.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::d_ltm -{ - - std::filesystem::path EntitySnapshot::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path EntitySnapshot::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName - / id().entityName - / std::to_string(id().timestamp.toMicroSeconds()); - } - - wm::EntitySnapshot EntitySnapshot::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - wm::EntitySnapshot m(id()); - for (const auto& s : _container) - { - m.addInstance(s.convert(expectedStructure)); - } - return m; - } - - void EntitySnapshot::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (!std::filesystem::is_directory(p)) - { - ARMARX_ERROR << "The entered path is not leading to a directory! Every EntitySnapshot must at least contain one EntityInstance."; - } - else - { - _container.clear(); - path = p_ptr; - - // todo - for (int i = 0; i < 1000; ++i) - { - std::filesystem::path d = p / std::to_string(i); - if (std::filesystem::is_directory(d)) - { - auto& wms = _container.emplace_back(id().withInstanceIndex(i)); - wms.reload(p_ptr); - } - else - { - break; - } - } - } - } - - void EntitySnapshot::setTo(const wm::EntitySnapshot& m) - { - try - { - std::filesystem::create_directories(_fullPath()); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - // We remove the content here and reset it with new values - _container.clear(); - - int i = 0; - m.forEachInstance([this, &i](wm::EntityInstance & s) - { - try - { - std::filesystem::create_directory(_fullPath() / std::to_string(i)); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return true; - } - - auto& wms = _container.emplace_back(id().withInstanceIndex(i++)); - wms.path = path; - wms.setTo(s); - - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.h b/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.h deleted file mode 100644 index 9587e3be44435814d4709979a1fb394c67b953c7..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include "EntityInstance.h" - -#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of an entity at one point in time. - */ - class EntitySnapshot : - public base::EntitySnapshotBase<EntityInstance, EntitySnapshot> - { - using Base = base::EntitySnapshotBase<EntityInstance, EntitySnapshot>; - - public: - - using Base::EntitySnapshotBase; - - - // Conversion - void reload(const std::shared_ptr<std::filesystem::path>&); - wm::EntitySnapshot convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // FS connection - void setTo(const wm::EntitySnapshot&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/Memory.cpp deleted file mode 100644 index 3fc60e0496aa21c441cf5e8a60443e43ef741a46..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "Memory.h" - -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::d_ltm -{ - std::filesystem::path Memory::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path Memory::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName; - } - - wm::Memory Memory::convert() const - { - wm::Memory m(id()); - for (const auto& [k, s] : _container) - { - m.addCoreSegment(s.convert()); - } - return m; - } - - void Memory::reload(const std::filesystem::path& p) - { - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = std::make_shared<std::filesystem::path>(p.parent_path()); - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Returning empty memory."; - } - else - { - id() = MemoryID().withMemoryName(p.filename()); - - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - auto wms = _container.emplace(k, id().withCoreSegmentName(k)); - wms.first->second.reload(path); - } - } - } - } - - void Memory::append(const wm::Memory& m) - { - std::filesystem::create_directories(_fullPath()); - m.forEachCoreSegment([this](const wm::CoreSegment & s) -> bool - { - if (auto it = _container.find(s.name()); it != _container.end()) - { - it->second.append(s); - } - else - { - try - { - std::filesystem::create_directory(_fullPath() / s.name()); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return false; - } - - auto wms = _container.emplace(s.name(), id().withCoreSegmentName(s.name())); - wms.first->second.path = path; - wms.first->second.append(s); - } - - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.h b/source/RobotAPI/libraries/armem/core/diskmemory/Memory.h deleted file mode 100644 index aaa1e460a5bff94195ae6da7662a7e53b33861e4..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/armem/core/base/MemoryBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include "CoreSegment.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class Memory : - public base::MemoryBase<CoreSegment, Memory> - { - using Base = base::MemoryBase<CoreSegment, Memory>; - - public: - - using Base::MemoryBase; - - - // Conversion - wm::Memory convert() const; - - // Filesystem connection - void reload(const std::filesystem::path&); - void append(const wm::Memory&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.cpp deleted file mode 100644 index 08ed0c2b07edc156764f02dc88c5f76160f9d355..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "ProviderSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "TypeIO.h" - - -namespace armarx::armem::d_ltm -{ - std::filesystem::path ProviderSegment::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path ProviderSegment::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName; - } - - wm::ProviderSegment ProviderSegment::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - wm::ProviderSegment m(id()); - for (const auto& [_, s] : _container) - { - if (hasAronType()) - { - m.addEntity(s.convert(_aronType)); - } - else - { - m.addEntity(s.convert(expectedStructure)); - } - } - return m; - } - - void ProviderSegment::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = p_ptr; - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Assuming an empty container."; - } - else - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - auto wms = _container.emplace(k, id().withEntityName(k)); - wms.first->second.reload(p_ptr); - } - - if (d.is_regular_file()) - { - if (auto type = TypeIO::readAronType(d.path())) - { - _aronType = type; - } - } - } - } - } - - void ProviderSegment::append(const wm::ProviderSegment& m) - { - - try - { - std::filesystem::create_directories(_fullPath()); - - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - TypeIO::writeAronType(_aronType, _fullPath()); - - m.forEachEntity([this](const wm::Entity & s) - { - if (auto it = _container.find(s.name()); it != _container.end()) - { - it->second.append(s); - } - else - { - - try - { - std::filesystem::create_directory(_fullPath() / s.name()); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return false; - } - - auto wms = _container.emplace(s.name(), id().withEntityName(s.name())); - wms.first->second.path = path; - wms.first->second.append(s); - } - - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.h b/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.h deleted file mode 100644 index 0e866b08cc5c50f623fd9b5676136834078c953a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include "Entity.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of a provider segment containing multiple entities. - */ - class ProviderSegment : - public base::ProviderSegmentBase<Entity, ProviderSegment> - { - using Base = base::ProviderSegmentBase<Entity, ProviderSegment>; - - public: - - using Base::ProviderSegmentBase; - - - // Conversion - wm::ProviderSegment convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void append(const wm::ProviderSegment&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.cpp deleted file mode 100644 index dc7f7a83706c7b9f69cc05a6e81bcf810c10a249..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "TypeIO.h" - -#include <iostream> -#include <fstream> -#include <filesystem> - -#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/visitor/Visitor.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/converter/Converter.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/reader/nlohmannJSON/NlohmannJSONReader.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/writer/navigator/NavigatorWriter.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/writer/nlohmannJSON/NlohmannJSONWriter.h> - - -namespace armarx::armem::d_ltm -{ - - aron::typenavigator::ObjectNavigatorPtr TypeIO::unwrapType(const aron::typenavigator::ObjectNavigatorPtr& type) - { - return aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(type->getMemberType(TYPE_WRAPPER_DATA_FIELD)); - } - - aron::typenavigator::ObjectNavigatorPtr TypeIO::wrapType(const aron::typenavigator::ObjectNavigatorPtr& type) - { - aron::typenavigator::ObjectNavigatorPtr typeWrapped(new aron::typenavigator::ObjectNavigator()); - typeWrapped->setObjectName(type->getObjectName() + "__ltm_type_export"); - typeWrapped->addMemberType(TYPE_WRAPPER_DATA_FIELD, type); - - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_STORED_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_CREATED_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_SENT_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_ARRIVED_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_CONFIDENCE_FIELD, std::make_shared<aron::typenavigator::DoubleNavigator>()); - - return typeWrapped; - } - - aron::typenavigator::ObjectNavigatorPtr TypeIO::readAronType(const std::filesystem::__cxx11::path& filepath) - { - if (std::filesystem::is_regular_file(filepath)) - { - if (filepath.filename() == (std::string(TYPE_FILENAME) + ".json")) - { - std::ifstream ifs(filepath); - std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); - - aron::typeIO::reader::NlohmannJSONReader typeReader(file_content); - aron::typeIO::writer::NavigatorWriter navWriter; - aron::typeIO::Converter::ReadAndConvert(typeReader, navWriter); - return aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(navWriter.getResult()); - } - } - return nullptr; - } - - void TypeIO::writeAronType(const aron::typenavigator::ObjectNavigatorPtr& type, const std::filesystem::__cxx11::path& filepath) - { - if (type) - { - std::ofstream ofs(filepath); - - aron::typeIO::writer::NlohmannJSONWriter typeWriter; - aron::typeIO::Visitor::VisitAndSetup(typeWriter, type); - std::string new_file_full_content = typeWriter.getResult().dump(2); - - ofs << new_file_full_content; - } - } - - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.h b/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.h deleted file mode 100644 index 4311bdcd7034a08b30b7c7f6ae9ee3eab0952fac..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief An entity container with a specific (Aron) type. - */ - class TypeIO - { - public: - - static aron::typenavigator::ObjectNavigatorPtr unwrapType(const aron::typenavigator::ObjectNavigatorPtr& type); - static aron::typenavigator::ObjectNavigatorPtr wrapType(const aron::typenavigator::ObjectNavigatorPtr& type); - - static aron::typenavigator::ObjectNavigatorPtr readAronType(const std::filesystem::path& filepath); - static void writeAronType(const aron::typenavigator::ObjectNavigatorPtr& type, const std::filesystem::path& filepath); - - - private: - - static const constexpr char* TYPE_FILENAME = "type"; - static constexpr const char* TYPE_WRAPPER_DATA_FIELD = "__ARON_DATA"; - static constexpr const char* TYPE_WRAPPER_TIME_STORED_FIELD = "__WRITER_METADATA__TIME_STORED"; - static constexpr const char* TYPE_WRAPPER_TIME_CREATED_FIELD = "__ENTITY_METADATA__TIME_CREATED"; - static constexpr const char* TYPE_WRAPPER_TIME_SENT_FIELD = "__ENTITY_METADATA__TIME_SENT"; - static constexpr const char* TYPE_WRAPPER_TIME_ARRIVED_FIELD = "__ENTITY_METADATA__TIME_ARRIVED"; - static constexpr const char* TYPE_WRAPPER_CONFIDENCE_FIELD = "__ENTITY_METADATA__CONFIDENCE"; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.cpp deleted file mode 100644 index 1fc021d65fe460b4b91331196446ef8661337aa6..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "CoreSegment.h" - -#include "error.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <SimoxUtility/json/json.hpp> - - -namespace armarx::armem::ltm -{ - - wm::CoreSegment CoreSegment::convert() const - { - wm::CoreSegment m(id()); - for (const auto& [_, s] : _container) - { - m.addProviderSegment(s.convert()); - } - return m; - } - - void CoreSegment::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - - mongocxx::cursor cursor = coll.find({}); - for (auto doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - ARMARX_INFO << "CoreSegment: Found foreign key: " << json.at("foreign_key"); - - MemoryID i = MemoryID::fromString(json.at("foreign_key").get<std::string>()); - if (i.coreSegmentName != id().coreSegmentName) - { - throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong coreSegment name. Expected " + id().coreSegmentName); - } - - std::string k = i.providerSegmentName; - - if (auto it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (core) container a key k = " + k + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(k, id().withProviderSegmentName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - } - - ARMARX_INFO << "After reload has core segment " << id().str() << " size: " << _container.size(); - } - - void CoreSegment::append(const wm::CoreSegment& m) - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - m.forEachProviderSegment([this, &coll](const wm::ProviderSegment & provSeg) - { - auto it = _container.find(provSeg.name()); - if (it == _container.end()) - { - bsoncxx::builder::stream::document builder; - bsoncxx::document::value foreign_key = builder - << "foreign_key" << provSeg.id().str() - << bsoncxx::builder::stream::finalize; - coll.insert_one(foreign_key.view()); - - it = _container.emplace(provSeg.name(), id().withProviderSegmentName(provSeg.name())).first; - it->second.dbsettings = dbsettings; - } - it->second.append(provSeg); - - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.h b/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.h deleted file mode 100644 index 32f05ef2f773388b6006c573b7017064193d80c3..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "ProviderSegment.h" -#include "mongodb/MongoDBConnectionManager.h" - -#include <RobotAPI/libraries/armem/core/base/CoreSegmentBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - - -namespace armarx::armem::ltm -{ - - /** - * @brief Data of a core segment containing multiple provider segments. - */ - class CoreSegment : - public base::CoreSegmentBase<ProviderSegment, CoreSegment> - { - using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; - - public: - - using Base::CoreSegmentBase; - - - // Conversion - wm::CoreSegment convert() const; - - // MongoDB connection - void reload(); - void append(const wm::CoreSegment&); - - public: - - MongoDBConnectionManager::MongoDBSettings dbsettings; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.cpp deleted file mode 100644 index 334eca272ceaa8fd1ec4141465aaabdee6fef51f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.cpp +++ /dev/null @@ -1,145 +0,0 @@ -#include "Entity.h" - -#include <ArmarXCore/core/logging/Logging.h> - -#include <SimoxUtility/json/json.hpp> - - -namespace armarx::armem::ltm -{ - - wm::Entity Entity::convert() const - { - wm::Entity m(id()); - for (const auto& [_, s] : _container) - { - m.addSnapshot(s.convert()); - } - return m; - } - - void Entity::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - mongocxx::cursor cursor = coll.find({}); - int i = 0; - for (auto doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - - auto k = armem::Time::microSeconds(json.at("timestamp")); - //ARMARX_INFO << "Entity: Found timestamp: " << std::to_string(k.toMicroSeconds()); - - if (auto it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (entity) container a key k = " + std::to_string(k.toMicroSeconds()) + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(std::make_pair(k, id().withTimestamp(k))); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - ++i; - } - - ARMARX_INFO << "After reload has entity " << id().str() << " size: " << _container.size(); - } - - void Entity::append(const wm::Entity& m) - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - m.forEachSnapshot([this](const wm::EntitySnapshot & snapshot) - { - if (auto it = _container.find(snapshot.time()); it != _container.end()) - { - // timestamp already exists - // We assume that a snapshot does not change, so ignore - } - else - { - auto wms = _container.emplace(std::make_pair(snapshot.time(), id().withTimestamp(snapshot.time()))); - wms.first->second.dbsettings = dbsettings; - wms.first->second.setTo(snapshot, snapshot.time()); - //truncateHistoryToSize(); - } - return true; - }); - } - - bool Entity::hasSnapshot(const Time& time) const - { - // check cache - if (Base::hasSnapshot(time)) - { - return true; - } - // check mongodb - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - bsoncxx::stdx::optional<bsoncxx::document::value> maybe_result = - coll.find_one(document{} << "timestamp" << time.toMicroSeconds() << finalize); - if (!maybe_result) - { - return false; - } - - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*maybe_result)); - MemoryID id = MemoryID::fromString(json["id"].get<std::string>()); - nlohmann::json instances = json["instances"]; - EntitySnapshot snapshot(id); - snapshot.dbsettings = dbsettings; - - for (size_t i = 0; i < instances.size(); ++i) - { - EntityInstance instance(id.withInstanceIndex(static_cast<int>(i))); - snapshot.addInstance(instance); - } - - _container.emplace(time, snapshot); - //truncateHistoryToSize(); - return true; - } - - std::vector<Time> Entity::getTimestamps() const - { - // get from cache - std::vector<Time> ret; // = Base::getTimestamps(); - - // get missing from mongodb - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - auto cursor = coll.find({}); - for (auto doc : cursor) - { - auto json = nlohmann::json::parse(bsoncxx::to_json(doc)); - auto ts = json["timestamp"].get<long>(); - ret.push_back(Time::microSeconds(ts)); - } - return ret; - } - - const EntitySnapshot& Entity::getSnapshot(const Time& time) const - { - if (!hasSnapshot(time)) - { - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); - } - - // the above command already puts the reference to the cache - return Base::getSnapshot(time); - } - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.h b/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.h deleted file mode 100644 index 3bbee7debd446de2e1bd47f401165544c6b5fe03..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "EntitySnapshot.h" -#include "mongodb/MongoDBConnectionManager.h" - -#include <RobotAPI/libraries/armem/core/base/EntityBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - - -namespace armarx::armem::ltm -{ - /** - * @brief An entity over a period of time. - * - * An entity should be a physical thing or abstract concept existing - * (and potentially evolving) over some time. - * - * Examples are: - * - objects (the green box) - * - agents (robot, human) - * - locations (frige, sink) - * - grasp affordances (general, or for a specific object) - * - images - * - point clouds - * - other sensory values - * - * At each point in time (`EntitySnapshot`), the entity can have a - * (potentially variable) number of instances (`EntityInstance`), - * each containing a single `AronData` object of a specific `AronType`. - */ - class Entity : - public base::EntityBase<EntitySnapshot, Entity> - { - using Base = base::EntityBase<EntitySnapshot, Entity>; - - public: - - using Base::EntityBase; - - - // Conversion - wm::Entity convert() const; - - // MongoDB connection - void reload(); - void append(const wm::Entity&); - - // overrides for LTM lookups - bool hasSnapshot(const Time& time) const; - std::vector<Time> getTimestamps() const; - const EntitySnapshot& getSnapshot(const Time& time) const; - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.cpp deleted file mode 100644 index 63fe18145c561e9efda0ba2a14cb07175b029d1c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "EntityInstance.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - - -namespace armarx::armem::ltm -{ - - bool EntityInstance::equalsDeep(const EntityInstance& other) const - { - return id() == other.id() && _metadata == other.metadata(); - } - - void EntityInstance::update(const EntityUpdate& update) - { - ARMARX_CHECK_FITS_SIZE(this->index(), update.instancesData.size()); - - this->_metadata.confidence = update.confidence; - this->_metadata.timeCreated = update.timeCreated; - this->_metadata.timeSent = update.timeSent; - this->_metadata.timeArrived = update.timeArrived; - } - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.h b/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.h deleted file mode 100644 index 6046f6ac50902d6f7f770361915dbd30d5a15ec1..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/armem/core/base/EntityInstanceBase.h> - - -namespace armarx::armem::ltm -{ - - using EntityInstanceMetadata = base::EntityInstanceMetadata; - - - /** - * @brief Data of a single entity instance. - */ - class EntityInstance : - public base::EntityInstanceBase<base::NoData, EntityInstanceMetadata> - { - using Base = base::EntityInstanceBase<base::NoData, EntityInstanceMetadata>; - - public: - - using Base::EntityInstanceBase; - - - /** - * @brief Fill `*this` with the update's values. - * @param update The update. - * @param index The instances index. - */ - void update(const EntityUpdate& update); - - bool equalsDeep(const EntityInstance& other) const; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.cpp deleted file mode 100644 index 8cbe688125a7571a3e8f0a19031b25722bb79bbf..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "EntitySnapshot.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> - -#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/wm/json_conversions.h> - - -#include "error.h" - - -namespace armarx::armem::ltm -{ - - wm::EntitySnapshot EntitySnapshot::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().getEntityID().str()]; - - auto res = coll.find_one(document{} << "id" << id().getEntitySnapshotID().str() << finalize); - if (!res) - { - throw error::ArMemError("Could not load an instance from the memory '" + id().getEntityID().str() + "'. Tried to access: " + id().getEntitySnapshotID().str()); - } - - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); - nlohmann::json instances = json["instances"]; - - if (instances.size() != _container.size()) - { - throw error::ArMemError("The size of the mongodb entity entry at id " + id().getEntitySnapshotID().str() + " has wrong size. Expected: " + std::to_string(_container.size()) + " but got: " + std::to_string(instances.size())); - } - - wm::EntitySnapshot m(id()); - for (unsigned int i = 0; i < _container.size(); ++i) - { - nlohmann::json doc = instances[i++]; - - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, doc, expectedStructure); - wm::EntityInstance e(id()); - from_aron(aron, e); - m.addInstance(e); - } - return m; - } - - void EntitySnapshot::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().getEntityID().str()]; - - auto res = coll.find_one(document{} << "id" << id().getEntitySnapshotID().str() << finalize); - - if (!res) - { - throw error::ArMemError("Could not load an instance from the memory '" + id().getEntityID().str() + "'. Tried to access: " + id().getEntitySnapshotID().str()); - } - - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); - for (unsigned int i = 0; i < json.at("instances").size(); ++i) - { - _container.emplace_back(id().withInstanceIndex(i)); - } - } - - void EntitySnapshot::setTo(const wm::EntitySnapshot& m, const armem::Time& t) - { - // We remove the contente here and reset it with new values - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().getEntityID().str()]; - - bsoncxx::builder::stream::document builder{}; - auto in_array = builder - << "id" << id().getEntitySnapshotID().str() - << "timestamp" << t.toMicroSeconds() - << "instances"; - auto array_builder = bsoncxx::builder::basic::array{}; - - int i = 0; - m.forEachInstance([this, &array_builder, &i](const wm::EntityInstance & instance) - { - auto wms = _container.emplace_back(id().withInstanceIndex(i++)); - - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, instance); - nlohmann::json j; - from_aron(aron, j); - - auto doc_value = bsoncxx::from_json(j.dump(2)); - array_builder.append(doc_value); - - return true; - }); - - auto after_array = in_array << array_builder; - bsoncxx::document::value doc = after_array << bsoncxx::builder::stream::finalize; - coll.insert_one(doc.view()); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.h b/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.h deleted file mode 100644 index 3029f8a7ca0278b9a505ee2fa5a744cfab327359..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "EntityInstance.h" -#include "mongodb/MongoDBConnectionManager.h" - -#include <RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> - - -namespace armarx::armem::ltm -{ - - /** - * @brief Data of an entity at one point in time. - */ - class EntitySnapshot : - public base::EntitySnapshotBase<EntityInstance, EntitySnapshot> - { - using Base = base::EntitySnapshotBase<EntityInstance, EntitySnapshot>; - - public: - - using Base::EntitySnapshotBase; - - - // Conversion - wm::EntitySnapshot convert(const aron::typenavigator::NavigatorPtr& = nullptr) const; - - // MongoDB connection - void reload(); - void setTo(const wm::EntitySnapshot&, const armem::Time& t); - - - public: - - MongoDBConnectionManager::MongoDBSettings dbsettings; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.cpp deleted file mode 100644 index 68c00061943b6d78be1948f4920dd3e01acfbdbe..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "Memory.h" - -#include "error.h" - -#include <ArmarXCore/core/application/properties/PluginAll.h> -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/time/TimeUtil.h> - - -namespace armarx::armem::ltm -{ - - Memory::Memory(const Memory& other) : - Base(other), - dbsettings(other.dbsettings), - periodicTransferSettings(other.periodicTransferSettings), - onFullTransferSettings(other.onFullTransferSettings), - mongoDBMutex() - { - // Do not copy _mutex. - } - - - Memory::Memory(Memory&& other) : - Base(std::move(other)), - dbsettings(other.dbsettings), - periodicTransferSettings(other.periodicTransferSettings), - onFullTransferSettings(other.onFullTransferSettings), - reloaded(other.reloaded) - { - // Do not move _mutex. - } - - - Memory& Memory::operator=(const Memory& other) - { - Base::operator=(other); - - dbsettings = other.dbsettings; - periodicTransferSettings = other.periodicTransferSettings; - onFullTransferSettings = other.onFullTransferSettings; - - // Don't copy _mutex. - return *this; - } - - - Memory& Memory::operator=(Memory&& other) - { - Base::operator=(std::move(other)); - - dbsettings = std::move(other.dbsettings); - periodicTransferSettings = std::move(other.periodicTransferSettings); - onFullTransferSettings = std::move(other.onFullTransferSettings); - reloaded = other.reloaded; - - // Don't move _mutex. - return *this; - } - - bool Memory::checkConnection() const - { - // Check connection: - ARMARX_INFO << "Checking connection"; - if (!MongoDBConnectionManager::ConnectionIsValid(dbsettings)) - { - ARMARX_WARNING << deactivateSpam("ConnectionIsNotValid") - << "The connection to mongocxx for memory '" << name() << "' is not valid. Settings are: " << dbsettings.toString() - << "\nTo start it, run e.g.: \n" - << "armarx memory start" - << "\n\n"; - return false; - } - return true; - } - - void Memory::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) - { - defs->optional(dbsettings.host, prefix + "ltm.10_host"); - defs->optional(dbsettings.port, prefix + "ltm.11_port"); - defs->optional(dbsettings.user, prefix + "ltm.20_user"); - defs->optional(dbsettings.password, prefix + "ltm.21_password"); - defs->optional(dbsettings.database, prefix + "ltm.22_database"); - defs->optional(periodicTransferSettings.enabled, prefix + "ltm.30_enablePeriodicTransfer", "Enable transfer based on periodic interval."); - defs->optional(onFullTransferSettings.enabled, prefix + "ltm.31_enableOnFullTransfer", "Enable transfer whenever the wm is full (see maxHistorySize)."); - } - - wm::Memory Memory::convert() const - { - wm::Memory m(id()); - - std::lock_guard l(mongoDBMutex); - if (!checkConnection()) - { - return m; - } - - ARMARX_INFO << "Converting Memory with connection to: " << dbsettings.toString(); - - TIMING_START(LTM_Convert); - - for (const auto& [_, s] : _container) - { - m.addCoreSegment(s.convert()); - } - - TIMING_END(LTM_Convert); - return m; - } - - void Memory::reload() - { - std::lock_guard l(mongoDBMutex); - reloaded = false; - - if (!checkConnection()) - { - return; - } - - ARMARX_INFO << "(Re)Establishing connection to: " << dbsettings.toString(); - - TIMING_START(LTM_Reload); - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - if (!client) - { - ARMARX_ERROR << "A client has died. Could not reload."; - return; - } - - auto databases = client.list_databases(); - for (const auto& doc : databases) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - ARMARX_INFO << "Found the database: " << json.at("name"); - } - - mongocxx::database db = client[dbsettings.database]; - ARMARX_INFO << "Getting collection for id: " << id().str(); - mongocxx::collection coll = db[id().str()]; - - ARMARX_IMPORTANT << "Memory Container size is: " << _container.size(); - - mongocxx::cursor cursor = coll.find({}); - for (const auto& doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - ARMARX_INFO << "Memory: Found foreign key: " << json.at("foreign_key"); - - MemoryID i((std::string) json.at("foreign_key")); - if (i.memoryName != id().memoryName) - { - throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name. Expected " + id().memoryName); - } - - std::string k = i.coreSegmentName; - - if (auto it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (memory) container a key k = " + k + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(k, id().withCoreSegmentName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - } - - reloaded = true; - for (const auto& m : toAppendQueue) - { - _append(m); - } - - TIMING_END(LTM_Reload); - ARMARX_INFO << "After reload memory " << id().str() << " size: " << _container.size() << ". Setting reloaded: " << reloaded; - } - - void Memory::_append(const wm::Memory& m) - { - if (!checkConnection() || !reloaded) - { - // We ignore if not fully loaded yet - return; - } - - //ARMARX_INFO << "Merge memory with name '" << m.name() << "' into the LTM with name '" << name() << "'"; - - TIMING_START(LTM_Append); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - m.forEachCoreSegment([this, &coll](const wm::CoreSegment & s) - { - if (auto it = _container.find(s.name()); it != _container.end()) - { - // TODO check if foreign key exists - it->second.append(s); - } - else - { - auto builder = bsoncxx::builder::stream::document{}; - bsoncxx::document::value foreign_key = builder - << "foreign_key" << s.id().withCoreSegmentName(s.name()).str() - << bsoncxx::builder::stream::finalize; - coll.insert_one(foreign_key.view()); - - auto wms = _container.emplace(s.name(), id().withCoreSegmentName(s.name())); - wms.first->second.dbsettings = dbsettings; - wms.first->second.append(s); - } - - return true; - }); - - TIMING_END(LTM_Append); - } - - void Memory::append(const wm::Memory& m) - { - std::lock_guard l(mongoDBMutex); - if (!checkConnection() || !reloaded) - { - toAppendQueue.push_back(m); - return; - } - _append(m); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.h b/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.h deleted file mode 100644 index 41e82bce38b50f9e122e34056c2b5fd74cec4bac..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.h +++ /dev/null @@ -1,87 +0,0 @@ -#pragma once - -#include "CoreSegment.h" -#include "mongodb/MongoDBConnectionManager.h" - -#include <RobotAPI/libraries/armem/core/base/MemoryBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - -#include <ArmarXCore/core/application/properties/forward_declarations.h> - -#include <mutex> - - -namespace armarx::armem::ltm -{ - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class Memory : - public base::MemoryBase<CoreSegment, Memory> - { - using Base = base::MemoryBase<CoreSegment, Memory>; - - public: - - struct TransferSettings - { - bool enabled = false; - }; - - struct PeriodicTransferSettings : public TransferSettings - { - bool deleteFromWMOnTransfer = false; - int frequencyHz = 1; - }; - - struct OnFullTransferSettings : public TransferSettings - { - enum class Mode - { - TRANSFER_LATEST, - TRANSFER_LEAST_USED - }; - - Mode mode; - int batch_size = 20; - }; - - - public: - - using Base::MemoryBase; - - Memory(const Memory& other); - Memory(Memory&& other); - Memory& operator=(const Memory& other); - Memory& operator=(Memory&& other); - - // PropertyDefinitions related to LTM - void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = ""); - - // Conversion - wm::Memory convert() const; - - // MongoDB connection - void reload(); - void append(const wm::Memory&); - - - private: - bool checkConnection() const; - - void _append(const wm::Memory&); - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - - PeriodicTransferSettings periodicTransferSettings; - OnFullTransferSettings onFullTransferSettings; - - private: - bool reloaded = false; - mutable std::mutex mongoDBMutex; - - std::vector<wm::Memory> toAppendQueue; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.cpp deleted file mode 100644 index 8126867e4db4be01c91dfb582b67c94303751927..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "ProviderSegment.h" - -#include "error.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <SimoxUtility/json/json.hpp> - - -namespace armarx::armem::ltm -{ - - wm::ProviderSegment ProviderSegment::convert() const - { - wm::ProviderSegment m(id()); - for (const auto& [_, s] : _container) - { - m.addEntity(s.convert()); - } - return m; - } - - void ProviderSegment::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - mongocxx::cursor cursor = coll.find({}); - for (auto doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - - MemoryID i((std::string) json.at("foreign_key")); - if (i.providerSegmentName != id().providerSegmentName) - { - throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong providerSegment name. Expected " + id().providerSegmentName); - } - - std::string k = i.entityName; - - if (auto it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (provvider) container a key k = " + k + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(k, id().withEntityName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - } - - ARMARX_INFO << "After reload has provider segment " << id().str() << " size: " << _container.size(); - } - - void ProviderSegment::append(const wm::ProviderSegment& m) - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - m.forEachEntity([this, &coll](const wm::Entity & s) - { - if (auto it = _container.find(s.name()); it != _container.end()) - { - it->second.append(s); - } - else - { - auto builder = bsoncxx::builder::stream::document{}; - bsoncxx::document::value foreign_key = builder - << "foreign_key" << s.id().withEntityName(s.name()).str() - << bsoncxx::builder::stream::finalize; - coll.insert_one(foreign_key.view()); - - auto wms = _container.emplace(s.name(), id().withEntityName(s.name())); - wms.first->second.dbsettings = dbsettings; - wms.first->second.append(s); - } - - return true; - }); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.h b/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.h deleted file mode 100644 index bf1d5e3a9f4cbd816e087957d0bb3d6351b0b6cd..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "Entity.h" -#include "mongodb/MongoDBConnectionManager.h" - -#include <RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> - - -namespace armarx::armem::ltm -{ - - /** - * @brief Data of a provider segment containing multiple entities. - */ - class ProviderSegment : - public base::ProviderSegmentBase<Entity, ProviderSegment> - { - using Base = base::ProviderSegmentBase<Entity, ProviderSegment>; - - public: - - using Base::ProviderSegmentBase; - - - // Conversion - wm::ProviderSegment convert() const; - - // MongoDB connection - void reload(); - void append(const wm::ProviderSegment&); - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.h b/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.h deleted file mode 100644 index 0cd3cb5ba8f6c575d6d3dd9357f16884f508914c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include <string> -#include <vector> -#include <map> -#include <memory> -#include <iostream> - -#include <bsoncxx/json.hpp> -#include <mongocxx/client.hpp> -#include <mongocxx/stdx.hpp> -#include <mongocxx/uri.hpp> -#include <mongocxx/instance.hpp> -#include <bsoncxx/builder/stream/helpers.hpp> -#include <bsoncxx/builder/stream/document.hpp> -#include <bsoncxx/builder/stream/array.hpp> - - -namespace armarx::armem::ltm -{ - - using bsoncxx::builder::stream::close_array; - using bsoncxx::builder::stream::close_document; - using bsoncxx::builder::stream::document; - using bsoncxx::builder::stream::finalize; - using bsoncxx::builder::stream::open_array; - using bsoncxx::builder::stream::open_document; - - - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class MongoDBConnectionManager - { - public: - struct MongoDBSettings - { - std::string host = "localhost"; - unsigned int port = 25270; - std::string user = ""; - std::string password = ""; - std::string database = "Test"; - - - bool isSet() const - { - // we always need a user and a host - return !host.empty() and port != 0 and !user.empty(); - } - - std::string uri() const - { - return "mongodb://" + host + ":" + std::to_string(port) + user; - } - - std::string toString() const - { - return uri() + ":" + password + "/" + database; - } - }; - - static mongocxx::client& EstablishConnection(const MongoDBSettings& settings); - static bool ConnectionIsValid(const MongoDBSettings& settings, bool force = false); - static bool ConnectionExists(const MongoDBSettings& settings); - - private: - static void initialize_if(); - - - private: - static bool initialized; - static std::map<std::string, mongocxx::client> Connections; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/wm/json_conversions.h b/source/RobotAPI/libraries/armem/core/wm/json_conversions.h deleted file mode 100644 index 4ce93812b606688e642eecc0b7856210adce1a90..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/wm/json_conversions.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> -#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> - -#include <SimoxUtility/json/json.hpp> - - -namespace armarx::armem -{ - void from_aron(const aron::datanavigator::DictNavigatorPtr&, nlohmann::json&); - void to_aron(aron::datanavigator::DictNavigatorPtr&, const nlohmann::json&, - const aron::typenavigator::NavigatorPtr& expectedStructure = nullptr); -} diff --git a/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp b/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp index 8e2026b3012ba84a9e77cb9e22ed28302443096c..28bf14da2878639bb57df42da833a1d3b5e15249 100644 --- a/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp +++ b/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp @@ -21,7 +21,6 @@ namespace armarx::armem::server::plugins properties->topic(memoryListener, parent.memoryListenerDefaultName); properties->optional(parent.longtermMemoryEnabled, "mem.ltm.00_enabled"); - parent.longtermMemory.defineProperties(properties, "mem."); } @@ -38,7 +37,7 @@ namespace armarx::armem::server::plugins // establishing connection to ltm and mongodb if (parent.longtermMemoryEnabled) { - parent.longtermMemory.reload(); + parent.longtermMemoryManager.reload(); } } @@ -114,6 +113,13 @@ namespace armarx::armem::server ComponentPluginUser::~ComponentPluginUser() = default; + // Set the name of a memory + void ComponentPluginUser::setMemoryName(const std::string& name) + { + workingMemory.name() = name; + longtermMemoryManager.setName(name); + } + // WRITING data::AddSegmentsResult ComponentPluginUser::addSegments(const data::AddSegmentsInput& input, const Ice::Current&) @@ -148,16 +154,11 @@ namespace armarx::armem::server // LTM STORING data::StoreResult ComponentPluginUser::store(const data::StoreInput& input, const Ice::Current&) { - std::scoped_lock lock(/*workingMemoryMutex,*/ longtermMemoryMutex); + //std::scoped_lock lock(workingMemoryMutex, longtermMemoryMutex); return iceMemory.store(input); } // LTM LOADING - armem::query::data::Result ComponentPluginUser::load(const armem::query::data::Input& input, const Ice::Current&) - { - std::scoped_lock lock(longtermMemoryMutex); - return iceMemory.load(input); - } } diff --git a/source/RobotAPI/libraries/armem/server/ComponentPlugin.h b/source/RobotAPI/libraries/armem/server/ComponentPlugin.h index 0b2b872c1ff4686eecefd8ae8508bcfc45600d5f..35a03e7b0a3ddd4f8aafebadbbd60474dc613c8e 100644 --- a/source/RobotAPI/libraries/armem/server/ComponentPlugin.h +++ b/source/RobotAPI/libraries/armem/server/ComponentPlugin.h @@ -9,7 +9,7 @@ #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> +#include <RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h> #include <RobotAPI/libraries/armem/client/MemoryNameSystemComponentPlugin.h> #include "MemoryToIceAdapter.h" @@ -75,6 +75,9 @@ namespace armarx::armem::server ComponentPluginUser(); virtual ~ComponentPluginUser() override; + /// Set the name of the wm and the ltm (if enabled) + void setMemoryName(const std::string&); + // WritingInterface interface virtual data::AddSegmentsResult addSegments(const data::AddSegmentsInput& input, const Ice::Current& = Ice::emptyCurrent) override; @@ -92,7 +95,6 @@ namespace armarx::armem::server // LoadingInterface interface - virtual armem::query::data::Result load(const armem::query::data::Input&, const Ice::Current& = Ice::emptyCurrent) override; public: @@ -102,15 +104,17 @@ namespace armarx::armem::server // [[deprecated ("The global working memory mutex is deprecated. Use the core segment mutexes instead.")]] // std::mutex workingMemoryMutex; + /// Parameter to indicate whether to use or not to use the ltm feature bool longtermMemoryEnabled = true; - ltm::Memory longtermMemory; - std::mutex longtermMemoryMutex; + + /// A manager class for the ltm. It internally holds a normal wm instance + server::ltm::mongodb::MemoryManager longtermMemoryManager; /// property defaults std::string memoryListenerDefaultName = "MemoryUpdates"; /// Helps connecting `memory` to ice. Used to handle Ice callbacks. - MemoryToIceAdapter iceMemory { &workingMemory, &longtermMemory}; + MemoryToIceAdapter iceMemory { &workingMemory, &longtermMemoryManager}; private: diff --git a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp index a61fbfefa85c3371187dde11fbaca82fcacc6b3a..b47c7f3135756cbfa7c33fcc6a2714b865cec010 100644 --- a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp +++ b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp @@ -17,8 +17,8 @@ namespace armarx::armem::server { - MemoryToIceAdapter::MemoryToIceAdapter(wm::Memory* workingMemory, ltm::Memory* longtermMemory) : - workingMemory(workingMemory), longtermMemory(longtermMemory) + MemoryToIceAdapter::MemoryToIceAdapter(wm::Memory* workingMemory, server::ltm::mongodb::MemoryManager* longtermMemory) : + workingMemory(workingMemory), longtermMemoryManager(longtermMemory) { } @@ -181,7 +181,7 @@ namespace armarx::armem::server // also store in ltm if transfermode is set to always // TODO: Move outside of loop? - if (longtermMemory) + if (longtermMemoryManager) { } @@ -236,25 +236,16 @@ namespace armarx::armem::server armem::wm::Memory wmResult = wmServerProcessor.process(input.memoryQueries, *workingMemory); query_proc::ltm::MemoryQueryProcessor ltmProcessor; - ltm::Memory ltmResult = ltmProcessor.process(input, *longtermMemory); + armem::wm::Memory ltmResult = ltmProcessor.process(input, longtermMemoryManager->getCacheAndLutNotConverted()); armem::query::data::Result result; if (not ltmResult.empty()) { ARMARX_INFO << "The LTM returned data after query"; - // ATTENTION: This code block moves data from LTM back into WM. - // However, since some segments are constrained, the WM might send data back to LTM. - // This may also affect the data returned by the current query. - // However, this is expected behavior, since we copy the data in the processor (copyEmpty) we can safely return the copy and - // remove the original memory reference from WM here. - armem::wm::Memory ltmConverted = ltmResult.convert(); - if (ltmConverted.empty()) - { - ARMARX_ERROR << "A converted memory contains no data although the original memory contained data. This indicates that something is wrong."; - } + longtermMemoryManager->convert(ltmResult); // convert memory ==> meaning resolving lut references to e.g. mongodb - wmResult.append(ltmConverted); + wmResult.append(ltmResult); if (wmResult.empty()) { ARMARX_ERROR << "A merged Memory has no data although at least the LTM result contains data. This indicates that something is wrong."; @@ -303,21 +294,12 @@ namespace armarx::armem::server // LTM LOADING FROM LTM - query::data::Result MemoryToIceAdapter::load(const armem::query::data::Input& query) - { - ARMARX_CHECK_NOT_NULL(longtermMemory); - query::data::Result output; - - output.success = true; - return output; - } - // LTM STORING data::StoreResult MemoryToIceAdapter::store(const armem::data::StoreInput& input) { ARMARX_CHECK_NOT_NULL(workingMemory); - ARMARX_CHECK_NOT_NULL(longtermMemory); + ARMARX_CHECK_NOT_NULL(longtermMemoryManager); data::StoreResult output; for (const auto& query : input.query.memoryQueries) @@ -334,7 +316,7 @@ namespace armarx::armem::server { armem::wm::Memory m; fromIce(queryResult.memory, m); - longtermMemory->append(m); + longtermMemoryManager->append(m); } return output; diff --git a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h index 6aa68b5714ad369e3b2bbad08d7c1ca06ab04bb8..f924399c7eb749f8404f28c1c2a9bd7945112a7c 100644 --- a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h +++ b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h @@ -4,7 +4,7 @@ #include <RobotAPI/interface/armem/client/MemoryListenerInterface.h> #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> +#include <RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h> #include <RobotAPI/libraries/armem/client/Query.h> #include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> @@ -24,7 +24,7 @@ namespace armarx::armem::server /// Construct an MemoryToIceAdapter from an existing Memory. MemoryToIceAdapter(server::wm::Memory* workingMemory = nullptr, - ltm::Memory* longtermMemory = nullptr); + server::ltm::mongodb::MemoryManager* longtermMemory = nullptr); void setMemoryListener(client::MemoryListenerInterfacePrx memoryListenerTopic); @@ -48,7 +48,6 @@ namespace armarx::armem::server client::QueryResult query(const client::QueryInput& input); // LTM LOADING - query::data::Result load(const armem::query::data::Input& input); // LTM STORING data::StoreResult store(const armem::data::StoreInput& input); @@ -56,7 +55,7 @@ namespace armarx::armem::server public: server::wm::Memory* workingMemory; - ltm::Memory* longtermMemory; + server::ltm::mongodb::MemoryManager* longtermMemoryManager; client::MemoryListenerInterfacePrx memoryListenerTopic; diff --git a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e8c64d64121eccc007cad7ba2d55c2e361b94e2 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp @@ -0,0 +1,134 @@ +// Header +#include "LongtermMemoryBase.h" + +// ArmarX +#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/logging/Logging.h> + + +namespace armarx::armem::server::ltm +{ + void LongtermMemoryBase::setName(const std::string& name) + { + cache.name() = name; + lut.name() = name; + } + + armem::wm::Memory LongtermMemoryBase::getCacheAndLutNotConverted() const + { + std::lock_guard l(cache_mutex); + std::lock_guard l2(lut_mutex); + + armem::wm::Memory m(lut.name()); + m.append(cache); + m.append(lut); + + // debug output + //lut.forEachSnapshot([](const auto & e) + //{ + // ARMARX_INFO << "The SNapshot: " << e.id().str() << " has size: " << e.size(); + //}); + + return m; + } + + void LongtermMemoryBase::append(const armem::wm::Memory& m) + { + TIMING_START(LTM_Append); + ARMARX_INFO << "Append memory with name '" << m.name() << "' into the LTM with name '" << cache.name() << "'"; + + std::lock_guard l(cache_mutex); + cache.append(m); + + encodeAndStore(); + + TIMING_END(LTM_Append); + } + + void LongtermMemoryBase::checkUpdateLatestSnapshot(const armem::wm::EntitySnapshot& newSnapshot) + { + // update map of latestSnapshots + if (auto it = latestSnapshots.find(newSnapshot.id().getEntityID().str()); it != latestSnapshots.end()) + { + auto ptr = it->second; + if (ptr->id().timestamp > newSnapshot.id().timestamp) + { + ptr = &newSnapshot; + } + // else ignore ==> no update + } + else + { + // no entry yet + latestSnapshots.emplace(newSnapshot.id().getEntityID().str(), &newSnapshot); + } + } + + bool LongtermMemoryBase::containsCoreSegment(const MemoryID& coreSegmentID) const + { + //ARMARX_INFO << "Check if lut has core seg"; + if (lut.hasCoreSegment(coreSegmentID.coreSegmentName)) + { + //ARMARX_INFO << "lus has core seg"; + return true; + } + return false; + } + + bool LongtermMemoryBase::containsProviderSegment(const MemoryID& providerSegmentID) const + { + //ARMARX_INFO << "Check if lut has prov seg"; + if (lut.hasCoreSegment(providerSegmentID.coreSegmentName)) + { + auto core = lut.getCoreSegment(providerSegmentID.coreSegmentName); + if (core.hasProviderSegment(providerSegmentID.providerSegmentName)) + { + //ARMARX_INFO << "lus has prov seg"; + return true; + } + } + return false; + } + + bool LongtermMemoryBase::containsEntity(const MemoryID& entityID) const + { + //ARMARX_INFO << "Check if lut has entity"; + if (lut.hasCoreSegment(entityID.coreSegmentName)) + { + auto core = lut.getCoreSegment(entityID.coreSegmentName); + if (core.hasProviderSegment(entityID.providerSegmentName)) + { + auto prov = core.getProviderSegment(entityID.providerSegmentName); + if (prov.hasEntity(entityID.entityName)) + { + //ARMARX_INFO << "lus has entity"; + return true; + } + } + } + return false; + } + + bool LongtermMemoryBase::containsSnapshot(const MemoryID& snapshotID) const + { + //ARMARX_INFO << "Check if lut has snapshot"; + if (lut.hasCoreSegment(snapshotID.coreSegmentName)) + { + auto core = lut.getCoreSegment(snapshotID.coreSegmentName); + if (core.hasProviderSegment(snapshotID.providerSegmentName)) + { + auto prov = core.getProviderSegment(snapshotID.providerSegmentName); + if (prov.hasEntity(snapshotID.entityName)) + { + auto entity = prov.getEntity(snapshotID.entityName); + if (entity.hasSnapshot(snapshotID.timestamp)) + { + //ARMARX_INFO << "lut has snapshot"; + return true; + } + } + } + } + return false; + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h new file mode 100644 index 0000000000000000000000000000000000000000..097f951548cc777fa03cedbc5f425e867439cd48 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h @@ -0,0 +1,65 @@ +#pragma once + +// STD / STL +#include <optional> +#include <mutex> + +// Memory +#include "../../core/wm/memory_definitions.h" + +namespace armarx::armem::server::ltm +{ + /// @brief Interface functions for the longterm memory classes + class LongtermMemoryBase + { + public: + struct AppendResult + { + std::vector<MemoryID> addedCoreSegments; + std::vector<MemoryID> addedProviderSegments; + std::vector<MemoryID> addedEntities; + + std::vector<MemoryID> addedSnapshots; + std::vector<MemoryID> replacedSnapshots; + std::vector<MemoryID> ignoredSnapshots; + }; + + struct ReloadResult + { + + }; + + void append(const armem::wm::Memory&); + + virtual void reload() = 0; + virtual void convert(armem::wm::Memory&) = 0; + virtual void encodeAndStore() = 0; + + // pass through to internal memory + void setName(const std::string& name); + + // get merged internal memory + armem::wm::Memory getCacheAndLutNotConverted() const; + + protected: + void checkUpdateLatestSnapshot(const armem::wm::EntitySnapshot& newSnapshot); + + bool containsCoreSegment(const MemoryID&) const; + bool containsProviderSegment(const MemoryID&) const; + bool containsEntity(const MemoryID&) const; + bool containsSnapshot(const MemoryID&) const; + + protected: + /// Internal memory for data consolidated from wm to ltm (cache) + armem::wm::Memory cache; + mutable std::recursive_mutex cache_mutex; + + /// Internal memory for indexes (lut) + armem::wm::Memory lut; + mutable std::recursive_mutex lut_mutex; + + /// A map from entityID to its latest snapshot stored. When adding a new snapshot we compare it to the last one stored. + std::map<std::string, const armem::wm::EntitySnapshot*> latestSnapshots; + + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d3a018d44acd587cd1c020127652c54a59a076e7 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp @@ -0,0 +1,401 @@ +// Header +#include "MemoryManager.h" + +// STD / STL +#include <iostream> +#include <fstream> + +// Simox +#include <SimoxUtility/json.h> + +// ArmarX +#include <ArmarXCore/core/time/TimeUtil.h> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> + +namespace +{ + // check whether a string is a number (timestamp folders) + bool isNumber(const std::string& s) + { + for (char const& ch : s) + { + if (std::isdigit(ch) == 0) + { + return false; + } + } + return true; + } +} + +namespace armarx::armem::server::ltm::disk +{ + namespace fs = std::filesystem; + + bool MemoryManager::checkPath() const + { + // Check connection: + ARMARX_INFO << "Checking Path"; + if (!fs::exists(basePathToMemory) || !fs::is_directory(basePathToMemory) || basePathToMemory.filename() != lut.name()) + { + ARMARX_WARNING << deactivateSpam("PathIsNotValid") + << "The entered path is not valid. Please use a path leading to a memory folder with name: " << lut.name() << "." + << "\n\n"; + return false; + } + + return true; + } + + void MemoryManager::reload() + { + TIMING_START(LTM_Reload); + ARMARX_INFO << "(Re)Loading a memory from: " << basePathToMemory.string(); + + if (!checkPath()) + { + // abort + ARMARX_WARNING << "Could not (pre)load a memory from the filesystem."; + return; + } + + armem::wm::Memory temp(lut.id()); // a temporary client wm. We will append temp to the lut at the end of this metho (append ignores duplicate entries) + ARMARX_INFO << "Loading memory: " << temp.id().str(); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + for (const auto& d : std::filesystem::directory_iterator(basePathToMemory)) + // Although this looks like code duplication, we need a distinguition between memories, + // core, prov and entities because of a different structure + // (only core and prov have same structure) + { + + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (temp.hasCoreSegment(k)) + { + throw error::ArMemError("Somehow the (memory) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over core segments + // /////////////////////////////// + auto& cSeg = temp.addCoreSegment(k); + for (const auto& d : std::filesystem::directory_iterator(d)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (cSeg.hasProviderSegment(k)) + { + throw error::ArMemError("Somehow the (core) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over provider segments + // /////////////////////////////// + auto& pSeg = cSeg.addProviderSegment(k); + for (const auto& d : std::filesystem::directory_iterator(d)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (pSeg.hasEntity(k)) + { + throw error::ArMemError("Somehow the (provider) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over entities + // /////////////////////////////// + auto& eSeg = pSeg.addEntity(k); + for (const auto& d : std::filesystem::directory_iterator(d)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (!isNumber(k)) + { + continue; + } + + auto ts = armem::Time::microSeconds(std::stol(k)); + // TODO catch exceptions? + + if (eSeg.hasSnapshot(ts)) + { + throw error::ArMemError("Somehow the (entity) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over entities + // /////////////////////////////// + auto& sSeg = eSeg.addSnapshot(ts); + for (unsigned int i = 0; i < 10000; ++i) // ugly workaround to get the folders in the correct order + { + fs::path p = d / std::to_string(i); + if (!fs::exists(p) || !fs::is_directory(p)) + { + // early stopping + break; + } + + fs::path data = p / DATA_FILENAME; + if (!fs::exists(data) || !fs::is_regular_file(data)) + { + // do not set data + continue; + } + + // else we have an instance + /*std::ifstream ifs(data); + std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); + + nlohmann::json j = nlohmann::json::parse(file_content); + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSON(j);*/ + + sSeg.addInstance(); + //from_aron(aron, instance); + } + } + } + } + } + + std::lock_guard l(cache_mutex); // we always take the cache mutex BEFORE the lut mutex! (otherwise we may have deadlocks) + std::lock_guard l2(lut_mutex); + lut.append(temp); + ARMARX_INFO << "After reload memory " << lut.id().str() << " has size: " << lut.size(); + + TIMING_END(LTM_Reload); + } + + void MemoryManager::convert(armem::wm::Memory& m) + { + TIMING_START(LTM_Convert); + if (!checkPath()) + { + // abort + ARMARX_WARNING << "Could not convert a memory from the filesystem."; + return; + } + + // update emtpy data ptr + m.forEachCoreSegment([this](armem::wm::CoreSegment & e) + { + e.forEachProviderSegment([this](armem::wm::ProviderSegment & e) + { + e.forEachEntity([this](armem::wm::Entity & e) + { + e.forEachSnapshot([this](armem::wm::EntitySnapshot & e) + { + // check whether data is nullptr + bool allDataIsNull = e.size() > 0; + e.forEachInstance([&allDataIsNull](armem::wm::EntityInstance & e) + { + if (e.data()) + { + allDataIsNull = false; + return false; // means break + } + return true; + }); + + if (allDataIsNull) // an entry from the lut (probably... for now we assume that every entry either has data (cache) or has null (lut)) + { + // Get data from mongodb + auto p = basePathToMemory / e.id().coreSegmentName / e.id().providerSegmentName / e.id().entityName / std::to_string(e.id().timestamp.toMicroSeconds()); + + if (fs::exists(p) && fs::is_directory(p)) + { + for (unsigned int i = 0; i < e.size(); ++i) + { + auto data = p / std::to_string(i) / DATA_FILENAME; + + if (fs::exists(data) && fs::is_regular_file(data)) + { + auto& ins = e.getInstance(i); + + std::ifstream ifs(data); + std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); + nlohmann::json doc = nlohmann::json::parse(file_content); + + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSON(doc); + + wm::EntityInstance tmp(e.id().withInstanceIndex(i)); + from_aron(aron, tmp); + + ins.data() = tmp.data(); + } + } + } + // else leave snapshot untouched + } + }); + }); + }); + }); + TIMING_END(LTM_Convert); + } + + void MemoryManager::encodeAndStore() + { + TIMING_START(LTM_Encode); + if (!checkPath()) + { + // abort + ARMARX_WARNING << "Could not (pre)load a memory from the filesystem."; + return; + } + + std::lock_guard l(cache_mutex); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + fs::path mPath = basePathToMemory; + cache.forEachCoreSegment([&mPath](armem::wm::CoreSegment & e) + { + fs::path cPath = mPath / e.id().coreSegmentName; + if (!fs::exists(cPath)) + { + // not found + fs::create_directory(cPath); + } + if (!fs::is_directory(cPath)) + { + throw error::ArMemError("Could not create the (core) folder: " + cPath.string()); + } + + // /////////////////////////////// + // Iterate over provider segments + // /////////////////////////////// + e.forEachProviderSegment([&cPath](armem::wm::ProviderSegment & e) + { + fs::path pPath = cPath / e.id().providerSegmentName; + if (!fs::exists(pPath)) + { + // not found + fs::create_directory(pPath); + } + if (!fs::is_directory(pPath)) + { + throw error::ArMemError("Could not create the (provider) folder: " + pPath.string()); + } + + // /////////////////////////////// + // Iterate over entities + // /////////////////////////////// + e.forEachEntity([&pPath](armem::wm::Entity & e) + { + fs::path ePath = pPath / e.id().entityName; + if (!fs::exists(ePath)) + { + // not found + fs::create_directory(ePath); + } + if (!fs::is_directory(ePath)) + { + throw error::ArMemError("Could not create the (entity) folder: " + ePath.string()); + } + + // /////////////////////////////// + // Iterate over snapshots + // /////////////////////////////// + e.forEachSnapshot([&ePath](armem::wm::EntitySnapshot & e) + { + fs::path sPath = ePath / std::to_string(e.id().timestamp.toMicroSeconds()); + if (!fs::exists(sPath)) + { + // not found + fs::create_directory(sPath); + } + else + { + ARMARX_INFO << "The snapshot " << sPath.string() << " already exists. Ingoring it."; + return; // continue if already exists + } + + if (!fs::is_directory(sPath)) + { + throw error::ArMemError("Could not create the (timestamp) folder: " + sPath.string()); + } + + // /////////////////////////////// + // Iterate over instances + // /////////////////////////////// + e.forEachInstance([&sPath](armem::wm::EntityInstance & e) + { + fs::path iPath = sPath / std::to_string(e.id().instanceIndex); + if (!fs::exists(iPath)) + { + // not found + fs::create_directory(iPath); + } + else + { + // This is strange, since we know, that the snapshot folder did not exist. + // However, we ignore and continue + ARMARX_INFO << "Somehow the instance folder " << iPath.string() << " already exists, although the snapshot folder was newly created. Ignore this."; + return; + } + if (!fs::is_directory(iPath)) + { + throw error::ArMemError("Could not create the (instance) folder: " + iPath.string()); + } + + fs::path data = iPath / DATA_FILENAME; + if (fs::exists(data)) + { + // Should not be the case. Anyway, if it happens, create new file! + fs::remove(data); + } + + std::ofstream ofs; + ofs.open(data); + + auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); + to_aron(aron, e); + nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(aron); + + ofs << j.dump(2); + ofs.close(); + }); + }); + }); + }); + + }); + + // what to do with clear text data after encoding? + // TODO! + + // Finaly clear cache and put reference to lut + cache.forEachInstance([](armem::wm::EntityInstance & i) + { + i.data() = nullptr; + }); + + std::lock_guard l2(lut_mutex); + lut.append(cache); + cache.clear(); + + TIMING_END(LTM_Encode); + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h new file mode 100644 index 0000000000000000000000000000000000000000..227d419119e3889006a3f0f30a4789e3b9e76dfe --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h @@ -0,0 +1,44 @@ +#pragma once + +// STD / STL +#include <mutex> +#include <optional> +#include <filesystem> + +// Base Class +#include "../LongtermMemoryBase.h" + +namespace armarx::armem::server::ltm::disk +{ + /// @brief A memory storing data in mongodb (needs 'armarx memory start' to start the mongod instance) + class MemoryManager : + public LongtermMemoryBase + { + using Base = LongtermMemoryBase; + + public: + MemoryManager() = default; + + void reload() override; + void convert(armem::wm::Memory&) override; + void encodeAndStore() override; + + void setBasePath(const std::filesystem::path& p) + { + basePathToMemory = p; + } + + protected: + + + private: + bool checkPath() const; + + public: + std::filesystem::path basePathToMemory; + + private: + static const constexpr char* TYPE_FILENAME = "type.aron.ltm.json"; + static const constexpr char* DATA_FILENAME = "data.aron.ltm.json"; + }; +} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp similarity index 51% rename from source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.cpp rename to source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp index fd2bbbb6ccb8aee93c4d343b818dc55872f7838a..ad2d95b0fed35991ae99e050a9939537a840a2a2 100644 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp @@ -1,13 +1,15 @@ -#include "MongoDBConnectionManager.h" +#include "ConnectionManager.h" -namespace armarx::armem::ltm +namespace armarx::armem::server::ltm::mongodb { - bool MongoDBConnectionManager::initialized = false; - std::map<std::string, mongocxx::client> MongoDBConnectionManager::Connections = {}; + std::mutex ConnectionManager::initializationMutex; + bool ConnectionManager::initialized = false; + std::map<std::string, std::unique_ptr<mongocxx::pool>> ConnectionManager::Connections = {}; - void MongoDBConnectionManager::initialize_if() + void ConnectionManager::initialize_if() { + std::lock_guard l(initializationMutex); // all others have to wait until the initialization is complete if (!initialized) { initialized = true; @@ -15,7 +17,7 @@ namespace armarx::armem::ltm } } - mongocxx::client& MongoDBConnectionManager::EstablishConnection(const MongoDBSettings& settings) + mongocxx::pool& ConnectionManager::Connect(const MongoDBSettings& settings) { initialize_if(); @@ -24,36 +26,36 @@ namespace armarx::armem::ltm if (it == Connections.end()) { mongocxx::uri uri(uri_str); - auto con = Connections.emplace(uri_str, mongocxx::client(uri)); - return con.first->second; + auto pool = std::make_unique<mongocxx::pool>(uri); + auto con = Connections.emplace(settings.key(), std::move(pool)); + return *con.first->second; } else { // A connection already exists. We do not need to open another one. - return it->second; + return *it->second; } } - bool MongoDBConnectionManager::ConnectionIsValid(const MongoDBSettings& settings, bool force) + bool ConnectionManager::ConnectionIsValid(const MongoDBSettings& settings, bool forceNewConnection) { initialize_if(); try { - if (!force) + if (!forceNewConnection) { - const auto uri_str = settings.uri(); - auto it = Connections.find(uri_str); + auto it = Connections.find(settings.key()); if (it != Connections.end()) { - auto admin = it->second["admin"]; + auto client = it->second->acquire(); + auto admin = client->database("admin"); auto result = admin.run_command(bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("isMaster", 1))); return true; } } - const auto uri_str = settings.uri(); - mongocxx::uri uri(uri_str); + mongocxx::uri uri(settings.uri()); auto client = mongocxx::client(uri); auto admin = client["admin"]; auto result = admin.run_command(bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("isMaster", 1))); @@ -65,12 +67,11 @@ namespace armarx::armem::ltm } } - bool MongoDBConnectionManager::ConnectionExists(const MongoDBSettings& settings) + bool ConnectionManager::ConnectionExists(const MongoDBSettings& settings) { initialize_if(); - const auto uri_str = settings.uri(); - auto it = Connections.find(uri_str); + auto it = Connections.find(settings.key()); return it != Connections.end(); } } diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h new file mode 100644 index 0000000000000000000000000000000000000000..15fac008d7963c034ce40fe55a4606ffe3fead3f --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h @@ -0,0 +1,97 @@ +#pragma once + +#include <string> +#include <mutex> +#include <map> +#include <memory> +#include <sstream> + +#include <bsoncxx/json.hpp> +#include <mongocxx/client.hpp> +#include <mongocxx/pool.hpp> +#include <mongocxx/stdx.hpp> +#include <mongocxx/uri.hpp> +#include <mongocxx/instance.hpp> +#include <bsoncxx/builder/stream/helpers.hpp> +#include <bsoncxx/builder/stream/document.hpp> +#include <bsoncxx/builder/stream/array.hpp> + + +namespace armarx::armem::server::ltm::mongodb +{ + + using PoolClientPtr = mongocxx::pool::entry; + + /** + * @brief A manager of multiple mongodb connection + */ + class ConnectionManager + { + public: + struct MongoDBSettings + { + std::string host = "localhost"; + unsigned int port = 25276; + std::string user = ""; + std::string password = ""; + std::string database = "Test"; + int minPoolSize = 5; + int maxPoolSize = 100; + + + bool isSet() const + { + // we always need a host and a port + return !host.empty() and port != 0; + } + + std::string baseUri() const + { + std::stringstream ss; + ss << "mongodb://"; + + if (!user.empty()) + { + ss << user; + if (!password.empty()) + { + ss << ":" << password; + } + ss << "@"; + } + ss << host; + return ss.str(); + } + + std::string key() const + { + // TODO: What happens if a connection exists and you would like to open another one with a different user (e.g. that sees different things)? + return "mongodb://" + host + ":" + std::to_string(port); + } + + std::string uri() const + { + return baseUri() + ":" + std::to_string(port) + "/?minPoolSize=" + std::to_string(minPoolSize) + "&maxPoolSize=" + std::to_string(maxPoolSize); + } + + std::string toString() const + { + return uri() + "&database=" + database; + } + }; + + static mongocxx::pool& Connect(const MongoDBSettings& settings); + static bool ConnectionIsValid(const MongoDBSettings& settings, bool forceNewConnection = false); + static bool ConnectionExists(const MongoDBSettings& settings); + + private: + static void initialize_if(); + + + private: + static std::mutex initializationMutex; + static bool initialized; + static std::map<std::string, std::unique_ptr<mongocxx::pool>> Connections; + + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6419584d4a1bd0e870a4dc48ff703c938c0a7789 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp @@ -0,0 +1,424 @@ +// Header +#include "MemoryManager.h" + +// Simox +#include <SimoxUtility/json.h> + +// ArmarX +#include <ArmarXCore/core/time/TimeUtil.h> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> + + +namespace armarx::armem::server::ltm::mongodb +{ + namespace bsoncxxbuilder = bsoncxx::builder::stream; + namespace bsoncxxdoc = bsoncxx::document; + + PoolClientPtr MemoryManager::checkConnection() const + { + // Check connection: + ARMARX_INFO << "Checking connection"; + if (!ConnectionManager::ConnectionIsValid(dbsettings)) + { + ARMARX_WARNING << deactivateSpam("ConnectionIsNotValid") + << "The connection to mongocxx for ltm '" << cache.name() << "' is not valid. Settings are: " << dbsettings.toString() + << "\nTo start it, run e.g.: \n" + << "armarx memory start" + << "\n\n"; + return nullptr; + } + + auto& pool = ConnectionManager::Connect(dbsettings); + auto client = pool.acquire(); + + return client; + } + + void MemoryManager::reload() + { + TIMING_START(LTM_Reload); + ARMARX_INFO << "(Re)Establishing connection to: " << dbsettings.toString(); + + auto client = checkConnection(); + if (!client) + { + // abort + ARMARX_WARNING << "A connection to: " << dbsettings.toString() << " is not possible. Could not (pre)load data from mongodb."; + return; + } + + auto databases = client->list_databases(); + for (const auto& doc : databases) + { + auto el = doc["name"]; + ARMARX_INFO << "Found Memory-Collection in MongoDB: " << el.get_utf8().value; + } + + armem::wm::Memory temp(lut.id()); // a temporary client wm. We will append temp to the lut at the end of this metho (append ignores duplicate entries) + mongocxx::database db = client->database(dbsettings.database); + + ARMARX_INFO << "Loading memory: " << temp.id().str(); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + mongocxx::collection mColl = db.collection(temp.id().str()); + mongocxx::cursor cursor = mColl.find({}); + for (const auto& doc : cursor) // Although this looks like code duplication, we need a distinguition between memories, + // core, prov and entities because of a different structure + // (only core and prov have same structure) + { + auto el = doc[MONGO_FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.memoryName != temp.id().memoryName) + { + throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name: " + i.str()); + } + + std::string k = i.coreSegmentName; + + if (temp.hasCoreSegment(k)) + { + throw error::ArMemError("Somehow the (memory) container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + // /////////////////////////////// + // Add and iterate over provider segments + // /////////////////////////////// + auto& cSeg = temp.addCoreSegment(k); + mongocxx::collection cColl = db.collection(cSeg.id().str()); + mongocxx::cursor cursor = cColl.find({}); + for (const auto& doc : cursor) + { + auto el = doc[MONGO_FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.coreSegmentName != cSeg.id().coreSegmentName || i.memoryName != cSeg.id().memoryName) + { + throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name: " + i.str()); + } + + std::string k = i.providerSegmentName; + if (cSeg.hasProviderSegment(k)) + { + throw error::ArMemError("Somehow the (core segment) container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + // /////////////////////////////// + // Add and iterate over entities + // /////////////////////////////// + auto& pSeg = cSeg.addProviderSegment(k); + mongocxx::collection pColl = db.collection(pSeg.id().str()); + mongocxx::cursor cursor = pColl.find({}); + for (const auto& doc : cursor) + { + auto el = doc[MONGO_FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.providerSegmentName != pSeg.id().providerSegmentName || i.coreSegmentName != pSeg.id().coreSegmentName || i.memoryName != pSeg.id().memoryName) + { + throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name: " + i.str()); + } + + std::string k = i.entityName; + if (pSeg.hasEntity(k)) + { + throw error::ArMemError("Somehow the (provider segment) container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + // /////////////////////////////// + // Add and iterate over snapshots + // /////////////////////////////// + auto& eSeg = pSeg.addEntity(k); + mongocxx::collection eColl = db.collection(eSeg.id().str()); + mongocxx::cursor cursor = eColl.find({}); + for (const auto& doc : cursor) + { + auto ts = armem::Time::microSeconds(doc[MONGO_TIMESTAMP].get_int64().value); + auto& newSnapshot = eSeg.addSnapshot(ts); + + auto i = doc[MONGO_INSTANCES]; + unsigned long length = std::distance(i.get_array().value.begin(), i.get_array().value.end()); + ARMARX_INFO << "The array legth for an snapshot '" << newSnapshot.id().str() << "' is: " << length; + + for (unsigned long i = 0; i < length; ++i) + { + // add an empty instance as reference + newSnapshot.addInstance({}); + } + + + // check to update lastSnapshot map + checkUpdateLatestSnapshot(newSnapshot); + } + } + } + } + } + } + } + + std::lock_guard l(cache_mutex); // we always take the cache mutex BEFORE the lut mutex! (otherwise we may have deadlocks) + std::lock_guard l2(lut_mutex); + lut.append(temp); + ARMARX_INFO << "After reload memory " << lut.id().str() << " has size: " << lut.size(); + + TIMING_END(LTM_Reload); + } + + void MemoryManager::convert(armem::wm::Memory& m) + { + TIMING_START(LTM_Convert); + + auto client = checkConnection(); + if (!client) + { + // abort + ARMARX_WARNING << "A connection to: " << dbsettings.toString() << " is not possible. Could not convert information, therefore returning leaving input untouched."; + return; + } + mongocxx::database db = client->database(dbsettings.database); + + // update emtpy data ptr + m.forEachCoreSegment([&db](armem::wm::CoreSegment & e) + { + e.forEachProviderSegment([&db](armem::wm::ProviderSegment & e) + { + e.forEachEntity([&db](armem::wm::Entity & e) + { + e.forEachSnapshot([&db](armem::wm::EntitySnapshot & e) + { + // check whether data is nullptr + bool allDataIsNull = e.size() > 0; + e.forEachInstance([&allDataIsNull](armem::wm::EntityInstance & e) + { + if (e.data()) + { + allDataIsNull = false; + return false; // means break + } + return true; + }); + + if (allDataIsNull) // an entry from the lut (probably... for now we assume that every entry either has data (cache) or has null (lut)) + { + // Get data from mongodb + auto eColl = db.collection(e.id().getEntityID().str()); + auto q = bsoncxxbuilder::document{} << std::string(MONGO_TIMESTAMP) << e.id().timestamp.toMicroSeconds() << bsoncxxbuilder::finalize; + auto res = eColl.find_one(q.view()); + + if (!res) + { + throw error::ArMemError("Could not load an instance from the memory '" + e.id().getEntityID().str() + "'. Tried to access: " + e.id().getEntitySnapshotID().str()); + } + + // convert full json of this entry + nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); + nlohmann::json instances = json[MONGO_INSTANCES]; + + if (instances.size() != e.size()) + { + throw error::ArMemError("The size of the mongodb entity entry at id " + e.id().getEntitySnapshotID().str() + " has wrong size. Expected: " + std::to_string(e.size()) + " but got: " + std::to_string(instances.size())); + } + + for (unsigned int i = 0; i < e.size(); ++i) + { + auto& ins = e.getInstance(i); + + // get ionstance json + nlohmann::json doc = instances[i]; + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSON(doc); + + // remove metadata + wm::EntityInstance tmp(e.id().withInstanceIndex(i)); + from_aron(aron, tmp); + + // set data + ins.data() = tmp.data(); + } + } + }); + }); + }); + }); + TIMING_END(LTM_Convert); + } + + void MemoryManager::encodeAndStore() + { + TIMING_START(LTM_Encode); + ARMARX_INFO << "Encode cache " << cache.id().str() << " with size: " << cache.size(); + + auto client = checkConnection(); + if (!client) + { + // abort + ARMARX_WARNING << "A connection to: " << dbsettings.toString() << " is not possible. Could not consolidate data from cache to mongodb."; + return; + } + + std::lock_guard l(cache_mutex); + mongocxx::database db = client->database(dbsettings.database); + auto mColl = db.collection(cache.id().str()); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + cache.forEachCoreSegment([this, &db, &mColl](armem::wm::CoreSegment & e) + { + auto cColl = db.collection(e.id().str()); + + if (!containsCoreSegment(mColl, e.id())) + { + // not found + mongodbInsertForeignKey(mColl, e.id().str()); + } + + // /////////////////////////////// + // Iterate over provider segments + // /////////////////////////////// + e.forEachProviderSegment([this, &db, &cColl](armem::wm::ProviderSegment & e) + { + auto pColl = db.collection(e.id().str()); + + if (!containsProviderSegment(cColl, e.id())) + { + // not found + mongodbInsertForeignKey(cColl, e.id().str()); + } + + // /////////////////////////////// + // Iterate over each segments + // /////////////////////////////// + e.forEachEntity([this, &db, &pColl](armem::wm::Entity & e) + { + auto eColl = db.collection(e.id().str()); + + if (!containsEntity(pColl, e.id())) + { + // not found + mongodbInsertForeignKey(pColl, e.id().str()); + } + + // /////////////////////////////// + // Iterate over snapshots + // /////////////////////////////// + e.forEachSnapshot([this, &eColl](armem::wm::EntitySnapshot & e) + { + if (!this->containsSnapshot(eColl, e.id())) + { + // timestamp not found in mongodb ==> new entry + bsoncxxbuilder::document builder{}; + auto in_array = builder + << std::string(MONGO_ID) << e.id().str() + << std::string(MONGO_TIMESTAMP) << e.id().timestamp.toMicroSeconds() + << std::string(MONGO_INSTANCES); + auto array_builder = bsoncxx::builder::basic::array{}; + + e.forEachInstance([&array_builder](const wm::EntityInstance & e) + { + auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); + to_aron(aron, e); + nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(aron); + + auto doc_value = bsoncxx::from_json(j.dump(2)); + array_builder.append(doc_value); + }); + + auto after_array = in_array << array_builder; + bsoncxx::document::value doc = after_array << bsoncxx::builder::stream::finalize; + ARMARX_INFO << "Insert into mongodb: " << e.id().timestamp.toMicroSeconds(); + eColl.insert_one(doc.view()); + + // check to update lastSnapshot map + checkUpdateLatestSnapshot(e); + } + else + { + ARMARX_INFO << "Timestamp already found for id: " << e.id().str(); + // timestamp already in mongodb. Ignore it + } + }); + }); + }); + }); + + // what to do with clear text data after encoding? + // TODO! + + // Finaly clear cache and put reference to lut + cache.forEachInstance([](armem::wm::EntityInstance & i) + { + i.data() = nullptr; + }); + + std::lock_guard l2(lut_mutex); + lut.append(cache); + cache.clear(); + + TIMING_END(LTM_Encode); + } + + void MemoryManager::mongodbInsertForeignKey(mongocxx::collection& coll, const std::string& key) + { + auto q = bsoncxxbuilder::document{} << std::string(MONGO_FOREIGN_KEY) << key << bsoncxxbuilder::finalize; + coll.insert_one(q.view()); + } + + bool MemoryManager::mongodbContainsForeignKey(mongocxx::collection& coll, const std::string& key) const + { + // check mongodb + auto q = bsoncxxbuilder::document{} << std::string(MONGO_FOREIGN_KEY) << key << bsoncxxbuilder::finalize; + auto res = coll.find_one(q.view()); + return (bool) res; + } + + bool MemoryManager::containsCoreSegment(mongocxx::collection& mColl, const MemoryID& coreSegmentID) const + { + if (Base::containsCoreSegment(coreSegmentID)) + { + return true; + } + return mongodbContainsForeignKey(mColl, coreSegmentID.str()); + } + + bool MemoryManager::containsProviderSegment(mongocxx::collection& cColl, const MemoryID& providerSegmentID) const + { + if (Base::containsProviderSegment(providerSegmentID)) + { + return true; + } + return mongodbContainsForeignKey(cColl, providerSegmentID.str()); + } + + bool MemoryManager::containsEntity(mongocxx::collection& pColl, const MemoryID& entityID) const + { + if (Base::containsEntity(entityID)) + { + return true; + } + return mongodbContainsForeignKey(pColl, entityID.str()); + } + + bool MemoryManager::containsSnapshot(mongocxx::collection& eColl, const MemoryID& snapshotID) const + { + if (Base::containsSnapshot(snapshotID)) + { + return true; + } + + // check mongodb + auto q = bsoncxxbuilder::document{} << std::string(MONGO_TIMESTAMP) << snapshotID.timestamp.toMicroSeconds() << bsoncxxbuilder::finalize; + auto res = eColl.find_one(q.view()); + return (bool) res; + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h new file mode 100644 index 0000000000000000000000000000000000000000..189ccc42563d17dd63116dc3210d99d648b28c31 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h @@ -0,0 +1,51 @@ +#pragma once + +// STD / STL +#include <mutex> +#include <optional> + +// Base Class +#include "../LongtermMemoryBase.h" + +// Data +# include "ConnectionManager.h" + +namespace armarx::armem::server::ltm::mongodb +{ + /// @brief A memory storing data in mongodb (needs 'armarx memory start' to start the mongod instance) + class MemoryManager : + public LongtermMemoryBase + { + using Base = LongtermMemoryBase; + + public: + MemoryManager() = default; + + void reload() override; + void convert(armem::wm::Memory&) override; + void encodeAndStore() override; + + protected: + + + private: + PoolClientPtr checkConnection() const; // return nullptr if not possible + + void mongodbInsertForeignKey(mongocxx::collection&, const std::string& key); + + bool mongodbContainsForeignKey(mongocxx::collection&, const std::string& key) const; + bool containsCoreSegment(mongocxx::collection&, const MemoryID&) const; + bool containsProviderSegment(mongocxx::collection&, const MemoryID&) const; + bool containsEntity(mongocxx::collection&, const MemoryID&) const; + bool containsSnapshot(mongocxx::collection&, const MemoryID&) const; + + public: + ConnectionManager::MongoDBSettings dbsettings; + + private: + static const constexpr char* MONGO_FOREIGN_KEY = "foreign_key"; + static const constexpr char* MONGO_ID = "id"; + static const constexpr char* MONGO_TIMESTAMP = "timestamp"; + static const constexpr char* MONGO_INSTANCES = "instances"; + }; +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory.cpp b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory.cpp deleted file mode 100644 index a695cf43d51dcbbc2329fa1c48fbe8e63565c24f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "diskmemory.h" - - -namespace armarx::armem::server::query_proc::d_ltm -{ -} - diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory.h b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory.h deleted file mode 100644 index 0f2ffe791d192b9bc2b5f2a2be311735808427a4..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/armem/core/diskmemory/Memory.h> -#include <RobotAPI/libraries/armem/server/query_proc/base.h> - - -namespace armarx::armem::server::query_proc::d_ltm -{ - static const base::QueryTarget queryTarget = query::data::QueryTarget::LTM; - - - class EntityQueryProcessor : - public base::EntityQueryProcessorBase<queryTarget, armem::d_ltm::Entity, armem::d_ltm::Entity> - { - }; - - class ProviderSegmentQueryProcessor : - public base::ProviderSegmentQueryProcessorBase <queryTarget, armem::d_ltm::ProviderSegment, armem::d_ltm::ProviderSegment, EntityQueryProcessor > - { - }; - - class CoreSegmentQueryProcessor : - public base::CoreSegmentQueryProcessorBase<queryTarget, armem::d_ltm::CoreSegment, armem::d_ltm::CoreSegment, ProviderSegmentQueryProcessor> - { - }; - - class MemoryQueryProcessor : - public base::MemoryQueryProcessorBase <queryTarget, armem::d_ltm::Memory, armem::d_ltm::Memory, CoreSegmentQueryProcessor > - { - }; - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/ltm.h b/source/RobotAPI/libraries/armem/server/query_proc/ltm.h index ae0dc778c821ae138c98b8554a22237bf26e5989..05734c2e15d6836b69719283805fcfc5c1d7702d 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/ltm.h +++ b/source/RobotAPI/libraries/armem/server/query_proc/ltm.h @@ -1,6 +1,6 @@ #pragma once -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> +#include <RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h> #include <RobotAPI/libraries/armem/server/query_proc/base.h> @@ -10,22 +10,22 @@ namespace armarx::armem::server::query_proc::ltm class EntityQueryProcessor : - public base::EntityQueryProcessorBase<queryTarget, armem::ltm::Entity, armem::ltm::Entity> + public base::EntityQueryProcessorBase<queryTarget, armem::wm::Entity, armem::wm::Entity> { }; class ProviderSegmentQueryProcessor : - public base::ProviderSegmentQueryProcessorBase <queryTarget, armem::ltm::ProviderSegment, armem::ltm::ProviderSegment, EntityQueryProcessor > + public base::ProviderSegmentQueryProcessorBase <queryTarget, armem::wm::ProviderSegment, armem::wm::ProviderSegment, EntityQueryProcessor > { }; class CoreSegmentQueryProcessor : - public base::CoreSegmentQueryProcessorBase <queryTarget, armem::ltm::CoreSegment, armem::ltm::CoreSegment, ProviderSegmentQueryProcessor> + public base::CoreSegmentQueryProcessorBase <queryTarget, armem::wm::CoreSegment, armem::wm::CoreSegment, ProviderSegmentQueryProcessor> { }; class MemoryQueryProcessor : - public base::MemoryQueryProcessorBase <queryTarget, armem::ltm::Memory, armem::ltm::Memory, CoreSegmentQueryProcessor > + public base::MemoryQueryProcessorBase <queryTarget, armem::wm::Memory, armem::wm::Memory, CoreSegmentQueryProcessor > { }; diff --git a/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp index 3ac3d50411854022aa31b781ba011e9ac8d91c75..f0ede7b42a3bdb34d1a103ab43226adae833740a 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp @@ -34,7 +34,6 @@ #include <RobotAPI/libraries/aron/core/Debug.h> -#include "../core/longtermmemory/Memory.h" //#include "../core/io/diskWriter/NlohmannJSON/NlohmannJSONDiskWriter.h" diff --git a/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp index 439f3d8bc338695ad293e8d179ea3f9d100b6ae9..e404024f9bdaabd7f532ada9a4cc9f02189c186c 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp @@ -28,8 +28,6 @@ #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/diskmemory/Memory.h> #include <RobotAPI/libraries/armem/core/error.h> #include <iostream> @@ -290,7 +288,7 @@ struct CustomChecks<armem::wm::EntityInstance> BOOST_CHECK_EQUAL(moved.data(), nullptr); } }; -template <> +/*template <> struct CustomChecks<armem::d_ltm::EntityInstance> { static void checkEqual(const armem::d_ltm::EntityInstance& lhs, const armem::d_ltm::EntityInstance& rhs) @@ -361,7 +359,7 @@ struct CustomChecks<armem::d_ltm::Memory> { checkMoved_d_ltm(moved); } -}; +};*/ struct CopyMoveCtorsOpsTestBase @@ -495,9 +493,7 @@ struct CopyMoveCtorsOpsTest : public CopyMoveCtorsOpsTestBase void reset() override { in = T {id}; - if constexpr(std::is_same_v<T, armem::wm::Memory> - || std::is_same_v<T, armem::ltm::Memory> - || std::is_same_v<T, armem::d_ltm::Memory>) + if constexpr(std::is_same_v<T, armem::wm::Memory>) { in.addCoreSegment("C"); } @@ -603,22 +599,22 @@ BOOST_AUTO_TEST_CASE(test_copy_move_ctors_ops) CopyMoveCtorsOpsTest<armem::wm::CoreSegment>().test(); CopyMoveCtorsOpsTest<armem::wm::Memory>().test(); } - { + /*{ InstanceCopyMoveCtorsOpsTest<armem::ltm::EntityInstance>().test(); CopyMoveCtorsOpsTest<armem::ltm::EntitySnapshot>().test(); CopyMoveCtorsOpsTest<armem::ltm::Entity>().test(); CopyMoveCtorsOpsTest<armem::ltm::ProviderSegment>().test(); CopyMoveCtorsOpsTest<armem::ltm::CoreSegment>().test(); CopyMoveCtorsOpsTest<armem::ltm::Memory>().test(); - } - { + }*/ + /*{ InstanceCopyMoveCtorsOpsTest<armem::d_ltm::EntityInstance>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::EntitySnapshot>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::Entity>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::ProviderSegment>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::CoreSegment>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::Memory>().test(); - } + }*/ } diff --git a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp b/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp index 38e4d8c88719541e7c4fca92964cfb069c22ba5e..273b1971163e9c5c24b9b38293bdd1af67996197 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp @@ -27,19 +27,18 @@ namespace armarx::armem::gui vlayout->setContentsMargins(margin, margin, margin, margin); _lineEdit = new QLineEdit("/tmp/MemoryExport", this); - _lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - _lineEdit->setMinimumWidth(150); + //_lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); _openFileBrowserButton = new QPushButton("Search files", this); - _storeOnDiskButton = new QPushButton("Store query (Disk)", this); - _storeInLTMButton = new QPushButton("Store query (LTM)", this); + _storeOnDiskButton = new QPushButton("Store", this); + _loadFromDiskButton = new QPushButton("Load", this); hlayout1->addWidget(_lineEdit); hlayout1->addWidget(_openFileBrowserButton); hlayout2->addWidget(_storeOnDiskButton); - hlayout2->addWidget(_storeInLTMButton); + hlayout2->addWidget(_loadFromDiskButton); vlayout->addItem(hlayout1); vlayout->addItem(hlayout2); @@ -49,7 +48,7 @@ namespace armarx::armem::gui // Public connections. connect(_openFileBrowserButton, &QPushButton::pressed, this, &This::openFileBrowser); - connect(_storeInLTMButton, &QPushButton::pressed, this, &This::storeInLTM); + connect(_loadFromDiskButton, &QPushButton::pressed, this, &This::loadFromDisk); connect(_storeOnDiskButton, &QPushButton::pressed, this, &This::storeOnDisk); } @@ -64,24 +63,9 @@ namespace armarx::armem::gui _lineEdit->setText(open); } - QLineEdit* MemoryControlWidget::pathInputBox() - { - return _lineEdit; - } - QString MemoryControlWidget::getEnteredPath() { return _lineEdit->text(); } - - QPushButton* MemoryControlWidget::storeInLTMButton() - { - return _storeInLTMButton; - } - - QPushButton* MemoryControlWidget::storeOnDiskButton() - { - return _storeOnDiskButton; - } } diff --git a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h b/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h index c6b156db313b01180a7f5884959ffc21b0a73f9d..d0c3ef2e8ac22422227e01dd8855350bb86df627 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h +++ b/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h @@ -18,21 +18,15 @@ namespace armarx::armem::gui MemoryControlWidget(); - QLineEdit* pathInputBox(); QString getEnteredPath(); - QPushButton* openFileBrowserButton(); - - QPushButton* storeInLTMButton(); - QPushButton* storeOnDiskButton(); - public slots: void openFileBrowser(); signals: - void storeInLTM(); + void loadFromDisk(); void storeOnDisk(); private slots: @@ -49,6 +43,7 @@ namespace armarx::armem::gui QPushButton* _storeInLTMButton; QPushButton* _storeOnDiskButton; + QPushButton* _loadFromDiskButton; }; diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp index bdb2597e0c7592fc1eb885be667dbcb36d61b095..51a0eba63cd3c9d30134491fd296bfdd8fc10b6a 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp +++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp @@ -3,10 +3,10 @@ #include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem_gui/gui_utils.h> -#include <RobotAPI/libraries/armem/core/diskmemory/Memory.h> +#include <RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h> -#include <RobotAPI/libraries/armem/server/query_proc/diskmemory.h> #include <RobotAPI/libraries/armem/server/query_proc/wm.h> +#include <RobotAPI/libraries/armem/server/query_proc/ltm.h> #include <ArmarXGui/libraries/SimpleConfigDialog/SimpleConfigDialog.h> @@ -84,11 +84,13 @@ namespace armarx::armem::gui //connect(this, &This::connected, this, &This::updateMemory); connect(memoryControlWidget, &armem::gui::MemoryControlWidget::storeOnDisk, this, &This::storeOnDisk); - connect(memoryControlWidget, &armem::gui::MemoryControlWidget::storeInLTM, this, &This::storeInLTM); + connect(memoryControlWidget, &armem::gui::MemoryControlWidget::loadFromDisk, this, &This::loadFromDisk); connect(this, &This::connected, this, &This::updateMemories); connect(updateWidget, &armem::gui::PeriodicUpdateWidget::update, this, &This::updateMemories); + connect(memoryGroup->queryWidget(), &armem::gui::QueryWidget::storeInLTM, this, &This::storeInLTM); + connect(this, &This::memoryDataChanged, this, &This::updateMemoryTree); connect(memoryGroup->tree(), &armem::gui::MemoryTreeWidget::selectedItemChanged, this, &This::updateInstanceTree); @@ -203,9 +205,14 @@ namespace armarx::armem::gui armem::client::QueryInput input = memoryGroup->queryWidget()->queryInput(); armem::client::QueryResult result = reader.query(input); - armem::d_ltm::Memory dMem(name); - dMem.reload(p / name); - dMem.append(result.memory); + // create folder + std::filesystem::create_directories(p / name); + + armem::server::ltm::disk::MemoryManager manager; + manager.setName(name); + manager.setBasePath(p / name); + manager.reload(); + manager.append(result.memory); } } else @@ -216,73 +223,85 @@ namespace armarx::armem::gui TIMING_END_STREAM(MemoryExport, ARMARX_VERBOSE); } - void MemoryViewer::updateMemories() - { - int errorCount = 0; - - memoryData.clear(); - memoryReaders = mns.getAllReaders(true); - - std::filesystem::path path(memoryControlWidget->getEnteredPath().toStdString()); + void MemoryViewer::loadFromDisk() + { armem::client::QueryInput input = memoryGroup->queryWidget()->queryInput(); + QString qs = memoryControlWidget->getEnteredPath(); + std::string utf8_text = qs.toUtf8().constData(); + std::filesystem::path p(utf8_text); + // first check if the local file system should be queried - if (memoryGroup->queryWidget()->alsoQueryLocalDisk()) + if (std::filesystem::is_directory(p)) { - if (std::filesystem::is_directory(path)) + for (const auto& d : std::filesystem::directory_iterator(p)) { - for (const auto& directory : std::filesystem::directory_iterator(path)) + if (d.is_directory()) { - if (directory.is_directory()) - { - std::string k = directory.path().filename(); - armem::d_ltm::Memory dMem(k); - dMem.reload(path / k); + std::string k = d.path().filename(); + armem::server::ltm::disk::MemoryManager manager; + manager.setName(k); + manager.setBasePath(p / k); - input.addQueryTargetToAll(armem::query::data::QueryTarget::LTM); // We use LTM as query target for the disk + manager.reload(); - armem::server::query_proc::d_ltm::MemoryQueryProcessor d_ltm_processor; - dMem = d_ltm_processor.process(input.toIce(), dMem); + input.addQueryTargetToAll(armem::query::data::QueryTarget::LTM); // We use LTM as query target for the disk - wm::Memory converted = dMem.convert(); - memoryData[k] = std::move(converted); - } + armem::server::query_proc::ltm::MemoryQueryProcessor ltm_processor; + armem::wm::Memory query_res = ltm_processor.process(input.toIce(), manager.getCacheAndLutNotConverted()); + + manager.convert(query_res); + memoryData[k] = std::move(query_res); } } - else - { - ARMARX_WARNING << "Could not import a memory from '" << path << "'. Skipping import."; - } } else { - for (auto& [name, reader] : memoryReaders) + ARMARX_WARNING << "Could not import a memory from '" << utf8_text << "'. Skipping import."; + } + } + + + void MemoryViewer::updateMemories() + { + int errorCount = 0; + + memoryData.clear(); + memoryReaders = mns.getAllReaders(true); + + armem::client::QueryInput input = memoryGroup->queryWidget()->queryInput(); + + for (auto& [name, reader] : memoryReaders) + { + TIMING_START(MemoryQuery); { - TIMING_START(MemoryQuery); + armem::client::QueryResult result = reader.query(input); + if (result.success) { - armem::client::QueryResult result = reader.query(input); - if (result.success) - { - memoryData[name] = result.memory; - } - else - { - ARMARX_INFO << "A query for memory '" << name << "' produced an error: " << result.errorMessage; - errorCount++; - } + memoryData[name] = result.memory; } - TIMING_END_STREAM(MemoryQuery, ARMARX_VERBOSE); - - if (debugObserver) + else { - debugObserver->setDebugDatafield(Logging::tag.tagName, "Memory Query [ms]", new Variant(MemoryQuery.toMilliSecondsDouble())); + ARMARX_WARNING << "A query for memory '" << name << "' produced an error: " << result.errorMessage; + errorCount++; } } + TIMING_END_STREAM(MemoryQuery, ARMARX_VERBOSE); + + if (debugObserver) + { + debugObserver->setDebugDatafield(Logging::tag.tagName, "Memory Query [ms]", new Variant(MemoryQuery.toMilliSecondsDouble())); + } } emit memoryDataChanged(); + updateStatusLabel(errorCount); + } + + void MemoryViewer::updateStatusLabel(int errorCount) + { // Code to output status label information if (statusLabel and errorCount > 0) { diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.h b/source/RobotAPI/libraries/armem_gui/MemoryViewer.h index 71870bdd565b0b8a585808f5f0e9f0f43c93d315..2153efb73244d4e92d1312a62f36f21db816fe35 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.h +++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.h @@ -70,9 +70,12 @@ namespace armarx::armem::gui void resolveMemoryID(const MemoryID& id); - // LTMControlWidget - void storeInLTM(); + // ControlWidget void storeOnDisk(); + void loadFromDisk(); + + void storeInLTM(); + signals: @@ -101,6 +104,7 @@ namespace armarx::armem::gui void onDisconnect(ManagedIceObject& component); const armem::wm::Memory* getSingleMemoryData(const std::string& memoryName); + void updateStatusLabel(int errorCount); public: diff --git a/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h b/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h index 72987cda1ef736a9f8943161cb8756a6e56b2cdc..d44867d246cb2e53d9fc2a6835482d0993f6430f 100644 --- a/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h +++ b/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h @@ -5,7 +5,6 @@ #include <QTreeWidget> #include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> #include <RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h> diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp index dbb3d3fcabfa2ccf0034d1ba72027664bebe6ebc..f549a9e86046f40feb4087d28acd48726293a5c7 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp @@ -1,5 +1,8 @@ #include "QueryWidget.h" +#include <QWidget> +#include <QTabWidget> +#include <QPushButton> #include <QCheckBox> #include <QHBoxLayout> #include <QVBoxLayout> @@ -12,25 +15,35 @@ namespace armarx::armem::gui QueryWidget::QueryWidget() { - QVBoxLayout* layout = new QVBoxLayout(); - setLayout(layout); + QHBoxLayout* hlayout1 = new QHBoxLayout(); + QHBoxLayout* hlayout2 = new QHBoxLayout(); + QVBoxLayout* vlayout = new QVBoxLayout(); _dataCheckBox = new QCheckBox("Get Data"); _dataCheckBox->setChecked(true); + _storeInLTMButton = new QPushButton("Store query result in LTM"); + _tabWidget = new QTabWidget(); _snapshotSelectorWidget = new SnapshotSelectorWidget(); _tabWidget->addTab(_snapshotSelectorWidget, QString("Snapshots")); - layout->addWidget(_dataCheckBox); - layout->addWidget(_tabWidget); + hlayout1->addWidget(_dataCheckBox); + hlayout1->addWidget(_storeInLTMButton); + + hlayout2->addWidget(_tabWidget); const int margin = 0; - layout->setContentsMargins(margin, margin, margin, margin); + vlayout->setContentsMargins(margin, margin, margin, margin); + + vlayout->addLayout(hlayout1); + vlayout->addLayout(hlayout2); + // Public connections. + connect(_storeInLTMButton, &QPushButton::pressed, this, &This::storeInLTM); - // connect to queryChanged + setLayout(vlayout); } @@ -41,11 +54,6 @@ namespace armarx::armem::gui : armem::DataMode::NoData; } - bool QueryWidget::alsoQueryLocalDisk() const - { - return _snapshotSelectorWidget->queryTargetContainsLocalDisk(); - } - armem::client::QueryInput QueryWidget::queryInput() { armem::client::query::Builder qb(dataMode()); diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h index d2c3fecd6198ad2ad5c3f37077eb07f4f365dea9..2bc7f223d5db52435b7571970bb4cd398aaeec49 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h @@ -1,13 +1,13 @@ #pragma once -#include <QWidget> -#include <QTabWidget> - #include <RobotAPI/libraries/armem/core/DataMode.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include "SnapshotSelectorWidget.h" +class QWidget; +class QTabWidget; +class QPushButton; namespace armarx::armem::gui { @@ -15,19 +15,21 @@ namespace armarx::armem::gui class QueryWidget : public QWidget { Q_OBJECT + using This = QueryWidget; + public: QueryWidget(); armem::DataMode dataMode() const; - bool alsoQueryLocalDisk() const; armem::client::QueryInput queryInput(); public slots: signals: + void storeInLTM(); // ToDo: // void queryChanged(armem::query::data::Input query); @@ -42,6 +44,8 @@ namespace armarx::armem::gui private: QCheckBox* _dataCheckBox; + QPushButton* _storeInLTMButton; + QTabWidget* _tabWidget; SnapshotSelectorWidget* _snapshotSelectorWidget; diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp index 9d5cc9ea6064bb6977892623649518791dcaffec..6b38efdddb30e67d1abfb3190d88508dff72258a 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp @@ -29,27 +29,14 @@ namespace armarx::armem::gui return targets; } - void SnapshotSelectorWidget::localDiskStateChanged() + void SnapshotSelectorWidget::queryTargetStateChanged() { - if (_LocalDiskMemoryQueryTargetCheckBox->isChecked()) + if (!_WMQueryTargetCheckBox->isChecked() && !_LTMQueryTargetCheckBox->isChecked()) { - _WMQueryTargetCheckBox->setChecked(false); - _LTMQueryTargetCheckBox->setChecked(false); - _WMQueryTargetCheckBox->setEnabled(false); - _LTMQueryTargetCheckBox->setEnabled(false); - } - else - { - _WMQueryTargetCheckBox->setEnabled(true); - _LTMQueryTargetCheckBox->setEnabled(true); + _WMQueryTargetCheckBox->setChecked(true); } } - bool SnapshotSelectorWidget::queryTargetContainsLocalDisk() const - { - return _LocalDiskMemoryQueryTargetCheckBox->isChecked(); - } - SnapshotSelectorWidget::SnapshotSelectorWidget() { _pageLayout = new QVBoxLayout(); @@ -72,13 +59,12 @@ namespace armarx::armem::gui auto queryTargetLayout = new QHBoxLayout(); _WMQueryTargetCheckBox = new QCheckBox("WM"); _LTMQueryTargetCheckBox = new QCheckBox("LTM"); - _LocalDiskMemoryQueryTargetCheckBox = new QCheckBox("Local Disk"); - connect(_LocalDiskMemoryQueryTargetCheckBox, &QCheckBox::stateChanged, this, &This::localDiskStateChanged); + connect(_WMQueryTargetCheckBox, &QCheckBox::stateChanged, this, &This::queryTargetStateChanged); + connect(_LTMQueryTargetCheckBox, &QCheckBox::stateChanged, this, &This::queryTargetStateChanged); queryTargetLayout->addWidget(_WMQueryTargetCheckBox); queryTargetLayout->addWidget(_LTMQueryTargetCheckBox); - queryTargetLayout->addWidget(_LocalDiskMemoryQueryTargetCheckBox); _WMQueryTargetCheckBox->setChecked(true); diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h index a0b69eb1b6f362e64880ab400c0bea29bcdc73fc..e7ec233c896133beb1b675baf37e2d0e2f53489c 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h @@ -37,7 +37,6 @@ namespace armarx::armem::gui armem::DataMode dataMode() const; client::query::SnapshotSelector selector(); query::data::QueryTargets queryTargets() const; - bool queryTargetContainsLocalDisk() const; public slots: @@ -51,7 +50,7 @@ namespace armarx::armem::gui void hideAllForms(); void showSelectedFormForQuery(QString selected); - void localDiskStateChanged(); + void queryTargetStateChanged(); signals: void queryOutdated(); @@ -67,7 +66,6 @@ namespace armarx::armem::gui QComboBox* _queryComboBox; QCheckBox* _WMQueryTargetCheckBox; QCheckBox* _LTMQueryTargetCheckBox; - QCheckBox* _LocalDiskMemoryQueryTargetCheckBox; /// The forms for the different query types. Hidden when not selected. std::map<QString, SnapshotForm*> _queryForms; diff --git a/source/RobotAPI/libraries/aron/converter/CMakeLists.txt b/source/RobotAPI/libraries/aron/converter/CMakeLists.txt index 5f8db702e460cbb2e729862458314920823be780..e93d191b2f99b0eccb973a925a77ebdf674ffaee 100644 --- a/source/RobotAPI/libraries/aron/converter/CMakeLists.txt +++ b/source/RobotAPI/libraries/aron/converter/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(ivt) add_subdirectory(pcl) add_subdirectory(eigen) add_subdirectory(opencv) +add_subdirectory(json) add_library(AronConverter INTERFACE) @@ -13,7 +14,8 @@ target_link_libraries(AronConverter RobotAPI::aron::converter::ivt RobotAPI::aron::converter::pcl RobotAPI::aron::converter::eigen - RobotAPI::aron::converter::opencv + RobotAPI::aron::converter::opencv + RobotAPI::aron::converter::json ) add_library(aron::converter ALIAS AronConverter) diff --git a/source/RobotAPI/libraries/aron/converter/json/CMakeLists.txt b/source/RobotAPI/libraries/aron/converter/json/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8f5c8006f3b16d7343a2f74b1c6b6562de426165 --- /dev/null +++ b/source/RobotAPI/libraries/aron/converter/json/CMakeLists.txt @@ -0,0 +1,24 @@ +set(LIB_NAME aronjsonconverter) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +find_package(IVT COMPONENTS ivt ivtopencv QUIET) +armarx_build_if(IVT_FOUND "IVT not available") + +set(LIBS + aron +) + +set(LIB_FILES + NLohmannJSONConverter.cpp +) + +set(LIB_HEADERS + NLohmannJSONConverter.h +) + +armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") + + +add_library(RobotAPI::aron::converter::json ALIAS aronjsonconverter) diff --git a/source/RobotAPI/libraries/armem/core/wm/json_conversions.cpp b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.cpp similarity index 50% rename from source/RobotAPI/libraries/armem/core/wm/json_conversions.cpp rename to source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.cpp index fd67c5f822cbfa3783bf1fc23b3de24f06a27f88..3eb6f82327e13ed89629e5de76a41909a9f0f9e2 100644 --- a/source/RobotAPI/libraries/armem/core/wm/json_conversions.cpp +++ b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.cpp @@ -1,4 +1,4 @@ -#include "json_conversions.h" +#include "NLohmannJSONConverter.h" #include <RobotAPI/libraries/aron/core/Debug.h> #include <RobotAPI/libraries/aron/core/io/dataIO/visitor/Visitor.h> @@ -7,17 +7,32 @@ #include <RobotAPI/libraries/aron/core/io/dataIO/writer/nlohmannJSON/NlohmannJSONWriter.h> -namespace armarx::armem +namespace armarx::aron::converter { - void from_aron(const aron::datanavigator::DictNavigatorPtr& aron, nlohmann::json& j) + nlohmann::json AronNlohmannJSONConverter::ConvertToNlohmannJSON(const datanavigator::DictNavigatorPtr& aron) + { + nlohmann::json j; + ConvertToNlohmannJSON(aron, j); + return j; + } + + void AronNlohmannJSONConverter::ConvertToNlohmannJSON(const aron::datanavigator::DictNavigatorPtr& aron, nlohmann::json& j) { aron::dataIO::writer::NlohmannJSONWriter dataWriter; aron::dataIO::Visitor::VisitAndSetup(dataWriter, aron); j = dataWriter.getResult(); } - void to_aron(aron::datanavigator::DictNavigatorPtr& a, const nlohmann::json& e, - const aron::typenavigator::NavigatorPtr& expectedStructure) + + + datanavigator::DictNavigatorPtr AronNlohmannJSONConverter::ConvertFromNlohmannJSON(const nlohmann::json& j) + { + auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); + ConvertFromNlohmannJSON(aron, j); + return aron; + } + + void AronNlohmannJSONConverter::ConvertFromNlohmannJSON(aron::datanavigator::DictNavigatorPtr& a, const nlohmann::json& e, const aron::typenavigator::NavigatorPtr& expectedStructure) { aron::dataIO::reader::NlohmannJSONReader dataReader(e); aron::dataIO::writer::NavigatorWriter navWriter; diff --git a/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..2e1a89c63511d9806951887a7461fa09dcb17da4 --- /dev/null +++ b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h @@ -0,0 +1,29 @@ +#pragma once + +// STD/STL +#include <memory> +#include <string> +#include <numeric> + +// Memory +#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> + +// JSON +#include <SimoxUtility/json/json.hpp> + +namespace armarx::aron::converter +{ + class AronNlohmannJSONConverter + { + + public: + AronNlohmannJSONConverter() = delete; + + static nlohmann::json ConvertToNlohmannJSON(const datanavigator::DictNavigatorPtr&); + static void ConvertToNlohmannJSON(const datanavigator::DictNavigatorPtr&, nlohmann::json&); + + static datanavigator::DictNavigatorPtr ConvertFromNlohmannJSON(const nlohmann::json&); + static void ConvertFromNlohmannJSON(datanavigator::DictNavigatorPtr&, const nlohmann::json&, const aron::typenavigator::NavigatorPtr& = nullptr); + }; +}