@@ -272,34 +272,36 @@ class MF4Reader(BinaryIOMessageReader):
272
272
273
273
The MF4Reader only supports MF4 files with CAN bus logging.
274
274
"""
275
-
275
+
276
276
# NOTE: Readout based on the bus logging code from asammdf GUI
277
-
277
+
278
278
class FrameIterator (object ):
279
279
"""
280
280
Iterator helper class for common handling among CAN DataFrames, ErrorFrames and RemoteFrames.
281
281
"""
282
-
282
+
283
283
# Number of records to request for each asammdf call
284
284
_chunk_size = 1000
285
-
286
- def __init__ (self , mdf : MDF , group_index : int , start_timestamp : float , name : str ):
285
+
286
+ def __init__ (
287
+ self , mdf : MDF , group_index : int , start_timestamp : float , name : str
288
+ ):
287
289
self ._mdf = mdf
288
290
self ._group_index = group_index
289
291
self ._start_timestamp = start_timestamp
290
292
self ._name = name
291
-
293
+
292
294
# Extract names
293
295
channel_group : ChannelGroup = self ._mdf .groups [self ._group_index ]
294
-
296
+
295
297
self ._channel_names = []
296
-
298
+
297
299
for channel in channel_group .channels :
298
300
if str (channel .name ).startswith (f"{ self ._name } ." ):
299
301
self ._channel_names .append (channel .name )
300
-
302
+
301
303
return
302
-
304
+
303
305
def _get_data (self , current_offset : int ) -> asammdf .Signal :
304
306
# NOTE: asammdf suggests using select instead of get. Select seem to miss converting some channels which
305
307
# get does convert as expected.
@@ -308,34 +310,40 @@ def _get_data(self, current_offset: int) -> asammdf.Signal:
308
310
self ._group_index ,
309
311
record_offset = current_offset ,
310
312
record_count = self ._chunk_size ,
311
- raw = False
313
+ raw = False ,
312
314
)
313
-
315
+
314
316
return data_raw
315
-
317
+
316
318
pass
317
-
319
+
318
320
class CANDataFrameIterator (FrameIterator ):
319
-
321
+
320
322
def __init__ (self , mdf : MDF , group_index : int , start_timestamp : float ):
321
323
super ().__init__ (mdf , group_index , start_timestamp , "CAN_DataFrame" )
322
-
324
+
323
325
return
324
-
326
+
325
327
def __iter__ (self ) -> Generator [Message , None , None ]:
326
- for current_offset in range (0 , self ._mdf .groups [self ._group_index ].channel_group .cycles_nr , self ._chunk_size ):
328
+ for current_offset in range (
329
+ 0 ,
330
+ self ._mdf .groups [self ._group_index ].channel_group .cycles_nr ,
331
+ self ._chunk_size ,
332
+ ):
327
333
data = self ._get_data (current_offset )
328
334
names = data .samples [0 ].dtype .names
329
-
335
+
330
336
for i in range (len (data )):
331
337
data_length = int (data ["CAN_DataFrame.DataLength" ][i ])
332
-
338
+
333
339
kv = {
334
340
"timestamp" : float (data .timestamps [i ]) + self ._start_timestamp ,
335
341
"arbitration_id" : int (data ["CAN_DataFrame.ID" ][i ]) & 0x1FFFFFFF ,
336
- "data" : data ["CAN_DataFrame.DataBytes" ][i ][:data_length ].tobytes (),
342
+ "data" : data ["CAN_DataFrame.DataBytes" ][i ][
343
+ :data_length
344
+ ].tobytes (),
337
345
}
338
-
346
+
339
347
if "CAN_DataFrame.BusChannel" in names :
340
348
kv ["channel" ] = int (data ["CAN_DataFrame.BusChannel" ][i ])
341
349
if "CAN_DataFrame.Dir" in names :
@@ -348,90 +356,108 @@ def __iter__(self) -> Generator[Message, None, None]:
348
356
kv ["bitrate_switch" ] = bool (data ["CAN_DataFrame.BRS" ][i ])
349
357
if "CAN_DataFrame.ESI" in names :
350
358
kv ["error_state_indicator" ] = bool (data ["CAN_DataFrame.ESI" ][i ])
351
-
359
+
352
360
yield Message (** kv )
353
-
361
+
354
362
return None
355
-
363
+
356
364
pass
357
-
365
+
358
366
class CANErrorFrameIterator (FrameIterator ):
359
-
367
+
360
368
def __init__ (self , mdf : MDF , group_index : int , start_timestamp : float ):
361
369
super ().__init__ (mdf , group_index , start_timestamp , "CAN_ErrorFrame" )
362
-
370
+
363
371
return
364
-
372
+
365
373
def __iter__ (self ) -> Generator [Message , None , None ]:
366
- for current_offset in range (0 , self ._mdf .groups [self ._group_index ].channel_group .cycles_nr , self ._chunk_size ):
374
+ for current_offset in range (
375
+ 0 ,
376
+ self ._mdf .groups [self ._group_index ].channel_group .cycles_nr ,
377
+ self ._chunk_size ,
378
+ ):
367
379
data = self ._get_data (current_offset )
368
380
names = data .samples [0 ].dtype .names
369
-
381
+
370
382
for i in range (len (data )):
371
383
kv = {
372
384
"timestamp" : float (data .timestamps [i ]) + self ._start_timestamp ,
373
385
"is_error_frame" : True ,
374
386
}
375
-
387
+
376
388
if "CAN_ErrorFrame.BusChannel" in names :
377
389
kv ["channel" ] = int (data ["CAN_ErrorFrame.BusChannel" ][i ])
378
390
if "CAN_ErrorFrame.Dir" in names :
379
391
kv ["is_rx" ] = int (data ["CAN_ErrorFrame.Dir" ][i ]) == 0
380
392
if "CAN_ErrorFrame.ID" in names :
381
- kv ["arbitration_id" ] = int (data ["CAN_ErrorFrame.ID" ][i ]) & 0x1FFFFFFF
393
+ kv ["arbitration_id" ] = (
394
+ int (data ["CAN_ErrorFrame.ID" ][i ]) & 0x1FFFFFFF
395
+ )
382
396
if "CAN_ErrorFrame.IDE" in names :
383
397
kv ["is_extended_id" ] = bool (data ["CAN_ErrorFrame.IDE" ][i ])
384
398
if "CAN_ErrorFrame.EDL" in names :
385
399
kv ["is_fd" ] = bool (data ["CAN_ErrorFrame.EDL" ][i ])
386
400
if "CAN_ErrorFrame.BRS" in names :
387
401
kv ["bitrate_switch" ] = bool (data ["CAN_ErrorFrame.BRS" ][i ])
388
402
if "CAN_ErrorFrame.ESI" in names :
389
- kv ["error_state_indicator" ] = bool (data ["CAN_ErrorFrame.ESI" ][i ])
403
+ kv ["error_state_indicator" ] = bool (
404
+ data ["CAN_ErrorFrame.ESI" ][i ]
405
+ )
390
406
if "CAN_ErrorFrame.RTR" in names :
391
407
kv ["is_remote_frame" ] = bool (data ["CAN_ErrorFrame.RTR" ][i ])
392
- if "CAN_ErrorFrame.DataLength" in names and "CAN_ErrorFrame.DataBytes" in names :
408
+ if (
409
+ "CAN_ErrorFrame.DataLength" in names
410
+ and "CAN_ErrorFrame.DataBytes" in names
411
+ ):
393
412
data_length = int (data ["CAN_ErrorFrame.DataLength" ][i ])
394
- kv ["data" ] = data ["CAN_ErrorFrame.DataBytes" ][i ][:data_length ].tobytes ()
395
-
413
+ kv ["data" ] = data ["CAN_ErrorFrame.DataBytes" ][i ][
414
+ :data_length
415
+ ].tobytes ()
416
+
396
417
yield Message (** kv )
397
-
418
+
398
419
return None
399
-
420
+
400
421
pass
401
-
422
+
402
423
class CANRemoteFrameIterator (FrameIterator ):
403
-
424
+
404
425
def __init__ (self , mdf : MDF , group_index : int , start_timestamp : float ):
405
426
super ().__init__ (mdf , group_index , start_timestamp , "CAN_RemoteFrame" )
406
-
427
+
407
428
return
408
-
429
+
409
430
def __iter__ (self ) -> Generator [Message , None , None ]:
410
- for current_offset in range (0 , self ._mdf .groups [self ._group_index ].channel_group .cycles_nr , self ._chunk_size ):
431
+ for current_offset in range (
432
+ 0 ,
433
+ self ._mdf .groups [self ._group_index ].channel_group .cycles_nr ,
434
+ self ._chunk_size ,
435
+ ):
411
436
data = self ._get_data (current_offset )
412
437
names = data .samples [0 ].dtype .names
413
-
438
+
414
439
for i in range (len (data )):
415
440
kv = {
416
441
"timestamp" : float (data .timestamps [i ]) + self ._start_timestamp ,
417
- "arbitration_id" : int (data ["CAN_RemoteFrame.ID" ][i ]) & 0x1FFFFFFF ,
442
+ "arbitration_id" : int (data ["CAN_RemoteFrame.ID" ][i ])
443
+ & 0x1FFFFFFF ,
418
444
"dlc" : int (data ["CAN_RemoteFrame.DLC" ][i ]),
419
445
"is_remote_frame" : True ,
420
446
}
421
-
447
+
422
448
if "CAN_RemoteFrame.BusChannel" in names :
423
449
kv ["channel" ] = int (data ["CAN_RemoteFrame.BusChannel" ][i ])
424
450
if "CAN_RemoteFrame.Dir" in names :
425
451
kv ["is_rx" ] = int (data ["CAN_RemoteFrame.Dir" ][i ]) == 0
426
452
if "CAN_RemoteFrame.IDE" in names :
427
453
kv ["is_extended_id" ] = bool (data ["CAN_RemoteFrame.IDE" ][i ])
428
-
454
+
429
455
yield Message (** kv )
430
-
456
+
431
457
return None
432
-
458
+
433
459
pass
434
-
460
+
435
461
def __init__ (
436
462
self ,
437
463
file : Union [StringPathLike , BinaryIO ],
@@ -455,48 +481,60 @@ def __init__(
455
481
self ._mdf = MDF (BytesIO (file .read ()))
456
482
else :
457
483
self ._mdf = MDF (file )
458
-
484
+
459
485
self ._start_timestamp = self ._mdf .header .start_time .timestamp ()
460
486
461
487
def __iter__ (self ) -> Iterable [Message ]:
462
488
import heapq
463
-
489
+
464
490
# To handle messages split over multiple channel groups, create a single iterator per channel group and merge
465
491
# these iterators into a single iterator using heapq.
466
492
iterators = []
467
493
for group_index , group in enumerate (self ._mdf .groups ):
468
494
channel_group : ChannelGroup = group .channel_group
469
-
495
+
470
496
if not channel_group .flags & FLAG_CG_BUS_EVENT :
471
497
# Not a bus event, skip
472
498
continue
473
-
499
+
474
500
if channel_group .cycles_nr == 0 :
475
501
# No data, skip
476
502
continue
477
-
503
+
478
504
acquisition_source : Optional [Source ] = channel_group .acq_source
479
-
505
+
480
506
if acquisition_source is None :
481
507
# No source information, skip
482
508
continue
483
509
elif not acquisition_source .source_type & Source .SOURCE_BUS :
484
510
# Not a bus type (likely already covered by the channel group flag), skip
485
511
continue
486
-
512
+
487
513
channel_names = [channel .name for channel in group .channels ]
488
-
514
+
489
515
if acquisition_source .bus_type == Source .BUS_TYPE_CAN :
490
516
if "CAN_DataFrame" in channel_names :
491
- iterators .append (self .CANDataFrameIterator (self ._mdf , group_index , self ._start_timestamp ))
517
+ iterators .append (
518
+ self .CANDataFrameIterator (
519
+ self ._mdf , group_index , self ._start_timestamp
520
+ )
521
+ )
492
522
elif "CAN_ErrorFrame" in channel_names :
493
- iterators .append (self .CANErrorFrameIterator (self ._mdf , group_index , self ._start_timestamp ))
523
+ iterators .append (
524
+ self .CANErrorFrameIterator (
525
+ self ._mdf , group_index , self ._start_timestamp
526
+ )
527
+ )
494
528
elif "CAN_RemoteFrame" in channel_names :
495
- iterators .append (self .CANRemoteFrameIterator (self ._mdf , group_index , self ._start_timestamp ))
529
+ iterators .append (
530
+ self .CANRemoteFrameIterator (
531
+ self ._mdf , group_index , self ._start_timestamp
532
+ )
533
+ )
496
534
else :
497
535
# Unknown bus type, skip
498
536
continue
499
-
537
+
500
538
# Create merged iterator over all the groups, using the timestamps as comparison key
501
539
return heapq .merge (* iterators , key = lambda x : x .timestamp )
502
540
0 commit comments