diff --git b/CMakeLists.txt a/CMakeLists.txt new file mode 100644 index 0000000..c421c5b --- /dev/null +++ a/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.0) +project(config) + +LIST( APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/submodules/cmake ) + +include(projectheader) +project_header(config) + +# Build the artifacts +add_subdirectory(${CMAKE_SOURCE_DIR}/submodules/logger) +add_subdirectory(${CMAKE_SOURCE_DIR}/submodules/pugixml) + +# Build the source code +add_subdirectory(src) + +include(packaging) +package_component() diff --git b/scripts/setup_submodules a/scripts/setup_submodules new file mode 100755 index 0000000..0aad384 --- /dev/null +++ a/scripts/setup_submodules @@ -0,0 +1,170 @@ +#!/bin/bash + +# =============================================== +# == Setting some environment variables +# =============================================== +GIT_URL_OPEN="http://gitlab.osdev.nl/open_source" +GIT_URL_CLOSED="git@gitlab.osdev.nl:closed_source" + +FUNC_RESULT="-1" + +# Name : print_usage_exit() +# Description : Print the way this script is intended to be used and exit. +# Parameters : None. +# Returns : err_code 1 to the Operating System +# -------------------------------------------------------------------------------------- +function print_usage_exit() +{ + echo "Usage $0 -i|--install|-u|--update" + echo " -i or --install Install the submodules mentioned in the submodules.list" + echo " -u or --update Update the submodules mentioned in the submodules.list" + echo " " + exit 1 +} + +# Name : check_top_or_sub +# Description : Determine if we're running in a "single" lib-build or part of a +# "meta"-repository ( submodule ). +# Parameters : None +# Returns : Updates the value FUNC_RESULT. +# -1 - We're neither a git-repo or submodule. +# 0 - We're a submodule +# 1 - We're a top-repo ( Single library ) +# -------------------------------------------------------------------------------------- +function check_top_or_sub() +{ + # This function checks if we're the top-repository. + # In that case we need the submodules.. If we're already a submodule, + # we simply exit this script with a message + if [ -e ./.git ]; then + FUNC_RESULT="1" + return + elif [ -e ../.git ]; then + if [ -e ../.submodules ]; then + echo "Seems like we're already a submodule. Nothing to do here." + FUNC_RESULT="0" + return + fi + fi + FUNC_RESULT="-1" + return +} + +# Name : check_working_dir +# Description : If we're in the top of our repo, we can run this script further. +# Parameters : None. +# Returns : Updates the value FUNC_RESULT. +# -1 - Not used. +# 0 - We're not on the top-level +# 1 - We're at the top-level. Good to go. +# -------------------------------------------------------------------------------------- +function check_working_dir() +{ + FUNC_RESULT="-1" + # Check if we're in the top-level directory of our repository. + if [ -f ./scripts/submodules.list ]; then + # We're good to go + FUNC_RESULT="1" + return + fi + FUNC_RESULT="0" + return +} + +# Name : read_submodules +# Description : Read the list of submodules needed for this project +# Parameters : None +# Returns : Updates the value FUNC_RESULT +# 0 - Module list was not found +# 1 - Module list was found and read. +# -------------------------------------------------------------------------------------- +function read_submodules() +{ + FUNC_RESULT="-1" + if [ -e ./scripts/submodules.list ]; then + source ./scripts/submodules.list + FUNC_RESULT="1" + return + fi + + echo "Submodules list not found...." + FUNC_RESULT="0" + return +} + +# Name : add_submodules +# Description : Configure the repo to add the submodules. +# Parameters : None. +# Returns : None. +# -------------------------------------------------------------------------------------- +function add_submodules() +{ + echo -e "Adding SubModule(s)." + for SUB_MODULE in ${SUB_MODULES_OPEN} + do + git submodule add -f ${GIT_URL_OPEN}/${SUB_MODULE}.git submodules/${SUB_MODULE} + git config submodule.${SUB_MODULE}.url ${GIT_URL_OPEN}/${SUB_MODULE}.git + done + + for SUB_MODULE in ${SUB_MODULES_CLOSED} + do + echo {GIT_URL_CLOSED}/${SUB_MODULE}.git + git submodule add -f ${GIT_URL_CLOSED}/${SUB_MODULE}.git submodules/${SUB_MODULE} + git config submodule.${SUB_MODULE}.url ${GIT_URL_CLOSED}/${SUB_MODULE}.git + done + +} + +# Name : get_submodules +# Description : Actually get the submodules from gitlab and add them. +# Parameters : None +# Returns : None +# -------------------------------------------------------------------------------------- +function get_submodules() +{ + git submodule update --init --recursive +} + +# Name : update_submodules +# Description : Update the submodules already added. +# Parameters : None +# Returns : None +# -------------------------------------------------------------------------------------- +function update_submodules() +{ + git submodule update --recursive +} + +# ============================================================================= +# == T H E M A I N E N T R Y O F T H I S S C R I P T == +# ============================================================================= +check_top_or_sub +if [ "${FUNC_RESULT}" == "0" ]; then + echo "Seems like we're a submodule already or not part of a repository." + exit 0 +fi + +check_working_dir +if [ "${FUNC_RESULT}" == "0" ]; then + echo "Go to the top of this repository and type : scripts/setup_submodules [-i|--install]" + exit 0 +fi + +read_submodules + +case "$1" in + -i*|--install*) + echo "Installing submodules for this repository ( ${PWD} )" + add_submodules + get_submodules + ;; + -u*|--update*) + echo "Update submodules : ${SUB_MODULES}" + update_submodules + ;; + *) + echo "No parameters found..." + print_usage_exit + ;; +esac + diff --git b/scripts/submodules.list a/scripts/submodules.list new file mode 100644 index 0000000..73b4432 --- /dev/null +++ a/scripts/submodules.list @@ -0,0 +1,5 @@ +SUB_MODULES_OPEN="cmake +logger +pugixml" + +SUB_MODULES_CLOSED="" diff --git b/src/CMakeLists.txt a/src/CMakeLists.txt new file mode 100644 index 0000000..00c5b5d --- /dev/null +++ a/src/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.0) +LIST(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/submodules/cmake) +include(projectheader) +project_header(config) + +find_package( Qt5Core REQUIRED ) +include_directories( SYSTEM + ${Qt5Core_INCLUDE_DIRS} +) + +include(compiler) + +include_directories( + ${CMAKE_SOURCE_DIR}/submodules/pugixml/src + ${CMAKE_SOURCE_DIR}/submodules/logger/src +) + +set(SRC_LIST + ${CMAKE_CURRENT_SOURCE_DIR}/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/connections.h + ${CMAKE_CURRENT_SOURCE_DIR}/connections.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/connectordata.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectordata.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmlbase.h + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmlbase.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmlconfig.h + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmlconfig.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmletlnetwork.h + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmletlnetwork.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmletlservices.h + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmletlservices.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmlmodelmapping.h + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmlmodelmapping.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmldal.h + ${CMAKE_CURRENT_SOURCE_DIR}/dcxmldal.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/objectdata.h + ${CMAKE_CURRENT_SOURCE_DIR}/objectdata.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/jobdata.h + ${CMAKE_CURRENT_SOURCE_DIR}/jobdata.cpp +) + +link_directories( + ${CMAKE_BINARY_DIR}/lib +) + +include(library) +add_libraries( + ${Qt5Core_LIBRARIES} + logger + pugixml +) + +include(installation) +install_component() diff --git b/src/connection.cpp a/src/connection.cpp new file mode 100644 index 0000000..4198caa --- /dev/null +++ a/src/connection.cpp @@ -0,0 +1,19 @@ +#include "connection.h" + +using namespace osdev::components; + +Connection::Connection() + : m_source() + , m_target() + , m_input() + , m_output() +{ +} + +Connection::Connection(const std::string& _source, const std::string& _target, const std::string& _output, const std::string& _input) + : m_source(_source) + , m_target(_target) + , m_input(_input) + , m_output(_output) +{ +} diff --git b/src/connection.h a/src/connection.h new file mode 100644 index 0000000..3dd99fc --- /dev/null +++ a/src/connection.h @@ -0,0 +1,83 @@ +#pragma once + +#include + +namespace osdev { +namespace components { + +/* + * _________________________________________ + * / In a hierarchy every employee tends to \ + * | rise to his level of incompetence ... | + * | in time every post tends to be occupied | + * | by an employee who is incompetent to | + * | carry out its duties ... Work is | + * | accomplished by those employees who | + * | have not yet reached their level of | + * | incompetence. | + * | | + * | -- Dr. Laurence J. Peter, "The Peter | + * \ Principle" / + * ----------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + *********************************************** + */ +/*! + * \brief The Connection class is a representation of the connection section in the Modelmapper configurator + */ +class Connection +{ +public: + Connection(); + /*! + * \brief CTor + * \param _source - The object we want to connect from + * \param _target - The object we want to connect to + * \param _output - The endpoint on the SOURCE + * \param _input - The endpoint on the TARGET + */ + Connection(const std::string& _source, const std::string& _target, const std::string& _output, const std::string& _input); + + // GETTERS + /*! + * \brief source + * \return The object-ID we want to connect from as std::string + */ + const std::string& source() const { return m_source; } + + /*! + * \brief target + * \return The object-ID we want to connect to as std::string + */ + const std::string& target() const { return m_target; } + + /*! + * \brief output + * \return The output endpoint on the Source object + */ + const std::string& output() const { return m_output; } + + /*! + * \brief input + * \return The input endpoint on the Target object. + */ + const std::string& input() const { return m_input; } + +private: + std::string m_source; ///< The object we want to connect from + std::string m_target; ///< The object we want to connect to + std::string m_input; ///< The endpoint ont the SOURCE + std::string m_output; ///< The endpoint ont the TARGET +}; + +} // namespace components +} // namespace osdev + diff --git b/src/connections.cpp a/src/connections.cpp new file mode 100644 index 0000000..0640fc6 --- /dev/null +++ a/src/connections.cpp @@ -0,0 +1,21 @@ +#include "connections.h" + +using namespace osdev::components; + +Connections::Connections() + : m_connections() +{ +} + +void Connections::addConnection(const QString& _source, const QString& _target, const QString& _output, const QString& _input) +{ + m_connections.append(QSharedPointer(new Connection(_source, _target, _output, _input))); +} + +QSharedPointer Connections::getConnection(int index) +{ + if (index < m_connections.count()) { + return m_connections.at(index); + } + return QSharedPointer(); +} diff --git b/src/connections.h a/src/connections.h new file mode 100644 index 0000000..0ba1897 --- /dev/null +++ a/src/connections.h @@ -0,0 +1,79 @@ +#pragma once + +#include "connection.h" + +#include +#include +#include +#include +#include + +namespace osdev { +namespace components { + +/* + * ______________________________________ + * / My opinions always matter :-) \ + * | | + * | - Dan Malek on the linuxppc-embedded | + * \ list / + * -------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + ******************************************* + */ +/*! + * \brief The connections class contains the source- and target + * endpoints of function-block connections. In- and outputs + * are represented by endpoint objects to be able to translate + * those to standard signals and slots. + */ + +class Connections +{ +public: + /*! + * \brief CTor + */ + Connections(); + + /*! + * \brief addConnection - Probably adds a connection... + * \param _source - The object we want to connect from by UUId + * \param _target - The object we want to connect to by UUId + * \param _output - The endpoint on the SOURCE by Name + * \param _input - The endpoint on the TARGET by Name + */ + void addConnection(const std::string& _source, const std::string& _target, const std::string& _output, const std::string& _input); + + /*! + * \return The number of connections stored. + */ + int count() const { return m_connections.size(); } + + /*! + * \brief Gets the Connection for the specified index. + * \param index The index for the connection. + * \return Shared pointer to the connection. + */ + std::shared_ptr getConnection(int index); + + /*! + * \brief Get the connections. + * \return A list of connection shared pointers. + */ + std::vector> getConnections() const { return m_connections; } + +private: + std::vector> m_connections; +}; + +} // namespace components +} // namespace osdev diff --git b/src/connectordata.cpp a/src/connectordata.cpp new file mode 100644 index 0000000..50fcb63 --- /dev/null +++ a/src/connectordata.cpp @@ -0,0 +1,31 @@ +#include "connectordata.h" + +using namespace osdev::components; + +ConnectorData::ConnectorData() + : m_name() + , m_type() + , m_id() + , m_default() + , m_excludeFromIdentityCheck(false) +{ +} + +ConnectorData::ConnectorData(const QString& _name, const QString& _type, const QString& _id, const QString& _default, bool _excludeFromIdentityCheck) + : m_name(_name) + , m_type(_type) + , m_id(_id) + , m_default(_default) + , m_excludeFromIdentityCheck(_excludeFromIdentityCheck) +{ +} + +void ConnectorData::setName(const QString& _name) { m_name = _name; } + +void ConnectorData::setType(const QString& _type) { m_type = _type; } + +void ConnectorData::setId(const QString& _id) { m_id = _id; } + +void ConnectorData::setDefault(const QString& _default) { m_default = _default; } + +void ConnectorData::setExcludeFromIdentityCheck(bool value) { m_excludeFromIdentityCheck = value; } diff --git b/src/connectordata.h a/src/connectordata.h new file mode 100644 index 0000000..e308ea9 --- /dev/null +++ a/src/connectordata.h @@ -0,0 +1,110 @@ +#pragma once + +#include + +namespace osdev { +namespace components { + +/* ______________________________________ + * / It's hard to think of you as the end \ + * | result of millions of years of | + * \ evolution. / + * -------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + ******************************************** + */ +/*! + * \brief The ConnectorData class + */ + +class ConnectorData +{ +public: + /*! + * \brief ConnectorData + */ + ConnectorData(); + + /*! + * \brief ConnectorData + * \param name The name of the ConnectorData. + * \param type The type of the ConnectorData. + * \param id The id of the ConnectorData. + * \param defaultValue The default of the ConnectorData. + * \param excludeFromIdentityCheck Flag that indicates if this variable should participate in a record identity check. Default false. + */ + ConnectorData(const std::string& name, const std::string& type, const std::string& id, const string& defaultValue, bool excludeFromIdentityCheck = false); + + /*! + * \brief setName + * \param name The name of the ConnectorData. + */ + void setName(const std::string& name); + + /*! + * \brief setType + * \param type The type of the ConnectorData. + */ + void setType(const std::string& type); + + /*! + * \brief setId + * \param id The id of the ConnectorData. + */ + void setId(const std::string& id); + + /*! + * \brief setDefault + * \param defaultValue The default of the ConnectorData. + */ + void setDefault(const std::string& defaultValue); + + /*! + * \brief set excludeFromIdentityCheck flag value + * \param value The excludeFromIdentityCheck flag value of the ConnectorData. + */ + void setExcludeFromIdentityCheck(bool value); + + /*! + * \return The name of the ConnectorData. + */ + const std::string& name() const { return m_name; } + + /*! + * \return The type of the ConnectorData. + */ + const std::string& type() const { return m_type; } + + /*! + * \return The id of the ConnectorData. A null string means that there is no id. + */ + const std::string& id() const { return m_id; } + + /*! + * \return default The default of the ConnectorData. + */ + const std::string& Default() const { return m_default; } + + /*! + * \return the excludeFromIdentityCheck flag + */ + bool excludeFromIdentityCheck() const { return m_excludeFromIdentityCheck; } + +private: + std::string m_name; ///< The name of the variable this connector represents. + std::string m_type; ///< The type of the variable this connector represents. + std::string m_id; ///< The id of the variable this connector represents. + std::string m_default; ///< The default value of the variable this connector represents. + bool m_excludeFromIdentityCheck; ///< Flag that indicates if this variable should participate in a record identity check. +}; + +} // namespace components +} // namespace osdev diff --git b/src/dcxmlbase.cpp a/src/dcxmlbase.cpp new file mode 100644 index 0000000..89cfb32 --- /dev/null +++ a/src/dcxmlbase.cpp @@ -0,0 +1,281 @@ +#include "dcxmlbase.h" +#include "log.h" + +/* + * ________________________________________ + * / Distance doesn't make you any smaller, \ + * | but it does make you part of a larger | + * \ picture. / + * ---------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + * + **********************************************************/ + +using namespace osdev::components; + +void xml_string_writer::write(const void* data, size_t size) +{ + result += std::string(static_cast(data), size); +} + +DcXmlBase::DcXmlBase(const QString& xmlFile) + : m_xmldoc() + , m_xPathHash() +{ + if (!xmlFile.isNull() || !xmlFile.isEmpty()) { + if (parseFile(xmlFile)) + { + LogDebug("[DcXmlBase::DcXmlBase]", std::string("File : " + xmlFile + "..............[OK].")); + } + else + { + LogError("[DcXmlBase::DcXmlBase]", QString("File : %1 ..............[Failed].").arg(xmlFile)); + throw std::runtime_error("[DcXmlBase::DcXmlBase] parseFile failed"); + } + } +} + +DcXmlBase::~DcXmlBase() = default; + +bool DcXmlBase::parseString(const QString& qsXml) +{ + bool bResult = false; + + if (!qsXml.isEmpty()) { + pugi::xml_parse_status parseStatus = m_xmldoc.load_buffer(qsXml.toStdString().c_str(), + qsXml.toStdString().size()) + .status; + bResult = checkError(parseStatus); + } + + return bResult; +} + +bool DcXmlBase::parseFile(const QString& qsXml) +{ + bool bResult = false; + + if (!qsXml.isEmpty()) { + pugi::xml_parse_status parseStatus = m_xmldoc.load_file(qsXml.toStdString().c_str()).status; + bResult = checkError(parseStatus); + } + + return bResult; +} + +bool DcXmlBase::checkError(pugi::xml_parse_status _parseStatus) +{ + bool bResult = false; + QString sLogMessage = "An unknown error occured."; + +/* +* GCC 5.3.1 insits on all enumeration cases for this switch to be specified. +* The enumeration cases that throws -Wswitch warning are not available in pugixml for FC21. +* For backward compatibility with older pugixml version "-Wswitch" warnings are temporarily supressed. +*/ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wswitch" + switch (_parseStatus) { + case pugi::status_ok: + sLogMessage = "File parsed successfully."; + bResult = true; + break; + case pugi::status_file_not_found: + sLogMessage = "File not found."; + break; + case pugi::status_io_error: + sLogMessage = "Some I/O Error occured during reading the file / stream. Check the hardware for errors."; + break; + case pugi::status_out_of_memory: + sLogMessage = "Out of Memory while parsing the file."; + break; + case pugi::status_internal_error: + sLogMessage = "Oh dear... That's different. Something went horribly wrong. It is unrecoverable. We're exiting the whole shabang.."; + break; + case pugi::status_unrecognized_tag: + sLogMessage = "Parsing stopping due to a tag with either an empty name or a name which starts with an incorrect character (Left a '#' somewhere?)"; + break; + case pugi::status_bad_pi: + sLogMessage = "Parsing stopped due to incorrect document declaration/processing instruction. "; + break; + case pugi::status_bad_comment: + sLogMessage = "Parsing stopped due to the invalid construct of a Comment."; + break; + case pugi::status_bad_cdata: + sLogMessage = "Parsing stopped due to the invalid construct of a CDATA section."; + break; + case pugi::status_bad_doctype: + sLogMessage = "Parsing stopped due to the invalid construct of a DocType."; + break; + case pugi::status_bad_pcdata: + sLogMessage = "Parsing stopped due to the invalid construct of a PCDATA section."; + break; + case pugi::status_bad_attribute: + sLogMessage = "Parsing stopped because there was an incorrect attribute, such as an attribute without value or with value that is not quoted (note that is incorrect in XML)."; + break; + case pugi::status_bad_start_element: + sLogMessage = "Parsing stopped because a starting tag either had no closing > symbol or contained some incorrect symbol."; + break; + case pugi::status_bad_end_element: + sLogMessage = "Parsing stopped because ending tag had incorrect syntax (i.e. extra non-whitespace symbols between tag name and >)."; + break; + case pugi::status_end_element_mismatch: + sLogMessage = "parsing stopped because the closing tag did not match the opening one (i.e. ) or because some tag was not closed at all."; + break; + // DISABLED ON FEDORA 21 and CentOS 7. Not present in pugixml 1.0-8.fc21 + // case pugi::status_append_invalid_root: + // case pugi::status_no_document_element: + } +#pragma GCC diagnostic pop + LogDebug("[DcXmlBase::checkError]", sLogMessage); + return bResult; +} + +void DcXmlBase::addXPath(const QString& qsName, const QString& qsXPath) +{ + if (m_xPathHash.contains(qsName)) { + LogWarning("[DcXmlBase::addXPath]", "XPath already registered : " + qsName); + } + m_xPathHash.insert(qsName, qsXPath); + + LogDebug("[DcXmlBase::addXPath]", QString("XPath" + qsXPath + " registered with key : " + qsName)); +} + +QString DcXmlBase::getXPath(const QString& qsXPathSelect) const +{ + QString qsXPath = m_xPathHash.value(qsXPathSelect); + + if (qsXPath.isEmpty()) { + LogWarning("dcxml", "XPath not registered : " + qsXPathSelect); + } + + return qsXPath; +} + +QString DcXmlBase::evaluateXPath(const QString& qsXPathSelect, + const QList& arguments) const +{ + QString qsResult = getXPath(qsXPathSelect); + + // LogInfo( "DcXmlBase::evaluateXPath", QString( "Found XPathExpression : " + qsResult + " for selection : " + qsXPathSelect ) ); + + for (auto& value : arguments) { + qsResult = qsResult.arg(value.toString()); + } + + // LogInfo( "DcXmlBase::evaluateXPath", QString( "Resulting XPathExpression : " + qsResult ) ); + return qsResult; +} + +void DcXmlBase::setSimpleData(const QString& qsXPathSelect, + const QList& arguments, + const QVariant& data) +{ + QString qsXPath = evaluateXPath(qsXPathSelect, arguments); + setNodeData(qsXPath, data); +} + +void DcXmlBase::setSimpleData(const QString& qsXPathSelect, const QVariant& data) +{ + QString qsXPath = getXPath(qsXPathSelect); + + setNodeData(qsXPath, data); +} + +QVariant DcXmlBase::getSimpleData(const QString& qsXPathSelect, + const QList& arguments) const +{ + QString qsXPath = evaluateXPath(qsXPathSelect, arguments); + + QVariant qvResult = getNodeData(qsXPath); + + return qvResult; +} + +// static +bool DcXmlBase::getBoolean(const QVariant& value) +{ + bool b_result = false; + + QString l_result = value.toString().toUpper(); + if ( // ------------------------ + ("Y" == l_result) || + ("YES" == l_result) || + ("TRUE" == l_result) || + ("ON" == l_result) || + ("1" == l_result) + // ------------------------ + ) { + b_result = true; + } + + return b_result; +} + +// static +QString DcXmlBase::getAttributeValue(const pugi::xml_node& xmlNode, const char* attributeName) +{ + const auto attr = xmlNode.attribute(attributeName); + if (attr.empty()) { + return {}; + } + return attr.value(); +} + +pugi::xpath_node DcXmlBase::selectNode(const QString& qsXPath) const +{ + return m_xmldoc.select_node(qsXPath.toStdString().c_str()); +} + +QList DcXmlBase::selectNodes(const QString& qsXPath) const +{ + QList lstResult; + + pugi::xpath_node_set nodes = m_xmldoc.select_nodes(qsXPath.toStdString().c_str()); + for (auto& node : nodes) { + lstResult.append(node); + } + + return lstResult; +} + +void DcXmlBase::setNodeData(const QString& qsXPath, const QVariant& qsData) +{ + pugi::xml_node selectedNode; + selectedNode = selectNode(qsXPath).node(); + + if (!selectedNode.empty()) { + selectedNode.set_value(qsData.toString().toStdString().c_str()); + } + else { + LogError("dcxml", + QString("No node(s) found for XPath expression : '%1'") + .arg(qsXPath)); + } +} + +QVariant DcXmlBase::getNodeData(const QString& qsXPath) const +{ + QVariant qvResult; + pugi::xml_node selectedNode = selectNode(qsXPath).node(); + if (!selectedNode.empty()) { + qvResult = QString(selectedNode.value()); + } + return qvResult; +} + +QString DcXmlBase::asString() const +{ + xml_string_writer writer; + m_xmldoc.save(writer); + + return QString(writer.result.c_str()); +} diff --git b/src/dcxmlbase.h a/src/dcxmlbase.h new file mode 100644 index 0000000..03b251a --- /dev/null +++ a/src/dcxmlbase.h @@ -0,0 +1,178 @@ +#pragma once + +#include +#include + +#include + +namespace osdev { +namespace components { + +/*! + * \brief xml_string_writer + * This struct is used to write a pugiXL DOM document to a std::string + * pugiXML is not able to this on its own, but we use the possibility to dump + * an XML-document to std::cout + */ +struct xml_string_writer : pugi::xml_writer +{ + std::string result = std::string(); + virtual void write(const void* data, size_t size) override; +}; + +/*! + * \brief The dcXmlBase class describes the base class for all xml related classes + * The basic functionality is implemented here. This class is intended to + * be inherited and specialized and not to be used directly. ( Nothing bad + * will happen, just to make life easier ) + */ +/* + * ________________________________________ + * / I'm serious about thinking through all \ + * | the possibilities before we settle on | + * | anything. All things have the | + * | advantages of their disadvantages, and | + * | vice versa. | + * | | + * | -- Larry Wall in | + * \ <199709032332.QAA21669@wall.org> / + * ---------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + */ + +class DcXmlBase +{ +public: + /*! Default constructor */ + explicit DcXmlBase(const std::string& xmlFile = std::string()); + virtual ~DcXmlBase(); + + /*! + * \brief Parses the XML contents of the string. + * \param qsXml - String containing the contents of the XML-string + * \return true if successfull, false if not... + */ + bool parseString(const std::string& qsXml); + + /*! + * \brief Parses the contents of the given XML-file. + * \param qsXml - String with the filepath & -name of the XML file. + * \return true if successfull, false if not... + */ + bool parseFile(const std::string& qsXml); + + /*! + * \brief Adds an XPath expression to the internal structure. + * \param qsName - Name of the XPath expression. This should be descriptive to make life easier + * \param qsXPath - The XPath expression to the specific data we're interested in. + */ + void addXPath(const std::string& qsName, const std::string& qsXPath); + + /*! + * \brief Retrieves an XPath expression from the internal structure + * \param qsXPathSelect - The name of the XPath expression. + * \return The XPath expression as stored in the internal Hash + */ + std::string getXPath(const std::string& qsXPathSelect) const; + + /*! + * \brief Interprets the XPath expression and adds values to the variables. + * \param qsXPathSelect - The XPath expression as given by getXPath. + * \param arguments - The list of variables to be added to the XPath expression. + * \return The interpreted XPath expression. + */ + std::string evaluateXPath(const std::string& qsXPathSelect, const std::vector>& arguments) const; + + /*! + * \brief Set a simple node in the xml-doc. + * \param qsXPathSelect - The XPath expression of the node we want to set. + * \param arguments - the list of arguments in the node + * \param data - The nodetext. + */ + void setSimpleData(const std::string& qsXPathSelect, const QList& arguments, const QVariant& data); + + /*! + * \brief Set a simple node in the xml-doc. + * \param qsXPathSelect - The XPath expression of the node we want to set. + * \param data - The nodetext. + */ + void setSimpleData(const std::string& qsXPathSelect, const QVariant& data); + + QVariant getSimpleData(const std::string& qsXPathSelect, const QList& arguments) const; + + /*! + * \brief Retreive a single node based on the XPath expression + * \param qsXPath - The XPath expression + * \return - a single xpath-node + */ + pugi::xpath_node selectNode(const std::string& qsXPath) const; + + /*! + * \brief Retreives a list of nodes based on the given XPath expression + * \param qsXPath - The XPath expression + * \return - The list of nodes + */ + std::vector selectNodes(const std::string& qsXPath) const; + + /*! Added for convenience. The DOMTree is exported as std::string */ + std::string asString() const; + + /*! Helper method for the setSimpleData */ + void setNodeData(const std::string& qsXPath, const QVariant& qsData); + + //! Helper method for the getSimpleData. Get data from the node that + //! is selected with the XPath expression. + virtual QVariant getNodeData(const std::string& qsXPath) const; + + /** + * @brief Turns a value into a boolean. + * @param value Value to be interpreted. + * @return boolean representation of value + * + * The following (capitalised) items convert to "true" : + * - Y + * - YES + * - TRUE + * - ON + * - 1 + * + * Everything else converts to false. + */ + static bool getBoolean(const QVariant& value); + + /** + * @param xmlNode The node to query for an attribute value. + * @param attributeName The name of the attribute to query. + * @return an attribute value. + * @retval value as std::string + * @retval null std::string when the attribute is not found. + */ + static std::string getAttributeValue(const pugi::xml_node& xmlNode, const char* attributeName); + +private: + /*! + * \brief Translates the pugiXML status into Human Readable messages + * \param parseStatus - The pugi::XML status enum from the parser. + * \return True if there was no error whatsoever. False if not.. 8-| + */ + bool checkError(pugi::xml_parse_status parseStatus); + + /*! + * \brief The internal representation of the XML document. + */ + pugi::xml_document m_xmldoc; + + //! Storage for the XPath expressions + QHash m_xPathHash; +}; + +} // namespace osdev +} // namespace components diff --git b/src/dcxmlconfig.cpp a/src/dcxmlconfig.cpp new file mode 100644 index 0000000..f3b1a14 --- /dev/null +++ a/src/dcxmlconfig.cpp @@ -0,0 +1,461 @@ +#include "dcxmlconfig.h" +#include "log.h" + +#include + +using namespace osdev::components; + +static const char* selectPluginPath = "selectPluginPath"; +static const char* selectConfigPath = "selectConfigPath"; +static const char* selectIniFile = "selectIniFile"; +static const char* selectLogPath = "selectLogPath"; +static const char* selectSslPath = "selectSslPath"; +static const char* selectPlugins = "selectPlugins"; +static const char* selectDbCreds = "selectDbCreds"; +static const char* selectDbUserName = "selectDbUserName"; +static const char* selectDbPassWord = "selectDbPassWord"; +static const char* selectDbName = "selectDbName"; +static const char* selectDbType = "selectDbType"; +static const char* selectDbIdentifier = "selectDbIdentifier"; +static const char* selectDbWdEnable = "selectDbWdEnabled"; +static const char* selectDbWdInterval = "selectDbWdInterval"; +static const char* selectLogFile = "selectLogFile"; +static const char* selectLogLevel = "selectLogLevel"; +static const char* selectModelMapConfig = "selectModelMapConfig"; +static const char* selectLogQueueSizeIntervalSecs = "selectLogQueueSizeIntervalSecs"; +static const char* selectOrmTables = "selectOrmTables"; +static const char* selectRecTrackField = "selectRecTrackField"; +static const char* selectGustavJung = "selectGustavJung"; +// static const char* selectPluginMaxThreads = "selectPluginMaxThreads"; +static const char* selectEventLogging = "selectEventLogging"; +static const char* selectTTL = "selectTTL"; +static const char* selectTransLogPath = "selectTransLogPath"; +static const char* selectJobs = "selectJobs"; +static const char* selectJobSettings = "selectJobSettings"; +static const char* selectJobParameters = "selectJobParameters"; +static const char* selectLogIpAddress = "selectLogIpAddress"; +static const char* selectLogPortNum = "selectLogPortNum"; +static const char* selectIpAddress = "selectIpAddress"; +static const char* selectPortNumber = "selectPortNumber"; +static const char* selectSslCert = "selectSslCert"; +static const char* selectSslKey = "selectSslKey"; +static const char* selectSslCaCert = "selectSslCaCert"; +static const char* selectSslProtocol = "selectSslProtocol"; + +/* Sensor Settings */ +static const char* selectSensorProtocol = "selectSensorProtocol"; +static const char* selectSensorCache = "selectSensorCache"; +static const char* selectSensorServerHost = "selectSensorServerHost"; +static const char* selectSensorServerPort = "selectSensorServerPort"; +static const char* selectSensorTopics = "selectSensorTopics"; + +static const char* selectTranslatorConfigPath = "selectTranslatorConfigPath"; +static const char* selectTranslatorConfigFile = "selectTranslatorConfigFile"; +static const char* selectDatarateDirect = "selectDatarateDirect"; +static const char* selectDatarateTimed = "selectDatarateTimed"; + +static const char* selectDalConfig = "selectDalConfig"; + +// The only instance of the singleton config parser. +std::unique_ptr DCXmlConfig::s_instance( nullptr ); + +DCXmlConfig::DCXmlConfig() + : m_qhDbHash() +{ + constructXPathHash(); + constructEnumHashes(); +} + +DCXmlConfig& DCXmlConfig::Instance() +{ + if( nullptr == s_instance ) + { + s_instance = std::unique_ptr( new DCXmlConfig() ); + } + + return *s_instance; +} + +bool DCXmlConfig::loadConfiguration( const QString& fileName ) +{ + return DcXmlBase::parseFile( fileName ); +} + +QStringList DCXmlConfig::getPluginPaths() const +{ + QStringList pathList; + for (const auto& pathNode : DcXmlBase::selectNodes( DcXmlBase::getXPath( selectPluginPath ) )) + { + pathList.append(pathNode.node().attribute("value").value()); + } + return pathList; +} + +QStringList DCXmlConfig::getPlugins() const +{ + QStringList pluginList; + + QList selectedNodes = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectPlugins ) ); + for ( auto& nodeItem : selectedNodes ) + { + pluginList.append( nodeItem.node().attribute( "value" ).value() ); + } + + return pluginList; +} + +QStringList DCXmlConfig::getOrmTables() const +{ + QStringList tableList; + + QList selectedNodes = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectOrmTables ) ); + for( auto& nodeItem : selectedNodes ) + { + tableList.append( nodeItem.node().attribute("value").value() ); + } + + return tableList; +} + +QList DCXmlConfig::getScheduledJobs() const +{ + QList lstResult; + + QList jobList = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectJobs ) ); + for( auto& jobItem : jobList ) + { + // Create a new JobData object for each job. + JobData oJob; + oJob.setJobName(jobItem.node().attribute("jobname").value()); + + // Get settings for the current Job. + QList varList; + varList.append( oJob.jobName() ); + QList jobSettings = DcXmlBase::selectNodes( DcXmlBase::evaluateXPath( selectJobSettings, varList ) ); + for (auto& jobSetting : jobSettings) { + // Attribute querying not supported in 1.0.x. Only from 1.7 and up. We have to do it the "dumb"-way. + if (!QString(jobSetting.node().attribute("date").value()).isEmpty()) { + oJob.setRunDate(QDate::fromString(QString(jobSetting.node().attribute("date").value()), QString("dd-MM-yyyy"))); + } + + if( QString( jobSetting.node().attribute( "name" ).value() ) == "date" ) + { + oJob.setRunDate( QDate::fromString( QString( jobSetting.node().attribute( "value" ).value() ), QString( "dd-MM-yyyy" ) ) ); + } + + if( QString( jobSetting.node().attribute( "name" ).value() ) == "time" ) + { + oJob.setRunTime( QTime::fromString( QString( jobSetting.node().attribute( "value" ).value() ), QString( "hh:mm:ss" ) ) ); + } + + if( QString( jobSetting.node().attribute( "name" ).value() ) == "interval" ) + { + oJob.setRunInterval( QString( jobSetting.node().attribute( "value" ).value() ).toInt() ); + } + } + + // Get all parameters of this job and add them to the jobData object + QList jobParameters = DcXmlBase::selectNodes( DcXmlBase::evaluateXPath( selectJobParameters, varList ) ); + for( auto& jobParameter : jobParameters ) + { + oJob.addParameter( jobParameter.node().attribute( "name" ).value(), QVariant( QString( jobParameter.node().attribute( "value" ).value() ) ) ); + } + + // If arrived here, we have all data from this job. Store the result. + lstResult.append( oJob ); + } + + return lstResult; +} + +bool DCXmlConfig::mqttDirectEnabled() const +{ + if (QString(DcXmlBase::selectNode(DcXmlBase::getXPath(selectDatarateDirect)).node().attribute("enabled").value()) == "1") + return true; + + return false; +} + +int DCXmlConfig::mqttTimedInterval() const +{ + return DcXmlBase::selectNode(DcXmlBase::getXPath(selectDatarateTimed)).node().attribute("interval").as_int(); +} + +QString DCXmlConfig::getRecordTrackFieldName() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectRecTrackField ) ).node().attribute( "value" ).value(); +} + +QHash DCXmlConfig::getDbCredentials() const +{ + return this->getServiceSettings(selectDbCreds, m_qhDbHash); +} + +bool DCXmlConfig::getEnableDbWatchDog() const +{ + return getBoolean( DcXmlBase::selectNode( DcXmlBase::getXPath( selectDbWdEnable ) ).node().attribute("value").value() ); +} + +int DCXmlConfig::getDbCheckInterval() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectDbWdInterval ) ).node().attribute("value").as_int(); +} + +QString DCXmlConfig::getLogPath() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectLogPath ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getLogFile() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectLogFile ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getIniFile() const +{ + auto iniFile = DcXmlBase::selectNode( DcXmlBase::getXPath( selectIniFile ) ).node().attribute("value").value(); + return getConfigPath() + "/" + iniFile; +} + +QString DCXmlConfig::getLogLevel() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectLogLevel ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getModelMapConfig() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectModelMapConfig ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getDalConfig() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectDalConfig ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getConfigPath() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectConfigPath ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getLogIpAddress() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectLogIpAddress ) ).node().attribute( "value" ).value(); +} + +int DCXmlConfig::getLogPortNum() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectLogPortNum ) ).node().attribute( "value" ).as_int(); +} + +QString DCXmlConfig::getIpAddress() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectIpAddress ) ).node().attribute("value").value(); +} + +int DCXmlConfig::getPortNumber() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectPortNumber ) ).node().attribute("value").as_int(); +} + +QString DCXmlConfig::getSslPath() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectSslPath ) ).node().attribute("value").value(); +} + +QString DCXmlConfig::getSslCert() const +{ + return ( this->getSslPath() + DcXmlBase::selectNode( DcXmlBase::getXPath( selectSslCert ) ).node().attribute("value").value() ); +} + +QString DCXmlConfig::getSslKey() const +{ + return ( this->getSslPath() + DcXmlBase::selectNode( DcXmlBase::getXPath( selectSslKey ) ).node().attribute("value").value() ); +} + +QString DCXmlConfig::getSslCaCert() const +{ + return ( this->getSslPath() + DcXmlBase::selectNode( DcXmlBase::getXPath( selectSslCaCert ) ).node().attribute("value").value() ); +} + +QString DCXmlConfig::getSslProtocol() const +{ + return ( this->getSslPath() + DcXmlBase::selectNode( DcXmlBase::getXPath( selectSslProtocol ) ).node().attribute("value").value() ); +} + +QString DCXmlConfig::transactionPath() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectTransLogPath ) ).node().attribute("value").value(); +} + +int DCXmlConfig::timeToLive() const +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectTTL ) ).node().attribute("value").as_int(); +} +// =========================================================================================================================================== +// Sensor Settings Co2Mos +// ------------------------------------------------------------------------------------------------------------------------------------------- +QString DCXmlConfig::getSensorProtocol() +{ + return DcXmlBase::selectNode(DcXmlBase::getXPath( selectSensorProtocol ) ).node().attribute( "value" ).value(); +} + +bool DCXmlConfig::getSensorCache() +{ + return getBoolean( DcXmlBase::selectNode( DcXmlBase::getXPath( selectSensorCache ) ).node().attribute("value").value() ); +} + +QString DCXmlConfig::getSensorServerHost() +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectSensorServerHost ) ).node().attribute( "value" ).value(); +} + +int DCXmlConfig::getSensorServerPort() +{ + return DcXmlBase::selectNode( DcXmlBase::getXPath( selectSensorServerPort ) ).node().attribute( "value" ).as_int(); +} + +QStringList DCXmlConfig::getSensorTopicList() +{ + QStringList l_result; + + QList node_list = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectSensorTopics ) ); + for( const auto& topic_node : node_list ) + { + l_result.append( topic_node.node().attribute( "value" ).value() ); + } + + return l_result; +} + +// =========================================================================================================================================== +void DCXmlConfig::constructXPathHash() +{ + // Generic Paths + DcXmlBase::addXPath( selectConfigPath , "//configuration/context[@name='global']/context[@name='paths']/setting[@name='configpath']" ); + DcXmlBase::addXPath( selectIniFile , "//configuration/context[@name='global']/context[@name='paths']/setting[@name='inifile']" ); + DcXmlBase::addXPath( selectLogPath , "//configuration/context[@name='global']/context[@name='paths']/setting[@name='logpath']" ); + DcXmlBase::addXPath( selectSslPath , "//configuration/context[@name='global']/context[@name='paths']/setting[@name='sslpath']" ); + + // Plugin SettingsePmsServiceName + DcXmlBase::addXPath( selectPluginPath , "//configuration/context[@name='global']/context[@name='paths']/setting[@name='pluginpath']" ); + DcXmlBase::addXPath( selectPlugins , "//configuration/context[@name='global']/context[@name='plugins']/setting" ); + + // Database Settings + DcXmlBase::addXPath( selectDbCreds , "//configuration/context[@name='global']/context[@name='database']/setting" ); + DcXmlBase::addXPath( selectDbUserName , "//configuration/context[@name='global']/context[@name='database']/setting[@name='username']" ); + DcXmlBase::addXPath( selectDbPassWord , "//configuration/context[@name='global']/context[@name='database']/setting[@name='password']" ); + DcXmlBase::addXPath( selectDbName , "//configuration/context[@name='global']/context[@name='database']/setting[@name='dbName']" ); + DcXmlBase::addXPath( selectDbType , "//configuration/context[@name='global']/context[@name='database']/setting[@name='dbtype']" ); + DcXmlBase::addXPath( selectDbIdentifier , "//configuration/context[@name='global']/context[@name='database']/setting[@name='identifier']" ); + DcXmlBase::addXPath( selectDbWdEnable , "//configuration/context[@name='global']/context[@name='database']/watchdog/setting[@name='enabled']" ); + DcXmlBase::addXPath( selectDbWdInterval , "//configuration/context[@name='global']/context[@name='database']/watchdog/setting[@name='interval']" ); + + // ORM Settings + DcXmlBase::addXPath( selectOrmTables , "//configuration/context[@name='orm']/context[@name='tables']/setting" ); + DcXmlBase::addXPath( selectRecTrackField, "//configuration/context[@name='orm']/context[@name='record_tracking']/setting" ); + + // Transaction Queue Settings + DcXmlBase::addXPath( selectTTL , "//configuration/context[@name='transqueue']/setting[@name='ttl']" ); + DcXmlBase::addXPath( selectTransLogPath , "//configuration/context[@name='transqueue']/setting[@name='transaction_logs']" ); + + // System SettingsePmsServiceName + DcXmlBase::addXPath( selectGustavJung , "//configuration/context[@name='global']/context[@name='system']/setting[@name='synchronicity']" ); +// DcXmlBase::addXPath( selectPluginMaxThreads, "//configuration/context[@name='global']/context[@name='system']/setting[@name='plugin_max_threads']" ); + DcXmlBase::addXPath( selectEventLogging , "//configuration/context[@name='global']/context[@name='logging']/setting[@name='event_logging']" ); + DcXmlBase::addXPath( selectLogFile , "//configuration/context[@name='global']/context[@name='logging']/setting[@name='log_file']" ); + DcXmlBase::addXPath( selectLogLevel , "//configuration/context[@name='global']/context[@name='logging']/setting[@name='log_level']" ); + DcXmlBase::addXPath(selectLogQueueSizeIntervalSecs, "//configuration/context[@name='global']/context[@name='logging']/setting[@name='queue_size_interval_secs']"); + + // Modelmapper Settings + DcXmlBase::addXPath( selectModelMapConfig, "//configuration/context[@name='modelmapper']/context[@name='modelmapping']/setting[@name='modelmapconfig']" ); + + // Jobscheduler settings + DcXmlBase::addXPath( selectJobs , "//configuration/context[@name='jobscheduler']/jobs/job" ); + DcXmlBase::addXPath( selectJobSettings , "//configuration/context[@name='jobscheduler']/jobs/job[@name='%1']/settings/setting" ); + DcXmlBase::addXPath( selectJobParameters, "//configuration/context[@name='jobscheduler']/jobs/job[@name='%1']/parameters/parameter" ); + + // EqMqtt Config settings + DcXmlBase::addXPath(selectTranslatorConfigPath, "//configuration/context[@name='eqmqtt']/context[@name='translation']/setting[@name='translate_path']"); + DcXmlBase::addXPath(selectTranslatorConfigFile, "//configuration/context[@name='eqmqtt']/context[@name='translation']/setting[@name='translate_file']"); + DcXmlBase::addXPath(selectDatarateDirect, "//configuration/context[@name='eqmqtt']/context[@name='datarate']/setting[@name='direct']"); + DcXmlBase::addXPath(selectDatarateTimed, "//configuration/context[@name='eqmqtt']/context[@name='datarate']/setting[@name='timed']"); + + // Remote Log Viewer / server + DcXmlBase::addXPath( selectLogIpAddress , "//configuration/context[@name='logprocessor']/setting[@name='ip-address']" ); + DcXmlBase::addXPath( selectLogPortNum , "//configuration/context[@name='logprocessor']/setting[@name='port']" ); + + // MedCow settings + DcXmlBase::addXPath( selectIpAddress , "//configuration/context[@name='network']/setting[@name='ip_address']" ); + DcXmlBase::addXPath( selectPortNumber , "//configuration/context[@name='network']/setting[@name='port_no']" ); + DcXmlBase::addXPath( selectSslCert , "//configuration/context[@name='network']/setting[@name='ssl_cert']" ); + DcXmlBase::addXPath( selectSslKey , "//configuration/context[@name='network']/setting[@name='ssl_key']" ); + DcXmlBase::addXPath( selectSslCaCert , "//configuration/context[@name='network']/setting[@name='ssl_ca']" ); + DcXmlBase::addXPath( selectSslProtocol , "//configuration/context[@name='network']/setting[@name='ssl_protocol']" ); + + // Dal settings + DcXmlBase::addXPath( selectDalConfig , "//configuration/context[@name='dal']/context[@name='data_access']/setting[@name='dalconfig']" ); + + // Sensor Settings + DcXmlBase::addXPath( selectSensorProtocol , "//configuration/context[@name='sensors']/setting[@name='protocol']" ); + DcXmlBase::addXPath( selectSensorCache , "//configuration/context[@name='sensors']/setting[@name='cached']" ); + DcXmlBase::addXPath( selectSensorServerHost , "//configuration/context[@name='sensors']/setting[@name='serverhost']" ); + DcXmlBase::addXPath( selectSensorServerPort , "//configuration/context[@name='sensors']/setting[@name='serverport']" ); + DcXmlBase::addXPath( selectSensorTopics , "//configuration/context[@name='sensors']/topics/topic" ); + + // Dal settings + DcXmlBase::addXPath( selectDalConfig , "//configuration/context[@name='dal']/context[@name='data_access']/setting[@name='dalconfig']" ); +} + +void DCXmlConfig::constructEnumHashes() +{ + // Build the Db Translator Hash + m_qhDbHash.insert( "username", eDbSettings::eDbUsername ); + m_qhDbHash.insert( "password", eDbSettings::eDbPassword ); + m_qhDbHash.insert( "dbname", eDbSettings::eDbName ); + m_qhDbHash.insert( "hostname", eDbSettings::eDbHostName ); + m_qhDbHash.insert( "identifier", eDbSettings::eDbIdentifier ); + m_qhDbHash.insert( "dbtype", eDbSettings::eDbType ); +} + +bool DCXmlConfig::getEventLogging() const +{ + bool b_result = false; + + b_result = getBoolean( DcXmlBase::selectNode( DcXmlBase::getXPath( selectEventLogging ) ).node().attribute("value").value() ); + + return b_result; +} + +bool DCXmlConfig::getSynchronicity() const +{ + bool b_result = false; + + b_result = getBoolean( DcXmlBase::selectNode( DcXmlBase::getXPath( selectGustavJung ) ).node().attribute("value").value() ); + + return b_result; +} + +int DCXmlConfig::getLogQueueSizeIntervalSecs() const +{ + return DcXmlBase::selectNode(DcXmlBase::getXPath(selectLogQueueSizeIntervalSecs)).node().attribute("value").as_int(); +} + +bool DCXmlConfig::getBoolean( const QVariant& value ) const +{ + bool b_result = false; + + QString l_result = value.toString().toUpper(); + if ( // ------------------------ + ( "Y" == l_result ) || + ( "YES" == l_result ) || + ( "TRUE" == l_result ) || + ( "ON" == l_result ) || + ( "1" == l_result ) + // ------------------------ + ) + { + b_result = true; + } + + return b_result; +} diff --git b/src/dcxmlconfig.h a/src/dcxmlconfig.h new file mode 100644 index 0000000..d8d1dc9 --- /dev/null +++ a/src/dcxmlconfig.h @@ -0,0 +1,282 @@ +#ifndef OSDEV_COMPONENTS_DCXMLCONFIG_H +#define OSDEV_COMPONENTS_DCXMLCONFIG_H + +#include "dcxmlbase.h" +#include "jobdata.h" + +#include +#include +#include + +namespace osdev { +namespace components { + +/*! \brief This component locates any configuration information needed + * to initialise the application and its components. + * Parses this information and make it available to other + * components that need it. + * + * This component is implemented as a singleton. XML has the "habit" + * of expand 15 times the filesize so with this in mind, it is more + * future proof to load this only once into memory. + * + *//* + * ________________________________________ + * / TCP/IP Slang Glossary, #1: \ + * | | + * | Gong, n: Medieval term for privy, or | + * | what pased for them in that era. Today | + * | used whimsically to describe the | + * | aftermath of a bogon attack. Think of | + * | our community as the Galapagos of the | + * | English language. | + * | | + * | "Vogons may read you bad poetry, but | + * | bogons make you study obsolete RFCs." | + * | | + * \ -- Dave Mills / + * ---------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + * + */ + +class DCXmlConfig : public DcXmlBase +{ +public: + enum eDbSettings + { + eDbUnknown = 0, ///< The default value if translation is not possible + eDbUsername, ///< The username used to connect to the DATA scheme + eDbPassword, ///< The corresponding password + eDbName, ///< The name of the database instance + eDbHostName, ///< The hostname the database resides on + eDbIdentifier, ///< Identifier. Used internally to distinguish between multiple connections / Databases + eDbType ///< The type of database we're connecting to. This can be all databases known by the Qt framework. (QPSQL, QMYSQL, QOCI etc..) + }; + + /// @return The one and only instance of the config object + static DCXmlConfig& Instance(); + + // ====================================================================== + /// The constructor. + DCXmlConfig(); + + /// Deleted copy-constructor + DCXmlConfig(const DCXmlConfig&) = delete; + + /// Deleted assignment operator + DCXmlConfig& operator=(const DCXmlConfig&) = delete; + + /// Deleted move-constructor + DCXmlConfig(DCXmlConfig&&) = delete; + + /// Deleted move-operator + DCXmlConfig& operator=(DCXmlConfig&&) = delete; + // ====================================================================== + /// @brief Loads the configuration from the given filename. + /// @param fileName - The configuration file in XML format. + virtual bool loadConfiguration( const QString& fileName ); + + /// @return Plugin paths that are defined in the Configuration file + QStringList getPluginPaths() const; + + /// @return Plugins to load + QStringList getPlugins() const; + + /// @return The tables, the ORM layer is a representation of. + QStringList getOrmTables() const; + + /// @return The name of the field in each table, which represents the last timestamp this record was mutated. + QString getRecordTrackFieldName() const; + + /// @return A QHash with all database credentials. + QHash getDbCredentials() const; + + /// @return Enable or not the database watchdog. + bool getEnableDbWatchDog() const; + + /// @return Get the number of seconds each connection check should run. + int getDbCheckInterval() const; + + /// @return Returns the path to the logging + QString getLogPath() const; + + /// @return Returns the logfilename + QString getLogFile() const; + + /// @return Returns the inifilename + QString getIniFile() const; + + /// @return Returns the LogLevel + QString getLogLevel() const; + + /// @return Returns the interval in seconds we want to use for logging the queue size(s). + int getLogQueueSizeIntervalSecs() const; + + /// @return Returns the ModelMapConfig filename + QString getModelMapConfig() const; + + /// @return Returns the DalConfig filename + QString getDalConfig() const; + + /// @return Returns the path to the configuration directory. + QString getConfigPath() const; + + /// @return Returns the switch for logging inside the EventManager. + /// true means log everything + /// false means log nothing + bool getEventLogging() const; + + /*! @return Returns the switch for synchronous processing. + * true means synchronous processing + * false means a-synchronous processing + */ + bool getSynchronicity() const; + + /*! + * \brief getLogIpAddress + * \return + */ + QString getLogIpAddress() const; + + /*! + * \brief getLogPortNum + * \return + */ + int getLogPortNum() const; + + /// @return Returns the ip addresses on which connections are allowed + QString getIpAddress() const; + + /// @return Returns the port number on which connections are allowed + int getPortNumber() const; + + /// @return Returns the ssl path where the certificates are found + QString getSslPath() const; + /// @return Returns the private ssl certificate name + QString getSslCert() const; + /// @return Returns the private ssl key name + QString getSslKey() const; + /// @return Returns the ssl CA certificate name + QString getSslCaCert() const; + /// @return Returns the protocol to use + QString getSslProtocol() const; + + /// Sensor Settings, mainly the brokersettings.. + QString getSensorProtocol(); + bool getSensorCache(); + QString getSensorServerHost(); + int getSensorServerPort(); + QStringList getSensorTopicList(); + + /*! + * \brief getMaxPluginThreads + * \return + */ +// int getMaxPluginThreads() const; + + /*! + * @return Returns the (relative path where the reject transactions will be logged. + * If empty, the default log location will be used. + */ + QString transactionPath() const; + + /*! + * @return Returns the TimeToLive counter. This will determine how many times a + * transaction can pass the queue. On each pass, its TTL-counter will be + * decreased until it reaches "zero". In that case it will be dumped to disk. + */ + int timeToLive() const; + + /*! + * \brief Retrieve all jobs configured in the main configuration file. + * \return A list with all scheduled jobs, empty if none were mentioned. + */ + QList getScheduledJobs() const; + + /*! + * \brief Check if the EqMqtt client is sending its data direct, or through a trimed interval. + * \return True if direct is enabled. False if direct is disabled, switching timed on. (Default) + */ + bool mqttDirectEnabled() const; + + /*! + * \brief Retrieve the time interval ( in seconds ) for the EqMqtt client to send its data in. + * \return Number of seconds (Integer) + */ + int mqttTimedInterval() const; + +private: + /// @brief Builds the XPath hash to access the nodes directly. + void constructXPathHash(); + + /// @brief Builds the enum hashes, used to translate strings to enums. + void constructEnumHashes(); + + /// @return True on success, false on failure + bool openConfig(); + + /// @return True on success, false on failure + bool closeConfig(); + + /// @return True if a configuration could be found, false otherwise + bool configExists(); + + /** + * @brief Turns a value into a boolean. + * @param value Value to be interpreted. + * @return boolean representation of value + * + * The following (capitalised) items convert to "true" : + * - Y + * - YES + * - TRUE + * - ON + * - 1 + * + * Everything else converts to false. + */ + bool getBoolean( const QVariant& value ) const; + + /** + * @brief Get the service settings from the xml configuration. + * @tparam TEnumSettings The enumeration of the various service settings. + * @tparam unknownValue The value to return when the translation from config item to enumeration value cannot be made. + * @param selectSettings Key that describes which service settings to get. + * @param translatorHash The hash that holds the mapping between config item names and their respective enumeration value. + * @return Hash with the requested configuration information. + */ + template + QHash getServiceSettings(const char* selectSettings, const QHash& translatorHash) const + { + QHash l_settings; + + QList selectedNodes = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectSettings ) ); + for( auto& selectedNode : selectedNodes ) + { + const QString key = QString(selectedNode.node().attribute( "name" ).value()).toLower(); + l_settings.insert( translatorHash.value(key, unknownValue) + , selectedNode.node().attribute( "value" ).value() ); + } + return l_settings; + } + + static std::unique_ptr s_instance; ///< Instantiated Config object. + + + QHash m_qhDbHash; ///< The Db Settings Translator Hash. +}; + +} /* End namespace components */ +} /* End namespace osdev */ + + +#endif /* OSDEV_COMPONENTS_DCXMLCONFIG_H */ diff --git b/src/dcxmldal.cpp a/src/dcxmldal.cpp new file mode 100644 index 0000000..c604bfc --- /dev/null +++ a/src/dcxmldal.cpp @@ -0,0 +1,78 @@ +#include "dcxmldal.h" +#include "dcxmlconfig.h" + +using namespace osdev::components; + +static const char* selectFunctions = "selectFunctions"; // return all functions +static const char* selectFunctionByName = "selectFunction"; // return function by command name + +// The only instance of the singleton Dal Config Parser. +std::unique_ptr DcXmlDal::s_instance( nullptr ); + +DcXmlDal& DcXmlDal::Instance() +{ + if( nullptr == s_instance ) + { + s_instance = std::unique_ptr( new DcXmlDal( DCXmlConfig::Instance().getDalConfig() ) ); + } + + return *s_instance; +} + +DcXmlDal::DcXmlDal( const QString &fileName ) +{ + if( !fileName.isEmpty() ) + { + if( !loadConfiguration( fileName ) ) + { + LogWarning( "[DcXmlDal::DcXmlDal]", QString( "There was an error reading file : %1" ).arg( fileName ) ); + } + } + else + { + LogWarning( "[DcXmlDal::DcXmlDal]", QString( "No filename given. No configuration read." ) ); + } + + constructXPathHash(); +} + +DcXmlDal::~DcXmlDal() +{ +} + +bool DcXmlDal::loadConfiguration( const QString &fileName ) +{ + return loadConfiguration( DCXmlConfig::Instance().getConfigPath(), fileName ); +} + +bool DcXmlDal::loadConfiguration( const QString &configDir, const QString &fileName ) +{ + return DcXmlBase::parseFile( configDir + fileName ); +} + +void DcXmlDal::constructXPathHash() +{ + DcXmlBase::addXPath( selectFunctions , "//data_access/functions/function" ); + DcXmlBase::addXPath( selectFunctionByName , "//data_access/functions/function[@name='%1']" ); +} + +QStringList DcXmlDal::getQueries() const +{ + QStringList lstResult; + + QList functionList = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectFunctions ) ); + for( const auto& nodeItem : functionList ) + { + lstResult.append( nodeItem.node().attribute( "name" ).value() ); + } + + return lstResult; +} + +QString DcXmlDal::getQueryByName( const QString &_queryName ) const +{ + QList varList; + varList.append( _queryName ); + + return QString( DcXmlBase::selectNode( evaluateXPath( selectFunctionByName, varList ) ).node().attribute( "value" ).value() ); +} diff --git b/src/dcxmldal.h a/src/dcxmldal.h new file mode 100644 index 0000000..c97ea50 --- /dev/null +++ a/src/dcxmldal.h @@ -0,0 +1,78 @@ +#ifndef OSDEV_COMPONENTS_DCXMLDAL_H +#define OSDEV_COMPONENTS_DCXMLDAL_H + +#include +#include + +#include "dcxmlbase.h" +#include "log.h" +#include "memory" + +namespace osdev { +namespace components { + +/*! + * \brief The DcXmlDal class + * Reads data from the configuration and makes it easily accessible + */ +class DcXmlDal : public DcXmlBase +{ +public: + /// @return the one and only instance of the config object. + static DcXmlDal& Instance(); + + /// The constructor + DcXmlDal( const QString &fileName = QString() ); + + /// The destructor + virtual ~DcXmlDal(); + + /// Deleted copy-constructor + DcXmlDal( const DcXmlDal& ) = delete; + /// Deleted assignment constructor + DcXmlDal& operator=( const DcXmlDal& ) = delete; + /// Deleted move-constructor + DcXmlDal( DcXmlDal&& ) = delete; + /// Deleted move-operator + DcXmlDal& operator=( DcXmlDal&& ) = delete; + // ========================================================== + + /*! + * \brief loadConfiguration - This function loads the xml file into memory + * \param configDir - configuration directory + * \param fileName - configuration file name + * \return true if succeeded + */ + bool loadConfiguration( const QString &configDir, const QString &fileName ); + /*! + * \brief loadConfiguration - This function loads the xml file into memory + * \param fileName - configuration file name + * \return true if succeeded + */ + bool loadConfiguration( const QString &fileName ); + + /*! + * \brief constructXPathHash - This function initializes all XML XPaths + */ + void constructXPathHash(); + + /*! + * \brief getQueries - This function fetches all function names from the config + * \return a list of function names received from the config + */ + QStringList getQueries() const; + /*! + * \brief getQueryByName - This function returns the value of the requested function name + * \param _queryName - function name to look up + * \return the value matching to the function name + */ + QString getQueryByName( const QString &_queryName ) const; + +private: + static std::unique_ptr s_instance; ///< Singleton pointer +}; + +} /* end namespace components */ +} /* end namespace osdev */ + +#endif // OSDEV_COMPONENTS_DCXMLDAL_H diff --git b/src/dcxmletlnetwork.cpp a/src/dcxmletlnetwork.cpp new file mode 100644 index 0000000..5099ee9 --- /dev/null +++ a/src/dcxmletlnetwork.cpp @@ -0,0 +1,365 @@ +#include "dcxmletlnetwork.h" +#include "log.h" + +using namespace osdev::components; + +// XPath names +// Network related +static const char* selectNetworks = "selectNetworks"; +static const char* selectNetwork = "selectNetwork"; + +// Functional Objects +static const char* selectObjects = "selectObjects"; +static const char* selectObject = "selectObject"; +// Inputs +static const char* selectInputs = "selectInputs"; +static const char* selectInput = "selectInput"; +static const char* selectInputVarNames = "selectInputVarNames"; +// Outputs +static const char* selectOutputs = "selectOutputs"; +static const char* selectOutput = "selectOutput"; +static const char* selectOutputVarNames = "selectOutputVarNames"; + +// Connections +static const char* selectConnections = "selectConnections"; + +// QThread priorities +static const char* prio_idle = "prio_idle"; +static const char* prio_lowest = "prio_lowest"; +static const char* prio_low = "prio_low"; +static const char* prio_normal = "prio_normal"; +static const char* prio_high = "prio_high"; +static const char* prio_highest = "prio_highest"; +static const char* prio_time_critical = "prio_time_critical"; +static const char* prio_inherit = "prio_inherit"; + +namespace { + +/** + * @brief Generates DataObject input data from an xml inputs node. + * The inputs node contains one or more input nodes which describe a database table column + */ +class GenerateInputData +{ +public: + // Non constructable + GenerateInputData() = delete; + + /** + * @brief Generate input data from an inputs node. + * @param inputsNode xml inputs node. The method does nothing when an xml node with a different name is supplied. + * @param[out] data The input data is added to this instance. + */ + static void generate(const pugi::xml_node& inputsNode, ObjectData& data); + +private: + /** + * @brief Internal method that parses an xml input node and calls this method on its child nodes when appropriate. + * @param inputNode xml input node. The method does nothing when an xml node with a different name is supplied. + * @param containerPath The context for which the input is generated. This argument is supplied by copy so that + * recursive calls can expand the context. + * @param[out] data The input data is added to this instance. + * @param level The recursion level (for debugging purposes) + */ + static void handleInputNode(const pugi::xml_node& inputNode, QStringList containerPath, ObjectData& data, int level); +}; + +// static +void GenerateInputData::generate(const pugi::xml_node& inputsNode, ObjectData& data) +{ + if (std::string(inputsNode.name()) != std::string("inputs")) { + return; + } + + for (auto inputNode = inputsNode.child("input"); inputNode; inputNode = inputNode.next_sibling("input")) { + handleInputNode(inputNode, QStringList{}, data, 1); + } +} + +// static +void GenerateInputData::handleInputNode(const pugi::xml_node& inputNode, QStringList containerPath, ObjectData& data, int level) +{ + LogTrace("[DcXmlEtlNetwork GenerateInputData]", QString("%1. current container path : %2").arg(level).arg(containerPath.join("/"))); + const auto containerAttribute = inputNode.attribute("container"); + const auto nameAttribute = inputNode.attribute("name"); + // only expand the container when the container attribute is available and the container path is either empty or the last item in the container path + // is different from the container attribute value. + if (containerAttribute && (containerPath.empty() || containerPath.back() != QString(containerAttribute.value()))) { + containerPath.append(containerAttribute.value()); + LogTrace("[DcXmlEtlNetwork GenerateInputData]", QString("%1. container path becomes %2").arg(level).arg(containerPath.join("/"))); + } + if (nameAttribute) { + auto excludeFlagAttr = inputNode.attribute("exclude_from_identity_check"); + bool excludeFlag = false; + if (!excludeFlagAttr.empty()) { + excludeFlag = DcXmlBase::getBoolean(excludeFlagAttr.value()); + } + LogTrace("[DcXmlEtlNetwork GenerateInputData]", QString("%1. Generate data for %2 and put it in %3").arg(level).arg(containerPath.join("/") + QString(".") + nameAttribute.value(), data.getObjectId())); + data.setInputData(containerPath.join("/") + QString(".") + nameAttribute.value(), + DcXmlBase::getAttributeValue(inputNode, "type"), + DcXmlBase::getAttributeValue(inputNode, "id"), + DcXmlBase::getAttributeValue(inputNode, "default"), + excludeFlag); + } + else { + for (auto childInputNode = inputNode.child("input"); childInputNode; childInputNode = childInputNode.next_sibling("input")) { + handleInputNode(childInputNode, containerPath, data, level + 1); + } + } +} + +} // namespace + +DcXmlEtlNetwork::DcXmlEtlNetwork() + : m_qhPriority() +{ + constructXPathHash(); + constructEnumHash(); +} + +DcXmlEtlNetwork::DcXmlEtlNetwork(const QString& fileName) + : m_qhPriority() +{ + if (!DcXmlBase::parseFile(fileName)) { + LogError("[DcXmlEtlNetwork]", + QString("There was an error reading configuration : %1").arg(fileName)); + throw std::runtime_error("[DcXmlEtlNetwork] parseFile failed"); + } + + constructXPathHash(); + constructEnumHash(); +} + +DcXmlEtlNetwork::~DcXmlEtlNetwork() = default; + +QStringList DcXmlEtlNetwork::getNetworkNames() const +{ + QStringList qsResult; + + QList nodeList = DcXmlBase::selectNodes(DcXmlBase::getXPath(selectNetworks)); + for (const auto& nodeItem : nodeList) { + qsResult.append(DcXmlBase::getAttributeValue(nodeItem.node(), "name")); + } + + return qsResult; +} + +QList> DcXmlEtlNetwork::getObjectsOfNetwork(const QString& networkName) const +{ + QList> lstResult; + + // Create the correct XPath expression. We have the service already... + QList varList; + varList.append(networkName); + + // First we get a list of objects + QList objectList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectObjects, varList)); + for (const auto& objectItem : objectList) { + auto pData = QSharedPointer::create(); + pData->setObjectId(DcXmlBase::getAttributeValue(objectItem.node(), "id")); + pData->setObjectType(DcXmlBase::getAttributeValue(objectItem.node(), "class")); + //==================================================================================== + // Get some headerinformation based on the objecttype. Some fields are re-used. + //==================================================================================== + if (pData->getObjectType() == "output") { + pData->setTargetName(DcXmlBase::getAttributeValue(objectItem.node(), "target")); + pData->setTargetAction(DcXmlBase::getAttributeValue(objectItem.node(), "default_action")); + pData->setKeyField(DcXmlBase::getAttributeValue(objectItem.node(), "key_field")); + pData->setForeignKeyField(DcXmlBase::getAttributeValue(objectItem.node(), "foreignkey_field")); + auto targetFieldAttr = objectItem.node().attribute("target_field"); // Available in combination with batch/merge update. If not set then targetField is the same as foreignKeyField + if (targetFieldAttr.empty()) { + pData->setTargetField(pData->getForeignKeyField()); + } + else { + pData->setTargetField(targetFieldAttr.value()); + } + + // Available in combination with merge update. Default is NULL. + pData->setTargetResetValue(DcXmlBase::getAttributeValue(objectItem.node(), "target_reset_value")); + + auto nonPersistentTimestampUsageAttr = objectItem.node().attribute("use_non_persistent_timestamp"); + if (!nonPersistentTimestampUsageAttr.empty()) { + pData->setNonPersistentTimestampUsage(getBoolean(nonPersistentTimestampUsageAttr.value())); + } + if (pData->getNonPersistentTimestampUsage()) { + auto nonPersistentTimestampBufferSize = objectItem.node().attribute("nr_of_timestamps"); + if (!nonPersistentTimestampBufferSize.empty()) { + bool conversionOk = false; + auto nrOfTimestamps = QVariant(nonPersistentTimestampBufferSize.value()).toUInt(&conversionOk); + if (conversionOk && nrOfTimestamps > 0) { + pData->setNonPersistentTimestampBufferSize(nrOfTimestamps); + } + else { + LogWarning("[DcXmlEtlNetwork]", + QString("Invalid nr_of_timestamps value %1, using default buffersize").arg(nonPersistentTimestampBufferSize.value())); + } + } + } + } + + if (pData->getObjectType() == "datafilter") { + pData->setTargetAction(DcXmlBase::getAttributeValue(objectItem.node(), "default_action")); + pData->setKeyField(DcXmlBase::getAttributeValue(objectItem.node(), "key_field")); + pData->setKeyValue(DcXmlBase::getAttributeValue(objectItem.node(), "key_value")); + } + //==================================================================================== + // Get all inputs if there are any + varList.clear(); + varList.append(networkName); + varList.append(DcXmlBase::getAttributeValue(objectItem.node(), "id")); + + if (pData->getObjectType() == "output") { + auto outputVarList = varList; + outputVarList.append("output"); // add the class + + // select all inputs from an output object + QList inputsList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectInputs, outputVarList)); + + // Only one inputs (plural!) node is expected. + // The for loop is defensive programming + for (const auto& xpathInputsNode : inputsList) { + GenerateInputData::generate(xpathInputsNode.node(), *pData); + } + } + else if (pData->getObjectType() == "datafilter") { + auto datafilterVarList = varList; + datafilterVarList.append("datafilter"); // add the class + + // select all inputs from an output object + QList inputsList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectInputs, datafilterVarList)); + + // Only one inputs (plural!) node is expected. + // The for loop is defensive programming + for (const auto& xpathInputsNode : inputsList) { + for (auto inputNode = xpathInputsNode.node().child("input"); inputNode; inputNode = inputNode.next_sibling("input")) { + pData->setInputData(DcXmlBase::getAttributeValue(inputNode, "name"), + DcXmlBase::getAttributeValue(inputNode, "type"), + QString{}, // no id + DcXmlBase::getAttributeValue(inputNode, "default"), + false); + } + } + } + + // And finally all outputs. + varList.clear(); + varList.append(networkName); + varList.append(DcXmlBase::getAttributeValue(objectItem.node(), "id")); + QList outputList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectOutputs, varList)); + for (const auto& outputItem : outputList) { + pData->setOutputData(DcXmlBase::getAttributeValue(outputItem.node(), "name"), + DcXmlBase::getAttributeValue(outputItem.node(), "type"), + DcXmlBase::getAttributeValue(outputItem.node(), "default")); + } + + // Add the entire objectData to the list... + lstResult.append(pData); + } + + return lstResult; +} + +Connections DcXmlEtlNetwork::getConnectionsOfNetwork(const QString& networkName) const +{ + Connections cResult; + + // Create the variable list and create the correct XPath expression. + QList varList; + varList.append(networkName); + + // Get the list of connections.. + QList connectList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectConnections, varList)); + for (const auto& connectItem : connectList) { + const auto node = connectItem.node(); + cResult.addConnection(DcXmlBase::getAttributeValue(node, "source"), + DcXmlBase::getAttributeValue(node, "target"), + DcXmlBase::getAttributeValue(node, "output"), + DcXmlBase::getAttributeValue(node, "input")); + } + + return cResult; +} + +QStringList DcXmlEtlNetwork::getInputVariables(const QString& networkName) const +{ + QStringList qlResult; + + // Create the variable list and return the correct XPath expression. + QList varList; + varList.append(networkName); + + QList inputList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectInputVarNames, varList)); + for (const auto& inputItem : inputList) { + qlResult.append(DcXmlBase::getAttributeValue(inputItem.node(), "name")); + } + + return qlResult; +} + +QStringList DcXmlEtlNetwork::getOutputVariables(const QString& networkName) const +{ + QStringList qlResult; + + // Create the variable list and return the correct XPath expression. + QList varList; + varList.append(networkName); + + QList outputList = DcXmlBase::selectNodes(DcXmlBase::evaluateXPath(selectOutputVarNames, varList)); + for (const auto& outputItem : outputList) { + qlResult.append(DcXmlBase::getAttributeValue(outputItem.node(), "name")); + } + + return qlResult; +} + +QThread::Priority DcXmlEtlNetwork::getPrioByNetworkName(const QString& networkName) const +{ + QThread::Priority priority = QThread::NormalPriority; + + // Create the variable list and create the correct XPath expression. + QList varList; + varList.append(networkName); + + pugi::xpath_node nodeItem = DcXmlBase::selectNode(DcXmlBase::evaluateXPath(selectNetwork, varList)); + if (nodeItem) { + const auto prio = DcXmlBase::getAttributeValue(nodeItem.node(), "priority"); + priority = m_qhPriority.value(prio, QThread::NormalPriority); + } + + return priority; +} + +void DcXmlEtlNetwork::constructXPathHash() +{ + /* Put all XPath expressions in one hash, this way they are all localized + * centrally and not scattered around the code. + */ + // Networks + DcXmlBase::addXPath(selectNetworks, "//network"); + DcXmlBase::addXPath(selectNetwork, "//network[@name='%1']"); + // Objects + DcXmlBase::addXPath(selectObjects, "//network[@name='%1']/objects/object"); + DcXmlBase::addXPath(selectObject, "//network[@name='%1']/objects/object[@id='%2']"); + // Inputs + DcXmlBase::addXPath(selectInputs, "//network[@name='%1']/objects/object[@id='%2' and @class='%3']/inputs"); + DcXmlBase::addXPath(selectInputVarNames, "//network[@name='%1']/objects/object[@class='input']/outputs/output"); + DcXmlBase::addXPath(selectInput, "//network[@name='%1']/objects/object[@id='%2']/inputs/input[@name='%3']"); + // Outputs + DcXmlBase::addXPath(selectOutputs, "//network[@name='%1']/objects/object[@id='%2']/outputs/output"); + DcXmlBase::addXPath(selectOutputVarNames, "//network[@name='%1']/objects/object[@class='output']/inputs/input"); + DcXmlBase::addXPath(selectOutput, "//network[@name='%1']/objects/object[@id='%2']/outputs/output[@name='%3']"); + // Connections + DcXmlBase::addXPath(selectConnections, "//network[@name='%1']/connections/connection"); +} + +void DcXmlEtlNetwork::constructEnumHash() +{ + m_qhPriority.insert(prio_idle, QThread::IdlePriority); + m_qhPriority.insert(prio_lowest, QThread::LowestPriority); + m_qhPriority.insert(prio_low, QThread::LowPriority); + m_qhPriority.insert(prio_normal, QThread::NormalPriority); + m_qhPriority.insert(prio_high, QThread::HighPriority); + m_qhPriority.insert(prio_highest, QThread::HighestPriority); + m_qhPriority.insert(prio_time_critical, QThread::TimeCriticalPriority); + m_qhPriority.insert(prio_inherit, QThread::InheritPriority); +} diff --git b/src/dcxmletlnetwork.h a/src/dcxmletlnetwork.h new file mode 100644 index 0000000..7cfb3ef --- /dev/null +++ a/src/dcxmletlnetwork.h @@ -0,0 +1,67 @@ +#ifndef OSDEV_COMPONENTS_DCXMLETLNETWORK_H +#define OSDEV_COMPONENTS_DCXMLETLNETWORK_H + +#include "connection.h" +#include "connections.h" +#include "connectordata.h" +#include "dcxmlbase.h" +#include "objectdata.h" + +// Qt +#include +#include +#include +#include +#include +#include + +namespace osdev { +namespace components { + +/* + * _________________________________________ + * / An expert is one who knows more and \ + * | more about less and less until he knows | + * \ absolutely everything about nothing. / + * ----------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + *******************************************************/ +/*! + * \brief The class that actually parses the configuration file and provides + * accessors to the internal data. + */ +class DcXmlEtlNetwork : public DcXmlBase +{ +public: + DcXmlEtlNetwork(); + explicit DcXmlEtlNetwork(const QString& fileName); + virtual ~DcXmlEtlNetwork(); + + QStringList getNetworkNames() const; + QList> getObjectsOfNetwork(const QString& networkName) const; + Connections getConnectionsOfNetwork(const QString& networkName) const; + + QStringList getInputVariables(const QString& serviceName) const; + QStringList getOutputVariables(const QString& networkName) const; + + QThread::Priority getPrioByNetworkName(const QString& networkName) const; + +private: + void constructXPathHash(); + void constructEnumHash(); + + QHash m_qhPriority; // Enum from QThread +}; + +} // namespace components +} // namespace osdev + +#endif // OSDEV_COMPONENTS_DCXMLETLNETWORK_H diff --git b/src/dcxmletlservices.cpp a/src/dcxmletlservices.cpp new file mode 100644 index 0000000..b02de36 --- /dev/null +++ a/src/dcxmletlservices.cpp @@ -0,0 +1,110 @@ +#include "dcxmletlservices.h" +#include "log.h" +#include + +using namespace osdev::components; + +static const char* selectServices = "selectServices"; +static const char* selectServiceById = "selectServiceById"; +static const char* selectServiceByName = "selectServiceByName"; + +DcXmlEtlServices::DcXmlEtlServices( const QString &fileName ) +{ + if( !fileName.isEmpty() ) + { + if( !loadConfiguration( fileName ) ) + { + LogInfo( "[DcXmlEtlServices::DcXmlEtlServices]", QString( "There was an error reading file : %1" ).arg( fileName ) ); + } + } + else + { + LogInfo( "[DcXmlEtlServices::DcXmlEtlServices]", QString( "No filename given. No configuration read." ) ); + } + + constructXPathHash(); +} + +bool DcXmlEtlServices::loadConfiguration( const QString &fileName ) +{ + return DcXmlBase::parseFile( fileName ); +} + +QStringList DcXmlEtlServices::getServiceNames() const +{ + QStringList lstResult; + + QList serviceList = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectServices ) ); + for( const auto& nodeItem : serviceList ) + { + lstResult.append( nodeItem.node().attribute( "name" ).value() ); + } + + return lstResult; +} + +QStringList DcXmlEtlServices::getServiceIds() const +{ + QStringList lstResult; + + QList serviceList = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectServices ) ); + for( const auto& nodeItem : serviceList ) + { + lstResult.append( nodeItem.node().attribute( "id" ).value() ); + } + + return lstResult; +} + +QString DcXmlEtlServices::getServiceConfig( const QString &serviceId ) const +{ + QList varList; + varList.append( serviceId ); + + return QString( DcXmlBase::selectNode( evaluateXPath( selectServiceById, varList ) ).node().attribute( "filename" ).value() ); +} + +QHash DcXmlEtlServices::getServiceConfigs() const +{ + QHash qhResult; + + QList serviceList = DcXmlBase::selectNodes( DcXmlBase::getXPath( selectServices ) ); + for( const auto& nodeItem : serviceList ) + { + qhResult.insert( nodeItem.node().attribute( "id" ).value(), nodeItem.node().attribute( "filename" ).value() ); + } + + return qhResult; +} + +void DcXmlEtlServices::constructXPathHash() +{ + DcXmlBase::addXPath( selectServices, "//modelmapping/services/service" ); + DcXmlBase::addXPath( selectServiceById, "//modelmapping/services/service[@id='%1']" ); + DcXmlBase::addXPath( selectServiceByName, "//modelmapping/services/service[@name='%1']" ); +} + +QString DcXmlEtlServices::getServiceConfigByName( const QString &serviceName ) const +{ + return this->getValueByAttribute( selectServiceByName, serviceName, "filename" ); +} + +QString DcXmlEtlServices::getServiceIdByName( const QString &serviceName ) const +{ + return this->getValueByAttribute( selectServiceByName, serviceName, "id" ); +} + +QString DcXmlEtlServices::getServiceConfigById( const QString &serviceId ) const +{ + return this->getValueByAttribute( selectServiceById, serviceId, "filename" ); +} + +QString DcXmlEtlServices::getValueByAttribute( const QString &XPath, + const QString &attrValue, + const QString &returnAttribute ) const +{ + QList varList; + varList.append( attrValue ); + + return QString( DcXmlBase::selectNode( evaluateXPath( XPath.toStdString().c_str(), varList ) ).node().attribute( returnAttribute.toStdString().c_str() ).value() ); +} diff --git b/src/dcxmletlservices.h a/src/dcxmletlservices.h new file mode 100644 index 0000000..a8e980b --- /dev/null +++ a/src/dcxmletlservices.h @@ -0,0 +1,116 @@ +#ifndef OSDEV_COMPONENTS_DCXMLETLSERVICES_H +#define OSDEV_COMPONENTS_DCXMLETLSERVICES_H + +#include "dcxmlbase.h" + +#include +#include +#include +#include + +namespace osdev { +namespace components { +/* + * _________________________________________ + * / River: "Little soul big world. Eat and \ + * | sleep and eat... Many souls. Very | + * | straight, very simple" | + * | | + * | Mal: "Cattle on the ship three weeks, | + * | she don't go near 'em. Suddenly, we're | + * | on Jiangyin and she's got a driving | + * | need to commune with the beasts?" | + * | | + * | River: "They weren't cows inside. They | + * | were waiting to be, but they forgot. | + * | Now they see sky and they remember what | + * | they are." | + * | | + * | Mal: "Is it bad that what she just said | + * | makes perfect sense to me?" | + * | | + * \ --Episode #5, "Safe" / + * ----------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + * + **************************************************************************************/ +/*! \brief This component locates all the services from the ModelMapper configuration. + * Although it can be used seperately, it is intended to be a helper class to + * XMLEtlConfig. + * While XMLEtlConfig is implemented as Singleton, this class isn't. + */ + +class DcXmlEtlServices : public DcXmlBase +{ +public: + DcXmlEtlServices( const QString &fileName = QString() ); + + /// Deleted copy constructor + DcXmlEtlServices( const DcXmlEtlServices& ) = delete; + + /// Deleted assignment constructor + DcXmlEtlServices& operator=( const DcXmlEtlServices& ) = delete; + + /// Deleted move constructor + DcXmlEtlServices( DcXmlEtlServices&& ) = delete; + + /// Deleeted move operator + DcXmlEtlServices& operator=( DcXmlEtlServices&& ) = delete; + + /// @brief Loads the configuration from the given fileName. + /// @param fileName - The configuration file in XML format. + virtual bool loadConfiguration( const QString &fileName ); + + // ========================================================================= + /*! + * \brief Get the list of services mentioned in the modelmapper configuration + * If no services were mentioned, this list will be empty. + */ + QStringList getServiceNames() const; + + /*! + * \brief Get the list of serviceIds mentioned in the modelmapper configuration + * If no ServiceIds were found, this list will be empty. + */ + QStringList getServiceIds() const; + + /*! + * \brief Get the filename of the service-configuration file. + * This file isn't parsed here but passed on to the DcXmlNetwork class. + */ + QString getServiceConfig( const QString &serviceId ) const; + + /*! + * \brief Get the list of configurations by serviceId in a Hash + * If no services are found, this Hash will be empty. + */ + QHash getServiceConfigs() const; + + // Convenient methods. + /*! + * @brief Added for convenience. The name is quite descriptive. + */ + QString getServiceConfigByName( const QString &serviceName ) const; + QString getServiceIdByName( const QString &serviceName ) const; + QString getServiceConfigById( const QString &serviceId ) const; + +private: + void constructXPathHash(); + QString getValueByAttribute( const QString &XPath, + const QString &attrValue, + const QString &returnAttribute ) const; + +}; + +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_DCXMLETLSERVICES_H */ diff --git b/src/dcxmlmodelmapping.cpp a/src/dcxmlmodelmapping.cpp new file mode 100644 index 0000000..52aec82 --- /dev/null +++ a/src/dcxmlmodelmapping.cpp @@ -0,0 +1,142 @@ +#include "dcxmlmodelmapping.h" +#include "dcxmlconfig.h" + +using namespace osdev::components; + +// The only instance of the singleton Modelmapper Config Parser. +std::unique_ptr DcXmlModelMapping::s_instance(nullptr); + +DcXmlModelMapping& DcXmlModelMapping::Instance() +{ + if (nullptr == s_instance) { + s_instance = std::unique_ptr(new DcXmlModelMapping()); + } + + return *s_instance; +} + +DcXmlModelMapping::DcXmlModelMapping() + : m_services(nullptr) + , m_networks() +{ +} + +DcXmlModelMapping::~DcXmlModelMapping() +{ + this->cleanInternals(); +} + +bool DcXmlModelMapping::loadConfiguration(const QString& configDir, const QString& fileName) +{ + // Check if the internal service parser is instantiated. If not, do so. + // The network parsers will be parsed "on the way". + if (nullptr != m_services || !m_networks.isEmpty()) { + // clean up the internal structures.. We obviously want to (re)load the configuration. + cleanInternals(); + } + + // Start the services configuration... + m_services = QSharedPointer(new DcXmlEtlServices(configDir + "/" + fileName)); + for (const QString& serviceName : m_services->getServiceNames()) { + QSharedPointer pNetwork = QSharedPointer(new DcXmlEtlNetwork(configDir + "/" + m_services->getServiceConfigByName(serviceName))); + + if (nullptr != pNetwork) { + m_networks.insert(serviceName, pNetwork); + } + } + + return true; +} + +bool DcXmlModelMapping::loadConfiguration(const QString& fileName) +{ + return loadConfiguration(DCXmlConfig::Instance().getConfigPath(), fileName); +} + +QStringList DcXmlModelMapping::getServices() const +{ + return QStringList(m_networks.keys()); +} + +QString DcXmlModelMapping::getServiceId(const QString& _serviceName) const +{ + return m_services->getServiceIdByName(_serviceName); +} + +QStringList DcXmlModelMapping::getNetworksOfService(const QString& _serviceName) const +{ + return m_networks.value(_serviceName)->getNetworkNames(); +} + +QList> DcXmlModelMapping::getObjectsOfNetwork(const QString& _networkName) const +{ + // Find the network pointer containing the networkName. + QSharedPointer pNetwork = findNetworkByName(_networkName); + if (nullptr != pNetwork) { + return pNetwork->getObjectsOfNetwork(_networkName); + } + return QList>(); +} + +Connections DcXmlModelMapping::getConnectionsOfNetwork(const QString& _networkName) const +{ + // Find the network pointer containing the networkName. + QSharedPointer pNetwork = findNetworkByName(_networkName); + if (nullptr != pNetwork) { + return pNetwork->getConnectionsOfNetwork(_networkName); + } + return Connections(); +} + +QStringList DcXmlModelMapping::getInputVariables(const QString& _networkName) const +{ + // Find the network pointer containing the networkName. + QSharedPointer pNetwork = findNetworkByName(_networkName); + if (nullptr != pNetwork) { + return pNetwork->getInputVariables(_networkName); + } + return QStringList(); +} + +QStringList DcXmlModelMapping::getOutputVariables(const QString& _networkName) const +{ + QSharedPointer pNetwork = findNetworkByName(_networkName); + if (nullptr != pNetwork) { + return pNetwork->getOutputVariables(_networkName); + } + return QStringList(); +} + +QThread::Priority DcXmlModelMapping::getPrioByNetworkName(const QString& _networkName) const +{ + // Find the network pointer containing the networkName. + QSharedPointer pNetwork = findNetworkByName(_networkName); + if (nullptr != pNetwork) { + return pNetwork->getPrioByNetworkName(_networkName); + } + return QThread::NormalPriority; +} + +void DcXmlModelMapping::cleanInternals() +{ + // Retrieve every network object and destroy it... + if (!m_networks.isEmpty()) { + for (const QString& serviceName : QStringList(m_networks.keys())) { + // Clear the reference. If it was the last (only) reference + // the pointer wil be deleted. + m_networks.take(serviceName).clear(); + } + } + + m_services.clear(); +} + +QSharedPointer DcXmlModelMapping::findNetworkByName(const QString& networkName) const +{ + for (QSharedPointer pNetwork : m_networks.values()) { + if (pNetwork->getNetworkNames().contains(networkName)) { + return pNetwork; + } + } + return QSharedPointer(); +} diff --git b/src/dcxmlmodelmapping.h a/src/dcxmlmodelmapping.h new file mode 100644 index 0000000..e984251 --- /dev/null +++ a/src/dcxmlmodelmapping.h @@ -0,0 +1,110 @@ +#ifndef OSDEV_COMPONENTS_DCXMLMODELMAPPING_H +#define OSDEV_COMPONENTS_DCXMLMODELMAPPING_H + +#include "dcxmletlnetwork.h" +#include "dcxmletlservices.h" + +#include +#include +#include +#include +#include + +namespace osdev { +namespace components { + +/*! \brief This component locates any configuration information needed + * to initialize the application and its components. + * Parses this information and make it available to other + * components that need it. + * + * This component is implemented as a singleton. XML has the + * "habit" of expand approx. 15 times the file size so with this + * in mind, it is more future proof to load this only once into memory. + */ +/* + * ________________________________________ + * / History, n.: \ + * | | + * | Papa Hegel he say that all we learn | + * | from history is that we | + * | learn nothing from history. I know | + * | people who can't even learn from | + * | what happened this morning. Hegel must | + * | have been taking the long view. | + * | | + * | -- Chad C. Mulligan, "The Hipcrime | + * \ Vocab" / + * ---------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + */ + +using network_container = QHash>; + +class DcXmlModelMapping +{ +public: + /// @return the one and only instance of the config object. + static DcXmlModelMapping& Instance(); + + // ========================================================= + /// The constructor + DcXmlModelMapping(); + + /// The destructor + virtual ~DcXmlModelMapping(); + + /// Deleted copy-constructor + DcXmlModelMapping(const DcXmlModelMapping&) = delete; + /// Deleted assignment constructor + DcXmlModelMapping& operator=(const DcXmlModelMapping&) = delete; + /// Deleted move-constructor + DcXmlModelMapping(DcXmlModelMapping&&) = delete; + /// Deleted move-operator + DcXmlModelMapping& operator=(DcXmlModelMapping&&) = delete; + + // ========================================================== + /// @brief Loads the configuration from the given filename + /// @param fileName - The configuration file in XML format. + bool loadConfiguration(const QString& configDir, const QString& fileName); + + bool loadConfiguration(const QString& fileName); + + /*! @brief Retrieve a list of services configured in the ModelMapper configuration + * @return A StringList of all serviceNames. + */ + QStringList getServices() const; + QString getServiceId(const QString& _serviceName) const; + + QStringList getNetworksOfService(const QString& _serviceName) const; + QList> getObjectsOfNetwork(const QString& _networkName) const; + Connections getConnectionsOfNetwork(const QString& _networkName) const; + + QStringList getInputVariables(const QString& _networkName) const; + QStringList getOutputVariables(const QString& _networkName) const; + + QThread::Priority getPrioByNetworkName(const QString& _networkName) const; + +private: + void cleanInternals(); ///< Cleanup the internal structures and destroy all objects. + QSharedPointer findNetworkByName(const QString& networkName) const; ///< Find the object that contains a config with this name. + + static std::unique_ptr s_instance; ///< Instantiated ModelMapper Configuration object. + + // Internal pointers...... + QSharedPointer m_services; ///< Instantiated Services Configuration object. + network_container m_networks; ///< Collection of instantiated network configuration objects. +}; + +} // namespace components +} // namespace osdev + +#endif // OSDEV_COMPONENTS_DCXMLMODELMAPPING_H diff --git b/src/jobdata.cpp a/src/jobdata.cpp new file mode 100644 index 0000000..ee2fff6 --- /dev/null +++ a/src/jobdata.cpp @@ -0,0 +1,85 @@ +#include "jobdata.h" + +using namespace osdev::components; + +JobData::JobData() + : m_jobName() + , m_runDate() + , m_runTime() + , m_interval( 0 ) + , m_targetObject() + , m_parameters() +{ +} + +JobData::JobData( const QString& _jobName, const QHash& _paramList, + const QTime& _runTime, const int _interval, + const QString &_targetObject, const QDate& _runDate ) + : m_jobName( _jobName ) + , m_runDate( _runDate ) + , m_runTime( _runTime ) + , m_interval( _interval ) + , m_targetObject( _targetObject ) + , m_parameters( _paramList ) +{ +} + +JobData::JobData(const JobData& source ) + : m_jobName( source.jobName() ) + , m_runDate( source.runDate() ) + , m_runTime( source.runTime() ) + , m_interval( source.runInterval() ) + , m_targetObject( source.targetObject() ) + , m_parameters( source.paramList() ) +{ +} + +JobData& JobData::operator=( const JobData& origin ) +{ + if( &origin != this ) + { + origin.copyImpl( this ); + } + return *this; +} + +void JobData::copyImpl( JobData* dest ) const +{ + dest->m_interval = m_interval; + dest->m_targetObject = m_targetObject; + dest->m_jobName = m_jobName; + dest->m_parameters = m_parameters; + dest->m_runDate = m_runDate; + dest->m_runTime = m_runTime; +} + +JobData::~JobData() +{ +} + +void JobData::addParameter( const QString& _varName, const QVariant &_value ) +{ + m_parameters.insert( _varName, _value ); +} + +void JobData::setParameters( const QHash& _paramList ) +{ + // Clear the old parameter list before assigning the new parameter list. Just to be shure. + m_parameters.clear(); + m_parameters = _paramList; +} + +QVariant JobData::paramValue( const QString& _varName ) const +{ + if( m_parameters.contains( _varName ) ) + { + return m_parameters.value( _varName ); + } + + return QVariant(); +} + +QStringList JobData::varNames() const +{ + return QStringList( m_parameters.keys() ); +} diff --git b/src/jobdata.h a/src/jobdata.h new file mode 100644 index 0000000..bdc8c69 --- /dev/null +++ a/src/jobdata.h @@ -0,0 +1,146 @@ +#ifndef OSDEV_COMPONENTS_JOBDATA_H +#define OSDEV_COMPONENTS_JOBDATA_H + +#include +#include +#include +#include +#include +#include + +namespace osdev { +namespace components { + +/* + * _________________________________________ + * / Despite the best efforts of a quantum \ + * | bigfoot drive (yes I know everyone told | + * | me they suck, now I know they were | + * | right) 2.1.109ac1 is now available | + * | | + * \ -- Alan Cox announcing Linux 2.1.109ac1 / + * ----------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + */ +/*! + * \brief A simple class to hold all info needed to schedule a job. + */ + +class JobData +{ +public: + /// Default constructor + JobData(); + + /*! + * \brief Construct the JobData object and set all the parameters + * \param _jobName - Name of the job (Coming from the config) + * \param _paramList - The parameters as coming from the config + * \param _runTime - Time this job has to run. (Coming from the configuration. + * \param _runDate - (Local)Date this job has to run. + * \param _interval - The interval this job has to be rescheduled + * \param _targetObject - The caelus object this job is intended for. + */ + JobData( const QString& _jobName, const QHash& _paramList, + const QTime& _runTime, const int _interval, const QString& _targetObject, + const QDate& _runDate = QDate::currentDate() ); + + /* Copy Constructor */ + JobData( const JobData& source ); + + /* Assignment operator */ + JobData& operator=( const JobData& origin ); + + /** D'tor */ + virtual ~JobData(); + + /*! + * \return The name of the job used for registration. + * It will be set by "setJobName( const QString& _jobName )" + */ + const QString& jobName() const { return m_jobName; } + void setJobName( const QString& _jobName ) { m_jobName = _jobName; } + + /*! + * \return Get the targetobject this job is intended for. + */ + const QString& targetObject() const { return m_targetObject; } + void setTargetObject( const QString& _targetObject ) { m_targetObject = _targetObject; } + + /*! + * \return The date this job should run. If this returns empty, it should run each and every day. + * It will be set by "setRunDate( const QDate& _runDate )".This is LocalTime.... + */ + const QDate& runDate() const { return m_runDate; } + void setRunDate( const QDate& _runDate ) { m_runDate = _runDate; } + + /*! + * \return The time this job should run. It cannot be empty. + * It will be set by "setRunTime( const QTime& _runTime )" + */ + const QTime& runTime() const { return m_runTime; } + void setRunTime( const QTime& _runTime ) { m_runTime = _runTime; } + + /*! + * \brief The interval the job will be re-scheduled to. + * \return + */ + int runInterval() const { return m_interval; } + void setRunInterval( const int _interval ) { m_interval = _interval; } + + // ======================================================================== + // == Implemented in the cpp file + // == --------------------------------------------------------------------- + /*! + * \brief Adds a parameter to the parameter list this job uses. + */ + void addParameter( const QString& _varName, const QVariant &_value ); + + /*! + * \brief Replace the set of parameters with the new one. The old list will be erased. + */ + void setParameters( const QHash < QString, QVariant > &_paramList ); + + /*! + * \return Get the value, belonging to the variable name from the hash. + * Empty if the parameter name doesn't exist. + */ + QVariant paramValue( const QString& _varName ) const; + + /*! + * \return Get all parameter names from the list. Empty if none exist. + */ + QStringList varNames() const; + + /*! + * \return The entire parameter list + */ + QHash paramList() const { return m_parameters; } + +private: + /*! + * \brief Copies this instance into the specified destination. + * \param[in,out] dest Pointer to the destination to which the properties of this instance are copied. + */ + void copyImpl( JobData* dest ) const; + + QString m_jobName; ///< The name of the job used for registration + QDate m_runDate; ///< The date this job should run. Empty if daily. + QTime m_runTime; ///< The time this job should run. + int m_interval; ///< The interval the jod should be scheduled on. + QString m_targetObject; ///< The plugin or object this job is intended for. Used as input to the pluginmanager. + QHash m_parameters; ///< Parameters used by the job. +}; + +} /* End namespace components */ +} /* End namespace osdev */ + +#endif /* OSDEV_COMPONENTS_JOBDATA_H */ diff --git b/src/objectdata.cpp a/src/objectdata.cpp new file mode 100644 index 0000000..6240793 --- /dev/null +++ a/src/objectdata.cpp @@ -0,0 +1,74 @@ +#include "objectdata.h" + +using namespace osdev::components; + +ObjectData::ObjectData() + : m_objectType() + , m_objectId() + , m_targetName() + , m_targetAction() + , m_keyField() + , m_keyValue() + , m_foreignKeyField() + , m_targetField() + , m_targetResetValue() + , m_useNonPersistentTimestamp(false) + , m_npTimestampBufferSize(10) + , m_qhInputs() + , m_qhOutputs() +{ +} + +void ObjectData::setObjectType(const QString& _objectType) { m_objectType = _objectType; } + +void ObjectData::setObjectId(const QString& _objectId) { m_objectId = _objectId; } + +void ObjectData::setTargetName(const QString& _targetName) { m_targetName = _targetName; } + +void ObjectData::setTargetAction(const QString& _targetAction) { m_targetAction = _targetAction; } + +void ObjectData::setKeyField(const QString& _keyField) { m_keyField = _keyField; } + +void ObjectData::setKeyValue(const QString& _keyValue) { m_keyValue = _keyValue; } + +void ObjectData::setForeignKeyField(const QString& foreignKeyField) { m_foreignKeyField = foreignKeyField; } + +void ObjectData::setTargetField(const QString& targetField) { m_targetField = targetField; } + +void ObjectData::setTargetResetValue(const QString& targetResetValue) { m_targetResetValue = targetResetValue; } + +void ObjectData::setNonPersistentTimestampUsage(bool value) { m_useNonPersistentTimestamp = value; } + +void ObjectData::setNonPersistentTimestampBufferSize(unsigned int value) { m_npTimestampBufferSize = value; } + +const QStringList ObjectData::getInputNames() const { return m_qhInputs.keys(); } + +const QString& ObjectData::getInputId(const QString& _inputName) const { return m_qhInputs.value(_inputName)->id(); } + +const QString& ObjectData::getInputType(const QString& _inputName) const { return m_qhInputs.value(_inputName)->type(); } + +const QString& ObjectData::getInputDefault(const QString& _inputName) const { return m_qhInputs.value(_inputName)->Default(); } + +bool ObjectData::getInputExcludeFromIdentityCheck(const QString& _inputName) const { return m_qhInputs.value(_inputName)->excludeFromIdentityCheck(); } + +const QStringList ObjectData::getOutputNames() const { return m_qhOutputs.keys(); } + +const QString& ObjectData::getOutputType(const QString& _outputName) const { return m_qhOutputs.value(_outputName)->type(); } + +const QString& ObjectData::getOutputDefault(const QString& _outputName) const { return m_qhOutputs.value(_outputName)->Default(); } + +void ObjectData::setInputData(const QString& _name, const QString& _type, const QString& _id, const QString& _default, bool excludeFromIdentityCheck) +{ + // Use multiInsert in order to detect double entries at a later stage. The getters will return the last entry and in that sense the + // behaviour is the same as when overwriting the value in the hash with an insert. The keys method used by getInputNames however will + // return a list that holds a key multiple times if it was inserted multiple times. + m_qhInputs.insertMulti(_name, (QSharedPointer(new ConnectorData(_name, _type, _id, _default, excludeFromIdentityCheck)))); +} + +void ObjectData::setOutputData(const QString& _name, const QString& _type, const QString& _default) +{ + // Use multiInsert in order to detect double entries at a later stage. The getters will return the last entry and in that sense the + // behaviour is the same as when overwriting the value in the hash with an insert. The keys method used by getOutputNames however will + // return a list that holds a key multiple times if it was inserted multiple times. + m_qhOutputs.insertMulti(_name, (QSharedPointer(new ConnectorData(_name, _type, QString{}, _default)))); // no id +} diff --git b/src/objectdata.h a/src/objectdata.h new file mode 100644 index 0000000..a31b188 --- /dev/null +++ a/src/objectdata.h @@ -0,0 +1,250 @@ +#ifndef OSDEV_COMPONENTS_OBJECTDATA_H +#define OSDEV_COMPONENTS_OBJECTDATA_H + +#include +#include + +#include "connectordata.h" + +namespace osdev { +namespace components { + +/* + * _____________________________________ + * < Excellent day to have a rotten day. > + * ------------------------------------- + * \ + * \ + * .--. + * |o_o | + * |:_/ | + * // \ \ + * (| | ) + * /'\_ _/`\ + * \___)=(___/ + * + */ +/*! + * \brief The ObjectData class + */ + +class ObjectData +{ +public: + /*! + * \brief Default constructor; + */ + ObjectData(); + + /*! + * \return The object type. + */ + const QString& getObjectType() const { return m_objectType; } + + /*! + * \return The object id. + */ + const QString& getObjectId() const { return m_objectId; } + + /*! + * \return The target name. + */ + const QString& getTargetName() const { return m_targetName; } + + /*! + * \return The target action. + */ + const QString& getTargetAction() const { return m_targetAction; } + + /*! + * \return The key field. + */ + const QString& getKeyField() const { return m_keyField; } + + /** + * @return The key value. + */ + const QString& getKeyValue() const { return m_keyValue; } + + /** + * @return The foreign key field. + */ + const QString& getForeignKeyField() const { return m_foreignKeyField; } + + /** + * @return The target field. + */ + const QString& getTargetField() const { return m_targetField; } + + /** + * @return The target field reset value. + */ + const QString& getTargetResetValue() const { return m_targetResetValue; } + + /** + * @return Flag value that indicates whether persistent or non persistent timestamps should be used. + */ + bool getNonPersistentTimestampUsage() const { return m_useNonPersistentTimestamp; } + + /** + * @return Size of the buffer that is used for storing the non persistent timestamps. + */ + unsigned int getNonPersistentTimestampBufferSize() const { return m_npTimestampBufferSize; } + + /*! + * \brief Sets the ObjectType. + * \param objectType The type to set. + */ + void setObjectType(const QString& objectType); + + /*! + * \brief setObjectId Sets the object id. + * \param objectId The id to set. + */ + void setObjectId(const QString& objectId); + + /*! + * \brief Sets the TargetName. + * \param targetName The target name to set. + */ + void setTargetName(const QString& targetName); + + /*! + * \brief Sets the TargetAction. + * \param targetAction The action to set. + */ + void setTargetAction(const QString& targetAction); + + /*! + * \brief Sets the KeyField. + * \param keyField The key field to set. + */ + void setKeyField(const QString& keyField); + + /** + * @brief Sets the keyvalue for a match in a DataFilter + * @param keyValue The value to set. + */ + void setKeyValue(const QString& keyValue); + + /*! + * \brief Sets the foreign key field (used for batch/merge update). + * \param foreignKeyField The foreign key field to set. + */ + void setForeignKeyField(const QString& foreignKeyField); + + /** + * @brief Set the field on which a merge/batch update operation operates. + * @param targetField The field to change values on in batch/merge updates. + */ + void setTargetField(const QString& targetField); + + /** + * @brief Set the reset value to which the targetfield is set in the decouple step of the merge update operation. + * @param targetResetValue The reset value to use in a field reset. + */ + void setTargetResetValue(const QString& targetResetValue); + + /** + * @brief Set the flag value that indicates whether persistent or non persistent timestamps should be used. + * @param value The value to set. + */ + void setNonPersistentTimestampUsage(bool value); + + /** + * @brief Set the size (number of timestamps) of the buffer used to store non persistent timestamps. + * @param value The value to set. + */ + void setNonPersistentTimestampBufferSize(unsigned int value); + + /*! + * \return The input names. + */ + const QStringList getInputNames() const; + + /*! + * \brief Get the id of the input element identified by name. + * \param inputName The name that identifies the input element. + * \return input id or a null string when the input does not have an id. + */ + const QString& getInputId(const QString& inputName) const; + + /*! + * \brief Gets the InputType for the specified input name. + * \param inputName The name for which to get the input type. + * \return The InputType for the specified input name. + */ + const QString& getInputType(const QString& inputName) const; + + /*! + * \brief Gets the InputDefault for the specified input name. + * \param inputName The inout name for chich to get the default. + * \return The InputDefault for the specified input name. + */ + const QString& getInputDefault(const QString& inputName) const; + + /*! + * \brief Get the excludeFromIdentityCheck flag for the specified input name. + * \param inputName The name for which to get the excludeFromIdentityCheck flag. + * \return The excludeFromIdentityCheck flag. + */ + bool getInputExcludeFromIdentityCheck(const QString& inputName) const; + + /*! + * \return The output names. + */ + const QStringList getOutputNames() const; + + /*! + * \brief Gets the OutputType for the specified output name. + * \param outputName The output name for which to get the type. + * \return The OutputType for the specified output name. + */ + const QString& getOutputType(const QString& outputName) const; + + /*! + * \brief Gets the OutputDefault for the specified output name. + * \param outputName The name for which to get the default. + * \return The OutputDefault for the specified output name. + */ + const QString& getOutputDefault(const QString& outputName) const; + + /*! + * \brief Sets the InputData. + * \param name The name of the input data. + * \param type The type of the input data. + * \param id The id of the input data. If there is no id a null string must be used. + * \param defaultValue The defaultValue of the input data. + * \param excludeFromIdentityCheck Flag that indicates whether this data should participate in the record identity check. + */ + void setInputData(const QString& name, const QString& type, const QString& id, const QString& defaultValue, bool excludeFromIdentityCheck); + + /*! + * \brief setOutputData + * \param name The name of the output data. + * \param type The type of the output data. + * \param defaultValue The defaultValue of the output data. + */ + void setOutputData(const QString& name, const QString& type, const QString& defaultValue); + +private: + QString m_objectType; ///< Type of Object + QString m_objectId; ///< UUID of the object + QString m_targetName; ///< TargetTable this object represent + QString m_targetAction; ///< What action should taken on the target + QString m_keyField; ///< What TargetField is used for Querying + QString m_keyValue; ///< The value we're querying for. + QString m_foreignKeyField; ///< Contains reference to a foreign table. + QString m_targetField; ///< The field on which batch/merge operations operate. + QString m_targetResetValue; ///< The value to which fields are reset when they are not part of the merge set. + bool m_useNonPersistentTimestamp; ///< Use persistent or non persistent timestamp. Default is false. + unsigned int m_npTimestampBufferSize; ///< Number of timestamps that can be stored (circular buffer). Default is 10. + + QHash> m_qhInputs; ///< Hash of all InputConnectors by Name + QHash> m_qhOutputs; ///< Hash of all OutputConnectors by Name +}; + +} // namespace components +} // namespace osdev + +#endif // OSDEV_COMPONENTS_OBJECTDATA_H