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 4f454ff4 authored by Micha Mueller's avatar Micha Mueller
Browse files

Further bugfixes and improvements to BACnet plugin. Should now work reliable

parent f976fb10
......@@ -394,7 +394,7 @@ Explanation of the values specific for the PDU plugin:
## BACnet
The BACnet plugin enables dcdbpusher to communicate and request data from devices which communicate via the BACnet protocol. A so called "read property" request is sent by the plugin to the BACnet devices as configured in the config file. The response value is then stored in the database. Usually one is only interested in collecting the current reading of a BACnet device (property PROP_PRESENT_VALUE, ID 85). However, also reading of other properties is supported.
> NOTE     On startup BACnet plugin does no device discovery. Instead it relies on the user providing an `address_cache` file with addresses of all required BACnet devices. One can generate such a address-file for example by using the `bacwi` demo tool provided by the BACnet-Stack.
> NOTE     On startup BACnet plugin does no device discovery. Instead it relies on the user providing an `address_cache` file with addresses of all required BACnet devices. One can generate such a address-file for example by using the `bacwi` demo tool provided by the BACnet-Stack. Currently the `address_cache` file is expected to be in the same directory as the dcdbpusher binary.
### config file format
......
......@@ -20,8 +20,8 @@ devices {
mqttPart FF
object o1 {
type 1
instance 1
type 8
instance 1234
mqttPart FF
property test1 {
default def0
......@@ -33,25 +33,36 @@ devices {
}
}
object o2 {
type 1
instance 2
; object o2 {
; type 1
; instance 2
; mqttPart FF
; property propName {
; default def0
; minValues 1
; mqttsuffix 0002
; }
; property propName2 {
; id 80
; minValues 1
; mqttsuffix 0003
; }
; }
}
device jklo {
instance 2222
mqttPart FF
object o {
type 8
instance 1234
mqttPart FF
property propName {
default def0
minValues 1
mqttsuffix 0002
}
property propName2 {
id 80
minValues 1
mqttsuffix 0003
property prop {
id 75
mqttsuffix 0004
}
}
}
; device jklo {
; ...
; }
}
......@@ -45,17 +45,11 @@ void BACnetClient::init(std::string interface, unsigned port, unsigned timeout,
apdu_timeout_set(timeout);
apdu_retries_set(retries);
/* === Initialize the Datalink Here === */
if (!datalink_init(&interface[0])) {
//TODO abort
exit(1);
throw std::runtime_error("Failed to setup datalink");
}
//end setup datalink
//Device_Init(NULL); // Initializes Device Object; TODO do we need this?
using namespace std::placeholders; // for _1, _2, ...
/* set the handler for all the services we don't implement
It is required to send the proper reject message... */
apdu_set_unrecognized_service_handler_handler(unrecognizedServiceHandler);
......@@ -72,7 +66,7 @@ void BACnetClient::init(std::string interface, unsigned port, unsigned timeout,
}
uint64_t BACnetClient::readProperty(uint32_t deviceObjInstance, uint32_t objInstance, BACNET_OBJECT_TYPE objType, BACNET_PROPERTY_ID objProperty, int32_t objIndex) {
//TODO better use readPropertyMultiple?
//TODO better use readPropertyMultiple? But how to assign the properties to multiple different MQTT topics?
uint16_t pdu_lenRec = 0;
int pdu_lenSent = 0;
......@@ -133,20 +127,17 @@ uint64_t BACnetClient::readProperty(uint32_t deviceObjInstance, uint32_t objInst
}
} else {
tsm_free_invoke_id(_invokeId);
_invokeId = 0;
throw std::runtime_error("Failed to Send ReadProperty Request (exceeds destination maximum APDU)");
}
// returns 0 on timeout
pdu_lenRec = datalink_receive(&src, &RecBuf[0], MAX_MPDU, _timeout); //TODO use bip_receive?
pdu_lenRec = datalink_receive(&src, &RecBuf[0], MAX_MPDU, _timeout);
if (pdu_lenRec) {
int apdu_offset = 0;
BACNET_ADDRESS destRec = { 0 };
BACNET_NPDU_DATA npdu_dataRec = { 0 };
//LOG(debug) << "BACnet: Protocol Version = " << (unsigned) RecBuf[0];
apdu_offset = npdu_decode(&RecBuf[0], &destRec, &src, &npdu_dataRec);
if (npdu_data.network_layer_message) {
/* network layer message received! Handle it! */
......@@ -210,7 +201,7 @@ void BACnetClient::readPropertyAckHandler(uint8_t * service_request, uint16_t se
{
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
int serviceLen = 0;
int appDataLen = 0;
//int appDataLen = 0;
uint8_t *application_data;
int application_data_len;
BACNET_READ_PROPERTY_DATA data;
......@@ -222,13 +213,14 @@ void BACnetClient::readPropertyAckHandler(uint8_t * service_request, uint16_t se
serviceLen = rp_ack_decode_service_request(service_request, service_len, &data);
if (serviceLen <= 0) {
tsm_free_invoke_id(service_data->invoke_id);
throw std::runtime_error("Decode failed");
}
// rp_ack_print_data(&data);
application_data = data.application_data;
application_data_len = data.application_data_len;
appDataLen = bacapp_decode_application_data(application_data, (uint8_t) application_data_len, &value);
/*appDataLen = */bacapp_decode_application_data(application_data, (uint8_t) application_data_len, &value);
//bacapp_print_value(stdout, &object_value);
......@@ -259,23 +251,31 @@ void BACnetClient::readPropertyAckHandler(uint8_t * service_request, uint16_t se
_presentValue = value.type.Double;
break;
default:
tsm_free_invoke_id(service_data->invoke_id);
throw std::runtime_error("Value tag not supported");
break;
}
}
void BACnetClient::errorHandler(BACNET_ADDRESS * src, uint8_t invokeId, BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code) {
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
LOG(error) << "BACnet Error: " << bactext_error_class_name((int) error_class) << bactext_error_code_name((int) error_code);
tsm_free_invoke_id(invokeId);
std::string str = "BACnet Error: ";
std::string errorMsg1 = bactext_error_class_name((int) error_class);
std::string errorMsg2 = bactext_error_code_name((int) error_code);
throw std::runtime_error(str + errorMsg1 + errorMsg2);
}
void BACnetClient::abortHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t abort_reason, bool server) {
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
LOG(error) << "BACnet Abort: " << bactext_abort_reason_name((int) abort_reason);
tsm_free_invoke_id(invokeId);
std::string str = "BACnet Abort: ";
std::string errorMsg = bactext_abort_reason_name((int) abort_reason);
throw std::runtime_error(str + errorMsg);
}
void BACnetClient::rejectHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t reject_reason) {
boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
LOG(error) << "BACnet Reject: " << bactext_reject_reason_name((int) reject_reason);
tsm_free_invoke_id(invokeId);
std::string str = "BACnet Reject: ";
std::string errorMsg = bactext_reject_reason_name((int) reject_reason);
throw std::runtime_error(str + errorMsg);
}
......@@ -59,7 +59,7 @@ std::vector<Sensor*>& BACnetConfigurator::readConfig(std::string cfgPath) {
try {
_bacClient->init(interface, port, apdu_timeout, apdu_retries);
} catch (const std::exception& e) {
LOG(error) << "Could not initialize BACnetClient!";
LOG(error) << "Could not initialize BACnetClient: " << e.what();
return _sensors;
}
......
......@@ -48,7 +48,7 @@ void BACnetSensor::readAsync() {
if (_timer != NULL && keepRunning) {
uint64_t next = now + MS_TO_NS(_interval);
_timer->expires_at(timestamp2ptime(next));
_timer->async_wait(std::bind(&BACnetSensor::readAsync, this));
_timer->async_wait(_bacClient->getStrand()->wrap(std::bind(&BACnetSensor::readAsync, this)));
}
}
......@@ -60,7 +60,7 @@ void BACnetSensor::startPolling(boost::asio::io_service& io) {
if (_bacClient) {
_bacClient->initializeStrand(io);
_timer = new boost::asio::deadline_timer(io, boost::posix_time::seconds(0));
_timer->async_wait(std::bind(&BACnetSensor::readAsync, this));
_timer->async_wait(_bacClient->getStrand()->wrap(std::bind(&BACnetSensor::readAsync, this)));
} else {
LOG(error) << "No BACnetClient set for sensor " << _name << "! Cannot start polling.";
}
......
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