@@ -34,8 +34,6 @@ class ASCReader(MessageReader):
34
34
35
35
file : TextIO
36
36
37
- FORMAT_START_OF_FILE_DATE = "%a %b %d %I:%M:%S.%f %p %Y"
38
-
39
37
def __init__ (
40
38
self ,
41
39
file : Union [StringPathLike , TextIO ],
@@ -60,51 +58,95 @@ def __init__(
60
58
self .base = base
61
59
self ._converted_base = self ._check_base (base )
62
60
self .relative_timestamp = relative_timestamp
63
- self .date = None
61
+ self .date : Optional [str ] = None
62
+ self .start_time = 0.0
64
63
# TODO - what is this used for? The ASC Writer only prints `absolute`
65
- self .timestamps_format = None
66
- self .internal_events_logged = None
64
+ self .timestamps_format : Optional [ str ] = None
65
+ self .internal_events_logged = False
67
66
68
- def _extract_header (self ):
67
+ def _extract_header (self ) -> None :
69
68
for line in self .file :
70
69
line = line .strip ()
71
- lower_case = line .lower ()
72
- if lower_case .startswith ("date" ):
73
- self .date = line [5 :]
74
- elif lower_case .startswith ("base" ):
75
- try :
76
- _ , base , _ , timestamp_format = line .split ()
77
- except ValueError as exception :
78
- raise Exception (
79
- f"Unsupported header string format: { line } "
80
- ) from exception
70
+
71
+ datetime_match = re .match (
72
+ r"date\s+\w+\s+(?P<datetime_string>.+)" , line , re .IGNORECASE
73
+ )
74
+ base_match = re .match (
75
+ r"base\s+(?P<base>hex|dec)(?:\s+timestamps\s+"
76
+ r"(?P<timestamp_format>absolute|relative))?" ,
77
+ line ,
78
+ re .IGNORECASE ,
79
+ )
80
+ comment_match = re .match (r"//.*" , line )
81
+ events_match = re .match (
82
+ r"(?P<no_events>no)?\s*internal\s+events\s+logged" , line , re .IGNORECASE
83
+ )
84
+
85
+ if datetime_match :
86
+ self .date = datetime_match .group ("datetime_string" )
87
+ self .start_time = (
88
+ 0.0
89
+ if self .relative_timestamp
90
+ else self ._datetime_to_timestamp (self .date )
91
+ )
92
+ continue
93
+
94
+ elif base_match :
95
+ base = base_match .group ("base" )
96
+ timestamp_format = base_match .group ("timestamp_format" )
81
97
self .base = base
82
98
self ._converted_base = self ._check_base (self .base )
83
- self .timestamps_format = timestamp_format
84
- elif lower_case .endswith ("internal events logged" ):
85
- self .internal_events_logged = not lower_case .startswith ("no" )
86
- elif lower_case .startswith ("//" ):
87
- # ignore comments
99
+ self .timestamps_format = timestamp_format or "absolute"
88
100
continue
89
- # grab absolute timestamp
90
- elif lower_case .startswith ("begin triggerblock" ):
91
- if self .relative_timestamp :
92
- self .start_time = 0.0
93
- else :
94
- try :
95
- _ , _ , start_time = lower_case .split (None , 2 )
96
- start_time = datetime .strptime (
97
- start_time , self .FORMAT_START_OF_FILE_DATE
98
- ).timestamp ()
99
- except (ValueError , OSError ):
100
- # `OSError` to handle non-POSIX capable timestamps
101
- start_time = 0.0
102
- self .start_time = start_time
103
- # Currently the last line in the header which is parsed
101
+
102
+ elif comment_match :
103
+ continue
104
+
105
+ elif events_match :
106
+ self .internal_events_logged = events_match .group ("no_events" ) is None
104
107
break
108
+
105
109
else :
106
110
break
107
111
112
+ @staticmethod
113
+ def _datetime_to_timestamp (datetime_string : str ) -> float :
114
+ # ugly locale independent solution
115
+ month_map = {
116
+ "Jan" : 1 ,
117
+ "Feb" : 2 ,
118
+ "Mar" : 3 ,
119
+ "Apr" : 4 ,
120
+ "May" : 5 ,
121
+ "Jun" : 6 ,
122
+ "Jul" : 7 ,
123
+ "Aug" : 8 ,
124
+ "Sep" : 9 ,
125
+ "Oct" : 10 ,
126
+ "Nov" : 11 ,
127
+ "Dec" : 12 ,
128
+ "Mär" : 3 ,
129
+ "Mai" : 5 ,
130
+ "Okt" : 10 ,
131
+ "Dez" : 12 ,
132
+ }
133
+ for name , number in month_map .items ():
134
+ datetime_string = datetime_string .replace (name , str (number ).zfill (2 ))
135
+
136
+ datetime_formats = (
137
+ "%m %d %I:%M:%S.%f %p %Y" ,
138
+ "%m %d %I:%M:%S %p %Y" ,
139
+ "%m %d %H:%M:%S.%f %Y" ,
140
+ "%m %d %H:%M:%S %Y" ,
141
+ )
142
+ for format_str in datetime_formats :
143
+ try :
144
+ return datetime .strptime (datetime_string , format_str ).timestamp ()
145
+ except ValueError :
146
+ continue
147
+
148
+ raise ValueError (f"Incompatible datetime string { datetime_string } " )
149
+
108
150
def _extract_can_id (self , str_can_id : str , msg_kwargs : Dict [str , Any ]) -> None :
109
151
if str_can_id [- 1 :].lower () == "x" :
110
152
msg_kwargs ["is_extended_id" ] = True
@@ -219,6 +261,20 @@ def __iter__(self) -> Generator[Message, None, None]:
219
261
for line in self .file :
220
262
line = line .strip ()
221
263
264
+ trigger_match = re .match (
265
+ r"begin\s+triggerblock\s+\w+\s+(?P<datetime_string>.+)" ,
266
+ line ,
267
+ re .IGNORECASE ,
268
+ )
269
+ if trigger_match :
270
+ datetime_str = trigger_match .group ("datetime_string" )
271
+ self .start_time = (
272
+ 0.0
273
+ if self .relative_timestamp
274
+ else self ._datetime_to_timestamp (datetime_str )
275
+ )
276
+ continue
277
+
222
278
if not re .match (
223
279
r"\d+\.\d+\s+(\d+\s+(\w+\s+(Tx|Rx)|ErrorFrame)|CANFD)" ,
224
280
line ,
0 commit comments