-
-
Notifications
You must be signed in to change notification settings - Fork 221
Refactoring J2534 #289
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
pylessard
merged 6 commits into
pylessard:master
from
kirya-dev:feature/refactoring-j2534
Feb 1, 2026
+147
−108
Merged
Refactoring J2534 #289
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
937babb
Refactoring J2534:
kirya-dev 24f4e23
Ignore start_of_message, limit=20
kirya-dev 434abcc
Improve KLine connection
kirya-dev ad6f95a
fix pr comments
kirya-dev 5a5fbef
Improve K-Line set filters
kirya-dev f6b4685
fix numpy
kirya-dev File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,7 @@ | |
| _import_isotp_err = e | ||
|
|
||
| try: | ||
| from udsoncan.j2534 import J2534, TxStatusFlag, Protocol_ID, Error_ID, Ioctl_Flags, Ioctl_ID, SCONFIG_LIST | ||
| from udsoncan.j2534 import J2534, Protocol_ID, Error_ID, Ioctl_Flags, Ioctl_ID, SCONFIG_LIST | ||
| _import_j2534_err = None | ||
| except Exception as e: | ||
| _import_j2534_err = e | ||
|
|
@@ -63,8 +63,7 @@ def send(self, data: Union[bytes, Request, Response], timeout: Optional[float] = | |
|
|
||
| :returns: None | ||
| """ | ||
| if not self.is_open(): | ||
| raise RuntimeError("Connection is not opened") | ||
| self.check_connection_opened() | ||
|
|
||
| if isinstance(data, Request) or isinstance(data, Response): | ||
| payload = data.get_payload() | ||
|
|
@@ -79,6 +78,10 @@ def send(self, data: Union[bytes, Request, Response], timeout: Optional[float] = | |
| else: | ||
| self.specific_send(payload) | ||
|
|
||
| def check_connection_opened(self) -> None: | ||
| if not self.is_open(): | ||
| raise RuntimeError(self.__class__.__name__ + ' is not opened') | ||
|
|
||
| def wait_frame(self, timeout: Optional[float] = None, exception: bool = False) -> Optional[bytes]: | ||
| """Waits for the reception of a frame of data from the underlying transport protocol | ||
|
|
||
|
|
@@ -92,8 +95,7 @@ def wait_frame(self, timeout: Optional[float] = None, exception: bool = False) - | |
| :returns: Received data | ||
| :rtype: bytes or None | ||
| """ | ||
| if not self.is_open(): | ||
| raise RuntimeError("Connection is not opened") | ||
| self.check_connection_opened() | ||
|
|
||
| try: | ||
| frame = self.specific_wait_frame(timeout=timeout) | ||
|
|
@@ -243,21 +245,13 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None) -> None | |
| self.sock.send(payload) | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("Connection is not open") | ||
| self.check_connection_opened() | ||
|
|
||
| timedout = False | ||
| frame = None | ||
| try: | ||
| frame = self.rxqueue.get(block=True, timeout=timeout) | ||
| return self.rxqueue.get(block=True, timeout=timeout) | ||
| except queue.Empty: | ||
| timedout = True | ||
|
|
||
| if timedout: | ||
| raise TimeoutException("Did not received frame in time (timeout=%s sec)" % timeout) | ||
|
|
||
| return frame | ||
|
|
||
| def empty_rxqueue(self) -> None: | ||
| while not self.rxqueue.empty(): | ||
| self.rxqueue.get() | ||
|
|
@@ -359,22 +353,13 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None) -> None | |
| self.tpsock.send(payload) | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("Connection is not open") | ||
| self.check_connection_opened() | ||
|
|
||
| timedout = False | ||
| frame = None | ||
| try: | ||
| frame = self.rxqueue.get(block=True, timeout=timeout) | ||
|
|
||
| return self.rxqueue.get(block=True, timeout=timeout) | ||
| except queue.Empty: | ||
| timedout = True | ||
|
|
||
| if timedout: | ||
| raise TimeoutException("Did not received ISOTP frame in time (timeout=%s sec)" % timeout) | ||
|
|
||
| return frame | ||
|
|
||
| def empty_rxqueue(self) -> None: | ||
| while not self.rxqueue.empty(): | ||
| self.rxqueue.get() | ||
|
|
@@ -442,17 +427,12 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None) -> None | |
| self.touserqueue.put(payload, block=True, timeout=timeout) | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("Connection is not open") | ||
| self.check_connection_opened() | ||
|
|
||
| timedout = False | ||
| frame = None | ||
| try: | ||
| frame = self.fromuserqueue.get(block=True, timeout=timeout) | ||
| except queue.Empty: | ||
| timedout = True | ||
|
|
||
| if timedout: | ||
| raise TimeoutException("Did not receive frame from user queue in time (timeout=%s sec)" % timeout) | ||
|
|
||
| if self.mtu is not None: | ||
|
|
@@ -577,8 +557,7 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None) -> None | |
| self.isotp_layer.send(payload, send_timeout=timeout) | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("Connection is not opened") | ||
| self.check_connection_opened() | ||
|
|
||
| frame = self.isotp_layer.recv(block=True, timeout=timeout) | ||
| if frame is None: | ||
|
|
@@ -649,25 +628,15 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None): | |
| self.toIsoTPQueue.put(bytearray(payload)) # isotp.protocol.TransportLayer uses byte array. udsoncan is strict on bytes format | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("Connection is not open") | ||
| self.check_connection_opened() | ||
|
|
||
| timedout = False | ||
| frame = None | ||
| try: | ||
| frame = self.fromIsoTPQueue.get(block=True, timeout=timeout) | ||
| # isotp.protocol.TransportLayer uses bytearray. udsoncan is strict on bytes format | ||
| return bytes(frame) | ||
| except queue.Empty: | ||
| timedout = True | ||
|
|
||
| if timedout: | ||
| raise TimeoutException("Did not receive IsoTP frame from the Transport layer in time (timeout=%s sec)" % timeout) | ||
|
|
||
| if frame is None: | ||
| return None | ||
|
|
||
| # isotp.protocol.TransportLayer uses bytearray. udsoncan is strict on bytes format | ||
| return bytes(frame) | ||
|
|
||
| def empty_rxqueue(self) -> None: | ||
| while not self.fromIsoTPQueue.empty(): | ||
| self.fromIsoTPQueue.get() | ||
|
|
@@ -776,13 +745,26 @@ def __init__(self, | |
| self.result, self.channelID = self.interface.PassThruConnect(self.devID, self.protocol.value, self.baudrate) | ||
| self.log_last_operation("PassThruConnect", with_raise=True) | ||
|
|
||
| configs = SCONFIG_LIST([ | ||
| configs = [ | ||
| (Ioctl_ID.DATA_RATE.value, self.baudrate), | ||
| (Ioctl_ID.LOOPBACK.value, 0), | ||
| (Ioctl_ID.ISO15765_BS.value, 0x20), | ||
| (Ioctl_ID.ISO15765_STMIN.value, 0), | ||
| ]) | ||
| self.result = self.interface.PassThruIoctl(self.channelID, Ioctl_ID.SET_CONFIG, configs) | ||
| ] | ||
| if self.protocol in [Protocol_ID.ISO9141, Protocol_ID.ISO14230]: | ||
| configs += [ | ||
| (Ioctl_ID.P1_MAX.value, 40), | ||
| (Ioctl_ID.P3_MIN.value, 110), | ||
| (Ioctl_ID.P4_MIN.value, 10), | ||
| (Ioctl_ID.TIDLE.value, 300), | ||
| (Ioctl_ID.TWUP.value, 50), | ||
| (Ioctl_ID.TINL.value, 25), | ||
| ] | ||
| elif self.protocol in [Protocol_ID.ISO15765]: | ||
| 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) | ||
|
|
@@ -804,7 +786,7 @@ def __init__(self, | |
|
|
||
| def open(self) -> "J2534Connection": | ||
| self.exit_requested = False | ||
| self.sem = threading.Semaphore() | ||
| self.interfaceSemaphore = threading.Semaphore() | ||
| self.rxthread = threading.Thread(target=self.rxthread_task, daemon=True) | ||
| self.rxthread.start() | ||
| self.opened = True | ||
|
|
@@ -822,15 +804,15 @@ def is_open(self) -> bool: | |
|
|
||
| def rxthread_task(self) -> None: | ||
| while not self.exit_requested: | ||
| self.sem.acquire() | ||
| self.interfaceSemaphore.acquire() | ||
| try: | ||
| result, data, numMessages = self.interface.PassThruReadMsgs(self.channelID, self.protocol.value, 1, 1) | ||
| 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.sem.release() | ||
| self.interfaceSemaphore.release() | ||
| time.sleep(0.001) | ||
|
|
||
| def log_last_operation(self, exec_method: str, with_raise = False) -> None: | ||
|
|
@@ -860,27 +842,19 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None): | |
| timeout = 0 if timeout is None else timeout | ||
|
|
||
| # Fix for avoid ERR_CONCURRENT_API_CALL. Stop reading | ||
| self.sem.acquire() | ||
| 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.sem.release() | ||
| self.interfaceSemaphore.release() | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("J2534 Connection is not open") | ||
| self.check_connection_opened() | ||
|
|
||
| timedout = False | ||
| frame = None | ||
| try: | ||
| frame = self.rxqueue.get(block=True, timeout=timeout) | ||
| return self.rxqueue.get(block=True, timeout=timeout) | ||
| except queue.Empty: | ||
| timedout = True | ||
|
|
||
| if timedout: | ||
| raise TimeoutException("Did not received response from J2534 RxQueue (timeout=%s sec)" % timeout) | ||
|
|
||
| return frame | ||
|
|
||
| def empty_rxqueue(self) -> None: | ||
| while not self.rxqueue.empty(): | ||
| self.rxqueue.get() | ||
|
|
@@ -942,21 +916,13 @@ def specific_send(self, payload: bytes, timeout: Optional[float] = None): | |
| self.rxqueue.put(self.ResponseData[payload]) | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened: | ||
| raise RuntimeError("Fake Connection is not open") | ||
| self.check_connection_opened() | ||
|
|
||
| timedout = False | ||
| frame = None | ||
| try: | ||
| frame = self.rxqueue.get(block=True, timeout=timeout) | ||
| return self.rxqueue.get(block=True, timeout=timeout) | ||
| except queue.Empty: | ||
| timedout = True | ||
|
|
||
| if timedout: | ||
| raise TimeoutException("Did not received response from J2534 RxQueue (timeout=%s sec)" % timeout) | ||
|
|
||
| return frame | ||
|
|
||
| def empty_rxqueue(self) -> None: | ||
| while not self.rxqueue.empty(): | ||
| self.rxqueue.get() | ||
|
|
@@ -1002,13 +968,14 @@ def __init__(self, rx_id: int, tx_id: int, name: Optional[str] = None, *args, ** | |
| self.opened = False | ||
|
|
||
| def specific_send(self, payload: bytes, timeout: Optional[float] = None) -> None: | ||
| if self.conn is None or not self.opened: | ||
| raise RuntimeError("Connection is not opened") | ||
| self.check_connection_opened() | ||
| assert self.conn is not None | ||
|
|
||
| self.conn.send(payload) | ||
|
|
||
| def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes]: | ||
| if not self.opened or self.conn is None: | ||
| raise RuntimeError("Connection is not open") | ||
| self.check_connection_opened() | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same issue here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed in SyncISOTPNetwork.is_open() |
||
| assert self.conn is not None | ||
|
|
||
| frame = cast(Optional[bytes], self.conn.recv(timeout)) | ||
|
|
||
|
|
@@ -1034,7 +1001,7 @@ def empty_rxqueue(self) -> None: | |
| self.conn.empty() | ||
|
|
||
| def is_open(self) -> bool: | ||
| return self.opened | ||
| return self.conn is not None and self.opened | ||
|
|
||
| def __enter__(self) -> "SyncAioIsotpConnection": | ||
| return self | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change breaks mypy.
You can add an assert to make it happy :
assert self.conn is not NoneThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in SyncISOTPNetwork.is_open()