Newer
Older
Roman Tolchenov
committed
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include <algorithm>
Roman Tolchenov
committed
#include "MantidKernel/InstrumentInfo.h"
#include "MantidKernel/FacilityInfo.h"
#include "MantidKernel/Logger.h"
Janik Zikovsky
committed
#include "MantidKernel/Strings.h"
Roman Tolchenov
committed
#include <boost/lexical_cast.hpp>
#include <Poco/AutoPtr.h>
#include <Poco/DOM/Element.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/DOM/Text.h>
Roman Tolchenov
committed
namespace Mantid {
namespace Kernel {
namespace {
// static logger object
Logger g_log("InstrumentInfo");
}
Roman Tolchenov
committed
/** Constructor.
* @param f :: The facility
* @param elem :: The Poco::XML::Element to read the data from
* @throw std::runtime_error if name or at least one technique are not defined
*/
InstrumentInfo::InstrumentInfo(const FacilityInfo *f,
const Poco::XML::Element *elem)
: m_facility(f) {
Roman Tolchenov
committed
m_name = elem->getAttribute("name");
if (m_name.empty()) {
throw std::runtime_error("Instrument name is not defined");
}
Roman Tolchenov
committed
// The string to separate the instrument name and the run number.
m_delimiter = elem->getAttribute("delimiter");
if (m_delimiter.empty()) {
m_delimiter = f->delimiter();
}
m_shortName = elem->getAttribute("shortname");
if (m_shortName.empty()) {
m_shortName = m_name;
}
Roman Tolchenov
committed
fillTechniques(elem);
fillLiveData(elem);
fillZeroPadding(elem);
}
Roman Tolchenov
committed
/**
* Equality operator. Two objects are considered equal if their names and short
* names are the same.
* @param rhs :: The object to compare this with
* @returns True if the objects are considered equal, false otherwise
*/
bool InstrumentInfo::operator==(const InstrumentInfo &rhs) const {
return (this->name() == rhs.name() && this->shortName() == rhs.shortName());
}
Roman Tolchenov
committed
/// Returns the default delimiter between instrument name and run number
std::string InstrumentInfo::delimiter() const { return m_delimiter; }
/// Return the name of the instrument
const std::string InstrumentInfo::name() const { return m_name; }
/// Return the short name of the instrument
const std::string InstrumentInfo::shortName() const { return m_shortName; }
/// Returns zero padding for this instrument
int InstrumentInfo::zeroPadding(unsigned int runNumber) const {
if (m_zeroPadding.empty())
return m_facility->zeroPadding();
if (m_zeroPadding.size() == 1) {
auto padding = m_zeroPadding.begin();
if (runNumber >= padding->first)
return getZeroPadding(padding);
else
return m_facility->zeroPadding();
}
auto last = m_zeroPadding.end();
--last;
for (auto it = m_zeroPadding.begin(); it != last; ++it) {
auto next = it;
++next;
if (runNumber >= it->first && runNumber < next->first) {
return getZeroPadding(it);
}
return getZeroPadding(last);
}
/**
* Returns file prefix for this instrument and a run number.
* @param runNumber :: A run number.
*/
std::string InstrumentInfo::filePrefix(unsigned int runNumber) const {
if (m_zeroPadding.empty())
return m_shortName;
if (m_zeroPadding.size() == 1) {
auto padding = m_zeroPadding.begin();
if (runNumber >= padding->first)
return getPrefix(padding);
else
return m_shortName;
}
auto last = m_zeroPadding.end();
--last;
for (auto it = m_zeroPadding.begin(); it != last; ++it) {
auto next = it;
++next;
if (runNumber >= it->first && runNumber < next->first) {
return getPrefix(it);
}
}
return getPrefix(last);
}
/// Returns the name of the live listener
const std::string &InstrumentInfo::liveListener() const {
// TODO: Return "" if no LiveListenerInfo?
static std::string foobar;
if (!hasLiveListenerInfo()) return foobar;
return liveListenerInfo().listener();
/** Returns the host & port to connect to for a live data stream
* No guarantees are given that the provided string is well-formed and valid
* - the caller should check this themselves
*/
const std::string &InstrumentInfo::liveDataAddress() const {
// TODO: Return "" if no LiveListenerInfo?
static std::string foobar;
if (!hasLiveListenerInfo()) return foobar;
return liveListenerInfo().address();
}
/**
* @brief Get LiveListenerInfo for specified connection (or default)
* @param name Name attribute of connection to return info on
* @return Reference to LiveListenerInfo for specified connection
* @throw std::runtime_error When no listeners, or name not found
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
*/
const LiveListenerInfo &InstrumentInfo::liveListenerInfo(
std::string name) const {
if (!hasLiveListenerInfo())
throw std::runtime_error("Attempted to access live listener for " +
m_name + " instrument, which has no listeners.");
// Default to specified default connection
if (name.empty()) name = m_defaultListener;
// If no default connection specified, fallback to first connection
if (name.empty()) return m_listeners.front();
// Name specified, find requested connection
for (auto& listener : m_listeners) {
if (listener.name() == name)
return listener;
}
// The provided name was not valid / did not match any listeners
throw std::runtime_error("Could not find connection " + name +
" for instrument " + m_name);
}
bool InstrumentInfo::hasLiveListenerInfo() const
{
return m_listeners.size() > 0;
const std::vector<LiveListenerInfo> &InstrumentInfo::liveListenerInfoList() const
{
return m_listeners;
}
/// Return list of techniques
const std::set<std::string> &InstrumentInfo::techniques() const {
return m_technique;
}
/// Return the facility
const FacilityInfo &InstrumentInfo::facility() const { return *m_facility; }
/// Called from constructor to fill zero padding
void InstrumentInfo::fillZeroPadding(const Poco::XML::Element *elem) {
Poco::AutoPtr<Poco::XML::NodeList> pNL_zeropadding =
elem->getElementsByTagName("zeropadding");
unsigned long n = pNL_zeropadding->length();
for (unsigned long i = 0; i < n; ++i) {
auto elem = dynamic_cast<Poco::XML::Element *>(pNL_zeropadding->item(i));
if (!elem)
continue;
// read the zero padding size
if (!elem->hasAttribute("size")) {
throw std::runtime_error("Zeropadding size is missing for instrument " +
m_name);
}
auto &sizeStr = elem->getAttribute("size");
int size = 0;
if (!Mantid::Kernel::Strings::convert(sizeStr, size)) {
throw std::runtime_error(
"Zeropadding size must be an integer value (instrument " + m_name +
")");
}
// read the start run number
unsigned int startRunNumber = 0;
if (!elem->hasAttribute("startRunNumber")) {
if (!m_zeroPadding.empty()) {
throw std::runtime_error("Zeropadding size is missing for instrument " +
m_name);
} else {
auto &startRunNumberStr = elem->getAttribute("startRunNumber");
try {
startRunNumber = boost::lexical_cast<unsigned int>(startRunNumberStr);
} catch (...) {
throw std::runtime_error("Zeropadding start run number must be an "
"integer value (instrument " +
m_name + ")");
// read the file prefix
std::string prefix = m_shortName;
if (elem->hasAttribute("prefix")) {
prefix = elem->getAttribute("prefix");
}
m_zeroPadding[startRunNumber] = std::make_pair(prefix, size);
}
if (m_zeroPadding.empty()) {
m_zeroPadding[0] = std::make_pair(m_shortName, m_facility->zeroPadding());
}
}
/// Called from constructor to fill live listener name
void InstrumentInfo::fillTechniques(const Poco::XML::Element *elem) {
Poco::AutoPtr<Poco::XML::NodeList> pNL_technique =
elem->getElementsByTagName("technique");
unsigned long n = pNL_technique->length();
for (unsigned long i = 0; i < n; ++i) {
Poco::AutoPtr<Poco::XML::NodeList> pNL =
pNL_technique->item(i)->childNodes();
if (pNL->length() > 0) {
Poco::XML::Text *txt = dynamic_cast<Poco::XML::Text *>(pNL->item(0));
if (txt) {
std::string tech = txt->getData();
if (!tech.empty()) {
m_technique.insert(tech);
}
if (m_technique.empty()) {
throw std::runtime_error("No technique is defined for instrument " +
m_name);
}
}
/// Called from constructor to fill live listener name
void InstrumentInfo::fillLiveData(const Poco::XML::Element *elem) {
// See if we have a <livedata> element (will be NULL if there's none)
Poco::XML::Element *live = elem->getChildElement("livedata");
if (!live) return;
// Load default connection name attribute
m_defaultListener = live->getAttribute("default");
// Get connections under <livedata>
Poco::AutoPtr<Poco::XML::NodeList> connections =
elem->getElementsByTagName("connection");
// Load connection info for each child element
for (ulong i = 0; i < connections->length(); ++i) {
auto *conn = dynamic_cast<Poco::XML::Element *>(connections->item(i));
try {
m_listeners.emplace_back(LiveListenerInfo(this, conn));
} catch (...) {
g_log.error() << "Exception occurred while loading livedata for "
<< m_name << " instrument. Skipping faulty connection.\n";
}
}
}
//-------------------------------------------------------------------------
// Non-member functions
//-------------------------------------------------------------------------
/**
* Prints the instrument name to the stream
* @param buffer :: A reference to an output stream
* @param instrumentDescriptor :: A reference to an InstrumentInfo object
* @return A reference to the stream written to
*/
std::ostream &operator<<(std::ostream &buffer,
const InstrumentInfo &instrumentDescriptor) {
buffer << instrumentDescriptor.name();
return buffer;
}
Roman Tolchenov
committed
} // namespace Mantid