BACnetClient.cpp 9.69 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
//#include "client.h"
#include "bacnetTEMP/address.h"
#include "bacnetTEMP/apdu.h"
13
#include "bacnetTEMP/bacapp.h"
14
#include "bacnetTEMP/bactext.h"
15
16
#include "bacnetTEMP/datalink.h"
#include "bacnetTEMP/npdu.h"
17
#include "bacnetTEMP/reject.h"
18
19
20
#include "bacnetTEMP/rp.h"
#include "bacnetTEMP/tsm.h"

21
22
uint64_t BACnetClient::_presentValue;
uint8_t BACnetClient::_handlerTransmitBuffer[MAX_PDU];
23

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

BACnetClient::~BACnetClient() {
33
	datalink_cleanup();
34
35
}

36
void BACnetClient::init(std::string interface, unsigned port, unsigned timeout, unsigned retries) {
37
38
39
40
41
42
	//compile BACnet stack with BACNET_ADDRESS_CACHE_FILE for initialization from file address_cache
	address_init();

	//setup datalink

//#if defined(BACDL_BIP)
43
    bip_set_port(port);
44
//#endif
45
46
47
	apdu_timeout_set(timeout);
	apdu_retries_set(retries);

48
    /* === Initialize the Datalink Here === */
49
50
    if (!datalink_init(&interface[0])) {
    	//TODO abort
51
52
53
        exit(1);
    }
    //end setup datalink
54
55
56

	//Device_Init(NULL); // Initializes Device Object; TODO do we need this?

57
58
    using namespace std::placeholders;	// for _1, _2, ...

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

63
	//NOTE: no handler for read property set even though it is required. We are no real BACnet device
64
65

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

	/* handle any errors coming back */
	apdu_set_error_handler(SERVICE_CONFIRMED_READ_PROPERTY, errorHandler);
	apdu_set_abort_handler(abortHandler);
	apdu_set_reject_handler(rejectHandler);
72
73
74
}

uint64_t BACnetClient::readProperty(uint32_t deviceObjInstance, uint32_t objInstance, BACNET_OBJECT_TYPE objType, BACNET_PROPERTY_ID objProperty, int32_t objIndex) {
75
76
	//TODO better use readPropertyMultiple?

77
78
79
80
	uint16_t pdu_lenRec = 0;
	int pdu_lenSent		= 0;
	int bytes_sent		= 0;
	unsigned max_apdu 	= 0;
81
	_targetAddress	= {0};		//where message goes to
82
83
84
85
86
87
88
89
	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};

90
	if (!address_get_by_device(deviceObjInstance, &max_apdu, &_targetAddress)) {
91
92
93
94
		throw std::runtime_error("Address not found");
	}

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

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

106
	if (!(_invokeId = tsm_next_free_invokeID())) {
107
108
109
110
		throw std::runtime_error("No TSM available");
	}

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

	/* encode the APDU portion of the packet */
	data.object_type = objType;
	data.object_instance = objInstance;
	data.object_property = objProperty;
	data.array_index = objIndex;
120
	pdu_lenSent += rp_encode_apdu(&_handlerTransmitBuffer[pdu_lenSent], _invokeId, &data);
121
122
123
124
125
126
	/* 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) {
127
128
		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);
129
		if (bytes_sent <= 0) {
130
131
132
			std::string errorMsg = strerror(errno);
			std::string str = "Failed to send ReadProperty Request ";
			throw std::runtime_error(str + errorMsg);
133
134
		}
	} else {
135
136
		tsm_free_invoke_id(_invokeId);
		_invokeId = 0;
137
138
139
140
		throw std::runtime_error("Failed to Send ReadProperty Request (exceeds destination maximum APDU)");
	}

	// returns 0 on timeout
141
	pdu_lenRec = datalink_receive(&src, &RecBuf[0], MAX_MPDU, _timeout);	//TODO use bip_receive?
142

143
144
145
146
147
148
149
	if (pdu_lenRec) {
	    int apdu_offset = 0;
	    BACNET_ADDRESS destRec = { 0 };
	    BACNET_NPDU_DATA npdu_dataRec = { 0 };

#ifdef DEBUG
	    LOG(debug) << "BACnet: Protocol Version = " << (unsigned) RecBuf[0];
150
#endif
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

		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));
				}
			}
		}
166
	} else {
167
		tsm_free_invoke_id(_invokeId);
168
169
170
		throw std::runtime_error("Timeout while waiting for response");
	}

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

176
177
178
179
180
181
182
183
184
185
	return 0;
}


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

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

    (void) service_request;
    (void) service_len;

    /* encode the NPDU portion of the packet */
197
    datalink_get_my_address(&myAddress);
198
    npdu_encode_npdu_data(&npdu_data, false, MESSAGE_PRIORITY_NORMAL);
199
    pdu_len = npdu_encode_pdu(&_handlerTransmitBuffer[0], src, &myAddress, &npdu_data);
200
    /* encode the APDU portion of the packet */
201
    pdu_len += reject_encode_apdu(&_handlerTransmitBuffer[pdu_len], service_data->invoke_id, REJECT_REASON_UNRECOGNIZED_SERVICE);
202
203
204
205
206
207
208
209
210
    /* 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);
    }
}

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

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

	serviceLen = rp_ack_decode_service_request(service_request, service_len, &data);
	if (serviceLen <= 0) {
		throw std::runtime_error("Decode failed");
	}
   //     rp_ack_print_data(&data);

231
232
233
234
235
236
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
263
264
265
	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);

	//bacapp_print_value(stdout, &object_value);

	//TODO what kind of data is returned? which cases do we need to handle? fit they into uint64_t at all??
	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:
			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
	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);
272
273
}

274
void BACnetClient::abortHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t abort_reason, bool server) {
275
276
	boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
	LOG(error) << "BACnet Abort: " << bactext_abort_reason_name((int) abort_reason);
277
278
279
}

void BACnetClient::rejectHandler(BACNET_ADDRESS * src, uint8_t invokeId, uint8_t reject_reason) {
280
281
	boost::log::sources::severity_logger<boost::log::trivial::severity_level> lg;
	LOG(error) << "BACnet Reject: " << bactext_reject_reason_name((int) reject_reason);
282
283
}