greenhouse/src/rosbags/convert/converter.py

176 lines
4.9 KiB
Python
Raw Normal View History

2021-05-02 14:51:08 +02:00
# Copyright 2020-2021 Ternaris.
# SPDX-License-Identifier: Apache-2.0
"""Rosbag1 to Rosbag2 Converter."""
from __future__ import annotations
from dataclasses import asdict
2021-05-02 14:51:08 +02:00
from typing import TYPE_CHECKING
2022-01-09 20:31:51 +01:00
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
2021-05-02 14:51:08 +02:00
from rosbags.typesys import get_types_from_msg, register_types
2022-01-09 20:31:51 +01:00
from rosbags.typesys.msg import generate_msgdef
2021-05-02 14:51:08 +02:00
if TYPE_CHECKING:
from pathlib import Path
from typing import Any, Optional
2021-05-02 14:51:08 +02:00
LATCH = """
- history: 3
depth: 0
reliability: 1
durability: 1
deadline:
sec: 2147483647
nsec: 4294967295
lifespan:
sec: 2147483647
nsec: 4294967295
liveliness: 1
liveliness_lease_duration:
sec: 2147483647
nsec: 4294967295
avoid_ros_namespace_conventions: false
""".strip()
2021-05-02 14:51:08 +02:00
class ConverterError(Exception):
"""Converter Error."""
2022-01-09 20:31:51 +01:00
def upgrade_connection(rconn: Connection1) -> Connection2:
"""Convert rosbag1 connection to rosbag2 connection.
Args:
rconn: Rosbag1 connection.
Returns:
Rosbag2 connection.
"""
2022-01-09 20:31:51 +01:00
return Connection2(
-1,
0,
rconn.topic,
rconn.msgtype,
'cdr',
LATCH if rconn.latching else '',
)
2022-01-09 20:31:51 +01:00
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:
2021-05-02 14:51:08 +02:00
"""Convert Rosbag1 to Rosbag2.
Args:
src: Rosbag1 path.
dst: Rosbag2 path.
2022-01-09 20:31:51 +01:00
"""
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.
2021-05-02 14:51:08 +02:00
Raises:
ConverterError: An error occured during reading, writing, or
converting.
"""
2022-01-09 20:31:51 +01:00
upgrade = src.suffix == '.bag'
dst = dst if dst else src.with_suffix('' if upgrade else '.bag')
2021-05-02 14:51:08 +02:00
if dst.exists():
raise ConverterError(f'Output path {str(dst)!r} exists already.')
2022-01-09 20:31:51 +01:00
func = convert_1to2 if upgrade else convert_2to1
2021-05-02 14:51:08 +02:00
try:
2022-01-09 20:31:51 +01:00
func(src, dst)
except (ReaderError1, ReaderError2) as err:
2021-05-02 14:51:08 +02:00
raise ConverterError(f'Reading source bag: {err}') from err
2022-01-09 20:31:51 +01:00
except (WriterError1, WriterError2) as err:
2021-05-02 14:51:08 +02:00
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