pylessard / python-udsoncan

Python implementation of UDS (ISO-14229) standard.
MIT License
586 stars 202 forks source link

There is no sample code for a client and server. Need some sample code to use the library. I am trying to make a client. My server is a embedded board(which is working fine).Please explain how to send a UDS message of a particular SID and DID to the server. #65

Closed mchaudh4 closed 4 years ago

pylessard commented 4 years ago

The documentation is full of example. Start by reading it, then try to write some code. If you have issues, show what you did and you may get help.

You can also read the code to get insight. Looking at unit yeat is a good way to learn about all the edge cases

mchaudh4 commented 4 years ago

Thank you pylessard for responding to my problem. I am average in python and completely new to UDS. Working on a problem, where i have to send a UDS message through my laptop with SID=0x22 and DID= 0x504 to a server(an embedded board, which i am sure is working fine) and the expected output message is SID=0x62 and DID= 0x504 to test the python UDS. For example,

Message 03 22 05 04 00 00 00 00

Response 07 62 05 04 01 60 00 00

I have a spend many hours on it, still not able to do this work. Also i want to know, do UDS messages have something like identifier as in CAN messages?

pylessard commented 4 years ago

Hi SID 0x22 is ReadDataByIdentifier. There is an example right here in the documentation : https://udsoncan.readthedocs.io/en/latest/udsoncan/examples.html#reading-a-did-with-readdatabyidentifier

First make sure your Connection is working and that you can send/receive data. Then use the UDS client and configure your Codec to tell the library how the data in your message is formatted.

UDS message doesn't have an ID like CAN messages, but they have the Service ID (SID). It defines what type of request is made, just like a command number.

If you have trouble with programming and/or UDS, I suggest you go on StackOverflow, this site is made for that.

Regards

mchaudh4 commented 4 years ago

Thank you pylessard. image Is there some error in the statement - from udsoncan.Connection import IsoTPConnection I think, it should be replaced with udsoncan.connections import IsoTPConnection

I am going to use Peak CAN USB adaptor for connecting to laptop(windows OS) with the embedded board. I hope that is fine?

Also, conn = IsoTPConnection('vcan0', rxid=0x123, txid=0x456) what are rxid and txid?

pylessard commented 4 years ago

Indeed, there's a typo in the example. Actually, I renamed the file in the past and forgot to update the example.

If you use windows, then you won't be able to use the IsotPConnection. Go for PythonIsoTpConnection which will use python-can as CAN layer which is compatible with Windows and also Peak device.

rxid and txid are the CAN ID to use when IsoTP uses Normal Addressing. This is all defined by ISO-15765. You may not be aware, but UDS works over a tranport layer called IsoTP. The connection object is the bridge between the UDS layer and the transport layer. This transport layer is handle by another module that I wrote and that you will need to install : can-isotp.

IsoTP addressing mode are explained in the documentation of the can-isotp module. https://can-isotp.readthedocs.io/en/latest/isotp/addressing.html

mchaudh4 commented 4 years ago

Thank you pylessard for this great help. I will do the testing now on Monday with the hardware. I have installed all the required libraries. Few more questions: Do i need to do any change in the below lines of your code?

config = dict(udsoncan.configs.default_client_config) config['data_identifiers'] = { 0x1234 : MyCustomCodecThatShiftBy4, # Uses own custom defined codec. Giving the class is ok 0x1235 : MyCustomCodecThatShiftBy4(), # Same as 0x1234, giving an instance is good also 0xF190 : udsoncan.AsciiCodec(15) # Codec that read ASCII string. We must tell the length of the string }

I am thinking of keeping them as Default.

I will put my system request and response CAN identifier in place of 0x123 and 0x456 in the below Code:

conn = PythonIsoTpConnection('vcan0', rxid=0x123, txid=0x456)

I will put my DID Number at the place of 0xF190 in the below code:

with Client(conn, request_timeout=2, config=config) as client: response = client.read_data_by_identifier(0xF190) print(response.service_data.values[0xF190]) # This is a dict of DID:Value

vin = client.read_data_by_identifier_first(0xF190)

I am hoping that i will get some kind of response from the system if our embedded server is working properly. Thank you for creating such a useful library.

pylessard commented 4 years ago

Hi, I am sorry, but I don't really want to write your code. You need to learn a bit of what you're doing, not just copy paste and ask if that is ok.

MyCustomCodecThatShiftBy4 is an example, you don't need that.

As for the PythonIsoTpConnection, it doesn't expect the same parameter as the other connection. Look at the documentation. There is a good example that shows how to setup the stack with python-can.
https://udsoncan.readthedocs.io/en/latest/udsoncan/examples.html#using-uds-over-python-can

vcan0 is a virtual CAN bus that only exist on Linux. Since you use a Peak device, this is definitely not adequate.

Regards

mchaudh4 commented 4 years ago

There are multiple libraries involved, which makes it a little difficult. Thank you for your inputs.

mchaudh4 commented 4 years ago

Hi pylessard, Working on the same problem. The connection using Peak CAN adaptor is working correctly. Below is the Code.

import isotp from can.interfaces.pcan import PcanBus from udsoncan.connections import PythonIsoTpConnection from udsoncan.client import Client import udsoncan.configs

config = dict(udsoncan.configs.default_client_config)

isotp_params = { 'stmin' : 32, # Will request the sender to wait 32ms between consecutive frame. 0-127ms or 100-900ns with values from 0xF1-0xF9 '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. }

bus = PcanBus(channel='PCAN_USBBUS1', bitrate=500000) # Link Layer (CAN protocol)

tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x7C2, rxid=0x7C3) # Network layer addressing scheme

stack = isotp.CanStack(bus=bus, address=tp_addr, params=isotp_params) # Network/Transport layer (IsoTP protocol)

conn = PythonIsoTpConnection(stack)

with Client(conn, request_timeout=2, config=config) as client: response = client.read_data_by_identifier(0x504) print(response.service_data.values[0x504]) # This is a dict of DID:Value


I am getting an error: image

How to do the data identifier configuration? Please forward some link for this?

pylessard commented 4 years ago

Hi, since the beginning, I keep forwarding link to the documentation I carefully wrote. Please, read it.

https://udsoncan.readthedocs.io/en/latest/udsoncan/examples.html#reading-a-did-with-readdatabyidentifier https://udsoncan.readthedocs.io/en/latest/udsoncan/client.html#data_identifiers

Add the data_identifiers entry to your client configuration and provide a Codec per DID you want to read.

mchaudh4 commented 4 years ago

Tried to understand the problem. Reading your codes, i did a small modification in my code. import isotp from can.interfaces.pcan import PcanBus from udsoncan.connections import PythonIsoTpConnection from udsoncan.client import Client import udsoncan.configs

udsoncan.setup_logging()

config = dict(udsoncan.configs.default_client_config) config['data_identifiers'] = { 0x0203 : udsoncan.DidCodec('@') }

isotp_params = { 'stmin' : 32, # Will request the sender to wait 32ms between consecutive frame. 0-127ms or 100-900ns with values from 0xF1-0xF9 '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. }

bus = PcanBus(channel='PCAN_USBBUS1', bitrate=500000) # Link Layer (CAN protocol)

tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x7C2, rxid=0x7C3) # Network layer addressing scheme

stack = isotp.CanStack(bus=bus, address=tp_addr, params=isotp_params) # Network/Transport layer (IsoTP protocol)

conn = PythonIsoTpConnection(stack)

with Client(conn, request_timeout=2, config=config) as client: response = client.read_data_by_identifier(0x0203) print(response.service_data.values[0x0203]) # This is a dict of DID:Value

The result i am getting is: image The result matches the result of our C# GUI. But the code ended with error. Still not sure how to do the DID Configuration. Please provide the guidance.

pylessard commented 4 years ago

Hi, Can you please do some font formatting on your code when copying it?

Codecs converts payload to a usable values in Python. 0x0203 : udsoncan.DidCodec('@') This does nothing. How would you convert this raw data 0000400000001800 into a value ? It is specific to your application.

mchaudh4 commented 4 years ago

I apologize for the unformatted code. Please review the formatted code below:

import isotp
from can.interfaces.pcan import PcanBus
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.client import Client
import udsoncan.configs

udsoncan.setup_logging()

config = dict(udsoncan.configs.default_client_config)
config['data_identifiers'] = { 0x0203 : udsoncan.DidCodec('@') }

isotp_params = {
   'stmin' : 32,                          # Will request the sender to wait 32ms between consecutive frame. 0-127ms or 100-900ns with values from 0xF1-0xF9
   '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.
}

bus = PcanBus(channel='PCAN_USBBUS1', bitrate=500000)                                          # Link Layer (CAN protocol)

tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x7C2, rxid=0x7C3) # Network layer addressing scheme

stack = isotp.CanStack(bus=bus, address=tp_addr, params=isotp_params)               # Network/Transport layer (IsoTP protocol)

conn = PythonIsoTpConnection(stack)

with Client(conn,  request_timeout=2, config=config) as client:
   response = client.read_data_by_identifier(0x0203)
   print(response.service_data.values[0x0203]) # This is a dict of DID:Value

Doing some hardware testing work of knowing the error by reading the DID and then clearing the error by writing a DID to prevent the system from stopping. I need to put the data in some variable, check its equality and them write the error clearing DID. Working with UDS for the first time. Thank you for this great help.

pylessard commented 4 years ago

Hi, I know that you need to put the data in some variable. The client does this. But since you decide on the content of the data, you need to tell the library how to parse your data. You receive this payload : 0000400000001800. How do you interpret that ?

mchaudh4 commented 4 years ago

I am not able to get the data from the Server into a variable, because of which the code is giving error. I have gone through your code. You have defined a code for codec, but that must be as per your application. I am not able to fetch the data, so that i can use that data.

pylessard commented 4 years ago

I think you do not understand what I am saying nor what is the purpose of the Codec object. The library correctly receive the payload 0000400000001800. The error you get is because you did not define a Codec. In order to help you write the codec, you need to explain how you would convert this payload : 0000400000001800 into a usable value.

mchaudh4 commented 4 years ago

Thank you Pylessard.

Actual work is : I have to keep sending the Reading DID 0x202F. The server will keep on sending me the state of the system. When i get the output as error, i have write the DID 0x2019 with data=0x00000001, which will clear the error and the system will not stop. There is some problem with my server. It is not responding to the DID 0x202F.

So, for making up the code, i am using the DID 0x0203.

Suppose, if i am getting the output 6202030000400000001800 If i can somehow put the value 0000400000001800 into some variable, that can easily solve my problem.

pylessard commented 4 years ago

Here's a Codec just for you that does nothing but make the raw data pass through the application. I'm sure you will end up changing this since getting raw data isn't very useful.

class mchaudh4_passthrough_codec(udsoncan.DidCodec):
   def encode(self, val):
      return val

   def decode(self, payload):
      return payload 

   def __len__(self):
      return 8  

Assign this codec to your DID.

config['data_identifiers'] = { 0x0203 : mchaudh4_passthrough_codec}

mchaudh4 commented 4 years ago

I am highly thankful to you for this kind help. By using UDS and python, we would be able to do error recovery automation and also use it for making GUIs. Best Regards Manu

mchaudh4 commented 4 years ago

Hi pylessard, I have written the write data by identifier below the read data by identifier. There is some problem with my server, because of which writing data is causing exception. Please review the writing data code in the last in the complete code. I feel, i am not writing it correctly.

import struct

import isotp
from can.interfaces.pcan import PcanBus
from udsoncan.connections import PythonIsoTpConnection
from udsoncan.client import Client
import udsoncan.configs

udsoncan.setup_logging()

class mchaudh4_passthrough_codec(udsoncan.DidCodec):
   def encode(self, val):
      return val

   def decode(self, payload):
      return payload

   def __len__(self):
      return 8

config = dict(udsoncan.configs.default_client_config)

config['data_identifiers'] = { 0x0203 : mchaudh4_passthrough_codec}

isotp_params = {
   'stmin' : 32,                          # Will request the sender to wait 32ms between consecutive frame. 0-127ms or 100-900ns with values from 0xF1-0xF9
   '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.
}

bus = PcanBus(channel='PCAN_USBBUS1', bitrate=500000)                                          # Link Layer (CAN protocol)

tp_addr = isotp.Address(isotp.AddressingMode.Normal_11bits, txid=0x7C2, rxid=0x7C3) # Network layer addressing scheme

stack = isotp.CanStack(bus=bus, address=tp_addr, params=isotp_params)               # Network/Transport layer (IsoTP protocol)

conn = PythonIsoTpConnection(stack)

with Client(conn,  request_timeout=2, config=config) as client:
   response = client.read_data_by_identifier(0x0203)
   print("Data is ")
   print(response.service_data.values[0x0203]) # This is a dict of DID:Value

# Writing the Data into the Server
#---------------------------------

class mchaudh4_passthrough_codec1(udsoncan.DidCodec):
   def encode(self, val):
      return val

   def decode(self, payload):
      return payload

   def __len__(self):
      return 8

config1 = dict(udsoncan.configs.default_client_config)

config1['data_identifiers'] = { 0x02023 : mchaudh4_passthrough_codec1}

conn = PythonIsoTpConnection(stack)

with Client(conn,  request_timeout=2, config=config1) as client1:

   response1 = client1.write_data_by_identifier(0x02023,b'\x00\x00\x00\x00\x01')    # Writing the Data using DID

Also, the data to be written should be written as b'\x00\x00.....' . Is it correct way, or it can be modified?

The output of my Code: image

pylessard commented 4 years ago

Your server refuse the request. That's your issue. Maybe change the diagnostic session first? It says "Service Not Supported In Active Session".

mchaudh4 commented 4 years ago

Hi pylessard, I checked the documents. There is something like UDS Extended Session for writing service. There may be some error in our documents, but it says that Message = 02 10 03 00 00 00 00 00 will put the UDS in extended session.

Also, 02 3E 80 00 00 00 00 00 - Tester present message from client to keep server in extended session, this message is needed to send faster than every 30s to keep session alive

Is there any example code in your library for helping in it. After doing this, write DID will start working ?

pylessard commented 4 years ago

client.change_session(3)

or cleaner version

from services.DiagnosticSessionControl import Session
client.change_session(Session.extendedDiagnosticSession)

and for tester present :

client.tester_present()

I don't know if writing the DID will work after this. This is up to your device, UDS does not define that.

Once again, you could have found all of this information very easily in the documentation. I am sorry, but I won't answer any additional questions that shows no effort on your side.

Regards

mchaudh4 commented 4 years ago

Thank you pylessard. My read and write DIDs are now working correctly. I am highly thankful to this great help.