diff --git a/source/RobotAPI/components/armem/client/CMakeLists.txt b/source/RobotAPI/components/armem/client/CMakeLists.txt index a7594d23e1668080fc0006afc44b1a09abfa8587..16f823e37de7efd21d6ac9f9633976a989ca0294 100644 --- a/source/RobotAPI/components/armem/client/CMakeLists.txt +++ b/source/RobotAPI/components/armem/client/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(ExampleMemoryClient) add_subdirectory(ArticulatedObjectExampleMemoryWriterClient) +add_subdirectory(VirtualRobotReaderExampleClient) diff --git a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/CMakeLists.txt b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..cd132bb87336bf793e86e79b7e048ea044921491 --- /dev/null +++ b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/CMakeLists.txt @@ -0,0 +1,29 @@ +armarx_component_set_name("VirtualRobotReaderExampleClient") + +find_package(IVT QUIET) +armarx_build_if(IVT_FOUND "IVT not available") + +set(COMPONENT_LIBS + ArmarXCore + ArmarXCoreInterfaces # for DebugObserverInterface + ArmarXGuiComponentPlugins + RobotAPICore + RobotAPIInterfaces + armem_robot_state +) + +set(SOURCES + VirtualRobotReaderExampleClient.cpp +) + +set(HEADERS + VirtualRobotReaderExampleClient.h +) + +armarx_add_component("${SOURCES}" "${HEADERS}") + +# add unit tests +# add_subdirectory(test) + +#generate the application +armarx_generate_and_add_component_executable(COMPONENT_NAMESPACE "armarx::robot_state") diff --git a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85ca85f48ca62c7bc686bd9286d52fae5480ebc7 --- /dev/null +++ b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.cpp @@ -0,0 +1,105 @@ + + +#include "VirtualRobotReaderExampleClient.h" + + +#include <memory> + +#include <Eigen/Geometry> + +#include <IceUtil/Time.h> + +#include <VirtualRobot/Robot.h> +#include <VirtualRobot/XML/RobotIO.h> +#include <VirtualRobot/VirtualRobot.h> + +#include <ArmarXCore/core/PackagePath.h> +#include <ArmarXCore/core/system/ArmarXDataPath.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/time/CycleUtil.h> +#include <ArmarXCore/core/logging/Logging.h> +#include <ArmarXCore/core/time/TimeUtil.h> +#include <ArmarXCore/core/services/tasks/PeriodicTask.h> + +#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/server/MemoryRemoteGui.h> +#include <RobotAPI/libraries/armem/core/Time.h> + + +namespace armarx::robot_state +{ + VirtualRobotReaderExampleClient::VirtualRobotReaderExampleClient() : + virtualRobotReader(*this) {} + + armarx::PropertyDefinitionsPtr VirtualRobotReaderExampleClient::createPropertyDefinitions() + { + armarx::PropertyDefinitionsPtr defs = + new ComponentPropertyDefinitions(getConfigIdentifier()); + + defs->topic(debugObserver); + + defs->optional(p.robotName, "robotName"); + defs->optional(p.updateFrequency, "updateFrequency"); + + virtualRobotReader.registerPropertyDefinitions(defs); + + return defs; + } + + std::string VirtualRobotReaderExampleClient::getDefaultName() const + { + return "VirtualRobotReaderExampleClient"; + } + + void VirtualRobotReaderExampleClient::onInitComponent() {} + + void VirtualRobotReaderExampleClient::onConnectComponent() + { + virtualRobotReader.connect(); + + ARMARX_IMPORTANT << "Running virtual robot synchronization example."; + + task = new PeriodicTask<VirtualRobotReaderExampleClient>(this, &VirtualRobotReaderExampleClient::run, 1000 / p.updateFrequency); + task->start(); + } + + void VirtualRobotReaderExampleClient::onDisconnectComponent() + { + task->stop(); + } + + void VirtualRobotReaderExampleClient::onExitComponent() {} + + void VirtualRobotReaderExampleClient::run() + { + + // initialize if needed + if (virtualRobot == nullptr) + { + TIMING_START(getRobot); + + virtualRobot = virtualRobotReader.getRobot(p.robotName, IceUtil::Time::now()); + + if (virtualRobot == nullptr) + { + ARMARX_WARNING << deactivateSpam(1) << "Could not create virtual robot."; + return; + } + // only print timing once the robot is loadable & loaded + TIMING_END_STREAM(getRobot, ARMARX_INFO); + } + + ARMARX_INFO << deactivateSpam(10) << "Synchronizing robot"; + + const IceUtil::Time now = TimeUtil::GetTime(); + + TIMING_START(synchronizeRobot); + virtualRobotReader.synchronizeRobot(*virtualRobot, now); + TIMING_END_STREAM(synchronizeRobot, ARMARX_INFO); + + + } + +} // namespace armarx::robot_state diff --git a/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h new file mode 100644 index 0000000000000000000000000000000000000000..4e1d01f5564e96c7e46ad2db33081835873dd7a6 --- /dev/null +++ b/source/RobotAPI/components/armem/client/VirtualRobotReaderExampleClient/VirtualRobotReaderExampleClient.h @@ -0,0 +1,96 @@ + +/* + * 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 Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + + +// ArmarX +#include <ArmarXCore/core/Component.h> +#include <ArmarXCore/interface/observers/ObserverInterface.h> +#include <ArmarXCore/util/tasks.h> +#include <ArmarXGui/libraries/ArmarXGuiComponentPlugins/LightweightRemoteGuiComponentPlugin.h> + +// RobotAPI +#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_robot_state/client/common/VirtualRobotReader.h> + +namespace armarx::robot_state +{ + + /** + * @defgroup Component-ExampleClient ExampleClient + * @ingroup RobotAPI-Components + * A description of the component ExampleClient. + * + * @class ExampleClient + * @ingroup Component-ExampleClient + * @brief Brief description of class ExampleClient. + * + * Detailed description of class ExampleClient. + */ + class VirtualRobotReaderExampleClient : + virtual public armarx::Component, + virtual public armarx::armem::client::ComponentPluginUser + { + public: + VirtualRobotReaderExampleClient(); + + /// @see armarx::ManagedIceObject::getDefaultName() + std::string getDefaultName() const override; + + protected: + + armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; + + void onInitComponent() override; + void onConnectComponent() override; + void onDisconnectComponent() override; + void onExitComponent() override; + + void run(); + + + private: + + struct Properties + { + std::string robotName{"Armar6"}; + float updateFrequency{10.F}; + } p; + + armarx::PeriodicTask<VirtualRobotReaderExampleClient>::pointer_type task; + + armarx::DebugObserverInterfacePrx debugObserver; + + // std::unique_ptr<::armarx::armem::articulated_object::Writer> articulatedObjectWriter; + + armem::robot_state::VirtualRobotReader virtualRobotReader; + + std::shared_ptr<VirtualRobot::Robot> virtualRobot{nullptr}; + + }; + +} // namespace armarx::robot_state diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp index 444c6fdc39e109de28afa911f74b19e01b4cd36c..5aba1210686f0cc0a821d83f1053e65a41c348e6 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp @@ -337,7 +337,7 @@ namespace armarx::armem::server::robot_state auto it = convertedAndGroupedData.groups.find("sens.Platform"); if (it != convertedAndGroupedData.groups.end()) { - ARMARX_DEBUG << " found odometry data."; + ARMARX_DEBUG << "Found odometry data."; const RobotUnitData::RobotUnitDataGroup& timestep = it->second; const float relPosX = aron::datanavigator::FloatNavigator::DynamicCast(timestep.data->getElement("relativePositionX"))->getValue(); diff --git a/source/RobotAPI/libraries/armem/client/query/query_fns.h b/source/RobotAPI/libraries/armem/client/query/query_fns.h index f08ae23bc6b8b6bc7fb4ed99f9ac51ed570f556c..35203a014fa423cec4f8ef0f4d09759bc54385a9 100644 --- a/source/RobotAPI/libraries/armem/client/query/query_fns.h +++ b/source/RobotAPI/libraries/armem/client/query/query_fns.h @@ -156,7 +156,7 @@ namespace armarx::armem::client::query_fns inline std::function<void(query::SnapshotSelector&)> - beforeTime(Time time, long nElements) + beforeTime(Time time, long nElements = 1) { return [ = ](query::SnapshotSelector & selector) { diff --git a/source/RobotAPI/libraries/armem/client/query/selectors.h b/source/RobotAPI/libraries/armem/client/query/selectors.h index a1c314232099438f9498fbabb763e52c00abe862..df642ded9a70ea2688eaca5865f730e8e99ee6de 100644 --- a/source/RobotAPI/libraries/armem/client/query/selectors.h +++ b/source/RobotAPI/libraries/armem/client/query/selectors.h @@ -28,7 +28,7 @@ namespace armarx::armem::client::query SnapshotSelector& atTime(Time timestamp); SnapshotSelector& atTimeApprox(Time timestamp, Duration eps); - SnapshotSelector& beforeTime(Time timestamp, long maxEntries); + SnapshotSelector& beforeTime(Time timestamp, long maxEntries = 1); SnapshotSelector& beforeOrAtTime(Time timestamp); SnapshotSelector& timeRange(Time min, Time max); diff --git a/source/RobotAPI/libraries/armem_robot/client/interfaces.h b/source/RobotAPI/libraries/armem_robot/client/interfaces.h new file mode 100644 index 0000000000000000000000000000000000000000..d127fbf51eee8d842ee259211fd63a467d0a2f9b --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot/client/interfaces.h @@ -0,0 +1,29 @@ +#pragma once + + +#include <RobotAPI/libraries/armem/core/Time.h> + +#include <RobotAPI/libraries/armem_robot/types.h> + +namespace armarx::armem::robot +{ + class ReaderInterface + { + public: + virtual ~ReaderInterface() = default; + + virtual bool synchronize(Robot& obj, const armem::Time& timestamp) = 0; + + virtual Robot get(const RobotDescription& description, const armem::Time& timestamp) = 0; + virtual std::optional<Robot> get(const std::string& name, const armem::Time& timestamp) = 0; + }; + + class WriterInterface + { + public: + virtual ~WriterInterface() = default; + + virtual bool store(const Robot& obj) = 0; + }; + +} // namespace armarx::armem::robot \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot/types.h b/source/RobotAPI/libraries/armem_robot/types.h index 03c8ae1bfed67fb900f50905caeee18ca668a835..4fed4d1cb277ab316a318cbc13b4f32222fd8939 100644 --- a/source/RobotAPI/libraries/armem_robot/types.h +++ b/source/RobotAPI/libraries/armem_robot/types.h @@ -22,10 +22,13 @@ namespace armarx::armem::robot struct RobotState { + using JointMap = std::map<std::string, float>; + using Pose = Eigen::Affine3f; + IceUtil::Time timestamp; - Eigen::Affine3f globalPose; - std::map<std::string, float> jointMap; + Pose globalPose; + JointMap jointMap; }; struct Robot diff --git a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt index e6a42e3a28b9db59d4dc24bf4dd34e5ee2074450..2eead70ac6845aedad5d4f1e5c69e9c0d8522347 100644 --- a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt @@ -21,10 +21,12 @@ armarx_add_library( Eigen3::Eigen aroncommon HEADERS - ./common/localization/types.h ./common/localization/TransformHelper.h + ./client/common/RobotReader.h + ./client/common/VirtualRobotReader.h + ./client/localization/interfaces.h ./client/localization/TransformReader.h ./client/localization/TransformWriter.h @@ -44,6 +46,9 @@ armarx_add_library( SOURCES ./common/localization/TransformHelper.cpp + ./client/common/RobotReader.cpp + ./client/common/VirtualRobotReader.cpp + ./client/localization/TransformReader.cpp ./client/localization/TransformWriter.cpp diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a635e51628f9b468e404eca10c4411b92171ade --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp @@ -0,0 +1,300 @@ +#include "RobotReader.h" + +#include <mutex> +#include <optional> + +#include "ArmarXCore/core/exceptions/LocalException.h" +#include "ArmarXCore/core/logging/Logging.h" +#include <ArmarXCore/core/PackagePath.h> + +#include "RobotAPI/libraries/armem/core/Time.h" +#include "RobotAPI/libraries/armem/client/query/Builder.h" +#include "RobotAPI/libraries/armem_robot/robot_conversions.h" +#include "RobotAPI/libraries/armem_robot/aron_conversions.h" +#include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> + + +namespace fs = ::std::filesystem; + +namespace armarx::armem::robot_state +{ + + RobotReader::RobotReader(armem::ClientReaderComponentPluginUser& component) : memoryClient(component), transformReader(component) {} + + void RobotReader::registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def) + { + transformReader.registerPropertyDefinitions(def); + + def->optional(properties.memoryName, propertyPrefix + "Memory"); + def->optional(properties.descriptionCoreSegment, propertyPrefix + "descriptionSegment"); + def->optional(properties.localizationCoreSegment, propertyPrefix + "localizationSegment"); + def->optional(properties.proprioceptionCoreSegment, propertyPrefix + "proprioceptionSegment"); + } + + void RobotReader::connect() + { + transformReader.connect(); + + // Wait for the memory to become available and add it as dependency. + ARMARX_IMPORTANT << "RobotReader: Waiting for memory '" << properties.memoryName + << "' ..."; + auto result = memoryClient.useMemory(properties.memoryName); + if (not result.success) + { + ARMARX_ERROR << result.errorMessage; + return; + } + + ARMARX_IMPORTANT << "RobotReader: Connected to memory '" << properties.memoryName; + + memoryReader.setReadingMemory(result.proxy); + } + + std::optional<robot::Robot> RobotReader::get(const std::string& name, const armem::Time& timestamp) + { + const auto description = queryDescription(name, timestamp); + + if (not description) + { + ARMARX_ERROR << "Unknown object " << name; + return std::nullopt; + } + + return get(*description, timestamp); + } + + + robot::Robot RobotReader::get(const robot::RobotDescription& description, + const armem::Time& timestamp) + { + robot::Robot robot + { + .description = description, + .instance = "", // TODO(fabian.reister): + .config = {}, // will be populated by synchronize + .timestamp = timestamp + }; + + synchronize(robot, timestamp); + + return robot; + } + + bool RobotReader::synchronize(robot::Robot& obj, const armem::Time& timestamp) + { + auto state = queryState(obj.description, timestamp); + + if (not state) /* c++20 [[unlikely]] */ + { + ARMARX_WARNING << "Could not synchronize object " << obj.description.name; + return false; + } + + obj.config = std::move(*state); + return true; + } + + std::optional<robot::RobotDescription> RobotReader::queryDescription(const std::string& name, const armem::Time& timestamp) + { + // Query all entities from provider. + armem::client::query::Builder qb; + + // clang-format off + qb + .coreSegments().all() // withName(properties.descriptionCoreSegment) + .providerSegments().all() // .withName(name) + .entities().all() + .snapshots().latest(); // TODO(fabian.reister): atTime(timestamp); + // clang-format on + + ARMARX_INFO << "Lookup query in reader"; + + if (not memoryReader) + { + ARMARX_WARNING << "Memory reader is null"; + return std::nullopt; + } + + try + { + const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput()); + + ARMARX_INFO << "Lookup result in reader: " << qResult; + + if (not qResult.success) /* c++20 [[unlikely]] */ + { + return std::nullopt; + } + + return getRobotDescription(qResult.memory, name); + + } + catch (...) + { + ARMARX_WARNING << "query description failure" << GetHandledExceptionString(); + } + + return std::nullopt; + + } + + std::optional<robot::RobotState> RobotReader::queryState(const robot::RobotDescription& description, const armem::Time& timestamp) + { + const auto jointMap = queryJointState(description, timestamp); + if (not jointMap) + { + return std::nullopt; + } + + const auto globalPose = queryGlobalPose(description, timestamp); + if (not globalPose) + { + return std::nullopt; + } + + return robot::RobotState + { + .timestamp = timestamp, + .globalPose = *globalPose, + .jointMap = *jointMap + }; + } + + std::optional<robot::RobotState::JointMap> RobotReader::queryJointState(const robot::RobotDescription& description, const armem::Time& timestamp) const + { + // TODO(fabian.reister): how to deal with multiple providers? + + // Query all entities from provider. + armem::client::query::Builder qb; + + // clang-format off + qb + .coreSegments().withName(properties.proprioceptionCoreSegment) + .providerSegments().withName(description.name) // agent + .entities().all() // TODO + .snapshots().beforeTime(timestamp); + // clang-format on + + const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput()); + + ARMARX_DEBUG << "Lookup result in reader: " << qResult; + + if (not qResult.success) /* c++20 [[unlikely]] */ + { + return std::nullopt; + } + + return getRobotJointState(qResult.memory, description.name); + } + + std::optional<robot::RobotState::Pose> RobotReader::queryGlobalPose(const robot::RobotDescription& description, const armem::Time& timestamp) const + { + const auto result = transformReader.getGlobalPose(description.name, "root", timestamp); + if (not result) + { + return std::nullopt; + } + + return result.transform.transform; + } + + + std::optional<robot::RobotState> RobotReader::getRobotState(const armarx::armem::wm::Memory& memory, const std::string& name) const + { + // clang-format off + 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()) + { + ARMARX_WARNING << "No entity found"; + return std::nullopt; + } + + const auto entitySnapshots = simox::alg::get_values(entities.front().history()); + + if (entitySnapshots.empty()) + { + 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); + } + + + std::optional<robot::RobotState::JointMap> RobotReader::getRobotJointState(const armarx::armem::wm::Memory& memory, const std::string& name) const + { + // // clang-format off + // 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()) + // { + // ARMARX_WARNING << "No entity found"; + // return std::nullopt; + // } + + // const auto entitySnapshots = simox::alg::get_values(entities.front().history()); + + // if (entitySnapshots.empty()) + // { + // 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 std::nullopt; // TODO implement + } + + + + std::optional<robot::RobotDescription> RobotReader::getRobotDescription(const armarx::armem::wm::Memory& memory, const std::string& name) const + { + // clang-format off + const armem::wm::ProviderSegment& providerSegment = memory + .getCoreSegment(properties.descriptionCoreSegment) + .getProviderSegment(name); // TODO(fabian.reister): all + // clang-format on + const auto entities = simox::alg::get_values(providerSegment.entities()); + + if (entities.empty()) + { + ARMARX_WARNING << "No entity found"; + return std::nullopt; + } + + const auto entitySnapshots = simox::alg::get_values(entities.front().history()); + + if (entitySnapshots.empty()) + { + 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); + } + +} // namespace armarx::armem::robot_state \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h new file mode 100644 index 0000000000000000000000000000000000000000..ba4d3d4177831c4609c65eedb5b51094b37d4e03 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h @@ -0,0 +1,85 @@ +/* + * 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 Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <mutex> +#include <optional> + +#include "RobotAPI/libraries/armem/client.h" +#include "RobotAPI/libraries/armem/client/Reader.h" + +#include "RobotAPI/libraries/armem_robot/types.h" +#include "RobotAPI/libraries/armem_robot/client/interfaces.h" + +#include "RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h" + +namespace armarx::armem::robot_state +{ + class RobotReader: + virtual public robot::ReaderInterface + { + public: + RobotReader(armem::ClientReaderComponentPluginUser& component); + virtual ~RobotReader() = default; + + void connect(); + + void registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def); + + bool synchronize(robot::Robot& obj, const armem::Time& timestamp) override; + + std::optional<robot::Robot> get(const std::string& name, const armem::Time& timestamp) override; + robot::Robot get(const robot::RobotDescription& description, const armem::Time& timestamp) override; + + std::optional<robot::RobotDescription> queryDescription(const std::string& name, const armem::Time& timestamp); + + std::optional<robot::RobotState> queryState(const robot::RobotDescription& description, const armem::Time& timestamp); + std::optional<robot::RobotState::JointMap> queryJointState(const robot::RobotDescription& description, const armem::Time& timestamp) const; + std::optional<robot::RobotState::Pose> queryGlobalPose(const robot::RobotDescription& description, const armem::Time& timestamp) const; + + private: + + + std::optional<robot::RobotState> getRobotState(const armarx::armem::wm::Memory& memory, const std::string& name) const; + std::optional<robot::RobotDescription> getRobotDescription(const armarx::armem::wm::Memory& memory, const std::string& name) const; + std::optional<robot::RobotState::JointMap> getRobotJointState(const armarx::armem::wm::Memory& memory, const std::string& name) const; + + struct Properties + { + std::string memoryName = "RobotStateMemory"; + std::string descriptionCoreSegment = "Description"; + std::string localizationCoreSegment = "Localization"; + std::string proprioceptionCoreSegment = "Proprioception"; + } properties; + + const std::string propertyPrefix = "mem.robot_state."; + + armem::client::Reader memoryReader; + std::mutex memoryWriterMutex; + + armem::ClientReaderComponentPluginUser& memoryClient; + + client::robot_state::localization::TransformReader transformReader; + }; + + +} // namespace armarx::armem::robot_state \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97633b788dcfae4cd93d6a1fd7a72850a9a009b1 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp @@ -0,0 +1,73 @@ +#include "VirtualRobotReader.h" + +#include <optional> + +#include <VirtualRobot/Robot.h> +#include <VirtualRobot/XML/RobotIO.h> + +#include "ArmarXCore/core/PackagePath.h" +#include "ArmarXCore/core/system/ArmarXDataPath.h" +#include "ArmarXCore/core/system/cmake/CMakePackageFinder.h" + + +namespace armarx::armem::robot_state +{ + + VirtualRobotReader::VirtualRobotReader(armem::ClientReaderComponentPluginUser& component) : + RobotReader(component) + { + } + + void VirtualRobotReader::connect() + { + RobotReader::connect(); + } + + // TODO(fabian.reister): register property defs + void VirtualRobotReader::registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def) + { + RobotReader::registerPropertyDefinitions(def); + } + + bool VirtualRobotReader::synchronizeRobot(VirtualRobot::Robot& robot, + const armem::Time& timestamp) + { + const auto packages = armarx::CMakePackageFinder::FindAllArmarXSourcePackages(); + const auto package = armarx::ArmarXDataPath::getProject(packages, robot.getFilename()); + + const robot::RobotDescription robotDescription{.name = robot.getName(), + .xml = PackagePath{package, robot.getFilename()}}; + + const auto robotState = queryState(robotDescription, timestamp); + if (not robotState) + { + return false; + } + + robot.setJointValues(robotState->jointMap); + robot.setGlobalPose(robotState->globalPose.matrix()); + + return true; + } + + VirtualRobot::RobotPtr VirtualRobotReader::getRobot(const std::string& name, + const armem::Time& timestamp, + const VirtualRobot::RobotIO::RobotDescription& loadMode) + { + ARMARX_INFO << "Querying robot description for robot '" << name << "'"; + const auto description = queryDescription(name, timestamp); + + if (not description) + { + return nullptr; + } + + const std::string xmlFilename = ArmarXDataPath::resolvePath(description->xml.serialize().path); + ARMARX_INFO << "Loading (virtual) robot '" << description->name << "' from XML file '" << xmlFilename << "'"; + + return VirtualRobot::RobotIO::loadRobot(xmlFilename, loadMode); + + } + + +} // namespace armarx::armem::robot_state \ No newline at end of file diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h new file mode 100644 index 0000000000000000000000000000000000000000..91e5392d2abd5b4f65678b35c0bf652d490c0e13 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h @@ -0,0 +1,56 @@ +/* + * 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 Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include "RobotReader.h" + +#include <VirtualRobot/Robot.h> +#include <VirtualRobot/VirtualRobot.h> + +#include <VirtualRobot/XML/RobotIO.h> + +namespace armarx::armem::robot_state +{ + /** + * @brief The VirtualRobotReader class. + * + * The aim of this class is to obtain a virtual robot instance and synchronize it + * with the data (joint positions, global pose, ...) stored in the working memory. + * + */ + class VirtualRobotReader : virtual public RobotReader + { + public: + VirtualRobotReader(armem::ClientReaderComponentPluginUser& component); + virtual ~VirtualRobotReader() = default; + + void connect(); + void registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def); + + bool synchronizeRobot(VirtualRobot::Robot& robot, const armem::Time& timestamp); + + VirtualRobot::RobotPtr getRobot(const std::string& name, + const armem::Time& timestamp, + const VirtualRobot::RobotIO::RobotDescription& loadMode = VirtualRobot::RobotIO::RobotDescription::eStructure); + }; + +} // namespace armarx::armem::robot_state \ No newline at end of file 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 e7b55ef6fb1c47b91802c8191ec9bcd0018a60d7..ea636eeb11b0b274d8fbf113616883a001d696b9 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.cpp @@ -22,6 +22,7 @@ #include "TransformReader.h" #include "RobotAPI/libraries/armem/core/Time.h" +#include "RobotAPI/libraries/armem_robot_state/common/localization/types.h" #include <algorithm> #include <iterator> diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h index fc789a7d9f7ea313bbf1c6c4edf088c9ecb257d0..3d7bf59eef0f97f18cf58087522630f87c9b0cbd 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h +++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformReader.h @@ -71,7 +71,7 @@ namespace armarx::armem::client::robot_state::localization } properties; - const std::string propertyPrefix = "mem.localization."; + const std::string propertyPrefix = "mem.robot_state."; armem::ClientReaderComponentPluginUser& memoryClient; 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 8755cee1384432de6b9ca995d3382303d5934d7f..2327c141840bc74f8b43ab14df03b03f2b98ccdf 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.cpp @@ -13,7 +13,6 @@ * 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:: * @author Fabian Reister ( fabian dot reister at kit dot edu ) * @date 2021 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt @@ -21,8 +20,8 @@ */ #include "TransformWriter.h" -#include "RobotAPI/libraries/armem/core/MemoryID.h" +#include <optional> #include <algorithm> #include <iterator> #include <numeric> @@ -47,7 +46,8 @@ #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron_conversions.h> -#include <optional> +#include "RobotAPI/libraries/armem/core/MemoryID.h" + namespace armarx::armem::client::robot_state::localization diff --git a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h index 1aea36345abb2b8a1252d525ed016593ee414a06..aa9e295bf19b34229697ad43725884469c9d1172 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h +++ b/source/RobotAPI/libraries/armem_robot_state/client/localization/TransformWriter.h @@ -74,7 +74,7 @@ namespace armarx::armem::client::robot_state::localization std::string localizationSegment = "Localization"; } properties; - const std::string propertyPrefix = "mem.localization."; + const std::string propertyPrefix = "mem.robot_state."; armem::ClientWriterComponentPluginUser& memoryClient; }; diff --git a/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h b/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h index 207660c68325fee922a7497ba771c014d597ef71..7f0c7c3396c7688ebe50b854579b107e9cd55b6f 100644 --- a/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h +++ b/source/RobotAPI/libraries/armem_robot_state/common/localization/types.h @@ -37,11 +37,15 @@ namespace armarx::armem::common::robot_state::localization enum class Status { Success, + Error, ErrorLookupIntoFuture, ErrorFrameNotAvailable } status; - explicit operator bool() const { return status == Status::Success; } + explicit operator bool() const + { + return status == Status::Success; + } std::string errorMessage = ""; }; 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 577d4ce120fa4ad7d12a7645bead65124092d5fd..826c5b484768686e2edb8cee409df4136bcc85ab 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.cpp @@ -8,6 +8,8 @@ #include "ArmarXCore/core/logging/Logging.h" #include <ArmarXCore/core/time/TimeUtil.h> +#include "ArmarXCore/core/system/ArmarXDataPath.h" +#include "ArmarXCore/core/system/cmake/CMakePackageFinder.h" #include "RobotAPI/libraries/armem_robot/types.h" #include "RobotAPI/libraries/aron/common/aron_conversions.h" @@ -119,11 +121,17 @@ namespace armarx::armem::server::robot_state::description ARMARX_CHECK_NOT_NULL(kinematicUnit); const auto robotName = kinematicUnit->getRobotName(); + const auto robotFilename = kinematicUnit->getRobotFilename(); + + const auto packages = armarx::CMakePackageFinder::FindAllArmarXSourcePackages(); + const auto package = armarx::ArmarXDataPath::getProject(packages, robotFilename); + + ARMARX_INFO << "Robot description '" << robotFilename << "' found in package " << package; const robot::RobotDescription robotDescription { .name = kinematicUnit->getRobotName(), - .xml = {"Armar6RT", kinematicUnit->getRobotFilename()}}; // FIXME + .xml = {package, kinematicUnit->getRobotFilename()}}; // FIXME storeRobotDescription(robotDescription); } 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 21bfd98fe652e9973478769640ab9d06c5ef7c21..2f603cf4961d90342fbd74770a0ef4d0d181c033 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/description/Segment.h @@ -111,4 +111,4 @@ namespace armarx::armem::server::robot_state::description }; -} // namespace armarx::armem::server::robot_state::localization +} // namespace armarx::armem::server::robot_state::description