ROS2SDFormatHooksUtils.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  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 "ROS2SDFormatHooksUtils.h"
  9. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  10. #include <ROS2/Communication/TopicConfiguration.h>
  11. #include <ROS2/Frame/ROS2FrameEditorComponent.h>
  12. #include <ROS2/Utilities/ROS2Names.h>
  13. #include <RobotImporter/Utils/RobotImporterUtils.h>
  14. #include <RobotImporter/Utils/TypeConversions.h>
  15. #include <SdfAssetBuilder/SdfAssetBuilderSettings.h>
  16. #include <VehicleDynamics/WheelControllerComponent.h>
  17. #include <sdf/Element.hh>
  18. #include <sdf/Joint.hh>
  19. namespace ROS2::SDFormat
  20. {
  21. // temporarily disable import hooks for sensors and models for https://github.com/o3de/sig-simulation/pull/96
  22. // void HooksUtils::AddTopicConfiguration(
  23. // SensorConfiguration& sensorConfig, const AZStd::string& topic, const AZStd::string& messageType, const AZStd::string& configName)
  24. // {
  25. // TopicConfiguration config;
  26. // config.m_topic = topic;
  27. // config.m_type = messageType;
  28. // sensorConfig.m_publishersConfigurations.insert(AZStd::make_pair(configName, config));
  29. // }
  30. AZ::EntityId HooksUtils::GetJointEntityId(
  31. const std::string& jointName, const sdf::Model& sdfModel, const CreatedEntitiesMap& createdEntities)
  32. {
  33. const auto jointPtr = sdfModel.JointByName(jointName);
  34. if (jointPtr != nullptr)
  35. {
  36. const auto linkName(jointPtr->ChildName().c_str());
  37. const auto linkPtr = sdfModel.LinkByName(linkName);
  38. if (linkPtr != nullptr && createdEntities.contains(linkPtr))
  39. {
  40. const auto& entityResult = createdEntities.at(linkPtr);
  41. return entityResult.IsSuccess() ? entityResult.GetValue() : AZ::EntityId();
  42. }
  43. }
  44. return AZ::EntityId();
  45. }
  46. void HooksUtils::EnableMotor(const AZ::EntityId& entityId)
  47. {
  48. AZ::Entity* entity = nullptr;
  49. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationRequests::FindEntity, entityId);
  50. if (entity == nullptr)
  51. {
  52. AZ_Warning("HooksUtils", false, "Cannot switch on motor in wheel joint. Entity was not created successfully.");
  53. return;
  54. }
  55. // Enable motor in hinge joint (only if articulations are not enabled)
  56. PhysX::EditorHingeJointComponent* jointComponent = entity->FindComponent<PhysX::EditorHingeJointComponent>();
  57. if (jointComponent != nullptr)
  58. {
  59. entity->Activate();
  60. if (entity->GetState() == AZ::Entity::State::Active)
  61. {
  62. PhysX::EditorJointRequestBus::Event(
  63. AZ::EntityComponentIdPair(entityId, jointComponent->GetId()),
  64. &PhysX::EditorJointRequests::SetBoolValue,
  65. PhysX::JointsComponentModeCommon::ParameterNames::EnableMotor,
  66. true);
  67. entity->Deactivate();
  68. }
  69. }
  70. }
  71. void HooksUtils::SetSensorEntityTransform(AZ::Entity& entity, const sdf::Sensor& sdfSensor)
  72. {
  73. const auto sensorSemanticPose = sdfSensor.SemanticPose();
  74. AZ::Transform tf = Utils::GetLocalTransformURDF(sensorSemanticPose);
  75. auto* transformInterface = entity.FindComponent<AzToolsFramework::Components::TransformComponent>();
  76. if (transformInterface)
  77. {
  78. AZ_Trace(
  79. "CreatePrefabFromUrdfOrSdf",
  80. "Setting transform %s to [%f %f %f] [%f %f %f %f]\n",
  81. sdfSensor.Name().c_str(),
  82. tf.GetTranslation().GetX(),
  83. tf.GetTranslation().GetY(),
  84. tf.GetTranslation().GetZ(),
  85. tf.GetRotation().GetX(),
  86. tf.GetRotation().GetY(),
  87. tf.GetRotation().GetZ(),
  88. tf.GetRotation().GetW());
  89. transformInterface->SetLocalTM(tf);
  90. }
  91. else
  92. {
  93. AZ_Trace(
  94. "CreatePrefabFromUrdfOrSdf", "Setting transform failed: %s does not have transform interface\n", sdfSensor.Name().c_str());
  95. }
  96. }
  97. namespace HooksUtils::PluginParser
  98. {
  99. // Inserts name (key) and value (val) of given parameter to map.
  100. void ParseRegularContent(const sdf::Element& content, HooksUtils::PluginParams& remappings)
  101. {
  102. const AZStd::string contentName = content.GetName().c_str();
  103. const sdf::ParamPtr contentValuePtr = content.GetValue();
  104. if (!contentValuePtr || contentName.empty())
  105. {
  106. AZ_Warning("PluginParser", false, "Encountered invalid (empty) remapping while parsing URDF/SDF plugin.");
  107. return;
  108. }
  109. const AZStd::string contentValue = contentValuePtr->GetAsString().c_str();
  110. if (!contentValue.empty())
  111. {
  112. remappings[contentName] = contentValue;
  113. }
  114. }
  115. // Parses parameters present in ros element, inserting them to the map.
  116. void ParseRos2Remapping(const sdf::Element& rosContent, HooksUtils::PluginParams& remappings)
  117. {
  118. if (rosContent.GetName() != "remapping" && rosContent.GetName() != "argument")
  119. {
  120. // parameter other than remapping or argument can be handled as regular parameter
  121. ParseRegularContent(rosContent, remappings);
  122. return;
  123. }
  124. const sdf::ParamPtr contentValuePtr = rosContent.GetValue();
  125. if (!contentValuePtr)
  126. {
  127. AZ_Warning("PluginParser", false, "ROS 2 content in parsing URDF/SDF plugin data not available.");
  128. return;
  129. }
  130. AZStd::string contentValue = contentValuePtr->GetAsString().c_str();
  131. if (contentValue.find_last_of('=') == AZStd::string::npos || contentValue.find_last_of(':') == AZStd::string::npos)
  132. {
  133. AZ_Warning("PluginParser", false, "Encountered invalid remapping while parsing URDF/SDF plugin.");
  134. return;
  135. }
  136. // get new name of the topic
  137. int startVal = contentValue.find_last_of('=');
  138. if (contentValue.find_last_of('/') != AZStd::string::npos && contentValue.find_last_of('/') > startVal)
  139. {
  140. startVal = contentValue.find_last_of("/");
  141. }
  142. startVal += 1;
  143. if (startVal >= contentValue.size())
  144. {
  145. AZ_Warning("PluginParser", false, "Encountered invalid (empty) remapping while parsing URDF/SDF plugin.");
  146. return;
  147. }
  148. AZStd::string newTopic = contentValue.substr(startVal, contentValue.size() - startVal);
  149. // get previous name of the topic
  150. contentValue = contentValue.substr(0, contentValue.find_first_of(':'));
  151. int startKey = contentValue.find_last_of('/') != std::string::npos ? contentValue.find_last_of('/') + 1 : 0;
  152. if (startKey >= contentValue.size())
  153. {
  154. AZ_Warning("PluginParser", false, "Encountered invalid (empty) remapping while parsing URDF/SDF plugin.");
  155. return;
  156. }
  157. AZStd::string prevTopic = contentValue.substr(startKey, contentValue.size() - startKey);
  158. if (ROS2Names::ValidateTopic(newTopic).IsSuccess() && ROS2Names::ValidateTopic(prevTopic).IsSuccess())
  159. {
  160. remappings[prevTopic] = newTopic;
  161. }
  162. else
  163. {
  164. AZ_Warning("PluginParser", false, "Encountered invalid topic name while parsing URDF/SDF plugin.");
  165. }
  166. }
  167. } // namespace HooksUtils::PluginParser
  168. ROS2FrameConfiguration HooksUtils::GetFrameConfiguration(const HooksUtils::PluginParams& pluginParams)
  169. {
  170. ROS2FrameConfiguration frameConfiguration;
  171. const static AZStd::vector<AZStd::string> namespaceRemapNames = { "robotNamespace", "namespace" };
  172. const AZStd::string remappedNamespace = HooksUtils::ValueOfAny(pluginParams, namespaceRemapNames);
  173. if (!ROS2Names::ValidateNamespace(remappedNamespace).IsSuccess())
  174. {
  175. AZ_Warning("PluginParser", false, "Encountered invalid namespace name while parsing URDF/SDF plugin.");
  176. }
  177. else if (!remappedNamespace.empty())
  178. {
  179. frameConfiguration.m_namespaceConfiguration.SetNamespace(remappedNamespace, NamespaceConfiguration::NamespaceStrategy::Custom);
  180. }
  181. if (pluginParams.contains("frameName"))
  182. {
  183. frameConfiguration.m_frameName = pluginParams.at("frameName");
  184. }
  185. else if (pluginParams.contains("frame_name"))
  186. {
  187. frameConfiguration.m_frameName = pluginParams.at("frame_name");
  188. }
  189. return frameConfiguration;
  190. }
  191. HooksUtils::PluginParams HooksUtils::GetPluginParams(const sdf::Plugins& plugins)
  192. {
  193. HooksUtils::PluginParams remappings;
  194. if (plugins.empty())
  195. {
  196. return remappings;
  197. }
  198. const auto plugin = plugins[0];
  199. for (const auto& content : plugin.Contents())
  200. {
  201. std::string contentName = content->GetName();
  202. if (contentName == "ros")
  203. {
  204. // when ros tag is present, parse it's elements and insert them into the map
  205. auto rosContent = content->GetFirstElement();
  206. while (rosContent != nullptr)
  207. {
  208. PluginParser::ParseRos2Remapping(*rosContent, remappings);
  209. rosContent = rosContent->GetNextElement();
  210. }
  211. }
  212. else
  213. {
  214. PluginParser::ParseRegularContent(*content, remappings);
  215. }
  216. }
  217. return remappings;
  218. }
  219. AZStd::string HooksUtils::ValueOfAny(
  220. const HooksUtils::PluginParams& pluginParams, const AZStd::vector<AZStd::string>& paramNames, const AZStd::string& defaultVal)
  221. {
  222. for (const auto& paramName : paramNames)
  223. {
  224. if (pluginParams.contains(paramName))
  225. {
  226. const AZStd::string paramValue = pluginParams.at(paramName);
  227. return AZ::IO::PathView(paramValue).Filename().String();
  228. }
  229. }
  230. return defaultVal;
  231. }
  232. AZStd::string HooksUtils::GetTopicName(const PluginParams& pluginParams, sdf::ElementPtr element, const AZStd::string& defaultVal)
  233. {
  234. if (element->HasElement("topic"))
  235. {
  236. return element->Get<std::string>("topic").c_str();
  237. }
  238. const static AZStd::vector<AZStd::string> remapParamNames = { "topicName", "out" };
  239. return ValueOfAny(pluginParams, remapParamNames, defaultVal);
  240. }
  241. float HooksUtils::GetFrequency(const PluginParams& pluginParams, const float defaultVal)
  242. {
  243. const static AZStd::vector<AZStd::string> frequencyParamNames = { "updateRate", "update_rate" };
  244. return AZStd::stof(ValueOfAny(pluginParams, frequencyParamNames, AZStd::to_string(defaultVal)));
  245. }
  246. } // namespace ROS2::SDFormat