ROS2SpawnerComponent.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  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 "ROS2SpawnerComponent.h"
  9. #include "Spawner/ROS2SpawnerComponentController.h"
  10. #include <AzCore/Component/EntityId.h>
  11. #include <AzCore/Math/Quaternion.h>
  12. #include <AzCore/Serialization/EditContext.h>
  13. #include <AzCore/Serialization/SerializeContext.h>
  14. #include <AzCore/std/string/conversions.h>
  15. #include <AzCore/std/string/string.h>
  16. #include <AzFramework/Spawnable/Spawnable.h>
  17. #include <Georeferencing/GeoreferenceBus.h>
  18. #include <ROS2/Frame/ROS2FrameComponent.h>
  19. #include <ROS2/ROS2Bus.h>
  20. #include <ROS2/ROS2NamesBus.h>
  21. #include <ROS2/Spawner/SpawnerBus.h>
  22. #include <ROS2/Spawner/SpawnerBusHandler.h>
  23. #include <ROS2/Utilities/ROS2Conversions.h>
  24. namespace ROS2
  25. {
  26. ROS2SpawnerComponent::ROS2SpawnerComponent(const ROS2SpawnerComponentConfig& properties)
  27. : ROS2SpawnerComponentBase(properties)
  28. {
  29. }
  30. void ROS2SpawnerComponent::Activate()
  31. {
  32. ROS2SpawnerComponentBase::Activate();
  33. auto ros2Node = ROS2Interface::Get()->GetNode();
  34. AZ_Assert(ros2Node, "ROS 2 node is not initialized");
  35. const auto serviceNames = m_controller.GetServiceNames();
  36. m_getSpawnablesNamesService = ros2Node->create_service<gazebo_msgs::srv::GetWorldProperties>(
  37. serviceNames.m_availableSpawnableNamesServiceName.c_str(),
  38. [this](const GetAvailableSpawnableNamesRequest request, GetAvailableSpawnableNamesResponse response)
  39. {
  40. GetAvailableSpawnableNames(request, response);
  41. });
  42. m_spawnService = ros2Node->create_service<gazebo_msgs::srv::SpawnEntity>(
  43. serviceNames.m_spawnEntityServiceName.c_str(),
  44. [this](
  45. const SpawnEntityServiceHandle service_handle,
  46. const std::shared_ptr<rmw_request_id_t> header,
  47. const SpawnEntityRequest request)
  48. {
  49. SpawnEntity(service_handle, header, request);
  50. });
  51. m_deleteService = ros2Node->create_service<gazebo_msgs::srv::DeleteEntity>(
  52. serviceNames.m_deleteEntityServiceName.c_str(),
  53. [this](
  54. const DeleteEntityServiceHandle service_handle, const std::shared_ptr<rmw_request_id_t> header, DeleteEntityRequest request)
  55. {
  56. DeleteEntity(service_handle, header, request);
  57. });
  58. m_getSpawnPointInfoService = ros2Node->create_service<gazebo_msgs::srv::GetModelState>(
  59. serviceNames.m_spawnPointInfoServiceName.c_str(),
  60. [this](const GetSpawnPointInfoRequest request, GetSpawnPointInfoResponse response)
  61. {
  62. GetSpawnPointInfo(request, response);
  63. });
  64. m_getSpawnPointsNamesService = ros2Node->create_service<gazebo_msgs::srv::GetWorldProperties>(
  65. serviceNames.m_spawnPointsNamesServiceName.c_str(),
  66. [this](const GetSpawnPointsNamesRequest request, GetSpawnPointsNamesResponse response)
  67. {
  68. GetSpawnPointsNames(request, response);
  69. });
  70. }
  71. void ROS2SpawnerComponent::Deactivate()
  72. {
  73. ROS2SpawnerComponentBase::Deactivate();
  74. m_getSpawnablesNamesService.reset();
  75. m_spawnService.reset();
  76. m_deleteService.reset();
  77. m_getSpawnPointInfoService.reset();
  78. m_getSpawnPointsNamesService.reset();
  79. m_tickets.clear();
  80. }
  81. void ROS2SpawnerComponent::Reflect(AZ::ReflectContext* context)
  82. {
  83. ROS2SpawnerComponentBase::Reflect(context);
  84. if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
  85. {
  86. serialize->Class<ROS2SpawnerComponent, ROS2SpawnerComponentBase>()->Version(1);
  87. }
  88. if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
  89. {
  90. behaviorContext->EBus<SpawnerNotificationBus>("ROS2SpawnerNotificationBus")->Handler<SpawnerNotificationsBusHandler>();
  91. }
  92. }
  93. void ROS2SpawnerComponent::GetAvailableSpawnableNames(
  94. const GetAvailableSpawnableNamesRequest request, GetAvailableSpawnableNamesResponse response)
  95. {
  96. for (const auto& spawnable : m_controller.GetSpawnables())
  97. {
  98. response->model_names.emplace_back(spawnable.first.c_str());
  99. }
  100. response->success = true;
  101. }
  102. void ROS2SpawnerComponent::SpawnEntity(
  103. const SpawnEntityServiceHandle service_handle, const std::shared_ptr<rmw_request_id_t> header, const SpawnEntityRequest request)
  104. {
  105. AZStd::string referenceFrame(request->reference_frame.c_str());
  106. const bool isWGS{ referenceFrame == "wgs84" && m_controller.GetSupportWGS() };
  107. SpawnEntityResponse response;
  108. if (isWGS && !Georeferencing::GeoreferenceRequestsBus::HasHandlers())
  109. {
  110. response.success = false;
  111. response.status_message = "Level is not geographically positioned. Action aborted.";
  112. service_handle->send_response(*header, response);
  113. return;
  114. }
  115. AZ::Quaternion rotation(
  116. request->initial_pose.orientation.x,
  117. request->initial_pose.orientation.y,
  118. request->initial_pose.orientation.z,
  119. request->initial_pose.orientation.w);
  120. if (rotation.IsZero())
  121. {
  122. response.success = false;
  123. response.status_message = "Rotation is undefined. Action aborted.";
  124. service_handle->send_response(*header, response);
  125. return;
  126. }
  127. AZStd::string spawnableName(request->name.c_str());
  128. AZStd::string spawnableNamespace(request->robot_namespace.c_str());
  129. AZStd::string spawnPointName(request->xml.c_str(), request->xml.size());
  130. AZ::Outcome<void, AZStd::string> namespaceValidation = AZ::Failure("Cannot validate namespace, ROS2NamesBus is not available");
  131. ROS2NamesRequestBus::BroadcastResult(namespaceValidation, &ROS2NamesRequestBus::Events::ValidateNamespace, spawnableNamespace);
  132. if (!namespaceValidation.IsSuccess())
  133. {
  134. response.success = false;
  135. response.status_message = namespaceValidation.GetError().data();
  136. service_handle->send_response(*header, response);
  137. return;
  138. }
  139. if (!m_controller.GetSpawnables().contains(spawnableName))
  140. {
  141. response.success = false;
  142. response.status_message = "Could not find spawnable with given name: " + request->name;
  143. service_handle->send_response(*header, response);
  144. return;
  145. }
  146. auto spawnable = m_controller.GetSpawnables().find(spawnableName);
  147. if (spawnable->second->IsLoading())
  148. {
  149. // This is an Editor only situation. All assets during game mode are fully loaded.
  150. response.success = false;
  151. response.status_message = "Asset for spawnable " + request->name + " has not yet loaded.";
  152. service_handle->send_response(*header, response);
  153. return;
  154. }
  155. if (spawnable->second->IsError())
  156. {
  157. response.success = false;
  158. response.status_message = "Spawnable " + request->name + " loaded with an error.";
  159. service_handle->send_response(*header, response);
  160. return;
  161. }
  162. if (!m_tickets.contains(spawnableName))
  163. {
  164. // if a ticket for this spawnable was not created but the spawnable name is correct, create the ticket and then use it to
  165. // spawn an entity
  166. m_tickets.emplace(spawnable->first, AzFramework::EntitySpawnTicket(spawnable->second));
  167. }
  168. auto spawnableTicket = AzFramework::EntitySpawnTicket(spawnable->second);
  169. auto ticketId = spawnableTicket.GetId();
  170. AZStd::string ticketName = spawnable->first + "_" + AZStd::to_string(ticketId);
  171. m_tickets.emplace(ticketName, AZStd::move(spawnableTicket));
  172. auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get();
  173. AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
  174. AZ::Transform transform;
  175. if (isWGS)
  176. {
  177. Georeferencing::WGS::WGS84Coordinate coordinate;
  178. AZ::Vector3 coordinateInLevel = AZ::Vector3(-1);
  179. AZ::Quaternion rotationInENU = AZ::Quaternion::CreateIdentity();
  180. coordinate.m_latitude = request->initial_pose.position.x;
  181. coordinate.m_longitude = request->initial_pose.position.y;
  182. coordinate.m_altitude = request->initial_pose.position.z;
  183. Georeferencing::GeoreferenceRequestsBus::BroadcastResult(
  184. rotationInENU, &Georeferencing::GeoreferenceRequests::GetRotationFromLevelToENU);
  185. Georeferencing::GeoreferenceRequestsBus::BroadcastResult(
  186. coordinateInLevel, &Georeferencing::GeoreferenceRequests::ConvertFromWGS84ToLevel, coordinate);
  187. rotationInENU = (rotationInENU.GetInverseFast() *
  188. AZ::Quaternion(
  189. request->initial_pose.orientation.x,
  190. request->initial_pose.orientation.y,
  191. request->initial_pose.orientation.z,
  192. request->initial_pose.orientation.w))
  193. .GetNormalized();
  194. transform = { coordinateInLevel, rotationInENU, 1.0f };
  195. }
  196. else
  197. {
  198. if (auto spawnPoints = GetSpawnPoints(); spawnPoints.contains(spawnPointName))
  199. {
  200. transform = spawnPoints.at(spawnPointName).pose;
  201. }
  202. else
  203. {
  204. transform = { AZ::Vector3(
  205. request->initial_pose.position.x, request->initial_pose.position.y, request->initial_pose.position.z),
  206. AZ::Quaternion(
  207. request->initial_pose.orientation.x,
  208. request->initial_pose.orientation.y,
  209. request->initial_pose.orientation.z,
  210. request->initial_pose.orientation.w)
  211. .GetNormalized(),
  212. 1.0f };
  213. }
  214. }
  215. optionalArgs.m_preInsertionCallback = [this, transform, spawnableName, spawnableNamespace](auto id, auto view)
  216. {
  217. PreSpawn(id, view, transform, spawnableName, spawnableNamespace);
  218. };
  219. optionalArgs.m_completionCallback =
  220. [service_handle, header, ticketName, spawnableName, parentId = GetEntityId()](auto id, auto view)
  221. {
  222. AZ::EntityId rootEntityId;
  223. if (!view.empty())
  224. {
  225. const AZ::Entity* root = *view.begin();
  226. auto* transformInterface = root->FindComponent<AzFramework::TransformComponent>();
  227. transformInterface->SetParent(parentId);
  228. rootEntityId = root->GetId();
  229. }
  230. SpawnEntityResponse response;
  231. response.success = true;
  232. response.status_message = ticketName.c_str();
  233. service_handle->send_response(*header, response);
  234. SpawnerNotificationBus::Broadcast(&SpawnerNotificationBus::Events::OnSpawned, spawnableName, rootEntityId, ticketName);
  235. };
  236. spawner->SpawnAllEntities(m_tickets.at(ticketName), optionalArgs);
  237. }
  238. void ROS2SpawnerComponent::PreSpawn(
  239. AzFramework::EntitySpawnTicket::Id id [[maybe_unused]],
  240. AzFramework::SpawnableEntityContainerView view,
  241. const AZ::Transform& transform,
  242. const AZStd::string& spawnableName,
  243. const AZStd::string& spawnableNamespace)
  244. {
  245. if (view.empty())
  246. {
  247. return;
  248. }
  249. AZ::Entity* root = *view.begin();
  250. auto* transformInterface = root->FindComponent<AzFramework::TransformComponent>();
  251. transformInterface->SetWorldTM(transform);
  252. AZStd::string instanceName = AZStd::string::format("%s_%d", spawnableName.c_str(), m_counter++);
  253. for (AZ::Entity* entity : view)
  254. { // Update name for the first entity with ROS2Frame in hierarchy (left to right)
  255. auto* frameComponent = entity->FindComponent<ROS2FrameComponent>();
  256. if (frameComponent)
  257. {
  258. entity->SetName(instanceName);
  259. if (!spawnableNamespace.empty())
  260. {
  261. auto configuration = frameComponent->GetConfiguration();
  262. configuration.m_namespaceConfiguration.m_customNamespace = spawnableNamespace;
  263. configuration.m_namespaceConfiguration.m_namespaceStrategy = ROS2::NamespaceConfiguration::NamespaceStrategy::Custom;
  264. frameComponent->SetConfiguration(configuration);
  265. }
  266. break;
  267. }
  268. }
  269. }
  270. void ROS2SpawnerComponent::DeleteEntity(
  271. const DeleteEntityServiceHandle service_handle, const std::shared_ptr<rmw_request_id_t> header, DeleteEntityRequest request)
  272. {
  273. auto deleteName = AZStd::string(request->name.c_str());
  274. if (!m_tickets.contains(deleteName))
  275. {
  276. DeleteEntityResponse response;
  277. response.success = false;
  278. response.status_message = "Could not find entity with given name: " + request->name;
  279. service_handle->send_response(*header, response);
  280. return;
  281. }
  282. auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get();
  283. AzFramework::DespawnAllEntitiesOptionalArgs optionalArgs;
  284. optionalArgs.m_completionCallback = [service_handle, header, deleteName](auto id)
  285. {
  286. DeleteEntityResponse response;
  287. response.success = true;
  288. service_handle->send_response(*header, response);
  289. SpawnerNotificationBus::Broadcast(&SpawnerNotificationBus::Events::OnDespawned, deleteName);
  290. };
  291. spawner->DespawnAllEntities(m_tickets.at(deleteName), optionalArgs);
  292. m_tickets.erase(deleteName);
  293. }
  294. void ROS2SpawnerComponent::GetSpawnPointsNames(
  295. const ROS2::GetSpawnPointsNamesRequest request, ROS2::GetSpawnPointsNamesResponse response)
  296. {
  297. for (auto spawnPoint : GetSpawnPoints())
  298. {
  299. response->model_names.emplace_back(spawnPoint.first.c_str());
  300. }
  301. }
  302. void ROS2SpawnerComponent::GetSpawnPointInfo(const ROS2::GetSpawnPointInfoRequest request, ROS2::GetSpawnPointInfoResponse response)
  303. {
  304. const AZStd::string_view key(request->model_name.c_str(), request->model_name.size());
  305. auto spawnPoints = GetSpawnPoints();
  306. if (spawnPoints.contains(key))
  307. {
  308. auto info = spawnPoints.at(key);
  309. response->pose = ROS2Conversions::ToROS2Pose(info.pose);
  310. response->status_message = info.info.c_str();
  311. }
  312. else
  313. {
  314. response->status_message = "Could not find spawn point with given name: " + request->model_name;
  315. }
  316. }
  317. AZStd::unordered_map<AZStd::string, SpawnPointInfo> ROS2SpawnerComponent::GetSpawnPoints()
  318. {
  319. return m_controller.GetSpawnPoints();
  320. }
  321. } // namespace ROS2