ATLAS Offline Software
TileCalibTools.py
Go to the documentation of this file.
1 #!/bin/env python
2 
3 # Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
4 # TileCalibTools.py
5 # Nils Gollub <nils.gollub@cern.ch>, 2007-11-23
6 #
7 # Carlos Solans <carlos.solans@cern.ch>, 2012-10-19
8 # Andrey Kamenshchikov <akamensh@cern.ch>, 2013-10-23
9 # Yuri Smirnov <iouri.smirnov@cern.ch>, 2014-12-24
10 
11 """
12 Python helper module for managing COOL DB connections and TileCalibBlobs.
13 """
14 
15 import cx_Oracle # noqa: F401
16 from PyCool import cool
17 import datetime, time, re, os
18 try:
19  # For Python 3.0 and later
20  from urllib.request import urlopen
21 except ImportError:
22  # Fall back to Python 2's urllib2
23  from urllib2 import urlopen
24 import cppyy
25 
26 from TileCalibBlobObjs.Classes import TileCalibUtils, TileCalibDrawerCmt, \
27  TileCalibDrawerInt, TileCalibDrawerOfc, TileCalibDrawerBch, \
28  TileCalibDrawerFlt, TileCalibType
29 
30 #=== get a logger
31 from TileCalibBlobPython.TileCalibLogger import TileCalibLogger, getLogger
32 log = getLogger("TileCalibTools")
33 
34 
35 #======================================================================
36 #===
37 #=== Global helper functions
38 #===
39 #======================================================================
40 
41 #
42 #______________________________________________________________________
43 #=== useful constants
44 MINRUN = 0
45 MINLBK = 0
46 MAXRUN = cool.Int32Max
47 MAXLBK = cool.UInt32Max
48 UNIX2COOL = 1000000000
49 UNIXTMAX = cool.Int32Max
50 # empty Tile channel for storing laser partition variation. DO NOT CHANGE.
51 LASPARTCHAN = 43
52 
53 #
54 #______________________________________________________________________
56  """
57  Return the run number of next run to be taken in the pit
58  """
59 
60  urls = ["http://atlas-service-db-runlist.web.cern.ch/atlas-service-db-runlist/cgi-bin/latestRun.py",
61  "http://pcata007.cern.ch/cgi-bin/getLastRunNumber.py",
62  "http://pcata007.cern.ch/latestRun"]
63 
64  run=0
65  for url in urls:
66  try:
67  for line in urlopen(url).readlines():
68  r=line.strip()
69  if r.isdigit():
70  run=int(r)
71  break
72  if run>0:
73  break
74  except Exception:
75  continue
76 
77  return max(run+1,222222)
78 
79 #
80 #______________________________________________________________________
82  """
83  Return the minimal run number of runs in prompt calibration loop
84  """
85 
86  promptCalibRuns = []
87 
88  try:
89  fin = open("/afs/cern.ch/user/a/atlcond/scratch0/nemo/prod/web/calibruns.txt","r").read().split()
90  for line in fin:
91  try:
92  if line:
93  promptCalibRuns.append( int(line) )
94  except ValueError:
95  pass
96  except Exception:
97  promptCalibRuns=[]
98 
99  if len(promptCalibRuns)==0:
100 
101  urls = ["http://pcata007.cern.ch/cgi-bin/getBulkRunNumber.py",
102  "http://pcata007.cern.ch/latestRun"]
103 
104  run=0
105  for url in urls:
106  try:
107  for line in urlopen(url).readlines():
108  r=line.strip()
109  if r.isdigit():
110  run=int(r)
111  break
112  if run>0:
113  promptCalibRuns=[run]
114  break
115  except Exception:
116  continue
117 
118  if len(promptCalibRuns) >= 1:
119  promptCalibRuns.sort()
120  return promptCalibRuns[0]
121  else:
122  return getLastRunNumber()
123 
124 #
125 #______________________________________________________________________
126 def getAliasFromFile(aliastype='Current'):
127  """
128  Return name of top-level tag for 'Current' or 'CurrentES' or 'Next' or 'NextES' aliases
129  """
130 
131  aliasfolder = '/afs/cern.ch/atlas/conditions/poolcond/buffer/BestKnowledge'
132  try:
133  falias = open('%s/%s' % (aliasfolder, aliastype))
134  alias = falias.readline()
135  falias.close()
136  return alias.replace('\n','').replace('*','')
137  except Exception:
138  import os
139  aliasfolder = os.getcwd()+'/BestKnowledge'
140  print("Looking for %s in %s" % (aliastype,aliasfolder))
141  try:
142  falias = open('%s/%s' % (aliasfolder, aliastype))
143  alias = falias.readline()
144  falias.close()
145  return alias.replace('\n','').replace('*','')
146  except Exception:
147  return aliastype.upper()
148 
149 #
150 #______________________________________________________________________
151 def getTilePrefix(ofl=True,splitOnlInOflSchema=True):
152  """
153  Returns the common Tile prefix used for all COOL folders.
154  ofl = False ... single version folders
155  ofl = True ... multiversion folders
156  splitOnlInOflSchema = False ... offline only folders or
157  splitOnline folders in Online schema
158  splitOnlInOflSchema = True ... splitOnlineFolders in
159  offline schema
160  """
161  if ofl:
162  if splitOnlInOflSchema:
163  return "/TILE/OFL02/"
164  else:
165  return "/TILE/OFL01/"
166  else:
167  return "/TILE/ONL01/"
168 
169 #
170 #______________________________________________________________________
172  """
173  Returns a list of all TILE folder prefixes
174  """
175  return ["/TILE/ONL01/","/TILE/OFL01/","/TILE/OFL02/"]
176 
177 #
178 #______________________________________________________________________
179 def getAthenaFolderDescr(type="run-lumi"):
180  """
181  Returns the run-lumi type folder description needed to read back the folder content
182  as a CondAttrListCollection in Athena.
183  """
184  desc ='<timeStamp>'+type+'</timeStamp>'
185  desc+='<addrHeader><address_header service_type="71" clid="1238547719" /></addrHeader>'
186  desc+='<typeName>CondAttrListCollection</typeName>'
187  return desc
188 
189 #
190 #______________________________________________________________________
191 def getAthenaFolderType(folderDescr):
192  type = re.compile(".*<timeStamp>(.*)</timeStamp>.*").search(folderDescr)
193  if not type:
194  raise Exception("No folder type info found in \'%s\'" % folderDescr)
195  return type.groups()[0]
196 
197 #
198 #______________________________________________________________________
199 def openDb(db, instance, mode="READONLY", schema="COOLOFL_TILE", sqlfn="tileSqlite.db"):
200  """
201  Opens a COOL db connection.
202  - db: The DB type. The following names are recognized:
203  * SQLITE: Opens file mentioned in sqlfn parameter
204  * ORACLE or FRONTIER: Opens ORACLE DB, forces READONLY
205  - instance: One of valid instances - CONDBR2 OFLP200 COMP200 CMCP200
206  - mode: Can be READONLY (default), RECREATE or UPDATE
207  - schema: One of valid schemas - COOLONL_CALO COOLOFL_CALO COOLONL_LAR COOLOFL_LAR COOLONL_TILE COOLOFL_TILE
208  - sqlfn: Name of sqlite file if db is SQLITE
209  """
210  #=== check for valid db names
211  if db is not None:
212  validDb = ["SQLITE", "ORACLE", "FRONTIER"]
213  if db not in validDb:
214  raise Exception( "DB not valid: %s, valid DBs are: %s" % (db,validDb) )
215  elif db == "ORACLE" or db == "FRONTIER":
216  mode = "READONLY"
217 
218  #=== check for valid instance names
219  validInstance = ["COMP200", "CONDBR2", "CMCP200", "OFLP200"]
220  if instance not in validInstance:
221  raise Exception( "Instance not valid: %s, valid instance are: %s" % (instance,validInstance) )
222 
223  #=== check for valid schema names
224  validSchema = ["COOLONL_TILE","COOLOFL_TILE"]
225  if schema not in validSchema:
226  raise Exception( "Schema not valid: %s, valid schemas are: %s" % (schema,validSchema) )
227 
228  #=== construct connection string
229  connStr = ""
230  if db=='SQLITE':
231  if mode=="READONLY" and not os.path.exists(sqlfn):
232  raise Exception( "Sqlite file %s does not exist" % (sqlfn) )
233  if (mode=="RECREATE" or mode=="UPDATE") and not os.path.exists(os.path.dirname(sqlfn)):
234  dirn=os.path.dirname(sqlfn)
235  if dirn:
236  os.makedirs(dirn)
237  connStr="sqlite://X;schema=%s;dbname=%s" % (sqlfn,instance)
238  elif db=='FRONTIER':
239  connStr='frontier://ATLF/()/;schema=ATLAS_%s;dbname=%s' % (schema,instance)
240  elif db=='ORACLE':
241  connStr='oracle://%s;schema=ATLAS_%s;dbname=%s' % ('ATLAS_COOLPROD',schema,instance)
242  else:
243  connStr=schema+'/'+instance
244 
245  return openDbConn(connStr, mode)
246 
247 #
248 #______________________________________________________________________
249 def openDbConn(connStr, mode="READONLY"):
250  """
251  Opens a COOL db connection.
252  - connStr: The DB connection string
253  - mode: Can be READONLY (default), RECREATE or UPDATE
254  or ORACLE or FRONTIER if connStr is only short name of the database
255  """
256 
257  #=== split the name into schema and dbinstance
258  splitname=connStr.split('/')
259  if (len(splitname)!=2): # string connection ready to be used as it is
260  connStr_new=connStr
261  else: # construct connection string
262  schema=splitname[0]
263  instance=splitname[1]
264  if mode=="ORACLE":
265  connStr_new='oracle://%s;schema=ATLAS_%s;dbname=%s' % ('ATLAS_COOLPROD',schema,instance)
266  else:
267  connStr_new='frontier://ATLF/()/;schema=ATLAS_%s;dbname=%s' % (schema,instance)
268 
269  #=== get dbSvc and print header info
270  dbSvc = cool.DatabaseSvcFactory.databaseService()
271  log.info( "---------------------------------------------------------------------------------" )
272  log.info( "-------------------------- TileCalibTools.openDbConn ----------------------------" )
273  log.info( "- using COOL version %s", dbSvc.serviceVersion() )
274  log.info( "- opening TileDb: %s",connStr_new )
275  log.info( "- mode: %s", mode )
276  log.info( "---------------------------------------------------------------------------------" )
277 
278  #=== action depends on mode
279  if mode in ["READONLY","ORACLE","FRONTIER","",None]:
280  #=== open read only
281  try:
282  db=dbSvc.openDatabase(connStr_new,True)
283  except Exception as e:
284  log.debug( e )
285  log.critical("Could not connect to %s" % connStr_new )
286  return None
287  return db
288  elif mode=="RECREATE":
289  #=== recreating database
290  dbSvc.dropDatabase(connStr_new)
291  try:
292  db = dbSvc.createDatabase(connStr_new)
293  except Exception as e:
294  log.debug( e )
295  log.critical( "Could not create database, giving up..." )
296  return None
297  return db
298  elif mode=="UPDATE":
299  #=== update database
300  try:
301  db=dbSvc.openDatabase(connStr_new,False)
302  except Exception as e:
303  log.debug( e )
304  log.warning( "Could not connect to \'%s\', trying to create it....", connStr_new )
305  try:
306  db=dbSvc.createDatabase(connStr_new)
307  except Exception as e:
308  log.debug( e )
309  log.critical( "Could not create database, giving up..." )
310  return None
311  return db
312  else:
313  log.error("Mode \"%s\" not recognized", mode )
314  return None
315 
316 #
317 #______________________________________________________________________
318 def coolTimeFromRunLumi(runNum, lbkNum):
319  """
320  Returns COOL timeStamp build from run and lumi block numbers
321  """
322  return (int(runNum)<<32) + int(lbkNum)
323 
324 #
325 #______________________________________________________________________
326 def decodeTimeString(timeString):
327  """
328  Retruns UNIX time stamp given an input time string
329  """
330  return int(time.mktime(time.strptime(timeString,"%Y-%m-%d %H:%M:%S")))
331 
332 #
333 #______________________________________________________________________
334 def getCoolValidityKey(pointInTime, isSince=True):
335  """
336  The interpretation of pointInTime depends on their type:
337  - tuple(int,int) : run and lbk number
338  - integer : Values are interpreted as unix time stamps
339  - string : time stamp of format 'yyyy-mm-dd hh:mm:ss'
340  """
341 
342  validityKey = None
343 
344  #=== string: convert to unix time and treat as latter
345  if isinstance(pointInTime, str):
346  pointInTime = decodeTimeString(pointInTime)
347 
348  #=== integer: unix time stamp
349  if isinstance(pointInTime, int):
350  if pointInTime >=0:
351  validityKey = pointInTime * UNIX2COOL
352  else:
353  if isSince:
354  validityKey = int(time.time()) * UNIX2COOL
355  else :
356  validityKey = cool.ValidityKeyMax
357  #=== run-lumi tuple
358  elif isinstance(pointInTime, tuple):
359  validityKey = coolTimeFromRunLumi(pointInTime[0],pointInTime[1])
360  #=== catch other types
361  else:
362  raise Exception("Unrecognized pointInTime type=%s" % type(pointInTime))
363  return cool.ValidityKey(validityKey)
364 
365 
366 #
367 #____________________________________________________________________
368 def getFolderTag(db, folderPath, globalTag):
369 
370  tag=""
371  if globalTag.startswith("/") or globalTag.startswith("TileO") or globalTag.startswith("CALO"):
372  tag = globalTag
373  log.warning("Using tag as-is for folder %s", folderPath)
374  elif '/TILE/ONL01' in folderPath:
375  log.info("Using empty tag for single-version folder %s", folderPath)
376  elif globalTag.startswith(" "):
377  log.warning("Using empty tag for folder %s", folderPath)
378  elif globalTag=="":
379  tag = TileCalibUtils.getFullTag(folderPath, globalTag)
380  log.warning("Using tag with empty suffix for folder %s", folderPath)
381  else:
382  if folderPath.startswith('/CALO'):
383  dbname = 'COOLOFL_CALO' if folderPath.startswith('/CALO/Ofl') else 'COOLONL_CALO'
384  else:
385  dbname ='COOLOFL_TILE'
386  schema=dbname+'/CONDBR2'
387  if isinstance(db, str):
388  if 'OFLP200' in db or 'MC' in db:
389  schema=dbname+'/OFLP200'
390  if not globalTag.startswith("OFLCOND"):
391  if globalTag.startswith("RUN"):
392  globalTag='OFLCOND-'+globalTag
393  log.info("Using Simulation global tag \'%s\'", globalTag)
394  elif 'COMP200' in db or 'RUN1' in db:
395  schema=dbname+'/COMP200'
396  if globalTag!='UPD1' and globalTag!='UPD4' and ('UPD1' in globalTag or 'UPD4' in globalTag or 'COND' not in globalTag):
397  log.info("Using suffix \'%s\' as it is", globalTag)
398  else:
399  globalTag='COMCOND-BLKPA-RUN1-06'
400  log.info("Using RUN1 global tag \'%s\'", globalTag)
401  if schema == dbname+'/CONDBR2':
402  if globalTag=='CURRENT' or globalTag=='UPD4' or globalTag=='':
403  globalTag=getAliasFromFile('Current')
404  log.info("Resolved CURRENT globalTag to \'%s\'", globalTag)
405  elif globalTag=='CURRENTES' or globalTag=='UPD1':
406  globalTag=getAliasFromFile('CurrentES')
407  log.info("Resolved CURRENT ES globalTag to \'%s\'", globalTag)
408  elif globalTag=='NEXT':
409  globalTag=getAliasFromFile('Next')
410  log.info("Resolved NEXT globalTag to \'%s\'", globalTag)
411  elif globalTag=='NEXTES':
412  globalTag=getAliasFromFile('NextES')
413  log.info("Resolved NEXT ES globalTag to \'%s\'", globalTag)
414  globalTag=globalTag.replace('*','')
415  if 'UPD1' in globalTag or 'UPD4' in globalTag or 'COND' not in globalTag:
416  tag = TileCalibUtils.getFullTag(folderPath, globalTag)
417  if tag.startswith('Calo'):
418  tag='CALO'+tag[4:]
419  log.info("Resolved localTag \'%s\' to folderTag \'%s\'", globalTag,tag)
420  else:
421  if not isinstance(db, str):
422  try:
423  folder = db.getFolder(folderPath)
424  tag = folder.resolveTag(globalTag)
425  log.info("Resolved globalTag \'%s\' to folderTag \'%s\'", globalTag,tag)
426  schema=""
427  except Exception as e:
428  log.warning(e)
429  log.warning("Using %s to resolve globalTag",schema)
430  if len(schema):
431  dbr = openDbConn(schema,'READONLY')
432  folder = dbr.getFolder(folderPath)
433  tag = folder.resolveTag(globalTag)
434  dbr.closeDatabase()
435  log.info("Resolved globalTag \'%s\' to folderTag \'%s\'", globalTag,tag)
436 
437  return tag
438 
439 #
440 #____________________________________________________________________
442  return (int(iov >> 32), int(iov & 0xFFFFFFFF))
443 
444 #
445 #____________________________________________________________________
446 def copyFolder(dbr, dbw, folder, tagr, tagw, chanNum, pointInTime1, pointInTime2):
447 
448  log.info("Copy channel %i", chanNum)
449 
450  folderR = dbr.getFolder(folder)
451  folderW = dbw.getFolder(folder)
452 
453  chansel = cool.ChannelSelection(chanNum)
454 
455  iov1 = getCoolValidityKey(pointInTime1,False)
456  iov2 = getCoolValidityKey(pointInTime2,False)
457 
458  if tagr=='':
459  multiVersion = False
460  objs = folderR.browseObjects(iov1,iov2,chansel)
461  else:
462  multiVersion = True
463  objs = folderR.browseObjects(iov1,iov2,chansel,tagr)
464  while objs.goToNext():
465  obj=objs.currentRef()
466  sinceCool=obj.since()
467  if sinceCool < iov1:
468  sinceCool = iov1
469  untilCool=obj.until()
470  data=obj.payload()
471  sinceTup = runLumiFromCoolTime(sinceCool)
472  untilTup = runLumiFromCoolTime(untilCool)
473  log.debug("Copy entry: [%i,%i] - [%i,%i]: %s", sinceTup[0],sinceTup[1],untilTup[0],untilTup[1], data)
474  folderW.storeObject(sinceCool, untilCool, data, chanNum, tagw, multiVersion)
475 
476 
477 
478 #======================================================================
479 #===
480 #=== TileBlobWriter
481 #===
482 #======================================================================
483 
484 #
485 #______________________________________________________________________
486 class TileBlobWriter(TileCalibLogger):
487  """
488  TileCalibBlobWriterBase is a helper class, managing the details of
489  COOL interactions for the user of TileCalibBlobs.
490  """
491 
492  #____________________________________________________________________
493  def __init__(self, db, folderPath, calibDrawerType,
494  isMultiVersionFolder=True, isRunLumiTimeStamp=True):
495  """
496  Input:
497  - db : db should be an open database connection
498  - folderPath: full folder path to create or update
499  """
500 
501  #=== initialize base class
502  TileCalibLogger.__init__(self,"TileBlobWriter")
503 
504  #=== store db
505  self.__db = db
506 
507  #=== determine folder mode
508  folderMode = cool.FolderVersioning.MULTI_VERSION
509  if not isMultiVersionFolder:
510  folderMode = cool.FolderVersioning.SINGLE_VERSION
511 
512  #=== determine folder description
513  folderDescr = getAthenaFolderDescr("run-lumi")
514  if not isRunLumiTimeStamp:
515  folderDescr = getAthenaFolderDescr("time")
516 
517  #=== access folder in db
518  try:
519  #=== create folder if it does not exist
520  if self.__db.existsFolder(folderPath):
521  self.__folder = self.__db.getFolder(folderPath)
522  #=== check folder mode
523  modeInCool = self.__folder.versioningMode()
524  if modeInCool!=folderMode:
525  str = "Incompatible folder mode detected, COOL folder has type "
526  if modeInCool==cool.FolderVersioning.MULTI_VERSION:
527  str += "MULTI"
528  else:
529  str += "SINGLE"
530  raise Exception(str)
531  else:
532  #=== create folder if it does not exist
533  payloadSpec = cool.RecordSpecification()
534  payloadSpec.extend( 'TileCalibBlob', cool.StorageType.Blob64k )
535  folderSpec = cool.FolderSpecification(folderMode, payloadSpec)
536  self.__folder = db.createFolder(folderPath, folderSpec, folderDescr, True)
537  except Exception as e:
538  self.log().critical( e )
539  raise
540 
541  #=== initialize channel dictionaries
542  self.__chanDictRecord = {} # <int, cool.Record >
543  self.__chanDictDrawer = {} # <int, TileCalibDrawer>
544 
545  #=== create default vectors based on calibDrawerType
546  self.__calibDrawerType = calibDrawerType
547  if calibDrawerType=='Flt':
548  self.__defVec = cppyy.gbl.std.vector('std::vector<float>')()
549  elif calibDrawerType=='Bch' or calibDrawerType=='Int':
550  self.__defVec = cppyy.gbl.std.vector('std::vector<unsigned int>')()
551  else:
552  raise Exception("Unknown calibDrawerType: %s" % calibDrawerType)
553 
554 
555  #____________________________________________________________________
556  def register(self, since=(MINRUN,MINLBK), until=(MAXRUN,MAXLBK), tag="", option=0):
557  """
558  Registers the folder in the database.
559  - since: lower limit of IOV
560  - until: upper limit of IOV
561  - tag : The cool folder tag to write to
562 
563  The interpretation of the 'since' and 'until' inputs depends on their type:
564  - tuple(int,int) : run and lbk number
565  - integer : Values are interpreted as unix time stamps
566  If since<0, current time is assumed
567  If until<0, infinity is assumed
568  - string : time stamp of format 'yyyy-mm-dd hh:mm:ss'
569  """
570 
571  #=== check for inconsistent input
572  if type(since)!=type(until):
573  raise Exception("Inconsistent types: since=%s, until=%s" % (type(since),type(until)))
574 
575  #=== write to user tag only if multiversion mode
576  userTagOnly = True
577  if self.__folder.versioningMode()==cool.FolderVersioning.SINGLE_VERSION:
578  userTagOnly = False
579  #=== no folder Tag allowed for singleversion
580  if tag!="":
581  self.log().warning( "Trying to store with tag \"%s\" to SINGLE_VERSION folder", tag )
582  self.log().warning( "... resetting tag to \"\"!" )
583  tag=""
584 
585  #=== get IOV limits
586  sinceCool = getCoolValidityKey(since, True )
587  untilCool = getCoolValidityKey(until, False)
588  if untilCool <= sinceCool:
589  raise Exception("Until(%i) <= Since(%i)" % (untilCool,sinceCool))
590 
591  #=== build IOV string
592  iovString = ""
593  if isinstance(since, tuple):
594  iovString = "[%i,%i] - [%i,%i]" % (since[0],since[1],until[0],until[1])
595  else:
596  sinceInfo = time.localtime( sinceCool//UNIX2COOL )
597  untilInfo = time.localtime(min(UNIXTMAX, (untilCool//UNIX2COOL)))
598  untilStr = "<infinity>"
599  if untilCool<cool.ValidityKeyMax:
600  untilStr = time.asctime(untilInfo)
601  if (untilCool//UNIX2COOL)>UNIXTMAX:
602  untilStr = " > "+untilStr
603  iovString = "[%s] - [%s]" % (time.asctime(sinceInfo), untilStr)
604 
605  #=== build tag
606  folderTag=tag
607 
608  #=== print info
609  comment=self.getComment()
610  onlyComment = (option<0)
611  noComment = (comment is None) or (comment == "None") or (comment.startswith("None") and comment.endswith("None")) or (option>0)
612  self.log().info( "Registering folder %s with tag \"%s\"", self.__folder.fullPath(),folderTag)
613  self.log().info( "... with IOV : %s" , iovString )
614  if noComment:
615  if (option<=0):
616  self.log().info( "... WITHOUT comment field" )
617  else:
618  self.log().info( "... with comment field: \"%s\"", self.getComment() )
619 
620  #=== register all channels by increasing channel number
621  if onlyComment:
622  chanList = [1000]
623  else:
624  chanList = sorted(self.__chanDictRecord.keys())
625  cnt=0
626  for chanNum in chanList:
627  if chanNum==1000 and noComment:
628  continue
629  data = self.__chanDictRecord[chanNum]
630  strout = "cool channel=%4i" % chanNum
631  self.log().debug("Registering %s %s", strout, data)
632  channelId = cool.ChannelId(chanNum)
633  self.__folder.storeObject(sinceCool, untilCool, data, channelId, folderTag, userTagOnly)
634  cnt+=1
635  if noComment:
636  self.log().info( "... %d cool channels have been written in total", cnt )
637  elif onlyComment:
638  self.log().info( "... 1 cool channel with comment field has been written" )
639  else:
640  self.log().info( "... %d cool channels have been written in total (including comment field)", cnt )
641 
642  #____________________________________________________________________
643  def setComment(self, author, comment=None):
644  """
645  Sets a general comment in the comment channel.
646  """
647  try:
649  data = self.__chanDictRecord.get(chanNum)
650  if not data:
651  spec = self.__folder.payloadSpecification()
652  data = cool.Record( spec )
653  self.__chanDictRecord[chanNum] = data
654  blob = data['TileCalibBlob']
655  if isinstance(author,tuple) and len(author)==3:
656  tm=time.mktime(datetime.datetime.strptime(author[2], "%a %b %d %H:%M:%S %Y").timetuple())
657  TileCalibDrawerCmt.getInstance(blob,author[0],author[1],int(tm))
658  else:
659  TileCalibDrawerCmt.getInstance(blob,author,comment)
660  except Exception as e:
661  self.log().critical( e )
662 
663  #____________________________________________________________________
664  def getComment(self, split=False):
665  """
666  Returns the general comment (default if none is set)
667  """
668  try:
670  data = self.__chanDictRecord.get(chanNum)
671  if not data:
672  return "<No general comment!>"
673  blob = data['TileCalibBlob']
675  if split:
676  return (cmt.getAuthor(),cmt.getComment(),cmt.getDate())
677  else:
678  return cmt.getFullComment()
679  except Exception as e:
680  self.log().critical( e )
681 
682  #____________________________________________________________________
683  def getDrawer(self, ros, drawer, calibDrawerTemplate=None):
684  """
685  Returns a TileCalibDrawer object of requested type
686  for the given ROS and drawer.
687  """
688  try:
689 
690  #=== check for already initialized calibDrawers
691  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
692  calibDrawer = self.__chanDictDrawer.get(chanNum,None)
693 
694  #=== initialize new calibDrawer if needed
695  if not calibDrawer:
696 
697  #=== create new blob
698  spec = self.__folder.payloadSpecification()
699  data = cool.Record( spec )
700  self.__chanDictRecord[chanNum] = data
701  blob = data['TileCalibBlob']
702 
703  #=== Create calibDrawer based on requested calibDrawerType
704  if self.__calibDrawerType=='Flt':
705  calibDrawer = TileCalibDrawerFlt.getInstance(blob,self.__defVec,0,0)
706  elif self.__calibDrawerType=='Int':
707  calibDrawer = TileCalibDrawerInt.getInstance(blob,self.__defVec,0,0)
708  elif self.__calibDrawerType=='Bch':
709  calibDrawer = TileCalibDrawerBch.getInstance(blob,self.__defVec,0,0)
710  else:
711  raise Exception( "Invalid blob type requested: %s" % type )
712 
713  #=== clone if requested
714  if calibDrawerTemplate:
715  calibDrawer.clone(calibDrawerTemplate)
716 
717  #=== put updated calibDrawer in dictionary and return
718  self.__chanDictDrawer[chanNum] = calibDrawer
719  return calibDrawer
720 
721  except Exception as e:
722  self.log().critical( e )
723  return None
724 
725  #____________________________________________________________________
726  def zeroBlob(self, ros, drawer):
727  """
728  Resets blob size to zero
729  """
730  try:
731  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
732  data = self.__chanDictRecord.get(chanNum)
733  if not data:
734  spec = self.__folder.payloadSpecification()
735  data = cool.Record( spec )
736  self.__chanDictRecord[chanNum] = data
737  blob = data['TileCalibBlob']
738  blob.resize(0)
739  except Exception as e:
740  self.log().critical( e )
741  return None
742 
743 
744 
745 #======================================================================
746 #===
747 #=== TileBlobReader
748 #===
749 #======================================================================
750 
751 #
752 #______________________________________________________________________
753 class TileBlobReader(TileCalibLogger):
754  """
755  TileCalibBlobReader is a helper class, managing the details of COOL interactions for
756  the user of TileCalibBlobs.
757  """
758 
759  #____________________________________________________________________
760  def __init__(self, db, folder, tag=""):
761  """
762  Input:
763  - db : db should be an open database connection
764  - folder: full folder path
765  - tag : The folder tag, e.g. \"000-00\"
766  """
767  #=== initialize base class
768  TileCalibLogger.__init__(self,"TileBlobReader")
769 
770  #=== try to open db
771  try:
772  self.__db = db # CoraCoolDatabase
773  self.__folder = self.__db.getFolder(folder) # CoraCoolFolder
774  except Exception as e:
775  self.log().critical( e )
776  raise
777 
778  #=== determine if "run-lumi" or "time" folder
779  validFolderTypes = ("run-lumi","time")
780  folderDescr = self.__folder.description()
781  self.__folderType = getAthenaFolderType(folderDescr)
782  if self.__folderType not in validFolderTypes:
783  raise Exception("Invalid folder type found: \'%s\'" % self.__folderType)
784 
785  #=== use camelized full folder path only if tag is given
786  self.__tag = tag
787 
788  #=== initialize dictionary to keep reference to DB object of given ros/drawer
789  #=== and timestamp, so they do not go out of scope
790  self.__objDict = {}
791 
792  #____________________________________________________________________
793  def getComment(self, pointInTime, split=False):
794  """
795  Returns the general comment (default if none is set)
796  """
797  validityKey = getCoolValidityKey(pointInTime)
798  try:
800  obj = self.__folder.findObject(validityKey, chanNum, self.__tag)
801  self.log().debug("getComment:Fetching from DB: %s", obj)
802  blob = obj.payload()[0]
804  if split:
805  return (cmt.getAuthor(),cmt.getComment(),cmt.getDate())
806  else:
807  return cmt.getFullComment()
808  except Exception:
809  return "<no comment found>"
810 
811  #____________________________________________________________________
812  def getDefault(self, ros, drawer):
813  """
814  Returns a default drawer number (among first 20 COOL channels) for any drawer in any partition
815  """
816  if ros==0:
817  if drawer<=4 or drawer==12 or drawer>=20:
818  drawer1=0
819  elif drawer<12:
820  drawer1=4
821  else:
822  drawer1=12
823  elif ros==1 or ros==2:
824  drawer1=4
825  elif ros==3:
826  OffsetEBA = [ 0, 0, 0, 0, 0, 0, 3, 2, #// Merged E+1: EBA07; Outer MBTS: EBA08
827  0, 0, 0, 0, 7, 6, 5, 7, #// D+4: EBA13, EBA16; Special D+4: EBA14; Special D+40: EBA15
828  7, 6, 6, 7, 0, 0, 0, 2, #// D+4: EBA17, EBA20; Special D+4: EBA18, EBA19; Outer MBTS: EBA24
829  3, 0, 0, 0, 0, 0, 0, 0, #// Merged E+1: EBA25
830  0, 0, 0, 0, 0, 0, 1, 1, #// Inner MBTS + special C+10: EBA39, EBA40
831  1, 1, 2, 3, 0, 0, 0, 0, #// Inner MBTS + special C+10: EBA41, EBA42; Outer MBTS: EBA43; Merged E+1: EBA44
832  0, 0, 0, 0, 3, 2, 1, 1, #// Merged E+1: EBA53; Outer MBTS: EBA54; Inner MBTS + special C+10: EBA55, EBA56
833  1, 1, 0, 0, 0, 0, 0, 0] #// Inner MBTS + special C+10: EBA57, EBA58
834  drawer1 = 12 + OffsetEBA[drawer]
835  elif ros==4:
836  OffsetEBC = [ 0, 0, 0, 0, 0, 0, 3, 2, #// Merged E-1: EBC07; Outer MBTS: EBC08
837  0, 0, 0, 0, 7, 6, 6, 7, # // D-4: EBC13, EBC16; Special D-4: EBC14, EBC15;
838  7, 5, 6, 7, 0, 0, 0, 2, #// D-4: EBC17, EBC20; Special D-40 EBC18; Special D-4: EBC19; Outer MBTS: EBC24
839  3, 0, 0, 3, 4, 0, 3, 4, #// Merged E-1: EBC25, EBC28, EBC31; E-4': EBC29, EBC32
840  0, 4, 3, 0, 4, 3, 1, 1, #// E-4': EBC34, EBC37; Merged E-1: EBC35, EBC38; Inner MBTS + special C-10: EBC39, EBC40
841  1, 1, 2, 3, 0, 0, 0, 0, #// Inner MBTS + special C-10: EBC41, EBC42; Outer MBTS: EBC43; Merged E-1: EBC44
842  0, 0, 0, 0, 3, 2, 1, 1, #// Merged E-1: EBC53; Outer MBTS: EBC54; Inner MBTS + special C-10: EBC55, EBC56
843  1, 1, 0, 0, 0, 0, 0, 0] #// Inner MBTS + special C-10: EBC57, EBC58
844  drawer1 = 12 + OffsetEBC[drawer]
845  else:
846  drawer1=0
847 
848  return (0,drawer1)
849 
850  #____________________________________________________________________
851  def getDrawer(self, ros, drawer, pointInTime, printError=True, useDefault=True):
852  """
853  Returns a TileCalibDrawer object for the given ROS and drawer.
854  """
855 
856  validityKey = getCoolValidityKey(pointInTime)
857  self.log().debug("Validity key is %s", validityKey)
858  try:
859  calibDrawer = None
860  #=== Have we retrieved data previously?
861  key = (ros,drawer,validityKey)
862  obj = self.__objDict.get(key)
863  #=== ... if not, get it from DB
864  if not obj:
865  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
866  obj = self.__folder.findObject(validityKey, chanNum, self.__tag)
867  self.log().debug("Fetching from DB: %s", obj)
868  blob = obj.payload()[0]
869  self.log().debug("blob size: %d", blob.size())
870  #=== default policy
871  if not useDefault and blob.size()==0:
872  return 0
873  while blob.size()==0:
874  #=== no default at all?
875  if ros==0 and drawer==0:
876  raise Exception('No default available')
877  #=== follow default policy
878  ros,drawer = self.getDefault(ros,drawer)
879  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
880  obj = self.__folder.findObject(validityKey, chanNum, self.__tag)
881  blob = obj.payload()[0]
882  #=== store object in dictionary
883  self.__objDict[key] = obj
884  #=== get blob
885  blob = obj.payload()[0]
886  self.log().debug("blob size: %d", blob.size())
887 
888  #=== create calibDrawer depending on type
889  calibDrawer = TileCalibDrawerCmt.getInstance(blob)
890  typeName = TileCalibType.getClassName(calibDrawer.getObjType())
891  del calibDrawer
892  if typeName=='TileCalibDrawerFlt':
893  calibDrawer = TileCalibDrawerFlt.getInstance(blob)
894  self.log().debug( "typeName = Flt " )
895  elif typeName=='TileCalibDrawerInt':
896  calibDrawer = TileCalibDrawerInt.getInstance(blob)
897  self.log().debug( "typeName = Int " )
898  elif typeName=='TileCalibDrawerBch':
899  calibDrawer = TileCalibDrawerBch.getInstance(blob)
900  self.log().debug( "typeName = Bch " )
901  elif typeName=='TileCalibDrawerOfc':
902  calibDrawer = TileCalibDrawerOfc.getInstance(blob)
903  self.log().debug( "typeName = Ofc " )
904  else:
905  raise Exception( "Invalid blob type requested: %s" % typeName )
906  return calibDrawer
907  except Exception as e:
908  if printError:
909  self.log().error("TileCalibTools.getDrawer(): Fetching of ros=%i, drawer=%i failed with exception %s", ros,drawer,e)
910  return None
911 
912  #____________________________________________________________________
913  def getDefaultDrawer(self, ros, drawer, pointInTime, printError=True):
914  """
915  Returns a TileCalibDrawer object for the given ROS and drawer.
916  """
917 
918  validityKey = getCoolValidityKey(pointInTime)
919  self.log().debug("Validity key is %s", validityKey)
920  try:
921  calibDrawer = None
922  #=== Have we retrieved data previously?
923  key = (ros,drawer,validityKey)
924  obj = self.__objDict.get(key)
925  #=== ... if not, get it from DB
926  if not obj:
927  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
928  obj = self.__folder.findObject(validityKey, chanNum, self.__tag)
929  self.log().debug("Fetching from DB: %s", obj)
930  blob = obj.payload()[0]
931  self.log().debug("blob size: %d", blob.size())
932  #=== default policy
933  while blob.size()==0:
934  #=== no default at all?
935  if ros==0 and drawer==0:
936  raise Exception('No default available')
937  #=== follow default policy
938  ros,drawer = self.getDefault(ros,drawer)
939  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
940  obj = self.__folder.findObject(validityKey, chanNum, self.__tag)
941  blob = obj.payload()[0]
942  #=== store object in dictionary
943  self.__objDict[key] = obj
944  #=== get blob
945  blob = obj.payload()[0]
946  self.log().debug("blob size: %d", blob.size())
947 
948  #=== create calibDrawer depending on type
949  calibDrawer = TileCalibDrawerCmt.getInstance(blob)
950  typeName = TileCalibType.getClassName(calibDrawer.getObjType())
951  del calibDrawer
952  if typeName=='TileCalibDrawerFlt':
953  calibDrawer = TileCalibDrawerFlt.getInstance(blob)
954  self.log().debug( "typeName = Flt " )
955  elif typeName=='TileCalibDrawerInt':
956  calibDrawer = TileCalibDrawerInt.getInstance(blob)
957  self.log().debug( "typeName = Int " )
958  elif typeName=='TileCalibDrawerBch':
959  calibDrawer = TileCalibDrawerBch.getInstance(blob)
960  self.log().debug( "typeName = Bch " )
961  elif typeName=='TileCalibDrawerOfc':
962  calibDrawer = TileCalibDrawerOfc.getInstance(blob)
963  self.log().debug( "typeName = Ofc " )
964  else:
965  raise Exception( "Invalid blob type requested: %s" % typeName )
966  return calibDrawer
967  except Exception as e:
968  if printError:
969  self.log().error("TileCalibTools.getDefaultDrawer(): Fetching of ros=%i, drawer=%i failed with exception %s", ros,drawer,e)
970  return None
971 
972  #____________________________________________________________________
973  def getDBobjsWithinRange(self, ros, drawer, point1inTime=(0,0), point2inTime=(2147483647,4294967295), printError=True):
974  """
975  Returns all DB objects for the given ROS and drawer, within given validity range -- default: [0-Infty)
976  Check getBlobsWithinRange for an example on how to loop over objects and check validity ranges.
977  """
978 
979  validityKey1 = getCoolValidityKey(point1inTime,True)
980  validityKey2 = getCoolValidityKey(point2inTime,False)
981 
982  #print "Validity keys range is %s - %s" % (validityKey1, validityKey2)
983  self.log().debug("Validity key range is %s - %s", validityKey1,validityKey2)
984 
985  objs = None
986  try:
987  dbChanNum = drawer if ros<0 else TileCalibUtils.getDrawerIdx(ros,drawer)
988  dbChanSel = cool.ChannelSelection(dbChanNum)
989  #self.log().debug("Fetching blobs from DB: %s" % obj)
990  objs = self.__folder.browseObjects(validityKey1,validityKey2,dbChanSel,self.__tag)
991  except Exception as e:
992  if printError:
993  self.log().error("TileCalibTools.getDBobjsWithinRange(): Fetching of ros=%i, drawer=%i failed with exception %s", ros,drawer,e)
994 
995  return objs
996 
997  #____________________________________________________________________
998  def getIOVsWithinRange(self, ros, drawer, point1inTime=(0,0), point2inTime=(2147483647,4294967295), printError=True):
999  """
1000  Returns list of IOVS for the given ROS and drawer, within given validity range -- default: [0-Infty)
1001  """
1002  iovs=[]
1003  dbobjs = self.getDBobjsWithinRange(ros,drawer,point1inTime, point2inTime, printError)
1004  if (dbobjs is None):
1005  log.warning( "Warning: can not read IOVs for ros %d drawer %d from input DB file", ros,drawer )
1006  else:
1007  while dbobjs.goToNext():
1008  obj = dbobjs.currentRef()
1009  objsince = obj.since()
1010  sinceRun = objsince >> 32
1011  sinceLum = objsince & 0xFFFFFFFF
1012  since = (sinceRun, sinceLum)
1013  iovs.append(since)
1014  return iovs
1015 
1016  #____________________________________________________________________
1017  def getBlobsWithinRange(self, ros, drawer, point1inTime=(0,0), point2inTime=(2147483647,4294967295)):
1018  """
1019  Returns all blob objects for the given ROS and drawer, within given validity range -- default: [0-Infty)
1020  Note: the blobs don't contain validity range info. Check method getDBobjsWithinRange()
1021  """
1022 
1023  validityKey1 = getCoolValidityKey(point1inTime,True)
1024  validityKey2 = getCoolValidityKey(point2inTime,False)
1025 
1026  print ("Validity keys range is %s - %s" % (validityKey1, validityKey2))
1027  self.log().debug("Validity key range is %s - %s", validityKey1,validityKey2)
1028 
1029  objs = self.getDBobjsWithinRange(self, ros, drawer, point1inTime, point2inTime)
1030 
1031  #-- Loop over objs to extract blobs
1032  blobs = []
1033  calibDrawer = None
1034  while objs.goToNext():
1035  obj=objs.currentRef()
1036  sinceCool=obj.since()
1037  if sinceCool < validityKey1:
1038  sinceCool = validityKey1
1039  untilCool=obj.until()
1040  blob = obj.payload()[0]
1041  print ("[%d,%d)-[%d,%d) - %s" % ((sinceCool>>32),(sinceCool&0xFFFFFFFF),(untilCool>>32),(untilCool&0xFFFFFFFF),blob))
1042  self.log().debug("blob size: %d", blob.size())
1043 
1044  #=== default policy
1045  while blob.size()==0:
1046  #=== no default at all?
1047  if ros==0 and drawer==0:
1048  raise Exception('No default available')
1049  #=== follow default policy
1050  ros,drawer = self.getDefault(ros,drawer)
1051  chanNum = TileCalibUtils.getDrawerIdx(ros,drawer)
1052  obj = self.__folder.findObject(sinceCool, chanNum, self.__tag)
1053  blob = obj.payload()[0]
1054  self.log().debug("blob size: 0 --> default: %d", blob.size())
1055 
1056  #=== store object in dictionary
1057  self.__objDict[sinceCool] = obj
1058 
1059  #=== create calibDrawer depending on type
1060  calibDrawer = TileCalibDrawerCmt.getInstance(blob)
1061  typeName = TileCalibType.getClassName(calibDrawer.getObjType())
1062  del calibDrawer
1063  if typeName=='TileCalibDrawerFlt':
1064  calibDrawer = TileCalibDrawerFlt.getInstance(blob)
1065  self.log().debug( "typeName = Flt " )
1066  elif typeName=='TileCalibDrawerInt':
1067  calibDrawer = TileCalibDrawerInt.getInstance(blob)
1068  self.log().debug( "typeName = Int " )
1069  elif typeName=='TileCalibDrawerBch':
1070  calibDrawer = TileCalibDrawerBch.getInstance(blob)
1071  self.log().debug( "typeName = Bch " )
1072  elif typeName=='TileCalibDrawerOfc':
1073  calibDrawer = TileCalibDrawerOfc.getInstance(blob)
1074  self.log().debug( "typeName = Ofc " )
1075  else:
1076  raise Exception( "Invalid blob type requested: %s" % typeName )
1077 
1078  blobs.append( calibDrawer )
1079 
1080  return blobs
1081 
1082  #____________________________________________________________________
1084  """
1085  Returns true if MultiVersion folder is connected
1086  """
1087  if self.__folder.versioningMode()==cool.FolderVersioning.MULTI_VERSION:
1088  return True
1089  else:
1090  return False
1091 
1092 
1093 #======================================================================
1094 #===
1095 #=== TileASCIIParser
1096 #===
1097 #======================================================================
1098 
1099 #
1100 #______________________________________________________________________
1101 class TileASCIIParser(TileCalibLogger):
1102  """
1103  This is a class capable of parsing TileCal conditions data stored in
1104  ASCII files. Both the single and multi-line formats are supported.
1105  """
1106 
1107  #____________________________________________________________________
1108  def __init__(self, fileName, calibId, isSingleLineFormat=True):
1109  """
1110  Input:
1111  - fileName : input file name
1112  - isSingleLineFormat: if False, multi line format is assumed
1113  """
1114 
1115  TileCalibLogger.__init__(self,"TileASCIIParser")
1116  self.__dataDict = {}
1117  try:
1118  lines = open(fileName,"r").readlines()
1119  except Exception as e:
1120  self.log().error( "TileCalibASCIIParser::ERROR: Problem opening input file:" )
1121  self.log().error( e )
1122  return
1123 
1124  for line in lines:
1125  fields = line.strip().split()
1126  #=== ignore empty and comment lines
1127  if not len(fields) :
1128  continue
1129  if fields[0].startswith("#"):
1130  continue
1131 
1132  #=== read in fields
1133  type = fields[0]
1134  frag = fields[1]
1135  chan = fields[2]
1136  data = fields[3:]
1137  if not isSingleLineFormat:
1138  raise Exception("Multiline format not implemented yet")
1139 
1140  #=== check for correct calibId
1141  if type!=calibId:
1142  raise Exception("%s is not calibId=%s" % (type,calibId))
1143 
1144  #=== decode fragment
1145  if not (frag.startswith('0x') or frag.startswith('-0x') or frag.startswith('h_')):
1146  raise Exception("Misformated fragment %s" % frag)
1147  if frag.startswith('0x') or frag.startswith('-0x'):
1148  frg = int(frag,16)
1149  ros = frg>>8
1150  if frg<0:
1151  mod = (-frg)&255
1152  else:
1153  mod = frg&255
1154  chn = int(chan)
1155  elif frag.startswith('h_'):
1156  part_dict = {'LBA':1,'LBC':2,'EBA':3,'EBC':4}
1157  partname = str(frag[2:5])
1158  ros=part_dict[partname]
1159  mod = int(frag[5:])-1
1160  if (chan.startswith('ch')):
1161  chn = int(chan[2:])
1162  else:
1163  pmt = int (chan)
1164  chn=self.PMT2channel(ros,mod,pmt)
1165 
1166  #=== fill dictionary
1167  dictKey = (ros,mod,chn)
1168  self.__dataDict[dictKey] = data
1169 
1170  #____________________________________________________________________
1171  def getData(self, ros, drawer, channel):
1172  dictKey = (int(ros), int(drawer), int(channel))
1173  data = self.__dataDict.get(dictKey,[])
1174  return data
1175 
1176  #____________________________________________________________________
1177  def getDict(self):
1178  import copy
1179  return copy.deepcopy(self.__dataDict)
1180 
1181  #____________________________________________________________________
1182  def PMT2channel(self,ros,drawer,pmt):
1183  "Reorder the PMTs (SV: how to get that from region.py???)"
1184  "This takes ros [1-4], drawer [0-63], pmt [1-48]"
1185 
1186  PMT2chan_Special={1:0,2:1,3:2,4:3,5:4,6:5,7:6,8:7,9:8,10:9,
1187  11:10,12:11,13:12,14:13,15:14,16:15,17:16,18:17, 19:18, 20:19,
1188  21:20,22:21,23:22,24:23,27:24,26:25,25:26,31:27,32:28,28:29,
1189  33:30,29:31,30:32,36:33,35:34,34:35,44:36,38:37,37:38,43:39,42:40,
1190  41:41,45:42,39:43,40:44,48:45,47:46,46:47}
1191 
1192 
1193  PMT2chan_LB={1:0,2:1,3:2,4:3,5:4,6:5,7:6,8:7,9:8,10:9,
1194  11:10,12:11,13:12,14:13,15:14,16:15,17:16,18:17,19:18,20:19,
1195  21:20,22:21,23:22,24:23,27:24,26:25,25:26,30:27,29:28,28:29,
1196  33:30,32:31,31:32,36:33,35:34,34:35,39:36,38:37,37:38,42:39,41:40,
1197  40:41,45:42,44:43,43:44,48:45,47:46,46:47}
1198 
1199 
1200  PMT2chan_EB={1:0,2:1,3:2,4:3,5:4,6:5,7:6,8:7,9:8,10:9,
1201  11:10,12:11,13:12,14:13,15:14,16:15,17:16,18:17,19:18,20:19,
1202  21:20,22:21,23:22,24:23,25:24,26:25,27:26,28:27,31:28,32:29,
1203  33:30,29:31,30:32,35:33,36:34,34:35,44:36,38:37,37:38,43:39,42:40,
1204  41:41,39:42,40:43,45:44,46:45,47:46,48:47}
1205 
1206  if ros <= 2:
1207  chan = PMT2chan_LB[pmt]
1208  elif (ros == 3 and drawer == 14) or (ros == 4 and drawer == 17):
1209  chan = PMT2chan_Special[pmt]
1210  else:
1211  chan = PMT2chan_EB[pmt]
1212 
1213  return chan
1214 
1215 #======================================================================
1216 #===
1217 #=== TileASCIIParser2
1218 #===
1219 #======================================================================
1220 
1221 #
1222 #______________________________________________________________________
1223 class TileASCIIParser2(TileCalibLogger):
1224  """
1225  This is a class capable of parsing TileCal conditions data stored in
1226  ASCII files. This version of parser can be used when mutiple IOVs are
1227  given in the file. First column is (run,lumi) pair in this case
1228  """
1229 
1230  #____________________________________________________________________
1231  def __init__(self, fileName, calibId="", readGain=True):
1232  """
1233  Input:
1234  - fileName : input file name
1235  - calibId : like Ped, Las, ... or (r,l) or (run,lumi) but can be empty string as well
1236  - readGain : if False, no gain field in input file
1237  """
1238 
1239  TileCalibLogger.__init__(self,"TileASCIIParser2")
1240  self.__dataDict = {}
1241  self.__manyIOVs = (calibId=="(run,lumi)" or calibId=="(r,l)" )
1242  self.__readGain = readGain
1243  iov=(0,0)
1244  gain=-1
1245 
1246  try:
1247  lines = open(fileName,"r").readlines()
1248  except Exception as e:
1249  self.log().error( "TileCalibASCIIParser2::ERROR: Problem opening input file:" )
1250  self.log().error( e )
1251  return
1252 
1253  self.log().info("Parsing file %s",fileName)
1254  if len(calibId)>0:
1255  self.log().info("Looking for prefix %s",calibId)
1256 
1257  for line in lines:
1258  fields = line.strip().split()
1259  #=== ignore empty and comment lines
1260  if not len(fields) :
1261  continue
1262  if fields[0].startswith("#"):
1263  continue
1264 
1265  #=== read in fields
1266  if len(calibId)>0:
1267  pref = fields[0]
1268  frag = fields[1]
1269  chan = fields[2]
1270  if str(chan)[0:2].lower() == "pm":
1271  chan = self.PMT2channel(frag,fields.pop(2))
1272  if readGain:
1273  gain = fields[3]
1274  data = fields[4:]
1275  else:
1276  data = fields[3:]
1277 
1278  #=== check for correct calibId
1279  if self.__manyIOVs:
1280  iov=tuple(int(i) for i in pref[1:-1].split(","))
1281  if len(iov)!=2 or pref[0]!="(" or pref[-1]!=")":
1282  raise Exception("%s is not %s IOV" % (pref,calibId))
1283  elif pref!=calibId:
1284  raise Exception("%s is not calibId=%s" % (pref,calibId))
1285  else:
1286  frag = fields[0]
1287  chan = fields[1]
1288  if str(chan)[0:2].lower() == "pm":
1289  chan = self.PMT2channel(frag,fields.pop(2))
1290  if readGain:
1291  gain = fields[2]
1292  data = fields[3:]
1293  else:
1294  data = fields[2:]
1295 
1296  #=== decode fragment
1297  if frag.startswith('0x') or frag.startswith('-0x'):
1298  frg = int(frag,16)
1299  ros = frg>>8
1300  if frg<0:
1301  mod = (-frg)&255
1302  else:
1303  mod = frg&255
1304  elif (frag.startswith("AUX") or
1305  frag.startswith("LBA") or
1306  frag.startswith("LBC") or
1307  frag.startswith("EBA") or
1308  frag.startswith("EBC") or
1309  frag.startswith("ALL") or
1310  frag.startswith("XXX") ):
1311  part_dict = {'AUX':0,'LBA':1,'LBC':2,'EBA':3,'EBC':4,'ALL':5,'XXX':-1}
1312  partname = str(frag[0:3])
1313  ros=part_dict[partname]
1314  mod = int(frag[3:])-1
1315  else:
1316  raise Exception("Unknown fragment %s" % frag)
1317 
1318  chn = int(chan)
1319  adc = int(gain)
1320 
1321  #=== fill dictionary
1322  if ros<0:
1323  rosmin=0
1324  rosmax=5
1325  elif ros>=5:
1326  rosmin=1
1327  rosmax=5
1328  else:
1329  rosmin=ros
1330  rosmax=ros+1
1331 
1332  if mod<0 or mod>=64:
1333  modmin=0
1334  modmax=64
1335  else:
1336  modmin=mod
1337  modmax=mod+1
1338 
1339  allchannels=True
1340  if chn<-2:
1341  chnmin=0
1342  chnmax=-chn
1343  elif chn<0:
1344  chnmin=0
1345  chnmax=48
1346  allchannels=(chn==-1) # if chn=-2 only connected channels will be updated
1347  else:
1348  chnmin=chn
1349  chnmax=chn+1
1350 
1351  if adc<-1:
1352  adcmin=0
1353  adcmax=-adc
1354  elif adc<0:
1355  adcmin=0
1356  adcmax=2
1357  else:
1358  adcmin=adc
1359  adcmax=adc+1
1360 
1361  for ros in range(rosmin,rosmax):
1362  for mod in range(modmin,modmax):
1363  for chn in range(chnmin,chnmax):
1364  if allchannels or self.channel2PMT(ros,mod,chn)>0:
1365  for adc in range (adcmin,adcmax):
1366  dictKey = (ros,mod,chn,adc)
1367  if self.__manyIOVs:
1368  if dictKey in self.__dataDict:
1369  self.__dataDict[dictKey] += [(iov,data)]
1370  else:
1371  self.__dataDict[dictKey] = [(iov,data)]
1372  else:
1373  self.__dataDict[dictKey] = data
1374 
1375  #____________________________________________________________________
1376  def getData(self, ros, drawer, channel, adc, iov=(MAXRUN,MAXLBK)):
1377  dictKey = (int(ros), int(drawer), int(channel), int(adc))
1378  data = self.__dataDict.get(dictKey,[])
1379  if self.__manyIOVs and len(data)>0:
1380  before= [i for i in sorted(data) if i[0] <= iov ]
1381  if len(before)>0:
1382  data = before[-1][1]
1383  else:
1384  data = []
1385  return data
1386 
1387  #____________________________________________________________________
1388  def getDict(self):
1389  import copy
1390  return copy.deepcopy(self.__dataDict)
1391 
1392  #____________________________________________________________________
1393  def channel2PMT(self,ros,drawer,chan):
1394  "Convert channel numbet to PMT number, negative for disconnected channels"
1395  "This takes ros [1-4], drawer [0-63], chan [0-47]"
1396 
1397  chan2PMT_LB=[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1398  13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1399  27, 26, 25, 30, 29, 28,-33,-32, 31, 36, 35, 34,
1400  39, 38, 37, 42, 41, 40, 45,-44, 43, 48, 47, 46 ]
1401 
1402  chan2PMT_EB=[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1403  13, 14, 15, 16, 17, 18,-19,-20, 21, 22, 23, 24,
1404  -27,-26,-25,-31,-32,-28, 33, 29, 30,-36,-35, 34,
1405  44, 38, 37, 43, 42, 41,-45,-39,-40,-48,-47,-46 ]
1406 
1407  chan2PMT_Sp=[ -1, -2, -3, -4, 5, 6, 7, 8, 9, 10, 11, 12, # first 4 do not exist
1408  13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, # PMT 19 and 20 exist
1409  -27,-26,-25,-31,-32,-28, 33, 29, 30,-36,-35, 34,
1410  44, 38, 37, 43, 42, 41,-45,-39,-40,-48,-47,-46 ]
1411 
1412  if ros <= 2:
1413  pmt = chan2PMT_LB[chan]
1414  elif (ros == 3 and drawer == 14) or (ros == 4 and drawer == 17):
1415  pmt = chan2PMT_Sp[chan]
1416  else:
1417  pmt = chan2PMT_EB[chan]
1418 
1419  return pmt
1420 
1421  #____________________________________________________________________
1422  def PMT2channel(self,partition,pmt):
1423  "Convert PMT number to channel numbet"
1424  "This takes partition (LBA,LBC,EBA,EBC) and pmt [1-48]"
1425 
1426  chan2PMT_LB=[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1427  13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1428  27, 26, 25, 30, 29, 28, 33, 32, 31, 36, 35, 34,
1429  39, 38, 37, 42, 41, 40, 45, 44, 43, 48, 47, 46 ]
1430 
1431  chan2PMT_EB=[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
1432  13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
1433  27, 26, 25, 31, 32, 28, 33, 29, 30, 36, 35, 34,
1434  44, 38, 37, 43, 42, 41, 45, 39, 40, 48, 47, 46 ]
1435 
1436  chan = -1
1437  pm=abs(int(pmt))
1438 
1439  if pm>0 and pm<=48:
1440  if str(partition)[0].upper() == "E":
1441  chan = chan2PMT_EB.index(pm)
1442  else:
1443  chan = chan2PMT_LB.index(pm)
1444 
1445  return chan
1446 
1447 #======================================================================
1448 #===
1449 #=== TileASCIIParser3
1450 #===
1451 #======================================================================
1452 
1453 #______________________________________________________________________
1454 class TileASCIIParser3(TileCalibLogger):
1455  """
1456  This is a class capable of parsing TileCal conditions data stored in
1457  ASCII files.
1458  """
1459 
1460  #____________________________________________________________________
1461  def __init__(self, fileName, calibId):
1462  """
1463  Input:
1464  - fileName : input file name
1465  - calibId : like Trip, ...
1466  """
1467 
1468  TileCalibLogger.__init__(self,"TileASCIIParser3")
1469  self.__dataDict = {}
1470  try:
1471  lines = open(fileName,"r").readlines()
1472  except Exception as e:
1473  self.log().error( "TileCalibASCIIParser3::ERROR: Problem opening input file:" )
1474  self.log().error( e )
1475  return
1476 
1477  for line in lines:
1478  fields = line.strip().split()
1479  #=== ignore empty and comment lines
1480  if not len(fields) :
1481  continue
1482  if fields[0].startswith("#"):
1483  continue
1484 
1485  #=== read in fields
1486  type = fields[0]
1487  frag = fields[1]
1488  data = fields[2:]
1489 
1490  #=== check for correct calibId
1491  if type != calibId:
1492  raise Exception("%s is not calibId=%s" % (type, calibId))
1493 
1494  #=== decode fragment
1495  if not (frag.startswith('0x') or frag.startswith('-0x')):
1496  raise Exception("Misformated fragment %s" % frag)
1497 
1498  frg = int(frag,16)
1499  ros = frg>>8
1500  if frg<0:
1501  mod = (-frg)&255
1502  else:
1503  mod = frg&255
1504 
1505  #=== fill dictionary
1506  dictKey = (ros, mod)
1507  self.__dataDict[dictKey] = data
1508 
1509  #____________________________________________________________________
1510  def getData(self, ros, drawer):
1511  dictKey = (int(ros), int(drawer))
1512  data = self.__dataDict.get(dictKey,[])
1513  return data
1514 
1515  #____________________________________________________________________
1516  def getDict(self):
1517  import copy
1518  return copy.deepcopy(self.__dataDict)
read
IovVectorMap_t read(const Folder &theFolder, const SelectionCriterion &choice, const unsigned int limit=10)
Definition: openCoraCool.cxx:569
python.TileCalibTools.TileASCIIParser2
Definition: TileCalibTools.py:1223
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename R::value_type > sorted(const R &r, PROJ proj={})
Helper function to create a sorted vector from an unsorted range.
replace
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition: hcg.cxx:307
python.TileCalibTools.TileASCIIParser3.getDict
def getDict(self)
Definition: TileCalibTools.py:1516
python.TileCalibTools.TileBlobReader
Definition: TileCalibTools.py:753
python.TileCalibTools.TileASCIIParser.getDict
def getDict(self)
Definition: TileCalibTools.py:1177
python.TileCalibTools.TileASCIIParser2.__dataDict
__dataDict
Definition: TileCalibTools.py:1240
python.TileCalibTools.TileASCIIParser
Definition: TileCalibTools.py:1101
python.TileCalibTools.openDb
def openDb(db, instance, mode="READONLY", schema="COOLOFL_TILE", sqlfn="tileSqlite.db")
Definition: TileCalibTools.py:199
TileCalibDrawerBch::getInstance
static const TileCalibDrawerBch * getInstance(const coral::Blob &blob)
Returns a pointer to a const TileCalibDrawerBch.
Definition: TileCalibDrawerBch.cxx:28
python.TileCalibTools.TileASCIIParser.getData
def getData(self, ros, drawer, channel)
Definition: TileCalibTools.py:1171
python.TileCalibTools.getPromptCalibRunNumber
def getPromptCalibRunNumber()
Definition: TileCalibTools.py:81
max
constexpr double max()
Definition: ap_fixedTest.cxx:33
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
CscCalibQuery.fullPath
string fullPath
Definition: CscCalibQuery.py:359
python.TileCalibTools.TileBlobReader.__objDict
__objDict
Definition: TileCalibTools.py:790
python.TileCalibTools.decodeTimeString
def decodeTimeString(timeString)
Definition: TileCalibTools.py:326
python.TileCalibTools.getLastRunNumber
def getLastRunNumber()
Definition: TileCalibTools.py:55
python.TileCalibTools.TileASCIIParser3.__init__
def __init__(self, fileName, calibId)
Definition: TileCalibTools.py:1461
python.TileCalibTools.TileASCIIParser2.channel2PMT
def channel2PMT(self, ros, drawer, chan)
Definition: TileCalibTools.py:1393
python.TileCalibTools.TileBlobReader.folderIsMultiVersion
def folderIsMultiVersion(self)
Definition: TileCalibTools.py:1083
TileCalibType::getClassName
static std::string getClassName(TileCalibType::TYPE type)
Returns the class name.
Definition: TileCalibType.cxx:10
upper
int upper(int c)
Definition: LArBadChannelParser.cxx:49
python.TileCalibTools.getTilePrefixes
def getTilePrefixes()
Definition: TileCalibTools.py:171
python.TileCalibTools.TileBlobWriter.register
def register(self, since=(MINRUN, MINLBK), until=(MAXRUN, MAXLBK), tag="", option=0)
Definition: TileCalibTools.py:556
python.TileCalibTools.openDbConn
def openDbConn(connStr, mode="READONLY")
Definition: TileCalibTools.py:249
python.TileCalibTools.TileASCIIParser2.getDict
def getDict(self)
Definition: TileCalibTools.py:1388
python.TileCalibTools.coolTimeFromRunLumi
def coolTimeFromRunLumi(runNum, lbkNum)
Definition: TileCalibTools.py:318
python.TileCalibTools.TileBlobWriter.__chanDictDrawer
__chanDictDrawer
Definition: TileCalibTools.py:542
python.TileCalibTools.runLumiFromCoolTime
def runLumiFromCoolTime(iov)
Definition: TileCalibTools.py:441
search
void search(TDirectory *td, const std::string &s, std::string cwd, node *n)
recursive directory search for TH1 and TH2 and TProfiles
Definition: hcg.cxx:738
python.CaloAddPedShiftConfig.type
type
Definition: CaloAddPedShiftConfig.py:42
python.TileCalibTools.TileASCIIParser3.__dataDict
__dataDict
Definition: TileCalibTools.py:1469
python.TileCalibTools.TileBlobWriter.getDrawer
def getDrawer(self, ros, drawer, calibDrawerTemplate=None)
Definition: TileCalibTools.py:683
python.TileCalibTools.TileBlobReader.getDefaultDrawer
def getDefaultDrawer(self, ros, drawer, pointInTime, printError=True)
Definition: TileCalibTools.py:913
python.TileCalibTools.getTilePrefix
def getTilePrefix(ofl=True, splitOnlInOflSchema=True)
Definition: TileCalibTools.py:151
python.TileCalibTools.TileBlobReader.__tag
__tag
Definition: TileCalibTools.py:786
python.TileCalibTools.TileASCIIParser2.__manyIOVs
__manyIOVs
Definition: TileCalibTools.py:1241
python.TileCalibTools.TileASCIIParser2.getData
def getData(self, ros, drawer, channel, adc, iov=(MAXRUN, MAXLBK))
Definition: TileCalibTools.py:1376
python.TileCalibTools.TileBlobReader.getDBobjsWithinRange
def getDBobjsWithinRange(self, ros, drawer, point1inTime=(0, 0), point2inTime=(2147483647, 4294967295), printError=True)
Definition: TileCalibTools.py:973
python.TileCalibTools.TileBlobWriter
Definition: TileCalibTools.py:486
python.TileCalibTools.copyFolder
def copyFolder(dbr, dbw, folder, tagr, tagw, chanNum, pointInTime1, pointInTime2)
Definition: TileCalibTools.py:446
python.TileCalibTools.TileASCIIParser.PMT2channel
def PMT2channel(self, ros, drawer, pmt)
Definition: TileCalibTools.py:1182
python.TileCalibTools.TileBlobWriter.getComment
def getComment(self, split=False)
Definition: TileCalibTools.py:664
python.TileCalibTools.TileASCIIParser2.__readGain
__readGain
Definition: TileCalibTools.py:1242
python.TileCalibTools.TileASCIIParser.__dataDict
__dataDict
Definition: TileCalibTools.py:1116
python.TileCalibTools.TileBlobWriter.__defVec
__defVec
Definition: TileCalibTools.py:547
TileCalibDrawerInt::getInstance
static const TileCalibDrawerInt * getInstance(const coral::Blob &blob)
Returns a pointer to a const TileCalibDrawerBch.
Definition: TileCalibDrawerInt.cxx:27
TileCalibDrawerFlt::getInstance
static const TileCalibDrawerFlt * getInstance(const coral::Blob &blob)
Returns a pointer to a const TileCalibDrawerFlt.
Definition: TileCalibDrawerFlt.cxx:13
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
python.TileCalibTools.TileBlobReader.__folderType
__folderType
Definition: TileCalibTools.py:781
python.TileCalibTools.TileBlobWriter.__chanDictRecord
__chanDictRecord
Definition: TileCalibTools.py:541
python.TileCalibTools.TileBlobWriter.setComment
def setComment(self, author, comment=None)
Definition: TileCalibTools.py:643
python.TileCalibTools.TileBlobWriter.zeroBlob
def zeroBlob(self, ros, drawer)
Definition: TileCalibTools.py:726
python.TileCalibTools.TileASCIIParser.__init__
def __init__(self, fileName, calibId, isSingleLineFormat=True)
Definition: TileCalibTools.py:1108
python.TileCalibTools.TileBlobReader.getDefault
def getDefault(self, ros, drawer)
Definition: TileCalibTools.py:812
python.TileCalibTools.TileBlobWriter.__init__
def __init__(self, db, folderPath, calibDrawerType, isMultiVersionFolder=True, isRunLumiTimeStamp=True)
Definition: TileCalibTools.py:493
python.TileCalibTools.getAthenaFolderType
def getAthenaFolderType(folderDescr)
Definition: TileCalibTools.py:191
print
void print(char *figname, TCanvas *c1)
Definition: TRTCalib_StrawStatusPlots.cxx:26
python.TileCalibTools.TileBlobReader.getDrawer
def getDrawer(self, ros, drawer, pointInTime, printError=True, useDefault=True)
Definition: TileCalibTools.py:851
python.TileCalibTools.TileASCIIParser2.__init__
def __init__(self, fileName, calibId="", readGain=True)
Definition: TileCalibTools.py:1231
python.TileCalibTools.TileASCIIParser3
Definition: TileCalibTools.py:1454
TileCalibUtils::getCommentChannel
static unsigned int getCommentChannel()
Returns the COOL channel number for the comment channel.
Definition: TileCalibUtils.h:82
python.TileCalibTools.getCoolValidityKey
def getCoolValidityKey(pointInTime, isSince=True)
Definition: TileCalibTools.py:334
debug
const bool debug
Definition: MakeUncertaintyPlots.cxx:53
Trk::open
@ open
Definition: BinningType.h:40
python.TileCalibTools.TileBlobWriter.__calibDrawerType
__calibDrawerType
Definition: TileCalibTools.py:545
python.TileCalibTools.TileBlobReader.__folder
__folder
Definition: TileCalibTools.py:773
python.TileCalibTools.TileBlobReader.getIOVsWithinRange
def getIOVsWithinRange(self, ros, drawer, point1inTime=(0, 0), point2inTime=(2147483647, 4294967295), printError=True)
Definition: TileCalibTools.py:998
python.TileCalibTools.getAthenaFolderDescr
def getAthenaFolderDescr(type="run-lumi")
Definition: TileCalibTools.py:179
TileCalibDrawerOfc::getInstance
static TileCalibDrawerOfc * getInstance(coral::Blob &blob, uint16_t objVersion, uint32_t nSamples, int32_t nPhases, uint16_t nChans, uint16_t nGains, const std::string &author="", const std::string &comment="", uint64_t timeStamp=0)
Returns a pointer to a non-const TileCalibDrawerOfc.
Definition: TileCalibDrawerOfc.cxx:14
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
python.TileCalibTools.TileBlobReader.getBlobsWithinRange
def getBlobsWithinRange(self, ros, drawer, point1inTime=(0, 0), point2inTime=(2147483647, 4294967295))
Definition: TileCalibTools.py:1017
TileCalibDrawerCmt::getInstance
static const TileCalibDrawerCmt * getInstance(const coral::Blob &blob)
Returns a pointer to a const TileCalibDrawerCmt.
Definition: TileCalibDrawerCmt.cxx:24
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:127
python.TileCalibTools.TileBlobWriter.__db
__db
Definition: TileCalibTools.py:504
python.TileCalibTools.TileASCIIParser3.getData
def getData(self, ros, drawer)
Definition: TileCalibTools.py:1510
python.TileCalibTools.getFolderTag
def getFolderTag(db, folderPath, globalTag)
Definition: TileCalibTools.py:368
python.TileCalibTools.getAliasFromFile
def getAliasFromFile(aliastype='Current')
Definition: TileCalibTools.py:126
str
Definition: BTagTrackIpAccessor.cxx:11
python.Bindings.keys
keys
Definition: Control/AthenaPython/python/Bindings.py:801
TileCalibUtils::getDrawerIdx
static unsigned int getDrawerIdx(unsigned int ros, unsigned int drawer)
Returns a drawer hash.
Definition: TileCalibUtils.cxx:60
python.TileCalibTools.TileBlobReader.__init__
def __init__(self, db, folder, tag="")
Definition: TileCalibTools.py:760
python.TileCalibTools.TileBlobReader.getComment
def getComment(self, pointInTime, split=False)
Definition: TileCalibTools.py:793
error
Definition: IImpactPoint3dEstimator.h:70
python.ParticleTypeUtil.info
def info
Definition: ParticleTypeUtil.py:87
python.TileCalibTools.TileBlobReader.__db
__db
Definition: TileCalibTools.py:772
Trk::split
@ split
Definition: LayerMaterialProperties.h:38
python.TileCalibTools.TileASCIIParser2.PMT2channel
def PMT2channel(self, partition, pmt)
Definition: TileCalibTools.py:1422
python.CaloCondLogger.getLogger
def getLogger(name="CaloCond")
Definition: CaloCondLogger.py:16
description
std::string description
glabal timer - how long have I taken so far?
Definition: hcg.cxx:88
TileCalibUtils::getFullTag
static std::string getFullTag(const std::string &folder, const std::string &tag)
Returns the full tag string, composed of camelized folder name and tag part.
Definition: TileCalibUtils.cxx:33
python.TileCalibTools.TileBlobWriter.__folder
__folder
Definition: TileCalibTools.py:520