Closed catmemo closed 1 year ago
@pylessard
hello pylessard, i have the same issues, this is my code:
import isotp import logging import udsoncan
from can.interfaces.vector import VectorBus # CANaylzer from udsoncan.connections import PythonIsoTpConnection from udsoncan.client import Client
udsoncan.setup_logging()
logger = logging.getLogger('UDSdemo_CANaylzer') logger.setLevel(level=logging.INFO) handler = logging.FileHandler("udsoncan.log") handler.setLevel(logging.INFO) formatter = logging.Formatter(fmt='%(asctime)s [%(levelname)s] %(name)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
handler.setFormatter(formatter) console = logging.StreamHandler() console.setLevel(logging.INFO) logger.addHandler(handler)
status_mask = 0xAF
isotp_params = { 'stmin': 32, # Will request the sender to wait 32ms between consecutive frame. 0-127ms
'blocksize': 8, # Request the sender to send 8 consecutives frames before sending a new
# flow control message
'wftmax': 0, # Number of wait frame allowed before triggering an error
'll_data_length': 8, # Link layer (CAN layer) works with 8 byte payload (CAN 2.0)
'tx_padding': 0, # Will pad all transmitted CAN messages with byte 0x00. None means no padding
'rx_flowcontrol_timeout': 1000, # Triggers a timeout if a flow control is awaited for more than 1000 milliseconds
'rx_consecutive_frame_timeout': 1000,
# Triggers a timeout if a consecutive frame is awaited for more than 1000 milliseconds
'squash_stmin_requirement': False,
# When sending, respect the stmin requirement of the receiver. If set to True, go as fast as possible.
'max_frame_size': 4095 # Limit the size of receive frame.
}
bus = VectorBus(channel=0, bitrate=500000)
tp_address = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x78A, rxid=0x7CA)
stack = isotp.CanStack(bus=bus, address=tp_address, params=isotp_params) # Network/Transport layer (IsoTP protocol) conn = PythonIsoTpConnection(stack) # interface between Application and Transport layer, Define the connection using
def intToBytes(value, length): # 十进制转bytes result = [] for i in range(0, length): result.append(value >> (i * 8) & 0xff) result.reverse() return result
with Client(conn, request_timeout=1000) as client: # Application layer (UDS protocol) response = client.get_dtc_by_status_mask(status_mask) # 19022F
I understand that you use your bus object for other functionality. You probably encounter an issue that many are encountering. Your other feature consume the messages from the b us queue, meaning that the isotp stack never receives them, therefore UDS throw a Timeout error.
The clean solution would be to modify the CanStack object to use a bus listener that duplicates each messages before consuming them. That's on my todo list (https://github.com/pylessard/python-can-isotp/issues/76)
There is many workaround to this. The simplest is that you write your own txfn and rxfn function and use the TransportLayer object. The glue code is easy to write, see how I did : https://github.com/pylessard/python-can-isotp/blob/master/isotp/protocol.py#L1108 BAsically, you can make a dispatching mechanism that feeds only the isotp message to the stack and redirect other message to the right portion of your project.
You can also try to do the lsitener approach, I accept PRs :)
There is at least 5 or 6 other issues on the isotp project reporting something similar.
Hi @pylessard,
Thanks for looking into this and thank you for your suggest! But it's strange that it works well with vector and only got errors when I tried to use PCAN. Isn't that a problem with pcan?
If 'GLOBAL_DICT['bus'].shutdown()' is removed, I'll get another error (maybe the case you mentioned above):
Traceback (most recent call last):
File "D:\Development_Tasks\CANSimulator\gui.py", line 1288, in start_can_bus_operation
dpg.get_value(self.response_id))
File "D:\Development_Tasks\CANSimulator\send_diag_request.py", line 186, in __init__
bus = can.interface.Bus(bustype=bustype1, app_name='Pcan', channel='PCAN_USBBUS1', bitrate=bitrate1)
File "D:\Development_Tools\Python\lib\site-packages\can\interface.py", line 120, in __new__
bus = cls(channel, *args, **kwargs)
File "D:\Development_Tools\Python\lib\site-packages\can\interfaces\pcan\pcan.py", line 245, in __init__
raise PcanCanInitializationError(self._get_formatted_error(result))
can.interfaces.pcan.pcan.PcanCanInitializationError: A PCAN Channel has not been initialized yet or the initialization process has failed
Is your bus object working? It says that it is not initialized. Are you able to send messages without isotp?
You can enable logging in debug mode to get more info. A raw can log would also help.
I must say that i find your GLOBAL_DICT strange. Are you sure you manage your initialization sequence properly?
Hi, I initialize it in a class - SendCANMessages:
class SendCANMessages:
def __init__(self, bustype1, bustype2, channel1, channel2, bitrate1, bitrate2):
if bustype1 != "None":
if bustype1 == "pcan":
channel1 = 'PCAN_USBBUS' + str(int(channel1))
else:
channel1 = str(int(channel1) - 1)
self.bus1 = can.ThreadSafeBus(bustype=bustype1, app_name=None, channel=channel1, bitrate=bitrate1, receive_own_messages=True, name="BUS1")
if bustype2 != "None":
if bustype2 == "pcan":
channel2 = 'PCAN_USBBUS' + str(int(channel2))
else:
channel2 = str(int(channel2) - 1)
self.bus2 = can.ThreadSafeBus(bustype=bustype2, app_name=None, channel=channel2, bitrate=bitrate2, receive_own_messages=True, name="BUS2")
And when I launch the test tool, I call this function, pass the parameters to it to initialize, and then add it to GLOBAL_DICT:
self.send_can_messages_obj = SendCANMessages(self.device_type1, self.device_type2, self.device_channel1, self.device_channel2, self.device_baudrates1, self.device_baudrates2)
self.bus1 = self.send_can_messages_obj.bus1
GLOBAL_DICT["bus1"] = self.bus1
self.bus2 = self.send_can_messages_obj.bus2
GLOBAL_DICT["bus2"] = self.bus2
And it works with tx and rx messages well when I used GLOBAL_DICT["bus1"] or GLOBAL_DICT["bus2"]. But when I tried to use GLOBAL_DICT["bus1"] or GLOBAL_DICT["bus2"] in diagnostic, it failed.
2023-03-23 16:27:51 [INFO] Connection: Connection opened
2023-03-23 16:27:56 [INFO] UdsClient: DiagnosticSessionControl<0x10> - Switching session to defaultSession (0x01)
2023-03-23 16:27:56 [DEBUG] Connection: Sending 2 bytes : [b'1001']
2023-03-23 16:28:06 [DEBUG] Connection: No data received: [TimeoutException] - Did not receive frame IsoTP Transport layer in time (timeout=10 sec)
2023-03-23 16:28:06 [ERROR] UdsClient: [TimeoutException] : Did not receive response in time. P2 timeout time has expired (timeout=10.000 sec)
Exception in thread Thread-3:
Traceback (most recent call last):
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 1647, in send_request
payload = self.conn.wait_frame(timeout=timeout_value, exception=True)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\connections.py", line 68, in wait_frame
frame = self.specific_wait_frame(timeout=timeout)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\connections.py", line 490, in specific_wait_frame
raise TimeoutException("Did not receive frame IsoTP Transport layer in time (timeout=%s sec)" % timeout)
udsoncan.exceptions.TimeoutException: Did not receive frame IsoTP Transport layer in time (timeout=10 sec)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Development_Tools\Python\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "D:\Development_Tools\Python\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "D:\Development_Tasks\CANSimulator\send_diag_request.py", line 289, in send_diag_session_control
session_response = self.client.change_session(DiagnosticSessionControl.Session.defaultSession)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 131, in decorated
return func(self, *args, **kwargs)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 189, in change_session
response = self.send_request(req)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 1655, in send_request
raise TimeoutException('Did not receive response in time. %s time has expired (timeout=%.3f sec)' % (timeout_name_to_report, timeout_value))
udsoncan.exceptions.TimeoutException: Did not receive response in time. P2 timeout time has expired (timeout=10.000 sec)
So I wrote the code like that to create a new bus:
if bustype1 == 'None' and bustype2 != 'None':
if bustype2 == 'pcan':
self.bus = can.interface.Bus(bustype='pcan', app_name='Pcan', channel='PCAN_USBBUS2', bitrate=500000)
else:
self.bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=1, bitrate=500000)
else:
if bustype1 == 'pcan':
self.bus = can.interface.Bus(bustype='pcan', app_name='Pcan', channel='PCAN_USBBUS1', bitrate=500000)
else:
self.bus = can.interface.Bus(bustype='vector', app_name='CANalyzer', channel=0, bitrate=500000)
And when I used vector can, it works:
2023-03-23 15:48:30 [INFO] Connection: Connection opened
2023-03-23 15:48:36 [INFO] UdsClient: DiagnosticSessionControl<0x10> - Switching session to defaultSession (0x01)
2023-03-23 15:48:36 [DEBUG] Connection: Sending 2 bytes : [b'1001']
2023-03-23 15:48:37 [DEBUG] Connection: Received 6 bytes : [b'5001003200c8']
2023-03-23 15:48:37 [INFO] UdsClient: Received positive response for service DiagnosticSessionControl (0x10) from server.
However, when I tried to make it works with pcan, it returned "PcanCanInitializationError".
Looks like you have issue with making python-can work with pcan. That is a bit out of the scope of this project. Try to debug your bus object and sends raw messages without uds or isotp.
You may want to check with the python-can community
Hi pylessard,
Thank you for your patience, but it works well in sending and receiving messages. As you see:
I'll review my code later. Thanks again!
Out of curiosity, are you playing with threads? This lib is not thread safe. Send and recv are, but not the other methods
Hi, Yes~ create the bus and call them in threads~ So this is the problem?
If you don't call uds or isotp layer across threads, that should be fine. The isotp connection is thread safe.
I understand that you use your bus object for other functionality. You probably encounter an issue that many are encountering. Your other feature consume the messages from the b us queue, meaning that the isotp stack never receives them, therefore UDS throw a Timeout error.
The clean solution would be to modify the CanStack object to use a bus listener that duplicates each messages before consuming them. That's on my todo list (pylessard/python-can-isotp#76)
There is many workaround to this. The simplest is that you write your own txfn and rxfn function and use the TransportLayer object. The glue code is easy to write, see how I did : https://github.com/pylessard/python-can-isotp/blob/master/isotp/protocol.py#L1108 BAsically, you can make a dispatching mechanism that feeds only the isotp message to the stack and redirect other message to the right portion of your project.
You can also try to do the lsitener approach, I accept PRs :)
There is at least 5 or 6 other issues on the isotp project reporting something similar.
Hi, pylessard
I tried to take your advice but failed again, there is my code:
class CanStack(isotp.TransportLayer):
def _tx_canbus_3plus(self, msg):
if msg.arbitration_id == 1835 or msg.arbitration_id == 1963:
self.bus.send(can.Message(arbitration_id=msg.arbitration_id, data=msg.data, is_extended_id=msg.is_extended_id, is_fd=msg.is_fd, bitrate_switch=msg.bitrate_switch))
def _tx_canbus_3minus(self, msg):
if msg.arbitration_id == 1835 or msg.arbitration_id == 1963:
self.bus.send(can.Message(arbitration_id=msg.arbitration_id, data=msg.data, extended_id=msg.is_extended_id, is_fd=msg.is_fd, bitrate_switch=msg.bitrate_switch))
def rx_canbus(self):
msg = self.bus.recv(self.timeout)
if msg is not None:
if msg.arbitration_id == 1835 or msg.arbitration_id == 1963:
return isotp.CanMessage(arbitration_id=msg.arbitration_id, data=msg.data, extended_id=msg.is_extended_id, is_fd=msg.is_fd, bitrate_switch=msg.bitrate_switch)
def __init__(self, bus, timeout=0.0, *args, **kwargs):
message_input_args = can.Message.__init__.__code__.co_varnames[:can.Message.__init__.__code__.co_argcount]
if 'is_extended_id' in message_input_args:
self.tx_canbus = self._tx_canbus_3plus
else:
self.tx_canbus = self._tx_canbus_3minus
self.set_bus(bus)
self.timeout = timeout
isotp.TransportLayer.__init__(self, rxfn=self.rx_canbus, txfn=self.tx_canbus, *args, **kwargs)
def set_bus(self, bus):
if not isinstance(bus, can.BusABC):
raise ValueError('bus must be a python-can BusABC object')
self.bus = bus
And the response:
2023-03-28 20:19:41 [INFO] Connection: Connection opened
2023-03-28 20:19:45 [INFO] UdsClient: DiagnosticSessionControl<0x10> - Switching session to defaultSession (0x01)
2023-03-28 20:19:45 [DEBUG] Connection: Sending 2 bytes : [b'1001']
Exception in thread Thread-3:
2023-03-28 20:19:55 [DEBUG] Connection: No data received: [TimeoutException] - Did not receive frame IsoTP Transport layer in time (timeout=10 sec)
2023-03-28 20:19:55 [ERROR] UdsClient: [TimeoutException] : Did not receive response in time. P2 timeout time has expired (timeout=10.000 sec)
Traceback (most recent call last):
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 1647, in send_request
payload = self.conn.wait_frame(timeout=timeout_value, exception=True)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\connections.py", line 68, in wait_frame
frame = self.specific_wait_frame(timeout=timeout)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\connections.py", line 490, in specific_wait_frame
raise TimeoutException("Did not receive frame IsoTP Transport layer in time (timeout=%s sec)" % timeout)
udsoncan.exceptions.TimeoutException: Did not receive frame IsoTP Transport layer in time (timeout=10 sec)
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "D:\Development_Tools\Python\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "D:\Development_Tools\Python\lib\threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "D:\Development_Tasks\CANSimulator\send_diag_request.py", line 258, in send_diag_session_control
session_response = self.client.change_session(DiagnosticSessionControl.Session.defaultSession)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 131, in decorated
return func(self, *args, **kwargs)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 189, in change_session
response = self.send_request(req)
File "D:\Development_Tools\Python\lib\site-packages\udsoncan\client.py", line 1655, in send_request
raise TimeoutException('Did not receive response in time. %s time has expired (timeout=%.3f sec)' % (timeout_name_to_report, timeout_value))
udsoncan.exceptions.TimeoutException: Did not receive response in time. P2 timeout time has expired (timeout=10.000 sec)
And I have to say it got the correct response in PCAN-View:
Could you please help to check when you are free? Thanks in advance!
@pylessard
If you have another section of your program rwading the bus, it will deplete the rx queue of the bus. You need to read a message and based on the ID, redirect to isotp or aomething else.
The best solution is to use a listener
If you have another section of your program rwading the bus, it will deplete the rx queue of the bus. You need to read a message and based on the ID, redirect to isotp or aomething else.
The best solution is to use a listener
Hi, I solve this issue with this method, thanks again!
如果你有程序的另一部分在总线上旋转,它将耗尽总线的 rx 队列。您需要阅读一条消息,并根据 ID 重定向到 isotp 或 aomething else。 最好的解决方案是使用侦听器
嗨,我用这种方法解决了这个问题,再次感谢!
How to use this method to solve it, I do not understand
I have already created a bus in another python file and added it to a global dict - GLOBAL_DICT. And I tried to use it as below:
But got error as below:
However, it works well when I changed the code as follows:
And then, I tried to added the new bus to GLOBAL_DICT, and it got the same error again:
I printed the GLOBAL_DICT before and after, but it seems normal:
I'm so confused about it. Could anyone help to check? Thanks in advance!