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