#!/usr/bin/env python import os import sys import getopt # Get ccdb API ccdb_home = os.environ["CCDB_HOME"] # Should be set! sys.path.append(os.path.join(ccdb_home, "python")) import ccdb from ccdb import Directory, TypeTable, Assignment, ConstantSet scriptname = os.path.basename(__file__) #--------------------------------------- # Usage #--------------------------------------- def Usage(): print ''' Usage: '''+scriptname+''' [options] command [command args] This script can be used to probe or modify the SQLite version of the Hall-D translation table DB. You will need a copy of the tt.db SQLite file which is maintained in the subversion repository and can be obtained with: svn co https://halldsvn.jlab.org/repos/trunk/online/packages/TranslationTable options: -h,--help Print help commands: --------- help [command] Display detailed help for a command chan system [idx1 idx2 ...] Display channel info using system indices daqchan [rocid slot chan] Display channel info using DAQ indices log [max [start [end]]] Display log of modifications to DB rocids Display list of rocids and their systems runrange [cmd [...]] Display, create, modify run ranges. swap Swap channels (e.g. record a cable swap) systems Display list of valid system names ''' #--------------------------------------- # main #--------------------------------------- def main(): # Parse command line arguments try: opts, args = getopt.getopt(sys.argv[1:], "hr:", ["help","run="]) if len(args)==0: Usage() sys.exit() global run run = 999999 for o, a in opts: if o in ("-h", "--help"): Usage() sys.exit() elif o in ("-r", "--run"): run = int(a) else: assert False, "unhandled option" except getopt.GetoptError, err: # print help information and exit: print str(err) # will print something like "option -a not recognized" Usage() sys.exit(2) # If help command issued, do that before attempting to interact with DB if args[0] == 'help' : CmdHelp( args[1:] ) ; sys.exit(0) # Get python module for interfacing with tt.db (this will automatically open the DB and verify the schema import halld_tt as tt global tt tt.Initialize() if not tt.db_valid: print '\n' + tt.db_filename + ' does not have an up to date schema.' print '\nYou should obtain the latest version from:\n' print ' https://halldsvn.jlab.org/repos/trunk/online/packages/TranslationTable/tt.db\n' sys.exit(-1) # Process command try: if args[0] == 'chan' : CmdGetChannel(args[1:]) if args[0] == 'daqchan' : CmdGetDAQChannel(args[1:]) if args[0] == 'log' : CmdLog(args[1:]) if args[0] == 'rocids' : CmdGetROCIDs() if args[0] == 'swap' : CmdSwap() if args[0] == 'systems' : CmdSystems() if args[0] == 'runrange' : CmdRunRange(args[1], args[2:]) except IndexError as error: print repr(error) print 'ERROR: Wrong number of arguments for command "' + args[0] + '"' CmdHelp([args[0]]) print '..............................................................................' print '(See error messages printed above help)\n' except ValueError as error: print repr(error) CmdHelp([args[0]]) print '..............................................................................' print '(See error messages printed above help)\n' #--------------------------------------- # CmdHelp # # Print help message #--------------------------------------- def CmdHelp(args): if len(args) == 0: args.append('help') for cmd in args: print '\n..............................................................................' #........ help ...............................................................| if cmd == 'help': print ''' help cmd [cmd2 cmd3 ...] Print detailed help on a specific command. Run with "-h" for list of commands with short descriptions. ''' Usage() #........ chan ...............................................................| if cmd == 'chan': print ''' chan system [idx1 idx2 ...] Display channel from the specified system with the corresponding indices. If the indices are ommitted, then a help message for that system is printed giving the indices in the order that they should be specified. When issuing the chan command with indicies all must be present for the given system. Wildcards "*" are allowed for any or all indicies. In that case, a list of all matching channels is printed. example 1: To see which indices are needed for a system and their order, issue the command with just the system name: '''+scriptname+''' TOF output: System "TOF" requires 3 indices chan TOF where the indices' ranges are: end : DW,S,UP,N plane : 0 - 1 bar : 1 - 44 example 2: To see the DAQ coordinates for TOF channels with plane=0 and bar=4: '''+scriptname+''' TOF "*" 0 4 output: ---------------------------------------------------------------------------- runrangeid system end plane bar -- rocid slot channel type ---------- -------- -------- -------- -------- ----- ---- ------- ------------ 12 TOF DW 0 4 -- 77 13 7 fADC250 12 TOF DW 0 4 -- 78 7 7 VX1290A 12 TOF DW 0 4 -- 79 12 8 DISC 12 TOF UP 0 4 -- 77 8 11 fADC250 12 TOF UP 0 4 -- 78 5 27 VX1290A 12 TOF UP 0 4 -- 79 9 12 DISC ''' #........ daqchan ............................................................| if cmd == 'daqchan': print ''' daqchan [rocid slot channel] Display channels with the corresponding rocid, slot, and channel. Any of these may be specified as a wildcard "*". To see a list of valid rocid values along with the systems that use them, see the "rocids" command. ''' #........ log ............................................................| if cmd == 'log': print ''' log [max_entries [start [end]]] Display log from DB. The log is not an extensive record of all DB modifications. It may however, provide some clues as to when certain changes were made. If no arguments are given then all log entries are displayed. Optionally, the maximum number of entries (starting with the most recent) can be specified with the first argument. The second and third arguments (if specified) give the date of the start and end range of the desired logs. (Specific times cannot be specified.) The format for specifying dates should be e.g. '2000-01-01' example 1: To see the 10 most recent log entries: '''+scriptname+''' log 10 output: Log ------------------------------------------ date time author message ---------- -------- ---------- ---------------------------------- 2017-12-02 14:12:41 davidl CreateRunRange 30000 2147483647 CCDB_30000_ 2017-12-02 13:45:23 davidl CreateRunRange 20000 29999 CCDB_20000_29999 2017-12-02 13:19:47 davidl CreateRunRange 20000 2147483647 CCDB_20000_ 2017-12-02 12:57:53 davidl CreateRunRange 10000 11680 CCDB_10000_11680 2017-12-02 12:37:34 davidl CreateRunRange 10000 2147483647 CCDB_10000_ 2017-12-02 12:19:38 davidl CreateRunRange 3850 2147483647 CCDB_3850_21 2017-12-02 12:03:22 davidl CreateRunRange 2881 2147483647 CCDB_2881_21 2017-12-02 11:49:43 davidl CreateRunRange 0 2439 CCDB_0_2439 2017-12-02 11:37:42 davidl CreateRunRange 2440 2460 CCDB_2440_2460 2017-12-02 11:28:45 davidl CreateRunRange 2461 2147483647 CCDB_2461_21 example 2: To see the 5 most recent log entries starting on 2017-12-01 (note at the time of this writing there are *only* entries from 2017-12-02 so nothing shows up from 2017-12-01) '''+scriptname+''' log 5 2017-12-01 output: Log ------------------------------------------ date time author message ---------- -------- ---------- ---------------------------------- 2017-12-02 14:12:41 davidl CreateRunRange 30000 2147483647 CCDB_30000_21 2017-12-02 13:45:23 davidl CreateRunRange 20000 29999 CCDB_20000_29999 2017-12-02 13:19:47 davidl CreateRunRange 20000 2147483647 CCDB_20000_21 2017-12-02 12:57:53 davidl CreateRunRange 10000 11680 CCDB_10000_11680 2017-12-02 12:37:34 davidl CreateRunRange 10000 2147483647 CCDB_10000_21 ''' #........ runranges ..........................................................| if cmd == 'runranges': print ''' runranges [sys [sys2 sys3 ...]] Display run ranges from DB. Optionally, one or more systems can be given and only the run ranges for each of those systems will be shown. Note that run ranges may overlap so for a given run number anf system, then one with the largest "id" value is considered the most recent and therefore most accurate entry. ''' #........ runrange ..........................................................| if cmd == 'runrange': print ''' runrange list [sys [sys2 sys3 ...]] runrange create minRun maxRun name runrange getid name|run runrange info name|id runrange modify name|id minRun maxRun The "list" subcommand will display run ranges from the DB. Optionally, one or more systems can be given and only the run ranges for each of those systems will be shown. Note that run ranges may overlap so for a given run number and system, then one with the largest "id" value is considered the most recent and therefore most accurate entry. The "create" subcommand will create a new run range in the DB. The minRun, maxRun and name parameters must be given. The name can be any string that is not a pure number and does not contain any white space characters. For maxRun, the string "inf" may be given to specify all runs > minRun. The "id" subcommand will print the runrangeid value from the DB for the given name or run number. The "info" subcommand will print information about the run range for the given name or id. The "modify" subcommand will modify the minRun and maxRun for the range with the given name or id. The first argument must be a valid name or runrangeid. Both the minRun and maxRun values must be given, even if you only want to change one. Use the info command to see the current minRun and maxRun values. For maxRun, the string "inf" may be given to specify all runs > minRun. ''' #........ rocids .............................................................| if cmd == 'rocids': print ''' rocids Display list of rocids and their systems ''' #........ swap ...............................................................| if cmd == 'swap': print ''' swap Record a cable swap in the TT database. Running this command will prompt you with a series of questions to define the swap. You will then be asked to review the information before it is committed to the DB. Swaps can be made between single channels or a group of channels in the case of a multi-channel connector. The information you will need for the swap are: 1. start and end runs (inf may be used for the end run) 2. rocid, slot, and channels of both partners of the swap It is important to remember that the commit will be made only to the local SQLite file. That file must be committed back to the central subversion repository for others to see it. One also needs to generate an XML representation and commit that to the CCDB for the analysis code to see the swap. Detailed instructions for this will be provided after the swap is committed to the local DB. ''' #........ systems ............................................................| if cmd == 'systems': print ''' systems Display list of valid system names ''' print '' #--------------------------------------- # CmdRunRange # # List, Create, modify run ranges #--------------------------------------- def CmdRunRange(cmd, args): #==== list ====== if cmd=='list': if len(args) == 0: print '\nRun Ranges (All)' else: print '\nRun Ranges for any of: ' + ' '.join(args) print '------------------------------------------' print ' id min max name' print '---- ------ ------ ---------------------' rrinfos = tt.GetRunRanges(args) for rrinfo in rrinfos: if int(rrinfo['maxRun'])>999999 : rrinfo['maxRun'] = ' inf' else: rrinfo['maxRun'] = '%6d' % int(rrinfo['maxRun']) print '%4d %6d-%s %s' % (rrinfo['runrangeid'], int(rrinfo['minRun']), rrinfo['maxRun'], rrinfo['name']) print '' #==== create ====== if cmd=='create': if len(args) != 3: raise(IndexError('"runrange create" requires 3 arguments!"')) else: if args[1] == 'inf': args[1] = 2147483647 minRun = int(args[0]) maxRun = int(args[1]) name = args[2] if maxRun < minRun: raise(ValueError('minRun>maxRun! (%d>%d)' % (minRun,maxRun))) if tt.GetRunRangeID( name ) != -1: raise(ValueError('runrange with name "' + name + '" already exists!')) else: runrangeid = tt.CreateRunRange( minRun, maxRun, name ) if runrangeid == -1: raise(ValueError('Unable to create requested run range!')) else: maxstr = str(maxRun) if maxRun >= 2147483647: maxstr = 'inf' print 'Created new runrange %d-%s with id=%d' % (minRun, maxstr,runrangeid) #==== id ====== if cmd=='id': if len(args) != 1: raise(IndexError('"runrange id" requires 1 arguments!"')) else: name_or_run = str(args[0]) id = tt.GetRunRangeID(name_or_run) if id == -1: try: id = tt.GetRunRangeIDByRun( int(name_or_run) ) except ValueError as error: pass if id == -1: print 'No run range for "' + name_or_run + '"' else: print 'runrangeid: ' + str(id) #==== info ====== if cmd=='info': if len(args) != 1: raise(IndexError('"runrange info" requires 1 arguments!"')) else: name_or_run = str(args[0]) id = tt.GetRunRangeID(name_or_run) if id == -1: try: id = tt.GetRunRangeIDByRun( int(name_or_run) ) except ValueError as error: pass if id == -1: print 'No run range for "' + name_or_run + '"' else: print '\nruninfo for: ' + name_or_run print '---------------------------------------' rrinfo = tt.GetRunRangeInfo(id) maxlen = len(max(rrinfo.keys(), key=len)) for key,val in rrinfo.iteritems(): print ' '*(maxlen+3-len(key)) + key + ': ' + str(val) #==== modify ====== if cmd=='modify': if len(args) != 4: raise(IndexError('"runrange modify" requires 4 arguments!"')) else: id = int(args[0]) minRun = int(args[1]) maxRun = int(args[2]) name = args[3] if maxRun < minRun: raise(ValueError('minRun>maxRun! (%d>%d)' % (minRun,maxRun))) if len( tt.GetRunRangeInfo(id) ) == 0: raise(ValueError('runrange id "%d" does not exist' % id)) tt.ChangeRunRange(id, minRun, maxRun, name) print 'Changed run range for id %d' % id CmdRunRange( 'info', [name]) #--------------------------------------- # CmdLog # # Print log. Optionally limit number of entries #--------------------------------------- def CmdLog(args): print '\nLog' print '------------------------------------------' print ' date time author message' print '---------- -------- ---------- ----------------------------------' if len(args) == 0: logs = tt. GetLog() if len(args) == 1: logs = tt. GetLog(max_messages=args[0]) if len(args) == 2: logs = tt. GetLog(max_messages=args[0], startdate=args[1]) if len(args) == 3: logs = tt. GetLog(max_messages=args[0], startdate=args[1], enddate=args[2]) for log in logs: print log print '' #--------------------------------------- # CmdGetDAQChannel # # Print list of channels indexed by rocid, slot, channel #--------------------------------------- def CmdGetDAQChannel(args): if len(args)!=3: print '\nThe "daqchan" command requires a rocid, slot, channel.' print 'You may specify "*" for any or all of these if desired.' print 'Run the "rocids" command to see a list of valid rocids.\n' sys.exit(0) rocid = args[0] slot = args[1] channel = args[2] rrid= tt.GetRunRangeIDByRun( int(run) ) if rrid<0: print 'Cannot find run range that includes run ' + run print 'Try using runrange command to see valid run ranges' sys.exit(-1) rrinfo = tt.GetRunRangeInfo(rrid) minRun = str(rrinfo['minRun']) maxRun = str(rrinfo['maxRun']) if int(maxRun)>999999: maxRun = 'inf' chans = tt. GetDAQChannels(runrangeid=rrid, rocid=rocid, slot=slot, channel=channel) sys.stdout.write('\nChannels for runrangeid: ' + str(rrid) + '('+minRun+'-'+maxRun+')') print ' rocid: ' +rocid+' slot: ' +slot+' channel: ' +channel print '------------------------------------------' print 'runrangeid rocid slot channel type system -- idx' print '---------- ------- ------ ------- --------- ------------- ------------------' for chan in chans: sys.stdout.write(' %4d %3d %2d %3d %10s %12s --' % (chan['runrangeid'], chan['rocid'], chan['slot'], chan['channel'], chan['type'], chan['system'])) idx = chan['system_indices'] for k in idx.keys(): sys.stdout.write(' '+k+'='+str(idx[k])) print '' print ' ' #--------------------------------------- # CmdGetChannel # # Print list of channels indexed by system-specific indices #--------------------------------------- def CmdGetChannel(args): if len(args)<1: print '\nYou must supply at least a system name to the "chan" command.' print '(run the "systems" command for a list of valid systems)\n' sys.exit(0) system = args[0] if system not in tt.GetSystems(): print '\nUnknown system "%s"!' % system print '(run the "systems" command for a list of valid systems)\n' sys.exit(-1) limits = tt.GetSystemIndexLimits(system) if len(args[1:]) != len(limits): print '\nSystem "%s" requires %d indices' % (system, len(limits)) sys.stdout.write('\n chan %s ' % system) for s in limits.keys(): sys.stdout.write('<%s> ' % s) print '\n\nwhere the indices\' ranges are:\n' for idx in limits: if len(limits[idx])==2 and 'min' in limits[idx] and 'max' in limits[idx]: print '%12s : %s - %s' % (idx, limits[idx]['min'], limits[idx]['max']) else: print '%12s : %s' % (idx, ','.join(limits[idx].keys())) print '' sys.exit(-1) rrid= tt.GetRunRangeIDByRun( int(run) ) if rrid<0: print 'Cannot find run range that includes run ' + run print 'Try using runrange command to see valid run ranges' sys.exit(-1) rrinfo = tt.GetRunRangeInfo(rrid) minRun = str(rrinfo['minRun']) maxRun = str(rrinfo['maxRun']) if int(maxRun)>999999: maxRun = 'inf' indices = {} iarg=1 for idx in limits.keys(): indices[idx] = args[iarg] iarg += 1 chinfos = tt. GetChannels(system, indices, runrangeid=rrid) print '----------------------------------------------------------------------------' sys.stdout.write('runrangeid system ') for k in limits.keys(): sys.stdout.write('%8s ' % k) print ' -- rocid slot channel type' sys.stdout.write('---------- --------') for k in limits.keys(): sys.stdout.write(' --------') print ' ----- ---- ------- ------------' for chinfo in chinfos: sys.stdout.write(' %4d %10s ' % (chinfo['runrangeid'], system)) for k in limits.keys(): sys.stdout.write('%8s ' % str(chinfo[k])) sys.stdout.write(' -- %3d %2d %3d %10s' % (chinfo['rocid'], chinfo['slot'], chinfo['channel'], chinfo['type'])) print '' #--------------------------------------- # CmdGetROCIDs # # Print list of rocids and systems that use them #--------------------------------------- def CmdGetROCIDs(): rrid= tt.GetRunRangeIDByRun( int(run) ) if rrid<0: print 'Cannot find run range that includes run ' + run print 'Try using runrange command to see valid run ranges' sys.exit(-1) rrinfo = tt.GetRunRangeInfo(rrid) minRun = str(rrinfo['minRun']) maxRun = str(rrinfo['maxRun']) if int(maxRun)>999999: maxRun = 'inf' rocids = tt.GetROCIDs(rrid) print '\nChannels for runrangeid: ' + str(rrid) + '('+minRun+'-'+maxRun+')' print '------------------------------------------' print ' rocid system(s)' print '------ -------------' for rocid in sorted(rocids): sys.stdout.write(' %3d ' % rocid) print ','.join(rocids[rocid]) #--------------------------------------- # CmdSwap # # Record a cable swap #--------------------------------------- def CmdSwap(): print '\nSwap channels in TT' print '===============================\n' print 'Please respond to the prompts below to enter the parameters' print 'of the swap. You may enter "quit" at any prompt to abort and' print 'quit the program without committing any changes.' # Get Run Range run_range = [] while len(run_range) != 2: print '\nPlease enter the run range where this swap occured.' print 'Use a "-" to separate first and last runs (inclusive)' print 'The end of the range may be specified as inf' print 'e.g. 30000-inf' run_range = [x.strip() for x in raw_input('run range: ').split('-')] print '' if 'quit' in run_range or 'exit' in run_range: sys.exit(0) if len(run_range) !=2 : print '\nBad formatting' elif not run_range[0].isdigit(): print 'starting run must be a number!' run_range = [] elif run_range[1] != 'inf' and not run_range[1].isdigit(): print 'end run must be either a number or "inf"!' run_range = [] # Make sure we have numerical values for run limits rrmin = int(run_range[0]) if run_range[1]=='inf': rrmax = 999999 else: rrmax = int(run_range[1]) print 'The parameters specifing channel group "A" and "B" will now' print 'be entered. The channels in group A will be swapped with ' print 'those in group B. The groups may consist of a single channel' print 'but the number of channels in groups A and B must match.' # Get group A parameters print '\nPlease enter the rocid for group A' rocidA = raw_input('rocid A: ') if 'quit' in rocidA or 'exit' in rocidA: sys.exit(0) print '\nPlease enter the slot for group A' slotA = raw_input('slot A: ') if 'quit' in slotA or 'exit' in slotA: sys.exit(0) print '\nPlease enter the channel or channel range for group A' print 'If specifying more than one channel, then the channels must be' print 'contiguous and have a format like: 1-24' channelsA = [x.strip() for x in raw_input('channel(s) A: ').split('-')] if 'quit' in channelsA[0] or 'exit' in channelsA[0]: sys.exit(0) # Get group B parameters print '\nPlease enter the rocid for group B' rocidB = raw_input('rocid B: ') if 'quit' in rocidB or 'exit' in rocidB: sys.exit(0) print '\nPlease enter the slot for group B' slotB = raw_input('slot B: ') if 'quit' in slotB or 'exit' in slotB: sys.exit(0) print '\nPlease enter the channel or channel range for group B' print 'If specifying more than one channel, then the channels must be' print 'contiguous and have a format like: 1-24' channelsB = [x.strip() for x in raw_input('channel(s) B: ').split('-')] if 'quit' in channelsB[0] or 'exit' in channelsB[0]: sys.exit(0) # Number of channels must be same if len(channelsA)>1 and len(channelsB)>1 : NA = int(channelsA[1]) - int(channelsA[0]) + 1 NB = int(channelsB[1]) - int(channelsB[0]) + 1 if NA != NB: print '\nNumber of channels in group A (%d) is not equal to number in group B (%d)!' % (NA,NB) print 'Groups must have same number of channels for swap.\n' sys.exit(-1) # Check if there is a run range already in the DB that matches this runrangeid = -1 overlapping_runrange = False for rrinfo in reversed( tt.GetRunRanges() ): # loop starting with most recent minRun = int(rrinfo['minRun']) if str(rrinfo['maxRun'])=='inf': maxRun = 999999 else: maxRun = int(rrinfo['maxRun']) if maxRun>999999: maxRun = 999999 if minRun==rrmin and maxRun==rrmax: runrangeid = rrinfo['runrangeid'] break if (maxRun>=rrmin) and (minRun<=rrmax): overlapping_runrange = True # Tell user what we're supposed to do print '\n The following swap will be made:' print 'run range: %d-%s' % (int(run_range[0]), run_range[1]) print '' print ' rocidA=%d' % int(rocidA) + ' ('+(','.join(tt.GetSystemsByROCID(rocidA)))+')' print ' slotA=%d' % int(slotA) print 'channelsA=' + '-'.join(channelsA) print '' print ' rocidB=%d' % int(rocidB) + ' ('+(','.join(tt.GetSystemsByROCID(rocidB)))+')' print ' slotB=%d' % int(slotB) print 'channelsB=' + '-'.join(channelsB) # Check if we need to create a new run range if (runrangeid>=0) and not overlapping_runrange: print 'Run range id=%d matches this request and will be used' % runrangeid else: runrangeid = -1 last_runrangeid = reversed( tt.GetRunRanges() )[0]['runrangeid'] next_runrangeid = int(last_runrangeid) + 1 name = 'CCDB_' + str(run_range[0]) + '_' + str(run_range[1]) + '_swap_'+str(next_runrangeid) if overlapping_runrange: print 'Run range id=%d matches this request, but newer run ranges overlap so' % runrangeid print 'the range will be be re-entered for this swap.' else: print 'No matching run range exists for this request. A new one will be created' # Create run range if needed if runrangeid<0 : runrangeid = CreateRunRange(run_range[0], run_range[1], name) print '' #--------------------------------------- # CmdSystems # # Print list of valid system names #--------------------------------------- def CmdSystems(): print '\nSystems' print '------------------------------------------' for s in tt.GetSystems(): print s print '' #======================================================================================= if __name__ == "__main__": main()