From 791307c9d8b733cc3357533fb74cf54a0dec6e2c Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Sat, 10 Feb 2018 13:24:59 +1100 Subject: [PATCH 01/57] Add changelog for v2.1.0 --- CHANGELOG.txt | 34 ++++++++++++++++++++++++++++++++++ can/__init__.py | 2 +- doc/development.rst | 2 +- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 CHANGELOG.txt diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 000000000..07b75975b --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,34 @@ +Version 2.1.0 (2018-02-10) +===== + + +* Support for out of tree can interfaces with pluggy. +* Neovi interface now uses Intrepid Control Systems's own interface library. +* Improvements and new documentation for SQL reader/writer + + +Version 2.0.0 (2018-01-05 +===== + +After an extended baking period we have finally tagged version 2.0.0! + +Quite a few major Changes from v1.x: + +* New interfaces: + * Vector + * NI-CAN + * isCAN + * neoVI +* Simplified periodic send API with initial support for SocketCAN +* Protocols module including J1939 support removed +* Logger script moved to module `can.logger` +* New `can.player` script to replay log files +* BLF, ASC log file support added in new `can.io` module + +You can install from [PyPi](https://pypi.python.org/pypi/python-can/2.0.0) with pip: + +``` +pip install python-can==2.0.0 +``` + +The documentation for v2.0.0 is available at http://python-can.readthedocs.io/en/2.0.0/ diff --git a/can/__init__.py b/can/__init__.py index 70d3b2531..71bc0f442 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -5,7 +5,7 @@ import logging -__version__ = "2.1.0.rc2" +__version__ = "2.1.0" log = logging.getLogger('can') diff --git a/doc/development.rst b/doc/development.rst index 6fb0de2f4..f7e09b671 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -38,7 +38,7 @@ Creating a Release - Upload with twine ``twine upload dist/python-can-X.Y.Z*`` - In a new virtual env check that the package can be installed with pip: ``pip install python-can==X.Y.Z`` - Create a new tag in the repository. -- Check the release on PyPi and github. +- Check the release on PyPi, readthedocs and github. Code Structure From cd53ec42c8c69d725ca9ce9ccc22f0ba94c09a3d Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Sat, 10 Feb 2018 13:33:45 +1100 Subject: [PATCH 02/57] Update changelog for v2.1.0 --- CHANGELOG.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 07b75975b..e81cedd16 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,11 +1,13 @@ -Version 2.1.0 (2018-02-10) +Version 2.1.0 (2018-02-17) ===== - * Support for out of tree can interfaces with pluggy. +* Initial support for CAN-FD for socketcan_native and kvaser interfaces. * Neovi interface now uses Intrepid Control Systems's own interface library. -* Improvements and new documentation for SQL reader/writer - +* Improvements and new documentation for SQL reader/writer. +* Fix bug in neovi serial number decoding. +* Add testing on OSX to TravisCI +* Fix non english decoding error on pcan Version 2.0.0 (2018-01-05 ===== From a43d32237d8490a83873fe076eac5653fbda4f79 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 11:51:59 +1100 Subject: [PATCH 03/57] Handle error frame messages in CanutilsLogWriter and CanutilsLogReader. Closes #217 (cherry picked from commit 61f27e1) --- test/logformats_test.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index 3cc494adb..64f27cc2f 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -112,7 +112,7 @@ def _test_writer_and_reader(test_case, writer_constructor, reader_constructor, s for i, (read, original) in enumerate(zip(read_messages, original_messages)): try: test_case.assertEqual(read, original) - test_case.assertAlmostEqual(read.timestamp, original.timestamp) + test_case.assertAlmostEqual(read.timestamp, original.timestamp, places=6) except Exception as exception: # attach the index exception.args += ("test failed at index #{}".format(i), ) @@ -133,7 +133,6 @@ class TestCanutilsLog(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.CanutilsLogWriter, can.CanutilsLogReader, - check_error_frames=False, # TODO this should get fixed, see Issue #217 check_comments=False) @@ -142,7 +141,6 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_error_frames=False, # TODO this should get fixed, see Issue #218 check_comments=True) @@ -192,7 +190,6 @@ class TestBlfFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.BLFWriter, can.BLFReader, - sleep_time=None, check_comments=False) def test_reader(self): From 7af1021f9617b12b92f2600ddd593aa04ae2341f Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Sat, 17 Feb 2018 14:05:12 +1100 Subject: [PATCH 04/57] Fix issues in USB2CAN with Python3 Closes #219 --- can/interfaces/usb2can/usb2canInterface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 9d9a68fc6..57c2b330c 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -114,14 +114,14 @@ def __init__(self, channel, *args, **kwargs): br = kwargs["bitrate"] # max rate is 1000 kbps - baudrate = max(1000, int(br/1000)) + baudrate = min(1000, int(br/1000)) # set default value else: baudrate = 500 connector = format_connection_string(deviceID, baudrate) - self.handle = self.can.open(connector, enable_flags) + self.handle = self.can.open(connector.encode('utf-8'), enable_flags) def send(self, msg, timeout=None): tx = message_convert_tx(msg) From 8e30e6097af1e5eda7435a18b38d26a0596d0a4a Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 11:39:48 +1100 Subject: [PATCH 05/57] Support error frame messages in ASCWriter and ASCReader. Closes #218 (cherry picked from commit e17207c) --- can/io/asc.py | 22 +++++++++++++--------- test/data/example_data.py | 10 ++++------ test/logformats_test.py | 4 ++-- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index d69436cf5..9ebecb96d 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -93,8 +93,8 @@ def __iter__(self): class ASCWriter(Listener): """Logs CAN data to an ASCII log file (.asc)""" - LOG_STRING = "{time: 9.4f} {channel} {id:<15} Rx {dtype} {data}\n" - EVENT_STRING = "{time: 9.4f} {message}\n" + LOG_STRING = "{time: 9.4f} {channel} {id:<15} Rx {dtype} {data}\n" + EVENT_STRING = "{time: 9.4f} {message}\n" def __init__(self, filename, channel=1): now = datetime.now().strftime("%a %b %m %I:%M:%S %p %Y") @@ -120,15 +120,19 @@ def log_event(self, message, timestamp=None): logger.debug("ASCWriter: ignoring empty message") return - timestamp = (timestamp or time.time()) + if timestamp is None: + timestamp = time.time() + if timestamp >= self.started: timestamp -= self.started line = self.EVENT_STRING.format(time=timestamp, message=message) + if not self.log_file.closed: self.log_file.write(line) def on_message_received(self, msg): + if msg.is_error_frame: self.log_event("{} ErrorFrame".format(self.channel), msg.timestamp) return @@ -139,18 +143,18 @@ def on_message_received(self, msg): else: dtype = "d {}".format(msg.dlc) data = ["{:02X}".format(byte) for byte in msg.data] + arb_id = "{:X}".format(msg.arbitration_id) - if msg.id_type: - arb_id = arb_id + "x" - timestamp = msg.timestamp - if timestamp >= self.started: - timestamp -= self.started + if msg.is_extended_id: + arb_id += "x" channel = msg.channel if isinstance(msg.channel, int) else self.channel - line = self.LOG_STRING.format(time=timestamp, + + line = self.LOG_STRING.format(time=msg.timestamp, channel=channel, id=arb_id, dtype=dtype, data=" ".join(data)) + if not self.log_file.closed: self.log_file.write(line) diff --git a/test/data/example_data.py b/test/data/example_data.py index bc097f755..6d54d683f 100644 --- a/test/data/example_data.py +++ b/test/data/example_data.py @@ -9,7 +9,6 @@ from can import Message - # make tests more reproducible random.seed(13339115) @@ -73,12 +72,12 @@ TEST_MESSAGES_REMOTE_FRAMES = [ Message( - arbitration_id=0xDADADA, extended_id=True, is_remote_frame=False, + arbitration_id=0xDADADA, extended_id=True, is_remote_frame=True, timestamp=TEST_TIME + .165, data=[1, 2, 3, 4, 5, 6, 7, 8] ), Message( - arbitration_id=0x123, extended_id=False, is_remote_frame=False, + arbitration_id=0x123, extended_id=False, is_remote_frame=True, timestamp=TEST_TIME + .365, data=[254, 255] ), @@ -124,6 +123,5 @@ def generate_message(arbitration_id): Generates a new message with the given ID, some random data and a non-extended ID. """ - data = [random.randrange(0, 2 ** 8 - 1) for _ in range(8)] - msg = Message(arbitration_id=arbitration_id, data=data, extended_id=False) - return msg + data = bytes([random.randrange(0, 2 ** 8 - 1) for _ in range(8)]) + return Message(arbitration_id=arbitration_id, data=data, extended_id=False) diff --git a/test/logformats_test.py b/test/logformats_test.py index 3cc494adb..82c472243 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -112,7 +112,7 @@ def _test_writer_and_reader(test_case, writer_constructor, reader_constructor, s for i, (read, original) in enumerate(zip(read_messages, original_messages)): try: test_case.assertEqual(read, original) - test_case.assertAlmostEqual(read.timestamp, original.timestamp) + test_case.assertAlmostEqual(read.timestamp, original.timestamp, places=6) except Exception as exception: # attach the index exception.args += ("test failed at index #{}".format(i), ) @@ -142,7 +142,7 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_error_frames=False, # TODO this should get fixed, see Issue #218 + check_error_frames=True, check_comments=True) From d8942ce75b3ebaf207bad6ea6da759a166ad8d65 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 11:06:49 +1100 Subject: [PATCH 06/57] Use configured bitrate in pcan. Closes #196 (cherry picked from commit bd26d99) --- can/interfaces/pcan/pcan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 8ff8b162c..8647222d9 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -9,6 +9,7 @@ from can.bus import BusABC from can.message import Message from can import CanError +import can import time boottimeEpoch = 0 @@ -82,7 +83,7 @@ def __init__(self, channel, *args, **kwargs): else: self.channel_info = channel - bitrate = kwargs.get('bitrate', 500000) + bitrate = kwargs.get('bitrate', can.rc.get('bitrate', 500000)) pcan_bitrate = pcan_bitrate_objs.get(bitrate, PCAN_BAUD_500K) hwtype = PCAN_TYPE_ISA From 9ca495263d9815ab3c3d0edbee15744cc7ad60ca Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Sun, 18 Feb 2018 17:03:53 +1100 Subject: [PATCH 07/57] Add misc fixes note to changelog --- CHANGELOG.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.txt b/CHANGELOG.txt index e81cedd16..06b6ef4ee 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -8,6 +8,8 @@ Version 2.1.0 (2018-02-17) * Fix bug in neovi serial number decoding. * Add testing on OSX to TravisCI * Fix non english decoding error on pcan +* Other misc improvements and bug fixes + Version 2.0.0 (2018-01-05 ===== From 1cb8ebc9f3e62c555c0b03bbb1ca8d3442b0c802 Mon Sep 17 00:00:00 2001 From: Brian Thorne Date: Sun, 18 Feb 2018 19:45:09 +1100 Subject: [PATCH 08/57] Update version to "2.1.1-dev" --- can/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/__init__.py b/can/__init__.py index 71bc0f442..574c2ca38 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -5,7 +5,7 @@ import logging -__version__ = "2.1.0" +__version__ = "2.1.1-dev" log = logging.getLogger('can') From 8fe1b5a74d90e703c6b0a4332fd154e6cc47d027 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 17:16:01 +0100 Subject: [PATCH 09/57] general cleanups of the main modules --- can/CAN.py | 6 +++++- can/__init__.py | 4 ++++ can/broadcastmanager.py | 5 +++-- can/bus.py | 6 ++++-- can/ctypesutil.py | 17 ++++++++++------- can/interface.py | 9 +++++++++ can/listener.py | 9 +++++++++ can/logger.py | 4 ++++ can/message.py | 7 +++++++ can/notifier.py | 7 +++++++ can/player.py | 2 ++ can/util.py | 5 ++++- 12 files changed, 68 insertions(+), 13 deletions(-) diff --git a/can/CAN.py b/can/CAN.py index 8e07e3dac..000b7f8c2 100644 --- a/can/CAN.py +++ b/can/CAN.py @@ -1,9 +1,13 @@ +#!/usr/bin/env python +# coding: utf-8 + """ This module was once the core of python-can, containing implementations of all the major classes in the library, now -however all functionality has been refactored out. This api +however all functionality has been refactored out. This API is left intact for version 2.0 to aide with migration. """ + from __future__ import absolute_import from can.message import Message diff --git a/can/__init__.py b/can/__init__.py index 574c2ca38..05ad4b444 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -1,6 +1,10 @@ +#!/usr/bin/env python +# coding: utf-8 + """ can is an object-orient Controller Area Network interface module. """ + from __future__ import absolute_import import logging diff --git a/can/broadcastmanager.py b/can/broadcastmanager.py index f7ff2f2d8..689f2777d 100644 --- a/can/broadcastmanager.py +++ b/can/broadcastmanager.py @@ -1,7 +1,8 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +# coding: utf-8 + """ Exposes several methods for transmitting cyclic messages. -20/09/13 """ import can diff --git a/can/bus.py b/can/bus.py index f42a4a149..05423c9c7 100644 --- a/can/bus.py +++ b/can/bus.py @@ -1,15 +1,17 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 """ Contains the ABC bus implementation. """ from __future__ import print_function, absolute_import + import abc import logging import threading -from can.broadcastmanager import ThreadBasedCyclicSendTask +from can.broadcastmanager import ThreadBasedCyclicSendTask logger = logging.getLogger(__name__) diff --git a/can/ctypesutil.py b/can/ctypesutil.py index d9166c582..5525f56a8 100644 --- a/can/ctypesutil.py +++ b/can/ctypesutil.py @@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 -" Common ctypes utils " +""" +This module contains common `ctypes` utils. +""" import binascii import ctypes @@ -11,6 +14,11 @@ __all__ = ['CLibrary', 'HANDLE', 'PHANDLE'] +try: + _LibBase = ctypes.WinDLL +except AttributeError: + _LibBase = ctypes.CDLL + class LibraryMixin: def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): @@ -46,11 +54,6 @@ def map_symbol(self, func_name, restype=None, argtypes=(), errcheck=None): return symbol -try: - _LibBase = ctypes.WinDLL -except AttributeError: - _LibBase = ctypes.CDLL - class CLibrary_Win32(_LibBase, LibraryMixin): " Basic ctypes.WinDLL derived class + LibraryMixin " diff --git a/can/interface.py b/can/interface.py index 174f03b58..96a22c780 100644 --- a/can/interface.py +++ b/can/interface.py @@ -1,3 +1,12 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module contains the base implementation of `can.Bus` as well +as a list of all avalibale backends and some implemented +CyclicSendTasks. +""" + from __future__ import absolute_import import can diff --git a/can/listener.py b/can/listener.py index daf90827e..d7e8e7ced 100644 --- a/can/listener.py +++ b/can/listener.py @@ -1,6 +1,15 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module contains the implementation of `can.Listener` and some readers. +""" + try: + # Python 3 import queue except ImportError: + # Python 2 import Queue as queue diff --git a/can/logger.py b/can/logger.py index ea5424ee7..bacc0b66f 100644 --- a/can/logger.py +++ b/can/logger.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# coding: utf-8 + """ logger.py logs CAN traffic to the terminal and to a file on disk. @@ -14,7 +16,9 @@ Dynamic Controls 2010 """ + from __future__ import print_function + import datetime import argparse import socket diff --git a/can/message.py b/can/message.py index 4f02476dd..e9806c12f 100644 --- a/can/message.py +++ b/can/message.py @@ -1,3 +1,10 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module contains the implementation of `can.Message`. +""" + import logging logger = logging.getLogger(__name__) diff --git a/can/notifier.py b/can/notifier.py index 4c2c59604..a3eaad6e5 100644 --- a/can/notifier.py +++ b/can/notifier.py @@ -1,3 +1,10 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module contains the implementation of `can.Notifier`. +""" + import threading import logging diff --git a/can/player.py b/can/player.py index 8b02a896d..582e5196c 100644 --- a/can/player.py +++ b/can/player.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# coding: utf-8 + """ Replays CAN traffic saved with can.logger back to a CAN bus. diff --git a/can/util.py b/can/util.py index dd1bf67a1..9eeb11e24 100644 --- a/can/util.py +++ b/can/util.py @@ -1,7 +1,10 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +# coding: utf-8 + """ Utilities and configuration file parsing. """ + from __future__ import absolute_import import can From cdc88cbd7f58d9ed0cb4a899e8671ac753d3cc93 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 17:35:35 +0100 Subject: [PATCH 10/57] general cleanup of the can/io/* modules --- can/io/__init__.py | 3 +++ can/io/asc.py | 9 ++++++++- can/io/blf.py | 8 ++++++-- can/io/csv.py | 25 ++++++++++++++++++++----- can/io/log.py | 39 ++++++++++++++++++++++++--------------- can/io/logger.py | 7 +++++++ can/io/player.py | 7 +++++++ can/io/sqlite.py | 4 ++++ can/io/stdout.py | 20 ++++++++++++++------ 9 files changed, 93 insertions(+), 29 deletions(-) diff --git a/can/io/__init__.py b/can/io/__init__.py index fd2738567..79f187ceb 100644 --- a/can/io/__init__.py +++ b/can/io/__init__.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + """ Read and Write CAN bus messages using a range of Readers and Writers based off the file extension. diff --git a/can/io/asc.py b/can/io/asc.py index 9ebecb96d..de3edf807 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -1,3 +1,10 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +Contains handling of ASC logging files. +""" + from datetime import datetime import time import logging @@ -13,7 +20,7 @@ class ASCReader(object): """ - Iterator of CAN messages from a ASC Logging File. + Iterator of CAN messages from a ASC logging file. """ def __init__(self, filename): diff --git a/can/io/blf.py b/can/io/blf.py index bdd994d17..3b96a621a 100644 --- a/can/io/blf.py +++ b/can/io/blf.py @@ -1,10 +1,14 @@ +#!/usr/bin/env python +# coding: utf-8 + """ Implements support for BLF (Binary Logging Format) which is a proprietary -CAN log format from Vector Informatik GmbH. +CAN log format from Vector Informatik GmbH (Germany). No official specification of the binary logging format is available. This implementation is based on Toby Lorenz' C++ library "Vector BLF" which is -licenced under GPLv3. https://bitbucket.org/tobylorenz/vector_blf. +licensed under GPLv3. https://bitbucket.org/tobylorenz/vector_blf. + The file starts with a header. The rest is one or more "log containers" which consists of a header and some zlib compressed data, usually up to 128 kB of uncompressed data each. This data contains the actual CAN messages and other diff --git a/can/io/csv.py b/can/io/csv.py index ba3b29a75..768a7a666 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -1,12 +1,28 @@ -from can.listener import Listener +#!/usr/bin/env python +# coding: utf-8 + +""" +This modules contains the handler for CSV (comma seperated values) files. +""" import base64 +from can.listener import Listener + + +# TODO allow other seperators in CSVWriter +# TODO add a CSVReader class CSVWriter(Listener): """Writes a comma separated text file of - timestamp, arbitration id, flags, dlc, data - for each messages received. + + * timestamp, + * arbitration id, + * flags (extended, remote, error), + * dlc and + * data + + for each messages received. Each line is terminated with a '\\n'. """ def __init__(self, filename): @@ -24,10 +40,9 @@ def on_message_received(self, msg): '1' if msg.is_error_frame else '0', str(msg.dlc), base64.b64encode(msg.data).decode('utf8') - ]) + ]) self.csv_file.write(row + '\n') def stop(self): self.csv_file.flush() self.csv_file.close() - diff --git a/can/io/log.py b/can/io/log.py index 3a7816e56..427b96a3b 100644 --- a/can/io/log.py +++ b/can/io/log.py @@ -1,12 +1,23 @@ -from can.listener import Listener -import datetime +#!/usr/bin/env python +# 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). +""" + import time +import datetime + from can.message import Message +from can.listener import Listener + -CAN_MSG_EXT = 0x80000000 -CAN_ERR_FLAG = 0x20000000 -CAN_ERR_BUSERROR = 0x00000080 -CAN_ERR_DLC = 8 +CAN_MSG_EXT = 0x80000000 +CAN_ERR_FLAG = 0x20000000 +CAN_ERR_BUSERROR = 0x00000080 +CAN_ERR_DLC = 8 class CanutilsLogReader(object): @@ -18,7 +29,7 @@ class CanutilsLogReader(object): """ def __init__(self, filename): - self.fp = open(filename, "r") + self.fp = open(filename, 'r') def __iter__(self): for line in self.fp: @@ -26,13 +37,13 @@ def __iter__(self): if len(temp) > 0: (timestamp, bus, frame) = temp.split() timestamp = float(timestamp[1:-1]) - (canId, data) = frame.split("#") + (canId, data) = frame.split('#') if len(canId) > 3: isExtended = True else: isExtended = False canId = int(canId, 16) - if len(data) > 0 and data[0].lower() == "r": + if len(data) > 0 and data[0].lower() == 'r': isRemoteFrame = True if len(data) > 1: dlc = int(data[1:]) @@ -56,13 +67,14 @@ def __iter__(self): class CanutilsLogWriter(Listener): - """Logs CAN data to an ASCII log file (.log) - compatible to candump -L """ + """Logs CAN data to an ASCII log file (.log). + This class is is compatible with "candump -L". + """ def __init__(self, filename, channel="vcan0"): self.channel = channel self.started = time.time() - self.log_file = open(filename, "w") + self.log_file = open(filename, 'w') def stop(self): """Stops logging and closes the file.""" @@ -93,6 +105,3 @@ def on_message_received(self, msg): self.log_file.write("(%f) vcan0 %08X#%s\n" % (msg.timestamp, msg.arbitration_id, "".join(data))) else: self.log_file.write("(%f) vcan0 %03X#%s\n" % (msg.timestamp, msg.arbitration_id, "".join(data))) - - - diff --git a/can/io/logger.py b/can/io/logger.py index 450f33c1c..78c93f07b 100755 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -1,3 +1,10 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +See the `Logger` class. +""" + from .asc import ASCWriter from .blf import BLFWriter from .csv import CSVWriter diff --git a/can/io/player.py b/can/io/player.py index a9f3c07c7..22668f4f6 100755 --- a/can/io/player.py +++ b/can/io/player.py @@ -1,4 +1,11 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from __future__ import print_function + import time import logging diff --git a/can/io/sqlite.py b/can/io/sqlite.py index fbb0895a2..454a09f2a 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + """ Implements an SQL database writer and reader for storing CAN messages. @@ -64,6 +67,7 @@ def close(self): # Backward compatibility +# TODO remove in later releases? SqlReader = SqliteReader diff --git a/can/io/stdout.py b/can/io/stdout.py index a85bbe04b..701a77c22 100644 --- a/can/io/stdout.py +++ b/can/io/stdout.py @@ -1,31 +1,39 @@ -from can.listener import Listener +#!/usr/bin/env python +# coding: utf-8 + +""" +This Listener simply prints to stdout / the terminal or a file. +""" + +from __future__ import print_function import logging +from can.listener import Listener + log = logging.getLogger('can.io.stdout') class Printer(Listener): """ The Printer class is a subclass of :class:`~can.Listener` which simply prints - any messages it receives to the terminal. + any messages it receives to the terminal (stdout). :param output_file: An optional file to "print" to. """ def __init__(self, output_file=None): if output_file is not None: - log.info("Creating log file '{}' ".format(output_file)) + log.info('Creating log file "{}"'.format(output_file)) output_file = open(output_file, 'wt') self.output_file = output_file def on_message_received(self, msg): if self.output_file is not None: - self.output_file.write(str(msg) + "\n") + self.output_file.write(str(msg) + '\n') else: print(msg) def stop(self): if self.output_file: - self.output_file.write("\n") + self.output_file.write('\n') self.output_file.close() - From 102be0d5e7877ee61f95b5cb323e6e9c3e432145 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 17:43:40 +0100 Subject: [PATCH 11/57] general cleanup of the can/interfaces/ modules (not yes the ones in child folders) --- can/interfaces/__init__.py | 6 +++-- can/interfaces/interface.py | 0 can/interfaces/iscan.py | 12 ++++++---- can/interfaces/nican.py | 48 ++++++++++++++++++++----------------- can/interfaces/slcan.py | 6 ++++- can/interfaces/virtual.py | 8 +++---- 6 files changed, 47 insertions(+), 33 deletions(-) delete mode 100644 can/interfaces/interface.py diff --git a/can/interfaces/__init__.py b/can/interfaces/__init__.py index 1bd132731..89ff2cdb7 100644 --- a/can/interfaces/__init__.py +++ b/can/interfaces/__init__.py @@ -1,7 +1,10 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Interfaces contain low level implementations that interact with CAN hardware. """ + from pkg_resources import iter_entry_points VALID_INTERFACES = set(['kvaser', 'serial', 'pcan', 'socketcan_native', @@ -9,7 +12,6 @@ 'nican', 'iscan', 'vector', 'virtual', 'neovi', 'slcan']) - VALID_INTERFACES.update(set([ interface.name for interface in iter_entry_points('python_can.interface') ])) diff --git a/can/interfaces/interface.py b/can/interfaces/interface.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index bc28818d3..6fd18b49b 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -1,17 +1,21 @@ +#!/usr/bin/env python +# coding: utf-8 + """ Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH. """ + import ctypes import time import logging from can import CanError, BusABC, Message - logger = logging.getLogger(__name__) CanData = ctypes.c_ubyte * 8 + class MessageExStruct(ctypes.Structure): _fields_ = [ ("message_id", ctypes.c_ulong), @@ -148,11 +152,11 @@ class IscanError(CanError): def __init__(self, function, error_code, arguments): super(IscanError, self).__init__() - #: Status code + # Status code self.error_code = error_code - #: Function that failed + # Function that failed self.function = function - #: Arguments passed to function + # Arguments passed to function self.arguments = arguments def __str__(self): diff --git a/can/interfaces/nican.py b/can/interfaces/nican.py index e4c7f9844..7d3a59fb3 100644 --- a/can/interfaces/nican.py +++ b/can/interfaces/nican.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + """ NI-CAN interface module. @@ -5,6 +8,7 @@ * http://www.ni.com/pdf/manuals/370289c.pdf * https://github.com/buendiya/NicanPython """ + import ctypes import logging import sys @@ -13,39 +17,39 @@ logger = logging.getLogger(__name__) -NC_SUCCESS = 0 -NC_ERR_TIMEOUT = 1 -TIMEOUT_ERROR_CODE = -1074388991 +NC_SUCCESS = 0 +NC_ERR_TIMEOUT = 1 +TIMEOUT_ERROR_CODE = -1074388991 -NC_DURATION_INFINITE = 0xFFFFFFFF +NC_DURATION_INFINITE = 0xFFFFFFFF -NC_OP_START = 0x80000001 -NC_OP_STOP = 0x80000002 -NC_OP_RESET = 0x80000003 +NC_OP_START = 0x80000001 +NC_OP_STOP = 0x80000002 +NC_OP_RESET = 0x80000003 -NC_FRMTYPE_REMOTE = 1 -NC_FRMTYPE_COMM_ERR = 2 +NC_FRMTYPE_REMOTE = 1 +NC_FRMTYPE_COMM_ERR = 2 -NC_ST_READ_AVAIL = 0x00000001 -NC_ST_WRITE_SUCCESS = 0x00000002 -NC_ST_ERROR = 0x00000010 -NC_ST_WARNING = 0x00000020 +NC_ST_READ_AVAIL = 0x00000001 +NC_ST_WRITE_SUCCESS = 0x00000002 +NC_ST_ERROR = 0x00000010 +NC_ST_WARNING = 0x00000020 -NC_ATTR_BAUD_RATE = 0x80000007 +NC_ATTR_BAUD_RATE = 0x80000007 NC_ATTR_START_ON_OPEN = 0x80000006 -NC_ATTR_READ_Q_LEN = 0x80000013 -NC_ATTR_WRITE_Q_LEN = 0x80000014 -NC_ATTR_CAN_COMP_STD = 0x80010001 -NC_ATTR_CAN_MASK_STD = 0x80010002 -NC_ATTR_CAN_COMP_XTD = 0x80010003 -NC_ATTR_CAN_MASK_XTD = 0x80010004 +NC_ATTR_READ_Q_LEN = 0x80000013 +NC_ATTR_WRITE_Q_LEN = 0x80000014 +NC_ATTR_CAN_COMP_STD = 0x80010001 +NC_ATTR_CAN_MASK_STD = 0x80010002 +NC_ATTR_CAN_COMP_XTD = 0x80010003 +NC_ATTR_CAN_MASK_XTD = 0x80010004 NC_ATTR_LOG_COMM_ERRS = 0x8001000A -NC_FL_CAN_ARBID_XTD = 0x20000000 - +NC_FL_CAN_ARBID_XTD = 0x20000000 CanData = ctypes.c_ubyte * 8 + class RxMessageStruct(ctypes.Structure): _pack_ = 1 _fields_ = [ diff --git a/can/interfaces/slcan.py b/can/interfaces/slcan.py index 4f8499028..bd5990eac 100755 --- a/can/interfaces/slcan.py +++ b/can/interfaces/slcan.py @@ -1,7 +1,10 @@ +#!/usr/bin/env python +# coding: utf-8 + """ Interface for slcan compatible interfaces (win32/linux). -Note Linux users can use slcand/socketcan as well. +Note: Linux users can use slcand/socketcan as well. """ from __future__ import absolute_import @@ -85,6 +88,7 @@ def __init__(self, channel, ttyBaudrate=115200, timeout=1, bitrate=None, **kwarg raise ValueError("Invalid bitrate, choose one of " + (', '.join(self._BITRATES)) + '.') self.open() + super(slcanBus, self).__init__(channel, **kwargs) def recv(self, timeout=None): diff --git a/can/interfaces/virtual.py b/can/interfaces/virtual.py index a51dd10d0..2a7186b77 100644 --- a/can/interfaces/virtual.py +++ b/can/interfaces/virtual.py @@ -1,11 +1,12 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 """ This module implements an OS and hardware independent virtual CAN interface for testing purposes. Any VirtualBus instances connecting to the same channel -will get the same messages. +and reside in the same process will receive the same messages. """ import logging @@ -14,11 +15,10 @@ import queue except ImportError: import Queue as queue -from can.bus import BusABC +from can.bus import BusABC logger = logging.getLogger(__name__) -#logger.setLevel(logging.DEBUG) # Channels are lists of queues, one for each connection From 23d928e2f89119a538411c70ded15a0d1a8cf263 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 18:36:33 +0100 Subject: [PATCH 12/57] general cleanup of the can/interfaces/*/* modules --- can/interfaces/ics_neovi/__init__.py | 6 + can/interfaces/ics_neovi/neovi_bus.py | 3 + can/interfaces/ixxat/__init__.py | 5 +- can/interfaces/ixxat/canlib.py | 31 +-- can/interfaces/ixxat/constants.py | 91 ++++---- can/interfaces/ixxat/exceptions.py | 5 +- can/interfaces/ixxat/structures.py | 5 +- can/interfaces/kvaser/__init__.py | 6 + can/interfaces/kvaser/argument_parser.py | 6 + can/interfaces/kvaser/canlib.py | 4 +- can/interfaces/kvaser/constants.py | 6 +- can/interfaces/pcan/PCANBasic.py | 215 +++++++----------- can/interfaces/pcan/__init__.py | 6 + can/interfaces/pcan/pcan.py | 19 +- can/interfaces/serial/__init__.py | 6 + can/interfaces/serial/serial_can.py | 7 +- can/interfaces/socketcan/__init__.py | 8 +- can/interfaces/socketcan/socketcan_common.py | 6 +- .../socketcan/socketcan_constants.py | 35 +-- can/interfaces/socketcan/socketcan_ctypes.py | 7 +- can/interfaces/socketcan/socketcan_native.py | 3 +- can/interfaces/usb2can/__init__.py | 6 + can/interfaces/usb2can/serial_selector.py | 15 +- can/interfaces/usb2can/usb2canInterface.py | 8 +- .../usb2can/usb2canabstractionlayer.py | 9 +- can/interfaces/vector/__init__.py | 6 + can/interfaces/vector/canlib.py | 9 +- can/interfaces/vector/exceptions.py | 6 + can/interfaces/vector/vxlapi.py | 8 +- 29 files changed, 307 insertions(+), 240 deletions(-) diff --git a/can/interfaces/ics_neovi/__init__.py b/can/interfaces/ics_neovi/__init__.py index 5b1aa2052..9e9f2b0ba 100644 --- a/can/interfaces/ics_neovi/__init__.py +++ b/can/interfaces/ics_neovi/__init__.py @@ -1 +1,7 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from can.interfaces.ics_neovi.neovi_bus import NeoViBus diff --git a/can/interfaces/ics_neovi/neovi_bus.py b/can/interfaces/ics_neovi/neovi_bus.py index 5e889091c..dd49b04ce 100644 --- a/can/interfaces/ics_neovi/neovi_bus.py +++ b/can/interfaces/ics_neovi/neovi_bus.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + """ ICS NeoVi interface module. diff --git a/can/interfaces/ixxat/__init__.py b/can/interfaces/ixxat/__init__.py index 266ddbb35..ab4e1f08c 100644 --- a/can/interfaces/ixxat/__init__.py +++ b/can/interfaces/ixxat/__init__.py @@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + Copyright (C) 2016 Giuseppe Corbelli """ diff --git a/can/interfaces/ixxat/canlib.py b/can/interfaces/ixxat/canlib.py index 1b098200a..cb0ea9176 100644 --- a/can/interfaces/ixxat/canlib.py +++ b/can/interfaces/ixxat/canlib.py @@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + Copyright (C) 2016 Giuseppe Corbelli """ @@ -12,11 +15,12 @@ from can import CanError, BusABC from can import Message -from can.interfaces.ixxat import constants, structures from can.broadcastmanager import (LimitedDurationCyclicSendTaskABC, RestartableCyclicTaskABC) from can.ctypesutil import CLibrary, HANDLE, PHANDLE +from can.interfaces.ixxat import constants, structures + from .constants import VCI_MAX_ERRSTRLEN from .exceptions import * @@ -24,9 +28,10 @@ log = logging.getLogger('can.ixxat') -if ((sys.version_info.major == 3) and (sys.version_info.minor >= 3)): +try: + # since Python 3.3 _timer_function = time.perf_counter -else: +except AttributeError: _timer_function = time.clock # Hack to have vciFormatError as a free function, see below @@ -203,18 +208,18 @@ def __check_status(result, function, arguments): CAN_INFO_MESSAGES = { - constants.CAN_INFO_START: "CAN started", - constants.CAN_INFO_STOP: "CAN stopped", - constants.CAN_INFO_RESET: "CAN reset", + constants.CAN_INFO_START: "CAN started", + constants.CAN_INFO_STOP: "CAN stopped", + constants.CAN_INFO_RESET: "CAN reset", } CAN_ERROR_MESSAGES = { - constants.CAN_ERROR_STUFF: "CAN bit stuff error", - constants.CAN_ERROR_FORM: "CAN form error", - constants.CAN_ERROR_ACK: "CAN acknowledgment error", - constants.CAN_ERROR_BIT: "CAN bit error", - constants.CAN_ERROR_CRC: "CAN CRC error", - constants.CAN_ERROR_OTHER: "Other (unknown) CAN error", + constants.CAN_ERROR_STUFF: "CAN bit stuff error", + constants.CAN_ERROR_FORM: "CAN form error", + constants.CAN_ERROR_ACK: "CAN acknowledgment error", + constants.CAN_ERROR_BIT: "CAN bit error", + constants.CAN_ERROR_CRC: "CAN CRC error", + constants.CAN_ERROR_OTHER: "Other (unknown) CAN error", } #---------------------------------------------------------------------------- diff --git a/can/interfaces/ixxat/constants.py b/can/interfaces/ixxat/constants.py index 966a77f18..62505dcc5 100644 --- a/can/interfaces/ixxat/constants.py +++ b/can/interfaces/ixxat/constants.py @@ -1,45 +1,48 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + Copyright (C) 2016 Giuseppe Corbelli """ -FALSE = 0 -TRUE = 1 +FALSE = 0 +TRUE = 1 -INFINITE = 0xFFFFFFFF +INFINITE = 0xFFFFFFFF VCI_MAX_ERRSTRLEN = 256 # Bitrates -CAN_BT0_10KB = 0x31 -CAN_BT1_10KB = 0x1C -CAN_BT0_20KB = 0x18 -CAN_BT1_20KB = 0x1C -CAN_BT0_50KB = 0x09 -CAN_BT1_50KB = 0x1C -CAN_BT0_100KB = 0x04 -CAN_BT1_100KB = 0x1C -CAN_BT0_125KB = 0x03 -CAN_BT1_125KB = 0x1C -CAN_BT0_250KB = 0x01 -CAN_BT1_250KB = 0x1C -CAN_BT0_500KB = 0x00 -CAN_BT1_500KB = 0x1C -CAN_BT0_800KB = 0x00 -CAN_BT1_800KB = 0x16 -CAN_BT0_1000KB = 0x00 -CAN_BT1_1000KB = 0x14 +CAN_BT0_10KB = 0x31 +CAN_BT1_10KB = 0x1C +CAN_BT0_20KB = 0x18 +CAN_BT1_20KB = 0x1C +CAN_BT0_50KB = 0x09 +CAN_BT1_50KB = 0x1C +CAN_BT0_100KB = 0x04 +CAN_BT1_100KB = 0x1C +CAN_BT0_125KB = 0x03 +CAN_BT1_125KB = 0x1C +CAN_BT0_250KB = 0x01 +CAN_BT1_250KB = 0x1C +CAN_BT0_500KB = 0x00 +CAN_BT1_500KB = 0x1C +CAN_BT0_800KB = 0x00 +CAN_BT1_800KB = 0x16 +CAN_BT0_1000KB = 0x00 +CAN_BT1_1000KB = 0x14 # Facilities/severities -SEV_INFO = 0x40000000 -SEV_WARN = 0x80000000 -SEV_ERROR = 0xC0000000 -SEV_MASK = 0xC0000000 -SEV_SUCCESS = 0x00000000 +SEV_INFO = 0x40000000 +SEV_WARN = 0x80000000 +SEV_ERROR = 0xC0000000 +SEV_MASK = 0xC0000000 +SEV_SUCCESS = 0x00000000 -RESERVED_FLAG = 0x10000000 -CUSTOMER_FLAG = 0x20000000 +RESERVED_FLAG = 0x10000000 +CUSTOMER_FLAG = 0x20000000 STATUS_MASK = 0x0000FFFF FACILITY_MASK = 0x0FFF0000 @@ -102,12 +105,12 @@ VCI_E_WRONG_FLASHFWVERSION = SEV_VCI_ERROR | 0x001A # Controller status -CAN_STATUS_TXPEND = 0x01 -CAN_STATUS_OVRRUN = 0x02 -CAN_STATUS_ERRLIM = 0x04 -CAN_STATUS_BUSOFF = 0x08 -CAN_STATUS_ININIT = 0x10 -CAN_STATUS_BUSCERR = 0x20 +CAN_STATUS_TXPEND = 0x01 +CAN_STATUS_OVRRUN = 0x02 +CAN_STATUS_ERRLIM = 0x04 +CAN_STATUS_BUSOFF = 0x08 +CAN_STATUS_ININIT = 0x10 +CAN_STATUS_BUSCERR = 0x20 # Controller operating modes CAN_OPMODE_UNDEFINED = 0x00 @@ -128,18 +131,18 @@ # Information supplied in the abData[0] field of info frames # (CANMSGINFO.Bytes.bType = CAN_MSGTYPE_INFO). -CAN_INFO_START = 1 -CAN_INFO_STOP = 2 -CAN_INFO_RESET = 3 +CAN_INFO_START = 1 +CAN_INFO_STOP = 2 +CAN_INFO_RESET = 3 # Information supplied in the abData[0] field of info frames # (CANMSGINFO.Bytes.bType = CAN_MSGTYPE_ERROR). -CAN_ERROR_STUFF = 1 # stuff error -CAN_ERROR_FORM = 2 # form error -CAN_ERROR_ACK = 3 # acknowledgment error -CAN_ERROR_BIT = 4 # bit error -CAN_ERROR_CRC = 6 # CRC error -CAN_ERROR_OTHER = 7 # other (unspecified) error +CAN_ERROR_STUFF = 1 # stuff error +CAN_ERROR_FORM = 2 # form error +CAN_ERROR_ACK = 3 # acknowledgment error +CAN_ERROR_BIT = 4 # bit error +CAN_ERROR_CRC = 6 # CRC error +CAN_ERROR_OTHER = 7 # other (unspecified) error # acceptance code and mask to reject all CAN IDs CAN_ACC_MASK_NONE = 0xFFFFFFFF diff --git a/can/interfaces/ixxat/exceptions.py b/can/interfaces/ixxat/exceptions.py index db5de5445..9ac5b8f80 100644 --- a/can/interfaces/ixxat/exceptions.py +++ b/can/interfaces/ixxat/exceptions.py @@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + Copyright (C) 2016 Giuseppe Corbelli """ diff --git a/can/interfaces/ixxat/structures.py b/can/interfaces/ixxat/structures.py index bfbacae1c..93eadd37c 100644 --- a/can/interfaces/ixxat/structures.py +++ b/can/interfaces/ixxat/structures.py @@ -1,6 +1,9 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Ctypes wrapper module for IXXAT Virtual CAN Interface V3 on win32 systems + Copyright (C) 2016 Giuseppe Corbelli """ diff --git a/can/interfaces/kvaser/__init__.py b/can/interfaces/kvaser/__init__.py index c9d291dde..c55ce39ed 100644 --- a/can/interfaces/kvaser/__init__.py +++ b/can/interfaces/kvaser/__init__.py @@ -1 +1,7 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from can.interfaces.kvaser.canlib import * diff --git a/can/interfaces/kvaser/argument_parser.py b/can/interfaces/kvaser/argument_parser.py index 230648aaf..63b11cb48 100644 --- a/can/interfaces/kvaser/argument_parser.py +++ b/can/interfaces/kvaser/argument_parser.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +TODO: Where is this used? Is this used? +""" def add_to_parser(parser): parser.add_argument("-c", "--channel", type=str, dest="channel", diff --git a/can/interfaces/kvaser/canlib.py b/can/interfaces/kvaser/canlib.py index f430356e7..51cd84d55 100644 --- a/can/interfaces/kvaser/canlib.py +++ b/can/interfaces/kvaser/canlib.py @@ -1,4 +1,6 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Contains Python equivalents of the function and constant definitions in CANLIB's canlib.h, with some supporting functionality diff --git a/can/interfaces/kvaser/constants.py b/can/interfaces/kvaser/constants.py index 20ca5204e..1c658dce0 100644 --- a/can/interfaces/kvaser/constants.py +++ b/can/interfaces/kvaser/constants.py @@ -1,17 +1,21 @@ +#!/usr/bin/env python +# coding: utf-8 + """ Contains Python equivalents of the function and constant definitions in CANLIB's canstat.h, with some supporting functionality specific to Python. Copyright (C) 2010 Dynamic Controls - """ + import ctypes class c_canStatus(ctypes.c_int): pass +# TODO better formatting canOK = 0 canERR_PARAM = -1 canERR_NOMSG = -2 diff --git a/can/interfaces/pcan/PCANBasic.py b/can/interfaces/pcan/PCANBasic.py index 8f023adc2..2fc442627 100644 --- a/can/interfaces/pcan/PCANBasic.py +++ b/can/interfaces/pcan/PCANBasic.py @@ -1,29 +1,19 @@ -# PCANBasic.py -# -# ~~~~~~~~~~~~ -# -# PCAN-Basic API -# -# ~~~~~~~~~~~~ -# -# ------------------------------------------------------------------ -# Author : Keneth Wagner -# Last change: 18.05.2016 Wagner -# -# Language: Python 2.7 -# ------------------------------------------------------------------ -# -# Copyright (C) 1999-2016 PEAK-System Technik GmbH, Darmstadt -# more Info at http://www.peak-system.com -# +#!/usr/bin/env python +# coding: utf-8 + +""" +PCAN-Basic API + +Author: Keneth Wagner + +Copyright (C) 1999-2016 PEAK-System Technik GmbH, Darmstadt, Germany +http://www.peak-system.com +""" -# Module Imports -# from ctypes import * import platform import logging -# Patched for python-can: use logger instead of print() logger = logging.getLogger('can.pcan') #/////////////////////////////////////////////////////////// @@ -46,7 +36,7 @@ #/////////////////////////////////////////////////////////// # Currently defined and supported PCAN channels -# + PCAN_NONEBUS = TPCANHandle(0x00) # Undefined/default value for a PCAN bus PCAN_ISABUS1 = TPCANHandle(0x21) # PCAN-ISA interface, channel 1 @@ -65,17 +55,17 @@ PCAN_PCIBUS3 = TPCANHandle(0x43) # PCAN-PCI interface, channel 3 PCAN_PCIBUS4 = TPCANHandle(0x44) # PCAN-PCI interface, channel 4 PCAN_PCIBUS5 = TPCANHandle(0x45) # PCAN-PCI interface, channel 5 -PCAN_PCIBUS6 = TPCANHandle(0x46) # PCAN-PCI interface, channel 6 -PCAN_PCIBUS7 = TPCANHandle(0x47) # PCAN-PCI interface, channel 7 -PCAN_PCIBUS8 = TPCANHandle(0x48) # PCAN-PCI interface, channel 8 -PCAN_PCIBUS9 = TPCANHandle(0x409) # PCAN-PCI interface, channel 9 -PCAN_PCIBUS10 = TPCANHandle(0x40A) # PCAN-PCI interface, channel 10 -PCAN_PCIBUS11 = TPCANHandle(0x40B) # PCAN-PCI interface, channel 11 -PCAN_PCIBUS12 = TPCANHandle(0x40C) # PCAN-PCI interface, channel 12 -PCAN_PCIBUS13 = TPCANHandle(0x40D) # PCAN-PCI interface, channel 13 -PCAN_PCIBUS14 = TPCANHandle(0x40E) # PCAN-PCI interface, channel 14 -PCAN_PCIBUS15 = TPCANHandle(0x40F) # PCAN-PCI interface, channel 15 -PCAN_PCIBUS16 = TPCANHandle(0x410) # PCAN-PCI interface, channel 16 +PCAN_PCIBUS6 = TPCANHandle(0x46) # PCAN-PCI interface, channel 6 +PCAN_PCIBUS7 = TPCANHandle(0x47) # PCAN-PCI interface, channel 7 +PCAN_PCIBUS8 = TPCANHandle(0x48) # PCAN-PCI interface, channel 8 +PCAN_PCIBUS9 = TPCANHandle(0x409) # PCAN-PCI interface, channel 9 +PCAN_PCIBUS10 = TPCANHandle(0x40A) # PCAN-PCI interface, channel 10 +PCAN_PCIBUS11 = TPCANHandle(0x40B) # PCAN-PCI interface, channel 11 +PCAN_PCIBUS12 = TPCANHandle(0x40C) # PCAN-PCI interface, channel 12 +PCAN_PCIBUS13 = TPCANHandle(0x40D) # PCAN-PCI interface, channel 13 +PCAN_PCIBUS14 = TPCANHandle(0x40E) # PCAN-PCI interface, channel 14 +PCAN_PCIBUS15 = TPCANHandle(0x40F) # PCAN-PCI interface, channel 15 +PCAN_PCIBUS16 = TPCANHandle(0x410) # PCAN-PCI interface, channel 16 PCAN_USBBUS1 = TPCANHandle(0x51) # PCAN-USB interface, channel 1 PCAN_USBBUS2 = TPCANHandle(0x52) # PCAN-USB interface, channel 2 @@ -85,37 +75,36 @@ PCAN_USBBUS6 = TPCANHandle(0x56) # PCAN-USB interface, channel 6 PCAN_USBBUS7 = TPCANHandle(0x57) # PCAN-USB interface, channel 7 PCAN_USBBUS8 = TPCANHandle(0x58) # PCAN-USB interface, channel 8 -PCAN_USBBUS9 = TPCANHandle(0x509) # PCAN-USB interface, channel 9 -PCAN_USBBUS10 = TPCANHandle(0x50A) # PCAN-USB interface, channel 10 -PCAN_USBBUS11 = TPCANHandle(0x50B) # PCAN-USB interface, channel 11 -PCAN_USBBUS12 = TPCANHandle(0x50C) # PCAN-USB interface, channel 12 -PCAN_USBBUS13 = TPCANHandle(0x50D) # PCAN-USB interface, channel 13 -PCAN_USBBUS14 = TPCANHandle(0x50E) # PCAN-USB interface, channel 14 -PCAN_USBBUS15 = TPCANHandle(0x50F) # PCAN-USB interface, channel 15 -PCAN_USBBUS16 = TPCANHandle(0x510) # PCAN-USB interface, channel 16 +PCAN_USBBUS9 = TPCANHandle(0x509) # PCAN-USB interface, channel 9 +PCAN_USBBUS10 = TPCANHandle(0x50A) # PCAN-USB interface, channel 10 +PCAN_USBBUS11 = TPCANHandle(0x50B) # PCAN-USB interface, channel 11 +PCAN_USBBUS12 = TPCANHandle(0x50C) # PCAN-USB interface, channel 12 +PCAN_USBBUS13 = TPCANHandle(0x50D) # PCAN-USB interface, channel 13 +PCAN_USBBUS14 = TPCANHandle(0x50E) # PCAN-USB interface, channel 14 +PCAN_USBBUS15 = TPCANHandle(0x50F) # PCAN-USB interface, channel 15 +PCAN_USBBUS16 = TPCANHandle(0x510) # PCAN-USB interface, channel 16 PCAN_PCCBUS1 = TPCANHandle(0x61) # PCAN-PC Card interface, channel 1 PCAN_PCCBUS2 = TPCANHandle(0x62) # PCAN-PC Card interface, channel 2 -PCAN_LANBUS1 = TPCANHandle(0x801) # PCAN-LAN interface, channel 1 -PCAN_LANBUS2 = TPCANHandle(0x802) # PCAN-LAN interface, channel 2 -PCAN_LANBUS3 = TPCANHandle(0x803) # PCAN-LAN interface, channel 3 -PCAN_LANBUS4 = TPCANHandle(0x804) # PCAN-LAN interface, channel 4 -PCAN_LANBUS5 = TPCANHandle(0x805) # PCAN-LAN interface, channel 5 -PCAN_LANBUS6 = TPCANHandle(0x806) # PCAN-LAN interface, channel 6 -PCAN_LANBUS7 = TPCANHandle(0x807) # PCAN-LAN interface, channel 7 -PCAN_LANBUS8 = TPCANHandle(0x808) # PCAN-LAN interface, channel 8 -PCAN_LANBUS9 = TPCANHandle(0x809) # PCAN-LAN interface, channel 9 -PCAN_LANBUS10 = TPCANHandle(0x80A) # PCAN-LAN interface, channel 10 -PCAN_LANBUS11 = TPCANHandle(0x80B) # PCAN-LAN interface, channel 11 -PCAN_LANBUS12 = TPCANHandle(0x80C) # PCAN-LAN interface, channel 12 -PCAN_LANBUS13 = TPCANHandle(0x80D) # PCAN-LAN interface, channel 13 -PCAN_LANBUS14 = TPCANHandle(0x80E) # PCAN-LAN interface, channel 14 -PCAN_LANBUS15 = TPCANHandle(0x80F) # PCAN-LAN interface, channel 15 -PCAN_LANBUS16 = TPCANHandle(0x810) # PCAN-LAN interface, channel 16 +PCAN_LANBUS1 = TPCANHandle(0x801) # PCAN-LAN interface, channel 1 +PCAN_LANBUS2 = TPCANHandle(0x802) # PCAN-LAN interface, channel 2 +PCAN_LANBUS3 = TPCANHandle(0x803) # PCAN-LAN interface, channel 3 +PCAN_LANBUS4 = TPCANHandle(0x804) # PCAN-LAN interface, channel 4 +PCAN_LANBUS5 = TPCANHandle(0x805) # PCAN-LAN interface, channel 5 +PCAN_LANBUS6 = TPCANHandle(0x806) # PCAN-LAN interface, channel 6 +PCAN_LANBUS7 = TPCANHandle(0x807) # PCAN-LAN interface, channel 7 +PCAN_LANBUS8 = TPCANHandle(0x808) # PCAN-LAN interface, channel 8 +PCAN_LANBUS9 = TPCANHandle(0x809) # PCAN-LAN interface, channel 9 +PCAN_LANBUS10 = TPCANHandle(0x80A) # PCAN-LAN interface, channel 10 +PCAN_LANBUS11 = TPCANHandle(0x80B) # PCAN-LAN interface, channel 11 +PCAN_LANBUS12 = TPCANHandle(0x80C) # PCAN-LAN interface, channel 12 +PCAN_LANBUS13 = TPCANHandle(0x80D) # PCAN-LAN interface, channel 13 +PCAN_LANBUS14 = TPCANHandle(0x80E) # PCAN-LAN interface, channel 14 +PCAN_LANBUS15 = TPCANHandle(0x80F) # PCAN-LAN interface, channel 15 +PCAN_LANBUS16 = TPCANHandle(0x810) # PCAN-LAN interface, channel 16 # Represent the PCAN error and status codes -# PCAN_ERROR_OK = TPCANStatus(0x00000) # No error PCAN_ERROR_XMTFULL = TPCANStatus(0x00001) # Transmit buffer in CAN controller is full PCAN_ERROR_OVERRUN = TPCANStatus(0x00002) # CAN controller was read too late @@ -146,19 +135,17 @@ PCAN_ERROR_ILLOPERATION = TPCANStatus(0x8000000)# Invalid operation [Value was changed from 0x80000 to 0x8000000] # PCAN devices -# -PCAN_NONE = TPCANDevice(0x00) # Undefined, unknown or not selected PCAN device value -PCAN_PEAKCAN = TPCANDevice(0x01) # PCAN Non-Plug&Play devices. NOT USED WITHIN PCAN-Basic API -PCAN_ISA = TPCANDevice(0x02) # PCAN-ISA, PCAN-PC/104, and PCAN-PC/104-Plus -PCAN_DNG = TPCANDevice(0x03) # PCAN-Dongle -PCAN_PCI = TPCANDevice(0x04) # PCAN-PCI, PCAN-cPCI, PCAN-miniPCI, and PCAN-PCI Express -PCAN_USB = TPCANDevice(0x05) # PCAN-USB and PCAN-USB Pro -PCAN_PCC = TPCANDevice(0x06) # PCAN-PC Card -PCAN_VIRTUAL = TPCANDevice(0x07) # PCAN Virtual hardware. NOT USED WITHIN PCAN-Basic API -PCAN_LAN = TPCANDevice(0x08) # PCAN Gateway devices +PCAN_NONE = TPCANDevice(0x00) # Undefined, unknown or not selected PCAN device value +PCAN_PEAKCAN = TPCANDevice(0x01) # PCAN Non-Plug&Play devices. NOT USED WITHIN PCAN-Basic API +PCAN_ISA = TPCANDevice(0x02) # PCAN-ISA, PCAN-PC/104, and PCAN-PC/104-Plus +PCAN_DNG = TPCANDevice(0x03) # PCAN-Dongle +PCAN_PCI = TPCANDevice(0x04) # PCAN-PCI, PCAN-cPCI, PCAN-miniPCI, and PCAN-PCI Express +PCAN_USB = TPCANDevice(0x05) # PCAN-USB and PCAN-USB Pro +PCAN_PCC = TPCANDevice(0x06) # PCAN-PC Card +PCAN_VIRTUAL = TPCANDevice(0x07) # PCAN Virtual hardware. NOT USED WITHIN PCAN-Basic API +PCAN_LAN = TPCANDevice(0x08) # PCAN Gateway devices # PCAN parameters -# PCAN_DEVICE_NUMBER = TPCANParameter(0x01) # PCAN-USB device number parameter PCAN_5VOLTS_POWER = TPCANParameter(0x02) # PCAN-PC Card 5-Volt power parameter PCAN_RECEIVE_EVENT = TPCANParameter(0x03) # PCAN receive event handler parameter @@ -190,7 +177,6 @@ PCAN_LAN_SERVICE_STATUS = TPCANParameter(0x1D) # Status of the Virtual PCAN-Gateway Service # PCAN parameter values -# PCAN_PARAMETER_OFF = int(0x00) # The PCAN parameter is not set (inactive) PCAN_PARAMETER_ON = int(0x01) # The PCAN parameter is set (active) PCAN_FILTER_CLOSE = int(0x00) # The PCAN filter is closed. No messages will be received @@ -201,13 +187,13 @@ PCAN_CHANNEL_OCCUPIED = int(0x02) # The PCAN-Channel handle is valid, and is already being used PCAN_CHANNEL_PCANVIEW = PCAN_CHANNEL_AVAILABLE | PCAN_CHANNEL_OCCUPIED # The PCAN-Channel handle is already being used by a PCAN-View application, but is available to connect -LOG_FUNCTION_DEFAULT = int(0x00) # Logs system exceptions / errors -LOG_FUNCTION_ENTRY = int(0x01) # Logs the entries to the PCAN-Basic API functions -LOG_FUNCTION_PARAMETERS = int(0x02) # Logs the parameters passed to the PCAN-Basic API functions -LOG_FUNCTION_LEAVE = int(0x04) # Logs the exits from the PCAN-Basic API functions -LOG_FUNCTION_WRITE = int(0x08) # Logs the CAN messages passed to the CAN_Write function -LOG_FUNCTION_READ = int(0x10) # Logs the CAN messages received within the CAN_Read function -LOG_FUNCTION_ALL = int(0xFFFF) # Logs all possible information within the PCAN-Basic API functions +LOG_FUNCTION_DEFAULT = int(0x00) # Logs system exceptions / errors +LOG_FUNCTION_ENTRY = int(0x01) # Logs the entries to the PCAN-Basic API functions +LOG_FUNCTION_PARAMETERS = int(0x02) # Logs the parameters passed to the PCAN-Basic API functions +LOG_FUNCTION_LEAVE = int(0x04) # Logs the exits from the PCAN-Basic API functions +LOG_FUNCTION_WRITE = int(0x08) # Logs the CAN messages passed to the CAN_Write function +LOG_FUNCTION_READ = int(0x10) # Logs the CAN messages received within the CAN_Read function +LOG_FUNCTION_ALL = int(0xFFFF)# Logs all possible information within the PCAN-Basic API functions TRACE_FILE_SINGLE = int(0x00) # A single file is written until it size reaches PAN_TRACE_SIZE TRACE_FILE_SEGMENTED = int(0x01) # Traced data is distributed in several files with size PAN_TRACE_SIZE @@ -221,7 +207,6 @@ SERVICE_STATUS_RUNNING = int(0x04) # The service is running # PCAN message types -# PCAN_MESSAGE_STANDARD = TPCANMessageType(0x00) # The PCAN message is a CAN Standard Frame (11-bit identifier) PCAN_MESSAGE_RTR = TPCANMessageType(0x01) # The PCAN message is a CAN Remote-Transfer-Request Frame PCAN_MESSAGE_EXTENDED = TPCANMessageType(0x02) # The PCAN message is a CAN Extended Frame (29-bit identifier) @@ -231,7 +216,6 @@ PCAN_MESSAGE_STATUS = TPCANMessageType(0x80) # The PCAN message represents a PCAN status message # Frame Type / Initialization Mode -# PCAN_MODE_STANDARD = PCAN_MESSAGE_STANDARD PCAN_MODE_EXTENDED = PCAN_MESSAGE_EXTENDED @@ -239,31 +223,30 @@ # You can define your own Baud rate with the BTROBTR1 register. # Take a look at www.peak-system.com for our free software "BAUDTOOL" # to calculate the BTROBTR1 register for every bit rate and sample point. -# -PCAN_BAUD_1M = TPCANBaudrate(0x0014) # 1 MBit/s -PCAN_BAUD_800K = TPCANBaudrate(0x0016) # 800 kBit/s -PCAN_BAUD_500K = TPCANBaudrate(0x001C) # 500 kBit/s -PCAN_BAUD_250K = TPCANBaudrate(0x011C) # 250 kBit/s -PCAN_BAUD_125K = TPCANBaudrate(0x031C) # 125 kBit/s -PCAN_BAUD_100K = TPCANBaudrate(0x432F) # 100 kBit/s + +PCAN_BAUD_1M = TPCANBaudrate(0x0014) # 1 MBit/s +PCAN_BAUD_800K = TPCANBaudrate(0x0016) # 800 kBit/s +PCAN_BAUD_500K = TPCANBaudrate(0x001C) # 500 kBit/s +PCAN_BAUD_250K = TPCANBaudrate(0x011C) # 250 kBit/s +PCAN_BAUD_125K = TPCANBaudrate(0x031C) # 125 kBit/s +PCAN_BAUD_100K = TPCANBaudrate(0x432F) # 100 kBit/s PCAN_BAUD_95K = TPCANBaudrate(0xC34E) # 95,238 kBit/s PCAN_BAUD_83K = TPCANBaudrate(0x852B) # 83,333 kBit/s -PCAN_BAUD_50K = TPCANBaudrate(0x472F) # 50 kBit/s +PCAN_BAUD_50K = TPCANBaudrate(0x472F) # 50 kBit/s PCAN_BAUD_47K = TPCANBaudrate(0x1414) # 47,619 kBit/s PCAN_BAUD_33K = TPCANBaudrate(0x8B2F) # 33,333 kBit/s -PCAN_BAUD_20K = TPCANBaudrate(0x532F) # 20 kBit/s -PCAN_BAUD_10K = TPCANBaudrate(0x672F) # 10 kBit/s -PCAN_BAUD_5K = TPCANBaudrate(0x7F7F) # 5 kBit/s +PCAN_BAUD_20K = TPCANBaudrate(0x532F) # 20 kBit/s +PCAN_BAUD_10K = TPCANBaudrate(0x672F) # 10 kBit/s +PCAN_BAUD_5K = TPCANBaudrate(0x7F7F) # 5 kBit/s # Represents the configuration for a CAN bit rate -# Note: +# Note: # * Each parameter and its value must be separated with a '='. # * Each pair of parameter/value must be separated using ','. # # Example: # f_clock=80000000,nom_brp=0,nom_tseg1=13,nom_tseg2=0,nom_sjw=0,data_brp=0,data_tseg1=13,data_tseg2=0,data_sjw=0 # -# Patched for python-can: use bytes to fix incompatibility with Python 3 PCAN_BR_CLOCK = TPCANBitrateFD(b"f_clock") PCAN_BR_CLOCK_MHZ = TPCANBitrateFD(b"f_clock_mhz") PCAN_BR_NOM_BRP = TPCANBitrateFD(b"nom_brp") @@ -278,7 +261,6 @@ PCAN_BR_DATA_SAMPLE = TPCANBitrateFD(b"data_ssp_offset") # Supported No-Plug-And-Play Hardware types -# PCAN_TYPE_ISA = TPCANType(0x01) # PCAN-ISA 82C200 PCAN_TYPE_ISA_SJA = TPCANType(0x09) # PCAN-ISA SJA1000 PCAN_TYPE_ISA_PHYTEC = TPCANType(0x04) # PHYTEC ISA @@ -287,36 +269,29 @@ PCAN_TYPE_DNG_SJA = TPCANType(0x05) # PCAN-Dongle SJA1000 PCAN_TYPE_DNG_SJA_EPP = TPCANType(0x06) # PCAN-Dongle EPP SJA1000 -# Represents a PCAN message -# class TPCANMsg (Structure): """ Represents a PCAN message """ - _fields_ = [ ("ID", c_uint), # 11/29-bit message identifier + _fields_ = [ ("ID", c_uint), # 11/29-bit message identifier ("MSGTYPE", TPCANMessageType), # Type of the message ("LEN", c_ubyte), # Data Length Code of the message (0..8) ("DATA", c_ubyte * 8) ] # Data of the message (DATA[0]..DATA[7]) -# Represents a timestamp of a received PCAN message -# Total Microseconds = micros + 1000 * millis + 0x100000000 * 1000 * millis_overflow -# class TPCANTimestamp (Structure): """ Represents a timestamp of a received PCAN message Total Microseconds = micros + 1000 * millis + 0x100000000 * 1000 * millis_overflow """ - _fields_ = [ ("millis", c_uint), # Base-value: milliseconds: 0.. 2^32-1 + _fields_ = [ ("millis", c_uint), # Base-value: milliseconds: 0.. 2^32-1 ("millis_overflow", c_ushort), # Roll-arounds of millis ("micros", c_ushort) ] # Microseconds: 0..999 -# Represents a PCAN message from a FD capable hardware -# class TPCANMsgFD (Structure): """ - Represents a PCAN message + Represents a PCAN message from a FD capable hardware """ - _fields_ = [ ("ID", c_uint), # 11/29-bit message identifier + _fields_ = [ ("ID", c_uint), # 11/29-bit message identifier ("MSGTYPE", TPCANMessageType), # Type of the message ("DLC", c_ubyte), # Data Length Code of the message (0..15) ("DATA", c_ubyte * 64) ] # Data of the message (DATA[0]..DATA[63]) @@ -325,15 +300,12 @@ class TPCANMsgFD (Structure): # PCAN-Basic API function declarations #/////////////////////////////////////////////////////////// -# PCAN-Basic API class implementation -# class PCANBasic: + """PCAN-Basic API class implementation """ - PCAN-Basic API class implementation - """ + def __init__(self): # Loads the PCANBasic.dll - # if platform.system() == 'Windows': self.__m_dllBasic = windll.LoadLibrary("PCANBasic") else: @@ -341,8 +313,6 @@ def __init__(self): if self.__m_dllBasic == None: logger.error("Exception: The PCAN-Basic DLL couldn't be loaded!") - # Initializes a PCAN Channel - # def Initialize( self, Channel, @@ -371,8 +341,6 @@ def Initialize( logger.error("Exception on PCANBasic.Initialize") raise - # Initializes a FD capable PCAN Channel - # def InitializeFD( self, Channel, @@ -406,8 +374,6 @@ def InitializeFD( logger.error("Exception on PCANBasic.InitializeFD") raise - # Uninitializes one or all PCAN Channels initialized by CAN_Initialize - # def Uninitialize( self, Channel): @@ -431,8 +397,6 @@ def Uninitialize( logger.error("Exception on PCANBasic.Uninitialize") raise - # Resets the receive and transmit queues of the PCAN Channel - # def Reset( self, Channel): @@ -456,8 +420,6 @@ def Reset( logger.error("Exception on PCANBasic.Reset") raise - # Gets the current status of a PCAN Channel - # def GetStatus( self, Channel): @@ -478,8 +440,6 @@ def GetStatus( logger.error("Exception on PCANBasic.GetStatus") raise - # Reads a CAN message from the receive queue of a PCAN Channel - # def Read( self, Channel): @@ -510,8 +470,6 @@ def Read( logger.error("Exception on PCANBasic.Read") raise - # Reads a CAN message from the receive queue of a FD capable PCAN Channel - # def ReadFD( self, Channel): @@ -542,8 +500,6 @@ def ReadFD( logger.error("Exception on PCANBasic.ReadFD") raise - # Transmits a CAN message - # def Write( self, Channel, @@ -566,8 +522,6 @@ def Write( logger.error("Exception on PCANBasic.Write") raise - # Transmits a CAN message over a FD capable PCAN Channel - # def WriteFD( self, Channel, @@ -590,8 +544,6 @@ def WriteFD( logger.error("Exception on PCANBasic.WriteFD") raise - # Configures the reception filter - # def FilterMessages( self, Channel, @@ -623,8 +575,6 @@ def FilterMessages( logger.error("Exception on PCANBasic.FilterMessages") raise - # Retrieves a PCAN Channel value - # def GetValue( self, Channel, @@ -661,9 +611,6 @@ def GetValue( logger.error("Exception on PCANBasic.GetValue") raise - # Returns a descriptive text of a given TPCANStatus - # error code, in any desired language - # def SetValue( self, Channel, diff --git a/can/interfaces/pcan/__init__.py b/can/interfaces/pcan/__init__.py index 618c182ce..8dbcfd0f9 100644 --- a/can/interfaces/pcan/__init__.py +++ b/can/interfaces/pcan/__init__.py @@ -1 +1,7 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from can.interfaces.pcan.pcan import PcanBus diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index 8647222d9..c46d06895 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -1,16 +1,19 @@ -""" -Enable basic can over a PCAN USB device. +#!/usr/bin/env python +# coding: utf-8 """ +Enable basic CAN over a PCAN USB device. +""" + import logging import sys +import time -from can.interfaces.pcan.PCANBasic import * +import can +from can import CanError from can.bus import BusABC from can.message import Message -from can import CanError -import can -import time +from can.interfaces.pcan.PCANBasic import * boottimeEpoch = 0 try: @@ -35,10 +38,10 @@ # Use polling instead HAS_EVENTS = False -if sys.version_info >= (3, 3): +try: # new in 3.3 timeout_clock = time.perf_counter -else: +except AttributeError: # deprecated in 3.3 timeout_clock = time.clock diff --git a/can/interfaces/serial/__init__.py b/can/interfaces/serial/__init__.py index 7dded7aff..6746fda0b 100644 --- a/can/interfaces/serial/__init__.py +++ b/can/interfaces/serial/__init__.py @@ -1 +1,7 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from can.interfaces.serial.serial_can import SerialBus as Bus diff --git a/can/interfaces/serial/serial_can.py b/can/interfaces/serial/serial_can.py index 7e5a81d0b..b7c2f7c0e 100644 --- a/can/interfaces/serial/serial_can.py +++ b/can/interfaces/serial/serial_can.py @@ -1,14 +1,16 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ A text based interface. For example use over serial ports like "/dev/ttyS1" or "/dev/ttyUSB0" on Linux machines or "COM1" on Windows. The interface is a simple implementation that has been used for recording CAN traces. - """ import logging import struct + from can.bus import BusABC from can.message import Message @@ -141,4 +143,3 @@ def recv(self, timeout=None): arbitration_id=arb_id, dlc=dlc, data=data) - diff --git a/can/interfaces/socketcan/__init__.py b/can/interfaces/socketcan/__init__.py index 2de902ea9..2e0a9ad0f 100644 --- a/can/interfaces/socketcan/__init__.py +++ b/can/interfaces/socketcan/__init__.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + +from can.interfaces.socketcan import socketcan_constants as constants from can.interfaces.socketcan.socketcan_ctypes import SocketcanCtypes_Bus from can.interfaces.socketcan.socketcan_native import SocketcanNative_Bus -from can.interfaces.socketcan import socketcan_constants as constants diff --git a/can/interfaces/socketcan/socketcan_common.py b/can/interfaces/socketcan/socketcan_common.py index 7a86822bc..05ce48b6c 100644 --- a/can/interfaces/socketcan/socketcan_common.py +++ b/can/interfaces/socketcan/socketcan_common.py @@ -1,7 +1,10 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ Defines common socketcan functions. """ + import struct from can.interfaces.socketcan.socketcan_constants import CAN_EFF_FLAG @@ -27,4 +30,5 @@ def pack_filters(can_filters=None): can_id |= CAN_EFF_FLAG filter_data.append(can_id) filter_data.append(can_mask) + return struct.pack(can_filter_fmt, *filter_data) diff --git a/can/interfaces/socketcan/socketcan_constants.py b/can/interfaces/socketcan/socketcan_constants.py index b3a7447fa..fb9eb3cad 100644 --- a/can/interfaces/socketcan/socketcan_constants.py +++ b/can/interfaces/socketcan/socketcan_constants.py @@ -1,22 +1,23 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ -Defines CAN constants. +Defines shared CAN constants. """ -canMSG_EXT = 0x0004 +canMSG_EXT = 0x0004 -CAN_ERR_FLAG = 0x20000000 -CAN_RTR_FLAG = 0x40000000 -CAN_EFF_FLAG = 0x80000000 +CAN_ERR_FLAG = 0x20000000 +CAN_RTR_FLAG = 0x40000000 +CAN_EFF_FLAG = 0x80000000 # BCM opcodes -CAN_BCM_TX_SETUP = 1 -CAN_BCM_TX_DELETE = 2 - -CAN_BCM_TX_EXPIRED = 9 +CAN_BCM_TX_SETUP = 1 +CAN_BCM_TX_DELETE = 2 -CAN_BCM_RX_TIMEOUT = 11 +CAN_BCM_TX_EXPIRED = 9 +CAN_BCM_RX_TIMEOUT = 11 # BCM flags SETTIMER = 0x0001 @@ -32,11 +33,11 @@ RX_RTR_FRAME = 0x0400 CAN_FD_FRAME = 0x0800 -CAN_RAW = 1 -CAN_BCM = 2 +CAN_RAW = 1 +CAN_BCM = 2 -SOL_CAN_BASE = 100 -SOL_CAN_RAW = SOL_CAN_BASE + CAN_RAW +SOL_CAN_BASE = 100 +SOL_CAN_RAW = SOL_CAN_BASE + CAN_RAW CAN_RAW_FILTER = 1 CAN_RAW_ERR_FILTER = 2 @@ -44,8 +45,8 @@ CAN_RAW_RECV_OWN_MSGS = 4 CAN_RAW_FD_FRAMES = 5 -MSK_ARBID = 0x1FFFFFFF -MSK_FLAGS = 0xE0000000 +MSK_ARBID = 0x1FFFFFFF +MSK_FLAGS = 0xE0000000 PF_CAN = 29 SOCK_RAW = 3 diff --git a/can/interfaces/socketcan/socketcan_ctypes.py b/can/interfaces/socketcan/socketcan_ctypes.py index 8d9b2e60d..d9e7f225b 100644 --- a/can/interfaces/socketcan/socketcan_ctypes.py +++ b/can/interfaces/socketcan/socketcan_ctypes.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from __future__ import print_function import ctypes @@ -527,4 +533,3 @@ def __init__(self, channel, message, count, initial_period, subsequent_period): bytes_sent = libc.send(self.bcm_socket, ctypes.byref(frame), ctypes.sizeof(frame)) if bytes_sent == -1: log.debug("Error sending frame :-/") - diff --git a/can/interfaces/socketcan/socketcan_native.py b/can/interfaces/socketcan/socketcan_native.py index 10933d79e..82e49511a 100644 --- a/can/interfaces/socketcan/socketcan_native.py +++ b/can/interfaces/socketcan/socketcan_native.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 """ This implementation is for versions of Python that have native diff --git a/can/interfaces/usb2can/__init__.py b/can/interfaces/usb2can/__init__.py index d3834495a..6cf4660f0 100644 --- a/can/interfaces/usb2can/__init__.py +++ b/can/interfaces/usb2can/__init__.py @@ -1,2 +1,8 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from can.interfaces.usb2can.usb2canInterface import Usb2canBus from can.interfaces.usb2can.usb2canabstractionlayer import Usb2CanAbstractionLayer diff --git a/can/interfaces/usb2can/serial_selector.py b/can/interfaces/usb2can/serial_selector.py index 46808852a..3e9490a01 100644 --- a/can/interfaces/usb2can/serial_selector.py +++ b/can/interfaces/usb2can/serial_selector.py @@ -1,4 +1,11 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + import logging + try: import win32com.client except ImportError: @@ -16,13 +23,13 @@ def WMIDateStringToDate(dtmDate): strDateTime = strDateTime + dtmDate[7] + '/' else: strDateTime = strDateTime + dtmDate[6] + dtmDate[7] + '/' - strDateTime = strDateTime + dtmDate[0] + dtmDate[1] + dtmDate[2] + dtmDate[3] + " " + dtmDate[8] + dtmDate[ - 9] + ":" + dtmDate[10] + dtmDate[11] + ':' + dtmDate[12] + dtmDate[13] + strDateTime = strDateTime + dtmDate[0] + dtmDate[1] + dtmDate[2] + dtmDate[3] + ' ' + dtmDate[8] + dtmDate[ + 9] + ':' + dtmDate[10] + dtmDate[11] + ':' + dtmDate[12] + dtmDate[13] return strDateTime def serial(): - strComputer = "." + strComputer = '.' objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator") objSWbemServices = objWMIService.ConnectServer(strComputer, "root\cimv2") colItems = objSWbemServices.ExecQuery("SELECT * FROM Win32_USBControllerDevice") @@ -30,7 +37,7 @@ def serial(): for objItem in colItems: string = objItem.Dependent # find based on beginning of serial - if "ED" in string: + if 'ED' in string: # print "Dependent:" + ` objItem.Dependent` string = string[len(string) - 9:len(string) - 1] diff --git a/can/interfaces/usb2can/usb2canInterface.py b/can/interfaces/usb2can/usb2canInterface.py index 57c2b330c..03f09ee8c 100644 --- a/can/interfaces/usb2can/usb2canInterface.py +++ b/can/interfaces/usb2can/usb2canInterface.py @@ -1,4 +1,9 @@ -# this interface is for windows only, otherwise use socketCAN +#!/usr/bin/env python +# coding: utf-8 + +""" +This interface is for windows only, otherwise use socketCAN. +""" import logging @@ -26,7 +31,6 @@ def format_connection_string(deviceID, baudrate='500'): return "%s; %s" % (deviceID, baudrate) -# TODO: Issue 36 with data being zeros or anything other than 8 must be fixed def message_convert_tx(msg): messagetx = CanalMsg() diff --git a/can/interfaces/usb2can/usb2canabstractionlayer.py b/can/interfaces/usb2can/usb2canabstractionlayer.py index c88b1fe65..608c1dca8 100644 --- a/can/interfaces/usb2can/usb2canabstractionlayer.py +++ b/can/interfaces/usb2can/usb2canabstractionlayer.py @@ -1,4 +1,11 @@ -# This wrapper is for windows or direct access via CANAL API. Socket CAN is recommended under Unix/Linux systems +#!/usr/bin/env python +# coding: utf-8 + +""" +This wrapper is for windows or direct access via CANAL API. +Socket CAN is recommended under Unix/Linux systems. +""" + import can from ctypes import * from struct import * diff --git a/can/interfaces/vector/__init__.py b/can/interfaces/vector/__init__.py index 36c368b57..9342e6d60 100644 --- a/can/interfaces/vector/__init__.py +++ b/can/interfaces/vector/__init__.py @@ -1,2 +1,8 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from .canlib import VectorBus from .exceptions import VectorError diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 19e2c78da..a5093b1a8 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -1,8 +1,12 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ -Ctypes wrapper module for Vector CAN Interface on win32/win64 systems +Ctypes wrapper module for Vector CAN Interface on win32/win64 systems. + Authors: Julien Grave , Christian Sandberg """ + # Import Standard Python Modules # ============================== import ctypes @@ -179,4 +183,3 @@ def reset(self): vxlapi.xlDeactivateChannel(self.port_handle, self.mask) vxlapi.xlActivateChannel(self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0) - diff --git a/can/interfaces/vector/exceptions.py b/can/interfaces/vector/exceptions.py index 64fef0824..1cbaddccd 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from can import CanError diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index 5b49faca8..cf08c4b2a 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -1,8 +1,12 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 + """ -Ctypes wrapper module for Vector CAN Interface on win32/win64 systems +Ctypes wrapper module for Vector CAN Interface on win32/win64 systems. + Authors: Julien Grave , Christian Sandberg """ + # Import Standard Python Modules # ============================== import ctypes From 78e4834ea2362583b8dffa57ff6f16d554b1b501 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 18:37:38 +0100 Subject: [PATCH 13/57] general cleanup of the tests --- test/back2back_test.py | 10 ++++++++++ test/data/__init__.py | 2 ++ test/data/example_data.py | 3 ++- test/listener_test.py | 6 ++++++ test/logformats_test.py | 5 +++++ test/network_test.py | 3 +++ test/serial_test.py | 26 ++++++-------------------- test/simplecyclic_test.py | 10 ++++++++++ test/test_kvaser.py | 9 +++++++-- test/zero_dlc_test.py | 12 ++++++++++-- 10 files changed, 61 insertions(+), 25 deletions(-) diff --git a/test/back2back_test.py b/test/back2back_test.py index 202a5365e..85c4c2e4a 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -1,3 +1,13 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module tests two virtual busses attached to each other. + +Some tests are skipped when run on Travis CI because they are not +reproducible, see #243 (https://github.com/hardbyte/python-can/issues/243). +""" + import os import unittest import time diff --git a/test/data/__init__.py b/test/data/__init__.py index e69de29bb..394a0a067 100644 --- a/test/data/__init__.py +++ b/test/data/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# coding: utf-8 diff --git a/test/data/example_data.py b/test/data/example_data.py index 6d54d683f..a66da1f36 100644 --- a/test/data/example_data.py +++ b/test/data/example_data.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 """ This module contains some example data, like messages of different diff --git a/test/listener_test.py b/test/listener_test.py index be795eaf4..4f6d9baea 100755 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -1,3 +1,9 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from time import sleep import unittest import random diff --git a/test/logformats_test.py b/test/logformats_test.py index 82c472243..019ea7147 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + """ This test module test the separate reader/writer combinations of the can.io.* modules by writing some messages to a temporary file and reading it again. @@ -8,6 +11,8 @@ comments. """ +from __future__ import print_function + import unittest import tempfile from time import sleep diff --git a/test/network_test.py b/test/network_test.py index 310aef54b..ae6c9a81a 100644 --- a/test/network_test.py +++ b/test/network_test.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + from __future__ import print_function import unittest diff --git a/test/serial_test.py b/test/serial_test.py index 63f8b8400..1c189e4fb 100644 --- a/test/serial_test.py +++ b/test/serial_test.py @@ -1,31 +1,17 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 """ -Name: serial_test -Purpose: Testing the serial interface +This module is testing the serial interface. -Copyright: 2017 Boris Wenzlaff - -This file is part of python-can . - -python-can is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation, either version 3 of the License, or -any later version. - -python-can is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License -along with python-can. If not, see . +Copyright: 2017 Boris Wenzlaff """ import unittest +from mock import patch + import can from can.interfaces.serial.serial_can import SerialBus -from mock import patch class SerialDummy: diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index 980fe1eee..362e89c2c 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -1,3 +1,13 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module tests cyclic send tasks. + +Some tests are skipped when run on Travis CI because they are not +reproducible, see #243 (https://github.com/hardbyte/python-can/issues/243). +""" + import os from time import sleep import unittest diff --git a/test/test_kvaser.py b/test/test_kvaser.py index 459b9978b..eb0a73ac3 100644 --- a/test/test_kvaser.py +++ b/test/test_kvaser.py @@ -1,9 +1,14 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + import ctypes import unittest import time import logging -logging.basicConfig(level=logging.DEBUG) + import can from can.interfaces.kvaser import canlib from can.interfaces.kvaser import constants diff --git a/test/zero_dlc_test.py b/test/zero_dlc_test.py index 74c8d7a06..83a073dc4 100644 --- a/test/zero_dlc_test.py +++ b/test/zero_dlc_test.py @@ -1,6 +1,13 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + from time import sleep import unittest import logging + import can logging.getLogger(__file__).setLevel(logging.DEBUG) @@ -11,7 +18,8 @@ class ZeroDLCTest(unittest.TestCase): def test_recv_non_zero_dlc(self): bus_send = can.interface.Bus(bustype='virtual') bus_recv = can.interface.Bus(bustype='virtual') - msg_send = can.Message(extended_id=False, arbitration_id=0x100, data=[0,1,2,3,4,5,6,7]) + data = [0, 1, 2, 3, 4, 5, 6, 7] + msg_send = can.Message(extended_id=False, arbitration_id=0x100, data=data) bus_send.send(msg_send) msg_recv = bus_recv.recv() @@ -27,7 +35,6 @@ def test_recv_none(self): # Receiving nothing should evaluate msg_recv to False self.assertFalse(msg_recv) - def test_recv_zero_dlc(self): bus_send = can.interface.Bus(bustype='virtual') bus_recv = can.interface.Bus(bustype='virtual') @@ -39,5 +46,6 @@ def test_recv_zero_dlc(self): # Receiving a frame without data (dlc == 0) should evaluate msg_recv to True self.assertTrue(msg_recv) + if __name__ == '__main__': unittest.main() From fab6f23a8ea5dc325e560bc6e0ba0f98a90aacef Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 18:37:53 +0100 Subject: [PATCH 14/57] general cleanup of the examples --- examples/cyclic.py | 7 +++++-- examples/send_one.py | 15 +++++++++++++-- examples/serial_com.py | 9 +++++++-- examples/simpleLogConvert.py | 10 ++++++++-- examples/vcan_filtered.py | 32 +++++++++++++++++++++----------- examples/virtual_can_demo.py | 4 ++++ 6 files changed, 58 insertions(+), 19 deletions(-) diff --git a/examples/cyclic.py b/examples/cyclic.py index 662e299a5..281b7c43e 100755 --- a/examples/cyclic.py +++ b/examples/cyclic.py @@ -1,4 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +# coding: utf-8 + """ This example exercises the periodic sending capabilities. @@ -8,6 +10,8 @@ """ +from __future__ import print_function + import logging import time @@ -125,5 +129,4 @@ def test_periodic_send_with_modifying_data(bus): bus.shutdown() - time.sleep(2) diff --git a/examples/send_one.py b/examples/send_one.py index 46ae20980..c147cd858 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -1,20 +1,31 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This example shows how sending a single message works. +""" + from __future__ import print_function -import can +import can def send_one(): bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) + + # Other buses work similar: + #bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) #bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) #bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) msg = can.Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], extended_id=True) + try: bus.send(msg) print("Message sent on {}".format(bus.channel_info)) except can.CanError: print("Message NOT sent") -if __name__ == "__main__": +if __name__ == '__main__': send_one() diff --git a/examples/serial_com.py b/examples/serial_com.py index dbceb8436..efa0bcdb5 100644 --- a/examples/serial_com.py +++ b/examples/serial_com.py @@ -1,4 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python +# coding: utf-8 + """ This example sends every second a messages over the serial interface and also receives incoming messages. @@ -17,10 +19,13 @@ com0com: http://com0com.sourceforge.net/ """ +from __future__ import print_function + import time -import can import threading +import can + def send_cyclic(bus, msg, stop_event): print("Start to send a message every 1s") diff --git a/examples/simpleLogConvert.py b/examples/simpleLogConvert.py index 9736ac5b8..aeb2830e7 100755 --- a/examples/simpleLogConvert.py +++ b/examples/simpleLogConvert.py @@ -1,8 +1,14 @@ #!/usr/bin/env python -# use it to convert .can-log files -# usage: simpleLogConvert.py sourceLog.asc targetLog.log +# coding: utf-8 + +""" +Use this to convert .can log files. + +Usage: simpleLogConvert.py sourceLog.asc targetLog.log +""" import sys + import can.io.logger import can.io.player diff --git a/examples/vcan_filtered.py b/examples/vcan_filtered.py index 99fed9fb8..86ee7f5ed 100644 --- a/examples/vcan_filtered.py +++ b/examples/vcan_filtered.py @@ -1,14 +1,24 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This shows how message filtering works. +""" + import time + import can -bus = can.interface.Bus(bustype='socketcan', - channel='vcan0', - receive_own_messages=True) - -can_filters = [{"can_id": 1, "can_mask": 0xf, "extended": True}] -bus.set_filters(can_filters) -notifier = can.Notifier(bus, [can.Printer()]) -bus.send(can.Message(arbitration_id=1, extended_id=True)) -bus.send(can.Message(arbitration_id=2, extended_id=True)) -bus.send(can.Message(arbitration_id=1, extended_id=False)) -time.sleep(10) +if __name__ == '__main__': + bus = can.interface.Bus(bustype='socketcan', + channel='vcan0', + receive_own_messages=True) + + can_filters = [{"can_id": 1, "can_mask": 0xf, "extended": True}] + bus.set_filters(can_filters) + notifier = can.Notifier(bus, [can.Printer()]) + bus.send(can.Message(arbitration_id=1, extended_id=True)) + bus.send(can.Message(arbitration_id=2, extended_id=True)) + bus.send(can.Message(arbitration_id=1, extended_id=False)) + + time.sleep(10) diff --git a/examples/virtual_can_demo.py b/examples/virtual_can_demo.py index 8f7c499ee..8845fc8d2 100644 --- a/examples/virtual_can_demo.py +++ b/examples/virtual_can_demo.py @@ -1,3 +1,6 @@ +#!/usr/bin/env python +# coding: utf-8 + """ This demo creates multiple processes of Producers to spam a socketcan bus. """ @@ -16,6 +19,7 @@ def producer(id): for i in range(16): msg = can.Message(arbitration_id=0x0cf02200+id, data=[id, i, 0, 1, 3, 1, 4, 1]) bus.send(msg) + # TODO Issue #3: Need to keep running to ensure the writing threads stay alive. ? time.sleep(2) From 434aa045b1135fffa1565ab1b4af311affa1cb80 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Tue, 20 Feb 2018 18:39:17 +0100 Subject: [PATCH 15/57] general cleanup of misc files & closing opened file in setup.py --- MANIFEST.in | 2 +- setup.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 2ebdd3cda..05cd56c0d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ include *.txt -recursive-include doc *.rst \ No newline at end of file +recursive-include doc *.rst diff --git a/setup.py b/setup.py index 98bf4a871..339daa2e3 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -*- +#!/usr/bin/env python +# coding: utf-8 """ python-can requires the setuptools package to be installed. @@ -12,6 +13,8 @@ version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', fd.read(), re.MULTILINE).group(1) +with open('README.rst', 'r') as f: + long_description = f.read() logging.basicConfig(level=logging.WARNING) @@ -23,7 +26,7 @@ author="Brian Thorne", author_email="brian@thorne.link", description="Controller Area Network interface module for Python", - long_description=open('README.rst').read(), + long_description=long_description, license="LGPL v3", package_data={ "": ["CONTRIBUTORS.txt", "LICENSE.txt"], From 1fbc398933b628cce2c338656f826210c2f36306 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 21 Feb 2018 16:07:24 +0100 Subject: [PATCH 16/57] removed unused file in kvaser interface --- can/interfaces/kvaser/argument_parser.py | 34 ------------------------ 1 file changed, 34 deletions(-) delete mode 100644 can/interfaces/kvaser/argument_parser.py diff --git a/can/interfaces/kvaser/argument_parser.py b/can/interfaces/kvaser/argument_parser.py deleted file mode 100644 index 63b11cb48..000000000 --- a/can/interfaces/kvaser/argument_parser.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -""" -TODO: Where is this used? Is this used? -""" - -def add_to_parser(parser): - parser.add_argument("-c", "--channel", type=str, dest="channel", - help=""" - If the CAN interface supports multiple channels, select which one - you are after here. For example on linux this might be 1 - """, default='0') - - parser.add_argument("-b", "--bitrate", type=int, dest="bitrate", - help="CAN bus bitrate", default=1000000) - - parser.add_argument("--tseg1", type=int, dest="tseg1", - help="CAN bus tseg1", default=4) - - parser.add_argument("--tseg2", type=int, dest="tseg2", - help="CAN bus tseg2", default=3) - - parser.add_argument("--sjw", type=int, dest="sjw", - help="Synchronisation Jump Width decides the maximum number of time quanta that the controller can resynchronise every bit.", - default=1) - - parser.add_argument("-n", "--num_samples", type=int, dest="no_samp", - help="""Some CAN controllers can also sample each bit three times. - In this case, the bit will be sampled three quanta in a row, - with the last sample being taken in the edge between TSEG1 and TSEG2. - - Three samples should only be used for relatively slow baudrates.""", - default=1) From ea8e107f4ab241606717a6f0ebe172874b996fd7 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 21 Feb 2018 16:21:34 +0100 Subject: [PATCH 17/57] better send_one example --- examples/send_one.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/examples/send_one.py b/examples/send_one.py index c147cd858..ebf0d1790 100755 --- a/examples/send_one.py +++ b/examples/send_one.py @@ -10,12 +10,17 @@ import can def send_one(): - bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) - # Other buses work similar: - #bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) - #bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) - #bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + # this uses the default configuration (for example from the config file) + # see http://python-can.readthedocs.io/en/latest/configuration.html + bus = can.interface.Bus() + + # Using specific buses works similar: + # bus = can.interface.Bus(bustype='socketcan', channel='vcan0', bitrate=250000) + # bus = can.interface.Bus(bustype='pcan', channel='PCAN_USBBUS1', bitrate=250000) + # bus = can.interface.Bus(bustype='ixxat', channel=0, bitrate=250000) + # bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=250000) + # ... msg = can.Message(arbitration_id=0xc0ffee, data=[0, 25, 0, 1, 3, 1, 4, 1], From cd003889aeb8429ce3d33be1a169be0ed84add76 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 11:51:59 +1100 Subject: [PATCH 18/57] Handle error frame messages in CanutilsLogWriter and CanutilsLogReader. Closes #217 (cherry picked from commit 61f27e1) --- test/logformats_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index 019ea7147..f27ecdf1c 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -138,7 +138,6 @@ class TestCanutilsLog(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.CanutilsLogWriter, can.CanutilsLogReader, - check_error_frames=False, # TODO this should get fixed, see Issue #217 check_comments=False) @@ -147,7 +146,6 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_error_frames=True, check_comments=True) @@ -197,7 +195,6 @@ class TestBlfFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.BLFWriter, can.BLFReader, - sleep_time=None, check_comments=False) def test_reader(self): From 012768358daa29c29f84ca52c63f121bca10ac1e Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 18:36:41 +0100 Subject: [PATCH 19/57] small changes in can/io/log.py --- can/io/log.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/can/io/log.py b/can/io/log.py index 427b96a3b..3d1fccf0b 100644 --- a/can/io/log.py +++ b/can/io/log.py @@ -34,16 +34,20 @@ def __init__(self, filename): def __iter__(self): for line in self.fp: temp = line.strip() - if len(temp) > 0: + + if temp: + (timestamp, bus, frame) = temp.split() timestamp = float(timestamp[1:-1]) (canId, data) = frame.split('#') + if len(canId) > 3: isExtended = True else: isExtended = False canId = int(canId, 16) - if len(data) > 0 and data[0].lower() == 'r': + + if data and data[0].lower() == 'r': isRemoteFrame = True if len(data) > 1: dlc = int(data[1:]) @@ -57,12 +61,12 @@ def __iter__(self): for i in range(0, 2 * dlc, 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) + extended_id=isExtended, is_remote_frame=isRemoteFrame, + dlc=dlc, data=dataBin) yield msg From 40d63c9656da860652402c49c41717b2e1b736f0 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 18:54:42 +0100 Subject: [PATCH 20/57] make ASCWriter's output consistent with the other loggers --- can/io/asc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index de3edf807..ea8d99ed6 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -128,9 +128,9 @@ def log_event(self, message, timestamp=None): return if timestamp is None: - timestamp = time.time() + timestamp = 0 - if timestamp >= self.started: + elif timestamp >= self.started: timestamp -= self.started line = self.EVENT_STRING.format(time=timestamp, message=message) From 0a6f9e5b2d97b7d8ac37f9ec631bdcded9e1bac1 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 20:53:37 +0100 Subject: [PATCH 21/57] more info about building the docs with Sphinx --- doc/development.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/development.rst b/doc/development.rst index f7e09b671..03dbd0d0c 100644 --- a/doc/development.rst +++ b/doc/development.rst @@ -21,7 +21,8 @@ The following assumes that the commands are executed from the root of the reposi ``python setup.py install``. - The unit tests can be run with ``python setup.py test``. The tests can be run with ``python2``, ``python3``, ``pypy`` or ``pypy3`` to test with other python versions, if they are installed. -- The docs can be built with ``sphinx-build doc/ doc/_build``. +- The docs can be built with ``sphinx-build doc/ doc/_build``. Appending ``-n`` to the command + makes Sphinx complain about more subtle problems. Creating a Release From eb4f8663e241cc05810dba55c23845e963aac74c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 00:15:28 +0100 Subject: [PATCH 22/57] removed unnessesary call to abs() in SqliteReader --- can/io/sqlite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 454a09f2a..215341bee 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -54,7 +54,7 @@ def __iter__(self): def __len__(self): # this might not run in constant time result = self.cursor.execute("SELECT COUNT(*) FROM messages") - return abs(int(result.fetchone()[0])) + return int(result.fetchone()[0]) def read_all(self): """Fetches all messages in the database.""" From 4a17a5f954bdaf79afe65fa38947905931e08cc2 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 01:30:52 +0100 Subject: [PATCH 23/57] renamed example file --- examples/{simpleLogConvert.py => simple_log_converter.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename examples/{simpleLogConvert.py => simple_log_converter.py} (85%) diff --git a/examples/simpleLogConvert.py b/examples/simple_log_converter.py similarity index 85% rename from examples/simpleLogConvert.py rename to examples/simple_log_converter.py index aeb2830e7..782ac9b7c 100755 --- a/examples/simpleLogConvert.py +++ b/examples/simple_log_converter.py @@ -2,7 +2,7 @@ # coding: utf-8 """ -Use this to convert .can log files. +Use this to convert .can/.asc files to .log files. Usage: simpleLogConvert.py sourceLog.asc targetLog.log """ From b0b4a75e9654ea060ff26306e9c1417dc7ca559d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 01:45:22 +0100 Subject: [PATCH 24/57] added small TODO in sqlite.py --- can/io/sqlite.py | 1 + 1 file changed, 1 insertion(+) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 215341bee..875044eb4 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -18,6 +18,7 @@ log = logging.getLogger('can.io.sql') +# TODO comment on this if sys.version_info > (3,): buffer = memoryview From cc069010247c77eac5c1e4e0d9a3d54ad756a437 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Sat, 18 Nov 2017 19:55:52 +0100 Subject: [PATCH 25/57] Vector: Reduce CPU usage by using Win32 events --- can/interfaces/vector/canlib.py | 51 ++++++++++++++++++++++++++--- can/interfaces/vector/exceptions.py | 5 +-- can/interfaces/vector/vxlapi.py | 17 +++++++++- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index a5093b1a8..44b72be62 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -14,6 +14,19 @@ import sys import time +try: + # Try builtin Python 3 Windows API + from _winapi import WaitForSingleObject, INFINITE + HAS_EVENTS = True +except ImportError: + try: + # Try pywin32 package + from win32event import WaitForSingleObject, INFINITE + HAS_EVENTS = True + except ImportError: + # Use polling instead + HAS_EVENTS = False + # Import Modules # ============== from can import BusABC, Message @@ -35,6 +48,7 @@ class VectorBus(BusABC): """The CAN Bus implemented for the Vector interface.""" def __init__(self, channel, can_filters=None, poll_interval=0.01, + receive_own_messages=False, bitrate=None, rx_queue_size=256, app_name="CANalyzer", **config): """ :param list channel: @@ -92,19 +106,33 @@ def __init__(self, channel, can_filters=None, poll_interval=0.01, self.port_handle.value, permission_mask.value) if bitrate: if permission_mask.value != self.mask: - LOG.warning('Can not set bitrate since no init access') + LOG.info('Can not set bitrate since no init access') vxlapi.xlCanSetChannelBitrate(self.port_handle, permission_mask, bitrate) + + # Enable/disable TX receipts + tx_receipts = 1 if receive_own_messages else 0 + vxlapi.xlCanSetChannelMode(self.port_handle, self.mask, tx_receipts, 0) + + if HAS_EVENTS: + self.event_handle = vxlapi.XLhandle() + vxlapi.xlSetNotification(self.port_handle, self.event_handle, 1) + else: + LOG.info('Install pywin32 to avoid polling') + self.set_filters(can_filters) + try: vxlapi.xlActivateChannel(self.port_handle, self.mask, vxlapi.XL_BUS_TYPE_CAN, 0) except VectorError: self.shutdown() raise + # Calculate time offset for absolute timestamps offset = vxlapi.XLuint64() vxlapi.xlGetSyncTime(self.port_handle, offset) - self._time_offset = time.time() - offset.value / 1000000000.0 + self._time_offset = time.time() - offset.value * 1e-9 + super(VectorBus, self).__init__() def set_filters(self, can_filters=None): @@ -127,8 +155,9 @@ def set_filters(self, can_filters=None): def recv(self, timeout=None): end_time = time.time() + timeout if timeout is not None else None event = vxlapi.XLevent(0) + event_count = ctypes.c_uint() while True: - event_count = ctypes.c_uint(1) + event_count.value = 1 try: vxlapi.xlReceive(self.port_handle, event_count, event) except VectorError as exc: @@ -139,7 +168,7 @@ def recv(self, timeout=None): msg_id = event.tagData.msg.id dlc = event.tagData.msg.dlc flags = event.tagData.msg.flags - timestamp = event.timeStamp / 1000000000.0 + timestamp = event.timeStamp * 1e-9 msg = Message( timestamp=timestamp + self._time_offset, arbitration_id=msg_id & 0x1FFFFFFF, @@ -150,9 +179,21 @@ def recv(self, timeout=None): data=event.tagData.msg.data[:dlc], channel=event.chanIndex) return msg + if end_time is not None and time.time() > end_time: return None - time.sleep(self.poll_interval) + + if HAS_EVENTS: + # Wait for receive event to occur + if timeout is None: + time_left_ms = INFINITE + else: + time_left = end_time - time.time() + time_left_ms = max(0, int(time_left * 1000)) + WaitForSingleObject(self.event_handle.value, time_left_ms) + else: + # Wait a short time until we try again + time.sleep(self.poll_interval) def send(self, msg, timeout=None): message_count = ctypes.c_uint(1) diff --git a/can/interfaces/vector/exceptions.py b/can/interfaces/vector/exceptions.py index 1cbaddccd..ab50ff60d 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -9,6 +9,7 @@ class VectorError(CanError): - def __init__(self, error_code, error_string): + def __init__(self, error_code, error_string, function): self.error_code = error_code - super(VectorError, self).__init__(error_string) + text = "%s failed (%s)" % (function, error_string) + super(VectorError, self).__init__(text) diff --git a/can/interfaces/vector/vxlapi.py b/can/interfaces/vector/vxlapi.py index cf08c4b2a..6612351b3 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -34,12 +34,14 @@ XL_CAN_EXT_MSG_ID = 0x80000000 XL_CAN_MSG_FLAG_ERROR_FRAME = 0x01 XL_CAN_MSG_FLAG_REMOTE_FRAME = 0x10 +XL_CAN_MSG_FLAG_TX_COMPLETED = 0x40 XL_CAN_STD = 1 XL_CAN_EXT = 2 XLuint64 = ctypes.c_ulonglong XLaccess = XLuint64 +XLhandle = ctypes.c_void_p MAX_MSG_LEN = 8 @@ -75,7 +77,7 @@ class XLevent(ctypes.Structure): def check_status(result, function, arguments): if result > 0: - raise VectorError(result, xlGetErrorString(result).decode()) + raise VectorError(result, xlGetErrorString(result).decode(), function.__name__) return result @@ -123,6 +125,19 @@ def check_status(result, function, arguments): xlClosePort.restype = XLstatus xlClosePort.errcheck = check_status +xlSetNotification = _xlapi_dll.xlSetNotification +xlSetNotification.argtypes = [XLportHandle, ctypes.POINTER(XLhandle), + ctypes.c_int] +xlSetNotification.restype = XLstatus +xlSetNotification.errcheck = check_status + +xlCanSetChannelMode = _xlapi_dll.xlCanSetChannelMode +xlCanSetChannelMode.argtypes = [ + XLportHandle, XLaccess, ctypes.c_int, ctypes.c_int +] +xlCanSetChannelMode.restype = XLstatus +xlCanSetChannelMode.errcheck = check_status + xlActivateChannel = _xlapi_dll.xlActivateChannel xlActivateChannel.argtypes = [ XLportHandle, XLaccess, ctypes.c_uint, ctypes.c_uint From 190f94946d1a0435326c4f250725318e3e2fbf06 Mon Sep 17 00:00:00 2001 From: Christian Sandberg Date: Tue, 20 Feb 2018 12:03:52 +0100 Subject: [PATCH 26/57] Add note in documentation about pywin32 --- doc/interfaces/vector.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/interfaces/vector.rst b/doc/interfaces/vector.rst index c8aeb0158..a936e693e 100644 --- a/doc/interfaces/vector.rst +++ b/doc/interfaces/vector.rst @@ -19,6 +19,9 @@ application named "python-can":: channel = 0, 1 app_name = python-can +If you are using Python 2.7 it is recommended to install pywin32_, otherwise a +slow and CPU intensive polling will be used when waiting for new messages. + Bus --- @@ -29,3 +32,4 @@ Bus .. _Vector: https://vector.com/ +.. _pywin32: https://sourceforge.net/projects/pywin32/ From 782c2abb488a0f12f61dda871669a83df5ed0468 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 14:12:24 +0100 Subject: [PATCH 27/57] simpler usage of abc.* --- can/bus.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/can/bus.py b/can/bus.py index 05423c9c7..ede8a74d0 100644 --- a/can/bus.py +++ b/can/bus.py @@ -7,7 +7,7 @@ from __future__ import print_function, absolute_import -import abc +from abc import ABCMeta, abstractmethod import logging import threading @@ -30,7 +30,7 @@ class BusABC(object): #: a string describing the underlying bus channel channel_info = 'unknown' - @abc.abstractmethod + @abstractmethod def __init__(self, channel=None, can_filters=None, **config): """ :param channel: @@ -52,7 +52,7 @@ def __init__(self, channel=None, can_filters=None, **config): def __str__(self): return self.channel_info - @abc.abstractmethod + @abstractmethod def recv(self, timeout=None): """Block waiting for a message from the Bus. @@ -63,7 +63,7 @@ def recv(self, timeout=None): """ raise NotImplementedError("Trying to read from a write only bus?") - @abc.abstractmethod + @abstractmethod def send(self, msg, timeout=None): """Transmit a message to CAN bus. Override this method to enable the transmit path. @@ -146,4 +146,4 @@ def shutdown(self): """ self.flush_tx_buffer() - __metaclass__ = abc.ABCMeta + __metaclass__ = ABCMeta From 0bdd978b7ba3dc853845373709f94ebe5f74704d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 16:54:36 +0100 Subject: [PATCH 28/57] fix inport in util.py --- can/util.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/can/util.py b/can/util.py index 9eeb11e24..c78b9d674 100644 --- a/can/util.py +++ b/can/util.py @@ -5,21 +5,21 @@ Utilities and configuration file parsing. """ -from __future__ import absolute_import +from __future__ import absolute_import, print_function -import can -from can.interfaces import VALID_INTERFACES - -try: - from configparser import ConfigParser -except ImportError: - from ConfigParser import SafeConfigParser as ConfigParser import os import os.path import sys import platform import re import logging +try: + from configparser import ConfigParser +except ImportError: + from ConfigParser import SafeConfigParser as ConfigParser + +import can +from can.interfaces import VALID_INTERFACES log = logging.getLogger('can.util') From 72cc66cc1a4ef2d0ee5b4fd0d60eea1acac5c128 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 23:17:18 +0100 Subject: [PATCH 29/57] structural changes to setup.py & added 'Deprecated' library --- requirements.txt | 3 ++- setup.py | 34 +++++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/requirements.txt b/requirements.txt index f6c1a1f57..2765eba2c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -pyserial +pyserial >= 3.0 +Deprecated >= 1.1.0 diff --git a/setup.py b/setup.py index 339daa2e3..c358e85bb 100644 --- a/setup.py +++ b/setup.py @@ -19,24 +19,44 @@ logging.basicConfig(level=logging.WARNING) setup( + + # Description name="python-can", url="https://github.com/hardbyte/python-can", + description="Controller Area Network interface module for Python", + long_description=long_description, + + # Code version=version, packages=find_packages(), + + # Author author="Brian Thorne", author_email="brian@thorne.link", - description="Controller Area Network interface module for Python", - long_description=long_description, + + # License license="LGPL v3", + + # Package data package_data={ "": ["CONTRIBUTORS.txt", "LICENSE.txt"], "doc": ["*.*"] }, - # Tests can be run using `python setup.py test` - test_suite="nose.collector", - tests_require=['mock', 'nose', 'pyserial'], + + # Installation + install_requires=[ + 'Deprecated >= 1.1.0', + ], extras_require={ - 'serial': ['pyserial'], + 'serial': ['pyserial >= 3.0'], 'neovi': ['python-ics'], - } + }, + + # Testing + test_suite="nose.collector", + tests_require=[ + 'mock', + 'nose', + 'pyserial >= 3.0' + ], ) From a2127dac89cf3fe6b625e955cf85ea1b8c728555 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 23:57:20 +0100 Subject: [PATCH 30/57] @deprecated the name SqlReader --- can/io/sqlite.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 875044eb4..463927730 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -13,6 +13,8 @@ import logging import sqlite3 +from deprecated import deprecated + from can.listener import BufferedReader from can.message import Message @@ -23,7 +25,8 @@ buffer = memoryview -class SqliteReader: +@deprecated(version='2.1', reason="Use the name SqliteReader instead") +class SqlReader: """ Reads recorded CAN messages from a simple SQL database. @@ -66,10 +69,8 @@ def close(self): """Closes the connection to the database.""" self.conn.close() - -# Backward compatibility -# TODO remove in later releases? -SqlReader = SqliteReader +# SqliteReader is the newer name +SqliteReader = SqlReader class SqliteWriter(BufferedReader): From be39d1cfbc71774423900943fd94453739af79a8 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 00:05:40 +0100 Subject: [PATCH 31/57] removed the version attribute from the deprecated decorator --- can/io/sqlite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 463927730..ecb315de1 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -25,7 +25,7 @@ buffer = memoryview -@deprecated(version='2.1', reason="Use the name SqliteReader instead") +@deprecated(reason="Use the name SqliteReader instead. (Replaced in v2.1)") class SqlReader: """ Reads recorded CAN messages from a simple SQL database. From 071152c7bc4f29313462d35969289b7906d7c0eb Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 00:13:02 +0100 Subject: [PATCH 32/57] fix wrong method call --- can/io/sqlite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index ecb315de1..f0d13ec60 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -53,7 +53,7 @@ def _create_frame_from_db_tuple(frame_data): def __iter__(self): log.debug("Iterating through messages from sql db") for frame_data in self.cursor.execute(self._SELECT_ALL_COMMAND): - yield SqliteReader._create_frame_from_db_tuple(frame_data) + yield SqlReader._create_frame_from_db_tuple(frame_data) def __len__(self): # this might not run in constant time From 67a71fc7b0bf50b598db3defca131873f067972d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 00:33:17 +0100 Subject: [PATCH 33/57] try to enable useful tests with appveyor --- .appveyor.yml | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 000000000..aab83f930 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,45 @@ +environment: + + matrix: + + # For Python versions available on Appveyor, see + # http://www.appveyor.com/docs/installed-software#python + + - PYTHON: "C:\\Python27" + - PYTHON: "C:\\Python33" + - PYTHON: "C:\\Python34" + - PYTHON: "C:\\Python35" + - PYTHON: "C:\\Python36" + - PYTHON: "C:\\Python27-x64" + - PYTHON: "C:\\Python33-x64" + - PYTHON: "C:\\Python34-x64" + - PYTHON: "C:\\Python35-x64" + - PYTHON: "C:\\Python36-x64" + +install: + # We need our usual libraries + - "%PYTHON%\\python.exe -m pip install -r requirements.txt" + # We need wheel installed to build wheels + - "%PYTHON%\\python.exe -m pip install wheel" + +build: off + +test_script: + # Put your test command here. + # Note that you must use the environment variable %PYTHON% to refer to + # the interpreter you're using - Appveyor does not do anything special + # to put the Python version you want to use on PATH. + - "%PYTHON%\\python.exe setup.py test" + +#after_test: +# # This step builds your wheels. +# - "%PYTHON%\\python.exe setup.py bdist_wheel" + +#artifacts: +# # bdist_wheel puts your built wheel in the dist directory +# - path: dist\* + +#on_success: +# You can use this step to upload your artifacts to a public website. +# See Appveyor's documentation for more details. Or you can simply +# access your wheels from the Appveyor "artifacts" tab for your build. From 6f1d7395399c3d64c2071aa2318792b7068c31a2 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 00:41:32 +0100 Subject: [PATCH 34/57] try simplifying .appveyor.yml --- .appveyor.yml | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index aab83f930..caacf9134 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,26 +20,12 @@ install: # We need our usual libraries - "%PYTHON%\\python.exe -m pip install -r requirements.txt" # We need wheel installed to build wheels - - "%PYTHON%\\python.exe -m pip install wheel" + #- "%PYTHON%\\python.exe -m pip install wheel" build: off test_script: - # Put your test command here. # Note that you must use the environment variable %PYTHON% to refer to # the interpreter you're using - Appveyor does not do anything special # to put the Python version you want to use on PATH. - "%PYTHON%\\python.exe setup.py test" - -#after_test: -# # This step builds your wheels. -# - "%PYTHON%\\python.exe setup.py bdist_wheel" - -#artifacts: -# # bdist_wheel puts your built wheel in the dist directory -# - path: dist\* - -#on_success: -# You can use this step to upload your artifacts to a public website. -# See Appveyor's documentation for more details. Or you can simply -# access your wheels from the Appveyor "artifacts" tab for your build. From e9aec8d18cb1fcc9b5c037616a81facbc1b03f79 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 01:05:57 +0100 Subject: [PATCH 35/57] factored out common Travis skipping behaviour and replaced it with generic CI skipping behaviour --- .appveyor.yml | 4 ++-- test/__init__.py | 2 ++ test/back2back_test.py | 11 ++++------- test/config.py | 24 ++++++++++++++++++++++++ test/simplecyclic_test.py | 10 ++++------ 5 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 test/__init__.py create mode 100644 test/config.py diff --git a/.appveyor.yml b/.appveyor.yml index caacf9134..58b3eebe2 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -19,8 +19,8 @@ environment: install: # We need our usual libraries - "%PYTHON%\\python.exe -m pip install -r requirements.txt" - # We need wheel installed to build wheels - #- "%PYTHON%\\python.exe -m pip install wheel" + # We need to install the python-can library itself + - "%PYTHON%\\python.exe -m pip install ." build: off diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 000000000..394a0a067 --- /dev/null +++ b/test/__init__.py @@ -0,0 +1,2 @@ +#!/usr/bin/env python +# coding: utf-8 diff --git a/test/back2back_test.py b/test/back2back_test.py index 85c4c2e4a..2b2f70198 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -3,22 +3,19 @@ """ This module tests two virtual busses attached to each other. - -Some tests are skipped when run on Travis CI because they are not -reproducible, see #243 (https://github.com/hardbyte/python-can/issues/243). """ -import os +from __future__ import absolute_import + import unittest import time import can -IS_TRAVIS = os.environ.get('TRAVIS', 'default') == 'true' +from .config import * BITRATE = 500000 TIMEOUT = 0.1 -TEST_CAN_FD = True INTERFACE_1 = 'virtual' CHANNEL_1 = 'vcan0' @@ -79,7 +76,7 @@ def _send_and_receive(self, msg): def test_no_message(self): self.assertIsNone(self.bus1.recv(0.1)) - @unittest.skipIf(IS_TRAVIS, "skip on Travis CI") + @unittest.skipIf(IS_CI, "the timing sensitive behaviour cannot be reproduced reliably on a CI server") def test_timestamp(self): self.bus2.send(can.Message()) recv_msg1 = self.bus1.recv(TIMEOUT) diff --git a/test/config.py b/test/config.py new file mode 100644 index 000000000..a25aff627 --- /dev/null +++ b/test/config.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +This module contains some configuration for the tests. + +Some tests are skipped when run on a CI server because they are not +reproducible, see #243 (https://github.com/hardbyte/python-can/issues/243). +""" + +from os import environ as environment + +# see here for the environment variables that are set on the CI servers: +# - https://docs.travis-ci.com/user/environment-variables/ +# - https://www.appveyor.com/docs/environment-variables/ + +IS_TRAVIS = environment.get('TRAVIS', '').lower() == 'true' +IS_APPVEYOR = environment.get('APPVEYOR', '').lower() == 'true' + +IS_CI = IS_TRAVIS or IS_APPVEYOR or \ + environment.get('CI', '').lower() == 'true' or \ + environment.get('CONTINUOUS_INTEGRATION', '').lower() == 'true' + +TEST_CAN_FD = True diff --git a/test/simplecyclic_test.py b/test/simplecyclic_test.py index 362e89c2c..f4ab7cab2 100644 --- a/test/simplecyclic_test.py +++ b/test/simplecyclic_test.py @@ -3,22 +3,20 @@ """ This module tests cyclic send tasks. - -Some tests are skipped when run on Travis CI because they are not -reproducible, see #243 (https://github.com/hardbyte/python-can/issues/243). """ -import os +from __future__ import absolute_import + from time import sleep import unittest import can -IS_TRAVIS = os.environ.get('TRAVIS', 'default') == 'true' +from .config import * class SimpleCyclicSendTaskTest(unittest.TestCase): - @unittest.skipIf(IS_TRAVIS, "skip on Travis CI") + @unittest.skipIf(IS_CI, "the timing sensitive behaviour cannot be reproduced reliably on a CI server") def test_cycle_time(self): msg = can.Message(extended_id=False, arbitration_id=0x100, data=[0,1,2,3,4,5,6,7]) bus1 = can.interface.Bus(bustype='virtual') From 5e0acf5c36c6ac84b4710f5f008c9afa2b285e82 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 01:24:33 +0100 Subject: [PATCH 36/57] add appveyor badge to README.md --- README.rst | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 8002997eb..a61ae9e6e 100644 --- a/README.rst +++ b/README.rst @@ -1,19 +1,23 @@ python-can ========== -|release| |docs| |build| +|release| |docs| |build_travis| |build_appveyor| .. |release| image:: https://img.shields.io/pypi/v/python-can.svg :target: https://pypi.python.org/pypi/python-can/ - :alt: Latest Version + :alt: Latest Version on PyPi .. |docs| image:: https://readthedocs.org/projects/python-can/badge/?version=stable :target: https://python-can.readthedocs.io/en/stable/ - :alt: Documentation Status + :alt: Documentation build Status -.. |build| image:: https://travis-ci.org/hardbyte/python-can.svg?branch=develop +.. |build_travis| image:: https://travis-ci.org/hardbyte/python-can.svg?branch=develop :target: https://travis-ci.org/hardbyte/python-can/branches - :alt: CI Server for develop branch + :alt: Travis CI Server for develop branch + +.. |build_appveyor| image:: https://ci.appveyor.com/api/projects/status/github/hardbyte/python-can?branch=develop&svg=true + :target: https://ci.appveyor.com/project/hardbyte/python-can/history + :alt: AppVeyor CI Server for develop branch The **C**\ ontroller **A**\ rea **N**\ etwork is a bus standard designed From 347fb18474c88504c032ded327a8f469eb1a493b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 01:57:06 +0100 Subject: [PATCH 37/57] fix problem from another branch so we can test here --- can/io/sqlite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index f0d13ec60..12dce3d8b 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -53,7 +53,7 @@ def _create_frame_from_db_tuple(frame_data): def __iter__(self): log.debug("Iterating through messages from sql db") for frame_data in self.cursor.execute(self._SELECT_ALL_COMMAND): - yield SqlReader._create_frame_from_db_tuple(frame_data) + yield self._create_frame_from_db_tuple(frame_data) def __len__(self): # this might not run in constant time From 4cdea042bbdcf681555435d7328e9b39a40da240 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 02:14:59 +0100 Subject: [PATCH 38/57] fix absolute imports --- test/listener_test.py | 4 +++- test/logformats_test.py | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/listener_test.py b/test/listener_test.py index 4f6d9baea..e9495c066 100755 --- a/test/listener_test.py +++ b/test/listener_test.py @@ -4,6 +4,8 @@ """ """ +from __future__ import absolute_import + from time import sleep import unittest import random @@ -14,7 +16,7 @@ import can -from data.example_data import generate_message +from .data.example_data import generate_message channel = 'vcan0' can.rc['interface'] = 'virtual' diff --git a/test/logformats_test.py b/test/logformats_test.py index 019ea7147..728d997e3 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -12,6 +12,7 @@ """ from __future__ import print_function +from __future__ import absolute_import import unittest import tempfile @@ -28,9 +29,9 @@ import can -from data.example_data import TEST_MESSAGES_BASE, TEST_MESSAGES_REMOTE_FRAMES, \ - TEST_MESSAGES_ERROR_FRAMES, TEST_COMMENTS, \ - generate_message +from .data.example_data import TEST_MESSAGES_BASE, TEST_MESSAGES_REMOTE_FRAMES, \ + TEST_MESSAGES_ERROR_FRAMES, TEST_COMMENTS, \ + generate_message def _test_writer_and_reader(test_case, writer_constructor, reader_constructor, sleep_time=None, From 07be6b5aa0fd6bcf84e24d37b76c8866120920f8 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 23:51:36 +0100 Subject: [PATCH 39/57] warn about usage of the old CAN module. fix #267 --- can/CAN.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/can/CAN.py b/can/CAN.py index 000b7f8c2..e1d0f496f 100644 --- a/can/CAN.py +++ b/can/CAN.py @@ -5,7 +5,11 @@ This module was once the core of python-can, containing implementations of all the major classes in the library, now however all functionality has been refactored out. This API -is left intact for version 2.0 to aide with migration. +is left intact for version 2.0 to 2.3 to aide with migration. + +WARNING: +This module is deprecated an will get removed in version 2.4. +Please use `import can` instead. """ from __future__ import absolute_import @@ -18,4 +22,11 @@ import logging log = logging.getLogger('can') -log.info("Loading python-can via the old CAN api") + +# See #267 +# Version 2.0 - 2.1: Log a Debug message +# Version 2.2: Log a Warning +# Version 2.3: Log an Error +# Version 2.4: Remove the module +log.warning('Loading python-can via the old "CAN" API is deprecated since v2.0 an will get removed in v2.4. ' + 'Please use `import can` instead.') From e4569c4996517e01857a4e1cc1ab81aa819fc459 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 24 Feb 2018 01:19:14 +0100 Subject: [PATCH 40/57] completely remove SqlReader. closes #263 --- can/io/sqlite.py | 6 +----- requirements.txt | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 12dce3d8b..7abf4d181 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -25,8 +25,7 @@ buffer = memoryview -@deprecated(reason="Use the name SqliteReader instead. (Replaced in v2.1)") -class SqlReader: +class SqliteReader: """ Reads recorded CAN messages from a simple SQL database. @@ -69,9 +68,6 @@ def close(self): """Closes the connection to the database.""" self.conn.close() -# SqliteReader is the newer name -SqliteReader = SqlReader - class SqliteWriter(BufferedReader): """Logs received CAN data to a simple SQL database. diff --git a/requirements.txt b/requirements.txt index 2765eba2c..74371374e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ pyserial >= 3.0 -Deprecated >= 1.1.0 +#Deprecated >= 1.1.0 diff --git a/setup.py b/setup.py index c358e85bb..68ba1ad4d 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ # Installation install_requires=[ - 'Deprecated >= 1.1.0', + #'Deprecated >= 1.1.0', ], extras_require={ 'serial': ['pyserial >= 3.0'], From 1c6da93be5079bf269deee1e61f323f959bd25d7 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 17:18:58 +0100 Subject: [PATCH 41/57] various improvments (e.g. docs) and added CSVReader as a placeholder --- can/io/csv.py | 6 ++++-- can/io/logger.py | 4 ++-- can/io/player.py | 30 +++++++++++++++++++++--------- can/io/stdout.py | 1 + 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/can/io/csv.py b/can/io/csv.py index 768a7a666..f6c6bcc6c 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -2,7 +2,7 @@ # coding: utf-8 """ -This modules contains the handler for CSV (comma seperated values) files. +This module contains handling for CSV (comma seperated values) files. """ import base64 @@ -11,7 +11,6 @@ # TODO allow other seperators in CSVWriter -# TODO add a CSVReader class CSVWriter(Listener): """Writes a comma separated text file of @@ -46,3 +45,6 @@ def on_message_received(self, msg): def stop(self): self.csv_file.flush() self.csv_file.close() + +class CSVReader(): + pass diff --git a/can/io/logger.py b/can/io/logger.py index 78c93f07b..1a1a6683b 100755 --- a/can/io/logger.py +++ b/can/io/logger.py @@ -2,15 +2,15 @@ # coding: utf-8 """ -See the `Logger` class. +See the :class:`Logger` class. """ from .asc import ASCWriter from .blf import BLFWriter from .csv import CSVWriter +from .log import CanutilsLogWriter from .sqlite import SqliteWriter from .stdout import Printer -from .log import CanutilsLogWriter class Logger(object): diff --git a/can/io/player.py b/can/io/player.py index 22668f4f6..3b839a63a 100755 --- a/can/io/player.py +++ b/can/io/player.py @@ -2,6 +2,9 @@ # coding: utf-8 """ +This module contains the generic :class:`LogReader` as +well as :class:`MessageSync` which plays back messages +in the recorded order an time intervals. """ from __future__ import print_function @@ -10,8 +13,9 @@ import logging from .asc import ASCReader -from .log import CanutilsLogReader from .blf import BLFReader +from .csv import CSVReader +from .log import CanutilsLogReader from .sqlite import SqliteReader log = logging.getLogger('can.io.player') @@ -26,6 +30,7 @@ class LogReader(object): * .blf * .csv * .db + * .log Exposes a simple iterator interface, to use simply: @@ -39,22 +44,27 @@ class LogReader(object): @classmethod def __new__(cls, other, filename): - if filename.endswith(".blf"): - return BLFReader(filename) - if filename.endswith(".db"): - return SqliteReader(filename) if filename.endswith(".asc"): return ASCReader(filename) - if filename.endswith(".log"): + elif filename.endswith(".blf"): + return BLFReader(filename) + elif filename.endswith(".csv"): + return CSVReader(filename) + elif filename.endswith(".db"): + return SqliteReader(filename) + elif filename.endswith(".log"): return CanutilsLogReader(filename) - - raise NotImplementedError("No read support for this log format") + else: + raise NotImplementedError("No read support for this log format: {}".format(filename)) class MessageSync(object): + """ + Used to iterate over some given messages in the recorded time. + """ def __init__(self, messages, timestamps=True, gap=0.0001, skip=60): - """ + """Creates an new `MessageSync` instance. :param messages: An iterable of :class:`can.Message` instances. :param timestamps: Use the messages' timestamps. @@ -68,6 +78,7 @@ def __init__(self, messages, timestamps=True, gap=0.0001, skip=60): def __iter__(self): log.debug("Iterating over messages at real speed") + playback_start_time = time.time() recorded_start_time = None @@ -87,4 +98,5 @@ def __iter__(self): sleep_period = self.gap time.sleep(sleep_period) + yield m diff --git a/can/io/stdout.py b/can/io/stdout.py index 701a77c22..c0a82ab5a 100644 --- a/can/io/stdout.py +++ b/can/io/stdout.py @@ -6,6 +6,7 @@ """ from __future__ import print_function + import logging from can.listener import Listener From f6ba6b53d0cc496eb38fd7eebf04fea27fa46924 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 18:45:12 +0100 Subject: [PATCH 42/57] added CSVReader --- can/__init__.py | 2 +- can/io/__init__.py | 2 +- can/io/csv.py | 46 +++++++++++++++++++++++++++++++++++++++------- can/io/log.py | 2 +- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/can/__init__.py b/can/__init__.py index 05ad4b444..f612eeda4 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -25,7 +25,7 @@ class CanError(IOError): from can.io import ASCWriter, ASCReader from can.io import BLFReader, BLFWriter from can.io import CanutilsLogReader, CanutilsLogWriter -from can.io import CSVWriter +from can.io import CSVWriter, CSVReader from can.io import SqliteWriter, SqliteReader from can.util import set_logging_level diff --git a/can/io/__init__.py b/can/io/__init__.py index 79f187ceb..f256fb918 100644 --- a/can/io/__init__.py +++ b/can/io/__init__.py @@ -11,6 +11,6 @@ from .log import CanutilsLogReader, CanutilsLogWriter from .asc import ASCWriter, ASCReader from .blf import BLFReader, BLFWriter -from .csv import CSVWriter +from .csv import CSVWriter, CSVReader from .sqlite import SqliteReader, SqliteWriter from .stdout import Printer diff --git a/can/io/csv.py b/can/io/csv.py index f6c6bcc6c..547aba774 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -3,15 +3,20 @@ """ This module contains handling for CSV (comma seperated values) files. + +TODO: CAN FD messages are not yet supported. +TODO: This module could use https://docs.python.org/2/library/csv.html#module-csv + to allow different delimiters for writing, special escape chars to circumvent + the base64 encoding and use csv.Sniffer to automatically deduce the delimiters + of a CSV file. """ -import base64 +from base64 import b64encode, b64decode +from can.message import Message from can.listener import Listener -# TODO allow other seperators in CSVWriter - class CSVWriter(Listener): """Writes a comma separated text file of @@ -21,14 +26,15 @@ class CSVWriter(Listener): * dlc and * data - for each messages received. Each line is terminated with a '\\n'. + for each message received. Each line is terminated with a platform + specific line seperator. """ def __init__(self, filename): self.csv_file = open(filename, 'wt') # Write a header row - self.csv_file.write("timestamp, arbitration id, extended, remote, error, dlc, data\n") + self.csv_file.write("timestamp,arbitration_id,extended,remote,error,dlc,data\n") def on_message_received(self, msg): row = ','.join([ @@ -38,7 +44,7 @@ def on_message_received(self, msg): '1' if msg.is_remote_frame else '0', '1' if msg.is_error_frame else '0', str(msg.dlc), - base64.b64encode(msg.data).decode('utf8') + b64encode(msg.data).decode('utf8') ]) self.csv_file.write(row + '\n') @@ -47,4 +53,30 @@ def stop(self): self.csv_file.close() class CSVReader(): - pass + """Iterator over CAN messages from a .csv file that was + generated by :class:`~can.CSVWriter` or that uses the same + format that is described there. + """ + + def __init__(self, filename): + self.csv_file = open(filename, 'rt') + + # skip the header line + self.header_line = next(self.csv_file).split(',') + + def __iter__(self): + for line in self.csv_file: + + timestamp, arbitration_id, extended, remote, error, dlc, data = line.split(',') + + yield Message( + timestamp=float(timestamp), + is_remote_frame=(remote == '1'), + extended_id=(extended == '1'), + is_error_frame=(error == '1'), + arbitration_id=int(arbitration_id, base=16), + dlc=int(dlc), + data=b64decode(data), + ) + + self.csv_file.close() diff --git a/can/io/log.py b/can/io/log.py index 427b96a3b..f8e50c157 100644 --- a/can/io/log.py +++ b/can/io/log.py @@ -22,7 +22,7 @@ class CanutilsLogReader(object): """ - Iterator of CAN messages from a .log Logging File (candump -L). + Iterator over CAN messages from a .log Logging File (candump -L). .log-format looks like this: (0.0) vcan0 001#8d00100100820100 From bcc6164fbb5bd71527f7e5c31f57e2358aa7d8ce Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 18:46:10 +0100 Subject: [PATCH 43/57] added tests for the CSVReader & CSVWriter pair --- test/logformats_test.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index 728d997e3..43ec1c4c9 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -9,6 +9,8 @@ is correct. The types of messages that are tested differs between the different writer/reader pairs - e.g., some don't handle error frames and comments. + +TODO: implement CAN FD support testing """ from __future__ import print_function @@ -148,11 +150,18 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_error_frames=True, check_comments=True) -class TestSqlFileFormat(unittest.TestCase): +class TestCsvFileFormat(unittest.TestCase): + """Tests can.ASCWriter and can.ASCReader""" + + def test_writer_and_reader(self): + _test_writer_and_reader(self, can.CSVWriter, can.CSVReader, + check_comments=False) + + +class TestSqliteDatabaseFormat(unittest.TestCase): """Tests can.SqliteWriter and can.SqliteReader""" def test_writer_and_reader(self): @@ -198,7 +207,6 @@ class TestBlfFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.BLFWriter, can.BLFReader, - sleep_time=None, check_comments=False) def test_reader(self): From 2eae2992c9c9b49e2dbdfbd4ff139d6103c10c04 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 18:46:59 +0100 Subject: [PATCH 44/57] fix a rounding problem in CSVWriter --- can/io/csv.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/io/csv.py b/can/io/csv.py index 547aba774..fa45bd196 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -38,7 +38,7 @@ def __init__(self, filename): def on_message_received(self, msg): row = ','.join([ - str(msg.timestamp), + repr(msg.timestamp), # cannot use str() here because that is rounding hex(msg.arbitration_id), '1' if msg.id_type else '0', '1' if msg.is_remote_frame else '0', From bd09a72d5eb0cbba85e53c10459f2f55be04e481 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 23 Feb 2018 19:35:25 +0100 Subject: [PATCH 45/57] described the CSV format --- can/io/csv.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/can/io/csv.py b/can/io/csv.py index fa45bd196..1933648ac 100644 --- a/can/io/csv.py +++ b/can/io/csv.py @@ -18,16 +18,24 @@ class CSVWriter(Listener): - """Writes a comma separated text file of - - * timestamp, - * arbitration id, - * flags (extended, remote, error), - * dlc and - * data - - for each message received. Each line is terminated with a platform - specific line seperator. + """Writes a comma separated text file with a line for + each message. + + The columns are as follows: + + ================ ======================= =============== + name of column format description example + ================ ======================= =============== + timestamp decimal float 1483389946.197 + arbitration_id hex 0x00dadada + extended 1 == True, 0 == False 1 + remote 1 == True, 0 == False 0 + error 1 == True, 0 == False 0 + dlc int 6 + data base64 encoded WzQyLCA5XQ== + ================ ======================= =============== + + Each line is terminated with a platform specific line seperator. """ def __init__(self, filename): From 6cb060a5ff5d986cef9b89fd3d395824bc04b61a Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 24 Feb 2018 01:35:19 +0100 Subject: [PATCH 46/57] removed problematic import of removed library --- can/io/sqlite.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/can/io/sqlite.py b/can/io/sqlite.py index 7abf4d181..5f3255729 100644 --- a/can/io/sqlite.py +++ b/can/io/sqlite.py @@ -13,8 +13,6 @@ import logging import sqlite3 -from deprecated import deprecated - from can.listener import BufferedReader from can.message import Message From 1a34e0180d2cb229b7a7b9238e49f177d0fd888f Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 26 Feb 2018 11:45:51 +0100 Subject: [PATCH 47/57] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a61ae9e6e..f8b1bca26 100644 --- a/README.rst +++ b/README.rst @@ -30,7 +30,7 @@ Python developers; providing `common abstractions to different hardware devices`, and a suite of utilities for sending and receiving messages on a can bus. -The library supports Python 2.7, Python 3.3+ as well as PyPy and runs on Mac, Linux and Windows. +The library supports Python 2.7, Python 3.3+ as well as PyPy 2 & 3 and runs on Mac, Linux and Windows. You can find more information in the documentation, online at `python-can.readthedocs.org `__. From 76dd14120fc61b18a0fd1d707cac2416967a9426 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 15 Mar 2018 20:49:46 +0100 Subject: [PATCH 48/57] applied requested changes from code review --- can/interfaces/iscan.py | 6 +++--- can/interfaces/pcan/pcan.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index 6fd18b49b..8bfb89b87 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -152,11 +152,11 @@ class IscanError(CanError): def __init__(self, function, error_code, arguments): super(IscanError, self).__init__() - # Status code + # :Status code self.error_code = error_code - # Function that failed + # :Function that failed self.function = function - # Arguments passed to function + # :Arguments passed to function self.arguments = arguments def __str__(self): diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index c46d06895..4f705b302 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -86,7 +86,7 @@ def __init__(self, channel, *args, **kwargs): else: self.channel_info = channel - bitrate = kwargs.get('bitrate', can.rc.get('bitrate', 500000)) + bitrate = kwargs.get('bitrate', 500000) pcan_bitrate = pcan_bitrate_objs.get(bitrate, PCAN_BAUD_500K) hwtype = PCAN_TYPE_ISA From f72f2adec024fa43e1364f49ffe14ad4fb28a531 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sun, 11 Feb 2018 11:51:59 +1100 Subject: [PATCH 49/57] Handle error frame messages in CanutilsLogWriter and CanutilsLogReader. Closes #217 (cherry picked from commit 61f27e1) --- test/logformats_test.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index 43ec1c4c9..8e5589c00 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -141,7 +141,6 @@ class TestCanutilsLog(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.CanutilsLogWriter, can.CanutilsLogReader, - check_error_frames=False, # TODO this should get fixed, see Issue #217 check_comments=False) From 472b0c7722d5eafb9f1ddbd856659a36de5089b8 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 18:36:41 +0100 Subject: [PATCH 50/57] small changes in can/io/log.py --- can/io/log.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/can/io/log.py b/can/io/log.py index f8e50c157..e44dd5153 100644 --- a/can/io/log.py +++ b/can/io/log.py @@ -34,16 +34,20 @@ def __init__(self, filename): def __iter__(self): for line in self.fp: temp = line.strip() - if len(temp) > 0: + + if temp: + (timestamp, bus, frame) = temp.split() timestamp = float(timestamp[1:-1]) (canId, data) = frame.split('#') + if len(canId) > 3: isExtended = True else: isExtended = False canId = int(canId, 16) - if len(data) > 0 and data[0].lower() == 'r': + + if data and data[0].lower() == 'r': isRemoteFrame = True if len(data) > 1: dlc = int(data[1:]) @@ -57,12 +61,12 @@ def __iter__(self): for i in range(0, 2 * dlc, 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) + extended_id=isExtended, is_remote_frame=isRemoteFrame, + dlc=dlc, data=dataBin) yield msg From 53abda4c8a8485c8b4c71d81cd18054e7c132ed5 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 22 Feb 2018 18:54:42 +0100 Subject: [PATCH 51/57] make ASCWriter's output consistent with the other loggers --- can/io/asc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index de3edf807..ea8d99ed6 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -128,9 +128,9 @@ def log_event(self, message, timestamp=None): return if timestamp is None: - timestamp = time.time() + timestamp = 0 - if timestamp >= self.started: + elif timestamp >= self.started: timestamp -= self.started line = self.EVENT_STRING.format(time=timestamp, message=message) From b4fed47e2f428958e62c1f03b39dc2799e5a0b1d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 15 Mar 2018 20:49:46 +0100 Subject: [PATCH 52/57] applied requested changes from code review --- can/interfaces/iscan.py | 6 +++--- can/interfaces/pcan/pcan.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/can/interfaces/iscan.py b/can/interfaces/iscan.py index 6fd18b49b..8bfb89b87 100644 --- a/can/interfaces/iscan.py +++ b/can/interfaces/iscan.py @@ -152,11 +152,11 @@ class IscanError(CanError): def __init__(self, function, error_code, arguments): super(IscanError, self).__init__() - # Status code + # :Status code self.error_code = error_code - # Function that failed + # :Function that failed self.function = function - # Arguments passed to function + # :Arguments passed to function self.arguments = arguments def __str__(self): diff --git a/can/interfaces/pcan/pcan.py b/can/interfaces/pcan/pcan.py index c46d06895..4f705b302 100644 --- a/can/interfaces/pcan/pcan.py +++ b/can/interfaces/pcan/pcan.py @@ -86,7 +86,7 @@ def __init__(self, channel, *args, **kwargs): else: self.channel_info = channel - bitrate = kwargs.get('bitrate', can.rc.get('bitrate', 500000)) + bitrate = kwargs.get('bitrate', 500000) pcan_bitrate = pcan_bitrate_objs.get(bitrate, PCAN_BAUD_500K) hwtype = PCAN_TYPE_ISA From 79d8d3854ab816b5df0db08fd270d58fa4792f61 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 17 Mar 2018 14:24:57 +0100 Subject: [PATCH 53/57] changed CanutilsLogWriter and ASCWriter to behave more similar --- can/io/asc.py | 48 ++++++++++++++++++++++++++++++++++++++---------- can/io/log.py | 43 +++++++++++++++++++++++++++++++------------ 2 files changed, 69 insertions(+), 22 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index ea8d99ed6..4866c311f 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -3,6 +3,8 @@ """ Contains handling of ASC logging files. + +Example .asc file: https://bitbucket.org/tobylorenz/vector_asc/src/47556e1a6d32c859224ca62d075e1efcc67fa690/src/Vector/ASC/tests/unittests/data/CAN_Log_Trigger_3_2.asc?at=master&fileviewer=file-view-default """ from datetime import datetime @@ -21,6 +23,8 @@ class ASCReader(object): """ Iterator of CAN messages from a ASC logging file. + + TODO: turn realtive timestamps back to absolute form """ def __init__(self, filename): @@ -28,7 +32,7 @@ def __init__(self, filename): @staticmethod def _extract_can_id(str_can_id): - if str_can_id[-1:].lower() == "x": + if str_can_id[-1:].lower() == 'x': is_extended = True can_id = int(str_can_id[0:-1], 16) else: @@ -98,21 +102,33 @@ def __iter__(self): class ASCWriter(Listener): - """Logs CAN data to an ASCII log file (.asc)""" + """Logs CAN data to an ASCII log file (.asc). + + The measurement starts with the timestamp of the first registered message. + 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. + """ LOG_STRING = "{time: 9.4f} {channel} {id:<15} Rx {dtype} {data}\n" EVENT_STRING = "{time: 9.4f} {message}\n" def __init__(self, filename, channel=1): - now = datetime.now().strftime("%a %b %m %I:%M:%S %p %Y") + # setup self.channel = channel self.started = time.time() self.log_file = open(filename, 'w') + + # write start of file header + now = datetime.now().strftime("%a %b %m %I:%M:%S %p %Y") self.log_file.write("date %s\n" % now) self.log_file.write("base hex timestamps absolute\n") self.log_file.write("internal events logged\n") - self.log_file.write("Begin Triggerblock %s\n" % now) - self.log_event("Start of measurement") + + # the last part is written with the timestamp of the first message + self.header_written = False + self.last_timestamp = None + self.started = None def stop(self): """Stops logging and closes the file.""" @@ -127,9 +143,21 @@ def log_event(self, message, timestamp=None): logger.debug("ASCWriter: ignoring empty message") return - if timestamp is None: - timestamp = 0 + # this is the case for the very first message: + if not self.header_written: + self.last_timestamp = (timestamp or 0.0) + self.started = self.last_timestamp + self.log_file.write("Begin Triggerblock %s\n" % self.last_timestamp) + self.log_event("Start of measurement") + self.header_written = True + + # figure out the correct timestamp + if msg.timestamp is None or msg.timestamp < self.last_timestamp: + timestamp = self.last_timestamp + else: + timestamp = msg.timestamp + # turn into relative timestamps elif timestamp >= self.started: timestamp -= self.started @@ -145,7 +173,7 @@ def on_message_received(self, msg): return if msg.is_remote_frame: - dtype = "r" + dtype = 'r' data = [] else: dtype = "d {}".format(msg.dlc) @@ -153,7 +181,7 @@ def on_message_received(self, msg): arb_id = "{:X}".format(msg.arbitration_id) if msg.is_extended_id: - arb_id += "x" + arb_id += 'x' channel = msg.channel if isinstance(msg.channel, int) else self.channel @@ -161,7 +189,7 @@ def on_message_received(self, msg): channel=channel, id=arb_id, dtype=dtype, - data=" ".join(data)) + data=' '.join(data)) if not self.log_file.closed: self.log_file.write(line) diff --git a/can/io/log.py b/can/io/log.py index e44dd5153..e8a822f34 100644 --- a/can/io/log.py +++ b/can/io/log.py @@ -5,15 +5,20 @@ 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). + +TODO: "channel" is not uesed by CanutilsLogWriter. Is that supposed to be like that? """ import time import datetime +import logging from can.message import Message from can.listener import Listener +log = logging.getLogger('can.io.canutils') + CAN_MSG_EXT = 0x80000000 CAN_ERR_FLAG = 0x20000000 CAN_ERR_BUSERROR = 0x00000080 @@ -73,39 +78,53 @@ def __iter__(self): class CanutilsLogWriter(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, filename, channel="vcan0"): self.channel = channel - self.started = time.time() self.log_file = open(filename, 'w') + self.last_timestamp = None def stop(self): """Stops logging and closes the file.""" if self.log_file is not None: self.log_file.close() self.log_file = None + else: + log.warn("ignoring attempt to colse a already closed file") def on_message_received(self, msg): if self.log_file is None: - return - if msg.is_error_frame: - self.log_file.write("(%f) vcan0 %08X#0000000000000000\n" % (msg.timestamp, CAN_ERR_FLAG | CAN_ERR_BUSERROR, )) + log.warn("ignoring write attempt to closed file") return - timestamp = msg.timestamp - if timestamp >= self.started: - timestamp -= self.started + # this is the case for the very first message: + if self.last_timestamp is None: + self.last_timestamp = (msg.timestamp or 0.0) - if msg.is_remote_frame: + # figure out the correct timestamp + if msg.timestamp is None or msg.timestamp < self.last_timestamp: + timestamp = self.last_timestamp + else: + timestamp = msg.timestamp + + if msg.is_error_frame: + self.log_file.write("(%f) vcan0 %08X#0000000000000000\n" % (timestamp, CAN_ERR_FLAG | CAN_ERR_BUSERROR)) + + elif msg.is_remote_frame: data = [] if msg.is_extended_id: - self.log_file.write("(%f) vcan0 %08X#R\n" % (msg.timestamp, msg.arbitration_id )) + self.log_file.write("(%f) vcan0 %08X#R\n" % (timestamp, msg.arbitration_id)) else: - self.log_file.write("(%f) vcan0 %03X#R\n" % (msg.timestamp, msg.arbitration_id )) + self.log_file.write("(%f) vcan0 %03X#R\n" % (timestamp, msg.arbitration_id)) + else: data = ["{:02X}".format(byte) for byte in msg.data] if msg.is_extended_id: - self.log_file.write("(%f) vcan0 %08X#%s\n" % (msg.timestamp, msg.arbitration_id, "".join(data))) + self.log_file.write("(%f) vcan0 %08X#%s\n" % (timestamp, msg.arbitration_id, ''.join(data))) else: - self.log_file.write("(%f) vcan0 %03X#%s\n" % (msg.timestamp, msg.arbitration_id, "".join(data))) + self.log_file.write("(%f) vcan0 %03X#%s\n" % (timestamp, msg.arbitration_id, ''.join(data))) From d1a6b16fcbcfd15082eaae9c47b1b6fd7fd20e3d Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Sat, 17 Mar 2018 14:32:20 +0100 Subject: [PATCH 54/57] small fixes --- can/io/asc.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index 4866c311f..6660d21dc 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -148,17 +148,15 @@ def log_event(self, message, timestamp=None): self.last_timestamp = (timestamp or 0.0) self.started = self.last_timestamp self.log_file.write("Begin Triggerblock %s\n" % self.last_timestamp) - self.log_event("Start of measurement") self.header_written = True + self.log_event("Start of measurement") # recursive # figure out the correct timestamp - if msg.timestamp is None or msg.timestamp < self.last_timestamp: + if timestamp is None or timestamp < self.last_timestamp: timestamp = self.last_timestamp - else: - timestamp = msg.timestamp # turn into relative timestamps - elif timestamp >= self.started: + if timestamp >= self.started: timestamp -= self.started line = self.EVENT_STRING.format(time=timestamp, message=message) From a4f420da997e3609cf79dbfb846cd3cbfd77a091 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 2 Apr 2018 12:22:31 +0200 Subject: [PATCH 55/57] adjusted the ASC test cases to check only rounded timestamps --- test/logformats_test.py | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/test/logformats_test.py b/test/logformats_test.py index 8e5589c00..21285217f 100644 --- a/test/logformats_test.py +++ b/test/logformats_test.py @@ -38,20 +38,25 @@ def _test_writer_and_reader(test_case, writer_constructor, reader_constructor, sleep_time=None, check_remote_frames=True, check_error_frames=True, - check_comments=False): + check_comments=False, round_timestamps=False): """Tests a pair of writer and reader by writing all data first and then reading all data and checking if they could be reconstructed correctly. - :param test_case: the test case the use the assert methods on - :param sleep_time: specifies the time to sleep after writing all messages. + :param unittest.TestCase test_case: the test case the use the assert methods on + :param Callable writer_constructor: the constructor of the writer class + :param Callable reader_constructor: the constructor of the reader class + + :param float sleep_time: specifies the time to sleep after writing all messages. gets ignored when set to None - :param check_remote_frames: if true, also tests remote frames - :param check_error_frames: if true, also tests error frames - :param check_comments: if true, also inserts comments at some + :param bool check_remote_frames: if True, also tests remote frames + :param bool check_error_frames: if True, also tests error frames + :param bool check_comments: if True, also inserts comments at some locations and checks if they are contained anywhere literally in the resulting file. The locations as selected randomly but deterministically, which makes the test reproducible. + :param bool round_timestamps: if True, rounds timestamps using :meth:`~builtin.round` + before comparing the read messages/events """ assert isinstance(test_case, unittest.TestCase), \ @@ -119,7 +124,12 @@ def _test_writer_and_reader(test_case, writer_constructor, reader_constructor, s # check the order and content of the individual messages for i, (read, original) in enumerate(zip(read_messages, original_messages)): try: + # check everything except the timestamp test_case.assertEqual(read, original) + # check the timestamp + if round_timestamps: + original.timestamp = round(original.timestamp) + read.timestamp = round(read.timestamp) test_case.assertAlmostEqual(read.timestamp, original.timestamp, places=6) except Exception as exception: # attach the index @@ -149,7 +159,7 @@ class TestAscFileFormat(unittest.TestCase): def test_writer_and_reader(self): _test_writer_and_reader(self, can.ASCWriter, can.ASCReader, - check_comments=True) + check_comments=True, round_timestamps=True) class TestCsvFileFormat(unittest.TestCase): From 0b9be035dc17603ffc18168fef5ebea42f0edae3 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 2 Apr 2018 12:23:40 +0200 Subject: [PATCH 56/57] unified the code in and corrected the ASCWriter class --- can/io/asc.py | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index 6660d21dc..d61b70b1d 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -110,13 +110,13 @@ class ASCWriter(Listener): It the first message does not have a timestamp, it is set to zero. """ - LOG_STRING = "{time: 9.4f} {channel} {id:<15} Rx {dtype} {data}\n" - EVENT_STRING = "{time: 9.4f} {message}\n" + FORMAT_MESSAGE = "{channel} {id:<15} Rx {dtype} {data}" + FORMAT_DATE = "%a %b %m %I:%M:%S %p %Y" + FORMAT_EVENT = "{time} {message}\n" def __init__(self, filename, channel=1): # setup self.channel = channel - self.started = time.time() self.log_file = open(filename, 'w') # write start of file header @@ -137,7 +137,11 @@ def stop(self): self.log_file.close() def log_event(self, message, timestamp=None): - """Add an arbitrary message to the log file.""" + """Add a message to the log file. + + :param str message: an arbitrary message + :param float message: the absolute timestamp of the event + """ if not message: # if empty or None logger.debug("ASCWriter: ignoring empty message") @@ -149,7 +153,7 @@ def log_event(self, message, timestamp=None): self.started = self.last_timestamp self.log_file.write("Begin Triggerblock %s\n" % self.last_timestamp) self.header_written = True - self.log_event("Start of measurement") # recursive + self.log_event("Start of measurement") # recursive call # figure out the correct timestamp if timestamp is None or timestamp < self.last_timestamp: @@ -159,9 +163,13 @@ def log_event(self, message, timestamp=None): if timestamp >= self.started: timestamp -= self.started - line = self.EVENT_STRING.format(time=timestamp, message=message) + formatted_date = time.strftime(self.FORMAT_DATE, time.localtime(timestamp)) - if not self.log_file.closed: + line = self.FORMAT_EVENT.format(time=timestamp, message=message) + + if self.log_file.closed: + logger.warn("ASCWriter: ignoring write call to closed file") + else: self.log_file.write(line) def on_message_received(self, msg): @@ -183,11 +191,9 @@ def on_message_received(self, msg): channel = msg.channel if isinstance(msg.channel, int) else self.channel - line = self.LOG_STRING.format(time=msg.timestamp, - channel=channel, - id=arb_id, - dtype=dtype, - data=' '.join(data)) + serialized = self.FORMAT_MESSAGE.format(channel=channel, + id=arb_id, + dtype=dtype, + data=' '.join(data)) - if not self.log_file.closed: - self.log_file.write(line) + self.log_event(serialized, msg.timestamp) From 8f526c5a91956909219f0e2ed8fee6d1189ed034 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 2 Apr 2018 12:34:22 +0200 Subject: [PATCH 57/57] small corrections --- can/io/asc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/can/io/asc.py b/can/io/asc.py index d61b70b1d..f892418f7 100644 --- a/can/io/asc.py +++ b/can/io/asc.py @@ -112,7 +112,7 @@ class ASCWriter(Listener): FORMAT_MESSAGE = "{channel} {id:<15} Rx {dtype} {data}" FORMAT_DATE = "%a %b %m %I:%M:%S %p %Y" - FORMAT_EVENT = "{time} {message}\n" + FORMAT_EVENT = "{timestamp: 9.4f} {message}\n" def __init__(self, filename, channel=1): # setup @@ -151,7 +151,9 @@ def log_event(self, message, timestamp=None): if not self.header_written: self.last_timestamp = (timestamp or 0.0) self.started = self.last_timestamp - self.log_file.write("Begin Triggerblock %s\n" % self.last_timestamp) + formatted_date = time.strftime(self.FORMAT_DATE, time.localtime(self.last_timestamp)) + self.log_file.write("base hex timestamps absolute\n") + self.log_file.write("Begin Triggerblock %s\n" % formatted_date) self.header_written = True self.log_event("Start of measurement") # recursive call @@ -159,13 +161,11 @@ def log_event(self, message, timestamp=None): if timestamp is None or timestamp < self.last_timestamp: timestamp = self.last_timestamp - # turn into relative timestamps + # turn into relative timestamps if necessary if timestamp >= self.started: timestamp -= self.started - formatted_date = time.strftime(self.FORMAT_DATE, time.localtime(timestamp)) - - line = self.FORMAT_EVENT.format(time=timestamp, message=message) + line = self.FORMAT_EVENT.format(timestamp=timestamp, message=message) if self.log_file.closed: logger.warn("ASCWriter: ignoring write call to closed file")