change_header.cpp
Go to the documentation of this file.
1 // SPDX-License-Identifier: BSD-3-Clause
2 // SPDX-FileCopyrightText: Czech Technical University in Prague
3 
11 #include <memory>
12 #include <string>
13 #include <utility>
14 
15 #include <nodelet/nodelet.h>
17 #include <ros/duration.h>
18 #include <ros/subscribe_options.h>
19 #include <ros/time.h>
20 
24 
28 
29 namespace cras
30 {
31 
33 {
34  const auto params = this->privateParams();
35  const auto inQueueSize = params->getParam("in_queue_size", 10);
36  const auto outQueueSize = params->getParam("out_queue_size", inQueueSize);
37  const auto lazy = params->getParam("lazy", false);
38  const auto tcpNoDelay = params->getParam("tcp_no_delay", false);
39 
40  auto nh = this->getMTPrivateNodeHandle();
41  std::string inTopic = "input";
42  std::string outTopic = "output";
43 
44  // Mimic the behavior of topic_tools/relay when called with CLI args
45  if (!this->getMyArgv().empty())
46  {
47  nh = this->getMTNodeHandle();
48  inTopic = this->getMyArgv()[0];
49  outTopic = (this->getMyArgv().size() >= 2 ? this->getMyArgv()[1] : (inTopic + "_change_header"));
50  }
51 
52  if (params->hasParam("frame_id_prefix"))
53  this->newFrameIdPrefix = params->getParam<std::string>("frame_id_prefix", cras::nullopt);
54 
55  if (params->hasParam("frame_id_suffix"))
56  this->newFrameIdSuffix = params->getParam<std::string>("frame_id_suffix", cras::nullopt);
57 
58  if (params->hasParam("frame_id"))
59  this->newFrameId = params->getParam<std::string>("frame_id", cras::nullopt);
60 
61  if (params->hasParam("frame_id_replace_start"))
62  {
63  const auto param = params->getParam<std::string>("frame_id_replace_start", cras::nullopt);
64  const auto parts = cras::split(param, "|", 1);
65  const auto& from = parts[0];
66  const auto& to = parts.size() > 1 ? parts[1] : "";
67  this->newFrameIdReplaceStart = std::make_pair(from, to);
68  }
69 
70  if (params->hasParam("frame_id_replace_end"))
71  {
72  const auto param = params->getParam<std::string>("frame_id_replace_end", cras::nullopt);
73  const auto parts = cras::split(param, "|", 1);
74  const auto& from = parts[0];
75  const auto& to = parts.size() > 1 ? parts[1] : "";
76  this->newFrameIdReplaceEnd = std::make_pair(from, to);
77  }
78 
79  if (params->hasParam("frame_id_replace"))
80  {
81  const auto param = params->getParam<std::string>("frame_id_replace", cras::nullopt);
82  const auto parts = cras::split(param, "|", 1);
83  const auto& from = parts[0];
84  const auto& to = parts.size() > 1 ? parts[1] : "";
85  this->newFrameIdReplace = std::make_pair(from, to);
86  }
87 
88  if (params->hasParam("stamp_relative"))
89  this->newStampRel = params->getParam<ros::Duration>("stamp_relative", cras::nullopt);
90 
91  if (params->hasParam("stamp"))
92  this->newStampAbs = params->getParam<ros::Time>("stamp", cras::nullopt);
93 
94  this->newStampRosTime = params->getParam("stamp_ros_time", false);
95  this->newStampWallTime = params->getParam("stamp_wall_time", false);
96 
98  opts.allow_concurrent_callbacks = true;
99  opts.transport_hints.tcpNoDelay(tcpNoDelay);
100  this->pubSub = std::make_unique<cras::GenericLazyPubSub>(nh, inTopic, outTopic, inQueueSize, outQueueSize,
102 
103  if (!lazy)
104  this->pubSub->setLazy(false);
105 
106  CRAS_INFO("Created%s header changer subscribing to %s and publishing to %s.",
107  (lazy ? " lazy" : ""), nh.resolveName(inTopic).c_str(), nh.resolveName(outTopic).c_str());
108 }
109 
112 {
113  const auto& msg = event.getConstMessage();
114 
115  if (!this->hasHeader.has_value())
116  {
117  this->hasHeader = cras::hasHeader(*msg);
118 
119  if (!this->hasHeader.value())
120  {
121  CRAS_ERROR("Running change_header on message type %s which does not have a header! Ignoring all messages.",
122  event.getConnectionHeader()["type"].c_str());
123  }
124  }
125 
126  if (!this->hasHeader.value())
127  return;
128 
129  auto header = cras::getHeader(*msg);
130  if (!header.has_value())
131  {
132  CRAS_ERROR("Change_header failed to extract a header from the message of type %s! "
133  "Ignoring the message.", event.getConnectionHeader()["type"].c_str());
134  return;
135  }
136  const auto origHeader = header.value();
137 
138  if (this->newFrameIdReplaceStart.has_value())
139  {
140  const auto fromTo = this->newFrameIdReplaceStart.value();
141  cras::replace(header->frame_id, fromTo.first, fromTo.second, cras::ReplacePosition::START);
142  }
143 
144  if (this->newFrameIdReplaceEnd.has_value())
145  {
146  const auto fromTo = this->newFrameIdReplaceEnd.value();
147  cras::replace(header->frame_id, fromTo.first, fromTo.second, cras::ReplacePosition::END);
148  }
149 
150  if (this->newFrameIdReplace.has_value())
151  {
152  const auto fromTo = this->newFrameIdReplace.value();
153  cras::replace(header->frame_id, fromTo.first, fromTo.second);
154  }
155 
156  if (this->newFrameIdPrefix.has_value())
157  header->frame_id = this->newFrameIdPrefix.value() + header->frame_id;
158 
159  if (this->newFrameIdSuffix.has_value())
160  header->frame_id += this->newFrameIdSuffix.value();
161 
162  if (this->newFrameId.has_value())
163  header->frame_id = this->newFrameId.value();
164 
165  if (this->newStampRosTime)
166  {
167  header->stamp = ros::Time::now();
168  }
169  else if (this->newStampWallTime)
170  {
171  const auto now = ros::WallTime::now();
172  header->stamp = {now.sec, now.nsec};
173  }
174 
175  if (this->newStampRel.has_value())
176  {
177  // Correctly handle overflow cases
178  try
179  {
180  header->stamp += this->newStampRel.value();
181  }
182  catch (const ::std::runtime_error&)
183  {
184  header->stamp = this->newStampRel.value().toSec() > 0 ? ros::TIME_MAX : ros::TIME_MIN;
185  }
186  }
187 
188  if (this->newStampAbs.has_value())
189  header->stamp = this->newStampAbs.value();
190 
191  if (header.value() == origHeader)
192  {
193  pub.publish(msg);
194  return;
195  }
196 
198  // cannot use newMsg = *msg in Melodic, that would segfault
199  cras::copyShapeShifter(*msg, newMsg);
200  if (!cras::setHeader(newMsg, header.value()))
201  {
202  CRAS_ERROR("Change_header failed to modify the header of the message of type %s! "
203  "Ignoring the message.", event.getConnectionHeader()["type"].c_str());
204  return;
205  }
206 
207  pub.publish(newMsg);
208 }
209 
210 }
211 
optional.hpp
msg
msg
ros::Publisher
cras
cras::ChangeHeaderNodelet::newFrameIdReplace
::cras::optional<::std::pair<::std::string, ::std::string > > newFrameIdReplace
Replace all occurrences of first string with the second one in frame_id.
Definition: change_header.h:110
cras::hasHeader
bool hasHeader(const ::topic_tools::ShapeShifter &msg)
Tell whether the given message has header field.
shape_shifter.h
Tools for more convenient working with ShapeShifter objects.
NodeletParamHelper< ::nodelet::Nodelet >::privateParams
::cras::BoundParamHelperPtr privateParams(const ::std::string &ns="") const
time.h
ros::SubscribeOptions::transport_hints
TransportHints transport_hints
change_header.h
This is a relay nodelet that can modify headers.
nodelet::Nodelet::getMTPrivateNodeHandle
ros::NodeHandle & getMTPrivateNodeHandle() const
ros::SubscribeOptions::allow_concurrent_callbacks
bool allow_concurrent_callbacks
cras::ChangeHeaderNodelet::newFrameIdSuffix
::cras::optional<::std::string > newFrameIdSuffix
Suffix frame_id with this value.
Definition: change_header.h:107
nodelet::Nodelet::getMyArgv
const V_string & getMyArgv() const
CRAS_ERROR
#define CRAS_ERROR(...)
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
cras::ChangeHeaderNodelet::hasHeader
::cras::optional< bool > hasHeader
Whether the subscribed topic has a header field. This is filled on receipt of the first message.
Definition: change_header.h:131
ros::TransportHints::tcpNoDelay
TransportHints & tcpNoDelay(bool nodelay=true)
functional.hpp
cras::ChangeHeaderNodelet::newStampRosTime
bool newStampRosTime
Change stamp to current ROS time.
Definition: change_header.h:125
CRAS_INFO
#define CRAS_INFO(...)
class_list_macros.h
string_utils.hpp
generic_lazy_pubsub.hpp
Lazy subscriber that subscribes only when a paired publisher has subscribers. Version for unknown mes...
duration.h
PLUGINLIB_EXPORT_CLASS
#define PLUGINLIB_EXPORT_CLASS(class_type, base_class_type)
cras::replace
void replace(::std::string &str, const ::std::string &from, const ::std::string &to, const ::cras::ReplacePosition &where=::cras::ReplacePosition::EVERYWHERE)
ros::WallTime::now
static WallTime now()
cras::ChangeHeaderNodelet::newStampAbs
::cras::optional<::ros::Time > newStampAbs
Replace stamp with the given value.
Definition: change_header.h:119
ros::TIME_MIN
const Time TIME_MIN(0, 1)
cras::ChangeHeaderNodelet::newFrameIdReplaceStart
::cras::optional<::std::pair<::std::string, ::std::string > > newFrameIdReplaceStart
If frame_id starts with the first string, replace it with the second one.
Definition: change_header.h:113
cras::ChangeHeaderNodelet::processMessage
virtual void processMessage(const ::ros::MessageEvent< const ::topic_tools::ShapeShifter > &event, ::ros::Publisher &pub)
Change the header of the incoming message and publish the result.
Definition: change_header.cpp:110
cras::ChangeHeaderNodelet::newFrameId
::cras::optional<::std::string > newFrameId
Replace the whole frame_id with this value.
Definition: change_header.h:101
cras::split
::std::vector<::std::string > split(const ::std::string &str, const ::std::string &delimiter, int maxSplits=-1)
cras::ChangeHeaderNodelet::newStampWallTime
bool newStampWallTime
Change stamp to current wall time.
Definition: change_header.h:128
cras::ChangeHeaderNodelet::pubSub
::std::unique_ptr<::cras::GenericLazyPubSub > pubSub
The lazy pair of subscriber and publisher.
Definition: change_header.h:88
cras::ChangeHeaderNodelet::newFrameIdReplaceEnd
::cras::optional<::std::pair<::std::string, ::std::string > > newFrameIdReplaceEnd
If frame_id ends with the first string, replace it with the second one.
Definition: change_header.h:116
cras::copyShapeShifter
void copyShapeShifter(const ::topic_tools::ShapeShifter &in, ::topic_tools::ShapeShifter &out)
Copy in ShapeShifter to out.
cras::bind_front
auto bind_front(F &&f, Args &&... args)
ros::TIME_MAX
const ROSTIME_DECL Time TIME_MAX
nodelet::Nodelet
ros::SubscribeOptions
ros::Time
NodeletParamHelper< ::nodelet::Nodelet >::params
::cras::BoundParamHelperPtr params(const ::ros::NodeHandle &node, const ::std::string &ns="") const
nodelet.h
cras::ReplacePosition::END
@ END
cras::HasLogger::log
::cras::LogHelperPtr log
param
T param(const std::string &param_name, const T &default_val)
ros::MessageEvent::getConnectionHeader
M_string & getConnectionHeader() const
topic_tools::ShapeShifter
header
const std::string header
ros::Duration
cras::ReplacePosition::START
@ START
subscribe_options.h
cras::getHeader
::cras::optional<::std_msgs::Header > getHeader(const ::topic_tools::ShapeShifter &msg)
Get the header field of the given message, if it has any.
cras::ChangeHeaderNodelet::newStampRel
::cras::optional<::ros::Duration > newStampRel
Add this value to stamp. In case of under/overflow, TIME_MIN or TIME_MAX are set.
Definition: change_header.h:122
ros::MessageEvent
cras::ChangeHeaderNodelet::newFrameIdPrefix
::cras::optional<::std::string > newFrameIdPrefix
Prefix frame_id with this value.
Definition: change_header.h:104
nodelet::Nodelet::getMTNodeHandle
ros::NodeHandle & getMTNodeHandle() const
cras::ChangeHeaderNodelet::onInit
void onInit() override
Definition: change_header.cpp:32
cras::ChangeHeaderNodelet
Nodelet for relaying messages and changing their header.
Definition: change_header.h:84
ros::Time::now
static Time now()
cras::setHeader
bool setHeader(::topic_tools::ShapeShifter &msg, ::std_msgs::Header &header)
Change the header field of the given message, if it has any.


cras_topic_tools
Author(s): Martin Pecka
autogenerated on Tue Nov 26 2024 03:49:18