diff --git a/source/RobotAPI/components/ArViz/Client/Elements.cpp b/source/RobotAPI/components/ArViz/Client/Elements.cpp index f479f4ef5456fc7a88d0f3a0fa87d2e77e9aab66..d97fea3b03d55ddb82a379628197a5f04ffc3d74 100644 --- a/source/RobotAPI/components/ArViz/Client/Elements.cpp +++ b/source/RobotAPI/components/ArViz/Client/Elements.cpp @@ -107,9 +107,9 @@ namespace armarx::viz float angle = std::acos(naturalDir.dot(dir)); if (cross.squaredNorm() < 1.0e-12f) { - // Directions are almost colinear ==> Do no rotation + // Directions are almost colinear ==> Angle is either 0 or 180 deg cross = Eigen::Vector3f::UnitX(); - angle = 0.0f; + // Keep angle } Eigen::Vector3f axis = cross.normalized(); Eigen::Quaternionf ori(Eigen::AngleAxisf(angle, axis)); diff --git a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp index 2c93faf7fa4a464909bd6dea214c74022571474f..e52609fa085feac2dc4ef8fc93f68127aef8b040 100644 --- a/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp +++ b/source/RobotAPI/components/ArViz/Example/ArVizExample.cpp @@ -275,13 +275,18 @@ namespace armarx void fillExampleLayer(viz::Layer& layer, double timeInSeconds) { + for (int i = 0; i < 6; ++i) { - Eigen::Vector3f pos = Eigen::Vector3f::Zero(); - pos.z() = +300.0f; + Eigen::Vector3f pos = Eigen::Vector3f(-200.0, 200.0, 300); + pos.x() += -300 * i; + + Eigen::Vector3f normal = Eigen::Vector3f::Zero(); + normal(i / 2) = (i % 2 == 0 ? 1.0 : -1.0); - viz::ArrowCircle circle = viz::ArrowCircle("circle") + viz::ArrowCircle circle = viz::ArrowCircle("circle " + std::to_string(i)) .position(pos) .radius(100.0f) + .normal(normal) .width(10.0f) .color(viz::Color::fromRGBA(255, 0, 255)); diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp index eab8bf5e6dd721395647519d3e04fecc62ffbb7f..f77e01607b0036a2f9525b88dc919f0f1882608b 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.cpp @@ -47,6 +47,7 @@ namespace armarx::armem::server::robot_state RobotStateMemory::RobotStateMemory() : descriptionSegment(iceAdapter()), proprioceptionSegment(iceAdapter()), + exteroceptionSegment(iceAdapter()), localizationSegment(iceAdapter()), commonVisu(descriptionSegment, proprioceptionSegment, localizationSegment) { @@ -86,6 +87,7 @@ namespace armarx::armem::server::robot_state setMemoryName(armem::robot_state::memoryID.memoryName); descriptionSegment.defineProperties(defs, prefix + "desc."); + exteroceptionSegment.defineProperties(defs, prefix + "ext."); proprioceptionSegment.defineProperties(defs, prefix + "prop."); localizationSegment.defineProperties(defs, prefix + "loc."); commonVisu.defineProperties(defs, prefix + "visu."); @@ -104,6 +106,7 @@ namespace armarx::armem::server::robot_state { descriptionSegment.init(); proprioceptionSegment.init(); + exteroceptionSegment.init(); localizationSegment.init(); commonVisu.init(); diff --git a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h index aed5fec8081906601372f4851afeb44d25f82507..70513a0cf56ff0cd6d82603cc3266fb405e5af3d 100644 --- a/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h +++ b/source/RobotAPI/components/armem/server/RobotStateMemory/RobotStateMemory.h @@ -38,6 +38,7 @@ #include <RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h> #include <RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h> #include <RobotAPI/libraries/armem_robot_state/server/proprioception/Segment.h> +#include <RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.h> namespace armarx::plugins @@ -105,6 +106,9 @@ namespace armarx::armem::server::robot_state proprioception::Segment proprioceptionSegment; armem::data::MemoryID robotUnitProviderID; + // - exteroception + exteroception::Segment exteroceptionSegment; + // - localization localization::Segment localizationSegment; diff --git a/source/RobotAPI/components/units/HandUnit.h b/source/RobotAPI/components/units/HandUnit.h index 3816605266f1bc44dce40a752bfe94b14af8177b..0241caff84c93709619c0bcabae2736d443e5c1b 100644 --- a/source/RobotAPI/components/units/HandUnit.h +++ b/source/RobotAPI/components/units/HandUnit.h @@ -172,7 +172,7 @@ namespace armarx // HandUnitInterface interface public: - std::string getHandName(const Ice::Current&) override; + std::string getHandName(const Ice::Current& = Ice::emptyCurrent) override; void setJointForces(const NameValueMap& targetJointForces, const Ice::Current&) override; void sendJointCommands(const NameCommandMap& targetJointCommands, const Ice::Current&) override; }; diff --git a/source/RobotAPI/interface/CMakeLists.txt b/source/RobotAPI/interface/CMakeLists.txt index 5d9d3c04b278f8032125f98cf82d63aaa8253155..9e290598fee8047f853044e54ea3775b62ab49f7 100644 --- a/source/RobotAPI/interface/CMakeLists.txt +++ b/source/RobotAPI/interface/CMakeLists.txt @@ -108,6 +108,7 @@ set(SLICE_FILES aron.ice aron/Aron.ice + aron/test/AronConversionTestInterface.ice armem.ice diff --git a/source/RobotAPI/interface/aron/test/AronConversionTestInterface.ice b/source/RobotAPI/interface/aron/test/AronConversionTestInterface.ice new file mode 100644 index 0000000000000000000000000000000000000000..924f40c6d8664123c822eb9f7eb338d8d50b52d5 --- /dev/null +++ b/source/RobotAPI/interface/aron/test/AronConversionTestInterface.ice @@ -0,0 +1,61 @@ +/* + * 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 SpeechX::aron_cpp_to_python_conv_test + * author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) + * date 2023 + * copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + + +#pragma once + +#include <RobotAPI/interface/aron/Aron.ice> + + +module armarx { module aron { module test +{ + +module dto +{ + + struct TestAronConversionRequest + { + string aronClassName; + ::armarx::aron::data::dto::Dict probe; + }; + struct TestAronConversionResponse + { + bool success; + string errorMessage; + + ::armarx::aron::data::dto::Dict probe; + }; + +}; + + +module dti +{ + + interface AronConversionTestInterface + { + dto::TestAronConversionResponse testAronConversion(dto::TestAronConversionRequest req); + }; + +}; + +};};}; diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp index 4b61a29ecf41a7af19336ccdc81d30c7dc32189a..f5516353eecf459e4d2cde12db3737b396908972 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectFinder.cpp @@ -104,6 +104,7 @@ namespace armarx { return findObject(id.dataset(), id.className()); } + std::optional<ObjectInfo> ObjectFinder::findObject(const objpose::ObjectPose& obj) const { return findObject(obj.objectID); diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp index fea3677bd5aa3d0d2b762beb32e2c7ff8d6cc9ea..6d20564f660aee832698ca9d1eda297b56de9f3c 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.cpp @@ -59,12 +59,17 @@ namespace armarx return _id.str(); } - ObjectInfo::path ObjectInfo::objectDirectory() const + ObjectInfo::path ObjectInfo::objectDirectory(const bool fixDataPath) const { + if(fixDataPath) + { + return _relObjectsPath / _id.dataset() / _id.className(); + } + return path(_packageName) / _relObjectsPath / _id.dataset() / _id.className(); } - PackageFileLocation ObjectInfo::file(const std::string& _extension, const std::string& suffix) const + PackageFileLocation ObjectInfo::file(const std::string& _extension, const std::string& suffix, const bool fixDataPath) const { std::string extension = _extension; if (extension.at(0) != '.') @@ -75,8 +80,8 @@ namespace armarx PackageFileLocation loc; loc.package = _packageName; - loc.relativePath = objectDirectory() / filename; - loc.absolutePath = _absPackageDataDir / loc.relativePath; + loc.relativePath = objectDirectory(fixDataPath) / filename; + loc.absolutePath = _absPackageDataDir / objectDirectory(false) / filename; return loc; } @@ -86,14 +91,49 @@ namespace armarx return file(".xml"); } + PackageFileLocation ObjectInfo::urdf() const + { + return file(".urdf"); + } + + PackageFileLocation ObjectInfo::sdf() const + { + return file(".sdf"); + } + + std::optional<PackageFileLocation> ObjectInfo::getModel() const + { + if (fs::is_regular_file(simoxXML().absolutePath)) + { + return simoxXML(); + } + else if (fs::is_regular_file(urdf().absolutePath)) + { + return urdf(); + } + else if (fs::is_regular_file(sdf().absolutePath)) + { + return sdf(); + } + else + { + return std::nullopt; + } + } + PackageFileLocation ObjectInfo::articulatedSimoxXML() const { - return file(".xml", "_articulated"); + return file(".xml", "_articulated", true); } PackageFileLocation ObjectInfo::articulatedUrdf() const { - return file(".urdf", "_articulated"); + return file(".urdf", "_articulated", true); + } + + PackageFileLocation ObjectInfo::articulatedSdf() const + { + return file(".sdf", "_articulated"); } std::optional<PackageFileLocation> ObjectInfo::getArticulatedModel() const @@ -106,6 +146,10 @@ namespace armarx { return articulatedUrdf(); } + else if (fs::is_regular_file(articulatedSdf().absolutePath)) + { + return articulatedSdf(); + } else { return std::nullopt; @@ -293,5 +337,3 @@ std::ostream& armarx::operator<<(std::ostream& os, const ObjectInfo& rhs) { return os << rhs.id(); } - - diff --git a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h index 70fd46b47fb7a8a3c992dfe5d6c3d4676915aed7..ccbd4eb1be32116d8fc7ced05d8148f027fefccf 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h +++ b/source/RobotAPI/libraries/ArmarXObjects/ObjectInfo.h @@ -76,14 +76,19 @@ namespace armarx std::string idStr() const; - PackageFileLocation file(const std::string& extension, const std::string& suffix = "") const; + PackageFileLocation file(const std::string& extension, const std::string& suffix = "", bool fixDataPath = false) const; PackageFileLocation simoxXML() const; + PackageFileLocation urdf() const; + PackageFileLocation sdf() const; + /// Return the Simox XML, URDF or SDF, if one exists. + std::optional<PackageFileLocation> getModel() const; PackageFileLocation articulatedSimoxXML() const; PackageFileLocation articulatedUrdf() const; - /// Return the articulated Simox XML or URDF, if one exists. + PackageFileLocation articulatedSdf() const; + /// Return the articulated Simox XML, URDF or SDF, if one exists. std::optional<PackageFileLocation> getArticulatedModel() const; @@ -131,7 +136,7 @@ namespace armarx private: - path objectDirectory() const; + path objectDirectory(bool fixDataPath) const; std::optional<std::vector<std::string>> loadNames(const std::string& jsonKey) const; diff --git a/source/RobotAPI/libraries/ArmarXObjects/json_conversions.cpp b/source/RobotAPI/libraries/ArmarXObjects/json_conversions.cpp index cf42e169e67f0f18aef427c65fd24a73160983bf..446b625e4e91359fadd8dd43b4dacdc460a9d513 100644 --- a/source/RobotAPI/libraries/ArmarXObjects/json_conversions.cpp +++ b/source/RobotAPI/libraries/ArmarXObjects/json_conversions.cpp @@ -83,7 +83,7 @@ void armarx::objects::to_json(simox::json::json& j, const SceneObject& rhs) j["instanceName"] = rhs.instanceName; j["collection"] = rhs.collection; j["position"] = rhs.position; - j["orientation"] = rhs.orientation; + j["orientation"] = rhs.orientation.normalized(); if (rhs.isStatic.has_value()) { j["isStatic"] = rhs.isStatic.value(); @@ -106,6 +106,7 @@ void armarx::objects::from_json(const simox::json::json& j, SceneObject& rhs) j.at("collection").get_to(rhs.collection); j.at("position").get_to(rhs.position); j.at("orientation").get_to(rhs.orientation); + rhs.orientation.normalize(); if (j.count("isStatic")) { diff --git a/source/RobotAPI/libraries/armem/client/util/MemoryListener.h b/source/RobotAPI/libraries/armem/client/util/MemoryListener.h index ceb765e60db13404fbe170900342cff554b3affe..521d9ce6981e9503d90515b0fc10c518eedaae5a 100644 --- a/source/RobotAPI/libraries/armem/client/util/MemoryListener.h +++ b/source/RobotAPI/libraries/armem/client/util/MemoryListener.h @@ -63,12 +63,16 @@ namespace armarx::armem::client::util }; subscribe(subscriptionID, cb); } + template <class CalleeT> void subscribe(const MemoryID& subscriptionID, CalleeT* callee, MemberCallbackUpdatedOnly<CalleeT> callback) { auto cb = [callee, callback](const MemoryID&, const std::vector<MemoryID>& updatedSnapshotIDs) { - (callee->*callback)(updatedSnapshotIDs); + if(callee) + { + (callee->*callback)(updatedSnapshotIDs); + } }; subscribe(subscriptionID, cb); } diff --git a/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.cpp b/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.cpp index ec17f944c60747160a3306032fa89c13899d6b86..e7ceabba97898778af1c13d9ad9c805357f79ac2 100644 --- a/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.cpp +++ b/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.cpp @@ -6,11 +6,10 @@ #include <map> #include <optional> #include <ostream> +#include <type_traits> #include <utility> #include <vector> -#include <type_traits> - // ICE #include <IceUtil/Handle.h> #include <IceUtil/Time.h> @@ -34,15 +33,13 @@ #include <RobotAPI/libraries/aron/core/data/variant/complex/NDArray.h> // RobotAPI Armem -#include <RobotAPI/libraries/armem/core/error.h> -#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> -#include <RobotAPI/libraries/armem/util/util.h> - #include <RobotAPI/libraries/armem/client/Query.h> #include <RobotAPI/libraries/armem/client/Reader.h> #include <RobotAPI/libraries/armem/client/query/Builder.h> #include <RobotAPI/libraries/armem/client/query/selectors.h> - +#include <RobotAPI/libraries/armem/core/error.h> +#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h> +#include <RobotAPI/libraries/armem/util/util.h> #include <RobotAPI/libraries/armem_laser_scans/aron/LaserScan.aron.generated.h> #include <RobotAPI/libraries/armem_laser_scans/aron_conversions.h> #include <RobotAPI/libraries/armem_laser_scans/types.h> @@ -62,21 +59,22 @@ namespace armarx::armem::laser_scans::client Reader::registerPropertyDefinitions(armarx::PropertyDefinitionsPtr& def) { ARMARX_DEBUG << "TransformReader: registerPropertyDefinitions"; - registerPropertyDefinitions(def); const std::string prefix = propertyPrefix; - } - void Reader::connect() + void + Reader::connect() { // Wait for the memory to become available and add it as dependency. - ARMARX_IMPORTANT << "MappingDataReader: Waiting for memory '" - << constants::memoryName << "' ..."; + ARMARX_IMPORTANT << "MappingDataReader: Waiting for memory '" << constants::memoryName + << "' ..."; try { - memoryReader = memoryNameSystem.useReader(MemoryID().withMemoryName(constants::memoryName)); - ARMARX_IMPORTANT << "MappingDataReader: Connected to memory '" << constants::memoryName << "'"; + memoryReader = + memoryNameSystem.useReader(MemoryID().withMemoryName(constants::memoryName)); + ARMARX_IMPORTANT << "MappingDataReader: Connected to memory '" << constants::memoryName + << "'"; } catch (const armem::error::CouldNotResolveMemoryServer& e) { @@ -91,27 +89,43 @@ namespace armarx::armem::laser_scans::client armarx::armem::client::query::Builder qb; ARMARX_VERBOSE << "Query for agent: " << query.agent - << " memory name: " << constants::memoryName; + << " memory name: " << constants::memoryName; if (query.sensorList.empty()) // all sensors { // clang-format off - qb + auto& snapshots = qb .coreSegments().withName(constants::memoryName) .providerSegments().withName(query.agent) .entities().all() - .snapshots().timeRange(query.timeRange.min, query.timeRange.max); + .snapshots(); // clang-format on + if (query.timeRange.has_value()) + { + snapshots.timeRange(query.timeRange->min, query.timeRange->max); + } + else + { + snapshots.latest(); + } } else { // clang-format off - qb + auto& snapshots = qb .coreSegments().withName(constants::memoryName) .providerSegments().withName(query.agent) .entities().withNames(query.sensorList) - .snapshots().timeRange(query.timeRange.min, query.timeRange.max); + .snapshots(); // clang-format on + if (query.timeRange.has_value()) + { + snapshots.timeRange(query.timeRange->min, query.timeRange->max); + } + else + { + snapshots.latest(); + } } return qb; @@ -126,16 +140,14 @@ namespace armarx::armem::laser_scans::client ARMARX_WARNING << "No entities!"; } - const auto convert = - [](const auto& aronLaserScanStamped, - const wm::EntityInstance & ei) -> LaserScanStamped + const auto convert = [](const auto& aronLaserScanStamped, + const wm::EntityInstance& ei) -> LaserScanStamped { LaserScanStamped laserScanStamped; fromAron(aronLaserScanStamped, laserScanStamped); const auto ndArrayNavigator = - aron::data::NDArray::DynamicCast( - ei.data()->getElement("scan")); + aron::data::NDArray::DynamicCast(ei.data()->getElement("scan")); ARMARX_CHECK_NOT_NULL(ndArrayNavigator); @@ -145,47 +157,48 @@ namespace armarx::armem::laser_scans::client }; // loop over all entities and their snapshots - providerSegment.forEachEntity([&outV, &convert](const wm::Entity & entity) - { - // If we don't need this warning, we could directly iterate over the snapshots. - if (entity.empty()) + providerSegment.forEachEntity( + [&outV, &convert](const wm::Entity& entity) { - ARMARX_WARNING << "Empty history for " << entity.id(); - } - ARMARX_DEBUG << "History size: " << entity.size(); - - entity.forEachInstance([&outV, &convert](const wm::EntityInstance & entityInstance) - { - if (const auto o = tryCast<arondto::LaserScanStamped>(entityInstance)) + // If we don't need this warning, we could directly iterate over the snapshots. + if (entity.empty()) { - outV.push_back(convert(*o, entityInstance)); + ARMARX_WARNING << "Empty history for " << entity.id(); } + ARMARX_DEBUG << "History size: " << entity.size(); + + entity.forEachInstance( + [&outV, &convert](const wm::EntityInstance& entityInstance) + { + if (const auto o = tryCast<arondto::LaserScanStamped>(entityInstance)) + { + outV.push_back(convert(*o, entityInstance)); + } + return true; + }); return true; }); - return true; - }); return outV; } - Reader::Result Reader::queryData(const Query& query) const + Reader::Result + Reader::queryData(const Query& query) const { const auto qb = buildQuery(query); ARMARX_DEBUG << "[MappingDataReader] query ... "; - const armem::client::QueryResult qResult = - memoryReader.query(qb.buildQueryInput()); + const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput()); ARMARX_DEBUG << "[MappingDataReader] result: " << qResult; if (not qResult.success) { - ARMARX_WARNING << "Failed to query data from memory: " - << qResult.errorMessage; - return {.laserScans = {}, - .sensors = {}, - .status = Result::Status::Error, + ARMARX_WARNING << "Failed to query data from memory: " << qResult.errorMessage; + return {.laserScans = {}, + .sensors = {}, + .status = Result::Status::Error, .errorMessage = qResult.errorMessage}; } @@ -195,16 +208,17 @@ namespace armarx::armem::laser_scans::client const auto laserScans = asLaserScans(providerSegment); std::vector<std::string> sensors; - providerSegment.forEachEntity([&sensors](const wm::Entity & entity) - { - sensors.push_back(entity.name()); - return true; - }); + providerSegment.forEachEntity( + [&sensors](const wm::Entity& entity) + { + sensors.push_back(entity.name()); + return true; + }); - return {.laserScans = laserScans, - .sensors = sensors, - .status = Result::Status::Success, + return {.laserScans = laserScans, + .sensors = sensors, + .status = Result::Status::Success, .errorMessage = ""}; } -} // namespace armarx::armem::vision::laser_scans::client +} // namespace armarx::armem::laser_scans::client diff --git a/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.h b/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.h index 70703521336f8ac0b1fdf9649df4a3777d95f431..55beab204b12a7f72432ef2e38789f1856058934 100644 --- a/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.h +++ b/source/RobotAPI/libraries/armem_laser_scans/client/common/Reader.h @@ -72,7 +72,8 @@ namespace armarx::armem::laser_scans::client { std::string agent; - TimeRange timeRange; + // if not provided, only latest is queried + std::optional<TimeRange> timeRange; // if empty, all sensors will be queried std::vector<std::string> sensorList; diff --git a/source/RobotAPI/libraries/armem_laser_scans/server/Visu.cpp b/source/RobotAPI/libraries/armem_laser_scans/server/Visu.cpp index 2387e7319e2646857d9c821647f32d136ffdd969..551e4792c2eba3b5e19d2ebf468f2d090a53af41 100644 --- a/source/RobotAPI/libraries/armem_laser_scans/server/Visu.cpp +++ b/source/RobotAPI/libraries/armem_laser_scans/server/Visu.cpp @@ -301,8 +301,7 @@ namespace armarx::armem::server::laser_scans if (robots.count(name) == 0) { ARMARX_CHECK_NOT_NULL(virtualRobotReader); - const auto robot = virtualRobotReader->getRobot( - name, timestamp, VirtualRobot::RobotIO::RobotDescription::eStructure); + const auto robot = virtualRobotReader->getRobot(name); if (robot) { @@ -324,7 +323,7 @@ namespace armarx::armem::server::laser_scans } else { - ARMARX_VERBOSE << "Faield to synchronize robot `" << name << "`"; + ARMARX_INFO << deactivateSpam(10) << "Failed to synchronize robot `" << name << "`"; } } return entry.first; diff --git a/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml b/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml index a90821817037a2deb733474743f33221048ab24b..e2d695429d3c4193ad97df106821f5bb912a0028 100644 --- a/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml +++ b/source/RobotAPI/libraries/armem_objects/aron/ObjectClass.xml @@ -27,6 +27,22 @@ Core segment type of Object/Class. <armarx::arondto::PackagePath /> </ObjectChild> + <ObjectChild key="urdfPath"> + <armarx::arondto::PackagePath /> + </ObjectChild> + + <ObjectChild key="articulatedUrdfPath"> + <armarx::arondto::PackagePath /> + </ObjectChild> + + <ObjectChild key="sdfPath"> + <armarx::arondto::PackagePath /> + </ObjectChild> + + <ObjectChild key="articulatedSdfPath"> + <armarx::arondto::PackagePath /> + </ObjectChild> + <ObjectChild key="meshWrlPath"> <armarx::arondto::PackagePath /> </ObjectChild> diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp index c16ff257603b9fded0a5ea72737b92b40667fdb6..a2b0ddd6b2985997156cfa6c82998de249409e5a 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectReader.cpp @@ -47,7 +47,7 @@ namespace armarx::armem::articulated_object ARMARX_DEBUG << "Object " << name << " available"; auto obj = VirtualRobot::RobotIO::loadRobot( - ArmarXDataPath::resolvePath(it->xml.serialize().path), + it->xml.toSystemPath(), VirtualRobot::RobotIO::eStructure); if (not obj) diff --git a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp index 54049d8bbeb899b350dfab4906a125157ea527b9..0b466d404fbc41d5eee48819418dd5b5cae35d1f 100644 --- a/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp +++ b/source/RobotAPI/libraries/armem_objects/client/articulated_object/ArticulatedObjectWriter.cpp @@ -3,44 +3,53 @@ #include <Eigen/Core> #include <Eigen/Geometry> +#include <VirtualRobot/Robot.h> + #include <ArmarXCore/core/exceptions/local/ExpressionException.h> #include <ArmarXCore/core/logging/Logging.h> #include <ArmarXCore/core/system/ArmarXDataPath.h> - -#include <VirtualRobot/Robot.h> +#include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> namespace armarx::armem::articulated_object { - armem::articulated_object::ArticulatedObject convert( - const VirtualRobot::Robot& obj, - const armem::Time& timestamp) + armem::articulated_object::ArticulatedObject + convert(const VirtualRobot::Robot& obj, const armem::Time& timestamp) { ARMARX_DEBUG << "Filename is " << obj.getFilename(); - // TODO(fabian.reister): remove "PriorKnowledgeData" below + const std::vector<std::string> packages = + armarx::CMakePackageFinder::FindAllArmarXSourcePackages(); + const std::string package = armarx::ArmarXDataPath::getProject(packages, obj.getFilename()); - return armem::articulated_object::ArticulatedObject + // make sure that the relative path is without the 'package/' prefix + const std::string fileRelPath = [&obj, &package]() -> std::string { - .description = { - .name = obj.getType(), - .xml = PackagePath(armarx::ArmarXDataPath::getProject( - {"PriorKnowledgeData"}, obj.getFilename()), - obj.getFilename()) - }, - .instance = obj.getName(), - .config = { - .timestamp = timestamp, - .globalPose = Eigen::Affine3f(obj.getRootNode()->getGlobalPose()), - .jointMap = obj.getJointValues() - }, - .timestamp = timestamp}; + if (simox::alg::starts_with(obj.getFilename(), package)) + { + // remove "package" + "/" + const std::string fixedFilename = obj.getFilename().substr(package.size() + 1, -1); + return fixedFilename; + } + + return obj.getFilename(); + }(); + + return armem::articulated_object::ArticulatedObject{ + .description = {.name = obj.getType(), + .xml = PackagePath(armarx::ArmarXDataPath::getProject( + {package}, fileRelPath), + obj.getFilename())}, + .instance = obj.getName(), + .config = {.timestamp = timestamp, + .globalPose = Eigen::Affine3f(obj.getRootNode()->getGlobalPose()), + .jointMap = obj.getJointValues()}, + .timestamp = timestamp}; } bool - ArticulatedObjectWriter::storeArticulatedObject( - const VirtualRobot::RobotPtr& articulatedObject, - const armem::Time& timestamp) + ArticulatedObjectWriter::storeArticulatedObject(const VirtualRobot::RobotPtr& articulatedObject, + const armem::Time& timestamp) { ARMARX_CHECK_NOT_NULL(articulatedObject); diff --git a/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp index a95e98d011e52c7f6251a2119b66b971b9d5a045..4dcf3262724cd6a9626a967f9582d4514a478d89 100644 --- a/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/class/Segment.cpp @@ -191,7 +191,11 @@ namespace armarx::armem::server::obj::clazz } }; setPathIfExists(data.simoxXmlPath, info.simoxXML()); + setPathIfExists(data.urdfPath, info.urdf()); + setPathIfExists(data.sdfPath, info.sdf()); setPathIfExists(data.articulatedSimoxXmlPath, info.articulatedSimoxXML()); + setPathIfExists(data.articulatedUrdfPath, info.articulatedUrdf()); + setPathIfExists(data.articulatedSdfPath, info.articulatedSdf()); setPathIfExists(data.meshObjPath, info.wavefrontObj()); setPathIfExists(data.meshWrlPath, info.meshWrl()); diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp index 887674e048a41f2a93996a0d6fbdb91e90b7f901..c17ce69020b2b7f2c363219339754919fc805723 100644 --- a/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/instance/Segment.cpp @@ -222,12 +222,21 @@ namespace armarx::armem::server::obj::instance stats.numUpdated++; VirtualRobot::RobotPtr robot = robots.get(provided.robotName, provided.providerName); + + if(not robot) + { + ARMARX_INFO << deactivateSpam(1) << "Failed to retrieve robot `" << provided.robotName << "`."; + } + // robot may be null! // Update the robot to obtain correct local -> global transformation if (robot and robotSyncTimestamp != timestamp) { - ARMARX_CHECK(robots.reader->synchronizeRobot(*robot, timestamp)); + ARMARX_CHECK(robots.reader->synchronizeRobot(*robot, timestamp)) + << "Failed to synchronize robot to timestamp " << timestamp + << ". This is " << (Clock::Now() - timestamp).toSecondsDouble() << " seconds in the past."; + robotSyncTimestamp = timestamp; @@ -1302,14 +1311,13 @@ namespace armarx::armem::server::obj::instance { // Try to fetch the robot. ARMARX_CHECK_NOT_NULL(reader); - bool warnings = false; VirtualRobot::RobotPtr robot = reader->getRobot( robotName, Clock::Now(), - VirtualRobot::RobotIO::RobotDescription::eStructure, warnings); + VirtualRobot::RobotIO::RobotDescription::eStructure); if (robot) { - reader->synchronizeRobot(*robot, Clock::Now()); + ARMARX_CHECK(reader->synchronizeRobot(*robot, Clock::Now())); // Store robot if valid. loaded.emplace(robotName, robot); } diff --git a/source/RobotAPI/libraries/armem_objects/server/instance/Visu.cpp b/source/RobotAPI/libraries/armem_objects/server/instance/Visu.cpp index 3a13f6722e5cd2c0f824c630a83367b47af1de72..514096731b83167b902dfe4198deae82725837ed 100644 --- a/source/RobotAPI/libraries/armem_objects/server/instance/Visu.cpp +++ b/source/RobotAPI/libraries/armem_objects/server/instance/Visu.cpp @@ -194,7 +194,7 @@ namespace armarx::armem::server::obj::instance { viz::Robot robot(key); robot.pose(pose); - robot.file(model->package, model->relativePath); + robot.file(model->package, model->package + "/" + model->relativePath); robot.joints(objectPose.objectJointValues); layer.add(robot); diff --git a/source/RobotAPI/libraries/armem_robot/types.h b/source/RobotAPI/libraries/armem_robot/types.h index 345564d4b859af306acecdc82d00096c4a559351..2bc45c144b53e3900e8c7041c54d33ee0d205f56 100644 --- a/source/RobotAPI/libraries/armem_robot/types.h +++ b/source/RobotAPI/libraries/armem_robot/types.h @@ -40,6 +40,15 @@ namespace armarx::armem::robot Eigen::Vector3f torque; }; + using ToFArray = Eigen::MatrixXf; + + struct ToF + { + ToFArray array; + std::string frame; + }; + + struct RobotState { using JointMap = std::map<std::string, float>; diff --git a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt index a9c0ceb1b8cd552915819a052a2ffd73bbfdec29..8dda90015cd030197cd4af43fd2b9d534968fc19 100644 --- a/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem_robot_state/CMakeLists.txt @@ -65,6 +65,7 @@ armarx_enable_aron_file_generation_for_target( ARON_FILES aron/JointState.xml aron/Proprioception.xml + aron/Exteroception.xml aron/TransformHeader.xml aron/Transform.xml ) diff --git a/source/RobotAPI/libraries/armem_robot_state/aron/Exteroception.xml b/source/RobotAPI/libraries/armem_robot_state/aron/Exteroception.xml new file mode 100644 index 0000000000000000000000000000000000000000..80f72e06059a39e60e6709a5e7acb28c63ee3395 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/aron/Exteroception.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<AronTypeDefinition> + <CodeIncludes> + <SystemInclude include="Eigen/Core" /> + <SystemInclude include="Eigen/Geometry" /> + </CodeIncludes> + + <GenerateTypes> + + <Object name="armarx::armem::exteroception::arondto::ToF"> + + <ObjectChild key="array"> + <Matrix rows="-1" cols="-1" type="float32" /> + </ObjectChild> + + </Object> + + + <Object name="armarx::armem::arondto::Exteroception"> + + <ObjectChild key="iterationID"> + <long /> + </ObjectChild> + + <ObjectChild key="tof"> + <Dict> + <armarx::armem::exteroception::arondto::ToF/> + </Dict> + </ObjectChild> + + </Object> + + + </GenerateTypes> +</AronTypeDefinition> diff --git a/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml b/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml index c7ac7670d399329bb6674dad3243a048153534f8..d5472cbde89434e346f116e6b6f461bf4f2ca34e 100644 --- a/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml +++ b/source/RobotAPI/libraries/armem_robot_state/aron/Proprioception.xml @@ -234,8 +234,6 @@ </ObjectChild> </Object> - - <Object name="armarx::armem::arondto::Proprioception"> @@ -257,7 +255,6 @@ </Dict> </ObjectChild> - <ObjectChild key="extraFloats"> <Dict> <Float /> diff --git a/source/RobotAPI/libraries/armem_robot_state/aron_conversions.cpp b/source/RobotAPI/libraries/armem_robot_state/aron_conversions.cpp index 1c18fe5b78799b1b2f9555a33a8997e7edb60d28..3749c892d5e46e6ce0f7a848a86e8059ed70991c 100644 --- a/source/RobotAPI/libraries/armem_robot_state/aron_conversions.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/aron_conversions.cpp @@ -6,6 +6,7 @@ #include <RobotAPI/libraries/armem_robot_state/aron/JointState.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> +#include <RobotAPI/libraries/armem_robot_state/aron/Exteroception.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron/Transform.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/types.h> #include <RobotAPI/libraries/aron/common/aron_conversions.h> @@ -97,4 +98,14 @@ namespace armarx::armem aron::toAron(dto.torque, bo.torque); } + + void fromAron(const armarx::armem::exteroception::arondto::ToF& dto, robot::ToFArray& bo){ + bo = dto.array; + } + + void toAron(armarx::armem::exteroception::arondto::ToF& dto, const robot::ToFArray& bo){ + dto.array = bo; + } + + } // namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem_robot_state/aron_conversions.h b/source/RobotAPI/libraries/armem_robot_state/aron_conversions.h index 75c339c7fc4aad7f24b5b52befdf2e7f1451b345..f78efc200ee18205523ddf7ea5c60e8c6949bf9f 100644 --- a/source/RobotAPI/libraries/armem_robot_state/aron_conversions.h +++ b/source/RobotAPI/libraries/armem_robot_state/aron_conversions.h @@ -40,6 +40,11 @@ namespace armarx::armem struct ForceTorque; } + namespace exteroception::arondto + { + struct ToF; + } + namespace arondto { struct Transform; @@ -65,5 +70,8 @@ namespace armarx::armem void fromAron(const armarx::armem::prop::arondto::ForceTorque& dto, robot::ForceTorque& bo); void toAron(armarx::armem::prop::arondto::ForceTorque& dto, const robot::ForceTorque& bo); + void fromAron(const armarx::armem::exteroception::arondto::ToF& dto, robot::ToFArray& bo); + void toAron(armarx::armem::exteroception::arondto::ToF& dto, const robot::ToFArray& bo); + } // namespace armarx::armem diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp index 611fa0e0d3f5bb931f5d64a60c70735d698c623f..eaa2e9ca07b063eaba3a71887eda2d14fa839c16 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.cpp @@ -22,6 +22,7 @@ #include <RobotAPI/libraries/armem_robot/aron_conversions.h> #include <RobotAPI/libraries/armem_robot/robot_conversions.h> #include <RobotAPI/libraries/armem_robot/types.h> +#include <RobotAPI/libraries/armem_robot_state/aron/Exteroception.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron/JointState.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> #include <RobotAPI/libraries/armem_robot_state/aron_conversions.h> @@ -121,7 +122,7 @@ namespace armarx::armem::robot_state const auto elapsedTime = armem::Time::Now() - tsStartFunctionInvokation; if (elapsedTime > syncTimeout) { - ARMARX_WARNING << "Could not synchronize object " << obj.description.name; + ARMARX_VERBOSE << "Could not synchronize object " << obj.description.name; return false; } @@ -135,11 +136,7 @@ namespace armarx::armem::robot_state } std::optional<robot::RobotDescription> - RobotReader::queryDescription( - const std::string& name, - const armem::Time& timestamp, - bool warnings - ) + RobotReader::queryDescription(const std::string& name, const armem::Time& timestamp) { const auto sanitizedTimestamp = timestamp.isValid() ? timestamp : Clock::Now(); @@ -159,11 +156,8 @@ namespace armarx::armem::robot_state if (not memoryReader) { - if (warnings) - { - ARMARX_WARNING << "Memory reader is null. Did you forget to call " - "RobotReader::connect() in onConnectComponent()?"; - } + ARMARX_WARNING << "Memory reader is null. Did you forget to call " + "RobotReader::connect() in onConnectComponent()?"; return std::nullopt; } @@ -182,10 +176,7 @@ namespace armarx::armem::robot_state } catch (...) { - if (warnings) - { - ARMARX_WARNING << "query description failure" << GetHandledExceptionString(); - } + ARMARX_VERBOSE << "Query description failure" << GetHandledExceptionString(); } return std::nullopt; @@ -198,14 +189,14 @@ namespace armarx::armem::robot_state const auto jointMap = queryJointState(description, timestamp); if (not jointMap) { - ARMARX_WARNING << "Failed to query joint state for robot '" << description.name << "'."; + ARMARX_VERBOSE << "Failed to query joint state for robot '" << description.name << "'."; return std::nullopt; } const auto globalPose = queryGlobalPose(description, timestamp); if (not globalPose) { - ARMARX_WARNING << "Failed to query global pose for robot " << description.name; + ARMARX_VERBOSE << "Failed to query global pose for robot " << description.name; return std::nullopt; } @@ -235,7 +226,7 @@ namespace armarx::armem::robot_state } catch (...) { - ARMARX_WARNING << GetHandledExceptionString(); + ARMARX_VERBOSE << GetHandledExceptionString(); return std::nullopt; } } @@ -268,7 +259,7 @@ namespace armarx::armem::robot_state if (not qResult.success) /* c++20 [[unlikely]] */ { - ARMARX_WARNING << qResult.errorMessage; + ARMARX_VERBOSE << qResult.errorMessage; return std::nullopt; } @@ -276,7 +267,7 @@ namespace armarx::armem::robot_state } catch (...) { - ARMARX_WARNING << deactivateSpam(1) << "Failed to query joint state. Reason: " + ARMARX_VERBOSE << deactivateSpam(1) << "Failed to query joint state. Reason: " << GetHandledExceptionString(); return std::nullopt; } @@ -306,7 +297,7 @@ namespace armarx::armem::robot_state if (not qResult.success) /* c++20 [[unlikely]] */ { - ARMARX_WARNING << qResult.errorMessage; + ARMARX_VERBOSE << qResult.errorMessage; return {}; } @@ -339,7 +330,7 @@ namespace armarx::armem::robot_state if (not qResult.success) /* c++20 [[unlikely]] */ { - ARMARX_WARNING << qResult.errorMessage; + ARMARX_VERBOSE << qResult.errorMessage; return std::nullopt; } @@ -352,7 +343,8 @@ namespace armarx::armem::robot_state { try { - const auto result = transformReader.getGlobalPose(description.name, constants::robotRootNodeName, timestamp); + const auto result = transformReader.getGlobalPose( + description.name, constants::robotRootNodeName, timestamp); if (not result) { return std::nullopt; @@ -362,7 +354,7 @@ namespace armarx::armem::robot_state } catch (...) { - ARMARX_WARNING << deactivateSpam(1) << "Failed to query global pose. Reason: " + ARMARX_VERBOSE << deactivateSpam(1) << "Failed to query global pose. Reason: " << GetHandledExceptionString(); return std::nullopt; } @@ -381,7 +373,7 @@ namespace armarx::armem::robot_state if (providerSegment.empty()) { - ARMARX_WARNING << "No entity found"; + ARMARX_VERBOSE << "No entity found"; return std::nullopt; } @@ -394,9 +386,10 @@ namespace armarx::armem::robot_state instance = &i; return false; // break }); - if (!instance) + + if (instance == nullptr) { - ARMARX_WARNING << "No entity snapshots found"; + ARMARX_VERBOSE << "No entity snapshots found"; return std::nullopt; } @@ -436,6 +429,11 @@ namespace armarx::armem::robot_state coreSegment.forEachEntity( [&jointMap](const wm::Entity& entity) { + if (not entity.getLatestSnapshot().hasInstance(0)) + { + return; + } + const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); const auto proprioception = @@ -525,7 +523,7 @@ namespace armarx::armem::robot_state if (not qResult.success) /* c++20 [[unlikely]] */ { - ARMARX_WARNING << qResult.errorMessage; + ARMARX_VERBOSE << qResult.errorMessage; return std::nullopt; } @@ -558,7 +556,7 @@ namespace armarx::armem::robot_state if (not qResult.success) /* c++20 [[unlikely]] */ { - ARMARX_WARNING << qResult.errorMessage; + ARMARX_VERBOSE << qResult.errorMessage; return std::nullopt; } @@ -566,6 +564,37 @@ namespace armarx::armem::robot_state } + std::optional<std::map<RobotReader::Hand, robot::ToFArray>> + RobotReader::queryToF(const robot::RobotDescription& description, + const armem::Time& timestamp) const + { + // Query all entities from provider. + armem::client::query::Builder qb; + + ARMARX_DEBUG << "Querying ToF data for robot: " << description; + + // clang-format off + qb + .coreSegments().withName(constants::exteroceptionCoreSegment) + .providerSegments().withName(description.name) // agent + .entities().all() // TODO + .snapshots().beforeOrAtTime(timestamp); + // clang-format on + + const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput()); + + ARMARX_DEBUG << "Lookup result in reader: " << qResult; + + if (not qResult.success) /* c++20 [[unlikely]] */ + { + ARMARX_VERBOSE << qResult.errorMessage; + return std::nullopt; + } + + return getToF(qResult.memory, description.name); + } + + std::optional<robot::PlatformState> RobotReader::getRobotPlatformState(const armarx::armem::wm::Memory& memory, const std::string& name) const @@ -580,6 +609,11 @@ namespace armarx::armem::robot_state coreSegment.forEachEntity( [&platformState](const wm::Entity& entity) { + if (not entity.getLatestSnapshot().hasInstance(0)) + { + return; + } + const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); const auto proprioception = @@ -623,6 +657,11 @@ namespace armarx::armem::robot_state coreSegment.forEachEntity( [&forceTorques](const wm::Entity& entity) { + if (not entity.getLatestSnapshot().hasInstance(0)) + { + return; + } + const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); const auto proprioception = @@ -675,6 +714,47 @@ namespace armarx::armem::robot_state return forceTorques; } + std::map<RobotReader::Hand, robot::ToFArray> + RobotReader::getToF(const armarx::armem::wm::Memory& memory, const std::string& name) const + { + std::map<RobotReader::Hand, robot::ToFArray> tofs; + + // clang-format off + const armem::wm::CoreSegment& coreSegment = memory + .getCoreSegment(constants::exteroceptionCoreSegment); + // clang-format on + + coreSegment.forEachEntity( + [&tofs](const wm::Entity& entity) + { + ARMARX_DEBUG << "Processing ToF element"; + + if (not entity.getLatestSnapshot().hasInstance(0)) + { + return; + } + const auto& entityInstance = entity.getLatestSnapshot().getInstance(0); + + const auto exteroception = + tryCast<::armarx::armem::arondto::Exteroception>(entityInstance); + ARMARX_CHECK(exteroception.has_value()); + + + for (const auto& [handName, dtoFt] : exteroception->tof) + { + ARMARX_DEBUG << "Processing ToF element for hand `" << handName << "`"; + + robot::ToFArray tof; + fromAron(dtoFt, tof); + + const auto hand = fromHandName(handName); + tofs.emplace(hand, tof); + } + }); + + return tofs; + } + std::optional<robot::RobotDescription> RobotReader::getRobotDescription(const armarx::armem::wm::Memory& memory, @@ -691,11 +771,77 @@ namespace armarx::armem::robot_state { instance = &i; }); if (instance == nullptr) { - ARMARX_WARNING << "No entity snapshots found in provider segment `" << name << "`"; + ARMARX_VERBOSE << "No entity snapshots found in provider segment `" << name << "`"; return std::nullopt; } return robot::convertRobotDescription(*instance); } + std::vector<robot::RobotDescription> + RobotReader::getRobotDescriptions(const armarx::armem::wm::Memory& memory) const + { + const armem::wm::CoreSegment& coreSegment = + memory.getCoreSegment(constants::descriptionCoreSegment); + + std::vector<robot::RobotDescription> descriptions; + + coreSegment.forEachInstance( + [&descriptions](const wm::EntityInstance& instance) + { + if (const std::optional<robot::RobotDescription> desc = + robot::convertRobotDescription(instance)) + { + descriptions.push_back(desc.value()); + } + }); + + return descriptions; + } + + std::vector<robot::RobotDescription> + RobotReader::queryDescriptions(const armem::Time& timestamp) + { + const auto sanitizedTimestamp = timestamp.isValid() ? timestamp : Clock::Now(); + + // Query all entities from provider. + armem::client::query::Builder qb; + + // clang-format off + qb + .coreSegments().withName(constants::descriptionCoreSegment) + .providerSegments().all() + .entities().all() + .snapshots().beforeOrAtTime(sanitizedTimestamp); + // clang-format on + + ARMARX_DEBUG << "Lookup query in reader"; + + if (not memoryReader) + { + ARMARX_WARNING << "Memory reader is null. Did you forget to call " + "RobotReader::connect() in onConnectComponent()?"; + return {}; + } + + try + { + const armem::client::QueryResult qResult = memoryReader.query(qb.buildQueryInput()); + + ARMARX_DEBUG << "Lookup result in reader: " << qResult; + + if (not qResult.success) /* c++20 [[unlikely]] */ + { + return {}; + } + + return getRobotDescriptions(qResult.memory); + } + catch (...) + { + ARMARX_VERBOSE << "Query description failure" << GetHandledExceptionString(); + } + + return {}; + } } // namespace armarx::armem::robot_state diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h index bb63c714180325961c434d61e4a8e31b78a84045..a9cddc6b2afd5258271f5718b4f1c82d0dd6a995 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/RobotReader.h @@ -23,6 +23,7 @@ #include <mutex> #include <optional> +#include <vector> #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h> #include <RobotAPI/libraries/armem/client/Reader.h> @@ -50,7 +51,7 @@ namespace armarx::armem::robot_state void registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def); - bool synchronize(robot::Robot& obj, const armem::Time& timestamp) override; + [[nodiscard]] bool synchronize(robot::Robot& obj, const armem::Time& timestamp) override; std::optional<robot::Robot> get(const std::string& name, const armem::Time& timestamp) override; @@ -58,8 +59,9 @@ namespace armarx::armem::robot_state const armem::Time& timestamp) override; std::optional<robot::RobotDescription> queryDescription(const std::string& name, - const armem::Time& timestamp, - bool warnings = true); + const armem::Time& timestamp); + + std::vector<robot::RobotDescription> queryDescriptions(const armem::Time& timestamp); std::optional<robot::RobotState> queryState(const robot::RobotDescription& description, const armem::Time& timestamp); @@ -100,6 +102,10 @@ namespace armarx::armem::robot_state const armem::Time& start, const armem::Time& end) const; + + std::optional<std::map<Hand, robot::ToFArray>> + queryToF(const robot::RobotDescription& description, const armem::Time& timestamp) const; + /** * @brief retrieve the robot's pose in the odometry frame. * @@ -125,6 +131,9 @@ namespace armarx::armem::robot_state std::optional<robot::RobotDescription> getRobotDescription(const armarx::armem::wm::Memory& memory, const std::string& name) const; + std::vector<robot::RobotDescription> + getRobotDescriptions(const armarx::armem::wm::Memory& memory) const; + std::optional<robot::RobotState::JointMap> getRobotJointState(const armarx::armem::wm::Memory& memory, const std::string& name) const; @@ -142,6 +151,9 @@ namespace armarx::armem::robot_state std::map<RobotReader::Hand, std::map<armem::Time, robot::ForceTorque>> getForceTorques(const armarx::armem::wm::Memory& memory, const std::string& name) const; + std::map<RobotReader::Hand, robot::ToFArray> getToF(const armarx::armem::wm::Memory& memory, + const std::string& name) const; + struct Properties { } properties; diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp index 8c1f8dfb0ad4f6f833d27191588fc8d766998235..225e048864e69a7c349b9e53163d41ed1e0eef6b 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.cpp @@ -38,16 +38,16 @@ namespace armarx::armem::robot_state bool VirtualRobotReader::synchronizeRobot(VirtualRobot::Robot& robot, const armem::Time& timestamp) { - const static auto packages = armarx::CMakePackageFinder::FindAllArmarXSourcePackages(); - const auto package = armarx::ArmarXDataPath::getProject(packages, robot.getFilename()); + // const static auto packages = armarx::CMakePackageFinder::FindAllArmarXSourcePackages(); + // const auto package = armarx::ArmarXDataPath::getProject(packages, robot.getFilename()); const robot::RobotDescription robotDescription{ - .name = robot.getName(), .xml = PackagePath{package, robot.getFilename()}}; + .name = robot.getName(), .xml = PackagePath{"", ""}}; const auto robotState = queryState(robotDescription, timestamp); if (not robotState) { - ARMARX_WARNING << "Querying robot state failed for robot `" << robot.getName() << "` " + ARMARX_VERBOSE << "Querying robot state failed for robot `" << robot.getName() << "` " << "(type `"<< robot.getType() << "`)!"; return false; } @@ -61,19 +61,16 @@ namespace armarx::armem::robot_state VirtualRobot::RobotPtr VirtualRobotReader::getRobot(const std::string& name, const armem::Time& timestamp, - const VirtualRobot::RobotIO::RobotDescription& loadMode, - bool warnings) + const VirtualRobot::RobotIO::RobotDescription& loadMode) { ARMARX_INFO << deactivateSpam(60) << "Querying robot description for robot '" << name << "'"; - const auto description = queryDescription(name, timestamp, warnings); + const auto description = queryDescription(name, timestamp); if (not description) { - if (warnings) - { - ARMARX_WARNING << "The description of robot `" << name << "` is not a available!"; - } + ARMARX_VERBOSE << "The description of robot `" << name << "` is not a available!"; + return nullptr; } @@ -134,7 +131,7 @@ namespace armarx::armem::robot_state Clock::WaitFor(sleepAfterFailure); } - ARMARX_WARNING << "Failed to get synchronized robot `" << name << "`"; + ARMARX_VERBOSE << "Failed to get synchronized robot `" << name << "`"; return nullptr; } diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h index d19e0483fdfa04f00579ba85b1e830f58feee241..ab30cbc1317d98f802dc24cbe5d844da02b038e9 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/VirtualRobotReader.h @@ -27,7 +27,6 @@ #include "RobotReader.h" - namespace armarx::armem::robot_state { /** @@ -47,14 +46,14 @@ namespace armarx::armem::robot_state void connect(); void registerPropertyDefinitions(::armarx::PropertyDefinitionsPtr& def); - bool synchronizeRobot(VirtualRobot::Robot& robot, const armem::Time& timestamp); + [[nodiscard]] bool synchronizeRobot(VirtualRobot::Robot& robot, + const armem::Time& timestamp); [[nodiscard]] VirtualRobot::RobotPtr getRobot(const std::string& name, const armem::Time& timestamp = armem::Time::Invalid(), const VirtualRobot::RobotIO::RobotDescription& loadMode = - VirtualRobot::RobotIO::RobotDescription::eStructure, - bool warnings = true); + VirtualRobot::RobotIO::RobotDescription::eStructure); [[nodiscard]] VirtualRobot::RobotPtr getSynchronizedRobot(const std::string& name, @@ -71,14 +70,12 @@ namespace armarx::armem::robot_state private: - [[nodiscard]] VirtualRobot::RobotPtr _getSynchronizedRobot(const std::string& name, const armem::Time& timestamp = armem::Time::Invalid(), const VirtualRobot::RobotIO::RobotDescription& loadMode = - VirtualRobot::RobotIO::RobotDescription::eStructure, + VirtualRobot::RobotIO::RobotDescription::eStructure, bool blocking = true); - }; } // namespace armarx::armem::robot_state diff --git a/source/RobotAPI/libraries/armem_robot_state/client/common/constants.h b/source/RobotAPI/libraries/armem_robot_state/client/common/constants.h index d9efcbcf795b46ddb4e9647919d38c0c617c1234..5d093c195addda7c64c378d919d38f09987cdfc9 100644 --- a/source/RobotAPI/libraries/armem_robot_state/client/common/constants.h +++ b/source/RobotAPI/libraries/armem_robot_state/client/common/constants.h @@ -28,6 +28,7 @@ namespace armarx::armem::robot_state::constants inline const std::string descriptionCoreSegment = "Description"; inline const std::string localizationCoreSegment = "Localization"; inline const std::string proprioceptionCoreSegment = "Proprioception"; + inline const std::string exteroceptionCoreSegment = "Exteroception"; inline const std::string descriptionEntityName = "description"; diff --git a/source/RobotAPI/libraries/armem_robot_state/memory_ids.cpp b/source/RobotAPI/libraries/armem_robot_state/memory_ids.cpp index 926c2e843e252efdc9c4711bbe2b03a3a7394bba..2f219eff39376ed3ecc44e47b0780fd1328785a0 100644 --- a/source/RobotAPI/libraries/armem_robot_state/memory_ids.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/memory_ids.cpp @@ -30,6 +30,7 @@ namespace armarx::armem const MemoryID robot_state::descriptionSegmentID { memoryID.withCoreSegmentName("Description") }; const MemoryID robot_state::proprioceptionSegmentID { memoryID.withCoreSegmentName("Proprioception") }; + const MemoryID robot_state::exteroceptionSegmentID { memoryID.withCoreSegmentName("Exteroception") }; const MemoryID robot_state::localizationSegmentID { memoryID.withCoreSegmentName("Localization") }; diff --git a/source/RobotAPI/libraries/armem_robot_state/memory_ids.h b/source/RobotAPI/libraries/armem_robot_state/memory_ids.h index 1d8008d4db8d7a71da0a8ca47420040060382ca6..f653236b1f62ec8aef905d45a7ee86af079b05b9 100644 --- a/source/RobotAPI/libraries/armem_robot_state/memory_ids.h +++ b/source/RobotAPI/libraries/armem_robot_state/memory_ids.h @@ -32,6 +32,7 @@ namespace armarx::armem::robot_state extern const MemoryID descriptionSegmentID; extern const MemoryID proprioceptionSegmentID; + extern const MemoryID exteroceptionSegmentID; extern const MemoryID localizationSegmentID; } // namespace armarx::armem::objects diff --git a/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt b/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt index 169d59f1a86d6a1bea1b9e54033409d23fb3cd7f..3ad1d50684d89c246e6123a6ef5d2055779eab6f 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem_robot_state/server/CMakeLists.txt @@ -43,10 +43,16 @@ armarx_add_library( proprioception/RobotUnitData.h proprioception/RobotUnitReader.h + proprioception/converters/ConverterInterface.h proprioception/converters/Armar6Converter.h proprioception/converters/ConverterTools.h proprioception/converters/ConverterRegistry.h - proprioception/converters/ConverterInterface.h + + exteroception/converters/ConverterInterface.h + exteroception/converters/ConverterTools.h + exteroception/converters/ConverterRegistry.h + exteroception/converters/ArmarDEConverter.h + exteroception/Segment.h description/Segment.h @@ -67,6 +73,12 @@ armarx_add_library( proprioception/converters/ConverterRegistry.cpp proprioception/converters/ConverterInterface.cpp + exteroception/converters/ConverterInterface.cpp + exteroception/converters/ConverterTools.cpp + exteroception/converters/ConverterRegistry.cpp + exteroception/converters/ArmarDEConverter.cpp + exteroception/Segment.cpp + description/Segment.cpp ) diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.cpp b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63f24d0f46a156adc023b3b64b4f27d344b151c6 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.cpp @@ -0,0 +1,50 @@ +#include "Segment.h" + +#include <filesystem> + +#include <SimoxUtility/algorithm/string/string_tools.h> + +#include <ArmarXCore/core/application/properties/PluginAll.h> +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/system/ArmarXDataPath.h> +#include <ArmarXCore/core/system/cmake/CMakePackageFinder.h> +#include <ArmarXCore/core/time/TimeUtil.h> + +#include <RobotAPI/libraries/armem/client/Writer.h> +#include <RobotAPI/libraries/armem/client/query/Builder.h> +#include <RobotAPI/libraries/armem/client/query/query_fns.h> +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/aron_conversions.h> +#include <RobotAPI/libraries/armem/server/MemoryToIceAdapter.h> +#include <RobotAPI/libraries/armem_robot/aron/Robot.aron.generated.h> +#include <RobotAPI/libraries/armem_robot/aron_conversions.h> +#include <RobotAPI/libraries/armem_robot/robot_conversions.h> +#include <RobotAPI/libraries/armem_robot/types.h> +#include <RobotAPI/libraries/armem_robot_state/aron/Exteroception.aron.generated.h> +#include <RobotAPI/libraries/armem_robot_state/memory_ids.h> +#include <RobotAPI/libraries/aron/common/aron_conversions.h> + + +namespace armarx::armem::server::robot_state::exteroception +{ + + Segment::Segment(armem::server::MemoryToIceAdapter& memoryToIceAdapter) : + Base(memoryToIceAdapter, + armem::robot_state::exteroceptionSegmentID.coreSegmentName, + arondto::Exteroception::ToAronType()) + { + } + + Segment::~Segment() = default; + + + // void + // Segment::onConnect(const RobotUnitInterfacePrx& robotUnitPrx) + // { + // robotUnit = robotUnitPrx; + + // // store the robot description linked to the robot unit in this segment + // } + + +} // namespace armarx::armem::server::robot_state::exteroception diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.h b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.h new file mode 100644 index 0000000000000000000000000000000000000000..e8471563d640800a44d4516d6eccdd7d7e355ccd --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/Segment.h @@ -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/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2023 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <mutex> +#include <optional> +#include <string> +#include <unordered_map> + +#include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> +#include <RobotAPI/libraries/armem/server/segment/SpecializedCoreSegment.h> +#include <RobotAPI/libraries/armem/server/segment/SpecializedProviderSegment.h> +#include <RobotAPI/libraries/armem_robot/aron/RobotDescription.aron.generated.h> +#include <RobotAPI/libraries/armem_robot_state/server/forward_declarations.h> + + +namespace armarx::armem::server::robot_state::exteroception +{ + + class Segment : public segment::SpecializedCoreSegment + { + using Base = segment::SpecializedCoreSegment; + + public: + Segment(server::MemoryToIceAdapter& iceMemory); + ~Segment() override; + + + // void onConnect(const RobotUnitInterfacePrx& robotUnitPrx); + + + private: + // RobotUnitInterfacePrx robotUnit; + }; + +} // namespace armarx::armem::server::robot_state::exteroception diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ArmarDEConverter.cpp b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ArmarDEConverter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d71bb045b32028df15bb37150892175e1ec8ec72 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ArmarDEConverter.cpp @@ -0,0 +1,130 @@ +#include "ArmarDEConverter.h" +#include <cmath> +#include <string> + +#include <SimoxUtility/algorithm/get_map_keys_values.h> +#include <SimoxUtility/algorithm/advanced.h> +#include "ArmarXCore/core/exceptions/local/ExpressionException.h" + +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> +#include <RobotAPI/libraries/armem_robot_state/aron/Exteroception.aron.generated.h> +#include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> + +#include <RobotAPI/libraries/armem_robot_state/server/proprioception/aron_conversions.h> + +#include "ConverterTools.h" + + +namespace armarx::armem::server::robot_state::exteroception +{ + + ArmarDEConverter::ArmarDEConverter() = default; + ArmarDEConverter::~ArmarDEConverter() = default; + + + aron::data::DictPtr + ArmarDEConverter::convert( + const RobotUnitDataStreaming::TimeStep& data, + const RobotUnitDataStreaming::DataStreamingDescription& description) + { + arondto::Exteroception dto; + dto.iterationID = data.iterationId; + + for (const auto& [dataEntryName, dataEntry] : description.entries) + { + process(dto, dataEntryName, {data, dataEntry}); + } + + // resize to square + for(auto& [_, tof] : dto.tof) + { + const int sr = std::sqrt(tof.array.size()); + const bool isSquare = (sr * sr == tof.array.size()); + + if(tof.array.size() > 0 and isSquare) + { + tof.array.resize(sr, sr); + } + } + + + return dto.toAron(); + } + + + void ArmarDEConverter::process( + arondto::Exteroception& dto, + const std::string& entryName, + const ConverterValue& value) + { + const std::vector<std::string> split = simox::alg::split(entryName, ".", false, false); + ARMARX_CHECK_GREATER_EQUAL(split.size(), 3); + const std::set<size_t> acceptedSizes{3, 4, 5}; + ARMARX_CHECK_GREATER(acceptedSizes.count(split.size()), 0) + << "Data entry name could not be parsed (exected 3 or 4 or 5 components between '.'): " + << "\n- split: '" << split << "'"; + + const std::string& category = split.at(0); + // const std::string& name = split.at(1); + const std::string& field = split.at(2); + ARMARX_CHECK_EQUAL(category, "sens") << category << " | " << entryName; + + if (simox::alg::contains(field, "tofDepth")) + { + // ARMARX_DEBUG << "Processing ToF sensor data"; + processToFEntry(dto.tof, split, value); + } + + } + + + + + void ArmarDEConverter::processToFEntry( + std::map<std::string, armarx::armem::exteroception::arondto::ToF>& tofs, + const std::vector<std::string>& split, + const ConverterValue& value) + { + // split e.g. "sens.LeftHand.tofDepth.element_15" (split at dot) + + ARMARX_CHECK_EQUAL(split.size(), 4); + ARMARX_CHECK_EQUAL(split.at(2), "tofDepth"); + + const std::string& name = split.at(1); + + // element 0 sens + // element 1 is either LeftHand or RightHand + + const std::map<std::string, std::string> sidePrefixMap + { + {"LeftHand", "Left"}, + {"RightHand", "Right"} + }; + + auto it = sidePrefixMap.find(name); + ARMARX_CHECK(it != sidePrefixMap.end()) << name; + + const std::string& side = it->second; + processToFEntry(tofs[side], split, value); + } + + void ArmarDEConverter::processToFEntry(armarx::armem::exteroception::arondto::ToF& tof, + const std::vector<std::string>& split, + const ConverterValue& value) + { + // split, e.g., element_12 + const std::vector<std::string> elements = simox::alg::split(split.back(), "_"); + ARMARX_CHECK_EQUAL(elements.size(), 2); + + const int idx = std::stoi(elements.at(1)); + + if(tof.array.size() < (idx +1)) + { + tof.array.resize(idx+1, 1); + } + + tof.array(idx) = getValueAs<float>(value); + } + + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ArmarDEConverter.h b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ArmarDEConverter.h new file mode 100644 index 0000000000000000000000000000000000000000..bf6c482a06bfd4be24fca5ad78a320b5b6834042 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ArmarDEConverter.h @@ -0,0 +1,51 @@ +#pragma once + +#include <map> +#include <string> + +#include <Eigen/Core> + +#include <RobotAPI/libraries/armem_robot_state/aron/Exteroception.aron.generated.h> + +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::exteroception +{ + struct ConverterValue; + // class ConverterTools; + + + class ArmarDEConverter : public ConverterInterface + { + public: + ArmarDEConverter(); + ~ArmarDEConverter() override; + + + aron::data::DictPtr + convert(const RobotUnitDataStreaming::TimeStep& data, + const RobotUnitDataStreaming::DataStreamingDescription& description) override; + + + protected: + void process(arondto::Exteroception& dtoExteroception, + const std::string& entryName, + const ConverterValue& value); + + + private: + + + void processToFEntry(std::map<std::string, armarx::armem::exteroception::arondto::ToF>& fts, + const std::vector<std::string>& split, + const ConverterValue& value); + + void processToFEntry(armarx::armem::exteroception::arondto::ToF& tof, + const std::vector<std::string>& split, + const ConverterValue& value); + + + // std::unique_ptr<ConverterTools> tools; + }; +} // namespace armarx::armem::server::robot_state::exteroception diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterInterface.cpp b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterInterface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ba33e6c5f308cd6213c8ccf02018ad63ba9440b --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterInterface.cpp @@ -0,0 +1,11 @@ +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::exteroception +{ + + ConverterInterface::~ConverterInterface() + { + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterInterface.h b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..5ba0ecc3c8aeaf5c2b3f3c03d9f36ed83269fb5e --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterInterface.h @@ -0,0 +1,32 @@ +#pragma once + +#include <memory> + +#include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> + +namespace armarx::RobotUnitDataStreaming +{ + struct TimeStep; + struct DataStreamingDescription; + struct DataEntry; +} +namespace armarx::armem::arondto +{ + class Proprioception; +} + +namespace armarx::armem::server::robot_state::exteroception +{ + class ConverterInterface + { + public: + + virtual ~ConverterInterface(); + + virtual + aron::data::DictPtr convert( + const RobotUnitDataStreaming::TimeStep& data, + const RobotUnitDataStreaming::DataStreamingDescription& description) = 0; + + }; +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterRegistry.cpp b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterRegistry.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef7296b669504e0f8d0aff8b4cd3991fe6ec10b3 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterRegistry.cpp @@ -0,0 +1,31 @@ +#include "ConverterRegistry.h" + +#include "ArmarDEConverter.h" + +#include <SimoxUtility/algorithm/get_map_keys_values.h> + + +namespace armarx::armem::server::robot_state::exteroception +{ + + ConverterRegistry::ConverterRegistry() + { + add<ArmarDEConverter>("ArmarDE"); + // add<Armar7Converter>("Armar7"); + } + + + ConverterInterface* + ConverterRegistry::get(const std::string& key) const + { + auto it = converters.find(key); + return it != converters.end() ? it->second.get() : nullptr; + } + + + std::vector<std::string> ConverterRegistry::getKeys() const + { + return simox::alg::get_keys(converters); + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterRegistry.h b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterRegistry.h new file mode 100644 index 0000000000000000000000000000000000000000..1e1cac9a18eb3d901b0ca0f61eccf08395a279b5 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterRegistry.h @@ -0,0 +1,36 @@ +#pragma once + +#include <map> +#include <memory> +#include <string> +#include <vector> + +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::exteroception +{ + class ConverterRegistry + { + public: + + ConverterRegistry(); + + + template <class ConverterT, class ...Args> + void add(const std::string& key, Args... args) + { + converters[key].reset(new ConverterT(args...)); + } + + + ConverterInterface* get(const std::string& key) const; + std::vector<std::string> getKeys() const; + + + private: + + std::map<std::string, std::unique_ptr<ConverterInterface>> converters; + + }; +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterTools.cpp b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterTools.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6086a9aee5f2adb8d997a65302084e0c902d747c --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterTools.cpp @@ -0,0 +1,179 @@ +#include "ConverterTools.h" + +#include <SimoxUtility/algorithm/get_map_keys_values.h> +#include <SimoxUtility/algorithm/advanced.h> + +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> +#include <RobotAPI/libraries/aron/core/data/variant/container/Dict.h> + + +namespace armarx::armem::server::robot_state +{ + + std::optional<std::string> + exteroception::findByPrefix(const std::string& key, const std::set<std::string>& prefixes) + { + for (const auto& prefix : prefixes) + { + if (simox::alg::starts_with(key, prefix)) + { + return prefix; + } + } + return std::nullopt; + } +} + + +namespace armarx::armem::server::robot_state::exteroception +{ + ConverterTools::ConverterTools() + { + { + vector3fSetters["X"] = [](Eigen::Vector3f & v, float f) + { + v.x() = f; + }; + vector3fSetters["Y"] = [](Eigen::Vector3f & v, float f) + { + v.y() = f; + }; + vector3fSetters["Z"] = [](Eigen::Vector3f & v, float f) + { + v.z() = f; + }; + vector3fSetters["x"] = vector3fSetters["X"]; + vector3fSetters["y"] = vector3fSetters["Y"]; + vector3fSetters["z"] = vector3fSetters["Z"]; + vector3fSetters["Rotation"] = vector3fSetters["Z"]; + } + { + platformPoseGetters["acceleration"] = [](prop::arondto::Platform & p) + { + return &p.acceleration; + }; + platformPoseGetters["relativePosition"] = [](prop::arondto::Platform & p) + { + return &p.relativePosition; + }; + platformPoseGetters["velocity"] = [](prop::arondto::Platform & p) + { + return &p.velocity; + }; + platformIgnored.insert("absolutePosition"); + } + { + ftGetters["gravCompF"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.gravityCompensationForce; + }; + ftGetters["gravCompT"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.gravityCompensationTorque; + }; + ftGetters["f"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.force; + }; + ftGetters["t"] = [](prop::arondto::ForceTorque & ft) + { + return &ft.torque; + }; + } + { + jointGetters["acceleration"] = [](prop::arondto::Joints & j) + { + return &j.acceleration; + }; + jointGetters["gravityTorque"] = [](prop::arondto::Joints & j) + { + return &j.gravityTorque; + }; + jointGetters["inertiaTorque"] = [](prop::arondto::Joints & j) + { + return &j.inertiaTorque; + }; + jointGetters["inverseDynamicsTorque"] = [](prop::arondto::Joints & j) + { + return &j.inverseDynamicsTorque; + }; + jointGetters["position"] = [](prop::arondto::Joints & j) + { + return &j.position; + }; + jointGetters["torque"] = [](prop::arondto::Joints & j) + { + return &j.torque; + }; + jointGetters["velocity"] = [](prop::arondto::Joints & j) + { + return &j.velocity; + }; + } + { + +#define ADD_SCALAR_SETTER(container, name, type) \ + container [ #name ] = []( \ + prop::arondto::Joints & dto, \ + const std::vector<std::string>& split, \ + const ConverterValue & value) \ + { \ + dto. name [split.at(1)] = getValueAs< type >(value); \ + } + + ADD_SCALAR_SETTER(jointSetters, position, float); + ADD_SCALAR_SETTER(jointSetters, velocity, float); + ADD_SCALAR_SETTER(jointSetters, acceleration, float); + + ADD_SCALAR_SETTER(jointSetters, relativePosition, float); + ADD_SCALAR_SETTER(jointSetters, filteredVelocity, float); + + ADD_SCALAR_SETTER(jointSetters, currentTarget, float); + ADD_SCALAR_SETTER(jointSetters, positionTarget, float); + ADD_SCALAR_SETTER(jointSetters, velocityTarget, float); + + ADD_SCALAR_SETTER(jointSetters, torque, float); + ADD_SCALAR_SETTER(jointSetters, inertiaTorque, float); + ADD_SCALAR_SETTER(jointSetters, gravityTorque, float); + ADD_SCALAR_SETTER(jointSetters, gravityCompensatedTorque, float); + ADD_SCALAR_SETTER(jointSetters, inverseDynamicsTorque, float); + ADD_SCALAR_SETTER(jointSetters, torqueTicks, int); + + ADD_SCALAR_SETTER(jointSetters, positionTarget, float); + ADD_SCALAR_SETTER(jointSetters, currentTarget, float); + ADD_SCALAR_SETTER(jointSetters, positionTarget, float); + + // "temperature" handled below + // ADD_SCALAR_SETTER(jointSetters, temperature, float); + + ADD_SCALAR_SETTER(jointSetters, motorCurrent, float); + ADD_SCALAR_SETTER(jointSetters, maxTargetCurrent, float); + + ADD_SCALAR_SETTER(jointSetters, sensorBoardUpdateRate, float); + ADD_SCALAR_SETTER(jointSetters, I2CUpdateRate, float); + + ADD_SCALAR_SETTER(jointSetters, JointStatusEmergencyStop, bool); + ADD_SCALAR_SETTER(jointSetters, JointStatusEnabled, bool); + ADD_SCALAR_SETTER(jointSetters, JointStatusError, int); + ADD_SCALAR_SETTER(jointSetters, JointStatusOperation, int); + + +#define ADD_VECTOR3_SETTER(container, name, type) \ + container [ #name ] = [this]( \ + prop::arondto::Joints & dto, \ + const std::vector<std::string>& split, \ + const ConverterValue & value) \ + { \ + auto& vec = dto. name [split.at(1)]; \ + auto& setter = this->vector3fSetters.at(split.at(3)); \ + setter(vec, getValueAs< type >(value)); \ + } + + ADD_VECTOR3_SETTER(jointSetters, angularVelocity, float); + ADD_VECTOR3_SETTER(jointSetters, linearAcceleration, float); + + // ADD_GETTER(jointVectorGetters, orientation, float); + } + } + +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterTools.h b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterTools.h new file mode 100644 index 0000000000000000000000000000000000000000..7460ebb133a01387c0f29a472961366877254506 --- /dev/null +++ b/source/RobotAPI/libraries/armem_robot_state/server/exteroception/converters/ConverterTools.h @@ -0,0 +1,106 @@ +#pragma once + +#include <map> +#include <set> +#include <string> + +#include <Eigen/Core> + +#include <SimoxUtility/algorithm/string/string_tools.h> + +#include <RobotAPI/libraries/armem_robot_state/aron/Proprioception.aron.generated.h> +#include <RobotAPI/libraries/RobotUnitDataStreamingReceiver/RobotUnitDataStreamingReceiver.h> + +#include "ConverterInterface.h" + + +namespace armarx::armem::server::robot_state::exteroception +{ + + struct ConverterValue + { + const RobotUnitDataStreaming::TimeStep& data; + const RobotUnitDataStreaming::DataEntry& entry; + }; + + + template <class T> + T + getValueAs(const ConverterValue& value) + { + return RobotUnitDataStreamingReceiver::GetAs<T>(value.data, value.entry); + } + + + /** + * @brief Search + * @param key + * @param prefixes + * @return + */ + std::optional<std::string> + findByPrefix(const std::string& key, const std::set<std::string>& prefixes); + + + template <class ValueT> + ValueT + findByPrefix(const std::string& key, const std::map<std::string, ValueT>& map) + { + for (const auto& [prefix, value] : map) + { + if (simox::alg::starts_with(key, prefix)) + { + return value; + } + } + return nullptr; + } + + + template <class ValueT> + ValueT + findBySuffix(const std::string& key, const std::map<std::string, ValueT>& map) + { + for (const auto& [suffix, value] : map) + { + if (simox::alg::ends_with(key, suffix)) + { + return value; + } + } + return nullptr; + } + + + + class ConverterTools + { + public: + + ConverterTools(); + + + public: + + std::map<std::string, std::function<void(Eigen::Vector3f&, float)> > vector3fSetters; + + std::map<std::string, std::function<std::map<std::string, float>*(prop::arondto::Joints&)> > jointGetters; + std::map<std::string, std::function<std::map<std::string, Eigen::Vector3f>*(prop::arondto::Joints&)> > jointVectorGetters; + + using JointSetter = std::function<void(prop::arondto::Joints& dto, const std::vector<std::string>& split, const ConverterValue& value)>; + std::map<std::string, JointSetter> jointSetters; + + std::map<std::string, std::function<Eigen::Vector3f*(prop::arondto::Platform&)> > platformPoseGetters; + std::set<std::string> platformIgnored; + + std::map<std::string, std::function<Eigen::Vector3f*(prop::arondto::ForceTorque&)> > ftGetters; + + + std::map<std::string, std::string> sidePrefixMap + { + { "R", "Right" }, + { "L", "Left" }, + }; + + }; +} diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp index 52de49d1420a9bf194cf4071b4f23ea8e2944ad6..847ec98c82966408545a50bf9d7fa93fc7081d9f 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.cpp @@ -29,6 +29,7 @@ #include <SimoxUtility/math/convert/rpy_to_mat3f.h> // ArmarX +#include "ArmarXCore/core/logging/Logging.h" #include "ArmarXCore/core/time/Metronome.h" #include <ArmarXCore/libraries/ArmarXCoreComponentPlugins/DebugObserverComponentPlugin.h> @@ -87,8 +88,11 @@ namespace armarx::armem::server::robot_state::proprioception // Commits lock the core segments. - // Proprioception - armem::CommitResult result = memory.commitLocking(update.proprioception); + // Proprioception + Exteroception + armem::CommitResult resultProprioception = memory.commitLocking(update.proprioception); + + ARMARX_DEBUG << deactivateSpam(1) << VAROUT(update.exteroception.updates.size()); + armem::CommitResult resultExteroception = memory.commitLocking(update.exteroception); endProprioception = std::chrono::high_resolution_clock::now(); // Localization @@ -100,11 +104,18 @@ namespace armarx::armem::server::robot_state::proprioception } endLocalization = std::chrono::high_resolution_clock::now(); - if (not result.allSuccess()) + if (not resultProprioception.allSuccess()) + { + ARMARX_WARNING << "Could not commit data to proprioception segment in memory. Error message: " + << resultProprioception.allErrorMessages(); + } + + if (not resultExteroception.allSuccess()) { - ARMARX_WARNING << "Could not commit data to memory. Error message: " - << result.allErrorMessages(); + ARMARX_WARNING << "Could not commit data to exteroception segment in memory. Error message: " + << resultExteroception.allErrorMessages(); } + if (debugObserver) { auto end = std::chrono::high_resolution_clock::now(); @@ -143,15 +154,27 @@ namespace armarx::armem::server::robot_state::proprioception // Send batch to memory Update update; - { + if(data.proprioception){ armem::EntityUpdate& up = update.proprioception.add(); up.entityID = properties.robotUnitProviderID.withEntityName( properties.robotUnitProviderID.providerSegmentName); + up.entityID.coreSegmentName =::armarx::armem::robot_state::constants::proprioceptionCoreSegment; up.timeCreated = data.timestamp; up.instancesData = {data.proprioception}; } + // Exteroception + if(data.exteroception){ + armem::EntityUpdate& up = update.exteroception.add(); + up.entityID = properties.robotUnitProviderID.withEntityName( + properties.robotUnitProviderID.providerSegmentName); + up.entityID.coreSegmentName = ::armarx::armem::robot_state::constants::exteroceptionCoreSegment; + up.timeCreated = data.timestamp; + up.instancesData = {data.exteroception}; + } + // Extract odometry data. + ARMARX_CHECK_NOT_NULL(data.proprioception); const std::string platformKey = "platform"; if (data.proprioception->hasElement(platformKey)) { diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h index f911d43fc7fabf6626c5e6e230cf74a66753d1cf..78863697a61ecd20c4947188a04965d5990c6f7b 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotStateWriter.h @@ -71,6 +71,7 @@ namespace armarx::armem::server::robot_state::proprioception struct Update { armem::Commit proprioception; + armem::Commit exteroception; std::vector<armem::robot_state::Transform> localization; }; Update buildUpdate(const RobotUnitData& dataQueue); diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h index f62d0b0c9c078185fe7959a229956f5f67b4afdc..8d904f249029cf9fd5ce010657cb4324f3ba5551 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitData.h @@ -14,6 +14,7 @@ namespace armarx::armem::server::robot_state::proprioception { Time timestamp; aron::data::DictPtr proprioception; + aron::data::DictPtr exteroception; }; using Queue = boost::sync_queue<RobotUnitData>; diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp index c74f355c247eb6fca2ca348b5459527aef204260..53021dc7d88f9362a967f395b1381d07934f271e 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.cpp @@ -24,15 +24,23 @@ namespace armarx::armem::server::robot_state::proprioception const std::string& robotTypeName) { { - converter = converterRegistry.get(robotTypeName); - ARMARX_CHECK_NOT_NULL(converter) + converterProprioception = proprioceptionConverterRegistry.get(robotTypeName); + ARMARX_CHECK_NOT_NULL(converterProprioception) << "No converter for robot type '" << robotTypeName << "' available. \n" - << "Known are: " << converterRegistry.getKeys(); + << "Known are: " << proprioceptionConverterRegistry.getKeys(); config.loggingNames.push_back(properties.sensorPrefix); receiver = robotUnitPlugin.startDataStreaming(config); description = receiver->getDataDescription(); } + + { + converterExteroception = exteroceptionConverterRegistry.get(robotTypeName); + ARMARX_CHECK_NOT_NULL(converterProprioception) + << "No converter for robot type '" << robotTypeName << "' available. \n" + << "Known are: " << exteroceptionConverterRegistry.getKeys(); + } + { // Thread-local copy of debug observer helper. debugObserver = DebugObserverHelper( @@ -67,7 +75,7 @@ namespace armarx::armem::server::robot_state::proprioception std::optional<RobotUnitData> RobotUnitReader::fetchAndConvertLatestRobotUnitData() { - ARMARX_CHECK_NOT_NULL(converter); + ARMARX_CHECK_NOT_NULL(converterProprioception); const std::optional<RobotUnitDataStreaming::TimeStep> data = fetchLatestData(); if (not data.has_value()) @@ -79,7 +87,17 @@ namespace armarx::armem::server::robot_state::proprioception auto start = std::chrono::high_resolution_clock::now(); RobotUnitData result; - result.proprioception = converter->convert(data.value(), description); + + if(converterProprioception != nullptr) + { + result.proprioception = converterProprioception->convert(data.value(), description); + } + + if(converterExteroception != nullptr) + { + result.exteroception = converterExteroception->convert(data.value(), description); + } + result.timestamp = Time(Duration::MicroSeconds(data->timestampUSec)); auto stop = std::chrono::high_resolution_clock::now(); diff --git a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h index cc3cd27581951a0365cf028ff2bf2d7929568f1d..88313402f217eb8f46470aafd796465354f6cc68 100644 --- a/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h +++ b/source/RobotAPI/libraries/armem_robot_state/server/proprioception/RobotUnitReader.h @@ -15,7 +15,9 @@ #include "RobotUnitData.h" #include "converters/ConverterInterface.h" +#include "../exteroception/converters/ConverterInterface.h" #include "converters/ConverterRegistry.h" +#include "../exteroception/converters/ConverterRegistry.h" namespace armarx::plugins @@ -28,6 +30,8 @@ namespace armarx using RobotUnitDataStreamingReceiverPtr = std::shared_ptr<class RobotUnitDataStreamingReceiver>; } + +// TODO move this out of proprioception. it is now for proprioception and exteroception namespace armarx::armem::server::robot_state::proprioception { class RobotUnitReader : public armarx::Logging @@ -68,8 +72,10 @@ namespace armarx::armem::server::robot_state::proprioception RobotUnitDataStreamingReceiverPtr receiver; RobotUnitDataStreaming::DataStreamingDescription description; - ConverterRegistry converterRegistry; - ConverterInterface* converter = nullptr; + ConverterRegistry proprioceptionConverterRegistry; + exteroception::ConverterRegistry exteroceptionConverterRegistry; + proprioception::ConverterInterface* converterProprioception = nullptr; + exteroception::ConverterInterface* converterExteroception = nullptr; armarx::SimpleRunningTask<>::pointer_type task = nullptr; }; diff --git a/source/RobotAPI/libraries/aron/CMakeLists.txt b/source/RobotAPI/libraries/aron/CMakeLists.txt index ce4f7bc99cb8d923decfdd7fdb79390837e79150..e1ccc8560ff75b018fbc958d0f1febf0512d9590 100644 --- a/source/RobotAPI/libraries/aron/CMakeLists.txt +++ b/source/RobotAPI/libraries/aron/CMakeLists.txt @@ -2,3 +2,4 @@ add_subdirectory(core) add_subdirectory(converter) add_subdirectory(codegeneration) add_subdirectory(common) +add_subdirectory(test) diff --git a/source/RobotAPI/libraries/aron/core/aron_conversions.h b/source/RobotAPI/libraries/aron/core/aron_conversions.h index d0f88cab840be59b86bbeb22d7229afb9f9f0b48..d27d6415877d4c15f9ddf36310e656590f8efd7e 100644 --- a/source/RobotAPI/libraries/aron/core/aron_conversions.h +++ b/source/RobotAPI/libraries/aron/core/aron_conversions.h @@ -3,12 +3,29 @@ #include <map> #include <memory> #include <optional> +#include <type_traits> #include <vector> #include "Path.h" namespace armarx::aron { + +namespace detail +{ + + // Helper concept to avoid ambiguities + template<typename DtoT, typename BoT> + concept DtoAndBoAreSame = std::is_same<DtoT, BoT>::value; + + template <class ...> + struct is_optional : public std::false_type {}; + + template <class ...Ts> + struct is_optional<std::optional<Ts...>> : public std::true_type {}; + +} + /** * Framework for converting ARON DTOs (Data Transfer Objects) to C++ BOs * (Business Objects) and back. @@ -51,12 +68,6 @@ namespace armarx::aron * } * @endcode */ - - // Helper concept to avoid ambiguities - template<typename DtoT, typename BoT> - concept DtoAndBoAreSame = std::is_same<DtoT, BoT>::value; - - // Same type template <class T> void toAron(T& dto, const T& bo) @@ -139,6 +150,24 @@ namespace armarx::aron } } + + // One-sided optional + template <class DtoT, class BoT> + requires (not detail::is_optional<BoT>::value) + void toAron(std::optional<DtoT>& dto, const BoT& bo) + { + dto = DtoT{}; + toAron(*dto, bo); + } + template <class DtoT, class BoT> + requires (not detail::is_optional<DtoT>::value) + void fromAron(DtoT& dto, const std::optional<BoT>& bo) + { + bo = BoT{}; + fromAron(dto, *bo); + } + + // Flag-controlled optional template <class DtoT, class BoT> void toAron(DtoT& dto, bool& dtoValid, const BoT& bo, bool boValid) @@ -154,6 +183,7 @@ namespace armarx::aron } } + template <class DtoT, class BoT> void fromAron(const DtoT& dto, bool dtoValid, BoT& bo, bool& boValid) { @@ -222,7 +252,7 @@ namespace armarx::aron // std::map template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT> - requires (!(DtoAndBoAreSame<DtoKeyT, BoKeyT> and DtoAndBoAreSame<DtoValueT, BoValueT>)) + requires (!(detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and detail::DtoAndBoAreSame<DtoValueT, BoValueT>)) void toAron(std::map<DtoKeyT, DtoValueT>& dtoMap, const std::map<BoKeyT, BoValueT>& boMap) { @@ -236,7 +266,7 @@ namespace armarx::aron } } template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT> - requires (!(DtoAndBoAreSame<DtoKeyT, BoKeyT> and DtoAndBoAreSame<DtoValueT, BoValueT>)) + requires (!(detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and detail::DtoAndBoAreSame<DtoValueT, BoValueT>)) void fromAron(const std::map<DtoKeyT, DtoValueT>& dtoMap, std::map<BoKeyT, BoValueT>& boMap) { @@ -252,7 +282,7 @@ namespace armarx::aron template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT> - requires (!(DtoAndBoAreSame<DtoKeyT, BoKeyT> and DtoAndBoAreSame<DtoValueT, BoValueT>)) + requires (!(detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and detail::DtoAndBoAreSame<DtoValueT, BoValueT>)) std::map<DtoKeyT, DtoValueT> toAron(const std::map<BoKeyT, BoValueT>& boMap) { std::map<DtoKeyT, DtoValueT> dtoMap; @@ -343,20 +373,20 @@ namespace armarx // std::vector template <class DtoT, class BoT> - requires (!aron::DtoAndBoAreSame<DtoT, BoT>) + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void toAron(std::vector<DtoT>& dtos, const std::vector<BoT>& bos) { armarx::aron::toAron(dtos, bos); } template <class DtoT, class BoT> - requires (!aron::DtoAndBoAreSame<DtoT, BoT>) + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void fromAron(const std::vector<DtoT>& dtos, std::vector<BoT>& bos) { armarx::aron::fromAron(dtos, bos); } template <class DtoT, class BoT> - requires (!aron::DtoAndBoAreSame<DtoT, BoT>) + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) std::vector<DtoT> toAron(const std::vector<BoT>& bos) { return armarx::aron::toAron<DtoT, BoT>(bos); @@ -365,13 +395,13 @@ namespace armarx // std::map template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT> - requires (!(aron::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::DtoAndBoAreSame<DtoValueT, BoValueT>)) + requires (!(aron::detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::detail::DtoAndBoAreSame<DtoValueT, BoValueT>)) void toAron(std::map<DtoKeyT, DtoValueT>& dtoMap, const std::map<BoKeyT, BoValueT>& boMap) { armarx::aron::toAron(dtoMap, boMap); } template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT> - requires (!(aron::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::DtoAndBoAreSame<DtoValueT, BoValueT>)) + requires (!(aron::detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::detail::DtoAndBoAreSame<DtoValueT, BoValueT>)) void fromAron(const std::map<DtoKeyT, DtoValueT>& dtoMap, std::map<BoKeyT, BoValueT>& boMap) { armarx::aron::fromAron(dtoMap, boMap); @@ -379,7 +409,7 @@ namespace armarx template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT> - requires (!(aron::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::DtoAndBoAreSame<DtoValueT, BoValueT>)) + requires (!(aron::detail::DtoAndBoAreSame<DtoKeyT, BoKeyT> and aron::detail::DtoAndBoAreSame<DtoValueT, BoValueT>)) std::map<DtoKeyT, DtoValueT> toAron(const std::map<BoKeyT, BoValueT>& boMap) { armarx::aron::toAron<DtoKeyT, DtoValueT, BoKeyT, BoValueT>(boMap); diff --git a/source/RobotAPI/libraries/aron/core/rw.h b/source/RobotAPI/libraries/aron/core/rw.h index 6257f2422687591b0991afed8a0577ee127207ef..95b6aac606f47d3c46de34ac5ec077ded28c41a4 100644 --- a/source/RobotAPI/libraries/aron/core/rw.h +++ b/source/RobotAPI/libraries/aron/core/rw.h @@ -26,7 +26,7 @@ namespace armarx::aron } template<class ReaderT, class DtoT, class BoT> - requires (data::isReader<ReaderT> && !DtoAndBoAreSame<DtoT, BoT>) + requires (data::isReader<ReaderT> && !detail::DtoAndBoAreSame<DtoT, BoT>) inline void read(ReaderT& aron_r, typename ReaderT::InputType& input, BoT& ret) { DtoT aron; @@ -36,7 +36,7 @@ namespace armarx::aron } template<class WriterT, class DtoT, class BoT> - requires (data::isWriter<WriterT> && !DtoAndBoAreSame<DtoT, BoT>) + requires (data::isWriter<WriterT> && !detail::DtoAndBoAreSame<DtoT, BoT>) inline void write(WriterT& aron_w, const BoT& input, typename WriterT::ReturnType& ret, const armarx::aron::Path& aron_p = armarx::aron::Path()) { DtoT aron; diff --git a/source/RobotAPI/libraries/aron/test/AronConversionTester.cpp b/source/RobotAPI/libraries/aron/test/AronConversionTester.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aac543a04b6ba1696dedc7a21c7d4ea125896e4e --- /dev/null +++ b/source/RobotAPI/libraries/aron/test/AronConversionTester.cpp @@ -0,0 +1,38 @@ +/** + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @package RobotAPI::ArmarXObjects::aron_cpp_to_python_conv_test + * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) + * @date 2023 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + + +#include "AronConversionTester.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + + +namespace armarx::aron::test +{ + + AronConversionTester::AronConversionTester(dti::AronConversionTestInterfacePrx python) : interface(python) + { + ARMARX_CHECK(python); + } + + +} // namespace armarx::aron::test diff --git a/source/RobotAPI/libraries/aron/test/AronConversionTester.h b/source/RobotAPI/libraries/aron/test/AronConversionTester.h new file mode 100644 index 0000000000000000000000000000000000000000..b7ad4b8dfdcf55a37f8efc3116c27440d925d428 --- /dev/null +++ b/source/RobotAPI/libraries/aron/test/AronConversionTester.h @@ -0,0 +1,111 @@ +/** + * 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::aron_cpp_to_python_conv_test + * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu ) + * @date 2023 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + + +#pragma once + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> +#include <ArmarXCore/core/logging/Logging.h> + +#include <RobotAPI/interface/aron/test/AronConversionTestInterface.h> + + +namespace armarx::aron::test +{ + + /** + * @brief Helper class for implementing distributed ARON conversion tests + * based on the `dti::AronConversionTestInterfacePrx`. + * + * Example usage: + * + * @code + * + * armarx::aron::test::dti::AronConversionTestInterfacePrx pythonComponent = ...; + * + * auto myAronClassProbeFn = []() + * { + * my::arondto::MyAronClass probe; + * probe.data = "42"; + * return probe, + * }; + * + * armarx::aron::test::AronConversionTester tester(pythonComponent); + * + * tester.test<my::arondto::MyAronClass>(myAronClassProbeFn, "MyAronClass"); + * + * @endcode + * + * Note that the `pythonComponent` must point to a corresponding + * implementation of the `AronConversionTestInterfacePrx`. + */ + class AronConversionTester + { + public: + template <class AronClassT> + using ProbeFn = std::function<AronClassT()>; + + + public: + AronConversionTester(dti::AronConversionTestInterfacePrx interface); + + /** + * @brief Test the conversion of a specific ARON class. + * + * @param probeFn A factory function creating a test instance of the + * ARON class. + * @param aronClassName The name of the ARON class. Can be used by the + * other component to decide which class to convert to. + */ + template <class AronClassT> + void + test(ProbeFn<AronClassT> probeFn, const std::string& aronClassName) + { + std::stringstream ss; + ss << "Test for ARON class '" << aronClassName << "': "; + + const AronClassT probe = probeFn(); + + dto::TestAronConversionRequest req; + req.aronClassName = aronClassName; + req.probe = probe.toAronDTO(); + + dto::TestAronConversionResponse res = interface->testAronConversion(req); + + if (res.success) + { + const AronClassT probeOut = AronClassT::FromAron(res.probe); + ARMARX_CHECK(probeOut == probe); + + ARMARX_IMPORTANT << ss.str() << "Success"; + } + else + { + ARMARX_WARNING << ss.str() << "Conversion in Python component failed: \n" << res.errorMessage; + } + } + + public: + dti::AronConversionTestInterfacePrx interface; + }; + +} // namespace armarx::aron::test diff --git a/source/RobotAPI/libraries/aron/test/CMakeLists.txt b/source/RobotAPI/libraries/aron/test/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3a199acf0f19c13f6253e66947e540bfe3950efd --- /dev/null +++ b/source/RobotAPI/libraries/aron/test/CMakeLists.txt @@ -0,0 +1,25 @@ +set(LIB_NAME arontest) + +armarx_component_set_name("${LIB_NAME}") +armarx_set_target("Library: ${LIB_NAME}") + + +armarx_add_library( + LIBS + # ArmarXCore + ArmarXCore + # RobotAPI + RobotAPICore + RobotAPIInterfaces + aron + + HEADERS + AronConversionTester.h + + SOURCES + AronConversionTester.cpp +) + + +add_library(aron::test ALIAS arontest) +add_library(${PROJECT_NAME}::Aron::test ALIAS arontest)