From def3cb14bbe73aa0c725f88e7632fb5d542f44b8 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Thu, 26 Jun 2025 13:17:15 -0500 Subject: [PATCH] Bugfix/ucov-timestamp-handler-and-notification (#1030) * Added the ability for apps/ucov to send property specific application tagged data using -1 argument for tag. * Added known property decoding in UnconfirmedCOVNotification handler. --- CHANGELOG.md | 3 + apps/ucov/main.c | 13 +++- src/bacnet/bacapp.c | 142 ++++++++++++++++++++++++++++++++++++++++++++ src/bacnet/bacapp.h | 6 ++ src/bacnet/cov.c | 4 +- 5 files changed, 163 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c32183..0f15a99a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,9 @@ The git repositories are hosted at the following sites: ### Added +* Added known property decoding in UnconfirmedCOVNotification handler. (#1030) +* Added the ability for apps/ucov to send property specific application + tagged data using -1 argument for tag. (#1030) * Added the ability to write to the mstp mac address and link speed property values in the basic network port object. (#1025) * Added new error-code enumerations from 135-2024 BACnet. diff --git a/apps/ucov/main.c b/apps/ucov/main.c index 224c5b46..9da86237 100644 --- a/apps/ucov/main.c +++ b/apps/ucov/main.c @@ -90,6 +90,9 @@ static void print_usage(const char *filename) "in bacenum.h. It is the data type of the value that you are\n" "monitoring. For example, if you were monitoring a REAL value,\n" "you would use a tag of 4.\n"); + printf("Complex data use the property argument and a tag number -1 to\n" + "lookup the appropriate internal application tag for the value.\n" + "The complex data value argument varies in its construction.\n"); printf("\n"); printf( "value:\n" @@ -126,7 +129,7 @@ int main(int argc, char *argv[]) bool status = false; BACNET_COV_DATA cov_data; BACNET_PROPERTY_VALUE value_list; - uint8_t tag; + long tag; unsigned object_type = 0; unsigned object_property = 0; @@ -190,9 +193,13 @@ int main(int argc, char *argv[]) cov_data.listOfValues->propertyIdentifier, MAX_BACNET_PROPERTY_ID); return 1; } - if (tag >= MAX_BACNET_APPLICATION_TAG) { + if (tag < 0) { + tag = bacapp_known_property_tag( + cov_data.monitoredObjectIdentifier.type, + cov_data.listOfValues->propertyIdentifier); + } else if (tag >= MAX_BACNET_APPLICATION_TAG) { fprintf( - stderr, "tag=%u - it must be less than %u\n", tag, + stderr, "tag=%ld - it must be less than %u\n", tag, MAX_BACNET_APPLICATION_TAG); return 1; } diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index 5e92b7da..7fbd1415 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -5050,6 +5050,148 @@ int bacapp_property_value_decode( return apdu_len; } +/** + * @brief Decode one BACnetPropertyValue value for a specific object type + * + * BACnetPropertyValue ::= SEQUENCE { + * property-identifier [0] BACnetPropertyIdentifier, + * property-array-index [1] Unsigned OPTIONAL, + * -- used only with array datatypes + * -- if omitted with an array the entire array is referenced + * property-value [2] ABSTRACT-SYNTAX.&Type, + * -- any datatype appropriate for the specified property + * priority [3] Unsigned (1..16) OPTIONAL + * -- used only when property is commandable + * } + * + * @param apdu Pointer to the buffer of encoded value + * @param apdu_size Size of the buffer holding the encode value + * @param value Pointer to the service data used for encoding values + * + * @return Bytes decoded or BACNET_STATUS_ERROR on error. + */ +int bacapp_object_property_value_decode( + const uint8_t *apdu, + uint32_t apdu_size, + BACNET_PROPERTY_VALUE *value, + BACNET_OBJECT_TYPE object_type) +{ + int len = 0; + int apdu_len = 0; + int tag_len = 0; + uint32_t enumerated_value = 0; + uint32_t len_value_type = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + BACNET_PROPERTY_ID property_identifier = PROP_ALL; + uint32_t array_index = BACNET_ARRAY_ALL; + BACNET_APPLICATION_DATA_VALUE *app_data = NULL; + + /* property-identifier [0] BACnetPropertyIdentifier */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enumerated_value); + if (len > 0) { + property_identifier = enumerated_value; + if (value) { + value->propertyIdentifier = property_identifier; + } + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* property-array-index [1] Unsigned OPTIONAL */ + if (bacnet_is_context_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 1, &len, &len_value_type)) { + apdu_len += len; + len = bacnet_unsigned_decode( + &apdu[apdu_len], apdu_size - apdu_len, len_value_type, + &unsigned_value); + if (len > 0) { + if (unsigned_value > UINT32_MAX) { + return BACNET_STATUS_ERROR; + } else { + apdu_len += len; + if (value) { + value->propertyArrayIndex = unsigned_value; + array_index = value->propertyArrayIndex; + } + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + if (value) { + value->propertyArrayIndex = BACNET_ARRAY_ALL; + } + } + /* property-value [2] ABSTRACT-SYNTAX.&Type */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + if (value) { + apdu_len += len; + app_data = &value->value; + while (app_data != NULL) { + len = bacapp_decode_known_array_property( + &apdu[apdu_len], apdu_size - apdu_len, app_data, + object_type, property_identifier, array_index); + if (len < 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + break; + } + app_data = app_data->next; + } + } else { + /* this len function needs to start at the opening tag + to match opening/closing tags like a stack. + However, it returns the len between the tags. + Therefore, store the length of the opening tag first */ + tag_len = len; + len = bacnet_enclosed_data_length( + &apdu[apdu_len], apdu_size - apdu_len); + apdu_len += len; + /* add the opening tag length to the totals */ + apdu_len += tag_len; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* priority [3] Unsigned (1..16) OPTIONAL */ + if (bacnet_is_context_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 3, &len, &len_value_type)) { + apdu_len += len; + len = bacnet_unsigned_decode( + &apdu[apdu_len], apdu_size - apdu_len, len_value_type, + &unsigned_value); + if (len > 0) { + if (unsigned_value > UINT8_MAX) { + return BACNET_STATUS_ERROR; + } else { + apdu_len += len; + if (value) { + value->priority = unsigned_value; + } + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + if (value) { + value->priority = BACNET_NO_PRIORITY; + } + } + + return apdu_len; +} + /* generic - can be used by other unit tests returns true if matching or same, false if different */ bool bacapp_same_value( diff --git a/src/bacnet/bacapp.h b/src/bacnet/bacapp.h index 536537bf..687a021c 100644 --- a/src/bacnet/bacapp.h +++ b/src/bacnet/bacapp.h @@ -243,6 +243,12 @@ int bacapp_property_value_encode( BACNET_STACK_EXPORT int bacapp_property_value_decode( const uint8_t *apdu, uint32_t apdu_size, BACNET_PROPERTY_VALUE *value); +BACNET_STACK_EXPORT +int bacapp_object_property_value_decode( + const uint8_t *apdu, + uint32_t apdu_size, + BACNET_PROPERTY_VALUE *value, + BACNET_OBJECT_TYPE object_type); BACNET_STACK_EXPORT int bacapp_encode_data( diff --git a/src/bacnet/cov.c b/src/bacnet/cov.c index c3aaac6f..025cc46e 100644 --- a/src/bacnet/cov.c +++ b/src/bacnet/cov.c @@ -264,8 +264,8 @@ int cov_notify_decode_service_request( /* the first value includes a pointer to the next value, etc */ value = data->listOfValues; while (value != NULL) { - value_len = bacapp_property_value_decode( - &apdu[len], apdu_size - len, value); + value_len = bacapp_object_property_value_decode( + &apdu[len], apdu_size - len, value, decoded_type); if (value_len == BACNET_STATUS_ERROR) { return BACNET_STATUS_ERROR; } else {