diff --git a/bacnet-stack/bacdcode.c b/bacnet-stack/bacdcode.c index acca3ac0..c6d8222e 100644 --- a/bacnet-stack/bacdcode.c +++ b/bacnet-stack/bacdcode.c @@ -121,6 +121,37 @@ uint8_t encode_max_segs_max_apdu(int max_segs, int max_apdu) 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 // returns the number of apdu bytes consumed int encode_tag(uint8_t * apdu, uint8_t tag_number, bool context_specific, diff --git a/bacnet-stack/bacdcode.h b/bacnet-stack/bacdcode.h index 659c534d..52093588 100644 --- a/bacnet-stack/bacdcode.h +++ b/bacnet-stack/bacdcode.h @@ -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 *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 diff --git a/bacnet-stack/bacdef.h b/bacnet-stack/bacdef.h index 827809af..68938cd0 100644 --- a/bacnet-stack/bacdef.h +++ b/bacnet-stack/bacdef.h @@ -52,6 +52,8 @@ struct BACnet_Device_Address { // the following are used if the device is behind a router // net = 0 indicates local 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 */ uint8_t adr[MAX_MAC_LEN]; /* hwaddr (MAC) address */ }; diff --git a/bacnet-stack/bacenum.h b/bacnet-stack/bacenum.h index 2ad7761e..71834a4e 100644 --- a/bacnet-stack/bacenum.h +++ b/bacnet-stack/bacenum.h @@ -737,9 +737,31 @@ typedef enum { typedef enum { 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; +//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 { REINITIALIZED_STATE_COLD_START = 0, REINITIALIZED_STATE_WARM_START = 1, diff --git a/bacnet-stack/npdu.c b/bacnet-stack/npdu.c index f327728f..aa8f2305 100644 --- a/bacnet-stack/npdu.c +++ b/bacnet-stack/npdu.c @@ -36,6 +36,7 @@ #include #include "bacdef.h" #include "bacenum.h" +#include "bits.h" /* max-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; } -int npdu_encode( - uint8_t *buf, +int npdu_encode_raw( + uint8_t *npdu, BACNET_ADDRESS *dest, 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) { - 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; } + +// 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 +#include +#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 */