2021-11-19 15:19:35 -08:00
|
|
|
use std::{collections::HashMap, convert::TryFrom, env::args, fs::File, io};
|
2021-11-18 15:11:56 -08:00
|
|
|
|
2021-11-19 18:01:25 -08:00
|
|
|
use eyre::{bail, Context};
|
2021-11-18 15:11:56 -08:00
|
|
|
use log::{error, info, trace};
|
2021-11-18 22:50:42 -08:00
|
|
|
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
2021-11-18 15:56:02 -08:00
|
|
|
use regex::Regex;
|
|
|
|
use ros_message::{MessagePath, Msg};
|
|
|
|
use rsbag::{
|
2021-11-19 18:01:25 -08:00
|
|
|
chunk::{ChunkHeader, MessageDataHeader},
|
2021-11-19 15:19:35 -08:00
|
|
|
index::{BagIndex, ConnInfo, IndexData},
|
2021-11-19 18:01:25 -08:00
|
|
|
reader::{BagReader, MmapReader, SliceReader},
|
2021-11-19 14:15:15 -08:00
|
|
|
Result,
|
2021-11-18 15:56:02 -08:00
|
|
|
};
|
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
fn parse_msgdef(message_name: &str, msgdef: &str) -> Result<Msg> {
|
2021-11-18 15:56:02 -08:00
|
|
|
trace!("message definition: {}", msgdef);
|
2021-11-19 14:15:15 -08:00
|
|
|
let path = MessagePath::try_from(message_name)?;
|
|
|
|
let msgtype = Msg::new(path, msgdef)?;
|
2021-11-18 15:56:02 -08:00
|
|
|
Ok(msgtype)
|
|
|
|
}
|
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
fn parse_message_definitions(conn: &ConnInfo) -> Result<Vec<Msg>> {
|
|
|
|
let msgdefs = &conn.message_definition;
|
|
|
|
let boundary_re = Regex::new(r"\r?\n==+\r?\nMSG: ([^\r\n]+)\r?\n")?;
|
|
|
|
let mut name = conn.datatype.clone();
|
2021-11-18 15:56:02 -08:00
|
|
|
let mut begin = 0usize;
|
|
|
|
let mut msgs = Vec::new();
|
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
for cap in boundary_re.captures_iter(msgdefs) {
|
2021-11-18 15:56:02 -08:00
|
|
|
let boundary_range = cap.get(0).unwrap();
|
|
|
|
let end = boundary_range.start();
|
|
|
|
|
|
|
|
let msgdef = &msgdefs[begin..end];
|
|
|
|
let msgtype = parse_msgdef(&name, msgdef)?;
|
|
|
|
msgs.push(msgtype);
|
|
|
|
|
|
|
|
name = cap[1].to_string();
|
|
|
|
begin = boundary_range.end();
|
|
|
|
}
|
|
|
|
|
|
|
|
let msgdef = &msgdefs[begin..];
|
|
|
|
|
|
|
|
let msg = parse_msgdef(&name, msgdef)?;
|
|
|
|
msgs.push(msg);
|
|
|
|
|
|
|
|
Ok(msgs)
|
|
|
|
}
|
2021-11-18 15:11:56 -08:00
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
fn read_chunk<R: BagReader>(bag_reader: &mut R, pos: u64) -> Result<Vec<u8>> {
|
2021-11-19 18:01:25 -08:00
|
|
|
bag_reader.seek(io::SeekFrom::Start(pos))?;
|
|
|
|
|
|
|
|
let chunk_header = ChunkHeader::read(bag_reader)?;
|
2021-11-18 22:50:42 -08:00
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
let compressed_data = bag_reader.read_data()?;
|
2021-11-18 22:50:42 -08:00
|
|
|
let mut data = Vec::with_capacity(chunk_header.uncompressed_size as usize);
|
2021-11-19 18:01:25 -08:00
|
|
|
let mut decompresor = chunk_header.compression.decompress_stream(compressed_data);
|
2021-11-19 14:15:15 -08:00
|
|
|
io::copy(&mut decompresor, &mut data)?;
|
2021-11-18 22:50:42 -08:00
|
|
|
|
|
|
|
Ok(data)
|
|
|
|
}
|
|
|
|
|
2021-11-19 15:19:35 -08:00
|
|
|
#[derive(Default, Debug)]
|
|
|
|
struct BagInfo {
|
|
|
|
total_uncompressed: u64,
|
|
|
|
per_connection: HashMap<u32, u64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BagInfo {
|
|
|
|
fn combine(mut self, other: BagInfo) -> BagInfo {
|
|
|
|
self.total_uncompressed += other.total_uncompressed;
|
|
|
|
for (conn, count) in other.per_connection {
|
|
|
|
*self.per_connection.entry(conn).or_insert(0) += count;
|
|
|
|
}
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
fn main() -> Result<()> {
|
|
|
|
color_eyre::install()?;
|
2021-11-18 15:11:56 -08:00
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
let args: Vec<_> = args().collect();
|
|
|
|
if args.len() != 2 {
|
|
|
|
eprintln!("Usage: {} <bag path>", args[0]);
|
2021-11-19 14:15:15 -08:00
|
|
|
return Ok(());
|
2021-11-18 15:11:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
let bag_path = &args[1];
|
|
|
|
let bag_file = File::open(bag_path).expect("Could not open bag file");
|
2021-11-19 18:01:25 -08:00
|
|
|
let mut bag_reader = MmapReader::map(bag_file)?;
|
2021-11-18 21:05:46 -08:00
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
let index = BagIndex::read_all(&mut bag_reader).wrap_err("bag parse error")?;
|
2021-11-18 21:05:46 -08:00
|
|
|
|
|
|
|
for conn in &index.connections {
|
|
|
|
match parse_message_definitions(conn) {
|
|
|
|
Ok(msgs) => {
|
|
|
|
for msg in &msgs {
|
2021-11-18 22:50:42 -08:00
|
|
|
trace!(
|
2021-11-18 21:05:46 -08:00
|
|
|
"message definition parsed: {:#?}",
|
|
|
|
msg.fields()
|
|
|
|
.iter()
|
|
|
|
.filter(|field| !field.is_constant())
|
|
|
|
.map(ToString::to_string)
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
);
|
2021-11-18 15:11:56 -08:00
|
|
|
}
|
|
|
|
}
|
2021-11-18 21:05:46 -08:00
|
|
|
Err(err) => error!("could not parse message definition: {}", err),
|
2021-11-18 15:56:02 -08:00
|
|
|
}
|
2021-11-18 15:11:56 -08:00
|
|
|
}
|
2021-11-18 21:05:46 -08:00
|
|
|
|
2021-11-19 18:01:25 -08:00
|
|
|
let info = index
|
2021-11-18 22:50:42 -08:00
|
|
|
.chunks
|
|
|
|
.par_iter()
|
2021-11-19 18:01:25 -08:00
|
|
|
// .try_fold(BagInfo::default, |mut info, chunk| -> rsbag::Result<_> {
|
|
|
|
// let mut reader = bag_reader.clone();
|
|
|
|
// let chunk_header = ChunkHeader::read(&mut reader, chunk.pos)?;
|
|
|
|
// info.total_uncompressed += chunk_header.uncompressed_size as u64;
|
|
|
|
// reader.skip_data()?;
|
|
|
|
// for _ in &chunk.connections {
|
|
|
|
// let index = IndexData::read(&mut reader)?;
|
|
|
|
// *info.per_connection.entry(index.conn_id).or_insert(0) +=
|
|
|
|
// index.entries.len() as u64;
|
|
|
|
// }
|
|
|
|
// Ok(info)
|
|
|
|
// })
|
|
|
|
.try_fold(BagInfo::default, |mut info, chunk| -> rsbag::Result<_> {
|
2021-11-19 15:19:35 -08:00
|
|
|
let mut reader = bag_reader.clone();
|
2021-11-19 18:01:25 -08:00
|
|
|
|
|
|
|
let data = read_chunk(&mut reader, chunk.pos)
|
|
|
|
.wrap_err_with(|| format!("failed to read chunk: {:#?}", chunk))?;
|
|
|
|
info.total_uncompressed += data.len() as u64;
|
|
|
|
|
|
|
|
let mut chunk_reader = SliceReader::from(data);
|
|
|
|
|
|
|
|
loop {
|
|
|
|
if chunk_reader.pos() == chunk_reader.as_ref().len() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let header = chunk_reader.read_header()?;
|
|
|
|
let op = header.read_op()?;
|
|
|
|
match op {
|
|
|
|
rsbag::parse::Op::MsgData => {
|
|
|
|
let header = MessageDataHeader::from_header(header)?;
|
|
|
|
let count = info.per_connection.entry(header.conn_id).or_insert(0);
|
|
|
|
*count += 1;
|
|
|
|
chunk_reader.skip_data()?;
|
|
|
|
}
|
|
|
|
rsbag::parse::Op::Connection => chunk_reader.skip_data()?,
|
|
|
|
_ => bail!("unexpected op in chunk: {:?}", op),
|
|
|
|
}
|
2021-11-19 15:19:35 -08:00
|
|
|
}
|
|
|
|
|
2021-11-19 18:01:25 -08:00
|
|
|
Ok(info)
|
2021-11-19 15:19:35 -08:00
|
|
|
})
|
|
|
|
.try_reduce(BagInfo::default, |a, b| Ok(a.combine(b)))
|
2021-11-19 14:15:15 -08:00
|
|
|
.unwrap();
|
|
|
|
|
2021-11-19 18:01:25 -08:00
|
|
|
info!("bag info: {:#?}", info);
|
2021-11-18 21:05:46 -08:00
|
|
|
|
2021-11-19 14:15:15 -08:00
|
|
|
Ok(())
|
2021-11-18 15:11:56 -08:00
|
|
|
}
|