Mantid
Loading...
Searching...
No Matches
InstrumentFileFinder.cpp
Go to the documentation of this file.
2
7
8#include <Poco/SAX/Attributes.h>
9#include <Poco/SAX/ContentHandler.h>
10#include <Poco/SAX/SAXParser.h>
11#include <boost/algorithm/string/find.hpp>
12#include <boost/regex.hpp>
13#include <filesystem>
14
15#include <string>
16#include <utility>
17#include <vector>
18
19using namespace Mantid::Kernel;
20using namespace Mantid::Types::Core;
21using namespace Poco::XML;
22
23namespace {
25Mantid::Kernel::Logger g_log("InstrumentFileFinder");
26
27// used to terminate SAX process
28class DummyException {
29public:
30 std::string m_validFrom;
31 std::string m_validTo;
32 DummyException(std::string validFrom, std::string validTo)
33 : m_validFrom(std::move(validFrom)), m_validTo(std::move(validTo)) {}
34};
35// SAX content handler for grapping stuff quickly from IDF
36class myContentHandler : public Poco::XML::ContentHandler {
37 void startElement(const XMLString & /*uri*/, const XMLString &localName, const XMLString & /*qname*/,
38 const Attributes &attrList) override {
39 if (localName == "instrument" || localName == "parameter-file") {
40 throw DummyException(static_cast<std::string>(attrList.getValue("", "valid-from")),
41 static_cast<std::string>(attrList.getValue("", "valid-to")));
42 }
43 }
44 void endElement(const XMLString & /*uri*/, const XMLString & /*localName*/, const XMLString & /*qname*/) override {}
45 void startDocument() override {}
46 void endDocument() override {}
47 void characters(const XMLChar /*ch*/[], int /*start*/, int /*length*/) override {}
48 void endPrefixMapping(const XMLString & /*prefix*/) override {}
49 void ignorableWhitespace(const XMLChar /*ch*/[], int /*start*/, int /*length*/) override {}
50 void processingInstruction(const XMLString & /*target*/, const XMLString & /*data*/) override {}
51 void setDocumentLocator(const Locator * /*loc*/) override {}
52 void skippedEntity(const XMLString & /*name*/) override {}
53 void startPrefixMapping(const XMLString & /*prefix*/, const XMLString & /*uri*/) override {}
54};
55} // namespace
56
57namespace Mantid::API {
58
81std::string InstrumentFileFinder::getInstrumentFilename(const std::string &instrumentName, const std::string &date) {
82 const std::vector<std::string> validFormats = {"xml", "nxs", "hdf5"};
83 g_log.debug() << "Looking for instrument file for " << instrumentName << " that is valid on '" << date << "'\n";
84 // Lookup the instrument (long) name
85 const std::string instrument(Kernel::ConfigService::Instance().getInstrument(instrumentName).name());
86
87 // Get the instrument directories for instrument file search
88 const std::vector<std::string> &directoryNames = Kernel::ConfigService::Instance().getInstrumentDirectories();
89
90 // matching files sorted with newest files coming first
91 const std::vector<std::string> matchingFiles =
92 getResourceFilenames(instrument + "_Definition", validFormats, directoryNames, date);
93 std::string instFile;
94 if (!matchingFiles.empty()) {
95 instFile = matchingFiles[0];
96 g_log.debug() << "Instrument file selected is " << instFile << '\n';
97 } else {
98 g_log.debug() << "No instrument file found\n";
99 }
100 return instFile;
101}
102
105// directoryName must include a final '/'.
106std::string InstrumentFileFinder::getParameterPath(const std::string &instName, const std::string &dirHint) {
107 // Remove the path from the filename, some legacy callers will pass in
108 // a full path rather than a filename
109 std::filesystem::path filePath(instName);
110 const std::string filename = filePath.filename().string();
111
112 // Try the hinted dir first
113 if (!dirHint.empty()) {
114 const std::string result = lookupIPF(dirHint, filename);
115 if (!result.empty()) {
116 return result;
117 }
118 }
119
120 const Kernel::ConfigServiceImpl &configService = Kernel::ConfigService::Instance();
121 const std::vector<std::string> directoryNames = configService.getInstrumentDirectories();
122
123 for (const auto &dirName : directoryNames) {
124 // This will iterate around the directories from user ->etc ->install, and
125 // find the first beat file
126 const std::string result = lookupIPF(dirName, filename);
127 if (!result.empty()) {
128 g_log.debug() << "Found: " << result << '\n';
129 return result;
130 }
131 }
132
133 g_log.debug() << "Found Nothing \n";
134 return "";
135}
136
137std::string InstrumentFileFinder::lookupIPF(const std::string &dir, std::string filename) {
138 const std::string ext = ".xml";
139 // Remove .xml for example if abc.xml was passed
140 boost::algorithm::ierase_all(filename, ext);
141
142 const std::string suffixSeperator("_Definition");
143
144 std::string prefix;
145 std::string suffix;
146
147 if (auto sepPos = boost::algorithm::ifind_first(filename, suffixSeperator)) {
148 prefix = std::string(filename.begin(), sepPos.begin());
149 suffix = std::string(sepPos.end(), filename.end());
150 } else {
151 prefix = filename;
152 }
153
154 std::filesystem::path directoryPath(dir);
155
156 // Assemble parameter file name
157 std::string fullPathParamIDF = (directoryPath / (prefix + "_Parameters" + suffix + ext)).string();
158
159 if (std::filesystem::exists(fullPathParamIDF)) {
160 return fullPathParamIDF;
161 }
162
163 fullPathParamIDF = (directoryPath / (prefix + "_Parameters" + ext)).string();
164 if (std::filesystem::exists(fullPathParamIDF)) {
165 return fullPathParamIDF;
166 }
167
168 return "";
169}
170
186std::vector<std::string> InstrumentFileFinder::getResourceFilenames(const std::string &prefix,
187 const std::vector<std::string> &fileFormats,
188 const std::vector<std::string> &directoryNames,
189 const std::string &date) {
190
191 if (date.empty()) {
192 // Just use the current date
193 g_log.debug() << "No date specified, using current date and time.\n";
194 const std::string now = Types::Core::DateAndTime::getCurrentTime().toISO8601String();
195 // Recursively call this method, but with all parameters.
196 return InstrumentFileFinder::getResourceFilenames(prefix, fileFormats, directoryNames, now);
197 }
198
199 // Join all the file formats into a single string
200 std::stringstream ss;
201 ss << "(";
202 for (size_t i = 0; i < fileFormats.size(); ++i) {
203 if (i != 0)
204 ss << "|";
205 ss << fileFormats[i];
206 }
207 ss << ")";
208 const std::string allFileFormats = ss.str();
209
210 const boost::regex regex(prefix + ".*\\." + allFileFormats, boost::regex_constants::icase);
211 DateAndTime d(date);
212
213 DateAndTime refDate("1900-01-31 23:59:00"); // used to help determine the most
214 // recently starting file, if none match
215 DateAndTime refDateGoodFile("1900-01-31 23:59:00"); // used to help determine the most recently
216
217 // Two files could have the same `from` date so multimap is required.
218 // Sort with newer dates placed at the beginning
219 std::multimap<DateAndTime, std::string, std::greater<DateAndTime>> matchingFiles;
220 bool foundFile = false;
221 std::string mostRecentFile; // path to the file with most recent "valid-from"
222 for (const auto &directoryName : directoryNames) {
223 // Iterate over the directories from user ->etc ->install, and find the
224 // first beat file
225 for (const auto &dir_entry : std::filesystem::directory_iterator(directoryName)) {
226
227 const auto &filePath = dir_entry.path();
228 if (!std::filesystem::is_regular_file(filePath))
229 continue;
230
231 const std::string l_filenamePart = filePath.filename().string();
232 if (regex_match(l_filenamePart, regex)) {
233 const std::string pathName = filePath.string();
234 g_log.debug() << "Found file: '" << pathName << "'\n";
235
236 std::string validFrom, validTo;
237 getValidFromTo(pathName, validFrom, validTo);
238 g_log.debug() << "File '" << pathName << " valid dates: from '" << validFrom << "' to '" << validTo << "'\n";
239 // Use default valid "from" and "to" dates if none were found.
240 DateAndTime to, from;
241 if (validFrom.length() > 0)
242 from.setFromISO8601(validFrom);
243 else
244 from = refDate;
245 if (validTo.length() > 0)
246 to.setFromISO8601(validTo);
247 else
248 to.setFromISO8601("2100-01-01T00:00:00");
249
250 if (from <= d && d <= to) {
251 foundFile = true;
252 matchingFiles.insert(std::pair<DateAndTime, std::string>(from, pathName));
253 }
254 // Consider the most recent file in the absence of matching files
255 if (!foundFile && (from >= refDate)) {
256 refDate = from;
257 mostRecentFile = pathName;
258 }
259 }
260 }
261 }
262
263 // Retrieve the file names only
264 std::vector<std::string> pathNames;
265 if (!matchingFiles.empty()) {
266 pathNames.reserve(matchingFiles.size());
267
268 std::transform(matchingFiles.begin(), matchingFiles.end(), std::back_inserter(pathNames),
269 [](const auto &elem) { return elem.second; });
270 } else {
271 pathNames.emplace_back(std::move(mostRecentFile));
272 }
273
274 return pathNames;
275}
276
283void InstrumentFileFinder::getValidFromTo(const std::string &IDFfilename, std::string &outValidFrom,
284 std::string &outValidTo) {
285 SAXParser pParser;
286 // Create on stack to ensure deletion. Relies on pParser also being local
287 // variable.
288 myContentHandler conHand;
289 pParser.setContentHandler(&conHand);
290
291 try {
292 pParser.parse(IDFfilename);
293 } catch (const DummyException &e) {
294 outValidFrom = e.m_validFrom;
295 outValidTo = e.m_validTo;
296 } catch (...) {
297 // should throw some sensible here
298 }
299}
300
301} // Namespace Mantid::API
std::string name
Definition Run.cpp:60
static void getValidFromTo(const std::string &IDFfilename, std::string &outValidFrom, std::string &outValidTo)
Utility to retrieve the validity dates for the given IDF.
static std::vector< std::string > getResourceFilenames(const std::string &prefix, const std::vector< std::string > &fileFormats, const std::vector< std::string > &directoryNames, const std::string &date)
Utility to retrieve a resource file (IDF, Parameters, ..)
static std::string getParameterPath(const std::string &instName, const std::string &dirHint="")
Search instrument directories for Parameter file, return full path name if found, else "".
static std::string lookupIPF(const std::string &dir, std::string filename)
static std::string getInstrumentFilename(const std::string &instrumentName, const std::string &date="")
Get the IDF using the instrument name and date.
The ConfigService class provides a simple facade to access the Configuration functionality of the Man...
const std::vector< std::string > & getInstrumentDirectories() const
Get instrument search directories.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
Kernel::Logger g_log("ExperimentInfo")
static logger object
STL namespace.