Skip to content

Commit 6ed6578

Browse files
lebuniAdrian Immer
authored and
Adrian Immer
committed
Improve speed of TRCReader
Rewrote some lines in the TRCReader to optimize for speed, mainly for TRC files v2.x. According to cProfile it's double as fast now.
1 parent 9a766ce commit 6ed6578

File tree

1 file changed

+30
-45
lines changed

1 file changed

+30
-45
lines changed

can/io/trc.py

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,12 @@
1111
import os
1212
from datetime import datetime, timedelta, timezone
1313
from enum import Enum
14-
from typing import Any, Callable, Dict, Generator, List, Optional, TextIO, Union
14+
from typing import Any, Callable, Dict, Generator, Optional, TextIO, Tuple, Union
1515

1616
from ..message import Message
1717
from ..typechecking import StringPathLike
18-
from ..util import channel2int, dlc2len, len2dlc
19-
from .generic import (
20-
TextIOMessageReader,
21-
TextIOMessageWriter,
22-
)
18+
from ..util import channel2int, len2dlc
19+
from .generic import TextIOMessageReader, TextIOMessageWriter
2320

2421
logger = logging.getLogger("can.io.trc")
2522

@@ -58,13 +55,16 @@ def __init__(
5855
"""
5956
super().__init__(file, mode="r")
6057
self.file_version = TRCFileVersion.UNKNOWN
61-
self.start_time: Optional[datetime] = None
58+
self.start_time: float = 0
6259
self.columns: Dict[str, int] = {}
60+
self._num_columns = -1
6361

6462
if not self.file:
6563
raise ValueError("The given file cannot be None")
6664

67-
self._parse_cols: Callable[[List[str]], Optional[Message]] = lambda x: None
65+
self._parse_cols: Callable[[Tuple[str, ...]], Optional[Message]] = (
66+
lambda x: None
67+
)
6868

6969
def _extract_header(self):
7070
line = ""
@@ -89,16 +89,18 @@ def _extract_header(self):
8989
elif line.startswith(";$STARTTIME"):
9090
logger.debug("TRCReader: Found start time '%s'", line)
9191
try:
92-
self.start_time = datetime(
93-
1899, 12, 30, tzinfo=timezone.utc
94-
) + timedelta(days=float(line.split("=")[1]))
92+
self.start_time = (
93+
datetime(1899, 12, 30, tzinfo=timezone.utc)
94+
+ timedelta(days=float(line.split("=")[1]))
95+
).timestamp()
9596
except IndexError:
9697
logger.debug("TRCReader: Failed to parse start time")
9798
elif line.startswith(";$COLUMNS"):
9899
logger.debug("TRCReader: Found columns '%s'", line)
99100
try:
100101
columns = line.split("=")[1].split(",")
101102
self.columns = {column: columns.index(column) for column in columns}
103+
self._num_columns = len(columns) - 1
102104
except IndexError:
103105
logger.debug("TRCReader: Failed to parse columns")
104106
elif line.startswith(";"):
@@ -132,7 +134,7 @@ def _extract_header(self):
132134

133135
return line
134136

135-
def _parse_msg_v1_0(self, cols: List[str]) -> Optional[Message]:
137+
def _parse_msg_v1_0(self, cols: Tuple[str, ...]) -> Optional[Message]:
136138
arbit_id = cols[2]
137139
if arbit_id == "FFFFFFFF":
138140
logger.info("TRCReader: Dropping bus info line")
@@ -147,16 +149,11 @@ def _parse_msg_v1_0(self, cols: List[str]) -> Optional[Message]:
147149
msg.data = bytearray([int(cols[i + 4], 16) for i in range(msg.dlc)])
148150
return msg
149151

150-
def _parse_msg_v1_1(self, cols: List[str]) -> Optional[Message]:
152+
def _parse_msg_v1_1(self, cols: Tuple[str, ...]) -> Optional[Message]:
151153
arbit_id = cols[3]
152154

153155
msg = Message()
154-
if isinstance(self.start_time, datetime):
155-
msg.timestamp = (
156-
self.start_time + timedelta(milliseconds=float(cols[1]))
157-
).timestamp()
158-
else:
159-
msg.timestamp = float(cols[1]) / 1000
156+
msg.timestamp = float(cols[1]) / 1000 + self.start_time
160157
msg.arbitration_id = int(arbit_id, 16)
161158
msg.is_extended_id = len(arbit_id) > 4
162159
msg.channel = 1
@@ -165,16 +162,11 @@ def _parse_msg_v1_1(self, cols: List[str]) -> Optional[Message]:
165162
msg.is_rx = cols[2] == "Rx"
166163
return msg
167164

168-
def _parse_msg_v1_3(self, cols: List[str]) -> Optional[Message]:
165+
def _parse_msg_v1_3(self, cols: Tuple[str, ...]) -> Optional[Message]:
169166
arbit_id = cols[4]
170167

171168
msg = Message()
172-
if isinstance(self.start_time, datetime):
173-
msg.timestamp = (
174-
self.start_time + timedelta(milliseconds=float(cols[1]))
175-
).timestamp()
176-
else:
177-
msg.timestamp = float(cols[1]) / 1000
169+
msg.timestamp = float(cols[1]) / 1000 + self.start_time
178170
msg.arbitration_id = int(arbit_id, 16)
179171
msg.is_extended_id = len(arbit_id) > 4
180172
msg.channel = int(cols[2])
@@ -183,7 +175,7 @@ def _parse_msg_v1_3(self, cols: List[str]) -> Optional[Message]:
183175
msg.is_rx = cols[3] == "Rx"
184176
return msg
185177

186-
def _parse_msg_v2_x(self, cols: List[str]) -> Optional[Message]:
178+
def _parse_msg_v2_x(self, cols: Tuple[str, ...]) -> Optional[Message]:
187179
type_ = cols[self.columns["T"]]
188180
bus = self.columns.get("B", None)
189181

@@ -192,50 +184,43 @@ def _parse_msg_v2_x(self, cols: List[str]) -> Optional[Message]:
192184
dlc = len2dlc(length)
193185
elif "L" in self.columns:
194186
dlc = int(cols[self.columns["L"]])
195-
length = dlc2len(dlc)
196187
else:
197188
raise ValueError("No length/dlc columns present.")
198189

199190
msg = Message()
200-
if isinstance(self.start_time, datetime):
201-
msg.timestamp = (
202-
self.start_time + timedelta(milliseconds=float(cols[self.columns["O"]]))
203-
).timestamp()
204-
else:
205-
msg.timestamp = float(cols[1]) / 1000
191+
msg.timestamp = float(cols[self.columns["O"]]) / 1000 + self.start_time
206192
msg.arbitration_id = int(cols[self.columns["I"]], 16)
207193
msg.is_extended_id = len(cols[self.columns["I"]]) > 4
208194
msg.channel = int(cols[bus]) if bus is not None else 1
209195
msg.dlc = dlc
210-
msg.data = bytearray(
211-
[int(cols[i + self.columns["D"]], 16) for i in range(length)]
212-
)
196+
if dlc:
197+
msg.data = bytearray.fromhex(cols[self.columns["D"]])
213198
msg.is_rx = cols[self.columns["d"]] == "Rx"
214-
msg.is_fd = type_ in ["FD", "FB", "FE", "BI"]
215-
msg.bitrate_switch = type_ in ["FB", " FE"]
216-
msg.error_state_indicator = type_ in ["FE", "BI"]
199+
msg.is_fd = type_ in {"FD", "FB", "FE", "BI"}
200+
msg.bitrate_switch = type_ in {"FB", "FE"}
201+
msg.error_state_indicator = type_ in {"FE", "BI"}
217202

218203
return msg
219204

220-
def _parse_cols_v1_1(self, cols: List[str]) -> Optional[Message]:
205+
def _parse_cols_v1_1(self, cols: Tuple[str, ...]) -> Optional[Message]:
221206
dtype = cols[2]
222207
if dtype in ("Tx", "Rx"):
223208
return self._parse_msg_v1_1(cols)
224209
else:
225210
logger.info("TRCReader: Unsupported type '%s'", dtype)
226211
return None
227212

228-
def _parse_cols_v1_3(self, cols: List[str]) -> Optional[Message]:
213+
def _parse_cols_v1_3(self, cols: Tuple[str, ...]) -> Optional[Message]:
229214
dtype = cols[3]
230215
if dtype in ("Tx", "Rx"):
231216
return self._parse_msg_v1_3(cols)
232217
else:
233218
logger.info("TRCReader: Unsupported type '%s'", dtype)
234219
return None
235220

236-
def _parse_cols_v2_x(self, cols: List[str]) -> Optional[Message]:
221+
def _parse_cols_v2_x(self, cols: Tuple[str, ...]) -> Optional[Message]:
237222
dtype = cols[self.columns["T"]]
238-
if dtype in ["DT", "FD", "FB"]:
223+
if dtype in {"DT", "FD", "FB", "FE", "BI"}:
239224
return self._parse_msg_v2_x(cols)
240225
else:
241226
logger.info("TRCReader: Unsupported type '%s'", dtype)
@@ -244,7 +229,7 @@ def _parse_cols_v2_x(self, cols: List[str]) -> Optional[Message]:
244229
def _parse_line(self, line: str) -> Optional[Message]:
245230
logger.debug("TRCReader: Parse '%s'", line)
246231
try:
247-
cols = line.split()
232+
cols = tuple(line.split(maxsplit=self._num_columns))
248233
return self._parse_cols(cols)
249234
except IndexError:
250235
logger.warning("TRCReader: Failed to parse message '%s'", line)

0 commit comments

Comments
 (0)