Skip to content

Commit 35de98e

Browse files
authored
Update PCAN Basic to 4.6.2.753 (#1481)
* update PCAN Basic to 4.6.2.753 * faster channel detection * fix PcanBus._detect_available_configs * remove TODO * add more information to pcan channel detection Co-authored-by: zariiii9003 <zariiii9003@users.noreply.github.com>
1 parent 69a5209 commit 35de98e

File tree

3 files changed

+120
-42
lines changed

3 files changed

+120
-42
lines changed

can/interfaces/pcan/basic.py

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88
#
99
# ------------------------------------------------------------------
1010
# Author : Keneth Wagner
11+
# Last change: 2022-07-06
1112
# ------------------------------------------------------------------
1213
#
13-
# Copyright (C) 1999-2021 PEAK-System Technik GmbH, Darmstadt
14+
# Copyright (C) 1999-2022 PEAK-System Technik GmbH, Darmstadt
1415
# more Info at http://www.peak-system.com
1516

1617
# Module Imports
1718
from ctypes import *
1819
from ctypes.util import find_library
19-
from string import *
2020
import platform
2121

2222
import logging
@@ -282,6 +282,12 @@
282282
PCAN_ATTACHED_CHANNELS = TPCANParameter(
283283
0x2B
284284
) # Get information about PCAN channels attached to a system
285+
PCAN_ALLOW_ECHO_FRAMES = TPCANParameter(
286+
0x2C
287+
) # Echo messages reception status within a PCAN-Channel
288+
PCAN_DEVICE_PART_NUMBER = TPCANParameter(
289+
0x2D
290+
) # Get the part number associated to a device
285291

286292
# DEPRECATED parameters
287293
#
@@ -354,8 +360,8 @@
354360
33
355361
) # Maximum length of the name of a device: 32 characters + terminator
356362
MAX_LENGTH_VERSION_STRING = int(
357-
18
358-
) # Maximum length of a version string: 17 characters + terminator
363+
256
364+
) # Maximum length of a version string: 255 characters + terminator
359365

360366
# PCAN message types
361367
#
@@ -377,6 +383,9 @@
377383
PCAN_MESSAGE_ESI = TPCANMessageType(
378384
0x10
379385
) # The PCAN message represents a FD error state indicator(CAN FD transmitter was error active)
386+
PCAN_MESSAGE_ECHO = TPCANMessageType(
387+
0x20
388+
) # The PCAN message represents an echo CAN Frame
380389
PCAN_MESSAGE_ERRFRAME = TPCANMessageType(
381390
0x40
382391
) # The PCAN message represents an error frame
@@ -654,33 +663,38 @@ class PCANBasic:
654663
"""PCAN-Basic API class implementation"""
655664

656665
def __init__(self):
657-
# Loads the PCANBasic API
658-
#
659666
if platform.system() == "Windows":
660-
# Loads the API on Windows
661-
_dll_path = find_library("PCANBasic")
662-
self.__m_dllBasic = windll.LoadLibrary(_dll_path) if _dll_path else None
663-
aReg = winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE)
664-
try:
665-
aKey = winreg.OpenKey(aReg, r"SOFTWARE\PEAK-System\PEAK-Drivers")
666-
winreg.CloseKey(aKey)
667-
except OSError:
668-
logger.error("Exception: The PEAK-driver couldn't be found!")
669-
finally:
670-
winreg.CloseKey(aReg)
671-
elif "CYGWIN" in platform.system():
672-
self.__m_dllBasic = cdll.LoadLibrary("PCANBasic.dll")
673-
# Unfortunately cygwin python has no winreg module, so we can't
674-
# check for the registry key.
675-
elif platform.system() == "Linux":
676-
# Loads the API on Linux
677-
self.__m_dllBasic = cdll.LoadLibrary("libpcanbasic.so")
667+
load_library_func = windll.LoadLibrary
668+
669+
# look for Peak drivers in Windows registry
670+
with winreg.ConnectRegistry(None, winreg.HKEY_LOCAL_MACHINE) as reg:
671+
try:
672+
with winreg.OpenKey(reg, r"SOFTWARE\PEAK-System\PEAK-Drivers"):
673+
pass
674+
except OSError:
675+
raise OSError("The PEAK-driver could not be found!") from None
676+
else:
677+
load_library_func = cdll.LoadLibrary
678+
679+
if platform.system() == "Windows" or "CYGWIN" in platform.system():
680+
lib_name = "PCANBasic.dll"
678681
elif platform.system() == "Darwin":
679-
self.__m_dllBasic = cdll.LoadLibrary(find_library("libPCBUSB.dylib"))
682+
# PCBUSB library is a third-party software created
683+
# and maintained by the MacCAN project
684+
lib_name = "libPCBUSB.dylib"
680685
else:
681-
self.__m_dllBasic = cdll.LoadLibrary("libpcanbasic.so")
682-
if self.__m_dllBasic is None:
683-
logger.error("Exception: The PCAN-Basic DLL couldn't be loaded!")
686+
lib_name = "libpcanbasic.so"
687+
688+
lib_path = find_library(lib_name)
689+
if not lib_path:
690+
raise OSError(f"{lib_name} library not found.")
691+
692+
try:
693+
self.__m_dllBasic = load_library_func(lib_path)
694+
except OSError:
695+
raise OSError(
696+
f"The PCAN-Basic API could not be loaded. ({lib_path})"
697+
) from None
684698

685699
# Initializes a PCAN Channel
686700
#
@@ -965,6 +979,7 @@ def GetValue(self, Channel, Parameter):
965979
or Parameter == PCAN_BITRATE_INFO_FD
966980
or Parameter == PCAN_IP_ADDRESS
967981
or Parameter == PCAN_FIRMWARE_VERSION
982+
or Parameter == PCAN_DEVICE_PART_NUMBER
968983
):
969984
mybuffer = create_string_buffer(256)
970985

@@ -974,6 +989,12 @@ def GetValue(self, Channel, Parameter):
974989
return (TPCANStatus(res[0]),)
975990
mybuffer = (TPCANChannelInformation * res[1])()
976991

992+
elif (
993+
Parameter == PCAN_ACCEPTANCE_FILTER_11BIT
994+
or PCAN_ACCEPTANCE_FILTER_29BIT
995+
):
996+
mybuffer = c_int64(0)
997+
977998
else:
978999
mybuffer = c_int(0)
9791000

@@ -1017,6 +1038,11 @@ def SetValue(self, Channel, Parameter, Buffer):
10171038
or Parameter == PCAN_TRACE_LOCATION
10181039
):
10191040
mybuffer = create_string_buffer(256)
1041+
elif (
1042+
Parameter == PCAN_ACCEPTANCE_FILTER_11BIT
1043+
or PCAN_ACCEPTANCE_FILTER_29BIT
1044+
):
1045+
mybuffer = c_int64(0)
10201046
else:
10211047
mybuffer = c_int(0)
10221048

can/interfaces/pcan/pcan.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66
import time
77
from datetime import datetime
88
import platform
9-
10-
from typing import Optional
9+
from typing import Optional, List
1110

1211
from packaging import version
1312

14-
from ...message import Message
15-
from ...bus import BusABC, BusState
16-
from ...util import len2dlc, dlc2len
17-
from ...exceptions import CanError, CanOperationError, CanInitializationError
18-
13+
from can import (
14+
BusABC,
15+
BusState,
16+
CanError,
17+
CanOperationError,
18+
CanInitializationError,
19+
Message,
20+
)
21+
from can.util import len2dlc, dlc2len
1922

2023
from .basic import (
2124
PCAN_BITRATES,
@@ -57,6 +60,8 @@
5760
FEATURE_FD_CAPABLE,
5861
PCAN_DICT_STATUS,
5962
PCAN_BUSOFF_AUTORESET,
63+
PCAN_ATTACHED_CHANNELS,
64+
TPCANChannelInformation,
6065
)
6166

6267

@@ -78,7 +83,6 @@
7883
except ImportError as error:
7984
log.warning(
8085
"uptime library not available, timestamps are relative to boot time and not to Epoch UTC",
81-
exc_info=True,
8286
)
8387
boottimeEpoch = 0
8488

@@ -278,7 +282,7 @@ def __init__(
278282
# TODO Remove Filter when MACCan actually supports it:
279283
# https://github.com/mac-can/PCBUSB-Library/
280284
log.debug(
281-
"Ignoring error. PCAN_ALLOW_ERROR_FRAMES is still unsupported by OSX Library PCANUSB v0.10"
285+
"Ignoring error. PCAN_ALLOW_ERROR_FRAMES is still unsupported by OSX Library PCANUSB v0.11.2"
282286
)
283287

284288
if kwargs.get("auto_reset", False):
@@ -624,7 +628,35 @@ def _detect_available_configs():
624628
library_handle = PCANBasic()
625629
except OSError:
626630
return channels
631+
627632
interfaces = []
633+
634+
if platform.system() != "Darwin":
635+
res, value = library_handle.GetValue(PCAN_NONEBUS, PCAN_ATTACHED_CHANNELS)
636+
if res != PCAN_ERROR_OK:
637+
return interfaces
638+
channel_information: List[TPCANChannelInformation] = list(value)
639+
for channel in channel_information:
640+
# find channel name in PCAN_CHANNEL_NAMES by value
641+
channel_name = next(
642+
_channel_name
643+
for _channel_name, channel_id in PCAN_CHANNEL_NAMES.items()
644+
if channel_id.value == channel.channel_handle
645+
)
646+
channel_config = {
647+
"interface": "pcan",
648+
"channel": channel_name,
649+
"supports_fd": bool(channel.device_features & FEATURE_FD_CAPABLE),
650+
"controller_number": channel.controller_number,
651+
"device_features": channel.device_features,
652+
"device_id": channel.device_id,
653+
"device_name": channel.device_name.decode("latin-1"),
654+
"device_type": channel.device_type,
655+
"channel_condition": channel.channel_condition,
656+
}
657+
interfaces.append(channel_config)
658+
return interfaces
659+
628660
for i in range(16):
629661
interfaces.append(
630662
{

test/test_pcan.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
import ctypes
6+
import platform
67
import unittest
78
from unittest import mock
89
from unittest.mock import Mock
@@ -330,11 +331,30 @@ def test_state(self, name, bus_state: BusState, expected_parameter) -> None:
330331
)
331332

332333
def test_detect_available_configs(self) -> None:
333-
self.mock_pcan.GetValue = Mock(
334-
return_value=(PCAN_ERROR_OK, PCAN_CHANNEL_AVAILABLE)
335-
)
336-
configs = PcanBus._detect_available_configs()
337-
self.assertEqual(len(configs), 50)
334+
if platform.system() == "Darwin":
335+
self.mock_pcan.GetValue = Mock(
336+
return_value=(PCAN_ERROR_OK, PCAN_CHANNEL_AVAILABLE)
337+
)
338+
configs = PcanBus._detect_available_configs()
339+
self.assertEqual(len(configs), 50)
340+
else:
341+
value = (TPCANChannelInformation * 1).from_buffer_copy(
342+
b"Q\x00\x05\x00\x01\x00\x00\x00PCAN-USB FD\x00\x00\x00\x00"
343+
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
344+
b'\x00\x00\x00\x00\x00\x00\x003"\x11\x00\x01\x00\x00\x00'
345+
)
346+
self.mock_pcan.GetValue = Mock(return_value=(PCAN_ERROR_OK, value))
347+
configs = PcanBus._detect_available_configs()
348+
assert len(configs) == 1
349+
assert configs[0]["interface"] == "pcan"
350+
assert configs[0]["channel"] == "PCAN_USBBUS1"
351+
assert configs[0]["supports_fd"]
352+
assert configs[0]["controller_number"] == 0
353+
assert configs[0]["device_features"] == 1
354+
assert configs[0]["device_id"] == 1122867
355+
assert configs[0]["device_name"] == "PCAN-USB FD"
356+
assert configs[0]["device_type"] == 5
357+
assert configs[0]["channel_condition"] == 1
338358

339359
@parameterized.expand([("valid", PCAN_ERROR_OK, "OK"), ("invalid", 0x00005, None)])
340360
def test_status_string(self, name, status, expected_result) -> None:

0 commit comments

Comments
 (0)