diff --git a/scenarios/SkillProviderTest/config/ArVizStorage.cfg b/scenarios/SkillProviderTest/config/ArVizStorage.cfg index 302ac28c37dd28de3e68fb4fe4c2174faa4ec3bf..a07db2940a4d3870521fe7d5f4faa74b95f8a788 100644 --- a/scenarios/SkillProviderTest/config/ArVizStorage.cfg +++ b/scenarios/SkillProviderTest/config/ArVizStorage.cfg @@ -68,6 +68,15 @@ # ArmarX.ArVizStorage.TopicName = ArVizTopic +# ArmarX.AutodiscoverPackages: If enabled, will discover all ArmarX packages based on the environment variables. Otherwise, the `DefaultPackages` and `AdditionalPackages` properties are used. +# Attributes: +# - Default: true +# - Case sensitivity: yes +# - Required: no +# - Possible values: {0, 1, false, no, true, yes} +# ArmarX.AutodiscoverPackages = true + + # ArmarX.CachePath: Path for cache files. If relative path AND env. variable ARMARX_CONFIG_DIR is set, the cache path will be made relative to ARMARX_CONFIG_DIR. Otherwise if relative it will be relative to the default ArmarX config dir (${ARMARX_WORKSPACE}/armarx_config) # Attributes: # - Default: mongo/.cache diff --git a/scenarios/SkillProviderTest/config/DebugObserver.cfg b/scenarios/SkillProviderTest/config/DebugObserver.cfg index 8dc7ead26b3bd2f7678b3b3e7a1b00c01213225d..4692d6eb22961724c00da6ea0655e14b529dc0b3 100644 --- a/scenarios/SkillProviderTest/config/DebugObserver.cfg +++ b/scenarios/SkillProviderTest/config/DebugObserver.cfg @@ -18,6 +18,15 @@ # ArmarX.ApplicationName = "" +# ArmarX.AutodiscoverPackages: If enabled, will discover all ArmarX packages based on the environment variables. Otherwise, the `DefaultPackages` and `AdditionalPackages` properties are used. +# Attributes: +# - Default: true +# - Case sensitivity: yes +# - Required: no +# - Possible values: {0, 1, false, no, true, yes} +# ArmarX.AutodiscoverPackages = true + + # ArmarX.CachePath: Path for cache files. If relative path AND env. variable ARMARX_CONFIG_DIR is set, the cache path will be made relative to ARMARX_CONFIG_DIR. Otherwise if relative it will be relative to the default ArmarX config dir (${ARMARX_WORKSPACE}/armarx_config) # Attributes: # - Default: mongo/.cache diff --git a/scenarios/SkillProviderTest/config/MemoryNameSystem.cfg b/scenarios/SkillProviderTest/config/MemoryNameSystem.cfg index b8bc70a66ca7f32a628886ad1bf13e373f9750d3..f3557b670ab65b2a6fbb924466be6539440df5a3 100644 --- a/scenarios/SkillProviderTest/config/MemoryNameSystem.cfg +++ b/scenarios/SkillProviderTest/config/MemoryNameSystem.cfg @@ -18,6 +18,15 @@ # ArmarX.ApplicationName = "" +# ArmarX.AutodiscoverPackages: If enabled, will discover all ArmarX packages based on the environment variables. Otherwise, the `DefaultPackages` and `AdditionalPackages` properties are used. +# Attributes: +# - Default: true +# - Case sensitivity: yes +# - Required: no +# - Possible values: {0, 1, false, no, true, yes} +# ArmarX.AutodiscoverPackages = true + + # ArmarX.CachePath: Path for cache files. If relative path AND env. variable ARMARX_CONFIG_DIR is set, the cache path will be made relative to ARMARX_CONFIG_DIR. Otherwise if relative it will be relative to the default ArmarX config dir (${ARMARX_WORKSPACE}/armarx_config) # Attributes: # - Default: mongo/.cache diff --git a/scenarios/SkillProviderTest/config/RemoteGuiProviderApp.cfg b/scenarios/SkillProviderTest/config/RemoteGuiProviderApp.cfg index 4b6abea40d72afd7d313ee47a9b191f3b26de30d..1a338e3828f75121a596368c0dd067ad5e105487 100644 --- a/scenarios/SkillProviderTest/config/RemoteGuiProviderApp.cfg +++ b/scenarios/SkillProviderTest/config/RemoteGuiProviderApp.cfg @@ -18,6 +18,15 @@ # ArmarX.ApplicationName = "" +# ArmarX.AutodiscoverPackages: If enabled, will discover all ArmarX packages based on the environment variables. Otherwise, the `DefaultPackages` and `AdditionalPackages` properties are used. +# Attributes: +# - Default: true +# - Case sensitivity: yes +# - Required: no +# - Possible values: {0, 1, false, no, true, yes} +# ArmarX.AutodiscoverPackages = true + + # ArmarX.CachePath: Path for cache files. If relative path AND env. variable ARMARX_CONFIG_DIR is set, the cache path will be made relative to ARMARX_CONFIG_DIR. Otherwise if relative it will be relative to the default ArmarX config dir (${ARMARX_WORKSPACE}/armarx_config) # Attributes: # - Default: mongo/.cache diff --git a/scenarios/SkillProviderTest/config/SkillProviderExample.cfg b/scenarios/SkillProviderTest/config/SkillProviderExample.cfg index 90ae11d1f4c91a4d1dc3e2b737fabe9475dd125d..fbc666bd2b68bcceaad2d9e882566fd9fb3c80a0 100644 --- a/scenarios/SkillProviderTest/config/SkillProviderExample.cfg +++ b/scenarios/SkillProviderTest/config/SkillProviderExample.cfg @@ -18,6 +18,15 @@ # ArmarX.ApplicationName = "" +# ArmarX.AutodiscoverPackages: If enabled, will discover all ArmarX packages based on the environment variables. Otherwise, the `DefaultPackages` and `AdditionalPackages` properties are used. +# Attributes: +# - Default: true +# - Case sensitivity: yes +# - Required: no +# - Possible values: {0, 1, false, no, true, yes} +# ArmarX.AutodiscoverPackages = true + + # ArmarX.CachePath: Path for cache files. If relative path AND env. variable ARMARX_CONFIG_DIR is set, the cache path will be made relative to ARMARX_CONFIG_DIR. Otherwise if relative it will be relative to the default ArmarX config dir (${ARMARX_WORKSPACE}/armarx_config) # Attributes: # - Default: mongo/.cache diff --git a/source/RobotAPI/components/CMakeLists.txt b/source/RobotAPI/components/CMakeLists.txt index 11908aeda6ade0ff4b46f63d0590c88d007e51c9..fcd09e9158a7bb7b36ab3a27a1edb875aaa7b6e6 100644 --- a/source/RobotAPI/components/CMakeLists.txt +++ b/source/RobotAPI/components/CMakeLists.txt @@ -27,6 +27,7 @@ add_subdirectory(NaturalIKTest) add_subdirectory(ObjectPoseClientExample) add_subdirectory(ObjectPoseProviderExample) add_subdirectory(RobotHealth) +add_subdirectory(RobotDefinition) add_subdirectory(RobotNameService) add_subdirectory(RobotState) add_subdirectory(RobotToArViz) diff --git a/source/RobotAPI/components/RobotDefinition/CMakeLists.txt b/source/RobotAPI/components/RobotDefinition/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..58ce4d1f06bc828033afa68fee73f95dca48557d --- /dev/null +++ b/source/RobotAPI/components/RobotDefinition/CMakeLists.txt @@ -0,0 +1,26 @@ +armarx_component_set_name("RobotDefinition") + +set(COMPONENT_LIBS + ArmarXCore + ArmarXCoreInterfaces # for DebugObserverInterface + ArmarXGuiComponentPlugins + RobotAPICore + RobotAPIInterfaces + RobotAPISkills + armem + robot_name_service_core + robot_name_service_client +) + +set(SOURCES + RobotDefinition.cpp +) + +set(HEADERS + RobotDefinition.h +) + +armarx_add_component("${SOURCES}" "${HEADERS}") + +#generate the application +armarx_generate_and_add_component_executable(COMPONENT_NAMESPACE "armarx") diff --git a/source/RobotAPI/components/RobotDefinition/RobotDefinition.cpp b/source/RobotAPI/components/RobotDefinition/RobotDefinition.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6a2df4ccd8d34174860acdb624b943ff232e98e5 --- /dev/null +++ b/source/RobotAPI/components/RobotDefinition/RobotDefinition.cpp @@ -0,0 +1,55 @@ +/* + * 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::RobotNameService + * @author [Author Name] ( [Author Email] ) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "RobotDefinition.h" + +namespace armarx +{ + void + RobotDefinition::onInitComponent() + { + } + + void + RobotDefinition::onConnectComponent() + { + } + + void + RobotDefinition::onDisconnectComponent() + { + } + + void + RobotDefinition::onExitComponent() + { + } + + armarx::PropertyDefinitionsPtr + RobotDefinition::createPropertyDefinitions() + { + armarx::PropertyDefinitionsPtr defs = + new ComponentPropertyDefinitions(getConfigIdentifier()); + + return defs; + } +} // namespace armarx diff --git a/source/RobotAPI/components/RobotDefinition/RobotDefinition.h b/source/RobotAPI/components/RobotDefinition/RobotDefinition.h new file mode 100644 index 0000000000000000000000000000000000000000..ba4eb80b97cc0a6ef049530c791f5e6a03c994a9 --- /dev/null +++ b/source/RobotAPI/components/RobotDefinition/RobotDefinition.h @@ -0,0 +1,88 @@ +/* + * 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::RobotNameService + * @author [Author Name] ( [Author Email] ) + * @date 2018 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <map> +#include <mutex> +#include <string> + +#include <ArmarXCore/core/Component.h> + +#include <RobotAPI/interface/robot_name_service/RobotNameServiceInterface.h> +#include <RobotAPI/libraries/robot_name_service/client/Plugin.h> +#include <RobotAPI/libraries/robot_name_service/core/Robot.h> + +namespace armarx +{ + /** + * @defgroup Component-RobotNameService RobotNameService + * @ingroup RobotAPI-Components + * A description of the component RobotNameService. + * + * @class RobotNameService + * @ingroup Component-RobotNameService + * @brief Brief description of class RobotNameService. + * + * Detailed description of class RobotNameService. + */ + class RobotDefinition : + virtual public armarx::Component, + virtual public armarx::RobotNameServiceComponentPluginUser + { + public: + /** + * @see armarx::ManagedIceObject::getDefaultName() + */ + std::string + getDefaultName() const override + { + return "RobotDefinition"; + } + + protected: + /** + * @see armarx::ManagedIceObject::onInitComponent() + */ + void onInitComponent() override; + + /** + * @see armarx::ManagedIceObject::onConnectComponent() + */ + void onConnectComponent() override; + + /** + * @see armarx::ManagedIceObject::onDisconnectComponent() + */ + void onDisconnectComponent() override; + + /** + * @see armarx::ManagedIceObject::onExitComponent() + */ + void onExitComponent() override; + + /** + * @see PropertyUser::createPropertyDefinitions() + */ + armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; + }; +} // namespace armarx diff --git a/source/RobotAPI/components/RobotDefinition/test/CMakeLists.txt b/source/RobotAPI/components/RobotDefinition/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..154bbec83350d9d49b761e5ad3845b9578612bf0 --- /dev/null +++ b/source/RobotAPI/components/RobotDefinition/test/CMakeLists.txt @@ -0,0 +1,5 @@ + +# Libs required for the tests +SET(LIBS ${LIBS} ArmarXCore RobotNameService) + +armarx_add_test(RobotNameServiceTest RobotNameServiceTest.cpp "${LIBS}") \ No newline at end of file diff --git a/source/RobotAPI/interface/components/RobotNameServiceInterface.ice b/source/RobotAPI/components/RobotDefinition/test/RobotNameServiceTest.cpp similarity index 63% rename from source/RobotAPI/interface/components/RobotNameServiceInterface.ice rename to source/RobotAPI/components/RobotDefinition/test/RobotNameServiceTest.cpp index 3478d40669895361a857ba972167bb85c131d0bc..6e3e6a973a04d105c2cf67478bab189ba7fbc2f4 100644 --- a/source/RobotAPI/interface/components/RobotNameServiceInterface.ice +++ b/source/RobotAPI/components/RobotDefinition/test/RobotNameServiceTest.cpp @@ -13,19 +13,25 @@ * 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 Simon Ottenhaus ( simon dot ottenhaus at kit dot edu ) + * @package RobotAPI::ArmarXObjects::RobotNameService + * @author [Author Name] ( [Author Email] ) * @date 2018 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt * GNU General Public License */ -#include <RobotAPI/interface/observers/KinematicUnitObserverInterface.ice> +#define BOOST_TEST_MODULE RobotAPI::ArmarXObjects::RobotNameService -#pragma once +#define ARMARX_BOOST_TEST -module armarx +#include <RobotAPI/Test.h> +#include <RobotAPI/components/RobotNameService/RobotNameService.h> + +#include <iostream> + +BOOST_AUTO_TEST_CASE(testExample) { - interface RobotNameServiceInterface - { - }; -}; + armarx::RobotNameService instance; + + BOOST_CHECK_EQUAL(true, true); +} diff --git a/source/RobotAPI/components/RobotNameService/CMakeLists.txt b/source/RobotAPI/components/RobotNameService/CMakeLists.txt index 1205ad25b0f143b5ca6af3719b44d4b3b6e07117..c362b8dab5b24f49a18c4a7d110e5dac956c8b2f 100644 --- a/source/RobotAPI/components/RobotNameService/CMakeLists.txt +++ b/source/RobotAPI/components/RobotNameService/CMakeLists.txt @@ -1,10 +1,26 @@ armarx_component_set_name("RobotNameService") -set(COMPONENT_LIBS ArmarXCore) -set(SOURCES RobotNameService.cpp) -set(HEADERS RobotNameService.h) -armarx_add_component("${SOURCES}" "${HEADERS}") -# add unit tests -add_subdirectory(test) +set(COMPONENT_LIBS + ArmarXCore + ArmarXCoreInterfaces # for DebugObserverInterface + ArmarXGuiComponentPlugins + RobotAPICore + RobotAPIInterfaces + RobotAPISkills + armem + robot_name_service_core + robot_name_service_server +) + +set(SOURCES + RobotNameService.cpp +) + +set(HEADERS + RobotNameService.h +) + +armarx_add_component("${SOURCES}" "${HEADERS}") -armarx_generate_and_add_component_executable(APPLICATION_APP_SUFFIX) +#generate the application +armarx_generate_and_add_component_executable(COMPONENT_NAMESPACE "armarx") diff --git a/source/RobotAPI/components/RobotNameService/RobotNameService.cpp b/source/RobotAPI/components/RobotNameService/RobotNameService.cpp index 65f97877de7dff1b4313fba2afde4838913488ca..9ab8e07d7ca5234e40e167ae73201cae04105db5 100644 --- a/source/RobotAPI/components/RobotNameService/RobotNameService.cpp +++ b/source/RobotAPI/components/RobotNameService/RobotNameService.cpp @@ -22,35 +22,76 @@ #include "RobotNameService.h" - namespace armarx { - void RobotNameService::onInitComponent() + void + RobotNameService::onInitComponent() { - } - - void RobotNameService::onConnectComponent() + void + RobotNameService::onConnectComponent() { + } + void + RobotNameService::onDisconnectComponent() + { } + void + RobotNameService::onExitComponent() + { + } - void RobotNameService::onDisconnectComponent() + armarx::PropertyDefinitionsPtr + RobotNameService::createPropertyDefinitions() { + armarx::PropertyDefinitionsPtr defs = + new ComponentPropertyDefinitions(getConfigIdentifier()); + return defs; } + bool + RobotNameService::registerRobot(const robot_name_service::dto::Robot& robot, + const Ice::Current& current) + { + std::scoped_lock l(robotsMutex); + ARMARX_INFO << "Register a new robot with name '" << robot.name << "' in RNS"; + + if (auto it = robots.find(robot.name); it != robots.end()) + { + ARMARX_ERROR << "The robot with name '" << robot.name + << "' is already registered. Ignoring it."; + return false; + } + + robots[robot.name].fromIce(robot); + return true; + } - void RobotNameService::onExitComponent() + void + RobotNameService::unregisterRobot(const std::string& name, const Ice::Current& current) { + std::scoped_lock l(robotsMutex); + if (auto it = robots.find(name); it != robots.end()) + { + robots.erase(it); + } } - armarx::PropertyDefinitionsPtr RobotNameService::createPropertyDefinitions() + IceUtil::Optional<robot_name_service::dto::Robot> + RobotNameService::getRobot(const std::string& name, const Ice::Current& current) { - return armarx::PropertyDefinitionsPtr(new RobotNameServicePropertyDefinitions( - getConfigIdentifier())); + std::scoped_lock l(robotsMutex); + + if (auto it = robots.find(name); it == robots.end()) + { + return {}; + } + + return robots[name].toIce(); } -} +} // namespace armarx diff --git a/source/RobotAPI/components/RobotNameService/RobotNameService.h b/source/RobotAPI/components/RobotNameService/RobotNameService.h index 92f5942342302f8fcb9db72830179552cc0188f8..7af787aef845a26c8ed47f7b9351c36cd6dce002 100644 --- a/source/RobotAPI/components/RobotNameService/RobotNameService.h +++ b/source/RobotAPI/components/RobotNameService/RobotNameService.h @@ -22,27 +22,17 @@ #pragma once +#include <map> +#include <mutex> +#include <string> #include <ArmarXCore/core/Component.h> +#include <RobotAPI/interface/robot_name_service/RobotNameServiceInterface.h> +#include <RobotAPI/libraries/robot_name_service/core/Robot.h> + namespace armarx { - /** - * @class RobotNameServicePropertyDefinitions - * @brief - */ - class RobotNameServicePropertyDefinitions: - public armarx::ComponentPropertyDefinitions - { - public: - RobotNameServicePropertyDefinitions(std::string prefix): - armarx::ComponentPropertyDefinitions(prefix) - { - //defineRequiredProperty<std::string>("PropertyName", "Description"); - //defineOptionalProperty<std::string>("PropertyName", "DefaultValue", "Description"); - } - }; - /** * @defgroup Component-RobotNameService RobotNameService * @ingroup RobotAPI-Components @@ -55,13 +45,15 @@ namespace armarx * Detailed description of class RobotNameService. */ class RobotNameService : - virtual public armarx::Component + virtual public armarx::Component, + virtual public armarx::robot_name_service::dti::RobotNameServiceInterface { public: /** * @see armarx::ManagedIceObject::getDefaultName() */ - std::string getDefaultName() const override + std::string + getDefaultName() const override { return "RobotNameService"; } @@ -91,6 +83,18 @@ namespace armarx * @see PropertyUser::createPropertyDefinitions() */ armarx::PropertyDefinitionsPtr createPropertyDefinitions() override; - }; -} + + // RobotNameServiceInterface interface + public: + bool registerRobot(const robot_name_service::dto::Robot& robot, + const Ice::Current& current) override; + void unregisterRobot(const std::string& name, const Ice::Current& current) override; + IceUtil::Optional<robot_name_service::dto::Robot> + getRobot(const std::string& name, const Ice::Current& current) override; + + private: + mutable std::mutex robotsMutex; + std::map<std::string, robot_name_service::core::Robot> robots; + }; +} // namespace armarx diff --git a/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp b/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp index d57f66c3a0e682d382dc91052c9a45b7acb639e7..edf918402a4c8ed1bbe510ab553d6dc568540dd5 100644 --- a/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp +++ b/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp @@ -20,6 +20,7 @@ namespace armarx::skills::provider root_profile_params.some_float = 5; root_profile_params.some_int = 42; root_profile_params.some_text = "YOLO"; + root_profile_params.some_optional_text = "OPTIONAL"; root_profile_params.some_list_of_matrices.push_back(Eigen::Matrix3f::Zero()); root_profile_params.some_matrix = Eigen::Matrix3f::Zero(); diff --git a/source/RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.xml b/source/RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.xml index e90781ca62d7ef67751ed79b7556ab2c61f160a5..10c81ab542736b5a06bcd605f95353a619c3ad25 100644 --- a/source/RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.xml +++ b/source/RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.xml @@ -22,6 +22,9 @@ <ObjectChild key='some_text'> <String /> </ObjectChild> + <ObjectChild key='some_optional_text'> + <String optional="True" /> + </ObjectChild> <ObjectChild key='some_list_of_matrices'> <List> diff --git a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp index 7c7cfae4d2fba28158d7bbe6cb2a3cb89fd5c195..f8a4b50ece99f95d8d4c618b377774e289ec4c03 100644 --- a/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp +++ b/source/RobotAPI/gui-plugins/KinematicUnitPlugin/KinematicUnitGuiPlugin.cpp @@ -651,7 +651,7 @@ namespace armarx { if (currentNode) { - if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint()) + if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or currentNode->isFourBarJoint()) { const bool isDeg = ui.checkBoxUseDegree->isChecked(); const auto factor = @@ -728,7 +728,7 @@ namespace armarx { const QString unit = [&]() -> QString { - if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint()) + if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or currentNode->isFourBarJoint()) { if (ui.checkBoxUseDegree->isChecked()) { @@ -751,7 +751,7 @@ namespace armarx const auto [factor, conversionFactor] = [&]() -> std::pair<float, float> { - if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint()) + if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or currentNode->isFourBarJoint()) { const bool isDeg = ui.checkBoxUseDegree->isChecked(); if (isDeg) @@ -841,7 +841,7 @@ namespace armarx const QString unit = [&]() -> QString { - if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint()) + if (currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or currentNode->isFourBarJoint()) { if (ui.checkBoxUseDegree->isChecked()) { @@ -865,7 +865,7 @@ namespace armarx << currentNode->getName() << flush; const bool isDeg = ui.checkBoxUseDegree->isChecked(); - const bool isRot = currentNode->isRotationalJoint() or currentNode->isHemisphereJoint(); + const bool isRot = currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or currentNode->isFourBarJoint(); const float lo = isRot ? (isDeg ? -90 : -M_PI * 100) : -1000; const float hi = isRot ? (isDeg ? +90 : +M_PI * 100) : 1000; @@ -1186,7 +1186,7 @@ namespace armarx const ControlMode currentControlMode = getSelectedControlMode(); const bool isDeg = ui.checkBoxUseDegree->isChecked(); - const bool isRot = currentNode->isRotationalJoint() or currentNode->isHemisphereJoint(); + const bool isRot = currentNode->isRotationalJoint() or currentNode->isHemisphereJoint() or currentNode->isFourBarJoint(); if (currentControlMode == ePositionControl) { @@ -1361,11 +1361,11 @@ namespace armarx newItem = new QTableWidgetItem(state); ui.tableJointList->setItem(i, eTabelColumnError, newItem); - state = currentStatus.enabled ? "X" : "-"; + state = currentStatus.enabled ? "yes" : "no"; newItem = new QTableWidgetItem(state); ui.tableJointList->setItem(i, eTabelColumnEnabled, newItem); - state = currentStatus.emergencyStop ? "X" : "-"; + state = currentStatus.emergencyStop ? "yes" : "no"; newItem = new QTableWidgetItem(state); ui.tableJointList->setItem(i, eTabelColumnEmergencyStop, newItem); } @@ -1443,7 +1443,7 @@ namespace armarx QModelIndex index = ui.tableJointList->model()->index(i, eTabelColumnAngleProgressbar); float conversionFactor = ui.checkBoxUseDegree->isChecked() && - (node->isRotationalJoint() or node->isHemisphereJoint()) + (node->isRotationalJoint() or node->isHemisphereJoint() or node->isFourBarJoint()) ? 180.0 / M_PI : 1; ui.tableJointList->model()->setData( @@ -1486,7 +1486,7 @@ namespace armarx float currentValue = it->second; if (ui.checkBoxUseDegree->isChecked() && - (rn[i]->isRotationalJoint() or rn[i]->isHemisphereJoint())) + (rn[i]->isRotationalJoint() or rn[i]->isHemisphereJoint() or rn[i]->isFourBarJoint())) { currentValue *= 180.0 / M_PI; } diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt b/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt index a6d2ff63c1462557b5c55fc9538228dc46509ef1..113ccd8706d8f7c64cbd067f1002a5969b3f9507 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/CMakeLists.txt @@ -10,12 +10,13 @@ set(SOURCES aronTreeWidget/visitors/AronTreeWidgetSetter.cpp aronTreeWidget/visitors/AronTreeWidgetModalCreator.cpp aronTreeWidget/visitors/AronTreeWidgetContextMenu.cpp - aronTreeWidget/Data.cpp aronTreeWidget/widgets/CustomWidget.cpp aronTreeWidget/widgets/EditMatrixWidget.cpp aronTreeWidget/widgets/IntEnumWidget.cpp - aronTreeWidget/ListDictHelper.cpp aronTreeWidget/widgets/QuaternionWidget.cpp + aronTreeWidget/widgets/SkillDescriptionWidget.cpp + aronTreeWidget/Data.cpp + aronTreeWidget/ListDictHelper.cpp aronTreeWidget/AronTreeWidgetItem.cpp aronTreeWidget/AronTreeWidgetController.cpp aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.cpp @@ -34,13 +35,14 @@ set(HEADERS aronTreeWidget/visitors/AronTreeWidgetSetter.h aronTreeWidget/visitors/AronTreeWidgetModalCreator.h aronTreeWidget/visitors/AronTreeWidgetContextMenu.h - aronTreeWidget/Data.h aronTreeWidget/widgets/NDArrayHelper.h aronTreeWidget/widgets/EditMatrixWidget.h aronTreeWidget/widgets/CustomWidget.h aronTreeWidget/widgets/IntEnumWidget.h - aronTreeWidget/ListDictHelper.h aronTreeWidget/widgets/QuaternionWidget.h + aronTreeWidget/widgets/SkillDescriptionWidget.h + aronTreeWidget/Data.h + aronTreeWidget/ListDictHelper.h aronTreeWidget/AronTreeWidgetItem.h aronTreeWidget/AronTreeWidgetController.h aronTreeWidget/modal/AronTreeWidgetModal.h diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui index b96d3850e2c7181892e22030c600157963fb41af..83e522b9d6f895b6f492ba63d13529712a3f194d 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidget.ui @@ -20,6 +20,9 @@ <string>SkillManagerMonitorWidget</string> </property> <layout class="QGridLayout" name="gridLayout_3"> + <item row="3" column="2"> + <widget class="QDoubleSpinBox" name="doubleSpinBoxUpdateFreq"/> + </item> <item row="3" column="0"> <widget class="QCheckBox" name="checkBoxAutoUpdate"> <property name="text"> @@ -27,16 +30,6 @@ </property> </widget> </item> - <item row="3" column="3"> - <widget class="QPushButton" name="pushButtonRefreshNow"> - <property name="text"> - <string>Refresh Now</string> - </property> - </widget> - </item> - <item row="3" column="2"> - <widget class="QDoubleSpinBox" name="doubleSpinBoxUpdateFreq"/> - </item> <item row="3" column="1"> <widget class="QLabel" name="label"> <property name="text"> @@ -47,6 +40,13 @@ </property> </widget> </item> + <item row="3" column="3"> + <widget class="QPushButton" name="pushButtonRefreshNow"> + <property name="text"> + <string>Refresh Now</string> + </property> + </widget> + </item> <item row="4" column="0" colspan="4"> <widget class="QSplitter" name="splitter_2"> <property name="enabled"> @@ -190,72 +190,137 @@ <property name="title"> <string>Skill Details</string> </property> - <layout class="QGridLayout" name="gridLayout_2"> - <item row="8" column="3"> - <widget class="QPushButton" name="pushButtonExecuteSkill"> - <property name="text"> - <string>Request Execution</string> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QSplitter" name="splitter_2"> + <property name="enabled"> + <bool>true</bool> </property> - </widget> - </item> - <item row="0" column="0"> - <widget class="QPushButton" name="pushButtonPaste"> - <property name="text"> - <string>Set args from clipboard</string> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - </widget> - </item> - <item row="0" column="3"> - <widget class="QPushButton" name="pushButtonReset"> - <property name="text"> - <string>Reset args to profile</string> - </property> - </widget> - </item> - <item row="3" column="0" colspan="4"> - <widget class="QTreeWidget" name="treeWidgetSkillDetails"> - <property name="contextMenuPolicy"> - <enum>Qt::CustomContextMenu</enum> + <property name="orientation"> + <enum>Qt::Vertical</enum> </property> - <property name="editTriggers"> - <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> + <property name="childrenCollapsible"> + <bool>false</bool> </property> - <column> - <property name="text"> - <string>Key</string> + <widget class="QWidget" name="groupBoxSkillDetailsTop" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="leftMargin"> + <number>0</number> </property> - </column> - <column> - <property name="text"> - <string>Value</string> + <property name="topMargin"> + <number>0</number> </property> - </column> - <column> - <property name="text"> - <string>Type</string> + <property name="rightMargin"> + <number>0</number> </property> - </column> - <column> - <property name="text"> - <string>defaultValue (hidden in GUI)</string> + <property name="bottomMargin"> + <number>0</number> </property> - </column> - </widget> - </item> - <item row="0" column="1"> - <widget class="QPushButton" name="pushButtonCopy"> - <property name="text"> - <string>Copy args to clipboard</string> - </property> + <item> + <layout class="QGridLayout" name="gridLayout_5"> + <item row="0" column="0"> + <widget class="QPushButton" name="pushButtonPaste"> + <property name="text"> + <string>Set args from clipboard</string> + </property> + </widget> + </item> + <item row="0" column="3"> + <widget class="QPushButton" name="pushButtonReset"> + <property name="text"> + <string>Reset args to profile</string> + </property> + </widget> + </item> + <item row="1" column="0" colspan="4"> + <widget class="QComboBox" name="comboBoxProfiles"> + <item> + <property name="text"> + <string><No Profile selected. Using root></string> + </property> + </item> + </widget> + </item> + <item row="0" column="1"> + <widget class="QPushButton" name="pushButtonCopy"> + <property name="text"> + <string>Copy args to clipboard</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QWidget" name="skillDescription" native="true"/> + </item> + </layout> </widget> - </item> - <item row="1" column="1" colspan="3"> - <widget class="QComboBox" name="comboBoxProfiles"> - <item> - <property name="text"> - <string><No Profile selected. Using root></string> - </property> - </item> + <widget class="QWidget" name="groupBoxSkillDetailsBottom" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QTreeWidget" name="treeWidgetSkillDetails"> + <property name="contextMenuPolicy"> + <enum>Qt::CustomContextMenu</enum> + </property> + <property name="editTriggers"> + <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed</set> + </property> + <column> + <property name="text"> + <string>Key</string> + </property> + </column> + <column> + <property name="text"> + <string>Value</string> + </property> + </column> + <column> + <property name="text"> + <string>Type</string> + </property> + </column> + <column> + <property name="text"> + <string>defaultValue (hidden in GUI)</string> + </property> + </column> + </widget> + </item> + <item> + <widget class="QPushButton" name="pushButtonExecuteSkill"> + <property name="text"> + <string>Request Execution</string> + </property> + </widget> + </item> + </layout> + </widget> </widget> </item> </layout> diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp index 191c160194207e561a1be7582c038361299a81ac..8b88718f93dc71bae29b16094d9701c0ae7d896a 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp @@ -24,25 +24,22 @@ #include <string> -#include <RobotAPI/libraries/skills/core/Skill.h> - -#include "aronTreeWidget/visitors/AronTreeWidgetConverter.h" -#include "aronTreeWidget/visitors/AronTreeWidgetCreator.h" -#include "aronTreeWidget/visitors/AronTreeWidgetModalCreator.h" - -// modals -#include "aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.h" - -// debug #include <QClipboard> #include <QDoubleSpinBox> +#include <QGridLayout> +#include <QTextBrowser> #include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> +#include <RobotAPI/libraries/skills/core/Skill.h> #include <RobotAPI/libraries/skills/core/SkillExecutionRequest.h> #include "aronTreeWidget/Data.h" +#include "aronTreeWidget/modal/text/AronTreeWidgetTextInputModalController.h" +#include "aronTreeWidget/visitors/AronTreeWidgetConverter.h" +#include "aronTreeWidget/visitors/AronTreeWidgetCreator.h" +#include "aronTreeWidget/visitors/AronTreeWidgetModalCreator.h" +#include "aronTreeWidget/widgets/SkillDescriptionWidget.h" -//configSk namespace armarx { QPointer<QDialog> @@ -112,6 +109,12 @@ namespace armarx widget.doubleSpinBoxUpdateFreq->setSingleStep(0.5); widget.doubleSpinBoxUpdateFreq->setSuffix(" Hz"); + skillDescriptionWidget = new SkillDescriptionWidget(); + widget.skillDescription->parentWidget()->layout()->replaceWidget(widget.skillDescription, + skillDescriptionWidget); + widget.skillDescription = skillDescriptionWidget; + + refreshSkillsResultTimer = new QTimer(this); updateTimerFrequency(); refreshSkillsResultTimer->start(); @@ -502,7 +505,9 @@ namespace armarx if (!current->parent()) { - // no parent available. Perhaps provider clicked? + // no parent available. Perhaps provider clicked? Reset selected skill. + selectedSkill.skillId.providerId->providerName = ""; + selectedSkill.skillId.skillName = ""; return; } @@ -532,28 +537,7 @@ namespace armarx ARMARX_CHECK(skills.at(*selectedSkill.skillId.providerId).count(selectedSkill.skillId) > 0); auto skillDesc = skills.at(*selectedSkill.skillId.providerId).at(selectedSkill.skillId); - { - auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails, - {QString::fromStdString("Name"), - QString::fromStdString(skillDesc.skillId.skillName)}); - widget.treeWidgetSkillDetails->addTopLevelItem(it); - } - - { - auto it = new QTreeWidgetItem(widget.treeWidgetSkillDetails, - {QString::fromStdString("Description"), - QString::fromStdString(skillDesc.description)}); - widget.treeWidgetSkillDetails->addTopLevelItem(it); - } - - { - auto it = new QTreeWidgetItem( - widget.treeWidgetSkillDetails, - {QString::fromStdString("Timeout"), - QString::fromStdString(std::to_string(skillDesc.timeout.toMilliSeconds())) + - " ms"}); - widget.treeWidgetSkillDetails->addTopLevelItem(it); - } + skillDescriptionWidget->setSkillDescription(skillDesc); // select root profile widget.comboBoxProfiles->setCurrentIndex(0); diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h index 1fd2e2f110fe8d34890f8d685c8faa5bc2031b70..9f251bf9e1e5890d9059836fa12017fca9264925 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.h @@ -46,6 +46,8 @@ namespace armarx { + class SkillDescriptionWidget; + class SkillInfoTreeWidgetItem : public QTreeWidgetItem { public: @@ -166,6 +168,7 @@ namespace armarx // others QTimer* refreshSkillsResultTimer; + SkillDescriptionWidget* skillDescriptionWidget = nullptr; // connected flag std::atomic_bool connected = false; diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetConverter.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetConverter.cpp index 2e52ed4cf4854583641d1a83f85822096d8f5226..5c3db21fbe8604c9842cb834565274a4294e6233 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetConverter.cpp +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetConverter.cpp @@ -90,6 +90,16 @@ namespace armarx createdAron = createdAronDict; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + unsigned int x = 0; for (const auto& [key, value] : i->getMemberTypes()) { @@ -113,6 +123,16 @@ namespace armarx createdAron = createdAronDict; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + for (int x = 0; x < el->childCount(); ++x) { auto it = el->child(x); @@ -134,12 +154,23 @@ namespace armarx ARMARX_TRACE; auto createdAronList = std::make_shared<aron::data::List>(i->getPath()); createdAron = createdAronList; - auto* elem = parentItem->child(index); + auto* el = parentItem->child(index); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + auto childrenTypes = i->getChildren(); ARMARX_CHECK(childrenTypes.size() == 1); - for (int j = 0; j < elem->childCount(); ++j) + for (int j = 0; j < el->childCount(); ++j) { - AronTreeWidgetConverterVisitor convVisitor(elem, j); + AronTreeWidgetConverterVisitor convVisitor(el, j); aron::type::visit(convVisitor, childrenTypes[0]); handleErrors(convVisitor); @@ -156,11 +187,21 @@ namespace armarx ARMARX_TRACE; auto createdAronPair = std::make_shared<aron::data::List>(i->getPath()); createdAron = createdAronPair; - auto* elem = parentItem->child(index); + auto* el = parentItem->child(index); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } for (int j = 0; j < 2; ++j) { - AronTreeWidgetConverterVisitor convVisitor(elem, j); + AronTreeWidgetConverterVisitor convVisitor(el, j); handleErrors(convVisitor); if (convVisitor.createdAron && convVisitor.isConversionSuccessful()) { @@ -177,6 +218,16 @@ namespace armarx createdAron = createdAronList; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + for (int x = 0; x < el->childCount(); ++x) { auto* it = el->child(x); @@ -242,11 +293,22 @@ namespace armarx createdMatrix->setShape({i->getRows(), i->getCols(), dataSize}); int totalByteSize = i->getRows() * i->getCols() * dataSize; createdAron = createdMatrix; + auto* el = parentItem->child(index); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } - auto* currElem = parentItem->child(index); - auto* rootWidget = currElem->treeWidget(); + + auto* rootWidget = el->treeWidget(); ARMARX_CHECK(rootWidget); - auto* widget = rootWidget->itemWidget(currElem, 1); + auto* widget = rootWidget->itemWidget(el, 1); auto* matrixWidget = EditMatrixWidget::DynamicCastAndCheck(widget); handleErrors(matrixWidget->hasParseErrors()); @@ -280,8 +342,19 @@ namespace armarx int dataSize = i->getElementType() == aron::type::quaternion::ElementType::FLOAT32 ? 4 : 8; createdQuat->setShape({1, 4, dataSize}); createdQuat->setType(i->getFullName()); - auto* currTreeElem = parentItem->child(index); - auto* itemWidget = currTreeElem->treeWidget()->itemWidget(currTreeElem, 1); + auto* el = parentItem->child(index); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + + auto* itemWidget = el->treeWidget()->itemWidget(el, 1); auto* quatWidget = QuaternionWidget::DynamicCastAndCheck(itemWidget); // error handling @@ -320,6 +393,17 @@ namespace armarx { ARMARX_TRACE; QTreeWidgetItem* el = parentItem->child(index); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + auto* genericWidget = el->treeWidget()->itemWidget(el, 1); auto* intEnumWidget = IntEnumWidget::DynamicCastAndCheck(genericWidget); if (!intEnumWidget) @@ -341,6 +425,16 @@ namespace armarx createdAron = createdAronInt; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + std::string str = el->text(1).toStdString(); if (str.empty()) { @@ -370,6 +464,16 @@ namespace armarx createdAron = createdAronLong; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + std::string str = el->text(1).toStdString(); if (str.empty()) { @@ -398,6 +502,16 @@ namespace armarx createdAron = createdAronFloat; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + std::string str = el->text(1).toStdString(); if (str.empty()) { @@ -425,6 +539,16 @@ namespace armarx createdAron = createdAronDouble; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + std::string str = el->text(1).toStdString(); if (str.empty()) { @@ -452,6 +576,16 @@ namespace armarx createdAron = createdAronBool; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + std::string str = el->text(1).toStdString(); if (str.empty()) { @@ -479,6 +613,16 @@ namespace armarx createdAron = createdAronString; QTreeWidgetItem* el = parentItem->child(index); + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + // its a maybetype. We have to check the state + if (el->checkState(1) == Qt::CheckState::Unchecked) + { + createdAron = nullptr; + return; + } + } + std::string str = el->text(1).toStdString(); createdAronString->setValue(str); } diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetCreator.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetCreator.cpp index 84cb58585e3b4e7f3a058db66f3b5f3a40f54aea..5bc5a40bbd407c104ce96905850e9d0beabecb48 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetCreator.cpp +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetCreator.cpp @@ -101,6 +101,11 @@ namespace armarx createdQWidgetItem = new AronTreeWidgetItem(isDictChild, editableValue, QString::fromStdString(key), i); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + createdQWidgetItem->setCheckState(1, Qt::CheckState::Unchecked); + } createdQWidgetItem->setText(1, QString::fromStdString(defaul)); createdQWidgetItem->setText(2, QString::fromStdString(i->getShortName())); createdQWidgetItem->setText( @@ -129,6 +134,12 @@ namespace armarx createdQWidgetItem = new AronTreeWidgetItem(editableValue, false, QString::fromStdString(key), i); + + if (i->getMaybe() != armarx::aron::type::Maybe::NONE) + { + createdQWidgetItem->setCheckState(1, Qt::CheckState::Unchecked); + } + parentOfCreatedObj->addChild(createdQWidgetItem); for (const auto& [key, value] : i->getMemberTypes()) @@ -149,6 +160,7 @@ namespace armarx // The DictType has only one member, its key-type. This also must be present ARMARX_CHECK_EQUAL(i->getChildren().size(), 1); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::PairPtr& pair) { @@ -167,6 +179,7 @@ namespace armarx } } } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::TuplePtr& tuple) { @@ -185,6 +198,7 @@ namespace armarx } } } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::ListPtr& i) { @@ -192,6 +206,7 @@ namespace armarx auto txt = misc::generateNumElementsText(0); createdQWidgetItem->setText(1, txt); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::NDArrayPtr& i) { @@ -233,6 +248,7 @@ namespace armarx i->getRows(), i->getCols(), i->getElementType(), createdQWidgetItem); toplevelWidget->setItemWidget(createdQWidgetItem, 1, matWidget); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::QuaternionPtr& i) { @@ -257,16 +273,19 @@ namespace armarx new QuaternionWidget(i->getElementType(), createdQWidgetItem); toplevelWidget->setItemWidget(createdQWidgetItem, 1, quatWidget); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::ImagePtr& i) { insertNewTreeViewWidget(i, ""); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::PointCloudPtr& i) { insertNewTreeViewWidget(i, ""); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::IntEnumPtr& i) { @@ -278,36 +297,42 @@ namespace armarx IntEnumWidget* widget = new IntEnumWidget(i, createdQWidgetItem); createdQWidgetItem->treeWidget()->setItemWidget(createdQWidgetItem, 1, widget); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::IntPtr& i) { editableValue = true; insertNewTreeViewWidget(i, "0"); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::LongPtr& i) { editableValue = true; insertNewTreeViewWidget(i, "0"); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::FloatPtr& i) { editableValue = true; insertNewTreeViewWidget(i, "0.0"); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::DoublePtr& i) { editableValue = true; insertNewTreeViewWidget(i, "0.0"); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::BoolPtr& i) { editableValue = true; insertNewTreeViewWidget(i, "false"); } + void AronTreeWidgetCreatorVisitor::visitAronVariant(const aron::type::StringPtr& i) { diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetSetter.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetSetter.cpp index cf719f8a2d1360d574fb8ebcac867378c3fd6f1b..cafc9b499b45a587efcc7a52ca531e028a8391df 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetSetter.cpp +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/visitors/AronTreeWidgetSetter.cpp @@ -31,7 +31,6 @@ #include "AronTreeWidgetContextMenu.h" #include "AronTreeWidgetCreator.h" - template <typename T> std::string usString(T number, size_t precision = 3) @@ -44,7 +43,6 @@ usString(T number, size_t precision = 3) return ss.str(); } - //visitors namespace armarx { @@ -101,7 +99,6 @@ namespace armarx } } - void AronTreeWidgetSetterVisitor::visitAronVariant(const aron::data::DictPtr& i) { @@ -109,7 +106,7 @@ namespace armarx if (i->getPath().size() == 0 || checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); auto* aronTreeWidget = AronTreeWidgetItem::DynamicCastAndCheck(el); // allocate enough child items adjustNumberOfChildren(aronTreeWidget, i->childrenSize()); @@ -123,7 +120,11 @@ namespace armarx AronTreeWidgetSetterVisitor v(el, x++); aron::data::visit(v, value); } - return; + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } @@ -132,7 +133,7 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); auto* aronTreeWidget = AronTreeWidgetItem::DynamicCastAndCheck(el); adjustNumberOfChildren(aronTreeWidget, i->childrenSize()); @@ -154,10 +155,14 @@ namespace armarx auto currFont = aronTreeWidget->font(1); currFont.setItalic(true); aronTreeWidget->setFont(1, currFont); + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } - void visitMatrix(EditMatrixWidget* matrixWidget, const std::shared_ptr<armarx::aron::type::Matrix>& matrixType, @@ -215,6 +220,7 @@ namespace armarx } } } + void visitQuaternion(QuaternionWidget* quatWidget, std::shared_ptr<armarx::aron::type::Quaternion>& quatType, @@ -256,16 +262,16 @@ namespace armarx AronTreeWidgetSetterVisitor::visitAronVariant(const aron::data::NDArrayPtr& arr) { // Matrices are handled as NDArray. Raw ndarrays cannot be created currently - auto* castedEl = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); - ARMARX_CHECK(castedEl); + auto* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); + ARMARX_CHECK(el); - auto matrixCast = aron::type::Matrix::DynamicCast(castedEl->aronType); - auto quaternionCast = aron::type::Quaternion::DynamicCast(castedEl->aronType); + auto matrixCast = aron::type::Matrix::DynamicCast(el->aronType); + auto quaternionCast = aron::type::Quaternion::DynamicCast(el->aronType); - auto* rootWidget = castedEl->treeWidget(); + auto* rootWidget = el->treeWidget(); ARMARX_CHECK(rootWidget); - auto* matrixWidget = EditMatrixWidget::DynamicCast(rootWidget->itemWidget(castedEl, 1)); - auto* quaternionWidget = QuaternionWidget::DynamicCast(rootWidget->itemWidget(castedEl, 1)); + auto* matrixWidget = EditMatrixWidget::DynamicCast(rootWidget->itemWidget(el, 1)); + auto* quaternionWidget = QuaternionWidget::DynamicCast(rootWidget->itemWidget(el, 1)); if (matrixCast && matrixWidget) { @@ -280,6 +286,11 @@ namespace armarx ARMARX_ERROR << "we do not support raw NDArrays. Ask Fabian Peller for more information."; } + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } void @@ -287,7 +298,7 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); auto* enumWidget = IntEnumWidget::DynamicCast(el->treeWidget()->itemWidget(el, 1)); auto newText = QString::fromStdString(usString<int>(i->getValue())); if (enumWidget) @@ -300,6 +311,11 @@ namespace armarx // Its just an int. -> do the QTreeWidgetItem call el->setText(1, newText); } + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } @@ -308,8 +324,13 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); el->setText(1, QString::fromStdString(usString<long>(i->getValue()))); + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } @@ -318,8 +339,13 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); el->setText(1, QString::fromStdString(usString<float>(i->getValue()))); + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } @@ -328,8 +354,13 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); el->setText(1, QString::fromStdString(usString<double>(i->getValue()))); + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } @@ -338,8 +369,13 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); el->setText(1, QString::fromStdString(usString<bool>(i->getValue()))); + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } @@ -348,8 +384,13 @@ namespace armarx { if (checkTreeWidgetItemForSimilarName(i->getPath().getLastElement())) { - QTreeWidgetItem* el = parentItem->child(index); + AronTreeWidgetItem* el = AronTreeWidgetItem::DynamicCast(parentItem->child(index)); el->setText(1, QString::fromStdString(i->getValue())); + + if (el->aronType && el->aronType->getMaybe() != armarx::aron::type::Maybe::NONE) + { + el->setCheckState(1, Qt::CheckState::Checked); + } } } diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60f5908c771cc6bfc5f3e5080445ec8321b613ac --- /dev/null +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.cpp @@ -0,0 +1,72 @@ +#include "SkillDescriptionWidget.h" + +#include <sstream> + +#include <QGridLayout> +#include <QLabel> +#include <QTextEdit> + +#include <ArmarXGui/libraries/ArmarXGuiBase/widgets/cpp-markdown/markdown.h> // ToDo: Move cpp-markdown to own Axii module. + +namespace armarx +{ + + static std::string + markdownToHtml(const std::string& markdownText, size_t spacesPerTab = 2) + { + ::markdown::Document document(spacesPerTab); + document.read(markdownText); + + std::stringstream html; + document.write(html); + return html.str(); + } + + SkillDescriptionWidget::SkillDescriptionWidget(QWidget* parent) : QWidget{parent} + { + QGridLayout* layout = new QGridLayout; + setLayout(layout); + + layout->setMargin(0); + + int row = 0; + int col = 0; + + layout->addWidget(new QLabel("Name:"), row, col++); + name = new QLabel; + layout->addWidget(name, row, col++); + + // Add some padding. + layout->addWidget(new QLabel(), row, col++); + + { + QLabel* label = new QLabel("Timeout:"); + label->setAlignment(Qt::AlignmentFlag::AlignRight); + layout->addWidget(label, row, col++); + } + timeout = new QLabel; + timeout->setAlignment(Qt::AlignmentFlag::AlignRight); + layout->addWidget(timeout, row, col++); + + ++row; + int numCols = col; + col = 0; + + description = new QTextEdit; + description->setReadOnly(true); + layout->addWidget(description, row, col, 1, numCols); + ++row; + } + + void + SkillDescriptionWidget::setSkillDescription(const skills::SkillDescription& desc) + { + name->setText(QString::fromStdString(desc.skillId.skillName)); + description->setHtml(QString::fromStdString(markdownToHtml(desc.description))); + + std::stringstream timeoutStr; + timeoutStr << desc.timeout; + timeout->setText(QString::fromStdString(timeoutStr.str())); + } + +} // namespace armarx diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..b58f06d0d6dc65893c2e7f65a4e768ef435d7201 --- /dev/null +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/aronTreeWidget/widgets/SkillDescriptionWidget.h @@ -0,0 +1,27 @@ +#pragma once + +#include <QWidget> + +#include <RobotAPI/libraries/skills/core/SkillDescription.h> + +class QLabel; +class QTextEdit; + +namespace armarx +{ + + class SkillDescriptionWidget : public QWidget + { + public: + SkillDescriptionWidget(QWidget* parent = nullptr); + + void setSkillDescription(const skills::SkillDescription& desc); + + private: + QLabel* name = nullptr; + QTextEdit* description = nullptr; + QLabel* timeout = nullptr; + }; + + +} // namespace armarx diff --git a/source/RobotAPI/interface/CMakeLists.txt b/source/RobotAPI/interface/CMakeLists.txt index 9e290598fee8047f853044e54ea3775b62ab49f7..d8f626d9fa4a752cd65eb7cbc95b3074e47c2638 100644 --- a/source/RobotAPI/interface/CMakeLists.txt +++ b/source/RobotAPI/interface/CMakeLists.txt @@ -92,7 +92,6 @@ set(SLICE_FILES # Disabled for being unstable. To be replaced by skills/view_selection. Use GazeControl of ActiveVision instead. # components/FrameTrackingInterface.ice components/RobotHealthInterface.ice - components/RobotNameServiceInterface.ice components/TrajectoryPlayerInterface.ice components/ViewSelectionInterface.ice @@ -152,6 +151,9 @@ set(SLICE_FILES skills/SkillProviderInterface.ice mdb/MotionDatabase.ice + + # RobotAPI + robot_name_service/RobotNameServiceInterface.ice ) #core/RobotIK.ice set(SLICE_FILES_ADDITIONAL_HEADERS diff --git a/source/RobotAPI/interface/robot_name_service/RobotNameServiceInterface.ice b/source/RobotAPI/interface/robot_name_service/RobotNameServiceInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..7891ed5dbfd4583eb592ebf8913d9c12ca3e9a4c --- /dev/null +++ b/source/RobotAPI/interface/robot_name_service/RobotNameServiceInterface.ice @@ -0,0 +1,90 @@ +/* + * This file is part of ArmarX. + * + * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved. + * + * 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 + * @author + * @date + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ +#pragma once + +#include <ArmarXCore/interface/core/PackagePath.ice> + +#include <RobotAPI/interface/armem/mns.ice> +#include <RobotAPI/interface/skills/SkillManagerInterface.ice> +#include <RobotAPI/interface/units/HandUnitInterface.ice> +#include <RobotAPI/interface/units/KinematicUnitInterface.ice> +#include <RobotAPI/interface/units/LocalizationUnitInterface.ice> +#include <RobotAPI/interface/units/PlatformUnitInterface.ice> + +module armarx +{ + module robot_name_service + { + module dto + { + + struct Hand + { + string name; + string ft_name; + HandUnitInterface* handUnitInterface; + }; + + dictionary<string, Hand> Hands; + + struct Arm + { + string kinematicChainName; + Hand hand; + }; + + dictionary<string, Arm> Arms; + + struct Robot + { + string name; + armarx::data::PackagePath xmlPackagePath; + + // Memory and skills. MAY BE NULL + armarx::armem::mns::MemoryNameSystemInterface* memoryNameSystem; + armarx::skills::manager::dti::SkillManagerInterface* skillManager; + + // kinematic stuff. MAY BE EMPTY + Arms arms; + + // units. MAY BE NULL + armarx::KinematicUnitInterface* kinematicUnitInterface; + armarx::PlatformUnitInterface* platformUnitInterface; + armarx::LocalizationUnitInterface* localizationUnitInterface; + + // TODO: Feel free to extend! + }; + }; + + module dti + { + interface RobotNameServiceInterface + { + bool registerRobot(dto::Robot robot); + void unregisterRobot(string name); + optional(1) dto::Robot getRobot(string name); + }; + }; + }; +}; diff --git a/source/RobotAPI/libraries/CMakeLists.txt b/source/RobotAPI/libraries/CMakeLists.txt index 057440c94c97f8428c191c2d9c7a83edc95cbf51..4d481f8619df7851446f95712d8dce766513c943 100644 --- a/source/RobotAPI/libraries/CMakeLists.txt +++ b/source/RobotAPI/libraries/CMakeLists.txt @@ -39,3 +39,5 @@ add_subdirectory(skills) add_subdirectory(RobotUnitDataStreamingReceiver) add_subdirectory(GraspingUtility) add_subdirectory(obstacle_avoidance) + +add_subdirectory(robot_name_service) diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index 676c1fd0752baf0c2995e479dfad2cca4f3d9960..9030bd17597399abf79e3413ec71a789f3a113ca 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -57,12 +57,14 @@ set(LIB_FILES client/MemoryNameSystem.cpp client/Reader.cpp client/Writer.cpp + client/ReadStream.cpp client/plugins/ListeningPluginUser.cpp client/plugins/ListeningPlugin.cpp client/plugins/PluginUser.cpp client/plugins/Plugin.cpp + client/util/SubscriptionHandle.cpp client/util/MemoryListener.cpp client/util/SimpleReaderBase.cpp client/util/SimpleWriterBase.cpp @@ -138,6 +140,7 @@ set(LIB_HEADERS client/MemoryNameSystem.h client/Reader.h client/Writer.h + client/ReadStream.h client/plugins.h client/plugins/ListeningPluginUser.h @@ -154,6 +157,7 @@ set(LIB_HEADERS client/query/detail/NameSelectorOps.h client/query/detail/SelectorOps.h + client/util/SubscriptionHandle.h client/util/MemoryListener.h client/util/SimpleReaderBase.h client/util/SimpleWriterBase.h diff --git a/source/RobotAPI/libraries/armem/client/ReadStream.cpp b/source/RobotAPI/libraries/armem/client/ReadStream.cpp new file mode 100644 index 0000000000000000000000000000000000000000..688357958419900abbea45f85b2c35cf4868f26f --- /dev/null +++ b/source/RobotAPI/libraries/armem/client/ReadStream.cpp @@ -0,0 +1,172 @@ +#include "ReadStream.h" + +#include <ArmarXCore/core/time/Clock.h> + +#include <RobotAPI/libraries/armem/client/query/Builder.h> + +namespace armarx::armem::client +{ + ReadStream::ReadStream() : metronome{armarx::core::time::Frequency::Hertz(10)} + { + } + + ReadStream::ReadStream(const Reader& reader, + const MemoryID& queriedId, + const core::time::Frequency& maxPollFrequency) : + reader{reader}, queriedId{queriedId}, metronome{maxPollFrequency} + { + timeStart = armarx::core::time::Clock::Now(); + } + + std::optional<wm::EntitySnapshot> + ReadStream::pollBlocking(const SnapshotCallbackT& callback) + { + if (isPolling.exchange(true)) + { + throw armarx::armem::error::ReadStreamAlreadyPolling(queriedId, __PRETTY_FUNCTION__); + } + pollingStoppedExternally = false; + + auto result = _pollBlocking(callback); + + isPolling = false; + return result; + } + + void + ReadStream::pollAsync(const SnapshotCallbackT& callback) + { + if (isPolling.exchange(true)) + { + throw armarx::armem::error::ReadStreamAlreadyPolling(queriedId, __PRETTY_FUNCTION__); + } + pollingStoppedExternally = false; + + this->pollingThread = std::thread([&]() { this->_pollBlocking(callback); }); + } + + std::optional<wm::EntitySnapshot> + ReadStream::_pollBlocking(const SnapshotCallbackT& callback) + { + while (not pollingStoppedExternally) + { + auto snapshot = _pollOnce(callback); + if (snapshot.has_value()) + { + return snapshot; + } + } + return std::nullopt; + } + + void + ReadStream::stop() + { + pollingStoppedExternally = true; + if (pollingThread.joinable()) + { + pollingThread.join(); + isPolling = false; + } + } + + std::optional<wm::EntitySnapshot> + ReadStream::pollOnce(const SnapshotCallbackT& callback) + { + if (isPolling.exchange(true)) + { + throw armarx::armem::error::ReadStreamAlreadyPolling(queriedId, __PRETTY_FUNCTION__); + } + + auto snapshot = _pollOnce(callback); + + isPolling = false; + return snapshot; + } + + std::optional<wm::EntitySnapshot> + ReadStream::_pollOnce(const SnapshotCallbackT& callback) + { + // Make sure to not busy wait. Also wait until probably data is available in first iteration. + metronome.waitForNextTick(); + + auto timeEnd = armarx::core::time::Clock::Now(); + auto makeQuery = [this, &timeEnd](const MemoryID& id) + { + query::Builder qb; + query::CoreSegmentSelector& core = + id.hasCoreSegmentName() ? qb.coreSegments().withID(id) : qb.coreSegments().all(); + query::ProviderSegmentSelector& prov = id.hasProviderSegmentName() + ? core.providerSegments().withID(id) + : core.providerSegments().all(); + query::EntitySelector& entity = + id.hasEntityName() ? prov.entities().withID(id) : prov.entities().all(); + entity.snapshots().timeRange(timeStart, timeEnd); + + return qb.buildQueryInput(); + }; + + auto query = makeQuery(queriedId); + + auto result = reader.query(query); + + if (result.success) + { + using EntitySnapshotReference = + std::reference_wrapper<armarx::armem::wm::EntitySnapshot>; + // Copy references of snapshots into vector to sort them. + std::vector<EntitySnapshotReference> snapshots; + + result.memory.forEachSnapshot([&snapshots](armarx::armem::wm::EntitySnapshot& snapshot) + { snapshots.push_back(snapshot); }); + + // Sort correctly. + std::sort(snapshots.begin(), + snapshots.end(), + [](const EntitySnapshotReference& a, const EntitySnapshotReference& b) + { return a.get().id().timestamp < b.get().id().timestamp; }); + + // Determine the next start time. + DateTime nextStart; + if (snapshots.size() > 0) + { + // Because they are sorted, back() has the highest time stamp. + nextStart = snapshots.back().get().id().timestamp + + armarx::core::time::Duration::MicroSeconds(1); + } + else + { + nextStart = timeStart; + } + + // Call the callback on all snapshots. + for (const auto& snapshot : snapshots) + { + // Assert times in correct interval. + ARMARX_CHECK_LESS_EQUAL(timeStart, snapshot.get().id().timestamp); + ARMARX_CHECK_GREATER_EQUAL(timeEnd, snapshot.get().id().timestamp); + + const bool continue_ = callback(snapshot.get()); + if (not continue_) + { + return snapshot; + } + } + + timeStart = nextStart; + } + else + { + ARMARX_WARNING + << deactivateSpam() + << "Received an error in ReadStream when querying data from a " + "memory. The error was '" + << result.errorMessage + << "'. Continue with stream, perhaps the memory was not yet initialized."; + } + + return std::nullopt; + } + + +} // namespace armarx::armem::client diff --git a/source/RobotAPI/libraries/armem/client/ReadStream.h b/source/RobotAPI/libraries/armem/client/ReadStream.h new file mode 100644 index 0000000000000000000000000000000000000000..c86fb5b0675ddfc3fa3c4cd9abe49df5ab7a3016 --- /dev/null +++ b/source/RobotAPI/libraries/armem/client/ReadStream.h @@ -0,0 +1,138 @@ +#pragma once + +#include <functional> +#include <optional> +#include <thread> + +#include <ArmarXCore/core/time/DateTime.h> +#include <ArmarXCore/core/time/Frequency.h> +#include <ArmarXCore/core/time/Metronome.h> + +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> + +#include "Reader.h" + +namespace armarx::armem::client +{ + /** + * @brief A stream reading entity snapshots from the memory. + * + * After constructing a ReadStream, polling can be started in three ways: + * + * 1. Run a polling loop in this thread, blocking execution until terminated. + * See pollBlocking(). + * 2. Run a polling loop in a new, separate thread, until it is stopped via stop(). + * See pollAsync() and stop(). + * 3. Perform a single query and process the result, embedded in your own loop or other control + * flow logic. + * See pollOnce(). + */ + class ReadStream + { + public: + /** + * @brief Callback called on each entity snapshot in the queried ID. + * + * If it returns false, the stream is stopped. + */ + using SnapshotCallbackT = std::function<bool(const wm::EntitySnapshot&)>; + + /** + * @brief Inizialize a ReadStream which does not represent a stream. + */ + ReadStream(); + + /** + * @brief Initialize a read stream. + * + * @param reader + * The reader to perform the queries. + * @param queriedId + * The memory ID in which all snapshots should be processed by the stream. + * @param maxPollFrequency + * The maximum frequency with which queries are performed. The + * real frequency might be lower. + */ + ReadStream(const Reader& reader, + const MemoryID& queriedId, + const armarx::core::time::Frequency& maxPollFrequency = + armarx::core::time::Frequency::Hertz(10)); + + + /** + * @brief Poll in this thread as long as callback returns true. + * + * @param callback Function to call on each entity snapshot. + * @return The snapshot object that returns false. + * @throw armarx::armem::error::ReadStreamAlreadyPolling If the stream is already polling. + */ + std::optional<wm::EntitySnapshot> pollBlocking(const SnapshotCallbackT& callback); + + /** + * @brief Poll in a new thread as long as callback returns true. + * + * Note that callback will be called in a separate thread, so take care of synchronizing + * access to variables in the callback appropriately. + * + * Roughly equivalent to: + * @code + * std::thread thread([]() { stream.pollBlocking(); }); + * @endcode + * + * @param callback Function to call on each entity snapshot. + * @throw armarx::armem::error::ReadStreamAlreadyPolling If the stream is already polling. + */ + void pollAsync(const SnapshotCallbackT& callback); + /** + * @brief Stop a running polling loop. + * + * If a polling thread has been started by pollAsync() before, joins the thread. + */ + void stop(); + + /** + * @brief Perform one query and call the callbacks on each snapshot. + * + * This allows you to define your own loop, for example: + * + * @code + * bool condition = true; + * while (condition) + * { + * auto snapshot = stream.pollOnce(callback); + * + * ... + * + * if (...) + * { + * condition = false; + * } + * } + * @endcode + * + * @param callback Function to call on each entity snapshot. + * @throw armarx::armem::error::ReadStreamAlreadyPolling If the stream is already polling. + */ + std::optional<wm::EntitySnapshot> pollOnce(const SnapshotCallbackT& callback); + + + private: + std::optional<wm::EntitySnapshot> _pollBlocking(const SnapshotCallbackT& callback); + std::optional<wm::EntitySnapshot> _pollOnce(const SnapshotCallbackT& callback); + + + private: + Reader reader; + MemoryID queriedId; + + armarx::core::time::Metronome metronome; + armarx::DateTime timeStart; + + std::atomic_bool isPolling = false; + std::atomic_bool pollingStoppedExternally = false; + + std::thread pollingThread; + }; + +} // namespace armarx::armem::client diff --git a/source/RobotAPI/libraries/armem/client/util/MemoryListener.cpp b/source/RobotAPI/libraries/armem/client/util/MemoryListener.cpp index c8a7c6c683467194d6e2248edc0e5be57d3b547b..9aa23153cbfe67047579fdedab8753e35c260002 100644 --- a/source/RobotAPI/libraries/armem/client/util/MemoryListener.cpp +++ b/source/RobotAPI/libraries/armem/client/util/MemoryListener.cpp @@ -2,36 +2,33 @@ #include <sstream> +#include <ArmarXCore/core/ManagedIceObject.h> #include <ArmarXCore/core/exceptions/LocalException.h> -#include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/ice_conversions/ice_conversions_templates.h> -#include <ArmarXCore/core/ManagedIceObject.h> +#include <ArmarXCore/core/logging/Logging.h> -#include <RobotAPI/libraries/armem/core/ice_conversions.h> #include <RobotAPI/libraries/armem/core/error.h> - +#include <RobotAPI/libraries/armem/core/ice_conversions.h> namespace armarx::armem::client::util { - std::string MemoryListener::MakeMemoryTopicName(const MemoryID& memoryID) + std::string + MemoryListener::MakeMemoryTopicName(const MemoryID& memoryID) { return "MemoryUpdates." + memoryID.memoryName; } - - MemoryListener::MemoryListener(ManagedIceObject* component) : - component(component) + MemoryListener::MemoryListener(ManagedIceObject* component) : component(component) { } - - void MemoryListener::setComponent(ManagedIceObject* component) + void + MemoryListener::setComponent(ManagedIceObject* component) { this->component = component; } - void MemoryListener::updated(const std::vector<data::MemoryID>& updatedSnapshotIDs) const { @@ -40,7 +37,6 @@ namespace armarx::armem::client::util updated(bos); } - void MemoryListener::updated(const std::vector<MemoryID>& updatedSnapshotIDs) const { @@ -76,68 +72,104 @@ namespace armarx::armem::client::util if (not matchingSnapshotIDs.empty()) { ARMARX_DEBUG << "Calling " << subCallbacks.size() << " callbacks" - << " subscribing " << subscription - << " with " << matchingSnapshotIDs.size() << " snapshot IDs ..."; - for (auto& callback : subCallbacks) + << " subscribing " << subscription << " with " + << matchingSnapshotIDs.size() << " snapshot IDs ..."; + for (auto& managedCallback : subCallbacks) { try { - callback(subscription, matchingSnapshotIDs); + managedCallback.callback(subscription, matchingSnapshotIDs); } catch (const armarx::LocalException& e) { error << "Calling callback subscribing " << subscription << " failed." << "\nCaught armarx::LocalException:" - << "\n" << e.getReason() - << "\n Stacktrace: \n" << e.generateBacktrace() << "\n" - ; + << e.getReason() << "\n Stacktrace: \n" + << e.generateBacktrace() << "\n"; } catch (const std::exception& e) { error << "Calling callback subscribing " << subscription << " failed." << "\nCaught armarx::Exception:" - << "\n" << e.what() << "\n" - ; + << e.what() << "\n"; } catch (...) { error << "Calling callback subscribing " << subscription << " failed." << "\nCaught unknown exception." - << "\n" - ; + << "\n"; } } } } if (error.str().size() > 0) { - ARMARX_WARNING << "The following issues were encountered during MemoryListener::" << __FUNCTION__ << "(): \n\n" + ARMARX_WARNING << "The following issues were encountered during MemoryListener::" + << __FUNCTION__ << "(): \n\n" << error.str(); } } - - void - MemoryListener::subscribe(const MemoryID& id, Callback callback) + SubscriptionHandle + MemoryListener::subscribe(const MemoryID& memoryID, Callback callback) { - callbacks[id].push_back(callback); - if (component) + ARMARX_CHECK_NOT_EMPTY(memoryID.memoryName) + << "The memoryName must be specified to subscribe"; + + if (component and memoryRefCount[memoryID.memoryName] == 0) { - component->usingTopic(MakeMemoryTopicName(id)); + component->usingTopic(MakeMemoryTopicName(memoryID)); } + + auto id = nextId++; + callbacks[memoryID].push_back({id, callback}); + + memoryRefCount[memoryID.memoryName]++; + + return SubscriptionHandle(this, memoryID, id); } + SubscriptionHandle + MemoryListener::subscribe(const MemoryID& subscriptionID, CallbackUpdatedOnly callback) + { + return subscribe( + subscriptionID, + [callback](const MemoryID&, const std::vector<MemoryID>& updatedSnapshotIDs) + { callback(updatedSnapshotIDs); }); + } void - MemoryListener::subscribe(const MemoryID& subscriptionID, CallbackUpdatedOnly callback) + MemoryListener::unsubscribe(SubscriptionHandle& handle) { - subscribe(subscriptionID, [callback](const MemoryID&, const std::vector<MemoryID>& updatedSnapshotIDs) + if (not handle.valid) + { + return; + } + handle.valid = false; + + // Remove ManagedCallback with ManagedCallback.id == handle.id from callbacks[handle.memoryID] + auto it = std::find_if(callbacks[handle.memoryID].begin(), + callbacks[handle.memoryID].end(), + [&handle](ManagedCallback& mCb) { return mCb.id == handle.id; }); + + std::iter_swap(it, callbacks[handle.memoryID].end() - 1); + callbacks[handle.memoryID].pop_back(); + + memoryRefCount[handle.memoryID.memoryName]--; + + if (callbacks[handle.memoryID].empty()) { - callback(updatedSnapshotIDs); - }); + callbacks.erase(handle.memoryID); + + // unsubscribe from memory topic if no remainig callback needs it + if (component and memoryRefCount[handle.memoryID.memoryName] == 0) + { + component->unsubscribeFromTopic(MakeMemoryTopicName(handle.memoryID)); + } + } } -} +} // namespace armarx::armem::client::util diff --git a/source/RobotAPI/libraries/armem/client/util/MemoryListener.h b/source/RobotAPI/libraries/armem/client/util/MemoryListener.h index 521d9ce6981e9503d90515b0fc10c518eedaae5a..80d8eb3313c44a99abcc87d0048c2a24c2a137e3 100644 --- a/source/RobotAPI/libraries/armem/client/util/MemoryListener.h +++ b/source/RobotAPI/libraries/armem/client/util/MemoryListener.h @@ -1,6 +1,5 @@ #pragma once - // STD/STL #include <functional> #include <unordered_map> @@ -10,7 +9,7 @@ #include <RobotAPI/interface/armem/client/MemoryListenerInterface.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> - +#include "SubscriptionHandle.h" namespace armarx { @@ -19,34 +18,36 @@ namespace armarx namespace armarx::armem::client::util { - /** * @brief Handles update signals from the memory system and distributes it * to its subsribers. */ class MemoryListener { - public: - using Callback = std::function<void(const MemoryID& subscriptionID, const std::vector<MemoryID>& updatedSnapshotIDs)>; - using CallbackUpdatedOnly = std::function<void(const std::vector<MemoryID>& updatedSnapshotIDs)>; + public: + using Callback = std::function<void(const MemoryID& subscriptionID, + const std::vector<MemoryID>& updatedSnapshotIDs)>; + using CallbackUpdatedOnly = + std::function<void(const std::vector<MemoryID>& updatedSnapshotIDs)>; template <class CalleeT> - using MemberCallback = void(CalleeT::*)(const MemoryID& subscriptionID, const std::vector<MemoryID>& updatedSnapshotIDs); + using MemberCallback = void (CalleeT::*)(const MemoryID& subscriptionID, + const std::vector<MemoryID>& updatedSnapshotIDs); template <class CalleeT> - using MemberCallbackUpdatedOnly = void(CalleeT::*)(const std::vector<MemoryID>& updatedSnapshotIDs); + using MemberCallbackUpdatedOnly = + void (CalleeT::*)(const std::vector<MemoryID>& updatedSnapshotIDs); static std::string MakeMemoryTopicName(const MemoryID& memoryID); public: - MemoryListener(ManagedIceObject* component = nullptr); void setComponent(ManagedIceObject* component); - void subscribe(const MemoryID& subscriptionID, Callback Callback); - void subscribe(const MemoryID& subscriptionID, CallbackUpdatedOnly Callback); + SubscriptionHandle subscribe(const MemoryID& subscriptionID, Callback Callback); + SubscriptionHandle subscribe(const MemoryID& subscriptionID, CallbackUpdatedOnly Callback); /** * Subscribe with a class member function: @@ -55,28 +56,33 @@ namespace armarx::armem::client::util * @endcode */ template <class CalleeT> - void subscribe(const MemoryID& subscriptionID, CalleeT* callee, MemberCallback<CalleeT> callback) + SubscriptionHandle + subscribe(const MemoryID& subscriptionID, CalleeT* callee, MemberCallback<CalleeT> callback) { - auto cb = [callee, callback](const MemoryID & subscriptionID, const std::vector<MemoryID>& updatedSnapshotIDs) - { - (callee->*callback)(subscriptionID, updatedSnapshotIDs); - }; - subscribe(subscriptionID, cb); + auto cb = [callee, callback](const MemoryID& subscriptionID, + const std::vector<MemoryID>& updatedSnapshotIDs) + { (callee->*callback)(subscriptionID, updatedSnapshotIDs); }; + return subscribe(subscriptionID, cb); } template <class CalleeT> - void subscribe(const MemoryID& subscriptionID, CalleeT* callee, MemberCallbackUpdatedOnly<CalleeT> callback) + SubscriptionHandle + subscribe(const MemoryID& subscriptionID, + CalleeT* callee, + MemberCallbackUpdatedOnly<CalleeT> callback) { - auto cb = [callee, callback](const MemoryID&, const std::vector<MemoryID>& updatedSnapshotIDs) + auto cb = + [callee, callback](const MemoryID&, const std::vector<MemoryID>& updatedSnapshotIDs) { - if(callee) + if (callee) { (callee->*callback)(updatedSnapshotIDs); } }; - subscribe(subscriptionID, cb); + return subscribe(subscriptionID, cb); } + void unsubscribe(SubscriptionHandle& subscriptionHandle); /// Function handling updates from the MemoryListener ice topic. void updated(const std::vector<MemoryID>& updatedIDs) const; @@ -84,13 +90,21 @@ namespace armarx::armem::client::util protected: + long nextId = 0; - std::unordered_map<MemoryID, std::vector<Callback>> callbacks; + struct ManagedCallback + { + long id = 0; + Callback callback; + }; - private: + std::unordered_map<MemoryID, std::vector<ManagedCallback>> callbacks; - armarx::ManagedIceObject* component; + /// memoryName -> #callbacks needing memory topic + std::unordered_map<std::string, int> memoryRefCount; + private: + armarx::ManagedIceObject* component; }; -} +} // namespace armarx::armem::client::util diff --git a/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp index e14ce8848fd1fe8242d681770563bf9cddefd7cd..650eae74f37e089c49a92b1592dff8e084a49be7 100644 --- a/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp +++ b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.cpp @@ -17,21 +17,25 @@ namespace armarx::armem::client::util const std::string prefix = propertyPrefix(); - props = defaultProperties(); + if (not props.has_value()) + { + props = defaultProperties(); + } - def->optional(props.memoryName, prefix + "Memory"); - def->optional(props.coreSegmentName, prefix + "CoreSegment"); + def->optional(props->memoryName, prefix + "Memory"); + def->optional(props->coreSegmentName, prefix + "CoreSegment"); } void SimpleReaderBase::connect(armarx::armem::client::MemoryNameSystem& mns) { // Wait for the memory to become available and add it as dependency. - ARMARX_IMPORTANT << "SimpleReaderBase: Waiting for memory '" << props.memoryName << "' ..."; + ARMARX_IMPORTANT << "SimpleReaderBase: Waiting for memory '" << properties().memoryName + << "' ..."; try { - memoryReaderClient = mns.useReader(MemoryID().withMemoryName(props.memoryName)); - ARMARX_IMPORTANT << "SimpleReaderBase: Connected to memory '" << props.memoryName + memoryReaderClient = mns.useReader(MemoryID().withMemoryName(properties().memoryName)); + ARMARX_IMPORTANT << "SimpleReaderBase: Connected to memory '" << properties().memoryName << "'"; } catch (const armem::error::CouldNotResolveMemoryServer& e) @@ -41,12 +45,6 @@ namespace armarx::armem::client::util } } - std::mutex& - SimpleReaderBase::memoryReaderMutex() - { - return memoryMutex; - } - const armem::client::Reader& SimpleReaderBase::memoryReader() const { @@ -56,7 +54,17 @@ namespace armarx::armem::client::util const SimpleReaderBase::Properties& SimpleReaderBase::properties() const { - return props; + if (not props.has_value()) + { + const_cast<std::optional<Properties>&>(props) = defaultProperties(); + } + return props.value(); + } + + void + SimpleReaderBase::setProperties(const Properties& p) + { + props = p; } } // namespace armarx::armem::client::util diff --git a/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h index 2b637193765215b81519c55397bcc51a7436041b..3e83956805c03705a90269a2dc565e14a6fdddf5 100644 --- a/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h +++ b/source/RobotAPI/libraries/armem/client/util/SimpleReaderBase.h @@ -21,7 +21,7 @@ #pragma once -#include <mutex> +#include <optional> #include <ArmarXCore/core/application/properties/PropertyDefinitionContainer.h> @@ -53,25 +53,18 @@ namespace armarx::armem::client::util virtual void connect(armarx::armem::client::MemoryNameSystem& mns); const Properties& properties() const; - - void - setProperties(const Properties& p) - { - props = p; - } + void setProperties(const Properties& p); protected: virtual std::string propertyPrefix() const = 0; virtual Properties defaultProperties() const = 0; - std::mutex& memoryReaderMutex(); const armem::client::Reader& memoryReader() const; private: - Properties props; + std::optional<Properties> props; armem::client::Reader memoryReaderClient; - std::mutex memoryMutex; }; } // namespace armarx::armem::client::util diff --git a/source/RobotAPI/libraries/armem/client/util/SubscriptionHandle.cpp b/source/RobotAPI/libraries/armem/client/util/SubscriptionHandle.cpp new file mode 100644 index 0000000000000000000000000000000000000000..114146c91699ff6a8a7fdf88bef61fdca734b892 --- /dev/null +++ b/source/RobotAPI/libraries/armem/client/util/SubscriptionHandle.cpp @@ -0,0 +1,73 @@ +#include "SubscriptionHandle.h" + +#include "MemoryListener.h" + +namespace armarx::armem::client::util +{ + SubscriptionHandle::SubscriptionHandle(MemoryListener* memoryListener, + const MemoryID& memoryID, + long id) : + valid{true}, memoryListener{memoryListener}, memoryID(memoryID), id{id} + { + } + + SubscriptionHandle::SubscriptionHandle() : valid{false} + { + } + + SubscriptionHandle::SubscriptionHandle(SubscriptionHandle&& other) : + valid{other.valid}, + memoryListener{other.memoryListener}, + memoryID(std::move(other.memoryID)), + id{other.id} + { + other.valid = false; + } + + SubscriptionHandle& + SubscriptionHandle::operator=(SubscriptionHandle other) + { + swap(*this, other); + return *this; + } + + void + SubscriptionHandle::release() + { + memoryListener->unsubscribe(*this); + } + + ScopedSubscriptionHandle::ScopedSubscriptionHandle() + { + } + + ScopedSubscriptionHandle::ScopedSubscriptionHandle(SubscriptionHandle&& handle) : + handle(std::move(handle)) + { + } + + ScopedSubscriptionHandle& + ScopedSubscriptionHandle::operator=(SubscriptionHandle handle) + { + std::swap(this->handle, handle); + return *this; + } + + ScopedSubscriptionHandle::~ScopedSubscriptionHandle() + { + handle.release(); + } + +} // namespace armarx::armem::client::util + +namespace armarx::armem::client +{ + void + util::swap(util::SubscriptionHandle& first, util::SubscriptionHandle& second) + { + std::swap(first.valid, second.valid); + std::swap(first.memoryListener, second.memoryListener); + std::swap(first.memoryID, second.memoryID); + std::swap(first.id, second.id); + } +} // namespace armarx::armem::client diff --git a/source/RobotAPI/libraries/armem/client/util/SubscriptionHandle.h b/source/RobotAPI/libraries/armem/client/util/SubscriptionHandle.h new file mode 100644 index 0000000000000000000000000000000000000000..6847ac9a7273cef86d91548b2e66befc8573c015 --- /dev/null +++ b/source/RobotAPI/libraries/armem/client/util/SubscriptionHandle.h @@ -0,0 +1,62 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/MemoryID.h> + +namespace armarx::armem::client::util +{ + + class MemoryListener; + + class SubscriptionHandle + { + friend class MemoryListener; + + public: + SubscriptionHandle(); + SubscriptionHandle(SubscriptionHandle&& other); + + /** + * @brief Assignment operator. + * + * @note Intentional call by value, since this leverages the move constructor. See + * https://stackoverflow.com/a/11540204 (section "Move assignment operators"). + */ + SubscriptionHandle& operator=(SubscriptionHandle other); + + friend void swap(SubscriptionHandle& first, SubscriptionHandle& second); + + void release(); + + private: + SubscriptionHandle(MemoryListener* memoryListener, const MemoryID& memoryID, long id); + + private: + bool valid = false; + MemoryListener* memoryListener = nullptr; + MemoryID memoryID; + long id = 0; + }; + + class ScopedSubscriptionHandle + { + public: + ScopedSubscriptionHandle(); + ScopedSubscriptionHandle(SubscriptionHandle&& handle); + + /** + * @brief Assignment operator. + * + * @note Intentional call by value, since this leverages the move constructor. See + * https://stackoverflow.com/a/11540204 (section "Move assignment operators"). + */ + ScopedSubscriptionHandle& operator=(SubscriptionHandle handle); + + ~ScopedSubscriptionHandle(); + + private: + SubscriptionHandle handle; + }; + + void swap(SubscriptionHandle& first, SubscriptionHandle& second); + +} // namespace armarx::armem::client::util diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp index 354b95712761f6a14d3fb6deb5baeff6b2a18aba..7abe9891d8a1d4f599ae1e135d0dbe8e0d2a29dd 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.cpp @@ -246,4 +246,19 @@ namespace armarx::armem::error return sstream.str(); } + ReadStreamAlreadyPolling::ReadStreamAlreadyPolling(const MemoryID& queriedId, + const std::string& calledFunction) : + ArMemError(makeMsg(queriedId, calledFunction)) + { + } + + std::string + ReadStreamAlreadyPolling::makeMsg(const MemoryID& queriedId, const std::string& calledFunction) + { + std::stringstream ss; + ss << "The ReadStream for " << queriedId << " was already running when " << calledFunction + << " was called."; + return ss.str(); + } + } // namespace armarx::armem::error diff --git a/source/RobotAPI/libraries/armem/core/error/ArMemError.h b/source/RobotAPI/libraries/armem/core/error/ArMemError.h index 69ab7d4ddff8e22791272c520386e2fdff3d6e6f..67435f8d3c4142d3453fc6f6626e4e48ebb33cad 100644 --- a/source/RobotAPI/libraries/armem/core/error/ArMemError.h +++ b/source/RobotAPI/libraries/armem/core/error/ArMemError.h @@ -220,4 +220,15 @@ namespace armarx::armem::error static std::string makeMsg(const std::string& proxyName, const std::string& message = ""); }; + /** + * @brief Indicates that a ReadStream is already polling when a polling method was called. + */ + class ReadStreamAlreadyPolling : public ArMemError + { + public: + ReadStreamAlreadyPolling(const MemoryID& queriedId, const std::string& calledFunction); + + static std::string makeMsg(const MemoryID& queriedId, const std::string& calledFunction); + }; + } // namespace armarx::armem::error diff --git a/source/RobotAPI/libraries/armem_grasping/client/KnownGraspCandidateReader.h b/source/RobotAPI/libraries/armem_grasping/client/KnownGraspCandidateReader.h index d0057afc8d23a30da6e5c6335143d58c638d0557..8d40f1077f46119e0ad7638c3cfc2ef157ed47db 100644 --- a/source/RobotAPI/libraries/armem_grasping/client/KnownGraspCandidateReader.h +++ b/source/RobotAPI/libraries/armem_grasping/client/KnownGraspCandidateReader.h @@ -56,7 +56,6 @@ namespace armarx::armem::grasping::known_grasps const std::string propertyPrefix = "mem.grasping.knowngrasps."; armem::client::Reader memoryReader; - mutable std::mutex memoryWriterMutex; }; diff --git a/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml b/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml index e2d695429d3c4193ad97df106821f5bb912a0028..019987f5054e844478066d377ad0bf1e948b6db7 100644 --- a/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml +++ b/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml @@ -13,6 +13,30 @@ Core segment type of Object/Class. </AronIncludes> <GenerateTypes> + <Object name="armarx::armem::arondto::Feature"> + <ObjectChild key="angle"> + <float32 /> + </ObjectChild> + + <ObjectChild key="scale"> + <float32 /> + </ObjectChild> + + <ObjectChild key="point2d"> + <vector2f /> + </ObjectChild> + + <ObjectChild key="point3d"> + <vector3f /> + </ObjectChild> + + <ObjectChild key="feature"> + <List> + <float32 /> + </List> + </ObjectChild> + </Object> + <Object name="armarx::armem::arondto::ObjectClass"> <ObjectChild key="id"> @@ -62,6 +86,12 @@ Core segment type of Object/Class. <armarx::arondto::ObjectNames /> </ObjectChild> + <ObjectChild key="ivtFeatures"> + <List> + <armarx::armem::arondto::Feature /> + </List> + </ObjectChild> + </Object> </GenerateTypes> diff --git a/source/RobotAPI/libraries/armem_objects/aron_conversions.cpp b/source/RobotAPI/libraries/armem_objects/aron_conversions.cpp index 6b3f3c184edb32070f6d7f28c853f340bcd78d32..da32c2771a2149f58e5ba3a1a01db93ecbe71028 100644 --- a/source/RobotAPI/libraries/armem_objects/aron_conversions.cpp +++ b/source/RobotAPI/libraries/armem_objects/aron_conversions.cpp @@ -111,6 +111,71 @@ namespace armarx::armem } // namespace armarx::armem +namespace armarx::armem::clazz +{ + + void + fromAron(const arondto::Feature& dto, Feature& bo) + { + bo.angle = dto.angle; + bo.scale = dto.scale; + bo.feature = dto.feature; + bo.point2d = dto.point2d; + bo.point3d = dto.point3d; + } + + void + toAron(arondto::Feature& dto, const Feature& bo) + { + dto.angle = bo.angle; + dto.scale = bo.scale; + dto.feature = bo.feature; + dto.point2d = bo.point2d; + dto.point3d = bo.point3d; + } + + void + fromAron(const arondto::ObjectClass& dto, ObjectClass& bo) + { + armarx::fromAron(dto.id, bo.id); + armarx::fromAron(dto.simoxXmlPath, bo.simoxXmlPath); + armarx::fromAron(dto.articulatedSimoxXmlPath, bo.articulatedSimoxXmlPath); + armarx::fromAron(dto.urdfPath, bo.urdfPath); + armarx::fromAron(dto.articulatedUrdfPath, bo.articulatedUrdfPath); + armarx::fromAron(dto.sdfPath, bo.sdfPath); + armarx::fromAron(dto.meshWrlPath, bo.meshWrlPath); + armarx::fromAron(dto.meshObjPath, bo.meshObjPath); + armarx::fromAron(dto.aabb, bo.aabb); + armarx::fromAron(dto.oobb, bo.oobb); + bo.ivtFeatures.clear(); + for (const auto& i : dto.ivtFeatures) + { + fromAron(i, bo.ivtFeatures.emplace_back()); + } + } + + void + toAron(arondto::ObjectClass& dto, const ObjectClass& bo) + { + armarx::toAron(dto.id, bo.id); + armarx::toAron(dto.simoxXmlPath, bo.simoxXmlPath); + armarx::toAron(dto.articulatedSimoxXmlPath, bo.articulatedSimoxXmlPath); + armarx::toAron(dto.urdfPath, bo.urdfPath); + armarx::toAron(dto.articulatedUrdfPath, bo.articulatedUrdfPath); + armarx::toAron(dto.sdfPath, bo.sdfPath); + armarx::toAron(dto.meshWrlPath, bo.meshWrlPath); + armarx::toAron(dto.meshObjPath, bo.meshObjPath); + armarx::toAron(dto.aabb, bo.aabb); + armarx::toAron(dto.oobb, bo.oobb); + dto.ivtFeatures.clear(); + for (const auto& i : bo.ivtFeatures) + { + toAron(dto.ivtFeatures.emplace_back(), i); + } + } + +} // namespace armarx::armem::clazz + armarx::armem::MemoryID armarx::armem::obj::makeObjectInstanceMemoryID(const objpose::ObjectPose& objectPose) { diff --git a/source/RobotAPI/libraries/armem_objects/aron_conversions.h b/source/RobotAPI/libraries/armem_objects/aron_conversions.h index e27ed16c4cede302e2eecf2c013440d240162550..b61164a5d6a7de07bd8ba3761117e263ac714949 100644 --- a/source/RobotAPI/libraries/armem_objects/aron_conversions.h +++ b/source/RobotAPI/libraries/armem_objects/aron_conversions.h @@ -1,11 +1,10 @@ #pragma once #include <RobotAPI/libraries/ArmarXObjects/ObjectPose.h> - -#include <RobotAPI/libraries/armem_objects/aron/ObjectInstance.aron.generated.h> #include <RobotAPI/libraries/armem_objects/aron/Attachment.aron.generated.h> #include <RobotAPI/libraries/armem_objects/aron/Marker.aron.generated.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/types.h> namespace armarx::armem @@ -18,19 +17,32 @@ namespace armarx::armem /* Attachments */ - void fromAron(const arondto::attachment::AgentDescription& dto, attachment::AgentDescription& bo); + void fromAron(const arondto::attachment::AgentDescription& dto, + attachment::AgentDescription& bo); void toAron(arondto::attachment::AgentDescription& dto, const attachment::AgentDescription& bo); - void fromAron(const arondto::attachment::ObjectAttachment& dto, attachment::ObjectAttachment& bo); + void fromAron(const arondto::attachment::ObjectAttachment& dto, + attachment::ObjectAttachment& bo); void toAron(arondto::attachment::ObjectAttachment& dto, const attachment::ObjectAttachment& bo); - void fromAron(const arondto::attachment::ArticulatedObjectAttachment& dto, attachment::ArticulatedObjectAttachment& bo); - void toAron(arondto::attachment::ArticulatedObjectAttachment& dto, const attachment::ArticulatedObjectAttachment& bo); + void fromAron(const arondto::attachment::ArticulatedObjectAttachment& dto, + attachment::ArticulatedObjectAttachment& bo); + void toAron(arondto::attachment::ArticulatedObjectAttachment& dto, + const attachment::ArticulatedObjectAttachment& bo); + + void fromAron(const arondto::Marker& dto, marker::Marker& bo); + void toAron(arondto::Marker& dto, const marker::Marker& bo); +} // namespace armarx::armem + +namespace armarx::armem::clazz +{ + void fromAron(const armarx::armem::arondto::Feature& dto, Feature& bo); + void toAron(armarx::armem::arondto::Feature& dto, const Feature& bo); - void fromAron(const arondto::Marker& dto, marker::Marker&bo); - void toAron(arondto::Marker& dto, const marker::Marker&bo); -} // namespace armarx::armem + void fromAron(const armarx::armem::arondto::ObjectClass& dto, ObjectClass& bo); + void toAron(armarx::armem::arondto::ObjectClass& dto, const ObjectClass& bo); +} // namespace armarx::armem::clazz #include <RobotAPI/libraries/armem/core/MemoryID.h> @@ -38,4 +50,4 @@ namespace armarx::armem::obj { /// Make a Memory ID for the object instance snapshot representing this pose. MemoryID makeObjectInstanceMemoryID(const objpose::ObjectPose& objectPose); -} +} // namespace armarx::armem::obj diff --git a/source/RobotAPI/libraries/armem_objects/client/instance/ObjectWriter.h b/source/RobotAPI/libraries/armem_objects/client/instance/ObjectWriter.h index d449502849b09de61e3abc65ab0364928502a083..da5744c594780644808d3f75b003cdb2d04d0465 100644 --- a/source/RobotAPI/libraries/armem_objects/client/instance/ObjectWriter.h +++ b/source/RobotAPI/libraries/armem_objects/client/instance/ObjectWriter.h @@ -57,7 +57,6 @@ namespace armarx::armem::obj::instance const std::string propertyPrefix = "mem.obj.instance."; armem::client::Writer memoryWriter; - mutable std::mutex memoryWriterMutex; }; diff --git a/source/RobotAPI/libraries/armem_objects/types.h b/source/RobotAPI/libraries/armem_objects/types.h index 79fe9ac956f65869ceb03b35db8545155c4c963c..598c78ca9c6bc5f4fc007059352ee94fc28b2fec 100644 --- a/source/RobotAPI/libraries/armem_objects/types.h +++ b/source/RobotAPI/libraries/armem_objects/types.h @@ -23,6 +23,8 @@ #include <Eigen/Geometry> +#include <SimoxUtility/shapes/AxisAlignedBoundingBox.h> + #include <RobotAPI/libraries/ArmarXObjects/ObjectPose.h> #include <RobotAPI/libraries/armem/core/MemoryID.h> #include <RobotAPI/libraries/armem/core/Time.h> @@ -31,7 +33,6 @@ #include "aron_forward_declarations.h" - namespace armarx::armem { struct ObjectInstance @@ -40,7 +41,36 @@ namespace armarx::armem MemoryID classID; MemoryID sourceID; }; -} +} // namespace armarx::armem + +namespace armarx::armem::clazz +{ + struct Feature + { + float angle; + float scale; + Eigen::Vector2f point2d; + Eigen::Vector3f point3d; + std::vector<float> feature; + }; + + struct ObjectClass + { + armarx::ObjectID id; + armarx::PackagePath simoxXmlPath; + armarx::PackagePath articulatedSimoxXmlPath; + armarx::PackagePath urdfPath; + armarx::PackagePath articulatedUrdfPath; + armarx::PackagePath sdfPath; + armarx::PackagePath articulatedSdfPath; + armarx::PackagePath meshWrlPath; + armarx::PackagePath meshObjPath; + simox::AxisAlignedBoundingBox aabb; + simox::OrientedBoxf oobb; + // TODO NAMES + std::vector<Feature> ivtFeatures; + }; +} // namespace armarx::armem::clazz namespace armarx::armem::attachment { @@ -110,7 +140,6 @@ namespace armarx::armem::articulated_object using ArticulatedObjects = armarx::armem::robot::Robots; } // namespace armarx::armem::articulated_object - namespace armarx::armem::marker { class Marker diff --git a/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt index 3ad1d50684d89c246e6123a6ef5d2055779eab6f..54d5823876f19fb00a6e9e6ec57ecc89db0c3a13 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt @@ -20,10 +20,12 @@ armarx_add_library( # This package RobotAPICore RobotAPIInterfaces + RobotStatechartHelpers RobotAPI::armem_server RobotAPI::armem_robot RobotAPI::armem_robot_state aroncommon + aroneigenconverter # System / External Eigen3::Eigen @@ -38,6 +40,7 @@ armarx_add_library( localization/Segment.h proprioception/Segment.h + proprioception/SensorValues.h proprioception/aron_conversions.h proprioception/RobotStateWriter.h proprioception/RobotUnitData.h @@ -63,6 +66,7 @@ armarx_add_library( localization/Segment.cpp proprioception/Segment.cpp + proprioception/SensorValues.cpp proprioception/aron_conversions.cpp proprioception/RobotStateWriter.cpp proprioception/RobotUnitData.cpp 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 41eaf6e2ec2f2bcead697ec6ca7e86868bca44c4..be19db6355d9c21e0b9a7e287e5889024a0c408c 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.cpp @@ -8,6 +8,9 @@ #include <SimoxUtility/algorithm/get_map_keys_values.h> #include <SimoxUtility/math/pose.h> +#include <SimoxUtility/math/rescale.h> + +#include <VirtualRobot/XML/RobotIO.h> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/time/CycleUtil.h> @@ -21,6 +24,7 @@ #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 <RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.h> #include "combine.h" @@ -43,8 +47,12 @@ namespace armarx::armem::server::robot_state void Visu::defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string& prefix) { defs->optional(p.enabled, prefix + "enabled", "Enable or disable visualization of objects."); - defs->optional(p.framesEnabled, prefix + "famesEnabled", "Enable or disable visualization of frames."); defs->optional(p.frequencyHz, prefix + "frequenzyHz", "Frequency of visualization."); + defs->optional(p.framesEnabled, prefix + "framesEnabled", "Enable or disable visualization of frames."); + defs->optional(p.forceTorque.enabled, prefix + "forceTorque.enabled", + "Enable or disable visualization of force torque sensors."); + defs->optional(p.forceTorque.forceScale, prefix + "forceTorque.forceScale", + "Scaling of force arrows."); } @@ -191,11 +199,11 @@ namespace armarx::armem::server::robot_state const auto frames = localizationSegment.getRobotFramePosesLocking(timestamp); TIMING_END_STREAM(tRobotFramePoses, ARMARX_DEBUG); - TIMING_START(tJointPositions); - const auto jointPositions = - proprioceptionSegment.getRobotJointPositionsLocking( + TIMING_START(tSensorValues); + const auto sensorValues = + proprioceptionSegment.getSensorValuesLocking( timestamp, debugObserver ? &*debugObserver : nullptr); - TIMING_END_STREAM(tJointPositions, ARMARX_DEBUG); + TIMING_END_STREAM(tSensorValues, ARMARX_DEBUG); TIMING_END_STREAM(tVisuGetData, ARMARX_DEBUG); @@ -211,10 +219,10 @@ namespace armarx::armem::server::robot_state ARMARX_DEBUG << "Combining robot ..." << "\n- " << robotDescriptions.size() << " descriptions" << "\n- " << globalPoses.size() << " global poses" - << "\n- " << jointPositions.size() << " joint positions"; + << "\n- " << sensorValues.size() << " joint positions"; const robot::Robots robots = - combine(robotDescriptions, globalPoses, jointPositions, timestamp); + combine(robotDescriptions, globalPoses, sensorValues, timestamp); ARMARX_DEBUG << "Visualize " << robots.size() << " robots ..."; viz::Layer layer = arviz.layer("Robots"); @@ -237,6 +245,65 @@ namespace armarx::armem::server::robot_state layers.push_back(layerFrames); } + if (p.forceTorque.enabled) + { + viz::Layer layerFrames = arviz.layer("ForceTorque"); + for (const robot::Robot& robot : robots) + { + const std::string& name = robot.description.name; + if (robotNameHelper.find(name) == robotNameHelper.end()) + { + const std::filesystem::path robotPath = robot.description.xml.toSystemPath(); + robotNameHelper[name] = RobotNameHelper::Create(robotPath); + robotModels[name] + = VirtualRobot::RobotIO::loadRobot(robotPath, VirtualRobot::RobotIO::eStructure); + } + + auto model = robotModels.at(name); + model->setJointValues(robot.config.jointMap); + model->setGlobalPose(robot.config.globalPose.matrix()); + + const proprioception::ForceTorqueValuesMap& forceTorques + = sensorValues.at(name).forceTorqueValuesMap; + + for (const auto& [side, ft] : forceTorques) + { + ARMARX_CHECK(side == RobotNameHelper::LocationLeft or side == RobotNameHelper::LocationRight) << side; + + const std::string forceTorqueSensorName = + robotNameHelper.at(name)->getArm(side).getForceTorqueSensor(); + + const Eigen::Matrix4f forceTorqueSensorPose + = model->getSensor(forceTorqueSensorName)->getGlobalPose(); + + const std::string xyz = "XYZ"; + + const Eigen::Vector3f from = simox::math::position(forceTorqueSensorPose); + for (int i = 0; i < 3; ++i) + { + simox::Color color = simox::Color((255 * Eigen::Matrix3i::Identity().col(i)).eval()); + color.a = 128; + + // Force arrows. + const float length = p.forceTorque.forceScale * ft.force(i); + const float width = std::min( + simox::math::rescale(std::abs(length), 0.F, 100.F, 1.F, 10.F), + 10.F); + + const Eigen::Vector3f to = from + length * simox::math::orientation(forceTorqueSensorPose).col(i); + + std::stringstream key; + key << side << " Force " << xyz.at(i); + layerFrames.add(viz::Arrow(key.str()).fromTo(from, to).color(color).width(width)); + + // Torque circle arrows. + } + } + + } + layers.push_back(layerFrames); + } + // Commit layers. @@ -255,7 +322,7 @@ namespace armarx::armem::server::robot_state 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 1.4 Sensor Values (ms)", tSensorValues.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 99321b656be9977a23096e3b9bdd9ed4ba62a101..265f08e2a0c10153c1b3cc8c624fd214713d57a9 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/Visu.h @@ -33,6 +33,8 @@ #include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> +#include <RobotAPI/libraries/RobotStatechartHelpers/RobotNameHelper.h> + namespace armarx::armem::server::robot_state { @@ -86,11 +88,23 @@ namespace armarx::armem::server::robot_state const proprioception::Segment& proprioceptionSegment; const localization::Segment& localizationSegment; + std::map<std::string, RobotNameHelperPtr> robotNameHelper; + std::map<std::string, VirtualRobot::RobotPtr> robotModels; + + struct Properties { bool enabled = true; + float frequencyHz = 25.f; + bool framesEnabled = false; - float frequencyHz = 25; + + struct ForceTorque + { + bool enabled = true; + float forceScale = 1.F; + }; + ForceTorque forceTorque; } p; diff --git a/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp index abe3f0bbb024d971052aad031b366a3d54b15ffe..997253f25c8bce846e3dddfe8e14aa9f04414d78 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.cpp @@ -2,6 +2,7 @@ #include <RobotAPI/libraries/armem_robot/types.h> #include <RobotAPI/libraries/armem/core/Time.h> +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.h> #include <ArmarXCore/core/logging/Logging.h> @@ -15,7 +16,7 @@ namespace armarx::armem::server robot_state::combine( const description::RobotDescriptionMap& robotDescriptions, const localization::RobotPoseMap& globalPoses, - const proprioception::RobotJointPositionMap& jointPositions, + const proprioception::SensorValuesMap& sensorValues, const armem::Time& timestamp) { std::stringstream logs; @@ -42,9 +43,12 @@ namespace armarx::armem::server { logs << "\nNo global pose for robot '" << robotName << "'."; } - if (auto it = jointPositions.find(robotName); it != jointPositions.end()) + if (auto it = sensorValues.find(robotName); it != sensorValues.end()) { - robot.config.jointMap = it->second; + for (const auto& [name, values] : it->second.jointValueMap) + { + robot.config.jointMap.emplace(name, values.position); + } } else { diff --git a/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h index 811599b69d8fe853154520f5b72b4ca95eb9488f..b96ca240c552426651ed4871779ea747604706cf 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/common/combine.h @@ -32,7 +32,7 @@ namespace armarx::armem::server::robot_state combine( const description::RobotDescriptionMap& robotDescriptions, const localization::RobotPoseMap& globalPoses, - const proprioception::RobotJointPositionMap& jointPositions, + const proprioception::SensorValuesMap& sensorValues, const armem::Time& timestamp); } // namespace armarx::armem::server::robot_state diff --git a/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h b/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h index e730e29f1d5c8e83f38701a2e26cfc7a55b23039..1b9c39543b21587f30a32feb2dfbbcac4fb5e257 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/forward_declarations.h @@ -74,7 +74,12 @@ namespace armarx::armem::server::robot_state::localization namespace armarx::armem::server::robot_state::proprioception { - using RobotJointPositionMap = std::unordered_map<std::string, std::map<std::string, float>>; + struct JointValues; + struct ForceTorqueValues; + struct SensorValues; + using JointValuesMap = std::unordered_map<std::string, JointValues>; + using ForceTorqueValuesMap = std::unordered_map<std::string, ForceTorqueValues>; + using SensorValuesMap = std::unordered_map<std::string, SensorValues>; class Segment; } 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 99c330e7661d35c5b161c26e1d18b45af2d91ce9..d7136b1ef301a2cdc455fef4fa3f4dbcad5dd1d3 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.cpp @@ -15,6 +15,9 @@ #include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/memory_ids.h> +#include "SensorValues.h" + +#include <RobotAPI/libraries/aron/converter/eigen/EigenConverter.h> namespace armarx::armem::server::robot_state::proprioception { @@ -70,13 +73,13 @@ namespace armarx::armem::server::robot_state::proprioception - RobotJointPositionMap Segment::getRobotJointPositionsLocking( + SensorValuesMap Segment::getSensorValuesLocking( const armem::Time& timestamp, DebugObserverHelper* debugObserver) const { return doLocked([this, ×tamp, &debugObserver]() { - return getRobotJointPositions(timestamp, debugObserver); + return getSensorValues(timestamp, debugObserver); }); } @@ -93,18 +96,18 @@ namespace armarx::armem::server::robot_state::proprioception } - RobotJointPositionMap - Segment::getRobotJointPositions( + SensorValuesMap + Segment::getSensorValues( const armem::Time& timestamp, DebugObserverHelper* debugObserver) const { namespace adn = aron::data; ARMARX_CHECK_NOT_NULL(segmentPtr); - RobotJointPositionMap jointMap; + SensorValuesMap sensorValues; int i = 0; - Duration tFindData = Duration::MilliSeconds(0), tReadJointPositions = Duration::MilliSeconds(0); + Duration tFindData = Duration::MilliSeconds(0), tReadSensorValues = Duration::MilliSeconds(0); TIMING_START(tProcessEntities) segmentPtr->forEachEntity([&](const wm::Entity & entity) { @@ -128,12 +131,12 @@ namespace armarx::armem::server::robot_state::proprioception } if (data) { - TIMING_START(_tReadJointPositions) + TIMING_START(_tReadSensorValues) - jointMap.emplace(entity.id().providerSegmentName, readJointPositions(*data)); + sensorValues.emplace(entity.id().providerSegmentName, readSensorValues(*data)); - TIMING_END_COMMENT_STREAM(_tReadJointPositions, "tReadJointPositions " + std::to_string(i), ARMARX_DEBUG) - tReadJointPositions += Duration::MicroSeconds(_tReadJointPositions.toMicroSeconds()); + TIMING_END_COMMENT_STREAM(_tReadSensorValues, "tReadSensorValues " + std::to_string(i), ARMARX_DEBUG) + tReadSensorValues += Duration::MicroSeconds(_tReadSensorValues.toMicroSeconds()); } ++i; }); @@ -143,10 +146,10 @@ namespace armarx::armem::server::robot_state::proprioception { 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()); + debugObserver->setDebugObserverDatafield(dp + "t 1.1.2 ReadSensorValues (ms)", tReadSensorValues.toMilliSecondsDouble()); } - return jointMap; + return sensorValues; } @@ -277,25 +280,73 @@ namespace armarx::armem::server::robot_state::proprioception } - std::map<std::string, float> - Segment::readJointPositions(const wm::EntityInstanceData& data) + SensorValues + Segment::readSensorValues(const wm::EntityInstanceData& data) { namespace adn = aron::data; // Just get what we need without casting the whole data. - std::map<std::string, float> jointPositions; + SensorValues sensorValues; + auto checkJVM = [&sensorValues](const std::string& name) + { + if (sensorValues.jointValueMap.find(name) == sensorValues.jointValueMap.end()) + { + sensorValues.jointValueMap[name] = JointValues(); + } + }; if (adn::DictPtr joints = getDictElement(data, "joints")) { - if (adn::DictPtr jointsPosition = getDictElement(*joints, "position")) + if (adn::DictPtr values = getDictElement(*joints, "position")) + { + for (const auto& [name, value] : values->getElements()) + { + checkJVM(name); + sensorValues.jointValueMap[name].position + = adn::Float::DynamicCastAndCheck(value)->getValue(); + } + } + if (adn::DictPtr values = getDictElement(*joints, "velocity")) + { + for (const auto& [name, value] : values->getElements()) + { + checkJVM(name); + sensorValues.jointValueMap[name].velocity + = adn::Float::DynamicCastAndCheck(value)->getValue(); + } + } + if (adn::DictPtr values = getDictElement(*joints, "torque")) + { + for (const auto& [name, value] : values->getElements()) + { + checkJVM(name); + sensorValues.jointValueMap[name].velocity + = adn::Float::DynamicCastAndCheck(value)->getValue(); + } + } + } + if (adn::DictPtr forceTorqueMap = getDictElement(data, "forceTorque")) + { + for (const auto& [name, value] : forceTorqueMap->getElements()) { - for (const auto& [name, value] : jointsPosition->getElements()) + if (adn::DictPtr forceTorqueValues = aron::data::Dict::DynamicCastAndCheck(value)) { - const float jointPosition = adn::Float::DynamicCastAndCheck(value)->getValue(); - jointPositions[name] = jointPosition; + const Eigen::Vector3f torque + = armarx::aron::converter::AronEigenConverter::ConvertToVector3f( + adn::NDArray::DynamicCastAndCheck(forceTorqueValues->getElement("torque"))); + + const Eigen::Vector3f force + = armarx::aron::converter::AronEigenConverter::ConvertToVector3f( + adn::NDArray::DynamicCastAndCheck(forceTorqueValues->getElement("force"))); + + sensorValues.forceTorqueValuesMap[name] = ForceTorqueValues + { + .force = force, + .torque = torque + }; } } } - return jointPositions; + return sensorValues; } } // 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 8123aea55ea50a11d02b8bf1d50aee0fbe5b09dc..fbce59a996acb8b17ffb104095eadf570d02d535 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h @@ -60,9 +60,9 @@ namespace armarx::armem::server::robot_state::proprioception void onConnect(RobotUnitInterfacePrx robotUnitPrx); - RobotJointPositionMap getRobotJointPositions( + SensorValuesMap getSensorValues( const armem::Time& timestamp, DebugObserverHelper* debugObserver = nullptr) const; - RobotJointPositionMap getRobotJointPositionsLocking( + SensorValuesMap getSensorValuesLocking( const armem::Time& timestamp, DebugObserverHelper* debugObserver = nullptr) const; const armem::MemoryID& getRobotUnitProviderID() const; @@ -73,8 +73,8 @@ namespace armarx::armem::server::robot_state::proprioception private: static - std::map<std::string, float> - readJointPositions(const wm::EntityInstanceData& data); + SensorValues + readSensorValues(const wm::EntityInstanceData& data); private: diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee25d654eb5ee1e045d67ef5ea504ad4834bc8a5 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.cpp @@ -0,0 +1,2 @@ +#include "SensorValues.h" + diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.h new file mode 100644 index 0000000000000000000000000000000000000000..2beb7608afb3a5710b761fce3651fe2dee81b73c --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/SensorValues.h @@ -0,0 +1,50 @@ +/* + * 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 Andre Meixner ( andre dot meixner at kit dot edu ) + * @date 2023 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> + +namespace armarx::armem::server::robot_state::proprioception +{ + +struct JointValues +{ + double position = 0.0; + double velocity = 0.0; + double torque = 0.0; + //double torqueTicks = 0.0; +}; + +struct ForceTorqueValues +{ + Eigen::Vector3f force = Eigen::Vector3f::Zero(); + Eigen::Vector3f torque = Eigen::Vector3f::Zero(); +}; + +struct SensorValues +{ + JointValuesMap jointValueMap; + ForceTorqueValuesMap forceTorqueValuesMap; +}; + +} + diff --git a/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp b/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp index 7e9c71cbecf3c5dd9d8952135b61625a84ce1a27..1955a76581429fd9b327fce7d3f0d23f9f64e8b8 100644 --- a/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp +++ b/source/RobotAPI/libraries/aron/codegeneration/typereader/xml/Reader.cpp @@ -21,25 +21,19 @@ * GNU General Public License */ -// STD/STL - - -// Header #include "Reader.h" + #include <sstream> -// Simox #include <SimoxUtility/algorithm/string/string_tools.h> #include <SimoxUtility/algorithm/vector.hpp> -// ArmarX #include <ArmarXCore/core/rapidxml/wrapper/RapidXmlReader.h> #include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> #include <RobotAPI/libraries/aron/codegeneration/typereader/xml/Data.h> #include <RobotAPI/libraries/aron/core/type/variant/Factory.h> - namespace armarx::aron::typereader::xml { namespace fs = std::filesystem; @@ -47,12 +41,14 @@ namespace armarx::aron::typereader::xml namespace { /// Resolve a relative package path - std::optional<fs::path> resolveRelativePackagePath(const fs::path& path, const std::vector<fs::path>& includePaths) + std::optional<fs::path> + resolveRelativePackagePath(const fs::path& filepath, + const std::vector<fs::path>& includePaths) { // new behavior: using provided include paths for (const auto& includePath : includePaths) { - fs::path absPath = includePath / path; + fs::path absPath = includePath / filepath; if (fs::is_regular_file(absPath)) { // path is valid @@ -61,27 +57,89 @@ namespace armarx::aron::typereader::xml } // legacy behavior: using cmake package finder paths - const std::string package = *path.begin(); - armarx::CMakePackageFinder finder(package); - if (finder.packageFound()) + + std::vector<std::string> packageNameCandidates; + { + auto it = filepath.begin(); + + std::string packageName = *it; + packageNameCandidates.push_back(packageName); + + ++it; + if (it != filepath.end()) + { + packageName += "_" + it->string(); + packageNameCandidates.push_back(packageName); + } + } + + std::vector<armarx::CMakePackageFinder> finders; + size_t foundFinderIndex = 0; + size_t numberOfFoundCandidates = 0; + for (const std::string& packageName : packageNameCandidates) + { + armarx::CMakePackageFinder& finder = finders.emplace_back(packageName); + if (finder.packageFound()) + { + // TODO: In theory, we could also consider whether the requested file exists in + // the found package (see check below). + numberOfFoundCandidates++; + foundFinderIndex = finders.size() - 1; + } + } + + if (numberOfFoundCandidates == 0) + { + std::stringstream msg; + msg << "No package matching the ARON include " << filepath + << " found. Tried CMake project names:"; + for (const std::string& packageName : packageNameCandidates) + { + msg << " '" << packageName << "',"; + } + throw error::AronException(__PRETTY_FUNCTION__, msg.str()); + } + if (numberOfFoundCandidates > 1) { - for (const std::string& includePath : finder.getIncludePathList()) + std::stringstream msg; + msg << "The ARON include " << filepath << " is ambiguous: The following " + << numberOfFoundCandidates + << " CMake projects matching the include were found:"; + ARMARX_CHECK_EQUAL(packageNameCandidates.size(), finders.size()); + for (size_t i = 0; i < packageNameCandidates.size(); ++i) { - fs::path absPath = includePath / path; - if (fs::is_regular_file(absPath)) + if (finders[i].packageFound()) { - // path is valid - return absPath; + msg << "\n- project '" << packageNameCandidates[i] << "' at '" + << finders[i].getPackageDir() << "'"; } } - return std::nullopt; + throw error::AronException(__PRETTY_FUNCTION__, msg.str()); + } + + ARMARX_CHECK_EQUAL(numberOfFoundCandidates, 1); + ARMARX_CHECK_LESS(foundFinderIndex, finders.size()); + + CMakePackageFinder& finder = finders.at(foundFinderIndex); + ARMARX_CHECK(finder.packageFound()); + + for (const std::string& includePath : finder.getIncludePathList()) + { + fs::path absPath = includePath / filepath; + if (fs::is_regular_file(absPath)) + { + // path is valid + return absPath; + } } return std::nullopt; } - } + } // namespace - void Reader::parseFile(const std::string& _filename, const std::vector<std::filesystem::path>& includePaths) + void + Reader::parseFile(const std::string& _filename, + const std::vector<std::filesystem::path>& includePaths) { std::string filename = _filename; @@ -93,7 +151,9 @@ namespace armarx::aron::typereader::xml parseFile(std::filesystem::path(filename), includePaths); } - void Reader::parseFile(const std::filesystem::path& _file, const std::vector<std::filesystem::path>& includePaths) + void + Reader::parseFile(const std::filesystem::path& _file, + const std::vector<std::filesystem::path>& includePaths) { fs::path file = _file; if (not std::filesystem::exists(file)) @@ -102,7 +162,10 @@ namespace armarx::aron::typereader::xml auto p = resolveRelativePackagePath(file, includePaths); if (not p) { - throw error::AronException(__PRETTY_FUNCTION__, "Could not find the file " + file.string() + ". Tried include paths: " + simox::alg::to_string(includePaths)); + throw error::AronException( + __PRETTY_FUNCTION__, + "Could not find the file " + file.string() + + ". Tried include paths: " + simox::alg::to_string(includePaths)); } file = *p; } @@ -112,7 +175,10 @@ namespace armarx::aron::typereader::xml } // private method reading nodes - void Reader::parse(const RapidXmlReaderPtr& reader, const std::filesystem::path& filePath, const std::vector<std::filesystem::path>& includePaths) + void + Reader::parse(const RapidXmlReaderPtr& reader, + const std::filesystem::path& filePath, + const std::vector<std::filesystem::path>& includePaths) { RapidXmlReaderNode root = reader->getRoot(); @@ -132,7 +198,10 @@ namespace armarx::aron::typereader::xml for (const auto& include : (*codeincludes).nodes()) { auto i = readCodeInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->systemIncludes.push_back(i); + if (not i.empty()) + { + this->systemIncludes.push_back(i); + } } } @@ -142,7 +211,10 @@ namespace armarx::aron::typereader::xml for (const auto& include : (*aronincludes).nodes()) { auto i = readAronInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->aronIncludes.push_back(i); + if (not i.empty()) + { + this->aronIncludes.push_back(i); + } } } @@ -151,10 +223,14 @@ namespace armarx::aron::typereader::xml { for (const auto& include : (*includes).nodes()) { - if (util::HasTagName(include, constantes::SYSTEM_INCLUDE_TAG)) // if its a system include tag then we know that it must be a code include + if (util::HasTagName( + include, + constantes:: + SYSTEM_INCLUDE_TAG)) // if its a system include tag then we know that it must be a code include { auto i = readCodeInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->systemIncludes.push_back(i); + if (not i.empty()) + this->systemIncludes.push_back(i); } else { @@ -171,17 +247,20 @@ namespace armarx::aron::typereader::xml what = util::GetAttribute(include, constantes::INCLUDE_ATTRIBUTE_NAME); } - if (not what.empty() && std::filesystem::path(what) != filePath) // did we found something? + if (not what.empty() && + std::filesystem::path(what) != filePath) // did we found something? { if (simox::alg::ends_with(what, ARON_FILE_SUFFIX)) { auto i = readAronInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->aronIncludes.push_back(i); + if (not i.empty()) + this->aronIncludes.push_back(i); } else // we believe that this is a code include since it is not an xml file { auto i = readCodeInclude(include, filePath.parent_path(), includePaths); - if (not i.empty()) this->systemIncludes.push_back(i); + if (not i.empty()) + this->systemIncludes.push_back(i); } } } @@ -195,13 +274,18 @@ namespace armarx::aron::typereader::xml { if (util::HasTagName(generateType, constantes::OBJECT_TAG)) { - for (const auto& additionalInclude : getAdditionalIncludesFromReplacements(generateType, filePath.parent_path())) + for (const auto& additionalInclude : getAdditionalIncludesFromReplacements( + generateType, filePath.parent_path())) { - RapidXmlReaderPtr reader = RapidXmlReader::FromXmlString("<PackagePath package=\""+additionalInclude.first+"\" path=\""+additionalInclude.second+"\"/>"); + RapidXmlReaderPtr reader = RapidXmlReader::FromXmlString( + "<PackagePath package=\"" + additionalInclude.first + "\" path=\"" + + additionalInclude.second + "\"/>"); auto node = reader->getRoot(); auto i = this->readAronInclude(node, filePath, includePaths); - if (not i.empty() && std::find(this->aronIncludes.begin(), this->aronIncludes.end(), i) == this->aronIncludes.end()) + if (not i.empty() && + std::find(this->aronIncludes.begin(), this->aronIncludes.end(), i) == + this->aronIncludes.end()) { this->aronIncludes.push_back(i); } @@ -218,26 +302,37 @@ namespace armarx::aron::typereader::xml if (util::HasTagName(generateType, constantes::OBJECT_TAG)) { const auto nav = readGenerateObject(generateType); - generateObjects.push_back(factory.allGeneratedPublicObjects.at(nav->getObjectName())); + generateObjects.push_back( + factory.allGeneratedPublicObjects.at(nav->getObjectName())); continue; } if (util::HasTagName(generateType, constantes::INT_ENUM_TAG)) { const auto nav = readGenerateIntEnum(generateType); - generateIntEnums.push_back(factory.allGeneratedPublicIntEnums.at(nav->getEnumName())); + generateIntEnums.push_back( + factory.allGeneratedPublicIntEnums.at(nav->getEnumName())); continue; } - throw error::ValueNotValidException("XMLReader", "parse", "Could not find a valid tag inside generatetypes", generateType.name()); + throw error::ValueNotValidException( + "XMLReader", + "parse", + "Could not find a valid tag inside generatetypes", + generateType.name()); } } else { - throw error::AronException(__PRETTY_FUNCTION__, "No generate types found in aron xml '" + filePath.string() + "'."); + throw error::AronException(__PRETTY_FUNCTION__, + "No generate types found in aron xml '" + filePath.string() + + "'."); } } - std::pair<std::string, std::string> Reader::readPackagePathInclude(const RapidXmlReaderNode& node, const std::filesystem::path&, const std::vector<std::filesystem::path>& includePaths) + std::pair<std::string, std::string> + Reader::readPackagePathInclude(const RapidXmlReaderNode& node, + const std::filesystem::path&, + const std::vector<std::filesystem::path>& includePaths) { util::EnforceTagName(node, constantes::PACKAGE_PATH_TAG); std::string package = util::GetAttribute(node, constantes::PACKAGE_ATTRIBUTE_NAME); @@ -250,15 +345,23 @@ namespace armarx::aron::typereader::xml path = simox::alg::replace_all(path, ">", ""); const std::filesystem::path includepath(package + "/" + path); - if (std::optional<fs::path> resolvedPackagePath = resolveRelativePackagePath(includepath, includePaths); resolvedPackagePath.has_value()) + if (std::optional<fs::path> resolvedPackagePath = + resolveRelativePackagePath(includepath, includePaths); + resolvedPackagePath.has_value()) { return {package + "/" + path, "<" + package + "/" + path + ">"}; } - throw error::AronException(__PRETTY_FUNCTION__, "Could not find an file `" + includepath.string() + "`. Search paths are: " + simox::alg::to_string(includePaths)); + throw error::AronException( + __PRETTY_FUNCTION__, + "Could not find an file `" + includepath.string() + + "`. Search paths are: " + simox::alg::to_string(includePaths)); } - std::pair<std::string, std::string> Reader::readInclude(const RapidXmlReaderNode& node, const std::filesystem::path& filepath, const std::vector<std::filesystem::path>& includePaths) + std::pair<std::string, std::string> + Reader::readInclude(const RapidXmlReaderNode& node, + const std::filesystem::path& filepath, + const std::vector<std::filesystem::path>& includePaths) { util::EnforceTagName(node, constantes::INCLUDE_TAG); std::string value = util::GetAttribute(node, constantes::INCLUDE_ATTRIBUTE_NAME); @@ -270,7 +373,10 @@ namespace armarx::aron::typereader::xml return {value, "<" + value + ">"}; } - std::pair<std::string, std::string> Reader::readSystemInclude(const RapidXmlReaderNode& node, const std::filesystem::path&, const std::vector<std::filesystem::path>& includePaths) + std::pair<std::string, std::string> + Reader::readSystemInclude(const RapidXmlReaderNode& node, + const std::filesystem::path&, + const std::vector<std::filesystem::path>& includePaths) { util::EnforceTagName(node, constantes::SYSTEM_INCLUDE_TAG); std::string value = util::GetAttribute(node, constantes::INCLUDE_ATTRIBUTE_NAME); @@ -282,7 +388,10 @@ namespace armarx::aron::typereader::xml return {"", "<" + value + ">"}; } - std::string Reader::readCodeInclude(const RapidXmlReaderNode& node, const std::filesystem::path& filepath, const std::vector<std::filesystem::path>& includePaths) + std::string + Reader::readCodeInclude(const RapidXmlReaderNode& node, + const std::filesystem::path& filepath, + const std::vector<std::filesystem::path>& includePaths) { if (util::HasTagName(node, constantes::PACKAGE_PATH_TAG)) { @@ -297,10 +406,14 @@ namespace armarx::aron::typereader::xml return readSystemInclude(node, filepath, includePaths).second; } - throw error::ValueNotValidException(__PRETTY_FUNCTION__, "Could not parse a code include. The tag is wrong.", node.name()); + throw error::ValueNotValidException( + __PRETTY_FUNCTION__, "Could not parse a code include. The tag is wrong.", node.name()); } - std::string Reader::readAronInclude(const RapidXmlReaderNode& node, const std::filesystem::path& filepath, const std::vector<std::filesystem::path>& includePaths) + std::string + Reader::readAronInclude(const RapidXmlReaderNode& node, + const std::filesystem::path& filepath, + const std::vector<std::filesystem::path>& includePaths) { std::string include; std::string include_unescaped; @@ -320,7 +433,8 @@ namespace armarx::aron::typereader::xml if (not include.empty()) // we found something { // autoinclude the code file (suffix will be replaced by correct language) - std::string codeinclude = simox::alg::replace_last(include, ARON_FILE_SUFFIX, CODE_FILE_SUFFIX); + std::string codeinclude = + simox::alg::replace_last(include, ARON_FILE_SUFFIX, CODE_FILE_SUFFIX); this->systemIncludes.push_back(codeinclude); // parse parent xml file and add objects to alreday known @@ -342,17 +456,22 @@ namespace armarx::aron::typereader::xml return include; } - throw error::ValueNotValidException(__PRETTY_FUNCTION__, "Could not parse an aron include. The tag is wrong.", node.name()); + throw error::ValueNotValidException( + __PRETTY_FUNCTION__, "Could not parse an aron include. The tag is wrong.", node.name()); } - std::vector<std::pair<std::string, std::string>> Reader::getAdditionalIncludesFromReplacements(const RapidXmlReaderNode& node, const std::filesystem::path& filePath) + std::vector<std::pair<std::string, std::string>> + Reader::getAdditionalIncludesFromReplacements(const RapidXmlReaderNode& node, + const std::filesystem::path& filePath) { std::vector<std::pair<std::string, std::string>> ret; for (const auto& repl : constantes::REPLACEMENTS) { auto replacement = repl.second; - if (not replacement.additionalAronDTOXMLIncludePackagePath.first.empty() && not replacement.additionalAronDTOXMLIncludePackagePath.second.empty() && util::HasTagName(node, repl.first)) + if (not replacement.additionalAronDTOXMLIncludePackagePath.first.empty() && + not replacement.additionalAronDTOXMLIncludePackagePath.second.empty() && + util::HasTagName(node, repl.first)) { // we found a string that will be replaced so we might need to add a default include ret.push_back(replacement.additionalAronDTOXMLIncludePackagePath); @@ -374,15 +493,17 @@ namespace armarx::aron::typereader::xml return ret; } - type::ObjectPtr Reader::readGenerateObject(const RapidXmlReaderNode& node) + type::ObjectPtr + Reader::readGenerateObject(const RapidXmlReaderNode& node) { util::EnforceTagName(node, constantes::OBJECT_TAG); return type::Object::DynamicCastAndCheck(factory.create(node, Path())); } - type::IntEnumPtr Reader::readGenerateIntEnum(const RapidXmlReaderNode& node) + type::IntEnumPtr + Reader::readGenerateIntEnum(const RapidXmlReaderNode& node) { util::EnforceTagName(node, constantes::INT_ENUM_TAG); return type::IntEnum::DynamicCastAndCheck(factory.create(node, Path())); } -} +} // namespace armarx::aron::typereader::xml diff --git a/source/RobotAPI/libraries/aron/common/CMakeLists.txt b/source/RobotAPI/libraries/aron/common/CMakeLists.txt index f8109b4a31f9aa10cfb3caae5b557e4a0c96a391..36cbeb1f837f96ea05b131a843654481b5195bb9 100644 --- a/source/RobotAPI/libraries/aron/common/CMakeLists.txt +++ b/source/RobotAPI/libraries/aron/common/CMakeLists.txt @@ -28,6 +28,7 @@ armarx_add_library( aron_conversions/eigen.h json_conversions/armarx.h + json_conversions/framed.h rw/time.h rw/eigen.h @@ -47,6 +48,7 @@ armarx_add_library( aron_conversions/eigen.cpp json_conversions/armarx.cpp + json_conversions/framed.cpp rw/time.cpp rw/eigen.cpp diff --git a/source/RobotAPI/libraries/aron/common/forward_declarations.h b/source/RobotAPI/libraries/aron/common/forward_declarations.h index 626147c3fc35e827a7b905c8a8b0e76babf3f25e..d3c7df9f82d251032ed5f1e49180f74f30cf223f 100644 --- a/source/RobotAPI/libraries/aron/common/forward_declarations.h +++ b/source/RobotAPI/libraries/aron/common/forward_declarations.h @@ -1,13 +1,14 @@ #pragma once - namespace simox::arondto { class AxisAlignedBoundingBox; class Color; class OrientedBox; -} +} // namespace simox::arondto + namespace armarx::arondto { class Names; -} + class FrameID; +} // namespace armarx::arondto diff --git a/source/RobotAPI/libraries/aron/common/json_conversions.h b/source/RobotAPI/libraries/aron/common/json_conversions.h index a56bd44287eb16161f84e5d1ef0f5496903e2669..b29300e6cf827cfd0908521a3b9fa040b3b32a3d 100644 --- a/source/RobotAPI/libraries/aron/common/json_conversions.h +++ b/source/RobotAPI/libraries/aron/common/json_conversions.h @@ -1,3 +1,4 @@ #pragma once #include "json_conversions/armarx.h" +#include "json_conversions/framed.h" diff --git a/source/RobotAPI/libraries/aron/common/json_conversions/framed.cpp b/source/RobotAPI/libraries/aron/common/json_conversions/framed.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa1cf6ae4237e505e00d8ac069824a0004a66e16 --- /dev/null +++ b/source/RobotAPI/libraries/aron/common/json_conversions/framed.cpp @@ -0,0 +1,17 @@ +#include "framed.h" + +#include <RobotAPI/libraries/aron/common/aron/framed.aron.generated.h> + +void +armarx::arondto::to_json(nlohmann::json& j, const FrameID& bo) +{ + j["agent"] = bo.agent; + j["frame"] = bo.frame; +} + +void +armarx::arondto::from_json(const nlohmann::json& j, FrameID& bo) +{ + j.at("agent").get_to(bo.agent); + j.at("frame").get_to(bo.frame); +} diff --git a/source/RobotAPI/libraries/aron/common/json_conversions/framed.h b/source/RobotAPI/libraries/aron/common/json_conversions/framed.h new file mode 100644 index 0000000000000000000000000000000000000000..fb7314e83628ab4ddf951766139fedeee731a0e9 --- /dev/null +++ b/source/RobotAPI/libraries/aron/common/json_conversions/framed.h @@ -0,0 +1,12 @@ +#pragma once + +#include <SimoxUtility/json/json.h> + +#include <RobotAPI/libraries/aron/common/forward_declarations.h> + +namespace armarx::arondto +{ + void to_json(nlohmann::json& j, const FrameID& bo); + void from_json(const nlohmann::json& j, FrameID& bo); + +} // namespace armarx::arondto diff --git a/source/RobotAPI/libraries/aron/core/data/variant/container/Dict.cpp b/source/RobotAPI/libraries/aron/core/data/variant/container/Dict.cpp index 1d85a7f800b038befc8dedd21561c133a1f13439..3e9f067c989d57087dc7311c73c8140cc7c1828a 100644 --- a/source/RobotAPI/libraries/aron/core/data/variant/container/Dict.cpp +++ b/source/RobotAPI/libraries/aron/core/data/variant/container/Dict.cpp @@ -261,9 +261,8 @@ namespace armarx::aron::data const auto& p = data->getPath(); if (not p.hasDirectPrefix(this->getPath())) { - ARMARX_WARNING - << "An element added to a dict does not have a correct path set. This " - "may cause errors. Please use setElemetCopy() instead."; + ARMARX_DEBUG << "An element added to a dict does not have a correct path set. This " + "may cause errors. Please use setElemetCopy() instead."; } } diff --git a/source/RobotAPI/libraries/aron_component_config/RemoteGuiVisitors.cpp b/source/RobotAPI/libraries/aron_component_config/RemoteGuiVisitors.cpp index 89499311e289b0d50bdba2f29637e5b7dc2087d6..b76afea2912134e0ac4f8aed12beb7aceb454603 100644 --- a/source/RobotAPI/libraries/aron_component_config/RemoteGuiVisitors.cpp +++ b/source/RobotAPI/libraries/aron_component_config/RemoteGuiVisitors.cpp @@ -167,39 +167,62 @@ namespace armarx::aron::component_config MakeConfigGuiVisitor::visitListOnEnter(DataInput& o, TypeInput& t) { in_list_ = true; - auto group = RemoteGui::makeSimpleGridLayout(pathToName(o) + "_grid").cols(20); - auto data = data::List::DynamicCastAndCheck(o); auto type = type::List::DynamicCast(t)->getAcceptedType()->getDescriptor(); - if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) == + + if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) != implementedListDescriptors.end()) { - return; + auto group = RemoteGui::makeSimpleGridLayout(pathToName(o) + "_grid").cols(20); + auto data = data::List::DynamicCastAndCheck(o); + + for (const auto& el : data->getElements()) + { + group.addChild(RemoteGui::makeLineEdit(pathToName(el)) + .value(factories::VariantHelper::make(type)->to_string(el)), + 10); + group.addHSpacer(8); + group.addChild(RemoteGui::makeButton(pathToName(el) + "_button") + .label("-") + .toolTip("Remove List Element"), + 2); + } + group_hierarchy_.back()->addChild( + RemoteGui::makeGroupBox(pathToName(o) + "_grp") + .label(o->getPath().getLastElement()) + .collapsed(true) + .addChild(group) + .addChild(RemoteGui::makeButton(pathToName(o) + "_add") + .label("+") + .toolTip("Add new list entry."))); } - for (const auto& el : data->getElements()) + else { - group.addChild(RemoteGui::makeLineEdit(pathToName(el)) - .value(factories::VariantHelper::make(type)->to_string(el)), - 10); - group.addHSpacer(8); - group.addChild(RemoteGui::makeButton(pathToName(el) + "_button") - .label("-") - .toolTip("Remove List Element"), - 2); + std::string name = pathToName(o); + group_hierarchy_.emplace_back(std::make_shared<RemoteGui::detail::GroupBoxBuilder>( + RemoteGui::makeGroupBox(name))); } - group_hierarchy_.back()->addChild( - RemoteGui::makeGroupBox(pathToName(o) + "_grp") - .label(o->getPath().getLastElement()) - .collapsed(true) - .addChild(group) - .addChild(RemoteGui::makeButton(pathToName(o) + "_add") - .label("+") - .toolTip("Add new list entry."))); } void - MakeConfigGuiVisitor::visitListOnExit(DataInput& /*unused*/, TypeInput& /*unused*/) + MakeConfigGuiVisitor::visitListOnExit(DataInput& /*unused*/, TypeInput& t) { in_list_ = false; + + auto type = type::List::DynamicCast(t)->getAcceptedType()->getDescriptor(); + + if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) != + implementedListDescriptors.end()) + { + } + else + { + auto builder = *group_hierarchy_.back(); + group_hierarchy_.pop_back(); + if (not group_hierarchy_.empty()) + { + group_hierarchy_.back()->addChild(builder); + } + } } void @@ -232,7 +255,7 @@ namespace armarx::aron::component_config group.addChild(RemoteGui::makeLabel(name + "_label").value(q->getPath().getLastElement())); group.addHSpacer(); - if (cols == 4 && rows == 4) + if (cols == 4 and rows == 4) { // Poses const auto& matrix = aron::data::converter::AronEigenConverter::ConvertToMatrix4f(data); @@ -488,36 +511,55 @@ namespace armarx::aron::component_config GetValueFromMapVisitor::visitListOnEnter(DataInput& o, TypeInput& t) { in_list_ = true; - auto data = data::List::DynamicCastAndCheck(o); + auto type = type::List::DynamicCast(t)->getAcceptedType()->getDescriptor(); - if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) == + + if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) != implementedListDescriptors.end()) { - return; - } - const auto& elements = data->getElements(); - for (const auto& [idx, el] : armarx::MakeIndexedContainer(elements)) - { - if (proxy_->getButtonClicked(pathToName(el) + "_button")) + auto data = data::List::DynamicCastAndCheck(o); + + const auto& elements = data->getElements(); + for (const auto& [idx, el] : armarx::MakeIndexedContainer(elements)) { - data->removeElement(idx); + if (proxy_->getButtonClicked(pathToName(el) + "_button")) + { + data->removeElement(idx); + tab_rebuild_required_ = true; + } + auto gui_value = proxy_->getValue<std::string>(pathToName(el)).get(); + factories::VariantHelper::make(type)->set_value_from_string(el, gui_value); + } + if (proxy_->getButtonClicked(pathToName(o) + "_add")) + { + data->addElement(factories::VariantHelper::make(type)->from_string( + "", o->getPath().withIndex(data->childrenSize()))); tab_rebuild_required_ = true; } - auto gui_value = proxy_->getValue<std::string>(pathToName(el)).get(); - factories::VariantHelper::make(type)->set_value_from_string(el, gui_value); } - if (proxy_->getButtonClicked(pathToName(o) + "_add")) + else { - data->addElement(factories::VariantHelper::make(type)->from_string( - "", o->getPath().withIndex(data->childrenSize()))); - tab_rebuild_required_ = true; + // TODO? Adapt to additional GroupBoxBuilder added by + // MakeConfigGuiVisitor::visitListOnEnter in this case? } } void - GetValueFromMapVisitor::visitListOnExit(DataInput&, TypeInput&) + GetValueFromMapVisitor::visitListOnExit(DataInput&, TypeInput& t) { in_list_ = false; + + auto type = type::List::DynamicCast(t)->getAcceptedType()->getDescriptor(); + + if (std::find(implementedListDescriptors.begin(), implementedListDescriptors.end(), type) != + implementedListDescriptors.end()) + { + } + else + { + // TODO? Adapt to additional GroupBoxBuilder added by + // MakeConfigGuiVisitor::visitListOnEnter in this case? + } } bool diff --git a/source/RobotAPI/libraries/robotapi/CMakeLists.txt b/source/RobotAPI/libraries/robot_name_service/CMakeLists.txt similarity index 100% rename from source/RobotAPI/libraries/robotapi/CMakeLists.txt rename to source/RobotAPI/libraries/robot_name_service/CMakeLists.txt diff --git a/source/RobotAPI/libraries/robot_name_service/client/CMakeLists.txt b/source/RobotAPI/libraries/robot_name_service/client/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..c59cbcaa176ff20936c9ef81a2c0f4f68b3a65d5 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/client/CMakeLists.txt @@ -0,0 +1,27 @@ +set(LIB_NAME robot_name_service_client) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +armarx_add_library( + LIBS + ArmarXCoreInterfaces + ArmarXCore + ArmarXCoreObservers + + RobotAPI::Core + RobotAPI::skills::core + RobotAPIInterfaces + armem + robot_name_service_core + + + SOURCES + plugins.cpp + Plugin.cpp + HEADERS + plugins.h + Plugin.h +) + +add_library(RobotAPI::robot_name_service_client ALIAS robot_name_service_client) diff --git a/source/RobotAPI/libraries/robot_name_service/client/Plugin.cpp b/source/RobotAPI/libraries/robot_name_service/client/Plugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..89f973a6e3f4588096a977091cdcbbeb4742bc9a --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/client/Plugin.cpp @@ -0,0 +1,145 @@ +#include "Plugin.h" + +#include <ArmarXCore/core/Component.h> + +namespace armarx +{ + + RobotNameServiceComponentPluginUser::RobotNameServiceComponentPluginUser() + { + addPlugin(plugin); + } +} // namespace armarx + +namespace armarx::plugins +{ + + void + RobotNameServiceComponentPlugin::postOnInitComponent() + { + using namespace armarx::robot_name_service::client::constants; + + nlohmann::json config = nlohmann::json::parse(properties.configJson); + + if (config.contains(CONFIG_ROBOT_NAME)) + { + robot.name = config[CONFIG_ROBOT_NAME]; + } + + if (config.contains(CONFIG_XML_PACKAGE_PATH)) + { + auto config_pp = config[CONFIG_XML_PACKAGE_PATH]; + if (config_pp.contains(CONFIG_XML_PACKAGE_PATH_PACKAGE)) + { + robot.xmlPackagePath.package = config_pp[CONFIG_XML_PACKAGE_PATH_PACKAGE]; + } + if (config_pp.contains(CONFIG_XML_PACKAGE_PATH_PATH)) + { + robot.xmlPackagePath.path = config_pp[CONFIG_XML_PACKAGE_PATH_PATH]; + } + } + auto& p = parent<RobotNameServiceComponentPluginUser>(); + + if (config.contains(CONFIG_MNS_PRX)) + { + p.usingProxy(config[CONFIG_MNS_PRX]); + } + if (config.contains(CONFIG_SKILLS_MANAGER_PRX)) + { + p.usingProxy(config[CONFIG_SKILLS_MANAGER_PRX]); + } + + if (config.contains(CONFIG_ARMS)) + { + for (const auto& [k, config_arm] : config[CONFIG_ARMS].items()) + { + auto& arm = robot.arms[k]; + + if (config_arm.contains(CONFIG_ARM_KINEMATIC_CHAIN_NAME)) + { + arm.kinematicChainName = config_arm[CONFIG_ARM_KINEMATIC_CHAIN_NAME]; + } + + if (config_arm.contains(CONFIG_ARM_HAND)) + { + auto config_hand = config_arm[CONFIG_ARM_HAND]; + + if (config_hand.contains(CONFIG_ARM_HAND_NAME)) + { + arm.hand.name = config_hand[CONFIG_ARM_HAND_NAME]; + } + if (config_hand.contains(CONFIG_ARM_HAND_FT_NAME)) + { + arm.hand.ft_name = config_hand[CONFIG_ARM_HAND_FT_NAME]; + } + + if (config_hand.contains(CONFIG_ARM_HAND_PRX)) + { + p.usingProxy(config_hand[CONFIG_ARM_HAND_PRX]); + } + } + } + } + } + + void + RobotNameServiceComponentPlugin::preOnConnectComponent() + { + using namespace armarx::robot_name_service::client::constants; + + nlohmann::json config = nlohmann::json::parse(properties.configJson); + + auto& p = parent<RobotNameServiceComponentPluginUser>(); + + if (config.contains(CONFIG_MNS_PRX)) + { + auto mns = p.getProxy<armarx::armem::mns::MemoryNameSystemInterfacePrx>( + config[CONFIG_MNS_PRX]); + robot.memoryNameSystem = armarx::armem::client::MemoryNameSystem(mns, &p); + } + + if (config.contains(CONFIG_SKILLS_MANAGER_PRX)) + { + auto skill_manager = p.getProxy<armarx::skills::manager::dti::SkillManagerInterfacePrx>( + config[CONFIG_SKILLS_MANAGER_PRX]); + robot.skillManager = skill_manager; + } + + if (config.contains(CONFIG_ARMS)) + { + for (const auto& [k, config_arm] : config[CONFIG_ARMS].items()) + { + auto& arm = robot.arms[k]; + + if (config_arm.contains(CONFIG_ARM_HAND)) + { + auto config_hand = config_arm[CONFIG_ARM_HAND]; + + if (config_hand.contains(CONFIG_ARM_HAND_PRX)) + { + auto hand_prx = p.getProxy<armarx::HandUnitInterfacePrx>( + config_hand[CONFIG_ARM_HAND_PRX]); + arm.hand.handUnitInterface = hand_prx; + } + } + } + } + + // finally commit robot + properties.rns->registerRobot(robot.toIce()); + } + + void + RobotNameServiceComponentPlugin::postCreatePropertyDefinitions(PropertyDefinitionsPtr& def) + { + def->optional(properties.configJson, "robotConfigJson"); + def->component(properties.rns); + } + + void + RobotNameServiceComponentPlugin::preOnDisconnectComponent() + { + properties.rns->unregisterRobot(robot.name); + } + +} // namespace armarx::plugins diff --git a/source/RobotAPI/libraries/robot_name_service/client/Plugin.h b/source/RobotAPI/libraries/robot_name_service/client/Plugin.h new file mode 100644 index 0000000000000000000000000000000000000000..813003f98554196e32fa9cc2c27eb650cea5f914 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/client/Plugin.h @@ -0,0 +1,81 @@ +#pragma once +#include <experimental/memory> +#include <functional> +#include <queue> +#include <shared_mutex> +#include <thread> +#include <type_traits> + +#include <SimoxUtility/json/json.h> + +#include <ArmarXCore/core/ComponentPlugin.h> +#include <ArmarXCore/core/ManagedIceObject.h> +#include <ArmarXCore/core/services/tasks/RunningTask.h> + +#include <RobotAPI/interface/robot_name_service/RobotNameServiceInterface.h> +#include <RobotAPI/libraries/robot_name_service/core/Robot.h> + +namespace armarx::robot_name_service::client::constants +{ + const auto CONFIG_ROBOT_NAME = "name"; + const auto CONFIG_XML_PACKAGE_PATH = "xml_package_path"; + const auto CONFIG_XML_PACKAGE_PATH_PACKAGE = "package"; + const auto CONFIG_XML_PACKAGE_PATH_PATH = "path"; + const auto CONFIG_MNS_PRX = "mns_prx"; + const auto CONFIG_SKILLS_MANAGER_PRX = "manager_prx"; + const auto CONFIG_ARMS = "arms"; + const auto CONFIG_ARM_KINEMATIC_CHAIN_NAME = "kinematic_chain_name"; + const auto CONFIG_ARM_HAND = "hand"; + const auto CONFIG_ARM_HAND_NAME = "name"; + const auto CONFIG_ARM_HAND_FT_NAME = "ft_name"; + const auto CONFIG_ARM_HAND_PRX = "hand_prx"; + // TODO +} // namespace armarx::robot_name_service::client::constants + +namespace armarx +{ + class RobotNameServiceComponentPluginUser; // forward declaration +} + +namespace armarx::plugins +{ + class RobotNameServiceComponentPlugin : public ComponentPlugin + { + public: + using ComponentPlugin::ComponentPlugin; + + void postOnInitComponent() override; + + void preOnConnectComponent() override; + + void postCreatePropertyDefinitions(PropertyDefinitionsPtr& properties) override; + + void preOnDisconnectComponent() override; + + private: + struct Properties + { + std::string configJson = "{}"; + + armarx::robot_name_service::dti::RobotNameServiceInterfacePrx rns; + } properties; + + armarx::robot_name_service::core::Robot robot; + + friend class armarx::RobotNameServiceComponentPluginUser; + }; +} // namespace armarx::plugins + +namespace armarx + +{ + class RobotNameServiceComponentPluginUser : virtual public ManagedIceObject + { + public: + RobotNameServiceComponentPluginUser(); + + + private: + std::experimental::observer_ptr<plugins::RobotNameServiceComponentPlugin> plugin = nullptr; + }; +} // namespace armarx diff --git a/source/RobotAPI/libraries/robot_name_service/client/plugins.cpp b/source/RobotAPI/libraries/robot_name_service/client/plugins.cpp new file mode 100644 index 0000000000000000000000000000000000000000..51f19fb344c000a07436e20ac3881c38cee9d904 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/client/plugins.cpp @@ -0,0 +1,6 @@ +#include "plugins.h" + +namespace armarx::robot_name_service::client +{ + +} // namespace armarx::robot_name_service::client diff --git a/source/RobotAPI/libraries/robot_name_service/client/plugins.h b/source/RobotAPI/libraries/robot_name_service/client/plugins.h new file mode 100644 index 0000000000000000000000000000000000000000..a37a92b69011490ebf30f0f7c28fdccea5d28834 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/client/plugins.h @@ -0,0 +1,8 @@ +#pragma once + +#include "Plugin.h" + +namespace armarx::robot_name_service::client +{ + +} // namespace armarx::robot_name_service::client diff --git a/source/RobotAPI/libraries/robot_name_service/core/CMakeLists.txt b/source/RobotAPI/libraries/robot_name_service/core/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..6c702dd1e210e92e3e14c4d02a4e6d870a731c44 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/core/CMakeLists.txt @@ -0,0 +1,24 @@ +set(LIB_NAME robot_name_service_core) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +armarx_add_library( + LIBS + ArmarXCoreInterfaces + ArmarXCore + ArmarXCoreObservers + + RobotAPI::Core + RobotAPI::skills::core + RobotAPIInterfaces + armem + + + SOURCES + Robot.cpp + HEADERS + Robot.h +) + +add_library(RobotAPI::robot_name_service_core ALIAS robot_name_service_core) diff --git a/source/RobotAPI/libraries/robot_name_service/core/Robot.cpp b/source/RobotAPI/libraries/robot_name_service/core/Robot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bfa5fbfe5d529388f9b545d93adae68d5f853f57 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/core/Robot.cpp @@ -0,0 +1,79 @@ +#include "Robot.h" + +namespace armarx::robot_name_service::core +{ + + void + Hand::fromIce(const dto::Hand& r) + { + name = r.name; + ft_name = r.ft_name; + handUnitInterface = r.handUnitInterface; + } + + dto::Hand + Hand::toIce() const + { + dto::Hand r; + r.name = name; + r.ft_name = ft_name; + r.handUnitInterface = handUnitInterface; + return r; + } + + void + Arm::fromIce(const dto::Arm& r) + { + kinematicChainName = r.kinematicChainName; + hand.fromIce(r.hand); + } + + dto::Arm + Arm::toIce() const + { + dto::Arm r; + r.kinematicChainName = kinematicChainName; + r.hand = hand.toIce(); + return r; + } + + void + Robot::fromIce(const dto::Robot& r) + { + name = r.name; + xmlPackagePath = r.xmlPackagePath; + memoryNameSystem = armarx::armem::client::MemoryNameSystem(r.memoryNameSystem); + skillManager = r.skillManager; + + arms.clear(); + for (const auto& [k, a] : r.arms) + { + arms[k].fromIce(a); + } + + kinematicUnitInterface = r.kinematicUnitInterface; + platformUnitInterface = r.platformUnitInterface; + localizationUnitInterface = r.localizationUnitInterface; + } + + dto::Robot + Robot::toIce() const + { + dto::Robot r; + r.name = name; + r.xmlPackagePath = xmlPackagePath; + r.memoryNameSystem = memoryNameSystem.getMemoryNameSystem(); + r.skillManager = skillManager; + + for (const auto& [k, a] : arms) + { + r.arms[k] = a.toIce(); + } + + r.kinematicUnitInterface = kinematicUnitInterface; + r.platformUnitInterface = platformUnitInterface; + r.localizationUnitInterface = localizationUnitInterface; + return r; + } + +} // namespace armarx::robot_name_service::core diff --git a/source/RobotAPI/libraries/robot_name_service/core/Robot.h b/source/RobotAPI/libraries/robot_name_service/core/Robot.h new file mode 100644 index 0000000000000000000000000000000000000000..243d94c1b2a7f72d8f050ee770d06611da5a83cf --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/core/Robot.h @@ -0,0 +1,59 @@ +#pragma once + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include <RobotAPI/interface/robot_name_service/RobotNameServiceInterface.h> +#include <RobotAPI/libraries/armem/client/MemoryNameSystem.h> + +namespace armarx::robot_name_service::core +{ + class Hand + { + public: + void fromIce(const armarx::robot_name_service::dto::Hand& r); + armarx::robot_name_service::dto::Hand toIce() const; + + public: + std::string name; + std::string ft_name; + armarx::HandUnitInterfacePrx handUnitInterface; + }; + + class Arm + { + public: + void fromIce(const armarx::robot_name_service::dto::Arm& r); + armarx::robot_name_service::dto::Arm toIce() const; + + public: + std::string kinematicChainName; + Hand hand; + }; + + class Robot + { + public: + void fromIce(const armarx::robot_name_service::dto::Robot& r); + armarx::robot_name_service::dto::Robot toIce() const; + + public: + // header + std::string name; + armarx::data::PackagePath xmlPackagePath; + + // memory and skills + armarx::armem::client::MemoryNameSystem memoryNameSystem; + armarx::skills::manager::dti::SkillManagerInterfacePrx skillManager; + + // kinematic stuff + std::map<std::string, Arm> arms; + + // units + armarx::KinematicUnitInterfacePrx kinematicUnitInterface; + armarx::PlatformUnitInterfacePrx platformUnitInterface; + armarx::LocalizationUnitInterfacePrx localizationUnitInterface; + }; +} // namespace armarx::robot_name_service::core diff --git a/source/RobotAPI/libraries/robot_name_service/server/CMakeLists.txt b/source/RobotAPI/libraries/robot_name_service/server/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d0c268aa439c501d38505d3fab455bf6b4a9b876 --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/server/CMakeLists.txt @@ -0,0 +1,25 @@ +set(LIB_NAME robot_name_service_server) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + +armarx_add_library( + LIBS + ArmarXCoreInterfaces + ArmarXCore + ArmarXCoreObservers + + RobotAPI::Core + RobotAPI::skills::core + RobotAPIInterfaces + armem + robot_name_service_core + + + SOURCES + plugins.cpp + HEADERS + plugins.h +) + +add_library(RobotAPI::robot_name_service_server ALIAS robot_name_service_server) diff --git a/source/RobotAPI/libraries/robot_name_service/server/plugins.cpp b/source/RobotAPI/libraries/robot_name_service/server/plugins.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fc90afa38d5542f6b1c156f359d72b912be3f4be --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/server/plugins.cpp @@ -0,0 +1,6 @@ +#include "plugins.h" + +namespace armarx::robot_name_service::server +{ + +} // namespace armarx::robot_name_service::server diff --git a/source/RobotAPI/libraries/robot_name_service/server/plugins.h b/source/RobotAPI/libraries/robot_name_service/server/plugins.h new file mode 100644 index 0000000000000000000000000000000000000000..ca43f153ee67d78b0b617370b93bec94e5cee3de --- /dev/null +++ b/source/RobotAPI/libraries/robot_name_service/server/plugins.h @@ -0,0 +1,6 @@ +#pragma once + +namespace armarx::robot_name_service::server +{ + +} // namespace armarx::robot_name_service::server diff --git a/source/RobotAPI/libraries/skills/core/Skill.cpp b/source/RobotAPI/libraries/skills/core/Skill.cpp index 7d94de3e90815a156289948fae26463ce5f28247..0b7a32df224d925ced896414d7d47f540f04a0d5 100644 --- a/source/RobotAPI/libraries/skills/core/Skill.cpp +++ b/source/RobotAPI/libraries/skills/core/Skill.cpp @@ -245,7 +245,7 @@ namespace armarx std::string("The skill '" + getSkillId().toString() + "' was asked to stop."); message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage; - throw error::SkillAbortedException(message); + throw error::SkillAbortedException(__PRETTY_FUNCTION__, message); return; } @@ -256,7 +256,7 @@ namespace armarx message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage; ARMARX_WARNING << message; - throw error::SkillFailedException(message); + throw error::SkillFailedException(__PRETTY_FUNCTION__, message); } } diff --git a/source/RobotAPI/libraries/skills/core/SkillProxy.cpp b/source/RobotAPI/libraries/skills/core/SkillProxy.cpp index f73c43dfd152c756f80e5ac3e83e901fe59e8ee1..ea4fc4d1a0192d5ad4819be4248df3ea52566164 100644 --- a/source/RobotAPI/libraries/skills/core/SkillProxy.cpp +++ b/source/RobotAPI/libraries/skills/core/SkillProxy.cpp @@ -12,9 +12,19 @@ namespace armarx manager(manager) { ARMARX_CHECK_NOT_NULL(manager); - skillDescription = SkillDescription::FromIce( - manager->getSkillDescription(skillId.toManagerIce()).value()); - ARMARX_CHECK(skillDescription.skillId.isFullySpecified()); + IceUtil::Optional<manager::dto::SkillDescription> description = + manager->getSkillDescription(skillId.toManagerIce()); + if (description) + { + skillDescription = SkillDescription::FromIce(description.value()); + ARMARX_CHECK(skillDescription.skillId.isFullySpecified()); + } + else + { + std::stringstream reason; + reason << "No skill with ID " << skillId << " found"; + throw error::SkillNotFoundException(__PRETTY_FUNCTION__, reason.str()); + } } SkillProxy::SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, diff --git a/source/RobotAPI/libraries/skills/core/SkillStatusUpdate.h b/source/RobotAPI/libraries/skills/core/SkillStatusUpdate.h index d517fc1ec8aa862d041a4d18fcbcc981f3c6f3f4..ace31510a8a362ed45289c4facdd51c97ba22fbb 100644 --- a/source/RobotAPI/libraries/skills/core/SkillStatusUpdate.h +++ b/source/RobotAPI/libraries/skills/core/SkillStatusUpdate.h @@ -86,6 +86,12 @@ namespace armarx return true; } + bool + hasBeenSucceeded() const + { + return status == TerminatedSkillStatus::Succeeded; + } + manager::dto::SkillStatusUpdate toManagerIce() const; provider::dto::SkillStatusUpdate toProviderIce() const; @@ -111,6 +117,12 @@ namespace armarx status == ActiveOrTerminatedSkillStatus::Aborted; } + bool + hasBeenSucceeded() const + { + return status == ActiveOrTerminatedSkillStatus::Succeeded; + } + manager::dto::SkillStatusUpdate toManagerIce() const; provider::dto::SkillStatusUpdate toProviderIce() const; @@ -195,6 +207,12 @@ namespace armarx status == SkillStatus::Aborted; } + bool + hasBeenSucceeded() const + { + return status == SkillStatus::Succeeded; + } + manager::dto::SkillStatusUpdate toManagerIce() const; provider::dto::SkillStatusUpdate toProviderIce() const; diff --git a/source/RobotAPI/libraries/skills/core/error/Exception.h b/source/RobotAPI/libraries/skills/core/error/Exception.h index 5e10baed1e9af3131226320397c11919d7a826e0..9e01205b27e74eddc69fa8ff7c28d2646eafc9e2 100644 --- a/source/RobotAPI/libraries/skills/core/error/Exception.h +++ b/source/RobotAPI/libraries/skills/core/error/Exception.h @@ -48,32 +48,38 @@ namespace armarx::skills::error } }; - class SkillAbortedException : public armarx::LocalException + /** + * @brief Indicates that a skill was not found, e.g., by the skill manager. + */ + class SkillNotFoundException : public SkillException { public: - SkillAbortedException() = delete; + SkillNotFoundException() = delete; - SkillAbortedException(const std::string& reason) : LocalException(reason) + SkillNotFoundException(const std::string& prettymethod, const std::string& reason) : + SkillException(prettymethod, reason) { } + }; + + class SkillAbortedException : public SkillException + { + public: + SkillAbortedException() = delete; SkillAbortedException(const std::string& prettymethod, const std::string& reason) : - LocalException(prettymethod + ": " + reason + ".") + SkillException(prettymethod, reason) { } }; - class SkillFailedException : public armarx::LocalException + class SkillFailedException : public SkillException { public: SkillFailedException() = delete; - SkillFailedException(const std::string& reason) : LocalException(reason) - { - } - SkillFailedException(const std::string& prettymethod, const std::string& reason) : - LocalException(prettymethod + ": " + reason + ".") + SkillException(prettymethod, reason) { } }; diff --git a/source/RobotAPI/libraries/skills/provider/mixins/GraspReadingSkillMixin.h b/source/RobotAPI/libraries/skills/provider/mixins/GraspReadingSkillMixin.h index d69f390dadcb7eb0daed57f8f7a82148c055fb52..abcfeb2d285a93254dfe1c029d7e7f1763709070 100644 --- a/source/RobotAPI/libraries/skills/provider/mixins/GraspReadingSkillMixin.h +++ b/source/RobotAPI/libraries/skills/provider/mixins/GraspReadingSkillMixin.h @@ -10,8 +10,10 @@ namespace armarx::skills::mixin { armem::grasping::known_grasps::Reader graspReader; - GraspReadingSkillMixin(armem::client::MemoryNameSystem& mns) : graspReader(mns) - {} + GraspReadingSkillMixin(const armem::grasping::known_grasps::Reader& r) : graspReader(r) + { + + } }; struct SpecificGraspReadingSkillMixin @@ -19,7 +21,7 @@ namespace armarx::skills::mixin std::string objectEntityId; armem::grasping::known_grasps::Reader graspReader; - SpecificGraspReadingSkillMixin(const std::string& n, armem::client::MemoryNameSystem& mns) : objectEntityId(n), graspReader(mns) + SpecificGraspReadingSkillMixin(const std::string& n, const armem::grasping::known_grasps::Reader& r) : objectEntityId(n), graspReader(r) {} }; } diff --git a/source/RobotAPI/libraries/skills/provider/mixins/MemoryReadingSkillMixin.h b/source/RobotAPI/libraries/skills/provider/mixins/MemoryReadingSkillMixin.h index 214fbac445e675d2bde67ae38372fa271a30b504..be453b9283d3db61b46df79fba50a3e2fb13be80 100644 --- a/source/RobotAPI/libraries/skills/provider/mixins/MemoryReadingSkillMixin.h +++ b/source/RobotAPI/libraries/skills/provider/mixins/MemoryReadingSkillMixin.h @@ -10,7 +10,8 @@ namespace armarx::skills::mixin { armem::client::Reader memoryReader; - //MemoryReadingSkillMixin(armem::client::MemoryNameSystem& mns) : memoryReader(mns) - //{} + MemoryReadingSkillMixin(const armem::client::Reader& r) : memoryReader(r) + { + } }; -} +} // namespace armarx::skills::mixin diff --git a/source/RobotAPI/libraries/skills/provider/mixins/ObjectReadingSkillMixin.h b/source/RobotAPI/libraries/skills/provider/mixins/ObjectReadingSkillMixin.h index ecf3d2a64ef0a5d3f01b5b9b23839b1db14cb921..84a7746a74177df50eff3f15c768885f77b6e446 100644 --- a/source/RobotAPI/libraries/skills/provider/mixins/ObjectReadingSkillMixin.h +++ b/source/RobotAPI/libraries/skills/provider/mixins/ObjectReadingSkillMixin.h @@ -10,8 +10,9 @@ namespace armarx::skills::mixin { armem::obj::instance::Reader objectReader; - ObjectReadingSkillMixin(armem::client::MemoryNameSystem& mns) : objectReader(mns) - {} + ObjectReadingSkillMixin(const armem::obj::instance::Reader& r) : objectReader(r) + { + } }; struct SpecificObjectReadingSkillMixin @@ -19,7 +20,10 @@ namespace armarx::skills::mixin std::string objectEntityId; armem::obj::instance::Reader objectReader; - SpecificObjectReadingSkillMixin(const std::string& n, armem::client::MemoryNameSystem& mns) : objectEntityId(n), objectReader(mns) - {} + SpecificObjectReadingSkillMixin(const std::string& n, + const armem::obj::instance::Reader& r) : + objectEntityId(n), objectReader(r) + { + } }; -} +} // namespace armarx::skills::mixin diff --git a/source/RobotAPI/libraries/skills/provider/mixins/ObjectWritingSkillMixin.h b/source/RobotAPI/libraries/skills/provider/mixins/ObjectWritingSkillMixin.h index edf8e6a2ef7360417f2e951ed36e0b24c62f4cef..2bf09e174f06717a79a00d503d325e9ed6be090d 100644 --- a/source/RobotAPI/libraries/skills/provider/mixins/ObjectWritingSkillMixin.h +++ b/source/RobotAPI/libraries/skills/provider/mixins/ObjectWritingSkillMixin.h @@ -10,7 +10,8 @@ namespace armarx::skills::mixin { armem::obj::instance::Writer objectWriter; - ObjectWritingSkillMixin(armem::client::MemoryNameSystem& mns) : objectWriter(mns) - {} + ObjectWritingSkillMixin(const armem::obj::instance::Writer& r) : objectWriter(r) + { + } }; -} +} // namespace armarx::skills::mixin diff --git a/source/RobotAPI/libraries/skills/provider/mixins/RobotReadingSkillMixin.h b/source/RobotAPI/libraries/skills/provider/mixins/RobotReadingSkillMixin.h index 631491ddcfb70a7fc1c6930317d30bbb3c969dca..9394445a59534482aee91a15a6e5e16429483564 100644 --- a/source/RobotAPI/libraries/skills/provider/mixins/RobotReadingSkillMixin.h +++ b/source/RobotAPI/libraries/skills/provider/mixins/RobotReadingSkillMixin.h @@ -10,9 +10,9 @@ namespace armarx::skills::mixin { armem::robot_state::VirtualRobotReader robotReader; - RobotReadingSkillMixin(armem::client::MemoryNameSystem& mns) : - robotReader(mns) - {} + RobotReadingSkillMixin(const armem::robot_state::VirtualRobotReader& r) : robotReader(r) + { + } }; struct SpecificRobotReadingSkillMixin @@ -20,9 +20,10 @@ namespace armarx::skills::mixin std::string robotName; armem::robot_state::VirtualRobotReader robotReader; - SpecificRobotReadingSkillMixin(const std::string& rn, armem::client::MemoryNameSystem& mns) : - robotName(rn), - robotReader(mns) - {} + SpecificRobotReadingSkillMixin(const std::string& rn, + const armem::robot_state::VirtualRobotReader& r) : + robotName(rn), robotReader(r) + { + } }; -} +} // namespace armarx::skills::mixin