Added BACnetRecipient and BACnetAddressBinding codecs for EPICS application (#1163)

* Added BACnetRecipient and BACnetAddressBinding encoding, decoding, ASCII conversion, comparison, and copy functions.

*  Added library specific string manipulation utilities including strcmp, strncmp, stptok, and snprintf with offset functions.

* Added test/README about verification and validation testing.
This commit is contained in:
Steve Karg
2025-11-27 23:12:39 -06:00
committed by GitHub
parent 21626d1ac5
commit ca9836b099
28 changed files with 1875 additions and 207 deletions
+8
View File
@@ -17,6 +17,14 @@ The git repositories are hosted at the following sites:
### Security
### Added
* Added BACnetRecipient and BACnetAddressBinding codecs for EPICS application.
The implementation includes full encode/decode, ASCII conversion,
comparison, and copy functions for both data types, along with
comprehensive test coverage and supporting string utility functions.
Integrated the new types into the application data framework with
application tag assignments. (#1163)
* Added library specific string manipulation utilities including strcmp,
strncmp, and snprintf with offset functions. (#1163)
* Added default option to bactext name functions so that NULL can be
returned when a name does not exist. (#1160)
* Added library specific ltrim, rtrim, and trim string functions. (#1159)
+1
View File
@@ -14,6 +14,7 @@ SRCS = main.c \
${BACNET_SRC_DIR}/bacnet/bacint.c \
${BACNET_SRC_DIR}/bacnet/bacreal.c \
${BACNET_SRC_DIR}/bacnet/bacstr.c \
${BACNET_SRC_DIR}/bacnet/bactext.c \
${BACNET_SRC_DIR}/bacnet/iam.c \
${BACNET_SRC_DIR}/bacnet/datetime.c \
${BACNET_SRC_DIR}/bacnet/indtext.c \
+1
View File
@@ -43,6 +43,7 @@ SRCS = main.c \
${BACNET_SOURCE_DIR}/bacint.c \
${BACNET_SOURCE_DIR}/bacreal.c \
${BACNET_SOURCE_DIR}/bacstr.c \
${BACNET_SOURCE_DIR}/bactext.c \
${BACNET_SOURCE_DIR}/npdu.c \
${BACNET_SOURCE_DIR}/bacaddr.c \
${BACNET_SOURCE_DIR}/hostnport.c \
+440 -17
View File
@@ -16,10 +16,11 @@
#include "bacnet/bacdcode.h"
#include "bacnet/bacint.h"
#include "bacnet/bacstr.h"
#include "bacnet/bactext.h"
#include "bacnet/bacaddr.h"
/**
* @brief Copy a #BACNET_ADDRESS value to another
* @brief Copy a #BACNET_ADDRESS value to another, or initialize if src=NULL
* @param dest - #BACNET_ADDRESS to be copied into
* @param src - #BACNET_ADDRESS to be copied from
*/
@@ -37,6 +38,16 @@ void bacnet_address_copy(BACNET_ADDRESS *dest, const BACNET_ADDRESS *src)
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = src->adr[i];
}
} else if (dest) {
dest->mac_len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->mac[i] = 0;
}
dest->net = 0;
dest->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
dest->adr[i] = 0;
}
}
}
@@ -87,6 +98,57 @@ bool bacnet_address_same(const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src)
return true;
}
/**
* @brief Compare two BACnetAddress strictly from encoding based
* on network number.
* BACnetAddress ::= SEQUENCE {
* network-number Unsigned16,-- A value of 0 indicates the local network
* mac-address OctetString -- A string of length 0 indicates a broadcast
* }
* @param dest - BACnetAddress to be compared
* @param src - BACnetAddress to be compared
* @return true if the same values
*/
bool bacnet_address_net_same(
const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src)
{
uint8_t i = 0; /* loop counter */
if (!dest || !src) {
return false;
}
if (dest->net != src->net) {
return false;
}
if (dest->net == 0) {
/* local address stored in MAC */
if (dest->mac_len != src->mac_len) {
return false;
}
for (i = 0; i < MAX_MAC_LEN; i++) {
if (i < dest->mac_len) {
if (dest->mac[i] != src->mac[i]) {
return false;
}
}
}
} else {
/* remote address stored in ADR */
if (dest->len != src->len) {
return false;
}
for (i = 0; i < MAX_MAC_LEN; i++) {
if (i < dest->len) {
if (dest->adr[i] != src->adr[i]) {
return false;
}
}
}
}
return true;
}
/**
* @brief Configure a #BACNET_ADDRESS from mac, dnet, and adr
* @param dest - #BACNET_ADDRESS to be configured
@@ -194,6 +256,10 @@ void bacnet_address_mac_init(
/**
* @brief Parse an ASCII string for a bacnet-address
* @details Address formats:
* 192.168.1.42:47808 - for BACnet/IP
* ff:aa:ff:bb:ff:cc - for Ethernet
* fa or 127 - for ARCNET or MS/TP
* @param mac [out] BACNET_MAC_ADDRESS structure to store the results
* @param arg [in] nul terminated ASCII string to parse
* @return true if the address was parsed
@@ -226,7 +292,6 @@ bool bacnet_address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, const char *arg)
c = sscanf(
arg, "%2x:%2x:%2x:%2x:%2x:%2x", &a[0], &a[1], &a[2], &a[3], &a[4],
&a[5]);
if (c > 0) {
for (i = 0; i < c; i++) {
mac->adr[i] = a[i];
@@ -239,6 +304,65 @@ bool bacnet_address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, const char *arg)
return status;
}
/**
* @brief Parse an ASCII string for a bacnet-address
* @details Address formats: mac net addr
* 192.168.1.42:47808 - for BACnet/IP
* ff:aa:ff:bb:ff:cc - for Ethernet
* fa - for ARCNET or MS/TP
* 65535 - network number, 0 if mac but no addr
* @param src [out] BACNET_MAC_ADDRESS structure to store the results
* @param arg [in] null terminated ASCII string to parse
* @return true if the address was parsed
*/
bool bacnet_address_from_ascii(BACNET_ADDRESS *src, const char *arg)
{
bool status = false;
int count = 0;
unsigned snet = 0, i;
char mac_string[80] = { "" }, sadr_string[80] = { "" };
BACNET_MAC_ADDRESS mac = { 0 };
if (!(src && arg)) {
return false;
}
count = sscanf(
arg, "{%79[^,],%u,%79[^}]}", &mac_string[0], &snet, &sadr_string[0]);
if (count > 0) {
if (bacnet_address_mac_from_ascii(&mac, mac_string)) {
if (src) {
src->mac_len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->mac[i] = mac.adr[i];
}
}
}
if (src) {
src->net = (uint16_t)snet;
}
if (snet) {
if (bacnet_address_mac_from_ascii(&mac, sadr_string)) {
if (src) {
src->len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = mac.adr[i];
}
}
}
} else {
if (src) {
src->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = 0;
}
}
}
status = true;
}
return status;
}
/**
* @brief Decodes a BACnetAddress value from APDU buffer
* From clause 21. FORMAL DESCRIPTION OF APPLICATION PROTOCOL DATA UNITS
@@ -260,19 +384,22 @@ int bacnet_address_decode(
int len = 0;
int apdu_len = 0;
uint8_t i = 0;
BACNET_UNSIGNED_INTEGER decoded_unsigned = 0;
BACNET_UNSIGNED_INTEGER snet = 0;
BACNET_OCTET_STRING mac_addr = { 0 };
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* network number */
len = bacnet_unsigned_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &decoded_unsigned);
&apdu[apdu_len], apdu_size - apdu_len, &snet);
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (decoded_unsigned <= UINT16_MAX) {
if (snet <= UINT16_MAX) {
/* bounds checking - passed! */
if (value) {
value->net = (uint16_t)decoded_unsigned;
value->net = (uint16_t)snet;
}
} else {
return BACNET_STATUS_ERROR;
@@ -284,6 +411,19 @@ int bacnet_address_decode(
if (len <= 0) {
return BACNET_STATUS_ERROR;
}
if (snet) {
if (value) {
if (mac_addr.length > sizeof(value->adr)) {
return BACNET_STATUS_ERROR;
}
/* bounds checking - passed! */
value->len = mac_addr.length;
/* copy address */
for (i = 0; i < value->len; i++) {
value->adr[i] = mac_addr.value[i];
}
}
} else {
if (value) {
if (mac_addr.length > sizeof(value->mac)) {
return BACNET_STATUS_ERROR;
@@ -295,6 +435,7 @@ int bacnet_address_decode(
value->mac[i] = mac_addr.value[i];
}
}
}
apdu_len += len;
return apdu_len;
@@ -317,6 +458,9 @@ int bacnet_address_context_decode(
int len = 0;
int apdu_len = 0;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return BACNET_STATUS_ERROR;
@@ -346,23 +490,28 @@ int bacnet_address_context_decode(
*/
int encode_bacnet_address(uint8_t *apdu, const BACNET_ADDRESS *destination)
{
int apdu_len = 0;
BACNET_OCTET_STRING mac_addr;
int apdu_len = 0, len, adr_len, mac_len;
if (destination) {
/* network number */
apdu_len += encode_application_unsigned(apdu, destination->net);
/* encode mac address as an octet-string */
if (destination->len != 0) {
octetstring_init(&mac_addr, destination->adr, destination->len);
} else {
octetstring_init(&mac_addr, destination->mac, destination->mac_len);
}
len = encode_application_unsigned(apdu, destination->net);
apdu_len += len;
if (apdu) {
apdu += apdu_len;
apdu += len;
}
apdu_len += encode_application_octet_string(apdu, &mac_addr);
/* encode mac address as an octet-string */
if (destination->len > 0) {
adr_len = min(destination->len, sizeof(destination->adr));
len = encode_application_octet_string_buffer(
apdu, destination->adr, adr_len);
} else {
mac_len = min(destination->mac_len, sizeof(destination->mac));
len = encode_application_octet_string_buffer(
apdu, destination->mac, mac_len);
}
apdu_len += len;
}
return apdu_len;
}
@@ -500,6 +649,9 @@ int bacnet_vmac_entry_decode(
size_t i = 0;
BACNET_OCTET_STRING mac_addr = { 0 };
if (!apdu) {
return BACNET_STATUS_ERROR;
}
/* virtual-mac-address [0] OctetString */
len = bacnet_octet_string_context_decode(
&apdu[apdu_len], apdu_size - apdu_len, 0, &mac_addr);
@@ -565,3 +717,274 @@ bool bacnet_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id)
return status;
}
/**
* @brief Encode a given BACnetAddressBinding
* @details
* BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
* @param apdu - APDU buffer for storing the encoded data, or NULL for length
* @param value - BACnetAddressBinding value
* @return number of bytes in the APDU
*/
int bacnet_address_binding_type_encode(
uint8_t *apdu, const BACNET_ADDRESS_BINDING *value)
{
int apdu_len = 0, len = 0;
if (!value) {
return 0;
}
len = encode_application_object_id(
apdu, OBJECT_DEVICE, value->device_identifier);
apdu_len += len;
if (apdu) {
apdu += len;
}
len = encode_bacnet_address(apdu, &value->device_address);
apdu_len += len;
return apdu_len;
}
/**
* @brief Encode a given BACnetAddressBinding
* @details
* BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
* @param apdu - APDU buffer for storing the encoded data, or NULL for length
* @param apdu_size - size of the APDU buffer
* @param value - BACnetAddressBinding value
* @return number of bytes in the APDU, or 0 if unable to fit.
*/
int bacnet_address_binding_encode(
uint8_t *apdu, size_t apdu_size, const BACNET_ADDRESS_BINDING *value)
{
size_t apdu_len = 0; /* total length of the apdu, return value */
apdu_len = bacnet_address_binding_type_encode(NULL, value);
if (apdu_len > apdu_size) {
apdu_len = 0;
} else {
apdu_len = bacnet_address_binding_type_encode(apdu, value);
}
return apdu_len;
}
/**
* @brief Decode a given BACnetAddressBinding
* @details
* BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
* @param apdu - APDU buffer for decoding
* @param apdu_size - Count of valid bytes in the buffer
* @param value - BACnetAddressBinding value to store the decoded data
* @return number of bytes decoded or BACNET_STATUS_ERROR on error
*/
int bacnet_address_binding_decode(
const uint8_t *apdu, size_t apdu_size, BACNET_ADDRESS_BINDING *value)
{
int apdu_len = 0, len = 0;
BACNET_OBJECT_TYPE object_type;
uint32_t object_instance;
BACNET_ADDRESS *address = NULL;
if (!apdu) {
return BACNET_STATUS_ERROR;
}
len = bacnet_object_id_application_decode(
&apdu[apdu_len], apdu_size - apdu_len, &object_type, &object_instance);
if (len > 0) {
apdu_len += len;
if (object_type != OBJECT_DEVICE) {
return BACNET_STATUS_ERROR;
}
if (value) {
value->device_identifier = object_instance;
}
} else {
return BACNET_STATUS_ERROR;
}
if (value) {
address = &value->device_address;
}
len = bacnet_address_decode(&apdu[apdu_len], apdu_size - apdu_len, address);
if (len > 0) {
apdu_len += len;
} else {
return BACNET_STATUS_ERROR;
}
return apdu_len;
}
/**
* @brief Compare two BACnetAddressBinding values
* @param value1 [in] The first BACnetAddressBinding value
* @param value2 [in] The second BACnetAddressBinding value
* @return True if the values are the same, else False
*/
bool bacnet_address_binding_same(
const BACNET_ADDRESS_BINDING *value1, const BACNET_ADDRESS_BINDING *value2)
{
if (!value1) {
return false;
}
if (!value2) {
return false;
}
if (value1->device_identifier != value2->device_identifier) {
return false;
}
return bacnet_address_same(
&value1->device_address, &value2->device_address);
}
/**
* @brief Copy a BACnetAddressBinding to another
* @param value1 [in] The first BACnetAddressBinding value
* @param value2 [in] The second BACnetAddressBinding value
* @return true if the value was copied, else false
*/
bool bacnet_address_binding_copy(
BACNET_ADDRESS_BINDING *dest, const BACNET_ADDRESS_BINDING *src)
{
if (!(dest && src)) {
return false;
}
dest->device_identifier = src->device_identifier;
bacnet_address_copy(&dest->device_address, &src->device_address);
return true;
}
/**
* @brief Initialize a BACnetAddressBinding from device-id and address
* @param dest [out] The BACnetAddressBinding value
* @param device_identifier [in] The device-identifier value
* @param address [in] #BACNET_ADDRESS value
* @return true if the values were copied, else false
*/
bool bacnet_address_binding_init(
BACNET_ADDRESS_BINDING *dest,
uint32_t device_id,
const BACNET_ADDRESS *address)
{
if (!dest) {
return false;
}
dest->device_identifier = device_id;
bacnet_address_copy(&dest->device_address, address);
return true;
}
/**
* @brief Produce a string from a BACnetAddressBinding structure
* @param value [in] The BACnetAddressBinding value
* @param str [out] The string to produce, NULL to get length only
* @param str_size [in] The size of the string buffer
* @return length of the produced string
* @details Output format:
* {(device, 1234),1234,X'c0:a8:00:0f'}
*/
int bacnet_address_binding_to_ascii(
const BACNET_ADDRESS_BINDING *value, char *str, size_t str_len)
{
int offset = 0;
int i;
if (!value) {
return 0;
}
offset = bacnet_snprintf(str, str_len, offset, "{");
/* device-identifier */
offset = bacnet_snprintf(str, str_len, offset, "(");
offset = bacnet_snprintf(
str, str_len, offset, "%s, ", bactext_object_type_name(OBJECT_DEVICE));
offset = bacnet_snprintf(
str, str_len, offset, "%lu),", (unsigned long)value->device_identifier);
/* snet */
offset = bacnet_snprintf(
str, str_len, offset, "%lu,", (unsigned long)value->device_address.net);
/* octetstring */
offset = bacnet_snprintf(str, str_len, offset, "X'");
if (value->device_address.net) {
/* adr */
for (i = 0; i < value->device_address.len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->device_address.adr[i]);
}
} else {
/* mac */
for (i = 0; i < value->device_address.mac_len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->device_address.mac[i]);
}
}
offset = bacnet_snprintf(str, str_len, offset, "'");
offset = bacnet_snprintf(str, str_len, offset, "}");
return offset;
}
/**
* @brief Parse a string into a BACnetAddressBinding structure
* @param value [out] The BACnetAddressBinding value
* @param argv [in] The string to parse
* @return true on success, else false
* @details format:
* {(device, 1234),1234,X'c0:a8:00:0f'}
*/
bool bacnet_address_binding_from_ascii(
BACNET_ADDRESS_BINDING *value, const char *arg)
{
int count = 0, i = 0;
unsigned snet = 0;
char mac_string[80] = { "" }, obj_string[80] = { "" };
BACNET_MAC_ADDRESS mac = { 0 };
uint32_t object_type = 0;
unsigned long object_instance = 0;
if (!(value && arg)) {
return false;
}
count = sscanf(
arg, "{(%79[^,], %lu),%u,X'%79[^']'}", obj_string, &object_instance,
&snet, mac_string);
if (count == 4) {
if (!bactext_object_type_strtol(obj_string, &object_type)) {
return false;
}
if (object_type != OBJECT_DEVICE) {
return false;
}
if (!bacnet_address_mac_from_ascii(&mac, mac_string)) {
return false;
}
if (value) {
value->device_identifier = object_instance;
value->device_address.net = snet;
if (snet) {
value->device_address.len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
value->device_address.adr[i] = mac.adr[i];
}
} else {
value->device_address.mac_len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
value->device_address.mac[i] = mac.adr[i];
}
}
}
return true;
}
return false;
}
+43
View File
@@ -30,6 +30,17 @@ typedef struct BACnetVMACEntry {
struct BACnetVMACEntry *next;
} BACNET_VMAC_ENTRY;
/** BACnetAddressBinding ::= SEQUENCE {
* device-identifier BACnetObjectIdentifier,
* device-address BACnetAddress
*
*/
typedef struct BACnetAddressBinding {
uint32_t device_identifier;
BACNET_ADDRESS device_address;
struct BACnetAddressBinding *next;
} BACNET_ADDRESS_BINDING;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -39,6 +50,9 @@ void bacnet_address_copy(BACNET_ADDRESS *dest, const BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool bacnet_address_same(const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool bacnet_address_net_same(
const BACNET_ADDRESS *dest, const BACNET_ADDRESS *src);
BACNET_STACK_EXPORT
bool bacnet_address_init(
BACNET_ADDRESS *dest,
const BACNET_MAC_ADDRESS *mac,
@@ -53,6 +67,8 @@ void bacnet_address_mac_init(
BACNET_MAC_ADDRESS *mac, const uint8_t *adr, uint8_t len);
BACNET_STACK_EXPORT
bool bacnet_address_mac_from_ascii(BACNET_MAC_ADDRESS *mac, const char *arg);
BACNET_STACK_EXPORT
bool bacnet_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
BACNET_STACK_EXPORT
int bacnet_address_decode(
@@ -90,6 +106,33 @@ int bacnet_vmac_entry_decode(
BACNET_STACK_EXPORT
bool bacnet_vmac_address_set(BACNET_ADDRESS *addr, uint32_t device_id);
BACNET_STACK_EXPORT
int bacnet_address_binding_type_encode(
uint8_t *apdu, const BACNET_ADDRESS_BINDING *value);
BACNET_STACK_EXPORT
int bacnet_address_binding_decode(
const uint8_t *apdu, size_t adpu_size, BACNET_ADDRESS_BINDING *value);
BACNET_STACK_EXPORT
int bacnet_address_binding_encode(
uint8_t *apdu, size_t adpu_size, const BACNET_ADDRESS_BINDING *value);
BACNET_STACK_EXPORT
bool bacnet_address_binding_same(
const BACNET_ADDRESS_BINDING *value1, const BACNET_ADDRESS_BINDING *value2);
BACNET_STACK_EXPORT
bool bacnet_address_binding_copy(
BACNET_ADDRESS_BINDING *dest, const BACNET_ADDRESS_BINDING *src);
BACNET_STACK_EXPORT
bool bacnet_address_binding_init(
BACNET_ADDRESS_BINDING *dest,
uint32_t device_id,
const BACNET_ADDRESS *address);
BACNET_STACK_EXPORT
int bacnet_address_binding_to_ascii(
const BACNET_ADDRESS_BINDING *value, char *buf, size_t buf_size);
BACNET_STACK_EXPORT
bool bacnet_address_binding_from_ascii(
BACNET_ADDRESS_BINDING *value, const char *arg);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+75 -7
View File
@@ -535,6 +535,18 @@ int bacapp_encode_application_data(
apdu, &value->type.Timer_Value);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
apdu_len =
bacnet_recipient_encode(apdu, &value->type.Recipient);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
apdu_len = bacnet_address_binding_type_encode(
apdu, &value->type.Address_Binding);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
apdu_len = bacnet_timer_value_no_value_encode(apdu);
@@ -1308,14 +1320,14 @@ int bacapp_known_property_tag(
case PROP_TIME_SYNCHRONIZATION_RECIPIENTS:
case PROP_RESTART_NOTIFICATION_RECIPIENTS:
case PROP_UTC_TIME_SYNCHRONIZATION_RECIPIENTS:
/* FIXME: Properties using BACnetRecipient */
return -1;
/* Properties using BACnetRecipient */
return BACNET_APPLICATION_TAG_RECIPIENT;
case PROP_DEVICE_ADDRESS_BINDING:
case PROP_MANUAL_SLAVE_ADDRESS_BINDING:
case PROP_SLAVE_ADDRESS_BINDING:
/* FIXME: BACnetAddressBinding */
return -1;
/* BACnetAddressBinding */
return BACNET_APPLICATION_TAG_ADDRESS_BINDING;
case PROP_LOG_BUFFER:
/* BACnetLogRecord */
@@ -1684,6 +1696,18 @@ int bacapp_decode_application_tag_value(
apdu, apdu_size, &value->type.Timer_Value);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
apdu_len = bacnet_recipient_decode(
apdu, apdu_size, &value->type.Recipient);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
apdu_len = bacnet_address_binding_decode(
apdu, apdu_size, &value->type.Address_Binding);
break;
#endif
#if defined(BACAPP_LOG_RECORD)
case BACNET_APPLICATION_TAG_LOG_RECORD:
/* BACnetLogRecord */
@@ -2196,10 +2220,12 @@ int bacapp_snprintf_octet_string(
int len = 0;
int slen = 0;
int i = 0;
const uint8_t *octet_str;
slen = bacapp_snprintf(str, str_len, "X'");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
len = octetstring_length(value);
if (len > 0) {
const uint8_t *octet_str;
octet_str = octetstring_value((BACNET_OCTET_STRING *)value);
for (i = 0; i < len; i++) {
slen = bacapp_snprintf(str, str_len, "%02X", *octet_str);
@@ -2207,6 +2233,8 @@ int bacapp_snprintf_octet_string(
octet_str++;
}
}
slen = bacapp_snprintf(str, str_len, "'");
ret_val += bacapp_snprintf_shift(slen, &str, &str_len);
return ret_val;
}
@@ -4027,6 +4055,18 @@ int bacapp_snprintf_value(
&value->type.Timer_Value, str, str_len);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
ret_val = bacnet_recipient_to_ascii(
&value->type.Recipient, str, str_len);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
ret_val = bacnet_address_binding_to_ascii(
&value->type.Address_Binding, str, str_len);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
ret_val = bacnet_timer_value_no_value_to_ascii(str, str_len);
@@ -4542,8 +4582,11 @@ bool bacapp_parse_application_data(
#endif
#if defined(BACAPP_OCTET_STRING)
case BACNET_APPLICATION_TAG_OCTET_STRING:
status =
octetstring_init_ascii_hex(&value->type.Octet_String, argv);
if (!octetstring_init_ascii_epics(
&value->type.Octet_String, argv)) {
status = octetstring_init_ascii_hex(
&value->type.Octet_String, argv);
}
break;
#endif
#if defined(BACAPP_CHARACTER_STRING)
@@ -4741,6 +4784,18 @@ bool bacapp_parse_application_data(
&value->type.Timer_Value, argv);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
status =
bacnet_recipient_from_ascii(&value->type.Recipient, argv);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
status = bacnet_address_binding_from_ascii(
&value->type.Address_Binding, argv);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
status =
@@ -5495,6 +5550,19 @@ bool bacapp_same_value(
&value->type.Timer_Value, &test_value->type.Timer_Value);
break;
#endif
#if defined(BACAPP_RECIPIENT)
case BACNET_APPLICATION_TAG_RECIPIENT:
status = bacnet_recipient_same(
&value->type.Recipient, &test_value->type.Recipient);
break;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
case BACNET_APPLICATION_TAG_ADDRESS_BINDING:
status = bacnet_address_binding_same(
&value->type.Address_Binding,
&test_value->type.Address_Binding);
break;
#endif
#if defined(BACAPP_NO_VALUE)
case BACNET_APPLICATION_TAG_NO_VALUE:
if (value->tag == test_value->tag) {
+7
View File
@@ -17,6 +17,7 @@
/* BACnet Stack API */
#include "bacnet/access_rule.h"
#include "bacnet/bacaction.h"
#include "bacnet/bacaddr.h"
#include "bacnet/bacdest.h"
#include "bacnet/bacint.h"
#include "bacnet/baclog.h"
@@ -179,6 +180,12 @@ typedef struct BACnet_Application_Data_Value {
#if defined(BACAPP_TIMER_VALUE)
BACNET_TIMER_STATE_CHANGE_VALUE Timer_Value;
#endif
#if defined(BACAPP_RECIPIENT)
BACNET_RECIPIENT Recipient;
#endif
#if defined(BACAPP_ADDRESS_BINDING)
BACNET_ADDRESS_BINDING Address_Binding;
#endif
#if defined(BACAPP_LOG_RECORD)
BACNET_LOG_RECORD Log_Record;
#endif
+30
View File
@@ -2256,6 +2256,36 @@ int encode_application_octet_string(
return len;
}
/**
* @brief Encode the BACnet Octet String value as application tagged
* from clause 20.2.8 Encoding of an Octet String Value
* and 20.2.1 General Rules for Encoding BACnet Tags
* @param apdu - buffer to hold the bytes
* @param buffer - the octet string to be encoded
* @param buffer_size - the size of the octet string to be encoded
* @return returns the number of apdu bytes encoded
*/
int encode_application_octet_string_buffer(
uint8_t *apdu, const uint8_t *buffer, size_t buffer_size)
{
int apdu_len = 0, len = 0, i = 0;
len = encode_tag(
apdu, BACNET_APPLICATION_TAG_OCTET_STRING, false, buffer_size);
apdu_len += len;
if (apdu) {
apdu += len;
}
for (i = 0; i < buffer_size; i++) {
if (apdu && buffer) {
apdu[i] = buffer[i];
}
}
apdu_len += buffer_size;
return apdu_len;
}
/**
* @brief Encode the BACnet Octet String Value as context tagged
* from clause 20.2.8 Encoding of an Octet String Value
+3
View File
@@ -383,6 +383,9 @@ BACNET_STACK_EXPORT
int encode_application_octet_string(
uint8_t *apdu, const BACNET_OCTET_STRING *octet_string);
BACNET_STACK_EXPORT
int encode_application_octet_string_buffer(
uint8_t *apdu, const uint8_t *buffer, size_t buffer_size);
BACNET_STACK_EXPORT
int encode_context_octet_string(
uint8_t *apdu, uint8_t tag_number, const BACNET_OCTET_STRING *octet_string);
BACNET_STACK_EXPORT
+268 -68
View File
@@ -19,6 +19,7 @@
#include "bacnet/bacapp.h"
#include "bacnet/bacdcode.h"
#include "bacnet/bacdest.h"
#include "bacnet/bactext.h"
#include "bacnet/basic/binding/address.h"
/**
@@ -83,8 +84,8 @@ bool bacnet_recipient_same(
status = true;
}
} else if (r1->tag == BACNET_RECIPIENT_TAG_ADDRESS) {
status =
bacnet_address_same(&r1->type.address, &r2->type.address);
status = bacnet_address_net_same(
&r1->type.address, &r2->type.address);
} else {
status = false;
}
@@ -136,6 +137,20 @@ void bacnet_recipient_copy(BACNET_RECIPIENT *dest, const BACNET_RECIPIENT *src)
}
}
/**
* @brief Compare the BACnetRecipient data structure device object wildcard
* @param recipient - BACnetRecipient structure
* @return true if BACnetRecipient is equal to the device object wildcard
*/
void bacnet_recipient_device_wildcard_set(BACNET_RECIPIENT *recipient)
{
if (recipient) {
recipient->tag = BACNET_RECIPIENT_TAG_DEVICE;
recipient->type.device.type = OBJECT_DEVICE;
recipient->type.device.instance = BACNET_MAX_INSTANCE;
}
}
/**
* @brief Compare the BACnetRecipient data structure device object wildcard
* @param recipient - BACnetRecipient structure
@@ -440,6 +455,46 @@ int bacnet_destination_decode(
return apdu_len;
}
/**
* @brief Decode the BACnetDestination context tagged complex data
* @param apdu Pointer to the APDU buffer.
* @param apdu_size - the APDU buffer length
* @param tag_number The tag number that shall
* hold the time stamp.
* @param value Pointer to the variable that shall
* take the time stamp values.
* @return number of bytes decoded, zero if tag mismatch,
* or BACNET_STATUS_ERROR if an error occurs
*/
int bacnet_destination_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_number,
BACNET_DESTINATION *value)
{
int len = 0;
int apdu_len = 0;
if (!bacnet_is_opening_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return 0;
}
apdu_len += len;
len =
bacnet_destination_decode(&apdu[apdu_len], apdu_size - apdu_len, value);
if (len < 0) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
if (!bacnet_is_closing_tag_number(
&apdu[apdu_len], apdu_size - apdu_len, tag_number, &len)) {
return BACNET_STATUS_ERROR;
}
apdu_len += len;
return apdu_len;
}
/**
* @brief Encode the BACnetRecipient complex data
*
@@ -648,13 +703,11 @@ int bacnet_recipient_context_decode(
int bacnet_destination_to_ascii(
const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size)
{
int len = 0;
int buf_len = 0;
int offset = 0;
bool comma;
int i;
len = snprintf(buf, buf_size, "(");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "(");
/*
BACnetDaysOfWeek ::= BIT STRING {
monday (0),
@@ -667,39 +720,32 @@ int bacnet_destination_to_ascii(
}
*/
/* Use numbers 1-7 (ISO 8601) */
len = snprintf(buf, buf_size, "ValidDays=[");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "ValidDays=[");
comma = false;
for (i = 0; i < 7; i++) {
if (bitstring_bit(&bacdest->ValidDays, i)) {
if (comma) {
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ",");
}
len = snprintf(buf, buf_size, "%d", i + 1);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "%d", i + 1);
comma = true;
}
}
len = snprintf(buf, buf_size, "];");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "FromTime=%d:%02d:%02d.%02d;", bacdest->FromTime.hour,
bacdest->FromTime.min, bacdest->FromTime.sec,
offset = bacnet_snprintf(buf, buf_size, offset, "];");
offset = bacnet_snprintf(
buf, buf_size, offset, "FromTime=%d:%02d:%02d.%02d;",
bacdest->FromTime.hour, bacdest->FromTime.min, bacdest->FromTime.sec,
bacdest->FromTime.hundredths);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "ToTime=%d:%02d:%02d.%02d;", bacdest->ToTime.hour,
bacdest->ToTime.min, bacdest->ToTime.sec, bacdest->ToTime.hundredths);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(buf, buf_size, "Recipient=");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(
buf, buf_size, offset, "ToTime=%d:%02d:%02d.%02d;",
bacdest->ToTime.hour, bacdest->ToTime.min, bacdest->ToTime.sec,
bacdest->ToTime.hundredths);
offset = bacnet_snprintf(buf, buf_size, offset, "Recipient=");
if (bacdest->Recipient.tag == BACNET_RECIPIENT_TAG_DEVICE) {
len = snprintf(
buf, buf_size, "Device(type=%d,instance=%lu)",
offset = bacnet_snprintf(
buf, buf_size, offset, "Device(type=%d,instance=%lu)",
bacdest->Recipient.type.device.type,
(unsigned long)bacdest->Recipient.type.device.instance);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
} else {
/*
BACnetAddress ::= SEQUENCE {
@@ -708,39 +754,28 @@ int bacnet_destination_to_ascii(
a broadcast
}
*/
len = snprintf(
buf, buf_size,
offset = bacnet_snprintf(
buf, buf_size, offset,
"Address(net=%d,mac=", bacdest->Recipient.type.address.net);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
/* TODO determine if it's IPv4+port or Ethernet mac address and print it
* nicer - how? Both are 6 bytes long. */
for (i = 0; i < bacdest->Recipient.type.address.mac_len; i++) {
if (i > 0) {
len = snprintf(buf, buf_size, ":");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ":");
}
len = snprintf(
buf, buf_size, "%02x", bacdest->Recipient.type.address.mac[i]);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(
buf, buf_size, offset, "%02x",
bacdest->Recipient.type.address.mac[i]);
}
len = snprintf(buf, buf_size, ")");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ")");
}
len = snprintf(buf, buf_size, ";");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "ProcessIdentifier=%lu;",
offset = bacnet_snprintf(buf, buf_size, offset, ";");
offset = bacnet_snprintf(
buf, buf_size, offset, "ProcessIdentifier=%lu;",
(unsigned long)bacdest->ProcessIdentifier);
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
len = snprintf(
buf, buf_size, "ConfirmedNotify=%s;",
offset = bacnet_snprintf(
buf, buf_size, offset, "ConfirmedNotify=%s;",
bacdest->ConfirmedNotify ? "true" : "false");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
/*
BACnetEventTransitionBits ::= BIT STRING {
to-offnormal (0),
@@ -748,38 +783,28 @@ int bacnet_destination_to_ascii(
to-normal (2)
}
*/
len = snprintf(buf, buf_size, "Transitions=[");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "Transitions=[");
comma = false;
/* TODO remove casting when bitstring_bit() has const added - Github issue
* #320 */
if (bitstring_bit(&bacdest->Transitions, TRANSITION_TO_OFFNORMAL)) {
len = snprintf(buf, buf_size, "to-offnormal");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "to-offnormal");
comma = true;
}
if (bitstring_bit(&bacdest->Transitions, TRANSITION_TO_FAULT)) {
if (comma) {
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ",");
}
len = snprintf(buf, buf_size, "to-fault");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "to-fault");
comma = true;
}
if (bitstring_bit(&bacdest->Transitions, TRANSITION_TO_NORMAL)) {
if (comma) {
len = snprintf(buf, buf_size, ",");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, ",");
}
len = snprintf(buf, buf_size, "to-normal");
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "to-normal");
}
len = snprintf(buf, buf_size, "])"); /* end of the outer paren */
buf_len += bacapp_snprintf_shift(len, &buf, &buf_size);
offset = bacnet_snprintf(buf, buf_size, offset, "])");
return buf_len;
return offset;
}
/**
@@ -1165,3 +1190,178 @@ parse_end:
return true;
}
/**
* @brief Parse an ASCII string for a BACnetRecipient address
* @details Address format: {X'c0:a8:00:0f',1234,X'c0:a8:00:0f'}
* @param src [out] BACNET_MAC_ADDRESS structure to store the results
* @param arg [in] null terminated ASCII string to parse
* @return true if the address was parsed
*/
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg)
{
bool status = false;
int count = 0;
unsigned snet = 0, i;
char mac_string[80] = { "" }, sadr_string[80] = { "" };
BACNET_MAC_ADDRESS mac = { 0 };
if (!(src && arg)) {
return false;
}
count = sscanf(
arg, "{X'%79[^']',%u,X'%79[^']'}", &mac_string[0], &snet,
&sadr_string[0]);
if (count > 0) {
if (bacnet_address_mac_from_ascii(&mac, mac_string)) {
if (src) {
src->mac_len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->mac[i] = mac.adr[i];
}
}
}
if (src) {
src->net = (uint16_t)snet;
}
if (snet) {
if (bacnet_address_mac_from_ascii(&mac, sadr_string)) {
if (src) {
src->len = mac.len;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = mac.adr[i];
}
}
}
} else {
if (src) {
src->len = 0;
for (i = 0; i < MAX_MAC_LEN; i++) {
src->adr[i] = 0;
}
}
}
status = true;
}
return status;
}
/**
* Parse BACnet_Recipient from ASCII string (as entered by user)
* @param value - struct to store data parsed from the ASCII
* @param str - ASCII string, zero terminated
* @return true on success
* @note
* BACnetRecipient ::= CHOICE {
* device [0] BACnetObjectIdentifier,
* address [1] BACnetAddress
* }
*/
bool bacnet_recipient_from_ascii(BACNET_RECIPIENT *value_out, const char *str)
{
BACNET_RECIPIENT value = { 0 };
char object_string[80] = "";
unsigned long object_instance = 0;
uint32_t found_index = 0;
int count;
if (!str) {
return false;
}
if (str[0] == 0) {
return false;
}
if (str[0] == '(') {
/* (device, 1234) */
count = sscanf(str, "(%79[^,], %lu)", object_string, &object_instance);
if (count == 2) {
if (bactext_object_type_strtol(object_string, &found_index)) {
value.tag = BACNET_RECIPIENT_TAG_DEVICE;
value.type.device.type = (BACNET_OBJECT_TYPE)found_index;
value.type.device.instance = object_instance;
} else {
return false;
}
} else {
return false;
}
} else if (str[0] == '{') {
value.tag = BACNET_RECIPIENT_TAG_ADDRESS;
/* {X'c0:a8:00:0f',1234,X'c0:a8:00:0f'} */
if (!bacnet_recipient_address_from_ascii(&value.type.address, str)) {
return false;
}
}
if (value_out) {
bacnet_recipient_copy(value_out, &value);
}
return true;
}
/**
* @brief Convert BACnetRecipient to ASCII for printing
* @note
* BACnetRecipient ::= CHOICE {
* device [0] BACnetObjectIdentifier,
* address [1] BACnetAddress
* }
* Output format:
* (device, 1234)
* {X'c0:a8:00:0f',1234,X'c0:a8:00:0f'}
* @param value - struct to convert to ASCII
* @param buf - ASCII output buffer
* @param buf_size - ASCII output buffer capacity
* @return the number of characters which would be generated for the given
* input, excluding the trailing null.
* @note buf and buf_size may be null and zero to return only the size
*/
int bacnet_recipient_to_ascii(
const BACNET_RECIPIENT *value, char *str, size_t str_len)
{
int offset = 0;
int i;
if (!value) {
return 0;
}
if (value->tag == BACNET_RECIPIENT_TAG_DEVICE) {
/* device-identifier */
offset = bacnet_snprintf(str, str_len, offset, "(");
offset = bacnet_snprintf(
str, str_len, offset, "%s, ",
bactext_object_type_name(value->type.device.type));
offset = bacnet_snprintf(
str, str_len, offset, "%lu)",
(unsigned long)value->type.device.instance);
} else {
offset = bacnet_snprintf(str, str_len, offset, "{");
/* MAC */
offset = bacnet_snprintf(str, str_len, offset, "X'");
for (i = 0; i < value->type.address.mac_len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->type.address.mac[i]);
}
offset = bacnet_snprintf(str, str_len, offset, "',");
/* snet */
offset = bacnet_snprintf(
str, str_len, offset, "%lu",
(unsigned long)value->type.address.net);
/* octetstring */
if (value->type.address.net) {
/* adr */
offset = bacnet_snprintf(str, str_len, offset, ",X'");
for (i = 0; i < value->type.address.len; i++) {
offset = bacnet_snprintf(
str, str_len, offset, "%02X",
(unsigned)value->type.address.adr[i]);
}
offset = bacnet_snprintf(str, str_len, offset, "'");
}
offset = bacnet_snprintf(str, str_len, offset, "}");
}
return offset;
}
+17
View File
@@ -71,6 +71,12 @@ BACNET_STACK_EXPORT
int bacnet_destination_decode(
const uint8_t *apdu, int apdu_len, BACNET_DESTINATION *destination);
BACNET_STACK_EXPORT
int bacnet_destination_context_decode(
const uint8_t *apdu,
uint32_t apdu_size,
uint8_t tag_number,
BACNET_DESTINATION *value);
BACNET_STACK_EXPORT
void bacnet_destination_default_init(BACNET_DESTINATION *destination);
BACNET_STACK_EXPORT
bool bacnet_destination_default(const BACNET_DESTINATION *destination);
@@ -92,6 +98,9 @@ void bacnet_recipient_copy(BACNET_RECIPIENT *dest, const BACNET_RECIPIENT *src);
BACNET_STACK_EXPORT
bool bacnet_recipient_same(
const BACNET_RECIPIENT *r1, const BACNET_RECIPIENT *r2);
BACNET_STACK_EXPORT
void bacnet_recipient_device_wildcard_set(BACNET_RECIPIENT *recipient);
BACNET_STACK_EXPORT
bool bacnet_recipient_device_wildcard(const BACNET_RECIPIENT *recipient);
BACNET_STACK_EXPORT
@@ -112,6 +121,14 @@ int bacnet_recipient_context_decode(
uint8_t tag_number,
BACNET_RECIPIENT *value);
BACNET_STACK_EXPORT
bool bacnet_recipient_address_from_ascii(BACNET_ADDRESS *src, const char *arg);
BACNET_STACK_EXPORT
bool bacnet_recipient_from_ascii(BACNET_RECIPIENT *value_out, const char *str);
BACNET_STACK_EXPORT
int bacnet_recipient_to_ascii(
const BACNET_RECIPIENT *value, char *buf, size_t buf_size);
BACNET_STACK_EXPORT
int bacnet_destination_to_ascii(
const BACNET_DESTINATION *bacdest, char *buf, size_t buf_size);
+2
View File
@@ -1691,6 +1691,8 @@ typedef enum {
BACNET_APPLICATION_TAG_PROPERTY_VALUE,
/* BACnetTimerStateChangeValue */
BACNET_APPLICATION_TAG_TIMER_VALUE,
/* BACnetAddressBinding */
BACNET_APPLICATION_TAG_ADDRESS_BINDING,
/* no-value - context tagged null */
BACNET_APPLICATION_TAG_NO_VALUE,
/* ABSTRACT-SYNTAX - constructed value */
+171 -37
View File
@@ -1087,6 +1087,32 @@ bool octetstring_init_ascii_hex(
return status;
}
/**
* @brief Converts an null terminated ASCII Hex EPICS formatted string
* to an octet string.
* @details format: X'c0:a8:00:0f'
* @param octet_string Pointer to the octet string.
* @param arg Pointer to the HEX-ASCII EPICS format string
* @return true if successfully converted and fits; false if too long
*/
bool octetstring_init_ascii_epics(
BACNET_OCTET_STRING *octet_string, const char *arg)
{
bool status = false; /* return value */
if (!octet_string) {
return false;
}
if (!arg) {
return false;
}
if (bacnet_strnicmp(arg, "X'", 2) == 0) {
status = octetstring_init_ascii_hex(octet_string, arg + 2);
}
return status;
}
/**
* Copy an octet string from source to destination.
*
@@ -1273,14 +1299,8 @@ bool octetstring_value_same(
}
#endif
/**
* @brief Compare two strings, case insensitive
* @param a - first string
* @param b - second string
* @return 0 if the strings are equal, non-zero if not
* @note The stricmp() function is not included in the C standard.
*/
int bacnet_stricmp(const char *a, const char *b)
static int bacnet_strnicmp_internal(
const char *a, const char *b, size_t length, bool case_insensitive)
{
int twin_a, twin_b;
@@ -1290,18 +1310,66 @@ int bacnet_stricmp(const char *a, const char *b)
if (b == NULL) {
return 1;
}
if (length == 0) {
length = strlen(a);
}
do {
twin_a = *(const unsigned char *)a;
twin_b = *(const unsigned char *)b;
if (case_insensitive) {
twin_a = tolower(toupper(twin_a));
twin_b = tolower(toupper(twin_b));
}
a++;
b++;
} while ((twin_a == twin_b) && (twin_a != '\0'));
length--;
} while ((twin_a == twin_b) && (twin_a != '\0') && (length > 0));
return twin_a - twin_b;
}
/**
* @brief Compare two strings, case sensitive
* @param a - first string
* @param b - second string
* @return 0 if the strings are equal, non-zero if not
*/
int bacnet_strcmp(const char *a, const char *b)
{
return bacnet_strnicmp_internal(a, b, 0, false);
}
/**
* @brief Compare two strings, case insensitive
* @param a - first string
* @param b - second string
* @return 0 if the strings are equal, non-zero if not
* @note The stricmp() function is not included in the C standard.
*/
int bacnet_stricmp(const char *a, const char *b)
{
return bacnet_strnicmp_internal(a, b, 0, true);
}
/**
* @brief Compare two strings, case sensitive, with length limit
* @details The strncmp() function compares, at most, the first n characters
* of string1 and string2 with sensitivity to case.
*
* The function operates on null terminated strings.
* The string arguments to the function are expected to contain
* a null character (\0) marking the end of the string.
*
* @param a - first string
* @param b - second string
* @param length - maximum length to compare
* @return 0 if the strings are equal, non-zero if not
*/
int bacnet_strncmp(const char *a, const char *b, size_t length)
{
return bacnet_strnicmp_internal(a, b, length, false);
}
/**
* @brief Compare two strings, case insensitive, with length limit
* @details The strnicmp() function compares, at most, the first n characters
@@ -1319,28 +1387,7 @@ int bacnet_stricmp(const char *a, const char *b)
*/
int bacnet_strnicmp(const char *a, const char *b, size_t length)
{
int twin_a, twin_b;
if (length == 0) {
return 0;
}
if (a == NULL) {
return -1;
}
if (b == NULL) {
return 1;
}
do {
twin_a = *(const unsigned char *)a;
twin_b = *(const unsigned char *)b;
twin_a = tolower(toupper(twin_a));
twin_b = tolower(toupper(twin_b));
a++;
b++;
length--;
} while ((twin_a == twin_b) && (twin_a != '\0') && (length > 0));
return twin_a - twin_b;
return bacnet_strnicmp_internal(a, b, length, true);
}
/**
@@ -1784,7 +1831,7 @@ bool bacnet_string_to_unsigned(
* @return character string buffer
*/
char *
bacnet_sprintf_to_ascii(char *buffer, size_t size, const char *format, ...)
bacnet_snprintf_to_ascii(char *buffer, size_t size, const char *format, ...)
{
va_list args;
@@ -1807,7 +1854,7 @@ bacnet_sprintf_to_ascii(char *buffer, size_t size, const char *format, ...)
*/
char *bacnet_dtoa(double value, char *buffer, size_t size, unsigned precision)
{
return bacnet_sprintf_to_ascii(buffer, size, "%.*f", precision, value);
return bacnet_snprintf_to_ascii(buffer, size, "%.*f", precision, value);
}
/**
@@ -1819,7 +1866,7 @@ char *bacnet_dtoa(double value, char *buffer, size_t size, unsigned precision)
*/
char *bacnet_itoa(int value, char *buffer, size_t size)
{
return bacnet_sprintf_to_ascii(buffer, size, "%d", value);
return bacnet_snprintf_to_ascii(buffer, size, "%d", value);
}
/**
@@ -1831,7 +1878,7 @@ char *bacnet_itoa(int value, char *buffer, size_t size)
*/
char *bacnet_ltoa(long value, char *buffer, size_t size)
{
return bacnet_sprintf_to_ascii(buffer, size, "%ld", value);
return bacnet_snprintf_to_ascii(buffer, size, "%ld", value);
}
/**
@@ -1843,7 +1890,7 @@ char *bacnet_ltoa(long value, char *buffer, size_t size)
*/
char *bacnet_utoa(unsigned value, char *buffer, size_t size)
{
return bacnet_sprintf_to_ascii(buffer, size, "%u", value);
return bacnet_snprintf_to_ascii(buffer, size, "%u", value);
}
/**
@@ -1855,7 +1902,7 @@ char *bacnet_utoa(unsigned value, char *buffer, size_t size)
*/
char *bacnet_ultoa(unsigned long value, char *buffer, size_t size)
{
return bacnet_sprintf_to_ascii(buffer, size, "%lu", value);
return bacnet_snprintf_to_ascii(buffer, size, "%lu", value);
}
/**
@@ -1909,3 +1956,90 @@ char *bacnet_trim(char *str, const char *trimmedchars)
{
return bacnet_ltrim(bacnet_rtrim(str, trimmedchars), trimmedchars);
}
/**
* @brief Parse a token from a string.
* @details From a string, a buffer to receive the "token" that gets scanned,
* the length of the buffer, and a string of "break" characters that stop
* the scan. The function will copy the string into the buffer up to any
* of the break characters, or until the buffer is full, and will always
* leave the buffer null-terminated. The function will return a pointer
* to the first non-breaking character after the one that stopped the scan.
* @param s string to parse
* @param tok buffer that receives the "token" that gets scanned
* @param toklen length of the buffer
* @param brk string of break characters that will stop the scan
* @return a pointer to the first non-breaking character after the one that
* stopped the scan or NULL on error or end of string.
* @note public domain by Ray Gardner, modified by Bob Stout and Steve Karg
*/
char *bacnet_stptok(const char *s, char *tok, size_t toklen, const char *brk)
{
char *lim; /* limit of token */
const char *b; /* current break character */
/* check for invalid pointers */
if (!s || !tok || !brk) {
return NULL;
}
/* check for empty string */
if (!*s) {
return NULL;
}
lim = tok + toklen - 1;
while (*s && tok < lim) {
for (b = brk; *b; b++) {
if (*s == *b) {
*tok = 0;
for (++s, b = brk; *s && *b; ++b) {
if (*s == *b) {
++s;
b = brk;
}
}
if (!*s) {
return NULL;
}
return (char *)s;
}
}
*tok++ = *s++;
}
*tok = 0;
if (!*s) {
return NULL;
}
return (char *)s;
}
/**
* @brief General purpose print formatter snprintf() function with offset
* @param buffer - destination string
* @param size - length of the destination string
* @param offset - offset into the buffer to start the string
* @param format - format string
* @return total number of characters written from the beginning of buffer
*/
int bacnet_snprintf(
char *buffer, size_t size, int offset, const char *format, ...)
{
int length = 0;
va_list args;
if (offset < size) {
size -= offset;
if (buffer) {
buffer += offset;
}
va_start(args, format);
length = vsnprintf(buffer, size, format, args);
va_end(args);
return offset + length;
}
return size;
}
+14 -1
View File
@@ -144,6 +144,9 @@ BACNET_STACK_EXPORT
bool octetstring_init_ascii_hex(
BACNET_OCTET_STRING *octet_string, const char *ascii_hex);
BACNET_STACK_EXPORT
bool octetstring_init_ascii_epics(
BACNET_OCTET_STRING *octet_string, const char *arg);
BACNET_STACK_EXPORT
bool octetstring_copy(
BACNET_OCTET_STRING *dest, const BACNET_OCTET_STRING *src);
BACNET_STACK_EXPORT
@@ -172,9 +175,13 @@ bool octetstring_value_same(
const BACNET_OCTET_STRING *octet_string1,
const BACNET_OCTET_STRING *octet_string2);
BACNET_STACK_EXPORT
int bacnet_strcmp(const char *a, const char *b);
BACNET_STACK_EXPORT
int bacnet_stricmp(const char *a, const char *b);
BACNET_STACK_EXPORT
int bacnet_strncmp(const char *a, const char *b, size_t length);
BACNET_STACK_EXPORT
int bacnet_strnicmp(const char *a, const char *b, size_t length);
BACNET_STACK_EXPORT
@@ -217,7 +224,10 @@ BACNET_STACK_EXPORT
char *bacnet_ultoa(unsigned long value, char *buffer, size_t size);
BACNET_STACK_EXPORT
char *
bacnet_sprintf_to_ascii(char *buffer, size_t count, const char *format, ...);
bacnet_snprintf_to_ascii(char *buffer, size_t count, const char *format, ...);
BACNET_STACK_EXPORT
int bacnet_snprintf(
char *buffer, size_t count, int offset, const char *format, ...);
BACNET_STACK_EXPORT
char *bacnet_ltrim(char *str, const char *trimmedchars);
@@ -226,6 +236,9 @@ char *bacnet_rtrim(char *str, const char *trimmedchars);
BACNET_STACK_EXPORT
char *bacnet_trim(char *str, const char *trimmedchars);
BACNET_STACK_EXPORT
char *bacnet_stptok(const char *s, char *tok, size_t toklen, const char *brk);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+6
View File
@@ -226,6 +226,8 @@
defined(BACAPP_ACCESS_RULE) || \
defined(BACAPP_CHANNEL_VALUE) || \
defined(BACAPP_TIMER_VALUE) || \
defined(BACAPP_RECIPIENT) || \
defined(BACAPP_ADDRESS_BINDING) || \
defined(BACAPP_NO_VALUE) || \
defined(BACAPP_LOG_RECORD) || \
defined(BACAPP_SECURE_CONNECT) || \
@@ -278,6 +280,8 @@
#define BACAPP_ACCESS_RULE
#define BACAPP_CHANNEL_VALUE
#define BACAPP_TIMER_VALUE
#define BACAPP_RECIPIENT
#define BACAPP_ADDRESS_BINDING
#define BACAPP_NO_VALUE
#define BACAPP_LOG_RECORD
#define BACAPP_SECURE_CONNECT
@@ -307,6 +311,8 @@
defined(BACAPP_ACCESS_RULE) || \
defined(BACAPP_CHANNEL_VALUE) || \
defined(BACAPP_TIMER_VALUE) || \
defined(BACAPP_RECIPIENT) || \
defined(BACAPP_ADDRESS_BINDING) || \
defined(BACAPP_NO_VALUE) || \
defined(BACAPP_LOG_RECORD)
#define BACAPP_COMPLEX_TYPES
+7
View File
@@ -46,6 +46,13 @@ if (CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_C_COMPILER_ID MATCHES "GNU")
add_compile_options(-Wno-write-strings)
add_compile_options(-Wno-implicit-fallthrough)
# some consistent defines used for all builds - avoids inconsistent error
add_compile_definitions(
BIG_ENDIAN=0
PRINT_ENABLED=1
BACAPP_PRINT_ENABLED=1
)
add_link_options(-fprofile-arcs -ftest-coverage)
endif()
+47
View File
@@ -0,0 +1,47 @@
# BACnet Stack Verification and Validation Testing
CMake and C99 are used for the verification and validation testing.
## Testing in the Github workflow pipeline with Makefile
The Makefile 'test' recipe is used for the CMake recipe used in the pipeline.
The 'retest' can be used to re-build the test.
Changing to the test/build folder and running 'make test' can show
more details about a failing build or failing test. Running the actual
test by running ./test/build/bacnet/.../test_xyz has more detailed
results of running the test.
## Testing with VS Code with CMake extension and C/C++ build tools
* Open the test/ folder as a workspace.
* The folder structure follows the src/bacnet folder structure.
* Configure the CMake extension to use the C/C++ build tools kit.
* Configure the CMake extension to use the Debug build type.
* Build each or 'all' project using the CMake extension.
* Each unit or verification test can be build individually.
* The LCOV Cmake recipe will create Line-of-Code Coverage that
can be viewed in a browser starting at test/build/lcoverage/index.html
* The CTest extension can also be used to run the tests.
## Validation and Unit testing with ctest or ztest
* The ctest suite is used from cmake managing the tests. Add new test
files following the src/bacnet folder structure into the CMakeLists.txt
file. Copy and existing test folder at the same folder depth to start.
* The ztest framework (from Zephyr) has been ported to use in the tests
and includes the fff mocking macros. The zassert_x() macros include a
formatted print as the last argument so that print style debug can be
used to show values that fail which aids in writing a test.
* Tests can also be written without the zassert_x() macros by including
assert.h and using the assert() macro.
## Use pre-commit tools for changes and additions
* This library uses 'pre-commit' tools to format the files.
* Run pre-commit after staging a file and before committing
to avoid pipeline build failures.
* If a file fails pre-commit checking, the file will be re-formated
correctly by the pre-commit tool. Add the fixed file to the staged
files and run pre-commit again.
+3 -1
View File
@@ -37,9 +37,11 @@ add_executable(${PROJECT_NAME}
# Support files and stubs (pathname alphabetical)
${SRC_DIR}/bacnet/bacaction.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/indtext.c
# Test and test library files
./src/main.c
${ZTST_DIR}/ztest_mock.c
+211 -2
View File
@@ -31,6 +31,10 @@ static void test_BACNET_ADDRESS(void)
zassert_false(status, NULL);
status = bacnet_address_same(NULL, NULL);
zassert_false(status, NULL);
bacnet_address_copy(&dest, NULL);
bacnet_address_copy(&src, NULL);
status = bacnet_address_same(&dest, &src);
zassert_true(status, NULL);
/* happy day cases */
status = bacnet_address_same(&dest, &dest);
@@ -44,6 +48,42 @@ static void test_BACNET_ADDRESS(void)
zassert_true(status, NULL);
status = bacnet_address_same(&dest, &src);
zassert_true(status, NULL);
mac.len = 1;
mac.adr[0] = 3;
adr.len = 1;
adr.adr[0] = 4;
dnet = 1234;
status = bacnet_address_init(&src, &mac, dnet, &adr);
zassert_true(status, NULL);
bacnet_address_copy(&dest, &src);
zassert_true(status, NULL);
status = bacnet_address_net_same(&dest, &src);
zassert_true(status, NULL);
dnet = 0;
status = bacnet_address_init(&src, &mac, dnet, &adr);
zassert_true(status, NULL);
bacnet_address_copy(&dest, &src);
zassert_true(status, NULL);
status = bacnet_address_net_same(&dest, &src);
zassert_true(status, NULL);
/* net + adr */
dnet = 1;
status = bacnet_address_init(&src, NULL, dnet, &adr);
status = bacnet_address_net_same(&dest, &src);
zassert_false(status, NULL);
/* net=0, mac */
dnet = 1;
status = bacnet_address_init(&dest, NULL, dnet, &adr);
zassert_true(status, NULL);
dnet = 0;
status = bacnet_address_init(&src, &mac, dnet, NULL);
zassert_true(status, NULL);
status = bacnet_address_net_same(&dest, &src);
zassert_false(status, NULL);
status = bacnet_address_net_same(NULL, &src);
zassert_false(status, NULL);
status = bacnet_address_net_same(&dest, NULL);
zassert_false(status, NULL);
/* remote dnet is non-zero */
dnet = 1;
status = bacnet_address_init(&dest, &mac, dnet, &adr);
@@ -105,6 +145,11 @@ static void test_BACNET_ADDRESS(void)
zassert_true(status, NULL);
status = bacnet_address_same(&dest, &src);
zassert_true(status, NULL);
/* initialize using copy */
bacnet_address_copy(&dest, NULL);
zassert_equal(dest.mac_len, 0, NULL);
zassert_equal(dest.len, 0, NULL);
zassert_equal(dest.net, 0, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
@@ -215,8 +260,36 @@ static void test_BACnetAddress_Codec(void)
zassert_equal(value.net, test_value.net, NULL);
zassert_equal(value.mac_len, test_value.mac_len, NULL);
zassert_equal(value.mac_len, 6, NULL);
/* test deprecated function */
test_len = decode_bacnet_address(apdu, &test_value);
zassert_equal(len, test_len, NULL);
zassert_equal(value.net, test_value.net, NULL);
zassert_equal(value.mac_len, test_value.mac_len, NULL);
zassert_equal(value.mac_len, 6, NULL);
/* negative tests - NULL value */
test_len = bacnet_address_decode(apdu, sizeof(apdu), NULL);
zassert_equal(len, test_len, NULL);
/* network address */
value.net = 1;
value.len = 3;
value.adr[0] = 1;
value.adr[1] = 2;
value.adr[2] = 3;
len = encode_bacnet_address(NULL, &value);
test_len = encode_bacnet_address(apdu, &value);
zassert_true(len > 0, NULL);
zassert_true(test_len > 0, NULL);
zassert_equal(len, test_len, "len=%d test_len=%d", len, test_len);
test_len = bacnet_address_decode(apdu, sizeof(apdu), &test_value);
zassert_equal(len, test_len, NULL);
zassert_equal(value.net, test_value.net, NULL);
zassert_equal(value.mac_len, test_value.mac_len, NULL);
zassert_equal(value.len, test_value.len, NULL);
zassert_equal(value.len, 3, NULL);
zassert_equal(value.adr[0], test_value.adr[0], NULL);
zassert_equal(value.adr[1], test_value.adr[1], NULL);
zassert_equal(value.adr[2], test_value.adr[2], NULL);
/* context tagged */
tag_number = 1;
len = encode_context_bacnet_address(NULL, tag_number, &value);
test_len = encode_context_bacnet_address(apdu, tag_number, &value);
@@ -229,6 +302,12 @@ static void test_BACnetAddress_Codec(void)
zassert_equal(value.net, test_value.net, NULL);
zassert_equal(value.mac_len, test_value.mac_len, NULL);
zassert_equal(value.mac_len, 6, NULL);
/* validate deprecated function */
test_len = decode_context_bacnet_address(apdu, tag_number, &test_value);
zassert_equal(len, test_len, NULL);
zassert_equal(value.net, test_value.net, NULL);
zassert_equal(value.mac_len, test_value.mac_len, NULL);
zassert_equal(value.mac_len, 6, NULL);
test_len = bacnet_address_context_decode(
apdu, sizeof(apdu), tag_number, &test_value);
/* negative tests - NULL value */
@@ -256,9 +335,18 @@ static void test_bacnet_vmac_entry_codec(void)
{
uint8_t apdu[MAX_APDU];
BACNET_VMAC_ENTRY value = { 0 }, test_value = { 0 };
BACNET_ADDRESS test_addr = { 0 };
int test_len = 0, apdu_len = 0, null_len = 0;
bool status = false;
unsigned i;
status = bacnet_vmac_address_set(NULL, 0);
zassert_false(status, NULL);
status = bacnet_vmac_address_set(&test_addr, 12345);
zassert_true(status, NULL);
zassert_equal(test_addr.mac_len, 3, NULL);
zassert_equal(test_addr.net, 0, NULL);
/* VMAC */
value.virtual_mac_address.adr[0] = 1;
value.virtual_mac_address.adr[1] = 2;
value.virtual_mac_address.adr[2] = 3;
@@ -268,6 +356,8 @@ static void test_bacnet_vmac_entry_codec(void)
value.native_mac_address[2] = 6;
value.native_mac_address[3] = 7;
value.native_mac_address_len = 4;
null_len = bacnet_vmac_entry_encode(NULL, sizeof(apdu), NULL);
zassert_equal(null_len, 0, NULL);
null_len = bacnet_vmac_entry_encode(NULL, sizeof(apdu), &value);
apdu_len = bacnet_vmac_entry_encode(apdu, sizeof(apdu), &value);
zassert_true(apdu_len > 0, NULL);
@@ -302,6 +392,124 @@ static void test_bacnet_vmac_entry_codec(void)
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacnet_address_tests, test_bacnet_address_ascii)
#else
static void test_bacnet_address_ascii(void)
#endif
{
char ascii_mac_net_adr[80] = "{ff:00:ff:01:ff:02,1,7f}";
char ascii_mac_net[80] = "{192.168.1.1:47808,0}";
BACNET_ADDRESS value = { 0 };
bool status;
status = bacnet_address_from_ascii(NULL, NULL);
zassert_false(status, NULL);
status = bacnet_address_from_ascii(&value, NULL);
zassert_false(status, NULL);
status = bacnet_address_from_ascii(NULL, ascii_mac_net_adr);
zassert_false(status, NULL);
status = bacnet_address_from_ascii(&value, ascii_mac_net_adr);
zassert_true(status, NULL);
zassert_equal(value.mac_len, 6, NULL);
zassert_equal(value.mac[0], 0xff, NULL);
zassert_equal(value.mac[1], 0x00, NULL);
zassert_equal(value.mac[2], 0xff, NULL);
zassert_equal(value.mac[3], 0x01, NULL);
zassert_equal(value.mac[4], 0xff, NULL);
zassert_equal(value.mac[5], 0x02, NULL);
zassert_equal(value.net, 1, NULL);
zassert_equal(value.len, 1, NULL);
zassert_equal(value.adr[0], 0x7f, NULL);
status = bacnet_address_from_ascii(&value, ascii_mac_net);
zassert_true(status, NULL);
zassert_equal(value.mac_len, 6, NULL);
zassert_equal(value.mac[0], 192, NULL);
zassert_equal(value.mac[1], 168, NULL);
zassert_equal(value.mac[2], 1, NULL);
zassert_equal(value.mac[3], 1, NULL);
zassert_equal(value.net, 0, NULL);
zassert_equal(value.len, 0, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacnet_address_tests, test_BACNET_ADDRESS_BINDING)
#else
static void test_BACNET_ADDRESS_BINDING(void)
#endif
{
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_ADDRESS addr = {
.mac_len = 1, .mac[0] = 0x01, .net = 0, .adr[0] = 0, .len = 0
};
BACNET_ADDRESS_BINDING binding = { 0 }, test_binding = { 0 };
bool status = false;
int len, test_len, apdu_len, null_len;
char str[80] = "";
null_len = bacnet_address_binding_type_encode(NULL, NULL);
zassert_equal(null_len, 0, NULL);
status = bacnet_address_binding_init(&binding, 12345, &addr);
len = bacnet_address_binding_type_encode(NULL, &binding);
null_len = bacnet_address_binding_type_encode(NULL, &binding);
zassert_not_equal(null_len, 0, NULL);
apdu_len = bacnet_address_binding_type_encode(apdu, &binding);
zassert_not_equal(null_len, 0, NULL);
zassert_equal(null_len, apdu_len, NULL);
test_len = bacnet_address_binding_decode(apdu, apdu_len, &test_binding);
zassert_equal(apdu_len, test_len, NULL);
status = bacnet_address_same(
&binding.device_address, &test_binding.device_address);
zassert_true(status, NULL);
status = bacnet_address_binding_same(&binding, &test_binding);
zassert_true(status, NULL);
null_len = bacnet_address_binding_encode(NULL, 0, &binding);
zassert_equal(null_len, 0, NULL);
null_len = bacnet_address_binding_encode(NULL, 0, NULL);
zassert_equal(null_len, 0, NULL);
null_len = bacnet_address_binding_encode(NULL, sizeof(apdu), &binding);
zassert_not_equal(null_len, 0, NULL);
apdu_len = bacnet_address_binding_encode(apdu, sizeof(apdu), &binding);
zassert_equal(null_len, apdu_len, NULL);
/* shrink the apdu size for decoding test */
while (apdu_len) {
apdu_len--;
test_len = bacnet_address_binding_decode(apdu, apdu_len, &test_binding);
zassert_equal(test_len, BACNET_STATUS_ERROR, NULL);
}
/* negative tests - NULL value */
apdu_len = bacnet_address_binding_encode(apdu, sizeof(apdu), &binding);
zassert_equal(null_len, apdu_len, NULL);
test_len = bacnet_address_binding_decode(apdu, sizeof(apdu), NULL);
zassert_equal(apdu_len, test_len, NULL);
test_len = bacnet_address_binding_decode(NULL, 0, NULL);
zassert_equal(test_len, BACNET_STATUS_ERROR, NULL);
status = bacnet_address_binding_copy(&test_binding, &binding);
zassert_true(status, NULL);
status = bacnet_address_binding_same(&test_binding, &binding);
zassert_true(status, NULL);
/* ASCII */
len = bacnet_address_binding_to_ascii(&binding, str, sizeof(str));
zassert_true(len > 0, NULL);
status = bacnet_address_binding_from_ascii(&test_binding, str);
zassert_true(status, NULL);
status = bacnet_address_binding_same(&test_binding, &binding);
zassert_true(status, NULL);
/* negative tests - NULL value */
status = bacnet_address_binding_same(NULL, &binding);
zassert_false(status, NULL);
status = bacnet_address_binding_same(&test_binding, NULL);
zassert_false(status, NULL);
status = bacnet_address_binding_copy(NULL, &binding);
zassert_false(status, NULL);
status = bacnet_address_binding_copy(&test_binding, NULL);
zassert_false(status, NULL);
status = bacnet_address_binding_init(NULL, 0, &addr);
zassert_false(status, NULL);
len = bacnet_address_binding_to_ascii(NULL, str, sizeof(str));
zassert_equal(len, 0, NULL);
}
/**
* @}
*/
@@ -314,8 +522,9 @@ void test_main(void)
bacnet_address_tests, ztest_unit_test(test_BACNET_ADDRESS),
ztest_unit_test(test_BACNET_MAC_ADDRESS),
ztest_unit_test(test_BACnetAddress_Codec),
ztest_unit_test(test_bacnet_vmac_entry_codec));
ztest_unit_test(test_bacnet_vmac_entry_codec),
ztest_unit_test(test_bacnet_address_ascii),
ztest_unit_test(test_BACNET_ADDRESS_BINDING));
ztest_run_test_suite(bacnet_address_tests);
}
#endif
+11 -7
View File
@@ -1334,7 +1334,8 @@ void test_bacapp_snprintf(
/* normal case */
len = bacapp_snprintf_value(str, str_len + 1, &object_value);
zassert_mem_equal(
str, expected, str_len, "str='%s' expected='%s'", str, expected);
str, expected, str_len, "%s: str='%s' expected='%s'",
bactext_application_tag_name(tag_number), str, expected);
zassert_equal(len, str_len, NULL);
/* test when buffer is too small the behavior matches snprintf(). */
test_len = len;
@@ -1380,14 +1381,17 @@ static void test_bacapp_sprintf_epics(void)
BACNET_APPLICATION_TAG_DOUBLE, "-3.14159", "-3.141590");
test_bacapp_snprintf(
BACNET_APPLICATION_TAG_OCTET_STRING, "1234567890ABCDEF",
"1234567890ABCDEF");
"X'1234567890ABCDEF'");
test_bacapp_snprintf(
BACNET_APPLICATION_TAG_OCTET_STRING, "12-34-56-78-90-AB-CD-EF",
"1234567890ABCDEF");
BACNET_APPLICATION_TAG_OCTET_STRING, "X'1234567890ABCDEF'",
"X'1234567890ABCDEF'");
test_bacapp_snprintf(
BACNET_APPLICATION_TAG_OCTET_STRING, "12 34 56 78 90 AB CD EF",
"1234567890ABCDEF");
test_bacapp_snprintf(BACNET_APPLICATION_TAG_OCTET_STRING, "", "");
BACNET_APPLICATION_TAG_OCTET_STRING, "X'12-34-56-78-90-AB-CD-EF'",
"X'1234567890ABCDEF'");
test_bacapp_snprintf(
BACNET_APPLICATION_TAG_OCTET_STRING, "X'12 34 56 78 90 AB CD EF'",
"X'1234567890ABCDEF'");
test_bacapp_snprintf(BACNET_APPLICATION_TAG_OCTET_STRING, "", "X''");
test_bacapp_snprintf(
BACNET_APPLICATION_TAG_CHARACTER_STRING, "Karg!", "\"Karg!\"");
test_bacapp_snprintf(BACNET_APPLICATION_TAG_CHARACTER_STRING, "", "\"\"");
+117 -46
View File
@@ -11,6 +11,7 @@
#include <stdlib.h>
#include <string.h>
#include <zephyr/ztest.h>
#include <bacnet/bacaddr.h>
#include <bacnet/bacdest.h>
/**
@@ -21,49 +22,6 @@
/**
* @brief Test
*/
static void
testBACnetRecipientData(BACNET_RECIPIENT *data1, BACNET_RECIPIENT *data2)
{
unsigned i = 0;
if (data1 && data2) {
zassert_equal(data1->tag, data2->tag, NULL);
if (data1->tag == BACNET_RECIPIENT_TAG_DEVICE) {
zassert_equal(
data1->type.device.type, data2->type.device.type, NULL);
zassert_equal(
data1->type.device.instance, data2->type.device.instance, NULL);
} else if (data1->tag == BACNET_RECIPIENT_TAG_ADDRESS) {
zassert_equal(
data1->type.address.net, data2->type.address.net, NULL);
if (data1->type.address.net == BACNET_BROADCAST_NETWORK) {
zassert_equal(
data1->type.address.mac_len, data2->type.address.mac_len,
NULL);
} else if (data1->type.address.net) {
zassert_equal(
data1->type.address.len, data2->type.address.len, NULL);
for (i = 0; i < data1->type.address.len; i++) {
zassert_equal(
data1->type.address.adr[i], data2->type.address.adr[i],
NULL);
}
} else {
zassert_equal(
data1->type.address.mac_len, data2->type.address.mac_len,
NULL);
for (i = 0; i < data1->type.address.mac_len; i++) {
zassert_equal(
data1->type.address.mac[i], data2->type.address.mac[i],
NULL);
}
}
} else {
zassert_true(data1->tag < BACNET_RECIPIENT_TAG_MAX, NULL);
}
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacnet_destination_tests, testBACnetDestination)
#else
@@ -73,6 +31,7 @@ static void testBACnetDestination(void)
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_DESTINATION destination = { 0 }, test_destination = { 0 };
int apdu_len = 0, null_len = 0, test_len = 0;
bool status = false;
destination.Recipient.tag = BACNET_RECIPIENT_TAG_DEVICE;
destination.Recipient.type.device.type = OBJECT_DEVICE;
@@ -82,8 +41,8 @@ static void testBACnetDestination(void)
zassert_equal(apdu_len, null_len, NULL);
test_len = bacnet_destination_decode(apdu, apdu_len, &test_destination);
zassert_equal(apdu_len, test_len, NULL);
testBACnetRecipientData(
&destination.Recipient, &test_destination.Recipient);
status = bacnet_destination_same(&destination, &test_destination);
zassert_true(status, NULL);
destination.Recipient.tag = BACNET_RECIPIENT_TAG_ADDRESS;
destination.Recipient.type.address.net = 1234;
@@ -99,6 +58,8 @@ static void testBACnetDestination(void)
zassert_equal(apdu_len, null_len, NULL);
test_len = bacnet_destination_decode(apdu, apdu_len, &test_destination);
zassert_equal(test_len, apdu_len, NULL);
status = bacnet_destination_same(&destination, &test_destination);
zassert_true(status, NULL);
null_len = bacnet_destination_encode(NULL, &destination);
apdu_len = bacnet_destination_encode(apdu, &destination);
@@ -107,12 +68,27 @@ static void testBACnetDestination(void)
zassert_equal(test_len, apdu_len, NULL);
test_len = bacnet_destination_decode(apdu, apdu_len, NULL);
zassert_equal(test_len, apdu_len, NULL);
status = bacnet_destination_same(&destination, &test_destination);
zassert_true(status, NULL);
bacnet_destination_copy(&test_destination, &destination);
status = bacnet_destination_same(&destination, &test_destination);
zassert_true(status, NULL);
/* decoding, some negative tests */
test_len = bacnet_destination_decode(NULL, apdu_len, &test_destination);
zassert_equal(test_len, BACNET_STATUS_REJECT, NULL);
test_len = bacnet_destination_decode(apdu, 0, &test_destination);
zassert_equal(test_len, BACNET_STATUS_REJECT, NULL);
/* context */
null_len = bacnet_destination_context_encode(NULL, 4, &destination);
apdu_len = bacnet_destination_context_encode(apdu, 4, &destination);
zassert_equal(apdu_len, null_len, NULL);
test_len =
bacnet_destination_context_decode(apdu, apdu_len, 4, &test_destination);
zassert_equal(apdu_len, test_len, NULL);
/* defaults */
status = bacnet_destination_default(&destination);
zassert_false(status, NULL);
}
/**
* @}
@@ -163,6 +139,99 @@ static void test_BACnetDestination_ASCII(void)
}
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacnet_destination_tests, testBACnetRecipient)
#else
static void testBACnetRecipient(void)
#endif
{
uint8_t apdu[MAX_APDU] = { 0 };
BACNET_RECIPIENT value = { 0 }, test_value = { 0 };
BACNET_MAC_ADDRESS mac = { .len = 1, .adr[0] = 0x01 };
BACNET_MAC_ADDRESS adr = { .len = 1, .adr[0] = 0x02 };
uint16_t snet = 1234;
BACNET_ADDRESS address = { 0 };
int apdu_len = 0, null_len = 0, test_len = 0;
uint8_t tag_number = 4;
bool status = false;
/* device */
bacnet_recipient_device_set(&value, OBJECT_DEVICE, 123);
status = bacnet_recipient_device_valid(&value);
zassert_true(status, NULL);
bacnet_recipient_copy(&test_value, &value);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
null_len = bacnet_recipient_encode(NULL, &value);
apdu_len = bacnet_recipient_encode(apdu, &value);
zassert_equal(apdu_len, null_len, NULL);
test_len = bacnet_recipient_decode(apdu, apdu_len, &test_value);
zassert_equal(apdu_len, test_len, NULL);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
/* address */
status = bacnet_address_init(&address, &mac, snet, &adr);
zassert_true(status, NULL);
bacnet_recipient_address_set(&value, &address);
bacnet_recipient_copy(&test_value, &value);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
null_len = bacnet_recipient_encode(NULL, &value);
apdu_len = bacnet_recipient_encode(apdu, &value);
zassert_equal(apdu_len, null_len, NULL);
test_len = bacnet_recipient_decode(apdu, apdu_len, &test_value);
zassert_equal(apdu_len, test_len, NULL);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
/* context */
null_len = bacnet_recipient_context_encode(NULL, tag_number, &value);
apdu_len = bacnet_recipient_context_encode(apdu, tag_number, &value);
zassert_equal(apdu_len, null_len, NULL);
test_len = bacnet_recipient_context_decode(
apdu, apdu_len, tag_number, &test_value);
zassert_equal(apdu_len, test_len, NULL);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
bacnet_recipient_device_wildcard_set(&value);
status = bacnet_recipient_device_wildcard(&value);
zassert_true(status, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacnet_destination_tests, test_BACnetRecipient_ASCII)
#else
static void test_BACnetRecipient_ASCII(void)
#endif
{
bool status = false;
BACNET_RECIPIENT value = { 0 }, test_value = { 0 };
BACNET_ADDRESS address = { 0 };
BACNET_MAC_ADDRESS mac = { .len = 1, .adr[0] = 0x01 };
BACNET_MAC_ADDRESS adr = { .len = 1, .adr[0] = 0x02 };
uint16_t snet = 1234;
int len = 0;
char ascii[80] = "";
bacnet_recipient_device_set(&value, OBJECT_DEVICE, 4194303);
len = bacnet_recipient_to_ascii(&value, ascii, sizeof(ascii));
zassert_true(len > 0, NULL);
status = bacnet_recipient_from_ascii(&test_value, ascii);
zassert_true(status, "ascii=%s", ascii);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
status = bacnet_address_init(&address, &mac, snet, &adr);
zassert_true(status, NULL);
bacnet_recipient_address_set(&value, &address);
len = bacnet_recipient_to_ascii(&value, ascii, sizeof(ascii));
zassert_true(len > 0, NULL);
status = bacnet_recipient_from_ascii(&test_value, ascii);
zassert_true(status, "ascii=%s", ascii);
status = bacnet_recipient_same(&value, &test_value);
zassert_true(status, NULL);
}
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST_SUITE(bacnet_destination_tests, NULL, NULL, NULL, NULL, NULL);
#else
@@ -170,7 +239,9 @@ void test_main(void)
{
ztest_test_suite(
bacnet_destination_tests, ztest_unit_test(testBACnetDestination),
ztest_unit_test(test_BACnetDestination_ASCII));
ztest_unit_test(test_BACnetDestination_ASCII),
ztest_unit_test(testBACnetRecipient),
ztest_unit_test(test_BACnetRecipient_ASCII));
ztest_run_test_suite(bacnet_destination_tests);
}
+367 -7
View File
@@ -5,9 +5,24 @@
* @date 2004
* @copyright SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <zephyr/ztest.h>
#include <bacnet/bacstr.h>
/**
* @brief compare two double precision floating points to 3 decimal places
* @param x1 - first comparison value
* @param x2 - second comparison value
* @return true if the value is the same to 3 decimal points
*/
static bool is_float_equal(double x1, double x2)
{
return fabs(x1 - x2) < 0.001;
}
/**
* @addtogroup bacnet_tests
* @{
@@ -120,6 +135,8 @@ static void testCharacterString(void)
{
BACNET_CHARACTER_STRING bacnet_string = { 0 }, bacnet_string2 = { 0 };
const char *value = "Joshua,Mary,Anna,Christopher";
const char *utf8_value = "Joshua😍Mary😍Anna😍Christopher";
const char *result = NULL;
char test_value[MAX_APDU] = "Patricia";
char test_append_value[MAX_APDU] = " and the Kids";
char test_append_string[MAX_APDU] = "";
@@ -129,12 +146,19 @@ static void testCharacterString(void)
size_t test_length = 0;
size_t i = 0;
/* verify initialization */
/* verify UTF8 initialization */
status = characterstring_init(&bacnet_string, CHARACTER_UTF8, NULL, 0);
zassert_true(status, NULL);
zassert_equal(characterstring_length(&bacnet_string), 0, NULL);
zassert_equal(
characterstring_encoding(&bacnet_string), CHARACTER_UTF8, NULL);
/* verify ANSI initialization */
status = characterstring_init(&bacnet_string, CHARACTER_ANSI_X34, NULL, 0);
zassert_true(status, NULL);
zassert_equal(characterstring_length(&bacnet_string), 0, NULL);
zassert_equal(
characterstring_encoding(&bacnet_string), CHARACTER_ANSI_X34, NULL);
/* empty string is the same as NULL */
status = characterstring_same(&bacnet_string, NULL);
zassert_true(status, NULL);
@@ -156,11 +180,11 @@ static void testCharacterString(void)
status = characterstring_init(
&bacnet_string, CHARACTER_ANSI_X34, &test_value[0], test_length);
zassert_true(status, NULL);
value = characterstring_value(&bacnet_string);
result = characterstring_value(&bacnet_string);
length = characterstring_length(&bacnet_string);
zassert_equal(length, test_length, NULL);
for (i = 0; i < test_length; i++) {
zassert_equal(value[i], test_value[i], NULL);
zassert_equal(result[i], test_value[i], NULL);
}
test_length = characterstring_copy_value(
test_string, sizeof(test_string), &bacnet_string);
@@ -174,10 +198,10 @@ static void testCharacterString(void)
test_length = strlen(test_append_string);
zassert_true(status, NULL);
length = characterstring_length(&bacnet_string);
value = characterstring_value(&bacnet_string);
result = characterstring_value(&bacnet_string);
zassert_equal(length, test_length, NULL);
for (i = 0; i < test_length; i++) {
zassert_equal(value[i], test_append_string[i], NULL);
zassert_equal(result[i], test_append_string[i], NULL);
}
/* init from valid ASCII string */
status = characterstring_init_ansi(&bacnet_string, value);
@@ -223,6 +247,22 @@ static void testCharacterString(void)
status = characterstring_ansi_copy(
test_append_string, sizeof(test_append_string), &bacnet_string);
zassert_equal(strncmp(value, test_append_string, strlen(value)), 0, NULL);
/* UTF-8 specific */
status = characterstring_init_ansi(&bacnet_string, value);
zassert_true(status, NULL);
length = characterstring_length(&bacnet_string);
status = characterstring_init_ansi(&bacnet_string, utf8_value);
zassert_true(status, NULL);
zassert_equal(
characterstring_encoding(&bacnet_string), CHARACTER_UTF8, NULL);
status = characterstring_valid(&bacnet_string);
zassert_true(status, NULL);
test_length = characterstring_utf8_length(&bacnet_string);
zassert_equal(
length, test_length, "value=\"%s\" length=%d, test_length=%d", value,
length, test_length);
status = characterstring_printable(&bacnet_string);
zassert_false(status, NULL);
}
/**
@@ -394,6 +434,17 @@ static void test_bacnet_stricmp(void)
zassert_not_equal(rv, 0, NULL);
rv = bacnet_stricmp(test_name_a, NULL);
zassert_not_equal(rv, 0, NULL);
/* case sensitive */
rv = bacnet_strcmp(name_a, name_a);
zassert_equal(rv, 0, NULL);
rv = bacnet_strcmp(name_a, test_name_a);
zassert_not_equal(rv, 0, NULL);
rv = bacnet_strcmp(test_name_a, test_name_b);
zassert_not_equal(rv, 0, NULL);
rv = bacnet_strcmp(NULL, test_name_b);
zassert_not_equal(rv, 0, NULL);
rv = bacnet_strcmp(test_name_a, NULL);
zassert_not_equal(rv, 0, NULL);
}
/**
@@ -409,6 +460,18 @@ static void test_bacnet_strnicmp(void)
const char *name_a = "Patricia", *test_name_a = "patricia";
const char *name_b = "CamelCase", *test_name_b = "CAMELCASE";
/* case sensitive */
rv = bacnet_strncmp(name_a, name_a, strlen(name_a));
zassert_equal(rv, 0, NULL);
rv = bacnet_strncmp(name_a, test_name_a, strlen(name_a));
zassert_not_equal(rv, 0, NULL);
rv = bacnet_strncmp(test_name_a, test_name_b, strlen(test_name_a));
zassert_not_equal(rv, 0, NULL);
rv = bacnet_strncmp(NULL, test_name_b, strlen(test_name_b));
zassert_not_equal(rv, 0, NULL);
rv = bacnet_strncmp(test_name_a, NULL, strlen(test_name_a));
zassert_not_equal(rv, 0, NULL);
/* case insensitive */
rv = bacnet_strnicmp(name_a, test_name_a, strlen(name_a));
zassert_equal(rv, 0, NULL);
rv = bacnet_strnicmp(name_b, test_name_b, strlen(name_b));
@@ -451,6 +514,299 @@ static void test_bacnet_strnlen(void)
test_len = bacnet_strnlen(test_name, 512);
zassert_equal(len, test_len, "len=%u test_len=%d", len, test_len);
}
/**
* @brief Test encode/decode API for bacnet_strto. functions
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacstr_tests, test_bacnet_strto)
#else
static void test_bacnet_strto(void)
#endif
{
bool status;
const char *empty_string = "";
const char *extra_text_string = "123yyx";
const char *test_unsigned_long_string = "1234567890";
unsigned long unsigned_long_value, test_unsigned_long_value = 1234567890;
const char *test_long_string = "-1234567890";
long long_value, test_long_value = -1234567890;
const char *test_float_positive_string = "1.23";
float float_value, test_float_value = 1.23f;
double double_value, test_double_value = 1.23;
long double long_double_value, test_long_double_value = 1.23L;
const char *test_float_negative_string = "-1.23";
float float_negative_value, test_float_negative_value = -1.23f;
double double_negative_value, test_double_negative_value = -1.23;
long double long_double_negative_value,
test_long_double_negative_value = -1.23L;
char buffer[80] = "", *ascii_result = NULL;
/* unsigned long */
status = bacnet_strtoul(test_unsigned_long_string, &unsigned_long_value);
zassert_true(status, NULL);
zassert_equal(unsigned_long_value, test_unsigned_long_value, NULL);
status = bacnet_strtoul(empty_string, &unsigned_long_value);
zassert_false(status, NULL);
status = bacnet_strtoul(extra_text_string, &unsigned_long_value);
zassert_false(status, NULL);
ascii_result = bacnet_ultoa(unsigned_long_value, buffer, sizeof(buffer));
zassert_equal(bacnet_strcmp(buffer, test_unsigned_long_string), 0, NULL);
zassert_equal(ascii_result, buffer, NULL);
ascii_result = bacnet_utoa(unsigned_long_value, buffer, sizeof(buffer));
zassert_equal(bacnet_strcmp(buffer, test_unsigned_long_string), 0, NULL);
zassert_equal(ascii_result, buffer, NULL);
/* long */
status = bacnet_strtol(test_long_string, &long_value);
zassert_true(status, NULL);
zassert_equal(long_value, test_long_value, NULL);
status = bacnet_strtol(empty_string, &long_value);
zassert_false(status, NULL);
status = bacnet_strtol(extra_text_string, &long_value);
zassert_false(status, NULL);
ascii_result = bacnet_ltoa(long_value, buffer, sizeof(buffer));
zassert_equal(bacnet_strcmp(buffer, test_long_string), 0, NULL);
zassert_equal(ascii_result, buffer, NULL);
ascii_result = bacnet_itoa(long_value, buffer, sizeof(buffer));
zassert_equal(bacnet_strcmp(buffer, test_long_string), 0, NULL);
zassert_equal(ascii_result, buffer, NULL);
/* single precision */
status = bacnet_strtof(test_float_positive_string, &float_value);
zassert_true(status, NULL);
zassert_true(is_float_equal(float_value, test_float_value), NULL);
status = bacnet_strtof(test_float_negative_string, &float_negative_value);
zassert_true(status, NULL);
zassert_true(
is_float_equal(float_negative_value, test_float_negative_value), NULL);
status = bacnet_strtof(empty_string, &float_value);
zassert_false(status, NULL);
status = bacnet_strtof(extra_text_string, &float_value);
zassert_false(status, NULL);
/* double precision */
status = bacnet_strtod(test_float_positive_string, &double_value);
zassert_true(status, NULL);
zassert_true(is_float_equal(double_value, test_double_value), NULL);
status = bacnet_strtod(test_float_negative_string, &double_negative_value);
zassert_true(status, NULL);
zassert_true(
is_float_equal(double_negative_value, test_double_negative_value),
NULL);
status = bacnet_strtod(empty_string, &double_value);
zassert_false(status, NULL);
status = bacnet_strtod(extra_text_string, &double_value);
zassert_false(status, NULL);
ascii_result =
bacnet_dtoa(double_negative_value, buffer, sizeof(buffer), 2);
zassert_equal(bacnet_strcmp(buffer, test_float_negative_string), 0, NULL);
zassert_equal(ascii_result, buffer, NULL);
/* long double precision */
status = bacnet_strtold(test_float_positive_string, &long_double_value);
zassert_true(status, NULL);
zassert_true(
is_float_equal(long_double_value, test_long_double_value), NULL);
status =
bacnet_strtold(test_float_negative_string, &long_double_negative_value);
zassert_true(status, NULL);
zassert_true(
is_float_equal(
long_double_negative_value, test_long_double_negative_value),
NULL);
status = bacnet_strtold(empty_string, &long_double_value);
zassert_false(status, NULL);
status = bacnet_strtold(extra_text_string, &long_double_value);
zassert_false(status, NULL);
}
/**
* @brief Test encode/decode API for bacnet_string_to_x functions
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacstr_tests, test_bacnet_string_to_x)
#else
static void test_bacnet_string_to_x(void)
#endif
{
bool status;
const char *empty_string = "";
const char *extra_text_string = "123yyx";
const char *test_uint8_t_string = "123";
const char *test_uint16_t_string = "12345";
const char *test_uint32_t_string = "1234567890";
const char *test_int32_t_string = "-1234567890";
const char *test_true_string = "true";
const char *test_false_string = "false";
const char *test_active_string = "active";
const char *test_inactive_string = "inactive";
const char *test_true_numeric_string = "1";
const char *test_false_numeric_string = "0";
const char *test_unsigned_string = "1234567890";
const char *test_ascii_string = "abcdefghijklmnopqrstuvwxyz";
uint8_t uint8_t_value, test_uint8_t_value = 123;
uint16_t uint16_t_value, test_uint16_t_value = 12345;
uint32_t uint32_t_value, test_uint32_t_value = 1234567890;
int32_t int32_t_value, test_int32_t_value = -1234567890;
BACNET_UNSIGNED_INTEGER bacnet_unsigned_integer,
test_bacnet_unsigned_integer = 1234567890;
bool bool_value, test_true_value = true, test_false_value = false;
char ascii_string[80] = "", *ascii_string_result = NULL;
/* uint8_t */
status = bacnet_string_to_uint8(test_uint8_t_string, &uint8_t_value);
zassert_true(status, NULL);
zassert_equal(uint8_t_value, test_uint8_t_value, NULL);
status = bacnet_string_to_uint8(empty_string, &uint8_t_value);
zassert_false(status, NULL);
status = bacnet_string_to_uint8(extra_text_string, &uint8_t_value);
zassert_false(status, NULL);
/* uint16_t */
status = bacnet_string_to_uint16(test_uint16_t_string, &uint16_t_value);
zassert_true(status, NULL);
zassert_equal(uint16_t_value, test_uint16_t_value, NULL);
status = bacnet_string_to_uint16(empty_string, &uint16_t_value);
zassert_false(status, NULL);
status = bacnet_string_to_uint16(extra_text_string, &uint16_t_value);
zassert_false(status, NULL);
/* uint32_t */
status = bacnet_string_to_uint32(test_uint32_t_string, &uint32_t_value);
zassert_true(status, NULL);
zassert_equal(uint32_t_value, test_uint32_t_value, NULL);
status = bacnet_string_to_uint32(empty_string, &uint32_t_value);
zassert_false(status, NULL);
status = bacnet_string_to_uint32(extra_text_string, &uint32_t_value);
zassert_false(status, NULL);
/* int32_t */
status = bacnet_string_to_int32(test_int32_t_string, &int32_t_value);
zassert_true(status, NULL);
zassert_equal(int32_t_value, test_int32_t_value, NULL);
status = bacnet_string_to_int32(empty_string, &int32_t_value);
zassert_false(status, NULL);
/* bool */
status = bacnet_string_to_bool(test_true_string, &bool_value);
zassert_true(status, NULL);
zassert_equal(bool_value, test_true_value, NULL);
status = bacnet_string_to_bool(test_false_string, &bool_value);
zassert_true(status, NULL);
zassert_equal(bool_value, test_false_value, NULL);
status = bacnet_string_to_bool(empty_string, &bool_value);
zassert_false(status, NULL);
status = bacnet_string_to_bool(extra_text_string, &bool_value);
zassert_false(status, NULL);
/* active/inactive */
status = bacnet_string_to_bool(test_active_string, &bool_value);
zassert_true(status, NULL);
zassert_equal(bool_value, test_true_value, NULL);
status = bacnet_string_to_bool(test_inactive_string, &bool_value);
zassert_true(status, NULL);
zassert_equal(bool_value, test_false_value, NULL);
status = bacnet_string_to_bool(empty_string, &bool_value);
zassert_false(status, NULL);
/* 0/1 */
status = bacnet_string_to_bool(test_true_numeric_string, &bool_value);
zassert_true(status, NULL);
zassert_equal(bool_value, test_true_value, NULL);
status = bacnet_string_to_bool(test_false_numeric_string, &bool_value);
zassert_true(status, NULL);
zassert_equal(bool_value, test_false_value, NULL);
/* bacnet_unsigned_integer */
status = bacnet_string_to_unsigned(
test_unsigned_string, &bacnet_unsigned_integer);
zassert_true(status, NULL);
zassert_equal(bacnet_unsigned_integer, test_bacnet_unsigned_integer, NULL);
status = bacnet_string_to_unsigned(empty_string, &bacnet_unsigned_integer);
zassert_false(status, NULL);
status =
bacnet_string_to_unsigned(extra_text_string, &bacnet_unsigned_integer);
zassert_false(status, NULL);
/* ascii string */
ascii_string_result = bacnet_snprintf_to_ascii(
ascii_string, sizeof(ascii_string), "%s", test_ascii_string);
zassert_equal(
bacnet_strcmp(ascii_string_result, test_ascii_string), 0, NULL);
}
/**
* @brief Test encode/decode API for bacnet trim functions
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacstr_tests, test_bacnet_string_trim)
#else
static void test_bacnet_string_trim(void)
#endif
{
char trim_left[80] = " abcdefg", *trim_left_result = NULL;
char trim_right[80] = "abcdefg ", *trim_right_result = NULL;
char trim_both[80] = " abcdefg ", *trim_both_result = NULL;
char *trim_test_value = "abcdefg";
char *empty_string = "";
trim_left_result = bacnet_ltrim(trim_left, " ");
trim_right_result = bacnet_rtrim(trim_right, " ");
trim_both_result = bacnet_trim(trim_both, " ");
zassert_equal(bacnet_strcmp(trim_left_result, trim_test_value), 0, NULL);
zassert_equal(bacnet_strcmp(trim_right_result, trim_test_value), 0, NULL);
zassert_equal(bacnet_strcmp(trim_both_result, trim_test_value), 0, NULL);
trim_left_result = bacnet_ltrim(empty_string, " ");
trim_right_result = bacnet_rtrim(empty_string, " ");
trim_both_result = bacnet_trim(empty_string, " ");
zassert_equal(bacnet_strcmp(trim_left_result, empty_string), 0, NULL);
zassert_equal(bacnet_strcmp(trim_right_result, empty_string), 0, NULL);
zassert_equal(bacnet_strcmp(trim_both_result, empty_string), 0, NULL);
}
/**
* @brief Test bacnet_stptok string tokenizer
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacstr_tests, test_bacnet_stptok)
#else
static void test_bacnet_stptok(void)
#endif
{
char *pCmd = "I Love You\r\n";
char token[80] = "";
pCmd = bacnet_stptok(pCmd, token, sizeof(token), " \r\n");
zassert_true(bacnet_strcmp(token, "I") == 0, NULL);
zassert_true(bacnet_strcmp(pCmd, "Love You\r\n") == 0, NULL);
pCmd = bacnet_stptok(pCmd, token, sizeof(token), " \r\n");
zassert_true(bacnet_strcmp(token, "Love") == 0, NULL);
zassert_true(bacnet_strcmp(pCmd, "You\r\n") == 0, NULL);
pCmd = bacnet_stptok(pCmd, token, sizeof(token), " \r\n");
zassert_true(bacnet_strcmp(token, "You") == 0, NULL);
zassert_true(pCmd == NULL, NULL);
}
/**
* @brief Test encode/decode API for bacnet snprintf and shift functions
*/
#if defined(CONFIG_ZTEST_NEW_API)
ZTEST(bacstr_tests, test_bacnet_snprintf)
#else
static void test_bacnet_snprintf(void)
#endif
{
int buf_len = 0, str_len;
int i;
char str[30] = "";
str_len = sizeof(str);
for (i = 0; i < 5; i++) {
/* appending formatted strings */
buf_len = bacnet_snprintf(str, str_len, buf_len, "{");
buf_len =
bacnet_snprintf(str, str_len, buf_len, "REALLY BIG STRING BASS");
buf_len = bacnet_snprintf(str, str_len, buf_len, "}");
}
zassert_equal(buf_len, str_len, "buf_len=%d str_len=%d", buf_len, str_len);
zassert_equal(
str[buf_len - 1], 0, "str[%d]=%c", buf_len - 1, str[buf_len - 1]);
}
/**
* @}
*/
@@ -465,8 +821,12 @@ void test_main(void)
ztest_unit_test(testCharacterString), ztest_unit_test(testOctetString),
ztest_unit_test(test_bacnet_stricmp),
ztest_unit_test(test_bacnet_strnicmp),
ztest_unit_test(test_bacnet_strnlen));
ztest_unit_test(test_bacnet_strnlen),
ztest_unit_test(test_bacnet_strto),
ztest_unit_test(test_bacnet_string_to_x),
ztest_unit_test(test_bacnet_string_trim),
ztest_unit_test(test_bacnet_stptok),
ztest_unit_test(test_bacnet_snprintf));
ztest_run_test_suite(bacstr_tests);
}
#endif
+3 -1
View File
@@ -34,10 +34,12 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/hostnport.c
${SRC_DIR}/bacnet/iam.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/npdu.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/debug.c
+3 -1
View File
@@ -35,9 +35,11 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/iam.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/npdu.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/debug.c
+3 -1
View File
@@ -33,9 +33,11 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/iam.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/npdu.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/debug.c
@@ -40,8 +40,10 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/sys/ringbuf.c
${SRC_DIR}/bacnet/basic/sys/days.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/npdu.c
# Test and test library files
./src/main.c
+3 -1
View File
@@ -45,8 +45,10 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacaddr.c
${SRC_DIR}/bacnet/bacdcode.c
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/npdu.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
# Support files and stubs (pathname alphabetical)
+2
View File
@@ -42,11 +42,13 @@ add_executable(${PROJECT_NAME}
${SRC_DIR}/bacnet/bacint.c
${SRC_DIR}/bacnet/bacreal.c
${SRC_DIR}/bacnet/bacstr.c
${SRC_DIR}/bacnet/bactext.c
${SRC_DIR}/bacnet/basic/service/h_apdu.c
${SRC_DIR}/bacnet/basic/sys/bigend.c
${SRC_DIR}/bacnet/basic/sys/debug.c
${SRC_DIR}/bacnet/basic/tsm/tsm.c
${SRC_DIR}/bacnet/dcc.c
${SRC_DIR}/bacnet/indtext.c
${SRC_DIR}/bacnet/proplist.c
${SRC_DIR}/bacnet/reject.c
${SRC_DIR}/bacnet/rp.c