py_canoe 3.0.4__py3-none-any.whl → 26.0.2__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.
Files changed (123) hide show
  1. py_canoe/__init__.py +2 -1
  2. py_canoe/canoe.py +911 -0
  3. py_canoe/core/__init__.py +0 -0
  4. py_canoe/core/application.py +170 -0
  5. py_canoe/core/bus.py +301 -0
  6. py_canoe/core/capl.py +59 -0
  7. py_canoe/core/child_elements/__init__.py +0 -0
  8. py_canoe/core/child_elements/application_model.py +24 -0
  9. py_canoe/core/child_elements/application_model_file.py +21 -0
  10. py_canoe/core/child_elements/application_model_files.py +22 -0
  11. py_canoe/core/child_elements/application_model_setup.py +15 -0
  12. py_canoe/core/child_elements/application_models.py +22 -0
  13. py_canoe/core/child_elements/application_socket.py +11 -0
  14. py_canoe/core/child_elements/application_specific_module.py +24 -0
  15. py_canoe/core/child_elements/application_specific_modules.py +16 -0
  16. py_canoe/core/child_elements/audio_interface.py +28 -0
  17. py_canoe/core/child_elements/available_modules.py +22 -0
  18. py_canoe/core/child_elements/basic_module.py +19 -0
  19. py_canoe/core/child_elements/basic_modules.py +16 -0
  20. py_canoe/core/child_elements/c_libraries.py +28 -0
  21. py_canoe/core/child_elements/c_library.py +33 -0
  22. py_canoe/core/child_elements/can_controller.py +74 -0
  23. py_canoe/core/child_elements/capl_function.py +17 -0
  24. py_canoe/core/child_elements/ccp_setup.py +15 -0
  25. py_canoe/core/child_elements/channel.py +20 -0
  26. py_canoe/core/child_elements/channels.py +19 -0
  27. py_canoe/core/child_elements/communication_setup.py +23 -0
  28. py_canoe/core/child_elements/compile_result.py +22 -0
  29. py_canoe/core/child_elements/configured_channel.py +48 -0
  30. py_canoe/core/child_elements/configured_channels.py +21 -0
  31. py_canoe/core/child_elements/configured_module.py +82 -0
  32. py_canoe/core/child_elements/configured_modules.py +61 -0
  33. py_canoe/core/child_elements/connected_modules.py +14 -0
  34. py_canoe/core/child_elements/data_source.py +42 -0
  35. py_canoe/core/child_elements/data_source_file.py +21 -0
  36. py_canoe/core/child_elements/data_source_files.py +22 -0
  37. py_canoe/core/child_elements/data_source_issue.py +22 -0
  38. py_canoe/core/child_elements/data_source_issues.py +16 -0
  39. py_canoe/core/child_elements/data_source_setup.py +17 -0
  40. py_canoe/core/child_elements/data_sources.py +27 -0
  41. py_canoe/core/child_elements/database_setup.py +62 -0
  42. py_canoe/core/child_elements/device.py +34 -0
  43. py_canoe/core/child_elements/devices.py +13 -0
  44. py_canoe/core/child_elements/diagnostic.py +22 -0
  45. py_canoe/core/child_elements/diagnostic_request.py +59 -0
  46. py_canoe/core/child_elements/diagnostic_response.py +34 -0
  47. py_canoe/core/child_elements/diagnostic_responses.py +13 -0
  48. py_canoe/core/child_elements/diagnostics_setup.py +254 -0
  49. py_canoe/core/child_elements/distributed_mode.py +74 -0
  50. py_canoe/core/child_elements/encoding.py +27 -0
  51. py_canoe/core/child_elements/encodings.py +13 -0
  52. py_canoe/core/child_elements/environment_array.py +13 -0
  53. py_canoe/core/child_elements/environment_group.py +22 -0
  54. py_canoe/core/child_elements/environment_info.py +14 -0
  55. py_canoe/core/child_elements/environment_variable.py +55 -0
  56. py_canoe/core/child_elements/fdx_files.py +50 -0
  57. py_canoe/core/child_elements/file_group_data_source.py +17 -0
  58. py_canoe/core/child_elements/general_setup.py +66 -0
  59. py_canoe/core/child_elements/macros_setup.py +52 -0
  60. py_canoe/core/child_elements/mc_ecus.py +428 -0
  61. py_canoe/core/child_elements/measurement_setup.py +269 -0
  62. py_canoe/core/child_elements/modules.py +87 -0
  63. py_canoe/core/child_elements/most_disassembler.py +21 -0
  64. py_canoe/core/child_elements/most_network_interface.py +4 -0
  65. py_canoe/core/child_elements/namespace.py +21 -0
  66. py_canoe/core/child_elements/namespaces.py +19 -0
  67. py_canoe/core/child_elements/network.py +18 -0
  68. py_canoe/core/child_elements/network_adapters.py +13 -0
  69. py_canoe/core/child_elements/nodes.py +119 -0
  70. py_canoe/core/child_elements/open_configuration_result.py +0 -0
  71. py_canoe/core/child_elements/panel_setup.py +97 -0
  72. py_canoe/core/child_elements/participant.py +17 -0
  73. py_canoe/core/child_elements/participants.py +22 -0
  74. py_canoe/core/child_elements/ports.py +81 -0
  75. py_canoe/core/child_elements/replay_collection.py +56 -0
  76. py_canoe/core/child_elements/security_configuration.py +20 -0
  77. py_canoe/core/child_elements/security_setup.py +31 -0
  78. py_canoe/core/child_elements/signals.py +39 -0
  79. py_canoe/core/child_elements/simulation_setup.py +0 -0
  80. py_canoe/core/child_elements/single_file_data_source.py +13 -0
  81. py_canoe/core/child_elements/snippet_setup.py +68 -0
  82. py_canoe/core/child_elements/standalone_mode.py +0 -0
  83. py_canoe/core/child_elements/start_value_list.py +0 -0
  84. py_canoe/core/child_elements/symbol_mappings.py +0 -0
  85. py_canoe/core/child_elements/tcp_ip_stack_setting.py +0 -0
  86. py_canoe/core/child_elements/test_configurations.py +0 -0
  87. py_canoe/core/child_elements/test_environment.py +64 -0
  88. py_canoe/core/child_elements/test_environments.py +26 -0
  89. py_canoe/core/child_elements/test_module.py +213 -0
  90. py_canoe/core/child_elements/test_modules.py +23 -0
  91. py_canoe/core/child_elements/test_setup.py +16 -0
  92. py_canoe/core/child_elements/test_setup_folder_ext.py +36 -0
  93. py_canoe/core/child_elements/test_setup_folders.py +25 -0
  94. py_canoe/core/child_elements/user_files.py +0 -0
  95. py_canoe/core/child_elements/variable.py +144 -0
  96. py_canoe/core/child_elements/variable_events.py +14 -0
  97. py_canoe/core/child_elements/variables.py +29 -0
  98. py_canoe/core/child_elements/variables_file.py +15 -0
  99. py_canoe/core/child_elements/variables_files.py +19 -0
  100. py_canoe/core/child_elements/visual_sequence_setup.py +46 -0
  101. py_canoe/core/child_elements/vt_system.py +83 -0
  102. py_canoe/core/child_elements/vtt_sut_import_result.py +21 -0
  103. py_canoe/core/child_elements/write.py +71 -0
  104. py_canoe/core/child_elements/xcp_setup.py +12 -0
  105. py_canoe/core/configuration.py +509 -0
  106. py_canoe/core/environment.py +59 -0
  107. py_canoe/core/measurement.py +149 -0
  108. py_canoe/core/networks.py +103 -0
  109. py_canoe/core/performance.py +21 -0
  110. py_canoe/core/simulation.py +53 -0
  111. py_canoe/core/system.py +164 -0
  112. py_canoe/core/ui.py +53 -0
  113. py_canoe/core/version.py +54 -0
  114. py_canoe/helpers/__init__.py +0 -0
  115. py_canoe/helpers/common.py +78 -0
  116. {py_canoe-3.0.4.dist-info → py_canoe-26.0.2.dist-info}/METADATA +332 -322
  117. py_canoe-26.0.2.dist-info/RECORD +118 -0
  118. {py_canoe-3.0.4.dist-info → py_canoe-26.0.2.dist-info}/WHEEL +1 -1
  119. py_canoe/py_canoe.py +0 -2586
  120. py_canoe/py_canoe_utils/logging_collection.py +0 -345
  121. py_canoe/py_canoe_utils/py_canoe_logger.py +0 -29
  122. py_canoe-3.0.4.dist-info/LICENSE +0 -21
  123. py_canoe-3.0.4.dist-info/RECORD +0 -8
@@ -0,0 +1,149 @@
1
+ from typing import Union
2
+ import win32com.client
3
+
4
+ from py_canoe.core.capl import CaplFunction
5
+ from py_canoe.helpers.common import DoEventsUntil
6
+ from py_canoe.helpers.common import logger, wait
7
+
8
+
9
+ class MeasurementEvents:
10
+ def __init__(self):
11
+ self.APP_COM_OBJ = object
12
+ self.INIT: bool = False
13
+ self.START: bool = False
14
+ self.STOP: bool = False
15
+ self.EXIT: bool = False
16
+ self.CAPL_FUNCTION_OBJECTS = dict()
17
+ self.CAPL_FUNCTION_NAMES = tuple()
18
+
19
+ def OnInit(self):
20
+ """measurement is initialized"""
21
+ for fun in self.CAPL_FUNCTION_NAMES:
22
+ self.CAPL_FUNCTION_OBJECTS[fun] = CaplFunction(self.APP_COM_OBJ.CAPL.GetFunction(fun))
23
+ self.INIT = True
24
+
25
+ def OnStart(self):
26
+ """measurement is started"""
27
+ self.START = True
28
+
29
+ def OnStop(self):
30
+ """measurement is stopped"""
31
+ self.STOP = True
32
+
33
+ def OnExit(self):
34
+ """measurement is exited"""
35
+ self.CAPL_FUNCTION_OBJECTS.clear()
36
+ self.EXIT = True
37
+
38
+
39
+ class Measurement:
40
+ def __init__(self, app):
41
+ self.com_object = win32com.client.Dispatch(app.com_object.Measurement)
42
+ self.measurement_events: MeasurementEvents = win32com.client.WithEvents(self.com_object, MeasurementEvents)
43
+ self.measurement_events.APP_COM_OBJ = app.com_object
44
+
45
+ @property
46
+ def animation_delay(self) -> int:
47
+ return self.com_object.AnimationDelay
48
+
49
+ @animation_delay.setter
50
+ def animation_delay(self, delay: int):
51
+ self.com_object.AnimationDelay = delay
52
+ logger.info(f"📢 Animation Delay ⏲️ set to: {delay} ms")
53
+
54
+ @property
55
+ def measurement_index(self) -> int:
56
+ index = self.com_object.MeasurementIndex
57
+ logger.info(f"📢 Measurement Index value: {index}")
58
+ return index
59
+
60
+ @measurement_index.setter
61
+ def measurement_index(self, index: int):
62
+ self.com_object.MeasurementIndex = index
63
+ logger.info(f"📢 Measurement Index set to: {index}")
64
+
65
+ @property
66
+ def running(self) -> bool:
67
+ return self.com_object.Running
68
+
69
+ def start(self, timeout=30) -> bool:
70
+ try:
71
+ if self.running:
72
+ logger.warning("⚠️ Measurement is already running")
73
+ return True
74
+ self.measurement_events.START = False
75
+ self.com_object.Start()
76
+ status = DoEventsUntil(lambda: self.measurement_events.START, timeout, "CANoe Measurement Start")
77
+ if status:
78
+ logger.info('📢 Measurement Started 🏃‍➡️')
79
+ return status
80
+ except Exception as e:
81
+ logger.error(f"❌ Error starting CANoe measurement: {e}")
82
+ return False
83
+
84
+ def stop(self, timeout=30) -> bool:
85
+ return self.stop_ex(timeout)
86
+
87
+ def stop_ex(self, timeout=30) -> bool:
88
+ try:
89
+ if not self.running:
90
+ logger.warning("⚠️ Measurement is already stopped")
91
+ return True
92
+ self.measurement_events.STOP = False
93
+ self.com_object.Stop()
94
+ status = DoEventsUntil(lambda: self.measurement_events.STOP, timeout, "CANoe Measurement Stop")
95
+ if status:
96
+ logger.info('📢 Measurement Stopped 🧍')
97
+ wait(1) # Allow 1 second buffer
98
+ return status
99
+ except Exception as e:
100
+ logger.error(f"❌ Error stopping CANoe measurement: {e}")
101
+ return False
102
+
103
+ def start_measurement_in_animation_mode(self, animation_delay=100, timeout=30) -> bool:
104
+ try:
105
+ if self.running:
106
+ logger.warning("⚠️ Measurement is already running, cannot animate")
107
+ return False
108
+ self.measurement_events.START = False
109
+ self.animation_delay = animation_delay
110
+ self.com_object.Animate()
111
+ status = DoEventsUntil(lambda: self.measurement_events.START, timeout, "CANoe Measurement Animation Initialization")
112
+ if status:
113
+ logger.info(f'📢 Measurement started 🏃‍➡️ in Animation mode with animation delay ⏲️ {animation_delay} ms')
114
+ else:
115
+ logger.error(f"❌ Measurement did not start in Animation mode within {timeout} seconds")
116
+ return status
117
+ except Exception as e:
118
+ logger.error(f"❌ Error starting CANoe measurement in animation mode: {e}")
119
+ return False
120
+
121
+ def break_measurement_in_offline_mode(self) -> bool:
122
+ try:
123
+ if not self.running:
124
+ logger.warning("⚠️ Measurement is not running, cannot break")
125
+ return False
126
+ self.com_object.Break()
127
+ logger.info('📢 Measurement break applied 🫷 in Offline mode')
128
+ return True
129
+ except Exception as e:
130
+ logger.error(f"❌ Error breaking CANoe measurement in offline mode: {e}")
131
+ return False
132
+
133
+ def reset_measurement_in_offline_mode(self) -> bool:
134
+ try:
135
+ self.com_object.Reset()
136
+ logger.info('📢 Measurement reset applied 🔁 in Offline mode')
137
+ return True
138
+ except Exception as e:
139
+ logger.error(f"❌ Error resetting CANoe measurement in offline mode: {e}")
140
+ return False
141
+
142
+ def process_measurement_event_in_single_step(self) -> bool:
143
+ try:
144
+ self.com_object.Step()
145
+ logger.info('📢 Processed a measurement event in single step 👣')
146
+ return True
147
+ except Exception as e:
148
+ logger.error(f"❌ Error processing CANoe measurement event in single step: {e}")
149
+ return False
@@ -0,0 +1,103 @@
1
+ from typing import Union
2
+
3
+ from py_canoe.helpers.common import logger
4
+ from py_canoe.helpers.common import wait
5
+ from py_canoe.core.child_elements.diagnostic import Diagnostic
6
+ from py_canoe.core.child_elements.network import Network
7
+
8
+
9
+ class Networks:
10
+ """
11
+ The Networks object represents the networks of CANoe.
12
+ """
13
+ def __init__(self, app):
14
+ self.com_object = app.com_object.Networks
15
+ self.diagnostic_devices: dict[str, Diagnostic] = dict()
16
+
17
+ @property
18
+ def count(self) -> int:
19
+ return self.com_object.Count
20
+
21
+ def item(self, index: int) -> Network:
22
+ return Network(self.com_object.Item(index))
23
+
24
+ def fetch_diagnostic_devices(self):
25
+ try:
26
+ for i in range(1, self.count + 1):
27
+ network = self.item(i)
28
+ for j in range(1, network.devices.count + 1):
29
+ device = network.devices.item(j)
30
+ try:
31
+ diagnostic = getattr(device.com_object, 'Diagnostic', None)
32
+ if diagnostic:
33
+ self.diagnostic_devices[device.name] = Diagnostic(diagnostic)
34
+ except Exception:
35
+ pass
36
+ except Exception as e:
37
+ logger.error(f"❌ Error fetching Diagnostic Devices: {e}")
38
+ return None
39
+
40
+ def send_diag_request(self, diag_ecu_qualifier_name: str, request: str, request_in_bytes=True, return_sender_name=False, response_in_bytearray=False) -> Union[str, dict]:
41
+ try:
42
+ diag_device: Diagnostic = self.diagnostic_devices.get(diag_ecu_qualifier_name)
43
+ if diag_device:
44
+ if request_in_bytes:
45
+ diag_req_in_bytes = bytearray()
46
+ byte_stream = ''.join(request.split(' '))
47
+ for i in range(0, len(byte_stream), 2):
48
+ diag_req_in_bytes.append(int(byte_stream[i:i + 2], 16))
49
+ diag_request = diag_device.create_request_from_stream(diag_req_in_bytes)
50
+ else:
51
+ diag_request = diag_device.create_request(request)
52
+ diag_request.send()
53
+ logger.info(f'💉 {diag_ecu_qualifier_name}: Diagnostic Request = {request}')
54
+ while diag_request.pending:
55
+ wait(0.01)
56
+ diag_responses_dict = {}
57
+ diag_response_including_sender_name = {}
58
+ for i in range(1, diag_request.responses.count + 1):
59
+ diag_response = diag_request.responses.item(i)
60
+ diag_response_positive = diag_response.positive
61
+ response_code = diag_response.response_code
62
+ response_sender = diag_response.sender
63
+ response_stream = diag_response.stream
64
+ response_stream_in_str = " ".join(f"{d:02X}" for d in response_stream).upper()
65
+ diag_responses_dict[response_sender] = {
66
+ "positive": diag_response_positive,
67
+ "response_code": response_code,
68
+ "stream": response_stream,
69
+ "stream_in_str": response_stream_in_str
70
+ }
71
+ if response_in_bytearray:
72
+ diag_response_including_sender_name[response_sender] = response_stream
73
+ else:
74
+ diag_response_including_sender_name[response_sender] = response_stream_in_str
75
+ if diag_response_positive:
76
+ logger.info(f'🟢 {response_sender}: Diagnostic Response = {response_stream_in_str}')
77
+ else:
78
+ logger.info(f'🔴 {response_sender}: Diagnostic Response = {response_stream_in_str}')
79
+ return diag_response_including_sender_name if return_sender_name else diag_response_including_sender_name[diag_ecu_qualifier_name]
80
+ else:
81
+ logger.warning(f'⚠️ No responses received for request: {request}')
82
+ return {"error": "No responses received"}
83
+ except Exception as e:
84
+ logger.error(f"❌ Error sending diagnostic request: {e}")
85
+ return {"error": str(e)}
86
+
87
+ def control_tester_present(self, diag_ecu_qualifier_name: str, value: bool) -> bool:
88
+ try:
89
+ diag_device: Diagnostic = self.diagnostic_devices.get(diag_ecu_qualifier_name)
90
+ if diag_device:
91
+ if value:
92
+ diag_device.diag_start_tester_present()
93
+ logger.info(f'✔️ {diag_ecu_qualifier_name}: Tester Present started 🏃‍➡️')
94
+ else:
95
+ diag_device.diag_stop_tester_present()
96
+ logger.info(f'⏹️ {diag_ecu_qualifier_name}: Tester Present stopped 🧍')
97
+ return True
98
+ else:
99
+ logger.warning(f'⚠️ No diagnostic device found for: {diag_ecu_qualifier_name}')
100
+ return False
101
+ except Exception as e:
102
+ logger.error(f"❌ Error controlling tester present: {e}")
103
+ return False
@@ -0,0 +1,21 @@
1
+ from py_canoe.helpers.common import logger
2
+
3
+
4
+ class Performance:
5
+ """
6
+ The Performance object allows setting or returning parameters that influence the performance on multicore processors.
7
+ """
8
+ def __init__(self, app):
9
+ self.app = app
10
+ self.com_object = self.app.com_object.Performance
11
+
12
+ @property
13
+ def max_num_measurement_setup_threads(self) -> int:
14
+ return self.com_object.MaxNumMeasurementSetupThreads
15
+
16
+ @max_num_measurement_setup_threads.setter
17
+ def max_num_measurement_setup_threads(self, num: int):
18
+ if not self.app.get_measurement_running_status():
19
+ self.com_object.MaxNumMeasurementSetupThreads = num
20
+ else:
21
+ logger.warning("⚠️ Cannot set MaxNumMeasurementSetupThreads while measurement is running.")
@@ -0,0 +1,53 @@
1
+ import win32com.client
2
+ from typing import Union
3
+
4
+
5
+ class SimulationEvents:
6
+ EVENTS_INFORMATION = {}
7
+
8
+ @staticmethod
9
+ def OnIdle(timeHigh, time):
10
+ SimulationEvents.EVENTS_INFORMATION['timeHigh'] = timeHigh
11
+ SimulationEvents.EVENTS_INFORMATION['time'] = time
12
+
13
+
14
+ class Simulation:
15
+ """
16
+ The Simulation object represents CANoe's measurement functions in the Simulation mode.
17
+ With the help of the Simulation object you can control the system time from an external source during the measurement.
18
+ CANoe automatically goes into Slave mode at the measurement start if you access the Simulation object.
19
+ """
20
+ def __init__(self, app, enable_events: bool = False):
21
+ self.com_object = app.com_object.Simulation
22
+ if enable_events:
23
+ win32com.client.WithEvents(self.com_object, SimulationEvents)
24
+
25
+ @property
26
+ def animation(self) -> Union[int, float]:
27
+ return self.com_object.Animation
28
+
29
+ @animation.setter
30
+ def animation(self, value: Union[int, float]):
31
+ self.com_object.Animation = value
32
+
33
+ @property
34
+ def current_time(self) ->int:
35
+ return self.com_object.CurrentTime
36
+
37
+ @property
38
+ def current_time_high(self) -> int:
39
+ return self.com_object.CurrentTimeHigh
40
+
41
+ @property
42
+ def notification_type(self) -> int:
43
+ return self.com_object.NotificationType
44
+
45
+ @notification_type.setter
46
+ def notification_type(self, value: int):
47
+ self.com_object.NotificationType = value
48
+
49
+ def increment_time(self, ticks: int):
50
+ self.com_object.IncrementTime(ticks)
51
+
52
+ def increment_time_and_wait(self, ticks: int):
53
+ self.com_object.IncrementTimeAndWait(ticks)
@@ -0,0 +1,164 @@
1
+ from typing import Union
2
+
3
+ from py_canoe.helpers.common import logger
4
+ from py_canoe.core.child_elements.namespaces import Namespaces
5
+ from py_canoe.core.child_elements.variables_files import VariablesFiles
6
+ from py_canoe.core.child_elements.variable import Variable
7
+
8
+
9
+ class System:
10
+ """
11
+ The System object represents the system of the CANoe application.
12
+ The System object offers access to the namespaces for data exchange with external applications.
13
+ """
14
+ def __init__(self, app):
15
+ self.com_object = app.com_object.System
16
+
17
+ @property
18
+ def namespaces(self) -> Namespaces:
19
+ return Namespaces(self.com_object.Namespaces)
20
+
21
+ @property
22
+ def variables_files(self) -> VariablesFiles:
23
+ return VariablesFiles(self.com_object.VariablesFiles)
24
+
25
+
26
+ def add_variable(self, sys_var_name: str, value: Union[int, float, str], read_only: bool = False) -> Union[object, None]:
27
+ new_var_com_obj = None
28
+ try:
29
+ parts = sys_var_name.split('::')
30
+ if len(parts) < 2:
31
+ logger.error(f"❌ Invalid system variable name '{sys_var_name}'. Must be in 'namespace::variable' format.")
32
+ return None
33
+ namespace = '::'.join(parts[:-1])
34
+ variable_name = parts[-1]
35
+ try:
36
+ namespace_obj = self.com_object.Namespaces(namespace)
37
+ except Exception:
38
+ logger.info(f"namespace '{namespace}' not present. Creating namespace...")
39
+ namespaces_obj = self.com_object.Namespaces
40
+ namespace_obj = namespaces_obj.Add(namespace)
41
+ logger.info(f"Created new namespace: {namespace}")
42
+ variables_obj = namespace_obj.Variables
43
+ if read_only:
44
+ new_var_com_obj = variables_obj.Add(variable_name, value)
45
+ else:
46
+ new_var_com_obj = variables_obj.AddWriteable(variable_name, value)
47
+ logger.info(f"System Variable '{sys_var_name}' defined successfully with value: {value}")
48
+ return new_var_com_obj
49
+ except Exception as e:
50
+ logger.error(f"❌ Error defining System Variable '{sys_var_name}': {e}")
51
+ return None
52
+
53
+ def remove_variable(self, sys_var_name: str) -> bool:
54
+ try:
55
+ parts = sys_var_name.split('::')
56
+ if len(parts) < 2:
57
+ logger.error(f"❌ Invalid system variable name '{sys_var_name}'. Must be in 'namespace::variable' format.")
58
+ return None
59
+ namespace = '::'.join(parts[:-1])
60
+ variable_name = parts[-1]
61
+ namespace_obj = self.com_object.Namespaces(namespace)
62
+ variables_obj = namespace_obj.Variables
63
+ for i in range(1, variables_obj.Count + 1):
64
+ variable_obj = variables_obj.Item(i)
65
+ if variable_obj.Name == variable_name:
66
+ variables_obj.Remove(i)
67
+ logger.info(f"System Variable '{sys_var_name}' removed successfully.")
68
+ return True
69
+ logger.info(f"System Variable '{sys_var_name}' not found.")
70
+ return False
71
+ except Exception as e:
72
+ logger.error(f"❌ Error removing System Variable '{sys_var_name}': {e}")
73
+ return False
74
+
75
+ def get_variable_value(self, sys_var_name: str, return_symbolic_name=False) -> Union[int, float, str, None]:
76
+ try:
77
+ parts = sys_var_name.split('::')
78
+ if len(parts) < 2:
79
+ logger.error(f"❌ Invalid system variable name '{sys_var_name}'. Must be in 'namespace::variable' format.")
80
+ return None
81
+ namespace = '::'.join(parts[:-1])
82
+ variable_name = parts[-1]
83
+ namespace_obj = self.com_object.Namespaces(namespace)
84
+ variable_obj = Variable(namespace_obj.Variables(variable_name))
85
+ value = variable_obj.get_value()
86
+ if return_symbolic_name:
87
+ symbolic_value = variable_obj.get_symbolic_value_name(value)
88
+ logger.info(f"System Variable '{sys_var_name}' symbolic value: {symbolic_value}")
89
+ return symbolic_value
90
+ logger.info(f"System Variable '{sys_var_name}' value: {value}")
91
+ return value
92
+ except Exception as e:
93
+ logger.error(f"❌ Error retrieving System Variable '{sys_var_name}': {e}")
94
+ return None
95
+
96
+ def set_variable_value(self, sys_var_name: str, value: Union[int, float, str], timeout: Union[int, float] = 1) -> bool:
97
+ try:
98
+ parts = sys_var_name.split('::')
99
+ if len(parts) < 2:
100
+ logger.error(f"❌ Invalid system variable name '{sys_var_name}'. Must be in 'namespace::variable' format.")
101
+ return False
102
+ namespace = '::'.join(parts[:-1])
103
+ variable_name = parts[-1]
104
+ namespace_obj = self.com_object.Namespaces(namespace)
105
+ variable_obj = Variable(namespace_obj.Variables(variable_name))
106
+ var_type = type(variable_obj.get_value())
107
+ try:
108
+ converted_value = var_type(value)
109
+ except Exception:
110
+ logger.error(f"❌ Could not convert value '{value}' to type {var_type.__name__} for '{sys_var_name}'")
111
+ return False
112
+ status = variable_obj.set_value(converted_value, timeout)
113
+ return status
114
+ except Exception as e:
115
+ logger.error(f"❌ Error setting System Variable '{sys_var_name}': {e}")
116
+ return False
117
+
118
+ def set_variable_array_values(self, sys_var_name: str, value: tuple, index: int = 0, timeout: Union[int, float] = 1) -> bool:
119
+ try:
120
+ parts = sys_var_name.split('::')
121
+ if len(parts) < 2:
122
+ logger.error(f"❌ Invalid system variable name '{sys_var_name}'. Must be in 'namespace::variable' format.")
123
+ return False
124
+ namespace = '::'.join(parts[:-1])
125
+ variable_name = parts[-1]
126
+ namespace_obj = self.com_object.Namespaces(namespace)
127
+ variable_obj = Variable(namespace_obj.Variables(variable_name))
128
+ arr = list(variable_obj.get_value())
129
+ if index < 0 or index + len(value) > len(arr):
130
+ logger.error(f"❌ Not enough space in System Variable Array '{sys_var_name}' to set values.")
131
+ return False
132
+ value_type = type(arr[0]) if arr else type(value[0])
133
+ arr[index:index + len(value)] = [value_type(v) for v in value]
134
+ status = variable_obj.set_value(tuple(arr), timeout)
135
+ return status
136
+ except Exception as e:
137
+ logger.error(f"❌ Error setting System Variable Array '{sys_var_name}': {e}")
138
+ return False
139
+
140
+ def get_namespaces(self) -> Union[dict['str': 'Namespace'], None]:
141
+ try:
142
+ namespaces_dict = {}
143
+ namespaces = self.namespaces
144
+ for index in range(1, namespaces.count + 1):
145
+ namespace = namespaces.item(index)
146
+ namespaces_dict[namespace.name] = namespace
147
+ logger.info(f"📢 total {namespaces.count} system root namespaces found.")
148
+ return namespaces_dict
149
+ except Exception as e:
150
+ logger.error(f"❌ Error getting system namespaces: {e}")
151
+ return None
152
+
153
+ def get_variables_files(self) -> Union[dict['str': 'VariablesFile'], None]:
154
+ try:
155
+ variables_files_dict = {}
156
+ variables_files = self.variables_files
157
+ for index in range(1, variables_files.count + 1):
158
+ variables_file = variables_files.item(index)
159
+ variables_files_dict[variables_file.full_name] = variables_file
160
+ logger.info(f"📢 total {variables_files.count} system variables files found.")
161
+ return variables_files_dict
162
+ except Exception as e:
163
+ logger.error(f"❌ Error getting system variables files: {e}")
164
+ return None
py_canoe/core/ui.py ADDED
@@ -0,0 +1,53 @@
1
+ from py_canoe.helpers.common import logger
2
+
3
+ from py_canoe.core.child_elements.write import Write
4
+
5
+
6
+ class Ui:
7
+ """
8
+ The UI object represents the user interface in CANoe.
9
+ """
10
+ def __init__(self, app):
11
+ self.app = app
12
+ self.com_object = self.app.com_object.UI
13
+
14
+ def get_command_enabled(self, command: str) -> bool:
15
+ return self.com_object.GetCommandEnabled(command)
16
+
17
+ def set_command_enabled(self, command: str, enabled: bool) -> None:
18
+ self.com_object.SetCommandEnabled(command, enabled)
19
+
20
+ @property
21
+ def write(self) -> Write:
22
+ return Write(self.com_object.Write)
23
+
24
+ def activate_desktop(self, desktop_name: str) -> bool:
25
+ try:
26
+ self.com_object.ActivateDesktop(desktop_name)
27
+ logger.info(f"📢 UI Desktop '{desktop_name}' activated successfully")
28
+ return True
29
+ except Exception as e:
30
+ logger.error(f"❌ Error activating UI Desktop '{desktop_name}': {e}")
31
+ return False
32
+
33
+ def create_desktop(self, desktop_name: str) -> bool:
34
+ try:
35
+ if float(f"{self.app.version.major}.{self.app.version.minor}") >= 15.3:
36
+ self.com_object.CreateDesktop(desktop_name)
37
+ logger.info(f"📢 UI Desktop '{desktop_name}' created successfully")
38
+ return True
39
+ else:
40
+ logger.warning(f"❌ Cannot create desktop '{desktop_name}': Requires CANoe version 15.3 or higher.")
41
+ return False
42
+ except Exception as e:
43
+ logger.error(f"❌ Error creating UI Desktop '{desktop_name}': {e}")
44
+ return False
45
+
46
+ def open_baudrate_dialog(self) -> bool:
47
+ try:
48
+ self.com_object.OpenBaudrateDialog()
49
+ logger.info("📢 UI Baudrate Dialog opened successfully")
50
+ return True
51
+ except Exception as e:
52
+ logger.error(f"❌ Error opening UI Baudrate Dialog: {e}")
53
+ return False
@@ -0,0 +1,54 @@
1
+ from py_canoe.helpers.common import logger
2
+
3
+
4
+ class Version:
5
+ """
6
+ The Version object represents the version of the CANoe application.
7
+ """
8
+ def __init__(self, app):
9
+ self.com_object = app.com_object.Version
10
+
11
+ def __str__(self):
12
+ return f"{self.full_name}"
13
+
14
+ @property
15
+ def build(self):
16
+ return self.com_object.Build
17
+
18
+ @property
19
+ def full_name(self):
20
+ return self.com_object.FullName
21
+
22
+ @property
23
+ def major(self):
24
+ return self.com_object.major
25
+
26
+ @property
27
+ def minor(self):
28
+ return self.com_object.minor
29
+
30
+ @property
31
+ def name(self):
32
+ return self.com_object.Name
33
+
34
+ @property
35
+ def patch(self):
36
+ return self.com_object.Patch
37
+
38
+ def get_canoe_version_info(self) -> dict[str, str | int]:
39
+ try:
40
+ version_info = {
41
+ 'full_name': self.full_name,
42
+ 'name': self.name,
43
+ 'major': self.major,
44
+ 'minor': self.minor,
45
+ 'build': self.build,
46
+ 'patch': self.patch
47
+ }
48
+ logger.info('📜 CANoe Version Information:')
49
+ for key, value in version_info.items():
50
+ logger.info(f" {key}: {value}")
51
+ return version_info
52
+ except Exception as e:
53
+ logger.error(f"❌ Error retrieving CANoe version information: {e}")
54
+ return {}
File without changes
@@ -0,0 +1,78 @@
1
+ import os
2
+ import sys
3
+ import time
4
+ import logging
5
+ import pythoncom
6
+ from datetime import datetime
7
+ from pathlib import Path
8
+
9
+ def check_if_path_exists(path: str, create_if_not_exist: bool=False) -> bool:
10
+ """Check if a given path exists. Optionally create it if it doesn't."""
11
+ if os.path.exists(path):
12
+ return True
13
+ else:
14
+ if create_if_not_exist:
15
+ try:
16
+ os.makedirs(path, exist_ok=True)
17
+ return True
18
+ except Exception as e:
19
+ logger.error(f"❌ Error creating directory {path}: {e}")
20
+ return False
21
+ else:
22
+ return False
23
+
24
+ def setup_logger(name='py_canoe', filename='py_canoe.log'):
25
+ """Set up and return a logger with console and file handlers."""
26
+ # os.makedirs(os.path.dirname(filename), exist_ok=True)
27
+ logger = logging.getLogger(name)
28
+ logger.setLevel(logging.INFO)
29
+ fmt = "%(asctime)s [PY_CANOE] [%(levelname)-4.8s] %(message)s"
30
+ # Add console handler if not already present
31
+ if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
32
+ console_handler = logging.StreamHandler(sys.stdout)
33
+ console_handler.setLevel(logging.INFO)
34
+ console_handler.setFormatter(logging.Formatter(fmt))
35
+ logger.addHandler(console_handler)
36
+ logger.propagate = False
37
+ return logger
38
+
39
+ def update_logger_file_path(logger: logging.Logger, log_dir_path: str | Path) -> None:
40
+ """Update the file handler of an existing logger to a new file path."""
41
+ new_filename = os.path.join(log_dir_path, 'py_canoe.log')
42
+ if check_if_path_exists(os.path.dirname(new_filename), create_if_not_exist=True):
43
+ # Remove existing FileHandlers
44
+ for handler in logger.handlers[:]:
45
+ if isinstance(handler, logging.FileHandler):
46
+ logger.removeHandler(handler)
47
+ handler.close()
48
+ # Add new FileHandler
49
+ fmt = "%(asctime)s [PY_CANOE] [%(levelname)-4.8s] %(message)s"
50
+ file_handler = logging.FileHandler(new_filename, mode='w', encoding='utf-8')
51
+ file_handler.setLevel(logging.INFO)
52
+ file_handler.setFormatter(logging.Formatter(fmt))
53
+ logger.addHandler(file_handler)
54
+ else:
55
+ logger.error(f"❌ Cannot update logger file path. Directory does not exist and could not be created: {os.path.dirname(new_filename)}")
56
+
57
+
58
+ logger = setup_logger()
59
+
60
+ def wait(timeout_seconds=0.1):
61
+ """Pump waiting COM messages and sleep for the given timeout."""
62
+ pythoncom.PumpWaitingMessages()
63
+ time.sleep(timeout_seconds)
64
+
65
+ def DoEvents() -> None:
66
+ wait(0.01)
67
+
68
+ def DoEventsUntil(cond, timeout, title) -> bool:
69
+ base_time = datetime.now()
70
+ while not cond():
71
+ DoEvents()
72
+ now = datetime.now()
73
+ difference = now - base_time
74
+ seconds = difference.seconds
75
+ if seconds > timeout:
76
+ logger.warning(f'⚠️ {title} timeout({timeout} s)')
77
+ return False
78
+ return True