winIDEA SDK
Loading...
Searching...
No Matches
data_recorder.py
1# This script is licensed under BSD License, see file LICENSE.txt, or search for `License` in the SDK online help.
2# (c) TASKING Germany GmbH, 2023
3#
4# This script demonstrates recording of watch expressions and variables
5# using slower but more flexible evaluator and fast batch access.
6# The recorded data is then written to CSV file. If the 'openpyxl'
7# module is installed, the data is also written to XLSX file.
8#
9# All the above functionality is grouped into functions, so you can
10# easily take out only part of the script. This script can also be
11# imported as a module to user's scripts, so it is easy to reuse
12# functions found here.
13#
14# See also data_recorder_with_daq.py
15
16import csv
17import time
18import isystem.connect as ic
19
20winidea_id = ''
21
22
23try:
24 import pylab as pl
25 isPyLabInstalled = True
26except ImportError as ex:
27 isPyLabInstalled = False
28
29
30def initTarget(cmgr):
31 """
32 This function initializes the target. Customize it according to
33 your needs.
34 """
35
36 debugCtrl = ic.CDebugFacade(cmgr)
37
38 debugCtrl.reset()
39 debugCtrl.runUntilFunction("main")
40 debugCtrl.waitUntilStopped()
41
42 debugCtrl.run()
43
44 return debugCtrl
45
46
47def recordWatchExpressions(debugCtrl,
48 watches,
49 recordingTimeInSeconds,
50 samplingInterval,
51 fileName,
52 isRecordToMemory,
53 isPrintToStdOut):
54 """
55 This function evaluates watch expressions and returns list of results. The
56 amount of data that can be recorded is limited by the amount of system
57 memory.
58
59 Parameters:
60
61 debugCtrl - iSYSTEM's debugCtrl controller, which provides access to target
62
63 watches - list of strings with watch expressions, for example
64 ['main_loop_counter,h', 'g_arrayInt[0]']
65
66 recordingTime - how long to record the expressions, in
67 seconds. Recording can also be terminated by
68 pressing the 'q' key
69
70 samplingInterval - defines how much time should pass between samples
71
72 isPrintToStdOut - if True, each row is printed to stdout during recording
73
74 Returns:
75 List of rows, where each row is a list containing time stamp and recorded
76 values in the same order as watch expressions were specified. Example for
77 three expressions:
78 [[0, 23, 45, 4.35],
79 [0.1, 24, -525, 1.78]
80 ]
81
82 """
83
84 startTime = time.time()
85 endTime = startTime + recordingTimeInSeconds
86
87 if isPrintToStdOut:
88 print(['Time'] + watches)
89
90 recordedData = []
91 sampleCounter = 0
92
93 while (time.time() < endTime):
94
95 currentTime = time.time() - startTime
96 row = [currentTime]
97
98 for expression in watches:
99
100 value = debugCtrl.evaluate(ic.IConnectDebug.fRealTime, expression)
101 # CValueType contains also other methods, if you want to get
102 # value as integer, float, ... See
103 # http://isystem.com/files/isystem.connect/api/classisys_1_1_c_value_type.html
104 row.append(value.getResult())
105
106 if isPrintToStdOut:
107 print(row)
108
109 if isRecordToMemory:
110 recordedData.append(row)
111
112 sampleCounter += 1
113 nextSamplingTime = startTime + samplingInterval * sampleCounter
114 sleepTime = nextSamplingTime - time.time()
115 if sleepTime > 0:
116 time.sleep(sleepTime)
117
118 return recordedData
119
120
121def recordWatchExpressionsToCSV(debugCtrl,
122 watches,
123 recordingTimeInSeconds,
124 samplingInterval,
125 fileName,
126 isRecordToMemory,
127 isPrintToStdOut):
128 """
129 This function evaluates watch expressions and writes them to CSV file.
130 If isRecordToMemory == False, the amount of data that can be recorded is
131 limited by the free disk size.
132
133 Parameters:
134
135 debugCtrl - iSYSTEM's debugCtrl controller, which provides access to target
136
137 watches - list of strings with watch expressions, for example
138 ['main_loop_counter,h', 'g_arrayInt[0]']
139
140 recordingTime - how long to record the expressions, in
141 seconds. Recording can also be terminated by
142 pressing the 'q' key
143
144 samplingInterval - defines how much time should pass between samples
145
146 fileName - name of the output CSV file.
147
148 isRecordToMemory - if True, then data is also stored into memory
149 array and returned as function return value. Be
150 aware of memory usage in this case. If false,
151 an empty list is returned.
152
153 isPrintToStdOut - if True, each row is printed to stdout during recording
154
155 Returns:
156 List of rows, where each row is a list containing time stamp and recorded
157 values in the same order as watch expressions were specified. Example for
158 three expressions:
159 [[0, 23, 45, 4.35],
160 [0.1, 24, -525, 1.78]
161 ]
162
163 If isRecordToMemory == False, an empty list is returned.
164
165 """
166
167 startTime = time.time()
168 endTime = startTime + recordingTimeInSeconds
169
170 recordedData = []
171
172 with open(fileName, 'w') as csvFile:
173
174 # add parameter dialect = 'excel' for Excel specific output
175 # See also http://docs.python.org/library/csv.html
176 # for configuring CSV output
177 csvWriter = csv.writer(csvFile)
178 # write header
179 header = ['Time'] + watches
180 print(header)
181 csvWriter.writerow(header)
182 sampleCounter = 0
183
184 while (time.time() < endTime):
185
186 currentTime = time.time() - startTime
187 row = [currentTime]
188
189 for expression in watches:
190
191 print('expression: ', expression)
192 value = debugCtrl.evaluate(ic.IConnectDebug.fRealTime, expression)
193 # CValueType contains also other methods, if you want to get
194 # value as integer, float, ... See
195 # http://isystem.com/files/isystem.connect/api/classisys_1_1_c_value_type.html
196 row.append(value.getResult())
197
198 csvWriter.writerow(row)
199
200 if isPrintToStdOut:
201 print(row)
202
203 if isRecordToMemory:
204 recordedData.append(row)
205
206 sampleCounter += 1
207 nextSamplingTime = startTime + samplingInterval * sampleCounter
208 sleepTime = nextSamplingTime - time.time()
209 if sleepTime > 0:
210 time.sleep(sleepTime)
211
212 return recordedData
213
214
215def recordBatch(cmgr,
216 dbgCtrl,
217 variables,
218 recordingTimeInSeconds,
219 samplingIntervalInSeconds):
220 """
221 This function runs batch access and returns recorded data as a list of
222 SBatchAccessItemResult structures.
223 The amount of data that can be recorded is limited by the amount of system
224 memory.
225
226 Parameters:
227
228 cmgr - iSYSTEM's connection manager, which provides connection to target
229 debugCtrl - iSYSTEM's debugCtrl controller, which provides access to target
230
231 variables - list of strings with variables, for example
232 ['main_loop_counter', 'g_baseStruct.i_base']
233
234 recordingTime - how long to record the expressions, in
235 seconds.
236
237 samplingInterval - defines how much time should pass between samples
238
239 Returns:
240 A list of SBatchAccessItemResult structures, one structure per variable
241 per time stamp.
242 """
243
244 numItems = len(variables)
245 numRuns = int(recordingTimeInSeconds / samplingIntervalInSeconds)
246
247 # Set parameters for batch access. If your target does not
248 # support real time access, use 'flStopResume' instead of
249 # 'flRealTime'.
250 header = ic.SBatchAccessHeader()
251 header.m_dwFlags = (ic.SBatchAccessHeader.flRealTime |
252 ic.SBatchAccessHeader.flWantTimeStamp)
253 header.m_dwNumItems = numItems
254 header.m_dwNumRuns = numRuns
255 header.m_qwStartAtTime = 0 # immediate start
256 header.m_qwRunInterval = int(samplingIntervalInSeconds * 1000000)
257
258 ba_items = ic.VectorBatchAccessItem()
259 itemSizes = []
260
261 for var in variables:
262 item = ic.SBatchAccessItem()
263
264 varInfo = dbgCtrl.getSymbolInfo(ic.IConnectDebug.fRealTime, var)
265
266 # Define item(s) to be accessed
267 item.m_byFlags = ic.SBatchAccessItem.flRead # it's a read
268 item.m_bySize = varInfo.getSizeMAUs() # var size
269 item.m_byMemArea = varInfo.getMemArea() # var memory space
270 item.m_aAddress = varInfo.getAddress() # var address
271 ba_items.push_back(item)
272 itemSizes.append(varInfo.getSizeMAUs())
273
274
275 ba_results = ic.VectorBatchAccessResult(numItems * numRuns)
276 dataCtrl = ic.CDataController(cmgr)
277
278 # Finally we record the data
279 dataCtrl.batchAccess(0, header, ba_items, ba_results)
280
281 return ba_results, itemSizes
282
283
284def bigEndian2Int(numBytes, cArray):
285 """ Converts big-endian seq. of bytes to integer. """
286
287 value = 0
288 for i in range(numBytes):
289 value <<= 8
290 value |= ic.CDataController.getByte(cArray, i)
291
292 return value
293
294
295def batchAccessResultToCSV(fileName, ba_results, itemSizes, variables):
296 """
297 Writes results of batchAccess() to CSV file. Uses big endian conversion.
298
299 Parameters:
300 fileName - then name of the output file
301 ba_results - results returned by function recordBatch()
302 itemSizes - results returned by function recordBatch()
303 """
304
305 numItems = len(itemSizes)
306 numRuns = int(ba_results.size() / numItems)
307
308 firstAccess = ba_results[0].m_qwTimeStamp
309
310 with open(fileName, 'w') as csvFile:
311
312 csvWriter = csv.writer(csvFile)
313
314 # write header
315 csvWriter.writerow(['Time'] + variables)
316
317 for runIdx in range(numRuns):
318
319 row = [(ba_results[runIdx * numItems].m_qwTimeStamp - firstAccess)/1000000.]
320
321 for varIdx in range(numItems):
322 result = ba_results[runIdx * numItems + varIdx]
323
324 if (result.m_byResult == ic.SBatchAccessItemResult.resOK):
325 value = bigEndian2Int(itemSizes[varIdx], result.m_abyData)
326 row.append(value)
327
328 elif (result.m_byResult == ic.SBatchAccessItemResult.resAccess):
329 row.append('Can not access. Check access flags in header!')
330 elif (result.m_byResult == ic.SBatchAccessItemResult.resTimeout):
331 row.append('Timeout! Probably the sampling rate is invalid!')
332 else:
333 row.append('Invalid error code: ' + str(result.m_byResult))
334
335 csvWriter.writerow(row)
336
337
338def batchAccessResultToList(ba_results, itemSizes):
339 """
340 Converts results of batchAccess() to Python list. Uses big endian conversion.
341
342 Parameters:
343 fileName - then name of the output file
344 ba_results - results returned by function recordBatch()
345 itemSizes - results returned by function recordBatch()
346 """
347
348 numItems = len(itemSizes)
349 numRuns = int(ba_results.size() / numItems)
350
351 firstAccess = ba_results[0].m_qwTimeStamp
352 recordedData = []
353
354 for runIdx in range(numRuns):
355
356 row = [(ba_results[runIdx * numItems].m_qwTimeStamp - firstAccess)/1000000.]
357
358 for varIdx in range(numItems):
359 result = ba_results[runIdx * numItems + varIdx]
360
361 if (result.m_byResult == ic.SBatchAccessItemResult.resOK):
362 value = bigEndian2Int(itemSizes[varIdx], result.m_abyData)
363 row.append(value)
364
365 elif (result.m_byResult == ic.SBatchAccessItemResult.resAccess):
366 row.append('Can not access. Check access flags in header!')
367 elif (result.m_byResult == ic.SBatchAccessItemResult.resTimeout):
368 row.append('Timeout! Probably the sampling rate is invalid!')
369 else:
370 row.append('Invalid error code: ' + str(result.m_byResult))
371
372 recordedData.append(row)
373
374 return recordedData
375
376
377def writeDatatoCSV(fileName, data, expressions):
378 with open(fileName, 'w', neline='') as csvFile:
379 csvWriter = csv.writer(csvFile)
380 # write header
381 csvWriter.writerow(['Time'] + expressions)
382
383 for row in data:
384 csvWriter.writerow(row)
385
386
387def main():
388 cmgr = ic.ConnectionMgr()
389 cmgr.connect(ic.CConnectionConfig().instanceId(winidea_id))
390
391 debugCtrl = initTarget(cmgr)
392 ideCtrl = ic.CIDEController(cmgr)
393
394 # required so that character codes are UTF-8 compliant
395 ideCtrl.setOption('/IDE/Debug.Symbols.Format.ANSI', True)
396
397 # watches can contain all expressions acceptable in winIDEA's Watch
398 # window. For example, to read input from IO module, one can
399 # evaluate expression: `DigitalIn.DIN0
400 # Modifiers 'd' and 'h' define hex or decimal values
401 watches = ['main_loop_counter,h', 'g_intArray1[0],d', 'g_complexStruct.m_struct.i_base,d']
402 recordingTimeInSeconds = 5
403 samplingIntervalInSeconds = 0.2
404 filePrefix = 'watches'
405 isRecordToMemory = True
406 isPrintToStdOut = True
407 print('Recording watch expressions:')
408 watchResults = recordWatchExpressionsToCSV(debugCtrl,
409 watches,
410 recordingTimeInSeconds,
411 samplingIntervalInSeconds,
412 filePrefix + '.csv',
413 isRecordToMemory,
414 isPrintToStdOut)
415
416 filePrefix = 'batch'
417 # Variables allocated in memory can be specified here
418 variables = ['main_loop_counter', 'g_intArray1[0]', 'g_complexStruct.m_struct.i_base']
419 # start from 0
420 debugCtrl.modify(ic.IConnectDebug.fRealTime, 'main_loop_counter', '0')
421 samplingIntervalInSeconds = 0.5
422
423 print('Recording with batch access...')
424 batchResults, itemSizes = recordBatch(cmgr,
425 debugCtrl,
426 variables,
427 recordingTimeInSeconds,
428 samplingIntervalInSeconds)
429
430 batchAccessResultToCSV(filePrefix + '.csv', batchResults,
431 itemSizes, variables)
432
433 batchData = batchAccessResultToList(batchResults, itemSizes)
434
435 if isPyLabInstalled:
436 data = pl.array(batchData)
437 times = data[:, 0] # the first column is time
438 pl.subplot(3, 1, 1)
439 pl.plot(times, data[:, 1])
440 pl.ylabel('main_loop_counter')
441
442 pl.subplot(3, 1, 2)
443 pl.plot(times, data[:, 2], 'g')
444 pl.ylabel('g_intArray1[0]')
445
446 pl.subplot(3, 1, 3)
447 pl.plot(times, data[:, 3], 'r')
448 pl.ylabel('g_complexStruct.m_struct.i_base')
449 pl.xlabel('t[s]')
450
451 pl.show()
452
453 print('Done!')
454
455
456if __name__ == '__main__':
457 main()