AutoAPMS
Resilient Robot Mission Management
Loading...
Searching...
No Matches
ros_publisher_node.hpp
Go to the documentation of this file.
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#pragma once
16
17#include <memory>
18#include <string>
19
20#include "rclcpp/qos.hpp"
21#include "behaviortree_cpp/condition_node.h"
24
26{
27
32template <class MessageT>
33class RosPublisherNode : public BT::ConditionNode
34{
35 using Publisher = typename rclcpp::Publisher<MessageT>;
36
37public:
38 using MessageType = MessageT;
39 using Config = BT::NodeConfig;
41
42 explicit RosPublisherNode(const std::string& instance_name, const Config& config, const Context& context,
43 const rclcpp::QoS& qos = { 10 });
44
45 virtual ~RosPublisherNode() = default;
46
54 static BT::PortsList providedBasicPorts(BT::PortsList addition)
55 {
56 BT::PortsList basic = { BT::InputPort<std::string>("topic_name", "", "Topic name") };
57 basic.insert(addition.begin(), addition.end());
58 return basic;
59 }
60
65 static BT::PortsList providedPorts()
66 {
67 return providedBasicPorts({});
68 }
69
70 BT::NodeStatus tick() override final;
71
80 virtual bool setMessage(MessageT& msg);
81
82 std::string getFullName() const;
83
84protected:
85 const Context& getRosContext();
86
87 const rclcpp::Logger logger_;
88
89private:
90 const Context context_;
91 const rclcpp::QoS qos_;
92 std::string topic_name_;
93 bool topic_name_should_be_checked_ = false;
94 std::shared_ptr<Publisher> publisher_;
95
96 bool createPublisher(const std::string& topic_name);
97};
98
99//----------------------------------------------------------------
100//---------------------- DEFINITIONS -----------------------------
101//----------------------------------------------------------------
102
103template <class MessageT>
104inline RosPublisherNode<MessageT>::RosPublisherNode(const std::string& instance_name, const Config& config,
105 const Context& context, const rclcpp::QoS& qos)
106 : BT::ConditionNode(instance_name, config), logger_(context.getLogger()), context_(context), qos_{ qos }
107{
108 // check port remapping
109 auto portIt = config.input_ports.find("topic_name");
110 if (portIt != config.input_ports.end())
111 {
112 const std::string& bb_topic_name = portIt->second;
113
114 if (isBlackboardPointer(bb_topic_name))
115 {
116 // unknown value at construction time. postpone to tick
117 topic_name_should_be_checked_ = true;
118 }
119 else if (!bb_topic_name.empty())
120 {
121 // "hard-coded" name in the bb_topic_name. Use it.
122 createPublisher(bb_topic_name);
123 }
124 }
125 // no port value or it is empty. Use the default port value
126 if (!publisher_ && !context.default_port_name.empty())
127 {
128 createPublisher(context.default_port_name);
129 }
130}
131
132template <class MessageT>
133inline bool RosPublisherNode<MessageT>::createPublisher(const std::string& topic_name)
134{
135 if (topic_name.empty())
136 {
137 throw exceptions::RosNodeError(getFullName() + " - Argument topic_name is empty when trying to create a client.");
138 }
139
140 auto node = getRosContext().nh.lock();
141 if (!node)
142 {
143 throw exceptions::RosNodeError(getFullName() +
144 " - The shared pointer to the ROS node went out of scope. The tree node doesn't "
145 "take the ownership of the node.");
146 }
147
148 publisher_ = node->template create_publisher<MessageT>(topic_name, qos_);
149 topic_name_ = topic_name;
150 RCLCPP_DEBUG(logger_, "%s - Created publisher for topic '%s'.", getFullName().c_str(), topic_name.c_str());
151 return true;
152}
153
154template <class MessageT>
155inline BT::NodeStatus RosPublisherNode<MessageT>::tick()
156{
157 // First, check if the subscriber_ is valid and that the name of the
158 // topic_name in the port didn't change.
159 // otherwise, create a new subscriber
160 if (!publisher_ || (status() == BT::NodeStatus::IDLE && topic_name_should_be_checked_))
161 {
162 std::string topic_name;
163 getInput("topic_name", topic_name);
164 if (topic_name_ != topic_name)
165 {
166 createPublisher(topic_name);
167 }
168 }
169
170 if (!publisher_)
171 {
173 " - You must specify a service name either by using a default value or by "
174 "passing a value to the corresponding dynamic input port.");
175 }
176
177 MessageT msg;
178 if (!setMessage(msg))
179 {
180 return BT::NodeStatus::FAILURE;
181 }
182 publisher_->publish(msg);
183 return BT::NodeStatus::SUCCESS;
184}
185
186template <class MessageT>
187inline bool RosPublisherNode<MessageT>::setMessage(MessageT& /*msg*/)
188{
189 return true;
190}
191
192template <class MessageT>
194{
195 // NOTE: registrationName() is empty during construction as this member is frist set after the factory constructed the
196 // object
197 if (registrationName().empty())
198 return name();
199 if (this->name() == this->registrationName())
200 return this->name();
201 return this->name() + " (Type: " + this->registrationName() + ")";
202}
203
204template <class MessageT>
206{
207 return context_;
208}
209
210} // namespace auto_apms_behavior_tree
Abstract class to wrap a ROS publisher.
RosPublisherNode(const std::string &instance_name, const Config &config, const Context &context, const rclcpp::QoS &qos={ 10 })
virtual bool setMessage(MessageT &msg)
setMessage is a callback invoked in tick to allow the user to pass the message to be published.
static BT::PortsList providedBasicPorts(BT::PortsList addition)
Any subclass of RosPublisherNode that has additinal ports must provide a providedPorts method and cal...
static BT::PortsList providedPorts()
Creates list of BT ports.
std::weak_ptr< rclcpp::Node > nh
Handle for the ROS2 node.
std::string default_port_name
Default port name of the corresponding ROS 2 communication interface.