Skip to content

Commit 9507e30

Browse files
Merge branch 'vector-interface' into develop
2 parents abb9f8f + 81c0edc commit 9507e30

9 files changed

Lines changed: 390 additions & 2 deletions

File tree

can/interface.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
'iscan': ('can.interfaces.iscan', 'IscanBus'),
2020
'remote': ('can.interfaces.remote', 'RemoteBus'),
2121
'virtual': ('can.interfaces.virtual', 'VirtualBus'),
22-
'neovi': ('can.interfaces.neovi_api', 'NeoVIBus')
22+
'neovi': ('can.interfaces.neovi_api', 'NeoVIBus'),
23+
'vector': ('can.interfaces.vector', 'VectorBus')
2324
}
2425

2526

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', 'iscan', 'remote', 'virtual', 'neovi'])
8+
'nican', 'iscan', 'vector', 'remote', 'virtual', 'neovi'])

can/interfaces/vector/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .canlib import VectorBus
2+
from .exceptions import VectorError

can/interfaces/vector/canlib.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Ctypes wrapper module for Vector CAN Interface on win32/win64 systems
4+
Authors: Julien Grave <grave.jul@gmail.com>, Christian Sandberg
5+
"""
6+
# Import Standard Python Modules
7+
# ==============================
8+
import ctypes
9+
import logging
10+
import sys
11+
import time
12+
13+
# Import Modules
14+
# ==============
15+
from can import BusABC, Message
16+
from .exceptions import VectorError
17+
18+
# Define Module Logger
19+
# ====================
20+
LOG = logging.getLogger(__name__)
21+
22+
# Import safely Vector API module for Travis tests
23+
vxlapi = None
24+
try:
25+
from . import vxlapi
26+
except Exception as exc:
27+
LOG.warning('Could not import vxlapi: %s', exc)
28+
29+
30+
class VectorBus(BusABC):
31+
"""The CAN Bus implemented for the Vector interface."""
32+
33+
def __init__(self, channel, can_filters=None, poll_interval=0.01,
34+
bitrate=None, rx_queue_size=256, app_name="CANalyzer", **config):
35+
"""
36+
:param list channel:
37+
The channel indexes to create this bus with.
38+
Can also be a single integer or a comma separated string.
39+
:param float poll_interval:
40+
Poll interval in seconds.
41+
:param int bitrate:
42+
Bitrate in bits/s.
43+
:param int rx_queue_size:
44+
Number of messages in receive queue.
45+
:param str app_name:
46+
Name of application in Hardware Config.
47+
"""
48+
if vxlapi is None:
49+
raise ImportError("The Vector API has not been loaded")
50+
self.poll_interval = poll_interval
51+
if isinstance(channel, (list, tuple)):
52+
self.channels = channel
53+
elif isinstance(channel, int):
54+
self.channels = [channel]
55+
else:
56+
# Assume comma separated string of channels
57+
self.channels = [int(ch.strip()) for ch in channel.split(',')]
58+
self._app_name = app_name.encode()
59+
self.channel_info = 'Application %s: %s' % (
60+
app_name, ', '.join('CAN %d' % (ch + 1) for ch in self.channels))
61+
62+
vxlapi.xlOpenDriver()
63+
self.port_handle = vxlapi.XLportHandle(vxlapi.XL_INVALID_PORTHANDLE)
64+
self.mask = 0
65+
# Get channels masks
66+
for channel in self.channels:
67+
hw_type = ctypes.c_uint(0)
68+
hw_index = ctypes.c_uint(0)
69+
hw_channel = ctypes.c_uint(0)
70+
vxlapi.xlGetApplConfig(self._app_name, channel, hw_type, hw_index,
71+
hw_channel, vxlapi.XL_BUS_TYPE_CAN)
72+
LOG.debug('Channel index %d found', channel)
73+
mask = vxlapi.xlGetChannelMask(hw_type.value, hw_index.value,
74+
hw_channel.value)
75+
LOG.debug('Channel %d, Type: %d, Mask: %d',
76+
hw_channel.value, hw_type.value, mask)
77+
self.mask |= mask
78+
permission_mask = vxlapi.XLaccess()
79+
vxlapi.xlOpenPort(self.port_handle, self._app_name, self.mask,
80+
permission_mask, rx_queue_size,
81+
vxlapi.XL_INTERFACE_VERSION, vxlapi.XL_BUS_TYPE_CAN)
82+
LOG.debug(
83+
'Open Port: PortHandle: %d, PermissionMask: 0x%X',
84+
self.port_handle.value, permission_mask.value)
85+
if bitrate:
86+
if permission_mask.value != self.mask:
87+
LOG.warning('Can not set bitrate since no init access')
88+
vxlapi.xlCanSetChannelBitrate(self.port_handle, permission_mask, bitrate)
89+
self.set_filters(can_filters)
90+
try:
91+
vxlapi.xlActivateChannel(self.port_handle, self.mask,
92+
vxlapi.XL_BUS_TYPE_CAN, 0)
93+
except VectorError:
94+
self.shutdown()
95+
raise
96+
# Calculate time offset for absolute timestamps
97+
offset = vxlapi.XLuint64()
98+
vxlapi.xlGetSyncTime(self.port_handle, offset)
99+
self._time_offset = time.time() - offset.value / 1000000000.0
100+
super(VectorBus, self).__init__()
101+
102+
def set_filters(self, can_filters=None):
103+
if can_filters:
104+
# Only one filter per ID type allowed
105+
if len(can_filters) == 1 or (
106+
len(can_filters) == 2 and
107+
can_filters[0].get("extended") != can_filters[1].get("extended")):
108+
for can_filter in can_filters:
109+
try:
110+
vxlapi.xlCanSetChannelAcceptance(
111+
self.port_handle, self.mask,
112+
can_filter["can_id"], can_filter["can_mask"],
113+
vxlapi.XL_CAN_EXT if can_filter.get("extended") else vxlapi.XL_CAN_STD)
114+
except VectorError as exc:
115+
LOG.warning("Could not set filters: %s", exc)
116+
else:
117+
LOG.warning("Only one filter per extended or standard ID allowed")
118+
119+
def recv(self, timeout=None):
120+
end_time = time.time() + timeout if timeout is not None else None
121+
event = vxlapi.XLevent(0)
122+
while True:
123+
event_count = ctypes.c_uint(1)
124+
try:
125+
vxlapi.xlReceive(self.port_handle, event_count, event)
126+
except VectorError as exc:
127+
if exc.error_code != vxlapi.XL_ERR_QUEUE_IS_EMPTY:
128+
raise
129+
else:
130+
if event.tag == vxlapi.XL_RECEIVE_MSG:
131+
msg_id = event.tagData.msg.id
132+
dlc = event.tagData.msg.dlc
133+
flags = event.tagData.msg.flags
134+
timestamp = event.timeStamp / 1000000000.0
135+
msg = Message(
136+
timestamp=timestamp + self._time_offset,
137+
arbitration_id=msg_id & 0x1FFFFFFF,
138+
extended_id=bool(msg_id & vxlapi.XL_CAN_EXT_MSG_ID),
139+
is_remote_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME),
140+
is_error_frame=bool(flags & vxlapi.XL_CAN_MSG_FLAG_ERROR_FRAME),
141+
dlc=dlc,
142+
data=event.tagData.msg.data[:dlc])
143+
msg.channel = event.chanIndex
144+
return msg
145+
if end_time is not None and time.time() > end_time:
146+
return None
147+
time.sleep(self.poll_interval)
148+
149+
def send(self, msg, timeout=None):
150+
message_count = ctypes.c_uint(1)
151+
msg_id = msg.arbitration_id
152+
if msg.id_type:
153+
msg_id |= vxlapi.XL_CAN_EXT_MSG_ID
154+
flags = 0
155+
if msg.is_remote_frame:
156+
flags |= vxlapi.XL_CAN_MSG_FLAG_REMOTE_FRAME
157+
xl_event = vxlapi.XLevent()
158+
xl_event.tag = vxlapi.XL_TRANSMIT_MSG
159+
xl_event.tagData.msg.id = msg_id
160+
xl_event.tagData.msg.dlc = msg.dlc
161+
xl_event.tagData.msg.flags = flags
162+
for idx, value in enumerate(msg.data):
163+
xl_event.tagData.msg.data[idx] = value
164+
vxlapi.xlCanTransmit(self.port_handle, self.mask, message_count, xl_event)
165+
166+
def flush_tx_buffer(self):
167+
vxlapi.xlCanFlushTransmitQueue(self.port_handle, self.mask)
168+
169+
def shutdown(self):
170+
vxlapi.xlDeactivateChannel(self.port_handle, self.mask)
171+
vxlapi.xlClosePort(self.port_handle)
172+
vxlapi.xlCloseDriver()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
from can import CanError
2+
3+
4+
class VectorError(CanError):
5+
6+
def __init__(self, error_code, error_string):
7+
self.error_code = error_code
8+
super(VectorError, self).__init__(error_string)

can/interfaces/vector/vxlapi.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Ctypes wrapper module for Vector CAN Interface on win32/win64 systems
4+
Authors: Julien Grave <grave.jul@gmail.com>, Christian Sandberg
5+
"""
6+
# Import Standard Python Modules
7+
# ==============================
8+
import ctypes
9+
import logging
10+
import platform
11+
from .exceptions import VectorError
12+
13+
# Define Module Logger
14+
# ====================
15+
LOG = logging.getLogger(__name__)
16+
17+
# Vector XL API Definitions
18+
# =========================
19+
# Load Windows DLL
20+
DLL_NAME = 'vxlapi64' if platform.architecture()[0] == '64bit' else 'vxlapi'
21+
_xlapi_dll = ctypes.windll.LoadLibrary(DLL_NAME)
22+
23+
XL_BUS_TYPE_CAN = 0x00000001
24+
25+
XL_ERR_QUEUE_IS_EMPTY = 10
26+
27+
XL_RECEIVE_MSG = 1
28+
XL_TRANSMIT_MSG = 10
29+
30+
XL_CAN_EXT_MSG_ID = 0x80000000
31+
XL_CAN_MSG_FLAG_ERROR_FRAME = 0x01
32+
XL_CAN_MSG_FLAG_REMOTE_FRAME = 0x10
33+
34+
XL_CAN_STD = 1
35+
XL_CAN_EXT = 2
36+
37+
XLuint64 = ctypes.c_ulonglong
38+
XLaccess = XLuint64
39+
40+
MAX_MSG_LEN = 8
41+
42+
# current version
43+
XL_INTERFACE_VERSION = 3
44+
45+
# structure for XL_RECEIVE_MSG, XL_TRANSMIT_MSG
46+
class s_xl_can_msg(ctypes.Structure):
47+
_fields_ = [('id', ctypes.c_ulong), ('flags', ctypes.c_ushort),
48+
('dlc', ctypes.c_ushort), ('res1', XLuint64),
49+
('data', ctypes.c_ubyte * MAX_MSG_LEN), ('res2', XLuint64)]
50+
51+
# BASIC bus message structure
52+
class s_xl_tag_data(ctypes.Union):
53+
_fields_ = [('msg', s_xl_can_msg)]
54+
55+
56+
XLeventTag = ctypes.c_ubyte
57+
58+
class XLevent(ctypes.Structure):
59+
_fields_ = [('tag', XLeventTag), ('chanIndex', ctypes.c_ubyte),
60+
('transId', ctypes.c_ushort), ('portHandle', ctypes.c_ushort),
61+
('flags', ctypes.c_ubyte), ('reserved', ctypes.c_ubyte),
62+
('timeStamp', XLuint64), ('tagData', s_xl_tag_data)]
63+
64+
# driver status
65+
XLstatus = ctypes.c_short
66+
67+
# porthandle
68+
XL_INVALID_PORTHANDLE = (-1)
69+
XLportHandle = ctypes.c_long
70+
71+
72+
def check_status(result, function, arguments):
73+
if result > 0:
74+
raise VectorError(result, xlGetErrorString(result).decode())
75+
return result
76+
77+
78+
xlOpenDriver = _xlapi_dll.xlOpenDriver
79+
xlOpenDriver.argtypes = []
80+
xlOpenDriver.restype = XLstatus
81+
xlOpenDriver.errcheck = check_status
82+
83+
xlCloseDriver = _xlapi_dll.xlCloseDriver
84+
xlCloseDriver.argtypes = []
85+
xlCloseDriver.restype = XLstatus
86+
xlCloseDriver.errcheck = check_status
87+
88+
xlGetApplConfig = _xlapi_dll.xlGetApplConfig
89+
xlGetApplConfig.argtypes = [
90+
ctypes.c_char_p, ctypes.c_uint, ctypes.POINTER(ctypes.c_uint),
91+
ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(ctypes.c_uint), ctypes.c_uint
92+
]
93+
xlGetApplConfig.restype = XLstatus
94+
xlGetApplConfig.errcheck = check_status
95+
96+
xlGetChannelIndex = _xlapi_dll.xlGetChannelIndex
97+
xlGetChannelIndex.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
98+
xlGetChannelIndex.restype = ctypes.c_int
99+
100+
xlGetChannelMask = _xlapi_dll.xlGetChannelMask
101+
xlGetChannelMask.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
102+
xlGetChannelMask.restype = XLaccess
103+
104+
xlOpenPort = _xlapi_dll.xlOpenPort
105+
xlOpenPort.argtypes = [
106+
ctypes.POINTER(XLportHandle), ctypes.c_char_p, XLaccess,
107+
ctypes.POINTER(XLaccess), ctypes.c_uint, ctypes.c_uint, ctypes.c_uint
108+
]
109+
xlOpenPort.restype = XLstatus
110+
xlOpenPort.errcheck = check_status
111+
112+
xlGetSyncTime = _xlapi_dll.xlGetSyncTime
113+
xlGetSyncTime.argtypes = [XLportHandle, ctypes.POINTER(XLuint64)]
114+
xlGetSyncTime.restype = XLstatus
115+
xlGetSyncTime.errcheck = check_status
116+
117+
xlClosePort = _xlapi_dll.xlClosePort
118+
xlClosePort.argtypes = [XLportHandle]
119+
xlClosePort.restype = XLstatus
120+
xlClosePort.errcheck = check_status
121+
122+
xlActivateChannel = _xlapi_dll.xlActivateChannel
123+
xlActivateChannel.argtypes = [
124+
XLportHandle, XLaccess, ctypes.c_uint, ctypes.c_uint
125+
]
126+
xlActivateChannel.restype = XLstatus
127+
xlActivateChannel.errcheck = check_status
128+
129+
xlDeactivateChannel = _xlapi_dll.xlDeactivateChannel
130+
xlDeactivateChannel.argtypes = [XLportHandle, XLaccess]
131+
xlDeactivateChannel.restype = XLstatus
132+
xlDeactivateChannel.errcheck = check_status
133+
134+
xlReceive = _xlapi_dll.xlReceive
135+
xlReceive.argtypes = [
136+
XLportHandle, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent)
137+
]
138+
xlReceive.restype = XLstatus
139+
xlReceive.errcheck = check_status
140+
141+
xlGetErrorString = _xlapi_dll.xlGetErrorString
142+
xlGetErrorString.argtypes = [XLstatus]
143+
xlGetErrorString.restype = ctypes.c_char_p
144+
145+
xlCanSetChannelBitrate = _xlapi_dll.xlCanSetChannelBitrate
146+
xlCanSetChannelBitrate.argtypes = [XLportHandle, XLaccess, ctypes.c_ulong]
147+
xlCanSetChannelBitrate.restype = XLstatus
148+
xlCanSetChannelBitrate.errcheck = check_status
149+
150+
xlCanTransmit = _xlapi_dll.xlCanTransmit
151+
xlCanTransmit.argtypes = [
152+
XLportHandle, XLaccess, ctypes.POINTER(ctypes.c_uint), ctypes.POINTER(XLevent)
153+
]
154+
xlCanTransmit.restype = XLstatus
155+
xlCanTransmit.errcheck = check_status
156+
157+
xlCanFlushTransmitQueue = _xlapi_dll.xlCanFlushTransmitQueue
158+
xlCanFlushTransmitQueue.argtypes = [XLportHandle, XLaccess]
159+
xlCanFlushTransmitQueue.restype = XLstatus
160+
xlCanFlushTransmitQueue.errcheck = check_status
161+
162+
xlCanSetChannelAcceptance = _xlapi_dll.xlCanSetChannelAcceptance
163+
xlCanSetChannelAcceptance.argtypes = [
164+
XLportHandle, XLaccess, ctypes.c_ulong, ctypes.c_ulong, ctypes.c_uint]
165+
xlCanSetChannelAcceptance.restype = XLstatus
166+
xlCanSetChannelAcceptance.errcheck = check_status
167+
168+
xlCanResetAcceptance = _xlapi_dll.xlCanResetAcceptance
169+
xlCanResetAcceptance.argtypes = [XLportHandle, XLaccess, ctypes.c_uint]
170+
xlCanResetAcceptance.restype = XLstatus
171+
xlCanResetAcceptance.errcheck = check_status

doc/configuration.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ Lookup table of interface names:
8686
+---------------------+-------------------------------------+
8787
| ``"neovi"`` | :doc:`interfaces/neovi` |
8888
+---------------------+-------------------------------------+
89+
| ``"vector"`` | :doc:`interfaces/vector` |
90+
+---------------------+-------------------------------------+
8991
| ``"remote"`` | :doc:`interfaces/remote` |
9092
+---------------------+-------------------------------------+
9193
| ``"virtual"`` | :doc:`interfaces/virtual` |

doc/interfaces.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ The available interfaces are:
2020
interfaces/nican
2121
interfaces/iscan
2222
interfaces/neovi
23+
interfaces/vector
2324
interfaces/remote
2425
interfaces/virtual
2526

0 commit comments

Comments
 (0)