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), m_liveListener(), m_liveDataAddress() {
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 {
return m_liveListener;
}
/** 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 {
return m_liveDataAddress;
}
/// 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) {
// Get the first livedata element (will be NULL if there's none)
Poco::XML::Element *live = elem->getChildElement("livedata");
if (live) {
// Get the name of the listener - empty string will be returned if missing
m_liveListener = live->getAttribute("listener");
// Get the host+port. Would have liked to put straight into a
// Poco::Net::SocketAddress
// but that tries to contact the given address on construction, which won't
// always be possible (or scalable)
m_liveDataAddress = live->getAttribute("address");
// Warn rather than throw if there are problems with the address
if (m_liveDataAddress.empty()) {
g_log.warning()
<< "No connection details specified for live data listener of "
<< m_name << "\n";
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
// Check for a colon, which would suggest that a host & port are present
else if (m_liveDataAddress.find(":") == std::string::npos) {
g_log.warning() << "Live data address for " << m_name
<< " appears not to have both host and port specified.\n";
}
}
// Apply the facility default listener if none specified for this instrument
if (m_liveListener.empty()) {
m_liveListener = m_facility->liveListener();
}
}
//-------------------------------------------------------------------------
// 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