Commit 30f00bf6 authored by Micha Mueller's avatar Micha Mueller
Browse files

Adapt PDU plugin to sensorgroupsV2 architecture

parent f0b8f7bf
......@@ -2,46 +2,47 @@ global {
mqttprefix /00112233445566778899AABBCCDD
}
sensorTemplates {
sensor def1 {
interval 1000
minValues 3
}
template_group def1 {
interval 1000
minValues 3
}
pdus {
pdu rack1 {
host testHorst:443
TTL 500
request "content-length: 167\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?><clustsafeRequest><authorization><username>admin</username><password>admin</password></authorization><energy/></clustsafeRequest>"
pdu rack1 {
host testHorst:443
TTL 500
request "content-length: 167\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?><clustsafeRequest><authorization><username>admin</username><password>admin</password></authorization><energy/></clustsafeRequest>"
sensors {
sensor total {
path "clustsafeResponse.energy.total"
mqttsuffix 0000
}
sensor pcs1 {
path "clustsafeResponse.energy.clustsafe(id=1).outlets.outlet(id=1)"
interval 2000
mqttsuffix 0001
}
sensor pcs2 {
default def1
path "clustsafeResponse.energy.clustsafe(id=2).outlets.outlet(id=3)"
mqttsuffix 0002
}
group pcs {
default def1
mqttpart 00
sensor pcs1 {
path "clustsafeResponse.energy.clustsafe(id=1).outlets.outlet(id=1)"
mqttsuffix 01
}
sensor pcs2 {
path "clustsafeResponse.energy.clustsafe(id=2).outlets.outlet(id=3)"
mqttsuffix 02
}
sensor pcs3 {
default def1
path "clustsafeResponse.energy.clustsafe(id=2).outlets.outlet(id=4)"
mqttsuffix 0003
}
sensor pcs3 {
path "clustsafeResponse.energy.clustsafe(id=2).outlets.outlet(id=4)"
mqttsuffix 03
}
}
pdu rack2 {
; ....
group total {
interval 2000
mqttpart 00
sensor total {
path "clustsafeResponse.energy.total"
mqttsuffix 00
}
}
}
;pdu rack2 {
; ....
;}
......@@ -22,7 +22,7 @@
#define CFG_VAL boost::property_tree::iptree&
#define ADD BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config)
#define ATTRIBUTE(name,setter) if (boost::iequals(val.first, name)) { s.setter(val.second.data()); }
#define ATTRIBUTE(name,setter) do { if (boost::iequals(val.first, name)) { s.setter(val.second.data()); } } while(0)
/**
* Non-virtual interface template for the configurators.
......@@ -353,9 +353,11 @@ protected:
}
}
for(auto g : _sensorGroups) {
if(isEntityOfGroup(sEntity, *g)) {
finalizeGroup(*g);
if(!isTemplate) {
for(auto g : _sensorGroups) {
if(isEntityOfGroup(sEntity, *g)) {
finalizeGroup(*g);
}
}
}
return true;
......
......@@ -10,158 +10,43 @@
#include <iostream>
#include <sstream>
using namespace std;
PDUConfigurator::PDUConfigurator() {}
PDUConfigurator::PDUConfigurator() {
_entityName = "pdu";
_groupName = "group";
_baseName = "sensor";
}
PDUConfigurator::~PDUConfigurator() {}
bool PDUConfigurator::derivedReadConfig(boost::property_tree::iptree& cfg) {
//read one pdu at a time
BOOST_FOREACH(boost::property_tree::iptree::value_type &pdu, cfg.get_child("pdus")) {
if (STRCMP(pdu, "pdu")) {
LOG(debug) << "PDU \"" << pdu.second.data() << "\"";
if (!pdu.second.empty()) {
//create PDU at end of list
_pdus.push_back(PDUUnit());
PDUUnit& pduUnit = _pdus.back();
//read PDU-values
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, pdu.second) {
if (STRCMP(val, "TTL")) {
pduUnit.setTTL(stoull(val.second.data()));
LOG(debug) << " TTL " << pduUnit.getTTL();
} else if (STRCMP(val, "host")) {
std::string host = val.second.data();
size_t pos = host.find(':');
if (pos != string::npos) {
pduUnit.setHost(host);
} else {
pduUnit.setHost(host + ":443");
}
LOG(debug) << " Host " << pduUnit.getHost();
} else if (STRCMP(val, "request")) {
pduUnit.setRequest(val.second.data());
LOG(debug) << " Request: \n" << pduUnit.getRequest();
} else if (STRCMP(val, "sensors")) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &sensor, val.second) {
LOG(debug) << "Sensor \"" << sensor.second.data() << "\"";
if (!sensor.second.empty()) {
std::string name = pdu.second.data() + "_" + sensor.second.data();
PDUSingleSensor* pduSensor = new PDUSingleSensor(name);
//first check if default sensor is given
boost::optional<boost::property_tree::iptree&> defaultC = sensor.second.get_child_optional("default");
if(defaultC) {
LOG(debug) << " Using \"" << defaultC.get().data() << "\" as default.";
sensorMap_t::iterator it = _templateSensors.find(defaultC.get().data());
if(it != _templateSensors.end()) {
*pduSensor = it->second;
pduSensor->setName(name);
} else {
LOG(warning) << " Template sensor \"" << defaultC.get().data() << "\" not found! Using standard values.";
}
}
//read remaining values
if(readSensorBase(*pduSensor, sensor.second)) {
//set pointer to corresponding pdu
pduSensor->setPdu(&pduUnit);
_sensors.push_back(pduSensor);
} else {
LOG(warning) << " Sensor \"" << sensor.second.data() << "\" has bad values! Ignoring...";
}
}
}
}
}
}
}
void PDUConfigurator::sensorBase(PDUSensorBase& s, CFG_VAL config) {
ADD {
ATTRIBUTE("path", setXMLPath);
}
return true;
}
bool PDUConfigurator::derivedReadSensorBase(PDUSensorBase& sensor, boost::property_tree::iptree& config) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
if (STRCMP(val, "mqttsuffix")) {
sensor.setMqtt(_mqttPrefix + val.second.data());
} else if (STRCMP(val, "path")) {
parsePathString(sensor, val.second.data());
}
void PDUConfigurator::sensorGroup(PDUSensorGroup& s, CFG_VAL config) {
ADD {
//no group attributes currently
}
LOG(debug) << " MQTT : " << sensor.getMqtt();
return true;
}
void PDUConfigurator::parsePathString(PDUSensorBase& sensor, const std::string& pathString) {
LOG(debug) << " Using " << pathString << " as XML search path";
std::vector<std::string> subStrings;
std::stringstream pathStream(pathString);
std::string item;
//split into parts if a attribute (indicated by '(' ')') was defined
while (std::getline(pathStream, item, ')')) {
subStrings.push_back(item);
void PDUConfigurator::sensorEntity(PDUUnit& s, CFG_VAL config) {
ADD {
ATTRIBUTE("TTL", setTTL);
ATTRIBUTE("host", setHost);
ATTRIBUTE("request", setRequest);
//TODO add support for mqttPart if required
}
}
for (auto subStr : subStrings) {
//extract the attributes from the path-parts
if (subStr.find('(') != std::string::npos) { //attribute specified
std::stringstream pathWithAttributesSStream(subStr);
//split into path and attributes string
std::string subPath, attributeString;
std::getline(pathWithAttributesSStream, subPath, '(');
std::getline(pathWithAttributesSStream, attributeString);
if (subPath.front() == '.') {
subPath.erase(0, 1);
}
//now further split the attributes string as multiple attributes could be defined
std::vector<std::string> attributes;
std::stringstream attributeStream(attributeString);
while (std::getline(attributeStream, item, ',')) {
attributes.push_back(item);
}
attributesVector_t attrs;
for (auto att : attributes) {
//part attributes into name and value
if (att.find('=') != std::string::npos) {
std::stringstream attStream(att);
std::string attName, attVal;
std::getline(attStream, attName, '=');
std::getline(attStream, attVal);
bool PDUConfigurator::isEntityOfGroup(PDUUnit& e, PDUSensorGroup& g) {
return (g.getPdu() == &e);
}
attrs.push_back(std::make_pair(attName, attVal));
LOG(debug) << " Attribute: " << attName << "=" << attVal;
} else { //should not happen. If it does the path was malformed
LOG(error) << " Could not parse XML-path!";
return;
}
}
//split of the last child in the path. Required to iterate over multiple nodes which only differ in the attributes with BOOST_FOREACH
auto index = subPath.find_last_of('.');
if (index != std::string::npos) {
std::string subPathChild(subPath.substr(++index));
subPath.erase(--index);
sensor.pushBackXMLPathPart(std::make_tuple(subPath, subPathChild, attrs));
LOG(debug) << " Subpath: " << subPath << " ; Child: " << subPathChild;
} else {//the path contained only one node
sensor.pushBackXMLPathPart(std::make_tuple("", subPath, attrs));
LOG(debug) << " Child: " << subPath;
}
} else { //no attributes specified. Last (sub)path
if (subStr.front() == '.') {
subStr.erase(0, 1);
}
sensor.pushBackXMLPathPart(std::make_tuple(subStr, "", attributesVector_t()));
LOG(debug) << " (Sub)path: " << subStr;
break;
}
}
void PDUConfigurator::setEntityForGroup(PDUUnit& e, PDUSensorGroup& g) {
g.setPdu(&e);
}
void PDUConfigurator::finalizeGroup(PDUSensorGroup& g) {
//nothing to finalize
}
......@@ -8,26 +8,26 @@
#ifndef SRC_SENSORS_PDU_PDUCONFIGURATOR_H_
#define SRC_SENSORS_PDU_PDUCONFIGURATOR_H_
#include "PDUUnit.h"
#include <list>
#include "../../includes/ConfiguratorTemplate.h"
#include "PDUSingleSensor.h"
class PDUConfigurator: public ConfiguratorTemplate<PDUSensorBase, PDUSingleSensor> {
#include "PDUUnit.h"
#include "PDUSensorGroup.h"
typedef std::list<PDUUnit> pduList_t;
class PDUConfigurator: public ConfiguratorTemplate<PDUSensorBase, PDUSensorGroup, PDUUnit> {
public:
PDUConfigurator();
virtual ~PDUConfigurator();
protected:
bool derivedReadConfig(boost::property_tree::iptree& cfg) override;
void derivedReReadConfig() override { _pdus.clear(); }
void derivedSetGlobalSettings(const pluginSettings_t& pluginSettings) override { /*nothing to overwrite*/ }
bool derivedReadSensorBase(PDUSensorBase& sensor, boost::property_tree::iptree& config) override;
/* Overwritten from ConfiguratorTemplate */
void sensorBase(PDUSensorBase& s, CFG_VAL config) override;
void sensorGroup(PDUSensorGroup& s, CFG_VAL config) override;
void sensorEntity(PDUUnit& s, CFG_VAL config) override;
bool isEntityOfGroup(PDUUnit& e, PDUSensorGroup& g) override;
void setEntityForGroup(PDUUnit& e, PDUSensorGroup& g) override;
void finalizeGroup(PDUSensorGroup& g) override;
private:
/**
......@@ -37,8 +37,6 @@ private:
* @param pathString Complete unrefined string as read from the config file
*/
void parsePathString(PDUSensorBase& sensor, const std::string& pathString);
pduList_t _pdus;
};
extern "C" ConfiguratorInterface* create() {
......
......@@ -15,24 +15,78 @@
class PDUSensorBase : virtual public SensorBase {
public:
PDUSensorBase(const std::string& name) :
SensorBase(name) {
_pdu = NULL;
}
SensorBase(name) {}
virtual ~PDUSensorBase() {}
void setPdu(PDUUnit* pdu) { _pdu = pdu; }
const xmlPathVector_t& getXMLPath() const { return _xmlPath; }
void setXMLPath(const std::string& path) {
std::vector<std::string> subStrings;
std::stringstream pathStream(path);
std::string item;
//split into parts if a attribute (indicated by '(' ')' ) was defined
while (std::getline(pathStream, item, ')')) {
subStrings.push_back(item);
}
for (auto subStr : subStrings) {
//extract the attributes from the path-parts
if (subStr.find('(') != std::string::npos) { //attribute specified
std::stringstream pathWithAttributesSStream(subStr);
//split into path and attributes string
std::string subPath, attributeString;
std::getline(pathWithAttributesSStream, subPath, '(');
std::getline(pathWithAttributesSStream, attributeString);
PDUUnit* getPdu() const { return _pdu; }
if (subPath.front() == '.') {
subPath.erase(0, 1);
}
void pushBackXMLPathPart(std::tuple<std::string, std::string, attributesVector_t> pathPart) {
_xmlPath.push_back(pathPart);
//now further split the attributes string as multiple attributes could be defined
std::vector<std::string> attributes;
std::stringstream attributeStream(attributeString);
while (std::getline(attributeStream, item, ',')) {
attributes.push_back(item);
}
attributesVector_t attrs;
for (auto att : attributes) {
//part attributes into name and value
if (att.find('=') != std::string::npos) {
std::stringstream attStream(att);
std::string attName, attVal;
std::getline(attStream, attName, '=');
std::getline(attStream, attVal);
attrs.push_back(std::make_pair(attName, attVal));
} else { //should not happen. If it does the path was malformed
//LOG(error) << " Could not parse XML-path!";
return;
}
}
//split of the last child in the path. Required to iterate over multiple nodes which only differ in the attributes with BOOST_FOREACH
auto index = subPath.find_last_of('.');
if (index != std::string::npos) {
std::string subPathChild(subPath.substr(++index));
subPath.erase(--index);
_xmlPath.push_back(std::make_tuple(subPath, subPathChild, attrs));
} else {//the path contained only one node
_xmlPath.push_back(std::make_tuple("", subPath, attrs));
}
} else { //no attributes specified. Last (sub)path
if (subStr.front() == '.') {
subStr.erase(0, 1);
}
_xmlPath.push_back(std::make_tuple(subStr, "", attributesVector_t()));
break;
}
}
}
protected:
xmlPathVector_t _xmlPath;
PDUUnit* _pdu;
};
#endif /* PDU_PDUSENSORBASE_H_ */
/*
* PDUSingleSensor.cpp
* PDUSensorGroup.cpp
*
* Created on: 24.02.2018
* Author: Micha Mueller
*/
#include "PDUSingleSensor.h"
#include "timestamp.h"
#include <string>
#include "PDUSensorGroup.h"
PDUSingleSensor::PDUSingleSensor(const std::string& name) :
SensorBase(name), PDUSensorBase(name), SingleSensor(name) {}
PDUSensorGroup::PDUSensorGroup(const std::string& name) :
SensorGroupTemplate(name) {
_pdu = nullptr;
}
PDUSingleSensor::~PDUSingleSensor() {}
PDUSensorGroup::~PDUSensorGroup() {}
void PDUSingleSensor::init(boost::asio::io_service& io) {
void PDUSensorGroup::init(boost::asio::io_service& io) {
SensorGroupTemplate::init(io);
if(_pdu) {
_pdu->initializeStrand(io);
} else {
LOG(error) << "No PDUUnit set for sensor " << _name << "! Cannot initialize sensor.";
LOG(error) << "No PDUUnit set for sensorgroup " << _groupName << "! Cannot initialize sensor.";
}
SingleSensor::init(io);
}
void PDUSingleSensor::start() {
void PDUSensorGroup::start() {
if (_keepRunning) {
//we have been started already
LOG(info) << "Sensor " << _name << " already running.";
LOG(info) << "Sensorgroup " << _groupName << " already running.";
return;
}
if (_pdu) {
_keepRunning = 1;
_pendingTasks++;
_timer->async_wait(_pdu->getStrand()->wrap(std::bind(&PDUSingleSensor::readAsync, this)));
LOG(info) << "Sensor " << _name << " started.";
_timer->async_wait(_pdu->getStrand()->wrap(std::bind(&PDUSensorGroup::readAsync, this)));
LOG(info) << "Sensorgroup " << _groupName << " started.";
} else {
LOG(error) << "No PDUUnit set for sensor " << _name << "! Cannot start polling.";
LOG(error) << "No PDUUnit set for sensorgroup " << _groupName << "! Cannot start polling.";
}
}
void PDUSingleSensor::stop() {
void PDUSensorGroup::stop() {
_keepRunning = 0;
//cancel any outstanding readAsync()
_timer->cancel();
LOG(info) << "Sensor " << _name << " stopped.";
LOG(info) << "Sensorgroup " << _groupName << " stopped.";
}
void PDUSingleSensor::read() {
void PDUSensorGroup::read() {
reading_t reading;
reading.timestamp = getTimestamp();
try {
reading.value = _pdu->readValue(_xmlPath);
storeReading(reading, _cacheIndex);
_cacheIndex = (_cacheIndex + 1) % _cacheSize;
for(auto s : _sensors) {
try {
reading.value = _pdu->readValue(s->getXMLPath());
} catch (const std::exception& e) {
LOG(error) << _groupName << "::" << s->getName() << " could not read value: " << e.what();
//dummy value
reading.value = 0;
}
#ifdef DEBUG
LOG(debug) << _pdu->getHost() << "::" << _name << ": \"" << reading.value << "\"";
LOG(debug) << _groupName << "::" << s->getName() << ": \"" << reading.value << "\"";
#endif
} catch (const std::exception& e) {
LOG(error) << _pdu->getHost() << "::" << _name << " could not read value: " << e.what();
//to keep the _cacheIndex uniform for all sensors store value in every case
s->storeReading(reading, _cacheIndex);
}
_cacheIndex = (_cacheIndex + 1) % _cacheSize;
}
void PDUSingleSensor::readAsync() {
void PDUSensorGroup::readAsync() {
uint64_t now = getTimestamp();
read();
if (_timer && _keepRunning) {
uint64_t next = now + MS_TO_NS(_interval);
_timer->expires_at(timestamp2ptime(next));
_pendingTasks++;
_timer->async_wait(_pdu->getStrand()->wrap(std::bind(&PDUSingleSensor::readAsync, this)));
_timer->async_wait(_pdu->getStrand()->wrap(std::bind(&PDUSensorGroup::readAsync, this)));
}
_pendingTasks--;
}
/*
* PDUSingleSensor.h
* PDUSensorGroup.h
*
* Created on: 24.02.2018
* Author: Micha Mueller
*/
#ifndef SRC_SENSORS_PDU_PDUSINGLESENSOR_H_
#define SRC_SENSORS_PDU_PDUSINGLESENSOR_H_
#ifndef SRC_SENSORS_PDU_PDUSENSORGROUP_H_
#define SRC_SENSORS_PDU_PDUSENSORGROUP_H_
#include "PDUSensorBase.h"
#include "../../includes/SingleSensor.h"
#include "../../includes/SensorGroupTemplate.h"
class PDUSingleSensor: public PDUSensorBase, public SingleSensor {
class PDUSensorGroup: public SensorGroupTemplate<PDUSensorBase> {
public:
PDUSingleSensor(const std::string& name);
virtual ~PDUSingleSensor();
PDUSensorGroup(const std::string& name);
virtual ~PDUSensorGroup();
void init(boost::asio::io_service& io) override;
void start() override;
void stop() override;
void setPdu(PDUUnit* pdu) { _pdu = pdu; }
PDUUnit* const getPdu() const { return _pdu; }
private:
void read() override;
void readAsync() override;
PDUUnit* _pdu;
};
#endif /* SRC_SENSORS_PDU_PDUSINGLESENSOR_H_ */
#endif /* SRC_SENSORS_PDU_PDUSENSORGROUP_H_ */
......@@ -22,7 +22,7 @@ PDUUnit::PDUUnit() {
_ttl = 1000;
_lastRefresh = 0;
_host = "";
_strand = NULL;
_strand = nullptr;
}
PDUUnit::~PDUUnit() {
......
......@@ -28,35 +28,22 @@ public:
PDUUnit();
virtual ~PDUUnit();
void setTTL(unsigned ttl) {
_ttl = ttl;
}
unsigned getTTL() const {
return _ttl;