Commit e3f9034d authored by Micha Mueller's avatar Micha Mueller
Browse files

Implement logic for BACnetConfigurator (not yet tested). Induced slightly...

Implement logic for BACnetConfigurator (not yet tested). Induced slightly changes to bacnet.conf and BACnetClient
parent c80dc233
global {
mqttPrefix /FF112233445566778899FFFF
interface eth0
port 22222
apdu_timeout 200
apdu_retries 1
}
templates {
......@@ -11,7 +15,7 @@ templates {
}
devices {
device {
device asdf {
instance 1234
mqttPart FF
......@@ -44,5 +48,9 @@ devices {
mqttsuffix 0003
}
}
device jklo {
...
}
}
......@@ -30,40 +30,21 @@ BACnetClient::~BACnetClient() {
datalink_cleanup();
}
void BACnetClient::init() {
void BACnetClient::init(std::string interface, unsigned port, unsigned timeout, unsigned retries) {
//compile BACnet stack with BACNET_ADDRESS_CACHE_FILE for initialization from file address_cache
address_init();
//setup datalink
char *pEnv = NULL;
//#if defined(BACDL_BIP)
pEnv = getenv("BACNET_IP_PORT");
if (pEnv) {
bip_set_port(htons((uint16_t) strtol(pEnv, NULL, 0)));
} else {
/* BIP_Port is statically initialized to 0xBAC0,
* so if it is different, then it was programmatically altered,
* and we shouldn't just stomp on it here.
* Unless it is set below 1024, since:
* "The range for well-known ports managed by the IANA is 0-1023."
*/
if (ntohs(bip_get_port()) < 1024)
bip_set_port(htons(0xBAC0));
}
bip_set_port(port);
//#endif
pEnv = getenv("BACNET_APDU_TIMEOUT");
if (pEnv) {
apdu_timeout_set((uint16_t) strtol(pEnv, NULL, 0));
} else {
//
}
pEnv = getenv("BACNET_APDU_RETRIES");
if (pEnv) {
apdu_retries_set((uint8_t) strtol(pEnv, NULL, 0));
}
apdu_timeout_set(timeout);
apdu_retries_set(retries);
/* === Initialize the Datalink Here === */
if (!datalink_init(getenv("BACNET_IFACE"))) {
if (!datalink_init(&interface[0])) {
//TODO abort
exit(1);
}
//end setup datalink
......
......@@ -26,7 +26,14 @@ public:
return _strand;
}
void init();
/**
* Initialize datalink layer and address cache.
* We assume BACnet/IP protocol is used. Also you need to compile with environment variable BACNET_ADDRESS_CACHE_FILE set
* to enable initialization of address cache from file "address_cache".
*
* @param port
*/
void init(std::string interface, unsigned port = 47808, unsigned timeout = 200, unsigned retries = 0);
/**
* Sends a READ_PROPERTY request for PROP_PRESENT_VALUE to specified device and decodes the response (READ_PROPERTY_ACK).
......
......@@ -7,6 +7,12 @@
#include "BACnetConfigurator.h"
#include <boost/property_tree/info_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/optional.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
BACnetConfigurator::BACnetConfigurator() {
_bacClient = NULL;
}
......@@ -19,10 +25,148 @@ BACnetConfigurator::~BACnetConfigurator() {
}
std::vector<Sensor*>& BACnetConfigurator::readConfig(std::string cfgPath) {
//TODO
boost::property_tree::iptree cfg;
boost::property_tree::read_info(cfgPath, cfg);
_bacClient = new BACnetClient();
std::string interface, mqttPartDevice, mqttPartObject;
unsigned port = 47808, apdu_timeout = 200, apdu_retries = 0;
unsigned deviceInstance, objInstance;
BACNET_OBJECT_TYPE objType;
//read global variables (if present overwrite those from global.conf)
BOOST_FOREACH(boost::property_tree::iptree::value_type &global, cfg.get_child("global")) {
if (boost::iequals(global.first, "interface")) {
interface = global.second.data();
LOG(debug) << " Interface " << interface;
} else if (boost::iequals(global.first, "port")) {
port = stoul(global.second.data());
LOG(debug) << " Port " << port;
} else if (boost::iequals(global.first, "apdu_timeout")) {
apdu_timeout = stoul(global.second.data());
LOG(debug) << " apdu_timeout " << apdu_timeout;
} else if (boost::iequals(global.first, "apdu_retries")) {
apdu_retries = stoul(global.second.data());
LOG(debug) << " apdu_retries " << apdu_retries;
} else if (boost::iequals(global.first, "mqttprefix")) {
_mqttPrefix = global.second.data();
LOG(debug) << " Using own MQTT-Prefix " << _mqttPrefix;
} else {
LOG(error) << " Value \"" << global.first << "\" not recognized. Omitting...";
}
}
try {
_bacClient->init(interface, port, apdu_timeout, apdu_retries);
} catch (const std::exception& e) {
LOG(error) << "Could not initialize BACnetClient!";
return NULL;
}
//read template sensors
BOOST_FOREACH(boost::property_tree::iptree::value_type &sensor, cfg.get_child("templates")) {
if (boost::iequals(sensor.first, "property")) {
LOG(debug) << "Template Sensor \"" << sensor.second.data() << "\"";
if (!sensor.second.empty()) {
BACnetSensor bacnetSensor(sensor.second.data());
if(readSensor(bacnetSensor, sensor.second)) {
_templateSensors.insert(sensorMap_t::value_type(bacnetSensor.getName(), bacnetSensor));
} else {
LOG(warning) << "Template sensor \"" << sensor.second.data() << "\" has bad values! Ignoring...";
}
}
}
}
BOOST_FOREACH(boost::property_tree::iptree::value_type &device, cfg.get_child("devices")) {
if (boost::iequals(device.first, ("device"))) {
LOG(debug) << "Device " << device.second.data();
if (!device.second.empty()) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &d, device.second) {
if (boost::iequals(d.first, "instance")) {
deviceInstance = stoul(d.second.data());
} else if (boost::iequals(d.first, "mqttPart")) {
mqttPartDevice = d.second.data();
} else if (boost::iequals(d.first, "object")) {
LOG(debug) << "Object \"" << d.second.data() << "\"";
if (!d.second.empty()) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &object, d.second) {
if (boost::iequals(object.first, "type")) {
objType = static_cast<BACNET_OBJECT_TYPE>(stoul(object.second.data()));
} else if (boost::iequals(object.first, "instance")) {
objInstance = stoul(object.second.data());
} else if (boost::iequals(object.first, "mqttPart")) {
mqttPartObject = object.second.data();
} else if (boost::iequals(object.first, "property")) {
LOG(debug) << "Property \"" << object.second.data() << "\"";
if (!object.second.empty()) {
BOOST_FOREACH(boost::property_tree::iptree::value_type &property, object.second) {
BACnetSensor * bacSensor = new BACnetSensor(property.second.data());
//first check if default sensor is given
boost::optional<boost::property_tree::iptree&> defaultC = property.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()) {
*bacSensor = it->second;
bacSensor->setName(property.second.data());
} else {
LOG(warning) << " Template sensor \"" << defaultC.get().data() << "\" not found! Using standard values.";
}
}
//set object and device related data
//assume property aka sensors are read at last
bacSensor->setDeviceInstance(deviceInstance);
bacSensor->setObjectInstance(objInstance);
bacSensor->setObjectType(objType);
bacSensor->setMqtt(_mqttPrefix + mqttPartDevice + mqttPartObject);
//read remaining values
if(readSensor(*bacSensor, property.second)) {
_sensors.push_back(bacSensor);
} else {
LOG(warning) << " Sensor \"" << property.second.data() << "\" has bad values! Ignoring..." << std::endl;
}
}
}
}
}
}
}
}
}
}
}
return _sensors;
}
void BACnetConfigurator::readSensor(BACnetSensor& sensor, boost::property_tree::iptree& config) {
//TODO
BOOST_FOREACH(boost::property_tree::iptree::value_type &val, config) {
if (boost::iequals(val.first, "interval")) {
sensor.setInterval(stoull(val.second.data()));
} else if (boost::iequals(val.first, "mqttsuffix")) {
//assume prefix mqtt part is already set in this sensor. Just append suffix
sensor.setMqtt(sensor.getMqtt() + val.second.data());
} else if (boost::iequals(val.first, "minValues")) {
sensor.setMinValues(stoull(val.second.data()));
} else if (boost::iequals(val.first, ("id"))) {
sensor.setPropertyId(static_cast<BACNET_PROPERTY_ID>(stoul(val.second.data())));
} else if (boost::iequals(val.first, "default")) {
//avoid unnecessary "Value not recognized" message
} else {
LOG(warning) << " Value \"" << val.first << "\" not recognized. Omitting...";
}
}
LOG(debug) << " MQTTtopic:" << sensor.getMqtt();
LOG(debug) << " Interval : " << sensor.getInterval();
LOG(debug) << " minValues: " << sensor.getMinValues();
LOG(debug) << " instance : " << sensor.getDeviceInstance();
LOG(debug) << " objInst : " << sensor.getObjectInstance();
LOG(debug) << " objType : " << sensor.getObjectType();
LOG(debug) << " Property : " << sensor.getPropertyId();
return;
}
Markdown is supported
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