From f61ee8554a071ecf66d3940d7a88a3a53208bd65 Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Thu, 6 Aug 2026 14:14:19 +0300 Subject: [PATCH 1/7] Refactoring J2534 --- udsoncan/connections.py | 173 ++++++------ udsoncan/j2534.py | 603 ++++++++++++++++++++-------------------- 2 files changed, 377 insertions(+), 399 deletions(-) diff --git a/udsoncan/connections.py b/udsoncan/connections.py index 88093c0..edb0729 100755 --- a/udsoncan/connections.py +++ b/udsoncan/connections.py @@ -250,7 +250,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes try: return self.rxqueue.get(block=True, timeout=timeout) except queue.Empty: - raise TimeoutException("Did not received frame in time (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) def empty_rxqueue(self) -> None: while not self.rxqueue.empty(): @@ -358,7 +358,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes try: return self.rxqueue.get(block=True, timeout=timeout) except queue.Empty: - raise TimeoutException("Did not received ISOTP frame in time (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) def empty_rxqueue(self) -> None: while not self.rxqueue.empty(): @@ -433,7 +433,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes try: frame = self.fromuserqueue.get(block=True, timeout=timeout) except queue.Empty: - raise TimeoutException("Did not receive frame from user queue in time (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) if self.mtu is not None: if frame is not None and len(frame) > self.mtu: @@ -561,7 +561,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes frame = self.isotp_layer.recv(block=True, timeout=timeout) if frame is None: - raise TimeoutException("Did not receive IsoTP frame from the Transport layer in time (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) return bytes(frame) @@ -635,7 +635,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes # isotp.protocol.TransportLayer uses bytearray. udsoncan is strict on bytes format return bytes(frame) except queue.Empty: - raise TimeoutException("Did not receive IsoTP frame from the Transport layer in time (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) def empty_rxqueue(self) -> None: while not self.fromIsoTPQueue.empty(): @@ -670,10 +670,6 @@ class J2534Connection(BaseConnection): :param windll: The path to the windows DLL for the J2534 interface (example: 'C:/Program Files{x86}../../openport 2.0/op20pt32.dll') :type interface: string - :param rxid: The reception CAN id - :type rxid: int - :param txid: The transmission CAN id - :type txid: int :param name: This name is included in the logger name so that its output can be redirected. The logger name will be ``Connection[]`` :type name: string :param debug: This will enable windows debugging mode in the dll (see tactrix doc for additional information) @@ -692,34 +688,57 @@ class J2534Connection(BaseConnection): firmwareVersion: "ctypes.Array[ctypes.c_char]" dllVersion: "ctypes.Array[ctypes.c_char]" apiVersion: "ctypes.Array[ctypes.c_char]" - rxqueue: "queue.Queue[bytes]" - exit_requested: bool opened: bool def __init__(self, windll: str, - rxid: int, - txid: int, + rxid: Optional[int] = None, + txid: Optional[int] = None, extid: Optional[int] = None, name: Optional[str] = None, debug: bool = False, protocol = None, baudrate = 500000, ): + + BaseConnection.__init__(self, name) + self.opened = False + self.result = None self.protocol = protocol if protocol else Protocol_ID.ISO15765 self.baudrate = baudrate - self.debug = debug + self.dll_debug = debug try: - # Set up a J2534 interface using the DLL provided - self.interface = J2534(windll=windll, rxid=rxid, txid=txid, extid=extid) + self.interface = J2534(windll) + except AttributeError as e: + raise RuntimeError('DLL invalid: ' + str(e)) + except FileNotFoundError: + raise RuntimeError('DLL not found') + + if (txid or rxid or extid) is not None: + self.logger.critical('txid, rxid, and extid are deprecated constructor arguments. Pass them, for example, "with J2534 Connection(windll) as conn: conn.set_can_id(txid, txid, extid)".') + self.open() + self.set_can_id(txid, rxid, extid) + + def __enter__(self) -> "J2534Connection": + self.open() + return self + + def __exit__(self, type, value, traceback) -> None: + self.close() + + def is_open(self) -> bool: + return self.opened + def empty_rxqueue(self) -> None: + pass + + def open(self) -> "J2534Connection": + try: # Open the interface (connect to the DLL) self.result, self.devID = self.interface.PassThruOpen() - except FileNotFoundError: - raise RuntimeError('DLL not found') except OSError as e: if e.errno in [0x16, 0xe06d7363]: raise RuntimeError('J2534 Device busy') @@ -730,7 +749,7 @@ def __init__(self, self.log_last_operation("PassThruOpen", with_raise=True) - if debug: + if self.dll_debug: self.result = self.interface.PassThruIoctl(0, Ioctl_Flags.TX_IOCTL_SET_DLL_DEBUG_FLAGS, SCONFIG_LIST([(0, Ioctl_Flags.TX_IOCTL_DLL_DEBUG_FLAG_J2534_CALLS.value)]) @@ -739,11 +758,12 @@ def __init__(self, # Get the firmeware and DLL version etc, mainly for debugging output self.result, self.firmwareVersion, self.dllVersion, self.apiVersion = self.interface.PassThruReadVersion(self.devID) + self.log_last_operation("PassThruReadVersion") self.logger.info("J2534 FirmwareVersion: " + str(self.firmwareVersion.value) + ", dllVersoin: " + str(self.dllVersion.value) + ", apiVersion" + str(self.apiVersion.value)) # get the channel ID of the interface (used for subsequent communication) - self.result, self.channelID = self.interface.PassThruConnect(self.devID, self.protocol.value, self.baudrate) + self.result, self.channelID = self.interface.PassThruConnect(self.devID, self.protocol, self.baudrate) self.log_last_operation("PassThruConnect", with_raise=True) configs = [ @@ -759,7 +779,7 @@ def __init__(self, (Ioctl_ID.TWUP.value, 50), (Ioctl_ID.TINL.value, 25), ] - elif self.protocol in [Protocol_ID.ISO15765]: + elif self.protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: configs += [ (Ioctl_ID.ISO15765_BS.value, 0x20), (Ioctl_ID.ISO15765_STMIN.value, 0), @@ -768,99 +788,66 @@ def __init__(self, self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.SET_CONFIG, SCONFIG_LIST(configs)) self.log_last_operation("PassThruIoctl SET_CONFIG") + self.opened = True + self.logger.info("J2534 Connection opened") + + def set_can_id(self, txid: int, rxid: int, extid: int=None): + self.check_connection_opened() + self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_MSG_FILTERS) - self.log_last_operation("PassThruIoctl CLEAR_MSG_FILTERS") + self.log_last_operation("PassThruIoctl CLEAR_MSG_FILTERS", with_raise=True) # Set the filters and clear the read buffer (filters will be set based on tx/rxids) - self.result = self.interface.PassThruStartMsgFilter(self.channelID, self.protocol.value) - self.log_last_operation("PassThruStartMsgFilter") + self.result = self.interface.PassThruStartMsgFilter(self.channelID, self.protocol, txid, rxid, extid) + self.log_last_operation("PassThruStartMsgFilter", with_raise=True) self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_RX_BUFFER) - self.log_last_operation("PassThruIoctl CLEAR_RX_BUFFER") + self.log_last_operation("PassThruIoctl CLEAR_RX_BUFFER", with_raise=True) self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_TX_BUFFER) - self.log_last_operation("PassThruIoctl CLEAR_TX_BUFFER") - - self.rxqueue = queue.Queue() - self.exit_requested = False - self.opened = False - - def open(self) -> "J2534Connection": - self.exit_requested = False - self.interfaceSemaphore = threading.Semaphore() - self.rxthread = threading.Thread(target=self.rxthread_task, daemon=True) - self.rxthread.start() - self.opened = True - self.logger.info('J2534 Connection opened') - return self - - def __enter__(self) -> "J2534Connection": - return self - - def __exit__(self, type, value, traceback) -> None: - self.close() - - def is_open(self) -> bool: - return self.opened - - def rxthread_task(self) -> None: - while not self.exit_requested: - self.interfaceSemaphore.acquire() - try: - result, data, numMessages = self.interface.PassThruReadMsgs(self.channelID, self.protocol.value, pNumMsgs=1) - if data is not None: - self.rxqueue.put(data) - except Exception: - self.logger.critical("Exiting J2534 rx thread") - self.exit_requested = True - self.interfaceSemaphore.release() - time.sleep(0.001) - - def log_last_operation(self, exec_method: str, with_raise = False) -> None: - if self.result != Error_ID.ERR_SUCCESS: - res, pErrDescr = self.interface.PassThruGetLastError() - err = "J2534 %s: %s (%s)" % (exec_method, pErrDescr, self.result) - self.logger.error(err) - if with_raise: - raise RuntimeError(err) - return - - elif self.debug: - self.logger.debug("J2534 %s: OK" % (exec_method)) + self.log_last_operation("PassThruIoctl CLEAR_TX_BUFFER", with_raise=True) def close(self) -> None: + if not self.opened: + return self.opened = False - self.exit_requested = True - self.rxthread.join() self.result = self.interface.PassThruDisconnect(self.channelID) - self.log_last_operation('PassThruDisconnect') + self.log_last_operation("PassThruDisconnect") - self.interface.PassThruClose(self.devID) - self.log_last_operation('PassThruClose') + self.result = self.interface.PassThruClose(self.devID) + self.log_last_operation("PassThruClose") def specific_send(self, payload: bytes, timeout: Optional[float] = None): - timeout = 0 if timeout is None else timeout + self.check_connection_opened() + + timeout = timeout or 0 - # Fix for avoid ERR_CONCURRENT_API_CALL. Stop reading - self.interfaceSemaphore.acquire() - self.result = self.interface.PassThruWriteMsgs(self.channelID, payload, self.protocol.value, Timeout=int(timeout * 1000)) - self.log_last_operation('PassThruWriteMsgs', with_raise=True) - self.interfaceSemaphore.release() + self.result = self.interface.PassThruWriteMsgs(self.channelID, payload, self.protocol, Timeout=int(timeout * 1000)) + self.log_last_operation("PassThruWriteMsgs", with_raise=True) def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: self.check_connection_opened() - try: - return self.rxqueue.get(block=True, timeout=timeout) - except queue.Empty: - raise TimeoutException("Did not received response from J2534 RxQueue (timeout=%s sec)" % timeout) + self.result, data, numMessages = self.interface.PassThruReadMsgs(self.channelID, self.protocol, pNumMsgs=1, Timeout=int(timeout * 1000)) + if self.result in [Error_ID.ERR_BUFFER_EMPTY, Error_ID.ERR_TIMEOUT]: + raise TimeoutException(timeout) - def empty_rxqueue(self) -> None: - while not self.rxqueue.empty(): - self.rxqueue.get() + self.log_last_operation("PassThruReadMsgs", with_raise=True) + return data + + def log_last_operation(self, exec_method: str, with_raise = False) -> None: + if self.result == Error_ID.ERR_SUCCESS: + return + res, desc = self.interface.PassThruGetLastError() + err = "%s: %s (%s)" % (exec_method, desc, self.result) + if with_raise: + raise RuntimeError(err) + self.logger.error(err) def read_vbatt(self, digits=1) -> float: + self.check_connection_opened() + vbatt = ctypes.POINTER(ctypes.c_int32)() self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.READ_VBATT, None, vbatt) @@ -922,7 +909,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes try: return self.rxqueue.get(block=True, timeout=timeout) except queue.Empty: - raise TimeoutException("Did not received response from J2534 RxQueue (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) def empty_rxqueue(self) -> None: while not self.rxqueue.empty(): @@ -981,7 +968,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes frame = cast(Optional[bytes], self.conn.recv(timeout)) if frame is None and timeout: - raise TimeoutException("Did not received frame in time (timeout=%s sec)" % timeout) + raise TimeoutException(timeout) return frame diff --git a/udsoncan/j2534.py b/udsoncan/j2534.py index 936323e..872815e 100755 --- a/udsoncan/j2534.py +++ b/udsoncan/j2534.py @@ -1,9 +1,243 @@ -import ctypes -from ctypes import Structure, WINFUNCTYPE, POINTER, cast, c_long, c_void_p, c_ulong, byref # type: ignore - from enum import Enum +from ctypes import Structure, WINFUNCTYPE, POINTER, cast, cdll, c_char, c_long, c_void_p, c_ubyte, c_ulong, byref # type: ignore + + +class Error_ID(Enum): + ERR_SUCCESS = 0x00 + STATUS_NOERROR = 0x00 + ERR_NOT_SUPPORTED = 0x01 + ERR_INVALID_CHANNEL_ID = 0x02 + ERR_INVALID_PROTOCOL_ID = 0x03 + ERR_NULL_PARAMETER = 0x04 + ERR_INVALID_IOCTL_VALUE = 0x05 + ERR_INVALID_FLAGS = 0x06 + ERR_FAILED = 0x07 + ERR_DEVICE_NOT_CONNECTED = 0x08 + ERR_TIMEOUT = 0x09 + ERR_INVALID_MSG = 0x0A + ERR_INVALID_TIME_INTERVAL = 0x0B + ERR_EXCEEDED_LIMIT = 0x0C + ERR_INVALID_MSG_ID = 0x0D + ERR_DEVICE_IN_USE = 0x0E + ERR_INVALID_IOCTL_ID = 0x0F + ERR_BUFFER_EMPTY = 0x10 + ERR_BUFFER_FULL = 0x11 + ERR_BUFFER_OVERFLOW = 0x12 + ERR_PIN_INVALID = 0x13 + ERR_CHANNEL_IN_USE = 0x14 + ERR_MSG_PROTOCOL_ID = 0x15 + ERR_INVALID_FILTER_ID = 0x16 + ERR_NO_FLOW_CONTROL = 0x17 + ERR_NOT_UNIQUE = 0x18 + ERR_INVALID_BAUDRATE = 0x19 + ERR_INVALID_DEVICE_ID = 0x1A + + +class Protocol_ID(Enum): + J1850VPW = 1 + J1850PWM = 2 + ISO9141 = 3 + ISO14230 = 4 + CAN = 5 + ISO15765 = 6 + SCI_A_ENGINE = 7 # OP2.0: Not supported + SCI_A_TRANS = 8 # OP2.0: Not supported + SCI_B_ENGINE = 9 # OP2.0: Not supported + SCI_B_TRANS = 10 # OP2.0: Not supported + + J1850VPW_PS = 0x8000 + J1850PWM_PS = 0x8001 + ISO9141_PS = 0x8002 + ISO14230_PS = 0x8003 + CAN_PS = 0x8004 + ISO15765_PS = 0x8005 + J2610_PS = 0x8006 + SW_ISO15765_PS = 0x8007 + SW_CAN_PS = 0x8008 + GM_UART_PS = 0x8009 + CAN_XON_XOFF_PS = 0x800A + ANALOG_IN_1 = 0x800B + ANALOG_IN_2 = 0x800C + ANALOG_IN_3 = 0x800D + ANALOG_IN_4 = 0x800E + ANALOG_IN_5 = 0x800F + ANALOG_IN_6 = 0x8010 + ANALOG_IN_7 = 0x8011 + ANALOG_IN_8 = 0x8012 + ANALOG_IN_9 = 0x8013 + ANALOG_IN_10 = 0x8014 + ANALOG_IN_11 = 0x8015 + ANALOG_IN_12 = 0x8016 + ANALOG_IN_13 = 0x8017 + ANALOG_IN_14 = 0x8018 + ANALOG_IN_15 = 0x8019 + ANALOG_IN_16 = 0x801A + ANALOG_IN_17 = 0x801B + ANALOG_IN_18 = 0x801C + ANALOG_IN_19 = 0x801D + ANALOG_IN_20 = 0x801E + ANALOG_IN_21 = 0x801F + ANALOG_IN_22 = 0x8020 + ANALOG_IN_23 = 0x8021 + ANALOG_IN_24 = 0x8022 + ANALOG_IN_25 = 0x8023 + ANALOG_IN_26 = 0x8024 + ANALOG_IN_27 = 0x8025 + ANALOG_IN_28 = 0x8026 + ANALOG_IN_29 = 0x8027 + ANALOG_IN_30 = 0x8028 + ANALOG_IN_31 = 0x8029 + ANALOG_IN_32 = 0x802A -import logging + +class Filter(Enum): + PASS_FILTER = 0x00000001 + BLOCK_FILTER = 0x00000002 + FLOW_CONTROL_FILTER = 0x00000003 + + +class ConnectFlags(Enum): + NONE = 0 + CAN_29_BIT_ID = 0x100 + ISO9141_NO_CHECKSUM = 0x200 + CAN_ID_BOTH = 0x800 + ISO9141_K_LINE_ONLY = 0x1000 + + +class TxFlags(Enum): + NONE = 0 + # 0 = no padding + # 1 = pad all flow controlled messages to a full CAN frame using zeroes + ISO15765_FRAME_PAD = 0x00000040 + + ISO15765_ADDR_TYPE = 0x00000080 + CAN_29_BIT_ID = 0x00000100 + + # 0 = Interface message timing as specified in ISO 14230 + # 1 = After a response is received for a physical request, the wait time shall be reduced to P3_MIN + # Does not affect timing on responses to functional requests + WAIT_P3_MIN_ONLY = 0x00000200 + + SW_CAN_HV_TX = 0x00000400 + + # 0 = Transmit using SCI Full duplex mode + # 1 = Transmit using SCI Half duplex mode + SCI_MODE = 0x00400000 + + # 0 = no voltage after message transmit + # 1 = apply 20V after message transmit + SCI_TX_VOLTAGE = 0x00800000 + + DT_PERIODIC_UPDATE = 0x10000000 + + +class RxStatus(Enum): + NONE = 0 + # 0 = received + # 1 = transmitted + TX_MSG_TYPE = 0x00000001 + + # 0 = Not a start of message indication + # 1 = First byte or frame received + START_OF_MESSAGE = 0x00000002 + ISO15765_FIRST_FRAME = 0x00000002 + + # 0 = No break received + # 1 = Break received + RX_BREAK = 0x00000004 + + # 0 = No TxDone + # 1 = TxDone + TX_INDICATION = 0x00000008 + TX_DONE = 0x00000008 + + # 0 = No Error + # 1 = Padding Error + ISO15765_PADDING_ERROR = 0x00000010 + + # 0 = no extended address, + # 1 = extended address is first byte after the CAN ID + ISO15765_ADDR_TYPE = 0x00000080 + + CAN_29_BIT_ID = 0x00000100 + + SW_CAN_NS_RX = 0x00040000 + SW_CAN_HS_RX = 0x00020000 + SW_CAN_HV_RX = 0x00010000 + + +class Ioctl_ID(Enum): + GET_CONFIG = 0x01 + SET_CONFIG = 0x02 + READ_VBATT = 0x03 + FIVE_BAUD_INIT = 0x04 + FAST_INIT = 0x05 + CLEAR_TX_BUFFER = 0x07 + CLEAR_RX_BUFFER = 0x08 + CLEAR_PERIODIC_MSGS = 0x09 + CLEAR_MSG_FILTERS = 0x0A + CLEAR_FUNCT_MSG_LOOKUP_TABLE = 0x0B + ADD_TO_FUNCT_MSG_LOOKUP_TABLE = 0x0C + DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE = 0x0D + READ_PROG_VOLTAGE = 0x0E + + DATA_RATE = 0x01 # 5 500000 # Baud rate value used for vehicle network. No default value specified. + LOOPBACK = 0x03 # 0(OFF)/1(ON) # 0 = Do not echo transmitted messages to the Receive queue. 1 = Echo transmitted messages to the Receive queue. + NODE_ADDRESS = 0x04 # 0x00-0xFF # J1850PWM specific, physical address for node of interest in the vehicle network. Default is no nodes are recognized by scan tool. + NETWORK_LINE = 0x05 # 0(BUS_NORMAL)/1(BUS_PLUS)/2(BUS_MINUS) # J1850PWM specific, network line(s) active during message transfers. Default value is 0(BUS_NORMAL). + P1_MIN = 0x06 # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU inter-byte time for responses [02.02-API: ms]. Default value is 0 ms. 04.04-API: NOT ADJUSTABLE, 0ms. + P1_MAX = 0x07 # 0x0/0x1-0xFFFF # ISO-9141/14230 specific, max. ECU inter-byte time for responses [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 20 ms. + P2_MIN = 0x08 # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU response time to a tester request or between ECU responses [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, 0ms. Default value is 25 ms. + P2_MAX = 0x09 # 0x0-0xFFFF # ISO-9141/14230 specific, max. ECU response time to a tester request or between ECU responses [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, all messages up to P3_MIN are receoved. Default value is 50 ms. + P3_MIN = 0x0A # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU response time between end of ECU response and next tester request [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 55 ms. + P3_MAX = 0x0B # 0x0-0xFFFF # ISO-9141/14230 specific, max. ECU response time between end of ECU response and next tester request [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, messages can be sent at anytime after P3_MIN. Default value is 5000 ms. + P4_MIN = 0x0C # 0x0-0xFFFF # ISO-9141/14230 specific, min. tester inter-byte time for a request [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 5 ms. + P4_MAX = 0x0D # 0x0-0xFFFF # ISO-9141/14230 specific, max. tester inter-byte time for a request [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, P4_MIN is always used. Default value is 20 ms. + W1 = 0x0E # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] from the address byte end to synchronization pattern start. Default value is 300 ms. + W2 = 0x0F # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] from the synchronization byte end to key byte 1 start. Default value is 20 ms. + W3 = 0x10 # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] between key byte 1 and key byte 2. Default value is 20 ms. + W4 = 0x11 # 0x0-0xFFFF # ISO 9141 specific, 02.02-API: max. time [ms] between key byte 2 and its inversion from the tester. Default value is 50 ms. + W5 = 0x12 # 0x0-0xFFFF # ISO 9141 specific, min. time [ms] before the tester begins retransmission of the address byte. Default value is 300 ms. + TIDLE = 0x13 # 0x0-0xFFFF # ISO 9141 specific, bus idle time required before starting a fast initialization sequence. Default value is W5 value. + TINL = 0x14 # 0x0-0xFFFF # ISO 9141 specific, the duration [ms] of the fast initialization low pulse. Default value is 25 ms. + TWUP = 0x15 # 0x0-0xFFFF # ISO 9141 specific, the duration [ms] of the fast initialization wake-up pulse. Default value is 50 ms. + PARITY = 0x16 # 0(NO_PARITY)/1(ODD_PARITY)/2(EVEN_PARITY) # ISO9141 specific, parity type for detecting bit errors. Default value is 0(NO_PARITY). + BIT_SAMPLE_POINT = 0x17 # 0-100 # CAN specific, the desired bit sample point as a percentage of bit time. Default value is 80%. + SYNCH_JUMP_WIDTH = 0x18 # 0-100 # CAN specific, the desired synchronization jump width as a percentage of the bit time. Default value is 15%. + W0 = 0x19 + T1_MAX = 0x1A # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe response delay. Default value is 20 ms. + T2_MAX = 0x1B # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe request delay.Default value is 100 ms. + T4_MAX = 0x1C # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage response delay. Default value is 20 ms. + T5_MAX = 0x1D # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage request delay. Default value is 100 ms. + ISO15765_BS = 0x1E # 0x0-0xFF # ISO15765 specific, the block size for segmented transfers. + ISO15765_STMIN = 0x1F # 0x0-0xFF # ISO15765 specific, the separation time for segmented transfers. + DATA_BITS = 0x20 # 04.04-API only + FIVE_BAUD_MOD = 0x21 + BS_TX = 0x22 + STMIN_TX = 0x23 + T3_MAX = 0x24 + ISO15765_WFT_MAX = 0x25 + + # J2534-2 + CAN_MIXED_FORMAT = 0x8000 + J1962_PINS = 0x8001 + SW_CAN_HS_DATA_RATE = 0x8010 + SW_CAN_SPEEDCHANGE_ENABLE = 0x8011 + SW_CAN_RES_SWITCH = 0x8012 + ACTIVE_CHANNELS = 0x8020 # Bitmask of channels being sampled + SAMPLE_RATE = 0x8021 # Samples/second or Seconds/sample + SAMPLES_PER_READING = 0x8022 # Samples to average into a single reading + READINGS_PER_MSG = 0x8023 # Number of readings for each active channel per PASSTHRU_MSG structure + AVERAGING_METHOD = 0x8024 # The way in which the samples will be averaged. + SAMPLE_RESOLUTION = 0x8025 # The number of bits of resolution for each channel in the subsystem. Read Only. + INPUT_RANGE_LOW = 0x8026 # Lower limit in millivolts of A/D input. Read Only. + INPUT_RANGE_HIGH = 0x8027 # Upper limit in millivolts of A/D input. Read Only. + + +class Ioctl_Flags(Enum): + TX_IOCTL_BASE = 0x70000 + TX_IOCTL_SET_DLL_DEBUG_FLAGS = 0x70001 + TX_IOCTL_DLL_DEBUG_FLAG_J2534_CALLS = 0x00000001 class PASSTHRU_MSG(Structure): @@ -13,7 +247,7 @@ class PASSTHRU_MSG(Structure): ("Timestamp", c_ulong), ("DataSize", c_ulong), ("ExtraDataIndex", c_ulong), - ("Data", ctypes.c_ubyte * 4128)] + ("Data", c_ubyte * 4128)] def setData(self, data: bytes): self.DataSize = len(data) @@ -53,7 +287,9 @@ class J2534(): dllPassThruStartMsgFilter = None dllPassThruIoctl = None - def __init__(self, windll, rxid, txid, extid): + def __init__(self, windll, txid=None, rxid=None, extid=None): + assert (txid or rxid or extid) is None, 'txid, rxid, extid its legacy argumets. Pass into J2534.PassThruStartMsgFilter() method.' + global dllPassThruOpen global dllPassThruClose global dllPassThruConnect @@ -67,22 +303,7 @@ def __init__(self, windll, rxid, txid, extid): global dllPassThruStartMsgFilter global dllPassThruIoctl - self.hDLL = ctypes.cdll.LoadLibrary(windll) - self.rxid = rxid.to_bytes(4, 'big') - self.txid = txid.to_bytes(4, 'big') - self.txFlags = TxFlags.ISO15765_FRAME_PAD.value - self.connectFlags = ConnectFlags.NONE.value - # Determine mode ID29 or ID11 - if txid >> 11: - self.txFlags |= TxFlags.CAN_29_BIT_ID.value - self.connectFlags |= ConnectFlags.CAN_29_BIT_ID.value - - if extid is not None: - self.rxid += extid.to_bytes(1, 'big') - self.txid += extid.to_bytes(1, 'big') - self.txFlags |= TxFlags.ISO15765_ADDR_TYPE.value - - self.logger = logging.getLogger() + self.hDLL = cdll.LoadLibrary(windll) dllPassThruOpenProto = WINFUNCTYPE( c_long, @@ -158,16 +379,16 @@ def __init__(self, windll, rxid, txid, extid): dllPassThruReadVersionProto = WINFUNCTYPE( c_long, c_ulong, - POINTER(ctypes.c_char), - POINTER(ctypes.c_char), - POINTER(ctypes.c_char)) + POINTER(c_char), + POINTER(c_char), + POINTER(c_char)) dllPassThruReadVersionParams = (1, "DeviceID", 0), (1, "pFirmwareVersion", 0), (1, "pDllVersion", 0), (1, "pApiVersoin", 0) dllPassThruReadVersion = dllPassThruReadVersionProto(("PassThruReadVersion", self.hDLL), dllPassThruReadVersionParams) dllPassThruGetLastErrorProto = WINFUNCTYPE( c_long, - POINTER(ctypes.c_char), + POINTER(c_char), ) dllPassThruGetLastErrorParams = (1, "pErrorDescription", 0), dllPassThruGetLastError = dllPassThruGetLastErrorProto(("PassThruGetLastError", self.hDLL), dllPassThruGetLastErrorParams) @@ -200,356 +421,126 @@ def __init__(self, windll, rxid, txid, extid): def PassThruOpen(self, pDeviceID=None): if not pDeviceID: - pDeviceID = ctypes.c_ulong() + pDeviceID = c_ulong() result = dllPassThruOpen(bytes('J2534-2:', 'ascii'), byref(pDeviceID)) - return Error_ID(hex(result)), pDeviceID + return Error_ID(result), pDeviceID + + def PassThruConnect(self, deviceID, protocol: Protocol_ID, baudrate, pChannelID=None): + self.txFlags = TxFlags.NONE.value + + if protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: + self.txFlags |= TxFlags.ISO15765_FRAME_PAD.value + + connectFlags = ConnectFlags.CAN_ID_BOTH.value - def PassThruConnect(self, deviceID, protocol, baudrate, pChannelID=None): if not pChannelID: pChannelID = c_ulong() - result = dllPassThruConnect(deviceID, protocol, self.connectFlags, baudrate, byref(pChannelID)) - return Error_ID(hex(result)), pChannelID + result = dllPassThruConnect(deviceID, protocol.value, connectFlags, baudrate, byref(pChannelID)) + return Error_ID(result), pChannelID def PassThruClose(self, DeviceID): result = dllPassThruClose(DeviceID) - return Error_ID(hex(result)) + return Error_ID(result) def PassThruDisconnect(self, ChannelID): result = dllPassThruDisconnect(ChannelID) - return Error_ID(hex(result)) + return Error_ID(result) - def PassThruReadMsgs(self, ChannelID, protocol, pNumMsgs=1, Timeout=20): + def PassThruReadMsgs(self, ChannelID, protocol: Protocol_ID, pNumMsgs=1, Timeout=1000): pMsg = PASSTHRU_MSG() - pMsg.ProtocolID = protocol + pMsg.ProtocolID = protocol.value pNumMsgs = c_ulong(pNumMsgs) while 1: # breakpoint() + # Do not wrap in queue for avoid mixing timeout of usb connection and real server response Timeout. result = dllPassThruReadMsgs(ChannelID, byref(pMsg), byref(pNumMsgs), c_ulong(Timeout)) - if hex(result) == Error_ID.ERR_BUFFER_EMPTY.value or pNumMsgs == 0: - return None, None, 0 - if pMsg.RxStatus & (RxStatus.TX_INDICATION.value | RxStatus.TX_MSG_TYPE.value | RxStatus.START_OF_MESSAGE.value): continue - return Error_ID(hex(result)), pMsg.getData(), pNumMsgs - - def PassThruWriteMsgs(self, ChannelID, Data, protocol, pNumMsgs=1, Timeout=1000): - Data = self.txid + Data - self.logger.info("Sending data: " + str(Data.hex())) + return Error_ID(result), pMsg.getData(), pNumMsgs + def PassThruWriteMsgs(self, ChannelID, Data, protocol: Protocol_ID, pNumMsgs=1, Timeout=1000): txmsg = PASSTHRU_MSG() txmsg.TxFlags = self.txFlags - txmsg.ProtocolID = protocol - txmsg.setData(Data) + txmsg.ProtocolID = protocol.value + txmsg.setData(self.txid + Data) result = dllPassThruWriteMsgs(ChannelID, byref(txmsg), byref(c_ulong(pNumMsgs)), c_ulong(Timeout)) - - return Error_ID(hex(result)) + return Error_ID(result) def PassThruStartPeriodicMsg(self, ChannelID, Data, MsgID=0, TimeInterval=100): pMsg = PASSTHRU_MSG() pMsg.setData(Data) result = dllPassThruStartPeriodicMsg(ChannelID, byref(pMsg), byref(c_ulong(MsgID)), c_ulong(TimeInterval)) - - return Error_ID(hex(result)) + return Error_ID(result) def PassThruStopPeriodicMsg(self, ChannelID, MsgID): result = dllPassThruStopPeriodicMsg(ChannelID, MsgID) - return Error_ID(hex(result)) + return Error_ID(result) def PassThruReadVersion(self, DeviceID): - pFirmwareVersion = (ctypes.c_char * 80)() - pDllVersion = (ctypes.c_char * 80)() - pApiVersion = (ctypes.c_char * 80)() - result = dllPassThruReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion) + pFirmwareVersion = (c_char * 80)() + pDllVersion = (c_char * 80)() + pApiVersion = (c_char * 80)() - return Error_ID(hex(result)), pFirmwareVersion, pDllVersion, pApiVersion + result = dllPassThruReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion) + return Error_ID(result), pFirmwareVersion, pDllVersion, pApiVersion def PassThruGetLastError(self): - pErrorDescription = (ctypes.c_char * 80)() + pErrorDescription = (c_char * 80)() result = dllPassThruGetLastError(pErrorDescription) - return Error_ID(hex(result)), pErrorDescription.value.decode() + return Error_ID(result), pErrorDescription.value.decode() def PassThruIoctl(self, Handle, IoctlID, ioctlInput=None, ioctlOutput=None): pInput = None if ioctlInput is None else byref(ioctlInput) pOutput = None if ioctlOutput is None else byref(ioctlOutput) result = dllPassThruIoctl(Handle, c_ulong(IoctlID.value), pInput, pOutput) + return Error_ID(result) - return Error_ID(hex(result)) + def PassThruStartMsgFilter(self, ChannelID, protocol: Protocol_ID, txid: int, rxid: int, extid: int = None): + self.txid = txid.to_bytes(4, 'big') + self.rxid = rxid.to_bytes(4, 'big') + + if extid is not None: + self.txid += extid.to_bytes(1, 'big') + self.rxid += extid.to_bytes(1, 'big') + self.txFlags |= TxFlags.ISO15765_ADDR_TYPE.value + else: + self.txFlags &= ~TxFlags.ISO15765_ADDR_TYPE.value - def PassThruStartMsgFilter(self, ChannelID, protocol): msgMask = PASSTHRU_MSG() - msgMask.ProtocolID = protocol + msgMask.ProtocolID = protocol.value msgMask.TxFlags = self.txFlags msgMask.RxStatus = msgMask.ExtraDataIndex = 0xCCCC_CCCC msgMask.setData(b'\xFF' * len(self.rxid)) msgPattern = PASSTHRU_MSG() - msgPattern.ProtocolID = protocol + msgPattern.ProtocolID = protocol.value msgPattern.TxFlags = self.txFlags msgPattern.RxStatus = msgPattern.ExtraDataIndex = 0xCCCC_CCCC msgPattern.setData(self.rxid) - if protocol in [Protocol_ID.ISO9141.value, Protocol_ID.ISO14230.value]: - filterType = c_ulong(Filter.PASS_FILTER.value) - msgFlow = None - else: + if protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: filterType = c_ulong(Filter.FLOW_CONTROL_FILTER.value) msgFlow = PASSTHRU_MSG() - msgFlow.ProtocolID = protocol + msgFlow.ProtocolID = protocol.value msgFlow.TxFlags = self.txFlags msgFlow.RxStatus = msgFlow.ExtraDataIndex = 0xCCCC_CCCC msgFlow.setData(self.txid) msgFlow = byref(msgFlow) + else: + filterType = c_ulong(Filter.PASS_FILTER.value) + msgFlow = None msgID = c_ulong(0) result = dllPassThruStartMsgFilter(ChannelID, filterType, byref(msgMask), byref(msgPattern), msgFlow, byref(msgID)) - - return Error_ID(hex(result)) - - -class Error_ID(Enum): - ERR_SUCCESS = hex(0x00) - STATUS_NOERROR = hex(0x00) - ERR_NOT_SUPPORTED = hex(0x01) - ERR_INVALID_CHANNEL_ID = hex(0x02) - ERR_INVALID_PROTOCOL_ID = hex(0x03) - ERR_NULL_PARAMETER = hex(0x04) - ERR_INVALID_IOCTL_VALUE = hex(0x05) - ERR_INVALID_FLAGS = hex(0x06) - ERR_FAILED = hex(0x07) - ERR_DEVICE_NOT_CONNECTED = hex(0x08) - ERR_TIMEOUT = hex(0x09) - ERR_INVALID_MSG = hex(0x0A) - ERR_INVALID_TIME_INTERVAL = hex(0x0B) - ERR_EXCEEDED_LIMIT = hex(0x0C) - ERR_INVALID_MSG_ID = hex(0x0D) - ERR_DEVICE_IN_USE = hex(0x0E) - ERR_INVALID_IOCTL_ID = hex(0x0F) - ERR_BUFFER_EMPTY = hex(0x10) - ERR_BUFFER_FULL = hex(0x11) - ERR_BUFFER_OVERFLOW = hex(0x12) - ERR_PIN_INVALID = hex(0x13) - ERR_CHANNEL_IN_USE = hex(0x14) - ERR_MSG_PROTOCOL_ID = hex(0x15) - ERR_INVALID_FILTER_ID = hex(0x16) - ERR_NO_FLOW_CONTROL = hex(0x17) - ERR_NOT_UNIQUE = hex(0x18) - ERR_INVALID_BAUDRATE = hex(0x19) - ERR_INVALID_DEVICE_ID = hex(0x1A) - - -class Protocol_ID(Enum): - J1850VPW = 1 - J1850PWM = 2 - ISO9141 = 3 - ISO14230 = 4 - CAN = 5 - ISO15765 = 6 - SCI_A_ENGINE = 7 # OP2.0: Not supported - SCI_A_TRANS = 8 # OP2.0: Not supported - SCI_B_ENGINE = 9 # OP2.0: Not supported - SCI_B_TRANS = 10 # OP2.0: Not supported - - J1850VPW_PS = 0x8000 - J1850PWM_PS = 0x8001 - ISO9141_PS = 0x8002 - ISO14230_PS = 0x8003 - CAN_PS = 0x8004 - ISO15765_PS = 0x8005 - J2610_PS = 0x8006 - SW_ISO15765_PS = 0x8007 - SW_CAN_PS = 0x8008 - GM_UART_PS = 0x8009 - CAN_XON_XOFF_PS = 0x800A - ANALOG_IN_1 = 0x800B - ANALOG_IN_2 = 0x800C - ANALOG_IN_3 = 0x800D - ANALOG_IN_4 = 0x800E - ANALOG_IN_5 = 0x800F - ANALOG_IN_6 = 0x8010 - ANALOG_IN_7 = 0x8011 - ANALOG_IN_8 = 0x8012 - ANALOG_IN_9 = 0x8013 - ANALOG_IN_10 = 0x8014 - ANALOG_IN_11 = 0x8015 - ANALOG_IN_12 = 0x8016 - ANALOG_IN_13 = 0x8017 - ANALOG_IN_14 = 0x8018 - ANALOG_IN_15 = 0x8019 - ANALOG_IN_16 = 0x801A - ANALOG_IN_17 = 0x801B - ANALOG_IN_18 = 0x801C - ANALOG_IN_19 = 0x801D - ANALOG_IN_20 = 0x801E - ANALOG_IN_21 = 0x801F - ANALOG_IN_22 = 0x8020 - ANALOG_IN_23 = 0x8021 - ANALOG_IN_24 = 0x8022 - ANALOG_IN_25 = 0x8023 - ANALOG_IN_26 = 0x8024 - ANALOG_IN_27 = 0x8025 - ANALOG_IN_28 = 0x8026 - ANALOG_IN_29 = 0x8027 - ANALOG_IN_30 = 0x8028 - ANALOG_IN_31 = 0x8029 - ANALOG_IN_32 = 0x802A - - -class Filter(Enum): - PASS_FILTER = 0x00000001 - BLOCK_FILTER = 0x00000002 - FLOW_CONTROL_FILTER = 0x00000003 - - -class ConnectFlags(Enum): - NONE = 0 - CAN_29_BIT_ID = 0x100 - ISO9141_NO_CHECKSUM = 0x200 - CAN_ID_BOTH = 0x800 - ISO9141_K_LINE_ONLY = 0x1000 - - -class TxFlags(Enum): - NONE = 0 - # 0 = no padding - # 1 = pad all flow controlled messages to a full CAN frame using zeroes - ISO15765_FRAME_PAD = 0x00000040 - - ISO15765_ADDR_TYPE = 0x00000080 - CAN_29_BIT_ID = 0x00000100 - - # 0 = Interface message timing as specified in ISO 14230 - # 1 = After a response is received for a physical request, the wait time shall be reduced to P3_MIN - # Does not affect timing on responses to functional requests - WAIT_P3_MIN_ONLY = 0x00000200 - - SW_CAN_HV_TX = 0x00000400 - - # 0 = Transmit using SCI Full duplex mode - # 1 = Transmit using SCI Half duplex mode - SCI_MODE = 0x00400000 - - # 0 = no voltage after message transmit - # 1 = apply 20V after message transmit - SCI_TX_VOLTAGE = 0x00800000 - - DT_PERIODIC_UPDATE = 0x10000000 - - -class RxStatus(Enum): - NONE = 0 - # 0 = received - # 1 = transmitted - TX_MSG_TYPE = 0x00000001 - - # 0 = Not a start of message indication - # 1 = First byte or frame received - START_OF_MESSAGE = 0x00000002 - ISO15765_FIRST_FRAME = 0x00000002 - - # 0 = No break received - # 1 = Break received - RX_BREAK = 0x00000004 - - # 0 = No TxDone - # 1 = TxDone - TX_INDICATION = 0x00000008 - TX_DONE = 0x00000008 - - # 0 = No Error - # 1 = Padding Error - ISO15765_PADDING_ERROR = 0x00000010 - - # 0 = no extended address, - # 1 = extended address is first byte after the CAN ID - ISO15765_ADDR_TYPE = 0x00000080 - - CAN_29_BIT_ID = 0x00000100 - - SW_CAN_NS_RX = 0x00040000 - SW_CAN_HS_RX = 0x00020000 - SW_CAN_HV_RX = 0x00010000 - - -class Ioctl_ID(Enum): - GET_CONFIG = 0x01 - SET_CONFIG = 0x02 - READ_VBATT = 0x03 - FIVE_BAUD_INIT = 0x04 - FAST_INIT = 0x05 - CLEAR_TX_BUFFER = 0x07 - CLEAR_RX_BUFFER = 0x08 - CLEAR_PERIODIC_MSGS = 0x09 - CLEAR_MSG_FILTERS = 0x0A - CLEAR_FUNCT_MSG_LOOKUP_TABLE = 0x0B - ADD_TO_FUNCT_MSG_LOOKUP_TABLE = 0x0C - DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE = 0x0D - READ_PROG_VOLTAGE = 0x0E - - DATA_RATE = 0x01 # 5 500000 # Baud rate value used for vehicle network. No default value specified. - LOOPBACK = 0x03 # 0(OFF)/1(ON) # 0 = Do not echo transmitted messages to the Receive queue. 1 = Echo transmitted messages to the Receive queue. - NODE_ADDRESS = 0x04 # 0x00-0xFF # J1850PWM specific, physical address for node of interest in the vehicle network. Default is no nodes are recognized by scan tool. - NETWORK_LINE = 0x05 # 0(BUS_NORMAL)/1(BUS_PLUS)/2(BUS_MINUS) # J1850PWM specific, network line(s) active during message transfers. Default value is 0(BUS_NORMAL). - P1_MIN = 0x06 # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU inter-byte time for responses [02.02-API: ms]. Default value is 0 ms. 04.04-API: NOT ADJUSTABLE, 0ms. - P1_MAX = 0x07 # 0x0/0x1-0xFFFF # ISO-9141/14230 specific, max. ECU inter-byte time for responses [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 20 ms. - P2_MIN = 0x08 # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU response time to a tester request or between ECU responses [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, 0ms. Default value is 25 ms. - P2_MAX = 0x09 # 0x0-0xFFFF # ISO-9141/14230 specific, max. ECU response time to a tester request or between ECU responses [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, all messages up to P3_MIN are receoved. Default value is 50 ms. - P3_MIN = 0x0A # 0x0-0xFFFF # ISO-9141/14230 specific, min. ECU response time between end of ECU response and next tester request [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 55 ms. - P3_MAX = 0x0B # 0x0-0xFFFF # ISO-9141/14230 specific, max. ECU response time between end of ECU response and next tester request [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, messages can be sent at anytime after P3_MIN. Default value is 5000 ms. - P4_MIN = 0x0C # 0x0-0xFFFF # ISO-9141/14230 specific, min. tester inter-byte time for a request [02.02-API: ms, 04.04-API: *0.5ms]. Default value is 5 ms. - P4_MAX = 0x0D # 0x0-0xFFFF # ISO-9141/14230 specific, max. tester inter-byte time for a request [02.02-API: ms, 04.04-API: *0.5ms]. 04.04-API: NOT ADJUSTABLE, P4_MIN is always used. Default value is 20 ms. - W1 = 0x0E # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] from the address byte end to synchronization pattern start. Default value is 300 ms. - W2 = 0x0F # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] from the synchronization byte end to key byte 1 start. Default value is 20 ms. - W3 = 0x10 # 0x0-0xFFFF # ISO 9141 specific, max. time [ms] between key byte 1 and key byte 2. Default value is 20 ms. - W4 = 0x11 # 0x0-0xFFFF # ISO 9141 specific, 02.02-API: max. time [ms] between key byte 2 and its inversion from the tester. Default value is 50 ms. - W5 = 0x12 # 0x0-0xFFFF # ISO 9141 specific, min. time [ms] before the tester begins retransmission of the address byte. Default value is 300 ms. - TIDLE = 0x13 # 0x0-0xFFFF # ISO 9141 specific, bus idle time required before starting a fast initialization sequence. Default value is W5 value. - TINL = 0x14 # 0x0-0xFFFF # ISO 9141 specific, the duration [ms] of the fast initialization low pulse. Default value is 25 ms. - TWUP = 0x15 # 0x0-0xFFFF # ISO 9141 specific, the duration [ms] of the fast initialization wake-up pulse. Default value is 50 ms. - PARITY = 0x16 # 0(NO_PARITY)/1(ODD_PARITY)/2(EVEN_PARITY) # ISO9141 specific, parity type for detecting bit errors. Default value is 0(NO_PARITY). - BIT_SAMPLE_POINT = 0x17 # 0-100 # CAN specific, the desired bit sample point as a percentage of bit time. Default value is 80%. - SYNCH_JUMP_WIDTH = 0x18 # 0-100 # CAN specific, the desired synchronization jump width as a percentage of the bit time. Default value is 15%. - W0 = 0x19 - T1_MAX = 0x1A # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe response delay. Default value is 20 ms. - T2_MAX = 0x1B # 0x0-0xFFFF # SCI_X_XXXX specific, the max. interframe request delay.Default value is 100 ms. - T4_MAX = 0x1C # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage response delay. Default value is 20 ms. - T5_MAX = 0x1D # 0x0-0xFFFF # SCI_X_XXXX specific, the max. intermessage request delay. Default value is 100 ms. - ISO15765_BS = 0x1E # 0x0-0xFF # ISO15765 specific, the block size for segmented transfers. - ISO15765_STMIN = 0x1F # 0x0-0xFF # ISO15765 specific, the separation time for segmented transfers. - DATA_BITS = 0x20 # 04.04-API only - FIVE_BAUD_MOD = 0x21 - BS_TX = 0x22 - STMIN_TX = 0x23 - T3_MAX = 0x24 - ISO15765_WFT_MAX = 0x25 - - # J2534-2 - CAN_MIXED_FORMAT = 0x8000 - J1962_PINS = 0x8001 - SW_CAN_HS_DATA_RATE = 0x8010 - SW_CAN_SPEEDCHANGE_ENABLE = 0x8011 - SW_CAN_RES_SWITCH = 0x8012 - ACTIVE_CHANNELS = 0x8020 # Bitmask of channels being sampled - SAMPLE_RATE = 0x8021 # Samples/second or Seconds/sample - SAMPLES_PER_READING = 0x8022 # Samples to average into a single reading - READINGS_PER_MSG = 0x8023 # Number of readings for each active channel per PASSTHRU_MSG structure - AVERAGING_METHOD = 0x8024 # The way in which the samples will be averaged. - SAMPLE_RESOLUTION = 0x8025 # The number of bits of resolution for each channel in the subsystem. Read Only. - INPUT_RANGE_LOW = 0x8026 # Lower limit in millivolts of A/D input. Read Only. - INPUT_RANGE_HIGH = 0x8027 # Upper limit in millivolts of A/D input. Read Only. - - -class Ioctl_Flags(Enum): - TX_IOCTL_BASE = 0x70000 - TX_IOCTL_SET_DLL_DEBUG_FLAGS = 0x70001 - TX_IOCTL_DLL_DEBUG_FLAG_J2534_CALLS = 0x00000001 + return Error_ID(result) From 7c84b7f02b8aea8b2e506d8cd00bb1b4737a8d47 Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Thu, 6 Aug 2026 14:23:57 +0300 Subject: [PATCH 2/7] TimeoutException refactoring --- udsoncan/client.py | 12 +++--------- udsoncan/exceptions.py | 8 ++++++-- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/udsoncan/client.py b/udsoncan/client.py index cac0c6c..7fc4982 100755 --- a/udsoncan/client.py +++ b/udsoncan/client.py @@ -2283,17 +2283,11 @@ def send_request(self, request: Request, timeout: int = -1) -> Optional[Response if spr_used: return None if timeout_type_used == 'single_request': - timeout_name_to_report = 'P2* timeout' if using_p2_star else 'P2 timeout' - timeout_value_to_report = single_request_timeout + raise TimeoutException(single_request_timeout, 'P2* timeout' if using_p2_star else 'P2 timeout') elif timeout_type_used == 'overall': - timeout_name_to_report = 'Global request timeout' - timeout_value_to_report = overall_timeout + raise TimeoutException(overall_timeout, 'Global request timeout') else: # Shouldn't go here. - timeout_name_to_report = 'Timeout' - timeout_value_to_report = timeout_value - - raise TimeoutException('Did not receive response in time. %s time has expired (timeout=%.3f sec)' % - (timeout_name_to_report, float(timeout_value_to_report))) + raise TimeoutException(timeout_value, 'Timeout') response = Response.from_payload(recv_payload) self.last_response = response diff --git a/udsoncan/exceptions.py b/udsoncan/exceptions.py index 239488c..ab82135 100755 --- a/udsoncan/exceptions.py +++ b/udsoncan/exceptions.py @@ -14,8 +14,12 @@ class TimeoutException(Exception): Simple extension of ``Exception`` with no additional property. Raised when a timeout in the communication happens. """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + def __init__(self, timeout, kind: str = 'Timeout'): + self.timeout = timeout + self.kind = kind + + def __str__(self): + return "Did not received frame in time (%s=%.3f sec)" % (self.kind, self.timeout) class NegativeResponseException(Exception): From 3f9f15384c6cf102442d2e4e13f071da751f82ca Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Thu, 6 Aug 2026 15:00:18 +0300 Subject: [PATCH 3/7] Fix setting TxFlags.CAN_29_BIT_ID after change IDs --- udsoncan/connections.py | 6 ++---- udsoncan/j2534.py | 5 +++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/udsoncan/connections.py b/udsoncan/connections.py index edb0729..b084367 100755 --- a/udsoncan/connections.py +++ b/udsoncan/connections.py @@ -700,13 +700,11 @@ def __init__(self, protocol = None, baudrate = 500000, ): - - BaseConnection.__init__(self, name) self.opened = False self.result = None - self.protocol = protocol if protocol else Protocol_ID.ISO15765 + self.protocol = protocol or Protocol_ID.ISO15765 self.baudrate = baudrate self.dll_debug = debug @@ -718,7 +716,7 @@ def __init__(self, raise RuntimeError('DLL not found') if (txid or rxid or extid) is not None: - self.logger.critical('txid, rxid, and extid are deprecated constructor arguments. Pass them, for example, "with J2534 Connection(windll) as conn: conn.set_can_id(txid, txid, extid)".') + self.logger.critical('txid, rxid, and extid are deprecated constructor arguments. Pass them, for example, "with J2534Connection(windll) as conn: conn.set_can_id(txid, txid, extid)".') self.open() self.set_can_id(txid, rxid, extid) diff --git a/udsoncan/j2534.py b/udsoncan/j2534.py index 872815e..6521fc3 100755 --- a/udsoncan/j2534.py +++ b/udsoncan/j2534.py @@ -516,6 +516,11 @@ def PassThruStartMsgFilter(self, ChannelID, protocol: Protocol_ID, txid: int, rx else: self.txFlags &= ~TxFlags.ISO15765_ADDR_TYPE.value + if txid >> 11: + self.txFlags |= TxFlags.CAN_29_BIT_ID.value + else: + self.txFlags &= ~TxFlags.CAN_29_BIT_ID.value + msgMask = PASSTHRU_MSG() msgMask.ProtocolID = protocol.value msgMask.TxFlags = self.txFlags From dcedcdc17bc5e9055591fd35d09b0592615dacfd Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Thu, 6 Aug 2026 15:14:03 +0300 Subject: [PATCH 4/7] Skip loopback messaages for someone tools --- udsoncan/j2534.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/udsoncan/j2534.py b/udsoncan/j2534.py index 6521fc3..0919327 100755 --- a/udsoncan/j2534.py +++ b/udsoncan/j2534.py @@ -458,7 +458,8 @@ def PassThruReadMsgs(self, ChannelID, protocol: Protocol_ID, pNumMsgs=1, Timeout # breakpoint() # Do not wrap in queue for avoid mixing timeout of usb connection and real server response Timeout. result = dllPassThruReadMsgs(ChannelID, byref(pMsg), byref(pNumMsgs), c_ulong(Timeout)) - if pMsg.RxStatus & (RxStatus.TX_INDICATION.value | RxStatus.TX_MSG_TYPE.value | RxStatus.START_OF_MESSAGE.value): + + if Error_ID(result) == Error_ID.ERR_SUCCESS and pMsg.RxStatus & (RxStatus.TX_INDICATION.value | RxStatus.TX_MSG_TYPE.value | RxStatus.START_OF_MESSAGE.value): continue return Error_ID(result), pMsg.getData(), pNumMsgs From 9c4b2bd72e5883f6066eb5e2a13b61cea5d5f8e0 Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Thu, 6 Aug 2026 15:54:19 +0300 Subject: [PATCH 5/7] Fix when repeat open --- udsoncan/connections.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/udsoncan/connections.py b/udsoncan/connections.py index b084367..3785840 100755 --- a/udsoncan/connections.py +++ b/udsoncan/connections.py @@ -734,6 +734,8 @@ def empty_rxqueue(self) -> None: pass def open(self) -> "J2534Connection": + if self.is_open(): + return try: # Open the interface (connect to the DLL) self.result, self.devID = self.interface.PassThruOpen() From 0564edd7ad6525a03e6bc753202f94924f728405 Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Sun, 9 Aug 2026 14:39:42 +0300 Subject: [PATCH 6/7] fix mypy warnings --- udsoncan/connections.py | 40 +++++---- udsoncan/j2534.py | 180 ++++++++++++++++------------------------ 2 files changed, 95 insertions(+), 125 deletions(-) diff --git a/udsoncan/connections.py b/udsoncan/connections.py index 3785840..8a49289 100755 --- a/udsoncan/connections.py +++ b/udsoncan/connections.py @@ -685,9 +685,9 @@ class J2534Connection(BaseConnection): protocol: "Protocol_ID" baudrate: int result: "Error_ID" - firmwareVersion: "ctypes.Array[ctypes.c_char]" - dllVersion: "ctypes.Array[ctypes.c_char]" - apiVersion: "ctypes.Array[ctypes.c_char]" + firmwareVersion: "str" + dllVersion: "str" + apiVersion: "str" opened: bool def __init__(self, @@ -703,7 +703,7 @@ def __init__(self, BaseConnection.__init__(self, name) self.opened = False - self.result = None + self.result = Error_ID.ERR_SUCCESS self.protocol = protocol or Protocol_ID.ISO15765 self.baudrate = baudrate self.dll_debug = debug @@ -711,12 +711,12 @@ def __init__(self, try: self.interface = J2534(windll) except AttributeError as e: - raise RuntimeError('DLL invalid: ' + str(e)) + raise RuntimeError("DLL invalid: " + str(e)) except FileNotFoundError: - raise RuntimeError('DLL not found') + raise RuntimeError("DLL not found") - if (txid or rxid or extid) is not None: - self.logger.critical('txid, rxid, and extid are deprecated constructor arguments. Pass them, for example, "with J2534Connection(windll) as conn: conn.set_can_id(txid, txid, extid)".') + if txid is not None: + self.logger.critical("Arguments txid, rxid, and extid are deprecated in the constructor. Pass them, for example, \"with J2534Connection(windll) as conn: conn.set_can_id(txid, txid, extid)\".") self.open() self.set_can_id(txid, rxid, extid) @@ -735,16 +735,16 @@ def empty_rxqueue(self) -> None: def open(self) -> "J2534Connection": if self.is_open(): - return + return self try: # Open the interface (connect to the DLL) self.result, self.devID = self.interface.PassThruOpen() except OSError as e: if e.errno in [0x16, 0xe06d7363]: - raise RuntimeError('J2534 Device busy') + raise RuntimeError("J2534 Device busy") exception_str = type(e).__name__ if e.errno is not None: - exception_str += ', %s' % e.errno + exception_str += ", %s" % e.errno raise RuntimeError(exception_str) self.log_last_operation("PassThruOpen", with_raise=True) @@ -759,8 +759,7 @@ def open(self) -> "J2534Connection": # Get the firmeware and DLL version etc, mainly for debugging output self.result, self.firmwareVersion, self.dllVersion, self.apiVersion = self.interface.PassThruReadVersion(self.devID) self.log_last_operation("PassThruReadVersion") - self.logger.info("J2534 FirmwareVersion: " + str(self.firmwareVersion.value) + ", dllVersoin: " + - str(self.dllVersion.value) + ", apiVersion" + str(self.apiVersion.value)) + self.logger.info("J2534 FirmwareVersion: %s, dllVersion: %s, apiVersion: %s." % (self.firmwareVersion, self.dllVersion, self.apiVersion)) # get the channel ID of the interface (used for subsequent communication) self.result, self.channelID = self.interface.PassThruConnect(self.devID, self.protocol, self.baudrate) @@ -786,19 +785,20 @@ def open(self) -> "J2534Connection": ] self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.SET_CONFIG, SCONFIG_LIST(configs)) - self.log_last_operation("PassThruIoctl SET_CONFIG") + self.log_last_operation("PassThruIoctl SET_CONFIG", with_raise=True) self.opened = True self.logger.info("J2534 Connection opened") + return self - def set_can_id(self, txid: int, rxid: int, extid: int=None): + def set_can_id(self, txid: int, rxid: Optional[int] = None, extid: Optional[int] = None) -> int: self.check_connection_opened() self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_MSG_FILTERS) self.log_last_operation("PassThruIoctl CLEAR_MSG_FILTERS", with_raise=True) # Set the filters and clear the read buffer (filters will be set based on tx/rxids) - self.result = self.interface.PassThruStartMsgFilter(self.channelID, self.protocol, txid, rxid, extid) + self.result, FilterID = self.interface.PassThruStartMsgFilter(self.channelID, txid, rxid, extid) self.log_last_operation("PassThruStartMsgFilter", with_raise=True) self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_RX_BUFFER) @@ -807,6 +807,8 @@ def set_can_id(self, txid: int, rxid: int, extid: int=None): self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_TX_BUFFER) self.log_last_operation("PassThruIoctl CLEAR_TX_BUFFER", with_raise=True) + return FilterID + def close(self) -> None: if not self.opened: return @@ -823,13 +825,15 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None): timeout = timeout or 0 - self.result = self.interface.PassThruWriteMsgs(self.channelID, payload, self.protocol, Timeout=int(timeout * 1000)) + self.result = self.interface.PassThruWriteMsgs(self.channelID, payload, Timeout=int(timeout * 1000)) self.log_last_operation("PassThruWriteMsgs", with_raise=True) def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: self.check_connection_opened() - self.result, data, numMessages = self.interface.PassThruReadMsgs(self.channelID, self.protocol, pNumMsgs=1, Timeout=int(timeout * 1000)) + timeout = timeout or 1 + + self.result, data, numMessages = self.interface.PassThruReadMsgs(self.channelID, pNumMsgs=1, Timeout=int(timeout * 1000)) if self.result in [Error_ID.ERR_BUFFER_EMPTY, Error_ID.ERR_TIMEOUT]: raise TimeoutException(timeout) diff --git a/udsoncan/j2534.py b/udsoncan/j2534.py index 0919327..2678373 100755 --- a/udsoncan/j2534.py +++ b/udsoncan/j2534.py @@ -274,51 +274,23 @@ def __init__(self, values): class J2534(): - dllPassThruOpen = None - dllPassThruClose = None - dllPassThruConnect = None - dllPassThruDisconnect = None - dllPassThruReadMsgs = None - dllPassThruWriteMsgs = None - dllPassThruStartPeriodicMsg = None - dllPassThruStopPeriodicMsg = None - dllPassThruReadVersion = None - dllPassThruGetLastError = None - dllPassThruStartMsgFilter = None - dllPassThruIoctl = None - - def __init__(self, windll, txid=None, rxid=None, extid=None): - assert (txid or rxid or extid) is None, 'txid, rxid, extid its legacy argumets. Pass into J2534.PassThruStartMsgFilter() method.' - - global dllPassThruOpen - global dllPassThruClose - global dllPassThruConnect - global dllPassThruDisconnect - global dllPassThruReadMsgs - global dllPassThruWriteMsgs - global dllPassThruStartPeriodicMsg - global dllPassThruStopPeriodicMsg - global dllPassThruReadVersion - global dllPassThruGetLastError - global dllPassThruStartMsgFilter - global dllPassThruIoctl - + def __init__(self, windll: str): self.hDLL = cdll.LoadLibrary(windll) dllPassThruOpenProto = WINFUNCTYPE( c_long, c_void_p, - POINTER(c_ulong)) - + POINTER(c_ulong), + ) dllPassThruOpenParams = (1, "pName", 0), (1, "pDeviceID", 0) - dllPassThruOpen = dllPassThruOpenProto(("PassThruOpen", self.hDLL), dllPassThruOpenParams) + self.dllPassThruOpen = dllPassThruOpenProto(("PassThruOpen", self.hDLL), dllPassThruOpenParams) dllPassThruCloseProto = WINFUNCTYPE( c_long, - c_ulong) - + c_ulong, + ) dllPassThruCloseParams = (1, "DeviceID", 0), - dllPassThruClose = dllPassThruCloseProto(("PassThruClose", self.hDLL), dllPassThruCloseParams) + self.dllPassThruClose = dllPassThruCloseProto(("PassThruClose", self.hDLL), dllPassThruCloseParams) dllPassThruConnectProto = WINFUNCTYPE( c_long, @@ -326,72 +298,72 @@ def __init__(self, windll, txid=None, rxid=None, extid=None): c_ulong, c_ulong, c_ulong, - POINTER(c_ulong)) - + POINTER(c_ulong), + ) dllPassThruConnectParams = (1, "DeviceID", 0), (1, "ProtocolID", 0), (1, "Flags", 0), (1, "BaudRate", 500000), (1, "pChannelID", 0) - dllPassThruConnect = dllPassThruConnectProto(("PassThruConnect", self.hDLL), dllPassThruConnectParams) + self.dllPassThruConnect = dllPassThruConnectProto(("PassThruConnect", self.hDLL), dllPassThruConnectParams) dllPassThruDisconnectProto = WINFUNCTYPE( c_long, - c_ulong) - + c_ulong, + ) dllPassThruDisconnectParams = (1, "ChannelID", 0), - dllPassThruDisconnect = dllPassThruDisconnectProto(("PassThruDisconnect", self.hDLL), dllPassThruDisconnectParams) + self.dllPassThruDisconnect = dllPassThruDisconnectProto(("PassThruDisconnect", self.hDLL), dllPassThruDisconnectParams) dllPassThruReadMsgsProto = WINFUNCTYPE( c_long, c_ulong, POINTER(PASSTHRU_MSG), POINTER(c_ulong), - c_ulong) - + c_ulong, + ) dllPassThruReadMsgsParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pNumMsgs", 0), (1, "Timeout", 0) - dllPassThruReadMsgs = dllPassThruReadMsgsProto(("PassThruReadMsgs", self.hDLL), dllPassThruReadMsgsParams) + self.dllPassThruReadMsgs = dllPassThruReadMsgsProto(("PassThruReadMsgs", self.hDLL), dllPassThruReadMsgsParams) dllPassThruWriteMsgsProto = WINFUNCTYPE( c_long, c_ulong, POINTER(PASSTHRU_MSG), POINTER(c_ulong), - c_ulong) - + c_ulong, + ) dllPassThruWriteMsgsParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pNumMsgs", 0), (1, "Timeout", 0) - dllPassThruWriteMsgs = dllPassThruWriteMsgsProto(("PassThruWriteMsgs", self.hDLL), dllPassThruWriteMsgsParams) + self.dllPassThruWriteMsgs = dllPassThruWriteMsgsProto(("PassThruWriteMsgs", self.hDLL), dllPassThruWriteMsgsParams) dllPassThruStartPeriodicMsgProto = WINFUNCTYPE( c_long, c_ulong, POINTER(PASSTHRU_MSG), POINTER(c_ulong), - c_ulong) - + c_ulong, + ) dllPassThruStartPeriodicMsgParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pMsgID", 0), (1, "TimeInterval", 0) - dllPassThruStartPeriodicMsg = dllPassThruStartPeriodicMsgProto(("PassThruStartPeriodicMsg", self.hDLL), dllPassThruStartPeriodicMsgParams) + self.dllPassThruStartPeriodicMsg = dllPassThruStartPeriodicMsgProto(("PassThruStartPeriodicMsg", self.hDLL), dllPassThruStartPeriodicMsgParams) dllPassThruStopPeriodicMsgProto = WINFUNCTYPE( c_long, c_ulong, - c_ulong) - + c_ulong, + ) dllPassThruStopPeriodicMsgParams = (1, "ChannelID", 0), (1, "MsgID", 0) - dllPassThruStopPeriodicMsg = dllPassThruStopPeriodicMsgProto(("PassThruStopPeriodicMsg", self.hDLL), dllPassThruStopPeriodicMsgParams) + self.dllPassThruStopPeriodicMsg = dllPassThruStopPeriodicMsgProto(("PassThruStopPeriodicMsg", self.hDLL), dllPassThruStopPeriodicMsgParams) dllPassThruReadVersionProto = WINFUNCTYPE( c_long, c_ulong, POINTER(c_char), POINTER(c_char), - POINTER(c_char)) - + POINTER(c_char), + ) dllPassThruReadVersionParams = (1, "DeviceID", 0), (1, "pFirmwareVersion", 0), (1, "pDllVersion", 0), (1, "pApiVersoin", 0) - dllPassThruReadVersion = dllPassThruReadVersionProto(("PassThruReadVersion", self.hDLL), dllPassThruReadVersionParams) + self.dllPassThruReadVersion = dllPassThruReadVersionProto(("PassThruReadVersion", self.hDLL), dllPassThruReadVersionParams) dllPassThruGetLastErrorProto = WINFUNCTYPE( c_long, POINTER(c_char), ) dllPassThruGetLastErrorParams = (1, "pErrorDescription", 0), - dllPassThruGetLastError = dllPassThruGetLastErrorProto(("PassThruGetLastError", self.hDLL), dllPassThruGetLastErrorParams) + self.dllPassThruGetLastError = dllPassThruGetLastErrorProto(("PassThruGetLastError", self.hDLL), dllPassThruGetLastErrorParams) dllPassThruStartMsgFilterProto = WINFUNCTYPE( c_long, @@ -400,88 +372,82 @@ def __init__(self, windll, txid=None, rxid=None, extid=None): POINTER(PASSTHRU_MSG), POINTER(PASSTHRU_MSG), POINTER(PASSTHRU_MSG), - POINTER(c_ulong) + POINTER(c_ulong), ) - dllPassThruStartMsgFilterParams = (1,"ChannelID",0), (1,"FilterType",0),(1,"pMaskMsg",0),(1,"pPatternMsg",0),(1,"pFlowControlMsg",0),(1,"pMsgID",0) - - dllPassThruStartMsgFilter = dllPassThruStartMsgFilterProto(("PassThruStartMsgFilter", self.hDLL), dllPassThruStartMsgFilterParams) + self.dllPassThruStartMsgFilter = dllPassThruStartMsgFilterProto(("PassThruStartMsgFilter", self.hDLL), dllPassThruStartMsgFilterParams) dllPassThruIoctlProto = WINFUNCTYPE( c_long, c_ulong, c_ulong, c_void_p, - c_void_p + c_void_p, ) - dllPassThruIoctlParams = (1, "Handle", 0), (1, "IoctlID", 0), (1, "pInput", 0), (1, "pOutput", 0) + self.dllPassThruIoctl = dllPassThruIoctlProto(("PassThruIoctl", self.hDLL), dllPassThruIoctlParams) - dllPassThruIoctl = dllPassThruIoctlProto(("PassThruIoctl", self.hDLL), dllPassThruIoctlParams) + def PassThruOpen(self): + DeviceID = c_ulong() - def PassThruOpen(self, pDeviceID=None): - if not pDeviceID: - pDeviceID = c_ulong() + result = self.dllPassThruOpen(bytes("J2534-2:", "ascii"), byref(DeviceID)) + return Error_ID(result), DeviceID - result = dllPassThruOpen(bytes('J2534-2:', 'ascii'), byref(pDeviceID)) - return Error_ID(result), pDeviceID - - def PassThruConnect(self, deviceID, protocol: Protocol_ID, baudrate, pChannelID=None): + def PassThruConnect(self, deviceID, protocol: Protocol_ID, baudrate: int): self.txFlags = TxFlags.NONE.value - - if protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: + self.protocol = protocol + if self.protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: self.txFlags |= TxFlags.ISO15765_FRAME_PAD.value connectFlags = ConnectFlags.CAN_ID_BOTH.value + ChannelID = c_ulong() - if not pChannelID: - pChannelID = c_ulong() - - result = dllPassThruConnect(deviceID, protocol.value, connectFlags, baudrate, byref(pChannelID)) - return Error_ID(result), pChannelID + result = self.dllPassThruConnect(deviceID, self.protocol.value, connectFlags, baudrate, byref(ChannelID)) + return Error_ID(result), ChannelID def PassThruClose(self, DeviceID): - result = dllPassThruClose(DeviceID) + result = self.dllPassThruClose(DeviceID) return Error_ID(result) def PassThruDisconnect(self, ChannelID): - result = dllPassThruDisconnect(ChannelID) + result = self.dllPassThruDisconnect(ChannelID) return Error_ID(result) - def PassThruReadMsgs(self, ChannelID, protocol: Protocol_ID, pNumMsgs=1, Timeout=1000): + def PassThruReadMsgs(self, ChannelID, pNumMsgs=1, Timeout=1000): pMsg = PASSTHRU_MSG() - pMsg.ProtocolID = protocol.value + pMsg.ProtocolID = self.protocol.value pNumMsgs = c_ulong(pNumMsgs) while 1: # breakpoint() # Do not wrap in queue for avoid mixing timeout of usb connection and real server response Timeout. - result = dllPassThruReadMsgs(ChannelID, byref(pMsg), byref(pNumMsgs), c_ulong(Timeout)) + result = self.dllPassThruReadMsgs(ChannelID, byref(pMsg), byref(pNumMsgs), c_ulong(Timeout)) if Error_ID(result) == Error_ID.ERR_SUCCESS and pMsg.RxStatus & (RxStatus.TX_INDICATION.value | RxStatus.TX_MSG_TYPE.value | RxStatus.START_OF_MESSAGE.value): continue return Error_ID(result), pMsg.getData(), pNumMsgs - def PassThruWriteMsgs(self, ChannelID, Data, protocol: Protocol_ID, pNumMsgs=1, Timeout=1000): + def PassThruWriteMsgs(self, ChannelID, Data, pNumMsgs=1, Timeout=1000): txmsg = PASSTHRU_MSG() txmsg.TxFlags = self.txFlags - txmsg.ProtocolID = protocol.value + txmsg.ProtocolID = self.protocol.value txmsg.setData(self.txid + Data) - result = dllPassThruWriteMsgs(ChannelID, byref(txmsg), byref(c_ulong(pNumMsgs)), c_ulong(Timeout)) + result = self.dllPassThruWriteMsgs(ChannelID, byref(txmsg), byref(c_ulong(pNumMsgs)), c_ulong(Timeout)) return Error_ID(result) def PassThruStartPeriodicMsg(self, ChannelID, Data, MsgID=0, TimeInterval=100): pMsg = PASSTHRU_MSG() + pMsg.ProtocolID = self.protocol.value pMsg.setData(Data) - result = dllPassThruStartPeriodicMsg(ChannelID, byref(pMsg), byref(c_ulong(MsgID)), c_ulong(TimeInterval)) + result = self.dllPassThruStartPeriodicMsg(ChannelID, byref(pMsg), byref(c_ulong(MsgID)), c_ulong(TimeInterval)) return Error_ID(result) def PassThruStopPeriodicMsg(self, ChannelID, MsgID): - result = dllPassThruStopPeriodicMsg(ChannelID, MsgID) + result = self.dllPassThruStopPeriodicMsg(ChannelID, MsgID) return Error_ID(result) @@ -490,29 +456,29 @@ def PassThruReadVersion(self, DeviceID): pDllVersion = (c_char * 80)() pApiVersion = (c_char * 80)() - result = dllPassThruReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion) - return Error_ID(result), pFirmwareVersion, pDllVersion, pApiVersion + result = self.dllPassThruReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion) + return Error_ID(result), pFirmwareVersion.value.decode(), pDllVersion.value.decode(), pApiVersion.value.decode() def PassThruGetLastError(self): pErrorDescription = (c_char * 80)() - result = dllPassThruGetLastError(pErrorDescription) + result = self.dllPassThruGetLastError(pErrorDescription) return Error_ID(result), pErrorDescription.value.decode() def PassThruIoctl(self, Handle, IoctlID, ioctlInput=None, ioctlOutput=None): pInput = None if ioctlInput is None else byref(ioctlInput) pOutput = None if ioctlOutput is None else byref(ioctlOutput) - result = dllPassThruIoctl(Handle, c_ulong(IoctlID.value), pInput, pOutput) + result = self.dllPassThruIoctl(Handle, c_ulong(IoctlID.value), pInput, pOutput) return Error_ID(result) - def PassThruStartMsgFilter(self, ChannelID, protocol: Protocol_ID, txid: int, rxid: int, extid: int = None): - self.txid = txid.to_bytes(4, 'big') - self.rxid = rxid.to_bytes(4, 'big') + def PassThruStartMsgFilter(self, ChannelID, txid: int, rxid: int, extid = None): + self.txid = txid.to_bytes(4, "big") + self.rxid = rxid.to_bytes(4, "big") if extid is not None: - self.txid += extid.to_bytes(1, 'big') - self.rxid += extid.to_bytes(1, 'big') + self.txid += extid.to_bytes(1, "big") + self.rxid += extid.to_bytes(1, "big") self.txFlags |= TxFlags.ISO15765_ADDR_TYPE.value else: self.txFlags &= ~TxFlags.ISO15765_ADDR_TYPE.value @@ -523,30 +489,30 @@ def PassThruStartMsgFilter(self, ChannelID, protocol: Protocol_ID, txid: int, rx self.txFlags &= ~TxFlags.CAN_29_BIT_ID.value msgMask = PASSTHRU_MSG() - msgMask.ProtocolID = protocol.value + msgMask.ProtocolID = self.protocol.value msgMask.TxFlags = self.txFlags msgMask.RxStatus = msgMask.ExtraDataIndex = 0xCCCC_CCCC - msgMask.setData(b'\xFF' * len(self.rxid)) + msgMask.setData(b"\xFF" * len(self.rxid)) msgPattern = PASSTHRU_MSG() - msgPattern.ProtocolID = protocol.value + msgPattern.ProtocolID = self.protocol.value msgPattern.TxFlags = self.txFlags msgPattern.RxStatus = msgPattern.ExtraDataIndex = 0xCCCC_CCCC msgPattern.setData(self.rxid) - if protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: + if self.protocol in [Protocol_ID.ISO15765, Protocol_ID.ISO15765_PS, Protocol_ID.SW_ISO15765_PS]: filterType = c_ulong(Filter.FLOW_CONTROL_FILTER.value) msgFlow = PASSTHRU_MSG() - msgFlow.ProtocolID = protocol.value + msgFlow.ProtocolID = self.protocol.value msgFlow.TxFlags = self.txFlags msgFlow.RxStatus = msgFlow.ExtraDataIndex = 0xCCCC_CCCC msgFlow.setData(self.txid) - msgFlow = byref(msgFlow) + pMsgFlow = byref(msgFlow) else: filterType = c_ulong(Filter.PASS_FILTER.value) - msgFlow = None + pMsgFlow = None - msgID = c_ulong(0) + FilterID = c_ulong(0) - result = dllPassThruStartMsgFilter(ChannelID, filterType, byref(msgMask), byref(msgPattern), msgFlow, byref(msgID)) - return Error_ID(result) + result = self.dllPassThruStartMsgFilter(ChannelID, filterType, byref(msgMask), byref(msgPattern), pMsgFlow, byref(FilterID)) + return Error_ID(result), FilterID From d4a91d2e53f2ac835b9f1710232c9c396165059a Mon Sep 17 00:00:00 2001 From: Kirill <33geek@gmail.com> Date: Sun, 9 Aug 2026 14:47:19 +0300 Subject: [PATCH 7/7] Automatic resolve rxid from txid --- udsoncan/connections.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/udsoncan/connections.py b/udsoncan/connections.py index 8a49289..2ed3a37 100755 --- a/udsoncan/connections.py +++ b/udsoncan/connections.py @@ -794,6 +794,14 @@ def open(self) -> "J2534Connection": def set_can_id(self, txid: int, rxid: Optional[int] = None, extid: Optional[int] = None) -> int: self.check_connection_opened() + if rxid is None: + if 0 <= txid <= 0xFF: + (txid, rxid) = (0x18DA00F1 | (txid << 8), 0x18DAF100 | txid) + elif 0x700 <= txid <= 0x7FF: + rxid = txid + 8 + else: + assert False, "txid must between 0 and 0xFF or between 0x700 and 0x7FF for automatic resolve rxid." + self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_MSG_FILTERS) self.log_last_operation("PassThruIoctl CLEAR_MSG_FILTERS", with_raise=True)