|  | 
|  | 1 | +from ctypes import * | 
|  | 2 | +import logging | 
|  | 3 | +import platform | 
|  | 4 | +from can import BusABC, Message | 
|  | 5 | + | 
|  | 6 | +logger = logging.getLogger(__name__) | 
|  | 7 | + | 
|  | 8 | + | 
|  | 9 | +class VCI_INIT_CONFIG(Structure): | 
|  | 10 | +    _fields_ = [("AccCode", c_int32), | 
|  | 11 | +                ("AccMask", c_int32), | 
|  | 12 | +                ("Reserved", c_int32), | 
|  | 13 | +                ("Filter", c_ubyte), | 
|  | 14 | +                ("Timing0", c_ubyte), | 
|  | 15 | +                ("Timing1", c_ubyte), | 
|  | 16 | +                ("Mode", c_ubyte)] | 
|  | 17 | + | 
|  | 18 | + | 
|  | 19 | +class VCI_CAN_OBJ(Structure): | 
|  | 20 | +    _fields_ = [("ID", c_uint), | 
|  | 21 | +                ("TimeStamp", c_int), | 
|  | 22 | +                ("TimeFlag", c_byte), | 
|  | 23 | +                ("SendType", c_byte), | 
|  | 24 | +                ("RemoteFlag", c_byte), | 
|  | 25 | +                ("ExternFlag", c_byte), | 
|  | 26 | +                ("DataLen", c_byte), | 
|  | 27 | +                ("Data", c_ubyte * 8), | 
|  | 28 | +                ("Reserved", c_byte * 3)] | 
|  | 29 | + | 
|  | 30 | + | 
|  | 31 | +VCI_USBCAN2 = 4 | 
|  | 32 | + | 
|  | 33 | +STATUS_OK = 0x01 | 
|  | 34 | +STATUS_ERR = 0x00 | 
|  | 35 | + | 
|  | 36 | +TIMING_DICT = { | 
|  | 37 | +    5000: (0xBF, 0xFF), | 
|  | 38 | +    10000: (0x31, 0x1C), | 
|  | 39 | +    20000: (0x18, 0x1C), | 
|  | 40 | +    33330: (0x09, 0x6F), | 
|  | 41 | +    40000: (0x87, 0xFF), | 
|  | 42 | +    50000: (0x09, 0x1C), | 
|  | 43 | +    66660: (0x04, 0x6F), | 
|  | 44 | +    80000: (0x83, 0xFF), | 
|  | 45 | +    83330: (0x03, 0x6F), | 
|  | 46 | +    100000: (0x04, 0x1C), | 
|  | 47 | +    125000: (0x03, 0x1C), | 
|  | 48 | +    200000: (0x81, 0xFA), | 
|  | 49 | +    250000: (0x01, 0x1C), | 
|  | 50 | +    400000: (0x80, 0xFA), | 
|  | 51 | +    500000: (0x00, 0x1C), | 
|  | 52 | +    666000: (0x80, 0xB6), | 
|  | 53 | +    800000: (0x00, 0x16), | 
|  | 54 | +    1000000: (0x00, 0x14), | 
|  | 55 | +} | 
|  | 56 | + | 
|  | 57 | +try: | 
|  | 58 | +    if platform.system() == "Windows": | 
|  | 59 | +        CANalystII = WinDLL("./ControlCAN.dll") | 
|  | 60 | +    else: | 
|  | 61 | +        CANalystII = CDLL("./libcontrolcan.so") | 
|  | 62 | +    logger.info("Loaded CANalystII library") | 
|  | 63 | +except OSError as e: | 
|  | 64 | +    CANalystII = None | 
|  | 65 | +    logger.info("Cannot load CANalystII library") | 
|  | 66 | + | 
|  | 67 | + | 
|  | 68 | +class CANalystIIBus(BusABC): | 
|  | 69 | +    def __init__(self, channel, device=0, baud=None, Timing0=None, Timing1=None, can_filters=None): | 
|  | 70 | +        """ | 
|  | 71 | +
 | 
|  | 72 | +        :param channel: channel number | 
|  | 73 | +        :param device: device number | 
|  | 74 | +        :param baud: baud rate | 
|  | 75 | +        :param Timing0: customize the timing register if baudrate is not specified | 
|  | 76 | +        :param Timing1: | 
|  | 77 | +        :param can_filters: filters for packet | 
|  | 78 | +        """ | 
|  | 79 | +        super(CANalystIIBus, self).__init__(channel, can_filters) | 
|  | 80 | + | 
|  | 81 | +        if isinstance(channel, (list, tuple)): | 
|  | 82 | +            self.channels = channel | 
|  | 83 | +        elif isinstance(channel, int): | 
|  | 84 | +            self.channels = [channel] | 
|  | 85 | +        else: | 
|  | 86 | +            # Assume comma separated string of channels | 
|  | 87 | +            self.channels = [int(ch.strip()) for ch in channel.split(',')] | 
|  | 88 | + | 
|  | 89 | +        self.device = device | 
|  | 90 | + | 
|  | 91 | +        self.channel_info = "CANalyst-II: device {}, channels {}".format(self.device, self.channels) | 
|  | 92 | + | 
|  | 93 | +        if baud is not None: | 
|  | 94 | +            try: | 
|  | 95 | +                Timing0, Timing1 = TIMING_DICT[baud] | 
|  | 96 | +            except KeyError: | 
|  | 97 | +                raise ValueError("Baudrate is not supported") | 
|  | 98 | + | 
|  | 99 | +        if Timing0 is None or Timing1 is None: | 
|  | 100 | +            raise ValueError("Timing registers are not set") | 
|  | 101 | + | 
|  | 102 | +        self.init_config = VCI_INIT_CONFIG(0, 0xFFFFFFFF, 0, 1, Timing0, Timing1, 0) | 
|  | 103 | + | 
|  | 104 | +        if CANalystII.VCI_OpenDevice(VCI_USBCAN2, self.device, 0) == STATUS_ERR: | 
|  | 105 | +            logger.error("VCI_OpenDevice Error") | 
|  | 106 | + | 
|  | 107 | +        for channel in self.channels: | 
|  | 108 | +            if CANalystII.VCI_InitCAN(VCI_USBCAN2, self.device, channel, byref(self.init_config)) == STATUS_ERR: | 
|  | 109 | +                logger.error("VCI_InitCAN Error") | 
|  | 110 | +                self.shutdown() | 
|  | 111 | +                return | 
|  | 112 | + | 
|  | 113 | +            if CANalystII.VCI_StartCAN(VCI_USBCAN2, self.device, channel) == STATUS_ERR: | 
|  | 114 | +                logger.error("VCI_StartCAN Error") | 
|  | 115 | +                self.shutdown() | 
|  | 116 | +                return | 
|  | 117 | + | 
|  | 118 | +    def send(self, msg, timeout=None): | 
|  | 119 | +        """ | 
|  | 120 | +
 | 
|  | 121 | +        :param msg: message to send | 
|  | 122 | +        :param timeout: timeout is not used here | 
|  | 123 | +        :return: | 
|  | 124 | +        """ | 
|  | 125 | +        extern_flag = 1 if msg.is_extended_id else 0 | 
|  | 126 | +        raw_message = VCI_CAN_OBJ(msg.arbitration_id, 0, 0, 1, msg.is_remote_frame, extern_flag, msg.dlc, (c_ubyte * 8)(*msg.data), (c_byte * 3)(*[0, 0, 0])) | 
|  | 127 | + | 
|  | 128 | +        if msg.channel is not None: | 
|  | 129 | +            channel = msg.channel | 
|  | 130 | +        elif len(self.channels) == 1: | 
|  | 131 | +            channel = self.channels[0] | 
|  | 132 | +        else: | 
|  | 133 | +            raise ValueError( | 
|  | 134 | +                "msg.channel must be set when using multiple channels.") | 
|  | 135 | + | 
|  | 136 | +        CANalystII.VCI_Transmit(VCI_USBCAN2, self.device, channel, byref(raw_message), 1) | 
|  | 137 | + | 
|  | 138 | +    def _recv_internal(self, timeout=None): | 
|  | 139 | +        """ | 
|  | 140 | +
 | 
|  | 141 | +        :param timeout: float in seconds | 
|  | 142 | +        :return: | 
|  | 143 | +        """ | 
|  | 144 | +        raw_message = VCI_CAN_OBJ() | 
|  | 145 | + | 
|  | 146 | +        timeout = -1 if timeout is None else int(timeout * 1000) | 
|  | 147 | + | 
|  | 148 | +        if CANalystII.VCI_Receive(VCI_USBCAN2, self.device, self.channels[0], byref(raw_message), 1, timeout) <= STATUS_ERR: | 
|  | 149 | +            return None, False | 
|  | 150 | +        else: | 
|  | 151 | +            return ( | 
|  | 152 | +                Message( | 
|  | 153 | +                    timestamp=raw_message.TimeStamp if raw_message.TimeFlag else 0.0, | 
|  | 154 | +                    arbitration_id=raw_message.ID, | 
|  | 155 | +                    is_remote_frame=raw_message.RemoteFlag, | 
|  | 156 | +                    channel=0, | 
|  | 157 | +                    dlc=raw_message.DataLen, | 
|  | 158 | +                    data=raw_message.Data, | 
|  | 159 | +                ), | 
|  | 160 | +                False, | 
|  | 161 | +            ) | 
|  | 162 | + | 
|  | 163 | +    def flush_tx_buffer(self): | 
|  | 164 | +        for channel in self.channels: | 
|  | 165 | +            CANalystII.VCI_ClearBuffer(VCI_USBCAN2, self.device, channel) | 
|  | 166 | + | 
|  | 167 | +    def shutdown(self): | 
|  | 168 | +        CANalystII.VCI_CloseDevice(VCI_USBCAN2, self.device) | 
0 commit comments