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 {