4 #include <boost/program_options.hpp>
8 #include <fmt/chrono.h>
14 #include <rosbag_fancy_msgs/PlayStatus.h>
15 #include <std_srvs/Trigger.h>
17 #include <rosgraph_msgs/Clock.h>
25 namespace po = boost::program_options;
27 using namespace rosbag_fancy_msgs;
29 int play(
const std::vector<std::string>& options)
35 po::options_description desc(
"Options");
37 (
"help",
"Display this help message")
38 (
"clock",
"Publish clock (requires use_sim_time)")
39 (
"no-ui",
"Disable terminal UI")
42 po::options_description hidden(
"Hidden");
44 (
"input", po::value<std::vector<std::string>>()->required(),
"Input file")
47 po::options_description all(
"All");
48 all.add(desc).add(hidden);
50 po::positional_options_description p;
54 std::cout <<
"Usage: rosbag_fancy play [options] <bag file(s)>\n\n";
55 std::cout << desc <<
"\n\n";
61 po::command_line_parser(options).options(all).positional(p).
run(),
75 std::cerr <<
"Could not parse arguments: " << e.what() <<
"\n\n";
81 bool publishClock = vm.count(
"clock");
87 pub_clock =
ros::NodeHandle{}.advertise<rosgraph_msgs::Clock>(
"/clock", 1,
true);
91 explicit Bag(
const std::string& filename)
92 : reader{filename}, filename_ ( filename )
96 std::string filename_;
97 std::unordered_map<int, ros::Publisher> publishers;
100 std::vector<Bag> bags;
103 std::map<std::string, int> topicMap;
110 for(
auto& filename : vm[
"input"].as<std::vector<std::string>>())
112 auto& bag = bags.emplace_back(filename);
114 if(startTime ==
ros::Time(0) || bag.reader.startTime() < startTime)
115 startTime = bag.reader.startTime();
117 if(endTime ==
ros::Time(0) || bag.reader.endTime() > endTime)
118 endTime = bag.reader.endTime();
120 for(
auto& [
id, con] : bag.reader.connections())
126 opts.
topic = con.topicInBag;
127 opts.
latch = con.latching;
133 auto it = topicMap.find(con.topicInBag);
134 if(it == topicMap.end())
136 topicMap[con.topicInBag] = topicManager.
topics().size();
137 topicManager.
addTopic(con.topicInBag);
142 std::vector<BagReader*> bagReaders;
143 for(
auto& bag : bags)
144 bagReaders.push_back(&bag.reader);
152 return connection.topicInBag !=
"/tf_static";
155 for(
auto& bag : bags)
156 view.
addBag(&bag.reader, topicPredicate);
164 std::unique_ptr<PlaybackUI> ui;
170 if(!vm.count(
"no-ui"))
172 ui.reset(
new PlaybackUI{topicManager, startTime, endTime});
174 ui->seekForwardRequested.connect([&](){
178 it = view.findTime(currentTime);
180 ui->seekBackwardRequested.connect([&](){
184 if(currentTime < startTime)
186 currentTime = startTime;
187 bagRefTime = currentTime;
191 it = view.findTime(currentTime);
193 ui->pauseRequested.connect([&](){
195 bagRefTime = currentTime;
198 ui->exitRequested.connect([&](){
206 resp.success = !paused;
208 ui->setPaused(paused);
213 resp.success = paused;
215 ui->setPaused(paused);
221 ui->setPaused(paused);
229 auto msg = boost::make_shared<PlayStatus>();
232 msg->status = paused ? PlayStatus::STATUS_PAUSED : PlayStatus::STATUS_RUNNING;
234 msg->currentTime = currentTime;
235 msg->startTime = startTime;
236 msg->endTime = endTime;
237 msg->current = currentTime-startTime;
238 msg->duration = endTime-startTime;
240 msg->bagfiles.reserve(bags.size());
241 for(
const auto& bag : bags)
242 msg->bagfiles.emplace_back(bag.filename_);
244 msg->topics.reserve(topicManager.
topics().size());
245 for(
const auto& topic : topicManager.
topics())
247 msg->topics.emplace_back();
248 auto& topicMsg = msg->topics.back();
250 topicMsg.name = topic.name;
251 topicMsg.bandwidth = topic.bandwidth;
252 topicMsg.rate = topic.messageRate;
267 auto& msg = *it->
msg;
269 if(msg.stamp < bagRefTime)
275 currentTime = msg.stamp;
277 ui->setPositionInBag(msg.stamp);
279 bags[it->
bagIndex].publishers[msg.connection->id].publish(msg);
281 auto& topic = topicManager.
topics()[topicMap[msg.connection->topicInBag]];
282 topic.notifyMessage(msg.size());
286 rosgraph_msgs::Clock msg;
287 msg.clock = currentTime;
289 lastClockPublishTime = wallStamp;
293 if(
auto tfmsg = tf2Scanner.fetchUpdate(currentTime))
306 FD_SET(STDIN_FILENO, &fds);
308 int ret = select(STDIN_FILENO+1, &fds,
nullptr,
nullptr, &timeout);
311 if(errno != EAGAIN && errno != EINTR)
312 throw std::runtime_error{
fmt::format(
"select() error: {}", strerror(errno))};
317 ui->setPaused(paused);