Nav2 Navigation Stack - humble  humble
ROS 2 Navigation Stack
route_operation_client.hpp
1 // Copyright (c) 2025 Open Navigation LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef NAV2_ROUTE__PLUGINS__ROUTE_OPERATION_CLIENT_HPP_
16 #define NAV2_ROUTE__PLUGINS__ROUTE_OPERATION_CLIENT_HPP_
17 
18 #include <memory>
19 #include <chrono>
20 #include <string>
21 
22 #include "rclcpp/rclcpp.hpp"
23 #include "rclcpp_lifecycle/lifecycle_node.hpp"
24 #include "nav2_route/interfaces/route_operation.hpp"
25 #include "nav2_core/route_exceptions.hpp"
26 #include "nav2_util/node_utils.hpp"
27 #include "nav2_util/service_client.hpp"
28 #include "std_srvs/srv/trigger.hpp"
29 
30 namespace nav2_route
31 {
32 
33 using namespace std::chrono_literals; // NOLINT
34 
61 template<typename SrvT>
63 {
64 public:
68  RouteOperationClient() = default;
69 
73  virtual ~RouteOperationClient() = default;
74 
80  virtual void configureEvent(
81  const rclcpp_lifecycle::LifecycleNode::SharedPtr /*node*/,
82  const std::string & /*name*/) {}
83 
87  virtual void populateRequest(
88  std::shared_ptr<typename SrvT::Request>/*request*/, const Metadata * /*mdata*/) {}
89 
94  std::shared_ptr<typename SrvT::Response>/*response*/) {return OperationResult();}
95 
96 protected:
100  void configure(
101  const rclcpp_lifecycle::LifecycleNode::SharedPtr node,
102  std::shared_ptr<nav2_costmap_2d::CostmapSubscriber>,
103  const std::string & name) final
104  {
105  RCLCPP_INFO(node->get_logger(), "Configuring route operation client: %s.", name.c_str());
106  name_ = name;
107  logger_ = node->get_logger();
108  node_ = node;
109  callback_group_ = node->create_callback_group(
110  rclcpp::CallbackGroupType::MutuallyExclusive,
111  false);
112  callback_group_executor_.add_callback_group(callback_group_, node->get_node_base_interface());
113 
114  nav2_util::declare_parameter_if_not_declared(
115  node, getName() + ".service_name", rclcpp::ParameterValue(""));
116  main_srv_name_ = node->get_parameter(getName() + ".service_name").as_string();
117 
118  configureEvent(node, name);
119 
120  // There exists a single central service to use, create client.
121  // If this is set to empty string after configuration, then the individual nodes will
122  // indicate the endpoint for the particular service call.
123  if (!main_srv_name_.empty()) {
124  main_client_ = node->create_client<SrvT>(
125  main_srv_name_, rclcpp::SystemDefaultsQoS().get_rmw_qos_profile(), callback_group_);
126  }
127  }
128 
141  NodePtr node_achieved,
142  EdgePtr /*edge_entered*/,
143  EdgePtr /*edge_exited*/,
144  const Route & /*route*/,
145  const geometry_msgs::msg::PoseStamped & /*curr_pose*/,
146  const Metadata * mdata) final
147  {
148  auto req = std::make_shared<typename SrvT::Request>();
149  std::shared_ptr<typename SrvT::Response> response;
150  populateRequest(req, mdata);
151 
152  std::string srv_name;
153  srv_name = mdata->getValue<std::string>("service_name", srv_name);
154  if (srv_name.empty() && !main_client_) {
156  "Route operation service (" + getName() + ") needs 'server_name' "
157  "set in the param file or in the operation's metadata!");
158  }
159 
160  try {
161  if (srv_name.empty()) {
162  srv_name = main_srv_name_;
163  response = callService(main_client_, req);
164  } else {
165  auto node = node_.lock();
166  if (!node) {
168  "Route operation service (" + getName() + ") failed to lock node.");
169  }
170  auto client = node->create_client<SrvT>(
171  srv_name, rclcpp::SystemDefaultsQoS().get_rmw_qos_profile(), callback_group_);
172  response = callService(client, req);
173  }
174  } catch (const std::exception & e) {
176  "Route operation service (" + getName() + ") failed to call service: " +
177  srv_name + " at Node " + std::to_string(node_achieved->nodeid));
178  }
179 
180  RCLCPP_INFO(
181  logger_,
182  "%s: Processed operation at Node %i with service %s.",
183  name_.c_str(), node_achieved->nodeid, srv_name.c_str());
184  return processResponse(response);
185  }
186 
187  std::shared_ptr<typename SrvT::Response> callService(
188  typename rclcpp::Client<SrvT>::SharedPtr client,
189  std::shared_ptr<typename SrvT::Request> req,
190  const std::chrono::nanoseconds timeout = std::chrono::nanoseconds(500ms))
191  {
192  auto node = node_.lock();
193  if (!client->wait_for_service(1s)) {
195  "Route operation service " +
196  std::string(client->get_service_name()) + "is not available!");
197  }
198 
199  auto result = client->async_send_request(req);
200  if (callback_group_executor_.spin_until_future_complete(result, timeout) !=
201  rclcpp::FutureReturnCode::SUCCESS)
202  {
204  "Route operation service " +
205  std::string(client->get_service_name()) + "failed to call!");
206  }
207 
208  return result.get();
209  }
210 
215  std::string getName() override {return name_;}
216 
222  RouteOperationType processType() final {return RouteOperationType::ON_GRAPH;}
223 
224  std::string name_, main_srv_name_;
225  std::atomic_bool reroute_;
226  rclcpp::Logger logger_{rclcpp::get_logger("RouteOperationClient")};
227  typename rclcpp::Client<SrvT>::SharedPtr main_client_;
228  rclcpp_lifecycle::LifecycleNode::WeakPtr node_;
229  rclcpp::CallbackGroup::SharedPtr callback_group_;
230  rclcpp::executors::SingleThreadedExecutor callback_group_executor_;
231 };
232 
233 } // namespace nav2_route
234 
235 #endif // NAV2_ROUTE__PLUGINS__ROUTE_OPERATION_CLIENT_HPP_
A route operation base class to trigger a service event at a node or edge. This is meant to be named ...
virtual OperationResult processResponse(std::shared_ptr< typename SrvT::Response >)
Process response from service to populate a result, if necessary.
virtual void populateRequest(std::shared_ptr< typename SrvT::Request >, const Metadata *)
Populate request with details for service, if necessary.
virtual void configureEvent(const rclcpp_lifecycle::LifecycleNode::SharedPtr, const std::string &)
Configure client with any necessary parameters, etc. May change or reset main_srv_name_ variable to c...
RouteOperationType processType() final
Indication that the adjust speed limit route operation is performed on all state changes.
virtual ~RouteOperationClient()=default
destructor
RouteOperationClient()=default
Constructor.
std::string getName() override
Get name of the plugin for parameter scope mapping.
void configure(const rclcpp_lifecycle::LifecycleNode::SharedPtr node, std::shared_ptr< nav2_costmap_2d::CostmapSubscriber >, const std::string &name) final
Configure.
OperationResult perform(NodePtr node_achieved, EdgePtr, EdgePtr, const Route &, const geometry_msgs::msg::PoseStamped &, const Metadata *mdata) final
The main operation to call a service of arbitrary type and arbitrary name.
A plugin interface to perform an operation while tracking the route such as triggered from the graph ...
An object representing edges between nodes.
Definition: types.hpp:134
An object to store arbitrary metadata regarding nodes from the graph file.
Definition: types.hpp:35
An object to store the nodes in the graph file.
Definition: types.hpp:183
a struct to hold return from an operation
An ordered set of nodes and edges corresponding to the planned route.
Definition: types.hpp:211