21#include "MantidTypes/Core/DateAndTimeHelpers.h"
23#include <Poco/DateTimeFormat.h>
24#include <Poco/DateTimeParser.h>
25#include <Poco/DirectoryIterator.h>
26#include <boost/algorithm/string.hpp>
33using Mantid::Types::Core::DateAndTime;
39using namespace Kernel;
40using API::FileProperty;
41using API::MatrixWorkspace;
43using API::WorkspaceProperty;
45using Types::Core::DateAndTime;
49template <
class MapClass,
class LoggerType>
50void addLogDataToRun(
Mantid::API::Run &run, MapClass &aMap, LoggerType &logger) {
51 for (
auto &itr : aMap) {
54 }
catch (std::invalid_argument &e) {
55 logger.warning() << e.what() <<
'\n';
56 }
catch (Exception::ExistsError &e) {
57 logger.warning() << e.what() <<
'\n';
72 "The name of the workspace to which the log data will be added.");
74 const std::vector<std::string> exts{
".txt",
".log"};
76 "The filename (including its full or relative path) of a SNS "
77 "text log file (not cvinfo), "
78 "an ISIS log file, or an ISIS raw file. "
79 "If a raw file is specified all log files associated with "
80 "that raw file are loaded into the specified workspace. The "
81 "file extension must "
82 "either be .raw or .s when specifying a raw file");
85 "For SNS-style log files only: the names of each column's log, separated "
87 "This must be one fewer than the number of columns in the file.");
90 "For SNS-style log files only: the units of each column's log, separated "
92 "This must be one fewer than the number of columns in the file. "
93 "Optional: leave blank for no units in any log.");
96 "Number of columns in the file. If not set Mantid will "
111 std::vector<std::string> names =
getProperty(
"Names");
113 std::ifstream logFileStream(
m_filename.c_str());
118 if (std::filesystem::is_directory(l_path)) {
134 if (names.size() > 1) {
135 throw std::invalid_argument(
"More than one log name provided. Invalid ISIS log file.");
140 throw std::invalid_argument(
"File " +
m_filename +
" cannot be read because it has an old unsupported format.");
143 int colNum =
static_cast<int>(
getProperty(
"NumberOfColumns"));
157 throw std::invalid_argument(
"The log file provided is invalid as it has "
158 "less than 2 or more than three columns.");
170 if (!logFileStream) {
171 throw std::invalid_argument(
"Unable to open file " +
m_filename);
178 throw std::invalid_argument(
"File " +
m_filename +
179 " is not a standard ISIS log file. Expected "
180 "to be a two column file.");
183 std::string DateAndTime;
184 std::stringstream ins(aLine);
189 std::string whatType;
194 throw std::invalid_argument(
"ISIS log file contains unrecognised second column entries: " +
m_filename);
202 }
catch (std::exception &) {
216 std::string propname;
217 std::map<std::string, std::unique_ptr<Kernel::TimeSeriesProperty<double>>> dMap;
218 std::map<std::string, std::unique_ptr<Kernel::TimeSeriesProperty<std::string>>> sMap;
220 bool isNumeric(
false);
222 if (!logFileStream) {
223 throw std::invalid_argument(
"Unable to open file " +
m_filename);
228 throw std::invalid_argument(
"File " + logFileName +
229 " is not a standard ISIS log file. Expected "
230 "to be a file starting with DateTime String "
239 std::stringstream line(str);
240 std::string timecolumn;
243 std::string blockcolumn;
248 g_log.
warning() <<
"Failed to parse line in log file: " << timecolumn <<
"\t" << blockcolumn;
253 throw std::invalid_argument(
"ISIS log file contains unrecognised second column entries: " + logFileName);
256 std::string valuecolumn;
267 std::istringstream istr(valuecolumn);
270 isNumeric = !istr.fail();
273 auto ditr = dMap.find(propname);
274 if (ditr != dMap.end()) {
275 auto prop = ditr->second.get();
277 prop->addValue(timecolumn, dvalue);
279 auto logd = std::make_unique<Kernel::TimeSeriesProperty<double>>(propname);
280 logd->addValue(timecolumn, dvalue);
281 dMap.emplace(propname, std::move(logd));
284 auto sitr = sMap.find(propname);
285 if (sitr != sMap.end()) {
286 auto prop = sitr->second.get();
288 prop->addValue(timecolumn, valuecolumn);
290 auto logs = std::make_unique<Kernel::TimeSeriesProperty<std::string>>(propname);
291 logs->addValue(timecolumn, valuecolumn);
292 sMap.emplace(propname, std::move(logs));
296 addLogDataToRun(run, dMap,
g_log);
297 addLogDataToRun(run, sMap,
g_log);
308 if (logName.empty()) {
309 return std::filesystem::path(
m_filename).stem().string();
311 return logName.front();
322 std::vector<std::string> names =
getProperty(
"Names");
323 std::vector<std::string> units =
getProperty(
"Units");
337 std::vector<double> cols;
340 if (!ret || cols.size() < 2)
343 auto numCols =
static_cast<size_t>(cols.size() - 1);
344 if (names.size() != numCols)
345 throw std::invalid_argument(
"The Names parameter should have one fewer "
346 "entry as the number of columns in a SNS-style "
348 if ((!units.empty()) && (units.size() != numCols))
349 throw std::invalid_argument(
"The Units parameter should have either 0 "
350 "entries or one fewer entry as the number of "
351 "columns in a SNS-style text log file.");
354 std::vector<TimeSeriesProperty<double> *> props;
355 for (
size_t i = 0; i < numCols; i++) {
357 if (units.size() == numCols)
358 p->setUnits(units[i]);
359 props.emplace_back(p);
368 if (cols.size() == numCols + 1) {
369 DateAndTime time(cols[0], 0.0);
370 for (
size_t i = 0; i < numCols; i++)
371 props[i]->addValue(time, cols[i + 1]);
373 throw std::runtime_error(
"Inconsistent number of columns while reading "
374 "SNS-style text file.");
376 throw std::runtime_error(
"Error while reading columns in SNS-style text file.");
379 for (
size_t i = 0; i < numCols; i++) {
380 std::string propName = props[i]->name();
381 if (localWorkspace->mutableRun().hasProperty(propName)) {
382 localWorkspace->mutableRun().removeLogData(propName);
383 g_log.
information() <<
"Log data named " << propName <<
" already existed and was overwritten.\n";
385 localWorkspace->mutableRun().addLogData(props[i]);
402 const string lower(
"abcdefghijklmnopqrstuvwxyz");
403 const string upper(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
406 if (letters.find_first_of(s) != string::npos) {
410 const auto isNumber = [](
const std::string &str) {
414 (void)std::stold(str);
416 }
catch (
const std::invalid_argument &) {
418 }
catch (
const std::out_of_range &) {
432 std::transform(strToConvert.begin(), strToConvert.end(), strToConvert.begin(), tolower);
442 FILE *file = fopen(filename.c_str(),
"rb");
445 size_t file_size = fread(data, 1,
sizeof(data), file);
447 char const *pend = &data[file_size];
452 for (
char *char_pos = data; char_pos < pend; ++char_pos) {
453 auto char_value =
static_cast<unsigned long>(*char_pos);
454 if (char_value > 0x7F) {
470 return Types::Core::DateAndTimeHelpers::stringIsISO8601(str.substr(0, 19));
483 std::string firstLine;
486 logFileStream.seekg(0);
488 std::regex oldDateFormat(R
"([A-Z][a-z]{2} [ 1-3]\d-[A-Z]{3}-\d{4} \d{2}:\d{2}:\d{2})");
490 return std::regex_match(firstLine.substr(0, 24), oldDateFormat);
500 std::vector<std::string> strs;
502 boost::split(strs, input, boost::is_any_of(
"\t "));
505 for (
auto &str : strs) {
506 if (!Strings::convert<double>(str, val))
509 out.emplace_back(val);
521 if (!logFileStream) {
522 throw std::invalid_argument(
"Unable to open file " +
m_filename);
532 throw std::invalid_argument(
"File " + logFileName +
533 " is not a standard ISIS log file. Expected to "
534 "be a file starting with DateTime String "
538 std::stringstream line(str);
539 std::string timecolumn;
542 std::string blockcolumn;
547 throw std::invalid_argument(
"ISIS log file contains unrecognised second column entries: " + logFileName);
550 std::string valuecolumn;
555 logFileStream.seekg(0);
#define DECLARE_ALGORITHM(classname)
double lower
lower and upper bounds on the multiplier, if known
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
@ Load
allowed here which will be passed to the algorithm
void addLogData(Kernel::Property *p)
Add a log entry.
This class stores information regarding an experimental run as a series of log entries.
A property class for workspaces.
bool LoadSNSText()
SNS text.
void init() override
Overwrites Algorithm method.
std::string m_filename
The name and path of an input file.
kind classify(const std::string &s) const
Takes as input a string and try to determine what type it is.
bool isAscii(const std::string &filename)
Checks if the file is an ASCII file.
LoadLog()
Default constructor.
bool isDateTimeString(const std::string &str) const
Check if first 19 characters of a string is date-time string according to yyyy-mm-ddThh:mm:ss.
std::string extractLogName(const std::vector< std::string > &logName)
Checks if a log file name was provided (e.g.
void exec() override
Overwrites Algorithm method.
bool SNSTextFormatColumns(const std::string &input, std::vector< double > &out) const
Check for SNS-style text file.
std::string stringToLower(std::string strToConvert)
Convert string to lower case.
int countNumberColumns(std::ifstream &logFileStream, const std::string &logFileName)
Returns the number of columns in the log file.
bool isOldDateTimeFormat(std::ifstream &logFileStream) const
Check whether the first 24 characters of a string are consistent with the date-time format used in ol...
void loadThreeColumnLogFile(std::ifstream &logFileStream, const std::string &logFileName, API::Run &run)
Create timeseries property from .log file and adds that to sample object.
void loadTwoColumnLogFile(std::ifstream &logFileStream, std::string logFileName, API::Run &run)
Loads two column log file data into local workspace.
kind
type returned by classify
Support for a property that holds an array of values.
Records the filename and the description of failure.
static Kernel::Property * createLogProperty(const std::string &logFName, const std::string &name)
Creates a TimeSeriesProperty of either double or string type depending on the log data Returns a poin...
void warning(const std::string &msg)
Logs at warning level.
void information(const std::string &msg)
Logs at information level.
Base class for properties.
A specialised Property class for holding a series of time-value pairs.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
MANTID_KERNEL_DLL std::istream & extractToEOL(std::istream &is, std::string &str)
Extract a line from input stream, discarding any EOL characters encountered.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
@ InOut
Both an input & output workspace.