166 BT::NodeStatus tick() override final;
169 const rclcpp::QoS qos_;
170 std::
string topic_name_;
171 bool dynamic_client_instance_ = false;
172 std::shared_ptr<SubscriberInstance> sub_instance_;
173 std::shared_ptr<MessageT> last_msg_;
180template <class MessageT>
182 rclcpp::Node::SharedPtr node, rclcpp::CallbackGroup::SharedPtr group, const std::
string & topic_name,
183 const rclcpp::QoS & qos)
185 rclcpp::SubscriptionOptions option;
186 option.callback_group = group;
189 auto callback = [
this](
const std::shared_ptr<MessageT> msg) {
190 this->last_msg = msg;
191 this->broadcast(msg);
193 subscriber = node->create_subscription<MessageT>(topic_name, qos, callback, option);
197template <
class MessageT>
198inline void RosSubscriberNode<MessageT>::SubscriberInstance::addCallback(
199 const void * callback_owner,
const std::function<
void(
const std::shared_ptr<MessageT>)> & callback)
201 callbacks.emplace_back(callback_owner, callback);
204template <
class MessageT>
205inline void RosSubscriberNode<MessageT>::SubscriberInstance::removeCallback(
const void * callback_owner)
209 callbacks.begin(), callbacks.end(), [callback_owner](
const auto & pair) { return pair.first == callback_owner; }),
213template <
class MessageT>
216 for (
auto & callback_pair : callbacks) {
217 callback_pair.second(msg);
221template <
class MessageT>
223 const std::string & instance_name,
const Config & config, Context context,
const rclcpp::QoS & qos)
224: RosConditionNode{instance_name, config, context}, qos_{qos}
228 if (
const BT::Expected<std::string> expected_name = context_.getTopicName(
this)) {
234 dynamic_client_instance_ =
true;
238template <
class MessageT>
241 if (topic_name.empty()) {
242 throw exceptions::RosNodeError(
243 context_.getFullyQualifiedTreeNodeName(
this) +
" - Argument topic_name is empty when trying to create a client.");
247 if (sub_instance_ && topic_name == sub_instance_->name)
return true;
249 rclcpp::Node::SharedPtr node = context_.
getRosNode();
250 rclcpp::CallbackGroup::SharedPtr group = context_.getWaitablesCallbackGroup();
252 throw exceptions::RosNodeError(
253 context_.getFullyQualifiedTreeNodeName(
this) +
254 " - The weak pointer to the ROS 2 callback group expired. The tree node doesn't "
255 "take ownership of it.");
261 topic_name, [&] {
return std::make_shared<SubscriberInstance>(node, group, topic_name, qos_); });
264 if (sub_instance_->last_msg) {
265 last_msg_ = sub_instance_->last_msg;
269 sub_instance_->addCallback(
this, [
this](
const std::shared_ptr<MessageT> msg) { last_msg_ = msg; });
274template <
class MessageT>
275inline BT::NodeStatus RosSubscriberNode<MessageT>::tick()
279 return BT::NodeStatus::FAILURE;
284 if (dynamic_client_instance_ && sub_instance_) {
285 dynamic_client_instance_ =
false;
290 if (status() == BT::NodeStatus::IDLE && dynamic_client_instance_) {
291 const BT::Expected<std::string> expected_name = context_.getTopicName(
this);
293 createSubscriber(expected_name.value());
295 throw exceptions::RosNodeError(
296 context_.getFullyQualifiedTreeNodeName(
this) +
297 " - Cannot create the subscriber because the topic name couldn't be resolved using "
298 "the expression specified by the node's registration parameters (" +
299 NodeRegistrationOptions::PARAM_NAME_ROS2TOPIC +
": " + context_.getRegistrationOptions().topic +
300 "). Error message: " + expected_name.error());
304 if (!sub_instance_) {
305 throw exceptions::RosNodeError(context_.getFullyQualifiedTreeNodeName(
this) +
" - sub_instance_ is nullptr.");
308 auto check_status = [
this](BT::NodeStatus status) {
309 if (!isStatusCompleted(status)) {
310 throw exceptions::RosNodeError(
311 context_.getFullyQualifiedTreeNodeName(
this) +
" - The callback must return either SUCCESS or FAILURE.");
315 auto status = check_status(onTick(last_msg_));
320template <
class MessageT>
323 if (!last_msg_ptr)
return BT::NodeStatus::FAILURE;
327template <
class MessageT>
330 return BT::NodeStatus::SUCCESS;
333template <
class MessageT>
336 if (sub_instance_)
return sub_instance_->name;