@@ -80,6 +80,14 @@ rclcpp::QoS publisher_qos_for_topic(
80
80
81
81
namespace rosbag2_transport
82
82
{
83
+
84
+ enum class PlayerStatus : char
85
+ {
86
+ RUNNING = ' R' ,
87
+ PAUSED = ' P' ,
88
+ DELAYED = ' D' ,
89
+ };
90
+
83
91
constexpr Player::callback_handle_t Player::invalid_callback_handle;
84
92
85
93
class PlayerImpl
@@ -123,12 +131,13 @@ class PlayerImpl
123
131
virtual bool set_rate (double );
124
132
125
133
// / \brief Playing next message from queue when in pause.
134
+ // / \param in_burst_mode If true, it will not call the progress bar update to avoid delays.
126
135
// / \details This is blocking call and it will wait until next available message will be
127
136
// / published or rclcpp context shut down.
128
137
// / \note If internal player queue is starving and storage has not been completely loaded,
129
138
// / this method will wait until new element will be pushed to the queue.
130
139
// / \return true if player in pause mode and successfully played next message, otherwise false.
131
- virtual bool play_next ();
140
+ virtual bool play_next (bool in_burst_mode = false );
132
141
133
142
// / \brief Burst the next \p num_messages messages from the queue when paused.
134
143
// / \param num_messages The number of messages to burst from the queue. Specifying zero means no
@@ -298,12 +307,8 @@ class PlayerImpl
298
307
void configure_play_until_timestamp ();
299
308
bool shall_stop_at_timestamp (const rcutils_time_point_value_t & msg_timestamp) const ;
300
309
301
- template <char status>
302
- void print_status () const ;
303
- void check_and_print_status () const ;
304
- void print_running_status () const ;
305
- void print_paused_status () const ;
306
- void print_delayed_status () const ;
310
+ void print_progress_bar_help_str (double progress_bar_print_frequency) const ;
311
+ void print_progress_bar_status (const PlayerStatus & status, bool force_update = false );
307
312
308
313
static constexpr double read_ahead_lower_bound_percentage_ = 0.9 ;
309
314
static const std::chrono::milliseconds queue_read_wait_period_;
@@ -360,7 +365,11 @@ class PlayerImpl
360
365
std::shared_ptr<PlayerServiceClientManager> player_service_client_manager_;
361
366
362
367
// progress bar
363
- const bool disable_progress_bar_;
368
+ const bool enable_progress_bar_;
369
+ const bool progress_bar_update_always_;
370
+ const rcutils_duration_value_t progress_bar_update_period_;
371
+ std::chrono::steady_clock::time_point progress_bar_last_time_printed_{};
372
+ rcutils_time_point_value_t recv_timestamp_last_published_;
364
373
};
365
374
366
375
PlayerImpl::PlayerImpl (
@@ -374,7 +383,11 @@ PlayerImpl::PlayerImpl(
374
383
play_options_(play_options),
375
384
keyboard_handler_(std::move(keyboard_handler)),
376
385
player_service_client_manager_(std::make_shared<PlayerServiceClientManager>()),
377
- disable_progress_bar_(play_options.disable_progress_bar)
386
+ enable_progress_bar_(play_options.progress_bar_print_frequency != 0 ),
387
+ progress_bar_update_always_(play_options.progress_bar_print_frequency < 0 ),
388
+ progress_bar_update_period_(play_options.progress_bar_print_frequency != 0 ?
389
+ RCUTILS_S_TO_NS (1.0 / play_options.progress_bar_print_frequency) :
390
+ std::numeric_limits<int64_t>::max())
378
391
{
379
392
for (auto & topic : play_options_.topics_to_filter ) {
380
393
topic = rclcpp::expand_topic_or_service_name (
@@ -423,6 +436,7 @@ PlayerImpl::PlayerImpl(
423
436
}
424
437
starting_time_secs_ = RCUTILS_NS_TO_S (static_cast <double >(starting_time_));
425
438
duration_secs_ = RCUTILS_NS_TO_S (static_cast <double >(bag_duration));
439
+ recv_timestamp_last_published_ = starting_time_;
426
440
clock_ = std::make_unique<rosbag2_cpp::TimeControllerClock>(
427
441
starting_time_, std::chrono::steady_clock::now,
428
442
std::chrono::milliseconds{100 }, play_options_.start_paused );
@@ -433,13 +447,7 @@ PlayerImpl::PlayerImpl(
433
447
}
434
448
create_control_services ();
435
449
add_keyboard_callbacks ();
436
- std::string progress_bar_help_str;
437
- if (!disable_progress_bar_) {
438
- progress_bar_help_str = " Bag Time and Duration [?]: ? = R running, P paused, D delayed" ;
439
- } else {
440
- progress_bar_help_str = " Bag Time and Duration progress bar disabled." ;
441
- }
442
- RCLCPP_INFO_STREAM (owner_->get_logger (), progress_bar_help_str);
450
+ print_progress_bar_help_str (play_options.progress_bar_print_frequency );
443
451
}
444
452
445
453
PlayerImpl::~PlayerImpl ()
@@ -460,10 +468,9 @@ PlayerImpl::~PlayerImpl()
460
468
if (reader_) {
461
469
reader_->close ();
462
470
}
463
- // print new line to keep on screen the latest progress bar
464
- if (!disable_progress_bar_) {
465
- printf (" \n " );
466
- fflush (stdout);
471
+ // print new line to keep on screen the latest progress bar (which had a carriage return)
472
+ if (enable_progress_bar_) {
473
+ std::cout << std::endl;
467
474
}
468
475
}
469
476
@@ -515,9 +522,12 @@ bool PlayerImpl::play()
515
522
do {
516
523
if (delay > rclcpp::Duration (0 , 0 )) {
517
524
RCLCPP_INFO_STREAM (owner_->get_logger (), " Sleep " << delay.nanoseconds () << " ns" );
518
- print_delayed_status ( );
525
+ print_progress_bar_status (PlayerStatus::DELAYED );
519
526
std::chrono::nanoseconds delay_duration (delay.nanoseconds ());
520
527
std::this_thread::sleep_for (delay_duration);
528
+ } else {
529
+ print_progress_bar_status (clock_->is_paused () ?
530
+ PlayerStatus::PAUSED : PlayerStatus::RUNNING, true );
521
531
}
522
532
{
523
533
std::lock_guard<std::mutex> lk (reader_mutex_);
@@ -582,6 +592,8 @@ bool PlayerImpl::play()
582
592
is_in_playback_ = false ;
583
593
playback_finished_cv_.notify_all ();
584
594
}
595
+ print_progress_bar_status (clock_->is_paused () ?
596
+ PlayerStatus::PAUSED : PlayerStatus::RUNNING, true );
585
597
});
586
598
return true ;
587
599
}
@@ -632,21 +644,22 @@ void PlayerImpl::stop()
632
644
playback_thread_.join ();
633
645
}
634
646
}
635
- check_and_print_status ();
647
+ print_progress_bar_status (clock_->is_paused () ?
648
+ PlayerStatus::PAUSED : PlayerStatus::RUNNING, true );
636
649
}
637
650
638
651
void PlayerImpl::pause ()
639
652
{
640
653
clock_->pause ();
641
654
RCLCPP_INFO_STREAM (owner_->get_logger (), " Pausing play." );
642
- print_paused_status ( );
655
+ print_progress_bar_status (PlayerStatus::PAUSED );
643
656
}
644
657
645
658
void PlayerImpl::resume ()
646
659
{
647
660
clock_->resume ();
648
661
RCLCPP_INFO_STREAM (owner_->get_logger (), " Resuming play." );
649
- print_running_status ( );
662
+ print_progress_bar_status (PlayerStatus::RUNNING, true );
650
663
}
651
664
652
665
void PlayerImpl::toggle_paused ()
@@ -673,7 +686,8 @@ bool PlayerImpl::set_rate(double rate)
673
686
} else {
674
687
RCLCPP_WARN_STREAM (owner_->get_logger (), " Failed to set rate to invalid value " << rate);
675
688
}
676
- check_and_print_status ();
689
+ print_progress_bar_status (clock_->is_paused () ?
690
+ PlayerStatus::PAUSED : PlayerStatus::RUNNING, true );
677
691
return ok;
678
692
}
679
693
@@ -706,10 +720,11 @@ rosbag2_storage::SerializedBagMessageSharedPtr PlayerImpl::peek_next_message_fro
706
720
return nullptr ;
707
721
}
708
722
709
- bool PlayerImpl::play_next ()
723
+ bool PlayerImpl::play_next (bool is_burst_mode )
710
724
{
711
725
if (!clock_->is_paused ()) {
712
726
RCLCPP_WARN_STREAM (owner_->get_logger (), " Called play next, but not in paused state." );
727
+ print_progress_bar_status (PlayerStatus::RUNNING, true );
713
728
return false ;
714
729
}
715
730
@@ -722,6 +737,7 @@ bool PlayerImpl::play_next()
722
737
// resume() or stop() from another thread while we were waiting on mutex.
723
738
if (!clock_->is_paused ()) {
724
739
RCLCPP_WARN_STREAM (owner_->get_logger (), " Called play next, but not in paused state." );
740
+ print_progress_bar_status (PlayerStatus::RUNNING, true );
725
741
return false ;
726
742
}
727
743
skip_message_in_main_play_loop_ = true ;
@@ -741,7 +757,11 @@ bool PlayerImpl::play_next()
741
757
next_message_published = publish_message (message_ptr);
742
758
clock_->jump (message_ptr->recv_timestamp );
743
759
744
- print_paused_status ();
760
+ // Update the progress bar if we were not called in burst mode to avoid delays.
761
+ if (!is_burst_mode) {
762
+ recv_timestamp_last_published_ = message_ptr->recv_timestamp ;
763
+ print_progress_bar_status (PlayerStatus::PAUSED);
764
+ }
745
765
message_queue_.pop ();
746
766
message_ptr = peek_next_message_from_queue ();
747
767
}
@@ -752,22 +772,23 @@ size_t PlayerImpl::burst(const size_t num_messages)
752
772
{
753
773
if (!clock_->is_paused ()) {
754
774
RCLCPP_WARN_STREAM (owner_->get_logger (), " Burst can only be used when in the paused state." );
755
- print_running_status ( );
775
+ print_progress_bar_status (PlayerStatus::RUNNING, true );
756
776
return 0 ;
757
777
}
758
778
759
779
uint64_t messages_played = 0 ;
760
780
761
781
for (auto ii = 0u ; ii < num_messages || num_messages == 0 ; ++ii) {
762
- if (play_next ()) {
782
+ if (play_next (true )) {
763
783
++messages_played;
764
784
} else {
765
785
break ;
766
786
}
767
787
}
768
788
769
789
RCLCPP_INFO_STREAM (owner_->get_logger (), " Burst " << messages_played << " messages." );
770
- print_running_status ();
790
+ print_progress_bar_status (clock_->is_paused () ?
791
+ PlayerStatus::PAUSED : PlayerStatus::RUNNING, true );
771
792
return messages_played;
772
793
}
773
794
@@ -993,7 +1014,8 @@ void PlayerImpl::play_messages_from_queue()
993
1014
continue ;
994
1015
}
995
1016
publish_message (message_ptr);
996
- print_running_status ();
1017
+ recv_timestamp_last_published_ = message_ptr->recv_timestamp ;
1018
+ print_progress_bar_status (PlayerStatus::RUNNING);
997
1019
}
998
1020
message_queue_.pop ();
999
1021
message_ptr = peek_next_message_from_queue ();
@@ -1565,48 +1587,57 @@ const rosbag2_transport::PlayOptions & PlayerImpl::get_play_options()
1565
1587
return play_options_;
1566
1588
}
1567
1589
1568
- template <char status>
1569
- void PlayerImpl::print_status () const
1570
- {
1571
- static_assert (status == ' R' || status == ' P' || status == ' D' ,
1572
- " Invalid status character" );
1573
- double current_time_secs = RCUTILS_NS_TO_S (static_cast <double >(clock_->now ()));
1574
- double progress_secs = current_time_secs - starting_time_secs_;
1575
- printf (" Bag Time %13.6f Duration %.6f/%.6f [%c] \r " ,
1576
- current_time_secs, progress_secs, duration_secs_, status);
1577
- fflush (stdout);
1578
- }
1579
-
1580
- void PlayerImpl::check_and_print_status () const
1590
+ void PlayerImpl::print_progress_bar_help_str (double progress_bar_print_frequency) const
1581
1591
{
1582
- if (!disable_progress_bar_) {
1583
- if (clock_->is_paused ()) {
1584
- print_status<' P' >();
1592
+ std::string progress_bar_help_str;
1593
+ if (enable_progress_bar_) {
1594
+ if (progress_bar_update_period_ < 0 ) {
1595
+ progress_bar_help_str = " Progress bar enabled with updates for every message." ;
1585
1596
} else {
1586
- print_status<' R' >();
1597
+ std::ostringstream oss;
1598
+ oss << " Progress bar enabled with updates at "
1599
+ << std::fixed << std::setprecision (1 ) << progress_bar_print_frequency << " Hz." ;
1600
+ progress_bar_help_str = oss.str ();
1587
1601
}
1602
+ progress_bar_help_str += " [?]: [R]unning, [P]aused, [D]elayed." ;
1603
+ } else {
1604
+ progress_bar_help_str = " Progress bar disabled." ;
1588
1605
}
1606
+ RCLCPP_INFO_STREAM (owner_->get_logger (), progress_bar_help_str);
1589
1607
}
1590
1608
1591
- void PlayerImpl::print_running_status () const
1609
+ void PlayerImpl::print_progress_bar_status ( const PlayerStatus & status, bool force_update)
1592
1610
{
1593
- if (!disable_progress_bar_ ) {
1594
- print_status< ' R ' >() ;
1611
+ if (!enable_progress_bar_ ) {
1612
+ return ;
1595
1613
}
1596
- }
1597
1614
1598
- void PlayerImpl::print_paused_status () const
1599
- {
1600
- if (!disable_progress_bar_) {
1601
- print_status<' P' >();
1602
- }
1603
- }
1615
+ const std::chrono::steady_clock::time_point steady_time_now = std::chrono::steady_clock::now ();
1604
1616
1605
- void PlayerImpl::print_delayed_status () const
1606
- {
1607
- if (!disable_progress_bar_) {
1608
- print_status<' D' >();
1617
+ // Check if we should force an update of the progress bar, otherwise check time since last update.
1618
+ // Force an update if:
1619
+ // - the user-defined update period is negative;
1620
+ // - or this function was called with a status other than RUNNING (since we want to update the
1621
+ // progress bar when we pause or delay the playback, otherwise it will be stuck
1622
+ // to the last update if not enough time has passed since it);
1623
+ // - or this function was called with force_update set to true.
1624
+ if (!(progress_bar_update_always_ || status != PlayerStatus::RUNNING || force_update)) {
1625
+ if (std::chrono::duration_cast<std::chrono::nanoseconds>(
1626
+ steady_time_now - progress_bar_last_time_printed_).count () < progress_bar_update_period_)
1627
+ {
1628
+ return ;
1629
+ }
1609
1630
}
1631
+
1632
+ const double current_time_secs = RCUTILS_NS_TO_S (
1633
+ static_cast <double >(recv_timestamp_last_published_));
1634
+ const double progress_secs = current_time_secs - starting_time_secs_;
1635
+ // Print progress bar ending witt carriage return '/r' so it will be overwritten by next print
1636
+ std::cout << " Bag Time " << std::setw (13 ) << std::fixed << std::setprecision (6 ) <<
1637
+ current_time_secs << " Duration " << std::fixed << std::setprecision (6 ) <<
1638
+ progress_secs << " /" << duration_secs_ << " [" << static_cast <char >(status) <<
1639
+ " ]\r " << std::flush;
1640
+ progress_bar_last_time_printed_ = steady_time_now;
1610
1641
}
1611
1642
1612
1643
// /////////////////////////////
0 commit comments