Commit 4e284396 authored by Micha Mueller's avatar Micha Mueller
Browse files

PDU plugin: moved request attribute from pdu to group; Remove TTL attribute +...

PDU plugin: moved request attribute from pdu to group; Remove TTL attribute + necessary internal changes
parent 5843b41f
......@@ -9,12 +9,11 @@ template_group def1 {
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>"
group pcs {
default def1
mqttpart 00
request "content-length: 167\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?><clustsafeRequest><authorization><username>admin</username><password>admin</password></authorization><energy/></clustsafeRequest>"
sensor pcs1 {
path "clustsafeResponse.energy.clustsafe(id=1).outlets.outlet(id=1)"
......@@ -35,6 +34,7 @@ pdu rack1 {
group total {
interval 2000
mqttpart 00
request "content-length: 167\n\n<?xml version=\"1.0\" encoding=\"UTF-8\"?><clustsafeRequest><authorization><username>admin</username><password>admin</password></authorization><energy/></clustsafeRequest>"
sensor total {
path "clustsafeResponse.energy.total"
......
......@@ -26,15 +26,13 @@ void PDUConfigurator::sensorBase(PDUSensorBase& s, CFG_VAL config) {
void PDUConfigurator::sensorGroup(PDUSensorGroup& s, CFG_VAL config) {
ADD {
//no group attributes currently
ATTRIBUTE("request", setRequest);
}
}
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
}
}
......
......@@ -5,8 +5,13 @@
* Author: Micha Mueller
*/
#include "timestamp.h"
#include "PDUSensorGroup.h"
#include "timestamp.h"
#include <sstream>
#include <boost/foreach.hpp>
#include <boost/property_tree/xml_parser.hpp>
PDUSensorGroup::PDUSensorGroup(const std::string& name) :
SensorGroupTemplate(name) {
......@@ -49,12 +54,69 @@ void PDUSensorGroup::stop() {
}
void PDUSensorGroup::read() {
//send request
std::string response;
if (!_pdu->sendRequest(_request, response)) {
LOG(error) << _groupName << " could not send request!";
return;
}
//parse response
boost::property_tree::ptree ptree;
std::string xml = response.substr(response.find("<"));
std::istringstream treeStream(xml);
try {
boost::property_tree::read_xml(treeStream, ptree);
} catch (const std::exception& e) {
LOG(error) << _groupName << " got malformed XML response";
return;
}
//read values for every sensor from response
reading_t reading;
reading.timestamp = getTimestamp();
for(auto s : _sensors) {
try {
reading.value = _pdu->readValue(s->getXMLPath());
std::string readStr;
const xmlPathVector_t& xmlPath = s->getXMLPath();
boost::property_tree::ptree node = ptree;
for (size_t i = 0; i < xmlPath.size(); i++) {
const std::string& path = std::get<0>(xmlPath[i]);
const std::string& child = std::get<1>(xmlPath[i]);
const attributesVector_t& attVec = std::get<2>(xmlPath[i]);
unsigned matchCount;
if (child != "") {
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, node.get_child(path)) {
if (v.first == child) {
matchCount = 0;
for (size_t j = 0; j < attVec.size(); j++) {
std::string attributeVal = v.second.get_child("<xmlattr>." + attVec[j].first).data();
if (attributeVal != attVec[j].second) { //attribute values don't match
break;
} else {
matchCount++;
}
}
if (matchCount == attVec.size()) { //all attributes matched
readStr = v.second.data();
node = v.second;
break;
}
}
}
} else { //child == ""
readStr = node.get(path, "");
break; //last (part of the) path
}
}
if (readStr == "") {
throw std::runtime_error("Value not found!");
}
reading.value = stoul(readStr);
} catch (const std::exception& e) {
LOG(error) << _groupName << "::" << s->getName() << " could not read value: " << e.what();
//dummy value
......
......@@ -21,13 +21,17 @@ public:
void start() override;
void stop() override;
void setPdu(PDUUnit* pdu) { _pdu = pdu; }
PDUUnit* const getPdu() const { return _pdu; }
void setRequest(const std::string& request) { _request = request; }
void setPdu(PDUUnit* pdu) { _pdu = pdu; }
const std::string& getRequest() { return _request; }
PDUUnit* const getPdu() const { return _pdu; }
private:
void read() override;
void readAsync() override;
std::string _request;
PDUUnit* _pdu;
};
......
......@@ -19,8 +19,6 @@
#include <openssl/bio.h>
PDUUnit::PDUUnit() {
_ttl = 1000;
_lastRefresh = 0;
_host = "";
_strand = nullptr;
}
......@@ -31,65 +29,13 @@ PDUUnit::~PDUUnit() {
}
}
uint64_t PDUUnit::readValue(const xmlPathVector_t& xmlPath) {
uint64_t now = getTimestamp();
if (now >= _lastRefresh + MS_TO_NS(_ttl)) {
refresh();
#ifdef DEBUG
LOG(debug) << "PDUUnit " << _host << " refreshed XML-file";
#endif
}
std::string reading;
boost::property_tree::ptree node = _ptree;
for (size_t i = 0; i < xmlPath.size(); i++) {
const std::string& path = std::get<0>(xmlPath[i]);
const std::string& child = std::get<1>(xmlPath[i]);
const attributesVector_t& attVec = std::get<2>(xmlPath[i]);
unsigned matchCount;
if (child != "") {
BOOST_FOREACH(boost::property_tree::ptree::value_type &v, node.get_child(path)) {
if (v.first == child) {
matchCount = 0;
for (size_t j = 0; j < attVec.size(); j++) {
std::string attributeVal = v.second.get_child("<xmlattr>." + attVec[j].first).data();
if (attributeVal != attVec[j].second) { //attribute values don't match
break;
} else {
matchCount++;
}
}
if (matchCount == attVec.size()) { //all attributes matched
reading = v.second.data();
node = v.second;
break;
}
}
}
} else { //child == ""
reading = node.get(path, "");
break; //last (part of the) path
}
}
if (reading == "") {
throw std::runtime_error("Value not found!");
}
#ifdef DEBUG
LOG(debug) << "Read: " << reading;
#endif
return stoul(reading);
}
void PDUUnit::initializeStrand(boost::asio::io_service& io) {
if (!_strand) {
_strand = new boost::asio::io_service::strand(io);
}
}
void PDUUnit::refresh() {
bool PDUUnit::sendRequest(const std::string& request, std::string& response) {
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
......@@ -102,7 +48,7 @@ void PDUUnit::refresh() {
if (!ctx) {
LOG(error) << "OpenSSL: Could not create context: " << ERR_reason_error_string(ERR_get_error());
return;
return false;
}
/*
......@@ -120,54 +66,45 @@ void PDUUnit::refresh() {
if(BIO_do_connect(bio) <= 0)
{
LOG(error) << "OpenSSL: Could not connect: " << ERR_reason_error_string(ERR_get_error());
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
size_t len = _request.length();
const char* reqBuf = _request.c_str(); //request
size_t len = request.length();
const char* reqBuf = request.c_str(); //request
char resBuf[2048]; //response
char resBuf[2048];
memset(resBuf, 0, 2048);
// do not bother too long if a write/read failed. Sensor intervals are usually small, just try again when next sensor wants to read.
// Current sensor has to use old value though.
// send request
if (BIO_write(bio, reqBuf, len) <= 0) {
LOG(error) << "OpenSSL: Could not send request: " << ERR_reason_error_string(ERR_get_error());
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
return false;
}
// read reply
std::string response;
response.clear();
while ((len = BIO_read(bio, resBuf, sizeof(resBuf))) > 0) {
resBuf[len] = 0;
response.append(resBuf);
}
resBuf[len] = 0;
response.append(resBuf);
}
if (len < 0) {
std::cerr << "OpenSSL: Could not read response: " << ERR_reason_error_string(ERR_get_error()) << std::endl;
BIO_free_all(bio);
SSL_CTX_free(ctx);
return;
}
std::cerr << "OpenSSL: Could not read response: " << ERR_reason_error_string(ERR_get_error()) << std::endl;
BIO_free_all(bio);
SSL_CTX_free(ctx);
return false;
}
BIO_free_all(bio);
SSL_CTX_free(ctx);
std::string xml = response.substr(response.find("<"));
std::istringstream treeStream(xml);
try {
boost::property_tree::read_xml(treeStream, _ptree);
} catch (const std::exception& e) {
LOG(error) << "PDU: Got malformed XML repsonse from " << _host;
return;
}
_lastRefresh = getTimestamp();
return;
return true;
}
......@@ -28,7 +28,6 @@ public:
PDUUnit();
virtual ~PDUUnit();
void setTTL(const std::string& ttl) { _ttl = stoull(ttl); }
void setHost(const std::string& host) {
size_t pos = host.find(':');
if (pos != std::string::npos) {
......@@ -37,35 +36,24 @@ public:
_host = host + ":443";
}
}
void setRequest(const std::string& request) { _request = request; }
void initializeStrand(boost::asio::io_service& io);
unsigned getTTL() const { return _ttl; }
const std::string& getHost() { return _host; }
const std::string& getRequest() { return _request; }
boost::asio::io_service::strand* getStrand() const { return _strand; }
/**
* Finds and returns the corresponding value in the energy.xml
* Checks if the _ptree is still up to date. If not updates it by a call to refresh().
* Send the request to the host and write the response into response.
*
* @param xmlPath Holds all data required to identify the XML path to the sensor value
* @return The value in the latest energy.xml, denoted by paths and attributes
* @param request String with the request to send
* @param response String where the response will be stored
*
* @return True on success, false otherwise
*/
uint64_t readValue(const xmlPathVector_t& xmlPath);
bool sendRequest(const std::string& request, std::string& response);
private:
/**
* Gets a current version of the XML-file energy.xml with sensor data and parses its content into _ptree
*/
void refresh();
uint64_t _lastRefresh;
unsigned int _ttl;
std::string _host;
std::string _request;
boost::property_tree::ptree _ptree;
boost::asio::io_service::strand* _strand;
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment