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