1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
# coding: utf-8
"""
This module works with CAN data in ASCII log files (*.log).
It is is compatible with "candump -L" from the canutils program
(https://github.com/linux-can/can-utils).
"""
from __future__ import absolute_import, division
import time
import datetime
import logging
from can.message import Message
from can.listener import Listener
from .generic import BaseIOHandler
log = logging.getLogger('can.io.canutils')
CAN_MSG_EXT = 0x80000000
CAN_ERR_FLAG = 0x20000000
CAN_ERR_BUSERROR = 0x00000080
CAN_ERR_DLC = 8
class CanutilsLogReader(BaseIOHandler):
"""
Iterator over CAN messages from a .log Logging File (candump -L).
.. note::
.log-format looks for example like this:
``(0.0) vcan0 001#8d00100100820100``
"""
def __init__(self, file):
"""
:param file: a path-like object or as file-like object to read from
If this is a file-like object, is has to opened in text
read mode, not binary read mode.
"""
super(CanutilsLogReader, self).__init__(file, mode='r')
def __iter__(self):
for line in self.file:
# skip empty lines
temp = line.strip()
if not temp:
continue
timestamp, channel, frame = temp.split()
timestamp = float(timestamp[1:-1])
canId, data = frame.split('#')
if channel.isdigit():
channel = int(channel)
if len(canId) > 3:
isExtended = True
else:
isExtended = False
canId = int(canId, 16)
if data and data[0].lower() == 'r':
isRemoteFrame = True
if len(data) > 1:
dlc = int(data[1:])
else:
dlc = 0
else:
isRemoteFrame = False
dlc = len(data) // 2
dataBin = bytearray()
for i in range(0, len(data), 2):
dataBin.append(int(data[i:(i + 2)], 16))
if canId & CAN_ERR_FLAG and canId & CAN_ERR_BUSERROR:
msg = Message(timestamp=timestamp, is_error_frame=True)
else:
msg = Message(timestamp=timestamp, arbitration_id=canId & 0x1FFFFFFF,
extended_id=isExtended, is_remote_frame=isRemoteFrame,
dlc=dlc, data=dataBin, channel=channel)
yield msg
self.stop()
class CanutilsLogWriter(BaseIOHandler, Listener):
"""Logs CAN data to an ASCII log file (.log).
This class is is compatible with "candump -L".
If a message has a timestamp smaller than the previous one (or 0 or None),
it gets assigned the timestamp that was written for the last message.
It the first message does not have a timestamp, it is set to zero.
"""
def __init__(self, file, channel="vcan0", append=False):
"""
:param file: a path-like object or as file-like object to write to
If this is a file-like object, is has to opened in text
write mode, not binary write mode.
:param channel: a default channel to use when the message does not
have a channel set
:param bool append: if set to `True` messages are appended to
the file, else the file is truncated
"""
mode = 'a' if append else 'w'
super(CanutilsLogWriter, self).__init__(file, mode=mode)
self.channel = channel
self.last_timestamp = None
def on_message_received(self, msg):
# this is the case for the very first message:
if self.last_timestamp is None:
self.last_timestamp = (msg.timestamp or 0.0)
# figure out the correct timestamp
if msg.timestamp is None or msg.timestamp < self.last_timestamp:
timestamp = self.last_timestamp
else:
timestamp = msg.timestamp
channel = msg.channel if msg.channel is not None else self.channel
if msg.is_error_frame:
self.file.write("(%f) %s %08X#0000000000000000\n" % (timestamp, channel, CAN_ERR_FLAG | CAN_ERR_BUSERROR))
elif msg.is_remote_frame:
if msg.is_extended_id:
self.file.write("(%f) %s %08X#R\n" % (timestamp, channel, msg.arbitration_id))
else:
self.file.write("(%f) %s %03X#R\n" % (timestamp, channel, msg.arbitration_id))
else:
data = ["{:02X}".format(byte) for byte in msg.data]
if msg.is_extended_id:
self.file.write("(%f) %s %08X#%s\n" % (timestamp, channel, msg.arbitration_id, ''.join(data)))
else:
self.file.write("(%f) %s %03X#%s\n" % (timestamp, channel, msg.arbitration_id, ''.join(data)))
|