66import sys
77from abc import ABC , abstractmethod
88import time
9- from typing import Union , Any , Dict
9+ from typing import Union , Dict
1010import ctypes
1111import selectors
1212
@@ -83,7 +83,7 @@ def wait_frame(self, timeout: Optional[float] = None, exception: bool = False) -
8383 """Waits for the reception of a frame of data from the underlying transport protocol
8484
8585 :param timeout: The maximum amount of time to wait before giving up in seconds
86- :type timeout: int
86+ :type timeout: float
8787 :param exception: Boolean value indicating if this function may return exceptions.
8888 When ``True``, all exceptions may be raised, including ``TimeoutException``
8989 When ``False``, all exceptions will be logged as ``DEBUG`` and ``None`` will be returned.
@@ -128,7 +128,7 @@ def specific_wait_frame(self, timeout: Optional[float] = None) -> Optional[bytes
128128 """The implementation of the ``wait_frame`` method.
129129
130130 :param timeout: The maximum amount of time to wait before giving up
131- :type timeout: int
131+ :type timeout: float
132132
133133 :returns: Received data
134134 :rtype: bytes or None
@@ -728,22 +728,28 @@ class J2534Connection(BaseConnection):
728728 opened : bool
729729
730730 def __init__ (self , windll : str , rxid : int , txid : int , name : Optional [str ] = None , debug : bool = False , * args , ** kwargs ):
731-
732731 BaseConnection .__init__ (self , name )
733732
734- # Determine mode ID29 or ID11
735- txFlags = TxStatusFlag .ISO15765_CAN_ID_29 .value if txid >> 11 else TxStatusFlag .ISO15765_CAN_ID_11 .value
736-
737733 # Set up a J2534 interface using the DLL provided
738- self .interface = J2534 (windll = windll , rxid = rxid , txid = txid , txFlags = txFlags )
734+ try :
735+ self .interface = J2534 (windll = windll , rxid = rxid , txid = txid )
736+ except FileNotFoundError :
737+ raise RuntimeError ('DLL not found' )
739738
740739 # Set the protocol to ISO15765, Baud rate to 500000
741740 self .protocol = Protocol_ID .ISO15765
742741 self .baudrate = 500000
743742 self .debug = debug
744743
745- # Open the interface (connect to the DLL)
746- result , self .devID = self .interface .PassThruOpen ()
744+ try :
745+ # Open the interface (connect to the DLL)
746+ self .result , self .devID = self .interface .PassThruOpen ()
747+ except OSError as e :
748+ if e .errno in [0x16 , 0xe06d7363 ]:
749+ raise RuntimeError ('J2534 Device busy' )
750+ raise RuntimeError ('%s, %X' % (type (e ).__name__ , e .errno ))
751+
752+ self .log_last_operation ("PassThruOpen" , with_raise = True )
747753
748754 if debug :
749755 self .result = self .interface .PassThruIoctl (0 ,
@@ -759,7 +765,7 @@ def __init__(self, windll: str, rxid: int, txid: int, name: Optional[str] = None
759765
760766 # get the channel ID of the interface (used for subsequent communication)
761767 self .result , self .channelID = self .interface .PassThruConnect (self .devID , self .protocol .value , self .baudrate )
762- self .log_last_operation ("PassThruConnect" )
768+ self .log_last_operation ("PassThruConnect" , with_raise = True )
763769
764770 configs = SCONFIG_LIST ([
765771 (Ioctl_ID .DATA_RATE .value , 500000 ),
@@ -805,7 +811,6 @@ def is_open(self) -> bool:
805811 return self .opened
806812
807813 def rxthread_task (self ) -> None :
808-
809814 while not self .exit_requested :
810815 try :
811816 result , data , numMessages = self .interface .PassThruReadMsgs (self .channelID , self .protocol .value , 1 , 1 )
@@ -815,20 +820,28 @@ def rxthread_task(self) -> None:
815820 self .logger .critical ("Exiting J2534 rx thread" )
816821 self .exit_requested = True
817822
818- def log_last_operation (self , exec_method : str ) -> None :
819- res , pErrDescr = self .interface .PassThruGetLastError ()
823+ def log_last_operation (self , exec_method : str , with_raise = False ) -> None :
820824 if self .result != Error_ID .ERR_SUCCESS :
821- self .logger .error ("J2534 %s: %s %s" % (exec_method , self .result , pErrDescr ))
825+ res , pErrDescr = self .interface .PassThruGetLastError ()
826+ err = "J2534 %s: %s (%s)" % (exec_method , pErrDescr , self .result )
827+ self .logger .error (err )
828+ if with_raise :
829+ raise RuntimeError (err )
830+ return
822831
823832 elif self .debug :
824833 self .logger .debug ("J2534 %s: OK" % (exec_method ))
825834
826835 def close (self ) -> None :
836+ self .opened = False
827837 self .exit_requested = True
828838 self .rxthread .join ()
839+
829840 self .result = self .interface .PassThruDisconnect (self .channelID )
830- self .opened = False
831- self .log_last_operation ("Connection closed" )
841+ self .log_last_operation ('PassThruDisconnect' )
842+
843+ self .interface .PassThruClose (self .devID )
844+ self .log_last_operation ('PassThruClose' )
832845
833846 def specific_send (self , payload : bytes , timeout : Optional [float ] = None ):
834847 if timeout is None :
@@ -855,6 +868,16 @@ def empty_rxqueue(self) -> None:
855868 while not self .rxqueue .empty ():
856869 self .rxqueue .get ()
857870
871+ def read_vbatt (self , digits = 1 ) -> float :
872+ vbatt = ctypes .POINTER (ctypes .c_int32 )()
873+
874+ self .result = self .interface .PassThruIoctl (self .channelID , Ioctl_ID .READ_VBATT , None , vbatt )
875+ self .log_last_operation ("PassThruIoctl READ_VBATT" )
876+
877+ value = ctypes .cast (vbatt , ctypes .c_void_p ).value
878+
879+ return round (value / 1000 , digits ) if value else 0
880+
858881
859882class FakeConnection (BaseConnection ):
860883 """
0 commit comments