NamespaceComputation.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "NamespaceComputation.h"
  9. #include <AzCore/Component/ComponentApplicationBus.h>
  10. #include <AzCore/Component/Entity.h>
  11. #include <AzCore/Component/TransformBus.h>
  12. #include <AzCore/Settings/SettingsRegistry.h>
  13. #include <ROS2/Frame/ROS2FrameComponent.h>
  14. #include <ROS2/Frame/ROS2FrameConfiguration.h>
  15. #include <ROS2/ROS2NamesBus.h>
  16. namespace ROS2
  17. {
  18. namespace
  19. {
  20. inline constexpr const char* DefaultGlobalFrameName = "odom";
  21. inline constexpr const char* DefaultGlobalFrameNameConfigurationKey = "/O3DE/ROS2/GlobalFrameName";
  22. constexpr char FramePathSeparator = '/';
  23. //! Helper to get the name of an entity from its EntityId.
  24. AZStd::string GetName(AZ::EntityId id)
  25. {
  26. AZStd::string name;
  27. AZ::ComponentApplicationBus::BroadcastResult(name, &AZ::ComponentApplicationRequests::GetEntityName, id);
  28. return name;
  29. }
  30. //! Helper to concatenate parts of a path with '/' separator.
  31. AZStd::string ConcatenatePath(const AZStd::vector<AZStd::string>& path)
  32. {
  33. AZStd::string result;
  34. for (const auto& part : path)
  35. {
  36. if (!result.empty())
  37. {
  38. result += FramePathSeparator;
  39. }
  40. result += part;
  41. }
  42. return result;
  43. }
  44. //! Helper to retrieve the ROS2FrameConfiguration from the entity with the given ID, if it has a ROS2FrameComponent or
  45. //! ROS2FrameEditorComponent.
  46. AZStd::optional<ROS2FrameConfiguration> GetConfigurationFromComponent(AZ::EntityId id)
  47. {
  48. AZ::Entity* entity = nullptr;
  49. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, id);
  50. AZ_Assert(entity, "No entity for id : %s", id.ToString().c_str());
  51. if (!entity)
  52. {
  53. return AZStd::nullopt;
  54. }
  55. const auto* componentEditor = entity->FindComponent(AZ::Uuid(ROS2FrameEditorComponentTypeId));
  56. if (componentEditor)
  57. {
  58. const auto* interface = dynamic_cast<const ROSFrameInterface*>(componentEditor);
  59. if (interface)
  60. {
  61. return interface->GetConfiguration();
  62. }
  63. }
  64. const auto* componentGame = entity->FindComponent(AZ::Uuid(ROS2FrameComponentTypeId));
  65. if (componentGame)
  66. {
  67. const auto* interface = dynamic_cast<const ROSFrameInterface*>(componentGame);
  68. if (interface)
  69. {
  70. return interface->GetConfiguration();
  71. }
  72. }
  73. return AZStd::nullopt;
  74. }
  75. //! Helper function to recursively traverse the transform hierarchy and collect ancestor entity IDs.
  76. //! Uses AZ::TransformInterface from O3DE to get parent entity IDs.
  77. void TraverseTransforms(AZ::EntityId id, AZStd::vector<AZ::EntityId>& predecessors)
  78. {
  79. predecessors.push_back(id);
  80. AZ::Entity* entity = nullptr;
  81. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, id);
  82. AZ_Assert(entity, "No entity for id : %s", id.ToString().c_str());
  83. if (!entity)
  84. {
  85. return;
  86. }
  87. auto* transformInterface = entity->GetTransform();
  88. if (!transformInterface)
  89. {
  90. return;
  91. }
  92. AZ::EntityId parentId = transformInterface->GetParentId();
  93. if (!parentId.IsValid())
  94. {
  95. return;
  96. }
  97. TraverseTransforms(parentId, predecessors);
  98. }
  99. } // namespace
  100. bool HasROS2FrameComponent(AZ::EntityId id)
  101. {
  102. AZ::Entity* entity = nullptr;
  103. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, id);
  104. if (!entity)
  105. {
  106. return false;
  107. }
  108. return (
  109. entity->FindComponent(AZ::Uuid(ROS2FrameEditorComponentTypeId)) || entity->FindComponent(AZ::Uuid(ROS2FrameComponentTypeId)));
  110. }
  111. AZStd::vector<AZ::EntityId> GetAllAncestorTransformBus(const AZ::EntityId& id)
  112. {
  113. AZStd::vector<AZ::EntityId> predecessors;
  114. TraverseTransforms(id, predecessors);
  115. return predecessors;
  116. }
  117. AZStd::vector<AZ::EntityId> GetEntitiesWithROS2FrameComponent(const AZStd::vector<AZ::EntityId>& unfiletered)
  118. {
  119. AZStd::vector<AZ::EntityId> filtered;
  120. for (auto it = unfiletered.begin(); it != unfiletered.end(); ++it)
  121. {
  122. AZ::Entity* entity = nullptr;
  123. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, *it);
  124. if (!entity)
  125. {
  126. continue;
  127. }
  128. if (HasROS2FrameComponent(*it))
  129. {
  130. filtered.push_back(*it);
  131. }
  132. }
  133. return filtered;
  134. }
  135. AZ::EntityId GetFirstEntityWithROS2FrameComponent(const AZStd::vector<AZ::EntityId>& predecessors)
  136. {
  137. for (auto it = predecessors.rbegin(); it != predecessors.rend(); ++it)
  138. {
  139. if (HasROS2FrameComponent(*it))
  140. {
  141. return *it;
  142. }
  143. }
  144. return AZ::EntityId(AZ::EntityId::InvalidEntityId);
  145. }
  146. AZ::EntityId GetLastEntityWithROS2FrameComponent(const AZStd::vector<AZ::EntityId>& predecessors)
  147. {
  148. for (auto it = predecessors.begin(); it != predecessors.end(); ++it)
  149. {
  150. if (HasROS2FrameComponent(*it))
  151. {
  152. return *it;
  153. }
  154. }
  155. return AZ::EntityId(AZ::EntityId::InvalidEntityId);
  156. }
  157. AZStd::string GetNamespacedName(const AZStd::string& namespaceName, const AZStd::string& name)
  158. {
  159. if (namespaceName.empty())
  160. {
  161. return name;
  162. }
  163. return namespaceName + "/" + name;
  164. }
  165. AZStd::string ComputeNamespace(const AZStd::vector<AZStd::pair<AZStd::string, ROS2FrameConfiguration>>& configurations)
  166. {
  167. if (configurations.empty())
  168. {
  169. return "";
  170. }
  171. AZStd::vector<AZStd::string> resolvedNames;
  172. for (auto it = configurations.rbegin(); it != configurations.rend(); ++it)
  173. {
  174. const auto& ancestorName = it->first;
  175. const auto& ancestorConfig = it->second;
  176. const auto ancestorStrategy = ancestorConfig.m_namespaceConfiguration.m_namespaceStrategy;
  177. if (ancestorStrategy == NamespaceConfiguration::NamespaceStrategy::FromEntityName)
  178. {
  179. resolvedNames.emplace_back(ancestorName);
  180. }
  181. else if (ancestorStrategy == NamespaceConfiguration::NamespaceStrategy::Custom)
  182. {
  183. resolvedNames.emplace_back(ancestorConfig.m_namespaceConfiguration.m_customNamespace);
  184. }
  185. else if (ancestorStrategy == NamespaceConfiguration::NamespaceStrategy::Empty)
  186. {
  187. void(); // do nothing
  188. }
  189. else if (ancestorStrategy == NamespaceConfiguration::NamespaceStrategy::Default)
  190. {
  191. if (it == configurations.rbegin())
  192. {
  193. resolvedNames.emplace_back(ancestorName);
  194. }
  195. }
  196. }
  197. return ConcatenatePath(resolvedNames);
  198. }
  199. AZStd::string ComputeNamespace(AZ::EntityId entity)
  200. {
  201. // Compute namespace based on ancestor frames.
  202. const AZStd::vector<AZ::EntityId> predecessors = GetAllAncestorTransformBus(entity);
  203. const AZStd::vector<AZ::EntityId> predecessorsWithRos2Frame = GetEntitiesWithROS2FrameComponent(predecessors);
  204. AZStd::vector<AZStd::pair<AZStd::string, ROS2FrameConfiguration>> configurations;
  205. for (const auto& predecessorId : predecessorsWithRos2Frame)
  206. {
  207. if (auto config = GetConfigurationFromComponent(predecessorId); config.has_value())
  208. {
  209. const auto name = GetName(predecessorId);
  210. configurations.emplace_back(name, config.value());
  211. }
  212. }
  213. return ComputeNamespace(configurations);
  214. }
  215. AZStd::string GetGlobalFrameIDFromRegistry()
  216. {
  217. // Get odometry frame, from settings registry
  218. AZStd::string odometryFrame;
  219. auto* registry = AZ::SettingsRegistry::Get();
  220. AZ_Error("ROS2FrameComponent", registry, "No settings registry found, using default odometry frame name");
  221. if (registry)
  222. {
  223. if (!registry->Get(odometryFrame, DefaultGlobalFrameNameConfigurationKey))
  224. {
  225. odometryFrame = DefaultGlobalFrameName;
  226. }
  227. }
  228. // check if name is rosified
  229. AZStd::string odometryFrameRosified;
  230. ROS2NamesRequestBus::BroadcastResult(odometryFrameRosified, &ROS2NamesRequests::RosifyName, odometryFrame);
  231. AZ_Warning(
  232. "GetGlobalFrameIDFromRegistry",
  233. odometryFrameRosified == odometryFrame,
  234. "Odometry frame name '%s' was not a valid ROS2 name, using rosified version '%s' instead",
  235. odometryFrame.c_str(),
  236. odometryFrameRosified.c_str());
  237. return odometryFrameRosified;
  238. }
  239. } // namespace ROS2