Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 3 additions & 9 deletions udsoncan/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
201 changes: 100 additions & 101 deletions udsoncan/connections.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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[<name>]``
:type name: string
:param debug: This will enable windows debugging mode in the dll (see tactrix doc for additional information)
Expand All @@ -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,
Expand All @@ -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)])
Expand All @@ -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 = [
Expand All @@ -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)
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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

Expand Down
8 changes: 6 additions & 2 deletions udsoncan/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Loading