##// END OF EJS Templates
act online
joabAM -
r1280:3e5818298b5e
parent child
Show More
1 NO CONTENT: modified file
@@ -1,649 +1,650
1 1 '''
2 2 Created on Set 9, 2015
3 3
4 4 @author: roj-idl71 Karim Kuyeng
5 5 '''
6 6
7 7 import os
8 8 import sys
9 9 import glob
10 10 import fnmatch
11 11 import datetime
12 12 import time
13 13 import re
14 14 import h5py
15 15 import numpy
16 16
17 17 try:
18 18 from gevent import sleep
19 19 except:
20 20 from time import sleep
21 21
22 22 from schainpy.model.data.jroheaderIO import RadarControllerHeader, SystemHeader
23 23 from schainpy.model.data.jrodata import Voltage
24 24 from schainpy.model.proc.jroproc_base import ProcessingUnit, Operation, MPDecorator
25 25 from numpy import imag
26 26
27 27 @MPDecorator
28 28 class AMISRReader(ProcessingUnit):
29 29 '''
30 30 classdocs
31 31 '''
32 32
33 33 def __init__(self):
34 34 '''
35 35 Constructor
36 36 '''
37 37
38 38 ProcessingUnit.__init__(self)
39 39
40 40 self.set = None
41 41 self.subset = None
42 42 self.extension_file = '.h5'
43 43 self.dtc_str = 'dtc'
44 44 self.dtc_id = 0
45 45 self.status = True
46 46 self.isConfig = False
47 47 self.dirnameList = []
48 48 self.filenameList = []
49 49 self.fileIndex = None
50 50 self.flagNoMoreFiles = False
51 51 self.flagIsNewFile = 0
52 52 self.filename = ''
53 53 self.amisrFilePointer = None
54 54
55 55
56 56 #self.dataset = None
57 57
58 58
59 59
60 60
61 61 self.profileIndex = 0
62 62
63 63
64 64 self.beamCodeByFrame = None
65 65 self.radacTimeByFrame = None
66 66
67 67 self.dataset = None
68 68
69 69
70 70
71 71
72 72 self.__firstFile = True
73 73
74 74 self.buffer = None
75 75
76 76
77 77 self.timezone = 'ut'
78 78
79 79 self.__waitForNewFile = 20
80 80 self.__filename_online = None
81 81 #Is really necessary create the output object in the initializer
82 82 self.dataOut = Voltage()
83 83 self.dataOut.error=False
84 84
85 85 def setup(self,path=None,
86 86 startDate=None,
87 87 endDate=None,
88 88 startTime=None,
89 89 endTime=None,
90 90 walk=True,
91 91 timezone='ut',
92 92 all=0,
93 93 code = None,
94 94 nCode = 0,
95 95 nBaud = 0,
96 96 online=False):
97 97
98 98 #print ("T",path)
99 99
100 100 self.timezone = timezone
101 101 self.all = all
102 102 self.online = online
103 103
104 104 self.code = code
105 105 self.nCode = int(nCode)
106 106 self.nBaud = int(nBaud)
107 107
108 108
109 109
110 110 #self.findFiles()
111 111 if not(online):
112 112 #Busqueda de archivos offline
113 113 self.searchFilesOffLine(path, startDate, endDate, startTime, endTime, walk)
114 114 else:
115 115 self.searchFilesOnLine(path, startDate, endDate, startTime,endTime,walk)
116 116
117 117 if not(self.filenameList):
118 118 print("There is no files into the folder: %s"%(path))
119 119 sys.exit(-1)
120 120
121 121 self.fileIndex = -1
122 122
123 123 self.readNextFile(online)
124 124
125 125 '''
126 126 Add code
127 127 '''
128 128 self.isConfig = True
129 129
130 130 pass
131 131
132 132
133 133 def readAMISRHeader(self,fp):
134 134 header = 'Raw11/Data/RadacHeader'
135 135 self.beamCodeByPulse = fp.get(header+'/BeamCode') # LIST OF BEAMS PER PROFILE, TO BE USED ON REARRANGE
136 136 self.beamCode = fp.get('Raw11/Data/Beamcodes') # NUMBER OF CHANNELS AND IDENTIFY POSITION TO CREATE A FILE WITH THAT INFO
137 137 #self.code = fp.get(header+'/Code') # NOT USE FOR THIS
138 138 self.frameCount = fp.get(header+'/FrameCount')# NOT USE FOR THIS
139 139 self.modeGroup = fp.get(header+'/ModeGroup')# NOT USE FOR THIS
140 140 self.nsamplesPulse = fp.get(header+'/NSamplesPulse')# TO GET NSA OR USING DATA FOR THAT
141 141 self.pulseCount = fp.get(header+'/PulseCount')# NOT USE FOR THIS
142 142 self.radacTime = fp.get(header+'/RadacTime')# 1st TIME ON FILE ANDE CALCULATE THE REST WITH IPP*nindexprofile
143 143 self.timeCount = fp.get(header+'/TimeCount')# NOT USE FOR THIS
144 144 self.timeStatus = fp.get(header+'/TimeStatus')# NOT USE FOR THIS
145 145 self.rangeFromFile = fp.get('Raw11/Data/Samples/Range')
146 146 self.frequency = fp.get('Rx/Frequency')
147 147 txAus = fp.get('Raw11/Data/Pulsewidth')
148 148
149 149
150 150 self.nblocks = self.pulseCount.shape[0] #nblocks
151 151
152 152 self.nprofiles = self.pulseCount.shape[1] #nprofile
153 153 self.nsa = self.nsamplesPulse[0,0] #ngates
154 154 self.nchannels = self.beamCode.shape[1]
155 155 self.ippSeconds = (self.radacTime[0][1] -self.radacTime[0][0]) #Ipp in seconds
156 156 #self.__waitForNewFile = self.nblocks # wait depending on the number of blocks since each block is 1 sec
157 157 self.__waitForNewFile = self.nblocks * self.nprofiles * self.ippSeconds # wait until new file is created
158 158
159 159 #filling radar controller header parameters
160 160 self.__ippKm = self.ippSeconds *.15*1e6 # in km
161 161 self.__txA = (txAus.value)*.15 #(ipp[us]*.15km/1us) in km
162 162 self.__txB = 0
163 163 nWindows=1
164 164 self.__nSamples = self.nsa
165 165 self.__firstHeight = self.rangeFromFile[0][0]/1000 #in km
166 166 self.__deltaHeight = (self.rangeFromFile[0][1] - self.rangeFromFile[0][0])/1000
167 167
168 168 #for now until understand why the code saved is different (code included even though code not in tuf file)
169 169 #self.__codeType = 0
170 170 # self.__nCode = None
171 171 # self.__nBaud = None
172 172 self.__code = self.code
173 173 self.__codeType = 0
174 174 if self.code != None:
175 175 self.__codeType = 1
176 176 self.__nCode = self.nCode
177 177 self.__nBaud = self.nBaud
178 178 #self.__code = 0
179 179
180 180 #filling system header parameters
181 181 self.__nSamples = self.nsa
182 182 self.newProfiles = self.nprofiles/self.nchannels
183 183 self.__channelList = list(range(self.nchannels))
184 184
185 185 self.__frequency = self.frequency[0][0]
186 186
187 187
188 188
189 189 def createBuffers(self):
190 190
191 191 pass
192 192
193 193 def __setParameters(self,path='', startDate='',endDate='',startTime='', endTime='', walk=''):
194 194 self.path = path
195 195 self.startDate = startDate
196 196 self.endDate = endDate
197 197 self.startTime = startTime
198 198 self.endTime = endTime
199 199 self.walk = walk
200 200
201 201 def __checkPath(self):
202 202 if os.path.exists(self.path):
203 203 self.status = 1
204 204 else:
205 205 self.status = 0
206 206 print('Path:%s does not exists'%self.path)
207 207
208 208 return
209 209
210 210
211 211 def __selDates(self, amisr_dirname_format):
212 212 try:
213 213 year = int(amisr_dirname_format[0:4])
214 214 month = int(amisr_dirname_format[4:6])
215 215 dom = int(amisr_dirname_format[6:8])
216 216 thisDate = datetime.date(year,month,dom)
217 217
218 218 if (thisDate>=self.startDate and thisDate <= self.endDate):
219 219 return amisr_dirname_format
220 220 except:
221 221 return None
222 222
223 223
224 224 def __findDataForDates(self,online=False):
225 225
226 226 if not(self.status):
227 227 return None
228 228
229 229 pat = '\d+.\d+'
230 230 dirnameList = [re.search(pat,x) for x in os.listdir(self.path)]
231 231 dirnameList = [x for x in dirnameList if x!=None]
232 232 dirnameList = [x.string for x in dirnameList]
233 233 if not(online):
234 234 dirnameList = [self.__selDates(x) for x in dirnameList]
235 235 dirnameList = [x for x in dirnameList if x!=None]
236 236 if len(dirnameList)>0:
237 237 self.status = 1
238 238 self.dirnameList = dirnameList
239 239 self.dirnameList.sort()
240 240 else:
241 241 self.status = 0
242 242 return None
243 243
244 244 def __getTimeFromData(self):
245 245 startDateTime_Reader = datetime.datetime.combine(self.startDate,self.startTime)
246 246 endDateTime_Reader = datetime.datetime.combine(self.endDate,self.endTime)
247 247
248 248 print('Filtering Files from %s to %s'%(startDateTime_Reader, endDateTime_Reader))
249 249 print('........................................')
250 250 filter_filenameList = []
251 251 self.filenameList.sort()
252 252 #for i in range(len(self.filenameList)-1):
253 253 for i in range(len(self.filenameList)):
254 254 filename = self.filenameList[i]
255 255 fp = h5py.File(filename,'r')
256 256 time_str = fp.get('Time/RadacTimeString')
257 257
258 258 startDateTimeStr_File = time_str[0][0].decode('UTF-8').split('.')[0]
259 259 #startDateTimeStr_File = "2019-12-16 09:21:11"
260 260 junk = time.strptime(startDateTimeStr_File, '%Y-%m-%d %H:%M:%S')
261 261 startDateTime_File = datetime.datetime(junk.tm_year,junk.tm_mon,junk.tm_mday,junk.tm_hour, junk.tm_min, junk.tm_sec)
262 262
263 263 #endDateTimeStr_File = "2019-12-16 11:10:11"
264 264 endDateTimeStr_File = time_str[-1][-1].decode('UTF-8').split('.')[0]
265 265 junk = time.strptime(endDateTimeStr_File, '%Y-%m-%d %H:%M:%S')
266 266 endDateTime_File = datetime.datetime(junk.tm_year,junk.tm_mon,junk.tm_mday,junk.tm_hour, junk.tm_min, junk.tm_sec)
267 267
268 268 fp.close()
269 269
270 270 #print("check time", startDateTime_File)
271 271 if self.timezone == 'lt':
272 272 startDateTime_File = startDateTime_File - datetime.timedelta(minutes = 300)
273 273 endDateTime_File = endDateTime_File - datetime.timedelta(minutes = 300)
274 274 if (endDateTime_File>=startDateTime_Reader and endDateTime_File<endDateTime_Reader):
275 275 #self.filenameList.remove(filename)
276 276 filter_filenameList.append(filename)
277 277
278 278 if (endDateTime_File>=endDateTime_Reader):
279 279 break
280 280
281 281
282 282 filter_filenameList.sort()
283 283 self.filenameList = filter_filenameList
284 284 return 1
285 285
286 286 def __filterByGlob1(self, dirName):
287 287 filter_files = glob.glob1(dirName, '*.*%s'%self.extension_file)
288 288 filter_files.sort()
289 289 filterDict = {}
290 290 filterDict.setdefault(dirName)
291 291 filterDict[dirName] = filter_files
292 292 return filterDict
293 293
294 294 def __getFilenameList(self, fileListInKeys, dirList):
295 295 for value in fileListInKeys:
296 296 dirName = list(value.keys())[0]
297 297 for file in value[dirName]:
298 298 filename = os.path.join(dirName, file)
299 299 self.filenameList.append(filename)
300 300
301 301
302 302 def __selectDataForTimes(self, online=False):
303 303 #aun no esta implementado el filtro for tiempo
304 304 if not(self.status):
305 305 return None
306 306
307 307 dirList = [os.path.join(self.path,x) for x in self.dirnameList]
308 308
309 309 fileListInKeys = [self.__filterByGlob1(x) for x in dirList]
310 310
311 311 self.__getFilenameList(fileListInKeys, dirList)
312 312 if not(online):
313 313 #filtro por tiempo
314 314 if not(self.all):
315 315 self.__getTimeFromData()
316 316
317 317 if len(self.filenameList)>0:
318 318 self.status = 1
319 319 self.filenameList.sort()
320 320 else:
321 321 self.status = 0
322 322 return None
323 323
324 324 else:
325 325 #get the last file - 1
326 326 self.filenameList = [self.filenameList[-2]]
327
328 327 new_dirnameList = []
329 328 for dirname in self.dirnameList:
330 329 junk = numpy.array([dirname in x for x in self.filenameList])
331 330 junk_sum = junk.sum()
332 331 if junk_sum > 0:
333 332 new_dirnameList.append(dirname)
334 333 self.dirnameList = new_dirnameList
335 334 return 1
336 335
337 336 def searchFilesOnLine(self, path, startDate, endDate, startTime=datetime.time(0,0,0),
338 337 endTime=datetime.time(23,59,59),walk=True):
339 338
340 339 if endDate ==None:
341 340 startDate = datetime.datetime.utcnow().date()
342 341 endDate = datetime.datetime.utcnow().date()
343 342
344 343 self.__setParameters(path=path, startDate=startDate, endDate=endDate,startTime = startTime,endTime=endTime, walk=walk)
345 344
346 345 self.__checkPath()
347 346
348 347 self.__findDataForDates(online=True)
349 348
350 349 self.dirnameList = [self.dirnameList[-1]]
351 350
352 351 self.__selectDataForTimes(online=True)
353 352
354 353 return
355 354
356 355
357 356 def searchFilesOffLine(self,
358 357 path,
359 358 startDate,
360 359 endDate,
361 360 startTime=datetime.time(0,0,0),
362 361 endTime=datetime.time(23,59,59),
363 362 walk=True):
364 363
365 364 self.__setParameters(path, startDate, endDate, startTime, endTime, walk)
366 365
367 366 self.__checkPath()
368 367
369 368 self.__findDataForDates()
370 369
371 370 self.__selectDataForTimes()
372 371
373 372 for i in range(len(self.filenameList)):
374 373 print("%s" %(self.filenameList[i]))
375 374
376 375 return
377 376
378 377 def __setNextFileOffline(self):
379 378 idFile = self.fileIndex
380 379
381 380 while (True):
382 381 idFile += 1
383 382 if not(idFile < len(self.filenameList)):
384 383 self.flagNoMoreFiles = 1
385 384 print("No more Files")
386 self.dataOut.error = True
387 385 return 0
388 386
389 387 filename = self.filenameList[idFile]
390 388
391 389 amisrFilePointer = h5py.File(filename,'r')
392 390
393 391 break
394 392
395 393 self.flagIsNewFile = 1
396 394 self.fileIndex = idFile
397 395 self.filename = filename
398 396
399 397 self.amisrFilePointer = amisrFilePointer
400 398
401 399 print("Setting the file: %s"%self.filename)
402 400
403 401 return 1
404 402
405 403
406 404 def __setNextFileOnline(self):
407 405 filename = self.filenameList[0]
408 406 if self.__filename_online != None:
409 407 self.__selectDataForTimes(online=True)
410 408 filename = self.filenameList[0]
411 409 wait = 0
410 #self.__waitForNewFile=5 ## DEBUG:
412 411 while self.__filename_online == filename:
413 412 print('waiting %d seconds to get a new file...'%(self.__waitForNewFile))
414 413 if wait == 5:
414 self.flagNoMoreFiles = 1
415 415 return 0
416 416 sleep(self.__waitForNewFile)
417 417 self.__selectDataForTimes(online=True)
418 418 filename = self.filenameList[0]
419 419 wait += 1
420 420
421 421 self.__filename_online = filename
422 422
423 423 self.amisrFilePointer = h5py.File(filename,'r')
424 424 self.flagIsNewFile = 1
425 425 self.filename = filename
426 426 print("Setting the file: %s"%self.filename)
427 427 return 1
428 428
429 429
430 430 def readData(self):
431 431 buffer = self.amisrFilePointer.get('Raw11/Data/Samples/Data')
432 432 re = buffer[:,:,:,0]
433 433 im = buffer[:,:,:,1]
434 434 dataset = re + im*1j
435 435
436 436 self.radacTime = self.amisrFilePointer.get('Raw11/Data/RadacHeader/RadacTime')
437 437 timeset = self.radacTime[:,0]
438 438
439 439 return dataset,timeset
440 440
441 441 def reshapeData(self):
442 442 #self.beamCodeByPulse, self.beamCode, self.nblocks, self.nprofiles, self.nsa,
443 443 channels = self.beamCodeByPulse[0,:]
444 444 nchan = self.nchannels
445 445 #self.newProfiles = self.nprofiles/nchan #must be defined on filljroheader
446 446 nblocks = self.nblocks
447 447 nsamples = self.nsa
448 448
449 449 #Dimensions : nChannels, nProfiles, nSamples
450 450 new_block = numpy.empty((nblocks, nchan, numpy.int_(self.newProfiles), nsamples), dtype="complex64")
451 451 ############################################
452 452
453 453 for thisChannel in range(nchan):
454 454 new_block[:,thisChannel,:,:] = self.dataset[:,numpy.where(channels==self.beamCode[0][thisChannel])[0],:]
455 455
456 456
457 457 new_block = numpy.transpose(new_block, (1,0,2,3))
458 458 new_block = numpy.reshape(new_block, (nchan,-1, nsamples))
459 459
460 460 return new_block
461 461
462 462 def updateIndexes(self):
463 463
464 464 pass
465 465
466 466 def fillJROHeader(self):
467 467
468 468 #fill radar controller header
469 469 self.dataOut.radarControllerHeaderObj = RadarControllerHeader(ipp=self.__ippKm,
470 470 txA=self.__txA,
471 471 txB=0,
472 472 nWindows=1,
473 473 nHeights=self.__nSamples,
474 474 firstHeight=self.__firstHeight,
475 475 deltaHeight=self.__deltaHeight,
476 476 codeType=self.__codeType,
477 477 nCode=self.__nCode, nBaud=self.__nBaud,
478 478 code = self.__code,
479 479 fClock=1)
480 480
481 481 #fill system header
482 482 self.dataOut.systemHeaderObj = SystemHeader(nSamples=self.__nSamples,
483 483 nProfiles=self.newProfiles,
484 484 nChannels=len(self.__channelList),
485 485 adcResolution=14,
486 486 pciDioBusWidth=32)
487 487
488 488 self.dataOut.type = "Voltage"
489 489
490 490 self.dataOut.data = None
491 491
492 492 self.dataOut.dtype = numpy.dtype([('real','<i8'),('imag','<i8')])
493 493
494 494 # self.dataOut.nChannels = 0
495 495
496 496 # self.dataOut.nHeights = 0
497 497
498 498 self.dataOut.nProfiles = self.newProfiles*self.nblocks
499 499
500 500 #self.dataOut.heightList = self.__firstHeigth + numpy.arange(self.__nSamples, dtype = numpy.float)*self.__deltaHeigth
501 501 ranges = numpy.reshape(self.rangeFromFile.value,(-1))
502 502 self.dataOut.heightList = ranges/1000.0 #km
503 503
504 504
505 505 self.dataOut.channelList = self.__channelList
506 506
507 507 self.dataOut.blocksize = self.dataOut.getNChannels() * self.dataOut.getNHeights()
508 508
509 509 # self.dataOut.channelIndexList = None
510 510
511 511 self.dataOut.flagNoData = True
512 512
513 513 #Set to TRUE if the data is discontinuous
514 514 self.dataOut.flagDiscontinuousBlock = False
515 515
516 516 self.dataOut.utctime = None
517 517
518 518 #self.dataOut.timeZone = -5 #self.__timezone/60 #timezone like jroheader, difference in minutes between UTC and localtime
519 519 if self.timezone == 'lt':
520 520 self.dataOut.timeZone = time.timezone / 60. #get the timezone in minutes
521 521 else:
522 522 self.dataOut.timeZone = 0 #by default time is UTC
523 523
524 524 self.dataOut.dstFlag = 0
525 525
526 526 self.dataOut.errorCount = 0
527 527
528 528 self.dataOut.nCohInt = 1
529 529
530 530 self.dataOut.flagDecodeData = False #asumo que la data esta decodificada
531 531
532 532 self.dataOut.flagDeflipData = False #asumo que la data esta sin flip
533 533
534 534 self.dataOut.flagShiftFFT = False
535 535
536 536 self.dataOut.ippSeconds = self.ippSeconds
537 537
538 538 #Time interval between profiles
539 539 #self.dataOut.timeInterval = self.dataOut.ippSeconds * self.dataOut.nCohInt
540 540
541 541 self.dataOut.frequency = self.__frequency
542 542 self.dataOut.realtime = self.online
543 543 pass
544 544
545 545 def readNextFile(self,online=False):
546 546
547 547 if not(online):
548 548 newFile = self.__setNextFileOffline()
549 549 else:
550 550 newFile = self.__setNextFileOnline()
551 551
552 552 if not(newFile):
553 self.dataOut.error = True
553 554 return 0
554 555 #if self.__firstFile:
555 556 self.readAMISRHeader(self.amisrFilePointer)
556 557
557 558 self.createBuffers()
558 559
559 560 self.fillJROHeader()
560 561
561 562 #self.__firstFile = False
562 563
563 564
564 565
565 566 self.dataset,self.timeset = self.readData()
566 567
567 568 if self.endDate!=None:
568 569 endDateTime_Reader = datetime.datetime.combine(self.endDate,self.endTime)
569 570 time_str = self.amisrFilePointer.get('Time/RadacTimeString')
570 571 startDateTimeStr_File = time_str[0][0].decode('UTF-8').split('.')[0]
571 572 junk = time.strptime(startDateTimeStr_File, '%Y-%m-%d %H:%M:%S')
572 573 startDateTime_File = datetime.datetime(junk.tm_year,junk.tm_mon,junk.tm_mday,junk.tm_hour, junk.tm_min, junk.tm_sec)
573 574 if self.timezone == 'lt':
574 575 startDateTime_File = startDateTime_File - datetime.timedelta(minutes = 300)
575 576 if (startDateTime_File>endDateTime_Reader):
576 577 return 0
577 578
578 579 self.jrodataset = self.reshapeData()
579 580 #----self.updateIndexes()
580 581 self.profileIndex = 0
581 582
582 583 return 1
583 584
584 585
585 586 def __hasNotDataInBuffer(self):
586 587 if self.profileIndex >= (self.newProfiles*self.nblocks):
587 588 return 1
588 589 return 0
589 590
590 591
591 592 def getData(self):
592 593
593 594 if self.flagNoMoreFiles:
594 595 self.dataOut.flagNoData = True
595 596 return 0
596 597
597 598 if self.__hasNotDataInBuffer():
598 599 if not (self.readNextFile(self.online)):
599 600 return 0
600 601
601 602
602 603 if self.dataset is None: # setear esta condicion cuando no hayan datos por leer
603 604 self.dataOut.flagNoData = True
604 605 return 0
605 606
606 607 #self.dataOut.data = numpy.reshape(self.jrodataset[self.profileIndex,:],(1,-1))
607 608
608 609 self.dataOut.data = self.jrodataset[:,self.profileIndex,:]
609 610
610 611 #print("R_t",self.timeset)
611 612
612 613 #self.dataOut.utctime = self.jrotimeset[self.profileIndex]
613 614 #verificar basic header de jro data y ver si es compatible con este valor
614 615 #self.dataOut.utctime = self.timeset + (self.profileIndex * self.ippSeconds * self.nchannels)
615 616 indexprof = numpy.mod(self.profileIndex, self.newProfiles)
616 617 indexblock = self.profileIndex/self.newProfiles
617 618 #print (indexblock, indexprof)
618 619 diffUTC = 1.8e4 #UTC diference from peru in seconds --Joab
619 620 diffUTC = 0
620 621 t_comp = (indexprof * self.ippSeconds * self.nchannels) + diffUTC #
621 622 #cambio posible 18/02/2020
622 623
623 624
624 625
625 626 #print("utc :",indexblock," __ ",t_comp)
626 627 #print(numpy.shape(self.timeset))
627 628 self.dataOut.utctime = self.timeset[numpy.int_(indexblock)] + t_comp
628 629 #self.dataOut.utctime = self.timeset[self.profileIndex] + t_comp
629 630 #print(self.dataOut.utctime)
630 631 self.dataOut.profileIndex = self.profileIndex
631 632 self.dataOut.flagNoData = False
632 633 # if indexprof == 0:
633 634 # print self.dataOut.utctime
634 635
635 636 self.profileIndex += 1
636 637
637 638 return self.dataOut.data
638 639
639 640
640 641 def run(self, **kwargs):
641 642 '''
642 643 This method will be called many times so here you should put all your code
643 644 '''
644 645
645 646 if not self.isConfig:
646 647 self.setup(**kwargs)
647 648 self.isConfig = True
648 649
649 650 self.getData()
General Comments 0
You need to be logged in to leave comments. Login now