ROS2SpawnerComponent.cpp 15 KB

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