Skip to content
Snippets Groups Projects
THcParmList.cxx 20.6 KiB
Newer Older
/** \class THcParmList
    \ingroup Core
A class that can read and hold the parmaters from the CTP formatted
parameter files used by the Fortran ENGINE.

*/

#define INCLUDESTR "#include"

#include "TObjArray.h"
#include "TObjString.h"
#include "TSystem.h"

#include "THcParmList.h"
#include "THaVar.h"
#include "THaFormula.h"

/* #incluce <algorithm> include <fstream> include <cstring> */
#include <iostream>
#include <fstream>
#include <cassert>
#include <cstdlib>

using namespace std;
Int_t  fDebug   = 1;  // Keep this at one while we're working on the code    
THcParmList::THcParmList() : THaVarList()
{
  TextList = new THaTextvars;
}

inline static bool IsComment( const string& s, string::size_type pos )
{
  return ( pos != string::npos && pos < s.length() &&
	   (s[pos] == '#' || s[pos] == ';' || s.substr(pos,2) == "//") );
}

void THcParmList::Load( const char* fname, Int_t RunNumber )
  /**
Read a CTP style parameter file.

Parameter values and arrays of values are cached in a THaVarList
and are available for use elsewhere in the analyzer.
Text strings are saved in a THaTextvars list.
Parameter files can contain "include" statements of the form
~~~
   #include "filename"
~~~

If a run number is given, ignore input until a line with a matching
run number or run number range is found.  All parameters following
the are read until a non matching run number or range is encountered.
  */

  static const char* const whtspc = " \t";

  ifstream ifiles[100];		// Should use stack instead

  Int_t nfiles=0;
  ifiles[nfiles].open(fname);
  if(ifiles[nfiles].is_open()) {
    cout << "Opening parameter file: [" << nfiles << "] " << fname << endl;
    static const char* const here   = "THcParmList::LoadFromFile";
    Error (here, "error opening parameter file %s",fname);
    return;			// Need a success argument returned
  }
  
  string line;
  char varname[100];
  if(RunNumber > 0) {
    InRunRange = 0;		// Wait until run number range matching RunNumber is found
    cout << "Reading Parameters for run " << RunNumber << endl;
  } else {
    InRunRange = 1;		// Interpret all lines
  }

  while(nfiles) {
    string current_comment("");
    // EJB_Note:  existing_comment is never used.
    // string existing_comment("");
    string::size_type start, pos = 0;

    if(!getline(ifiles[nfiles-1],line)) {
      ifiles[nfiles-1].close();
      nfiles--;
      //      cout << nfiles << ": " << "Closed" << endl;
      continue;
    }
    // Look for include statement
    if(line.compare(0,strlen(INCLUDESTR),INCLUDESTR)==0) {
      line.erase(0,strlen(INCLUDESTR));
      pos = line.find_first_not_of(whtspc);
      // Strip leading white space
      if(pos != string::npos && pos > 0 && pos < line.length()) {
	line.erase(0,pos);
      }
      char quotechar=line[0];
      if(quotechar == '"' || quotechar == '\'') {
	line.erase(0,1);
	line.erase(line.find_first_of(quotechar));
      } else {
	line.erase(line.find_first_of(whtspc));
      }
      //      cout << line << endl;
      ifiles[nfiles].open(line.c_str());
      if(ifiles[nfiles].is_open()) {
	cout << "Opening parameter file: [" << nfiles << "] " << line << endl;
	nfiles++;
      }
      continue;
    }

    // Blank line or comment?
    if( line.empty()
	|| (start = line.find_first_not_of( whtspc )) == string::npos
	|| IsComment(line, start) )
      continue;

    // Get rid of trailing comments and leading and trailing whitespace
    // Need to save the comment and put it in the thVar
    
    while( (pos = line.find_first_of("#;/", pos+1)) != string::npos ) {
      if( IsComment(line, pos) ) {
	current_comment.assign(line,pos+1,line.length());
	line.erase(pos);	// Strip off comment
	// Strip leading white space from comment
	//cout << "CommentA: " << current_comment << endl;
	pos = current_comment.find_first_not_of(whtspc);
	if(pos!=string::npos && pos > 0 && pos < current_comment.length()) {
	  current_comment.erase(0,pos);
	}
	//cout << "CommentB: " << current_comment << endl;
	break;
      }
    }
    pos = line.find_last_not_of( whtspc );
    assert( pos != string::npos );
    if( pos != string::npos && ++pos < line.length() )
      line.erase(pos);
    pos = line.find_first_not_of(whtspc);
    // Strip leading white space
    if(pos != string::npos && pos > 0 && pos < line.length()) {
      line.erase(0,pos);
    }
    // Ignore begin and end statements
    if(line.compare(0,5,"begin")==0 ||
       line.compare(0,3,"end")==0) {
      cout << "Skipping: " << line << endl;
      continue;
    }

    // Get rid of all white space not in quotes
    // Step through one char at a time 
    pos = 0;
    int inquote=0;
    char quotechar=' ';
    // cout << "Unstripped line: |" << line << "|" << endl;
    while(pos<line.length()) {
      if(inquote) {
	if(line[pos++] == quotechar) { // Possibly end of quoted string
	  if(line[pos] == quotechar) { // Protected quote
	    pos++;		// Skip the protected quote
	  } else {		// End of quoted string
	    inquote = 0;
	    quotechar = ' ';
	    pos++;
	  }
	}
      } else {
	if(line[pos] == ' ' || line[pos] == '\t') {
	  line.erase(pos,1);
	} else if(line[pos] == '"' || line[pos] == '\'') {
	  quotechar = line[pos++];\
	  inquote = 1;
	} else {
	  pos++;
	}
      }
    // cout << "Stripped line: |" << line << "|" << endl;

    // Need to do something to bug out if line is empty

    // If in Engine database mode, check if line is a number range AAAA-BBBB
    if(RunNumber>0) {
      if(line.find_first_not_of("0123456789-")==string::npos) { // Interpret as runnum range
	if( (pos=line.find_first_of("-")) != string::npos) {
	  Int_t RangeStart=atoi(line.substr(0,pos).c_str());
	  Int_t RangeEnd=atoi(line.substr(pos+1,string::npos).c_str());
	  if(RunNumber >= RangeStart && RunNumber <= RangeEnd) {
	    InRunRange = 1;
	  } else {
	    InRunRange = 0;
	  }
	} else {		// A single number.  Run 
	  if(atoi(line.c_str()) == RunNumber) {
	    InRunRange = 1;
	  } else {
	    InRunRange = 0;
	  }
	}
    // Interpret left of = as var name
    Int_t valuestartpos=0;  // Stays zero if no = found
    Int_t ttype = 0;     // Are any of the values floating point?
    if((pos=line.find_first_of("="))!=string::npos) {
      strcpy(varname, (line.substr(0,pos)).c_str());
      valuestartpos = pos+1;
    // If first char after = is a quote, then this is a string assignment
    if(line[valuestartpos] == '"' || line[valuestartpos] == '\'') {
      quotechar = line[valuestartpos++];
      // Scan until end of line or terminating quote
      //      valuestartpos++;
      pos = valuestartpos;
      while(pos<line.length()) {
	if(line[pos++] == quotechar) { // Possibly end of quoted string
	  if(line[pos] == quotechar) { // Protected quote
	    pos++;
	  } else {
	    pos--;
	    break;
	  }
	}
      }
      if(TextList) {
	// Should check that a numerical assignment doesn't exist, but for
	// now, the same variable name can be used for strings and numbers
	string varnames(varname);
	AddString(varnames, line.substr(valuestartpos,pos-valuestartpos));
      }
      continue;
    }
      
    TString values((line.substr(valuestartpos)).c_str());
    TObjArray *vararr = values.Tokenize(",");
    Int_t nvals = vararr->GetLast()+1;
    
    Int_t* ip=0;
    Double_t* fp=0;
    // or expressions
    for(Int_t i=0;(ttype==0&&i<nvals);i++) {
      TString valstr = ((TObjString *)vararr->At(i))->GetString();
      if(valstr.IsFloat()) {	// Is a number
	if(valstr.Contains(".") || valstr.Contains("e",TString::kIgnoreCase)) {
	  ttype = 1;
	  break;
	}
      } else {
	ttype = 2;		// Force float if expression or Var
	break;
      }
    }

    // New pseudo code
    // currentindex = where next item goes
    // nvals = number of new items on line
    // newlength = curentindex+nvals

    // if (variable already exists)   (valuestartpos is 0 or a find succeeded)
    //       get existinglegnth
    //       if (existinglength > newlength && type doesn't change) {
    //            copy nvals values directly into array
    //       else
    //            make new longer array of length max(existinglength, newlength)
    //            copy existinglength values into longer array changing type if needed
    //            delete old varname
    //            recreate varname of proper length
    // else (variable doesn't exist)
    //      make array of newlength
    //      create varname
    //  
    // There is some code duplication here.  Refactor later

    Int_t newlength = currentindex + nvals;
    THaVar* existingvar=Find(varname);
    if(existingvar) {
      string existingcomment;
      existingcomment.assign(existingvar->GetTitle());
      if(!existingcomment.empty()) {
	current_comment.assign(existingcomment);
      }
      Int_t existingtype=existingvar->GetType();
      Int_t existinglength=existingvar->GetLen();
      if(newlength > existinglength ||
	 (existingtype == kInt && ttype > 0)) { // Length or type change needed
	if(newlength < existinglength) newlength = existinglength;
	Int_t newtype=-1;
	if(ttype>0 || existingtype == kDouble) {
	  newtype = kDouble;
	  fp = new Double_t[newlength];
	  if(existingtype == kDouble) {
	    Double_t* existingp= (Double_t*) existingvar->GetValuePointer();
	    for(Int_t i=0;i<existinglength;i++) {
	      fp[i] = existingp[i];
	    }
	  } else if(existingtype == kInt) {
	    Int_t* existingp= (Int_t*) existingvar->GetValuePointer();
	    for(Int_t i=0;i<existinglength;i++) {
	      fp[i] = existingp[i];
	    }
	  } else {
	    cout << "Whoops!" << endl;
	  }
	} else if(existingtype == kInt) {	// Stays int
	  Int_t* existingp= (Int_t*) existingvar->GetValuePointer();
	  for(Int_t i=0;i<existinglength;i++) {
	    ip[i] = existingp[i];
	  }
	} else {
	  cout << "Whoops!" << endl;
	}
	// Now copy new values in
	for(Int_t i=0;i<nvals;i++) {
	  TString valstr = ((TObjString *)vararr->At(i))->GetString();
	  if(newtype == kInt) {
	    ip[currentindex+i] = valstr.Atoi();
	  } else {
	    if(valstr.IsFloat()) {
	      fp[currentindex+i] = valstr.Atof();
	    } else {
	      THaFormula* formula = new THaFormula
		("temp",valstr.Data(), (Bool_t) 0, this, 0);
	      fp[currentindex+i] = formula->Eval();
	      delete formula;
	    }
	  }
	}
	currentindex += nvals;
	// Remove old variable and recreate
	if(existingtype == kDouble) {
	  delete [] (Double_t*) existingvar->GetValuePointer();
	} else if (existingtype == kInt) {
	  delete [] (Int_t*) existingvar->GetValuePointer();
	}
	char *arrayname=new char [strlen(varname)+20];
	sprintf(arrayname,"%s[%d]",varname,newlength);
	if(newtype == kInt) {
	  Define(arrayname, current_comment.c_str(), *ip);
	  Define(arrayname, current_comment.c_str(), *fp);
	delete[] arrayname;
      } else {
	// Existing array long enough and of right type, just copy to it.
	if(ttype == 0 && existingtype == kInt) {
	  Int_t* existingp= (Int_t*) existingvar->GetValuePointer();
	  for(Int_t i=0;i<nvals;i++) {
	    TString valstr = ((TObjString *)vararr->At(i))->GetString();
	    existingp[currentindex+i] = valstr.Atoi();
	  }
	} else {
	  Double_t* existingp= (Double_t*) existingvar->GetValuePointer();
	  for(Int_t i=0;i<nvals;i++) {
	    TString valstr = ((TObjString *)vararr->At(i))->GetString();
	    if(valstr.IsFloat()) {
	      existingp[currentindex+i] = valstr.Atof();
	    } else {
	      THaFormula* formula = new THaFormula
		("temp",valstr.Data(), (Bool_t) 0, this, 0);
	      existingp[currentindex+i] = formula->Eval();
	      delete formula;
	    }
	  }
	}
	currentindex += nvals;
      }	
    } else {
      if(currentindex !=0) {
	cout << "currentindex=" << currentindex << " shouldn't be!" << endl;
      }
      if(ttype==0) {
	ip = new Int_t[nvals];
      } else {
	fp = new Double_t[nvals];
      }
      for(Int_t i=0;i<nvals;i++) {
	TString valstr = ((TObjString *)vararr->At(i))->GetString();
	if(ttype==0) {
	} else {
	  if(valstr.IsFloat()) {
	    THaFormula* formula = new THaFormula
	      ("temp",valstr.Data(), (Bool_t) 0, this, 0);
      char *arrayname=new char [strlen(varname)+20];
      sprintf(arrayname,"%s[%d]",varname,nvals);
      if(ttype==0) {
	Define(arrayname, current_comment.c_str(), *ip);
      } else {
	Define(arrayname, current_comment.c_str(), *fp);
      }
      delete[] arrayname;
    }

    delete vararr;		// Discard result of Tokenize

//_____________________________________________________________________________
Int_t THcParmList::LoadParmValues(const DBRequest* list, const char* prefix)
{
  // Load a number of entries from the database.
  // For array entries, the number of elements to be read in
  // must be given, and the memory already allocated
  // NOTE: initial code taken wholesale from THaDBFile. 
  // GN 2012
  // If prefix is specified, prepend each requested parameter name with
  // the prefix.
  
  const DBRequest *ti = list;
  Int_t cnt=0;
  Int_t this_cnt=0;

    string keystr(prefix); keystr.append(ti->name);
    const char* key = keystr.c_str();
    ///    cout <<"Now at "<<ti->name<<endl;
      VarType ty = this->Find(key)->GetType();
      if (ti->nelem>1) {
	// it is an array, use the appropriateinterface
	switch (ti->type) {
	case (kDouble) :
	  this_cnt = GetArray(key,static_cast<Double_t*>(ti->var),ti->nelem);
	  break;
	case (kInt) :
	  this_cnt = GetArray(key,static_cast<Int_t*>(ti->var),ti->nelem);
	  break;
	default:
	  Error("THcParmList","Invalid type to read %s",key);
	  break;
	}

      } else {
	switch (ti->type) {
	case (kDouble) :
	  if(ty == kInt) {
	    *static_cast<Double_t*>(ti->var)=*(Int_t *)this->Find(key)->GetValuePointer();	    
	  } else if (ty == kDouble) {
	    *static_cast<Double_t*>(ti->var)=*(Double_t *)this->Find(key)->GetValuePointer();
	  } else {
	    cout << "*** ERROR!!! Type Mismatch " << key << endl;
	  }
	  if(ty == kInt) {
	    *static_cast<Int_t*>(ti->var)=*(Int_t *)this->Find(key)->GetValuePointer();
	  } else if (ty == kDouble) {
	    *static_cast<Int_t*>(ti->var)=TMath::Nint(*(Double_t *)this->Find(key)->GetValuePointer());
	    cout << "*** WARNING!!!  Rounded " << key << " to nearest integer " << endl;
	  } else {
	    cout << "*** ERROR!!! Type Mismatch " << key << endl;
	  }
	  this_cnt=1;
	  break;
	default:
	  Error("THcParmList","Invalid type to read %s",key);
	  break;
    } else {			// See if it is a text variable
      const char* value = GetString(key);
      if(value) {
	this_cnt = 1;
	if(ti->type == kString) {
	  *((string*)ti->var) = string(value);
	} else if (ti->type == kTString) {
	  *((TString*)ti->var) = (TString) value;
	} else {
	  Error("THcParmList","No conversion for strings: %s",key);
	}
      }
    }
    if (this_cnt<=0) {
      if ( !ti->optional ) {
	Fatal("THcParmList","Could not find %s in database!",key);
//  READING AN ARRAY INTO A C-style ARRAY
//_____________________________________________________________________________
Int_t THcParmList::GetArray(const char* attr, Int_t* array, Int_t size)
{
  // Read in a set of Int_t's in to a C-style array.
  
  return ReadArray(attr,array,size);
}
//_____________________________________________________________________________
Int_t THcParmList::GetArray(const char* attr, Double_t* array, Int_t size)
{
  // Read in a set of Double_t's in to a vector.
  
  return ReadArray(attr,array,size);
}

//_____________________________________________________________________________
template<class T>
Int_t THcParmList::ReadArray(const char* attrC, T* array, Int_t size)
{
  // Copy values from parameter store to array
  // No resizing is done, so only 'size' elements may be stored.

  Int_t cnt=0;

  THaVar *var = Find(attrC);
  if(!var) return(cnt);
  VarType ty = var->GetType();
  if( ty != kInt && ty != kDouble) {
    cout << "*** ERROR: " << attrC << " has unsupported type " << ty << endl;
    return(cnt);
  }
  Int_t sz = var->GetLen();
  const void *vp = var->GetValuePointer();
  if(size != sz) {
    cout << "*** WARNING: requested " << size << " elements of " << attrC <<
      " which has length " << sz << endl;
  }
  if(size<sz) sz = size;
  Int_t donint = 0;
  if(ty == kDouble && typeid(array[0]) == typeid(Int_t)) {
    donint = 1;			// Use nint when putting doubles in nint
    cout << "*** WARNING!!!  Rounded " << attrC << " elements to nearest integer " << endl;
  }
  for(cnt=0;cnt<sz;cnt++) {
    if(ty == kInt) {
      array[cnt] = ((Int_t*)vp)[cnt];
    } else
      if(donint) {
	array[cnt] = TMath::Nint(((Double_t*)vp)[cnt]);
      } else {
	array[cnt] = ((Double_t*)vp)[cnt];
      }
//_____________________________________________________________________________
void THcParmList::PrintFull( Option_t* option ) const
{
  // Print all the numeric parameter desciptions and values.
  // Print all the text parameters
  THaVarList::PrintFull(option);
  TextList->Print();
}
#ifdef WITH_CCDB
//_____________________________________________________________________________
Int_t THcParmList::OpenCCDB(Int_t runnum)
{
  // Use the Environment variable "CCDB_CONNECTION" as the
  // connection string
  const char* connection_string = gSystem->Getenv("CCDB_CONNECTION");

  return(OpenCCDB(runnum,connection_string));
}
Int_t THcParmList::OpenCCDB(Int_t runnum, const char* connection_string)
{
  // Connect to a CCDB database pointed to by connection_string

  std::string s (connection_string);
  CCDB_obj = new SQLiteCalibration(runnum);
  Int_t result = CCDB_obj->Connect(s);
  if(!result) return -1;	// Need some error codes
  cout << "Opened " << s << " for run " << runnum << endl;
  return 0;
}
Int_t THcParmList::CloseCCDB()
{
  delete CCDB_obj;
  return(0);
}
Int_t THcParmList::LoadCCDBDirectory(const char* directory, 
				     const char* prefix)
{
  // Load all parameters in directory
  // Prepend prefix onto the name of each

  std::string dirname (directory);

  if(dirname[dirname.length()-1]!='/') {
    dirname.append("/");
  }
  Int_t dirlen=dirname.length();

  vector<string> namepaths;
  CCDB_obj->GetListOfNamepaths(namepaths);
  for(UInt_t iname=0;iname<namepaths.size();iname++) {
    std::string varname (namepaths[iname]);
    if(varname.compare(0,dirlen,dirname) == 0) {
      varname.replace(0,dirlen,prefix);
      //      cout << namepaths[iname] << " -> " << varname << endl;

      // To what extent is there duplication here with Load() method?

      // Retrieve assignment
      Assignment* assignment = CCDB_obj->GetAssignment(namepaths[iname], true);
      ConstantsTypeColumn::ColumnTypes ccdbtype=assignment->GetValueType(0);
      Int_t ccdbncolumns=assignment->GetColumnsCount();
      Int_t ccdbnrows=assignment->GetRowsCount();
      std::string title = assignment->GetTypeTable()->GetComment();

      // Only load single column tables
      if(ccdbncolumns == 1) {

	THaVar* existingvar=Find(varname.c_str());
	// Need to append [size] to end of varname
	char sizestring[20];
	sprintf(sizestring,"[%d]",ccdbnrows);
	std::string size_str (sizestring);
	std::string varnamearray (varname);
	varnamearray.append(size_str);
	// Select data type
	if(ccdbtype==ConstantsTypeColumn::cIntColumn) {
	  vector<vector<int> > data;
	  CCDB_obj->GetCalib(data, namepaths[iname]);

	  if(existingvar) {
	    RemoveName(varname.c_str());
	  }

	  Int_t* ip = new Int_t[data.size()];
	  for(UInt_t row=0;row<data.size(); row++) {
	    ip[row] = data[row][0];
	  }
	  Define(varnamearray.c_str(), title.c_str(), *ip);

	} else if (ccdbtype==ConstantsTypeColumn::cDoubleColumn) {
	  vector<vector<double> > data;
	  CCDB_obj->GetCalib(data, namepaths[iname]);

	  if(existingvar) {
	    RemoveName(varname.c_str());
	  }

	  Double_t* fp = new Double_t[data.size()];
	  for(UInt_t row=0;row<data.size(); row++) {
	    fp[row] = data[row][0];
	  }
	  Define(varnamearray.c_str(), title.c_str(), *fp);
	} else if (ccdbtype==ConstantsTypeColumn::cStringColumn) {
	  if(ccdbnrows > 1) {
	    cout << namepaths[iname] << ": Only first element of CCDB string array loaded."  << endl;
	  }
	  vector<vector<string> > data;
	  CCDB_obj->GetCalib(data, namepaths[iname]);
	  AddString(varname, data[0][0]);
	} else {
	  cout << namepaths[iname] << ": Unsupported CCDB data type: " << ccdbtype << endl;
	}
      } else {
	cout << namepaths[iname] << ": Multicolumn CCDB variables not supported" << endl;
      }
    }