Skip to content

Commit abb9f8f

Browse files
Merge branch 'ifak' into develop
2 parents 67f6995 + 865a7de commit abb9f8f

6 files changed

Lines changed: 180 additions & 1 deletion

File tree

can/interface.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
'usb2can': ('can.interfaces.usb2can', 'Usb2canBus'),
1717
'ixxat': ('can.interfaces.ixxat', 'IXXATBus'),
1818
'nican': ('can.interfaces.nican', 'NicanBus'),
19+
'iscan': ('can.interfaces.iscan', 'IscanBus'),
1920
'remote': ('can.interfaces.remote', 'RemoteBus'),
2021
'virtual': ('can.interfaces.virtual', 'VirtualBus'),
2122
'neovi': ('can.interfaces.neovi_api', 'NeoVIBus')

can/interfaces/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55

66
VALID_INTERFACES = set(['kvaser', 'serial', 'pcan', 'socketcan_native',
77
'socketcan_ctypes', 'socketcan', 'usb2can', 'ixxat',
8-
'nican', 'remote', 'virtual', 'neovi'])
8+
'nican', 'iscan', 'remote', 'virtual', 'neovi'])

can/interfaces/iscan.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
"""
2+
Interface for isCAN from Thorsis Technologies GmbH, former ifak system GmbH.
3+
"""
4+
import ctypes
5+
import time
6+
import logging
7+
8+
from can import CanError, BusABC, Message
9+
10+
11+
logger = logging.getLogger(__name__)
12+
13+
CanData = ctypes.c_ubyte * 8
14+
15+
class MessageExStruct(ctypes.Structure):
16+
_fields_ = [
17+
("message_id", ctypes.c_ulong),
18+
("is_extended", ctypes.c_ubyte),
19+
("remote_req", ctypes.c_ubyte),
20+
("data_len", ctypes.c_ubyte),
21+
("data", CanData),
22+
]
23+
24+
25+
def check_status(result, function, arguments):
26+
if result > 0:
27+
raise IscanError(function, result, arguments)
28+
return result
29+
30+
31+
try:
32+
iscan = ctypes.cdll.LoadLibrary("iscandrv")
33+
except Exception as e:
34+
iscan = None
35+
logger.warning("Failed to load IS-CAN driver: %s", e)
36+
else:
37+
iscan.isCAN_DeviceInitEx.argtypes = [ctypes.c_ubyte, ctypes.c_ubyte]
38+
iscan.isCAN_DeviceInitEx.errcheck = check_status
39+
iscan.isCAN_DeviceInitEx.restype = ctypes.c_ubyte
40+
iscan.isCAN_ReceiveMessageEx.errcheck = check_status
41+
iscan.isCAN_ReceiveMessageEx.restype = ctypes.c_ubyte
42+
iscan.isCAN_TransmitMessageEx.errcheck = check_status
43+
iscan.isCAN_TransmitMessageEx.restype = ctypes.c_ubyte
44+
iscan.isCAN_CloseDevice.errcheck = check_status
45+
iscan.isCAN_CloseDevice.restype = ctypes.c_ubyte
46+
47+
48+
class IscanBus(BusABC):
49+
"""isCAN interface"""
50+
51+
BAUDRATES = {
52+
5000: 0,
53+
10000: 1,
54+
20000: 2,
55+
50000: 3,
56+
100000: 4,
57+
125000: 5,
58+
250000: 6,
59+
500000: 7,
60+
800000: 8,
61+
1000000: 9
62+
}
63+
64+
def __init__(self, channel, bitrate=500000, poll_interval=0.01, **kwargs):
65+
"""
66+
:param int channel:
67+
Device number
68+
:param int bitrate:
69+
Bitrate in bits/s
70+
:param float poll_interval:
71+
Poll interval in seconds when reading messages
72+
"""
73+
if iscan is None:
74+
raise ImportError("Could not load isCAN driver")
75+
self.channel = ctypes.c_ubyte(int(channel))
76+
self.channel_info = "IS-CAN: %s" % channel
77+
if bitrate not in self.BAUDRATES:
78+
valid_bitrates = ", ".join(str(bitrate) for bitrate in self.BAUDRATES)
79+
raise ValueError("Invalid bitrate, choose one of " + valid_bitrates)
80+
self.poll_interval = poll_interval
81+
iscan.isCAN_DeviceInitEx(self.channel, self.BAUDRATES[bitrate])
82+
super(IscanBus, self).__init__(channel, **kwargs)
83+
84+
def recv(self, timeout=None):
85+
raw_msg = MessageExStruct()
86+
end_time = time.time() + timeout if timeout is not None else None
87+
while True:
88+
try:
89+
iscan.isCAN_ReceiveMessageEx(self.channel, ctypes.byref(raw_msg))
90+
except IscanError as e:
91+
if e.error_code != 8:
92+
# An error occurred
93+
raise
94+
if end_time is not None and time.time() > end_time:
95+
# No message within timeout
96+
return None
97+
# Sleep a short time to avoid hammering
98+
time.sleep(self.poll_interval)
99+
else:
100+
# A message was received
101+
break
102+
return Message(arbitration_id=raw_msg.message_id,
103+
extended_id=bool(raw_msg.is_extended),
104+
timestamp=time.time(), # Better than nothing...
105+
is_remote_frame=bool(raw_msg.remote_req),
106+
dlc=raw_msg.data_len,
107+
data=raw_msg.data[:raw_msg.data_len])
108+
109+
def send(self, msg, timeout=None):
110+
raw_msg = MessageExStruct(msg.arbitration_id,
111+
bool(msg.is_extended_id),
112+
bool(msg.is_remote_frame),
113+
msg.dlc,
114+
CanData(*msg.data))
115+
iscan.isCAN_TransmitMessageEx(self.channel, ctypes.byref(raw_msg))
116+
117+
def shutdown(self):
118+
iscan.isCAN_CloseDevice(self.channel)
119+
120+
121+
class IscanError(CanError):
122+
123+
ERROR_CODES = {
124+
1: "No access to device",
125+
2: "Device with ID not found",
126+
3: "Driver operation failed",
127+
4: "Invalid parameter",
128+
5: "Operation allowed only in online state",
129+
6: "Device timeout",
130+
7: "Device is transmitting a message",
131+
8: "No message received",
132+
9: "Thread not started",
133+
10: "Thread already started",
134+
11: "Buffer overrun",
135+
12: "Device not initialized",
136+
16: "Bus error",
137+
17: "Bus off",
138+
18: "Error passive",
139+
19: "Data overrun",
140+
20: "Error warning",
141+
30: "Send error",
142+
31: "Transmission not acknowledged on bus",
143+
32: "Error critical bus",
144+
35: "Callbackthread is blocked, stopping thread failed",
145+
40: "Need a licence number under NT4"
146+
}
147+
148+
def __init__(self, function, error_code, arguments):
149+
super(IscanError, self).__init__()
150+
#: Status code
151+
self.error_code = error_code
152+
#: Function that failed
153+
self.function = function
154+
#: Arguments passed to function
155+
self.arguments = arguments
156+
157+
def __str__(self):
158+
description = self.ERROR_CODES.get(self.error_code,
159+
"Error code %d" % self.error_code)
160+
return "Function %s failed: %s" % (self.function.__name__, description)

doc/configuration.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ Lookup table of interface names:
8282
+---------------------+-------------------------------------+
8383
| ``"nican"`` | :doc:`interfaces/nican` |
8484
+---------------------+-------------------------------------+
85+
| ``"iscan"`` | :doc:`interfaces/iscan` |
86+
+---------------------+-------------------------------------+
8587
| ``"neovi"`` | :doc:`interfaces/neovi` |
8688
+---------------------+-------------------------------------+
8789
| ``"remote"`` | :doc:`interfaces/remote` |

doc/interfaces.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ The available interfaces are:
1818
interfaces/pcan
1919
interfaces/usb2can
2020
interfaces/nican
21+
interfaces/iscan
2122
interfaces/neovi
2223
interfaces/remote
2324
interfaces/virtual

doc/interfaces/iscan.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
isCAN
2+
=====
3+
4+
Interface for isCAN from `Thorsis Technologies GmbH`_, former ifak system GmbH.
5+
6+
7+
Bus
8+
---
9+
10+
.. autoclass:: can.interfaces.iscan.IscanBus
11+
12+
.. autoexception:: can.interfaces.iscan.IscanError
13+
14+
15+
.. _Thorsis Technologies GmbH: https://www.thorsis.com/en/industrial-automation/usb-interfaces/can/iscan-usb-interface/

0 commit comments

Comments
 (0)