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:
@@ -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)
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
+448
-25
@@ -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,15 +411,29 @@ int bacnet_address_decode(
|
||||
if (len <= 0) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
if (value) {
|
||||
if (mac_addr.length > sizeof(value->mac)) {
|
||||
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];
|
||||
}
|
||||
}
|
||||
/* bounds checking - passed! */
|
||||
value->mac_len = mac_addr.length;
|
||||
/* copy address */
|
||||
for (i = 0; i < value->mac_len; i++) {
|
||||
value->mac[i] = mac_addr.value[i];
|
||||
} else {
|
||||
if (value) {
|
||||
if (mac_addr.length > sizeof(value->mac)) {
|
||||
return BACNET_STATUS_ERROR;
|
||||
}
|
||||
/* bounds checking - passed! */
|
||||
value->mac_len = mac_addr.length;
|
||||
/* copy address */
|
||||
for (i = 0; i < value->mac_len; i++) {
|
||||
value->mac[i] = mac_addr.value[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
apdu_len += 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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
+173
-39
@@ -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;
|
||||
twin_a = tolower(toupper(twin_a));
|
||||
twin_b = tolower(toupper(twin_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
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user