2
0

ROS2SpawnerComponent.cpp 15 KB

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