Adding code to support NPDU.
This commit is contained in:
@@ -121,6 +121,37 @@ uint8_t encode_max_segs_max_apdu(int max_segs, int max_apdu)
|
|||||||
return octet;
|
return octet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int encode_bacnet_unsigned16(uint8_t * apdu, uint16_t value)
|
||||||
|
{
|
||||||
|
int len = 0; // return value
|
||||||
|
|
||||||
|
if (value < 0x100) {
|
||||||
|
apdu[0] = value;
|
||||||
|
apdu[1] = 0;
|
||||||
|
len = 2;
|
||||||
|
} else {
|
||||||
|
apdu[0] = value / 0x100;
|
||||||
|
apdu[1] = value - (apdu[0] * 0x100);
|
||||||
|
len = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int decode_unsigned16(uint8_t * apdu, uint16_t *value)
|
||||||
|
{
|
||||||
|
int len = 0; // return value
|
||||||
|
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
*value = (apdu[len] * 0x100) + apdu[len + 1];
|
||||||
|
len = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// from clause 20.2.1 General Rules for Encoding BACnet Tags
|
// from clause 20.2.1 General Rules for Encoding BACnet Tags
|
||||||
// returns the number of apdu bytes consumed
|
// returns the number of apdu bytes consumed
|
||||||
int encode_tag(uint8_t * apdu, uint8_t tag_number, bool context_specific,
|
int encode_tag(uint8_t * apdu, uint8_t tag_number, bool context_specific,
|
||||||
|
|||||||
@@ -135,4 +135,13 @@ int encode_tagged_date(uint8_t * apdu, int year, int month, int day,
|
|||||||
int decode_date(uint8_t * apdu, int *year, int *month, int *day,
|
int decode_date(uint8_t * apdu, int *year, int *month, int *day,
|
||||||
int *wday);
|
int *wday);
|
||||||
|
|
||||||
|
// two octet unsigned16
|
||||||
|
int encode_bacnet_unsigned16(uint8_t * apdu, uint16_t value);
|
||||||
|
int decode_unsigned16(uint8_t * apdu, uint16_t *value);
|
||||||
|
|
||||||
|
// from clause 20.1.2.4 max-segments-accepted
|
||||||
|
// and clause 20.1.2.5 max-APDU-length-accepted
|
||||||
|
// returns the encoded octet
|
||||||
|
uint8_t encode_max_segs_max_apdu(int max_segs, int max_apdu);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ struct BACnet_Device_Address {
|
|||||||
// the following are used if the device is behind a router
|
// the following are used if the device is behind a router
|
||||||
// net = 0 indicates local
|
// net = 0 indicates local
|
||||||
uint16_t net; /* BACnet network number */
|
uint16_t net; /* BACnet network number */
|
||||||
|
// LEN = 0 denotes broadcast MAC ADR and ADR field is absent
|
||||||
|
// LEN > 0 specifies length of ADR field
|
||||||
int adr_len; /* length of MAC address */
|
int adr_len; /* length of MAC address */
|
||||||
uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */
|
uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */
|
||||||
};
|
};
|
||||||
|
|||||||
+23
-1
@@ -737,9 +737,31 @@ typedef enum {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MESSAGE_PRIORITY_NORMAL = 0,
|
MESSAGE_PRIORITY_NORMAL = 0,
|
||||||
MESSAGE_PRIORITY_URGENT = 1
|
MESSAGE_PRIORITY_URGENT = 1,
|
||||||
|
MESSAGE_PRIORITY_CRITICAL_EQUIPMENT = 2,
|
||||||
|
MESSAGE_PRIORITY_LIFE_SAFETY = 3
|
||||||
} BACNET_MESSAGE_PRIORITY;
|
} BACNET_MESSAGE_PRIORITY;
|
||||||
|
|
||||||
|
//Network Layer Message Type
|
||||||
|
//If Bit 7 of the control octet described in 6.2.2 is 1,
|
||||||
|
// a message type octet shall be present as shown in Figure 6-1.
|
||||||
|
// The following message types are indicated:
|
||||||
|
typedef enum {
|
||||||
|
NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK = 0,
|
||||||
|
NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK = 1,
|
||||||
|
NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK = 2,
|
||||||
|
NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK = 3,
|
||||||
|
NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK = 4,
|
||||||
|
NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK = 5,
|
||||||
|
NETWORK_MESSAGE_INITIALIZE_ROUTING_TABLE = 6,
|
||||||
|
NETWORK_MESSAGE_INITIALIZE_ROUTING_TABLE_ACK = 7,
|
||||||
|
NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK = 8,
|
||||||
|
NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK = 9,
|
||||||
|
// X'0A' to X'7F': Reserved for use by ASHRAE,
|
||||||
|
// X'80' to X'FF': Available for vendor proprietary messages
|
||||||
|
} BACNET_NETWORK_MESSAGE_TYPE;
|
||||||
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
REINITIALIZED_STATE_COLD_START = 0,
|
REINITIALIZED_STATE_COLD_START = 0,
|
||||||
REINITIALIZED_STATE_WARM_START = 1,
|
REINITIALIZED_STATE_WARM_START = 1,
|
||||||
|
|||||||
+177
-4
@@ -36,6 +36,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "bacdef.h"
|
#include "bacdef.h"
|
||||||
#include "bacenum.h"
|
#include "bacenum.h"
|
||||||
|
#include "bits.h"
|
||||||
|
|
||||||
/* max-segments-accepted
|
/* max-segments-accepted
|
||||||
B'000' Unspecified number of segments accepted.
|
B'000' Unspecified number of segments accepted.
|
||||||
@@ -108,14 +109,186 @@ uint8_t npdu_encode_max_seg_max_apdu(int max_segs, int max_apdu)
|
|||||||
return octet;
|
return octet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int npdu_encode(
|
int npdu_encode_raw(
|
||||||
uint8_t *buf,
|
uint8_t *npdu,
|
||||||
BACNET_ADDRESS *dest,
|
BACNET_ADDRESS *dest,
|
||||||
BACNET_ADDRESS *src,
|
BACNET_ADDRESS *src,
|
||||||
bool data_expecting_reply,
|
bool data_expecting_reply, // true for confirmed messages
|
||||||
|
bool network_layer_message, // false if APDU
|
||||||
|
BACNET_NETWORK_MESSAGE_TYPE network_message_type, // optional
|
||||||
|
uint16_t vendor_id, // optional, if net message type is > 0x80
|
||||||
|
BACNET_MESSAGE_PRIORITY priority,
|
||||||
uint8_t invoke_id)
|
uint8_t invoke_id)
|
||||||
{
|
{
|
||||||
int len = 0;
|
int len = 0; // return value - number of octets loaded in this function
|
||||||
|
int i = 0; // counter
|
||||||
|
|
||||||
|
if (npdu)
|
||||||
|
{
|
||||||
|
// Protocol Version
|
||||||
|
npdu[0] = 1;
|
||||||
|
// control octet
|
||||||
|
npdu[1] = 0;
|
||||||
|
// Bit 7: 1 indicates that the NSDU conveys a network layer message.
|
||||||
|
// Message Type field is present.
|
||||||
|
// 0 indicates that the NSDU contains a BACnet APDU.
|
||||||
|
// Message Type field is absent.
|
||||||
|
if (network_layer_message)
|
||||||
|
npdu[1] |= BIT7;
|
||||||
|
//Bit 6: Reserved. Shall be zero.
|
||||||
|
//Bit 5: Destination specifier where:
|
||||||
|
// 0 = DNET, DLEN, DADR, and Hop Count absent
|
||||||
|
// 1 = DNET, DLEN, and Hop Count present
|
||||||
|
// DLEN = 0 denotes broadcast MAC DADR and DADR field is absent
|
||||||
|
// DLEN > 0 specifies length of DADR field
|
||||||
|
if (dest->net)
|
||||||
|
npdu[1] |= BIT5;
|
||||||
|
// Bit 4: Reserved. Shall be zero.
|
||||||
|
// Bit 3: Source specifier where:
|
||||||
|
// 0 = SNET, SLEN, and SADR absent
|
||||||
|
// 1 = SNET, SLEN, and SADR present
|
||||||
|
// SLEN = 0 Invalid
|
||||||
|
// SLEN > 0 specifies length of SADR field
|
||||||
|
if (src->net)
|
||||||
|
npdu[1] |= BIT3;
|
||||||
|
// Bit 2: The value of this bit corresponds to the data_expecting_reply
|
||||||
|
// parameter in the N-UNITDATA primitives.
|
||||||
|
// 1 indicates that a BACnet-Confirmed-Request-PDU,
|
||||||
|
// a segment of a BACnet-ComplexACK-PDU,
|
||||||
|
// or a network layer message expecting a reply is present.
|
||||||
|
// 0 indicates that other than a BACnet-Confirmed-Request-PDU,
|
||||||
|
// a segment of a BACnet-ComplexACK-PDU,
|
||||||
|
// or a network layer message expecting a reply is present.
|
||||||
|
if (data_expecting_reply)
|
||||||
|
npdu[1] |= BIT2;
|
||||||
|
// Bits 1,0: Network priority where:
|
||||||
|
// B'11' = Life Safety message
|
||||||
|
// B'10' = Critical Equipment message
|
||||||
|
// B'01' = Urgent message
|
||||||
|
// B'00' = Normal message
|
||||||
|
npdu[1] |= (priority & 0x03);
|
||||||
|
len = 2;
|
||||||
|
if (dest->net)
|
||||||
|
{
|
||||||
|
len += encode_bacnet_unsigned16(&npdu[len], dest->net);
|
||||||
|
// DLEN = 0 denotes broadcast MAC DADR and DADR field is absent
|
||||||
|
// DLEN > 0 specifies length of DADR field
|
||||||
|
if (dest->adr_len)
|
||||||
|
{
|
||||||
|
npdu[len] = dest->adr_len;
|
||||||
|
len++;
|
||||||
|
for (i = 0; i < dest->adr_len; i++)
|
||||||
|
{
|
||||||
|
npdu[len] = dest->adr[i];
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (src->net)
|
||||||
|
{
|
||||||
|
len += encode_bacnet_unsigned16(&npdu[len], src->net);
|
||||||
|
// SLEN = 0 denotes broadcast MAC SADR and SADR field is absent
|
||||||
|
// SLEN > 0 specifies length of SADR field
|
||||||
|
if (src->adr_len)
|
||||||
|
{
|
||||||
|
npdu[len] = src->adr_len;
|
||||||
|
len++;
|
||||||
|
for (i = 0; i < src->adr_len; i++)
|
||||||
|
{
|
||||||
|
npdu[len] = src->adr[i];
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The Hop Count field shall be present only if the message is
|
||||||
|
// destined for a remote network, i.e., if DNET is present.
|
||||||
|
// This is a one-octet field that is initialized to a value of 0xff.
|
||||||
|
if (dest->net)
|
||||||
|
{
|
||||||
|
npdu[len] = 0xFF;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
if (network_layer_message)
|
||||||
|
{
|
||||||
|
npdu[len] = network_message_type;
|
||||||
|
len++;
|
||||||
|
// Message Type field contains a value in the range 0x80 - 0xFF,
|
||||||
|
// then a Vendor ID field shall be present
|
||||||
|
if (network_message_type >= 0x80)
|
||||||
|
len += encode_bacnet_unsigned16(&npdu[len], vendor_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// encode the NPDU portion of the packet for an APDU
|
||||||
|
int npdu_encode_apdu(
|
||||||
|
uint8_t *npdu,
|
||||||
|
BACNET_ADDRESS *dest,
|
||||||
|
BACNET_ADDRESS *src,
|
||||||
|
bool data_expecting_reply, // true for confirmed messages
|
||||||
|
BACNET_MESSAGE_PRIORITY priority,
|
||||||
|
uint8_t invoke_id)
|
||||||
|
{
|
||||||
|
return npdu_encode_raw(npdu,dest,src,data_expecting_reply,
|
||||||
|
false,0,0,priority,invoke_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "ctest.h"
|
||||||
|
|
||||||
|
void testNPDU(Test * pTest)
|
||||||
|
{
|
||||||
|
uint8_t pdu[480] = {0};
|
||||||
|
BACNET_ADDRESS dest = {0};
|
||||||
|
BACNET_ADDRESS src = {0};
|
||||||
|
int len = 0;
|
||||||
|
bool data_expecting_reply = false; // true for confirmed messages
|
||||||
|
BACNET_MESSAGE_PRIORITY priority = MESSAGE_PRIORITY_NORMAL;
|
||||||
|
uint8_t invoke_id = 1;
|
||||||
|
|
||||||
|
dest.mac_len = 0;
|
||||||
|
for (i = 0; i < MAX_MAC_LEN; i++)
|
||||||
|
{
|
||||||
|
dest.mac[i] = 0;
|
||||||
|
}
|
||||||
|
dest.net = 0;
|
||||||
|
dest.adr_len = 0;
|
||||||
|
for (i = 0; i < MAX_MAC_LEN; i++)
|
||||||
|
{
|
||||||
|
dest.adr[i] = 0;
|
||||||
|
}
|
||||||
|
len = ndpu_encode_apdu(
|
||||||
|
&pdu[0],
|
||||||
|
&dest,
|
||||||
|
&src,
|
||||||
|
data_expecting_reply,
|
||||||
|
priority,
|
||||||
|
invoke_id)
|
||||||
|
ct_test(pTest, len != 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST_NPDU
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
Test *pTest;
|
||||||
|
bool rc;
|
||||||
|
|
||||||
|
pTest = ct_create("BACnet NPDU", NULL);
|
||||||
|
/* individual tests */
|
||||||
|
rc = ct_addTestFunction(pTest, testNPDU);
|
||||||
|
assert(rc);
|
||||||
|
|
||||||
|
ct_setStream(pTest, stdout);
|
||||||
|
ct_run(pTest);
|
||||||
|
(void) ct_report(pTest);
|
||||||
|
ct_destroy(pTest);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif /* TEST_NPDU */
|
||||||
|
#endif /* TEST */
|
||||||
|
|||||||
Reference in New Issue
Block a user