예제 #1
0
 def _send_periodic_internal(self, msg, period, duration=None):
     """Send a message using built-in cyclic transmit list functionality."""
     if self._scheduler is None:
         self._scheduler = HANDLE()
         _canlib.canSchedulerOpen(self._device_handle, self.channel,
                                  self._scheduler)
         caps = structures.CANCAPABILITIES()
         _canlib.canSchedulerGetCaps(self._scheduler, caps)
         self._scheduler_resolution = float(caps.dwClockFreq) / caps.dwCmsDivisor
         _canlib.canSchedulerActivate(self._scheduler, constants.TRUE)
     return CyclicSendTask(self._scheduler, msg, period, duration,
                           self._scheduler_resolution)
예제 #2
0
def get_ixxat_hwids():
    """Get a list of hardware ids of all available IXXAT devices."""
    hwids = []
    device_handle = HANDLE()
    device_info = structures.VCIDEVICEINFO()

    _canlib.vciEnumDeviceOpen(ctypes.byref(device_handle))
    while True:
        try:
            _canlib.vciEnumDeviceNext(device_handle, ctypes.byref(device_info))
        except StopIteration:
            break
        else:
            hwids.append(device_info.UniqueHardwareId.AsChar.decode("ascii"))
    _canlib.vciEnumDeviceClose(device_handle)

    return hwids
예제 #3
0
    def __init__(self, channel, can_filters=None, **config):
        """
        :param int channel:
            The Channel id to create this bus with.

        :param list can_filters:
            See :meth:`can.BusABC.set_filters`.

        :param bool receive_own_messages:
            Enable self-reception of sent messages.

        :param int UniqueHardwareId:
            UniqueHardwareId to connect (optional, will use the first found if not supplied)

        :param int bitrate:
            Channel bitrate in bit/s
        """
        if _canlib is None:
            raise ImportError("The IXXAT VCI library has not been initialized. Check the logs for more details.")
        log.info("CAN Filters: %s", can_filters)
        log.info("Got configuration of: %s", config)
        # Configuration options
        bitrate = config.get('bitrate', 500000)
        UniqueHardwareId = config.get('UniqueHardwareId', None)
        rxFifoSize = config.get('rxFifoSize', 16)
        txFifoSize = config.get('txFifoSize', 16)
        self._receive_own_messages = config.get('receive_own_messages', False)
        # Usually comes as a string from the config file
        channel = int(channel)

        if (bitrate not in self.CHANNEL_BITRATES[0]):
            raise ValueError("Invalid bitrate {}".format(bitrate))

        self._device_handle = HANDLE()
        self._device_info = structures.VCIDEVICEINFO()
        self._control_handle = HANDLE()
        self._channel_handle = HANDLE()
        self._channel_capabilities = structures.CANCAPABILITIES()
        self._message = structures.CANMSG()
        self._payload = (ctypes.c_byte * 8)()

        # Search for supplied device
        if UniqueHardwareId is None:
            log.info("Searching for first available device")
        else:
            log.info("Searching for unique HW ID %s", UniqueHardwareId)
        _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle))
        while True:
            try:
                _canlib.vciEnumDeviceNext(self._device_handle, ctypes.byref(self._device_info))
            except StopIteration:
                if (UniqueHardwareId is None):
                    raise VCIDeviceNotFoundError("No IXXAT device(s) connected or device(s) in use by other process(es).")
                else:
                    raise VCIDeviceNotFoundError("Unique HW ID {} not connected or not available.".format(UniqueHardwareId))
            else:
                if (UniqueHardwareId is None) or (self._device_info.UniqueHardwareId.AsChar == bytes(UniqueHardwareId, 'ascii')):
                    break
                else:
                    log.debug("Ignoring IXXAT with hardware id '%s'.", self._device_info.UniqueHardwareId.AsChar.decode("ascii"))
        _canlib.vciEnumDeviceClose(self._device_handle)
        _canlib.vciDeviceOpen(ctypes.byref(self._device_info.VciObjectId), ctypes.byref(self._device_handle))
        log.info("Using unique HW ID %s", self._device_info.UniqueHardwareId.AsChar)

        log.info("Initializing channel %d in shared mode, %d rx buffers, %d tx buffers", channel, rxFifoSize, txFifoSize)
        _canlib.canChannelOpen(self._device_handle, channel, constants.FALSE, ctypes.byref(self._channel_handle))
        # Signal TX/RX events when at least one frame has been handled
        _canlib.canChannelInitialize(self._channel_handle, rxFifoSize, 1, txFifoSize, 1)
        _canlib.canChannelActivate(self._channel_handle, constants.TRUE)

        log.info("Initializing control %d bitrate %d", channel, bitrate)
        _canlib.canControlOpen(self._device_handle, channel, ctypes.byref(self._control_handle))
        _canlib.canControlInitialize(
            self._control_handle,
            constants.CAN_OPMODE_STANDARD|constants.CAN_OPMODE_EXTENDED|constants.CAN_OPMODE_ERRFRAME,
            self.CHANNEL_BITRATES[0][bitrate],
            self.CHANNEL_BITRATES[1][bitrate]
        )
        _canlib.canControlGetCaps(self._control_handle, ctypes.byref(self._channel_capabilities))

        # With receive messages, this field contains the relative reception time of
        # the message in ticks. The resolution of a tick can be calculated from the fields
        # dwClockFreq and dwTscDivisor of the structure  CANCAPABILITIES in accordance with the following formula:
        # frequency [1/s] = dwClockFreq / dwTscDivisor
        # We explicitly cast to float for Python 2.x users
        self._tick_resolution =  float(self._channel_capabilities.dwClockFreq / self._channel_capabilities.dwTscDivisor)

        # Setup filters before starting the channel
        if can_filters:
            log.info("The IXXAT VCI backend is filtering messages")
            # Disable every message coming in
            for extended in (0, 1):
                _canlib.canControlSetAccFilter(self._control_handle,
                                               extended,
                                               constants.CAN_ACC_CODE_NONE,
                                               constants.CAN_ACC_MASK_NONE)
            for can_filter in can_filters:
                # Whitelist
                code = int(can_filter['can_id'])
                mask = int(can_filter['can_mask'])
                extended = can_filter.get('extended', False)
                _canlib.canControlAddFilterIds(self._control_handle,
                                               1 if extended else 0,
                                               code << 1,
                                               mask << 1)
                log.info("Accepting ID: 0x%X MASK: 0x%X", code, mask)

        # Start the CAN controller. Messages will be forwarded to the channel
        _canlib.canControlStart(self._control_handle, constants.TRUE)

        # For cyclic transmit list. Set when .send_periodic() is first called
        self._scheduler = None
        self._scheduler_resolution = None
        self.channel = channel

        # Usually you get back 3 messages like "CAN initialized" ecc...
        # Clear the FIFO by filter them out with low timeout
        for i in range(rxFifoSize):
            try:
                _canlib.canChannelReadMessage(self._channel_handle, 0, ctypes.byref(self._message))
            except (VCITimeout, VCIRxQueueEmptyError):
                break

        super(IXXATBus, self).__init__(channel=channel, can_filters=None, **config)
예제 #4
0
    def __init__(
        self,
        channel: int,
        can_filters=None,
        receive_own_messages: bool = False,
        unique_hardware_id: Optional[int] = None,
        extended: bool = True,
        rx_fifo_size: int = 16,
        tx_fifo_size: int = 16,
        bitrate: int = 500000,
        **kwargs,
    ):
        """
        :param channel:
            The Channel id to create this bus with.

        :param can_filters:
            See :meth:`can.BusABC.set_filters`.

        :param receive_own_messages:
            Enable self-reception of sent messages.

        :param unique_hardware_id:
            unique_hardware_id to connect (optional, will use the first found if not supplied)

        :param extended:
            Default True, enables the capability to use extended IDs.

        :param rx_fifo_size:
            Receive fifo size (default 16)

        :param tx_fifo_size:
            Transmit fifo size (default 16)

        :param bitrate:
            Channel bitrate in bit/s
        """
        if _canlib is None:
            raise CanInterfaceNotImplementedError(
                "The IXXAT VCI library has not been initialized. Check the logs for more details."
            )
        log.info("CAN Filters: %s", can_filters)
        # Configuration options
        self._receive_own_messages = receive_own_messages
        # Usually comes as a string from the config file
        channel = int(channel)

        if bitrate not in self.CHANNEL_BITRATES[0]:
            raise ValueError("Invalid bitrate {}".format(bitrate))

        if rx_fifo_size <= 0:
            raise ValueError("rx_fifo_size must be > 0")

        if tx_fifo_size <= 0:
            raise ValueError("tx_fifo_size must be > 0")

        if channel < 0:
            raise ValueError("channel number must be >= 0")

        self._device_handle = HANDLE()
        self._device_info = structures.VCIDEVICEINFO()
        self._control_handle = HANDLE()
        self._channel_handle = HANDLE()
        self._channel_capabilities = structures.CANCAPABILITIES()
        self._message = structures.CANMSG()
        self._payload = (ctypes.c_byte * 8)()

        # Search for supplied device
        if unique_hardware_id is None:
            log.info("Searching for first available device")
        else:
            log.info("Searching for unique HW ID %s", unique_hardware_id)
        _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle))
        while True:
            try:
                _canlib.vciEnumDeviceNext(
                    self._device_handle, ctypes.byref(self._device_info)
                )
            except StopIteration:
                if unique_hardware_id is None:
                    raise VCIDeviceNotFoundError(
                        "No IXXAT device(s) connected or device(s) in use by other process(es)."
                    )
                else:
                    raise VCIDeviceNotFoundError(
                        "Unique HW ID {} not connected or not available.".format(
                            unique_hardware_id
                        )
                    )
            else:
                if (unique_hardware_id is None) or (
                    self._device_info.UniqueHardwareId.AsChar
                    == bytes(unique_hardware_id, "ascii")
                ):
                    break
                else:
                    log.debug(
                        "Ignoring IXXAT with hardware id '%s'.",
                        self._device_info.UniqueHardwareId.AsChar.decode("ascii"),
                    )
        _canlib.vciEnumDeviceClose(self._device_handle)

        try:
            _canlib.vciDeviceOpen(
                ctypes.byref(self._device_info.VciObjectId),
                ctypes.byref(self._device_handle),
            )
        except Exception as exception:
            raise CanInitializationError(f"Could not open device: {exception}")

        log.info("Using unique HW ID %s", self._device_info.UniqueHardwareId.AsChar)

        log.info(
            "Initializing channel %d in shared mode, %d rx buffers, %d tx buffers",
            channel,
            rx_fifo_size,
            tx_fifo_size,
        )

        try:
            _canlib.canChannelOpen(
                self._device_handle,
                channel,
                constants.FALSE,
                ctypes.byref(self._channel_handle),
            )
        except Exception as exception:
            raise CanInitializationError(
                f"Could not open and initialize channel: {exception}"
            )

        # Signal TX/RX events when at least one frame has been handled
        _canlib.canChannelInitialize(
            self._channel_handle, rx_fifo_size, 1, tx_fifo_size, 1
        )
        _canlib.canChannelActivate(self._channel_handle, constants.TRUE)

        log.info("Initializing control %d bitrate %d", channel, bitrate)
        _canlib.canControlOpen(
            self._device_handle, channel, ctypes.byref(self._control_handle)
        )

        # compute opmode before control initialize
        opmode = constants.CAN_OPMODE_STANDARD | constants.CAN_OPMODE_ERRFRAME
        if extended:
            opmode |= constants.CAN_OPMODE_EXTENDED

        # control initialize
        _canlib.canControlInitialize(
            self._control_handle,
            opmode,
            self.CHANNEL_BITRATES[0][bitrate],
            self.CHANNEL_BITRATES[1][bitrate],
        )
        _canlib.canControlGetCaps(
            self._control_handle, ctypes.byref(self._channel_capabilities)
        )

        # With receive messages, this field contains the relative reception time of
        # the message in ticks. The resolution of a tick can be calculated from the fields
        # dwClockFreq and dwTscDivisor of the structure  CANCAPABILITIES in accordance with the following formula:
        # frequency [1/s] = dwClockFreq / dwTscDivisor
        self._tick_resolution = (
            self._channel_capabilities.dwClockFreq
            / self._channel_capabilities.dwTscDivisor
        )

        # Setup filters before starting the channel
        if can_filters:
            log.info("The IXXAT VCI backend is filtering messages")
            # Disable every message coming in
            for extended in (0, 1):
                _canlib.canControlSetAccFilter(
                    self._control_handle,
                    extended,
                    constants.CAN_ACC_CODE_NONE,
                    constants.CAN_ACC_MASK_NONE,
                )
            for can_filter in can_filters:
                # Filters define what messages are accepted
                code = int(can_filter["can_id"])
                mask = int(can_filter["can_mask"])
                extended = can_filter.get("extended", False)
                _canlib.canControlAddFilterIds(
                    self._control_handle, 1 if extended else 0, code << 1, mask << 1
                )
                log.info("Accepting ID: 0x%X MASK: 0x%X", code, mask)

        # Start the CAN controller. Messages will be forwarded to the channel
        _canlib.canControlStart(self._control_handle, constants.TRUE)

        # For cyclic transmit list. Set when .send_periodic() is first called
        self._scheduler = None
        self._scheduler_resolution = None
        self.channel = channel

        # Usually you get back 3 messages like "CAN initialized" ecc...
        # Clear the FIFO by filter them out with low timeout
        for _ in range(rx_fifo_size):
            try:
                _canlib.canChannelReadMessage(
                    self._channel_handle, 0, ctypes.byref(self._message)
                )
            except (VCITimeout, VCIRxQueueEmptyError):
                break

        super().__init__(channel=channel, can_filters=None, **kwargs)
예제 #5
0
    def __init__(
        self,
        channel: int,
        can_filters=None,
        receive_own_messages: int = False,
        unique_hardware_id: Optional[int] = None,
        extended: bool = True,
        rx_fifo_size: int = 1024,
        tx_fifo_size: int = 128,
        bitrate: int = 500000,
        data_bitrate: int = 2000000,
        sjw_abr: int = None,
        tseg1_abr: int = None,
        tseg2_abr: int = None,
        sjw_dbr: int = None,
        tseg1_dbr: int = None,
        tseg2_dbr: int = None,
        ssp_dbr: int = None,
        **kwargs,
    ):
        """
        :param channel:
            The Channel id to create this bus with.

        :param can_filters:
            See :meth:`can.BusABC.set_filters`.

        :param receive_own_messages:
            Enable self-reception of sent messages.

        :param unique_hardware_id:
            unique_hardware_id to connect (optional, will use the first found if not supplied)

        :param extended:
            Default True, enables the capability to use extended IDs.

        :param rx_fifo_size:
            Receive fifo size (default 1024)

        :param tx_fifo_size:
            Transmit fifo size (default 128)

        :param bitrate:
            Channel bitrate in bit/s

        :param data_bitrate:
            Channel bitrate in bit/s (only in CAN-Fd if baudrate switch enabled).

        :param sjw_abr:
            Bus timing value sample jump width (arbitration).

        :param tseg1_abr:
            Bus timing value tseg1 (arbitration)

        :param tseg2_abr:
            Bus timing value tseg2 (arbitration)

        :param sjw_dbr:
            Bus timing value sample jump width (data)

        :param tseg1_dbr:
            Bus timing value tseg1 (data). Only takes effect with fd and bitrate switch enabled.

        :param tseg2_dbr:
            Bus timing value tseg2 (data). Only takes effect with fd and bitrate switch enabled.

        :param ssp_dbr:
            Secondary sample point (data). Only takes effect with fd and bitrate switch enabled.

        """
        if _canlib is None:
            raise CanInterfaceNotImplementedError(
                "The IXXAT VCI library has not been initialized. Check the logs for more details."
            )
        log.info("CAN Filters: %s", can_filters)
        # Configuration options
        self._receive_own_messages = receive_own_messages
        # Usually comes as a string from the config file
        channel = int(channel)

        if bitrate not in constants.CAN_BITRATE_PRESETS and (
                tseg1_abr is None or tseg2_abr is None or sjw_abr is None):
            raise ValueError(
                "To use bitrate {} (that has not predefined preset) is mandatory to use also parameters tseg1_abr, tseg2_abr and swj_abr"
                .format(bitrate))
        if data_bitrate not in constants.CAN_DATABITRATE_PRESETS and (
                tseg1_dbr is None or tseg2_dbr is None or sjw_dbr is None):
            raise ValueError(
                "To use data_bitrate {} (that has not predefined preset) is mandatory to use also parameters tseg1_dbr, tseg2_dbr and swj_dbr"
                .format(data_bitrate))

        if rx_fifo_size <= 0:
            raise ValueError("rx_fifo_size must be > 0")

        if tx_fifo_size <= 0:
            raise ValueError("tx_fifo_size must be > 0")

        if channel < 0:
            raise ValueError("channel number must be >= 0")

        self._device_handle = HANDLE()
        self._device_info = structures.VCIDEVICEINFO()
        self._control_handle = HANDLE()
        self._channel_handle = HANDLE()
        self._channel_capabilities = structures.CANCAPABILITIES2()
        self._message = structures.CANMSG2()
        self._payload = (ctypes.c_byte * 64)()

        # Search for supplied device
        if unique_hardware_id is None:
            log.info("Searching for first available device")
        else:
            log.info("Searching for unique HW ID %s", unique_hardware_id)
        _canlib.vciEnumDeviceOpen(ctypes.byref(self._device_handle))
        while True:
            try:
                _canlib.vciEnumDeviceNext(self._device_handle,
                                          ctypes.byref(self._device_info))
            except StopIteration:
                if unique_hardware_id is None:
                    raise VCIDeviceNotFoundError(
                        "No IXXAT device(s) connected or device(s) in use by other process(es)."
                    )
                else:
                    raise VCIDeviceNotFoundError(
                        "Unique HW ID {} not connected or not available.".
                        format(unique_hardware_id))
            else:
                if (unique_hardware_id is
                        None) or (self._device_info.UniqueHardwareId.AsChar
                                  == bytes(unique_hardware_id, "ascii")):
                    break
                else:
                    log.debug(
                        "Ignoring IXXAT with hardware id '%s'.",
                        self._device_info.UniqueHardwareId.AsChar.decode(
                            "ascii"),
                    )
        _canlib.vciEnumDeviceClose(self._device_handle)

        try:
            _canlib.vciDeviceOpen(
                ctypes.byref(self._device_info.VciObjectId),
                ctypes.byref(self._device_handle),
            )
        except Exception as exception:
            raise CanInitializationError(f"Could not open device: {exception}")

        log.info("Using unique HW ID %s",
                 self._device_info.UniqueHardwareId.AsChar)

        log.info(
            "Initializing channel %d in shared mode, %d rx buffers, %d tx buffers",
            channel,
            rx_fifo_size,
            tx_fifo_size,
        )

        try:
            _canlib.canChannelOpen(
                self._device_handle,
                channel,
                constants.FALSE,
                ctypes.byref(self._channel_handle),
            )
        except Exception as exception:
            raise CanInitializationError(
                f"Could not open and initialize channel: {exception}")

        # Signal TX/RX events when at least one frame has been handled
        _canlib.canChannelInitialize(
            self._channel_handle,
            rx_fifo_size,
            1,
            tx_fifo_size,
            1,
            0,
            constants.CAN_FILTER_PASS,
        )
        _canlib.canChannelActivate(self._channel_handle, constants.TRUE)

        pBtpSDR = IXXATBus._canptb_build(
            defaults=constants.CAN_BITRATE_PRESETS,
            bitrate=bitrate,
            tseg1=tseg1_abr,
            tseg2=tseg2_abr,
            sjw=sjw_abr,
            ssp=0,
        )
        pBtpFDR = IXXATBus._canptb_build(
            defaults=constants.CAN_DATABITRATE_PRESETS,
            bitrate=data_bitrate,
            tseg1=tseg1_dbr,
            tseg2=tseg2_dbr,
            sjw=sjw_dbr,
            ssp=ssp_dbr if ssp_dbr is not None else tseg1_dbr,
        )

        log.info(
            "Initializing control %d with SDR={%s}, FDR={%s}",
            channel,
            pBtpSDR,
            pBtpFDR,
        )
        _canlib.canControlOpen(self._device_handle, channel,
                               ctypes.byref(self._control_handle))

        _canlib.canControlGetCaps(self._control_handle,
                                  ctypes.byref(self._channel_capabilities))

        # check capabilities
        bOpMode = constants.CAN_OPMODE_UNDEFINED
        if (self._channel_capabilities.dwFeatures
                & constants.CAN_FEATURE_STDANDEXT) != 0:
            # controller supportes CAN_OPMODE_STANDARD and CAN_OPMODE_EXTENDED at the same time
            bOpMode |= constants.CAN_OPMODE_STANDARD  # enable both 11 bits reception
            if extended:  # parameter from configuration
                bOpMode |= constants.CAN_OPMODE_EXTENDED  # enable 29 bits reception
        elif (self._channel_capabilities.dwFeatures
              & constants.CAN_FEATURE_STDANDEXT) != 0:
            log.warning(
                "Channel %d capabilities allow either basic or extended IDs, but not both. using %s according to parameter [extended=%s]",
                channel,
                "extended" if extended else "basic",
                "True" if extended else "False",
            )
            bOpMode |= (constants.CAN_OPMODE_EXTENDED
                        if extended else constants.CAN_OPMODE_STANDARD)

        if (self._channel_capabilities.dwFeatures
                & constants.CAN_FEATURE_ERRFRAME) != 0:
            bOpMode |= constants.CAN_OPMODE_ERRFRAME

        bExMode = constants.CAN_EXMODE_DISABLED
        if (self._channel_capabilities.dwFeatures
                & constants.CAN_FEATURE_EXTDATA) != 0:
            bExMode |= constants.CAN_EXMODE_EXTDATALEN

        if (self._channel_capabilities.dwFeatures
                & constants.CAN_FEATURE_FASTDATA) != 0:
            bExMode |= constants.CAN_EXMODE_FASTDATA

        _canlib.canControlInitialize(
            self._control_handle,
            bOpMode,
            bExMode,
            constants.CAN_FILTER_PASS,
            constants.CAN_FILTER_PASS,
            0,
            0,
            ctypes.byref(pBtpSDR),
            ctypes.byref(pBtpFDR),
        )

        # With receive messages, this field contains the relative reception time of
        # the message in ticks. The resolution of a tick can be calculated from the fields
        # dwClockFreq and dwTscDivisor of the structure  CANCAPABILITIES in accordance with the following formula:
        # frequency [1/s] = dwClockFreq / dwTscDivisor
        self._tick_resolution = (self._channel_capabilities.dwTscClkFreq /
                                 self._channel_capabilities.dwTscDivisor)

        # Setup filters before starting the channel
        if can_filters:
            log.info("The IXXAT VCI backend is filtering messages")
            # Disable every message coming in
            for extended in (0, 1):
                _canlib.canControlSetAccFilter(
                    self._control_handle,
                    extended,
                    constants.CAN_ACC_CODE_NONE,
                    constants.CAN_ACC_MASK_NONE,
                )
            for can_filter in can_filters:
                # Filters define what messages are accepted
                code = int(can_filter["can_id"])
                mask = int(can_filter["can_mask"])
                extended = can_filter.get("extended", False)
                _canlib.canControlAddFilterIds(self._control_handle,
                                               1 if extended else 0, code << 1,
                                               mask << 1)
                log.info("Accepting ID: 0x%X MASK: 0x%X", code, mask)

        # Start the CAN controller. Messages will be forwarded to the channel
        _canlib.canControlStart(self._control_handle, constants.TRUE)

        # For cyclic transmit list. Set when .send_periodic() is first called
        self._scheduler = None
        self._scheduler_resolution = None
        self.channel = channel

        # Usually you get back 3 messages like "CAN initialized" ecc...
        # Clear the FIFO by filter them out with low timeout
        for _ in range(rx_fifo_size):
            try:
                _canlib.canChannelReadMessage(self._channel_handle, 0,
                                              ctypes.byref(self._message))
            except (VCITimeout, VCIRxQueueEmptyError):
                break

        super().__init__(channel=channel, can_filters=None, **kwargs)