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,422 @@
|
|
|
1
|
+
from nicegui import ui
|
|
2
|
+
|
|
3
|
+
from py_uds_demo.core.client import UdsClient
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Web:
|
|
7
|
+
"""
|
|
8
|
+
WebUi provides a NiceGUI-based web interface for interacting with the UDS (Unified Diagnostic Services) simulator.
|
|
9
|
+
|
|
10
|
+
This class manages the UI components, handles user input, processes diagnostic requests, and logs interactions.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# UI Constants
|
|
14
|
+
HEADER_HEIGHT = '60px'
|
|
15
|
+
SIDEBAR_WIDTH = '400px'
|
|
16
|
+
HEADER_PADDING = '0 20px'
|
|
17
|
+
CARD_PADDING = '16px'
|
|
18
|
+
CARD_HEADER_MARGIN = '12px'
|
|
19
|
+
INNER_PADDING = '12px'
|
|
20
|
+
SMALL_PADDING = '8px'
|
|
21
|
+
HELP_INPUT_WIDTH = '180px'
|
|
22
|
+
CHAT_MAX_WIDTH = '80%'
|
|
23
|
+
SPACING_SM = '8px'
|
|
24
|
+
SPACING_MD = '12px'
|
|
25
|
+
SPACING_LG = '16px'
|
|
26
|
+
|
|
27
|
+
# Color Scheme
|
|
28
|
+
COLORS = {
|
|
29
|
+
'header_gradient': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
30
|
+
'primary': '#667eea',
|
|
31
|
+
'primary_light': '#8b9efc',
|
|
32
|
+
'secondary': '#f093fb',
|
|
33
|
+
'success': '#4ade80',
|
|
34
|
+
'success_light': '#86efac',
|
|
35
|
+
'warning': '#fbbf24',
|
|
36
|
+
'error': '#f87171',
|
|
37
|
+
'info': '#60a5fa',
|
|
38
|
+
'purple': '#a855f7',
|
|
39
|
+
'pink': '#ec4899',
|
|
40
|
+
'indigo': '#6366f1',
|
|
41
|
+
'teal': '#14b8a6',
|
|
42
|
+
'orange': '#f97316',
|
|
43
|
+
'user_message': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
|
|
44
|
+
'assistant_message': 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)',
|
|
45
|
+
'card_bg': '#ffffff',
|
|
46
|
+
'card_shadow': '0 10px 40px rgba(0,0,0,0.1)',
|
|
47
|
+
'card_shadow_hover': '0 20px 60px rgba(0,0,0,0.15)',
|
|
48
|
+
'sidebar_bg': 'linear-gradient(180deg, #f8f9fa 0%, #e9ecef 100%)',
|
|
49
|
+
'main_bg': 'linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%)',
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Animations
|
|
53
|
+
TRANSITIONS = {
|
|
54
|
+
'smooth': 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
55
|
+
'bounce': 'all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55)',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# Style Templates
|
|
59
|
+
GLASS_CONTAINER = 'background: rgba(255,255,255,0.12); backdrop-filter: blur(12px); border-radius: 24px; border: 1px solid rgba(255,255,255,0.2); transition: all 0.3s ease;'
|
|
60
|
+
CARD_BORDER_RADIUS = '16px'
|
|
61
|
+
CARD_ICON_RADIUS = '10px'
|
|
62
|
+
SID_ITEM_RADIUS = '10px'
|
|
63
|
+
|
|
64
|
+
# Quick Reference SIDs with icons and colors
|
|
65
|
+
SID_REFERENCE = [
|
|
66
|
+
('settings', '0x10 - Diagnostic Session Control', '#667eea'),
|
|
67
|
+
('refresh', '0x11 - ECU Reset', '#14b8a6'),
|
|
68
|
+
('search', '0x22 - Read Data By Identifier', '#f97316'),
|
|
69
|
+
('lock', '0x27 - Security Access', '#f87171'),
|
|
70
|
+
('edit', '0x2E - Write Data By Identifier', '#a855f7'),
|
|
71
|
+
('build', '0x31 - Routine Control', '#6366f1'),
|
|
72
|
+
('favorite', '0x3E - Tester Present', '#ec4899'),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
def __init__(self):
|
|
76
|
+
"""
|
|
77
|
+
Initialize the WebUi instance.
|
|
78
|
+
|
|
79
|
+
Sets up the UdsClient and builds the UI components.
|
|
80
|
+
"""
|
|
81
|
+
self.uds_client = UdsClient()
|
|
82
|
+
self.logger = self.uds_client.server.logger
|
|
83
|
+
self._build_ui()
|
|
84
|
+
|
|
85
|
+
def _build_ui(self):
|
|
86
|
+
"""Build the complete UI interface."""
|
|
87
|
+
self._build_header()
|
|
88
|
+
self._build_main_content()
|
|
89
|
+
|
|
90
|
+
# UI Helper Methods
|
|
91
|
+
|
|
92
|
+
def _create_card_header(self, icon: str, title: str, color1: str, color2: str):
|
|
93
|
+
"""Create a card header with icon badge and gradient text.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
icon: Material icon name
|
|
97
|
+
title: Header title text
|
|
98
|
+
color1: First gradient color
|
|
99
|
+
color2: Second gradient color
|
|
100
|
+
"""
|
|
101
|
+
with ui.row().classes('items-center gap-3').style(f'margin-bottom: {self.CARD_HEADER_MARGIN};'):
|
|
102
|
+
with ui.card().classes('').style(f'padding: 8px; background: linear-gradient(135deg, {color1} 0%, {color2} 100%); border-radius: {self.CARD_ICON_RADIUS};'):
|
|
103
|
+
ui.icon(icon, size='sm').classes('text-white')
|
|
104
|
+
ui.label(title).classes('text-base font-bold').style(f'background: linear-gradient(135deg, {color1} 0%, {color2} 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent;')
|
|
105
|
+
|
|
106
|
+
def _create_glass_container(self, content_builder, hover_class: str = 'hover:bg-white/20'):
|
|
107
|
+
"""Create a glass morphism container.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
content_builder: Function to build content inside the container
|
|
111
|
+
hover_class: CSS class for hover effect
|
|
112
|
+
"""
|
|
113
|
+
with ui.row().classes(f'items-center gap-2 px-3 py-2 {hover_class}').style(self.GLASS_CONTAINER):
|
|
114
|
+
content_builder()
|
|
115
|
+
|
|
116
|
+
def _create_sid_item(self, icon: str, sid_info: str, color: str, click_handler):
|
|
117
|
+
"""Create a clickable SID reference item.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
icon: Material icon name
|
|
121
|
+
sid_info: SID information text
|
|
122
|
+
color: Theme color for the item
|
|
123
|
+
click_handler: Click event handler
|
|
124
|
+
"""
|
|
125
|
+
with ui.card().classes('w-full').style(
|
|
126
|
+
f'padding: {self.SPACING_SM} {self.SPACING_MD}; '
|
|
127
|
+
f'background: linear-gradient(135deg, {color}15 0%, {color}05 100%); '
|
|
128
|
+
f'border-radius: {self.SID_ITEM_RADIUS}; cursor: pointer; '
|
|
129
|
+
f'transition: {self.TRANSITIONS["smooth"]}; border-left: 3px solid {color};'
|
|
130
|
+
).classes('hover:scale-105 hover:shadow-lg').on('click', click_handler):
|
|
131
|
+
with ui.row().classes('items-center gap-3'):
|
|
132
|
+
with ui.card().classes('').style(f'padding: 6px; background: {color}; border-radius: 8px;'):
|
|
133
|
+
ui.icon(icon, size='xs').classes('text-white')
|
|
134
|
+
ui.label(sid_info).classes('text-xs font-medium').style(f'color: {color};')
|
|
135
|
+
|
|
136
|
+
def run(self):
|
|
137
|
+
"""
|
|
138
|
+
Launch the NiceGUI app for the UDS simulator UI.
|
|
139
|
+
|
|
140
|
+
Starts the NiceGUI server.
|
|
141
|
+
"""
|
|
142
|
+
ui.run(title="PY-UDS-DEMO SIM", favicon="🚗")
|
|
143
|
+
|
|
144
|
+
def _build_header(self):
|
|
145
|
+
"""Build the application header with controls."""
|
|
146
|
+
with ui.header().classes('text-white shadow-xl').style(
|
|
147
|
+
f'height: {self.HEADER_HEIGHT}; padding: {self.HEADER_PADDING}; '
|
|
148
|
+
f'background: {self.COLORS["header_gradient"]}; '
|
|
149
|
+
f'box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3); '
|
|
150
|
+
f'border-bottom: 1px solid rgba(255,255,255,0.1);'
|
|
151
|
+
):
|
|
152
|
+
with ui.row().classes('w-full items-center justify-between'):
|
|
153
|
+
self._build_header_branding()
|
|
154
|
+
self._build_header_controls()
|
|
155
|
+
self._build_header_help()
|
|
156
|
+
|
|
157
|
+
def _build_header_branding(self):
|
|
158
|
+
"""Build the header branding section."""
|
|
159
|
+
with ui.row().classes('items-center gap-4'):
|
|
160
|
+
ui.icon('directions_car', size='lg').classes('text-white').style(
|
|
161
|
+
'filter: drop-shadow(0 2px 8px rgba(255,255,255,0.3));'
|
|
162
|
+
)
|
|
163
|
+
ui.label('UDS Diagnostic Simulator').classes('text-lg font-bold text-white').style(
|
|
164
|
+
'letter-spacing: 0.5px; text-shadow: 0 2px 4px rgba(0,0,0,0.3);'
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def _build_header_controls(self):
|
|
168
|
+
"""Build the header controls section."""
|
|
169
|
+
with ui.row().classes('items-center gap-6 flex-grow justify-end'):
|
|
170
|
+
with ui.row().classes('items-center gap-3 px-4 py-2 hover:bg-white/20').style(self.GLASS_CONTAINER):
|
|
171
|
+
self.tester_present_checkbox = ui.checkbox(
|
|
172
|
+
'Tester Present',
|
|
173
|
+
value=False,
|
|
174
|
+
on_change=self._update_tester_present
|
|
175
|
+
).classes('text-white font-medium').props('dark').style('margin: 0; font-size: 13px;')
|
|
176
|
+
|
|
177
|
+
def _build_header_help(self):
|
|
178
|
+
"""Build the header help section."""
|
|
179
|
+
with ui.row().classes('items-center gap-2'):
|
|
180
|
+
def build_help_content():
|
|
181
|
+
ui.icon('help_outline', size='sm').classes('text-white').style('opacity: 0.9;')
|
|
182
|
+
self.help_sid_input = ui.input(
|
|
183
|
+
placeholder='Search SID...',
|
|
184
|
+
).classes('text-sm').props('dark dense borderless').style(
|
|
185
|
+
f'width: {self.HELP_INPUT_WIDTH}; background: transparent; color: white;'
|
|
186
|
+
).on('keydown.enter', self._show_help)
|
|
187
|
+
ui.button(icon='search', on_click=self._show_help).props('flat dense round').classes(
|
|
188
|
+
'text-white'
|
|
189
|
+
).tooltip('Search SID Help').style('transition: all 0.2s ease;').classes('hover:bg-white/20')
|
|
190
|
+
|
|
191
|
+
self._create_glass_container(build_help_content)
|
|
192
|
+
|
|
193
|
+
def _build_main_content(self):
|
|
194
|
+
"""Build the main content area with sidebar and chat."""
|
|
195
|
+
with ui.row().classes('w-full gap-4 p-4').style(f'height: calc(100vh - {self.HEADER_HEIGHT}); background: {self.COLORS["main_bg"]}; overflow: hidden;'):
|
|
196
|
+
self._build_sidebar()
|
|
197
|
+
self._build_chat_area()
|
|
198
|
+
|
|
199
|
+
def _build_sidebar(self):
|
|
200
|
+
"""Build the left sidebar with controls and reference."""
|
|
201
|
+
with ui.column().classes('gap-4').style(f'width: {self.SIDEBAR_WIDTH}; height: 100%; overflow-y: auto; background: {self.COLORS["sidebar_bg"]}; padding: {self.SPACING_MD}; border-radius: 20px; box-shadow: 0 8px 32px rgba(0,0,0,0.1);'):
|
|
202
|
+
self._build_diagnostic_request_card()
|
|
203
|
+
self._build_quick_reference_card()
|
|
204
|
+
|
|
205
|
+
def _build_diagnostic_request_card(self):
|
|
206
|
+
"""Build the diagnostic request input card."""
|
|
207
|
+
with ui.card().classes('w-full shadow-lg').style(
|
|
208
|
+
f'padding: {self.CARD_PADDING}; background: {self.COLORS["card_bg"]}; '
|
|
209
|
+
f'border-radius: {self.CARD_BORDER_RADIUS}; border: 2px solid {self.COLORS["primary_light"]}; '
|
|
210
|
+
f'box-shadow: {self.COLORS["card_shadow"]};'
|
|
211
|
+
):
|
|
212
|
+
self._create_card_header('send', 'Diagnostic Request', self.COLORS['primary'], self.COLORS['purple'])
|
|
213
|
+
|
|
214
|
+
self.diag_req_input = ui.input(
|
|
215
|
+
placeholder='e.g., 22 F1 87',
|
|
216
|
+
).classes('w-full').props('outlined rounded dense').style(
|
|
217
|
+
'font-size: 14px;'
|
|
218
|
+
).on('keydown.enter', self._handle_diagnostic_request)
|
|
219
|
+
|
|
220
|
+
ui.button('Send Request', on_click=self._handle_diagnostic_request, icon='send').props(
|
|
221
|
+
'rounded'
|
|
222
|
+
).classes('w-full hover:scale-105').style(
|
|
223
|
+
f'background: linear-gradient(135deg, {self.COLORS["primary"]} 0%, {self.COLORS["purple"]} 100%); '
|
|
224
|
+
f'color: white; font-weight: 600; padding: {self.INNER_PADDING}; '
|
|
225
|
+
f'margin-top: {self.CARD_HEADER_MARGIN}; transition: {self.TRANSITIONS["smooth"]}; '
|
|
226
|
+
f'box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);'
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def _build_quick_reference_card(self):
|
|
230
|
+
"""Build the quick reference card with SID information."""
|
|
231
|
+
with ui.card().classes('w-full flex-grow shadow-lg').style(
|
|
232
|
+
f'padding: {self.CARD_PADDING}; background: {self.COLORS["card_bg"]}; '
|
|
233
|
+
f'border-radius: {self.CARD_BORDER_RADIUS}; border: 2px solid {self.COLORS["indigo"]}; '
|
|
234
|
+
f'box-shadow: {self.COLORS["card_shadow"]};'
|
|
235
|
+
):
|
|
236
|
+
self._create_card_header('bookmarks', 'Quick Reference', self.COLORS['indigo'], self.COLORS['purple'])
|
|
237
|
+
|
|
238
|
+
with ui.column().classes('gap-2'):
|
|
239
|
+
for icon, sid_info, color in self.SID_REFERENCE:
|
|
240
|
+
sid_hex = sid_info.split(' ')[0]
|
|
241
|
+
self._create_sid_item(icon, sid_info, color, lambda sid=sid_hex: self._show_help_for_sid(sid))
|
|
242
|
+
|
|
243
|
+
def _build_chat_area(self):
|
|
244
|
+
"""Build the chat display area."""
|
|
245
|
+
with ui.column().classes('flex-1').style(
|
|
246
|
+
f'height: 100%; overflow: hidden; '
|
|
247
|
+
f'background: linear-gradient(135deg, rgba(236, 72, 153, 0.05) 0%, rgba(249, 168, 212, 0.05) 100%); '
|
|
248
|
+
f'padding: {self.SPACING_MD}; border-radius: 20px; box-shadow: 0 8px 32px rgba(0,0,0,0.1);'
|
|
249
|
+
):
|
|
250
|
+
with ui.card().classes('w-full shadow-lg').style(
|
|
251
|
+
f'padding: {self.CARD_PADDING}; background: {self.COLORS["card_bg"]}; '
|
|
252
|
+
f'border-radius: {self.CARD_BORDER_RADIUS}; border: 2px solid {self.COLORS["pink"]}; '
|
|
253
|
+
f'height: 100%; display: flex; flex-direction: column; box-shadow: {self.COLORS["card_shadow"]};'
|
|
254
|
+
):
|
|
255
|
+
with ui.row().classes('items-center gap-3').style(f'flex-shrink: 0; margin-bottom: {self.CARD_HEADER_MARGIN};'):
|
|
256
|
+
self._create_card_header('chat', 'Diagnostic Communication', self.COLORS['pink'], self.COLORS['secondary'])
|
|
257
|
+
|
|
258
|
+
self.chat_container = ui.column().classes('w-full gap-2 rounded-lg').style(
|
|
259
|
+
f'flex: 1; overflow-y: auto; padding: {self.INNER_PADDING}; '
|
|
260
|
+
f'background: linear-gradient(135deg, #fdfbfb 0%, #ebedee 100%);'
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
def _add_message(self, role: str, content: str):
|
|
264
|
+
"""
|
|
265
|
+
Add a message to the chat display.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
role (str): Either 'user' or 'assistant'
|
|
269
|
+
content (str): The message content
|
|
270
|
+
"""
|
|
271
|
+
is_user = role == 'user'
|
|
272
|
+
alignment = 'justify-end' if is_user else 'justify-start'
|
|
273
|
+
sender_name = 'You' if is_user else '🤖 UDS Simulator'
|
|
274
|
+
gradient = self.COLORS['user_message'] if is_user else self.COLORS['assistant_message']
|
|
275
|
+
|
|
276
|
+
with self.chat_container:
|
|
277
|
+
with ui.row().classes(f'w-full {alignment}').style(f'margin-bottom: {self.SPACING_SM}; animation: slideIn 0.3s ease-out;'):
|
|
278
|
+
with ui.card().classes('shadow-md').style(f'max-width: {self.CHAT_MAX_WIDTH}; background: {gradient}; border-radius: 16px; padding: {self.INNER_PADDING}; box-shadow: 0 4px 20px rgba(0,0,0,0.15);'):
|
|
279
|
+
ui.label(sender_name).classes('text-xs font-bold text-white').style(f'margin-bottom: {self.SPACING_SM};')
|
|
280
|
+
ui.label(content).classes('text-sm text-white').style('word-break: break-word; white-space: pre-wrap;')
|
|
281
|
+
|
|
282
|
+
# Add animation keyframes
|
|
283
|
+
ui.add_head_html('''
|
|
284
|
+
<style>
|
|
285
|
+
@keyframes slideIn {
|
|
286
|
+
from {
|
|
287
|
+
opacity: 0;
|
|
288
|
+
transform: translateY(10px);
|
|
289
|
+
}
|
|
290
|
+
to {
|
|
291
|
+
opacity: 1;
|
|
292
|
+
transform: translateY(0);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
</style>
|
|
296
|
+
''')
|
|
297
|
+
|
|
298
|
+
self._scroll_chat_to_bottom()
|
|
299
|
+
|
|
300
|
+
def _scroll_chat_to_bottom(self):
|
|
301
|
+
"""Scroll chat container to the bottom."""
|
|
302
|
+
ui.run_javascript(f'getElement({self.chat_container.id}).scrollTop = getElement({self.chat_container.id}).scrollHeight')
|
|
303
|
+
|
|
304
|
+
def _handle_diagnostic_request(self):
|
|
305
|
+
"""
|
|
306
|
+
Handle diagnostic request submission and display response.
|
|
307
|
+
"""
|
|
308
|
+
diagnostic_request = self.diag_req_input.value
|
|
309
|
+
if not self._validate_input(diagnostic_request):
|
|
310
|
+
return
|
|
311
|
+
|
|
312
|
+
diagnostic_request_clean = diagnostic_request.replace(" ", "")
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
diagnostic_request_stream = self._parse_hex_string(diagnostic_request_clean)
|
|
316
|
+
user_sent_request = self.uds_client.format_request(diagnostic_request_stream)
|
|
317
|
+
self._add_message('user', user_sent_request)
|
|
318
|
+
|
|
319
|
+
diagnostic_response = self.uds_client.send_request(diagnostic_request_stream, True)
|
|
320
|
+
self._add_message('assistant', diagnostic_response)
|
|
321
|
+
except ValueError:
|
|
322
|
+
self._handle_invalid_hex(diagnostic_request)
|
|
323
|
+
except Exception as e:
|
|
324
|
+
self._handle_request_error(diagnostic_request, e)
|
|
325
|
+
finally:
|
|
326
|
+
self.diag_req_input.value = ''
|
|
327
|
+
|
|
328
|
+
def _show_help(self):
|
|
329
|
+
"""
|
|
330
|
+
Display help information for a specific SID.
|
|
331
|
+
"""
|
|
332
|
+
sid_str = self.help_sid_input.value
|
|
333
|
+
if not self._validate_input(sid_str):
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
try:
|
|
337
|
+
sid = int(sid_str, 16)
|
|
338
|
+
service = self.uds_client.server.service_map.get(sid)
|
|
339
|
+
|
|
340
|
+
self._add_message('user', f'Help for SID 0x{sid:02X}')
|
|
341
|
+
|
|
342
|
+
help_text = service.__doc__ if service else f'No help found for SID 0x{sid:02X}.'
|
|
343
|
+
if service and not service.__doc__:
|
|
344
|
+
help_text = f'No documentation available for SID 0x{sid:02X}.'
|
|
345
|
+
|
|
346
|
+
self._add_message('assistant', help_text)
|
|
347
|
+
except (ValueError, IndexError):
|
|
348
|
+
self._add_message('user', f'Help for SID {sid_str}')
|
|
349
|
+
self._add_message('assistant', 'Invalid SID. Please enter a valid hex value.')
|
|
350
|
+
finally:
|
|
351
|
+
self.help_sid_input.value = ''
|
|
352
|
+
|
|
353
|
+
def _show_help_for_sid(self, sid_hex: str):
|
|
354
|
+
"""Display help information for a clicked SID from Quick Reference.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
sid_hex (str): The SID in hex format (e.g., '0x10')
|
|
358
|
+
"""
|
|
359
|
+
try:
|
|
360
|
+
sid = int(sid_hex, 16)
|
|
361
|
+
service = self.uds_client.server.service_map.get(sid)
|
|
362
|
+
|
|
363
|
+
self._add_message('user', f'Help for SID {sid_hex}')
|
|
364
|
+
|
|
365
|
+
help_text = service.__doc__ if service else f'No help found for SID {sid_hex}.'
|
|
366
|
+
if service and not service.__doc__:
|
|
367
|
+
help_text = f'No documentation available for SID {sid_hex}.'
|
|
368
|
+
|
|
369
|
+
self._add_message('assistant', help_text)
|
|
370
|
+
except (ValueError, IndexError):
|
|
371
|
+
self._add_message('user', f'Help for SID {sid_hex}')
|
|
372
|
+
self._add_message('assistant', 'Invalid SID. Please enter a valid hex value.')
|
|
373
|
+
|
|
374
|
+
def _update_tester_present(self, event):
|
|
375
|
+
"""
|
|
376
|
+
Update the tester present flag in the UDS server and log the action.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
event: NiceGUI event object containing the checkbox value
|
|
380
|
+
"""
|
|
381
|
+
value = event.value
|
|
382
|
+
self.uds_client.server.diagnostic_session_control.tester_present_active = value
|
|
383
|
+
|
|
384
|
+
status = 'activated' if value else 'deactivated'
|
|
385
|
+
icon = '✔️' if value else '✖️'
|
|
386
|
+
self.logger.info(f'tester present [{icon}] {status}')
|
|
387
|
+
|
|
388
|
+
ui.notify(
|
|
389
|
+
f'Tester Present {status} {icon}',
|
|
390
|
+
type='positive' if value else 'info'
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
# Validation & Parsing Helpers
|
|
394
|
+
|
|
395
|
+
@staticmethod
|
|
396
|
+
def _validate_input(value: str) -> bool:
|
|
397
|
+
"""Validate that input is not empty."""
|
|
398
|
+
return bool(value and value.strip())
|
|
399
|
+
|
|
400
|
+
@staticmethod
|
|
401
|
+
def _parse_hex_string(hex_string: str) -> list[int]:
|
|
402
|
+
"""Parse hex string into list of integers."""
|
|
403
|
+
return [int(hex_string[i:i+2], 16) for i in range(0, len(hex_string), 2)]
|
|
404
|
+
|
|
405
|
+
# Error Handling Helpers
|
|
406
|
+
|
|
407
|
+
def _handle_invalid_hex(self, diagnostic_request: str):
|
|
408
|
+
"""Handle invalid hex input."""
|
|
409
|
+
self._add_message('user', diagnostic_request)
|
|
410
|
+
self._add_message('assistant', 'Invalid hex input. Please enter a valid hex string.')
|
|
411
|
+
self.logger.warning(f"Invalid Diagnostic Request 💉 {diagnostic_request}")
|
|
412
|
+
|
|
413
|
+
def _handle_request_error(self, diagnostic_request: str, error: Exception):
|
|
414
|
+
"""Handle request processing error."""
|
|
415
|
+
self._add_message('user', diagnostic_request)
|
|
416
|
+
self._add_message('assistant', f'An error occurred while processing the request. {error}')
|
|
417
|
+
self.logger.error(f"Error occurred while processing request 💉 {diagnostic_request}: {error}")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
if __name__ in {"__main__", "__mp_main__"}:
|
|
421
|
+
web_ui = Web()
|
|
422
|
+
web_ui.run()
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: py-uds-demo
|
|
3
|
+
Version: 26.0.1
|
|
4
|
+
Summary: Learn and Practice UDS Protocol
|
|
5
|
+
Keywords: python,template
|
|
6
|
+
Author: chaitu-ycr
|
|
7
|
+
Author-email: chaitu-ycr <chaitu.ycr@gmail.com>
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 chaitu-ycr
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
Classifier: Programming Language :: Python :: 3
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
32
|
+
Requires-Dist: dearpygui
|
|
33
|
+
Requires-Dist: nicegui[native]
|
|
34
|
+
Requires-Python: >=3.10, <=3.14
|
|
35
|
+
Project-URL: homepage, https://github.com/chaitu-ycr/py-uds-demo
|
|
36
|
+
Project-URL: repository, https://github.com/chaitu-ycr/py-uds-demo
|
|
37
|
+
Project-URL: documentation, https://chaitu-ycr.github.io/py-uds-demo/
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# py_uds_demo
|
|
41
|
+
|
|
42
|
+
## Overview
|
|
43
|
+
|
|
44
|
+
`py_uds_demo` is a Python package for learning and practicing the Unified Diagnostic Services (UDS) protocol. It provides a simulator with CLI, GUI, and Web interfaces, allowing users to send diagnostic requests and view responses as per ISO 14229.
|
|
45
|
+
|
|
46
|
+
### Features
|
|
47
|
+
|
|
48
|
+
- UDS protocol simulation (ISO 14229)
|
|
49
|
+
- CLI, GUI (CustomTkinter), and Web (Gradio) interfaces
|
|
50
|
+
- Diagnostic session management, data transmission, input/output control, and more
|
|
51
|
+
- Extensible and modular codebase
|
|
52
|
+
|
|
53
|
+
## [source manual](https://chaitu-ycr.github.io/py-uds-demo/source-manual)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
py_uds_demo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
py_uds_demo/__main__.py,sha256=fA9chl-2dvQnEwASlZ61Bsh7zJvstHRokOYk0raYarA,2148
|
|
3
|
+
py_uds_demo/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
py_uds_demo/core/client.py,sha256=r9p-m68qsgyNLAji0znute0nAY_8VPvavvtbWCYTri4,3104
|
|
5
|
+
py_uds_demo/core/server.py,sha256=6DYcHajC7p4sEp-LP2UHYBEoCK2gZxNBauuQP_IUChM,12812
|
|
6
|
+
py_uds_demo/core/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
py_uds_demo/core/utils/helpers.py,sha256=mhrvOs1PCcgTNZFJPiVocp_3jOjgKC1sPnfCaUVwgmE,14162
|
|
8
|
+
py_uds_demo/core/utils/responses.py,sha256=Oqz36f79dF0pdawA04eXxfvjuvvgqdmxue45A0hk738,1749
|
|
9
|
+
py_uds_demo/core/utils/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
py_uds_demo/core/utils/services/data_transmission.py,sha256=JeTtcNKi3keLfDgdHh_4RdNm9ieCGl4twDvqa0oTyFA,16895
|
|
11
|
+
py_uds_demo/core/utils/services/diagnostic_and_commmunication_management.py,sha256=wX2qVEW29LGT4_XCM-iVhFXuGJukFVaY6lYcQQnids8,31161
|
|
12
|
+
py_uds_demo/core/utils/services/input_output_contol.py,sha256=DJ5z_JAYjphF7JSiSAkx1_Tj5511g4005duoLiFdDiQ,2495
|
|
13
|
+
py_uds_demo/core/utils/services/negative_response.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
14
|
+
py_uds_demo/core/utils/services/remote_activation_of_routine.py,sha256=id_vFBmANOTul5E-PU7ltRuMSE9poF4bTotbEKSkHFo,3311
|
|
15
|
+
py_uds_demo/core/utils/services/stored_data_transmission.py,sha256=IFeiFqZZtRvTm7MgZII1haU1FlATfjKsevC5kxG6D_4,5490
|
|
16
|
+
py_uds_demo/core/utils/services/upload_download.py,sha256=nLhGBANAYCuCTxv9D78FuEE2YR5FEsjCznK_aEkep40,6055
|
|
17
|
+
py_uds_demo/interface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
py_uds_demo/interface/api.py,sha256=CIqQJx_WsTc5rb4D9gFXnScVjtlBV5WJnA0_xfNfVeY,813
|
|
19
|
+
py_uds_demo/interface/cli.py,sha256=aDkAGkJlTnSd45NzpObpf6rY8Jndty1vMWqaQ85LPEk,2138
|
|
20
|
+
py_uds_demo/interface/gui.py,sha256=IpRa--_UKhx2xF-hlWrdFOsQsTxlmnOnMNMwkSoPyTw,4259
|
|
21
|
+
py_uds_demo/interface/web.py,sha256=YwUyS-8Q3T4XiOC0qVfFkUAg2_1E8LZTUoaM_9iu5UY,19724
|
|
22
|
+
py_uds_demo-26.0.1.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
|
|
23
|
+
py_uds_demo-26.0.1.dist-info/METADATA,sha256=WVIgGapVoMVu__wNBrIHGmD8qujWWUtFfuCK2xxpi5U,2522
|
|
24
|
+
py_uds_demo-26.0.1.dist-info/RECORD,,
|