py-uds-demo 26.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_uds_demo/__init__.py +0 -0
- py_uds_demo/__main__.py +57 -0
- py_uds_demo/core/__init__.py +0 -0
- py_uds_demo/core/client.py +80 -0
- py_uds_demo/core/server.py +227 -0
- py_uds_demo/core/utils/__init__.py +0 -0
- py_uds_demo/core/utils/helpers.py +314 -0
- py_uds_demo/core/utils/responses.py +55 -0
- py_uds_demo/core/utils/services/__init__.py +0 -0
- py_uds_demo/core/utils/services/data_transmission.py +398 -0
- py_uds_demo/core/utils/services/diagnostic_and_commmunication_management.py +755 -0
- py_uds_demo/core/utils/services/input_output_contol.py +63 -0
- py_uds_demo/core/utils/services/negative_response.py +1 -0
- py_uds_demo/core/utils/services/remote_activation_of_routine.py +80 -0
- py_uds_demo/core/utils/services/stored_data_transmission.py +132 -0
- py_uds_demo/core/utils/services/upload_download.py +189 -0
- py_uds_demo/interface/__init__.py +0 -0
- py_uds_demo/interface/api.py +30 -0
- py_uds_demo/interface/cli.py +51 -0
- py_uds_demo/interface/gui.py +83 -0
- py_uds_demo/interface/web.py +422 -0
- py_uds_demo-26.0.1.dist-info/METADATA +53 -0
- py_uds_demo-26.0.1.dist-info/RECORD +24 -0
- py_uds_demo-26.0.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
if TYPE_CHECKING:
|
|
3
|
+
from py_uds_demo.core.server import UdsServer
|
|
4
|
+
from py_uds_demo.core.utils.helpers import split_integer_to_bytes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ReadDataByIdentifier:
|
|
8
|
+
"""
|
|
9
|
+
Handles Read Data By Identifier (0x22) service requests.
|
|
10
|
+
|
|
11
|
+
What:
|
|
12
|
+
This service is used to read data from the server (ECU), identified
|
|
13
|
+
by a 2-byte Data Identifier (DID). It's one of the most common UDS
|
|
14
|
+
services.
|
|
15
|
+
|
|
16
|
+
Why:
|
|
17
|
+
It provides a standardized way to read a wide variety of data, such
|
|
18
|
+
as sensor values, configuration settings, part numbers, software
|
|
19
|
+
versions, and more.
|
|
20
|
+
|
|
21
|
+
How:
|
|
22
|
+
The client sends a request with the SID 0x22 followed by one or more
|
|
23
|
+
2-byte DIDs. The server responds with the SID 0x62, the requested
|
|
24
|
+
DID(s), and the corresponding data.
|
|
25
|
+
|
|
26
|
+
Real-world example:
|
|
27
|
+
A workshop tool needs to verify the software version of an ECU. It
|
|
28
|
+
sends a Read Data By Identifier request with the DID for the software
|
|
29
|
+
version (e.g., 0xF188). The ECU responds with the version, which the
|
|
30
|
+
tool then displays to the technician.
|
|
31
|
+
|
|
32
|
+
Attributes:
|
|
33
|
+
uds_server: The UDS server instance.
|
|
34
|
+
"""
|
|
35
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
36
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
37
|
+
|
|
38
|
+
def process_request(self, data_stream: list) -> list:
|
|
39
|
+
"""
|
|
40
|
+
Processes a Read Data By Identifier request.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data_stream: The request data stream.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
A list of bytes representing the response.
|
|
47
|
+
"""
|
|
48
|
+
if len(data_stream) != 3:
|
|
49
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
50
|
+
self.uds_server.SID.RDBI, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
|
|
51
|
+
)
|
|
52
|
+
did = (data_stream[1] << 8) | data_stream[2]
|
|
53
|
+
match did:
|
|
54
|
+
case self.uds_server.did.ACTIVE_DIAGNOSTIC_SESSION:
|
|
55
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
56
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + [self.uds_server.diagnostic_session_control.active_session]
|
|
57
|
+
)
|
|
58
|
+
case self.uds_server.did.VEHICLE_IDENTIFICATION_NUMBER:
|
|
59
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
60
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.vehicle_identification_number
|
|
61
|
+
)
|
|
62
|
+
case self.uds_server.did.MANUFACTURER_SPARE_PART_NUMBER:
|
|
63
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
64
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.manufacturer_spare_part_number
|
|
65
|
+
)
|
|
66
|
+
case self.uds_server.did.MANUFACTURER_ECU_SOFTWARE_NUMBER:
|
|
67
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
68
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.manufacturer_ecu_software_number
|
|
69
|
+
)
|
|
70
|
+
case self.uds_server.did.MANUFACTURER_ECU_SOFTWARE_VERSION:
|
|
71
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
72
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.manufacturer_ecu_software_version
|
|
73
|
+
)
|
|
74
|
+
case self.uds_server.did.ECU_MANUFACTURING_DATE:
|
|
75
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
76
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.ecu_manufacturing_date
|
|
77
|
+
)
|
|
78
|
+
case self.uds_server.did.ECU_SERIAL_NUMBER:
|
|
79
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
80
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.ecu_serial_number
|
|
81
|
+
)
|
|
82
|
+
case self.uds_server.did.SUPPORTED_FUNCTIONAL_UNITS:
|
|
83
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
84
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.supported_functional_units
|
|
85
|
+
)
|
|
86
|
+
case self.uds_server.did.SYSTEM_SUPPLIER_ECU_SOFTWARE_NUMBER:
|
|
87
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
88
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.system_supplier_ecu_software_number
|
|
89
|
+
)
|
|
90
|
+
case self.uds_server.did.SYSTEM_SUPPLIER_ECU_SOFTWARE_VERSION:
|
|
91
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
92
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.system_supplier_ecu_software_version
|
|
93
|
+
)
|
|
94
|
+
case self.uds_server.did.PROGRAMMING_DATE:
|
|
95
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
96
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.programming_date
|
|
97
|
+
)
|
|
98
|
+
case self.uds_server.did.REPAIR_SHOP_CODE:
|
|
99
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
100
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.repair_shop_code
|
|
101
|
+
)
|
|
102
|
+
case self.uds_server.did.EXHAUST_REGULATION_TYPE_APPROVAL_NUMBER:
|
|
103
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
104
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.exhaust_regulation_type_approval_number
|
|
105
|
+
)
|
|
106
|
+
case self.uds_server.did.INSTALLATION_DATE:
|
|
107
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
108
|
+
self.uds_server.SID.RDBI, data_stream[1:3] + self.uds_server.memory.ecu_installation_date
|
|
109
|
+
)
|
|
110
|
+
case _:
|
|
111
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
112
|
+
self.uds_server.SID.RDBI, self.uds_server.NRC.REQUEST_OUT_OF_RANGE
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class ReadMemoryByAddress:
|
|
117
|
+
"""
|
|
118
|
+
Handles Read Memory By Address (0x23) service requests.
|
|
119
|
+
|
|
120
|
+
What:
|
|
121
|
+
This service is used to read data from a specific memory address in
|
|
122
|
+
the server (ECU).
|
|
123
|
+
|
|
124
|
+
Why:
|
|
125
|
+
It provides a low-level way to access the ECU's memory, which is
|
|
126
|
+
useful for debugging, reverse engineering, or accessing data that
|
|
127
|
+
is not available through a Data Identifier (DID).
|
|
128
|
+
|
|
129
|
+
How:
|
|
130
|
+
The client sends a request with the SID 0x23, followed by the memory
|
|
131
|
+
address and the number of bytes to read. The server responds with
|
|
132
|
+
the SID 0x63 and the requested data.
|
|
133
|
+
|
|
134
|
+
Real-world example:
|
|
135
|
+
A software developer is debugging a new feature and wants to inspect
|
|
136
|
+
the value of a variable in real-time. They use the Read Memory By
|
|
137
|
+
Address service to read the memory location where that variable is
|
|
138
|
+
stored, helping them to understand its behavior.
|
|
139
|
+
|
|
140
|
+
Attributes:
|
|
141
|
+
uds_server: The UDS server instance.
|
|
142
|
+
"""
|
|
143
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
144
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
145
|
+
|
|
146
|
+
def process_request(self, data_stream: list) -> list:
|
|
147
|
+
"""
|
|
148
|
+
Processes a Read Memory By Address request.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
data_stream: The request data stream.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
A list of bytes representing the response.
|
|
155
|
+
"""
|
|
156
|
+
if len(data_stream) != 5:
|
|
157
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
158
|
+
self.uds_server.SID.RMBA, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
address = (data_stream[1] << 24) | (data_stream[2] << 16) | (data_stream[3] << 8) | data_stream[4]
|
|
162
|
+
|
|
163
|
+
if address not in self.uds_server.memory.memory_map:
|
|
164
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
165
|
+
self.uds_server.SID.RMBA, self.uds_server.NRC.REQUEST_OUT_OF_RANGE
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
return self.uds_server.positive_response.report_positive_response(
|
|
169
|
+
self.uds_server.SID.RMBA, self.uds_server.memory.memory_map[address]
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class ReadScalingDataByIdentifier:
|
|
174
|
+
"""
|
|
175
|
+
Handles Read Scaling Data By Identifier (0x24) service requests.
|
|
176
|
+
|
|
177
|
+
What:
|
|
178
|
+
This service is used to retrieve the scaling information for a data
|
|
179
|
+
value that is returned by the ReadDataByIdentifier service.
|
|
180
|
+
|
|
181
|
+
Why:
|
|
182
|
+
Some data values are transmitted as scaled integers to save space or
|
|
183
|
+
for other reasons. This service provides the necessary information
|
|
184
|
+
(e.g., a formula or a lookup table) to convert the raw integer value
|
|
185
|
+
into a physical value (e.g., a floating-point number with a unit).
|
|
186
|
+
|
|
187
|
+
How:
|
|
188
|
+
The client sends a request with the SID 0x24 and a DID. The server
|
|
189
|
+
responds with the scaling information for that DID.
|
|
190
|
+
|
|
191
|
+
Note:
|
|
192
|
+
This service is not fully implemented in this simulator.
|
|
193
|
+
"""
|
|
194
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
195
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
196
|
+
|
|
197
|
+
def process_request(self, data_stream: list) -> list:
|
|
198
|
+
"""
|
|
199
|
+
Processes a Read Scaling Data By Identifier request.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
data_stream: The request data stream.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
A negative response, as this service is not supported.
|
|
206
|
+
"""
|
|
207
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
208
|
+
self.uds_server.SID.RSDBI, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class ReadDataByPeriodicIdentifier:
|
|
213
|
+
"""
|
|
214
|
+
Handles Read Data By Periodic Identifier (0x2A) service requests.
|
|
215
|
+
|
|
216
|
+
What:
|
|
217
|
+
This service is used to request that the server (ECU) periodically
|
|
218
|
+
transmits the data values for one or more Data Identifiers (DIDs).
|
|
219
|
+
|
|
220
|
+
Why:
|
|
221
|
+
It's an efficient way to monitor data over time without the need for
|
|
222
|
+
the client to continuously send requests. This is useful for data
|
|
223
|
+
logging or for displaying live data on a diagnostic tool.
|
|
224
|
+
|
|
225
|
+
How:
|
|
226
|
+
The client sends a request with the SID 0x2A, specifying the DIDs
|
|
227
|
+
to be read and the transmission rate. The server then starts sending
|
|
228
|
+
the data periodically until instructed to stop.
|
|
229
|
+
|
|
230
|
+
Note:
|
|
231
|
+
This service is not fully implemented in this simulator.
|
|
232
|
+
"""
|
|
233
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
234
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
235
|
+
|
|
236
|
+
def process_request(self, data_stream: list) -> list:
|
|
237
|
+
"""
|
|
238
|
+
Processes a Read Data By Periodic Identifier request.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
data_stream: The request data stream.
|
|
242
|
+
|
|
243
|
+
Returns:
|
|
244
|
+
A negative response, as this service is not supported.
|
|
245
|
+
"""
|
|
246
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
247
|
+
self.uds_server.SID.RDBPI, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class DynamicallyDefineDataIdentifier:
|
|
252
|
+
"""
|
|
253
|
+
Handles Dynamically Define Data Identifier (0x2C) service requests.
|
|
254
|
+
|
|
255
|
+
What:
|
|
256
|
+
This service allows a client to dynamically define a new Data
|
|
257
|
+
Identifier (DID) at runtime. This new DID can be composed of data
|
|
258
|
+
from other existing DIDs or from specific memory addresses.
|
|
259
|
+
|
|
260
|
+
Why:
|
|
261
|
+
It's useful when you need to read a combination of data that is not
|
|
262
|
+
available in a single, predefined DID. Instead of sending multiple
|
|
263
|
+
requests, you can create one dynamic DID to get all the data in a
|
|
264
|
+
single response, which can be more efficient.
|
|
265
|
+
|
|
266
|
+
How:
|
|
267
|
+
The client sends a request with the SID 0x2C and the definition of
|
|
268
|
+
the new DID, which includes the source DIDs or memory addresses.
|
|
269
|
+
|
|
270
|
+
Note:
|
|
271
|
+
This service is not fully implemented in this simulator.
|
|
272
|
+
"""
|
|
273
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
274
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
275
|
+
|
|
276
|
+
def process_request(self, data_stream: list) -> list:
|
|
277
|
+
"""
|
|
278
|
+
Processes a Dynamically Define Data Identifier request.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
data_stream: The request data stream.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
A negative response, as this service is not supported.
|
|
285
|
+
"""
|
|
286
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
287
|
+
self.uds_server.SID.DDDI, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class WriteDataByIdentifier:
|
|
292
|
+
"""
|
|
293
|
+
Handles Write Data By Identifier (0x2E) service requests.
|
|
294
|
+
|
|
295
|
+
What:
|
|
296
|
+
This service is used to write data to the server (ECU) at a location
|
|
297
|
+
specified by a Data Identifier (DID).
|
|
298
|
+
|
|
299
|
+
Why:
|
|
300
|
+
It's used to change the ECU's behavior or update its configuration.
|
|
301
|
+
This can include things like setting a new speed limit, updating the
|
|
302
|
+
VIN, or changing calibration values.
|
|
303
|
+
|
|
304
|
+
How:
|
|
305
|
+
The client sends a request with the SID 0x2E, the DID to be written,
|
|
306
|
+
and the data to write. The server responds with the SID 0x6E and the
|
|
307
|
+
DID that was written to confirm the operation.
|
|
308
|
+
|
|
309
|
+
Real-world example:
|
|
310
|
+
A car manufacturer wants to update the service date in the instrument
|
|
311
|
+
cluster. A technician uses a diagnostic tool to send a Write Data By
|
|
312
|
+
Identifier request with the DID for the service date and the new date.
|
|
313
|
+
The instrument cluster then updates its display accordingly.
|
|
314
|
+
|
|
315
|
+
Attributes:
|
|
316
|
+
uds_server: The UDS server instance.
|
|
317
|
+
"""
|
|
318
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
319
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
320
|
+
|
|
321
|
+
def process_request(self, data_stream: list) -> list:
|
|
322
|
+
"""
|
|
323
|
+
Processes a Write Data By Identifier request.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
data_stream: The request data stream.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
A list of bytes representing the response.
|
|
330
|
+
"""
|
|
331
|
+
if len(data_stream) < 4:
|
|
332
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
333
|
+
self.uds_server.SID.WDBI, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
did = (data_stream[1] << 8) | data_stream[2]
|
|
337
|
+
|
|
338
|
+
if did not in self.uds_server.memory.writable_dids:
|
|
339
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
340
|
+
self.uds_server.SID.WDBI, self.uds_server.NRC.REQUEST_OUT_OF_RANGE
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
data_to_write = data_stream[3:]
|
|
344
|
+
self.uds_server.memory.did_data[did] = data_to_write
|
|
345
|
+
|
|
346
|
+
return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.WDBI, data_stream[1:3])
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class WriteMemoryByAddress:
|
|
350
|
+
"""
|
|
351
|
+
Handles Write Memory By Address (0x3D) service requests.
|
|
352
|
+
|
|
353
|
+
What:
|
|
354
|
+
This service is used to write data to a specific memory address in
|
|
355
|
+
the server (ECU).
|
|
356
|
+
|
|
357
|
+
Why:
|
|
358
|
+
It provides a low-level way to modify the ECU's memory, which is
|
|
359
|
+
useful for debugging, applying patches, or writing data to memory
|
|
360
|
+
locations that are not accessible through a Data Identifier (DID).
|
|
361
|
+
|
|
362
|
+
How:
|
|
363
|
+
The client sends a request with the SID 0x3D, the memory address,
|
|
364
|
+
and the data to be written. The server responds with the SID 0x7D
|
|
365
|
+
to confirm the operation.
|
|
366
|
+
|
|
367
|
+
Real-world example:
|
|
368
|
+
A developer needs to apply a small patch to the ECU's software
|
|
369
|
+
without performing a full reflash. They can use the Write Memory By
|
|
370
|
+
Address service to write the patched code directly into the
|
|
371
|
+
specified memory locations.
|
|
372
|
+
|
|
373
|
+
Attributes:
|
|
374
|
+
uds_server: The UDS server instance.
|
|
375
|
+
"""
|
|
376
|
+
def __init__(self, uds_server: 'UdsServer') -> None:
|
|
377
|
+
self.uds_server: 'UdsServer' = uds_server
|
|
378
|
+
|
|
379
|
+
def process_request(self, data_stream: list) -> list:
|
|
380
|
+
"""
|
|
381
|
+
Processes a Write Memory By Address request.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
data_stream: The request data stream.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
A list of bytes representing the response.
|
|
388
|
+
"""
|
|
389
|
+
if len(data_stream) < 6:
|
|
390
|
+
return self.uds_server.negative_response.report_negative_response(
|
|
391
|
+
self.uds_server.SID.WMBA, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
address = (data_stream[1] << 24) | (data_stream[2] << 16) | (data_stream[3] << 8) | data_stream[4]
|
|
395
|
+
data_to_write = data_stream[5:]
|
|
396
|
+
self.uds_server.memory.memory_map[address] = data_to_write
|
|
397
|
+
|
|
398
|
+
return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.WMBA, [])
|