# # Copyright (C) 2023 DroneCAN Development Team # # This software is distributed under the terms of the MIT License. # ''' driver for reading/writing CAN frames from/to a file - MAGIC 0x2934 16 bit - 64 bit monotonic timestamp (microseconds) - 16 bit CRC CRC-16-CCITT (over all bytes after CRC) - 16 bit length - 16 bit flags - 32 bit message ID - data[] all data is little endian ''' import os import dronecan.dsdl.common as common from logging import getLogger from .common import DriverError, CANFrame, AbstractDriver import struct import time import dronecan.dsdl.common as common FILE_MAGIC = 0x2934 FILE_FLAG_CANFD = 0x0001 class file(AbstractDriver): """ Driver for CAN Log File """ def __init__(self, filename, **kwargs): """ :param filename: filename to read/write :param kwargs: see AbstractDriver """ super(file, self).__init__() self.filename = filename self.start_file_monotonic_ts = 0 self.speedup = kwargs.get('speedup', 1) # check if read only option selected if kwargs.get('readonly', False): self.readonly = True self.file = open(filename, 'rb') self.curr_frame = None self.start_monotonic_ts = time.monotonic() self.file_size = os.path.getsize(filename) self.set_first_and_last_timestamp() self.eof = False else: self.readonly = False self.file = open(filename, 'wb') self.file.seek(0, os.SEEK_END) self.first_receive = False def close(self): if self.file is not None: self.file.flush() self.file.close() def __del__(self): self.close() def _read_frame(self): if (self.file.tell() >= self.file_size): self.eof = True return None, None # read header header = self.file.read(20) if len(header) < 20: return None, None magic, timestamp, crc, length, flags, msgid = struct.unpack(' 0: if timeout is not None and time_to_next_frame <= timeout: # sleep until next frame time.sleep(time_to_next_frame) else: return None self.curr_frame.ts_real = time.time() frame = self.curr_frame self.curr_frame = None return frame def send_frame(self, frame, timeout=None): if self.readonly: return if self.first_receive is False: self.first_receive = True self.start_file_monotonic_ts = time.monotonic() self.start_monotonic_ts = self.start_file_monotonic_ts # calculate flags flags = 0 if frame.canfd: flags |= FILE_FLAG_CANFD # calculate message id msgid = frame.id if frame.extended: msgid |= 1<<31 # calculate CRC crc = common.crc16_from_bytes(frame.data) # write header header = struct.pack('