diff --git a/source/RobotAPI/components/armem/client/ExampleMemoryClient/CMakeLists.txt b/source/RobotAPI/components/armem/client/ExampleMemoryClient/CMakeLists.txt index 3bd038480506fbd454a10111ca64a9f4c6ef1575..667e5d5ad070e08f331d505fa4e742ce61b39df7 100644 --- a/source/RobotAPI/components/armem/client/ExampleMemoryClient/CMakeLists.txt +++ b/source/RobotAPI/components/armem/client/ExampleMemoryClient/CMakeLists.txt @@ -7,7 +7,7 @@ armarx_build_if(OpenCV_FOUND "OpenCV not available") set(COMPONENT_LIBS ArmarXCore ArmarXCoreInterfaces # for DebugObserverInterface ArmarXGuiComponentPlugins - RobotAPICore RobotAPIInterfaces armem aronopencvconverter + RobotAPICore RobotAPIInterfaces armem aronopencvconverter aronjsonconverter ${OpenCV_LIBRARIES} ) diff --git a/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp b/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp index 8e5ee8dfa8914d9792152d4fe082d49c55bc7133..eae52c5054369e599acebfa5378075b7a448d6e5 100644 --- a/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp +++ b/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp @@ -60,6 +60,7 @@ #include <opencv2/imgproc.hpp> #include <RobotAPI/libraries/aron/converter/opencv/OpenCVConverter.h> +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> namespace armarx { @@ -175,14 +176,9 @@ namespace armarx } CycleUtil c(static_cast<int>(1000 / p.commitFrequency)); - int i = 0; while (!task->isStopped()) { - commitExampleData(); - if (i++ < 10000) - { - commitSingleSnapshot(exampleEntityID); - } + commitSingleSnapshot(exampleEntityID); c.waitForCycleDuration(); } } @@ -367,7 +363,7 @@ namespace armarx const armem::Time time = armem::Time::now(); armem::Commit commit; - //default + //commit to default { armem::EntityUpdate& update = commit.add(); update.entityID = exampleDataProviderID.withEntityName("default"); @@ -375,9 +371,12 @@ namespace armarx armem::example::ExampleData data; toAron(data.memoryLink, armem::MemoryID()); + ARMARX_CHECK_NOT_NULL(data.toAron()); update.instancesData = { data.toAron() }; } - //the answer + + + //commit to the answer { armem::EntityUpdate& update = commit.add(); update.entityID = exampleDataProviderID.withEntityName("the answer"); @@ -415,7 +414,7 @@ namespace armarx data.the_3x1_vector = { 24, 42, 2442 }; data.the_4x4_matrix = 42 * Eigen::Matrix4f::Identity(); - toAron(data.memoryLink, armem::MemoryID()); + toAron(data.memoryLink, armem::MemoryID()); // ////1/1 simox::ColorMap cmap = simox::color::cmaps::plasma(); { @@ -433,7 +432,7 @@ namespace armarx //cv::Mat out; //cv::cvtColor(image, out, /*CV_RGB2BGR*/); - cv::imwrite("the_rgb24_image.png", image); // out + cv::imwrite("/tmp/the_rgb24_image.png", image); // out } { cv::Mat& image = data.the_depth32_image; @@ -456,9 +455,10 @@ namespace armarx //cv::Mat out; //cv::cvtColor(rgb, out, CV_RGB2BGR); - cv::imwrite("the_depth32_image.png", image); // out + cv::imwrite("/tmp/the_depth32_image.png", image); // out } + ARMARX_CHECK_NOT_NULL(data.toAron()); update.instancesData = { data.toAron() }; } diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index cf8ce72dfd79c7148548940fc954b4ea10ad1fda..c589f0b3aca9247fbd5fe232a78b54d752d94e13 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -83,10 +83,12 @@ set(LIB_FILES server/MemoryRemoteGui.cpp server/RemoteGuiAronDataVisitor.cpp + server/ltm/operations.cpp server/ltm/LongtermMemoryBase.cpp server/ltm/disk/operations.cpp server/ltm/disk/MemoryManager.cpp server/ltm/mongodb/MemoryManager.cpp + server/ltm/mongodb/operations.cpp server/ltm/mongodb/ConnectionManager.cpp server/wm/memory_definitions.cpp @@ -196,10 +198,13 @@ set(LIB_HEADERS server/MemoryRemoteGui.h server/RemoteGuiAronDataVisitor.h + + server/ltm/operations.h server/ltm/LongtermMemoryBase.h server/ltm/disk/operations.h server/ltm/disk/MemoryManager.h server/ltm/mongodb/MemoryManager.h + server/ltm/mongodb/operations.h server/ltm/mongodb/ConnectionManager.h server/wm/memory_definitions.h diff --git a/source/RobotAPI/libraries/armem/core/ice_conversions.cpp b/source/RobotAPI/libraries/armem/core/ice_conversions.cpp index 8a3597808f8c01a7f5d1ffad54a171c00109736e..f019f3d78a9ed3a38116ee86a11ef42ef942e9d4 100644 --- a/source/RobotAPI/libraries/armem/core/ice_conversions.cpp +++ b/source/RobotAPI/libraries/armem/core/ice_conversions.cpp @@ -36,7 +36,6 @@ namespace armarx id.instanceIndex = ice.instanceIndex; } - void armem::fromIce(const data::Commit& ice, Commit& commit) { commit.updates.clear(); @@ -57,7 +56,6 @@ namespace armarx } } - void armem::fromIce(const data::CommitResult& ice, CommitResult& result) { result.results.clear(); @@ -78,7 +76,6 @@ namespace armarx } } - void armem::fromIce(const data::EntityUpdate& ice, EntityUpdate& update) { fromIce(ice.entityID, update.entityID); @@ -109,7 +106,6 @@ namespace armarx ice.timeSentMicroSeconds = update.timeSent.toMicroSeconds(); } - void armem::fromIce(const data::EntityUpdateResult& ice, EntityUpdateResult& result) { result.success = ice.success; @@ -126,7 +122,6 @@ namespace armarx ice.errorMessage = result.errorMessage; } - void armem::fromIce(const data::Commit& ice, Commit& commit, Time timeArrived) { commit.updates.clear(); @@ -137,7 +132,6 @@ namespace armarx } } - void armem::fromIce(const data::EntityUpdate& ice, EntityUpdate& update, Time timeArrived) { armem::fromIce(ice, update); diff --git a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp index 73d00c3a370fd14e40771df77d0d96cc6b9dbf42..625d76b967e17439f1d82ca6f4e8ebacd108b8a2 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, server::ltm::mongodb::MemoryManager* longtermMemory) : - workingMemory(workingMemory), longtermMemoryManager(longtermMemory) + MemoryToIceAdapter::MemoryToIceAdapter(wm::Memory* workingMemory, server::ltm::disk::Memory* longtermMemory) : + workingMemory(workingMemory), longtermMemory(longtermMemory) { } @@ -188,7 +188,7 @@ namespace armarx::armem::server // also store in ltm if transfermode is set to always // TODO: Move outside of loop? - if (longtermMemoryManager) + if (longtermMemory) { } @@ -243,15 +243,18 @@ namespace armarx::armem::server input.withData ? armem::DataMode::WithData : armem::DataMode::NoData); armem::wm::Memory wmResult = wmServerProcessor.process(input.memoryQueries, *workingMemory); + armem::wm::Memory cacheAndLut = longtermMemory->getCache(); + cacheAndLut.append(longtermMemory->getLUT()); + query_proc::ltm::MemoryQueryProcessor ltmProcessor; - armem::wm::Memory ltmResult = ltmProcessor.process(input, longtermMemoryManager->getCacheAndLutNotConverted()); + armem::wm::Memory ltmResult = ltmProcessor.process(input, cacheAndLut); armem::query::data::Result result; if (not ltmResult.empty()) { ARMARX_INFO << "The LTM returned data after query"; - longtermMemoryManager->convert(ltmResult); // convert memory ==> meaning resolving lut references to e.g. mongodb + longtermMemory->convert(ltmResult); // convert memory ==> meaning resolving lut references to e.g. mongodb wmResult.append(ltmResult); if (wmResult.empty()) @@ -309,7 +312,7 @@ namespace armarx::armem::server { ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(workingMemory); - ARMARX_CHECK_NOT_NULL(longtermMemoryManager); + ARMARX_CHECK_NOT_NULL(longtermMemory); data::StoreResult output; for (const auto& query : input.query.memoryQueries) @@ -326,7 +329,7 @@ namespace armarx::armem::server { armem::wm::Memory m; fromIce(queryResult.memory, m); - longtermMemoryManager->append(m); + longtermMemory->append(m); } return output; diff --git a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h index f924399c7eb749f8404f28c1c2a9bd7945112a7c..7e45ab6f1a67795697a5ceafb3c3c4a3d063a5b2 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/server/ltm/mongodb/MemoryManager.h> +#include <RobotAPI/libraries/armem/server/ltm/disk/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, - server::ltm::mongodb::MemoryManager* longtermMemory = nullptr); + server::ltm::disk::Memory* longtermMemory = nullptr); void setMemoryListener(client::MemoryListenerInterfacePrx memoryListenerTopic); @@ -55,7 +55,7 @@ namespace armarx::armem::server public: server::wm::Memory* workingMemory; - server::ltm::mongodb::MemoryManager* longtermMemoryManager; + server::ltm::disk::Memory* longtermMemory; client::MemoryListenerInterfacePrx memoryListenerTopic; diff --git a/source/RobotAPI/libraries/armem/server/forward_declarations.h b/source/RobotAPI/libraries/armem/server/forward_declarations.h index d65ec0f36b9f60e680c6e720d80cd9cc3d6e789f..956a92b96ea8a75f7526fc5d111b0cc63bec26b8 100644 --- a/source/RobotAPI/libraries/armem/server/forward_declarations.h +++ b/source/RobotAPI/libraries/armem/server/forward_declarations.h @@ -18,5 +18,9 @@ namespace armarx::armem::server::wm } namespace armarx::armem::server::ltm::mongodb { - class MemoryManager; + class Memory; +} +namespace armarx::armem::server::ltm::disk +{ + class Memory; } diff --git a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp index 53e6f0169ab5ccb459ff730d67651836e78d5ec1..0eff25718d5c33653bc32439e2ed1638843b7a80 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp @@ -8,73 +8,83 @@ namespace armarx::armem::server::ltm { - void - LongtermMemoryBase::setName(const std::string& name) + + MemoryBase::MemoryBase(const std::string& s) + { + std::lock_guard l(ltm_mutex); + setName(s); + } + + void MemoryBase::setName(const std::string& name) { + std::lock_guard l(ltm_mutex); cache.name() = name; lut.name() = name; } - armem::wm::Memory - LongtermMemoryBase::getCacheAndLutNotConverted() const + armem::wm::Memory MemoryBase::getCache() 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(); - //}); + std::lock_guard l(ltm_mutex); + return cache; + } - return m; + armem::wm::Memory MemoryBase::getLUT() const + { + std::lock_guard l(ltm_mutex); + return lut; } + armem::wm::Memory MemoryBase::convert() + { + std::lock_guard l(ltm_mutex); + armem::wm::Memory tmp = cache; + tmp.append(lut); + convert(tmp); + return tmp; + } template <class ...Args> - void LongtermMemoryBase::_append(const armem::base::MemoryBase<Args...>& memory) + void MemoryBase::_append(const armem::base::MemoryBase<Args...>& memory) { TIMING_START(LTM_Append); - ARMARX_INFO << "Append memory with name '" << memory.name() << "' into the LTM with name '" - << cache.name() << "'"; + ARMARX_INFO << "Append memory with name '" << memory.name() << "' into the cache of the LTM with name '" << cache.name() << "'"; - std::lock_guard l(cache_mutex); + std::lock_guard l(ltm_mutex); cache.append(memory); - encodeAndStore(); - - TIMING_END(LTM_Append); + TIMING_END_STREAM(LTM_Append, ARMARX_DEBUG); } - void - LongtermMemoryBase::append(const armem::wm::Memory& memory) + void MemoryBase::append(const armem::wm::Memory& memory) { this->_append(memory); } - void - LongtermMemoryBase::append(const armem::server::wm::Memory& memory) + void MemoryBase::append(const armem::server::wm::Memory& memory) { this->_append(memory); } + void MemoryBase::moveCacheToLUTAndClearCache() + { + cache.forEachInstance([](armem::wm::EntityInstance & i) + { + i.data() = nullptr; + }); + + lut.append(cache); + cache.clear(); + } - void - LongtermMemoryBase::checkUpdateLatestSnapshot(const armem::wm::EntitySnapshot& newSnapshot) + void MemoryBase::checkUpdateLatestSnapshot(const armem::wm::EntitySnapshot& newSnapshot) { - // update map of latestSnapshots - if (auto it = latestSnapshots.find(newSnapshot.id().getEntityID().str()); - it != latestSnapshots.end()) + if (auto it = latestSnapshots.find(newSnapshot.id().getEntityID()); it != latestSnapshots.end()) { - auto ptr = it->second; - if (ptr->id().timestamp > newSnapshot.id().timestamp) + auto& ptr = it->second; + if (ptr->id().timestamp < newSnapshot.id().timestamp) { ptr = &newSnapshot; } @@ -87,80 +97,4 @@ namespace armarx::armem::server::ltm } } - - 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; - } - } // namespace armarx::armem::server::ltm diff --git a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h index 463c4c37998bd95ce4ec81f2e92eb0c937e2dc7c..0514eec76a14ae2e566579fa2f9e251a6ae69b52 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h +++ b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h @@ -13,56 +13,51 @@ namespace armarx::armem::server::ltm { /// @brief Interface functions for the longterm memory classes - class LongtermMemoryBase + class MemoryBase { 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 - { - }; + MemoryBase() = default; + MemoryBase(const std::string&); void append(const armem::wm::Memory& memory); void append(const armem::server::wm::Memory& memory); + /// reload the lut content from the long-term memory virtual void reload() = 0; + + /// convert the references of the input into a wm::Memory virtual void convert(armem::wm::Memory&) = 0; + + /// convert the references of the cache and the LUT into a wm::Memory + armem::wm::Memory convert(); + + /// encode the content in the cache and store it in the long-term memory virtual void encodeAndStore() = 0; - // pass through to internal memory + /// set the name of this memory void setName(const std::string& name); - // get merged internal memory - armem::wm::Memory getCacheAndLutNotConverted() const; + armem::wm::Memory getCache() const; + armem::wm::Memory getLUT() const; protected: + void moveCacheToLUTAndClearCache(); 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: + mutable std::recursive_mutex ltm_mutex; + /// Internal memory for data consolidated from wm to ltm (cache) + /// The to-put-to-ltm cache (contains data in plain text) armem::wm::Memory cache; - mutable std::recursive_mutex cache_mutex; /// Internal memory for indexes (lut) + /// It contains indexes to all memory entries (TODO!) 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; + /// A map from entityID to its latest snapshot stored. + /// When adding a new snapshot we compare it to the last one stored without querying the long-term memory + std::map<MemoryID, const armem::wm::EntitySnapshot*> latestSnapshots; private: diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp index 93f6169b0cf55f3139b6852deddc9d257879372a..f503dc066e9b3a899b67072025dce4256296ce28 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp @@ -1,13 +1,6 @@ // Header #include "MemoryManager.h" -// STD / STL -#include <iostream> -#include <fstream> - -// Simox -#include <SimoxUtility/json.h> - // ArmarX #include <ArmarXCore/core/time/TimeUtil.h> #include <ArmarXCore/core/logging/Logging.h> @@ -15,34 +8,50 @@ #include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> #include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> -namespace +#include "operations.h" + +namespace armarx::armem::server::ltm::disk { - // check whether a string is a number (timestamp folders) - bool isNumber(const std::string& s) + Memory::Memory() : + MemoryBase() { - for (char const& ch : s) + std::string armarx_home = std::string(getenv("HOME")) + "/.armarx"; + if (getenv("ARMARX_DEFAULTS_DIR")) { - if (std::isdigit(ch) == 0) - { - return false; - } + armarx_home = getenv("ARMARX_DEFAULTS_DIR"); } - return true; + memoryPathString = armarx_home + "/diskmemory/data/db"; } -} -namespace armarx::armem::server::ltm::disk -{ - namespace fs = std::filesystem; + Memory::Memory(const std::string& s) : + MemoryBase(s) + { + std::string armarx_home = std::string(getenv("HOME")) + "/.armarx"; + if (getenv("ARMARX_DEFAULTS_DIR")) + { + armarx_home = getenv("ARMARX_DEFAULTS_DIR"); + } + memoryPathString = armarx_home + "/diskmemory/data/db"; + } + + Memory::Memory(const std::filesystem::path& p) : + MemoryBase(p.filename()), + memoryPath(p) + { + } - bool MemoryManager::checkPath() const + bool Memory::checkPath() const { // Check connection: - ARMARX_INFO << "Checking Path"; - if (!fs::exists(basePathToMemory) || !fs::is_directory(basePathToMemory) || basePathToMemory.filename() != lut.name()) + if (!std::filesystem::exists(memoryPath)) { - ARMARX_WARNING << deactivateSpam("PathIsNotValid") - << "The entered path is not valid. Please use a path leading to a memory folder with name: " << lut.name() << "." + std::filesystem::create_directories(memoryPath); + return true; + } + else if (!std::filesystem::is_directory(memoryPath) || memoryPath.filename() != cache.name()) + { + ARMARX_WARNING << deactivateSpam("LTM_PathError_" + cache.name()) + << "The entered path is not valid. Please use a path leading to a memory folder with name: " << cache.name() << "." << "\n\n"; return false; } @@ -50,353 +59,61 @@ namespace armarx::armem::server::ltm::disk return true; } - void MemoryManager::reload() + void Memory::reload() { + memoryPath = std::filesystem::path(memoryPathString); + 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."); - } + std::lock_guard l(ltm_mutex); + armem::wm::Memory temp(lut.id()); + util::load(memoryPath, temp); - // /////////////////////////////// - // 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); + TIMING_END_STREAM(LTM_Reload, ARMARX_DEBUG); } - void MemoryManager::convert(armem::wm::Memory& m) + void Memory::convert(armem::wm::Memory& m) { + memoryPath = std::filesystem::path(memoryPathString); + 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; - }); + std::lock_guard l(ltm_mutex); + util::convert(memoryPath, m); - 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::ConvertFromNlohmannJSONObject(doc); - - wm::EntityInstance tmp(e.id().withInstanceIndex(i)); - from_aron(aron, tmp); - - ins.data() = tmp.data(); - } - } - } - // else leave snapshot untouched - } - }); - }); - }); - }); - TIMING_END(LTM_Convert); + TIMING_END_STREAM(LTM_Convert, ARMARX_DEBUG); } - void MemoryManager::encodeAndStore() + void Memory::encodeAndStore() { + memoryPath = std::filesystem::path(memoryPathString); + 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::data::Dict>(); - to_aron(aron, e); - nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(aron); - - ofs << j.dump(2); - ofs.close(); - }); - }); - }); - }); - - }); + std::lock_guard l(ltm_mutex); + util::store(memoryPath, cache); // 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(); + moveCacheToLUTAndClearCache(); - TIMING_END(LTM_Encode); + TIMING_END_STREAM(LTM_Encode, ARMARX_DEBUG); } } diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h index e9eeffb05667a7e4feeb9c638abea41be02201cd..66a6a3e425224bd261a580af126ee798113479d5 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h @@ -10,33 +10,30 @@ 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 + class Memory : public MemoryBase { - using Base = LongtermMemoryBase; + using Base = MemoryBase; public: - MemoryManager() = default; + using Base::convert; + + Memory(); + Memory(const std::string&); + Memory(const std::filesystem::path&); 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; + std::string memoryPathString; private: - static const constexpr char* TYPE_FILENAME = "type.aron.ltm.json"; - static const constexpr char* DATA_FILENAME = "data.aron.ltm.json"; + std::filesystem::path memoryPath; + }; } // namespace armarx::armem::server::ltm::disk diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/operations.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/operations.cpp index 83d2b379d85225e6e369e8711c04ef068e5016e0..34c6757621a7465af8da4c103aed15940aeb8cd6 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/operations.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/operations.cpp @@ -1,76 +1,281 @@ #include "operations.h" -#include <RobotAPI/libraries/armem/client/query/Builder.h> -#include <RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h> -#include <RobotAPI/libraries/armem/server/query_proc/ltm.h> -#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> +// STD / STL +#include <iostream> +#include <fstream> +// Simox +#include <SimoxUtility/json.h> -namespace armarx::armem::server::ltm +#include "../operations.h" +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> +#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> + +namespace armarx::armem::server::ltm::disk::util { - armem::wm::Memory - disk::load(const std::filesystem::path& directory) + namespace fs = std::filesystem; + + 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; + } + + void ensureFolderExists(const fs::path& p) + { + if (!fs::exists(p)) + { + // not found + fs::create_directory(p); + } + if (!fs::is_directory(p)) + { + throw error::ArMemError("Could not create the folder: " + p.string()); + } + } + } + + void load(const std::filesystem::path& directory, armem::wm::Memory& m) + { + for (const auto& d : std::filesystem::directory_iterator(directory)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (m.hasCoreSegment(k)) + { + throw error::ArMemError("Somehow the (memory) container already contains the key k = " + k + ". This should not happen."); + } + auto& cSeg = m.addCoreSegment(k); + load((directory / k), cSeg); + } + } + + void load(const std::filesystem::path& directory, armem::wm::CoreSegment& c) { - const std::string key = directory.filename(); - armem::server::ltm::disk::MemoryManager manager; - manager.setName(key); - manager.setBasePath(directory); - manager.reload(); - - armem::client::QueryBuilder builder; - builder.all(); - armem::client::QueryInput queryInput = builder.buildQueryInput(); - queryInput.addQueryTargetToAll(armem::query::data::QueryTarget::LTM); - - armem::server::query_proc::ltm::MemoryQueryProcessor processor; - armem::wm::Memory memory = - processor.process(queryInput.toIce(), manager.getCacheAndLutNotConverted()); - manager.convert(memory); - - return memory; + for (const auto& d : std::filesystem::directory_iterator(directory)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (c.hasProviderSegment(k)) + { + throw error::ArMemError("Somehow the (core) container already contains the key k = " + k + ". This should not happen."); + } + auto& pSeg = c.addProviderSegment(k); + load((directory / k), pSeg); + } } + void load(const std::filesystem::path& directory, armem::wm::ProviderSegment& p) + { + for (const auto& d : std::filesystem::directory_iterator(directory)) + { + if (!d.is_directory()) + { + continue; + } - namespace detail + std::string k = d.path().filename(); + if (p.hasEntity(k)) + { + throw error::ArMemError("Somehow the (provider) container already contains the key k = " + k + ". This should not happen."); + } + auto& eSeg = p.addEntity(k); + load((directory / k), eSeg); + } + } + + void load(const std::filesystem::path& directory, armem::wm::Entity& e) { - template <class MemoryT> - void - store(const std::filesystem::path& directory, const MemoryT& memory) + for (const auto& d : std::filesystem::directory_iterator(directory)) { - const std::string& name = memory.name(); - if (std::filesystem::is_regular_file(directory / name)) + 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 (e.hasSnapshot(ts)) { - std::stringstream ss; - ss << "Could not export memory '" << name << "' to " << directory << ": " - << "Cannot overwrite existing file.\n"; - throw error::IOError(directory, ss.str()); + throw error::ArMemError("Somehow the (entity) container already contains the key k = " + k + ". This should not happen."); } - else + + auto& sSeg = e.addSnapshot(ts); + for (unsigned int i = 0; i < 10000; ++i) // ugly workaround to get the folders in the correct order { - std::filesystem::create_directories(directory / name); + fs::path p = d / std::to_string(i); + if (!fs::exists(p) || !fs::is_directory(p)) + { + // early stopping + break; + } + + fs::path data = p / constantes::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>())); - armem::server::ltm::disk::MemoryManager manager; - manager.setName(name); - manager.setBasePath(directory / name); - manager.reload(); - manager.append(memory); + nlohmann::json j = nlohmann::json::parse(file_content); + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSONObject(j); + + auto& instance = sSeg.addInstance(); + from_aron(aron, instance); } } - } // namespace detail + } + + void convert(const std::filesystem::path& directory, armem::wm::Memory& m) + { + m.forEachCoreSegment([&directory](armem::wm::CoreSegment & e) + { + convert((directory / e.id().coreSegmentName), e); + }); + } + + void convert(const std::filesystem::path& directory, armem::wm::CoreSegment& c) + { + c.forEachProviderSegment([&directory](armem::wm::ProviderSegment & e) + { + convert((directory / e.id().providerSegmentName), e); + }); + } + + void convert(const std::filesystem::path& directory, armem::wm::ProviderSegment& p) + { + p.forEachEntity([&directory](armem::wm::Entity & e) + { + convert((directory / e.id().entityName), e); + }); + } + + void convert(const std::filesystem::path& directory, armem::wm::Entity& e) + { + e.forEachSnapshot([&directory](armem::wm::EntitySnapshot & e) + { + if (!ltm::util::entityHasData(e)) // 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 = directory / 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) / constantes::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::ConvertFromNlohmannJSONObject(doc); + + wm::EntityInstance tmp(e.id().withInstanceIndex(i)); + from_aron(aron, tmp); + ins.data() = tmp.data(); + } + } + } + } + }); + } - void - disk::store(const std::filesystem::path& directory, const armem::wm::Memory& memory) + void store(const std::filesystem::path& directory, const armem::wm::Memory& m) { - detail::store(directory, memory); + m.forEachCoreSegment([&directory](armem::wm::CoreSegment & e) + { + fs::path cPath = directory / e.id().coreSegmentName; + ensureFolderExists(cPath); + store(cPath, e); + }); } + void store(const std::filesystem::path& directory, const armem::wm::CoreSegment& c) + { + c.forEachProviderSegment([&directory](armem::wm::ProviderSegment & e) + { + fs::path pPath = directory / e.id().providerSegmentName; + ensureFolderExists(pPath); + store(pPath, e); + }); + } - void - disk::store(const std::filesystem::path& directory, const armem::server::wm::Memory& memory) + void store(const std::filesystem::path& directory, const armem::wm::ProviderSegment& p) { - detail::store(directory, memory); + p.forEachEntity([&directory](armem::wm::Entity & e) + { + fs::path ePath = directory / e.id().entityName; + ensureFolderExists(ePath); + store(ePath, e); + }); + } + void store(const std::filesystem::path& directory, const armem::wm::Entity& e) + { + e.forEachSnapshot([&directory](armem::wm::EntitySnapshot & e) + { + fs::path sPath = directory / std::to_string(e.id().timestamp.toMicroSeconds()); + ensureFolderExists(sPath); + + e.forEachInstance([&sPath](armem::wm::EntityInstance & e) + { + fs::path iPath = sPath / std::to_string(e.id().instanceIndex); + if (fs::exists(iPath)) + { + // if instance already exists, we skip + return; + } + + ensureFolderExists(iPath); + + fs::path data = iPath / constantes::DATA_FILENAME; + fs::remove(data); // Should not be the case. Anyway, if it happens, create new file! + + std::ofstream ofs; + ofs.open(data); + + auto aron = std::make_shared<aron::data::Dict>(); + to_aron(aron, e); + nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(aron); + + ofs << j.dump(2); + ofs.close(); + }); + }); } } // namespace armarx::armem::server::ltm diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/operations.h b/source/RobotAPI/libraries/armem/server/ltm/disk/operations.h index b18575996f0ba38b0251844b5b9f18a2c98d0752..5e930ff7b52898115ce0af365afcfbc125836fb4 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/disk/operations.h +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/operations.h @@ -9,10 +9,28 @@ namespace armarx::armem::server::ltm::disk { - armem::wm::Memory load(const std::filesystem::path& directory); - - void store(const std::filesystem::path& directory, const armem::wm::Memory& memory); - void store(const std::filesystem::path& directory, const armem::server::wm::Memory& memory); - + namespace constantes + { + const std::string TYPE_FILENAME = "type.aron.ltm.json"; + const std::string DATA_FILENAME = "data.aron.ltm.json"; + } + + namespace util + { + void load(const std::filesystem::path& directory, armem::wm::Memory& memory); + void load(const std::filesystem::path& directory, armem::wm::CoreSegment& memory); + void load(const std::filesystem::path& directory, armem::wm::ProviderSegment& memory); + void load(const std::filesystem::path& directory, armem::wm::Entity& memory); + + void convert(const std::filesystem::path& directory, armem::wm::Memory& memory); + void convert(const std::filesystem::path& directory, armem::wm::CoreSegment& memory); + void convert(const std::filesystem::path& directory, armem::wm::ProviderSegment& memory); + void convert(const std::filesystem::path& directory, armem::wm::Entity& memory); + + void store(const std::filesystem::path& directory, const armem::wm::Memory& memory); + void store(const std::filesystem::path& directory, const armem::wm::CoreSegment& memory); + void store(const std::filesystem::path& directory, const armem::wm::ProviderSegment& memory); + void store(const std::filesystem::path& directory, const armem::wm::Entity& memory); + } } // namespace armarx::armem::server::ltm::disk diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp index ad2d95b0fed35991ae99e050a9939537a840a2a2..11414c6a21e518a17b7bb1e2925ba60efbf65dfe 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp @@ -1,11 +1,95 @@ #include "ConnectionManager.h" +#include <iostream> +#include <fstream> +#include <algorithm> + namespace armarx::armem::server::ltm::mongodb { std::mutex ConnectionManager::initializationMutex; bool ConnectionManager::initialized = false; std::map<std::string, std::unique_ptr<mongocxx::pool>> ConnectionManager::Connections = {}; + ConnectionManager::MongoDBSettings::MongoDBSettings() + { + std::string armarx_home = std::string(getenv("HOME")) + "/.armarx"; + if (getenv("ARMARX_DEFAULTS_DIR")) + { + armarx_home = getenv("ARMARX_DEFAULTS_DIR"); + } + std::ifstream cFile (armarx_home + "/default.cfg"); + if (cFile.is_open()) + { + std::string line; + while(getline(cFile, line)) + { + line.erase(std::remove_if(line.begin(), line.end(), isspace), line.end()); + if(line[0] == '#' || line.empty()) + { + continue; + } + auto delimiterPos = line.find("="); + const auto name = line.substr(0, delimiterPos); + const auto value = line.substr(delimiterPos + 1); + if (name == "ArmarX.MongoHost") + { + host = value; + } + if (name == "ArmarX.MongoPort") + { + port = (unsigned int) std::stoi(value); + } + if (name == "ArmarX.MongoUser") + { + user = value; + } + if (name == "ArmarX.MongoPassword") + { + password = value; + } + } + } + } + + bool ConnectionManager::MongoDBSettings::isSet() const + { + // we always need a host and a port + return !host.empty() and port != 0; + } + + std::string ConnectionManager::MongoDBSettings::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 ConnectionManager::MongoDBSettings::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 ConnectionManager::MongoDBSettings::uri() const + { + return baseUri() + ":" + std::to_string(port) + "/?minPoolSize=" + std::to_string(minPoolSize) + "&maxPoolSize=" + std::to_string(maxPoolSize); + } + + std::string ConnectionManager::MongoDBSettings::toString() const + { + return uri() + "&database=" + database; + } void ConnectionManager::initialize_if() { diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h index 15fac008d7963c034ce40fe55a4606ffe3fead3f..51acb7a0c7848b944beef878b969f54d5c141f1a 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h @@ -28,56 +28,29 @@ namespace armarx::armem::server::ltm::mongodb class ConnectionManager { public: + ConnectionManager() = delete; + struct MongoDBSettings { - std::string host = "localhost"; - unsigned int port = 25276; + std::string host = ""; + unsigned int port = 0; std::string user = ""; std::string password = ""; std::string database = "Test"; int minPoolSize = 5; int maxPoolSize = 100; + MongoDBSettings(); + + bool isSet() const; + + std::string baseUri() const; + + std::string key() const; + + std::string uri() const; - 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; - } + std::string toString() const; }; static mongocxx::pool& Connect(const MongoDBSettings& settings); diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp index 15e1d23d8ac96b567aeb9d33324deda821a418cd..2381297ee3a8311497b175c09372d6776cd1bf3b 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp @@ -12,19 +12,19 @@ #include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> #include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> +#include "operations.h" namespace armarx::armem::server::ltm::mongodb { namespace bsoncxxbuilder = bsoncxx::builder::stream; namespace bsoncxxdoc = bsoncxx::document; - PoolClientPtr MemoryManager::checkConnection() const + PoolClientPtr Memory::checkConnection() const { // Check connection: - ARMARX_INFO << "Checking connection"; if (!ConnectionManager::ConnectionIsValid(dbsettings)) { - ARMARX_WARNING << deactivateSpam("ConnectionIsNotValid") + ARMARX_WARNING << deactivateSpam("LTM_ConnectionError_" + cache.name()) << "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" @@ -38,389 +38,75 @@ namespace armarx::armem::server::ltm::mongodb return client; } - void MemoryManager::reload() + void Memory::reload() { TIMING_START(LTM_Reload); - ARMARX_INFO << "(Re)Establishing connection to: " << dbsettings.toString(); + ARMARX_DEBUG << "(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; } + std::lock_guard l(ltm_mutex); auto databases = client->list_databases(); + std::stringstream ss; + ss << "Found Memory-Collection in MongoDB for '" + cache.name() + "': \n"; for (const auto& doc : databases) { auto el = doc["name"]; - ARMARX_INFO << "Found Memory-Collection in MongoDB: " << el.get_utf8().value; + ss << "\t - " << el.get_utf8().value << "\n"; } + ARMARX_DEBUG << ss.str(); - 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) + armem::wm::Memory temp(lut.id()); mongocxx::database db = client->database(dbsettings.database); + util::load(db, temp); - 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); + TIMING_END_STREAM(LTM_Reload, ARMARX_DEBUG); } - void MemoryManager::convert(armem::wm::Memory& m) + void Memory::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::ConvertFromNlohmannJSONObject(doc); + std::lock_guard l(ltm_mutex); + mongocxx::database db = client->database(dbsettings.database); - // remove metadata - wm::EntityInstance tmp(e.id().withInstanceIndex(i)); - from_aron(aron, tmp); + util::convert(db, m); - // set data - ins.data() = tmp.data(); - } - } - }); - }); - }); - }); - TIMING_END(LTM_Convert); + TIMING_END_STREAM(LTM_Convert, ARMARX_DEBUG); } - void MemoryManager::encodeAndStore() + void Memory::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); + std::lock_guard l(ltm_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::data::Dict>(); - 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 - } - }); - }); - }); - }); + util::store(db, cache); // 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; - } + moveCacheToLUTAndClearCache(); - // 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; + TIMING_END_STREAM(LTM_Encode, ARMARX_DEBUG); } } diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h index 189ccc42563d17dd63116dc3210d99d648b28c31..99a728f8d87fb24ffed147cace21831e90705db0 100644 --- a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h @@ -13,39 +13,28 @@ 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 + class Memory : + public MemoryBase { - using Base = LongtermMemoryBase; + using Base = MemoryBase; public: - MemoryManager() = default; + using Base::MemoryBase; + using Base::convert; + + Memory() = 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/ltm/mongodb/operations.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/operations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4169a858fadd55757b04055b80d8836dad4a131 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/operations.cpp @@ -0,0 +1,314 @@ +#include "operations.h" + +// Simox +#include <SimoxUtility/json.h> + +#include "../operations.h" +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> +#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> + +namespace armarx::armem::server::ltm::mongodb::util +{ + namespace bsoncxxbuilder = bsoncxx::builder::stream; + namespace bsoncxxdoc = bsoncxx::document; + + namespace + { + void mongodbInsertForeignKey(mongocxx::collection& coll, const std::string& key) + { + auto q = bsoncxxbuilder::document{} << std::string(constantes::FOREIGN_KEY) << key << bsoncxxbuilder::finalize; + coll.insert_one(q.view()); + } + + bool mongodbContainsForeignKey(mongocxx::collection& coll, const std::string& key) + { + // check mongodb + auto q = bsoncxxbuilder::document{} << std::string(constantes::FOREIGN_KEY) << key << bsoncxxbuilder::finalize; + auto res = coll.find_one(q.view()); + return (bool) res; + } + + bool mongodbContainsTimestamp(mongocxx::collection& coll, const long ts) + { + // check mongodb + auto q = bsoncxxbuilder::document{} << std::string(constantes::TIMESTAMP) << ts << bsoncxxbuilder::finalize; + auto res = coll.find_one(q.view()); + return (bool) res; + } + } + + void load(const mongocxx::database& db, armem::wm::Memory& m) + { + if (!db.has_collection(m.id().str())) + { + return; + } + mongocxx::collection coll = db.collection(m.id().str()); + mongocxx::cursor cursor = coll.find({}); + for (const auto& doc : cursor) + { + auto el = doc[constantes::FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.memoryName != m.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 (m.hasCoreSegment(k)) + { + throw error::ArMemError("Somehow the container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + auto& cSeg = m.addCoreSegment(k); + load(db, cSeg); + } + } + } + + void load(const mongocxx::database& db, armem::wm::CoreSegment& c) + { + if (!db.has_collection(c.id().str())) + { + return; + } + mongocxx::collection coll = db.collection(c.id().str()); + mongocxx::cursor cursor = coll.find({}); + for (const auto& doc : cursor) + { + auto el = doc[constantes::FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.coreSegmentName != c.id().coreSegmentName || + i.memoryName != c.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 (c.hasProviderSegment(k)) + { + throw error::ArMemError("Somehow the container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + auto& pSeg = c.addProviderSegment(k); + load(db, pSeg); + } + } + } + + void load(const mongocxx::database& db, armem::wm::ProviderSegment& p) + { + if (!db.has_collection(p.id().str())) + { + return; + } + mongocxx::collection coll = db.collection(p.id().str()); + mongocxx::cursor cursor = coll.find({}); + for (const auto& doc : cursor) + { + auto el = doc[constantes::FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.providerSegmentName != p.id().providerSegmentName || + i.coreSegmentName != p.id().coreSegmentName || + i.memoryName != p.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 (p.hasEntity(k)) + { + throw error::ArMemError("Somehow the container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + auto& eSeg = p.addEntity(k); + load(db, eSeg); + } + } + } + + void load(const mongocxx::database& db, armem::wm::Entity& e) + { + if (!db.has_collection(e.id().str())) + { + return; + } + mongocxx::collection coll = db.collection(e.id().str()); + mongocxx::cursor cursor = coll.find({}); + for (const auto& doc : cursor) + { + auto ts = armem::Time::microSeconds(doc[constantes::TIMESTAMP].get_int64().value); + auto& newSnapshot = e.addSnapshot(ts); + + auto i = doc[constantes::INSTANCES]; + unsigned long length = std::distance(i.get_array().value.begin(), i.get_array().value.end()); + + for (unsigned long i = 0; i < length; ++i) + { + // add an empty instance as reference + newSnapshot.addInstance({}); + } + + // check to update lastSnapshot map TODO + //checkUpdateLatestSnapshot(newSnapshot); + } + } + + void convert(const mongocxx::database& db, armem::wm::Memory& m) + { + m.forEachCoreSegment([&db](armem::wm::CoreSegment & e) + { + convert(db, e); + }); + } + + void convert(const mongocxx::database& db, armem::wm::CoreSegment& c) + { + c.forEachProviderSegment([&db](armem::wm::ProviderSegment & e) + { + convert(db, e); + }); + } + + void convert(const mongocxx::database& db, armem::wm::ProviderSegment& p) + { + p.forEachEntity([&db](armem::wm::Entity & e) + { + convert(db, e); + }); + } + + void convert(const mongocxx::database& db, armem::wm::Entity& e) + { + e.forEachSnapshot([&db](armem::wm::EntitySnapshot & e) + { + if (!ltm::util::entityHasData(e)) + { + // Get data from mongodb + auto eColl = db.collection(e.id().getEntityID().str()); + auto q = bsoncxxbuilder::document{} << std::string(constantes::TIMESTAMP) << e.id().timestamp.toMicroSeconds() << bsoncxxbuilder::finalize; + auto res = eColl.find_one(q.view()); + + if (!res) + { + // break if the data could not be found in ltm storage + // perhaps its a not-set maybe type from the cache (not yet consollidated to ltm) + return false; + } + + // convert full json of this entry + nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); + nlohmann::json instances = json[constantes::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(e.id().withInstanceIndex(i)); + + // get ionstance json + nlohmann::json doc = instances[i]; + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSONObject(doc); + + // remove metadata + wm::EntityInstance tmp(e.id().withInstanceIndex(i)); + from_aron(aron, tmp); + + // set data + ins.data() = tmp.data(); + } + } + return true; + }); + } + + void store(const mongocxx::database& db, const armem::wm::Memory& m) + { + auto coll = db.collection(m.id().str()); + m.forEachCoreSegment([&db, &coll](armem::wm::CoreSegment & e) + { + if (!mongodbContainsForeignKey(coll, e.id().str())) + { + mongodbInsertForeignKey(coll, e.id().str()); + } + + store(db, e); + }); + } + + void store(const mongocxx::database& db, const armem::wm::CoreSegment& c) + { + auto coll = db.collection(c.id().str()); + c.forEachProviderSegment([&db, &coll](armem::wm::ProviderSegment & e) + { + if (!mongodbContainsForeignKey(coll, e.id().str())) + { + mongodbInsertForeignKey(coll, e.id().str()); + } + + store(db, e); + }); + } + + void store(const mongocxx::database& db, const armem::wm::ProviderSegment& p) + { + auto coll = db.collection(p.id().str()); + p.forEachEntity([&db, &coll](armem::wm::Entity & e) + { + if (!mongodbContainsForeignKey(coll, e.id().str())) + { + mongodbInsertForeignKey(coll, e.id().str()); + } + + store(db, e); + }); + } + + void store(const mongocxx::database& db, const armem::wm::Entity& e) + { + auto coll = db.collection(e.id().str()); + e.forEachSnapshot([&coll](armem::wm::EntitySnapshot & e) + { + if (!mongodbContainsTimestamp(coll, e.id().timestamp.toMilliSeconds())) + { + // timestamp not found in mongodb ==> new entry + bsoncxxbuilder::document builder{}; + auto in_array = builder + << std::string(constantes::ID) << e.id().str() + << std::string(constantes::TIMESTAMP) << e.id().timestamp.toMicroSeconds() + << std::string(constantes::INSTANCES); + auto array_builder = bsoncxx::builder::basic::array{}; + + e.forEachInstance([&array_builder](const wm::EntityInstance & e) + { + auto aron = std::make_shared<aron::data::Dict>(); + 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; + coll.insert_one(doc.view()); + + // check to update lastSnapshot map TODO + //checkUpdateLatestSnapshot(e); + } + }); + } + +} // namespace armarx::armem::server::ltm diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/operations.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/operations.h new file mode 100644 index 0000000000000000000000000000000000000000..c81c2312e3e4083e1323465cb5b92ee54bb75818 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/operations.h @@ -0,0 +1,38 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/forward_declarations.h> +#include <RobotAPI/libraries/armem/server/forward_declarations.h> + +// Data +# include "ConnectionManager.h" + +namespace armarx::armem::server::ltm::mongodb +{ + + namespace constantes + { + const std::string FOREIGN_KEY = "foreign_key"; + const std::string ID = "id"; + const std::string TIMESTAMP = "timestamp"; + const std::string INSTANCES = "instances"; + } + + namespace util + { + void load(const mongocxx::database& db, armem::wm::Memory& memory); + void load(const mongocxx::database& db, armem::wm::CoreSegment& memory); + void load(const mongocxx::database& db, armem::wm::ProviderSegment& memory); + void load(const mongocxx::database& db, armem::wm::Entity& memory); + + void convert(const mongocxx::database& db, armem::wm::Memory& memory); + void convert(const mongocxx::database& db, armem::wm::CoreSegment& memory); + void convert(const mongocxx::database& db, armem::wm::ProviderSegment& memory); + void convert(const mongocxx::database& db, armem::wm::Entity& memory); + + void store(const mongocxx::database& db, const armem::wm::Memory& memory); + void store(const mongocxx::database& db, const armem::wm::CoreSegment& memory); + void store(const mongocxx::database& db, const armem::wm::ProviderSegment& memory); + void store(const mongocxx::database& db, const armem::wm::Entity& memory); + } + +} // namespace armarx::armem::server::ltm::mongodb diff --git a/source/RobotAPI/libraries/armem/server/ltm/operations.cpp b/source/RobotAPI/libraries/armem/server/ltm/operations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..188f0f547dd0f519952bcc4db4ea28deae3e0a57 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/operations.cpp @@ -0,0 +1,22 @@ +#include "operations.h" + +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> + +namespace armarx::armem::server::ltm::util +{ + bool entityHasData(const armem::wm::EntitySnapshot& e) + { + // check whether all 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; + }); + return !allDataIsNull; + } +} // namespace armarx::armem::server::ltm diff --git a/source/RobotAPI/libraries/armem/server/ltm/operations.h b/source/RobotAPI/libraries/armem/server/ltm/operations.h new file mode 100644 index 0000000000000000000000000000000000000000..b430afdf11277e67487842fcf0f89d4ca30a4688 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/operations.h @@ -0,0 +1,13 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/forward_declarations.h> +#include <RobotAPI/libraries/armem/server/forward_declarations.h> + +namespace armarx::armem::server::ltm +{ + namespace util + { + bool entityHasData(const armem::wm::EntitySnapshot& e); + } + +} // namespace armarx::armem::server::ltm diff --git a/source/RobotAPI/libraries/armem/server/plugins/Plugin.cpp b/source/RobotAPI/libraries/armem/server/plugins/Plugin.cpp index 031e8fbdfe55b1024bac050b341327e6ef16e482..93c701eaf6248500194403bdbf2a43e195c7125f 100644 --- a/source/RobotAPI/libraries/armem/server/plugins/Plugin.cpp +++ b/source/RobotAPI/libraries/armem/server/plugins/Plugin.cpp @@ -30,8 +30,21 @@ namespace armarx::armem::server::plugins { properties->optional(workingMemory.name(), prefix + "MemoryName", "Name of this memory server."); } - properties->optional(longtermMemoryEnabled, prefix + "ltm.00_enabled"); + if (longtermMemoryEnabled // if not explicitly set to false in constructor of component + and not properties->hasDefinition(prefix + "ltm.enabled")) + { + // stuff for mongodb memory + properties->optional(longtermMemoryEnabled, prefix + "ltm.enabled"); + /*properties->optional(longtermMemory.dbsettings.host, prefix + "ltm.dbhost", "Only set if you want to have a custom host. Takes the host from 'armarx memory start' if not set."); + properties->optional(longtermMemory.dbsettings.port, prefix + "ltm.dbport", "Only set if you want to have a custom port. Takes the port from 'armarx memory start' if not set."); + properties->optional(longtermMemory.dbsettings.user, prefix + "ltm.dbuser", "Only set if you want to have a custom user. Takes the user from 'armarx memory start' if not set."); + properties->optional(longtermMemory.dbsettings.password, prefix + "ltm.dbpassword", "Only set if you want to have a custom password. Takes the password from 'armarx memory start' if not set."); + properties->optional(longtermMemory.dbsettings.database, prefix + "ltm.dbdatabase", "The name of the database you want to use for this memory. You can have multiple memories in one database.");*/ + + // stuff if disk memory + properties->optional(longtermMemory.memoryPathString, "ltm.storagepath", "The path to the memory storage."); + } // Publish memory updates topic properties->topic(memoryTopic, memoryTopicDefaultName); @@ -51,7 +64,7 @@ namespace armarx::armem::server::plugins // establishing connection to ltm and mongodb if (longtermMemoryEnabled) { - longtermMemoryManager.reload(); + longtermMemory.reload(); } } @@ -68,7 +81,7 @@ namespace armarx::armem::server::plugins void Plugin::setMemoryName(const std::string& memoryName) { workingMemory.name() = memoryName; - longtermMemoryManager.setName(memoryName); + longtermMemory.setName(memoryName); } diff --git a/source/RobotAPI/libraries/armem/server/plugins/Plugin.h b/source/RobotAPI/libraries/armem/server/plugins/Plugin.h index 49cc07bb08dc7265fa797e2f5f77a3d81511c33c..9d443c681260a8cffb3d9c9b4b7e6c6564e61ef6 100644 --- a/source/RobotAPI/libraries/armem/server/plugins/Plugin.h +++ b/source/RobotAPI/libraries/armem/server/plugins/Plugin.h @@ -70,7 +70,7 @@ namespace armarx::armem::server::plugins server::wm::Memory workingMemory; /// Helps connecting `memory` to ice. Used to handle Ice callbacks. - MemoryToIceAdapter iceAdapter { &workingMemory, &longtermMemoryManager}; + MemoryToIceAdapter iceAdapter { &workingMemory, &longtermMemory}; // Working Memory Updates (publishing) @@ -85,11 +85,7 @@ namespace armarx::armem::server::plugins bool longtermMemoryEnabled = true; /// A manager class for the ltm. It internally holds a normal wm instance as a cache. - server::ltm::mongodb::MemoryManager longtermMemoryManager; - - std::string longTermMemoryDatabaseHost; - std::string longTermMemoryDatabaseUser; - std::string longTermMemoryDatabasePassword; + server::ltm::disk::Memory longtermMemory; private: diff --git a/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.cpp b/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.cpp index f3da4f3ee885a2f1cda0a86ad195ae466f4fb9e5..7ffd2f0246de5b6ebc84eab8a33943c133a73e71 100644 --- a/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.cpp +++ b/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.cpp @@ -92,9 +92,9 @@ namespace armarx::armem::server::plugins } - ltm::mongodb::MemoryManager& ReadWritePluginUser::longtermMemoryManager() + ltm::disk::Memory& ReadWritePluginUser::longtermMemoryManager() { - return plugin->longtermMemoryManager; + return plugin->longtermMemory; } } diff --git a/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.h b/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.h index 6736224199489fb171d49b092fd91e88e59fd9b0..1c2d2fd500b9bd84b1b6cdfa890053990fe986ac 100644 --- a/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.h +++ b/source/RobotAPI/libraries/armem/server/plugins/ReadWritePluginUser.h @@ -56,7 +56,7 @@ namespace armarx::armem::server::plugins MemoryToIceAdapter& iceAdapter(); bool isLongtermMemoryEnabled(); - server::ltm::mongodb::MemoryManager& longtermMemoryManager(); + server::ltm::disk::Memory& longtermMemoryManager(); private: diff --git a/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp index 166256ba2e20bc72b560f481069b4fb9a927a825..406dcfdf698c92e81f544da3154573764a6a31fa 100644 --- a/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp @@ -103,9 +103,7 @@ namespace armarx::armem::gui::disk { std::filesystem::create_directories(path / name); - armem::server::ltm::disk::MemoryManager manager; - manager.setName(name); - manager.setBasePath(path / name); + armem::server::ltm::disk::Memory manager((path / name)); manager.reload(); manager.append(data); @@ -150,7 +148,9 @@ namespace armarx::armem::gui::disk if (dir.is_directory()) { std::string memoryName = dir.path().filename(); - armem::wm::Memory memory = armem::server::ltm::disk::load(dir.path()); + armem::server::ltm::disk::Memory ltm(dir.path()); + ltm.reload(); + armem::wm::Memory memory = ltm.convert(); memoryData[memoryName] = std::move(memory); numLoaded++; diff --git a/source/RobotAPI/libraries/armem_gui/instance/ImageView.h b/source/RobotAPI/libraries/armem_gui/instance/ImageView.h index 4b85e093a9ef522b7d125a6a0362a98dc4e11c52..ad3c6a0934e8cf7a9a09b17d1e2bd71b697008c3 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/ImageView.h +++ b/source/RobotAPI/libraries/armem_gui/instance/ImageView.h @@ -63,7 +63,6 @@ namespace armarx::armem::gui::instance QImage sourceImage; QImage scaledImage; - }; } diff --git a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp index 593a341b1b6d8e127b12c4c5622b85710a7e1a04..c5cfddb7c11c39f6953453a7f36e6ab7f5ad56f1 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.cpp @@ -13,12 +13,14 @@ namespace armarx::aron void DataDisplayVisitor::visitDict(const data::VariantPtr& n) { - value << n->childrenSize() << " items"; + auto x = data::Dict::DynamicCastAndCheck(n); + value << x->childrenSize() << " items"; } void DataDisplayVisitor::visitList(const data::VariantPtr& n) { - value << n->childrenSize() << " items"; + auto x = data::List::DynamicCastAndCheck(n); + value << x->childrenSize() << " items"; } void DataDisplayVisitor::visitBool(const data::VariantPtr& b) @@ -36,7 +38,7 @@ namespace armarx::aron void DataDisplayVisitor::visitDouble(const data::VariantPtr& n) { - auto x = data::Bool::DynamicCastAndCheck(n); + auto x = data::Double::DynamicCastAndCheck(n); value << x->getValue(); } diff --git a/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp b/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp index 259e30eccd82addfd1769daee9432b46aa5353cf..bc4b1878fba11a5e9621735b9bee19e72548b05c 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp @@ -38,26 +38,10 @@ std::string armarx::armem::gui::instance::sanitizeTypeName(const std::string& ty n = s::replace_all(n, "armarx::aron::type::", ""); n = s::replace_all(n, "armarx::aron::data::", ""); - if (false) + if (s::starts_with(n, "Object<")) { - const std::vector<std::string> containers { "Dict", "List", "Object", "Tuple", "Pair", "NDArray" }; - for (const std::string& s : containers) - { - n = s::replace_all(n, s + "Type", s); - } - } - else - { - n = s::replace_all(n, "Type", ""); - } - - - n = simox::alg::remove_prefix(n, "Aron"); - n = remove_wrap(n, "Object<", ">"); - - if (true) - { - const std::string del = "::"; + n = remove_wrap(n, "Object<", ">"); + const std::string del = "::"; // remove namespace size_t find = n.rfind(del); if (find != n.npos) { @@ -67,6 +51,16 @@ std::string armarx::armem::gui::instance::sanitizeTypeName(const std::string& ty n = ss.str(); } } + else + { + if (s::contains(n, "<")) // another containertype + { + std::string container = n.substr(0, n.find("<")); + std::string subtype = remove_wrap(n, (container + "<"), ">"); + subtype = sanitizeTypeName(subtype); + n = n.substr(0, n.find("<")+1) + subtype + ">"; + } + } return n; diff --git a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.cpp b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.cpp index 48ad7e64c36471745b22db851f946ed9dd1a379c..eae4cb934e7c878c2c2f7787f0ef0cd96a2f9775 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.cpp @@ -196,26 +196,27 @@ namespace armarx::armem::gui::instance if (data) { - if (const auto d = aron::data::Dict::DynamicCastAndCheck(data); const auto t = type::Object::DynamicCastAndCheck(type)) + if (const auto d = aron::data::Dict::DynamicCast(data); const auto t = type::Object::DynamicCast(type)) { _updateTree(item, *t, *d); } - else if (const auto d = aron::data::Dict::DynamicCastAndCheck(data); const auto t = type::Dict::DynamicCastAndCheck(type)) + else if (const auto d = aron::data::Dict::DynamicCast(data); const auto t = type::Dict::DynamicCast(type)) { _updateTree(item, *t, *d); } - else if (const auto d = aron::data::List::DynamicCastAndCheck(data); const auto t = type::List::DynamicCastAndCheck(type)) + else if (const auto d = aron::data::List::DynamicCast(data); const auto t = type::List::DynamicCast(type)) { _updateTree(item, *t, *d); } - else if (const auto d = aron::data::List::DynamicCastAndCheck(data); const auto t = type::Pair::DynamicCastAndCheck(type)) + else if (const auto d = aron::data::List::DynamicCast(data); const auto t = type::Pair::DynamicCast(type)) { _updateTree(item, *t, *d); } - else if (const auto d = aron::data::List::DynamicCastAndCheck(data); const auto t = type::Tuple::DynamicCastAndCheck(type)) + else if (const auto d = aron::data::List::DynamicCast(data); const auto t = type::Tuple::DynamicCast(type)) { _updateTree(item, *t, *d); } + // else??? } } diff --git a/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.cpp b/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.cpp index ce9dc9b21766313ef8f5fc33de871f626d60c278..c994d831e3e80634c3627853e333c47e3fde1a91 100644 --- a/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.cpp +++ b/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.cpp @@ -43,6 +43,12 @@ namespace armarx::aron::codegenerator::cpp::generator { } + CppBlockPtr Image::getResetHardBlock(const std::string& cppAccessor) const + { + CppBlockPtr block_if_data = detail::NDArrayGenerator<type::Image, Image>::getResetHardBlock(cppAccessor); + block_if_data->addLine(cppAccessor + ".create(0, 0, " + PixelType2Cpp.at(type.getPixelType()).first + ");"); + return this->resolveMaybeResetSoftBlock(block_if_data, cppAccessor); + } CppBlockPtr Image::getResetSoftBlock(const std::string& cppAccessor) const { diff --git a/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.h b/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.h index a39eb8c918a3ad885a2a95d3822db4a4117ad221..8c56ead469a3fba497d94b93154d06285d56b49f 100644 --- a/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.h +++ b/source/RobotAPI/libraries/aron/core/codegenerator/codewriter/cpp/generator/ndarray/Image.h @@ -42,6 +42,7 @@ namespace armarx::aron::codegenerator::cpp::generator virtual ~Image() = default; // virtual implementations + CppBlockPtr getResetHardBlock(const std::string& cppAccessor) const override; CppBlockPtr getResetSoftBlock(const std::string& cppAccessor) const override; CppBlockPtr getWriteTypeBlock(const std::string& typeAccessor, const std::string& cppAccessor, std::string& variantAccessor) const override; CppBlockPtr getWriteBlock(const std::string& cppAccessor, std::string& variantAccessor) const override; diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.cpp b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.cpp index 7e06776cfb0dbd8ad220afc416f580cfc1d01518..b4efd4c8ca3a0e414460d4506f7db75f18865b01 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.cpp +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.cpp @@ -31,6 +31,13 @@ namespace armarx::aron::type { + const std::map<image::PixelType, std::string> Image::Pixeltype2String + { + {image::PixelType::rgb24, "rgb24"}, + {image::PixelType::depth32, "depth32"} + }; + const std::map<std::string, image::PixelType> Image::String2Pixeltype = conversion::util::InvertMap(Pixeltype2String); + // constructors Image::Image(const Path& path) : @@ -68,7 +75,7 @@ namespace armarx::aron::type std::string Image::getFullName() const { - return "armarx::aron::type::Image<" + std::to_string(this->aron->pixelType) + ">"; + return "armarx::aron::type::Image<" + Pixeltype2String.at(this->aron->pixelType) + ">"; } } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.h b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.h index d3a519a2df871b0bc4fa2aec59823adc2ddf2b20..0b9f9dd0992c79187368a7136b7fa957a42cacae 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.h +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Image.h @@ -53,5 +53,8 @@ namespace armarx::aron::type // virtual implementations std::string getShortName() const override; std::string getFullName() const override; + + static const std::map<image::PixelType, std::string> Pixeltype2String; + static const std::map<std::string, image::PixelType> String2Pixeltype; }; } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.cpp b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.cpp index ee34b14f19ba003f537e093ada42dfb9460701cb..62eabd986890eb92cd5bf9b050c887ff985da6d6 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.cpp +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.cpp @@ -29,6 +29,17 @@ namespace armarx::aron::type { + const std::map<matrix::ElementType, std::string> Matrix::Elementtype2String + { + {matrix::ElementType::int16, "int16"}, + {matrix::ElementType::int32, "int32"}, + {matrix::ElementType::int64, "int64"}, + {matrix::ElementType::float32, "float32"}, + {matrix::ElementType::float64, "float64"} + }; + + const std::map<std::string, matrix::ElementType> Matrix::String2Elementtype = conversion::util::InvertMap(Elementtype2String); + // constructors Matrix::Matrix(const Path& path) : detail::NDArrayVariant<type::dto::Matrix, Matrix>(type::Descriptor::eMatrix, path) @@ -91,7 +102,7 @@ namespace armarx::aron::type std::string Matrix::getFullName() const { - return "armarx::aron::type::Matrix<" + std::to_string(this->aron->rows) + ", " + std::to_string(this->aron->cols) + ", " + std::to_string(this->aron->elementType) + ">"; + return "armarx::aron::type::Matrix<" + std::to_string(this->aron->rows) + ", " + std::to_string(this->aron->cols) + ", " + Elementtype2String.at(this->aron->elementType) + ">"; } } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.h b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.h index af2e271f7ce85b3f2338ac506d6b1357730e4f19..da9fd537dbcf62f9e17e54b9c42815009d408e8a 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.h +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Matrix.h @@ -57,5 +57,8 @@ namespace armarx::aron::type // virtual implementations std::string getShortName() const override; std::string getFullName() const override; + + static const std::map<matrix::ElementType, std::string> Elementtype2String; + static const std::map<std::string, matrix::ElementType> String2Elementtype; }; } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.cpp b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.cpp index 43e21a3abd74580f29ae615d9199d5ae93983c8a..10a3473021b616c78cc7e88b2e45dc94226663bb 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.cpp +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.cpp @@ -26,6 +26,19 @@ namespace armarx::aron::type { + const std::map<pointcloud::VoxelType, std::string> PointCloud::Voxeltype2String + { + {pointcloud::VoxelType::PointXYZ, "PointXYZ"}, + {pointcloud::VoxelType::PointXYZI, "PointXYZI"}, + {pointcloud::VoxelType::PointXYZL, "PointXYZL"}, + {pointcloud::VoxelType::PointXYZRGB, "PointXYZRGB"}, + {pointcloud::VoxelType::PointXYZRGBL, "PointXYZRGBL"}, + {pointcloud::VoxelType::PointXYZRGBA, "PointXYZRGBA"}, + {pointcloud::VoxelType::PointXYZHSV, "PointXYZHSV"} + }; + + const std::map<std::string, pointcloud::VoxelType> PointCloud::String2Voxeltype = conversion::util::InvertMap(Voxeltype2String); + // constructors PointCloud::PointCloud(const Path& path) : detail::NDArrayVariant<type::dto::PointCloud, PointCloud>(type::Descriptor::ePointCloud, path) @@ -60,7 +73,7 @@ namespace armarx::aron::type std::string PointCloud::getFullName() const { - return "armarx::aron::type::PointCloud<" + std::to_string(this->aron->voxelType) + ">"; + return "armarx::aron::type::PointCloud<" + Voxeltype2String.at(this->aron->voxelType) + ">"; } } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.h b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.h index fbcb1f1c1b637a04004104c447c952bcfcb3e176..37d3aacdbed11c396cdc7351f8e7077705f2e2a6 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.h +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/PointCloud.h @@ -54,5 +54,8 @@ namespace armarx::aron::type // virtual implementations std::string getShortName() const override; std::string getFullName() const override; + + static const std::map<pointcloud::VoxelType, std::string> Voxeltype2String; + static const std::map<std::string, pointcloud::VoxelType> String2Voxeltype; }; } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.cpp b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.cpp index 499243b9dbba2f68a88f25e83da91044210c8d39..5457ee84421cc570dda07881070f6f9249359b73 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.cpp +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.cpp @@ -28,6 +28,13 @@ namespace armarx::aron::type { + const std::map<quaternion::ElementType, std::string> Quaternion::Elementtype2String = + { + {quaternion::ElementType::float32, "float32"}, + {quaternion::ElementType::float64, "float64"} + }; + const std::map<std::string, quaternion::ElementType> Quaternion::String2Elementtype = conversion::util::InvertMap(Elementtype2String); + // constructors Quaternion::Quaternion(const Path& path) : detail::NDArrayVariant<type::dto::Quaternion, Quaternion>(type::Descriptor::eQuaternion, path) @@ -62,7 +69,7 @@ namespace armarx::aron::type std::string Quaternion::getFullName() const { - return "armarx::aron::type::Quaternion<" + std::to_string(this->aron->elementType) + ">"; + return "armarx::aron::type::Quaternion<" + Elementtype2String.at(this->aron->elementType) + ">"; } } diff --git a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.h b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.h index 87cff8e501654778464799198401a48d05ba818e..f660702be8c84613877d164763914bcf4cd57bc9 100644 --- a/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.h +++ b/source/RobotAPI/libraries/aron/core/type/variant/ndarray/Quaternion.h @@ -53,5 +53,8 @@ namespace armarx::aron::type // virtual implementations virtual std::string getShortName() const override; virtual std::string getFullName() const override; + + static const std::map<quaternion::ElementType, std::string> Elementtype2String; + static const std::map<std::string, quaternion::ElementType> String2Elementtype; }; }