The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 7a67c75b authored by Micha Müller's avatar Micha Müller
Browse files

Finish Refactoring of SensorGroups:

- Adapt Configurators:
  *Introduce partial template specialization in ConfiguratorTemplate
  *Move common logic to ConfiguratorInterface
  *Update and improve doxygen documentation
  *Adapt all plugin configurators
- Refactor printConfig of SensorGroups
- Minor improvements overall
parent c3da3320
......@@ -32,27 +32,268 @@
#include "globalconfiguration.h"
#include "SensorGroupTemplate.h"
#include "version.h"
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/property_tree/ptree.hpp>
//#define STRCMP(node,str) boost::iequals(node.first,str) //DEPRECATED
#define CFG_VAL boost::property_tree::iptree&
#define ADD BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
#define ATTRIBUTE(name,setter) do { if (boost::iequals(val.first, name)) { s.setter(val.second.data()); } } while(0)
#define SETTING(name) if (boost::iequals(val.first, name))
#define DEFAULT_CACHE_INTERVAL 900000
//TODO doxygen: cross reference both templates?
/**
* @brief Abstract interface defining plugin configurator functionality.
*
* @details Plugin configurator should not implement this interface themselves
* but inherit the ConfiguratorTemplate instead.
*
* @ingroup pusherplugins
*/
class ConfiguratorInterface {
protected:
const char COMMA = ',';
const char OPEN_SQBRKET = '[';
const char CLOSE_SQBRKET = ']';
const char DASH = '-';
public:
virtual ~ConfiguratorInterface() {}
virtual std::string getVersion() = 0;
virtual bool readConfig(std::string cfgPath) = 0;
virtual bool reReadConfig() = 0;
virtual void clearConfig() = 0;
virtual void printConfig(LOG_LEVEL ll) = 0;
virtual void setGlobalSettings(const pluginSettings_t& pluginSettings) = 0;
virtual std::vector<SGroupPtr>& getSensorGroups() = 0;
ConfiguratorInterface() :
_cfgPath(""),
_mqttPrefix(""),
_cacheInterval(DEFAULT_CACHE_INTERVAL) {}
ConfiguratorInterface(const ConfiguratorInterface&) = delete;
virtual ~ConfiguratorInterface() {
_sensorGroupInterfaces.clear();
}
ConfiguratorInterface& operator=(const ConfiguratorInterface&) = delete;
/**
* @brief Read in plugin configuration.
*
* @param cfgPath Path + file name of configuration file to read from.
*/
virtual bool readConfig(std::string cfgPath) = 0;
/**
* @brief Clear internal storage and return plugin in unconfigured state.
*/
virtual void clearConfig() {
_sensorGroupInterfaces.clear();
}
/**
* @brief Clear internal storage and read in the configuration again.
*
* @return True on success, false otherwise
*/
bool reReadConfig() {
clearConfig();
//back to the very beginning
return readConfig(_cfgPath);
}
/**
* @brief Sets internal variables with the ones provided by pluginSettings.
* This method should be called once after constructing a configurator
* to provide him with the global default values.
*
* @param pluginSettings Struct with global default settings for the plugins.
*/
void setGlobalSettings(const pluginSettings_t& pluginSettings) {
_mqttPrefix = pluginSettings.mqttPrefix;
_cacheInterval = pluginSettings.cacheInterval;
derivedSetGlobalSettings(pluginSettings);
}
/**
* @brief Get all sensor groups.
*
* @return Vector containing pointers to all sensor groups of this plugin.
*/
std::vector<SGroupPtr>& getSensorGroups() {
return _sensorGroupInterfaces;
}
/**
* @brief Get current DCDB version.
*
* @return Version number as string.
*/
std::string getVersion() {
return std::string(VERSION);
}
/**
* @brief Print configuration.
*
* @param ll Log severity level to be used from logger.
*/
virtual void printConfig(LOG_LEVEL ll) {
LOG_VAR(ll) << " General: ";
if (_mqttPrefix != "") {
LOG_VAR(ll) << " MQTT-Prefix: " << _mqttPrefix;
} else {
LOG_VAR(ll) << " MQTT-Prefix: DEFAULT";
}
if (_cacheInterval != DEFAULT_CACHE_INTERVAL) {
LOG_VAR(ll) << " Cache interval: " << _cacheInterval << " ms";
} else {
LOG_VAR(ll) << " Cache interval: DEFAULT";
}
}
protected:
LOGGER lg;
///@name Overwrite on demand
///@{
/**
* @brief Set global values specifically for plugin.
*
* @param pluginSettings The struct with global default plugin settings
*/
virtual void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) {}
/**
* @brief Print information about configurable configurator attributes (or
* nothing if no such attributes are required).
*
* @param ll Severity level to log with
*/
virtual void printConfiguratorConfig(LOG_LEVEL ll) {
//Overwrite if necessary
LOG_VAR(ll) << " No other plugin-specific general parameters or entities defined";
}
/**
* @brief Overwrite to read in plugin-specific global values.
*
* @param config A boost property (sub-)tree containing the global values.
*/
virtual void global(CFG_VAL config) {}
///@}
///@name Utility
///@{
/**
* @brief Read in global values.
*
* @details Reads in global interface values and then calls global().
*
* @param config A boost property (sub-)tree containing the global values.
*/
bool readGlobal(CFG_VAL config) {
boost::optional<boost::property_tree::iptree&> globalVals = config.get_child_optional("global");
if (globalVals) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, config.get_child("global")) {
if (boost::iequals(global.first, "mqttprefix")) {
_mqttPrefix = global.second.data();
if (_mqttPrefix[_mqttPrefix.length()-1] != '/') {
_mqttPrefix.append("/");
}
} else if (boost::iequals(global.first, "cacheInterval")) {
_cacheInterval = stoul(global.second.data());
_cacheInterval *= 1000;
}
}
global(config.get_child("global"));
}
return true;
}
/**
* @brief Try to parse the given cpuString as integer numbers.
*
* @details On success, the specified numbers will be inserted into a set,
* which will be returned. On failure, an empty set is returned. A
* set is used to maintain uniqueness and an ascending order among
* the numbers although this is not strictly required.
*
* @param cpuString String which specifies a range and/or set of numbers
* (e.g. "1,2,3-5,7-9,10")
*
* @return A set of integers as specified in the cpuString. If the string
* could not be parsed the set will be empty.
*/
std::set<int> parseCpuString(const std::string& cpuString) {
std::set<int> cpus;
int maxCpu = 512;
std::vector<std::string> subStrings;
std::stringstream ssComma(cpuString);
std::string item;
while (std::getline(ssComma, item, ',')) {
subStrings.push_back(item);
}
for (auto s : subStrings) {
if (s.find('-') != std::string::npos) { //range of values (e.g. 1-5) specified
std::stringstream ssHyphen(s);
std::string min, max;
std::getline(ssHyphen, min, '-');
std::getline(ssHyphen, max);
try {
int minVal = stoi(min);
int maxVal = stoi(max);
for (int i = minVal; i <= maxVal; i++) {
if (i >= 0 && i < maxCpu) {
cpus.insert((int)i);
}
}
} catch (const std::exception& e) {
LOG(debug) << "Could not parse values \"" << min << "-" << max << "\"";
}
} else { //single value
try {
int val = stoi(s);
if (val >= 0 && val < maxCpu) {
cpus.insert((int)val);
}
} catch (const std::exception& e) {
LOG(debug) << "Could not parse value \"" << s << "\"";
}
}
}
if (cpus.empty()) {
LOG(warning) << " CPUs could not be parsed!";
} else {
std::stringstream sstream;
sstream << " CPUS: ";
for (auto i : cpus) {
sstream << i << ", ";
}
std::string msg = sstream.str();
msg.pop_back();
msg.pop_back();
LOG(debug) << msg;
}
return cpus;
}
///@}
std::string _cfgPath; ///< Path + name of config file to read from
std::string _mqttPrefix; ///< Global MQTT prefix
unsigned int _cacheInterval; ///< Time interval in ms all sensors should cache
std::vector<SGroupPtr> _sensorGroupInterfaces; ///< Sensor group storage
LOGGER lg; ///< Personal logging instance
};
//typedef for more readable usage of create()- and destroy()-methods, required for dynamic libraries
......
This diff is collapsed.
......@@ -91,10 +91,16 @@ public:
return *this;
}
//TODO check if getHostName() aliases are still required after refactor
///@name Getters
///@{
const std::string& getEntityName() const { return _name; }
const std::string& getMqttPart() const { return _mqttPart; }
const std::unique_ptr<strand>& getStrand() const { return _strand; }
///@}
///@name Setters
///@{
void setEntityName(const std::string& name) { _name = name; }
void setMqttPart(const std::string& mqttPart) {
_mqttPart = mqttPart;
......@@ -106,7 +112,9 @@ public:
_mqttPart.erase(_mqttPart.size()-1);
}
}
///@}
//TODO refactor naming scheme uniform to SensorGroup
/**
* @brief Initialize this entity.
*
......@@ -125,7 +133,7 @@ public:
* @brief Print complete configuration of this entity.
*
* @details Prints configuration of base class and subsequently calls
* printConfig().
* derived printConfig().
*
* @param ll Log severity level to be used from logger.
*/
......
......@@ -53,63 +53,63 @@
class SensorGroupInterface {
public:
SensorGroupInterface(const std::string& groupName) :
_groupName(groupName),
_mqttPart(""),
_sync(true),
_keepRunning(false),
_minValues(1),
_interval(1000),
_pendingTasks(0),
_timer(nullptr) {}
SensorGroupInterface(const SensorGroupInterface& other) :
_groupName(other._groupName),
_mqttPart(other._mqttPart),
_sync(other._sync),
_keepRunning(other._keepRunning),
_minValues(other._minValues),
_interval(other._interval),
_timer(nullptr) {
_pendingTasks.store(other._pendingTasks.load());
}
virtual ~SensorGroupInterface() {}
SensorGroupInterface& operator=(const SensorGroupInterface& other) {
_groupName = other._groupName;
_mqttPart = other._mqttPart;
_sync = other._sync;
_keepRunning = other._keepRunning;
_minValues = other._minValues;
_interval = other._interval;
_pendingTasks.store(other._pendingTasks.load());
_timer = nullptr;
return *this;
}
///@name Getters
///@{
const std::string& getGroupName() const { return _groupName; }
const std::string& getMqttPart() const { return _mqttPart; }
bool getSync() const { return _sync; }
unsigned getMinValues() const { return _minValues; }
unsigned getInterval() const { return _interval; }
///@}
SensorGroupInterface(const std::string& groupName) :
_groupName(groupName),
_mqttPart(""),
_sync(true),
_keepRunning(false),
_minValues(1),
_interval(1000),
_pendingTasks(0),
_timer(nullptr) {
}
///@name Setters
///@{
void setGroupName(const std::string& groupName) { _groupName = groupName; }
void setMqttPart(const std::string& mqttPart) { _mqttPart = mqttPart; }
void setSync(bool sync) { _sync = sync; }
void setMinValues(unsigned minValues) { _minValues = minValues; }
void setInterval(unsigned interval) { _interval = interval; }
///@}
SensorGroupInterface(const SensorGroupInterface& other) :
_groupName(other._groupName),
_mqttPart(other._mqttPart),
_sync(other._sync),
_keepRunning(other._keepRunning),
_minValues(other._minValues),
_interval(other._interval),
_timer(nullptr) {
_pendingTasks.store(other._pendingTasks.load());
}
virtual ~SensorGroupInterface() {}
SensorGroupInterface& operator=(const SensorGroupInterface& other) {
_groupName = other._groupName;
_mqttPart = other._mqttPart;
_sync = other._sync;
_keepRunning = other._keepRunning;
_minValues = other._minValues;
_interval = other._interval;
_pendingTasks.store(other._pendingTasks.load());
_timer = nullptr;
return *this;
}
///@name Getters
///@{
const std::string& getGroupName() const { return _groupName; }
const std::string& getMqttPart() const { return _mqttPart; }
bool getSync() const { return _sync; }
unsigned getMinValues() const { return _minValues; }
unsigned getInterval() const { return _interval; }
///@}
///@name Setters
///@{
void setGroupName(const std::string& groupName) { _groupName = groupName; }
void setMqttPart(const std::string& mqttPart) { _mqttPart = mqttPart; }
void setSync(bool sync) { _sync = sync; }
void setMinValues(unsigned minValues) { _minValues = minValues; }
void setInterval(unsigned interval) { _interval = interval; }
///@}
///@name Interface methods
///@{
///@name Interface methods
///@{
/**
* @brief Initialize the sensor group.
*
......@@ -122,29 +122,29 @@ public:
_timer.reset(new boost::asio::deadline_timer(io, boost::posix_time::seconds(0)));
}
/**
* @brief Start the sensor group (i.e. start collecting data).
*/
virtual void start() = 0;
/**
* @brief Start the sensor group (i.e. start collecting data).
*/
virtual void start() = 0;
/**
/**
* @brief Stop the sensor group (i.e. stop collecting data).
*/
virtual void stop() = 0;
/**
* @brief Add a sensor to this group.
*
* @param s Shared pointer to the sensor.
*/
virtual void pushBackSensor(SBasePtr s) = 0;
/**
* @brief Retrieve all sensors of this group.
*
* @return A std::vector with shared pointers to all associated sensors.
*/
virtual std::vector<SBasePtr>& getSensors() = 0;
virtual void stop() = 0;
/**
* @brief Add a sensor to this group.
*
* @param s Shared pointer to the sensor.
*/
virtual void pushBackSensor(SBasePtr s) = 0;
/**
* @brief Retrieve all sensors of this group.
*
* @return A std::vector with shared pointers to all associated sensors.
*/
virtual std::vector<SBasePtr>& getSensors() = 0;
/**
* @brief Print interface configuration.
......@@ -175,14 +175,26 @@ protected:
/**
* @brief Read data for all sensors once.
*/
virtual void read() = 0;
/**
* @brief Asynchronous callback if _timer expires.
*
* @details Issues a read() and sets the timer again if _keepRunning is true.
*/
virtual void readAsync() = 0;
virtual void read() = 0;
/**
* @brief Asynchronous callback if _timer expires.
*
* @details Issues a read() and sets the timer again if _keepRunning is true.
*/
virtual void readAsync() = 0;
/**
* @brief Print information about plugin specific group attributes (or
* nothing if no such attributes are present).
*
* @details Only overwrite if necessary.
*
* @param ll Severity level to log with
*/
virtual void printGroupConfig(LOG_LEVEL ll) {
LOG_VAR(ll) << " No other plugin-specific group attributes defined";
}
///@}
///@name Utility methods
......@@ -235,15 +247,15 @@ protected:
}
///@}
std::string _groupName; ///< String name of this group
std::string _mqttPart; ///< MQTT part identifying this group
bool _sync; ///< Should the timer (i.e. the read cycle of this groups) be synchronized with other groups?
bool _keepRunning; ///< Continue with next reading cycle (i.e. set timer again after reading)?
unsigned int _minValues; ///< Minimum number of values a sensor should gather before they get pushed (to reduce MQTT overhead)
unsigned int _interval; ///< Reading interval cycle in milliseconds
std::atomic_uint _pendingTasks; ///< Number of currently outstanding read operations
std::unique_ptr<boost::asio::deadline_timer> _timer; ///< Time readings in a periodic interval
LOGGER lg; ///< Personal logging instance
std::string _groupName; ///< String name of this group
std::string _mqttPart; ///< MQTT part identifying this group
bool _sync; ///< Should the timer (i.e. the read cycle of this groups) be synchronized with other groups?
bool _keepRunning; ///< Continue with next reading cycle (i.e. set timer again after reading)?
unsigned int _minValues; ///< Minimum number of values a sensor should gather before they get pushed (to reduce MQTT overhead)
unsigned int _interval; ///< Reading interval cycle in milliseconds
std::atomic_uint _pendingTasks; ///< Number of currently outstanding read operations
std::unique_ptr<boost::asio::deadline_timer> _timer; ///< Time readings in a periodic interval
LOGGER lg; ///< Personal logging instance
};
//for better readability
......
......@@ -201,8 +201,6 @@ public:
*/
virtual std::vector<SBasePtr>& getSensors() final override { return _baseSensors; }
//TODO only call this printMethod!
//TODO refactor: use printGroupConfig in derived classes
/**
* @brief Print SensorGroup configuration.
*
......@@ -213,12 +211,12 @@ public:
*
* @param ll Log severity level to be used from logger.
*/
virtual void printConfig(LOG_LEVEL ll) override {
virtual void printConfig(LOG_LEVEL ll) final override {
//print common base attributes
SensorGroupInterface::printConfig(ll);
//print plugin specific group attributes
this->printConfig(ll);
this->printGroupConfig(ll);
if (_entity) {
LOG_VAR(ll) << " Entity " << _entity->getEntityName();
......@@ -293,6 +291,12 @@ protected:
E* _entity; ///< Entity this group is associated to
};
////////////////////////////////////////////////////////////////////////////////
// Partial template specialization for E = nullptr_t
////////////////////////////////////////////////////////////////////////////////
//TODO cross reference general template in doxygen docs
/**
* @brief Interface partial template specialization for sensor group
......@@ -441,7 +445,6 @@ public:
*/
virtual std::vector<SBasePtr>& getSensors() final override { return _baseSensors; }
//TODO only call this printMethod!
/**
* @brief Print SensorGroup configuration.
*
......@@ -452,12 +455,12 @@ public:
*
* @param ll Log severity level to be used from logger.
*/
virtual void printConfig(LOG_LEVEL ll) override {
virtual void printConfig(LOG_LEVEL ll) final override {
//print common base attributes
SensorGroupInterface::printConfig(ll);
//print plugin specific group attributes
this->printConfig(ll);
this->printGroupConfig(ll);
//print associated sensors
LOG_VAR(ll) << " Sensors:";
......
......@@ -51,7 +51,7 @@ void BACnetConfigurator::sensorGroup(BACnetSensorGroup& s, CFG_VAL config) {
ADD {