diff --git a/CHANGELOG.md b/CHANGELOG.md index 62146f7f..dab933f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ The git repositories are hosted at the following sites: ### Security +* Secured UnconfirmedEventNotification-Request and + ConfirmedEventNotification-Request, BACnetNotificationParameters, + and BACnetAuthenticationFactor decoding by refactoring deprecated + functions and validating with unit testing. (#1126) * Secured I-Am request encoding and decoding, and updated the example apps and handlers to use secure version of I-Am decoder. (#1080) * Secured GetEventInformation-Request and -ACK decoder and encoder. (#1026) diff --git a/Makefile b/Makefile index f9745f75..0072a020 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,10 @@ all: apps bsd: $(MAKE) LEGACY=true BACNET_PORT=bsd -s -C apps all +.PHONY: linux +linux: + $(MAKE) LEGACY=true BACNET_PORT=linux -s -C apps all + .PHONY: win32 win32: $(MAKE) LEGACY=true BACNET_PORT=win32 -s -C apps all @@ -62,15 +66,15 @@ bsc: .PHONY: apps apps: - $(MAKE) -s LEGACY=true -C apps all + $(MAKE) -s -C apps all .PHONY: lib lib: - $(MAKE) -s LEGACY=true -C apps $@ + $(MAKE) -s -C apps $@ .PHONY: library library: - $(MAKE) -s LEGACY=true -C apps lib + $(MAKE) -s -C apps lib CMAKE_BUILD_DIR=build .PHONY: cmake diff --git a/src/bacnet/authentication_factor.c b/src/bacnet/authentication_factor.c index cefd9c76..32b0a055 100644 --- a/src/bacnet/authentication_factor.c +++ b/src/bacnet/authentication_factor.c @@ -22,29 +22,17 @@ int bacapp_encode_authentication_factor( int apdu_len = 0; len = encode_context_enumerated(apdu, 0, af->format_type); - if (len < 0) { - return -1; - } else { - apdu_len += len; - if (apdu) { - apdu += len; - } + apdu_len += len; + if (apdu) { + apdu += len; } len = encode_context_unsigned(apdu, 1, af->format_class); - if (len < 0) { - return -1; - } else { - apdu_len += len; - if (apdu) { - apdu += len; - } + apdu_len += len; + if (apdu) { + apdu += len; } len = encode_context_octet_string(apdu, 2, &af->value); - if (len < 0) { - return -1; - } else { - apdu_len += len; - } + apdu_len += len; return apdu_len; } @@ -77,56 +65,122 @@ int bacapp_encode_context_authentication_factor( return apdu_len; } +/** + * @brief Decode the BACnetAuthenticationFactor complex data + * @details + * BACnetAuthenticationFactor ::= SEQUENCE { + * format-type[0] BACnetAuthenticationFactorType, + * format-class[1] Unsigned, + * value[2] OctetString + * -- for encoding of values into this octet string see Annex P. + * } + * + * @param apdu Pointer to the buffer for decoding. + * @param apdu_size Number of valid bytes in the buffer. + * @param af Pointer to the property decoded data to be stored, or NULL for + * length of the decoded bytes. + * @return Number of bytes decoded or BACNET_STATUS_ERROR on error. + */ +int bacnet_authentication_factor_decode( + const uint8_t *apdu, unsigned apdu_size, BACNET_AUTHENTICATION_FACTOR *af) +{ + int len = 0, apdu_len = 0; + uint32_t enum_value = 0; + BACNET_UNSIGNED_INTEGER unsigned_value = 0; + BACNET_OCTET_STRING *octet_string_value = NULL; + + /* format-type[0] BACnetAuthenticationFactorType */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enum_value); + if (len > 0) { + if (af) { + if (enum_value > AUTHENTICATION_FACTOR_MAX) { + enum_value = AUTHENTICATION_FACTOR_MAX; + } + af->format_type = (BACNET_AUTHENTICATION_FACTOR_TYPE)enum_value; + } + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* format-class[1] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &unsigned_value); + if (len > 0) { + if (af) { + af->format_class = unsigned_value; + } + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* value[2] OctetString */ + if (af) { + octet_string_value = &af->value; + } else { + octet_string_value = NULL; + } + len = bacnet_octet_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, octet_string_value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + + return apdu_len; +} + /** * @brief Decode the BACnetAuthenticationFactor complex data * @param apdu Pointer to the buffer for decoding. * @param af Pointer to the property decoded data to be stored * @return Bytes decoded or BACNET_STATUS_REJECT on error. + * @deprecated Use bacnet_authentication_factor_decode() instead */ int bacapp_decode_authentication_factor( const uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR *af) { - int len; - int apdu_len = 0; - uint32_t format_type = af->format_type; - BACNET_UNSIGNED_INTEGER unsigned_value = 0; + return bacnet_authentication_factor_decode(apdu, MAX_APDU, af); +} - if (decode_is_context_tag(&apdu[apdu_len], 0)) { - len = decode_context_enumerated(&apdu[apdu_len], 0, &format_type); - if (len < 0) { - return -1; - } else if (format_type < AUTHENTICATION_FACTOR_MAX) { +/** + * @brief Decode the context tagged BACnetAuthenticationFactor complex data + * @param apdu Pointer to the buffer for decoding. + * @param apdu_size Number of valid bytes in the buffer. + * @param tag context tag number wrapping the complex data + * @param af Pointer to the property decoded data to be stored, or NULL + * for the length in bytes decoded + * @return Number of bytes decoded, + * 0 if opening tag doesn't match (use to detect OPTIONAL), + * or BACNET_STATUS_ERROR on error. + */ +int bacnet_authentication_factor_context_decode( + const uint8_t *apdu, + unsigned apdu_size, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR *af) +{ + int len = 0, apdu_len = 0; + + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag, &len)) { + apdu_len += len; + len = bacnet_authentication_factor_decode( + &apdu[apdu_len], apdu_size - apdu_len, af); + if (len > 0) { apdu_len += len; - af->format_type = (BACNET_AUTHENTICATION_FACTOR_TYPE)format_type; } else { - /* FIXME: Maybe this should return BACNET_STATUS_REJECT */ - return -1; + return BACNET_STATUS_ERROR; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, tag, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; } } else { - return -1; - } - - if (decode_is_context_tag(&apdu[apdu_len], 1)) { - len = decode_context_unsigned(&apdu[apdu_len], 1, &unsigned_value); - if (len < 0) { - return -1; - } else { - af->format_class = unsigned_value; - apdu_len += len; - } - } else { - return -1; - } - - if (decode_is_context_tag(&apdu[apdu_len], 2)) { - len = decode_context_octet_string(&apdu[apdu_len], 2, &af->value); - if (len < 0) { - return -1; - } else { - apdu_len += len; - } - } else { - return -1; + return 0; } return apdu_len; @@ -137,30 +191,13 @@ int bacapp_decode_authentication_factor( * @param apdu Pointer to the buffer for decoding. * @param tag context tag number wrapping the complex data * @param af Pointer to the property decoded data to be stored - * @return Bytes decoded or BACNET_STATUS_REJECT on error. + * @return Number of bytes decoded, + * 0 if opening tag doesn't match (use to detect OPTIONAL), + * or BACNET_STATUS_ERROR on error. + * @deprecated Use bacnet_authentication_factor_context_decode() instead */ int bacapp_decode_context_authentication_factor( const uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR *af) { - int len = 0; - int section_length; - - if (decode_is_opening_tag_number(&apdu[len], tag)) { - len++; - section_length = bacapp_decode_authentication_factor(&apdu[len], af); - - if (section_length == -1) { - len = -1; - } else { - len += section_length; - if (decode_is_closing_tag_number(&apdu[len], tag)) { - len++; - } else { - len = -1; - } - } - } else { - len = -1; - } - return len; + return bacnet_authentication_factor_context_decode(apdu, MAX_APDU, tag, af); } diff --git a/src/bacnet/authentication_factor.h b/src/bacnet/authentication_factor.h index 1eae77c5..cf3ac343 100644 --- a/src/bacnet/authentication_factor.h +++ b/src/bacnet/authentication_factor.h @@ -31,9 +31,23 @@ int bacapp_encode_authentication_factor( BACNET_STACK_EXPORT int bacapp_encode_context_authentication_factor( uint8_t *apdu, uint8_t tag, const BACNET_AUTHENTICATION_FACTOR *af); + +BACNET_STACK_EXPORT +int bacnet_authentication_factor_decode( + const uint8_t *apdu, unsigned apdu_size, BACNET_AUTHENTICATION_FACTOR *af); +BACNET_STACK_DEPRECATED("Use bacnet_authentication_factor_decode() instead") BACNET_STACK_EXPORT int bacapp_decode_authentication_factor( const uint8_t *apdu, BACNET_AUTHENTICATION_FACTOR *af); + +BACNET_STACK_EXPORT +int bacnet_authentication_factor_context_decode( + const uint8_t *apdu, + unsigned apdu_size, + uint8_t tag, + BACNET_AUTHENTICATION_FACTOR *af); +BACNET_STACK_DEPRECATED( + "Use bacnet_authentication_factor_context_decode() instead") BACNET_STACK_EXPORT int bacapp_decode_context_authentication_factor( const uint8_t *apdu, uint8_t tag, BACNET_AUTHENTICATION_FACTOR *af); diff --git a/src/bacnet/bacapp.c b/src/bacnet/bacapp.c index 64a247fe..8661761d 100644 --- a/src/bacnet/bacapp.c +++ b/src/bacnet/bacapp.c @@ -5010,6 +5010,35 @@ int bacapp_property_value_encode( return apdu_len; } +/** + * @brief Encode one BACnetPropertyValue value within context tags + * @param apdu Pointer to the buffer for encoded values, or NULL for length + * @param tag_number + * @param value Pointer to the service data used for encoding values + * @return Bytes encoded or zero on error. + */ +int bacapp_property_value_context_encode( + uint8_t *apdu, uint8_t tag_number, const BACNET_PROPERTY_VALUE *value) +{ + int len = 0; /* length of each encoding */ + int apdu_len = 0; /* total length of the apdu, return value */ + + len = encode_opening_tag(apdu, tag_number); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = bacapp_property_value_encode(apdu, value); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, tag_number); + apdu_len += len; + + return apdu_len; +} + /** * @brief Decode one BACnetPropertyValue value * @@ -5574,3 +5603,229 @@ bool bacapp_same_value( } return status; } + +/** + * @brief Encode a BACnetDeviceObjectPropertyValue into a buffer + * BACnetDeviceObjectPropertyValue ::= SEQUENCE { + * device-identifier [0] BACnetObjectIdentifier, + * object-identifier [1] BACnetObjectIdentifier, + * property-identifier [2] BACnetPropertyIdentifier, + * property-array-index [3] Unsigned OPTIONAL, + * -- used only with array datatype + * property-value [4] ABSTRACT-SYNTAX.&Type + * } + * @param apdu - the APDU buffer, or NULL for length + * @param value - BACnetDeviceObjectPropertyValue + * @return length of the APDU buffer + */ +int bacapp_device_object_property_value_encode( + uint8_t *apdu, const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value) +{ + int len = 0; + int apdu_len = 0; + + if (!value) { + return 0; + } + len = encode_context_object_id( + apdu, 0, value->device_identifier.type, + value->device_identifier.instance); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_object_id( + apdu, 1, value->object_identifier.type, + value->object_identifier.instance); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_context_enumerated(apdu, 2, value->property_identifier); + apdu_len += len; + if (apdu) { + apdu += len; + } + if (value->property_array_index != BACNET_ARRAY_ALL) { + len = encode_context_unsigned(apdu, 3, value->property_array_index); + apdu_len += len; + } + len = encode_opening_tag(apdu, 4); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = bacapp_encode_application_data(apdu, value->value); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 4); + apdu_len += len; + + return apdu_len; +} + +/** + * @brief Decode a BACnetDeviceObjectPropertyValue from a buffer + * BACnetDeviceObjectPropertyValue ::= SEQUENCE { + * device-identifier [0] BACnetObjectIdentifier, + * object-identifier [1] BACnetObjectIdentifier, + * property-identifier [2] BACnetPropertyIdentifier, + * property-array-index [3] Unsigned OPTIONAL, + * -- used only with array datatype + * property-value [4] ABSTRACT-SYNTAX.&Type + * } + * @param apdu - the APDU buffer + * @param apdu_size - the size of the APDU buffer + * @param value - BACnetDeviceObjectPropertyValue to decode into + * @return number of bytes decoded or BACNET_STATUS_ERROR on failure. + */ +int bacapp_device_object_property_value_decode( + uint8_t *apdu, + uint32_t apdu_size, + BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value) +{ + int apdu_len = 0; + int len = 0; + BACNET_UNSIGNED_INTEGER array_index = 0; + BACNET_OBJECT_TYPE object_type = 0; + uint32_t object_instance = 0; + uint32_t property_identifier = 0; + BACNET_APPLICATION_DATA_VALUE *property_value = NULL; + + if (!apdu) { + return BACNET_STATUS_ERROR; + } + /* device-identifier [0] BACnetObjectIdentifier */ + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &object_type, + &object_instance); + if (len > 0) { + apdu_len += len; + if (value) { + value->device_identifier.type = object_type; + value->device_identifier.instance = object_instance; + } + } else { + return BACNET_STATUS_ERROR; + } + /* object-identifier [1] BACnetObjectIdentifier */ + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &object_type, + &object_instance); + if (len > 0) { + apdu_len += len; + if (value) { + value->object_identifier.instance = object_instance; + value->object_identifier.type = object_type; + } + } else { + return len; + } + /* property-identifier [2] BACnetPropertyIdentifier */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &property_identifier); + if (len > 0) { + apdu_len += len; + if (value) { + value->property_identifier = property_identifier; + } + } else { + return BACNET_STATUS_ERROR; + } + /* property-array-index [3] Unsigned OPTIONAL */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &array_index); + if (len > 0) { + apdu_len += len; + if (value) { + value->property_array_index = array_index; + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* OPTIONAL - skip apdu_len increment */ + if (value) { + value->property_array_index = BACNET_ARRAY_ALL; + } + } + if (bacnet_is_opening_tag_number(apdu, apdu_size, 4, &len)) { + /* property-value [4] ABSTRACT-SYNTAX.&Type */ + apdu_len += len; + if (value) { + property_value = value->value; + } + len = bacapp_decode_application_data( + &apdu[apdu_len], apdu_size - apdu_len, property_value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (bacnet_is_closing_tag_number(apdu, apdu_size, 4, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } + + return apdu_len; +} + +/** + * @brief Compare the complex data of value1 and value2 + * @param value1 - value 1 structure + * @param value2 - value 2 structure + * @return true if the values are the same + */ +bool bacapp_device_object_property_value_same( + const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value1, + const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value2) +{ + const BACNET_APPLICATION_DATA_VALUE *data_value1, *data_value2; + if (value1 && value2) { + if (!bacnet_object_id_same( + value1->device_identifier.type, + value1->device_identifier.instance, + value2->device_identifier.type, + value2->device_identifier.instance)) { + return false; + } + if (!bacnet_object_id_same( + value1->object_identifier.type, + value1->object_identifier.instance, + value2->object_identifier.type, + value2->object_identifier.instance)) { + return false; + } + if (value1->property_identifier != value2->property_identifier) { + return false; + } + if (value1->property_array_index != value2->property_array_index) { + return false; + } + data_value1 = value1->value; + data_value2 = value2->value; + if (!bacapp_same_value(data_value1, data_value2)) { + return false; + } + return true; + } + + return false; +} + +/** + * @brief Copy the complex data of src to dest + * @param dest - destination structure + * @param src - source structure + */ +void bacapp_device_object_property_value_copy( + BACNET_DEVICE_OBJECT_PROPERTY_VALUE *dest, + const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *src) +{ + if (dest && src) { + memcpy(dest, src, sizeof(BACNET_DEVICE_OBJECT_PROPERTY_VALUE)); + } +} diff --git a/src/bacnet/bacapp.h b/src/bacnet/bacapp.h index 193cad59..397335d4 100644 --- a/src/bacnet/bacapp.h +++ b/src/bacnet/bacapp.h @@ -232,6 +232,18 @@ typedef struct BACnet_Object_Property_Value { BACNET_APPLICATION_DATA_VALUE *value; } BACNET_OBJECT_PROPERTY_VALUE; +struct BACnetDeviceObjectPropertyValue; +typedef struct BACnetDeviceObjectPropertyValue { + BACNET_OBJECT_ID device_identifier; + BACNET_OBJECT_ID object_identifier; + BACNET_PROPERTY_ID property_identifier; + /* optional array index */ + BACNET_ARRAY_INDEX property_array_index; + BACNET_APPLICATION_DATA_VALUE *value; + /* simple linked list */ + struct BACnetDeviceObjectPropertyValue *next; +} BACNET_DEVICE_OBJECT_PROPERTY_VALUE; + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -250,6 +262,9 @@ BACNET_STACK_EXPORT int bacapp_property_value_encode( uint8_t *apdu, const BACNET_PROPERTY_VALUE *value); BACNET_STACK_EXPORT +int bacapp_property_value_context_encode( + uint8_t *apdu, uint8_t tag_number, const BACNET_PROPERTY_VALUE *value); +BACNET_STACK_EXPORT int bacapp_property_value_decode( const uint8_t *apdu, uint32_t apdu_size, BACNET_PROPERTY_VALUE *value); BACNET_STACK_EXPORT @@ -259,6 +274,23 @@ int bacapp_object_property_value_decode( BACNET_PROPERTY_VALUE *value, BACNET_OBJECT_TYPE object_type); +BACNET_STACK_EXPORT +int bacapp_device_object_property_value_encode( + uint8_t *apdu, const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value); +BACNET_STACK_EXPORT +int bacapp_device_object_property_value_decode( + uint8_t *apdu, + uint32_t apdu_size, + BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value); +BACNET_STACK_EXPORT +bool bacapp_device_object_property_value_same( + const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value1, + const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *value2); +BACNET_STACK_EXPORT +void bacapp_device_object_property_value_copy( + BACNET_DEVICE_OBJECT_PROPERTY_VALUE *dest, + const BACNET_DEVICE_OBJECT_PROPERTY_VALUE *src); + BACNET_STACK_EXPORT int bacapp_encode_data( uint8_t *apdu, const BACNET_APPLICATION_DATA_VALUE *value); diff --git a/src/bacnet/bacenum.h b/src/bacnet/bacenum.h index 96261d59..a8595770 100644 --- a/src/bacnet/bacenum.h +++ b/src/bacnet/bacenum.h @@ -1309,12 +1309,14 @@ typedef enum BACnetEventType { EVENT_COMMAND_FAILURE = 3, EVENT_FLOATING_LIMIT = 4, EVENT_OUT_OF_RANGE = 5, - /* complex-event-type (6), -- see comment below */ + EVENT_COMPLEX_EVENT_TYPE = 6, /* -- see comment below */ /* event-buffer-ready (7), -- context tag 7 is deprecated */ EVENT_CHANGE_OF_LIFE_SAFETY = 8, EVENT_EXTENDED = 9, EVENT_BUFFER_READY = 10, EVENT_UNSIGNED_RANGE = 11, + /* -- context tag 12 is reserved for future addenda + since it conflicts with event-values[12] OPTIONAL */ EVENT_ACCESS_EVENT = 13, EVENT_DOUBLE_OUT_OF_RANGE = 14, EVENT_SIGNED_OUT_OF_RANGE = 15, @@ -1684,6 +1686,8 @@ typedef enum { BACNET_APPLICATION_TAG_CHANNEL_VALUE, /* BACnetLogRecord */ BACNET_APPLICATION_TAG_LOG_RECORD, + /* BACnetPropertyValue */ + BACNET_APPLICATION_TAG_PROPERTY_VALUE, /* BACnetTimerStateChangeValue */ BACNET_APPLICATION_TAG_TIMER_VALUE, /* no-value - context tagged null */ @@ -2625,10 +2629,13 @@ typedef enum BACnetAccessEvent { ACCESS_EVENT_DENIED_LOCKOUT = 161, ACCESS_EVENT_DENIED_VERIFICATION_FAILED = 162, ACCESS_EVENT_DENIED_VERIFICATION_TIMEOUT = 163, - ACCESS_EVENT_DENIED_OTHER = 164 + ACCESS_EVENT_DENIED_OTHER = 164, /* Enumerated values 0-511 are reserved for definition by ASHRAE. Enumerated values 512-65535 may be used by others subject to the procedures and constraints described in Clause 23. */ + ACCESS_EVENT_PROPRIETARY_MIN = 512, + ACCESS_EVENT_PROPRIETARY_MAX = 65535 + } BACNET_ACCESS_EVENT; /* Authentication Status */ @@ -2961,7 +2968,8 @@ typedef enum BACnetBackupState { typedef enum BACnetTimerState { TIMER_STATE_IDLE = 0, TIMER_STATE_RUNNING = 1, - TIMER_STATE_EXPIRED = 2 + TIMER_STATE_EXPIRED = 2, + TIMER_STATE_MAX = 3 } BACNET_TIMER_STATE; typedef enum BACnetTimerTransition { diff --git a/src/bacnet/bactext.c b/src/bacnet/bactext.c index 6c8176c7..1411acea 100644 --- a/src/bacnet/bactext.c +++ b/src/bacnet/bactext.c @@ -178,6 +178,8 @@ INDTEXT_DATA bacnet_application_tag_names[] = { { BACNET_APPLICATION_TAG_ACCESS_RULE, "BACnetAccessRule" }, { BACNET_APPLICATION_TAG_CHANNEL_VALUE, "BACnetChannelValue" }, { BACNET_APPLICATION_TAG_LOG_RECORD, "BACnetLogRecord" }, + { BACNET_APPLICATION_TAG_PROPERTY_VALUE, "BACnetPropertyValue" }, + { BACNET_APPLICATION_TAG_LOG_RECORD, "BACnetLogRecord" }, { BACNET_APPLICATION_TAG_NO_VALUE, "BACnetNoValue" }, { BACNET_APPLICATION_TAG_ABSTRACT_SYNTAX, "ABSTRACT-SYNTAX" }, { 0, NULL } diff --git a/src/bacnet/dcc.c b/src/bacnet/dcc.c index 4989e816..387f6c35 100644 --- a/src/bacnet/dcc.c +++ b/src/bacnet/dcc.c @@ -178,7 +178,7 @@ int dcc_apdu_encode( } /** - * @brief Encode the COVNotification service request + * @brief Encode the DeviceCommunicationControl service request * @param apdu Pointer to the buffer for encoding into * @param apdu_size number of bytes available in the buffer * @param timeDuration Optional time duration in minutes. diff --git a/src/bacnet/event.c b/src/bacnet/event.c index 6db65a69..76a90044 100644 --- a/src/bacnet/event.c +++ b/src/bacnet/event.c @@ -12,64 +12,12 @@ #include "bacnet/npdu.h" #include "bacnet/timestamp.h" #include "bacnet/authentication_factor.h" +#include "bacnet/bacapp.h" /** @file event.c Encode/Decode Event Notifications */ /** - * @brief Decode the array of complex-event-type notification parameters - * @param apdu - apdu buffer - * @param apdu_size - APDU total length - * @param data - the event data struct to store the results in - * @return number of apdu bytes decoded, or BACNET_STATUS_ERROR on error. - */ -static int complex_event_type_values_decode( - const uint8_t *apdu, - unsigned apdu_size, - BACNET_EVENT_NOTIFICATION_DATA *data) -{ - int len = 0; /* return value */ - BACNET_PROPERTY_VALUE *value; - int value_len = 0, tag_len = 0; - -#if (BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS == 1) - /* we want to extract the values */ - value = data->notificationParams.complexEventType.values; - bacapp_property_value_list_init( - value, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS); -#else - /* we just want to discard the complex values */ - BACNET_PROPERTY_VALUE dummyValue; - bacapp_property_value_list_init(&dummyValue, 1); - value = &dummyValue; -#endif - while (value != NULL) { - value_len = - bacapp_property_value_decode(&apdu[len], apdu_size - len, value); - if (value_len == BACNET_STATUS_ERROR) { - return BACNET_STATUS_ERROR; - } else { - len += value_len; - } - /* end of list? */ - if (bacnet_is_closing_tag_number( - &apdu[len], apdu_size - len, 6, &tag_len)) { - len += tag_len; - value->next = NULL; - break; - } - /* is there another one to decode? */ - value = value->next; - if (value == NULL) { - /* out of room to store next value */ - return BACNET_STATUS_ERROR; - } - } - - return len; -} - -/** - * @brief Encode the unconfirmed COVNotification service request + * @brief Encode the unconfirmed EventNotification service request * @param apdu Pointer to the buffer for encoding into * @param data Pointer to the service data used for encoding values * @return number of bytes encoded, or zero if unable to encode @@ -100,7 +48,7 @@ int uevent_notify_encode_apdu( } /** - * @brief Encode the confirmed COVNotification service request + * @brief Encode the ConfirmedEventNotification service request * @param apdu Pointer to the buffer for encoding into * @param invoke_id ID to invoke for notification * @param data Pointer to the service data used for encoding values @@ -135,8 +83,394 @@ int cevent_notify_encode_apdu( return apdu_len; } +#if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED /** - * @brief Encode the COVNotification service request + * @brief Encode the EXTENDED event parameter + * @param apdu Pointer to the buffer for encoding into + * @param data Pointer to the service data used for encoding values + * @return number of bytes encoded, or zero if unable to encode + */ +static int event_extended_parameter_encode( + uint8_t *apdu, const BACNET_EVENT_EXTENDED_PARAMETER *value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (!value) { + return 0; + } + switch (value->tag) { + case BACNET_APPLICATION_TAG_NULL: + if (apdu) { + apdu[0] = value->tag; + } + apdu_len++; + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = encode_application_boolean(apdu, value->type.Boolean); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = + encode_application_unsigned(apdu, value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = encode_application_signed(apdu, value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + apdu_len = encode_application_real(apdu, value->type.Real); + break; + case BACNET_APPLICATION_TAG_DOUBLE: + apdu_len = encode_application_double(apdu, value->type.Double); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = + encode_application_octet_string(apdu, value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = encode_application_character_string( + apdu, value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + apdu_len = + encode_application_bitstring(apdu, value->type.Bit_String); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = + encode_application_enumerated(apdu, value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + apdu_len = encode_application_date(apdu, &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + apdu_len = encode_application_time(apdu, &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = encode_application_object_id( + apdu, value->type.Object_Id.type, + value->type.Object_Id.instance); + break; + case BACNET_APPLICATION_TAG_PROPERTY_VALUE: + apdu_len = bacapp_property_value_context_encode( + apdu, 0, value->type.Property_Value); + break; + default: + break; + } + + return apdu_len; +} +#endif + +/** + * @brief Decode the EXTENDED event parameter from a buffer + * @param apdu - the APDU buffer + * @param apdu_size - the size of the APDU buffer + * @param value - BACnetDeviceObjectPropertyValue to decode into + * @return number of bytes decoded or BACNET_STATUS_ERROR on failure. + */ +static int event_extended_parameter_decode( + const uint8_t *apdu, + uint32_t apdu_size, + BACNET_EVENT_EXTENDED_PARAMETER *value) +{ + int len, apdu_len = 0; + BACNET_TAG tag = { 0 }; + + if (!value) { + return 0; + } + len = bacnet_tag_decode(&apdu[apdu_len], apdu_size - apdu_len, &tag); + if (len > 0) { + apdu_len += len; + if (tag.application) { + switch (tag.number) { + case BACNET_APPLICATION_TAG_NULL: + len = 0; + break; + case BACNET_APPLICATION_TAG_BOOLEAN: + value->type.Boolean = decode_boolean(tag.len_value_type); + len = 0; + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + len = bacnet_unsigned_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + len = bacnet_signed_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_REAL: + len = bacnet_real_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Real); + break; + case BACNET_APPLICATION_TAG_DOUBLE: + len = bacnet_double_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Double); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + len = bacnet_octet_string_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + len = bacnet_character_string_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + len = bacnet_bitstring_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, value->type.Bit_String); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + len = bacnet_enumerated_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + len = bacnet_date_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + len = bacnet_time_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + len = bacnet_object_id_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Object_Id.type, + &value->type.Object_Id.instance); + break; + default: + len = 0; + break; + } + if ((len == 0) && (tag.number != BACNET_APPLICATION_TAG_NULL) && + (tag.number != BACNET_APPLICATION_TAG_BOOLEAN) && + (tag.number != BACNET_APPLICATION_TAG_OCTET_STRING)) { + /* tags that have a valid zero length */ + /* indicate that we were not able to decode the value */ + value->tag = MAX_BACNET_APPLICATION_TAG; + } else { + value->tag = tag.number; + if (len >= 0) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + } else if (tag.opening) { + switch (tag.number) { + case 0: + len = bacapp_property_value_decode( + &apdu[apdu_len], apdu_size - apdu_len, + value->type.Property_Value); + if (len > 0) { + apdu_len += len; + value->tag = BACNET_APPLICATION_TAG_PROPERTY_VALUE; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, + tag.number, &len)) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; + default: + value->tag = MAX_BACNET_APPLICATION_TAG; + break; + } + } + } + + return apdu_len; +} + +#if BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED +/** + * @brief Encode the DISCRETE_VALUE event + * @param apdu Pointer to the buffer for encoding into + * @param data Pointer to the DISCRETE_VALUE used for encoding values + * @return number of bytes encoded, or zero if unable to encode + */ +static int event_discrete_value_encode( + uint8_t *apdu, const BACNET_EVENT_DISCRETE_VALUE *value) +{ + int apdu_len = 0; /* total length of the apdu, return value */ + + if (!value) { + return 0; + } + switch (value->tag) { + case BACNET_APPLICATION_TAG_BOOLEAN: + apdu_len = encode_application_boolean(apdu, value->type.Boolean); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + apdu_len = + encode_application_unsigned(apdu, value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + apdu_len = encode_application_signed(apdu, value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + apdu_len = + encode_application_octet_string(apdu, value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + apdu_len = encode_application_character_string( + apdu, value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + apdu_len = + encode_application_enumerated(apdu, value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + apdu_len = encode_application_date(apdu, &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + apdu_len = encode_application_time(apdu, &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + apdu_len = encode_application_object_id( + apdu, value->type.Object_Id.type, + value->type.Object_Id.instance); + break; + case BACNET_APPLICATION_TAG_DATETIME: + apdu_len = + bacapp_encode_context_datetime(apdu, 0, &value->type.Date_Time); + break; + default: + break; + } + + return apdu_len; +} +#endif + +/** + * @brief Decode a DISCRETE_VALUE from a buffer + * @param apdu - the APDU buffer + * @param apdu_size - the size of the APDU buffer + * @param value - DISCRETE_VALUE to decode into + * @return number of bytes decoded or BACNET_STATUS_ERROR on failure. + */ +static int event_discrete_value_decode( + const uint8_t *apdu, uint32_t apdu_size, BACNET_EVENT_DISCRETE_VALUE *value) +{ + int len, apdu_len = 0; + BACNET_TAG tag = { 0 }; + + if (!value) { + return 0; + } + len = bacnet_tag_decode(&apdu[apdu_len], apdu_size - apdu_len, &tag); + if (len > 0) { + apdu_len += len; + if (tag.application) { + switch (tag.number) { + case BACNET_APPLICATION_TAG_BOOLEAN: + len = 0; + value->type.Boolean = decode_boolean(tag.len_value_type); + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + len = bacnet_unsigned_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Unsigned_Int); + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + len = bacnet_signed_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Signed_Int); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + len = bacnet_octet_string_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, value->type.Octet_String); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + len = bacnet_character_string_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, value->type.Character_String); + break; + case BACNET_APPLICATION_TAG_ENUMERATED: + len = bacnet_enumerated_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Enumerated); + break; + case BACNET_APPLICATION_TAG_DATE: + len = bacnet_date_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Date); + break; + case BACNET_APPLICATION_TAG_TIME: + len = bacnet_time_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Time); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + len = bacnet_object_id_decode( + &apdu[apdu_len], apdu_size - apdu_len, + tag.len_value_type, &value->type.Object_Id.type, + &value->type.Object_Id.instance); + break; + default: + len = 0; + break; + } + if ((len == 0) && (tag.number != BACNET_APPLICATION_TAG_NULL) && + (tag.number != BACNET_APPLICATION_TAG_BOOLEAN) && + (tag.number != BACNET_APPLICATION_TAG_OCTET_STRING)) { + /* tags that have a valid zero length */ + /* indicate that we were not able to decode the value */ + value->tag = MAX_BACNET_APPLICATION_TAG; + } else { + value->tag = tag.number; + if (len >= 0) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } + } else if (tag.opening) { + switch (tag.number) { + case 0: + len = bacnet_datetime_decode( + &apdu[apdu_len], apdu_size - apdu_len, + &value->type.Date_Time); + if (len > 0) { + apdu_len += len; + value->tag = BACNET_APPLICATION_TAG_DATETIME; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, + tag.number, &len)) { + apdu_len += len; + } else { + apdu_len = BACNET_STATUS_ERROR; + } + } else { + apdu_len = BACNET_STATUS_ERROR; + } + break; + default: + value->tag = MAX_BACNET_APPLICATION_TAG; + break; + } + } + } + + return apdu_len; +} + +/** + * @brief Encode the EventNotification service request * @param apdu Pointer to the buffer for encoding into * @param data Pointer to the service data used for encoding values * @return number of bytes encoded, or zero if unable to encode @@ -146,6 +480,7 @@ int event_notify_encode_service_request( { int len = 0; /* length of each encoding */ int apdu_len = 0; /* total length of the apdu, return value */ + const BACNET_PROPERTY_VALUE *value = NULL; if (!data) { return 0; @@ -699,9 +1034,508 @@ int event_notify_encode_service_request( apdu += len; } break; +#if BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED + case EVENT_DOUBLE_OUT_OF_RANGE: + /* double-out-of-range[14] SEQUENCE */ + len = encode_opening_tag(apdu, 14); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* exceeding-value[0] Double */ + len = encode_context_double( + apdu, 0, + data->notificationParams.doubleOutOfRange + .exceedingValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags[1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.doubleOutOfRange.statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* deadband[2] Double */ + len = encode_context_double( + apdu, 2, + data->notificationParams.doubleOutOfRange.deadband); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* exceeded-limit[3] Double */ + len = encode_context_double( + apdu, 3, + data->notificationParams.doubleOutOfRange + .exceededLimit); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 14); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED + case EVENT_SIGNED_OUT_OF_RANGE: + /* signed-out-of-range[15] SEQUENCE */ + len = encode_opening_tag(apdu, 15); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* exceeding-value[0] Integer */ + len = encode_context_signed( + apdu, 0, + data->notificationParams.signedOutOfRange + .exceedingValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags[1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.signedOutOfRange.statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* deadband[2] Unsigned */ + len = encode_context_unsigned( + apdu, 2, + data->notificationParams.signedOutOfRange.deadband); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* exceeded-limit[3] Integer */ + len = encode_context_signed( + apdu, 3, + data->notificationParams.signedOutOfRange + .exceededLimit); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 15); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED + case EVENT_UNSIGNED_OUT_OF_RANGE: + /* unsigned-out-of-range[16] SEQUENCE */ + len = encode_opening_tag(apdu, EVENT_UNSIGNED_OUT_OF_RANGE); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* exceeding-value[0] Unsigned */ + len = encode_context_unsigned( + apdu, 0, + data->notificationParams.unsignedOutOfRange + .exceedingValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags[1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.unsignedOutOfRange + .statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* deadband[2] Unsigned */ + len = encode_context_unsigned( + apdu, 2, + data->notificationParams.unsignedOutOfRange.deadband); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* exceeded-limit[3] Unsigned */ + len = encode_context_unsigned( + apdu, 3, + data->notificationParams.unsignedOutOfRange + .exceededLimit); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, EVENT_UNSIGNED_OUT_OF_RANGE); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED + case EVENT_CHANGE_OF_CHARACTERSTRING: + len = encode_opening_tag( + apdu, EVENT_CHANGE_OF_CHARACTERSTRING); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* changed-value [0] CharacterString */ + len = encode_context_character_string( + apdu, 0, + data->notificationParams.changeOfCharacterstring + .changedValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags[1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.changeOfCharacterstring + .statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* alarm-value [2] CharacterString */ + len = encode_context_character_string( + apdu, 2, + data->notificationParams.changeOfCharacterstring + .alarmValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag( + apdu, EVENT_CHANGE_OF_CHARACTERSTRING); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED + case EVENT_CHANGE_OF_STATUS_FLAGS: + len = + encode_opening_tag(apdu, EVENT_CHANGE_OF_STATUS_FLAGS); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* present-value [0] ABSTRACT-SYNTAX.&Type OPTIONAL */ + if (data->notificationParams.changeOfStatusFlags + .presentValue.tag != + BACNET_APPLICATION_TAG_EMPTYLIST) { + len = encode_opening_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = event_extended_parameter_encode( + apdu, + &data->notificationParams.changeOfStatusFlags + .presentValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + /* referenced-flags[1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.changeOfStatusFlags + .referencedFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = + encode_closing_tag(apdu, EVENT_CHANGE_OF_STATUS_FLAGS); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + case EVENT_CHANGE_OF_RELIABILITY: + len = encode_opening_tag(apdu, EVENT_CHANGE_OF_RELIABILITY); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* reliability [0] BACnetReliability */ + len = encode_context_enumerated( + apdu, 0, + data->notificationParams.changeOfReliability + .reliability); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags [1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.changeOfReliability + .statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* property-values [2] SEQUENCE OF BACnetPropertyValue */ + len = encode_opening_tag(apdu, 2); + apdu_len += len; + if (apdu) { + apdu += len; + } + value = data->notificationParams.changeOfReliability + .propertyValues; + while (value) { + len = bacapp_property_value_encode(apdu, value); + apdu_len += len; + if (apdu) { + apdu += len; + } + value = value->next; + } + len = encode_closing_tag(apdu, 2); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, EVENT_CHANGE_OF_RELIABILITY); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED + case EVENT_CHANGE_OF_DISCRETE_VALUE: + /* change-of-discrete-value [21] SEQUENCE */ + len = encode_opening_tag( + apdu, EVENT_CHANGE_OF_DISCRETE_VALUE); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* new-value [0] CHOICE */ + len = encode_opening_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = event_discrete_value_encode( + apdu, + &data->notificationParams.changeOfDiscreteValue + .newValue); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 0); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags[1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.changeOfDiscreteValue + .statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag( + apdu, EVENT_CHANGE_OF_DISCRETE_VALUE); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + case EVENT_CHANGE_OF_TIMER: + /* change-of-timer [22] SEQUENCE */ + len = encode_opening_tag(apdu, EVENT_CHANGE_OF_TIMER); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* new-state [0] BACnetTimerState */ + len = encode_context_enumerated( + apdu, 0, + data->notificationParams.changeOfTimer.newState); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* status-flags [1] BACnetStatusFlags */ + len = encode_context_bitstring( + apdu, 1, + &data->notificationParams.changeOfTimer.statusFlags); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* update-time [2] BACnetDateTime */ + len = bacapp_encode_context_datetime( + apdu, 2, + &data->notificationParams.changeOfTimer.updateTime); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* last-state-change [3] BACnetTimerTransition OPTIONAL */ + if (data->notificationParams.changeOfTimer.lastStateChange < + TIMER_TRANSITION_MAX) { + len = encode_context_enumerated( + apdu, 3, + data->notificationParams.changeOfTimer + .lastStateChange); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + /* initial-timeout [4] Unsigned OPTIONAL */ + if (data->notificationParams.changeOfTimer.initialTimeout > + 0) { + len = encode_context_enumerated( + apdu, 4, + data->notificationParams.changeOfTimer + .initialTimeout); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + /* expiration-time [5] BACnetDateTime OPTIONAL */ + if (!datetime_wildcard( + &data->notificationParams.changeOfTimer + .expirationTime)) { + len = bacapp_encode_context_datetime( + apdu, 5, + &data->notificationParams.changeOfTimer + .expirationTime); + apdu_len += len; + if (apdu) { + apdu += len; + } + } + len = encode_closing_tag(apdu, EVENT_CHANGE_OF_TIMER); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif + case EVENT_NONE: + len = encode_opening_tag(apdu, EVENT_NONE); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, EVENT_NONE); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#if BACNET_EVENT_EXTENDED_ENABLED case EVENT_EXTENDED: + len = encode_opening_tag(apdu, EVENT_EXTENDED); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* vendor-id [0] Unsigned16 */ + len = encode_context_unsigned( + apdu, 0, data->notificationParams.extended.vendorID); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* extended-event-type [1] Unsigned */ + len = encode_context_unsigned( + apdu, 1, + data->notificationParams.extended.extendedEventType); + apdu_len += len; + if (apdu) { + apdu += len; + } + /* parameters [2] SEQUENCE OF CHOICE */ + len = encode_opening_tag(apdu, 2); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = event_extended_parameter_encode( + apdu, &data->notificationParams.extended.parameters); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, 2); + apdu_len += len; + if (apdu) { + apdu += len; + } + len = encode_closing_tag(apdu, EVENT_EXTENDED); + apdu_len += len; + if (apdu) { + apdu += len; + } + break; +#endif default: - assert(0); + if (data->eventType >= EVENT_PROPRIETARY_MIN && + data->eventType <= EVENT_PROPRIETARY_MAX) { + len = + encode_opening_tag(apdu, EVENT_COMPLEX_EVENT_TYPE); + apdu_len += len; + if (apdu) { + apdu += len; + } +#if BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS + value = + data->notificationParams.complexEventType.values; +#endif + while (value) { + len = bacapp_property_value_encode(apdu, value); + apdu_len += len; + if (apdu) { + apdu += len; + } + value = value->next; + } + len = + encode_closing_tag(apdu, EVENT_COMPLEX_EVENT_TYPE); + apdu_len += len; + if (apdu) { + apdu += len; + } + } else { + /* FIXME: add or enable an encoder for event type */ + assert(0); + } break; } len = encode_closing_tag(apdu, 12); @@ -740,665 +1574,1543 @@ size_t event_notification_service_request_encode( /** * @brief Decode the EventNotification service request only. + * @details Confirmed and Unconfirmed are the same encoding + * UnconfirmedEventNotification-Request ::= SEQUENCE { + * ConfirmedEventNotification-Request ::= SEQUENCE { + * process-identifier[0] Unsigned32, + * initiating-device-identifier[1] BACnetObjectIdentifier, + * event-object-identifier[2] BACnetObjectIdentifier, + * timestamp[3] BACnetTimeStamp, + * notification-class[4] Unsigned, + * priority[5] Unsigned8, + * event-type[6] BACnetEventType, + * message-text[7] CharacterString OPTIONAL, + * notify-type[8] BACnetNotifyType, + * ack-required[9] Boolean OPTIONAL, + * from-state[10] BACnetEventState OPTIONAL, + * to-state[11] BACnetEventState, + * event-values[12] BACnetNotificationParameters OPTIONAL + * } + * * @param apdu Pointer to the buffer. - * @param apdu_len Number of valid bytes in the buffer. + * @param apdu_size Number of valid bytes in the buffer. * @param data Pointer to the data to store the decoded values, or NULL * * @return Bytes decoded or BACNET_STATUS_ERROR on error. */ int event_notify_decode_service_request( const uint8_t *apdu, - unsigned apdu_len, + unsigned apdu_size, BACNET_EVENT_NOTIFICATION_DATA *data) { - int len = 0; /* return value */ - int section_length = 0; + int apdu_len = 0; /* return value */ + int len = 0, tag_len = 0; BACNET_UNSIGNED_INTEGER unsigned_value = 0; + BACNET_OBJECT_ID object_id = { 0 }; + BACNET_TIMESTAMP timestamp_value = { 0 }; + BACNET_CHARACTER_STRING *cstring = NULL; + BACNET_BIT_STRING *bstring = NULL; + BACNET_PROPERTY_STATE *property_state = NULL; + BACNET_NOTIFY_TYPE notify_type = NOTIFY_MAX; + BACNET_EVENT_TYPE event_type = EVENT_NONE; + BACNET_DEVICE_OBJECT_REFERENCE *dev_obj_ref = NULL; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE *dev_obj_prop_ref = NULL; + BACNET_AUTHENTICATION_FACTOR *auth_factor = NULL; + BACNET_PROPERTY_VALUE *property_value = NULL; + BACNET_EVENT_EXTENDED_PARAMETER *parameter_value = NULL; + BACNET_EVENT_DISCRETE_VALUE *discrete_value = NULL; + BACNET_DATE_TIME *datetime_value = NULL; + bool boolean_value = false; + float real_value = 0.0f; + double double_value = 0.0; + int32_t signed_value = 0; uint32_t enum_value = 0; - uint32_t len_value = 0; - uint8_t tag_number = 0; - bool is_complex_event_type = false; - if (apdu_len && data) { - /* tag 0 - processIdentifier */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 0, &unsigned_value); - if (section_length > 0) { - len += section_length; + if (apdu_size) { + /* process-identifier[0] Unsigned32 */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &unsigned_value); + if (len > 0) { + apdu_len += len; if (unsigned_value <= UINT32_MAX) { - data->processIdentifier = (uint32_t)unsigned_value; + if (data) { + data->processIdentifier = (uint32_t)unsigned_value; + } } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } - /* tag 1 - initiatingObjectIdentifier */ - if ((section_length = decode_context_object_id( - &apdu[len], 1, &data->initiatingObjectIdentifier.type, - &data->initiatingObjectIdentifier.instance)) == -1) { - return -1; + /* initiating-device-identifier[1] BACnetObjectIdentifier */ + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &object_id.type, + &object_id.instance); + if (len > 0) { + apdu_len += len; + if (data) { + data->initiatingObjectIdentifier.type = object_id.type; + data->initiatingObjectIdentifier.instance = object_id.instance; + } } else { - len += section_length; + return BACNET_STATUS_ERROR; } - /* tag 2 - eventObjectIdentifier */ - if ((section_length = decode_context_object_id( - &apdu[len], 2, &data->eventObjectIdentifier.type, - &data->eventObjectIdentifier.instance)) == -1) { - return -1; + /* event-object-identifier[2] BACnetObjectIdentifier */ + len = bacnet_object_id_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &object_id.type, + &object_id.instance); + if (len > 0) { + apdu_len += len; + if (data) { + data->eventObjectIdentifier.type = object_id.type; + data->eventObjectIdentifier.instance = object_id.instance; + } } else { - len += section_length; + return BACNET_STATUS_ERROR; } - /* tag 3 - timeStamp */ - if ((section_length = bacapp_decode_context_timestamp( - &apdu[len], 3, &data->timeStamp)) == -1) { - return -1; + /* timestamp[3] BACnetTimeStamp */ + len = bacnet_timestamp_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, ×tamp_value); + if (len > 0) { + apdu_len += len; + if (data) { + bacapp_timestamp_copy(&data->timeStamp, ×tamp_value); + } } else { - len += section_length; + return BACNET_STATUS_ERROR; } - /* tag 4 - noticicationClass */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 4, &unsigned_value); - if (section_length > 0) { - len += section_length; + /* notification-class[4] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, &unsigned_value); + if (len > 0) { + apdu_len += len; if (unsigned_value <= UINT32_MAX) { - data->notificationClass = (uint32_t)unsigned_value; + if (data) { + data->notificationClass = (uint32_t)unsigned_value; + } } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } - /* tag 5 - priority */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 5, &unsigned_value); - if (section_length > 0) { - len += section_length; + /* priority[5] Unsigned8 */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, &unsigned_value); + if (len > 0) { + apdu_len += len; if (unsigned_value <= UINT8_MAX) { - data->priority = (uint8_t)unsigned_value; + if (data) { + data->priority = (uint8_t)unsigned_value; + } } else { return BACNET_STATUS_ERROR; } } else { return BACNET_STATUS_ERROR; } - /* tag 6 - eventType */ - if ((section_length = - decode_context_enumerated(&apdu[len], 6, &enum_value)) == -1) { - return -1; - } else { - data->eventType = (BACNET_EVENT_TYPE)enum_value; - len += section_length; - } - /* tag 7 - messageText */ - - if (decode_is_context_tag(&apdu[len], 7)) { - if (data->messageText != NULL) { - if ((section_length = decode_context_character_string( - &apdu[len], 7, data->messageText)) == -1) { - /*FIXME This is an optional parameter */ - return -1; - } else { - len += section_length; + /* event-type[6] BACnetEventType */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 6, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value <= EVENT_PROPRIETARY_MAX) { + event_type = (BACNET_EVENT_TYPE)enum_value; + if (data) { + data->eventType = event_type; } } else { - return -1; + return BACNET_STATUS_ERROR; } } else { - if (data->messageText != NULL) { - characterstring_init_ansi(data->messageText, ""); - } + return BACNET_STATUS_ERROR; } - - /* tag 8 - notifyType */ - if ((section_length = - decode_context_enumerated(&apdu[len], 8, &enum_value)) == -1) { - return -1; + /* message-text[7] CharacterString OPTIONAL */ + if (data) { + cstring = data->messageText; } else { - data->notifyType = (BACNET_NOTIFY_TYPE)enum_value; - len += section_length; + cstring = NULL; } - switch (data->notifyType) { - case NOTIFY_ALARM: - case NOTIFY_EVENT: - /* tag 9 - ackRequired */ - section_length = - decode_context_boolean2(&apdu[len], 9, &data->ackRequired); - if (section_length == BACNET_STATUS_ERROR) { - return -1; + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 7, cstring); + if (len > 0) { + apdu_len += len; + } else if (len == 0) { + /* OPTIONAL - set default */ + characterstring_init_ansi(cstring, ""); + } else { + return BACNET_STATUS_ERROR; + } + /* notify-type[8] BACnetNotifyType */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 8, &enum_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notifyType = (BACNET_NOTIFY_TYPE)enum_value; + if (enum_value >= NOTIFY_MAX) { + enum_value = NOTIFY_MAX; } - len += section_length; - - /* tag 10 - fromState */ - if ((section_length = decode_context_enumerated( - &apdu[len], 10, &enum_value)) == -1) { - return -1; - } else { + notify_type = enum_value; + } + } else { + return BACNET_STATUS_ERROR; + } + if ((notify_type == NOTIFY_ALARM) || (notify_type == NOTIFY_EVENT)) { + /* ack-required[9] Boolean OPTIONAL */ + len = bacnet_boolean_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 9, &boolean_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->ackRequired = boolean_value; + } + } else if (len == 0) { + /* OPTIONAL - set default */ + if (data) { + data->ackRequired = false; + } + } else { + return BACNET_STATUS_ERROR; + } + /* from-state[10] BACnetEventState OPTIONAL */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 10, &enum_value); + if (len > 0) { + apdu_len += len; + if (data) { data->fromState = (BACNET_EVENT_STATE)enum_value; - len += section_length; } - break; - /* In cases other than alarm and event - there's no data, so do not return an error - but continue normally */ - case NOTIFY_ACK_NOTIFICATION: - default: - break; + } else if (len == 0) { + /* OPTIONAL - set default out of range */ + if (data) { + data->fromState = EVENT_STATE_MAX; + } + } else { + return BACNET_STATUS_ERROR; + } } - /* tag 11 - toState */ - if ((section_length = decode_context_enumerated( - &apdu[len], 11, &enum_value)) == -1) { - return -1; + /* to-state[11] BACnetEventState */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 11, &enum_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->toState = (BACNET_EVENT_STATE)enum_value; + } } else { - data->toState = (BACNET_EVENT_STATE)enum_value; - len += section_length; + return BACNET_STATUS_ERROR; } - /* tag 12 - eventValues */ - switch (data->notifyType) { - case NOTIFY_ALARM: - case NOTIFY_EVENT: - if (decode_is_opening_tag_number(&apdu[len], 12)) { - len++; - } else { - return -1; + } + if ((notify_type == NOTIFY_ALARM) || (notify_type == NOTIFY_EVENT)) { + /* event-values[12] BACnetNotificationParameters OPTIONAL */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 12, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (event_type >= EVENT_PROPRIETARY_MIN) { + /* complex-event-type [6] SEQUENCE OF BACnetPropertyValue */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, + EVENT_COMPLEX_EVENT_TYPE, &len)) { + apdu_len += len; + if (data) { +#if BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS + property_value = + data->notificationParams.complexEventType.values; +#endif } - if (decode_is_opening_tag_number( - &apdu[len], (uint8_t)data->eventType)) { - len++; - switch (data->eventType) { - case EVENT_CHANGE_OF_BITSTRING: - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 0, - &data->notificationParams.changeOfBitstring - .referencedBitString))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.changeOfBitstring - .statusFlags))) { - return -1; - } - len += section_length; - - break; - - case EVENT_CHANGE_OF_STATE: - if (-1 == - (section_length = - bacapp_decode_context_property_state( - &apdu[len], 0, - &data->notificationParams.changeOfState - .newState))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.changeOfState - .statusFlags))) { - return -1; - } - len += section_length; - - break; - - case EVENT_CHANGE_OF_VALUE: - if (!decode_is_opening_tag_number(&apdu[len], 0)) { - return -1; - } - len++; - - if (decode_is_context_tag( - &apdu[len], CHANGE_OF_VALUE_BITS)) { - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 0, - &data->notificationParams.changeOfValue - .newValue.changedBits))) { - return -1; - } - - len += section_length; - data->notificationParams.changeOfValue.tag = - CHANGE_OF_VALUE_BITS; - } else if (decode_is_context_tag( - &apdu[len], CHANGE_OF_VALUE_REAL)) { - if (-1 == - (section_length = decode_context_real( - &apdu[len], 1, - &data->notificationParams.changeOfValue - .newValue.changeValue))) { - return -1; - } - - len += section_length; + bacapp_property_value_list_init( + property_value, BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS); + while (apdu_len < apdu_size) { + len = bacapp_property_value_decode( + &apdu[apdu_len], apdu_size - apdu_len, property_value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* end of list? */ + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, + EVENT_COMPLEX_EVENT_TYPE, &len)) { + apdu_len += len; + if (property_value) { + /* mark the end of the list */ + property_value->next = NULL; + } + break; + } + /* is there another slot in the data store? */ + if (property_value) { + property_value = property_value->next; + } + } + } else { + return BACNET_STATUS_ERROR; + } + } else if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, + (uint8_t)event_type, &len)) { + /* BACnetNotificationParameters */ + apdu_len += len; + switch (event_type) { + case EVENT_CHANGE_OF_BITSTRING: + /* change-of-bitstring [0] SEQUENCE */ + /* referenced-bitstring[0] BitString */ + if (data) { + bstring = &data->notificationParams.changeOfBitstring + .referencedBitString; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = &data->notificationParams.changeOfBitstring + .statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_CHANGE_OF_STATE: + /* change-of-state [1] SEQUENCE */ + /* new-state[0] BACnetEventState */ + if (data) { + property_state = + &data->notificationParams.changeOfState.newState; + } else { + property_state = NULL; + } + len = bacapp_decode_context_property_state( + &apdu[apdu_len], 0, property_state); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = + &data->notificationParams.changeOfState.statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_CHANGE_OF_VALUE: + /* change-of-value [2] SEQUENCE */ + /* new-value [0] CHOICE */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* changed-bits[0] BitString */ + if (data) { + bstring = &data->notificationParams.changeOfValue + .newValue.changedBits; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, bstring); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.changeOfValue.tag = + CHANGE_OF_VALUE_BITS; + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } else { + /* changed-value[1] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, + &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.changeOfValue.newValue + .changeValue = real_value; data->notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; - } else { - return -1; } - if (!decode_is_closing_tag_number(&apdu[len], 0)) { - return -1; + } else { + return BACNET_STATUS_ERROR; + } + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags [1] BACnetStatusFlags*/ + if (data) { + bstring = + &data->notificationParams.changeOfValue.statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_COMMAND_FAILURE: + /* command-failure [3] SEQUENCE */ + /* command-value [0] ABSTRACT-SYNTAX.&Type + -- depends on ref property */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + len = bacnet_enumerated_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &enum_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.commandFailure.commandValue + .binaryValue = enum_value; + data->notificationParams.commandFailure.tag = + COMMAND_FAILURE_BINARY_PV; + } + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } + if (len == 0) { + len = bacnet_unsigned_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.commandFailure + .commandValue.unsignedValue = + unsigned_value; + data->notificationParams.commandFailure.tag = + COMMAND_FAILURE_UNSIGNED; } - len++; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.changeOfValue - .statusFlags))) { - return -1; + } else { + return BACNET_STATUS_ERROR; + } + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = &data->notificationParams.commandFailure + .statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* feedback-value[2] ABSTRACT-SYNTAX.&Type + -- depends on ref property */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + if (data->notificationParams.commandFailure.tag == + COMMAND_FAILURE_BINARY_PV) { + len = bacnet_enumerated_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, &enum_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.commandFailure + .feedbackValue.binaryValue = enum_value; } - len += section_length; - break; - - case EVENT_COMMAND_FAILURE: - if (!decode_is_opening_tag_number(&apdu[len], 0)) { - return -1; + } else if (len < 0) { + return BACNET_STATUS_ERROR; + } + } else { + len = bacnet_unsigned_application_decode( + &apdu[apdu_len], apdu_size - apdu_len, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.commandFailure + .feedbackValue.unsignedValue = + unsigned_value; } - len++; + } else { + return BACNET_STATUS_ERROR; + } + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_FLOATING_LIMIT: + /* floating-limit [4] SEQUENCE*/ + /* reference-value[0] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.floatingLimit + .referenceValue = real_value; + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = + &data->notificationParams.floatingLimit.statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* setpoint-value[2] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.floatingLimit + .setPointValue = real_value; + } + } else { + return BACNET_STATUS_ERROR; + } + /* error-limit[3] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.floatingLimit.errorLimit = + real_value; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_OUT_OF_RANGE: + /* out-of-range [5] SEQUENCE */ + /* exceeding-value[0] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.outOfRange.exceedingValue = + real_value; + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = + &data->notificationParams.outOfRange.statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* deadband[2] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.outOfRange.deadband = + real_value; + } + } else { + return BACNET_STATUS_ERROR; + } + /* exceeded-limit[3] Real */ + len = bacnet_real_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &real_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.outOfRange.exceededLimit = + real_value; + } + } else { + return BACNET_STATUS_ERROR; + } + break; - if (-1 == - (section_length = decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value))) { - return -1; - } - len += section_length; - - switch (tag_number) { - case BACNET_APPLICATION_TAG_ENUMERATED: - if (-1 == - (section_length = decode_enumerated( - &apdu[len], len_value, - &enum_value))) { - return -1; - } - data->notificationParams.commandFailure - .commandValue.binaryValue = enum_value; - break; - - case BACNET_APPLICATION_TAG_UNSIGNED_INT: - if (-1 == - (section_length = decode_unsigned( - &apdu[len], len_value, - &data->notificationParams - .commandFailure.commandValue - .unsignedValue))) { - return -1; - } - break; - - default: - return 0; - } - len += section_length; - - if (!decode_is_closing_tag_number(&apdu[len], 0)) { - return -1; - } - len++; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.commandFailure - .statusFlags))) { - return -1; - } - len += section_length; - - if (!decode_is_opening_tag_number(&apdu[len], 2)) { - return -1; - } - len++; - - if (-1 == - (section_length = decode_tag_number_and_value( - &apdu[len], &tag_number, &len_value))) { - return -1; - } - len += section_length; - - switch (tag_number) { - case BACNET_APPLICATION_TAG_ENUMERATED: - if (-1 == - (section_length = decode_enumerated( - &apdu[len], len_value, - &enum_value))) { - return -1; - } - data->notificationParams.commandFailure - .feedbackValue.binaryValue = enum_value; - break; - - case BACNET_APPLICATION_TAG_UNSIGNED_INT: - if (-1 == - (section_length = decode_unsigned( - &apdu[len], len_value, - &data->notificationParams - .commandFailure.feedbackValue - .unsignedValue))) { - return -1; - } - break; - - default: - return 0; - } - len += section_length; - - if (!decode_is_closing_tag_number(&apdu[len], 2)) { - return -1; - } - len++; - - break; - - case EVENT_FLOATING_LIMIT: - if (-1 == - (section_length = decode_context_real( - &apdu[len], 0, - &data->notificationParams.floatingLimit - .referenceValue))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.floatingLimit - .statusFlags))) { - return -1; - } - len += section_length; - if (-1 == - (section_length = decode_context_real( - &apdu[len], 2, - &data->notificationParams.floatingLimit - .setPointValue))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_real( - &apdu[len], 3, - &data->notificationParams.floatingLimit - .errorLimit))) { - return -1; - } - len += section_length; - break; - - case EVENT_OUT_OF_RANGE: - if (-1 == - (section_length = decode_context_real( - &apdu[len], 0, - &data->notificationParams.outOfRange - .exceedingValue))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.outOfRange - .statusFlags))) { - return -1; - } - len += section_length; - if (-1 == - (section_length = decode_context_real( - &apdu[len], 2, - &data->notificationParams.outOfRange - .deadband))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_real( - &apdu[len], 3, - &data->notificationParams.outOfRange - .exceededLimit))) { - return -1; - } - len += section_length; - break; - - case EVENT_CHANGE_OF_LIFE_SAFETY: - if (-1 == - (section_length = decode_context_enumerated( - &apdu[len], 0, &enum_value))) { - return -1; - } + case EVENT_CHANGE_OF_LIFE_SAFETY: + /* change-of-life-safety [8] SEQUENCE */ + /* new-state[0] BACnetLifeSafetyState */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value > LIFE_SAFETY_STATE_PROPRIETARY_MAX) { + enum_value = LIFE_SAFETY_STATE_PROPRIETARY_MAX; + } + if (data) { data->notificationParams.changeOfLifeSafety .newState = (BACNET_LIFE_SAFETY_STATE)enum_value; - len += section_length; - - if (-1 == - (section_length = decode_context_enumerated( - &apdu[len], 1, &enum_value))) { - return -1; - } + } + } else { + return BACNET_STATUS_ERROR; + } + /* new-mode[1] BACnetLifeSafetyMode */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value > LIFE_SAFETY_MODE_PROPRIETARY_MAX) { + enum_value = LIFE_SAFETY_MODE_PROPRIETARY_MAX; + } + if (data) { data->notificationParams.changeOfLifeSafety .newMode = (BACNET_LIFE_SAFETY_MODE)enum_value; - len += section_length; - - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 2, - &data->notificationParams - .changeOfLifeSafety.statusFlags))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = decode_context_enumerated( - &apdu[len], 3, &enum_value))) { - return -1; - } + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[2] BACnetStatusFlags */ + if (data) { + bstring = &data->notificationParams.changeOfLifeSafety + .statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* operation-expected[3] BACnetLifeSafetyOperation */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value > LIFE_SAFETY_OP_PROPRIETARY_MAX) { + enum_value = LIFE_SAFETY_OP_PROPRIETARY_MAX; + } + if (data) { data->notificationParams.changeOfLifeSafety .operationExpected = (BACNET_LIFE_SAFETY_OPERATION)enum_value; - len += section_length; - break; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_EXTENDED: + /* extended [9] SEQUENCE */ + /* vendor-id[0] Unsigned16 */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (unsigned_value <= UINT16_MAX) { +#if BACNET_EVENT_EXTENDED_ENABLED + if (data) { + data->notificationParams.extended.vendorID = + (uint16_t)unsigned_value; + } +#endif + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* extended-event-type[1] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_EXTENDED_ENABLED + data->notificationParams.extended + .extendedEventType = unsigned_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* parameters[2] SEQUENCE OF CHOICE */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; +#if BACNET_EVENT_EXTENDED_ENABLED + if (data) { + parameter_value = + &data->notificationParams.extended.parameters; + } else +#endif + { + parameter_value = NULL; + } + len = event_extended_parameter_decode( + &apdu[apdu_len], apdu_size - apdu_len, + parameter_value); + 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)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_BUFFER_READY: + /* buffer-ready [10] SEQUENCE */ + /* buffer-property[0] BACnetDeviceObjectPropertyReference */ + if (data) { + dev_obj_prop_ref = &data->notificationParams.bufferReady + .bufferProperty; + } else { + dev_obj_prop_ref = NULL; + } + len = + bacnet_device_object_property_reference_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + dev_obj_prop_ref); + if (len <= 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + /* previous-notification[1] Unsigned32 */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (unsigned_value <= UINT32_MAX) { + if (data) { + data->notificationParams.bufferReady + .previousNotification = + (uint32_t)unsigned_value; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* current-notification[2] Unsigned32 */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (unsigned_value <= UINT32_MAX) { + if (data) { + data->notificationParams.bufferReady + .currentNotification = + (uint32_t)unsigned_value; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_UNSIGNED_RANGE: + /* unsigned-range[11] SEQUENCE*/ + /* exceeding-value[0] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (unsigned_value <= UINT32_MAX) { + if (data) { + data->notificationParams.unsignedRange + .exceedingValue = (uint32_t)unsigned_value; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = + &data->notificationParams.unsignedRange.statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* exceeded-limit[2] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (unsigned_value <= UINT32_MAX) { + if (data) { + data->notificationParams.unsignedRange + .exceededLimit = (uint32_t)unsigned_value; + } + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; + } + break; - case EVENT_BUFFER_READY: - /* Tag 0 - bufferProperty */ - section_length = - bacnet_device_object_property_reference_context_decode( - &apdu[len], apdu_len - len, 0, - &data->notificationParams.bufferReady - .bufferProperty); - if (section_length <= 0) { - return -1; - } - len += section_length; - /* Tag 1 - PreviousNotification */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 1, &unsigned_value); - if (section_length > 0) { - len += section_length; - if (unsigned_value <= UINT32_MAX) { - data->notificationParams.bufferReady - .previousNotification = - (uint32_t)unsigned_value; - } else { - return BACNET_STATUS_ERROR; - } - } else { - return BACNET_STATUS_ERROR; - } - /* Tag 2 - currentNotification */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 2, &unsigned_value); - if (section_length > 0) { - len += section_length; - if (unsigned_value <= UINT32_MAX) { - data->notificationParams.bufferReady - .currentNotification = - (uint32_t)unsigned_value; - } else { - return BACNET_STATUS_ERROR; - } - } else { - return BACNET_STATUS_ERROR; - } - break; - - case EVENT_UNSIGNED_RANGE: - /* Tag 0 - PreviousNotification */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 0, &unsigned_value); - if (section_length > 0) { - len += section_length; - if (unsigned_value <= UINT32_MAX) { - data->notificationParams.unsignedRange - .exceedingValue = - (uint32_t)unsigned_value; - } else { - return BACNET_STATUS_ERROR; - } - } else { - return BACNET_STATUS_ERROR; - } - /* Tag 1 - statusFlags */ - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.unsignedRange - .statusFlags))) { - return -1; - } - len += section_length; - /* Tag 2 - exceededLimit */ - section_length = bacnet_unsigned_context_decode( - &apdu[len], apdu_len - len, 2, &unsigned_value); - if (section_length > 0) { - len += section_length; - if (unsigned_value <= UINT32_MAX) { - data->notificationParams.unsignedRange - .exceededLimit = - (uint32_t)unsigned_value; - } else { - return BACNET_STATUS_ERROR; - } - } else { - return BACNET_STATUS_ERROR; - } - break; - - case EVENT_ACCESS_EVENT: - if (-1 == - (section_length = decode_context_enumerated( - &apdu[len], 0, &enum_value))) { - return -1; - } + case EVENT_ACCESS_EVENT: + /* access-event[13] SEQUENCE */ + /* access-event[0] BACnetAccessEvent */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value > ACCESS_EVENT_PROPRIETARY_MAX) { + enum_value = ACCESS_EVENT_PROPRIETARY_MAX; + } + if (data) { data->notificationParams.accessEvent.accessEvent = - enum_value; - len += section_length; - if (-1 == - (section_length = decode_context_bitstring( - &apdu[len], 1, - &data->notificationParams.accessEvent - .statusFlags))) { - return -1; - } - len += section_length; + (BACNET_ACCESS_EVENT)enum_value; + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ + if (data) { + bstring = + &data->notificationParams.accessEvent.statusFlags; + } else { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* access-event-tag[2] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { + data->notificationParams.accessEvent + .accessEventTag = unsigned_value; + } + } else { + return BACNET_STATUS_ERROR; + } + /* access-event-time[3] BACnetTimeStamp */ + len = bacnet_timestamp_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, + ×tamp_value); + if (len > 0) { + apdu_len += len; + if (data) { + bacapp_timestamp_copy( + &data->notificationParams.accessEvent + .accessEventTime, + ×tamp_value); + } + } else { + return BACNET_STATUS_ERROR; + } + /* access-credential[4] BACnetDeviceObjectReference */ + if (data) { + dev_obj_ref = &data->notificationParams.accessEvent + .accessCredential; + } else { + dev_obj_ref = NULL; + } + len = bacnet_device_object_reference_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, dev_obj_ref); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* authentication-factor[5] BACnetAuthenticationFactor + * OPTIONAL */ + if (data) { + auth_factor = &data->notificationParams.accessEvent + .authenticationFactor; + } else { + auth_factor = NULL; + } + len = bacnet_authentication_factor_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, auth_factor); + if (len > 0) { + apdu_len += len; + } else if (len == 0) { + /* OPTIONAL - set default */ + if (data) { + data->notificationParams.accessEvent + .authenticationFactor.format_type = + AUTHENTICATION_FACTOR_MAX; + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_DOUBLE_OUT_OF_RANGE: + /* double-out-of-range[14] SEQUENCE */ + /* exceeding-value[0] Double */ + len = bacnet_double_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &double_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED + data->notificationParams.doubleOutOfRange + .exceedingValue = double_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ +#if BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED + if (data) { + bstring = &data->notificationParams.doubleOutOfRange + .statusFlags; - if (-1 == - (section_length = decode_context_unsigned( - &apdu[len], 2, - &data->notificationParams.accessEvent - .accessEventTag))) { - return -1; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* deadband[2] Double */ + len = bacnet_double_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &double_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED + data->notificationParams.doubleOutOfRange.deadband = + double_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* exceeded-limit[3] Double */ + len = bacnet_double_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, + &double_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED + data->notificationParams.doubleOutOfRange + .exceededLimit = double_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_SIGNED_OUT_OF_RANGE: + /* signed-out-of-range[15] SEQUENCE */ + /* exceeding-value[0] Integer */ + len = bacnet_signed_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &signed_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED + data->notificationParams.signedOutOfRange + .exceedingValue = signed_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ +#if BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED + if (data) { + bstring = &data->notificationParams.signedOutOfRange + .statusFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* deadband[2] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED + data->notificationParams.signedOutOfRange.deadband = + unsigned_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* exceeded-limit[3] Integer */ + len = bacnet_signed_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, + &signed_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED + data->notificationParams.signedOutOfRange + .exceededLimit = signed_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_UNSIGNED_OUT_OF_RANGE: + /* unsigned-out-of-range[16] SEQUENCE */ + /* exceeding-value[0] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED + data->notificationParams.unsignedOutOfRange + .exceedingValue = unsigned_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ +#if BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED + if (data) { + bstring = &data->notificationParams.unsignedOutOfRange + .statusFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* deadband[2] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED + data->notificationParams.unsignedOutOfRange + .deadband = unsigned_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* exceeded-limit[3] Unsigned */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED + data->notificationParams.unsignedOutOfRange + .exceededLimit = unsigned_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_CHANGE_OF_CHARACTERSTRING: + /* change-of-characterstring [17] SEQUENCE */ + /* changed-value[0] CharacterString */ +#if BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED + if (data) { + cstring = data->notificationParams + .changeOfCharacterstring.changedValue; + } else +#endif + { + cstring = NULL; + } + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, cstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ +#if BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED + if (data) { + bstring = &data->notificationParams + .changeOfCharacterstring.statusFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* alarm-value[2] CharacterString */ +#if BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED + if (data) { + cstring = data->notificationParams + .changeOfCharacterstring.alarmValue; + } else +#endif + { + cstring = NULL; + } + len = bacnet_character_string_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, cstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_CHANGE_OF_STATUS_FLAGS: + /* change-of-status-flags[18] SEQUENCE */ + /* present-value[0] ABSTRACT-SYNTAX.&Type OPTIONAL, + -- depends on referenced property */ +#if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED + if (data) { + parameter_value = + &data->notificationParams.changeOfStatusFlags + .presentValue; + } else +#endif + { + parameter_value = NULL; + } + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; + len = event_extended_parameter_decode( + &apdu[apdu_len], apdu_size - apdu_len, + parameter_value); + if (len < 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + /* OPTIONAL */ + if (parameter_value) { +#if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED + parameter_value->tag = + BACNET_APPLICATION_TAG_EMPTYLIST; +#endif + } + } + /* referenced-flags[1] BACnetStatusFlags*/ +#if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED + if (data) { + bstring = &data->notificationParams.changeOfStatusFlags + .referencedFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_CHANGE_OF_RELIABILITY: + /* change-of-reliability[19] SEQUENCE */ + /* reliability[0] BACnetReliability */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value > RELIABILITY_PROPRIETARY_MAX) { + enum_value = RELIABILITY_PROPRIETARY_MAX; + } +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + if (data) { + data->notificationParams.changeOfReliability + .reliability = enum_value; + } +#endif + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + if (data) { + bstring = &data->notificationParams.changeOfReliability + .statusFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* property-values[2] SEQUENCE OF BACnetPropertyValue */ +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + if (data) { + property_value = + data->notificationParams.changeOfReliability + .propertyValues; + } else +#endif + { + property_value = NULL; + } + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, &len)) { + apdu_len += len; + while (apdu_len < apdu_size) { + len = bacapp_property_value_decode( + &apdu[apdu_len], apdu_size - apdu_len, + property_value); + if (len >= 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; } - len += section_length; - - if (-1 == - (section_length = - bacapp_decode_context_timestamp( - &apdu[len], 3, - &data->notificationParams.accessEvent - .accessEventTime))) { - return -1; - } - len += section_length; - - if (-1 == - (section_length = - bacapp_decode_context_device_obj_ref( - &apdu[len], 4, - &data->notificationParams.accessEvent - .accessCredential))) { - return -1; - } - len += section_length; - - if (!decode_is_closing_tag(&apdu[len])) { - if (-1 == - (section_length = - bacapp_decode_context_authentication_factor( - &apdu[len], 5, - &data->notificationParams - .accessEvent - .authenticationFactor))) { - return -1; + /* end of list? */ + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 2, + &len)) { + apdu_len += len; +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + if (property_value) { + /* mark the end of the list */ + property_value->next = NULL; } - len += section_length; +#endif + break; } - break; - - default: - return -1; +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + if (property_value) { + /* load the next value store */ + property_value = property_value->next; + } +#endif + } } - } else if (decode_is_opening_tag_number(&apdu[len], 6)) { - /* complex-event-type [6] SEQUENCE OF BACnetPropertyValue */ - len++; - is_complex_event_type = true; - - len = complex_event_type_values_decode( - &apdu[len], apdu_len - len, data); - if (len < 0) { - return -1; + break; + case EVENT_NONE: + /* -- CHOICE [20] has been intentionally omitted. + It parallels the 'none' event type CHOICE[20] of + the BACnetEventParameter production which was + introduced for the case an object does not apply + an event algorithm */ + break; + case EVENT_CHANGE_OF_DISCRETE_VALUE: + /* change-of-discrete-value [21] SEQUENCE */ + /* new-value [0] CHOICE */ + if (bacnet_is_opening_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, &len)) { + apdu_len += len; +#if BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED + if (data) { + discrete_value = + &data->notificationParams.changeOfDiscreteValue + .newValue; + } else +#endif + { + discrete_value = NULL; + } + len = event_discrete_value_decode( + &apdu[apdu_len], apdu_size - apdu_len, + discrete_value); + if (len < 0) { + return BACNET_STATUS_ERROR; + } + apdu_len += len; + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 0, + &tag_len)) { + apdu_len += tag_len; + } else { + return BACNET_STATUS_ERROR; + } + } else { + return BACNET_STATUS_ERROR; } - } else { - return -1; - } - - if (decode_is_closing_tag_number( - &apdu[len], - is_complex_event_type ? 6 : (uint8_t)data->eventType)) { - len++; - } else { - return -1; - } - if (decode_is_closing_tag_number(&apdu[len], 12)) { - len++; - } else { - return -1; - } - break; - /* In cases other than alarm and event - there's no data, so do not return an error - but continue normally */ - case NOTIFY_ACK_NOTIFICATION: - default: - break; + /* status-flags[1] BACnetStatusFlags*/ +#if BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED + if (data) { + bstring = &data->notificationParams + .changeOfDiscreteValue.statusFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + break; + case EVENT_CHANGE_OF_TIMER: + /* change-of-timer [22] SEQUENCE */ + /* new-state[0] BACnetTimerState */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 0, &enum_value); + if (len > 0) { + apdu_len += len; + if (data) { + if (enum_value > TIMER_STATE_MAX) { + enum_value = TIMER_STATE_MAX; + } +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + data->notificationParams.changeOfTimer.newState = + enum_value; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* status-flags[1] BACnetStatusFlags */ +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + if (data) { + bstring = + &data->notificationParams.changeOfTimer.statusFlags; + } else +#endif + { + bstring = NULL; + } + len = bacnet_bitstring_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 1, bstring); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* update-time[2] BACnetDateTime */ +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + if (data) { + datetime_value = + &data->notificationParams.changeOfTimer.updateTime; + } else +#endif + { + datetime_value = NULL; + } + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 2, + datetime_value); + if (len > 0) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + /* last-state-change[3] BACnetTimerTransition OPTIONAL */ + len = bacnet_enumerated_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 3, &enum_value); + if (len > 0) { + apdu_len += len; + if (enum_value > TIMER_TRANSITION_MAX) { + enum_value = TIMER_TRANSITION_MAX; + } +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + data->notificationParams.changeOfTimer.lastStateChange = + enum_value; +#endif + } else if (len == 0) { + /* OPTIONAL - set default */ +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + data->notificationParams.changeOfTimer.lastStateChange = + TIMER_TRANSITION_MAX; +#endif + } else { + return BACNET_STATUS_ERROR; + } + /* initial-timeout[4] Unsigned OPTIONAL */ + len = bacnet_unsigned_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 4, + &unsigned_value); + if (len > 0) { + apdu_len += len; + if (data) { +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + data->notificationParams.changeOfTimer + .initialTimeout = unsigned_value; +#endif + } + } else if (len == 0) { + /* OPTIONAL - set default */ + if (data) { +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + data->notificationParams.changeOfTimer + .initialTimeout = 0; +#endif + } + } else { + return BACNET_STATUS_ERROR; + } + /* expiration-time[5] BACnetDateTime OPTIONAL */ +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + if (data) { + datetime_value = &data->notificationParams.changeOfTimer + .expirationTime; + } else +#endif + { + datetime_value = NULL; + } + len = bacnet_datetime_context_decode( + &apdu[apdu_len], apdu_size - apdu_len, 5, + datetime_value); + if (len > 0) { + apdu_len += len; + } else if (len == 0) { + /* OPTIONAL - set default */ + datetime_wildcard_set(datetime_value); + } else { + return BACNET_STATUS_ERROR; + } + break; + default: + return BACNET_STATUS_ERROR; + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, (uint8_t)event_type, + &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; + } + } + if (bacnet_is_closing_tag_number( + &apdu[apdu_len], apdu_size - apdu_len, 12, &len)) { + apdu_len += len; + } else { + return BACNET_STATUS_ERROR; } } - return len; + return apdu_len; } diff --git a/src/bacnet/event.h b/src/bacnet/event.h index c263b4de..6e7718fc 100644 --- a/src/bacnet/event.h +++ b/src/bacnet/event.h @@ -34,10 +34,47 @@ typedef enum { ** Based on UnconfirmedEventNotification-Request */ -/** Enable decoding of complex-event-type property-values. If set to 0, the - * values are decoded and discarded. */ +/** Enable decoding of certain large event parameters. + * If set to 0, the values are decoded and discarded, + * and the encoders are disabled. */ +#ifndef BACNET_EVENT_EXTENDED_ENABLED +#define BACNET_EVENT_EXTENDED_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED +#define BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED +#define BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED +#define BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED +#define BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED +#define BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED +#define BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED +#define BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED 0 +#endif + +#ifndef BACNET_EVENT_CHANGE_OF_TIMER_ENABLED +#define BACNET_EVENT_CHANGE_OF_TIMER_ENABLED 0 +#endif + #ifndef BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS -#define BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS 1 +#define BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS 0 #endif /** Max complex-event-type property-values to decode. Events with more values @@ -46,6 +83,43 @@ typedef enum { #define BACNET_COMPLEX_EVENT_TYPE_MAX_PARAMETERS 5 #endif +typedef struct BACnetEventExtendedParameter { + uint8_t tag; /* application tag data type */ + union { + /* NULL - not needed as it is encoded in the tag alone */ + float Real; + bool Boolean; + BACNET_UNSIGNED_INTEGER Unsigned_Int; + int32_t Signed_Int; + double Double; + BACNET_OCTET_STRING *Octet_String; + BACNET_CHARACTER_STRING *Character_String; + BACNET_BIT_STRING *Bit_String; + uint32_t Enumerated; + BACNET_DATE Date; + BACNET_TIME Time; + BACNET_OBJECT_ID Object_Id; + BACNET_DATE_TIME Date_Time; + BACNET_PROPERTY_VALUE *Property_Value; + } type; +} BACNET_EVENT_EXTENDED_PARAMETER; + +typedef struct BACnetEventDiscreteValue { + uint8_t tag; /* application tag data type */ + union { + bool Boolean; + BACNET_UNSIGNED_INTEGER Unsigned_Int; + int32_t Signed_Int; + uint32_t Enumerated; + BACNET_CHARACTER_STRING *Character_String; + BACNET_OCTET_STRING *Octet_String; + BACNET_DATE Date; + BACNET_TIME Time; + BACNET_OBJECT_ID Object_Id; + BACNET_DATE_TIME Date_Time; + } type; +} BACNET_EVENT_DISCRETE_VALUE; + typedef struct BACnet_Event_Notification_Data { uint32_t processIdentifier; BACNET_OBJECT_ID initiatingObjectIdentifier; @@ -133,11 +207,34 @@ typedef struct BACnet_Event_Notification_Data { BACNET_BIT_STRING statusFlags; BACNET_LIFE_SAFETY_OPERATION operationExpected; } changeOfLifeSafety; - /* - ** EVENT_EXTENDED - ** - ** Not Supported! - */ +#if BACNET_EVENT_EXTENDED_ENABLED + /* EVENT_EXTENDED + extended [9] SEQUENCE { + vendor-id [0] Unsigned16, + extended-event-type [1] Unsigned, + parameters [2] SEQUENCE OF CHOICE { + null NULL, + real REAL, + unsigned Unsigned, + boolean BOOLEAN, + integer INTEGER, + double Double, + octetstring OCTET STRING, + characterstring CharacterString, + bitstring BIT STRING, + enumerated ENUMERATED, + date Date, + time Time, + objectidentifier BACnetObjectIdentifier, + property-value [0] BACnetDeviceObjectPropertyValue + } + } */ + struct { + uint16_t vendorID; + BACNET_UNSIGNED_INTEGER extendedEventType; + BACNET_EVENT_EXTENDED_PARAMETER parameters; + } extended; +#endif /* ** EVENT_BUFFER_READY */ @@ -167,7 +264,140 @@ typedef struct BACnet_Event_Notification_Data { /* OPTIONAL - Set authenticationFactor.format_type to AUTHENTICATION_FACTOR_MAX if not being used */ } accessEvent; -#if (BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS == 1) +#if BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED + /* EVENT_DOUBLE_OUT_OF_RANGE + double-out-of-range[14] SEQUENCE { + exceeding-value[0] Double, + status-flags[1] BACnetStatusFlags, + deadband[2] Double, + exceeded-limit[3] Double + } */ + struct { + double exceedingValue; + BACNET_BIT_STRING statusFlags; + double deadband; + double exceededLimit; + } doubleOutOfRange; +#endif +#if BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED + /* EVENT_SIGNED_OUT_OF_RANGE + signed-out-of-range[14] SEQUENCE { + exceeding-value[0] Integer, + status-flags[1] BACnetStatusFlags, + deadband[2] Unsigned, + exceeded-limit[3] Integer + } */ + struct { + int32_t exceedingValue; + BACNET_BIT_STRING statusFlags; + uint32_t deadband; + int32_t exceededLimit; + } signedOutOfRange; +#endif +#if BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED + /* EVENT_UNSIGNED_OUT_OF_RANGE + unsigned-out-of-range[14] SEQUENCE { + exceeding-value[0] Unsigned, + status-flags[1] BACnetStatusFlags, + deadband[2] Unsigned, + exceeded-limit[3] Unsigned + } */ + struct { + BACNET_UNSIGNED_INTEGER exceedingValue; + BACNET_BIT_STRING statusFlags; + BACNET_UNSIGNED_INTEGER deadband; + BACNET_UNSIGNED_INTEGER exceededLimit; + } unsignedOutOfRange; +#endif +#if BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED + /* EVENT_CHANGE_OF_CHARACTERSTRING + change-of-characterstring [17] SEQUENCE { + changed-value [0] CharacterString, + status-flags [1] BACnetStatusFlags, + alarm-value [2] CharacterString + } */ + struct { + BACNET_CHARACTER_STRING *changedValue; + BACNET_BIT_STRING statusFlags; + BACNET_CHARACTER_STRING *alarmValue; + } changeOfCharacterstring; +#endif +#if BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED + /* EVENT_CHANGE_OF_STATUS_FLAGS + change-of-status-flags [18] SEQUENCE { + present-value [0] ABSTRACT-SYNTAX.&Type OPTIONAL, + -- depends on referenced property + referenced-flags [1] BACnetStatusFlags + } */ + /* OPTIONAL - Set present-value.tag to + BACNET_APPLICATION_TAG_EMPTYLIST if not used */ + struct { + BACNET_EVENT_EXTENDED_PARAMETER presentValue; + BACNET_BIT_STRING referencedFlags; + } changeOfStatusFlags; +#endif +#if BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED + /* EVENT_CHANGE_OF_RELIABILITY + change-of-reliability [19] SEQUENCE { + reliability [0] BACnetReliability, + status-flags [1] BACnetStatusFlags, + property-values [2] SEQUENCE OF BACnetPropertyValue + } */ + struct { + BACNET_RELIABILITY reliability; + BACNET_BIT_STRING statusFlags; + BACNET_PROPERTY_VALUE *propertyValues; + } changeOfReliability; +#endif +#if BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED + /* EVENT_CHANGE_OF_DISCRETE_VALUE + change-of-discrete-value [21] SEQUENCE { + new-value [0] CHOICE { + boolean BOOLEAN, + unsigned Unsigned, + integer INTEGER, + enumerated ENUMERATED, + characterstring CharacterString, + octetstring OCTET STRING, + date Date, + time Time, + objectidentifier BACnetObjectIdentifier, + datetime [0] BACnetDateTime + }, + status-flags [1] BACnetStatusFlags + }*/ + struct { + BACNET_EVENT_DISCRETE_VALUE newValue; + BACNET_BIT_STRING statusFlags; + } changeOfDiscreteValue; +#endif +#if BACNET_EVENT_CHANGE_OF_TIMER_ENABLED + /* EVENT_CHANGE_OF_TIMER + change-of-timer [22] SEQUENCE { + new-state [0] BACnetTimerState, + status-flags [1] BACnetStatusFlags, + update-time [2] BACnetDateTime, + last-state-change [3] BACnetTimerTransition OPTIONAL, + initial-timeout [4] Unsigned OPTIONAL, + expiration-time [5] BACnetDateTime OPTIONAL + } */ + /* OPTIONAL - Set last-state-change to TIMER_TRANSITION_MAX + if not used. */ + /* OPTIONAL - Set expiration-time to wildcards if not used. */ + /* OPTIONAL - Set initial-timeout to 0 if not used. */ + struct { + BACNET_TIMER_STATE newState; + BACNET_BIT_STRING statusFlags; + BACNET_DATE_TIME updateTime; + BACNET_TIMER_TRANSITION lastStateChange; + BACNET_UNSIGNED_INTEGER initialTimeout; + BACNET_DATE_TIME expirationTime; + } changeOfTimer; +#endif + /* + ** EVENT_NONE - tag only + */ +#if BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS /* * complex-event-type - a sequence of values, used for proprietary event * types diff --git a/src/bacnet/rd.c b/src/bacnet/rd.c index 3005a4ef..8f7f16b2 100644 --- a/src/bacnet/rd.c +++ b/src/bacnet/rd.c @@ -68,7 +68,7 @@ int reinitialize_device_encode( } /** - * @brief Encode the COVNotification service request + * @brief Encode the ReinitializeDevice-Request service * @param apdu Pointer to the buffer for encoding into * @param apdu_size number of bytes available in the buffer * @param state Reinitialization state diff --git a/src/bacnet/rp.c b/src/bacnet/rp.c index bcee486f..6a502f34 100644 --- a/src/bacnet/rp.c +++ b/src/bacnet/rp.c @@ -270,7 +270,7 @@ int read_property_ack_encode( } /** - * @brief Encode the COVNotification service request + * @brief Encode the ReadProperty-ACK service * @param apdu Pointer to the buffer for encoding into * @param apdu_size number of bytes available in the buffer * @param data Pointer to the service data used for encoding values diff --git a/test/bacnet/event/CMakeLists.txt b/test/bacnet/event/CMakeLists.txt index 3494846d..ef457681 100644 --- a/test/bacnet/event/CMakeLists.txt +++ b/test/bacnet/event/CMakeLists.txt @@ -23,6 +23,16 @@ set(ZTST_DIR "${TST_DIR}/ztest/src") add_compile_definitions( BIG_ENDIAN=0 CONFIG_ZTEST=1 + BACNET_EVENT_EXTENDED_ENABLED=1 + BACNET_EVENT_DOUBLE_OUT_OF_RANGE_ENABLED=1 + BACNET_EVENT_SIGNED_OUT_OF_RANGE_ENABLED=1 + BACNET_EVENT_UNSIGNED_OUT_OF_RANGE_ENABLED=1 + BACNET_EVENT_CHANGE_OF_CHARACTERSTRING_ENABLED=1 + BACNET_EVENT_CHANGE_OF_STATUS_FLAGS_ENABLED=1 + BACNET_EVENT_CHANGE_OF_RELIABILITY_ENABLED=1 + BACNET_EVENT_CHANGE_OF_DISCRETE_VALUE_ENABLED=1 + BACNET_EVENT_CHANGE_OF_TIMER_ENABLED=1 + BACNET_DECODE_COMPLEX_EVENT_TYPE_PARAMETERS=1 ) include_directories( diff --git a/test/bacnet/event/src/main.c b/test/bacnet/event/src/main.c index e736ae92..2c28e59c 100644 --- a/test/bacnet/event/src/main.c +++ b/test/bacnet/event/src/main.c @@ -7,6 +7,7 @@ */ #include #include +#include /** * @addtogroup bacnet_tests @@ -108,604 +109,12 @@ static void verifyBaseEventState(void) } } -/** - * @brief Test BACnet event handlers - */ -#if defined(CONFIG_ZTEST_NEW_API) -ZTEST(event_tests, testEventEventState) -#else -static void testEventEventState(void) -#endif +static void testEventBufferReady(void) { - uint8_t buffer[MAX_APDU]; + uint8_t apdu[MAX_APDU]; int apdu_len, test_len, null_len; - BACNET_CHARACTER_STRING messageText; - BACNET_CHARACTER_STRING messageText2; - characterstring_init_ansi( - &messageText, "This is a test of the message text\n"); + BACNET_CHARACTER_STRING messageText2 = { 0 }; - data.messageText = &messageText; - data2.messageText = &messageText2; - - data.processIdentifier = 1234; - data.initiatingObjectIdentifier.type = OBJECT_ANALOG_INPUT; - data.initiatingObjectIdentifier.instance = 100; - data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; - data.eventObjectIdentifier.instance = 200; - data.timeStamp.value.sequenceNum = 1234; - data.timeStamp.tag = TIME_STAMP_SEQUENCE; - data.notificationClass = 50; - data.priority = 50; - data.notifyType = NOTIFY_ALARM; - data.fromState = EVENT_STATE_NORMAL; - data.toState = EVENT_STATE_OFFNORMAL; - - data.eventType = EVENT_CHANGE_OF_STATE; - data.notificationParams.changeOfState.newState.tag = PROP_STATE_UNITS; - data.notificationParams.changeOfState.newState.state.units = - UNITS_SQUARE_METERS; - - bitstring_init(&data.notificationParams.changeOfState.statusFlags); - bitstring_set_bit( - &data.notificationParams.changeOfState.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.changeOfState.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.changeOfState.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.changeOfState.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - zassert_equal( - apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); - verifyBaseEventState(); - - zassert_equal( - data.notificationParams.changeOfState.newState.tag, - data2.notificationParams.changeOfState.newState.tag, NULL); - zassert_equal( - data.notificationParams.changeOfState.newState.state.units, - data2.notificationParams.changeOfState.newState.state.units, NULL); - - zassert_true( - bitstring_same( - &data.notificationParams.changeOfState.statusFlags, - &data2.notificationParams.changeOfState.statusFlags), - NULL); - - /* - ** Same, but timestamp of - */ - data.timeStamp.tag = TIME_STAMP_DATETIME; - data.timeStamp.value.dateTime.time.hour = 1; - data.timeStamp.value.dateTime.time.min = 2; - data.timeStamp.value.dateTime.time.sec = 3; - data.timeStamp.value.dateTime.time.hundredths = 4; - - data.timeStamp.value.dateTime.date.day = 1; - data.timeStamp.value.dateTime.date.month = 1; - data.timeStamp.value.dateTime.date.wday = 1; - data.timeStamp.value.dateTime.date.year = 1945; - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - zassert_equal( - data.notificationParams.changeOfState.newState.tag, - data2.notificationParams.changeOfState.newState.tag, NULL); - zassert_equal( - data.notificationParams.changeOfState.newState.state.units, - data2.notificationParams.changeOfState.newState.state.units, NULL); - /* - ** Event Type = EVENT_CHANGE_OF_BITSTRING - */ - data.timeStamp.value.sequenceNum = 1234; - data.timeStamp.tag = TIME_STAMP_SEQUENCE; - - data.eventType = EVENT_CHANGE_OF_BITSTRING; - - bitstring_init( - &data.notificationParams.changeOfBitstring.referencedBitString); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.referencedBitString, 0, - true); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.referencedBitString, 1, - false); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.referencedBitString, 2, - true); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.referencedBitString, 2, - false); - - bitstring_init(&data.notificationParams.changeOfBitstring.statusFlags); - - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.statusFlags, - STATUS_FLAG_FAULT, false); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.changeOfBitstring.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - - zassert_true( - bitstring_same( - &data.notificationParams.changeOfBitstring.referencedBitString, - &data2.notificationParams.changeOfBitstring.referencedBitString), - NULL); - - zassert_true( - bitstring_same( - &data.notificationParams.changeOfBitstring.statusFlags, - &data2.notificationParams.changeOfBitstring.statusFlags), - NULL); - - /* - ** Event Type = EVENT_CHANGE_OF_VALUE - float value - */ - data.eventType = EVENT_CHANGE_OF_VALUE; - data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; - data.notificationParams.changeOfValue.newValue.changeValue = 1.23f; - - bitstring_init(&data.notificationParams.changeOfValue.statusFlags); - - bitstring_set_bit( - &data.notificationParams.changeOfValue.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.changeOfValue.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.changeOfValue.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.changeOfValue.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - - zassert_true( - bitstring_same( - &data.notificationParams.changeOfValue.statusFlags, - &data2.notificationParams.changeOfValue.statusFlags), - NULL); - - zassert_equal( - data.notificationParams.changeOfValue.tag, - data2.notificationParams.changeOfValue.tag, NULL); - - zassert_false( - islessgreater( - data.notificationParams.changeOfValue.newValue.changeValue, - data2.notificationParams.changeOfValue.newValue.changeValue), - NULL); - - /* - ** Event Type = EVENT_CHANGE_OF_VALUE - bitstring value - */ - - data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_BITS; - bitstring_init(&data.notificationParams.changeOfValue.newValue.changedBits); - bitstring_set_bit( - &data.notificationParams.changeOfValue.newValue.changedBits, 0, true); - bitstring_set_bit( - &data.notificationParams.changeOfValue.newValue.changedBits, 1, false); - bitstring_set_bit( - &data.notificationParams.changeOfValue.newValue.changedBits, 2, false); - bitstring_set_bit( - &data.notificationParams.changeOfValue.newValue.changedBits, 3, false); - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - zassert_equal(apdu_len, test_len, NULL); - - verifyBaseEventState(); - zassert_true( - bitstring_same( - &data.notificationParams.changeOfValue.statusFlags, - &data2.notificationParams.changeOfValue.statusFlags), - NULL); - zassert_equal( - data.notificationParams.changeOfValue.tag, - data2.notificationParams.changeOfValue.tag, NULL); - zassert_true( - bitstring_same( - &data.notificationParams.changeOfValue.newValue.changedBits, - &data2.notificationParams.changeOfValue.newValue.changedBits), - NULL); - - /* - ** Event Type = EVENT_COMMAND_FAILURE - */ - - /* - ** commandValue = enumerated - */ - data.eventType = EVENT_COMMAND_FAILURE; - data.notificationParams.commandFailure.tag = COMMAND_FAILURE_BINARY_PV; - data.notificationParams.commandFailure.commandValue.binaryValue = - BINARY_INACTIVE; - data.notificationParams.commandFailure.feedbackValue.binaryValue = - BINARY_ACTIVE; - bitstring_init(&data.notificationParams.commandFailure.statusFlags); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - zassert_equal( - apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); - - verifyBaseEventState(); - - zassert_equal( - data.notificationParams.commandFailure.commandValue.binaryValue, - data2.notificationParams.commandFailure.commandValue.binaryValue, NULL); - - zassert_equal( - data.notificationParams.commandFailure.feedbackValue.binaryValue, - data2.notificationParams.commandFailure.feedbackValue.binaryValue, - NULL); - - zassert_true( - bitstring_same( - &data.notificationParams.commandFailure.statusFlags, - &data2.notificationParams.commandFailure.statusFlags), - NULL); - - /* - ** commandValue = unsigned - */ - data.eventType = EVENT_COMMAND_FAILURE; - data.notificationParams.commandFailure.tag = COMMAND_FAILURE_UNSIGNED; - data.notificationParams.commandFailure.commandValue.unsignedValue = 10; - data.notificationParams.commandFailure.feedbackValue.unsignedValue = 2; - - bitstring_init(&data.notificationParams.commandFailure.statusFlags); - - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.commandFailure.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - - zassert_equal( - data.notificationParams.commandFailure.commandValue.unsignedValue, - data2.notificationParams.commandFailure.commandValue.unsignedValue, - NULL); - - zassert_equal( - data.notificationParams.commandFailure.feedbackValue.unsignedValue, - data2.notificationParams.commandFailure.feedbackValue.unsignedValue, - NULL); - - zassert_true( - bitstring_same( - &data.notificationParams.commandFailure.statusFlags, - &data2.notificationParams.commandFailure.statusFlags), - NULL); - - /* - ** Event Type = EVENT_FLOATING_LIMIT - */ - data.eventType = EVENT_FLOATING_LIMIT; - data.notificationParams.floatingLimit.referenceValue = 1.23f; - data.notificationParams.floatingLimit.setPointValue = 2.34f; - data.notificationParams.floatingLimit.errorLimit = 3.45f; - - bitstring_init(&data.notificationParams.floatingLimit.statusFlags); - - bitstring_set_bit( - &data.notificationParams.floatingLimit.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.floatingLimit.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.floatingLimit.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.floatingLimit.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - zassert_equal( - apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); - verifyBaseEventState(); - - zassert_false( - islessgreater( - data.notificationParams.floatingLimit.referenceValue, - data2.notificationParams.floatingLimit.referenceValue), - NULL); - zassert_false( - islessgreater( - data.notificationParams.floatingLimit.setPointValue, - data2.notificationParams.floatingLimit.setPointValue), - NULL); - zassert_false( - islessgreater( - data.notificationParams.floatingLimit.errorLimit, - data2.notificationParams.floatingLimit.errorLimit), - NULL); - zassert_true( - bitstring_same( - &data.notificationParams.floatingLimit.statusFlags, - &data2.notificationParams.floatingLimit.statusFlags), - NULL); - - /* - ** Event Type = EVENT_OUT_OF_RANGE - */ - data.eventType = EVENT_OUT_OF_RANGE; - data.notificationParams.outOfRange.exceedingValue = 3.45f; - data.notificationParams.outOfRange.deadband = 2.34f; - data.notificationParams.outOfRange.exceededLimit = 1.23f; - - bitstring_init(&data.notificationParams.outOfRange.statusFlags); - - bitstring_set_bit( - &data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_IN_ALARM, - true); - bitstring_set_bit( - &data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_OVERRIDDEN, - false); - bitstring_set_bit( - &data.notificationParams.outOfRange.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - - zassert_false( - islessgreater( - data.notificationParams.outOfRange.deadband, - data2.notificationParams.outOfRange.deadband), - NULL); - - zassert_false( - islessgreater( - data.notificationParams.outOfRange.exceededLimit, - data2.notificationParams.outOfRange.exceededLimit), - NULL); - - zassert_false( - islessgreater( - data.notificationParams.outOfRange.exceedingValue, - data2.notificationParams.outOfRange.exceedingValue), - NULL); - zassert_true( - bitstring_same( - &data.notificationParams.outOfRange.statusFlags, - &data2.notificationParams.outOfRange.statusFlags), - NULL); - - /* - ** Event Type = EVENT_CHANGE_OF_LIFE_SAFETY - */ - data.eventType = EVENT_CHANGE_OF_LIFE_SAFETY; - data.notificationParams.changeOfLifeSafety.newState = - LIFE_SAFETY_STATE_ALARM; - data.notificationParams.changeOfLifeSafety.newMode = LIFE_SAFETY_MODE_ARMED; - data.notificationParams.changeOfLifeSafety.operationExpected = - LIFE_SAFETY_OP_RESET; - - bitstring_init(&data.notificationParams.changeOfLifeSafety.statusFlags); - - bitstring_set_bit( - &data.notificationParams.changeOfLifeSafety.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.changeOfLifeSafety.statusFlags, - STATUS_FLAG_FAULT, false); - bitstring_set_bit( - &data.notificationParams.changeOfLifeSafety.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.changeOfLifeSafety.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - - zassert_equal( - data.notificationParams.changeOfLifeSafety.newMode, - data2.notificationParams.changeOfLifeSafety.newMode, NULL); - - zassert_equal( - data.notificationParams.changeOfLifeSafety.newState, - data2.notificationParams.changeOfLifeSafety.newState, NULL); - - zassert_equal( - data.notificationParams.changeOfLifeSafety.operationExpected, - data2.notificationParams.changeOfLifeSafety.operationExpected, NULL); - - zassert_true( - bitstring_same( - &data.notificationParams.changeOfLifeSafety.statusFlags, - &data2.notificationParams.changeOfLifeSafety.statusFlags), - NULL); - - /* - ** Event Type = EVENT_UNSIGNED_RANGE - */ - data.eventType = EVENT_UNSIGNED_RANGE; - data.notificationParams.unsignedRange.exceedingValue = 1234; - data.notificationParams.unsignedRange.exceededLimit = 2345; - - bitstring_init(&data.notificationParams.unsignedRange.statusFlags); - - bitstring_set_bit( - &data.notificationParams.unsignedRange.statusFlags, - STATUS_FLAG_IN_ALARM, true); - bitstring_set_bit( - &data.notificationParams.unsignedRange.statusFlags, STATUS_FLAG_FAULT, - false); - bitstring_set_bit( - &data.notificationParams.unsignedRange.statusFlags, - STATUS_FLAG_OVERRIDDEN, false); - bitstring_set_bit( - &data.notificationParams.unsignedRange.statusFlags, - STATUS_FLAG_OUT_OF_SERVICE, false); - - memset(buffer, 0, MAX_APDU); - null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); - zassert_equal( - apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); - - memset(&data2, 0, sizeof(data2)); - data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); - - zassert_equal(apdu_len, test_len, NULL); - verifyBaseEventState(); - - zassert_equal( - data.notificationParams.unsignedRange.exceedingValue, - data2.notificationParams.unsignedRange.exceedingValue, NULL); - - zassert_equal( - data.notificationParams.unsignedRange.exceededLimit, - data2.notificationParams.unsignedRange.exceededLimit, NULL); - - zassert_true( - bitstring_same( - &data.notificationParams.unsignedRange.statusFlags, - &data2.notificationParams.unsignedRange.statusFlags), - NULL); - - /* - ** Event Type = EVENT_BUFFER_READY - */ data.eventType = EVENT_BUFFER_READY; data.notificationParams.bufferReady.previousNotification = 1234; data.notificationParams.bufferReady.currentNotification = 2345; @@ -721,16 +130,15 @@ static void testEventEventState(void) PROP_PRESENT_VALUE; data.notificationParams.bufferReady.bufferProperty.arrayIndex = 0; - memset(buffer, 0, MAX_APDU); + memset(apdu, 0, MAX_APDU); null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); zassert_equal( apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); memset(&data2, 0, sizeof(data2)); data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); zassert_equal(apdu_len, test_len, NULL); verifyBaseEventState(); @@ -779,10 +187,13 @@ static void testEventEventState(void) zassert_equal( data.notificationParams.bufferReady.bufferProperty.arrayIndex, data2.notificationParams.bufferReady.bufferProperty.arrayIndex, NULL); +} - /* - ** Event Type = EVENT_ACCESS_EVENT - */ +static void testEventAccessEvent(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; // OPTIONAL authenticationFactor omitted data.eventType = EVENT_ACCESS_EVENT; @@ -817,16 +228,15 @@ static void testEventEventState(void) &data.notificationParams.accessEvent.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); - memset(buffer, 0, MAX_APDU); + memset(apdu, 0, MAX_APDU); null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); zassert_equal( apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); memset(&data2, 0, sizeof(data2)); data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); zassert_equal(apdu_len, test_len, NULL); verifyBaseEventState(); @@ -921,16 +331,15 @@ static void testEventEventState(void) &data.notificationParams.accessEvent.statusFlags, STATUS_FLAG_OUT_OF_SERVICE, false); - memset(buffer, 0, MAX_APDU); + memset(apdu, 0, MAX_APDU); null_len = event_notify_encode_service_request(NULL, &data); - apdu_len = event_notify_encode_service_request(&buffer[0], &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); zassert_equal( apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); memset(&data2, 0, sizeof(data2)); data2.messageText = &messageText2; - test_len = - event_notify_decode_service_request(&buffer[0], apdu_len, &data2); + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); zassert_equal(apdu_len, test_len, NULL); verifyBaseEventState(); @@ -1002,6 +411,1393 @@ static void testEventEventState(void) &data2.notificationParams.accessEvent.authenticationFactor.value), NULL); } + +static void testEventDoubleOutOfRange(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_DOUBLE_OUT_OF_RANGE; + data.notificationParams.doubleOutOfRange.exceedingValue = 3.45; + data.notificationParams.doubleOutOfRange.deadband = 2.34; + data.notificationParams.doubleOutOfRange.exceededLimit = 1.23; + bitstring_init(&data.notificationParams.doubleOutOfRange.statusFlags); + bitstring_set_bit( + &data.notificationParams.doubleOutOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.doubleOutOfRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.doubleOutOfRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.doubleOutOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + zassert_false( + islessgreater( + data.notificationParams.doubleOutOfRange.deadband, + data2.notificationParams.doubleOutOfRange.deadband), + NULL); + zassert_false( + islessgreater( + data.notificationParams.doubleOutOfRange.exceededLimit, + data2.notificationParams.doubleOutOfRange.exceededLimit), + NULL); + zassert_false( + islessgreater( + data.notificationParams.doubleOutOfRange.exceedingValue, + data2.notificationParams.doubleOutOfRange.exceedingValue), + NULL); + zassert_true( + bitstring_same( + &data.notificationParams.doubleOutOfRange.statusFlags, + &data2.notificationParams.doubleOutOfRange.statusFlags), + NULL); +} + +static void testEventSignedOutOfRange(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_SIGNED_OUT_OF_RANGE; + data.notificationParams.signedOutOfRange.exceedingValue = -345; + data.notificationParams.signedOutOfRange.deadband = 234; + data.notificationParams.signedOutOfRange.exceededLimit = -123; + bitstring_init(&data.notificationParams.signedOutOfRange.statusFlags); + bitstring_set_bit( + &data.notificationParams.signedOutOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.signedOutOfRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.signedOutOfRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.signedOutOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.signedOutOfRange.deadband, + data2.notificationParams.signedOutOfRange.deadband, NULL); + zassert_equal( + data.notificationParams.signedOutOfRange.exceededLimit, + data2.notificationParams.signedOutOfRange.exceededLimit, NULL); + zassert_equal( + data.notificationParams.signedOutOfRange.exceedingValue, + data2.notificationParams.signedOutOfRange.exceedingValue, NULL); + zassert_true( + bitstring_same( + &data.notificationParams.signedOutOfRange.statusFlags, + &data2.notificationParams.signedOutOfRange.statusFlags), + NULL); +} + +static void testEventProprietary(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_PROPRIETARY_MIN; + data.notificationParams.complexEventType.values[0].propertyIdentifier = + PROP_PRESENT_VALUE; + data.notificationParams.complexEventType.values[0].priority = 1; + data.notificationParams.complexEventType.values[0].propertyArrayIndex = + BACNET_ARRAY_ALL; + data.notificationParams.complexEventType.values[0].value.tag = + BACNET_APPLICATION_TAG_REAL; + data.notificationParams.complexEventType.values[0].value.type.Real = 1.0f; + data.notificationParams.complexEventType.values[0].value.context_specific = + false; + data.notificationParams.complexEventType.values[0].value.context_tag = 0; + data.notificationParams.complexEventType.values[0].value.next = NULL; + data.notificationParams.complexEventType.values[0].next = NULL; + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + zassert_true( + bacapp_same_value( + &data.notificationParams.complexEventType.values[0].value, + &data2.notificationParams.complexEventType.values[0].value), + NULL); +} + +static void testEventUnsignedOutOfRange(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_UNSIGNED_OUT_OF_RANGE; + data.notificationParams.unsignedOutOfRange.exceedingValue = 345; + data.notificationParams.unsignedOutOfRange.deadband = 234; + data.notificationParams.unsignedOutOfRange.exceededLimit = 123; + bitstring_init(&data.notificationParams.unsignedOutOfRange.statusFlags); + bitstring_set_bit( + &data.notificationParams.unsignedOutOfRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.unsignedOutOfRange.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.unsignedOutOfRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.unsignedOutOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.unsignedOutOfRange.deadband, + data2.notificationParams.unsignedOutOfRange.deadband, NULL); + zassert_equal( + data.notificationParams.unsignedOutOfRange.exceededLimit, + data2.notificationParams.unsignedOutOfRange.exceededLimit, NULL); + zassert_equal( + data.notificationParams.unsignedOutOfRange.exceedingValue, + data2.notificationParams.unsignedOutOfRange.exceedingValue, NULL); + zassert_true( + bitstring_same( + &data.notificationParams.unsignedOutOfRange.statusFlags, + &data2.notificationParams.unsignedOutOfRange.statusFlags), + NULL); +} + +static void testEventChangeOfCharacterstring(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + BACNET_CHARACTER_STRING changed_value = { 0 }; + BACNET_CHARACTER_STRING alarm_value = { 0 }; + + data.eventType = EVENT_CHANGE_OF_CHARACTERSTRING; + data.notificationParams.changeOfCharacterstring.changedValue = + &changed_value; + data.notificationParams.changeOfCharacterstring.alarmValue = &alarm_value; + bitstring_init( + &data.notificationParams.changeOfCharacterstring.statusFlags); + bitstring_set_bit( + &data.notificationParams.changeOfCharacterstring.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfCharacterstring.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.changeOfCharacterstring.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfCharacterstring.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + zassert_true( + characterstring_same( + data.notificationParams.changeOfCharacterstring.changedValue, + data2.notificationParams.changeOfCharacterstring.changedValue), + NULL); + zassert_true( + bitstring_same( + &data.notificationParams.changeOfCharacterstring.statusFlags, + &data2.notificationParams.changeOfCharacterstring.statusFlags), + NULL); + zassert_true( + characterstring_same( + data.notificationParams.changeOfCharacterstring.alarmValue, + data2.notificationParams.changeOfCharacterstring.alarmValue), + NULL); +} + +static void testEventChangeOfStatusFlags(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + BACNET_OCTET_STRING extended_ostring = { 0 }; + BACNET_CHARACTER_STRING extended_cstring = { 0 }; + BACNET_BIT_STRING extended_bstring = { 0 }; + BACNET_PROPERTY_VALUE extended_pvalue = { + .next = NULL, + .priority = 1, + .propertyArrayIndex = BACNET_ARRAY_ALL, + .propertyIdentifier = PROP_PRESENT_VALUE, + .value = { .context_specific = false, + .context_tag = 0, + .next = NULL, + .tag = BACNET_APPLICATION_TAG_REAL, + .type.Real = 1.0f } + }; + unsigned i; + BACNET_EVENT_EXTENDED_PARAMETER present_value[] = { + { .tag = BACNET_APPLICATION_TAG_EMPTYLIST, .type.Unsigned_Int = 0 }, + { .tag = BACNET_APPLICATION_TAG_NULL, .type.Unsigned_Int = 0 }, + { .tag = BACNET_APPLICATION_TAG_BOOLEAN, .type.Boolean = true }, + { .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT, + .type.Unsigned_Int = 1234 }, + { .tag = BACNET_APPLICATION_TAG_SIGNED_INT, .type.Signed_Int = -1234 }, + { .tag = BACNET_APPLICATION_TAG_REAL, .type.Real = 1.0f }, + { .tag = BACNET_APPLICATION_TAG_DOUBLE, .type.Double = 1.0 }, + { .tag = BACNET_APPLICATION_TAG_OCTET_STRING, + .type.Octet_String = &extended_ostring }, + { .tag = BACNET_APPLICATION_TAG_CHARACTER_STRING, + .type.Character_String = &extended_cstring }, + { .tag = BACNET_APPLICATION_TAG_BIT_STRING, + .type.Bit_String = &extended_bstring }, + { .tag = BACNET_APPLICATION_TAG_ENUMERATED, .type.Enumerated = 1 }, + { .tag = BACNET_APPLICATION_TAG_DATE, + .type.Date = { .day = 1, .month = 1, .year = 1945 } }, + { .tag = BACNET_APPLICATION_TAG_TIME, + .type.Time = { .hour = 1, .min = 2, .sec = 3, .hundredths = 4 } }, + { .tag = BACNET_APPLICATION_TAG_OBJECT_ID, + .type.Object_Id = { .instance = 100, .type = OBJECT_ANALOG_INPUT } }, + { .tag = BACNET_APPLICATION_TAG_PROPERTY_VALUE, + .type.Property_Value = &extended_pvalue }, + { .tag = MAX_BACNET_APPLICATION_TAG, .type.Unsigned_Int = 0 }, + }; + + data.eventType = EVENT_CHANGE_OF_STATUS_FLAGS; + bitstring_init( + &data.notificationParams.changeOfStatusFlags.referencedFlags); + bitstring_set_bit( + &data.notificationParams.changeOfStatusFlags.referencedFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfStatusFlags.referencedFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.changeOfStatusFlags.referencedFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfStatusFlags.referencedFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + i = 0; + while (present_value[i].tag != MAX_BACNET_APPLICATION_TAG) { + memcpy( + &data.notificationParams.changeOfStatusFlags.presentValue, + &present_value[i], sizeof(present_value[i])); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = + event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "tag=%s apdu_len=%d test_len=%d", + bactext_application_tag_name( + data.notificationParams.changeOfStatusFlags.presentValue.tag), + apdu_len, test_len); + verifyBaseEventState(); + zassert_true( + bitstring_same( + &data.notificationParams.changeOfStatusFlags.referencedFlags, + &data2.notificationParams.changeOfStatusFlags.referencedFlags), + NULL); + i++; + } +} + +static void testEventchangeOfReliability(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_PROPERTY_VALUE property_values = { + .priority = 1, + .propertyArrayIndex = BACNET_ARRAY_ALL, + .propertyIdentifier = PROP_PRESENT_VALUE, + .value = { .context_specific = false, + .context_tag = 0, + .next = NULL, + .tag = BACNET_APPLICATION_TAG_REAL, + .type.Real = 1.0f }, + .next = NULL + }; + BACNET_PROPERTY_VALUE test_property_values = { 0 }; + + data.eventType = EVENT_CHANGE_OF_RELIABILITY; + data.notificationParams.changeOfReliability.reliability = + RELIABILITY_NO_FAULT_DETECTED; + data.notificationParams.changeOfReliability.propertyValues = + &property_values; + + bitstring_init(&data.notificationParams.changeOfReliability.statusFlags); + bitstring_set_bit( + &data.notificationParams.changeOfReliability.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfReliability.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.changeOfReliability.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfReliability.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + data2.notificationParams.changeOfReliability.propertyValues = + &test_property_values; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.changeOfReliability.reliability, + data2.notificationParams.changeOfReliability.reliability, NULL); + zassert_true( + bitstring_same( + &data.notificationParams.changeOfReliability.statusFlags, + &data2.notificationParams.changeOfReliability.statusFlags), + NULL); + /* property_values vs test_property_values*/ + zassert_equal( + property_values.propertyIdentifier, + test_property_values.propertyIdentifier, "property=%u test_property=%u", + (unsigned)property_values.propertyIdentifier, + (unsigned)test_property_values.propertyIdentifier); + zassert_equal( + property_values.propertyArrayIndex, + test_property_values.propertyArrayIndex, NULL); + zassert_equal( + property_values.priority, test_property_values.priority, NULL); + zassert_true( + bacapp_same_value(&property_values.value, &test_property_values.value), + NULL); + zassert_equal(property_values.next, test_property_values.next, NULL); +} + +static void testEventNone(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + + data.eventType = EVENT_NONE; + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); +} + +static void testEventChangeOfState(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_CHANGE_OF_STATE; + data.notificationParams.changeOfState.newState.tag = PROP_STATE_UNITS; + data.notificationParams.changeOfState.newState.state.units = + UNITS_SQUARE_METERS; + + bitstring_init(&data.notificationParams.changeOfState.statusFlags); + bitstring_set_bit( + &data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfState.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfState.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + + zassert_equal( + data.notificationParams.changeOfState.newState.tag, + data2.notificationParams.changeOfState.newState.tag, NULL); + zassert_equal( + data.notificationParams.changeOfState.newState.state.units, + data2.notificationParams.changeOfState.newState.state.units, NULL); + + zassert_true( + bitstring_same( + &data.notificationParams.changeOfState.statusFlags, + &data2.notificationParams.changeOfState.statusFlags), + NULL); + + /* + ** Same, but timestamp of + */ + data.timeStamp.tag = TIME_STAMP_DATETIME; + data.timeStamp.value.dateTime.time.hour = 1; + data.timeStamp.value.dateTime.time.min = 2; + data.timeStamp.value.dateTime.time.sec = 3; + data.timeStamp.value.dateTime.time.hundredths = 4; + + data.timeStamp.value.dateTime.date.day = 1; + data.timeStamp.value.dateTime.date.month = 1; + data.timeStamp.value.dateTime.date.wday = 1; + data.timeStamp.value.dateTime.date.year = 1945; + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.changeOfState.newState.tag, + data2.notificationParams.changeOfState.newState.tag, NULL); + zassert_equal( + data.notificationParams.changeOfState.newState.state.units, + data2.notificationParams.changeOfState.newState.state.units, NULL); +} + +static void testEventChangeOfBitstring(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + + data.eventType = EVENT_CHANGE_OF_BITSTRING; + + bitstring_init( + &data.notificationParams.changeOfBitstring.referencedBitString); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 0, + true); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 1, + false); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 2, + true); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.referencedBitString, 2, + false); + + bitstring_init(&data.notificationParams.changeOfBitstring.statusFlags); + + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfBitstring.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + + zassert_true( + bitstring_same( + &data.notificationParams.changeOfBitstring.referencedBitString, + &data2.notificationParams.changeOfBitstring.referencedBitString), + NULL); + + zassert_true( + bitstring_same( + &data.notificationParams.changeOfBitstring.statusFlags, + &data2.notificationParams.changeOfBitstring.statusFlags), + NULL); +} + +static void testEventChangeOfValue(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_CHANGE_OF_VALUE; + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_REAL; + data.notificationParams.changeOfValue.newValue.changeValue = 1.23f; + + bitstring_init(&data.notificationParams.changeOfValue.statusFlags); + + bitstring_set_bit( + &data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfValue.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfValue.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + + zassert_true( + bitstring_same( + &data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags), + NULL); + + zassert_equal( + data.notificationParams.changeOfValue.tag, + data2.notificationParams.changeOfValue.tag, NULL); + + zassert_false( + islessgreater( + data.notificationParams.changeOfValue.newValue.changeValue, + data2.notificationParams.changeOfValue.newValue.changeValue), + NULL); + + /* + ** Event Type = EVENT_CHANGE_OF_VALUE - bitstring value + */ + + data.notificationParams.changeOfValue.tag = CHANGE_OF_VALUE_BITS; + bitstring_init(&data.notificationParams.changeOfValue.newValue.changedBits); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 0, true); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 1, false); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 2, false); + bitstring_set_bit( + &data.notificationParams.changeOfValue.newValue.changedBits, 3, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal(apdu_len, test_len, NULL); + + verifyBaseEventState(); + zassert_true( + bitstring_same( + &data.notificationParams.changeOfValue.statusFlags, + &data2.notificationParams.changeOfValue.statusFlags), + NULL); + zassert_equal( + data.notificationParams.changeOfValue.tag, + data2.notificationParams.changeOfValue.tag, NULL); + zassert_true( + bitstring_same( + &data.notificationParams.changeOfValue.newValue.changedBits, + &data2.notificationParams.changeOfValue.newValue.changedBits), + NULL); +} + +static void testEventCommandFailure(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + /* + ** commandValue = enumerated + */ + data.eventType = EVENT_COMMAND_FAILURE; + data.notificationParams.commandFailure.tag = COMMAND_FAILURE_BINARY_PV; + data.notificationParams.commandFailure.commandValue.binaryValue = + BINARY_INACTIVE; + data.notificationParams.commandFailure.feedbackValue.binaryValue = + BINARY_ACTIVE; + bitstring_init(&data.notificationParams.commandFailure.statusFlags); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + + verifyBaseEventState(); + + zassert_equal( + data.notificationParams.commandFailure.commandValue.binaryValue, + data2.notificationParams.commandFailure.commandValue.binaryValue, NULL); + + zassert_equal( + data.notificationParams.commandFailure.feedbackValue.binaryValue, + data2.notificationParams.commandFailure.feedbackValue.binaryValue, + NULL); + + zassert_true( + bitstring_same( + &data.notificationParams.commandFailure.statusFlags, + &data2.notificationParams.commandFailure.statusFlags), + NULL); + + /* + ** commandValue = unsigned + */ + data.eventType = EVENT_COMMAND_FAILURE; + data.notificationParams.commandFailure.tag = COMMAND_FAILURE_UNSIGNED; + data.notificationParams.commandFailure.commandValue.unsignedValue = 10; + data.notificationParams.commandFailure.feedbackValue.unsignedValue = 2; + + bitstring_init(&data.notificationParams.commandFailure.statusFlags); + + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.commandFailure.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + + zassert_equal( + data.notificationParams.commandFailure.commandValue.unsignedValue, + data2.notificationParams.commandFailure.commandValue.unsignedValue, + NULL); + + zassert_equal( + data.notificationParams.commandFailure.feedbackValue.unsignedValue, + data2.notificationParams.commandFailure.feedbackValue.unsignedValue, + NULL); + + zassert_true( + bitstring_same( + &data.notificationParams.commandFailure.statusFlags, + &data2.notificationParams.commandFailure.statusFlags), + NULL); +} + +static void testEventFloatingLimit(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_FLOATING_LIMIT; + data.notificationParams.floatingLimit.referenceValue = 1.23f; + data.notificationParams.floatingLimit.setPointValue = 2.34f; + data.notificationParams.floatingLimit.errorLimit = 3.45f; + + bitstring_init(&data.notificationParams.floatingLimit.statusFlags); + + bitstring_set_bit( + &data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.floatingLimit.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.floatingLimit.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + + zassert_false( + islessgreater( + data.notificationParams.floatingLimit.referenceValue, + data2.notificationParams.floatingLimit.referenceValue), + NULL); + zassert_false( + islessgreater( + data.notificationParams.floatingLimit.setPointValue, + data2.notificationParams.floatingLimit.setPointValue), + NULL); + zassert_false( + islessgreater( + data.notificationParams.floatingLimit.errorLimit, + data2.notificationParams.floatingLimit.errorLimit), + NULL); + zassert_true( + bitstring_same( + &data.notificationParams.floatingLimit.statusFlags, + &data2.notificationParams.floatingLimit.statusFlags), + NULL); +} + +static void testEventOutOfRange(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_OUT_OF_RANGE; + data.notificationParams.outOfRange.exceedingValue = 3.45f; + data.notificationParams.outOfRange.deadband = 2.34f; + data.notificationParams.outOfRange.exceededLimit = 1.23f; + + bitstring_init(&data.notificationParams.outOfRange.statusFlags); + + bitstring_set_bit( + &data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_IN_ALARM, + true); + bitstring_set_bit( + &data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.outOfRange.statusFlags, STATUS_FLAG_OVERRIDDEN, + false); + bitstring_set_bit( + &data.notificationParams.outOfRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + + zassert_false( + islessgreater( + data.notificationParams.outOfRange.deadband, + data2.notificationParams.outOfRange.deadband), + NULL); + + zassert_false( + islessgreater( + data.notificationParams.outOfRange.exceededLimit, + data2.notificationParams.outOfRange.exceededLimit), + NULL); + + zassert_false( + islessgreater( + data.notificationParams.outOfRange.exceedingValue, + data2.notificationParams.outOfRange.exceedingValue), + NULL); + zassert_true( + bitstring_same( + &data.notificationParams.outOfRange.statusFlags, + &data2.notificationParams.outOfRange.statusFlags), + NULL); +} + +static void testEventChangeOfLifeSafety(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_CHANGE_OF_LIFE_SAFETY; + data.notificationParams.changeOfLifeSafety.newState = + LIFE_SAFETY_STATE_ALARM; + data.notificationParams.changeOfLifeSafety.newMode = LIFE_SAFETY_MODE_ARMED; + data.notificationParams.changeOfLifeSafety.operationExpected = + LIFE_SAFETY_OP_RESET; + + bitstring_init(&data.notificationParams.changeOfLifeSafety.statusFlags); + + bitstring_set_bit( + &data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfLifeSafety.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + + zassert_equal( + data.notificationParams.changeOfLifeSafety.newMode, + data2.notificationParams.changeOfLifeSafety.newMode, NULL); + + zassert_equal( + data.notificationParams.changeOfLifeSafety.newState, + data2.notificationParams.changeOfLifeSafety.newState, NULL); + + zassert_equal( + data.notificationParams.changeOfLifeSafety.operationExpected, + data2.notificationParams.changeOfLifeSafety.operationExpected, NULL); + + zassert_true( + bitstring_same( + &data.notificationParams.changeOfLifeSafety.statusFlags, + &data2.notificationParams.changeOfLifeSafety.statusFlags), + NULL); +} + +static void testEventUnsignedRange(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_UNSIGNED_RANGE; + data.notificationParams.unsignedRange.exceedingValue = 1234; + data.notificationParams.unsignedRange.exceededLimit = 2345; + + bitstring_init(&data.notificationParams.unsignedRange.statusFlags); + + bitstring_set_bit( + &data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.unsignedRange.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.unsignedRange.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + + zassert_equal(apdu_len, test_len, NULL); + verifyBaseEventState(); + + zassert_equal( + data.notificationParams.unsignedRange.exceedingValue, + data2.notificationParams.unsignedRange.exceedingValue, NULL); + + zassert_equal( + data.notificationParams.unsignedRange.exceededLimit, + data2.notificationParams.unsignedRange.exceededLimit, NULL); + + zassert_true( + bitstring_same( + &data.notificationParams.unsignedRange.statusFlags, + &data2.notificationParams.unsignedRange.statusFlags), + NULL); +} + +static void testEventExtended(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + BACNET_OCTET_STRING extended_ostring = { 0 }; + BACNET_CHARACTER_STRING extended_cstring = { 0 }; + BACNET_BIT_STRING extended_bstring = { 0 }; + BACNET_PROPERTY_VALUE extended_pvalue = { + .next = NULL, + .priority = 1, + .propertyArrayIndex = BACNET_ARRAY_ALL, + .propertyIdentifier = PROP_PRESENT_VALUE, + .value = { .context_specific = false, + .context_tag = 0, + .next = NULL, + .tag = BACNET_APPLICATION_TAG_REAL, + .type.Real = 1.0f } + }; + unsigned i; + BACNET_EVENT_EXTENDED_PARAMETER extended_parameters[] = { + { .tag = BACNET_APPLICATION_TAG_NULL, .type.Unsigned_Int = 0 }, + { .tag = BACNET_APPLICATION_TAG_BOOLEAN, .type.Boolean = true }, + { .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT, + .type.Unsigned_Int = 1234 }, + { .tag = BACNET_APPLICATION_TAG_SIGNED_INT, .type.Signed_Int = -1234 }, + { .tag = BACNET_APPLICATION_TAG_REAL, .type.Real = 1.0f }, + { .tag = BACNET_APPLICATION_TAG_DOUBLE, .type.Double = 1.0 }, + { .tag = BACNET_APPLICATION_TAG_OCTET_STRING, + .type.Octet_String = &extended_ostring }, + { .tag = BACNET_APPLICATION_TAG_CHARACTER_STRING, + .type.Character_String = &extended_cstring }, + { .tag = BACNET_APPLICATION_TAG_BIT_STRING, + .type.Bit_String = &extended_bstring }, + { .tag = BACNET_APPLICATION_TAG_ENUMERATED, .type.Enumerated = 1 }, + { .tag = BACNET_APPLICATION_TAG_DATE, + .type.Date = { .day = 1, .month = 1, .year = 1945 } }, + { .tag = BACNET_APPLICATION_TAG_TIME, + .type.Time = { .hour = 1, .min = 2, .sec = 3, .hundredths = 4 } }, + { .tag = BACNET_APPLICATION_TAG_OBJECT_ID, + .type.Object_Id = { .instance = 100, .type = OBJECT_ANALOG_INPUT } }, + { .tag = BACNET_APPLICATION_TAG_PROPERTY_VALUE, + .type.Property_Value = &extended_pvalue }, + { .tag = MAX_BACNET_APPLICATION_TAG, .type.Unsigned_Int = 0 }, + }; + + data.eventType = EVENT_EXTENDED; + data.notificationParams.extended.vendorID = 1234; + data.notificationParams.extended.extendedEventType = 4321; + i = 0; + while (extended_parameters[i].tag != MAX_BACNET_APPLICATION_TAG) { + memcpy( + &data.notificationParams.extended.parameters, + &extended_parameters[i], sizeof(extended_parameters[i])); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = + event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "tag=%s apdu_len=%d test_len=%d", + bactext_application_tag_name( + data.notificationParams.extended.parameters.tag), + apdu_len, test_len); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.extended.vendorID, + data2.notificationParams.extended.vendorID, NULL); + zassert_equal( + data.notificationParams.extended.extendedEventType, + data2.notificationParams.extended.extendedEventType, NULL); + zassert_equal( + data.notificationParams.extended.parameters.tag, + data2.notificationParams.extended.parameters.tag, NULL); + i++; + } +} + +static void testEventChangeOfDiscreteValue(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + BACNET_OCTET_STRING extended_ostring = { 0 }; + BACNET_CHARACTER_STRING extended_cstring = { 0 }; + unsigned i; + BACNET_EVENT_DISCRETE_VALUE discrete_values[] = { + { .tag = BACNET_APPLICATION_TAG_BOOLEAN, .type.Boolean = true }, + { .tag = BACNET_APPLICATION_TAG_UNSIGNED_INT, + .type.Unsigned_Int = 1234 }, + { .tag = BACNET_APPLICATION_TAG_SIGNED_INT, .type.Signed_Int = -1234 }, + { .tag = BACNET_APPLICATION_TAG_OCTET_STRING, + .type.Octet_String = &extended_ostring }, + { .tag = BACNET_APPLICATION_TAG_CHARACTER_STRING, + .type.Character_String = &extended_cstring }, + { .tag = BACNET_APPLICATION_TAG_ENUMERATED, .type.Enumerated = 1 }, + { .tag = BACNET_APPLICATION_TAG_DATE, + .type.Date = { .day = 1, .month = 1, .year = 1945 } }, + { .tag = BACNET_APPLICATION_TAG_TIME, + .type.Time = { .hour = 1, .min = 2, .sec = 3, .hundredths = 4 } }, + { .tag = BACNET_APPLICATION_TAG_OBJECT_ID, + .type.Object_Id = { .instance = 100, .type = OBJECT_ANALOG_INPUT } }, + { .tag = BACNET_APPLICATION_TAG_DATETIME, + .type.Date_Time = { { .day = 1, .month = 1, .year = 2025 }, + { .hour = 1, + .min = 1, + .sec = 1, + .hundredths = 1 } } }, + { .tag = MAX_BACNET_APPLICATION_TAG, .type.Unsigned_Int = 0 }, + }; + + data.eventType = EVENT_CHANGE_OF_DISCRETE_VALUE; + bitstring_init(&data.notificationParams.changeOfDiscreteValue.statusFlags); + bitstring_set_bit( + &data.notificationParams.changeOfDiscreteValue.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfDiscreteValue.statusFlags, + STATUS_FLAG_FAULT, false); + bitstring_set_bit( + &data.notificationParams.changeOfDiscreteValue.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfDiscreteValue.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + i = 0; + while (discrete_values[i].tag != MAX_BACNET_APPLICATION_TAG) { + memcpy( + &data.notificationParams.changeOfDiscreteValue.newValue, + &discrete_values[i], sizeof(discrete_values[i])); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = + event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "tag[%u]=%s apdu_len=%d test_len=%d", + (unsigned) + data.notificationParams.changeOfDiscreteValue.newValue.tag, + bactext_application_tag_name( + data.notificationParams.changeOfDiscreteValue.newValue.tag), + apdu_len, test_len); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.changeOfDiscreteValue.newValue.tag, + data2.notificationParams.changeOfDiscreteValue.newValue.tag, NULL); + i++; + } + zassert_true( + bitstring_same( + &data.notificationParams.changeOfDiscreteValue.statusFlags, + &data2.notificationParams.changeOfDiscreteValue.statusFlags), + NULL); +} + +static void testEventChangeOfTimer(void) +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + + data.eventType = EVENT_CHANGE_OF_TIMER; + data.notificationParams.changeOfTimer.newState = TIMER_STATE_IDLE; + data.notificationParams.changeOfTimer.initialTimeout = 1500; + datetime_init_ascii( + &data.notificationParams.changeOfTimer.expirationTime, + "2025/12/31-23:59:59.99"); + datetime_init_ascii( + &data.notificationParams.changeOfTimer.updateTime, + "2025/11/04-16:42:01.01"); + data.notificationParams.changeOfTimer.lastStateChange = + TIMER_TRANSITION_NONE; + bitstring_init(&data.notificationParams.changeOfTimer.statusFlags); + bitstring_set_bit( + &data.notificationParams.changeOfTimer.statusFlags, + STATUS_FLAG_IN_ALARM, true); + bitstring_set_bit( + &data.notificationParams.changeOfTimer.statusFlags, STATUS_FLAG_FAULT, + false); + bitstring_set_bit( + &data.notificationParams.changeOfTimer.statusFlags, + STATUS_FLAG_OVERRIDDEN, false); + bitstring_set_bit( + &data.notificationParams.changeOfTimer.statusFlags, + STATUS_FLAG_OUT_OF_SERVICE, false); + memset(apdu, 0, MAX_APDU); + null_len = event_notify_encode_service_request(NULL, &data); + apdu_len = event_notify_encode_service_request(&apdu[0], &data); + zassert_equal( + apdu_len, null_len, "apdu_len=%d null_len=%d", apdu_len, null_len); + memset(&data2, 0, sizeof(data2)); + data2.messageText = &messageText2; + test_len = event_notify_decode_service_request(&apdu[0], apdu_len, &data2); + zassert_equal( + apdu_len, test_len, "apdu_len=%d test_len=%d", apdu_len, test_len); + verifyBaseEventState(); + zassert_equal( + data.notificationParams.changeOfTimer.newState, + data2.notificationParams.changeOfTimer.newState, NULL); + zassert_equal( + data.notificationParams.changeOfTimer.initialTimeout, + data2.notificationParams.changeOfTimer.initialTimeout, NULL); + zassert_equal( + data.notificationParams.changeOfTimer.lastStateChange, + data2.notificationParams.changeOfTimer.lastStateChange, NULL); + zassert_equal( + datetime_compare( + &data.notificationParams.changeOfTimer.expirationTime, + &data2.notificationParams.changeOfTimer.expirationTime), + 0, NULL); + zassert_equal( + datetime_compare( + &data.notificationParams.changeOfTimer.updateTime, + &data2.notificationParams.changeOfTimer.updateTime), + 0, NULL); + zassert_true( + bitstring_same( + &data.notificationParams.changeOfTimer.statusFlags, + &data2.notificationParams.changeOfTimer.statusFlags), + NULL); +} + +/** + * @brief Test BACnet event handlers + */ +#if defined(CONFIG_ZTEST_NEW_API) +ZTEST(event_tests, testEventNotification) +#else +static void testEventNotification(void) +#endif +{ + uint8_t apdu[MAX_APDU]; + int apdu_len, test_len, null_len; + BACNET_CHARACTER_STRING messageText = { 0 }; + BACNET_CHARACTER_STRING messageText2 = { 0 }; + uint8_t invoke_id = 2; + + /* common to all the notification types */ + characterstring_init_ansi( + &messageText, "This is a test of the message text\n"); + data.messageText = &messageText; + data2.messageText = &messageText2; + data.processIdentifier = 1234; + data.initiatingObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.initiatingObjectIdentifier.instance = 100; + data.eventObjectIdentifier.type = OBJECT_ANALOG_INPUT; + data.eventObjectIdentifier.instance = 200; + data.timeStamp.value.sequenceNum = 1234; + data.timeStamp.tag = TIME_STAMP_SEQUENCE; + data.notificationClass = 50; + data.priority = 50; + data.notifyType = NOTIFY_ALARM; + data.fromState = EVENT_STATE_NORMAL; + data.toState = EVENT_STATE_OFFNORMAL; + /* + ** Event Type = EVENT_CHANGE_OF_BITSTRING + */ + testEventChangeOfBitstring(); + /* + ** Event Type = EVENT_CHANGE_OF_STATE + */ + testEventChangeOfState(); + /* + ** Event Type = EVENT_CHANGE_OF_VALUE + */ + testEventChangeOfValue(); + /* + ** Event Type = EVENT_COMMAND_FAILURE + */ + testEventCommandFailure(); + /* + ** Event Type = EVENT_FLOATING_LIMIT + */ + testEventFloatingLimit(); + /* + ** Event Type = EVENT_OUT_OF_RANGE + */ + testEventOutOfRange(); + /* + ** Event Type = EVENT_CHANGE_OF_LIFE_SAFETY + */ + testEventChangeOfLifeSafety(); + /* + ** Event Type = EVENT_EXTENDED + */ + testEventExtended(); + /* + ** Event Type = EVENT_BUFFER_READY + */ + testEventBufferReady(); + /* + ** Event Type = EVENT_UNSIGNED_RANGE + */ + testEventUnsignedRange(); + /* + ** Event Type = EVENT_ACCESS_EVENT + */ + testEventAccessEvent(); + /* + ** Event Type = EVENT_DOUBLE_OUT_OF_RANGE + */ + testEventDoubleOutOfRange(); + /* + ** Event Type = EVENT_SIGNED_OUT_OF_RANGE + */ + testEventSignedOutOfRange(); + /* + ** Event Type = EVENT_UNSIGNED_OUT_OF_RANGE + */ + testEventUnsignedOutOfRange(); + /* + ** EVENT_CHANGE_OF_CHARACTERSTRING + */ + testEventChangeOfCharacterstring(); + /* + ** EVENT_CHANGE_OF_STATUS_FLAGS + */ + testEventChangeOfStatusFlags(); + /* + ** EVENT_CHANGE_OF_RELIABILITY + */ + testEventchangeOfReliability(); + /* + ** EVENT_NONE + */ + testEventNone(); + /* + ** Event Type = EVENT_CHANGE_OF_DISCRETE_VALUE + */ + testEventChangeOfDiscreteValue(); + /* + ** Event Type = EVENT_CHANGE_OF_TIMER + */ + testEventChangeOfTimer(); + /* + ** Event Type = EVENT_PROPRIETARY_MIN + */ + testEventProprietary(); + /* function coverage: Confirmed and Unconfirmed Event Notifications */ + null_len = cevent_notify_encode_apdu(NULL, invoke_id, &data); + apdu_len = cevent_notify_encode_apdu(apdu, invoke_id, &data); + zassert_true(apdu_len > 0, NULL); + zassert_equal(apdu_len, null_len, NULL); + zassert_equal(apdu[0], PDU_TYPE_CONFIRMED_SERVICE_REQUEST, NULL); + zassert_equal(apdu[2], invoke_id, NULL); + zassert_equal(apdu[3], SERVICE_CONFIRMED_EVENT_NOTIFICATION, NULL); + null_len = uevent_notify_encode_apdu(NULL, &data); + apdu_len = uevent_notify_encode_apdu(apdu, &data); + zassert_true(apdu_len > 0, NULL); + zassert_equal(apdu_len, null_len, NULL); + zassert_equal(apdu[0], PDU_TYPE_UNCONFIRMED_SERVICE_REQUEST, NULL); + zassert_equal(apdu[1], SERVICE_UNCONFIRMED_EVENT_NOTIFICATION, NULL); + + null_len = + event_notification_service_request_encode(NULL, sizeof(apdu), &data); + apdu_len = + event_notification_service_request_encode(apdu, sizeof(apdu), &data); + zassert_true(apdu_len > 0, NULL); + zassert_equal(apdu_len, null_len, NULL); + while (apdu_len > 0) { + apdu_len--; + test_len = + event_notification_service_request_encode(apdu, apdu_len, &data); + zassert_equal(test_len, 0, NULL); + } +} /** * @} */ @@ -1011,7 +1807,7 @@ ZTEST_SUITE(event_tests, NULL, NULL, NULL, NULL, NULL); #else void test_main(void) { - ztest_test_suite(event_tests, ztest_unit_test(testEventEventState)); + ztest_test_suite(event_tests, ztest_unit_test(testEventNotification)); ztest_run_test_suite(event_tests); }