diff --git a/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.cpp b/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.cpp index 487c9c6fc4c732fc1d4fe2f126724cb74c8dd810..72e5b8f33073f716e9d3a299ea26dda288dc6066 100644 --- a/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.cpp +++ b/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.cpp @@ -42,7 +42,7 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> #include <RobotAPI/libraries/armem/core/Time.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem/server/MemoryRemoteGui.h> #include <RobotAPI/libraries/armem_objects/types.h> diff --git a/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.h b/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.h index f85a804e97337e1193ca1f38048beb103e65ce59..b4cab3fc71e6d6b1dd92e3128fd3a90a8c191109 100644 --- a/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.h +++ b/source/RobotAPI/components/ArticulatedObjectLocalizerExample/ArticulatedObjectLocalizerExample.h @@ -14,7 +14,7 @@ #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/interface/armem/server/MemoryInterface.h> #include <RobotAPI/libraries/armem/client/ComponentPlugin.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.h> #include <RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.h> diff --git a/source/RobotAPI/components/KITHandUnit/KITHandUnit.cpp b/source/RobotAPI/components/KITHandUnit/KITHandUnit.cpp index e7d20106efb18cad009e22a32e3528d6c8197e06..fb4cb1d94baf63e8013099a443d18890fb5c6352 100644 --- a/source/RobotAPI/components/KITHandUnit/KITHandUnit.cpp +++ b/source/RobotAPI/components/KITHandUnit/KITHandUnit.cpp @@ -165,7 +165,8 @@ namespace armarx { const std::uint64_t pos = std::clamp( static_cast<std::uint64_t>(pair.second * KITHand::ControlOptions::maxPosFingers), - 0, KITHand::ControlOptions::maxPosFingers); + static_cast<std::uint64_t>(0), + KITHand::ControlOptions::maxPosFingers); ARMARX_DEBUG << deactivateSpam(1, std::to_string(pos)) << "set fingers " << pos; _driver->sendFingersPosition(pos); } @@ -173,7 +174,8 @@ namespace armarx { const std::uint64_t pos = std::clamp( static_cast<std::uint64_t>(pair.second * KITHand::ControlOptions::maxPosThumb), - 0, KITHand::ControlOptions::maxPosThumb); + static_cast<std::uint64_t>(0), + KITHand::ControlOptions::maxPosThumb); ARMARX_DEBUG << deactivateSpam(1, std::to_string(pos)) << "set thumb " << pos; _driver->sendThumbPosition(pos); } diff --git a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp index a47012e9cd16988958672ab6de5f3ece1aedd9d3..997be9e96b87d88593d27fd7120f332a21912b05 100644 --- a/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp +++ b/source/RobotAPI/components/KITProstheticHandUnit/KITProstheticHandUnit.cpp @@ -197,7 +197,7 @@ namespace armarx shapeNames->addVariant(currentPreshape); } - void KITProstheticHandUnit::setShape(const std::string& shapeName, const Ice::Current& c) + void KITProstheticHandUnit::setShape(const std::string& shapeName, const Ice::Current&) { if (std::regex_match(shapeName, std::regex{"[gG](0|[1-9][0-9]*)"})) { diff --git a/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp b/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp index 8ae42e00bd5385886696dd6ead35feb927665947..3678ecd55d64e033e03c1e5f7400db875054cc87 100644 --- a/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp +++ b/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.cpp @@ -34,7 +34,8 @@ #include <RobotAPI/libraries/armem/client/query/query_fns.h> #include <RobotAPI/libraries/armem/core/error.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/operations.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/components/armem/server/ExampleMemory/aron/ExampleData.aron.generated.h> @@ -257,40 +258,27 @@ namespace armarx armem::client::QueryResult qResult = memoryReader.query(builder.buildQueryInput()); ARMARX_INFO << qResult; - if (qResult.success) { - armem::wm::Memory& memory = qResult.memory; - - ARMARX_CHECK_EQUAL(memory.coreSegments().size(), 1); - armem::wm::CoreSegment& coreSeg = memory.coreSegments().begin()->second; + ARMARX_IMPORTANT << "Getting entity via ID"; - ARMARX_CHECK_EQUAL(coreSeg.providerSegments().size(), 1); - armem::wm::ProviderSegment& provSeg = coreSeg.providerSegments().begin()->second; + armem::wm::Memory& memory = qResult.memory; + ARMARX_CHECK_GREATER_EQUAL(memory.size(), 1); - ARMARX_CHECK_EQUAL(provSeg.entities().size(), 1); - armem::wm::Entity& entity = provSeg.entities().begin()->second; + const armem::wm::Entity* entity = memory.findEntity(entityID); + ARMARX_CHECK_NOT_NULL(entity) + << "Entity " << entityID << " was not found in " << armem::print(memory); + ARMARX_CHECK_GREATER_EQUAL(entity->size(), 1); - ARMARX_CHECK_GREATER_EQUAL(entity.history().size(), 1); - armem::wm::EntitySnapshot& snapshot = entity.history().begin()->second; + const armem::wm::EntitySnapshot& snapshot = entity->getLatestSnapshot(); + ARMARX_CHECK_GREATER_EQUAL(snapshot.size(), 1); ARMARX_INFO << "Result: " - << "\n- memory: \t" << memory.name() - << "\n- core segment: \t" << coreSeg.name() - << "\n- prov segment: \t" << provSeg.name() - << "\n- entity: \t" << entity.name() + << "\n- entity: \t" << entity->name() << "\n- snapshot: \t" << snapshot.time() << "\n- #instances: \t" << snapshot.size() ; - ARMARX_IMPORTANT << "Getting entity via ID"; - const armem::wm::Entity& e = memory.getEntity(entityID); - ARMARX_INFO << "Result: " - << "\n- entity: \t" << e.name() - << "\n- snapshot: \t" << e.getLatestSnapshot().time() - << "\n- #instances: \t" << e.getLatestSnapshot().size() - ; - // Show memory contents in remote gui. tab.queryResult = std::move(memory); tab.rebuild = true; diff --git a/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.h b/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.h index 5680e316a6228021932afea5d73abd02fdcda6d8..ca28ab86bc1e755c49b93649fd77b26aac0a4250 100644 --- a/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.h +++ b/source/RobotAPI/components/armem/client/ExampleMemoryClient/ExampleMemoryClient.h @@ -34,7 +34,7 @@ #include <RobotAPI/interface/armem/server/MemoryInterface.h> #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/libraries/armem/client/ComponentPlugin.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> namespace armarx diff --git a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp index 6650e573785cce732f0f2bbe7c128aa34fc58626..8ab75efc78ddad8492598356df374266947fe515 100644 --- a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp +++ b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp @@ -23,7 +23,7 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem/server/MemoryRemoteGui.h> #include <RobotAPI/libraries/armem/core/Time.h> diff --git a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h index 4e1d01f5564e96c7e46ad2db33081835873dd7a6..33770ad5425f8824fca2f40ae45323861f29cc91 100644 --- a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h +++ b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h @@ -33,7 +33,7 @@ #include <RobotAPI/interface/armem/server/MemoryInterface.h> #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/libraries/armem/client/ComponentPlugin.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h> diff --git a/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp b/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp index e007e8f5e3ad47c57f9bb71c75e517d57af1addf..879809064b9cdb1429b49925e54ba5765cb534cf 100644 --- a/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp +++ b/source/RobotAPI/components/armem/server/ExampleMemory/ExampleMemory.cpp @@ -60,8 +60,7 @@ namespace armarx void ExampleMemory::onInitComponent() { - workingMemory.name() = p.memoryName; - longtermMemory.name() = p.memoryName; + this->setMemoryName(p.memoryName); // Usually, the memory server will specify a number of core segments with a specific aron type. workingMemory.addCoreSegment("ExampleData", armem::example::ExampleData::toAronType()); @@ -70,7 +69,11 @@ namespace armarx bool trim = true; p.core.defaultCoreSegments = simox::alg::split(p.core._defaultSegmentsStr, ",", trim); p.core._defaultSegmentsStr.clear(); - workingMemory.addCoreSegments(p.core.defaultCoreSegments); + + for (const std::string& name : p.core.defaultCoreSegments) + { + workingMemory.addCoreSegment(name); + } } void ExampleMemory::onConnectComponent() diff --git a/source/RobotAPI/components/armem/server/ExampleMemory/test/ExampleMemoryTest.cpp b/source/RobotAPI/components/armem/server/ExampleMemory/test/ExampleMemoryTest.cpp index c5ba35a13aa4ec127f64d0992021d29262d54407..56c9d08f49759de2f75c8085551ea63fb9263935 100644 --- a/source/RobotAPI/components/armem/server/ExampleMemory/test/ExampleMemoryTest.cpp +++ b/source/RobotAPI/components/armem/server/ExampleMemory/test/ExampleMemoryTest.cpp @@ -41,7 +41,7 @@ BOOST_AUTO_TEST_CASE(test_ExampleData_type) { armarx::aron::typenavigator::ObjectNavigatorPtr type = ExampleData::toAronType(); - BOOST_CHECK_EQUAL(type->childrenSize(), 15); + BOOST_CHECK_EQUAL(type->childrenSize(), 16); armem::wm::Memory memory; armem::wm::CoreSegment& core = memory.addCoreSegment("ExampleData", type); diff --git a/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp b/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp index 350810558ce9d2200e61bda865dd1633da15c128..15253dffb3eb8c7e49c114d2a7659627cd797071 100644 --- a/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp +++ b/source/RobotAPI/components/armem/server/MotionMemory/MotionMemory.cpp @@ -55,8 +55,7 @@ namespace armarx void MotionMemory::onInitComponent() { - workingMemory.name() = memoryName; - longtermMemory.name() = memoryName; + this->setMemoryName(memoryName); mdbMotions.onInit(); } diff --git a/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.cpp b/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.cpp index c624379b89ae336aa561e364706f3780b7fbf0e9..40ceb2864360a7f9412091a39870c8a51846f015 100644 --- a/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.cpp +++ b/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.cpp @@ -22,8 +22,6 @@ #include "ObjectMemory.h" -#include <mutex> - namespace armarx::armem::server::obj { diff --git a/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.h b/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.h index b90406be31ac76a40172d67076f931ebb1a6ba1a..44332653fc095f826aac0ccf5e5e79c124769dfd 100644 --- a/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.h +++ b/source/RobotAPI/components/armem/server/ObjectMemory/ObjectMemory.h @@ -22,23 +22,20 @@ #pragma once -#include <memory> -#include <mutex> - -#include <VirtualRobot/VirtualRobot.h> - -#include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h> +#include <RobotAPI/libraries/armem_objects/server/class/Segment.h> +#include <RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.h> +#include <RobotAPI/libraries/armem_objects/server/attachments/Segment.h> -#include <RobotAPI/interface/armem/server/ObjectMemoryInterface.h> +#include <RobotAPI/libraries/armem/server/ComponentPlugin.h> #include <RobotAPI/libraries/RobotAPIComponentPlugins/ArVizComponentPlugin.h> #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotStateComponentPlugin.h> -#include <RobotAPI/libraries/armem/server/ComponentPlugin.h> +#include <RobotAPI/interface/armem/server/ObjectMemoryInterface.h> -#include <RobotAPI/libraries/armem_objects/server/class/Segment.h> -#include <RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.h> -#include <RobotAPI/libraries/armem_objects/server/attachments/Segment.h> +#include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h> + +#include <memory> #define ICE_CURRENT_ARG const Ice::Current& = Ice::emptyCurrent @@ -79,7 +76,7 @@ namespace armarx::armem::server::obj public: ObjectMemory(); - virtual ~ObjectMemory(); + virtual ~ObjectMemory() override; /// @see armarx::ManagedIceObject::getDefaultName() diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt b/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt index a25161ff2cdf35173921720f68ad5bcec16cba30..f49cb33c231a20857aa30458b4f7f5f14c05acdc 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/CMakeLists.txt @@ -15,18 +15,10 @@ set(COMPONENT_LIBS ) set(SOURCES - aron_conversions.cpp RobotStateMemory.cpp - RobotStateWriter.cpp - RobotUnitData.cpp - RobotUnitReader.cpp ) set(HEADERS - aron_conversions.h RobotStateMemory.h - RobotStateWriter.h - RobotUnitData.h - RobotUnitReader.h ) armarx_add_component("${SOURCES}" "${HEADERS}") diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp index c269bd56e747159919ce7358739e6964f20d6355..582cf20586d119f1ef232cb32c706bbd1eef1e4e 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp @@ -38,7 +38,7 @@ #include <RobotAPI/libraries/armem_robot_state/server/common/Visu.h> #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotUnitComponentPlugin.h> -#include "aron_conversions.h" +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.h> namespace armarx::armem::server::robot_state @@ -68,8 +68,7 @@ namespace armarx::armem::server::robot_state const std::string prefix = "mem."; - workingMemory.name() = "RobotState"; - longtermMemory.name() = "RobotState"; + this->setMemoryName("RobotState"); defs->optional(workingMemory.name(), prefix + "MemoryName", "Name of this memory server."); const std::string robotUnitPrefix{sensorValuePrefix}; @@ -83,8 +82,6 @@ namespace armarx::armem::server::robot_state "The frequency to store values in Hz. All other values get discarded. " "Minimum is 1, max is " + std::to_string(ROBOT_UNIT_MAXIMUM_FREQUENCY) + ".") .setMin(1).setMax(ROBOT_UNIT_MAXIMUM_FREQUENCY); - defs->optional(robotUnit.configPath, robotUnitPrefix + "ConfigPath", - "Specify a configuration file to group the sensor values specifically."); descriptionSegment.defineProperties(defs, prefix + "desc."); proprioceptionSegment.defineProperties(defs, prefix + "prop."); @@ -127,8 +124,6 @@ namespace armarx::armem::server::robot_state std::vector<std::string> projectIncludePaths = simox::alg::split(pathsString, ";,"); includePaths.insert(includePaths.end(), projectIncludePaths.begin(), projectIncludePaths.end()); } - - ArmarXDataPath::getAbsolutePath(robotUnit.configPath, robotUnit.configPath, includePaths); } @@ -148,7 +143,8 @@ namespace armarx::armem::server::robot_state commonVisu.connect(getArvizClient(), debugObserver->getDebugObserver()); - robotUnit.reader.connect(*robotUnit.plugin, *debugObserver); + robotUnit.reader.connect(*robotUnit.plugin, *debugObserver, + proprioceptionSegment.getRobotUnitProviderID().providerSegmentName); robotUnit.writer.connect(*debugObserver); robotUnit.writer.properties.robotUnitProviderID = proprioceptionSegment.getRobotUnitProviderID(); @@ -210,18 +206,6 @@ namespace armarx::armem::server::robot_state ARMARX_INFO << ss.str(); } - // parse config - if (std::filesystem::is_regular_file(robotUnit.configPath)) - { - ARMARX_INFO << "Found a configuration file at: " << robotUnit.configPath; - // A simple self-made parser for the config file. Extend it if you need to. - robotUnit.reader.configSensorMapping = RobotUnitReader::readConfig(robotUnit.configPath); - } - else - { - ARMARX_INFO << "The config path '" << robotUnit.configPath << "' is not valid."; - } - robotUnit.reader.task->start(); robotUnit.writer.task->start(); } diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h index c89a51ae47158d0c646a63f216f0a4ff397ba5d6..2cd2be9e3a5838f16b1a385e829416429b7869cd 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h @@ -35,13 +35,12 @@ #include <RobotAPI/libraries/armem/server/ComponentPlugin.h> #include <RobotAPI/libraries/armem_robot_state/server/description/Segment.h> -#include <RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h> #include <RobotAPI/libraries/armem_robot_state/server/localization/Segment.h> #include <RobotAPI/libraries/armem_robot_state/server/common/Visu.h> - -#include "RobotUnitData.h" -#include "RobotUnitReader.h" -#include "RobotStateWriter.h" +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h> +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h> +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h> +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h> namespace armarx::plugins @@ -49,7 +48,6 @@ namespace armarx::plugins class DebugObserverComponentPlugin; class RobotUnitComponentPlugin; } - namespace armarx::armem::server::robot_state { @@ -90,11 +88,6 @@ namespace armarx::armem::server::robot_state private: - /// Reads from `robotUnitDataQueue` and commits into memory. - //void convertRobotUnitStreamingDataToAronTask(); - /// Reads from `robotUnitDataQueue` and commits into memory. - //void storeConvertedRobotUnitDataInMemoryTask(); - void startRobotUnitStream(); void stopRobotUnitStream(); @@ -123,14 +116,13 @@ namespace armarx::armem::server::robot_state struct RobotUnit { int pollFrequency = 50; - std::string configPath = "NO CONFIG SET"; armarx::plugins::RobotUnitComponentPlugin* plugin = nullptr; - RobotUnitReader reader; - RobotStateWriter writer; + proprioception::RobotUnitReader reader; + proprioception::RobotStateWriter writer; // queue - std::queue<RobotUnitData> dataQueue; + std::queue<proprioception::RobotUnitData> dataQueue; mutable std::mutex dataMutex; }; RobotUnit robotUnit; diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.h deleted file mode 100644 index dc43dcddca3eb693fe5200dcc0a256a15be23cac..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include <map> -#include <memory> -#include <string> - -#include <RobotAPI/libraries/armem/core/Time.h> - - -namespace armarx::aron::datanavigator -{ - using DictNavigatorPtr = std::shared_ptr<class DictNavigator>; -} - -namespace armarx::armem::server::robot_state -{ - - struct RobotUnitData - { - struct RobotUnitDataGroup - { - armem::Time timestamp; - std::string name; - aron::datanavigator::DictNavigatorPtr data; - }; - - std::map<std::string, RobotUnitDataGroup> groups; - }; - -} - diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.cpp deleted file mode 100644 index 94dc94f8b1b0763dcf0e05e8dae75c05aa261896..0000000000000000000000000000000000000000 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.cpp +++ /dev/null @@ -1,202 +0,0 @@ -#include "RobotUnitReader.h" - -#include "aron_conversions.h" - -#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> -#include <RobotAPI/libraries/aron/core/navigator/data/primitive/Long.h> -#include <RobotAPI/libraries/aron/core/navigator/data/primitive/String.h> - -#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> - -#include <ArmarXCore/core/time/CycleUtil.h> -#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h> - -#include <SimoxUtility/algorithm/string/string_tools.h> - -#include <istream> -#include <filesystem> -#include <fstream> - - -namespace armarx::armem::server::robot_state -{ - - std::map<std::string, std::string> RobotUnitReader::readConfig(const std::string& configPath) - { - ARMARX_CHECK(std::filesystem::is_regular_file(configPath)); - - std::map<std::string, std::string> config; - - std::ifstream in(configPath); - std::string line; - while (std::getline(in, line)) - { - if (line.size() > 0) - { - if (simox::alg::starts_with(line, "//")) - { - continue; - } - - std::vector<std::string> mappings = simox::alg::split(line, ","); - for (const auto& mapping : mappings) - { - std::vector<std::string> split = simox::alg::split(mapping, "=>"); - if (split.size() < 2) - { - ARMARX_WARNING_S << "A mapping in the RobotUnitConfig file is not valid. Ignoring it: " << mapping; - continue; - } - for (unsigned int i = 0; i < split.size() - 1; ++i) - { - ARMARX_DEBUG_S << "Setting sensor value '" << split[i] << "' to group '" << split[split.size() - 1] << "'."; - config.emplace(split[i], split[split.size() - 1]); - } - } - } - } - - return config; - } - - - void RobotUnitReader::connect( - armarx::plugins::RobotUnitComponentPlugin& robotUnitPlugin, - armarx::plugins::DebugObserverComponentPlugin& debugObserverPlugin) - { - { - config.loggingNames.push_back(properties.sensorPrefix); - receiver = robotUnitPlugin.startDataSatreming(config); - description = receiver->getDataDescription(); - } - { - // Thread-local copy of debug observer helper. - debugObserver = DebugObserverHelper( - Logging::tag.tagName, debugObserverPlugin.getDebugObserver(), true - ); - } - } - - - void RobotUnitReader::run( - float pollFrequency, - std::queue<RobotUnitData>& dataQueue, - std::mutex& dataMutex) - { - CycleUtil cycle(static_cast<int>(1000.f / pollFrequency)); - while (task and not task->isStopped()) - { - if (std::optional<RobotUnitData> data = fetchAndGroupLatestRobotUnitData()) - { - std::lock_guard g{dataMutex}; - dataQueue.push(data.value()); - } - - if (debugObserver) - { - debugObserver->sendDebugObserverBatch(); - } - cycle.waitForCycleDuration(); - } - } - - - std::optional<RobotUnitData> RobotUnitReader::fetchAndGroupLatestRobotUnitData() - { - const std::optional<RobotUnitDataStreaming::TimeStep> data = fetchLatestData(); - if (not data.has_value()) - { - return std::nullopt; - } - - ARMARX_DEBUG << "RobotUnitData: Generating new aron map for current timestep"; - auto start = std::chrono::high_resolution_clock::now(); - - RobotUnitData convertedAndGroupedData; - for (const auto& [nameEntry, dataEntry] : description.entries) - { - std::string name = nameEntry; - std::string jointOrWhateverName = nameEntry; - - std::string groupName = ""; - if (auto it = configSensorMapping.find(name); it != configSensorMapping.end()) - { - groupName = it->second; - jointOrWhateverName = it->second; // ??? - } - else - { - const size_t first_dot_pos = name.find("."); - const size_t second_dot_pos = name.find(".", first_dot_pos + 1); // find second occurence of "." - if (second_dot_pos == std::string::npos) - { - ARMARX_WARNING << "Could not find a groupname for the sensor with name " << name << ". " - << "All sensors must be called sens.X.Y where X is the name of the group " - << "and Y is the actual sensor. Ignoring this sensor."; - continue; - } - - groupName = name.substr(0, second_dot_pos); - jointOrWhateverName = name.substr(first_dot_pos + 1, second_dot_pos - first_dot_pos - 1); - name = name.substr(second_dot_pos + 1); // remove the groupName, TODO check if +1 is valid - } - - auto it = convertedAndGroupedData.groups.find(groupName); - if (it == convertedAndGroupedData.groups.end()) - { - namespace adn = aron::datanavigator; - - // Generate new dict for the group - RobotUnitData::RobotUnitDataGroup group; - group.timestamp = armem::Time::microSeconds(data->timestampUSec); - group.name = groupName; - group.data = std::make_shared<adn::DictNavigator>(); - - group.data->addElement("EncoderGroupName", std::make_shared<adn::StringNavigator>(groupName)); - group.data->addElement("IterationId", std::make_shared<adn::LongNavigator>(data->iterationId)); - group.data->addElement("name", std::make_shared<adn::StringNavigator>(jointOrWhateverName)); - - auto r = convertedAndGroupedData.groups.emplace(groupName, group); - it = r.first; - } - - RobotUnitData::RobotUnitDataGroup& group = it->second; - const std::string escapedName = simox::alg::replace_all(name, ".", "/"); - group.data->addElement(escapedName, RobotUnitDataStreaming::toAron(data.value(), dataEntry)); - } - - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start); - ARMARX_DEBUG << "RobotUnitData: The total time needed to convert the data to aron is: " << duration; - - if (debugObserver) - { - debugObserver->setDebugObserverDatafield("RobotUnitReader | t Read+Group [ms]", duration.count() / 1000.f); - } - - return convertedAndGroupedData; - } - - - std::optional<RobotUnitDataStreaming::TimeStep> RobotUnitReader::fetchLatestData() - { - std::deque<RobotUnitDataStreaming::TimeStep>& data = receiver->getDataBuffer(); - if (debugObserver) - { - debugObserver->setDebugObserverDatafield("RobotUnitReader | Buffer Size", data.size()); - } - if (data.empty()) - { - return std::nullopt; - } - else - { - RobotUnitDataStreaming::TimeStep currentTimestep = data.back(); - data.clear(); - return currentTimestep; - } - } - - -} - diff --git a/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp b/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp index c0a9fd2f33211d9a130f236b9350836b75b51466..6c86b6afa76fe043b4dea16affb609f7db049caf 100644 --- a/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp +++ b/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp @@ -29,7 +29,8 @@ #include <ArmarXCore/core/logging/Logging.h> #include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/server/MemoryRemoteGui.h> #include <RobotAPI/libraries/armem_skills/aron_conversions.h> @@ -70,7 +71,7 @@ namespace armarx workingMemory.name() = p.memoryName; { - armarx::armem::wm::CoreSegment& c = workingMemory.addCoreSegment(p.statechartCoreSegmentName, armarx::armem::arondto::Statechart::Transition::toAronType()); + armarx::armem::server::wm::CoreSegment& c = workingMemory.addCoreSegment(p.statechartCoreSegmentName, armarx::armem::arondto::Statechart::Transition::toAronType()); c.addProviderSegment("Transitions", armarx::armem::arondto::Statechart::Transition::toAronType()); } } diff --git a/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidget.ui b/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidget.ui index 819331b5f27f58c036bb867e8a17dda8a0c8b977..58bead75ff15f27b039c50e712c1406f83e2fa32 100644 --- a/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidget.ui +++ b/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidget.ui @@ -34,7 +34,7 @@ </layout> </item> <item> - <layout class="QHBoxLayout" name="ltmControlWidgetLayout"> + <layout class="QHBoxLayout" name="diskControlWidgetLayout"> <property name="spacing"> <number>6</number> </property> diff --git a/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidgetController.cpp b/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidgetController.cpp index 83d1d8743a3b6ed0ebfeca5c704c96334b22108a..ff79f501c35016154272c659db18233caf39a9d6 100644 --- a/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ArMemMemoryViewer/ArMemMemoryViewerWidgetController.cpp @@ -43,7 +43,6 @@ namespace armarx viewer = std::make_unique<MemoryViewer>( widget.updateWidgetLayout, - widget.ltmControlWidgetLayout, widget.memoryGroupBox, widget.treesLayout, @@ -51,9 +50,11 @@ namespace armarx widget.instanceGroupBox, widget.treesLayout, + widget.diskControlWidgetLayout, + widget.statusLabel ); - viewer->setLogTag(getName()); + viewer->setLogTag("ArMem Memory Viewer"); armarx::gui::useSplitter(widget.treesLayout); diff --git a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp index dffe7711debc728d596f5ecd7cd851df2527e120..adb7c77bd1e04491b7e16ecb1599119df0ee2ce6 100644 --- a/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/ObjectPoseGui/ObjectPoseGuiWidgetController.cpp @@ -201,7 +201,7 @@ namespace armarx { bool expand = item->childCount() == 0; - TreeWidgetBuilder builder(objectPoses); + TreeWidgetBuilder<objpose::ObjectPose> builder; builder.setNameFn([](const objpose::ObjectPose & pose) { return pose.objectID.str(); @@ -245,7 +245,7 @@ namespace armarx return true; }); - builder.updateTree(item, objectPoses); + builder.updateTreeWithContainer(item, objectPoses); if (expand) { @@ -299,7 +299,7 @@ namespace armarx { (void) dataset; - TreeWidgetBuilder builder(datasetData); + TreeWidgetBuilder<std::pair<std::string, std::string>> builder; builder.setCompareFn([](const std::pair<std::string, std::string>& lhs, QTreeWidgetItem * item) { auto rhs = std::make_pair(item->text(0).toStdString(), item->text(1).toStdString()); @@ -324,7 +324,7 @@ namespace armarx } return true; }); - builder.updateTree(datasetItem, datasetData); + builder.updateTreeWithContainer(datasetItem, datasetData); return true; }); diff --git a/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice b/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice index bb1e7f221856b4505184fdeb380683c6f8cb917b..68a3ff2ef6fc06114eaff88dcb3e0393002b1160 100644 --- a/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice +++ b/source/RobotAPI/interface/armem/server/LoadingMemoryInterface.ice @@ -11,7 +11,7 @@ module armarx { interface LoadingMemoryInterface { - armem::query::data::Result load(armem::query::data::Input query); + }; }; }; diff --git a/source/RobotAPI/interface/objectpose/ObjectPoseStorageInterface.ice b/source/RobotAPI/interface/objectpose/ObjectPoseStorageInterface.ice index 3097f6c7596fd3fdf82aedd9d974712081cf503c..1dfe8b9f299d24684a0acdd3d6f53ed61063d397 100644 --- a/source/RobotAPI/interface/objectpose/ObjectPoseStorageInterface.ice +++ b/source/RobotAPI/interface/objectpose/ObjectPoseStorageInterface.ice @@ -81,7 +81,7 @@ module armarx }; struct AttachObjectToRobotNodeOutput { - bool success; + bool success = false; data::ObjectAttachmentInfo attachment; }; @@ -99,7 +99,7 @@ module armarx struct DetachObjectFromRobotNodeOutput { /// Whether the object was attached before. - bool wasAttached; + bool wasAttached = false; }; struct DetachAllObjectsFromRobotNodesInput @@ -113,7 +113,7 @@ module armarx struct DetachAllObjectsFromRobotNodesOutput { /// Number of objects that have been detached. - int numDetached; + int numDetached = 0; }; struct AgentFrames diff --git a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp index 90e2cb1f2278cdac7b6bdcc469314a2ff84a9f3a..e5077dbad6abca53970f20a82f2b4c7534333cc7 100644 --- a/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp +++ b/source/RobotAPI/libraries/RobotAPINJointControllers/DMPController/NJointTaskSpaceImpedanceDMPController.cpp @@ -1,6 +1,9 @@ #include "NJointTaskSpaceImpedanceDMPController.h" #include <ArmarXCore/core/ArmarXObjectScheduler.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/logging/Logging.h> + namespace armarx { diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index ef5f227686bf252f3f7941fd3f0d96aed72442d1..1fdf140a9507cd439b45226e4886ec89fe0ccfcf 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -17,6 +17,9 @@ set(LIBS ArmarXCoreInterfaces ArmarXCore RemoteGui aron + + # Needed for LTM + RobotAPI::aron::converter::json ${LIBMONGOCXX_LIBRARIES} ${LIBBSONCXX_LIBRARIES} ) @@ -25,54 +28,34 @@ set(LIB_FILES core/Commit.cpp core/MemoryID.cpp core/MemoryID_operators.cpp + core/operations.cpp core/SuccessHeader.cpp core/Time.cpp - core/ice_conversions.cpp core/aron_conversions.cpp + core/ice_conversions.cpp core/json_conversions.cpp core/base/detail/MemoryItem.cpp - core/base/detail/MaxHistorySize.cpp core/base/detail/MemoryContainerBase.cpp - core/base/detail/EntityContainerBase.cpp core/base/detail/AronTyped.cpp + core/base/detail/iteration_mixins.cpp + core/base/detail/lookup_mixins.cpp + core/base/detail/negative_index_semantics.cpp - core/base/CoreSegmentBase.cpp - core/base/EntityBase.cpp + # core/base/CoreSegmentBase.cpp + # core/base/EntityBase.cpp core/base/EntityInstanceBase.cpp core/base/EntitySnapshotBase.cpp - core/base/MemoryBase.cpp - core/base/ProviderSegmentBase.cpp - - core/workingmemory/ice_conversions.cpp - core/workingmemory/detail/CopyWithoutData.cpp - core/workingmemory/CoreSegment.cpp - core/workingmemory/Entity.cpp - core/workingmemory/EntityInstance.cpp - core/workingmemory/EntitySnapshot.cpp - core/workingmemory/Memory.cpp - core/workingmemory/ProviderSegment.cpp - core/workingmemory/ice_conversions.cpp - core/workingmemory/json_conversions.cpp - core/workingmemory/entityInstance_conversions.cpp - core/workingmemory/visitor/Visitor.cpp - core/workingmemory/visitor/FunctionalVisitor.cpp - - core/longtermmemory/CoreSegment.cpp - core/longtermmemory/Entity.cpp - core/longtermmemory/EntityInstance.cpp - core/longtermmemory/EntitySnapshot.cpp - core/longtermmemory/Memory.cpp - core/longtermmemory/ProviderSegment.cpp - core/longtermmemory/mongodb/MongoDBConnectionManager.cpp - - core/diskmemory/TypeIO.cpp - core/diskmemory/CoreSegment.cpp - core/diskmemory/Entity.cpp - core/diskmemory/EntityInstance.cpp - core/diskmemory/EntitySnapshot.cpp - core/diskmemory/Memory.cpp - core/diskmemory/ProviderSegment.cpp + # core/base/MemoryBase.cpp + # core/base/ProviderSegmentBase.cpp + core/base/ice_conversions.cpp + + core/wm/memory_definitions.cpp + core/wm/aron_conversions.cpp + core/wm/ice_conversions.cpp + core/wm/detail/data_lookup_mixins.cpp + core/wm/visitor/Visitor.cpp + core/wm/visitor/FunctionalVisitor.cpp core/error/ArMemError.cpp core/error/mns.cpp @@ -98,6 +81,15 @@ set(LIB_FILES server/MemoryRemoteGui.cpp server/RemoteGuiAronDataVisitor.cpp + server/ltm/LongtermMemoryBase.cpp + server/ltm/disk/MemoryManager.cpp + server/ltm/mongodb/MemoryManager.cpp + server/ltm/mongodb/ConnectionManager.cpp + + server/wm/memory_definitions.cpp + server/wm/ice_conversions.cpp + server/wm/detail/MaxHistorySize.cpp + server/segment/Segment.cpp server/segment/SpecializedSegment.cpp @@ -107,23 +99,8 @@ set(LIB_FILES server/query_proc/base/CoreSegmentQueryProcessorBase.cpp server/query_proc/base/MemoryQueryProcessorBase.cpp - server/query_proc/workingmemory/BaseQueryProcessor.cpp - server/query_proc/workingmemory/EntityQueryProcessor.cpp - server/query_proc/workingmemory/ProviderSegmentQueryProcessor.cpp - server/query_proc/workingmemory/CoreSegmentQueryProcessor.cpp - server/query_proc/workingmemory/MemoryQueryProcessor.cpp - - server/query_proc/longtermmemory/BaseQueryProcessor.cpp - server/query_proc/longtermmemory/EntityQueryProcessor.cpp - server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.cpp - server/query_proc/longtermmemory/CoreSegmentQueryProcessor.cpp - server/query_proc/longtermmemory/MemoryQueryProcessor.cpp - - server/query_proc/diskmemory/BaseQueryProcessor.cpp - server/query_proc/diskmemory/EntityQueryProcessor.cpp - server/query_proc/diskmemory/ProviderSegmentQueryProcessor.cpp - server/query_proc/diskmemory/CoreSegmentQueryProcessor.cpp - server/query_proc/diskmemory/MemoryQueryProcessor.cpp + server/query_proc/ltm.cpp + server/query_proc/wm.cpp mns/MemoryNameSystem.cpp mns/ComponentPlugin.cpp @@ -133,15 +110,18 @@ set(LIB_FILES set(LIB_HEADERS core.h + core/forward_declarations.h + core/Commit.h core/DataMode.h core/MemoryID.h core/MemoryID_operators.h + core/operations.h core/SuccessHeader.h core/Time.h core/aron_conversions.h - core/json_conversions.h core/ice_conversions.h + core/json_conversions.h core/ice_conversions_boost_templates.h core/ice_conversions_templates.h @@ -150,10 +130,12 @@ set(LIB_HEADERS core/error/mns.h core/base/detail/MemoryItem.h - core/base/detail/MaxHistorySize.h core/base/detail/MemoryContainerBase.h - core/base/detail/EntityContainerBase.h core/base/detail/AronTyped.h + core/base/detail/derived.h + core/base/detail/iteration_mixins.h + core/base/detail/lookup_mixins.h + core/base/detail/negative_index_semantics.h core/base/CoreSegmentBase.h core/base/EntityBase.h @@ -161,41 +143,18 @@ set(LIB_HEADERS core/base/EntitySnapshotBase.h core/base/MemoryBase.h core/base/ProviderSegmentBase.h + core/base/ice_conversions.h - core/workingmemory/detail/CopyWithoutData.h - core/workingmemory/CoreSegment.h - core/workingmemory/Entity.h - core/workingmemory/EntityInstance.h - core/workingmemory/EntitySnapshot.h - core/workingmemory/Memory.h - core/workingmemory/ProviderSegment.h - core/workingmemory/ice_conversions.h - core/workingmemory/json_conversions.h - core/workingmemory/entityInstance_conversions.h - core/workingmemory/visitor.h - core/workingmemory/visitor/Visitor.h - core/workingmemory/visitor/FunctionalVisitor.h - - core/longtermmemory/CoreSegment.h - core/longtermmemory/Entity.h - core/longtermmemory/EntityInstance.h - core/longtermmemory/EntitySnapshot.h - core/longtermmemory/Memory.h - core/longtermmemory/ProviderSegment.h - core/longtermmemory/mongodb/MongoDBConnectionManager.h - - core/diskmemory/TypeIO.h - core/diskmemory/CoreSegment.h - core/diskmemory/Entity.h - core/diskmemory/EntityInstance.h - core/diskmemory/EntitySnapshot.h - core/diskmemory/Memory.h - core/diskmemory/ProviderSegment.h - - core/ice_conversions_templates.h - core/ice_conversions.h + core/wm.h + core/wm/memory_definitions.h + core/wm/aron_conversions.h + core/wm/ice_conversions.h + core/wm/detail/data_lookup_mixins.h + core/wm/visitor/Visitor.h + core/wm/visitor/FunctionalVisitor.h client.h + client/forward_declarations.h client/ComponentPlugin.h client/MemoryNameSystem.h client/MemoryNameSystemComponentPlugin.h @@ -217,45 +176,42 @@ set(LIB_HEADERS client/util/SimpleWriterBase.h server.h + server/forward_declarations.h + server/ComponentPlugin.h server/MemoryToIceAdapter.h server/MemoryRemoteGui.h server/RemoteGuiAronDataVisitor.h + server/ltm/LongtermMemoryBase.h + server/ltm/disk/MemoryManager.h + server/ltm/mongodb/MemoryManager.h + server/ltm/mongodb/ConnectionManager.h + + server/wm/memory_definitions.h + server/wm/ice_conversions.h + server/wm/detail/MaxHistorySize.h + server/segment/Segment.h server/segment/SpecializedSegment.h server/query_proc.h + + server/query_proc/base.h server/query_proc/base/BaseQueryProcessorBase.h server/query_proc/base/EntityQueryProcessorBase.h server/query_proc/base/ProviderSegmentQueryProcessorBase.h server/query_proc/base/CoreSegmentQueryProcessorBase.h server/query_proc/base/MemoryQueryProcessorBase.h - server/query_proc/workingmemory/BaseQueryProcessor.h - server/query_proc/workingmemory/EntityQueryProcessor.h - server/query_proc/workingmemory/ProviderSegmentQueryProcessor.h - server/query_proc/workingmemory/CoreSegmentQueryProcessor.h - server/query_proc/workingmemory/MemoryQueryProcessor.h - - server/query_proc/longtermmemory/BaseQueryProcessor.h - server/query_proc/longtermmemory/EntityQueryProcessor.h - server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.h - server/query_proc/longtermmemory/CoreSegmentQueryProcessor.h - server/query_proc/longtermmemory/MemoryQueryProcessor.h - - server/query_proc/diskmemory/BaseQueryProcessor.h - server/query_proc/diskmemory/EntityQueryProcessor.h - server/query_proc/diskmemory/ProviderSegmentQueryProcessor.h - server/query_proc/diskmemory/CoreSegmentQueryProcessor.h - server/query_proc/diskmemory/MemoryQueryProcessor.h + server/query_proc/ltm.h + server/query_proc/wm.h mns.h mns/MemoryNameSystem.h mns/ComponentPlugin.h util/util.h - ) armarx_add_library( diff --git a/source/RobotAPI/libraries/armem/client/MemoryNameSystem.cpp b/source/RobotAPI/libraries/armem/client/MemoryNameSystem.cpp index 4395b55fd45e6bdb019ee1285fbc5cc3dac648f9..79140344ee99a143e29bcc655d6e1ed23d77b041 100644 --- a/source/RobotAPI/libraries/armem/client/MemoryNameSystem.cpp +++ b/source/RobotAPI/libraries/armem/client/MemoryNameSystem.cpp @@ -39,7 +39,32 @@ namespace armarx::armem::client if (result.success) { - this->servers = result.proxies; + for (const auto& [name, proxy] : result.proxies) + { + auto [it, inserted] = servers.try_emplace(name, proxy); + // inserted: OK + if (not inserted) + { + // Compare ice identities, update if it changed. + if (it->second->ice_getIdentity() != proxy->ice_getIdentity()) + { + // Replace old proxy by new one. + it->second = proxy; + } + } + } + // Remove all entries which are not there anymore. + for (auto it = servers.begin(); it != servers.end();) + { + if (result.proxies.count(it->first) == 0) + { + it = servers.erase(it); + } + else + { + ++it; + } + } } else { @@ -272,11 +297,11 @@ namespace armarx::armem::client { if (id.hasInstanceIndex()) { - result[id] = queryResult.memory.getEntityInstance(id); + result[id] = queryResult.memory.getInstance(id); } else if (id.hasTimestamp()) { - result[id] = queryResult.memory.getEntitySnapshot(id).getInstance(0); + result[id] = queryResult.memory.getSnapshot(id).getInstance(0); } else if (id.hasEntityName()) { diff --git a/source/RobotAPI/libraries/armem/client/Query.cpp b/source/RobotAPI/libraries/armem/client/Query.cpp index 1777839f24e6f2522e7f0efb9670f79ff463231b..52000cf3d25f118ea80cf7dfc7c5ea5830cb36f8 100644 --- a/source/RobotAPI/libraries/armem/client/Query.cpp +++ b/source/RobotAPI/libraries/armem/client/Query.cpp @@ -1,6 +1,6 @@ #include "Query.h" -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> namespace armarx::armem::client { diff --git a/source/RobotAPI/libraries/armem/client/Query.h b/source/RobotAPI/libraries/armem/client/Query.h index c6bb445bfe8ee9a3547ae621aa8c16ec8e0e2be9..56b9caa79b06bf598836a8cb4df5ab492177604a 100644 --- a/source/RobotAPI/libraries/armem/client/Query.h +++ b/source/RobotAPI/libraries/armem/client/Query.h @@ -3,10 +3,10 @@ // RobotAPI #include <RobotAPI/interface/armem/query.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem/core/SuccessHeader.h> #include <RobotAPI/libraries/armem/core/DataMode.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> namespace armarx::armem::client::query diff --git a/source/RobotAPI/libraries/armem/client/Reader.cpp b/source/RobotAPI/libraries/armem/client/Reader.cpp index a589512f13bcdaaa2536f597a54474eb72f96574..2662f86e0d7f36827f9d5cff67f963ac29613de9 100644 --- a/source/RobotAPI/libraries/armem/client/Reader.cpp +++ b/source/RobotAPI/libraries/armem/client/Reader.cpp @@ -5,10 +5,8 @@ #include <ArmarXCore/core/logging/Logging.h> #include <RobotAPI/libraries/armem/core/MemoryID_operators.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/workingmemory/visitor.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem/util/util.h> #include "query/Builder.h" @@ -117,7 +115,7 @@ namespace armarx::armem::client { try { - wm::EntitySnapshot& snapshot = result.memory.getEntitySnapshot(snapshotID); + wm::EntitySnapshot& snapshot = result.memory.getSnapshot(snapshotID); return snapshot; } catch (const armem::error::ArMemError&) @@ -162,30 +160,25 @@ namespace armarx::armem::client } - struct FindLatestSnapshotVisitor : public wm::Visitor - { - std::optional<wm::EntitySnapshot> latest = std::nullopt; - - bool visitEnter(const wm::EntitySnapshot& snapshot) override; - }; - bool FindLatestSnapshotVisitor::visitEnter(const wm::EntitySnapshot& snapshot) - { - if (not latest.has_value() or snapshot.time() < latest->time()) - { - latest = snapshot; - } - return true; - } - - std::optional<wm::EntitySnapshot> Reader::getLatestSnapshotIn(const MemoryID& id, DataMode dataMode) const { client::QueryResult result = getLatestSnapshotsIn(id, dataMode); if (result.success) { - FindLatestSnapshotVisitor visitor; - visitor.applyTo(result.memory); - return visitor.latest; + std::optional<wm::EntitySnapshot> latest = std::nullopt; + result.memory.forEachEntity([&latest](const wm::Entity & entity) + { + if (not entity.empty()) + { + const wm::EntitySnapshot& snapshot = entity.getLatestSnapshot(); + if (not latest.has_value() or latest->time() < snapshot.time()) + { + latest = snapshot; + } + } + return true; + }); + return latest; } else { diff --git a/source/RobotAPI/libraries/armem/client/Reader.h b/source/RobotAPI/libraries/armem/client/Reader.h index 1d69491d2cf33e7bdf460634ef69bc26db4bb52b..e9ee1b69afef7d2befdaaf0169b0117a13edbad5 100644 --- a/source/RobotAPI/libraries/armem/client/Reader.h +++ b/source/RobotAPI/libraries/armem/client/Reader.h @@ -8,8 +8,7 @@ // RobotAPI #include <RobotAPI/interface/armem/server/ReadingMemoryInterface.h> #include <RobotAPI/interface/armem/server/StoringMemoryInterface.h> -#include <RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.h> -// #include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include "Query.h" diff --git a/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.cpp b/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.cpp index 3ac9f7438408460c157f7484d313ef0d6a3808a6..c2e4d5ed6df5c3e12c0d3e2ada0b93c91e232133 100644 --- a/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.cpp +++ b/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.cpp @@ -45,16 +45,4 @@ namespace armarx::armem::client memoryReader.setReadingMemory(memory); } - - armem::data::WaitForMemoryResult ReaderComponentPluginUser::useMemoryServer(const std::string& memoryName) - { - armem::data::WaitForMemoryResult result; - result.proxy = memoryNameSystem.useServer(memoryName); - if (result.proxy) - { - setReadingMemoryServer(result.proxy); - } - return result; - } - } diff --git a/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.h b/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.h index c0139e609e830e2c07517837f6cc3d48aff434db..059c5f93d5e8d872ecdc9bff86593db2c3de1163 100644 --- a/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.h +++ b/source/RobotAPI/libraries/armem/client/ReaderComponentPlugin.h @@ -50,10 +50,6 @@ namespace armarx::armem::client ReaderComponentPluginUser(); ~ReaderComponentPluginUser() override; - [[deprecated("Use memoryNameSystem member instead of function inherited by ReaderComponentPluginUser.")]] - virtual armem::data::WaitForMemoryResult useMemoryServer(const std::string& memoryName) override; - using MemoryNameSystemComponentPluginUser::useMemoryServer; - protected: diff --git a/source/RobotAPI/libraries/armem/client/Writer.cpp b/source/RobotAPI/libraries/armem/client/Writer.cpp index 2021f3a189367dc9134d60ce1dd597cb8513dfaf..d9f64e4ca4ea068a9beb01807aa1c8fb4f7075be 100644 --- a/source/RobotAPI/libraries/armem/client/Writer.cpp +++ b/source/RobotAPI/libraries/armem/client/Writer.cpp @@ -12,17 +12,23 @@ namespace armarx::armem::client { } - data::AddSegmentResult Writer::addSegment(const std::string& coreSegmentName, const std::string& providerSegmentName) + data::AddSegmentResult Writer::addSegment(const std::string& coreSegmentName, const std::string& providerSegmentName, bool clearWhenExists) { data::AddSegmentInput input; input.coreSegmentName = coreSegmentName; input.providerSegmentName = providerSegmentName; + input.clearWhenExists = clearWhenExists; return addSegment(input); } - data::AddSegmentResult Writer::addSegment(const std::pair<std::string, std::string>& names) + data::AddSegmentResult Writer::addSegment(const MemoryID& providerSegmentID, bool clearWhenExists) { - return addSegment(names.first, names.second); + return addSegment(providerSegmentID.coreSegmentName, providerSegmentID.providerSegmentName, clearWhenExists); + } + + data::AddSegmentResult Writer::addSegment(const std::pair<std::string, std::string>& names, bool clearWhenExists) + { + return addSegment(names.first, names.second, clearWhenExists); } data::AddSegmentResult Writer::addSegment(const data::AddSegmentInput& input) diff --git a/source/RobotAPI/libraries/armem/client/Writer.h b/source/RobotAPI/libraries/armem/client/Writer.h index dd953fa368d9a46363677d3bc39bc71f8da2a9ce..c63ce8a6fc11ae221caa17dab95dde1c205e1186 100644 --- a/source/RobotAPI/libraries/armem/client/Writer.h +++ b/source/RobotAPI/libraries/armem/client/Writer.h @@ -2,7 +2,7 @@ #include <RobotAPI/interface/armem/server/WritingMemoryInterface.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> namespace armarx::armem::client @@ -32,8 +32,9 @@ namespace armarx::armem::client Writer(server::WritingMemoryInterfacePrx memory = nullptr); - data::AddSegmentResult addSegment(const std::string& coreSegmentName, const std::string& providerSegmentName); - data::AddSegmentResult addSegment(const std::pair<std::string, std::string>& names); + data::AddSegmentResult addSegment(const std::string& coreSegmentName, const std::string& providerSegmentName, bool clearWhenExists = false); + data::AddSegmentResult addSegment(const MemoryID& providerSegmentID, bool clearWhenExists = false); + data::AddSegmentResult addSegment(const std::pair<std::string, std::string>& names, bool clearWhenExists = false); data::AddSegmentResult addSegment(const data::AddSegmentInput& input); data::AddSegmentsResult addSegments(const data::AddSegmentsInput& input); diff --git a/source/RobotAPI/libraries/armem/client/forward_declarations.h b/source/RobotAPI/libraries/armem/client/forward_declarations.h new file mode 100644 index 0000000000000000000000000000000000000000..205f56634456d4179d1baac494837d1ecdfe7069 --- /dev/null +++ b/source/RobotAPI/libraries/armem/client/forward_declarations.h @@ -0,0 +1,24 @@ +#pragma once + +// #include <RobotAPI/libraries/armem/core/forward_declarations.h> + + + +namespace armarx::armem::client::query +{ + class Builder; +} +namespace armarx::armem::client +{ + class ComponentPluginUser; + class MemoryNameSystem; + + class Reader; + class Writer; + + using QueryBuilder = query::Builder; + struct QueryInput; + struct QueryResult; + +} + diff --git a/source/RobotAPI/libraries/armem/client/query/Builder.cpp b/source/RobotAPI/libraries/armem/client/query/Builder.cpp index 59ae0b7c082bc25a8b1084f8ac49611df107d132..8637055e5b850494f15c10255b29de94785cec08 100644 --- a/source/RobotAPI/libraries/armem/client/query/Builder.cpp +++ b/source/RobotAPI/libraries/armem/client/query/Builder.cpp @@ -45,6 +45,25 @@ namespace armarx::armem::client::query return _addChild(selector); } + + void Builder::all() + { + coreSegments().all() + .providerSegments().all() + .entities().all() + .snapshots().all(); + } + + + void Builder::allLatest() + { + coreSegments().all() + .providerSegments().all() + .entities().all() + .snapshots().latest(); + } + + void Builder::allInCoreSegment(const MemoryID& coreSegmentID) { coreSegments().withName(coreSegmentID.coreSegmentName) diff --git a/source/RobotAPI/libraries/armem/client/query/Builder.h b/source/RobotAPI/libraries/armem/client/query/Builder.h index dc6147ef03e7de81518e64bddc9bb486f0062b07..3b52aa4b1945d6a2bef62932403013670843f94a 100644 --- a/source/RobotAPI/libraries/armem/client/query/Builder.h +++ b/source/RobotAPI/libraries/armem/client/query/Builder.h @@ -42,6 +42,11 @@ namespace armarx::armem::client::query // Short hands for common queries + /// Get all snapshots from all entities in all segments. + void all(); + /// Get all latest snapshots from entities in all segments. + void allLatest(); + /// Get all snapshots from all entities in all provider segments in a core segment. void allInCoreSegment(const MemoryID& coreSegmentID); /// Get latest snapshots from all entities in all provider segments in a core segment. diff --git a/source/RobotAPI/libraries/armem/client/query/selectors.cpp b/source/RobotAPI/libraries/armem/client/query/selectors.cpp index 98e79d06ab3642b25bee40e89daf728ab3dfee2b..ecd3f8c5f4a1b3ed0a7dde04c8dc1ac8a06b5265 100644 --- a/source/RobotAPI/libraries/armem/client/query/selectors.cpp +++ b/source/RobotAPI/libraries/armem/client/query/selectors.cpp @@ -1,7 +1,7 @@ #include "selectors.h" #include "RobotAPI/libraries/armem/core/ice_conversions.h" -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> namespace dq = ::armarx::armem::query::data; diff --git a/source/RobotAPI/libraries/armem/core.h b/source/RobotAPI/libraries/armem/core.h index c8ddee31ce054decbc8fdc33edf7959502601da7..11abe789b8cd0aef6ab2d9017c96cb1237cc5040 100644 --- a/source/RobotAPI/libraries/armem/core.h +++ b/source/RobotAPI/libraries/armem/core.h @@ -6,13 +6,7 @@ #include "core/MemoryID.h" #include "core/Time.h" -#include "core/workingmemory/Memory.h" -#include "core/workingmemory/CoreSegment.h" -#include "core/workingmemory/ProviderSegment.h" -#include "core/workingmemory/Entity.h" -#include "core/workingmemory/EntitySnapshot.h" -#include "core/workingmemory/EntityInstance.h" -#include "core/workingmemory/ice_conversions.h" +#include "core/wm.h" namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/core/Commit.cpp b/source/RobotAPI/libraries/armem/core/Commit.cpp index fed7ed660b450bb0865a018f9c8d7119dd96a7b4..db5f1b630e84ff276901f3321dd2a3fbbccc0442 100644 --- a/source/RobotAPI/libraries/armem/core/Commit.cpp +++ b/source/RobotAPI/libraries/armem/core/Commit.cpp @@ -1,9 +1,11 @@ #include "Commit.h" -#include <SimoxUtility/algorithm/apply.hpp> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <SimoxUtility/algorithm/apply.hpp> + namespace armarx::armem { @@ -12,7 +14,7 @@ namespace armarx::armem { return os << "Entity update: " << "\n- success: \t" << rhs.entityID - << "\n- timestamp: \t" << rhs.timeCreated + << "\n- timestamp: \t" << toDateTimeMilliSeconds(rhs.timeCreated) << "\n- #instances: \t" << rhs.instancesData.size() << "\n" ; @@ -23,7 +25,7 @@ namespace armarx::armem return os << "Entity update result: " << "\n- success: \t" << (rhs.success ? "true" : "false") << "\n- snapshotID: \t" << rhs.snapshotID - << "\n- time arrived: \t" << rhs.timeArrived.toDateTime() + << "\n- time arrived: \t" << toDateTimeMilliSeconds(rhs.timeArrived) << "\n- error message: \t" << rhs.errorMessage << "\n" ; diff --git a/source/RobotAPI/libraries/armem/core/Commit.h b/source/RobotAPI/libraries/armem/core/Commit.h index fc69a4b53dcaecc52d703aa42c00405f4162825f..685886a7f27b5d43b78cd0195693e32c3a58fffa 100644 --- a/source/RobotAPI/libraries/armem/core/Commit.h +++ b/source/RobotAPI/libraries/armem/core/Commit.h @@ -1,11 +1,12 @@ #pragma once -#include <vector> +#include <RobotAPI/libraries/armem/core/Time.h> +#include <RobotAPI/libraries/armem/core/MemoryID.h> -#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> -#include "../core/MemoryID.h" -#include "../core/Time.h" +#include <memory> +#include <vector> namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/core/MemoryID.cpp b/source/RobotAPI/libraries/armem/core/MemoryID.cpp index f8d7c832401a4ebf335347c67745c1ce82af4e03..f3b8c662ddef099b18dc09e03a81f21bd7de2bde 100644 --- a/source/RobotAPI/libraries/armem/core/MemoryID.cpp +++ b/source/RobotAPI/libraries/armem/core/MemoryID.cpp @@ -245,6 +245,31 @@ namespace armarx::armem return id; } + MemoryID MemoryID::removeLeafItem() const + { + if (instanceIndex != -1) + { + return getEntitySnapshotID(); + } + if (timestamp != Time::microSeconds(-1)) + { + return getEntityID(); + } + if (!entityName.empty()) + { + return getProviderSegmentID(); + } + if (!providerSegmentName.empty()) + { + return getCoreSegmentID(); + } + if (!coreSegmentName.empty()) + { + return getMemoryID(); + } + return {}; // return empty if already empty. Client needs to check (Avoids using optional as additional include) + } + void MemoryID::setMemoryID(const MemoryID& id) { memoryName = id.memoryName; diff --git a/source/RobotAPI/libraries/armem/core/MemoryID.h b/source/RobotAPI/libraries/armem/core/MemoryID.h index f9d3d5b58ca4670a8abb4333e417a9a2109843ae..d583e752258d69e8271e30be2a2d091493aa18cc 100644 --- a/source/RobotAPI/libraries/armem/core/MemoryID.h +++ b/source/RobotAPI/libraries/armem/core/MemoryID.h @@ -138,7 +138,11 @@ namespace armarx::armem MemoryID getEntitySnapshotID() const; MemoryID getEntityInstanceID() const; - // Slice setters: Set upper part of the ID. + // Slice getter: remove the last set name + MemoryID removeLeafItem() const; + + + // Slice setters: Set part of the ID. void setMemoryID(const MemoryID& id); void setCoreSegmentID(const MemoryID& id); diff --git a/source/RobotAPI/libraries/armem/core/aron_conversions.cpp b/source/RobotAPI/libraries/armem/core/aron_conversions.cpp index 1f8e33c403eaeb3570d6848616d556772170e929..4eafef35a33f746b5c8b898598ac6273cce7030e 100644 --- a/source/RobotAPI/libraries/armem/core/aron_conversions.cpp +++ b/source/RobotAPI/libraries/armem/core/aron_conversions.cpp @@ -1,5 +1,8 @@ #include "aron_conversions.h" +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/aron/MemoryID.aron.generated.h> + void armarx::armem::fromAron(const arondto::MemoryID& dto, MemoryID& bo) { diff --git a/source/RobotAPI/libraries/armem/core/aron_conversions.h b/source/RobotAPI/libraries/armem/core/aron_conversions.h index 8688c3b3fdf5dcb2ce9684960ab3a98449c8c13a..064aafe8d7bd4ea964aa1e020ca692d8eed45bf2 100644 --- a/source/RobotAPI/libraries/armem/core/aron_conversions.h +++ b/source/RobotAPI/libraries/armem/core/aron_conversions.h @@ -1,7 +1,8 @@ #pragma once -#include <RobotAPI/libraries/armem/core/MemoryID.h> -#include <RobotAPI/libraries/armem/aron/MemoryID.aron.generated.h> +#include "forward_declarations.h" + +#include <ostream> namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h b/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h index 5e22ba7036a7d0b94a4a329e3d73e591b29a9543..f4d0b530f43afcfa7f7aeba95389bdb8a5371918 100644 --- a/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h +++ b/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h @@ -5,8 +5,9 @@ #include "ProviderSegmentBase.h" #include "detail/AronTyped.h" -#include "detail/EntityContainerBase.h" -#include "detail/MaxHistorySize.h" +#include "detail/MemoryContainerBase.h" +#include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base @@ -17,11 +18,16 @@ namespace armarx::armem::base */ template <class _ProviderSegmentT, class _Derived> class CoreSegmentBase : - public detail::EntityContainerBase<_ProviderSegmentT, typename _ProviderSegmentT::EntityT, _Derived>, - public detail::MaxHistorySize, - public detail::AronTyped + public detail::MemoryContainerBase<std::map<std::string, _ProviderSegmentT>, _Derived> + , public detail::AronTyped + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::ForEachEntitySnapshotMixin<_Derived> + , public detail::ForEachEntityMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> + , public detail::GetFindSnapshotMixin<_Derived> + , public detail::GetFindEntityMixin<_Derived> { - using Base = detail::EntityContainerBase<_ProviderSegmentT, typename _ProviderSegmentT::EntityT, _Derived>; + using Base = detail::MemoryContainerBase<std::map<std::string, _ProviderSegmentT>, _Derived>; public: @@ -33,6 +39,9 @@ namespace armarx::armem::base using EntitySnapshotT = typename EntityT::EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = ProviderSegmentT; + + struct UpdateResult { armarx::armem::UpdateType coreSegmentUpdateType; @@ -50,20 +59,21 @@ namespace armarx::armem::base {} }; + public: CoreSegmentBase() { } - CoreSegmentBase(const std::string& name, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : - CoreSegmentBase(MemoryID().withCoreSegmentName(name), aronType) + explicit CoreSegmentBase(const std::string& name, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : + CoreSegmentBase(name, MemoryID(), aronType) { } - CoreSegmentBase(const std::string& name, const MemoryID& parentID, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : - CoreSegmentBase(parentID.withProviderSegmentName(name), aronType) + explicit CoreSegmentBase(const std::string& name, const MemoryID& parentID, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : + CoreSegmentBase(parentID.withCoreSegmentName(name), aronType) { } - CoreSegmentBase(const MemoryID& id, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : + explicit CoreSegmentBase(const MemoryID& id, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : Base(id), AronTyped(aronType) { @@ -75,86 +85,152 @@ namespace armarx::armem::base CoreSegmentBase& operator=(CoreSegmentBase&& other) = default; - inline const std::string& name() const + // READ ACCESS + + // Get key + inline std::string& name() { return this->id().coreSegmentName; } - inline std::string& name() + inline const std::string& name() const { - return const_cast<std::string&>(const_cast<const CoreSegmentBase*>(this)->name()); + return this->id().coreSegmentName; } - inline const auto& providerSegments() const + // Has child by key + bool hasProviderSegment(const std::string& name) const { - return this->_container; + return this->findProviderSegment(name) != nullptr; } - inline auto& providerSegments() + // Has child by memory ID + bool hasProviderSegment(const MemoryID& providerSegmentID) const { - return this->_container; + return this->findProviderSegment(providerSegmentID) != nullptr; } - bool hasProviderSegment(const std::string& name) const + // Find child by key + ProviderSegmentT* findProviderSegment(const std::string& name) + { + return detail::findChildByKey(name, this->_container); + } + const ProviderSegmentT* findProviderSegment(const std::string& name) const { - return this->_container.count(name) > 0; + return detail::findChildByKey(name, this->_container); } + // Get child by key ProviderSegmentT& getProviderSegment(const std::string& name) { - return const_cast<ProviderSegmentT&>(const_cast<const CoreSegmentBase*>(this)->getProviderSegment(name)); + return detail::getChildByKey(name, this->_container, *this); } - const ProviderSegmentT& getProviderSegment(const std::string& name) const { - auto it = this->_container.find(name); - if (it != this->_container.end()) - { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<ProviderSegmentT>(name, *this); - } + return detail::getChildByKey(name, this->_container, *this); } - using Base::getEntity; - const EntityT& getEntity(const MemoryID& id) const override + // Find child by MemoryID + ProviderSegmentT* findProviderSegment(const MemoryID& providerSegmentID) + { + detail::checkHasProviderSegmentName(providerSegmentID); + return this->findProviderSegment(providerSegmentID.providerSegmentName); + } + const ProviderSegmentT* findProviderSegment(const MemoryID& providerSegmentID) const { - this->_checkContainerName(id.coreSegmentName, this->getKeyString()); - return getProviderSegment(id.providerSegmentName).getEntity(id); + detail::checkHasProviderSegmentName(providerSegmentID); + return this->findProviderSegment(providerSegmentID.providerSegmentName); } - const EntityT* findEntity(const MemoryID& id) const override + // Get child by MemoryID + ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) { - this->_checkContainerName(id.coreSegmentName, this->getKeyString()); - if (id.hasProviderSegmentName()) - { - return getProviderSegment(id.providerSegmentName).findEntity(id); - } - else - { - for (const auto& [_, providerSegment] : this->_container) - { - if (auto entity = providerSegment.findEntity(id)) - { - return entity; - } - } - return nullptr; - } + detail::checkHasProviderSegmentName(providerSegmentID); + return this->getProviderSegment(providerSegmentID.providerSegmentName); + } + const ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) const + { + detail::checkHasProviderSegmentName(providerSegmentID); + return this->getProviderSegment(providerSegmentID.providerSegmentName); + } + + // get/findInstance are provided by GetFindInstanceMixin + // get/findSnapshot are provided by GetFindSnapshotMixin + // get/findEntity are provided by GetFindEntityMixin + using detail::GetFindEntityMixin<DerivedT>::hasEntity; + using detail::GetFindEntityMixin<DerivedT>::findEntity; + + // Search all provider segments for the first matching entity. + + bool hasEntity(const std::string& entityName) const + { + return this->findEntity(entityName) != nullptr; + } + EntityT* findEntity(const std::string& entityName) + { + return _findEntity(*this, entityName); + } + const EntityT* findEntity(const std::string& entityName) const + { + return _findEntity(*this, entityName); + } + + + // ITERATION + + /** + * @param func Function like: bool process(ProviderSegmentT& provSeg) + */ + template <class ProviderSegmentFunctionT> + bool forEachProviderSegment(ProviderSegmentFunctionT&& func) + { + return this->forEachChild(func); + } + /** + * @param func Function like: bool process(const ProviderSegmentT& provSeg) + */ + template <class ProviderSegmentFunctionT> + bool forEachProviderSegment(ProviderSegmentFunctionT&& func) const + { + return this->forEachChild(func); + } + + // forEachEntity() is provided by ForEachEntityMixin. + // forEachSnapshot() is provided by ForEachEntitySnapshotMixin. + // forEachInstance() is provided by ForEachEntityInstanceMixin. + + + // Get child keys + std::vector<std::string> getProviderSegmentNames() const + { + return simox::alg::get_keys(this->_container); + } + + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline const auto& providerSegments() const + { + return this->_container; } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline auto& providerSegments() + { + return this->_container; + } + + + // MODIFICATION /** * @brief Updates an entity's history. * * Missing entity entries are added before updating. */ - virtual UpdateResult update(const EntityUpdate& update) + UpdateResult update(const EntityUpdate& update) { this->_checkContainerName(update.entityID.coreSegmentName, this->name()); - auto [inserted, provSeg] = addProviderSegmentIfMissing(update.entityID.providerSegmentName); + auto [inserted, provSeg] = _addProviderSegmentIfMissing(update.entityID.providerSegmentName); // Update entry. @@ -170,48 +246,21 @@ namespace armarx::armem::base return ret; } - std::pair<bool, ProviderSegmentT*> addProviderSegmentIfMissing(const std::string& providerSegmentName) - { - ProviderSegmentT* provSeg; - - auto it = this->_container.find(providerSegmentName); - if (it == this->_container.end()) - { - if (_addMissingProviderSegmentDuringUpdate) - { - // Insert into map. - provSeg = &addProviderSegment(providerSegmentName); - return {true, provSeg}; - } - else - { - throw error::MissingEntry::create<EntitySnapshotT>(providerSegmentName, *this); - } - } - else - { - provSeg = &it->second; - return {false, provSeg}; - } - } void append(const _Derived& m) { - ARMARX_INFO << "CoreSegment: Merge name '" << m.name() << "' into '" << name() << "'"; + // ARMARX_INFO << "CoreSegment: Merge name '" << m.name() << "' into '" << name() << "'"; - for (const auto& [k, s] : m.container()) + m.forEachProviderSegment([this](const ProviderSegmentT & provSeg) { - if (const auto& it = this->_container.find(k); it != this->_container.end()) - { - // segment already exists - it->second.append(s); - } - else + auto it = this->_container.find(provSeg.name()); + if (it == this->_container.end()) { - auto wms = this->_container.emplace(k, this->id().withProviderSegmentName(k)); - wms.first->second.append(s); + it = this->_container.emplace(provSeg.name(), this->id().withProviderSegmentName(provSeg.name())).first; } - } + it->second.append(provSeg); + return true; + }); } /** @@ -223,46 +272,35 @@ namespace armarx::armem::base ProviderSegmentT& addProviderSegment(const std::string& name, aron::typenavigator::ObjectNavigatorPtr providerSegmentType = nullptr) { aron::typenavigator::ObjectNavigatorPtr type = providerSegmentType ? providerSegmentType : this->aronType(); - return addProviderSegment(ProviderSegmentT(name, type)); + return this->_derived().addProviderSegment(name, name, type); } /// Copy and insert a provider segment. ProviderSegmentT& addProviderSegment(const ProviderSegmentT& providerSegment) { - return addProviderSegment(ProviderSegment(providerSegment)); + return this->_derived().addProviderSegment(providerSegment.name(), ProviderSegmentT(providerSegment)); } /// Move and insert a provider segment. ProviderSegmentT& addProviderSegment(ProviderSegmentT&& providerSegment) { - if (hasProviderSegment(providerSegment.name())) - { - throw armem::error::ContainerEntryAlreadyExists( - providerSegment.getLevelName(), providerSegment.name(), getLevelName(), this->getKeyString()); - } - - auto it = this->_container.emplace(providerSegment.name(), std::move(providerSegment)).first; - it->second.id().setCoreSegmentID(this->id()); - it->second.setMaxHistorySize(_maxHistorySize); - return it->second; + const std::string name = providerSegment.name(); // Copy before move. + return this->_derived().addProviderSegment(name, std::move(providerSegment)); } - - /** - * @brief Sets the maximum history size of entities in this segment. - * This affects all current entities as well as new ones. - * @see Entity::setMaxHistorySize() - */ - void setMaxHistorySize(long maxSize) override + /// Insert a provider segment in-place. + template <class ...Args> + ProviderSegmentT& addProviderSegment(const std::string& name, Args... args) { - MaxHistorySize::setMaxHistorySize(maxSize); - for (auto& [name, seg] : this->_container) - { - seg.setMaxHistorySize(maxSize); - } + ChildT& child = this->template _addChild<ChildT>(name, args...); + child.id() = this->id().withProviderSegmentName(name); + return child; } - virtual bool equalsDeep(const CoreSegmentBase& other) const + + // MISC + + bool equalsDeep(const DerivedT& other) const { //std::cout << "CoreSegment::equalsDeep" << std::endl; if (this->size() != other.size()) @@ -283,12 +321,12 @@ namespace armarx::armem::base return true; } - std::string getLevelName() const override + static std::string getLevelName() { return "core segment"; } - std::string getKeyString() const override + std::string getKeyString() const { return this->name(); } @@ -296,17 +334,45 @@ namespace armarx::armem::base protected: - virtual void _copySelf(DerivedT& other) const override + template <class ParentT> + static + auto* + _findEntity(ParentT&& parent, const std::string& entityName) { - Base::_copySelf(other); - other.aronType() = _aronType; - other._addMissingProviderSegmentDuringUpdate = _addMissingProviderSegmentDuringUpdate; + decltype(parent.findEntity(entityName)) result = nullptr; + parent.forEachProviderSegment([&result, &entityName](auto & provSeg) + { + result = provSeg.findEntity(entityName); + return result == nullptr; // Keep going if null, break if not null. + }); + return result; } - virtual void _copySelfEmpty(DerivedT& other) const override + + + std::pair<bool, ProviderSegmentT*> + _addProviderSegmentIfMissing(const std::string& providerSegmentName) { - Base::_copySelfEmpty(other); - other.aronType() = _aronType; - other._addMissingProviderSegmentDuringUpdate = _addMissingProviderSegmentDuringUpdate; + ProviderSegmentT* provSeg; + + auto it = this->_container.find(providerSegmentName); + if (it == this->_container.end()) + { + if (_addMissingProviderSegmentDuringUpdate) + { + // Insert into map. + provSeg = &addProviderSegment(providerSegmentName); + return {true, provSeg}; + } + else + { + throw error::MissingEntry::create<ProviderSegmentT>(providerSegmentName, *this); + } + } + else + { + provSeg = &it->second; + return {false, provSeg}; + } } diff --git a/source/RobotAPI/libraries/armem/core/base/EntityBase.cpp b/source/RobotAPI/libraries/armem/core/base/EntityBase.cpp index 0d2c67b63d566f7170bd9b31cfaf18785b85f87b..d3038a357bdad823f04e8d154a569a7eebaf90d5 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityBase.cpp +++ b/source/RobotAPI/libraries/armem/core/base/EntityBase.cpp @@ -1 +1,2 @@ #include "EntityBase.h" + diff --git a/source/RobotAPI/libraries/armem/core/base/EntityBase.h b/source/RobotAPI/libraries/armem/core/base/EntityBase.h index 4fd0611bb61bcd513de5d9b4c1d066c3dd5814af..f45018ff955fac99b7cf0824eaff7539eee9ac29 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntityBase.h @@ -5,15 +5,14 @@ #include <SimoxUtility/algorithm/get_map_keys_values.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <ArmarXCore/core/logging/Logging.h> - -#include "../../core/Time.h" -#include "../../core/MemoryID.h" +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/Time.h> #include "EntitySnapshotBase.h" -#include "detail/MaxHistorySize.h" #include "detail/MemoryContainerBase.h" +#include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" +#include "detail/negative_index_semantics.h" namespace armarx::armem::base @@ -40,8 +39,9 @@ namespace armarx::armem::base */ template <class _EntitySnapshotT, class _Derived> class EntityBase : - public detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived>, - public detail::MaxHistorySize + public detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived> + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> { using Base = detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived>; @@ -53,6 +53,8 @@ namespace armarx::armem::base using EntitySnapshotT = _EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = EntitySnapshotT; + struct UpdateResult { armarx::armem::UpdateType entityUpdateType; @@ -67,11 +69,11 @@ namespace armarx::armem::base EntityBase() { } - EntityBase(const std::string& name, const MemoryID& parentID = {}) : + explicit EntityBase(const std::string& name, const MemoryID& parentID = {}) : EntityBase(parentID.withEntityName(name)) { } - EntityBase(const MemoryID& id) : + explicit EntityBase(const MemoryID& id) : Base(id) { } @@ -83,277 +85,422 @@ namespace armarx::armem::base EntityBase& operator=(EntityBase&& other) = default; - virtual bool equalsDeep(const EntityBase& other) const + // READING + + // Get key + inline std::string& name() { - //std::cout << "Entity::equalsDeep" << std::endl; - if (this->size() != other.size()) - { - return false; - } - for (const auto& [key, snapshot] : this->_container) - { - if (not other.hasSnapshot(key)) - { - return false; - } - if (not snapshot.equalsDeep(other.getSnapshot(key))) - { - return false; - } - } - return true; + return this->id().entityName; } - - inline const std::string& name() const { return this->id().entityName; } - inline std::string& name() + + + // Has child by key + /// Indicates whether a history entry for the given time exists. + bool hasSnapshot(const Time& time) const { - return const_cast<std::string&>(const_cast<const EntityBase*>(this)->name()); + return this->findSnapshot(time) != nullptr; + } + // Has child by MemoryID + bool hasSnapshot(const MemoryID& snapshotID) const + { + return this->findSnapshot(snapshotID) != nullptr; } - inline const ContainerT& history() const + // Find child via key + EntitySnapshotT* + findSnapshot(const Time& timestamp) { - return this->_container; + return detail::findChildByKey(timestamp, this->_container); } - inline ContainerT& history() + const EntitySnapshotT* + findSnapshot(const Time& timestamp) const { - return const_cast<ContainerT&>(const_cast<const EntityBase*>(this)->history()); + return detail::findChildByKey(timestamp, this->_container); } - + // Get child via key /** - * @brief Indicates whether a history entry for the given time exists. + * @brief Get a snapshot. + * @param time The time. + * @return The snapshot, if it exists. + * + * @throws `armem::error::MissingEntry` If there is no such entry. */ - virtual bool hasSnapshot(const Time& time) + EntitySnapshotT& + getSnapshot(const Time& time) + { + return detail::getChildByKey(time, this->_container, *this, [](const Time & time) + { + return toDateTimeMilliSeconds(time); + }); + } + const EntitySnapshotT& + getSnapshot(const Time& time) const + { + return detail::getChildByKey(time, this->_container, *this, [](const Time & time) + { + return toDateTimeMilliSeconds(time); + }); + } + + // Find child via MemoryID + EntitySnapshotT* + findSnapshot(const MemoryID& snapshotID) { - return const_cast<const EntityBase*>(this)->hasSnapshot(time); + detail::checkHasTimestamp(snapshotID); + return this->findSnapshot(snapshotID.timestamp); + } + const EntitySnapshotT* + findSnapshot(const MemoryID& snapshotID) const + { + detail::checkHasTimestamp(snapshotID); + return this->findSnapshot(snapshotID.timestamp); } - virtual bool hasSnapshot(const Time& time) const + // Get child via MemoryID + EntitySnapshotT& + getSnapshot(const MemoryID& snapshotID) + { + detail::checkHasTimestamp(snapshotID); + return this->getSnapshot(snapshotID.timestamp); + } + const EntitySnapshotT& + getSnapshot(const MemoryID& snapshotID) const { - return this->_container.count(time) > 0; + detail::checkHasTimestamp(snapshotID); + return this->getSnapshot(snapshotID.timestamp); + } + // get/findInstance are provided by GetFindInstanceMixin + + + // More getter/finder for snapshots + /** * @brief Get the latest timestamp. * @throw `armem::error::EntityHistoryEmpty` If the history is empty. */ - Time getLatestTimestamp() + Time getLatestTimestamp() const { - return const_cast<const EntityBase*>(this)->getLatestTimestamp(); + return this->getLatestSnapshot().time(); } - - Time getLatestTimestamp() const + /** + * @brief Get the oldest timestamp. + * @throw `armem::error::EntityHistoryEmpty` If the history is empty. + */ + Time getFirstTimestamp() const { - return getLatestItem().first; + return this->getFirstSnapshot().time(); } /** - * @brief Get all timestamps in the history. + * @brief Return the snapshot with the most recent timestamp. + * @return The latest snapshot or nullptr if the entity is empty. */ - virtual std::vector<Time> getTimestamps() + EntitySnapshotT* findLatestSnapshot() { - return const_cast<const EntityBase*>(this)->getTimestamps(); + return this->empty() ? nullptr : &this->_container.rbegin()->second; } - - virtual std::vector<Time> getTimestamps() const + const EntitySnapshotT* findLatestSnapshot() const { - return simox::alg::get_keys(this->_container); + return this->empty() ? nullptr : &this->_container.rbegin()->second; } - /** - * @brief Get a snapshot. - * @param time The time. - * @return The snapshot, if it exists. - * - * @throws `armem::error::MissingEntry` If there is no such entry. + * @brief Return the snapshot with the most recent timestamp. + * @return The latest snapshot. + * @throw `armem::error::EntityHistoryEmpty` If the history is empty. */ - virtual EntitySnapshotT& getSnapshot(const Time& time) + EntitySnapshotT& getLatestSnapshot() { - return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getSnapshot(time)); + return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getLatestSnapshot()); } - - virtual const EntitySnapshotT& getSnapshot(const Time& time) const + const EntitySnapshotT& getLatestSnapshot() const { - auto it = this->_container.find(time); - if (it != this->_container.end()) + if (const EntitySnapshotT* snapshot = this->findLatestSnapshot()) { - return it->second; + return *snapshot; } else { - throw armem::error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + throw armem::error::EntityHistoryEmpty(name(), "when getting the latest snapshot."); } } - EntitySnapshotT& getSnapshot(const MemoryID& id) + + /** + * @brief Return the snapshot with the least recent timestamp. + * @return The first snapshot or nullptr if the entity is empty. + */ + EntitySnapshotT* findFirstSnapshot() { - return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getSnapshot(id)); + return this->empty() ? nullptr : &this->_container.begin()->second; } - - const EntitySnapshotT& getSnapshot(const MemoryID& id) const + const EntitySnapshotT* findFirstSnapshot() const { - this->_checkContainerName(id.entityName, this->name()); - return getSnapshot(id.timestamp); + return this->empty() ? nullptr : &this->_container.begin()->second; } /** - * @brief Return the snapshot with the most recent timestamp. - * @return The latest snapshot. + * @brief Return the snapshot with the least recent timestamp. + * @return The first snapshot. * @throw `armem::error::EntityHistoryEmpty` If the history is empty. */ - EntitySnapshotT& getLatestSnapshot() + EntitySnapshotT& getFirstSnapshot() { - return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getLatestSnapshot()); + return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getFirstSnapshot()); } - - const EntitySnapshotT& getLatestSnapshot() const + const EntitySnapshotT& getFirstSnapshot() const { - return getLatestItem().second; + if (const EntitySnapshotT* snapshot = this->findFirstSnapshot()) + { + return *snapshot; + } + else + { + throw armem::error::EntityHistoryEmpty(name(), "when getting the first snapshot."); + } } + /** - * @brief Return the lastest snapshot before or at time. + * @brief Return the lastest snapshot before time. * @param time The time. - * @return The latest snapshot. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - * @throw `armem::error::MissingEntry` If no such snapshot + * @return The latest snapshot < time or nullptr if none was found. */ - virtual const EntitySnapshotT& getLatestSnapshotBeforeOrAt(const Time& time) const + const EntitySnapshotT* findLatestSnapshotBefore(const Time& time) const { - // first element equal or greater - typename ContainerT::const_iterator refIt = this->_container.upper_bound(time); - - if (refIt == this->_container.begin()) + if (this->empty()) { - if (refIt->first == time) - { - return refIt->second; - } - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + return nullptr; } - // last element less than - typename ContainerT::const_iterator refItPrev = std::prev(refIt); - - // ... or we return the element before if possible - if (refItPrev != this->_container.begin()) + // We want the rightmost element < time + // lower_bound() gives us the the leftmost >= time, which is probably the right neighbour + typename ContainerT::const_iterator greaterEq = this->_container.lower_bound(time); + // Special cases: + // refIt = begin() => no element < time + if (greaterEq == this->_container.begin()) { - return refItPrev->second; + return nullptr; } - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + // end(): No element >= time, we can still have one < time (rbegin()) => std::prev(end()) + // empty has been checked above + + // std::prev of end() is ok + typename ContainerT::const_iterator less = std::prev(greaterEq); + + // we return the element before if possible + return &less->second; } /** - * @brief Return the lastest snapshot before time. + * @brief Return the latest snapshot before or at time. * @param time The time. - * @return The latest snapshot. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - * @throw `armem::error::MissingEntry` If no such snapshot + * @return The latest snapshot <= time or nullptr if none was found. + */ + const EntitySnapshotT* findLatestSnapshotBeforeOrAt(const Time& time) const + { + return findLatestSnapshotBefore(time + Time::microSeconds(1)); + } + + /** + * @brief Return first snapshot after or at time. + * @param time The time. + * @return The first snapshot >= time or nullptr if none was found. */ - virtual const EntitySnapshotT& getLatestSnapshotBefore(const Time& time) const + const EntitySnapshotT* findFirstSnapshotAfterOrAt(const Time& time) const { - // first element equal or greater - typename ContainerT::const_iterator refIt = this->_container.upper_bound(time); + // We want the leftmost element >= time. + // That's lower bound. + typename ContainerT::const_iterator greaterEq = this->_container.lower_bound(time); - if (refIt == this->_container.begin()) + if (greaterEq == this->_container.end()) { - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + return nullptr; } + return &greaterEq->second; + } - // last element less than - typename ContainerT::const_iterator refItPrev = std::prev(refIt); + /** + * @brief Return first snapshot after time. + * @param time The time. + * @return The first snapshot >= time or nullptr if none was found. + */ + const EntitySnapshotT* findFirstSnapshotAfter(const Time& time) const + { + return findFirstSnapshotAfter(time - Time::microSeconds(1)); + } - // we return the element before if possible - if (refItPrev != this->_container.begin()) - { - return refItPrev->second; - } - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + auto* findLatestInstance(int instanceIndex = 0) + { + auto* snapshot = this->findLatestSnapshot(); + return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; + } + const auto* findLatestInstance(int instanceIndex = 0) const + { + auto* snapshot = this->findLatestSnapshot(); + return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; } +#if 0 // Do not offer this yet. + auto* findLatestInstanceData(int instanceIndex = 0) + { + auto* instance = this->findLatestInstance(instanceIndex); + return instance ? &instance->data() : nullptr; + } + const auto* findLatestInstanceData(int instanceIndex = 0) const + { + auto* instance = this->findLatestInstance(instanceIndex); + return instance ? &instance->data() : nullptr; + } +#endif + + + // ITERATION + /** - * @brief Return all snapshots before or at time. + * @param func Function like: bool process(EntitySnapshotT& snapshot) + */ + template <class SnapshotFunctionT> + bool forEachSnapshot(SnapshotFunctionT&& func) + { + return this->forEachChild(func); + } + /** + * @param func Function like void process(const EntitySnapshotT& snapshot) + */ + template <class SnapshotFunctionT> + bool forEachSnapshot(SnapshotFunctionT&& func) const + { + return this->forEachChild(func); + } + // forEachInstance() is provided by ForEachEntityInstanceMixin. + + + /** + * @brief Return all snapshots before (excluding) time. * @param time The time. * @return The latest snapshots. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - * @throw `armem::error::MissingEntry` If no such snapshots */ - virtual const std::vector<std::reference_wrapper<const EntitySnapshotT>> getSnapshotsBefore(const Time& time) const + template <class FunctionT> + void forEachSnapshotBefore(const Time& time, FunctionT&& func) const { - std::vector<std::reference_wrapper<const EntitySnapshotT>> ret; - const EntitySnapshotT& latest = getLatestSnapshotBefore(time); - for (const auto& [timestamp, snapshot] : this->_container) { - ret.emplace_back(snapshot); - if (timestamp == latest.id().timestamp) + if (timestamp >= time) + { + break; + } + if (not call(func, snapshot)) { break; } } - return ret; } /** - * @brief Return first snapshot after or at time. + * @brief Return all snapshots before or at time. * @param time The time. - * @return The first snapshot. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - * @throw `armem::error::MissingEntry` If no such snapshot + * @return The latest snapshots. */ - virtual const EntitySnapshotT& getFirstSnapshotAfterOrAt(const Time& time) const + template <class FunctionT> + void forEachSnapshotBeforeOrAt(const Time& time, FunctionT&& func) const { - // first element equal or greater - typename ContainerT::const_iterator refIt = this->_container.upper_bound(time); - - if (refIt == this->_container.end()) - { - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); - } - return refIt->second; + getSnapshotsBefore(time + Time::microSeconds(1), func); } + /** - * @brief Return first snapshot after time. - * @param time The time. - * @return The first snapshot. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - * @throw `armem::error::MissingEntry` If no such snapshot + * @brief Return all snapshots between, including, min and max. + * @param min The lowest time to include. + * @param min The highest time to include. + * @return The snapshots in [min, max]. */ - virtual const EntitySnapshotT& getFirstSnapshotAfter(const Time& time) const + template <class FunctionT> + void forEachSnapshotInTimeRange(const Time& min, const Time& max, FunctionT&& func) const { - // first element equal or greater - typename ContainerT::const_iterator refIt = this->_container.upper_bound(time); + // Returns an iterator pointing to the first element that is not less than (i.e. greater or equal to) key. + auto begin = min.toMicroSeconds() > 0 ? this->_container.lower_bound(min) : this->_container.begin(); + // Returns an iterator pointing to the first element that is *greater than* key. + auto end = max.toMicroSeconds() > 0 ? this->_container.upper_bound(max) : this->_container.end(); - if (refIt == this->_container.end()) + for (auto it = begin; it != end && it != this->_container.end(); ++it) { - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + if (not call(func, it->second)) + { + break; + } } + } - if (refIt->first > time) + /** + * @brief Return all snapshots from first to last index. + * + * Negative index are counted from the end, e.g. + * last == -1 results in getting all queries until the end. + * + * @param first The first index to include. + * @param first The last index to include. + * @return The snapshots in [first, last]. + */ + template <class FunctionT> + void forEachSnapshotInIndexRange(long first, long last, FunctionT&& func) const + { + if (this->empty()) { - return refIt->second; + return; } - // first element greater than - typename ContainerT::const_iterator refItNext = std::next(refIt); + const size_t first_ = detail::negativeIndexSemantics(first, this->size()); + const size_t last_ = detail::negativeIndexSemantics(last, this->size()); - if (refItNext != this->_container.end()) + if (first_ <= last_) { - return refItNext->second; + auto it = this->_container.begin(); + std::advance(it, first_); + + size_t num = last_ - first_ + 1; // +1 to make last inclusive + for (size_t i = 0; i < num; ++i, ++it) + { + if (not call(func, it->second)) + { + break; + } + } } + } + + + // Get child keys + /// @brief Get all timestamps in the history. + std::vector<Time> getTimestamps() const + { + return simox::alg::get_keys(this->_container); + } + - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline const ContainerT& history() const + { + return this->_container; } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline ContainerT& history() + { + return const_cast<ContainerT&>(const_cast<const EntityBase*>(this)->history()); + } + + // MODIFICATION /** * @brief Add the given update to this entity's history. @@ -372,7 +519,7 @@ namespace armarx::armem::base { // Insert into history. snapshot = &addSnapshot(update.timeCreated); - ret.removedSnapshots = truncate(); + // ret.removedSnapshots = this->truncate(); ret.entityUpdateType = UpdateType::InsertedNew; } else @@ -381,108 +528,93 @@ namespace armarx::armem::base ret.entityUpdateType = UpdateType::UpdatedExisting; } // Update entry. - snapshot->update(update, this->id()); + snapshot->update(update); ret.id = snapshot->id(); return ret; } + void append(const DerivedT& m) { - ARMARX_INFO << "Entity: Merge name '" << m.name() << "' into '" << name() << "'"; + // ARMARX_INFO << "Entity: Merge name '" << m.name() << "' into '" << name() << "'"; - for (const auto& [k, s] : m.container()) + m.forEachSnapshot([this](const EntitySnapshotT & snapshot) { - if (const auto& it = this->_container.find(k); it != this->_container.end()) + auto it = this->_container.find(snapshot.time()); + if (it == this->_container.end()) { - // segment already exists - // We assume that a snapshot does not change, so ignore + EntitySnapshotT copy { snapshot }; + copy.id() = this->id().withTimestamp(snapshot.time()); // update id (e.g. memory name) if necessary + this->_container.emplace(snapshot.time(), copy); } - else - { - auto snapshot = s.copy(); - snapshot.id() = this->id().withTimestamp(k); // update id (e.g. memory name) if necessary - this->_container.insert(std::make_pair(k, std::move(snapshot))); - } - } - } - - /** - * @brief Add a single snapshot with data. - * @param snapshot The snapshot. - * @return The stored snapshot. - */ - EntitySnapshotT& addSnapshot(const EntitySnapshotT& snapshot) - { - return addSnapshot(EntitySnapshotT(snapshot)); + // else: segment already exists + // We assume that a snapshot does not change, so ignore + return true; + }); } - EntitySnapshotT& addSnapshot(EntitySnapshotT&& snapshot) - { - auto it = this->_container.emplace_hint(this->_container.end(), snapshot.time(), std::move(snapshot)); - it->second.id().setEntityID(this->id()); - return it->second; - } + /// Add a snapshot at the given time. EntitySnapshotT& addSnapshot(const Time& timestamp) { - return addSnapshot(EntitySnapshotT(timestamp)); + return this->addSnapshot(timestamp, EntitySnapshotT(timestamp)); } - - /** - * @brief Sets the maximum history size. - * - * The current history is truncated if necessary. - */ - void setMaxHistorySize(long maxSize) override + /// Copy and insert a snapshot + EntitySnapshotT& addSnapshot(const EntitySnapshotT& snapshot) { - MaxHistorySize::setMaxHistorySize(maxSize); - truncate(); + return this->addSnapshot(snapshot.time(), EntitySnapshotT(snapshot)); } - std::string getKeyString() const override + /// Move and insert a snapshot + EntitySnapshotT& addSnapshot(EntitySnapshotT&& snapshot) { - return this->id().entityName; + Time timestamp = snapshot.time(); // Copy before move. + return this->addSnapshot(timestamp, std::move(snapshot)); } - std::string getLevelName() const override + + /// Insert a snapshot in-place. + template <class ...Args> + EntitySnapshotT& addSnapshot(const Time& timestamp, Args... args) { - return "entity"; + auto it = this->_container.emplace_hint(this->_container.end(), timestamp, args...); + it->second.id() = this->id().withTimestamp(timestamp); + return it->second; } - protected: - /// If maximum size is set, ensure `history`'s is not higher. - std::vector<EntitySnapshotT> truncate() + // MISC + + bool equalsDeep(const DerivedT& other) const { - std::vector<EntitySnapshotT> removedElements; - if (_maxHistorySize >= 0) + //std::cout << "Entity::equalsDeep" << std::endl; + if (this->size() != other.size()) { - while (this->_container.size() > size_t(_maxHistorySize)) + return false; + } + for (const auto& [key, snapshot] : this->_container) + { + if (not other.hasSnapshot(key)) + { + return false; + } + if (not snapshot.equalsDeep(other.getSnapshot(key))) { - removedElements.push_back(std::move(this->_container.begin()->second)); - this->_container.erase(this->_container.begin()); + return false; } - ARMARX_CHECK_LESS_EQUAL(this->_container.size(), _maxHistorySize); } - return removedElements; + return true; } - /** - * @brief Return the snapshot with the most recent timestamp. - * @return The latest snapshot. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - */ - virtual - const typename ContainerT::value_type& - getLatestItem() const + std::string getKeyString() const { - if (this->_container.empty()) - { - throw armem::error::EntityHistoryEmpty(name(), "when getting the latest snapshot."); - } - return *this->_container.rbegin(); + return this->id().entityName; + } + static std::string getLevelName() + { + return "entity"; } }; diff --git a/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.cpp b/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.cpp index a6cfc7103728d5a81fecefe4ade6c96efef2814e..1cc2e9544d00ce96d65658acaf853c670418bdce 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.cpp +++ b/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.cpp @@ -1 +1,25 @@ #include "EntityInstanceBase.h" + + +namespace armarx::armem::base +{ + bool EntityInstanceMetadata::operator==(const EntityInstanceMetadata& other) const + { + return timeCreated == other.timeCreated + && timeSent == other.timeSent + && timeArrived == other.timeArrived + && std::abs(confidence - other.confidence) < 1e-6f; + } +} + + +std::ostream& armarx::armem::base::operator<<(std::ostream& os, const EntityInstanceMetadata& d) +{ + os << "EntityInstanceMetadata: " + << "\n- t_create = \t" << armem::toStringMicroSeconds(d.timeCreated) << " us" + << "\n- t_sent = \t" << armem::toStringMicroSeconds(d.timeSent) << " us" + << "\n- t_arrived = \t" << armem::toStringMicroSeconds(d.timeArrived) << " us" + << "\n- confidence = \t" << d.confidence << " us" + ; + return os; +} diff --git a/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h b/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h index 8dd474c667687e6235a96a191e577163ca194cba..8f7b8aafc4cebd74c241bfb1245388a943897e01 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h @@ -1,17 +1,53 @@ #pragma once -#include "../../core/Time.h" +#include <RobotAPI/libraries/armem/core/Commit.h> +#include <RobotAPI/libraries/armem/core/Time.h> -#include "../Commit.h" #include "detail/MemoryItem.h" namespace armarx::armem::base { + + /** + * @brief Default data of an entity instance (empty). + */ + struct NoData + { + }; + + + /** + * @brief Metadata of an entity instance. + */ + struct EntityInstanceMetadata + { + /// Time when this value was created. + Time timeCreated; + /// Time when this value was sent to the memory. + Time timeSent; + /// Time when this value has arrived at the memory. + Time timeArrived; + + /// An optional confidence, may be used for things like decay. + float confidence = 1.0; + + + bool operator==(const EntityInstanceMetadata& other) const; + inline bool operator!=(const EntityInstanceMetadata& other) const + { + return !(*this == other); + } + }; + + std::ostream& operator<<(std::ostream& os, const EntityInstanceMetadata& rhs); + + + /** * @brief Data of a single entity instance. */ - template <class _DerivedT> + template <class _DataT = NoData, class _MetadataT = EntityInstanceMetadata> class EntityInstanceBase : public detail::MemoryItem { @@ -19,24 +55,24 @@ namespace armarx::armem::base public: - using DerivedT = _DerivedT; + using MetadataT = _MetadataT; + using DataT = _DataT; - public: - EntityInstanceBase() { } - EntityInstanceBase(int index, const MemoryID& parentID = {}) : + explicit EntityInstanceBase(int index, const MemoryID& parentID = {}) : EntityInstanceBase(parentID.withInstanceIndex(index)) { } - EntityInstanceBase(const MemoryID& id) : + explicit EntityInstanceBase(const MemoryID& id) : Base(id) { } + // Key inline int& index() { return id().instanceIndex; @@ -46,29 +82,37 @@ namespace armarx::armem::base return id().instanceIndex; } - /** - * @brief Fill `*this` with the update's values. - * @param update The update. - * @param index The instances index. - */ - virtual void update(const EntityUpdate& update, int index) = 0; - virtual bool equalsDeep(const DerivedT& other) const = 0; + // Data - virtual DerivedT copy() const + EntityInstanceMetadata& metadata() { - DerivedT d; - this->_copySelf(d); - return d; + return _metadata; + } + const EntityInstanceMetadata& metadata() const + { + return _metadata; } + const DataT& data() const + { + return _data; + } - std::string getLevelName() const override + DataT& data() + { + return _data; + } + + + // Misc + + static std::string getLevelName() { return "entity instance"; } - std::string getKeyString() const override + std::string getKeyString() const { return std::to_string(index()); } @@ -76,10 +120,11 @@ namespace armarx::armem::base protected: - virtual void _copySelf(DerivedT& other) const - { - Base::_copySelf(other); - } + /// The metadata. + MetadataT _metadata; + + /// The data. May be nullptr. + DataT _data; }; diff --git a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.cpp b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.cpp index 5f7899f50d954f16b2d9c0a9569519529895eb20..72b4c48877c78dce70737fa58c58cd1b82b2d2e5 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.cpp +++ b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.cpp @@ -1 +1,11 @@ #include "EntitySnapshotBase.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + + +void armarx::armem::base::detail::throwIfNotEqual(const Time& ownTime, const Time& updateTime) +{ + ARMARX_CHECK_EQUAL(ownTime, updateTime) + << "Cannot update a snapshot to an update with another timestamp. \n" + << "Note: A snapshot's timestamp must not be changed after construction."; +} diff --git a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h index cf68286be26368cb4821fecbbe241e3ef33e0fbe..0d0f091f59847cabd2b9d5f0982d634daaa2723a 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h @@ -1,15 +1,20 @@ #pragma once -#include <memory> #include <vector> -#include "../MemoryID.h" -#include "../Time.h" +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/Time.h> #include "EntityInstanceBase.h" #include "detail/MemoryContainerBase.h" +#include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" +namespace armarx::armem::base::detail +{ + void throwIfNotEqual(const Time& ownTime, const Time& updateTime); +} namespace armarx::armem::base { /** @@ -34,11 +39,11 @@ namespace armarx::armem::base EntitySnapshotBase() { } - EntitySnapshotBase(Time time, const MemoryID& parentID = {}) : + explicit EntitySnapshotBase(Time time, const MemoryID& parentID = {}) : EntitySnapshotBase(parentID.withTimestamp(time)) { } - EntitySnapshotBase(const MemoryID& id) : + explicit EntitySnapshotBase(const MemoryID& id) : Base(id) { } @@ -49,85 +54,62 @@ namespace armarx::armem::base EntitySnapshotBase& operator=(EntitySnapshotBase&& other) = default; - bool equalsDeep(const EntitySnapshotBase& other) const - { - //std::cout << "EntitySnapshot::equalsDeep" << std::endl; - if (this->size() != other.size()) - { - return false; - } - int i = 0; - for (const auto& instance : this->_container) - { - if (not instance.equalsDeep(other.getInstance(i))) - { - return false; - } - i++; - } - return true; - } + // READING + // Get key inline Time& time() { return this->id().timestamp; } - inline const Time& time() const { return this->id().timestamp; } - inline const std::vector<EntityInstanceT>& instances() const + // Has child by key + bool hasInstance(int index) const { - return this->_container; + return this->findInstance(index) != nullptr; } - inline std::vector<EntityInstanceT>& instances() + // Has child by ID + bool hasInstance(const MemoryID& instanceID) const { - return const_cast<std::vector<EntityInstanceT>&>(const_cast<const EntitySnapshotBase*>(this)->instances()); + return this->findInstance(instanceID) != nullptr; } - void update(const EntityUpdate& update, std::optional<MemoryID> parentID = std::nullopt) + // Find child by key + EntityInstanceT* findInstance(int index) { - if (parentID) - { - this->id() = *parentID; - } - time() = update.timeCreated; - - this->_container.clear(); - for (int i = 0; i < int(update.instancesData.size()); ++i) - { - EntityInstanceT& data = this->_container.emplace_back(i, this->id()); - data.update(update, i); - } + return const_cast<EntityInstanceT*>(const_cast<const EntitySnapshotBase*>(this)->findInstance(index)); } - - - bool hasInstance(int index) const + const EntityInstanceT* findInstance(int index) const { - size_t si = size_t(index); - return index >= 0 && si < this->_container.size(); + const size_t si = static_cast<size_t>(index); + return (index >= 0 && si < this->_container.size()) + ? &this->_container[si] + : nullptr; } + // Get child by key /** * @brief Get the given instance. * @param index The instance's index. * @return The instance. * @throw `armem::error::MissingEntry` If the given index is invalid. */ - EntityInstanceT& getInstance(int index) + EntityInstanceT& + getInstance(int index) { return const_cast<EntityInstanceT&>(const_cast<const EntitySnapshotBase*>(this)->getInstance(index)); } - - const EntityInstanceT& getInstance(int index) const + const EntityInstanceT& + getInstance(int index) const { - if (hasInstance(index)) + if (const EntityInstanceT* instance = findInstance(index)) { - return this->_container[static_cast<size_t>(index)]; + return *instance; } else { @@ -135,6 +117,21 @@ namespace armarx::armem::base } } + // Find child by MemoryID + EntityInstanceT* + findInstance(const MemoryID& instanceID) + { + detail::checkHasInstanceIndex(instanceID); + return this->findInstance(instanceID.instanceIndex); + } + const EntityInstanceT* + findInstance(const MemoryID& instanceID) const + { + detail::checkHasInstanceIndex(instanceID); + return this->findInstance(instanceID.instanceIndex); + } + + // Get child by MemoryID /** * @brief Get the given instance. * @param index The instance's index. @@ -142,21 +139,79 @@ namespace armarx::armem::base * @throw `armem::error::MissingEntry` If the given index is invalid. * @throw `armem::error::InvalidMemoryID` If memory ID does not have an instance index. */ - EntityInstanceT& getInstance(const MemoryID& id) + EntityInstanceT& + getInstance(const MemoryID& instanceID) + { + detail::checkHasInstanceIndex(instanceID); + return this->getInstance(instanceID.instanceIndex); + } + const EntityInstanceT& + getInstance(const MemoryID& instanceID) const { - return const_cast<EntityInstanceT&>(const_cast<const EntitySnapshotBase*>(this)->getInstance(id)); + detail::checkHasInstanceIndex(instanceID); + return this->getInstance(instanceID.instanceIndex); } - const EntityInstanceT& getInstance(const MemoryID& id) const + + // ITERATION + + /** + * @param func Function like void process(EntityInstanceT& instance)> + */ + template <class InstanceFunctionT> + bool forEachInstance(InstanceFunctionT&& func) { - if (!id.hasInstanceIndex()) + return this->forEachChild(func); + } + /** + * @param func Function like void process (const EntityInstanceT& instance) + */ + template <class InstanceFunctionT> + bool forEachInstance(InstanceFunctionT&& func) const + { + return this->forEachChild(func); + } + + + // Get child keys + std::vector<int> getInstanceIndices() const + { + std::vector<int> indices; + indices.reserve(this->size()); + for (size_t i = 0; i < this->size(); ++i) { - throw armem::error::InvalidMemoryID(id, "ID has no instance index."); + indices.push_back(static_cast<int>(i)); } - return getInstance(id.instanceIndex); + return indices; } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline const std::vector<EntityInstanceT>& instances() const + { + return this->_container; + } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline std::vector<EntityInstanceT>& instances() + { + return const_cast<std::vector<EntityInstanceT>&>(const_cast<const EntitySnapshotBase*>(this)->instances()); + } + + + // MODIFICATION + + void update(const EntityUpdate& update) + { + detail::throwIfNotEqual(time(), update.timeCreated); + + this->_container.clear(); + for (int index = 0; index < int(update.instancesData.size()); ++index) + { + EntityInstanceT& data = this->_container.emplace_back(index, this->id()); + data.update(update); + } + } + /** * @brief Add a single instance with data. * @param instance The instance. @@ -181,27 +236,49 @@ namespace armarx::armem::base "Cannot add an EntityInstance because its index is too big."); } - int new_index = this->_container.size(); - auto& it = this->_container.emplace_back(std::move(instance)); - it.index() = new_index; - return it; + int index = static_cast<int>(this->_container.size()); + EntityInstanceT& added = this->_container.emplace_back(std::move(instance)); + added.id() = this->id().withInstanceIndex(index); + return added; } EntityInstanceT& addInstance() { - int new_index = this->_container.size(); - auto& it = this->_container.emplace_back(EntityInstanceT());; - it.index() = new_index; - it.id() = this->id().withInstanceIndex(new_index); - return it; + int index = static_cast<int>(this->size()); + EntityInstanceT& added = this->_container.emplace_back(EntityInstanceT()); + added.id() = this->id().withInstanceIndex(index); + return added; } - std::string getKeyString() const override + + // MISC + + bool equalsDeep(const DerivedT& other) const + { + //std::cout << "EntitySnapshot::equalsDeep" << std::endl; + if (this->size() != other.size()) + { + return false; + } + int i = 0; + for (const auto& instance : this->_container) + { + if (not instance.equalsDeep(other.getInstance(i))) + { + return false; + } + i++; + } + return true; + } + + + std::string getKeyString() const { return toDateTimeMilliSeconds(this->time()); } - std::string getLevelName() const override + static std::string getLevelName() { return "entity snapshot"; } diff --git a/source/RobotAPI/libraries/armem/core/base/MemoryBase.h b/source/RobotAPI/libraries/armem/core/base/MemoryBase.h index 1bc42b0cc579dbf8abe4f2831608166451ff8fbb..e1729bbcd90cc6062e93b8e62ed8db29e3bb4387 100644 --- a/source/RobotAPI/libraries/armem/core/base/MemoryBase.h +++ b/source/RobotAPI/libraries/armem/core/base/MemoryBase.h @@ -3,10 +3,10 @@ #include <map> #include <string> -#include <ArmarXCore/core/logging/Logging.h> - #include "CoreSegmentBase.h" -#include "detail/EntityContainerBase.h" +#include "detail/MemoryContainerBase.h" +#include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base @@ -17,9 +17,17 @@ namespace armarx::armem::base */ template <class _CoreSegmentT, class _Derived> class MemoryBase : - public detail::EntityContainerBase<_CoreSegmentT, typename _CoreSegmentT::ProviderSegmentT::EntityT, _Derived> + public detail::MemoryContainerBase<std::map<std::string, _CoreSegmentT>, _Derived> + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::ForEachEntitySnapshotMixin<_Derived> + , public detail::ForEachEntityMixin<_Derived> + , public detail::ForEachProviderSegmentMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> + , public detail::GetFindSnapshotMixin<_Derived> + , public detail::GetFindEntityMixin<_Derived> + , public detail::GetFindProviderSegmentMixin<_Derived> { - using Base = detail::EntityContainerBase<_CoreSegmentT, typename _CoreSegmentT::ProviderSegmentT::EntityT, _Derived>; + using Base = detail::MemoryContainerBase<std::map<std::string, _CoreSegmentT>, _Derived>; public: @@ -32,6 +40,8 @@ namespace armarx::armem::base using EntitySnapshotT = typename EntityT::EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = CoreSegmentT; + struct UpdateResult { @@ -57,11 +67,11 @@ namespace armarx::armem::base MemoryBase() { } - MemoryBase(const std::string& name) : + explicit MemoryBase(const std::string& name) : MemoryBase(MemoryID().withMemoryName(name)) { } - MemoryBase(const MemoryID& id) : + explicit MemoryBase(const MemoryID& id) : Base(id) { } @@ -72,149 +82,158 @@ namespace armarx::armem::base MemoryBase& operator=(MemoryBase&& other) = default; - inline const std::string& name() const + // READ ACCESS + + // Get key + inline std::string& name() { return this->id().memoryName; } - inline std::string& name() + inline const std::string& name() const { - return const_cast<std::string&>(const_cast<const MemoryBase*>(this)->name()); + return this->id().memoryName; } - inline auto& coreSegments() const + // Has child by key + bool hasCoreSegment(const std::string& name) const { - return this->_container; + return this->findCoreSegment(name) != nullptr; } - inline auto& coreSegments() + // Has child by MemoryID + bool hasCoreSegment(const MemoryID& coreSegmentID) const { - return this->_container; + return this->findCoreSegment(coreSegmentID) != nullptr; } - - bool hasCoreSegment(const std::string& name) const + // Find child by key + CoreSegmentT* findCoreSegment(const std::string& name) { - return this->_container.count(name) > 0; + return detail::findChildByKey(name, this->_container); + } + const CoreSegmentT* findCoreSegment(const std::string& name) const + { + return detail::findChildByKey(name, this->_container); } + // Get child by key CoreSegmentT& getCoreSegment(const std::string& name) { - return const_cast<CoreSegmentT&>(const_cast<const MemoryBase*>(this)->getCoreSegment(name)); + return detail::getChildByKey(name, this->_container, *this); } - const CoreSegmentT& getCoreSegment(const std::string& name) const { - auto it = this->_container.find(name); - if (it != this->_container.end()) - { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<CoreSegmentT>(name, *this); - } + return detail::getChildByKey(name, this->_container, *this); } - - bool hasProviderSegment(const MemoryID& providerSegmentID) const + // Find child by MemoryID + CoreSegmentT* findCoreSegment(const MemoryID& coreSegmentID) { - auto it = this->_container.find(providerSegmentID.coreSegmentName); - if (it != this->_container.end()) - { - return it->second.hasProviderSegment(providerSegmentID.providerSegmentName); - } - else - { - return false; - } + detail::checkHasCoreSegmentName(coreSegmentID); + return this->findCoreSegment(coreSegmentID.coreSegmentName); + } + const CoreSegmentT* findCoreSegment(const MemoryID& coreSegmentID) const + { + detail::checkHasCoreSegmentName(coreSegmentID); + return this->findCoreSegment(coreSegmentID.coreSegmentName); } - ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) + // Get child by MemoryID + CoreSegmentT& getCoreSegment(const MemoryID& coreSegmentID) + { + detail::checkHasCoreSegmentName(coreSegmentID); + return this->getCoreSegment(coreSegmentID.coreSegmentName); + } + const CoreSegmentT& getCoreSegment(const MemoryID& coreSegmentID) const { - return getCoreSegment(providerSegmentID.coreSegmentName).getProviderSegment(providerSegmentID.providerSegmentName); + detail::checkHasCoreSegmentName(coreSegmentID); + return this->getCoreSegment(coreSegmentID.coreSegmentName); } - const ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) const + // get/findInstance are provided by GetFindInstanceMixin + // get/findSnapshot are provided by GetFindSnapshotMixin + // get/findEntity are provided by GetFindEntityMixin + // get/findProviderSegment are provided by GetFindProviderSegmentMixin + + + + // ITERATION + + /** + * @param func Function like: bool process(CoreSegmentT& coreSeg) + */ + template <class CoreSegmentFunctionT> + bool forEachCoreSegment(CoreSegmentFunctionT&& func) + { + return this->forEachChild(func); + } + /** + * @param func Function like: bool process(const CoreSegmentT& coreSeg) + */ + template <class CoreSegmentFunctionT> + bool forEachCoreSegment(CoreSegmentFunctionT&& func) const { - return getCoreSegment(providerSegmentID.coreSegmentName).getProviderSegment(providerSegmentID.providerSegmentName); + return this->forEachChild(func); } + // forEachProviderSegment() is provided by ForEachProviderSegmentMixin. + // forEachEntity() is provided by ForEachEntityMixin. + // forEachSnapshot() is provided by ForEachEntitySnapshotMixin. + // forEachInstance() is provided by ForEachEntityInstanceMixin. + - using Base::getEntity; - const EntityT& getEntity(const MemoryID& id) const override + std::vector<std::string> getCoreSegmentNames() const { - this->_checkContainerName(id.memoryName, this->name()); - return getCoreSegment(id.coreSegmentName).getEntity(id); + return simox::alg::get_keys(this->_container); } - const EntityT* findEntity(const MemoryID& id) const override + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline auto& coreSegments() const { - this->_checkContainerName(id.memoryName, this->name()); - if (id.hasCoreSegmentName()) - { - return getCoreSegment(id.coreSegmentName).findEntity(id); - } - else - { - for (const auto& [_, coreSegment] : this->_container) - { - if (auto entity = coreSegment.findEntity(id)) - { - return entity; - } - } - return nullptr; - } + return this->_container; + } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline auto& coreSegments() + { + return this->_container; } + + // MODIFICATION + /** * @brief Add an empty core segment with the given name. * @param name The core segment name. * @param coreSegmentType The core segment type (optional). * @return The added core segment. */ - CoreSegmentT& addCoreSegment(const std::string& name, aron::typenavigator::ObjectNavigatorPtr coreSegmentType = nullptr) + CoreSegmentT& addCoreSegment( + const std::string& name, aron::typenavigator::ObjectNavigatorPtr coreSegmentType = nullptr) { - return addCoreSegment(CoreSegmentT(name, coreSegmentType)); + return this->_derived().addCoreSegment(name, name, coreSegmentType); } + /// Copy and insert a core segment. CoreSegmentT& addCoreSegment(const CoreSegmentT& coreSegment) { - return addCoreSegment(CoreSegmentT(coreSegment)); + return this->_derived().addCoreSegment(coreSegment.name(), CoreSegmentT(coreSegment)); } + /// Move and insert a core segment. CoreSegmentT& addCoreSegment(CoreSegmentT&& coreSegment) { - if (this->_container.count(coreSegment.name()) > 0) - { - throw armem::error::ContainerEntryAlreadyExists(coreSegment.getLevelName(), coreSegment.name(), - this->getLevelName(), this->name()); - } - auto it = this->_container.emplace(coreSegment.name(), std::move(coreSegment)).first; - it->second.id().setMemoryID(this->id()); - return it->second; + const std::string name = coreSegment.name(); // Copy before move. + return this->_derived().addCoreSegment(name, std::move(coreSegment)); } - /** - * @brief Add multiple core segments. - * @param The core segment names. - * @return The core segments. The contained pointers are never null. - */ - std::vector<CoreSegmentT*> addCoreSegments(const std::vector<std::string>& names) + /// Move and insert a core segment. + template <class ...Args> + CoreSegmentT& addCoreSegment(const std::string& name, Args... args) { - std::vector<CoreSegmentT*> segments; - for (const auto& name : names) - { - try - { - segments.push_back(&addCoreSegment(name)); - } - catch (const armem::error::ContainerEntryAlreadyExists& e) - { - ARMARX_INFO << e.what() << "\nIgnoring multiple addition."; - } - } - return segments; + CoreSegmentT& child = this->template _addChild<ChildT>(name, args...); + child.id() = this->id().withCoreSegmentName(name); + return child; } @@ -223,10 +242,10 @@ namespace armarx::armem::base * @param commit The commit. * @return The resulting memory IDs. */ - virtual std::vector<UpdateResult> update(const Commit& commit) + std::vector<UpdateResult> update(const Commit& commit) { std::vector<UpdateResult> results; - for (const auto& update : commit.updates) + for (const EntityUpdate& update : commit.updates) { results.push_back(this->update(update)); } @@ -238,11 +257,11 @@ namespace armarx::armem::base * @param update The update. * @return The resulting entity snapshot's ID. */ - virtual UpdateResult update(const EntityUpdate& update) + UpdateResult update(const EntityUpdate& update) { this->_checkContainerName(update.entityID.memoryName, this->name()); - auto [inserted, coreSeg] = addCoreSegmentIfMissing(update.entityID.coreSegmentName); + auto [inserted, coreSeg] = _addCoreSegmentIfMissing(update.entityID.coreSegmentName); // Update entry. UpdateResult ret(coreSeg->update(update)); @@ -257,30 +276,6 @@ namespace armarx::armem::base return ret; } - std::pair<bool, CoreSegmentT*> addCoreSegmentIfMissing(const std::string& coreSegmentName) - { - CoreSegmentT* coreSeg; - - auto it = this->_container.find(coreSegmentName); - if (it == this->_container.end()) - { - if (_addMissingCoreSegmentDuringUpdate) - { - // Insert into map. - coreSeg = &addCoreSegment(coreSegmentName); - return {true, coreSeg}; - } - else - { - throw error::MissingEntry::create<EntitySnapshotT>(coreSegmentName, *this); - } - } - else - { - coreSeg = &it->second; - return {false, coreSeg}; - } - } /** * @brief Merge another memory into this one. Append all data @@ -288,24 +283,21 @@ namespace armarx::armem::base */ void append(const _Derived& m) { - ARMARX_INFO << "Memory: Merge name '" << m.name() << "' into '" << name() << "'"; + // ARMARX_INFO << "Memory: Merge name '" << m.name() << "' into '" << name() << "'"; - for (const auto& [k, s] : m.container()) + m.forEachCoreSegment([this](const CoreSegmentT & coreSeg) { - if (const auto& it = this->_container.find(k); it != this->_container.end()) - { - // segment already exists - it->second.append(s); - } - else + auto it = this->_container.find(coreSeg.name()); + if (it == this->_container.end()) { - auto wms = this->_container.emplace(k, this->id().withCoreSegmentName(k)); - wms.first->second.append(s); + it = this->_container.emplace(coreSeg.name(), this->id().withCoreSegmentName(coreSeg.name())).first; } - } + it->second.append(coreSeg); + return true; + }); } - virtual bool equalsDeep(const MemoryBase& other) const + bool equalsDeep(const MemoryBase& other) const { //std::cout << "Memory::equalsDeep" << std::endl; if (this->size() != other.size()) @@ -326,50 +318,50 @@ namespace armarx::armem::base return true; } - std::string getLevelName() const override + static std::string getLevelName() { return "memory"; } - std::string getKeyString() const override + std::string getKeyString() const { return this->name(); } - std::string dump() const + + protected: + + std::pair<bool, CoreSegmentT*> _addCoreSegmentIfMissing(const std::string& coreSegmentName) { - std::stringstream ss; - ss << "Memory: " << this->name() << "\n"; - for (const auto& [ckey, cseg] : this->container()) + CoreSegmentT* coreSeg = nullptr; + + auto it = this->_container.find(coreSegmentName); + if (it == this->_container.end()) { - ss << " |- Found core seg: " << ckey << "\n"; - for (const auto& [pkey, pseg] : cseg.container()) + if (_addMissingCoreSegmentDuringUpdate) { - ss << " | |- Found prov seg: " << pkey << "\n"; - for (const auto& [ekey, eseg] : pseg.container()) - { - ss << " | | |- Found entity: " << ekey << "\n"; - } + // Insert into map. + coreSeg = &addCoreSegment(coreSegmentName); + return {true, coreSeg}; } + else + { + throw error::MissingEntry::create<CoreSegmentT>(coreSegmentName, *this); + } + } + else + { + coreSeg = &it->second; + return {false, coreSeg}; } - return ss.str(); } - protected: - - virtual void _copySelf(DerivedT& other) const override - { - Base::_copySelf(other); - other._addMissingCoreSegmentDuringUpdate = _addMissingCoreSegmentDuringUpdate; - } - virtual void _copySelfEmpty(DerivedT& other) const override - { - Base::_copySelfEmpty(other); - other._addMissingCoreSegmentDuringUpdate = _addMissingCoreSegmentDuringUpdate; - } public: + // ToDo: Remove from base structure - this should be specific to the server versions. + // If necessary at all. bool _addMissingCoreSegmentDuringUpdate = false; + }; } diff --git a/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h b/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h index 57ff3210244fa516551f4906a2ea07c2cc81f09d..6a5497444c9e5959ac7d59300e1bc99e0fac2018 100644 --- a/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h +++ b/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h @@ -5,8 +5,9 @@ #include "EntityBase.h" #include "detail/AronTyped.h" -#include "detail/EntityContainerBase.h" -#include "detail/MaxHistorySize.h" +#include "detail/MemoryContainerBase.h" +#include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base @@ -17,11 +18,14 @@ namespace armarx::armem::base */ template <class _EntityT, class _Derived> class ProviderSegmentBase : - public detail::EntityContainerBase<_EntityT, _EntityT, _Derived>, - public detail::MaxHistorySize, - public detail::AronTyped + public detail::MemoryContainerBase<std::map<std::string, _EntityT>, _Derived> + , public detail::AronTyped + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::ForEachEntitySnapshotMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> + , public detail::GetFindSnapshotMixin<_Derived> { - using Base = detail::EntityContainerBase<_EntityT, _EntityT, _Derived>; + using Base = detail::MemoryContainerBase<std::map<std::string, _EntityT>, _Derived>; public: @@ -32,6 +36,8 @@ namespace armarx::armem::base using EntitySnapshotT = typename EntityT::EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = EntityT; + struct UpdateResult { armarx::armem::UpdateType providerSegmentUpdateType; @@ -47,21 +53,22 @@ namespace armarx::armem::base {} }; + public: ProviderSegmentBase() { } - ProviderSegmentBase(const std::string& name, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : - ProviderSegmentBase(MemoryID().withProviderSegmentName(name), aronType) + explicit ProviderSegmentBase(const std::string& name, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : + ProviderSegmentBase(name, MemoryID(), aronType) { } - ProviderSegmentBase(const std::string& name, const MemoryID parentID, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : + explicit ProviderSegmentBase(const std::string& name, const MemoryID parentID, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : ProviderSegmentBase(parentID.withProviderSegmentName(name), aronType) { } - ProviderSegmentBase(const MemoryID id, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : + explicit ProviderSegmentBase(const MemoryID id, aron::typenavigator::ObjectNavigatorPtr aronType = nullptr) : Base(id), AronTyped(aronType) { @@ -73,70 +80,125 @@ namespace armarx::armem::base ProviderSegmentBase& operator=(ProviderSegmentBase&& other) = default; + // READ ACCESS + + // Get key + inline std::string& name() + { + return this->id().providerSegmentName; + } inline const std::string& name() const { return this->id().providerSegmentName; } - inline std::string& name() + + + // Has child by key + bool hasEntity(const std::string& name) const + { + return this->findEntity(name) != nullptr; + } + // Has child by ID + bool hasEntity(const MemoryID& entityID) const { - return const_cast<std::string&>(const_cast<const ProviderSegmentBase*>(this)->name()); + return this->findEntity(entityID) != nullptr; } - inline const auto& entities() const + // Find child by key + EntityT* findEntity(const std::string& name) { - return this->_container; + return detail::findChildByKey(name, this->_container); } - inline auto& entities() + const EntityT* findEntity(const std::string& name) const { - return this->_container; + return detail::findChildByKey(name, this->_container); } + // Get child by key + EntityT& getEntity(const std::string& name) + { + return detail::getChildByKey(name, this->_container, *this); + } + const EntityT& getEntity(const std::string& name) const + { + return detail::getChildByKey(name, this->_container, *this); + } - bool hasEntity(const std::string& name) const + // Find child by MemoryID + EntityT* findEntity(const MemoryID& entityID) { - return this->_container.count(name) > 0; + detail::checkHasEntityName(entityID); + return this->findEntity(entityID.entityName); + } + const EntityT* findEntity(const MemoryID& entityID) const + { + detail::checkHasEntityName(entityID); + return this->findEntity(entityID.entityName); } - using Base::getEntity; - const EntityT& getEntity(const MemoryID& id) const override + // Get child by MemoryID + EntityT& getEntity(const MemoryID& entityID) + { + detail::checkHasEntityName(entityID); + return this->getEntity(entityID.entityName); + } + const EntityT& getEntity(const MemoryID& entityID) const { - this->_checkContainerName(id.providerSegmentName, this->getKeyString()); - return getEntity(id.entityName); + detail::checkHasEntityName(entityID); + return this->getEntity(entityID.entityName); } - EntityT& getEntity(const std::string& name) + // get/findInstance are provided by GetFindInstanceMixin + // get/findSnapshot are provided by GetFindSnapshotMixin + + + // ITERATION + + // All functors are taken as universal reference (F&&). + + /** + * @param func Function like: bool process(EntityT& entity) + */ + template <class EntityFunctionT> + bool forEachEntity(EntityFunctionT&& func) { - return const_cast<EntityT&>(const_cast<const ProviderSegmentBase*>(this)->getEntity(name)); + return this->forEachChild(func); + } + /** + * @param func Function like: bool process(const EntityT& entity) + */ + template <class EntityFunctionT> + bool forEachEntity(EntityFunctionT&& func) const + { + return this->forEachChild(func); } - const EntityT& getEntity(const std::string& name) const + // forEachSnapshot() is provided by ForEachEntitySnapshotMixin + // forEachInstance() is provided by ForEachEntityInstanceMixin + + + // Get child keys + std::vector<std::string> getEntityNames() const { - auto it = this->_container.find(name); - if (it != this->_container.end()) - { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<EntityT>(name, *this); - } + return simox::alg::get_keys(this->_container); } - const EntityT* findEntity(const MemoryID& id) const override + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline const ContainerT& entities() const { - this->_checkContainerName(id.providerSegmentName, this->getKeyString()); - auto it = this->_container.find(id.entityName); - if (it != this->_container.end()) - { - return &it->second; - } - else - { - return nullptr; - } + return this->_container; + } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] + inline ContainerT& entities() + { + return this->_container; } + + // MODIFICATION + /** * @brief Updates an entity's history. * @@ -154,7 +216,6 @@ namespace armarx::armem::base { // Add entity entry. entity = &addEntity(update.entityID.entityName); - entity->setMaxHistorySize(_maxHistorySize); updateType = UpdateType::InsertedNew; } else @@ -168,62 +229,54 @@ namespace armarx::armem::base return ret; } - void append(const _Derived& m) + void append(const DerivedT& m) { - ARMARX_INFO << "ProviderSegment: Merge name '" << m.name() << "' into '" << name() << "'"; - - for (const auto& [k, s] : m.container()) + m.forEachEntity([this](const EntityT & entity) { - if (const auto& it = this->_container.find(k); it != this->_container.end()) + auto it = this->_container.find(entity.name()); + if (it == this->_container.end()) { - // segment already exists - it->second.append(s); + it = this->_container.emplace(entity.name(), this->id().withEntityName(entity.name())).first; } - else - { - auto wms = this->_container.emplace(k, this->id().withEntityName(k)); - wms.first->second.append(s); - } - } + it->second.append(entity); + return true; + }); } + /// Add an empty entity with the given name. EntityT& addEntity(const std::string& name) { - return addEntity(EntityT(name)); + return this->_derived().addEntity(name, name); } + /// Copy and insert an entity. EntityT& addEntity(const EntityT& entity) { - return addEntity(EntityT(entity)); + return this->_derived().addEntity(entity.name(), EntityT(entity)); } + /// Move and insert an entity. EntityT& addEntity(EntityT&& entity) { - auto it = this->_container.emplace(entity.name(), std::move(entity)).first; - it->second.id().setProviderSegmentID(this->id()); - return it->second; + const std::string name = entity.name(); // Copy before move. + return this->_derived().addEntity(name, std::move(entity)); } - - /** - * @brief Sets the maximum history size of container in this segment. - * This affects all current container as well as new ones. - * @see Entity::setMaxHistorySize() - */ - void setMaxHistorySize(long maxSize) override + /// Insert an entity in-place. + template <class ...Args> + EntityT& addEntity(const std::string& name, Args... args) { - MaxHistorySize::setMaxHistorySize(maxSize); - for (auto& [name, entity] : this->_container) - { - entity.setMaxHistorySize(maxSize); - } + ChildT& child = this->template _addChild<ChildT>(name, args...); + child.id() = this->id().withEntityName(name); + return child; } - virtual bool equalsDeep(const ProviderSegmentBase& other) const + // MISC + + bool equalsDeep(const DerivedT& other) const { - //std::cout << "ProviderSegment::equalsDeep" << std::endl; if (this->size() != other.size()) { return false; @@ -243,30 +296,17 @@ namespace armarx::armem::base return true; } - std::string getLevelName() const override + + static std::string getLevelName() { return "provider segment"; } - std::string getKeyString() const override + std::string getKeyString() const { return this->name(); } - - protected: - - virtual void _copySelf(DerivedT& other) const override - { - Base::_copySelf(other); - other.aronType() = _aronType; - } - virtual void _copySelfEmpty(DerivedT& other) const override - { - Base::_copySelfEmpty(other); - other.aronType() = _aronType; - } - }; } diff --git a/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.cpp b/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.cpp index ec7ca0402ad128dc6d342aa28279929c376df557..48147758528cb5ac46baa8a952272ee831ed1e01 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.cpp +++ b/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.cpp @@ -1,5 +1,8 @@ #include "AronTyped.h" +#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> + + namespace armarx::armem::base::detail { diff --git a/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.h b/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.h index fb0ad088fb4fb27ca39cf09f034ea7eb3b202cfe..16233556c83f8febb732824a7001c9b2e8d1e2ac 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.h +++ b/source/RobotAPI/libraries/armem/core/base/detail/AronTyped.h @@ -1,6 +1,6 @@ #pragma once -#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> namespace armarx::armem::base::detail @@ -13,7 +13,7 @@ namespace armarx::armem::base::detail { public: - AronTyped(aron::typenavigator::ObjectNavigatorPtr aronType = nullptr); + explicit AronTyped(aron::typenavigator::ObjectNavigatorPtr aronType = nullptr); bool hasAronType() const; diff --git a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp b/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp deleted file mode 100644 index 05a23bc12fea34e175218498beca73e01b6f1b13..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "EntityContainerBase.h" diff --git a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h b/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h deleted file mode 100644 index 95993d560c6435fd147ddfc7a2a7aa9267d94855..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "../../Commit.h" -#include "../../error/ArMemError.h" - -#include "MemoryContainerBase.h" - -#include "../EntityBase.h" -#include "../EntitySnapshotBase.h" - - -namespace armarx::armem::base::detail -{ - - /** - * @brief A container of entities at some point in the hierarchy. - * - * Can be updated by multiple entity updates. - */ - template <class _ValueT, class _EntityT, class _Derived> - class EntityContainerBase : - public MemoryContainerBase<std::map<std::string, _ValueT>, _Derived> - { - using Base = MemoryContainerBase<std::map<std::string, _ValueT>, _Derived>; - - public: - - using DerivedT = _Derived; - using ValueT = _ValueT; - - using EntityT = _EntityT; - using EntitySnapshotT = typename EntityT::EntitySnapshotT; - using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; - - public: - - using Base::MemoryContainerBase; - using Base::operator=; - - /** - * @brief Retrieve an entity. - * @param id The entity ID. - * @return The entity. - * @throw An exception deriving from `armem::error::ArMemError` if the entity is missing. - */ - EntityT& getEntity(const MemoryID& id) - { - return const_cast<EntityT&>(const_cast<const EntityContainerBase*>(this)->getEntity(id)); - } - virtual const EntityT& getEntity(const MemoryID& id) const = 0; - - /** - * @brief Find an entity. - * - * Search for the entity with the given ID and return a pointer to the - * first match. If `id` is underspecified (e.g. no provider segment name), - * search all children until the first match is found. - * - * If no matching entity is found, return `nullptr`. - * - * @param id The entities ID. - * @return A pointer to the first matching entity or `nullptr` if none was found. - */ - virtual const EntityT* findEntity(const MemoryID& id) const = 0; - - - /** - * @brief Retrieve an entity snapshot. - * - * Uses `getEntity()` to retrieve the respective entity. - * - * @param id The snapshot ID. - * @return The entity snapshot. - * @throw An exception deriving from `armem::error::ArMemError` if the snapshot is missing. - */ - EntitySnapshotT& getEntitySnapshot(const MemoryID& id) - { - return const_cast<EntitySnapshotT&>(const_cast<const EntityContainerBase*>(this)->getEntitySnapshot(id)); - } - - const EntitySnapshotT& getEntitySnapshot(const MemoryID& id) const - { - const EntityT& entity = getEntity(id); - - if (id.hasTimestamp()) - { - return entity.getSnapshot(id); - } - else - { - return entity.getLatestSnapshot(); - } - } - - EntityInstanceT& getEntityInstance(const MemoryID& id) - { - return getEntitySnapshot(id).getInstance(id); - } - - const EntityInstanceT& getEntityInstance(const MemoryID& id) const - { - return getEntitySnapshot(id).getInstance(id); - } - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/MaxHistorySize.h b/source/RobotAPI/libraries/armem/core/base/detail/MaxHistorySize.h deleted file mode 100644 index 1fc07ed85e54716d24d2447fa17cb95b291f2766..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/base/detail/MaxHistorySize.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - - - -namespace armarx::armem::base::detail -{ - // TODO: Replace by ConstrainedHistorySize (not only max entries, e.g. delete oldest / delete least accessed / ...) - class MaxHistorySize - { - public: - - virtual ~MaxHistorySize(); - - /** - * @brief Sets the maximum history size of entities in this segment. - * This affects all current entities as well as new ones. - * @see Entity::setMaxHistorySize() - */ - virtual void setMaxHistorySize(long maxSize); - virtual long getMaxHistorySize() const; - - - protected: - - /** - * @brief Maximum size of entity histories. - * - * If negative, the size of `history` is not limited. - * - * @see Entity::maxHstorySize - */ - long _maxHistorySize = -1; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/MemoryContainerBase.h b/source/RobotAPI/libraries/armem/core/base/detail/MemoryContainerBase.h index 518e3f877f38ea364657c0a5448d48bc6cf36fbf..3e22caa31456c6a2afab30a721d064bb661427f5 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/MemoryContainerBase.h +++ b/source/RobotAPI/libraries/armem/core/base/detail/MemoryContainerBase.h @@ -3,6 +3,7 @@ #include <RobotAPI/libraries/armem/core/error/ArMemError.h> #include "MemoryItem.h" +#include "iteration_mixins.h" namespace armarx::armem::base::detail @@ -18,16 +19,16 @@ namespace armarx::armem::base::detail using Base = MemoryItem; public: + using DerivedT = _DerivedT; using ContainerT = _ContainerT; public: - MemoryContainerBase() {} - MemoryContainerBase(const MemoryID& id) : + explicit MemoryContainerBase(const MemoryID& id) : MemoryItem(id) { } @@ -48,32 +49,56 @@ namespace armarx::armem::base::detail { return _container.size(); } - bool hasData() const - { - return size() > 0; - } void clear() { return _container.clear(); } + + // ITERATION + + /** + * @param func Function like: bool process(ChildT& provSeg) + */ + template <class ChildFunctionT> + bool forEachChild(ChildFunctionT&& func) + { + return base::detail::forEachChild(this->_container, func); + } + /** + * @param func Function like: bool process(const ChildT& provSeg) + */ + template <class ChildFunctionT> + bool forEachChild(ChildFunctionT&& func) const + { + return base::detail::forEachChild(this->_container, func); + } + + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] typename ContainerT::const_iterator begin() const { return _container.begin(); } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] typename ContainerT::iterator begin() { return _container.begin(); } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] typename ContainerT::const_iterator end() const { return _container.end(); } + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] typename ContainerT::iterator end() { return _container.end(); } + + protected: + const ContainerT& container() const { return _container; @@ -83,25 +108,16 @@ namespace armarx::armem::base::detail return _container; } - // Copying - DerivedT copy() const + DerivedT& _derived() { - DerivedT d; - this->_copySelf(d); - return d; + return static_cast<DerivedT&>(*this); } - - /// Make a copy not containing any elements. - DerivedT copyEmpty() const + const DerivedT& _derived() const { - DerivedT d; - this->_copySelfEmpty(d); - return std::move(d); + return static_cast<DerivedT&>(*this); } - protected: - /** * @throw `armem::error::ContainerNameMismatch` if `gottenName` does not match `actualName`. */ @@ -110,19 +126,22 @@ namespace armarx::armem::base::detail { if (!((emptyOk && gottenName.empty()) || gottenName == actualName)) { - throw armem::error::ContainerNameMismatch(gottenName, this->getLevelName(), actualName); + throw armem::error::ContainerNameMismatch( + gottenName, DerivedT::getLevelName(), actualName); } } - virtual void _copySelf(DerivedT& other) const - { - Base::_copySelf(other); - other.container() = _container; - } - virtual void _copySelfEmpty(DerivedT& other) const + template <class ChildT, class KeyT, class ...ChildArgs> + ChildT& _addChild(const KeyT& key, ChildArgs... childArgs) { - Base::_copySelf(other); + auto [it, inserted] = this->_container.try_emplace(key, childArgs...); + if (not inserted) + { + throw armem::error::ContainerEntryAlreadyExists( + ChildT::getLevelName(), key, DerivedT::getLevelName(), this->_derived().name()); + } + return it->second; } diff --git a/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.cpp b/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.cpp index a6a9f73e17443091503c09472bc3cee7259644ac..8f4a1920f0a1fcd669b9c3fc2193a617b4dba205 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.cpp +++ b/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.cpp @@ -8,33 +8,15 @@ namespace armarx::armem::base::detail { } + MemoryItem::MemoryItem(const MemoryID& id) : _id(id) { } - /* - MemoryItem::MemoryItem(const MemoryItem& other) : - _id(other.id()) - {} - */ - MemoryItem::~MemoryItem() { } - /* - MemoryItem& MemoryItem::operator=(const MemoryItem& other) - { - other._copySelf(*this); - return *this; - } - */ - - void MemoryItem::_copySelf(MemoryItem& other) const - { - other.id() = id(); - } - } diff --git a/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.h b/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.h index 45226e6327520b7bbddc198364c16c2b8229352c..6aea07121aa201fcbbf0f0b18f46ccc3ab4e2aac 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.h +++ b/source/RobotAPI/libraries/armem/core/base/detail/MemoryItem.h @@ -2,7 +2,7 @@ #include <string> -#include "../../MemoryID.h" +#include <RobotAPI/libraries/armem/core/MemoryID.h> namespace armarx::armem::base::detail @@ -16,18 +16,13 @@ namespace armarx::armem::base::detail public: MemoryItem(); - MemoryItem(const MemoryID& id); + explicit MemoryItem(const MemoryID& id); MemoryItem(const MemoryItem& other) = default; MemoryItem(MemoryItem&& other) = default; MemoryItem& operator=(const MemoryItem& other) = default; MemoryItem& operator=(MemoryItem&& other) = default; - virtual ~MemoryItem(); - - - //MemoryItem& operator=(const MemoryItem& other); - inline MemoryID& id() { @@ -39,19 +34,11 @@ namespace armarx::armem::base::detail } - // Introspection - - /// Get a string version of `*this`' key. - virtual std::string getKeyString() const = 0; - - /// Get a readable name of this level for messages, errors etc. - virtual std::string getLevelName() const = 0; - - protected: - /// Copy `*this` to `other`. - virtual void _copySelf(MemoryItem& other) const; + // Protected so we get a compile error if someone tries to destruct a MemoryItem + // instead of a derived type. + ~MemoryItem(); protected: diff --git a/source/RobotAPI/libraries/armem/core/base/detail/derived.h b/source/RobotAPI/libraries/armem/core/base/detail/derived.h new file mode 100644 index 0000000000000000000000000000000000000000..721becfa05e34e603f2c1bdb852cea191faf42a5 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/derived.h @@ -0,0 +1,22 @@ +#pragma once + + +namespace armarx::armem::base::detail +{ + + template <class DerivedT, class ThisT> + DerivedT& + derived(ThisT* t) + { + return static_cast<DerivedT&>(*t); + } + + + template <class DerivedT, class ThisT> + const DerivedT& + derived(const ThisT* t) + { + return static_cast<const DerivedT&>(*t); + } + +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.cpp b/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.cpp new file mode 100644 index 0000000000000000000000000000000000000000..459cd4d5c7bd694bcb289496352d7e2cad4966cd --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.cpp @@ -0,0 +1,7 @@ +#include "iteration_mixins.h" + + +namespace armarx::armem::base::detail +{ + +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h b/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h new file mode 100644 index 0000000000000000000000000000000000000000..c923a857e1d016b06b313f7e860ab57ec438a9d4 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/iteration_mixins.h @@ -0,0 +1,214 @@ +#pragma once + +#include <type_traits> + + +namespace armarx::armem::base::detail +{ + + // Helper functions to implement the forEach*() method at the current level. + + // Handle functions with different return type. + template <class FunctionT, class ChildT> + bool call(FunctionT&& func, ChildT&& child) + { + if constexpr(std::is_same_v<decltype(func(child)), bool>) + { + if (!func(child)) + { + return false; + } + return true; + } + else + { + func(child); + return true; + } + } + + + // Single-valued containers. + template <class ContainerT, class FunctionT> + bool forEachChildSingle(ContainerT& container, FunctionT&& func) + { + for (auto& child : container) + { + if (not call(func, child)) + { + return false; + } + } + return true; + } + + + // Pair-valued containers. + template <class ContainerT, class FunctionT> + bool forEachChildPair(ContainerT& container, FunctionT&& func) + { + for (auto& [_, child] : container) + { + if (not call(func, child)) + { + return false; + } + } + return true; + } + + + // see: https://en.cppreference.com/w/cpp/types/void_t + + // primary template handles types that have no nested ::type member: + template< class, class = void > + struct has_mapped_type : std::false_type { }; + + // specialization recognizes types that do have a nested ::type member: + template< class T > + struct has_mapped_type<T, std::void_t<typename T::mapped_type>> : std::true_type { }; + + + template <class ContainerT, class FunctionT> + bool forEachChild(ContainerT& container, FunctionT&& func) + { + if constexpr(has_mapped_type<ContainerT>::value) + { + return forEachChildPair(container, func); + } + else + { + return forEachChildSingle(container, func); + } + } + + + // We use auto instead of, e.g. DerivedT::EntitySnapshotT, + // as we cannot use the typedef before DerivedT was completely defined. + + template <class DerivedT> + struct ForEachEntityInstanceMixin + { + // not: using EntitySnapshotT = typename DerivedT::EntitySnapshotT; + + /** + * @param func Function like: bool process(EntityInstanceT& instance)> + */ + template <class InstanceFunctionT> + bool forEachInstance(InstanceFunctionT&& func) + { + return static_cast<DerivedT*>(this)->forEachSnapshot( + [&func](auto & snapshot) -> bool + { + return snapshot.forEachInstance(func); + }); + } + + /** + * @param func Function like: bool process(const EntityInstanceT& instance) + */ + template <class InstanceFunctionT> + bool forEachInstance(InstanceFunctionT&& func) const + { + return static_cast<const DerivedT*>(this)->forEachSnapshot( + [&func](const auto & snapshot) -> bool + { + return snapshot.forEachInstance(func); + }); + } + }; + + + template <class DerivedT> + struct ForEachEntitySnapshotMixin + { + /** + * @param func Function like: bool process(EntitySnapshotT& snapshot)> + */ + template <class SnapshotFunctionT> + bool forEachSnapshot(SnapshotFunctionT&& func) + { + return static_cast<DerivedT*>(this)->forEachEntity( + [&func](auto & entity) -> bool + { + return entity.forEachSnapshot(func); + }); + } + + /** + * @param func Function like: bool process(const EntitySnapshotT& snapshot) + */ + template <class SnapshotFunctionT> + bool forEachSnapshot(SnapshotFunctionT&& func) const + { + return static_cast<const DerivedT*>(this)->forEachEntity( + [&func](const auto & entity) -> bool + { + return entity.forEachSnapshot(func); + }); + } + }; + + + template <class DerivedT> + struct ForEachEntityMixin + { + /** + * @param func Function like: bool process(EntityT& entity)> + */ + template <class FunctionT> + bool forEachEntity(FunctionT&& func) + { + return static_cast<DerivedT*>(this)->forEachProviderSegment( + [&func](auto & providerSegment) -> bool + { + return providerSegment.forEachEntity(func); + }); + } + + /** + * @param func Function like: bool process(const EntityT& entity) + */ + template <class FunctionT> + bool forEachEntity(FunctionT&& func) const + { + return static_cast<const DerivedT*>(this)->forEachProviderSegment( + [&func](const auto & providerSegment) -> bool + { + return providerSegment.forEachEntity(func); + }); + } + }; + + + template <class DerivedT> + struct ForEachProviderSegmentMixin + { + /** + * @param func Function like: bool process(ProviderSegmentT& providerSegment)> + */ + template <class FunctionT> + bool forEachProviderSegment(FunctionT&& func) + { + return static_cast<DerivedT*>(this)->forEachCoreSegment( + [&func](auto & coreSegment) -> bool + { + return coreSegment.forEachProviderSegment(func); + }); + } + + /** + * @param func Function like: bool process(const ProviderSegmentT& providerSegment) + */ + template <class FunctionT> + bool forEachProviderSegment(FunctionT&& func) const + { + return static_cast<const DerivedT*>(this)->forEachCoreSegment( + [&func](const auto & coreSegment) -> bool + { + return coreSegment.forEachProviderSegment(func); + }); + } + }; + +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2b35f0b895cf6e4ef3e53a81a8af3ac0eb31a89d --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp @@ -0,0 +1,63 @@ +#include "lookup_mixins.h" + +#include <RobotAPI/libraries/armem/core/error/ArMemError.h> + + +namespace armarx::armem::base +{ + + void detail::checkHasInstanceIndex(const MemoryID& instanceID) + { + if (not instanceID.hasInstanceIndex()) + { + throw armem::error::InvalidMemoryID(instanceID, "Instance ID has no instance index."); + } + } + + + void detail::checkHasTimestamp(const MemoryID& snapshotID) + { + if (not snapshotID.hasTimestamp()) + { + throw armem::error::InvalidMemoryID(snapshotID, "Snapshot ID has no timestamp."); + } + } + + + void detail::checkHasEntityName(const MemoryID& entityID) + { + if (not entityID.hasEntityName()) + { + throw armem::error::InvalidMemoryID(entityID, "Entity ID has no entity name."); + } + } + + + void detail::checkHasProviderSegmentName(const MemoryID& providerSegmentID) + { + if (not providerSegmentID.hasProviderSegmentName()) + { + throw armem::error::InvalidMemoryID(providerSegmentID, "Provider segment ID has no provider segment name."); + } + } + + + void detail::checkHasCoreSegmentName(const MemoryID& coreSegmentID) + { + if (not coreSegmentID.hasCoreSegmentName()) + { + throw armem::error::InvalidMemoryID(coreSegmentID, "Core segment ID has no core segment name."); + } + } + + + void detail::checkHasMemoryName(const MemoryID& memoryID) + { + if (not memoryID.hasMemoryName()) + { + throw armem::error::InvalidMemoryID(memoryID, "Memory ID has no memory name."); + } + } + + +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h new file mode 100644 index 0000000000000000000000000000000000000000..006a5fbcef24dd6c3978d78546cd15dff03a72bc --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h @@ -0,0 +1,274 @@ +#pragma once + +#include "derived.h" + +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/error/ArMemError.h> + + +namespace armarx::armem::base::detail +{ + + void checkHasInstanceIndex(const MemoryID& instanceID); + void checkHasTimestamp(const MemoryID& snapshotID); + void checkHasEntityName(const MemoryID& entityID); + void checkHasProviderSegmentName(const MemoryID& providerSegmentID); + void checkHasCoreSegmentName(const MemoryID& coreSegmentID); + void checkHasMemoryName(const MemoryID& memory); + + + template <class KeyT, class ContainerT> + auto* findChildByKey(const KeyT& key, ContainerT&& container) + { + auto it = container.find(key); + return it != container.end() ? &it->second : nullptr; + } + + template <class KeyT, class ContainerT, class ParentT, class KeyStringFn> + auto& + getChildByKey( + const KeyT& key, + ContainerT&& container, + const ParentT& parent, + KeyStringFn&& keyStringFn) + { + if (auto* child = findChildByKey(key, container)) + { + return *child; + } + else + { + throw armem::error::MissingEntry::create<typename ParentT::ChildT>(keyStringFn(key), parent); + } + } + template <class KeyT, class ContainerT, class ParentT> + auto& + getChildByKey( + const KeyT& key, + ContainerT&& container, + const ParentT& parent) + { + return getChildByKey(key, container, parent, [](const KeyT & key) + { + return key; + }); + } + + + template <class DerivedT> + struct GetFindInstanceMixin + { + // Relies on this->find/getSnapshot() + + bool hasInstance(const MemoryID& instanceID) const + { + return derived<DerivedT>(this).findInstance(instanceID) != nullptr; + } + + /** + * @brief Find an entity instance. + * @param id The instance ID. + * @return The instance or nullptr if it is missing. + */ + auto* + findInstance(const MemoryID& instanceID) + { + auto* snapshot = derived<DerivedT>(this).findSnapshot(instanceID); + return snapshot ? snapshot->findInstance(instanceID) : nullptr; + } + const auto* + findInstance(const MemoryID& instanceID) const + { + const auto* snapshot = derived<DerivedT>(this).findSnapshot(instanceID); + return snapshot ? snapshot->findInstance(instanceID) : nullptr; + } + + /** + * @brief Retrieve an entity instance. + * @param id The instance ID. + * @return The instance if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getInstance(const MemoryID& instanceID) + { + return derived<DerivedT>(this).getSnapshot(instanceID).getInstance(instanceID); + } + const auto& + getInstance(const MemoryID& instanceID) const + { + return derived<DerivedT>(this).getSnapshot(instanceID).getInstance(instanceID); + } + }; + + + template <class DerivedT> + struct GetFindSnapshotMixin + { + // Relies on this->find/getEntity() + + bool hasSnapshot(const MemoryID& snapshotID) const + { + return derived<DerivedT>(this).findSnapshot(snapshotID) != nullptr; + } + + /** + * @brief Find an entity snapshot. + * @param id The snapshot ID. + * @return The snapshot or nullptr if it is missing. + */ + auto* + findSnapshot(const MemoryID& snapshotID) + { + auto* entity = derived<DerivedT>(this).findEntity(snapshotID); + return entity ? entity->findSnapshot(snapshotID) : nullptr; + } + const auto* + findSnapshot(const MemoryID& snapshotID) const + { + auto* entity = derived<DerivedT>(this).findEntity(snapshotID); + return entity ? entity->findSnapshot(snapshotID) : nullptr; + } + + /** + * @brief Retrieve an entity snapshot. + * @param id The snapshot ID. + * @return The snapshot if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getSnapshot(const MemoryID& snapshotID) + { + return derived<DerivedT>(this).getEntity(snapshotID).getSnapshot(snapshotID); + } + const auto& + getSnapshot(const MemoryID& snapshotID) const + { + return derived<DerivedT>(this).getEntity(snapshotID).getSnapshot(snapshotID); + } + + + // More elaborate cases + + auto* findLatestSnapshot(const MemoryID& entityID) + { + auto* entity = derived<DerivedT>(this).findEntity(entityID); + return entity ? entity->findLatestSnapshot() : nullptr; + } + const auto* findLatestSnapshot(const MemoryID& entityID) const + { + auto* entity = derived<DerivedT>(this).findEntity(entityID); + return entity ? entity->findLatestSnapshot() : nullptr; + } + + auto* findLatestInstance(const MemoryID& entityID, int instanceIndex = 0) + { + auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(entityID); + return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; + } + const auto* findLatestInstance(const MemoryID& entityID, int instanceIndex = 0) const + { + auto* snapshot = derived<DerivedT>(this).findLatestSnapshot(entityID); + return snapshot ? snapshot->findInstance(instanceIndex) : nullptr; + } + + }; + + + + template <class DerivedT> + struct GetFindEntityMixin + { + // Relies on this->find/getProviderSegment() + + bool hasEntity(const MemoryID& entityID) const + { + return derived<DerivedT>(this).findEntity(entityID) != nullptr; + } + + /** + * @brief Find an entity. + * @param id The entity ID. + * @return The entity or nullptr if it is missing. + */ + auto* + findEntity(const MemoryID& entityID) + { + auto* provSeg = derived<DerivedT>(this).findProviderSegment(entityID); + return provSeg ? provSeg->findEntity(entityID) : nullptr; + } + const auto* + findEntity(const MemoryID& entityID) const + { + auto* provSeg = derived<DerivedT>(this).findProviderSegment(entityID); + return provSeg ? provSeg->findEntity(entityID) : nullptr; + } + + /** + * @brief Retrieve an entity. + * @param id The entity ID. + * @return The entity if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getEntity(const MemoryID& entityID) + { + return derived<DerivedT>(this).getProviderSegment(entityID).getEntity(entityID); + } + const auto& + getEntity(const MemoryID& entityID) const + { + return derived<DerivedT>(this).getProviderSegment(entityID).getEntity(entityID); + } + }; + + + + template <class DerivedT> + struct GetFindProviderSegmentMixin + { + // Relies on this->find/getCoreSegment() + + bool hasProviderSegment(const MemoryID& providerSegmentID) const + { + return derived<DerivedT>(this).findProviderSegment(providerSegmentID) != nullptr; + } + + /** + * @brief Retrieve a provider segment. + * @param id The provider segment ID. + * @return The provider segment if it is found or nullptr if it missing. + */ + auto* + findProviderSegment(const MemoryID& providerSegmentID) + { + auto* provSeg = derived<DerivedT>(this).findCoreSegment(providerSegmentID); + return provSeg ? provSeg->findProviderSegment(providerSegmentID) : nullptr; + } + const auto* + findProviderSegment(const MemoryID& providerSegmentID) const + { + auto* provSeg = derived<DerivedT>(this).findCoreSegment(providerSegmentID); + return provSeg ? provSeg->findProviderSegment(providerSegmentID) : nullptr; + } + + /** + * @brief Retrieve a provider segment. + * @param id The provider segment ID. + * @return The provider segment if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getProviderSegment(const MemoryID& providerSegmentID) + { + return derived<DerivedT>(this).getCoreSegment(providerSegmentID).getProviderSegment(providerSegmentID); + } + const auto& + getProviderSegment(const MemoryID& providerSegmentID) const + { + return derived<DerivedT>(this).getCoreSegment(providerSegmentID).getProviderSegment(providerSegmentID); + } + + }; + +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.cpp b/source/RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a1875ff53545a3d2574cf9764a14d321946e5514 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.cpp @@ -0,0 +1,17 @@ +#include "negative_index_semantics.h" + +#include <algorithm> + + +size_t armarx::armem::base::detail::negativeIndexSemantics(long index, size_t size) +{ + const size_t max = size > 0 ? size - 1 : 0; + if (index >= 0) + { + return std::clamp<size_t>(static_cast<size_t>(index), 0, max); + } + else + { + return static_cast<size_t>(std::clamp<long>(static_cast<long>(size) + index, 0, static_cast<long>(max))); + } +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.h b/source/RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.h new file mode 100644 index 0000000000000000000000000000000000000000..4efe169276c83ff12c13400e6de162a8b4db5d8d --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.h @@ -0,0 +1,11 @@ +#pragma once + +#include <stddef.h> + + +namespace armarx::armem::base::detail +{ + + size_t negativeIndexSemantics(long index, size_t size); + +} diff --git a/source/RobotAPI/libraries/armem/core/base/ice_conversions.cpp b/source/RobotAPI/libraries/armem/core/base/ice_conversions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48c084154c41b26b174504d2565fe57d3a49d55a --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/ice_conversions.cpp @@ -0,0 +1,78 @@ +#include "ice_conversions.h" + +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> + +#include <RobotAPI/libraries/armem/core/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/ice_conversions_templates.h> + + +namespace armarx::armem::base +{ + + void detail::toIceItem(data::detail::MemoryItem& ice, const armem::base::detail::MemoryItem& item) + { + toIce(ice.id, item.id()); + } + void detail::fromIceItem(const data::detail::MemoryItem& ice, armem::base::detail::MemoryItem& item) + { + fromIce(ice.id, item.id()); + } + + + void detail::toIce(aron::data::AronDictPtr& ice, const aron::datanavigator::DictNavigatorPtr& data) + { + ice = data ? data->toAronDictPtr() : nullptr; + } + void detail::fromIce(const aron::data::AronDictPtr& ice, aron::datanavigator::DictNavigatorPtr& data) + { + if (ice) + { + data = aron::datanavigator::DictNavigator::FromAronDictPtr(ice); + } + else + { + data = nullptr; + }; + } + + void detail::toIce(aron::type::AronTypePtr& ice, const aron::typenavigator::ObjectNavigatorPtr& bo) + { + ice = bo ? bo->toAronPtr() : nullptr; + } + void detail::fromIce(const aron::type::AronTypePtr& ice, aron::typenavigator::ObjectNavigatorPtr& bo) + { + bo = ice + ? aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(aron::typenavigator::Navigator::FromAronType(ice)) + : nullptr; + } + +} +namespace armarx::armem +{ + void base::toIce(data::EntityInstanceMetadata& ice, const EntityInstanceMetadata& metadata) + { + ice.confidence = metadata.confidence; + toIce(ice.timeArrivedMicroSeconds, metadata.timeArrived); + toIce(ice.timeCreatedMicroSeconds, metadata.timeCreated); + toIce(ice.timeSentMicroSeconds, metadata.timeSent); + } + void base::fromIce(const data::EntityInstanceMetadata& ice, EntityInstanceMetadata& metadata) + { + metadata.confidence = ice.confidence; + fromIce(ice.timeArrivedMicroSeconds, metadata.timeArrived); + fromIce(ice.timeCreatedMicroSeconds, metadata.timeCreated); + fromIce(ice.timeSentMicroSeconds, metadata.timeSent); + } + + + void base::toIce(data::EntityInstanceMetadataPtr& ice, const EntityInstanceMetadata& metadata) + { + armem::toIce<data::EntityInstanceMetadata>(ice, metadata); + } + void base::fromIce(const data::EntityInstanceMetadataPtr& ice, EntityInstanceMetadata& metadata) + { + armem::fromIce<data::EntityInstanceMetadata>(ice, metadata); + } + +} diff --git a/source/RobotAPI/libraries/armem/core/base/ice_conversions.h b/source/RobotAPI/libraries/armem/core/base/ice_conversions.h new file mode 100644 index 0000000000000000000000000000000000000000..182e0c0a47aff1a75b356b9cc0b3930450de75e4 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/ice_conversions.h @@ -0,0 +1,190 @@ +#pragma once + +#include "EntityInstanceBase.h" +#include "EntitySnapshotBase.h" +#include "EntityBase.h" +#include "ProviderSegmentBase.h" +#include "CoreSegmentBase.h" +#include "MemoryBase.h" + +#include <RobotAPI/libraries/armem/core/ice_conversions_templates.h> +#include <RobotAPI/libraries/armem/core/ice_conversions.h> + +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> + +#include <RobotAPI/interface/armem/memory.h> + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + + +namespace armarx::armem::base::detail +{ + void toIceItem(data::detail::MemoryItem& ice, const MemoryItem& item); + void fromIceItem(const data::detail::MemoryItem& ice, MemoryItem& item); + + void toIce(aron::data::AronDictPtr& ice, const aron::datanavigator::DictNavigatorPtr& bo); + void fromIce(const aron::data::AronDictPtr& ice, aron::datanavigator::DictNavigatorPtr& bo); + + void toIce(aron::type::AronTypePtr& ice, const aron::typenavigator::ObjectNavigatorPtr& bo); + void fromIce(const aron::type::AronTypePtr& ice, aron::typenavigator::ObjectNavigatorPtr& bo); +} +namespace armarx::armem::base +{ + void toIce(data::EntityInstanceMetadata& ice, const EntityInstanceMetadata& metadata); + void fromIce(const data::EntityInstanceMetadata& ice, EntityInstanceMetadata& metadata); + + void toIce(data::EntityInstanceMetadataPtr& ice, const EntityInstanceMetadata& metadata); + void fromIce(const data::EntityInstanceMetadataPtr& ice, EntityInstanceMetadata& metadata); + + + template <class ...Args> + void toIce(data::EntityInstance& ice, const EntityInstanceBase<Args...>& data) + { + detail::toIceItem(ice, data); + + // detail::toIce(std::ref(ice.data).get(), data.data()); + detail::toIce(ice.data, data.data()); + toIce(ice.metadata, data.metadata()); + } + template <class ...Args> + void fromIce(const data::EntityInstance& ice, EntityInstanceBase<Args...>& data) + { + detail::fromIceItem(ice, data); + + detail::fromIce(ice.data, data.data()); + fromIce(ice.metadata, data.metadata()); + } + + template <class ...Args> + void toIce(data::EntitySnapshot& ice, const EntitySnapshotBase<Args...>& snapshot) + { + detail::toIceItem(ice, snapshot); + + ice.instances.clear(); + snapshot.forEachInstance([&ice](const auto & instance) + { + armem::toIce(ice.instances.emplace_back(), instance); + }); + } + template <class ...Args> + void fromIce(const data::EntitySnapshot& ice, EntitySnapshotBase<Args...>& snapshot) + { + detail::fromIceItem(ice, snapshot); + + snapshot.clear(); + for (const data::EntityInstancePtr& iceInstance : ice.instances) + { + snapshot.addInstance(armem::fromIce<typename EntitySnapshotBase<Args...>::EntityInstanceT>(iceInstance)); + } + } + + template <class ...Args> + void toIce(data::Entity& ice, const EntityBase<Args...>& entity) + { + detail::toIceItem(ice, entity); + + ice.history.clear(); + entity.forEachSnapshot([&ice](const auto & snapshot) + { + armem::toIce(ice.history[armem::toIce<long>(snapshot.time())], snapshot); + }); + } + template <class ...Args> + void fromIce(const data::Entity& ice, EntityBase<Args...>& entity) + { + detail::fromIceItem(ice, entity); + + entity.clear(); + for (const auto& [key, snapshot] : ice.history) + { + entity.addSnapshot(armem::fromIce<typename EntityBase<Args...>::EntitySnapshotT>(snapshot)); + } + } + + + template <class ...Args> + void toIce(data::ProviderSegment& ice, const ProviderSegmentBase<Args...>& providerSegment) + { + detail::toIceItem(ice, providerSegment); + + detail::toIce(ice.aronType, providerSegment.aronType()); + ARMARX_CHECK(!providerSegment.aronType() || ice.aronType); + + ice.entities.clear(); + providerSegment.forEachEntity([&ice](const auto & entity) + { + armem::toIce(ice.entities[entity.name()], entity); + }); + } + template <class ...Args> + void fromIce(const data::ProviderSegment& ice, ProviderSegmentBase<Args...>& providerSegment) + { + detail::fromIceItem(ice, providerSegment); + + detail::fromIce(ice.aronType, providerSegment.aronType()); + ARMARX_CHECK(!providerSegment.aronType() || ice.aronType); + + providerSegment.clear(); + for (const auto& [key, entity] : ice.entities) + { + providerSegment.addEntity( + armem::fromIce<typename ProviderSegmentBase<Args...>::EntityT>(entity)); + } + } + + template <class ...Args> + void toIce(data::CoreSegment& ice, const CoreSegmentBase<Args...>& coreSegment) + { + detail::toIceItem(ice, coreSegment); + + detail::toIce(ice.aronType, coreSegment.aronType()); + ARMARX_CHECK(!coreSegment.aronType() || ice.aronType); + + ice.providerSegments.clear(); + coreSegment.forEachProviderSegment([&ice](const auto & providerSegment) + { + armem::toIce(ice.providerSegments[providerSegment.name()], providerSegment); + }); + } + template <class ...Args> + void fromIce(const data::CoreSegment& ice, CoreSegmentBase<Args...>& coreSegment) + { + detail::fromIceItem(ice, coreSegment); + + detail::fromIce(ice.aronType, coreSegment.aronType()); + ARMARX_CHECK(!coreSegment.aronType() || ice.aronType); + + // fromIce(ice.providerSegments, coreSegment.providerSegments()); + coreSegment.clear(); + for (const auto& [key, providerSegment] : ice.providerSegments) + { + coreSegment.addProviderSegment( + armem::fromIce<typename CoreSegmentBase<Args...>::ProviderSegmentT>(providerSegment)); + } + } + + template <class ...Args> + void toIce(data::Memory& ice, const MemoryBase<Args...>& memory) + { + base::detail::toIceItem(ice, memory); + + ice.coreSegments.clear(); + memory.forEachCoreSegment([&ice](const auto & coreSegment) + { + armem::toIce(ice.coreSegments[coreSegment.name()], coreSegment); + }); + } + template <class ...Args> + void fromIce(const data::Memory& ice, MemoryBase<Args...>& memory) + { + base::detail::fromIceItem(ice, memory); + + memory.clear(); + for (const auto& [key, coreSegment] : ice.coreSegments) + { + // We can avoid using CoreSegment's copy constructor this way: + armem::fromIce(coreSegment, memory.addCoreSegment(coreSegment->id.coreSegmentName)); + } + } + +} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.cpp deleted file mode 100644 index b166468d81929da12c8204a6e2df9ab90b994528..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "CoreSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "TypeIO.h" - - -namespace armarx::armem::d_ltm -{ - - std::filesystem::path CoreSegment::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path CoreSegment::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName; - } - - wm::CoreSegment CoreSegment::convert() const - { - wm::CoreSegment m(id()); - for (const auto& [_, s] : _container) - { - m.addProviderSegment(s.convert(_aronType)); - } - return m; - } - - void CoreSegment::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = p_ptr; - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Assuming an empty container."; - } - else - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - auto wms = _container.emplace(k, id().withProviderSegmentName(k)); - wms.first->second.reload(p_ptr); - } - - if (d.is_regular_file()) - { - if (auto type = TypeIO::readAronType(d.path())) - { - _aronType = type; - } - } - } - } - } - - void CoreSegment::append(const wm::CoreSegment& m) - { - std::filesystem::create_directories(_fullPath()); - TypeIO::writeAronType(_aronType, _fullPath()); - - for (const auto& [k, s] : m) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - it->second.append(s); - } - else - { - try - { - std::filesystem::create_directory(_fullPath() / k); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - auto wms = _container.emplace(k, id().withProviderSegmentName(k)); - wms.first->second.path = path; - wms.first->second.append(s); - } - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.h b/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.h deleted file mode 100644 index 76bb297e76d268ce8517449686ffae1cdb3ed2cd..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/CoreSegment.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include <filesystem> - -#include "../base/CoreSegmentBase.h" -#include "../workingmemory/CoreSegment.h" - -#include "ProviderSegment.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of a core segment containing multiple provider segments. - */ - class CoreSegment : - public base::CoreSegmentBase<ProviderSegment, CoreSegment> - { - using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; - - public: - - using Base::CoreSegmentBase; - using Base::operator=; - - - // Conversion - wm::CoreSegment convert() const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void append(const wm::CoreSegment&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/Entity.cpp deleted file mode 100644 index 5d784a097de4ef68ec2df0bdc1adce94fa737809..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "Entity.h" - -namespace armarx::armem::d_ltm -{ - std::filesystem::path Entity::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path Entity::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName / id().entityName; - } - - wm::Entity Entity::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - wm::Entity m(id()); - for (const auto& [_, s] : _container) - { - m.addSnapshot(s.convert(expectedStructure)); - } - return m; - } - - void Entity::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = p_ptr; - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Assuming an empty container."; - } - else - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - armem::Time t = armem::Time::microSeconds(std::stol(k)); - auto wms = _container.emplace(std::make_pair(t, id().withTimestamp(t))); - wms.first->second.reload(p_ptr); - } - } - } - } - - void Entity::append(const wm::Entity& m) - { - std::filesystem::create_directories(_fullPath()); - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - // timestamp already exists - // We assume that a snapshot does not change, so ignore - } - else - { - std::filesystem::create_directory(_fullPath() / std::to_string(k.toMicroSeconds())); - auto wms = _container.emplace(std::make_pair(k, id().withTimestamp(k))); - wms.first->second.path = path; - wms.first->second.setTo(s); - } - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.h b/source/RobotAPI/libraries/armem/core/diskmemory/Entity.h deleted file mode 100644 index 35022275570c93378cbe0c767aad1776da594838..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Entity.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include <filesystem> - -#include "../base/EntityBase.h" -#include "../workingmemory/Entity.h" - -#include "EntitySnapshot.h" - - - -namespace armarx::armem::d_ltm -{ - /** - * @brief An entity over a period of time. - * - * An entity should be a physical thing or abstract concept existing - * (and potentially evolving) over some time. - * - * Examples are: - * - objects (the green box) - * - agents (robot, human) - * - locations (frige, sink) - * - grasp affordances (general, or for a specific object) - * - images - * - point clouds - * - other sensory values - * - * At each point in time (`EntitySnapshot`), the entity can have a - * (potentially variable) number of instances (`EntityInstance`), - * each containing a single `AronData` object of a specific `AronType`. - */ - class Entity : - public base::EntityBase<EntitySnapshot, Entity> - { - using Base = base::EntityBase<EntitySnapshot, Entity>; - - public: - - using Base::EntityBase; - using Base::operator=; - - - // Conversion - wm::Entity convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void append(const wm::Entity&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.cpp deleted file mode 100644 index dd0458b83bbb8ed461ddeab50af42181fda843e4..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "EntityInstance.h" - -#include <iostream> -#include <fstream> - -#include "../../core/error.h" -#include "ArmarXCore/core/exceptions/LocalException.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -namespace armarx::armem::d_ltm -{ - - EntityInstance::EntityInstance(const EntityInstance& other) : - Base(other), - path(other.path) - { - } - - bool EntityInstance::equalsDeep(const EntityInstance& other) const - { - return id() == other.id(); - } - - - - void EntityInstance::update(const EntityUpdate& update, int index) - { - ARMARX_CHECK_FITS_SIZE(index, update.instancesData.size()); - - this->index() = index; - } - - EntityInstance EntityInstance::copy() const - { - EntityInstance d; - this->_copySelf(d); - return d; - } - - void EntityInstance::_copySelf(EntityInstance& other) const - { - EntityInstanceBase<EntityInstance>::_copySelf(other); - other.path = path; - } - - std::filesystem::path EntityInstance::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - ARMARX_WARNING << "The path of the disk memory instance with id '" << id().str() << "' is not set. This may lead to errors."; - return std::filesystem::path(); - } - - std::filesystem::path EntityInstance::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName / id().entityName / std::to_string(id().timestamp.toMicroSeconds()) / std::to_string(id().instanceIndex); - } - - wm::EntityInstance EntityInstance::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - std::filesystem::path p = _fullPath(); // here we assume that "reload" has been called first - std::filesystem::path d = p / (std::string(DATA_FILENAME) + ".json"); - - if (std::filesystem::is_regular_file(d)) - { - std::ifstream ifs(d); - std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); - - nlohmann::json j = nlohmann::json::parse(file_content); - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, j, expectedStructure); - wm::EntityInstance e(id()); - from_aron(aron, e); - return e; - } - else - { - throw error::ArMemError("An diskMemory EntityInstance is not leading to a regular file. The path was: " + d.string()); - } - } - - void EntityInstance::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered path is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (!std::filesystem::is_directory(p)) - { - ARMARX_ERROR << "The entered path is not leading to a file! This is an error since if the folder for an EntityInstance exists there must be a data file in it (containing at least the metadata)."; - } - else - { - path = p_ptr; - } - } - - void EntityInstance::setTo(const wm::EntityInstance& m) - { - std::filesystem::path p = _fullPath(); - - try - { - std::filesystem::create_directories(p); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - std::filesystem::path d = p / (std::string(DATA_FILENAME) + ".json"); - - if (std::filesystem::is_regular_file(d)) - { - std::filesystem::remove(d); - } - - std::ofstream ofs; - ofs.open(d); - - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, m); - nlohmann::json j; - from_aron(aron, j); - - ofs << j.dump(2); - ofs.close(); - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.h b/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.h deleted file mode 100644 index 89b96c011fcccb6bfc5471850185b0cea49f77b0..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntityInstance.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include <filesystem> - -#include "../base/EntityInstanceBase.h" -#include "../workingmemory/EntityInstance.h" - -#include "../workingmemory/entityInstance_conversions.h" -#include "../workingmemory/json_conversions.h" - - -namespace armarx::armem::d_ltm -{ - /** - * @brief Data of a single entity instance. - */ - class EntityInstance : - public base::EntityInstanceBase<EntityInstance> - { - using Base = base::EntityInstanceBase<EntityInstance>; - - public: - - using Base::EntityInstanceBase; - - EntityInstance(const EntityInstance& other); - EntityInstance(EntityInstance&& other) = default; - EntityInstance& operator=(const EntityInstance& other) = default; - EntityInstance& operator=(EntityInstance&& other) = default; - - - /** - * @brief Fill `*this` with the update's values. - * @param update The update. - * @param index The instances index. - */ - virtual void update(const EntityUpdate& update, int index) override; - - virtual bool equalsDeep(const EntityInstance& other) const override; - - virtual EntityInstance copy() const override; - - // Conversion - wm::EntityInstance convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void setTo(const wm::EntityInstance&); - - protected: - virtual void _copySelf(EntityInstance& other) const override; - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - - private: - static const constexpr char* DATA_FILENAME = "data"; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.cpp deleted file mode 100644 index ab5b56d2274f78c350818693c269af18f0a4a979..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "EntitySnapshot.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::d_ltm -{ - - std::filesystem::path EntitySnapshot::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path EntitySnapshot::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName - / id().entityName - / std::to_string(id().timestamp.toMicroSeconds()); - } - - wm::EntitySnapshot EntitySnapshot::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - wm::EntitySnapshot m(id()); - for (const auto& s : _container) - { - m.addInstance(s.convert(expectedStructure)); - } - return m; - } - - void EntitySnapshot::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (!std::filesystem::is_directory(p)) - { - ARMARX_ERROR << "The entered path is not leading to a directory! Every EntitySnapshot must at least contain one EntityInstance."; - } - else - { - _container.clear(); - path = p_ptr; - - // todo - for (int i = 0; i < 1000; ++i) - { - std::filesystem::path d = p / std::to_string(i); - if (std::filesystem::is_directory(d)) - { - auto& wms = _container.emplace_back(id().withInstanceIndex(i)); - wms.reload(p_ptr); - } - else - { - break; - } - } - } - } - - void EntitySnapshot::setTo(const wm::EntitySnapshot& m) - { - try - { - std::filesystem::create_directories(_fullPath()); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - // We remove the content here and reset it with new values - _container.clear(); - - int i = 0; - for (const auto& s : m.instances()) - { - try - { - std::filesystem::create_directory(_fullPath() / std::to_string(i)); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - continue;; - } - - auto& wms = _container.emplace_back(id().withInstanceIndex(i++)); - wms.path = path; - wms.setTo(s); - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.h b/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.h deleted file mode 100644 index 545a8c0d64641c65e7faf7aa26dea77ee9865e0c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/EntitySnapshot.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include <filesystem> - -#include "../base/EntitySnapshotBase.h" -#include "../workingmemory/EntitySnapshot.h" - -#include "EntityInstance.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of an entity at one point in time. - */ - class EntitySnapshot : - public base::EntitySnapshotBase<EntityInstance, EntitySnapshot> - { - using Base = base::EntitySnapshotBase<EntityInstance, EntitySnapshot>; - - public: - - using Base::EntitySnapshotBase; - using Base::operator=; - - - // Conversion - void reload(const std::shared_ptr<std::filesystem::path>&); - wm::EntitySnapshot convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // FS connection - void setTo(const wm::EntitySnapshot&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/Memory.cpp deleted file mode 100644 index 0fb6fba92b2ebc116787bffba2d7ed5bc743227b..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "Memory.h" - -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::d_ltm -{ - std::filesystem::path Memory::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path Memory::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName; - } - - wm::Memory Memory::convert() const - { - wm::Memory m(id()); - for (const auto& [k, s] : _container) - { - m.addCoreSegment(s.convert()); - } - return m; - } - - void Memory::reload(const std::filesystem::path& p) - { - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = std::make_shared<std::filesystem::path>(p.parent_path()); - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Returning empty memory."; - } - else - { - id() = MemoryID().withMemoryName(p.filename()); - - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - auto wms = _container.emplace(k, id().withCoreSegmentName(k)); - wms.first->second.reload(path); - } - } - } - } - - void Memory::append(const wm::Memory& m) - { - std::filesystem::create_directories(_fullPath()); - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - it->second.append(s); - } - else - { - try - { - std::filesystem::create_directory(_fullPath() / k); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - auto wms = _container.emplace(k, id().withCoreSegmentName(k)); - wms.first->second.path = path; - wms.first->second.append(s); - } - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.h b/source/RobotAPI/libraries/armem/core/diskmemory/Memory.h deleted file mode 100644 index 7f7872f7a638cc31122f09b00a95755ede4d9606..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/Memory.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include <filesystem> - -#include "../base/MemoryBase.h" -#include "../workingmemory/Memory.h" - -#include "CoreSegment.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class Memory : - public base::MemoryBase<CoreSegment, Memory> - { - using Base = base::MemoryBase<CoreSegment, Memory>; - - public: - - using Base::MemoryBase; - using Base::operator=; - - - // Conversion - wm::Memory convert() const; - - // Filesystem connection - void reload(const std::filesystem::path&); - void append(const wm::Memory&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.cpp deleted file mode 100644 index 620e819f6b1fc05d4aeadc285edb087f5ea0e119..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "ProviderSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "TypeIO.h" - - -namespace armarx::armem::d_ltm -{ - std::filesystem::path ProviderSegment::_fullPath() const - { - if (path) - { - return _fullPath(*path); - } - return std::filesystem::path(); - } - - std::filesystem::path ProviderSegment::_fullPath(const std::filesystem::path& path) const - { - return path / id().memoryName / id().coreSegmentName / id().providerSegmentName; - } - - wm::ProviderSegment ProviderSegment::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - wm::ProviderSegment m(id()); - for (const auto& [_, s] : _container) - { - if (hasAronType()) - { - m.addEntity(s.convert(_aronType)); - } - else - { - m.addEntity(s.convert(expectedStructure)); - } - } - return m; - } - - void ProviderSegment::reload(const std::shared_ptr<std::filesystem::path>& p_ptr) - { - if (!p_ptr) - { - ARMARX_WARNING << "The entered is NULL."; - } - std::filesystem::path p = _fullPath(*p_ptr); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_ERROR << "The entered path is leading to a file! Abort due to error."; - } - - _container.clear(); - path = p_ptr; - - if (!std::filesystem::exists(p)) - { - ARMARX_INFO << "The entered path does not exist. Assuming an empty container."; - } - else - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - auto wms = _container.emplace(k, id().withEntityName(k)); - wms.first->second.reload(p_ptr); - } - - if (d.is_regular_file()) - { - if (auto type = TypeIO::readAronType(d.path())) - { - _aronType = type; - } - } - } - } - } - - void ProviderSegment::append(const wm::ProviderSegment& m) - { - - try - { - std::filesystem::create_directories(_fullPath()); - - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - TypeIO::writeAronType(_aronType, _fullPath()); - - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - it->second.append(s); - } - else - { - - try - { - std::filesystem::create_directory(_fullPath() / k); - } - catch (...) - { - ARMARX_WARNING << GetHandledExceptionString(); - return; - } - - auto wms = _container.emplace(k, id().withEntityName(k)); - wms.first->second.path = path; - wms.first->second.append(s); - } - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.h b/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.h deleted file mode 100644 index 8ad96ea0630af0d5aba8b7270fe229c235c2a010..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/ProviderSegment.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include <filesystem> - -#include "../base/ProviderSegmentBase.h" -#include "../workingmemory/ProviderSegment.h" - -#include "Entity.h" - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief Data of a provider segment containing multiple entities. - */ - class ProviderSegment : - public base::ProviderSegmentBase<Entity, ProviderSegment> - { - using Base = base::ProviderSegmentBase<Entity, ProviderSegment>; - - public: - - using Base::ProviderSegmentBase; - using Base::operator=; - - - // Conversion - wm::ProviderSegment convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const; - - // Filesystem connection - void reload(const std::shared_ptr<std::filesystem::path>&); - void append(const wm::ProviderSegment&); - - private: - std::filesystem::path _fullPath() const; - std::filesystem::path _fullPath(const std::filesystem::path&) const; - - public: - std::shared_ptr<std::filesystem::path> path; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.cpp b/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.cpp deleted file mode 100644 index dc7f7a83706c7b9f69cc05a6e81bcf810c10a249..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "TypeIO.h" - -#include <iostream> -#include <fstream> -#include <filesystem> - -#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/visitor/Visitor.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/converter/Converter.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/reader/nlohmannJSON/NlohmannJSONReader.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/writer/navigator/NavigatorWriter.h> -#include <RobotAPI/libraries/aron/core/io/typeIO/writer/nlohmannJSON/NlohmannJSONWriter.h> - - -namespace armarx::armem::d_ltm -{ - - aron::typenavigator::ObjectNavigatorPtr TypeIO::unwrapType(const aron::typenavigator::ObjectNavigatorPtr& type) - { - return aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(type->getMemberType(TYPE_WRAPPER_DATA_FIELD)); - } - - aron::typenavigator::ObjectNavigatorPtr TypeIO::wrapType(const aron::typenavigator::ObjectNavigatorPtr& type) - { - aron::typenavigator::ObjectNavigatorPtr typeWrapped(new aron::typenavigator::ObjectNavigator()); - typeWrapped->setObjectName(type->getObjectName() + "__ltm_type_export"); - typeWrapped->addMemberType(TYPE_WRAPPER_DATA_FIELD, type); - - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_STORED_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_CREATED_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_SENT_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_TIME_ARRIVED_FIELD, std::make_shared<aron::typenavigator::LongNavigator>()); - typeWrapped->addMemberType(TYPE_WRAPPER_CONFIDENCE_FIELD, std::make_shared<aron::typenavigator::DoubleNavigator>()); - - return typeWrapped; - } - - aron::typenavigator::ObjectNavigatorPtr TypeIO::readAronType(const std::filesystem::__cxx11::path& filepath) - { - if (std::filesystem::is_regular_file(filepath)) - { - if (filepath.filename() == (std::string(TYPE_FILENAME) + ".json")) - { - std::ifstream ifs(filepath); - std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); - - aron::typeIO::reader::NlohmannJSONReader typeReader(file_content); - aron::typeIO::writer::NavigatorWriter navWriter; - aron::typeIO::Converter::ReadAndConvert(typeReader, navWriter); - return aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(navWriter.getResult()); - } - } - return nullptr; - } - - void TypeIO::writeAronType(const aron::typenavigator::ObjectNavigatorPtr& type, const std::filesystem::__cxx11::path& filepath) - { - if (type) - { - std::ofstream ofs(filepath); - - aron::typeIO::writer::NlohmannJSONWriter typeWriter; - aron::typeIO::Visitor::VisitAndSetup(typeWriter, type); - std::string new_file_full_content = typeWriter.getResult().dump(2); - - ofs << new_file_full_content; - } - } - - -} diff --git a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.h b/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.h deleted file mode 100644 index 4311bdcd7034a08b30b7c7f6ae9ee3eab0952fac..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/diskmemory/TypeIO.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include <filesystem> - -#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> - - -namespace armarx::armem::d_ltm -{ - - /** - * @brief An entity container with a specific (Aron) type. - */ - class TypeIO - { - public: - - static aron::typenavigator::ObjectNavigatorPtr unwrapType(const aron::typenavigator::ObjectNavigatorPtr& type); - static aron::typenavigator::ObjectNavigatorPtr wrapType(const aron::typenavigator::ObjectNavigatorPtr& type); - - static aron::typenavigator::ObjectNavigatorPtr readAronType(const std::filesystem::path& filepath); - static void writeAronType(const aron::typenavigator::ObjectNavigatorPtr& type, const std::filesystem::path& filepath); - - - private: - - static const constexpr char* TYPE_FILENAME = "type"; - static constexpr const char* TYPE_WRAPPER_DATA_FIELD = "__ARON_DATA"; - static constexpr const char* TYPE_WRAPPER_TIME_STORED_FIELD = "__WRITER_METADATA__TIME_STORED"; - static constexpr const char* TYPE_WRAPPER_TIME_CREATED_FIELD = "__ENTITY_METADATA__TIME_CREATED"; - static constexpr const char* TYPE_WRAPPER_TIME_SENT_FIELD = "__ENTITY_METADATA__TIME_SENT"; - static constexpr const char* TYPE_WRAPPER_TIME_ARRIVED_FIELD = "__ENTITY_METADATA__TIME_ARRIVED"; - static constexpr const char* TYPE_WRAPPER_CONFIDENCE_FIELD = "__ENTITY_METADATA__CONFIDENCE"; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp index 8936fab6fe953bc3b7fee870a97ffee598234ea7..003ef9fb5496b92814ad68cd9904987498460c46 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp @@ -59,7 +59,7 @@ namespace armarx::armem::error { std::stringstream ss; ss << simox::alg::capitalize_words(existingTerm) << " with name '" << existingName << "' " - << " already exists in " << ownTerm << " '" << ownName << "'."; + << "already exists in " << ownTerm << " '" << ownName << "'."; return ss.str(); } diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.h b/source/RobotAPI/libraries/armem/core/error/ArMemError.h index e6843d13be5bc07e71d90b00cf6a911ca717e524..219d489fad038da1098d724bddd9e2bc74b0a5d7 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.h +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.h @@ -84,8 +84,8 @@ namespace armarx::armem::error template <class MissingT, class ContainerT> static MissingEntry create(const std::string& missingKey, const ContainerT& container) { - return MissingEntry(MissingT().getLevelName(), missingKey, - container.getLevelName(), container.getKeyString(), container.size()); + return MissingEntry(MissingT::getLevelName(), missingKey, + ContainerT::getLevelName(), container.getKeyString(), container.size()); } diff --git a/source/RobotAPI/libraries/armem/core/forward_declarations.h b/source/RobotAPI/libraries/armem/core/forward_declarations.h new file mode 100644 index 0000000000000000000000000000000000000000..04cfbfae26533af158ad52c6c39fd49c837f1afb --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/forward_declarations.h @@ -0,0 +1,64 @@ +#pragma once + + +namespace IceUtil +{ + class Time; +} + +namespace armarx::armem +{ + using Time = IceUtil::Time; + using Duration = IceUtil::Time; + + class MemoryID; + class Commit; + class EntityUpdate; + class CommitResult; + class EntityUpdateResult; +} + +namespace armarx::armem::arondto +{ + class MemoryID; +} + +namespace armarx::armem::base +{ + struct NoData; + struct EntityInstanceMetadata; + + template <class _DataT, class _MetadataT> + class EntityInstanceBase; + template <class _EntityInstanceT, class _Derived> + class EntitySnapshotBase; + template <class _EntitySnapshotT, class _Derived> + class EntityBase; + template <class _EntityT, class _Derived> + class ProviderSegmentBase; + template <class _ProviderSegmentT, class _Derived> + class CoreSegmentBase; + template <class _CoreSegmentT, class _Derived> + class MemoryBase; +} + +namespace armarx::armem::wm +{ + class EntityInstance; + class EntitySnapshot; + class Entity; + class ProviderSegment; + class CoreSegment; + class Memory; +} + +namespace armarx::armem::server::wm +{ + using EntityInstance = armem::wm::EntityInstance; + using EntitySnapshot = armem::wm::EntitySnapshot; + class Entity; + class ProviderSegment; + class CoreSegment; + class Memory; +} + diff --git a/source/RobotAPI/libraries/armem/core/ice_conversions.cpp b/source/RobotAPI/libraries/armem/core/ice_conversions.cpp index d79acf1da2b86f94414205131173797e0354bc24..cca45a3b2cf04f26ec76d158e9cf1e711bbfecb2 100644 --- a/source/RobotAPI/libraries/armem/core/ice_conversions.cpp +++ b/source/RobotAPI/libraries/armem/core/ice_conversions.cpp @@ -1,20 +1,20 @@ #include "ice_conversions.h" +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> -namespace armarx + +void IceUtil::toIce(long& ice, const IceUtil::Time& time) { + ice = time.toMicroSeconds(); +} - /* - void armem::fromIce(long ice, Time& time) - { - time = Time::microSeconds(ice); - } +void IceUtil::fromIce(const long& ice, IceUtil::Time& time) +{ + time = Time::microSeconds(ice); +} - void armem::toIce(long& ice, const Time& time) - { - ice = time.toMicroSeconds(); - } - */ +namespace armarx +{ void armem::toIce(data::MemoryID& ice, const MemoryID& id) { @@ -144,14 +144,5 @@ namespace armarx update.timeArrived = timeArrived; } - - void armem::detail::toIceItem(data::detail::MemoryItem& ice, const armem::base::detail::MemoryItem& item) - { - toIce(ice.id, item.id()); - } - - void armem::detail::fromIceItem(const data::detail::MemoryItem& ice, armem::base::detail::MemoryItem& item) - { - fromIce(ice.id, item.id()); - } } + diff --git a/source/RobotAPI/libraries/armem/core/ice_conversions.h b/source/RobotAPI/libraries/armem/core/ice_conversions.h index 874684cac2a6a52b8742c36dcc2bdea8346e1586..e8f104fe04ebcb721b0ffbf4b70b18ada0af234c 100644 --- a/source/RobotAPI/libraries/armem/core/ice_conversions.h +++ b/source/RobotAPI/libraries/armem/core/ice_conversions.h @@ -6,29 +6,21 @@ #include "ice_conversions_templates.h" #include "Commit.h" -#include "base/detail/MemoryItem.h" +#include "MemoryID.h" +#include "Time.h" namespace IceUtil { // Define in original namespace to allow ADL. - inline void toIce(long& ice, const Time& time) - { - ice = time.toMicroSeconds(); - } - inline void fromIce(const long& ice, Time& time) - { - time = Time::microSeconds(ice); - } + void toIce(long& ice, const Time& time); + void fromIce(const long& ice, Time& time); } namespace armarx::armem { - // void fromIce(long ice, Time& time); - // void toIce(long& ice, const Time& time); - void fromIce(const data::MemoryID& ice, MemoryID& id); void toIce(data::MemoryID& ice, const MemoryID& id); @@ -51,12 +43,5 @@ namespace armarx::armem void fromIce(const data::Commit& ice, Commit& commit, Time timeArrived); void fromIce(const data::EntityUpdate& ice, EntityUpdate& update, Time timeArrived); - - namespace detail - { - void toIceItem(data::detail::MemoryItem& ice, const armem::base::detail::MemoryItem& item); - void fromIceItem(const data::detail::MemoryItem& ice, armem::base::detail::MemoryItem& item); - } - } diff --git a/source/RobotAPI/libraries/armem/core/ice_conversions_templates.h b/source/RobotAPI/libraries/armem/core/ice_conversions_templates.h index e0c71faac37cd899d85d174c33196499a29df770..8060e22a0782e23c6bb989fe900fcce99c7ff7c6 100644 --- a/source/RobotAPI/libraries/armem/core/ice_conversions_templates.h +++ b/source/RobotAPI/libraries/armem/core/ice_conversions_templates.h @@ -1,9 +1,12 @@ #pragma once -#include <memory> - #include "Commit.h" +#include <Ice/Handle.h> + +#include <map> +#include <memory> +#include <vector> namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.cpp deleted file mode 100644 index d47dc46cb63c5266edc8b9e8457c1fb4b75a4527..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "CoreSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::ltm -{ - - wm::CoreSegment CoreSegment::convert() const - { - wm::CoreSegment m(id()); - for (const auto& [_, s] : _container) - { - m.addProviderSegment(s.convert()); - } - return m; - } - - void CoreSegment::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - - mongocxx::cursor cursor = coll.find({}); - for (auto doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - ARMARX_INFO << "CoreSegment: Found foreign key: " << json.at("foreign_key"); - - MemoryID i((std::string) json.at("foreign_key")); - if (i.coreSegmentName != id().coreSegmentName) - { - throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong coreSegment name. Expected " + id().coreSegmentName); - } - - std::string k = i.providerSegmentName; - - if (const auto& it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (core) container a key k = " + k + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(k, id().withProviderSegmentName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - } - - ARMARX_INFO << "After reload has core segment " << id().str() << " size: " << _container.size(); - } - - void CoreSegment::append(const wm::CoreSegment& m) - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - it->second.append(s); - } - else - { - auto builder = bsoncxx::builder::stream::document{}; - bsoncxx::document::value foreign_key = builder - << "foreign_key" << s.id().withProviderSegmentName(k).str() - << bsoncxx::builder::stream::finalize; - coll.insert_one(foreign_key.view()); - - auto wms = _container.emplace(k, id().withProviderSegmentName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.append(s); - } - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.h b/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.h deleted file mode 100644 index 3a7225058a265949de50e98a63d1d3c6ce5bf371..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/CoreSegment.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "../base/CoreSegmentBase.h" - -#include "ProviderSegment.h" - -#include "../workingmemory/CoreSegment.h" - - -namespace armarx::armem::ltm -{ - - /** - * @brief Data of a core segment containing multiple provider segments. - */ - class CoreSegment : - public base::CoreSegmentBase<ProviderSegment, CoreSegment> - { - using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; - - public: - - using Base::CoreSegmentBase; - using Base::operator=; - - - // Conversion - wm::CoreSegment convert() const; - - // MongoDB connection - void reload(); - void append(const wm::CoreSegment&); - - protected: - virtual void _copySelf(CoreSegment& other) const override - { - Base::_copySelf(other); - other.dbsettings = dbsettings; - } - - virtual void _copySelfEmpty(CoreSegment& other) const override - { - Base::_copySelfEmpty(other); - other.dbsettings = dbsettings; - } - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.cpp deleted file mode 100644 index bccb79c9d1d162a6c67a4f80c57b58b04425863e..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "Entity.h" - - -namespace armarx::armem::ltm -{ - - wm::Entity Entity::convert() const - { - wm::Entity m(id()); - for (const auto& [_, s] : _container) - { - m.addSnapshot(s.convert()); - } - return m; - } - - void Entity::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - mongocxx::cursor cursor = coll.find({}); - int i = 0; - for (auto doc : cursor) - { - if (i > MAX_HISTORY_SIZE) - { - // TODO: Add logic for most queried data? - break; - } - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - - auto k = armem::Time::microSeconds(json.at("timestamp")); - //ARMARX_INFO << "Entity: Found timestamp: " << std::to_string(k.toMicroSeconds()); - - if (const auto& it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (entity) container a key k = " + std::to_string(k.toMicroSeconds()) + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(std::make_pair(k, id().withTimestamp(k))); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - ++i; - } - - ARMARX_INFO << "After reload has entity " << id().str() << " size: " << _container.size(); - } - - void Entity::append(const wm::Entity& m) - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - // timestamp already exists - // We assume that a snapshot does not change, so ignore - } - else - { - auto wms = _container.emplace(std::make_pair(k, id().withTimestamp(k))); - wms.first->second.dbsettings = dbsettings; - wms.first->second.setTo(s, k); - //truncateHistoryToSize(); - } - } - } - - bool Entity::hasSnapshot(const Time& time) const - { - // check cache - if (Base::hasSnapshot(time)) - { - return true; - } - // check mongodb - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - bsoncxx::stdx::optional<bsoncxx::document::value> maybe_result = coll.find_one(document{} << "timestamp" << time.toMicroSeconds() << finalize); - if (!maybe_result) - { - return false; - } - - auto json = nlohmann::json::parse(bsoncxx::to_json(*maybe_result)); - auto id = MemoryID::fromString(json["id"].get<std::string>()); - auto instances = json["instances"]; - EntitySnapshot snapshot(id); - snapshot.dbsettings = dbsettings; - - for (unsigned int i = 0; i < instances.size(); ++i) - { - EntityInstance instance(id.withInstanceIndex(i)); - snapshot.addInstance(instance); - } - - _container.emplace(time, snapshot); - //truncateHistoryToSize(); - return true; - } - - std::vector<Time> Entity::getTimestamps() const - { - // get from cache - std::vector<Time> ret; // = Base::getTimestamps(); - - // get missing from mongodb - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - auto cursor = coll.find({}); - for (auto doc : cursor) - { - auto json = nlohmann::json::parse(bsoncxx::to_json(doc)); - auto ts = json["timestamp"].get<long>(); - ret.push_back(Time::microSeconds(ts)); - } - return ret; - } - - const EntitySnapshot& Entity::getSnapshot(const Time& time) const - { - if (!hasSnapshot(time)) - { - throw error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); - } - - // the above command already puts the reference to the cache - return Base::getSnapshot(time); - } - - auto Entity::getLatestItem() const -> const ContainerT::value_type& - { - // Directly query mongodb (cache cant know whether it is the latest or not) - // TODO - return Base::getLatestItem(); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.h b/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.h deleted file mode 100644 index 20377befd61f741a642e139aafe1de3c2cc7f24b..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Entity.h +++ /dev/null @@ -1,83 +0,0 @@ -#pragma once - -#include "../base/EntityBase.h" - -#include "EntitySnapshot.h" - -#include "../workingmemory/Entity.h" - - -namespace armarx::armem::ltm -{ - /** - * @brief An entity over a period of time. - * - * An entity should be a physical thing or abstract concept existing - * (and potentially evolving) over some time. - * - * Examples are: - * - objects (the green box) - * - agents (robot, human) - * - locations (frige, sink) - * - grasp affordances (general, or for a specific object) - * - images - * - point clouds - * - other sensory values - * - * At each point in time (`EntitySnapshot`), the entity can have a - * (potentially variable) number of instances (`EntityInstance`), - * each containing a single `AronData` object of a specific `AronType`. - */ - class Entity : - public base::EntityBase<EntitySnapshot, Entity> - { - using Base = base::EntityBase<EntitySnapshot, Entity>; - - public: - - using Base::EntityBase; - using Base::operator=; - - Entity() - { - // the history of snapshots is just a cache of frequently used elements - setMaxHistorySize(MAX_HISTORY_SIZE); - } - - // Conversion - wm::Entity convert() const; - - // MongoDB connection - void reload(); - void append(const wm::Entity&); - - // virtual overrides for LTM lookups - virtual bool hasSnapshot(const Time& time) const override; - virtual std::vector<Time> getTimestamps() const override; - virtual const EntitySnapshot& getSnapshot(const Time& time) const override; - - protected: - virtual void _copySelf(Entity& other) const override - { - Base::_copySelf(other); - other.dbsettings = dbsettings; - } - - virtual void _copySelfEmpty(Entity& other) const override - { - Base::_copySelfEmpty(other); - other.dbsettings = dbsettings; - } - - protected: - // virtual overrides for LTM storage - virtual const ContainerT::value_type& getLatestItem() const override; - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - - private: - const static constexpr int MAX_HISTORY_SIZE = 200; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.cpp deleted file mode 100644 index 5e44714904efe74f920b891b665e13497b749508..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "EntityInstance.h" - -#include "../../core/error.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -namespace armarx::armem::ltm -{ - - bool EntityInstanceMetadata::operator==(const EntityInstanceMetadata& other) const - { - return timeCreated == other.timeCreated - && timeSent == other.timeSent - && timeArrived == other.timeArrived - && std::abs(confidence - other.confidence) < 1e-6f; - } - - bool EntityInstance::equalsDeep(const EntityInstance& other) const - { - return id() == other.id() && _metadata == other.metadata(); - } - - void EntityInstance::update(const EntityUpdate& update, int index) - { - ARMARX_CHECK_FITS_SIZE(index, update.instancesData.size()); - - this->index() = index; - - this->_metadata.confidence = update.confidence; - this->_metadata.timeCreated = update.timeCreated; - this->_metadata.timeSent = update.timeSent; - this->_metadata.timeArrived = update.timeArrived; - } - - EntityInstance EntityInstance::copy() const - { - EntityInstance d; - this->_copySelf(d); - return d; - } - - void EntityInstance::_copySelf(EntityInstance& other) const - { - Base::_copySelf(other); - other._metadata = _metadata; - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.h b/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.h deleted file mode 100644 index e95ae277e83b2a927172ad1e663c42a700fef849..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntityInstance.h +++ /dev/null @@ -1,84 +0,0 @@ -#pragma once - -#include "../base/EntityInstanceBase.h" - -#include "../workingmemory/EntityInstance.h" -#include "mongodb/MongoDBConnectionManager.h" - -#include "../workingmemory/entityInstance_conversions.h" -#include "../workingmemory/json_conversions.h" - -namespace armarx::armem::ltm -{ - - /** - * @brief Metadata of an entity instance. - */ - struct EntityInstanceMetadata - { - /// Time when this value was created. - Time timeCreated; - /// Time when this value was sent to the memory. - Time timeSent; - /// Time when this value has arrived at the memory. - Time timeArrived; - - /// An optional confidence, may be used for things like decay. - float confidence = 1.0; - - - bool operator==(const EntityInstanceMetadata& other) const; - inline bool operator!=(const EntityInstanceMetadata& other) const - { - return !(*this == other); - } - }; - - /** - * @brief Data of a single entity instance. - */ - class EntityInstance : - public base::EntityInstanceBase<EntityInstance> - { - using Base = base::EntityInstanceBase<EntityInstance>; - - public: - - using Base::EntityInstanceBase; - - EntityInstance(const EntityInstance& other) = default; - EntityInstance(EntityInstance&& other) = default; - EntityInstance& operator=(const EntityInstance& other) = default; - EntityInstance& operator=(EntityInstance&& other) = default; - - - EntityInstanceMetadata& metadata() - { - return _metadata; - } - inline const EntityInstanceMetadata& metadata() const - { - return _metadata; - } - - - /** - * @brief Fill `*this` with the update's values. - * @param update The update. - * @param index The instances index. - */ - virtual void update(const EntityUpdate& update, int index) override; - - virtual bool equalsDeep(const EntityInstance& other) const override; - - virtual EntityInstance copy() const override; - - protected: - virtual void _copySelf(EntityInstance& other) const override; - - - private: - /// The metadata. - EntityInstanceMetadata _metadata; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.cpp deleted file mode 100644 index aae8a6d3258c94a27e9f931838a44c8d1b6426aa..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "EntitySnapshot.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "../workingmemory/entityInstance_conversions.h" -#include "../workingmemory/json_conversions.h" - -#include "error.h" - - -namespace armarx::armem::ltm -{ - - wm::EntitySnapshot EntitySnapshot::convert(const aron::typenavigator::NavigatorPtr& expectedStructure) const - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().getEntityID().str()]; - - auto res = coll.find_one(document{} << "id" << id().getEntitySnapshotID().str() << finalize); - - if (!res) - { - throw error::ArMemError("Could not load an instance from the memory '" + id().getEntityID().str() + "'. Tried to access: " + id().getEntitySnapshotID().str()); - } - - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); - nlohmann::json instances = json["instances"]; - - if (instances.size() != _container.size()) - { - throw error::ArMemError("The size of the mongodb entity entry at id " + id().getEntitySnapshotID().str() + " has wrong size. Expected: " + std::to_string(_container.size()) + " but got: " + std::to_string(instances.size())); - } - - wm::EntitySnapshot m(id()); - for (unsigned int i = 0; i < _container.size(); ++i) - { - nlohmann::json doc = instances[i++]; - - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, doc, expectedStructure); - wm::EntityInstance e(id()); - from_aron(aron, e); - m.addInstance(e); - } - return m; - } - - void EntitySnapshot::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().getEntityID().str()]; - - auto res = coll.find_one(document{} << "id" << id().getEntitySnapshotID().str() << finalize); - - if (!res) - { - throw error::ArMemError("Could not load an instance from the memory '" + id().getEntityID().str() + "'. Tried to access: " + id().getEntitySnapshotID().str()); - } - - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); - for (unsigned int i = 0; i < json.at("instances").size(); ++i) - { - _container.emplace_back(id().withInstanceIndex(i)); - } - } - - void EntitySnapshot::setTo(const wm::EntitySnapshot& m, const armem::Time& t) - { - // We remove the contente here and reset it with new values - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().getEntityID().str()]; - - bsoncxx::builder::stream::document builder{}; - auto in_array = builder - << "id" << id().getEntitySnapshotID().str() - << "timestamp" << t.toMicroSeconds() - << "instances"; - auto array_builder = bsoncxx::builder::basic::array{}; - - int i = 0; - for (const auto& s : m.container()) - { - auto wms = _container.emplace_back(id().withInstanceIndex(i++)); - - auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); - to_aron(aron, s); - nlohmann::json j; - from_aron(aron, j); - - 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()); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.h b/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.h deleted file mode 100644 index 02361d2e708365303fbce81f56eaa9b49fb07a61..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/EntitySnapshot.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include "../base/EntitySnapshotBase.h" - -#include "EntityInstance.h" - -#include "../workingmemory/EntitySnapshot.h" - - -namespace armarx::armem::ltm -{ - - /** - * @brief Data of an entity at one point in time. - */ - class EntitySnapshot : - public base::EntitySnapshotBase<EntityInstance, EntitySnapshot> - { - using Base = base::EntitySnapshotBase<EntityInstance, EntitySnapshot>; - - public: - - using Base::EntitySnapshotBase; - using Base::operator=; - - - // Conversion - wm::EntitySnapshot convert(const aron::typenavigator::NavigatorPtr& = nullptr) const; - - // MongoDB connection - void reload(); - void setTo(const wm::EntitySnapshot&, const armem::Time& t); - - protected: - virtual void _copySelf(EntitySnapshot& other) const override - { - Base::_copySelf(other); - other.dbsettings = dbsettings; - } - - virtual void _copySelfEmpty(EntitySnapshot& other) const override - { - Base::_copySelfEmpty(other); - other.dbsettings = dbsettings; - } - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.cpp deleted file mode 100644 index 31636867698321b0d04a52d4c8946d82d7b78eb8..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.cpp +++ /dev/null @@ -1,230 +0,0 @@ -#include "Memory.h" - -#include <ArmarXCore/core/time/TimeUtil.h> -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::ltm -{ - - Memory::Memory(const Memory& other) : - Base(other), - dbsettings(other.dbsettings), - periodicTransferSettings(other.periodicTransferSettings), - onFullTransferSettings(other.onFullTransferSettings), - mongoDBMutex() - { - // Do not copy _mutex. - } - - - Memory::Memory(Memory&& other) : - Base(other), - dbsettings(other.dbsettings), - periodicTransferSettings(other.periodicTransferSettings), - onFullTransferSettings(other.onFullTransferSettings), - reloaded(other.reloaded) - { - // Do not move _mutex. - } - - - Memory& Memory::operator=(const Memory& other) - { - Base::operator=(other); - dbsettings = other.dbsettings; - periodicTransferSettings = other.periodicTransferSettings; - onFullTransferSettings = other.onFullTransferSettings; - - // Don't copy _mutex. - return *this; - } - - - Memory& Memory::operator=(Memory&& other) - { - Base::operator=(other); - dbsettings = std::move(other.dbsettings); - periodicTransferSettings = std::move(other.periodicTransferSettings); - onFullTransferSettings = std::move(other.onFullTransferSettings); - reloaded = other.reloaded; - - // Don't move _mutex. - return *this; - } - - bool Memory::checkConnection() const - { - // Check connection: - ARMARX_INFO << "Checking connection"; - if (!MongoDBConnectionManager::ConnectionIsValid(dbsettings)) - { - ARMARX_WARNING << deactivateSpam("ConnectionIsNotValid") - << "The connection to mongocxx for memory '" << name() << "' is not valid. Settings are: " << dbsettings.toString() - << "\nTo start it, run e.g.: \n" - << "armarx memory start" - << "\n\n"; - return false; - } - return true; - } - - void Memory::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) - { - defs->optional(dbsettings.host, prefix + ".ltm.10_host"); - defs->optional(dbsettings.port, prefix + "ltm.11_port"); - defs->optional(dbsettings.user, prefix + "ltm.20_user"); - defs->optional(dbsettings.password, prefix + "ltm.21_password"); - defs->optional(dbsettings.database, prefix + "ltm.22_database"); - defs->optional(periodicTransferSettings.enabled, prefix + "ltm.30_enablePeriodicTransfer", "Enable transfer based on periodic interval."); - defs->optional(onFullTransferSettings.enabled, prefix + "ltm.31_enableOnFullTransfer", "Enable transfer whenever the wm is full (see maxHistorySize)."); - } - - wm::Memory Memory::convert() const - { - std::lock_guard l(mongoDBMutex); - if (!checkConnection()) - { - wm::Memory m(id()); - return m; - } - - ARMARX_INFO << "Converting Memory with connection to: " << dbsettings.toString(); - - TIMING_START(LTM_Convert); - - wm::Memory m(id()); - for (const auto& [_, s] : _container) - { - m.addCoreSegment(s.convert()); - } - - TIMING_END(LTM_Convert); - return m; - } - - void Memory::reload() - { - std::lock_guard l(mongoDBMutex); - reloaded = false; - - if (!checkConnection()) - { - return; - } - - ARMARX_INFO << "(Re)Establishing connection to: " << dbsettings.toString(); - - TIMING_START(LTM_Reload); - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - if (!client) - { - ARMARX_ERROR << "A client has died. Could not reload."; - return; - } - - auto databases = client.list_databases(); - for (const auto& doc : databases) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - ARMARX_INFO << "Found the database: " << json.at("name"); - } - - mongocxx::database db = client[dbsettings.database]; - ARMARX_INFO << "Getting collection for id: " << id().str(); - mongocxx::collection coll = db[id().str()]; - - ARMARX_IMPORTANT << "Memory Container size is: " << _container.size(); - - mongocxx::cursor cursor = coll.find({}); - for (const auto& doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - ARMARX_INFO << "Memory: Found foreign key: " << json.at("foreign_key"); - - MemoryID i((std::string) json.at("foreign_key")); - if (i.memoryName != id().memoryName) - { - throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name. Expected " + id().memoryName); - } - - std::string k = i.coreSegmentName; - - if (const auto& it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (memory) container a key k = " + k + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(k, id().withCoreSegmentName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - } - - reloaded = true; - for (const auto& m : toAppendQueue) - { - _append(m); - } - - TIMING_END(LTM_Reload); - ARMARX_INFO << "After reload memory " << id().str() << " size: " << _container.size() << ". Setting reloaded: " << reloaded; - } - - void Memory::_append(const wm::Memory& m) - { - if (!checkConnection() || !reloaded) - { - // We ignore if not fully loaded yet - return; - } - - //ARMARX_INFO << "Merge memory with name '" << m.name() << "' into the LTM with name '" << name() << "'"; - - TIMING_START(LTM_Append); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - // TODO check if foreign key exists - it->second.append(s); - } - else - { - auto builder = bsoncxx::builder::stream::document{}; - bsoncxx::document::value foreign_key = builder - << "foreign_key" << s.id().withCoreSegmentName(k).str() - << bsoncxx::builder::stream::finalize; - coll.insert_one(foreign_key.view()); - - auto wms = _container.emplace(k, id().withCoreSegmentName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.append(s); - } - } - - TIMING_END(LTM_Append); - } - - void Memory::append(const wm::Memory& m) - { - std::lock_guard l(mongoDBMutex); - if (!checkConnection() || !reloaded) - { - toAppendQueue.push_back(m); - return; - } - _append(m); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.h b/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.h deleted file mode 100644 index 4902701b33374c75affc2499d9ec93d3a18a9912..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/Memory.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -// BaseClass -#include "../base/MemoryBase.h" - -// CoreSegment -#include "CoreSegment.h" - -// WM -#include "../workingmemory/Memory.h" - -// Properties -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> - -namespace armarx::armem::ltm -{ - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class Memory : - public base::MemoryBase<CoreSegment, Memory> - { - using Base = base::MemoryBase<CoreSegment, Memory>; - - public: - - using Base::MemoryBase; - using Base::operator=; - - struct TransferSettings - { - bool enabled = false; - }; - - struct PeriodicTransferSettings : public TransferSettings - { - bool deleteFromWMOnTransfer = false; - int frequencyHz = 1; - }; - - struct OnFullTransferSettings : public TransferSettings - { - enum class Mode - { - TRANSFER_LATEST, - TRANSFER_LEAST_USED - }; - - Mode mode; - int batch_size = 20; - }; - - - Memory(const Memory& other); - Memory(Memory&& other); - Memory& operator=(const Memory& other); - Memory& operator=(Memory&& other); - - // PropertyDefinitions related to LTM - void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = ""); - - // Conversion - wm::Memory convert() const; - - // MongoDB connection - void reload(); - void append(const wm::Memory&); - - protected: - virtual void _copySelf(Memory& other) const override - { - Base::_copySelf(other); - other.dbsettings = dbsettings; - } - - virtual void _copySelfEmpty(Memory& other) const override - { - Base::_copySelfEmpty(other); - other.dbsettings = dbsettings; - } - - private: - bool checkConnection() const; - - void _append(const wm::Memory&); - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - - PeriodicTransferSettings periodicTransferSettings; - OnFullTransferSettings onFullTransferSettings; - - private: - bool reloaded = false; - mutable std::mutex mongoDBMutex; - - std::vector<wm::Memory> toAppendQueue; - }; -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.cpp deleted file mode 100644 index 5fcf4a1351e34f257e773c2ac32e9d4eb64c5401..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "ProviderSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::ltm -{ - - wm::ProviderSegment ProviderSegment::convert() const - { - wm::ProviderSegment m(id()); - for (const auto& [_, s] : _container) - { - m.addEntity(s.convert()); - } - return m; - } - - void ProviderSegment::reload() - { - _container.clear(); - - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - mongocxx::cursor cursor = coll.find({}); - for (auto doc : cursor) - { - nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(doc)); - - MemoryID i((std::string) json.at("foreign_key")); - if (i.providerSegmentName != id().providerSegmentName) - { - throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong providerSegment name. Expected " + id().providerSegmentName); - } - - std::string k = i.entityName; - - if (const auto& it = _container.find(k); it != _container.end()) - { - throw error::ArMemError("Somehow after clearing the (provvider) container a key k = " + k + " was found. Do you have double entries in mongodb?"); - } - else - { - auto wms = _container.emplace(k, id().withEntityName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.reload(); - } - } - - ARMARX_INFO << "After reload has provider segment " << id().str() << " size: " << _container.size(); - } - - void ProviderSegment::append(const wm::ProviderSegment& m) - { - mongocxx::client& client = MongoDBConnectionManager::EstablishConnection(dbsettings); - mongocxx::database db = client[dbsettings.database]; - mongocxx::collection coll = db[id().str()]; - - for (const auto& [k, s] : m.container()) - { - if (const auto& it = _container.find(k); it != _container.end()) - { - it->second.append(s); - } - else - { - auto builder = bsoncxx::builder::stream::document{}; - bsoncxx::document::value foreign_key = builder - << "foreign_key" << s.id().withEntityName(k).str() - << bsoncxx::builder::stream::finalize; - coll.insert_one(foreign_key.view()); - - auto wms = _container.emplace(k, id().withEntityName(k)); - wms.first->second.dbsettings = dbsettings; - wms.first->second.append(s); - } - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.h b/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.h deleted file mode 100644 index bb49f232e9fe231e1cd500e3f9739ddaa0d7d83f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/ProviderSegment.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include "../base/ProviderSegmentBase.h" - -#include "Entity.h" - -#include "../workingmemory/ProviderSegment.h" - - -namespace armarx::armem::ltm -{ - - /** - * @brief Data of a provider segment containing multiple entities. - */ - class ProviderSegment : - public base::ProviderSegmentBase<Entity, ProviderSegment> - { - using Base = base::ProviderSegmentBase<Entity, ProviderSegment>; - - public: - - using Base::ProviderSegmentBase; - using Base::operator=; - - - // Conversion - wm::ProviderSegment convert() const; - - // MongoDB connection - void reload(); - void append(const wm::ProviderSegment&); - - protected: - virtual void _copySelf(ProviderSegment& other) const override - { - Base::_copySelf(other); - other.dbsettings = dbsettings; - } - - virtual void _copySelfEmpty(ProviderSegment& other) const override - { - Base::_copySelfEmpty(other); - other.dbsettings = dbsettings; - } - - public: - MongoDBConnectionManager::MongoDBSettings dbsettings; - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.cpp b/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.cpp deleted file mode 100644 index 6ad44836900e04765790a9a3c2a19881e1288656..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "MongoDBConnectionManager.h" - -namespace armarx::armem::ltm -{ - bool MongoDBConnectionManager::initialized = false; - std::map<std::string, mongocxx::client> MongoDBConnectionManager::Connections = {}; - - - void MongoDBConnectionManager::initialize_if() - { - if (!initialized) - { - initialized = true; - mongocxx::instance instance{}; // This should be done only once. - } - } - - mongocxx::client& MongoDBConnectionManager::EstablishConnection(const MongoDBSettings& settings) - { - initialize_if(); - - const auto uri_str = settings.uri(); - const auto& it = Connections.find(uri_str); - if (it == Connections.end()) - { - mongocxx::uri uri(uri_str); - auto con = Connections.emplace(uri_str, mongocxx::client(uri)); - return con.first->second; - } - else - { - // A connection already exists. We do not need to open another one. - return it->second; - } - } - - bool MongoDBConnectionManager::ConnectionIsValid(const MongoDBSettings& settings, bool force) - { - initialize_if(); - - try - { - if (!force) - { - const auto uri_str = settings.uri(); - const auto& it = Connections.find(uri_str); - if (it != Connections.end()) - { - auto admin = it->second["admin"]; - auto result = admin.run_command(bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("isMaster", 1))); - return true; - } - } - - const auto uri_str = settings.uri(); - mongocxx::uri uri(uri_str); - auto client = mongocxx::client(uri); - auto admin = client["admin"]; - auto result = admin.run_command(bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("isMaster", 1))); - return true; - } - catch (const std::exception& xcp) - { - return false; - } - } - - bool MongoDBConnectionManager::ConnectionExists(const MongoDBSettings& settings) - { - initialize_if(); - - const auto uri_str = settings.uri(); - const auto& it = Connections.find(uri_str); - return it != Connections.end(); - } -} diff --git a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.h b/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.h deleted file mode 100644 index 0cd3cb5ba8f6c575d6d3dd9357f16884f508914c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/longtermmemory/mongodb/MongoDBConnectionManager.h +++ /dev/null @@ -1,75 +0,0 @@ -#pragma once - -#include <string> -#include <vector> -#include <map> -#include <memory> -#include <iostream> - -#include <bsoncxx/json.hpp> -#include <mongocxx/client.hpp> -#include <mongocxx/stdx.hpp> -#include <mongocxx/uri.hpp> -#include <mongocxx/instance.hpp> -#include <bsoncxx/builder/stream/helpers.hpp> -#include <bsoncxx/builder/stream/document.hpp> -#include <bsoncxx/builder/stream/array.hpp> - - -namespace armarx::armem::ltm -{ - - using bsoncxx::builder::stream::close_array; - using bsoncxx::builder::stream::close_document; - using bsoncxx::builder::stream::document; - using bsoncxx::builder::stream::finalize; - using bsoncxx::builder::stream::open_array; - using bsoncxx::builder::stream::open_document; - - - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class MongoDBConnectionManager - { - public: - struct MongoDBSettings - { - std::string host = "localhost"; - unsigned int port = 25270; - std::string user = ""; - std::string password = ""; - std::string database = "Test"; - - - bool isSet() const - { - // we always need a user and a host - return !host.empty() and port != 0 and !user.empty(); - } - - std::string uri() const - { - return "mongodb://" + host + ":" + std::to_string(port) + user; - } - - std::string toString() const - { - return uri() + ":" + password + "/" + database; - } - }; - - static mongocxx::client& EstablishConnection(const MongoDBSettings& settings); - static bool ConnectionIsValid(const MongoDBSettings& settings, bool force = false); - static bool ConnectionExists(const MongoDBSettings& settings); - - private: - static void initialize_if(); - - - private: - static bool initialized; - static std::map<std::string, mongocxx::client> Connections; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/operations.cpp b/source/RobotAPI/libraries/armem/core/operations.cpp new file mode 100644 index 0000000000000000000000000000000000000000..533bc623f3844a06ab7b87d858c00942a637fcbc --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/operations.cpp @@ -0,0 +1,101 @@ +#include "operations.h" + +#include <RobotAPI/libraries/armem/core/Time.h> + +#include <SimoxUtility/algorithm/string/string_tools.h> + + +namespace armarx +{ + + std::vector<aron::datanavigator::DictNavigatorPtr> + armem::getAronData(const wm::EntitySnapshot& snapshot) + { + std::vector<aron::datanavigator::DictNavigatorPtr> result; + snapshot.forEachChild([&result](const wm::EntityInstance & instance) + { + result.push_back(instance.data()); + return true; + }); + return result; + } + + + armem::EntityUpdate armem::toEntityUpdate(const wm::EntitySnapshot& snapshot) + { + EntityUpdate up; + up.entityID = snapshot.id().getEntityID(); + up.timeCreated = snapshot.time(); + up.instancesData = getAronData(snapshot); + return up; + } + + + template <class DataT> + static std::string makeLine(int depth, const DataT& data, std::optional<size_t> size = std::nullopt) + { + std::stringstream ss; + while (depth > 1) + { + ss << "| "; + depth--; + } + if (depth > 0) + { + ss << "+- "; + } + ss << simox::alg::capitalize_words(DataT::getLevelName()); + ss << " '" << simox::alg::capitalize_words(data.getKeyString()) << "'"; + if (size.has_value()) + { + ss << " (size " << size.value() << ")"; + } + ss << "\n"; + return ss.str(); + } + + + template <class DataT> + static std::string _print(const DataT& data, int maxDepth, int depth) + { + std::stringstream ss; + ss << makeLine(depth, data, data.size()); + if (maxDepth < 0 || maxDepth > 0) + { + data.forEachChild([&ss, maxDepth, depth](const auto& instance) + { + ss << armem::print(instance, maxDepth - 1, depth + 1); + }); + } + return ss.str(); + } + + std::string armem::print(const wm::Memory& data, int maxDepth, int depth) + { + return _print(data, maxDepth, depth); + } + std::string armem::print(const wm::CoreSegment& data, int maxDepth, int depth) + { + return _print(data, maxDepth, depth); + } + std::string armem::print(const wm::ProviderSegment& data, int maxDepth, int depth) + { + return _print(data, maxDepth, depth); + } + std::string armem::print(const wm::Entity& data, int maxDepth, int depth) + { + return _print(data, maxDepth, depth); + } + std::string armem::print(const wm::EntitySnapshot& data, int maxDepth, int depth) + { + return _print(data, maxDepth, depth); + } + + std::string armem::print(const wm::EntityInstance& data, int maxDepth, int depth) + { + (void) maxDepth; + std::stringstream ss; + ss << makeLine(depth, data); + return ss.str(); + } +} diff --git a/source/RobotAPI/libraries/armem/core/operations.h b/source/RobotAPI/libraries/armem/core/operations.h new file mode 100644 index 0000000000000000000000000000000000000000..775b6dd07216a13dcffafc73f8651f6722b56b09 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/operations.h @@ -0,0 +1,61 @@ +#pragma once + +#include "wm/memory_definitions.h" + +#include <string> + + +/* + * Functions performing some "advanced" operations on the memory + * data structure, which are using their public API but should not + * be part of it. + */ + +namespace armarx::armem +{ + + std::vector<aron::datanavigator::DictNavigatorPtr> + getAronData(const wm::EntitySnapshot& snapshot); + + + EntityUpdate + toEntityUpdate(const wm::EntitySnapshot& snapshot); + + + template <class ContainerT> + Commit + toCommit(const ContainerT& container) + { + Commit commit; + container.forEachSnapshot([&commit](const wm::EntitySnapshot & snapshot) + { + commit.add(toEntityUpdate(snapshot)); + return true; + }); + return commit; + } + + + template <class ContainerT> + const typename ContainerT::EntityInstanceT* + findFirstInstance(const ContainerT& container) + { + const typename ContainerT::EntityInstanceT* instance = nullptr; + container.forEachInstance([&instance](const wm::EntityInstance & i) + { + instance = &i; + return false; + }); + return instance; + } + + + std::string print(const wm::Memory& data, int maxDepth = -1, int depth = 0); + std::string print(const wm::CoreSegment& data, int maxDepth = -1, int depth = 0); + std::string print(const wm::ProviderSegment& data, int maxDepth = -1, int depth = 0); + std::string print(const wm::Entity& data, int maxDepth = -1, int depth = 0); + std::string print(const wm::EntitySnapshot& data, int maxDepth = -1, int depth = 0); + std::string print(const wm::EntityInstance& data, int maxDepth = -1, int depth = 0); + +} + diff --git a/source/RobotAPI/libraries/armem/core/wm.h b/source/RobotAPI/libraries/armem/core/wm.h new file mode 100644 index 0000000000000000000000000000000000000000..83776ed701261bde89ed1fe2fd1e4ff479f06f44 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm.h @@ -0,0 +1,10 @@ +#pragma once + +#include "wm/memory_definitions.h" + + +namespace armarx::armem::wm +{ + +} + diff --git a/source/RobotAPI/libraries/armem/core/wm/aron_conversions.cpp b/source/RobotAPI/libraries/armem/core/wm/aron_conversions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a4a5faa1404129feed5f2573c706c22900c108df --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/aron_conversions.cpp @@ -0,0 +1,69 @@ +#include "aron_conversions.h" + +#include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h> + + +namespace armarx::armem +{ + + constexpr const char* DATA_WRAPPER_DATA_FIELD = "__ARON_DATA"; + constexpr const char* DATA_WRAPPER_TIME_STORED_FIELD = "__WRITER_METADATA__TIME_STORED"; + constexpr const char* DATA_WRAPPER_TIME_CREATED_FIELD = "__ENTITY_METADATA__TIME_CREATED"; + constexpr const char* DATA_WRAPPER_TIME_SENT_FIELD = "__ENTITY_METADATA__TIME_SENT"; + constexpr const char* DATA_WRAPPER_TIME_ARRIVED_FIELD = "__ENTITY_METADATA__TIME_ARRIVED"; + constexpr const char* DATA_WRAPPER_CONFIDENCE_FIELD = "__ENTITY_METADATA__CONFIDENCE"; + +} + + +void armarx::armem::from_aron(const aron::datanavigator::DictNavigatorPtr& dataWrapped, wm::EntityInstance& e) +{ + wm::EntityInstanceMetadata& metadata = e.metadata(); + + if (dataWrapped->hasElement(DATA_WRAPPER_DATA_FIELD)) + { + e.data() = aron::datanavigator::DictNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_DATA_FIELD)); + } + + auto timeCreated = aron::datanavigator::LongNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_TIME_CREATED_FIELD)); + metadata.timeCreated = Time::microSeconds(timeCreated->toAronLongPtr()->value); + + auto timeSent = aron::datanavigator::LongNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_TIME_SENT_FIELD)); + metadata.timeSent = Time::microSeconds(timeSent->toAronLongPtr()->value); + + auto timeArrived = aron::datanavigator::LongNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_TIME_ARRIVED_FIELD)); + metadata.timeArrived = Time::microSeconds(timeArrived->toAronLongPtr()->value); + + auto confidence = aron::datanavigator::DoubleNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_CONFIDENCE_FIELD)); + metadata.confidence = static_cast<float>(confidence->toAronDoublePtr()->value); +} + + +void armarx::armem::to_aron(aron::datanavigator::DictNavigatorPtr& a, const wm::EntityInstance& e) +{ + if (e.data()) + { + a->addElement(DATA_WRAPPER_DATA_FIELD, e.data()); + } + + auto timeWrapped = std::make_shared<aron::datanavigator::LongNavigator>(); + timeWrapped->setValue(Time::now().toMicroSeconds()); + a->addElement(DATA_WRAPPER_TIME_STORED_FIELD, timeWrapped); + + const wm::EntityInstanceMetadata& metadata = e.metadata(); + auto timeCreated = std::make_shared<aron::datanavigator::LongNavigator>(); + timeCreated->setValue(metadata.timeCreated.toMicroSeconds()); + a->addElement(DATA_WRAPPER_TIME_CREATED_FIELD, timeCreated); + + auto timeSent = std::make_shared<aron::datanavigator::LongNavigator>(); + timeSent->setValue(metadata.timeSent.toMicroSeconds()); + a->addElement(DATA_WRAPPER_TIME_SENT_FIELD, timeSent); + + auto timeArrived = std::make_shared<aron::datanavigator::LongNavigator>(); + timeArrived->setValue(metadata.timeArrived.toMicroSeconds()); + a->addElement(DATA_WRAPPER_TIME_ARRIVED_FIELD, timeArrived); + + auto confidence = std::make_shared<aron::datanavigator::DoubleNavigator>(); + confidence->setValue(static_cast<double>(metadata.confidence)); + a->addElement(DATA_WRAPPER_CONFIDENCE_FIELD, confidence); +} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/entityInstance_conversions.h b/source/RobotAPI/libraries/armem/core/wm/aron_conversions.h similarity index 59% rename from source/RobotAPI/libraries/armem/core/workingmemory/entityInstance_conversions.h rename to source/RobotAPI/libraries/armem/core/wm/aron_conversions.h index f3b25566f110dca844e24dcac4c997f724c74548..f6b43dc8bd468008b9fe8ee470564f5de89e16cb 100644 --- a/source/RobotAPI/libraries/armem/core/workingmemory/entityInstance_conversions.h +++ b/source/RobotAPI/libraries/armem/core/wm/aron_conversions.h @@ -1,6 +1,9 @@ #pragma once -#include "Memory.h" +#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> + +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> + namespace armarx::armem { diff --git a/source/RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.cpp b/source/RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.cpp new file mode 100644 index 0000000000000000000000000000000000000000..50fd63bf64dabfedbab4e980f9450563d301e02b --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.cpp @@ -0,0 +1,7 @@ +#include "data_lookup_mixins.h" + + +namespace armarx::armem::base +{ + +} diff --git a/source/RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.h b/source/RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.h new file mode 100644 index 0000000000000000000000000000000000000000..c2d240e2977e1a11df3d8959a4f4200382d8650c --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.h @@ -0,0 +1,100 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/forward_declarations.h> +#include <RobotAPI/libraries/armem/core/base/detail/derived.h> + +#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> + +#include <optional> + + +namespace armarx::armem::wm::detail +{ + using base::detail::derived; + + + template <class AronDtoT> + std::optional<AronDtoT> + getInstanceDataAs(aron::datanavigator::DictNavigatorPtr data) + { + if (data) + { + AronDtoT aron; + aron.fromAron(data); + return aron; + } + else + { + return std::nullopt; + } + } + + + + template <class DerivedT> + struct FindInstanceDataMixinForSnapshot + { + + aron::datanavigator::DictNavigatorPtr + findInstanceData(int instanceIndex = 0) const + { + const auto* instance = derived<DerivedT>(this).findInstance(instanceIndex); + return instance ? instance->data() : nullptr; + } + + + template <class AronDtoT> + std::optional<AronDtoT> + findInstanceDataAs(int instanceIndex = 0) const + { + return getInstanceDataAs<AronDtoT>(derived<DerivedT>(this).findInstanceData(instanceIndex)); + } + + }; + + + + template <class DerivedT> + struct FindInstanceDataMixinForEntity + { + + aron::datanavigator::DictNavigatorPtr + findLatestInstanceData(int instanceIndex = 0) const + { + const auto* instance = derived<DerivedT>(this).findLatestInstance(instanceIndex); + return instance ? instance->data() : nullptr; + } + + + template <class AronDtoT> + std::optional<AronDtoT> + findLatestInstanceDataAs(int instanceIndex = 0) const + { + return getInstanceDataAs<AronDtoT>(derived<DerivedT>(this).findLatestInstanceData(instanceIndex)); + } + + }; + + + + template <class DerivedT> + struct FindInstanceDataMixin + { + + aron::datanavigator::DictNavigatorPtr + findLatestInstanceData(const MemoryID& entityID, int instanceIndex = 0) const + { + const auto* instance = derived<DerivedT>(this).findLatestInstance(entityID, instanceIndex); + return instance ? instance->data() : nullptr; + } + + template <class AronDtoT> + std::optional<AronDtoT> + findLatestInstanceDataAs(const MemoryID& entityID, int instanceIndex = 0) const + { + return getInstanceDataAs<AronDtoT>(derived<DerivedT>(this).findLatestInstanceData(entityID, instanceIndex)); + } + + }; + +} diff --git a/source/RobotAPI/libraries/armem/core/wm/ice_conversions.cpp b/source/RobotAPI/libraries/armem/core/wm/ice_conversions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba6c9782e0e2663ee9395fde31956d5fdbfc2ad2 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/ice_conversions.cpp @@ -0,0 +1,63 @@ +#include "ice_conversions.h" + +#include <RobotAPI/libraries/armem/core/base/ice_conversions.h> + + +namespace armarx::armem +{ + + void wm::toIce(data::EntityInstance& ice, const EntityInstance& data) + { + base::toIce(ice, data); + } + void wm::fromIce(const data::EntityInstance& ice, EntityInstance& data) + { + base::fromIce(ice, data); + } + + void wm::toIce(data::EntitySnapshot& ice, const EntitySnapshot& snapshot) + { + base::toIce(ice, snapshot); + } + void wm::fromIce(const data::EntitySnapshot& ice, EntitySnapshot& snapshot) + { + base::fromIce(ice, snapshot); + } + + void wm::toIce(data::Entity& ice, const Entity& entity) + { + base::toIce(ice, entity); + } + void wm::fromIce(const data::Entity& ice, Entity& entity) + { + base::fromIce(ice, entity); + } + + void wm::toIce(data::ProviderSegment& ice, const ProviderSegment& providerSegment) + { + base::toIce(ice, providerSegment); + } + void wm::fromIce(const data::ProviderSegment& ice, ProviderSegment& providerSegment) + { + base::fromIce(ice, providerSegment); + } + + void wm::toIce(data::CoreSegment& ice, const CoreSegment& coreSegment) + { + base::toIce(ice, coreSegment); + } + void wm::fromIce(const data::CoreSegment& ice, CoreSegment& coreSegment) + { + base::fromIce(ice, coreSegment); + } + + void wm::toIce(data::Memory& ice, const Memory& memory) + { + base::toIce(ice, memory); + } + void wm::fromIce(const data::Memory& ice, Memory& memory) + { + base::fromIce(ice, memory); + } + +} diff --git a/source/RobotAPI/libraries/armem/core/wm/ice_conversions.h b/source/RobotAPI/libraries/armem/core/wm/ice_conversions.h new file mode 100644 index 0000000000000000000000000000000000000000..4a58f16aaa6eef4c6f6685a86ed66298c1407454 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/ice_conversions.h @@ -0,0 +1,34 @@ +#pragma once + +#include <RobotAPI/interface/armem/commit.h> +#include <RobotAPI/interface/armem/memory.h> + +#include "memory_definitions.h" + + +namespace armarx::armem::wm +{ + + void toIce(data::EntityInstance& ice, const EntityInstance& data); + void fromIce(const data::EntityInstance& ice, EntityInstance& data); + + + void toIce(data::EntitySnapshot& ice, const EntitySnapshot& snapshot); + void fromIce(const data::EntitySnapshot& ice, EntitySnapshot& snapshot); + + void toIce(data::Entity& ice, const Entity& entity); + void fromIce(const data::Entity& ice, Entity& entity); + + + void toIce(data::ProviderSegment& ice, const ProviderSegment& providerSegment); + void fromIce(const data::ProviderSegment& ice, ProviderSegment& providerSegment); + + void toIce(data::CoreSegment& ice, const CoreSegment& coreSegment); + void fromIce(const data::CoreSegment& ice, CoreSegment& coreSegment); + + void toIce(data::Memory& ice, const Memory& memory); + void fromIce(const data::Memory& ice, Memory& memory); +} + +// Must be included after the prototypes. Otherwise the compiler cannot find the correct methods in ice_coversion_templates.h +#include <RobotAPI/libraries/armem/core/ice_conversions.h> diff --git a/source/RobotAPI/libraries/armem/core/wm/memory_definitions.cpp b/source/RobotAPI/libraries/armem/core/wm/memory_definitions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..de5ad02c2e9e4f24304c2ae9b9eb97f0cd3e53ee --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/memory_definitions.cpp @@ -0,0 +1,45 @@ +#include "memory_definitions.h" + +#include "error.h" + +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <map> +#include <vector> + + + +namespace armarx::armem::wm +{ + + bool EntityInstance::equalsDeep(const EntityInstance& other) const + { + if (_data and other.data()) + { + return id() == other.id() + && _metadata == other.metadata() + && *_data == *other.data(); + } + if (_data or other.data()) + { + return false; + } + return id() == other.id() && _metadata == other.metadata(); + } + + + void EntityInstance::update(const EntityUpdate& update) + { + ARMARX_CHECK_FITS_SIZE(this->index(), update.instancesData.size()); + + this->data() = update.instancesData.at(size_t(this->index())); + + this->_metadata.confidence = update.confidence; + + this->_metadata.timeCreated = update.timeCreated; + this->_metadata.timeSent = update.timeSent; + this->_metadata.timeArrived = update.timeArrived; + } + +} diff --git a/source/RobotAPI/libraries/armem/core/wm/memory_definitions.h b/source/RobotAPI/libraries/armem/core/wm/memory_definitions.h new file mode 100644 index 0000000000000000000000000000000000000000..b77ea3c6e9eb17b69ce3feb848951012a0ed0304 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/memory_definitions.h @@ -0,0 +1,116 @@ +#pragma once + +#include "detail/data_lookup_mixins.h" + +#include <RobotAPI/libraries/armem/core/base/EntityInstanceBase.h> +#include <RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h> +#include <RobotAPI/libraries/armem/core/base/EntityBase.h> +#include <RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h> +#include <RobotAPI/libraries/armem/core/base/CoreSegmentBase.h> +#include <RobotAPI/libraries/armem/core/base/MemoryBase.h> + +#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> + + +namespace armarx::armem::wm +{ + + using EntityInstanceMetadata = base::EntityInstanceMetadata; + using EntityInstanceData = armarx::aron::datanavigator::DictNavigator; + using EntityInstanceDataPtr = armarx::aron::datanavigator::DictNavigatorPtr; + + + /// @see base::EntityInstanceBase + class EntityInstance : + public base::EntityInstanceBase<EntityInstanceDataPtr, EntityInstanceMetadata> + { + using Base = base::EntityInstanceBase<EntityInstanceDataPtr, EntityInstanceMetadata>; + + public: + + using Base::EntityInstanceBase; + + + /** + * @brief Fill `*this` with the update's values. + * @param update The update. + * @param index The instances index. + */ + void update(const EntityUpdate& update); + + bool equalsDeep(const EntityInstance& other) const; + + }; + + + + /// @see base::EntitySnapshotBase + class EntitySnapshot : + public base::EntitySnapshotBase<EntityInstance, EntitySnapshot> + , public detail::FindInstanceDataMixinForSnapshot<EntitySnapshot> + { + public: + + using base::EntitySnapshotBase<EntityInstance, EntitySnapshot>::EntitySnapshotBase; + + }; + + + /// @see base::EntityBase + class Entity : + public base::EntityBase<EntitySnapshot, Entity> + , public detail::FindInstanceDataMixinForEntity<Entity> + { + public: + + using base::EntityBase<EntitySnapshot, Entity>::EntityBase; + + }; + + + + /// @see base::ProviderSegmentBase + class ProviderSegment : + public base::ProviderSegmentBase<Entity, ProviderSegment> + , public detail::FindInstanceDataMixin<ProviderSegment> + { + using Base = base::ProviderSegmentBase<Entity, ProviderSegment>; + + public: + + using Base::ProviderSegmentBase; + + }; + + + + /// @see base::CoreSegmentBase + class CoreSegment : + public base::CoreSegmentBase<ProviderSegment, CoreSegment> + , public detail::FindInstanceDataMixin<CoreSegment> + { + using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; + + public: + + using Base::CoreSegmentBase; + + }; + + + + /// @see base::MemoryBase + class Memory : + public base::MemoryBase<CoreSegment, Memory> + , public detail::FindInstanceDataMixin<Memory> + { + using Base = base::MemoryBase<CoreSegment, Memory>; + + public: + + using Base::MemoryBase; + + }; + +} + diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/visitor/FunctionalVisitor.cpp b/source/RobotAPI/libraries/armem/core/wm/visitor/FunctionalVisitor.cpp similarity index 82% rename from source/RobotAPI/libraries/armem/core/workingmemory/visitor/FunctionalVisitor.cpp rename to source/RobotAPI/libraries/armem/core/wm/visitor/FunctionalVisitor.cpp index 53016fc6c83d9ef6e24a2e75942a12bced7d0eaa..f72b37c1a7ab68320c8ff2ec8c071dd741e61f22 100644 --- a/source/RobotAPI/libraries/armem/core/workingmemory/visitor/FunctionalVisitor.cpp +++ b/source/RobotAPI/libraries/armem/core/wm/visitor/FunctionalVisitor.cpp @@ -2,7 +2,7 @@ #include <ArmarXCore/core/exceptions/local/ExpressionException.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/core/error.h> @@ -18,6 +18,4 @@ namespace armarx::armem::wm { } - - } diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/visitor/FunctionalVisitor.h b/source/RobotAPI/libraries/armem/core/wm/visitor/FunctionalVisitor.h similarity index 100% rename from source/RobotAPI/libraries/armem/core/workingmemory/visitor/FunctionalVisitor.h rename to source/RobotAPI/libraries/armem/core/wm/visitor/FunctionalVisitor.h diff --git a/source/RobotAPI/libraries/armem/core/wm/visitor/Visitor.cpp b/source/RobotAPI/libraries/armem/core/wm/visitor/Visitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9448f55f8f68385d716f9b8e7da26e890ffb48fd --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/wm/visitor/Visitor.cpp @@ -0,0 +1,142 @@ +#include "Visitor.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/error.h> + + +namespace armarx::armem::wm +{ + + Visitor::Visitor() + { + } + + Visitor::~Visitor() + { + } + + bool Visitor::applyTo(Memory& memory) + { + visitEnter(memory); + bool cont = memory.forEachCoreSegment([this](CoreSegment & coreSeg) + { + return applyTo(coreSeg); + }); + visitExit(memory); + return cont; + } + + bool Visitor::applyTo(CoreSegment& coreSegment) + { + visitEnter(coreSegment); + bool cont = coreSegment.forEachProviderSegment([this](ProviderSegment & provSeg) + { + return applyTo(provSeg); + }); + visitExit(coreSegment); + return cont; + } + + bool Visitor::applyTo(ProviderSegment& providerSegment) + { + visitEnter(providerSegment); + bool cont = providerSegment.forEachEntity([this](Entity & entity) + { + return applyTo(entity); + }); + visitExit(providerSegment); + return cont; + } + + bool Visitor::applyTo(Entity& entity) + { + visitEnter(entity); + bool cont = entity.forEachSnapshot([this](EntitySnapshot & snapshot) + { + return applyTo(snapshot); + }); + visitExit(entity); + return cont; + } + + bool Visitor::applyTo(EntitySnapshot& snapshot) + { + visitEnter(snapshot); + bool cont = snapshot.forEachInstance([this](EntityInstance & instance) + { + return applyTo(instance); + }); + visitExit(snapshot); + return cont; + } + + bool Visitor::applyTo(EntityInstance& instance) + { + return visit(instance); + } + + + bool Visitor::applyTo(const Memory& memory) + { + visitEnter(memory); + bool cont = memory.forEachCoreSegment([this](const CoreSegment & coreSeg) + { + return applyTo(coreSeg); + }); + visitExit(memory); + return cont; + } + + bool Visitor::applyTo(const CoreSegment& coreSegment) + { + visitEnter(coreSegment); + bool cont = coreSegment.forEachProviderSegment([this](const ProviderSegment & provSeg) + { + return applyTo(provSeg); + }); + visitExit(coreSegment); + return cont; + } + + bool Visitor::applyTo(const ProviderSegment& providerSegment) + { + visitEnter(providerSegment); + bool cont = providerSegment.forEachEntity([this](const Entity & entity) + { + return applyTo(entity); + }); + visitExit(providerSegment); + return cont; + } + + bool Visitor::applyTo(const Entity& entity) + { + visitEnter(entity); + bool cont = entity.forEachSnapshot([this](const EntitySnapshot & snapshot) + { + return applyTo(snapshot); + }); + visitExit(entity); + return cont; + } + + bool Visitor::applyTo(const EntitySnapshot& snapshot) + { + visitEnter(snapshot); + bool cont = snapshot.forEachInstance([this](const EntityInstance & instance) + { + return applyTo(instance); + }); + visitExit(snapshot); + return cont; + } + + bool Visitor::applyTo(const EntityInstance& instance) + { + return visit(instance); + } + +} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/visitor/Visitor.h b/source/RobotAPI/libraries/armem/core/wm/visitor/Visitor.h similarity index 100% rename from source/RobotAPI/libraries/armem/core/workingmemory/visitor/Visitor.h rename to source/RobotAPI/libraries/armem/core/wm/visitor/Visitor.h diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/CoreSegment.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/CoreSegment.cpp deleted file mode 100644 index 601f079350de8536aff130943d539eafee4e43b3..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/CoreSegment.cpp +++ /dev/null @@ -1,136 +0,0 @@ -#include "CoreSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::wm -{ - - CoreSegment::CoreSegment(const CoreSegment& other) : - CoreSegment::Base(other), - _mutex() - { - // Do not copy _mutex. - } - - - CoreSegment::CoreSegment(CoreSegment&& other) : CoreSegment::Base(other) - { - // Do not move _mutex. - } - - - CoreSegment& CoreSegment::operator=(const CoreSegment& other) - { - Base::operator=(other); - // Don't copy _mutex. - return *this; - } - - - CoreSegment& CoreSegment::operator=(CoreSegment&& other) - { - Base::operator=(other); - // Don't move _mutex. - return *this; - } - - - Commit CoreSegment::toCommit() const - { - Commit c; - for (const auto& [k, s] : _container) - { - c.append(s.toCommit()); - } - return c; - } - - - void CoreSegment::_copySelfWithoutData(CoreSegment& other) const - { - other.id() = _id; - other.setMaxHistorySize(_maxHistorySize); - for (const auto& [k, s] : _container) - { - other.addProviderSegment(s.copyWithoutData()); - } - } - - - std::mutex& CoreSegment::mutex() const - { - return _mutex; - } - - - std::optional<wm::EntitySnapshot> CoreSegment::getLatestEntitySnapshot(const MemoryID& entityID) const - { - const wm::Entity& entity = this->getEntity(entityID); - if (entity.empty()) - { - return std::nullopt; - } - else - { - return entity.getLatestSnapshot(); - } - } - - - std::optional<wm::EntityInstance> CoreSegment::getLatestEntityInstance( - const MemoryID& entityID, int instanceIndex) const - { - auto snapshot = getLatestEntitySnapshot(entityID); - if (snapshot.has_value() - and instanceIndex >= 0 - and static_cast<size_t>(instanceIndex) < snapshot->size()) - { - return snapshot->getInstance(instanceIndex); - } - else - { - return std::nullopt; - } - } - - - armarx::aron::datanavigator::DictNavigatorPtr - CoreSegment::getLatestEntityInstanceData(const MemoryID& entityID, int instanceIndex) const - { - auto instance = getLatestEntityInstance(entityID, instanceIndex); - if (instance.has_value()) - { - return instance->data(); - } - else - { - return nullptr; - } - } - - - std::optional<wm::EntitySnapshot> - CoreSegment::getLatestEntitySnapshotLocking(const MemoryID& entityID) const - { - std::scoped_lock lock(_mutex); - return getLatestEntitySnapshot(entityID); - } - - std::optional<wm::EntityInstance> - CoreSegment::getLatestEntityInstanceLocking(const MemoryID& entityID, int instanceIndex) const - { - std::scoped_lock lock(_mutex); - return getLatestEntityInstance(entityID, instanceIndex); - } - - armarx::aron::datanavigator::DictNavigatorPtr - CoreSegment::getLatestEntityInstanceDataLocking(const MemoryID& entityID, int instanceIndex) const - { - std::scoped_lock lock(_mutex); - return getLatestEntityInstanceData(entityID, instanceIndex); - } - -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h b/source/RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h deleted file mode 100644 index 86b9b439e72d4d686b2c52fe5fd39f0a9b6aaf1c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include <mutex> -#include <optional> - -#include "../base/CoreSegmentBase.h" - -#include "ProviderSegment.h" -#include "detail/CopyWithoutData.h" - - -namespace armarx::armem::wm -{ - - /** - * @brief Data of a core segment containing multiple provider segments. - */ - class CoreSegment : - public base::CoreSegmentBase<ProviderSegment, CoreSegment>, - public detail::CopyWithoutData<CoreSegment> - { - using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; - - public: - - using Base::CoreSegmentBase; - - CoreSegment(const CoreSegment& other); - CoreSegment(CoreSegment&& other); - CoreSegment& operator=(const CoreSegment& other); - CoreSegment& operator=(CoreSegment&& other); - - /** - * @brief Convert the content of this segmnet into a commit - * @return The resulting commit - */ - Commit toCommit() const; - - std::mutex& mutex() const; - - - // Non-locking interface - - std::optional<wm::EntitySnapshot> getLatestEntitySnapshot( - const MemoryID& entityID) const; - std::optional<wm::EntityInstance> getLatestEntityInstance( - const MemoryID& entityID, int instanceIndex = 0) const; - armarx::aron::datanavigator::DictNavigatorPtr getLatestEntityInstanceData( - const MemoryID& entityID, int instanceIndex = 0) const; - - - template <class AronDtoT> - std::optional<AronDtoT> getLatestEntityInstanceDataAs( - const MemoryID& entityID, int instanceIndex = 0) const - { - wm::EntityInstanceDataPtr data = getLatestEntityInstanceData(entityID, instanceIndex); - if (data) - { - AronDtoT aron; - aron.fromAron(data); - return aron; - } - else - { - return std::nullopt; - } - } - - - // Locking interface - - std::optional<wm::EntitySnapshot> getLatestEntitySnapshotLocking( - const MemoryID& entityID) const; - std::optional<wm::EntityInstance> getLatestEntityInstanceLocking( - const MemoryID& entityID, int instanceIndex = 0) const; - armarx::aron::datanavigator::DictNavigatorPtr getLatestEntityInstanceDataLocking( - const MemoryID& entityID, int instanceIndex = 0) const; - - - template <class AronDtoT> - std::optional<AronDtoT> getLatestEntityInstanceDataLockingAs( - const MemoryID& entityID, int instanceIndex = 0) const - { - // Keep lock to a minimum. - wm::EntityInstanceDataPtr data = nullptr; - { - std::scoped_lock lock(_mutex); - data = getLatestEntityInstanceData(entityID, instanceIndex); - } - if (data) - { - AronDtoT aron; - aron.fromAron(data); - return aron; - } - else - { - return std::nullopt; - } - } - - - protected: - - virtual void _copySelfWithoutData(CoreSegment& other) const override; - - mutable std::mutex _mutex; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/Entity.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/Entity.cpp deleted file mode 100644 index e704e96d5454d27bfb095120c1901e000611122a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/Entity.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "Entity.h" - -namespace armarx::armem::wm -{ - - Commit Entity::toCommit() const - { - Commit c; - for (const auto& [k, s] : _container) - { - EntityUpdate& up = c.add(); - up.entityID = id(); - up.timeCreated = k; - up.instancesData = s.getAronData(); - } - return c; - } - - void Entity::_copySelfWithoutData(Entity& other) const - { - other.id() = _id; - other.setMaxHistorySize(_maxHistorySize); - for (const auto& [k, s] : _container) - { - other.addSnapshot(s.copyWithoutData()); - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/Entity.h b/source/RobotAPI/libraries/armem/core/workingmemory/Entity.h deleted file mode 100644 index a6a7621e21272db6447e9e09b695900eefd263c1..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/Entity.h +++ /dev/null @@ -1,57 +0,0 @@ -#pragma once - -#include "../base/EntityBase.h" - -#include "EntitySnapshot.h" -#include "detail/CopyWithoutData.h" - - -namespace armarx::armem::wm -{ - /** - * @brief An entity over a period of time. - * - * An entity should be a physical thing or abstract concept existing - * (and potentially evolving) over some time. - * - * Examples are: - * - objects (the green box) - * - agents (robot, human) - * - locations (frige, sink) - * - grasp affordances (general, or for a specific object) - * - images - * - point clouds - * - other sensory values - * - * At each point in time (`EntitySnapshot`), the entity can have a - * (potentially variable) number of instances (`EntityInstance`), - * each containing a single `AronData` object of a specific `AronType`. - */ - class Entity : - public base::EntityBase<EntitySnapshot, Entity>, - public detail::CopyWithoutData<Entity> - { - using Base = base::EntityBase<EntitySnapshot, Entity>; - - public: - - using Base::EntityBase; - - Entity(const Entity& other) = default; - Entity(Entity&& other) = default; - Entity& operator=(const Entity& other) = default; - Entity& operator=(Entity&& other) = default; - - /** - * @brief Convert the content of this segmnet into a commit - * @return The resulting commit - */ - Commit toCommit() const; - - protected: - - virtual void _copySelfWithoutData(Entity& other) const override; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/EntityInstance.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/EntityInstance.cpp deleted file mode 100644 index 6149449c77533abd656a460097969f0066d8d88f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/EntityInstance.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "EntityInstance.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - - -std::ostream& armarx::armem::wm::operator<<(std::ostream& os, const EntityInstanceMetadata& d) -{ - os << "EntityInstanceMetadata: " - << "\n- t_create = \t" << armem::toStringMicroSeconds(d.timeCreated) << " us" - << "\n- t_sent = \t" << armem::toStringMicroSeconds(d.timeSent) << " us" - << "\n- t_arrived = \t" << armem::toStringMicroSeconds(d.timeArrived) << " us" - << "\n- confidence = \t" << d.confidence << " us" - ; - return os; -} - -namespace armarx::armem::wm -{ - - bool EntityInstanceMetadata::operator==(const EntityInstanceMetadata& other) const - { - return timeCreated == other.timeCreated - && timeSent == other.timeSent - && timeArrived == other.timeArrived - && std::abs(confidence - other.confidence) < 1e-6f; - } - - - bool EntityInstance::equalsDeep(const EntityInstance& other) const - { - if (_data and other.data()) - { - return id() == other.id() - && _metadata == other.metadata() - && *_data == *other.data(); - } - if (_data or other.data()) - { - return false; - } - return id() == other.id() && _metadata == other.metadata(); - } - - void EntityInstance::update(const EntityUpdate& update, int index) - { - ARMARX_CHECK_FITS_SIZE(index, update.instancesData.size()); - - this->index() = index; - setData(update.instancesData.at(size_t(index))); - - this->_metadata.confidence = update.confidence; - - this->_metadata.timeCreated = update.timeCreated; - this->_metadata.timeSent = update.timeSent; - this->_metadata.timeArrived = update.timeArrived; - } - - EntityInstance EntityInstance::copy() const - { - EntityInstance d; - this->_copySelf(d); - return d; - } - - void EntityInstance::_copySelf(EntityInstance& other) const - { - Base::_copySelf(other); - other._metadata = _metadata; - other._data = _data; - } - - void EntityInstance::_copySelfWithoutData(EntityInstance& other) const - { - Base::_copySelf(other); - other._metadata = _metadata; - } -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h b/source/RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h deleted file mode 100644 index d3dc888d0304276bc6aaf1c677e8b33f4a1cae1a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "../base/EntityInstanceBase.h" - -#include "detail/CopyWithoutData.h" - - -namespace armarx::armem::wm -{ - - using EntityInstanceData = armarx::aron::datanavigator::DictNavigator; - using EntityInstanceDataPtr = armarx::aron::datanavigator::DictNavigatorPtr; - - - /** - * @brief Metadata of an entity instance. - */ - struct EntityInstanceMetadata - { - /// Time when this value was created. - Time timeCreated; - /// Time when this value was sent to the memory. - Time timeSent; - /// Time when this value has arrived at the memory. - Time timeArrived; - - /// An optional confidence, may be used for things like decay. - float confidence = 1.0; - - - bool operator==(const EntityInstanceMetadata& other) const; - inline bool operator!=(const EntityInstanceMetadata& other) const - { - return !(*this == other); - } - }; - - std::ostream& operator<<(std::ostream& os, const EntityInstanceMetadata& rhs); - - - /** - * @brief Data of a single entity instance. - */ - class EntityInstance : - public base::EntityInstanceBase<EntityInstance>, - public detail::CopyWithoutData<EntityInstance> - { - using Base = base::EntityInstanceBase<EntityInstance>; - - public: - - using Base::EntityInstanceBase; - - EntityInstance(const EntityInstance& other) = default; - EntityInstance(EntityInstance&& other) = default; - EntityInstance& operator=(const EntityInstance& other) = default; - EntityInstance& operator=(EntityInstance&& other) = default; - - - EntityInstanceMetadata& metadata() - { - return _metadata; - } - inline const EntityInstanceMetadata& metadata() const - { - return _metadata; - } - - inline EntityInstanceDataPtr data() const - { - return _data; - } - - void setData(const aron::datanavigator::DictNavigatorPtr& data) - { - this->_data = data; - } - - - /** - * @brief Fill `*this` with the update's values. - * @param update The update. - * @param index The instances index. - */ - virtual void update(const EntityUpdate& update, int index) override; - - virtual bool equalsDeep(const EntityInstance& other) const override; - - virtual EntityInstance copy() const override; - - - protected: - - virtual void _copySelf(EntityInstance& other) const override; - virtual void _copySelfWithoutData(EntityInstance& other) const override; - - - private: - - /// The metadata. - EntityInstanceMetadata _metadata; - - /// The data. May be nullptr. - EntityInstanceDataPtr _data; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.cpp deleted file mode 100644 index 76d5400131a7ec6a57478f13afb0dfc38a4da174..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include "EntitySnapshot.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::wm -{ - std::vector<aron::datanavigator::DictNavigatorPtr> EntitySnapshot::getAronData() const - { - std::vector<aron::datanavigator::DictNavigatorPtr> ret; - for (const auto& aron : _container) - { - ret.push_back(aron.data()); - } - return ret; - } - - void EntitySnapshot::_copySelfWithoutData(EntitySnapshot& other) const - { - other.id() = _id; - for (const auto& s : _container) - { - other.addInstance(s.copyWithoutData()); - } - } -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.h b/source/RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.h deleted file mode 100644 index 3eedb48d0b4aaa5191541d8ca1586ffc59ab5998..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/EntitySnapshot.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "../base/EntitySnapshotBase.h" - -#include "EntityInstance.h" -#include "detail/CopyWithoutData.h" - - -namespace armarx::armem::wm -{ - - /** - * @brief Data of an entity at one point in time. - */ - class EntitySnapshot : - public base::EntitySnapshotBase<EntityInstance, EntitySnapshot>, - public detail::CopyWithoutData<EntitySnapshot> - { - using Base = base::EntitySnapshotBase<EntityInstance, EntitySnapshot>; - - public: - - using Base::EntitySnapshotBase; - using Base::operator=; - - std::vector<aron::datanavigator::DictNavigatorPtr> getAronData() const; - - protected: - - virtual void _copySelfWithoutData(EntitySnapshot& other) const override; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/Memory.h b/source/RobotAPI/libraries/armem/core/workingmemory/Memory.h deleted file mode 100644 index 5093ffce2adddfcdd27e1f08529475335fc84ff2..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/Memory.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -// Base Class -#include <RobotAPI/libraries/armem/core/base/MemoryBase.h> - -// ArmarX -#include "CoreSegment.h" -#include "detail/CopyWithoutData.h" - -// Properties -#include <ArmarXCore/core/application/properties/PluginAll.h> -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> - -namespace armarx::armem::wm -{ - - /** - * @brief Data of a memory consisting of multiple core segments. - */ - class Memory : - public base::MemoryBase<CoreSegment, Memory>, - public detail::CopyWithoutData<Memory> - { - using Base = base::MemoryBase<CoreSegment, Memory>; - - public: - - using Base::MemoryBase; - - Memory(const Memory& other) = default; - Memory(Memory&& other) = default; - Memory& operator=(const Memory& other) = default; - Memory& operator=(Memory&& other) = default; - - // PropertyDefinitions related to LTM - void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = ""); - - /** - * @brief Perform the commit, locking the core segments. - * - * Groups the commits by core segment, and updates each core segment - * in a batch, locking the core segment. - */ - std::vector<Base::UpdateResult> updateLocking(const Commit& commit); - - /** - * @brief Update the memory, locking the updated core segment. - */ - Base::UpdateResult updateLocking(const EntityUpdate& update); - - - /** - * @brief Convert the content of this memory into a commit - * @return The resulting commit - */ - Commit toCommit() const; - - - protected: - - virtual void _copySelfWithoutData(Memory& other) const override; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.cpp deleted file mode 100644 index bd03cf21cd8b616a3afcc4d531377288e55c5f2f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "ProviderSegment.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include "error.h" - - -namespace armarx::armem::wm -{ - - Commit ProviderSegment::toCommit() const - { - Commit c; - for (const auto& [k, s] : _container) - { - c.append(s.toCommit()); - } - return c; - } - - void ProviderSegment::_copySelfWithoutData(ProviderSegment& other) const - { - other.id() = _id; - other.setMaxHistorySize(_maxHistorySize); - for (const auto& [k, s] : _container) - { - other.addEntity(s.copyWithoutData()); - } - } - -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h b/source/RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h deleted file mode 100644 index 7d1e9db335ee39433351a8e15113059836bf825d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "../base/ProviderSegmentBase.h" - -#include "Entity.h" -#include "detail/CopyWithoutData.h" - - -namespace armarx::armem::wm -{ - - /** - * @brief Data of a provider segment containing multiple entities. - */ - class ProviderSegment : - public base::ProviderSegmentBase<Entity, ProviderSegment>, - public detail::CopyWithoutData<ProviderSegment> - - { - using Base = base::ProviderSegmentBase<Entity, ProviderSegment>; - - public: - - using Base::ProviderSegmentBase; - - ProviderSegment(const ProviderSegment& other) = default; - ProviderSegment(ProviderSegment&& other) = default; - ProviderSegment& operator=(const ProviderSegment& other) = default; - ProviderSegment& operator=(ProviderSegment&& other) = default; - - /** - * @brief Convert the content of this segmnet into a commit - * @return The resulting commit - */ - Commit toCommit() const; - - - protected: - - virtual void _copySelfWithoutData(ProviderSegment& other) const override; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/detail/CopyWithoutData.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/detail/CopyWithoutData.cpp deleted file mode 100644 index 70441033dd0fcb24a502bcd4e552764c7c2f5d3c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/detail/CopyWithoutData.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "CopyWithoutData.h" diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/detail/CopyWithoutData.h b/source/RobotAPI/libraries/armem/core/workingmemory/detail/CopyWithoutData.h deleted file mode 100644 index caabd1e6002c803aa6a1015afd7ce660abd2482f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/detail/CopyWithoutData.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - - -namespace armarx::armem::wm::detail -{ - - /** - * @class Allows copying `*this` without data in the leaf - * data structures. - */ - template <class DerivedT> - class CopyWithoutData - { - public: - - /// Get a copy of `this` without data. - virtual DerivedT copyWithoutData() const - { - DerivedT t; - _copySelfWithoutData(t); - return t; - } - - - protected: - - virtual void _copySelfWithoutData(DerivedT& other) const = 0; - - }; -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/entityInstance_conversions.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/entityInstance_conversions.cpp deleted file mode 100644 index 31538f8d8e2f1eeaf932ed16e50b7040520f0a99..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/entityInstance_conversions.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "json_conversions.h" - -namespace armarx::armem -{ - - constexpr const char* DATA_WRAPPER_DATA_FIELD = "__ARON_DATA"; - constexpr const char* DATA_WRAPPER_TIME_STORED_FIELD = "__WRITER_METADATA__TIME_STORED"; - constexpr const char* DATA_WRAPPER_TIME_CREATED_FIELD = "__ENTITY_METADATA__TIME_CREATED"; - constexpr const char* DATA_WRAPPER_TIME_SENT_FIELD = "__ENTITY_METADATA__TIME_SENT"; - constexpr const char* DATA_WRAPPER_TIME_ARRIVED_FIELD = "__ENTITY_METADATA__TIME_ARRIVED"; - constexpr const char* DATA_WRAPPER_CONFIDENCE_FIELD = "__ENTITY_METADATA__CONFIDENCE"; - - void from_aron(const aron::datanavigator::DictNavigatorPtr& dataWrapped, wm::EntityInstance& e) - { - wm::EntityInstanceMetadata& metadata = e.metadata(); - - if (dataWrapped->hasElement(DATA_WRAPPER_DATA_FIELD)) - { - aron::datanavigator::DictNavigatorPtr data = aron::datanavigator::DictNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_DATA_FIELD)); - e.setData(data); - } - - auto timeCreated = aron::datanavigator::LongNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_TIME_CREATED_FIELD)); - metadata.timeCreated = Time::microSeconds(timeCreated->toAronLongPtr()->value); - - auto timeSent = aron::datanavigator::LongNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_TIME_SENT_FIELD)); - metadata.timeSent = Time::microSeconds(timeSent->toAronLongPtr()->value); - - auto timeArrived = aron::datanavigator::LongNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_TIME_ARRIVED_FIELD)); - metadata.timeArrived = Time::microSeconds(timeArrived->toAronLongPtr()->value); - - auto confidence = aron::datanavigator::DoubleNavigator::DynamicCastAndCheck(dataWrapped->getElement(DATA_WRAPPER_CONFIDENCE_FIELD)); - metadata.confidence = static_cast<float>(confidence->toAronDoublePtr()->value); - } - - void to_aron(aron::datanavigator::DictNavigatorPtr& a, const wm::EntityInstance& e) - { - if (e.data()) - { - a->addElement(DATA_WRAPPER_DATA_FIELD, e.data()); - } - - auto timeWrapped = std::make_shared<aron::datanavigator::LongNavigator>(); - timeWrapped->setValue(Time::now().toMicroSeconds()); - a->addElement(DATA_WRAPPER_TIME_STORED_FIELD, timeWrapped); - - const wm::EntityInstanceMetadata& metadata = e.metadata(); - auto timeCreated = std::make_shared<aron::datanavigator::LongNavigator>(); - timeCreated->setValue(metadata.timeCreated.toMicroSeconds()); - a->addElement(DATA_WRAPPER_TIME_CREATED_FIELD, timeCreated); - - auto timeSent = std::make_shared<aron::datanavigator::LongNavigator>(); - timeSent->setValue(metadata.timeSent.toMicroSeconds()); - a->addElement(DATA_WRAPPER_TIME_SENT_FIELD, timeSent); - - auto timeArrived = std::make_shared<aron::datanavigator::LongNavigator>(); - timeArrived->setValue(metadata.timeArrived.toMicroSeconds()); - a->addElement(DATA_WRAPPER_TIME_ARRIVED_FIELD, timeArrived); - - auto confidence = std::make_shared<aron::datanavigator::DoubleNavigator>(); - confidence->setValue(static_cast<double>(metadata.confidence)); - a->addElement(DATA_WRAPPER_CONFIDENCE_FIELD, confidence); - } -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/ice_conversions.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/ice_conversions.cpp deleted file mode 100644 index bfc049995a6553e53a78be00294cb9c162e80a0d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/ice_conversions.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "ice_conversions.h" - -namespace armarx::armem -{ - void toIce(armarx::armem::data::EntityInstanceMetadata& ice, const armarx::armem::wm::EntityInstanceMetadata& metadata) - { - ice.confidence = metadata.confidence; - toIce(ice.timeArrivedMicroSeconds, metadata.timeArrived); - toIce(ice.timeCreatedMicroSeconds, metadata.timeCreated); - toIce(ice.timeSentMicroSeconds, metadata.timeSent); - } - void fromIce(const armarx::armem::data::EntityInstanceMetadata& ice, armarx::armem::wm::EntityInstanceMetadata& metadata) - { - metadata.confidence = ice.confidence; - fromIce(ice.timeArrivedMicroSeconds, metadata.timeArrived); - fromIce(ice.timeCreatedMicroSeconds, metadata.timeCreated); - fromIce(ice.timeSentMicroSeconds, metadata.timeSent); - } - - void toIce(data::EntityInstance& ice, const wm::EntityInstance& data) - { - detail::toIceItem(ice, data); - - if (data.data()) - { - ice.data = data.data()->toAronDictPtr(); - } - toIce(ice.metadata, data.metadata()); - } - void fromIce(const data::EntityInstance& ice, wm::EntityInstance& data) - { - detail::fromIceItem(ice, data); - - if (ice.data) - { - data.setData(aron::datanavigator::DictNavigator::FromAronDictPtr(ice.data)); - } - fromIce(ice.metadata, data.metadata()); - } - - - void toIce(data::EntitySnapshot& ice, const wm::EntitySnapshot& snapshot) - { - detail::toIceItem(ice, snapshot); - - toIce(ice.instances, snapshot.instances()); - } - void fromIce(const data::EntitySnapshot& ice, wm::EntitySnapshot& snapshot) - { - detail::fromIceItem(ice, snapshot); - - fromIce(ice.instances, snapshot.instances()); - } - - void toIce(data::Entity& ice, const wm::Entity& entity) - { - detail::toIceItem(ice, entity); - - toIce(ice.history, entity.history()); - } - void fromIce(const data::Entity& ice, wm::Entity& entity) - { - detail::fromIceItem(ice, entity); - - fromIce(ice.history, entity.history()); - } - - - void toIce(data::ProviderSegment& ice, const wm::ProviderSegment& providerSegment) - { - detail::toIceItem(ice, providerSegment); - - if (providerSegment.hasAronType()) - { - ice.aronType = providerSegment.aronType()->toAronPtr(); - } - ARMARX_CHECK(!providerSegment.aronType() || ice.aronType); - toIce(ice.entities, providerSegment.entities()); - } - void fromIce(const data::ProviderSegment& ice, wm::ProviderSegment& providerSegment) - { - detail::fromIceItem(ice, providerSegment); - - if (ice.aronType) - { - providerSegment.aronType() = aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(aron::typenavigator::Navigator::FromAronType(ice.aronType)); - } - ARMARX_CHECK(!providerSegment.aronType() || ice.aronType); - fromIce(ice.entities, providerSegment.entities()); - } - - void toIce(data::CoreSegment& ice, const wm::CoreSegment& coreSegment) - { - detail::toIceItem(ice, coreSegment); - - if (coreSegment.hasAronType()) - { - ice.aronType = coreSegment.aronType()->toAronPtr(); - } - ARMARX_CHECK(!coreSegment.aronType() || ice.aronType); - toIce(ice.providerSegments, coreSegment.providerSegments()); - } - void fromIce(const data::CoreSegment& ice, wm::CoreSegment& coreSegment) - { - detail::fromIceItem(ice, coreSegment); - - if (ice.aronType) - { - coreSegment.aronType() = aron::typenavigator::ObjectNavigator::DynamicCastAndCheck(aron::typenavigator::Navigator::FromAronType(ice.aronType)); - } - ARMARX_CHECK(!coreSegment.aronType() || ice.aronType); - fromIce(ice.providerSegments, coreSegment.providerSegments()); - } - - void toIce(data::Memory& ice, const wm::Memory& memory) - { - detail::toIceItem(ice, memory); - - toIce(ice.coreSegments, memory.coreSegments()); - } - void fromIce(const data::Memory& ice, wm::Memory& memory) - { - detail::fromIceItem(ice, memory); - - fromIce(ice.coreSegments, memory.coreSegments()); - } - -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h b/source/RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h deleted file mode 100644 index 4d14e6b0d04e9086e701cc38a159e516e533d95c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include <RobotAPI/interface/armem/commit.h> -#include <RobotAPI/interface/armem/memory.h> - -#include "Memory.h" - - -namespace armarx::armem -{ - void toIce(data::EntityInstanceMetadata& ice, const wm::EntityInstanceMetadata& metadata); - void fromIce(const data::EntityInstanceMetadata& ice, wm::EntityInstanceMetadata& metadata); - - void toIce(data::EntityInstance& ice, const wm::EntityInstance& data); - void fromIce(const data::EntityInstance& ice, wm::EntityInstance& data); - - - void toIce(data::EntitySnapshot& ice, const wm::EntitySnapshot& snapshot); - void fromIce(const data::EntitySnapshot& ice, wm::EntitySnapshot& snapshot); - - void toIce(data::Entity& ice, const wm::Entity& entity); - void fromIce(const data::Entity& ice, wm::Entity& entity); - - - void toIce(data::ProviderSegment& ice, const wm::ProviderSegment& providerSegment); - void fromIce(const data::ProviderSegment& ice, wm::ProviderSegment& providerSegment); - - void toIce(data::CoreSegment& ice, const wm::CoreSegment& coreSegment); - void fromIce(const data::CoreSegment& ice, wm::CoreSegment& coreSegment); - - void toIce(data::Memory& ice, const wm::Memory& memory); - void fromIce(const data::Memory& ice, wm::Memory& memory); -} - -// Must be included after the prototypes. Otherwise the compiler cannot find the correct methods in ice_coversion_templates.h -#include "../ice_conversions.h" diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/json_conversions.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/json_conversions.cpp deleted file mode 100644 index e7499d8b42edf895bbbdb2f0d06a1fe3ed2ee823..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/json_conversions.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "json_conversions.h" - -#include <RobotAPI/libraries/aron/core/Debug.h> - -namespace armarx::armem -{ - - void from_aron(const aron::datanavigator::DictNavigatorPtr& aron, nlohmann::json& j) - { - aron::dataIO::writer::NlohmannJSONWriter dataWriter; - aron::dataIO::Visitor::VisitAndSetup(dataWriter, aron); - j = dataWriter.getResult(); - } - - void to_aron(aron::datanavigator::DictNavigatorPtr& a, const nlohmann::json& e, const aron::typenavigator::NavigatorPtr& expectedStructure) - { - aron::dataIO::reader::NlohmannJSONReader dataReader(e); - aron::dataIO::writer::NavigatorWriter navWriter; - aron::dataIO::Converter::ReadAndConvert(dataReader, navWriter, expectedStructure); - a = aron::datanavigator::DictNavigator::DynamicCastAndCheck(navWriter.getResult()); - } -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/json_conversions.h b/source/RobotAPI/libraries/armem/core/workingmemory/json_conversions.h deleted file mode 100644 index d132d88f7d7fb694be8b3dc086c87dcafa276e6a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/json_conversions.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "Memory.h" - -#include <RobotAPI/libraries/aron/core/io/dataIO/converter/Converter.h> -#include <RobotAPI/libraries/aron/core/io/dataIO/visitor/Visitor.h> -#include <RobotAPI/libraries/aron/core/io/dataIO/reader/nlohmannJSON/NlohmannJSONReader.h> -#include <RobotAPI/libraries/aron/core/io/dataIO/writer/nlohmannJSON/NlohmannJSONWriter.h> - - -namespace armarx::armem -{ - void from_aron(const aron::datanavigator::DictNavigatorPtr&, nlohmann::json&); - void to_aron(aron::datanavigator::DictNavigatorPtr&, const nlohmann::json&, const aron::typenavigator::NavigatorPtr& expectedStructure = nullptr); -} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/visitor.h b/source/RobotAPI/libraries/armem/core/workingmemory/visitor.h deleted file mode 100644 index 815ee1b34908dc5916e7d909e709bc735409f63c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/visitor.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "visitor/FunctionalVisitor.h" -#include "visitor/Visitor.h" diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/visitor/Visitor.cpp b/source/RobotAPI/libraries/armem/core/workingmemory/visitor/Visitor.cpp deleted file mode 100644 index ede63a494a7aff26c54ba1d7149200c91be7d987..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/core/workingmemory/visitor/Visitor.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "Visitor.h" - -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/error.h> - - -namespace armarx::armem::wm -{ - - Visitor::Visitor() - { - } - - Visitor::~Visitor() - { - } - - bool Visitor::applyTo(Memory& memory) - { - bool cont = true; - visitEnter(memory); - for (auto& [_, coreSeg] : memory) - { - if (!applyTo(coreSeg)) - { - cont = false; - break; - } - } - visitExit(memory); - return cont; - } - - bool Visitor::applyTo(CoreSegment& coreSegment) - { - bool cont = true; - visitEnter(coreSegment); - for (auto& [_, provSeg] : coreSegment) - { - if (!applyTo(provSeg)) - { - cont = false; - break; - } - } - visitExit(coreSegment); - return cont; - } - - bool Visitor::applyTo(ProviderSegment& providerSegment) - { - bool cont = true; - visitEnter(providerSegment); - for (auto& [_, entity] : providerSegment) - { - if (!applyTo(entity)) - { - cont = false; - break; - } - } - visitExit(providerSegment); - return cont; - } - - bool Visitor::applyTo(Entity& entity) - { - bool cont = true; - visitEnter(entity); - for (auto& [_, snapshot] : entity) - { - if (!applyTo(snapshot)) - { - cont = false; - break; - } - } - visitExit(entity); - return cont; - } - - bool Visitor::applyTo(EntitySnapshot& snapshot) - { - bool cont = true; - visitEnter(snapshot); - for (auto& instance : snapshot) - { - if (!applyTo(instance)) - { - cont = false; - break; - } - } - visitExit(snapshot); - return cont; - } - - bool Visitor::applyTo(EntityInstance& instance) - { - return visit(instance); - } - - - bool Visitor::applyTo(const Memory& memory) - { - bool cont = true; - visitEnter(memory); - for (const auto& [_, coreSeg] : memory) - { - if (!applyTo(coreSeg)) - { - cont = false; - break; - } - } - visitExit(memory); - return cont; - } - - bool Visitor::applyTo(const CoreSegment& coreSegment) - { - bool cont = true; - visitEnter(coreSegment); - for (const auto& [_, provSeg] : coreSegment) - { - if (!applyTo(provSeg)) - { - cont = false; - break; - } - } - visitExit(coreSegment); - return cont; - } - - bool Visitor::applyTo(const ProviderSegment& providerSegment) - { - bool cont = true; - visitEnter(providerSegment); - for (const auto& [_, entity] : providerSegment) - { - if (!applyTo(entity)) - { - cont = false; - break; - } - } - visitExit(providerSegment); - return cont; - } - - bool Visitor::applyTo(const Entity& entity) - { - bool cont = true; - visitEnter(entity); - for (const auto& [_, snapshot] : entity) - { - if (!applyTo(snapshot)) - { - cont = false; - break; - } - } - visitExit(entity); - return cont; - } - - bool Visitor::applyTo(const EntitySnapshot& snapshot) - { - bool cont = true; - visitEnter(snapshot); - for (const auto& instance : snapshot) - { - if (!applyTo(instance)) - { - cont = false; - break; - } - } - visitExit(snapshot); - return cont; - } - - bool Visitor::applyTo(const EntityInstance& instance) - { - return visit(instance); - } - -} diff --git a/source/RobotAPI/libraries/armem/mns/MemoryNameSystem.cpp b/source/RobotAPI/libraries/armem/mns/MemoryNameSystem.cpp index ece0fa74306ed77465ce0bb0c643b781ec704178..6e001784b90104580a49b2510bdb6583fa36783c 100644 --- a/source/RobotAPI/libraries/armem/mns/MemoryNameSystem.cpp +++ b/source/RobotAPI/libraries/armem/mns/MemoryNameSystem.cpp @@ -98,7 +98,6 @@ namespace armarx::armem::mns } catch (const Ice::Exception&) { - ; } } diff --git a/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp b/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp index 8c814a160b49b0367cece67d407b986ac4d372e5..18a24d77f82baf4f07879caeb21bb52b308fdac7 100644 --- a/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp +++ b/source/RobotAPI/libraries/armem/server/ComponentPlugin.cpp @@ -1,13 +1,11 @@ #include "ComponentPlugin.h" -#include <ArmarXCore/core/Component.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include "MemoryToIceAdapter.h" #include <RobotAPI/libraries/armem/core/error.h> -#include "MemoryToIceAdapter.h" - -//#include <RobotAPI/libraries/armem/core/io/diskWriter/NlohmannJSON/NlohmannJSONDiskWriter.h> +#include <ArmarXCore/core/Component.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> namespace armarx::armem::server::plugins @@ -22,8 +20,7 @@ namespace armarx::armem::server::plugins ComponentPluginUser& parent = this->parent<ComponentPluginUser>(); properties->topic(memoryListener, parent.memoryListenerDefaultName); - properties->optional(parent.longtermMemoryEnabled, "memory.ltm.00_enabled"); - parent.longtermMemory.defineProperties(properties, "memory"); + properties->optional(parent.longtermMemoryEnabled, "mem.ltm.00_enabled"); } @@ -40,7 +37,7 @@ namespace armarx::armem::server::plugins // establishing connection to ltm and mongodb if (parent.longtermMemoryEnabled) { - parent.longtermMemory.reload(); + parent.longtermMemoryManager.reload(); } } @@ -116,17 +113,25 @@ namespace armarx::armem::server ComponentPluginUser::~ComponentPluginUser() = default; + // Set the name of a memory + void ComponentPluginUser::setMemoryName(const std::string& memoryName) + { + workingMemory.name() = memoryName; + longtermMemoryManager.setName(memoryName); + } + // WRITING data::AddSegmentsResult ComponentPluginUser::addSegments(const data::AddSegmentsInput& input, const Ice::Current&) { + ARMARX_TRACE; bool addCoreSegmentOnUsage = false; return addSegments(input, addCoreSegmentOnUsage); } data::AddSegmentsResult ComponentPluginUser::addSegments(const data::AddSegmentsInput& input, bool addCoreSegments) { - // std::scoped_lock lock(workingMemoryMutex); + ARMARX_TRACE; data::AddSegmentsResult result = iceMemory.addSegments(input, addCoreSegments); return result; } @@ -134,7 +139,7 @@ namespace armarx::armem::server data::CommitResult ComponentPluginUser::commit(const data::Commit& commitIce, const Ice::Current&) { - // std::scoped_lock lock(workingMemoryMutex); + ARMARX_TRACE; return iceMemory.commit(commitIce); } @@ -142,7 +147,7 @@ namespace armarx::armem::server // READING armem::query::data::Result ComponentPluginUser::query(const armem::query::data::Input& input, const Ice::Current&) { - // std::scoped_lock lock(workingMemoryMutex); + ARMARX_TRACE; return iceMemory.query(input); } @@ -150,16 +155,11 @@ namespace armarx::armem::server // LTM STORING data::StoreResult ComponentPluginUser::store(const data::StoreInput& input, const Ice::Current&) { - std::scoped_lock lock(/*workingMemoryMutex,*/ longtermMemoryMutex); + ARMARX_TRACE; return iceMemory.store(input); } // LTM LOADING - armem::query::data::Result ComponentPluginUser::load(const armem::query::data::Input& input, const Ice::Current&) - { - std::scoped_lock lock(longtermMemoryMutex); - return iceMemory.load(input); - } } diff --git a/source/RobotAPI/libraries/armem/server/ComponentPlugin.h b/source/RobotAPI/libraries/armem/server/ComponentPlugin.h index 06ba31f7a128549f51be85de300a92bdd310cf95..f55bfc6c024922f10b84d76a52b21135db58e928 100644 --- a/source/RobotAPI/libraries/armem/server/ComponentPlugin.h +++ b/source/RobotAPI/libraries/armem/server/ComponentPlugin.h @@ -8,8 +8,8 @@ #include <RobotAPI/interface/armem/client/MemoryListenerInterface.h> #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h> #include <RobotAPI/libraries/armem/client/MemoryNameSystemComponentPlugin.h> #include "MemoryToIceAdapter.h" @@ -75,6 +75,9 @@ namespace armarx::armem::server ComponentPluginUser(); virtual ~ComponentPluginUser() override; + /// Set the name of the wm and the ltm (if enabled) + void setMemoryName(const std::string& memoryName); + // WritingInterface interface virtual data::AddSegmentsResult addSegments(const data::AddSegmentsInput& input, const Ice::Current& = Ice::emptyCurrent) override; @@ -92,25 +95,26 @@ namespace armarx::armem::server // LoadingInterface interface - virtual armem::query::data::Result load(const armem::query::data::Input&, const Ice::Current& = Ice::emptyCurrent) override; public: /// The actual memory. - wm::Memory workingMemory; + server::wm::Memory workingMemory; // [[deprecated ("The global working memory mutex is deprecated. Use the core segment mutexes instead.")]] // std::mutex workingMemoryMutex; + /// Parameter to indicate whether to use or not to use the ltm feature bool longtermMemoryEnabled = true; - ltm::Memory longtermMemory; - std::mutex longtermMemoryMutex; + + /// A manager class for the ltm. It internally holds a normal wm instance + server::ltm::mongodb::MemoryManager longtermMemoryManager; /// property defaults std::string memoryListenerDefaultName = "MemoryUpdates"; /// Helps connecting `memory` to ice. Used to handle Ice callbacks. - MemoryToIceAdapter iceMemory { &workingMemory, &longtermMemory}; + MemoryToIceAdapter iceMemory { &workingMemory, &longtermMemoryManager}; private: diff --git a/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.cpp b/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.cpp index 9347cb7e9234723d33407693cb68ec1f5ea8448d..57b495a527d07924176314edc70fbfa1c53f1cae 100644 --- a/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.cpp +++ b/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.cpp @@ -1,106 +1,116 @@ #include "MemoryRemoteGui.h" -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include "RemoteGuiAronDataVisitor.h" #include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h> +#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> #include <RobotAPI/libraries/aron/core/navigator/visitors/DataVisitor.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + #include <SimoxUtility/meta/type_name.h> -#include "RemoteGuiAronDataVisitor.h" +#include <mutex> namespace armarx::armem::server { - MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const wm::Memory& memory) const + template <class ...Args> + MemoryRemoteGui::GroupBox MemoryRemoteGui::_makeGroupBox(const armem::base::MemoryBase<Args...>& memory) const { GroupBox group; - group.setLabel(makeGroupLabel("Memory", memory.name(), memory.coreSegments().size())); + group.setLabel(makeGroupLabel("Memory", memory.name(), memory.size())); - if (memory.coreSegments().empty()) + if (memory.empty()) { group.addChild(Label(makeNoItemsMessage("core segments"))); } - for (const auto& [name, coreSegment] : memory.coreSegments()) + memory.forEachCoreSegment([this, &group](const auto & coreSegment) { - group.addChild(makeGroupBox(coreSegment)); - } - + group.addChild(this->makeGroupBox(coreSegment)); + }); return group; } - MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const wm::CoreSegment& coreSegment) const + static std::string getTypeString(const armem::base::detail::AronTyped& typed) { - std::scoped_lock lock(coreSegment.mutex()); + std::stringstream type; + if (typed.aronType()) + { + type << " (" << typed.aronType()->getName() << ")"; + } + else + { + type << " (no Aron type)"; + } + return type.str(); + } + template <class ...Args> + MemoryRemoteGui::GroupBox MemoryRemoteGui::_makeGroupBox(const armem::base::CoreSegmentBase<Args...>& coreSegment) const + { GroupBox group; - group.setLabel(makeGroupLabel("Core Segment", coreSegment.name(), coreSegment.providerSegments().size())); + group.setLabel(makeGroupLabel("Core Segment", coreSegment.name(), coreSegment.size()) + + getTypeString(coreSegment)); - if (coreSegment.providerSegments().empty()) + if (coreSegment.empty()) { group.addChild(Label(makeNoItemsMessage("provider segments"))); } - for (const auto& [name, providerSegment] : coreSegment.providerSegments()) + coreSegment.forEachProviderSegment([this, &group](const auto & providerSegment) { - group.addChild(makeGroupBox(providerSegment)); - } - + group.addChild(this->makeGroupBox(providerSegment)); + }); return group; } - MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const wm::ProviderSegment& providerSegment) const + template <class ...Args> + MemoryRemoteGui::GroupBox MemoryRemoteGui::_makeGroupBox(const armem::base::ProviderSegmentBase<Args...>& providerSegment) const { GroupBox group; - group.setLabel(makeGroupLabel("Provider Segment", providerSegment.name(), providerSegment.entities().size())); + group.setLabel(makeGroupLabel("Provider Segment", providerSegment.name(), providerSegment.size()) + + getTypeString(providerSegment)); - if (providerSegment.entities().empty()) + if (providerSegment.empty()) { group.addChild(Label(makeNoItemsMessage("entities"))); } - for (const auto& [name, entity] : providerSegment.entities()) + providerSegment.forEachEntity([this, &group](const auto & entity) { - group.addChild(makeGroupBox(entity)); - } - + group.addChild(this->makeGroupBox(entity)); + }); return group; } - MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const wm::Entity& entity) const + template <class ...Args> + MemoryRemoteGui::GroupBox MemoryRemoteGui::_makeGroupBox(const armem::base::EntityBase<Args...>& entity) const { GroupBox group; - group.setLabel(makeGroupLabel("Entity", entity.name(), entity.history().size())); + group.setLabel(makeGroupLabel("Entity", entity.name(), entity.size())); - if (entity.history().empty()) + if (entity.empty()) { group.addChild(Label(makeNoItemsMessage("snapshots"))); } - if (int(entity.history().size()) <= maxHistorySize) + + auto addChild = [this, &group](const armem::wm::EntitySnapshot & snapshot) + { + group.addChild(makeGroupBox(snapshot)); + }; + + if (int(entity.size()) <= maxHistorySize) { - for (const auto& [time, snapshot] : entity.history()) - { - group.addChild(makeGroupBox(snapshot)); - } + entity.forEachSnapshot(addChild); } else { - int margin = 2; - auto it = entity.history().begin(); - auto rit = entity.history().end(); - --rit; - for (int i = 0; i < margin; ++i, ++it) - { - group.addChild(makeGroupBox(it->second)); - --rit; - } - group.addChild(Label("...")); - for (; rit != entity.history().end(); ++rit) - { - group.addChild(makeGroupBox(rit->second)); - } + const int margin = 2; + entity.forEachSnapshotInIndexRange(0, margin, addChild); + entity.forEachSnapshotInIndexRange(-margin, -1, addChild); } group.setCollapsed(true); @@ -108,27 +118,78 @@ namespace armarx::armem::server } - MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const wm::EntitySnapshot& snapshot) const + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::server::wm::Memory& memory) const + { + return this->_makeGroupBox(memory); + } + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::wm::Memory& memory) const + { + return this->_makeGroupBox(memory); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::server::wm::CoreSegment& coreSegment) const + { + return coreSegment.doLocked([this, &coreSegment]() + { + return this->_makeGroupBox(coreSegment); + }); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::wm::CoreSegment& coreSegment) const + { + return this->_makeGroupBox(coreSegment); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::server::wm::ProviderSegment& providerSegment) const + { + return this->_makeGroupBox(providerSegment); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::wm::ProviderSegment& providerSegment) const + { + return this->_makeGroupBox(providerSegment); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::wm::Entity& entity) const + { + return this->_makeGroupBox(entity); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::server::wm::Entity& entity) const + { + return this->_makeGroupBox(entity); + } + + + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::wm::EntitySnapshot& snapshot) const { GroupBox group; group.setLabel(makeGroupLabel("t", armem::toDateTimeMilliSeconds(snapshot.time()), - snapshot.instances().size(), " = ", "")); + snapshot.size(), " = ", "")); - if (snapshot.instances().empty()) + if (snapshot.empty()) { group.addChild(Label(makeNoItemsMessage("instances"))); } - for (const wm::EntityInstance& instance : snapshot.instances()) + snapshot.forEachInstance([this, &group](const armem::wm::EntityInstance & instance) { group.addChild(makeGroupBox(instance)); - } + return true; + }); group.setCollapsed(true); return group; } - MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const wm::EntityInstance& instance) const + MemoryRemoteGui::GroupBox MemoryRemoteGui::makeGroupBox(const armem::wm::EntityInstance& instance) const { GroupBox group; diff --git a/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.h b/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.h index f4a7fad95f1efa38d8ad7044d661e77fef60be31..c6aef5c51546ac203c50d38f80696b89f3b6f3b9 100644 --- a/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.h +++ b/source/RobotAPI/libraries/armem/server/MemoryRemoteGui.h @@ -2,7 +2,7 @@ #include <ArmarXGui/libraries/RemoteGui/Client/Widgets.h> -#include "../core/workingmemory/Memory.h" +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> namespace armarx::armem::server @@ -17,12 +17,18 @@ namespace armarx::armem::server using GroupBox = armarx::RemoteGui::Client::GroupBox; using Label = armarx::RemoteGui::Client::Label; - GroupBox makeGroupBox(const wm::Memory& memory) const; - GroupBox makeGroupBox(const wm::CoreSegment& coreSegment) const; - GroupBox makeGroupBox(const wm::ProviderSegment& providerSegment) const; - GroupBox makeGroupBox(const wm::Entity& entity) const; - GroupBox makeGroupBox(const wm::EntitySnapshot& entitySnapshot) const; - GroupBox makeGroupBox(const wm::EntityInstance& instance) const; + + GroupBox makeGroupBox(const armem::wm::Memory& memory) const; + GroupBox makeGroupBox(const armem::wm::CoreSegment& coreSegment) const; + GroupBox makeGroupBox(const armem::wm::ProviderSegment& providerSegment) const; + GroupBox makeGroupBox(const armem::wm::Entity& entity) const; + GroupBox makeGroupBox(const armem::wm::EntitySnapshot& entitySnapshot) const; + GroupBox makeGroupBox(const armem::wm::EntityInstance& instance) const; + + GroupBox makeGroupBox(const armem::server::wm::Memory& memory) const; + GroupBox makeGroupBox(const armem::server::wm::CoreSegment& coreSegment) const; + GroupBox makeGroupBox(const armem::server::wm::ProviderSegment& providerSegment) const; + GroupBox makeGroupBox(const armem::server::wm::Entity& entity) const; std::string makeGroupLabel(const std::string& term, const std::string& name, size_t size, @@ -33,6 +39,17 @@ namespace armarx::armem::server int maxHistorySize = 10; + private: + + template <class ...Args> + MemoryRemoteGui::GroupBox _makeGroupBox(const armem::base::MemoryBase<Args...>& memory) const; + template <class ...Args> + MemoryRemoteGui::GroupBox _makeGroupBox(const armem::base::CoreSegmentBase<Args...>& coreSegment) const; + template <class ...Args> + MemoryRemoteGui::GroupBox _makeGroupBox(const armem::base::ProviderSegmentBase<Args...>& providerSegment) const; + template <class ...Args> + MemoryRemoteGui::GroupBox _makeGroupBox(const armem::base::EntityBase<Args...>& entity) const; + }; diff --git a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp index 65bd2f6429c6fb2c25f48d2fa1218bc5fa308409..73d00c3a370fd14e40771df77d0d96cc6b9dbf42 100644 --- a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp +++ b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.cpp @@ -1,19 +1,24 @@ #include "MemoryToIceAdapter.h" -#include "ArmarXCore/core/logging/Logging.h" -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include "query_proc/wm.h" +#include "query_proc/ltm.h" + +#include <RobotAPI/libraries/armem/server/wm/ice_conversions.h> + +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> -#include "../error.h" -#include "../core/workingmemory/ice_conversions.h" -#include "query_proc/workingmemory/MemoryQueryProcessor.h" -#include "query_proc/longtermmemory/MemoryQueryProcessor.h" +#include <RobotAPI/libraries/aron/core/Exception.h> + +#include <ArmarXCore/core/logging/Logging.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> namespace armarx::armem::server { - MemoryToIceAdapter::MemoryToIceAdapter(wm::Memory* workingMemory, ltm::Memory* longtermMemory) : - workingMemory(workingMemory), longtermMemory(longtermMemory) + MemoryToIceAdapter::MemoryToIceAdapter(wm::Memory* workingMemory, server::ltm::mongodb::MemoryManager* longtermMemory) : + workingMemory(workingMemory), longtermMemoryManager(longtermMemory) { } @@ -29,11 +34,12 @@ namespace armarx::armem::server data::AddSegmentResult MemoryToIceAdapter::addSegment(const data::AddSegmentInput& input, bool addCoreSegments) { + ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(workingMemory); data::AddSegmentResult output; - armem::wm::CoreSegment* coreSegment = nullptr; + server::wm::CoreSegment* coreSegment = nullptr; try { coreSegment = &workingMemory->getCoreSegment(input.coreSegmentName); @@ -55,20 +61,22 @@ namespace armarx::armem::server if (input.providerSegmentName.size() > 0) { - std::scoped_lock lock(coreSegment->mutex()); - try + coreSegment->doLocked([&coreSegment, &input]() { - coreSegment->addProviderSegment(input.providerSegmentName); - } - catch (const armem::error::ContainerEntryAlreadyExists&) - { - // This is ok. - if (input.clearWhenExists) + try { - wm::ProviderSegment& provider = coreSegment->getProviderSegment(input.providerSegmentName); - provider.clear(); + coreSegment->addProviderSegment(input.providerSegmentName); } - } + catch (const armem::error::ContainerEntryAlreadyExists&) + { + // This is ok. + if (input.clearWhenExists) + { + server::wm::ProviderSegment& provider = coreSegment->getProviderSegment(input.providerSegmentName); + provider.clear(); + } + } + }); } armem::MemoryID segmentID; @@ -85,6 +93,7 @@ namespace armarx::armem::server data::AddSegmentsResult MemoryToIceAdapter::addSegments(const data::AddSegmentsInput& input, bool addCoreSegments) { + ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(workingMemory); data::AddSegmentsResult output; @@ -99,6 +108,7 @@ namespace armarx::armem::server data::CommitResult MemoryToIceAdapter::commit(const data::Commit& commitIce, Time timeArrived) { + ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(workingMemory); auto handleException = [](const std::string & what) { @@ -136,6 +146,7 @@ namespace armarx::armem::server data::CommitResult MemoryToIceAdapter::commit(const data::Commit& commitIce) { + ARMARX_TRACE; return commit(commitIce, armem::Time::now()); } @@ -143,18 +154,21 @@ namespace armarx::armem::server armem::CommitResult MemoryToIceAdapter::commit(const armem::Commit& commit) { + ARMARX_TRACE; return this->_commit(commit, false); } armem::CommitResult MemoryToIceAdapter::commitLocking(const armem::Commit& commit) { + ARMARX_TRACE; return this->_commit(commit, true); } armem::CommitResult MemoryToIceAdapter::_commit(const armem::Commit& commit, bool locking) { + ARMARX_TRACE; std::vector<data::MemoryID> updatedIDs; const bool publishUpdates = bool(memoryListenerTopic); @@ -174,7 +188,7 @@ namespace armarx::armem::server // also store in ltm if transfermode is set to always // TODO: Move outside of loop? - if (longtermMemory) + if (longtermMemoryManager) { } @@ -221,34 +235,26 @@ namespace armarx::armem::server armem::query::data::Result MemoryToIceAdapter::query(const armem::query::data::Input& input) { + ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(workingMemory); // Core segment processors will aquire the core segment locks. - armem::wm::query_proc::MemoryQueryProcessor wmProcessor( + query_proc::wm_server::MemoryQueryProcessor wmServerProcessor( input.withData ? armem::DataMode::WithData : armem::DataMode::NoData); - wm::Memory wmResult = wmProcessor.process(input, *workingMemory); + armem::wm::Memory wmResult = wmServerProcessor.process(input.memoryQueries, *workingMemory); - armem::ltm::query_proc::MemoryQueryProcessor ltmProcessor; - ltm::Memory ltmResult = ltmProcessor.process(input, *longtermMemory); + query_proc::ltm::MemoryQueryProcessor ltmProcessor; + armem::wm::Memory ltmResult = ltmProcessor.process(input, longtermMemoryManager->getCacheAndLutNotConverted()); armem::query::data::Result result; - if (ltmResult.hasData()) + if (not ltmResult.empty()) { ARMARX_INFO << "The LTM returned data after query"; - // ATTENTION: This code block moves data from LTM back into WM. - // However, since some segments are constrained, the WM might send data back to LTM. - // This may also affect the data returned by the current query. - // However, this is expected behavior, since we copy the data in the processor (copyEmpty) we can safely return the copy and - // remove the original memory reference from WM here. - wm::Memory ltmConverted = ltmResult.convert(); - if (!ltmConverted.hasData()) - { - ARMARX_ERROR << "A converted memory contains no data although the original memory contained data. This indicates that something is wrong."; - } + longtermMemoryManager->convert(ltmResult); // convert memory ==> meaning resolving lut references to e.g. mongodb - wmResult.append(ltmConverted); - if (!wmResult.hasData()) + wmResult.append(ltmResult); + if (wmResult.empty()) { ARMARX_ERROR << "A merged Memory has no data although at least the LTM result contains data. This indicates that something is wrong."; } @@ -257,16 +263,18 @@ namespace armarx::armem::server auto queryInput = armem::client::QueryInput::fromIce(input); queryInput.replaceQueryTarget(query::data::QueryTarget::LTM, query::data::QueryTarget::WM); - wm::Memory merged_result = wmProcessor.process(queryInput.toIce(), wmResult); - if (!merged_result.hasData()) + query_proc::wm::MemoryQueryProcessor wm2wmProcessor( + input.withData ? armem::DataMode::WithData : armem::DataMode::NoData); + armem::wm::Memory mergedResult = wm2wmProcessor.process(queryInput.toIce(), wmResult); + if (mergedResult.empty()) { ARMARX_ERROR << "A merged and postprocessed Memory has no data although at least the LTM result contains data. This indicates that something is wrong."; } - result.memory = toIce<data::MemoryPtr>(merged_result); + result.memory = toIce<data::MemoryPtr>(mergedResult); // also move results of ltm to wm - //this->commit(ltm_converted.toCommit()); + //this->commit(toCommit(ltm_converted)); // mark removed entries of wm in viewer // TODO @@ -289,26 +297,19 @@ namespace armarx::armem::server client::QueryResult MemoryToIceAdapter::query(const client::QueryInput& input) { + ARMARX_TRACE; return client::QueryResult::fromIce(query(input.toIce())); } // LTM LOADING FROM LTM - query::data::Result MemoryToIceAdapter::load(const armem::query::data::Input& query) - { - ARMARX_CHECK_NOT_NULL(longtermMemory); - query::data::Result output; - - output.success = true; - return output; - } - // LTM STORING data::StoreResult MemoryToIceAdapter::store(const armem::data::StoreInput& input) { + ARMARX_TRACE; ARMARX_CHECK_NOT_NULL(workingMemory); - ARMARX_CHECK_NOT_NULL(longtermMemory); + ARMARX_CHECK_NOT_NULL(longtermMemoryManager); data::StoreResult output; for (const auto& query : input.query.memoryQueries) @@ -323,16 +324,12 @@ namespace armarx::armem::server if (queryResult.success) { - wm::Memory m; + armem::wm::Memory m; fromIce(queryResult.memory, m); - longtermMemory->append(m); + longtermMemoryManager->append(m); } return output; } - - - - } diff --git a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h index f4b4b4c94eac3a51cb8c90e2fcabfc09288e3ead..f924399c7eb749f8404f28c1c2a9bd7945112a7c 100644 --- a/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h +++ b/source/RobotAPI/libraries/armem/server/MemoryToIceAdapter.h @@ -3,15 +3,15 @@ #include <RobotAPI/interface/armem/server/MemoryInterface.h> #include <RobotAPI/interface/armem/client/MemoryListenerInterface.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h> #include <RobotAPI/libraries/armem/client/Query.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> namespace armarx::armem::server { - /** * @brief Helps connecting a Memory server to the Ice interface. * @@ -23,7 +23,8 @@ namespace armarx::armem::server public: /// Construct an MemoryToIceAdapter from an existing Memory. - MemoryToIceAdapter(wm::Memory* workingMemory = nullptr, ltm::Memory* longtermMemory = nullptr); + MemoryToIceAdapter(server::wm::Memory* workingMemory = nullptr, + server::ltm::mongodb::MemoryManager* longtermMemory = nullptr); void setMemoryListener(client::MemoryListenerInterfacePrx memoryListenerTopic); @@ -47,15 +48,14 @@ namespace armarx::armem::server client::QueryResult query(const client::QueryInput& input); // LTM LOADING - query::data::Result load(const armem::query::data::Input& input); // LTM STORING data::StoreResult store(const armem::data::StoreInput& input); public: - wm::Memory* workingMemory; - ltm::Memory* longtermMemory; + server::wm::Memory* workingMemory; + server::ltm::mongodb::MemoryManager* longtermMemoryManager; client::MemoryListenerInterfacePrx memoryListenerTopic; diff --git a/source/RobotAPI/libraries/armem/server/forward_declarations.h b/source/RobotAPI/libraries/armem/server/forward_declarations.h new file mode 100644 index 0000000000000000000000000000000000000000..6872e2584352195ca8e587043e275acfbffb3739 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/forward_declarations.h @@ -0,0 +1,18 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/forward_declarations.h> + + +namespace armarx::armem::server +{ + class MemoryToIceAdapter; +} +namespace armarx::armem::server::wm +{ + using EntityInstance = armem::wm::EntityInstance; + using EntitySnapshot = armem::wm::EntitySnapshot; + class Entity; + class ProviderSegment; + class CoreSegment; + class Memory; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e8c64d64121eccc007cad7ba2d55c2e361b94e2 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.cpp @@ -0,0 +1,134 @@ +// Header +#include "LongtermMemoryBase.h" + +// ArmarX +#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/logging/Logging.h> + + +namespace armarx::armem::server::ltm +{ + void LongtermMemoryBase::setName(const std::string& name) + { + cache.name() = name; + lut.name() = name; + } + + armem::wm::Memory LongtermMemoryBase::getCacheAndLutNotConverted() const + { + std::lock_guard l(cache_mutex); + std::lock_guard l2(lut_mutex); + + armem::wm::Memory m(lut.name()); + m.append(cache); + m.append(lut); + + // debug output + //lut.forEachSnapshot([](const auto & e) + //{ + // ARMARX_INFO << "The SNapshot: " << e.id().str() << " has size: " << e.size(); + //}); + + return m; + } + + void LongtermMemoryBase::append(const armem::wm::Memory& m) + { + TIMING_START(LTM_Append); + ARMARX_INFO << "Append memory with name '" << m.name() << "' into the LTM with name '" << cache.name() << "'"; + + std::lock_guard l(cache_mutex); + cache.append(m); + + encodeAndStore(); + + TIMING_END(LTM_Append); + } + + void LongtermMemoryBase::checkUpdateLatestSnapshot(const armem::wm::EntitySnapshot& newSnapshot) + { + // update map of latestSnapshots + if (auto it = latestSnapshots.find(newSnapshot.id().getEntityID().str()); it != latestSnapshots.end()) + { + auto ptr = it->second; + if (ptr->id().timestamp > newSnapshot.id().timestamp) + { + ptr = &newSnapshot; + } + // else ignore ==> no update + } + else + { + // no entry yet + latestSnapshots.emplace(newSnapshot.id().getEntityID().str(), &newSnapshot); + } + } + + bool LongtermMemoryBase::containsCoreSegment(const MemoryID& coreSegmentID) const + { + //ARMARX_INFO << "Check if lut has core seg"; + if (lut.hasCoreSegment(coreSegmentID.coreSegmentName)) + { + //ARMARX_INFO << "lus has core seg"; + return true; + } + return false; + } + + bool LongtermMemoryBase::containsProviderSegment(const MemoryID& providerSegmentID) const + { + //ARMARX_INFO << "Check if lut has prov seg"; + if (lut.hasCoreSegment(providerSegmentID.coreSegmentName)) + { + auto core = lut.getCoreSegment(providerSegmentID.coreSegmentName); + if (core.hasProviderSegment(providerSegmentID.providerSegmentName)) + { + //ARMARX_INFO << "lus has prov seg"; + return true; + } + } + return false; + } + + bool LongtermMemoryBase::containsEntity(const MemoryID& entityID) const + { + //ARMARX_INFO << "Check if lut has entity"; + if (lut.hasCoreSegment(entityID.coreSegmentName)) + { + auto core = lut.getCoreSegment(entityID.coreSegmentName); + if (core.hasProviderSegment(entityID.providerSegmentName)) + { + auto prov = core.getProviderSegment(entityID.providerSegmentName); + if (prov.hasEntity(entityID.entityName)) + { + //ARMARX_INFO << "lus has entity"; + return true; + } + } + } + return false; + } + + bool LongtermMemoryBase::containsSnapshot(const MemoryID& snapshotID) const + { + //ARMARX_INFO << "Check if lut has snapshot"; + if (lut.hasCoreSegment(snapshotID.coreSegmentName)) + { + auto core = lut.getCoreSegment(snapshotID.coreSegmentName); + if (core.hasProviderSegment(snapshotID.providerSegmentName)) + { + auto prov = core.getProviderSegment(snapshotID.providerSegmentName); + if (prov.hasEntity(snapshotID.entityName)) + { + auto entity = prov.getEntity(snapshotID.entityName); + if (entity.hasSnapshot(snapshotID.timestamp)) + { + //ARMARX_INFO << "lut has snapshot"; + return true; + } + } + } + } + return false; + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h new file mode 100644 index 0000000000000000000000000000000000000000..097f951548cc777fa03cedbc5f425e867439cd48 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h @@ -0,0 +1,65 @@ +#pragma once + +// STD / STL +#include <optional> +#include <mutex> + +// Memory +#include "../../core/wm/memory_definitions.h" + +namespace armarx::armem::server::ltm +{ + /// @brief Interface functions for the longterm memory classes + class LongtermMemoryBase + { + public: + struct AppendResult + { + std::vector<MemoryID> addedCoreSegments; + std::vector<MemoryID> addedProviderSegments; + std::vector<MemoryID> addedEntities; + + std::vector<MemoryID> addedSnapshots; + std::vector<MemoryID> replacedSnapshots; + std::vector<MemoryID> ignoredSnapshots; + }; + + struct ReloadResult + { + + }; + + void append(const armem::wm::Memory&); + + virtual void reload() = 0; + virtual void convert(armem::wm::Memory&) = 0; + virtual void encodeAndStore() = 0; + + // pass through to internal memory + void setName(const std::string& name); + + // get merged internal memory + armem::wm::Memory getCacheAndLutNotConverted() const; + + protected: + void checkUpdateLatestSnapshot(const armem::wm::EntitySnapshot& newSnapshot); + + bool containsCoreSegment(const MemoryID&) const; + bool containsProviderSegment(const MemoryID&) const; + bool containsEntity(const MemoryID&) const; + bool containsSnapshot(const MemoryID&) const; + + protected: + /// Internal memory for data consolidated from wm to ltm (cache) + armem::wm::Memory cache; + mutable std::recursive_mutex cache_mutex; + + /// Internal memory for indexes (lut) + armem::wm::Memory lut; + mutable std::recursive_mutex lut_mutex; + + /// A map from entityID to its latest snapshot stored. When adding a new snapshot we compare it to the last one stored. + std::map<std::string, const armem::wm::EntitySnapshot*> latestSnapshots; + + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b2360b5c6580ea37bbbbb6765817e998e910c302 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.cpp @@ -0,0 +1,402 @@ +// 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> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> + +namespace +{ + // check whether a string is a number (timestamp folders) + bool isNumber(const std::string& s) + { + for (char const& ch : s) + { + if (std::isdigit(ch) == 0) + { + return false; + } + } + return true; + } +} + +namespace armarx::armem::server::ltm::disk +{ + namespace fs = std::filesystem; + + bool MemoryManager::checkPath() const + { + // Check connection: + ARMARX_INFO << "Checking Path"; + if (!fs::exists(basePathToMemory) || !fs::is_directory(basePathToMemory) || basePathToMemory.filename() != lut.name()) + { + ARMARX_WARNING << deactivateSpam("PathIsNotValid") + << "The entered path is not valid. Please use a path leading to a memory folder with name: " << lut.name() << "." + << "\n\n"; + return false; + } + + return true; + } + + void MemoryManager::reload() + { + TIMING_START(LTM_Reload); + ARMARX_INFO << "(Re)Loading a memory from: " << basePathToMemory.string(); + + if (!checkPath()) + { + // abort + ARMARX_WARNING << "Could not (pre)load a memory from the filesystem."; + return; + } + + armem::wm::Memory temp(lut.id()); // a temporary client wm. We will append temp to the lut at the end of this metho (append ignores duplicate entries) + ARMARX_INFO << "Loading memory: " << temp.id().str(); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + for (const auto& d : std::filesystem::directory_iterator(basePathToMemory)) + // Although this looks like code duplication, we need a distinguition between memories, + // core, prov and entities because of a different structure + // (only core and prov have same structure) + { + + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (temp.hasCoreSegment(k)) + { + throw error::ArMemError("Somehow the (memory) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over core segments + // /////////////////////////////// + auto& cSeg = temp.addCoreSegment(k); + for (const auto& d : std::filesystem::directory_iterator(d)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (cSeg.hasProviderSegment(k)) + { + throw error::ArMemError("Somehow the (core) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over provider segments + // /////////////////////////////// + auto& pSeg = cSeg.addProviderSegment(k); + for (const auto& d : std::filesystem::directory_iterator(d)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (pSeg.hasEntity(k)) + { + throw error::ArMemError("Somehow the (provider) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over entities + // /////////////////////////////// + auto& eSeg = pSeg.addEntity(k); + for (const auto& d : std::filesystem::directory_iterator(d)) + { + if (!d.is_directory()) + { + continue; + } + + std::string k = d.path().filename(); + if (!isNumber(k)) + { + continue; + } + + auto ts = armem::Time::microSeconds(std::stol(k)); + // TODO catch exceptions? + + if (eSeg.hasSnapshot(ts)) + { + throw error::ArMemError("Somehow the (entity) container already contains the key k = " + k + ". This should not happen."); + } + + // /////////////////////////////// + // Add and iterate over entities + // /////////////////////////////// + auto& sSeg = eSeg.addSnapshot(ts); + for (unsigned int i = 0; i < 10000; ++i) // ugly workaround to get the folders in the correct order + { + fs::path p = d / std::to_string(i); + if (!fs::exists(p) || !fs::is_directory(p)) + { + // early stopping + break; + } + + fs::path data = p / DATA_FILENAME; + if (!fs::exists(data) || !fs::is_regular_file(data)) + { + // do not set data + continue; + } + + // else we have an instance + /*std::ifstream ifs(data); + std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); + + nlohmann::json j = nlohmann::json::parse(file_content); + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSON(j);*/ + + sSeg.addInstance(); + //from_aron(aron, instance); + } + } + } + } + } + + std::lock_guard l(cache_mutex); // we always take the cache mutex BEFORE the lut mutex! (otherwise we may have deadlocks) + std::lock_guard l2(lut_mutex); + lut.append(temp); + ARMARX_INFO << "After reload memory " << lut.id().str() << " has size: " << lut.size(); + + TIMING_END(LTM_Reload); + } + + void MemoryManager::convert(armem::wm::Memory& m) + { + TIMING_START(LTM_Convert); + if (!checkPath()) + { + // abort + ARMARX_WARNING << "Could not convert a memory from the filesystem."; + return; + } + + // update emtpy data ptr + m.forEachCoreSegment([this](armem::wm::CoreSegment & e) + { + e.forEachProviderSegment([this](armem::wm::ProviderSegment & e) + { + e.forEachEntity([this](armem::wm::Entity & e) + { + e.forEachSnapshot([this](armem::wm::EntitySnapshot & e) + { + // check whether data is nullptr + bool allDataIsNull = e.size() > 0; + e.forEachInstance([&allDataIsNull](armem::wm::EntityInstance & e) + { + if (e.data()) + { + allDataIsNull = false; + return false; // means break + } + return true; + }); + + if (allDataIsNull) // an entry from the lut (probably... for now we assume that every entry either has data (cache) or has null (lut)) + { + // Get data from mongodb + auto p = basePathToMemory / e.id().coreSegmentName / e.id().providerSegmentName / e.id().entityName / std::to_string(e.id().timestamp.toMicroSeconds()); + + if (fs::exists(p) && fs::is_directory(p)) + { + for (unsigned int i = 0; i < e.size(); ++i) + { + auto data = p / std::to_string(i) / DATA_FILENAME; + + if (fs::exists(data) && fs::is_regular_file(data)) + { + auto& ins = e.getInstance(i); + + std::ifstream ifs(data); + std::string file_content((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); + nlohmann::json doc = nlohmann::json::parse(file_content); + + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSON(doc); + + wm::EntityInstance tmp(e.id().withInstanceIndex(i)); + from_aron(aron, tmp); + + ins.data() = tmp.data(); + } + } + } + // else leave snapshot untouched + } + }); + }); + }); + }); + TIMING_END(LTM_Convert); + } + + void MemoryManager::encodeAndStore() + { + TIMING_START(LTM_Encode); + if (!checkPath()) + { + // abort + ARMARX_WARNING << "Could not (pre)load a memory from the filesystem."; + return; + } + + std::lock_guard l(cache_mutex); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + fs::path mPath = basePathToMemory; + cache.forEachCoreSegment([&mPath](armem::wm::CoreSegment & e) + { + fs::path cPath = mPath / e.id().coreSegmentName; + if (!fs::exists(cPath)) + { + // not found + fs::create_directory(cPath); + } + if (!fs::is_directory(cPath)) + { + throw error::ArMemError("Could not create the (core) folder: " + cPath.string()); + } + + // /////////////////////////////// + // Iterate over provider segments + // /////////////////////////////// + e.forEachProviderSegment([&cPath](armem::wm::ProviderSegment & e) + { + fs::path pPath = cPath / e.id().providerSegmentName; + if (!fs::exists(pPath)) + { + // not found + fs::create_directory(pPath); + } + if (!fs::is_directory(pPath)) + { + throw error::ArMemError("Could not create the (provider) folder: " + pPath.string()); + } + + // /////////////////////////////// + // Iterate over entities + // /////////////////////////////// + e.forEachEntity([&pPath](armem::wm::Entity & e) + { + fs::path ePath = pPath / e.id().entityName; + if (!fs::exists(ePath)) + { + // not found + fs::create_directory(ePath); + } + if (!fs::is_directory(ePath)) + { + throw error::ArMemError("Could not create the (entity) folder: " + ePath.string()); + } + + // /////////////////////////////// + // Iterate over snapshots + // /////////////////////////////// + e.forEachSnapshot([&ePath](armem::wm::EntitySnapshot & e) + { + fs::path sPath = ePath / std::to_string(e.id().timestamp.toMicroSeconds()); + if (!fs::exists(sPath)) + { + // not found + fs::create_directory(sPath); + } + else + { + ARMARX_INFO << "The snapshot " << sPath.string() << " already exists. Ingoring it."; + return; // continue if already exists + } + + if (!fs::is_directory(sPath)) + { + throw error::ArMemError("Could not create the (timestamp) folder: " + sPath.string()); + } + + // /////////////////////////////// + // Iterate over instances + // /////////////////////////////// + e.forEachInstance([&sPath](armem::wm::EntityInstance & e) + { + fs::path iPath = sPath / std::to_string(e.id().instanceIndex); + if (!fs::exists(iPath)) + { + // not found + fs::create_directory(iPath); + } + else + { + // This is strange, since we know, that the snapshot folder did not exist. + // However, we ignore and continue + ARMARX_INFO << "Somehow the instance folder " << iPath.string() << " already exists, although the snapshot folder was newly created. Ignore this."; + return; + } + if (!fs::is_directory(iPath)) + { + throw error::ArMemError("Could not create the (instance) folder: " + iPath.string()); + } + + fs::path data = iPath / DATA_FILENAME; + if (fs::exists(data)) + { + // Should not be the case. Anyway, if it happens, create new file! + fs::remove(data); + } + + std::ofstream ofs; + ofs.open(data); + + auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); + to_aron(aron, e); + nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(aron); + + ofs << j.dump(2); + ofs.close(); + }); + }); + }); + }); + + }); + + // what to do with clear text data after encoding? + // TODO! + + // Finaly clear cache and put reference to lut + cache.forEachInstance([](armem::wm::EntityInstance & i) + { + i.data() = nullptr; + }); + + std::lock_guard l2(lut_mutex); + lut.append(cache); + cache.clear(); + + TIMING_END(LTM_Encode); + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h new file mode 100644 index 0000000000000000000000000000000000000000..227d419119e3889006a3f0f30a4789e3b9e76dfe --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h @@ -0,0 +1,44 @@ +#pragma once + +// STD / STL +#include <mutex> +#include <optional> +#include <filesystem> + +// Base Class +#include "../LongtermMemoryBase.h" + +namespace armarx::armem::server::ltm::disk +{ + /// @brief A memory storing data in mongodb (needs 'armarx memory start' to start the mongod instance) + class MemoryManager : + public LongtermMemoryBase + { + using Base = LongtermMemoryBase; + + public: + MemoryManager() = default; + + void reload() override; + void convert(armem::wm::Memory&) override; + void encodeAndStore() override; + + void setBasePath(const std::filesystem::path& p) + { + basePathToMemory = p; + } + + protected: + + + private: + bool checkPath() const; + + public: + std::filesystem::path basePathToMemory; + + private: + static const constexpr char* TYPE_FILENAME = "type.aron.ltm.json"; + static const constexpr char* DATA_FILENAME = "data.aron.ltm.json"; + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad2d95b0fed35991ae99e050a9939537a840a2a2 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.cpp @@ -0,0 +1,77 @@ +#include "ConnectionManager.h" + +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 = {}; + + + void ConnectionManager::initialize_if() + { + std::lock_guard l(initializationMutex); // all others have to wait until the initialization is complete + if (!initialized) + { + initialized = true; + mongocxx::instance instance{}; // This should be done only once. + } + } + + mongocxx::pool& ConnectionManager::Connect(const MongoDBSettings& settings) + { + initialize_if(); + + const auto uri_str = settings.uri(); + auto it = Connections.find(uri_str); + if (it == Connections.end()) + { + mongocxx::uri uri(uri_str); + auto pool = std::make_unique<mongocxx::pool>(uri); + auto con = Connections.emplace(settings.key(), std::move(pool)); + return *con.first->second; + } + else + { + // A connection already exists. We do not need to open another one. + return *it->second; + } + } + + bool ConnectionManager::ConnectionIsValid(const MongoDBSettings& settings, bool forceNewConnection) + { + initialize_if(); + + try + { + if (!forceNewConnection) + { + auto it = Connections.find(settings.key()); + if (it != Connections.end()) + { + auto client = it->second->acquire(); + auto admin = client->database("admin"); + auto result = admin.run_command(bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("isMaster", 1))); + return true; + } + } + + mongocxx::uri uri(settings.uri()); + auto client = mongocxx::client(uri); + auto admin = client["admin"]; + auto result = admin.run_command(bsoncxx::builder::basic::make_document(bsoncxx::builder::basic::kvp("isMaster", 1))); + return true; + } + catch (const std::exception& xcp) + { + return false; + } + } + + bool ConnectionManager::ConnectionExists(const MongoDBSettings& settings) + { + initialize_if(); + + auto it = Connections.find(settings.key()); + return it != Connections.end(); + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h new file mode 100644 index 0000000000000000000000000000000000000000..15fac008d7963c034ce40fe55a4606ffe3fead3f --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/ConnectionManager.h @@ -0,0 +1,97 @@ +#pragma once + +#include <string> +#include <mutex> +#include <map> +#include <memory> +#include <sstream> + +#include <bsoncxx/json.hpp> +#include <mongocxx/client.hpp> +#include <mongocxx/pool.hpp> +#include <mongocxx/stdx.hpp> +#include <mongocxx/uri.hpp> +#include <mongocxx/instance.hpp> +#include <bsoncxx/builder/stream/helpers.hpp> +#include <bsoncxx/builder/stream/document.hpp> +#include <bsoncxx/builder/stream/array.hpp> + + +namespace armarx::armem::server::ltm::mongodb +{ + + using PoolClientPtr = mongocxx::pool::entry; + + /** + * @brief A manager of multiple mongodb connection + */ + class ConnectionManager + { + public: + struct MongoDBSettings + { + std::string host = "localhost"; + unsigned int port = 25276; + std::string user = ""; + std::string password = ""; + std::string database = "Test"; + int minPoolSize = 5; + int maxPoolSize = 100; + + + bool isSet() const + { + // we always need a host and a port + return !host.empty() and port != 0; + } + + std::string baseUri() const + { + std::stringstream ss; + ss << "mongodb://"; + + if (!user.empty()) + { + ss << user; + if (!password.empty()) + { + ss << ":" << password; + } + ss << "@"; + } + ss << host; + return ss.str(); + } + + std::string key() const + { + // TODO: What happens if a connection exists and you would like to open another one with a different user (e.g. that sees different things)? + return "mongodb://" + host + ":" + std::to_string(port); + } + + std::string uri() const + { + return baseUri() + ":" + std::to_string(port) + "/?minPoolSize=" + std::to_string(minPoolSize) + "&maxPoolSize=" + std::to_string(maxPoolSize); + } + + std::string toString() const + { + return uri() + "&database=" + database; + } + }; + + static mongocxx::pool& Connect(const MongoDBSettings& settings); + static bool ConnectionIsValid(const MongoDBSettings& settings, bool forceNewConnection = false); + static bool ConnectionExists(const MongoDBSettings& settings); + + private: + static void initialize_if(); + + + private: + static std::mutex initializationMutex; + static bool initialized; + static std::map<std::string, std::unique_ptr<mongocxx::pool>> Connections; + + }; +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73a06f5bda18fbc90f3d95be12987fc382eb2b82 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.cpp @@ -0,0 +1,426 @@ +// Header +#include "MemoryManager.h" + +// Simox +#include <SimoxUtility/json.h> + +// ArmarX +#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/logging/Logging.h> + +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/armem/core/wm/aron_conversions.h> +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> + + +namespace armarx::armem::server::ltm::mongodb +{ + namespace bsoncxxbuilder = bsoncxx::builder::stream; + namespace bsoncxxdoc = bsoncxx::document; + + PoolClientPtr MemoryManager::checkConnection() const + { + // Check connection: + ARMARX_INFO << "Checking connection"; + if (!ConnectionManager::ConnectionIsValid(dbsettings)) + { + ARMARX_WARNING << deactivateSpam("ConnectionIsNotValid") + << "The connection to mongocxx for ltm '" << cache.name() << "' is not valid. Settings are: " << dbsettings.toString() + << "\nTo start it, run e.g.: \n" + << "armarx memory start" + << "\n\n"; + return nullptr; + } + + auto& pool = ConnectionManager::Connect(dbsettings); + auto client = pool.acquire(); + + return client; + } + + void MemoryManager::reload() + { + TIMING_START(LTM_Reload); + ARMARX_INFO << "(Re)Establishing connection to: " << dbsettings.toString(); + + auto client = checkConnection(); + if (!client) + { + // abort + ARMARX_WARNING << "A connection to: " << dbsettings.toString() << " is not possible. Could not (pre)load data from mongodb."; + return; + } + + auto databases = client->list_databases(); + for (const auto& doc : databases) + { + auto el = doc["name"]; + ARMARX_INFO << "Found Memory-Collection in MongoDB: " << el.get_utf8().value; + } + + armem::wm::Memory temp(lut.id()); // a temporary client wm. We will append temp to the lut at the end of this metho (append ignores duplicate entries) + mongocxx::database db = client->database(dbsettings.database); + + ARMARX_INFO << "Loading memory: " << temp.id().str(); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + mongocxx::collection mColl = db.collection(temp.id().str()); + mongocxx::cursor cursor = mColl.find({}); + for (const auto& doc : cursor) // Although this looks like code duplication, we need a distinguition between memories, + // core, prov and entities because of a different structure + // (only core and prov have same structure) + { + auto el = doc[MONGO_FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.memoryName != temp.id().memoryName) + { + throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name: " + i.str()); + } + + std::string k = i.coreSegmentName; + + if (temp.hasCoreSegment(k)) + { + throw error::ArMemError("Somehow the (memory) container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + // /////////////////////////////// + // Add and iterate over provider segments + // /////////////////////////////// + auto& cSeg = temp.addCoreSegment(k); + mongocxx::collection cColl = db.collection(cSeg.id().str()); + mongocxx::cursor cursor = cColl.find({}); + for (const auto& doc : cursor) + { + auto el = doc[MONGO_FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.coreSegmentName != cSeg.id().coreSegmentName || i.memoryName != cSeg.id().memoryName) + { + throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name: " + i.str()); + } + + std::string k = i.providerSegmentName; + if (cSeg.hasProviderSegment(k)) + { + throw error::ArMemError("Somehow the (core segment) container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + // /////////////////////////////// + // Add and iterate over entities + // /////////////////////////////// + auto& pSeg = cSeg.addProviderSegment(k); + mongocxx::collection pColl = db.collection(pSeg.id().str()); + mongocxx::cursor cursor = pColl.find({}); + for (const auto& doc : cursor) + { + auto el = doc[MONGO_FOREIGN_KEY]; + auto foreignKey = el.get_utf8().value; + + MemoryID i((std::string) foreignKey); + if (i.providerSegmentName != pSeg.id().providerSegmentName || i.coreSegmentName != pSeg.id().coreSegmentName || i.memoryName != pSeg.id().memoryName) + { + throw error::InvalidMemoryID(i, "A MemoryID in mongodb was invalid. Found the wrong memory name: " + i.str()); + } + + std::string k = i.entityName; + if (pSeg.hasEntity(k)) + { + throw error::ArMemError("Somehow the (provider segment) container already contains the key k = " + k + ". Do you have double entries in mongodb?"); + } + else + { + // /////////////////////////////// + // Add and iterate over snapshots + // /////////////////////////////// + auto& eSeg = pSeg.addEntity(k); + mongocxx::collection eColl = db.collection(eSeg.id().str()); + mongocxx::cursor cursor = eColl.find({}); + for (const auto& doc : cursor) + { + auto ts = armem::Time::microSeconds(doc[MONGO_TIMESTAMP].get_int64().value); + auto& newSnapshot = eSeg.addSnapshot(ts); + + auto i = doc[MONGO_INSTANCES]; + unsigned long length = std::distance(i.get_array().value.begin(), i.get_array().value.end()); + ARMARX_INFO << "The array legth for an snapshot '" << newSnapshot.id().str() << "' is: " << length; + + for (unsigned long i = 0; i < length; ++i) + { + // add an empty instance as reference + newSnapshot.addInstance({}); + } + + + // check to update lastSnapshot map + checkUpdateLatestSnapshot(newSnapshot); + } + } + } + } + } + } + } + + std::lock_guard l(cache_mutex); // we always take the cache mutex BEFORE the lut mutex! (otherwise we may have deadlocks) + std::lock_guard l2(lut_mutex); + lut.append(temp); + ARMARX_INFO << "After reload memory " << lut.id().str() << " has size: " << lut.size(); + + TIMING_END(LTM_Reload); + } + + void MemoryManager::convert(armem::wm::Memory& m) + { + TIMING_START(LTM_Convert); + + auto client = checkConnection(); + if (!client) + { + // abort + ARMARX_WARNING << "A connection to: " << dbsettings.toString() << " is not possible. Could not convert information, therefore returning leaving input untouched."; + return; + } + mongocxx::database db = client->database(dbsettings.database); + + // update emtpy data ptr + m.forEachCoreSegment([&db](armem::wm::CoreSegment & e) + { + e.forEachProviderSegment([&db](armem::wm::ProviderSegment & e) + { + e.forEachEntity([&db](armem::wm::Entity & e) + { + e.forEachSnapshot([&db](armem::wm::EntitySnapshot & e) + { + // check whether data is nullptr + bool allDataIsNull = e.size() > 0; + e.forEachInstance([&allDataIsNull](armem::wm::EntityInstance & e) + { + if (e.data()) + { + allDataIsNull = false; + return false; // means break + } + return true; + }); + + if (allDataIsNull) // an entry from the lut (probably... for now we assume that every entry either has data (cache) or has null (lut)) + { + // Get data from mongodb + auto eColl = db.collection(e.id().getEntityID().str()); + auto q = bsoncxxbuilder::document{} << std::string(MONGO_TIMESTAMP) << e.id().timestamp.toMicroSeconds() << bsoncxxbuilder::finalize; + auto res = eColl.find_one(q.view()); + + if (!res) + { + throw error::ArMemError("Could not load an instance from the memory '" + e.id().getEntityID().str() + "'. Tried to access: " + e.id().getEntitySnapshotID().str()); + } + + // convert full json of this entry + nlohmann::json json = nlohmann::json::parse(bsoncxx::to_json(*res)); + nlohmann::json instances = json[MONGO_INSTANCES]; + + if (instances.size() != e.size()) + { + throw error::ArMemError("The size of the mongodb entity entry at id " + e.id().getEntitySnapshotID().str() + " has wrong size. Expected: " + std::to_string(e.size()) + " but got: " + std::to_string(instances.size())); + } + + for (unsigned int i = 0; i < e.size(); ++i) + { + auto& ins = e.getInstance(i); + + // get ionstance json + nlohmann::json doc = instances[i]; + auto aron = aron::converter::AronNlohmannJSONConverter::ConvertFromNlohmannJSON(doc); + + // remove metadata + wm::EntityInstance tmp(e.id().withInstanceIndex(i)); + from_aron(aron, tmp); + + // set data + ins.data() = tmp.data(); + } + } + }); + }); + }); + }); + TIMING_END(LTM_Convert); + } + + void MemoryManager::encodeAndStore() + { + TIMING_START(LTM_Encode); + ARMARX_INFO << "Encode cache " << cache.id().str() << " with size: " << cache.size(); + + auto client = checkConnection(); + if (!client) + { + // abort + ARMARX_WARNING << "A connection to: " << dbsettings.toString() << " is not possible. Could not consolidate data from cache to mongodb."; + return; + } + + std::lock_guard l(cache_mutex); + mongocxx::database db = client->database(dbsettings.database); + auto mColl = db.collection(cache.id().str()); + + // /////////////////////////////// + // Iterate over core segments + // /////////////////////////////// + cache.forEachCoreSegment([this, &db, &mColl](armem::wm::CoreSegment & e) + { + auto cColl = db.collection(e.id().str()); + + if (!containsCoreSegment(mColl, e.id())) + { + // not found + mongodbInsertForeignKey(mColl, e.id().str()); + } + + // /////////////////////////////// + // Iterate over provider segments + // /////////////////////////////// + e.forEachProviderSegment([this, &db, &cColl](armem::wm::ProviderSegment & e) + { + auto pColl = db.collection(e.id().str()); + + if (!containsProviderSegment(cColl, e.id())) + { + // not found + mongodbInsertForeignKey(cColl, e.id().str()); + } + + // /////////////////////////////// + // Iterate over each segments + // /////////////////////////////// + e.forEachEntity([this, &db, &pColl](armem::wm::Entity & e) + { + auto eColl = db.collection(e.id().str()); + + if (!containsEntity(pColl, e.id())) + { + // not found + mongodbInsertForeignKey(pColl, e.id().str()); + } + + // /////////////////////////////// + // Iterate over snapshots + // /////////////////////////////// + e.forEachSnapshot([this, &eColl](armem::wm::EntitySnapshot & e) + { + if (!this->containsSnapshot(eColl, e.id())) + { + // timestamp not found in mongodb ==> new entry + bsoncxxbuilder::document builder{}; + auto in_array = builder + << std::string(MONGO_ID) << e.id().str() + << std::string(MONGO_TIMESTAMP) << e.id().timestamp.toMicroSeconds() + << std::string(MONGO_INSTANCES); + auto array_builder = bsoncxx::builder::basic::array{}; + + e.forEachInstance([&array_builder](const wm::EntityInstance & e) + { + auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); + to_aron(aron, e); + nlohmann::json j = aron::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON(aron); + + auto doc_value = bsoncxx::from_json(j.dump(2)); + array_builder.append(doc_value); + }); + + auto after_array = in_array << array_builder; + bsoncxx::document::value doc = after_array << bsoncxx::builder::stream::finalize; + ARMARX_INFO << "Insert into mongodb: " << e.id().timestamp.toMicroSeconds(); + eColl.insert_one(doc.view()); + + // check to update lastSnapshot map + checkUpdateLatestSnapshot(e); + } + else + { + ARMARX_INFO << "Timestamp already found for id: " << e.id().str(); + // timestamp already in mongodb. Ignore it + } + }); + }); + }); + }); + + // what to do with clear text data after encoding? + // TODO! + + // Finaly clear cache and put reference to lut + cache.forEachInstance([](armem::wm::EntityInstance & i) + { + i.data() = nullptr; + }); + + std::lock_guard l2(lut_mutex); + lut.append(cache); + cache.clear(); + + TIMING_END(LTM_Encode); + } + + void MemoryManager::mongodbInsertForeignKey(mongocxx::collection& coll, const std::string& key) + { + auto q = bsoncxxbuilder::document{} << std::string(MONGO_FOREIGN_KEY) << key << bsoncxxbuilder::finalize; + coll.insert_one(q.view()); + } + + bool MemoryManager::mongodbContainsForeignKey(mongocxx::collection& coll, const std::string& key) const + { + // check mongodb + auto q = bsoncxxbuilder::document{} << std::string(MONGO_FOREIGN_KEY) << key << bsoncxxbuilder::finalize; + auto res = coll.find_one(q.view()); + return (bool) res; + } + + bool MemoryManager::containsCoreSegment(mongocxx::collection& mColl, const MemoryID& coreSegmentID) const + { + if (Base::containsCoreSegment(coreSegmentID)) + { + return true; + } + return mongodbContainsForeignKey(mColl, coreSegmentID.str()); + } + + bool MemoryManager::containsProviderSegment(mongocxx::collection& cColl, const MemoryID& providerSegmentID) const + { + if (Base::containsProviderSegment(providerSegmentID)) + { + return true; + } + return mongodbContainsForeignKey(cColl, providerSegmentID.str()); + } + + bool MemoryManager::containsEntity(mongocxx::collection& pColl, const MemoryID& entityID) const + { + if (Base::containsEntity(entityID)) + { + return true; + } + return mongodbContainsForeignKey(pColl, entityID.str()); + } + + bool MemoryManager::containsSnapshot(mongocxx::collection& eColl, const MemoryID& snapshotID) const + { + if (Base::containsSnapshot(snapshotID)) + { + return true; + } + + // check mongodb + auto q = bsoncxxbuilder::document{} << std::string(MONGO_TIMESTAMP) << snapshotID.timestamp.toMicroSeconds() << bsoncxxbuilder::finalize; + auto res = eColl.find_one(q.view()); + return (bool) res; + } +} diff --git a/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h new file mode 100644 index 0000000000000000000000000000000000000000..189ccc42563d17dd63116dc3210d99d648b28c31 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/ltm/mongodb/MemoryManager.h @@ -0,0 +1,51 @@ +#pragma once + +// STD / STL +#include <mutex> +#include <optional> + +// Base Class +#include "../LongtermMemoryBase.h" + +// Data +# include "ConnectionManager.h" + +namespace armarx::armem::server::ltm::mongodb +{ + /// @brief A memory storing data in mongodb (needs 'armarx memory start' to start the mongod instance) + class MemoryManager : + public LongtermMemoryBase + { + using Base = LongtermMemoryBase; + + public: + MemoryManager() = default; + + void reload() override; + void convert(armem::wm::Memory&) override; + void encodeAndStore() override; + + protected: + + + private: + PoolClientPtr checkConnection() const; // return nullptr if not possible + + void mongodbInsertForeignKey(mongocxx::collection&, const std::string& key); + + bool mongodbContainsForeignKey(mongocxx::collection&, const std::string& key) const; + bool containsCoreSegment(mongocxx::collection&, const MemoryID&) const; + bool containsProviderSegment(mongocxx::collection&, const MemoryID&) const; + bool containsEntity(mongocxx::collection&, const MemoryID&) const; + bool containsSnapshot(mongocxx::collection&, const MemoryID&) const; + + public: + ConnectionManager::MongoDBSettings dbsettings; + + private: + static const constexpr char* MONGO_FOREIGN_KEY = "foreign_key"; + static const constexpr char* MONGO_ID = "id"; + static const constexpr char* MONGO_TIMESTAMP = "timestamp"; + static const constexpr char* MONGO_INSTANCES = "instances"; + }; +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base.h b/source/RobotAPI/libraries/armem/server/query_proc/base.h new file mode 100644 index 0000000000000000000000000000000000000000..1d826798cecac7371882ce0783ddc05414a4f237 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/query_proc/base.h @@ -0,0 +1,12 @@ +#pragma once + +#include <RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h> +#include <RobotAPI/libraries/armem/server/query_proc/base/ProviderSegmentQueryProcessorBase.h> +#include <RobotAPI/libraries/armem/server/query_proc/base/CoreSegmentQueryProcessorBase.h> +#include <RobotAPI/libraries/armem/server/query_proc/base/MemoryQueryProcessorBase.h> + + +namespace armarx::armem::server::query_proc::base +{ + +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.cpp b/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.cpp index 34e2abd99c889d65723065c8b4726b2ea0aa9bd1..a3dfb4eb4102e21ee0cc97d7a9919b55c7c83f35 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.cpp +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.cpp @@ -1,2 +1,21 @@ #include "BaseQueryProcessorBase.h" +#include <ArmarXCore/core/logging/Logging.h> + + +namespace armarx::armem::server::query_proc::base +{ + + std::set<query::data::QueryTarget> + detail::getTargets(const std::vector<query::data::QueryTarget>& _targets) + { + std::set<query::data::QueryTarget> targets(_targets.begin(), _targets.end()); + if (targets.empty()) + { + ARMARX_DEBUG << "Query has no targets - using WM as default."; + targets.insert(query::data::QueryTarget::WM); + } + return targets; + } + +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.h b/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.h index 2af92450309d640122bffc9066c1a1df7bdfff49..73c662e04ebd734cbe79c3b19f676f684223a88c 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.h +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.h @@ -1,88 +1,91 @@ #pragma once -#include <ArmarXCore/core/logging/Logging.h> #include <RobotAPI/interface/armem/query.h> +#include <Ice/Handle.h> -namespace armarx::armem::base::query_proc +#include <set> +#include <vector> + + +namespace armarx::armem::server::query_proc::base::detail { + // If empty, e.g. when receiving queries from python, we use WM as default. + // We do it here as (Sl)ice does not support default values for vectors. + std::set<query::data::QueryTarget> getTargets(const std::vector<query::data::QueryTarget>& _targets); +} +namespace armarx::armem::server::query_proc::base +{ + + using QueryTarget = query::data::QueryTarget; + /** * @brief Base class for memory query processors. */ - template <class DataT, class QueryT> + template <QueryTarget queryTarget, class DataT, class ResultT, class QueryT> class BaseQueryProcessorBase { public: + using QueryPtrT = ::IceInternal::Handle<QueryT>; using QuerySeqT = std::vector<QueryPtrT>; + public: virtual ~BaseQueryProcessorBase() = default; - DataT process(const QueryT& query, const DataT& data) const + ResultT process(const QueryT& query, const DataT& data) const { - DataT result = data.copyEmpty(); - if (getTargets(query.targets).count(getTargetType())) + ResultT result { data.id() }; + if (detail::getTargets(query.targets).count(queryTarget)) { this->process(result, query, data); } return result; } - DataT process(const QueryPtrT& query, const DataT& data) const + ResultT process(const QueryPtrT& query, const DataT& data) const { return this->process(*query, *data); } - DataT process(const QuerySeqT& queries, const DataT& data) const + ResultT process(const QuerySeqT& queries, const DataT& data) const { - DataT result = data.copyEmpty(); + ResultT result { data.id() }; this->process(result, queries, data); return result; } - void process(DataT& result, const QuerySeqT& queries, const DataT& data) const + void process(ResultT& result, const QuerySeqT& queries, const DataT& data) const { if (queries.empty()) { - ARMARX_DEBUG << "There are no queries to process."; return; } for (const auto& query : queries) { - if (getTargets(query->targets).count(getTargetType())) + if (detail::getTargets(query->targets).count(queryTarget)) { this->process(result, *query, data); } } } - virtual void process(DataT& result, const QueryT& query, const DataT& data) const = 0; - - protected: + /** + * @brief Process the query and populate `result`. + * + * @param result The result container. + * @param query The query. + * @param data The source container. + */ + virtual void process(ResultT& result, const QueryT& query, const DataT& data) const = 0; - virtual query::data::QueryTarget getTargetType() const = 0; - - - private: + }; - /// If empty, e.g. when receiving queries from python, we use WM as default. - /// We do it here as (Sl)ice does not support default values for vectors. - static std::set<query::data::QueryTarget> getTargets(const std::vector<query::data::QueryTarget>& _targets) - { - std::set<query::data::QueryTarget> targets(_targets.begin(), _targets.end()); - if (targets.empty()) - { - ARMARX_DEBUG << "Query has no targets - using WM as default."; - targets.insert(query::data::QueryTarget::WM); - } - return targets; - } - }; } diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/CoreSegmentQueryProcessorBase.h b/source/RobotAPI/libraries/armem/server/query_proc/base/CoreSegmentQueryProcessorBase.h index 9d7b2518227e4ddf0c5d759963af193656e39894..0c6199eac95465a27da8bc0d59b4fcd40607d139 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/CoreSegmentQueryProcessorBase.h +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/CoreSegmentQueryProcessorBase.h @@ -1,39 +1,51 @@ #pragma once -#include <regex> - -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include "BaseQueryProcessorBase.h" -#include <RobotAPI/interface/armem/query.h> #include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/interface/armem/query.h> -#include "BaseQueryProcessorBase.h" -#include "ProviderSegmentQueryProcessorBase.h" +#include <regex> -namespace armarx::armem::base::query_proc +namespace armarx::armem::server::query_proc::base { /** * @brief Handles memory queries. */ - template <class _CoreSegmentT> + template <QueryTarget queryTarget, class _CoreSegmentT, class _ResultCoreSegmentT, class _ChildProcessorT> class CoreSegmentQueryProcessorBase : - virtual public BaseQueryProcessorBase<_CoreSegmentT, armem::query::data::CoreSegmentQuery> + public BaseQueryProcessorBase<queryTarget, _CoreSegmentT, _ResultCoreSegmentT, armem::query::data::CoreSegmentQuery> { - using Base = BaseQueryProcessorBase<_CoreSegmentT, armem::query::data::CoreSegmentQuery>; + using Base = BaseQueryProcessorBase<queryTarget, _CoreSegmentT, _ResultCoreSegmentT, armem::query::data::CoreSegmentQuery>; public: + using CoreSegmentT = _CoreSegmentT; - using ProviderSegmentT = typename _CoreSegmentT::ProviderSegmentT; - using EntityT = typename ProviderSegmentT::EntityT; - using EntitySnapshotT = typename EntityT::EntitySnapshotT; + using ProviderSegmentT = typename CoreSegmentT::ProviderSegmentT; + + using ResultCoreSegmentT = _ResultCoreSegmentT; + using ResultProviderSegmentT = typename ResultCoreSegmentT::ProviderSegmentT; + + using ChildProcessorT = _ChildProcessorT; + + + public: + + CoreSegmentQueryProcessorBase() + { + } + CoreSegmentQueryProcessorBase(ChildProcessorT&& childProcessor) : + childProcessor(childProcessor) + { + } + using Base::process; - virtual void process(_CoreSegmentT& result, + virtual void process(ResultCoreSegmentT& result, const armem::query::data::CoreSegmentQuery& query, - const _CoreSegmentT& coreSegment) const override + const CoreSegmentT& coreSegment) const override { if (auto q = dynamic_cast<const armem::query::data::core::All*>(&query)) { @@ -53,46 +65,57 @@ namespace armarx::armem::base::query_proc } } - void process(_CoreSegmentT& result, + void process(ResultCoreSegmentT& result, const armem::query::data::core::All& query, - const _CoreSegmentT& coreSegment) const + const CoreSegmentT& coreSegment) const { - for (const auto& [name, providerSegment] : coreSegment.providerSegments()) + coreSegment.forEachProviderSegment([this, &query, &result](const ProviderSegmentT & providerSegment) { - result.addProviderSegment(providerSegmentProcessorProcess(query.providerSegmentQueries, providerSegment)); - } + this->_processResult(result, providerSegment, query); + }); } - void process(_CoreSegmentT& result, + void process(ResultCoreSegmentT& result, const armem::query::data::core::Single& query, - const _CoreSegmentT& coreSegment) const + const CoreSegmentT& coreSegment) const { - try + const ProviderSegmentT* providerSegment = coreSegment.findProviderSegment(query.providerSegmentName); + if (providerSegment) { - const ProviderSegmentT& providerSegment = coreSegment.getProviderSegment(query.providerSegmentName); - result.addProviderSegment(providerSegmentProcessorProcess(query.providerSegmentQueries, providerSegment)); - } - catch (const error::MissingEntry&) - { - // Leave empty. + this->_processResult(result, *providerSegment, query); } } - void process(_CoreSegmentT& result, + void process(ResultCoreSegmentT& result, const armem::query::data::core::Regex& query, - const _CoreSegmentT& coreSegment) const + const CoreSegmentT& coreSegment) const { - std::regex regex(query.providerSegmentNameRegex); - for (const auto& [name, providerSegment] : coreSegment.providerSegments()) + const std::regex regex(query.providerSegmentNameRegex); + coreSegment.forEachProviderSegment( + [this, &result, &query, ®ex](const ProviderSegmentT & providerSegment) { if (std::regex_search(providerSegment.name(), regex)) { - result.addProviderSegment(providerSegmentProcessorProcess(query.providerSegmentQueries, providerSegment)); + this->_processResult(result, providerSegment, query); } - } + }); } + + private: + + void _processResult(ResultCoreSegmentT& result, + const ProviderSegmentT& providerSegment, + const armem::query::data::CoreSegmentQuery& query) const + { + ResultProviderSegmentT& added = result.addProviderSegment(providerSegment.name(), providerSegment.aronType()); + childProcessor.process(added, query.providerSegmentQueries, providerSegment); + } + + protected: - virtual ProviderSegmentT providerSegmentProcessorProcess(const armem::query::data::ProviderSegmentQuerySeq& a, const ProviderSegmentT& o) const = 0; + + ChildProcessorT childProcessor; + }; } diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.cpp b/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.cpp index 5cbac85a8dc5f8899e7c72434516063121a2b4c1..d9df528842d11fd66ba38877318785d6c4dd5263 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.cpp +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.cpp @@ -1 +1,12 @@ #include "EntityQueryProcessorBase.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + + +namespace armarx::armem::server::query_proc::base +{ + void detail::checkReferenceTimestampNonNegative(const Time& timestamp) + { + ARMARX_CHECK_NONNEGATIVE(timestamp.toMicroSeconds()) << "Reference timestamp must be non-negative."; + } +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h b/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h index 40113207f194c76301fbe8768a6d42c22f8b01db..b0210c37e4ed71b64875a79453e56d928852b54f 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h @@ -1,41 +1,43 @@ #pragma once -#include <cstdint> -#include <iterator> - -#include <RobotAPI/interface/armem/query.h> - -#include <ArmarXCore/core/exceptions/LocalException.h> -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include "BaseQueryProcessorBase.h" +#include <RobotAPI/libraries/armem/core/Time.h> #include <RobotAPI/libraries/armem/core/error.h> #include <RobotAPI/libraries/armem/core/ice_conversions.h> -#include "BaseQueryProcessorBase.h" -#include "RobotAPI/libraries/armem/core/Time.h" +#include <RobotAPI/interface/armem/query.h> + +#include <cstdint> +#include <iterator> -namespace armarx::armem::base::query_proc +namespace armarx::armem::server::query_proc::base::detail +{ + void checkReferenceTimestampNonNegative(const Time& timestamp); +} +namespace armarx::armem::server::query_proc::base { - /** - * @brief Handles memory queries. - */ - template <class _EntityT> + template <QueryTarget queryTarget, class _EntityT, class _ResultEntityT> class EntityQueryProcessorBase : - virtual public BaseQueryProcessorBase<_EntityT, armem::query::data::EntityQuery> + public BaseQueryProcessorBase<queryTarget, _EntityT, _ResultEntityT, armem::query::data::EntityQuery> { - using Base = BaseQueryProcessorBase<_EntityT, armem::query::data::EntityQuery>; + using Base = BaseQueryProcessorBase<queryTarget, _EntityT, _ResultEntityT, armem::query::data::EntityQuery>; public: + using EntityT = _EntityT; using EntitySnapshotT = typename EntityT::EntitySnapshotT; + using ResultEntityT = _ResultEntityT; + using ResultSnapshotT = typename ResultEntityT::EntitySnapshotT; + + using Base::process; - void process(_EntityT& result, - const armem::query::data::EntityQuery& query, - const _EntityT& entity) const override + virtual void process(ResultEntityT& result, + const armem::query::data::EntityQuery& query, + const EntityT& entity) const override { if (auto q = dynamic_cast<const armem::query::data::entity::All*>(&query)) { @@ -71,69 +73,62 @@ namespace armarx::armem::base::query_proc } } - void process(_EntityT& result, + + void process(ResultEntityT& result, const armem::query::data::entity::All& query, - const _EntityT& entity) const + const EntityT& entity) const { (void) query; // Copy this entitiy and its contents. - for (const auto& [time, snapshot] : entity) + entity.forEachSnapshot([this, &result](const EntitySnapshotT & snapshot) { addResultSnapshot(result, snapshot); - } + }); } - void process(_EntityT& result, + + void process(ResultEntityT& result, const armem::query::data::entity::Single& query, - const _EntityT& entity) const + const EntityT& entity) const { if (query.timestamp < 0) { - try + if (const ResultSnapshotT* snapshot = entity.findLatestSnapshot()) { - addResultSnapshot(result, entity.getLatestSnapshot()); - } - catch (const armem::error::EntityHistoryEmpty&) - { - if (false) - { - ARMARX_IMPORTANT << "Failed to retrieve latest snapshot from entity " << entity.id() << ". " - << "Entity has not timestamps."; - } + addResultSnapshot(result, *snapshot); } } else { const Time time = fromIce<Time>(query.timestamp); - try + if (const ResultSnapshotT* snapshot = entity.findSnapshot(time)) { - auto snapshot = entity.getSnapshot(time); - addResultSnapshot(result, snapshot); + addResultSnapshot(result, *snapshot); } - catch (const armem::error::MissingEntry&) + else { // Leave empty. - if (false) +#if 0 + std::stringstream ss; + ss << "Failed to retrieve snapshot with timestamp " + << armem::toDateTimeMilliSeconds(time) + << " from entity " << entity.id() << ".\n" + << "Entity has timestamps: "; + for (const Time& t : entity.getTimestamps()) { - std::stringstream ss; - ss << "Failed to retrieve snapshot with timestamp " - << armem::toDateTimeMilliSeconds(time) - << " from entity " << entity.id() << ".\n" - << "Entity has timestamps: "; - for (const Time& t : entity.getTimestamps()) - { - ss << "\n- " << armem::toDateTimeMilliSeconds(t); - } - ARMARX_IMPORTANT << ss.str(); + ss << "\n- " << armem::toDateTimeMilliSeconds(t); } + ARMARX_IMPORTANT << ss.str(); +#endif } } } - void process(_EntityT& result, + + void process(ResultEntityT& result, const armem::query::data::entity::TimeRange& query, - const _EntityT& entity) const + const EntityT& entity) const { if (query.minTimestamp <= query.maxTimestamp || query.minTimestamp < 0 || query.maxTimestamp < 0) { @@ -143,109 +138,90 @@ namespace armarx::armem::base::query_proc } } - void process(_EntityT& result, + + void process(ResultEntityT& result, const armem::query::data::entity::IndexRange& query, - const _EntityT& entity) const + const EntityT& entity) const { - if (entity.empty()) + entity.forEachSnapshotInIndexRange( + query.first, query.last, + [this, &result](const EntitySnapshotT & snapshot) { - return; - } - - size_t first = negativeIndexSemantics(query.first, entity.history().size()); - size_t last = negativeIndexSemantics(query.last, entity.history().size()); - - if (first <= last) - { - auto it = entity.begin(); - std::advance(it, first); - - size_t num = last - first + 1; // +1 to make last inclusive - for (size_t i = 0; i < num; ++i, ++it) - { - addResultSnapshot(result, it); - } - } + addResultSnapshot(result, snapshot); + }); } - void process(_EntityT& result, + + void process(ResultEntityT& result, const Time& min, const Time& max, - const _EntityT& entity, + const EntityT& entity, const armem::query::data::EntityQuery& query) const { (void) query; - - // Returns an iterator pointing to the first element that is not less than (i.e. greater or equal to) key. - auto begin = min.toMicroSeconds() > 0 ? entity.history().lower_bound(min) : entity.history().begin(); - // Returns an iterator pointing to the first element that is *greater than* key. - auto end = max.toMicroSeconds() > 0 ? entity.history().upper_bound(max) : entity.history().end(); - - for (auto it = begin; it != end && it != entity.history().end(); ++it) + entity.forEachSnapshotInTimeRange( + min, max, + [this, &result](const EntitySnapshotT & snapshot) { - addResultSnapshot(result, it); - } + addResultSnapshot(result, snapshot); + }); } - void process(_EntityT& result, + void process(ResultEntityT& result, const armem::query::data::entity::BeforeOrAtTime& query, - const _EntityT& entity) const + const EntityT& entity) const { - const auto referenceTimestamp = fromIce<Time>(query.timestamp); - ARMARX_CHECK(referenceTimestamp.toMicroSeconds() >= 0) << "Reference timestamp is negative!"; + const Time referenceTimestamp = fromIce<Time>(query.timestamp); + detail::checkReferenceTimestampNonNegative(referenceTimestamp); - try + if (auto* beforeOrAt = entity.findLatestSnapshotBeforeOrAt(referenceTimestamp)) { - auto beforeOrAt = entity.getLatestSnapshotBeforeOrAt(referenceTimestamp); - addResultSnapshot(result, beforeOrAt); - } - catch (const armem::error::MissingEntry&) - { - // Leave empty. + addResultSnapshot(result, *beforeOrAt); } } - void process(_EntityT& result, + void process(ResultEntityT& result, const armem::query::data::entity::BeforeTime& query, - const _EntityT& entity) const + const EntityT& entity) const { - const auto referenceTimestamp = fromIce<Time>(query.timestamp); - ARMARX_CHECK(referenceTimestamp.toMicroSeconds() >= 0) << "Reference timestamp is negative!"; + const Time referenceTimestamp = fromIce<Time>(query.timestamp); + detail::checkReferenceTimestampNonNegative(referenceTimestamp); - const auto maxEntries = fromIce<std::int64_t>(query.maxEntries); - - try + std::vector<const EntitySnapshotT*> befores; + entity.forEachSnapshotBefore(referenceTimestamp, [&befores](const EntitySnapshotT & s) { - const auto before = entity.getSnapshotsBefore(referenceTimestamp); + befores.push_back(&s); + }); - int i = 0; - for (const auto& snapshot : before) - { - if (maxEntries >= 0 && i >= maxEntries) - { - break; - } - addResultSnapshot(result, snapshot); - ++i; - } + size_t num = 0; + if (query.maxEntries < 0) + { + num = befores.size(); } - catch (const error::MissingEntry&) + else + { + num = std::min(befores.size(), static_cast<size_t>(query.maxEntries)); + } + + for (size_t r = 0; r < num; ++r) { - // Leave empty. + size_t i = befores.size() - 1 - r; + addResultSnapshot(result, *befores[i]); } } - void process(_EntityT& result, + + void process(ResultEntityT& result, const armem::query::data::entity::TimeApprox& query, - const _EntityT& entity) const + const EntityT& entity) const { - const auto referenceTimestamp = fromIce<Time>(query.timestamp); - ARMARX_CHECK(referenceTimestamp.toMicroSeconds() >= 0) << "Reference timestamp is negative!"; + const Time referenceTimestamp = fromIce<Time>(query.timestamp); + detail::checkReferenceTimestampNonNegative(referenceTimestamp); - const auto referenceTimestampMicroSeconds = referenceTimestamp.toMicroSeconds(); - const auto epsDuration = fromIce<Time>(query.eps).toMicroSeconds(); + const float referenceTimestampMicroSeconds = referenceTimestamp.toMicroSeconds(); + const float epsDuration = fromIce<Time>(query.eps).toMicroSeconds(); // elements have to be in range [t_ref - eps, t_ref + eps] if eps is positive const auto isInRange = [&](const Time & t) -> bool @@ -258,15 +234,14 @@ namespace armarx::armem::base::query_proc return std::abs(t.toMicroSeconds() - referenceTimestampMicroSeconds) <= epsDuration; }; - try + // last element before or at timestamp + if (auto* beforeOrAt = entity.findLatestSnapshotBeforeOrAt(referenceTimestamp)) { - // last element before or at timestamp - auto beforeOrAt = entity.getLatestSnapshotBeforeOrAt(referenceTimestamp); - const auto timestampOfMatchBefore = beforeOrAt.id().timestamp; + const auto timestampOfMatchBefore = beforeOrAt->id().timestamp; const auto isPerfectMatch = timestampOfMatchBefore == referenceTimestamp; if (isInRange(timestampOfMatchBefore)) { - addResultSnapshot(result, beforeOrAt); + addResultSnapshot(result, *beforeOrAt); } // earsly stop, not necessary to also get the next since the match is perfect @@ -276,35 +251,25 @@ namespace armarx::armem::base::query_proc } // first element after or at timestamp (or at because of fewer checks, we can assure that there is not element at) - const auto after = entity.getFirstSnapshotAfterOrAt(referenceTimestamp); - const auto timestampOfMatchAfter = after.id().timestamp; - if (isInRange(timestampOfMatchAfter)) + const auto* after = entity.findFirstSnapshotAfterOrAt(referenceTimestamp); + if (after) { - addResultSnapshot(result, after); + const auto timestampOfMatchAfter = after->id().timestamp; + if (isInRange(timestampOfMatchAfter)) + { + addResultSnapshot(result, *after); + } } } - catch (const armem::error::MissingEntry&) - { - - } } - static size_t negativeIndexSemantics(long index, size_t size) - { - const size_t max = size > 0 ? size - 1 : 0; - if (index >= 0) - { - return std::clamp<size_t>(static_cast<size_t>(index), 0, max); - } - else - { - return static_cast<size_t>(std::clamp<long>(static_cast<long>(size) + index, 0, static_cast<long>(max))); - } - } protected: - virtual void addResultSnapshot(_EntityT& result, typename _EntityT::ContainerT::const_iterator it) const = 0; - virtual void addResultSnapshot(_EntityT& result, const typename _EntityT::EntitySnapshotT& snapshot) const = 0; + + void addResultSnapshot(ResultEntityT& result, const EntitySnapshotT& snapshot) const + { + result.addSnapshot(snapshot); + } }; } diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/MemoryQueryProcessorBase.h b/source/RobotAPI/libraries/armem/server/query_proc/base/MemoryQueryProcessorBase.h index 55c5e51126b49775db7317d6401aa695c4b5581c..91c41d9f54cb762654db9763dfe6d9c3af1aa60d 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/MemoryQueryProcessorBase.h +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/MemoryQueryProcessorBase.h @@ -1,47 +1,53 @@ #pragma once -#include <regex> - -#include <RobotAPI/interface/armem/query.h> - #include "BaseQueryProcessorBase.h" -#include "CoreSegmentQueryProcessorBase.h" - - -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/interface/armem/query.h> + +#include <regex> -namespace armarx::armem::base::query_proc +namespace armarx::armem::server::query_proc::base { - /** - * @brief Handles memory queries. - */ - template <class _MemoryT> + + template <QueryTarget queryTarget, class _MemoryT, class _ResultMemoryT, class _ChildProcessorT> class MemoryQueryProcessorBase : - virtual public BaseQueryProcessorBase<_MemoryT, armem::query::data::MemoryQuery> + public BaseQueryProcessorBase<queryTarget, _MemoryT, _ResultMemoryT, armem::query::data::MemoryQuery> { - using Base = BaseQueryProcessorBase<_MemoryT, armem::query::data::MemoryQuery>; + using Base = BaseQueryProcessorBase<queryTarget, _MemoryT, _ResultMemoryT, armem::query::data::MemoryQuery>; public: + using MemoryT = _MemoryT; using CoreSegmentT = typename MemoryT::CoreSegmentT; - using ProviderSegmentT = typename CoreSegmentT::ProviderSegmentT; - using EntityT = typename ProviderSegmentT::EntityT; - using EntitySnapshotT = typename EntityT::EntitySnapshotT; + + using ResultMemoryT = _ResultMemoryT; + using ResultCoreSegmentT = typename ResultMemoryT::CoreSegmentT; + + using ChildProcessorT = _ChildProcessorT; + + + public: + + MemoryQueryProcessorBase() + { + } + MemoryQueryProcessorBase(ChildProcessorT&& childProcessor) : + childProcessor(childProcessor) + { + } using Base::process; - _MemoryT process(const armem::query::data::Input& input, const _MemoryT& memory) const + ResultMemoryT process(const armem::query::data::Input& input, const MemoryT& memory) const { return this->process(input.memoryQueries, memory); } - void process(_MemoryT& result, - const armem::query::data::MemoryQuery& query, - const _MemoryT& memory) const override + virtual void process(ResultMemoryT& result, + const armem::query::data::MemoryQuery& query, + const MemoryT& memory) const override { if (auto q = dynamic_cast<const armem::query::data::memory::All*>(&query)) { @@ -57,49 +63,59 @@ namespace armarx::armem::base::query_proc } else { - throw armem::error::UnknownQueryType("memory segment", query); + throw armem::error::UnknownQueryType(MemoryT::getLevelName(), query); } } - void process(_MemoryT& result, + void process(ResultMemoryT& result, const armem::query::data::memory::All& query, - const _MemoryT& memory) const + const MemoryT& memory) const { - for (const auto& [name, coreSegment] : memory.coreSegments()) + memory.forEachCoreSegment([this, &result, &query](const CoreSegmentT & coreSegment) { - result.addCoreSegment(coreSegmentProcessorProcess(query.coreSegmentQueries, coreSegment)); - } + this->_processResult(result, coreSegment, query); + }); } - void process(_MemoryT& result, + void process(ResultMemoryT& result, const armem::query::data::memory::Single& query, - const _MemoryT& memory) const + const MemoryT& memory) const { - try - { - const CoreSegmentT& coreSegment = memory.getCoreSegment(query.coreSegmentName); - result.addCoreSegment(coreSegmentProcessorProcess(query.coreSegmentQueries, coreSegment)); - } - catch (const error::MissingEntry&) + if (const CoreSegmentT* coreSegment = memory.findCoreSegment(query.coreSegmentName)) { + this->_processResult(result, *coreSegment, query); } } - void process(_MemoryT& result, + void process(ResultMemoryT& result, const armem::query::data::memory::Regex& query, - const _MemoryT& memory) const + const MemoryT& memory) const { - std::regex regex(query.coreSegmentNameRegex); - for (const auto& [name, coreSegment] : memory.coreSegments()) + const std::regex regex(query.coreSegmentNameRegex); + memory.forEachCoreSegment([this, &result, &query, ®ex](const CoreSegmentT & coreSegment) { if (std::regex_search(coreSegment.name(), regex)) { - result.addCoreSegment(coreSegmentProcessorProcess(query.coreSegmentQueries, coreSegment)); + this->_processResult(result, coreSegment, query); } - } + }); } + + private: + + void _processResult(ResultMemoryT& result, + const CoreSegmentT& coreSegment, + const armem::query::data::MemoryQuery& query) const + { + ResultCoreSegmentT& added = result.addCoreSegment(coreSegment.name(), coreSegment.aronType()); + childProcessor.process(added, query.coreSegmentQueries, coreSegment); + } + + protected: - virtual CoreSegmentT coreSegmentProcessorProcess(const armem::query::data::CoreSegmentQuerySeq& a, const CoreSegmentT& o) const = 0; + + ChildProcessorT childProcessor; + }; } diff --git a/source/RobotAPI/libraries/armem/server/query_proc/base/ProviderSegmentQueryProcessorBase.h b/source/RobotAPI/libraries/armem/server/query_proc/base/ProviderSegmentQueryProcessorBase.h index b3b7da7ffa9cc1d06aeadabef58c5aebba3974de..46f146694825936d4834b579093881e8452db3ca 100644 --- a/source/RobotAPI/libraries/armem/server/query_proc/base/ProviderSegmentQueryProcessorBase.h +++ b/source/RobotAPI/libraries/armem/server/query_proc/base/ProviderSegmentQueryProcessorBase.h @@ -1,39 +1,50 @@ #pragma once -#include <RobotAPI/interface/armem/query.h> - #include "BaseQueryProcessorBase.h" -#include "EntityQueryProcessorBase.h" -#include <regex> +#include <RobotAPI/libraries/armem/core/error.h> -#include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <RobotAPI/interface/armem/query.h> -#include <RobotAPI/libraries/armem/core/error.h> +#include <regex> -namespace armarx::armem::base::query_proc +namespace armarx::armem::server::query_proc::base { - /** - * @brief Handles memory queries. - */ - template <class _ProviderSegmentT> + template <QueryTarget queryTarget, class _ProviderSegmentT, class _ResultProviderSegmentT, class _ChildProcessorT> class ProviderSegmentQueryProcessorBase : - virtual public BaseQueryProcessorBase<_ProviderSegmentT, armem::query::data::ProviderSegmentQuery> + public BaseQueryProcessorBase<queryTarget, _ProviderSegmentT, _ResultProviderSegmentT, armem::query::data::ProviderSegmentQuery> { - using Base = BaseQueryProcessorBase<_ProviderSegmentT, armem::query::data::ProviderSegmentQuery>; + using Base = BaseQueryProcessorBase<queryTarget, _ProviderSegmentT, _ResultProviderSegmentT, armem::query::data::ProviderSegmentQuery>; + public: + using ProviderSegmentT = _ProviderSegmentT; using EntityT = typename ProviderSegmentT::EntityT; - using EntitySnapshotT = typename EntityT::EntitySnapshotT; + + using ResultProviderSegmentT = _ResultProviderSegmentT; + using ResultEntityT = typename ResultProviderSegmentT::EntityT; + + using ChildProcessorT = _ChildProcessorT; + + + public: + + ProviderSegmentQueryProcessorBase() + { + } + ProviderSegmentQueryProcessorBase(ChildProcessorT&& childProcessor) : + childProcessor(childProcessor) + { + } + using Base::process; - void process(_ProviderSegmentT& result, - const armem::query::data::ProviderSegmentQuery& query, - const _ProviderSegmentT& providerSegment) const override + virtual void process(ResultProviderSegmentT& result, + const armem::query::data::ProviderSegmentQuery& query, + const ProviderSegmentT& providerSegment) const override { if (auto q = dynamic_cast<const armem::query::data::provider::All*>(&query)) { @@ -49,50 +60,60 @@ namespace armarx::armem::base::query_proc } else { - throw armem::error::UnknownQueryType("provider segment", query); + throw armem::error::UnknownQueryType(ProviderSegmentT::getLevelName(), query); } } - void process(_ProviderSegmentT& result, + void process(ResultProviderSegmentT& result, const armem::query::data::provider::All& query, - const _ProviderSegmentT& providerSegment) const + const ProviderSegmentT& providerSegment) const { - for (const auto& [name, entity] : providerSegment.entities()) + providerSegment.forEachEntity([this, &result, &query](const EntityT & entity) { - result.addEntity(entityProcessorProcess(query.entityQueries, entity)); - } + this->_processResult(result, entity, query); + }); } - void process(_ProviderSegmentT& result, + void process(ResultProviderSegmentT& result, const armem::query::data::provider::Single& query, - const _ProviderSegmentT& providerSegment) const + const ProviderSegmentT& providerSegment) const { - try - { - const EntityT& entity = providerSegment.getEntity(query.entityName); - result.addEntity(entityProcessorProcess(query.entityQueries, entity)); - } - catch (const error::MissingEntry&) + if (const EntityT* entity = providerSegment.findEntity(query.entityName)) { - // Leave empty. + this->_processResult(result, *entity, query); } } - void process(_ProviderSegmentT& result, + void process(ResultProviderSegmentT& result, const armem::query::data::provider::Regex& query, - const _ProviderSegmentT& providerSegment) const + const ProviderSegmentT& providerSegment) const { - std::regex regex(query.entityNameRegex); - for (const auto& [name, entity] : providerSegment.entities()) + const std::regex regex(query.entityNameRegex); + providerSegment.forEachEntity([this, &result, &query, ®ex](const EntityT & entity) { if (std::regex_search(entity.name(), regex)) { - result.addEntity(entityProcessorProcess(query.entityQueries, entity)); + this->_processResult(result, entity, query); } - } + return true; + }); } + + private: + + void _processResult(ResultProviderSegmentT& result, + const EntityT& entity, + const armem::query::data::ProviderSegmentQuery& query) const + { + ResultEntityT& added = result.addEntity(entity.name()); + childProcessor.process(added, query.entityQueries, entity); + } + + protected: - virtual EntityT entityProcessorProcess(const armem::query::data::EntityQuerySeq& a, const EntityT& o) const = 0; + + ChildProcessorT childProcessor; + }; } diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/BaseQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/BaseQueryProcessor.cpp deleted file mode 100644 index 68821bcd95cdf2aaccf7126d4055495d39d0393d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/BaseQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "BaseQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/BaseQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/BaseQueryProcessor.h deleted file mode 100644 index 0e3ddac45c6a8eee377f468767a3bc39a580c554..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/BaseQueryProcessor.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include <RobotAPI/interface/armem/query.h> -#include <RobotAPI/libraries/armem/core/DataMode.h> - -#include "../base/BaseQueryProcessorBase.h" - - -namespace armarx::armem::d_ltm::query_proc -{ - /** - * @brief Base class for memory query processors. - */ - template <class DataT, class QueryT> - class BaseQueryProcessor : - virtual public base::query_proc::BaseQueryProcessorBase<DataT, QueryT> - { - using Base = base::query_proc::BaseQueryProcessorBase<DataT, QueryT>; - - public: - BaseQueryProcessor() - {} - - protected: - - query::data::QueryTarget getTargetType() const override - { - return query::data::QueryTarget::LTM; - } - - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/CoreSegmentQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/CoreSegmentQueryProcessor.cpp deleted file mode 100644 index afbe35ad15eea0342b0b0d4df0200aaf0d32aa2a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/CoreSegmentQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "CoreSegmentQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/CoreSegmentQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/CoreSegmentQueryProcessor.h deleted file mode 100644 index 5c043a2d1dbe2218078591adc3fc0ebac0bfe932..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/CoreSegmentQueryProcessor.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/CoreSegmentQueryProcessorBase.h" - -#include "../../../core/diskmemory/CoreSegment.h" - -#include "ProviderSegmentQueryProcessor.h" - - -namespace armarx::armem::d_ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class CoreSegmentQueryProcessor : - virtual public BaseQueryProcessor<d_ltm::CoreSegment, armem::query::data::CoreSegmentQuery>, - virtual public base::query_proc::CoreSegmentQueryProcessorBase<d_ltm::CoreSegment> - { - using Base = BaseQueryProcessor<d_ltm::CoreSegment, armem::query::data::CoreSegmentQuery>; - - public: - CoreSegmentQueryProcessor() : - Base() - {} - - protected: - virtual ProviderSegmentT providerSegmentProcessorProcess(const armem::query::data::ProviderSegmentQuerySeq& q, const ProviderSegmentT& s) const override - { - return providerSegmentProcessor.process(q, s); - } - - private: - ProviderSegmentQueryProcessor providerSegmentProcessor; - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/EntityQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/EntityQueryProcessor.cpp deleted file mode 100644 index c1c321b026b173c6552758fb9d8b9fdf722ea5a4..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/EntityQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "EntityQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/EntityQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/EntityQueryProcessor.h deleted file mode 100644 index c57c2c2da39ca53ce97d27b498e69b648a16f25a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/EntityQueryProcessor.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/EntityQueryProcessorBase.h" - -#include "../../../core/diskmemory/Entity.h" - -#include "EntityQueryProcessor.h" - -namespace armarx::armem::d_ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class EntityQueryProcessor : - virtual public BaseQueryProcessor<d_ltm::Entity, armem::query::data::EntityQuery>, - virtual public base::query_proc::EntityQueryProcessorBase<d_ltm::Entity> - { - using Base = BaseQueryProcessor<d_ltm::Entity, armem::query::data::EntityQuery>; - - public: - - EntityQueryProcessor() : - Base() - {} - - - private: - - void addResultSnapshot(d_ltm::Entity& result, d_ltm::Entity::ContainerT::const_iterator it) const override - { - addResultSnapshot(result, it->second); - } - - void addResultSnapshot(d_ltm::Entity& result, const d_ltm::EntitySnapshot& snapshot) const override - { - result.addSnapshot(snapshot.copy()); - } - - }; - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.cpp deleted file mode 100644 index 69b04de6c9e623286a5bda836dddfdc8b551b64a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "MemoryQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.h deleted file mode 100644 index 038523950bfe399c2ef7a65b79fde43376651617..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/MemoryQueryProcessorBase.h" - -#include "../../../core/diskmemory/Memory.h" - -#include "CoreSegmentQueryProcessor.h" - -namespace armarx::armem::d_ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class MemoryQueryProcessor : - virtual public BaseQueryProcessor<d_ltm::Memory, armem::query::data::MemoryQuery>, - virtual public base::query_proc::MemoryQueryProcessorBase<d_ltm::Memory> - { - using Base = BaseQueryProcessor<d_ltm::Memory, armem::query::data::MemoryQuery>; - - public: - MemoryQueryProcessor() : - Base() - {} - - protected: - virtual CoreSegmentT coreSegmentProcessorProcess(const armem::query::data::CoreSegmentQuerySeq& q, const CoreSegmentT& s) const override - { - return coreSegmentProcessor.process(q, s); - } - - private: - CoreSegmentQueryProcessor coreSegmentProcessor; - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/ProviderSegmentQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/ProviderSegmentQueryProcessor.cpp deleted file mode 100644 index 9a2a4405001f0904b74fc6afcf96813eef0879cd..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/ProviderSegmentQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "ProviderSegmentQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/ProviderSegmentQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/ProviderSegmentQueryProcessor.h deleted file mode 100644 index 2043273ec7c5f3fee2d02410a145ef436c47459c..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/diskmemory/ProviderSegmentQueryProcessor.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/ProviderSegmentQueryProcessorBase.h" - -#include "../../../core/diskmemory/ProviderSegment.h" - -#include "EntityQueryProcessor.h" - -namespace armarx::armem::d_ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class ProviderSegmentQueryProcessor : - virtual public BaseQueryProcessor<d_ltm::ProviderSegment, armem::query::data::ProviderSegmentQuery>, - virtual public base::query_proc::ProviderSegmentQueryProcessorBase<d_ltm::ProviderSegment> - { - using Base = BaseQueryProcessor<d_ltm::ProviderSegment, armem::query::data::ProviderSegmentQuery>; - - public: - ProviderSegmentQueryProcessor() : - Base() - {} - - protected: - virtual EntityT entityProcessorProcess(const armem::query::data::EntityQuerySeq& q, const EntityT& s) const override - { - return entityProcessor.process(q, s); - } - - private: - EntityQueryProcessor entityProcessor; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/BaseQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/BaseQueryProcessor.cpp deleted file mode 100644 index 68821bcd95cdf2aaccf7126d4055495d39d0393d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/BaseQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "BaseQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/BaseQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/BaseQueryProcessor.h deleted file mode 100644 index 64f560afff93e5d0cfff4b036f68f0fb4e8cdaba..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/BaseQueryProcessor.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include <RobotAPI/interface/armem/query.h> -#include <RobotAPI/libraries/armem/core/DataMode.h> - -#include "../base/BaseQueryProcessorBase.h" - - -namespace armarx::armem::ltm::query_proc -{ - /** - * @brief Base class for memory query processors. - */ - template <class DataT, class QueryT> - class BaseQueryProcessor : - virtual public base::query_proc::BaseQueryProcessorBase<DataT, QueryT> - { - using Base = base::query_proc::BaseQueryProcessorBase<DataT, QueryT>; - - public: - BaseQueryProcessor() - {} - - protected: - - query::data::QueryTarget getTargetType() const override - { - return query::data::QueryTarget::LTM; - } - - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/CoreSegmentQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/CoreSegmentQueryProcessor.cpp deleted file mode 100644 index afbe35ad15eea0342b0b0d4df0200aaf0d32aa2a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/CoreSegmentQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "CoreSegmentQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/CoreSegmentQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/CoreSegmentQueryProcessor.h deleted file mode 100644 index dc379f1d88fa82bbe886e412e0341079fa43fce9..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/CoreSegmentQueryProcessor.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/CoreSegmentQueryProcessorBase.h" - -#include "../../../core/longtermmemory/CoreSegment.h" - -#include "ProviderSegmentQueryProcessor.h" - - -namespace armarx::armem::ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class CoreSegmentQueryProcessor : - virtual public BaseQueryProcessor<ltm::CoreSegment, armem::query::data::CoreSegmentQuery>, - virtual public base::query_proc::CoreSegmentQueryProcessorBase<ltm::CoreSegment> - { - using Base = BaseQueryProcessor<ltm::CoreSegment, armem::query::data::CoreSegmentQuery>; - - public: - CoreSegmentQueryProcessor() : - Base() - {} - - protected: - virtual ProviderSegmentT providerSegmentProcessorProcess(const armem::query::data::ProviderSegmentQuerySeq& q, const ProviderSegmentT& s) const override - { - return providerSegmentProcessor.process(q, s); - } - - private: - ProviderSegmentQueryProcessor providerSegmentProcessor; - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/EntityQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/EntityQueryProcessor.cpp deleted file mode 100644 index c1c321b026b173c6552758fb9d8b9fdf722ea5a4..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/EntityQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "EntityQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/EntityQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/EntityQueryProcessor.h deleted file mode 100644 index 5ec4c5dde481e16f0af30589d57ff2a447d81221..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/EntityQueryProcessor.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/EntityQueryProcessorBase.h" - -#include "../../../core/longtermmemory/Entity.h" - -#include "EntityQueryProcessor.h" - -namespace armarx::armem::ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class EntityQueryProcessor : - virtual public BaseQueryProcessor<ltm::Entity, armem::query::data::EntityQuery>, - virtual public base::query_proc::EntityQueryProcessorBase<ltm::Entity> - { - using Base = BaseQueryProcessor<ltm::Entity, armem::query::data::EntityQuery>; - - public: - EntityQueryProcessor() : - Base() - {} - - private: - void addResultSnapshot(ltm::Entity& result, ltm::Entity::ContainerT::const_iterator it) const override - { - addResultSnapshot(result, it->second); - } - - void addResultSnapshot(ltm::Entity& result, const ltm::EntitySnapshot& snapshot) const override - { - result.addSnapshot(snapshot.copy()); - } - - }; - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/MemoryQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/MemoryQueryProcessor.cpp deleted file mode 100644 index 69b04de6c9e623286a5bda836dddfdc8b551b64a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/MemoryQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "MemoryQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/MemoryQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/MemoryQueryProcessor.h deleted file mode 100644 index a5129417262515975efa247398602272865ee5ec..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/MemoryQueryProcessor.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/MemoryQueryProcessorBase.h" - -#include "../../../core/longtermmemory/Memory.h" - -#include "CoreSegmentQueryProcessor.h" - -namespace armarx::armem::ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class MemoryQueryProcessor : - virtual public BaseQueryProcessor<ltm::Memory, armem::query::data::MemoryQuery>, - virtual public base::query_proc::MemoryQueryProcessorBase<ltm::Memory> - { - using Base = BaseQueryProcessor<ltm::Memory, armem::query::data::MemoryQuery>; - - public: - MemoryQueryProcessor() : - Base() - {} - - protected: - virtual CoreSegmentT coreSegmentProcessorProcess(const armem::query::data::CoreSegmentQuerySeq& q, const CoreSegmentT& s) const override - { - return coreSegmentProcessor.process(q, s); - } - - private: - CoreSegmentQueryProcessor coreSegmentProcessor; - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.cpp deleted file mode 100644 index 9a2a4405001f0904b74fc6afcf96813eef0879cd..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "ProviderSegmentQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.h deleted file mode 100644 index e0c6db9e3ef06e6241e9af8c572bb7fc78eec452..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/longtermmemory/ProviderSegmentQueryProcessor.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include "BaseQueryProcessor.h" -#include "../base/ProviderSegmentQueryProcessorBase.h" - -#include "../../../core/longtermmemory/ProviderSegment.h" - -#include "EntityQueryProcessor.h" - -namespace armarx::armem::ltm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class ProviderSegmentQueryProcessor : - virtual public BaseQueryProcessor<ltm::ProviderSegment, armem::query::data::ProviderSegmentQuery>, - virtual public base::query_proc::ProviderSegmentQueryProcessorBase<ltm::ProviderSegment> - { - using Base = BaseQueryProcessor<ltm::ProviderSegment, armem::query::data::ProviderSegmentQuery>; - - public: - ProviderSegmentQueryProcessor() : - Base() - {} - - protected: - virtual EntityT entityProcessorProcess(const armem::query::data::EntityQuerySeq& q, const EntityT& s) const override - { - return entityProcessor.process(q, s); - } - - private: - EntityQueryProcessor entityProcessor; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/ltm.cpp b/source/RobotAPI/libraries/armem/server/query_proc/ltm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..238cc4b8d1601bf1ffae131caabbf8ca8435899a --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/query_proc/ltm.cpp @@ -0,0 +1,7 @@ +#include "ltm.h" + + +namespace armarx::armem::server::query_proc::ltm +{ +} + diff --git a/source/RobotAPI/libraries/armem/server/query_proc/ltm.h b/source/RobotAPI/libraries/armem/server/query_proc/ltm.h new file mode 100644 index 0000000000000000000000000000000000000000..05734c2e15d6836b69719283805fcfc5c1d7702d --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/query_proc/ltm.h @@ -0,0 +1,32 @@ +#pragma once + +#include <RobotAPI/libraries/armem/server/ltm/LongtermMemoryBase.h> +#include <RobotAPI/libraries/armem/server/query_proc/base.h> + + +namespace armarx::armem::server::query_proc::ltm +{ + static const base::QueryTarget queryTarget = query::data::QueryTarget::LTM; + + + class EntityQueryProcessor : + public base::EntityQueryProcessorBase<queryTarget, armem::wm::Entity, armem::wm::Entity> + { + }; + + class ProviderSegmentQueryProcessor : + public base::ProviderSegmentQueryProcessorBase <queryTarget, armem::wm::ProviderSegment, armem::wm::ProviderSegment, EntityQueryProcessor > + { + }; + + class CoreSegmentQueryProcessor : + public base::CoreSegmentQueryProcessorBase <queryTarget, armem::wm::CoreSegment, armem::wm::CoreSegment, ProviderSegmentQueryProcessor> + { + }; + + class MemoryQueryProcessor : + public base::MemoryQueryProcessorBase <queryTarget, armem::wm::Memory, armem::wm::Memory, CoreSegmentQueryProcessor > + { + }; + +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/wm.cpp b/source/RobotAPI/libraries/armem/server/query_proc/wm.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ace705be878087b9023ea574f50769489514cb5 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/query_proc/wm.cpp @@ -0,0 +1,72 @@ +#include "wm.h" + + + +namespace armarx::armem::server::query_proc::wm::detail +{ + + HasDataMode::HasDataMode(armem::DataMode dataMode) : dataMode(dataMode) + { + } + +} + + +namespace armarx::armem::server::query_proc::wm +{ + + ProviderSegmentQueryProcessor::ProviderSegmentQueryProcessor(DataMode dataMode) : + ProviderSegmentQueryProcessorBase(dataMode), HasDataMode(dataMode) + { + } + + + CoreSegmentQueryProcessor::CoreSegmentQueryProcessor(DataMode dataMode) : + CoreSegmentQueryProcessorBase(dataMode), HasDataMode(dataMode) + { + } + + + MemoryQueryProcessor::MemoryQueryProcessor(DataMode dataMode) : + MemoryQueryProcessorBase(dataMode), HasDataMode(dataMode) + { + } + +} + + +namespace armarx::armem::server::query_proc::wm_server +{ + ProviderSegmentQueryProcessor::ProviderSegmentQueryProcessor(DataMode dataMode) : + ProviderSegmentQueryProcessorBase(dataMode), HasDataMode(dataMode) + { + } + + + CoreSegmentQueryProcessor::CoreSegmentQueryProcessor(DataMode dataMode) : + CoreSegmentQueryProcessorBase(dataMode), + HasDataMode(dataMode) + { + } + + + void CoreSegmentQueryProcessor::process( + armem::wm::CoreSegment& result, + const armem::query::data::CoreSegmentQuery& query, + const CoreSegment& coreSegment) const + { + coreSegment.doLocked([&]() + { + CoreSegmentQueryProcessorBase::process(result, query, coreSegment); + }); + } + + + MemoryQueryProcessor::MemoryQueryProcessor(DataMode dataMode) : + MemoryQueryProcessorBase(dataMode), + HasDataMode(dataMode) + { + } + + +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/wm.h b/source/RobotAPI/libraries/armem/server/query_proc/wm.h new file mode 100644 index 0000000000000000000000000000000000000000..aa52b4ab22fe1c6ef194ef37d4e38f5c7720d6c5 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/query_proc/wm.h @@ -0,0 +1,164 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/DataMode.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/query_proc/base.h> + + +namespace armarx::armem::server::query_proc::wm +{ + static const base::QueryTarget queryTarget = query::data::QueryTarget::WM; +} +namespace armarx::armem::server::query_proc::wm::detail +{ + + class HasDataMode + { + public: + + HasDataMode(armem::DataMode dataMode); + + + protected: + + armem::DataMode dataMode; + + }; + + + + template <class SourceEntityT> + class EntityQueryProcessor : + public base::EntityQueryProcessorBase<queryTarget, SourceEntityT, armem::wm::Entity>, + public HasDataMode + { + public: + + EntityQueryProcessor(DataMode dataMode = DataMode::WithData) : + HasDataMode(dataMode) + {} + + + protected: + + void addResultSnapshot(armem::wm::Entity& result, const typename SourceEntityT::EntitySnapshotT& snapshot) const + { + bool withData = (dataMode == DataMode::WithData); + if (withData) + { + result.addSnapshot(server::wm::EntitySnapshot{ snapshot }); + } + else + { + server::wm::EntitySnapshot copy = snapshot; + copy.forEachInstance([](server::wm::EntityInstance & i) + { + i.data() = nullptr; + return true; + }); + result.addSnapshot(std::move(copy)); + } + } + + }; +} + + +namespace armarx::armem::server::query_proc::wm +{ + + using EntityQueryProcessor = detail::EntityQueryProcessor<armem::wm::Entity>; + + + class ProviderSegmentQueryProcessor : + public base::ProviderSegmentQueryProcessorBase<queryTarget, armem::wm::ProviderSegment, armem::wm::ProviderSegment, EntityQueryProcessor >, + public detail::HasDataMode + { + public: + + ProviderSegmentQueryProcessor(DataMode dataMode = DataMode::WithData); + + }; + + + class CoreSegmentQueryProcessor : + public base::CoreSegmentQueryProcessorBase <queryTarget, armem::wm::CoreSegment, armem::wm::CoreSegment, ProviderSegmentQueryProcessor >, + public detail::HasDataMode + { + using Base = base::CoreSegmentQueryProcessorBase<queryTarget, armem::wm::CoreSegment, armem::wm::CoreSegment, ProviderSegmentQueryProcessor>; + using CoreSegment = armem::wm::CoreSegment; + using ProviderSegment = armem::wm::ProviderSegment; + + public: + + CoreSegmentQueryProcessor(DataMode dataMode = DataMode::WithData); + + }; + + + class MemoryQueryProcessor : + public base::MemoryQueryProcessorBase<queryTarget, armem::wm::Memory, armem::wm::Memory, CoreSegmentQueryProcessor>, + public detail::HasDataMode + { + public: + + MemoryQueryProcessor(DataMode dataMode = DataMode::WithData); + + }; + +} + + +namespace armarx::armem::server::query_proc::wm_server +{ + + using EntityQueryProcessor = wm::detail::EntityQueryProcessor<server::wm::Entity>; + + + class ProviderSegmentQueryProcessor : + public base::ProviderSegmentQueryProcessorBase<wm::queryTarget, server::wm::ProviderSegment, armem::wm::ProviderSegment, EntityQueryProcessor >, + public wm::detail::HasDataMode + { + public: + + ProviderSegmentQueryProcessor(DataMode dataMode = DataMode::WithData); + + }; + + + class CoreSegmentQueryProcessor : + public base::CoreSegmentQueryProcessorBase <wm::queryTarget, server::wm::CoreSegment, armem::wm::CoreSegment, ProviderSegmentQueryProcessor >, + public wm::detail::HasDataMode + { + using Base = base::CoreSegmentQueryProcessorBase<wm::queryTarget, server::wm::CoreSegment, armem::wm::CoreSegment, ProviderSegmentQueryProcessor>; + using CoreSegment = server::wm::CoreSegment; + using ProviderSegment = server::wm::ProviderSegment; + + public: + + CoreSegmentQueryProcessor(DataMode dataMode = DataMode::WithData); + + + using Base::process; + + /// Locks the core segment, then delegates back to `CoreSegmentQueryProcessorBase`. + void process( + armem::wm::CoreSegment& result, + const armem::query::data::CoreSegmentQuery& query, + const CoreSegment& coreSegment) const override; + + }; + + + class MemoryQueryProcessor : + public base::MemoryQueryProcessorBase<wm::queryTarget, server::wm::Memory, armem::wm::Memory, CoreSegmentQueryProcessor>, + public wm::detail::HasDataMode + { + public: + + MemoryQueryProcessor(DataMode dataMode = DataMode::WithData); + + }; + +} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/BaseQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/BaseQueryProcessor.cpp deleted file mode 100644 index 68821bcd95cdf2aaccf7126d4055495d39d0393d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/BaseQueryProcessor.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "BaseQueryProcessor.h" diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/BaseQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/BaseQueryProcessor.h deleted file mode 100644 index d4875053c93dc2278cdb9f327fc0739370811fe7..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/BaseQueryProcessor.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include <RobotAPI/interface/armem/query.h> -#include <RobotAPI/libraries/armem/core/DataMode.h> - -#include <RobotAPI/libraries/armem/server/query_proc/base/BaseQueryProcessorBase.h> - - -namespace armarx::armem::wm::query_proc -{ - /** - * @brief Base class for memory query processors. - */ - template <class DataT, class QueryT> - class BaseQueryProcessor : - virtual public base::query_proc::BaseQueryProcessorBase<DataT, QueryT> - { - using Base = base::query_proc::BaseQueryProcessorBase<DataT, QueryT>; - - public: - - BaseQueryProcessor(DataMode dataMode = DataMode::WithData) : - dataMode(dataMode) - {} - - - protected: - - query::data::QueryTarget getTargetType() const override - { - return query::data::QueryTarget::WM; - } - - DataMode dataMode; - - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/CoreSegmentQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/CoreSegmentQueryProcessor.cpp deleted file mode 100644 index 4c41f16664af134b66b00355080d8de362d7eaf9..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/CoreSegmentQueryProcessor.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "CoreSegmentQueryProcessor.h" - -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> - - -namespace armarx::armem::wm::query_proc -{ - - CoreSegmentQueryProcessor::CoreSegmentQueryProcessor(DataMode dataMode) : - Base(dataMode), providerSegmentProcessor(dataMode) - {} - - - CoreSegmentQueryProcessor::~CoreSegmentQueryProcessor() = default; - - - void CoreSegmentQueryProcessor::process( - CoreSegment& result, const armem::query::data::CoreSegmentQuery& query, const CoreSegment& coreSegment) const - { - std::scoped_lock lock(coreSegment.mutex()); - CoreSegmentQueryProcessorBase::process(result, query, coreSegment); - } - - - data::CoreSegment CoreSegmentQueryProcessor::processToIce(const armem::query::data::CoreSegmentQuery& query, const wm::CoreSegment& coreSegment) const - { - data::CoreSegment data; - toIce(data, process(query, coreSegment)); - return data; - } - - - ProviderSegment CoreSegmentQueryProcessor::providerSegmentProcessorProcess(const armem::query::data::ProviderSegmentQuerySeq& q, const ProviderSegment& s) const - { - return providerSegmentProcessor.process(q, s); - } - - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/CoreSegmentQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/CoreSegmentQueryProcessor.h deleted file mode 100644 index f9e1d59c9f75866e3817440bb163ec5789c0f359..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/CoreSegmentQueryProcessor.h +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include <mutex> - -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> -#include <RobotAPI/libraries/armem/server/query_proc/base/CoreSegmentQueryProcessorBase.h> - -#include "BaseQueryProcessor.h" -#include "ProviderSegmentQueryProcessor.h" - - -namespace armarx::armem::wm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class CoreSegmentQueryProcessor : - virtual public BaseQueryProcessor<wm::CoreSegment, armem::query::data::CoreSegmentQuery>, - virtual public base::query_proc::CoreSegmentQueryProcessorBase<wm::CoreSegment> - { - using Base = BaseQueryProcessor<wm::CoreSegment, armem::query::data::CoreSegmentQuery>; - - public: - - CoreSegmentQueryProcessor(DataMode dataMode = DataMode::WithData); - virtual ~CoreSegmentQueryProcessor() override; - - using Base::process; - - /// Locks the core segment, then delegates back to `CoreSegmentQueryProcessorBase`. - void process(CoreSegment& result, - const armem::query::data::CoreSegmentQuery& query, - const CoreSegment& coreSegment) const override; - - data::CoreSegment processToIce(const armem::query::data::CoreSegmentQuery& query, const wm::CoreSegment& coreSegment) const; - - - protected: - - virtual ProviderSegment providerSegmentProcessorProcess(const armem::query::data::ProviderSegmentQuerySeq& q, const ProviderSegmentT& s) const override; - - - private: - - ProviderSegmentQueryProcessor providerSegmentProcessor; - - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/EntityQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/EntityQueryProcessor.cpp deleted file mode 100644 index 005649b8f670586f16177f90a43c6e0f48b3879d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/EntityQueryProcessor.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "EntityQueryProcessor.h" - -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> - - -namespace armarx::armem::wm::query_proc -{ - EntityQueryProcessor::EntityQueryProcessor(DataMode dataMode) : - BaseQueryProcessor<wm::Entity, armem::query::data::EntityQuery>(dataMode) - {} - - - EntityQueryProcessor::~EntityQueryProcessor() = default; - - - void EntityQueryProcessor::addResultSnapshot(wm::Entity& result, const wm::EntitySnapshot& snapshot) const - { - bool withData = (dataMode == DataMode::WithData); - if (withData) - { - result.addSnapshot(snapshot.copy()); - } - else - { - result.addSnapshot(snapshot.copyWithoutData()); - } - } - - - data::Entity EntityQueryProcessor::processToIce(const armem::query::data::EntityQuery& query, const wm::Entity& entity) const - { - data::Entity data; - toIce(data, process(query, entity)); - return data; - } - - - void EntityQueryProcessor::addResultSnapshot(wm::Entity& result, wm::Entity::ContainerT::const_iterator it) const - { - addResultSnapshot(result, it->second); - } - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/EntityQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/EntityQueryProcessor.h deleted file mode 100644 index dde53a8ff3393dde385ccc49aa937fcf196476d0..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/EntityQueryProcessor.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/armem/core/workingmemory/Entity.h> -#include <RobotAPI/libraries/armem/server/query_proc/base/EntityQueryProcessorBase.h> - -#include "BaseQueryProcessor.h" - - -namespace armarx::armem::wm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class EntityQueryProcessor : - virtual public BaseQueryProcessor<wm::Entity, armem::query::data::EntityQuery>, - virtual public base::query_proc::EntityQueryProcessorBase<wm::Entity> - { - using Base = BaseQueryProcessor<wm::Entity, armem::query::data::EntityQuery>; - - public: - - EntityQueryProcessor(DataMode dataMode = DataMode::WithData); - virtual ~EntityQueryProcessor() override; - - data::Entity processToIce(const armem::query::data::EntityQuery& query, const wm::Entity& entity) const; - - - private: - - void addResultSnapshot(wm::Entity& result, wm::Entity::ContainerT::const_iterator it) const override; - void addResultSnapshot(wm::Entity& result, const wm::EntitySnapshot& snapshot) const override; - - }; - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.cpp deleted file mode 100644 index 7aa868af13bc525b42d93f230a8398c27531525d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "MemoryQueryProcessor.h" - - -namespace armarx::armem::wm::query_proc -{ - - MemoryQueryProcessor::MemoryQueryProcessor(DataMode dataMode) : - Base(dataMode), coreSegmentProcessor(dataMode) - {} - - - MemoryQueryProcessor::~MemoryQueryProcessor() - { - } - - - CoreSegment MemoryQueryProcessor::coreSegmentProcessorProcess(const armem::query::data::CoreSegmentQuerySeq& q, const CoreSegment& s) const - { - return coreSegmentProcessor.process(q, s); - } - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.h deleted file mode 100644 index c57eea94a464a2e2191e614b5f1d6095d716405a..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/server/query_proc/base/MemoryQueryProcessorBase.h> - -#include "BaseQueryProcessor.h" - -#include "CoreSegmentQueryProcessor.h" - - -namespace armarx::armem::wm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class MemoryQueryProcessor : - virtual public BaseQueryProcessor<wm::Memory, armem::query::data::MemoryQuery>, - virtual public base::query_proc::MemoryQueryProcessorBase<wm::Memory> - { - using Base = BaseQueryProcessor<wm::Memory, armem::query::data::MemoryQuery>; - - public: - - MemoryQueryProcessor(DataMode dataMode = DataMode::WithData); - virtual ~MemoryQueryProcessor() override; - - - protected: - - virtual CoreSegment coreSegmentProcessorProcess( - const armem::query::data::CoreSegmentQuerySeq& q, const CoreSegment& s) const override; - - - private: - - CoreSegmentQueryProcessor coreSegmentProcessor; - - }; -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/ProviderSegmentQueryProcessor.cpp b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/ProviderSegmentQueryProcessor.cpp deleted file mode 100644 index eb2524631b4c1d2fb89eb3e7c36a3aaf86f31e29..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/ProviderSegmentQueryProcessor.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "ProviderSegmentQueryProcessor.h" - -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> - - -namespace armarx::armem::wm::query_proc -{ - - ProviderSegmentQueryProcessor::ProviderSegmentQueryProcessor(DataMode dataMode) : - Base(dataMode), entityProcessor(dataMode) - {} - - - ProviderSegmentQueryProcessor::~ProviderSegmentQueryProcessor() = default; - - - data::ProviderSegment ProviderSegmentQueryProcessor::processToIce(const armem::query::data::ProviderSegmentQuery& query, const wm::ProviderSegment& providerSegment) const - { - data::ProviderSegment data; - toIce(data, process(query, providerSegment)); - return data; - } - - - Entity ProviderSegmentQueryProcessor::entityProcessorProcess(const armem::query::data::EntityQuerySeq& q, const Entity& s) const - { - return entityProcessor.process(q, s); - } - -} diff --git a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/ProviderSegmentQueryProcessor.h b/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/ProviderSegmentQueryProcessor.h deleted file mode 100644 index 8e4f5a5e85d6c3c7f58433a0c2ddcd50074c7d7e..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem/server/query_proc/workingmemory/ProviderSegmentQueryProcessor.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h> -#include <RobotAPI/libraries/armem/server/query_proc/base/ProviderSegmentQueryProcessorBase.h> - -#include "BaseQueryProcessor.h" -#include "EntityQueryProcessor.h" - - -namespace armarx::armem::wm::query_proc -{ - /** - * @brief Handles memory queries. - */ - class ProviderSegmentQueryProcessor : - virtual public BaseQueryProcessor<wm::ProviderSegment, armem::query::data::ProviderSegmentQuery>, - virtual public base::query_proc::ProviderSegmentQueryProcessorBase<wm::ProviderSegment> - { - using Base = BaseQueryProcessor<wm::ProviderSegment, armem::query::data::ProviderSegmentQuery>; - - public: - - ProviderSegmentQueryProcessor(DataMode dataMode = DataMode::WithData); - virtual ~ProviderSegmentQueryProcessor() override; - - - using Base::process; - data::ProviderSegment processToIce(const armem::query::data::ProviderSegmentQuery& query, const wm::ProviderSegment& providerSegment) const; - - - protected: - - virtual Entity entityProcessorProcess(const armem::query::data::EntityQuerySeq& q, const Entity& s) const override; - - private: - - EntityQueryProcessor entityProcessor; - - }; -} diff --git a/source/RobotAPI/libraries/armem/server/segment/Segment.cpp b/source/RobotAPI/libraries/armem/server/segment/Segment.cpp index ec25ce7843a6cca3555dc9789f3570e0bfb51425..d538568c2430c9ffbd00c9b48bebcd46086280ea 100644 --- a/source/RobotAPI/libraries/armem/server/segment/Segment.cpp +++ b/source/RobotAPI/libraries/armem/server/segment/Segment.cpp @@ -1 +1,101 @@ #include "Segment.h" + +#include <ArmarXCore/core/application/properties/PluginAll.h> +#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> + +#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> +#include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h> + + +namespace armarx::armem::server::segment +{ + + wm::CoreSegmentBase::CoreSegmentBase( + armem::server::MemoryToIceAdapter& iceMemory, + const std::string& defaultCoreSegmentName, + aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType, + int defaultMaxHistorySize) : + Base(iceMemory), + p({defaultCoreSegmentName, defaultMaxHistorySize}), + coreSegmentAronType(coreSegmentAronType) + { + Logging::setTag("armarx::armem::wm::Segment"); + } + + + wm::CoreSegmentBase::~CoreSegmentBase() + { + } + + + void wm::CoreSegmentBase::defineProperties( + armarx::PropertyDefinitionsPtr defs, + const std::string& prefix) + { + ARMARX_CHECK_NOT_NULL(defs); + + defs->optional(p.coreSegmentName, prefix + "CoreSegmentName", "Name of the '" + p.coreSegmentName + "' core segment."); + defs->optional(p.maxHistorySize, prefix + "MaxHistorySize", "Maximal size of '" + p.coreSegmentName + "' history (-1 for infinite)."); + } + + + void wm::CoreSegmentBase::onInit() + { + ARMARX_CHECK_NOT_NULL(iceMemory.workingMemory); + + segment = &iceMemory.workingMemory->addCoreSegment(p.coreSegmentName, coreSegmentAronType); + segment->setMaxHistorySize(p.maxHistorySize); + } + + + wm::ProviderSegmentBase::ProviderSegmentBase( + armem::server::MemoryToIceAdapter& iceMemory, + const std::string& defaultProviderSegmentName, + aron::typenavigator::ObjectNavigatorPtr providerSegmentAronType, + const std::string& defaultCoreSegmentName, + aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType, + int defaultMaxHistorySize) : + + Base(iceMemory), + p({defaultCoreSegmentName, defaultProviderSegmentName, defaultMaxHistorySize}), + coreSegmentAronType(coreSegmentAronType), + providerSegmentAronType(providerSegmentAronType) + { + Logging::setTag("armarx::armem::wm::Segment"); + } + + + wm::ProviderSegmentBase::~ProviderSegmentBase() + { + } + + + void wm::ProviderSegmentBase::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) + { + ARMARX_CHECK_NOT_NULL(defs); + + defs->optional(p.coreSegmentName, prefix + "CoreSegmentName", "Name of the '" + p.coreSegmentName + "' core segment."); + defs->optional(p.providerSegmentName, prefix + "ProviderSegmentName", "Name of the '" + p.providerSegmentName + "' provider segment."); + defs->optional(p.maxHistorySize, prefix + "MaxHistorySize", "Maximal size of '" + p.providerSegmentName + "' history (-1 for infinite)."); + } + + + void wm::ProviderSegmentBase::onInit() + { + ARMARX_CHECK_NOT_NULL(iceMemory.workingMemory); + + if (!iceMemory.workingMemory->hasCoreSegment(p.coreSegmentName)) + { + coreSegment = &iceMemory.workingMemory->addCoreSegment(p.coreSegmentName, coreSegmentAronType); + coreSegment->setMaxHistorySize(p.maxHistorySize); + } + else + { + coreSegment = &iceMemory.workingMemory->getCoreSegment(p.coreSegmentName); + } + + segment = &coreSegment->addProviderSegment(p.providerSegmentName, providerSegmentAronType); + segment->setMaxHistorySize(p.maxHistorySize); + } + +} diff --git a/source/RobotAPI/libraries/armem/server/segment/Segment.h b/source/RobotAPI/libraries/armem/server/segment/Segment.h index 6cd9761f89e50ea1e23ce75b27263242e0fe4f29..dc35846aa89d14c125e5a6f3093e1a19ee85c6e2 100644 --- a/source/RobotAPI/libraries/armem/server/segment/Segment.h +++ b/source/RobotAPI/libraries/armem/server/segment/Segment.h @@ -1,15 +1,14 @@ #pragma once -// STD/STL -#include <mutex> -#include <string> +#include <RobotAPI/libraries/armem/server/forward_declarations.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> + +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> -// ArmarX #include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/application/properties/PluginAll.h> -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> +#include <ArmarXCore/core/application/properties/forward_declarations.h> -#include "../MemoryToIceAdapter.h" +#include <string> namespace armarx::armem::server::segment @@ -25,8 +24,9 @@ namespace armarx::armem::server::segment class SegmentBase : public armarx::Logging { public: + SegmentBase() = delete; - SegmentBase(armem::server::MemoryToIceAdapter& iceMemory) : + SegmentBase(MemoryToIceAdapter& iceMemory) : iceMemory(iceMemory) { Logging::setTag("armarx::armem::Segment"); @@ -36,69 +36,54 @@ namespace armarx::armem::server::segment virtual void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = "") = 0; virtual void onInit() = 0; - virtual void onConnect() = 0; + // virtual void onConnect() = 0; + protected: + // Memory connection - armem::server::MemoryToIceAdapter& iceMemory; + MemoryToIceAdapter& iceMemory; SegmentType* segment; - private: }; } + namespace wm { /** * @brief A base class for core segments */ - class CoreSegmentBase : public detail::SegmentBase<armarx::armem::wm::CoreSegment> + class CoreSegmentBase : public detail::SegmentBase<server::wm::CoreSegment> { - using Base = detail::SegmentBase<armarx::armem::wm::CoreSegment>; + using Base = detail::SegmentBase<server::wm::CoreSegment>; public: - CoreSegmentBase(armem::server::MemoryToIceAdapter& iceMemory, - const std::string& defaultCoreSegmentName = "", - aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType = nullptr, - int defaultMaxHistorySize = -1): - Base(iceMemory), - p({defaultCoreSegmentName, defaultMaxHistorySize}), - coreSegmentAronType(coreSegmentAronType) - { - Logging::setTag("armarx::armem::wm::Segment"); - } - virtual ~CoreSegmentBase() = default; + CoreSegmentBase( + MemoryToIceAdapter& iceMemory, + const std::string& defaultCoreSegmentName = "", + aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType = nullptr, + int defaultMaxHistorySize = -1); - virtual void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = "") override - { - ARMARX_CHECK_NOT_NULL(defs); + virtual ~CoreSegmentBase() override; - defs->optional(p.coreSegmentName, prefix + "CoreSegmentName", "Name of the '" + p.coreSegmentName + "' core segment."); - defs->optional(p.maxHistorySize, prefix + "MaxHistorySize", "Maximal size of '" + p.coreSegmentName + "' history (-1 for infinite)."); - } - virtual void onInit() override - { - ARMARX_CHECK_NOT_NULL(iceMemory.workingMemory); + virtual void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = "") override; + virtual void onInit() override; + // virtual void onConnect() override; - segment = &iceMemory.workingMemory->addCoreSegment(p.coreSegmentName, coreSegmentAronType); - segment->setMaxHistorySize(p.maxHistorySize); - } - virtual void onConnect() override + template <class FunctionT> + auto doLocked(FunctionT&& function) const { - + return segment->doLocked(function); } - std::mutex& mutex() const - { - ARMARX_CHECK_NOT_NULL(segment); - return segment->mutex(); - } protected: + struct Properties { std::string coreSegmentName; @@ -107,66 +92,37 @@ namespace armarx::armem::server::segment Properties p; aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType; + }; /** * @brief A base class for provider segments */ - class ProviderSegmentBase : public detail::SegmentBase<armarx::armem::wm::ProviderSegment> + class ProviderSegmentBase : public detail::SegmentBase<server::wm::ProviderSegment> { - using Base = detail::SegmentBase<armarx::armem::wm::ProviderSegment>; + using Base = detail::SegmentBase<server::wm::ProviderSegment>; public: - ProviderSegmentBase(armem::server::MemoryToIceAdapter& iceMemory, - const std::string& defaultProviderSegmentName = "", - aron::typenavigator::ObjectNavigatorPtr providerSegmentAronType = nullptr, - const std::string& defaultCoreSegmentName = "", - aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType = nullptr, - int defaultMaxHistorySize = -1): - Base(iceMemory), - p({defaultCoreSegmentName, defaultProviderSegmentName, defaultMaxHistorySize}), - coreSegmentAronType(coreSegmentAronType), - providerSegmentAronType(providerSegmentAronType) - { - Logging::setTag("armarx::armem::wm::Segment"); - } - - virtual ~ProviderSegmentBase() = default; - virtual void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = "") override - { - ARMARX_CHECK_NOT_NULL(defs); + ProviderSegmentBase( + MemoryToIceAdapter& iceMemory, + const std::string& defaultProviderSegmentName = "", + aron::typenavigator::ObjectNavigatorPtr providerSegmentAronType = nullptr, + const std::string& defaultCoreSegmentName = "", + aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType = nullptr, + int defaultMaxHistorySize = -1); - defs->optional(p.coreSegmentName, prefix + "CoreSegmentName", "Name of the '" + p.coreSegmentName + "' core segment."); - defs->optional(p.providerSegmentName, prefix + "ProviderSegmentName", "Name of the '" + p.providerSegmentName + "' provider segment."); - defs->optional(p.maxHistorySize, prefix + "MaxHistorySize", "Maximal size of '" + p.providerSegmentName + "' history (-1 for infinite)."); - } + virtual ~ProviderSegmentBase() override; - virtual void onInit() override - { - ARMARX_CHECK_NOT_NULL(iceMemory.workingMemory); - - if (!iceMemory.workingMemory->hasCoreSegment(p.coreSegmentName)) - { - coreSegment = &iceMemory.workingMemory->addCoreSegment(p.coreSegmentName, coreSegmentAronType); - coreSegment->setMaxHistorySize(p.maxHistorySize); - } - else - { - coreSegment = &iceMemory.workingMemory->getCoreSegment(p.coreSegmentName); - } - - segment = &coreSegment->addProviderSegment(p.providerSegmentName, providerSegmentAronType); - segment->setMaxHistorySize(p.maxHistorySize); - } - virtual void onConnect() override - { + virtual void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = "") override; + virtual void onInit() override; + // virtual void onConnect() override; - } protected: + struct Properties { std::string coreSegmentName; @@ -178,10 +134,11 @@ namespace armarx::armem::server::segment aron::typenavigator::ObjectNavigatorPtr coreSegmentAronType; aron::typenavigator::ObjectNavigatorPtr providerSegmentAronType; - armarx::armem::wm::CoreSegment* coreSegment; + server::wm::CoreSegment* coreSegment; }; + /** * @brief A base class for provider segments with an aron business object */ @@ -191,10 +148,13 @@ namespace armarx::armem::server::segment using Base = CoreSegmentBase; public: - AronTypedCoreSegmentBase(armem::server::MemoryToIceAdapter& iceMemory, - const std::string& defaultCoreSegmentName = "", - int defaultMaxHistorySize = -1): - Base(iceMemory, defaultCoreSegmentName, BusinessClassObject::toAronType(), defaultMaxHistorySize) + + AronTypedCoreSegmentBase( + MemoryToIceAdapter& iceMemory, + const std::string& defaultCoreSegmentName = "", + int defaultMaxHistorySize = -1) : + Base(iceMemory, defaultCoreSegmentName, BusinessClassObject::toAronType(), + defaultMaxHistorySize) { } @@ -202,6 +162,7 @@ namespace armarx::armem::server::segment }; + /** * @brief A base class for provider segments with an aron business object */ @@ -211,15 +172,19 @@ namespace armarx::armem::server::segment using Base = ProviderSegmentBase; public: - AronTypedProviderSegmentBase(armem::server::MemoryToIceAdapter& iceMemory, - const std::string& defaultProviderSegmentName = "", - const std::string& defaultCoreSegmentName = "", - int defaultMaxHistorySize = -1): - Base(iceMemory, defaultProviderSegmentName, BusinessClassObject::toAronType(), defaultCoreSegmentName, nullptr, defaultMaxHistorySize) + + AronTypedProviderSegmentBase( + MemoryToIceAdapter& iceMemory, + const std::string& defaultProviderSegmentName = "", + const std::string& defaultCoreSegmentName = "", + int defaultMaxHistorySize = -1) : + Base(iceMemory, defaultProviderSegmentName, BusinessClassObject::toAronType(), + defaultCoreSegmentName, nullptr, defaultMaxHistorySize) { } virtual ~AronTypedProviderSegmentBase() = default; + }; } } diff --git a/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.cpp b/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.cpp index 8184e27c985877a19aaa873db07883685140b17b..50f5ae6451bb49f9a4356579d78618a865297881 100644 --- a/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.cpp +++ b/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.cpp @@ -12,11 +12,11 @@ namespace armarx::armem::server::segment { SpecializedSegment::SpecializedSegment( - server::MemoryToIceAdapter& iceMemory, - aron::typenavigator::ObjectNavigatorPtr aronType, - const std::string& defaultCoreSegmentName, - int64_t defaultMaxHistorySize - ) : + server::MemoryToIceAdapter& iceMemory, + aron::typenavigator::ObjectNavigatorPtr aronType, + const std::string& defaultCoreSegmentName, + int64_t defaultMaxHistorySize + ) : iceMemory(iceMemory), aronType(aronType) { setDefaultCoreSegmentName(defaultCoreSegmentName); @@ -52,13 +52,6 @@ namespace armarx::armem::server::segment } - std::mutex& SpecializedSegment::mutex() const - { - ARMARX_CHECK_NOT_NULL(coreSegment); - return coreSegment->mutex(); - } - - void SpecializedSegment::setDefaultCoreSegmentName(const std::string& coreSegmentName) { this->properties.coreSegmentName = coreSegmentName; diff --git a/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.h b/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.h index fd3a456c55d785471e86a0d3f1e12a5491d3e32c..271536db21ffdfadb3fba874f60bdd8a33d21f44 100644 --- a/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.h +++ b/source/RobotAPI/libraries/armem/server/segment/SpecializedSegment.h @@ -1,28 +1,14 @@ #pragma once -#include <mutex> -#include <string> +#include <RobotAPI/libraries/armem/server/forward_declarations.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> #include <ArmarXCore/core/application/properties/forward_declarations.h> #include <ArmarXCore/core/logging/Logging.h> +#include <string> -namespace armarx::aron::typenavigator -{ - using ObjectNavigatorPtr = std::shared_ptr<class ObjectNavigator>; -} -namespace armarx::armem -{ - namespace server - { - class MemoryToIceAdapter; - } - - namespace wm - { - class CoreSegment; - } -} namespace armarx::armem::server::segment { @@ -31,15 +17,15 @@ namespace armarx::armem::server::segment * @brief Specialized management of a core segment. */ class SpecializedSegment : - public armarx::Logging + public armarx::Logging { public: SpecializedSegment( - server::MemoryToIceAdapter& iceMemory, - aron::typenavigator::ObjectNavigatorPtr aronType = nullptr, - const std::string& defaultCoreSegmentName = "", - int64_t defaultMaxHistorySize = -1); + server::MemoryToIceAdapter& iceMemory, + aron::typenavigator::ObjectNavigatorPtr aronType = nullptr, + const std::string& defaultCoreSegmentName = "", + int64_t defaultMaxHistorySize = -1); virtual ~SpecializedSegment(); @@ -48,7 +34,12 @@ namespace armarx::armem::server::segment virtual void init(); // void connect(); - std::mutex& mutex() const; + + template <class FunctionT> + auto doLocked(FunctionT&& function) const + { + return coreSegment->doLocked(function); + } protected: @@ -61,7 +52,7 @@ namespace armarx::armem::server::segment protected: server::MemoryToIceAdapter& iceMemory; - wm::CoreSegment* coreSegment = nullptr; + server::wm::CoreSegment* coreSegment = nullptr; aron::typenavigator::ObjectNavigatorPtr aronType; struct Properties diff --git a/source/RobotAPI/libraries/armem/core/base/detail/MaxHistorySize.cpp b/source/RobotAPI/libraries/armem/server/wm/detail/MaxHistorySize.cpp similarity index 72% rename from source/RobotAPI/libraries/armem/core/base/detail/MaxHistorySize.cpp rename to source/RobotAPI/libraries/armem/server/wm/detail/MaxHistorySize.cpp index 2c3b0554c65523826db0a7709599ed8e5140e0a1..d90756691871b6ad444a67fa76a733d0086b5b29 100644 --- a/source/RobotAPI/libraries/armem/core/base/detail/MaxHistorySize.cpp +++ b/source/RobotAPI/libraries/armem/server/wm/detail/MaxHistorySize.cpp @@ -1,12 +1,8 @@ #include "MaxHistorySize.h" -namespace armarx::armem::base::detail +namespace armarx::armem::server::detail { - MaxHistorySize::~MaxHistorySize() - { - } - void MaxHistorySize::setMaxHistorySize(long maxSize) { this->_maxHistorySize = maxSize; diff --git a/source/RobotAPI/libraries/armem/server/wm/detail/MaxHistorySize.h b/source/RobotAPI/libraries/armem/server/wm/detail/MaxHistorySize.h new file mode 100644 index 0000000000000000000000000000000000000000..962179a68aeb601dc261eb6d496389295597a2ea --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/wm/detail/MaxHistorySize.h @@ -0,0 +1,56 @@ +#pragma once + +namespace armarx::armem::server::detail +{ + // TODO: Replace by ConstrainedHistorySize (not only max entries, e.g. delete oldest / delete least accessed / ...) + + class MaxHistorySize + { + public: + + /** + * @brief Set the maximum number of snapshots to be contained in an entity. + * Affected entities are to be update right away. + */ + void setMaxHistorySize(long maxSize); + + long getMaxHistorySize() const; + + + protected: + + /** + * @brief Maximum size of entity histories. + * + * If negative, the size of `history` is not limited. + * + * @see Entity::maxHstorySize + */ + long _maxHistorySize = -1; + + }; + + + + template <class DerivedT> + class MaxHistorySizeParent : public MaxHistorySize + { + public: + + /** + * @brief Sets the maximum history size of entities in this container. + * This affects all current entities as well as new ones. + * + * @see MaxHistorySize::setMaxHistorySize() + */ + void setMaxHistorySize(long maxSize) + { + MaxHistorySize::setMaxHistorySize(maxSize); + static_cast<DerivedT&>(*this).forEachChild([maxSize](auto & child) + { + child.setMaxHistorySize(maxSize); + }); + } + + }; +} diff --git a/source/RobotAPI/libraries/armem/server/wm/ice_conversions.cpp b/source/RobotAPI/libraries/armem/server/wm/ice_conversions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1ba3352d6e8bb443693ebfa5752a46cad9c24267 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/wm/ice_conversions.cpp @@ -0,0 +1,64 @@ +#include "ice_conversions.h" + +#include <RobotAPI/libraries/armem/core/base/ice_conversions.h> + + +namespace armarx::armem::server +{ + + void wm::toIce(data::EntityInstance& ice, const EntityInstance& data) + { + base::toIce(ice, data); + } + void wm::fromIce(const data::EntityInstance& ice, EntityInstance& data) + { + base::fromIce(ice, data); + } + + + void wm::toIce(data::EntitySnapshot& ice, const EntitySnapshot& snapshot) + { + base::toIce(ice, snapshot); + } + void wm::fromIce(const data::EntitySnapshot& ice, EntitySnapshot& snapshot) + { + base::fromIce(ice, snapshot); + } + + void wm::toIce(data::Entity& ice, const Entity& entity) + { + base::toIce(ice, entity); + } + void wm::fromIce(const data::Entity& ice, Entity& entity) + { + base::fromIce(ice, entity); + } + + void wm::toIce(data::ProviderSegment& ice, const ProviderSegment& providerSegment) + { + base::toIce(ice, providerSegment); + } + void wm::fromIce(const data::ProviderSegment& ice, ProviderSegment& providerSegment) + { + base::fromIce(ice, providerSegment); + } + + void wm::toIce(data::CoreSegment& ice, const CoreSegment& coreSegment) + { + base::toIce(ice, coreSegment); + } + void wm::fromIce(const data::CoreSegment& ice, CoreSegment& coreSegment) + { + base::fromIce(ice, coreSegment); + } + + void wm::toIce(data::Memory& ice, const Memory& memory) + { + base::toIce(ice, memory); + } + void wm::fromIce(const data::Memory& ice, Memory& memory) + { + base::fromIce(ice, memory); + } + +} diff --git a/source/RobotAPI/libraries/armem/server/wm/ice_conversions.h b/source/RobotAPI/libraries/armem/server/wm/ice_conversions.h new file mode 100644 index 0000000000000000000000000000000000000000..c25b16775bc577c61d0c804cf4525b23ecd5e5d1 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/wm/ice_conversions.h @@ -0,0 +1,30 @@ +#pragma once + +#include <RobotAPI/interface/armem/memory.h> + +#include "memory_definitions.h" + + +namespace armarx::armem::server::wm +{ + + void toIce(data::EntityInstance& ice, const EntityInstance& data); + void fromIce(const data::EntityInstance& ice, EntityInstance& data); + + + void toIce(data::EntitySnapshot& ice, const EntitySnapshot& snapshot); + void fromIce(const data::EntitySnapshot& ice, EntitySnapshot& snapshot); + + void toIce(data::Entity& ice, const Entity& entity); + void fromIce(const data::Entity& ice, Entity& entity); + + + void toIce(data::ProviderSegment& ice, const ProviderSegment& providerSegment); + void fromIce(const data::ProviderSegment& ice, ProviderSegment& providerSegment); + + void toIce(data::CoreSegment& ice, const CoreSegment& coreSegment); + void fromIce(const data::CoreSegment& ice, CoreSegment& coreSegment); + + void toIce(data::Memory& ice, const Memory& memory); + void fromIce(const data::Memory& ice, Memory& memory); +} diff --git a/source/RobotAPI/libraries/armem/core/workingmemory/Memory.cpp b/source/RobotAPI/libraries/armem/server/wm/memory_definitions.cpp similarity index 58% rename from source/RobotAPI/libraries/armem/core/workingmemory/Memory.cpp rename to source/RobotAPI/libraries/armem/server/wm/memory_definitions.cpp index 3d0888c7ff4d2f1b46f54e9d51a1a3c62b7dd395..c2dc4f6b38abdd3de9e67afe0f7031f2209d2055 100644 --- a/source/RobotAPI/libraries/armem/core/workingmemory/Memory.cpp +++ b/source/RobotAPI/libraries/armem/server/wm/memory_definitions.cpp @@ -1,12 +1,48 @@ -#include "Memory.h" +#include "memory_definitions.h" + +#include "error.h" + +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <map> #include <vector> -namespace armarx::armem::wm +namespace armarx::armem::server::wm { + void Entity::setMaxHistorySize(long maxSize) + { + MaxHistorySize::setMaxHistorySize(maxSize); + truncate(); + } + + + auto Entity::update(const EntityUpdate& update) -> UpdateResult + { + UpdateResult result = EntityBase::update(update); + result.removedSnapshots = this->truncate(); + return result; + } + + + std::vector<EntitySnapshot> Entity::truncate() + { + std::vector<EntitySnapshot> removedElements; + if (_maxHistorySize >= 0) + { + while (this->_container.size() > size_t(_maxHistorySize)) + { + removedElements.push_back(std::move(this->_container.begin()->second)); + this->_container.erase(this->_container.begin()); + } + ARMARX_CHECK_LESS_EQUAL(this->_container.size(), size_t(_maxHistorySize)); + } + return removedElements; + } + + // TODO: add core segment if param is set std::vector<Memory::Base::UpdateResult> Memory::updateLocking(const Commit& commit) @@ -29,15 +65,16 @@ namespace armarx::armem::wm CoreSegment& coreSegment = it->second; // Lock the core segment for the whole batch. - std::scoped_lock lock(coreSegment.mutex()); - - for (const EntityUpdate* update : updates) + coreSegment.doLocked([&result, &coreSegment, updates = &updates]() { - auto r = coreSegment.update(*update); - Base::UpdateResult ret { r }; - ret.memoryUpdateType = UpdateType::UpdatedExisting; - result.push_back(ret); - } + for (const EntityUpdate* update : *updates) + { + auto r = coreSegment.update(*update); + Base::UpdateResult ret { r }; + ret.memoryUpdateType = UpdateType::UpdatedExisting; + result.push_back(ret); + } + }); } else { @@ -55,11 +92,6 @@ namespace armarx::armem::wm } - void Memory::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) - { - } - - // TODO: Add core segment if param is set Memory::Base::UpdateResult Memory::updateLocking(const EntityUpdate& update) @@ -68,33 +100,12 @@ namespace armarx::armem::wm CoreSegment& segment = getCoreSegment(update.entityID.coreSegmentName); Base::UpdateResult result; + segment.doLocked([&result, &segment, &update]() { - std::scoped_lock lock(segment.mutex()); result = segment.update(update); - } + }); result.memoryUpdateType = UpdateType::UpdatedExisting; return result; } - - Commit Memory::toCommit() const - { - Commit c; - for (const auto& [k, s] : _container) - { - c.append(s.toCommit()); - } - return c; - } - - - void Memory::_copySelfWithoutData(Memory& other) const - { - other.id() = _id; - for (const auto& [k, s] : _container) - { - other.addCoreSegment(s.copyWithoutData()); - } - } - } diff --git a/source/RobotAPI/libraries/armem/server/wm/memory_definitions.h b/source/RobotAPI/libraries/armem/server/wm/memory_definitions.h new file mode 100644 index 0000000000000000000000000000000000000000..029d185c62445a5a83dd749d1b86f2393f95e6d2 --- /dev/null +++ b/source/RobotAPI/libraries/armem/server/wm/memory_definitions.h @@ -0,0 +1,152 @@ +#pragma once + +#include "detail/MaxHistorySize.h" + +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/base/EntityInstanceBase.h> +#include <RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h> +#include <RobotAPI/libraries/armem/core/base/EntityBase.h> +#include <RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h> +#include <RobotAPI/libraries/armem/core/base/CoreSegmentBase.h> +#include <RobotAPI/libraries/armem/core/base/MemoryBase.h> + +#include <RobotAPI/libraries/armem/core/wm/detail/data_lookup_mixins.h> + +#include <mutex> + + +namespace armarx::armem::server::wm +{ + using EntityInstanceMetadata = base::EntityInstanceMetadata; + using EntityInstanceData = armarx::aron::datanavigator::DictNavigator; + using EntityInstanceDataPtr = armarx::aron::datanavigator::DictNavigatorPtr; + + using EntityInstance = armem::wm::EntityInstance; + using EntitySnapshot = armem::wm::EntitySnapshot; + + + /// @see base::EntityBase + class Entity : + public base::EntityBase<EntitySnapshot, Entity> + , public detail::MaxHistorySize + , public armem::wm::detail::FindInstanceDataMixinForEntity<Entity> + { + public: + + using base::EntityBase<EntitySnapshot, Entity>::EntityBase; + + + /** + * @brief Sets the maximum history size. + * + * The current history is truncated if necessary. + */ + void setMaxHistorySize(long maxSize); + + UpdateResult update(const EntityUpdate& update); + + + protected: + + /// If maximum size is set, ensure `history`'s is not higher. + std::vector<EntitySnapshotT> truncate(); + + }; + + + + /// @see base::ProviderSegmentBase + class ProviderSegment : + public base::ProviderSegmentBase<Entity, ProviderSegment> + , public detail::MaxHistorySizeParent<ProviderSegment> + , public armem::wm::detail::FindInstanceDataMixin<ProviderSegment> + { + public: + + using base::ProviderSegmentBase<Entity, ProviderSegment>::ProviderSegmentBase; + + + using ProviderSegmentBase::addEntity; + + template <class ...Args> + Entity& addEntity(const std::string& name, Args... args) + { + Entity& added = ProviderSegmentBase::addEntity(name, args...); + added.setMaxHistorySize(this->getMaxHistorySize()); + return added; + } + + }; + + + + /// @brief base::CoreSegmentBase + class CoreSegment : + public base::CoreSegmentBase<ProviderSegment, CoreSegment>, + public detail::MaxHistorySizeParent<CoreSegment> + , public armem::wm::detail::FindInstanceDataMixin<CoreSegment> + { + using Base = base::CoreSegmentBase<ProviderSegment, CoreSegment>; + + public: + + using Base::CoreSegmentBase; + + /// @see base::CoreSegmentBase::addProviderSegment() + using CoreSegmentBase::addProviderSegment; + template <class ...Args> + ProviderSegment& addProviderSegment(const std::string& name, Args... args) + { + ProviderSegmentT& added = CoreSegmentBase::addProviderSegment(name, args...); + added.setMaxHistorySize(this->getMaxHistorySize()); + return added; + } + + + // Locking interface + + template <class FunctionT> + auto doLocked(FunctionT&& function) const + { + std::scoped_lock lock(_mutex); + return function(); + } + + + private: + + mutable std::mutex _mutex; + + }; + + + + /// @see base::MemoryBase + class Memory : + public base::MemoryBase<CoreSegment, Memory> + , public armem::wm::detail::FindInstanceDataMixin<Memory> + { + using Base = base::MemoryBase<CoreSegment, Memory>; + + public: + + using Base::MemoryBase; + + + /** + * @brief Perform the commit, locking the core segments. + * + * Groups the commits by core segment, and updates each core segment + * in a batch, locking the core segment. + */ + std::vector<Base::UpdateResult> updateLocking(const Commit& commit); + + /** + * @brief Update the memory, locking the updated core segment. + */ + Base::UpdateResult updateLocking(const EntityUpdate& update); + + }; + +} + diff --git a/source/RobotAPI/libraries/armem/test/ArMemForEachTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemForEachTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9c29804ebdc56444b175b3de0abd115b3a1067c0 --- /dev/null +++ b/source/RobotAPI/libraries/armem/test/ArMemForEachTest.cpp @@ -0,0 +1,188 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::armem + * @author Simon Ottenhaus ( simon dot ottenhaus at kit dot edu ) + * @date 2020 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#define BOOST_TEST_MODULE RobotAPI::ArmarXLibraries::armem + +#define ARMARX_BOOST_TEST + +#include <RobotAPI/Test.h> +#include <RobotAPI/libraries/armem/core/Commit.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/operations.h> + +#include <iostream> +#include <set> + + +namespace armem = armarx::armem; + + +template <class ValueT> +static +std::vector<ValueT> toVector(const std::set<ValueT>& set) +{ + return {set.begin(), set.end()}; +} + + + +BOOST_AUTO_TEST_CASE(test_forEach) +{ + using namespace armarx::armem; + + const MemoryID mid("memory"); + const std::set<MemoryID> emptySet; // For comparison. + + wm::Memory memory(mid); + std::set<MemoryID> cids, pids, eids, sids, iids; + + for (size_t c = 0; c <= 2; ++c) + { + const MemoryID cid = mid.withCoreSegmentName("core_" + std::to_string(c)); + cids.insert(cid); + wm::CoreSegment& coreSeg = memory.addCoreSegment(cid.coreSegmentName); + for (size_t p = 0; p <= c; ++p) + { + const MemoryID pid = cid.withProviderSegmentName("prov_" + std::to_string(p)); + pids.insert(pid); + for (size_t e = 0; e <= p; ++e) + { + const MemoryID eid = pid.withEntityName("entity_" + std::to_string(e)); + eids.insert(eid); + for (size_t s = 0; s <= e; ++s) + { + const MemoryID sid = eid.withTimestamp(Time::microSeconds(int(s))); + sids.insert(sid); + + EntityUpdate update; + update.entityID = eid; + update.timeCreated = sid.timestamp; + for (size_t i = 0; i <= s; ++i) + { + const MemoryID iid = sid.withInstanceIndex(int(i)); + iids.insert(iid); + update.instancesData.emplace_back(); + } + auto r = memory.update(update); + + BOOST_CHECK(coreSeg.hasProviderSegment(pid.providerSegmentName)); + } + } + + BOOST_CHECK_EQUAL(coreSeg.size(), p + 1); + } + BOOST_CHECK_EQUAL(memory.size(), c + 1); + } + + BOOST_CHECK_GT(iids.size(), 0); + BOOST_CHECK_GT(sids.size(), 0); + BOOST_CHECK_GT(eids.size(), 0); + BOOST_CHECK_GT(pids.size(), 0); + BOOST_CHECK_GT(cids.size(), 0); + + BOOST_TEST_MESSAGE("Memory: \n" << armem::print(memory)); + + memory.forEachInstance([&](const wm::EntityInstance & i) -> bool + { + BOOST_TEST_CONTEXT(wm::EntityInstance::getLevelName() << " " << i.id()) + { + BOOST_TEST_INFO(toVector(iids)); + BOOST_CHECK_EQUAL(iids.count(i.id()), 1); + iids.erase(i.id()); + } + return true; + }); + BOOST_TEST_CONTEXT(toVector(iids)) + { + BOOST_CHECK_EQUAL(iids.size(), 0); + } + + memory.forEachSnapshot([&](const wm::EntitySnapshot & s) -> bool + { + BOOST_TEST_CONTEXT(wm::EntitySnapshot::getLevelName() << " " << s.id()) + { + BOOST_TEST_INFO(toVector(sids)); + BOOST_CHECK_EQUAL(sids.count(s.id()), 1); + sids.erase(s.id()); + } + return true; + }); + BOOST_TEST_INFO(toVector(sids)); + BOOST_CHECK_EQUAL(sids.size(), 0); + + memory.forEachEntity([&](const wm::Entity & e) -> bool + { + BOOST_TEST_CONTEXT(wm::Entity::getLevelName() << " " << e.id()) + { + BOOST_TEST_INFO(toVector(eids)); + BOOST_CHECK_EQUAL(eids.count(e.id()), 1); + eids.erase(e.id()); + } + return true; + }); + BOOST_TEST_INFO(toVector(eids)); + BOOST_CHECK_EQUAL(eids.size(), 0); + + memory.forEachProviderSegment([&](const wm::ProviderSegment & p) // -> bool + { + BOOST_TEST_CONTEXT(wm::ProviderSegment::getLevelName() << " " << p.id()) + { + BOOST_TEST_INFO(toVector(pids)); + BOOST_CHECK_EQUAL(pids.count(p.id()), 1); + pids.erase(p.id()); + } + return true; + }); + BOOST_TEST_INFO(toVector(pids)); + BOOST_CHECK_EQUAL(pids.size(), 0); + + memory.forEachCoreSegment([&](const wm::CoreSegment & c) // -> bool + { + BOOST_TEST_CONTEXT(wm::CoreSegment::getLevelName() << " " << c.id()) + { + BOOST_TEST_INFO(toVector(cids)); + BOOST_CHECK_EQUAL(cids.count(c.id()), 1); + cids.erase(c.id()); + } + return true; + }); + BOOST_TEST_INFO(toVector(cids)); + BOOST_CHECK_EQUAL(cids.size(), 0); + +} + + +BOOST_AUTO_TEST_CASE(test_forEach_non_bool_func) +{ + // Check whether this compiles + runs without break. + + armem::wm::Entity entity; + entity.addSnapshot(armem::Time::microSeconds(500)); + entity.addSnapshot(armem::Time::microSeconds(1500)); + + int i = 0; + entity.forEachSnapshot([&i](const armem::wm::EntitySnapshot&) -> void + { + ++i; + }); + BOOST_CHECK_EQUAL(i, 2); +} diff --git a/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..29c12884d15292706ac2be33410352fe832dbc2a --- /dev/null +++ b/source/RobotAPI/libraries/armem/test/ArMemGetFindTest.cpp @@ -0,0 +1,347 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::armem + * @author Simon Ottenhaus ( simon dot ottenhaus at kit dot edu ) + * @date 2020 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#define BOOST_TEST_MODULE RobotAPI::ArmarXLibraries::armem + +#define ARMARX_BOOST_TEST + +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/operations.h> + +#include <RobotAPI/Test.h> + +#include <iostream> + + +namespace armem = armarx::armem; +namespace wm = armarx::armem::wm; +namespace aron = armarx::aron; + + +namespace ArMemGetFindTest +{ + struct Fixture + { + wm::EntitySnapshot snapshot; + wm::Entity entity; + wm::ProviderSegment provSeg; + wm::CoreSegment coreSeg; + wm::Memory memory; + + armem::MemoryID instanceID; + armem::MemoryID snapshotID; + armem::MemoryID entityID; + armem::MemoryID provSegID; + armem::MemoryID coreSegID; + armem::MemoryID memoryID; + + Fixture() + { + { + snapshot.time() = armem::Time::microSeconds(1000); + snapshot.addInstance(); + } + { + entity.name() = "entity"; + entity.addSnapshot(snapshot); + } + { + provSeg.name() = "provider segment"; + provSeg.addEntity(entity); + } + { + coreSeg.name() = "core segment"; + coreSeg.addProviderSegment(provSeg); + } + { + memory.name() = "memory"; + memory.addCoreSegment(coreSeg); + } + + memoryID = memory.id(); + coreSegID = memoryID.withCoreSegmentName(coreSeg.name()); + provSegID = coreSegID.withProviderSegmentName(provSeg.name()); + entityID = provSegID.withEntityName(entity.name()); + snapshotID = entityID.withTimestamp(snapshot.time()); + instanceID = snapshotID.withInstanceIndex(0); + } + ~Fixture() + { + } + + + template <class ParentT> + void test_get_find_instance_by_id(ParentT&& parent) + { + _test_get_find_instance_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_instance_by_id(const_cast<ParentT&>(parent)); + } + template <class ParentT> + void _test_get_find_instance_by_id(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasInstance(snapshotID.withInstanceIndex(0)), true); + BOOST_CHECK_EQUAL(parent.hasInstance(snapshotID.withInstanceIndex(1)), false); + + BOOST_CHECK_NE(parent.findInstance(snapshotID.withInstanceIndex(0)), nullptr); + BOOST_CHECK_EQUAL(parent.findInstance(snapshotID.withInstanceIndex(1)), nullptr); + + BOOST_CHECK_NO_THROW(parent.getInstance(snapshotID.withInstanceIndex(0))); + BOOST_CHECK_THROW(parent.getInstance(snapshotID.withInstanceIndex(1)), armem::error::MissingEntry); + } + } + + template <class ParentT> + void test_get_find_snapshot_by_id(ParentT&& parent) + { + _test_get_find_snapshot_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_snapshot_by_id(const_cast<ParentT&>(parent)); + } + template <class ParentT> + void _test_get_find_snapshot_by_id(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasSnapshot(entityID.withTimestamp(armem::Time::microSeconds(1000))), true); + BOOST_CHECK_EQUAL(parent.hasSnapshot(entityID.withTimestamp(armem::Time::microSeconds(2000))), false); + + BOOST_CHECK_NE(parent.findSnapshot(entityID.withTimestamp(armem::Time::microSeconds(1000))), nullptr); + BOOST_CHECK_EQUAL(parent.findSnapshot(entityID.withTimestamp(armem::Time::microSeconds(2000))), nullptr); + + BOOST_CHECK_NO_THROW(parent.getSnapshot(entityID.withTimestamp(armem::Time::microSeconds(1000)))); + BOOST_CHECK_THROW(parent.getSnapshot(entityID.withTimestamp(armem::Time::microSeconds(2000))), armem::error::MissingEntry); + } + } + + + template <class ParentT> + void test_get_find_entity_by_id(ParentT&& parent) + { + _test_get_find_entity_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_entity_by_id(const_cast<ParentT&>(parent)); + } + template <class ParentT> + void _test_get_find_entity_by_id(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasEntity(provSegID.withEntityName("entity")), true); + BOOST_CHECK_EQUAL(parent.hasEntity(provSegID.withEntityName("other entity")), false); + + BOOST_CHECK_NE(parent.findEntity(provSegID.withEntityName("entity")), nullptr); + BOOST_CHECK_EQUAL(parent.findEntity(provSegID.withEntityName("other entity")), nullptr); + + BOOST_CHECK_NO_THROW(parent.getEntity(provSegID.withEntityName("entity"))); + BOOST_CHECK_THROW(parent.getEntity(provSegID.withEntityName("other entity")), armem::error::MissingEntry); + } + } + + template <class ParentT> + void test_get_find_provider_segment_by_id(ParentT&& parent) + { + _test_get_find_provider_segment_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_provider_segment_by_id(const_cast<ParentT&>(parent)); + } + template <class ParentT> + void _test_get_find_provider_segment_by_id(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasProviderSegment(provSegID.withProviderSegmentName("provider segment")), true); + BOOST_CHECK_EQUAL(parent.hasProviderSegment(provSegID.withProviderSegmentName("other provider segment")), false); + + BOOST_CHECK_NE(parent.findProviderSegment(provSegID.withProviderSegmentName("provider segment")), nullptr); + BOOST_CHECK_EQUAL(parent.findProviderSegment(provSegID.withProviderSegmentName("other provider segment")), nullptr); + + BOOST_CHECK_NO_THROW(parent.getProviderSegment(provSegID.withProviderSegmentName("provider segment"))); + BOOST_CHECK_THROW(parent.getProviderSegment(provSegID.withProviderSegmentName("other provider segment")), armem::error::MissingEntry); + } + } + + template <class ParentT> + void test_get_find_core_segment_by_id(ParentT&& parent) + { + _test_get_find_core_segment_by_id(const_cast<const ParentT&>(parent)); + _test_get_find_core_segment_by_id(const_cast<ParentT&>(parent)); + } + template <class ParentT> + void _test_get_find_core_segment_by_id(ParentT&& parent) + { + BOOST_TEST_CONTEXT("Parent: " << armem::print(parent)) + { + BOOST_CHECK_EQUAL(parent.hasCoreSegment(provSegID.withCoreSegmentName("core segment")), true); + BOOST_CHECK_EQUAL(parent.hasCoreSegment(provSegID.withCoreSegmentName("other core segment")), false); + + BOOST_CHECK_NE(parent.findCoreSegment(provSegID.withCoreSegmentName("core segment")), nullptr); + BOOST_CHECK_EQUAL(parent.findCoreSegment(provSegID.withCoreSegmentName("other core segment")), nullptr); + + BOOST_CHECK_NO_THROW(parent.getCoreSegment(provSegID.withCoreSegmentName("core segment"))); + BOOST_CHECK_THROW(parent.getCoreSegment(provSegID.withCoreSegmentName("other core segment")), armem::error::MissingEntry); + } + } + }; +} + + +BOOST_FIXTURE_TEST_SUITE(ArMemGetFindTest, Fixture) + + + +BOOST_AUTO_TEST_CASE(test_snapshot_get_find_instance_by_key) +{ + BOOST_CHECK_EQUAL(snapshot.hasInstance(0), true); + BOOST_CHECK_EQUAL(snapshot.hasInstance(1), false); + + BOOST_CHECK_NE(snapshot.findInstance(0), nullptr); + BOOST_CHECK_EQUAL(snapshot.findInstance(1), nullptr); + + BOOST_CHECK_NO_THROW(snapshot.getInstance(0)); + BOOST_CHECK_THROW(snapshot.getInstance(1), armem::error::MissingEntry); +} + + +BOOST_AUTO_TEST_CASE(test_entity_get_find_snapshot_by_key) +{ + BOOST_CHECK_EQUAL(entity.hasSnapshot(armem::Time::microSeconds(1000)), true); + BOOST_CHECK_EQUAL(entity.hasSnapshot(armem::Time::microSeconds(2000)), false); + + BOOST_CHECK_NE(entity.findSnapshot(armem::Time::microSeconds(1000)), nullptr); + BOOST_CHECK_EQUAL(entity.findSnapshot(armem::Time::microSeconds(2000)), nullptr); + + BOOST_CHECK_NO_THROW(entity.getSnapshot(armem::Time::microSeconds(1000))); + BOOST_CHECK_THROW(entity.getSnapshot(armem::Time::microSeconds(2000)), armem::error::MissingEntry); +} + + +BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_entity_by_key) +{ + BOOST_CHECK_EQUAL(provSeg.hasEntity("entity"), true); + BOOST_CHECK_EQUAL(provSeg.hasEntity("other entity"), false); + + BOOST_CHECK_NE(provSeg.findEntity("entity"), nullptr); + BOOST_CHECK_EQUAL(provSeg.findEntity("other entity"), nullptr); + + BOOST_CHECK_NO_THROW(provSeg.getEntity("entity")); + BOOST_CHECK_THROW(provSeg.getEntity("other entity"), armem::error::MissingEntry); +} + + +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_provider_segment_by_key) +{ + BOOST_CHECK_EQUAL(coreSeg.hasProviderSegment("provider segment"), true); + BOOST_CHECK_EQUAL(coreSeg.hasProviderSegment("other provider segment"), false); + + BOOST_CHECK_NE(coreSeg.findProviderSegment("provider segment"), nullptr); + BOOST_CHECK_EQUAL(coreSeg.findProviderSegment("other provider segment"), nullptr); + + BOOST_CHECK_NO_THROW(coreSeg.getProviderSegment("provider segment")); + BOOST_CHECK_THROW(coreSeg.getProviderSegment("other provider segment"), armem::error::MissingEntry); +} + + +BOOST_AUTO_TEST_CASE(test_memory_get_find_core_segment_by_key) +{ + BOOST_CHECK_EQUAL(memory.hasCoreSegment("core segment"), true); + BOOST_CHECK_EQUAL(memory.hasCoreSegment("other core segment"), false); + + BOOST_CHECK_NE(memory.findCoreSegment("core segment"), nullptr); + BOOST_CHECK_EQUAL(memory.findCoreSegment("other core segment"), nullptr); + + BOOST_CHECK_NO_THROW(memory.getCoreSegment("core segment")); + BOOST_CHECK_THROW(memory.getCoreSegment("other core segment"), armem::error::MissingEntry); +} + + + +BOOST_AUTO_TEST_CASE(test_snapshot_get_find_instance_by_id) +{ + test_get_find_instance_by_id(snapshot); +} +BOOST_AUTO_TEST_CASE(test_entity_get_find_instance_by_id) +{ + test_get_find_instance_by_id(entity); +} +BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_instance_by_id) +{ + test_get_find_instance_by_id(provSeg); +} +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_instance_by_id) +{ + test_get_find_instance_by_id(coreSeg); +} +BOOST_AUTO_TEST_CASE(test_memory_get_find_instance_by_id) +{ + test_get_find_instance_by_id(memory); +} + + +BOOST_AUTO_TEST_CASE(test_entity_get_find_snapshot_by_id) +{ + test_get_find_snapshot_by_id(entity); +} +BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_snapshot_by_id) +{ + test_get_find_snapshot_by_id(provSeg); +} +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_snapshot_by_id) +{ + test_get_find_snapshot_by_id(coreSeg); +} +BOOST_AUTO_TEST_CASE(test_memory_get_find_snapshot_by_id) +{ + test_get_find_snapshot_by_id(memory); +} + + +BOOST_AUTO_TEST_CASE(test_provider_segment_get_find_entity_by_id) +{ + test_get_find_entity_by_id(provSeg); +} +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_entity_by_id) +{ + test_get_find_entity_by_id(coreSeg); +} +BOOST_AUTO_TEST_CASE(test_memory_get_find_entity_by_id) +{ + test_get_find_entity_by_id(memory); +} + + +BOOST_AUTO_TEST_CASE(test_core_segment_get_find_provider_segment_by_id) +{ + test_get_find_provider_segment_by_id(coreSeg); +} +BOOST_AUTO_TEST_CASE(test_memory_get_find_provider_segment_by_id) +{ + test_get_find_provider_segment_by_id(memory); +} + +BOOST_AUTO_TEST_CASE(test_memory_get_find_core_segment_by_id) +{ + test_get_find_core_segment_by_id(memory); +} + + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/source/RobotAPI/libraries/armem/test/ArMemIceConversionsTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemIceConversionsTest.cpp index a835f39bef74f745b22c4ee167e4755a031fec50..9fd00a4cf944f50cbd1c0d7bd06b9b56d812c161 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemIceConversionsTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemIceConversionsTest.cpp @@ -25,7 +25,7 @@ #define ARMARX_BOOST_TEST #include <RobotAPI/Test.h> -#include "../core/workingmemory/ice_conversions.h" +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <iostream> @@ -59,12 +59,12 @@ BOOST_AUTO_TEST_CASE(test_entity) BOOST_CHECK_EQUAL(ice->id.providerSegmentName, entity.id().providerSegmentName); BOOST_CHECK_EQUAL(ice->id.entityName, entity.id().entityName); - BOOST_CHECK_EQUAL(ice->history.size(), entity.history().size()); + BOOST_CHECK_EQUAL(ice->history.size(), entity.size()); armem::wm::Entity entityOut; armem::fromIce(ice, entityOut); - BOOST_CHECK_EQUAL(entityOut.history().size(), entity.history().size()); + BOOST_CHECK_EQUAL(entityOut.size(), entity.size()); std::vector<armem::Time> timestamps = entityOut.getTimestamps(); BOOST_CHECK_EQUAL_COLLECTIONS(timestamps.begin(), timestamps.end(), expectedTimestamps.begin(), expectedTimestamps.end()); diff --git a/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp index 24c0a22203aae81bfc52802a9f48fbb9755c9c5f..f0ede7b42a3bdb34d1a103ab43226adae833740a 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemLTMTest.cpp @@ -25,8 +25,8 @@ #define ARMARX_BOOST_TEST #include <RobotAPI/Test.h> -#include "../core/workingmemory/Memory.h" -#include "../core/error.h" +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/error.h> #include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h> #include <RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h> @@ -34,7 +34,6 @@ #include <RobotAPI/libraries/aron/core/Debug.h> -#include "../core/longtermmemory/Memory.h" //#include "../core/io/diskWriter/NlohmannJSON/NlohmannJSONDiskWriter.h" @@ -122,7 +121,7 @@ namespace ArMemLTMTest update.instancesData = q; update.timeCreated = armem::Time::now(); BOOST_CHECK_NO_THROW(providerSegment.update(update)); - BOOST_CHECK_EQUAL(providerSegment.entities().size(), 1); + BOOST_CHECK_EQUAL(providerSegment.size(), 1); } BOOST_CHECK(providerSegment.hasEntity("TestEntity")); diff --git a/source/RobotAPI/libraries/armem/test/ArMemMemoryIDTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemMemoryIDTest.cpp index aad3359b0d57cfed79d463cc27dcced44550ef00..72f09fb225aa22b50ed55a9482e7f973acbf0fd2 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemMemoryIDTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemMemoryIDTest.cpp @@ -25,8 +25,8 @@ #define ARMARX_BOOST_TEST #include <RobotAPI/Test.h> -#include "../core/MemoryID.h" -#include "../core/error.h" +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/error.h> #include <iostream> diff --git a/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp index 984c1f8e508ec631527f53d5fdff8ea32081ad6e..e9f694a27104c142603752f22f5452c8024663b0 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemMemoryTest.cpp @@ -26,18 +26,21 @@ #include <RobotAPI/Test.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/diskmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/operations.h> #include <iostream> #include <SimoxUtility/meta/type_name.h> #include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> namespace armem = armarx::armem; namespace aron = armarx::aron; +namespace wm = armarx::armem::wm; + BOOST_AUTO_TEST_CASE(test_time_to_string) { @@ -78,6 +81,157 @@ BOOST_AUTO_TEST_CASE(test_time_to_string) } +// Public interface tests + + +namespace ArMemMemoryTest +{ + struct APITestFixture + { + wm::EntityInstance instance { 0 }; + wm::EntitySnapshot snapshot { armem::Time::microSeconds(1000) }; + wm::Entity entity { "entity" }; + wm::ProviderSegment provSeg { "provider" }; + wm::CoreSegment coreSeg { "core" }; + wm::Memory memory { "memory" }; + + + void test_add_instance(wm::EntityInstance& added, const wm::EntitySnapshot& parent) + { + BOOST_TEST_CONTEXT("Added: " << armem::print(added) << "\n Parent: " << armem::print(parent)) + { + const int index = 0; + BOOST_CHECK_EQUAL(added.index(), index); + BOOST_CHECK_EQUAL(parent.size(), 1); + BOOST_CHECK(parent.hasInstance(index)); + BOOST_CHECK_EQUAL(&parent.getInstance(index), &added); + } + } + void test_add_snapshot(wm::EntitySnapshot& added, const wm::Entity& parent) + { + BOOST_TEST_CONTEXT("Added: " << armem::print(added) << "\n Parent: " << armem::print(parent)) + { + const armem::Time time = armem::Time::microSeconds(1000); + BOOST_CHECK_EQUAL(added.time(), time); + BOOST_CHECK_EQUAL(parent.size(), 1); + BOOST_CHECK(parent.hasSnapshot(time)); + BOOST_CHECK_EQUAL(&parent.getSnapshot(time), &added); + } + } + void test_add_entity(wm::Entity& added, const wm::ProviderSegment& parent) + { + BOOST_TEST_CONTEXT("Added: " << armem::print(added) << "\n Parent: " << armem::print(parent)) + { + const std::string name = "entity"; + BOOST_CHECK_EQUAL(added.name(), name); + BOOST_CHECK_EQUAL(parent.size(), 1); + BOOST_CHECK(parent.hasEntity(name)); + BOOST_CHECK_EQUAL(&parent.getEntity(name), &added); + } + } + void test_add_provider_segment(wm::ProviderSegment& added, const wm::CoreSegment& parent) + { + BOOST_TEST_CONTEXT("Added: " << armem::print(added) << "\n Parent: " << armem::print(parent)) + { + const std::string name = "provider"; + BOOST_CHECK_EQUAL(added.name(), name); + BOOST_CHECK_EQUAL(parent.size(), 1); + BOOST_CHECK(parent.hasProviderSegment(name)); + BOOST_CHECK_EQUAL(&parent.getProviderSegment(name), &added); + } + } + void test_add_core_segment(wm::CoreSegment& added, const wm::Memory& parent) + { + BOOST_TEST_CONTEXT("Added: " << armem::print(added) << "\n Parent: " << armem::print(parent)) + { + const std::string name = "core"; + BOOST_CHECK_EQUAL(added.name(), name); + BOOST_CHECK_EQUAL(parent.size(), 1); + BOOST_CHECK(parent.hasCoreSegment(name)); + BOOST_CHECK_EQUAL(&parent.getCoreSegment(name), &added); + } + } + }; +} + + + +BOOST_FIXTURE_TEST_SUITE(APITest, ArMemMemoryTest::APITestFixture) + + +BOOST_AUTO_TEST_CASE(test_add_instance_no_args) +{ + test_add_instance(snapshot.addInstance(), snapshot); +} +BOOST_AUTO_TEST_CASE(test_add_instance_copy) +{ + test_add_instance(snapshot.addInstance(instance), snapshot); +} +BOOST_AUTO_TEST_CASE(test_add_instance_move) +{ + test_add_instance(snapshot.addInstance(std::move(instance)), snapshot); +} + +BOOST_AUTO_TEST_CASE(test_add_snapshot_time) +{ + test_add_snapshot(entity.addSnapshot(armem::Time::microSeconds(1000)), entity); +} +BOOST_AUTO_TEST_CASE(test_add_snapshot_copy) +{ + test_add_snapshot(entity.addSnapshot(snapshot), entity); +} +BOOST_AUTO_TEST_CASE(test_add_snapshot_move) +{ + test_add_snapshot(entity.addSnapshot(std::move(snapshot)), entity); +} + +BOOST_AUTO_TEST_CASE(test_add_entity_name) +{ + test_add_entity(provSeg.addEntity("entity"), provSeg); +} +BOOST_AUTO_TEST_CASE(test_add_entity_copy) +{ + test_add_entity(provSeg.addEntity(entity), provSeg); +} +BOOST_AUTO_TEST_CASE(test_add_entity_move) +{ + test_add_entity(provSeg.addEntity(std::move(entity)), provSeg); +} + +BOOST_AUTO_TEST_CASE(test_add_provider_segment_name) +{ + test_add_provider_segment(coreSeg.addProviderSegment("provider"), coreSeg); +} +BOOST_AUTO_TEST_CASE(test_add_provider_segment_copy) +{ + test_add_provider_segment(coreSeg.addProviderSegment(provSeg), coreSeg); +} +BOOST_AUTO_TEST_CASE(test_add_provider_segment_move) +{ + test_add_provider_segment(coreSeg.addProviderSegment(std::move(provSeg)), coreSeg); +} + +BOOST_AUTO_TEST_CASE(test_add_core_segment_name) +{ + test_add_core_segment(memory.addCoreSegment("core"), memory); +} +BOOST_AUTO_TEST_CASE(test_add_core_segment_copy) +{ + test_add_core_segment(memory.addCoreSegment(coreSeg), memory); +} +BOOST_AUTO_TEST_CASE(test_add_core_segment_move) +{ + test_add_core_segment(memory.addCoreSegment(std::move(coreSeg)), memory); +} + + +BOOST_AUTO_TEST_SUITE_END() + + + + + +// COPY/MOVE CTOR/OP TESTS namespace ArMemMemoryTest @@ -87,11 +241,11 @@ namespace ArMemMemoryTest using MemoryItem::MemoryItem; using MemoryItem::operator=; - std::string getKeyString() const override + std::string getKeyString() const { return ""; } - std::string getLevelName() const override + std::string getLevelName() const { return ""; } @@ -154,24 +308,29 @@ namespace ArMemMemoryTest using MemoryContainerBase::MemoryContainerBase; using MemoryContainerBase::operator=; - std::string getKeyString() const override + std::string getKeyString() const { return ""; } - std::string getLevelName() const override + std::string getLevelName() const { return ""; } + + void fill() + { + _container = std::vector<int> { -1, 2, -3 }; + } }; struct MemoryContainerCtorOpTestFixture { - const armem::MemoryID id {"A/B/C/123/1"}; - const armem::MemoryID moved {"////1"}; // int is not moved + const armem::MemoryID id {"M/C/P/E/123/1"}; + const armem::MemoryID moved {"////123/1"}; // Time and int are just copied, not moved TestMemoryContainer cont {id}; MemoryContainerCtorOpTestFixture() { - cont.container() = std::vector<int> { -1, 2, -3 }; + cont.fill(); BOOST_CHECK_EQUAL(cont.id(), id); BOOST_CHECK_EQUAL(cont.size(), 3); } @@ -224,37 +383,26 @@ BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_CASE(test_key_ctors) { - armem::wm::EntityInstance instance(10); + wm::EntityInstance instance(10); BOOST_CHECK_EQUAL(instance.index(), 10); - armem::wm::EntitySnapshot snapshot(armem::Time::milliSeconds(100)); + wm::EntitySnapshot snapshot(armem::Time::milliSeconds(100)); BOOST_CHECK_EQUAL(snapshot.time(), armem::Time::milliSeconds(100)); - armem::wm::Entity entity("entity"); + wm::Entity entity("entity"); BOOST_CHECK_EQUAL(entity.name(), "entity"); - armem::wm::ProviderSegment provSeg("provSeg"); + wm::ProviderSegment provSeg("provSeg"); BOOST_CHECK_EQUAL(provSeg.name(), "provSeg"); - armem::wm::CoreSegment coreSeg("coreSeg"); + wm::CoreSegment coreSeg("coreSeg"); BOOST_CHECK_EQUAL(coreSeg.name(), "coreSeg"); - armem::wm::Memory memory("memory"); + wm::Memory memory("memory"); BOOST_CHECK_EQUAL(memory.name(), "memory"); } -template <class ...Args> -auto add_element(std::vector<Args...>& vector) -{ - return vector.emplace_back(); -} -template <class ...Args> -auto add_element(std::map<Args...>& map) -{ - return map.emplace(); -} - template <class T> struct CustomChecks @@ -282,19 +430,19 @@ void checkMoved_d_ltm(const T& moved) } template <> -struct CustomChecks<armem::wm::EntityInstance> +struct CustomChecks<wm::EntityInstance> { - static void checkEqual(const armem::wm::EntityInstance& lhs, const armem::wm::EntityInstance& rhs) + static void checkEqual(const wm::EntityInstance& lhs, const wm::EntityInstance& rhs) { BOOST_CHECK_EQUAL(lhs.metadata(), rhs.metadata()); BOOST_CHECK_EQUAL(lhs.data(), rhs.data()); } - static void checkMoved(const armem::wm::EntityInstance& moved) + static void checkMoved(const wm::EntityInstance& moved) { BOOST_CHECK_EQUAL(moved.data(), nullptr); } }; -template <> +/*template <> struct CustomChecks<armem::d_ltm::EntityInstance> { static void checkEqual(const armem::d_ltm::EntityInstance& lhs, const armem::d_ltm::EntityInstance& rhs) @@ -365,19 +513,22 @@ struct CustomChecks<armem::d_ltm::Memory> { checkMoved_d_ltm(moved); } -}; +};*/ struct CopyMoveCtorsOpsTestBase { - const armem::MemoryID id {"A/B/C/123000"}; // int index would not be moved - const armem::MemoryID idMoved; + const armem::MemoryID id {"M", "C", "P", "E", armem::Time::microSeconds(123000), 1}; + //const armem::MemoryID idMoved {"", "", "", "", armem::Time::microSeconds(123000), 1}; + armem::MemoryID idMoved = id; std::string typeName; CopyMoveCtorsOpsTestBase(const std::string& typeName) : typeName(typeName) { + armem::MemoryID copy = std::move(idMoved); + (void) copy; } virtual ~CopyMoveCtorsOpsTestBase() { @@ -459,21 +610,25 @@ struct InstanceCopyMoveCtorsOpsTest : public CopyMoveCtorsOpsTestBase } void testMoveCtor() override { + T copy { in }; T out { std::move(in) }; BOOST_CHECK_EQUAL(in.id(), idMoved); BOOST_CHECK_EQUAL(out.id(), id); + CustomChecks<T>::checkEqual(out, copy); CustomChecks<T>::checkMoved(in); } void testMoveOp() override { + T copy { in }; T out; out = std::move(in); BOOST_CHECK_EQUAL(in.id(), idMoved); BOOST_CHECK_EQUAL(out.id(), id); + CustomChecks<T>::checkEqual(out, copy); CustomChecks<T>::checkMoved(in); } }; @@ -492,7 +647,17 @@ struct CopyMoveCtorsOpsTest : public CopyMoveCtorsOpsTestBase void reset() override { in = T {id}; - add_element(in.container()); + if constexpr(std::is_same_v<T, wm::Memory>) + { + in.addCoreSegment("C"); + } + { + armem::EntityUpdate update; + update.entityID = armem::MemoryID("M", "C", "P", "E", armem::Time::microSeconds(123000), 0); + update.timeCreated = update.entityID.timestamp; + update.instancesData.emplace_back(); + in.update(update); + } BOOST_CHECK_EQUAL(in.id(), id); BOOST_CHECK_EQUAL(in.size(), 1); @@ -581,29 +746,29 @@ struct CopyMoveCtorsOpsTest : public CopyMoveCtorsOpsTestBase BOOST_AUTO_TEST_CASE(test_copy_move_ctors_ops) { { - InstanceCopyMoveCtorsOpsTest<armem::wm::EntityInstance>().test(); - CopyMoveCtorsOpsTest<armem::wm::EntitySnapshot>().test(); - CopyMoveCtorsOpsTest<armem::wm::Entity>().test(); - CopyMoveCtorsOpsTest<armem::wm::ProviderSegment>().test(); - CopyMoveCtorsOpsTest<armem::wm::CoreSegment>().test(); - CopyMoveCtorsOpsTest<armem::wm::Memory>().test(); + InstanceCopyMoveCtorsOpsTest<wm::EntityInstance>().test(); + CopyMoveCtorsOpsTest<wm::EntitySnapshot>().test(); + CopyMoveCtorsOpsTest<wm::Entity>().test(); + CopyMoveCtorsOpsTest<wm::ProviderSegment>().test(); + CopyMoveCtorsOpsTest<wm::CoreSegment>().test(); + CopyMoveCtorsOpsTest<wm::Memory>().test(); } - { + /*{ InstanceCopyMoveCtorsOpsTest<armem::ltm::EntityInstance>().test(); CopyMoveCtorsOpsTest<armem::ltm::EntitySnapshot>().test(); CopyMoveCtorsOpsTest<armem::ltm::Entity>().test(); CopyMoveCtorsOpsTest<armem::ltm::ProviderSegment>().test(); CopyMoveCtorsOpsTest<armem::ltm::CoreSegment>().test(); CopyMoveCtorsOpsTest<armem::ltm::Memory>().test(); - } - { + }*/ + /*{ InstanceCopyMoveCtorsOpsTest<armem::d_ltm::EntityInstance>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::EntitySnapshot>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::Entity>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::ProviderSegment>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::CoreSegment>().test(); CopyMoveCtorsOpsTest<armem::d_ltm::Memory>().test(); - } + }*/ } @@ -612,7 +777,7 @@ BOOST_AUTO_TEST_CASE(test_segment_setup) { armem::EntityUpdate update; - armem::wm::Memory memory("Memory"); + wm::Memory memory("Memory"); BOOST_CHECK_EQUAL(memory.name(), "Memory"); { update.entityID = armem::MemoryID::fromString("OtherMemory/SomeSegment"); @@ -621,7 +786,7 @@ BOOST_AUTO_TEST_CASE(test_segment_setup) BOOST_CHECK_THROW(memory.update(update), armem::error::MissingEntry); } - armem::wm::CoreSegment& coreSegment = memory.addCoreSegment("ImageRGB"); + wm::CoreSegment& coreSegment = memory.addCoreSegment("ImageRGB"); BOOST_CHECK_EQUAL(coreSegment.name(), "ImageRGB"); BOOST_CHECK(memory.hasCoreSegment(coreSegment.name())); { @@ -632,7 +797,7 @@ BOOST_AUTO_TEST_CASE(test_segment_setup) //BOOST_CHECK_THROW(coreSegment.update(update), armem::error::MissingEntry); } - armem::wm::ProviderSegment& providerSegment = coreSegment.addProviderSegment("SomeRGBImageProvider"); + wm::ProviderSegment& providerSegment = coreSegment.addProviderSegment("SomeRGBImageProvider"); BOOST_CHECK_EQUAL(providerSegment.name(), "SomeRGBImageProvider"); BOOST_CHECK(coreSegment.hasProviderSegment(providerSegment.name())); { @@ -652,17 +817,17 @@ BOOST_AUTO_TEST_CASE(test_segment_setup) update.timeCreated = armem::Time::milliSeconds(1000); BOOST_CHECK_NO_THROW(providerSegment.update(update)); - BOOST_CHECK_EQUAL(providerSegment.entities().size(), 1); + BOOST_CHECK_EQUAL(providerSegment.size(), 1); BOOST_CHECK(providerSegment.hasEntity("image")); BOOST_CHECK(!providerSegment.hasEntity("other_image")); - armem::wm::Entity& entity = providerSegment.getEntity("image"); + wm::Entity& entity = providerSegment.getEntity("image"); BOOST_CHECK_EQUAL(entity.name(), "image"); - BOOST_CHECK_EQUAL(entity.history().size(), 1); - BOOST_CHECK_EQUAL(entity.history().count(update.timeCreated), 1); + BOOST_CHECK_EQUAL(entity.size(), 1); + BOOST_CHECK(entity.hasSnapshot(update.timeCreated)); - armem::wm::EntitySnapshot& entitySnapshot = entity.history().at(update.timeCreated); - BOOST_CHECK_EQUAL(entitySnapshot.instances().size(), update.instancesData.size()); + wm::EntitySnapshot& entitySnapshot = entity.getSnapshot(update.timeCreated); + BOOST_CHECK_EQUAL(entitySnapshot.size(), update.instancesData.size()); // Another update (on memory). @@ -670,16 +835,16 @@ BOOST_AUTO_TEST_CASE(test_segment_setup) update.instancesData = { std::make_shared<aron::datanavigator::DictNavigator>() }; update.timeCreated = armem::Time::milliSeconds(2000); memory.update(update); - BOOST_CHECK_EQUAL(entity.history().size(), 2); - BOOST_CHECK_EQUAL(entity.history().count(update.timeCreated), 1); - BOOST_CHECK_EQUAL(entity.history().at(update.timeCreated).instances().size(), update.instancesData.size()); + BOOST_CHECK_EQUAL(entity.size(), 2); + BOOST_CHECK(entity.hasSnapshot(update.timeCreated)); + BOOST_CHECK_EQUAL(entity.getSnapshot(update.timeCreated).size(), update.instancesData.size()); // A third update (on entity). update.instancesData = { std::make_shared<aron::datanavigator::DictNavigator>() }; update.timeCreated = armem::Time::milliSeconds(3000); entity.update(update); - BOOST_CHECK_EQUAL(entity.history().size(), 3); + BOOST_CHECK_EQUAL(entity.size(), 3); } @@ -687,7 +852,7 @@ BOOST_AUTO_TEST_CASE(test_segment_setup) BOOST_AUTO_TEST_CASE(test_history_size_in_entity) { - armem::wm::Entity entity("entity"); + armem::server::wm::Entity entity("entity"); armem::EntityUpdate update; update.entityID.entityName = entity.name(); @@ -699,38 +864,38 @@ BOOST_AUTO_TEST_CASE(test_history_size_in_entity) entity.update(update); update.timeCreated = armem::Time::milliSeconds(3000); entity.update(update); - BOOST_CHECK_EQUAL(entity.history().size(), 3); + BOOST_CHECK_EQUAL(entity.size(), 3); // Now with maximum history size. entity.setMaxHistorySize(2); - BOOST_CHECK_EQUAL(entity.history().size(), 2); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(1000)), 0); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(2000)), 1); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(3000)), 1); + BOOST_CHECK_EQUAL(entity.size(), 2); + BOOST_CHECK(not entity.hasSnapshot(armem::Time::milliSeconds(1000))); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(2000))); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(3000))); update.timeCreated = armem::Time::milliSeconds(4000); entity.update(update); - BOOST_CHECK_EQUAL(entity.history().size(), 2); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(2000)), 0); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(3000)), 1); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(4000)), 1); + BOOST_CHECK_EQUAL(entity.size(), 2); + BOOST_CHECK(not entity.hasSnapshot(armem::Time::milliSeconds(2000))); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(3000))); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(4000))); // Disable maximum history size. entity.setMaxHistorySize(-1); update.timeCreated = armem::Time::milliSeconds(5000); entity.update(update); - BOOST_CHECK_EQUAL(entity.history().size(), 3); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(3000)), 1); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(4000)), 1); - BOOST_CHECK_EQUAL(entity.history().count(armem::Time::milliSeconds(5000)), 1); + BOOST_CHECK_EQUAL(entity.size(), 3); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(3000))); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(4000))); + BOOST_CHECK(entity.hasSnapshot(armem::Time::milliSeconds(5000))); } BOOST_AUTO_TEST_CASE(test_history_size_in_provider_segment) { - armem::wm::ProviderSegment providerSegment("SomeRGBImageProvider"); + armem::server::wm::ProviderSegment providerSegment("SomeRGBImageProvider"); armem::EntityUpdate update; update.entityID.providerSegmentName = providerSegment.name(); @@ -738,7 +903,7 @@ BOOST_AUTO_TEST_CASE(test_history_size_in_provider_segment) std::vector<std::string> entityNames = { "A", "B" }; // Fill entities and histories with unlimited size. - for (const auto& name : entityNames) + for (const std::string& name : entityNames) { update.entityID.entityName = name; @@ -753,22 +918,22 @@ BOOST_AUTO_TEST_CASE(test_history_size_in_provider_segment) update.timeCreated = armem::Time::milliSeconds(4000); providerSegment.update(update); - BOOST_CHECK_EQUAL(providerSegment.getEntity("A").history().size(), 3); - BOOST_CHECK_EQUAL(providerSegment.getEntity("B").history().size(), 4); + BOOST_CHECK_EQUAL(providerSegment.getEntity("A").size(), 3); + BOOST_CHECK_EQUAL(providerSegment.getEntity("B").size(), 4); // Employ maximum history size. providerSegment.setMaxHistorySize(3); - BOOST_CHECK_EQUAL(providerSegment.getEntity("A").history().size(), 3); - BOOST_CHECK_EQUAL(providerSegment.getEntity("B").history().size(), 3); + BOOST_CHECK_EQUAL(providerSegment.getEntity("A").size(), 3); + BOOST_CHECK_EQUAL(providerSegment.getEntity("B").size(), 3); providerSegment.setMaxHistorySize(2); - BOOST_CHECK_EQUAL(providerSegment.getEntity("A").history().size(), 2); - BOOST_CHECK_EQUAL(providerSegment.getEntity("B").history().size(), 2); + BOOST_CHECK_EQUAL(providerSegment.getEntity("A").size(), 2); + BOOST_CHECK_EQUAL(providerSegment.getEntity("B").size(), 2); providerSegment.setMaxHistorySize(3); - BOOST_CHECK_EQUAL(providerSegment.getEntity("A").history().size(), 2); - BOOST_CHECK_EQUAL(providerSegment.getEntity("B").history().size(), 2); + BOOST_CHECK_EQUAL(providerSegment.getEntity("A").size(), 2); + BOOST_CHECK_EQUAL(providerSegment.getEntity("B").size(), 2); // Add new entity. providerSegment.setMaxHistorySize(2); @@ -784,69 +949,18 @@ BOOST_AUTO_TEST_CASE(test_history_size_in_provider_segment) // Check correctly inherited history size. BOOST_CHECK_EQUAL(providerSegment.getEntity("C").getMaxHistorySize(), 2); // Check actual history size. - BOOST_CHECK_EQUAL(providerSegment.getEntity("C").history().size(), 2); + BOOST_CHECK_EQUAL(providerSegment.getEntity("C").size(), 2); // Remove maximum. providerSegment.setMaxHistorySize(-1); entityNames.push_back("C"); - for (const auto& name : entityNames) + for (const std::string& name : entityNames) { update.entityID.entityName = name; update.timeCreated = armem::Time::milliSeconds(5000); providerSegment.update(update); - BOOST_CHECK_EQUAL(providerSegment.getEntity(name).history().size(), 3); - } -} - - - -template <class T> -struct CopyTest -{ - CopyTest() - { - } - - void test() - { - // At least check whether they can be called. - T t; - T t2 = t.copy(); - - if constexpr(!std::is_base_of_v<armem::base::EntityInstanceBase<T>, T>) - { - t2 = t.copyEmpty(); - } - } -}; - - -BOOST_AUTO_TEST_CASE(test_copy) -{ - { - CopyTest<armem::wm::EntityInstance>().test(); - CopyTest<armem::wm::EntitySnapshot>().test(); - CopyTest<armem::wm::Entity>().test(); - CopyTest<armem::wm::ProviderSegment>().test(); - CopyTest<armem::wm::CoreSegment>().test(); - CopyTest<armem::wm::Memory>().test(); - } - { - CopyTest<armem::ltm::EntityInstance>().test(); - CopyTest<armem::ltm::EntitySnapshot>().test(); - CopyTest<armem::ltm::Entity>().test(); - CopyTest<armem::ltm::ProviderSegment>().test(); - CopyTest<armem::ltm::CoreSegment>().test(); - CopyTest<armem::ltm::Memory>().test(); - } - { - CopyTest<armem::d_ltm::EntityInstance>().test(); - CopyTest<armem::d_ltm::EntitySnapshot>().test(); - CopyTest<armem::d_ltm::Entity>().test(); - CopyTest<armem::d_ltm::ProviderSegment>().test(); - CopyTest<armem::d_ltm::CoreSegment>().test(); - CopyTest<armem::d_ltm::Memory>().test(); + BOOST_CHECK_EQUAL(providerSegment.getEntity(name).getMaxHistorySize(), -1); + BOOST_CHECK_EQUAL(providerSegment.getEntity(name).size(), 3); } } - diff --git a/source/RobotAPI/libraries/armem/test/ArMemQueryBuilderTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemQueryBuilderTest.cpp index 535735b09eeb61d41cdf0dbaf658cebcdb08f4db..78d3077c082e41f32bf6dfd398e9e7d474e19dc3 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemQueryBuilderTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemQueryBuilderTest.cpp @@ -25,9 +25,10 @@ #define ARMARX_BOOST_TEST #include <RobotAPI/Test.h> -#include "../client/query/Builder.h" -#include "../client/query/query_fns.h" -#include "../core/workingmemory/ice_conversions.h" +#include <RobotAPI/libraries/armem/client/query/Builder.h> +#include <RobotAPI/libraries/armem/client/query/query_fns.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> + #include <iostream> diff --git a/source/RobotAPI/libraries/armem/test/ArMemQueryTest.cpp b/source/RobotAPI/libraries/armem/test/ArMemQueryTest.cpp index 4b83fd43261e1d97da45495f5da94279931e606f..13d050572342263d273dc5080f1631cec15faf05 100644 --- a/source/RobotAPI/libraries/armem/test/ArMemQueryTest.cpp +++ b/source/RobotAPI/libraries/armem/test/ArMemQueryTest.cpp @@ -20,28 +20,28 @@ * GNU General Public License */ -#include "ArmarXCore/core/exceptions/LocalException.h" -#include <RobotAPI/interface/armem/query.h> -#include <boost/test/tools/old/interface.hpp> #define BOOST_TEST_MODULE RobotAPI::ArmarXLibraries::armem #define ARMARX_BOOST_TEST #include <RobotAPI/Test.h> -#include "../server/query_proc/workingmemory/EntityQueryProcessor.h" -#include "../error.h" - +#include <RobotAPI/interface/armem/query.h> +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/base/detail/negative_index_semantics.h> +#include <RobotAPI/libraries/armem/server/query_proc/wm.h> -#include <iostream> +#include <ArmarXCore/core/exceptions/LocalException.h> #include <SimoxUtility/algorithm/get_map_keys_values.h> #include <SimoxUtility/algorithm/string.h> +#include <iostream> + namespace armem = armarx::armem; namespace aron = armarx::aron; namespace query = armarx::armem::query::data; -using EntityQueryProcessor = armarx::armem::wm::query_proc::EntityQueryProcessor; +using EntityQueryProcessor = armarx::armem::server::query_proc::wm::EntityQueryProcessor; namespace ArMemQueryTest @@ -55,8 +55,7 @@ namespace ArMemQueryTest { armem::wm::Entity entity; - armem::wm::query_proc::EntityQueryProcessor processor; - query::EntityQueryPtr entityQuery; + armem::server::query_proc::wm::EntityQueryProcessor processor; std::vector<armem::wm::Entity> results; @@ -85,10 +84,10 @@ namespace ArMemQueryTest template <class QueryT> void addResults(QueryT query = {}) { - entityQuery = new QueryT(query); - + // Test whether we get the same result when we pass the concrete type + // (static resolution) and when we pass the base type (dynamic resolution). results.emplace_back(processor.process(query, entity)); - results.emplace_back(processor.process(*entityQuery, entity)); + results.emplace_back(processor.process(static_cast<query::EntityQuery const&>(query), entity)); } }; @@ -126,8 +125,9 @@ BOOST_AUTO_TEST_CASE(test_entity_Single_latest) { BOOST_CHECK_EQUAL(result.name(), entity.name()); BOOST_CHECK_EQUAL(result.size(), 1); - BOOST_CHECK_EQUAL(result.history().begin()->second.time(), entity.getLatestSnapshot().time()); - BOOST_CHECK_NE(&result.history().begin()->second, &entity.history().rbegin()->second); + const armem::wm::EntitySnapshot* first = result.findFirstSnapshotAfterOrAt(armem::Time::microSeconds(0)); + BOOST_REQUIRE_NE(first, nullptr); + BOOST_CHECK_EQUAL(first->time(), armem::Time::microSeconds(5000)); } } @@ -140,8 +140,8 @@ BOOST_AUTO_TEST_CASE(test_entity_Single_existing) for (const armem::wm::Entity& result : results) { BOOST_CHECK_EQUAL(result.name(), entity.name()); - BOOST_CHECK_EQUAL(result.size(), 1); - BOOST_CHECK_EQUAL(result.history().begin()->second.time(), armem::Time::microSeconds(3000)); + BOOST_REQUIRE_EQUAL(result.size(), 1); + BOOST_CHECK_EQUAL(result.getFirstSnapshot().time(), armem::Time::microSeconds(3000)); } } @@ -168,12 +168,13 @@ BOOST_AUTO_TEST_CASE(test_entity_All) { BOOST_CHECK_EQUAL(result.name(), entity.name()); BOOST_CHECK_EQUAL(result.size(), entity.size()); - auto jt = entity.history().begin(); - for (auto it = result.history().begin(); it != result.history().end(); ++it, ++jt) + for (armem::Time time : entity.getTimestamps()) { - BOOST_CHECK_EQUAL(it->first, jt->first); - BOOST_CHECK_EQUAL(it->second.time(), jt->second.time()); - BOOST_CHECK_NE(&it->second, &jt->second); + BOOST_REQUIRE(result.hasSnapshot(time)); + const armem::wm::EntitySnapshot& rs = result.getSnapshot(time); + const armem::wm::EntitySnapshot& es = entity.getSnapshot(time); + BOOST_CHECK_EQUAL(rs.time(), es.time()); + BOOST_CHECK_NE(&rs, &es); } } } @@ -191,7 +192,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeRange_slice) BOOST_CHECK_EQUAL(result.size(), 2); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); std::vector<armem::Time> expected { armem::Time::microSeconds(2000), armem::Time::microSeconds(3000) @@ -212,7 +213,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeRange_exact) BOOST_CHECK_EQUAL(result.size(), 3); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); std::vector<armem::Time> expected { armem::Time::microSeconds(2000), armem::Time::microSeconds(3000), armem::Time::microSeconds(4000) @@ -233,8 +234,8 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeRange_all) BOOST_CHECK_EQUAL(result.size(), entity.size()); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); - std::vector<armem::Time> expected = simox::alg::get_keys(entity.history()); + const std::vector<armem::Time> times = result.getTimestamps(); + const std::vector<armem::Time> expected = entity.getTimestamps(); BOOST_CHECK_EQUAL_COLLECTIONS(times.begin(), times.end(), expected.begin(), expected.end()); } } @@ -266,7 +267,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeRange_from_start) BOOST_CHECK_EQUAL(result.size(), 2); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + const std::vector<armem::Time> times = result.getTimestamps(); std::vector<armem::Time> expected { armem::Time::microSeconds(1000), armem::Time::microSeconds(2000) @@ -284,10 +285,9 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeRange_to_end) for (const armem::wm::Entity& result : results) { BOOST_CHECK_EQUAL(result.name(), entity.name()); - BOOST_CHECK_EQUAL(result.size(), 3); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + const std::vector<armem::Time> times = result.getTimestamps(); std::vector<armem::Time> expected { armem::Time::microSeconds(3000), armem::Time::microSeconds(4000), armem::Time::microSeconds(5000) @@ -307,12 +307,11 @@ BOOST_AUTO_TEST_CASE(test_entity_BeforeTime_1) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + const std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); - BOOST_REQUIRE_EQUAL(times.front(), armem::Time::microSeconds(3000)); + BOOST_CHECK_EQUAL(times.front(), armem::Time::microSeconds(3000)); } - } BOOST_AUTO_TEST_CASE(test_entity_BeforeTime_2) @@ -323,7 +322,7 @@ BOOST_AUTO_TEST_CASE(test_entity_BeforeTime_2) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + const std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 2); std::vector<armem::Time> expected @@ -348,7 +347,7 @@ BOOST_AUTO_TEST_CASE(test_entity_BeforeOrAtTime_before) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); BOOST_REQUIRE_EQUAL(times.front(), armem::Time::microSeconds(3000)); @@ -363,7 +362,7 @@ BOOST_AUTO_TEST_CASE(test_entity_BeforeOrAtTime_at) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); BOOST_REQUIRE_EQUAL(times.front(), armem::Time::microSeconds(3000)); @@ -378,7 +377,7 @@ BOOST_AUTO_TEST_CASE(test_entity_BeforeOrAtTime_lookup_past) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE(times.empty()); } } @@ -398,7 +397,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_no_limit) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 2); std::vector<armem::Time> expected @@ -424,7 +423,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_limit_600) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 2); std::vector<armem::Time> expected @@ -450,7 +449,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_limit_too_small) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 0); } @@ -469,7 +468,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_limit_only_next) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); std::vector<armem::Time> expected @@ -494,7 +493,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_limit_only_previous) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); std::vector<armem::Time> expected @@ -519,7 +518,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_perfect_match) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); std::vector<armem::Time> expected @@ -544,7 +543,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_lookup_past) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE(times.empty()); } } @@ -562,7 +561,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_lookup_future) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE(times.empty()); } } @@ -580,7 +579,7 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_lookup_future_valid) for (const auto& result : results) { - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); BOOST_REQUIRE_EQUAL(times.size(), 1); std::vector<armem::Time> expected @@ -601,33 +600,33 @@ BOOST_AUTO_TEST_CASE(test_entity_TimeApprox_lookup_invalid_timestamp) BOOST_AUTO_TEST_CASE(test_negative_index_semantics) { - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(0, 0), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(0, 1), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(0, 10), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(0, 0), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(0, 1), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(0, 10), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(1, 1), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(5, 1), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(1, 1), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(5, 1), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(1, 5), 1); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(5, 5), 4); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(5, 6), 5); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(5, 10), 5); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(1, 5), 1); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(5, 5), 4); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(5, 6), 5); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(5, 10), 5); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-1, 0), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-5, 0), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-1, 0), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-5, 0), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-1, 1), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-5, 1), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-1, 1), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-5, 1), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-1, 2), 1); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-5, 2), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-1, 2), 1); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-5, 2), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-1, 5), 4); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-5, 5), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-5, 6), 1); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-5, 10), 5); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-10, 10), 0); - BOOST_CHECK_EQUAL(EntityQueryProcessor::negativeIndexSemantics(-20, 10), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-1, 5), 4); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-5, 5), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-5, 6), 1); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-5, 10), 5); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-10, 10), 0); + BOOST_CHECK_EQUAL(armem::base::detail::negativeIndexSemantics(-20, 10), 0); } @@ -643,8 +642,8 @@ BOOST_AUTO_TEST_CASE(test_entity_IndexRange_all_default) BOOST_CHECK_EQUAL(result.size(), entity.size()); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); - std::vector<armem::Time> expected = simox::alg::get_keys(entity.history()); + const std::vector<armem::Time> times = result.getTimestamps(); + const std::vector<armem::Time> expected = entity.getTimestamps(); BOOST_CHECK_EQUAL_COLLECTIONS(times.begin(), times.end(), expected.begin(), expected.end()); } @@ -666,7 +665,7 @@ BOOST_AUTO_TEST_CASE(test_entity_IndexRange_slice) BOOST_CHECK_EQUAL(result.size(), 3); - std::vector<armem::Time> times = simox::alg::get_keys(result.history()); + std::vector<armem::Time> times = result.getTimestamps(); std::vector<armem::Time> expected { armem::Time::microSeconds(2000), armem::Time::microSeconds(3000), armem::Time::microSeconds(4000) diff --git a/source/RobotAPI/libraries/armem/test/CMakeLists.txt b/source/RobotAPI/libraries/armem/test/CMakeLists.txt index e7fa876c119596f5c49352cfd0dca757ab01019c..43b75650492c4d69a6fb08d5a550796e5dfd3a3b 100644 --- a/source/RobotAPI/libraries/armem/test/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/test/CMakeLists.txt @@ -2,9 +2,11 @@ # Libs required for the tests SET(LIBS ${LIBS} ArmarXCore ${LIB_NAME}) +armarx_add_test(ArMemForEachTest ArMemForEachTest.cpp "${LIBS}") +armarx_add_test(ArMemGetFindTest ArMemGetFindTest.cpp "${LIBS}") armarx_add_test(ArMemIceConversionsTest ArMemIceConversionsTest.cpp "${LIBS}") +armarx_add_test(ArMemLTMTest ArMemLTMTest.cpp "${LIBS}") armarx_add_test(ArMemMemoryTest ArMemMemoryTest.cpp "${LIBS}") armarx_add_test(ArMemMemoryIDTest ArMemMemoryIDTest.cpp "${LIBS}") -armarx_add_test(ArMemQueryTest ArMemQueryTest.cpp "${LIBS}") -armarx_add_test(ArMemLTMTest ArMemLTMTest.cpp "${LIBS}") armarx_add_test(ArMemQueryBuilderTest ArMemQueryBuilderTest.cpp "${LIBS}") +armarx_add_test(ArMemQueryTest ArMemQueryTest.cpp "${LIBS}") diff --git a/source/RobotAPI/libraries/armem/util/util.h b/source/RobotAPI/libraries/armem/util/util.h index 35fc1538b78df0b817e7ae0676be68e9675d1f0f..3045d583a6a4144ed996a286257eb5ba2910b9c5 100644 --- a/source/RobotAPI/libraries/armem/util/util.h +++ b/source/RobotAPI/libraries/armem/util/util.h @@ -24,12 +24,12 @@ #include <vector> #include <optional> -#include "ArmarXCore/core/logging/Logging.h" +#include <ArmarXCore/core/logging/Logging.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Entity.h> -#include <RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/aron/core/codegenerator/codeWriter/cpp/AronCppClass.h> + namespace armarx::armem { @@ -52,10 +52,13 @@ namespace armarx::armem return std::nullopt; } +#if 0 + // item.data() is by definition a DictNavigator if (item.data()->getDescriptor() != aron::data::Descriptor::eDict) { return std::nullopt; } +#endif try { diff --git a/source/RobotAPI/libraries/armem_gui/CMakeLists.txt b/source/RobotAPI/libraries/armem_gui/CMakeLists.txt index 696c6db777d8512d49168682c8a368243f2b930d..2a781dd3989d5731393e1b5d9304b06f467611a4 100644 --- a/source/RobotAPI/libraries/armem_gui/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem_gui/CMakeLists.txt @@ -18,10 +18,11 @@ set(SOURCES gui_utils.cpp lifecycle.cpp - MemoryControlWidget.cpp MemoryViewer.cpp PeriodicUpdateWidget.cpp + disk/ControlWidget.cpp + instance/GroupBox.cpp instance/ImageView.cpp instance/InstanceView.cpp @@ -53,7 +54,6 @@ set(HEADERS gui_utils.h lifecycle.h - MemoryControlWidget.h MemoryViewer.h PeriodicUpdateWidget.h TreeWidgetBuilder.h @@ -61,6 +61,8 @@ set(HEADERS PeriodicUpdateWidget.h TreeWidgetBuilder.h + disk/ControlWidget.h + instance/GroupBox.h instance/ImageView.h instance/InstanceView.h diff --git a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp b/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp deleted file mode 100644 index 38e4d8c88719541e7c4fca92964cfb069c22ba5e..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "MemoryControlWidget.h" - -#include <QPushButton> -#include <QLineEdit> -#include <QTimer> -#include <QHBoxLayout> -#include <QFileDialog> - -#include <cmath> - - -namespace armarx::armem::gui -{ - - MemoryControlWidget::MemoryControlWidget() - { - setSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Fixed); - - auto vlayout = new QVBoxLayout(); - auto hlayout1 = new QHBoxLayout(); - auto hlayout2 = new QHBoxLayout(); - - hlayout1->setContentsMargins(10, 2, 10, 2); - hlayout2->setContentsMargins(10, 2, 10, 2); - - const int margin = 0; - vlayout->setContentsMargins(margin, margin, margin, margin); - - _lineEdit = new QLineEdit("/tmp/MemoryExport", this); - _lineEdit->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); - _lineEdit->setMinimumWidth(150); - - _openFileBrowserButton = new QPushButton("Search files", this); - - _storeOnDiskButton = new QPushButton("Store query (Disk)", this); - _storeInLTMButton = new QPushButton("Store query (LTM)", this); - - hlayout1->addWidget(_lineEdit); - hlayout1->addWidget(_openFileBrowserButton); - - hlayout2->addWidget(_storeOnDiskButton); - hlayout2->addWidget(_storeInLTMButton); - - vlayout->addItem(hlayout1); - vlayout->addItem(hlayout2); - - this->setLayout(vlayout); - // Private connections. - - // Public connections. - connect(_openFileBrowserButton, &QPushButton::pressed, this, &This::openFileBrowser); - connect(_storeInLTMButton, &QPushButton::pressed, this, &This::storeInLTM); - connect(_storeOnDiskButton, &QPushButton::pressed, this, &This::storeOnDisk); - } - - void MemoryControlWidget::openFileBrowser() - { - QFileDialog dialog; - dialog.setFileMode(QFileDialog::DirectoryOnly); - //dialog.setOption(QFileDialog::DontUseNativeDialog, true); - dialog.setOption(QFileDialog::ShowDirsOnly, false); - dialog.exec(); - QString open = dialog.directory().path(); - _lineEdit->setText(open); - } - - QLineEdit* MemoryControlWidget::pathInputBox() - { - return _lineEdit; - } - - QString MemoryControlWidget::getEnteredPath() - { - return _lineEdit->text(); - } - - QPushButton* MemoryControlWidget::storeInLTMButton() - { - return _storeInLTMButton; - } - - QPushButton* MemoryControlWidget::storeOnDiskButton() - { - return _storeOnDiskButton; - } -} - diff --git a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h b/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h deleted file mode 100644 index c6b156db313b01180a7f5884959ffc21b0a73f9d..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/armem_gui/MemoryControlWidget.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include <QWidget> - -class QPushButton; -class QLineEdit; -class QFileDialog; - -namespace armarx::armem::gui -{ - - class MemoryControlWidget : public QWidget - { - Q_OBJECT - using This = MemoryControlWidget; - - public: - - MemoryControlWidget(); - - QLineEdit* pathInputBox(); - QString getEnteredPath(); - - QPushButton* openFileBrowserButton(); - - QPushButton* storeInLTMButton(); - QPushButton* storeOnDiskButton(); - - public slots: - - void openFileBrowser(); - - signals: - - void storeInLTM(); - void storeOnDisk(); - - private slots: - - - signals: - - - private: - - QLineEdit* _lineEdit; - - QPushButton* _openFileBrowserButton; - - QPushButton* _storeInLTMButton; - QPushButton* _storeOnDiskButton; - - }; - -} diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp index 4edb2b417bc9b40fde95643519701b4962aee114..c56cc1077ef852c2a92d53cb3ae24f65a3974ccd 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp +++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.cpp @@ -1,12 +1,10 @@ #include "MemoryViewer.h" -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem_gui/gui_utils.h> -#include <RobotAPI/libraries/armem/core/diskmemory/Memory.h> - -#include <RobotAPI/libraries/armem/server/query_proc/diskmemory/MemoryQueryProcessor.h> -#include <RobotAPI/libraries/armem/server/query_proc/workingmemory/MemoryQueryProcessor.h> +#include <RobotAPI/libraries/armem/server/query_proc/wm.h> +#include <RobotAPI/libraries/armem/server/query_proc/ltm.h> #include <ArmarXGui/libraries/SimpleConfigDialog/SimpleConfigDialog.h> @@ -16,24 +14,28 @@ #include <SimoxUtility/algorithm/get_map_keys_values.h> +#include <QApplication> #include <QBoxLayout> #include <QCheckBox> +#include <QClipboard> #include <QDialog> #include <QGroupBox> #include <QMenu> #include <QLabel> #include <QLayout> #include <QSettings> +#include <QTimer> + +#include <iomanip> -#include <filesystem> namespace armarx::armem::gui { MemoryViewer::MemoryViewer( QBoxLayout* updateWidgetLayout, - QBoxLayout* memoryControlWidgetLayout, QGroupBox* memoryGroupBox, QLayout* memoryGroupBoxParentLayout, QGroupBox* instanceGroupBox, QLayout* instanceGroupBoxParentLayout, + QBoxLayout* diskControlWidgetLayout, QLabel* statusLabel ) { @@ -46,6 +48,10 @@ namespace armarx::armem::gui connect(statusLabel, &QLabel::customContextMenuRequested, [statusLabel](const QPoint & pos) { QMenu menu(statusLabel); + menu.addAction("Copy to clipboard", [statusLabel]() + { + QApplication::clipboard()->setText(statusLabel->text()); + }); menu.addAction("Clear status", [statusLabel]() { statusLabel->clear(); @@ -59,13 +65,10 @@ namespace armarx::armem::gui updateWidget = new armem::gui::PeriodicUpdateWidget(2.0, 60); updateWidgetLayout->insertWidget(0, updateWidget); - // LTM Control - if (memoryControlWidgetLayout) - { - this->memoryControlWidgetLayout = memoryControlWidgetLayout; - memoryControlWidget = new armem::gui::MemoryControlWidget(); - memoryControlWidgetLayout->addWidget(memoryControlWidget); - } + processQueryResultTimer = new QTimer(this); + processQueryResultTimer->setInterval(1000 / 60); // Keep this stable. + processQueryResultTimer->start(); + // Memory View memoryGroup = new armem::gui::MemoryGroupBox(); @@ -78,14 +81,25 @@ namespace armarx::armem::gui this->instanceGroup->setStatusLabel(statusLabel); ARMARX_CHECK_NULL(instanceGroupBox); + // Disk Control + if (diskControlWidgetLayout) + { + this->diskControlLayout = diskControlWidgetLayout; + diskControl = new armem::gui::disk::ControlWidget(); + diskControlWidgetLayout->addWidget(diskControl); + } + // Connections //connect(this, &This::connected, this, &This::updateMemory); - connect(memoryControlWidget, &armem::gui::MemoryControlWidget::storeOnDisk, this, &This::storeOnDisk); - connect(memoryControlWidget, &armem::gui::MemoryControlWidget::storeInLTM, this, &This::storeInLTM); + connect(diskControl, &armem::gui::disk::ControlWidget::requestedStoreOnDisk, this, &This::storeOnDisk); + connect(diskControl, &armem::gui::disk::ControlWidget::requestedLoadFromDisk, this, &This::loadFromDisk); + + connect(this, &This::connected, this, &This::startQueries); + connect(updateWidget, &armem::gui::PeriodicUpdateWidget::update, this, &This::startQueries); + connect(processQueryResultTimer, &QTimer::timeout, this, &This::processQueryResults); - connect(this, &This::connected, this, &This::updateMemories); - connect(updateWidget, &armem::gui::PeriodicUpdateWidget::update, this, &This::updateMemories); + connect(memoryGroup->queryWidget(), &armem::gui::QueryWidget::storeInLTM, this, &This::storeInLTM); connect(this, &This::memoryDataChanged, this, &This::updateMemoryTree); connect(memoryGroup->tree(), &armem::gui::MemoryTreeWidget::selectedItemChanged, this, &This::updateInstanceTree); @@ -95,11 +109,13 @@ namespace armarx::armem::gui connect(instanceGroup->view, &armem::gui::InstanceView::memoryIdResolutionRequested, this, &This::resolveMemoryID); } - void MemoryViewer::setLogTag(const std::string& tag) + + void MemoryViewer::setLogTag(const std::string& _tag) // Leading _ silences a warning { - Logging::setTag(tag); + Logging::setTag(_tag); } + void MemoryViewer::onInit(ManagedIceObject& component) { if (mnsName.size() > 0) @@ -114,6 +130,7 @@ namespace armarx::armem::gui emit initialized(); } + void MemoryViewer::onConnect(ManagedIceObject& component) { if (!mnsName.empty()) @@ -135,6 +152,7 @@ namespace armarx::armem::gui emit connected(); } + void MemoryViewer::onDisconnect(ManagedIceObject&) { updateWidget->stopTimer(); @@ -142,6 +160,7 @@ namespace armarx::armem::gui emit disconnected(); } + const armem::wm::Memory* MemoryViewer::getSingleMemoryData(const std::string& memoryName) { auto it = memoryData.find(memoryName); @@ -164,9 +183,10 @@ namespace armarx::armem::gui } } + void MemoryViewer::storeInLTM() { - TIMING_START(MemoryStore); + TIMING_START(MemoryStore) for (auto& [name, reader] : memoryReaders) { @@ -175,162 +195,183 @@ namespace armarx::armem::gui reader.readAndStore(input); } - TIMING_END_STREAM(MemoryStore, ARMARX_VERBOSE); + TIMING_END_STREAM(MemoryStore, ARMARX_VERBOSE) } - void MemoryViewer::storeOnDisk() + + void MemoryViewer::storeOnDisk(QString directory) { - TIMING_START(MemoryExport); - QString qs = memoryControlWidget->getEnteredPath(); - std::string utf8_text = qs.toUtf8().constData(); + TIMING_START(MemoryExport) - if (not utf8_text.empty()) - { - ARMARX_IMPORTANT << "Exporting all memories at '" << utf8_text << "'."; + std::string status; + diskControl->storeOnDisk(directory, memoryData, &status); - std::filesystem::path p(utf8_text); - if (std::filesystem::is_regular_file(p)) - { - ARMARX_WARNING << "Could not export a memory at '" << utf8_text << "'. Skipping export."; - return; - } + statusLabel->setText(QString::fromStdString(status)); + TIMING_END_STREAM(MemoryExport, ARMARX_VERBOSE) + } - std::filesystem::create_directories(p); - for (auto& [name, reader] : memoryReaders) - { - armem::client::QueryInput input = memoryGroup->queryWidget()->queryInput(); - armem::client::QueryResult result = reader.query(input); - armem::d_ltm::Memory dMem(name); - dMem.reload(p / name); - dMem.append(result.memory); - } - } - else + void MemoryViewer::loadFromDisk(QString directory) + { + std::string status; + std::map<std::string, wm::Memory> data = + diskControl->loadFromDisk(directory, memoryGroup->queryWidget()->queryInput(), &status); + + for (auto& [name, memory] : data) { - ARMARX_WARNING << "The path is empty. Could not export the memory in nirvana."; + this->memoryData[name] = std::move(memory); } + statusLabel->setText(QString::fromStdString(status)); - TIMING_END_STREAM(MemoryExport, ARMARX_VERBOSE); + emit memoryDataChanged(); } - void MemoryViewer::updateMemories() - { - int errorCnt = 0; - - memoryReaders.clear(); - memoryData.clear(); + void MemoryViewer::startQueries() + { memoryReaders = mns.getAllReaders(true); + startDueQueries(runningQueries, memoryReaders); + } + - bool dataChanged = false; + void MemoryViewer::processQueryResults() + { + const std::map<std::string, client::QueryResult> results = collectQueryResults(runningQueries, memoryReaders); - QString qs = memoryControlWidget->getEnteredPath(); - std::string utf8_text = qs.toUtf8().constData(); + int errorCount = 0; + applyQueryResults(results, &errorCount); - std::filesystem::path p(utf8_text); + emit memoryDataChanged(); + updateStatusLabel(errorCount); + } - armem::client::QueryInput input = memoryGroup->queryWidget()->queryInput(); - // first check if the local file system should be queried - if (memoryGroup->queryWidget()->alsoQueryLocalDisk()) + void MemoryViewer::updateStatusLabel(int errorCount) + { + // Code to output status label information + if (statusLabel and errorCount > 0) { - if (std::filesystem::is_directory(p)) - { - for (const auto& d : std::filesystem::directory_iterator(p)) - { - if (d.is_directory()) - { - std::string k = d.path().filename(); - armem::d_ltm::Memory dMem(k); - dMem.reload(p / k); + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); - input.addQueryTargetToAll(armem::query::data::QueryTarget::LTM); // We use LTM as query target for the disk + std::stringstream ss; + ss << "Last update: " << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X"); + ss << "\nThe query produced " << errorCount << " errors! Please check log."; - armem::d_ltm::query_proc::MemoryQueryProcessor d_ltm_processor; - dMem = d_ltm_processor.process(input.toIce(), dMem); + statusLabel->setText(QString::fromStdString(ss.str())); + } + } - wm::Memory converted = dMem.convert(); - memoryData[k] = std::move(converted); - } - } - } - else + + void MemoryViewer::startDueQueries( + std::map<std::string, Ice::AsyncResultPtr>& queries, + const std::map<std::string, armem::client::Reader>& readers) + { + armem::client::QueryInput input = memoryGroup->queryWidget()->queryInput(); + + for (auto& [name, reader] : readers) + { + if (queries.count(name) == 0) { - ARMARX_WARNING << "Could not import a memory from '" << utf8_text << "'. Skipping import."; + queries[name] = reader.memoryPrx->begin_query(input.toIce()); } - - dataChanged = true; // in any case we update the view (even if there was no result) } - else + } + + + std::map<std::string, client::QueryResult> + MemoryViewer::collectQueryResults( + std::map<std::string, Ice::AsyncResultPtr>& queries, + const std::map<std::string, client::Reader>& readers) + { + TIMING_START(tCollectQueryResults) + + std::map<std::string, client::QueryResult> results; + for (auto it = queries.begin(); it != queries.end();) { + const std::string& name = it->first; + Ice::AsyncResultPtr& queryPromise = it->second; - for (auto& [name, reader] : memoryReaders) + if (queryPromise->isCompleted()) { - TIMING_START(MemoryQuery); + if (auto jt = memoryReaders.find(name); jt != readers.end()) { - armem::client::QueryResult result = reader.query(input); - if (result) + try { - if (result.memory.hasData()) - { - if (const auto& it = memoryData.find(name); it != memoryData.end()) - { - // TODO is this necessary (or dead code?) - result.memory.append(it->second); - - if (!result.memory.hasData()) - { - ARMARX_ERROR << "A memory which had data before lost data. This indicates that there is something wrong."; - } - } - - memoryData[name] = std::move(result.memory); - } - else - { - ARMARX_INFO << "The memory " << name << " has no data after querying."; - } - dataChanged = true; // in any case we update the view (even if there was no result) + results[name] = client::QueryResult::fromIce(jt->second.memoryPrx->end_query(queryPromise)); } - else + catch (const Ice::ConnectionRefusedException&) { - ARMARX_WARNING << "A query for memory '" << name << "' produced an error: " << result.errorMessage; - errorCnt++; + // Server is gone (MNS did not now about it yet) => Skip result. } } - TIMING_END_STREAM(MemoryQuery, ARMARX_VERBOSE); + // else: Server is gone (MNS new about it) => Skip result. - if (debugObserver) - { - debugObserver->setDebugDatafield(Logging::tag.tagName, "Memory Query [ms]", new Variant(MemoryQuery.toMilliSecondsDouble())); - } + // Promise is completed => Clean up in any case. + it = runningQueries.erase(it); + } + else + { + ++it; // Uncompleted => Keep. } } - // Code to output status label information - std::stringstream ss; - auto now = std::chrono::system_clock::now(); - auto in_time_t = std::chrono::system_clock::to_time_t(now); - ss << "Last update: " << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X"); - - if (dataChanged) + TIMING_END_STREAM(tCollectQueryResults, ARMARX_VERBOSE) + if (debugObserver) { - emit memoryDataChanged(); + debugObserver->begin_setDebugChannel(Logging::tag.tagName, + { + { "t Collect Query Results [ms]", new Variant(tCollectQueryResults.toMilliSecondsDouble()) }, + { "# Collected Query Results", new Variant(static_cast<int>(results.size())) }, + }); } - else + + return results; + } + + + void MemoryViewer::applyQueryResults( + const std::map<std::string, client::QueryResult>& results, + int* outErrorCount) + { + TIMING_START(tProcessQueryResults) + for (const auto& [name, result] : results) { - ss << "- No query result. "; + if (result.success) + { + memoryData[name] = std::move(result.memory); + } + else + { + ARMARX_WARNING << "Querying memory server '" << name << "' produced an error: \n" << result.errorMessage; + if (outErrorCount) + { + outErrorCount++; + } + } } - if (errorCnt > 0) + // Drop all entries in memoryData which are not in memoryReaders anymore. + for (auto it = memoryData.begin(); it != memoryData.end();) { - ss << "The query produced " << errorCnt << " errors! Please check log."; + if (memoryReaders.count(it->first) == 0) + { + it = memoryData.erase(it); + } + else + { + ++it; + } } - if (statusLabel) + TIMING_END_STREAM(tProcessQueryResults, ARMARX_VERBOSE) + if (debugObserver) { - statusLabel->setText(ss.str().c_str()); + debugObserver->begin_setDebugChannel(Logging::tag.tagName, + { + { "t Process Query Results [ms]", new Variant(tProcessQueryResults.toMilliSecondsDouble()) }, + { "# Processed Query Results", new Variant(static_cast<int>(results.size())) }, + }); } } @@ -362,7 +403,7 @@ namespace armarx::armem::gui { try { - snapshot = &data->getEntitySnapshot(id); + snapshot = &data->getSnapshot(id); } catch (const armem::error::ArMemError& e) { @@ -384,6 +425,7 @@ namespace armarx::armem::gui } } + void MemoryViewer::resolveMemoryID(const MemoryID& id) { // ARMARX_IMPORTANT << "Resolving memory ID: " << id; @@ -403,18 +445,17 @@ namespace armarx::armem::gui std::optional<wm::EntityInstance> instance; try { - const wm::Memory* data = getSingleMemoryData(id.memoryName); - if (data) + if (const wm::Memory* data = getSingleMemoryData(id.memoryName)) { segmentType = data->getProviderSegment(id).aronType(); if (id.hasInstanceIndex()) { - instance = data->getEntityInstance(id); + instance = data->getInstance(id); } else if (id.hasTimestamp()) { - instance = data->getEntitySnapshot(id).getInstance(0); + instance = data->getSnapshot(id).getInstance(0); } else { @@ -422,13 +463,12 @@ namespace armarx::armem::gui } } } - catch (const armem::error::ArMemError& e) + catch (const armem::error::ArMemError&) { // May be handled by remote lookup - (void) e; } - if (!instance) + if (not instance) { try { @@ -452,26 +492,20 @@ namespace armarx::armem::gui } } + void MemoryViewer::updateMemoryTree() { std::map<std::string, const armem::wm::Memory*> convMap; for (auto& [name, data] : memoryData) { - /*if (data.has_value()) - { - convMap[name] = &data.value(); - }*/ convMap[name] = &data; } - if (convMap.empty()) - { - return; - } + // if convMap.empty(), we still need to update to remove existing entries. - TIMING_START(GuiUpdate); + TIMING_START(GuiUpdate) memoryGroup->tree()->update(convMap); - TIMING_END_STREAM(GuiUpdate, ARMARX_VERBOSE); + TIMING_END_STREAM(GuiUpdate, ARMARX_VERBOSE) if (debugObserver) { @@ -479,9 +513,11 @@ namespace armarx::armem::gui } } + const static std::string CONFIG_KEY_MEMORY = "MemoryViewer.MemoryNameSystem"; const static std::string CONFIG_KEY_DEBUG_OBSERVER = "MemoryViewer.DebugObserverName"; + void MemoryViewer::loadSettings(QSettings* settings) { mnsName = settings->value(QString::fromStdString(CONFIG_KEY_MEMORY), "MemoryNameSystem").toString().toStdString(); @@ -493,12 +529,12 @@ namespace armarx::armem::gui settings->setValue(QString::fromStdString(CONFIG_KEY_DEBUG_OBSERVER), QString::fromStdString(debugObserverName)); } + void MemoryViewer::writeConfigDialog(SimpleConfigDialog* dialog) { - dialog->addProxyFinder<armarx::armem::mns::MemoryNameSystemInterfacePrx>({CONFIG_KEY_MEMORY, "MemoryNameSystem", "*"}); + dialog->addProxyFinder<armarx::armem::mns::MemoryNameSystemInterfacePrx>({CONFIG_KEY_MEMORY, "MemoryNameSystem", "MemoryNameSystem"}); dialog->addProxyFinder<armarx::DebugObserverInterfacePrx>({CONFIG_KEY_DEBUG_OBSERVER, "Debug Observer", "DebugObserver"}); } - void MemoryViewer::readConfigDialog(SimpleConfigDialog* dialog) { mnsName = dialog->getProxyName(CONFIG_KEY_MEMORY); diff --git a/source/RobotAPI/libraries/armem_gui/MemoryViewer.h b/source/RobotAPI/libraries/armem_gui/MemoryViewer.h index 71870bdd565b0b8a585808f5f0e9f0f43c93d315..e29a2352014cebaf1a700097f7409f7f32cb5229 100644 --- a/source/RobotAPI/libraries/armem_gui/MemoryViewer.h +++ b/source/RobotAPI/libraries/armem_gui/MemoryViewer.h @@ -10,11 +10,11 @@ #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h> #include <RobotAPI/libraries/armem/client/Reader.h> +#include <RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.h> #include <RobotAPI/libraries/armem_gui/lifecycle.h> #include <RobotAPI/libraries/armem_gui/instance/GroupBox.h> #include <RobotAPI/libraries/armem_gui/memory/GroupBox.h> -#include <RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.h> -#include <RobotAPI/libraries/armem_gui/MemoryControlWidget.h> +#include <RobotAPI/libraries/armem_gui/disk/ControlWidget.h> class QBoxLayout; @@ -23,6 +23,7 @@ class QGroupBox; class QLabel; class QLayout; class QSettings; +class QTimer; namespace armarx @@ -47,9 +48,9 @@ namespace armarx::armem::gui MemoryViewer( QBoxLayout* updateWidgetLayout, - QBoxLayout* ltmControlWidgetLayout, QGroupBox* memoryGroupBox, QLayout* memoryGroupBoxParentLayout, QGroupBox* instanceGroupBox, QLayout* instanceGroupBoxParentLayout, + QBoxLayout* diskControlWidgetLayout, QLabel* statusLabel ); @@ -65,14 +66,16 @@ namespace armarx::armem::gui public slots: - void updateMemories(); void updateInstanceTree(const armem::MemoryID& selectedID); void resolveMemoryID(const MemoryID& id); - // LTMControlWidget + // Disk Control + void storeOnDisk(QString directory); + void loadFromDisk(QString directory); + void storeInLTM(); - void storeOnDisk(); + signals: @@ -87,8 +90,13 @@ namespace armarx::armem::gui private slots: + void startQueries(); + void processQueryResults(); + void updateMemoryTree(); + + signals: void memoryDataChanged(); @@ -101,6 +109,24 @@ namespace armarx::armem::gui void onDisconnect(ManagedIceObject& component); const armem::wm::Memory* getSingleMemoryData(const std::string& memoryName); + void updateStatusLabel(int errorCount); + + + + /// Start a query for each entry in `memoryReaders` which is not in `runningQueries`. + void startDueQueries( + std::map<std::string, Ice::AsyncResultPtr>& queries, + const std::map<std::string, armem::client::Reader>& readers); + + std::map<std::string, client::QueryResult> + collectQueryResults( + std::map<std::string, Ice::AsyncResultPtr>& queries, + const std::map<std::string, armem::client::Reader>& readers); + + void applyQueryResults( + const std::map<std::string, client::QueryResult>& results, + int* outErrorCount = nullptr); + public: @@ -111,11 +137,16 @@ namespace armarx::armem::gui std::map<std::string, armem::client::Reader> memoryReaders; std::map<std::string, armem::wm::Memory> memoryData; + std::map<std::string, Ice::AsyncResultPtr> runningQueries; + /// Periodically triggers query result collection. + QTimer* processQueryResultTimer; + + QLayout* updateWidgetLayout = nullptr; armem::gui::PeriodicUpdateWidget* updateWidget = nullptr; - QLayout* memoryControlWidgetLayout = nullptr; - armem::gui::MemoryControlWidget* memoryControlWidget = nullptr; + QLayout* diskControlLayout = nullptr; + armem::gui::disk::ControlWidget* diskControl = nullptr; armem::gui::MemoryGroupBox* memoryGroup = nullptr; diff --git a/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.cpp b/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.cpp index a62659cd78d4448bb72913ae432fc22f8f2b6eae..2e315e6f06f8e141c3911479685424b3f5585580 100644 --- a/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.cpp @@ -28,7 +28,7 @@ namespace armarx::armem::gui _frequencySpinBox->setValue(frequency); _frequencySpinBox->setMinimum(0); _frequencySpinBox->setMaximum(maxFrequency); - _frequencySpinBox->setSingleStep(1.0); + _frequencySpinBox->setSingleStep(0.5); _frequencySpinBox->setSuffix(" Hz"); @@ -46,8 +46,11 @@ namespace armarx::armem::gui connect(_frequencySpinBox, &QDoubleSpinBox::editingFinished, this, &This::_updateTimerFrequency); // Public connections. - connect(_updateButton, &QPushButton::pressed, this, &This::update); - connect(_timer, &QTimer::timeout, this, &This::update); + connect(_updateButton, &QPushButton::pressed, this, &This::updateSingle); + connect(_timer, &QTimer::timeout, this, &This::updatePeriodic); + + connect(this, &This::updateSingle, this, &This::update); + connect(this, &This::updatePeriodic, this, &This::update); } QPushButton* PeriodicUpdateWidget::updateButton() @@ -55,6 +58,12 @@ namespace armarx::armem::gui return _updateButton; } + + int PeriodicUpdateWidget::getUpdateIntervalMs() const + { + return static_cast<int>(std::round(1000 / _frequencySpinBox->value())); + } + void PeriodicUpdateWidget::startTimerIfEnabled() { if (_autoCheckBox->isChecked()) @@ -77,7 +86,7 @@ namespace armarx::armem::gui void PeriodicUpdateWidget::_updateTimerFrequency() { - _timer->setInterval(static_cast<int>(std::round(1000 / _frequencySpinBox->value()))); + _timer->setInterval(getUpdateIntervalMs()); } void PeriodicUpdateWidget::_toggleAutoUpdates(bool enabled) diff --git a/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.h b/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.h index 41b725220d283ddcae534d68c9932f89c89169e4..db8d9d5a30264180d93243c3a6f631f5666ed784 100644 --- a/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.h +++ b/source/RobotAPI/libraries/armem_gui/PeriodicUpdateWidget.h @@ -27,8 +27,9 @@ namespace armarx::armem::gui QDoubleSpinBox* frequencySpinBox(); QPushButton* updateButton(); - bool autoEnabled() const; - double updateFrequency() const; + bool isAutoEnabled() const; + double getUpdateFrequency() const; + int getUpdateIntervalMs() const; void startTimerIfEnabled(); void stopTimer(); @@ -40,6 +41,9 @@ namespace armarx::armem::gui void update(); + void updateSingle(); + void updatePeriodic(); + private slots: diff --git a/source/RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h b/source/RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h index 37de7699ce2c8d9245ced9e26fa8625d9b62159e..f3ca45cf17b05c72936a5afb8958e4eb5686aaab 100644 --- a/source/RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h +++ b/source/RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h @@ -16,10 +16,10 @@ namespace armarx * A class to efficiently build and maintain sorted items of `QTreeWidget` * or `QTreeWidgetItem` based on a sorted container matching the intended structure. */ - template <class ContainerT> + template <class _ElementT> struct TreeWidgetBuilder { - using ElementT = typename ContainerT::value_type; + using ElementT = _ElementT; /// Return < 0 if `element < item`, 0 if `element == item`, and > 0 if `element > item`. using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>; @@ -30,14 +30,15 @@ namespace armarx TreeWidgetBuilder() = default; /// Constructor to automatically derive the template argument. - TreeWidgetBuilder(const ContainerT&) + TreeWidgetBuilder(const ElementT&) {} TreeWidgetBuilder(CompareFn compareFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate) : compareFn(compareFn), makeItemFn(makeItemFn), updateItemFn(updateItemFn) {} - TreeWidgetBuilder(NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate) : - compareFn(MakeCompareNameFn(nameFn)), makeItemFn(makeItemFn), updateItemFn(updateItemFn) + TreeWidgetBuilder(NameFn nameFn, MakeItemFn makeItemFn, UpdateItemFn updateItemFn = NoUpdate, + int nameColumn = 0) : + compareFn(MakeCompareNameFn(nameFn, nameColumn)), makeItemFn(makeItemFn), updateItemFn(updateItemFn) {} @@ -45,9 +46,9 @@ namespace armarx { this->compareFn = compareFn; } - void setNameFn(NameFn nameFn) + void setNameFn(NameFn nameFn, int nameColumn = 0) { - compareFn = MakeCompareNameFn(nameFn); + compareFn = MakeCompareNameFn(nameFn, nameColumn); } void setMakeItemFn(MakeItemFn makeItemFn) { @@ -64,8 +65,31 @@ namespace armarx } - template <class ParentT> - void updateTree(ParentT* parent, const ContainerT& elements); + /** + * @brief Update the tree according to the elements iterated over by `iteratorFn`. + * + * @param parent + * The parent, i.e. the tree widget or a tree widget item. + * @param iteratorFn + * Function taking a function `bool fn(const ElementT& element)` + * and calling it for each element in the underlying container, i.e. like: + * void iteratorFn(bool (*elementFn)(const ElementT& element)); + */ + template <class ParentT, class IteratorFn> + void updateTreeWithIterator(ParentT* parent, IteratorFn&& iteratorFn); + + /** + * @brief Update the tree with the iterable container. + * + * @param parent + * The parent, i.e. the tree widget or a tree widget item. + * @param iteratorFn + * Function taking a function `bool fn(const ElementT& element)` + * and calling it for each element in the underlying container, i.e. like: + * void iteratorFn(bool (*elementFn)(const ElementT& element)); + */ + template <class ParentT, class ContainerT> + void updateTreeWithContainer(ParentT* parent, const ContainerT& elements); /// No update function (default). @@ -76,7 +100,7 @@ namespace armarx } /// Uses the name for comparison. - static CompareFn MakeCompareNameFn(NameFn nameFn); + static CompareFn MakeCompareNameFn(NameFn nameFn, int nameColumn); private: @@ -100,7 +124,7 @@ namespace armarx struct MapTreeWidgetBuilder { using MapT = std::map<KeyT, ValueT>; - using Base = TreeWidgetBuilder<MapT>; + using Base = TreeWidgetBuilder<typename MapT::value_type>; using ElementT = typename Base::ElementT; using CompareFn = std::function<int(const ElementT& element, QTreeWidgetItem* item)>; @@ -170,7 +194,7 @@ namespace armarx template <class ParentT> void updateTree(ParentT* tree, const MapT& elements) { - builder.updateTree(tree, elements); + builder.updateTreeWithContainer(tree, elements); } @@ -200,7 +224,7 @@ namespace armarx private: - TreeWidgetBuilder<MapT> builder; + TreeWidgetBuilder<typename MapT::value_type> builder; }; @@ -273,30 +297,52 @@ namespace armarx } - template <class ContainerT> - auto TreeWidgetBuilder<ContainerT>::MakeCompareNameFn(NameFn nameFn) -> CompareFn + template <class ElementT> + auto TreeWidgetBuilder<ElementT>::MakeCompareNameFn(NameFn nameFn, int nameColumn) -> CompareFn { - return [nameFn](const ElementT & element, QTreeWidgetItem * item) + return [nameFn, nameColumn](const ElementT & element, QTreeWidgetItem * item) { - return detail::compare(nameFn(element), item->text(0).toStdString()); + return detail::compare(nameFn(element), item->text(nameColumn).toStdString()); }; } - template <class ContainerT> - template <typename ParentT> - void TreeWidgetBuilder<ContainerT>::updateTree(ParentT* parent, const ContainerT& elements) + template <class ElementT> + template <class ParentT, class ContainerT> + void TreeWidgetBuilder<ElementT>::updateTreeWithContainer(ParentT* parent, const ContainerT& elements) + { + this->updateTreeWithIterator(parent, [&elements](auto&& elementFn) + { + for (const auto& element : elements) + { + if (not elementFn(element)) + { + break; + } + } + }); + } + + + template <class ElementT> + template <class ParentT, class IteratorFn> + void TreeWidgetBuilder<ElementT>::updateTreeWithIterator(ParentT* parent, IteratorFn&& iteratorFn) { using api = detail::ParentAPI<ParentT>; + ARMARX_CHECK_NOT_NULL(makeItemFn) << "makeItemFn must be set"; + ARMARX_CHECK_NOT_NULL(updateItemFn) << "updateItemFn must be set"; + ARMARX_CHECK_NOT_NULL(compareFn) << "compareFn must be set"; + int currentIndex = 0; - for (const auto& element : elements) + iteratorFn([this, &parent, ¤tIndex](const auto & element) { bool inserted = false; QTreeWidgetItem* item = nullptr; if (currentIndex >= api::getItemCount(parent)) { // Add elements to the end of the list. + ARMARX_CHECK_NOT_NULL(makeItemFn); item = makeItemFn(element); api::insertItem(parent, api::getItemCount(parent), item); ++currentIndex; @@ -331,11 +377,8 @@ namespace armarx { item->setExpanded(true); } - if (!cont) - { - break; - } - } + return cont; + }); // Remove superfluous items. (currentIndex must point behind the last item) while (api::getItemCount(parent) > currentIndex) { diff --git a/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e361438099e5e2a34b80d5f64f810a23fb3270d2 --- /dev/null +++ b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.cpp @@ -0,0 +1,196 @@ +#include "ControlWidget.h" + +#include <RobotAPI/libraries/armem/server/ltm/disk/MemoryManager.h> +#include <RobotAPI/libraries/armem/server/query_proc/ltm.h> + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <QHBoxLayout> +#include <QSpacerItem> +#include <QFileDialog> +#include <QPushButton> + +#include <filesystem> + +#include <cmath> + + +namespace armarx::armem::gui::disk +{ + + ControlWidget::ControlWidget() + { + _latestDirectory = QString::fromStdString("/tmp/MemoryExport"); + + _loadFromDiskButton = new QPushButton(" Load from Disk", this); + _loadFromDiskButton->setIcon(QIcon(":/icons/document-open.svg")); + _storeOnDiskButton = new QPushButton(" Store on Disk", this); + _storeOnDiskButton->setIcon(QIcon(":/icons/document-save.svg")); + + // Allow horizontal shrinking of buttons + std::vector<QPushButton*> buttons { _storeOnDiskButton, _loadFromDiskButton }; + for (QPushButton* button : buttons) + { + button->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); + } + + this->setSizePolicy(QSizePolicy::Policy::Preferred, QSizePolicy::Policy::Fixed); + + QHBoxLayout* layout = new QHBoxLayout(); + this->setLayout(layout); + + const int margin = 0; + layout->setContentsMargins(margin, margin, margin, margin); + + layout->addWidget(_loadFromDiskButton); + layout->addWidget(_storeOnDiskButton); + + + // Connections + + connect(_loadFromDiskButton, &QPushButton::pressed, [this]() + { + QString directory = chooseDirectoryDialog(); + if (directory.size() > 0) + { + emit requestedLoadFromDisk(directory); + } + }); + connect(_storeOnDiskButton, &QPushButton::pressed, [this]() + { + QString directory = chooseDirectoryDialog(); + if (directory.size() > 0) + { + emit requestedStoreOnDisk(directory); + } + }); + } + + + static + const std::string& + handleSingular(int num, const std::string& singular, const std::string& plural) + { + return num == 1 ? singular : plural; + } + + + void + ControlWidget::storeOnDisk( + QString directory, + const std::map<std::string, wm::Memory> memoryData, + std::string* outStatus) + { + std::filesystem::path path(directory.toUtf8().constData()); + ARMARX_CHECK_POSITIVE(path.string().size()); // An empty path indicates an error. + + std::stringstream status; + if (std::filesystem::is_regular_file(path)) + { + status << "Could not export memories contents to " << path << ": Cannot overwrite existing file."; + } + else + { + int numStored = 0; + for (const auto& [name, data] : memoryData) + { + if (std::filesystem::is_regular_file(path / name)) + { + status << "Could not export memory '" << name << "' to " << path << ": Cannot overwrite existing file.\n"; + } + else + { + std::filesystem::create_directories(path / name); + + armem::server::ltm::disk::MemoryManager manager; + manager.setName(name); + manager.setBasePath(path / name); + manager.reload(); + manager.append(data); + + numStored++; + } + } + status << "Exported " << numStored << " " << handleSingular(numStored, "memory", "memories") << " to " << path << "."; + } + + if (outStatus) + { + *outStatus = status.str(); + } + } + + + std::map<std::string, wm::Memory> + ControlWidget::loadFromDisk( + QString directory, + const armem::client::QueryInput& _queryInput, + std::string* outStatus) + { + std::filesystem::path path(directory.toUtf8().constData()); + + std::map<std::string, wm::Memory> memoryData; + std::stringstream status; + + if (not std::filesystem::is_directory(path)) + { + status << "Could not import a memory from " << path << ". Skipping import."; + } + else + { + // We use LTM as query target for the disk + armem::client::QueryInput queryInput = _queryInput; + queryInput.addQueryTargetToAll(armem::query::data::QueryTarget::LTM); + const query::data::Input queryIce = queryInput.toIce(); + + int numLoaded = 0; + for (const auto& dir : std::filesystem::directory_iterator(path)) + { + if (dir.is_directory()) + { + const std::string key = dir.path().filename(); + armem::server::ltm::disk::MemoryManager manager; + manager.setName(key); + manager.setBasePath(path / key); + + manager.reload(); + + armem::server::query_proc::ltm::MemoryQueryProcessor ltm_processor; + armem::wm::Memory query_res = ltm_processor.process(queryIce, manager.getCacheAndLutNotConverted()); + + manager.convert(query_res); + memoryData[key] = std::move(query_res); + + numLoaded++; + } + } + status << "Loaded " << numLoaded << " " << handleSingular(numLoaded, "memory", "memories") << " from " << path << "."; + } + + if (outStatus) + { + *outStatus = status.str(); + } + return memoryData; + } + + + QString ControlWidget::chooseDirectoryDialog() + { + QFileDialog dialog; + dialog.setFileMode(QFileDialog::DirectoryOnly); + dialog.setOption(QFileDialog::ShowDirsOnly, false); + dialog.setDirectory(_latestDirectory); + if (dialog.exec()) + { + _latestDirectory = dialog.directory().path(); + return _latestDirectory; + } + else + { + return QString::fromStdString(""); + } + } + +} + diff --git a/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.h b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..b14c9b0a0810acbafba74638e6ebd87989e81165 --- /dev/null +++ b/source/RobotAPI/libraries/armem_gui/disk/ControlWidget.h @@ -0,0 +1,62 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/forward_declarations.h> +#include <RobotAPI/libraries/armem/client/Query.h> + +#include <QWidget> +#include <QString> + + +class QPushButton; + + +namespace armarx::armem::gui::disk +{ + + class ControlWidget : public QWidget + { + Q_OBJECT + using This = ControlWidget; + + public: + + ControlWidget(); + + + void + storeOnDisk( + QString directory, + const std::map<std::string, wm::Memory> memoryData, + std::string* outStatus = nullptr); + + std::map<std::string, wm::Memory> + loadFromDisk( + QString directory, + const armem::client::QueryInput& queryInput, + std::string* outStatus = nullptr); + + + signals: + + void requestedLoadFromDisk(QString directory); + void requestedStoreOnDisk(QString directory); + + + private slots: + + QString chooseDirectoryDialog(); + + + signals: + + + private: + + QPushButton* _loadFromDiskButton; + QPushButton* _storeOnDiskButton; + + QString _latestDirectory; + + }; + +} diff --git a/source/RobotAPI/libraries/armem_gui/instance/GroupBox.cpp b/source/RobotAPI/libraries/armem_gui/instance/GroupBox.cpp index ff828d4437f042c7857332ec3957f9b5f55c60a5..6d233680524bef1b515236fb03ec6e7e8e965ec0 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/GroupBox.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/GroupBox.cpp @@ -15,7 +15,7 @@ namespace armarx::armem::gui::instance view = new armem::gui::InstanceView(); - useTypeInfoCheckBox = new QCheckBox("Use Type Info", this); + useTypeInfoCheckBox = new QCheckBox("Use Type Information", this); useTypeInfoCheckBox->setChecked(true); QHBoxLayout* checkBoxLayout = new QHBoxLayout(); diff --git a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp index 2b4ab121dabc7d52188f1d10b8adb501f2c72d32..8fda3cc4bafd3458cc76f1829466afc8f84abeb9 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.cpp @@ -104,7 +104,7 @@ namespace armarx::armem::gui::instance const armem::wm::EntityInstance* instance = nullptr; try { - instance = &memory.getEntityInstance(id); + instance = &memory.getInstance(id); if (useTypeInfo) { aronType = memory.getProviderSegment(id).aronType(); @@ -113,8 +113,7 @@ namespace armarx::armem::gui::instance catch (const armem::error::ArMemError& e) { showErrorMessage(e.what()); - return; - }; + } if (instance) { update(*instance, aronType); @@ -146,7 +145,7 @@ namespace armarx::armem::gui::instance void InstanceView::addInstanceView(const wm::EntityInstance& instance, aron::typenavigator::ObjectNavigatorPtr aronType) { - ARMARX_IMPORTANT << "Adding instance view with toolbar for instance: " << instance.id(); + // ARMARX_IMPORTANT << "Adding instance view with toolbar for instance: " << instance.id(); InstanceView* view = new InstanceView; view->setStatusLabel(statusLabel); view->setUseTypeInfo(useTypeInfo); @@ -175,6 +174,8 @@ namespace armarx::armem::gui::instance { if (!data) { + treeItemData->setText(int(Columns::TYPE), QString::fromStdString("")); + armarx::gui::clearItem(treeItemData); QTreeWidgetItem* item = new QTreeWidgetItem({"(No data.)"}); treeItemData->addChild(item); @@ -189,6 +190,8 @@ namespace armarx::armem::gui::instance } else { + treeItemData->setText(int(Columns::TYPE), QString::fromStdString("")); + DataTreeBuilder builder; builder.setColumns(int(Columns::KEY), int(Columns::VALUE), int(Columns::TYPE)); builder.updateTree(treeItemData, data); @@ -205,7 +208,7 @@ namespace armarx::armem::gui::instance armem::toDateTimeMilliSeconds(metadata.timeSent), armem::toDateTimeMilliSeconds(metadata.timeArrived) }; - ARMARX_CHECK_EQUAL(treeItemMetadata->childCount(), items.size()); + ARMARX_CHECK_EQUAL(static_cast<size_t>(treeItemMetadata->childCount()), items.size()); int i = 0; for (const std::string& item : items) { @@ -222,11 +225,18 @@ namespace armarx::armem::gui::instance } - aron::Path InstanceView::getElementPath(const QTreeWidgetItem* item) const + std::optional<aron::Path> InstanceView::getElementPath(const QTreeWidgetItem* item) const { QStringList qpath = item->data(int(Columns::KEY), Qt::UserRole).toStringList(); - aron::Path path = deserializePath(qpath); - return path; + if (qpath.empty()) + { + return std::nullopt; + } + else + { + aron::Path path = deserializePath(qpath); + return path; + } } @@ -258,14 +268,15 @@ namespace armarx::armem::gui::instance { case aron::type::Descriptor::eIVTCByteImage: { - const aron::Path path = getElementPath(item); - - QAction* viewAction = new QAction("Show image"); - menu.addAction(viewAction); - connect(viewAction, &QAction::triggered, [this, path]() + if (const std::optional<aron::Path> path = getElementPath(item)) { - this->showImageView(path); - }); + QAction* viewAction = new QAction("Show image"); + menu.addAction(viewAction); + connect(viewAction, &QAction::triggered, [this, path]() + { + this->showImageView(path.value()); + }); + } } break; default: @@ -276,17 +287,18 @@ namespace armarx::armem::gui::instance const std::string typeName = item->text(int(Columns::TYPE)).toStdString(); if (typeName == instance::sanitizedMemoryIDTypeName) { - const aron::Path path = getElementPath(item); - - if (std::optional<MemoryID> id = getElementMemoryID(path)) + if (const std::optional<aron::Path> path = getElementPath(item)) { - if (QAction* action = makeActionCopyMemoryID(id.value())) - { - menu.addAction(action); - } - if (QAction* action = makeActionResolveMemoryID(id.value())) + if (std::optional<MemoryID> id = getElementMemoryID(path.value())) { - menu.addAction(action); + if (QAction* action = makeActionCopyMemoryID(id.value())) + { + menu.addAction(action); + } + if (QAction* action = makeActionResolveMemoryID(id.value())) + { + menu.addAction(action); + } } } } diff --git a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.h b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.h index 26f9d3be5fccfdce4fdb17a9ddbe430c2f334433..76a3988db8112f9b9437172710e05a900bb95f8f 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/InstanceView.h +++ b/source/RobotAPI/libraries/armem_gui/instance/InstanceView.h @@ -5,9 +5,12 @@ #include <QWidget> #include <QGroupBox> -#include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> +#include <ArmarXCore/core/logging/Logging.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> +#include <RobotAPI/libraries/aron/core/Path.h> + +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> class QGroupBox; @@ -24,7 +27,7 @@ namespace armarx::armem::gui::instance class WidgetsWithToolbar; - class InstanceView : public QWidget, public Logging + class InstanceView : public QWidget, public armarx::Logging { Q_OBJECT using This = InstanceView; @@ -68,7 +71,7 @@ namespace armarx::armem::gui::instance void showErrorMessage(const std::string& message); - aron::Path getElementPath(const QTreeWidgetItem* item) const; + std::optional<aron::Path> getElementPath(const QTreeWidgetItem* item) const; std::optional<MemoryID> getElementMemoryID(const aron::Path& elementPath); QAction* makeActionResolveMemoryID(const MemoryID& id); diff --git a/source/RobotAPI/libraries/armem_gui/instance/InstanceViewList.h b/source/RobotAPI/libraries/armem_gui/instance/InstanceViewList.h index 5f937f419cd3111daf1146a6640e0ea9348f6d42..8dc24e0dcd9759f3ab89e21e33ff600ad253f082 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/InstanceViewList.h +++ b/source/RobotAPI/libraries/armem_gui/instance/InstanceViewList.h @@ -6,7 +6,7 @@ #include <RobotAPI/libraries/aron/aroncore/navigators/typenavigator/AronObjectTypeNavigator.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> class QGroupBox; diff --git a/source/RobotAPI/libraries/armem_gui/instance/MemoryIDTreeWidgetItem.cpp b/source/RobotAPI/libraries/armem_gui/instance/MemoryIDTreeWidgetItem.cpp index b8f568c79cdd1d84fa803df9ffaee737e24c9ea2..ebb4473bb1941a713526d646b37744f5a3314c1c 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/MemoryIDTreeWidgetItem.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/MemoryIDTreeWidgetItem.cpp @@ -33,7 +33,11 @@ namespace armarx::armem::gui::instance // Timestamp in human-readable format if (id.hasTimestamp()) { - child(4)->setText(valueColumn, QString::fromStdString(toDateTimeMilliSeconds(id.timestamp))); + static const char* mu = "\u03BC"; + std::stringstream ss; + ss << toDateTimeMilliSeconds(id.timestamp) + << " (" << id.timestamp.toMicroSeconds() << " " << mu << "s)"; + child(4)->setText(valueColumn, QString::fromStdString(ss.str())); } } diff --git a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp index 827d14a599af43e6067fd1b3fa18bb136dfcbed8..eb1cda5fd7ffd8b4b06f7286d7292e3a058cdc35 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.cpp @@ -3,6 +3,9 @@ #include <iomanip> // std::setprecision #include <SimoxUtility/algorithm/string.h> +#include <SimoxUtility/math/pose/pose.h> + +#include <ArmarXCore/core/logging/Logging.h> #include <RobotAPI/libraries/aron/core/Exception.h> #include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h> @@ -22,6 +25,14 @@ namespace armarx::aron return v.value.str(); } + + TypedDataDisplayVisitor::TypedDataDisplayVisitor() : + coeffSep(" "), + eigenIof(Eigen::StreamPrecision, 0, coeffSep, "\n", "", "", "", "") + { + } + + bool TypedDataDisplayVisitor::visitEnter(DictTypeNavigator&, DictDataNavigator& data) { value << DataDisplayVisitor::getValue(data); @@ -54,12 +65,14 @@ namespace armarx::aron bool TypedDataDisplayVisitor::visit(DoubleTypeNavigator&, DoubleDataNavigator& data) { + setStreamPrecision(); value << DataDisplayVisitor::getValue(data); return false; } bool TypedDataDisplayVisitor::visit(FloatTypeNavigator&, FloatDataNavigator& data) { + setStreamPrecision(); value << DataDisplayVisitor::getValue(data); return false; } @@ -85,20 +98,45 @@ namespace armarx::aron bool TypedDataDisplayVisitor::visit(TimeTypeNavigator&, LongDataNavigator& data) { armem::Time time = armem::Time::microSeconds(data.getValue()); - armem::toDateTimeMilliSeconds(time); value << armem::toDateTimeMilliSeconds(time); return false; } - bool TypedDataDisplayVisitor::visit(EigenMatrixTypeNavigator&, NDArrayDataNavigator& data) + + template <typename ScalarT> + void TypedDataDisplayVisitor::processEigenMatrix(EigenMatrixTypeNavigator& type, NDArrayDataNavigator& data) + { + Eigen::Map<Eigen::Matrix<ScalarT, Eigen::Dynamic, Eigen::Dynamic>> m(reinterpret_cast<ScalarT*>(data.getData()), type.getRows(), type.getCols()); + value << m.format(eigenIof); + } + + bool TypedDataDisplayVisitor::visit(EigenMatrixTypeNavigator& t, NDArrayDataNavigator& data) { - value << DataDisplayVisitor::getValue(data); + if (std::max(t.getRows(), t.getCols()) > 10) + { + // Just show the shape. + value << DataDisplayVisitor::getValue(data); + } + else if (data.getType() == "float") + { + setStreamPrecision(); + processEigenMatrix<float>(t, data); + } + else if (data.getType() == "double") + { + setStreamPrecision(); + processEigenMatrix<double>(t, data); + } + else + { + value << DataDisplayVisitor::getValue(data); + } return false; } bool TypedDataDisplayVisitor::visit(EigenQuaternionTypeNavigator&, NDArrayDataNavigator& data) { - value << DataDisplayVisitor::getValue(data); + processEigenQuaternion(data); return false; } @@ -122,26 +160,62 @@ namespace armarx::aron bool TypedDataDisplayVisitor::visit(PoseTypeNavigator&, NDArrayDataNavigator& data) { + const Eigen::IOFormat eigenIof(Eigen::StreamPrecision, 0, " ", "\n", "( ", " )", "", ""); + const std::string cdot = "\u00B7"; // center dot: https://unicode-table.com/de/00B7/ + + auto getLines = [&](auto&& mat) + { + std::stringstream ss; + setStreamPrecision(ss); + ss << mat.format(eigenIof); + std::vector<std::string> lines = simox::alg::split(ss.str(), "\n"); + ARMARX_CHECK_EQUAL(lines.size(), 3); + return lines; + }; + const Eigen::Matrix4f pose = aron::converter::AronEigenConverter::ConvertToMatrix4f(data); - value << std::setprecision(2) << std::fixed; - value << pose.format(Eigen::IOFormat(Eigen::StreamPrecision, 0, coeffSep, "\n", "", "", "", "")); + const std::vector<std::string> r = getLines(simox::math::orientation(pose)); + const std::vector<std::string> t = getLines(simox::math::position(pose)); + + std::vector<std::string> lines; + lines.push_back(r.at(0) + " \t" + t.at(0)); + lines.push_back(r.at(1) + " " + cdot + " x + \t" + t.at(1)); + lines.push_back(r.at(2) + " \t" + t.at(2)); + + value << simox::alg::join(lines, "\n"); return false; } + bool TypedDataDisplayVisitor::visit(PositionTypeNavigator&, NDArrayDataNavigator& data) { const Eigen::Vector3f pos = aron::converter::AronEigenConverter::ConvertToVector3f(data); - value << std::setprecision(2) << std::fixed; - value << pos.format(Eigen::IOFormat(Eigen::StreamPrecision, 0, "", coeffSep, "", "", "", "")); + setStreamPrecision(); + value << pos.format(eigenIof); return false; } bool TypedDataDisplayVisitor::visit(OrientationTypeNavigator&, NDArrayDataNavigator& data) + { + processEigenQuaternion(data); + return false; + } + + void TypedDataDisplayVisitor::processEigenQuaternion(visitor::TypedDataVisitor::NDArrayDataNavigator& data) { const Eigen::Quaternionf quat = aron::converter::AronEigenConverter::ConvertToQuaternionf(data); - value << std::setprecision(2) << std::fixed; + setStreamPrecision(); value << quat.w() << coeffSep << "|" << coeffSep << quat.x() << coeffSep << quat.y() << coeffSep << quat.z(); - return false; + } + + void TypedDataDisplayVisitor::setStreamPrecision() + { + setStreamPrecision(value); + } + + void TypedDataDisplayVisitor::setStreamPrecision(std::ostream& os) + { + os << std::setprecision(2) << std::fixed; } } diff --git a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h index 3923b7d0d74a1ee9c4b1ea7a5664ad3f83396051..87220babb8b64d78d7db6bfb1fe7cce41b070fd6 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h +++ b/source/RobotAPI/libraries/armem_gui/instance/display_visitors/TypedDataDisplayVisitor.h @@ -3,6 +3,8 @@ #include <sstream> #include <string> +#include <Eigen/Core> + #include <RobotAPI/libraries/aron/core/navigator/visitors/TypedDataVisitor.h> @@ -18,6 +20,9 @@ namespace armarx::aron public: + TypedDataDisplayVisitor(); + + std::stringstream value; @@ -49,7 +54,18 @@ namespace armarx::aron protected: - std::string coeffSep = " "; + std::string coeffSep; + Eigen::IOFormat eigenIof; + + + private: + + template <typename ScalarT> + void processEigenMatrix(EigenMatrixTypeNavigator&, NDArrayDataNavigator& data); + void processEigenQuaternion(NDArrayDataNavigator& data); + + void setStreamPrecision(); + void setStreamPrecision(std::ostream& os); }; diff --git a/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp b/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp index 0180b51961c795ca4a45fd748078cb5cda1ca663..c548192cb7a98a390f708c8bd7789e51e63089cc 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/sanitize_typename.cpp @@ -10,6 +10,28 @@ const std::string armarx::armem::gui::instance::rawMemoryIDTypeName = armarx::armem::arondto::MemoryID::toAronType()->getName(); const std::string armarx::armem::gui::instance::sanitizedMemoryIDTypeName = "MemoryID"; + +namespace +{ + std::string remove_prefix(const std::string& string, const std::string& prefix) + { + return simox::alg::starts_with(string, prefix) + ? string.substr(prefix.size(), std::string::npos) + : string; + } + std::string remove_wrap(const std::string& string, const std::string& prefix, const std::string& suffix) + { + if (simox::alg::starts_with(string, prefix) && simox::alg::ends_with(string, suffix)) + { + return string.substr(prefix.size(), string.size() - prefix.size() - suffix.size()); + } + else + { + return string; + } + } +} + std::string armarx::armem::gui::instance::sanitizeTypeName(const std::string& typeName) { if (typeName == rawMemoryIDTypeName) @@ -19,15 +41,25 @@ std::string armarx::armem::gui::instance::sanitizeTypeName(const std::string& ty namespace s = simox::alg; std::string n = typeName; - n = s::replace_all(n, "Aron", ""); - n = s::replace_all(n, "Type", ""); - n = s::replace_all(n, "type::", ""); - if (s::starts_with(n, "Object<") && s::ends_with(n, ">")) + n = s::replace_all(n, "type::Aron", ""); + n = s::replace_all(n, "data::Aron", ""); + + if (false) { - std::string begin = "Object<"; - std::string end = ">"; - n = n.substr(begin.size(), n.size() - begin.size() - end.size()); + 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 = remove_prefix(n, "Aron"); + n = remove_wrap(n, "Object<", ">"); if (true) { diff --git a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilder.cpp b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilder.cpp index a2811c0bc5a01ef29fbb9cc41d7b5f47118f5a9a..bdf6aed5bb00495bf74d0125a60b4e108e8bcd64 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilder.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilder.cpp @@ -22,7 +22,7 @@ namespace armarx::armem::gui::instance return true; }); - builder.updateTree(parent, data->getAllKeys()); + builder.updateTreeWithContainer(parent, data->getAllKeys()); } void DataTreeBuilder::updateTree(QTreeWidgetItem* parent, const aron::datanavigator::ListNavigatorPtr& data) @@ -36,27 +36,29 @@ namespace armarx::armem::gui::instance return true; }); - builder.updateTree(parent, getIndex(children.size())); + builder.updateTreeWithContainer(parent, getIndex(children.size())); } void DataTreeBuilder::update(QTreeWidgetItem* item, const std::string& key, const aron::datanavigator::NavigatorPtr& data) { - if (!data) + if (data) { - this->setRowTexts(item, key, "null"); - return; + this->setRowTexts(item, key, *data); + + if (auto cast = aron::datanavigator::DictNavigator::DynamicCast(data)) + { + updateTree(item, cast); + } + else if (auto cast = aron::datanavigator::ListNavigator::DynamicCast(data)) + { + updateTree(item, cast); + } } - - this->setRowTexts(item, key, *data); - - if (auto cast = aron::datanavigator::DictNavigator::DynamicCast(data)) - { - updateTree(item, cast); - } - else if (auto cast = aron::datanavigator::ListNavigator::DynamicCast(data)) + else { - updateTree(item, cast); + // Optional data? + this->setRowTexts(item, key, "(none)"); } } diff --git a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.cpp b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.cpp index f837fe2fa6451cf8edd82793f2c6ec773560ce2c..8a73afd2069896bbea2577b8f6d73f7df95b1ab5 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.cpp @@ -3,14 +3,19 @@ #include <QTreeWidgetItem> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> +// #include <ArmarXCore/core/logging/Logging.h> #include <RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h> +#include <RobotAPI/libraries/armem_gui/instance/sanitize_typename.h> #include <RobotAPI/libraries/armem_gui/instance/display_visitors/DataDisplayVisitor.h> namespace armarx::armem::gui::instance { + const int keyIndexRole = Qt::UserRole + 10; + + DataTreeBuilderBase::DataTreeBuilderBase() { } @@ -34,7 +39,7 @@ namespace armarx::armem::gui::instance QTreeWidgetItem* DataTreeBuilderBase::makeItem(size_t key) const { QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setData(0, Qt::UserRole, static_cast<int>(key)); + item->setData(0, keyIndexRole, static_cast<int>(key)); return item; } @@ -50,7 +55,7 @@ namespace armarx::armem::gui::instance QTreeWidgetItem* item, const std::string& key, aron::datanavigator::Navigator& data) { const std::string value = armarx::aron::DataDisplayVisitor::getValue(data); - setRowTexts(item, key, value, data.getName()); + setRowTexts(item, key, value, sanitizeTypeName(data.getName())); } DataTreeBuilderBase::DictBuilder DataTreeBuilderBase::getDictBuilder() const @@ -73,7 +78,11 @@ namespace armarx::armem::gui::instance ListBuilder builder; builder.setCompareFn([](size_t key, QTreeWidgetItem * item) { - return armarx::detail::compare(static_cast<int>(key), item->data(0, Qt::UserRole).toInt()); + QVariant itemKey = item->data(0, keyIndexRole); + ARMARX_CHECK_EQUAL(itemKey.type(), QVariant::Type::Int); + // << VAROUT(key) << " | " << VAROUT(item->text(0).toStdString()) << itemKey.typeName(); + + return armarx::detail::compare(static_cast<int>(key), itemKey.toInt()); }); builder.setMakeItemFn([this](size_t key) { diff --git a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.h b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.h index b26a422ea71c90c2b769217a274eac7887eb8f9d..d8ce4516b43c7cbccfaff7bfbd5f362d6dbfef0a 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.h +++ b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/DataTreeBuilderBase.h @@ -28,8 +28,8 @@ namespace armarx::armem::gui::instance protected: - using DictBuilder = armarx::TreeWidgetBuilder<std::vector<std::string>>; - using ListBuilder = armarx::TreeWidgetBuilder<std::vector<size_t>>; + using DictBuilder = armarx::TreeWidgetBuilder<std::string>; + using ListBuilder = armarx::TreeWidgetBuilder<size_t>; DictBuilder getDictBuilder() const; ListBuilder getListBuilder() const; @@ -42,6 +42,7 @@ namespace armarx::armem::gui::instance void setRowTexts(QTreeWidgetItem* item, const std::string& key, const std::string& value, const std::string& typeName = "") const; void setRowTexts(QTreeWidgetItem* item, const std::string& key, aron::datanavigator::Navigator& data); + public: int columnKey = 0; 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 56d204abf42573540c363c0973f4bf10db357317..bda945607d2dc2e11b40957941b370613825aad3 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.cpp +++ b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.cpp @@ -4,6 +4,7 @@ #include <RobotAPI/libraries/aron/common/aron_conversions.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> +#include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/aron/MemoryID.aron.generated.h> #include <RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h> @@ -35,12 +36,12 @@ namespace armarx::armem::gui::instance auto childData = data.getElement(key); if (childData) { - this->update(item, key, *childType, *childData); + this->update(item, key, *childType, childData.get()); } return true; }); - builder.updateTree(parent, data.getAllKeys()); + builder.updateTreeWithContainer(parent, data.getAllKeys()); } } @@ -69,14 +70,14 @@ namespace armarx::armem::gui::instance auto childType = type.getMemberType(key); auto childData = data.getElement(key); - if (childType && childData) + if (childType) { - this->update(item, key, *childType, *childData); + this->update(item, key, *childType, childData.get()); } return true; }); - builder.updateTree(parent, type.getAllKeys()); + builder.updateTreeWithContainer(parent, type.getAllKeys()); } @@ -92,15 +93,14 @@ namespace armarx::armem::gui::instance ListBuilder builder = getListBuilder(); builder.setUpdateItemFn([this, &children, &childType](size_t key, QTreeWidgetItem * item) { - auto childData = children.at(key); - if (childData) + if (auto childData = children.at(key)) { - this->update(item, std::to_string(key), *childType, *childData); + this->update(item, std::to_string(key), *childType, childData.get()); } return true; }); - builder.updateTree(parent, getIndex(children.size())); + builder.updateTreeWithContainer(parent, getIndex(children.size())); } } @@ -119,14 +119,14 @@ namespace armarx::armem::gui::instance auto childType = i == 0 ? childTypes.first : childTypes.second; auto childData = data.getElement(static_cast<unsigned int>(i)); - if (childType && childData) + if (childType) { - this->update(item, std::to_string(i), *childType, *childData); + this->update(item, std::to_string(i), *childType, childData.get()); } return true; }); - builder.updateTree(parent, getIndex(data.childrenSize())); + builder.updateTreeWithContainer(parent, getIndex(data.childrenSize())); } @@ -143,14 +143,14 @@ namespace armarx::armem::gui::instance auto childType = childTypes.at(i); auto childData = data.getElement(static_cast<unsigned int>(i)); - if (childType && childData) + if (childType) { - this->update(item, std::to_string(i), *childType, *childData); + this->update(item, std::to_string(i), *childType, childData.get()); } return true; }); - builder.updateTree(parent, getIndex(type.getAcceptedTypes().size())); + builder.updateTreeWithContainer(parent, getIndex(type.getAcceptedTypes().size())); } @@ -158,21 +158,30 @@ namespace armarx::armem::gui::instance QTreeWidgetItem* item, const std::string& key, aron::typenavigator::Navigator& type, - aron::datanavigator::Navigator& data) + aron::datanavigator::Navigator* data) { using namespace aron; - const std::string value = aron::TypedDataDisplayVisitor::getValue(type, data); - const std::string typeName = instance::sanitizeTypeName(type.getName()); + const std::string value = data ? aron::TypedDataDisplayVisitor::getValue(type, *data) : "(none)"; + std::string typeName = instance::sanitizeTypeName(type.getName()); + switch (type.getMaybe()) + { + case aron::type::Maybe::eOptional: + typeName = "Optional[" + typeName + "]"; + break; + default: + break; + } + setRowTexts(item, key, value, typeName); - item->setData(columnKey, Qt::UserRole, instance::serializePath(data.getPath())); + item->setData(columnKey, Qt::UserRole, data ? instance::serializePath(data->getPath()) : QStringList()); item->setData(columnType, Qt::UserRole, static_cast<int>(type.getDescriptor())); if (typeName == sanitizedMemoryIDTypeName) { MemoryIDTreeWidgetItem* memoryIDItem = dynamic_cast<MemoryIDTreeWidgetItem*>(item); - datanavigator::DictNavigator* dictData = dynamic_cast<datanavigator::DictNavigator*>(&data); + datanavigator::DictNavigator* dictData = dynamic_cast<datanavigator::DictNavigator*>(data); if (memoryIDItem && dictData) { arondto::MemoryID dto; @@ -185,25 +194,28 @@ namespace armarx::armem::gui::instance } } - if (auto t = dynamic_cast<typenavigator::ObjectNavigator*>(&type)) - { - _updateTree<datanavigator::DictNavigator>(item, *t, data); - } - else if (auto t = dynamic_cast<typenavigator::DictNavigator*>(&type)) - { - _updateTree<datanavigator::DictNavigator>(item, *t, data); - } - else if (auto t = dynamic_cast<typenavigator::ListNavigator*>(&type)) - { - _updateTree<datanavigator::ListNavigator>(item, *t, data); - } - else if (auto t = dynamic_cast<typenavigator::PairNavigator*>(&type)) - { - _updateTree<datanavigator::ListNavigator>(item, *t, data); - } - else if (auto t = dynamic_cast<typenavigator::TupleNavigator*>(&type)) + if (data) { - _updateTree<datanavigator::ListNavigator>(item, *t, data); + if (auto t = dynamic_cast<typenavigator::ObjectNavigator*>(&type)) + { + _updateTree<datanavigator::DictNavigator>(item, *t, *data); + } + else if (auto t = dynamic_cast<typenavigator::DictNavigator*>(&type)) + { + _updateTree<datanavigator::DictNavigator>(item, *t, *data); + } + else if (auto t = dynamic_cast<typenavigator::ListNavigator*>(&type)) + { + _updateTree<datanavigator::ListNavigator>(item, *t, *data); + } + else if (auto t = dynamic_cast<typenavigator::PairNavigator*>(&type)) + { + _updateTree<datanavigator::ListNavigator>(item, *t, *data); + } + else if (auto t = dynamic_cast<typenavigator::TupleNavigator*>(&type)) + { + _updateTree<datanavigator::ListNavigator>(item, *t, *data); + } } } diff --git a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.h b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.h index 57580a1f6d3a84ca639131e0da375a6129650e0a..0dead6736a93073854f8593d423ba03e60e8e0cb 100644 --- a/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.h +++ b/source/RobotAPI/libraries/armem_gui/instance/tree_builders/TypedDataTreeBuilder.h @@ -52,7 +52,7 @@ namespace armarx::armem::gui::instance void update(QTreeWidgetItem* item, const std::string& key, aron::typenavigator::Navigator& type, - aron::datanavigator::Navigator& data); + aron::datanavigator::Navigator* data); template <class DataT, class TypeT> void _updateTree(QTreeWidgetItem* item, TypeT& type, aron::datanavigator::Navigator& data); diff --git a/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.cpp b/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.cpp index 1d4a2b908eba38fbb4be2d028030598c220aae44..1ff5b509001d5cdb4d095442b4614878396b4157 100644 --- a/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.cpp @@ -4,8 +4,6 @@ #include <RobotAPI/libraries/aron/core/navigator/type/container/Object.h> -#include <ArmarXCore/core/exceptions/local/ExpressionException.h> - #include <SimoxUtility/algorithm/string.h> #include <QHeaderView> @@ -51,12 +49,12 @@ namespace armarx::armem::gui::memory void TreeWidget::initBuilders() { - workingmemoryBuilder.setExpand(true); - workingmemoryBuilder.setMakeItemFn([this](const std::string & name, const wm::Memory * memory) + memoryBuilder.setExpand(true); + memoryBuilder.setMakeItemFn([this](const std::string & name, const wm::Memory * memory) { return makeItem(name, *memory); }); - workingmemoryBuilder.setUpdateItemFn([this](const std::string&, const wm::Memory * memory, QTreeWidgetItem * memoryItem) + memoryBuilder.setUpdateItemFn([this](const std::string&, const wm::Memory * memory, QTreeWidgetItem * memoryItem) { updateContainerItem(*memory, memoryItem); if (memoryItem) @@ -66,25 +64,31 @@ namespace armarx::armem::gui::memory return true; }); + auto nameFn = [](const auto & element) + { + return element.name(); + }; - workingmemoryCoreSegmentBuilder.setExpand(true); - workingmemoryCoreSegmentBuilder.setMakeItemFn([this](const std::string & name, const wm::CoreSegment & coreSeg) + coreSegmentBuilder.setExpand(true); + coreSegmentBuilder.setNameFn(nameFn, int(Columns::KEY)); + coreSegmentBuilder.setMakeItemFn([this](const wm::CoreSegment & coreSeg) { - return makeItem(name, coreSeg); + return makeItem(coreSeg.name(), coreSeg); }); - workingmemoryCoreSegmentBuilder.setUpdateItemFn([this](const std::string&, const wm::CoreSegment & coreSeg, QTreeWidgetItem * coreSegItem) + coreSegmentBuilder.setUpdateItemFn([this](const wm::CoreSegment & coreSeg, QTreeWidgetItem * coreSegItem) { updateContainerItem(coreSeg, coreSegItem); updateChildren(coreSeg, coreSegItem); return true; }); - workingmemoryProvSegmentBuilder.setExpand(true); - workingmemoryProvSegmentBuilder.setMakeItemFn([this](const std::string & name, const wm::ProviderSegment & provSeg) + provSegmentBuilder.setExpand(true); + provSegmentBuilder.setNameFn(nameFn, int(Columns::KEY)); + provSegmentBuilder.setMakeItemFn([this](const wm::ProviderSegment & provSeg) { - return makeItem(name, provSeg); + return makeItem(provSeg.name(), provSeg); }); - workingmemoryProvSegmentBuilder.setUpdateItemFn([this](const std::string&, const wm::ProviderSegment & provSeg, QTreeWidgetItem * provSegItem) + provSegmentBuilder.setUpdateItemFn([this](const wm::ProviderSegment & provSeg, QTreeWidgetItem * provSegItem) { updateContainerItem(provSeg, provSegItem); updateChildren(provSeg, provSegItem); @@ -92,46 +96,46 @@ namespace armarx::armem::gui::memory }); // entityBuilder.setExpand(true); - workingmemoryEntityBuilder.setMakeItemFn([this](const std::string & name, const wm::Entity & entity) + entityBuilder.setNameFn(nameFn, int(Columns::KEY)); + entityBuilder.setMakeItemFn([this](const wm::Entity & entity) { - return makeItem(name, entity); + return makeItem(entity.name(), entity); }); - workingmemoryEntityBuilder.setUpdateItemFn([this](const std::string&, const wm::Entity & entity, QTreeWidgetItem * entityItem) + entityBuilder.setUpdateItemFn([this](const wm::Entity & entity, QTreeWidgetItem * entityItem) { updateContainerItem(entity, entityItem); updateChildren(entity, entityItem); return true; }); - workingmemorySnapshotBuilder.setMakeItemFn([this](const armem::Time & time, const wm::EntitySnapshot & snapshot) + snapshotBuilder.setMakeItemFn([this](const wm::EntitySnapshot & snapshot) { - QTreeWidgetItem* item = makeItem(toDateTimeMilliSeconds(time), snapshot); + QTreeWidgetItem* item = makeItem(toDateTimeMilliSeconds(snapshot.time()), snapshot); item->setData(int(Columns::KEY), Qt::ItemDataRole::UserRole, QVariant(static_cast<qlonglong>(snapshot.time().toMicroSeconds()))); return item; }); - workingmemorySnapshotBuilder.setCompareFn([](const auto & pair, QTreeWidgetItem * item) + snapshotBuilder.setCompareFn([](const wm::EntitySnapshot & snapshot, QTreeWidgetItem * item) { - const wm::EntitySnapshot& snapshot = pair.second; return armarx::detail::compare(static_cast<qlonglong>(snapshot.time().toMicroSeconds()), item->data(int(Columns::KEY), Qt::ItemDataRole::UserRole).toLongLong()); }); - workingmemorySnapshotBuilder.setUpdateItemFn([this](const armem::Time&, const wm::EntitySnapshot & snapshot, QTreeWidgetItem * snapshotItem) + snapshotBuilder.setUpdateItemFn([this](const wm::EntitySnapshot & snapshot, QTreeWidgetItem * snapshotItem) { updateContainerItem(snapshot, snapshotItem); updateChildren(snapshot, snapshotItem); return true; }); - workingmemoryInstanceBuilder.setMakeItemFn([this](const wm::EntityInstance & instance) + instanceBuilder.setMakeItemFn([this](const wm::EntityInstance & instance) { QTreeWidgetItem* item = makeItem("", instance); return item; }); - workingmemoryInstanceBuilder.setCompareFn([](const wm::EntityInstance & lhs, QTreeWidgetItem * rhsItem) + instanceBuilder.setCompareFn([](const wm::EntityInstance & lhs, QTreeWidgetItem * rhsItem) { return armarx::detail::compare(lhs.index(), rhsItem->text(0).toInt()); }); - workingmemoryInstanceBuilder.setUpdateItemFn([this](const wm::EntityInstance & instance, QTreeWidgetItem * instanceItem) + instanceBuilder.setUpdateItemFn([this](const wm::EntityInstance & instance, QTreeWidgetItem * instanceItem) { updateItemItem(instance, instanceItem); updateChildren(instance, instanceItem); @@ -183,27 +187,27 @@ namespace armarx::armem::gui::memory _selectedID = id; const std::string levelName = item->data(int(Columns::LEVEL), Qt::UserRole).toString().toStdString(); - if (levelName == wm::Memory().getLevelName()) + if (levelName == wm::Memory::getLevelName()) { emit memorySelected(*_selectedID); } - else if (levelName == wm::CoreSegment().getLevelName()) + else if (levelName == wm::CoreSegment::getLevelName()) { emit coreSegmentSelected(*_selectedID); } - else if (levelName == wm::ProviderSegment().getLevelName()) + else if (levelName == wm::ProviderSegment::getLevelName()) { emit providerSegmentSelected(*_selectedID); } - else if (levelName == wm::Entity().getLevelName()) + else if (levelName == wm::Entity::getLevelName()) { emit entitySelected(*_selectedID); } - else if (levelName == wm::EntitySnapshot().getLevelName()) + else if (levelName == wm::EntitySnapshot::getLevelName()) { emit snapshotSelected(*_selectedID); } - else if (levelName == wm::EntityInstance().getLevelName()) + else if (levelName == wm::EntityInstance::getLevelName()) { emit instanceSelected(*_selectedID); } @@ -213,6 +217,16 @@ namespace armarx::armem::gui::memory emit selectedItemChanged(*_selectedID); } + template <class ContainerT> + static + auto makeIteratorFn(const ContainerT& container) + { + return [&container](auto&& elementFn) + { + container.forEachChild(elementFn); + }; + } + void TreeWidget::updateChildren(const armem::wm::Memory& memory, QTreeWidget* tree) { @@ -221,33 +235,33 @@ namespace armarx::armem::gui::memory void TreeWidget::updateChildren(const std::map<std::string, const armem::wm::Memory*>& memories, QTreeWidget* tree) { - workingmemoryBuilder.updateTree(tree, memories); + memoryBuilder.updateTree(tree, memories); } void TreeWidget::updateChildren(const armem::wm::Memory& memory, QTreeWidgetItem* memoryItem) { - workingmemoryCoreSegmentBuilder.updateTree(memoryItem, memory.coreSegments()); + coreSegmentBuilder.updateTreeWithIterator(memoryItem, makeIteratorFn(memory)); } void TreeWidget::updateChildren(const armem::wm::CoreSegment& coreSeg, QTreeWidgetItem* coreSegItem) { - workingmemoryProvSegmentBuilder.updateTree(coreSegItem, coreSeg.providerSegments()); + provSegmentBuilder.updateTreeWithIterator(coreSegItem, makeIteratorFn(coreSeg)); } void TreeWidget::updateChildren(const armem::wm::ProviderSegment& provSeg, QTreeWidgetItem* provSegItem) { - workingmemoryEntityBuilder.updateTree(provSegItem, provSeg.entities()); + entityBuilder.updateTreeWithIterator(provSegItem, makeIteratorFn(provSeg)); } void TreeWidget::updateChildren(const armem::wm::Entity& entity, QTreeWidgetItem* entityItem) { - workingmemorySnapshotBuilder.updateTree(entityItem, entity.history()); + snapshotBuilder.updateTreeWithIterator(entityItem, makeIteratorFn(entity)); } void TreeWidget::updateChildren(const armem::wm::EntitySnapshot& snapshot, QTreeWidgetItem* snapshotItem) { - workingmemoryInstanceBuilder.updateTree(snapshotItem, snapshot.instances()); + instanceBuilder.updateTreeWithIterator(snapshotItem, makeIteratorFn(snapshot)); } void TreeWidget::updateChildren(const armem::wm::EntityInstance& data, QTreeWidgetItem* dataItem) @@ -255,19 +269,25 @@ namespace armarx::armem::gui::memory (void) data, (void) dataItem; } - QTreeWidgetItem* TreeWidget::makeItem(const std::string& key, const armem::base::detail::MemoryItem& memoryItem) + template <class MemoryItemT> + QTreeWidgetItem* TreeWidget::makeItem(const std::string& key, const MemoryItemT& memoryItem) { (void) key; + return makeItem(memoryItem.getKeyString(), MemoryItemT::getLevelName(), memoryItem.id()); + } + + QTreeWidgetItem* TreeWidget::makeItem(const std::string& key, const std::string& levelName, const MemoryID& id) + { QStringList columns; - columns.insert(int(Columns::KEY), QString::fromStdString(memoryItem.getKeyString())); + columns.insert(int(Columns::KEY), QString::fromStdString(key)); columns.insert(int(Columns::SIZE), ""); columns.insert(int(Columns::TYPE), ""); - columns.insert(int(Columns::LEVEL), QString::fromStdString(simox::alg::capitalize_words(memoryItem.getLevelName()))); - columns.insert(int(Columns::ID), QString::fromStdString(memoryItem.id().str())); + columns.insert(int(Columns::LEVEL), QString::fromStdString(simox::alg::capitalize_words(levelName))); + columns.insert(int(Columns::ID), QString::fromStdString(id.str())); QTreeWidgetItem* item = new QTreeWidgetItem(columns); - item->setData(int(Columns::LEVEL), Qt::UserRole, QString::fromStdString(memoryItem.getLevelName())); - item->setData(int(Columns::ID), Qt::UserRole, QString::fromStdString(memoryItem.id().str())); + item->setData(int(Columns::LEVEL), Qt::UserRole, QString::fromStdString(levelName)); + item->setData(int(Columns::ID), Qt::UserRole, QString::fromStdString(id.str())); item->setTextAlignment(int(Columns::SIZE), Qt::AlignRight); return item; } @@ -277,16 +297,16 @@ namespace armarx::armem::gui::memory (void) level, (void) item; } - template <class... T> - void TreeWidget::updateContainerItem( - const base::detail::MemoryContainerBase<T...>& container, QTreeWidgetItem* item) + template <class ContainerT> + void TreeWidget::updateContainerItem(const ContainerT& container, QTreeWidgetItem* item) { updateItemItem(container, item); item->setText(int(Columns::SIZE), QString::number(container.size())); - if constexpr(std::is_base_of_v<base::detail::AronTyped, base::detail::MemoryContainerBase<T...>>) + // Does not work + if constexpr(std::is_base_of_v<base::detail::AronTyped, ContainerT>) { - const base::detail::AronTyped& cast = dynamic_cast<const base::detail::AronTyped&>(container); + const base::detail::AronTyped& cast = static_cast<const base::detail::AronTyped&>(container); std::string typeName; if (cast.aronType()) { diff --git a/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h b/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h index 2bcf0fa1b6135534407044a3ce341b86afeab442..75f56c9ace248e58daf3f7ee3ac69baa12b0edfe 100644 --- a/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h +++ b/source/RobotAPI/libraries/armem_gui/memory/TreeWidget.h @@ -4,8 +4,7 @@ #include <QTreeWidget> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/longtermmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_gui/TreeWidgetBuilder.h> @@ -67,21 +66,23 @@ namespace armarx::armem::gui::memory void updateChildren(const armem::wm::EntityInstance& data, QTreeWidgetItem* parent); - QTreeWidgetItem* makeItem(const std::string& key, const armem::base::detail::MemoryItem& container); + template <class MemoryItemT> + QTreeWidgetItem* makeItem(const std::string& key, const MemoryItemT& memoryItem); + QTreeWidgetItem* makeItem(const std::string& key, const std::string& levelName, const MemoryID& id); void updateItemItem(const armem::base::detail::MemoryItem& level, QTreeWidgetItem* item); - template <class... T> - void updateContainerItem(const armem::base::detail::MemoryContainerBase<T...>& container, QTreeWidgetItem* item); + template <class ContainerT> + void updateContainerItem(const ContainerT& container, QTreeWidgetItem* item); private: - MapTreeWidgetBuilder<std::string, const wm::Memory*> workingmemoryBuilder; - MapTreeWidgetBuilder<std::string, wm::CoreSegment> workingmemoryCoreSegmentBuilder; - MapTreeWidgetBuilder<std::string, wm::ProviderSegment> workingmemoryProvSegmentBuilder; - MapTreeWidgetBuilder<std::string, wm::Entity> workingmemoryEntityBuilder; - MapTreeWidgetBuilder<armem::Time, wm::EntitySnapshot> workingmemorySnapshotBuilder; - TreeWidgetBuilder<std::vector<wm::EntityInstance>> workingmemoryInstanceBuilder; + MapTreeWidgetBuilder<std::string, const wm::Memory*> memoryBuilder; + TreeWidgetBuilder<wm::CoreSegment> coreSegmentBuilder; + TreeWidgetBuilder<wm::ProviderSegment> provSegmentBuilder; + TreeWidgetBuilder<wm::Entity> entityBuilder; + TreeWidgetBuilder<wm::EntitySnapshot> snapshotBuilder; + TreeWidgetBuilder<wm::EntityInstance> instanceBuilder; std::optional<MemoryID> _selectedID; /// While this is false, do not handle selection updates. diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp index dbb3d3fcabfa2ccf0034d1ba72027664bebe6ebc..f549a9e86046f40feb4087d28acd48726293a5c7 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.cpp @@ -1,5 +1,8 @@ #include "QueryWidget.h" +#include <QWidget> +#include <QTabWidget> +#include <QPushButton> #include <QCheckBox> #include <QHBoxLayout> #include <QVBoxLayout> @@ -12,25 +15,35 @@ namespace armarx::armem::gui QueryWidget::QueryWidget() { - QVBoxLayout* layout = new QVBoxLayout(); - setLayout(layout); + QHBoxLayout* hlayout1 = new QHBoxLayout(); + QHBoxLayout* hlayout2 = new QHBoxLayout(); + QVBoxLayout* vlayout = new QVBoxLayout(); _dataCheckBox = new QCheckBox("Get Data"); _dataCheckBox->setChecked(true); + _storeInLTMButton = new QPushButton("Store query result in LTM"); + _tabWidget = new QTabWidget(); _snapshotSelectorWidget = new SnapshotSelectorWidget(); _tabWidget->addTab(_snapshotSelectorWidget, QString("Snapshots")); - layout->addWidget(_dataCheckBox); - layout->addWidget(_tabWidget); + hlayout1->addWidget(_dataCheckBox); + hlayout1->addWidget(_storeInLTMButton); + + hlayout2->addWidget(_tabWidget); const int margin = 0; - layout->setContentsMargins(margin, margin, margin, margin); + vlayout->setContentsMargins(margin, margin, margin, margin); + + vlayout->addLayout(hlayout1); + vlayout->addLayout(hlayout2); + // Public connections. + connect(_storeInLTMButton, &QPushButton::pressed, this, &This::storeInLTM); - // connect to queryChanged + setLayout(vlayout); } @@ -41,11 +54,6 @@ namespace armarx::armem::gui : armem::DataMode::NoData; } - bool QueryWidget::alsoQueryLocalDisk() const - { - return _snapshotSelectorWidget->queryTargetContainsLocalDisk(); - } - armem::client::QueryInput QueryWidget::queryInput() { armem::client::query::Builder qb(dataMode()); diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h index d2c3fecd6198ad2ad5c3f37077eb07f4f365dea9..2bc7f223d5db52435b7571970bb4cd398aaeec49 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/QueryWidget.h @@ -1,13 +1,13 @@ #pragma once -#include <QWidget> -#include <QTabWidget> - #include <RobotAPI/libraries/armem/core/DataMode.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include "SnapshotSelectorWidget.h" +class QWidget; +class QTabWidget; +class QPushButton; namespace armarx::armem::gui { @@ -15,19 +15,21 @@ namespace armarx::armem::gui class QueryWidget : public QWidget { Q_OBJECT + using This = QueryWidget; + public: QueryWidget(); armem::DataMode dataMode() const; - bool alsoQueryLocalDisk() const; armem::client::QueryInput queryInput(); public slots: signals: + void storeInLTM(); // ToDo: // void queryChanged(armem::query::data::Input query); @@ -42,6 +44,8 @@ namespace armarx::armem::gui private: QCheckBox* _dataCheckBox; + QPushButton* _storeInLTMButton; + QTabWidget* _tabWidget; SnapshotSelectorWidget* _snapshotSelectorWidget; diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp index 9d5cc9ea6064bb6977892623649518791dcaffec..6b38efdddb30e67d1abfb3190d88508dff72258a 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.cpp @@ -29,27 +29,14 @@ namespace armarx::armem::gui return targets; } - void SnapshotSelectorWidget::localDiskStateChanged() + void SnapshotSelectorWidget::queryTargetStateChanged() { - if (_LocalDiskMemoryQueryTargetCheckBox->isChecked()) + if (!_WMQueryTargetCheckBox->isChecked() && !_LTMQueryTargetCheckBox->isChecked()) { - _WMQueryTargetCheckBox->setChecked(false); - _LTMQueryTargetCheckBox->setChecked(false); - _WMQueryTargetCheckBox->setEnabled(false); - _LTMQueryTargetCheckBox->setEnabled(false); - } - else - { - _WMQueryTargetCheckBox->setEnabled(true); - _LTMQueryTargetCheckBox->setEnabled(true); + _WMQueryTargetCheckBox->setChecked(true); } } - bool SnapshotSelectorWidget::queryTargetContainsLocalDisk() const - { - return _LocalDiskMemoryQueryTargetCheckBox->isChecked(); - } - SnapshotSelectorWidget::SnapshotSelectorWidget() { _pageLayout = new QVBoxLayout(); @@ -72,13 +59,12 @@ namespace armarx::armem::gui auto queryTargetLayout = new QHBoxLayout(); _WMQueryTargetCheckBox = new QCheckBox("WM"); _LTMQueryTargetCheckBox = new QCheckBox("LTM"); - _LocalDiskMemoryQueryTargetCheckBox = new QCheckBox("Local Disk"); - connect(_LocalDiskMemoryQueryTargetCheckBox, &QCheckBox::stateChanged, this, &This::localDiskStateChanged); + connect(_WMQueryTargetCheckBox, &QCheckBox::stateChanged, this, &This::queryTargetStateChanged); + connect(_LTMQueryTargetCheckBox, &QCheckBox::stateChanged, this, &This::queryTargetStateChanged); queryTargetLayout->addWidget(_WMQueryTargetCheckBox); queryTargetLayout->addWidget(_LTMQueryTargetCheckBox); - queryTargetLayout->addWidget(_LocalDiskMemoryQueryTargetCheckBox); _WMQueryTargetCheckBox->setChecked(true); diff --git a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h index a0b69eb1b6f362e64880ab400c0bea29bcdc73fc..e7ec233c896133beb1b675baf37e2d0e2f53489c 100644 --- a/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h +++ b/source/RobotAPI/libraries/armem_gui/query_widgets/SnapshotSelectorWidget.h @@ -37,7 +37,6 @@ namespace armarx::armem::gui armem::DataMode dataMode() const; client::query::SnapshotSelector selector(); query::data::QueryTargets queryTargets() const; - bool queryTargetContainsLocalDisk() const; public slots: @@ -51,7 +50,7 @@ namespace armarx::armem::gui void hideAllForms(); void showSelectedFormForQuery(QString selected); - void localDiskStateChanged(); + void queryTargetStateChanged(); signals: void queryOutdated(); @@ -67,7 +66,6 @@ namespace armarx::armem::gui QComboBox* _queryComboBox; QCheckBox* _WMQueryTargetCheckBox; QCheckBox* _LTMQueryTargetCheckBox; - QCheckBox* _LocalDiskMemoryQueryTargetCheckBox; /// The forms for the different query types. Hidden when not selected. std::map<QString, SnapshotForm*> _queryForms; diff --git a/source/RobotAPI/libraries/armem_gui/test/ArMemGuiTest.cpp b/source/RobotAPI/libraries/armem_gui/test/ArMemGuiTest.cpp index 4f259c7af5a50ff198b139084eb604bfd06fead5..f7bb3e3e9a4cdc560838c0a3d25c0d7074a96503 100644 --- a/source/RobotAPI/libraries/armem_gui/test/ArMemGuiTest.cpp +++ b/source/RobotAPI/libraries/armem_gui/test/ArMemGuiTest.cpp @@ -30,27 +30,101 @@ #include <iostream> #include <RobotAPI/libraries/armem_gui/instance/sanitize_typename.h> +#include <RobotAPI/libraries/aron/core/navigator/data/AllNavigators.h> #include <RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h> -using namespace armarx::armem::gui::instance; +namespace dn = armarx::aron::datanavigator; +namespace tn = armarx::aron::typenavigator; -BOOST_AUTO_TEST_CASE(test_sanitizeTypeName) +namespace ArMemGuiTest { - using namespace armarx::aron::typenavigator; + struct Fixture { - DictNavigator dict; - dict.setAcceptedType(std::make_shared<FloatNavigator>()); - BOOST_CHECK_EQUAL(sanitizeTypeName(dict.getName()), "Dict<Float>"); + }; + + void test_sanitize(const std::string& in, const std::string& expected) + { + const std::string out = armarx::armem::gui::instance::sanitizeTypeName(in); + BOOST_TEST_CONTEXT("in = '" << in << "'") + { + BOOST_CHECK_EQUAL(out, expected); + } } +} + + + +BOOST_FIXTURE_TEST_SUITE(ArMemGuiTest, ArMemGuiTest::Fixture) + + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_int) +{ + test_sanitize(tn::IntNavigator().getName(), "Int"); + test_sanitize(dn::IntNavigator().getName(), "Int"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_float) +{ + test_sanitize(tn::FloatNavigator().getName(), "Float"); + test_sanitize(dn::FloatNavigator().getName(), "Float"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_dict) +{ + tn::DictNavigator dict; + dict.setAcceptedType(std::make_shared<tn::FloatNavigator>()); + test_sanitize(dict.getName(), "Dict<Float>"); + + test_sanitize(dn::DictNavigator().getName(), "Dict"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_list) +{ + tn::ListNavigator list; + list.setAcceptedType(std::make_shared<tn::LongNavigator>()); + test_sanitize(list.getName(), "List<Long>"); + + test_sanitize(dn::ListNavigator().getName(), "List"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_object) +{ + tn::ObjectNavigator obj; + obj.setObjectName("namespace::MyObjectName"); + test_sanitize(obj.getName(), "MyObjectName (namespace)"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_tuple) +{ + tn::TupleNavigator type; + type.addAcceptedType(std::make_shared<tn::IntNavigator>()); + type.addAcceptedType(std::make_shared<tn::FloatNavigator>()); + test_sanitize(type.getName(), "Tuple<Int, Float>"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_pair) +{ + tn::PairNavigator type; + type.setFirstAcceptedType(std::make_shared<tn::IntNavigator>()); + type.setSecondAcceptedType(std::make_shared<tn::FloatNavigator>()); + test_sanitize(type.getName(), "Pair<Int, Float>"); +} + +BOOST_AUTO_TEST_CASE(test_sanitizeTypeName_ndarry) +{ { - ListNavigator dict; - dict.setAcceptedType(std::make_shared<LongNavigator>()); - BOOST_CHECK_EQUAL(sanitizeTypeName(dict.getName()), "Dict<Long>"); + tn::NDArrayNavigator type; + type.setDimensions({ 3, 2, 1}); + type.setTypename("float"); + test_sanitize(type.getName(), "NDArray"); } { - ObjectNavigator dict; - dict.setObjectName("namespace::MyObjectName"); - BOOST_CHECK_EQUAL(sanitizeTypeName(dict.getName()), "namespace::MyObjectName"); + dn::NDArrayNavigator data; + data.setDimensions({ 3, 2, 1}); + test_sanitize(data.getName(), "NDArray<3, 2, 1, >"); } } + + +BOOST_AUTO_TEST_SUITE_END() diff --git a/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.cpp b/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.cpp index 8fab7c2c6e113e5ba5eb9b6ba8d250d8fb6fec4f..5860b2f48f9659c7ac631e1270425f112a0efcab 100644 --- a/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.cpp +++ b/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.cpp @@ -1,14 +1,21 @@ -// STD / STL -#include <iostream> -#include <fstream> -#include <sstream> - // BaseClass #include "Segment.h" // ArmarX #include "MotionConverter.h" + #include <RobotAPI/libraries/PriorKnowledge/motions/MotionFinder.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> + +#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> +#include <ArmarXCore/core/application/properties/ProxyPropertyDefinition.h> + +// STD / STL +#include <iostream> +#include <fstream> +#include <sstream> + namespace armarx::armem::server::motions::mdb { @@ -72,7 +79,7 @@ namespace armarx::armem::server::motions::mdb instance.metadata().timeSent = IceUtil::Time::now(); instance.metadata().timeArrived = IceUtil::Time::now(); instance.metadata().confidence = 1.0; - instance.setData(op->toAron()); + instance.data() = op->toAron(); } else { diff --git a/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.h b/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.h index 66e55f4107153532dd2df75a37fb369ba89817b1..f0dc4d726f83895876e23ab3bc52af36895df5b3 100644 --- a/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.h +++ b/source/RobotAPI/libraries/armem_motions/server/MotionDatabase/MDBMotions/Segment.h @@ -22,7 +22,7 @@ namespace armarx::armem::server::motions::mdb virtual void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix = "") override; virtual void onInit() override; - virtual void onConnect() override; + virtual void onConnect(); private: int loadByMotionFinder(const std::string&); diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp index c973cf24801967244589677f9b044be099f5c306..976d5bf9abb8a7d7586a0501568e6c8240c1e45c 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp @@ -16,7 +16,7 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/core/Time.h> #include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> #include <RobotAPI/libraries/armem_robot/aron_conversions.h> #include <RobotAPI/libraries/armem_robot/robot_conversions.h> diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp index f86f5f90d7787078572c9efa4b20cd5142100273..8caad31be27b227a8df1eb482f568ccf023b0f33 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp @@ -3,11 +3,13 @@ #include <Eigen/Core> #include <Eigen/Geometry> -#include "ArmarXCore/core/exceptions/local/ExpressionException.h" +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> #include <VirtualRobot/Robot.h> + namespace armarx::armem::articulated_object { armem::articulated_object::ArticulatedObject convert(const VirtualRobot::Robot& obj, @@ -46,4 +48,4 @@ namespace armarx::armem::articulated_object return store(armemArticulatedObject); } -} // namespace armarx::armem::articulated_object \ No newline at end of file +} // namespace armarx::armem::articulated_object diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/Reader.cpp b/source/RobotAPI/libraries/armem_objects/client/articulated_object/Reader.cpp index d0bfb35708104712d2bebc33b4d7676b999ddac5..27d1cd851dd4500a2d3c6373a2b7c1cd2c5eb3e5 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/Reader.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/Reader.cpp @@ -13,7 +13,7 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/core/Time.h> #include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> #include <RobotAPI/libraries/armem_robot/aron_conversions.h> #include <RobotAPI/libraries/armem_robot/robot_conversions.h> @@ -242,32 +242,19 @@ namespace armarx::armem::articulated_object .getCoreSegment(properties.coreInstanceSegmentName); // clang-format on - for (const auto& [_, providerSegment] : coreSegment.providerSegments()) + std::optional<wm::EntityInstance> instance; + coreSegment.forEachInstance([&instance](const wm::EntityInstance & i) { - - const auto entities = simox::alg::get_values(providerSegment.entities()); - - if (entities.empty()) - { - ARMARX_WARNING << "No entity found"; - continue; - } - - const auto entitySnapshots = simox::alg::get_values(entities.front().history()); - - if (entitySnapshots.empty()) - { - ARMARX_WARNING << "No entity snapshots found"; - continue; - } - - // TODO(fabian.reister): check if 0 available - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - - return robot::convertRobotState(instance); + instance = i; + }); + if (instance.has_value()) + { + return robot::convertRobotState(instance.value()); + } + else + { + return std::nullopt; } - - return std::nullopt; } @@ -275,67 +262,43 @@ namespace armarx::armem::articulated_object Reader::getRobotDescription(const armarx::armem::wm::Memory& memory) const { // clang-format off - const armem::wm::CoreSegment& coreSegment = memory - .getCoreSegment(properties.coreClassSegmentName); + const armem::wm::CoreSegment& coreSegment = memory.getCoreSegment(properties.coreClassSegmentName); // clang-format on - for (const auto& [_, providerSegment] : coreSegment.providerSegments()) + std::optional<wm::EntityInstance> instance; + coreSegment.forEachInstance([&instance](const wm::EntityInstance & i) { - const auto entities = simox::alg::get_values(providerSegment.entities()); - - if (entities.empty()) - { - ARMARX_WARNING << "No entity found"; - continue; - } - - const auto entitySnapshots = simox::alg::get_values(entities.front().history()); - - if (entitySnapshots.empty()) - { - ARMARX_WARNING << "No entity snapshots found"; - continue; - } - - if (entitySnapshots.front().hasInstance(0)) - { - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - return convertRobotDescription(instance); - } + instance = i; + }); + if (instance.has_value()) + { + return convertRobotDescription(instance.value()); + } + else + { + return std::nullopt; } - - return std::nullopt; } + std::vector<robot::RobotDescription> Reader::getRobotDescriptions(const armarx::armem::wm::Memory& memory) const { - std::vector<robot::RobotDescription> descriptions; - const armem::wm::CoreSegment& coreSegment = memory.getCoreSegment(properties.coreClassSegmentName); - for (const auto& [providerName, providerSegment] : coreSegment.providerSegments()) + std::vector<robot::RobotDescription> descriptions; + coreSegment.forEachEntity([&descriptions](const wm::Entity & entity) { - for (const auto& [name, entity] : providerSegment.entities()) + if (not entity.empty()) { - if (entity.empty()) - { - ARMARX_WARNING << "No entity found"; - continue; - } - - const auto entitySnapshots = simox::alg::get_values(entity.history()); - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - - const auto robotDescription = convertRobotDescription(instance); - + const auto robotDescription = convertRobotDescription(entity.getFirstSnapshot().getInstance(0)); if (robotDescription) { descriptions.push_back(*robotDescription); } } - } + }); return descriptions; } diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.cpp b/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.cpp index 237bc31cc8827f0fb9f80284bf0c639c23d093ec..d1cb1e0082d6ef95491530518e7f52839601d771 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.cpp @@ -1,25 +1,20 @@ #include "Writer.h" -#include <mutex> -#include <optional> - -#include <IceUtil/Time.h> - #include <SimoxUtility/algorithm/get_map_keys_values.h> -#include "ArmarXCore/core/exceptions/LocalException.h" +#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> #include <ArmarXCore/core/logging/Logging.h> -#include "RobotAPI/libraries/ArmarXObjects/ObjectID.h" -#include "RobotAPI/libraries/ArmarXObjects/ObjectInfo.h" +#include <RobotAPI/libraries/ArmarXObjects/ObjectID.h> +#include <RobotAPI/libraries/ArmarXObjects/ObjectInfo.h> #include <RobotAPI/libraries/ArmarXObjects/aron/ObjectID.aron.generated.h> #include <RobotAPI/libraries/armem/client/query.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> #include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/operations.h> #include <RobotAPI/libraries/armem_objects/aron/ObjectClass.aron.generated.h> #include <RobotAPI/libraries/armem_objects/aron/ObjectInstance.aron.generated.h> -#include <RobotAPI/libraries/armem_objects/aron_conversions.h> #include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> #include <RobotAPI/libraries/armem_robot/aron/RobotDescription.aron.generated.h> #include <RobotAPI/libraries/armem_robot/aron_conversions.h> @@ -27,6 +22,7 @@ #include "utils.h" + namespace armarx::armem::articulated_object { Writer::Writer(armem::client::MemoryNameSystem& memoryNameSystem) : @@ -86,7 +82,6 @@ namespace armarx::armem::articulated_object void Writer::updateKnownObject(const armem::MemoryID& snapshotId) { - arondto::RobotDescription aronArticulatedObjectDescription; // aronArticulatedObjectDescription.fromAron(snapshotId.ent); @@ -269,59 +264,41 @@ namespace armarx::armem::articulated_object .getCoreSegment(properties.coreClassSegmentName) .getProviderSegment(properties.providerName); // TODO(fabian.reister): all // clang-format on - const auto entities = simox::alg::get_values(providerSegment.entities()); - if (entities.empty()) + if (const armem::wm::EntityInstance* instance = findFirstInstance(providerSegment)) { - ARMARX_WARNING << "No entity found"; - return std::nullopt; + return convertRobotDescription(*instance); } - - const auto entitySnapshots = simox::alg::get_values(entities.front().history()); - - if (entitySnapshots.empty()) + else { ARMARX_WARNING << "No entity snapshots found"; return std::nullopt; } - - // TODO(fabian.reister): check if 0 available - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - - return convertRobotDescription(instance); } std::unordered_map<std::string, armem::MemoryID> Writer::getRobotDescriptions(const armarx::armem::wm::Memory& memory) const { - std::unordered_map<std::string, armem::MemoryID> descriptions; - const armem::wm::CoreSegment& coreSegment = memory.getCoreSegment(properties.coreClassSegmentName); - for (const auto& [providerName, providerSegment] : coreSegment.providerSegments()) + std::unordered_map<std::string, armem::MemoryID> descriptions; + coreSegment.forEachEntity([&descriptions](const wm::Entity & entity) { - for (const auto& [name, entity] : providerSegment.entities()) + if (entity.empty()) { - if (entity.empty()) - { - ARMARX_WARNING << "No entity found"; - continue; - } - - const auto entitySnapshots = simox::alg::get_values(entity.history()); - const armem::wm::EntitySnapshot& sn = entitySnapshots.front(); - const armem::wm::EntityInstance& instance = sn.getInstance(0); - - const auto robotDescription = convertRobotDescription(instance); - - if (robotDescription) - { - const armem::MemoryID snapshotID(sn.id()); - descriptions.insert({robotDescription->name, snapshotID}); - } + ARMARX_WARNING << "No entity found"; + return true; } - } + + const armem::wm::EntitySnapshot& sn = entity.getFirstSnapshot(); + if (const auto robotDescription = convertRobotDescription(sn.getInstance(0))) + { + const armem::MemoryID snapshotID(sn.id()); + descriptions.insert({robotDescription->name, snapshotID}); + } + return true; + }); return descriptions; } diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.h b/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.h index 95495220780d95278685c8c349462708aa822a3e..3e827d66a496ddf8df4448788f39ca1c36722290 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.h +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/Writer.h @@ -22,8 +22,9 @@ #pragma once #include <mutex> +#include <optional> -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> +#include <ArmarXCore/core/application/properties/forward_declarations.h> #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h> #include <RobotAPI/libraries/armem/client/Reader.h> @@ -39,7 +40,7 @@ namespace armarx::armem::articulated_object virtual public WriterInterface { public: - Writer(armem::client::MemoryNameSystem& memoryNameSystem); + Writer(armem::client::MemoryNameSystem& memoryNameSystemopti); virtual ~Writer() = default; void registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def); diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/utils.h b/source/RobotAPI/libraries/armem_objects/client/articulated_object/utils.h index 2e51a9e4b336bd4550027227d30a0236efa0f217..9c5b48f39864b16637739b022c0d1908de69c9a5 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/utils.h +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/utils.h @@ -2,7 +2,7 @@ #include <optional> -#include "RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h" +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include "RobotAPI/libraries/armem_robot/types.h" #include <RobotAPI/libraries/armem_objects/aron/ObjectClass.aron.generated.h> diff --git a/source/RobotAPI/libraries/armem_objects/client/attachment/Reader.cpp b/source/RobotAPI/libraries/armem_objects/client/attachment/Reader.cpp index f723f30b2eb2d704dcfae73f47e9351cf659b7ad..938ba098681aca145efbeb131af9c7a6a1b6c054 100644 --- a/source/RobotAPI/libraries/armem_objects/client/attachment/Reader.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/attachment/Reader.cpp @@ -9,7 +9,7 @@ #include <RobotAPI/libraries/armem/core/Time.h> #include <RobotAPI/libraries/armem/core/error.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/util/util.h> #include <RobotAPI/libraries/armem_robot/robot_conversions.h> #include <RobotAPI/libraries/armem_robot/aron_conversions.h> @@ -25,50 +25,42 @@ namespace armarx::armem::attachment namespace { - template<typename AronClass, typename ArmemClass> + template <typename AronClass, typename ArmemClass> auto getAttachments(const armarx::armem::wm::Memory& memory) { // using ArmemClass = decltype(fromAron(AronClass())); using ArmemClassVector = std::vector<ArmemClass>; ArmemClassVector attachments; - - for (const auto&[_, coreSegment] : memory.coreSegments()) + memory.forEachEntity([&attachments](const wm::Entity & entity) { - for (const auto& [providerName, providerSegment] : coreSegment.providerSegments()) + if (entity.empty()) { - for (const auto& [name, entity] : providerSegment.entities()) + ARMARX_WARNING << "No entity snapshot found"; + return true; + } + + const armem::wm::EntityInstance& instance = entity.getFirstSnapshot().getInstance(0); + try + { + AronClass aronAttachment; + aronAttachment.fromAron(instance.data()); + + ArmemClass attachment; + fromAron(aronAttachment, attachment); + + if (attachment.active) { - if (entity.empty()) - { - ARMARX_WARNING << "No entity found"; - continue; - } - - const auto entitySnapshots = simox::alg::get_values(entity.history()); - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - - try - { - AronClass aronAttachment; - aronAttachment.fromAron(instance.data()); - - ArmemClass attachment; - fromAron(aronAttachment, attachment); - - if (attachment.active) - { - attachments.push_back(attachment); - } - - } - catch (const armarx::aron::error::AronException&) - { - continue; - } + attachments.push_back(attachment); } + } - } + catch (const armarx::aron::error::AronException&) + { + return true; + } + return true; + }); return attachments; } @@ -131,7 +123,9 @@ namespace armarx::armem::attachment return {}; } - return getAttachments<::armarx::armem::arondto::attachment::ObjectAttachment, ::armarx::armem::attachment::ObjectAttachment>(qResult.memory); + return getAttachments < + ::armarx::armem::arondto::attachment::ObjectAttachment, + ::armarx::armem::attachment::ObjectAttachment > (qResult.memory); } std::vector<ObjectAttachment> Reader::queryObjectAttachments(const armem::Time& timestamp, const std::string& providerName) const diff --git a/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.cpp index bb5d930da65b6ddb0e7966aa53e5e8fa766d383d..2c60cb9bd35ab7b3010e5b5f5fb0e719d0817f98 100644 --- a/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.cpp @@ -3,15 +3,15 @@ #include <sstream> #include <ArmarXCore/core/time/TimeUtil.h> -#include "ArmarXCore/core/logging/Logging.h" -#include <ArmarXCore/core/application/properties/forward_declarations.h> +#include <ArmarXCore/core/logging/Logging.h> +#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> +#include <ArmarXCore/core/application/properties/PluginAll.h> -#include "RobotAPI/libraries/armem/util/util.h" -#include "RobotAPI/libraries/aron/common/aron_conversions.h" +#include <RobotAPI/libraries/armem/util/util.h> +#include <RobotAPI/libraries/aron/common/aron_conversions.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/workingmemory/visitor.h> -#include "RobotAPI/libraries/armem/core/MemoryID.h" +#include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/client/Writer.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> @@ -50,24 +50,22 @@ namespace armarx::armem::server::obj::attachments { } - std::vector<armarx::armem::attachment::ObjectAttachment> Segment::getAttachments(const armem::Time& timestamp) const + std::vector<armarx::armem::attachment::ObjectAttachment> + Segment::getAttachments(const armem::Time& timestamp) const { ARMARX_CHECK_NOT_NULL(coreSegment); - std::scoped_lock(coreSegment->mutex()); - - std::vector<armarx::armem::attachment::ObjectAttachment> attachments; - - for (const auto& [_, provSeg] : *coreSegment) + return coreSegment->doLocked([this]() { - for (const auto& [name, entity] : provSeg.entities()) + std::vector<armarx::armem::attachment::ObjectAttachment> attachments; + coreSegment->forEachEntity([this, &attachments](const wm::Entity & entity) { - const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); - const auto aronAttachment = tryCast<armarx::armem::arondto::attachment::ObjectAttachment>(entityInstance); + const wm::EntityInstance& entityInstance = entity.getLatestSnapshot().getInstance(0); + const auto aronAttachment = tryCast<armarx::armem::arondto::attachment::ObjectAttachment>(entityInstance); if (not aronAttachment) { ARMARX_WARNING << "Could not convert entity instance to 'ObjectAttachment'"; - continue; + return true; } ARMARX_DEBUG << "Key is " << armem::MemoryID(entity.id()); @@ -76,10 +74,11 @@ namespace armarx::armem::server::obj::attachments fromAron(*aronAttachment, attachment); attachments.push_back(attachment); - } - } + return true; + }); - return attachments; + return attachments; + }); } } // namespace armarx::armem::server::obj::attachments diff --git a/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.h b/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.h index 0b9302d93390c473b87c67066bfe173237f3774b..4f8cd20ccd9a0ce456bb664f5f5eb8a25f1981e0 100644 --- a/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.h +++ b/source/RobotAPI/libraries/armem_objects/server/attachments/Segment.h @@ -21,41 +21,19 @@ #pragma once -#include <string> -#include <optional> -#include <mutex> -#include <unordered_map> +#include <RobotAPI/libraries/armem/core/Time.h> +#include <RobotAPI/libraries/armem/core/forward_declarations.h> +#include <RobotAPI/libraries/armem/server/forward_declarations.h> +#include <RobotAPI/libraries/armem_objects/types.h> -#include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/application/properties/forward_declarations.h> +#include <ArmarXCore/core/logging/Logging.h> -// #include "ArmarXGui/libraries/RemoteGui/Client/Widgets.h" - -#include "RobotAPI/components/ArViz/Client/Client.h" - -#include "RobotAPI/libraries/armem/core/MemoryID.h" -#include "RobotAPI/libraries/armem/core/Time.h" -#include "RobotAPI/libraries/armem_objects/types.h" - - -namespace armarx::armem -{ - namespace server - { - class MemoryToIceAdapter; - } - - namespace wm - { - class CoreSegment; - } -} // namespace armarx::armem +#include <string> namespace armarx::armem::server::obj::attachments { - class Visu; - class Segment : public armarx::Logging { diff --git a/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp b/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp index 08927d1e77d1963234f42ea9ab861ec9a33f86c4..cb0088285ca161ddc9283f3a024b65a86809fb1b 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.cpp @@ -1,6 +1,7 @@ #include "FloorVis.h" -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_objects/aron/ObjectClass.aron.generated.h> #include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> @@ -13,22 +14,25 @@ namespace armarx::armem::server::obj::clazz { } + void FloorVis::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) { properties.define(defs, prefix); } + void FloorVis::setArViz(armarx::viz::Client arviz) { this->arviz = arviz; } + void FloorVis::updateFloorObject(const wm::CoreSegment& classCoreSegment) { viz::Layer layer = arviz.layer(properties.layerName); if (properties.show) { - const wm::Entity* entity = classCoreSegment.findEntity(MemoryID().withEntityName(properties.entityName)); + const wm::Entity* entity = classCoreSegment.findEntity(properties.entityName); if (entity) { ARMARX_INFO << "Drawing floor class '" << properties.entityName << "'."; @@ -42,6 +46,7 @@ namespace armarx::armem::server::obj::clazz arviz.commit(layer); } + armarx::viz::Object FloorVis::makeFloorObject(const wm::Entity& classEntity) { const wm::EntityInstance& instance = classEntity.getLatestSnapshot().getInstance(0); @@ -50,6 +55,7 @@ namespace armarx::armem::server::obj::clazz return makeFloorObject(classEntity.name(), data); } + armarx::viz::Object FloorVis::makeFloorObject( const std::string& name, const arondto::ObjectClass& objectClass) @@ -61,7 +67,6 @@ namespace armarx::armem::server::obj::clazz } - void FloorVis::Properties::define(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) { defs->optional(show, prefix + "Show", "Whether to show the floor."); diff --git a/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.h b/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.h index dcce63977438bdd0d646b50aa8e5dd9679354df8..49f8a6ddd71f5c24172ddfbbfd8e312dcea2fd3f 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.h +++ b/source/RobotAPI/libraries/armem_objects/server/class/FloorVis.h @@ -10,7 +10,7 @@ namespace armarx { using PropertyDefinitionsPtr = IceUtil::Handle<class PropertyDefinitionContainer>; } -namespace armarx::armem::wm +namespace armarx::armem::server::wm { class CoreSegment; class Entity; diff --git a/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp index 22608257c1abcf39340643870b704c2dadfcd54d..8b1ae942a95b73119eea072b7f33a539a05fbbdf 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp @@ -124,8 +124,10 @@ namespace armarx::armem::server::obj::clazz { try { - std::optional<arondto::ObjectClass> aron = - coreSegment->getLatestEntityInstanceDataLockingAs<arondto::ObjectClass>(entityID, 0); + std::optional<arondto::ObjectClass> aron = coreSegment->doLocked([this, &entityID]() + { + return coreSegment->findLatestInstanceDataAs<arondto::ObjectClass>(entityID, 0); + }); if (not aron.has_value()) { return; @@ -269,12 +271,14 @@ namespace armarx::armem::server::obj::clazz } if (infiniteHistory.hasValueChanged() || maxHistorySize.hasValueChanged()) { - std::scoped_lock lock(segment.mutex()); - segment.properties.maxHistorySize = infiniteHistory.getValue() ? -1 : maxHistorySize.getValue(); - if (segment.coreSegment) + segment.doLocked([this, &segment]() { - segment.coreSegment->setMaxHistorySize(long(segment.properties.maxHistorySize)); - } + segment.properties.maxHistorySize = infiniteHistory.getValue() ? -1 : maxHistorySize.getValue(); + if (segment.coreSegment) + { + segment.coreSegment->setMaxHistorySize(long(segment.properties.maxHistorySize)); + } + }); } } @@ -285,16 +289,13 @@ namespace armarx::armem::server::obj::clazz showComboBox = {}; showOptionsIndex.clear(); - for (const auto& [_, prov] : *segment.coreSegment) + segment.coreSegment->forEachEntity([this](const wm::Entity & entity) { - for (const auto& [_, entity] : prov) - { - std::stringstream option; - option << entity.id().entityName << " (" << entity.id().providerSegmentName << ")"; - showComboBox.addOption(option.str()); - showOptionsIndex.push_back(entity.id()); - } - } + std::stringstream option; + option << entity.id().entityName << " (" << entity.id().providerSegmentName << ")"; + showComboBox.addOption(option.str()); + showOptionsIndex.push_back(entity.id()); + }); if (showOptionsIndex.empty()) { showComboBox.addOption("<none>"); diff --git a/source/RobotAPI/libraries/armem_objects/server/class/Segment.h b/source/RobotAPI/libraries/armem_objects/server/class/Segment.h index 15c7c4f373f20c43c0febfb142675c5058c45909..3e9717b3e51dc07491e55761664e5cc76add574b 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/Segment.h +++ b/source/RobotAPI/libraries/armem_objects/server/class/Segment.h @@ -9,7 +9,7 @@ #include <RobotAPI/libraries/ArmarXObjects/ObjectID.h> #include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/server/segment/SpecializedSegment.h> #include <RobotAPI/libraries/armem_objects/server/class/FloorVis.h> diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp index 09a412f4bfa40a1a6288e86dfe65fa8b50953061..9668e307ba7c7ff3ed1143087bf4aee1adf2608c 100644 --- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp @@ -7,7 +7,6 @@ #include <RobotAPI/libraries/armem/core/aron_conversions.h> #include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/workingmemory/visitor.h> #include <RobotAPI/libraries/armem/client/Writer.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> @@ -26,8 +25,9 @@ #include <RobotAPI/libraries/core/FramedPose.h> #include <RobotAPI/libraries/core/remoterobot/RemoteRobot.h> -#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> +#include <ArmarXCore/core/time/TimeUtil.h> #include <SimoxUtility/algorithm/get_map_keys_values.h> #include <SimoxUtility/json.h> @@ -143,7 +143,7 @@ namespace armarx::armem::server::obj::instance // Check whether we have an old snapshot for this object. std::optional<objpose::ObjectPose> previousPose; - const armem::wm::Entity* entity = findObjectEntity(armarx::fromIce(provided.objectID), providerName); + const wm::Entity* entity = findObjectEntity(armarx::fromIce(provided.objectID), providerName); if (entity) { const arondto::ObjectInstance data = getLatestInstanceData(*entity); @@ -250,21 +250,24 @@ namespace armarx::armem::server::obj::instance } - wm::CoreSegment& Segment::getCoreSegment() + wm::CoreSegment& + Segment::getCoreSegment() { ARMARX_CHECK_NOT_NULL(coreSegment); return *coreSegment; } - const wm::CoreSegment& Segment::getCoreSegment() const + const wm::CoreSegment& + Segment::getCoreSegment() const { ARMARX_CHECK_NOT_NULL(coreSegment); return *coreSegment; } - objpose::ObjectPoseMap Segment::getObjectPoses(IceUtil::Time now) + objpose::ObjectPoseMap + Segment::getObjectPoses(IceUtil::Time now) { ObjectPoseMap objectPoses = getLatestObjectPoses(); updateObjectPoses(objectPoses, now); @@ -273,7 +276,8 @@ namespace armarx::armem::server::obj::instance - objpose::ObjectPoseMap Segment::getObjectPosesByProvider( + objpose::ObjectPoseMap + Segment::getObjectPosesByProvider( const std::string& providerName, IceUtil::Time now) { @@ -284,27 +288,31 @@ namespace armarx::armem::server::obj::instance } - armem::wm::Entity* Segment::findObjectEntity(const ObjectID& objectID, const std::string& providerName) + wm::Entity* + Segment::findObjectEntity(const ObjectID& objectID, const std::string& providerName) { ARMARX_CHECK_NOT_NULL(coreSegment); armem::MemoryID entityID = armem::MemoryID().withEntityName(objectID.str()); if (providerName.empty()) { - for (auto& [_, prov] : *coreSegment) + wm::Entity* result = nullptr; + coreSegment->forEachProviderSegment([&result, &entityID](wm::ProviderSegment & prov) { if (prov.hasEntity(entityID.entityName)) { - return &prov.getEntity(entityID); + result = &prov.getEntity(entityID); + return false; } - } - return nullptr; + return true; + }); + return result; } else { entityID.providerSegmentName = providerName; if (coreSegment->hasProviderSegment(providerName)) { - armem::wm::ProviderSegment& prov = coreSegment->getProviderSegment(providerName); + wm::ProviderSegment& prov = coreSegment->getProviderSegment(providerName); return prov.hasEntity(entityID.entityName) ? &prov.getEntity(entityID) : nullptr; } else @@ -387,14 +395,16 @@ namespace armarx::armem::server::obj::instance } - objpose::ObjectPoseMap Segment::getLatestObjectPoses() const + objpose::ObjectPoseMap + Segment::getLatestObjectPoses() const { ARMARX_CHECK_NOT_NULL(coreSegment); return getLatestObjectPoses(*coreSegment); } - objpose::ObjectPoseMap Segment::getLatestObjectPoses(const armem::wm::CoreSegment& coreSeg) + objpose::ObjectPoseMap + Segment::getLatestObjectPoses(const wm::CoreSegment& coreSeg) { ObjectPoseMap result; getLatestObjectPoses(coreSeg, result); @@ -402,7 +412,8 @@ namespace armarx::armem::server::obj::instance } - objpose::ObjectPoseMap Segment::getLatestObjectPoses(const armem::wm::ProviderSegment& provSeg) + objpose::ObjectPoseMap + Segment::getLatestObjectPoses(const wm::ProviderSegment& provSeg) { ObjectPoseMap result; getLatestObjectPoses(provSeg, result); @@ -410,7 +421,8 @@ namespace armarx::armem::server::obj::instance } - objpose::ObjectPose Segment::getLatestObjectPose(const armem::wm::Entity& entity) + objpose::ObjectPose + Segment::getLatestObjectPose(const wm::Entity& entity) { ObjectPose result; getLatestObjectPose(entity, result); @@ -418,18 +430,18 @@ namespace armarx::armem::server::obj::instance } - void Segment::getLatestObjectPoses(const armem::wm::CoreSegment& coreSeg, ObjectPoseMap& out) + void Segment::getLatestObjectPoses(const wm::CoreSegment& coreSeg, ObjectPoseMap& out) { - for (const auto& [_, provSegment] : coreSeg) + coreSeg.forEachProviderSegment([&out](const wm::ProviderSegment & provSegment) { getLatestObjectPoses(provSegment, out); - } + }); } - void Segment::getLatestObjectPoses(const armem::wm::ProviderSegment& provSegment, ObjectPoseMap& out) + void Segment::getLatestObjectPoses(const wm::ProviderSegment& provSegment, ObjectPoseMap& out) { - for (const auto& [_, entity] : provSegment) + provSegment.forEachEntity([&out](const wm::Entity & entity) { if (!entity.empty()) { @@ -445,29 +457,29 @@ namespace armarx::armem::server::obj::instance } } } - } + }); } - void Segment::getLatestObjectPose(const armem::wm::Entity& entity, ObjectPose& out) + void Segment::getLatestObjectPose(const wm::Entity& entity, ObjectPose& out) { - for (const armem::wm::EntityInstance& instance : entity.getLatestSnapshot()) + entity.getLatestSnapshot().forEachInstance([&out](const wm::EntityInstance & instance) { arondto::ObjectInstance dto; dto.fromAron(instance.data()); fromAron(dto, out); - } + }); } - arondto::ObjectInstance Segment::getLatestInstanceData(const armem::wm::Entity& entity) + arondto::ObjectInstance Segment::getLatestInstanceData(const wm::Entity& entity) { ARMARX_CHECK_GREATER_EQUAL(entity.size(), 1); - const armem::wm::EntitySnapshot& snapshot = entity.getLatestSnapshot(); + const wm::EntitySnapshot& snapshot = entity.getLatestSnapshot(); ARMARX_CHECK_EQUAL(snapshot.size(), 1); - const armem::wm::EntityInstance& instance = snapshot.getInstance(0); + const wm::EntityInstance& instance = snapshot.getInstance(0); arondto::ObjectInstance data; data.fromAron(instance.data()); @@ -476,7 +488,8 @@ namespace armarx::armem::server::obj::instance } - ::armarx::armem::articulated_object::ArticulatedObjects Segment::getArticulatedObjects() + ::armarx::armem::articulated_object::ArticulatedObjects + Segment::getArticulatedObjects() { objpose::ObjectPoseMap objectPoses = getObjectPoses(IceUtil::Time::now()); @@ -589,7 +602,7 @@ namespace armarx::armem::server::obj::instance // Find object pose (provider name can be empty). - armem::wm::Entity* objectEntity = this->findObjectEntity(objectID, input.providerName); + wm::Entity* objectEntity = this->findObjectEntity(objectID, input.providerName); if (!objectEntity || objectEntity->empty()) { ARMARX_WARNING << "Tried to attach object " << objectID << " to node '" << frameName @@ -650,7 +663,8 @@ namespace armarx::armem::server::obj::instance } - objpose::DetachObjectFromRobotNodeOutput Segment::detachObjectFromRobotNode( + objpose::DetachObjectFromRobotNodeOutput + Segment::detachObjectFromRobotNode( const objpose::DetachObjectFromRobotNodeInput& input) { const armem::Time now = armem::Time::now(); @@ -662,7 +676,7 @@ namespace armarx::armem::server::obj::instance { // Remove from latest pose (if it was cached). // Find object pose (provider name can be empty). - armem::wm::Entity* entity = this->findObjectEntity(objectID, input.providerName); + wm::Entity* entity = this->findObjectEntity(objectID, input.providerName); if (entity) { const arondto::ObjectInstance data = getLatestInstanceData(*entity); @@ -697,48 +711,26 @@ namespace armarx::armem::server::obj::instance } - struct DetachVisitor : public armem::wm::Visitor - { - Segment& owner; - armem::Time now; - bool commitAttachedPose; - - int numDetached = 0; - - DetachVisitor(Segment& owner, armem::Time now, bool commitAttachedPose) : - owner(owner), now(now), commitAttachedPose(commitAttachedPose) - { - } - - virtual bool visitEnter(armem::wm::Entity& entity) override; - }; - - - bool DetachVisitor::visitEnter(armem::wm::Entity& entity) - { - const arondto::ObjectInstance data = owner.getLatestInstanceData(entity); - if (data.pose.attachmentValid) - { - numDetached++; - // Store non-attached pose in new snapshot. - owner.storeDetachedSnapshot(entity, data, now, commitAttachedPose); - } - return false; // Stop descending. - } - - - objpose::DetachAllObjectsFromRobotNodesOutput Segment::detachAllObjectsFromRobotNodes( + objpose::DetachAllObjectsFromRobotNodesOutput + Segment::detachAllObjectsFromRobotNodes( const objpose::DetachAllObjectsFromRobotNodesInput& input) { ARMARX_CHECK_NOT_NULL(coreSegment); const armem::Time now = armem::Time::now(); - DetachVisitor visitor(*this, now, input.commitAttachedPose); - visitor.applyTo(*coreSegment); - objpose::DetachAllObjectsFromRobotNodesOutput output; - output.numDetached = visitor.numDetached; + output.numDetached = 0; + coreSegment->forEachEntity([this, now, &input, &output](wm::Entity & entity) + { + const arondto::ObjectInstance data = this->getLatestInstanceData(entity); + if (data.pose.attachmentValid) + { + ++output.numDetached; + // Store non-attached pose in new snapshot. + this->storeDetachedSnapshot(entity, data, now, input.commitAttachedPose); + } + }); ARMARX_INFO << "Detached all objects (" << output.numDetached << ") from robot nodes."; @@ -747,9 +739,9 @@ namespace armarx::armem::server::obj::instance void Segment::storeDetachedSnapshot( - armem::wm::Entity& entity, + wm::Entity& entity, const arondto::ObjectInstance& data, - armem::Time now, + Time now, bool commitAttachedPose) { armem::EntityUpdate update; @@ -781,21 +773,29 @@ namespace armarx::armem::server::obj::instance } - std::optional<wm::EntityInstance> Segment::findClassInstance(const ObjectID& objectID) const + std::optional<wm::EntityInstance> + Segment::findClassInstance(const ObjectID& objectID) const { const ObjectID classID = { objectID.dataset(), objectID.className() }; try { - for (const auto& [_, provSeg] : iceMemory.workingMemory->getCoreSegment("Class")) + std::optional<wm::EntityInstance> result; + iceMemory.workingMemory->getCoreSegment("Class").forEachProviderSegment( + [&classID, &result](const wm::ProviderSegment & provSeg) { if (provSeg.hasEntity(classID.str())) { - return provSeg.getEntity(classID.str()).getLatestSnapshot().getInstance(0); + result = provSeg.getEntity(classID.str()).getLatestSnapshot().getInstance(0); + return false; } - } + return true; + }); - ARMARX_WARNING << "No provider segment for classID " << classID.str() << " found"; - return std::nullopt; + if (not result.has_value()) + { + ARMARX_WARNING << "No provider segment for classID " << classID.str() << " found"; + } + return result; } catch (const armem::error::ArMemError& e) { @@ -824,7 +824,8 @@ namespace armarx::armem::server::obj::instance } - std::optional<armem::obj::SceneSnapshot> Segment::loadScene(const std::string& filename) + std::optional<armem::obj::SceneSnapshot> + Segment::loadScene(const std::string& filename) { if (const std::optional<std::filesystem::path> path = resolveSceneFilename(filename)) { @@ -837,7 +838,8 @@ namespace armarx::armem::server::obj::instance } - std::optional<armem::obj::SceneSnapshot> Segment::loadScene(const std::filesystem::path& path) + std::optional<armem::obj::SceneSnapshot> + Segment::loadScene(const std::filesystem::path& path) { ARMARX_INFO << "Loading scene snapshot from: \n" << path; nlohmann::json j; @@ -859,7 +861,8 @@ namespace armarx::armem::server::obj::instance const std::string Segment::timestampPlaceholder = "%TIMESTAMP"; - std::optional<std::filesystem::path> Segment::resolveSceneFilename(const std::string& _filename) + std::optional<std::filesystem::path> + Segment::resolveSceneFilename(const std::string& _filename) { std::string filename = _filename; @@ -894,9 +897,7 @@ namespace armarx::armem::server::obj::instance armem::obj::SceneSnapshot Segment::getSceneSnapshot() const { armem::obj::SceneSnapshot scene; - - wm::FunctionalVisitor visitor; - visitor.entityFn = [&scene](wm::Entity & entity) + coreSegment->forEachEntity([&scene](wm::Entity & entity) { try { @@ -916,10 +917,8 @@ namespace armarx::armem::server::obj::instance { ARMARX_WARNING_S << e.what(); } - return false; - }; + }); - visitor.applyTo(*coreSegment); return scene; } @@ -1040,28 +1039,29 @@ namespace armarx::armem::server::obj::instance if (storeButton.wasClicked()) { armem::obj::SceneSnapshot scene; + segment.doLocked([&scene, &segment]() { - std::scoped_lock lock(segment.mutex()); scene = segment.getSceneSnapshot(); - } + }); segment.storeScene(storeLoadLine.getValue(), scene); } if (infiniteHistory.hasValueChanged() || maxHistorySize.hasValueChanged() || discardSnapshotsWhileAttached.hasValueChanged()) { - std::scoped_lock lock(segment.mutex()); - - if (infiniteHistory.hasValueChanged() || maxHistorySize.hasValueChanged()) + segment.doLocked([this, &segment]() { - segment.properties.maxHistorySize = infiniteHistory.getValue() ? -1 : maxHistorySize.getValue(); - if (segment.coreSegment) + if (infiniteHistory.hasValueChanged() || maxHistorySize.hasValueChanged()) { - segment.coreSegment->setMaxHistorySize(long(segment.properties.maxHistorySize)); + segment.properties.maxHistorySize = infiniteHistory.getValue() ? -1 : maxHistorySize.getValue(); + if (segment.coreSegment) + { + segment.coreSegment->setMaxHistorySize(long(segment.properties.maxHistorySize)); + } } - } - segment.p.discardSnapshotsWhileAttached = discardSnapshotsWhileAttached.getValue(); + segment.p.discardSnapshotsWhileAttached = discardSnapshotsWhileAttached.getValue(); + }); } } diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h index 918a4efade3c596763d90fec427b18fa78a774bf..27ffbc746af05451b5fdd54f15a3afc4c7822e08 100644 --- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h +++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.h @@ -8,8 +8,6 @@ #include <SimoxUtility/caching/CacheMap.h> #include <SimoxUtility/shapes/OrientedBox.h> -#include <ArmarXCore/core/logging/Logging.h> - #include <RobotAPI/interface/core/RobotState.h> #include <RobotAPI/interface/objectpose/ObjectPoseStorageInterface.h> @@ -19,7 +17,7 @@ #include <RobotAPI/libraries/ArmarXObjects/ObjectPose.h> #include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/server/segment/SpecializedSegment.h> #include "ArticulatedObjectVisu.h" diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp index 7cd8b1202ac34600f8bfc6ecc912037b49d8bee6..ef00638e9ed76d0bbe05de3ab6af98f3687fe9d7 100644 --- a/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/instance/SegmentAdapter.cpp @@ -123,8 +123,8 @@ namespace armarx::armem::server::obj::instance << "with invalid provider proxy.\nIgnoring provider '" << providerName << "'."; return; } + segment.doLocked([this, &providerName, &info]() { - std::scoped_lock lock(segment.mutex()); std::stringstream ss; for (const auto& id : info.supportedObjects) { @@ -133,7 +133,7 @@ namespace armarx::armem::server::obj::instance ARMARX_VERBOSE << "Provider '" << providerName << "' available.\n" << "Supported objects: \n" << ss.str(); segment.providers[providerName] = info; - } + }); } @@ -164,8 +164,8 @@ namespace armarx::armem::server::obj::instance return; } + segment.doLocked([&]() { - std::scoped_lock lock(segment.mutex()); RemoteRobot::synchronizeLocalClone(segment.robot, segment.robotStateComponent); if (segment.robot->hasRobotNode(calibration.robotNode)) @@ -200,7 +200,7 @@ namespace armarx::armem::server::obj::instance { "t MemorySetObjectPoses [ms]", new Variant(tCommitObjectPoses.toMilliSecondsDouble()) }, }); } - } + }); } @@ -218,12 +218,12 @@ namespace armarx::armem::server::obj::instance { TIMING_START(tGetObjectPoses); - TIMING_START(tGetObjectPosesLock); - std::scoped_lock lock(segment.mutex()); - TIMING_END_STREAM(tGetObjectPosesLock, ARMARX_VERBOSE); - const IceUtil::Time now = TimeUtil::GetTime(); - const objpose::data::ObjectPoseSeq result = objpose::toIce(simox::alg::get_values(segment.getObjectPoses(now))); + const objpose::ObjectPoseSeq objectPoses = segment.doLocked([this, &now]() + { + return simox::alg::get_values(segment.getObjectPoses(now)); + }); + const objpose::data::ObjectPoseSeq result = objpose::toIce(objectPoses); TIMING_END_STREAM(tGetObjectPoses, ARMARX_VERBOSE); @@ -232,7 +232,6 @@ namespace armarx::armem::server::obj::instance debugObserver->setDebugChannel(getName(), { { "t GetObjectPoses() [ms]", new Variant(tGetObjectPoses.toMilliSecondsDouble()) }, - { "t GetObjectPoses() lock [ms]", new Variant(tGetObjectPosesLock.toMilliSecondsDouble()) } }); } @@ -243,12 +242,12 @@ namespace armarx::armem::server::obj::instance { TIMING_START(GetObjectPoses); - TIMING_START(GetObjectPosesLock); - std::scoped_lock lock(segment.mutex()); - TIMING_END_STREAM(GetObjectPosesLock, ARMARX_VERBOSE); - const IceUtil::Time now = TimeUtil::GetTime(); - const objpose::data::ObjectPoseSeq result = objpose::toIce(simox::alg::get_values(segment.getObjectPosesByProvider(providerName, now))); + const objpose::ObjectPoseSeq objectPoses = segment.doLocked([this, &now, &providerName]() + { + return simox::alg::get_values(segment.getObjectPosesByProvider(providerName, now)); + }); + const objpose::data::ObjectPoseSeq result = objpose::toIce(objectPoses); TIMING_END_STREAM(GetObjectPoses, ARMARX_VERBOSE); @@ -257,7 +256,6 @@ namespace armarx::armem::server::obj::instance debugObserver->setDebugChannel(getName(), { { "t GetObjectPosesByProvider() [ms]", new Variant(GetObjectPoses.toMilliSecondsDouble()) }, - { "t GetObjectPosesByProvider() lock [ms]", new Variant(GetObjectPosesLock.toMilliSecondsDouble()) } }); } @@ -296,26 +294,28 @@ namespace armarx::armem::server::obj::instance } else { - std::scoped_lock lock(segment.mutex()); - for (const auto& objectID : input.request.objectIDs) + segment.doLocked([&]() { - bool found = true; - for (const auto& [providerName, info] : segment.providers) + for (const auto& objectID : input.request.objectIDs) { - // ToDo: optimize look up. - if (std::find(info.supportedObjects.begin(), info.supportedObjects.end(), objectID) != info.supportedObjects.end()) + bool found = true; + for (const auto& [providerName, info] : segment.providers) + { + // ToDo: optimize look up. + if (std::find(info.supportedObjects.begin(), info.supportedObjects.end(), objectID) != info.supportedObjects.end()) + { + providerRequests[providerName].objectIDs.push_back(objectID); + updateProxy(providerName); + break; + } + } + if (!found) { - providerRequests[providerName].objectIDs.push_back(objectID); - updateProxy(providerName); - break; + ARMARX_ERROR << "Did not find a provider for " << objectID << "."; + output.results[objectID].providerName = ""; } } - if (!found) - { - ARMARX_ERROR << "Did not find a provider for " << objectID << "."; - output.results[objectID].providerName = ""; - } - } + }); } for (const auto& [providerName, request] : providerRequests) @@ -343,69 +343,84 @@ namespace armarx::armem::server::obj::instance objpose::ProviderInfoMap SegmentAdapter::getAvailableProvidersInfo(const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return segment.providers; + return segment.doLocked([this]() + { + return segment.providers; + }); } Ice::StringSeq SegmentAdapter::getAvailableProviderNames(const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return simox::alg::get_keys(segment.providers); + return segment.doLocked([this]() + { + return simox::alg::get_keys(segment.providers); + }); } objpose::ProviderInfo SegmentAdapter::getProviderInfo(const std::string& providerName, const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return segment.getProviderInfo(providerName); + return segment.doLocked([this, &providerName]() + { + return segment.getProviderInfo(providerName); + }); } bool SegmentAdapter::hasProvider(const std::string& providerName, const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return segment.providers.count(providerName) > 0; + return segment.doLocked([this, &providerName]() + { + return segment.providers.count(providerName) > 0; + }); } objpose::AttachObjectToRobotNodeOutput SegmentAdapter::attachObjectToRobotNode( const objpose::AttachObjectToRobotNodeInput& input, const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return segment.attachObjectToRobotNode(input); + return segment.doLocked([this, &input]() + { + return segment.attachObjectToRobotNode(input); + }); } objpose::DetachObjectFromRobotNodeOutput SegmentAdapter::detachObjectFromRobotNode( const objpose::DetachObjectFromRobotNodeInput& input, const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return segment.detachObjectFromRobotNode(input); + return segment.doLocked([this, &input]() + { + return segment.detachObjectFromRobotNode(input); + }); } objpose::DetachAllObjectsFromRobotNodesOutput SegmentAdapter::detachAllObjectsFromRobotNodes( const objpose::DetachAllObjectsFromRobotNodesInput& input, const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - return segment.detachAllObjectsFromRobotNodes(input); + return segment.doLocked([this, &input]() + { + return segment.detachAllObjectsFromRobotNodes(input); + }); } objpose::AgentFramesSeq SegmentAdapter::getAttachableFrames(const Ice::Current&) { - std::scoped_lock lock(segment.mutex()); - - objpose::AgentFramesSeq output; - std::vector<VirtualRobot::RobotPtr> agents = { segment.robot }; - for (VirtualRobot::RobotPtr agent : agents) + return segment.doLocked([this]() { - objpose::AgentFrames& frames = output.emplace_back(); - frames.agent = agent->getName(); - frames.frames = agent->getRobotNodeNames(); - } - return output; + objpose::AgentFramesSeq output; + std::vector<VirtualRobot::RobotPtr> agents = { segment.robot }; + for (VirtualRobot::RobotPtr agent : agents) + { + objpose::AgentFrames& frames = output.emplace_back(); + frames.agent = agent->getName(); + frames.frames = agent->getRobotNodeNames(); + } + return output; + }); } @@ -433,9 +448,9 @@ namespace armarx::armem::server::obj::instance objpose::ObjectPoseMap objectPoses; visu.minConfidence = -1; - { - std::scoped_lock lock(segment.mutex()); + segment.doLocked([this, &objectPoses, &objectFinder]() + { const IceUtil::Time now = TimeUtil::GetTime(); // Also include decayed objects in result @@ -452,7 +467,7 @@ namespace armarx::armem::server::obj::instance { visu.minConfidence = segment.decay.removeObjectsBelowConfidence; } - } + }); const std::vector<viz::Layer> layers = visu.visualizeCommit(objectPoses, objectFinder); arviz.commit(layers); @@ -514,8 +529,10 @@ namespace armarx::armem::server::obj::instance } this->segment.update(adapter.segment); { - std::scoped_lock lock(adapter.segment.mutex()); - this->decay.update(adapter.segment.decay); + adapter.segment.doLocked([this, &adapter]() + { + this->decay.update(adapter.segment.decay); + }); } { std::scoped_lock lock(adapter.robotHeadMutex); diff --git a/source/RobotAPI/libraries/armem_objects/types.h b/source/RobotAPI/libraries/armem_objects/types.h index 976f438e7c39009fd5d37fb6afdf19378da488c4..5ca91ff285e5fc92d1d3523a504379d582dda8b5 100644 --- a/source/RobotAPI/libraries/armem_objects/types.h +++ b/source/RobotAPI/libraries/armem_objects/types.h @@ -21,13 +21,11 @@ #pragma once -#include "RobotAPI/libraries/armem/core/Time.h" -#include <vector> - #include <Eigen/Geometry> -#include <RobotAPI/libraries/armem_robot/types.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/Time.h> +#include <RobotAPI/libraries/armem_robot/types.h> namespace armarx::armem::attachment @@ -97,4 +95,4 @@ namespace armarx::armem::articulated_object using ArticulatedObject = armarx::armem::robot::Robot; using ArticulatedObjects = armarx::armem::robot::Robots; -} // namespace armarx::armem::articulated_object \ No newline at end of file +} // namespace armarx::armem::articulated_object diff --git a/source/RobotAPI/libraries/armem_robot/robot_conversions.cpp b/source/RobotAPI/libraries/armem_robot/robot_conversions.cpp index 291a7ef1f65546af0c698736bd099756830490b1..8b7829a2f0c3f6dadd54bf2ce624c9fcbe793efd 100644 --- a/source/RobotAPI/libraries/armem_robot/robot_conversions.cpp +++ b/source/RobotAPI/libraries/armem_robot/robot_conversions.cpp @@ -4,7 +4,7 @@ #include <RobotAPI/libraries/aron/common/aron_conversions.h> -#include "RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h" +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> #include <RobotAPI/libraries/armem_robot/aron/RobotDescription.aron.generated.h> diff --git a/source/RobotAPI/libraries/armem_robot/types.h b/source/RobotAPI/libraries/armem_robot/types.h index 4fed4d1cb277ab316a318cbc13b4f32222fd8939..4ae48e00f0755b8a24b7baab6f48df14e5f70632 100644 --- a/source/RobotAPI/libraries/armem_robot/types.h +++ b/source/RobotAPI/libraries/armem_robot/types.h @@ -10,6 +10,7 @@ #include <ArmarXCore/core/PackagePath.h> + namespace armarx::armem::robot { struct RobotDescription @@ -45,4 +46,4 @@ namespace armarx::armem::robot using RobotDescriptions = std::vector<RobotDescription>; using RobotStates = std::vector<RobotState>; -} // namespace armarx::armem::robot \ No newline at end of file +} // namespace armarx::armem::robot diff --git a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt index defd8a3f48aa68b63673498549a731b573b9b9b4..c86ed671ef17fcd287c05a7083abf3803b852e99 100644 --- a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt @@ -6,6 +6,7 @@ armarx_set_target("Library: ${LIB_NAME}") find_package(Eigen3 QUIET) armarx_build_if(Eigen3_FOUND "Eigen3 not available") + armarx_add_library( LIBS # ArmarX @@ -19,61 +20,81 @@ armarx_add_library( RobotAPIInterfaces RobotAPI::armem RobotAPI::armem_robot + aroncommon + # System / External Eigen3::Eigen - aroncommon + HEADERS - ./common/localization/types.h - ./common/localization/TransformHelper.h + common/localization/types.h + common/localization/TransformHelper.h + + client/common/RobotReader.h + client/common/VirtualRobotReader.h - ./client/common/RobotReader.h - ./client/common/VirtualRobotReader.h + client/localization/interfaces.h + client/localization/TransformReader.h + client/localization/TransformWriter.h - ./client/localization/interfaces.h - ./client/localization/TransformReader.h - ./client/localization/TransformWriter.h + server/forward_declarations.h - ./server/common/Visu.h + server/common/Visu.h + server/common/combine.h - ./server/localization/Segment.h - # ./server/localization/Visu.h + server/localization/Segment.h - ./server/proprioception/Segment.h - # ./server/proprioception/Visu.h + server/proprioception/Segment.h + server/proprioception/aron_conversions.h + server/proprioception/RobotStateWriter.h + server/proprioception/RobotUnitData.h + server/proprioception/RobotUnitReader.h - ./server/description/Segment.h + server/proprioception/converters/Armar6Converter.h + server/proprioception/converters/ConverterTools.h + server/proprioception/converters/ConverterRegistry.h + server/proprioception/converters/ConverterInterface.h + server/description/Segment.h + + aron_conversions.h + utils.h - ./aron_conversions.h - ./utils.h SOURCES - ./common/localization/TransformHelper.cpp + common/localization/TransformHelper.cpp - ./client/common/RobotReader.cpp - ./client/common/VirtualRobotReader.cpp + client/common/RobotReader.cpp + client/common/VirtualRobotReader.cpp - ./client/localization/TransformReader.cpp - ./client/localization/TransformWriter.cpp + client/localization/TransformReader.cpp + client/localization/TransformWriter.cpp - ./server/common/Visu.cpp + server/common/Visu.cpp + server/common/combine.cpp - ./server/localization/Segment.cpp - # ./server/localization/Visu.cpp + server/localization/Segment.cpp - ./server/proprioception/Segment.cpp - # ./server/proprioception/Visu.cpp + server/proprioception/Segment.cpp + server/proprioception/aron_conversions.cpp + server/proprioception/RobotStateWriter.cpp + server/proprioception/RobotUnitData.cpp + server/proprioception/RobotUnitReader.cpp - ./server/description/Segment.cpp + server/proprioception/converters/Armar6Converter.cpp + server/proprioception/converters/ConverterTools.cpp + server/proprioception/converters/ConverterRegistry.cpp + server/proprioception/converters/ConverterInterface.cpp - ./aron_conversions.cpp - ./utils.cpp + server/description/Segment.cpp + aron_conversions.cpp + utils.cpp ) armarx_enable_aron_file_generation_for_target( TARGET_NAME "${LIB_NAME}" + ARON_FILES aron/JointState.xml aron/Proprioception.xml diff --git a/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml b/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml index 38eb6bb439197032cac8968a0dbee131eec8efbc..99900b7b0766613a555fb060fa19fddf987def7f 100644 --- a/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml +++ b/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml @@ -1,79 +1,214 @@ <?xml version="1.0" encoding="UTF-8" ?> <!-- -Robot proprioception. +(Armar6) Robot proprioception. --> <AronTypeDefinition> + <CodeIncludes> + <Include include="<Eigen/Core>" /> + <Include include="<Eigen/Geometry>" /> + </CodeIncludes> + <GenerateTypes> <Object name="armarx::armem::prop::arondto::Joints"> - <ObjectChild key="positions"> + <ObjectChild key="position"> <Dict> <Float /> </Dict> </ObjectChild> - <ObjectChild key="velocities"> + <ObjectChild key="velocity"> <Dict> <Float /> </Dict> </ObjectChild> - <ObjectChild key="accellerations"> + <ObjectChild key="acceleration"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + + <ObjectChild key="relativePosition"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + <ObjectChild key="filteredVelocity"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + + <ObjectChild key="currentTarget"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + <ObjectChild key="positionTarget"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + <ObjectChild key="velocityTarget"> <Dict> <Float /> </Dict> </ObjectChild> - <ObjectChild key="torques"> + + <ObjectChild key="torque"> <Dict> <Float /> </Dict> </ObjectChild> - <ObjectChild key="inertiaTorques"> + <ObjectChild key="inertiaTorque"> <Dict> <Float /> </Dict> </ObjectChild> - <ObjectChild key="gravityTorques"> + <ObjectChild key="gravityTorque"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + <ObjectChild key="gravityCompensatedTorque"> <Dict> <Float /> </Dict> </ObjectChild> - <ObjectChild key="inserveDynamicsTorques"> + <ObjectChild key="inverseDynamicsTorque"> <Dict> <Float /> </Dict> </ObjectChild> + + <ObjectChild key="torqueTicks"> + <Dict> + <Int /> + </Dict> + </ObjectChild> + + + <ObjectChild key="orientation"> + <Dict> + <EigenMatrix rows="4" cols="1" type="float" /> + <!--Orientation /--> + </Dict> + </ObjectChild> + + <ObjectChild key="angularVelocity"> + <Dict> + <EigenMatrix rows="3" cols="1" type="float" /> + </Dict> + </ObjectChild> + + <ObjectChild key="linearAcceleration"> + <Dict> + <EigenMatrix rows="3" cols="1" type="float" /> + </Dict> + </ObjectChild> + + + <ObjectChild key="temperature"> + <Dict> + <Dict> + <Float /> + </Dict> + </Dict> + </ObjectChild> + + + <ObjectChild key="motorCurrent"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + <ObjectChild key="maxTargetCurrent"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + + <ObjectChild key="sensorBoardUpdateRate"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + <ObjectChild key="I2CUpdateRate"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + + <ObjectChild key="JointStatusEmergencyStop"> + <Dict> + <Bool /> + </Dict> + </ObjectChild> + + <ObjectChild key="JointStatusEnabled"> + <Dict> + <Bool /> + </Dict> + </ObjectChild> + + <ObjectChild key="JointStatusError"> + <Dict> + <Int /> + </Dict> + </ObjectChild> + + <ObjectChild key="JointStatusOperation"> + <Dict> + <Int /> + </Dict> + </ObjectChild> </Object> <Object name="armarx::armem::prop::arondto::Platform"> - <ObjectChild key="velocity"> + <ObjectChild key="relativePosition"> <EigenMatrix rows="3" cols="1" type="float" /> </ObjectChild> - - <ObjectChild key="acceleration"> + + <!-- Global pose is not part of proprioception. --> + <!--ObjectChild key="absolutePosition"> + <EigenMatrix rows="3" cols="1" type="float" /> + </ObjectChild--> + + <ObjectChild key="velocity"> <EigenMatrix rows="3" cols="1" type="float" /> </ObjectChild> - <ObjectChild key="rot_velocity"> + <ObjectChild key="acceleration"> <EigenMatrix rows="3" cols="1" type="float" /> </ObjectChild> - <ObjectChild key="rot_acceleration"> - <EigenMatrix rows="3" cols="1" type="float" /> + <ObjectChild key="extra"> + <Dict> + <Float /> + </Dict> </ObjectChild> - + </Object> - <Object name="armarx::armem::prop::arondto::ForceTorque"> <ObjectChild key="force"> @@ -92,12 +227,22 @@ Robot proprioception. <EigenMatrix rows="3" cols="1" type="float" /> </ObjectChild> + <ObjectChild key="extra"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + </Object> - <Object name="armarx::armem::prop::arondto::Proprioception"> + <Object name="armarx::armem::arondto::Proprioception"> + <ObjectChild key="iterationID"> + <long /> + </ObjectChild> + <ObjectChild key="joints"> <armarx::armem::prop::arondto::Joints/> </ObjectChild> @@ -111,7 +256,20 @@ Robot proprioception. <armarx::armem::prop::arondto::ForceTorque/> </Dict> </ObjectChild> - + + + <ObjectChild key="extraFloats"> + <Dict> + <Float /> + </Dict> + </ObjectChild> + + <ObjectChild key="extraLongs"> + <Dict> + <Long /> + </Dict> + </ObjectChild> + </Object> diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp index 7a500d1391c0f0942795b49af65740f350eecbed..85370141f880a72b26039c0af0c765f929e6a902 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp @@ -10,7 +10,7 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/core/error.h> #include <RobotAPI/libraries/armem/core/Time.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_robot/aron_conversions.h> #include <RobotAPI/libraries/armem_robot/robot_conversions.h> #include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> @@ -214,30 +214,31 @@ namespace armarx::armem::robot_state const armem::wm::ProviderSegment& providerSegment = memory .getCoreSegment(properties.proprioceptionCoreSegment) .getProviderSegment(name); - // clang-format on - const auto entities = simox::alg::get_values(providerSegment.entities()); // TODO entitiesToRobotState() - if (entities.empty()) + if (providerSegment.empty()) { ARMARX_WARNING << "No entity found"; return std::nullopt; } - const auto entitySnapshots = simox::alg::get_values(entities.front().history()); + // clang-format on - if (entitySnapshots.empty()) + const armem::wm::EntityInstance* instance = nullptr; + providerSegment.forEachInstance([&instance](const wm::EntityInstance & i) + { + instance = &i; + return false; // break + }); + if (!instance) { ARMARX_WARNING << "No entity snapshots found"; return std::nullopt; } - // TODO(fabian.reister): check if 0 available - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - // Here, we access the RobotUnit streaming data stored in the proprioception segment. - return robot::convertRobotState(instance); + return robot::convertRobotState(*instance); } // FIXME remove this, use armem/util/util.h @@ -271,24 +272,19 @@ namespace armarx::armem::robot_state .getCoreSegment(properties.proprioceptionCoreSegment); // clang-format on - for (const auto &[_, providerSegment] : coreSegment.providerSegments()) + coreSegment.forEachEntity([&jointMap](const wm::Entity & entity) { + const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); - for (const auto &[name, entity] : providerSegment.entities()) + const auto jointState = tryCast<::armarx::armem::arondto::JointState>(entityInstance); + if (not jointState) { - const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); - - const auto jointState = tryCast<::armarx::armem::arondto::JointState>(entityInstance); - - if (not jointState) - { - // ARMARX_WARNING << "Could not convert entity instance to 'JointState'"; - continue; - } - - jointMap.emplace(jointState->name, jointState->position); + // ARMARX_WARNING << "Could not convert entity instance to 'JointState'"; + return; } - } + + jointMap.emplace(jointState->name, jointState->position); + }); if (jointMap.empty()) { @@ -307,26 +303,19 @@ namespace armarx::armem::robot_state .getCoreSegment(properties.descriptionCoreSegment) .getProviderSegment(name); // TODO(fabian.reister): all // clang-format on - const auto entities = simox::alg::get_values(providerSegment.entities()); - if (entities.empty()) + const armem::wm::EntityInstance* instance = nullptr; + providerSegment.forEachInstance([&instance](const wm::EntityInstance & i) { - ARMARX_WARNING << "No entity found"; - return std::nullopt; - } - - const auto entitySnapshots = simox::alg::get_values(entities.front().history()); - - if (entitySnapshots.empty()) + instance = &i; + }); + if (!instance) { ARMARX_WARNING << "No entity snapshots found"; return std::nullopt; } - // TODO(fabian.reister): check if 0 available - const armem::wm::EntityInstance& instance = entitySnapshots.front().getInstance(0); - - return robot::convertRobotDescription(instance); + return robot::convertRobotDescription(*instance); } } // namespace armarx::armem::robot_state diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp index 6aa026431e7d9efd0128a9007d23918b291b54bc..d46c7dfa1cc72847b27a820ecd4281ec1cb649e6 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp @@ -20,8 +20,6 @@ */ #include "TransformReader.h" -#include "RobotAPI/libraries/armem/core/Time.h" -#include "RobotAPI/libraries/armem_robot_state/common/localization/types.h" #include <algorithm> #include <iterator> @@ -45,20 +43,23 @@ #include <ArmarXCore/core/time/CycleUtil.h> // this package +#include <RobotAPI/libraries/core/FramedPose.h> + #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> +#include <RobotAPI/libraries/armem/core/Time.h> #include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> #include <RobotAPI/libraries/armem/server/MemoryRemoteGui.h> #include <RobotAPI/libraries/aron/core/navigator/type/NavigatorFactory.h> -#include <RobotAPI/libraries/core/FramedPose.h> #include <RobotAPI/libraries/armem_robot_state/aron_conversions.h> #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h> - +#include <RobotAPI/libraries/armem_robot_state/common/localization/types.h> #include <RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h> + namespace armarx::armem::client::robot_state::localization { @@ -135,11 +136,11 @@ namespace armarx::armem::client::robot_state::localization TransformResult TransformReader::lookupTransform(const TransformQuery& query) const { const auto& timestamp = query.header.timestamp; - - const auto durationEpsilon = IceUtil::Time::milliSeconds(-1); - ARMARX_DEBUG << "Looking up transform at timestamp " << timestamp; + const IceUtil::Time durationEpsilon = IceUtil::Time::milliSeconds(-1); + (void) durationEpsilon; + // Query all entities from provider. armem::client::query::Builder qb; @@ -152,18 +153,20 @@ namespace armarx::armem::client::robot_state::localization // clang-format on const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput()); - ARMARX_DEBUG << "Lookup result in reader: " << qResult; if (not qResult.success) { - return {.transform = + return + { + .transform = { .header = query.header, }, .status = TransformResult::Status::ErrorFrameNotAvailable, .errorMessage = "Error in tf lookup '" + query.header.parentFrame + " -> " + - query.header.frame + "' : " + qResult.errorMessage}; + query.header.frame + "' : " + qResult.errorMessage + }; } const auto& localizationCoreSegment = qResult.memory.getCoreSegment(properties.localizationSegment); diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.cpp b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.cpp index 4230394aa823c96e2382fd73af68d9663513b989..608dfa3947153985dc4f528bb0746ab95df706fc 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.cpp @@ -37,9 +37,9 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ice_conversions.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h> +#include <RobotAPI/libraries/armem/core/wm/ice_conversions.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/server/MemoryRemoteGui.h> #include <RobotAPI/libraries/aron/core/navigator/type/NavigatorFactory.h> #include <RobotAPI/libraries/core/FramedPose.h> diff --git a/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp index c4d0e05b4a1120b0ba6d256ad646508d7556ab7d..f20f68cf8bfda8dd57f97264e98bee84d61a83bd 100644 --- a/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.cpp @@ -2,104 +2,119 @@ #include <SimoxUtility/algorithm/get_map_keys_values.h> #include <SimoxUtility/algorithm/string/string_tools.h> -#include <SimoxUtility/color/cmaps.h> #include <SimoxUtility/math/pose/interpolate.h> -#include "ArmarXCore/core/exceptions/LocalException.h" -#include "RobotAPI/libraries/core/FramedPose.h" +#include <ArmarXCore/core/exceptions/LocalException.h> +#include <RobotAPI/libraries/core/FramedPose.h> -#include "RobotAPI/libraries/aron/common/aron_conversions.h" +#include <RobotAPI/libraries/aron/common/aron_conversions.h> -#include "RobotAPI/libraries/armem/core/error/ArMemError.h" -#include "RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h" -#include "RobotAPI/libraries/armem/core/workingmemory/Memory.h" +#include <RobotAPI/libraries/armem/core/Time.h> +#include <RobotAPI/libraries/armem/core/error/ArMemError.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/server/wm/memory_definitions.h> #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron_conversions.h> + namespace armarx::armem::common::robot_state::localization { - TransformChainResult TransformHelper::lookupTransformChain(const armem::wm::CoreSegment& localizationCoreSegment, - const TransformQuery& query) + template <class ...Args> + TransformResult + TransformHelper::_lookupTransform( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const TransformQuery& query) { - const std::vector<std::string> tfChain = - buildTransformChain(localizationCoreSegment, query); - + const std::vector<std::string> tfChain = _buildTransformChain(localizationCoreSegment, query); if (tfChain.empty()) { - return {.header = query.header, - .transforms = std::vector<Eigen::Affine3f>{}, - .status = TransformChainResult::Status::ErrorFrameNotAvailable, + return {.transform = {.header = query.header}, + .status = TransformResult::Status::ErrorFrameNotAvailable, .errorMessage = "Cannot create tf lookup chain '" + query.header.parentFrame + " -> " + query.header.frame + "'"}; } - const std::vector<Eigen::Affine3f> transforms = obtainTransforms( + const std::vector<Eigen::Affine3f> transforms = _obtainTransforms( localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp); - if (transforms.empty()) { ARMARX_WARNING << deactivateSpam(1) << "No transform available."; - return {.header = query.header, - .transforms = {}, - .status = TransformChainResult::Status::ErrorFrameNotAvailable, + return {.transform = {.header = query.header}, + .status = TransformResult::Status::ErrorFrameNotAvailable, .errorMessage = "Error in TF loookup: '" + query.header.parentFrame + " -> " + query.header.frame + "'. No memory data in time range."}; } + const Eigen::Affine3f transform = std::accumulate(transforms.begin(), + transforms.end(), + Eigen::Affine3f::Identity(), + std::multiplies<>()); ARMARX_DEBUG << "Found valid transform"; - return {.header = query.header, - .transforms = transforms, - .status = TransformChainResult::Status::Success}; + return {.transform = {.header = query.header, .transform = transform}, + .status = TransformResult::Status::Success}; } - TransformResult TransformHelper::lookupTransform(const armem::wm::CoreSegment& localizationCoreSegment, - const TransformQuery& query) - { - const std::vector<std::string> tfChain = - buildTransformChain(localizationCoreSegment, query); + template <class ...Args> + TransformChainResult + TransformHelper::_lookupTransformChain( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const TransformQuery& query) + { + const std::vector<std::string> tfChain = _buildTransformChain(localizationCoreSegment, query); if (tfChain.empty()) { - return {.transform = {.header = query.header}, - .status = TransformResult::Status::ErrorFrameNotAvailable, - .errorMessage = "Cannot create tf lookup chain '" + - query.header.parentFrame + " -> " + query.header.frame + - "'"}; + return + { + .header = query.header, + .transforms = std::vector<Eigen::Affine3f>{}, + .status = TransformChainResult::Status::ErrorFrameNotAvailable, + .errorMessage = "Cannot create tf lookup chain '" + + query.header.parentFrame + " -> " + query.header.frame + + "'" + }; } - const std::vector<Eigen::Affine3f> transforms = obtainTransforms( + const std::vector<Eigen::Affine3f> transforms = _obtainTransforms( localizationCoreSegment, tfChain, query.header.agent, query.header.timestamp); - if (transforms.empty()) { ARMARX_WARNING << deactivateSpam(1) << "No transform available."; - return {.transform = {.header = query.header}, - .status = TransformResult::Status::ErrorFrameNotAvailable, - .errorMessage = "Error in TF loookup: '" + query.header.parentFrame + - " -> " + query.header.frame + - "'. No memory data in time range."}; + return + { + .header = query.header, + .transforms = {}, + .status = TransformChainResult::Status::ErrorFrameNotAvailable, + .errorMessage = "Error in TF loookup: '" + query.header.parentFrame + + " -> " + query.header.frame + + "'. No memory data in time range." + }; } - const Eigen::Affine3f transform = std::accumulate(transforms.begin(), - transforms.end(), - Eigen::Affine3f::Identity(), - std::multiplies<>()); ARMARX_DEBUG << "Found valid transform"; - return {.transform = {.header = query.header, .transform = transform}, - .status = TransformResult::Status::Success}; + return + { + .header = query.header, + .transforms = transforms, + .status = TransformChainResult::Status::Success + }; } + + + template <class ...Args> std::vector<std::string> - TransformHelper::buildTransformChain(const armem::wm::CoreSegment& localizationCoreSegment, - const TransformQuery& query) + TransformHelper::_buildTransformChain( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const TransformQuery& query) { ARMARX_DEBUG << "Building transform chain"; @@ -110,8 +125,7 @@ namespace armarx::armem::common::robot_state::localization std::vector<std::string> chain; - const auto& agentProviderSegment = - localizationCoreSegment.getProviderSegment(query.header.agent); + const auto& agentProviderSegment = localizationCoreSegment.getProviderSegment(query.header.agent); auto addToChain = [&](const std::string & parentFrame, const std::string & frame) { @@ -123,14 +137,14 @@ namespace armarx::armem::common::robot_state::localization } else { - ARMARX_WARNING << deactivateSpam(10) << "Cannot perform tf lookup '" << parentFrame << " -> " << frame - << "'"; + ARMARX_WARNING << deactivateSpam(10) << "Cannot perform tf lookup '" << parentFrame << " -> " << frame << "'"; } }; std::array<std::string, 3> knownChain { - GlobalFrame, MapFrame, OdometryFrame}; // Robot comes next + GlobalFrame, MapFrame, OdometryFrame + }; // Robot comes next auto* frameBeginIt = std::find(knownChain.begin(), knownChain.end(), query.header.parentFrame); @@ -172,8 +186,100 @@ namespace armarx::armem::common::robot_state::localization return chain; } - inline ::armarx::armem::robot_state::Transform - convertEntityToTransform(const armem::wm::EntityInstance& item) + + template <class ...Args> + std::vector<Eigen::Affine3f> + TransformHelper::_obtainTransforms( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const std::vector<std::string>& tfChain, + const std::string& agent, + const armem::Time& timestamp) + { + const auto& agentProviderSegment = localizationCoreSegment.getProviderSegment(agent); + + ARMARX_DEBUG << "Provider segments" << localizationCoreSegment.getProviderSegmentNames(); + ARMARX_DEBUG << "Entities: " << agentProviderSegment.getEntityNames(); + + try + { + std::vector<Eigen::Affine3f> transforms; + transforms.reserve(tfChain.size()); + std::transform(tfChain.begin(), + tfChain.end(), + std::back_inserter(transforms), + [&](const std::string & entityName) + { + return _obtainTransform(entityName, agentProviderSegment, timestamp); + }); + return transforms; + } + catch (const armem::error::MissingEntry& missingEntryError) + { + ARMARX_WARNING << missingEntryError.what(); + } + catch (const ::armarx::exceptions::local::ExpressionException& ex) + { + ARMARX_WARNING << "local exception: " << ex.what(); + } + catch (...) + { + ARMARX_WARNING << "Error: " << GetHandledExceptionString(); + } + + return {}; + } + + + template <class ...Args> + Eigen::Affine3f + TransformHelper::_obtainTransform( + const std::string& entityName, + const armem::base::ProviderSegmentBase<Args...>& agentProviderSegment, + const armem::Time& timestamp) + { + // ARMARX_DEBUG << "getEntity:" + entityName; + const auto& entity = agentProviderSegment.getEntity(entityName); + + // ARMARX_DEBUG << "History (size: " << entity.size() << "): " << entity.getTimestamps(); + + // if (entity.history.empty()) + // { + // // TODO(fabian.reister): fixme boom + // ARMARX_ERROR << "No snapshots received."; + // return Eigen::Affine3f::Identity(); + // } + + std::vector<::armarx::armem::robot_state::Transform> transforms; + transforms.push_back(_convertEntityToTransform(entity.getLatestSnapshot().getInstance(0))); + + // ARMARX_DEBUG << "obtaining transform"; + if (transforms.size() > 1) + { + // TODO(fabian.reister): remove + return transforms.front().transform; + + // ARMARX_DEBUG << "More than one snapshots received: " << transforms.size(); + const auto p = _interpolateTransform(transforms, timestamp); + // ARMARX_DEBUG << "Done interpolating transform"; + return p; + } + + // accept this to fail (will raise armem::error::MissingEntry) + if (transforms.empty()) + { + // ARMARX_DEBUG << "empty transform"; + + throw armem::error::MissingEntry("foo", "bar", "foo2", "bar2", 0); + } + + // ARMARX_DEBUG << "single transform"; + + return transforms.front().transform; + } + + + ::armarx::armem::robot_state::Transform + TransformHelper::_convertEntityToTransform(const armem::wm::EntityInstance& item) { arondto::Transform aronTransform; aronTransform.fromAron(item.data()); @@ -203,16 +309,15 @@ namespace armarx::armem::common::robot_state::localization const auto poseNextIt = std::find_if(transforms.begin(), transforms.end(), timestampBeyond); - ARMARX_WARNING << "Fooasdfasdjfh"; - ARMARX_CHECK(it == poseNextIt); return poseNextIt; } Eigen::Affine3f - interpolateTransform(const std::vector<::armarx::armem::robot_state::Transform>& queue, - armem::Time timestamp) + TransformHelper::_interpolateTransform( + const std::vector<::armarx::armem::robot_state::Transform>& queue, + armem::Time timestamp) { ARMARX_TRACE; @@ -255,109 +360,33 @@ namespace armarx::armem::common::robot_state::localization return simox::math::interpolatePose(posePreIt->transform, poseNextIt->transform, static_cast<float>(t)); } - std::vector<Eigen::Affine3f> - TransformHelper::obtainTransforms(const armem::wm::CoreSegment& localizationCoreSegment, - const std::vector<std::string>& tfChain, - const std::string& agent, - const armem::Time& timestamp) - { - const auto& agentProviderSegment = localizationCoreSegment.getProviderSegment(agent); - - ARMARX_DEBUG << "Provider segments" - << simox::alg::get_keys(localizationCoreSegment.providerSegments()); - - ARMARX_DEBUG << "Entities: " << simox::alg::get_keys(agentProviderSegment.entities()); - - try - { - std::vector<Eigen::Affine3f> transforms; - transforms.reserve(tfChain.size()); - std::transform(tfChain.begin(), - tfChain.end(), - std::back_inserter(transforms), - [&](const std::string & entityName) - { - return obtainTransform( - entityName, agentProviderSegment, timestamp); - }); - return transforms; - } - catch (const armem::error::MissingEntry& missingEntryError) - { - ARMARX_WARNING << missingEntryError.what(); - } - catch (const ::armarx::exceptions::local::ExpressionException& ex) - { - ARMARX_WARNING << "local exception: " << ex.what(); - } - catch (...) - { - ARMARX_WARNING << "Error: " << GetHandledExceptionString(); - } - - - return {}; + TransformResult TransformHelper::lookupTransform( + const armem::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query) + { + return _lookupTransform(localizationCoreSegment, query); } - - Eigen::Affine3f - TransformHelper::obtainTransform(const std::string& entityName, - const armem::wm::ProviderSegment& agentProviderSegment, - const armem::Time& timestamp) + TransformResult TransformHelper::lookupTransform( + const armem::server::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query) { + return _lookupTransform(localizationCoreSegment, query); + } - ARMARX_DEBUG << "getEntity:" + entityName; - const auto& entity = agentProviderSegment.getEntity(entityName); - - ARMARX_DEBUG << "History (size: " << entity.history().size() << ")" - << simox::alg::get_keys(entity.history()); - - // if (entity.history.empty()) - // { - // // TODO(fabian.reister): fixme boom - // ARMARX_ERROR << "No snapshots received."; - // return Eigen::Affine3f::Identity(); - // } - - std::vector<::armarx::armem::robot_state::Transform> transforms; - transforms.reserve(entity.history().size()); - - // const auto entitySnapshots = simox::alg::get_values(entity.history()); - - const std::vector<wm::EntitySnapshot> entitySnapshots = {entity.getLatestSnapshot()}; - - std::transform( - entitySnapshots.begin(), - entitySnapshots.end(), - std::back_inserter(transforms), - [](const auto & entitySnapshot) - { - return convertEntityToTransform(entitySnapshot.getInstance(0)); - }); - - ARMARX_DEBUG << "obtaining transform"; - - if (transforms.size() > 1) - { - // TODO(fabian.reister): remove - return transforms.front().transform; - - ARMARX_DEBUG << "More than one snapshots received: " << transforms.size(); - const auto p = interpolateTransform(transforms, timestamp); - ARMARX_DEBUG << "Done interpolating transform"; - return p; - } - - // accept this to fail (will raise armem::error::MissingEntry) - if (transforms.empty()) - { - ARMARX_DEBUG << "empty transform"; - throw armem::error::MissingEntry("foo", "bar", "foo2", "bar2", 0); - } + TransformChainResult TransformHelper::lookupTransformChain( + const armem::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query) + { + return _lookupTransformChain(localizationCoreSegment, query); + } + TransformChainResult TransformHelper::lookupTransformChain( + const armem::server::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query) + { + return _lookupTransformChain(localizationCoreSegment, query); + } - ARMARX_DEBUG << "single transform"; - return transforms.front().transform; - } } // namespace armarx::armem::common::robot_state::localization diff --git a/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h index cafbedcbca60f859f6fe158190a5985350ddad65..9de1fbd994f5a17c00987ce70d31525db7d9d1dd 100644 --- a/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h +++ b/source/RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h @@ -26,44 +26,102 @@ #include <Eigen/Core> #include <Eigen/Geometry> +#include <RobotAPI/libraries/armem/core/forward_declarations.h> #include <RobotAPI/libraries/armem_robot_state/common/localization/types.h> -namespace armarx::armem::wm -{ - class CoreSegment; - class ProviderSegment; -} // namespace armarx::armem::wm namespace armarx::armem::common::robot_state::localization { using armarx::armem::common::robot_state::localization::TransformQuery; using armarx::armem::common::robot_state::localization::TransformResult; + + class TransformHelper { public: - static TransformResult - lookupTransform(const armem::wm::CoreSegment& localizationCoreSegment, - const TransformQuery& query); - static TransformChainResult - lookupTransformChain(const armem::wm::CoreSegment& localizationCoreSegment, - const TransformQuery& query); + static + TransformResult + lookupTransform( + const armem::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query); + + static + TransformResult + lookupTransform( + const armem::server::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query); + + + static + TransformChainResult + lookupTransformChain( + const armem::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query); + + static + TransformChainResult + lookupTransformChain( + const armem::server::wm::CoreSegment& localizationCoreSegment, + const TransformQuery& query); + private: - static std::vector<std::string> - buildTransformChain(const armem::wm::CoreSegment& localizationCoreSegment, - const TransformQuery& query); - - static std::vector<Eigen::Affine3f> - obtainTransforms(const armem::wm::CoreSegment& localizationCoreSegment, - const std::vector<std::string>& tfChain, - const std::string& agent, - const armem::Time& timestamp); - - static Eigen::Affine3f - obtainTransform(const std::string& entityName, - const armem::wm::ProviderSegment& agentProviderSegment, - const armem::Time& timestamp); + + template <class ...Args> + static + TransformResult + _lookupTransform( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const TransformQuery& query); + + + template <class ...Args> + static + TransformChainResult + _lookupTransformChain( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const TransformQuery& query); + + + template <class ...Args> + static + std::vector<std::string> + _buildTransformChain( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const TransformQuery& query); + + + template <class ...Args> + static + std::vector<Eigen::Affine3f> + _obtainTransforms( + const armem::base::CoreSegmentBase<Args...>& localizationCoreSegment, + const std::vector<std::string>& tfChain, + const std::string& agent, + const armem::Time& timestamp); + + + template <class ...Args> + static + Eigen::Affine3f + _obtainTransform( + const std::string& entityName, + const armem::base::ProviderSegmentBase<Args...>& agentProviderSegment, + const armem::Time& timestamp); + + + static + Eigen::Affine3f + _interpolateTransform( + const std::vector<::armarx::armem::robot_state::Transform>& queue, + armem::Time timestamp); + + static + ::armarx::armem::robot_state::Transform + _convertEntityToTransform( + const armem::wm::EntityInstance& item); + }; -} // namespace armarx::armem::common::robot_state::localization \ No newline at end of file +} // namespace armarx::armem::common::robot_state::localization diff --git a/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.cpp b/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.cpp index d6dcddd7d3fd568216a63657bfe6ae5ca1f10f9c..051ab03e793a0afceca524e007b834f4f510e6a0 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.cpp @@ -6,8 +6,6 @@ #include <Eigen/Geometry> -#include <IceUtil/Time.h> - #include <SimoxUtility/algorithm/get_map_keys_values.h> #include <SimoxUtility/math/pose.h> @@ -15,23 +13,30 @@ #include <ArmarXCore/core/time/CycleUtil.h> #include <ArmarXCore/core/time/TimeUtil.h> -#include <RobotAPI/components/ArViz/Client/Elements.h> #include <RobotAPI/libraries/armem/core/Time.h> -#include <RobotAPI/libraries/ArmarXObjects/ObjectFinder.h> -#include "../description/Segment.h" -#include "../localization/Segment.h" -#include "../proprioception/Segment.h" +#include <RobotAPI/components/ArViz/Client/Elements.h> + +#include <RobotAPI/libraries/armem_robot_state/server/description/Segment.h> +#include <RobotAPI/libraries/armem_robot_state/server/localization/Segment.h> +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h> + +#include "combine.h" namespace armarx::armem::server::robot_state { - Visu::Visu(const description::Segment& descriptionSegment, const proprioception::Segment& proprioceptionSegment, const localization::Segment& localizationSegment) + Visu::Visu( + const description::Segment& descriptionSegment, + const proprioception::Segment& proprioceptionSegment, + const localization::Segment& localizationSegment) : descriptionSegment(descriptionSegment), proprioceptionSegment(proprioceptionSegment), localizationSegment(localizationSegment) - {} + { + Logging::setTag("Visu"); + } void Visu::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) @@ -56,6 +61,12 @@ namespace armarx::armem::server::robot_state this->debugObserver = DebugObserverHelper("RobotStateMemory", debugObserver, batchMode); } + if (updateTask) + { + updateTask->stop(); + updateTask->join(); + updateTask = nullptr; + } updateTask = new SimpleRunningTask<>([this]() { this->visualizeRun(); @@ -66,24 +77,21 @@ namespace armarx::armem::server::robot_state void Visu::visualizeRobots(viz::Layer& layer, const robot::Robots& robots) { - const auto visualizeRobot = [&](const robot::Robot & robot) + for (const robot::Robot& robot : robots) { - const auto xmlPath = robot.description.xml.serialize(); + const data::PackagePath xmlPath = robot.description.xml.serialize(); // clang-format off - auto robotVisu = viz::Robot(robot.description.name) - // .file(xmlPath.package, xmlPath.path) - .file(xmlPath.package, xmlPath.path) - .joints(robot.config.jointMap) - .pose(robot.config.globalPose); + viz::Robot robotVisu = viz::Robot(robot.description.name) + .file(xmlPath.package, xmlPath.path) + .joints(robot.config.jointMap) + .pose(robot.config.globalPose); robotVisu.useFullModel(); // clang-format on layer.add(robotVisu); - }; - - std::for_each(robots.begin(), robots.end(), visualizeRobot); + } } @@ -107,161 +115,117 @@ namespace armarx::armem::server::robot_state } - robot::Robots combine( - const std::unordered_map<std::string, robot::RobotDescription>& robotDescriptions, - const std::unordered_map<std::string, Eigen::Affine3f>& globalRobotPoseMap, - const std::unordered_map<std::string, std::map<std::string, float>>& robotJointPositionMap) + void Visu::visualizeRun() { - - robot::Robots robots; - - for (const auto &[robotName, robotDescription] : robotDescriptions) + CycleUtil cycle(static_cast<int>(1000 / p.frequencyHz)); + while (updateTask and not updateTask->isStopped()) { - const auto& globalPose = globalRobotPoseMap.find(robotName); - if (globalPose == globalRobotPoseMap.end()) + if (p.enabled) { - ARMARX_WARNING << deactivateSpam(10) << "No global pose for robot '" << robotName - << "'"; - continue; - } + const Time timestamp = Time::now(); + ARMARX_DEBUG << "Visu task at " << armem::toStringMilliSeconds(timestamp); - const auto& jointMap = robotJointPositionMap.find(robotName); - if (jointMap == robotJointPositionMap.end()) - { - ARMARX_WARNING << deactivateSpam(10) << "No joint positions for robot '" - << robotName << "'"; - continue; + try + { + visualizeOnce(timestamp); + } + catch (const std::exception& e) + { + ARMARX_WARNING << "Caught exception while visualizing robots: \n" << e.what(); + } + catch (...) + { + ARMARX_WARNING << "Caught unknown exception while visualizing robots."; + } + + if (debugObserver.has_value()) + { + debugObserver->sendDebugObserverBatch(); + } } + cycle.waitForCycleDuration(); + } + } - ARMARX_DEBUG << "Found the following joints for robot " << robotName << ": " - << simox::alg::get_keys(jointMap->second); - // TODO(fabian.reister): based on data - const armem::Time timestamp = IceUtil::Time::now(); + void Visu::visualizeOnce(const Time& timestamp) + { + TIMING_START(tVisuTotal); - robots.emplace_back(robot::Robot - { - .description = robotDescription, - .instance = "", // TODO(fabian.reister): set this properly - .config = { - .timestamp = timestamp, - .globalPose = globalPose->second, - .jointMap = jointMap->second - }, - .timestamp = timestamp, - }); - } + // TODO(fabian.reister): use timestamp - return robots; - } + // Get data. + TIMING_START(tVisuGetData); + TIMING_START(tRobotDescriptions); + const description::RobotDescriptionMap robotDescriptions = + descriptionSegment.getRobotDescriptionsLocking(timestamp); + TIMING_END_STREAM(tRobotDescriptions, ARMARX_DEBUG); - void Visu::visualizeRun() - { - CycleUtil cycle(static_cast<int>(1000 / p.frequencyHz)); - while (updateTask && not updateTask->isStopped()) + TIMING_START(tGlobalPoses); + const auto globalPoses = localizationSegment.getRobotGlobalPosesLocking(timestamp); + TIMING_END_STREAM(tGlobalPoses, ARMARX_DEBUG); + + TIMING_START(tRobotFramePoses); + const auto frames = localizationSegment.getRobotFramePosesLocking(timestamp); + TIMING_END_STREAM(tRobotFramePoses, ARMARX_DEBUG); + + TIMING_START(tJointPositions); + const auto jointPositions = + proprioceptionSegment.getRobotJointPositionsLocking( + timestamp, debugObserver ? &*debugObserver : nullptr); + TIMING_END_STREAM(tJointPositions, ARMARX_DEBUG); + + TIMING_END_STREAM(tVisuGetData, ARMARX_DEBUG); + + + // Build layers. + TIMING_START(tVisuBuildLayers); + + // We need all 3 information: + // - robot description + // - global pose + // - joint positions + // => this is nothing but an armem::Robot + ARMARX_DEBUG << "Combining robot ..." + << "\n- " << robotDescriptions.size() << " descriptions" + << "\n- " << globalPoses.size() << " global poses" + << "\n- " << jointPositions.size() << " joint positions"; + + const robot::Robots robots = + combine(robotDescriptions, globalPoses, jointPositions, timestamp); + + ARMARX_DEBUG << "Visualize " << robots.size() << " robots ..."; + viz::Layer layer = arviz.layer("Robots"); + visualizeRobots(layer, robots); + + ARMARX_DEBUG << "Visualize frames ..."; + viz::Layer layerFrames = arviz.layer("Frames"); + visualizeFrames(layerFrames, frames); + + TIMING_END_STREAM(tVisuBuildLayers, ARMARX_DEBUG); + + + // Commit layers. + + ARMARX_DEBUG << "Commit visualization ..."; + TIMING_START(tVisuCommit); + arviz.commit({layer, layerFrames}); + TIMING_END_STREAM(tVisuCommit, ARMARX_DEBUG); + + TIMING_END_STREAM(tVisuTotal, ARMARX_DEBUG); + + if (debugObserver.has_value()) { - { - // std::scoped_lock lock(visuMutex); - ARMARX_DEBUG << "Update task"; - if (p.enabled) - { - TIMING_START(tVisuTotal); - - // TODO(fabian.reister): use timestamp - const Time timestamp = Time::now(); - - try - { - // Get data. - TIMING_START(tVisuGetData); - - TIMING_START(tRobotDescriptions); - const description::Segment::RobotDescriptionMap robotDescriptions = - descriptionSegment.getRobotDescriptionsLocking(timestamp); - TIMING_END_STREAM(tRobotDescriptions, ARMARX_DEBUG); - - TIMING_START(tGlobalRobotPoseMap); - const auto globalRobotPoseMap = - localizationSegment.getRobotGlobalPosesLocking(timestamp); - TIMING_END_STREAM(tGlobalRobotPoseMap, ARMARX_DEBUG); - - TIMING_START(tRobotFramePoses); - const auto frames = localizationSegment.getRobotFramePosesLocking(timestamp); - TIMING_END_STREAM(tRobotFramePoses, ARMARX_DEBUG); - - TIMING_START(tRobotJointPositionMap); - const auto robotJointPositionMap = - proprioceptionSegment.getRobotJointPositionsLocking( - timestamp, debugObserver ? &*debugObserver : nullptr); - TIMING_END_STREAM(tRobotJointPositionMap, ARMARX_DEBUG); - - TIMING_END_STREAM(tVisuGetData, ARMARX_DEBUG); - - - // Build layers. - TIMING_START(tVisuBuildLayers); - - // we need all 3 informations: - // - robot description - // - global pose - // - joint positions - // => this is nothing but a armem::Robot - - const robot::Robots robots = - combine(robotDescriptions, globalRobotPoseMap, robotJointPositionMap); - - ARMARX_DEBUG << "Visualize robots ..."; - viz::Layer layer = arviz.layer("Robots"); - visualizeRobots(layer, robots); - - ARMARX_DEBUG << "Visualize frames ..."; - viz::Layer layerFrames = arviz.layer("Frames"); - visualizeFrames(layerFrames, frames); - - TIMING_END_STREAM(tVisuBuildLayers, ARMARX_DEBUG); - - - // Commit layers. - - ARMARX_DEBUG << "Commit visualization ..."; - TIMING_START(tVisuCommit); - arviz.commit({layer, layerFrames}); - TIMING_END_STREAM(tVisuCommit, ARMARX_DEBUG); - - TIMING_END_STREAM(tVisuTotal, ARMARX_DEBUG); - - if (debugObserver.has_value()) - { - const std::string p = "Visu | "; - debugObserver->setDebugObserverDatafield(p + "t Total (ms)", tVisuTotal.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 1 Get Data (ms)", tVisuGetData.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 1.1 Descriptions (ms)", tRobotDescriptions.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 1.2 Global Pose (ms)", tGlobalRobotPoseMap.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 1.3 Frames (ms)", tRobotFramePoses.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 1.4 Joint Positions (ms)", tRobotJointPositionMap.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 2 Build Layers (ms)", tVisuBuildLayers.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(p + "t 3 Commit (ms)", tVisuCommit.toMilliSecondsDouble()); - } - } - catch (const std::exception& ex) - { - ARMARX_WARNING << "Caught exception while visualizing robots: \n" << ex.what(); - continue; - } - catch (...) - { - ARMARX_WARNING << "Caught unknown exception while visualizing robots."; - continue; - } - - if (debugObserver.has_value()) - { - debugObserver->sendDebugObserverBatch(); - } - } - } - cycle.waitForCycleDuration(); + const std::string p = "Visu | "; + debugObserver->setDebugObserverDatafield(p + "t Total (ms)", tVisuTotal.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 1 Get Data (ms)", tVisuGetData.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 1.1 Descriptions (ms)", tRobotDescriptions.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 1.2 Global Poses (ms)", tGlobalPoses.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 1.3 Frames (ms)", tRobotFramePoses.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 1.4 Joint Positions (ms)", tJointPositions.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 2 Build Layers (ms)", tVisuBuildLayers.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(p + "t 3 Commit (ms)", tVisuCommit.toMilliSecondsDouble()); } } diff --git a/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.h b/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.h index 95efae41963c63669e4e4bb11b202ba9ef948ec9..ce69772c1b1c55553c62e39d3db5e8e4503d2592 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.h @@ -31,27 +31,11 @@ #include <RobotAPI/libraries/armem_objects/types.h> +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> -namespace armarx -{ - class ObjectFinder; -} namespace armarx::armem::server::robot_state { - namespace localization - { - class Segment; - } - namespace proprioception - { - class Segment; - } - namespace description - { - class Segment; - } - /** * @brief Models decay of object localizations by decreasing the confidence @@ -71,14 +55,19 @@ namespace armarx::armem::server::robot_state void connect(const viz::Client& arviz, DebugObserverInterfacePrx debugObserver = nullptr); - protected: + private: + + void visualizeRun(); + void visualizeOnce(const Time& timestamp); - static void visualizeRobots( + + static + void visualizeRobots( viz::Layer& layer, - const robot::Robots& robots - ); + const robot::Robots& robots); - static void visualizeFrames( + static + void visualizeFrames( viz::Layer& layerFrames, const std::unordered_map<std::string, std::vector<Eigen::Affine3f>>& frames); @@ -100,7 +89,6 @@ namespace armarx::armem::server::robot_state SimpleRunningTask<>::pointer_type updateTask; - void visualizeRun(); }; diff --git a/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abe3f0bbb024d971052aad031b366a3d54b15ffe --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp @@ -0,0 +1,64 @@ +#include "combine.h" + +#include <RobotAPI/libraries/armem_robot/types.h> +#include <RobotAPI/libraries/armem/core/Time.h> + +#include <ArmarXCore/core/logging/Logging.h> + +#include <sstream> + + +namespace armarx::armem::server +{ + + robot::Robots + robot_state::combine( + const description::RobotDescriptionMap& robotDescriptions, + const localization::RobotPoseMap& globalPoses, + const proprioception::RobotJointPositionMap& jointPositions, + const armem::Time& timestamp) + { + std::stringstream logs; + + robot::Robots robots; + for (const auto& [robotName, robotDescription] : robotDescriptions) + { + // Handle missing values gracefully instead of skipping the robot altogether. + + robot::Robot& robot = robots.emplace_back(); + + robot.description = robotDescription; + robot.instance = ""; // TODO(fabian.reister): set this properly + robot.config.timestamp = timestamp; + robot.config.globalPose = Eigen::Affine3f::Identity(); + robot.config.jointMap = {}; + robot.timestamp = timestamp; + + if (auto it = globalPoses.find(robotName); it != globalPoses.end()) + { + robot.config.globalPose = it->second; + } + else + { + logs << "\nNo global pose for robot '" << robotName << "'."; + } + if (auto it = jointPositions.find(robotName); it != jointPositions.end()) + { + robot.config.jointMap = it->second; + } + else + { + logs << "\nNo joint positions for robot '" << robotName << "'."; + } + } + + if (not logs.str().empty()) + { + // These are handled, so they are no warnings. + ARMARX_VERBOSE << deactivateSpam(60) << logs.str(); + } + + return robots; + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h new file mode 100644 index 0000000000000000000000000000000000000000..811599b69d8fe853154520f5b72b4ca95eb9488f --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h @@ -0,0 +1,38 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/libraries/armem/core/forward_declarations.h> +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> + + +namespace armarx::armem::server::robot_state +{ + + robot::Robots + combine( + const description::RobotDescriptionMap& robotDescriptions, + const localization::RobotPoseMap& globalPoses, + const proprioception::RobotJointPositionMap& jointPositions, + const armem::Time& timestamp); + +} // namespace armarx::armem::server::robot_state diff --git a/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.cpp b/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.cpp index bd1e9c4fc9f8d95f2abbfd86f3eee5081e8f8ab4..7ca3f370184163d06419d84bfee6e7f4fd2876ff 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.cpp @@ -1,10 +1,5 @@ #include "Segment.h" -#include <sstream> -#include <thread> - -#include <IceUtil/Time.h> - #include <ArmarXCore/core/application/properties/PluginAll.h> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> @@ -19,7 +14,6 @@ #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/query_fns.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/workingmemory/visitor.h> #include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h> #include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> @@ -41,8 +35,6 @@ namespace armarx::armem::server::robot_state::description void Segment::onConnect(const RobotUnitInterfacePrx& robotUnitPrx) { - Base::onConnect(); - robotUnit = robotUnitPrx; // store the robot description linked to the robot unit in this segment @@ -104,39 +96,37 @@ namespace armarx::armem::server::robot_state::description } - Segment::RobotDescriptionMap Segment::getRobotDescriptionsLocking(const armem::Time& timestamp) const + RobotDescriptionMap + Segment::getRobotDescriptionsLocking(const armem::Time& timestamp) const { - std::scoped_lock lock(mutex()); - return getRobotDescriptions(timestamp); + return doLocked([this, ×tamp]() + { + return getRobotDescriptions(timestamp); + }); } - Segment::RobotDescriptionMap Segment::getRobotDescriptions(const armem::Time& timestamp) const + RobotDescriptionMap + Segment::getRobotDescriptions(const armem::Time& timestamp) const { ARMARX_CHECK_NOT_NULL(segment); (void) timestamp; // Unused RobotDescriptionMap robotDescriptions; + segment->forEachEntity([this, &robotDescriptions](const wm::Entity & entity) { - for (const auto& [_, provSeg] : *segment) + const wm::EntityInstance& entityInstance = entity.getLatestSnapshot().getInstance(0); + const auto description = robot::convertRobotDescription(entityInstance); + if (description) { - for (const auto& [name, entity] : provSeg.entities()) - { - const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); - const auto description = robot::convertRobotDescription(entityInstance); - - if (description) - { - ARMARX_DEBUG << "Key is " << armem::MemoryID(entity.id()); - robotDescriptions.emplace(description->name, *description); - } - else - { - ARMARX_WARNING << "Could not convert entity instance to 'RobotDescription'"; - } - } + ARMARX_DEBUG << "Key is " << armem::MemoryID(entity.id()); + robotDescriptions.emplace(description->name, *description); } - } + else + { + ARMARX_WARNING << "Could not convert entity instance to 'RobotDescription'"; + } + }); ARMARX_INFO << deactivateSpam(60) << "Number of known robot descriptions: " << robotDescriptions.size(); return robotDescriptions; diff --git a/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.h b/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.h index c62821c4afacbf6caa87b5540a4c05c778254f25..40eac57706b1cfc14d2cd823cda222d673f958c2 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.h @@ -28,12 +28,9 @@ #include <unordered_map> // ArmarX -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> - #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> -#include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/server/segment/Segment.h> -#include <RobotAPI/libraries/armem_objects/types.h> +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> // Aron #include <RobotAPI/libraries/armem_robot/aron/RobotDescription.aron.generated.h> @@ -41,6 +38,7 @@ namespace armarx::armem::server::robot_state::description { + class Segment : public segment::wm::AronTypedCoreSegmentBase<arondto::RobotDescription> { using Base = segment::wm::AronTypedCoreSegmentBase<arondto::RobotDescription>; @@ -54,9 +52,6 @@ namespace armarx::armem::server::robot_state::description void onConnect(const RobotUnitInterfacePrx& robotUnitPrx); - /// mapping "robot name" -> "robot description" - using RobotDescriptionMap = std::unordered_map<std::string, robot::RobotDescription>; - RobotDescriptionMap getRobotDescriptions(const armem::Time& timestamp) const; RobotDescriptionMap getRobotDescriptionsLocking(const armem::Time& timestamp) const; diff --git a/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h b/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h new file mode 100644 index 0000000000000000000000000000000000000000..e730e29f1d5c8e83f38701a2e26cfc7a55b23039 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h @@ -0,0 +1,80 @@ +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <map> +#include <string> +#include <unordered_map> +#include <vector> + +#include <Eigen/Geometry> + + + +namespace armarx::armem::arondto +{ + struct Transform; + struct TransformHeader; + + struct JointState; + + class RobotDescription; +} + +namespace armarx::armem::robot +{ + struct RobotDescription; + struct RobotState; + struct Robot; + + using Robots = std::vector<Robot>; + using RobotDescriptions = std::vector<RobotDescription>; + using RobotStates = std::vector<RobotState>; +} + +namespace armarx::armem::robot_state +{ + struct JointState; + + struct Transform; + struct TransformHeader; +} + +namespace armarx::armem::server::robot_state::description +{ + using RobotDescriptionMap = std::unordered_map<std::string, robot::RobotDescription>; + class Segment; +} + +namespace armarx::armem::server::robot_state::localization +{ + using RobotPoseMap = std::unordered_map<std::string, Eigen::Affine3f>; + using RobotFramePoseMap = std::unordered_map<std::string, std::vector<Eigen::Affine3f>>; + class Segment; +} + +namespace armarx::armem::server::robot_state::proprioception +{ + using RobotJointPositionMap = std::unordered_map<std::string, std::map<std::string, float>>; + class Segment; +} + diff --git a/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.cpp b/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.cpp index 6f7dc4c14db6da83691331063ff273cd5ddc4158..1a73d571d47912809e8f5eb63b20cff5e9678020 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.cpp @@ -2,37 +2,22 @@ // STL #include <iterator> -#include <sstream> -// Ice -#include <IceUtil/Time.h> - -// Eigen -#include <Eigen/Core> -#include <Eigen/Geometry> - -#include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> -#include <ArmarXCore/core/application/properties/PluginAll.h> #include <ArmarXCore/core/logging/Logging.h> -#include <ArmarXCore/core/time/TimeUtil.h> #include <RobotAPI/libraries/core/FramedPose.h> - #include <RobotAPI/libraries/aron/common/aron_conversions.h> -#include <RobotAPI/libraries/armem/client/Writer.h> -#include <RobotAPI/libraries/armem/client/query/Builder.h> -#include <RobotAPI/libraries/armem/client/query/query_fns.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/core/Time.h> #include <RobotAPI/libraries/armem/core/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/workingmemory/visitor.h> #include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h> #include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> #include <RobotAPI/libraries/armem_robot/robot_conversions.h> #include <RobotAPI/libraries/armem_robot_state/aron_conversions.h> +#include <RobotAPI/libraries/armem_robot_state/types.h> #include <RobotAPI/libraries/armem_robot_state/common/localization/TransformHelper.h> #include <RobotAPI/libraries/armem_robot_state/common/localization/types.h> @@ -46,32 +31,44 @@ namespace armarx::armem::server::robot_state::localization } - Segment::~Segment() = default; + Segment::~Segment() + { + } + + + void Segment::onConnect() + { + } - Segment::RobotFramePoseMap + RobotFramePoseMap Segment::getRobotFramePosesLocking(const armem::Time& timestamp) const { - std::scoped_lock lock(mutex()); - return getRobotFramePoses(timestamp); + return this->doLocked([this, ×tamp]() + { + return getRobotFramePoses(timestamp); + }); } - Segment::RobotFramePoseMap + RobotFramePoseMap Segment::getRobotFramePoses(const armem::Time& timestamp) const { using common::robot_state::localization::TransformHelper; using common::robot_state::localization::TransformQuery; RobotFramePoseMap frames; - - const auto knownRobots = simox::alg::get_keys(segment->container()); - for (const auto& robotName : knownRobots) + for (const std::string& robotName : segment->getProviderSegmentNames()) { - TransformQuery query{.header{.parentFrame = GlobalFrame, - .frame = "root", // TODO, FIXME - .agent = robotName, - .timestamp = timestamp}}; + TransformQuery query + { + .header = { + .parentFrame = armarx::GlobalFrame, + .frame = "root", // TODO, FIXME + .agent = robotName, + .timestamp = timestamp + } + }; const auto result = TransformHelper::lookupTransformChain(*segment, query); if (not result) @@ -90,40 +87,44 @@ namespace armarx::armem::server::robot_state::localization } - Segment::RobotPoseMap + RobotPoseMap Segment::getRobotGlobalPosesLocking(const armem::Time& timestamp) const { - std::scoped_lock lock(mutex()); - return getRobotGlobalPoses(timestamp); + return this->doLocked([this, ×tamp]() + { + return getRobotGlobalPoses(timestamp); + }); } - Segment::RobotPoseMap + RobotPoseMap Segment::getRobotGlobalPoses(const armem::Time& timestamp) const { using common::robot_state::localization::TransformHelper; using common::robot_state::localization::TransformQuery; RobotPoseMap robotGlobalPoses; - - const std::vector<std::string> knownRobots = simox::alg::get_keys(segment->container()); - - for (const std::string& robotName : knownRobots) + for (const std::string& robotName : segment->getProviderSegmentNames()) { - - TransformQuery query{.header{.parentFrame = GlobalFrame, - .frame = "root", // TODO, FIXME - .agent = robotName, - .timestamp = timestamp}}; - - const auto result = TransformHelper::lookupTransform(*segment, query); - if (not result) + TransformQuery query + { + .header = + { + .parentFrame = GlobalFrame, + .frame = "root", // TODO, FIXME + .agent = robotName, + .timestamp = timestamp + } + }; + + if (const auto result = TransformHelper::lookupTransform(*segment, query)) + { + robotGlobalPoses.emplace(robotName, result.transform.transform); + } + else { // TODO - continue; } - - robotGlobalPoses.emplace(robotName, result.transform.transform); } ARMARX_INFO << deactivateSpam(60) diff --git a/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.h b/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.h index 53d11236231f9e6e7017f4de984c5ad4da97e6c2..f0b228800b66426da38391ebf60c57d290926959 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/localization/Segment.h @@ -30,20 +30,13 @@ #include <Eigen/Geometry> // ArmarX -#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/forward_declarations.h> #include <RobotAPI/libraries/armem/server/segment/Segment.h> -#include <RobotAPI/libraries/armem_objects/types.h> -#include <RobotAPI/libraries/armem_robot_state/types.h> -// Aron #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h> +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> -namespace armarx::armem -{ - class EntityUpdate; -} // namespace armarx::armem - namespace armarx::armem::server::robot_state::localization { class Segment : public segment::wm::AronTypedCoreSegmentBase<arondto::Transform> @@ -52,14 +45,12 @@ namespace armarx::armem::server::robot_state::localization public: - using RobotPoseMap = std::unordered_map<std::string, Eigen::Affine3f>; - using RobotFramePoseMap = std::unordered_map<std::string, std::vector<Eigen::Affine3f>>; + Segment(server::MemoryToIceAdapter& iceMemory); + virtual ~Segment() override; - public: + void onConnect(); - Segment(server::MemoryToIceAdapter& iceMemory); - virtual ~Segment() override; RobotPoseMap getRobotGlobalPoses(const armem::Time& timestamp) const; RobotPoseMap getRobotGlobalPosesLocking(const armem::Time& timestamp) const; @@ -67,13 +58,13 @@ namespace armarx::armem::server::robot_state::localization RobotFramePoseMap getRobotFramePoses(const armem::Time& timestamp) const; RobotFramePoseMap getRobotFramePosesLocking(const armem::Time& timestamp) const; - bool commitTransform(const armarx::armem::robot_state::Transform& transform); - bool commitTransformLocking(const armarx::armem::robot_state::Transform& transform); + bool commitTransform(const armem::robot_state::Transform& transform); + bool commitTransformLocking(const armem::robot_state::Transform& transform); private: - EntityUpdate makeUpdate(const armarx::armem::robot_state::Transform& transform) const; + EntityUpdate makeUpdate(const armem::robot_state::Transform& transform) const; }; diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp similarity index 50% rename from source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.cpp rename to source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp index 39439e2e77bbd8c3ebea44782ae44d62286c6ed9..c2ed636a93e6a1b8feb9331daf68812160a44ba8 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp @@ -39,20 +39,18 @@ #include <RobotAPI/libraries/armem/core/ice_conversions.h> #include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h> #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h> +#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/server/localization/Segment.h> -namespace armarx::armem::server::robot_state +namespace armarx::armem::server::robot_state::proprioception { void RobotStateWriter::connect(armarx::plugins::DebugObserverComponentPlugin& debugObserver) { - { - // Thread-local copy of debug observer helper. - this->debugObserver = DebugObserverHelper( - Logging::tag.tagName, debugObserver.getDebugObserver(), true - ); - } + // Thread-local copy of debug observer helper. + this->debugObserver = + DebugObserverHelper(Logging::tag.tagName, debugObserver.getDebugObserver(), true); } @@ -65,9 +63,12 @@ namespace armarx::armem::server::robot_state } - void RobotStateWriter::run(float pollFrequency, - std::queue<RobotUnitData>& dataQueue, std::mutex& dataMutex, - MemoryToIceAdapter& memory, localization::Segment& localizationSegment) + void RobotStateWriter::run( + float pollFrequency, + std::queue<RobotUnitData>& dataQueue, + std::mutex& dataMutex, + MemoryToIceAdapter& memory, + localization::Segment& localizationSegment) { CycleUtil cycle(static_cast<int>(1000.f / pollFrequency)); while (task and not task->isStopped()) @@ -86,34 +87,42 @@ namespace armarx::armem::server::robot_state { debugObserver->setDebugObserverDatafield("RobotStateWriter | Queue Size", queueSize); } - if (batch.size() > 0) + if (not batch.empty()) { - Update update = buildUpdate(batch); - auto start = std::chrono::high_resolution_clock::now(); - auto endProprioception = start, endLocalization = start; + auto endBuildUpdate = start, endProprioception = start, endLocalization = start; - armem::CommitResult result; - { - // Commits lock the core segments. + Update update = buildUpdate(batch); + endBuildUpdate = std::chrono::high_resolution_clock::now(); + + // Commits lock the core segments. - result = memory.commitLocking(update.proprioception); - endProprioception = std::chrono::high_resolution_clock::now(); + // Proprioception + armem::CommitResult result = memory.commitLocking(update.proprioception); + endProprioception = std::chrono::high_resolution_clock::now(); - localizationSegment.commitTransformLocking(update.localization); - endLocalization = std::chrono::high_resolution_clock::now(); + // Localization + for (const armem::robot_state::Transform& transform : update.localization) + { + localizationSegment.doLocked([&localizationSegment, &transform]() + { + localizationSegment.commitTransform(transform); + }); } + endLocalization = std::chrono::high_resolution_clock::now(); + if (not result.allSuccess()) { - ARMARX_WARNING << "Could not add data to memory. Error message: " << result.allErrorMessages(); + ARMARX_WARNING << "Could not commit data to memory. Error message: " << result.allErrorMessages(); } if (debugObserver) { auto end = std::chrono::high_resolution_clock::now(); - debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit [ms]", toDurationMs(start, end)); - debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit Proprioception [ms]", toDurationMs(start, endProprioception)); - debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit Localization [ms]", toDurationMs(endProprioception, endLocalization)); + debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit (ms)", toDurationMs(start, end)); + debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit 1. Build Update (ms)", toDurationMs(start, endBuildUpdate)); + debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit 2. Proprioception (ms)", toDurationMs(endBuildUpdate, endProprioception)); + debugObserver->setDebugObserverDatafield("RobotStateWriter | t Commit 3. Localization (ms)", toDurationMs(endProprioception, endLocalization)); } } @@ -126,73 +135,73 @@ namespace armarx::armem::server::robot_state } - RobotStateWriter::Update RobotStateWriter::buildUpdate(std::queue<RobotUnitData> dataQueue) + RobotStateWriter::Update RobotStateWriter::buildUpdate(std::queue<RobotUnitData>& dataQueue) { ARMARX_CHECK_GREATER(dataQueue.size(), 0); - ARMARX_DEBUG << "RobotStateWriter: Commit batch of " << dataQueue.size() << " timesteps to memory..."; - auto start = std::chrono::high_resolution_clock::now(); // Send batch to memory Update update; while (dataQueue.size() > 0) { - const RobotUnitData& convertedAndGroupedData = dataQueue.front(); + const RobotUnitData& data = dataQueue.front(); - for (const auto& [encName, encTimestep] : convertedAndGroupedData.groups) { - ARMARX_CHECK_EQUAL(encName, encTimestep.name); - armem::EntityUpdate& up = update.proprioception.add(); - up.entityID = properties.robotUnitProviderID.withEntityName(encName); - up.timeCreated = encTimestep.timestamp; - up.instancesData = { encTimestep.data }; + up.entityID = properties.robotUnitProviderID.withEntityName(properties.robotUnitProviderID.providerSegmentName); + up.timeCreated = data.timestamp; + up.instancesData = { data.proprioception }; } - // odometry pose -> localization segment - auto it = convertedAndGroupedData.groups.find(properties.platformGroupName); - if (it != convertedAndGroupedData.groups.end()) + // Extract odometry data. + const std::string platformKey = "platform"; + if (data.proprioception->hasElement(platformKey)) { ARMARX_DEBUG << "Found odometry data."; - - const RobotUnitData::RobotUnitDataGroup& timestep = it->second; - const float relPosX = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionX"))->getValue(); - const float relPosY = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionY"))->getValue(); - const float relOrientation = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionRotation"))->getValue(); - - Eigen::Affine3f odometryPose = Eigen::Affine3f::Identity(); - odometryPose.translation() << relPosX, relPosY, 0; // TODO set height - odometryPose.linear() = simox::math::rpy_to_mat3f(0.F, 0.F, relOrientation); - - // const auto timestamp = armem::Time::microSeconds(it->second.timestamp); - - armem::robot_state::Transform& transform = update.localization; - transform.header.parentFrame = armarx::OdometryFrame; - transform.header.frame = "root"; // TODO: robot root node - transform.header.agent = properties.robotUnitProviderID.providerSegmentName; - transform.header.timestamp = it->second.timestamp; - transform.transform = odometryPose; + auto platformData = aron::datanavigator::DictNavigator::DynamicCastAndCheck(data.proprioception->getElement(platformKey)); + update.localization.emplace_back(getTransform(platformData, data.timestamp)); } - else if (!noOdometryDataLogged) + else { - ARMARX_INFO << "No odometry data! (No group with name '" << properties.platformGroupName << "'.). \n" - << "If you are using a mobile platform this should not have happened."; + ARMARX_INFO << "No odometry data! " + << "(No element '" << platformKey << "' in proprioception data.)" + << "\nIf you are using a mobile platform this should not have happened." + << "\nThis error is only logged once." + << "\nThese keys exist: " << data.proprioception->getAllKeys() + ; noOdometryDataLogged = true; } dataQueue.pop(); } - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start); - ARMARX_DEBUG << "RobotUnitData: The total runtime of sending a batch to the memory is: " << duration; - if (debugObserver) - { - debugObserver->setDebugObserverDatafield("RobotStateWriter | t Build Update [ms]", duration.count() / 1000.f); - } - return update; } + + armem::robot_state::Transform + RobotStateWriter::getTransform( + const aron::datanavigator::DictNavigatorPtr& platformData, + const Time& timestamp) const + { + prop::arondto::Platform platform; + platform.fromAron(platformData); + + const Eigen::Vector3f& relPose = platform.relativePosition; + + Eigen::Affine3f odometryPose = Eigen::Affine3f::Identity(); + odometryPose.translation() << relPose.x(), relPose.y(), 0; // TODO set height + odometryPose.linear() = simox::math::rpy_to_mat3f(0.f, 0.f, relPose.z()); + + armem::robot_state::Transform transform; + transform.header.parentFrame = armarx::OdometryFrame; + transform.header.frame = "root"; // TODO: robot root node + transform.header.agent = properties.robotUnitProviderID.providerSegmentName; + transform.header.timestamp = timestamp; + transform.transform = odometryPose; + + return transform; + } + } diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h similarity index 86% rename from source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.h rename to source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h index 93ba74d2d9d622f4d72d1d5a4d08acd97f3eb64b..10c5a8b9e4c52c2a45198d62f7661cda042ba8a4 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateWriter.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h @@ -48,9 +48,8 @@ namespace armarx::armem::server::robot_state::localization { class Segment; } -namespace armarx::armem::server::robot_state +namespace armarx::armem::server::robot_state::proprioception { - class RobotStateWriter : public armarx::Logging { public: @@ -68,9 +67,17 @@ namespace armarx::armem::server::robot_state struct Update { armem::Commit proprioception; - armem::robot_state::Transform localization; + std::vector<armem::robot_state::Transform> localization; }; - Update buildUpdate(std::queue<RobotUnitData> dataQueue); + Update buildUpdate(std::queue<RobotUnitData>& dataQueue); + + + private: + + armem::robot_state::Transform + getTransform( + const aron::datanavigator::DictNavigatorPtr& platformData, + const Time& timestamp) const; public: @@ -79,7 +86,6 @@ namespace armarx::armem::server::robot_state { unsigned int memoryBatchSize = 50; armem::MemoryID robotUnitProviderID; - std::string platformGroupName = "sens.Platform"; }; Properties properties; diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.cpp similarity index 63% rename from source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.cpp rename to source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.cpp index 9a9b72e2a74203cb9f44dd54796b397770257ecd..4262bf76e634ff03445a7460cd4fd55a4df861d9 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitData.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.cpp @@ -3,7 +3,7 @@ #include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> -namespace armarx::armem::server::robot_state +namespace armarx::armem::server::robot_state::proprioception { } diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h new file mode 100644 index 0000000000000000000000000000000000000000..4045742af416c96ad2d101d98f4559d8cef1dce9 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h @@ -0,0 +1,22 @@ +#pragma once + +#include <memory> + +#include <RobotAPI/libraries/armem/core/Time.h> + + +namespace armarx::aron::datanavigator +{ + using DictNavigatorPtr = std::shared_ptr<class DictNavigator>; +} +namespace armarx::armem::server::robot_state::proprioception +{ + + struct RobotUnitData + { + Time timestamp; + aron::datanavigator::DictNavigatorPtr proprioception; + }; + +} + diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4fb42da5e5fa804fad70b3978766323a487ad1fb --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp @@ -0,0 +1,128 @@ +#include "RobotUnitReader.h" + +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.h> +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.h> +#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> + +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> +#include <RobotAPI/libraries/aron/core/navigator/data/primitive/Long.h> +#include <RobotAPI/libraries/aron/core/navigator/data/primitive/String.h> + +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> + +#include <ArmarXCore/core/time/CycleUtil.h> +#include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h> + +#include <SimoxUtility/algorithm/string/string_tools.h> + +#include <istream> +#include <filesystem> +#include <fstream> + + +namespace armarx::armem::server::robot_state::proprioception +{ + + RobotUnitReader::RobotUnitReader() + { + } + + + void RobotUnitReader::connect( + armarx::plugins::RobotUnitComponentPlugin& robotUnitPlugin, + armarx::plugins::DebugObserverComponentPlugin& debugObserverPlugin, + const std::string& robotTypeName) + { + { + converter = converterRegistry.get(robotTypeName); + ARMARX_CHECK_NOT_NULL(converter) + << "No converter for robot type '" << robotTypeName << "' available. \n" + << "Known are: " << converterRegistry.getKeys(); + + config.loggingNames.push_back(properties.sensorPrefix); + receiver = robotUnitPlugin.startDataSatreming(config); + description = receiver->getDataDescription(); + } + { + // Thread-local copy of debug observer helper. + debugObserver = + DebugObserverHelper(Logging::tag.tagName, debugObserverPlugin.getDebugObserver(), true); + } + } + + + void RobotUnitReader::run( + float pollFrequency, + std::queue<RobotUnitData>& dataQueue, + std::mutex& dataMutex) + { + CycleUtil cycle(static_cast<int>(1000.f / pollFrequency)); + while (task and not task->isStopped()) + { + if (std::optional<RobotUnitData> commit = fetchAndConvertLatestRobotUnitData()) + { + std::lock_guard g{dataMutex}; + dataQueue.push(std::move(commit.value())); + } + + if (debugObserver) + { + debugObserver->sendDebugObserverBatch(); + } + cycle.waitForCycleDuration(); + } + } + + + std::optional<RobotUnitData> RobotUnitReader::fetchAndConvertLatestRobotUnitData() + { + ARMARX_CHECK_NOT_NULL(converter); + + const std::optional<RobotUnitDataStreaming::TimeStep> data = fetchLatestData(); + if (not data.has_value()) + { + return std::nullopt; + } + + ARMARX_DEBUG << "RobotUnitReader: Converting data current timestep to commit"; + auto start = std::chrono::high_resolution_clock::now(); + + RobotUnitData result; + result.proprioception = converter->convert(data.value(), description); + result.timestamp = Time::microSeconds(data->timestampUSec); + + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast<std::chrono::microseconds>(stop - start); + ARMARX_DEBUG << "RobotUnitReader: The total time needed to convert the data to commit: " << duration; + + if (debugObserver) + { + debugObserver->setDebugObserverDatafield("RobotUnitReader | t Read+Group [ms]", duration.count() / 1000.f); + } + + return result; + } + + + std::optional<RobotUnitDataStreaming::TimeStep> RobotUnitReader::fetchLatestData() + { + std::deque<RobotUnitDataStreaming::TimeStep>& data = receiver->getDataBuffer(); + if (debugObserver) + { + debugObserver->setDebugObserverDatafield("RobotUnitReader | Buffer Size", data.size()); + } + if (data.empty()) + { + return std::nullopt; + } + else + { + RobotUnitDataStreaming::TimeStep currentTimestep = data.back(); + data.clear(); + return currentTimestep; + } + } + + +} + diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h similarity index 70% rename from source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.h rename to source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h index a44c9c0f19a586f48b3f899ae314ca70cc223abe..efb7a0d8175de36ebf963fd035d176442df822f2 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotUnitReader.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h @@ -10,11 +10,12 @@ #include <ArmarXCore/core/services/tasks/TaskUtil.h> #include <ArmarXCore/libraries/DebugObserverHelper/DebugObserverHelper.h> - #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> #include <RobotAPI/libraries/RobotAPIComponentPlugins/RobotUnitComponentPlugin.h> #include "RobotUnitData.h" +#include "converters/ConverterRegistry.h" +#include "converters/ConverterInterface.h" namespace armarx::plugins @@ -27,29 +28,28 @@ namespace armarx using RobotUnitDataStreamingReceiverPtr = std::shared_ptr<class RobotUnitDataStreamingReceiver>; } -namespace armarx::armem::server::robot_state +namespace armarx::armem::server::robot_state::proprioception { - class RobotUnitReader : public armarx::Logging { public: - static std::map<std::string, std::string> readConfig(const std::string& configPath); + RobotUnitReader(); - public: - - void connect(armarx::plugins::RobotUnitComponentPlugin& robotUnitPlugin, - armarx::plugins::DebugObserverComponentPlugin& debugObserverPlugin); + void connect( + armarx::plugins::RobotUnitComponentPlugin& robotUnitPlugin, + armarx::plugins::DebugObserverComponentPlugin& debugObserverPlugin, + const std::string& robotTypeName); - /// Reads data from `handler` and fills `robotUnitDataQueue`. + /// Reads data from `handler` and fills `dataQueue`. void run(float pollFrequency, std::queue<RobotUnitData>& dataQueue, std::mutex& dataMutex); - std::optional<RobotUnitData> fetchAndGroupLatestRobotUnitData(); + std::optional<RobotUnitData> fetchAndConvertLatestRobotUnitData(); private: @@ -73,8 +73,8 @@ namespace armarx::armem::server::robot_state RobotUnitDataStreamingReceiverPtr receiver; RobotUnitDataStreaming::DataStreamingDescription description; - /// RobotUnit name -> group name. - std::map<std::string, std::string> configSensorMapping; + ConverterRegistry converterRegistry; + ConverterInterface* converter = nullptr; armarx::SimpleRunningTask<>::pointer_type task = nullptr; diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.cpp index de880c85df95dd8e0238defcce0a1831ddbbc0c9..02a98ef640f4cf4b2dc0f6166c18fd3c5ccbc5bc 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.cpp @@ -1,36 +1,22 @@ #include "Segment.h" -#include <sstream> - #include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/time/TimeUtil.h> #include <ArmarXCore/libraries/DebugObserverHelper/DebugObserverHelper.h> +#include <ArmarXCore/observers/ObserverObjectFactories.h> -#include <RobotAPI/libraries/aron/common/aron_conversions.h> -#include <RobotAPI/libraries/armem_robot_state/types.h> - -#include <RobotAPI/libraries/armem/core/aron_conversions.h> -#include <RobotAPI/libraries/armem/core/workingmemory/visitor.h> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> -#include <RobotAPI/libraries/armem/client/Writer.h> -#include <RobotAPI/libraries/armem/client/query/Builder.h> -#include <RobotAPI/libraries/armem/client/query/query_fns.h> -#include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h> -#include <RobotAPI/libraries/armem/util/util.h> - -#include <RobotAPI/libraries/armem_robot/robot_conversions.h> -#include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> - -#include <RobotAPI/libraries/armem_objects/aron_conversions.h> - +#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> namespace armarx::armem::server::robot_state::proprioception { Segment::Segment(armem::server::MemoryToIceAdapter& memoryToIceAdapter) : - Base(memoryToIceAdapter, "Proprioception", nullptr, 1024) + Base(memoryToIceAdapter, "Proprioception", arondto::Proprioception::toAronType(), 1024) { } @@ -39,8 +25,6 @@ namespace armarx::armem::server::robot_state::proprioception void Segment::onConnect(RobotUnitInterfacePrx robotUnitPrx) { - Base::onConnect(); - this->robotUnit = robotUnitPrx; std::string providerSegmentName = "Robot"; @@ -59,22 +43,31 @@ namespace armarx::armem::server::robot_state::proprioception - Segment::RobotJointPositionMap Segment::getRobotJointPositionsLocking( + RobotJointPositionMap Segment::getRobotJointPositionsLocking( const armem::Time& timestamp, DebugObserverHelper* debugObserver) const { - TIMING_START(tRobotJointPositionsLock); - std::scoped_lock lock(mutex()); - TIMING_END_STREAM(tRobotJointPositionsLock, ARMARX_DEBUG); - if (debugObserver) + return doLocked([this, ×tamp, &debugObserver]() + { + return getRobotJointPositions(timestamp, debugObserver); + }); + } + + + static + aron::datanavigator::DictNavigatorPtr + getDictElement(const aron::datanavigator::DictNavigator& dict, const std::string& key) + { + if (dict.hasElement(key)) { - debugObserver->setDebugObserverDatafield(dp + "t 0 Lock Core Segment (ms)", tRobotJointPositionsLock.toMilliSecondsDouble()); + return aron::datanavigator::DictNavigator::DynamicCastAndCheck(dict.getElement(key)); } - return getRobotJointPositions(timestamp, debugObserver); + return nullptr; } - Segment::RobotJointPositionMap Segment::getRobotJointPositions( + RobotJointPositionMap + Segment::getRobotJointPositions( const armem::Time& timestamp, DebugObserverHelper* debugObserver) const { @@ -82,76 +75,48 @@ namespace armarx::armem::server::robot_state::proprioception ARMARX_CHECK_NOT_NULL(segment); RobotJointPositionMap jointMap; + int i = 0; - TIMING_START(tProcessProviders); - for (const auto& [robotName, provSeg] : *segment) + Duration tFindData = Duration::milliSeconds(0), tReadJointPositions = Duration::milliSeconds(0); + TIMING_START(tProcessEntities) + segment->forEachEntity([&](const wm::Entity & entity) { - TIMING_START(tProcessEntities); - int i = 0; - for (const auto& [name, entity] : provSeg.entities()) + adn::DictNavigatorPtr data; { - TIMING_START(tProcessEntity); - - TIMING_START(tGetLatestInstance); - if (entity.empty()) - { - continue; - } - const wm::EntitySnapshot& snapshot = entity.getLatestSnapshot(); - if (snapshot.empty()) - { - continue; - } - const wm::EntityInstance& entityInstance = snapshot.getInstance(0); - TIMING_END_COMMENT_STREAM(tGetLatestInstance, "tGetLatestInstance " + std::to_string(i), ARMARX_DEBUG); - - TIMING_START(tCastJointState); - aron::datanavigator::DictNavigatorPtr data = entityInstance.data(); - // Avoid throwing an exception. - if (!(data->hasElement("name") && data->hasElement("position"))) - { - continue; - } - const std::string jointName = adn::StringNavigator::DynamicCastAndCheck(data->getElement("name"))->getValue(); - const float jointPosition = adn::FloatNavigator::DynamicCastAndCheck(data->getElement("position"))->getValue(); - TIMING_END_COMMENT_STREAM(tCastJointState, "tCastJointState " + std::to_string(i), ARMARX_DEBUG); + TIMING_START(_tFindData) - TIMING_START(tEmplace); - if (not jointName.empty()) + const wm::EntitySnapshot* snapshot = entity.findLatestSnapshotBeforeOrAt(timestamp); + if (not snapshot) { - jointMap[robotName].emplace(jointName, jointPosition); + // Got no snapshot <= timestamp. Take latest instead (if present). + snapshot = entity.findLatestSnapshot(); } - else + if (snapshot) { - // This may happen, just ignore it. - // ARMARX_WARNING << "Could not convert entity instance to 'JointState'"; + data = snapshot->findInstanceData(); } - TIMING_END_COMMENT_STREAM(tEmplace, "tEmplace " + std::to_string(i), ARMARX_DEBUG); - TIMING_END_COMMENT_STREAM(tProcessEntity, "tProcessEntity " + std::to_string(i), ARMARX_DEBUG); - - if (debugObserver) - { - debugObserver->setDebugObserverDatafield(dp + "t 1.1.1.1 GetLatestInstance (ms)", tGetLatestInstance.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(dp + "t 1.1.1.2 CastJointState (ms)", tCastJointState.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(dp + "t 1.1.1.3 Emplace (ms)", tEmplace.toMilliSecondsDouble()); - debugObserver->setDebugObserverDatafield(dp + "t 1.1.1.T Process Entity (ms)", tProcessEntity.toMilliSecondsDouble()); - } - ++i; + TIMING_END_COMMENT_STREAM(_tFindData, "tFindData " + std::to_string(i), ARMARX_DEBUG) + tFindData += _tFindData; } - TIMING_END_STREAM(tProcessEntities, ARMARX_DEBUG); - if (debugObserver) + if (data) { - debugObserver->setDebugObserverDatafield(dp + "t 1.1 Process Entities (ms)", tProcessEntities.toMilliSecondsDouble()); - } - } - TIMING_END_STREAM(tProcessProviders, ARMARX_DEBUG); + TIMING_START(_tReadJointPositions) - ARMARX_INFO << deactivateSpam(60) << "Number of known robot joint maps: " << jointMap.size(); + jointMap.emplace(entity.id().providerSegmentName, readJointPositions(*data)); + + TIMING_END_COMMENT_STREAM(_tReadJointPositions, "tReadJointPositions " + std::to_string(i), ARMARX_DEBUG) + tReadJointPositions += _tReadJointPositions; + } + ++i; + }); + TIMING_END_STREAM(tProcessEntities, ARMARX_DEBUG) if (debugObserver) { - debugObserver->setDebugObserverDatafield(dp + "t 1 Process Providers (ms)", tProcessProviders.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(dp + "t 1.1 Process Entities (ms)", tProcessEntities.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(dp + "t 1.1.1 FindData (ms)", tFindData.toMilliSecondsDouble()); + debugObserver->setDebugObserverDatafield(dp + "t 1.1.2 ReadJointPositions (ms)", tReadJointPositions.toMilliSecondsDouble()); } return jointMap; @@ -163,4 +128,26 @@ namespace armarx::armem::server::robot_state::proprioception return robotUnitProviderID; } + + std::map<std::string, float> + Segment::readJointPositions(const wm::EntityInstanceData& data) + { + namespace adn = aron::datanavigator; + + // Just get what we need without casting the whole data. + std::map<std::string, float> jointPositions; + if (adn::DictNavigatorPtr joints = getDictElement(data, "joints")) + { + if (adn::DictNavigatorPtr jointsPosition = getDictElement(*joints, "position")) + { + for (const auto& [name, value] : jointsPosition->getElements()) + { + const float jointPosition = adn::FloatNavigator::DynamicCastAndCheck(value)->getValue(); + jointPositions[name] = jointPosition; + } + } + } + return jointPositions; + } + } // namespace armarx::armem::server::robot_state::proprioception diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h index cca0b35a0e179a15880bc38a08a1f1f20ce9f42f..e27984bba26136defa5e2ef2b940790abe44f3f7 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h @@ -14,6 +14,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) * @date 2021 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License @@ -22,20 +23,18 @@ #pragma once // STD / STL -#include <string> +#include <map> #include <optional> -#include <unordered_map> +#include <string> // ArmarX #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> -#include <RobotAPI/components/ArViz/Client/Client.h> +// RobotAPI #include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/server/segment/Segment.h> -#include <RobotAPI/libraries/armem_objects/types.h> +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> -// Aron -#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> namespace armarx { @@ -47,10 +46,6 @@ namespace armarx::armem::server::robot_state::proprioception { using Base = segment::wm::CoreSegmentBase; - public: - - using RobotJointPositionMap = std::unordered_map<std::string, std::map<std::string, float>>; - public: Segment(server::MemoryToIceAdapter& iceMemory); @@ -67,6 +62,13 @@ namespace armarx::armem::server::robot_state::proprioception const armem::MemoryID& getRobotUnitProviderID() const; + private: + + static + std::map<std::string, float> + readJointPositions(const wm::EntityInstanceData& data); + + private: RobotUnitInterfacePrx robotUnit; diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.cpp similarity index 100% rename from source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.cpp rename to source/RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.cpp diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.h similarity index 100% rename from source/RobotAPI/components/armem/server/RobotStateMemory/aron_conversions.h rename to source/RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.h diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cb1192954cf52fedeec2040e161c72fae15479c6 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.cpp @@ -0,0 +1,212 @@ +#include "Armar6Converter.h" + +#include <SimoxUtility/algorithm/get_map_keys_values.h> +#include <SimoxUtility/algorithm/advanced.h> + +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> + +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.h> + +#include "ConverterTools.h" + + +namespace armarx::armem::server::robot_state::proprioception +{ + + Armar6Converter::Armar6Converter() : + tools(std::make_unique<ConverterTools>()) + { + } + + + Armar6Converter::~Armar6Converter() + { + } + + + aron::datanavigator::DictNavigatorPtr + Armar6Converter::convert( + const RobotUnitDataStreaming::TimeStep& data, + const RobotUnitDataStreaming::DataStreamingDescription& description) + { + arondto::Proprioception dto; + dto.iterationID = data.iterationId; + + for (const auto& [dataEntryName, dataEntry] : description.entries) + { + process(dto, dataEntryName, {data, dataEntry}); + } + return dto.toAron(); + } + + + void Armar6Converter::process( + arondto::Proprioception& dto, + const std::string& entryName, + const ConverterValue& value) + { + const std::vector<std::string> split = simox::alg::split(entryName, ".", false, false); + ARMARX_CHECK_GREATER_EQUAL(split.size(), 3); + const std::set<size_t> acceptedSizes{3, 4}; + ARMARX_CHECK_GREATER(acceptedSizes.count(split.size()), 0) + << "Data entry name could not be parsed (exected 3 or 4 components between '.'): " + << "\n- split: '" << split << "'"; + + const std::string& category = split.at(0); + const std::string& name = split.at(1); + const std::string& field = split.at(2); + ARMARX_CHECK_EQUAL(category, "sens") << category << " | " << entryName; + + if (name == "Platform") + { + // Platform + processPlatformEntry(dto.platform, field, value); + } + else if (simox::alg::starts_with(name, "FT")) + { + // Force Torque + processForceTorqueEntry(dto.forceTorque, split, value); + } + else + { + // Joint + bool processed = processJointEntry(dto.joints, split, value); + if (not processed) + { + // Fallback: Put in extra. + const std::vector<std::string> comps{simox::alg::advanced(split.begin(), 1), split.end()}; + const std::string key = simox::alg::join(comps, "."); + + switch (value.entry.type) + { + case RobotUnitDataStreaming::NodeTypeFloat: + dto.extraFloats[key] = getValueAs<float>(value); + break; + case RobotUnitDataStreaming::NodeTypeLong: + dto.extraLongs[key] = getValueAs<long>(value); + break; + default: + ARMARX_DEBUG << "Cannot handle extra field '" << key << "' of type " + << RobotUnitDataStreaming::DataEntryNames.to_name(value.entry.type); + break; + } + } + } + } + + + + void Armar6Converter::processPlatformEntry( + prop::arondto::Platform& dto, + const std::string& fieldName, + const ConverterValue& value) + { + if (findByPrefix(fieldName, tools->platformIgnored)) + { + return; + } + else if (auto getter = findByPrefix(fieldName, tools->platformPoseGetters)) + { + if (Eigen::Vector3f* dst = getter(dto)) + { + if (auto setter = findBySuffix(fieldName, tools->vector3fSetters)) + { + setter(*dst, getValueAs<float>(value)); + } + } + } + else + { + // No setter for this field. Put in extra. + dto.extra[fieldName] = getValueAs<float>(value); + } + } + + + void Armar6Converter::processForceTorqueEntry( + std::map<std::string, prop::arondto::ForceTorque>& fts, + const std::vector<std::string>& split, + const ConverterValue& value) + { + const std::string& name = split.at(1); + std::vector<std::string> splitName = simox::alg::split(name, " ", false, false); + ARMARX_CHECK_EQUAL(splitName.size(), 2); + ARMARX_CHECK_EQUAL(splitName.at(0), "FT"); + + auto it = tools->sidePrefixMap.find(splitName.at(1)); + ARMARX_CHECK(it != tools->sidePrefixMap.end()) << splitName.at(1); + + const std::string& side = it->second; + processForceTorqueEntry(fts[side], split, value); + } + + + void Armar6Converter::processForceTorqueEntry( + prop::arondto::ForceTorque& dto, + const std::vector<std::string>& split, + const ConverterValue& value) + { + const std::string& fieldName = split.at(2); + if (auto getter = findByPrefix(fieldName, tools->ftGetters)) + { + if (Eigen::Vector3f* dst = getter(dto)) + { + if (auto setter = findBySuffix(fieldName, tools->vector3fSetters)) + { + setter(*dst, getValueAs<float>(value)); + } + } + } + else + { + // No setter for this field. Put in extra. + std::string key = split.size() == 4 + ? (fieldName + "." + split.at(3)) + : fieldName; + dto.extra[key] = getValueAs<float>(value); + } + } + + + bool Armar6Converter::processJointEntry( + prop::arondto::Joints& dto, + const std::vector<std::string>& split, + const ConverterValue& value) + { + const std::string& jointName = split.at(1); + const std::string& fieldName = split.at(2); + if (false) + { + // Only in simulation. + if (auto getter = findByPrefix(fieldName, tools->jointGetters)) + { + if (std::map<std::string, float>* map = getter(dto)) + { + (*map)[jointName] = getValueAs<float>(value); + } + } + } + + const std::string tempSuffix = "Temperature"; + if (simox::alg::ends_with(split.at(2), tempSuffix)) + { + // Handle "dieTemperature" etc + const std::string name = split.at(2).substr(0, split.at(2).size() - tempSuffix.size()); + dto.temperature[split.at(1)][name] = getValueAs<float>(value); + return true; + } + else if (auto it = tools->jointSetters.find(fieldName); it != tools->jointSetters.end()) + { + const ConverterTools::JointSetter& setter = it->second; + setter(dto, split, value); + return true; + } + else + { + // ARMARX_DEBUG << "Ignoring unhandled field: '" << simox::alg::join(split, ".") << "'"; + return false; + } + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.h new file mode 100644 index 0000000000000000000000000000000000000000..03c0a05d0387cae6faba1a0a779ba874a3006730 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/Armar6Converter.h @@ -0,0 +1,69 @@ +#pragma once + +#include <map> +#include <string> + +#include <Eigen/Core> + +#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> + +#include "ConverterInterface.h" + + + +namespace armarx::armem::server::robot_state::proprioception +{ + struct ConverterValue; + class ConverterTools; + + + class Armar6Converter : public ConverterInterface + { + public: + + Armar6Converter(); + virtual ~Armar6Converter() override; + + + aron::datanavigator::DictNavigatorPtr + convert( + const RobotUnitDataStreaming::TimeStep& data, + const RobotUnitDataStreaming::DataStreamingDescription& description) override; + + + protected: + + void process(arondto::Proprioception& dto, const std::string& entryName, const ConverterValue& value); + + + + private: + + void processPlatformEntry( + prop::arondto::Platform& dto, + const std::string& fieldName, + const ConverterValue& value); + + void processForceTorqueEntry( + std::map<std::string, prop::arondto::ForceTorque>& fts, + const std::vector<std::string>& split, + const ConverterValue& value); + + void processForceTorqueEntry( + prop::arondto::ForceTorque& ft, + const std::vector<std::string>& split, + const ConverterValue& value); + + bool processJointEntry( + prop::arondto::Joints& dto, + const std::vector<std::string>& split, + const ConverterValue& value); + + + private: + + std::unique_ptr<ConverterTools> tools; + + }; +} + diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterInterface.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterInterface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8ddf012be951db403448f855966b026efe33ba8f --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterInterface.cpp @@ -0,0 +1,11 @@ +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::proprioception +{ + + ConverterInterface::~ConverterInterface() + { + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterInterface.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..1162623895611e89d69740f6bf7d014c325f1427 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterInterface.h @@ -0,0 +1,36 @@ +#pragma once + +#include <memory> + + +namespace armarx::RobotUnitDataStreaming +{ + struct TimeStep; + struct DataStreamingDescription; + struct DataEntry; +} +namespace armarx::armem::arondto +{ + class Proprioception; +} +namespace armarx::aron::datanavigator +{ + using DictNavigatorPtr = std::shared_ptr<class DictNavigator>; +} + +namespace armarx::armem::server::robot_state::proprioception +{ + class ConverterInterface + { + public: + + virtual ~ConverterInterface(); + + virtual + aron::datanavigator::DictNavigatorPtr convert( + const RobotUnitDataStreaming::TimeStep& data, + const RobotUnitDataStreaming::DataStreamingDescription& description) = 0; + + }; +} + diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterRegistry.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterRegistry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a04ca4cbab6b876b3eb2e8f146ca7858a35f0bc4 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterRegistry.cpp @@ -0,0 +1,30 @@ +#include "ConverterRegistry.h" + +#include "Armar6Converter.h" + +#include <SimoxUtility/algorithm/get_map_keys_values.h> + + +namespace armarx::armem::server::robot_state::proprioception +{ + + ConverterRegistry::ConverterRegistry() + { + add<Armar6Converter>("Armar6"); + } + + + ConverterInterface* + ConverterRegistry::get(const std::string& key) const + { + auto it = converters.find(key); + return it != converters.end() ? it->second.get() : nullptr; + } + + + std::vector<std::string> ConverterRegistry::getKeys() const + { + return simox::alg::get_keys(converters); + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterRegistry.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterRegistry.h new file mode 100644 index 0000000000000000000000000000000000000000..f62f808f6ee305f67aba8ba0bff24e414baaa802 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterRegistry.h @@ -0,0 +1,37 @@ +#pragma once + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::proprioception +{ + class ConverterRegistry + { + public: + + ConverterRegistry(); + + + template <class ConverterT, class ...Args> + void add(const std::string& key, Args... args) + { + converters[key].reset(new ConverterT(args...)); + } + + + ConverterInterface* get(const std::string& key) const; + std::vector<std::string> getKeys() const; + + + private: + + std::map<std::string, std::unique_ptr<ConverterInterface>> converters; + + }; +} + diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterTools.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterTools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7cc2fc128b55dc9af794804cc569d91b75ba7071 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterTools.cpp @@ -0,0 +1,179 @@ +#include "ConverterTools.h" + +#include <SimoxUtility/algorithm/get_map_keys_values.h> +#include <SimoxUtility/algorithm/advanced.h> + +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> +#include <RobotAPI/libraries/aron/core/navigator/data/container/Dict.h> + + +namespace armarx::armem::server::robot_state +{ + + std::optional<std::string> + proprioception::findByPrefix(const std::string& key, const std::set<std::string>& prefixes) + { + for (const auto& prefix : prefixes) + { + if (simox::alg::starts_with(key, prefix)) + { + return prefix; + } + } + return std::nullopt; + } +} + + +namespace armarx::armem::server::robot_state::proprioception +{ + ConverterTools::ConverterTools() + { + { + vector3fSetters["X"] = [](Eigen::Vector3f & v, float f) + { + v.x() = f; + }; + vector3fSetters["Y"] = [](Eigen::Vector3f & v, float f) + { + v.y() = f; + }; + vector3fSetters["Z"] = [](Eigen::Vector3f & v, float f) + { + v.z() = f; + }; + vector3fSetters["x"] = vector3fSetters["X"]; + vector3fSetters["y"] = vector3fSetters["Y"]; + vector3fSetters["z"] = vector3fSetters["Z"]; + vector3fSetters["Rotation"] = vector3fSetters["Z"]; + } + { + platformPoseGetters["acceleration"] = [](prop::arondto::Platform & p) + { + return &p.acceleration; + }; + platformPoseGetters["relativePosition"] = [](prop::arondto::Platform & p) + { + return &p.relativePosition; + }; + platformPoseGetters["velocity"] = [](prop::arondto::Platform & p) + { + return &p.velocity; + }; + platformIgnored.insert("absolutePosition"); + } + { + ftGetters["gravCompF"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.gravityCompensationForce; + }; + ftGetters["gravCompT"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.gravityCompensationTorque; + }; + ftGetters["f"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.force; + }; + ftGetters["t"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.torque; + }; + } + { + jointGetters["acceleration"] = [](prop::arondto::Joints & j) + { + return &j.acceleration; + }; + jointGetters["gravityTorque"] = [](prop::arondto::Joints & j) + { + return &j.gravityTorque; + }; + jointGetters["inertiaTorque"] = [](prop::arondto::Joints & j) + { + return &j.inertiaTorque; + }; + jointGetters["inverseDynamicsTorque"] = [](prop::arondto::Joints & j) + { + return &j.inverseDynamicsTorque; + }; + jointGetters["position"] = [](prop::arondto::Joints & j) + { + return &j.position; + }; + jointGetters["torque"] = [](prop::arondto::Joints & j) + { + return &j.torque; + }; + jointGetters["velocity"] = [](prop::arondto::Joints & j) + { + return &j.velocity; + }; + } + { + +#define ADD_SCALAR_SETTER(container, name, type) \ + container [ #name ] = []( \ + prop::arondto::Joints & dto, \ + const std::vector<std::string>& split, \ + const ConverterValue & value) \ + { \ + dto. name [split.at(1)] = getValueAs< type >(value); \ + } + + ADD_SCALAR_SETTER(jointSetters, position, float); + ADD_SCALAR_SETTER(jointSetters, velocity, float); + ADD_SCALAR_SETTER(jointSetters, acceleration, float); + + ADD_SCALAR_SETTER(jointSetters, relativePosition, float); + ADD_SCALAR_SETTER(jointSetters, filteredVelocity, float); + + ADD_SCALAR_SETTER(jointSetters, currentTarget, float); + ADD_SCALAR_SETTER(jointSetters, positionTarget, float); + ADD_SCALAR_SETTER(jointSetters, velocityTarget, float); + + ADD_SCALAR_SETTER(jointSetters, torque, float); + ADD_SCALAR_SETTER(jointSetters, inertiaTorque, float); + ADD_SCALAR_SETTER(jointSetters, gravityTorque, float); + ADD_SCALAR_SETTER(jointSetters, gravityCompensatedTorque, float); + ADD_SCALAR_SETTER(jointSetters, inverseDynamicsTorque, float); + ADD_SCALAR_SETTER(jointSetters, torqueTicks, int); + + ADD_SCALAR_SETTER(jointSetters, positionTarget, float); + ADD_SCALAR_SETTER(jointSetters, currentTarget, float); + ADD_SCALAR_SETTER(jointSetters, positionTarget, float); + + // "temperature" handled below + // ADD_SCALAR_SETTER(jointSetters, temperature, float); + + ADD_SCALAR_SETTER(jointSetters, motorCurrent, float); + ADD_SCALAR_SETTER(jointSetters, maxTargetCurrent, float); + + ADD_SCALAR_SETTER(jointSetters, sensorBoardUpdateRate, float); + ADD_SCALAR_SETTER(jointSetters, I2CUpdateRate, float); + + ADD_SCALAR_SETTER(jointSetters, JointStatusEmergencyStop, bool); + ADD_SCALAR_SETTER(jointSetters, JointStatusEnabled, bool); + ADD_SCALAR_SETTER(jointSetters, JointStatusError, int); + ADD_SCALAR_SETTER(jointSetters, JointStatusOperation, int); + + +#define ADD_VECTOR3_SETTER(container, name, type) \ + container [ #name ] = [this]( \ + prop::arondto::Joints & dto, \ + const std::vector<std::string>& split, \ + const ConverterValue & value) \ + { \ + auto& vec = dto. name [split.at(1)]; \ + auto& setter = this->vector3fSetters.at(split.at(3)); \ + setter(vec, getValueAs< type >(value)); \ + } + + ADD_VECTOR3_SETTER(jointSetters, angularVelocity, float); + ADD_VECTOR3_SETTER(jointSetters, linearAcceleration, float); + + // ADD_GETTER(jointVectorGetters, orientation, float); + } + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterTools.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterTools.h new file mode 100644 index 0000000000000000000000000000000000000000..ff667a1b578c074126e2a2bfb5db9100dafa964d --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/converters/ConverterTools.h @@ -0,0 +1,107 @@ +#pragma once + +#include <map> +#include <set> +#include <string> + +#include <Eigen/Core> + +#include <SimoxUtility/algorithm/string/string_tools.h> + +#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> + +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::proprioception +{ + + struct ConverterValue + { + const RobotUnitDataStreaming::TimeStep& data; + const RobotUnitDataStreaming::DataEntry& entry; + }; + + + template <class T> + T + getValueAs(const ConverterValue& value) + { + return RobotUnitDataStreamingReceiver::GetAs<T>(value.data, value.entry); + } + + + /** + * @brief Search + * @param key + * @param prefixes + * @return + */ + std::optional<std::string> + findByPrefix(const std::string& key, const std::set<std::string>& prefixes); + + + template <class ValueT> + ValueT + findByPrefix(const std::string& key, const std::map<std::string, ValueT>& map) + { + for (const auto& [prefix, value] : map) + { + if (simox::alg::starts_with(key, prefix)) + { + return value; + } + } + return nullptr; + } + + + template <class ValueT> + ValueT + findBySuffix(const std::string& key, const std::map<std::string, ValueT>& map) + { + for (const auto& [suffix, value] : map) + { + if (simox::alg::ends_with(key, suffix)) + { + return value; + } + } + return nullptr; + } + + + + class ConverterTools + { + public: + + ConverterTools(); + + + public: + + std::map<std::string, std::function<void(Eigen::Vector3f&, float)> > vector3fSetters; + + std::map<std::string, std::function<std::map<std::string, float>*(prop::arondto::Joints&)> > jointGetters; + std::map<std::string, std::function<std::map<std::string, Eigen::Vector3f>*(prop::arondto::Joints&)> > jointVectorGetters; + + using JointSetter = std::function<void(prop::arondto::Joints& dto, const std::vector<std::string>& split, const ConverterValue& value)>; + std::map<std::string, JointSetter> jointSetters; + + std::map<std::string, std::function<Eigen::Vector3f*(prop::arondto::Platform&)> > platformPoseGetters; + std::set<std::string> platformIgnored; + + std::map<std::string, std::function<Eigen::Vector3f*(prop::arondto::ForceTorque&)> > ftGetters; + + + std::map<std::string, std::string> sidePrefixMap + { + { "R", "Right" }, + { "L", "Left" }, + }; + + }; +} + diff --git a/source/RobotAPI/libraries/armem_robot_state/types.h b/source/RobotAPI/libraries/armem_robot_state/types.h index 39daba5d2d7db0148cb9df264d8da2acac54d036..b6d2b67dec2b28425e7b3dd4b0630fc39df02017 100644 --- a/source/RobotAPI/libraries/armem_robot_state/types.h +++ b/source/RobotAPI/libraries/armem_robot_state/types.h @@ -23,7 +23,8 @@ #include <Eigen/Geometry> -#include "RobotAPI/libraries/armem/core/Time.h" +#include <RobotAPI/libraries/armem/core/Time.h> + namespace armarx::armem::robot_state { @@ -34,7 +35,7 @@ namespace armarx::armem::robot_state std::string agent; - armem::Time timestamp; + armem::Time timestamp; }; struct Transform diff --git a/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp index 9c46b0c4729dfe6ca038c1f31ef81255f56d4968..4bc22306d7d26603d1edba2c8fc8f8aed40b1a8d 100644 --- a/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp +++ b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Reader.cpp @@ -33,22 +33,20 @@ #include <RobotAPI/libraries/aron/core/navigator/data/complex/NDArray.h> // RobotAPI Armem +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/util/util.h> + #include <RobotAPI/libraries/armem/client/Query.h> #include <RobotAPI/libraries/armem/client/Reader.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/selectors.h> -#include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Entity.h> -#include <RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h> -#include <RobotAPI/libraries/armem/util/util.h> #include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h> #include <RobotAPI/libraries/armem_vision/aron_conversions.h> #include <RobotAPI/libraries/armem_vision/types.h> + namespace armarx::armem::vision::laser_scans::client { @@ -124,11 +122,10 @@ namespace armarx::armem::vision::laser_scans::client } std::vector<LaserScanStamped> - asLaserScans(const std::map<std::string, wm::Entity>& entities) + asLaserScans(const wm::ProviderSegment& providerSegment) { std::vector<LaserScanStamped> outV; - - if (entities.empty()) + if (providerSegment.empty()) { ARMARX_WARNING << "No entities!"; } @@ -153,29 +150,25 @@ namespace armarx::armem::vision::laser_scans::client }; // loop over all entities and their snapshots - for (const auto& [s, entity] : entities) + providerSegment.forEachEntity([&outV, &convert](const wm::Entity & entity) { + // If we don't need this warning, we could directly iterate over the snapshots. if (entity.empty()) { - ARMARX_WARNING << "Empty history for " << s; + ARMARX_WARNING << "Empty history for " << entity.id(); } - ARMARX_DEBUG << "History size: " << entity.size(); - for (const auto& [ss, entitySnapshot] : entity) + entity.forEachInstance([&outV, &convert](const wm::EntityInstance & entityInstance) { - for (const auto& entityInstance : entitySnapshot.instances()) + if (const auto o = tryCast<arondto::LaserScanStamped>(entityInstance)) { - const auto o = - tryCast<arondto::LaserScanStamped>(entityInstance); - - if (o) - { - outV.push_back(convert(*o, entityInstance)); - } + outV.push_back(convert(*o, entityInstance)); } - } - } + return true; + }); + return true; + }); return outV; } @@ -202,13 +195,16 @@ namespace armarx::armem::vision::laser_scans::client } // now create result from memory - const auto& entities = - qResult.memory.getCoreSegment(properties.memoryName) - .getProviderSegment(query.agent) - .entities(); + const wm::ProviderSegment& providerSegment = + qResult.memory.getCoreSegment(properties.memoryName).getProviderSegment(query.agent); - const auto laserScans = asLaserScans(entities); - const auto sensors = simox::alg::get_keys(entities); + const auto laserScans = asLaserScans(providerSegment); + std::vector<std::string> sensors; + providerSegment.forEachEntity([&sensors](const wm::Entity & entity) + { + sensors.push_back(entity.name()); + return true; + }); return {.laserScans = laserScans, .sensors = sensors, diff --git a/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp index 7358e504ffecf3a2a710bd77b1631437e3818a42..861e02c4c9fcabbc40caebba3451b69e81e5016c 100644 --- a/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp +++ b/source/RobotAPI/libraries/armem_vision/client/laser_scans/Writer.cpp @@ -1,7 +1,7 @@ #include "Writer.h" #include <RobotAPI/libraries/armem/core/error.h> -#include "RobotAPI/libraries/armem_vision/aron_conversions.h" +#include <RobotAPI/libraries/armem_vision/aron_conversions.h> #include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h> diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp index ba7edf547d1d45873609d723b92934b18d7f5c88..717a2ec98e5283515a5454bfa31eb7c00636b01c 100644 --- a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp +++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.cpp @@ -19,13 +19,13 @@ #include <SimoxUtility/algorithm/get_map_keys_values.h> // ArmarXCore -#include "ArmarXCore/core/exceptions/LocalException.h" +#include <ArmarXCore/core/exceptions/LocalException.h> #include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <ArmarXCore/core/logging/LogSender.h> #include <ArmarXCore/core/logging/Logging.h> // RobotAPI Interfaces -#include "RobotAPI/libraries/aron/converter/eigen/EigenConverter.h" +#include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h> #include <RobotAPI/interface/armem/mns/MemoryNameSystemInterface.h> #include <RobotAPI/interface/armem/server/ReadingMemoryInterface.h> #include <RobotAPI/interface/units/LaserScannerUnit.h> @@ -40,11 +40,7 @@ #include <RobotAPI/libraries/armem/client/Reader.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/selectors.h> -#include <RobotAPI/libraries/armem/core/workingmemory/CoreSegment.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Entity.h> -#include <RobotAPI/libraries/armem/core/workingmemory/EntityInstance.h> -#include <RobotAPI/libraries/armem/core/workingmemory/Memory.h> -#include <RobotAPI/libraries/armem/core/workingmemory/ProviderSegment.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> #include <RobotAPI/libraries/armem/util/util.h> #include <RobotAPI/libraries/armem_vision/aron/LaserScan.aron.generated.h> #include <RobotAPI/libraries/armem_vision/aron_conversions.h> @@ -68,20 +64,23 @@ namespace armarx::armem::vision::occupancy_grid::client return qb; } - OccupancyGrid asOccupancyGrid(const std::map<std::string, wm::Entity>& entities) + OccupancyGrid asOccupancyGrid(const wm::ProviderSegment& providerSegment) { - ARMARX_CHECK(not entities.empty()) << "No entities"; - ARMARX_CHECK(entities.size() == 1) << "There should be only one entity!"; + ARMARX_CHECK(not providerSegment.empty()) << "No entities"; + ARMARX_CHECK(providerSegment.size() == 1) << "There should be only one entity!"; - const wm::Entity& entity = entities.begin()->second; - ARMARX_CHECK(not entity.empty()) << "No snapshots"; - - const auto& entitySnapshot = entity.getLatestSnapshot(); - ARMARX_CHECK(not entitySnapshot.empty()) << "No entity snapshot instances"; + const wm::EntityInstance* entityInstance = nullptr; + providerSegment.forEachEntity([&entityInstance](const wm::Entity & entity) + { + const auto& entitySnapshot = entity.getLatestSnapshot(); + ARMARX_CHECK(not entitySnapshot.empty()) << "No entity snapshot instances"; - const auto& entityInstance = entitySnapshot.instances().front(); + entityInstance = &entitySnapshot.getInstance(0); + return false; + }); + ARMARX_CHECK_NOT_NULL(entityInstance); - const auto aronDto = tryCast<arondto::OccupancyGrid>(entityInstance); + const auto aronDto = tryCast<arondto::OccupancyGrid>(*entityInstance); ARMARX_CHECK(aronDto) << "Failed casting to OccupancyGrid"; OccupancyGrid occupancyGrid; @@ -89,7 +88,7 @@ namespace armarx::armem::vision::occupancy_grid::client // direct access to grid data const auto ndArrayNavigator = aron::datanavigator::NDArrayNavigator::DynamicCast( - entityInstance.data()->getElement("grid")); + entityInstance->data()->getElement("grid")); ARMARX_CHECK_NOT_NULL(ndArrayNavigator); occupancyGrid.grid = aron::converter::AronEigenConverter::ConvertToArray<float>(*ndArrayNavigator); @@ -118,11 +117,10 @@ namespace armarx::armem::vision::occupancy_grid::client } // now create result from memory - const auto& entities = qResult.memory.getCoreSegment(properties().memoryName) - .getProviderSegment(query.providerName) - .entities(); + const wm::ProviderSegment& providerSegment = qResult.memory.getCoreSegment(properties().memoryName) + .getProviderSegment(query.providerName); - if (entities.empty()) + if (providerSegment.empty()) { ARMARX_WARNING << "No entities."; return {.occupancyGrid = std::nullopt, @@ -132,7 +130,7 @@ namespace armarx::armem::vision::occupancy_grid::client try { - const auto occupancyGrid = asOccupancyGrid(entities); + const auto occupancyGrid = asOccupancyGrid(providerSegment); return Result{.occupancyGrid = occupancyGrid, .status = Result::Status::Success}; } diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h index a43c2c7c1151223358d5b1ab608f824fd3cdf66f..eaf9b2fdfa2021db7f8e1215baedc6aba907dec3 100644 --- a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h +++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Reader.h @@ -23,9 +23,9 @@ #include <mutex> -#include "RobotAPI/libraries/armem/client/util/SimpleReaderBase.h" -#include "RobotAPI/libraries/armem/core/Time.h" -#include "RobotAPI/libraries/armem_vision/types.h" +#include <RobotAPI/libraries/armem/client/util/SimpleReaderBase.h> +#include <RobotAPI/libraries/armem/core/Time.h> +#include <RobotAPI/libraries/armem_vision/types.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> namespace armarx::armem::vision::occupancy_grid::client diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp index f2930f14f479d9ae68ee222f2643edf327392a43..ce5883afb6eeb72c7ebb9337d0b4980eee7e462e 100644 --- a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp +++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.cpp @@ -1,8 +1,9 @@ #include "Writer.h" -#include "RobotAPI/libraries/armem_vision/aron_conversions.h" +#include <RobotAPI/libraries/armem_vision/aron_conversions.h> #include <RobotAPI/libraries/armem_vision/aron/OccupancyGrid.aron.generated.h> + namespace armarx::armem::vision::occupancy_grid::client { Writer::~Writer() = default; @@ -67,4 +68,4 @@ namespace armarx::armem::vision::occupancy_grid::client return SimpleWriterBase::Properties{.memoryName = "Vision", .coreSegmentName = "OccupancyGrid"}; } -} // namespace armarx::armem::vision::occupancy_grid::client \ No newline at end of file +} // namespace armarx::armem::vision::occupancy_grid::client diff --git a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h index bf1268444c333d10bb3c2f9efb68da11fe697cc8..b2a900321224d2c4617cab541c7b1cf77f41d1f1 100644 --- a/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h +++ b/source/RobotAPI/libraries/armem_vision/client/occupancy_grid/Writer.h @@ -24,8 +24,8 @@ #include <mutex> -#include "RobotAPI/libraries/armem/client/util/SimpleWriterBase.h" -#include "RobotAPI/libraries/armem_vision/types.h" +#include <RobotAPI/libraries/armem/client/util/SimpleWriterBase.h> +#include <RobotAPI/libraries/armem_vision/types.h> namespace armarx::armem::vision::occupancy_grid::client { diff --git a/source/RobotAPI/libraries/aron/converter/CMakeLists.txt b/source/RobotAPI/libraries/aron/converter/CMakeLists.txt index 5f8db702e460cbb2e729862458314920823be780..e93d191b2f99b0eccb973a925a77ebdf674ffaee 100644 --- a/source/RobotAPI/libraries/aron/converter/CMakeLists.txt +++ b/source/RobotAPI/libraries/aron/converter/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(ivt) add_subdirectory(pcl) add_subdirectory(eigen) add_subdirectory(opencv) +add_subdirectory(json) add_library(AronConverter INTERFACE) @@ -13,7 +14,8 @@ target_link_libraries(AronConverter RobotAPI::aron::converter::ivt RobotAPI::aron::converter::pcl RobotAPI::aron::converter::eigen - RobotAPI::aron::converter::opencv + RobotAPI::aron::converter::opencv + RobotAPI::aron::converter::json ) add_library(aron::converter ALIAS AronConverter) diff --git a/source/RobotAPI/libraries/aron/converter/json/CMakeLists.txt b/source/RobotAPI/libraries/aron/converter/json/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8f5c8006f3b16d7343a2f74b1c6b6562de426165 --- /dev/null +++ b/source/RobotAPI/libraries/aron/converter/json/CMakeLists.txt @@ -0,0 +1,24 @@ +set(LIB_NAME aronjsonconverter) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +find_package(IVT COMPONENTS ivt ivtopencv QUIET) +armarx_build_if(IVT_FOUND "IVT not available") + +set(LIBS + aron +) + +set(LIB_FILES + NLohmannJSONConverter.cpp +) + +set(LIB_HEADERS + NLohmannJSONConverter.h +) + +armarx_add_library("${LIB_NAME}" "${LIB_FILES}" "${LIB_HEADERS}" "${LIBS}") + + +add_library(RobotAPI::aron::converter::json ALIAS aronjsonconverter) diff --git a/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.cpp b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3eb6f82327e13ed89629e5de76a41909a9f0f9e2 --- /dev/null +++ b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.cpp @@ -0,0 +1,42 @@ +#include "NLohmannJSONConverter.h" + +#include <RobotAPI/libraries/aron/core/Debug.h> +#include <RobotAPI/libraries/aron/core/io/dataIO/visitor/Visitor.h> +#include <RobotAPI/libraries/aron/core/io/dataIO/converter/Converter.h> +#include <RobotAPI/libraries/aron/core/io/dataIO/reader/nlohmannJSON/NlohmannJSONReader.h> +#include <RobotAPI/libraries/aron/core/io/dataIO/writer/nlohmannJSON/NlohmannJSONWriter.h> + + +namespace armarx::aron::converter +{ + nlohmann::json AronNlohmannJSONConverter::ConvertToNlohmannJSON(const datanavigator::DictNavigatorPtr& aron) + { + nlohmann::json j; + ConvertToNlohmannJSON(aron, j); + return j; + } + + void AronNlohmannJSONConverter::ConvertToNlohmannJSON(const aron::datanavigator::DictNavigatorPtr& aron, nlohmann::json& j) + { + aron::dataIO::writer::NlohmannJSONWriter dataWriter; + aron::dataIO::Visitor::VisitAndSetup(dataWriter, aron); + j = dataWriter.getResult(); + } + + + + datanavigator::DictNavigatorPtr AronNlohmannJSONConverter::ConvertFromNlohmannJSON(const nlohmann::json& j) + { + auto aron = std::make_shared<aron::datanavigator::DictNavigator>(); + ConvertFromNlohmannJSON(aron, j); + return aron; + } + + void AronNlohmannJSONConverter::ConvertFromNlohmannJSON(aron::datanavigator::DictNavigatorPtr& a, const nlohmann::json& e, const aron::typenavigator::NavigatorPtr& expectedStructure) + { + aron::dataIO::reader::NlohmannJSONReader dataReader(e); + aron::dataIO::writer::NavigatorWriter navWriter; + aron::dataIO::Converter::ReadAndConvert(dataReader, navWriter, expectedStructure); + a = aron::datanavigator::DictNavigator::DynamicCastAndCheck(navWriter.getResult()); + } +} diff --git a/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..2e1a89c63511d9806951887a7461fa09dcb17da4 --- /dev/null +++ b/source/RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h @@ -0,0 +1,29 @@ +#pragma once + +// STD/STL +#include <memory> +#include <string> +#include <numeric> + +// Memory +#include <RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h> +#include <RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h> + +// JSON +#include <SimoxUtility/json/json.hpp> + +namespace armarx::aron::converter +{ + class AronNlohmannJSONConverter + { + + public: + AronNlohmannJSONConverter() = delete; + + static nlohmann::json ConvertToNlohmannJSON(const datanavigator::DictNavigatorPtr&); + static void ConvertToNlohmannJSON(const datanavigator::DictNavigatorPtr&, nlohmann::json&); + + static datanavigator::DictNavigatorPtr ConvertFromNlohmannJSON(const nlohmann::json&); + static void ConvertFromNlohmannJSON(datanavigator::DictNavigatorPtr&, const nlohmann::json&, const aron::typenavigator::NavigatorPtr& = nullptr); + }; +} diff --git a/source/RobotAPI/libraries/aron/core/CMakeLists.txt b/source/RobotAPI/libraries/aron/core/CMakeLists.txt index 62548c29719f3942afee49a45e9a3f24a4232634..c5282fb54aba97b583f15f2df1876c0dde3e4ead 100644 --- a/source/RobotAPI/libraries/aron/core/CMakeLists.txt +++ b/source/RobotAPI/libraries/aron/core/CMakeLists.txt @@ -151,6 +151,7 @@ set(LIB_HEADERS navigator/data/primitive/Bool.h navigator/data/AllNavigators.h navigator/data/NavigatorFactory.h + navigator/data/forward_declarations.h navigator/type/Navigator.h navigator/type/detail/NavigatorBase.h @@ -181,6 +182,7 @@ set(LIB_HEADERS navigator/type/primitive/Time.h navigator/type/AllNavigators.h navigator/type/NavigatorFactory.h + navigator/type/forward_declarations.h navigator/visitors/DataVisitor.h navigator/visitors/TypedDataVisitor.h diff --git a/source/RobotAPI/libraries/aron/core/io/dataIO/reader/navigator/NavigatorReader.cpp b/source/RobotAPI/libraries/aron/core/io/dataIO/reader/navigator/NavigatorReader.cpp index c431b5b49ea2547f262da4fd657be6aa6fed1fac..6c95ec35b8eb3c7ce42bf6cbc74c0c56adc68829 100644 --- a/source/RobotAPI/libraries/aron/core/io/dataIO/reader/navigator/NavigatorReader.cpp +++ b/source/RobotAPI/libraries/aron/core/io/dataIO/reader/navigator/NavigatorReader.cpp @@ -150,7 +150,7 @@ namespace armarx::aron::dataIO::reader void NavigatorReader::readEndNDArray(unsigned char* data) { - if (data == NULL) + if (data == nullptr) { throw error::AronException("NavigatorReader", "readEndNDArray", "The corresponding data pointer of an complex aron ptr is NULL."); } diff --git a/source/RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h b/source/RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h new file mode 100644 index 0000000000000000000000000000000000000000..d76939c317aafd2acc4eb797d38d1ba1ff96da26 --- /dev/null +++ b/source/RobotAPI/libraries/aron/core/navigator/data/forward_declarations.h @@ -0,0 +1,22 @@ +#pragma once + +#include <memory> + + +namespace armarx::aron::datanavigator +{ + class Navigator; + using NavigatorPtr = std::shared_ptr<Navigator>; + + using DictNavigatorPtr = std::shared_ptr<class DictNavigator>; + using ListNavigatorPtr = std::shared_ptr<class ListNavigator>; + using NDArrayNavigatorPtr = std::shared_ptr<class NDArrayNavigator>; + + using IntNavigatorPtr = std::shared_ptr<class IntNavigator>; + using LongNavigatorPtr = std::shared_ptr<class LongNavigator>; + using FloatNavigatorPtr = std::shared_ptr<class FloatNavigator>; + using DoubleNavigatorPtr = std::shared_ptr<class DoubleNavigator>; + using StringNavigatorPtr = std::shared_ptr<class StringNavigator>; + using BoolNavigatorPtr = std::shared_ptr<class BoolNavigator>; + +} diff --git a/source/RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h b/source/RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h index 51ea5a762037da7b7e4a3808c42d36a468d9c6b1..82702de998bb7e6b0972afd9e96ae027b433eae3 100644 --- a/source/RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h +++ b/source/RobotAPI/libraries/aron/core/navigator/type/AllNavigators.h @@ -12,7 +12,6 @@ #include "ndarray/OpenCVMat.h" #include "ndarray/Orientation.h" #include "ndarray/PCLPointCloud.h" -#include "ndarray/PCLPointCloud.h" #include "ndarray/Pose.h" #include "ndarray/Position.h" #include "enum/IntEnum.h" diff --git a/source/RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h b/source/RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h new file mode 100644 index 0000000000000000000000000000000000000000..af5d62e7c0decbc296cea942fa1983b0e0cb5924 --- /dev/null +++ b/source/RobotAPI/libraries/aron/core/navigator/type/forward_declarations.h @@ -0,0 +1,34 @@ +#pragma once + +#include <memory> + + +namespace armarx::aron::typenavigator +{ + using NavigatorPtr = std::shared_ptr<class Navigator>; + + using DictNavigatorPtr = std::shared_ptr<class DictNavigator>; + using ListNavigatorPtr = std::shared_ptr<class ListNavigator>; + using ObjectNavigatorPtr = std::shared_ptr<class ObjectNavigator>; + using PoseNavigatorPtr = std::shared_ptr<class PoseNavigator>; + using TupleNavigatorPtr = std::shared_ptr<class TupleNavigator>; + using NDArrayNavigatorPtr = std::shared_ptr<class NDArrayNavigator>; + using EigenMatrixNavigatorPtr = std::shared_ptr<class EigenMatrixNavigator>; + using EigenQuaternionNavigatorPtr = std::shared_ptr<class EigenQuaternionNavigator>; + using IVTCByteImageNavigatorPtr = std::shared_ptr<class IVTCByteImageNavigator>; + using OpenCVMatNavigatorPtr = std::shared_ptr<class OpenCVMatNavigator>; + using OrientationNavigatorPtr = std::shared_ptr<class OrientationNavigator>; + using PCLPointCloudNavigatorPtr = std::shared_ptr<class PCLPointCloudNavigator>; + using PositionNavigatorPtr = std::shared_ptr<class PositionNavigator>; + using IntEnumNavigatorPtr = std::shared_ptr<class IntEnumNavigator>; + + using IntNavigatorPtr = std::shared_ptr<class IntNavigator>; + using LongNavigatorPtr = std::shared_ptr<class LongNavigator>; + using FloatNavigatorPtr = std::shared_ptr<class FloatNavigator>; + using DoubleNavigatorPtr = std::shared_ptr<class DoubleNavigator>; + using StringNavigatorPtr = std::shared_ptr<class StringNavigator>; + using BoolNavigatorPtr = std::shared_ptr<class BoolNavigator>; + using TimeNavigatorPtr = std::shared_ptr<class TimeNavigator>; + +} +