Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
operations_manager.cpp
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 #include "pluginlib/class_loader.hpp"
16 #include "pluginlib/class_list_macros.hpp"
17 #include "nav2_ros_common/node_utils.hpp"
18 #include "nav2_ros_common/lifecycle_node.hpp"
19 #include "nav2_route/types.hpp"
20 #include "nav2_route/utils.hpp"
21 #include "nav2_route/interfaces/route_operation.hpp"
22 #include "nav2_route/operations_manager.hpp"
23 
24 namespace nav2_route
25 {
26 
28  nav2::LifecycleNode::SharedPtr node,
29  std::shared_ptr<nav2_costmap_2d::CostmapSubscriber> costmap_subscriber)
30 : plugin_loader_("nav2_route", "nav2_route::RouteOperation")
31 {
32  logger_ = node->get_logger();
33 
34  // Have some default operations
35  const std::vector<std::string> default_plugin_ids(
36  {"AdjustSpeedLimit", "ReroutingService"});
37  const std::vector<std::string> default_plugin_types(
38  {"nav2_route::AdjustSpeedLimit", "nav2_route::ReroutingService"});
39 
40  auto operation_ids = node->declare_or_get_parameter(
41  "operations", default_plugin_ids);
42 
43  if (operation_ids == default_plugin_ids) {
44  for (unsigned int i = 0; i != operation_ids.size(); i++) {
45  nav2::declare_parameter_if_not_declared(
46  node, default_plugin_ids[i] + ".plugin", rclcpp::ParameterValue(default_plugin_types[i]));
47  }
48  }
49 
50  // Create plugins and sort them into On Query, Status Change, and Graph-calling Operations
51  for (size_t i = 0; i != operation_ids.size(); i++) {
52  try {
53  std::string type = nav2::get_plugin_type_param(node, operation_ids[i]);
54  RouteOperation::Ptr operation = plugin_loader_.createSharedInstance(type);
55  RCLCPP_INFO(
56  node->get_logger(), "Created route operation %s of type %s",
57  operation_ids[i].c_str(), type.c_str());
58  operation->configure(node, costmap_subscriber, operation_ids[i]);
59  RouteOperationType process_type = operation->processType();
60  if (process_type == RouteOperationType::ON_QUERY) {
61  query_operations_.push_back(std::move(operation));
62  } else if (process_type == RouteOperationType::ON_STATUS_CHANGE) {
63  change_operations_.push_back(std::move(operation));
64  } else {
65  graph_operations_[operation->getName()] = operation;
66  }
67  } catch (const pluginlib::PluginlibException & ex) {
68  throw ex;
69  }
70  }
71 }
72 
73 template<typename T>
75  T & obj, const OperationTrigger & trigger,
76  OperationPtrs & operations)
77 {
78  if (!obj) {
79  return;
80  }
81 
82  Operations & op_vec = obj->operations;
83  for (Operations::iterator it = op_vec.begin(); it != op_vec.end(); ++it) {
84  if (it->trigger == trigger) {
85  operations.push_back(&(*it));
86  }
87  }
88 }
89 
91  const NodePtr node, const EdgePtr edge_enter, const EdgePtr edge_exit)
92 {
93  OperationPtrs ops;
94  findGraphOperationsToProcess(node, OperationTrigger::NODE, ops);
95  findGraphOperationsToProcess(edge_enter, OperationTrigger::ON_ENTER, ops);
96  findGraphOperationsToProcess(edge_exit, OperationTrigger::ON_EXIT, ops);
97  return ops;
98 }
99 
101  const std::string & name, const OperationResult & op_result, OperationsResult & result)
102 {
103  result.reroute = result.reroute || op_result.reroute;
104  result.blocked_ids.insert(
105  result.blocked_ids.end(), op_result.blocked_ids.begin(), op_result.blocked_ids.end());
106  result.operations_triggered.push_back(name);
107 }
108 
110  const std::vector<RouteOperation::Ptr> & route_operations,
111  OperationsResult & result,
112  const NodePtr node,
113  const EdgePtr edge_entered,
114  const EdgePtr edge_exited,
115  const Route & route,
116  const geometry_msgs::msg::PoseStamped & pose)
117 {
118  for (OperationsIter it = route_operations.begin(); it != route_operations.end(); ++it) {
119  const RouteOperation::Ptr & plugin = *it;
120  OperationResult op_result = plugin->perform(node, edge_entered, edge_exited, route, pose);
121  updateResult(plugin->getName(), op_result, result);
122  }
123 }
124 
126  const bool status_change,
127  const RouteTrackingState & state,
128  const Route & route,
129  const geometry_msgs::msg::PoseStamped & pose,
130  const ReroutingState & rerouting_info)
131 {
132  // Get important state information
133  OperationsResult result;
134  NodePtr node = state.last_node;
135  EdgePtr edge_entered = state.current_edge;
136  EdgePtr edge_exited =
137  state.route_edges_idx > 0 ? route.edges[state.route_edges_idx - 1] : nullptr;
138 
139  // If we have rerouting_info.curr_edge, then after the first node is achieved,
140  // the robot is exiting the partial previous edge.
141  if (state.route_edges_idx == 0 && rerouting_info.curr_edge) {
142  edge_exited = rerouting_info.curr_edge;
143  }
144 
145  if (status_change) {
146  // Process operations defined in the navigation graph at node or edge
147  OperationPtrs operations = findGraphOperations(node, edge_entered, edge_exited);
148  for (unsigned int i = 0; i != operations.size(); i++) {
149  auto op = graph_operations_.find(operations[i]->type);
150  if (op != graph_operations_.end()) {
151  OperationResult op_result = op->second->perform(
152  node, edge_entered, edge_exited, route, pose, &operations[i]->metadata);
153  updateResult(op->second->getName(), op_result, result);
154  } else {
156  "Operation " + operations[i]->type +
157  " does not exist in route operations loaded!");
158  }
159  }
160 
161  // Process operations which trigger on any status changes
163  change_operations_, result, node, edge_entered, edge_exited, route, pose);
164  }
165 
166  // Process operations which trigger regardless of status change or nodes / edges
168  query_operations_, result, node, edge_entered /*edge_curr*/, edge_exited, route, pose);
169  return result;
170 }
171 
172 } // namespace nav2_route
OperationsResult process(const bool status_change, const RouteTrackingState &state, const Route &route, const geometry_msgs::msg::PoseStamped &pose, const ReroutingState &rerouting_info)
Processes the operations at this tracker state.
void processOperationsPluginVector(const std::vector< RouteOperation::Ptr > &operations, OperationsResult &result, const NodePtr node, const EdgePtr edge_entered, const EdgePtr edge_exited, const Route &route, const geometry_msgs::msg::PoseStamped &pose)
Processes a vector of operations plugins, by trigger.
OperationPtrs findGraphOperations(const NodePtr node, const EdgePtr edge_enter, const EdgePtr edge_exit)
Finds the set of operations stored in the graph to trigger at this transition.
void findGraphOperationsToProcess(T &obj, const OperationTrigger &trigger, OperationPtrs &operations)
Finds the set of operations stored in graph objects, by event.
void updateResult(const std::string &name, const OperationResult &op_result, OperationsResult &result)
Updates manager result state by an individual operation's result.
OperationsManager(nav2::LifecycleNode::SharedPtr node, std::shared_ptr< nav2_costmap_2d::CostmapSubscriber > costmap_subscriber)
A constructor for nav2_route::OperationsManager.
An object representing edges between nodes.
Definition: types.hpp:134
An object to store the nodes in the graph file.
Definition: types.hpp:183
a struct to hold return from an operation
Result information from the operations manager.
Definition: types.hpp:123
State shared to objects to communicate important rerouting data to avoid rerouting over blocked edges...
Definition: types.hpp:264
Current state management of route tracking class.
Definition: types.hpp:248
An ordered set of nodes and edges corresponding to the planned route.
Definition: types.hpp:211