Skip to content

Commit 57b19a7

Browse files
committed
Add PcapngReader
1 parent 6beebe1 commit 57b19a7

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

can/io/pcapng.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88

99
import logging
1010

11-
from typing import Any, BinaryIO, Dict, Union
11+
from typing import Any, BinaryIO, Dict, Generator, Union
1212

1313
from ..message import Message
1414
from ..typechecking import StringPathLike, Channel
15-
from .generic import BinaryIOMessageWriter
16-
from ..socketcan_common import build_can_frame, CAN_FRAME_HEADER_STRUCT_BE
15+
from .generic import BinaryIOMessageWriter, BinaryIOMessageReader
16+
from ..socketcan_common import build_can_frame, parse_can_frame, CAN_FRAME_HEADER_STRUCT_BE
1717

1818
logger = logging.getLogger("can.io.pcapng")
1919

@@ -106,3 +106,44 @@ def on_message_received(self, msg: Message) -> None:
106106
timestamp_low=timestamp_units & 0xFFFFFFFF,
107107
endianness=">" # big
108108
))
109+
110+
class PcapngReader(BinaryIOMessageReader):
111+
"""
112+
Iterator of CAN messages from a Pcapng File.
113+
"""
114+
115+
file: BinaryIO
116+
117+
def __init__(
118+
self,
119+
file: Union[StringPathLike, BinaryIO],
120+
**kwargs: Any,
121+
) -> None:
122+
"""
123+
:param file: a path-like object or as file-like object to read from
124+
If this is a file-like object, is has to opened in binary
125+
read mode, not text read mode.
126+
"""
127+
super().__init__(file, mode="rb")
128+
self._scanner = pcapng.FileScanner(self.file)
129+
130+
def __iter__(self) -> Generator[Message, None, None]:
131+
for block in self._scanner:
132+
if isinstance(block, blocks.EnhancedPacket):
133+
idn: blocks.InterfaceDescription = block.interface
134+
# We only care about the CAN packets
135+
if idn.link_type != LINKTYPE_CAN_SOCKETCAN:
136+
logger.debug("Skipping non-CAN packet, link type: %s", idn.link_type)
137+
continue
138+
139+
msg = parse_can_frame(block.packet_data, struct=CAN_FRAME_HEADER_STRUCT_BE)
140+
141+
timestamp64 = (block.timestamp_high << 32) + block.timestamp_low
142+
msg.timestamp = timestamp64 * idn.timestamp_resolution
143+
144+
if 'if_name' in idn.options:
145+
msg.channel = idn.options['if_name']
146+
else:
147+
msg.channel = block.interface_id
148+
149+
yield msg

can/io/player.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
from .mf4 import MF4Reader
3030
from .sqlite import SqliteReader
3131
from .trc import TRCReader
32+
from .pcapng import PcapngReader
3233

3334
#: A map of file suffixes to their corresponding
3435
#: :class:`can.io.generic.MessageReader` class
@@ -39,6 +40,7 @@
3940
".db": SqliteReader,
4041
".log": CanutilsLogReader,
4142
".mf4": MF4Reader,
43+
".pcapng": PcapngReader,
4244
".trc": TRCReader,
4345
}
4446

0 commit comments

Comments
 (0)