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/connections.py b/udsoncan/connections.py index 88093c0..2ed3a37 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) @@ -689,17 +685,15 @@ 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]" - rxqueue: "queue.Queue[bytes]" - exit_requested: bool + firmwareVersion: "str" + dllVersion: "str" + apiVersion: "str" 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, @@ -708,29 +702,54 @@ def __init__(self, ): BaseConnection.__init__(self, name) - self.protocol = protocol if protocol else Protocol_ID.ISO15765 + self.opened = False + self.result = Error_ID.ERR_SUCCESS + self.protocol = protocol or 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 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) + + 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": + if self.is_open(): + return self + 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') + 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) - 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,11 @@ 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.logger.info("J2534 FirmwareVersion: " + str(self.firmwareVersion.value) + ", dllVersoin: " + - str(self.dllVersion.value) + ", apiVersion" + str(self.apiVersion.value)) + self.log_last_operation("PassThruReadVersion") + 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.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,108 +778,88 @@ 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), ] self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.SET_CONFIG, SCONFIG_LIST(configs)) - self.log_last_operation("PassThruIoctl SET_CONFIG") - - self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_MSG_FILTERS) - self.log_last_operation("PassThruIoctl CLEAR_MSG_FILTERS") - - # 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.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_RX_BUFFER) - self.log_last_operation("PassThruIoctl CLEAR_RX_BUFFER") + self.log_last_operation("PassThruIoctl SET_CONFIG", 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') + self.logger.info("J2534 Connection opened") return self - def __enter__(self) -> "J2534Connection": - return self + def set_can_id(self, txid: int, rxid: Optional[int] = None, extid: Optional[int] = None) -> int: + self.check_connection_opened() - def __exit__(self, type, value, traceback) -> None: - self.close() + 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." - def is_open(self) -> bool: - return self.opened + self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_MSG_FILTERS) + self.log_last_operation("PassThruIoctl CLEAR_MSG_FILTERS", with_raise=True) - 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) + # Set the filters and clear the read buffer (filters will be set based on tx/rxids) + self.result, FilterID = self.interface.PassThruStartMsgFilter(self.channelID, txid, rxid, extid) + self.log_last_operation("PassThruStartMsgFilter", with_raise=True) - 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 + self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.CLEAR_RX_BUFFER) + self.log_last_operation("PassThruIoctl CLEAR_RX_BUFFER", with_raise=True) - elif self.debug: - self.logger.debug("J2534 %s: OK" % (exec_method)) + 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 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() - # 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() + timeout = timeout or 0 + + 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() - 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) + timeout = timeout or 1 - def empty_rxqueue(self) -> None: - while not self.rxqueue.empty(): - self.rxqueue.get() + 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) + + 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 +921,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 +980,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/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): diff --git a/udsoncan/j2534.py b/udsoncan/j2534.py index 936323e..2678373 100755 --- a/udsoncan/j2534.py +++ b/udsoncan/j2534.py @@ -1,351 +1,36 @@ -import ctypes -from ctypes import Structure, WINFUNCTYPE, POINTER, cast, c_long, c_void_p, c_ulong, byref # type: ignore - from enum import Enum - -import logging - - -class PASSTHRU_MSG(Structure): - _fields_ = [("ProtocolID", c_ulong), - ("RxStatus", c_ulong), - ("TxFlags", c_ulong), - ("Timestamp", c_ulong), - ("DataSize", c_ulong), - ("ExtraDataIndex", c_ulong), - ("Data", ctypes.c_ubyte * 4128)] - - def setData(self, data: bytes): - self.DataSize = len(data) - for i in range(self.DataSize): - self.Data[i] = data[i] - - def getData(self): - addr_size = 5 if self.RxStatus & RxStatus.ISO15765_ADDR_TYPE.value else 4 - return bytes(self.Data[addr_size : self.DataSize]) - - -class SCONFIG(Structure): - _fields_ = [("Parameter", c_ulong), - ("Value", c_ulong)] - - -class SCONFIG_LIST(Structure): - _fields_ = [("NumOfParams", c_ulong), - ("ConfigPtr", POINTER(SCONFIG))] - - def __init__(self, values): - self.NumOfParams = len(values) - self.ConfigPtr = (SCONFIG * self.NumOfParams)(*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, rxid, txid, extid): - global dllPassThruOpen - global dllPassThruClose - global dllPassThruConnect - global dllPassThruDisconnect - global dllPassThruReadMsgs - global dllPassThruWriteMsgs - global dllPassThruStartPeriodicMsg - global dllPassThruStopPeriodicMsg - global dllPassThruReadVersion - global dllPassThruGetLastError - 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() - - dllPassThruOpenProto = WINFUNCTYPE( - c_long, - c_void_p, - POINTER(c_ulong)) - - dllPassThruOpenParams = (1, "pName", 0), (1, "pDeviceID", 0) - dllPassThruOpen = dllPassThruOpenProto(("PassThruOpen", self.hDLL), dllPassThruOpenParams) - - dllPassThruCloseProto = WINFUNCTYPE( - c_long, - c_ulong) - - dllPassThruCloseParams = (1, "DeviceID", 0), - dllPassThruClose = dllPassThruCloseProto(("PassThruClose", self.hDLL), dllPassThruCloseParams) - - dllPassThruConnectProto = WINFUNCTYPE( - c_long, - c_ulong, - c_ulong, - c_ulong, - 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) - - dllPassThruDisconnectProto = WINFUNCTYPE( - c_long, - c_ulong) - - dllPassThruDisconnectParams = (1, "ChannelID", 0), - dllPassThruDisconnect = dllPassThruDisconnectProto(("PassThruDisconnect", self.hDLL), dllPassThruDisconnectParams) - - dllPassThruReadMsgsProto = WINFUNCTYPE( - c_long, - c_ulong, - POINTER(PASSTHRU_MSG), - POINTER(c_ulong), - c_ulong) - - dllPassThruReadMsgsParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pNumMsgs", 0), (1, "Timeout", 0) - dllPassThruReadMsgs = dllPassThruReadMsgsProto(("PassThruReadMsgs", self.hDLL), dllPassThruReadMsgsParams) - - dllPassThruWriteMsgsProto = WINFUNCTYPE( - c_long, - c_ulong, - POINTER(PASSTHRU_MSG), - POINTER(c_ulong), - c_ulong) - - dllPassThruWriteMsgsParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pNumMsgs", 0), (1, "Timeout", 0) - dllPassThruWriteMsgs = dllPassThruWriteMsgsProto(("PassThruWriteMsgs", self.hDLL), dllPassThruWriteMsgsParams) - - dllPassThruStartPeriodicMsgProto = WINFUNCTYPE( - c_long, - c_ulong, - POINTER(PASSTHRU_MSG), - POINTER(c_ulong), - c_ulong) - - dllPassThruStartPeriodicMsgParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pMsgID", 0), (1, "TimeInterval", 0) - dllPassThruStartPeriodicMsg = dllPassThruStartPeriodicMsgProto(("PassThruStartPeriodicMsg", self.hDLL), dllPassThruStartPeriodicMsgParams) - - dllPassThruStopPeriodicMsgProto = WINFUNCTYPE( - c_long, - c_ulong, - c_ulong) - - dllPassThruStopPeriodicMsgParams = (1, "ChannelID", 0), (1, "MsgID", 0) - dllPassThruStopPeriodicMsg = dllPassThruStopPeriodicMsgProto(("PassThruStopPeriodicMsg", self.hDLL), dllPassThruStopPeriodicMsgParams) - - dllPassThruReadVersionProto = WINFUNCTYPE( - c_long, - c_ulong, - POINTER(ctypes.c_char), - POINTER(ctypes.c_char), - POINTER(ctypes.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), - ) - dllPassThruGetLastErrorParams = (1, "pErrorDescription", 0), - dllPassThruGetLastError = dllPassThruGetLastErrorProto(("PassThruGetLastError", self.hDLL), dllPassThruGetLastErrorParams) - - dllPassThruStartMsgFilterProto = WINFUNCTYPE( - c_long, - c_ulong, - c_ulong, - POINTER(PASSTHRU_MSG), - POINTER(PASSTHRU_MSG), - POINTER(PASSTHRU_MSG), - 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) - - dllPassThruIoctlProto = WINFUNCTYPE( - c_long, - c_ulong, - c_ulong, - c_void_p, - c_void_p - ) - - dllPassThruIoctlParams = (1, "Handle", 0), (1, "IoctlID", 0), (1, "pInput", 0), (1, "pOutput", 0) - - dllPassThruIoctl = dllPassThruIoctlProto(("PassThruIoctl", self.hDLL), dllPassThruIoctlParams) - - def PassThruOpen(self, pDeviceID=None): - if not pDeviceID: - pDeviceID = ctypes.c_ulong() - - result = dllPassThruOpen(bytes('J2534-2:', 'ascii'), byref(pDeviceID)) - return Error_ID(hex(result)), pDeviceID - - 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 - - def PassThruClose(self, DeviceID): - result = dllPassThruClose(DeviceID) - return Error_ID(hex(result)) - - def PassThruDisconnect(self, ChannelID): - result = dllPassThruDisconnect(ChannelID) - return Error_ID(hex(result)) - - def PassThruReadMsgs(self, ChannelID, protocol, pNumMsgs=1, Timeout=20): - pMsg = PASSTHRU_MSG() - pMsg.ProtocolID = protocol - - pNumMsgs = c_ulong(pNumMsgs) - - while 1: - # breakpoint() - 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())) - - txmsg = PASSTHRU_MSG() - txmsg.TxFlags = self.txFlags - txmsg.ProtocolID = protocol - txmsg.setData(Data) - - result = dllPassThruWriteMsgs(ChannelID, byref(txmsg), byref(c_ulong(pNumMsgs)), c_ulong(Timeout)) - - return Error_ID(hex(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)) - - def PassThruStopPeriodicMsg(self, ChannelID, MsgID): - result = dllPassThruStopPeriodicMsg(ChannelID, MsgID) - - return Error_ID(hex(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) - - return Error_ID(hex(result)), pFirmwareVersion, pDllVersion, pApiVersion - - def PassThruGetLastError(self): - pErrorDescription = (ctypes.c_char * 80)() - result = dllPassThruGetLastError(pErrorDescription) - - return Error_ID(hex(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(hex(result)) - - def PassThruStartMsgFilter(self, ChannelID, protocol): - msgMask = PASSTHRU_MSG() - msgMask.ProtocolID = protocol - msgMask.TxFlags = self.txFlags - msgMask.RxStatus = msgMask.ExtraDataIndex = 0xCCCC_CCCC - msgMask.setData(b'\xFF' * len(self.rxid)) - - msgPattern = PASSTHRU_MSG() - msgPattern.ProtocolID = protocol - 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: - filterType = c_ulong(Filter.FLOW_CONTROL_FILTER.value) - msgFlow = PASSTHRU_MSG() - msgFlow.ProtocolID = protocol - msgFlow.TxFlags = self.txFlags - msgFlow.RxStatus = msgFlow.ExtraDataIndex = 0xCCCC_CCCC - msgFlow.setData(self.txid) - msgFlow = byref(msgFlow) - - msgID = c_ulong(0) - - result = dllPassThruStartMsgFilter(ChannelID, filterType, byref(msgMask), byref(msgPattern), msgFlow, byref(msgID)) - - return Error_ID(hex(result)) +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 = 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) + 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): @@ -553,3 +238,281 @@ 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): + _fields_ = [("ProtocolID", c_ulong), + ("RxStatus", c_ulong), + ("TxFlags", c_ulong), + ("Timestamp", c_ulong), + ("DataSize", c_ulong), + ("ExtraDataIndex", c_ulong), + ("Data", c_ubyte * 4128)] + + def setData(self, data: bytes): + self.DataSize = len(data) + for i in range(self.DataSize): + self.Data[i] = data[i] + + def getData(self): + addr_size = 5 if self.RxStatus & RxStatus.ISO15765_ADDR_TYPE.value else 4 + return bytes(self.Data[addr_size : self.DataSize]) + + +class SCONFIG(Structure): + _fields_ = [("Parameter", c_ulong), + ("Value", c_ulong)] + + +class SCONFIG_LIST(Structure): + _fields_ = [("NumOfParams", c_ulong), + ("ConfigPtr", POINTER(SCONFIG))] + + def __init__(self, values): + self.NumOfParams = len(values) + self.ConfigPtr = (SCONFIG * self.NumOfParams)(*values) + + +class J2534(): + def __init__(self, windll: str): + self.hDLL = cdll.LoadLibrary(windll) + + dllPassThruOpenProto = WINFUNCTYPE( + c_long, + c_void_p, + POINTER(c_ulong), + ) + dllPassThruOpenParams = (1, "pName", 0), (1, "pDeviceID", 0) + self.dllPassThruOpen = dllPassThruOpenProto(("PassThruOpen", self.hDLL), dllPassThruOpenParams) + + dllPassThruCloseProto = WINFUNCTYPE( + c_long, + c_ulong, + ) + dllPassThruCloseParams = (1, "DeviceID", 0), + self.dllPassThruClose = dllPassThruCloseProto(("PassThruClose", self.hDLL), dllPassThruCloseParams) + + dllPassThruConnectProto = WINFUNCTYPE( + c_long, + c_ulong, + c_ulong, + c_ulong, + c_ulong, + POINTER(c_ulong), + ) + dllPassThruConnectParams = (1, "DeviceID", 0), (1, "ProtocolID", 0), (1, "Flags", 0), (1, "BaudRate", 500000), (1, "pChannelID", 0) + self.dllPassThruConnect = dllPassThruConnectProto(("PassThruConnect", self.hDLL), dllPassThruConnectParams) + + dllPassThruDisconnectProto = WINFUNCTYPE( + c_long, + c_ulong, + ) + dllPassThruDisconnectParams = (1, "ChannelID", 0), + self.dllPassThruDisconnect = dllPassThruDisconnectProto(("PassThruDisconnect", self.hDLL), dllPassThruDisconnectParams) + + dllPassThruReadMsgsProto = WINFUNCTYPE( + c_long, + c_ulong, + POINTER(PASSTHRU_MSG), + POINTER(c_ulong), + c_ulong, + ) + dllPassThruReadMsgsParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pNumMsgs", 0), (1, "Timeout", 0) + self.dllPassThruReadMsgs = dllPassThruReadMsgsProto(("PassThruReadMsgs", self.hDLL), dllPassThruReadMsgsParams) + + dllPassThruWriteMsgsProto = WINFUNCTYPE( + c_long, + c_ulong, + POINTER(PASSTHRU_MSG), + POINTER(c_ulong), + c_ulong, + ) + dllPassThruWriteMsgsParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pNumMsgs", 0), (1, "Timeout", 0) + self.dllPassThruWriteMsgs = dllPassThruWriteMsgsProto(("PassThruWriteMsgs", self.hDLL), dllPassThruWriteMsgsParams) + + dllPassThruStartPeriodicMsgProto = WINFUNCTYPE( + c_long, + c_ulong, + POINTER(PASSTHRU_MSG), + POINTER(c_ulong), + c_ulong, + ) + dllPassThruStartPeriodicMsgParams = (1, "ChannelID", 0), (1, "pMsg", 0), (1, "pMsgID", 0), (1, "TimeInterval", 0) + self.dllPassThruStartPeriodicMsg = dllPassThruStartPeriodicMsgProto(("PassThruStartPeriodicMsg", self.hDLL), dllPassThruStartPeriodicMsgParams) + + dllPassThruStopPeriodicMsgProto = WINFUNCTYPE( + c_long, + c_ulong, + c_ulong, + ) + dllPassThruStopPeriodicMsgParams = (1, "ChannelID", 0), (1, "MsgID", 0) + self.dllPassThruStopPeriodicMsg = dllPassThruStopPeriodicMsgProto(("PassThruStopPeriodicMsg", self.hDLL), dllPassThruStopPeriodicMsgParams) + + dllPassThruReadVersionProto = WINFUNCTYPE( + c_long, + c_ulong, + POINTER(c_char), + POINTER(c_char), + POINTER(c_char), + ) + dllPassThruReadVersionParams = (1, "DeviceID", 0), (1, "pFirmwareVersion", 0), (1, "pDllVersion", 0), (1, "pApiVersoin", 0) + self.dllPassThruReadVersion = dllPassThruReadVersionProto(("PassThruReadVersion", self.hDLL), dllPassThruReadVersionParams) + + dllPassThruGetLastErrorProto = WINFUNCTYPE( + c_long, + POINTER(c_char), + ) + dllPassThruGetLastErrorParams = (1, "pErrorDescription", 0), + self.dllPassThruGetLastError = dllPassThruGetLastErrorProto(("PassThruGetLastError", self.hDLL), dllPassThruGetLastErrorParams) + + dllPassThruStartMsgFilterProto = WINFUNCTYPE( + c_long, + c_ulong, + c_ulong, + POINTER(PASSTHRU_MSG), + POINTER(PASSTHRU_MSG), + POINTER(PASSTHRU_MSG), + POINTER(c_ulong), + ) + dllPassThruStartMsgFilterParams = (1,"ChannelID",0), (1,"FilterType",0),(1,"pMaskMsg",0),(1,"pPatternMsg",0),(1,"pFlowControlMsg",0),(1,"pMsgID",0) + self.dllPassThruStartMsgFilter = dllPassThruStartMsgFilterProto(("PassThruStartMsgFilter", self.hDLL), dllPassThruStartMsgFilterParams) + + dllPassThruIoctlProto = WINFUNCTYPE( + c_long, + c_ulong, + c_ulong, + 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) + + def PassThruOpen(self): + DeviceID = c_ulong() + + result = self.dllPassThruOpen(bytes("J2534-2:", "ascii"), byref(DeviceID)) + return Error_ID(result), DeviceID + + def PassThruConnect(self, deviceID, protocol: Protocol_ID, baudrate: int): + self.txFlags = TxFlags.NONE.value + 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() + + result = self.dllPassThruConnect(deviceID, self.protocol.value, connectFlags, baudrate, byref(ChannelID)) + return Error_ID(result), ChannelID + + def PassThruClose(self, DeviceID): + result = self.dllPassThruClose(DeviceID) + return Error_ID(result) + + def PassThruDisconnect(self, ChannelID): + result = self.dllPassThruDisconnect(ChannelID) + return Error_ID(result) + + def PassThruReadMsgs(self, ChannelID, pNumMsgs=1, Timeout=1000): + pMsg = PASSTHRU_MSG() + 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 = 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, pNumMsgs=1, Timeout=1000): + txmsg = PASSTHRU_MSG() + txmsg.TxFlags = self.txFlags + txmsg.ProtocolID = self.protocol.value + txmsg.setData(self.txid + Data) + + 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 = self.dllPassThruStartPeriodicMsg(ChannelID, byref(pMsg), byref(c_ulong(MsgID)), c_ulong(TimeInterval)) + return Error_ID(result) + + def PassThruStopPeriodicMsg(self, ChannelID, MsgID): + result = self.dllPassThruStopPeriodicMsg(ChannelID, MsgID) + + return Error_ID(result) + + def PassThruReadVersion(self, DeviceID): + pFirmwareVersion = (c_char * 80)() + pDllVersion = (c_char * 80)() + pApiVersion = (c_char * 80)() + + 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 = 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 = self.dllPassThruIoctl(Handle, c_ulong(IoctlID.value), pInput, pOutput) + return Error_ID(result) + + 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.txFlags |= TxFlags.ISO15765_ADDR_TYPE.value + 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 = self.protocol.value + msgMask.TxFlags = self.txFlags + msgMask.RxStatus = msgMask.ExtraDataIndex = 0xCCCC_CCCC + msgMask.setData(b"\xFF" * len(self.rxid)) + + msgPattern = PASSTHRU_MSG() + msgPattern.ProtocolID = self.protocol.value + msgPattern.TxFlags = self.txFlags + msgPattern.RxStatus = msgPattern.ExtraDataIndex = 0xCCCC_CCCC + msgPattern.setData(self.rxid) + + 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 = self.protocol.value + msgFlow.TxFlags = self.txFlags + msgFlow.RxStatus = msgFlow.ExtraDataIndex = 0xCCCC_CCCC + msgFlow.setData(self.txid) + pMsgFlow = byref(msgFlow) + else: + filterType = c_ulong(Filter.PASS_FILTER.value) + pMsgFlow = None + + FilterID = c_ulong(0) + + result = self.dllPassThruStartMsgFilter(ChannelID, filterType, byref(msgMask), byref(msgPattern), pMsgFlow, byref(FilterID)) + return Error_ID(result), FilterID