py-uds-demo 25.0.0__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.

Potentially problematic release.


This version of py-uds-demo might be problematic. Click here for more details.

File without changes
@@ -0,0 +1,57 @@
1
+ import argparse
2
+ import sys
3
+
4
+
5
+ from py_uds_demo.interface.cli import Cli
6
+ from py_uds_demo.interface.gui import Gui
7
+ from py_uds_demo.interface.web import Web
8
+ import uvicorn
9
+
10
+ def main():
11
+ parser = argparse.ArgumentParser(
12
+ description="UDS Simulator\n\nThis tool runs the UDS Simulator in different modes.\n\n"
13
+ "Modes available:\n"
14
+ " cli - Command Line Interface mode (default)\n"
15
+ " gui - Graphical User Interface mode\n"
16
+ " web - Web Server mode\n",
17
+ epilog="Example usage:\n"
18
+ " python -m py_uds_demo --mode cli\n"
19
+ " python -m py_uds_demo --mode gui\n"
20
+ " python -m py_uds_demo --mode web\n"
21
+ "You can also use '?' instead of --help to display this message.",
22
+ formatter_class=argparse.RawDescriptionHelpFormatter
23
+ )
24
+
25
+ parser.add_argument(
26
+ "--mode",
27
+ choices=["cli", "gui", "web", "api"],
28
+ default="cli",
29
+ help="Select mode to run: cli (default), gui, web, or api (FastAPI server)"
30
+ )
31
+
32
+ if "?" in sys.argv:
33
+ sys.argv[sys.argv.index("?")] = "--help"
34
+
35
+ args = parser.parse_args()
36
+
37
+ match args.mode:
38
+ case "cli":
39
+ print("Starting CLI Mode...")
40
+ cli = Cli()
41
+ cli.run()
42
+ case "gui":
43
+ print("Starting GUI Mode...")
44
+ gui = Gui()
45
+ gui.run()
46
+ case "web":
47
+ print("Starting Web Mode...")
48
+ web = Web()
49
+ web.run()
50
+ case "api":
51
+ print("Starting FastAPI server (API Mode)...")
52
+ uvicorn.run("py_uds_demo.interface.api:app", host="127.0.0.1", port=8000, reload=True)
53
+ case _:
54
+ print("Unknown mode selected.")
55
+
56
+ if __name__ == "__main__":
57
+ main()
File without changes
@@ -0,0 +1,80 @@
1
+ from typing import Union
2
+ from py_uds_demo.core.server import UdsServer
3
+
4
+
5
+ class UdsClient:
6
+ """A client for sending UDS requests and formatting responses.
7
+
8
+ This class interfaces with the UdsServer to send diagnostic requests
9
+ and process the corresponding responses.
10
+
11
+ Attributes:
12
+ server (UdsServer): An instance of the UdsServer to process requests.
13
+ """
14
+ def __init__(self) -> None:
15
+ """Initializes the UdsClient.
16
+
17
+ This creates a new instance of the UdsServer, which will be used
18
+ for processing all UDS requests initiated by this client.
19
+ """
20
+ self.server = UdsServer()
21
+
22
+ def format_request(self, request: list) -> str:
23
+ """Formats a UDS request list into a human-readable string.
24
+
25
+ Args:
26
+ request: A list of integers representing the request bytes.
27
+
28
+ Returns:
29
+ A string representation of the UDS request, with each byte
30
+ formatted as a two-digit hexadecimal number.
31
+ """
32
+ return "💉 " + " ".join(f"{byte:02X}" for byte in request)
33
+
34
+ def _format_response(self, response: list) -> str:
35
+ """Formats a server response list into a human-readable string.
36
+
37
+ The formatted string includes a status indicator:
38
+ - 🟢 for a positive response.
39
+ - 🔴 for a negative response.
40
+
41
+ Args:
42
+ response: A list of integers representing the response bytes
43
+ from the server.
44
+
45
+ Returns:
46
+ A formatted string representation of the server response.
47
+ """
48
+ if response and response[0] == self.server.SID.NEGATIVE_RESPONSE:
49
+ return "🔴 " + " ".join(f"{byte:02X}" for byte in response)
50
+ else:
51
+ return "🟢 " + " ".join(f"{byte:02X}" for byte in response)
52
+
53
+ def send_request(
54
+ self, data_stream: Union[list, list[int]], return_formatted_stream: bool
55
+ ) -> Union[list, str]:
56
+ """Sends a UDS request to the server and retrieves the response.
57
+
58
+ The request is logged, processed by the server, and the response is
59
+ also logged. The response can be returned as either a raw list of
60
+ bytes or a formatted string.
61
+
62
+ Args:
63
+ data_stream: The request data to send to the server, as a list
64
+ of integers.
65
+ return_formatted_stream: If True, the response is returned as a
66
+ formatted string. Otherwise, it is returned as a list of
67
+ integers.
68
+
69
+ Returns:
70
+ The server's response, which can be either a list of bytes or a
71
+ formatted string, depending on the value of `return_formatted_stream`.
72
+ """
73
+ self.server.logger.info(self.format_request(data_stream))
74
+ response = self.server.process_request(data_stream)
75
+ formatted_response = self._format_response(response)
76
+ self.server.logger.info(formatted_response)
77
+ if return_formatted_stream:
78
+ return formatted_response
79
+ else:
80
+ return response
@@ -0,0 +1,227 @@
1
+ import os
2
+ import sys
3
+ import logging
4
+
5
+ from py_uds_demo.core.utils.services import diagnostic_and_commmunication_management
6
+ from py_uds_demo.core.utils.services import data_transmission
7
+ from py_uds_demo.core.utils.services import stored_data_transmission
8
+ from py_uds_demo.core.utils.services import input_output_contol
9
+ from py_uds_demo.core.utils.services import remote_activation_of_routine
10
+ from py_uds_demo.core.utils.services import upload_download
11
+ from py_uds_demo.core.utils.responses import PositiveResponse, NegativeResponse
12
+ from py_uds_demo.core.utils.helpers import Sid, Sfid, Nrc, Did, Memory
13
+
14
+
15
+ class UdsServer:
16
+ """
17
+ Implements the UDS server functionality.
18
+
19
+ This class initializes all supported UDS services, constants, and response
20
+ handlers. It provides a method to process incoming diagnostic requests and
21
+ route them to the appropriate service handler.
22
+
23
+ Attributes:
24
+ DEFAULT_LOG_FILE (str): The default path for the log file.
25
+ logger (logging.Logger): The logger instance for the server.
26
+ SID (Sid): Service identifiers.
27
+ SFID (Sfid): Sub-function identifiers.
28
+ NRC (Nrc): Negative response codes.
29
+ did (Did): Diagnostic identifiers.
30
+ memory (Memory): Memory map and data.
31
+ positive_response (PositiveResponse): Handler for positive responses.
32
+ negative_response (NegativeResponse): Handler for negative responses.
33
+ diagnostic_and_commmunication_management: Diagnostic and communication management service handler.
34
+ data_transmission: Data transmission service handler.
35
+ stored_data_transmission: Stored data transmission service handler.
36
+ input_output_contol: Input/output control service handler.
37
+ remote_activation_of_routine: Remote activation of routine service handler.
38
+ upload_download: Upload/download service handler.
39
+ """
40
+ def __init__(self):
41
+ # Logger
42
+ self.DEFAULT_LOG_FILE = "_temp/logs/uds_simulator.log"
43
+ self.logger = self._initialize_logger()
44
+ # Constants
45
+ self.SID = Sid()
46
+ self.SFID = Sfid()
47
+ self.NRC = Nrc()
48
+ self.did = Did()
49
+ self.memory = Memory()
50
+ # Responses
51
+ self.positive_response = PositiveResponse()
52
+ self.negative_response = NegativeResponse()
53
+ # Diagnostic and communication management
54
+ self.diagnostic_session_control = diagnostic_and_commmunication_management.DiagnosticSessionControl(self)
55
+ self.ecu_reset = diagnostic_and_commmunication_management.EcuReset(self)
56
+ self.security_access = diagnostic_and_commmunication_management.SecurityAccess(self)
57
+ self.communication_control = diagnostic_and_commmunication_management.CommunicationControl(self)
58
+ self.tester_present = diagnostic_and_commmunication_management.TesterPresent(self)
59
+ self.access_timing_parameter = diagnostic_and_commmunication_management.AccessTimingParameter(self)
60
+ self.secured_data_transmission = diagnostic_and_commmunication_management.SecuredDataTransmission(self)
61
+ self.control_dtc_setting = diagnostic_and_commmunication_management.ControlDtcSetting(self)
62
+ self.response_on_event = diagnostic_and_commmunication_management.ResponseOnEvent(self)
63
+ self.link_control = diagnostic_and_commmunication_management.LinkControl(self)
64
+ # Data transmission
65
+ self.read_data_by_identifier = data_transmission.ReadDataByIdentifier(self)
66
+ self.read_memory_by_address = data_transmission.ReadMemoryByAddress(self)
67
+ self.read_scaling_data_by_identifier = data_transmission.ReadScalingDataByIdentifier(self)
68
+ self.read_data_by_periodic_identifier = data_transmission.ReadDataByPeriodicIdentifier(self)
69
+ self.dynamically_define_data_identifier = data_transmission.DynamicallyDefineDataIdentifier(self)
70
+ self.write_data_by_identifier = data_transmission.WriteDataByIdentifier(self)
71
+ self.write_memory_by_address = data_transmission.WriteMemoryByAddress(self)
72
+ # Stored data transmission
73
+ self.clear_diagnostic_information = stored_data_transmission.ClearDiagnosticInformation(self)
74
+ self.read_dtc_information = stored_data_transmission.ReadDtcInformation(self)
75
+ # Input Output control
76
+ self.input_output_control_by_identifier = input_output_contol.InputOutputControlByIdentifier(self)
77
+ # Remote activation of routine
78
+ self.routine_control = remote_activation_of_routine.RoutineControl(self)
79
+ # Upload download
80
+ self.request_download = upload_download.RequestDownload(self)
81
+ self.request_upload = upload_download.RequestUpload(self)
82
+ self.transfer_data = upload_download.TransferData(self)
83
+ self.request_transfer_exit = upload_download.RequestTransferExit(self)
84
+ self.request_file_transfer = upload_download.RequestFileTransfer(self)
85
+ # Service map
86
+ self.service_map = {
87
+ self.SID.DIAGNOSTIC_SESSION_CONTROL: self.diagnostic_session_control,
88
+ self.SID.ECU_RESET: self.ecu_reset,
89
+ self.SID.SECURITY_ACCESS: self.security_access,
90
+ self.SID.COMMUNICATION_CONTROL: self.communication_control,
91
+ self.SID.TESTER_PRESENT: self.tester_present,
92
+ self.SID.ACCESS_TIMING_PARAMETER: self.access_timing_parameter,
93
+ self.SID.SECURED_DATA_TRANSMISSION: self.secured_data_transmission,
94
+ self.SID.CONTROL_DTC_SETTING: self.control_dtc_setting,
95
+ self.SID.RESPONSE_ON_EVENT: self.response_on_event,
96
+ self.SID.LINK_CONTROL: self.link_control,
97
+ self.SID.READ_DATA_BY_IDENTIFIER: self.read_data_by_identifier,
98
+ self.SID.READ_MEMORY_BY_ADDRESS: self.read_memory_by_address,
99
+ self.SID.READ_SCALING_DATA_BY_IDENTIFIER: self.read_scaling_data_by_identifier,
100
+ self.SID.READ_DATA_BY_PERIODIC_IDENTIFIER: self.read_data_by_periodic_identifier,
101
+ self.SID.DYNAMICALLY_DEFINE_DATA_IDENTIFIER: self.dynamically_define_data_identifier,
102
+ self.SID.WRITE_DATA_BY_IDENTIFIER: self.write_data_by_identifier,
103
+ self.SID.WRITE_MEMORY_BY_ADDRESS: self.write_memory_by_address,
104
+ self.SID.CLEAR_DIAGNOSTIC_INFORMATION: self.clear_diagnostic_information,
105
+ self.SID.READ_DTC_INFORMATION: self.read_dtc_information,
106
+ self.SID.INPUT_OUTPUT_CONTROL_BY_IDENTIFIER: self.input_output_control_by_identifier,
107
+ self.SID.ROUTINE_CONTROL: self.routine_control,
108
+ self.SID.REQUEST_DOWNLOAD: self.request_download,
109
+ self.SID.REQUEST_UPLOAD: self.request_upload,
110
+ self.SID.TRANSFER_DATA: self.transfer_data,
111
+ self.SID.REQUEST_TRANSFER_EXIT: self.request_transfer_exit,
112
+ self.SID.REQUEST_FILE_TRANSFER: self.request_file_transfer,
113
+ }
114
+
115
+ def _initialize_logger(self):
116
+ """
117
+ Initializes the logger for the UDS server.
118
+
119
+ Sets up the logging configuration to output messages to both the console
120
+ and a file.
121
+
122
+ Returns:
123
+ logging.Logger: The configured logger instance.
124
+ """
125
+ os.makedirs(os.path.dirname(self.DEFAULT_LOG_FILE), exist_ok=True)
126
+ logger = logging.getLogger(__name__)
127
+ logger.setLevel(logging.INFO)
128
+ fmt = "%(asctime)s [UDS_SIM_UI] [%(levelname)-4.8s] %(message)s"
129
+ if not any(isinstance(h, logging.FileHandler) for h in logger.handlers):
130
+ file_handler = logging.FileHandler(self.DEFAULT_LOG_FILE, encoding='utf-8')
131
+ file_handler.setLevel(logging.INFO)
132
+ file_handler.setFormatter(logging.Formatter(fmt))
133
+ logger.addHandler(file_handler)
134
+ if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
135
+ console_handler = logging.StreamHandler(sys.stdout)
136
+ console_handler.setLevel(logging.INFO)
137
+ console_handler.setFormatter(logging.Formatter(fmt))
138
+ logger.addHandler(console_handler)
139
+ logger.propagate = True
140
+ return logger
141
+
142
+ @property
143
+ def supported_services(self) -> list:
144
+ """
145
+ Returns a list of all supported UDS service identifiers (SIDs).
146
+
147
+ Returns:
148
+ A list of integers representing the supported SIDs.
149
+ """
150
+ return list(self.SID.__dict__.values())
151
+
152
+ def process_request(self, data_stream: list) -> list:
153
+ """
154
+ Processes an incoming UDS request and returns a response.
155
+
156
+ Args:
157
+ data_stream: A list of integers representing the incoming
158
+ diagnostic request bytes.
159
+
160
+ Returns:
161
+ A list of integers representing the response to the request.
162
+ """
163
+ if not data_stream:
164
+ return self.negative_response.report_negative_response(0x00, self.NRC.GENERAL_REJECT)
165
+ sid = data_stream[0]
166
+ match sid:
167
+ # Diagnostic and communication management
168
+ case self.SID.DIAGNOSTIC_SESSION_CONTROL:
169
+ return self.diagnostic_session_control.process_request(data_stream)
170
+ case self.SID.ECU_RESET:
171
+ return self.ecu_reset.process_request(data_stream)
172
+ case self.SID.SECURITY_ACCESS:
173
+ return self.security_access.process_request(data_stream)
174
+ case self.SID.COMMUNICATION_CONTROL:
175
+ return self.communication_control.process_request(data_stream)
176
+ case self.SID.TESTER_PRESENT:
177
+ return self.tester_present.process_request(data_stream)
178
+ case self.SID.ACCESS_TIMING_PARAMETER:
179
+ return self.access_timing_parameter.process_request(data_stream)
180
+ case self.SID.SECURED_DATA_TRANSMISSION:
181
+ return self.secured_data_transmission.process_request(data_stream)
182
+ case self.SID.CONTROL_DTC_SETTING:
183
+ return self.control_dtc_setting.process_request(data_stream)
184
+ case self.SID.RESPONSE_ON_EVENT:
185
+ return self.response_on_event.process_request(data_stream)
186
+ case self.SID.LINK_CONTROL:
187
+ return self.link_control.process_request(data_stream)
188
+ # Data transmission
189
+ case self.SID.READ_DATA_BY_IDENTIFIER:
190
+ return self.read_data_by_identifier.process_request(data_stream)
191
+ case self.SID.READ_MEMORY_BY_ADDRESS:
192
+ return self.read_memory_by_address.process_request(data_stream)
193
+ case self.SID.READ_SCALING_DATA_BY_IDENTIFIER:
194
+ return self.read_scaling_data_by_identifier.process_request(data_stream)
195
+ case self.SID.READ_DATA_BY_PERIODIC_IDENTIFIER:
196
+ return self.read_data_by_periodic_identifier.process_request(data_stream)
197
+ case self.SID.DYNAMICALLY_DEFINE_DATA_IDENTIFIER:
198
+ return self.dynamically_define_data_identifier.process_request(data_stream)
199
+ case self.SID.WRITE_DATA_BY_IDENTIFIER:
200
+ return self.write_data_by_identifier.process_request(data_stream)
201
+ case self.SID.WRITE_MEMORY_BY_ADDRESS:
202
+ return self.write_memory_by_address.process_request(data_stream)
203
+ # Stored data transmission
204
+ case self.SID.CLEAR_DIAGNOSTIC_INFORMATION:
205
+ return self.clear_diagnostic_information.process_request(data_stream)
206
+ case self.SID.READ_DTC_INFORMATION:
207
+ return self.read_dtc_information.process_request(data_stream)
208
+ # Input Output control
209
+ case self.SID.INPUT_OUTPUT_CONTROL_BY_IDENTIFIER:
210
+ return self.input_output_control_by_identifier.process_request(data_stream)
211
+ # Remote activation of routine
212
+ case self.SID.ROUTINE_CONTROL:
213
+ return self.routine_control.process_request(data_stream)
214
+ # Upload download
215
+ case self.SID.REQUEST_DOWNLOAD:
216
+ return self.request_download.process_request(data_stream)
217
+ case self.SID.REQUEST_UPLOAD:
218
+ return self.request_upload.process_request(data_stream)
219
+ case self.SID.TRANSFER_DATA:
220
+ return self.transfer_data.process_request(data_stream)
221
+ case self.SID.REQUEST_TRANSFER_EXIT:
222
+ return self.request_transfer_exit.process_request(data_stream)
223
+ case self.SID.REQUEST_FILE_TRANSFER:
224
+ return self.request_file_transfer.process_request(data_stream)
225
+ # Negative Response
226
+ case _:
227
+ return self.negative_response.report_negative_response(sid, self.NRC.SERVICE_NOT_SUPPORTED)
File without changes