diff --git a/can/interfaces/vector/canlib.py b/can/interfaces/vector/canlib.py index 19e2c78da..07a77e1c1 100644 --- a/can/interfaces/vector/canlib.py +++ b/can/interfaces/vector/canlib.py @@ -10,6 +10,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 @@ -31,6 +44,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: @@ -88,19 +102,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): @@ -123,8 +151,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: @@ -135,7 +164,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, @@ -146,9 +175,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 64fef0824..808ad77f1 100644 --- a/can/interfaces/vector/exceptions.py +++ b/can/interfaces/vector/exceptions.py @@ -3,6 +3,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 5b49faca8..bd7751fb7 100644 --- a/can/interfaces/vector/vxlapi.py +++ b/can/interfaces/vector/vxlapi.py @@ -30,12 +30,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 @@ -71,7 +73,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 @@ -119,6 +121,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 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/