statepublisher_rostopic.cpp
Go to the documentation of this file.
2 #include "PlotJuggler/any.hpp"
3 #include "../qnodedialog.h"
5 #include <QDialog>
6 #include <QFormLayout>
7 #include <QCheckBox>
8 #include <QLabel>
9 #include <QHBoxLayout>
10 #include <QVBoxLayout>
11 #include <QDialogButtonBox>
12 #include <QScrollArea>
13 #include <QPushButton>
14 #include <QSettings>
15 #include <QRadioButton>
16 #include <rosbag/bag.h>
17 #include <std_msgs/Header.h>
18 #include <unordered_map>
19 #include <rosgraph_msgs/Clock.h>
20 #include <QMessageBox>
21 
23  _enabled(false ),
24  _node(nullptr),
25  _publish_clock(true)
26 {
27  QSettings settings;
28  _publish_clock = settings.value( "TopicPublisherROS/publish_clock", true ).toBool();
29 
30 }
31 
33 {
34  _enabled = false;
35 }
36 
37 void TopicPublisherROS::setParentMenu(QMenu *menu, QAction* action)
38 {
39  StatePublisher::setParentMenu( menu, action );
40 
41  _enable_self_action = menu->actions().back();
42 
43  _select_topics_to_publish = new QAction(QString("Select topics to be published"), _menu);
44  _menu->addAction( _select_topics_to_publish );
45  connect(_select_topics_to_publish, &QAction::triggered,
47 }
48 
49 void TopicPublisherROS::setEnabled(bool to_enable)
50 {
51  if( !_node && to_enable)
52  {
54  }
55  _enabled = (to_enable && _node);
56 
57  if(_enabled)
58  {
59  filterDialog(true);
60  if( !_tf_publisher )
61  {
62  _tf_publisher = std::unique_ptr<tf::TransformBroadcaster>( new tf::TransformBroadcaster );
63  }
64  _previous_play_index = std::numeric_limits<int>::max();
65 
66  if( _publish_clock )
67  {
68  _clock_publisher = _node->advertise<rosgraph_msgs::Clock>( "/clock", 10, true);
69  }
70  else{
72  }
73  }
74  else{
75  _node.reset();
76  _publishers.clear();
78  }
79 
80 
82 }
83 
84 void TopicPublisherROS::filterDialog(bool autoconfirm)
85 {
86  auto all_topics = RosIntrospectionFactory::get().getTopicList();
87 
88  if( all_topics.empty() ) return;
89 
90  QDialog* dialog = new QDialog();
91  dialog->setWindowTitle("Select topics to be published");
92  dialog->setMinimumWidth(350);
93  QVBoxLayout* vertical_layout = new QVBoxLayout();
94  QFormLayout* grid_layout = new QFormLayout();
95 
96  std::map<std::string, QCheckBox*> checkbox;
97 
98  QFrame* frame = new QFrame;
99 
100  auto publish_sim_time = new QRadioButton("Keep original timestamp and publish [/clock]");
101  auto publish_real_time = new QRadioButton("Overwrite timestamp [std_msgs/Header/stamp]");
102  QPushButton* select_button = new QPushButton("Select all");
103  QPushButton* deselect_button = new QPushButton("Deselect all");
104 
105  publish_sim_time->setChecked( _publish_clock );
106  publish_sim_time->setFocusPolicy(Qt::NoFocus);
107  publish_sim_time->setToolTip("Publish the topic [/clock].\n"
108  "You might want to set rosparam use_sim_time = true" );
109 
110  publish_real_time->setChecked( !_publish_clock );
111  publish_real_time->setFocusPolicy(Qt::NoFocus);
112  publish_real_time->setToolTip("Pretend it is a new message.\n"
113  "The timestamp of the original message will be overwritten"
114  "with ros::Time::Now()");
115 
116  select_button->setFocusPolicy(Qt::NoFocus);
117  deselect_button->setFocusPolicy(Qt::NoFocus);
118 
119  for (const auto& topic: all_topics)
120  {
121  auto cb = new QCheckBox(dialog);
122  auto filter_it = _topics_to_publish.find( *topic );
123  if( filter_it == _topics_to_publish.end() )
124  {
125  cb->setChecked( true );
126  }
127  else{
128  cb->setChecked( filter_it->second );
129  }
130  cb->setFocusPolicy(Qt::NoFocus);
131  grid_layout->addRow( new QLabel( QString::fromStdString(*topic)), cb);
132  checkbox.insert( std::make_pair(*topic, cb) );
133  connect( select_button, &QPushButton::pressed, [cb](){ cb->setChecked(true);} );
134  connect( deselect_button, &QPushButton::pressed, [cb](){ cb->setChecked(false);} );
135  }
136 
137  frame->setLayout(grid_layout);
138 
139  QScrollArea* scrollArea = new QScrollArea;
140  scrollArea->setWidget(frame);
141 
142  QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
143 
144  QHBoxLayout* select_buttons_layout = new QHBoxLayout;
145  select_buttons_layout->addWidget( select_button );
146  select_buttons_layout->addWidget( deselect_button );
147 
148  vertical_layout->addWidget( publish_sim_time );
149  vertical_layout->addWidget( publish_real_time );
150  vertical_layout->addWidget(scrollArea);
151  vertical_layout->addLayout(select_buttons_layout);
152  vertical_layout->addWidget( buttons );
153 
154  connect(buttons, SIGNAL(accepted()), dialog, SLOT(accept()));
155  connect(buttons, SIGNAL(rejected()), dialog, SLOT(reject()));
156 
157  dialog->setLayout(vertical_layout);
158 
159  if( !autoconfirm )
160  {
161  auto result = dialog->exec();
162  }
163 
164  if(autoconfirm || dialog->result() == QDialog::Accepted)
165  {
166  _topics_to_publish.clear();
167  for(const auto& it: checkbox )
168  {
169  _topics_to_publish.insert( {it.first, it.second->isChecked() } );
170  }
171 
172  //remove already created publisher if not needed anymore
173  for (auto it = _publishers.begin(); it != _publishers.end(); /* no increment */)
174  {
175  const std::string& topic_name = it->first;
176  if( !toPublish(topic_name) )
177  {
178  it = _publishers.erase(it);
179  }
180  else{
181  it++;
182  }
183  }
184 
185  _publish_clock = publish_sim_time->isChecked();
186 
187  QSettings settings;
188  settings.setValue( "TopicPublisherROS/publish_clock", _publish_clock );
189  }
190 }
191 
192 void TopicPublisherROS::broadcastTF(double current_time)
193 {
194  std::unordered_map<std::string, geometry_msgs::TransformStamped> transforms;
195 
196  for(const auto& data_it: _datamap->user_defined )
197  {
198  const std::string& topic_name = data_it.first;
199  const PlotDataAny& plot_any = data_it.second;
200 
201  if( !toPublish(topic_name) )
202  {
203  continue;// Not selected
204  }
205  const RosIntrospection::ShapeShifter* shapeshifter =
207  if( shapeshifter->getDataType() != "tf/tfMessage" &&
208  shapeshifter->getDataType() != "tf2_msgs/TFMessage" )
209  {
210  continue;
211  }
212 
213  const PlotDataAny* tf_data = &plot_any;
214  int last_index = tf_data->getIndexFromX( current_time );
215  if( last_index < 0)
216  {
217  continue;
218  }
219 
220  std::vector<uint8_t> raw_buffer;
221  // 1 second in the past (to be configurable in the future
222  int initial_index = tf_data->getIndexFromX( current_time - 2.0 );
223 
224  if( _previous_play_index < last_index &&
225  _previous_play_index > initial_index )
226  {
227  initial_index = _previous_play_index;
228  }
229 
230  for(size_t index = std::max(0, initial_index); index <= last_index; index++ )
231  {
232  const nonstd::any& any_value = tf_data->at(index).y;
233 
234  const bool isRosbagMessage = any_value.type() == typeid(rosbag::MessageInstance);
235 
236  if( isRosbagMessage )
237  {
238  const auto& msg_instance = nonstd::any_cast<rosbag::MessageInstance>( any_value );
239 
240  raw_buffer.resize( msg_instance.size() );
241  ros::serialization::OStream ostream(raw_buffer.data(), raw_buffer.size());
242  msg_instance.write(ostream);
243 
244  tf::tfMessage tf_msg;
245  ros::serialization::IStream istream( raw_buffer.data(), raw_buffer.size() );
246  ros::serialization::deserialize(istream, tf_msg);
247 
248  for(const auto& stamped_transform: tf_msg.transforms)
249  {
250  const auto& child_id = stamped_transform.child_frame_id;
251  auto it = transforms.find(child_id);
252  if( it == transforms.end())
253  {
254  transforms.insert( {stamped_transform.child_frame_id, stamped_transform} );
255  }
256  else if( it->second.header.stamp <= stamped_transform.header.stamp)
257  {
258  it->second = stamped_transform;
259  }
260  }
261  }
262  }
263  }
264 
265  std::vector<geometry_msgs::TransformStamped> transforms_vector;
266  transforms_vector.reserve(transforms.size());
267 
268  const auto now = ros::Time::now();
269  for(auto& trans: transforms)
270  {
271  if( !_publish_clock )
272  {
273  trans.second.header.stamp = now;
274  }
275  transforms_vector.emplace_back( std::move(trans.second) );
276  }
277 
278  _tf_publisher->sendTransform(transforms_vector);
279 }
280 
281 bool TopicPublisherROS::toPublish(const std::string &topic_name)
282 {
283  auto it = _topics_to_publish.find( topic_name );
284  if( it == _topics_to_publish.end() ){
285 
286  return false;
287  }
288  else {
289  return it->second;
290  }
291 }
292 
293 
295 {
296  using namespace RosIntrospection;
297 
298  const auto& topic_name = msg_instance.getTopic();
299  RosIntrospection::ShapeShifter* shapeshifted =
301 
302  if( ! shapeshifted )
303  {
304  return;// Not registered, just skip
305  }
306 
307  std::vector<uint8_t> raw_buffer;
308  raw_buffer.resize( msg_instance.size() );
309  ros::serialization::OStream ostream(raw_buffer.data(), raw_buffer.size());
310  msg_instance.write(ostream);
311 
312  if( !_publish_clock )
313  {
314  const ROSMessageInfo* msg_info = RosIntrospectionFactory::parser().getMessageInfo( topic_name );
315  if(msg_info && msg_info->message_tree.croot()->children().size() >= 1)
316  {
317  const auto& first_field = msg_info->message_tree.croot()->child(0)->value();
318  if(first_field->type().baseName() == "std_msgs/Header")
319  {
320  absl::Span<uint8_t> buffer(raw_buffer);
321  std_msgs::Header msg;
322  ros::serialization::IStream is( buffer.data(), buffer.size() );
324  msg.stamp = ros::Time::now();
325  ros::serialization::OStream os( buffer.data(), buffer.size() );
327  }
328  }
329  }
330 
331  ros::serialization::IStream istream( raw_buffer.data(), raw_buffer.size() );
332  shapeshifted->read( istream );
333 
334  auto publisher_it = _publishers.find( topic_name );
335  if( publisher_it == _publishers.end())
336  {
337  auto res = _publishers.insert( {topic_name, shapeshifted->advertise( *_node, topic_name, 10, true)} );
338  publisher_it = res.first;
339  }
340 
341 
342  const ros::Publisher& publisher = publisher_it->second;
343  publisher.publish( *shapeshifted );
344 }
345 
346 
347 
348 void TopicPublisherROS::updateState(double current_time)
349 {
350  if(!_enabled || !_node) return;
351 
352  if( !ros::master::check() )
353  {
354  QMessageBox::warning(nullptr, tr("Disconnected!"),
355  "The roscore master cannot be detected.\n"
356  "The publisher will be disabled.");
357  _enable_self_action->setChecked(false);
358  return;
359  }
360 
361  //-----------------------------------------------
362  broadcastTF(current_time);
363  //-----------------------------------------------
364 
365  auto data_it = _datamap->user_defined.find( "__consecutive_message_instances__" );
366  if( data_it != _datamap->user_defined.end() )
367  {
368  const PlotDataAny& continuous_msgs = data_it->second;
369  _previous_play_index = continuous_msgs.getIndexFromX(current_time);
370  //qDebug() << QString("u: %1").arg( current_index ).arg(current_time, 0, 'f', 4 );
371  }
372 
373 
374  for(const auto& data_it: _datamap->user_defined )
375  {
376  const std::string& topic_name = data_it.first;
377  const PlotDataAny& plot_any = data_it.second;
378  if( !toPublish(topic_name) )
379  {
380  continue;// Not selected
381  }
382 
383  const RosIntrospection::ShapeShifter* shapeshifter =
385 
386  if( shapeshifter->getDataType() == "tf/tfMessage" ||
387  shapeshifter->getDataType() == "tf2_msgs/TFMessage" )
388  {
389  continue;
390  }
391 
392  int last_index = plot_any.getIndexFromX( current_time );
393  if( last_index < 0)
394  {
395  continue;
396  }
397 
398  const auto& any_value = plot_any.at(last_index).y;
399 
400  if( any_value.type() == typeid(rosbag::MessageInstance) )
401  {
402  const auto& msg_instance = nonstd::any_cast<rosbag::MessageInstance>( any_value );
403  publishAnyMsg( msg_instance );
404  }
405  }
406 
407  if( _publish_clock )
408  {
409  rosgraph_msgs::Clock clock;
410  try{
411  clock.clock.fromSec( current_time );
412  _clock_publisher.publish( clock );
413  }
414  catch(...)
415  {
416  qDebug() << "error: " << current_time;
417  }
418  }
419 }
420 
421 
422 void TopicPublisherROS::play(double current_time)
423 {
424  if(!_enabled || !_node) return;
425 
426  if( !ros::master::check() )
427  {
428  QMessageBox::warning(nullptr, tr("Disconnected!"),
429  "The roscore master cannot be detected.\n"
430  "The publisher will be disabled.");
431  _enable_self_action->setChecked(false);
432  return;
433  }
434 
435  auto data_it = _datamap->user_defined.find( "__consecutive_message_instances__" );
436  if( data_it == _datamap->user_defined.end() )
437  {
438  return;
439  }
440  const PlotDataAny& continuous_msgs = data_it->second;
441  int current_index = continuous_msgs.getIndexFromX(current_time);
442 
443  if( _previous_play_index > current_index)
444  {
445  _previous_play_index = current_index;
446  updateState(current_time);
447  return;
448  }
449  else
450  {
451  const PlotDataAny& consecutive_msg = data_it->second;
452  for(int index = _previous_play_index+1; index <= current_index; index++)
453  {
454 
455  const auto& any_value = consecutive_msg.at(index).y;
456  if( any_value.type() == typeid(rosbag::MessageInstance) )
457  {
458  const auto& msg_instance = nonstd::any_cast<rosbag::MessageInstance>( any_value );
459 
460  if( !toPublish( msg_instance.getTopic() ) )
461  {
462  continue;// Not selected
463  }
464 
465  //qDebug() << QString("p: %1").arg( index );
466  publishAnyMsg( msg_instance );
467 
468  if( _publish_clock )
469  {
470  rosgraph_msgs::Clock clock;
471  clock.clock = msg_instance.getTime();
472  _clock_publisher.publish( clock );
473  }
474  }
475  }
476  _previous_play_index = current_index;
477  }
478 }
ValueType any_cast(any const &operand)
Definition: any.hpp:432
const ROSMessageInfo * getMessageInfo(const std::string &msg_identifier) const
bool toPublish(const std::string &topic_name)
void filterDialog(bool autoconfirm)
ROSCPP_DECL bool check()
const Point & at(size_t index) const
Definition: plotdata.h:288
void publish(const boost::shared_ptr< M > &message) const
int getIndexFromX(Time x) const
Definition: plotdata.h:242
virtual void setParentMenu(QMenu *parent_menu, QAction *parent_action)
constexpr pointer data() const noexcept
static RosIntrospectionFactory & get()
const TreeNode< T > * croot() const
void broadcastTF(double current_time)
ros::Publisher _clock_publisher
std::unordered_map< std::string, PlotDataAny > user_defined
Definition: plotdata.h:145
constexpr size_type size() const noexcept
void read(Stream &stream)
void serialize(Stream &stream, const T &t)
uint32_t size() const
ros::Publisher advertise(ros::NodeHandle &nh, const std::string &topic, uint32_t queue_size, bool latch=false, const ros::SubscriberStatusCallback &connect_cb=ros::SubscriberStatusCallback()) const
void setParentMenu(QMenu *menu, QAction *action) override
static RosIntrospection::Parser & parser()
ros::NodeHandlePtr _node
static std::vector< const std::string * > getTopicList()
const PlotDataMapRef * _datamap
std::string const & getDataType() const
virtual ~TopicPublisherROS() override
virtual void updateState(double current_time) override
static ros::NodeHandlePtr getNode()
const char * msg
std::unordered_map< std::string, bool > _topics_to_publish
std::map< std::string, ros::Publisher > _publishers
virtual void setEnabled(bool enabled) override
std::string const & getTopic() const
static RosIntrospection::ShapeShifter * getShapeShifter(const std::string &topic_name)
static Time now()
virtual void play(double interval) override
std::unique_ptr< tf::TransformBroadcaster > _tf_publisher
const std::type_info & type() const any_noexcept
Definition: any.hpp:342
void publishAnyMsg(const rosbag::MessageInstance &msg_instance)
void deserialize(Stream &stream, T &t)
void write(Stream &stream) const
virtual void setEnabled(bool enabled)


plotjuggler
Author(s): Davide Faconti
autogenerated on Sat Jul 6 2019 03:44:18