Add conversion from rosbag2 to rosbag1

This commit is contained in:
Marko Durkovic
2022-01-09 20:31:51 +01:00
parent ac55fd2f4a
commit 2eb3b1c671
6 changed files with 269 additions and 42 deletions
+8 -3
View File
@@ -39,18 +39,23 @@ def pathtype(exists: bool = True) -> Callable[[str], Path]:
def main() -> None:
"""Parse cli arguments and run conversion."""
parser = argparse.ArgumentParser(description='Convert rosbag1 to rosbag2.')
parser = argparse.ArgumentParser(description='Convert between rosbag1 and rosbag2.')
parser.add_argument(
'src',
type=pathtype(),
help='source path to read rosbag1 from',
help='source path to read rosbag1 or rosbag2 from',
)
parser.add_argument(
'--dst',
type=pathtype(exists=False),
help='destination path for rosbag2',
help='destination path for converted rosbag',
)
args = parser.parse_args()
if args.dst is not None and (args.src.suffix == '.bag') == (args.dst.suffix == '.bag'):
print('Source and destination rosbag versions must differ.') # noqa: T001
sys.exit(1)
try:
convert(args.src, args.dst)
except ConverterError as err:
+100 -27
View File
@@ -7,18 +7,24 @@ from __future__ import annotations
from dataclasses import asdict
from typing import TYPE_CHECKING
from rosbags.rosbag1 import Reader, ReaderError
from rosbags.rosbag2 import Writer, WriterError
from rosbags.rosbag2.connection import Connection as WConnection
from rosbags.serde import ros1_to_cdr
from rosbags.rosbag1 import Reader as Reader1
from rosbags.rosbag1 import ReaderError as ReaderError1
from rosbags.rosbag1 import Writer as Writer1
from rosbags.rosbag1 import WriterError as WriterError1
from rosbags.rosbag1.reader import Connection as Connection1
from rosbags.rosbag2 import Reader as Reader2
from rosbags.rosbag2 import ReaderError as ReaderError2
from rosbags.rosbag2 import Writer as Writer2
from rosbags.rosbag2 import WriterError as WriterError2
from rosbags.rosbag2.connection import Connection as Connection2
from rosbags.serde import cdr_to_ros1, ros1_to_cdr
from rosbags.typesys import get_types_from_msg, register_types
from rosbags.typesys.msg import generate_msgdef
if TYPE_CHECKING:
from pathlib import Path
from typing import Any, Optional
from rosbags.rosbag1.reader import Connection as RConnection
LATCH = """
- history: 3
depth: 0
@@ -42,7 +48,7 @@ class ConverterError(Exception):
"""Converter Error."""
def convert_connection(rconn: RConnection) -> WConnection:
def upgrade_connection(rconn: Connection1) -> Connection2:
"""Convert rosbag1 connection to rosbag2 connection.
Args:
@@ -52,7 +58,7 @@ def convert_connection(rconn: RConnection) -> WConnection:
Rosbag2 connection.
"""
return WConnection(
return Connection2(
-1,
0,
rconn.topic,
@@ -62,41 +68,108 @@ def convert_connection(rconn: RConnection) -> WConnection:
)
def convert(src: Path, dst: Optional[Path]) -> None:
def downgrade_connection(rconn: Connection2) -> Connection1:
"""Convert rosbag2 connection to rosbag1 connection.
Args:
rconn: Rosbag2 connection.
Returns:
Rosbag1 connection.
"""
msgdef, md5sum = generate_msgdef(rconn.msgtype)
return Connection1(
-1,
rconn.topic,
rconn.msgtype,
msgdef,
md5sum,
None,
int('durability: 1' in rconn.offered_qos_profiles),
[],
)
def convert_1to2(src: Path, dst: Path) -> None:
"""Convert Rosbag1 to Rosbag2.
Args:
src: Rosbag1 path.
dst: Rosbag2 path.
"""
with Reader1(src) as reader, Writer2(dst) as writer:
typs: dict[str, Any] = {}
connmap: dict[int, Connection2] = {}
for rconn in reader.connections.values():
candidate = upgrade_connection(rconn)
existing = next((x for x in writer.connections.values() if x == candidate), None)
wconn = existing if existing else writer.add_connection(**asdict(candidate))
connmap[rconn.cid] = wconn
typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype))
register_types(typs)
for rconn, timestamp, data in reader.messages():
data = ros1_to_cdr(data, rconn.msgtype)
writer.write(connmap[rconn.cid], timestamp, data)
def convert_2to1(src: Path, dst: Path) -> None:
"""Convert Rosbag2 to Rosbag1.
Args:
src: Rosbag2 path.
dst: Rosbag1 path.
"""
with Reader2(src) as reader, Writer1(dst) as writer:
connmap: dict[int, Connection1] = {}
for rconn in reader.connections.values():
candidate = downgrade_connection(rconn)
# yapf: disable
existing = next(
(
x
for x in writer.connections.values()
if x.topic == candidate.topic
if x.md5sum == candidate.md5sum
if x.latching == candidate.latching
),
None,
)
# yapf: enable
connmap[rconn.id] = existing if existing else writer.add_connection(*candidate[1:-1])
for rconn, timestamp, data in reader.messages():
data = cdr_to_ros1(data, rconn.msgtype)
writer.write(connmap[rconn.id], timestamp, data)
def convert(src: Path, dst: Optional[Path]) -> None:
"""Convert between Rosbag1 and Rosbag2.
Args:
src: Source rosbag.
dst: Destination rosbag.
Raises:
ConverterError: An error occured during reading, writing, or
converting.
"""
dst = dst if dst else src.with_suffix('')
upgrade = src.suffix == '.bag'
dst = dst if dst else src.with_suffix('' if upgrade else '.bag')
if dst.exists():
raise ConverterError(f'Output path {str(dst)!r} exists already.')
func = convert_1to2 if upgrade else convert_2to1
try:
with Reader(src) as reader, Writer(dst) as writer:
typs: dict[str, Any] = {}
connmap: dict[int, WConnection] = {}
for rconn in reader.connections.values():
candidate = convert_connection(rconn)
existing = next((x for x in writer.connections.values() if x == candidate), None)
wconn = existing if existing else writer.add_connection(**asdict(candidate))
connmap[rconn.cid] = wconn
typs.update(get_types_from_msg(rconn.msgdef, rconn.msgtype))
register_types(typs)
for rconn, timestamp, data in reader.messages():
data = ros1_to_cdr(data, rconn.msgtype)
writer.write(connmap[rconn.cid], timestamp, data)
except ReaderError as err:
func(src, dst)
except (ReaderError1, ReaderError2) as err:
raise ConverterError(f'Reading source bag: {err}') from err
except WriterError as err:
except (WriterError1, WriterError2) as err:
raise ConverterError(f'Writing destination bag: {err}') from err
except Exception as err: # pylint: disable=broad-except
raise ConverterError(f'Converting rosbag: {err!r}') from err
+2 -2
View File
@@ -126,7 +126,7 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview:
None,
0,
)
assert ipos + 4 == len(raw)
assert ipos + 4 + 3 >= len(raw)
raw = memoryview(raw)
size = opos
@@ -138,6 +138,6 @@ def cdr_to_ros1(raw: bytes, typename: str) -> memoryview:
rawdata,
0,
)
assert ipos + 4 == len(raw)
assert ipos + 4 + 3 >= len(raw)
assert opos == size
return rawdata.toreadonly()