2
0

ROS2SpawnerComponent.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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/Spawner/SpawnerBus.h>
  21. #include <ROS2/Spawner/SpawnerBusHandler.h>
  22. #include <ROS2/Utilities/ROS2Conversions.h>
  23. #include <ROS2/Utilities/ROS2Names.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. if (auto namespaceValidation = ROS2Names::ValidateNamespace(spawnableNamespace); !namespaceValidation.IsSuccess())
  131. {
  132. response.success = false;
  133. response.status_message = namespaceValidation.GetError().data();
  134. service_handle->send_response(*header, response);
  135. return;
  136. }
  137. if (!m_controller.GetSpawnables().contains(spawnableName))
  138. {
  139. response.success = false;
  140. response.status_message = "Could not find spawnable with given name: " + request->name;
  141. service_handle->send_response(*header, response);
  142. return;
  143. }
  144. auto spawnable = m_controller.GetSpawnables().find(spawnableName);
  145. if (spawnable->second->IsLoading())
  146. {
  147. // This is an Editor only situation. All assets during game mode are fully loaded.
  148. response.success = false;
  149. response.status_message = "Asset for spawnable " + request->name + " has not yet loaded.";
  150. service_handle->send_response(*header, response);
  151. return;
  152. }
  153. if (spawnable->second->IsError())
  154. {
  155. response.success = false;
  156. response.status_message = "Spawnable " + request->name + " loaded with an error.";
  157. service_handle->send_response(*header, response);
  158. return;
  159. }
  160. if (!m_tickets.contains(spawnableName))
  161. {
  162. // if a ticket for this spawnable was not created but the spawnable name is correct, create the ticket and then use it to
  163. // spawn an entity
  164. m_tickets.emplace(spawnable->first, AzFramework::EntitySpawnTicket(spawnable->second));
  165. }
  166. auto spawnableTicket = AzFramework::EntitySpawnTicket(spawnable->second);
  167. auto ticketId = spawnableTicket.GetId();
  168. AZStd::string ticketName = spawnable->first + "_" + AZStd::to_string(ticketId);
  169. m_tickets.emplace(ticketName, AZStd::move(spawnableTicket));
  170. auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get();
  171. AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
  172. AZ::Transform transform;
  173. if (isWGS)
  174. {
  175. Georeferencing::WGS::WGS84Coordinate coordinate;
  176. AZ::Vector3 coordinateInLevel = AZ::Vector3(-1);
  177. AZ::Quaternion rotationInENU = AZ::Quaternion::CreateIdentity();
  178. coordinate.m_latitude = request->initial_pose.position.x;
  179. coordinate.m_longitude = request->initial_pose.position.y;
  180. coordinate.m_altitude = request->initial_pose.position.z;
  181. Georeferencing::GeoreferenceRequestsBus::BroadcastResult(
  182. rotationInENU, &Georeferencing::GeoreferenceRequests::GetRotationFromLevelToENU);
  183. Georeferencing::GeoreferenceRequestsBus::BroadcastResult(
  184. coordinateInLevel, &Georeferencing::GeoreferenceRequests::ConvertFromWGS84ToLevel, coordinate);
  185. rotationInENU = (rotationInENU.GetInverseFast() *
  186. AZ::Quaternion(
  187. request->initial_pose.orientation.x,
  188. request->initial_pose.orientation.y,
  189. request->initial_pose.orientation.z,
  190. request->initial_pose.orientation.w))
  191. .GetNormalized();
  192. transform = { coordinateInLevel, rotationInENU, 1.0f };
  193. }
  194. else
  195. {
  196. if (auto spawnPoints = GetSpawnPoints(); spawnPoints.contains(spawnPointName))
  197. {
  198. transform = spawnPoints.at(spawnPointName).pose;
  199. }
  200. else
  201. {
  202. transform = { AZ::Vector3(
  203. request->initial_pose.position.x, request->initial_pose.position.y, request->initial_pose.position.z),
  204. AZ::Quaternion(
  205. request->initial_pose.orientation.x,
  206. request->initial_pose.orientation.y,
  207. request->initial_pose.orientation.z,
  208. request->initial_pose.orientation.w)
  209. .GetNormalized(),
  210. 1.0f };
  211. }
  212. }
  213. optionalArgs.m_preInsertionCallback = [this, transform, spawnableName, spawnableNamespace](auto id, auto view)
  214. {
  215. PreSpawn(id, view, transform, spawnableName, spawnableNamespace);
  216. };
  217. optionalArgs.m_completionCallback =
  218. [service_handle, header, ticketName, spawnableName, parentId = GetEntityId()](auto id, auto view)
  219. {
  220. AZ::EntityId rootEntityId;
  221. if (!view.empty())
  222. {
  223. const AZ::Entity* root = *view.begin();
  224. auto* transformInterface = root->FindComponent<AzFramework::TransformComponent>();
  225. transformInterface->SetParent(parentId);
  226. rootEntityId = root->GetId();
  227. }
  228. SpawnEntityResponse response;
  229. response.success = true;
  230. response.status_message = ticketName.c_str();
  231. service_handle->send_response(*header, response);
  232. SpawnerNotificationBus::Broadcast(&SpawnerNotificationBus::Events::OnSpawned, spawnableName, rootEntityId, ticketName);
  233. };
  234. spawner->SpawnAllEntities(m_tickets.at(ticketName), optionalArgs);
  235. }
  236. void ROS2SpawnerComponent::PreSpawn(
  237. AzFramework::EntitySpawnTicket::Id id [[maybe_unused]],
  238. AzFramework::SpawnableEntityContainerView view,
  239. const AZ::Transform& transform,
  240. const AZStd::string& spawnableName,
  241. const AZStd::string& spawnableNamespace)
  242. {
  243. if (view.empty())
  244. {
  245. return;
  246. }
  247. AZ::Entity* root = *view.begin();
  248. auto* transformInterface = root->FindComponent<AzFramework::TransformComponent>();
  249. transformInterface->SetWorldTM(transform);
  250. AZStd::string instanceName = AZStd::string::format("%s_%d", spawnableName.c_str(), m_counter++);
  251. for (AZ::Entity* entity : view)
  252. { // Update name for the first entity with ROS2Frame in hierarchy (left to right)
  253. auto* frameComponent = entity->FindComponent<ROS2FrameComponent>();
  254. if (frameComponent)
  255. {
  256. entity->SetName(instanceName);
  257. if (!spawnableNamespace.empty())
  258. {
  259. frameComponent->UpdateNamespaceConfiguration(spawnableNamespace, NamespaceConfiguration::NamespaceStrategy::Custom);
  260. }
  261. break;
  262. }
  263. }
  264. }
  265. void ROS2SpawnerComponent::DeleteEntity(
  266. const DeleteEntityServiceHandle service_handle, const std::shared_ptr<rmw_request_id_t> header, DeleteEntityRequest request)
  267. {
  268. auto deleteName = AZStd::string(request->name.c_str());
  269. if (!m_tickets.contains(deleteName))
  270. {
  271. DeleteEntityResponse response;
  272. response.success = false;
  273. response.status_message = "Could not find entity with given name: " + request->name;
  274. service_handle->send_response(*header, response);
  275. return;
  276. }
  277. auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get();
  278. AzFramework::DespawnAllEntitiesOptionalArgs optionalArgs;
  279. optionalArgs.m_completionCallback = [service_handle, header, deleteName](auto id)
  280. {
  281. DeleteEntityResponse response;
  282. response.success = true;
  283. service_handle->send_response(*header, response);
  284. SpawnerNotificationBus::Broadcast(&SpawnerNotificationBus::Events::OnDespawned, deleteName);
  285. };
  286. spawner->DespawnAllEntities(m_tickets.at(deleteName), optionalArgs);
  287. m_tickets.erase(deleteName);
  288. }
  289. void ROS2SpawnerComponent::GetSpawnPointsNames(
  290. const ROS2::GetSpawnPointsNamesRequest request, ROS2::GetSpawnPointsNamesResponse response)
  291. {
  292. for (auto spawnPoint : GetSpawnPoints())
  293. {
  294. response->model_names.emplace_back(spawnPoint.first.c_str());
  295. }
  296. }
  297. void ROS2SpawnerComponent::GetSpawnPointInfo(const ROS2::GetSpawnPointInfoRequest request, ROS2::GetSpawnPointInfoResponse response)
  298. {
  299. const AZStd::string_view key(request->model_name.c_str(), request->model_name.size());
  300. auto spawnPoints = GetSpawnPoints();
  301. if (spawnPoints.contains(key))
  302. {
  303. auto info = spawnPoints.at(key);
  304. response->pose = ROS2Conversions::ToROS2Pose(info.pose);
  305. response->status_message = info.info.c_str();
  306. }
  307. else
  308. {
  309. response->status_message = "Could not find spawn point with given name: " + request->model_name;
  310. }
  311. }
  312. AZStd::unordered_map<AZStd::string, SpawnPointInfo> ROS2SpawnerComponent::GetSpawnPoints()
  313. {
  314. return m_controller.GetSpawnPoints();
  315. }
  316. } // namespace ROS2