Skip to content

Commit ab0cc7f

Browse files
authored
Merge branch 'develop' into update-ci-tools
2 parents 0acabbf + 1ce5614 commit ab0cc7f

File tree

6 files changed

+79
-6
lines changed

6 files changed

+79
-6
lines changed

README.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ Python developers; providing common abstractions to
5151
different hardware devices, and a suite of utilities for sending and receiving
5252
messages on a can bus.
5353

54-
The library currently supports Python 3.7+ as well as PyPy 3 and runs
55-
on Mac, Linux and Windows.
54+
The library currently supports CPython as well as PyPy and runs on Mac, Linux and Windows.
5655

5756
============================== ===========
5857
Library Version Python

can/interfaces/pcan/pcan.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
from typing import Optional
1111

12+
from packaging import version
13+
1214
from ...message import Message
1315
from ...bus import BusABC, BusState
1416
from ...util import len2dlc, dlc2len
@@ -19,13 +21,15 @@
1921
PCAN_BITRATES,
2022
PCAN_FD_PARAMETER_LIST,
2123
PCAN_CHANNEL_NAMES,
24+
PCAN_NONEBUS,
2225
PCAN_BAUD_500K,
2326
PCAN_TYPE_ISA,
2427
PCANBasic,
2528
PCAN_ERROR_OK,
2629
PCAN_ALLOW_ERROR_FRAMES,
2730
PCAN_PARAMETER_ON,
2831
PCAN_RECEIVE_EVENT,
32+
PCAN_API_VERSION,
2933
PCAN_DEVICE_NUMBER,
3034
PCAN_ERROR_QRCVEMPTY,
3135
PCAN_ERROR_BUSLIGHT,
@@ -58,6 +62,8 @@
5862
# Set up logging
5963
log = logging.getLogger("can.pcan")
6064

65+
MIN_PCAN_API_VERSION = version.parse("4.2.0")
66+
6167

6268
try:
6369
# use the "uptime" library if available
@@ -100,7 +106,7 @@ def __init__(
100106
state=BusState.ACTIVE,
101107
bitrate=500000,
102108
*args,
103-
**kwargs
109+
**kwargs,
104110
):
105111
"""A PCAN USB interface to CAN.
106112
@@ -206,6 +212,8 @@ def __init__(
206212
self.m_objPCANBasic = PCANBasic()
207213
self.m_PcanHandle = channel
208214

215+
self.check_api_version()
216+
209217
if state is BusState.ACTIVE or state is BusState.PASSIVE:
210218
self.state = state
211219
else:
@@ -304,6 +312,21 @@ def bits(n):
304312

305313
return complete_text
306314

315+
def get_api_version(self):
316+
error, value = self.m_objPCANBasic.GetValue(PCAN_NONEBUS, PCAN_API_VERSION)
317+
if error != PCAN_ERROR_OK:
318+
raise CanInitializationError(f"Failed to read pcan basic api version")
319+
320+
return version.parse(value.decode("ascii"))
321+
322+
def check_api_version(self):
323+
apv = self.get_api_version()
324+
if apv < MIN_PCAN_API_VERSION:
325+
log.warning(
326+
f"Minimum version of pcan api is {MIN_PCAN_API_VERSION}."
327+
f" Installed version is {apv}. Consider upgrade of pcan basic package"
328+
)
329+
307330
def status(self):
308331
"""
309332
Query the PCAN bus status.

can/util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ def _create_bus_config(config: Dict[str, Any]) -> typechecking.BusConfig:
232232
"btr1",
233233
):
234234
if key in config:
235-
timing_conf[key] = int(config[key], base=0)
235+
timing_conf[key] = int(str(config[key]), base=0)
236236
del config[key]
237237
if timing_conf:
238238
timing_conf["bitrate"] = config["bitrate"]

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"typing_extensions>=3.10.0.0",
9090
'pywin32;platform_system=="Windows" and platform_python_implementation=="CPython"',
9191
'msgpack~=1.0.0;platform_system!="Windows"',
92+
"packaging",
9293
],
9394
extras_require=extras_require,
9495
)

test/test_pcan.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import can
1414
from can.bus import BusState
15+
from can.exceptions import CanInitializationError
1516
from can.interfaces.pcan.basic import *
1617
from can.interfaces.pcan import PcanBus, PcanError
1718

@@ -26,6 +27,8 @@ def setUp(self) -> None:
2627
self.mock_pcan.Initialize.return_value = PCAN_ERROR_OK
2728
self.mock_pcan.InitializeFD = Mock(return_value=PCAN_ERROR_OK)
2829
self.mock_pcan.SetValue = Mock(return_value=PCAN_ERROR_OK)
30+
self.mock_pcan.GetValue = self._mockGetValue
31+
self.PCAN_API_VERSION_SIM = "4.2"
2932

3033
self.bus = None
3134

@@ -34,6 +37,17 @@ def tearDown(self) -> None:
3437
self.bus.shutdown()
3538
self.bus = None
3639

40+
def _mockGetValue(self, channel, parameter):
41+
"""
42+
This method is used as mock for GetValue method of PCANBasic object.
43+
Only a subset of parameters are supported.
44+
"""
45+
if parameter == PCAN_API_VERSION:
46+
return PCAN_ERROR_OK, self.PCAN_API_VERSION_SIM.encode("ascii")
47+
raise NotImplementedError(
48+
f"No mock return value specified for parameter {parameter}"
49+
)
50+
3751
def test_bus_creation(self) -> None:
3852
self.bus = can.Bus(bustype="pcan")
3953
self.assertIsInstance(self.bus, PcanBus)
@@ -52,6 +66,24 @@ def test_bus_creation_fd(self) -> None:
5266
self.mock_pcan.Initialize.assert_not_called()
5367
self.mock_pcan.InitializeFD.assert_called_once()
5468

69+
def test_api_version_low(self) -> None:
70+
self.PCAN_API_VERSION_SIM = "1.0"
71+
with self.assertLogs("can.pcan", level="WARNING") as cm:
72+
self.bus = can.Bus(bustype="pcan")
73+
found_version_warning = False
74+
for i in cm.output:
75+
if "version" in i and "pcan" in i:
76+
found_version_warning = True
77+
self.assertTrue(
78+
found_version_warning,
79+
f"No warning was logged for incompatible api version {cm.output}",
80+
)
81+
82+
def test_api_version_read_fail(self) -> None:
83+
self.mock_pcan.GetValue = Mock(return_value=(PCAN_ERROR_ILLOPERATION, None))
84+
with self.assertRaises(CanInitializationError):
85+
self.bus = can.Bus(bustype="pcan")
86+
5587
@parameterized.expand(
5688
[
5789
("no_error", PCAN_ERROR_OK, PCAN_ERROR_OK, "some ok text 1"),
@@ -108,8 +140,11 @@ def test_reset(self, name, status, expected_result) -> None:
108140
)
109141
def test_get_device_number(self, name, status, expected_result) -> None:
110142
with self.subTest(name):
111-
self.mock_pcan.GetValue = Mock(return_value=(status, 1))
112143
self.bus = can.Bus(bustype="pcan", fd=True)
144+
# Mock GetValue after creation of bus to use first mock of
145+
# GetValue in constructor
146+
self.mock_pcan.GetValue = Mock(return_value=(status, 1))
147+
113148
self.assertEqual(self.bus.get_device_number(), expected_result)
114149
self.mock_pcan.GetValue.assert_called_once_with(
115150
PCAN_USBBUS1, PCAN_DEVICE_NUMBER

test/test_util.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22
import warnings
33

4-
from can.util import _rename_kwargs
4+
from can.util import _create_bus_config, _rename_kwargs
55

66

77
class RenameKwargsTest(unittest.TestCase):
@@ -47,3 +47,18 @@ def test_with_new_and_alias_present(self):
4747
aliases = {"old_a": "a", "old_b": "b", "z": None}
4848
with self.assertRaises(TypeError):
4949
self._test(kwargs, aliases)
50+
51+
52+
class TestBusConfig(unittest.TestCase):
53+
base_config = dict(interface="socketcan", bitrate=500_000)
54+
55+
def test_timing_can_use_int(self):
56+
"""
57+
Test that an exception is not raised when using
58+
integers for timing values in config.
59+
"""
60+
timing_conf = dict(tseg1=5, tseg2=10, sjw=25)
61+
try:
62+
_create_bus_config({**self.base_config, **timing_conf})
63+
except TypeError as e:
64+
self.fail(e)

0 commit comments

Comments
 (0)