py_canoe 3.0.2__tar.gz → 3.0.3__tar.gz

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.
@@ -1,8 +1,7 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: py_canoe
3
- Version: 3.0.2
3
+ Version: 3.0.3
4
4
  Summary: Python CANoe Package
5
- Home-page: https://github.com/chaitu-ycr/py_canoe
6
5
  License: LICENSE
7
6
  Keywords: Vector,CANoe,py_canoe
8
7
  Author: chaitu-ycr
@@ -20,6 +19,7 @@ Classifier: Programming Language :: Python :: 3.13
20
19
  Classifier: Topic :: Software Development :: Embedded Systems
21
20
  Requires-Dist: pywin32 (>=306,<=308)
22
21
  Project-URL: Documentation, https://chaitu-ycr.github.io/py_canoe/
22
+ Project-URL: Homepage, https://github.com/chaitu-ycr/py_canoe
23
23
  Project-URL: Repository, https://github.com/chaitu-ycr/py_canoe
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -78,8 +78,7 @@ pip install py_canoe --upgrade
78
78
  ### import CANoe module and create CANoe object instance
79
79
 
80
80
  ```python
81
- from py_canoe import CANoe
82
- from time import sleep as wait
81
+ from py_canoe import CANoe, wait
83
82
 
84
83
  canoe_inst = CANoe()
85
84
  ```
@@ -289,3 +288,49 @@ wait(1)
289
288
  canoe_inst.stop_measurement()
290
289
  ```
291
290
 
291
+ ### add/remove database
292
+
293
+ ```python
294
+ self.canoe_inst.open(canoe_cfg=self.canoe_cfg_gen_db_setup, visible=True, auto_save=True, prompt_user=False, auto_stop=True)
295
+ self.canoe_inst.start_measurement()
296
+ wait(1)
297
+ # add database
298
+ self.canoe_inst.add_database(fr"{self.file_path}\demo_cfg\DBs\sample_databases\XCP.dbc", 'CAN1', 1)
299
+ # remove database
300
+ self.canoe_inst.remove_database(fr"{self.file_path}\demo_cfg\DBs\sample_databases\XCP.dbc", 1)
301
+ ```
302
+
303
+ ### start/stop online measurement setup logging block
304
+
305
+ ```python
306
+ self.canoe_inst.open(canoe_cfg=self.canoe_cfg_online_setup)
307
+ self.canoe_inst.start_measurement()
308
+ wait(1)
309
+ # stop logging block
310
+ self.canoe_inst.start_stop_online_measurement_setup_logging_block(fr'{self.demo_cfg_dir}\Logs\demo_online_setup_log.blf', start=False)
311
+ wait(2)
312
+ # start logging block
313
+ self.canoe_inst.start_stop_online_measurement_setup_logging_block(fr'{self.demo_cfg_dir}\Logs\demo_online_setup_log.blf', start=True)
314
+ ```
315
+
316
+ ### working with logging blocks
317
+
318
+ ```python
319
+ # remove current logging blocks
320
+ for i in range(canoe_inst.logging_collection.count):
321
+ canoe_inst.remove_logging_block(1) # iteration start from 1 and shifts after each delete
322
+
323
+ # add a new block
324
+ # define dest path with file format as asc, blf or other
325
+ # may include field functions like {IncMeasurement}
326
+ full_path = "C:/sample_log_{IncMeasurement}.blf"
327
+ canoe_inst.add_logging_block(full_path)
328
+ canoe_inst.start_measurement()
329
+ # ...
330
+ canoe_inst.stop_measurement()
331
+ # log should be fully generated at this point for you to analyze
332
+ canoe_inst.set_configuration_modified(False) # to avoid popup asking to save changes
333
+ canoe_inst.quit()
334
+ ```
335
+
336
+
@@ -53,8 +53,7 @@ pip install py_canoe --upgrade
53
53
  ### import CANoe module and create CANoe object instance
54
54
 
55
55
  ```python
56
- from py_canoe import CANoe
57
- from time import sleep as wait
56
+ from py_canoe import CANoe, wait
58
57
 
59
58
  canoe_inst = CANoe()
60
59
  ```
@@ -263,3 +262,49 @@ var_value = canoe_inst.get_environment_variable_value('data_var')
263
262
  wait(1)
264
263
  canoe_inst.stop_measurement()
265
264
  ```
265
+
266
+ ### add/remove database
267
+
268
+ ```python
269
+ self.canoe_inst.open(canoe_cfg=self.canoe_cfg_gen_db_setup, visible=True, auto_save=True, prompt_user=False, auto_stop=True)
270
+ self.canoe_inst.start_measurement()
271
+ wait(1)
272
+ # add database
273
+ self.canoe_inst.add_database(fr"{self.file_path}\demo_cfg\DBs\sample_databases\XCP.dbc", 'CAN1', 1)
274
+ # remove database
275
+ self.canoe_inst.remove_database(fr"{self.file_path}\demo_cfg\DBs\sample_databases\XCP.dbc", 1)
276
+ ```
277
+
278
+ ### start/stop online measurement setup logging block
279
+
280
+ ```python
281
+ self.canoe_inst.open(canoe_cfg=self.canoe_cfg_online_setup)
282
+ self.canoe_inst.start_measurement()
283
+ wait(1)
284
+ # stop logging block
285
+ self.canoe_inst.start_stop_online_measurement_setup_logging_block(fr'{self.demo_cfg_dir}\Logs\demo_online_setup_log.blf', start=False)
286
+ wait(2)
287
+ # start logging block
288
+ self.canoe_inst.start_stop_online_measurement_setup_logging_block(fr'{self.demo_cfg_dir}\Logs\demo_online_setup_log.blf', start=True)
289
+ ```
290
+
291
+ ### working with logging blocks
292
+
293
+ ```python
294
+ # remove current logging blocks
295
+ for i in range(canoe_inst.logging_collection.count):
296
+ canoe_inst.remove_logging_block(1) # iteration start from 1 and shifts after each delete
297
+
298
+ # add a new block
299
+ # define dest path with file format as asc, blf or other
300
+ # may include field functions like {IncMeasurement}
301
+ full_path = "C:/sample_log_{IncMeasurement}.blf"
302
+ canoe_inst.add_logging_block(full_path)
303
+ canoe_inst.start_measurement()
304
+ # ...
305
+ canoe_inst.stop_measurement()
306
+ # log should be fully generated at this point for you to analyze
307
+ canoe_inst.set_configuration_modified(False) # to avoid popup asking to save changes
308
+ canoe_inst.quit()
309
+ ```
310
+
@@ -0,0 +1 @@
1
+ from .py_canoe import CANoe, wait
@@ -0,0 +1,345 @@
1
+ """CANoe COM objects related to logging setup"""
2
+
3
+ import logging
4
+
5
+ import win32com
6
+
7
+ logger = logging.getLogger('CANOE_LOG')
8
+
9
+
10
+ class LoggingCollection:
11
+ """Collection of all Logging Blocks of the current configuration."""
12
+
13
+ def __init__(self, logging_collection_com):
14
+ self._com = win32com.client.Dispatch(logging_collection_com)
15
+
16
+ @property
17
+ def count(self) -> int:
18
+ """This property returns the number of objects inside the collection."""
19
+ return int(self._com.Count)
20
+
21
+ def item(self, index: int) -> "Logging":
22
+ """This property returns an object from the collection."""
23
+ return Logging(self._com.Item(index))
24
+
25
+ def add(self, full_name: str) -> "Logging":
26
+ """This method adds a logging block to the Measurement Setup.
27
+
28
+ :param full_name: full path to log file as "C:/file.(asc|blf|mf4)", may have
29
+ field functions like {IncMeasurement} in the file name
30
+ """
31
+ return Logging(self._com.Add(full_name))
32
+
33
+ def remove(self, index: int):
34
+ """This method removes a logging block (Logging) from a logging collection."""
35
+ self._com.Remove(index)
36
+
37
+
38
+ class Logging:
39
+
40
+ """The Logging object represents a Logging Block in the current configuration.
41
+
42
+ The filename extension of the logging file which is specified with this property
43
+ determines the logging format. The full name can now also contain field functions;
44
+ e.g. Logging.FullName = "LOGFILE_M{IncMeasurement}.ASC
45
+
46
+ """
47
+
48
+ def __init__(self, logging_com):
49
+ self._com = win32com.client.Dispatch(logging_com)
50
+
51
+ @property
52
+ def exporter(self) -> "Exporter":
53
+ """This property returns an Exporter object."""
54
+ return Exporter(self._com.Exporter)
55
+
56
+ def file_name_options(self):
57
+ """This property returns a LoggingFileNameOptions object."""
58
+ raise NotImplementedError("FileNameOptions access is not implemented yet.")
59
+
60
+ def filter(self):
61
+ """This property returns a LoggingFilter object."""
62
+ raise NotImplementedError("FileNameOptions access is not implemented yet.")
63
+
64
+ @property
65
+ def full_name(self) -> str:
66
+ """This property sets or determines the complete path to the logging file."""
67
+ return self._com.FullName
68
+
69
+ @full_name.setter
70
+ def full_name(self, fullname: str):
71
+ self._com.FullName = fullname
72
+
73
+ @property
74
+ def trigger(self) -> "Trigger":
75
+ """This property returns a Trigger object."""
76
+ return Trigger(self._com.Trigger)
77
+
78
+
79
+ class Trigger:
80
+
81
+ """Trigger block located before the Logging Block in the Measurement Setup."""
82
+
83
+ def __init__(self, trigger_com):
84
+ self._com = win32com.client.Dispatch(trigger_com)
85
+
86
+ @property
87
+ def active(self) -> bool:
88
+ """This property sets or returns the status of the trigger."""
89
+ return self._com.Active
90
+
91
+ @active.setter
92
+ def active(self, _active: bool):
93
+ self._com.Active = _active
94
+
95
+ def start(self):
96
+ """This method starts the trigger."""
97
+ self._com.Start()
98
+
99
+ def stop(self):
100
+ """This method stops the trigger."""
101
+ self._com.Stop()
102
+
103
+
104
+ class Exporter:
105
+
106
+ """Export dialog.
107
+
108
+ The Exporter object represents an export dialog, as it can
109
+ be used e.g. in a Logging Block in the Measurement Setup.
110
+
111
+ """
112
+
113
+ def __init__(self, exporter_com):
114
+ self._com = win32com.client.Dispatch(exporter_com)
115
+
116
+ def destinations(self):
117
+ """Returns a Files object for the destination files of an exporter.
118
+
119
+ On the first access to the destination files of an exporter the Files object
120
+ contains the destination file as it is defined in the according Export dialog.
121
+
122
+ If no destination file is set a destination file is derived from the source
123
+ file. Name and path of this destination file correspond to the name and path
124
+ of the source file. The format is CSV. If as many destination files as source
125
+ files are given, an according destination file will be generated for each
126
+ source file (n to n).
127
+
128
+ If the numbers of destination files and source files don't match, the number of
129
+ destination files must be 1. All source files will be merged into the
130
+ destination file (n to 1).
131
+ """
132
+ raise NotImplementedError("Destinations access is not implemented yet.")
133
+
134
+ @property
135
+ def filter(self) -> "Filter":
136
+ """Returns a Filter object"""
137
+ return Filter(self._com.Filter)
138
+
139
+ @property
140
+ def messages(self) -> list["Message"]:
141
+ """Returns all messages that have been detected during loading.
142
+
143
+ Consist of messages that come from the source files of an exporter
144
+ and can be exported/converted.
145
+
146
+ """
147
+ messages_collection = Messages(self._com.Symbols)
148
+ messages = []
149
+ for i in range(1, messages_collection.count + 1):
150
+ messages.append(messages_collection.item(i))
151
+ return messages
152
+
153
+ def settings(self):
154
+ """Returns an ExporterSettings object."""
155
+ raise NotImplementedError("FileNameOptions access is not implemented yet.")
156
+
157
+ def sources(self):
158
+ """Returns Files object for each source file of the exporter or offline source.
159
+
160
+ On the first access to the source files of the exporter the Files object
161
+ contains the source file as defined in the Export dialog.
162
+
163
+ Typically this is the logging file as set in the Logging Block, as long as no
164
+ other source file has been defined manually.
165
+
166
+ """
167
+ raise NotImplementedError("FileNameOptions access is not implemented yet.")
168
+
169
+ @property
170
+ def symbols(self) -> list["ExporterSymbol"]:
171
+ """Returns all symbols that have been detected during loading the source files.
172
+
173
+ This includes signals, system variables and bus statistics information
174
+ that can be exported/converted.
175
+
176
+ """
177
+ symbols_collection = ExporterSymbols(self._com.Symbols)
178
+ symbols = []
179
+ for i in range(1, symbols_collection.count + 1):
180
+ symbols.append(symbols_collection.item(i))
181
+ return symbols
182
+
183
+ def time_section(self):
184
+ """Returns the TimeSection object."""
185
+ raise NotImplementedError("TimeSection access is not implemented yet.")
186
+
187
+ def load(self):
188
+ """Loads source files of an exporter and determines the signals and messages.
189
+
190
+ If several source files are set, all signals and messages of all
191
+ source files are determined.
192
+
193
+ """
194
+ self._com.Load()
195
+
196
+ def save(self, no_prompt_user: bool = True):
197
+ """Starts the export/conversion.
198
+
199
+ Although the parameter noPromptUser is set to True, the function will fail
200
+ if a failure situation occurs and the storage cannot be performed. Possible
201
+ failure situations are e.g. write-protection or disk full.
202
+
203
+ """
204
+ self._com.Save(noPromptUser=no_prompt_user)
205
+
206
+
207
+ class Filter:
208
+ """Represents a Pass Filter for messages and signals in usage with an exporter."""
209
+
210
+ def __init__(self, filter_com):
211
+ self._com = win32com.client.Dispatch(filter_com)
212
+
213
+ @property
214
+ def count(self) -> int:
215
+ """This property returns the number of objects inside the collection."""
216
+ return int(self._com.Count)
217
+
218
+ @property
219
+ def enabled(self) -> bool:
220
+ """This property activates/deactivates a Filter object or returns its state.
221
+
222
+ The initial value of this property is False.
223
+
224
+ """
225
+ return self._com.Enabled
226
+
227
+ @enabled.setter
228
+ def enabled(self, enabled: bool):
229
+ self._com.Enabled = enabled
230
+
231
+ def item(self, index):
232
+ """This property returns an object from the collection."""
233
+ raise NotImplementedError("Item access is not implemented yet.")
234
+
235
+ def add(self, fullname: str):
236
+ """This method adds a message or a signal to the explorer's filter.
237
+
238
+ The Exporter object needs fully qualified names of all messages and signals
239
+ that have to be taken into consideration during export or conversion:
240
+
241
+ Messages:
242
+ <DatabaseName>::<MessageName>
243
+ Signals:
244
+ <DatabaseName>::<MessageName>::<SignalName>
245
+ System variables:
246
+ <Namespace>::<SystemVariable>
247
+ Environment variables:
248
+ <DatabaseName>::<EnvironmentVariable>
249
+
250
+ When performing a message-oriented conversion only messages, system variables
251
+ and environment variables are taken into consideration. Any (even specified)
252
+ signals will be ignored.
253
+
254
+ When performing a signal-orientierted export messages, signals, system variables
255
+ and environment variables are taken into consideration. All signals included in
256
+ specified messages, system variables and environment variables will be
257
+ exported as well.
258
+
259
+ Besides using their symbolic name messages can be declared by using their
260
+ numeric ID, which either can be decimal or hexadecimal.
261
+
262
+ :param fullname: of the signal or message
263
+
264
+ """
265
+ self._com.Add(fullname)
266
+
267
+ def clear(self):
268
+ """This method clears all messages and signals of the filter."""
269
+ self._com.Clear()
270
+
271
+ def remove(self, index: int):
272
+ """This method removes a message or a signal from the filter.
273
+
274
+ :param index: number
275
+
276
+ """
277
+ self._com.Remove(index)
278
+
279
+
280
+ class Messages:
281
+
282
+ """Collection of messages."""
283
+
284
+ def __init__(self, messages_com):
285
+ self._com = win32com.client.Dispatch(messages_com)
286
+
287
+ @property
288
+ def count(self) -> int:
289
+ """This property returns the number of objects inside the collection."""
290
+ return int(self._com.Count)
291
+
292
+ def item(self, index: int) -> "Message":
293
+ """This property returns an object from the collection."""
294
+ return Message(self._com.Item(index))
295
+
296
+
297
+ class ExporterSymbols:
298
+
299
+ """Collection of signals."""
300
+
301
+ def __init__(self, symbols_com):
302
+ self._com = win32com.client.Dispatch(symbols_com)
303
+
304
+ @property
305
+ def count(self) -> int:
306
+ """This property returns the number of objects inside the collection."""
307
+ return int(self._com.Count)
308
+
309
+ def item(self, index: int) -> "ExporterSymbol":
310
+ """This property returns an object from the collection."""
311
+ return ExporterSymbol(self._com.Item(index))
312
+
313
+
314
+ class Message:
315
+
316
+ """The Message object represents a single message."""
317
+
318
+ def __init__(self, message_com):
319
+ self._com = win32com.client.Dispatch(message_com)
320
+
321
+ @property
322
+ def full_name(self) -> str:
323
+ """This property determines the fully qualified name of a message.
324
+
325
+ The following format is used: <DatabaseName>::<MessageName>
326
+
327
+ """
328
+ return self._com.FullName
329
+
330
+
331
+ class ExporterSymbol:
332
+
333
+ """Symbol (signal, system variable or bus statistics information).
334
+
335
+ Found in a source file, loaded by the Exporter.
336
+
337
+ """
338
+
339
+ def __init__(self, message_com):
340
+ self._com = win32com.client.Dispatch(message_com)
341
+
342
+ @property
343
+ def full_name(self) -> str:
344
+ """This property returns the fully qualified symbol name."""
345
+ return self._com.FullName
@@ -2,12 +2,14 @@
2
2
  import os
3
3
  import time
4
4
  import logging
5
+
5
6
  import pythoncom
6
7
  import win32com.client
7
- from typing import Union
8
+ from typing import Union, Iterable
8
9
  from datetime import datetime
9
10
 
10
11
  # import internal modules here
12
+ from .logging_collection import LoggingCollection, Logging, ExporterSymbol, Message
11
13
  from .py_canoe_logger import PyCanoeLogger
12
14
 
13
15
 
@@ -53,8 +55,8 @@ class CANoe:
53
55
 
54
56
  def __init_canoe_application_bus(self):
55
57
  self.bus_com_obj = win32com.client.Dispatch(self.application_com_obj.Bus)
56
- self.bus_databases = win32com.client.Dispatch(self.bus_com_obj.Databases)
57
- self.bus_nodes = win32com.client.Dispatch(self.bus_com_obj.Nodes)
58
+ self.bus_databases_com_obj = win32com.client.Dispatch(self.bus_com_obj.Databases)
59
+ self.bus_nodes_com_obj = win32com.client.Dispatch(self.bus_com_obj.Nodes)
58
60
 
59
61
  def __init_canoe_application_capl(self):
60
62
  self.capl_obj = lambda: CanoeCapl(self.application_com_obj)
@@ -63,16 +65,18 @@ class CANoe:
63
65
  self.configuration_com_obj = win32com.client.Dispatch(self.application_com_obj.Configuration)
64
66
  if self.configuration_events_enabled:
65
67
  win32com.client.WithEvents(self.configuration_com_obj, CanoeConfigurationEvents)
66
- self.configuration_offline_setup = win32com.client.Dispatch(self.configuration_com_obj.OfflineSetup)
67
- self.configuration_offline_setup_source = win32com.client.Dispatch(self.configuration_offline_setup.Source)
68
- self.configuration_offline_setup_source_sources = win32com.client.Dispatch(self.configuration_offline_setup_source.Sources)
69
- sources = self.configuration_offline_setup_source_sources
68
+ self.configuration_offline_setup_com_obj = win32com.client.Dispatch(self.configuration_com_obj.OfflineSetup)
69
+ self.configuration_offline_setup_source_com_obj = win32com.client.Dispatch(self.configuration_offline_setup_com_obj.Source)
70
+ self.configuration_offline_setup_logging_collection = lambda: win32com.client.Dispatch(self.configuration_offline_setup_com_obj.LoggingCollection)
71
+ self.configuration_offline_setup_source_sources_com_obj = win32com.client.Dispatch(self.configuration_offline_setup_source_com_obj.Sources)
72
+ sources = self.configuration_offline_setup_source_sources_com_obj
70
73
  sources_count = sources.Count + 1
71
74
  self.configuration_offline_setup_source_sources_paths = lambda: [sources.Item(index) for index in range(1, sources_count)]
72
- self.configuration_online_setup = win32com.client.Dispatch(self.configuration_com_obj.OnlineSetup)
73
- self.configuration_online_setup_bus_statistics = win32com.client.Dispatch(self.configuration_online_setup.BusStatistics)
74
- self.configuration_online_setup_bus_statistics_bus_statistic = lambda bus_type, channel: win32com.client.Dispatch(self.configuration_online_setup_bus_statistics.BusStatistic(bus_type, channel))
75
- self.configuration_general_setup = CanoeConfigurationGeneralSetup(self.configuration_com_obj)
75
+ self.configuration_online_setup_com_obj = win32com.client.Dispatch(self.configuration_com_obj.OnlineSetup)
76
+ self.configuration_online_setup_bus_statistics_com_obj = win32com.client.Dispatch(self.configuration_online_setup_com_obj.BusStatistics)
77
+ self.configuration_online_setup_bus_statistics_bus_statistic = lambda bus_type, channel: win32com.client.Dispatch(self.configuration_online_setup_bus_statistics_com_obj.BusStatistic(bus_type, channel))
78
+ self.configuration_online_setup_logging_collection = lambda: win32com.client.Dispatch(self.configuration_online_setup_com_obj.LoggingCollection)
79
+ self.configuration_general_setup_obj = CanoeConfigurationGeneralSetup(self.configuration_com_obj)
76
80
  self.configuration_simulation_setup = lambda: CanoeConfigurationSimulationSetup(self.configuration_com_obj)
77
81
  self.__replay_blocks = self.configuration_simulation_setup().replay_collection.fetch_replay_blocks()
78
82
  self.configuration_test_setup = lambda: CanoeConfigurationTestSetup(self.configuration_com_obj)
@@ -113,6 +117,9 @@ class CANoe:
113
117
  def __init_canoe_application_version(self):
114
118
  self.version_com_obj = win32com.client.Dispatch(self.application_com_obj.Version)
115
119
 
120
+ def __init_logging_collection(self):
121
+ self.logging_collection = LoggingCollection(self.configuration_com_obj.OnlineSetup.LoggingCollection)
122
+
116
123
  def new(self, auto_save=False, prompt_user=False) -> None:
117
124
  try:
118
125
  self.__init_canoe_application()
@@ -155,6 +162,7 @@ class CANoe:
155
162
  self.__init_canoe_application_networks()
156
163
  self.__init_canoe_application_system()
157
164
  self.__init_canoe_application_ui()
165
+ self.__init_logging_collection()
158
166
  self.__log.debug(f'📢 CANoe configuration successfully opened 🎉')
159
167
  else:
160
168
  raise PyCanoeException(f'CANoe configuration "{canoe_cfg}" not found')
@@ -283,7 +291,7 @@ class CANoe:
283
291
  if file_already_added:
284
292
  self.__log.warning(f'⚠️ File "{absolute_log_file_path}" already added as offline source')
285
293
  else:
286
- self.configuration_offline_setup_source_sources.Add(absolute_log_file_path)
294
+ self.configuration_offline_setup_source_sources_com_obj.Add(absolute_log_file_path)
287
295
  self.__log.debug(f'📢 File "{absolute_log_file_path}" added as offline source')
288
296
  return True
289
297
  else:
@@ -1281,14 +1289,14 @@ class CANoe:
1281
1289
  self.__log.warning('⚠️ measurement is running. not possible to add database')
1282
1290
  return False
1283
1291
  else:
1284
- databases = self.configuration_general_setup.database_setup.databases.fetch_databases()
1292
+ databases = self.configuration_general_setup_obj.database_setup.databases.fetch_databases()
1285
1293
  if database_file in [database.full_name for database in databases.values()]:
1286
1294
  self.__log.warning(f'⚠️ database "{database_file}" already added')
1287
1295
  return False
1288
1296
  else:
1289
- self.configuration_general_setup.database_setup.databases.add_network(database_file, database_network)
1297
+ self.configuration_general_setup_obj.database_setup.databases.add_network(database_file, database_network)
1290
1298
  wait(1)
1291
- databases = self.configuration_general_setup.database_setup.databases.fetch_databases()
1299
+ databases = self.configuration_general_setup_obj.database_setup.databases.fetch_databases()
1292
1300
  for database in databases.values():
1293
1301
  if database.full_name == database_file:
1294
1302
  database.channel = database_channel
@@ -1311,7 +1319,7 @@ class CANoe:
1311
1319
  self.__log.warning('⚠️ measurement is running. not possible to remove database')
1312
1320
  return False
1313
1321
  else:
1314
- databases = self.configuration_general_setup.database_setup.databases
1322
+ databases = self.configuration_general_setup_obj.database_setup.databases
1315
1323
  if database_file not in [database.full_name for database in databases.fetch_databases().values()]:
1316
1324
  self.__log.warning(f'⚠️ database "{database_file}" not available to remove')
1317
1325
  return False
@@ -1319,7 +1327,7 @@ class CANoe:
1319
1327
  for i in range(1, databases.count + 1):
1320
1328
  database_com_obj = databases.com_obj.Item(i)
1321
1329
  if database_com_obj.FullName == database_file and database_com_obj.Channel == database_channel:
1322
- self.configuration_general_setup.database_setup.databases.remove(i)
1330
+ self.configuration_general_setup_obj.database_setup.databases.remove(i)
1323
1331
  wait(1)
1324
1332
  self.__log.debug(f'👉 database "{database_file}" removed from channel {database_channel}')
1325
1333
  return True
@@ -1327,6 +1335,81 @@ class CANoe:
1327
1335
  self.__log.error(f'😡 failed to remove database "{database_file}". {e}')
1328
1336
  return False
1329
1337
 
1338
+ def get_logging_blocks(self) -> list[Logging]:
1339
+ """Return all available logging blocks."""
1340
+ blocks = []
1341
+ for i in range(1, self.logging_collection.count + 1):
1342
+ blocks.append(self.logging_collection.item(i))
1343
+ return blocks
1344
+
1345
+ def add_logging_block(self, full_name: str) -> Logging:
1346
+ """Adds new logging block.
1347
+
1348
+ :param full_name: full path to log file as "C:/file.(asc|blf|mf4|...)", may have
1349
+ field functions like {IncMeasurement} in the file name
1350
+
1351
+ """
1352
+ return self.logging_collection.add(full_name)
1353
+
1354
+ def remove_logging_block(self, index: int) -> None:
1355
+ """Remove logging block at given index.
1356
+
1357
+ Blocks` indexes starts from 1 instead of 0. After one block is removed
1358
+ the rest shift up e.g. having 3 loggers [L1, L2, L3] if we remove index=2
1359
+ the new order will be [L1, L3] with indexes 1 and 2.
1360
+
1361
+ """
1362
+ if index==0:
1363
+ raise ValueError("Logging blocks indexing starts from 1 and not 0.")
1364
+
1365
+ self.logging_collection.remove(index)
1366
+
1367
+ def load_logs_for_exporter(self, logger_index: int) -> None:
1368
+ """Load all source files of exporter and determine symbols/messages.
1369
+
1370
+ :param logger_index: indicates logger and its log files
1371
+ """
1372
+ self.logging_collection.item(logger_index).exporter.load()
1373
+
1374
+ def get_symbols(self, logger_index: int) -> list[ExporterSymbol]:
1375
+ """Return all exporter symbols from given logger."""
1376
+ return self.logging_collection.item(logger_index).exporter.symbols
1377
+
1378
+ def get_messages(self, logger_index: int) -> list[Message]:
1379
+ """Return all messages from given logger."""
1380
+ return self.logging_collection.item(logger_index).exporter.messages
1381
+
1382
+ def add_filters_to_exporter(self, logger_index: int, full_names: Iterable):
1383
+ """Add messages and symbols to exporter filter by their full names.
1384
+
1385
+ :param logger_index: indicates logger
1386
+ :param full_names: of messages and symbols
1387
+
1388
+ """
1389
+ expo_filter = self.logging_collection.item(logger_index).exporter.filter
1390
+ for name in full_names:
1391
+ expo_filter.add(name)
1392
+
1393
+ def start_export(self, logger_index: int):
1394
+ """Starts the export/conversion of exporter.
1395
+
1396
+ :param logger_index: indicates logger
1397
+
1398
+ """
1399
+ self.logging_collection.item(logger_index).exporter.save()
1400
+
1401
+ def set_configuration_modified(self, modified: bool) -> None:
1402
+ """Change status of configuration.
1403
+
1404
+ Modified=False makes it possible to load other config or close
1405
+ the application without the popup "Do you want to save changes?".
1406
+ Without that the application will stop on that popup.
1407
+
1408
+ :param modified: change configuration flag
1409
+
1410
+ """
1411
+ self.configuration_com_obj.Modified = modified
1412
+
1330
1413
 
1331
1414
  def wait(timeout_seconds=0.1):
1332
1415
  """Waits for a specified timeout, pumping Windows messages.
@@ -16,17 +16,14 @@ class PyCanoeLogger:
16
16
  self.log = logging.getLogger('CANOE_LOG')
17
17
  self.log.handlers.clear()
18
18
  self.log.propagate = False
19
- self.__py_canoe_log_initialization(py_canoe_log_dir)
20
-
21
- def __py_canoe_log_initialization(self, py_canoe_log_dir):
22
19
  self.log.setLevel(logging.DEBUG)
23
- log_format = logging.Formatter("%(asctime)s [CANOE_LOG] [%(levelname)-4.8s] %(message)s")
24
- ch = logging.StreamHandler(sys.stdout)
25
- ch.setFormatter(log_format)
26
- self.log.addHandler(ch)
20
+ self.__log_format = logging.Formatter("%(asctime)s [CANOE_LOG] [%(levelname)-4.8s] %(message)s")
21
+ self.__handler = logging.StreamHandler(sys.stdout)
22
+ self.__handler.setFormatter(self.__log_format)
23
+ self.log.addHandler(self.__handler)
27
24
  if py_canoe_log_dir != '' and not os.path.exists(py_canoe_log_dir):
28
25
  os.makedirs(py_canoe_log_dir, exist_ok=True)
29
26
  if os.path.exists(py_canoe_log_dir):
30
- fh = handlers.RotatingFileHandler(fr'{py_canoe_log_dir}\py_canoe.log', maxBytes=0, encoding='utf-8')
31
- fh.setFormatter(log_format)
32
- self.log.addHandler(fh)
27
+ file_handler = handlers.RotatingFileHandler(fr'{py_canoe_log_dir}\py_canoe.log', maxBytes=0, encoding='utf-8')
28
+ file_handler.setFormatter(self.__log_format)
29
+ self.log.addHandler(file_handler)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "py_canoe"
3
- version = "3.0.2"
3
+ version = "3.0.3"
4
4
  description = "Python CANoe Package"
5
5
  authors = ["chaitu-ycr <chaitu.ycr@gmail.com>"]
6
6
  license = "LICENSE"
File without changes