Newer
Older
Anders Markvardsen
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidDataHandling/LoadLog.h"
#include "MantidAPI/LogParser.h"
Anders Markvardsen
committed
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/PropertyWithValue.h"
Anders Markvardsen
committed
#include "MantidDataObjects/Workspace2D.h"
Roman Tolchenov
committed
#include "MantidKernel/Glob.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/FileProperty.h"
Gigg, Martyn Anthony
committed
#include "LoadRaw/isisraw2.h"
Anders Markvardsen
committed
Gigg, Martyn Anthony
committed
#include "Poco/File.h"
#include "Poco/Path.h"
#include "Poco/DirectoryIterator.h"
Gigg, Martyn Anthony
committed
#include "Poco/DateTimeParser.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/RegularExpression.h"
Anders Markvardsen
committed
#include <fstream> // used to get ifstream
Anders Markvardsen
committed
namespace Mantid
{
namespace DataHandling
{
// Register the algorithm into the algorithm factory
DECLARE_ALGORITHM(LoadLog)
Anders Markvardsen
committed
using namespace Kernel;
using API::WorkspaceProperty;
Roman Tolchenov
committed
using API::MatrixWorkspace;
using API::MatrixWorkspace_sptr;
using DataObjects::Workspace2D;
/// Empty default constructor
LoadLog::LoadLog()
{}
/// Initialisation method.
void LoadLog::init()
{
Russell Taylor
committed
// When used as a sub-algorithm the workspace name is not used - hence the "Anonymous" to satisfy the validator
Steve Williams
committed
declareProperty(
new WorkspaceProperty<MatrixWorkspace>("Workspace","Anonymous",Direction::InOut),
"The name of the workspace to which the log data will be added");
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
std::vector<std::string> exts(4, "");
exts[0] = "txt";
exts[1] = "raw";
exts[2] = "s*";
exts[3] = "add";
declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts),
Gigg, Martyn Anthony
committed
"The filename (including its full or relative path) of either an ISIS log file\n"
"or an ISIS raw file. If a raw file is specified all log files associated with\n"
"that raw file are loaded into the specified workspace. The file extension must\n"
"either be .raw or .s when specifying a raw file");
Sofia Antony
committed
//declareProperty("Period",1);
Gigg, Martyn Anthony
committed
//@cond NODOC
namespace
{
struct FileMatcher
{
FileMatcher(const std::string & expression) : m_expression(expression) {}
bool operator()(const std::string & test) const
{
Poco::RegularExpression regex(m_expression, Poco::RegularExpression::RE_CASELESS);
return regex.match(test);
}
private:
FileMatcher();
const std::string m_expression;
};
}
//@endcond
/** Executes the algorithm. Reading in ISIS log file(s)
*
* @throw Mantid::Kernel::Exception::FileError Thrown if file is not recognised to be a raw datafile or log file
* @throw std::runtime_error Thrown with Workspace problems
*/
void LoadLog::exec()
{
// Retrieve the filename from the properties and perform some initial checks on the filename
Anders Markvardsen
committed
m_filename = getPropertyValue("Filename");
Anders Markvardsen
committed
Gigg, Martyn Anthony
committed
// File property checks whether the given path exists, just check that is actually a file
Gigg, Martyn Anthony
committed
Poco::File l_path( m_filename );
if ( l_path.isDirectory() )
Anders Markvardsen
committed
{
g_log.error("In LoadLog: " + m_filename + " must be a filename not a directory.");
throw Exception::FileError("Filename is a directory:" , m_filename);
}
Anders Markvardsen
committed
// Get the input workspace and retrieve sample from workspace.
// the log file(s) will be loaded into the Sample container of the workspace
Roman Tolchenov
committed
const MatrixWorkspace_sptr localWorkspace = getProperty("Workspace");
Sofia Antony
committed
//boost::shared_ptr<API::Sample> sample = localWorkspace->getSample();
// If m_filename is the filename of a raw datafile then search for potential log files
// in the directory of this raw datafile. Otherwise check if m_filename is a potential
// log file. Add the filename of these potential log files to: potentialLogFiles.
Gigg, Martyn Anthony
committed
std::set<std::string> potentialLogFiles;
// start the process or populating potential log files into the container: potentialLogFiles
Gigg, Martyn Anthony
committed
std::string l_filenamePart = Poco::Path(l_path.path()).getFileName();// get filename part only
Sofia Antony
committed
std::string threecolumnLogfile;
bool rawFile = false;// Will be true if Filename property is a name of a RAW file
Gigg, Martyn Anthony
committed
if ( isAscii(m_filename) && l_filenamePart.find("_") != std::string::npos )
{
// then we will assume that m_filename is an ISIS log file
Gigg, Martyn Anthony
committed
potentialLogFiles.insert(m_filename);
Gigg, Martyn Anthony
committed
else
Gigg, Martyn Anthony
committed
// then we will assume that m_filename is an ISIS raw file. The file validator will have warned the user if the
// extension is not one of the suggested ones
rawFile = true;
// strip out the raw data file identifier
Gigg, Martyn Anthony
committed
std::string l_rawID("");
size_t idx = l_filenamePart.rfind('.');
if( idx != std::string::npos )
{
l_rawID = l_filenamePart.substr(0, l_filenamePart.rfind('.'));
}
else
{
l_rawID = l_filenamePart;
}
/// check for alternate data stream exists for raw file
/// if exists open the stream and read log files name from ADS
if(adsExists())potentialLogFiles=getLogfilenamesfromADS();
else
{
// look for log files in the directory of the raw datafile
std::string pattern(l_rawID + "_*.txt");
Poco::Path dir(m_filename);
dir.makeParent();
try
{
Kernel::Glob::glob(Poco::Path(dir).resolve(pattern),potentialLogFiles);
}
catch(std::exception &)
{
}
Gigg, Martyn Anthony
committed
if( potentialLogFiles.empty() )
{
Poco::RegularExpression regex(l_rawID + "_.*\\.txt", Poco::RegularExpression::RE_CASELESS );
Poco::DirectoryIterator end_iter;
for ( Poco::DirectoryIterator dir_itr(Poco::Path(m_filename).parent()); dir_itr != end_iter; ++dir_itr )
{
if ( !Poco::File(dir_itr->path() ).isFile() ) continue;
l_filenamePart = Poco::Path(dir_itr->path()).getFileName();
if ( regex.match(l_filenamePart) )
{
potentialLogFiles.insert( dir_itr->path() );
}
}
Gigg, Martyn Anthony
committed
}
}
//.if a .log file exists in the raw file directory
Sofia Antony
committed
if (threeColumnFormatLogFileExists())
{
threecolumnLogfile=getThreecolumnFormatLogFile();
Sofia Antony
committed
std::set<std::string> blockFileNameList=createthreecolumnFileLogProperty(threecolumnLogfile,localWorkspace->mutableSample());
Sofia Antony
committed
//remove the file name from potential logfiles list if it's there in the .log file.
std::set<std::string>::const_iterator itr;
for(itr=blockFileNameList.begin();itr!=blockFileNameList.end();++itr)
{
std::set<std::string>::iterator litr= find(potentialLogFiles.begin(),potentialLogFiles.end(),*itr);
if(litr!=potentialLogFiles.end())
{ potentialLogFiles.erase(litr);}
}
}
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
//If there are no log files by now, we have nothing else to do
if( potentialLogFiles.empty() ) return;
Roman Tolchenov
committed
Gigg, Martyn Anthony
committed
//Do a quick search for the icpevent file
std::string icpevent_file_name("");
std::set<std::string>::const_iterator icpfile = find_if(potentialLogFiles.begin(), potentialLogFiles.end(), FileMatcher(std::string(".*icpevent.*")));
if( icpfile != potentialLogFiles.end() )
{
icpevent_file_name = *icpfile;
Gigg, Martyn Anthony
committed
API::LogParser parser(icpevent_file_name);
// Add mantid-created logs
Sofia Antony
committed
/*int period = getproperty("period");
property* log = parser.createperiodlog(period);
Gigg, Martyn Anthony
committed
if (log)
Sofia Antony
committed
sample->addlogdata(log);
}*/
m_periods=parser.getPeriodsProperty();
localWorkspace->mutableSample().addLogData(parser.createAllPeriodsLog());
localWorkspace->mutableSample().addLogData(parser.createRunningLog());
Gigg, Martyn Anthony
committed
// Extract the common part of log file names (the workspace name)
std::string ws_name = Poco::Path(m_filename).getFileName();
ws_name.erase(ws_name.find_last_of('.'));
ws_name += '_';
size_t n_common_chars = ws_name.size();
Anders Markvardsen
committed
Gigg, Martyn Anthony
committed
// Attempt to load the content of each potential log file into the Sample object
std::set<std::string>::const_iterator logs_end = potentialLogFiles.end();
for(std::set<std::string>::const_iterator logs_itr = potentialLogFiles.begin(); logs_itr != logs_end; ++logs_itr)
Gigg, Martyn Anthony
committed
std::string filename = *logs_itr;
Gigg, Martyn Anthony
committed
std::ifstream inLogFile(filename.c_str());
Roman Tolchenov
committed
if (!inLogFile)
{
// Unable to open file
Gigg, Martyn Anthony
committed
g_log.error("Unable to open file " + filename);
throw Exception::FileError("Unable to open file:" , filename);
}
// figure out if second column is a number or a string
std::string aLine;
if( std::getline(inLogFile, aLine, '\n') )
{
if ( !isDateTimeString(aLine) )
{
Gigg, Martyn Anthony
committed
g_log.warning("File" + filename + " is not a standard ISIS log file. Expected to be a two column file.");
Gigg, Martyn Anthony
committed
std::string dateAndTime;
std::stringstream ins(aLine);
ins >> dateAndTime;
Anders Markvardsen
committed
// read in what follows the date-time string in the log file and figure out
// what type it is
std::string whatType;
ins >> whatType;
Gigg, Martyn Anthony
committed
kind l_kind = classify(whatType);
if ( LoadLog::string != l_kind && LoadLog::number != l_kind )
Gigg, Martyn Anthony
committed
g_log.warning("ISIS log file contains unrecognised second column entries: " + filename);
inLogFile.close();
continue;
Gigg, Martyn Anthony
committed
try
{
Gigg, Martyn Anthony
committed
// Make the property name by removing the workspce name and file extension from the log filename
std::string log_name = Poco::Path(Poco::Path(filename).getFileName()).getBaseName();
if (rawFile)
{
log_name.erase(0, n_common_chars);
}
Property* log = parser.createLogProperty(*logs_itr,stringToLower(log_name));
if (log)
{
Sofia Antony
committed
//sample->addLogData(log);
localWorkspace->mutableSample().addLogData(log);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
catch(std::exception&)
Gigg, Martyn Anthony
committed
continue;
Gigg, Martyn Anthony
committed
}
inLogFile.close();
} // end for
Anders Markvardsen
committed
// operation was a success and ended normally
return;
}
Sofia Antony
committed
/** this method looks in the rawfile directory for rawfilename.log (three column format )file
@returns true if the file exists
*/
bool LoadLog::threeColumnFormatLogFileExists()
{
std::string rawID;
Sofia Antony
committed
size_t pos = m_filename.find(".");
if(pos!=std::string::npos)
rawID=m_filename.substr(0,pos);
Sofia Antony
committed
// append .log to get the .log file name
std::string logfileName=rawID+".log";
Sofia Antony
committed
if (Poco::File(logfileName).exists())
{
//validate the file
std::ifstream inLogFile(logfileName.c_str());
if (!inLogFile)
{ throw Exception::FileError("Unable to open file:" ,logfileName );}
//check if first 19 characters of a string is data-time string according to yyyy-mm-ddThh:mm:ss
std::string aLine;
kind l_kind(LoadLog::empty);
//if( std::getline(inLogFile, aLine, '\n') )
while(Mantid::API::extractToEOL(inLogFile,aLine))
Sofia Antony
committed
if ( !isDateTimeString(aLine) )
{ g_log.warning("File" + logfileName + " is not a standard ISIS log file. Expected to be a file starting with DateTime String format.");
inLogFile.close();
return false;
Sofia Antony
committed
}
std::stringstream ins(aLine);
std::string firstcolumn;
ins >> firstcolumn;
// read in what follows the date-time string in the log file and figure out
// what type it is
std::string secondcolumn;
ins >> secondcolumn;
l_kind = classify(secondcolumn);
if ( LoadLog::string != l_kind )
{
g_log.warning("ISIS log file contains unrecognised second column entries: " + logfileName);
inLogFile.close();
return false;
Sofia Antony
committed
}
std::string thirdcolumn;
ins>>thirdcolumn;
l_kind = classify(thirdcolumn);
if ( LoadLog::string != l_kind && LoadLog::number!=l_kind)
{
g_log.warning("ISIS log file contains unrecognised third column entries: " + logfileName);
Sofia Antony
committed
inLogFile.close();
return false;
Sofia Antony
committed
}
++count;
if(count==2) ///reading first two lines from file for validation purpose.
break;
Sofia Antony
committed
}
return true;
}
else return false;
}
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/* this method looks for ADS with name checksum exists
*@return if ADS stream checkum exists
*/
bool LoadLog::adsExists()
{
std::string adsname(m_filename+":checksum");
std::ifstream adstream(adsname.c_str());
if(!adstream)
{return false;
}
return true;
}
/* this method reads the checksum ADS associated with the
* raw file and returns the filensmes of the log files
*@return list of logfile names.
*/
std::set<std::string> LoadLog::getLogfilenamesfromADS()
{
std::string adsname(m_filename+":checksum");
std::ifstream adstream(adsname.c_str());
if(!adstream)
return std::set<std::string>();
std::string str;
std::string path;
std::string logFile;
std::set<std::string>logfilesList;
Poco::Path logpath(m_filename);
std::string p=logpath.home();
size_t pos =m_filename.find_last_of("/");
if(pos==std::string::npos)
{
pos =m_filename.find_last_of("\\");
}
if(pos!=std::string::npos)
path=m_filename.substr(0,pos);
while(Mantid::API::extractToEOL(adstream,str))
{
std::string fileName;
pos = str.find("*");
if(pos==std::string::npos)
continue;
fileName=str.substr(pos+1,str.length()-pos);
pos= fileName.find("txt");
if(pos==std::string::npos)
continue;
logFile=path+"\\"+fileName;
if(logFile.empty())
continue;
logfilesList.insert(logFile);
}
return logfilesList;
}
Sofia Antony
committed
/** this method returns the name of three column log file
@returns file name of the.log file
*/
std::string LoadLog::getThreecolumnFormatLogFile()
{
size_t pos = m_filename.find(".");
std::string rawID;
if(pos!=std::string::npos)
rawID=m_filename.substr(0,pos);
Sofia Antony
committed
// append .log to get the .log file name
std::string logfileName=rawID+".log";
if (Poco::File(logfileName).exists())
return logfileName;
else return "";
}
/**
This method reads the.log file and creates timeseries property and sets that to the sample object
*@param logfile three column log(.log) file name.
*@param sample sample object
@returns list of logfiles which exists as blockname in the .log file
*/
Sofia Antony
committed
std::set<std::string> LoadLog::createthreecolumnFileLogProperty(const std::string& logfile,API::Sample& sample)
{
Sofia Antony
committed
std::set<std::string> blockFileNameList;
Sofia Antony
committed
std::string sdata,str;
std::string propname;
Mantid::Kernel::TimeSeriesProperty<double>* logd=0;
Mantid::Kernel::TimeSeriesProperty<std::string>* logs=0;
std::map<std::string,Kernel::TimeSeriesProperty<double>*> dMap;
std::map<std::string,Kernel::TimeSeriesProperty<std::string>*> sMap;
typedef std::pair<std::string,Kernel::TimeSeriesProperty<double>* > dpair;
typedef std::pair<std::string,Kernel::TimeSeriesProperty<std::string>* > spair;
Sofia Antony
committed
Sofia Antony
committed
std::basic_string <char>::size_type pos=m_filename.find(".");
bool isNumeric(false);
std::string path =m_filename.substr(0,pos);
Sofia Antony
committed
std::ifstream file(logfile.c_str());
if (!file)
{ g_log.warning()<<"Cannot open log file "<<logfile<<"\n";
return std::set<std::string>();
}
while(Mantid::API::extractToEOL(file,str))
{
Sofia Antony
committed
if (!Kernel::TimeSeriesProperty<double>::isTimeString(str) || (str[0]=='#'))
Sofia Antony
committed
{ //if the line doesn't start with a time read the next line
continue;
}
std::stringstream line(str);
std::string timecolumn;
line>>timecolumn;
std::string blockcolumn;
line>>blockcolumn;
std::string valuecolumn;
line>>valuecolumn;
sdata=valuecolumn;
Russell Taylor
committed
/////column two in .log file is called block column
/////if any .txt file with rawfilename_blockcolumn.txt exists
///// donot load that txt files
///// blockFileNameList conatins the file names to be removed from potentiallogfiles list.
//std::string blockcolumnFileName=path+"_"+blockcolumn+".txt";
//if(blockcolumnFileExists(blockcolumnFileName))
//{ blockFileNameList.insert(blockcolumnFileName);
//}
Sofia Antony
committed
propname=stringToLower(blockcolumn);
//check if the data is numeric
std::istringstream istr(valuecolumn);
double dvalue;
istr >> dvalue;
isNumeric = !istr.fail();
if (isNumeric)
{
std::map<std::string,Kernel::TimeSeriesProperty<double>*>::iterator ditr=dMap.find(propname);
if(ditr!=dMap.end())
{ Kernel::TimeSeriesProperty<double>* p=ditr->second;
if(p)p->addValue(timecolumn,dvalue);
Sofia Antony
committed
dMap.insert(dpair(propname,p));
Sofia Antony
committed
}
else
{ logd = new Kernel::TimeSeriesProperty<double>(propname);
logd->addValue(timecolumn,dvalue);
Sofia Antony
committed
dMap.insert(dpair(propname,logd));
Russell Taylor
committed
std::string blockcolumnFileName=path+"_"+blockcolumn+".txt";
if(blockcolumnFileExists(blockcolumnFileName))
{
blockFileNameList.insert(blockcolumnFileName);
}
Sofia Antony
committed
}
Sofia Antony
committed
Sofia Antony
committed
}
else
{
std::map<std::string,Kernel::TimeSeriesProperty<std::string>*>::iterator sitr=sMap.find(propname);
if(sitr!=sMap.end())
{ Kernel::TimeSeriesProperty<std::string>* prop=sitr->second;
if(prop) prop->addValue(timecolumn,valuecolumn);
Sofia Antony
committed
sMap.insert(spair(propname,prop));
Sofia Antony
committed
}
else
{ logs = new Kernel::TimeSeriesProperty<std::string>(propname);
logs->addValue(timecolumn,valuecolumn);
Sofia Antony
committed
sMap.insert(spair(propname,logs)); //sMap[propname]=logs;
}
Russell Taylor
committed
std::string blockcolumnFileName=path+"_"+blockcolumn+".txt";
if(blockcolumnFileExists(blockcolumnFileName))
{
blockFileNameList.insert(blockcolumnFileName);
}
}
Sofia Antony
committed
}
try
{
std::map<std::string,Kernel::TimeSeriesProperty<double>*>::const_iterator itr=dMap.begin();
for(;itr!=dMap.end();++itr)
Sofia Antony
committed
{sample.addLogData(itr->second);
}
std::map<std::string,Kernel::TimeSeriesProperty<std::string>*>::const_iterator sitr=sMap.begin();
for(;sitr!=sMap.end();++sitr)
{
Sofia Antony
committed
sample.addLogData(sitr->second);
}
}
catch(std::invalid_argument &e)
Sofia Antony
committed
{
g_log.warning()<<e.what();
Sofia Antony
committed
}
catch(Exception::ExistsError&e)
Sofia Antony
committed
{
g_log.warning()<<e.what();
Sofia Antony
committed
}
return blockFileNameList;
}
/** this method looks for file with second column(block column) name exists in the raw file directory
*@param fileName -name of the file
*@returns true if the file exists
*/
bool LoadLog::blockcolumnFileExists(const std::string& fileName)
Sofia Antony
committed
{ if (Poco::File(fileName).exists())
return true;
else return false;
}
Anders Markvardsen
committed
/** Takes as input a string and try to determine what type it is.
* @param s The input string
* @param s string to be classified
* @return A enum kind which tells what type the string is
*/
LoadLog::kind LoadLog::classify(const std::string& s)
{
Gigg, Martyn Anthony
committed
if( s.empty() )
{
return LoadLog::empty;
}
using std::string;
const string lower("abcdefghijklmnopqrstuvwxyz");
const string upper("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
const string letters = lower + upper + '_';
if (letters.find_first_of(s) != string::npos)
Gigg, Martyn Anthony
committed
{
return LoadLog::string;
}
Gigg, Martyn Anthony
committed
{
return LoadLog::number;
}
/** change each element of the string to lower case
* @param strToConvert The input string
* @returns The string but with all characters in lower case
*/
std::string LoadLog::stringToLower(std::string strToConvert)
{
Gigg, Martyn Anthony
committed
std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), tolower);
return strToConvert;
Anders Markvardsen
committed
Gigg, Martyn Anthony
committed
/** Checks whether filename is a simple text file
* @param filename The filename to inspect
Gigg, Martyn Anthony
committed
* @returns true if the filename has the .txt extension
Gigg, Martyn Anthony
committed
bool LoadLog::isAscii(const std::string& filename)
Gigg, Martyn Anthony
committed
FILE* file = fopen(filename.c_str(), "rb");
char data[256];
int n = fread(data, 1, sizeof(data), file);
char *pend = &data[n];
/*
* Call it a binary file if we find a non-ascii character in the
* first 256 bytes of the file.
*/
for( char *p = data; p < pend; ++p )
{
unsigned long ch = (unsigned long)*p;
if( !(ch <= 0x7F) )
{
return false;
}
}
return true;
}
Gigg, Martyn Anthony
committed
/** check if first 19 characters of a string is date-time string according to yyyy-mm-ddThh:mm:ss
* @param str The string to test
* @returns true if the strings format matched the expected date format
*/
bool LoadLog::isDateTimeString(const std::string& str)
{
Gigg, Martyn Anthony
committed
Poco::DateTime dt;
int tz_diff;
return Poco::DateTimeParser::tryParse(Poco::DateTimeFormat::ISO8601_FORMAT, str.substr(0,19), dt, tz_diff);
Anders Markvardsen
committed
} // namespace DataHandling
} // namespace Mantid