BACnetClient.cpp 9.75 KB
Newer Older
1
2
3
4
5
6
7
8
9
/*
 * BACnetClient.cpp
 *
 *  Created on: 20.04.2018
 *      Author: Micha Mueller
 */

#include "BACnetClient.h"

10
11
12
13
14
15
16
17
18
#include "address.h"
#include "apdu.h"
#include "bacapp.h"
#include "bactext.h"
#include "datalink.h"
#include "npdu.h"
#include "reject.h"
#include "rp.h"
#include "tsm.h"
19

20
double BACnetClient::_presentValue;
21
uint8_t BACnetClient::_handlerTransmitBuffer[MAX_PDU];
22

23
24
BACnetClient::BACnetClient() {
	_strand = NULL;
25
26
	_invokeId = 0;
	_presentValue = 0;
27
	_timeout = 1000;
28
	_targetAddress = {0};
29
30
31
}

BACnetClient::~BACnetClient() {
32
33
34
	if (_strand) {
		delete _strand;
	}
35
	datalink_cleanup();
36
37
}

38
void BACnetClient::init(std::string interface, const std::string& address_cache, unsigned port, unsigned timeout, unsigned apdu_timeout, unsigned retries) {
39
40
	_timeout = timeout;

41
42
43
44
45
46
47
	if (FILE *file = fopen(address_cache.c_str(), "r")) {
		fclose(file);
	} else {
		throw std::runtime_error("Can not open address cache file");
	}

	address_init_by_file(address_cache.c_str());
48
49
50
51

	//setup datalink

//#if defined(BACDL_BIP)
52
    bip_set_port(port);
53
//#endif
54
	apdu_timeout_set(apdu_timeout);
55
56
57
	apdu_retries_set(retries);

    if (!datalink_init(&interface[0])) {
58
    	throw std::runtime_error("Failed to setup datalink");
59
60
    }
    //end setup datalink
61
62
63

	/* set the handler for all the services we don't implement
	   It is required to send the proper reject message... */
64
	apdu_set_unrecognized_service_handler_handler(unrecognizedServiceHandler);
65

66
	//NOTE: no handler for read property set even though it is required. We are no real BACnet device
67
68

	/* we only need to handle the data coming back from confirmed (read property) requests */
69
	apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY,	readPropertyAckHandler);
70
71
72
73
74

	/* handle any errors coming back */
	apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, errorHandler);
	apdu_set_abort_handler(abortHandler);
	apdu_set_reject_handler(rejectHandler);
75
76
}

77
double BACnetClient::readProperty(uint32_t deviceObjInstance, uint32_t objInstance, BACNET_OBJECT_TYPE objType, BACNET_PROPERTY_ID objProperty, int32_t objIndex) {
78
	//TODO better use readPropertyMultiple? But how to assign the properties to multiple different MQTT topics?
79

80
81
82
83
	uint16_t pdu_lenRec = 0;
	int pdu_lenSent		= 0;
	int bytes_sent		= 0;
	unsigned max_apdu 	= 0;
84
	_targetAddress	= {0};		//where message goes to
85
86
87
88
89
90
91
92
	BACNET_ADDRESS src	= {0}; 	//where response came from
	BACNET_ADDRESS myAddr = {0};//our address

	BACNET_READ_PROPERTY_DATA data;
	BACNET_NPDU_DATA npdu_data;

	uint8_t RecBuf[MAX_MPDU] = {0};

93
	if (!address_get_by_device(deviceObjInstance, &max_apdu, &_targetAddress)) {
94
95
96
97
		throw std::runtime_error("Address not found");
	}

	/*
98
	if (!_targetAddress) {
99
100
101
102
103
104
105
106
107
108
		throw std::runtime_error("Destination address empty");
	}
	*/

	/*
	if (!dcc_communication_enabled()) {
		throw std::runtime_error("Communication Control disabled");
	}
	*/

109
	if (!(_invokeId = tsm_next_free_invokeID())) {
110
111
112
113
		throw std::runtime_error("No TSM available");
	}

	/* encode the NPDU portion of the packet */
114
	datalink_get_my_address(&myAddr);
115
	npdu_encode_npdu_data(&npdu_data, true, MESSAGE_PRIORITY_NORMAL);
116
	pdu_lenSent = npdu_encode_pdu(&_handlerTransmitBuffer[0], &_targetAddress, &myAddr, &npdu_data);
117
118
119
120
121
122

	/* encode the APDU portion of the packet */
	data.object_type = objType;
	data.object_instance = objInstance;
	data.object_property = objProperty;
	data.array_index = objIndex;
123
	pdu_lenSent += rp_encode_apdu(&_handlerTransmitBuffer[pdu_lenSent], _invokeId, &data);
124
125
126
127
128
129
	/* will it fit in the sender?
	   note: if there is a bottleneck router in between
	   us and the destination, we won't know unless
	   we have a way to check for that and update the
	   max_apdu in the address binding table. */
	if ((uint16_t) pdu_lenSent < max_apdu) {
130
131
		tsm_set_confirmed_unsegmented_transaction(_invokeId, &_targetAddress, &npdu_data, &_handlerTransmitBuffer[0], (uint16_t) pdu_lenSent);
		bytes_sent = datalink_send_pdu(&_targetAddress, &npdu_data, &_handlerTransmitBuffer[0], pdu_lenSent);
132
		if (bytes_sent <= 0) {
133
134
135
			std::string errorMsg = strerror(errno);
			std::string str = "Failed to send ReadProperty Request ";
			throw std::runtime_error(str + errorMsg);
136
137
		}
	} else {
138
		tsm_free_invoke_id(_invokeId);
139
140
141
142
		throw std::runtime_error("Failed to Send ReadProperty Request (exceeds destination maximum APDU)");
	}

	// returns 0 on timeout
143
	pdu_lenRec = datalink_receive(&src, &RecBuf[0], MAX_MPDU, _timeout);
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
	if (pdu_lenRec) {
	    int apdu_offset = 0;
	    BACNET_ADDRESS destRec = { 0 };
	    BACNET_NPDU_DATA npdu_dataRec = { 0 };

		apdu_offset = npdu_decode(&RecBuf[0], &destRec, &src, &npdu_dataRec);
		if (npdu_data.network_layer_message) {
			/* network layer message received!  Handle it! */
			LOG(error) << "Network layer message received. Discarding";
		} else if ((apdu_offset > 0) && (apdu_offset <= pdu_lenRec)) {
			if ((destRec.net == 0) || (destRec.net == BACNET_BROADCAST_NETWORK)) {
				/* only handle the version that we know how to handle */
				/* and we are not a router, so ignore messages with
				   routing information cause they are not for us */
				if (!(destRec.net == BACNET_BROADCAST_NETWORK) && !((RecBuf[apdu_offset] & 0xF0) ==	PDU_TYPE_CONFIRMED_SERVICE_REQUEST)) {
					apdu_handler(&src, &RecBuf[apdu_offset], (uint16_t) (pdu_lenRec - apdu_offset));
				}
			}
		}
164
	} else {
165
		tsm_free_invoke_id(_invokeId);
166
167
168
		throw std::runtime_error("Timeout while waiting for response");
	}

169
170
	if (!tsm_invoke_id_free(_invokeId)) { //should be freed by apdu_handler on success
		tsm_free_invoke_id(_invokeId);
171
172
173
		throw std::runtime_error("Invoke ID was not freed");
	}

Micha Mueller's avatar
Micha Mueller committed
174
	return _presentValue;
175
176
177
178
179
180
181
182
183
}


void BACnetClient::initializeStrand(boost::asio::io_service& io) {
	if (!_strand) {
		_strand = new boost::asio::io_service::strand(io);
	}
}

184
void BACnetClient::unrecognizedServiceHandler(uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_DATA * service_data) {
185
	boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
186
187
188
    int pdu_len = 0;
    int bytes_sent = 0;
    BACNET_NPDU_DATA npdu_data;
189
    BACNET_ADDRESS myAddress;
190
191
192
193
194

    (void) service_request;
    (void) service_len;

    /* encode the NPDU portion of the packet */
195
    datalink_get_my_address(&myAddress);
196
    npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
197
    pdu_len = npdu_encode_pdu(&_handlerTransmitBuffer[0], src, &myAddress, &npdu_data);
198
    /* encode the APDU portion of the packet */
199
    pdu_len += reject_encode_apdu(&_handlerTransmitBuffer[pdu_len], service_data->invoke_id, REJECT_REASON_UNRECOGNIZED_SERVICE);
200
201
202
203
204
205
206
207
208
    /* send the data */
    bytes_sent = datalink_send_pdu(src, &npdu_data, &_handlerTransmitBuffer[0], pdu_len);
    if (bytes_sent > 0) {
    	LOG(info) << "BACnet: Sent Reject";
    } else {
    	LOG(info) << "BACnet: Could not send Reject: " << strerror(errno);
    }
}

209
210
void BACnetClient::readPropertyAckHandler(uint8_t * service_request, uint16_t service_len, BACNET_ADDRESS * src, BACNET_CONFIRMED_SERVICE_ACK_DATA * service_data)
{
211
	boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
212
    int serviceLen = 0;
213
    //int appDataLen = 0;
214
215
216
217
218
	uint8_t *application_data;
	int application_data_len;
    BACNET_READ_PROPERTY_DATA data;
    BACNET_APPLICATION_DATA_VALUE value;        /* for decode value data */

219
    /*if (!(address_match(&_targetAddress, src) && (service_data->invoke_id == _invokeId))) {
220
    	throw std::runtime_error("Message not determined for us");
221
    }*/
222
223
224

	serviceLen = rp_ack_decode_service_request(service_request, service_len, &data);
	if (serviceLen <= 0) {
225
		tsm_free_invoke_id(service_data->invoke_id);
226
227
228
229
		throw std::runtime_error("Decode failed");
	}
   //     rp_ack_print_data(&data);

230
231
	application_data = data.application_data;
	application_data_len = data.application_data_len;
232
	/*appDataLen = */bacapp_decode_application_data(application_data, (uint8_t) application_data_len, &value);
233
234
235

	//bacapp_print_value(stdout, &object_value);

236
	//TODO what kind of data is returned? which cases do we need to handle? fit they into int64_t at all??
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
	switch (value.tag) {
		case BACNET_APPLICATION_TAG_NULL:
			LOG(trace) << "TAG_NULL";
			_presentValue = 0;
			break;
		case BACNET_APPLICATION_TAG_BOOLEAN:
			LOG(trace) << "TAG_BOOLEAN";
			_presentValue =	(value.type.Boolean) ? 1 : 0;
			break;
		case BACNET_APPLICATION_TAG_UNSIGNED_INT:
			LOG(trace) << "TAG_UNSIGNED_INT";
			_presentValue =	(unsigned long) value.type.Unsigned_Int;
			break;
		case BACNET_APPLICATION_TAG_SIGNED_INT:
			LOG(trace) << "TAG_SIGNED_INT";
			_presentValue =	(long) value.type.Signed_Int;
			break;
		case BACNET_APPLICATION_TAG_REAL:
			LOG(trace) << "TAG_REAL";
			_presentValue = (double) value.type.Real;
			break;
		case BACNET_APPLICATION_TAG_DOUBLE:
			LOG(trace) << "TAG_DOUBLE";
			_presentValue = value.type.Double;
			break;
		default:
263
			tsm_free_invoke_id(service_data->invoke_id);
264
265
			throw std::runtime_error("Value tag not supported");
			break;
266
	}
267
268
}

269
void BACnetClient::errorHandler(BACNET_ADDRESS * src, uint8_t invokeId, BACNET_ERROR_CLASS error_class, BACNET_ERROR_CODE error_code) {
270
271
272
273
274
	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);
275
276
}

277
void BACnetClient::abortHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t abort_reason, bool server) {
278
279
280
281
	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);
282
283
284
}

void BACnetClient::rejectHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t reject_reason) {
285
286
287
288
	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);
289
290
}