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.
@@ -0,0 +1,63 @@
1
+ from typing import TYPE_CHECKING
2
+ if TYPE_CHECKING:
3
+ from py_uds_demo.core.server import UdsServer
4
+
5
+
6
+ class InputOutputControlByIdentifier:
7
+ """
8
+ Handles Input Output Control By Identifier (0x2F) service requests.
9
+
10
+ What:
11
+ This service allows a client to take control of a server's (ECU's)
12
+ inputs and outputs.
13
+
14
+ Why:
15
+ It's used for testing and diagnostics. For example, a technician can
16
+ use it to manually activate an actuator (like a fan or a motor) to
17
+ verify its operation, or to simulate a sensor input to see how the
18
+ ECU responds.
19
+
20
+ How:
21
+ The client sends a request with the SID 0x2F, a Data Identifier (DID)
22
+ to specify the I/O channel, and a control option (e.g., return
23
+ control to ECU, freeze current state, short term adjustment).
24
+
25
+ Real-world example:
26
+ A technician suspects a radiator fan is faulty. They use a diagnostic
27
+ tool to send an Input Output Control By Identifier request to the
28
+ engine control unit, commanding it to turn on the fan. If the fan
29
+ starts, the technician knows the fan motor is working and the issue
30
+ lies elsewhere, perhaps with the temperature sensor or control logic.
31
+
32
+ Attributes:
33
+ uds_server: The UDS server instance.
34
+ io_control_status: A dictionary to store the status of I/O controls.
35
+ """
36
+ def __init__(self, uds_server: 'UdsServer') -> None:
37
+ self.uds_server: 'UdsServer' = uds_server
38
+ self.io_control_status = {}
39
+
40
+ def process_request(self, data_stream: list) -> list:
41
+ """
42
+ Processes an Input Output Control By Identifier request.
43
+
44
+ Args:
45
+ data_stream: The request data stream.
46
+
47
+ Returns:
48
+ A list of bytes representing the response.
49
+ """
50
+ if len(data_stream) < 4:
51
+ return self.uds_server.negative_response.report_negative_response(
52
+ self.uds_server.SID.IOCBI, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
53
+ )
54
+
55
+ did = (data_stream[1] << 8) | data_stream[2]
56
+ control_option = data_stream[3]
57
+
58
+ # In a real ECU, you would check if the DID is valid for I/O control
59
+ # and if the control option is supported.
60
+
61
+ self.io_control_status[did] = control_option
62
+
63
+ return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.IOCBI, data_stream[1:3])
@@ -0,0 +1,80 @@
1
+ from typing import TYPE_CHECKING
2
+ if TYPE_CHECKING:
3
+ from py_uds_demo.core.server import UdsServer
4
+
5
+
6
+ class RoutineControl:
7
+ """
8
+ Handles Routine Control (0x31) service requests.
9
+
10
+ What:
11
+ This service is used to start, stop, and request the results of a
12
+ routine in the server (ECU).
13
+
14
+ Why:
15
+ Routines are used to perform more complex tasks than what can be
16
+ achieved with a simple read or write service. This can include
17
+ things like running a self-test, erasing memory, or learning new
18
+ adaptive values.
19
+
20
+ How:
21
+ The client sends a request with the SID 0x31, a sub-function
22
+ (e.g., startRoutine, stopRoutine, requestRoutineResults), and a
23
+ 2-byte routine identifier.
24
+
25
+ Real-world example:
26
+ A technician wants to perform a self-test on the ABS. They use a
27
+ diagnostic tool to send a Routine Control request with the
28
+ 'startRoutine' sub-function and the routine identifier for the ABS
29
+ self-test. After the routine completes, they send another request
30
+ with 'requestRoutineResults' to check if the test passed.
31
+
32
+ Attributes:
33
+ uds_server: The UDS server instance.
34
+ routine_status: A dictionary to store the status of routines.
35
+ """
36
+ def __init__(self, uds_server: 'UdsServer') -> None:
37
+ self.uds_server: 'UdsServer' = uds_server
38
+ self.routine_status = {}
39
+
40
+ def process_request(self, data_stream: list) -> list:
41
+ """
42
+ Processes a Routine Control request.
43
+
44
+ Args:
45
+ data_stream: The request data stream.
46
+
47
+ Returns:
48
+ A list of bytes representing the response.
49
+ """
50
+ if len(data_stream) < 4:
51
+ return self.uds_server.negative_response.report_negative_response(
52
+ self.uds_server.SID.RC, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
53
+ )
54
+
55
+ sub_function = data_stream[1]
56
+ routine_id = (data_stream[2] << 8) | data_stream[3]
57
+
58
+ if sub_function == self.uds_server.SFID.START_ROUTINE:
59
+ self.routine_status[routine_id] = "Started"
60
+ return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.RC, data_stream[1:4])
61
+
62
+ elif sub_function == self.uds_server.SFID.STOP_ROUTINE:
63
+ self.routine_status[routine_id] = "Stopped"
64
+ return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.RC, data_stream[1:4])
65
+
66
+ elif sub_function == self.uds_server.SFID.REQUEST_ROUTINE_RESULT:
67
+ if routine_id in self.routine_status:
68
+ # Returning a dummy result
69
+ return self.uds_server.positive_response.report_positive_response(
70
+ self.uds_server.SID.RC, data_stream[1:4] + [0x01, 0x02, 0x03]
71
+ )
72
+ else:
73
+ return self.uds_server.negative_response.report_negative_response(
74
+ self.uds_server.SID.RC, self.uds_server.NRC.REQUEST_OUT_OF_RANGE
75
+ )
76
+
77
+ else:
78
+ return self.uds_server.negative_response.report_negative_response(
79
+ self.uds_server.SID.RC, self.uds_server.NRC.SUB_FUNCTION_NOT_SUPPORTED
80
+ )
@@ -0,0 +1,132 @@
1
+ from typing import TYPE_CHECKING
2
+ if TYPE_CHECKING:
3
+ from py_uds_demo.core.server import UdsServer
4
+
5
+
6
+ class ClearDiagnosticInformation:
7
+ """
8
+ Handles Clear Diagnostic Information (0x14) service requests.
9
+
10
+ What:
11
+ This service is used to clear Diagnostic Trouble Codes (DTCs) from
12
+ the server's (ECU's) memory.
13
+
14
+ Why:
15
+ After a vehicle has been repaired, the stored DTCs related to the
16
+ fixed issue need to be cleared. This service provides the means to
17
+ do so.
18
+
19
+ How:
20
+ The client sends a request with the SID 0x14, followed by a 3-byte
21
+ groupOfDTC parameter, which specifies which DTCs to clear. A value
22
+ of 0xFFFFFF is typically used to clear all DTCs.
23
+
24
+ Real-world example:
25
+ A "Check Engine" light is on. A technician reads the DTCs and finds
26
+ a code for a faulty sensor. After replacing the sensor, the technician
27
+ uses this service to clear the DTC, which turns off the light.
28
+
29
+ Attributes:
30
+ uds_server: The UDS server instance.
31
+ """
32
+ def __init__(self, uds_server: 'UdsServer') -> None:
33
+ self.uds_server: 'UdsServer' = uds_server
34
+
35
+ def process_request(self, data_stream: list) -> list:
36
+ """
37
+ Processes a Clear Diagnostic Information request.
38
+
39
+ Args:
40
+ data_stream: The request data stream.
41
+
42
+ Returns:
43
+ A list of bytes representing the response.
44
+ """
45
+ if self.uds_server.control_dtc_setting.dtc_setting == self.uds_server.SFID.OFF:
46
+ return self.uds_server.negative_response.report_negative_response(
47
+ self.uds_server.SID.CDTCI, self.uds_server.NRC.CONDITIONS_NOT_CORRECT
48
+ )
49
+
50
+ self.uds_server.memory.dtcs = []
51
+ return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.CDTCI, [])
52
+
53
+
54
+ class ReadDtcInformation:
55
+ """
56
+ Handles Read DTC Information (0x19) service requests.
57
+
58
+ What:
59
+ This service is used to read Diagnostic Trouble Codes (DTCs) and
60
+ related data from the server's (ECU's) memory.
61
+
62
+ Why:
63
+ It's the primary service for diagnosing vehicle problems. By reading
64
+ DTCs, a technician can identify the system or component that is
65
+ faulty. It also allows reading additional data, like "freeze frames"
66
+ or "snapshot data," which is a snapshot of the vehicle's state at
67
+ the time the fault occurred.
68
+
69
+ How:
70
+ The client sends a request with the SID 0x19 and a sub-function that
71
+ specifies what information to read (e.g., number of DTCs, DTCs by
72
+ status mask, snapshot data).
73
+
74
+ Real-world example:
75
+ A technician connects a diagnostic tool to a car with the "Check
76
+ Engine" light on. The tool uses this service with the
77
+ 'reportDTCByStatusMask' sub-function to retrieve all active DTCs.
78
+ The tool might then use another sub-function to read the snapshot
79
+ data for a specific DTC to get more context about when the fault
80
+ occurred.
81
+
82
+ Attributes:
83
+ uds_server: The UDS server instance.
84
+ """
85
+ def __init__(self, uds_server: 'UdsServer') -> None:
86
+ self.uds_server: 'UdsServer' = uds_server
87
+
88
+ def process_request(self, data_stream: list) -> list:
89
+ """
90
+ Processes a Read DTC Information request.
91
+
92
+ Args:
93
+ data_stream: The request data stream.
94
+
95
+ Returns:
96
+ A list of bytes representing the response.
97
+ """
98
+ if len(data_stream) < 2:
99
+ return self.uds_server.negative_response.report_negative_response(
100
+ self.uds_server.SID.RDTCI, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
101
+ )
102
+
103
+ sub_function = data_stream[1]
104
+
105
+ if sub_function == self.uds_server.SFID.REPORT_NUMBER_OF_DTC_BY_STATUS_MASK:
106
+ if len(data_stream) != 3:
107
+ return self.uds_server.negative_response.report_negative_response(
108
+ self.uds_server.SID.RDTCI, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
109
+ )
110
+ status_mask = data_stream[2]
111
+ # In this simulation, we'll just count all DTCs regardless of status mask
112
+ num_dtcs = len(self.uds_server.memory.dtcs)
113
+ return self.uds_server.positive_response.report_positive_response(
114
+ self.uds_server.SID.RDTCI, [sub_function, status_mask, 0x01, num_dtcs]
115
+ )
116
+
117
+ elif sub_function == self.uds_server.SFID.REPORT_DTC_BY_STATUS_MASK:
118
+ if len(data_stream) != 3:
119
+ return self.uds_server.negative_response.report_negative_response(
120
+ self.uds_server.SID.RDTCI, self.uds_server.NRC.INCORRECT_MESSAGE_LENGTH_OR_INVALID_FORMAT
121
+ )
122
+ status_mask = data_stream[2]
123
+ # In this simulation, we'll return all DTCs regardless of status mask
124
+ response_data = [sub_function, status_mask]
125
+ for dtc in self.uds_server.memory.dtcs:
126
+ response_data.extend(dtc)
127
+ return self.uds_server.positive_response.report_positive_response(self.uds_server.SID.RDTCI, response_data)
128
+
129
+ else:
130
+ return self.uds_server.negative_response.report_negative_response(
131
+ self.uds_server.SID.RDTCI, self.uds_server.NRC.SUB_FUNCTION_NOT_SUPPORTED
132
+ )
@@ -0,0 +1,189 @@
1
+ from typing import TYPE_CHECKING
2
+ if TYPE_CHECKING:
3
+ from py_uds_demo.core.server import UdsServer
4
+
5
+
6
+ class RequestDownload:
7
+ """
8
+ Handles Request Download (0x34) service requests.
9
+
10
+ What:
11
+ This service is used to initiate a data download from the client to
12
+ the server (ECU). It's the first step in the process of flashing new
13
+ software or writing a large block of data to the ECU.
14
+
15
+ Why:
16
+ It prepares the ECU to receive data, and the ECU can specify the
17
+ maximum size of the data blocks it can accept at a time.
18
+
19
+ How:
20
+ The client sends a request with the SID 0x34, the memory address
21
+ where the data should be stored, and the total size of the data.
22
+
23
+ Note:
24
+ This service is not fully implemented in this simulator.
25
+ """
26
+ def __init__(self, uds_server: 'UdsServer') -> None:
27
+ self.uds_server: 'UdsServer' = uds_server
28
+
29
+ def process_request(self, data_stream: list) -> list:
30
+ """
31
+ Processes a Request Download request.
32
+
33
+ Args:
34
+ data_stream: The request data stream.
35
+
36
+ Returns:
37
+ A negative response, as this service is not supported.
38
+ """
39
+ return self.uds_server.negative_response.report_negative_response(
40
+ self.uds_server.SID.RD, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
41
+ )
42
+
43
+
44
+ class RequestUpload:
45
+ """
46
+ Handles Request Upload (0x35) service requests.
47
+
48
+ What:
49
+ This service is used to initiate a data upload from the server (ECU)
50
+ to the client.
51
+
52
+ Why:
53
+ It's used to read large blocks of data from the ECU, such as log
54
+ files, calibration data, or the entire memory content.
55
+
56
+ How:
57
+ The client sends a request with the SID 0x35, the memory address
58
+ of the data to be uploaded, and the size of the data.
59
+
60
+ Note:
61
+ This service is not fully implemented in this simulator.
62
+ """
63
+ def __init__(self, uds_server: 'UdsServer') -> None:
64
+ self.uds_server: 'UdsServer' = uds_server
65
+
66
+ def process_request(self, data_stream: list) -> list:
67
+ """
68
+ Processes a Request Upload request.
69
+
70
+ Args:
71
+ data_stream: The request data stream.
72
+
73
+ Returns:
74
+ A negative response, as this service is not supported.
75
+ """
76
+ return self.uds_server.negative_response.report_negative_response(
77
+ self.uds_server.SID.RU, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
78
+ )
79
+
80
+
81
+ class TransferData:
82
+ """
83
+ Handles Transfer Data (0x36) service requests.
84
+
85
+ What:
86
+ This service is used to transfer data blocks between the client and
87
+ the server during an upload or download operation.
88
+
89
+ Why:
90
+ It's the workhorse of the data transfer process, responsible for
91
+ moving the actual data in chunks.
92
+
93
+ How:
94
+ After a download or upload is initiated, the client (for downloads)
95
+ or server (for uploads) sends a sequence of Transfer Data requests,
96
+ each containing a block of data.
97
+
98
+ Note:
99
+ This service is not fully implemented in this simulator.
100
+ """
101
+ def __init__(self, uds_server: 'UdsServer') -> None:
102
+ self.uds_server: 'UdsServer' = uds_server
103
+
104
+ def process_request(self, data_stream: list) -> list:
105
+ """
106
+ Processes a Transfer Data request.
107
+
108
+ Args:
109
+ data_stream: The request data stream.
110
+
111
+ Returns:
112
+ A negative response, as this service is not supported.
113
+ """
114
+ return self.uds_server.negative_response.report_negative_response(
115
+ self.uds_server.SID.TD, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
116
+ )
117
+
118
+
119
+ class RequestTransferExit:
120
+ """
121
+ Handles Request Transfer Exit (0x37) service requests.
122
+
123
+ What:
124
+ This service is used to terminate a data transfer sequence.
125
+
126
+ Why:
127
+ It signals the end of the upload or download process, allowing the
128
+ server to perform any necessary cleanup or verification.
129
+
130
+ How:
131
+ The client sends a request with the SID 0x37 to indicate that the
132
+ transfer is complete.
133
+
134
+ Note:
135
+ This service is not fully implemented in this simulator.
136
+ """
137
+ def __init__(self, uds_server: 'UdsServer') -> None:
138
+ self.uds_server: 'UdsServer' = uds_server
139
+
140
+ def process_request(self, data_stream: list) -> list:
141
+ """
142
+ Processes a Request Transfer Exit request.
143
+
144
+ Args:
145
+ data_stream: The request data stream.
146
+
147
+ Returns:
148
+ A negative response, as this service is not supported.
149
+ """
150
+ return self.uds_server.negative_response.report_negative_response(
151
+ self.uds_server.SID.RTE, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
152
+ )
153
+
154
+
155
+ class RequestFileTransfer:
156
+ """
157
+ Handles Request File Transfer (0x38) service requests.
158
+
159
+ What:
160
+ This service provides a more advanced and flexible way to transfer
161
+ files between the client and the server, often with file-system-like
162
+ operations.
163
+
164
+ Why:
165
+ It's designed to be more powerful than the older upload/download
166
+ services, supporting more complex use cases.
167
+
168
+ How:
169
+ The specifics are complex and can vary between implementations.
170
+
171
+ Note:
172
+ This service is not fully implemented in this simulator.
173
+ """
174
+ def __init__(self, uds_server: 'UdsServer') -> None:
175
+ self.uds_server: 'UdsServer' = uds_server
176
+
177
+ def process_request(self, data_stream: list) -> list:
178
+ """
179
+ Processes a Request File Transfer request.
180
+
181
+ Args:
182
+ data_stream: The request data stream.
183
+
184
+ Returns:
185
+ A negative response, as this service is not supported.
186
+ """
187
+ return self.uds_server.negative_response.report_negative_response(
188
+ self.uds_server.SID.RFT, self.uds_server.NRC.SERVICE_NOT_SUPPORTED
189
+ )
File without changes
@@ -0,0 +1,30 @@
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
+ from typing import List
4
+
5
+ from py_uds_demo.core.client import UdsClient
6
+
7
+ app = FastAPI()
8
+ client = UdsClient()
9
+
10
+ class UdsRequest(BaseModel):
11
+ data: List[int]
12
+
13
+ @app.post("/send_request")
14
+ async def send_request(request: UdsRequest):
15
+ """
16
+ Sends a UDS request to the server.
17
+ """
18
+ response = client.send_request(request.data, False)
19
+ return {"response": response}
20
+
21
+ @app.get("/help/{sid}")
22
+ async def get_help(sid: int):
23
+ """
24
+ Returns the docstring for a given service ID.
25
+ """
26
+ service = client.server.service_map.get(sid)
27
+ if service:
28
+ return {"docstring": service.__doc__}
29
+ else:
30
+ raise HTTPException(status_code=404, detail=f"No help found for SID 0x{sid:02X}")
@@ -0,0 +1,51 @@
1
+ from py_uds_demo.core.client import UdsClient
2
+
3
+
4
+ class Cli:
5
+ def __init__(self):
6
+ self.client = UdsClient()
7
+
8
+ def _help(self):
9
+ print("💡 Displaying help information.")
10
+ print("💡 UDS CLI Help:")
11
+ print("💡 Enter diagnostic requests in hex format (e.g., 22 F1 87).")
12
+ print("💡 Type 'exit' or 'q' to quit the CLI.")
13
+ print("💡 Type 'help' or 'h' or '?' for general help.")
14
+ print("💡 Type 'help <SID>' for help on a specific service (e.g., 'help 10').")
15
+
16
+ def run(self):
17
+ print("🏃‍➡️ Running UDS Simulation in CLI mode.")
18
+ self._help()
19
+ while True:
20
+ user_input = input("💉 ")
21
+ if user_input.lower() in ['exit', 'q']:
22
+ print("👋 Closed UDS Simulation CLI mode.")
23
+ break
24
+ if user_input.lower().startswith('help '):
25
+ try:
26
+ sid_str = user_input.split(' ')[1]
27
+ sid = int(sid_str, 16)
28
+ service = self.client.server.service_map.get(sid)
29
+ if service:
30
+ print(service.__doc__)
31
+ else:
32
+ print(f"😡 No help found for SID 0x{sid:02X}.")
33
+ except (ValueError, IndexError):
34
+ print("😡 Invalid help command. Use 'help <SID_in_hex>' (e.g., 'help 10').")
35
+ continue
36
+ if user_input.lower() in ['help', 'h', '?']:
37
+ self._help()
38
+ continue
39
+ diagnostic_request_clean = user_input.replace(" ", "")
40
+ try:
41
+ diagnostic_request_stream = [int(diagnostic_request_clean[i:i+2], 16) for i in range(0, len(diagnostic_request_clean), 2)]
42
+ response = self.client.send_request(diagnostic_request_stream, True)
43
+ print(response)
44
+ except ValueError:
45
+ print(f"😡 Invalid input({user_input}). Please enter a valid hex string.")
46
+ continue
47
+
48
+
49
+ if __name__ == "__main__":
50
+ cli = Cli()
51
+ cli.run()
@@ -0,0 +1,83 @@
1
+ from dearpygui import dearpygui as dpg
2
+ from py_uds_demo.core.client import UdsClient
3
+
4
+ class Gui:
5
+ def __init__(self):
6
+ self.client = UdsClient()
7
+ self._setup_ui()
8
+
9
+ def _setup_ui(self):
10
+ dpg.create_context()
11
+ dpg.create_viewport(title="UDS Simulation GUI", width=920, height=500)
12
+
13
+ with dpg.window(label="UDS Simulation GUI", width=900, height=480, tag="main_window"):
14
+ with dpg.group(horizontal=True):
15
+ dpg.add_checkbox(label="Tester Present", callback=self._toggle_tester_present, tag="tester_present_checkbox")
16
+
17
+ with dpg.child_window(border=True, autosize_x=True, autosize_y=False, height=120):
18
+ with dpg.group(horizontal=True):
19
+ dpg.add_text("Tx Request")
20
+ dpg.add_input_text(label="", width=400, tag="request_entry", on_enter=True, callback=self._send_request_callback)
21
+ dpg.add_button(label="Send", callback=self._send_request_callback)
22
+ dpg.add_text("Rx Response")
23
+ dpg.add_input_text(label="", width=850, height=50, multiline=True, tag="response_textbox", readonly=True)
24
+
25
+ with dpg.child_window(border=True, autosize_x=True, autosize_y=False, height=120):
26
+ dpg.add_text("History")
27
+ dpg.add_input_text(multiline=True, width=850, height=80, tag="history_textbox", readonly=True)
28
+
29
+ with dpg.group(horizontal=True):
30
+ dpg.add_text("Help")
31
+ dpg.add_input_text(label="", width=200, tag="help_entry", hint="Enter SID (e.g., 10)", on_enter=True, callback=self._show_help_callback)
32
+ dpg.add_button(label="Get Help", callback=self._show_help_callback)
33
+
34
+ dpg.setup_dearpygui()
35
+ dpg.show_viewport()
36
+ dpg.set_primary_window("main_window", True)
37
+ dpg.start_dearpygui()
38
+ dpg.destroy_context()
39
+
40
+ def _show_help_callback(self, sender, app_data, user_data):
41
+ sid_str = dpg.get_value("help_entry")
42
+ try:
43
+ sid = int(sid_str, 16)
44
+ service = self.client.server.service_map.get(sid)
45
+ if service:
46
+ title = f"Help for SID 0x{sid:02X}"
47
+ message = service.__doc__ or "No documentation available."
48
+ else:
49
+ title = "Error"
50
+ message = f"No help found for SID 0x{sid:02X}"
51
+ except ValueError:
52
+ title = "Error"
53
+ message = "Invalid SID. Please enter a valid hex value."
54
+
55
+ if dpg.does_item_exist("help_modal"):
56
+ dpg.delete_item("help_modal")
57
+ with dpg.window(label=title, modal=True, tag="help_modal", no_close=False, width=700, height=300):
58
+ dpg.add_text(message)
59
+ dpg.add_button(label="Close", callback=lambda: dpg.delete_item("help_modal"))
60
+
61
+ def _send_request_callback(self, sender=None, app_data=None, user_data=None):
62
+ request_data = dpg.get_value("request_entry").replace(" ", "")
63
+ try:
64
+ request_data_stream = [int(request_data[i:i+2], 16) for i in range(0, len(request_data), 2)]
65
+ request_data_formatted = " ".join(f"{b:02X}" for b in request_data_stream)
66
+ response_data = self.client.send_request(request_data_stream, False)
67
+ response_data_formatted = " ".join(f"{b:02X}" for b in response_data)
68
+ except ValueError:
69
+ request_data_formatted = f"😡 Invalid input({request_data}). Please enter a valid hex string."
70
+ response_data_formatted = ""
71
+ except Exception as e:
72
+ request_data_formatted = f"😡 An error occurred: {e}"
73
+ response_data_formatted = ""
74
+ dpg.set_value("response_textbox", response_data_formatted)
75
+ current_history = dpg.get_value("history_textbox")
76
+ new_entry = f"-> {request_data_formatted}\n<- {response_data_formatted}\n"
77
+ dpg.set_value("history_textbox", new_entry + current_history)
78
+
79
+ def _toggle_tester_present(self, sender, app_data, user_data):
80
+ self.client.server.diagnostic_session_control.tester_present_active = bool(dpg.get_value("tester_present_checkbox"))
81
+
82
+ if __name__ == "__main__":
83
+ Gui()