Nav2 Navigation Stack - rolling  main
ROS 2 Navigation Stack
wait_behavior_tester.cpp
1 // Copyright (c) 2020 Samsung Research
2 // Copyright (c) 2020 Sarthak Mittal
3 // Copyright (c) 2018 Intel Corporation
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License. Reserved.
16 
17 #include <string>
18 #include <random>
19 #include <tuple>
20 #include <memory>
21 #include <iostream>
22 #include <chrono>
23 #include <sstream>
24 #include <iomanip>
25 
26 #include "wait_behavior_tester.hpp"
27 
28 using namespace std::chrono_literals;
29 using namespace std::chrono; // NOLINT
30 
31 namespace nav2_system_tests
32 {
33 
34 WaitBehaviorTester::WaitBehaviorTester()
35 : is_active_(false),
36  initial_pose_received_(false)
37 {
38  node_ = rclcpp::Node::make_shared("wait_behavior_test");
39 
40  tf_buffer_ = std::make_shared<tf2_ros::Buffer>(node_->get_clock());
41  tf_listener_ = std::make_shared<tf2_ros::TransformListener>(*tf_buffer_);
42 
43  client_ptr_ = rclcpp_action::create_client<Wait>(
44  node_->get_node_base_interface(),
45  node_->get_node_graph_interface(),
46  node_->get_node_logging_interface(),
47  node_->get_node_waitables_interface(),
48  "wait");
49 
50  publisher_ =
51  node_->create_publisher<geometry_msgs::msg::PoseWithCovarianceStamped>("initialpose", 10);
52 
53  subscription_ = node_->create_subscription<geometry_msgs::msg::PoseWithCovarianceStamped>(
54  "amcl_pose", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable(),
55  std::bind(&WaitBehaviorTester::amclPoseCallback, this, std::placeholders::_1));
56 }
57 
58 WaitBehaviorTester::~WaitBehaviorTester()
59 {
60  if (is_active_) {
61  deactivate();
62  }
63 }
64 
65 void WaitBehaviorTester::activate()
66 {
67  if (is_active_) {
68  throw std::runtime_error("Trying to activate while already active");
69  return;
70  }
71 
72  while (!initial_pose_received_) {
73  RCLCPP_WARN(node_->get_logger(), "Initial pose not received");
74  sendInitialPose();
75  std::this_thread::sleep_for(100ms);
76  rclcpp::spin_some(node_);
77  }
78 
79  // Wait for lifecycle_manager_navigation to activate behavior_server
80  std::this_thread::sleep_for(10s);
81 
82  if (!client_ptr_) {
83  RCLCPP_ERROR(node_->get_logger(), "Action client not initialized");
84  is_active_ = false;
85  return;
86  }
87 
88  if (!client_ptr_->wait_for_action_server(10s)) {
89  RCLCPP_ERROR(node_->get_logger(), "Action server not available after waiting");
90  is_active_ = false;
91  return;
92  }
93 
94  RCLCPP_INFO(this->node_->get_logger(), "Wait action server is ready");
95  is_active_ = true;
96 }
97 
98 void WaitBehaviorTester::deactivate()
99 {
100  if (!is_active_) {
101  throw std::runtime_error("Trying to deactivate while already inactive");
102  }
103  is_active_ = false;
104 }
105 
106 bool WaitBehaviorTester::behaviorTest(
107  const float wait_time)
108 {
109  if (!is_active_) {
110  RCLCPP_ERROR(node_->get_logger(), "Not activated");
111  return false;
112  }
113 
114  // Sleep to let behavior server be ready for serving in multiple runs
115  std::this_thread::sleep_for(5s);
116 
117  auto start_time = node_->now();
118  auto goal_msg = Wait::Goal();
119  goal_msg.time = rclcpp::Duration(wait_time, 0.0);
120 
121  RCLCPP_INFO(this->node_->get_logger(), "Sending goal");
122 
123  auto goal_handle_future = client_ptr_->async_send_goal(goal_msg);
124 
125  if (rclcpp::spin_until_future_complete(node_, goal_handle_future) !=
126  rclcpp::FutureReturnCode::SUCCESS)
127  {
128  RCLCPP_ERROR(node_->get_logger(), "send goal call failed :(");
129  return false;
130  }
131 
132  rclcpp_action::ClientGoalHandle<Wait>::SharedPtr goal_handle = goal_handle_future.get();
133  if (!goal_handle) {
134  RCLCPP_ERROR(node_->get_logger(), "Goal was rejected by server");
135  return false;
136  }
137 
138  // Wait for the server to be done with the goal
139  auto result_future = client_ptr_->async_get_result(goal_handle);
140 
141  RCLCPP_INFO(node_->get_logger(), "Waiting for result");
142  if (rclcpp::spin_until_future_complete(node_, result_future) !=
143  rclcpp::FutureReturnCode::SUCCESS)
144  {
145  RCLCPP_ERROR(node_->get_logger(), "get result call failed :(");
146  return false;
147  }
148 
149  rclcpp_action::ClientGoalHandle<Wait>::WrappedResult wrapped_result = result_future.get();
150 
151  switch (wrapped_result.code) {
152  case rclcpp_action::ResultCode::SUCCEEDED: break;
153  case rclcpp_action::ResultCode::ABORTED: RCLCPP_ERROR(
154  node_->get_logger(),
155  "Goal was aborted");
156  return false;
157  case rclcpp_action::ResultCode::CANCELED: RCLCPP_ERROR(
158  node_->get_logger(),
159  "Goal was canceled");
160  return false;
161  default: RCLCPP_ERROR(node_->get_logger(), "Unknown result code");
162  return false;
163  }
164 
165  RCLCPP_INFO(node_->get_logger(), "result received");
166 
167  if ((node_->now() - start_time).seconds() < static_cast<double>(wait_time)) {
168  return false;
169  }
170 
171  return true;
172 }
173 
174 bool WaitBehaviorTester::behaviorTestCancel(
175  const float wait_time)
176 {
177  if (!is_active_) {
178  RCLCPP_ERROR(node_->get_logger(), "Not activated");
179  return false;
180  }
181 
182  // Sleep to let behavior server be ready for serving in multiple runs
183  std::this_thread::sleep_for(5s);
184 
185  auto start_time = node_->now();
186  auto goal_msg = Wait::Goal();
187  goal_msg.time = rclcpp::Duration(wait_time, 0.0);
188 
189  RCLCPP_INFO(this->node_->get_logger(), "Sending goal");
190 
191  auto goal_handle_future = client_ptr_->async_send_goal(goal_msg);
192 
193  if (rclcpp::spin_until_future_complete(node_, goal_handle_future) !=
194  rclcpp::FutureReturnCode::SUCCESS)
195  {
196  RCLCPP_ERROR(node_->get_logger(), "send goal call failed :(");
197  return false;
198  }
199 
200  rclcpp_action::ClientGoalHandle<Wait>::SharedPtr goal_handle = goal_handle_future.get();
201  if (!goal_handle) {
202  RCLCPP_ERROR(node_->get_logger(), "Goal was rejected by server");
203  return false;
204  }
205 
206  // Wait for the server to be done with the goal
207  auto result_future = client_ptr_->async_cancel_all_goals();
208 
209  RCLCPP_INFO(node_->get_logger(), "Waiting for cancellation");
210  if (rclcpp::spin_until_future_complete(node_, result_future) !=
211  rclcpp::FutureReturnCode::SUCCESS)
212  {
213  RCLCPP_ERROR(node_->get_logger(), "get cancel result call failed :(");
214  return false;
215  }
216 
217  auto status = goal_handle_future.get()->get_status();
218 
219  switch (status) {
220  case rclcpp_action::GoalStatus::STATUS_SUCCEEDED: RCLCPP_ERROR(
221  node_->get_logger(),
222  "Goal succeeded");
223  return false;
224  case rclcpp_action::GoalStatus::STATUS_ABORTED: RCLCPP_ERROR(
225  node_->get_logger(),
226  "Goal was aborted");
227  return false;
228  case rclcpp_action::GoalStatus::STATUS_CANCELED: RCLCPP_INFO(
229  node_->get_logger(),
230  "Goal was canceled");
231  return true;
232  case rclcpp_action::GoalStatus::STATUS_CANCELING: RCLCPP_INFO(
233  node_->get_logger(),
234  "Goal is cancelling");
235  return true;
236  case rclcpp_action::GoalStatus::STATUS_EXECUTING: RCLCPP_ERROR(
237  node_->get_logger(),
238  "Goal is executing");
239  return false;
240  case rclcpp_action::GoalStatus::STATUS_ACCEPTED: RCLCPP_ERROR(
241  node_->get_logger(),
242  "Goal is processing");
243  return false;
244  default: RCLCPP_ERROR(node_->get_logger(), "Unknown result code");
245  return false;
246  }
247 
248  return false;
249 }
250 
251 void WaitBehaviorTester::sendInitialPose()
252 {
253  geometry_msgs::msg::PoseWithCovarianceStamped pose;
254  pose.header.frame_id = "map";
255  pose.header.stamp = rclcpp::Time();
256  pose.pose.pose.position.x = -2.0;
257  pose.pose.pose.position.y = -0.5;
258  pose.pose.pose.position.z = 0.0;
259  pose.pose.pose.orientation.x = 0.0;
260  pose.pose.pose.orientation.y = 0.0;
261  pose.pose.pose.orientation.z = 0.0;
262  pose.pose.pose.orientation.w = 1.0;
263  for (int i = 0; i < 35; i++) {
264  pose.pose.covariance[i] = 0.0;
265  }
266  pose.pose.covariance[0] = 0.08;
267  pose.pose.covariance[7] = 0.08;
268  pose.pose.covariance[35] = 0.05;
269 
270  publisher_->publish(pose);
271  RCLCPP_INFO(node_->get_logger(), "Sent initial pose");
272 }
273 
274 void WaitBehaviorTester::amclPoseCallback(
275  const geometry_msgs::msg::PoseWithCovarianceStamped::SharedPtr)
276 {
277  initial_pose_received_ = true;
278 }
279 
280 } // namespace nav2_system_tests