2828
2929import threading
3030import time
31+ from typing import Callable , Iterable , Iterator , Optional , Tuple , Union
3132
3233from python_qt_binding .QtCore import qDebug , Qt , QTimer , qWarning , Signal
3334from python_qt_binding .QtWidgets import QGraphicsScene , QMessageBox
4344from .message_loader_thread import MessageLoaderThread
4445from .player import Player
4546from .recorder import Recorder
47+ from .rosbag2 import Entry , Rosbag2
4648from .timeline_frame import TimelineFrame
4749
4850
@@ -67,6 +69,7 @@ def __init__(self, context, publish_clock):
6769 super (BagTimeline , self ).__init__ ()
6870 self ._bags = []
6971 self ._bag_lock = threading .RLock ()
72+ self ._bags_size = 0
7073
7174 self .background_task = None # Display string
7275 self .background_task_cancel = False
@@ -158,6 +161,7 @@ def add_bag(self, bag):
158161 :param bag: ros bag file, ''rosbag2.bag''
159162 """
160163 self ._bags .append (bag )
164+ self ._bags_size += bag .size ()
161165
162166 bag_topics = bag .get_topics ()
163167 qDebug ('Topics from this bag: {}' .format (bag_topics ))
@@ -187,8 +191,7 @@ def add_bag(self, bag):
187191 self ._timeline_frame .index_cache_cv .notify ()
188192
189193 def file_size (self ):
190- with self ._bag_lock :
191- return sum (b .size () for b in self ._bags )
194+ return self ._bags_size
192195
193196 # TODO Rethink API and if these need to be visible
194197 def _get_start_stamp (self ):
@@ -264,47 +267,37 @@ def get_datatype(self, topic):
264267 datatype = bag_datatype
265268 return datatype
266269
267- def get_entries (self , topics , start_stamp , end_stamp ):
270+ def get_entries (self , topics : Optional [Union [str , Iterable [str ]]],
271+ start_stamp : Time , end_stamp : Time ,
272+ progress_cb : Optional [Callable [[int ], None ]] = None ) -> Iterator [Entry ]:
268273 """
269274 Get a generator for bag entries.
270275
271276 :param topics: list of topics to query, ''list(str)''
272277 :param start_stamp: stamp to start at, ''rclpy.time.Time''
273- :param end_stamp: stamp to end at, ''rclpy.time,Time''
278+ :param end_stamp: stamp to end at, ''rclpy.time.Time''
279+ :param progress_cb: callback function to report progress, called once per each percent.
274280 :returns: entries the bag file, ''msg''
275281 """
276- with self ._bag_lock :
277- bag_entries = []
278- for b in self ._bags :
279- bag_start_time = b .get_earliest_timestamp ()
280- if bag_start_time is not None and bag_start_time > end_stamp :
281- continue
282+ for b , entry in self .get_entries_with_bags (topics , start_stamp , end_stamp , progress_cb ):
283+ yield entry
284+ return None
282285
283- bag_end_time = b .get_latest_timestamp ()
284- if bag_end_time is not None and bag_end_time < start_stamp :
285- continue
286-
287- # Get all of the entries for each topic. When opening multiple
288- # bags, the requested topic may not be in a given bag database
289- for topic in topics :
290- entries = b .get_entries_in_range (start_stamp , end_stamp , topic )
291- if entries is not None :
292- bag_entries .extend (entries )
293-
294- for entry in sorted (bag_entries , key = lambda entry : entry .timestamp ):
295- yield entry
296-
297- def get_entries_with_bags (self , topic , start_stamp , end_stamp ):
286+ def get_entries_with_bags (self , topics : Optional [Union [str , Iterable [str ]]],
287+ start_stamp : Time , end_stamp : Time ,
288+ progress_cb : Optional [Callable [[int ], None ]] = None ) \
289+ -> Iterator [Tuple [Rosbag2 , Entry ]]:
298290 """
299291 Get a generator of bag entries.
300292
301- :param topics: list of topics to query, ''list(str)''
293+ :param topics: list of topics to query (if None, all topics are used) , ''list(str)''
302294 :param start_stamp: stamp to start at, ''rclpy.time.Time''
303- :param end_stamp: stamp to end at, ''rclpy.time,Time''
304- :returns: tuple of (bag, entry) for the entries in the bag file, ''(rosbag2.bag, msg)''
295+ :param end_stamp: stamp to end at, ''rclpy.time.Time''
296+ :param progress_cb: callback function to report progress, called once per each percent.
297+ :returns: tuple of (bag, entry) for the entries in the bag file, ''(rosbag2.Rosbag2, msg)''
305298 """
306299 with self ._bag_lock :
307- bag_entries = []
300+ relevant_bags = []
308301 for b in self ._bags :
309302 bag_start_time = b .get_earliest_timestamp ()
310303 if bag_start_time is not None and bag_start_time > end_stamp :
@@ -314,11 +307,61 @@ def get_entries_with_bags(self, topic, start_stamp, end_stamp):
314307 if bag_end_time is not None and bag_end_time < start_stamp :
315308 continue
316309
317- for entry in b .get_entries_in_range (start_stamp , end_stamp ):
318- bag_entries .append ((b , entry ))
310+ relevant_bags .append (b )
311+
312+ generators = {}
313+ last_entries = {}
314+ for b in relevant_bags :
315+ generators [b ] = b .entries_in_range_generator (start_stamp , end_stamp , topics )
316+ try :
317+ last_entries [b ] = next (generators [b ])
318+ except StopIteration :
319+ last_entries [b ] = None
320+
321+ to_delete = []
322+ for b in last_entries :
323+ if last_entries [b ] is None :
324+ to_delete .append (b )
325+
326+ for b in to_delete :
327+ del last_entries [b ]
328+ del generators [b ]
329+ relevant_bags .remove (b )
330+
331+ if progress_cb is not None :
332+ progress = 0
333+ num_entries = 0
334+ estimated_num_entries = 0
335+ for b in relevant_bags :
336+ estimated_num_entries += b .estimate_num_entries_in_range (
337+ start_stamp , end_stamp , topics )
338+
339+ while any (last_entries .values ()):
340+ min_bag = None
341+ min_entry = None
342+ for b , entry in last_entries .items ():
343+ if entry is not None :
344+ if min_entry is None or entry .timestamp < min_entry .timestamp :
345+ min_bag = b
346+ min_entry = entry
347+ if min_bag is None :
348+ return
349+
350+ if progress_cb is not None :
351+ num_entries += 1
352+ new_progress = int (100.0 * (float (num_entries ) / estimated_num_entries ))
353+ if new_progress != progress :
354+ progress_cb (new_progress )
355+ progress = new_progress
356+
357+ yield min_bag , min_entry
358+
359+ try :
360+ last_entries [min_bag ] = next (generators [min_bag ])
361+ except StopIteration :
362+ last_entries [min_bag ] = None
319363
320- for bag , entry in sorted (bag_entries , key = lambda item : item [1 ].timestamp ):
321- yield bag , entry
364+ return
322365
323366 def get_entry (self , t , topic ):
324367 """
0 commit comments