AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
request_world_state.cpp
1// Copyright 2024 Robin Müller
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// https://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 "pyrobosim_msgs/srv/request_world_state.hpp"
16
17#include <regex>
18
19#include "auto_apms_behavior_tree_core/node.hpp"
20#include "auto_apms_simulation/util.hpp"
21#include "pyrobosim_msgs/msg/robot_state.hpp"
22
23#define INPUT_KEY_TARGET_LOCATION "target_loc"
24#define INPUT_KEY_FILTER_LOCATION "filter_loc"
25#define INPUT_KEY_FILTER_ROBOT "filter_robot"
26#define INPUT_KEY_TARGET_ROBOT "robot"
27#define OUTPUT_KEY_CURRENT_LOCATION "current_loc"
28
29namespace auto_apms_simulation
30{
31
32class IsLocationOccupied : public auto_apms_behavior_tree::core::RosServiceNode<pyrobosim_msgs::srv::RequestWorldState>
33{
34 std::string target_location_;
35
36public:
37 using RosServiceNode::RosServiceNode;
38
39 static BT::PortsList providedPorts()
40 {
41 return {
42 BT::InputPort<std::string>(INPUT_KEY_FILTER_ROBOT, ".*", "Regex filter for robots to consider."),
43 BT::InputPort<std::string>(
44 INPUT_KEY_FILTER_LOCATION, ".*", "Regex filter for locations to match against the target location."),
45 BT::InputPort<std::string>(INPUT_KEY_TARGET_LOCATION, "Name of the location to test for occupancy.")};
46 }
47
48 bool setRequest(Request::SharedPtr & request)
49 {
50 const BT::Expected<std::string> expected_location = getInput<std::string>(INPUT_KEY_TARGET_LOCATION);
51 if (!expected_location || expected_location.value().empty()) {
52 RCLCPP_ERROR(
53 logger_, "%s - You must provide a non-empty target location name.",
54 context_.getFullyQualifiedTreeNodeName(this).c_str());
55 RCLCPP_DEBUG_EXPRESSION(
56 logger_, !expected_location, "%s - Error message: %s", context_.getFullyQualifiedTreeNodeName(this).c_str(),
57 expected_location.error().c_str());
58 return false;
59 }
60 target_location_ = expected_location.value();
61 request->robot = ""; // Request state for all robots
62 return true;
63 }
64
65 BT::NodeStatus onResponseReceived(const Response::SharedPtr & response) override final
66 {
67 std::regex loc_regex(getInput<std::string>(INPUT_KEY_FILTER_LOCATION).value());
68 std::regex robot_regex(getInput<std::string>(INPUT_KEY_FILTER_ROBOT).value());
69 for (const pyrobosim_msgs::msg::RobotState & robot_state : response->state.robots) {
70 if (
71 std::regex_match(robot_state.last_visited_location, loc_regex) &&
72 std::regex_match(robot_state.name, robot_regex) && robot_state.last_visited_location == target_location_)
73 return BT::NodeStatus::SUCCESS;
74 }
75 return BT::NodeStatus::FAILURE;
76 }
77};
78
79class RobotSharesCurrentLocation
80: public auto_apms_behavior_tree::core::RosServiceNode<pyrobosim_msgs::srv::RequestWorldState>
81{
82 std::string target_robot_;
83
84public:
85 using RosServiceNode::RosServiceNode;
86
87 static BT::PortsList providedPorts()
88 {
89 return {
90 BT::OutputPort<std::string>(OUTPUT_KEY_CURRENT_LOCATION, "{=}", "Current location name."),
91 BT::InputPort<std::string>(INPUT_KEY_FILTER_LOCATION, ".*", "Regex filter for locations to consider."),
92 BT::InputPort<std::string>(
93 INPUT_KEY_TARGET_ROBOT, "Target robot in question to be the first at the given location.")};
94 }
95
96 bool setRequest(Request::SharedPtr & request)
97 {
98 const BT::Expected<std::string> expected_robot = getInput<std::string>(INPUT_KEY_TARGET_ROBOT);
99 if (!expected_robot || expected_robot.value().empty()) {
100 RCLCPP_ERROR(
101 logger_, "%s - You must provide a non-empty target robot name.",
102 context_.getFullyQualifiedTreeNodeName(this).c_str());
103 RCLCPP_DEBUG_EXPRESSION(
104 logger_, !expected_robot, "%s - Error message: %s", context_.getFullyQualifiedTreeNodeName(this).c_str(),
105 expected_robot.error().c_str());
106 return false;
107 }
108 target_robot_ = expected_robot.value();
109 request->robot = ""; // Request state for all robots
110 return true;
111 }
112
113 BT::NodeStatus onResponseReceived(const Response::SharedPtr & response) override final
114 {
115 // Determine current location of robot
116 std::string current_loc;
117 for (const pyrobosim_msgs::msg::RobotState & robot_state : response->state.robots) {
118 if (robot_state.name == target_robot_) {
119 current_loc = robot_state.last_visited_location;
120 break;
121 }
122 }
123 if (const BT::Result res = setOutput(OUTPUT_KEY_CURRENT_LOCATION, current_loc); !res) {
124 throw auto_apms_behavior_tree::exceptions::RosNodeError(
125 context_.getFullyQualifiedTreeNodeName(this) + " - Error setting output port '" + OUTPUT_KEY_CURRENT_LOCATION +
126 "': " + res.error());
127 }
128 std::regex loc_regex(getInput<std::string>(INPUT_KEY_FILTER_LOCATION).value());
129 for (const pyrobosim_msgs::msg::RobotState & robot_state : response->state.robots) {
130 if (robot_state.name == target_robot_) continue;
131 if (
132 std::regex_match(robot_state.last_visited_location, loc_regex) &&
133 robot_state.last_visited_location == current_loc)
134 return BT::NodeStatus::SUCCESS;
135 }
136 return BT::NodeStatus::FAILURE;
137 }
138};
139
140} // namespace auto_apms_simulation
141
142AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(auto_apms_simulation::IsLocationOccupied)
143AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(auto_apms_simulation::RobotSharesCurrentLocation)
#define AUTO_APMS_BEHAVIOR_TREE_DECLARE_NODE(type)
Macro for registering a behavior tree node plugin.
Definition node.hpp:40