winIDEA SDK
data_recorder_with_daq.py
1# This script is licensed under BSD License, see file LICENSE.txt.
2#
3# (c) TASKING Germany GmbH, 2023
4
5"""
6This script demonstrates recording of variables using fast data
7acquisition (DAQ) approach. The recorded data is written to CSV
8file. If the 'openpyxl' module is installed, the data is also
9written to XLSX file. If 'pylab' module is installed, the acquired
10data is also plotted.
11
12All the above functionality is grouped into functions, so you can
13easily take out only part of the script. This script can also be
14imported as a module to user's scripts, so it is easy to reuse
15functions found here.
16"""
17
18import sys
19import csv
20import time
21import isystem.connect as ic
22
23winidea_id = ''
24
25
26try:
27 import openpyxl
28 isOpenPyXLInstalled = True
29except (ImportError) as ex:
30 isOpenPyXLInstalled = False
31
32try:
33 import pylab as plab
34 isPyLabInstalled = True
35except (ImportError) as ex:
36 isPyLabInstalled = False
37
38
39def printHelp():
40 print("Usage: dataRecorderWithDAQ.py <variableName1 samplingRateInSeconds1> ...")
41 print("")
42 print(" If 'samplingRateInSeconds' == 0, the smallest possible sampling")
43 print(" time is used. Supported values: 0.001, 0.01, 0.1, 1 second.")
44 print(" See CDAQController::EDAQSampligFlags for all possible")
45 print(" values.")
46 print("")
47 print("")
48 print("Example: dataRecorderWithDAQ.py main_loop_counter 0.1 g_baseStruct.i_base 0.01")
49 print(" records main_loop_counter each 100 ms, and g_baseStruct.i_base with 10 ms period.")
50
51
52def initTarget(cmgr):
53 """
54 This function initializes the target. Customize it according to
55 your needs.
56 """
57
58 debugCtrl = ic.CDebugFacade(cmgr)
59
60 debugCtrl.download()
61 debugCtrl.runUntilFunction("main")
62 debugCtrl.waitUntilStopped()
63
64 debugCtrl.run()
65
66 return debugCtrl
67
68
69def realTimeToDAQTimeFlags(samplTime):
70 """
71 Converts the given sampling time in seconds to EDAQSamplingFlags.
72 """
73 if samplTime < 0.001:
74 return ic.CDAQController.daqSampleMax
75 elif samplTime < 0.01:
76 return ic.CDAQController.daqSample1ms
77 elif samplTime < 0.1:
78 return ic.CDAQController.daqSample10ms
79 elif samplTime < 1:
80 return ic.CDAQController.daqSample100ms
81 else:
82 return ic.CDAQController.daqSample1s
83
84
85def recordVariables(cmgr,
86 debugCtrl,
87 variables,
88 recordingTimeInSeconds,
89 fileName,
90 isRecordToMemory,
91 isPrintToStdOut):
92 """
93 This function reads varibles and writes them to CSV file.
94 If isRecordToMemory == False, the amount of data that can be recorded is
95 limited by the free disk size.
96
97 Parameters:
98
99 debugCtrl - iSYSTEM's debugCtrl controller, which provides access to target
100
101 variables - array of two element arrays, where the first element contains
102 variable name and the second element contains sampling interval,
103 for example: [['main_loop_counter', 0.1], ['g_baseStruct.i_base', 0]].
104 Sampling interval set to 0 means the smallest interval possible.
105
106 recordingTimeInSeconds - how long to record the data, in seconds. Recording
107 can also be terminated by pressing the 'q' key
108
109 fileName - name of the output CSV file.
110
111 isRecordToMemory - if True, then data is also stored into memory
112 array and returned as function return value. Be
113 aware of memory usage in this case. If false,
114 an empty list is returned.
115
116 isPrintToStdOut - if True, each row is printed to stdout during recording
117
118 Returns:
119 List of rows, where each row is a list containing time stamp and recorded
120 values in the same order as variable names were specified. Example for
121 two samples of three variables:
122 [[0, 23, 45, 4.35],
123 [0.1, 24, -525, 1.78]
124 ]
125 """
126
127 daqCtrl = ic.CDAQController(cmgr)
128
129 # check if DAQ system is available
130 daqInfo = daqCtrl.info()
131 if daqInfo.getMaxItems() == 0:
132 raise Exception("Data Acquisition (DAQ) system is not available.")
133
134 print('MaxItems = ', daqInfo.getMaxItems())
135
136 daqVariables = ic.DAQConfigVector()
137 for varSamplData in variables:
138 varName = varSamplData[0]
139 samplTime = realTimeToDAQTimeFlags(varSamplData[1])
140
141 if varName.startswith('0x'):
142 # Direct memory address reading (32bit variable)
143 memAddr = int(varName)
144 daqVariables.append(ic.CDAQConfigItem(4, 0, memAddr, samplTime))
145 else:
146 # Reading a regular variable
147 daqVariables.append(ic.CDAQConfigItem(varName, samplTime))
148
149 # note the time of the DAQ system
150 daqTimeStart = daqCtrl.status().getTime()
151
152 daqCtrl.configure(daqVariables)
153
154 # enable DAQ on the entire SoC
155 daqCtrl.enableGlobal(True)
156
157 startTime = time.time()
158 endTime = startTime + recordingTimeInSeconds
159
160 numVars = len(variables)
161 recordedData = []
162
163 with open(fileName, 'w') as csvFile:
164
165 # add parameter dialect = 'excel' for Excel specific output
166 # See also http://docs.python.org/library/csv.html
167 # for configuring CSV output
168 csvWriter = csv.writer(csvFile)
169
170 varNames = []
171 for varData in variables:
172 varNames.append(varData[0])
173
174 csvWriter.writerow(['Time'] + varNames)
175
176 sampleCounter = 0
177
178 lastTime = 0
179 row = [''] * (1 + numVars)
180
181 while time.time() < endTime:
182
183 daqStatus = daqCtrl.status()
184 # if any sample is available, display the status and print the samples
185 if daqStatus.getNumSamplesAvailable() > 0:
186 # print 'Last DAQ acquisition time = ', daqStatus.getLastLoopTime()
187 if daqStatus.getOverflow():
188 print('SAMPLING OVERFLOW!')
189
190 # read available samples into daqSamples
191 daqSamples = ic.DAQSampleVector()
192 daqCtrl.read(daqSamples)
193
194 # print 'Number of samples = ', daqSamples.size()
195 for daqSample in daqSamples:
196
197 sampleTime = daqSample.getTime() - daqTimeStart
198 columnIndex = daqSample.getIndex()+1
199
200 var = daqCtrl.getDataValue(daqSample)
201
202 if var.isTypeUnsigned() or var.isTypeSigned():
203 value = var.getLong()
204 elif var.isTypeFloat():
205 value = var.getDouble()
206 elif var.isTypeAddress():
207 value = var.getAddress().m_aAddress
208 elif var.isTypeCompound():
209 value = 'Struct'
210
211 # new time found - writing
212 if (sampleTime != lastTime):
213
214 # Write the last row of data if this is not the first
215 # data received
216 if (lastTime != 0):
217 csvWriter.writerow(row)
218 if isPrintToStdOut:
219 print(row)
220 if isRecordToMemory:
221 recordedData.append(row)
222
223 #row[:] = ''
224 row = [''] * (numVars+1)
225 row[0] = sampleTime
226
227 # Remember what time was last used to fill rhe row of data
228 lastTime = sampleTime
229
230 row[columnIndex] = value
231
232 # And the last line of data
233 if (lastTime != 0):
234 csvWriter.writerow(row)
235 if isPrintToStdOut:
236 print(row)
237 if isRecordToMemory:
238 recordedData.append(row)
239
240 return recordedData
241
242
243def writeDataToXLSX(fileName, data, expressions):
244 book = openpyxl.Workbook()
245 sheet = book.create_sheet()
246 sheet.title = 'Variables'
247
248 # write header
249 sheet.append(['Time'] + expressions)
250
251 for row in data:
252 sheet.append(row)
253
254 book.save(fileName)
255
256
257def main():
258
259 if len(sys.argv) < 2:
260 printHelp()
261 return
262
263 samplingInfo = []
264 varNames = []
265
266 # create (varName, samplingTime) pairs from params in command line
267 # Example input parameters:
268 # a_my_signal 0.01 a_my_int 0 a_my_char 1 DigitalIn.DIN0 0.1 AnalogIn.AIN1 0.1 DigitalIn.DIN125
269 # For digital mask testing:
270 # DigitalIn.DIN 1 DigitalIn.DIN2 1 DigitalIn.DIN123 1 DigitalIn.DIN765 1 DigitalIn.DIN23456 1
271 for idx in range(1, len(sys.argv), 2):
272 varName = sys.argv[idx]
273 varNames.append(varName)
274 samplingInfo.append([varName, float(sys.argv[idx + 1])])
275
276 cmgr = ic.ConnectionMgr()
277 cmgr.initLogger('daq', 'daqExample.log', ic.CLogger.PYTHON)
278 cmgr.connect(ic.CConnectionConfig().instanceId(winidea_id))
279 if not cmgr.isAttached():
280 print("The connection to winIDEA has not been established - exiting script.")
281 sys.exit(-1)
282 else:
283 print("Established connection to winIDEA.")
284 debugCtrl = initTarget(cmgr)
285
286 # Here you can modify recording time and file name.
287 recordingTimeInSeconds = 3 # after this time the script will stop
288 filePrefix = 'daqData'
289 isRecordToMemory = True
290 isPrintToStdOut = True
291 print('Recording ...')
292
293 daqResults = recordVariables(cmgr,
294 debugCtrl,
295 samplingInfo,
296 recordingTimeInSeconds,
297 filePrefix + '.csv',
298 isRecordToMemory,
299 isPrintToStdOut)
300
301 if isOpenPyXLInstalled and daqResults:
302 writeDataToXLSX(filePrefix + '.xlsx', daqResults, varNames)
303
304 if isPyLabInstalled:
305 lineTypes = ['k', 'r', 'g', 'b', 'k:', 'r:', 'g:', 'b:']
306
307 # create array of strings, since empty values are stored as empty strings
308 data = plab.array(daqResults).astype('S12')
309
310 plotIdx = 1
311 for varName in varNames:
312 plab.subplot(len(varNames), 1, plotIdx)
313
314 # filters all lines, where data is not available - if we have
315 # different sampling times for variables, then the less frequently
316 # sampled variables have no values.
317 linesData = data[data[:, plotIdx] != b'']
318 times = linesData[:, 0] # the first column is time
319 plab.plot(times, linesData[:, plotIdx], lineTypes[(plotIdx - 1)% len(lineTypes)])
320 plab.ylabel(varName)
321 plotIdx += 1
322
323 plab.xlabel('t[s]')
324 plab.show()
325
326 print('Done!')
327
328if __name__ == '__main__':
329 main()