Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:17:32

0001 
0002 // Copyright 2012-2025, Jefferson Science Associates, LLC.
0003 // Subject to the terms in the LICENSE file found in the top-level directory.
0004 // Author: David Lawrence
0005 
0006 #include "JCalibrationFile.h"
0007 #include <JANA/JLogger.h>
0008 
0009 #include <dirent.h>
0010 #include <sys/stat.h>
0011 #include <stdlib.h>
0012 #include <time.h>
0013 
0014 #include <iostream>
0015 #include <iomanip>
0016 #include <fstream>
0017 using namespace std;
0018 
0019 
0020 //---------------------------------
0021 // JCalibrationFile    (Constructor)
0022 //---------------------------------
0023 JCalibrationFile::JCalibrationFile(string url, int32_t run, string context):JCalibration(url,run,context)
0024 {
0025     // File URL should be of form:
0026     // file:///path-to-root-dir
0027     // The value of namepath will then be followed relative to this.
0028 
0029     // NOTE: The original design here added an additional "context" directory
0030     // that was always appended to the URL. This idea was shouted down at
0031     // the July 17, 2007 software meeting and so was removed.
0032 
0033     // First, check that the URL even starts with "file://"
0034     if(url.substr(0, 7)!=string("file://")){
0035         LOG<<"Poorly formed URL. Should start with \"file://\"."<<LOG_END;
0036         LOG<<"URL:"<<url<<LOG_END;
0037         return;
0038     }
0039 
0040     // Fill basedir with path to directory.
0041     basedir = url;
0042     basedir.replace(0,7,string("")); // wipe out "file://" part
0043     if(basedir[basedir.size()-1]!='/')basedir += "/";
0044     //basedir += context + "/"; // (see not above)
0045 
0046     // Try and get a hold of the info.xml file in basedir
0047     string fname = basedir + "info.xml";
0048     ifstream f(fname.c_str());
0049     if(!f.is_open()){
0050         jout<<"WARNING: Unable to open \""<<fname<<"\" (not necessarily a problem)"<<std::endl;
0051     }
0052 
0053     // At this point, we need to read in the info file and probably
0054     // pass it to some standard parser in the JCalibration base class.
0055     // Any volunteers?
0056     // Among other things, this should contain the range of runs for which
0057     // this calibration is valid. For now, just set the found run to run_requested.
0058     run_number = GetRun(); // redunant, yes, but it's just a place holder
0059 
0060     // Close info file
0061     f.close();
0062 }
0063 
0064 //---------------------------------
0065 // ~JCalibrationFile    (Destructor)
0066 //---------------------------------
0067 JCalibrationFile::~JCalibrationFile()
0068 {
0069 
0070 }
0071 
0072 //---------------------------------
0073 // GetCalib
0074 //---------------------------------
0075 bool JCalibrationFile::GetCalib(string namepath, map<string, string> &svals, uint64_t /*event_number*/)
0076 {
0077     /// Open file specified by namepath (and the url passed to us in the
0078     /// constructor) and read in the calibration constants in plain
0079     /// ascii from it. Values are copied into <i>svals</i>.
0080     ///
0081     /// File format can be one of two forms. Either one value per line
0082     /// or one key/value pair per line. If only one value is found,
0083     /// the line number is used as the key. When both key and value
0084     /// are present, they must be separated by white space.
0085     /// Lines starting with # are considered comments and are ignored.
0086 
0087     // Clear svals map.
0088     svals.clear();
0089 
0090     // Open file
0091     string fname = basedir + namepath;
0092     ifstream f(fname.c_str());
0093     if(!f.is_open()){
0094         string mess = "Unable to open \"" + fname + "\"!";
0095         throw JException(mess);
0096         return true; // never gets to this line
0097     }
0098 
0099     // Loop over all lines in the file
0100     int nvals = 0;
0101     string line;
0102     while(getline(f, line, '\n')){
0103         if(line.length()==0)continue;
0104         if(line[0] == '#')continue;
0105 
0106         // Try and break up the line into a key and a value.
0107         stringstream ss(line);
0108         string key, val;
0109         ss>>key;
0110         if(key==line){
0111             // Must be just one item here. Copy it to val and set key to "nvals"
0112             // colum names are set to position with zero padding so map maintains
0113             // correct order (up to 9999)
0114             val = key;
0115             stringstream sss;
0116             sss << setw(4) << setfill('0') << nvals; // This tricky bit is just to convert from an int to a string
0117             sss>>key;
0118         }else{
0119             // Looks like there is a key and a value here.
0120             ss>>val;
0121         }
0122 
0123         // Add this key value pair to svals.
0124         svals[key]=val;
0125 
0126         nvals++;
0127     }
0128 
0129     // Close file
0130     f.close();
0131 
0132     return false;
0133 }
0134 
0135 //---------------------------------
0136 // GetCalib
0137 //---------------------------------
0138 bool JCalibrationFile::GetCalib(string namepath, vector<string> &svals, uint64_t /*event_number*/)
0139 {
0140     /// Open file specified by namepath (and the url passed to us in the
0141     /// constructor) and read in the calibration constants in plain
0142     /// ascii from it. Values are copied into <i>svals</i>.
0143     ///
0144     /// File format can be one of two forms. Either one value per line
0145     /// or one key/value pair per line. If only one value is found,
0146     /// the line number is used as the key. When both key and value
0147     /// are present, they must be separated by white space.
0148     /// Lines starting with # are considered comments and are ignored.
0149 
0150     // n.b. this was copied from the above and only the "push_back" line modified
0151     // to ignore the key and store only the values in the order they are encountered
0152 
0153     // Clear svals map.
0154     svals.clear();
0155 
0156     // Open file
0157     string fname = basedir + namepath;
0158     ifstream f(fname.c_str());
0159     if(!f.is_open()){
0160         string mess = "Unable to open \"" + fname + "\"!";
0161         throw JException(mess);
0162         return true; // never gets to this line
0163     }
0164 
0165     // Loop over all lines in the file
0166     int nvals = 0;
0167     string line;
0168     while(getline(f, line, '\n')){
0169         if(line.length()==0)continue;
0170         if(line[0] == '#')continue;
0171 
0172         // Try and break up the line into a key and a value.
0173         stringstream ss(line);
0174         string key, val;
0175         ss>>key;
0176         if(key==line){
0177             // Must be just one item here. Copy it to val and set key to "nvals"
0178             // colum names are set to position with zero padding so map maintains
0179             // correct order (up to 9999)
0180             val = key;
0181             stringstream sss;
0182             sss << setw(4) << setfill('0') << nvals; // This tricky bit is just to convert from an int to a string
0183             sss>>key;
0184         }else{
0185             // Looks like there is a key and a value here.
0186             ss>>val;
0187         }
0188 
0189         // Add this key value pair to svals.
0190         svals.push_back(val);
0191 
0192         nvals++;
0193     }
0194 
0195     // Close file
0196     f.close();
0197 
0198     return false;
0199 }
0200 
0201 //---------------------------------
0202 // GetCalib
0203 //---------------------------------
0204 bool JCalibrationFile::GetCalib(string namepath, vector< map<string, string> > &svals, uint64_t /*event_number*/)
0205 {
0206     /// Open file specified by namepath (and the url passed to us in the
0207     /// constructor) and read in a table of calibration constants in plain
0208     /// ascii. Values are copied into <i>svals</i>.
0209     ///
0210     /// File format must be in a table form with the same number of items
0211     /// on every line. Items should be separated by white space.
0212     /// Lines starting with # are considered comments and are ignored
0213     /// except for the last commented line before the first line containing
0214     /// data. This is parsed (if present) to determine the column names
0215     /// that will be used to index the map. If an appropriate column
0216     /// names line is not found, numerical column names will be assigned
0217     /// starting from zero.
0218 
0219     // Clear svals map.
0220     svals.clear();
0221 
0222     // Open file
0223     string fname = basedir + namepath;
0224     ifstream f(fname.c_str());
0225     if(!f.is_open()){
0226         string mess = "Unable to open \"" + fname + "\"!";
0227         throw JException(mess);
0228         return true; // never gets to this line
0229     }
0230 
0231     // Loop over all lines in the file
0232     string colnamesline;
0233     vector<string> colnames;
0234     string line;
0235     while(getline(f, line, '\n')){
0236         if(line.length()==0)continue;
0237         if(line.substr(0,2) == "#%"){
0238             // Parse special comment line with column names
0239             string s(line);
0240             s.erase(0,2); // remove leading "#%"
0241             stringstream sss(s);
0242             colnames.clear();
0243             string colname;
0244             while(sss>>colname)colnames.push_back(colname);
0245         }
0246         if(line[0] == '#')continue;
0247 
0248         // Break up the line into individual values and create a map indexed by column names
0249         stringstream ss(line);
0250         string val;
0251         map<string, string> mval;
0252         unsigned int icol = 0;
0253         while(ss>>val){
0254             // Make sure a name exists for this column
0255             if(colnames.size()<=icol){
0256                 stringstream sss;
0257                 sss << setw(4) << setfill('0') << icol;
0258                 colnames.push_back(sss.str());
0259             }
0260             mval[colnames[icol++]] = val;
0261         }
0262 
0263         // Push row onto vector
0264         svals.push_back(mval);
0265     }
0266 
0267     // Close file
0268     f.close();
0269 
0270     // Check that all rows have the same number of columns
0271     unsigned int ncols = svals[0].size();
0272     for(unsigned int i=1; i<svals.size(); i++){
0273         if(svals[i].size() != ncols){
0274             LOG<<"Number of columns not the same for all rows in "<<fname<<LOG_END;
0275             return true;
0276         }
0277     }
0278 
0279     return false;
0280 }
0281 
0282 //---------------------------------
0283 // GetCalib
0284 //---------------------------------
0285 bool JCalibrationFile::GetCalib(string namepath, vector< vector<string> > &svals, uint64_t /*event_number*/)
0286 {
0287     /// Open file specified by namepath (and the url passed to us in the
0288     /// constructor) and read in a table of calibration constants in plain
0289     /// ascii. Values are copied into <i>svals</i>.
0290     ///
0291     /// File format must be in a table form with the same number of items
0292     /// on every line. Items should be separated by white space.
0293     /// Lines starting with # are considered comments and are ignored
0294     /// except for the last commented line before the first line containing
0295     /// data. This is parsed (if present) to determine the column names
0296     /// that will be used to index the map. If an appropriate column
0297     /// names line is not found, numerical column names will be assigned
0298     /// starting from zero.
0299 
0300     // n.b. this was copied from the above and only the minimal changes made
0301     // to ignore the key and store only the values in the order they are encountered
0302 
0303     // Clear svals map.
0304     svals.clear();
0305 
0306     // Open file
0307     string fname = basedir + namepath;
0308     ifstream f(fname.c_str());
0309     if(!f.is_open()){
0310         string mess = "Unable to open \"" + fname + "\"!";
0311         throw JException(mess);
0312         return true; // never gets to this line
0313     }
0314 
0315     // Loop over all lines in the file
0316     string colnamesline;
0317     vector<string> colnames;
0318     string line;
0319     while(getline(f, line, '\n')){
0320         if(line.length()==0)continue;
0321         if(line.substr(0,2) == "#%"){
0322             // Parse special comment line with column names
0323             string s(line);
0324             s.erase(0,2); // remove leading "#%"
0325             stringstream sss(s);
0326             colnames.clear();
0327             string colname;
0328             while(sss>>colname)colnames.push_back(colname);
0329         }
0330         if(line[0] == '#')continue;
0331 
0332         // Break up the line into individual values and create a map indexed by column names
0333         stringstream ss(line);
0334         string val;
0335         vector<string> mval;
0336         unsigned int icol = 0;
0337         while(ss>>val){
0338             // Make sure a name exists for this column
0339             if(colnames.size()<=icol){
0340                 stringstream sss;
0341                 sss << setw(4) << setfill('0') << icol;
0342                 colnames.push_back(sss.str());
0343             }
0344             mval.push_back(val);
0345         }
0346 
0347         // Push row onto vector
0348         svals.push_back(mval);
0349     }
0350 
0351     // Close file
0352     f.close();
0353 
0354     // Check that all rows have the same number of columns
0355     unsigned int ncols = svals[0].size();
0356     for(unsigned int i=1; i<svals.size(); i++){
0357         if(svals[i].size() != ncols){
0358             LOG<<"Number of columns not the same for all rows in "<<fname<<LOG_END;
0359             return true;
0360         }
0361     }
0362 
0363     return false;
0364 }
0365 
0366 //---------------------------------
0367 // PutCalib
0368 //---------------------------------
0369 bool JCalibrationFile::JCalibrationFile::PutCalib(string namepath, int32_t run_min, int32_t run_max, uint64_t /*event_min*/, uint64_t /*event_max*/, string &author, map<string, string> &svals, string comment)
0370 {
0371     // Open the item file creating the directory path if needed and
0372     // writing a header to it.
0373     ofstream *fptr = CreateItemFile(namepath, run_min, run_max, author, comment);
0374     if(!fptr)return true;
0375 
0376     // Loop over values
0377     map<string, string>::iterator iter;
0378     for(iter=svals.begin(); iter!=svals.end(); iter++){
0379         (*fptr)<<iter->first<<"  "<<iter->second<<endl;
0380     }
0381 
0382     // Close file
0383     fptr->close();
0384     delete fptr;
0385 
0386     return true;
0387 }
0388 
0389 //---------------------------------
0390 // PutCalib
0391 //---------------------------------
0392 bool JCalibrationFile::JCalibrationFile::PutCalib(string namepath, int32_t run_min, int32_t run_max, uint64_t /*event_min*/, uint64_t /*event_max*/, string &author, vector< map<string, string> > &svals, string comment)
0393 {
0394     // We need at least one element to make this worthwhile
0395     if(svals.size()<1)return true;
0396 
0397     // Open the item file creating the directory path if needed and
0398     // writing a header to it.
0399     ofstream *fptr = CreateItemFile(namepath, run_min, run_max, author, comment);
0400     if(!fptr)return true;
0401 
0402     // Write list of column names from first element
0403     vector<string> colnames;
0404     map<string,string>::const_iterator iter = svals[0].begin();
0405     for(; iter!=svals[0].end(); iter++){
0406         colnames.push_back(iter->first);
0407     }
0408 
0409     // Write column names to header
0410     (*fptr)<<"#% ";
0411     for(unsigned int i=0; i<colnames.size(); i++){
0412         (*fptr)<<" "<<colnames[i];
0413     }
0414     (*fptr)<<endl;
0415 
0416     // Loop over values
0417     for(unsigned int j=0; j<svals.size(); j++){
0418         map<string, string> &mvals = svals[j];
0419         for(unsigned int i=0; i<colnames.size(); i++){
0420             (*fptr)<<mvals[colnames[i]]<<"  ";
0421         }
0422         (*fptr)<<endl;
0423     }
0424 
0425     // Close file
0426     fptr->close();
0427     delete fptr;
0428 
0429     return true;
0430 }
0431 
0432 //---------------------------------
0433 // CreateItemFile
0434 //---------------------------------
0435 ofstream* JCalibrationFile::CreateItemFile(string namepath, int32_t run_min, int32_t run_max, string &author, string &comment)
0436 {
0437     // Make sure the directory path exists for this namepath
0438     MakeDirectoryPath(namepath);
0439 
0440     // Open file, overwriting any existing file
0441     string fname = basedir + namepath;
0442     ofstream *fptr = new ofstream(fname.c_str());
0443     ofstream &f = *fptr;
0444     if(!f.is_open()){
0445         LOG<<"Unable to open \""<<fname<<"\"!"<<LOG_END;
0446         delete fptr;
0447         return NULL;
0448     }
0449 
0450     // The comment string may come in several lines and we want each of those to be
0451     // prefixed with a comment character "#".
0452     vector<string>lines;
0453     size_t start = 0;
0454     size_t end;
0455     while((end=comment.find('\n', start))!=string::npos){
0456         lines.push_back(comment.substr(start, end-start));
0457         start = end+1;
0458     }
0459     if(start<comment.size())lines.push_back(comment.substr(start, (comment.size())-start));
0460 
0461     // Write comments in header
0462     time_t now = time(NULL);
0463     string host = "unknown";
0464     const char *hostptr = getenv("HOST");
0465     if(hostptr)host = hostptr;
0466     f<<"#"<<endl;
0467     f<<"# Created: "<<ctime(&now);
0468     f<<"# Host: "<<host<<endl;
0469     f<<"# Author: "<<author<<endl;
0470     f<<"# Namepath: "<<namepath<<endl;
0471     f<<"# Context: "<<GetContext()<<endl;
0472     f<<"# Run range: "<<run_min<<" - "<<run_max<<endl;
0473     f<<"#"<<endl;
0474     f<<"# Comment:"<<endl;
0475     for(unsigned int i=0; i<lines.size(); i++){
0476         f<<"# "<<lines[i];
0477         if(lines[i][lines.size()-1] != '\n')f<<endl;
0478     }
0479     f<<"#"<<endl;
0480 
0481     return fptr;
0482 }
0483 
0484 //---------------------------------
0485 // MakeDirectoryPath
0486 //---------------------------------
0487 void JCalibrationFile::MakeDirectoryPath(string namepath)
0488 {
0489     /// Create all subdirectories of basedir needed to hold the
0490     /// item identified by the given namepath.
0491 
0492     // Make sure full directory structure exists. The basedir is
0493     // created first then each subdirectory within that.
0494     vector<string>dirs;
0495     size_t start = 0;
0496     size_t end;
0497     while((end=namepath.find('/', start))!=string::npos){
0498         dirs.push_back(namepath.substr(start, end-start));
0499         start = end+1;
0500     }
0501     if(start<namepath.size())dirs.push_back(namepath.substr(start, (namepath.size())-start));
0502 
0503     // Last element in dirs should be item name. Check that dirs
0504     // has at least one element (so we can use dirs.size()-1 below)
0505     // and bail now if it doesn't.
0506     if(dirs.size()<1)return;
0507 
0508     // Loop over subdirectories, creating them as we go
0509     string dirname = basedir;
0510     mkdir(dirname.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
0511     for(unsigned int i=0; i<dirs.size()-1; i++){
0512         dirname += string("/") + dirs[i];
0513         mkdir(dirname.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
0514     }
0515 }
0516 
0517 //---------------------------------
0518 // GetListOfNamepaths
0519 //---------------------------------
0520 void JCalibrationFile::GetListOfNamepaths(vector<string> &namepaths)
0521 {
0522     /// Get a list of the available namepaths by traversing the
0523     /// directory stucture looking for files.
0524     /// Note that this does NOT check whether the files are of
0525     /// a vaild format.
0526     AddToNamepathList(basedir.substr(0,basedir.length()-1), namepaths);
0527 }
0528 
0529 //---------------------------------
0530 // AddToNamepathList
0531 //---------------------------------
0532 void JCalibrationFile::AddToNamepathList(string dirname, vector<string> &namepaths)
0533 {
0534     /// Add the files from the specified directory to the list of namepaths.
0535     /// and then recall ourselves for any directories found.
0536 
0537     // Open the directory
0538     DIR *dir = opendir(dirname.c_str());
0539     if(!dir)return;
0540 
0541     // Loop over directory entries
0542     struct dirent *dp;
0543     while((dp=readdir(dir))!=NULL){
0544         string name(dp->d_name);
0545         if(name=="." || name==".." || name==".svn")continue; // ignore this directory and its parent
0546         if(name=="info.xml" || name==".DS_Store")continue;
0547 
0548         // Check if this is a directory and if so, recall to add those
0549         // namepaths as well.
0550         struct stat st;
0551         string fullpath = dirname+"/"+name;
0552         if(stat(fullpath.c_str(),&st) == 0){
0553             if(st.st_mode & S_IFDIR){
0554                 // yep, this is a directory
0555                 AddToNamepathList(dirname+"/"+name, namepaths);
0556 
0557                 // go to next iteration of loop over current directory's contents
0558                 continue;
0559             }
0560         }
0561 
0562         // If we get here then this item is not a directory. Assume it is a valid
0563         // namepath and add it to the list
0564         namepaths.push_back(fullpath.substr(basedir.length()));
0565     }
0566     closedir(dir);
0567 }
0568