Code Monkey home page Code Monkey logo

python-doipclient's Introduction

python-doipclient

image

doipclient is a pure Python 3 Diagnostic over IP (DoIP) client which can be used for communicating with modern ECU's over automotive ethernet. It implements the majority of ISO-13400 (2019) from the perspective of a short-lived synchronous client. The primary use case is to serve as a transport layer implementation for the udsoncan library. The code is published under MIT license on GitHub (jacobschaer/python-doipclient).

Documentation

The documentation is available here : https://python-doipclient.readthedocs.io/

Requirements

  • Python 3.6+

Installation

using pip:

pip install doipclient

Running Tests from source

using pytest:

pip install pytest pytest-mock
pytest

Example

Updated version of udsoncan's example using python_doip instead of IsoTPSocketConnection

import SomeLib.SomeCar.SomeModel as MyCar

import udsoncan
from doipclient import DoIPClient
from doipclient.connectors import DoIPClientUDSConnector
from udsoncan.client import Client
from udsoncan.exceptions import *
from udsoncan.services import *

udsoncan.setup_logging()

ecu_ip = '127.0.0.1'
ecu_logical_address = 0x00E0
doip_client = DoIPClient(ecu_ip, ecu_logical_address)
conn = DoIPClientUDSConnector(doip_client)
with Client(conn, request_timeout=2, config=MyCar.config) as client:
   try:
      client.change_session(DiagnosticSessionControl.Session.extendedDiagnosticSession)  # integer with value of 3
      client.unlock_security_access(MyCar.debug_level)                                   # Fictive security level. Integer coming from fictive lib, let's say its value is 5
      client.write_data_by_identifier(udsoncan.DataIdentifier.VIN, 'ABC123456789')       # Standard ID for VIN is 0xF190. Codec is set in the client configuration
      print('Vehicle Identification Number successfully changed.')
      client.ecu_reset(ECUReset.ResetType.hardReset)                                     # HardReset = 0x01
   except NegativeResponseException as e:
      print('Server refused our request for service %s with code "%s" (0x%02x)' % (e.response.service.get_name(), e.response.code_name, e.response.code))
   except (InvalidResponseException, UnexpectedResponseException) as e:
      print('Server sent an invalid payload : %s' % e.response.original_payload)

   # Because we reset our UDS server, we must also reconnect/reactivate the DoIP socket
   # Alternatively, we could have used the auto_reconnect_tcp flag on the DoIPClient
   # Note: ECU's do not restart instantly, so you may need a sleep() before moving on
   doip_client.reconnect()
   client.tester_present()

# Cleanup the DoIP Socket when we're done. Alternatively, we could have used the
# close_connection flag on conn so that the udsoncan client would clean it up
doip_client.close()

python-uds Support

The python-uds can also be used but requires a fork until the owner merges this PR Doip #63. For now, to use the port:

using pip:

git clone https://github.com/jacobschaer/python-uds
git checkout doip
cd python-uds
pip install .

Example:

from uds import Uds

ecu = Uds(transportProtocol="DoIP", ecu_ip="192.168.1.1", ecu_logical_address=1)
try:
    response = ecu.send([0x3E, 0x00])
    print(response)  # This should be [0x7E, 0x00]
except:
    print("Send did not complete")

python-doipclient's People

Contributors

harshalaxman avatar jacobschaer avatar navajowhite avatar sfaiss avatar stefankopf avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

python-doipclient's Issues

help for using python-uds

Hello Jacob,
i'm trying to use the python-uds you fork.
I've respect the process to install and setup your fork like describe on your github page.
But i've got an error message with the parameter: baseConfig.
Cna you help me ?
Many thanks

See below the error message:

from uds import Uds
ecu = Uds(transportProtocol="DoIP", ecu_ip="192.168.1.1", ecu_logical_address=1)

In [9]: from uds import Uds
...: ecu = Uds(transportProtocol="DoIP", ecu_ip="192.168.1.1", ecu_logical_address=1)

FileNotFoundError Traceback (most recent call last)
in
1 from uds import Uds
----> 2 ecu = Uds(transportProtocol="DoIP", ecu_ip="192.168.1.1", ecu_logical_address=1)

/usr/local/lib/python3.10/dist-packages/uds/uds_communications/Uds/Uds.py in init(self, configPath, ihexFile, **kwargs)
33 self.__P2_CAN_Server = None
34
---> 35 self.__loadConfiguration(configPath)
36 self.__checkKwargs(**kwargs)
37

/usr/local/lib/python3.10/dist-packages/uds/uds_communications/Uds/Uds.py in __loadConfiguration(self, configPath)
62 self.__config.read(baseConfig)
63 else:
---> 64 raise FileNotFoundError("No base config file")
65
66 # check the config path

FileNotFoundError: No base config file

Alive check request and response over UDP

According to ISO 13400-2, DoIP entity sends alive check via TCP_DATA socket SA(DoIP-091).
ISO 13400-2 Table 17 - Overview of DoIP payload type
Alive check request - TCP_DATA
Alive check response - TCP_DATA

How to customize the connection timeout?

I want to customize the connection timeout, how should I do it?
One way that comes to my mind is to update the value of A_PROCESSING_TIME in constants.py. But how to update the value of A_PROCESSING_TIME in constants.py?

Thank you very much for providing this library, it is very useful!

[WinError 10061] when creating a DoipClient with client_ip_address != None

I was trying to create a DoipClient using a client_ip_address != None, trying to bind the client's IP in the network.
The only response was ConnectionRefusedError, using a configured ethernet loop or a hardware server.

server = DoIPClient('192.48.48.58', 0xD, udp_port=13400, client_ip_address='192.48.48.13')

I have also tried using the same IP address for the ecu_ip and the client_ip. The same behavior was triggered.

image

Note: I'm using doipclient 1.0.6 and the firewall is deactivated.

help: network management

Hi Jacob,
i would like to know how to manage all socket errors.
When you try to send doip packets to an IP doesn't exist or is not available or ...
How to test that the destination is available before sending DoIP/UDS packet ?
BR.

Support Mercedes-Benz HU55 non-standard DoIP V1 packet format

Does this look right to you? I have a DoIP VCI connected and open in DTS.Monaco trying to do an Ethernet activation/Broadcast.

192.168.8.1:54279 sends a UDP packet to 255.255.255.255:6818

Payload is 64 bytes long

02 fd f0 10 00 00 00 38 00 00 06 00 0c 0c 00 00
00 00 00 00 56 39 34 58 44 30 30 30 31 35 00 00
44 6f 49 50 2d 56 43 49 2d 34 44 35 36 00 00 00
31 32 33 34 35 36 37 38 00 00 00 00 00 00 00 00

Looks like this:

image

Can't create clients for multiple ECUs

When I try to create a client for each of a few different ECUs I get an error because it creates multiple sockets on the same port but only the most recent socket remains usable.

How to choose network interface for DoIp client

Hello everyone.

Many thanks for this package which works perfectly with udsoncan to connect to an ECU using DoIp.

However, I am encountering a problem choosing the network interface to use to create the DoIPClient client.

I have a classic internal network card and a USB/Ethernet adapter on my computer. My ECU supporting Doip is plugged to the usb/ethernet adapter. The network card is used for other communication.
When I simply create my client (as in this package's documentation : address, announcement = DoIPClient.get_entity()), the VehicleIdentificationRequest is sent by default to my internal ethernet card.

I would like this request to go to my usb/ethernet adapter.

If I deactivate the internal network card via the Windows control panel, the VehicleIdentificationRequest goes to my usb/ethernet adapter and I correctly receive the response from my ECU with its IP address, its port and its logical address.

How can we define the communication interface of the DoIP client?

Thanks for your help

Gateway Configuration

First of all, thank you very much for great work. I've been successfully using doipclient for a while in combination with udsoncan. Until now, my tests were limited to sending and receiving UDS request to / from the primary gateway ECU of the vehicle and other ECUs that are connected to the gateway via CAN.

However, I was not able to communicate with other gateway ECUs or ECUs that or connected to these gateways. More precisely, the primary gateway always responds with NACK 0x03 (Invalid Target Address).

Since I could not find any hints in the docs about how to properly configure gateways, I wonder if there are any additional configuration steps necessary to make that routing possible.

Doesn't work Rouine Activation in some vehicles.

I tried to connect to a vehicle as DoIP interface with this library. And It didn't work Rotine Activation at below code.

import udsoncan
from doipclient import DoIPClient
from doipclient.connectors import DoIPClientUDSConnector
from udsoncan.client import Client
from udsoncan.exceptions import *
from udsoncan.services import *

udsoncan.setup_logging()

ecu_ip = '192.168.11.1'
ecu_logical_address = 0x0E80

doip_client = DoIPClient(ecu_ip, ecu_logical_address) # This is doesn'work.
print('Success IP is %x',ecu_logical_address)
conn = DoIPClientUDSConnector(doip_client)
C:\ProgramData\Anaconda3\lib\site-packages\doipclient\client.py in __init__(self, ecu_ip_address, ecu_logical_address, tcp_port, udp_port, activation_type, protocol_version, client_logical_address, client_ip_address, use_secure, auto_reconnect_tcp)
    186             result = self.request_activation(self._activation_type, vm_specific=None, disable_retry=True)
    187             print('this is log')
--> 188             if result.response_code != RoutingActivationResponse.ResponseCode.Success:
    189                 raise ConnectionRefusedError(
    190                     f"Activation Request failed with code {result.response_code}"

ConnectionRefusedError: Activation Request failed with code 0

The issue show "ConnectionRefusedError: Activation Request failed with code 0". It means the header is wrong.
The root cause is vm_specific=None. In ISO-13400-2, vm-specific is optional. But some vehicle have the specific, It need to vm_specific data. So I want to change vm_specific=ecu_vm_specific.

help: send doip packet only on UDP

Hi jacob,
I would like to know how to send doip packet to the udp port 13400 and only the UDP one, not on the tcp port that would be the default.
BR.

help me about DID scanning

Hi Jacob,
i don't know how to scan DID with DoiPClient library.
I use this piece of code:
from doipclient.connectors import DoIPClientUDSConnector
from udsoncan.client import Client, Request

uds_client = Client(uds_connection)
with Client(uds_connection, request_timeout=2) as client:
try:
client.read_data_by_identifier_first([0xF190])
except NegativeResponseException as e:
print('Server refused our request for service %s with code "%s" (0x%02x)' % (e.response.service.get_name(), e.response.code_name, e.response.code))
except (InvalidResponseException, UnexpectedResponseException) as e:
print('Server sent an invalid payload : %s' % e.response.original_payload)
and when I run this script I 've this error:
[ConfigError] : Actual data identifier configuration contains no definition for data identifier 0xf190
[ConfigError] : Actual data identifier configuration contains no definition for data identifier 0xf190

For me that normal, because I've don't initiate the config dictionnary with right parameters for this record.

But my aim is to discovers services and DID in UDS so how to define something I don't know at the beginning ?

So if you can help me ? many thanks by advance :)

Handling "pending" message from ECU

Hi! I'm new at software development so go easy on me ;-) I am plenty technical though. I am using this client with udsoncan to implement flash programming and diag over Ethernet. I am hitting a problem that I think is contained in the way doipclient handles wait state frames. In this case when doing data transfer. ECU is unlocked, programming mode, 0x34 Request Download is sent and acknowledge so all is well. Then send 0x36 request to transfer data, ECU will always respond with a 0x3f error, "Code: Request correctly received, but response is pending (0x78)". I beleive udsoncan has been updated to handle this, i.e. keep waiting on pending message return (if timeout not expired). I am getting immediate timeout from doipclient though.

Relevant code:

from doipclient import DoIPClient
from doipclient.connectors import DoIPClientUDSConnector
import udsoncan
from udsoncan.client import Client
from udsoncan.exceptions import *
from udsoncan.services import *
import time

prog_client = DoIPClient(ecu_ip, currentecu, client_logical_address=OrBit)
    conn = DoIPClientUDSConnector(prog_client)
    with Client(conn, request_timeout=25, config=Carconfig) as client:
	try:
		client.request_download(mem_loc, dfi=SBLdfi)
		for c in chunk:
			seq=seq+1
			client.transfer_data(seq, data=c)
			time.sleep(.5)
		client.request_transfer_exit()

The first transfer of the first block, the client.transfer_data times out almost immediately, via the doipclient.

errors in testing:

  File "C:\Program Files\Python39\lib\site-packages\udsoncan\client.py", line 128, in decorated
    return func(self, *args, **kwargs)
  File "C:\Program Files\Python39\lib\site-packages\udsoncan\client.py", line 844, in transfer_data
    response = self.send_request(request)
  File "C:\Program Files\Python39\lib\site-packages\udsoncan\client.py", line 1956, in send_request
    raise e
  File "C:\Program Files\Python39\lib\site-packages\udsoncan\client.py", line 1946, in send_request
    payload = self.conn.wait_frame(timeout=timeout_value, exception=True)
  File "C:\Program Files\Python39\lib\site-packages\udsoncan\connections.py", line 77, in wait_frame
    frame = self.specific_wait_frame(timeout=timeout)
  File "C:\Program Files\Python39\lib\site-packages\doipclient\connectors.py", line 46, in specific_wait_frame
    return bytes(self._connection.receive_diagnostic(timeout=timeout))
  File "C:\Program Files\Python39\lib\site-packages\doipclient\client.py", line 666, in receive_diagnostic
    result = self.read_doip(timeout=(timeout - ellapsed_time))
  File "C:\Program Files\Python39\lib\site-packages\doipclient\client.py", line 351, in read_doip
    raise TimeoutError("ECU failed to respond in time")
TimeoutError: ECU failed to respond in time

In trying to figure out where this goes wrong, I think it's in connectors.py, the specific_wait_frame, as that returns from the function? I could be wrong about that, but at any rate it appears as far as I can see, doipclient is handling the pending message as the result and returning it, not waiting for the result. I have tried playing with the timeout there and in the constants, A_PROCESSING_TIME, setting to 20 to check if there is an effect, but there is not, return is still immediate timeout on pending message. If I sniff the factory tool, the ECU issues the pending message within 0.3 sec, then 0.5 sec later sends the transfer confirmation, so that seems to be happening within the 2 sec timeout of doipclient.

    def specific_wait_frame(self, timeout=20):
        return bytes(self._connection.receive_diagnostic(timeout=timeout))

I could be way off though, I'm a n00b but wanted to bring it up as it seems an issue with doipclient.

Vehicle Identification Request over TCP

Hey,

is there a reason, why the following payload types use a TCP socket for communication with the server entity, since ISO13400 recommends an UDP socket?

class DoIPClient:
...
    def request_vehicle_identification(self, eid=None, vin=None)...
    def request_diagnostic_power_mode(self)...
    def request_entity_status(self)...

Thanks!

Error when using encrypted communication with preconfigured SSL context

Hello,

if isinstance(self._use_secure, type(ssl.SSLContext)):

If I create a DoIPClient instance with use_secure=ssl_context _connect still tries to create a default context.
I think that in client.py, line 777, in isinstance, type is not needed.

How it works on my setup:
if isinstance(self._use_secure, ssl.SSLContext):

Thank you very much for providing this great library! 👍

request_vehicle_identification

when we try to connect the vehicle, we should use vehicle_identification_request to get the DoIP Node IP address and then try to bind it
but firstly we init DoIPClient(), we need input the ecu_ip_address, which is unknown param
so I suggest to make this func to be a classmethod, then we can get the euc_ip_address firstly

help: [OSError] : Diagnostic request rejected with negative acknowledge code: 3

Hi Jacob,

I'm just New in DOIP and I've tried some simple code in my vehicle.
I'm not sure why some [OSError] happend since I run my code, did I forget some config or something?

My Code

import udsoncan
from doipclient import DoIPClient
from udsoncan.services import *
from doipclient.connectors import DoIPClientUDSConnector
from udsoncan.client import Client

udsoncan.setup_logging()
ecu_ip = '169.254.1.0'
ecu_logical_address = 0x0C6C
doip_client = DoIPClient(ecu_ip, ecu_logical_address)
print(doip_client.request_vehicle_identification())
uds_connection = DoIPClientUDSConnector(doip_client)
with Client(uds_connection, request_timeout=2) as uds_client:
    uds_client.change_session(DiagnosticSessionControl.Session.extendedDiagnosticSession) # 10 03
   
#    uds_client.tester_present() # 3E 00
#    uds_client.read_data_by_identifier([0x45A8, 0xF190]) # 22 45A8 
doip_client.close()

Outputs

VehicleIdentificationResponse (0x4): { vin: "XXX(for security)", logical_address : 3141, eid : XXX(for security), gid : b'\x00\x00\x00\x00\x00\x00', further_action_required : FurtherActionCodes.NoFurtherActionRequired, vin_sync_status : None }
2023-04-12 18:56:05 [INFO] UdsClient: DiagnosticSessionControl<0x10> - Switching session to extendedDiagnosticSession (0x03)
2023-04-12 18:56:05 [DEBUG] Connection: Sending 2 bytes : [b'1003']
2023-04-12 18:56:05 [ERROR] UdsClient: [OSError] : Diagnostic request rejected with negative acknowledge code: 3     
2023-04-12 18:56:05 [INFO] UdsClient: TesterPresent<0x3e> - Sending TesterPresent request
2023-04-12 18:56:05 [DEBUG] Connection: Sending 2 bytes : [b'3e00']
2023-04-12 18:56:05 [ERROR] UdsClient: [OSError] : Diagnostic request rejected with negative acknowledge code: 3   

ConnectionRefusedError

Hi,I used like this, but occur an error like this,do you know the reason about this?
image

Help: error in instance client = DoIPClient(ip, logical_address).

from doipclient import DoIPClient
address, announcement = DoIPClient.get_entity()
logical_address = announcement.logical_address
ip, port = address
print(ip, port, logical_address)  #  ip, port, logical_address can be displayed correctly.
# Then I executed this line of code and it started reporting errors
client = DoIPClient(ip, logical_address)

# The error is
result = self.request_activation(self._activation_type, disable_retry=True)
if result.response_code != RoutingActivationResponse.ResponseCode.Success:
    raise ConnectionRefusedError(
        f"Activation Request failed with code {result.response_code}"
    )

I checked the result is

RoutingActivationResponse (0x6): { client_logical_address : 3584, logical_address : 4112, response_code : ResponseCode.DeniedUnknownSourceAddress, reserved : 0, vm_specific : None }

routing activation request is 02 FD 00 05 00 00 00 07 0e 00 00 00 00 00 00
routing activation response is 02 FD 00 06 00 00 00 09 0e 00 10 10 00 00 00 00 00
What does this mean, if you see it help answer it, thank you very much.

DHCP case add to this package

The CGW is DHCP, everytime the tester need to offer a IP to the vehicle,could add DHCP function or class in this package.

DoIPClient.get_entity() with ipv6

On IPv4, we can send the vehicle request identification message to via the broadcast address and we will get a response along with the ECU's IP address. Of course on IPv6, there is no broadcast. Is the only way to get the ECU's IP address on IPv6 is to listen to the vehicle announcement message on ff02::1?

address2, announcement2 = DoIPClient.await_vehicle_announcement(ipv6=True)

I've tried sending the vehicle request identification message(get_entity) on ff02::1, the gateway does not respond at all.

python-doipclient as transport layer to python-uds

Hi,

Would you be interested in creating a fork to interface to python-uds?
https://python-uds.readthedocs.io/en/latest/index.html

I've had a look myself, but I can't figure it out😒

I have tried your library on a Volvo SPA product, and it works a treat using python-udsoncan. Just had to change the tester address to 0x0E80

Main reason is that python-uds supports ODX for module definitions and therefore everything can be defined in ODX including service response codes etc.

Andy

question about doipclient documentation

hello Jaco

thanks for your contributions firstly.

I have one question when i read the documentation for doipclient.
in the documentation, something as below:
"class doipclient.messages.AliveCheckRequest
Alive check request - Table 27"

but i can't find the Table 27 in the documentation.

thanks again

[General Inquiry] DoIP test suite

I am new to DoIP, and I am planning to work on a rust implementation of DoIP. Just wondering if there is any well accepted test suite (that is publicly available) for the protocol?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.