diff --git a/MANIFEST.in b/MANIFEST.in index f9def91..82b2c5e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,5 @@ include uds\uds_configuration\defaultConfig.ini include uds\uds_communications\Uds\config.ini include uds\uds_communications\TransportProtocols\config.ini include uds\uds_communications\TransportProtocols\Can\config.ini -include uds\uds_communications\TransportProtocols\Lin\config.ini \ No newline at end of file +include uds\uds_communications\TransportProtocols\Lin\config.ini +uds\uds_communications\TransportProtocols\Doip\config.ini \ No newline at end of file diff --git a/README.md b/README.md index 350a3d2..466004b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ An extensible UDS library for python including the diagnostic service definitions from the ISO14229 standard. -Currently supports CAN connections using the [Python-CAN] (https://github.com/hardbyte/python-can) package and a standalone ISO-15765 transport protocol. \ No newline at end of file +Currently supports CAN connections using the [Python-CAN] (https://github.com/hardbyte/python-can) package and a standalone ISO-15765 transport protocol. + +Support for DoIP is provided via the [python-doipclient](https://github.com/jacobschaer/python-doipclient) package. \ No newline at end of file diff --git a/docs/configuration.rst b/docs/configuration.rst index 0ed40fb..b921428 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -42,6 +42,17 @@ These keywords are used to configure the UDS instance: - P2_CAN_Client (DEFAULT: 1) - transportProtocol (DEFAULT: CAN) Currently CAN is the only supported transport protocol +DoipTP +------ +These keywords are used to configure the DoIP Transport Protocol Instance (ISO 13400): + - ecuIP (DEFAULT: 127.0.0.1) This it the IP of the target ECU + - ecuLogicalAddress (DEFAULT: 0x00E0) This is the logical address of the target ECU + - tcpPort (DEFAULT: 13400) This is the TCP port of the target ECU. Defined in the spec + - activationType (DEFAULT: 0) This is the activation request type to use + - protocolVersion (DEFAULT: 0x02) This is the DoIP Protocol Version. Shouldn't need to change + - clientLogicalAddress (DEFAULT: 0x0E00) This is the logical address of the tester + - useSecure (DEFAULT: False) Enables TLS if required + CanTp ----- These keywords are used to configure the CAN Transport Protocol Instance (ISO 14229): diff --git a/docs/examples.rst b/docs/examples.rst index abc6a9c..25ad4e2 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -8,7 +8,8 @@ Example 1 - Simple Peak This example sets up the connection using CAN with the Peak-USB Interface. This is using an E400 with the standard Embed bootloader which supports ISO-14229 UDS. The serial number of the ECU is an ASCII encoded string, in this case "0000000000000001". :: -from uds import Uds + + from uds import Uds E400 = Uds(resId=0x600, reqId=0x650, transportProtocol="CAN", interface="peak", device="PCAN_USBBUS1") try: @@ -65,6 +66,21 @@ This example uses the readDataByIdentifier and writeDataByIdentifier to get the The returned values are encoded into their physical datatype defined in the ODX file rather than the user having to know the encoding format. +Example 5 - Simple DOIP Using Media Converter +---------------------------------------------- + +:: + + 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(TesterPresent) # This should be [0x7E, 0x00] + except: + print("Send did not complete") + + Programming Sequence 1 ---------------------- diff --git a/docs/examples/objectSetup.py b/docs/examples/objectSetup.py index e3d15ee..0f5206d 100644 --- a/docs/examples/objectSetup.py +++ b/docs/examples/objectSetup.py @@ -4,7 +4,7 @@ if __name__ == "__main__": # This creates an Uds object from the Bootloader.odx file - odxEcu = uds.createUdsConnection("Bootloader.odx", "", inteface="peak") + odxEcu = uds.createUdsConnection("Bootloader.odx", "", interface="peak") # This sends a request for Ecu Serial number and stores the result esn = odxEcu.readDataByIdentifier("ECU Serial Number") diff --git a/setup.py b/setup.py index 0a142eb..1ef1de9 100644 --- a/setup.py +++ b/setup.py @@ -25,9 +25,9 @@ # Needed to actually package something packages=find_packages(exclude=["test", "test.*"]), # Needed for dependencies - install_requires=['python-can>=3.0.0', 'python-lin>=0.1.0'], + install_requires=['python-can>=3.0.0', 'python-lin>=0.1.0', 'doipclient>=1.0.1'], # *strongly* suggested for sharing - version='1.1.0', + version='1.2.0', # The license can be anything you like license='MIT', description='A library for interfacing with UDS using python', diff --git a/uds/__init__.py b/uds/__init__.py index 20d1a27..df128ab 100644 --- a/uds/__init__.py +++ b/uds/__init__.py @@ -13,6 +13,9 @@ from uds.uds_communications.TransportProtocols.Can.CanConnectionFactory import CanConnectionFactory +# DoIP Imports +from uds.uds_communications.TransportProtocols.Doip.DoipTp import DoipTp + # CAN Imports from uds.uds_communications.TransportProtocols.Can import CanTpTypes from uds.uds_communications.TransportProtocols.Can.CanTp import CanTp diff --git a/uds/uds_communications/TransportProtocols/Doip/DoipTp.py b/uds/uds_communications/TransportProtocols/Doip/DoipTp.py new file mode 100644 index 0000000..006ecf4 --- /dev/null +++ b/uds/uds_communications/TransportProtocols/Doip/DoipTp.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python + +__author__ = "Jacob Schaer" +__copyrights__ = "Copyright 2021, the python-uds project" +__credits__ = ["Richard Clubb", "Jacob Schaer"] + +__license__ = "MIT" +__maintainer__ = "Richard Clubb" +__email__ = "richard.clubb@embeduk.com" +__status__ = "Development" + + +from os import path + +from uds import iTp +from uds import Config + +# Load python-doipclient library +from doipclient import DoIPClient + +class DoipTp(iTp): + + #__metaclass__ = iTp + + def __init__(self, configPath=None, **kwargs): + + # perform the instance config + self.__config = None + + self.__loadConfiguration(configPath) + self.__checkKwargs(**kwargs) + + self.__ecu_ip = self.__config["DoIP"]["ecuIP"] + self.__ecu_logical_address = int(self.__config["DoIP"]["ecuLogicalAddress"], 16) + self.__tcp_port = int(self.__config["DoIP"]["tcpPort"]) + self.__activation_type = int(self.__config["DoIP"]["activationType"], 16) + self.__protocol_version = int(self.__config["DoIP"]["protocolVersion"], 16) + self.__client_logical_address = int(self.__config["DoIP"]["clientLogicalAddress"], 16) + self.__use_secure = self.__config["DoIP"]["useSecure"] == 'True' + + self.__connection = DoIPClient( + self.__ecu_ip, + self.__ecu_logical_address, + tcp_port=self.__tcp_port, + activation_type=self.__activation_type, + protocol_version=self.__protocol_version, + client_logical_address=self.__client_logical_address, + use_secure=self.__use_secure) + + def send(self, payload, functionalReq=False): # TODO: functionalReq not used??? + self.__connection.send_diagnostic(bytearray(payload)) + + def recv(self, timeout_s): + return list(self.__connection.receive_diagnostic(timeout=timeout_s)) + + def closeConnection(self): + self.__connection.close() + + ## + # @brief clear out the receive list + def clearBufferedMessages(self): + self.__connection.empty_rxqueue() + + ## + # @brief used to load the local configuration options and override them with any passed in from a config file + def __loadConfiguration(self, configPath, **kwargs): + # load the base config + baseConfig = path.dirname(__file__) + "/config.ini" + self.__config = Config() + if path.exists(baseConfig): + self.__config.read(baseConfig) + else: + raise FileNotFoundError("No base config file") + + # check the config path + if configPath is not None: + if path.exists(configPath): + self.__config.read(configPath) + else: + raise FileNotFoundError("specified config not found") + + ## + # @brief goes through the kwargs and overrides any of the local configuration options + def __checkKwargs(self, **kwargs): + if 'ecu_ip' in kwargs: + self.__config['DoIP']['ecuIP'] = kwargs['ecu_ip'] + if 'ecu_logical_address' in kwargs: + self.__config['DoIP']['ecuLogicalAddress'] = hex(kwargs['ecu_logical_address']) + if 'tcp_port' in kwargs: + self.__config['DoIP']['tcpPort'] = str(kwargs['tcp_port']) + if 'activation_type' in kwargs: + self.__config['DoIP']['activationType'] = hex(kwargs['activation_type']) + if 'protocol_version' in kwargs: + self.__config['DoIP']['protocolVersion'] = hex(kwargs['protocol_version']) + if 'client_logical_address' in kwargs: + self.__config['DoIP']['clientLogicalAddress'] = hex(kwargs['client_logical_address']) + if 'use_secure' in kwargs: + self.__config['DoIP']['useSecure'] = 'True' if kwargs['use_secure'] else 'False' \ No newline at end of file diff --git a/uds/uds_communications/TransportProtocols/Doip/__init__.py b/uds/uds_communications/TransportProtocols/Doip/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/uds/uds_communications/TransportProtocols/Doip/config.ini b/uds/uds_communications/TransportProtocols/Doip/config.ini new file mode 100644 index 0000000..483676a --- /dev/null +++ b/uds/uds_communications/TransportProtocols/Doip/config.ini @@ -0,0 +1,8 @@ +[DoIP] +ecuIP=127.0.0.1 +ecuLogicalAddress=0x00E0 +tcpPort=13400 +activationType=0 +protocolVersion=0x02 +clientLogicalAddress=0x0E00 +useSecure=False \ No newline at end of file diff --git a/uds/uds_communications/TransportProtocols/TpFactory.py b/uds/uds_communications/TransportProtocols/TpFactory.py index 8550fda..59e1a67 100644 --- a/uds/uds_communications/TransportProtocols/TpFactory.py +++ b/uds/uds_communications/TransportProtocols/TpFactory.py @@ -14,6 +14,7 @@ from uds import CanTp from uds import LinTp from uds import TestTp +from uds import DoipTp from os import path @@ -32,11 +33,10 @@ class TpFactory(object): def __call__(tpType, configPath=None, **kwargs): #TpFactory.loadConfiguration(configPath) - if(tpType == "CAN"): return CanTp(configPath=configPath, **kwargs) elif(tpType == "DoIP"): - raise NotImplementedError("DoIP transport not currently supported") + return DoipTp(configPath=configPath, **kwargs) elif(tpType == "K-LINE"): raise NotImplementedError("K-Line Transport not currently supported") elif(tpType == "LIN"):